| // 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 "<nil>" | |
| } | |
| 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) > 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 cancellation 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))) | |
| } |