summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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)
+}