Wrap godoc at 80 chars

This commit is contained in:
Chad Retz 2018-05-14 15:36:29 -05:00
parent 3e1430dee1
commit ca48a86ff3
14 changed files with 162 additions and 107 deletions

View File

@ -9,9 +9,11 @@ import (
"strings" "strings"
) )
// Authenticate authenticates with the Tor instance using the "best" possible authentication method if not already // Authenticate authenticates with the Tor instance using the "best" possible
// authenticated and sets the Authenticated field. The password argument is optional, and will only be used if the // authentication method if not already authenticated and sets the Authenticated
// "SAFECOOKIE" and "NULL" authentication methods are not available and "HASHEDPASSWORD" is. // field. The password argument is optional, and will only be used if the
// "SAFECOOKIE" and "NULL" authentication methods are not available and
// "HASHEDPASSWORD" is.
func (c *Conn) Authenticate(password string) error { func (c *Conn) Authenticate(password string) error {
if c.Authenticated { if c.Authenticated {
return nil return nil

View File

@ -48,8 +48,9 @@ const (
EventCodeTransportLaunched EventCode = "TRANSPORT_LAUNCHED" EventCodeTransportLaunched EventCode = "TRANSPORT_LAUNCHED"
) )
// EventCodeUnrecognized is a special event code that is only used with AddEventListener and RemoveEventListener to listen // EventCodeUnrecognized is a special event code that is only used with
// for events that are unrecognized by this library (i.e. UnrecognizedEvent). // AddEventListener and RemoveEventListener to listen for events that are
// unrecognized by this library (i.e. UnrecognizedEvent).
var EventCodeUnrecognized EventCode = "<unrecognized>" var EventCodeUnrecognized EventCode = "<unrecognized>"
var recognizedEventCodes = []EventCode{ var recognizedEventCodes = []EventCode{
@ -97,16 +98,18 @@ func mapEventCodes() map[EventCode]struct{} {
return ret return ret
} }
// EventCodes returns a new slice of all event codes that are recognized (i.e. does not include EventCodeUnrecognized). // EventCodes returns a new slice of all event codes that are recognized (i.e.
// does not include EventCodeUnrecognized).
func EventCodes() []EventCode { func EventCodes() []EventCode {
ret := make([]EventCode, len(recognizedEventCodes)) ret := make([]EventCode, len(recognizedEventCodes))
copy(ret, recognizedEventCodes) copy(ret, recognizedEventCodes)
return ret return ret
} }
// HandleEvents loops until the context is closed dispatching async events. Can dispatch events even after context is // HandleEvents loops until the context is closed dispatching async events. Can
// done and of course during synchronous request. This will always end with an error, either from ctx.Done() or from an // dispatch events even after context is done and of course during synchronous
// error reading/handling the event. // request. This will always end with an error, either from ctx.Done() or from
// an error reading/handling the event.
func (c *Conn) HandleEvents(ctx context.Context) error { func (c *Conn) HandleEvents(ctx context.Context) error {
errCh := make(chan error, 1) errCh := make(chan error, 1)
go func() { go func() {
@ -125,8 +128,9 @@ func (c *Conn) HandleEvents(ctx context.Context) error {
} }
} }
// HandleNextEvent attempts to read and handle the next event. It will return on first message seen, event or not. // HandleNextEvent attempts to read and handle the next event. It will return on
// Otherwise it will wait until there is a message read. // first message seen, event or not. Otherwise it will wait until there is a
// message read.
func (c *Conn) HandleNextEvent() error { func (c *Conn) HandleNextEvent() error {
c.readLock.Lock() c.readLock.Lock()
defer c.readLock.Unlock() defer c.readLock.Unlock()
@ -148,10 +152,12 @@ func (c *Conn) HandleNextEvent() error {
return nil return nil
} }
// AddEventListener adds the given channel as an event listener for the given events. Then Tor is notified about which // AddEventListener adds the given channel as an event listener for the given
// events should be listened to. Callers are expected to call RemoveEventListener for the channel and all event codes // events. Then Tor is notified about which events should be listened to.
// used here before closing the channel. If no events are provided, this is essentially a no-op. The // Callers are expected to call RemoveEventListener for the channel and all
// EventCodeUnrecognized event code can be used to listen for unrecognized events. // event codes used here before closing the channel. If no events are provided,
// this is essentially a no-op. The EventCodeUnrecognized event code can be used
// to listen for unrecognized events.
func (c *Conn) AddEventListener(ch chan<- Event, events ...EventCode) error { func (c *Conn) AddEventListener(ch chan<- Event, events ...EventCode) error {
// TODO: do we want to set the local map first? Or do we want to lock on the net request too? // TODO: do we want to set the local map first? Or do we want to lock on the net request too?
c.eventListenersLock.Lock() c.eventListenersLock.Lock()
@ -167,9 +173,11 @@ func (c *Conn) AddEventListener(ch chan<- Event, events ...EventCode) error {
return c.sendSetEvents() return c.sendSetEvents()
} }
// RemoveEventListener removes the given channel from being sent to by the given event codes. It is not an error to // RemoveEventListener removes the given channel from being sent to by the given
// remove a channel from events AddEventListener was not called for. Tor is notified about events which may no longer be // event codes. It is not an error to remove a channel from events
// listened to. If no events are provided, this is essentially a no-op. // AddEventListener was not called for. Tor is notified about events which may
// no longer be listened to. If no events are provided, this is essentially a
// no-op.
func (c *Conn) RemoveEventListener(ch chan<- Event, events ...EventCode) error { func (c *Conn) RemoveEventListener(ch chan<- Event, events ...EventCode) error {
c.eventListenersLock.Lock() c.eventListenersLock.Lock()
for _, event := range events { for _, event := range events {
@ -266,14 +274,16 @@ func parseISOTime2Frac(str string) time.Time {
return ret return ret
} }
// Event is the base interface for all known asynchronous // Event is the base interface for all known asynchronous events.
type Event interface { type Event interface {
Code() EventCode Code() EventCode
} }
// ParseEvent returns an Event for the given code and data info. Raw is the raw single line if it is a single-line // ParseEvent returns an Event for the given code and data info. Raw is the raw
// event (even if it has newlines), dataArray is the array of lines for multi-line events. Only one of the two needs to // single line if it is a single-line event (even if it has newlines), dataArray
// be set. The response is never nil, but may be UnrecognizedEvent. Format errors are ignored per the Tor spec. // is the array of lines for multi-line events. Only one of the two needs to be
// set. The response is never nil, but may be UnrecognizedEvent. Format errors
// are ignored per the Tor spec.
func ParseEvent(code EventCode, raw string, dataArray []string) Event { func ParseEvent(code EventCode, raw string, dataArray []string) Event {
switch code { switch code {
case EventCodeAddrMap: case EventCodeAddrMap:

View File

@ -36,7 +36,8 @@ const (
KeyAlgoED25519V3 KeyAlgo = "ED25519-V3" KeyAlgoED25519V3 KeyAlgo = "ED25519-V3"
) )
// Key is a type of key to use for AddOnion. Implementations include GenKey, RSAKey, and ED25519Key. // Key is a type of key to use for AddOnion. Implementations include GenKey,
// RSAKey, and ED25519Key.
type Key interface { type Key interface {
// Type is the KeyType for AddOnion. // Type is the KeyType for AddOnion.
Type() KeyType Type() KeyType
@ -59,10 +60,12 @@ func KeyFromString(str string) (Key, error) {
} }
} }
// GenKey is a Key for AddOnion that asks Tor to generate a key for the given algorithm. // GenKey is a Key for AddOnion that asks Tor to generate a key for the given
// algorithm.
type GenKey KeyAlgo type GenKey KeyAlgo
// GenKeyFromBlob creates a GenKey for the given response blob which is a KeyAlgo. // GenKeyFromBlob creates a GenKey for the given response blob which is a
// KeyAlgo.
func GenKeyFromBlob(blob string) GenKey { return GenKey(KeyAlgo(blob)) } func GenKeyFromBlob(blob string) GenKey { return GenKey(KeyAlgo(blob)) }
// Type implements Key.Type. // Type implements Key.Type.
@ -121,9 +124,11 @@ type AddOnionRequest struct {
Flags []string Flags []string
// MaxStreams is ADD_ONION MaxStreams. // MaxStreams is ADD_ONION MaxStreams.
MaxStreams int MaxStreams int
// Ports are ADD_ONION Port values. Key is virtual port, value is target port (or can be empty to use virtual port). // Ports are ADD_ONION Port values. Key is virtual port, value is target
// port (or can be empty to use virtual port).
Ports map[string]string Ports map[string]string
// ClientAuths are ADD_ONION ClientAuth values. If value is empty string, Tor will generate the password. // ClientAuths are ADD_ONION ClientAuth values. If value is empty string,
// Tor will generate the password.
ClientAuths map[string]string ClientAuths map[string]string
} }

View File

@ -24,7 +24,8 @@ func (p *ProtocolInfo) HasAuthMethod(authMethod string) bool {
return false return false
} }
// ProtocolInfo invokes PROTOCOLINFO on first invocation and returns a cached result on all others. // ProtocolInfo invokes PROTOCOLINFO on first invocation and returns a cached
// result on all others.
func (c *Conn) ProtocolInfo() (*ProtocolInfo, error) { func (c *Conn) ProtocolInfo() (*ProtocolInfo, error) {
var err error var err error
if c.protocolInfo == nil { if c.protocolInfo == nil {

View File

@ -9,15 +9,14 @@ import (
// Conn is the connection to the Tor control port. // Conn is the connection to the Tor control port.
type Conn struct { type Conn struct {
// No debug logs if nil // DebugWriter is the writer that debug logs for this library (not Tor
// DebugWriter is the writer that debug logs for this library (not Tor itself) will be written to. If nil, no debug // itself) will be written to. If nil, no debug logs are generated/written.
// logs are generated/written.
DebugWriter io.Writer DebugWriter io.Writer
// This is the underlying connection. // This is the underlying connection.
conn *textproto.Conn conn *textproto.Conn
// This is set lazily by ProtocolInfo() // This is set lazily by ProtocolInfo().
protocolInfo *ProtocolInfo protocolInfo *ProtocolInfo
// True if Authenticate has been called successfully. // True if Authenticate has been called successfully.
@ -25,12 +24,14 @@ type Conn struct {
// The lock fot eventListeners // The lock fot eventListeners
eventListenersLock sync.RWMutex eventListenersLock sync.RWMutex
// The value slices can be traversed outside of lock, they are completely replaced on change, never mutated. But the // The value slices can be traversed outside of lock, they are completely
// map itself must be locked on when reading or writing. // replaced on change, never mutated. But the map itself must be locked on
// when reading or writing.
eventListeners map[EventCode][]chan<- Event eventListeners map[EventCode][]chan<- Event
// This mutex is locked on when an entire response needs to be read. It helps synchronize accesses to the response // This mutex is locked on when an entire response needs to be read. It
// by the asynchronous response listeners and the synchronous responses. // helps synchronize accesses to the response by the asynchronous response
// listeners and the synchronous responses.
readLock sync.Mutex readLock sync.Mutex
} }
@ -47,8 +48,9 @@ func (c *Conn) sendRequestIgnoreResponse(format string, args ...interface{}) err
return err return err
} }
// SendRequest sends a synchronous request to Tor and awaits the response. If the response errors, the error result will // SendRequest sends a synchronous request to Tor and awaits the response. If
// be set, but the response will be set also. This is usually not directly used by callers, but instead called by // the response errors, the error result will be set, but the response will be
// set also. This is usually not directly used by callers, but instead called by
// higher-level methods. // higher-level methods.
func (c *Conn) SendRequest(format string, args ...interface{}) (*Response, error) { func (c *Conn) SendRequest(format string, args ...interface{}) (*Response, error) {
if c.debugEnabled() { if c.debugEnabled() {
@ -76,8 +78,9 @@ func (c *Conn) SendRequest(format string, args ...interface{}) (*Response, error
return resp, err return resp, err
} }
// Close sends a QUIT and closes the underlying Tor connection. This does not error if the QUIT is not accepted but does // Close sends a QUIT and closes the underlying Tor connection. This does not
// relay any error that occurs while closing the underlying connection. // error if the QUIT is not accepted but does relay any error that occurs while
// closing the underlying connection.
func (c *Conn) Close() error { func (c *Conn) Close() error {
// Ignore the response and ignore the error // Ignore the response and ignore the error
c.Quit() c.Quit()

View File

@ -1,8 +1,10 @@
// Package control implements a low-level client for the Tor control spec version 1. // Package control implements a low-level client for the Tor control spec
// version 1.
// //
// The primary entrypoint is the Conn struct, instantiated with NewConn. This is the low-level layer to the control // The primary entrypoint is the Conn struct, instantiated with NewConn. This is
// port of an already-running Tor instance. Most developers will prefer the tor package adjacent to this one for a // the low-level layer to the control port of an already-running Tor instance.
// higher level abstraction over the process and this connection. // Most developers will prefer the tor package adjacent to this one for a higher
// level abstraction over the process and this connection.
// //
// Some of this code is lifted from https://github.com/yawning/bulb with thanks. // Some of this code is lifted from https://github.com/yawning/bulb with thanks.
package control package control

View File

@ -1,17 +1,18 @@
package control package control
// KeyVal is a simple key-value struct. In cases where Val can be nil, an empty string represents that unless // KeyVal is a simple key-value struct. In cases where Val can be nil, an empty
// ValSetAndEmpty is true. // string represents that unless ValSetAndEmpty is true.
type KeyVal struct { type KeyVal struct {
// Key is the always-present key // Key is the always-present key
Key string Key string
// Val is the value. If it's an empty string and nils are accepted/supported where this is used, it means nil unless // Val is the value. If it's an empty string and nils are accepted/supported
// ValSetAndEmpty is true. // where this is used, it means nil unless ValSetAndEmpty is true.
Val string Val string
// ValSetAndEmpty is true when Val is an empty string, the associated command supports nils, and Val should NOT be // ValSetAndEmpty is true when Val is an empty string, the associated
// treated as nil. False otherwise. // command supports nils, and Val should NOT be treated as nil. False
// otherwise.
ValSetAndEmpty bool ValSetAndEmpty bool
} }
@ -20,8 +21,8 @@ func NewKeyVal(key string, val string) *KeyVal {
return &KeyVal{Key: key, Val: val} return &KeyVal{Key: key, Val: val}
} }
// KeyVals creates multiple new key-value pairs from the given strings. The provided set of strings must have a length // KeyVals creates multiple new key-value pairs from the given strings. The
// that is a multiple of 2. // provided set of strings must have a length that is a multiple of 2.
func KeyVals(keysAndVals ...string) []*KeyVal { func KeyVals(keysAndVals ...string) []*KeyVal {
if len(keysAndVals)%2 != 0 { if len(keysAndVals)%2 != 0 {
panic("Expected multiple of 2") panic("Expected multiple of 2")

View File

@ -8,22 +8,25 @@ import (
// Response is a response to a control port command or an asynchronous event. // Response is a response to a control port command or an asynchronous event.
type Response struct { type Response struct {
// Err is the status code and string representation associated with a response. Responses that have completed // Err is the status code and string representation associated with a
// successfully will also have Err set to indicate such. // response. Responses that have completed successfully will also have Err
// set to indicate such.
Err *textproto.Error Err *textproto.Error
// Reply is the text on the EndReplyLine of the response. // Reply is the text on the EndReplyLine of the response.
Reply string Reply string
// Data is the MidReplyLines/DataReplyLines of the response. Dot encoded data is "decoded" and presented as a single // Data is the MidReplyLines/DataReplyLines of the response. Dot encoded
// string (terminal ".CRLF" removed, all intervening CRs stripped). // data is "decoded" and presented as a single string (terminal ".CRLF"
// removed, all intervening CRs stripped).
Data []string Data []string
// RawLines is all of the lines of a response, without CRLFs. // RawLines is all of the lines of a response, without CRLFs.
RawLines []string RawLines []string
} }
// IsOk returns true if the response status code indicates success or an asynchronous event. // IsOk returns true if the response status code indicates success or an
// asynchronous event.
func (r *Response) IsOk() bool { func (r *Response) IsOk() bool {
switch r.Err.Code { switch r.Err.Code {
case StatusOk, StatusOkUnnecessary, StatusAsyncEvent: case StatusOk, StatusOkUnnecessary, StatusAsyncEvent:
@ -33,7 +36,8 @@ func (r *Response) IsOk() bool {
} }
} }
// DataWithReply returns a combination of Data and Reply to give a full set of the lines of the response. // DataWithReply returns a combination of Data and Reply to give a full set of
// the lines of the response.
func (r *Response) DataWithReply() []string { func (r *Response) DataWithReply() []string {
ret := make([]string, len(r.Data)+1) ret := make([]string, len(r.Data)+1)
copy(ret, r.Data) copy(ret, r.Data)
@ -41,7 +45,7 @@ func (r *Response) DataWithReply() []string {
return ret return ret
} }
// IsAsync returns true if the response is an asyncrhonous event. // IsAsync returns true if the response is an asynchronous event.
func (r *Response) IsAsync() bool { func (r *Response) IsAsync() bool {
return r.Err.Code == StatusAsyncEvent return r.Err.Code == StatusAsyncEvent
} }

View File

@ -1,11 +1,13 @@
// Package embedded implements process interfaces for statically linked, embedded Tor. // Package embedded implements process interfaces for statically linked,
// embedded Tor.
// //
// TODO: not finished yet // TODO: not finished yet
package embedded package embedded
import "github.com/cretz/bine/process" import "github.com/cretz/bine/process"
// NewCreator creates a process.Creator for statically linked embedded in the binary Tor. // NewCreator creates a process.Creator for statically-linked Tor embedded in
// the binary.
func NewCreator() process.Creator { func NewCreator() process.Creator {
panic("TODO: embedding not implemented yet") panic("TODO: embedding not implemented yet")
} }

View File

@ -1,8 +1,10 @@
// Package process is the low-level abstraction for a Tor instance. // Package process is the low-level abstraction for a Tor instance.
// //
// The standard use is to create a Creator with NewCreator and the path to the Tor executable. The child package // The standard use is to create a Creator with NewCreator and the path to the
// 'embedded' can be used if Tor is statically linked in the binary. Most developers will prefer the tor package // Tor executable. The child package 'embedded' can be used if Tor is statically
// adjacent to this one for a higher level abstraction over the process and control port connection. // linked in the binary. Most developers will prefer the tor package adjacent to
// this one for a higher level abstraction over the process and control port
// connection.
package process package process
import ( import (
@ -18,10 +20,11 @@ import (
// Process is the interface implemented by Tor processes. // Process is the interface implemented by Tor processes.
type Process interface { type Process interface {
// Start starts the Tor process in the background and returns. It is analagous to os/exec.Cmd.Start. // Start starts the Tor process in the background and returns. It is
// analagous to os/exec.Cmd.Start.
Start() error Start() error
// Wait waits for the Tor process to exit and returns error if it was not a successful exit. It is analagous to // Wait waits for the Tor process to exit and returns error if it was not a
// os/exec.Cmd.Wait. // successful exit. It is analagous to os/exec.Cmd.Wait.
Wait() error Wait() error
} }
@ -34,7 +37,8 @@ type exeProcessCreator struct {
exePath string exePath string
} }
// NewCreator creates a Creator for external Tor process execution based on the given exe path. // NewCreator creates a Creator for external Tor process execution based on the
// given exe path.
func NewCreator(exePath string) Creator { func NewCreator(exePath string) Creator {
return &exeProcessCreator{exePath} return &exeProcessCreator{exePath}
} }
@ -46,7 +50,8 @@ func (e *exeProcessCreator) New(ctx context.Context, args ...string) (Process, e
return cmd, nil return cmd, nil
} }
// ControlPortFromFileContents reads a control port file that is written by Tor when ControlPortWriteToFile is set. // ControlPortFromFileContents reads a control port file that is written by Tor
// when ControlPortWriteToFile is set.
func ControlPortFromFileContents(contents string) (int, error) { func ControlPortFromFileContents(contents string) (int, error) {
contents = strings.TrimSpace(contents) contents = strings.TrimSpace(contents)
_, port, ok := util.PartitionString(contents, ':') _, port, ok := util.PartitionString(contents, ':')

View File

@ -1,2 +1,2 @@
// Package tor is the high-level client for Tor // Package tor is the high-level client for Tor.
package tor package tor

View File

@ -7,7 +7,8 @@ func (t *Tor) DebugEnabled() bool {
return t.DebugWriter != nil return t.DebugWriter != nil
} }
// Debugf writes the formatted string with a newline appended to the DebugWriter if present. // Debugf writes the formatted string with a newline appended to the DebugWriter
// if present.
func (t *Tor) Debugf(format string, args ...interface{}) { func (t *Tor) Debugf(format string, args ...interface{}) {
if w := t.DebugWriter; w != nil { if w := t.DebugWriter; w != nil {
fmt.Fprintf(w, format+"\n", args...) fmt.Fprintf(w, format+"\n", args...)

View File

@ -16,8 +16,9 @@ import (
"github.com/cretz/bine/process" "github.com/cretz/bine/process"
) )
// Tor is the wrapper around the Tor process and control port connection. It should be created with Start and developers // Tor is the wrapper around the Tor process and control port connection. It
// should always call Close when done. // should be created with Start and developers should always call Close when
// done.
type Tor struct { type Tor struct {
// Process is the Tor instance that is running. // Process is the Tor instance that is running.
Process process.Process Process process.Process
@ -25,8 +26,8 @@ type Tor struct {
// Control is the Tor controller connection. // Control is the Tor controller connection.
Control *control.Conn Control *control.Conn
// ProcessCancelFunc is the context cancellation func for the Tor process. It is used by Close and should not be // ProcessCancelFunc is the context cancellation func for the Tor process.
// called directly. This can be nil. // It is used by Close and should not be called directly. This can be nil.
ProcessCancelFunc context.CancelFunc ProcessCancelFunc context.CancelFunc
// ControlPort is the port that Control is connected on. // ControlPort is the port that Control is connected on.
@ -35,40 +36,49 @@ type Tor struct {
// DataDir is the path to the data directory that Tor is using. // DataDir is the path to the data directory that Tor is using.
DataDir string DataDir string
// DeleteDataDirOnClose is true if, when Close is invoked, the entire directory will be deleted. // DeleteDataDirOnClose is true if, when Close is invoked, the entire
// directory will be deleted.
DeleteDataDirOnClose bool DeleteDataDirOnClose bool
// DebugWriter is the writer used for debug logs, or nil if debug logs should not be emitted. // DebugWriter is the writer used for debug logs, or nil if debug logs
// should not be emitted.
DebugWriter io.Writer DebugWriter io.Writer
// StopProcessOnClose, if true, will attempt to halt the process on close. // StopProcessOnClose, if true, will attempt to halt the process on close.
StopProcessOnClose bool StopProcessOnClose bool
} }
// StartConf is the configuration used for Start when starting a Tor instance. A default instance with no fields set is // StartConf is the configuration used for Start when starting a Tor instance. A
// the default used for Start. // default instance with no fields set is the default used for Start.
type StartConf struct { type StartConf struct {
// ExePath is the path to the Tor executable. If it is not present, "tor" is used either locally or on the PATH. // ExePath is the path to the Tor executable. If it is not present, "tor" is
// used either locally or on the PATH.
ExePath string ExePath string
// Embedded is true if Tor is statically compiled. If true, ExePath is ignored. // Embedded is true if Tor is statically compiled. If true, ExePath is
// ignored.
Embedded bool Embedded bool
// ControlPort is the port to use for the Tor controller. If it is 0, Tor picks a port for use. // ControlPort is the port to use for the Tor controller. If it is 0, Tor
// picks a port for use.
ControlPort int ControlPort int
// DataDir is the directory used by Tor. If it is empty, a temporary directory is created in TempDataDirBase. // DataDir is the directory used by Tor. If it is empty, a temporary
// directory is created in TempDataDirBase.
DataDir string DataDir string
// TempDataDirBase is the parent directory that a temporary data directory will be created under for use by Tor. // TempDataDirBase is the parent directory that a temporary data directory
// This is ignored if DataDir is not empty. If empty it is assumed to be the current working directory. // will be created under for use by Tor. This is ignored if DataDir is not
// empty. If empty it is assumed to be the current working directory.
TempDataDirBase string TempDataDirBase string
// RetainTempDataDir, if true, will not set the created temporary data directory to be deleted on close. This is // RetainTempDataDir, if true, will not set the created temporary data
// ignored if DataDir is not empty. // directory to be deleted on close. This is ignored if DataDir is not
// empty.
RetainTempDataDir bool RetainTempDataDir bool
// DisableCookieAuth, if true, will not use the default SAFECOOKIE authentication mechanism for the Tor controller. // DisableCookieAuth, if true, will not use the default SAFECOOKIE
// authentication mechanism for the Tor controller.
DisableCookieAuth bool DisableCookieAuth bool
// DisableEagerAuth, if true, will not authenticate on Start. // DisableEagerAuth, if true, will not authenticate on Start.
@ -77,19 +87,21 @@ type StartConf struct {
// EnableNetwork, if true, will connect to the wider Tor network on start. // EnableNetwork, if true, will connect to the wider Tor network on start.
EnableNetwork bool EnableNetwork bool
// ExtraArgs is the set of extra args passed to the Tor instance when started. // ExtraArgs is the set of extra args passed to the Tor instance when
// started.
ExtraArgs []string ExtraArgs []string
// TorrcFile is the torrc file to on start. If empty, a blank torrc is created in the data directory and is used // TorrcFile is the torrc file to on start. If empty, a blank torrc is
// instead. // created in the data directory and is used instead.
TorrcFile string TorrcFile string
// DebugWriter is the writer to use for debug logs, or nil for no debug logs. // DebugWriter is the writer to use for debug logs, or nil for no debug
// logs.
DebugWriter io.Writer DebugWriter io.Writer
} }
// Start a Tor instance and connect to it. If ctx is nil, context.Background() is used. If conf is nil, a default // Start a Tor instance and connect to it. If ctx is nil, context.Background()
// instance is used. // is used. If conf is nil, a default instance is used.
func Start(ctx context.Context, conf *StartConf) (*Tor, error) { func Start(ctx context.Context, conf *StartConf) (*Tor, error) {
if ctx == nil { if ctx == nil {
ctx = context.Background() ctx = context.Background()
@ -231,7 +243,8 @@ func (t *Tor) connectController(ctx context.Context, conf *StartConf) error {
return nil return nil
} }
// Close sends a halt to the Tor process if it can, closes the controller connection, and stops the process. // Close sends a halt to the Tor process if it can, closes the controller
// connection, and stops the process.
func (t *Tor) Close() error { func (t *Tor) Close() error {
errs := []error{} errs := []error{}
// If controller is authenticated, send the quit signal to the process. Otherwise, just close the controller. // If controller is authenticated, send the quit signal to the process. Otherwise, just close the controller.

View File

@ -5,8 +5,9 @@ import (
"strings" "strings"
) )
// PartitionString returns the two parts of a string delimited by the first occurrence of ch. If ch does not exist, the // PartitionString returns the two parts of a string delimited by the first
// second string is empty and the resulting bool is false. Otherwise it is true. // occurrence of ch. If ch does not exist, the second string is empty and the
// resulting bool is false. Otherwise it is true.
func PartitionString(str string, ch byte) (string, string, bool) { func PartitionString(str string, ch byte) (string, string, bool) {
index := strings.IndexByte(str, ch) index := strings.IndexByte(str, ch)
if index == -1 { if index == -1 {
@ -15,8 +16,8 @@ func PartitionString(str string, ch byte) (string, string, bool) {
return str[:index], str[index+1:], true return str[:index], str[index+1:], true
} }
// PartitionStringFromEnd is same as PartitionString except it delimts by the last occurrence of ch instead of the // PartitionStringFromEnd is same as PartitionString except it delimts by the
// first. // last occurrence of ch instead of the first.
func PartitionStringFromEnd(str string, ch byte) (string, string, bool) { func PartitionStringFromEnd(str string, ch byte) (string, string, bool) {
index := strings.LastIndexByte(str, ch) index := strings.LastIndexByte(str, ch)
if index == -1 { if index == -1 {
@ -25,8 +26,9 @@ func PartitionStringFromEnd(str string, ch byte) (string, string, bool) {
return str[:index], str[index+1:], true return str[:index], str[index+1:], true
} }
// EscapeSimpleQuotedStringIfNeeded calls EscapeSimpleQuotedString only if the string contains a space, backslash, // EscapeSimpleQuotedStringIfNeeded calls EscapeSimpleQuotedString only if the
// double quote, newline, or carriage return character. // string contains a space, backslash, double quote, newline, or carriage return
// character.
func EscapeSimpleQuotedStringIfNeeded(str string) string { func EscapeSimpleQuotedStringIfNeeded(str string) string {
if strings.ContainsAny(str, " \\\"\r\n") { if strings.ContainsAny(str, " \\\"\r\n") {
return EscapeSimpleQuotedString(str) return EscapeSimpleQuotedString(str)
@ -41,18 +43,20 @@ var simpleQuotedStringEscapeReplacer = strings.NewReplacer(
"\n", "\\n", "\n", "\\n",
) )
// EscapeSimpleQuotedString calls EscapeSimpleQuotedStringContents and then surrounds the entire string with double // EscapeSimpleQuotedString calls EscapeSimpleQuotedStringContents and then
// quotes. // surrounds the entire string with double quotes.
func EscapeSimpleQuotedString(str string) string { func EscapeSimpleQuotedString(str string) string {
return "\"" + simpleQuotedStringEscapeReplacer.Replace(str) + "\"" return "\"" + simpleQuotedStringEscapeReplacer.Replace(str) + "\""
} }
// EscapeSimpleQuotedStringContents escapes backslashes, double quotes, newlines, and carriage returns in str. // EscapeSimpleQuotedStringContents escapes backslashes, double quotes,
// newlines, and carriage returns in str.
func EscapeSimpleQuotedStringContents(str string) string { func EscapeSimpleQuotedStringContents(str string) string {
return simpleQuotedStringEscapeReplacer.Replace(str) return simpleQuotedStringEscapeReplacer.Replace(str)
} }
// UnescapeSimpleQuotedStringIfNeeded calls UnescapeSimpleQuotedString only if str is surrounded with double quotes. // UnescapeSimpleQuotedStringIfNeeded calls UnescapeSimpleQuotedString only if
// str is surrounded with double quotes.
func UnescapeSimpleQuotedStringIfNeeded(str string) (string, error) { func UnescapeSimpleQuotedStringIfNeeded(str string) (string, error) {
if len(str) >= 2 && str[0] == '"' && str[len(str)-1] == '"' { if len(str) >= 2 && str[0] == '"' && str[len(str)-1] == '"' {
return UnescapeSimpleQuotedString(str) return UnescapeSimpleQuotedString(str)
@ -60,7 +64,8 @@ func UnescapeSimpleQuotedStringIfNeeded(str string) (string, error) {
return str, nil return str, nil
} }
// UnescapeSimpleQuotedString removes surrounding double quotes and calls UnescapeSimpleQuotedStringContents. // UnescapeSimpleQuotedString removes surrounding double quotes and calls
// UnescapeSimpleQuotedStringContents.
func UnescapeSimpleQuotedString(str string) (string, error) { func UnescapeSimpleQuotedString(str string) (string, error) {
if len(str) < 2 || str[0] != '"' || str[len(str)-1] != '"' { if len(str) < 2 || str[0] != '"' || str[len(str)-1] != '"' {
return "", fmt.Errorf("Missing quotes") return "", fmt.Errorf("Missing quotes")
@ -68,7 +73,8 @@ func UnescapeSimpleQuotedString(str string) (string, error) {
return UnescapeSimpleQuotedStringContents(str[1 : len(str)-1]) return UnescapeSimpleQuotedStringContents(str[1 : len(str)-1])
} }
// UnescapeSimpleQuotedStringContents unescapes backslashes, double quotes, newlines, and carriage returns. // UnescapeSimpleQuotedStringContents unescapes backslashes, double quotes,
// newlines, and carriage returns.
func UnescapeSimpleQuotedStringContents(str string) (string, error) { func UnescapeSimpleQuotedStringContents(str string) (string, error) {
ret := "" ret := ""
escaping := false escaping := false