mirror of
https://github.com/SoftEtherVPN/SoftEtherVPN.git
synced 2024-12-27 10:39:53 +03:00
Merge PR #1022: Move generic proxy stuff from Cedar to Mayaqua
This commit is contained in:
commit
4acd7ab98e
@ -116,8 +116,6 @@
|
||||
#define MAX_ACCOUNT_NAME_LEN 255 // Maximum account name length
|
||||
#define MAX_USERNAME_LEN 255 // User 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_CLIENT_STR_LEN 255 // Maximum length of client string
|
||||
#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_DEFAULT (30 * 1000) // Default number of seconds to timeout
|
||||
#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 MIN_RETRY_INTERVAL (5 * 1000) // Minimum retry interval
|
||||
#define MAX_RETRY_INTERVAL (300 * 1000) // Maximum retry interval
|
||||
|
@ -64,8 +64,8 @@ struct CLIENT_OPTION
|
||||
UINT ProxyType; // Type of proxy
|
||||
char ProxyName[MAX_HOST_NAME_LEN + 1]; // Proxy server name
|
||||
UINT ProxyPort; // Port number of the proxy server
|
||||
char ProxyUsername[MAX_PROXY_USERNAME_LEN + 1]; // Maximum user name length
|
||||
char ProxyPassword[MAX_PROXY_PASSWORD_LEN + 1]; // Maximum password length
|
||||
char ProxyUsername[PROXY_MAX_USERNAME_LEN + 1]; // Maximum user name length
|
||||
char ProxyPassword[PROXY_MAX_PASSWORD_LEN + 1]; // Maximum password length
|
||||
char CustomHttpHeader[HTTP_CUSTOM_HEADER_MAX_SIZE + 1]; // Custom HTTP proxy header
|
||||
UINT NumRetry; // Automatic retries
|
||||
UINT RetryInterval; // Retry interval
|
||||
|
1007
src/Cedar/Protocol.c
1007
src/Cedar/Protocol.c
File diff suppressed because it is too large
Load Diff
@ -120,11 +120,11 @@ bool ClientUploadAuth(CONNECTION *c);
|
||||
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);
|
||||
|
||||
UINT ProxyCodeToCedar(UINT code);
|
||||
|
||||
void InitProtocol();
|
||||
void FreeProtocol();
|
||||
|
||||
|
||||
|
||||
POLICY *PackGetPolicy(PACK *p);
|
||||
void PackAddPolicy(PACK *p, POLICY *y);
|
||||
PACK *PackWelcome(SESSION *s);
|
||||
@ -151,20 +151,6 @@ SOCK *ClientAdditionalConnectToServer(CONNECTION *c);
|
||||
bool ClientUploadAuth2(CONNECTION *c, SOCK *s);
|
||||
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);
|
||||
UINT SecureSign(SECURE_SIGN *sign, UINT device_id, char *pin);
|
||||
void ClientUploadNoop(CONNECTION *c);
|
||||
|
101
src/Cedar/Wpc.c
101
src/Cedar/Wpc.c
@ -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)
|
||||
{
|
||||
CONNECTION c;
|
||||
SOCK *sock;
|
||||
UINT err = ERR_NO_ERROR;
|
||||
UINT ret;
|
||||
// Validate arguments
|
||||
if (param == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Zero(&c, sizeof(c));
|
||||
|
||||
sock = NULL;
|
||||
err = ERR_INTERNAL_ERROR;
|
||||
|
||||
switch (param->ProxyType)
|
||||
if (error_code == NULL)
|
||||
{
|
||||
error_code = &ret;
|
||||
}
|
||||
|
||||
if (param->ProxyType == PROXY_DIRECT)
|
||||
{
|
||||
case PROXY_DIRECT:
|
||||
sock = TcpConnectEx3(param->HostName, param->Port, timeout, cancel, NULL, true, NULL, false, NULL);
|
||||
if (sock == NULL)
|
||||
{
|
||||
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;
|
||||
}
|
||||
*error_code = (sock != NULL ? ERR_NO_ERROR : ERR_CONNECT_FAILED);
|
||||
return sock;
|
||||
}
|
||||
|
||||
if (error_code != NULL)
|
||||
else
|
||||
{
|
||||
*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)
|
||||
{
|
||||
|
@ -16,15 +16,17 @@
|
||||
#define HTTP_SAITAMA "/saitama.jpg"
|
||||
#define HTTP_PICTURES "/picture"
|
||||
// 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
|
||||
#define HTTP_HEADER_LINE_MAX_SIZE 4096
|
||||
#define HTTP_HEADER_LINE_MAX_SIZE 4096
|
||||
// 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
|
||||
#define HTTP_PACK_RAND_SIZE_MAX 1000
|
||||
#define HTTP_PACK_RAND_SIZE_MAX 1000
|
||||
// Maximum PACK size in the HTTP
|
||||
#define HTTP_PACK_MAX_SIZE 65536
|
||||
#define HTTP_PACK_MAX_SIZE 65536
|
||||
|
||||
// HTTP value
|
||||
struct HTTP_VALUE
|
||||
|
@ -510,5 +510,8 @@ typedef struct IKE_HEADER IKE_HEADER;
|
||||
// HTTP.h
|
||||
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
|
||||
|
@ -277,6 +277,9 @@ int iconv_close (iconv_t __cd);
|
||||
// HTTP
|
||||
#include <Mayaqua/HTTP.h>
|
||||
|
||||
// Proxy
|
||||
#include <Mayaqua/Proxy.h>
|
||||
|
||||
// 64 bit real-time clock
|
||||
#include <Mayaqua/Tick64.h>
|
||||
|
||||
|
@ -366,6 +366,10 @@
|
||||
RelativePath=".\Pack.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Proxy.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Secure.c"
|
||||
>
|
||||
@ -464,6 +468,10 @@
|
||||
RelativePath=".\Pack.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Proxy.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\pkcs11.h"
|
||||
>
|
||||
|
618
src/Mayaqua/Proxy.c
Normal file
618
src/Mayaqua/Proxy.c
Normal 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 | 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;
|
||||
}
|
45
src/Mayaqua/Proxy.h
Normal file
45
src/Mayaqua/Proxy.h
Normal 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
|
Loading…
Reference in New Issue
Block a user