1 // SoftEther VPN Source Code - Stable Edition Repository
2 // Cedar Communication Module
3 //
4 // SoftEther VPN Server, Client and Bridge are free software under the Apache License, Version 2.0.
5 //
6 // Copyright (c) Daiyuu Nobori.
7 // Copyright (c) SoftEther VPN Project, University of Tsukuba, Japan.
8 // Copyright (c) SoftEther Corporation.
9 // Copyright (c) all contributors on SoftEther VPN project in GitHub.
10 //
11 // All Rights Reserved.
12 //
13 // http://www.softether.org/
14 //
15 // This stable branch is officially managed by Daiyuu Nobori, the owner of SoftEther VPN Project.
16 // Pull requests should be sent to the Developer Edition Master Repository on https://github.com/SoftEtherVPN/SoftEtherVPN
17 //
18 // License: The Apache License, Version 2.0
19 // https://www.apache.org/licenses/LICENSE-2.0
20 //
21 // DISCLAIMER
22 // ==========
23 //
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30 // SOFTWARE.
31 //
32 // THIS SOFTWARE IS DEVELOPED IN JAPAN, AND DISTRIBUTED FROM JAPAN, UNDER
33 // JAPANESE LAWS. YOU MUST AGREE IN ADVANCE TO USE, COPY, MODIFY, MERGE, PUBLISH,
34 // DISTRIBUTE, SUBLICENSE, AND/OR SELL COPIES OF THIS SOFTWARE, THAT ANY
35 // JURIDICAL DISPUTES WHICH ARE CONCERNED TO THIS SOFTWARE OR ITS CONTENTS,
36 // AGAINST US (SOFTETHER PROJECT, SOFTETHER CORPORATION, DAIYUU NOBORI OR OTHER
37 // SUPPLIERS), OR ANY JURIDICAL DISPUTES AGAINST US WHICH ARE CAUSED BY ANY KIND
38 // OF USING, COPYING, MODIFYING, MERGING, PUBLISHING, DISTRIBUTING, SUBLICENSING,
39 // AND/OR SELLING COPIES OF THIS SOFTWARE SHALL BE REGARDED AS BE CONSTRUED AND
40 // CONTROLLED BY JAPANESE LAWS, AND YOU MUST FURTHER CONSENT TO EXCLUSIVE
41 // JURISDICTION AND VENUE IN THE COURTS SITTING IN TOKYO, JAPAN. YOU MUST WAIVE
42 // ALL DEFENSES OF LACK OF PERSONAL JURISDICTION AND FORUM NON CONVENIENS.
43 // PROCESS MAY BE SERVED ON EITHER PARTY IN THE MANNER AUTHORIZED BY APPLICABLE
44 // LAW OR COURT RULE.
45 //
46 // USE ONLY IN JAPAN. DO NOT USE THIS SOFTWARE IN ANOTHER COUNTRY UNLESS YOU HAVE
47 // A CONFIRMATION THAT THIS SOFTWARE DOES NOT VIOLATE ANY CRIMINAL LAWS OR CIVIL
48 // RIGHTS IN THAT PARTICULAR COUNTRY. USING THIS SOFTWARE IN OTHER COUNTRIES IS
49 // COMPLETELY AT YOUR OWN RISK. THE SOFTETHER VPN PROJECT HAS DEVELOPED AND
50 // DISTRIBUTED THIS SOFTWARE TO COMPLY ONLY WITH THE JAPANESE LAWS AND EXISTING
51 // CIVIL RIGHTS INCLUDING PATENTS WHICH ARE SUBJECTS APPLY IN JAPAN. OTHER
52 // COUNTRIES' LAWS OR CIVIL RIGHTS ARE NONE OF OUR CONCERNS NOR RESPONSIBILITIES.
53 // WE HAVE NEVER INVESTIGATED ANY CRIMINAL REGULATIONS, CIVIL LAWS OR
54 // INTELLECTUAL PROPERTY RIGHTS INCLUDING PATENTS IN ANY OF OTHER 200+ COUNTRIES
55 // AND TERRITORIES. BY NATURE, THERE ARE 200+ REGIONS IN THE WORLD, WITH
56 // DIFFERENT LAWS. IT IS IMPOSSIBLE TO VERIFY EVERY COUNTRIES' LAWS, REGULATIONS
57 // AND CIVIL RIGHTS TO MAKE THE SOFTWARE COMPLY WITH ALL COUNTRIES' LAWS BY THE
58 // PROJECT. EVEN IF YOU WILL BE SUED BY A PRIVATE ENTITY OR BE DAMAGED BY A
59 // PUBLIC SERVANT IN YOUR COUNTRY, THE DEVELOPERS OF THIS SOFTWARE WILL NEVER BE
60 // LIABLE TO RECOVER OR COMPENSATE SUCH DAMAGES, CRIMINAL OR CIVIL
61 // RESPONSIBILITIES. NOTE THAT THIS LINE IS NOT LICENSE RESTRICTION BUT JUST A
62 // STATEMENT FOR WARNING AND DISCLAIMER.
63 //
64 // READ AND UNDERSTAND THE 'WARNING.TXT' FILE BEFORE USING THIS SOFTWARE.
65 // SOME SOFTWARE PROGRAMS FROM THIRD PARTIES ARE INCLUDED ON THIS SOFTWARE WITH
66 // LICENSE CONDITIONS WHICH ARE DESCRIBED ON THE 'THIRD_PARTY.TXT' FILE.
67 //
68 //
69 // SOURCE CODE CONTRIBUTION
70 // ------------------------
71 //
72 // Your contribution to SoftEther VPN Project is much appreciated.
73 // Please send patches to us through GitHub.
74 // Read the SoftEther VPN Patch Acceptance Policy in advance:
75 // http://www.softether.org/5-download/src/9.patch
76 //
77 //
78 // DEAR SECURITY EXPERTS
79 // ---------------------
80 //
81 // If you find a bug or a security vulnerability please kindly inform us
82 // about the problem immediately so that we can fix the security problem
83 // to protect a lot of users around the world as soon as possible.
84 //
85 // Our e-mail address for security reports is:
86 // softether-vpn-security [at] softether.org
87 //
88 // Please note that the above e-mail address is not a technical support
89 // inquiry address. If you need technical assistance, please visit
90 // http://www.softether.org/ and ask your question on the users forum.
91 //
92 // Thank you for your cooperation.
93 //
94 //
95 // NO MEMORY OR RESOURCE LEAKS
96 // ---------------------------
97 //
98 // The memory-leaks and resource-leaks verification under the stress
99 // test has been passed before release this source code.
100
101
102 // Wpc.c
103 // RPC over HTTP
104
105 #include <GlobalConst.h>
106
107 #include "CedarPch.h"
108
109 // Get whether the proxy server is specified by a private IP
IsProxyPrivateIp(INTERNET_SETTING * s)110 bool IsProxyPrivateIp(INTERNET_SETTING *s)
111 {
112 IP ip;
113 // Validate arguments
114 if (s == NULL)
115 {
116 return false;
117 }
118
119 if (s->ProxyType == PROXY_DIRECT)
120 {
121 return false;
122 }
123
124 if (GetIP(&ip, s->ProxyHostName) == false)
125 {
126 return false;
127 }
128
129 if (IsIPPrivate(&ip))
130 {
131 return true;
132 }
133
134 if (IsIPMyHost(&ip))
135 {
136 return true;
137 }
138
139 if (IsLocalHostIP(&ip))
140 {
141 return true;
142 }
143
144 return false;
145 }
146
147 // Call
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)148 PACK *WpcCall(char *url, INTERNET_SETTING *setting, UINT timeout_connect, UINT timeout_comm,
149 char *function_name, PACK *pack, X *cert, K *key, void *sha1_cert_hash)
150 {
151 return WpcCallEx(url, setting, timeout_connect, timeout_comm, function_name, pack, cert, key,
152 sha1_cert_hash, NULL, 0, NULL, NULL);
153 }
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)154 PACK *WpcCallEx(char *url, INTERNET_SETTING *setting, UINT timeout_connect, UINT timeout_comm,
155 char *function_name, PACK *pack, X *cert, K *key, void *sha1_cert_hash, bool *cancel, UINT max_recv_size,
156 char *additional_header_name, char *additional_header_value)
157 {
158 return WpcCallEx2(url, setting, timeout_connect, timeout_comm, function_name, pack,
159 cert, key, sha1_cert_hash, (sha1_cert_hash == NULL ? 0 : 1),
160 cancel, max_recv_size, additional_header_name, additional_header_value, NULL);
161 }
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)162 PACK *WpcCallEx2(char *url, INTERNET_SETTING *setting, UINT timeout_connect, UINT timeout_comm,
163 char *function_name, PACK *pack, X *cert, K *key, void *sha1_cert_hash, UINT num_hashes, bool *cancel, UINT max_recv_size,
164 char *additional_header_name, char *additional_header_value, char *sni_string)
165 {
166 URL_DATA data;
167 BUF *b, *recv;
168 UINT error;
169 WPC_PACKET packet;
170 // Validate arguments
171 if (function_name == NULL || pack == NULL)
172 {
173 return PackError(ERR_INTERNAL_ERROR);
174 }
175
176 if (ParseUrl(&data, url, true, NULL) == false)
177 {
178 return PackError(ERR_INTERNAL_ERROR);
179 }
180
181 PackAddStr(pack, "function", function_name);
182
183 b = WpcGeneratePacket(pack, cert, key);
184 if (b == NULL)
185 {
186 return PackError(ERR_INTERNAL_ERROR);
187 }
188
189 SeekBuf(b, b->Size, 0);
190 WriteBufInt(b, 0);
191 SeekBuf(b, 0, 0);
192
193 if (IsEmptyStr(additional_header_name) == false && IsEmptyStr(additional_header_value) == false)
194 {
195 StrCpy(data.AdditionalHeaderName, sizeof(data.AdditionalHeaderName), additional_header_name);
196 StrCpy(data.AdditionalHeaderValue, sizeof(data.AdditionalHeaderValue), additional_header_value);
197 }
198
199 if (sni_string != NULL && IsEmptyStr(sni_string) == false)
200 {
201 StrCpy(data.SniString, sizeof(data.SniString), sni_string);
202 }
203
204 recv = HttpRequestEx3(&data, setting, timeout_connect, timeout_comm, &error,
205 false, b->Buf, NULL, NULL, sha1_cert_hash, num_hashes, cancel, max_recv_size,
206 NULL, NULL);
207
208 FreeBuf(b);
209
210 if (recv == NULL)
211 {
212 return PackError(error);
213 }
214
215 if (WpcParsePacket(&packet, recv) == false)
216 {
217 FreeBuf(recv);
218 return PackError(ERR_PROTOCOL_ERROR);
219 }
220
221 FreeBuf(recv);
222
223 FreeX(packet.Cert);
224
225 return packet.Pack;
226 }
227
228 // Release the packet
WpcFreePacket(WPC_PACKET * packet)229 void WpcFreePacket(WPC_PACKET *packet)
230 {
231 // Validate arguments
232 if (packet == NULL)
233 {
234 return;
235 }
236
237 FreePack(packet->Pack);
238 FreeX(packet->Cert);
239 }
240
241 // Parse the packet
WpcParsePacket(WPC_PACKET * packet,BUF * buf)242 bool WpcParsePacket(WPC_PACKET *packet, BUF *buf)
243 {
244 LIST *o;
245 BUF *b;
246 bool ret = false;
247 UCHAR hash[SHA1_SIZE];
248 // Validate arguments
249 if (packet == NULL || buf == NULL)
250 {
251 return false;
252 }
253
254 Zero(packet, sizeof(WPC_PACKET));
255
256 o = WpcParseDataEntry(buf);
257
258 b = WpcDataEntryToBuf(WpcFindDataEntry(o, "PACK"));
259 if (b != NULL)
260 {
261 HashSha1(hash, b->Buf, b->Size);
262
263 packet->Pack = BufToPack(b);
264 FreeBuf(b);
265
266 if (packet->Pack != NULL)
267 {
268 BUF *b;
269
270 ret = true;
271
272 b = WpcDataEntryToBuf(WpcFindDataEntry(o, "HASH"));
273
274 if (b != NULL)
275 {
276 if (b->Size != SHA1_SIZE || Cmp(b->Buf, hash, SHA1_SIZE) != 0)
277 {
278 ret = false;
279 FreePack(packet->Pack);
280 }
281 else
282 {
283 BUF *b;
284
285 Copy(packet->Hash, hash, SHA1_SIZE);
286
287 b = WpcDataEntryToBuf(WpcFindDataEntry(o, "CERT"));
288
289 if (b != NULL)
290 {
291 X *cert = BufToX(b, false);
292 if (cert == NULL)
293 {
294 ret = false;
295 FreePack(packet->Pack);
296 }
297 else
298 {
299 BUF *b = WpcDataEntryToBuf(WpcFindDataEntry(o, "SIGN"));
300
301 if (b == NULL || (b->Size != 128))
302 {
303 ret = false;
304 FreeX(cert);
305 FreePack(packet->Pack);
306 }
307 else
308 {
309 K *k = GetKFromX(cert);
310
311 if (RsaVerify(hash, SHA1_SIZE, b->Buf, k) == false)
312 {
313 ret = false;
314 FreeX(cert);
315 FreePack(packet->Pack);
316 }
317 else
318 {
319 packet->Cert = cert;
320 Copy(packet->Sign, b->Buf, 128);
321 }
322
323 FreeK(k);
324 }
325
326 FreeBuf(b);
327 }
328 FreeBuf(b);
329 }
330 }
331 FreeBuf(b);
332 }
333 }
334 }
335
336 WpcFreeDataEntryList(o);
337
338 return ret;
339 }
340
341 // Generate the packet
WpcGeneratePacket(PACK * pack,X * cert,K * key)342 BUF *WpcGeneratePacket(PACK *pack, X *cert, K *key)
343 {
344 UCHAR hash[SHA1_SIZE];
345 BUF *pack_data;
346 BUF *cert_data = NULL;
347 BUF *sign_data = NULL;
348 BUF *b;
349 // Validate arguments
350 if (pack == NULL)
351 {
352 return NULL;
353 }
354
355 pack_data = PackToBuf(pack);
356 HashSha1(hash, pack_data->Buf, pack_data->Size);
357
358 if (cert != NULL && key != NULL)
359 {
360 UCHAR sign[128];
361 cert_data = XToBuf(cert, false);
362
363 RsaSign(sign, hash, sizeof(hash), key);
364
365 sign_data = NewBuf();
366 WriteBuf(sign_data, sign, sizeof(sign));
367 SeekBuf(sign_data, 0, 0);
368 }
369
370 b = NewBuf();
371
372 WpcAddDataEntryBin(b, "PACK", pack_data->Buf, pack_data->Size);
373 WpcAddDataEntryBin(b, "HASH", hash, sizeof(hash));
374
375 if (cert_data != NULL)
376 {
377 WpcAddDataEntryBin(b, "CERT", cert_data->Buf, cert_data->Size);
378 WpcAddDataEntryBin(b, "SIGN", sign_data->Buf, sign_data->Size);
379 }
380
381 FreeBuf(pack_data);
382 FreeBuf(cert_data);
383 FreeBuf(sign_data);
384
385 SeekBuf(b, 0, 0);
386
387 return b;
388 }
389
390 // Decode the buffer from WPC_ENTRY
WpcDataEntryToBuf(WPC_ENTRY * e)391 BUF *WpcDataEntryToBuf(WPC_ENTRY *e)
392 {
393 void *data;
394 UINT data_size;
395 UINT size;
396 BUF *b;
397 // Validate arguments
398 if (e == NULL)
399 {
400 return NULL;
401 }
402
403 data_size = e->Size + 4096;
404 data = Malloc(data_size);
405 size = DecodeSafe64(data, e->Data, e->Size);
406
407 b = NewBuf();
408 WriteBuf(b, data, size);
409 SeekBuf(b, 0, 0);
410
411 Free(data);
412
413 return b;
414 }
415
416 // Search for the data entry
WpcFindDataEntry(LIST * o,char * name)417 WPC_ENTRY *WpcFindDataEntry(LIST *o, char *name)
418 {
419 UINT i;
420 char name_str[WPC_DATA_ENTRY_SIZE];
421 // Validate arguments
422 if (o == NULL || name == NULL)
423 {
424 return NULL;
425 }
426
427 WpcFillEntryName(name_str, name);
428
429 for (i = 0;i < LIST_NUM(o);i++)
430 {
431 WPC_ENTRY *e = LIST_DATA(o, i);
432
433 if (Cmp(e->EntryName, name_str, WPC_DATA_ENTRY_SIZE) == 0)
434 {
435 return e;
436 }
437 }
438
439 return NULL;
440 }
441
442 // Release the data entry list
WpcFreeDataEntryList(LIST * o)443 void WpcFreeDataEntryList(LIST *o)
444 {
445 UINT i;
446 // Validate arguments
447 if (o == NULL)
448 {
449 return;
450 }
451
452 for (i = 0;i < LIST_NUM(o);i++)
453 {
454 WPC_ENTRY *e = LIST_DATA(o, i);
455
456 Free(e);
457 }
458
459 ReleaseList(o);
460 }
461
462 // Parse the data entry
WpcParseDataEntry(BUF * b)463 LIST *WpcParseDataEntry(BUF *b)
464 {
465 char entry_name[WPC_DATA_ENTRY_SIZE];
466 char size_str[11];
467 LIST *o;
468 // Validate arguments
469 if (b == NULL)
470 {
471 return NULL;
472 }
473
474 SeekBuf(b, 0, 0);
475
476 o = NewListFast(NULL);
477
478 while (true)
479 {
480 UINT size;
481 WPC_ENTRY *e;
482
483 if (ReadBuf(b, entry_name, WPC_DATA_ENTRY_SIZE) != WPC_DATA_ENTRY_SIZE)
484 {
485 break;
486 }
487
488 Zero(size_str, sizeof(size_str));
489 if (ReadBuf(b, size_str, 10) != 10)
490 {
491 break;
492 }
493
494 size = ToInt(size_str);
495 if ((b->Size - b->Current) < size)
496 {
497 break;
498 }
499
500 e = ZeroMalloc(sizeof(WPC_ENTRY));
501 e->Data = (UCHAR *)b->Buf + b->Current;
502 Copy(e->EntryName, entry_name, WPC_DATA_ENTRY_SIZE);
503 e->Size = size;
504
505 SeekBuf(b, size, 1);
506
507 Add(o, e);
508 }
509
510 return o;
511 }
512
513 // Generate a entry name
WpcFillEntryName(char * dst,char * name)514 void WpcFillEntryName(char *dst, char *name)
515 {
516 UINT i, len;
517 char tmp[MAX_SIZE];
518 // Validate arguments
519 if (dst == NULL || name == NULL)
520 {
521 return;
522 }
523
524 StrCpy(tmp, sizeof(tmp), name);
525 StrUpper(tmp);
526 len = StrLen(tmp);
527
528 for (i = 0;i < WPC_DATA_ENTRY_SIZE;i++)
529 {
530 dst[i] = ' ';
531 }
532
533 if (len <= WPC_DATA_ENTRY_SIZE)
534 {
535 Copy(dst, tmp, len);
536 }
537 else
538 {
539 Copy(dst, tmp, WPC_DATA_ENTRY_SIZE);
540 }
541 }
542
543 // Add the data entry (binary)
WpcAddDataEntryBin(BUF * b,char * name,void * data,UINT size)544 void WpcAddDataEntryBin(BUF *b, char *name, void *data, UINT size)
545 {
546 char *str;
547 // Validate arguments
548 if (b == NULL || name == NULL || (data == NULL && size != 0))
549 {
550 return;
551 }
552
553 str = Malloc(size * 2 + 64);
554
555 EncodeSafe64(str, data, size);
556
557 WpcAddDataEntry(b, name, str, StrLen(str));
558
559 Free(str);
560 }
561
562
563 // Add the data entry
WpcAddDataEntry(BUF * b,char * name,void * data,UINT size)564 void WpcAddDataEntry(BUF *b, char *name, void *data, UINT size)
565 {
566 char entry_name[WPC_DATA_ENTRY_SIZE];
567 char size_str[11];
568 // Validate arguments
569 if (b == NULL || name == NULL || (data == NULL && size != 0))
570 {
571 return;
572 }
573
574 WpcFillEntryName(entry_name, name);
575 WriteBuf(b, entry_name, WPC_DATA_ENTRY_SIZE);
576
577 Format(size_str, sizeof(size_str), "%010u", size);
578 WriteBuf(b, size_str, 10);
579
580 WriteBuf(b, data, size);
581 }
582
583 // Get the empty INTERNET_SETTING
GetNullInternetSetting()584 INTERNET_SETTING *GetNullInternetSetting()
585 {
586 static INTERNET_SETTING ret;
587
588 Zero(&ret, sizeof(ret));
589
590 return &ret;
591 }
592
593 // Socket connection
WpcSockConnect(WPC_CONNECT * param,UINT * error_code,UINT timeout)594 SOCK *WpcSockConnect(WPC_CONNECT *param, UINT *error_code, UINT timeout)
595 {
596 return WpcSockConnectEx(param, error_code, timeout, NULL);
597 }
WpcSockConnectEx(WPC_CONNECT * param,UINT * error_code,UINT timeout,bool * cancel)598 SOCK *WpcSockConnectEx(WPC_CONNECT *param, UINT *error_code, UINT timeout, bool *cancel)
599 {
600 CONNECTION c;
601 SOCK *sock;
602 UINT err = ERR_NO_ERROR;
603 // Validate arguments
604 if (param == NULL)
605 {
606 return NULL;
607 }
608
609 Zero(&c, sizeof(c));
610
611 sock = NULL;
612 err = ERR_INTERNAL_ERROR;
613
614 switch (param->ProxyType)
615 {
616 case PROXY_DIRECT:
617 sock = TcpConnectEx3(param->HostName, param->Port, timeout, cancel, NULL, true, NULL, false, false, NULL);
618 if (sock == NULL)
619 {
620 err = ERR_CONNECT_FAILED;
621 }
622 break;
623
624 case PROXY_HTTP:
625 sock = ProxyConnectEx2(&c, param->ProxyHostName, param->ProxyPort,
626 param->HostName, param->Port,
627 param->ProxyUsername, param->ProxyPassword, false, cancel, NULL, timeout);
628 if (sock == NULL)
629 {
630 err = c.Err;
631 }
632 break;
633
634 case PROXY_SOCKS:
635 sock = SocksConnectEx2(&c, param->ProxyHostName, param->ProxyPort,
636 param->HostName, param->Port,
637 param->ProxyUsername, false, cancel, NULL, timeout, NULL);
638 if (sock == NULL)
639 {
640 err = c.Err;
641 }
642 break;
643 }
644
645 if (error_code != NULL)
646 {
647 *error_code = err;
648 }
649
650 return sock;
651 }
WpcSockConnect2(char * hostname,UINT port,INTERNET_SETTING * t,UINT * error_code,UINT timeout)652 SOCK *WpcSockConnect2(char *hostname, UINT port, INTERNET_SETTING *t, UINT *error_code, UINT timeout)
653 {
654 // Validate arguments
655 INTERNET_SETTING t2;
656 WPC_CONNECT c;
657 if (t == NULL)
658 {
659 Zero(&t2, sizeof(t2));
660
661 t = &t2;
662 }
663
664 Zero(&c, sizeof(c));
665 StrCpy(c.HostName, sizeof(c.HostName), hostname);
666 c.Port = port;
667 c.ProxyType = t->ProxyType;
668 StrCpy(c.ProxyHostName, sizeof(c.HostName), t->ProxyHostName);
669 c.ProxyPort = t->ProxyPort;
670 StrCpy(c.ProxyUsername, sizeof(c.ProxyUsername), t->ProxyUsername);
671 StrCpy(c.ProxyPassword, sizeof(c.ProxyPassword), t->ProxyPassword);
672
673 return WpcSockConnect(&c, error_code, timeout);
674 }
675
676 // Handle the HTTP request
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)677 BUF *HttpRequest(URL_DATA *data, INTERNET_SETTING *setting,
678 UINT timeout_connect, UINT timeout_comm,
679 UINT *error_code, bool check_ssl_trust, char *post_data,
680 WPC_RECV_CALLBACK *recv_callback, void *recv_callback_param, void *sha1_cert_hash)
681 {
682 return HttpRequestEx(data, setting, timeout_connect, timeout_comm,
683 error_code, check_ssl_trust, post_data,
684 recv_callback, recv_callback_param, sha1_cert_hash, NULL, 0);
685 }
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)686 BUF *HttpRequestEx(URL_DATA *data, INTERNET_SETTING *setting,
687 UINT timeout_connect, UINT timeout_comm,
688 UINT *error_code, bool check_ssl_trust, char *post_data,
689 WPC_RECV_CALLBACK *recv_callback, void *recv_callback_param, void *sha1_cert_hash,
690 bool *cancel, UINT max_recv_size)
691 {
692 return HttpRequestEx2(data, setting, timeout_connect, timeout_comm, error_code,
693 check_ssl_trust, post_data, recv_callback, recv_callback_param, sha1_cert_hash,
694 cancel, max_recv_size, NULL, NULL);
695 }
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)696 BUF *HttpRequestEx2(URL_DATA *data, INTERNET_SETTING *setting,
697 UINT timeout_connect, UINT timeout_comm,
698 UINT *error_code, bool check_ssl_trust, char *post_data,
699 WPC_RECV_CALLBACK *recv_callback, void *recv_callback_param, void *sha1_cert_hash,
700 bool *cancel, UINT max_recv_size, char *header_name, char *header_value)
701 {
702 return HttpRequestEx3(data, setting, timeout_connect, timeout_comm, error_code, check_ssl_trust,
703 post_data, recv_callback, recv_callback_param, sha1_cert_hash, (sha1_cert_hash == NULL ? 0 : 1),
704 cancel, max_recv_size, header_name, header_value);
705 }
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)706 BUF *HttpRequestEx3(URL_DATA *data, INTERNET_SETTING *setting,
707 UINT timeout_connect, UINT timeout_comm,
708 UINT *error_code, bool check_ssl_trust, char *post_data,
709 WPC_RECV_CALLBACK *recv_callback, void *recv_callback_param, void *sha1_cert_hash, UINT num_hashes,
710 bool *cancel, UINT max_recv_size, char *header_name, char *header_value)
711 {
712 WPC_CONNECT con;
713 SOCK *s;
714 HTTP_HEADER *h;
715 bool use_http_proxy = false;
716 char target[MAX_SIZE * 4];
717 char *send_str;
718 BUF *send_buf;
719 BUF *recv_buf;
720 UINT http_error_code;
721 char len_str[100];
722 UINT content_len;
723 void *socket_buffer;
724 UINT socket_buffer_size = WPC_RECV_BUF_SIZE;
725 UINT num_continue = 0;
726 INTERNET_SETTING wt_setting;
727 // Validate arguments
728 if (data == NULL)
729 {
730 return NULL;
731 }
732 if (setting == NULL)
733 {
734 Zero(&wt_setting, sizeof(wt_setting));
735 setting = &wt_setting;
736 }
737 if (error_code == NULL)
738 {
739 static UINT ret = 0;
740 error_code = &ret;
741 }
742 if (timeout_comm == 0)
743 {
744 timeout_comm = WPC_TIMEOUT;
745 }
746 if (sha1_cert_hash == NULL)
747 {
748 num_hashes = 0;
749 }
750 if (num_hashes == 0)
751 {
752 sha1_cert_hash = NULL;
753 }
754
755 // Connection
756 Zero(&con, sizeof(con));
757 StrCpy(con.HostName, sizeof(con.HostName), data->HostName);
758 con.Port = data->Port;
759 con.ProxyType = setting->ProxyType;
760 StrCpy(con.ProxyHostName, sizeof(con.ProxyHostName), setting->ProxyHostName);
761 con.ProxyPort = setting->ProxyPort;
762 StrCpy(con.ProxyUsername, sizeof(con.ProxyUsername), setting->ProxyUsername);
763 StrCpy(con.ProxyPassword, sizeof(con.ProxyPassword), setting->ProxyPassword);
764
765 if (setting->ProxyType != PROXY_HTTP || data->Secure)
766 {
767 use_http_proxy = false;
768 StrCpy(target, sizeof(target), data->Target);
769 }
770 else
771 {
772 use_http_proxy = true;
773 CreateUrl(target, sizeof(target), data);
774 }
775
776 if (use_http_proxy == false)
777 {
778 // If the connection is not via HTTP Proxy, or is a SSL connection even via HTTP Proxy
779 s = WpcSockConnectEx(&con, error_code, timeout_connect, cancel);
780 }
781 else
782 {
783 // If the connection is not SSL via HTTP Proxy
784 s = TcpConnectEx3(con.ProxyHostName, con.ProxyPort, timeout_connect, cancel, NULL, true, NULL, false, false, NULL);
785 if (s == NULL)
786 {
787 *error_code = ERR_PROXY_CONNECT_FAILED;
788 }
789 }
790
791 if (s == NULL)
792 {
793 return NULL;
794 }
795
796 if (data->Secure)
797 {
798 // Start the SSL communication
799 if (StartSSLEx(s, NULL, NULL, true, 0, (IsEmptyStr(data->SniString) ? NULL : data->SniString)) == false)
800 {
801 // SSL connection failed
802 *error_code = ERR_PROTOCOL_ERROR;
803 Disconnect(s);
804 ReleaseSock(s);
805 return NULL;
806 }
807
808 if (sha1_cert_hash != NULL && num_hashes >= 1)
809 {
810 UCHAR hash[SHA1_SIZE];
811 UINT i;
812 bool ok = false;
813
814 Zero(hash, sizeof(hash));
815 GetXDigest(s->RemoteX, hash, true);
816
817 for (i = 0;i < num_hashes;i++)
818 {
819 UCHAR *a = (UCHAR *)sha1_cert_hash;
820 a += (SHA1_SIZE * i);
821
822 if (Cmp(hash, a, SHA1_SIZE) == 0)
823 {
824 ok = true;
825 break;
826 }
827 }
828
829 if (ok == false)
830 {
831 // Destination certificate hash mismatch
832 *error_code = ERR_CERT_NOT_TRUSTED;
833 Disconnect(s);
834 ReleaseSock(s);
835 return NULL;
836 }
837 }
838 }
839
840 // Timeout setting
841 SetTimeout(s, timeout_comm);
842
843 // Generate a request
844 h = NewHttpHeader(data->Method, target, use_http_proxy ? "HTTP/1.0" : "HTTP/1.1");
845 AddHttpValue(h, NewHttpValue("Keep-Alive", HTTP_KEEP_ALIVE));
846 AddHttpValue(h, NewHttpValue("Connection", "Keep-Alive"));
847 AddHttpValue(h, NewHttpValue("Accept-Language", "ja"));
848 AddHttpValue(h, NewHttpValue("User-Agent", WPC_USER_AGENT));
849 AddHttpValue(h, NewHttpValue("Pragma", "no-cache"));
850 AddHttpValue(h, NewHttpValue("Cache-Control", "no-cache"));
851 AddHttpValue(h, NewHttpValue("Host", data->HeaderHostName));
852
853 if (IsEmptyStr(header_name) == false && IsEmptyStr(header_value) == false)
854 {
855 AddHttpValue(h, NewHttpValue(header_name, header_value));
856 }
857
858 if (IsEmptyStr(data->Referer) == false)
859 {
860 AddHttpValue(h, NewHttpValue("Referer", data->Referer));
861 }
862
863 if (StrCmpi(data->Method, WPC_HTTP_POST_NAME) == 0)
864 {
865 ToStr(len_str, StrLen(post_data));
866 AddHttpValue(h, NewHttpValue("Content-Type", "application/x-www-form-urlencoded"));
867 AddHttpValue(h, NewHttpValue("Content-Length", len_str));
868 }
869
870 if (IsEmptyStr(data->AdditionalHeaderName) == false && IsEmptyStr(data->AdditionalHeaderValue) == false)
871 {
872 AddHttpValue(h, NewHttpValue(data->AdditionalHeaderName, data->AdditionalHeaderValue));
873 }
874
875 if (use_http_proxy)
876 {
877 AddHttpValue(h, NewHttpValue("Proxy-Connection", "Keep-Alive"));
878
879 if (IsEmptyStr(setting->ProxyUsername) == false || IsEmptyStr(setting->ProxyPassword) == false)
880 {
881 char auth_tmp_str[MAX_SIZE], auth_b64_str[MAX_SIZE * 2];
882 char basic_str[MAX_SIZE * 2];
883
884 // Generate the authentication string
885 Format(auth_tmp_str, sizeof(auth_tmp_str), "%s:%s",
886 setting->ProxyUsername, setting->ProxyPassword);
887
888 // Base64 encode
889 Zero(auth_b64_str, sizeof(auth_b64_str));
890 Encode64(auth_b64_str, auth_tmp_str);
891 Format(basic_str, sizeof(basic_str), "Basic %s", auth_b64_str);
892
893 AddHttpValue(h, NewHttpValue("Proxy-Authorization", basic_str));
894 }
895 }
896
897 send_str = HttpHeaderToStr(h);
898 FreeHttpHeader(h);
899
900 send_buf = NewBuf();
901 WriteBuf(send_buf, send_str, StrLen(send_str));
902 Free(send_str);
903
904 // Append to the sending data in the case of POST
905 if (StrCmpi(data->Method, WPC_HTTP_POST_NAME) == 0)
906 {
907 WriteBuf(send_buf, post_data, StrLen(post_data));
908 }
909
910 // Send
911 if (SendAll(s, send_buf->Buf, send_buf->Size, s->SecureMode) == false)
912 {
913 Disconnect(s);
914 ReleaseSock(s);
915 FreeBuf(send_buf);
916
917 *error_code = ERR_DISCONNECTED;
918
919 return NULL;
920 }
921
922 FreeBuf(send_buf);
923
924 CONT:
925 // Receive
926 h = RecvHttpHeader(s);
927 if (h == NULL)
928 {
929 Disconnect(s);
930 ReleaseSock(s);
931
932 *error_code = ERR_DISCONNECTED;
933
934 return NULL;
935 }
936
937 http_error_code = 0;
938 if (StrLen(h->Method) == 8)
939 {
940 if (Cmp(h->Method, "HTTP/1.", 7) == 0)
941 {
942 http_error_code = ToInt(h->Target);
943 }
944 }
945
946 *error_code = ERR_NO_ERROR;
947
948 switch (http_error_code)
949 {
950 case 401:
951 case 407:
952 // Proxy authentication error
953 *error_code = ERR_PROXY_AUTH_FAILED;
954 break;
955
956 case 404:
957 // 404 File Not Found
958 *error_code = ERR_OBJECT_NOT_FOUND;
959 break;
960
961 case 100:
962 // Continue
963 num_continue++;
964 if (num_continue >= 10)
965 {
966 goto DEF;
967 }
968 FreeHttpHeader(h);
969 goto CONT;
970
971 case 200:
972 // Success
973 break;
974
975 default:
976 // Protocol error
977 DEF:
978 *error_code = ERR_PROTOCOL_ERROR;
979 break;
980 }
981
982 if (*error_code != ERR_NO_ERROR)
983 {
984 // An error has occured
985 Disconnect(s);
986 ReleaseSock(s);
987 FreeHttpHeader(h);
988 return NULL;
989 }
990
991 // Get the length of the content
992 content_len = GetContentLength(h);
993 if (max_recv_size != 0)
994 {
995 content_len = MIN(content_len, max_recv_size);
996 }
997
998 FreeHttpHeader(h);
999
1000 socket_buffer = Malloc(socket_buffer_size);
1001
1002 // Receive the content
1003 recv_buf = NewBuf();
1004
1005 while (true)
1006 {
1007 UINT recvsize = MIN(socket_buffer_size, content_len - recv_buf->Size);
1008 UINT size;
1009
1010 if (recv_callback != NULL)
1011 {
1012 if (recv_callback(recv_callback_param,
1013 content_len, recv_buf->Size, recv_buf) == false)
1014 {
1015 // Cancel the reception
1016 *error_code = ERR_USER_CANCEL;
1017 goto RECV_CANCEL;
1018 }
1019 }
1020
1021 if (recvsize == 0)
1022 {
1023 break;
1024 }
1025
1026 size = Recv(s, socket_buffer, recvsize, s->SecureMode);
1027 if (size == 0)
1028 {
1029 // Disconnected
1030 *error_code = ERR_DISCONNECTED;
1031
1032 RECV_CANCEL:
1033 FreeBuf(recv_buf);
1034 Free(socket_buffer);
1035 Disconnect(s);
1036 ReleaseSock(s);
1037
1038 return NULL;
1039 }
1040
1041 WriteBuf(recv_buf, socket_buffer, size);
1042 }
1043
1044 SeekBuf(recv_buf, 0, 0);
1045 Free(socket_buffer);
1046
1047 Disconnect(s);
1048 ReleaseSock(s);
1049
1050 // Transmission
1051 return recv_buf;
1052 }
1053
1054 // Get the proxy server settings from the registry string of IE
GetProxyServerNameAndPortFromIeProxyRegStr(char * name,UINT name_size,UINT * port,char * str,char * server_type)1055 bool GetProxyServerNameAndPortFromIeProxyRegStr(char *name, UINT name_size, UINT *port, char *str, char *server_type)
1056 {
1057 #ifdef OS_WIN32
1058 TOKEN_LIST *t;
1059 UINT i;
1060 bool ret = false;
1061 // Validate arguments
1062 if (name == NULL || port == NULL || str == NULL || server_type == NULL)
1063 {
1064 return false;
1065 }
1066
1067 t = ParseToken(str, ";");
1068
1069 for (i = 0;i < t->NumTokens;i++)
1070 {
1071 char *s = t->Token[i];
1072 UINT i;
1073
1074 Trim(s);
1075
1076 i = SearchStrEx(s, "=", 0, false);
1077 if (i != INFINITE)
1078 {
1079 char tmp[MAX_PATH];
1080
1081 StrCpy(name, name_size, s);
1082 name[i] = 0;
1083
1084 if (StrCmpi(name, server_type) == 0)
1085 {
1086 char *host;
1087 StrCpy(tmp, sizeof(tmp), s + i + 1);
1088
1089 if (ParseHostPort(tmp, &host, port, 0))
1090 {
1091 StrCpy(name, name_size, host);
1092 Free(host);
1093
1094 if (*port != 0)
1095 {
1096 ret = true;
1097 }
1098 break;
1099 }
1100 }
1101 }
1102 }
1103
1104 FreeToken(t);
1105
1106 return ret;
1107 #else // OS_WIN32
1108 return true;
1109 #endif // OS_WIN32
1110 }
1111
1112 // Get the internet connection settings of the system
GetSystemInternetSetting(INTERNET_SETTING * setting)1113 void GetSystemInternetSetting(INTERNET_SETTING *setting)
1114 {
1115 #ifdef OS_WIN32
1116 bool use_proxy;
1117 // Validate arguments
1118 if (setting == NULL)
1119 {
1120 return;
1121 }
1122
1123 Zero(setting, sizeof(INTERNET_SETTING));
1124
1125 use_proxy = MsRegReadInt(REG_CURRENT_USER,
1126 "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
1127 "ProxyEnable");
1128
1129 if (use_proxy)
1130 {
1131 char *str = MsRegReadStr(REG_CURRENT_USER,
1132 "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
1133 "ProxyServer");
1134 if (str != NULL)
1135 {
1136 char name[MAX_HOST_NAME_LEN + 1];
1137 UINT port;
1138
1139 if (GetProxyServerNameAndPortFromIeProxyRegStr(name, sizeof(name),
1140 &port, str, "https"))
1141 {
1142 setting->ProxyType = PROXY_HTTP;
1143 StrCpy(setting->ProxyHostName, sizeof(setting->ProxyHostName), name);
1144 setting->ProxyPort = port;
1145 }
1146 else if (GetProxyServerNameAndPortFromIeProxyRegStr(name, sizeof(name),
1147 &port, str, "http"))
1148 {
1149 setting->ProxyType = PROXY_HTTP;
1150 StrCpy(setting->ProxyHostName, sizeof(setting->ProxyHostName), name);
1151 setting->ProxyPort = port;
1152 }
1153 else if (GetProxyServerNameAndPortFromIeProxyRegStr(name, sizeof(name),
1154 &port, str, "socks"))
1155 {
1156 setting->ProxyType = PROXY_SOCKS;
1157 StrCpy(setting->ProxyHostName, sizeof(setting->ProxyHostName), name);
1158 setting->ProxyPort = port;
1159 }
1160 else
1161 {
1162 if (SearchStrEx(str, "=", 0, false) == INFINITE)
1163 {
1164 char *host;
1165 UINT port;
1166 if (ParseHostPort(str, &host, &port, 0))
1167 {
1168 if (port != 0)
1169 {
1170 setting->ProxyType = PROXY_HTTP;
1171 StrCpy(setting->ProxyHostName, sizeof(setting->ProxyHostName), host);
1172 setting->ProxyPort = port;
1173 }
1174 Free(host);
1175 }
1176 }
1177 }
1178
1179 Free(str);
1180 }
1181 }
1182 #else // OS_WIN32
1183 Zero(setting, sizeof(INTERNET_SETTING));
1184 #endif // OS_WIN32
1185 }
1186
1187 // Generate the URL
CreateUrl(char * url,UINT url_size,URL_DATA * data)1188 void CreateUrl(char *url, UINT url_size, URL_DATA *data)
1189 {
1190 char *protocol;
1191 // Validate arguments
1192 if (url == NULL || data == NULL)
1193 {
1194 return;
1195 }
1196
1197 if (data->Secure == false)
1198 {
1199 protocol = "http://";
1200 }
1201 else
1202 {
1203 protocol = "https://";
1204 }
1205
1206 Format(url, url_size, "%s%s%s", protocol, data->HeaderHostName, data->Target);
1207 }
1208
1209
1210 // Parse the URL
ParseUrl(URL_DATA * data,char * str,bool is_post,char * referrer)1211 bool ParseUrl(URL_DATA *data, char *str, bool is_post, char *referrer)
1212 {
1213 char tmp[MAX_SIZE * 3];
1214 char server_port[MAX_HOST_NAME_LEN + 16];
1215 char *s = NULL;
1216 char *host;
1217 UINT port;
1218 UINT i;
1219 // Validate arguments
1220 if (data == NULL || str == NULL)
1221 {
1222 return false;
1223 }
1224
1225 Zero(data, sizeof(URL_DATA));
1226
1227 if (is_post)
1228 {
1229 StrCpy(data->Method, sizeof(data->Method), WPC_HTTP_POST_NAME);
1230 }
1231 else
1232 {
1233 StrCpy(data->Method, sizeof(data->Method), WPC_HTTP_GET_NAME);
1234 }
1235
1236 if (referrer != NULL)
1237 {
1238 StrCpy(data->Referer, sizeof(data->Referer), referrer);
1239 }
1240
1241 StrCpy(tmp, sizeof(tmp), str);
1242 Trim(tmp);
1243
1244 // Determine the protocol
1245 if (StartWith(tmp, "http://"))
1246 {
1247 data->Secure = false;
1248 s = &tmp[7];
1249 }
1250 else if (StartWith(tmp, "https://"))
1251 {
1252 data->Secure = true;
1253 s = &tmp[8];
1254 }
1255 else
1256 {
1257 if (SearchStrEx(tmp, "://", 0, false) != INFINITE)
1258 {
1259 return false;
1260 }
1261 data->Secure = false;
1262 s = &tmp[0];
1263 }
1264
1265 // Get the "server name:port number"
1266 StrCpy(server_port, sizeof(server_port), s);
1267 i = SearchStrEx(server_port, "/", 0, false);
1268 if (i != INFINITE)
1269 {
1270 server_port[i] = 0;
1271 s += StrLen(server_port);
1272 StrCpy(data->Target, sizeof(data->Target), s);
1273 }
1274 else
1275 {
1276 StrCpy(data->Target, sizeof(data->Target), "/");
1277 }
1278
1279 if (ParseHostPort(server_port, &host, &port, data->Secure ? 443 : 80) == false)
1280 {
1281 return false;
1282 }
1283
1284 StrCpy(data->HostName, sizeof(data->HostName), host);
1285 data->Port = port;
1286
1287 Free(host);
1288
1289 if ((data->Secure && data->Port == 443) || (data->Secure == false && data->Port == 80))
1290 {
1291 StrCpy(data->HeaderHostName, sizeof(data->HeaderHostName), data->HostName);
1292 }
1293 else
1294 {
1295 Format(data->HeaderHostName, sizeof(data->HeaderHostName),
1296 "%s:%u", data->HostName, data->Port);
1297 }
1298
1299 return true;
1300 }
1301
1302 // String replacement
Base64ToSafe64(char * str)1303 void Base64ToSafe64(char *str)
1304 {
1305 UINT i, len;
1306 // Validate arguments
1307 if (str == NULL)
1308 {
1309 return;
1310 }
1311
1312 len = StrLen(str);
1313
1314 for (i = 0;i < len;i++)
1315 {
1316 switch (str[i])
1317 {
1318 case '=':
1319 str[i] = '(';
1320 break;
1321
1322 case '+':
1323 str[i] = ')';
1324 break;
1325
1326 case '/':
1327 str[i] = '_';
1328 break;
1329 }
1330 }
1331 }
Safe64ToBase64(char * str)1332 void Safe64ToBase64(char *str)
1333 {
1334 UINT i, len;
1335 // Validate arguments
1336 if (str == NULL)
1337 {
1338 return;
1339 }
1340
1341 len = StrLen(str);
1342
1343 for (i = 0;i < len;i++)
1344 {
1345 switch (str[i])
1346 {
1347 case '(':
1348 str[i] = '=';
1349 break;
1350
1351 case ')':
1352 str[i] = '+';
1353 break;
1354
1355 case '_':
1356 str[i] = '/';
1357 break;
1358 }
1359 }
1360 }
1361
1362 // Decode from Safe64
DecodeSafe64(void * dst,char * src,UINT src_strlen)1363 UINT DecodeSafe64(void *dst, char *src, UINT src_strlen)
1364 {
1365 char *tmp;
1366 UINT ret;
1367 if (dst == NULL || src == NULL)
1368 {
1369 return 0;
1370 }
1371
1372 if (src_strlen == 0)
1373 {
1374 src_strlen = StrLen(src);
1375 }
1376
1377 tmp = Malloc(src_strlen + 1);
1378 Copy(tmp, src, src_strlen);
1379 tmp[src_strlen] = 0;
1380 Safe64ToBase64(tmp);
1381
1382 ret = B64_Decode(dst, tmp, src_strlen);
1383 Free(tmp);
1384
1385 return ret;
1386 }
1387
1388 // Encode to Safe64
EncodeSafe64(char * dst,void * src,UINT src_size)1389 void EncodeSafe64(char *dst, void *src, UINT src_size)
1390 {
1391 UINT size;
1392 if (dst == NULL || src == NULL)
1393 {
1394 return;
1395 }
1396
1397 size = B64_Encode(dst, src, src_size);
1398 dst[size] = 0;
1399
1400 Base64ToSafe64(dst);
1401 }
1402
1403