mirror of
https://github.com/SoftEtherVPN/SoftEtherVPN.git
synced 2024-11-13 05:00:41 +03:00
619 lines
15 KiB
C
619 lines
15 KiB
C
|
#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;
|
|||
|
}
|