@@ -184,6 +184,48 @@ struct timeval MillisToTimeval(int64_t nTimeout)
184
184
return timeout;
185
185
}
186
186
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 */
187
229
enum class IntrRecvError {
188
230
OK,
189
231
Timeout,
@@ -203,15 +245,15 @@ enum class IntrRecvError {
203
245
*
204
246
* @note This function requires that hSocket is in non-blocking mode.
205
247
*/
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)
207
249
{
208
250
int64_t curTime = GetTimeMillis ();
209
251
int64_t endTime = curTime + timeout;
210
252
// Maximum time to wait in one select call. It will take up until this time (in millis)
211
253
// to break off in case of an interruption.
212
254
const int64_t maxWait = 1000 ;
213
255
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
215
257
if (ret > 0 ) {
216
258
len -= ret;
217
259
data += ret;
@@ -242,24 +284,35 @@ static IntrRecvError InterruptibleRecv(char* data, size_t len, int timeout, cons
242
284
return len == 0 ? IntrRecvError::OK : IntrRecvError::Timeout;
243
285
}
244
286
287
+ /* * Credentials for proxy authentication */
245
288
struct ProxyCredentials
246
289
{
247
290
std::string username;
248
291
std::string password;
249
292
};
250
293
251
- std::string Socks5ErrorString (int err)
294
+ /* * Convert SOCKS5 reply to a an error message */
295
+ std::string Socks5ErrorString (uint8_t err)
252
296
{
253
297
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" ;
263
316
}
264
317
}
265
318
@@ -274,34 +327,34 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
274
327
}
275
328
// Accepted authentication methods
276
329
std::vector<uint8_t > vSocks5Init;
277
- vSocks5Init.push_back (0x05 );
330
+ vSocks5Init.push_back (SOCKSVersion::SOCKS5 );
278
331
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);
282
335
} 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);
285
338
}
286
339
ssize_t ret = send (hSocket, (const char *)vSocks5Init.data (), vSocks5Init.size (), MSG_NOSIGNAL);
287
340
if (ret != (ssize_t )vSocks5Init.size ()) {
288
341
CloseSocket (hSocket);
289
342
return error (" Error sending to proxy" );
290
343
}
291
- char pchRet1[2 ];
344
+ uint8_t pchRet1[2 ];
292
345
if ((recvr = InterruptibleRecv (pchRet1, 2 , SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) {
293
346
CloseSocket (hSocket);
294
347
LogPrintf (" Socks5() connect to %s:%d failed: InterruptibleRecv() timeout or other failure\n " , strDest, port);
295
348
return false ;
296
349
}
297
- if (pchRet1[0 ] != 0x05 ) {
350
+ if (pchRet1[0 ] != SOCKSVersion::SOCKS5 ) {
298
351
CloseSocket (hSocket);
299
352
return error (" Proxy failed to initialize" );
300
353
}
301
- if (pchRet1[1 ] == 0x02 && auth) {
354
+ if (pchRet1[1 ] == SOCKS5Method::USER_PASS && auth) {
302
355
// Perform username/password authentication (as described in RFC1929)
303
356
std::vector<uint8_t > vAuth;
304
- vAuth.push_back (0x01 );
357
+ vAuth.push_back (0x01 ); // Current (and only) version of user/pass subnegotiation
305
358
if (auth->username .size () > 255 || auth->password .size () > 255 )
306
359
return error (" Proxy username or password too long" );
307
360
vAuth.push_back (auth->username .size ());
@@ -314,7 +367,7 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
314
367
return error (" Error sending authentication to proxy" );
315
368
}
316
369
LogPrint (BCLog::PROXY, " SOCKS5 sending proxy authentication %s:%s\n " , auth->username , auth->password );
317
- char pchRetA[2 ];
370
+ uint8_t pchRetA[2 ];
318
371
if ((recvr = InterruptibleRecv (pchRetA, 2 , SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) {
319
372
CloseSocket (hSocket);
320
373
return error (" Error reading proxy authentication response" );
@@ -323,17 +376,17 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
323
376
CloseSocket (hSocket);
324
377
return error (" Proxy authentication unsuccessful" );
325
378
}
326
- } else if (pchRet1[1 ] == 0x00 ) {
379
+ } else if (pchRet1[1 ] == SOCKS5Method::NOAUTH ) {
327
380
// Perform no authentication
328
381
} else {
329
382
CloseSocket (hSocket);
330
383
return error (" Proxy requested wrong authentication method %02x" , pchRet1[1 ]);
331
384
}
332
385
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
337
390
vSocks5.push_back (strDest.size ()); // Length<=255 is checked at beginning of function
338
391
vSocks5.insert (vSocks5.end (), strDest.begin (), strDest.end ());
339
392
vSocks5.push_back ((port >> 8 ) & 0xFF );
@@ -343,7 +396,7 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
343
396
CloseSocket (hSocket);
344
397
return error (" Error sending to proxy" );
345
398
}
346
- char pchRet2[4 ];
399
+ uint8_t pchRet2[4 ];
347
400
if ((recvr = InterruptibleRecv (pchRet2, 4 , SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) {
348
401
CloseSocket (hSocket);
349
402
if (recvr == IntrRecvError::Timeout) {
@@ -355,26 +408,26 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
355
408
return error (" Error while reading proxy response" );
356
409
}
357
410
}
358
- if (pchRet2[0 ] != 0x05 ) {
411
+ if (pchRet2[0 ] != SOCKSVersion::SOCKS5 ) {
359
412
CloseSocket (hSocket);
360
413
return error (" Proxy failed to accept request" );
361
414
}
362
- if (pchRet2[1 ] != 0x00 ) {
415
+ if (pchRet2[1 ] != SOCKS5Reply::SUCCEEDED ) {
363
416
// Failures to connect to a peer that are not proxy errors
364
417
CloseSocket (hSocket);
365
418
LogPrintf (" Socks5() connect to %s:%d failed: %s\n " , strDest, port, Socks5ErrorString (pchRet2[1 ]));
366
419
return false ;
367
420
}
368
- if (pchRet2[2 ] != 0x00 ) {
421
+ if (pchRet2[2 ] != 0x00 ) { // Reserved field must be 0
369
422
CloseSocket (hSocket);
370
423
return error (" Error: malformed proxy response" );
371
424
}
372
- char pchRet3[256 ];
425
+ uint8_t pchRet3[256 ];
373
426
switch (pchRet2[3 ])
374
427
{
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 :
378
431
{
379
432
recvr = InterruptibleRecv (pchRet3, 1 , SOCKS5_RECV_TIMEOUT, hSocket);
380
433
if (recvr != IntrRecvError::OK) {
0 commit comments