@@ -184,6 +184,48 @@ struct timeval MillisToTimeval(int64_t nTimeout)
184184 return timeout;
185185}
186186
187+ /* * SOCKS version */
188+ enum SOCKSVersion: uint8_t {
189+ SOCKS4 = 0x04 ,
190+ SOCKS5 = 0x05
191+ };
192+
193+ /* * Values defined for METHOD in RFC1928 */
194+ enum SOCKS5Method: uint8_t {
195+ NOAUTH = 0x00 , // ! No authentication required
196+ GSSAPI = 0x01 , // ! GSSAPI
197+ USER_PASS = 0x02 , // ! Username/password
198+ NO_ACCEPTABLE = 0xff , // ! No acceptable methods
199+ };
200+
201+ /* * Values defined for CMD in RFC1928 */
202+ enum SOCKS5Command: uint8_t {
203+ CONNECT = 0x01 ,
204+ BIND = 0x02 ,
205+ UDP_ASSOCIATE = 0x03
206+ };
207+
208+ /* * Values defined for REP in RFC1928 */
209+ enum SOCKS5Reply: uint8_t {
210+ SUCCEEDED = 0x00 , // ! Succeeded
211+ GENFAILURE = 0x01 , // ! General failure
212+ NOTALLOWED = 0x02 , // ! Connection not allowed by ruleset
213+ NETUNREACHABLE = 0x03 , // ! Network unreachable
214+ HOSTUNREACHABLE = 0x04 , // ! Network unreachable
215+ CONNREFUSED = 0x05 , // ! Connection refused
216+ TTLEXPIRED = 0x06 , // ! TTL expired
217+ CMDUNSUPPORTED = 0x07 , // ! Command not supported
218+ ATYPEUNSUPPORTED = 0x08 , // ! Address type not supported
219+ };
220+
221+ /* * Values defined for ATYPE in RFC1928 */
222+ enum SOCKS5Atyp: uint8_t {
223+ IPV4 = 0x01 ,
224+ DOMAINNAME = 0x03 ,
225+ IPV6 = 0x04 ,
226+ };
227+
228+ /* * Status codes that can be returned by InterruptibleRecv */
187229enum class IntrRecvError {
188230 OK,
189231 Timeout,
@@ -203,15 +245,15 @@ enum class IntrRecvError {
203245 *
204246 * @note This function requires that hSocket is in non-blocking mode.
205247 */
206- static IntrRecvError InterruptibleRecv (char * data, size_t len, int timeout, const SOCKET& hSocket)
248+ static IntrRecvError InterruptibleRecv (uint8_t * data, size_t len, int timeout, const SOCKET& hSocket)
207249{
208250 int64_t curTime = GetTimeMillis ();
209251 int64_t endTime = curTime + timeout;
210252 // Maximum time to wait in one select call. It will take up until this time (in millis)
211253 // to break off in case of an interruption.
212254 const int64_t maxWait = 1000 ;
213255 while (len > 0 && curTime < endTime) {
214- ssize_t ret = recv (hSocket, data, len, 0 ); // Optimistically try the recv first
256+ ssize_t ret = recv (hSocket, ( char *) data, len, 0 ); // Optimistically try the recv first
215257 if (ret > 0 ) {
216258 len -= ret;
217259 data += ret;
@@ -242,24 +284,35 @@ static IntrRecvError InterruptibleRecv(char* data, size_t len, int timeout, cons
242284 return len == 0 ? IntrRecvError::OK : IntrRecvError::Timeout;
243285}
244286
287+ /* * Credentials for proxy authentication */
245288struct ProxyCredentials
246289{
247290 std::string username;
248291 std::string password;
249292};
250293
251- std::string Socks5ErrorString (int err)
294+ /* * Convert SOCKS5 reply to a an error message */
295+ std::string Socks5ErrorString (uint8_t err)
252296{
253297 switch (err) {
254- case 0x01 : return " general failure" ;
255- case 0x02 : return " connection not allowed" ;
256- case 0x03 : return " network unreachable" ;
257- case 0x04 : return " host unreachable" ;
258- case 0x05 : return " connection refused" ;
259- case 0x06 : return " TTL expired" ;
260- case 0x07 : return " protocol error" ;
261- case 0x08 : return " address type not supported" ;
262- default : return " unknown" ;
298+ case SOCKS5Reply::GENFAILURE:
299+ return " general failure" ;
300+ case SOCKS5Reply::NOTALLOWED:
301+ return " connection not allowed" ;
302+ case SOCKS5Reply::NETUNREACHABLE:
303+ return " network unreachable" ;
304+ case SOCKS5Reply::HOSTUNREACHABLE:
305+ return " host unreachable" ;
306+ case SOCKS5Reply::CONNREFUSED:
307+ return " connection refused" ;
308+ case SOCKS5Reply::TTLEXPIRED:
309+ return " TTL expired" ;
310+ case SOCKS5Reply::CMDUNSUPPORTED:
311+ return " protocol error" ;
312+ case SOCKS5Reply::ATYPEUNSUPPORTED:
313+ return " address type not supported" ;
314+ default :
315+ return " unknown" ;
263316 }
264317}
265318
@@ -274,34 +327,34 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
274327 }
275328 // Accepted authentication methods
276329 std::vector<uint8_t > vSocks5Init;
277- vSocks5Init.push_back (0x05 );
330+ vSocks5Init.push_back (SOCKSVersion::SOCKS5 );
278331 if (auth) {
279- vSocks5Init.push_back (0x02 ); // # METHODS
280- vSocks5Init.push_back (0x00 ); // X'00' NO AUTHENTICATION REQUIRED
281- vSocks5Init.push_back (0x02 ); // X'02' USERNAME/PASSWORD (RFC1929)
332+ vSocks5Init.push_back (0x02 ); // Number of methods
333+ vSocks5Init.push_back (SOCKS5Method::NOAUTH);
334+ vSocks5Init.push_back (SOCKS5Method::USER_PASS);
282335 } else {
283- vSocks5Init.push_back (0x01 ); // # METHODS
284- vSocks5Init.push_back (0x00 ); // X'00' NO AUTHENTICATION REQUIRED
336+ vSocks5Init.push_back (0x01 ); // Number of methods
337+ vSocks5Init.push_back (SOCKS5Method::NOAUTH);
285338 }
286339 ssize_t ret = send (hSocket, (const char *)vSocks5Init.data (), vSocks5Init.size (), MSG_NOSIGNAL);
287340 if (ret != (ssize_t )vSocks5Init.size ()) {
288341 CloseSocket (hSocket);
289342 return error (" Error sending to proxy" );
290343 }
291- char pchRet1[2 ];
344+ uint8_t pchRet1[2 ];
292345 if ((recvr = InterruptibleRecv (pchRet1, 2 , SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) {
293346 CloseSocket (hSocket);
294347 LogPrintf (" Socks5() connect to %s:%d failed: InterruptibleRecv() timeout or other failure\n " , strDest, port);
295348 return false ;
296349 }
297- if (pchRet1[0 ] != 0x05 ) {
350+ if (pchRet1[0 ] != SOCKSVersion::SOCKS5 ) {
298351 CloseSocket (hSocket);
299352 return error (" Proxy failed to initialize" );
300353 }
301- if (pchRet1[1 ] == 0x02 && auth) {
354+ if (pchRet1[1 ] == SOCKS5Method::USER_PASS && auth) {
302355 // Perform username/password authentication (as described in RFC1929)
303356 std::vector<uint8_t > vAuth;
304- vAuth.push_back (0x01 );
357+ vAuth.push_back (0x01 ); // Current (and only) version of user/pass subnegotiation
305358 if (auth->username .size () > 255 || auth->password .size () > 255 )
306359 return error (" Proxy username or password too long" );
307360 vAuth.push_back (auth->username .size ());
@@ -314,7 +367,7 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
314367 return error (" Error sending authentication to proxy" );
315368 }
316369 LogPrint (BCLog::PROXY, " SOCKS5 sending proxy authentication %s:%s\n " , auth->username , auth->password );
317- char pchRetA[2 ];
370+ uint8_t pchRetA[2 ];
318371 if ((recvr = InterruptibleRecv (pchRetA, 2 , SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) {
319372 CloseSocket (hSocket);
320373 return error (" Error reading proxy authentication response" );
@@ -323,17 +376,17 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
323376 CloseSocket (hSocket);
324377 return error (" Proxy authentication unsuccessful" );
325378 }
326- } else if (pchRet1[1 ] == 0x00 ) {
379+ } else if (pchRet1[1 ] == SOCKS5Method::NOAUTH ) {
327380 // Perform no authentication
328381 } else {
329382 CloseSocket (hSocket);
330383 return error (" Proxy requested wrong authentication method %02x" , pchRet1[1 ]);
331384 }
332385 std::vector<uint8_t > vSocks5;
333- vSocks5.push_back (0x05 ); // VER protocol version
334- vSocks5.push_back (0x01 ); // CMD CONNECT
335- vSocks5.push_back (0x00 ); // RSV Reserved
336- vSocks5.push_back (0x03 ); // ATYP DOMAINNAME
386+ vSocks5.push_back (SOCKSVersion::SOCKS5 ); // VER protocol version
387+ vSocks5.push_back (SOCKS5Command::CONNECT ); // CMD CONNECT
388+ vSocks5.push_back (0x00 ); // RSV Reserved must be 0
389+ vSocks5.push_back (SOCKS5Atyp::DOMAINNAME ); // ATYP DOMAINNAME
337390 vSocks5.push_back (strDest.size ()); // Length<=255 is checked at beginning of function
338391 vSocks5.insert (vSocks5.end (), strDest.begin (), strDest.end ());
339392 vSocks5.push_back ((port >> 8 ) & 0xFF );
@@ -343,7 +396,7 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
343396 CloseSocket (hSocket);
344397 return error (" Error sending to proxy" );
345398 }
346- char pchRet2[4 ];
399+ uint8_t pchRet2[4 ];
347400 if ((recvr = InterruptibleRecv (pchRet2, 4 , SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) {
348401 CloseSocket (hSocket);
349402 if (recvr == IntrRecvError::Timeout) {
@@ -355,26 +408,26 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
355408 return error (" Error while reading proxy response" );
356409 }
357410 }
358- if (pchRet2[0 ] != 0x05 ) {
411+ if (pchRet2[0 ] != SOCKSVersion::SOCKS5 ) {
359412 CloseSocket (hSocket);
360413 return error (" Proxy failed to accept request" );
361414 }
362- if (pchRet2[1 ] != 0x00 ) {
415+ if (pchRet2[1 ] != SOCKS5Reply::SUCCEEDED ) {
363416 // Failures to connect to a peer that are not proxy errors
364417 CloseSocket (hSocket);
365418 LogPrintf (" Socks5() connect to %s:%d failed: %s\n " , strDest, port, Socks5ErrorString (pchRet2[1 ]));
366419 return false ;
367420 }
368- if (pchRet2[2 ] != 0x00 ) {
421+ if (pchRet2[2 ] != 0x00 ) { // Reserved field must be 0
369422 CloseSocket (hSocket);
370423 return error (" Error: malformed proxy response" );
371424 }
372- char pchRet3[256 ];
425+ uint8_t pchRet3[256 ];
373426 switch (pchRet2[3 ])
374427 {
375- case 0x01 : recvr = InterruptibleRecv (pchRet3, 4 , SOCKS5_RECV_TIMEOUT, hSocket); break ;
376- case 0x04 : recvr = InterruptibleRecv (pchRet3, 16 , SOCKS5_RECV_TIMEOUT, hSocket); break ;
377- case 0x03 :
428+ case SOCKS5Atyp::IPV4 : recvr = InterruptibleRecv (pchRet3, 4 , SOCKS5_RECV_TIMEOUT, hSocket); break ;
429+ case SOCKS5Atyp::IPV6 : recvr = InterruptibleRecv (pchRet3, 16 , SOCKS5_RECV_TIMEOUT, hSocket); break ;
430+ case SOCKS5Atyp::DOMAINNAME :
378431 {
379432 recvr = InterruptibleRecv (pchRet3, 1 , SOCKS5_RECV_TIMEOUT, hSocket);
380433 if (recvr != IntrRecvError::OK) {
0 commit comments