OpenVPN
openvpnmsica.c
Go to the documentation of this file.
1/*
2 * openvpnmsica -- Custom Action DLL to provide OpenVPN-specific support to MSI packages
3 * https://community.openvpn.net/openvpn/wiki/OpenVPNMSICA
4 *
5 * Copyright (C) 2018-2026 Simon Rozman <simon@rozman.si>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2
9 * as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, see <https://www.gnu.org/licenses/>.
18 */
19
20#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23#include <winsock2.h> /* Must be included _before_ <windows.h> */
24
25#include "openvpnmsica.h"
26#include "msica_arg.h"
27#include "msiex.h"
28
29#include "../tapctl/basic.h"
30#include "../tapctl/error.h"
31#include "../tapctl/tap.h"
32
33#include <windows.h>
34#include <iphlpapi.h>
35#include <malloc.h>
36#include <memory.h>
37#include <msiquery.h>
38#include <shellapi.h>
39#include <shlwapi.h>
40#include <stdbool.h>
41#include <stdlib.h>
42#include <wchar.h>
43#include <setupapi.h>
44#include <newdev.h>
45#include <initguid.h>
46#include <devguid.h>
47
48#ifdef _MSC_VER
49#pragma comment(lib, "advapi32.lib")
50#pragma comment(lib, "iphlpapi.lib")
51#pragma comment(lib, "shell32.lib")
52#pragma comment(lib, "shlwapi.lib")
53#pragma comment(lib, "version.lib")
54#endif
55
56
62#define MSICA_ADAPTER_TICK_SIZE (16 * 1024)
63
64#define FILE_NEED_REBOOT L".ovpn_need_reboot"
65
66#define OPENVPN_CONNECT_ADAPTER_SUBSTR L"OpenVPN Connect"
67
79static UINT
80setup_sequence(_In_ MSIHANDLE hInstall, _In_z_ LPCWSTR szProperty, _In_ struct msica_arg_seq *seq)
81{
82 UINT uiResult;
83 LPWSTR szSequence = msica_arg_seq_join(seq);
84 uiResult = MsiSetProperty(hInstall, szProperty, szSequence);
85 free(szSequence);
86 if (uiResult != ERROR_SUCCESS)
87 {
88 /* MSDN does not mention MsiSetProperty() to set GetLastError(). But we do have an error
89 * code. Set last error manually. */
90 SetLastError(uiResult);
91 msg(M_NONFATAL | M_ERRNO, "%s: MsiSetProperty(\"%ls\") failed", __FUNCTION__, szProperty);
92 return uiResult;
93 }
94 return ERROR_SUCCESS;
95}
96
97
98#ifdef _DEBUG
99
107static void
108_debug_popup(_In_z_ LPCSTR szFunctionName)
109{
110 WCHAR szTitle[0x100], szMessage[0x100 + MAX_PATH], szProcessPath[MAX_PATH];
111
112 /* Compose pop-up title. The dialog title will contain function name to ease the process
113 * locating. Mind that Visual Studio displays window titles on the process list. */
114 swprintf_s(szTitle, _countof(szTitle), L"%hs v%ls", szFunctionName, _L(PACKAGE_VERSION));
115
116 /* Get process name. */
117 GetModuleFileName(NULL, szProcessPath, _countof(szProcessPath));
118 LPCWSTR szProcessName = wcsrchr(szProcessPath, L'\\');
119 szProcessName = szProcessName ? szProcessName + 1 : szProcessPath;
120
121 /* Compose the pop-up message. */
122 swprintf_s(
123 szMessage, _countof(szMessage),
124 L"The %ls process (PID: %u) has started to execute the %hs"
125 L" custom action.\r\n"
126 L"\r\n"
127 L"If you would like to debug the custom action, attach a debugger to this process and set breakpoints before dismissing this dialog.\r\n"
128 L"\r\n"
129 L"If you are not debugging this custom action, you can safely ignore this message.",
130 szProcessName, GetCurrentProcessId(), szFunctionName);
131
132 MessageBox(NULL, szMessage, szTitle, MB_OK);
133}
134
135#define debug_popup(f) _debug_popup(f)
136#else /* ifdef _DEBUG */
137#define debug_popup(f)
138#endif /* ifdef _DEBUG */
139
140static void
141find_adapters(_In_ MSIHANDLE hInstall, _In_z_ LPCWSTR szzHardwareIDs,
142 _In_z_ LPCWSTR szAdaptersPropertyName, _In_z_ LPCWSTR szActiveAdaptersPropertyName)
143{
144 UINT uiResult;
145
146 /* Get network adapters with given hardware ID. */
147 struct tap_adapter_node *pAdapterList = NULL;
148 uiResult = tap_list_adapters(NULL, szzHardwareIDs, &pAdapterList);
149 if (uiResult != ERROR_SUCCESS)
150 {
151 return;
152 }
153 else if (pAdapterList == NULL)
154 {
155 /* No adapters - no fun. */
156 return;
157 }
158
159 /* Get IPv4/v6 info for all network adapters. Actually, we're interested in link status only:
160 * up/down? */
161 PIP_ADAPTER_ADDRESSES pAdapterAdresses = NULL;
162 ULONG ulAdapterAdressesSize = 16 * 1024;
163 for (size_t iteration = 0; iteration < 2; iteration++)
164 {
165 pAdapterAdresses = (PIP_ADAPTER_ADDRESSES)malloc(ulAdapterAdressesSize);
166 if (pAdapterAdresses == NULL)
167 {
168 msg(M_NONFATAL, "%s: malloc(%lu) failed", __FUNCTION__, ulAdapterAdressesSize);
169 uiResult = ERROR_OUTOFMEMORY;
170 goto cleanup_pAdapterList;
171 }
172
173 ULONG ulResult = GetAdaptersAddresses(
174 AF_UNSPEC,
175 GAA_FLAG_SKIP_UNICAST | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST
176 | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME
177 | GAA_FLAG_INCLUDE_ALL_INTERFACES,
178 NULL, pAdapterAdresses, &ulAdapterAdressesSize);
179
180 if (ulResult == ERROR_SUCCESS)
181 {
182 break;
183 }
184
185 free(pAdapterAdresses);
186 if (ulResult != ERROR_BUFFER_OVERFLOW)
187 {
188 /* MSDN does not mention GetAdaptersAddresses() to set GetLastError().
189 * But we do have an error code. Set last error manually. */
190 SetLastError(ulResult);
191 msg(M_NONFATAL | M_ERRNO, "%s: GetAdaptersAddresses() failed", __FUNCTION__);
192 uiResult = ulResult;
193 goto cleanup_pAdapterList;
194 }
195 }
196
197 /* Count adapters. */
198 size_t adapter_count = 0;
199 for (const struct tap_adapter_node *pAdapter = pAdapterList; pAdapter; pAdapter = pAdapter->pNext)
200 {
201 adapter_count++;
202 }
203
204 /* Prepare semicolon delimited list of TAP adapter ID(s) and active TAP adapter ID(s). */
205 LPWSTR
206 szAdapters =
207 (LPWSTR)malloc(adapter_count * (38 /*GUID*/ + 1 /*separator/terminator*/) * sizeof(WCHAR)),
208 szAdaptersTail = szAdapters;
209 if (szAdapters == NULL)
210 {
211 msg(M_FATAL, "%s: malloc(%zu) failed", __FUNCTION__,
212 adapter_count * (38 /*GUID*/ + 1 /*separator/terminator*/) * sizeof(WCHAR));
213 uiResult = ERROR_OUTOFMEMORY;
214 goto cleanup_pAdapterAdresses;
215 }
216
217 LPWSTR
218 szAdaptersActive =
219 (LPWSTR)malloc(adapter_count * (38 /*GUID*/ + 1 /*separator/terminator*/) * sizeof(WCHAR)),
220 szAdaptersActiveTail = szAdaptersActive;
221 if (szAdaptersActive == NULL)
222 {
223 msg(M_FATAL, "%s: malloc(%zu) failed", __FUNCTION__,
224 adapter_count * (38 /*GUID*/ + 1 /*separator/terminator*/) * sizeof(WCHAR));
225 uiResult = ERROR_OUTOFMEMORY;
226 goto cleanup_szAdapters;
227 }
228
229 for (struct tap_adapter_node *pAdapter = pAdapterList; pAdapter; pAdapter = pAdapter->pNext)
230 {
231 /* exclude adapters created by OpenVPN Connect, since they're removed on Connect
232 * uninstallation */
233 if (wcsstr(pAdapter->szName, OPENVPN_CONNECT_ADAPTER_SUBSTR))
234 {
235 msg(M_WARN, "%s: skip OpenVPN Connect adapter '%ls'", __FUNCTION__, pAdapter->szName);
236 continue;
237 }
238
239 /* Convert adapter GUID to UTF-16 string. (LPOLESTR defaults to LPWSTR) */
240 LPOLESTR szAdapterId = NULL;
241 StringFromIID((REFIID)&pAdapter->guid, &szAdapterId);
242
243 /* Append to the list of TAP adapter ID(s). */
244 if (szAdapters < szAdaptersTail)
245 {
246 *(szAdaptersTail++) = L';';
247 }
248 memcpy(szAdaptersTail, szAdapterId, 38 * sizeof(WCHAR));
249 szAdaptersTail += 38;
250
251 /* If this adapter is active (connected), add it to the list of active TAP adapter ID(s). */
252 for (PIP_ADAPTER_ADDRESSES p = pAdapterAdresses; p; p = p->Next)
253 {
254 OLECHAR szId[38 /*GUID*/ + 1 /*terminator*/];
255 GUID guid;
256 if (MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, p->AdapterName, -1, szId,
257 _countof(szId))
258 > 0
259 && SUCCEEDED(IIDFromString(szId, &guid))
260 && memcmp(&guid, &pAdapter->guid, sizeof(GUID)) == 0)
261 {
262 if (p->OperStatus == IfOperStatusUp)
263 {
264 /* This TAP adapter is active (connected). */
265 if (szAdaptersActive < szAdaptersActiveTail)
266 {
267 *(szAdaptersActiveTail++) = L';';
268 }
269 memcpy(szAdaptersActiveTail, szAdapterId, 38 * sizeof(WCHAR));
270 szAdaptersActiveTail += 38;
271 }
272 break;
273 }
274 }
275 CoTaskMemFree(szAdapterId);
276 }
277 szAdaptersTail[0] = 0;
278 szAdaptersActiveTail[0] = 0;
279
280 /* Set Installer properties. */
281 uiResult = MsiSetProperty(hInstall, szAdaptersPropertyName, szAdapters);
282 if (uiResult != ERROR_SUCCESS)
283 {
284 SetLastError(uiResult); /* MSDN does not mention MsiSetProperty() to set GetLastError(). But
285 we do have an error code. Set last error manually. */
286 msg(M_NONFATAL | M_ERRNO, "%s: MsiSetProperty(\"%s\") failed", __FUNCTION__,
287 szAdaptersPropertyName);
288 goto cleanup_szAdaptersActive;
289 }
290 uiResult = MsiSetProperty(hInstall, szActiveAdaptersPropertyName, szAdaptersActive);
291 if (uiResult != ERROR_SUCCESS)
292 {
293 /* MSDN does not mention MsiSetProperty() to set GetLastError(). But
294 * we do have an error code. Set last error manually. */
295 SetLastError(uiResult);
296 msg(M_NONFATAL | M_ERRNO, "%s: MsiSetProperty(\"%s\") failed", __FUNCTION__,
297 szActiveAdaptersPropertyName);
298 goto cleanup_szAdaptersActive;
299 }
300
301cleanup_szAdaptersActive:
302 free(szAdaptersActive);
303cleanup_szAdapters:
304 free(szAdapters);
305cleanup_pAdapterAdresses:
306 free(pAdapterAdresses);
307cleanup_pAdapterList:
308 tap_free_adapter_list(pAdapterList);
309}
310
311
312UINT __stdcall
313FindSystemInfo(_In_ MSIHANDLE hInstall)
314{
315#ifdef DLLEXP_EXPORT
316#pragma comment(linker, DLLEXP_EXPORT)
317#endif
318
319 debug_popup(__FUNCTION__);
320
321 BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL));
322
324
325 find_adapters(hInstall, L"root\\" _L(TAP_WIN_COMPONENT_ID) L"\0" _L(TAP_WIN_COMPONENT_ID) L"\0",
326 L"TAPWINDOWS6ADAPTERS", L"ACTIVETAPWINDOWS6ADAPTERS");
327 find_adapters(hInstall,
328 L"ovpn-dco"
329 L"\0",
330 L"OVPNDCOADAPTERS", L"ACTIVEOVPNDCOADAPTERS");
331
332 if (bIsCoInitialized)
333 {
334 CoUninitialize();
335 }
336 return ERROR_SUCCESS;
337}
338
339
340UINT __stdcall
341CloseOpenVPNGUI(_In_ MSIHANDLE hInstall)
342{
343#ifdef DLLEXP_EXPORT
344#pragma comment(linker, DLLEXP_EXPORT)
345#endif
346 UNREFERENCED_PARAMETER(hInstall); /* This CA is does not interact with MSI session (report
347 errors, access properties, tables, etc.). */
348
349 debug_popup(__FUNCTION__);
350
351 /* Find OpenVPN GUI window. */
352 HWND hWnd = FindWindow(L"OpenVPN-GUI", NULL);
353 if (hWnd)
354 {
355 /* Ask it to close and wait for 100ms. Unfortunately, this will succeed only for recent
356 * OpenVPN GUI that do not run elevated. */
357 SendMessage(hWnd, WM_CLOSE, 0, 0);
358 Sleep(100);
359 }
360
361 return ERROR_SUCCESS;
362}
363
364
365UINT __stdcall
366StartOpenVPNGUI(_In_ MSIHANDLE hInstall)
367{
368#ifdef DLLEXP_EXPORT
369#pragma comment(linker, DLLEXP_EXPORT)
370#endif
371
372 debug_popup(__FUNCTION__);
373
374 UINT uiResult;
375 BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL));
376
378
379 /* Create and populate a MSI record. */
380 MSIHANDLE hRecord = MsiCreateRecord(1);
381 if (!hRecord)
382 {
383 uiResult = ERROR_INVALID_HANDLE;
384 msg(M_NONFATAL, "%s: MsiCreateRecord failed", __FUNCTION__);
385 goto cleanup_CoInitialize;
386 }
387 uiResult = MsiRecordSetString(hRecord, 0, L"\"[#bin.openvpn_gui.exe]\"");
388 if (uiResult != ERROR_SUCCESS)
389 {
390 /* MSDN does not mention MsiRecordSetString() to set GetLastError().
391 * But we do have an error code. Set last error manually. */
392 SetLastError(uiResult);
393 msg(M_NONFATAL | M_ERRNO, "%s: MsiRecordSetString failed", __FUNCTION__);
394 goto cleanup_MsiCreateRecord;
395 }
396
397 /* Format string. */
398 WCHAR szStackBuf[MAX_PATH];
399 DWORD dwPathSize = _countof(szStackBuf);
400 LPWSTR szPath = szStackBuf;
401 uiResult = MsiFormatRecord(hInstall, hRecord, szPath, &dwPathSize);
402 if (uiResult == ERROR_MORE_DATA)
403 {
404 /* Allocate buffer on heap (+1 for terminator), and retry. */
405 szPath = (LPWSTR)malloc((++dwPathSize) * sizeof(WCHAR));
406 if (szPath == NULL)
407 {
408 msg(M_FATAL, "%s: malloc(%zu) failed", __FUNCTION__, dwPathSize * sizeof(WCHAR));
409 uiResult = ERROR_OUTOFMEMORY;
410 goto cleanup_MsiCreateRecord;
411 }
412
413 uiResult = MsiFormatRecord(hInstall, hRecord, szPath, &dwPathSize);
414 }
415 if (uiResult != ERROR_SUCCESS)
416 {
417 /* MSDN does not mention MsiFormatRecord() to set GetLastError().
418 * But we do have an error code. Set last error manually. */
419 SetLastError(uiResult);
420 msg(M_NONFATAL | M_ERRNO, "%s: MsiFormatRecord failed", __FUNCTION__);
421 goto cleanup_malloc_szPath;
422 }
423
424 /* Launch the OpenVPN GUI. */
425 SHELLEXECUTEINFO sei = { .cbSize = sizeof(SHELLEXECUTEINFO),
426 /* Don't show error UI, we'll display it. */
427 .fMask = SEE_MASK_FLAG_NO_UI,
428 .lpFile = szPath,
429 .nShow = SW_SHOWNORMAL };
430 if (!ShellExecuteEx(&sei))
431 {
432 uiResult = GetLastError();
433 msg(M_NONFATAL | M_ERRNO, "%s: ShellExecuteEx(%s) failed", __FUNCTION__, szPath);
434 goto cleanup_malloc_szPath;
435 }
436
437 uiResult = ERROR_SUCCESS;
438
439cleanup_malloc_szPath:
440 if (szPath != szStackBuf)
441 {
442 free(szPath);
443 }
444cleanup_MsiCreateRecord:
445 MsiCloseHandle(hRecord);
446cleanup_CoInitialize:
447 if (bIsCoInitialized)
448 {
449 CoUninitialize();
450 }
451 return uiResult;
452}
453
454
475static DWORD
477 _Inout_opt_ struct msica_arg_seq *seqRollback, _In_z_ LPCWSTR szDisplayName,
478 _In_z_ LPCWSTR szHardwareId, _Inout_ int *iTicks)
479{
480 /* Get existing network adapters. */
481 struct tap_adapter_node *pAdapterList = NULL;
482 DWORD dwResult = tap_list_adapters(NULL, NULL, &pAdapterList);
483 if (dwResult != ERROR_SUCCESS)
484 {
485 return dwResult;
486 }
487
488 /* Does adapter exist? */
489 for (struct tap_adapter_node *pAdapterOther = pAdapterList;;
490 pAdapterOther = pAdapterOther->pNext)
491 {
492 if (pAdapterOther == NULL)
493 {
494 /* No adapter with a same name found. */
495 WCHAR szArgument[10 /*create=""|deleteN=""*/ + MAX_PATH /*szDisplayName*/ + 1 /*|*/
496 + MAX_PATH /*szHardwareId*/ + 1 /*terminator*/];
497
498 /* InstallTUNTAPAdapters will create the adapter. */
499 swprintf_s(szArgument, _countof(szArgument), L"create=\"%.*s|%.*s\"", MAX_PATH,
500 szDisplayName, MAX_PATH, szHardwareId);
501 msica_arg_seq_add_tail(seq, szArgument);
502
503 if (seqRollback)
504 {
505 /* InstallTUNTAPAdaptersRollback will delete the adapter. */
506 swprintf_s(szArgument, _countof(szArgument), L"deleteN=\"%.*s\"", MAX_PATH,
507 szDisplayName);
508 msica_arg_seq_add_head(seqRollback, szArgument);
509 }
510
511 *iTicks += MSICA_ADAPTER_TICK_SIZE;
512 break;
513 }
514 else if (_wcsicmp(szDisplayName, pAdapterOther->szName) == 0)
515 {
516 /* Adapter with a same name found. */
517 for (LPCWSTR hwid = pAdapterOther->szzHardwareIDs;; hwid += wcslen(hwid) + 1)
518 {
519 if (hwid[0] == 0)
520 {
521 /* This adapter has a different hardware ID. */
522 msg(M_NONFATAL, "%s: Adapter with name \"%ls\" already exists", __FUNCTION__,
523 pAdapterOther->szName);
524 dwResult = ERROR_ALREADY_EXISTS;
525 goto cleanup_pAdapterList;
526 }
527 else if (_wcsicmp(hwid, szHardwareId) == 0)
528 {
529 /* This is an adapter with the requested hardware ID. We already have what we
530 * want! */
531 break;
532 }
533 }
534 /* Adapter names are unique. There should be no other adapter with this name. */
535 break;
536 }
537 }
538
539cleanup_pAdapterList:
540 tap_free_adapter_list(pAdapterList);
541 return dwResult;
542}
543
544
572static DWORD
574 _Inout_opt_ struct msica_arg_seq *seqCommit,
575 _Inout_opt_ struct msica_arg_seq *seqRollback, _In_z_ LPCWSTR szDisplayName,
576 _In_z_ LPCWSTR szzHardwareIDs, _Inout_ int *iTicks)
577{
578 /* Get adapters with given hardware ID. */
579 struct tap_adapter_node *pAdapterList = NULL;
580 DWORD dwResult = tap_list_adapters(NULL, szzHardwareIDs, &pAdapterList);
581 if (dwResult != ERROR_SUCCESS)
582 {
583 return dwResult;
584 }
585
586 /* Does adapter exist? */
587 for (struct tap_adapter_node *pAdapter = pAdapterList; pAdapter != NULL;
588 pAdapter = pAdapter->pNext)
589 {
590 if (_wcsicmp(szDisplayName, pAdapter->szName) == 0)
591 {
592 /* Adapter found. */
593 WCHAR szArgument[8 /*disable=|enable=|delete=*/
594 + 38 /*{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}*/ + 1 /*terminator*/];
595 if (seqCommit && seqRollback)
596 {
597 /* UninstallTUNTAPAdapters will disable the adapter. */
598 swprintf_s(szArgument, _countof(szArgument), L"disable=" _L(PRIXGUID),
599 PRIGUID_PARAM(pAdapter->guid));
600 msica_arg_seq_add_tail(seq, szArgument);
601
602 /* UninstallTUNTAPAdaptersRollback will re-enable the adapter. */
603 swprintf_s(szArgument, _countof(szArgument), L"enable=" _L(PRIXGUID),
604 PRIGUID_PARAM(pAdapter->guid));
605 msica_arg_seq_add_head(seqRollback, szArgument);
606
607 /* UninstallTUNTAPAdaptersCommit will delete the adapter. */
608 swprintf_s(szArgument, _countof(szArgument), L"delete=" _L(PRIXGUID),
609 PRIGUID_PARAM(pAdapter->guid));
610 msica_arg_seq_add_tail(seqCommit, szArgument);
611 }
612 else
613 {
614 /* UninstallTUNTAPAdapters will delete the adapter. */
615 swprintf_s(szArgument, _countof(szArgument), L"delete=" _L(PRIXGUID),
616 PRIGUID_PARAM(pAdapter->guid));
617 msica_arg_seq_add_tail(seq, szArgument);
618 }
619
620 *iTicks += MSICA_ADAPTER_TICK_SIZE;
621 /* Adapter names are unique. There should be no other adapter with this name. */
622 break;
623 }
624 }
625
626 tap_free_adapter_list(pAdapterList);
627 return dwResult;
628}
629
630
631UINT __stdcall
632EvaluateTUNTAPAdapters(_In_ MSIHANDLE hInstall)
633{
634#ifdef DLLEXP_EXPORT
635#pragma comment(linker, DLLEXP_EXPORT)
636#endif
637
638 debug_popup(__FUNCTION__);
639
640 UINT uiResult;
641 BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL));
642
644
645 struct msica_arg_seq seqInstall, seqInstallCommit, seqInstallRollback, seqUninstall,
646 seqUninstallCommit, seqUninstallRollback;
647 msica_arg_seq_init(&seqInstall);
648 msica_arg_seq_init(&seqInstallCommit);
649 msica_arg_seq_init(&seqInstallRollback);
650 msica_arg_seq_init(&seqUninstall);
651 msica_arg_seq_init(&seqUninstallCommit);
652 msica_arg_seq_init(&seqUninstallRollback);
653
654 /* Check rollback state. */
655 bool bRollbackEnabled =
656 MsiEvaluateCondition(hInstall, L"RollbackDisabled") != MSICONDITION_TRUE;
657
658 /* Open MSI database. */
659 MSIHANDLE hDatabase = MsiGetActiveDatabase(hInstall);
660 if (hDatabase == 0)
661 {
662 msg(M_NONFATAL, "%s: MsiGetActiveDatabase failed", __FUNCTION__);
663 uiResult = ERROR_INVALID_HANDLE;
664 goto cleanup_exec_seq;
665 }
666
667 /* Check if TUNTAPAdapter table exists. If it doesn't exist, there's nothing to do. */
668 switch (MsiDatabaseIsTablePersistent(hDatabase, L"TUNTAPAdapter"))
669 {
670 case MSICONDITION_FALSE:
671 case MSICONDITION_TRUE:
672 break;
673
674 default:
675 uiResult = ERROR_SUCCESS;
676 goto cleanup_hDatabase;
677 }
678
679 /* Prepare a query to get a list/view of adapters. */
680 MSIHANDLE hViewST = 0;
681 LPCWSTR szQuery =
682 L"SELECT `Adapter`,`DisplayName`,`Condition`,`Component_`,`HardwareId` FROM `TUNTAPAdapter`";
683 uiResult = MsiDatabaseOpenView(hDatabase, szQuery, &hViewST);
684 if (uiResult != ERROR_SUCCESS)
685 {
686 /* MSDN does not mention MsiDatabaseOpenView() to set GetLastError(). But we
687 * do have an error code. Set last error manually. */
688 SetLastError(uiResult);
689 msg(M_NONFATAL | M_ERRNO, "%s: MsiDatabaseOpenView(\"%ls\") failed", __FUNCTION__, szQuery);
690 goto cleanup_hDatabase;
691 }
692
693 /* Execute query! */
694 uiResult = MsiViewExecute(hViewST, 0);
695 if (uiResult != ERROR_SUCCESS)
696 {
697 /* MSDN does not mention MsiViewExecute() to set GetLastError(). But
698 * we do have an error code. Set last error manually. */
699 SetLastError(uiResult);
700 msg(M_NONFATAL | M_ERRNO, "%s: MsiViewExecute(\"%ls\") failed", __FUNCTION__, szQuery);
701 goto cleanup_hViewST;
702 }
703
704 /* Create a record to report progress with. */
705 MSIHANDLE hRecordProg = MsiCreateRecord(2);
706 if (!hRecordProg)
707 {
708 uiResult = ERROR_INVALID_HANDLE;
709 msg(M_NONFATAL, "%s: MsiCreateRecord failed", __FUNCTION__);
710 goto cleanup_hViewST_close;
711 }
712
713 for (;;)
714 {
715 /* Fetch one record from the view. */
716 MSIHANDLE hRecord = 0;
717 uiResult = MsiViewFetch(hViewST, &hRecord);
718 if (uiResult == ERROR_NO_MORE_ITEMS)
719 {
720 break;
721 }
722 else if (uiResult != ERROR_SUCCESS)
723 {
724 /* MSDN does not mention MsiViewFetch() to set GetLastError().
725 * But we do have an error code. Set last error manually. */
726 SetLastError(uiResult);
727 msg(M_NONFATAL | M_ERRNO, "%s: MsiViewFetch failed", __FUNCTION__);
728 goto cleanup_hRecordProg;
729 }
730
731 INSTALLSTATE iInstalled, iAction;
732 {
733 /* Read adapter component ID (`Component_` is field #4). */
734 LPWSTR szValue = NULL;
735 uiResult = msi_get_record_string(hRecord, 4, &szValue);
736 if (uiResult != ERROR_SUCCESS)
737 {
738 goto cleanup_hRecord;
739 }
740
741 /* Get the component state. */
742 uiResult = MsiGetComponentState(hInstall, szValue, &iInstalled, &iAction);
743 if (uiResult != ERROR_SUCCESS)
744 {
745 /* MSDN does not mention MsiGetComponentState() to set
746 * GetLastError(). But we do have an error code. Set last
747 * error manually. */
748 SetLastError(uiResult);
749 msg(M_NONFATAL | M_ERRNO, "%s: MsiGetComponentState(\"%ls\") failed", __FUNCTION__,
750 szValue);
751 free(szValue);
752 goto cleanup_hRecord;
753 }
754 free(szValue);
755 }
756
757 /* Get adapter display name (`DisplayName` is field #2). */
758 LPWSTR szDisplayName = NULL;
759 uiResult = msi_format_field(hInstall, hRecord, 2, &szDisplayName);
760 if (uiResult != ERROR_SUCCESS)
761 {
762 goto cleanup_hRecord;
763 }
764 /* `DisplayName` field type is
765 * [Filename](https://docs.microsoft.com/en-us/windows/win32/msi/filename), which is either
766 * "8.3|long name" or "8.3". */
767 LPWSTR szDisplayNameEx = wcschr(szDisplayName, L'|');
768 szDisplayNameEx = szDisplayNameEx != NULL ? szDisplayNameEx + 1 : szDisplayName;
769
770 /* Get adapter hardware ID (`HardwareId` is field #5). */
771 WCHAR szzHardwareIDs[0x100] = { 0 };
772 {
773 LPWSTR szHwId = NULL;
774 uiResult = msi_get_record_string(hRecord, 5, &szHwId);
775 if (uiResult != ERROR_SUCCESS)
776 {
777 goto cleanup_szDisplayName;
778 }
779 memcpy_s(szzHardwareIDs,
780 sizeof(szzHardwareIDs)
781 - 2 * sizeof(WCHAR) /*requires double zero termination*/,
782 szHwId, wcslen(szHwId) * sizeof(WCHAR));
783 free(szHwId);
784 }
785
786 if (iAction > INSTALLSTATE_BROKEN)
787 {
788 int iTicks = 0;
789
790 if (iAction >= INSTALLSTATE_LOCAL)
791 {
792 /* Read and evaluate adapter condition (`Condition` is field #3). */
793 LPWSTR szValue = NULL;
794 uiResult = msi_get_record_string(hRecord, 3, &szValue);
795 if (uiResult != ERROR_SUCCESS)
796 {
797 goto cleanup_szDisplayName;
798 }
799#if defined(__GNUC__) || defined(__clang__)
800/*
801 * warning: enumeration value ‘MSICONDITION_TRUE’ not handled in switch
802 * warning: enumeration value ‘MSICONDITION_NONE’ not handled in switch
803 */
804#pragma GCC diagnostic push
805#pragma GCC diagnostic ignored "-Wswitch"
806#endif
807 switch (MsiEvaluateCondition(hInstall, szValue))
808 {
809 case MSICONDITION_FALSE:
810 free(szValue);
811 goto cleanup_szDisplayName;
812
813 case MSICONDITION_ERROR:
814 uiResult = ERROR_INVALID_FIELD;
815 msg(M_NONFATAL | M_ERRNO, "%s: MsiEvaluateCondition(\"%ls\") failed",
816 __FUNCTION__, szValue);
817 free(szValue);
818 goto cleanup_szDisplayName;
819 }
820#if defined(__GNUC__) || defined(__clang__)
821#pragma GCC diagnostic pop
822#endif
823 free(szValue);
824
825 /* Component is or should be installed. Schedule adapter creation. */
826 if (schedule_adapter_create(&seqInstall,
827 bRollbackEnabled ? &seqInstallRollback : NULL,
828 szDisplayNameEx, szzHardwareIDs, &iTicks)
829 != ERROR_SUCCESS)
830 {
831 uiResult = ERROR_INSTALL_FAILED;
832 goto cleanup_szDisplayName;
833 }
834 }
835 else
836 {
837 /* Component is installed, but should be degraded to advertised/removed. Schedule
838 * adapter deletition.
839 *
840 * Note: On adapter removal (product is being uninstalled), we tolerate dwResult
841 * error. Better a partial uninstallation than no uninstallation at all.
842 */
843 schedule_adapter_delete(&seqUninstall,
844 bRollbackEnabled ? &seqUninstallCommit : NULL,
845 bRollbackEnabled ? &seqUninstallRollback : NULL,
846 szDisplayNameEx, szzHardwareIDs, &iTicks);
847 }
848
849 /* Arrange the amount of tick space to add to the progress indicator.
850 * Do this within the loop to poll for user cancellation. */
851 MsiRecordSetInteger(hRecordProg, 1, 3 /* OP3 = Add ticks to the expected total number of progress of the progress bar */);
852 MsiRecordSetInteger(hRecordProg, 2, iTicks);
853 if (MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRecordProg) == IDCANCEL)
854 {
855 uiResult = ERROR_INSTALL_USEREXIT;
856 goto cleanup_szDisplayName;
857 }
858 }
859
860cleanup_szDisplayName:
861 free(szDisplayName);
862cleanup_hRecord:
863 MsiCloseHandle(hRecord);
864 if (uiResult != ERROR_SUCCESS)
865 {
866 goto cleanup_hRecordProg;
867 }
868 }
869
870 /* save path to user's temp dir to be used later by deferred actions */
871 WCHAR tmpDir[MAX_PATH];
872 GetTempPath(MAX_PATH, tmpDir);
873
874 WCHAR str[MAX_PATH + 7];
875 swprintf_s(str, _countof(str), L"tmpdir=%ls", tmpDir);
876 msica_arg_seq_add_tail(&seqInstall, str);
877 msica_arg_seq_add_tail(&seqInstallCommit, str);
878 msica_arg_seq_add_tail(&seqInstallRollback, str);
879 msica_arg_seq_add_tail(&seqUninstall, str);
880 msica_arg_seq_add_tail(&seqUninstallCommit, str);
881 msica_arg_seq_add_tail(&seqUninstallRollback, str);
882
883 /* Store deferred custom action parameters. */
884 if ((uiResult = setup_sequence(hInstall, L"InstallTUNTAPAdapters", &seqInstall))
885 != ERROR_SUCCESS
886 || (uiResult = setup_sequence(hInstall, L"InstallTUNTAPAdaptersCommit", &seqInstallCommit))
887 != ERROR_SUCCESS
888 || (uiResult =
889 setup_sequence(hInstall, L"InstallTUNTAPAdaptersRollback", &seqInstallRollback))
890 != ERROR_SUCCESS
891 || (uiResult = setup_sequence(hInstall, L"UninstallTUNTAPAdapters", &seqUninstall))
892 != ERROR_SUCCESS
893 || (uiResult =
894 setup_sequence(hInstall, L"UninstallTUNTAPAdaptersCommit", &seqUninstallCommit))
895 != ERROR_SUCCESS
896 || (uiResult =
897 setup_sequence(hInstall, L"UninstallTUNTAPAdaptersRollback", &seqUninstallRollback))
898 != ERROR_SUCCESS)
899 {
900 goto cleanup_hRecordProg;
901 }
902
903 uiResult = ERROR_SUCCESS;
904
905cleanup_hRecordProg:
906 MsiCloseHandle(hRecordProg);
907cleanup_hViewST_close:
908 MsiViewClose(hViewST);
909cleanup_hViewST:
910 MsiCloseHandle(hViewST);
911cleanup_hDatabase:
912 MsiCloseHandle(hDatabase);
913cleanup_exec_seq:
914 msica_arg_seq_free(&seqInstall);
915 msica_arg_seq_free(&seqInstallCommit);
916 msica_arg_seq_free(&seqInstallRollback);
917 msica_arg_seq_free(&seqUninstall);
918 msica_arg_seq_free(&seqUninstallCommit);
919 msica_arg_seq_free(&seqUninstallRollback);
920 if (bIsCoInitialized)
921 {
922 CoUninitialize();
923 }
924 return uiResult;
925}
926
927
937static BOOL
938parse_guid(_In_z_ LPCWSTR szArg, _Out_ GUID *guid)
939{
940 if (swscanf_s(szArg, _L(PRIXGUID), PRIGUID_PARAM_REF(*guid)) != 11)
941 {
942 msg(M_NONFATAL | M_ERRNO, "%s: swscanf_s(\"%ls\") failed", __FUNCTION__, szArg);
943 return FALSE;
944 }
945 return TRUE;
946}
947
948
957static void
958CreateRebootFile(_In_z_ LPCWSTR szTmpDir)
959{
960 WCHAR path[MAX_PATH];
961 swprintf_s(path, _countof(path), L"%s%s", szTmpDir, FILE_NEED_REBOOT);
962
963 msg(M_WARN, "%s: Reboot required, create reboot indication file \"%ls\"", __FUNCTION__, path);
964
965 HANDLE file =
966 CreateFileW(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
967 if (file == INVALID_HANDLE_VALUE)
968 {
969 msg(M_NONFATAL | M_ERRNO, "%s: CreateFile(\"%ls\") failed", __FUNCTION__, path);
970 }
971 else
972 {
973 CloseHandle(file);
974 }
975}
976
977UINT __stdcall
978ProcessDeferredAction(_In_ MSIHANDLE hInstall)
979{
980#ifdef DLLEXP_EXPORT
981#pragma comment(linker, DLLEXP_EXPORT)
982#endif
983
984 debug_popup(__FUNCTION__);
985
986 UINT uiResult;
987 BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL));
988 WCHAR tmpDir[MAX_PATH] = { 0 };
989
991
992 BOOL bIsCleanup =
993 MsiGetMode(hInstall, MSIRUNMODE_COMMIT) || MsiGetMode(hInstall, MSIRUNMODE_ROLLBACK);
994
995 /* Get sequence arguments. Always Unicode as CommandLineToArgvW() is available as Unicode-only.
996 */
997 LPWSTR szSequence = NULL;
998 uiResult = msi_get_string(hInstall, L"CustomActionData", &szSequence);
999 if (uiResult != ERROR_SUCCESS)
1000 {
1001 goto cleanup_CoInitialize;
1002 }
1003 int nArgs;
1004 LPWSTR *szArg = CommandLineToArgvW(szSequence, &nArgs);
1005 if (szArg == NULL)
1006 {
1007 uiResult = GetLastError();
1008 msg(M_NONFATAL | M_ERRNO, "%s: CommandLineToArgvW(\"%ls\") failed", __FUNCTION__,
1009 szSequence);
1010 goto cleanup_szSequence;
1011 }
1012
1013 /* Tell the installer to use explicit progress messages. */
1014 MSIHANDLE hRecordProg = MsiCreateRecord(3);
1015 MsiRecordSetInteger(hRecordProg, 1, 1);
1016 MsiRecordSetInteger(hRecordProg, 2, 1);
1017 MsiRecordSetInteger(hRecordProg, 3, 0);
1018 MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRecordProg);
1019
1020 /* Prepare hRecordProg for progress messages. */
1021 MsiRecordSetInteger(hRecordProg, 1, 2);
1022 MsiRecordSetInteger(hRecordProg, 3, 0);
1023
1024 BOOL bRebootRequired = FALSE;
1025
1026 for (int i = 1 /*CommandLineToArgvW injects msiexec.exe as szArg[0]*/; i < nArgs; ++i)
1027 {
1028 DWORD dwResult = ERROR_SUCCESS;
1029
1030 if (wcsncmp(szArg[i], L"create=", 7) == 0)
1031 {
1032 /* Create an adapter with a given name and hardware ID. */
1033 LPWSTR szName = szArg[i] + 7;
1034 LPWSTR szHardwareId = wcschr(szName, L'|');
1035 if (szHardwareId == NULL)
1036 {
1037 goto invalid_argument;
1038 }
1039 szHardwareId[0] = 0;
1040 ++szHardwareId;
1041
1042 {
1043 /* Report the name of the adapter to installer. */
1044 MSIHANDLE hRecord = MsiCreateRecord(4);
1045 MsiRecordSetString(hRecord, 1, L"Creating adapter");
1046 MsiRecordSetString(hRecord, 2, szName);
1047 MsiRecordSetString(hRecord, 3, szHardwareId);
1048 int iResult = MsiProcessMessage(hInstall, INSTALLMESSAGE_ACTIONDATA, hRecord);
1049 MsiCloseHandle(hRecord);
1050 if (iResult == IDCANCEL)
1051 {
1052 uiResult = ERROR_INSTALL_USEREXIT;
1053 goto cleanup;
1054 }
1055 }
1056
1057 GUID guidAdapter;
1058 dwResult = tap_create_adapter(NULL, NULL, szHardwareId, &bRebootRequired, &guidAdapter);
1059 if (dwResult == ERROR_SUCCESS)
1060 {
1061 /* Set adapter name. May fail on some machines, but that is not critical - use
1062 * silent flag to mute messagebox and print error only to log */
1063 tap_set_adapter_name(&guidAdapter, szName, TRUE);
1064 }
1065 }
1066 else if (wcsncmp(szArg[i], L"deleteN=", 8) == 0)
1067 {
1068 /* Delete the adapter by name. */
1069 LPCWSTR szName = szArg[i] + 8;
1070
1071 {
1072 /* Report the name of the adapter to installer. */
1073 MSIHANDLE hRecord = MsiCreateRecord(3);
1074 MsiRecordSetString(hRecord, 1, L"Deleting adapter");
1075 MsiRecordSetString(hRecord, 2, szName);
1076 int iResult = MsiProcessMessage(hInstall, INSTALLMESSAGE_ACTIONDATA, hRecord);
1077 MsiCloseHandle(hRecord);
1078 if (iResult == IDCANCEL)
1079 {
1080 uiResult = ERROR_INSTALL_USEREXIT;
1081 goto cleanup;
1082 }
1083 }
1084
1085 /* Get existing adapters. */
1086 struct tap_adapter_node *pAdapterList = NULL;
1087 dwResult = tap_list_adapters(NULL, NULL, &pAdapterList);
1088 if (dwResult == ERROR_SUCCESS)
1089 {
1090 /* Does the adapter exist? */
1091 for (struct tap_adapter_node *pAdapter = pAdapterList; pAdapter != NULL;
1092 pAdapter = pAdapter->pNext)
1093 {
1094 if (_wcsicmp(szName, pAdapter->szName) == 0)
1095 {
1096 /* Adapter found. */
1097 dwResult = tap_delete_adapter(NULL, &pAdapter->guid, &bRebootRequired);
1098 break;
1099 }
1100 }
1101
1102 tap_free_adapter_list(pAdapterList);
1103 }
1104 }
1105 else if (wcsncmp(szArg[i], L"delete=", 7) == 0)
1106 {
1107 /* Delete the adapter by GUID. */
1108 GUID guid;
1109 if (!parse_guid(szArg[i] + 7, &guid))
1110 {
1111 goto invalid_argument;
1112 }
1113 dwResult = tap_delete_adapter(NULL, &guid, &bRebootRequired);
1114 }
1115 else if (wcsncmp(szArg[i], L"enable=", 7) == 0)
1116 {
1117 /* Enable the adapter. */
1118 GUID guid;
1119 if (!parse_guid(szArg[i] + 7, &guid))
1120 {
1121 goto invalid_argument;
1122 }
1123 dwResult = tap_enable_adapter(NULL, &guid, TRUE, &bRebootRequired);
1124 }
1125 else if (wcsncmp(szArg[i], L"disable=", 8) == 0)
1126 {
1127 /* Disable the adapter. */
1128 GUID guid;
1129 if (!parse_guid(szArg[i] + 8, &guid))
1130 {
1131 goto invalid_argument;
1132 }
1133 dwResult = tap_enable_adapter(NULL, &guid, FALSE, &bRebootRequired);
1134 }
1135 else if (wcsncmp(szArg[i], L"tmpdir=", 7) == 0)
1136 {
1137 wcscpy_s(tmpDir, _countof(tmpDir), szArg[i] + 7);
1138 }
1139 else
1140 {
1141 goto invalid_argument;
1142 }
1143
1144 if (dwResult != ERROR_SUCCESS && !bIsCleanup /* Ignore errors in case of commit/rollback to do as much work as possible. */)
1145 {
1146 uiResult = ERROR_INSTALL_FAILURE;
1147 goto cleanup;
1148 }
1149
1150 /* Report progress and check for user cancellation. */
1151 MsiRecordSetInteger(hRecordProg, 2, MSICA_ADAPTER_TICK_SIZE);
1152 if (MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRecordProg) == IDCANCEL)
1153 {
1154 dwResult = ERROR_INSTALL_USEREXIT;
1155 goto cleanup;
1156 }
1157
1158 continue;
1159
1160invalid_argument:
1161 msg(M_NONFATAL, "%s: Ignoring invalid argument: %ls", __FUNCTION__, szArg[i]);
1162 }
1163
1164cleanup:
1165 if (bRebootRequired && wcslen(tmpDir) > 0)
1166 {
1167 CreateRebootFile(tmpDir);
1168 }
1169 MsiCloseHandle(hRecordProg);
1170 LocalFree(szArg);
1171cleanup_szSequence:
1172 free(szSequence);
1173cleanup_CoInitialize:
1174 if (bIsCoInitialized)
1175 {
1176 CoUninitialize();
1177 }
1178 return uiResult;
1179}
1180
1181UINT __stdcall
1182CheckAndScheduleReboot(_In_ MSIHANDLE hInstall)
1183{
1184#ifdef DLLEXP_EXPORT
1185#pragma comment(linker, DLLEXP_EXPORT)
1186#endif
1187
1188 debug_popup(__FUNCTION__);
1189
1190 BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL));
1191
1193
1194 /* get user-specific temp path, to where we create reboot indication file */
1195 WCHAR tempPath[MAX_PATH];
1196 GetTempPathW(MAX_PATH, tempPath);
1197
1198 /* check if reboot file exists */
1199 WCHAR path[MAX_PATH];
1200 swprintf_s(path, _countof(path), L"%s%s", tempPath, FILE_NEED_REBOOT);
1201 WIN32_FIND_DATA data = { 0 };
1202 HANDLE searchHandle = FindFirstFileW(path, &data);
1203 if (searchHandle != INVALID_HANDLE_VALUE)
1204 {
1205 msg(M_WARN, "%s: Reboot file exists, schedule reboot", __FUNCTION__);
1206
1207 FindClose(searchHandle);
1208 DeleteFileW(path);
1209
1210 MsiSetMode(hInstall, MSIRUNMODE_REBOOTATEND, TRUE);
1211 }
1212
1213 if (bIsCoInitialized)
1214 {
1215 CoUninitialize();
1216 }
1217 return ERROR_SUCCESS;
1218}
#define TAP_WIN_COMPONENT_ID
Definition config.h:546
#define PACKAGE_VERSION
Definition config.h:504
void msica_arg_seq_free(_Inout_ struct msica_arg_seq *seq)
Frees argument sequence.
Definition msica_arg.c:41
void msica_arg_seq_init(_Inout_ struct msica_arg_seq *seq)
Initializes argument sequence.
Definition msica_arg.c:33
void msica_arg_seq_add_tail(_Inout_ struct msica_arg_seq *seq, _Inout_ LPCWSTR argument)
Appends argument to the end of the argument sequence.
Definition msica_arg.c:74
void msica_arg_seq_add_head(_Inout_ struct msica_arg_seq *seq, _In_z_ LPCWSTR argument)
Inserts argument to the beginning of the argument sequence.
Definition msica_arg.c:54
LPWSTR msica_arg_seq_join(_In_ const struct msica_arg_seq *seq)
Join arguments of the argument sequence into a space delimited string.
Definition msica_arg.c:91
UINT msi_format_field(_In_ MSIHANDLE hInstall, _In_ MSIHANDLE hRecord, _In_ unsigned int iField, _Out_ LPWSTR *pszValue)
Formats MSI record field.
Definition msiex.c:205
UINT msi_get_record_string(_In_ MSIHANDLE hRecord, _In_ unsigned int iField, _Out_ LPWSTR *pszValue)
Gets MSI record string value.
Definition msiex.c:93
UINT msi_get_string(_In_ MSIHANDLE hInstall, _In_z_ LPCWSTR szName, _Out_ LPWSTR *pszValue)
Gets MSI property value.
Definition msiex.c:37
#define M_FATAL
Definition error.h:90
#define M_NONFATAL
Definition error.h:91
#define msg(flags,...)
Definition error.h:152
#define M_WARN
Definition error.h:92
#define M_ERRNO
Definition error.h:95
UINT __stdcall CheckAndScheduleReboot(_In_ MSIHANDLE hInstall)
Schedule reboot after installation if reboot indication file is found in user's temp directory.
#define OPENVPN_CONNECT_ADAPTER_SUBSTR
UINT __stdcall FindSystemInfo(_In_ MSIHANDLE hInstall)
Determines Windows information:
UINT __stdcall ProcessDeferredAction(_In_ MSIHANDLE hInstall)
Perform scheduled deferred action.
#define MSICA_ADAPTER_TICK_SIZE
Local constants.
#define debug_popup(f)
UINT __stdcall CloseOpenVPNGUI(_In_ MSIHANDLE hInstall)
Find OpenVPN GUI window and send it a WM_CLOSE message.
#define FILE_NEED_REBOOT
static DWORD schedule_adapter_delete(_Inout_ struct msica_arg_seq *seq, _Inout_opt_ struct msica_arg_seq *seqCommit, _Inout_opt_ struct msica_arg_seq *seqRollback, _In_z_ LPCWSTR szDisplayName, _In_z_ LPCWSTR szzHardwareIDs, _Inout_ int *iTicks)
Schedules adapter deletion.
UINT __stdcall StartOpenVPNGUI(_In_ MSIHANDLE hInstall)
Launches OpenVPN GUI.
static void find_adapters(_In_ MSIHANDLE hInstall, _In_z_ LPCWSTR szzHardwareIDs, _In_z_ LPCWSTR szAdaptersPropertyName, _In_z_ LPCWSTR szActiveAdaptersPropertyName)
static UINT setup_sequence(_In_ MSIHANDLE hInstall, _In_z_ LPCWSTR szProperty, _In_ struct msica_arg_seq *seq)
Joins an argument sequence and sets it to the MSI property.
static BOOL parse_guid(_In_z_ LPCWSTR szArg, _Out_ GUID *guid)
Parses string encoded GUID.
static void CreateRebootFile(_In_z_ LPCWSTR szTmpDir)
Create empty file in user's temp directory.
static DWORD schedule_adapter_create(_Inout_ struct msica_arg_seq *seq, _Inout_opt_ struct msica_arg_seq *seqRollback, _In_z_ LPCWSTR szDisplayName, _In_z_ LPCWSTR szHardwareId, _Inout_ int *iTicks)
Schedules adapter creation.
UINT __stdcall EvaluateTUNTAPAdapters(_In_ MSIHANDLE hInstall)
Evaluate the TUNTAPAdapter table of the MSI package database and prepare a list of TAP adapters to in...
#define OPENVPNMSICA_SAVE_MSI_SESSION(hInstall)
Set MSI session handle in thread local storage.
Argument sequence.
Definition msica_arg.h:48
Network adapter list node.
Definition tap.h:124
LPWSTR szzHardwareIDs
Device hardware ID(s)
Definition tap.h:126
struct tap_adapter_node * pNext
Pointer to next adapter.
Definition tap.h:129
LPWSTR szName
Adapter name.
Definition tap.h:127
GUID guid
Adapter GUID.
Definition tap.h:125
DWORD tap_list_adapters(_In_opt_ HWND hwndParent, _In_opt_ LPCWSTR szzHwIDs, _Out_ struct tap_adapter_node **ppAdapter)
Creates a list of existing network adapters.
Definition tap.c:1041
DWORD tap_set_adapter_name(_In_ LPCGUID pguidAdapter, _In_ LPCWSTR szName, _In_ BOOL bSilent)
Sets adapter name.
Definition tap.c:968
DWORD tap_create_adapter(_In_opt_ HWND hwndParent, _In_opt_ LPCWSTR szDeviceDescription, _In_ LPCWSTR szHwId, _Inout_ LPBOOL pbRebootRequired, _Out_ LPGUID pguidAdapter)
Creates a TUN/TAP adapter.
Definition tap.c:667
DWORD tap_delete_adapter(_In_opt_ HWND hwndParent, _In_ LPCGUID pguidAdapter, _Inout_ LPBOOL pbRebootRequired)
Deletes an adapter.
Definition tap.c:909
void tap_free_adapter_list(_In_ struct tap_adapter_node *pAdapterList)
Frees a list of network adapters.
Definition tap.c:1231
DWORD tap_enable_adapter(_In_opt_ HWND hwndParent, _In_ LPCGUID pguidAdapter, _In_ BOOL bEnable, _Inout_ LPBOOL pbRebootRequired)
Enables or disables an adapter.
Definition tap.c:917
#define PRIGUID_PARAM(g)
Definition basic.h:30
#define _Inout_
Definition basic.h:50
#define _In_z_
Definition basic.h:47
#define PRIGUID_PARAM_REF(g)
Definition basic.h:33
#define _Inout_opt_
Definition basic.h:53
#define _Out_
Definition basic.h:56
#define _In_
Definition basic.h:41
#define PRIXGUID
Definition basic.h:29
#define _L(q)
Definition basic.h:38
static int cleanup(void **state)