summaryrefslogtreecommitdiff
path: root/obfs4proxy
diff options
context:
space:
mode:
authorYawning Angel <yawning@schwanenlied.me>2014-05-25 10:28:32 +0000
committerYawning Angel <yawning@schwanenlied.me>2014-05-25 10:28:32 +0000
commit3c64e04e0237e7b730c277d7977ac1cf18a483b9 (patch)
treed008edc21bf97573609ff065b07780322d055560 /obfs4proxy
parentf0090b5127689b8673543138662b6163ed792e11 (diff)
Add support for SOCKS4.
Despite the unfortunate scheme name, this really is SOCKS4, and not 4a, as the torrc Socks4Proxy option only supports addresses and not FQDNs. Part of issue #7.
Diffstat (limited to 'obfs4proxy')
-rw-r--r--obfs4proxy/obfs4proxy.go2
-rw-r--r--obfs4proxy/proxy_extras.go107
2 files changed, 108 insertions, 1 deletions
diff --git a/obfs4proxy/obfs4proxy.go b/obfs4proxy/obfs4proxy.go
index c00ee88..2d292ce 100644
--- a/obfs4proxy/obfs4proxy.go
+++ b/obfs4proxy/obfs4proxy.go
@@ -310,7 +310,7 @@ func clientSetup() (launched bool) {
}
if ptClientProxy != nil {
// XXX: Limit this to SOCKS5 for now.
- if ptClientProxy.Scheme != "socks5" {
+ if ptClientProxy.Scheme == "http" {
ptProxyError(fmt.Sprintf("proxy scheme not supported: %s",
ptClientProxy.Scheme))
return
diff --git a/obfs4proxy/proxy_extras.go b/obfs4proxy/proxy_extras.go
index 27b638b..080e6b0 100644
--- a/obfs4proxy/proxy_extras.go
+++ b/obfs4proxy/proxy_extras.go
@@ -28,7 +28,11 @@
package main
import (
+ "errors"
+ "io"
+ "net"
"net/url"
+ "strconv"
"code.google.com/p/go.net/proxy"
@@ -49,3 +53,106 @@ func getProxyDialer(uri *url.URL) (obfs4.DialFn, error) {
return dialer.Dial, nil
}
+
+// socks4 is a SOCKSv4 proxy.
+type socks4 struct {
+ hostPort string
+ username string
+ forward proxy.Dialer
+}
+
+const (
+ socks4Version = 0x04
+ socks4CommandConnect = 0x01
+ socks4Null = 0x00
+ socks4ReplyVersion = 0x00
+
+ socks4Granted = 0x5a
+)
+
+func newSOCKS4(uri *url.URL, forward proxy.Dialer) (proxy.Dialer, error) {
+ s := new(socks4)
+ s.hostPort = uri.Host
+ s.forward = forward
+ if uri.User != nil {
+ s.username = uri.User.Username()
+ }
+ return s, nil
+}
+
+func (s *socks4) 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, errors.New("proxy rejected the connect request")
+ }
+
+ return c, nil
+}
+
+func init() {
+ // Despite the scheme name, this really is SOCKS4.
+ proxy.RegisterDialerType("socks4a", newSOCKS4)
+}