Documentation ¶
Overview ¶
Package pt implements the Tor pluggable transports specification.
Sample client usage:
var ptInfo pt.ClientInfo ... func handler(conn *pt.SocksConn) error { defer conn.Close() remote, err := net.Dial("tcp", conn.Req.Target) if err != nil { conn.Reject() return err } defer remote.Close() err = conn.Grant(remote.RemoteAddr().(*net.TCPAddr)) if err != nil { return err } // do something with conn and remote. return nil } func acceptLoop(ln *pt.SocksListener) error { defer ln.Close() for { conn, err := ln.AcceptSocks() if err != nil { if e, ok := err.(net.Error); ok && !e.Temporary() { return err } continue } go handler(conn) } return nil } ... func main() { var err error ptInfo, err = pt.ClientSetup([]string{"foo"}) if err != nil { os.Exit(1) } for _, methodName := range ptInfo.MethodNames { switch methodName { case "foo": ln, err := pt.ListenSocks("tcp", "127.0.0.1:0") if err != nil { pt.CmethodError(methodName, err.Error()) break } go acceptLoop(ln) pt.Cmethod(methodName, ln.Version(), ln.Addr()) default: pt.CmethodError(methodName, "no such method") } } pt.CmethodsDone() }
Sample server usage:
var ptInfo pt.ServerInfo ... func handler(conn net.Conn) error { defer conn.Close() or, err := pt.DialOr(&ptInfo, conn.RemoteAddr().String(), "foo") if err != nil { return } defer or.Close() // do something with or and conn return nil } func acceptLoop(ln net.Listener) error { defer ln.Close() for { conn, err := ln.Accept() if err != nil { if e, ok := err.(net.Error); ok && !e.Temporary() { return err } continue } go handler(conn) } return nil } ... func main() { var err error ptInfo, err = pt.ServerSetup([]string{"foo"}) if err != nil { os.Exit(1) } for _, bindaddr := range ptInfo.Bindaddrs { switch bindaddr.MethodName { case "foo": ln, err := net.ListenTCP("tcp", bindaddr.Addr) if err != nil { pt.SmethodError(bindaddr.MethodName, err.Error()) break } go acceptLoop(ln) pt.Smethod(bindaddr.MethodName, ln.Addr()) default: pt.SmethodError(bindaddr.MethodName, "no such method") } } pt.SmethodsDone() }
Some additional care is needed to handle SIGINT and shutdown properly. See the example programs dummy-client and dummy-server.
Tor pluggable transports specification: https://gitweb.torproject.org/torspec.git/tree/pt-spec.txt.
Extended ORPort: https://gitweb.torproject.org/torspec.git/tree/proposals/196-transport-control-ports.txt.
Extended ORPort Authentication: https://gitweb.torproject.org/torspec.git/tree/proposals/217-ext-orport-auth.txt.
The package implements a SOCKS4a server sufficient for a Tor client transport plugin.
Index ¶
- Variables
- func Cmethod(name string, socks string, addr net.Addr)
- func CmethodError(methodName, msg string) error
- func CmethodsDone()
- func DialOr(info *ServerInfo, addr, methodName string) (*net.TCPConn, error)
- func MakeStateDir() (string, error)
- func Smethod(name string, addr net.Addr)
- func SmethodArgs(name string, addr net.Addr, args Args)
- func SmethodError(methodName, msg string) error
- func SmethodsDone()
- type Args
- type Bindaddr
- type ClientInfo
- type ServerInfo
- type SocksConn
- type SocksListener
- type SocksRequest
Constants ¶
This section is empty.
Variables ¶
var Stdout io.Writer = syncWriter{os.Stdout}
Writer to which pluggable transports negotiation messages are written. It defaults to a Writer that writes to os.Stdout and calls Sync after each write.
You may, for example, log pluggable transports messages by defining a Writer that logs what is written to it:
type logWriteWrapper struct { io.Writer } func (w logWriteWrapper) Write(p []byte) (int, error) { log.Print(string(p)) return w.Writer.Write(p) }
and then redefining Stdout:
pt.Stdout = logWriteWrapper{pt.Stdout}
Functions ¶
func Cmethod ¶
Emit a CMETHOD line. socks must be "socks4" or "socks5". Call this once for each listening client SOCKS port.
func CmethodError ¶
Emit a CMETHOD-ERROR line with explanation text. Returns a representation of the error.
func CmethodsDone ¶
func CmethodsDone()
Emit a CMETHODS DONE line. Call this after opening all client listeners.
func DialOr ¶
func DialOr(info *ServerInfo, addr, methodName string) (*net.TCPConn, error)
Dial info.ExtendedOrAddr if defined, or else info.OrAddr, and return an open *net.TCPConn. If connecting to the extended OR port, extended OR port authentication à la 217-ext-orport-auth.txt is done before returning; an error is returned if authentication fails.
The addr and methodName arguments are put in USERADDR and TRANSPORT ExtOrPort commands, respectively. If either is "", the corresponding command is not sent.
func MakeStateDir ¶
Return the directory name in the TOR_PT_STATE_LOCATION environment variable, creating it if it doesn't exist. Returns non-nil error if TOR_PT_STATE_LOCATION is not set or if there is an error creating the directory.
func SmethodArgs ¶
Emit an SMETHOD line with an ARGS option. args is a name–value mapping that will be added to the server's extrainfo document.
This is an example of how to check for a required option:
secret, ok := bindaddr.Options.Get("shared-secret") if ok { args := pt.Args{} args.Add("shared-secret", secret) pt.SmethodArgs(bindaddr.MethodName, ln.Addr(), args) } else { pt.SmethodError(bindaddr.MethodName, "need a shared-secret option") }
Or, if you just want to echo back the options provided by Tor from the TransportServerOptions configuration,
pt.SmethodArgs(bindaddr.MethodName, ln.Addr(), bindaddr.Options)
func SmethodError ¶
Emit an SMETHOD-ERROR line with explanation text. Returns a representation of the error.
func SmethodsDone ¶
func SmethodsDone()
Emit an SMETHODS DONE line. Call this after opening all server listeners.
Types ¶
type Args ¶
Args maps a string key to a list of values. It is similar to url.Values.
func (Args) Get ¶
Get the first value associated with the given key. If there are any values associated with the key, the value return has the value and ok is set to true. If there are no values for the given key, value is "" and ok is false. If you need access to multiple values, use the map directly.
type Bindaddr ¶
type Bindaddr struct { MethodName string Addr *net.TCPAddr // Options from TOR_PT_SERVER_TRANSPORT_OPTIONS that pertain to this // transport. Options Args }
A combination of a method name and an address, as extracted from TOR_PT_SERVER_BINDADDR.
type ClientInfo ¶
type ClientInfo struct {
MethodNames []string
}
This structure is returned by ClientSetup. It consists of a list of method names.
func ClientSetup ¶
func ClientSetup(star []string) (info ClientInfo, err error)
Check the client pluggable transports environment, emitting an error message and returning a non-nil error if any error is encountered. star is the list of method names to use in case "*" is requested by Tor. Returns a ClientInfo struct.
If your program needs to know whether to call ClientSetup or ServerSetup (i.e., if the same program can be run as either a client or a server), check whether the TOR_PT_CLIENT_TRANSPORTS environment variable is set:
if os.Getenv("TOR_PT_CLIENT_TRANSPORTS") != "" { // Client mode; call pt.ClientSetup. } else { // Server mode; call pt.ServerSetup. }
type ServerInfo ¶
type ServerInfo struct { Bindaddrs []Bindaddr OrAddr *net.TCPAddr ExtendedOrAddr *net.TCPAddr AuthCookiePath string }
This structure is returned by ServerSetup. It consists of a list of Bindaddrs, an address for the ORPort, an address for the extended ORPort (if any), and an authentication cookie (if any).
func ServerSetup ¶
func ServerSetup(star []string) (info ServerInfo, err error)
Check the server pluggable transports environment, emitting an error message and returning a non-nil error if any error is encountered. star is the list of method names to use in case "*" is requested by Tor. Resolves the various requested bind addresses, the server ORPort and extended ORPort, and reads the auth cookie file. Returns a ServerInfo struct.
If your program needs to know whether to call ClientSetup or ServerSetup (i.e., if the same program can be run as either a client or a server), check whether the TOR_PT_CLIENT_TRANSPORTS environment variable is set:
if os.Getenv("TOR_PT_CLIENT_TRANSPORTS") != "" { // Client mode; call pt.ClientSetup. } else { // Server mode; call pt.ServerSetup. }
type SocksConn ¶
type SocksConn struct { net.Conn Req SocksRequest }
SocksConn encapsulates a net.Conn and information associated with a SOCKS request.
type SocksListener ¶
SocksListener wraps a net.Listener in order to read a SOCKS request on Accept.
func handleConn(conn *pt.SocksConn) error { defer conn.Close() remote, err := net.Dial("tcp", conn.Req.Target) if err != nil { conn.Reject() return err } defer remote.Close() err = conn.Grant(remote.RemoteAddr().(*net.TCPAddr)) if err != nil { return err } // do something with conn and remote return nil } ... ln, err := pt.ListenSocks("tcp", "127.0.0.1:0") if err != nil { panic(err.Error()) } for { conn, err := ln.AcceptSocks() if err != nil { log.Printf("accept error: %s", err) if e, ok := err.(net.Error); ok && !e.Temporary() { break } continue } go handleConn(conn) }
func ListenSocks ¶
func ListenSocks(network, laddr string) (*SocksListener, error)
Open a net.Listener according to network and laddr, and return it as a SocksListener.
func NewSocksListener ¶
func NewSocksListener(ln net.Listener) *SocksListener
Create a new SocksListener wrapping the given net.Listener.
func (*SocksListener) Accept ¶
func (ln *SocksListener) Accept() (net.Conn, error)
Accept is the same as AcceptSocks, except that it returns a generic net.Conn. It is present for the sake of satisfying the net.Listener interface.
func (*SocksListener) AcceptSocks ¶
func (ln *SocksListener) AcceptSocks() (*SocksConn, error)
Call Accept on the wrapped net.Listener, do SOCKS negotiation, and return a SocksConn. After accepting, you must call either conn.Grant or conn.Reject (presumably after trying to connect to conn.Req.Target).
Errors returned by AcceptSocks may be temporary (for example, EOF while reading the request, or a badly formatted userid string), or permanent (e.g., the underlying socket is closed). You can determine whether an error is temporary and take appropriate action with a type conversion to net.Error. For example:
for { conn, err := ln.AcceptSocks() if err != nil { if e, ok := err.(net.Error); ok && !e.Temporary() { log.Printf("permanent accept error; giving up: %s", err) break } log.Printf("temporary accept error; trying again: %s", err) continue } go handleConn(conn) }
func (*SocksListener) Version ¶
func (ln *SocksListener) Version() string
Returns "socks4", suitable to be included in a call to Cmethod.
type SocksRequest ¶
type SocksRequest struct { // The endpoint requested by the client as a "host:port" string. Target string // The userid string sent by the client. Username string // The parsed contents of Username as a key–value mapping. Args Args }
SocksRequest describes a SOCKS request.
Directories ¶
Path | Synopsis |
---|---|
examples
|
|
dummy-client
Dummy no-op pluggable transport client.
|
Dummy no-op pluggable transport client. |
dummy-server
Dummy no-op pluggable transport server.
|
Dummy no-op pluggable transport server. |