@ -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)
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)
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)
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)
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
// 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
}
}
// 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 ;
}