summaryrefslogtreecommitdiff
path: root/obfs4proxy/proxy_extras.go
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/proxy_extras.go
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/proxy_extras.go')
-rw-r--r--obfs4proxy/proxy_extras.go107
1 files changed, 107 insertions, 0 deletions
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)
+}