More explicit embedded socket support for issue #13

This commit is contained in:
Chad Retz 2018-09-21 12:03:30 -05:00
parent b4e16a90bf
commit 8ba66edb0c
4 changed files with 86 additions and 36 deletions

View File

@ -110,3 +110,7 @@ func (e *embeddedProcess) Wait() error {
return fmt.Errorf("Command completed with error exit code: %v", code) return fmt.Errorf("Command completed with error exit code: %v", code)
} }
} }
func (e *embeddedProcess) EmbeddedControlConn() (net.Conn, error) {
return nil, process.ErrControlConnUnsupported
}

View File

@ -4,26 +4,59 @@ import (
"context" "context"
"fmt" "fmt"
"log" "log"
"net/textproto"
"os" "os"
"github.com/cretz/bine/control"
tor035 "github.com/cretz/bine/process/embedded/tor-0.3.5" tor035 "github.com/cretz/bine/process/embedded/tor-0.3.5"
) )
// Simply calls Tor will the same parameters // Simply calls Tor will the same parameters, unless "embedconn" is the arg
func main() { func main() {
if err := runTor(os.Args[1:]...); err != nil { fmt.Printf("Provider version: %v\n", tor035.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) log.Fatal(err)
} }
} }
func runTor(args ...string) error { func runTor(args ...string) error {
creator := tor035.NewProcessCreator() process, err := tor035.NewCreator().New(context.Background(), args...)
creator.SetupControlSocket = true
process, err := creator.New(context.Background(), args...)
if err == nil { if err == nil {
fmt.Printf("Socket pointer: %v\n", tor035.ProcessControlSocket(process))
process.Start() process.Start()
err = process.Wait() err = process.Wait()
} }
return err return err
} }
func testEmbedConn() error {
process, err := tor035.NewCreator().New(context.Background())
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")
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
}

View File

@ -6,6 +6,8 @@ package tor035
import ( import (
"context" "context"
"fmt" "fmt"
"net"
"os"
"github.com/cretz/bine/process" "github.com/cretz/bine/process"
) )
@ -50,12 +52,7 @@ static void freeCharArray(char **a, int size) {
*/ */
import "C" import "C"
// ProcessCreator implements process.Creator type embeddedCreator struct{}
type ProcessCreator struct {
// If set to true, ProcessControlSocket will have a raw socket to
// communicate with Tor on.
SetupControlSocket bool
}
// ProviderVersion returns the Tor provider name and version exposed from the // ProviderVersion returns the Tor provider name and version exposed from the
// Tor embedded API. // Tor embedded API.
@ -63,32 +60,27 @@ func ProviderVersion() string {
return C.GoString(C.tor_api_get_provider_version()) return C.GoString(C.tor_api_get_provider_version())
} }
// NewProcessCreator creates a process.Creator for statically-linked Tor // NewCreator creates a process.Creator for statically-linked Tor embedded in
// embedded in the binary. // the binary.
func NewProcessCreator() *ProcessCreator { func NewCreator() process.Creator {
return &ProcessCreator{} return embeddedCreator{}
} }
type embeddedProcess struct { type embeddedProcess struct {
ctx context.Context ctx context.Context
mainConf *C.struct_tor_main_configuration_t mainConf *C.struct_tor_main_configuration_t
controlSocket uintptr
args []string args []string
doneCh chan int doneCh chan int
} }
// New implements process.Creator.New // New implements process.Creator.New
func (p *ProcessCreator) New(ctx context.Context, args ...string) (process.Process, error) { func (embeddedCreator) New(ctx context.Context, args ...string) (process.Process, error) {
ret := &embeddedProcess{ return &embeddedProcess{
ctx: ctx, ctx: ctx,
// TODO: mem leak if they never call Start; consider adding a Close()
mainConf: C.tor_main_configuration_new(), mainConf: C.tor_main_configuration_new(),
args: args, args: args,
} }, nil
// If they want a control socket, this is where we add it
if p.SetupControlSocket {
ret.controlSocket = uintptr(C.tor_main_configuration_setup_control_socket(ret.mainConf))
}
return ret, nil
} }
func (e *embeddedProcess) Start() error { func (e *embeddedProcess) Start() error {
@ -136,9 +128,11 @@ func (e *embeddedProcess) Wait() error {
} }
} }
// ProcessControlSocket returns a non-zero value for a process created by a func (e *embeddedProcess) EmbeddedControlConn() (net.Conn, error) {
// ProcessCreator with SetupControlSocket as true. Note, the value of this is file := os.NewFile(uintptr(C.tor_main_configuration_setup_control_socket(e.mainConf)), "")
// invalid when Start returns. conn, err := net.FileConn(file)
func ProcessControlSocket(p process.Process) uintptr { if err != nil {
return p.(*embeddedProcess).controlSocket err = fmt.Errorf("Unable to create conn from control socket: %v", err)
}
return conn, err
} }

View File

@ -10,6 +10,7 @@ package process
import ( import (
"context" "context"
"fmt" "fmt"
"net"
"os" "os"
"os/exec" "os/exec"
"strconv" "strconv"
@ -26,6 +27,12 @@ type Process interface {
// Wait waits for the Tor process to exit and returns error if it was not a // Wait waits for the Tor process to exit and returns error if it was not a
// successful exit. It is analagous to os/exec.Cmd.Wait. // successful exit. It is analagous to os/exec.Cmd.Wait.
Wait() error Wait() error
// ControlConn is used for statically linked, embedded processes to create
// a controller connection. For non-embedded processes or Tor versions that
// don't support embedded control connections, ErrControlConnUnsupported is
// returned. Note, this should only be called once per process before
// Start, and the connection does not need to be closed.
EmbeddedControlConn() (net.Conn, error)
} }
// Creator is the interface for process creation. // Creator is the interface for process creation.
@ -43,11 +50,23 @@ func NewCreator(exePath string) Creator {
return &exeProcessCreator{exePath} return &exeProcessCreator{exePath}
} }
type exeProcess struct {
*exec.Cmd
}
func (e *exeProcessCreator) New(ctx context.Context, args ...string) (Process, error) { func (e *exeProcessCreator) New(ctx context.Context, args ...string) (Process, error) {
cmd := exec.CommandContext(ctx, e.exePath, args...) cmd := exec.CommandContext(ctx, e.exePath, args...)
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
return cmd, nil return &exeProcess{cmd}, nil
}
// ErrControlConnUnsupported is returned by Process.EmbeddedControlConn when
// it is unsupported.
var ErrControlConnUnsupported = fmt.Errorf("Control conn not supported")
func (e *exeProcess) EmbeddedControlConn() (net.Conn, error) {
return nil, ErrControlConnUnsupported
} }
// ControlPortFromFileContents reads a control port file that is written by Tor // ControlPortFromFileContents reads a control port file that is written by Tor