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 // AzureClient.c
103 // VPN Azure Client
104
105 #include "CedarPch.h"
106
107 // Wait for connection request
AcWaitForRequest(AZURE_CLIENT * ac,SOCK * s,AZURE_PARAM * param)108 void AcWaitForRequest(AZURE_CLIENT *ac, SOCK *s, AZURE_PARAM *param)
109 {
110 // Validate arguments
111 if (ac == NULL || s == NULL || param == NULL)
112 {
113 return;
114 }
115
116 while (ac->Halt == false)
117 {
118 UCHAR uc;
119
120 // Receive 1 byte
121 if (RecvAll(s, &uc, 1, false) == 0)
122 {
123 break;
124 }
125
126 if (uc != 0)
127 {
128 // Receive a Pack
129 PACK *p = RecvPackWithHash(s);
130
131 if (p == NULL)
132 {
133 break;
134 }
135 else
136 {
137 // Verify contents of Pack
138 char opcode[MAX_SIZE];
139 char cipher_name[MAX_SIZE];
140 char hostname[MAX_SIZE];
141
142 PackGetStr(p, "opcode", opcode, sizeof(opcode));
143 PackGetStr(p, "cipher_name", cipher_name, sizeof(cipher_name));
144 PackGetStr(p, "hostname", hostname, sizeof(hostname));
145
146 if (StrCmpi(opcode, "relay") == 0)
147 {
148 IP client_ip, server_ip;
149 UINT client_port;
150 UINT server_port;
151 UCHAR session_id[SHA1_SIZE];
152
153 if (PackGetIp(p, "client_ip", &client_ip) &&
154 PackGetIp(p, "server_ip", &server_ip) &&
155 PackGetData2(p, "session_id", session_id, sizeof(session_id)))
156 {
157 client_port = PackGetInt(p, "client_port");
158 server_port = PackGetInt(p, "server_port");
159
160 if (client_port != 0 && server_port != 0)
161 {
162 SOCK *ns;
163 Debug("Connect Request from %r:%u\n", &client_ip, client_port);
164
165 // Create new socket and connect VPN Azure Server
166 if (ac->DDnsStatusCopy.InternetSetting.ProxyType == PROXY_DIRECT)
167 {
168 ns = ConnectEx2(ac->DDnsStatusCopy.CurrentAzureIp, AZURE_SERVER_PORT,
169 0, (bool *)&ac->Halt);
170 }
171 else
172 {
173 ns = WpcSockConnect2(ac->DDnsStatusCopy.CurrentAzureIp, AZURE_SERVER_PORT,
174 &ac->DDnsStatusCopy.InternetSetting, NULL, AZURE_VIA_PROXY_TIMEOUT);
175 }
176
177 if (ns == NULL)
178 {
179 Debug("Connect Error.\n");
180 }
181 else
182 {
183 Debug("Connected to the relay server.\n");
184
185 SetTimeout(ns, param->DataTimeout);
186
187 if (StartSSLEx(ns, NULL, NULL, true, 0, NULL))
188 {
189 // Check certification
190 char server_cert_hash_str[MAX_SIZE];
191 UCHAR server_cert_hash[SHA1_SIZE];
192
193 Zero(server_cert_hash, sizeof(server_cert_hash));
194 GetXDigest(ns->RemoteX, server_cert_hash, true);
195
196 BinToStr(server_cert_hash_str, sizeof(server_cert_hash_str),
197 server_cert_hash, SHA1_SIZE);
198
199 if (IsEmptyStr(ac->DDnsStatusCopy.AzureCertHash) || StrCmpi(server_cert_hash_str, ac->DDnsStatusCopy.AzureCertHash) == 0
200 || StrCmpi(server_cert_hash_str, ac->DDnsStatus.AzureCertHash) == 0)
201 {
202 if (SendAll(ns, AZURE_PROTOCOL_DATA_SIANGTURE, 24, true))
203 {
204 PACK *p2 = NewPack();
205
206 PackAddStr(p2, "hostname", hostname);
207 PackAddData(p2, "session_id", session_id, sizeof(session_id));
208
209 if (SendPackWithHash(ns, p2))
210 {
211 UCHAR uc;
212
213 if (RecvAll(ns, &uc, 1, true) != false)
214 {
215 if (uc != 0)
216 {
217 SOCK *accept_sock = GetReverseListeningSock(ac->Cedar);
218
219 if (accept_sock != NULL)
220 {
221 AddRef(ns->ref);
222
223 SetTimeout(ns, INFINITE);
224
225 Copy(&ns->Reverse_MyServerGlobalIp, &server_ip, sizeof(IP));
226 ns->Reverse_MyServerPort = server_port;
227
228 InjectNewReverseSocketToAccept(accept_sock, ns,
229 &client_ip, client_port);
230
231 ReleaseSock(accept_sock);
232 }
233 }
234 }
235 }
236
237 FreePack(p2);
238 }
239 }
240 }
241
242 ReleaseSock(ns);
243 }
244 }
245 }
246 }
247
248 FreePack(p);
249 }
250 }
251
252 // Send 1 byte
253 uc = 0;
254 if (SendAll(s, &uc, 1, false) == 0)
255 {
256 break;
257 }
258 }
259 }
260
261 // VPN Azure client main thread
AcMainThread(THREAD * thread,void * param)262 void AcMainThread(THREAD *thread, void *param)
263 {
264 AZURE_CLIENT *ac = (AZURE_CLIENT *)param;
265 UINT last_ip_revision = INFINITE;
266 UINT64 last_reconnect_tick = 0;
267 UINT64 next_reconnect_interval = AZURE_CONNECT_INITIAL_RETRY_INTERVAL;
268 UINT num_reconnect_retry = 0;
269 UINT64 next_ddns_retry_tick = 0;
270 bool last_connect_ok = false;
271 // Validate arguments
272 if (ac == NULL || thread == NULL)
273 {
274 return;
275 }
276
277 while (ac->Halt == false)
278 {
279 UINT64 now = Tick64();
280 bool connect_was_ok = false;
281 // Wait for enabling VPN Azure function
282 if (ac->IsEnabled)
283 {
284 // VPN Azure is enabled
285 DDNS_CLIENT_STATUS st;
286 bool connect_now = false;
287 bool azure_ip_changed = false;
288
289 Lock(ac->Lock);
290 {
291 Copy(&st, &ac->DDnsStatus, sizeof(DDNS_CLIENT_STATUS));
292
293 if (StrCmpi(st.CurrentAzureIp, ac->DDnsStatusCopy.CurrentAzureIp) != 0)
294 {
295 if (IsEmptyStr(st.CurrentAzureIp) == false)
296 {
297 // Destination IP address is changed
298 connect_now = true;
299 num_reconnect_retry = 0;
300 }
301 }
302
303 if (StrCmpi(st.CurrentHostName, ac->DDnsStatusCopy.CurrentHostName) != 0)
304 {
305 // DDNS host name is changed
306 connect_now = true;
307 num_reconnect_retry = 0;
308 }
309
310 Copy(&ac->DDnsStatusCopy, &st, sizeof(DDNS_CLIENT_STATUS));
311 }
312 Unlock(ac->Lock);
313
314 if (last_ip_revision != ac->IpStatusRevision)
315 {
316 last_ip_revision = ac->IpStatusRevision;
317
318 connect_now = true;
319
320 num_reconnect_retry = 0;
321 }
322
323 if (last_reconnect_tick == 0 || (now >= (last_reconnect_tick + next_reconnect_interval)))
324 {
325 UINT r;
326
327 last_reconnect_tick = now;
328 num_reconnect_retry++;
329 next_reconnect_interval = (UINT64)num_reconnect_retry * AZURE_CONNECT_INITIAL_RETRY_INTERVAL;
330 next_reconnect_interval = MIN(next_reconnect_interval, AZURE_CONNECT_MAX_RETRY_INTERVAL);
331
332 r = (UINT)next_reconnect_interval;
333
334 r = GenRandInterval(r / 2, r);
335
336 next_reconnect_interval = r;
337
338 connect_now = true;
339 }
340
341 if (IsEmptyStr(st.CurrentAzureIp) == false && IsEmptyStr(st.CurrentHostName) == false)
342 {
343 if (connect_now)
344 {
345 SOCK *s;
346 char *host = NULL;
347 UINT port = AZURE_SERVER_PORT;
348
349 Debug("VPN Azure: Connecting to %s...\n", st.CurrentAzureIp);
350
351 if (ParseHostPort(st.CurrentAzureIp, &host, &port, AZURE_SERVER_PORT))
352 {
353 if (st.InternetSetting.ProxyType == PROXY_DIRECT)
354 {
355 s = ConnectEx2(host, port, 0, (bool *)&ac->Halt);
356 }
357 else
358 {
359 s = WpcSockConnect2(host, port, &st.InternetSetting, NULL, AZURE_VIA_PROXY_TIMEOUT);
360 }
361
362 if (s != NULL)
363 {
364 PACK *p;
365 UINT64 established_tick = 0;
366
367 Debug("VPN Azure: Connected.\n");
368
369 SetTimeout(s, AZURE_PROTOCOL_CONTROL_TIMEOUT_DEFAULT);
370
371 Lock(ac->Lock);
372 {
373 ac->CurrentSock = s;
374 ac->IsConnected = true;
375 StrCpy(ac->ConnectingAzureIp, sizeof(ac->ConnectingAzureIp), st.CurrentAzureIp);
376 }
377 Unlock(ac->Lock);
378
379 SendAll(s, AZURE_PROTOCOL_CONTROL_SIGNATURE, StrLen(AZURE_PROTOCOL_CONTROL_SIGNATURE), false);
380
381 // Receive parameter
382 p = RecvPackWithHash(s);
383 if (p != NULL)
384 {
385 UCHAR c;
386 AZURE_PARAM param;
387 bool hostname_changed = false;
388
389 Zero(¶m, sizeof(param));
390
391 param.ControlKeepAlive = PackGetInt(p, "ControlKeepAlive");
392 param.ControlTimeout = PackGetInt(p, "ControlTimeout");
393 param.DataTimeout = PackGetInt(p, "DataTimeout");
394 param.SslTimeout = PackGetInt(p, "SslTimeout");
395
396 FreePack(p);
397
398 param.ControlKeepAlive = MAKESURE(param.ControlKeepAlive, 1000, AZURE_SERVER_MAX_KEEPALIVE);
399 param.ControlTimeout = MAKESURE(param.ControlTimeout, 1000, AZURE_SERVER_MAX_TIMEOUT);
400 param.DataTimeout = MAKESURE(param.DataTimeout, 1000, AZURE_SERVER_MAX_TIMEOUT);
401 param.SslTimeout = MAKESURE(param.SslTimeout, 1000, AZURE_SERVER_MAX_TIMEOUT);
402
403 Lock(ac->Lock);
404 {
405 Copy(&ac->AzureParam, ¶m, sizeof(AZURE_PARAM));
406 }
407 Unlock(ac->Lock);
408
409 SetTimeout(s, param.ControlTimeout);
410
411 // Send parameter
412 p = NewPack();
413 PackAddStr(p, "CurrentHostName", st.CurrentHostName);
414 PackAddStr(p, "CurrentAzureIp", st.CurrentAzureIp);
415 PackAddInt64(p, "CurrentAzureTimestamp", st.CurrentAzureTimestamp);
416 PackAddStr(p, "CurrentAzureSignature", st.CurrentAzureSignature);
417
418 Lock(ac->Lock);
419 {
420 if (StrCmpi(st.CurrentHostName, ac->DDnsStatus.CurrentHostName) != 0)
421 {
422 hostname_changed = true;
423 }
424 }
425 Unlock(ac->Lock);
426
427 if (hostname_changed == false)
428 {
429 if (SendPackWithHash(s, p))
430 {
431 // Receive result
432 if (RecvAll(s, &c, 1, false))
433 {
434 if (c && ac->Halt == false)
435 {
436 connect_was_ok = true;
437
438 established_tick = Tick64();
439
440 AcWaitForRequest(ac, s, ¶m);
441 }
442 }
443 }
444 }
445
446 FreePack(p);
447 }
448 else
449 {
450 WHERE;
451 }
452
453 Debug("VPN Azure: Disconnected.\n");
454
455 Lock(ac->Lock);
456 {
457 ac->IsConnected = false;
458 ac->CurrentSock = NULL;
459 ClearStr(ac->ConnectingAzureIp, sizeof(ac->ConnectingAzureIp));
460 }
461 Unlock(ac->Lock);
462
463 if (established_tick != 0)
464 {
465 if ((established_tick + (UINT64)AZURE_CONNECT_MAX_RETRY_INTERVAL) <= Tick64())
466 {
467 // If the connected time exceeds the AZURE_CONNECT_MAX_RETRY_INTERVAL, reset the retry counter.
468 last_reconnect_tick = 0;
469 num_reconnect_retry = 0;
470 next_reconnect_interval = AZURE_CONNECT_INITIAL_RETRY_INTERVAL;
471 }
472 }
473
474 Disconnect(s);
475 ReleaseSock(s);
476 }
477 else
478 {
479 Debug("VPN Azure: Error: Connect Failed.\n");
480 }
481
482 Free(host);
483 }
484 }
485 }
486 }
487 else
488 {
489 last_reconnect_tick = 0;
490 num_reconnect_retry = 0;
491 next_reconnect_interval = AZURE_CONNECT_INITIAL_RETRY_INTERVAL;
492 }
493
494 if (ac->Halt)
495 {
496 break;
497 }
498
499 if (connect_was_ok)
500 {
501 // If connection goes out after connected, increment connection success count to urge DDNS client query
502 next_ddns_retry_tick = Tick64() + MIN((UINT64)DDNS_VPN_AZURE_CONNECT_ERROR_DDNS_RETRY_TIME_DIFF * (UINT64)(num_reconnect_retry + 1), (UINT64)DDNS_VPN_AZURE_CONNECT_ERROR_DDNS_RETRY_TIME_DIFF_MAX);
503 }
504
505 if ((next_ddns_retry_tick != 0) && (Tick64() >= next_ddns_retry_tick))
506 {
507 next_ddns_retry_tick = 0;
508
509 ac->DDnsTriggerInt++;
510 }
511
512 Wait(ac->Event, rand() % 1000);
513 }
514 }
515
516 // Get enabled or disabled VPN Azure client
AcGetEnable(AZURE_CLIENT * ac)517 bool AcGetEnable(AZURE_CLIENT *ac)
518 {
519 // Validate arguments
520 if (ac == NULL)
521 {
522 return false;
523 }
524
525 return ac->IsEnabled;
526 }
527
528 // Enable or disable VPN Azure client
AcSetEnable(AZURE_CLIENT * ac,bool enabled)529 void AcSetEnable(AZURE_CLIENT *ac, bool enabled)
530 {
531 bool old_status;
532 // Validate arguments
533 if (ac == NULL)
534 {
535 return;
536 }
537
538 old_status = ac->IsEnabled;
539
540 ac->IsEnabled = enabled;
541
542 if (ac->IsEnabled && (ac->IsEnabled != old_status))
543 {
544 ac->DDnsTriggerInt++;
545 }
546
547 AcApplyCurrentConfig(ac, NULL);
548 }
549
550 // Set current configuration to VPN Azure client
AcApplyCurrentConfig(AZURE_CLIENT * ac,DDNS_CLIENT_STATUS * ddns_status)551 void AcApplyCurrentConfig(AZURE_CLIENT *ac, DDNS_CLIENT_STATUS *ddns_status)
552 {
553 bool disconnect_now = false;
554 SOCK *disconnect_sock = NULL;
555 // Validate arguments
556 if (ac == NULL)
557 {
558 return;
559 }
560
561 // Get current DDNS configuration
562 Lock(ac->Lock);
563 {
564 if (ddns_status != NULL)
565 {
566 if (StrCmpi(ac->DDnsStatus.CurrentHostName, ddns_status->CurrentHostName) != 0)
567 {
568 // If host name is changed, disconnect current data connection
569 disconnect_now = true;
570 }
571
572 if (Cmp(&ac->DDnsStatus.InternetSetting, &ddns_status->InternetSetting, sizeof(INTERNET_SETTING)) != 0)
573 {
574 // If proxy setting is changed, disconnect current data connection
575 disconnect_now = true;
576 }
577
578 Copy(&ac->DDnsStatus, ddns_status, sizeof(DDNS_CLIENT_STATUS));
579 }
580
581 if (ac->IsEnabled == false)
582 {
583 // If VPN Azure client is disabled, disconnect current data connection
584 disconnect_now = true;
585 }
586
587 if (disconnect_now)
588 {
589 if (ac->CurrentSock != NULL)
590 {
591 disconnect_sock = ac->CurrentSock;
592 AddRef(disconnect_sock->ref);
593 }
594 }
595 }
596 Unlock(ac->Lock);
597
598 if (disconnect_sock != NULL)
599 {
600 Disconnect(disconnect_sock);
601 ReleaseSock(disconnect_sock);
602 }
603
604 Set(ac->Event);
605 }
606
607 // Free VPN Azure client
FreeAzureClient(AZURE_CLIENT * ac)608 void FreeAzureClient(AZURE_CLIENT *ac)
609 {
610 SOCK *disconnect_sock = NULL;
611 // Validate arguments
612 if (ac == NULL)
613 {
614 return;
615 }
616
617 ac->Halt = true;
618
619 Lock(ac->Lock);
620 {
621 if (ac->CurrentSock != NULL)
622 {
623 disconnect_sock = ac->CurrentSock;
624
625 AddRef(disconnect_sock->ref);
626 }
627 }
628 Unlock(ac->Lock);
629
630 if (disconnect_sock != NULL)
631 {
632 Disconnect(disconnect_sock);
633 ReleaseSock(disconnect_sock);
634 }
635
636 Set(ac->Event);
637
638 // Stop main thread
639 WaitThread(ac->MainThread, INFINITE);
640 ReleaseThread(ac->MainThread);
641
642 ReleaseEvent(ac->Event);
643
644 DeleteLock(ac->Lock);
645
646 Free(ac);
647 }
648
649 // Create new VPN Azure client
NewAzureClient(CEDAR * cedar,SERVER * server)650 AZURE_CLIENT *NewAzureClient(CEDAR *cedar, SERVER *server)
651 {
652 AZURE_CLIENT *ac;
653 // Validate arguments
654 if (cedar == NULL || server == NULL)
655 {
656 return NULL;
657 }
658
659 ac = ZeroMalloc(sizeof(AZURE_CLIENT));
660
661 ac->Cedar = cedar;
662
663 ac->Server = server;
664
665 ac->Lock = NewLock();
666
667 ac->IsEnabled = false;
668
669 ac->Event = NewEvent();
670
671 // Start main thread
672 ac->MainThread = NewThread(AcMainThread, ac);
673
674 return ac;
675 }
676
677