mirror of
https://github.com/bitcoin/bitcoin.git
synced 2024-11-20 10:38:42 +01:00
net: Improve and document SOCKS code
Make the SOCKS code more consistent, and document the constants used.
This commit is contained in:
parent
10a20bf770
commit
22f816ef4d
125
src/netbase.cpp
125
src/netbase.cpp
@ -184,6 +184,48 @@ struct timeval MillisToTimeval(int64_t nTimeout)
|
||||
return timeout;
|
||||
}
|
||||
|
||||
/** SOCKS version */
|
||||
enum SOCKSVersion: uint8_t {
|
||||
SOCKS4 = 0x04,
|
||||
SOCKS5 = 0x05
|
||||
};
|
||||
|
||||
/** Values defined for METHOD in RFC1928 */
|
||||
enum SOCKS5Method: uint8_t {
|
||||
NOAUTH = 0x00, //! No authentication required
|
||||
GSSAPI = 0x01, //! GSSAPI
|
||||
USER_PASS = 0x02, //! Username/password
|
||||
NO_ACCEPTABLE = 0xff, //! No acceptable methods
|
||||
};
|
||||
|
||||
/** Values defined for CMD in RFC1928 */
|
||||
enum SOCKS5Command: uint8_t {
|
||||
CONNECT = 0x01,
|
||||
BIND = 0x02,
|
||||
UDP_ASSOCIATE = 0x03
|
||||
};
|
||||
|
||||
/** Values defined for REP in RFC1928 */
|
||||
enum SOCKS5Reply: uint8_t {
|
||||
SUCCEEDED = 0x00, //! Succeeded
|
||||
GENFAILURE = 0x01, //! General failure
|
||||
NOTALLOWED = 0x02, //! Connection not allowed by ruleset
|
||||
NETUNREACHABLE = 0x03, //! Network unreachable
|
||||
HOSTUNREACHABLE = 0x04, //! Network unreachable
|
||||
CONNREFUSED = 0x05, //! Connection refused
|
||||
TTLEXPIRED = 0x06, //! TTL expired
|
||||
CMDUNSUPPORTED = 0x07, //! Command not supported
|
||||
ATYPEUNSUPPORTED = 0x08, //! Address type not supported
|
||||
};
|
||||
|
||||
/** Values defined for ATYPE in RFC1928 */
|
||||
enum SOCKS5Atyp: uint8_t {
|
||||
IPV4 = 0x01,
|
||||
DOMAINNAME = 0x03,
|
||||
IPV6 = 0x04,
|
||||
};
|
||||
|
||||
/** Status codes that can be returned by InterruptibleRecv */
|
||||
enum class IntrRecvError {
|
||||
OK,
|
||||
Timeout,
|
||||
@ -203,7 +245,7 @@ enum class IntrRecvError {
|
||||
*
|
||||
* @note This function requires that hSocket is in non-blocking mode.
|
||||
*/
|
||||
static IntrRecvError InterruptibleRecv(char* data, size_t len, int timeout, const SOCKET& hSocket)
|
||||
static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, const SOCKET& hSocket)
|
||||
{
|
||||
int64_t curTime = GetTimeMillis();
|
||||
int64_t endTime = curTime + timeout;
|
||||
@ -211,7 +253,7 @@ static IntrRecvError InterruptibleRecv(char* data, size_t len, int timeout, cons
|
||||
// to break off in case of an interruption.
|
||||
const int64_t maxWait = 1000;
|
||||
while (len > 0 && curTime < endTime) {
|
||||
ssize_t ret = recv(hSocket, data, len, 0); // Optimistically try the recv first
|
||||
ssize_t ret = recv(hSocket, (char*)data, len, 0); // Optimistically try the recv first
|
||||
if (ret > 0) {
|
||||
len -= ret;
|
||||
data += ret;
|
||||
@ -242,24 +284,35 @@ static IntrRecvError InterruptibleRecv(char* data, size_t len, int timeout, cons
|
||||
return len == 0 ? IntrRecvError::OK : IntrRecvError::Timeout;
|
||||
}
|
||||
|
||||
/** Credentials for proxy authentication */
|
||||
struct ProxyCredentials
|
||||
{
|
||||
std::string username;
|
||||
std::string password;
|
||||
};
|
||||
|
||||
std::string Socks5ErrorString(int err)
|
||||
/** Convert SOCKS5 reply to a an error message */
|
||||
std::string Socks5ErrorString(uint8_t err)
|
||||
{
|
||||
switch(err) {
|
||||
case 0x01: return "general failure";
|
||||
case 0x02: return "connection not allowed";
|
||||
case 0x03: return "network unreachable";
|
||||
case 0x04: return "host unreachable";
|
||||
case 0x05: return "connection refused";
|
||||
case 0x06: return "TTL expired";
|
||||
case 0x07: return "protocol error";
|
||||
case 0x08: return "address type not supported";
|
||||
default: return "unknown";
|
||||
case SOCKS5Reply::GENFAILURE:
|
||||
return "general failure";
|
||||
case SOCKS5Reply::NOTALLOWED:
|
||||
return "connection not allowed";
|
||||
case SOCKS5Reply::NETUNREACHABLE:
|
||||
return "network unreachable";
|
||||
case SOCKS5Reply::HOSTUNREACHABLE:
|
||||
return "host unreachable";
|
||||
case SOCKS5Reply::CONNREFUSED:
|
||||
return "connection refused";
|
||||
case SOCKS5Reply::TTLEXPIRED:
|
||||
return "TTL expired";
|
||||
case SOCKS5Reply::CMDUNSUPPORTED:
|
||||
return "protocol error";
|
||||
case SOCKS5Reply::ATYPEUNSUPPORTED:
|
||||
return "address type not supported";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
@ -274,34 +327,34 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
|
||||
}
|
||||
// Accepted authentication methods
|
||||
std::vector<uint8_t> vSocks5Init;
|
||||
vSocks5Init.push_back(0x05);
|
||||
vSocks5Init.push_back(SOCKSVersion::SOCKS5);
|
||||
if (auth) {
|
||||
vSocks5Init.push_back(0x02); // # METHODS
|
||||
vSocks5Init.push_back(0x00); // X'00' NO AUTHENTICATION REQUIRED
|
||||
vSocks5Init.push_back(0x02); // X'02' USERNAME/PASSWORD (RFC1929)
|
||||
vSocks5Init.push_back(0x02); // Number of methods
|
||||
vSocks5Init.push_back(SOCKS5Method::NOAUTH);
|
||||
vSocks5Init.push_back(SOCKS5Method::USER_PASS);
|
||||
} else {
|
||||
vSocks5Init.push_back(0x01); // # METHODS
|
||||
vSocks5Init.push_back(0x00); // X'00' NO AUTHENTICATION REQUIRED
|
||||
vSocks5Init.push_back(0x01); // Number of methods
|
||||
vSocks5Init.push_back(SOCKS5Method::NOAUTH);
|
||||
}
|
||||
ssize_t ret = send(hSocket, (const char*)vSocks5Init.data(), vSocks5Init.size(), MSG_NOSIGNAL);
|
||||
if (ret != (ssize_t)vSocks5Init.size()) {
|
||||
CloseSocket(hSocket);
|
||||
return error("Error sending to proxy");
|
||||
}
|
||||
char pchRet1[2];
|
||||
uint8_t pchRet1[2];
|
||||
if ((recvr = InterruptibleRecv(pchRet1, 2, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) {
|
||||
CloseSocket(hSocket);
|
||||
LogPrintf("Socks5() connect to %s:%d failed: InterruptibleRecv() timeout or other failure\n", strDest, port);
|
||||
return false;
|
||||
}
|
||||
if (pchRet1[0] != 0x05) {
|
||||
if (pchRet1[0] != SOCKSVersion::SOCKS5) {
|
||||
CloseSocket(hSocket);
|
||||
return error("Proxy failed to initialize");
|
||||
}
|
||||
if (pchRet1[1] == 0x02 && auth) {
|
||||
if (pchRet1[1] == SOCKS5Method::USER_PASS && auth) {
|
||||
// Perform username/password authentication (as described in RFC1929)
|
||||
std::vector<uint8_t> vAuth;
|
||||
vAuth.push_back(0x01);
|
||||
vAuth.push_back(0x01); // Current (and only) version of user/pass subnegotiation
|
||||
if (auth->username.size() > 255 || auth->password.size() > 255)
|
||||
return error("Proxy username or password too long");
|
||||
vAuth.push_back(auth->username.size());
|
||||
@ -314,7 +367,7 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
|
||||
return error("Error sending authentication to proxy");
|
||||
}
|
||||
LogPrint(BCLog::PROXY, "SOCKS5 sending proxy authentication %s:%s\n", auth->username, auth->password);
|
||||
char pchRetA[2];
|
||||
uint8_t pchRetA[2];
|
||||
if ((recvr = InterruptibleRecv(pchRetA, 2, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) {
|
||||
CloseSocket(hSocket);
|
||||
return error("Error reading proxy authentication response");
|
||||
@ -323,17 +376,17 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
|
||||
CloseSocket(hSocket);
|
||||
return error("Proxy authentication unsuccessful");
|
||||
}
|
||||
} else if (pchRet1[1] == 0x00) {
|
||||
} else if (pchRet1[1] == SOCKS5Method::NOAUTH) {
|
||||
// Perform no authentication
|
||||
} else {
|
||||
CloseSocket(hSocket);
|
||||
return error("Proxy requested wrong authentication method %02x", pchRet1[1]);
|
||||
}
|
||||
std::vector<uint8_t> vSocks5;
|
||||
vSocks5.push_back(0x05); // VER protocol version
|
||||
vSocks5.push_back(0x01); // CMD CONNECT
|
||||
vSocks5.push_back(0x00); // RSV Reserved
|
||||
vSocks5.push_back(0x03); // ATYP DOMAINNAME
|
||||
vSocks5.push_back(SOCKSVersion::SOCKS5); // VER protocol version
|
||||
vSocks5.push_back(SOCKS5Command::CONNECT); // CMD CONNECT
|
||||
vSocks5.push_back(0x00); // RSV Reserved must be 0
|
||||
vSocks5.push_back(SOCKS5Atyp::DOMAINNAME); // ATYP DOMAINNAME
|
||||
vSocks5.push_back(strDest.size()); // Length<=255 is checked at beginning of function
|
||||
vSocks5.insert(vSocks5.end(), strDest.begin(), strDest.end());
|
||||
vSocks5.push_back((port >> 8) & 0xFF);
|
||||
@ -343,7 +396,7 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
|
||||
CloseSocket(hSocket);
|
||||
return error("Error sending to proxy");
|
||||
}
|
||||
char pchRet2[4];
|
||||
uint8_t pchRet2[4];
|
||||
if ((recvr = InterruptibleRecv(pchRet2, 4, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) {
|
||||
CloseSocket(hSocket);
|
||||
if (recvr == IntrRecvError::Timeout) {
|
||||
@ -355,26 +408,26 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
|
||||
return error("Error while reading proxy response");
|
||||
}
|
||||
}
|
||||
if (pchRet2[0] != 0x05) {
|
||||
if (pchRet2[0] != SOCKSVersion::SOCKS5) {
|
||||
CloseSocket(hSocket);
|
||||
return error("Proxy failed to accept request");
|
||||
}
|
||||
if (pchRet2[1] != 0x00) {
|
||||
if (pchRet2[1] != SOCKS5Reply::SUCCEEDED) {
|
||||
// Failures to connect to a peer that are not proxy errors
|
||||
CloseSocket(hSocket);
|
||||
LogPrintf("Socks5() connect to %s:%d failed: %s\n", strDest, port, Socks5ErrorString(pchRet2[1]));
|
||||
return false;
|
||||
}
|
||||
if (pchRet2[2] != 0x00) {
|
||||
if (pchRet2[2] != 0x00) { // Reserved field must be 0
|
||||
CloseSocket(hSocket);
|
||||
return error("Error: malformed proxy response");
|
||||
}
|
||||
char pchRet3[256];
|
||||
uint8_t pchRet3[256];
|
||||
switch (pchRet2[3])
|
||||
{
|
||||
case 0x01: recvr = InterruptibleRecv(pchRet3, 4, SOCKS5_RECV_TIMEOUT, hSocket); break;
|
||||
case 0x04: recvr = InterruptibleRecv(pchRet3, 16, SOCKS5_RECV_TIMEOUT, hSocket); break;
|
||||
case 0x03:
|
||||
case SOCKS5Atyp::IPV4: recvr = InterruptibleRecv(pchRet3, 4, SOCKS5_RECV_TIMEOUT, hSocket); break;
|
||||
case SOCKS5Atyp::IPV6: recvr = InterruptibleRecv(pchRet3, 16, SOCKS5_RECV_TIMEOUT, hSocket); break;
|
||||
case SOCKS5Atyp::DOMAINNAME:
|
||||
{
|
||||
recvr = InterruptibleRecv(pchRet3, 1, SOCKS5_RECV_TIMEOUT, hSocket);
|
||||
if (recvr != IntrRecvError::OK) {
|
||||
|
Loading…
Reference in New Issue
Block a user