@ -184,6 +184,48 @@ struct timeval MillisToTimeval(int64_t nTimeout)
@@ -184,6 +184,48 @@ struct timeval MillisToTimeval(int64_t nTimeout)
return timeout ;
}
/** SOCKS version */
enum SOCKSVersion : uint8_t {
SOCKS4 = 0x04 ,
SOCKS5 = 0x05
} ;
/** Values defined for METHOD in RFC1928 */
enum SOCKS5Method : uint8_t {
NOAUTH = 0x00 , //! No authentication required
GSSAPI = 0x01 , //! GSSAPI
USER_PASS = 0x02 , //! Username/password
NO_ACCEPTABLE = 0xff , //! No acceptable methods
} ;
/** Values defined for CMD in RFC1928 */
enum SOCKS5Command : uint8_t {
CONNECT = 0x01 ,
BIND = 0x02 ,
UDP_ASSOCIATE = 0x03
} ;
/** Values defined for REP in RFC1928 */
enum SOCKS5Reply : uint8_t {
SUCCEEDED = 0x00 , //! Succeeded
GENFAILURE = 0x01 , //! General failure
NOTALLOWED = 0x02 , //! Connection not allowed by ruleset
NETUNREACHABLE = 0x03 , //! Network unreachable
HOSTUNREACHABLE = 0x04 , //! Network unreachable
CONNREFUSED = 0x05 , //! Connection refused
TTLEXPIRED = 0x06 , //! TTL expired
CMDUNSUPPORTED = 0x07 , //! Command not supported
ATYPEUNSUPPORTED = 0x08 , //! Address type not supported
} ;
/** Values defined for ATYPE in RFC1928 */
enum SOCKS5Atyp : uint8_t {
IPV4 = 0x01 ,
DOMAINNAME = 0x03 ,
IPV6 = 0x04 ,
} ;
/** Status codes that can be returned by InterruptibleRecv */
enum class IntrRecvError {
OK ,
Timeout ,
@ -203,7 +245,7 @@ enum class IntrRecvError {
@@ -203,7 +245,7 @@ enum class IntrRecvError {
*
* @ note This function requires that hSocket is in non - blocking mode .
*/
static IntrRecvError InterruptibleRecv ( char * data , size_t len , int timeout , const SOCKET & hSocket )
static IntrRecvError InterruptibleRecv ( uint8_t * data , size_t len , int timeout , const SOCKET & hSocket )
{
int64_t curTime = GetTimeMillis ( ) ;
int64_t endTime = curTime + timeout ;
@ -211,7 +253,7 @@ static IntrRecvError InterruptibleRecv(char* data, size_t len, int timeout, cons
@@ -211,7 +253,7 @@ static IntrRecvError InterruptibleRecv(char* data, size_t len, int timeout, cons
// 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
ssize_t ret = recv ( hSocket , ( char * ) data , len , 0 ) ; // Optimistically try the recv first
if ( ret > 0 ) {
len - = ret ;
data + = ret ;
@ -242,24 +284,35 @@ static IntrRecvError InterruptibleRecv(char* data, size_t len, int timeout, cons
@@ -242,24 +284,35 @@ static IntrRecvError InterruptibleRecv(char* data, size_t len, int timeout, cons
return len = = 0 ? IntrRecvError : : OK : IntrRecvError : : Timeout ;
}
/** Credentials for proxy authentication */
struct ProxyCredentials
{
std : : string username ;
std : : string password ;
} ;
std : : string Socks5ErrorString ( int err )
/** Convert SOCKS5 reply to a an error message */
std : : string Socks5ErrorString ( uint8_t err )
{
switch ( err ) {
case 0x01 : return " general failure " ;
case 0x02 : return " connection not allowed " ;
case 0x03 : return " network unreachable " ;
case 0x04 : return " host unreachable " ;
case 0x05 : return " connection refused " ;
case 0x06 : return " TTL expired " ;
case 0x07 : return " protocol error " ;
case 0x08 : return " address type not supported " ;
default : return " unknown " ;
case SOCKS5Reply : : GENFAILURE :
return " general failure " ;
case SOCKS5Reply : : NOTALLOWED :
return " connection not allowed " ;
case SOCKS5Reply : : NETUNREACHABLE :
return " network unreachable " ;
case SOCKS5Reply : : HOSTUNREACHABLE :
return " host unreachable " ;
case SOCKS5Reply : : CONNREFUSED :
return " connection refused " ;
case SOCKS5Reply : : TTLEXPIRED :
return " TTL expired " ;
case SOCKS5Reply : : CMDUNSUPPORTED :
return " protocol error " ;
case SOCKS5Reply : : ATYPEUNSUPPORTED :
return " address type not supported " ;
default :
return " unknown " ;
}
}
@ -274,34 +327,34 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
@@ -274,34 +327,34 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
}
// Accepted authentication methods
std : : vector < uint8_t > vSocks5Init ;
vSocks5Init . push_back ( 0x0 5) ;
vSocks5Init . push_back ( SOCKSVersion : : SOCKS 5) ;
if ( auth ) {
vSocks5Init . push_back ( 0x02 ) ; // # METHODS
vSocks5Init . push_back ( 0x00 ) ; // X'00' NO AUTHENTICATION REQUIRED
vSocks5Init . push_back ( 0x02 ) ; // X'02' USERNAME/PASSWORD (RFC1929)
vSocks5Init . push_back ( 0x02 ) ; // Number of methods
vSocks5Init . push_back ( SOCKS5Method : : NOAUTH ) ;
vSocks5Init . push_back ( SOCKS5Method : : USER_PASS ) ;
} else {
vSocks5Init . push_back ( 0x01 ) ; // # METHODS
vSocks5Init . push_back ( 0x00 ) ; // X'00' NO AUTHENTICATION REQUIRED
vSocks5Init . push_back ( 0x01 ) ; // Number of methods
vSocks5Init . push_back ( SOCKS5Method : : NOAUTH ) ;
}
ssize_t ret = send ( hSocket , ( const char * ) vSocks5Init . data ( ) , vSocks5Init . size ( ) , MSG_NOSIGNAL ) ;
if ( ret ! = ( ssize_t ) vSocks5Init . size ( ) ) {
CloseSocket ( hSocket ) ;
return error ( " Error sending to proxy " ) ;
}
char pchRet1 [ 2 ] ;
uint8_t pchRet1 [ 2 ] ;
if ( ( recvr = InterruptibleRecv ( pchRet1 , 2 , SOCKS5_RECV_TIMEOUT , hSocket ) ) ! = IntrRecvError : : OK ) {
CloseSocket ( hSocket ) ;
LogPrintf ( " Socks5() connect to %s:%d failed: InterruptibleRecv() timeout or other failure \n " , strDest , port ) ;
return false ;
}
if ( pchRet1 [ 0 ] ! = 0x0 5) {
if ( pchRet1 [ 0 ] ! = SOCKSVersion : : SOCKS 5) {
CloseSocket ( hSocket ) ;
return error ( " Proxy failed to initialize " ) ;
}
if ( pchRet1 [ 1 ] = = 0x02 & & auth ) {
if ( pchRet1 [ 1 ] = = SOCKS5Method : : USER_PASS & & auth ) {
// Perform username/password authentication (as described in RFC1929)
std : : vector < uint8_t > vAuth ;
vAuth . push_back ( 0x01 ) ;
vAuth . push_back ( 0x01 ) ; // Current (and only) version of user/pass subnegotiation
if ( auth - > username . size ( ) > 255 | | auth - > password . size ( ) > 255 )
return error ( " Proxy username or password too long " ) ;
vAuth . push_back ( auth - > username . size ( ) ) ;
@ -314,7 +367,7 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
@@ -314,7 +367,7 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
return error ( " Error sending authentication to proxy " ) ;
}
LogPrint ( BCLog : : PROXY , " SOCKS5 sending proxy authentication %s:%s \n " , auth - > username , auth - > password ) ;
char pchRetA [ 2 ] ;
uint8_t pchRetA [ 2 ] ;
if ( ( recvr = InterruptibleRecv ( pchRetA , 2 , SOCKS5_RECV_TIMEOUT , hSocket ) ) ! = IntrRecvError : : OK ) {
CloseSocket ( hSocket ) ;
return error ( " Error reading proxy authentication response " ) ;
@ -323,17 +376,17 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
@@ -323,17 +376,17 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
CloseSocket ( hSocket ) ;
return error ( " Proxy authentication unsuccessful " ) ;
}
} else if ( pchRet1 [ 1 ] = = 0x00 ) {
} else if ( pchRet1 [ 1 ] = = SOCKS5Method : : NOAUTH ) {
// Perform no authentication
} else {
CloseSocket ( hSocket ) ;
return error ( " Proxy requested wrong authentication method %02x " , pchRet1 [ 1 ] ) ;
}
std : : vector < uint8_t > vSocks5 ;
vSocks5 . push_back ( 0x0 5) ; // VER protocol version
vSocks5 . push_back ( 0x01 ) ; // CMD CONNECT
vSocks5 . push_back ( 0x00 ) ; // RSV Reserved
vSocks5 . push_back ( 0x03 ) ; // ATYP DOMAINNAME
vSocks5 . push_back ( SOCKSVersion : : SOCKS 5) ; // VER protocol version
vSocks5 . push_back ( SOCKS5Command : : CONNECT ) ; // CMD CONNECT
vSocks5 . push_back ( 0x00 ) ; // RSV Reserved must be 0
vSocks5 . push_back ( SOCKS5Atyp : : DOMAINNAME ) ; // ATYP DOMAINNAME
vSocks5 . push_back ( strDest . size ( ) ) ; // Length<=255 is checked at beginning of function
vSocks5 . insert ( vSocks5 . end ( ) , strDest . begin ( ) , strDest . end ( ) ) ;
vSocks5 . push_back ( ( port > > 8 ) & 0xFF ) ;
@ -343,7 +396,7 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
@@ -343,7 +396,7 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
CloseSocket ( hSocket ) ;
return error ( " Error sending to proxy " ) ;
}
char pchRet2 [ 4 ] ;
uint8_t pchRet2 [ 4 ] ;
if ( ( recvr = InterruptibleRecv ( pchRet2 , 4 , SOCKS5_RECV_TIMEOUT , hSocket ) ) ! = IntrRecvError : : OK ) {
CloseSocket ( hSocket ) ;
if ( recvr = = IntrRecvError : : Timeout ) {
@ -355,26 +408,26 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
@@ -355,26 +408,26 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
return error ( " Error while reading proxy response " ) ;
}
}
if ( pchRet2 [ 0 ] ! = 0x0 5) {
if ( pchRet2 [ 0 ] ! = SOCKSVersion : : SOCKS 5) {
CloseSocket ( hSocket ) ;
return error ( " Proxy failed to accept request " ) ;
}
if ( pchRet2 [ 1 ] ! = 0x00 ) {
if ( pchRet2 [ 1 ] ! = SOCKS5Reply : : SUCCEEDED ) {
// Failures to connect to a peer that are not proxy errors
CloseSocket ( hSocket ) ;
LogPrintf ( " Socks5() connect to %s:%d failed: %s \n " , strDest , port , Socks5ErrorString ( pchRet2 [ 1 ] ) ) ;
return false ;
}
if ( pchRet2 [ 2 ] ! = 0x00 ) {
if ( pchRet2 [ 2 ] ! = 0x00 ) { // Reserved field must be 0
CloseSocket ( hSocket ) ;
return error ( " Error: malformed proxy response " ) ;
}
char pchRet3 [ 256 ] ;
uint8_t pchRet3 [ 256 ] ;
switch ( pchRet2 [ 3 ] )
{
case 0x01 : recvr = InterruptibleRecv ( pchRet3 , 4 , SOCKS5_RECV_TIMEOUT , hSocket ) ; break ;
case 0x04 : recvr = InterruptibleRecv ( pchRet3 , 16 , SOCKS5_RECV_TIMEOUT , hSocket ) ; break ;
case 0x03 :
case SOCKS5Atyp : : IPV4 : recvr = InterruptibleRecv ( pchRet3 , 4 , SOCKS5_RECV_TIMEOUT , hSocket ) ; break ;
case SOCKS5Atyp : : IPV6 : recvr = InterruptibleRecv ( pchRet3 , 16 , SOCKS5_RECV_TIMEOUT , hSocket ) ; break ;
case SOCKS5Atyp : : DOMAINNAME :
{
recvr = InterruptibleRecv ( pchRet3 , 1 , SOCKS5_RECV_TIMEOUT , hSocket ) ;
if ( recvr ! = IntrRecvError : : OK ) {