diff --git a/process/embedded/tor-0.4.7/embeddedtest/main.go b/process/embedded/tor-0.4.7/embeddedtest/main.go new file mode 100644 index 0000000..acfd8cf --- /dev/null +++ b/process/embedded/tor-0.4.7/embeddedtest/main.go @@ -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 +} diff --git a/process/embedded/tor-0.4.7/process.go b/process/embedded/tor-0.4.7/process.go new file mode 100644 index 0000000..018e52c --- /dev/null +++ b/process/embedded/tor-0.4.7/process.go @@ -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 +}