1
0
mirror of https://github.com/SoftEtherVPN/SoftEtherVPN.git synced 2024-11-09 19:20:41 +03:00
SoftEtherVPN/src/Cedar/Wpc.c
Davide Beatrici 233e28f38c Refactor Base64 functions, encode/decode using OpenSSL's EVP interface
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.
2021-07-02 09:24:41 +02:00

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);
}