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(&param, 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, &param, 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, &param);
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