1
0
mirror of https://github.com/SoftEtherVPN/SoftEtherVPN.git synced 2024-09-19 18:20:40 +03:00
SoftEtherVPN/src/Cedar/Wpc.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);
}