package socks import ( "fmt" "io" "net" "strconv" ) const ( // version socks5Version = uint8(5) // commands https://tools.ietf.org/html/rfc1928#section-4 connectCommand = uint8(1) bindCommand = uint8(2) associateCommand = uint8(3) // address types ipv4Address = uint8(1) fqdnAddress = uint8(3) ipv6Address = uint8(4) ) // https://tools.ietf.org/html/rfc1928#section-6 const ( successReply uint8 = iota serverFailure ruleFailure networkUnreachable hostUnreachable connectionRefused ttlExpired commandNotSupported addrTypeNotSupported ) // AddrSpec is used to return the target IPv4, IPv6, or a FQDN type AddrSpec struct { FQDN string IP net.IP Port int } // String gives a host version of the Address func (a *AddrSpec) String() string { if a.FQDN != "" { return fmt.Sprintf("%s (%s):%d", a.FQDN, a.IP, a.Port) } return fmt.Sprintf("%s:%d", a.IP, a.Port) } // Address returns a string suitable to dial; prefer returning IP-based // address, fallback to FQDN func (a AddrSpec) Address() string { if len(a.IP) != 0 { return net.JoinHostPort(a.IP.String(), strconv.Itoa(a.Port)) } return net.JoinHostPort(a.FQDN, strconv.Itoa(a.Port)) } // Request is a SOCKS5 command with supporting field of the connection type Request struct { // Protocol version Version uint8 // Requested command Command uint8 // AddrSpec of the destination DestAddr *AddrSpec // reading from the connection bufConn io.Reader } // NewRequest creates a new request from the connection data stream func NewRequest(bufConn io.Reader) (*Request, error) { // Read the version byte header := []byte{0, 0, 0} if _, err := io.ReadAtLeast(bufConn, header, 3); err != nil { return nil, fmt.Errorf("Failed to get command version: %v", err) } // ensure compatibility if header[0] != socks5Version { return nil, fmt.Errorf("Unsupported command version: %v", header[0]) } // Read in the destination address dest, err := readAddrSpec(bufConn) if err != nil { return nil, err } return &Request{ Version: socks5Version, Command: header[1], DestAddr: dest, bufConn: bufConn, }, nil } func sendReply(w io.Writer, resp uint8, addr *AddrSpec) error { var addrType uint8 var addrBody []byte var addrPort uint16 switch { case addr == nil: addrType = ipv4Address addrBody = []byte{0, 0, 0, 0} addrPort = 0 case addr.FQDN != "": addrType = fqdnAddress addrBody = append([]byte{byte(len(addr.FQDN))}, addr.FQDN...) addrPort = uint16(addr.Port) case addr.IP.To4() != nil: addrType = ipv4Address addrBody = []byte(addr.IP.To4()) addrPort = uint16(addr.Port) case addr.IP.To16() != nil: addrType = ipv6Address addrBody = []byte(addr.IP.To16()) addrPort = uint16(addr.Port) default: return fmt.Errorf("Failed to format address: %v", addr) } // Format the message msg := make([]byte, 6+len(addrBody)) msg[0] = socks5Version msg[1] = resp msg[2] = 0 // Reserved msg[3] = addrType copy(msg[4:], addrBody) msg[4+len(addrBody)] = byte(addrPort >> 8) msg[4+len(addrBody)+1] = byte(addrPort & 0xff) // Send the message _, err := w.Write(msg) return err } // readAddrSpec is used to read AddrSpec. // Expects an address type byte, followed by the address and port func readAddrSpec(r io.Reader) (*AddrSpec, error) { d := &AddrSpec{} // Get the address type addrType := []byte{0} if _, err := r.Read(addrType); err != nil { return nil, err } // Handle on a per type basis switch addrType[0] { case ipv4Address: addr := make([]byte, 4) if _, err := io.ReadAtLeast(r, addr, len(addr)); err != nil { return nil, err } d.IP = net.IP(addr) case ipv6Address: addr := make([]byte, 16) if _, err := io.ReadAtLeast(r, addr, len(addr)); err != nil { return nil, err } d.IP = net.IP(addr) case fqdnAddress: if _, err := r.Read(addrType); err != nil { return nil, err } addrLen := int(addrType[0]) fqdn := make([]byte, addrLen) if _, err := io.ReadAtLeast(r, fqdn, addrLen); err != nil { return nil, err } d.FQDN = string(fqdn) default: return nil, fmt.Errorf("Unrecognized address type") } // Read the port port := []byte{0, 0} if _, err := io.ReadAtLeast(r, port, 2); err != nil { return nil, err } d.Port = (int(port[0]) << 8) | int(port[1]) return d, nil }