|
|
@ -198,6 +198,14 @@ struct timeval MillisToTimeval(int64_t nTimeout) |
|
|
|
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 requested |
|
|
|
* Read bytes from socket. This will either read the full number of bytes requested |
|
|
|
* or return False on error or timeout. |
|
|
|
* or return False on error or timeout. |
|
|
@ -209,7 +217,7 @@ struct timeval MillisToTimeval(int64_t nTimeout) |
|
|
|
* |
|
|
|
* |
|
|
|
* @note This function requires that hSocket is in non-blocking mode. |
|
|
|
* @note This function requires that hSocket is in non-blocking mode. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
bool static InterruptibleRecv(char* data, size_t len, int timeout, SOCKET& hSocket) |
|
|
|
static IntrRecvError InterruptibleRecv(char* data, size_t len, int timeout, SOCKET& hSocket) |
|
|
|
{ |
|
|
|
{ |
|
|
|
int64_t curTime = GetTimeMillis(); |
|
|
|
int64_t curTime = GetTimeMillis(); |
|
|
|
int64_t endTime = curTime + timeout; |
|
|
|
int64_t endTime = curTime + timeout; |
|
|
@ -222,12 +230,12 @@ bool static InterruptibleRecv(char* data, size_t len, int timeout, SOCKET& hSock |
|
|
|
len -= ret; |
|
|
|
len -= ret; |
|
|
|
data += ret; |
|
|
|
data += ret; |
|
|
|
} else if (ret == 0) { // Unexpected disconnection
|
|
|
|
} else if (ret == 0) { // Unexpected disconnection
|
|
|
|
return false; |
|
|
|
return IntrRecvError::Disconnected; |
|
|
|
} else { // Other error or blocking
|
|
|
|
} else { // Other error or blocking
|
|
|
|
int nErr = WSAGetLastError(); |
|
|
|
int nErr = WSAGetLastError(); |
|
|
|
if (nErr == WSAEINPROGRESS || nErr == WSAEWOULDBLOCK || nErr == WSAEINVAL) { |
|
|
|
if (nErr == WSAEINPROGRESS || nErr == WSAEWOULDBLOCK || nErr == WSAEINVAL) { |
|
|
|
if (!IsSelectableSocket(hSocket)) { |
|
|
|
if (!IsSelectableSocket(hSocket)) { |
|
|
|
return false; |
|
|
|
return IntrRecvError::NetworkError; |
|
|
|
} |
|
|
|
} |
|
|
|
struct timeval tval = MillisToTimeval(std::min(endTime - curTime, maxWait)); |
|
|
|
struct timeval tval = MillisToTimeval(std::min(endTime - curTime, maxWait)); |
|
|
|
fd_set fdset; |
|
|
|
fd_set fdset; |
|
|
@ -235,17 +243,17 @@ bool static InterruptibleRecv(char* data, size_t len, int timeout, SOCKET& hSock |
|
|
|
FD_SET(hSocket, &fdset); |
|
|
|
FD_SET(hSocket, &fdset); |
|
|
|
int nRet = select(hSocket + 1, &fdset, NULL, NULL, &tval); |
|
|
|
int nRet = select(hSocket + 1, &fdset, NULL, NULL, &tval); |
|
|
|
if (nRet == SOCKET_ERROR) { |
|
|
|
if (nRet == SOCKET_ERROR) { |
|
|
|
return false; |
|
|
|
return IntrRecvError::NetworkError; |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
return false; |
|
|
|
return IntrRecvError::NetworkError; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if (interruptSocks5Recv) |
|
|
|
if (interruptSocks5Recv) |
|
|
|
return false; |
|
|
|
return IntrRecvError::Interrupted; |
|
|
|
curTime = GetTimeMillis(); |
|
|
|
curTime = GetTimeMillis(); |
|
|
|
} |
|
|
|
} |
|
|
|
return len == 0; |
|
|
|
return len == 0 ? IntrRecvError::OK : IntrRecvError::Timeout; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
struct ProxyCredentials |
|
|
|
struct ProxyCredentials |
|
|
@ -272,6 +280,7 @@ std::string Socks5ErrorString(int err) |
|
|
|
/** Connect using SOCKS5 (as described in RFC1928) */ |
|
|
|
/** Connect using SOCKS5 (as described in RFC1928) */ |
|
|
|
static bool Socks5(const std::string& strDest, int port, const ProxyCredentials *auth, SOCKET& hSocket) |
|
|
|
static bool Socks5(const std::string& strDest, int port, const ProxyCredentials *auth, SOCKET& hSocket) |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
IntrRecvError recvr; |
|
|
|
LogPrint("net", "SOCKS5 connecting %s\n", strDest); |
|
|
|
LogPrint("net", "SOCKS5 connecting %s\n", strDest); |
|
|
|
if (strDest.size() > 255) { |
|
|
|
if (strDest.size() > 255) { |
|
|
|
CloseSocket(hSocket); |
|
|
|
CloseSocket(hSocket); |
|
|
@ -294,7 +303,7 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials |
|
|
|
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() timeout or other failure\n", strDest, port); |
|
|
|
LogPrintf("Socks5() connect to %s:%d failed: InterruptibleRecv() timeout or other failure\n", strDest, port); |
|
|
|
return false; |
|
|
|
return false; |
|
|
@ -320,7 +329,7 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials |
|
|
|
} |
|
|
|
} |
|
|
|
LogPrint("proxy", "SOCKS5 sending proxy authentication %s:%s\n", auth->username, auth->password); |
|
|
|
LogPrint("proxy", "SOCKS5 sending proxy authentication %s:%s\n", 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"); |
|
|
|
} |
|
|
|
} |
|
|
@ -349,9 +358,9 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials |
|
|
|
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"); |
|
|
|
return error("Error while reading proxy response"); |
|
|
|
} |
|
|
|
} |
|
|
|
if (pchRet2[0] != 0x05) { |
|
|
|
if (pchRet2[0] != 0x05) { |
|
|
|
CloseSocket(hSocket); |
|
|
|
CloseSocket(hSocket); |
|
|
@ -370,26 +379,26 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials |
|
|
|
char pchRet3[256]; |
|
|
|
char pchRet3[256]; |
|
|
|
switch (pchRet2[3]) |
|
|
|
switch (pchRet2[3]) |
|
|
|
{ |
|
|
|
{ |
|
|
|
case 0x01: ret = InterruptibleRecv(pchRet3, 4, SOCKS5_RECV_TIMEOUT, hSocket); break; |
|
|
|
case 0x01: recvr = InterruptibleRecv(pchRet3, 4, SOCKS5_RECV_TIMEOUT, hSocket); break; |
|
|
|
case 0x04: ret = InterruptibleRecv(pchRet3, 16, SOCKS5_RECV_TIMEOUT, hSocket); break; |
|
|
|
case 0x04: recvr = InterruptibleRecv(pchRet3, 16, SOCKS5_RECV_TIMEOUT, hSocket); 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 = InterruptibleRecv(pchRet3, nRecv, SOCKS5_RECV_TIMEOUT, hSocket); |
|
|
|
recvr = InterruptibleRecv(pchRet3, nRecv, SOCKS5_RECV_TIMEOUT, hSocket); |
|
|
|
break; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
default: CloseSocket(hSocket); return error("Error: malformed proxy response"); |
|
|
|
default: CloseSocket(hSocket); 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"); |
|
|
|
} |
|
|
|
} |
|
|
|