bine/control/conn.go

103 lines
2.8 KiB
Go
Raw Normal View History

2018-05-10 18:11:18 +00:00
package control
import (
"fmt"
2018-05-10 21:29:16 +00:00
"io"
2018-05-10 18:11:18 +00:00
"net/textproto"
"sync"
)
2018-05-14 19:47:05 +00:00
// Conn is the connection to the Tor control port.
2018-05-10 18:11:18 +00:00
type Conn struct {
2018-05-14 20:36:29 +00:00
// DebugWriter is the writer that debug logs for this library (not Tor
// itself) will be written to. If nil, no debug logs are generated/written.
2018-05-10 21:29:16 +00:00
DebugWriter io.Writer
2018-05-14 19:47:05 +00:00
// This is the underlying connection.
2018-05-10 18:11:18 +00:00
conn *textproto.Conn
2018-05-14 20:36:29 +00:00
// This is set lazily by ProtocolInfo().
2018-05-10 23:05:43 +00:00
protocolInfo *ProtocolInfo
2018-05-14 19:47:05 +00:00
// True if Authenticate has been called successfully.
2018-05-10 23:05:43 +00:00
Authenticated bool
2018-05-10 18:11:18 +00:00
2018-05-14 19:47:05 +00:00
// The lock fot eventListeners
2018-05-11 04:08:20 +00:00
eventListenersLock sync.RWMutex
2018-05-14 20:36:29 +00:00
// The value slices can be traversed outside of lock, they are completely
// replaced on change, never mutated. But the map itself must be locked on
// when reading or writing.
2018-05-11 04:08:20 +00:00
eventListeners map[EventCode][]chan<- Event
2018-05-12 05:41:36 +00:00
2018-05-14 20:36:29 +00:00
// This mutex is locked on when an entire response needs to be read. It
// helps synchronize accesses to the response by the asynchronous response
// listeners and the synchronous responses.
2018-05-12 05:41:36 +00:00
readLock sync.Mutex
2018-05-11 04:08:20 +00:00
}
2018-05-10 18:11:18 +00:00
2018-05-14 19:47:05 +00:00
// NewConn creates a Conn from the given textproto connection.
2018-05-11 04:08:20 +00:00
func NewConn(conn *textproto.Conn) *Conn {
return &Conn{
conn: conn,
eventListeners: map[EventCode][]chan<- Event{},
}
2018-05-10 18:11:18 +00:00
}
func (c *Conn) sendRequestIgnoreResponse(format string, args ...interface{}) error {
_, err := c.SendRequest(format, args...)
return err
}
2018-05-14 20:36:29 +00:00
// SendRequest sends a synchronous request to Tor and awaits the response. If
// 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
2018-05-14 19:47:05 +00:00
// higher-level methods.
2018-05-10 18:11:18 +00:00
func (c *Conn) SendRequest(format string, args ...interface{}) (*Response, error) {
2018-05-10 23:05:43 +00:00
if c.debugEnabled() {
c.debugf("Write line: %v", fmt.Sprintf(format, args...))
}
2018-05-10 18:11:18 +00:00
id, err := c.conn.Cmd(format, args...)
if err != nil {
return nil, err
}
2018-05-12 05:41:36 +00:00
c.readLock.Lock()
defer c.readLock.Unlock()
2018-05-10 18:11:18 +00:00
c.conn.StartResponse(id)
defer c.conn.EndResponse(id)
// Get the first non-async response
var resp *Response
for {
if resp, err = c.ReadResponse(); err != nil || !resp.IsAsync() {
break
}
2018-05-14 19:47:05 +00:00
c.relayAsyncEvents(resp)
2018-05-10 18:11:18 +00:00
}
if err == nil && !resp.IsOk() {
err = resp.Err
}
return resp, err
}
2018-05-14 20:36:29 +00:00
// Close sends a QUIT and closes the underlying Tor connection. This does not
// error if the QUIT is not accepted but does relay any error that occurs while
// closing the underlying connection.
2018-05-10 18:11:18 +00:00
func (c *Conn) Close() error {
// Ignore the response and ignore the error
2018-05-11 04:08:20 +00:00
c.Quit()
2018-05-10 18:11:18 +00:00
return c.conn.Close()
}
2018-05-10 21:29:16 +00:00
func (c *Conn) debugEnabled() bool {
return c.DebugWriter != nil
}
func (c *Conn) debugf(format string, args ...interface{}) {
if w := c.DebugWriter; w != nil {
fmt.Fprintf(w, format+"\n", args...)
}
}
2018-05-10 23:05:43 +00:00
func (*Conn) protoErr(format string, args ...interface{}) textproto.ProtocolError {
2018-05-10 18:11:18 +00:00
return textproto.ProtocolError(fmt.Sprintf(format, args...))
}