mirror of
https://github.com/SoftEtherVPN/SoftEtherVPN.git
synced 2024-11-23 01:49:53 +03:00
233e28f38c
Our own implementation works fine, however we should use OpenSSL's one since we already link to the library. Base64Decode() and Base64Encode() return the required buffer size when "dst" is NULL. This allows to efficiently allocate a buffer, without wasting memory or risking an overflow. Base64FromBin() and Base64ToBin() perform all steps, returning a heap-allocated buffer with the data in it.
1315 lines
28 KiB
C
1315 lines
28 KiB
C
// SoftEther VPN Source Code - Developer Edition Master Branch
|
|
// Cedar Communication Module
|
|
|
|
|
|
// Wpc.c
|
|
// RPC over HTTP
|
|
|
|
#include "Wpc.h"
|
|
|
|
#include "Command.h"
|
|
#include "Protocol.h"
|
|
|
|
#include "Mayaqua/DNS.h"
|
|
#include "Mayaqua/Encoding.h"
|
|
#include "Mayaqua/Memory.h"
|
|
#include "Mayaqua/Microsoft.h"
|
|
#include "Mayaqua/Pack.h"
|
|
#include "Mayaqua/Proxy.h"
|
|
#include "Mayaqua/Str.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_str[MAX_SIZE * 2];
|
|
Format(auth_str, sizeof(auth_str), "%s:%s", setting->ProxyUsername, setting->ProxyPassword);
|
|
|
|
char *base64 = Base64FromBin(NULL, auth_str, StrLen(auth_str));
|
|
Format(auth_str, sizeof(auth_str), "Basic %s", base64);
|
|
Free(base64);
|
|
|
|
AddHttpValue(h, NewHttpValue("Proxy-Authorization", auth_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, const UINT size)
|
|
{
|
|
if (str == NULL || size == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (UINT i = 0; i < size; ++i)
|
|
{
|
|
switch (str[i])
|
|
{
|
|
case '=':
|
|
str[i] = '(';
|
|
break;
|
|
|
|
case '+':
|
|
str[i] = ')';
|
|
break;
|
|
|
|
case '/':
|
|
str[i] = '_';
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
void Safe64ToBase64(char *str, const UINT size)
|
|
{
|
|
if (str == NULL || size == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (UINT i = 0; i < size; ++i)
|
|
{
|
|
switch (str[i])
|
|
{
|
|
case '(':
|
|
str[i] = '=';
|
|
break;
|
|
|
|
case ')':
|
|
str[i] = '+';
|
|
break;
|
|
|
|
case '_':
|
|
str[i] = '/';
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Decode from escaped Base64
|
|
UINT DecodeSafe64(void *dst, const char *src, UINT size)
|
|
{
|
|
if (dst == NULL || src == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (size == 0)
|
|
{
|
|
size = StrLen(src);
|
|
}
|
|
|
|
char *tmp = Malloc(size + 1);
|
|
Copy(tmp, src, size);
|
|
tmp[size] = '\0';
|
|
|
|
Safe64ToBase64(tmp, size);
|
|
const UINT ret = Base64Decode(dst, tmp, size);
|
|
Free(tmp);
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Encode to escaped Base64
|
|
void EncodeSafe64(char *dst, const void *src, const UINT size)
|
|
{
|
|
if (dst == NULL || src == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const UINT ret = Base64Encode(dst, src, size);
|
|
|
|
Base64ToSafe64(dst, ret);
|
|
}
|