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.
merge-requests/3/head
Yawning Angel 10 years ago
parent f0090b5127
commit 3c64e04e02

@ -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

@ -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)
}

Loading…
Cancel
Save