From a8d7134f1097bd50803da0e2a86c07524e433b51 Mon Sep 17 00:00:00 2001 From: Yawning Angel Date: Sun, 12 Apr 2015 19:00:46 +0000 Subject: Use a built in SOCKS 5 server instead of goptlibs. Differences from my goptlib branch: * Instead of exposing a net.Listener, just expose a Handshake() routine that takes an existing net.Conn. (#14135 is irrelevant to this socks server. * There's an extra routine for sending back sensible errors on Dial failure instead of "General failure". * The code is slightly cleaner (IMO). Gotchas: * If the goptlib pt.Args datatype or external interface changes, args.go will need to be updated. Tested with obfs3 and obfs4, including IPv6. --- obfs4proxy/obfs4proxy.go | 51 ++++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 25 deletions(-) (limited to 'obfs4proxy/obfs4proxy.go') diff --git a/obfs4proxy/obfs4proxy.go b/obfs4proxy/obfs4proxy.go index 608dd55..33fbce7 100644 --- a/obfs4proxy/obfs4proxy.go +++ b/obfs4proxy/obfs4proxy.go @@ -45,6 +45,7 @@ import ( "git.torproject.org/pluggable-transports/goptlib.git" "git.torproject.org/pluggable-transports/obfs4.git/common/log" + "git.torproject.org/pluggable-transports/obfs4.git/common/socks5" "git.torproject.org/pluggable-transports/obfs4.git/transports" "git.torproject.org/pluggable-transports/obfs4.git/transports/base" ) @@ -58,10 +59,6 @@ const ( var stateDir string var termMon *termMonitor -// DialFn is a function pointer to a function that matches the net.Dialer.Dial -// interface. -type DialFn func(string, string) (net.Conn, error) - func clientSetup() (launched bool, listeners []net.Listener) { ptClientInfo, err := pt.ClientSetup(transports.Transports()) if err != nil { @@ -89,14 +86,14 @@ func clientSetup() (launched bool, listeners []net.Listener) { continue } - ln, err := pt.ListenSocks("tcp", socksAddr) + ln, err := net.Listen("tcp", socksAddr) if err != nil { pt.CmethodError(name, err.Error()) continue } go clientAcceptLoop(f, ln, ptClientProxy) - pt.Cmethod(name, ln.Version(), ln.Addr()) + pt.Cmethod(name, socks5.Version(), ln.Addr()) log.Infof("%s - registered listener: %s", name, ln.Addr()) @@ -108,10 +105,10 @@ func clientSetup() (launched bool, listeners []net.Listener) { return } -func clientAcceptLoop(f base.ClientFactory, ln *pt.SocksListener, proxyURI *url.URL) error { +func clientAcceptLoop(f base.ClientFactory, ln net.Listener, proxyURI *url.URL) error { defer ln.Close() for { - conn, err := ln.AcceptSocks() + conn, err := ln.Accept() if err != nil { if e, ok := err.(net.Error); ok && !e.Temporary() { return err @@ -122,42 +119,46 @@ func clientAcceptLoop(f base.ClientFactory, ln *pt.SocksListener, proxyURI *url. } } -func clientHandler(f base.ClientFactory, conn *pt.SocksConn, proxyURI *url.URL) { +func clientHandler(f base.ClientFactory, conn net.Conn, proxyURI *url.URL) { defer conn.Close() termMon.onHandlerStart() defer termMon.onHandlerFinish() name := f.Transport().Name() - addrStr := log.ElideAddr(conn.Req.Target) - log.Infof("%s(%s) - new connection", name, addrStr) + + // Read the client's SOCKS handshake. + socksReq, err := socks5.Handshake(conn) + if err != nil { + log.Errorf("%s - client failed socks handshake: %s", name, err) + return + } + addrStr := log.ElideAddr(socksReq.Target) // Deal with arguments. - args, err := f.ParseArgs(&conn.Req.Args) + args, err := f.ParseArgs(&socksReq.Args) if err != nil { log.Errorf("%s(%s) - invalid arguments: %s", name, addrStr, err) - conn.Reject() + socksReq.Reply(socks5.ReplyGeneralFailure) return } // Obtain the proxy dialer if any, and create the outgoing TCP connection. - var dialFn DialFn - if proxyURI == nil { - dialFn = proxy.Direct.Dial - } else { - // This is unlikely to happen as the proxy protocol is verified during - // the configuration phase. + dialFn := proxy.Direct.Dial + if proxyURI != nil { dialer, err := proxy.FromURL(proxyURI, proxy.Direct) if err != nil { + // This should basically never happen, since config protocol + // verifies this. log.Errorf("%s(%s) - failed to obtain proxy dialer: %s", name, addrStr, log.ElideError(err)) - conn.Reject() + socksReq.Reply(socks5.ReplyGeneralFailure) return } dialFn = dialer.Dial } - remoteConn, err := dialFn("tcp", conn.Req.Target) // XXX: Allow UDP? + remoteConn, err := dialFn("tcp", socksReq.Target) // XXX: Allow UDP? if err != nil { log.Errorf("%s(%s) - outgoing connection failed: %s", name, addrStr, log.ElideError(err)) - conn.Reject() + socksReq.Reply(socks5.ErrorToReplyCode(err)) return } defer remoteConn.Close() @@ -167,12 +168,12 @@ func clientHandler(f base.ClientFactory, conn *pt.SocksConn, proxyURI *url.URL) remote, err := f.WrapConn(remoteConn, args) if err != nil { log.Errorf("%s(%s) - handshake failed: %s", name, addrStr, log.ElideError(err)) - conn.Reject() + socksReq.Reply(socks5.ReplyGeneralFailure) return } - err = conn.Grant(remoteConn.RemoteAddr().(*net.TCPAddr)) + err = socksReq.Reply(socks5.ReplySucceeded) if err != nil { - log.Errorf("%s(%s) - SOCKS grant failed: %s", name, addrStr, log.ElideError(err)) + log.Errorf("%s(%s) - SOCKS reply failed: %s", name, addrStr, log.ElideError(err)) return } -- cgit v1.2.3