Wrap godoc at 80 chars
This commit is contained in:
parent
3e1430dee1
commit
ca48a86ff3
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
|
@ -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, ':')
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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...)
|
||||||
|
|
61
tor/tor.go
61
tor/tor.go
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue