...
Run Format

Source file src/net/http/socks_bundle.go

Documentation: net/http

  // Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT.
  //go:generate bundle -o socks_bundle.go -dst net/http -prefix socks -underscore 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 authention
  	// 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 {
  		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)))
  }
  

View as plain text