github.com/castai/kvisor@v1.7.1-0.20240516114728-b3572a2607b5/pkg/net/packet/socks5.go (about) 1 package packet 2 3 import ( 4 "encoding/binary" 5 "errors" 6 ) 7 8 type SOCKS5MessagesType uint8 9 10 const ( 11 SOCKS5MessageUnknown SOCKS5MessagesType = iota 12 SOCKS5MessageInitialClientRequest 13 SOCKS5MessageInitialServerResponse 14 15 // they request and reply message look identical on the wire 16 // so hence we need to share a constant 17 SOCKS5MessageRequestOrReply 18 ) 19 20 var socks5MessageTypeNames = map[SOCKS5MessagesType]string{ 21 SOCKS5MessageInitialClientRequest: "InitialClientRequest", 22 SOCKS5MessageInitialServerResponse: "InitialServerResponse", 23 SOCKS5MessageRequestOrReply: "RequestOrReply", 24 } 25 26 func (m SOCKS5MessagesType) String() string { 27 if name, found := socks5MessageTypeNames[m]; found { 28 return name 29 } 30 31 return "Unknown" 32 } 33 34 type SOCKS5AddressType uint8 35 36 const ( 37 SOCKS5AddressTypeIPv4 SOCKS5AddressType = 0x01 38 SOCKS5AddressTypeDomainName SOCKS5AddressType = 0x03 39 SOCKS5AddressTypeIPv6 SOCKS5AddressType = 0x04 40 ) 41 42 var socks5SAddressTypeNames = map[SOCKS5AddressType]string{ 43 SOCKS5AddressTypeIPv4: "IPv4", 44 SOCKS5AddressTypeDomainName: "DomainName", 45 SOCKS5AddressTypeIPv6: "IPv6", 46 } 47 48 func (a SOCKS5AddressType) String() string { 49 if name, found := socks5SAddressTypeNames[a]; found { 50 return name 51 } 52 53 return "Unknown" 54 } 55 56 type SOCKS5Message interface { 57 internal() 58 MessageType() SOCKS5MessagesType 59 } 60 61 type SOCKS5InitialClientRequest struct{} 62 63 func (SOCKS5InitialClientRequest) internal() {} 64 func (SOCKS5InitialClientRequest) MessageType() SOCKS5MessagesType { 65 return SOCKS5MessageInitialClientRequest 66 } 67 68 type SOCKS5InitialServerResponse struct{} 69 70 func (SOCKS5InitialServerResponse) internal() {} 71 func (SOCKS5InitialServerResponse) MessageType() SOCKS5MessagesType { 72 return SOCKS5MessageInitialServerResponse 73 } 74 75 type SOCKS5RequestOrReply struct { 76 CmdOrReply uint8 77 AddressType SOCKS5AddressType 78 Address []byte // can either be a domain name, ipv4 or ipv6. check the address type to know how to parse it 79 Port uint16 80 } 81 82 func (SOCKS5RequestOrReply) internal() {} 83 func (SOCKS5RequestOrReply) MessageType() SOCKS5MessagesType { 84 return SOCKS5MessageRequestOrReply 85 } 86 87 var ( 88 ErrSOCKS5InvalidMessage = errors.New("invalid socks5 message") 89 ErrSOCKS5InvalidVersion = errors.New("invalid version set in socks5 payload") 90 ErrSOCKS5InvalidAddressType = errors.New("invalid address type") 91 ) 92 93 // ParseSOCKS5 tries to parses the given data based on https://datatracker.ietf.org/doc/html/rfc1928 94 func ParseSOCKS5(data []byte) (SOCKS5Message, error) { 95 if len(data) < 2 { 96 return nil, ErrSOCKS5InvalidMessage 97 } 98 99 // socks5 messages always start with the number 5 100 if data[0] != 0x05 { 101 return nil, ErrSOCKS5InvalidVersion 102 } 103 104 // only the initial server response matches a len of 2 105 if len(data) == 2 { 106 return SOCKS5InitialServerResponse{}, nil 107 } 108 109 // the client sends a message with potential methods it can connect with. the second field 110 // gives the number of methods. if the message is the same length as the number of message + 2 111 // (since the first two bytes contain other info), we should have an initial client request. 112 // this could in theory clash with client request/server response , but is rather unlikely 113 if int(data[1]+2) == len(data) { 114 return SOCKS5InitialClientRequest{}, nil 115 } 116 117 // the request/response needs more than 4 bytes, but we use at least 4 for parsing 118 if len(data) < 4 { 119 return nil, ErrSOCKS5InvalidMessage 120 } 121 122 // the third byte is reserved to be 0x00 in both request and response 123 if data[2] != 0x00 { 124 return nil, ErrSOCKS5InvalidMessage 125 } 126 addressType := SOCKS5AddressType(data[3]) 127 128 switch addressType { 129 case SOCKS5AddressTypeIPv4: 130 // 4 bytes protocol + 4 bytes ipv4 + 2 bytes port = 10 bytes 131 if len(data) != 10 { 132 return nil, ErrSOCKS5InvalidMessage 133 } 134 135 return SOCKS5RequestOrReply{ 136 CmdOrReply: data[1], 137 AddressType: addressType, 138 Address: data[4:8], 139 Port: binary.BigEndian.Uint16(data[8:]), 140 }, nil 141 142 case SOCKS5AddressTypeIPv6: 143 // 4 bytes protocol + 16 bytes ipv6 + 2 bytes port = 22 bytes 144 if len(data) != 22 { 145 return nil, ErrSOCKS5InvalidMessage 146 } 147 148 return SOCKS5RequestOrReply{ 149 CmdOrReply: data[1], 150 AddressType: addressType, 151 Address: data[4:20], 152 Port: binary.BigEndian.Uint16(data[20:]), 153 }, nil 154 case SOCKS5AddressTypeDomainName: 155 // first byte in addr contains domain name length 156 if len(data) < 5 { 157 return nil, ErrSOCKS5InvalidMessage 158 } 159 numChars := int(data[4]) 160 161 // 4 bytes protocol + 1 byte len + num chars + 2 bytes port 162 if len(data) != 4+1+numChars+2 { 163 return nil, ErrSOCKS5InvalidMessage 164 } 165 166 return SOCKS5RequestOrReply{ 167 CmdOrReply: data[1], 168 AddressType: addressType, 169 Address: data[5 : 5+numChars], 170 Port: binary.BigEndian.Uint16(data[5+numChars:]), 171 }, nil 172 default: 173 return nil, ErrSOCKS5InvalidAddressType 174 } 175 }