diff options
Diffstat (limited to 'obfs4proxy/obfs4proxy.go')
-rw-r--r-- | obfs4proxy/obfs4proxy.go | 354 |
1 files changed, 91 insertions, 263 deletions
diff --git a/obfs4proxy/obfs4proxy.go b/obfs4proxy/obfs4proxy.go index f66f7c0..92a34c0 100644 --- a/obfs4proxy/obfs4proxy.go +++ b/obfs4proxy/obfs4proxy.go @@ -32,22 +32,21 @@ package main import ( "flag" "fmt" - "io" golog "log" "net" - "net/url" "os" "path" - "sync" "syscall" - "golang.org/x/net/proxy" - "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" + "github.com/OperatorFoundation/obfs4/common/log" + "github.com/OperatorFoundation/obfs4/common/termmon" + "github.com/OperatorFoundation/obfs4/common/pt_extras" + "github.com/OperatorFoundation/obfs4/transports" + + "github.com/OperatorFoundation/obfs4/proxies/proxy_socks5" + "github.com/OperatorFoundation/obfs4/proxies/proxy_transparent" + "github.com/OperatorFoundation/obfs4/proxies/proxy_transparent_udp" ) const ( @@ -57,249 +56,7 @@ const ( ) var stateDir string -var termMon *termMonitor - -func clientSetup() (launched bool, listeners []net.Listener) { - ptClientInfo, err := pt.ClientSetup(transports.Transports()) - if err != nil { - golog.Fatal(err) - } - - ptClientProxy, err := ptGetProxy() - if err != nil { - golog.Fatal(err) - } else if ptClientProxy != nil { - ptProxyDone() - } - - // Launch each of the client listeners. - for _, name := range ptClientInfo.MethodNames { - t := transports.Get(name) - if t == nil { - pt.CmethodError(name, "no such transport is supported") - continue - } - - f, err := t.ClientFactory(stateDir) - if err != nil { - pt.CmethodError(name, "failed to get ClientFactory") - continue - } - - ln, err := net.Listen("tcp", socksAddr) - if err != nil { - pt.CmethodError(name, err.Error()) - continue - } - - go clientAcceptLoop(f, ln, ptClientProxy) - pt.Cmethod(name, socks5.Version(), ln.Addr()) - - log.Infof("%s - registered listener: %s", name, ln.Addr()) - - listeners = append(listeners, ln) - launched = true - } - pt.CmethodsDone() - - return -} - -func clientAcceptLoop(f base.ClientFactory, ln net.Listener, proxyURI *url.URL) 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 clientHandler(f, conn, proxyURI) - } -} - -func clientHandler(f base.ClientFactory, conn net.Conn, proxyURI *url.URL) { - defer conn.Close() - termMon.onHandlerStart() - defer termMon.onHandlerFinish() - - name := f.Transport().Name() - - // 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(&socksReq.Args) - if err != nil { - log.Errorf("%s(%s) - invalid arguments: %s", name, addrStr, err) - socksReq.Reply(socks5.ReplyGeneralFailure) - return - } - - // Obtain the proxy dialer if any, and create the outgoing TCP connection. - 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)) - socksReq.Reply(socks5.ReplyGeneralFailure) - return - } - dialFn = dialer.Dial - } - remote, err := f.Dial("tcp", socksReq.Target, dialFn, args) - if err != nil { - log.Errorf("%s(%s) - outgoing connection failed: %s", name, addrStr, log.ElideError(err)) - socksReq.Reply(socks5.ErrorToReplyCode(err)) - return - } - defer remote.Close() - err = socksReq.Reply(socks5.ReplySucceeded) - if err != nil { - log.Errorf("%s(%s) - SOCKS reply failed: %s", name, addrStr, log.ElideError(err)) - return - } - - if err = copyLoop(conn, remote); err != nil { - log.Warnf("%s(%s) - closed connection: %s", name, addrStr, log.ElideError(err)) - } else { - log.Infof("%s(%s) - closed connection", name, addrStr) - } - - return -} - -func serverSetup() (launched bool, listeners []net.Listener) { - ptServerInfo, err := pt.ServerSetup(transports.Transports()) - if err != nil { - golog.Fatal(err) - } - - for _, bindaddr := range ptServerInfo.Bindaddrs { - name := bindaddr.MethodName - t := transports.Get(name) - if t == nil { - pt.SmethodError(name, "no such transport is supported") - continue - } - - f, err := t.ServerFactory(stateDir, &bindaddr.Options) - if err != nil { - pt.SmethodError(name, err.Error()) - continue - } - - ln, err := net.ListenTCP("tcp", bindaddr.Addr) - if err != nil { - pt.SmethodError(name, err.Error()) - continue - } - - go serverAcceptLoop(f, ln, &ptServerInfo) - if args := f.Args(); args != nil { - pt.SmethodArgs(name, ln.Addr(), *args) - } else { - pt.SmethodArgs(name, ln.Addr(), nil) - } - - log.Infof("%s - registered listener: %s", name, log.ElideAddr(ln.Addr().String())) - - listeners = append(listeners, ln) - launched = true - } - pt.SmethodsDone() - - return -} - -func serverAcceptLoop(f base.ServerFactory, ln net.Listener, info *pt.ServerInfo) 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 serverHandler(f, conn, info) - } -} - -func serverHandler(f base.ServerFactory, conn net.Conn, info *pt.ServerInfo) { - defer conn.Close() - termMon.onHandlerStart() - defer termMon.onHandlerFinish() - - name := f.Transport().Name() - addrStr := log.ElideAddr(conn.RemoteAddr().String()) - log.Infof("%s(%s) - new connection", name, addrStr) - - // Instantiate the server transport method and handshake. - remote, err := f.WrapConn(conn) - if err != nil { - log.Warnf("%s(%s) - handshake failed: %s", name, addrStr, log.ElideError(err)) - return - } - - // Connect to the orport. - orConn, err := pt.DialOr(info, conn.RemoteAddr().String(), name) - if err != nil { - log.Errorf("%s(%s) - failed to connect to ORPort: %s", name, addrStr, log.ElideError(err)) - return - } - defer orConn.Close() - - if err = copyLoop(orConn, remote); err != nil { - log.Warnf("%s(%s) - closed connection: %s", name, addrStr, log.ElideError(err)) - } else { - log.Infof("%s(%s) - closed connection", name, addrStr) - } - - return -} - -func copyLoop(a net.Conn, b net.Conn) error { - // Note: b is always the pt connection. a is the SOCKS/ORPort connection. - errChan := make(chan error, 2) - - var wg sync.WaitGroup - wg.Add(2) - - go func() { - defer wg.Done() - defer b.Close() - defer a.Close() - _, err := io.Copy(b, a) - errChan <- err - }() - go func() { - defer wg.Done() - defer a.Close() - defer b.Close() - _, err := io.Copy(a, b) - errChan <- err - }() - - // Wait for both upstream and downstream to close. Since one side - // terminating closes the other, the second error in the channel will be - // something like EINVAL (though io.Copy() will swallow EOF), so only the - // first error is returned. - wg.Wait() - if len(errChan) > 0 { - return <-errChan - } - - return nil -} +var termMon *termmon.TermMonitor func getVersion() string { return fmt.Sprintf("obfs4proxy-%s", obfs4proxyVersion) @@ -307,7 +64,7 @@ func getVersion() string { func main() { // Initialize the termination state monitor as soon as possible. - termMon = newTermMonitor() + termMon = termmon.NewTermMonitor() // Handle the command line arguments. _, execName := path.Split(os.Args[0]) @@ -315,6 +72,13 @@ func main() { logLevelStr := flag.String("logLevel", "ERROR", "Log level (ERROR/WARN/INFO/DEBUG)") enableLogging := flag.Bool("enableLogging", false, "Log to TOR_PT_STATE_LOCATION/"+obfs4proxyLogFile) unsafeLogging := flag.Bool("unsafeLogging", false, "Disable the address scrubber") + transparent := flag.Bool("transparent", false, "Enable transparent proxy mode") + udp := flag.Bool("udp", false, "Enable UDP proxy mode") + target := flag.String("target", "", "Specify transport server destination address") + clientMode := flag.Bool("client", false, "Enable client mode") + serverMode := flag.Bool("server", false, "Enable server mode") + statePath := flag.String("state", "", "Specify transport server destination address") + bindAddr := flag.String("bindaddr", "", "Specify the bind address for transparent server") flag.Parse() if *showVer { @@ -328,11 +92,11 @@ func main() { // Determine if this is a client or server, initialize the common state. var ptListeners []net.Listener launched := false - isClient, err := ptIsClient() + isClient, err := checkIsClient(*clientMode, *serverMode) if err != nil { golog.Fatalf("[ERROR]: %s - must be run as a managed transport", execName) } - if stateDir, err = pt.MakeStateDir(); err != nil { + if stateDir, err = makeStateDir(*statePath); err != nil { golog.Fatalf("[ERROR]: %s - No state directory: %s", execName, err) } if err = log.Init(*enableLogging, path.Join(stateDir, obfs4proxyLogFile), *unsafeLogging); err != nil { @@ -345,14 +109,58 @@ func main() { log.Noticef("%s - launched", getVersion()) - // Do the managed pluggable transport protocol configuration. - if isClient { - log.Infof("%s - initializing client transport listeners", execName) - launched, ptListeners = clientSetup() + if *transparent { + // Do the transparent proxy configuration. + log.Infof("%s - initializing transparent proxy", execName) + if *udp { + log.Infof("%s - initializing UDP transparent proxy", execName) + if isClient { + log.Infof("%s - initializing client transport listeners", execName) + if *target == "" { + log.Errorf("%s - transparent mode requires a target", execName) + } else { + launched = proxy_transparent_udp.ClientSetup(termMon, *target) + } + } else { + log.Infof("%s - initializing server transport listeners", execName) + if *bindAddr == "" { + fmt.Println("%s - transparent mode requires a bindaddr", execName) + } else { + launched = proxy_transparent_udp.ServerSetup(termMon, *bindAddr) + fmt.Println("launched", launched, ptListeners) + } + } + } else { + log.Infof("%s - initializing TCP transparent proxy", execName) + if isClient { + log.Infof("%s - initializing client transport listeners", execName) + if *target == "" { + log.Errorf("%s - transparent mode requires a target", execName) + } else { + launched, ptListeners = proxy_transparent.ClientSetup(termMon, *target) + } + } else { + log.Infof("%s - initializing server transport listeners", execName) + if *bindAddr == "" { + fmt.Println("%s - transparent mode requires a bindaddr", execName) + } else { + launched, ptListeners = proxy_transparent.ServerSetup(termMon, *bindAddr) + fmt.Println("launched", launched, ptListeners) + } + } + } } else { - log.Infof("%s - initializing server transport listeners", execName) - launched, ptListeners = serverSetup() + // Do the managed pluggable transport protocol configuration. + log.Infof("%s - initializing PT 1.0 proxy", execName) + if isClient { + log.Infof("%s - initializing client transport listeners", execName) + launched, ptListeners = proxy_socks5.ClientSetup(termMon) + } else { + log.Infof("%s - initializing server transport listeners", execName) + launched, ptListeners = proxy_socks5.ServerSetup(termMon) + } } + if !launched { // Initialization failed, the client or server setup routines should // have logged, so just exit here. @@ -368,7 +176,7 @@ func main() { // connections will be processed. Wait till the parent dies // (immediate exit), a SIGTERM is received (immediate exit), // or a SIGINT is received. - if sig := termMon.wait(false); sig == syscall.SIGTERM { + if sig := termMon.Wait(false); sig == syscall.SIGTERM { return } @@ -378,5 +186,25 @@ func main() { for _, ln := range ptListeners { ln.Close() } - termMon.wait(true) + + termMon.Wait(true) +} + +func checkIsClient(client bool, server bool) (bool, error) { + if client { + return true, nil + } else if server { + return false, nil + } else { + return pt_extras.PtIsClient() + } +} + +func makeStateDir(statePath string) (string, error) { + if statePath != "" { + err := os.MkdirAll(statePath, 0700) + return statePath, err + } else { + return pt.MakeStateDir() + } } |