github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/net/http/socks_bundle.go (about)

     1  // Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT.
     2  //go:generate bundle -o socks_bundle.go -prefix socks golang.org/x/net/internal/socks
     3  
     4  // Package socks provides a SOCKS version 5 client implementation.
     5  //
     6  // SOCKS protocol version 5 is defined in RFC 1928.
     7  // Username/Password authentication for SOCKS version 5 is defined in
     8  // RFC 1929.
     9  //
    10  
    11  package http
    12  
    13  import (
    14  	"context"
    15  	"errors"
    16  	"io"
    17  	"net"
    18  	"strconv"
    19  	"time"
    20  )
    21  
    22  var (
    23  	socksnoDeadline   = time.Time{}
    24  	socksaLongTimeAgo = time.Unix(1, 0)
    25  )
    26  
    27  func (d *socksDialer) connect(ctx context.Context, c net.Conn, address string) (_ net.Addr, ctxErr error) {
    28  	host, port, err := sockssplitHostPort(address)
    29  	if err != nil {
    30  		return nil, err
    31  	}
    32  	if deadline, ok := ctx.Deadline(); ok && !deadline.IsZero() {
    33  		c.SetDeadline(deadline)
    34  		defer c.SetDeadline(socksnoDeadline)
    35  	}
    36  	if ctx != context.Background() {
    37  		errCh := make(chan error, 1)
    38  		done := make(chan struct{})
    39  		defer func() {
    40  			close(done)
    41  			if ctxErr == nil {
    42  				ctxErr = <-errCh
    43  			}
    44  		}()
    45  		go func() {
    46  			select {
    47  			case <-ctx.Done():
    48  				c.SetDeadline(socksaLongTimeAgo)
    49  				errCh <- ctx.Err()
    50  			case <-done:
    51  				errCh <- nil
    52  			}
    53  		}()
    54  	}
    55  
    56  	b := make([]byte, 0, 6+len(host)) // the size here is just an estimate
    57  	b = append(b, socksVersion5)
    58  	if len(d.AuthMethods) == 0 || d.Authenticate == nil {
    59  		b = append(b, 1, byte(socksAuthMethodNotRequired))
    60  	} else {
    61  		ams := d.AuthMethods
    62  		if len(ams) > 255 {
    63  			return nil, errors.New("too many authentication methods")
    64  		}
    65  		b = append(b, byte(len(ams)))
    66  		for _, am := range ams {
    67  			b = append(b, byte(am))
    68  		}
    69  	}
    70  	if _, ctxErr = c.Write(b); ctxErr != nil {
    71  		return
    72  	}
    73  
    74  	if _, ctxErr = io.ReadFull(c, b[:2]); ctxErr != nil {
    75  		return
    76  	}
    77  	if b[0] != socksVersion5 {
    78  		return nil, errors.New("unexpected protocol version " + strconv.Itoa(int(b[0])))
    79  	}
    80  	am := socksAuthMethod(b[1])
    81  	if am == socksAuthMethodNoAcceptableMethods {
    82  		return nil, errors.New("no acceptable authentication methods")
    83  	}
    84  	if d.Authenticate != nil {
    85  		if ctxErr = d.Authenticate(ctx, c, am); ctxErr != nil {
    86  			return
    87  		}
    88  	}
    89  
    90  	b = b[:0]
    91  	b = append(b, socksVersion5, byte(d.cmd), 0)
    92  	if ip := net.ParseIP(host); ip != nil {
    93  		if ip4 := ip.To4(); ip4 != nil {
    94  			b = append(b, socksAddrTypeIPv4)
    95  			b = append(b, ip4...)
    96  		} else if ip6 := ip.To16(); ip6 != nil {
    97  			b = append(b, socksAddrTypeIPv6)
    98  			b = append(b, ip6...)
    99  		} else {
   100  			return nil, errors.New("unknown address type")
   101  		}
   102  	} else {
   103  		if len(host) > 255 {
   104  			return nil, errors.New("FQDN too long")
   105  		}
   106  		b = append(b, socksAddrTypeFQDN)
   107  		b = append(b, byte(len(host)))
   108  		b = append(b, host...)
   109  	}
   110  	b = append(b, byte(port>>8), byte(port))
   111  	if _, ctxErr = c.Write(b); ctxErr != nil {
   112  		return
   113  	}
   114  
   115  	if _, ctxErr = io.ReadFull(c, b[:4]); ctxErr != nil {
   116  		return
   117  	}
   118  	if b[0] != socksVersion5 {
   119  		return nil, errors.New("unexpected protocol version " + strconv.Itoa(int(b[0])))
   120  	}
   121  	if cmdErr := socksReply(b[1]); cmdErr != socksStatusSucceeded {
   122  		return nil, errors.New("unknown error " + cmdErr.String())
   123  	}
   124  	if b[2] != 0 {
   125  		return nil, errors.New("non-zero reserved field")
   126  	}
   127  	l := 2
   128  	var a socksAddr
   129  	switch b[3] {
   130  	case socksAddrTypeIPv4:
   131  		l += net.IPv4len
   132  		a.IP = make(net.IP, net.IPv4len)
   133  	case socksAddrTypeIPv6:
   134  		l += net.IPv6len
   135  		a.IP = make(net.IP, net.IPv6len)
   136  	case socksAddrTypeFQDN:
   137  		if _, err := io.ReadFull(c, b[:1]); err != nil {
   138  			return nil, err
   139  		}
   140  		l += int(b[0])
   141  	default:
   142  		return nil, errors.New("unknown address type " + strconv.Itoa(int(b[3])))
   143  	}
   144  	if cap(b) < l {
   145  		b = make([]byte, l)
   146  	} else {
   147  		b = b[:l]
   148  	}
   149  	if _, ctxErr = io.ReadFull(c, b); ctxErr != nil {
   150  		return
   151  	}
   152  	if a.IP != nil {
   153  		copy(a.IP, b)
   154  	} else {
   155  		a.Name = string(b[:len(b)-2])
   156  	}
   157  	a.Port = int(b[len(b)-2])<<8 | int(b[len(b)-1])
   158  	return &a, nil
   159  }
   160  
   161  func sockssplitHostPort(address string) (string, int, error) {
   162  	host, port, err := net.SplitHostPort(address)
   163  	if err != nil {
   164  		return "", 0, err
   165  	}
   166  	portnum, err := strconv.Atoi(port)
   167  	if err != nil {
   168  		return "", 0, err
   169  	}
   170  	if 1 > portnum || portnum > 0xffff {
   171  		return "", 0, errors.New("port number out of range " + port)
   172  	}
   173  	return host, portnum, nil
   174  }
   175  
   176  // A Command represents a SOCKS command.
   177  type socksCommand int
   178  
   179  func (cmd socksCommand) String() string {
   180  	switch cmd {
   181  	case socksCmdConnect:
   182  		return "socks connect"
   183  	case sockscmdBind:
   184  		return "socks bind"
   185  	default:
   186  		return "socks " + strconv.Itoa(int(cmd))
   187  	}
   188  }
   189  
   190  // An AuthMethod represents a SOCKS authentication method.
   191  type socksAuthMethod int
   192  
   193  // A Reply represents a SOCKS command reply code.
   194  type socksReply int
   195  
   196  func (code socksReply) String() string {
   197  	switch code {
   198  	case socksStatusSucceeded:
   199  		return "succeeded"
   200  	case 0x01:
   201  		return "general SOCKS server failure"
   202  	case 0x02:
   203  		return "connection not allowed by ruleset"
   204  	case 0x03:
   205  		return "network unreachable"
   206  	case 0x04:
   207  		return "host unreachable"
   208  	case 0x05:
   209  		return "connection refused"
   210  	case 0x06:
   211  		return "TTL expired"
   212  	case 0x07:
   213  		return "command not supported"
   214  	case 0x08:
   215  		return "address type not supported"
   216  	default:
   217  		return "unknown code: " + strconv.Itoa(int(code))
   218  	}
   219  }
   220  
   221  // Wire protocol constants.
   222  const (
   223  	socksVersion5 = 0x05
   224  
   225  	socksAddrTypeIPv4 = 0x01
   226  	socksAddrTypeFQDN = 0x03
   227  	socksAddrTypeIPv6 = 0x04
   228  
   229  	socksCmdConnect socksCommand = 0x01 // establishes an active-open forward proxy connection
   230  	sockscmdBind    socksCommand = 0x02 // establishes a passive-open forward proxy connection
   231  
   232  	socksAuthMethodNotRequired         socksAuthMethod = 0x00 // no authentication required
   233  	socksAuthMethodUsernamePassword    socksAuthMethod = 0x02 // use username/password
   234  	socksAuthMethodNoAcceptableMethods socksAuthMethod = 0xff // no acceptable authentication methods
   235  
   236  	socksStatusSucceeded socksReply = 0x00
   237  )
   238  
   239  // An Addr represents a SOCKS-specific address.
   240  // Either Name or IP is used exclusively.
   241  type socksAddr struct {
   242  	Name string // fully-qualified domain name
   243  	IP   net.IP
   244  	Port int
   245  }
   246  
   247  func (a *socksAddr) Network() string { return "socks" }
   248  
   249  func (a *socksAddr) String() string {
   250  	if a == nil {
   251  		return "<nil>"
   252  	}
   253  	port := strconv.Itoa(a.Port)
   254  	if a.IP == nil {
   255  		return net.JoinHostPort(a.Name, port)
   256  	}
   257  	return net.JoinHostPort(a.IP.String(), port)
   258  }
   259  
   260  // A Conn represents a forward proxy connection.
   261  type socksConn struct {
   262  	net.Conn
   263  
   264  	boundAddr net.Addr
   265  }
   266  
   267  // BoundAddr returns the address assigned by the proxy server for
   268  // connecting to the command target address from the proxy server.
   269  func (c *socksConn) BoundAddr() net.Addr {
   270  	if c == nil {
   271  		return nil
   272  	}
   273  	return c.boundAddr
   274  }
   275  
   276  // A Dialer holds SOCKS-specific options.
   277  type socksDialer struct {
   278  	cmd          socksCommand // either CmdConnect or cmdBind
   279  	proxyNetwork string       // network between a proxy server and a client
   280  	proxyAddress string       // proxy server address
   281  
   282  	// ProxyDial specifies the optional dial function for
   283  	// establishing the transport connection.
   284  	ProxyDial func(context.Context, string, string) (net.Conn, error)
   285  
   286  	// AuthMethods specifies the list of request authentication
   287  	// methods.
   288  	// If empty, SOCKS client requests only AuthMethodNotRequired.
   289  	AuthMethods []socksAuthMethod
   290  
   291  	// Authenticate specifies the optional authentication
   292  	// function. It must be non-nil when AuthMethods is not empty.
   293  	// It must return an error when the authentication is failed.
   294  	Authenticate func(context.Context, io.ReadWriter, socksAuthMethod) error
   295  }
   296  
   297  // DialContext connects to the provided address on the provided
   298  // network.
   299  //
   300  // The returned error value may be a net.OpError. When the Op field of
   301  // net.OpError contains "socks", the Source field contains a proxy
   302  // server address and the Addr field contains a command target
   303  // address.
   304  //
   305  // See func Dial of the net package of standard library for a
   306  // description of the network and address parameters.
   307  func (d *socksDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
   308  	if err := d.validateTarget(network, address); err != nil {
   309  		proxy, dst, _ := d.pathAddrs(address)
   310  		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
   311  	}
   312  	if ctx == nil {
   313  		proxy, dst, _ := d.pathAddrs(address)
   314  		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")}
   315  	}
   316  	var err error
   317  	var c net.Conn
   318  	if d.ProxyDial != nil {
   319  		c, err = d.ProxyDial(ctx, d.proxyNetwork, d.proxyAddress)
   320  	} else {
   321  		var dd net.Dialer
   322  		c, err = dd.DialContext(ctx, d.proxyNetwork, d.proxyAddress)
   323  	}
   324  	if err != nil {
   325  		proxy, dst, _ := d.pathAddrs(address)
   326  		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
   327  	}
   328  	a, err := d.connect(ctx, c, address)
   329  	if err != nil {
   330  		c.Close()
   331  		proxy, dst, _ := d.pathAddrs(address)
   332  		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
   333  	}
   334  	return &socksConn{Conn: c, boundAddr: a}, nil
   335  }
   336  
   337  // DialWithConn initiates a connection from SOCKS server to the target
   338  // network and address using the connection c that is already
   339  // connected to the SOCKS server.
   340  //
   341  // It returns the connection's local address assigned by the SOCKS
   342  // server.
   343  func (d *socksDialer) DialWithConn(ctx context.Context, c net.Conn, network, address string) (net.Addr, error) {
   344  	if err := d.validateTarget(network, address); err != nil {
   345  		proxy, dst, _ := d.pathAddrs(address)
   346  		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
   347  	}
   348  	if ctx == nil {
   349  		proxy, dst, _ := d.pathAddrs(address)
   350  		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")}
   351  	}
   352  	a, err := d.connect(ctx, c, address)
   353  	if err != nil {
   354  		proxy, dst, _ := d.pathAddrs(address)
   355  		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
   356  	}
   357  	return a, nil
   358  }
   359  
   360  // Dial connects to the provided address on the provided network.
   361  //
   362  // Unlike DialContext, it returns a raw transport connection instead
   363  // of a forward proxy connection.
   364  //
   365  // Deprecated: Use DialContext or DialWithConn instead.
   366  func (d *socksDialer) Dial(network, address string) (net.Conn, error) {
   367  	if err := d.validateTarget(network, address); err != nil {
   368  		proxy, dst, _ := d.pathAddrs(address)
   369  		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
   370  	}
   371  	var err error
   372  	var c net.Conn
   373  	if d.ProxyDial != nil {
   374  		c, err = d.ProxyDial(context.Background(), d.proxyNetwork, d.proxyAddress)
   375  	} else {
   376  		c, err = net.Dial(d.proxyNetwork, d.proxyAddress)
   377  	}
   378  	if err != nil {
   379  		proxy, dst, _ := d.pathAddrs(address)
   380  		return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
   381  	}
   382  	if _, err := d.DialWithConn(context.Background(), c, network, address); err != nil {
   383  		c.Close()
   384  		return nil, err
   385  	}
   386  	return c, nil
   387  }
   388  
   389  func (d *socksDialer) validateTarget(network, address string) error {
   390  	switch network {
   391  	case "tcp", "tcp6", "tcp4":
   392  	default:
   393  		return errors.New("network not implemented")
   394  	}
   395  	switch d.cmd {
   396  	case socksCmdConnect, sockscmdBind:
   397  	default:
   398  		return errors.New("command not implemented")
   399  	}
   400  	return nil
   401  }
   402  
   403  func (d *socksDialer) pathAddrs(address string) (proxy, dst net.Addr, err error) {
   404  	for i, s := range []string{d.proxyAddress, address} {
   405  		host, port, err := sockssplitHostPort(s)
   406  		if err != nil {
   407  			return nil, nil, err
   408  		}
   409  		a := &socksAddr{Port: port}
   410  		a.IP = net.ParseIP(host)
   411  		if a.IP == nil {
   412  			a.Name = host
   413  		}
   414  		if i == 0 {
   415  			proxy = a
   416  		} else {
   417  			dst = a
   418  		}
   419  	}
   420  	return
   421  }
   422  
   423  // NewDialer returns a new Dialer that dials through the provided
   424  // proxy server's network and address.
   425  func socksNewDialer(network, address string) *socksDialer {
   426  	return &socksDialer{proxyNetwork: network, proxyAddress: address, cmd: socksCmdConnect}
   427  }
   428  
   429  const (
   430  	socksauthUsernamePasswordVersion = 0x01
   431  	socksauthStatusSucceeded         = 0x00
   432  )
   433  
   434  // UsernamePassword are the credentials for the username/password
   435  // authentication method.
   436  type socksUsernamePassword struct {
   437  	Username string
   438  	Password string
   439  }
   440  
   441  // Authenticate authenticates a pair of username and password with the
   442  // proxy server.
   443  func (up *socksUsernamePassword) Authenticate(ctx context.Context, rw io.ReadWriter, auth socksAuthMethod) error {
   444  	switch auth {
   445  	case socksAuthMethodNotRequired:
   446  		return nil
   447  	case socksAuthMethodUsernamePassword:
   448  		if len(up.Username) == 0 || len(up.Username) > 255 || len(up.Password) > 255 {
   449  			return errors.New("invalid username/password")
   450  		}
   451  		b := []byte{socksauthUsernamePasswordVersion}
   452  		b = append(b, byte(len(up.Username)))
   453  		b = append(b, up.Username...)
   454  		b = append(b, byte(len(up.Password)))
   455  		b = append(b, up.Password...)
   456  		// TODO(mikio): handle IO deadlines and cancelation if
   457  		// necessary
   458  		if _, err := rw.Write(b); err != nil {
   459  			return err
   460  		}
   461  		if _, err := io.ReadFull(rw, b[:2]); err != nil {
   462  			return err
   463  		}
   464  		if b[0] != socksauthUsernamePasswordVersion {
   465  			return errors.New("invalid username/password version")
   466  		}
   467  		if b[1] != socksauthStatusSucceeded {
   468  			return errors.New("username/password authentication failed")
   469  		}
   470  		return nil
   471  	}
   472  	return errors.New("unsupported authentication method " + strconv.Itoa(int(auth)))
   473  }