Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for embedded Tor v0.4.7.x #64

Merged
merged 1 commit into from
Jul 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions process/embedded/tor-0.4.7/embeddedtest/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package main

import (
"context"
"fmt"
"log"
"net/textproto"
"os"

"github.com/cretz/bine/control"
tor047 "github.com/cretz/bine/process/embedded/tor-0.4.7"
)

// Simply calls Tor will the same parameters, unless "embedconn" is the arg
func main() {
fmt.Printf("Provider version: %v\n", tor047.ProviderVersion())
var err error
if len(os.Args) == 2 && os.Args[1] == "embedconn" {
fmt.Println("Testing embedded conn")
err = testEmbedConn()
} else {
fmt.Println("Running Tor with given args")
err = runTor(os.Args[1:]...)
}
if err != nil {
log.Fatal(err)
}
}

func runTor(args ...string) error {
process, err := tor047.NewCreator().New(context.Background(), args...)
if err == nil {
process.Start()
err = process.Wait()
}
return err
}

func testEmbedConn() error {
process, err := tor047.NewCreator().New(context.Background(), "--DisableNetwork", "1")
if err != nil {
return fmt.Errorf("Failed creating process: %v", err)
}
// Try to create an embedded conn
embedConn, err := process.EmbeddedControlConn()
if err != nil {
return fmt.Errorf("Failed creating embedded control conn: %v", err)
}
if err = process.Start(); err != nil {
return fmt.Errorf("Failed starting process: %v", err)
}
controlConn := control.NewConn(textproto.NewConn(embedConn))
info, err := controlConn.GetInfo("version")
controlConn.Close()
if err != nil {
return fmt.Errorf("Failed getting version: %v", err)
}
fmt.Printf("Got info, %v: %v\n", info[0].Key, info[0].Val)
if err = process.Wait(); err != nil {
return fmt.Errorf("Failed waiting for process: %v", err)
}
return nil
}
100 changes: 100 additions & 0 deletions process/embedded/tor-0.4.7/process.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Package tor047 implements process interfaces for statically linked
// Tor 0.4.7.x versions. See the process/embedded package for the generic
// abstraction
package tor047

import (
"context"
"fmt"
"net"
"os"

"github.com/cretz/bine/process"
)

import "C"

type embeddedCreator struct{}

// ProviderVersion returns the Tor provider name and version exposed from the
// Tor embedded API.
func ProviderVersion() string {
return C.GoString(C.tor_api_get_provider_version())
}

// NewCreator creates a process.Creator for statically-linked Tor embedded in
// the binary.
func NewCreator() process.Creator {
return embeddedCreator{}
}

type embeddedProcess struct {
ctx context.Context
mainConf *C.struct_tor_main_configuration_t
args []string
doneCh chan int
}

// New implements process.Creator.New
func (embeddedCreator) New(ctx context.Context, args ...string) (process.Process, error) {
return &embeddedProcess{
ctx: ctx,
// TODO: mem leak if they never call Start; consider adding a Close()
mainConf: C.tor_main_configuration_new(),
args: args,
}, nil
}

func (e *embeddedProcess) Start() error {
if e.doneCh != nil {
return fmt.Errorf("Already started")
}
// Create the char array for the args
args := append([]string{"tor"}, e.args...)
charArray := C.makeCharArray(C.int(len(args)))
for i, a := range args {
C.setArrayString(charArray, C.CString(a), C.int(i))
}
// Build the conf
if code := C.tor_main_configuration_set_command_line(e.mainConf, C.int(len(args)), charArray); code != 0 {
C.tor_main_configuration_free(e.mainConf)
C.freeCharArray(charArray, C.int(len(args)))
return fmt.Errorf("Failed to set command line args, code: %v", int(code))
}
// Run it async
e.doneCh = make(chan int, 1)
go func() {
defer C.freeCharArray(charArray, C.int(len(args)))
defer C.tor_main_configuration_free(e.mainConf)
e.doneCh <- int(C.tor_run_main(e.mainConf))
}()
return nil
}

func (e *embeddedProcess) Wait() error {
if e.doneCh == nil {
return fmt.Errorf("Not started")
}
ctx := e.ctx
if ctx == nil {
ctx = context.Background()
}
select {
case <-ctx.Done():
return ctx.Err()
case code := <-e.doneCh:
if code == 0 {
return nil
}
return fmt.Errorf("Command completed with error exit code: %v", code)
}
}

func (e *embeddedProcess) EmbeddedControlConn() (net.Conn, error) {
file := os.NewFile(uintptr(C.tor_main_configuration_setup_control_socket(e.mainConf)), "")
conn, err := net.FileConn(file)
if err != nil {
err = fmt.Errorf("Unable to create conn from control socket: %v", err)
}
return conn, err
}