@@ -0,0 +1,618 @@
# include <GlobalConst.h>
# include <Mayaqua/Mayaqua.h>
SOCK * Internal_ProxyTcpConnect ( PROXY_PARAM_IN * param , volatile bool * cancel_flag , IP * resolved_ip )
{
# ifdef OS_WIN32
if ( param - > Hwnd ! = NULL )
{
return WinConnectEx3 ( ( HWND ) param - > Hwnd , param - > Hostname , param - > Port , param - > Timeout , 0 , NULL , NULL , NULL , NULL , false ) ;
}
# endif
return ConnectEx4 ( param - > Hostname , param - > Port , param - > Timeout , ( bool * ) cancel_flag , NULL , NULL , false , true , resolved_ip ) ;
}
// Connect to an HTTP proxy
UINT ProxyHttpConnect ( PROXY_PARAM_OUT * out , PROXY_PARAM_IN * in , volatile bool * cancel_flag )
{
bool dummy_cancel_flag = false , use_auth = false ;
char target_hostname [ MAX_HOST_NAME_LEN + 1 ] ;
char target_hostname_port [ MAX_SIZE ] ;
HTTP_HEADER * h ;
UINT i , ret ;
SOCK * s ;
// Validate arguments
if ( out = = NULL | | in = = NULL | | in - > Port = = 0 | | in - > TargetPort = = 0 | | IsEmptyStr ( in - > Hostname ) | | IsEmptyStr ( in - > TargetHostname ) )
{
return PROXY_ERROR_PARAMETER ;
}
if ( cancel_flag = = NULL )
{
cancel_flag = & dummy_cancel_flag ;
}
else if ( * cancel_flag )
{
return PROXY_ERROR_CANCELED ;
}
Zero ( out , sizeof ( PROXY_PARAM_OUT ) ) ;
// Open TCP connection to the proxy server
s = Internal_ProxyTcpConnect ( in , cancel_flag , & out - > ResolvedIp ) ;
if ( s = = NULL )
{
return PROXY_ERROR_CONNECTION ;
}
SetTimeout ( s , MIN ( PROXY_CONNECTION_TIMEOUT , ( in - > Timeout = = 0 ? INFINITE : in - > Timeout ) ) ) ;
if ( ( IsEmptyStr ( in - > Username ) | | IsEmptyStr ( in - > Password ) ) = = false )
{
use_auth = true ;
}
Zero ( target_hostname , sizeof ( target_hostname ) ) ;
StrCpy ( target_hostname , sizeof ( target_hostname ) , in - > TargetHostname ) ;
for ( i = 0 ; i < StrLen ( target_hostname ) ; + + i )
{
if ( target_hostname [ i ] = = ' / ' )
{
target_hostname [ i ] = 0 ;
}
}
// Generate HTTP header
if ( IsStrIPv6Address ( target_hostname ) )
{
IP ip ;
char iptmp [ MAX_PATH ] ;
StrToIP ( & ip , target_hostname ) ;
IPToStr ( iptmp , sizeof ( iptmp ) , & ip ) ;
Format ( target_hostname_port , sizeof ( target_hostname_port ) , " [%s]:%hu " , iptmp , in - > TargetPort ) ;
}
else
{
Format ( target_hostname_port , sizeof ( target_hostname_port ) , " %s:%hu " , target_hostname , in - > TargetPort ) ;
}
h = NewHttpHeader ( " CONNECT " , target_hostname_port , " HTTP/1.0 " ) ;
if ( IsEmptyStr ( in - > HttpCustomHeader ) = = false )
{
TOKEN_LIST * tokens = ParseToken ( in - > HttpCustomHeader , " \r \n " ) ;
if ( tokens ! = NULL )
{
for ( i = 0 ; i < tokens - > NumTokens ; i + + )
{
AddHttpValueStr ( h , tokens - > Token [ i ] ) ;
}
FreeToken ( tokens ) ;
}
}
if ( GetHttpValue ( h , " User-Agent " ) = = NULL )
{
AddHttpValue ( h , NewHttpValue ( " User-Agent " , IsEmptyStr ( in - > HttpUserAgent ) ? DEFAULT_USER_AGENT : in - > HttpUserAgent ) ) ;
}
if ( GetHttpValue ( h , " Host " ) = = NULL )
{
AddHttpValue ( h , NewHttpValue ( " Host " , target_hostname ) ) ;
}
if ( GetHttpValue ( h , " Content-Length " ) = = NULL )
{
AddHttpValue ( h , NewHttpValue ( " Content-Length " , " 0 " ) ) ;
}
if ( GetHttpValue ( h , " Proxy-Connection " ) = = NULL )
{
AddHttpValue ( h , NewHttpValue ( " Proxy-Connection " , " Keep-Alive " ) ) ;
}
if ( GetHttpValue ( h , " Pragma " ) = = NULL )
{
AddHttpValue ( h , NewHttpValue ( " Pragma " , " no-cache " ) ) ;
}
if ( use_auth & & GetHttpValue ( h , " Proxy-Authorization " ) = = NULL )
{
char auth_str [ MAX_SIZE * 2 ] , auth_b64_str [ MAX_SIZE * 2 ] ;
// Generate the authentication string
Format ( auth_str , sizeof ( auth_str ) , " %s:%s " , in - > Username , in - > Password ) ;
// Base64 encode
Zero ( auth_b64_str , sizeof ( auth_b64_str ) ) ;
Encode64 ( auth_b64_str , auth_str ) ;
// Generate final string
Format ( auth_str , sizeof ( auth_str ) , " Basic %s " , auth_b64_str ) ;
AddHttpValue ( h , NewHttpValue ( " Proxy-Authorization " , auth_str ) ) ;
}
// Transmission
ret = SendHttpHeader ( s , h ) ;
FreeHttpHeader ( h ) ;
if ( ret = = false )
{
ret = PROXY_ERROR_DISCONNECTED ;
goto FAILURE ;
}
if ( * cancel_flag )
{
ret = PROXY_ERROR_CANCELED ;
goto FAILURE ;
}
// Receive the results
h = RecvHttpHeader ( s ) ;
if ( h = = NULL )
{
FreeHttpHeader ( h ) ;
ret = PROXY_ERROR_GENERIC ;
goto FAILURE ;
}
ret = 0 ;
if ( StrLen ( h - > Method ) = = 8 )
{
if ( Cmp ( h - > Method , " HTTP/1. " , 7 ) = = 0 )
{
ret = ToInt ( h - > Target ) ;
}
}
FreeHttpHeader ( h ) ;
// Check the code
switch ( ret )
{
case 401 :
case 403 :
case 407 :
// Authentication failure
ret = PROXY_ERROR_AUTHENTICATION ;
goto FAILURE ;
default :
if ( ( ret / 100 ) = = 2 )
{
// Success
SetTimeout ( s , INFINITE ) ;
out - > Sock = s ;
return PROXY_ERROR_SUCCESS ;
}
else
{
// Unknown result
ret = PROXY_ERROR_GENERIC ;
}
}
FAILURE :
Disconnect ( s ) ;
ReleaseSock ( s ) ;
return ret ;
}
// Connect to a SOCKS5 proxy (RFC1928, RFC1929 defines username/password authentication)
UINT ProxySocks5Connect ( PROXY_PARAM_OUT * out , PROXY_PARAM_IN * in , volatile bool * cancel_flag )
{
bool dummy_cancel_flag = false ;
UCHAR tmp , recv_buf [ 2 ] , * recv_buf_final ;
USHORT target_port ;
IP target_ip ;
UINT ret ;
SOCK * s ;
BUF * b ;
// Validate arguments
if ( out = = NULL | | in = = NULL | | in - > Port = = 0 | | in - > TargetPort = = 0 | | IsEmptyStr ( in - > Hostname ) | | IsEmptyStr ( in - > TargetHostname ) )
{
return PROXY_ERROR_PARAMETER ;
}
if ( cancel_flag = = NULL )
{
cancel_flag = & dummy_cancel_flag ;
}
else if ( * cancel_flag )
{
return PROXY_ERROR_CANCELED ;
}
Zero ( out , sizeof ( PROXY_PARAM_OUT ) ) ;
// Open TCP connection to the proxy server
s = Internal_ProxyTcpConnect ( in , cancel_flag , & out - > ResolvedIp ) ;
if ( s = = NULL )
{
return PROXY_ERROR_CONNECTION ;
}
SetTimeout ( s , MIN ( PROXY_CONNECTION_TIMEOUT , ( in - > Timeout = = 0 ? INFINITE : in - > Timeout ) ) ) ;
// +----+----------+----------+
// |VER | NMETHODS | METHODS |
// +----+----------+----------+
// | 1 | 1 | 1 to 255 |
// +----+----------+----------+
//
// X'00' NO AUTHENTICATION REQUIRED
// X'01' GSSAPI
// X'02' USERNAME/PASSWORD
// X'03' to X'7F' IANA ASSIGNED
// X'80' to X'FE' RESERVED FOR PRIVATE METHODS
// X'FF' NO ACCEPTABLE METHOD
b = NewBuf ( ) ;
tmp = 5 ;
WriteBuf ( b , & tmp , sizeof ( tmp ) ) ; // SOCKS version
tmp = 2 ;
WriteBuf ( b , & tmp , sizeof ( tmp ) ) ; // Number of supported methods
tmp = 0 ;
WriteBuf ( b , & tmp , sizeof ( tmp ) ) ; // No authentication
tmp = 2 ;
WriteBuf ( b , & tmp , sizeof ( tmp ) ) ; // Username/password
ret = SendAll ( s , b - > Buf , b - > Size , false ) ;
if ( ret = = false )
{
FreeBuf ( b ) ;
Debug ( " ProxySocks5Connect(): [Phase 1] Failed to send initial data to the server. \n " ) ;
ret = PROXY_ERROR_DISCONNECTED ;
goto FAILURE ;
}
// +----+--------+
// |VER | METHOD |
// +----+--------+
// | 1 | 1 |
// +----+--------+
if ( RecvAll ( s , recv_buf , sizeof ( recv_buf ) , false ) = = false )
{
FreeBuf ( b ) ;
Debug ( " ProxySocks5Connect(): [Phase 1] Failed to receive initial data response from the server. \n " ) ;
ret = PROXY_ERROR_DISCONNECTED ;
goto FAILURE ;
}
if ( recv_buf [ 0 ] ! = 5 )
{
FreeBuf ( b ) ;
Debug ( " ProxySocks5Connect(): [Phase 1] Unmatching version: %u. \n " , recv_buf [ 0 ] ) ;
ret = PROXY_ERROR_VERSION ;
goto FAILURE ;
}
ClearBuf ( b ) ;
// Username/password authentication (RFC1929)
if ( recv_buf [ 1 ] = = 2 )
{
// +----+------+----------+------+----------+
// |VER | ULEN | UNAME | PLEN | PASSWD |
// +----+------+----------+------+----------+
// | 1 | 1 | 1 to 255 | 1 | 1 to 255 |
// +----+------+----------+------+----------+
tmp = 1 ;
WriteBuf ( b , & tmp , sizeof ( tmp ) ) ; // Authentication protocol version
tmp = StrLen ( in - > Username ) ;
WriteBuf ( b , & tmp , sizeof ( tmp ) ) ; // Username length
WriteBuf ( b , in - > Username , tmp ) ; // Username
tmp = StrLen ( in - > Password ) ;
WriteBuf ( b , & tmp , sizeof ( tmp ) ) ; // Password length
WriteBuf ( b , in - > Password , tmp ) ; // Password
ret = SendAll ( s , b - > Buf , b - > Size , false ) ;
ClearBuf ( b ) ;
if ( ret = = false )
{
Debug ( " ProxySocks5Connect(): [Phase 1] Failed to send authentication data to the server. \n " ) ;
ret = PROXY_ERROR_DISCONNECTED ;
goto FAILURE ;
}
// +----+--------+
// |VER | STATUS |
// +----+--------+
// | 1 | 1 |
// +----+--------+
if ( RecvAll ( s , recv_buf , sizeof ( recv_buf ) , false ) = = false )
{
Debug ( " ProxySocks5Connect(): [Phase 1] Failed to receive authentication data response from the server. \n " ) ;
ret = PROXY_ERROR_DISCONNECTED ;
goto FAILURE ;
}
if ( recv_buf [ 1 ] ! = 0 )
{
Debug ( " ProxySocks5Connect(): [Phase 1] Authentication failure, error code sent by the server: %u. \n " , recv_buf [ 1 ] ) ;
ret = PROXY_ERROR_AUTHENTICATION ;
goto FAILURE ;
}
}
// +----+-----+-------+------+----------+----------+
// |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
// +----+-----+-------+------+----------+----------+
// | 1 | 1 | X'00' | 1 | Variable | 2 |
// +----+-----+-------+------+----------+----------+
//
// VER protocol version: X'05'
// CMD
// CONNECT X'01'
// BIND X'02'
// UDP ASSOCIATE X'03'
// RSV RESERVED
// ATYP address type of following address
// IP V4 address X'01'
// DOMAINNAME X'03'
// IP V6 address X'04'
// DST.ADDR desired destination address
// DST.PORT desired destination port in network octet order
// Prepare data to send
tmp = 5 ;
WriteBuf ( b , & tmp , sizeof ( tmp ) ) ; // SOCKS version
tmp = 1 ;
WriteBuf ( b , & tmp , sizeof ( tmp ) ) ; // Command
tmp = 0 ;
WriteBuf ( b , & tmp , sizeof ( tmp ) ) ; // Reserved byte
// Convert the hostname to an IP structure (if it's an IP address)
StrToIP ( & target_ip , in - > TargetHostname ) ;
// If the IP structure doesn't contain an IP address, the string should be an hostname
if ( IsZeroIp ( & target_ip ) )
{
UCHAR dest_length = StrLen ( in - > TargetHostname ) ;
tmp = 3 ;
WriteBuf ( b , & tmp , sizeof ( tmp ) ) ; // Destination type (hostname)
WriteBuf ( b , & dest_length , sizeof ( dest_length ) ) ; // Destination hostname length
WriteBuf ( b , in - > TargetHostname , dest_length ) ; // Destination hostname
}
else
{
if ( IsIP6 ( & target_ip ) )
{
tmp = 4 ;
WriteBuf ( b , & tmp , sizeof ( tmp ) ) ; // Destination type (IPv6)
WriteBuf ( b , target_ip . ipv6_addr , sizeof ( target_ip . ipv6_addr ) ) ; // Destination IPv6 address
}
else
{
tmp = 1 ;
WriteBuf ( b , & tmp , sizeof ( tmp ) ) ; // Destination type (IPv4)
WriteBuf ( b , target_ip . addr , sizeof ( target_ip . addr ) ) ; // Destination IPv4 address
}
}
// Convert the port in network octet order
target_port = Endian16 ( in - > TargetPort ) ;
WriteBuf ( b , & target_port , sizeof ( target_port ) ) ; // Destination port
// Send data
ret = SendAll ( s , b - > Buf , b - > Size , false ) ;
FreeBuf ( b ) ;
if ( ret = = false )
{
Debug ( " ProxySocks5Connect(): [Phase 2] Failed to send data to the server. \n " ) ;
ret = PROXY_ERROR_DISCONNECTED ;
goto FAILURE ;
}
// +----+-----+-------+------+----------+----------+
// |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
// +----+-----+-------+------+----------+----------+
// | 1 | 1 | X’ 00’ | 1 | Variable | 2 |
// +----+-----+-------+------+----------+----------+
//
// VER protocol version: X’ 05’
// REP Reply field:
// X’ 00’ succeeded
// X’ 01’ general SOCKS server failure
// X’ 02’ connection not allowed by ruleset
// X’ 03’ Network unreachable
// X’ 04’ Host unreachable
// X’ 05’ Connection refused
// X’ 06’ TTL expired
// X’ 07’ Command not supported
// X’ 08’ Address type not supported
// X’ 09’ to X’ FF’ unassigned
// The packet sent by the server should always have the same size as the one we sent to it.
// However, there are some implementations which send fixed values (aside from the first 2 bytes).
// In order to support such implementations, we read the first 4 bytes in order to know the address type before trying to read the rest of the packet.
recv_buf_final = Malloc ( 4 ) ;
if ( RecvAll ( s , recv_buf_final , 4 , false ) = = false )
{
Free ( recv_buf_final ) ;
Debug ( " ProxySocks5Connect(): [Phase 2] Failed to receive response from the server. \n " ) ;
ret = PROXY_ERROR_DISCONNECTED ;
goto FAILURE ;
}
// We only need the first two bytes (version and response code), but we have to read the entire packet from the socket
recv_buf [ 0 ] = recv_buf_final [ 0 ] ;
recv_buf [ 1 ] = recv_buf_final [ 1 ] ;
// We receive the rest of the packet by knowing the size according to the address type
switch ( recv_buf_final [ 3 ] )
{
case 1 :
// IPv4
recv_buf_final = ReAlloc ( recv_buf_final , 6 ) ; // 4 bytes (IPv4) + 2 bytes (port)
ret = RecvAll ( s , recv_buf_final , 6 , false ) ;
break ;
case 4 :
// IPv6
recv_buf_final = ReAlloc ( recv_buf_final , 18 ) ; // 16 bytes (IPv6) + 2 bytes (port)
ret = RecvAll ( s , recv_buf_final , 18 , false ) ;
break ;
case 3 :
// Hostname
ret = RecvAll ( s , & tmp , 1 , false ) ;
if ( ret = = true )
{
recv_buf_final = ReAlloc ( recv_buf_final , tmp + 2 ) ; // Hostname length + 2 bytes (port)
ret = RecvAll ( s , recv_buf_final , tmp + 2 , false ) ;
}
}
Free ( recv_buf_final ) ;
if ( ret = = false )
{
Debug ( " ProxySocks5Connect(): [Phase 2] Malformed response received from the server. \n " ) ;
ret = PROXY_ERROR_DISCONNECTED ;
goto FAILURE ;
}
if ( recv_buf [ 0 ] ! = 5 )
{
Debug ( " ProxySocks5Connect(): [Phase 2] Unmatching version: %u. \n " , recv_buf_final [ 0 ] ) ;
ret = PROXY_ERROR_VERSION ;
goto FAILURE ;
}
switch ( recv_buf [ 1 ] )
{
case 0 :
// Success
SetTimeout ( s , INFINITE ) ;
out - > Sock = s ;
return PROXY_ERROR_SUCCESS ;
case 3 :
case 4 :
case 5 :
Debug ( " ProxySocks5Connect(): [Phase 2] Connection to target failed with error: %u \n " , recv_buf [ 1 ] ) ;
ret = PROXY_ERROR_TARGET ;
goto FAILURE ;
default :
Debug ( " ProxySocks5Connect(): [Phase 2] Connection failed with error: %u \n " , recv_buf [ 1 ] ) ;
ret = PROXY_ERROR_GENERIC ;
goto FAILURE ;
}
FAILURE :
Disconnect ( s ) ;
ReleaseSock ( s ) ;
return ret ;
}
// Connect to a SOCKS4 proxy
UINT ProxySocks4Connect ( PROXY_PARAM_OUT * out , PROXY_PARAM_IN * in , volatile bool * cancel_flag )
{
bool dummy_cancel_flag = false ;
UCHAR tmp , recv_buf [ 8 ] ;
USHORT target_port ;
IP target_ip ;
UINT ret ;
SOCK * s ;
BUF * b ;
// Validate arguments
if ( out = = NULL | | in = = NULL | | in - > Port = = 0 | | in - > TargetPort = = 0 | | IsEmptyStr ( in - > Hostname ) | | IsEmptyStr ( in - > TargetHostname ) )
{
return PROXY_ERROR_PARAMETER ;
}
if ( cancel_flag = = NULL )
{
cancel_flag = & dummy_cancel_flag ;
}
else if ( * cancel_flag )
{
return PROXY_ERROR_CANCELED ;
}
Zero ( out , sizeof ( PROXY_PARAM_OUT ) ) ;
// Get the IP address of the destination server
if ( GetIP ( & target_ip , in - > TargetHostname ) = = false )
{
return PROXY_ERROR_CONNECTION ;
}
// Open TCP connection to the proxy server
s = Internal_ProxyTcpConnect ( in , cancel_flag , & out - > ResolvedIp ) ;
if ( s = = NULL )
{
return PROXY_ERROR_CONNECTION ;
}
SetTimeout ( s , MIN ( PROXY_CONNECTION_TIMEOUT , ( in - > Timeout = = 0 ? INFINITE : in - > Timeout ) ) ) ;
// Send request packet
b = NewBuf ( ) ;
tmp = 4 ;
WriteBuf ( b , & tmp , sizeof ( tmp ) ) ;
tmp = 1 ;
WriteBuf ( b , & tmp , sizeof ( tmp ) ) ;
target_port = Endian16 ( in - > TargetPort ) ;
WriteBuf ( b , & target_port , sizeof ( target_port ) ) ;
WriteBuf ( b , target_ip . addr , sizeof ( target_ip . addr ) ) ;
WriteBuf ( b , in - > Username , StrLen ( in - > Username ) + 1 ) ;
ret = SendAll ( s , b - > Buf , b - > Size , false ) ;
FreeBuf ( b ) ;
if ( ret = = false )
{
ret = PROXY_ERROR_DISCONNECTED ;
goto FAILURE ;
}
// Receive response packet
if ( RecvAll ( s , recv_buf , sizeof ( recv_buf ) , false ) = = false )
{
ret = PROXY_ERROR_DISCONNECTED ;
goto FAILURE ;
}
if ( recv_buf [ 0 ] ! = 0 )
{
ret = PROXY_ERROR_GENERIC ;
goto FAILURE ;
}
switch ( recv_buf [ 1 ] )
{
case 90 :
// Success
SetTimeout ( s , INFINITE ) ;
out - > Sock = s ;
return PROXY_ERROR_SUCCESS ;
case 93 :
// Authentication failure
ret = PROXY_ERROR_AUTHENTICATION ;
goto FAILURE ;
default :
// Failed to connect to the target server
ret = PROXY_ERROR_TARGET ;
}
FAILURE :
Disconnect ( s ) ;
ReleaseSock ( s ) ;
return ret ;
}