1
0
mirror of https://github.com/SoftEtherVPN/SoftEtherVPN.git synced 2024-11-22 17:39:53 +03:00

Merge PR #1022: Move generic proxy stuff from Cedar to Mayaqua

This commit is contained in:
Davide Beatrici 2019-11-01 09:35:18 +01:00 committed by GitHub
commit 4acd7ab98e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 883 additions and 941 deletions

View File

@ -116,8 +116,6 @@
#define MAX_ACCOUNT_NAME_LEN 255 // Maximum account name length #define MAX_ACCOUNT_NAME_LEN 255 // Maximum account name length
#define MAX_USERNAME_LEN 255 // User name maximum length #define MAX_USERNAME_LEN 255 // User name maximum length
#define MAX_PASSWORD_LEN 255 // Password name maximum length #define MAX_PASSWORD_LEN 255 // Password name maximum length
#define MAX_PROXY_USERNAME_LEN 255 // Proxy user name maximum length
#define MAX_PROXY_PASSWORD_LEN 255 // Proxy Password maximum length
#define MAX_SERVER_STR_LEN 255 // Maximum length of server string #define MAX_SERVER_STR_LEN 255 // Maximum length of server string
#define MAX_CLIENT_STR_LEN 255 // Maximum length of client string #define MAX_CLIENT_STR_LEN 255 // Maximum length of client string
#define MAX_HUBNAME_LEN 255 // Maximum length of HUB name #define MAX_HUBNAME_LEN 255 // Maximum length of HUB name
@ -190,7 +188,6 @@
#define TIMEOUT_MAX (60 * 1000) // Maximum timeout in seconds #define TIMEOUT_MAX (60 * 1000) // Maximum timeout in seconds
#define TIMEOUT_DEFAULT (30 * 1000) // Default number of seconds to timeout #define TIMEOUT_DEFAULT (30 * 1000) // Default number of seconds to timeout
#define CONNECTING_TIMEOUT (15 * 1000) // Timeout in seconds of being connected #define CONNECTING_TIMEOUT (15 * 1000) // Timeout in seconds of being connected
#define CONNECTING_TIMEOUT_PROXY (4 * 1000) // Timeout in seconds of being connected (Proxy)
#define CONNECTING_POOLING_SPAN (3 * 1000) // Polling interval of connected #define CONNECTING_POOLING_SPAN (3 * 1000) // Polling interval of connected
#define MIN_RETRY_INTERVAL (5 * 1000) // Minimum retry interval #define MIN_RETRY_INTERVAL (5 * 1000) // Minimum retry interval
#define MAX_RETRY_INTERVAL (300 * 1000) // Maximum retry interval #define MAX_RETRY_INTERVAL (300 * 1000) // Maximum retry interval

View File

@ -64,8 +64,8 @@ struct CLIENT_OPTION
UINT ProxyType; // Type of proxy UINT ProxyType; // Type of proxy
char ProxyName[MAX_HOST_NAME_LEN + 1]; // Proxy server name char ProxyName[MAX_HOST_NAME_LEN + 1]; // Proxy server name
UINT ProxyPort; // Port number of the proxy server UINT ProxyPort; // Port number of the proxy server
char ProxyUsername[MAX_PROXY_USERNAME_LEN + 1]; // Maximum user name length char ProxyUsername[PROXY_MAX_USERNAME_LEN + 1]; // Maximum user name length
char ProxyPassword[MAX_PROXY_PASSWORD_LEN + 1]; // Maximum password length char ProxyPassword[PROXY_MAX_PASSWORD_LEN + 1]; // Maximum password length
char CustomHttpHeader[HTTP_CUSTOM_HEADER_MAX_SIZE + 1]; // Custom HTTP proxy header char CustomHttpHeader[HTTP_CUSTOM_HEADER_MAX_SIZE + 1]; // Custom HTTP proxy header
UINT NumRetry; // Automatic retries UINT NumRetry; // Automatic retries
UINT RetryInterval; // Retry interval UINT RetryInterval; // Retry interval

File diff suppressed because it is too large Load Diff

View File

@ -120,11 +120,11 @@ bool ClientUploadAuth(CONNECTION *c);
SOCK *ClientConnectGetSocket(CONNECTION *c, bool additional_connect); SOCK *ClientConnectGetSocket(CONNECTION *c, bool additional_connect);
SOCK *TcpConnectEx3(char *hostname, UINT port, UINT timeout, bool *cancel_flag, void *hWnd, bool no_nat_t, UINT *nat_t_error_code, bool try_start_ssl, IP *ret_ip); SOCK *TcpConnectEx3(char *hostname, UINT port, UINT timeout, bool *cancel_flag, void *hWnd, bool no_nat_t, UINT *nat_t_error_code, bool try_start_ssl, IP *ret_ip);
UINT ProxyCodeToCedar(UINT code);
void InitProtocol(); void InitProtocol();
void FreeProtocol(); void FreeProtocol();
POLICY *PackGetPolicy(PACK *p); POLICY *PackGetPolicy(PACK *p);
void PackAddPolicy(PACK *p, POLICY *y); void PackAddPolicy(PACK *p, POLICY *y);
PACK *PackWelcome(SESSION *s); PACK *PackWelcome(SESSION *s);
@ -151,20 +151,6 @@ SOCK *ClientAdditionalConnectToServer(CONNECTION *c);
bool ClientUploadAuth2(CONNECTION *c, SOCK *s); bool ClientUploadAuth2(CONNECTION *c, SOCK *s);
bool GetSessionKeyFromPack(PACK *p, UCHAR *session_key, UINT *session_key_32); bool GetSessionKeyFromPack(PACK *p, UCHAR *session_key, UINT *session_key_32);
SOCK *ProxyConnectEx2(CONNECTION *c, char *proxy_host_name, UINT proxy_port,
char *server_host_name, UINT server_port,
char *username, char *password, bool additional_connect,
bool *cancel_flag, void *hWnd, UINT timeout);
SOCK *ProxyConnectEx3(CONNECTION *c, WPC_CONNECT *wpc_connect,
bool additional_connect, bool *cancel_flag, void *hWnd,
UINT timeout);
SOCK *SocksConnectEx2(CONNECTION *c, char *proxy_host_name, UINT proxy_port,
char *server_host_name, UINT server_port,
char *username, bool additional_connect,
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); void CreateNodeInfo(NODE_INFO *info, CONNECTION *c);
UINT SecureSign(SECURE_SIGN *sign, UINT device_id, char *pin); UINT SecureSign(SECURE_SIGN *sign, UINT device_id, char *pin);
void ClientUploadNoop(CONNECTION *c); void ClientUploadNoop(CONNECTION *c);

View File

@ -500,64 +500,73 @@ SOCK *WpcSockConnect(WPC_CONNECT *param, UINT *error_code, UINT timeout)
} }
SOCK *WpcSockConnectEx(WPC_CONNECT *param, UINT *error_code, UINT timeout, bool *cancel) SOCK *WpcSockConnectEx(WPC_CONNECT *param, UINT *error_code, UINT timeout, bool *cancel)
{ {
CONNECTION c;
SOCK *sock; SOCK *sock;
UINT err = ERR_NO_ERROR; UINT ret;
// Validate arguments // Validate arguments
if (param == NULL) if (param == NULL)
{ {
return NULL; return NULL;
} }
Zero(&c, sizeof(c)); if (error_code == NULL)
{
sock = NULL; error_code = &ret;
err = ERR_INTERNAL_ERROR; }
switch (param->ProxyType) if (param->ProxyType == PROXY_DIRECT)
{ {
case PROXY_DIRECT:
sock = TcpConnectEx3(param->HostName, param->Port, timeout, cancel, NULL, true, NULL, false, NULL); sock = TcpConnectEx3(param->HostName, param->Port, timeout, cancel, NULL, true, NULL, false, NULL);
if (sock == NULL) *error_code = (sock != NULL ? ERR_NO_ERROR : ERR_CONNECT_FAILED);
{ return sock;
err = ERR_CONNECT_FAILED;
}
break;
case PROXY_HTTP:
sock = ProxyConnectEx3(&c, param, false, cancel, NULL, timeout);
if (sock == NULL)
{
err = c.Err;
}
break;
case PROXY_SOCKS:
// SOCKS4 connection
sock = SocksConnectEx2(&c, param->ProxyHostName, param->ProxyPort,
param->HostName, param->Port,
param->ProxyUsername, false, cancel, NULL, timeout, NULL);
if (sock == NULL)
{
err = c.Err;
}
break;
case PROXY_SOCKS5:
// SOCKS5 connection
sock = Socks5Connect(&c, param, false, cancel, NULL, timeout, NULL);
if (sock == NULL)
{
err = c.Err;
}
} }
else
if (error_code != NULL)
{ {
*error_code = err; PROXY_PARAM_OUT out;
} PROXY_PARAM_IN in;
UINT ret;
return sock; Zero(&in, sizeof(in));
in.Timeout = timeout;
StrCpy(in.TargetHostname, sizeof(in.TargetHostname), param->HostName);
in.TargetPort = param->Port;
StrCpy(in.Hostname, sizeof(in.Hostname), param->ProxyHostName);
in.Port = param->ProxyPort;
StrCpy(in.Username, sizeof(in.Username), param->ProxyUsername);
StrCpy(in.Password, sizeof(in.Password), param->ProxyPassword);
StrCpy(in.HttpCustomHeader, sizeof(in.HttpCustomHeader), param->CustomHttpHeader);
switch (param->ProxyType)
{
case PROXY_HTTP:
ret = ProxyHttpConnect(&out, &in, cancel);
break;
case PROXY_SOCKS:
ret = ProxySocks4Connect(&out, &in, cancel);
break;
case PROXY_SOCKS5:
ret = ProxySocks5Connect(&out, &in, cancel);
break;
default:
*error_code = ERR_INTERNAL_ERROR;
Debug("WpcSockConnect(): Unknown proxy type: %u!\n", param->ProxyType);
return NULL;
}
*error_code = ProxyCodeToCedar(ret);
if (*error_code != ERR_NO_ERROR)
{
Debug("ClientConnectGetSocket(): Connection via proxy server failed with error %u\n", ret);
return NULL;
}
return out.Sock;
}
} }
SOCK *WpcSockConnect2(char *hostname, UINT port, INTERNET_SETTING *t, UINT *error_code, UINT timeout) SOCK *WpcSockConnect2(char *hostname, UINT port, INTERNET_SETTING *t, UINT *error_code, UINT timeout)
{ {

View File

@ -16,15 +16,17 @@
#define HTTP_SAITAMA "/saitama.jpg" #define HTTP_SAITAMA "/saitama.jpg"
#define HTTP_PICTURES "/picture" #define HTTP_PICTURES "/picture"
// Maximum size of the custom HTTP header // Maximum size of the custom HTTP header
#define HTTP_CUSTOM_HEADER_MAX_SIZE 1024 #define HTTP_CUSTOM_HEADER_MAX_SIZE 1024
// Maximum size of a single line in the HTTP header // Maximum size of a single line in the HTTP header
#define HTTP_HEADER_LINE_MAX_SIZE 4096 #define HTTP_HEADER_LINE_MAX_SIZE 4096
// Maximum number of lines in the HTTP header // Maximum number of lines in the HTTP header
#define HTTP_HEADER_MAX_LINES 128 #define HTTP_HEADER_MAX_LINES 128
// Maximum size of the user agent string
#define HTTP_HEADER_USER_AGENT_MAX_SIZE 512
// Maximum size of the random number to be included in the PACK // Maximum size of the random number to be included in the PACK
#define HTTP_PACK_RAND_SIZE_MAX 1000 #define HTTP_PACK_RAND_SIZE_MAX 1000
// Maximum PACK size in the HTTP // Maximum PACK size in the HTTP
#define HTTP_PACK_MAX_SIZE 65536 #define HTTP_PACK_MAX_SIZE 65536
// HTTP value // HTTP value
struct HTTP_VALUE struct HTTP_VALUE

View File

@ -510,5 +510,8 @@ typedef struct IKE_HEADER IKE_HEADER;
// HTTP.h // HTTP.h
typedef struct HTTP_MIME_TYPE HTTP_MIME_TYPE; typedef struct HTTP_MIME_TYPE HTTP_MIME_TYPE;
#endif // MAYATYPE_H // Proxy.h
typedef struct PROXY_PARAM_IN PROXY_PARAM_IN;
typedef struct PROXY_PARAM_OUT PROXY_PARAM_OUT;
#endif // MAYATYPE_H

View File

@ -277,6 +277,9 @@ int iconv_close (iconv_t __cd);
// HTTP // HTTP
#include <Mayaqua/HTTP.h> #include <Mayaqua/HTTP.h>
// Proxy
#include <Mayaqua/Proxy.h>
// 64 bit real-time clock // 64 bit real-time clock
#include <Mayaqua/Tick64.h> #include <Mayaqua/Tick64.h>

View File

@ -366,6 +366,10 @@
RelativePath=".\Pack.c" RelativePath=".\Pack.c"
> >
</File> </File>
<File
RelativePath=".\Proxy.c"
>
</File>
<File <File
RelativePath=".\Secure.c" RelativePath=".\Secure.c"
> >
@ -464,6 +468,10 @@
RelativePath=".\Pack.h" RelativePath=".\Pack.h"
> >
</File> </File>
<File
RelativePath=".\Proxy.h"
>
</File>
<File <File
RelativePath=".\pkcs11.h" RelativePath=".\pkcs11.h"
> >

618
src/Mayaqua/Proxy.c Normal file
View File

@ -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 | 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("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;
}

45
src/Mayaqua/Proxy.h Normal file
View File

@ -0,0 +1,45 @@
#ifndef PROXY_H
#define PROXY_H
#define PROXY_CONNECTION_TIMEOUT (4 * 1000)
#define PROXY_MAX_USERNAME_LEN 255
#define PROXY_MAX_PASSWORD_LEN 255
#define PROXY_ERROR_SUCCESS 0
#define PROXY_ERROR_GENERIC 1
#define PROXY_ERROR_PARAMETER 2
#define PROXY_ERROR_CANCELED 3
#define PROXY_ERROR_CONNECTION 4
#define PROXY_ERROR_DISCONNECTED 5
#define PROXY_ERROR_VERSION 6
#define PROXY_ERROR_AUTHENTICATION 7
#define PROXY_ERROR_TARGET 8
struct PROXY_PARAM_IN
{
char Hostname[MAX_HOST_NAME_LEN + 1];
USHORT Port;
char TargetHostname[MAX_HOST_NAME_LEN + 1];
USHORT TargetPort;
char Username[PROXY_MAX_USERNAME_LEN + 1];
char Password[PROXY_MAX_PASSWORD_LEN + 1];
UINT Timeout;
char HttpCustomHeader[HTTP_CUSTOM_HEADER_MAX_SIZE + 1];
char HttpUserAgent[HTTP_HEADER_USER_AGENT_MAX_SIZE + 1];
#ifdef OS_WIN32
void *Hwnd;
#endif
};
struct PROXY_PARAM_OUT
{
SOCK *Sock;
IP ResolvedIp;
};
UINT ProxyHttpConnect(PROXY_PARAM_OUT *out, PROXY_PARAM_IN *in, volatile bool *cancel_flag);
UINT ProxySocks5Connect(PROXY_PARAM_OUT *out, PROXY_PARAM_IN *in, volatile bool *cancel_flag);
UINT ProxySocks4Connect(PROXY_PARAM_OUT *out, PROXY_PARAM_IN *in, volatile bool *cancel_flag);
#endif