mirror of
https://github.com/SoftEtherVPN/SoftEtherVPN.git
synced 2024-11-10 11:40:40 +03:00
b8f58a2f94
This commit moves the generic (not related to our protocol) proxy stuff from Cedar to Mayaqua, in dedicated files. The functions are refactored so that they all have the same arguments and follow the same logic. Dedicated error codes are added, in order to indicate clearly why the function(s) failed.
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;
|
||
}
|