Skip to content

Commit 22f816e

Browse files
committed
net: Improve and document SOCKS code
Make the SOCKS code more consistent, and document the constants used.
1 parent 10a20bf commit 22f816e

File tree

1 file changed

+89
-36
lines changed

1 file changed

+89
-36
lines changed

src/netbase.cpp

+89-36
Original file line numberDiff line numberDiff line change
@@ -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 */
187229
enum 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 */
245288
struct 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

Comments
 (0)