diff options
author | Yawning Angel <yawning@schwanenlied.me> | 2014-05-21 00:26:25 +0000 |
---|---|---|
committer | Yawning Angel <yawning@schwanenlied.me> | 2014-05-21 00:26:25 +0000 |
commit | b50a8bdf244c9d62aa1d13a966c877945ed9cf47 (patch) | |
tree | 38e1898b179ff8ab1592dddb9d2916b930ef329c | |
parent | 676e0428e75a7808e3fac81d777b8cc69e81b712 (diff) |
Finish adding godoc comments to all public interfaces.
-rw-r--r-- | handshake_ntor.go | 18 | ||||
-rw-r--r-- | obfs4.go | 79 | ||||
-rw-r--r-- | obfs4proxy/obfs4proxy.go | 16 |
3 files changed, 92 insertions, 21 deletions
diff --git a/handshake_ntor.go b/handshake_ntor.go index 1aa00bc..bff500c 100644 --- a/handshake_ntor.go +++ b/handshake_ntor.go @@ -63,10 +63,22 @@ const ( inlineSeedFrameLength = framing.FrameOverhead + packetOverhead + seedPacketPayloadLength ) +// ErrMarkNotFoundYet is the error returned when the obfs4 handshake is +// incomplete and requires more data to continue. This error is non-fatal and +// is the equivalent to EAGAIN/EWOULDBLOCK. var ErrMarkNotFoundYet = errors.New("handshake: M_[C,S] not found yet") + +// ErrInvalidHandshake is the error returned when the obfs4 handshake fails due +// to the peer not sending the correct mark. This error is fatal and the +// connection MUST be dropped. var ErrInvalidHandshake = errors.New("handshake: Failed to find M_[C,S]") + +// ErrNtorFailed is the error returned when the ntor handshake fails. This +// error is fatal and the connection MUST be dropped. var ErrNtorFailed = errors.New("handshake: ntor handshake failure") +// InvalidMacError is the error returned when the handshake MACs do not match. +// This error is fatal and the connection MUST be dropped. type InvalidMacError struct { Derived []byte Received []byte @@ -77,6 +89,8 @@ func (e *InvalidMacError) Error() string { hex.EncodeToString(e.Derived), hex.EncodeToString(e.Received)) } +// InvalidAuthError is the error returned when the ntor AUTH tags do not match. +// This error is fatal and the connection MUST be dropped. type InvalidAuthError struct { Derived *ntor.Auth Received *ntor.Auth @@ -363,7 +377,7 @@ func findMarkMac(mark, buf []byte, startPos, maxPos int, fromTail bool) (pos int if endPos > maxPos { endPos = maxPos } - if endPos - startPos < markLength + macLength { + if endPos-startPos < markLength+macLength { return -1 } @@ -389,7 +403,7 @@ func findMarkMac(mark, buf []byte, startPos, maxPos int, fromTail bool) (pos int } // Ensure that there is enough trailing data for the MAC. - if startPos + pos + markLength + macLength > endPos { + if startPos+pos+markLength+macLength > endPos { return -1 } @@ -25,7 +25,10 @@ * POSSIBILITY OF SUCH DAMAGE. */ -// Package obfs4 implements the obfs4 protocol. +// Package obfs4 implements the obfs4 protocol. For the most part, obfs4 +// connections are exposed via the net.Conn and net.Listener interface, though +// accepting connections as a server requires calling ServerHandshake on the +// conn to finish connection establishment. package obfs4 import ( @@ -46,7 +49,7 @@ const ( connectionTimeout = time.Duration(30) * time.Second maxCloseDelayBytes = framing.MaximumSegmentLength * 5 - maxCloseDelay = 60 + maxCloseDelay = 60 ) type connState int @@ -75,7 +78,7 @@ type Obfs4Conn struct { isServer bool // Server side state. - listener *Obfs4Listener + listener *Obfs4Listener startTime time.Time } @@ -116,7 +119,7 @@ func (c *Obfs4Conn) closeAfterDelay() { // I-it's not like I w-wanna handshake with you or anything. B-b-baka! defer c.conn.Close() - delay := time.Duration(c.listener.closeDelay) * time.Second + connectionTimeout + delay := time.Duration(c.listener.closeDelay)*time.Second + connectionTimeout deadline := c.startTime.Add(delay) if time.Now().After(deadline) { return @@ -302,14 +305,22 @@ func (c *Obfs4Conn) serverHandshake(nodeID *ntor.NodeID, keypair *ntor.Keypair) return } +// CanHandshake queries the connection state to see if it is appropriate to +// call ServerHandshake to complete connection establishment. func (c *Obfs4Conn) CanHandshake() bool { return c.state == stateInit } +// CanReadWrite queries the connection state to see if it is possible to read +// and write data. func (c *Obfs4Conn) CanReadWrite() bool { return c.state == stateEstablished } +// ServerHandshake completes the server side of the obfs4 handshake. Servers +// are required to call this after accepting a connection. ServerHandshake +// will treat errors encountered during the handshake as fatal and drop the +// connection before returning. func (c *Obfs4Conn) ServerHandshake() error { // Handshakes when already established are a no-op. if c.CanReadWrite() { @@ -332,6 +343,7 @@ func (c *Obfs4Conn) ServerHandshake() error { return err } +// Read implements the net.Conn Read method. func (c *Obfs4Conn) Read(b []byte) (n int, err error) { if !c.CanReadWrite() { return 0, syscall.EINVAL @@ -350,6 +362,7 @@ func (c *Obfs4Conn) Read(b []byte) (n int, err error) { return } +// WriteTo implements the io.WriterTo WriteTo method. func (c *Obfs4Conn) WriteTo(w io.Writer) (n int64, err error) { if !c.CanReadWrite() { return 0, syscall.EINVAL @@ -386,6 +399,10 @@ func (c *Obfs4Conn) WriteTo(w io.Writer) (n int64, err error) { return } +// Write implements the net.Conn Write method. The obfs4 lengt obfuscation is +// done based on the amount of data passed to Write (each call to Write results +// in up to 2 frames of padding). Passing excessively short buffers to Write +// will result in significant overhead. func (c *Obfs4Conn) Write(b []byte) (n int, err error) { if !c.CanReadWrite() { return 0, syscall.EINVAL @@ -439,6 +456,7 @@ func (c *Obfs4Conn) Write(b []byte) (n int, err error) { return } +// Close closes the connection. func (c *Obfs4Conn) Close() error { if c.conn == nil { return syscall.EINVAL @@ -449,6 +467,7 @@ func (c *Obfs4Conn) Close() error { return c.conn.Close() } +// LocalAddr returns the local network address. func (c *Obfs4Conn) LocalAddr() net.Addr { if c.state == stateClosed { return nil @@ -457,6 +476,7 @@ func (c *Obfs4Conn) LocalAddr() net.Addr { return c.conn.LocalAddr() } +// RemoteAddr returns the remote network address. func (c *Obfs4Conn) RemoteAddr() net.Addr { if c.state == stateClosed { return nil @@ -465,10 +485,13 @@ func (c *Obfs4Conn) RemoteAddr() net.Addr { return c.conn.RemoteAddr() } +// SetDeadline is a convoluted way to get syscall.ENOTSUP. func (c *Obfs4Conn) SetDeadline(t time.Time) error { return syscall.ENOTSUP } +// SetReadDeadline implements the net.Conn SetReadDeadline method. Connections +// must be in the established state (CanReadWrite). func (c *Obfs4Conn) SetReadDeadline(t time.Time) error { if !c.CanReadWrite() { return syscall.EINVAL @@ -477,11 +500,15 @@ func (c *Obfs4Conn) SetReadDeadline(t time.Time) error { return c.conn.SetReadDeadline(t) } +// SetWriteDeadline is a convoluted way to get syscall.ENOTSUP. func (c *Obfs4Conn) SetWriteDeadline(t time.Time) error { return syscall.ENOTSUP } -func Dial(network, address, nodeID, publicKey string) (net.Conn, error) { +// DialObfs4 connects to the remote address on the network, and handshakes with +// the peer's obfs4 Node ID and Identity Public Key. nodeID and publicKey are +// expected as strings containing the Base64 encoded values. +func DialObfs4(network, address, nodeID, publicKey string) (*Obfs4Conn, error) { // Decode the node_id/public_key. pub, err := ntor.PublicKeyFromBase64(publicKey) if err != nil { @@ -516,21 +543,36 @@ func Dial(network, address, nodeID, publicKey string) (net.Conn, error) { return c, nil } -// Obfs4Listener a obfs4 network listener. Servers should use variables of -// type Listener instead of assuming obfs4. +// Obfs4Listener is the implementation of the net.Listener interface for obfs4 +// connections. type Obfs4Listener struct { listener net.Listener keyPair *ntor.Keypair nodeID *ntor.NodeID - seed *DrbgSeed + seed *DrbgSeed closeDelayBytes int - closeDelay int + closeDelay int } +// Accept implements the Accept method of the net.Listener interface; it waits +// for the next call and returns a generic net.Conn. Callers are responsible +// for completing the handshake by calling Obfs4Conn.ServerHandshake(). func (l *Obfs4Listener) Accept() (net.Conn, error) { + conn, err := l.AcceptObfs4() + if err != nil { + return nil, err + } + + return conn, nil +} + +// AcceptObfs4 accepts the next incoming call and returns a new connection. +// Callers are responsible for completing the handshake by calling +// Obfs4Conn.ServerHandshake(). +func (l *Obfs4Listener) AcceptObfs4() (*Obfs4Conn, error) { // Accept a connection. c, err := l.listener.Accept() if err != nil { @@ -552,14 +594,19 @@ func (l *Obfs4Listener) Accept() (net.Conn, error) { return cObfs, nil } +// Close stops listening on the Obfs4 endpoint. Already Accepted connections +// are not closed. func (l *Obfs4Listener) Close() error { return l.listener.Close() } +// Addr returns the listener's network address. func (l *Obfs4Listener) Addr() net.Addr { return l.listener.Addr() } +// PublicKey returns the listener's Identity Public Key, a Base64 encoded +// obfs4.ntor.PublicKey. func (l *Obfs4Listener) PublicKey() string { if l.keyPair == nil { return "" @@ -568,7 +615,19 @@ func (l *Obfs4Listener) PublicKey() string { return l.keyPair.Public().Base64() } -func Listen(network, laddr, nodeID, privateKey, seed string) (net.Listener, error) { +// NodeID returns the listener's NodeID, a Base64 encoded obfs4.ntor.NodeID. +func (l *Obfs4Listener) NodeID() string { + if l.nodeID == nil { + return "" + } + + return l.nodeID.Base64() +} + +// ListenObfs4 annnounces on the network and address, and returns and +// Obfs4Listener. nodeId, privateKey and seed are expected as strings +// containing the Base64 encoded values. +func ListenObfs4(network, laddr, nodeID, privateKey, seed string) (*Obfs4Listener, error) { var err error // Decode node_id/private_key. diff --git a/obfs4proxy/obfs4proxy.go b/obfs4proxy/obfs4proxy.go index 215068d..9b474dd 100644 --- a/obfs4proxy/obfs4proxy.go +++ b/obfs4proxy/obfs4proxy.go @@ -110,7 +110,7 @@ func copyLoop(a, b net.Conn) { wg.Wait() } -func serverHandler(conn net.Conn, info *pt.ServerInfo) error { +func serverHandler(conn *obfs4.Obfs4Conn, info *pt.ServerInfo) error { defer conn.Close() defer logAndRecover() @@ -120,8 +120,7 @@ func serverHandler(conn net.Conn, info *pt.ServerInfo) error { }() // Handshake with the client. - oConn, _ := conn.(*obfs4.Obfs4Conn) - err := oConn.ServerHandshake() + err := conn.ServerHandshake() if err != nil { log.Printf("[WARN] server: Handshake failed: %s", err) return err @@ -139,10 +138,10 @@ func serverHandler(conn net.Conn, info *pt.ServerInfo) error { return nil } -func serverAcceptLoop(ln net.Listener, info *pt.ServerInfo) error { +func serverAcceptLoop(ln *obfs4.Obfs4Listener, info *pt.ServerInfo) error { defer ln.Close() for { - conn, err := ln.Accept() + conn, err := ln.AcceptObfs4() if err != nil { if e, ok := err.(net.Error); ok && !e.Temporary() { return err @@ -183,7 +182,7 @@ func serverSetup() bool { } // Initialize the listener. - ln, err := obfs4.Listen("tcp", bindaddr.Addr.String(), nodeID, + ln, err := obfs4.ListenObfs4("tcp", bindaddr.Addr.String(), nodeID, privateKey, seed) if err != nil { pt.SmethodError(bindaddr.MethodName, err.Error()) @@ -191,10 +190,9 @@ func serverSetup() bool { } // Report the SMETHOD including the parameters. - oLn, _ := ln.(*obfs4.Obfs4Listener) args := pt.Args{} args.Add("node-id", nodeID) - args.Add("public-key", oLn.PublicKey()) + args.Add("public-key", ln.PublicKey()) go serverAcceptLoop(ln, &ptServerInfo) pt.SmethodArgs(bindaddr.MethodName, ln.Addr(), args) ptListeners = append(ptListeners, ln) @@ -231,7 +229,7 @@ func clientHandler(conn *pt.SocksConn) error { }() defer logAndRecover() - remote, err := obfs4.Dial("tcp", conn.Req.Target, nodeID, publicKey) + remote, err := obfs4.DialObfs4("tcp", conn.Req.Target, nodeID, publicKey) if err != nil { log.Printf("[ERROR] client: Handshake failed: %s", err) conn.Reject() |