// Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT. //go:generate bundle -o socks_bundle.go -prefix socks golang.org/x/net/internal/socks // Package socks provides a SOCKS version 5 client implementation. // // SOCKS protocol version 5 is defined in RFC 1928. // Username/Password authentication for SOCKS version 5 is defined in // RFC 1929. // package http import ( "context" "errors" "io" "net" "strconv" "time" ) var ( socksnoDeadline = time.Time{} socksaLongTimeAgo = time.Unix(1, 0) ) func (d *socksDialer) connect(ctx context.Context, c net.Conn, address string) (_ net.Addr, ctxErr error) { host, port, err := sockssplitHostPort(address) if err != nil { return nil, err } if deadline, ok := ctx.Deadline(); ok && !deadline.IsZero() { c.SetDeadline(deadline) defer c.SetDeadline(socksnoDeadline) } if ctx != context.Background() { errCh := make(chan error, 1) done := make(chan struct{}) defer func() { close(done) if ctxErr == nil { ctxErr = <-errCh } }() go func() { select { case <-ctx.Done(): c.SetDeadline(socksaLongTimeAgo) errCh <- ctx.Err() case <-done: errCh <- nil } }() } b := make([]byte, 0, 6+len(host)) // the size here is just an estimate b = append(b, socksVersion5) if len(d.AuthMethods) == 0 || d.Authenticate == nil { b = append(b, 1, byte(socksAuthMethodNotRequired)) } else { ams := d.AuthMethods if len(ams) > 255 { return nil, errors.New("too many authentication methods") } b = append(b, byte(len(ams))) for _, am := range ams { b = append(b, byte(am)) } } if _, ctxErr = c.Write(b); ctxErr != nil { return } if _, ctxErr = io.ReadFull(c, b[:2]); ctxErr != nil { return } if b[0] != socksVersion5 { return nil, errors.New("unexpected protocol version " + strconv.Itoa(int(b[0]))) } am := socksAuthMethod(b[1]) if am == socksAuthMethodNoAcceptableMethods { return nil, errors.New("no acceptable authentication methods") } if d.Authenticate != nil { if ctxErr = d.Authenticate(ctx, c, am); ctxErr != nil { return } } b = b[:0] b = append(b, socksVersion5, byte(d.cmd), 0) if ip := net.ParseIP(host); ip != nil { if ip4 := ip.To4(); ip4 != nil { b = append(b, socksAddrTypeIPv4) b = append(b, ip4...) } else if ip6 := ip.To16(); ip6 != nil { b = append(b, socksAddrTypeIPv6) b = append(b, ip6...) } else { return nil, errors.New("unknown address type") } } else { if len(host) > 255 { return nil, errors.New("FQDN too long") } b = append(b, socksAddrTypeFQDN) b = append(b, byte(len(host))) b = append(b, host...) } b = append(b, byte(port>>8), byte(port)) if _, ctxErr = c.Write(b); ctxErr != nil { return } if _, ctxErr = io.ReadFull(c, b[:4]); ctxErr != nil { return } if b[0] != socksVersion5 { return nil, errors.New("unexpected protocol version " + strconv.Itoa(int(b[0]))) } if cmdErr := socksReply(b[1]); cmdErr != socksStatusSucceeded { return nil, errors.New("unknown error " + cmdErr.String()) } if b[2] != 0 { return nil, errors.New("non-zero reserved field") } l := 2 var a socksAddr switch b[3] { case socksAddrTypeIPv4: l += net.IPv4len a.IP = make(net.IP, net.IPv4len) case socksAddrTypeIPv6: l += net.IPv6len a.IP = make(net.IP, net.IPv6len) case socksAddrTypeFQDN: if _, err := io.ReadFull(c, b[:1]); err != nil { return nil, err } l += int(b[0]) default: return nil, errors.New("unknown address type " + strconv.Itoa(int(b[3]))) } if cap(b) < l { b = make([]byte, l) } else { b = b[:l] } if _, ctxErr = io.ReadFull(c, b); ctxErr != nil { return } if a.IP != nil { copy(a.IP, b) } else { a.Name = string(b[:len(b)-2]) } a.Port = int(b[len(b)-2])<<8 | int(b[len(b)-1]) return &a, nil } func sockssplitHostPort(address string) (string, int, error) { host, port, err := net.SplitHostPort(address) if err != nil { return "", 0, err } portnum, err := strconv.Atoi(port) if err != nil { return "", 0, err } if 1 > portnum || portnum > 0xffff { return "", 0, errors.New("port number out of range " + port) } return host, portnum, nil } // A Command represents a SOCKS command. type socksCommand int func (cmd socksCommand) String() string { switch cmd { case socksCmdConnect: return "socks connect" case sockscmdBind: return "socks bind" default: return "socks " + strconv.Itoa(int(cmd)) } } // An AuthMethod represents a SOCKS authentication method. type socksAuthMethod int // A Reply represents a SOCKS command reply code. type socksReply int func (code socksReply) String() string { switch code { case socksStatusSucceeded: return "succeeded" case 0x01: return "general SOCKS server failure" case 0x02: return "connection not allowed by ruleset" case 0x03: return "network unreachable" case 0x04: return "host unreachable" case 0x05: return "connection refused" case 0x06: return "TTL expired" case 0x07: return "command not supported" case 0x08: return "address type not supported" default: return "unknown code: " + strconv.Itoa(int(code)) } } // Wire protocol constants. const ( socksVersion5 = 0x05 socksAddrTypeIPv4 = 0x01 socksAddrTypeFQDN = 0x03 socksAddrTypeIPv6 = 0x04 socksCmdConnect socksCommand = 0x01 // establishes an active-open forward proxy connection sockscmdBind socksCommand = 0x02 // establishes a passive-open forward proxy connection socksAuthMethodNotRequired socksAuthMethod = 0x00 // no authentication required socksAuthMethodUsernamePassword socksAuthMethod = 0x02 // use username/password socksAuthMethodNoAcceptableMethods socksAuthMethod = 0xff // no acceptable authentication methods socksStatusSucceeded socksReply = 0x00 ) // An Addr represents a SOCKS-specific address. // Either Name or IP is used exclusively. type socksAddr struct { Name string // fully-qualified domain name IP net.IP Port int } func (a *socksAddr) Network() string { return "socks" } func (a *socksAddr) String() string { if a == nil { return "" } port := strconv.Itoa(a.Port) if a.IP == nil { return net.JoinHostPort(a.Name, port) } return net.JoinHostPort(a.IP.String(), port) } // A Conn represents a forward proxy connection. type socksConn struct { net.Conn boundAddr net.Addr } // BoundAddr returns the address assigned by the proxy server for // connecting to the command target address from the proxy server. func (c *socksConn) BoundAddr() net.Addr { if c == nil { return nil } return c.boundAddr } // A Dialer holds SOCKS-specific options. type socksDialer struct { cmd socksCommand // either CmdConnect or cmdBind proxyNetwork string // network between a proxy server and a client proxyAddress string // proxy server address // ProxyDial specifies the optional dial function for // establishing the transport connection. ProxyDial func(context.Context, string, string) (net.Conn, error) // AuthMethods specifies the list of request authentication // methods. // If empty, SOCKS client requests only AuthMethodNotRequired. AuthMethods []socksAuthMethod // Authenticate specifies the optional authentication // function. It must be non-nil when AuthMethods is not empty. // It must return an error when the authentication is failed. Authenticate func(context.Context, io.ReadWriter, socksAuthMethod) error } // DialContext connects to the provided address on the provided // network. // // The returned error value may be a net.OpError. When the Op field of // net.OpError contains "socks", the Source field contains a proxy // server address and the Addr field contains a command target // address. // // See func Dial of the net package of standard library for a // description of the network and address parameters. func (d *socksDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) { if err := d.validateTarget(network, address); err != nil { proxy, dst, _ := d.pathAddrs(address) return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err} } if ctx == nil { proxy, dst, _ := d.pathAddrs(address) return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")} } var err error var c net.Conn if d.ProxyDial != nil { c, err = d.ProxyDial(ctx, d.proxyNetwork, d.proxyAddress) } else { var dd net.Dialer c, err = dd.DialContext(ctx, d.proxyNetwork, d.proxyAddress) } if err != nil { proxy, dst, _ := d.pathAddrs(address) return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err} } a, err := d.connect(ctx, c, address) if err != nil { c.Close() proxy, dst, _ := d.pathAddrs(address) return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err} } return &socksConn{Conn: c, boundAddr: a}, nil } // DialWithConn initiates a connection from SOCKS server to the target // network and address using the connection c that is already // connected to the SOCKS server. // // It returns the connection's local address assigned by the SOCKS // server. func (d *socksDialer) DialWithConn(ctx context.Context, c net.Conn, network, address string) (net.Addr, error) { if err := d.validateTarget(network, address); err != nil { proxy, dst, _ := d.pathAddrs(address) return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err} } if ctx == nil { proxy, dst, _ := d.pathAddrs(address) return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")} } a, err := d.connect(ctx, c, address) if err != nil { proxy, dst, _ := d.pathAddrs(address) return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err} } return a, nil } // Dial connects to the provided address on the provided network. // // Unlike DialContext, it returns a raw transport connection instead // of a forward proxy connection. // // Deprecated: Use DialContext or DialWithConn instead. func (d *socksDialer) Dial(network, address string) (net.Conn, error) { if err := d.validateTarget(network, address); err != nil { proxy, dst, _ := d.pathAddrs(address) return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err} } var err error var c net.Conn if d.ProxyDial != nil { c, err = d.ProxyDial(context.Background(), d.proxyNetwork, d.proxyAddress) } else { c, err = net.Dial(d.proxyNetwork, d.proxyAddress) } if err != nil { proxy, dst, _ := d.pathAddrs(address) return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err} } if _, err := d.DialWithConn(context.Background(), c, network, address); err != nil { c.Close() return nil, err } return c, nil } func (d *socksDialer) validateTarget(network, address string) error { switch network { case "tcp", "tcp6", "tcp4": default: return errors.New("network not implemented") } switch d.cmd { case socksCmdConnect, sockscmdBind: default: return errors.New("command not implemented") } return nil } func (d *socksDialer) pathAddrs(address string) (proxy, dst net.Addr, err error) { for i, s := range []string{d.proxyAddress, address} { host, port, err := sockssplitHostPort(s) if err != nil { return nil, nil, err } a := &socksAddr{Port: port} a.IP = net.ParseIP(host) if a.IP == nil { a.Name = host } if i == 0 { proxy = a } else { dst = a } } return } // NewDialer returns a new Dialer that dials through the provided // proxy server's network and address. func socksNewDialer(network, address string) *socksDialer { return &socksDialer{proxyNetwork: network, proxyAddress: address, cmd: socksCmdConnect} } const ( socksauthUsernamePasswordVersion = 0x01 socksauthStatusSucceeded = 0x00 ) // UsernamePassword are the credentials for the username/password // authentication method. type socksUsernamePassword struct { Username string Password string } // Authenticate authenticates a pair of username and password with the // proxy server. func (up *socksUsernamePassword) Authenticate(ctx context.Context, rw io.ReadWriter, auth socksAuthMethod) error { switch auth { case socksAuthMethodNotRequired: return nil case socksAuthMethodUsernamePassword: if len(up.Username) == 0 || len(up.Username) > 255 || len(up.Password) == 0 || len(up.Password) > 255 { return errors.New("invalid username/password") } b := []byte{socksauthUsernamePasswordVersion} b = append(b, byte(len(up.Username))) b = append(b, up.Username...) b = append(b, byte(len(up.Password))) b = append(b, up.Password...) // TODO(mikio): handle IO deadlines and cancelation if // necessary if _, err := rw.Write(b); err != nil { return err } if _, err := io.ReadFull(rw, b[:2]); err != nil { return err } if b[0] != socksauthUsernamePasswordVersion { return errors.New("invalid username/password version") } if b[1] != socksauthStatusSucceeded { return errors.New("username/password authentication failed") } return nil } return errors.New("unsupported authentication method " + strconv.Itoa(int(auth))) }