|
|
|
@ -45,6 +45,9 @@ bool fNameLookup = false;
@@ -45,6 +45,9 @@ bool fNameLookup = false;
|
|
|
|
|
|
|
|
|
|
static const unsigned char pchIPv4[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff }; |
|
|
|
|
|
|
|
|
|
// Need ample time for negotiation for very slow proxies such as Tor (milliseconds)
|
|
|
|
|
static const int SOCKS5_RECV_TIMEOUT = 20 * 1000; |
|
|
|
|
|
|
|
|
|
enum Network ParseNetwork(std::string net) { |
|
|
|
|
boost::to_lower(net); |
|
|
|
|
if (net == "ipv4") return NET_IPV4; |
|
|
|
@ -225,6 +228,63 @@ bool LookupNumeric(const char *pszName, CService& addr, int portDefault)
@@ -225,6 +228,63 @@ bool LookupNumeric(const char *pszName, CService& addr, int portDefault)
|
|
|
|
|
return Lookup(pszName, addr, portDefault, false); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Convert milliseconds to a struct timeval for select. |
|
|
|
|
*/ |
|
|
|
|
struct timeval static MillisToTimeval(int64_t nTimeout) |
|
|
|
|
{ |
|
|
|
|
struct timeval timeout; |
|
|
|
|
timeout.tv_sec = nTimeout / 1000; |
|
|
|
|
timeout.tv_usec = (nTimeout % 1000) * 1000; |
|
|
|
|
return timeout; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Read bytes from socket. This will either read the full number of bytes requested |
|
|
|
|
* or return False on error or timeout. |
|
|
|
|
* This function can be interrupted by boost thread interrupt. |
|
|
|
|
* |
|
|
|
|
* @param data Buffer to receive into |
|
|
|
|
* @param len Length of data to receive |
|
|
|
|
* @param timeout Timeout in milliseconds for receive operation |
|
|
|
|
* |
|
|
|
|
* @note This function requires that hSocket is in non-blocking mode. |
|
|
|
|
*/ |
|
|
|
|
bool static InterruptibleRecv(char* data, size_t len, int timeout, SOCKET& hSocket) |
|
|
|
|
{ |
|
|
|
|
int64_t curTime = GetTimeMillis(); |
|
|
|
|
int64_t endTime = curTime + timeout; |
|
|
|
|
// 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.
|
|
|
|
|
const int64_t maxWait = 1000; |
|
|
|
|
while (len > 0 && curTime < endTime) { |
|
|
|
|
ssize_t ret = recv(hSocket, data, len, 0); // Optimistically try the recv first
|
|
|
|
|
if (ret > 0) { |
|
|
|
|
len -= ret; |
|
|
|
|
data += ret; |
|
|
|
|
} else if (ret == 0) { // Unexpected disconnection
|
|
|
|
|
return false; |
|
|
|
|
} else { // Other error or blocking
|
|
|
|
|
int nErr = WSAGetLastError(); |
|
|
|
|
if (nErr == WSAEINPROGRESS || nErr == WSAEWOULDBLOCK || nErr == WSAEINVAL) { |
|
|
|
|
struct timeval tval = MillisToTimeval(std::min(endTime - curTime, maxWait)); |
|
|
|
|
fd_set fdset; |
|
|
|
|
FD_ZERO(&fdset); |
|
|
|
|
FD_SET(hSocket, &fdset); |
|
|
|
|
int nRet = select(hSocket + 1, &fdset, NULL, NULL, &tval); |
|
|
|
|
if (nRet == SOCKET_ERROR) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
boost::this_thread::interruption_point(); |
|
|
|
|
curTime = GetTimeMillis(); |
|
|
|
|
} |
|
|
|
|
return len == 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool static Socks5(string strDest, int port, SOCKET& hSocket) |
|
|
|
|
{ |
|
|
|
|
LogPrintf("SOCKS5 connecting %s\n", strDest); |
|
|
|
@ -243,7 +303,7 @@ bool static Socks5(string strDest, int port, SOCKET& hSocket)
@@ -243,7 +303,7 @@ bool static Socks5(string strDest, int port, SOCKET& hSocket)
|
|
|
|
|
return error("Error sending to proxy"); |
|
|
|
|
} |
|
|
|
|
char pchRet1[2]; |
|
|
|
|
if (recv(hSocket, pchRet1, 2, 0) != 2) |
|
|
|
|
if (!InterruptibleRecv(pchRet1, 2, SOCKS5_RECV_TIMEOUT, hSocket)) |
|
|
|
|
{ |
|
|
|
|
CloseSocket(hSocket); |
|
|
|
|
return error("Error reading proxy response"); |
|
|
|
@ -266,7 +326,7 @@ bool static Socks5(string strDest, int port, SOCKET& hSocket)
@@ -266,7 +326,7 @@ bool static Socks5(string strDest, int port, SOCKET& hSocket)
|
|
|
|
|
return error("Error sending to proxy"); |
|
|
|
|
} |
|
|
|
|
char pchRet2[4]; |
|
|
|
|
if (recv(hSocket, pchRet2, 4, 0) != 4) |
|
|
|
|
if (!InterruptibleRecv(pchRet2, 4, SOCKS5_RECV_TIMEOUT, hSocket)) |
|
|
|
|
{ |
|
|
|
|
CloseSocket(hSocket); |
|
|
|
|
return error("Error reading proxy response"); |
|
|
|
@ -300,27 +360,27 @@ bool static Socks5(string strDest, int port, SOCKET& hSocket)
@@ -300,27 +360,27 @@ bool static Socks5(string strDest, int port, SOCKET& hSocket)
|
|
|
|
|
char pchRet3[256]; |
|
|
|
|
switch (pchRet2[3]) |
|
|
|
|
{ |
|
|
|
|
case 0x01: ret = recv(hSocket, pchRet3, 4, 0) != 4; break; |
|
|
|
|
case 0x04: ret = recv(hSocket, pchRet3, 16, 0) != 16; break; |
|
|
|
|
case 0x01: ret = InterruptibleRecv(pchRet3, 4, SOCKS5_RECV_TIMEOUT, hSocket); break; |
|
|
|
|
case 0x04: ret = InterruptibleRecv(pchRet3, 16, SOCKS5_RECV_TIMEOUT, hSocket); break; |
|
|
|
|
case 0x03: |
|
|
|
|
{ |
|
|
|
|
ret = recv(hSocket, pchRet3, 1, 0) != 1; |
|
|
|
|
if (ret) { |
|
|
|
|
ret = InterruptibleRecv(pchRet3, 1, SOCKS5_RECV_TIMEOUT, hSocket); |
|
|
|
|
if (!ret) { |
|
|
|
|
CloseSocket(hSocket); |
|
|
|
|
return error("Error reading from proxy"); |
|
|
|
|
} |
|
|
|
|
int nRecv = pchRet3[0]; |
|
|
|
|
ret = recv(hSocket, pchRet3, nRecv, 0) != nRecv; |
|
|
|
|
ret = InterruptibleRecv(pchRet3, nRecv, SOCKS5_RECV_TIMEOUT, hSocket); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
default: CloseSocket(hSocket); return error("Error: malformed proxy response"); |
|
|
|
|
} |
|
|
|
|
if (ret) |
|
|
|
|
if (!ret) |
|
|
|
|
{ |
|
|
|
|
CloseSocket(hSocket); |
|
|
|
|
return error("Error reading from proxy"); |
|
|
|
|
} |
|
|
|
|
if (recv(hSocket, pchRet3, 2, 0) != 2) |
|
|
|
|
if (!InterruptibleRecv(pchRet3, 2, SOCKS5_RECV_TIMEOUT, hSocket)) |
|
|
|
|
{ |
|
|
|
|
CloseSocket(hSocket); |
|
|
|
|
return error("Error reading from proxy"); |
|
|
|
@ -360,10 +420,7 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe
@@ -360,10 +420,7 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe
|
|
|
|
|
// WSAEINVAL is here because some legacy version of winsock uses it
|
|
|
|
|
if (nErr == WSAEINPROGRESS || nErr == WSAEWOULDBLOCK || nErr == WSAEINVAL) |
|
|
|
|
{ |
|
|
|
|
struct timeval timeout; |
|
|
|
|
timeout.tv_sec = nTimeout / 1000; |
|
|
|
|
timeout.tv_usec = (nTimeout % 1000) * 1000; |
|
|
|
|
|
|
|
|
|
struct timeval timeout = MillisToTimeval(nTimeout); |
|
|
|
|
fd_set fdset; |
|
|
|
|
FD_ZERO(&fdset); |
|
|
|
|
FD_SET(hSocket, &fdset); |
|
|
|
@ -410,11 +467,6 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe
@@ -410,11 +467,6 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// This is required when using SOCKS5 proxy!
|
|
|
|
|
// CNode::ConnectNode turns the socket back to non-blocking.
|
|
|
|
|
if (!SetSocketNonBlocking(hSocket, false)) |
|
|
|
|
return error("ConnectSocketDirectly: Setting socket to blocking failed, error %s\n", NetworkErrorString(WSAGetLastError())); |
|
|
|
|
|
|
|
|
|
hSocketRet = hSocket; |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|