summaryrefslogtreecommitdiff
path: root/obfs4proxy/obfs4proxy.go
diff options
context:
space:
mode:
Diffstat (limited to 'obfs4proxy/obfs4proxy.go')
-rw-r--r--obfs4proxy/obfs4proxy.go354
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()
+ }
}