mirror of
https://github.com/SoftEtherVPN/SoftEtherVPN.git
synced 2024-11-14 05:30:41 +03:00
1324 lines
28 KiB
C
1324 lines
28 KiB
C
// SoftEther VPN Source Code - Developer Edition Master Branch
|
|
// Cedar Communication Module
|
|
|
|
|
|
// Wpc.c
|
|
// RPC over HTTP
|
|
|
|
#include <GlobalConst.h>
|
|
|
|
#include "CedarPch.h"
|
|
|
|
// Get whether the proxy server is specified by a private IP
|
|
bool IsProxyPrivateIp(INTERNET_SETTING *s)
|
|
{
|
|
IP ip;
|
|
// Validate arguments
|
|
if (s == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (s->ProxyType == PROXY_DIRECT)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (GetIP(&ip, s->ProxyHostName) == false)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (IsIPPrivate(&ip))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (IsIPMyHost(&ip))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (IsLocalHostIP(&ip))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Call
|
|
PACK *WpcCall(char *url, INTERNET_SETTING *setting, UINT timeout_connect, UINT timeout_comm,
|
|
char *function_name, PACK *pack, X *cert, K *key, void *sha1_cert_hash)
|
|
{
|
|
return WpcCallEx(url, setting, timeout_connect, timeout_comm, function_name, pack, cert, key,
|
|
sha1_cert_hash, NULL, 0, NULL, NULL);
|
|
}
|
|
PACK *WpcCallEx(char *url, INTERNET_SETTING *setting, UINT timeout_connect, UINT timeout_comm,
|
|
char *function_name, PACK *pack, X *cert, K *key, void *sha1_cert_hash, bool *cancel, UINT max_recv_size,
|
|
char *additional_header_name, char *additional_header_value)
|
|
{
|
|
return WpcCallEx2(url, setting, timeout_connect, timeout_comm, function_name, pack,
|
|
cert, key, sha1_cert_hash, (sha1_cert_hash == NULL ? 0 : 1),
|
|
cancel, max_recv_size, additional_header_name, additional_header_value, NULL);
|
|
}
|
|
PACK *WpcCallEx2(char *url, INTERNET_SETTING *setting, UINT timeout_connect, UINT timeout_comm,
|
|
char *function_name, PACK *pack, X *cert, K *key, void *sha1_cert_hash, UINT num_hashes, bool *cancel, UINT max_recv_size,
|
|
char *additional_header_name, char *additional_header_value, char *sni_string)
|
|
{
|
|
URL_DATA data;
|
|
BUF *b, *recv;
|
|
UINT error;
|
|
WPC_PACKET packet;
|
|
// Validate arguments
|
|
if (function_name == NULL || pack == NULL)
|
|
{
|
|
return PackError(ERR_INTERNAL_ERROR);
|
|
}
|
|
|
|
if (ParseUrl(&data, url, true, NULL) == false)
|
|
{
|
|
return PackError(ERR_INTERNAL_ERROR);
|
|
}
|
|
|
|
PackAddStr(pack, "function", function_name);
|
|
|
|
b = WpcGeneratePacket(pack, cert, key);
|
|
if (b == NULL)
|
|
{
|
|
return PackError(ERR_INTERNAL_ERROR);
|
|
}
|
|
|
|
SeekBuf(b, b->Size, 0);
|
|
WriteBufInt(b, 0);
|
|
SeekBuf(b, 0, 0);
|
|
|
|
if (IsEmptyStr(additional_header_name) == false && IsEmptyStr(additional_header_value) == false)
|
|
{
|
|
StrCpy(data.AdditionalHeaderName, sizeof(data.AdditionalHeaderName), additional_header_name);
|
|
StrCpy(data.AdditionalHeaderValue, sizeof(data.AdditionalHeaderValue), additional_header_value);
|
|
}
|
|
|
|
if (sni_string != NULL && IsEmptyStr(sni_string) == false)
|
|
{
|
|
StrCpy(data.SniString, sizeof(data.SniString), sni_string);
|
|
}
|
|
|
|
recv = HttpRequestEx3(&data, setting, timeout_connect, timeout_comm, &error,
|
|
false, b->Buf, NULL, NULL, sha1_cert_hash, num_hashes, cancel, max_recv_size,
|
|
NULL, NULL);
|
|
|
|
FreeBuf(b);
|
|
|
|
if (recv == NULL)
|
|
{
|
|
return PackError(error);
|
|
}
|
|
|
|
if (WpcParsePacket(&packet, recv) == false)
|
|
{
|
|
FreeBuf(recv);
|
|
return PackError(ERR_PROTOCOL_ERROR);
|
|
}
|
|
|
|
FreeBuf(recv);
|
|
|
|
FreeX(packet.Cert);
|
|
|
|
return packet.Pack;
|
|
}
|
|
|
|
// Release the packet
|
|
void WpcFreePacket(WPC_PACKET *packet)
|
|
{
|
|
// Validate arguments
|
|
if (packet == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
FreePack(packet->Pack);
|
|
FreeX(packet->Cert);
|
|
}
|
|
|
|
// Parse the packet
|
|
bool WpcParsePacket(WPC_PACKET *packet, BUF *buf)
|
|
{
|
|
LIST *o;
|
|
BUF *b;
|
|
bool ret = false;
|
|
UCHAR hash[SHA1_SIZE];
|
|
// Validate arguments
|
|
if (packet == NULL || buf == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Zero(packet, sizeof(WPC_PACKET));
|
|
|
|
o = WpcParseDataEntry(buf);
|
|
|
|
b = WpcDataEntryToBuf(WpcFindDataEntry(o, "PACK"));
|
|
if (b != NULL)
|
|
{
|
|
Sha1(hash, b->Buf, b->Size);
|
|
|
|
packet->Pack = BufToPack(b);
|
|
FreeBuf(b);
|
|
|
|
if (packet->Pack != NULL)
|
|
{
|
|
BUF *b;
|
|
|
|
ret = true;
|
|
|
|
b = WpcDataEntryToBuf(WpcFindDataEntry(o, "HASH"));
|
|
|
|
if (b != NULL)
|
|
{
|
|
if (b->Size != SHA1_SIZE || Cmp(b->Buf, hash, SHA1_SIZE) != 0)
|
|
{
|
|
ret = false;
|
|
FreePack(packet->Pack);
|
|
}
|
|
else
|
|
{
|
|
BUF *b;
|
|
|
|
Copy(packet->Hash, hash, SHA1_SIZE);
|
|
|
|
b = WpcDataEntryToBuf(WpcFindDataEntry(o, "CERT"));
|
|
|
|
if (b != NULL)
|
|
{
|
|
X *cert = BufToX(b, false);
|
|
if (cert == NULL)
|
|
{
|
|
ret = false;
|
|
FreePack(packet->Pack);
|
|
}
|
|
else
|
|
{
|
|
BUF *b = WpcDataEntryToBuf(WpcFindDataEntry(o, "SIGN"));
|
|
|
|
if (b == NULL || (b->Size != 128))
|
|
{
|
|
ret = false;
|
|
FreeX(cert);
|
|
FreePack(packet->Pack);
|
|
}
|
|
else
|
|
{
|
|
K *k = GetKFromX(cert);
|
|
|
|
if (RsaVerify(hash, SHA1_SIZE, b->Buf, k) == false)
|
|
{
|
|
ret = false;
|
|
FreeX(cert);
|
|
FreePack(packet->Pack);
|
|
}
|
|
else
|
|
{
|
|
packet->Cert = cert;
|
|
Copy(packet->Sign, b->Buf, 128);
|
|
}
|
|
|
|
FreeK(k);
|
|
}
|
|
|
|
FreeBuf(b);
|
|
}
|
|
FreeBuf(b);
|
|
}
|
|
}
|
|
FreeBuf(b);
|
|
}
|
|
}
|
|
}
|
|
|
|
WpcFreeDataEntryList(o);
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Generate the packet
|
|
BUF *WpcGeneratePacket(PACK *pack, X *cert, K *key)
|
|
{
|
|
UCHAR hash[SHA1_SIZE];
|
|
BUF *pack_data;
|
|
BUF *cert_data = NULL;
|
|
BUF *sign_data = NULL;
|
|
BUF *b;
|
|
// Validate arguments
|
|
if (pack == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
pack_data = PackToBuf(pack);
|
|
Sha1(hash, pack_data->Buf, pack_data->Size);
|
|
|
|
if (cert != NULL && key != NULL)
|
|
{
|
|
UCHAR sign[128];
|
|
cert_data = XToBuf(cert, false);
|
|
|
|
RsaSign(sign, hash, sizeof(hash), key);
|
|
|
|
sign_data = NewBuf();
|
|
WriteBuf(sign_data, sign, sizeof(sign));
|
|
SeekBuf(sign_data, 0, 0);
|
|
}
|
|
|
|
b = NewBuf();
|
|
|
|
WpcAddDataEntryBin(b, "PACK", pack_data->Buf, pack_data->Size);
|
|
WpcAddDataEntryBin(b, "HASH", hash, sizeof(hash));
|
|
|
|
if (cert_data != NULL)
|
|
{
|
|
WpcAddDataEntryBin(b, "CERT", cert_data->Buf, cert_data->Size);
|
|
WpcAddDataEntryBin(b, "SIGN", sign_data->Buf, sign_data->Size);
|
|
}
|
|
|
|
FreeBuf(pack_data);
|
|
FreeBuf(cert_data);
|
|
FreeBuf(sign_data);
|
|
|
|
SeekBuf(b, 0, 0);
|
|
|
|
return b;
|
|
}
|
|
|
|
// Decode the buffer from WPC_ENTRY
|
|
BUF *WpcDataEntryToBuf(WPC_ENTRY *e)
|
|
{
|
|
void *data;
|
|
UINT data_size;
|
|
UINT size;
|
|
BUF *b;
|
|
// Validate arguments
|
|
if (e == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
data_size = e->Size + 4096;
|
|
data = Malloc(data_size);
|
|
size = DecodeSafe64(data, e->Data, e->Size);
|
|
|
|
b = NewBuf();
|
|
WriteBuf(b, data, size);
|
|
SeekBuf(b, 0, 0);
|
|
|
|
Free(data);
|
|
|
|
return b;
|
|
}
|
|
|
|
// Search for the data entry
|
|
WPC_ENTRY *WpcFindDataEntry(LIST *o, char *name)
|
|
{
|
|
UINT i;
|
|
char name_str[WPC_DATA_ENTRY_SIZE];
|
|
// Validate arguments
|
|
if (o == NULL || name == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
WpcFillEntryName(name_str, name);
|
|
|
|
for (i = 0;i < LIST_NUM(o);i++)
|
|
{
|
|
WPC_ENTRY *e = LIST_DATA(o, i);
|
|
|
|
if (Cmp(e->EntryName, name_str, WPC_DATA_ENTRY_SIZE) == 0)
|
|
{
|
|
return e;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// Release the data entry list
|
|
void WpcFreeDataEntryList(LIST *o)
|
|
{
|
|
UINT i;
|
|
// Validate arguments
|
|
if (o == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (i = 0;i < LIST_NUM(o);i++)
|
|
{
|
|
WPC_ENTRY *e = LIST_DATA(o, i);
|
|
|
|
Free(e);
|
|
}
|
|
|
|
ReleaseList(o);
|
|
}
|
|
|
|
// Parse the data entry
|
|
LIST *WpcParseDataEntry(BUF *b)
|
|
{
|
|
char entry_name[WPC_DATA_ENTRY_SIZE];
|
|
char size_str[11];
|
|
LIST *o;
|
|
// Validate arguments
|
|
if (b == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
SeekBuf(b, 0, 0);
|
|
|
|
o = NewListFast(NULL);
|
|
|
|
while (true)
|
|
{
|
|
UINT size;
|
|
WPC_ENTRY *e;
|
|
|
|
if (ReadBuf(b, entry_name, WPC_DATA_ENTRY_SIZE) != WPC_DATA_ENTRY_SIZE)
|
|
{
|
|
break;
|
|
}
|
|
|
|
Zero(size_str, sizeof(size_str));
|
|
if (ReadBuf(b, size_str, 10) != 10)
|
|
{
|
|
break;
|
|
}
|
|
|
|
size = ToInt(size_str);
|
|
if ((b->Size - b->Current) < size)
|
|
{
|
|
break;
|
|
}
|
|
|
|
e = ZeroMalloc(sizeof(WPC_ENTRY));
|
|
e->Data = (UCHAR *)b->Buf + b->Current;
|
|
Copy(e->EntryName, entry_name, WPC_DATA_ENTRY_SIZE);
|
|
e->Size = size;
|
|
|
|
SeekBuf(b, size, 1);
|
|
|
|
Add(o, e);
|
|
}
|
|
|
|
return o;
|
|
}
|
|
|
|
// Generate a entry name
|
|
void WpcFillEntryName(char *dst, char *name)
|
|
{
|
|
UINT i, len;
|
|
char tmp[MAX_SIZE];
|
|
// Validate arguments
|
|
if (dst == NULL || name == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
StrCpy(tmp, sizeof(tmp), name);
|
|
StrUpper(tmp);
|
|
len = StrLen(tmp);
|
|
|
|
for (i = 0;i < WPC_DATA_ENTRY_SIZE;i++)
|
|
{
|
|
dst[i] = ' ';
|
|
}
|
|
|
|
if (len <= WPC_DATA_ENTRY_SIZE)
|
|
{
|
|
Copy(dst, tmp, len);
|
|
}
|
|
else
|
|
{
|
|
Copy(dst, tmp, WPC_DATA_ENTRY_SIZE);
|
|
}
|
|
}
|
|
|
|
// Add the data entry (binary)
|
|
void WpcAddDataEntryBin(BUF *b, char *name, void *data, UINT size)
|
|
{
|
|
char *str;
|
|
// Validate arguments
|
|
if (b == NULL || name == NULL || (data == NULL && size != 0))
|
|
{
|
|
return;
|
|
}
|
|
|
|
str = Malloc(size * 2 + 64);
|
|
|
|
EncodeSafe64(str, data, size);
|
|
|
|
WpcAddDataEntry(b, name, str, StrLen(str));
|
|
|
|
Free(str);
|
|
}
|
|
|
|
|
|
// Add the data entry
|
|
void WpcAddDataEntry(BUF *b, char *name, void *data, UINT size)
|
|
{
|
|
char entry_name[WPC_DATA_ENTRY_SIZE];
|
|
char size_str[11];
|
|
// Validate arguments
|
|
if (b == NULL || name == NULL || (data == NULL && size != 0))
|
|
{
|
|
return;
|
|
}
|
|
|
|
WpcFillEntryName(entry_name, name);
|
|
WriteBuf(b, entry_name, WPC_DATA_ENTRY_SIZE);
|
|
|
|
Format(size_str, sizeof(size_str), "%010u", size);
|
|
WriteBuf(b, size_str, 10);
|
|
|
|
WriteBuf(b, data, size);
|
|
}
|
|
|
|
// Get the empty INTERNET_SETTING
|
|
INTERNET_SETTING *GetNullInternetSetting()
|
|
{
|
|
static INTERNET_SETTING ret;
|
|
|
|
Zero(&ret, sizeof(ret));
|
|
|
|
return &ret;
|
|
}
|
|
|
|
// Socket connection
|
|
SOCK *WpcSockConnect(WPC_CONNECT *param, UINT *error_code, UINT timeout)
|
|
{
|
|
return WpcSockConnectEx(param, error_code, timeout, NULL);
|
|
}
|
|
SOCK *WpcSockConnectEx(WPC_CONNECT *param, UINT *error_code, UINT timeout, bool *cancel)
|
|
{
|
|
SOCK *sock;
|
|
UINT ret;
|
|
// Validate arguments
|
|
if (param == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (error_code == NULL)
|
|
{
|
|
error_code = &ret;
|
|
}
|
|
|
|
if (param->ProxyType == PROXY_DIRECT)
|
|
{
|
|
sock = TcpConnectEx3(param->HostName, param->Port, timeout, cancel, NULL, true, NULL, false, NULL);
|
|
*error_code = (sock != NULL ? ERR_NO_ERROR : ERR_CONNECT_FAILED);
|
|
return sock;
|
|
}
|
|
else
|
|
{
|
|
PROXY_PARAM_OUT out;
|
|
PROXY_PARAM_IN in;
|
|
UINT ret;
|
|
|
|
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)
|
|
{
|
|
// Validate arguments
|
|
INTERNET_SETTING t2;
|
|
WPC_CONNECT c;
|
|
if (t == NULL)
|
|
{
|
|
Zero(&t2, sizeof(t2));
|
|
|
|
t = &t2;
|
|
}
|
|
|
|
Zero(&c, sizeof(c));
|
|
StrCpy(c.HostName, sizeof(c.HostName), hostname);
|
|
c.Port = port;
|
|
c.ProxyType = t->ProxyType;
|
|
StrCpy(c.ProxyHostName, sizeof(c.HostName), t->ProxyHostName);
|
|
c.ProxyPort = t->ProxyPort;
|
|
StrCpy(c.ProxyUsername, sizeof(c.ProxyUsername), t->ProxyUsername);
|
|
StrCpy(c.ProxyPassword, sizeof(c.ProxyPassword), t->ProxyPassword);
|
|
StrCpy(c.CustomHttpHeader, sizeof(c.CustomHttpHeader), t->CustomHttpHeader);
|
|
|
|
return WpcSockConnect(&c, error_code, timeout);
|
|
}
|
|
|
|
// Handle the HTTP request
|
|
BUF *HttpRequest(URL_DATA *data, INTERNET_SETTING *setting,
|
|
UINT timeout_connect, UINT timeout_comm,
|
|
UINT *error_code, bool check_ssl_trust, char *post_data,
|
|
WPC_RECV_CALLBACK *recv_callback, void *recv_callback_param, void *sha1_cert_hash)
|
|
{
|
|
return HttpRequestEx(data, setting, timeout_connect, timeout_comm,
|
|
error_code, check_ssl_trust, post_data,
|
|
recv_callback, recv_callback_param, sha1_cert_hash, NULL, 0);
|
|
}
|
|
BUF *HttpRequestEx(URL_DATA *data, INTERNET_SETTING *setting,
|
|
UINT timeout_connect, UINT timeout_comm,
|
|
UINT *error_code, bool check_ssl_trust, char *post_data,
|
|
WPC_RECV_CALLBACK *recv_callback, void *recv_callback_param, void *sha1_cert_hash,
|
|
bool *cancel, UINT max_recv_size)
|
|
{
|
|
return HttpRequestEx2(data, setting, timeout_connect, timeout_comm, error_code,
|
|
check_ssl_trust, post_data, recv_callback, recv_callback_param, sha1_cert_hash,
|
|
cancel, max_recv_size, NULL, NULL);
|
|
}
|
|
BUF *HttpRequestEx2(URL_DATA *data, INTERNET_SETTING *setting,
|
|
UINT timeout_connect, UINT timeout_comm,
|
|
UINT *error_code, bool check_ssl_trust, char *post_data,
|
|
WPC_RECV_CALLBACK *recv_callback, void *recv_callback_param, void *sha1_cert_hash,
|
|
bool *cancel, UINT max_recv_size, char *header_name, char *header_value)
|
|
{
|
|
return HttpRequestEx3(data, setting, timeout_connect, timeout_comm, error_code, check_ssl_trust,
|
|
post_data, recv_callback, recv_callback_param, sha1_cert_hash, (sha1_cert_hash == NULL ? 0 : 1),
|
|
cancel, max_recv_size, header_name, header_value);
|
|
}
|
|
BUF *HttpRequestEx3(URL_DATA *data, INTERNET_SETTING *setting,
|
|
UINT timeout_connect, UINT timeout_comm,
|
|
UINT *error_code, bool check_ssl_trust, char *post_data,
|
|
WPC_RECV_CALLBACK *recv_callback, void *recv_callback_param, void *sha1_cert_hash, UINT num_hashes,
|
|
bool *cancel, UINT max_recv_size, char *header_name, char *header_value)
|
|
{
|
|
WPC_CONNECT con;
|
|
SOCK *s;
|
|
HTTP_HEADER *h;
|
|
bool use_http_proxy = false;
|
|
char target[MAX_SIZE * 4];
|
|
char *send_str;
|
|
BUF *send_buf;
|
|
BUF *recv_buf;
|
|
UINT http_error_code;
|
|
char len_str[100];
|
|
UINT content_len;
|
|
void *socket_buffer;
|
|
UINT socket_buffer_size = WPC_RECV_BUF_SIZE;
|
|
UINT num_continue = 0;
|
|
INTERNET_SETTING wt_setting;
|
|
// Validate arguments
|
|
if (data == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
if (setting == NULL)
|
|
{
|
|
Zero(&wt_setting, sizeof(wt_setting));
|
|
setting = &wt_setting;
|
|
}
|
|
if (error_code == NULL)
|
|
{
|
|
static UINT ret = 0;
|
|
error_code = &ret;
|
|
}
|
|
if (timeout_comm == 0)
|
|
{
|
|
timeout_comm = WPC_TIMEOUT;
|
|
}
|
|
if (sha1_cert_hash == NULL)
|
|
{
|
|
num_hashes = 0;
|
|
}
|
|
if (num_hashes == 0)
|
|
{
|
|
sha1_cert_hash = NULL;
|
|
}
|
|
|
|
// Connection
|
|
Zero(&con, sizeof(con));
|
|
StrCpy(con.HostName, sizeof(con.HostName), data->HostName);
|
|
con.Port = data->Port;
|
|
con.ProxyType = setting->ProxyType;
|
|
StrCpy(con.ProxyHostName, sizeof(con.ProxyHostName), setting->ProxyHostName);
|
|
con.ProxyPort = setting->ProxyPort;
|
|
StrCpy(con.ProxyUsername, sizeof(con.ProxyUsername), setting->ProxyUsername);
|
|
StrCpy(con.ProxyPassword, sizeof(con.ProxyPassword), setting->ProxyPassword);
|
|
StrCpy(con.CustomHttpHeader, sizeof(con.CustomHttpHeader), setting->CustomHttpHeader);
|
|
|
|
if (setting->ProxyType != PROXY_HTTP || data->Secure)
|
|
{
|
|
use_http_proxy = false;
|
|
StrCpy(target, sizeof(target), data->Target);
|
|
}
|
|
else
|
|
{
|
|
use_http_proxy = true;
|
|
CreateUrl(target, sizeof(target), data);
|
|
}
|
|
|
|
if (use_http_proxy == false)
|
|
{
|
|
// If the connection is not via HTTP Proxy, or is a SSL connection even via HTTP Proxy
|
|
s = WpcSockConnectEx(&con, error_code, timeout_connect, cancel);
|
|
}
|
|
else
|
|
{
|
|
// If the connection is not SSL via HTTP Proxy
|
|
s = TcpConnectEx3(con.ProxyHostName, con.ProxyPort, timeout_connect, cancel, NULL, true, NULL, false, NULL);
|
|
if (s == NULL)
|
|
{
|
|
*error_code = ERR_PROXY_CONNECT_FAILED;
|
|
}
|
|
}
|
|
|
|
if (s == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (data->Secure)
|
|
{
|
|
// Start the SSL communication
|
|
if (StartSSLEx(s, NULL, NULL, 0, (IsEmptyStr(data->SniString) ? NULL : data->SniString)) == false)
|
|
{
|
|
// SSL connection failed
|
|
*error_code = ERR_PROTOCOL_ERROR;
|
|
Disconnect(s);
|
|
ReleaseSock(s);
|
|
return NULL;
|
|
}
|
|
|
|
if (sha1_cert_hash != NULL && num_hashes >= 1)
|
|
{
|
|
UCHAR hash[SHA1_SIZE];
|
|
UINT i;
|
|
bool ok = false;
|
|
|
|
Zero(hash, sizeof(hash));
|
|
GetXDigest(s->RemoteX, hash, true);
|
|
|
|
for (i = 0;i < num_hashes;i++)
|
|
{
|
|
UCHAR *a = (UCHAR *)sha1_cert_hash;
|
|
a += (SHA1_SIZE * i);
|
|
|
|
if (Cmp(hash, a, SHA1_SIZE) == 0)
|
|
{
|
|
ok = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ok == false)
|
|
{
|
|
// Destination certificate hash mismatch
|
|
*error_code = ERR_CERT_NOT_TRUSTED;
|
|
Disconnect(s);
|
|
ReleaseSock(s);
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Timeout setting
|
|
SetTimeout(s, timeout_comm);
|
|
|
|
// Generate a request
|
|
h = NewHttpHeader(data->Method, target, use_http_proxy ? "HTTP/1.0" : "HTTP/1.1");
|
|
AddHttpValue(h, NewHttpValue("Keep-Alive", HTTP_KEEP_ALIVE));
|
|
AddHttpValue(h, NewHttpValue("Connection", "Keep-Alive"));
|
|
AddHttpValue(h, NewHttpValue("Accept-Language", "ja"));
|
|
AddHttpValue(h, NewHttpValue("User-Agent", WPC_USER_AGENT));
|
|
AddHttpValue(h, NewHttpValue("Pragma", "no-cache"));
|
|
AddHttpValue(h, NewHttpValue("Cache-Control", "no-cache"));
|
|
AddHttpValue(h, NewHttpValue("Host", data->HeaderHostName));
|
|
|
|
if (IsEmptyStr(header_name) == false && IsEmptyStr(header_value) == false)
|
|
{
|
|
AddHttpValue(h, NewHttpValue(header_name, header_value));
|
|
}
|
|
|
|
if (IsEmptyStr(data->Referer) == false)
|
|
{
|
|
AddHttpValue(h, NewHttpValue("Referer", data->Referer));
|
|
}
|
|
|
|
if (StrCmpi(data->Method, WPC_HTTP_POST_NAME) == 0)
|
|
{
|
|
ToStr(len_str, StrLen(post_data));
|
|
AddHttpValue(h, NewHttpValue("Content-Type", "application/x-www-form-urlencoded"));
|
|
AddHttpValue(h, NewHttpValue("Content-Length", len_str));
|
|
}
|
|
|
|
if (IsEmptyStr(data->AdditionalHeaderName) == false && IsEmptyStr(data->AdditionalHeaderValue) == false)
|
|
{
|
|
AddHttpValue(h, NewHttpValue(data->AdditionalHeaderName, data->AdditionalHeaderValue));
|
|
}
|
|
|
|
if (use_http_proxy)
|
|
{
|
|
AddHttpValue(h, NewHttpValue("Proxy-Connection", "Keep-Alive"));
|
|
|
|
if (IsEmptyStr(setting->ProxyUsername) == false || IsEmptyStr(setting->ProxyPassword) == false)
|
|
{
|
|
char auth_tmp_str[MAX_SIZE], auth_b64_str[MAX_SIZE * 2];
|
|
char basic_str[MAX_SIZE * 2];
|
|
|
|
// Generate the authentication string
|
|
Format(auth_tmp_str, sizeof(auth_tmp_str), "%s:%s",
|
|
setting->ProxyUsername, setting->ProxyPassword);
|
|
|
|
// Base64 encode
|
|
Zero(auth_b64_str, sizeof(auth_b64_str));
|
|
Encode64(auth_b64_str, auth_tmp_str);
|
|
Format(basic_str, sizeof(basic_str), "Basic %s", auth_b64_str);
|
|
|
|
AddHttpValue(h, NewHttpValue("Proxy-Authorization", basic_str));
|
|
}
|
|
}
|
|
|
|
send_str = HttpHeaderToStr(h);
|
|
FreeHttpHeader(h);
|
|
|
|
send_buf = NewBuf();
|
|
WriteBuf(send_buf, send_str, StrLen(send_str));
|
|
Free(send_str);
|
|
|
|
// Append to the sending data in the case of POST
|
|
if (StrCmpi(data->Method, WPC_HTTP_POST_NAME) == 0)
|
|
{
|
|
WriteBuf(send_buf, post_data, StrLen(post_data));
|
|
}
|
|
|
|
// Send
|
|
if (SendAll(s, send_buf->Buf, send_buf->Size, s->SecureMode) == false)
|
|
{
|
|
Disconnect(s);
|
|
ReleaseSock(s);
|
|
FreeBuf(send_buf);
|
|
|
|
*error_code = ERR_DISCONNECTED;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
FreeBuf(send_buf);
|
|
|
|
CONT:
|
|
// Receive
|
|
h = RecvHttpHeader(s);
|
|
if (h == NULL)
|
|
{
|
|
Disconnect(s);
|
|
ReleaseSock(s);
|
|
|
|
*error_code = ERR_DISCONNECTED;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
http_error_code = 0;
|
|
if (StrLen(h->Method) == 8)
|
|
{
|
|
if (Cmp(h->Method, "HTTP/1.", 7) == 0)
|
|
{
|
|
http_error_code = ToInt(h->Target);
|
|
}
|
|
}
|
|
|
|
*error_code = ERR_NO_ERROR;
|
|
|
|
switch (http_error_code)
|
|
{
|
|
case 401:
|
|
case 407:
|
|
// Proxy authentication error
|
|
*error_code = ERR_PROXY_AUTH_FAILED;
|
|
break;
|
|
|
|
case 404:
|
|
// 404 File Not Found
|
|
*error_code = ERR_OBJECT_NOT_FOUND;
|
|
break;
|
|
|
|
case 100:
|
|
// Continue
|
|
num_continue++;
|
|
if (num_continue >= 10)
|
|
{
|
|
goto DEF;
|
|
}
|
|
FreeHttpHeader(h);
|
|
goto CONT;
|
|
|
|
case 200:
|
|
// Success
|
|
break;
|
|
|
|
default:
|
|
// Protocol error
|
|
DEF:
|
|
*error_code = ERR_PROTOCOL_ERROR;
|
|
break;
|
|
}
|
|
|
|
if (*error_code != ERR_NO_ERROR)
|
|
{
|
|
// An error has occured
|
|
Disconnect(s);
|
|
ReleaseSock(s);
|
|
FreeHttpHeader(h);
|
|
return NULL;
|
|
}
|
|
|
|
// Get the length of the content
|
|
content_len = GetContentLength(h);
|
|
if (max_recv_size != 0)
|
|
{
|
|
content_len = MIN(content_len, max_recv_size);
|
|
}
|
|
|
|
FreeHttpHeader(h);
|
|
|
|
socket_buffer = Malloc(socket_buffer_size);
|
|
|
|
// Receive the content
|
|
recv_buf = NewBuf();
|
|
|
|
while (true)
|
|
{
|
|
UINT recvsize = MIN(socket_buffer_size, content_len - recv_buf->Size);
|
|
UINT size;
|
|
|
|
if (recv_callback != NULL)
|
|
{
|
|
if (recv_callback(recv_callback_param,
|
|
content_len, recv_buf->Size, recv_buf) == false)
|
|
{
|
|
// Cancel the reception
|
|
*error_code = ERR_USER_CANCEL;
|
|
goto RECV_CANCEL;
|
|
}
|
|
}
|
|
|
|
if (recvsize == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
size = Recv(s, socket_buffer, recvsize, s->SecureMode);
|
|
if (size == 0)
|
|
{
|
|
// Disconnected
|
|
*error_code = ERR_DISCONNECTED;
|
|
|
|
RECV_CANCEL:
|
|
FreeBuf(recv_buf);
|
|
Free(socket_buffer);
|
|
Disconnect(s);
|
|
ReleaseSock(s);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
WriteBuf(recv_buf, socket_buffer, size);
|
|
}
|
|
|
|
SeekBuf(recv_buf, 0, 0);
|
|
Free(socket_buffer);
|
|
|
|
Disconnect(s);
|
|
ReleaseSock(s);
|
|
|
|
// Transmission
|
|
return recv_buf;
|
|
}
|
|
|
|
// Get the proxy server settings from the registry string of IE
|
|
bool GetProxyServerNameAndPortFromIeProxyRegStr(char *name, UINT name_size, UINT *port, char *str, char *server_type)
|
|
{
|
|
#ifdef OS_WIN32
|
|
TOKEN_LIST *t;
|
|
UINT i;
|
|
bool ret = false;
|
|
// Validate arguments
|
|
if (name == NULL || port == NULL || str == NULL || server_type == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
t = ParseToken(str, ";");
|
|
|
|
for (i = 0;i < t->NumTokens;i++)
|
|
{
|
|
char *s = t->Token[i];
|
|
UINT i;
|
|
|
|
Trim(s);
|
|
|
|
i = SearchStrEx(s, "=", 0, false);
|
|
if (i != INFINITE)
|
|
{
|
|
char tmp[MAX_PATH];
|
|
|
|
StrCpy(name, name_size, s);
|
|
name[i] = 0;
|
|
|
|
if (StrCmpi(name, server_type) == 0)
|
|
{
|
|
char *host;
|
|
StrCpy(tmp, sizeof(tmp), s + i + 1);
|
|
|
|
if (ParseHostPort(tmp, &host, port, 0))
|
|
{
|
|
StrCpy(name, name_size, host);
|
|
Free(host);
|
|
|
|
if (*port != 0)
|
|
{
|
|
ret = true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FreeToken(t);
|
|
|
|
return ret;
|
|
#else // OS_WIN32
|
|
return true;
|
|
#endif // OS_WIN32
|
|
}
|
|
|
|
// Get the internet connection settings of the system
|
|
void GetSystemInternetSetting(INTERNET_SETTING *setting)
|
|
{
|
|
#ifdef OS_WIN32
|
|
bool use_proxy;
|
|
// Validate arguments
|
|
if (setting == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Zero(setting, sizeof(INTERNET_SETTING));
|
|
|
|
use_proxy = MsRegReadInt(REG_CURRENT_USER,
|
|
"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
|
|
"ProxyEnable");
|
|
|
|
if (use_proxy)
|
|
{
|
|
char *str = MsRegReadStr(REG_CURRENT_USER,
|
|
"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
|
|
"ProxyServer");
|
|
if (str != NULL)
|
|
{
|
|
char name[MAX_HOST_NAME_LEN + 1];
|
|
UINT port;
|
|
|
|
if (GetProxyServerNameAndPortFromIeProxyRegStr(name, sizeof(name),
|
|
&port, str, "https"))
|
|
{
|
|
setting->ProxyType = PROXY_HTTP;
|
|
StrCpy(setting->ProxyHostName, sizeof(setting->ProxyHostName), name);
|
|
setting->ProxyPort = port;
|
|
}
|
|
else if (GetProxyServerNameAndPortFromIeProxyRegStr(name, sizeof(name),
|
|
&port, str, "http"))
|
|
{
|
|
setting->ProxyType = PROXY_HTTP;
|
|
StrCpy(setting->ProxyHostName, sizeof(setting->ProxyHostName), name);
|
|
setting->ProxyPort = port;
|
|
}
|
|
else if (GetProxyServerNameAndPortFromIeProxyRegStr(name, sizeof(name),
|
|
&port, str, "socks"))
|
|
{
|
|
setting->ProxyType = PROXY_SOCKS;
|
|
StrCpy(setting->ProxyHostName, sizeof(setting->ProxyHostName), name);
|
|
setting->ProxyPort = port;
|
|
}
|
|
else
|
|
{
|
|
if (SearchStrEx(str, "=", 0, false) == INFINITE)
|
|
{
|
|
char *host;
|
|
UINT port;
|
|
if (ParseHostPort(str, &host, &port, 0))
|
|
{
|
|
if (port != 0)
|
|
{
|
|
setting->ProxyType = PROXY_HTTP;
|
|
StrCpy(setting->ProxyHostName, sizeof(setting->ProxyHostName), host);
|
|
setting->ProxyPort = port;
|
|
}
|
|
Free(host);
|
|
}
|
|
}
|
|
}
|
|
|
|
Free(str);
|
|
}
|
|
}
|
|
#else // OS_WIN32
|
|
Zero(setting, sizeof(INTERNET_SETTING));
|
|
#endif // OS_WIN32
|
|
}
|
|
|
|
// Generate the URL
|
|
void CreateUrl(char *url, UINT url_size, URL_DATA *data)
|
|
{
|
|
char *protocol;
|
|
// Validate arguments
|
|
if (url == NULL || data == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (data->Secure == false)
|
|
{
|
|
protocol = "http://";
|
|
}
|
|
else
|
|
{
|
|
protocol = "https://";
|
|
}
|
|
|
|
Format(url, url_size, "%s%s%s", protocol, data->HeaderHostName, data->Target);
|
|
}
|
|
|
|
|
|
// Parse the URL
|
|
bool ParseUrl(URL_DATA *data, char *str, bool is_post, char *referrer)
|
|
{
|
|
char tmp[MAX_SIZE * 3];
|
|
char server_port[MAX_HOST_NAME_LEN + 16];
|
|
char *s = NULL;
|
|
char *host;
|
|
UINT port;
|
|
UINT i;
|
|
// Validate arguments
|
|
if (data == NULL || str == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Zero(data, sizeof(URL_DATA));
|
|
|
|
if (is_post)
|
|
{
|
|
StrCpy(data->Method, sizeof(data->Method), WPC_HTTP_POST_NAME);
|
|
}
|
|
else
|
|
{
|
|
StrCpy(data->Method, sizeof(data->Method), WPC_HTTP_GET_NAME);
|
|
}
|
|
|
|
if (referrer != NULL)
|
|
{
|
|
StrCpy(data->Referer, sizeof(data->Referer), referrer);
|
|
}
|
|
|
|
StrCpy(tmp, sizeof(tmp), str);
|
|
Trim(tmp);
|
|
|
|
// Determine the protocol
|
|
if (StartWith(tmp, "http://"))
|
|
{
|
|
data->Secure = false;
|
|
s = &tmp[7];
|
|
}
|
|
else if (StartWith(tmp, "https://"))
|
|
{
|
|
data->Secure = true;
|
|
s = &tmp[8];
|
|
}
|
|
else
|
|
{
|
|
if (SearchStrEx(tmp, "://", 0, false) != INFINITE)
|
|
{
|
|
return false;
|
|
}
|
|
data->Secure = false;
|
|
s = &tmp[0];
|
|
}
|
|
|
|
// Get the "server name:port number"
|
|
StrCpy(server_port, sizeof(server_port), s);
|
|
i = SearchStrEx(server_port, "/", 0, false);
|
|
if (i != INFINITE)
|
|
{
|
|
server_port[i] = 0;
|
|
s += StrLen(server_port);
|
|
StrCpy(data->Target, sizeof(data->Target), s);
|
|
}
|
|
else
|
|
{
|
|
StrCpy(data->Target, sizeof(data->Target), "/");
|
|
}
|
|
|
|
if (ParseHostPort(server_port, &host, &port, data->Secure ? 443 : 80) == false)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
StrCpy(data->HostName, sizeof(data->HostName), host);
|
|
data->Port = port;
|
|
|
|
Free(host);
|
|
|
|
if ((data->Secure && data->Port == 443) || (data->Secure == false && data->Port == 80))
|
|
{
|
|
StrCpy(data->HeaderHostName, sizeof(data->HeaderHostName), data->HostName);
|
|
}
|
|
else
|
|
{
|
|
Format(data->HeaderHostName, sizeof(data->HeaderHostName),
|
|
"%s:%u", data->HostName, data->Port);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// String replacement
|
|
void Base64ToSafe64(char *str)
|
|
{
|
|
UINT i, len;
|
|
// Validate arguments
|
|
if (str == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
len = StrLen(str);
|
|
|
|
for (i = 0;i < len;i++)
|
|
{
|
|
switch (str[i])
|
|
{
|
|
case '=':
|
|
str[i] = '(';
|
|
break;
|
|
|
|
case '+':
|
|
str[i] = ')';
|
|
break;
|
|
|
|
case '/':
|
|
str[i] = '_';
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
void Safe64ToBase64(char *str)
|
|
{
|
|
UINT i, len;
|
|
// Validate arguments
|
|
if (str == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
len = StrLen(str);
|
|
|
|
for (i = 0;i < len;i++)
|
|
{
|
|
switch (str[i])
|
|
{
|
|
case '(':
|
|
str[i] = '=';
|
|
break;
|
|
|
|
case ')':
|
|
str[i] = '+';
|
|
break;
|
|
|
|
case '_':
|
|
str[i] = '/';
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Decode from Safe64
|
|
UINT DecodeSafe64(void *dst, char *src, UINT src_strlen)
|
|
{
|
|
char *tmp;
|
|
UINT ret;
|
|
if (dst == NULL || src == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (src_strlen == 0)
|
|
{
|
|
src_strlen = StrLen(src);
|
|
}
|
|
|
|
tmp = Malloc(src_strlen + 1);
|
|
Copy(tmp, src, src_strlen);
|
|
tmp[src_strlen] = 0;
|
|
Safe64ToBase64(tmp);
|
|
|
|
ret = B64_Decode(dst, tmp, src_strlen);
|
|
Free(tmp);
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Encode to Safe64
|
|
void EncodeSafe64(char *dst, void *src, UINT src_size)
|
|
{
|
|
UINT size;
|
|
if (dst == NULL || src == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
size = B64_Encode(dst, src, src_size);
|
|
dst[size] = 0;
|
|
|
|
Base64ToSafe64(dst);
|
|
}
|
|
|