1
0
mirror of https://github.com/SoftEtherVPN/SoftEtherVPN.git synced 2025-07-08 00:34:57 +03:00

Add support for SOCKS5 proxy protocol

This commit is contained in:
Davide Beatrici
2018-10-30 15:11:16 +01:00
parent 919cb2ae75
commit 53e0a2e5e2
16 changed files with 791 additions and 147 deletions

View File

@ -222,6 +222,7 @@ void CmProxyDlgSet(HWND hWnd, CLIENT_OPTION *o, CM_INTERNET_SETTING *setting)
Check(hWnd, R_DIRECT_TCP, setting->ProxyType == PROXY_DIRECT);
Check(hWnd, R_HTTPS, setting->ProxyType == PROXY_HTTP);
Check(hWnd, R_SOCKS, setting->ProxyType == PROXY_SOCKS);
Check(hWnd, R_SOCKS5, setting->ProxyType == PROXY_SOCKS5);
// Proxy Settings
if(setting->ProxyType != PROXY_DIRECT)
@ -6906,6 +6907,10 @@ void CmEditAccountDlgUpdate(HWND hWnd, CM_ACCOUNT *a)
{
a->ClientOption->ProxyType = PROXY_SOCKS;
}
if (IsChecked(hWnd, R_SOCKS5))
{
a->ClientOption->ProxyType = PROXY_SOCKS5;
}
// To validate the server certificate
a->CheckServerCert = IsChecked(hWnd, R_CHECK_CERT);
@ -7344,6 +7349,7 @@ void CmEditAccountDlgInit(HWND hWnd, CM_ACCOUNT *a)
Check(hWnd, R_DIRECT_TCP, a->ClientOption->ProxyType == PROXY_DIRECT);
Check(hWnd, R_HTTPS, a->ClientOption->ProxyType == PROXY_HTTP);
Check(hWnd, R_SOCKS, a->ClientOption->ProxyType == PROXY_SOCKS);
Check(hWnd, R_SOCKS5, a->ClientOption->ProxyType == PROXY_SOCKS5);
// Verify the server certificate
Check(hWnd, R_CHECK_CERT, a->CheckServerCert);
@ -8620,6 +8626,10 @@ void CmEditAccountDlgStartEnumHub(HWND hWnd, CM_ACCOUNT *a)
{
a->ClientOption->ProxyType = PROXY_SOCKS;
}
if (IsChecked(hWnd, R_SOCKS5))
{
a->ClientOption->ProxyType = PROXY_SOCKS5;
}
CmEnumHubStart(hWnd, a->ClientOption);

View File

@ -391,7 +391,8 @@
// Type of proxy
#define PROXY_DIRECT 0 // Direct TCP connection
#define PROXY_HTTP 1 // Connection via HTTP proxy server
#define PROXY_SOCKS 2 // Connection via SOCKS proxy server
#define PROXY_SOCKS 2 // Connection via SOCKS4 proxy server
#define PROXY_SOCKS5 3 // Connection via SOCKS5 proxy server
// Direction of data flow
#define TCP_BOTH 0 // Bi-directional

View File

@ -3057,6 +3057,7 @@ void PcMain(PC *pc)
{"AccountProxyNone", PcAccountProxyNone},
{"AccountProxyHttp", PcAccountProxyHttp},
{"AccountProxySocks", PcAccountProxySocks},
{"AccountProxySocks5", PcAccountProxySocks5},
{"AccountServerCertEnable", PcAccountServerCertEnable},
{"AccountServerCertDisable", PcAccountServerCertDisable},
{"AccountRetryOnServerCertEnable", PcAccountRetryOnServerCertEnable},
@ -4075,6 +4076,8 @@ wchar_t *GetProtocolName(UINT n)
return _UU("PROTO_HTTP_PROXY");
case PROXY_SOCKS:
return _UU("PROTO_SOCKS_PROXY");
case PROXY_SOCKS5:
return _UU("PROTO_SOCKS5_PROXY");
}
return _UU("PROTO_UNKNOWN");
@ -5239,7 +5242,7 @@ UINT PcAccountProxyHttp(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
return ret;
}
// Set the connection method of the connection settings to the SOCKS proxy server connection
// Set the connection method of the connection settings to the SOCKS4 proxy server connection
UINT PcAccountProxySocks(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
{
LIST *o;
@ -5276,7 +5279,7 @@ UINT PcAccountProxySocks(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
UINT port;
// Data change
if (ParseHostPort(GetParamStr(o, "SERVER"), &host, &port, 8080))
if (ParseHostPort(GetParamStr(o, "SERVER"), &host, &port, 1080))
{
t.ClientOption->ProxyType = PROXY_SOCKS;
StrCpy(t.ClientOption->ProxyName, sizeof(t.ClientOption->ProxyName), host);
@ -5311,6 +5314,78 @@ UINT PcAccountProxySocks(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
return ret;
}
// Set the connection method of the connection settings to the SOCKS5 proxy server connection
UINT PcAccountProxySocks5(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
{
LIST *o;
PC *pc = (PC *)param;
UINT ret = ERR_NO_ERROR;
RPC_CLIENT_GET_ACCOUNT t;
// Parameter list that can be specified
PARAM args[] =
{
{"[name]", CmdPrompt, _UU("CMD_AccountCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
{"SERVER", CmdPrompt, _UU("CMD_AccountProxyHttp_Prompt_Server"), CmdEvalHostAndPort, NULL},
{"USERNAME", CmdPrompt, NULL, NULL, NULL},
{"PASSWORD", CmdPrompt, NULL, NULL, NULL},
};
// Get the parameter list
o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
if (o == NULL)
{
return ERR_INVALID_PARAMETER;
}
// RPC call
Zero(&t, sizeof(t));
UniStrCpy(t.AccountName, sizeof(t.AccountName), GetParamUniStr(o, "[name]"));
ret = CcGetAccount(pc->RemoteClient, &t);
if (ret == ERR_NO_ERROR)
{
RPC_CLIENT_CREATE_ACCOUNT z;
char *host;
UINT port;
// Data change
if (ParseHostPort(GetParamStr(o, "SERVER"), &host, &port, 1080))
{
t.ClientOption->ProxyType = PROXY_SOCKS5;
StrCpy(t.ClientOption->ProxyName, sizeof(t.ClientOption->ProxyName), host);
t.ClientOption->ProxyPort = port;
StrCpy(t.ClientOption->ProxyUsername, sizeof(t.ClientOption->ProxyName), GetParamStr(o, "USERNAME"));
StrCpy(t.ClientOption->ProxyPassword, sizeof(t.ClientOption->ProxyName), GetParamStr(o, "PASSWORD"));
Free(host);
}
Zero(&z, sizeof(z));
z.CheckServerCert = t.CheckServerCert;
z.RetryOnServerCert = t.RetryOnServerCert;
z.ClientAuth = t.ClientAuth;
z.ClientOption = t.ClientOption;
z.ServerCert = t.ServerCert;
z.StartupAccount = t.StartupAccount;
ret = CcSetAccount(pc->RemoteClient, &z);
}
if (ret != ERR_NO_ERROR)
{
// Error has occurred
CmdPrintError(c, ret);
}
CiFreeClientGetAccount(&t);
// Release of the parameter list
FreeParamValueList(o);
return ret;
}
// Enable validation option for server certificate of connection settings
UINT PcAccountServerCertEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
{
@ -7403,6 +7478,7 @@ void PsMain(PS *ps)
{"CascadeProxyNone", PsCascadeProxyNone},
{"CascadeProxyHttp", PsCascadeProxyHttp},
{"CascadeProxySocks", PsCascadeProxySocks},
{"CascadeProxySocks5", PsCascadeProxySocks5},
{"CascadeServerCertEnable", PsCascadeServerCertEnable},
{"CascadeServerCertDisable", PsCascadeServerCertDisable},
{"CascadeServerCertSet", PsCascadeServerCertSet},
@ -13644,7 +13720,7 @@ UINT PsCascadeProxyHttp(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
return 0;
}
// Set the cascade connection method as the mode via SOCKS proxy server
// Set the cascade connection method as the mode via SOCKS4 proxy server
UINT PsCascadeProxySocks(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
{
LIST *o;
@ -13695,7 +13771,7 @@ UINT PsCascadeProxySocks(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
UINT port;
// Data change
if (ParseHostPort(GetParamStr(o, "SERVER"), &host, &port, 8080))
if (ParseHostPort(GetParamStr(o, "SERVER"), &host, &port, 1080))
{
t.ClientOption->ProxyType = PROXY_SOCKS;
StrCpy(t.ClientOption->ProxyName, sizeof(t.ClientOption->ProxyName), host);
@ -13722,6 +13798,84 @@ UINT PsCascadeProxySocks(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
return 0;
}
// Set the cascade connection method as the mode via SOCKS5 proxy server
UINT PsCascadeProxySocks5(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
{
LIST *o;
PS *ps = (PS *)param;
UINT ret = 0;
RPC_CREATE_LINK t;
// Parameter list that can be specified
PARAM args[] =
{
// "name", prompt_proc, prompt_param, eval_proc, eval_param
{"[name]", CmdPrompt, _UU("CMD_CascadeCreate_Prompt_Name"), CmdEvalNotEmpty, NULL},
{"SERVER", CmdPrompt, _UU("CMD_CascadeProxyHttp_Prompt_Server"), CmdEvalHostAndPort, NULL},
{"USERNAME", NULL, NULL, NULL, NULL},
{"PASSWORD", NULL, NULL, NULL, NULL},
};
// If virtual HUB is not selected, it's an error
if (ps->HubName == NULL)
{
c->Write(c, _UU("CMD_Hub_Not_Selected"));
return ERR_INVALID_PARAMETER;
}
o = ParseCommandList(c, cmd_name, str, args, sizeof(args) / sizeof(args[0]));
if (o == NULL)
{
return ERR_INVALID_PARAMETER;
}
Zero(&t, sizeof(t));
StrCpy(t.HubName, sizeof(t.HubName), ps->HubName);
t.ClientOption = ZeroMalloc(sizeof(CLIENT_OPTION));
UniStrCpy(t.ClientOption->AccountName, sizeof(t.ClientOption->AccountName), GetParamUniStr(o, "[name]"));
// RPC call
ret = ScGetLink(ps->Rpc, &t);
if (ret != ERR_NO_ERROR)
{
// An error has occured
CmdPrintError(c, ret);
FreeParamValueList(o);
return ret;
}
else
{
char *host;
UINT port;
// Data change
if (ParseHostPort(GetParamStr(o, "SERVER"), &host, &port, 1080))
{
t.ClientOption->ProxyType = PROXY_SOCKS5;
StrCpy(t.ClientOption->ProxyName, sizeof(t.ClientOption->ProxyName), host);
t.ClientOption->ProxyPort = port;
StrCpy(t.ClientOption->ProxyUsername, sizeof(t.ClientOption->ProxyName), GetParamStr(o, "USERNAME"));
StrCpy(t.ClientOption->ProxyPassword, sizeof(t.ClientOption->ProxyName), GetParamStr(o, "PASSWORD"));
Free(host);
}
ret = ScSetLink(ps->Rpc, &t);
if (ret != ERR_NO_ERROR)
{
// An error has occured
CmdPrintError(c, ret);
FreeParamValueList(o);
return ret;
}
FreeRpcCreateLink(&t);
}
FreeParamValueList(o);
return 0;
}
// Enable the validation options for the server certificate of cascade connection
UINT PsCascadeServerCertEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param)
{

View File

@ -456,6 +456,7 @@ UINT PcAccountCompressDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *pa
UINT PcAccountProxyNone(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
UINT PcAccountProxyHttp(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
UINT PcAccountProxySocks(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
UINT PcAccountProxySocks5(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
UINT PcAccountServerCertEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
UINT PcAccountServerCertDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
UINT PcAccountRetryOnServerCertEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
@ -593,6 +594,7 @@ UINT PsCascadeCompressDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *pa
UINT PsCascadeProxyNone(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
UINT PsCascadeProxyHttp(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
UINT PsCascadeProxySocks(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
UINT PsCascadeProxySocks5(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
UINT PsCascadeServerCertEnable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
UINT PsCascadeServerCertDisable(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);
UINT PsCascadeServerCertSet(CONSOLE *c, char *cmd_name, wchar_t *str, void *param);

View File

@ -6271,8 +6271,7 @@ SOCK *ClientConnectGetSocket(CONNECTION *c, bool additional_connect)
{
SOCK *s = NULL;
CLIENT_OPTION *o;
char *host_for_direct_connection;
UINT port_for_direct_connection;
WPC_CONNECT w;
wchar_t tmp[MAX_SIZE];
SESSION *sess;
volatile bool *cancel_flag = NULL;
@ -6288,6 +6287,7 @@ SOCK *ClientConnectGetSocket(CONNECTION *c, bool additional_connect)
}
Zero(&ret_ip, sizeof(IP));
Zero(&w, sizeof(w));
sess = c->Session;
@ -6323,21 +6323,26 @@ SOCK *ClientConnectGetSocket(CONNECTION *c, bool additional_connect)
c->ServerPort = o->Port;
}
host_for_direct_connection = c->ServerName;
port_for_direct_connection = c->ServerPort;
StrCpy(w.HostName, sizeof(w.HostName), c->ServerName);
w.Port = c->ServerPort;
StrCpy(w.ProxyHostName, sizeof(w.ProxyHostName), o->ProxyName);
w.ProxyPort = o->ProxyPort;
StrCpy(w.ProxyUsername, sizeof(w.ProxyUsername), o->ProxyUsername);
StrCpy(w.ProxyPassword, sizeof(w.ProxyPassword), o->ProxyPassword);
switch (o->ProxyType)
{
case PROXY_DIRECT: // TCP/IP
UniFormat(tmp, sizeof(tmp), _UU("STATUS_4"), c->ServerName);
UniFormat(tmp, sizeof(tmp), _UU("STATUS_4"), w.HostName);
PrintStatus(sess, tmp);
// Production job
if (o->PortUDP == 0)
{
{
// If additional_connect == false, enable trying to NAT-T connection
// If additional_connect == true, follow the IsRUDPSession setting in this session
s = TcpIpConnectEx(host_for_direct_connection, port_for_direct_connection,
s = TcpIpConnectEx(w.HostName, w.Port,
(bool *)cancel_flag, hWnd, &nat_t_err, (additional_connect ? (!is_additional_rudp_session) : false),
true, &ret_ip);
}
@ -6376,16 +6381,12 @@ SOCK *ClientConnectGetSocket(CONNECTION *c, bool additional_connect)
break;
case PROXY_HTTP: // HTTP Proxy
host_for_direct_connection = o->ProxyName;
port_for_direct_connection = o->ProxyPort;
UniFormat(tmp, sizeof(tmp), _UU("STATUS_2"), c->ServerName, o->ProxyName);
UniFormat(tmp, sizeof(tmp), _UU("STATUS_2"), w.HostName, w.ProxyHostName);
PrintStatus(sess, tmp);
// Proxy connection
s = ProxyConnectEx(c, host_for_direct_connection, port_for_direct_connection,
c->ServerName, c->ServerPort, o->ProxyUsername, o->ProxyPassword,
s = ProxyConnectEx(c, w.ProxyHostName, w.ProxyPort,
w.HostName, w.Port, w.ProxyUsername, w.ProxyPassword,
additional_connect, (bool *)cancel_flag, hWnd);
if (s == NULL)
{
@ -6394,19 +6395,27 @@ SOCK *ClientConnectGetSocket(CONNECTION *c, bool additional_connect)
}
break;
case PROXY_SOCKS: // SOCKS Proxy
host_for_direct_connection = o->ProxyName;
port_for_direct_connection = o->ProxyPort;
UniFormat(tmp, sizeof(tmp), _UU("STATUS_2"), c->ServerName, o->ProxyName);
case PROXY_SOCKS: // SOCKS4 Proxy
UniFormat(tmp, sizeof(tmp), _UU("STATUS_2"), w.HostName, w.ProxyHostName);
PrintStatus(sess, tmp);
// SOCKS4 connection
s = SocksConnectEx2(c, w.ProxyHostName, w.ProxyPort,
w.HostName, w.Port, w.ProxyUsername, additional_connect, (bool *)cancel_flag,
hWnd, 0, &ret_ip);
if (s == NULL)
{
// Connection failure
return NULL;
}
break;
// SOCKS connection
s = SocksConnectEx2(c, host_for_direct_connection, port_for_direct_connection,
c->ServerName, c->ServerPort, o->ProxyUsername,
additional_connect, (bool *)cancel_flag, hWnd, 0, &ret_ip);
case PROXY_SOCKS5: // SOCKS5 Proxy
UniFormat(tmp, sizeof(tmp), _UU("STATUS_2"), w.HostName, w.ProxyHostName);
PrintStatus(sess, tmp);
// SOCKS5 connection
s = Socks5Connect(c, &w, additional_connect, (bool *)cancel_flag, hWnd, 0, &ret_ip);
if (s == NULL)
{
// Connection failure
@ -6426,7 +6435,8 @@ SOCK *ClientConnectGetSocket(CONNECTION *c, bool additional_connect)
// Keep a note of the IP address
if (additional_connect == false || IsZeroIP(&s->RemoteIP))
{
if (((s->IsRUDPSocket || s->IPv6) && IsZeroIP(&s->RemoteIP) == false && o->ProxyType == PROXY_DIRECT) || GetIP(&c->Session->ServerIP, host_for_direct_connection) == false)
char *hostname = o->ProxyType == PROXY_DIRECT ? w.HostName : w.ProxyHostName;
if (((s->IsRUDPSocket || s->IPv6) && IsZeroIP(&s->RemoteIP) == false && o->ProxyType == PROXY_DIRECT) || GetIP(&c->Session->ServerIP, hostname) == false)
{
Copy(&c->Session->ServerIP, &s->RemoteIP, sizeof(IP));
}
@ -6449,7 +6459,7 @@ SOCK *ClientConnectGetSocket(CONNECTION *c, bool additional_connect)
return s;
}
// Connect via SOCKS
// Connect via SOCKS4
SOCK *SocksConnect(CONNECTION *c, char *proxy_host_name, UINT proxy_port,
char *server_host_name, UINT server_port,
char *username, bool additional_connect)
@ -6547,7 +6557,7 @@ SOCK *SocksConnectEx2(CONNECTION *c, char *proxy_host_name, UINT proxy_port,
return s;
}
// Receive a SOCKS response packet
// Receive a SOCKS4 response packet
bool SocksRecvResponsePacket(CONNECTION *c, SOCK *s)
{
BUF *b;
@ -6599,7 +6609,7 @@ bool SocksRecvResponsePacket(CONNECTION *c, SOCK *s)
}
}
// Send a SOCKS request packet
// Send a SOCKS4 request packet
bool SocksSendRequestPacket(CONNECTION *c, SOCK *s, UINT dest_port, IP *dest_ip, char *userid)
{
BUF *b;
@ -6639,6 +6649,321 @@ bool SocksSendRequestPacket(CONNECTION *c, SOCK *s, UINT dest_port, IP *dest_ip,
return ret;
}
// Connect via SOCKS5 (RFC1928)
SOCK *Socks5Connect(CONNECTION *c, WPC_CONNECT *w, bool additional_connect, bool *cancel_flag, void *hWnd, UINT timeout, IP *ret_ip)
{
UCHAR tmp, recv_buf[2], *recv_buf_final;
USHORT port;
bool ret;
SOCK *s;
BUF *b;
IP ip;
// Validate arguments
if (c == NULL || w == NULL || w->Port == 0 || w->ProxyPort == 0 || IsEmptyStr(w->HostName) || IsEmptyStr(w->ProxyHostName))
{
if (c != NULL)
{
c->Err = ERR_PROXY_CONNECT_FAILED;
}
return NULL;
}
if (c->Halt)
{
// Stop
c->Err = ERR_USER_CANCEL;
return NULL;
}
// Open TCP connection to the proxy server
s = TcpConnectEx3(w->ProxyHostName, w->ProxyPort, timeout, cancel_flag, hWnd, true, NULL, false, ret_ip);
if (s == NULL)
{
// Failure
c->Err = ERR_PROXY_CONNECT_FAILED;
return NULL;
}
// Set the timeout setting
SetTimeout(s, MIN(CONNECTING_TIMEOUT_PROXY, (timeout == 0 ? INFINITE : timeout)));
if (additional_connect == false)
{
c->FirstSock = s;
}
// +----+----------+----------+
// |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);
FreeBuf(b);
if (ret == false)
{
Debug("Socks5Connect(): [Phase 1] Failed to send initial data to the server.\n");
c->Err = ERR_DISCONNECTED;
goto failure;
}
// +----+--------+
// |VER | METHOD |
// +----+--------+
// | 1 | 1 |
// +----+--------+
if (RecvAll(s, recv_buf, sizeof(recv_buf), false) == false)
{
Debug("Socks5Connect(): [Phase 1] Failed to receive initial data response from the server.\n");
c->Err = ERR_DISCONNECTED;
goto failure;
}
if (recv_buf[0] != 5)
{
Debug("Socks5Connect(): [Phase 1] Unmatching version: %u.\n", recv_buf[0]);
c->Err = ERR_PROXY_ERROR;
goto failure;
}
// Username/password authentication (RFC1929)
if (recv_buf[1] == 2)
{
// +----+------+----------+------+----------+
// |VER | ULEN | UNAME | PLEN | PASSWD |
// +----+------+----------+------+----------+
// | 1 | 1 | 1 to 255 | 1 | 1 to 255 |
// +----+------+----------+------+----------+
b = NewBuf();
tmp = 1;
WriteBuf(b, &tmp, sizeof(tmp)); // Authentication protocol version
tmp = StrLen(w->ProxyUsername);
WriteBuf(b, &tmp, sizeof(tmp)); // Username length
WriteBuf(b, w->ProxyUsername, tmp); // Username
tmp = StrLen(w->ProxyPassword);
WriteBuf(b, &tmp, sizeof(tmp)); // Password length
WriteBuf(b, w->ProxyPassword, tmp); // Password
ret = SendAll(s, b->Buf, b->Size, false);
FreeBuf(b);
if (ret == false)
{
Debug("Socks5Connect(): [Phase 1] Failed to send authentication data to the server.\n");
c->Err = ERR_DISCONNECTED;
goto failure;
}
// +----+--------+
// |VER | STATUS |
// +----+--------+
// | 1 | 1 |
// +----+--------+
if (RecvAll(s, recv_buf, sizeof(recv_buf), false) == false)
{
Debug("Socks5Connect(): [Phase 1] Failed to receive authentication data response from the server.\n");
c->Err = ERR_DISCONNECTED;
goto failure;
}
if (recv_buf[1] != 0)
{
Debug("Socks5Connect(): [Phase 1] Authentication failure error code sent by the server: %u.\n", recv_buf[1]);
c->Err = ERR_PROXY_AUTH_FAILED;
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
b = NewBuf();
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(&ip, w->HostName);
// If the IP structure doesn't contain an IP address, it means that the string is an hostname
if (IsZeroIp(&ip))
{
UCHAR dest_length = StrLen(w->HostName);
tmp = 3;
WriteBuf(b, &tmp, sizeof(tmp)); // Destination type (hostname)
WriteBuf(b, &dest_length, sizeof(dest_length)); // Destination hostname length
WriteBuf(b, w->HostName, dest_length); // Destination hostname
}
else
{
if (IsIP6(&ip))
{
tmp = 4;
WriteBuf(b, &tmp, sizeof(tmp)); // Destination type (IPv6)
WriteBuf(b, ip.ipv6_addr, sizeof(ip.ipv6_addr)); // Destination IPv6 address
}
else
{
tmp = 1;
WriteBuf(b, &tmp, sizeof(tmp)); // Destination type (IPv4)
WriteBuf(b, ip.addr, sizeof(ip.addr)); // Destination IPv4 address
}
}
// Convert the port in network octet order
port = Endian16((USHORT)w->Port);
WriteBuf(b, &port, sizeof(port)); // Destination port
// Send data
ret = SendAll(s, b->Buf, b->Size, false);
FreeBuf(b);
if (ret == false)
{
Debug("Socks5Connect(): [Phase 2] Failed to send data to the server.\n");
c->Err = ERR_DISCONNECTED;
goto failure;
}
// +----+-----+-------+------+----------+----------+
// |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
// +----+-----+-------+------+----------+----------+
// | 1 | 1 | X00 | 1 | Variable | 2 |
// +----+-----+-------+------+----------+----------+
//
// VER protocol version: X05
// REP Reply field:
// X00 succeeded
// X01 general SOCKS server failure
// X02 connection not allowed by ruleset
// X03 Network unreachable
// X04 Host unreachable
// X05 Connection refused
// X06 TTL expired
// X07 Command not supported
// X08 Address type not supported
// X09 to XFF 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("Socks5Connect(): [Phase 2] Failed to receive response from the server.\n");
c->Err = ERR_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); // 4 bytes (IPv4) + 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("Socks5Connect(): [Phase 2] Malformed response received from the server.\n");
c->Err = ERR_DISCONNECTED;
goto failure;
}
if (recv_buf[0] != 5)
{
Debug("Socks5Connect(): [Phase 2] Unmatching version: %u.\n", recv_buf_final[0]);
c->Err = ERR_PROXY_ERROR;
goto failure;
}
if (recv_buf[1] == 0)
{
// Success
SetTimeout(s, INFINITE);
return s;
}
else
{
Debug("Socks5Connect(): [Phase 2] Connection failed with error: %u\n", recv_buf[1]);
c->Err = ERR_PROXY_ERROR;
}
failure:
if (additional_connect == false)
{
c->FirstSock = NULL;
}
Disconnect(s);
ReleaseSock(s);
return NULL;
}
// Connect through a proxy
SOCK *ProxyConnect(CONNECTION *c, char *proxy_host_name, UINT proxy_port,
char *server_host_name, UINT server_port,

View File

@ -283,6 +283,7 @@ SOCK *SocksConnectEx2(CONNECTION *c, char *proxy_host_name, UINT proxy_port,
bool *cancel_flag, void *hWnd, UINT timeout, IP *ret_ip);
bool SocksSendRequestPacket(CONNECTION *c, SOCK *s, UINT dest_port, IP *dest_ip, char *userid);
bool SocksRecvResponsePacket(CONNECTION *c, SOCK *s);
SOCK *Socks5Connect(CONNECTION *c, WPC_CONNECT *w, bool additional_connect, bool *cancel_flag, void *hWnd, UINT timeout, IP *ret_ip);
void CreateNodeInfo(NODE_INFO *info, CONNECTION *c);
UINT SecureSign(SECURE_SIGN *sign, UINT device_id, char *pin);
void ClientUploadNoop(CONNECTION *c);

View File

@ -160,6 +160,7 @@ void SmProxyDlgInit(HWND hWnd, INTERNET_SETTING *t)
Check(hWnd, R_DIRECT_TCP, t->ProxyType == PROXY_DIRECT);
Check(hWnd, R_HTTPS, t->ProxyType == PROXY_HTTP);
Check(hWnd, R_SOCKS, t->ProxyType == PROXY_SOCKS);
Check(hWnd, R_SOCKS5, t->ProxyType == PROXY_SOCKS5);
SmProxyDlgUpdate(hWnd, t);
}
@ -226,6 +227,10 @@ UINT SmProxyDlg(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, void *param)
{
t->ProxyType = PROXY_SOCKS;
}
else if (IsChecked(hWnd, R_SOCKS5))
{
t->ProxyType = PROXY_SOCKS5;
}
else
{
t->ProxyType = PROXY_DIRECT;
@ -19434,6 +19439,7 @@ void SmEditSettingDlgInit(HWND hWnd, SM_EDIT_SETTING *p)
Check(hWnd, R_DIRECT_TCP, s->ClientOption.ProxyType == PROXY_DIRECT);
Check(hWnd, R_HTTPS, s->ClientOption.ProxyType == PROXY_HTTP);
Check(hWnd, R_SOCKS, s->ClientOption.ProxyType == PROXY_SOCKS);
Check(hWnd, R_SOCKS5, s->ClientOption.ProxyType == PROXY_SOCKS5);
// Management mode setting
Check(hWnd, R_SERVER_ADMIN, s->ServerAdminMode);

View File

@ -641,6 +641,7 @@ SOCK *WpcSockConnectEx(WPC_CONNECT *param, UINT *error_code, UINT timeout, bool
break;
case PROXY_SOCKS:
// SOCKS4 connection
sock = SocksConnectEx2(&c, param->ProxyHostName, param->ProxyPort,
param->HostName, param->Port,
param->ProxyUsername, false, cancel, NULL, timeout, NULL);
@ -649,6 +650,14 @@ SOCK *WpcSockConnectEx(WPC_CONNECT *param, UINT *error_code, UINT timeout, bool
err = c.Err;
}
break;
case PROXY_SOCKS5:
// SOCKS5 connection
sock = Socks5Connect(&c, param, false, cancel, NULL, timeout, NULL);
if (sock == NULL)
{
err = c.Err;
}
}
if (error_code != NULL)