package socks import ( "bytes" "errors" "io" "net" "strconv" ) const Version = 5 // Error represents a SOCKS error type Error byte func (err Error) Error() string { return "SOCKS5 error: " + strconv.Itoa(int(err)) } // Command is request commands as defined in RFC 1928 section 4. type Command = uint8 // SOCKS request commands as defined in RFC 1928 section 4. const ( CmdConnect Command = 1 CmdBind Command = 2 CmdUDPAssociate Command = 3 ) // SOCKS address types as defined in RFC 1928 section 5. const ( AtypIPv4 = 1 AtypDomainName = 3 AtypIPv6 = 4 ) // MaxAddrLen is the maximum size of SOCKS address in bytes. const MaxAddrLen = 1 + 1 + 255 + 2 // MaxAuthLen is the maximum size of user/password field in SOCKS5 Auth const MaxAuthLen = 255 // Addr represents a SOCKS address as defined in RFC 1928 section 5. type Addr []byte func (a Addr) String() string { var host, port string switch a[0] { case AtypDomainName: hostLen := uint16(a[1]) host = string(a[2 : 2+hostLen]) port = strconv.Itoa((int(a[2+hostLen]) << 8) | int(a[2+hostLen+1])) case AtypIPv4: host = net.IP(a[1 : 1+net.IPv4len]).String() port = strconv.Itoa((int(a[1+net.IPv4len]) << 8) | int(a[1+net.IPv4len+1])) case AtypIPv6: host = net.IP(a[1 : 1+net.IPv6len]).String() port = strconv.Itoa((int(a[1+net.IPv6len]) << 8) | int(a[1+net.IPv6len+1])) } return net.JoinHostPort(host, port) } // SOCKS errors as defined in RFC 1928 section 6. const ( ErrGeneralFailure = Error(1) ErrConnectionNotAllowed = Error(2) ErrNetworkUnreachable = Error(3) ErrHostUnreachable = Error(4) ErrConnectionRefused = Error(5) ErrTTLExpired = Error(6) ErrCommandNotSupported = Error(7) ErrAddressNotSupported = Error(8) ) // Auth errors used to return a specific "Auth failed" error var ErrAuth = errors.New("auth failed") // ClientHandshake fast-tracks SOCKS initialization to get target address to connect on client side. func ClientHandshake(rw io.ReadWriter, addr Addr, command Command) (Addr, error) { buf := make([]byte, MaxAddrLen) var err error // VER, NMETHODS, METHODS _, err = rw.Write([]byte{Version, 1, 0}) if err != nil { return nil, err } // VER, METHOD if _, err := io.ReadFull(rw, buf[:2]); err != nil { return nil, err } if buf[0] != 5 { return nil, errors.New("SOCKS version error") } if buf[1] != 0 { return nil, errors.New("SOCKS need auth") } // VER, CMD, RSV, ADDR if _, err := rw.Write(bytes.Join([][]byte{{5, command, 0}, addr}, []byte{})); err != nil { return nil, err } // VER, REP, RSV if _, err := io.ReadFull(rw, buf[:3]); err != nil { return nil, err } return readAddr(rw, buf) } func readAddr(r io.Reader, b []byte) (Addr, error) { if len(b) < MaxAddrLen { return nil, io.ErrShortBuffer } _, err := io.ReadFull(r, b[:1]) // read 1st byte for address type if err != nil { return nil, err } switch b[0] { case AtypDomainName: _, err = io.ReadFull(r, b[1:2]) // read 2nd byte for domain length if err != nil { return nil, err } domainLength := uint16(b[1]) _, err = io.ReadFull(r, b[2:2+domainLength+2]) return b[:1+1+domainLength+2], err case AtypIPv4: _, err = io.ReadFull(r, b[1:1+net.IPv4len+2]) return b[:1+net.IPv4len+2], err case AtypIPv6: _, err = io.ReadFull(r, b[1:1+net.IPv6len+2]) return b[:1+net.IPv6len+2], err } return nil, ErrAddressNotSupported } // SplitAddr slices a SOCKS address from beginning of b. Returns nil if failed. func SplitAddr(b []byte) Addr { addrLen := 1 if len(b) < addrLen { return nil } switch b[0] { case AtypDomainName: if len(b) < 2 { return nil } addrLen = 1 + 1 + int(b[1]) + 2 case AtypIPv4: addrLen = 1 + net.IPv4len + 2 case AtypIPv6: addrLen = 1 + net.IPv6len + 2 default: return nil } if len(b) < addrLen { return nil } return b[:addrLen] } // ParseAddr parses the address in string s. Returns nil if failed. func ParseAddr(s string) Addr { var addr Addr host, port, err := net.SplitHostPort(s) if err != nil { return nil } if ip := net.ParseIP(host); ip != nil { if ip4 := ip.To4(); ip4 != nil { addr = make([]byte, 1+net.IPv4len+2) addr[0] = AtypIPv4 copy(addr[1:], ip4) } else { addr = make([]byte, 1+net.IPv6len+2) addr[0] = AtypIPv6 copy(addr[1:], ip) } } else { if len(host) > 255 { return nil } addr = make([]byte, 1+1+len(host)+2) addr[0] = AtypDomainName addr[1] = byte(len(host)) copy(addr[2:], host) } portUint, err := strconv.ParseUint(port, 10, 16) if err != nil { return nil } addr[len(addr)-2], addr[len(addr)-1] = byte(portUint>>8), byte(portUint) return addr } func DecodeUDPPacket(packet []byte) (addr Addr, payload []byte, err error) { if len(packet) < 5 { err = errors.New("insufficient length of packet") return } // packet[0] and packet[1] are reserved if !bytes.Equal(packet[:2], []byte{0, 0}) { err = errors.New("reserved fields should be zero") return } if packet[2] != 0 /* fragments */ { err = errors.New("discarding fragmented payload") return } addr = SplitAddr(packet[3:]) if addr == nil { err = errors.New("failed to read UDP header") } payload = make([]byte, len(packet[3+len(addr):])) copy(payload, packet[3+len(addr):]) return } func EncodeUDPPacket(addr Addr, payload []byte) (packet []byte, err error) { if addr == nil { err = errors.New("address is invalid") return } packet = bytes.Join([][]byte{{0, 0, 0}, addr, payload}, []byte{}) return }