summaryrefslogtreecommitdiff
path: root/obfs4proxy
diff options
context:
space:
mode:
Diffstat (limited to 'obfs4proxy')
-rw-r--r--obfs4proxy/obfs4proxy.go354
-rw-r--r--obfs4proxy/proxy_http.go158
-rw-r--r--obfs4proxy/proxy_socks4.go164
-rw-r--r--obfs4proxy/pt_extras.go166
-rw-r--r--obfs4proxy/termmon.go135
-rw-r--r--obfs4proxy/termmon_linux.go49
6 files changed, 91 insertions, 935 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()
+ }
}
diff --git a/obfs4proxy/proxy_http.go b/obfs4proxy/proxy_http.go
deleted file mode 100644
index 6f11790..0000000
--- a/obfs4proxy/proxy_http.go
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (c) 2014, Yawning Angel <yawning at torproject dot org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package main
-
-import (
- "bufio"
- "fmt"
- "net"
- "net/http"
- "net/http/httputil"
- "net/url"
- "time"
-
- "golang.org/x/net/proxy"
-)
-
-// httpProxy is a HTTP connect proxy.
-type httpProxy struct {
- hostPort string
- haveAuth bool
- username string
- password string
- forward proxy.Dialer
-}
-
-func newHTTP(uri *url.URL, forward proxy.Dialer) (proxy.Dialer, error) {
- s := new(httpProxy)
- s.hostPort = uri.Host
- s.forward = forward
- if uri.User != nil {
- s.haveAuth = true
- s.username = uri.User.Username()
- s.password, _ = uri.User.Password()
- }
-
- return s, nil
-}
-
-func (s *httpProxy) Dial(network, addr string) (net.Conn, error) {
- // Dial and create the http client connection.
- c, err := s.forward.Dial("tcp", s.hostPort)
- if err != nil {
- return nil, err
- }
- conn := new(httpConn)
- conn.httpConn = httputil.NewClientConn(c, nil)
- conn.remoteAddr, err = net.ResolveTCPAddr(network, addr)
- if err != nil {
- conn.httpConn.Close()
- return nil, err
- }
-
- // HACK HACK HACK HACK. http.ReadRequest also does this.
- reqURL, err := url.Parse("http://" + addr)
- if err != nil {
- conn.httpConn.Close()
- return nil, err
- }
- reqURL.Scheme = ""
-
- req, err := http.NewRequest("CONNECT", reqURL.String(), nil)
- if err != nil {
- conn.httpConn.Close()
- return nil, err
- }
- req.Close = false
- if s.haveAuth {
- req.SetBasicAuth(s.username, s.password)
- }
- req.Header.Set("User-Agent", "")
-
- resp, err := conn.httpConn.Do(req)
- if err != nil && err != httputil.ErrPersistEOF {
- conn.httpConn.Close()
- return nil, err
- }
- if resp.StatusCode != 200 {
- conn.httpConn.Close()
- return nil, fmt.Errorf("proxy error: %s", resp.Status)
- }
-
- conn.hijackedConn, conn.staleReader = conn.httpConn.Hijack()
- return conn, nil
-}
-
-type httpConn struct {
- remoteAddr *net.TCPAddr
- httpConn *httputil.ClientConn
- hijackedConn net.Conn
- staleReader *bufio.Reader
-}
-
-func (c *httpConn) Read(b []byte) (int, error) {
- if c.staleReader != nil {
- if c.staleReader.Buffered() > 0 {
- return c.staleReader.Read(b)
- }
- c.staleReader = nil
- }
- return c.hijackedConn.Read(b)
-}
-
-func (c *httpConn) Write(b []byte) (int, error) {
- return c.hijackedConn.Write(b)
-}
-
-func (c *httpConn) Close() error {
- return c.hijackedConn.Close()
-}
-
-func (c *httpConn) LocalAddr() net.Addr {
- return c.hijackedConn.LocalAddr()
-}
-
-func (c *httpConn) RemoteAddr() net.Addr {
- return c.remoteAddr
-}
-
-func (c *httpConn) SetDeadline(t time.Time) error {
- return c.hijackedConn.SetDeadline(t)
-}
-
-func (c *httpConn) SetReadDeadline(t time.Time) error {
- return c.hijackedConn.SetReadDeadline(t)
-}
-
-func (c *httpConn) SetWriteDeadline(t time.Time) error {
- return c.hijackedConn.SetWriteDeadline(t)
-}
-
-func init() {
- proxy.RegisterDialerType("http", newHTTP)
-}
diff --git a/obfs4proxy/proxy_socks4.go b/obfs4proxy/proxy_socks4.go
deleted file mode 100644
index 536dd96..0000000
--- a/obfs4proxy/proxy_socks4.go
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (c) 2014, Yawning Angel <yawning at torproject dot org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- * This is inspired by go.net/proxy/socks5.go:
- *
- * Copyright 2011 The Go Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style
- * license that can be found in the LICENSE file.
- */
-
-package main
-
-import (
- "errors"
- "fmt"
- "io"
- "net"
- "net/url"
- "strconv"
-
- "golang.org/x/net/proxy"
-)
-
-// socks4Proxy is a SOCKS4 proxy.
-type socks4Proxy struct {
- hostPort string
- username string
- forward proxy.Dialer
-}
-
-const (
- socks4Version = 0x04
- socks4CommandConnect = 0x01
- socks4Null = 0x00
- socks4ReplyVersion = 0x00
-
- socks4Granted = 0x5a
- socks4Rejected = 0x5b
- socks4RejectedIdentdFailed = 0x5c
- socks4RejectedIdentdMismatch = 0x5d
-)
-
-func newSOCKS4(uri *url.URL, forward proxy.Dialer) (proxy.Dialer, error) {
- s := new(socks4Proxy)
- s.hostPort = uri.Host
- s.forward = forward
- if uri.User != nil {
- s.username = uri.User.Username()
- }
- return s, nil
-}
-
-func (s *socks4Proxy) Dial(network, addr string) (net.Conn, error) {
- if network != "tcp" && network != "tcp4" {
- return nil, errors.New("invalid network type")
- }
-
- // Deal with the destination address/string.
- ipStr, portStr, err := net.SplitHostPort(addr)
- if err != nil {
- return nil, err
- }
- ip := net.ParseIP(ipStr)
- if ip == nil {
- return nil, errors.New("failed to parse destination IP")
- }
- ip4 := ip.To4()
- if ip4 == nil {
- return nil, errors.New("destination address is not IPv4")
- }
- port, err := strconv.ParseUint(portStr, 10, 16)
- if err != nil {
- return nil, err
- }
-
- // Connect to the proxy.
- c, err := s.forward.Dial("tcp", s.hostPort)
- if err != nil {
- return nil, err
- }
-
- // Make/write the request:
- // +----+----+----+----+----+----+----+----+----+----+....+----+
- // | VN | CD | DSTPORT | DSTIP | USERID |NULL|
- // +----+----+----+----+----+----+----+----+----+----+....+----+
-
- req := make([]byte, 0, 9+len(s.username))
- req = append(req, socks4Version)
- req = append(req, socks4CommandConnect)
- req = append(req, byte(port>>8), byte(port))
- req = append(req, ip4...)
- if s.username != "" {
- req = append(req, s.username...)
- }
- req = append(req, socks4Null)
- _, err = c.Write(req)
- if err != nil {
- c.Close()
- return nil, err
- }
-
- // Read the response:
- // +----+----+----+----+----+----+----+----+
- // | VN | CD | DSTPORT | DSTIP |
- // +----+----+----+----+----+----+----+----+
-
- var resp [8]byte
- _, err = io.ReadFull(c, resp[:])
- if err != nil {
- c.Close()
- return nil, err
- }
- if resp[0] != socks4ReplyVersion {
- c.Close()
- return nil, errors.New("proxy returned invalid SOCKS4 version")
- }
- if resp[1] != socks4Granted {
- c.Close()
- return nil, fmt.Errorf("proxy error: %s", socks4ErrorToString(resp[1]))
- }
-
- return c, nil
-}
-
-func socks4ErrorToString(code byte) string {
- switch code {
- case socks4Rejected:
- return "request rejected or failed"
- case socks4RejectedIdentdFailed:
- return "request rejected becasue SOCKS server cannot connect to identd on the client"
- case socks4RejectedIdentdMismatch:
- return "request rejected because the client program and identd report different user-ids"
- default:
- return fmt.Sprintf("unknown failure code %x", code)
- }
-}
-
-func init() {
- // Despite the scheme name, this really is SOCKS4.
- proxy.RegisterDialerType("socks4a", newSOCKS4)
-}
diff --git a/obfs4proxy/pt_extras.go b/obfs4proxy/pt_extras.go
deleted file mode 100644
index f490fbc..0000000
--- a/obfs4proxy/pt_extras.go
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Copyright (c) 2014, Yawning Angel <yawning at torproject dot org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package main
-
-import (
- "errors"
- "fmt"
- "net"
- "net/url"
- "os"
- "strconv"
-
- "git.torproject.org/pluggable-transports/goptlib.git"
-)
-
-// This file contains things that probably should be in goptlib but are not
-// yet or are not finalized.
-
-func ptEnvError(msg string) error {
- line := []byte(fmt.Sprintf("ENV-ERROR %s\n", msg))
- pt.Stdout.Write(line)
- return errors.New(msg)
-}
-
-func ptProxyError(msg string) error {
- line := []byte(fmt.Sprintf("PROXY-ERROR %s\n", msg))
- pt.Stdout.Write(line)
- return errors.New(msg)
-}
-
-func ptProxyDone() {
- line := []byte("PROXY DONE\n")
- pt.Stdout.Write(line)
-}
-
-func ptIsClient() (bool, error) {
- clientEnv := os.Getenv("TOR_PT_CLIENT_TRANSPORTS")
- serverEnv := os.Getenv("TOR_PT_SERVER_TRANSPORTS")
- if clientEnv != "" && serverEnv != "" {
- return false, ptEnvError("TOR_PT_[CLIENT,SERVER]_TRANSPORTS both set")
- } else if clientEnv != "" {
- return true, nil
- } else if serverEnv != "" {
- return false, nil
- }
- return false, errors.New("not launched as a managed transport")
-}
-
-func ptGetProxy() (*url.URL, error) {
- specString := os.Getenv("TOR_PT_PROXY")
- if specString == "" {
- return nil, nil
- }
- spec, err := url.Parse(specString)
- if err != nil {
- return nil, ptProxyError(fmt.Sprintf("failed to parse proxy config: %s", err))
- }
-
- // Validate the TOR_PT_PROXY uri.
- if !spec.IsAbs() {
- return nil, ptProxyError("proxy URI is relative, must be absolute")
- }
- if spec.Path != "" {
- return nil, ptProxyError("proxy URI has a path defined")
- }
- if spec.RawQuery != "" {
- return nil, ptProxyError("proxy URI has a query defined")
- }
- if spec.Fragment != "" {
- return nil, ptProxyError("proxy URI has a fragment defined")
- }
-
- switch spec.Scheme {
- case "http":
- // The most forgiving of proxies.
-
- case "socks4a":
- if spec.User != nil {
- _, isSet := spec.User.Password()
- if isSet {
- return nil, ptProxyError("proxy URI specified SOCKS4a and a password")
- }
- }
-
- case "socks5":
- if spec.User != nil {
- // UNAME/PASSWD both must be between 1 and 255 bytes long. (RFC1929)
- user := spec.User.Username()
- passwd, isSet := spec.User.Password()
- if len(user) < 1 || len(user) > 255 {
- return nil, ptProxyError("proxy URI specified a invalid SOCKS5 username")
- }
- if !isSet || len(passwd) < 1 || len(passwd) > 255 {
- return nil, ptProxyError("proxy URI specified a invalid SOCKS5 password")
- }
- }
-
- default:
- return nil, ptProxyError(fmt.Sprintf("proxy URI has invalid scheme: %s", spec.Scheme))
- }
-
- _, err = resolveAddrStr(spec.Host)
- if err != nil {
- return nil, ptProxyError(fmt.Sprintf("proxy URI has invalid host: %s", err))
- }
-
- return spec, nil
-}
-
-// Sigh, pt.resolveAddr() isn't exported. Include our own getto version that
-// doesn't work around #7011, because we don't work with pre-0.2.5.x tor, and
-// all we care about is validation anyway.
-func resolveAddrStr(addrStr string) (*net.TCPAddr, error) {
- ipStr, portStr, err := net.SplitHostPort(addrStr)
- if err != nil {
- return nil, err
- }
-
- if ipStr == "" {
- return nil, net.InvalidAddrError(fmt.Sprintf("address string %q lacks a host part", addrStr))
- }
- if portStr == "" {
- return nil, net.InvalidAddrError(fmt.Sprintf("address string %q lacks a port part", addrStr))
- }
- ip := net.ParseIP(ipStr)
- if ip == nil {
- return nil, net.InvalidAddrError(fmt.Sprintf("not an IP string: %q", ipStr))
- }
- port, err := strconv.ParseUint(portStr, 10, 16)
- if err != nil {
- return nil, net.InvalidAddrError(fmt.Sprintf("not a Port string: %q", portStr))
- }
-
- return &net.TCPAddr{IP: ip, Port: int(port), Zone: ""}, nil
-}
-
-// Feature #15435 adds a new env var for determining if Tor keeps stdin
-// open for use in termination detection.
-func ptShouldExitOnStdinClose() bool {
- return os.Getenv("TOR_PT_EXIT_ON_STDIN_CLOSE") == "1"
-}
diff --git a/obfs4proxy/termmon.go b/obfs4proxy/termmon.go
deleted file mode 100644
index 86db190..0000000
--- a/obfs4proxy/termmon.go
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (c) 2015, Yawning Angel <yawning at torproject dot org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package main
-
-import (
- "io"
- "io/ioutil"
- "os"
- "os/signal"
- "runtime"
- "syscall"
- "time"
-
- "git.torproject.org/pluggable-transports/obfs4.git/common/log"
-)
-
-var termMonitorOSInit func(*termMonitor) error
-
-type termMonitor struct {
- sigChan chan os.Signal
- handlerChan chan int
- numHandlers int
-}
-
-func (m *termMonitor) onHandlerStart() {
- m.handlerChan <- 1
-}
-
-func (m *termMonitor) onHandlerFinish() {
- m.handlerChan <- -1
-}
-
-func (m *termMonitor) wait(termOnNoHandlers bool) os.Signal {
- // Block until a signal has been received, or (optionally) the
- // number of pending handlers has hit 0. In the case of the
- // latter, treat it as if a SIGTERM has been received.
- for {
- select {
- case n := <-m.handlerChan:
- m.numHandlers += n
- case sig := <-m.sigChan:
- return sig
- }
- if termOnNoHandlers && m.numHandlers == 0 {
- return syscall.SIGTERM
- }
- }
-}
-
-func (m *termMonitor) termOnStdinClose() {
- _, err := io.Copy(ioutil.Discard, os.Stdin)
-
- // io.Copy() will return a nil on EOF, since reaching EOF is
- // expected behavior. No matter what, if this unblocks, assume
- // that stdin is closed, and treat that as having received a
- // SIGTERM.
- log.Noticef("Stdin is closed or unreadable: %v", err)
- m.sigChan <- syscall.SIGTERM
-}
-
-func (m *termMonitor) termOnPPIDChange(ppid int) {
- // Under most if not all U*IX systems, the parent PID will change
- // to that of init once the parent dies. There are several notable
- // exceptions (Slowlaris/Android), but the parent PID changes
- // under those platforms as well.
- //
- // Naturally we lose if the parent has died by the time when the
- // Getppid() call was issued in our parent, but, this is better
- // than nothing.
- const ppidPollInterval = 1 * time.Second
- for ppid == os.Getppid() {
- time.Sleep(ppidPollInterval)
- }
-
- // Treat the parent PID changing as the same as having received
- // a SIGTERM.
- log.Noticef("Parent pid changed: %d (was %d)", os.Getppid(), ppid)
- m.sigChan <- syscall.SIGTERM
-}
-
-func newTermMonitor() (m *termMonitor) {
- ppid := os.Getppid()
- m = new(termMonitor)
- m.sigChan = make(chan os.Signal)
- m.handlerChan = make(chan int)
- signal.Notify(m.sigChan, syscall.SIGINT, syscall.SIGTERM)
-
- // If tor supports feature #15435, we can use Stdin being closed as an
- // indication that tor has died, or wants the PT to shutdown for any
- // reason.
- if ptShouldExitOnStdinClose() {
- go m.termOnStdinClose()
- } else {
- // Instead of feature #15435, use various kludges and hacks:
- // * Linux - Platform specific code that should always work.
- // * Other U*IX - Somewhat generic code, that works unless the
- // parent dies before the monitor is initialized.
- if termMonitorOSInit != nil {
- // Errors here are non-fatal, since it might still be
- // possible to fall back to a generic implementation.
- if err := termMonitorOSInit(m); err == nil {
- return
- }
- }
- if runtime.GOOS != "windows" {
- go m.termOnPPIDChange(ppid)
- }
- }
- return
-}
diff --git a/obfs4proxy/termmon_linux.go b/obfs4proxy/termmon_linux.go
deleted file mode 100644
index 9711cfc..0000000
--- a/obfs4proxy/termmon_linux.go
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) 2015, Yawning Angel <yawning at torproject dot org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package main
-
-import (
- "fmt"
- "syscall"
-)
-
-func termMonitorInitLinux(m *termMonitor) error {
- // Use prctl() to have the kernel deliver a SIGTERM if the parent
- // process dies. This beats anything else that can be done before
- // #15435 is implemented.
- _, _, errno := syscall.Syscall(syscall.SYS_PRCTL, syscall.PR_SET_PDEATHSIG, uintptr(syscall.SIGTERM), 0)
- if errno != 0 {
- var err error = errno
- return fmt.Errorf("prctl(PR_SET_PDEATHSIG, SIGTERM) returned: %s", err)
- }
- return nil
-}
-
-func init() {
- termMonitorOSInit = termMonitorInitLinux
-}