@ -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,16 @@ 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 " ) ;
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 ) ;
@ -370,26 +386,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 " ) ;
}
}