Changeset View
Changeset View
Standalone View
Standalone View
src/netbase.cpp
Show First 20 Lines • Show All 174 Lines • ▼ Show 20 Lines | |||||
struct timeval MillisToTimeval(int64_t nTimeout) { | struct timeval MillisToTimeval(int64_t nTimeout) { | ||||
struct timeval timeout; | struct timeval timeout; | ||||
timeout.tv_sec = nTimeout / 1000; | timeout.tv_sec = nTimeout / 1000; | ||||
timeout.tv_usec = (nTimeout % 1000) * 1000; | timeout.tv_usec = (nTimeout % 1000) * 1000; | ||||
return timeout; | return timeout; | ||||
} | } | ||||
enum class IntrRecvError { | |||||
OK, | |||||
Timeout, | |||||
Disconnected, | |||||
NetworkError, | |||||
Interrupted | |||||
}; | |||||
/** | /** | ||||
* Read bytes from socket. This will either read the full number of bytes | * Read bytes from socket. This will either read the full number of bytes | ||||
* requested or return False on error or timeout. | * requested or return False on error or timeout. | ||||
* This function can be interrupted by calling InterruptSocks5() | * This function can be interrupted by calling InterruptSocks5() | ||||
* | * | ||||
* @param data Buffer to receive into | * @param data Buffer to receive into | ||||
* @param len Length of data to receive | * @param len Length of data to receive | ||||
* @param timeout Timeout in milliseconds for receive operation | * @param timeout Timeout in milliseconds for receive operation | ||||
* | * | ||||
* @note This function requires that hSocket is in non-blocking mode. | * @note This function requires that hSocket is in non-blocking mode. | ||||
*/ | */ | ||||
static bool InterruptibleRecv(char *data, size_t len, int timeout, | static IntrRecvError InterruptibleRecv(char *data, size_t len, int timeout, | ||||
SOCKET &hSocket) { | SOCKET &hSocket) { | ||||
int64_t curTime = GetTimeMillis(); | int64_t curTime = GetTimeMillis(); | ||||
int64_t endTime = curTime + timeout; | int64_t endTime = curTime + timeout; | ||||
// Maximum time to wait in one select call. It will take up until this time | // Maximum time to wait in one select call. It will take up until this time | ||||
// (in millis) to break off in case of an interruption. | // (in millis) to break off in case of an interruption. | ||||
const int64_t maxWait = 1000; | const int64_t maxWait = 1000; | ||||
while (len > 0 && curTime < endTime) { | while (len > 0 && curTime < endTime) { | ||||
// Optimistically try the recv first | // Optimistically try the recv first | ||||
ssize_t ret = recv(hSocket, data, len, 0); | ssize_t ret = recv(hSocket, data, len, 0); | ||||
if (ret > 0) { | if (ret > 0) { | ||||
len -= ret; | len -= ret; | ||||
data += ret; | data += ret; | ||||
} else if (ret == 0) { | } else if (ret == 0) { | ||||
// Unexpected disconnection | // Unexpected disconnection | ||||
return false; | return IntrRecvError::Disconnected; | ||||
} else { | } else { | ||||
// Other error or blocking | // Other error or blocking | ||||
int nErr = WSAGetLastError(); | int nErr = WSAGetLastError(); | ||||
if (nErr == WSAEINPROGRESS || nErr == WSAEWOULDBLOCK || | if (nErr == WSAEINPROGRESS || nErr == WSAEWOULDBLOCK || | ||||
nErr == WSAEINVAL) { | nErr == WSAEINVAL) { | ||||
if (!IsSelectableSocket(hSocket)) { | if (!IsSelectableSocket(hSocket)) { | ||||
return false; | return IntrRecvError::NetworkError; | ||||
} | } | ||||
struct timeval tval = | struct timeval tval = | ||||
MillisToTimeval(std::min(endTime - curTime, maxWait)); | MillisToTimeval(std::min(endTime - curTime, maxWait)); | ||||
fd_set fdset; | fd_set fdset; | ||||
FD_ZERO(&fdset); | FD_ZERO(&fdset); | ||||
FD_SET(hSocket, &fdset); | FD_SET(hSocket, &fdset); | ||||
int nRet = select(hSocket + 1, &fdset, nullptr, nullptr, &tval); | int nRet = select(hSocket + 1, &fdset, nullptr, nullptr, &tval); | ||||
if (nRet == SOCKET_ERROR) { | if (nRet == SOCKET_ERROR) { | ||||
return false; | return IntrRecvError::NetworkError; | ||||
} | } | ||||
} else { | } else { | ||||
return false; | return IntrRecvError::NetworkError; | ||||
} | } | ||||
} | } | ||||
if (interruptSocks5Recv) return false; | if (interruptSocks5Recv) { | ||||
return IntrRecvError::Interrupted; | |||||
} | |||||
curTime = GetTimeMillis(); | curTime = GetTimeMillis(); | ||||
} | } | ||||
return len == 0; | return len == 0 ? IntrRecvError::OK : IntrRecvError::Timeout; | ||||
} | } | ||||
struct ProxyCredentials { | struct ProxyCredentials { | ||||
std::string username; | std::string username; | ||||
std::string password; | std::string password; | ||||
}; | }; | ||||
std::string Socks5ErrorString(int err) { | std::string Socks5ErrorString(int err) { | ||||
Show All 17 Lines | switch (err) { | ||||
default: | default: | ||||
return "unknown"; | return "unknown"; | ||||
} | } | ||||
} | } | ||||
/** Connect using SOCKS5 (as described in RFC1928) */ | /** Connect using SOCKS5 (as described in RFC1928) */ | ||||
static bool Socks5(const std::string &strDest, int port, | static bool Socks5(const std::string &strDest, int port, | ||||
const ProxyCredentials *auth, SOCKET &hSocket) { | const ProxyCredentials *auth, SOCKET &hSocket) { | ||||
IntrRecvError recvr; | |||||
LogPrint(BCLog::NET, "SOCKS5 connecting %s\n", strDest); | LogPrint(BCLog::NET, "SOCKS5 connecting %s\n", strDest); | ||||
if (strDest.size() > 255) { | if (strDest.size() > 255) { | ||||
CloseSocket(hSocket); | CloseSocket(hSocket); | ||||
return error("Hostname too long"); | return error("Hostname too long"); | ||||
} | } | ||||
// Accepted authentication methods | // Accepted authentication methods | ||||
std::vector<uint8_t> vSocks5Init; | std::vector<uint8_t> vSocks5Init; | ||||
vSocks5Init.push_back(0x05); | vSocks5Init.push_back(0x05); | ||||
Show All 12 Lines | static bool Socks5(const std::string &strDest, int port, | ||||
} | } | ||||
ssize_t ret = send(hSocket, (const char *)vSocks5Init.data(), | ssize_t ret = send(hSocket, (const char *)vSocks5Init.data(), | ||||
vSocks5Init.size(), MSG_NOSIGNAL); | vSocks5Init.size(), MSG_NOSIGNAL); | ||||
if (ret != (ssize_t)vSocks5Init.size()) { | if (ret != (ssize_t)vSocks5Init.size()) { | ||||
CloseSocket(hSocket); | CloseSocket(hSocket); | ||||
return error("Error sending to proxy"); | return error("Error sending to proxy"); | ||||
} | } | ||||
char pchRet1[2]; | char pchRet1[2]; | ||||
if (!InterruptibleRecv(pchRet1, 2, SOCKS5_RECV_TIMEOUT, hSocket)) { | if ((recvr = InterruptibleRecv(pchRet1, 2, SOCKS5_RECV_TIMEOUT, hSocket)) != | ||||
IntrRecvError::OK) { | |||||
CloseSocket(hSocket); | CloseSocket(hSocket); | ||||
LogPrintf("Socks5() connect to %s:%d failed: InterruptibleRecv() " | LogPrintf("Socks5() connect to %s:%d failed: InterruptibleRecv() " | ||||
"timeout or other failure\n", | "timeout or other failure\n", | ||||
strDest, port); | strDest, port); | ||||
return false; | return false; | ||||
} | } | ||||
if (pchRet1[0] != 0x05) { | if (pchRet1[0] != 0x05) { | ||||
CloseSocket(hSocket); | CloseSocket(hSocket); | ||||
Show All 13 Lines | if (pchRet1[1] == 0x02 && auth) { | ||||
MSG_NOSIGNAL); | MSG_NOSIGNAL); | ||||
if (ret != (ssize_t)vAuth.size()) { | if (ret != (ssize_t)vAuth.size()) { | ||||
CloseSocket(hSocket); | CloseSocket(hSocket); | ||||
return error("Error sending authentication to proxy"); | return error("Error sending authentication to proxy"); | ||||
} | } | ||||
LogPrint(BCLog::PROXY, "SOCKS5 sending proxy authentication %s:%s\n", | LogPrint(BCLog::PROXY, "SOCKS5 sending proxy authentication %s:%s\n", | ||||
auth->username, auth->password); | auth->username, auth->password); | ||||
char pchRetA[2]; | char pchRetA[2]; | ||||
if (!InterruptibleRecv(pchRetA, 2, SOCKS5_RECV_TIMEOUT, hSocket)) { | if ((recvr = InterruptibleRecv(pchRetA, 2, SOCKS5_RECV_TIMEOUT, | ||||
hSocket)) != IntrRecvError::OK) { | |||||
CloseSocket(hSocket); | CloseSocket(hSocket); | ||||
return error("Error reading proxy authentication response"); | return error("Error reading proxy authentication response"); | ||||
} | } | ||||
if (pchRetA[0] != 0x01 || pchRetA[1] != 0x00) { | if (pchRetA[0] != 0x01 || pchRetA[1] != 0x00) { | ||||
CloseSocket(hSocket); | CloseSocket(hSocket); | ||||
return error("Proxy authentication unsuccessful"); | return error("Proxy authentication unsuccessful"); | ||||
} | } | ||||
} else if (pchRet1[1] == 0x00) { | } else if (pchRet1[1] == 0x00) { | ||||
Show All 19 Lines | static bool Socks5(const std::string &strDest, int port, | ||||
vSocks5.push_back((port >> 0) & 0xFF); | vSocks5.push_back((port >> 0) & 0xFF); | ||||
ret = send(hSocket, (const char *)vSocks5.data(), vSocks5.size(), | ret = send(hSocket, (const char *)vSocks5.data(), vSocks5.size(), | ||||
MSG_NOSIGNAL); | MSG_NOSIGNAL); | ||||
if (ret != (ssize_t)vSocks5.size()) { | if (ret != (ssize_t)vSocks5.size()) { | ||||
CloseSocket(hSocket); | CloseSocket(hSocket); | ||||
return error("Error sending to proxy"); | return error("Error sending to proxy"); | ||||
} | } | ||||
char pchRet2[4]; | char pchRet2[4]; | ||||
if (!InterruptibleRecv(pchRet2, 4, SOCKS5_RECV_TIMEOUT, hSocket)) { | if ((recvr = InterruptibleRecv(pchRet2, 4, SOCKS5_RECV_TIMEOUT, hSocket)) != | ||||
IntrRecvError::OK) { | |||||
CloseSocket(hSocket); | CloseSocket(hSocket); | ||||
return error("Error reading proxy response"); | if (recvr == IntrRecvError::Timeout) { | ||||
/** | |||||
* If a timeout happens here, this effectively means we timed out | |||||
* while connecting to the remote node. This is very common for Tor, | |||||
* so do not print an error message. | |||||
*/ | |||||
return false; | |||||
} else { | |||||
return error("Error while reading proxy response"); | |||||
} | |||||
} | } | ||||
if (pchRet2[0] != 0x05) { | if (pchRet2[0] != 0x05) { | ||||
CloseSocket(hSocket); | CloseSocket(hSocket); | ||||
return error("Proxy failed to accept request"); | return error("Proxy failed to accept request"); | ||||
} | } | ||||
if (pchRet2[1] != 0x00) { | if (pchRet2[1] != 0x00) { | ||||
// Failures to connect to a peer that are not proxy errors | // Failures to connect to a peer that are not proxy errors | ||||
CloseSocket(hSocket); | CloseSocket(hSocket); | ||||
LogPrintf("Socks5() connect to %s:%d failed: %s\n", strDest, port, | LogPrintf("Socks5() connect to %s:%d failed: %s\n", strDest, port, | ||||
Socks5ErrorString(pchRet2[1])); | Socks5ErrorString(pchRet2[1])); | ||||
return false; | return false; | ||||
} | } | ||||
if (pchRet2[2] != 0x00) { | if (pchRet2[2] != 0x00) { | ||||
CloseSocket(hSocket); | CloseSocket(hSocket); | ||||
return error("Error: malformed proxy response"); | return error("Error: malformed proxy response"); | ||||
} | } | ||||
char pchRet3[256]; | char pchRet3[256]; | ||||
switch (pchRet2[3]) { | switch (pchRet2[3]) { | ||||
case 0x01: | case 0x01: | ||||
ret = InterruptibleRecv(pchRet3, 4, SOCKS5_RECV_TIMEOUT, hSocket); | recvr = InterruptibleRecv(pchRet3, 4, SOCKS5_RECV_TIMEOUT, hSocket); | ||||
break; | break; | ||||
case 0x04: | case 0x04: | ||||
ret = InterruptibleRecv(pchRet3, 16, SOCKS5_RECV_TIMEOUT, hSocket); | recvr = | ||||
InterruptibleRecv(pchRet3, 16, SOCKS5_RECV_TIMEOUT, hSocket); | |||||
break; | break; | ||||
case 0x03: { | case 0x03: { | ||||
ret = InterruptibleRecv(pchRet3, 1, SOCKS5_RECV_TIMEOUT, hSocket); | recvr = InterruptibleRecv(pchRet3, 1, SOCKS5_RECV_TIMEOUT, hSocket); | ||||
if (!ret) { | if (recvr != IntrRecvError::OK) { | ||||
CloseSocket(hSocket); | CloseSocket(hSocket); | ||||
return error("Error reading from proxy"); | return error("Error reading from proxy"); | ||||
} | } | ||||
int nRecv = pchRet3[0]; | int nRecv = pchRet3[0]; | ||||
ret = | recvr = | ||||
InterruptibleRecv(pchRet3, nRecv, SOCKS5_RECV_TIMEOUT, hSocket); | InterruptibleRecv(pchRet3, nRecv, SOCKS5_RECV_TIMEOUT, hSocket); | ||||
break; | break; | ||||
} | } | ||||
default: | default: | ||||
CloseSocket(hSocket); | CloseSocket(hSocket); | ||||
return error("Error: malformed proxy response"); | return error("Error: malformed proxy response"); | ||||
} | } | ||||
if (!ret) { | if (recvr != IntrRecvError::OK) { | ||||
CloseSocket(hSocket); | CloseSocket(hSocket); | ||||
return error("Error reading from proxy"); | return error("Error reading from proxy"); | ||||
} | } | ||||
if (!InterruptibleRecv(pchRet3, 2, SOCKS5_RECV_TIMEOUT, hSocket)) { | if ((recvr = InterruptibleRecv(pchRet3, 2, SOCKS5_RECV_TIMEOUT, hSocket)) != | ||||
IntrRecvError::OK) { | |||||
CloseSocket(hSocket); | CloseSocket(hSocket); | ||||
return error("Error reading from proxy"); | return error("Error reading from proxy"); | ||||
} | } | ||||
LogPrint(BCLog::NET, "SOCKS5 connected %s\n", strDest); | LogPrint(BCLog::NET, "SOCKS5 connected %s\n", strDest); | ||||
return true; | return true; | ||||
} | } | ||||
bool ConnectSocketDirectly(const CService &addrConnect, SOCKET &hSocketRet, | bool ConnectSocketDirectly(const CService &addrConnect, SOCKET &hSocketRet, | ||||
▲ Show 20 Lines • Show All 274 Lines • Show Last 20 Lines |