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)
}
}
func (e *embeddedProcess) EmbeddedControlConn() (net.Conn, error) {
return nil, process.ErrControlConnUnsupported
}

View File

@ -4,26 +4,59 @@ import (
"context"
"fmt"
"log"
"net/textproto"
"os"
"github.com/cretz/bine/control"
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() {
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)
}
}
func runTor(args ...string) error {
creator := tor035.NewProcessCreator()
creator.SetupControlSocket = true
process, err := creator.New(context.Background(), args...)
process, err := tor035.NewCreator().New(context.Background(), args...)
if err == nil {
fmt.Printf("Socket pointer: %v\n", tor035.ProcessControlSocket(process))
process.Start()
err = process.Wait()
}
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 (
"context"
"fmt"
"net"
"os"
"github.com/cretz/bine/process"
)
@ -50,12 +52,7 @@ static void freeCharArray(char **a, int size) {
*/
import "C"
// ProcessCreator implements process.Creator
type ProcessCreator struct {
// If set to true, ProcessControlSocket will have a raw socket to
// communicate with Tor on.
SetupControlSocket bool
}
type embeddedCreator struct{}
// ProviderVersion returns the Tor provider name and version exposed from the
// Tor embedded API.
@ -63,32 +60,27 @@ func ProviderVersion() string {
return C.GoString(C.tor_api_get_provider_version())
}
// NewProcessCreator creates a process.Creator for statically-linked Tor
// embedded in the binary.
func NewProcessCreator() *ProcessCreator {
return &ProcessCreator{}
// 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
controlSocket uintptr
args []string
doneCh chan int
ctx context.Context
mainConf *C.struct_tor_main_configuration_t
args []string
doneCh chan int
}
// New implements process.Creator.New
func (p *ProcessCreator) New(ctx context.Context, args ...string) (process.Process, error) {
ret := &embeddedProcess{
ctx: ctx,
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,
}
// 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
}, nil
}
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
// ProcessCreator with SetupControlSocket as true. Note, the value of this is
// invalid when Start returns.
func ProcessControlSocket(p process.Process) uintptr {
return p.(*embeddedProcess).controlSocket
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
}

View File

@ -10,6 +10,7 @@ package process
import (
"context"
"fmt"
"net"
"os"
"os/exec"
"strconv"
@ -26,6 +27,12 @@ type Process interface {
// 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.
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.
@ -43,11 +50,23 @@ func NewCreator(exePath string) Creator {
return &exeProcessCreator{exePath}
}
type exeProcess struct {
*exec.Cmd
}
func (e *exeProcessCreator) New(ctx context.Context, args ...string) (Process, error) {
cmd := exec.CommandContext(ctx, e.exePath, args...)
cmd.Stdout = os.Stdout
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