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(%u) 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 SetLastError(
189 ulResult); /* MSDN does not mention GetAdaptersAddresses() to set GetLastError().
190 But we do have an error code. Set last error manually. */
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 (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(%u) 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(%u) 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 SetLastError(uiResult); /* MSDN does not mention MsiSetProperty() to set GetLastError(). But
294 we do have an error code. Set last error manually. */
295 msg(M_NONFATAL | M_ERRNO, "%s: MsiSetProperty(\"%s\") failed", __FUNCTION__,
296 szActiveAdaptersPropertyName);
297 goto cleanup_szAdaptersActive;
298 }
299
300cleanup_szAdaptersActive:
301 free(szAdaptersActive);
302cleanup_szAdapters:
303 free(szAdapters);
304cleanup_pAdapterAdresses:
305 free(pAdapterAdresses);
306cleanup_pAdapterList:
307 tap_free_adapter_list(pAdapterList);
308}
309
310
311UINT __stdcall
312FindSystemInfo(_In_ MSIHANDLE hInstall)
313{
314#ifdef DLLEXP_EXPORT
315#pragma comment(linker, DLLEXP_EXPORT)
316#endif
317
318 debug_popup(__FUNCTION__);
319
320 BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL));
321
323
324 find_adapters(hInstall, L"root\\" _L(TAP_WIN_COMPONENT_ID) L"\0" _L(TAP_WIN_COMPONENT_ID) L"\0",
325 L"TAPWINDOWS6ADAPTERS", L"ACTIVETAPWINDOWS6ADAPTERS");
326 find_adapters(hInstall,
327 L"ovpn-dco"
328 L"\0",
329 L"OVPNDCOADAPTERS", L"ACTIVEOVPNDCOADAPTERS");
330
331 if (bIsCoInitialized)
332 {
333 CoUninitialize();
334 }
335 return ERROR_SUCCESS;
336}
337
338
339UINT __stdcall
340CloseOpenVPNGUI(_In_ MSIHANDLE hInstall)
341{
342#ifdef DLLEXP_EXPORT
343#pragma comment(linker, DLLEXP_EXPORT)
344#endif
345 UNREFERENCED_PARAMETER(hInstall); /* This CA is does not interact with MSI session (report
346 errors, access properties, tables, etc.). */
347
348 debug_popup(__FUNCTION__);
349
350 /* Find OpenVPN GUI window. */
351 HWND hWnd = FindWindow(L"OpenVPN-GUI", NULL);
352 if (hWnd)
353 {
354 /* Ask it to close and wait for 100ms. Unfortunately, this will succeed only for recent
355 * OpenVPN GUI that do not run elevated. */
356 SendMessage(hWnd, WM_CLOSE, 0, 0);
357 Sleep(100);
358 }
359
360 return ERROR_SUCCESS;
361}
362
363
364UINT __stdcall
365StartOpenVPNGUI(_In_ MSIHANDLE hInstall)
366{
367#ifdef DLLEXP_EXPORT
368#pragma comment(linker, DLLEXP_EXPORT)
369#endif
370
371 debug_popup(__FUNCTION__);
372
373 UINT uiResult;
374 BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL));
375
377
378 /* Create and populate a MSI record. */
379 MSIHANDLE hRecord = MsiCreateRecord(1);
380 if (!hRecord)
381 {
382 uiResult = ERROR_INVALID_HANDLE;
383 msg(M_NONFATAL, "%s: MsiCreateRecord failed", __FUNCTION__);
384 goto cleanup_CoInitialize;
385 }
386 uiResult = MsiRecordSetString(hRecord, 0, L"\"[#bin.openvpn_gui.exe]\"");
387 if (uiResult != ERROR_SUCCESS)
388 {
389 SetLastError(uiResult); /* MSDN does not mention MsiRecordSetString() to set GetLastError().
390 But we do have an error code. Set last error manually. */
391 msg(M_NONFATAL | M_ERRNO, "%s: MsiRecordSetString failed", __FUNCTION__);
392 goto cleanup_MsiCreateRecord;
393 }
394
395 /* Format string. */
396 WCHAR szStackBuf[MAX_PATH];
397 DWORD dwPathSize = _countof(szStackBuf);
398 LPWSTR szPath = szStackBuf;
399 uiResult = MsiFormatRecord(hInstall, hRecord, szPath, &dwPathSize);
400 if (uiResult == ERROR_MORE_DATA)
401 {
402 /* Allocate buffer on heap (+1 for terminator), and retry. */
403 szPath = (LPWSTR)malloc((++dwPathSize) * sizeof(WCHAR));
404 if (szPath == NULL)
405 {
406 msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwPathSize * sizeof(WCHAR));
407 uiResult = ERROR_OUTOFMEMORY;
408 goto cleanup_MsiCreateRecord;
409 }
410
411 uiResult = MsiFormatRecord(hInstall, hRecord, szPath, &dwPathSize);
412 }
413 if (uiResult != ERROR_SUCCESS)
414 {
415 SetLastError(uiResult); /* MSDN does not mention MsiFormatRecord() to set GetLastError().
416 But we do have an error code. Set last error manually. */
417 msg(M_NONFATAL | M_ERRNO, "%s: MsiFormatRecord failed", __FUNCTION__);
418 goto cleanup_malloc_szPath;
419 }
420
421 /* Launch the OpenVPN GUI. */
422 SHELLEXECUTEINFO sei = { .cbSize = sizeof(SHELLEXECUTEINFO),
423 .fMask =
424 SEE_MASK_FLAG_NO_UI, /* Don't show error UI, we'll display it. */
425 .lpFile = szPath,
426 .nShow = SW_SHOWNORMAL };
427 if (!ShellExecuteEx(&sei))
428 {
429 uiResult = GetLastError();
430 msg(M_NONFATAL | M_ERRNO, "%s: ShellExecuteEx(%s) failed", __FUNCTION__, szPath);
431 goto cleanup_malloc_szPath;
432 }
433
434 uiResult = ERROR_SUCCESS;
435
436cleanup_malloc_szPath:
437 if (szPath != szStackBuf)
438 {
439 free(szPath);
440 }
441cleanup_MsiCreateRecord:
442 MsiCloseHandle(hRecord);
443cleanup_CoInitialize:
444 if (bIsCoInitialized)
445 {
446 CoUninitialize();
447 }
448 return uiResult;
449}
450
451
472static DWORD
474 _Inout_opt_ struct msica_arg_seq *seqRollback, _In_z_ LPCWSTR szDisplayName,
475 _In_z_ LPCWSTR szHardwareId, _Inout_ int *iTicks)
476{
477 /* Get existing network adapters. */
478 struct tap_adapter_node *pAdapterList = NULL;
479 DWORD dwResult = tap_list_adapters(NULL, NULL, &pAdapterList);
480 if (dwResult != ERROR_SUCCESS)
481 {
482 return dwResult;
483 }
484
485 /* Does adapter exist? */
486 for (struct tap_adapter_node *pAdapterOther = pAdapterList;;
487 pAdapterOther = pAdapterOther->pNext)
488 {
489 if (pAdapterOther == NULL)
490 {
491 /* No adapter with a same name found. */
492 WCHAR szArgument[10 /*create=""|deleteN=""*/ + MAX_PATH /*szDisplayName*/ + 1 /*|*/
493 + MAX_PATH /*szHardwareId*/ + 1 /*terminator*/];
494
495 /* InstallTUNTAPAdapters will create the adapter. */
496 swprintf_s(szArgument, _countof(szArgument), L"create=\"%.*s|%.*s\"", MAX_PATH,
497 szDisplayName, MAX_PATH, szHardwareId);
498 msica_arg_seq_add_tail(seq, szArgument);
499
500 if (seqRollback)
501 {
502 /* InstallTUNTAPAdaptersRollback will delete the adapter. */
503 swprintf_s(szArgument, _countof(szArgument), L"deleteN=\"%.*s\"", MAX_PATH,
504 szDisplayName);
505 msica_arg_seq_add_head(seqRollback, szArgument);
506 }
507
508 *iTicks += MSICA_ADAPTER_TICK_SIZE;
509 break;
510 }
511 else if (wcsicmp(szDisplayName, pAdapterOther->szName) == 0)
512 {
513 /* Adapter with a same name found. */
514 for (LPCWSTR hwid = pAdapterOther->szzHardwareIDs;; hwid += wcslen(hwid) + 1)
515 {
516 if (hwid[0] == 0)
517 {
518 /* This adapter has a different hardware ID. */
519 msg(M_NONFATAL, "%s: Adapter with name \"%ls\" already exists", __FUNCTION__,
520 pAdapterOther->szName);
521 dwResult = ERROR_ALREADY_EXISTS;
522 goto cleanup_pAdapterList;
523 }
524 else if (wcsicmp(hwid, szHardwareId) == 0)
525 {
526 /* This is an adapter with the requested hardware ID. We already have what we
527 * want! */
528 break;
529 }
530 }
531 break; /* Adapter names are unique. There should be no other adapter with this name. */
532 }
533 }
534
535cleanup_pAdapterList:
536 tap_free_adapter_list(pAdapterList);
537 return dwResult;
538}
539
540
568static DWORD
570 _Inout_opt_ struct msica_arg_seq *seqCommit,
571 _Inout_opt_ struct msica_arg_seq *seqRollback, _In_z_ LPCWSTR szDisplayName,
572 _In_z_ LPCWSTR szzHardwareIDs, _Inout_ int *iTicks)
573{
574 /* Get adapters with given hardware ID. */
575 struct tap_adapter_node *pAdapterList = NULL;
576 DWORD dwResult = tap_list_adapters(NULL, szzHardwareIDs, &pAdapterList);
577 if (dwResult != ERROR_SUCCESS)
578 {
579 return dwResult;
580 }
581
582 /* Does adapter exist? */
583 for (struct tap_adapter_node *pAdapter = pAdapterList; pAdapter != NULL;
584 pAdapter = pAdapter->pNext)
585 {
586 if (wcsicmp(szDisplayName, pAdapter->szName) == 0)
587 {
588 /* Adapter found. */
589 WCHAR szArgument[8 /*disable=|enable=|delete=*/
590 + 38 /*{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}*/ + 1 /*terminator*/];
591 if (seqCommit && seqRollback)
592 {
593 /* UninstallTUNTAPAdapters will disable the adapter. */
594 swprintf_s(szArgument, _countof(szArgument), L"disable=" _L(PRIXGUID),
595 PRIGUID_PARAM(pAdapter->guid));
596 msica_arg_seq_add_tail(seq, szArgument);
597
598 /* UninstallTUNTAPAdaptersRollback will re-enable the adapter. */
599 swprintf_s(szArgument, _countof(szArgument), L"enable=" _L(PRIXGUID),
600 PRIGUID_PARAM(pAdapter->guid));
601 msica_arg_seq_add_head(seqRollback, szArgument);
602
603 /* UninstallTUNTAPAdaptersCommit will delete the adapter. */
604 swprintf_s(szArgument, _countof(szArgument), L"delete=" _L(PRIXGUID),
605 PRIGUID_PARAM(pAdapter->guid));
606 msica_arg_seq_add_tail(seqCommit, szArgument);
607 }
608 else
609 {
610 /* UninstallTUNTAPAdapters will delete the adapter. */
611 swprintf_s(szArgument, _countof(szArgument), L"delete=" _L(PRIXGUID),
612 PRIGUID_PARAM(pAdapter->guid));
613 msica_arg_seq_add_tail(seq, szArgument);
614 }
615
616 iTicks += MSICA_ADAPTER_TICK_SIZE;
617 break; /* Adapter names are unique. There should be no other adapter with this name. */
618 }
619 }
620
621 tap_free_adapter_list(pAdapterList);
622 return dwResult;
623}
624
625
626UINT __stdcall
627EvaluateTUNTAPAdapters(_In_ MSIHANDLE hInstall)
628{
629#ifdef DLLEXP_EXPORT
630#pragma comment(linker, DLLEXP_EXPORT)
631#endif
632
633 debug_popup(__FUNCTION__);
634
635 UINT uiResult;
636 BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL));
637
639
640 struct msica_arg_seq seqInstall, seqInstallCommit, seqInstallRollback, seqUninstall,
641 seqUninstallCommit, seqUninstallRollback;
642 msica_arg_seq_init(&seqInstall);
643 msica_arg_seq_init(&seqInstallCommit);
644 msica_arg_seq_init(&seqInstallRollback);
645 msica_arg_seq_init(&seqUninstall);
646 msica_arg_seq_init(&seqUninstallCommit);
647 msica_arg_seq_init(&seqUninstallRollback);
648
649 /* Check rollback state. */
650 bool bRollbackEnabled =
651 MsiEvaluateCondition(hInstall, L"RollbackDisabled") != MSICONDITION_TRUE;
652
653 /* Open MSI database. */
654 MSIHANDLE hDatabase = MsiGetActiveDatabase(hInstall);
655 if (hDatabase == 0)
656 {
657 msg(M_NONFATAL, "%s: MsiGetActiveDatabase failed", __FUNCTION__);
658 uiResult = ERROR_INVALID_HANDLE;
659 goto cleanup_exec_seq;
660 }
661
662 /* Check if TUNTAPAdapter table exists. If it doesn't exist, there's nothing to do. */
663 switch (MsiDatabaseIsTablePersistent(hDatabase, L"TUNTAPAdapter"))
664 {
665 case MSICONDITION_FALSE:
666 case MSICONDITION_TRUE:
667 break;
668
669 default:
670 uiResult = ERROR_SUCCESS;
671 goto cleanup_hDatabase;
672 }
673
674 /* Prepare a query to get a list/view of adapters. */
675 MSIHANDLE hViewST = 0;
676 LPCWSTR szQuery =
677 L"SELECT `Adapter`,`DisplayName`,`Condition`,`Component_`,`HardwareId` FROM `TUNTAPAdapter`";
678 uiResult = MsiDatabaseOpenView(hDatabase, szQuery, &hViewST);
679 if (uiResult != ERROR_SUCCESS)
680 {
681 SetLastError(
682 uiResult); /* MSDN does not mention MsiDatabaseOpenView() to set GetLastError(). But we
683 do have an error code. Set last error manually. */
684 msg(M_NONFATAL | M_ERRNO, "%s: MsiDatabaseOpenView(\"%ls\") failed", __FUNCTION__, szQuery);
685 goto cleanup_hDatabase;
686 }
687
688 /* Execute query! */
689 uiResult = MsiViewExecute(hViewST, 0);
690 if (uiResult != ERROR_SUCCESS)
691 {
692 SetLastError(uiResult); /* MSDN does not mention MsiViewExecute() to set GetLastError(). But
693 we do have an error code. Set last error manually. */
694 msg(M_NONFATAL | M_ERRNO, "%s: MsiViewExecute(\"%ls\") failed", __FUNCTION__, szQuery);
695 goto cleanup_hViewST;
696 }
697
698 /* Create a record to report progress with. */
699 MSIHANDLE hRecordProg = MsiCreateRecord(2);
700 if (!hRecordProg)
701 {
702 uiResult = ERROR_INVALID_HANDLE;
703 msg(M_NONFATAL, "%s: MsiCreateRecord failed", __FUNCTION__);
704 goto cleanup_hViewST_close;
705 }
706
707 for (;;)
708 {
709 /* Fetch one record from the view. */
710 MSIHANDLE hRecord = 0;
711 uiResult = MsiViewFetch(hViewST, &hRecord);
712 if (uiResult == ERROR_NO_MORE_ITEMS)
713 {
714 uiResult = ERROR_SUCCESS;
715 break;
716 }
717 else if (uiResult != ERROR_SUCCESS)
718 {
719 SetLastError(uiResult); /* MSDN does not mention MsiViewFetch() to set GetLastError().
720 But we do have an error code. Set last error manually. */
721 msg(M_NONFATAL | M_ERRNO, "%s: MsiViewFetch failed", __FUNCTION__);
722 goto cleanup_hRecordProg;
723 }
724
725 INSTALLSTATE iInstalled, iAction;
726 {
727 /* Read adapter component ID (`Component_` is field #4). */
728 LPWSTR szValue = NULL;
729 uiResult = msi_get_record_string(hRecord, 4, &szValue);
730 if (uiResult != ERROR_SUCCESS)
731 {
732 goto cleanup_hRecord;
733 }
734
735 /* Get the component state. */
736 uiResult = MsiGetComponentState(hInstall, szValue, &iInstalled, &iAction);
737 if (uiResult != ERROR_SUCCESS)
738 {
739 SetLastError(uiResult); /* MSDN does not mention MsiGetComponentState() to set
740 GetLastError(). But we do have an error code. Set last
741 error manually. */
742 msg(M_NONFATAL | M_ERRNO, "%s: MsiGetComponentState(\"%ls\") failed", __FUNCTION__,
743 szValue);
744 free(szValue);
745 goto cleanup_hRecord;
746 }
747 free(szValue);
748 }
749
750 /* Get adapter display name (`DisplayName` is field #2). */
751 LPWSTR szDisplayName = NULL;
752 uiResult = msi_format_field(hInstall, hRecord, 2, &szDisplayName);
753 if (uiResult != ERROR_SUCCESS)
754 {
755 goto cleanup_hRecord;
756 }
757 /* `DisplayName` field type is
758 * [Filename](https://docs.microsoft.com/en-us/windows/win32/msi/filename), which is either
759 * "8.3|long name" or "8.3". */
760 LPWSTR szDisplayNameEx = wcschr(szDisplayName, L'|');
761 szDisplayNameEx = szDisplayNameEx != NULL ? szDisplayNameEx + 1 : szDisplayName;
762
763 /* Get adapter hardware ID (`HardwareId` is field #5). */
764 WCHAR szzHardwareIDs[0x100] = { 0 };
765 {
766 LPWSTR szHwId = NULL;
767 uiResult = msi_get_record_string(hRecord, 5, &szHwId);
768 if (uiResult != ERROR_SUCCESS)
769 {
770 goto cleanup_szDisplayName;
771 }
772 memcpy_s(szzHardwareIDs,
773 sizeof(szzHardwareIDs)
774 - 2 * sizeof(WCHAR) /*requires double zero termination*/,
775 szHwId, wcslen(szHwId) * sizeof(WCHAR));
776 free(szHwId);
777 }
778
779 if (iAction > INSTALLSTATE_BROKEN)
780 {
781 int iTicks = 0;
782
783 if (iAction >= INSTALLSTATE_LOCAL)
784 {
785 /* Read and evaluate adapter condition (`Condition` is field #3). */
786 LPWSTR szValue = NULL;
787 uiResult = msi_get_record_string(hRecord, 3, &szValue);
788 if (uiResult != ERROR_SUCCESS)
789 {
790 goto cleanup_szDisplayName;
791 }
792#if defined(__GNUC__) || defined(__clang__)
793/*
794 * warning: enumeration value ‘MSICONDITION_TRUE’ not handled in switch
795 * warning: enumeration value ‘MSICONDITION_NONE’ not handled in switch
796 */
797#pragma GCC diagnostic push
798#pragma GCC diagnostic ignored "-Wswitch"
799#endif
800 switch (MsiEvaluateCondition(hInstall, szValue))
801 {
802 case MSICONDITION_FALSE:
803 free(szValue);
804 goto cleanup_szDisplayName;
805
806 case MSICONDITION_ERROR:
807 uiResult = ERROR_INVALID_FIELD;
808 msg(M_NONFATAL | M_ERRNO, "%s: MsiEvaluateCondition(\"%ls\") failed",
809 __FUNCTION__, szValue);
810 free(szValue);
811 goto cleanup_szDisplayName;
812 }
813#if defined(__GNUC__) || defined(__clang__)
814#pragma GCC diagnostic pop
815#endif
816 free(szValue);
817
818 /* Component is or should be installed. Schedule adapter creation. */
819 if (schedule_adapter_create(&seqInstall,
820 bRollbackEnabled ? &seqInstallRollback : NULL,
821 szDisplayNameEx, szzHardwareIDs, &iTicks)
822 != ERROR_SUCCESS)
823 {
824 uiResult = ERROR_INSTALL_FAILED;
825 goto cleanup_szDisplayName;
826 }
827 }
828 else
829 {
830 /* Component is installed, but should be degraded to advertised/removed. Schedule
831 * adapter deletition.
832 *
833 * Note: On adapter removal (product is being uninstalled), we tolerate dwResult
834 * error. Better a partial uninstallation than no uninstallation at all.
835 */
836 schedule_adapter_delete(&seqUninstall,
837 bRollbackEnabled ? &seqUninstallCommit : NULL,
838 bRollbackEnabled ? &seqUninstallRollback : NULL,
839 szDisplayNameEx, szzHardwareIDs, &iTicks);
840 }
841
842 /* Arrange the amount of tick space to add to the progress indicator.
843 * Do this within the loop to poll for user cancellation. */
844 MsiRecordSetInteger(hRecordProg, 1, 3 /* OP3 = Add ticks to the expected total number of progress of the progress bar */);
845 MsiRecordSetInteger(hRecordProg, 2, iTicks);
846 if (MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRecordProg) == IDCANCEL)
847 {
848 uiResult = ERROR_INSTALL_USEREXIT;
849 goto cleanup_szDisplayName;
850 }
851 }
852
853cleanup_szDisplayName:
854 free(szDisplayName);
855cleanup_hRecord:
856 MsiCloseHandle(hRecord);
857 if (uiResult != ERROR_SUCCESS)
858 {
859 goto cleanup_hRecordProg;
860 }
861 }
862
863 /* save path to user's temp dir to be used later by deferred actions */
864 WCHAR tmpDir[MAX_PATH];
865 GetTempPath(MAX_PATH, tmpDir);
866
867 WCHAR str[MAX_PATH + 7];
868 swprintf_s(str, _countof(str), L"tmpdir=%ls", tmpDir);
869 msica_arg_seq_add_tail(&seqInstall, str);
870 msica_arg_seq_add_tail(&seqInstallCommit, str);
871 msica_arg_seq_add_tail(&seqInstallRollback, str);
872 msica_arg_seq_add_tail(&seqUninstall, str);
873 msica_arg_seq_add_tail(&seqUninstallCommit, str);
874 msica_arg_seq_add_tail(&seqUninstallRollback, str);
875
876 /* Store deferred custom action parameters. */
877 if ((uiResult = setup_sequence(hInstall, L"InstallTUNTAPAdapters", &seqInstall))
878 != ERROR_SUCCESS
879 || (uiResult = setup_sequence(hInstall, L"InstallTUNTAPAdaptersCommit", &seqInstallCommit))
880 != ERROR_SUCCESS
881 || (uiResult =
882 setup_sequence(hInstall, L"InstallTUNTAPAdaptersRollback", &seqInstallRollback))
883 != ERROR_SUCCESS
884 || (uiResult = setup_sequence(hInstall, L"UninstallTUNTAPAdapters", &seqUninstall))
885 != ERROR_SUCCESS
886 || (uiResult =
887 setup_sequence(hInstall, L"UninstallTUNTAPAdaptersCommit", &seqUninstallCommit))
888 != ERROR_SUCCESS
889 || (uiResult =
890 setup_sequence(hInstall, L"UninstallTUNTAPAdaptersRollback", &seqUninstallRollback))
891 != ERROR_SUCCESS)
892 {
893 goto cleanup_hRecordProg;
894 }
895
896 uiResult = ERROR_SUCCESS;
897
898cleanup_hRecordProg:
899 MsiCloseHandle(hRecordProg);
900cleanup_hViewST_close:
901 MsiViewClose(hViewST);
902cleanup_hViewST:
903 MsiCloseHandle(hViewST);
904cleanup_hDatabase:
905 MsiCloseHandle(hDatabase);
906cleanup_exec_seq:
907 msica_arg_seq_free(&seqInstall);
908 msica_arg_seq_free(&seqInstallCommit);
909 msica_arg_seq_free(&seqInstallRollback);
910 msica_arg_seq_free(&seqUninstall);
911 msica_arg_seq_free(&seqUninstallCommit);
912 msica_arg_seq_free(&seqUninstallRollback);
913 if (bIsCoInitialized)
914 {
915 CoUninitialize();
916 }
917 return uiResult;
918}
919
920
930static BOOL
931parse_guid(_In_z_ LPCWSTR szArg, _Out_ GUID *guid)
932{
933 if (swscanf_s(szArg, _L(PRIXGUID), PRIGUID_PARAM_REF(*guid)) != 11)
934 {
935 msg(M_NONFATAL | M_ERRNO, "%s: swscanf_s(\"%ls\") failed", __FUNCTION__, szArg);
936 return FALSE;
937 }
938 return TRUE;
939}
940
941
950static void
951CreateRebootFile(_In_z_ LPCWSTR szTmpDir)
952{
953 WCHAR path[MAX_PATH];
954 swprintf_s(path, _countof(path), L"%s%s", szTmpDir, FILE_NEED_REBOOT);
955
956 msg(M_WARN, "%s: Reboot required, create reboot indication file \"%ls\"", __FUNCTION__, path);
957
958 HANDLE file =
959 CreateFileW(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
960 if (file == INVALID_HANDLE_VALUE)
961 {
962 msg(M_NONFATAL | M_ERRNO, "%s: CreateFile(\"%ls\") failed", __FUNCTION__, path);
963 }
964 else
965 {
966 CloseHandle(file);
967 }
968}
969
970UINT __stdcall
971ProcessDeferredAction(_In_ MSIHANDLE hInstall)
972{
973#ifdef DLLEXP_EXPORT
974#pragma comment(linker, DLLEXP_EXPORT)
975#endif
976
977 debug_popup(__FUNCTION__);
978
979 UINT uiResult;
980 BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL));
981 WCHAR tmpDir[MAX_PATH] = { 0 };
982
984
985 BOOL bIsCleanup =
986 MsiGetMode(hInstall, MSIRUNMODE_COMMIT) || MsiGetMode(hInstall, MSIRUNMODE_ROLLBACK);
987
988 /* Get sequence arguments. Always Unicode as CommandLineToArgvW() is available as Unicode-only.
989 */
990 LPWSTR szSequence = NULL;
991 uiResult = msi_get_string(hInstall, L"CustomActionData", &szSequence);
992 if (uiResult != ERROR_SUCCESS)
993 {
994 goto cleanup_CoInitialize;
995 }
996 int nArgs;
997 LPWSTR *szArg = CommandLineToArgvW(szSequence, &nArgs);
998 if (szArg == NULL)
999 {
1000 uiResult = GetLastError();
1001 msg(M_NONFATAL | M_ERRNO, "%s: CommandLineToArgvW(\"%ls\") failed", __FUNCTION__,
1002 szSequence);
1003 goto cleanup_szSequence;
1004 }
1005
1006 /* Tell the installer to use explicit progress messages. */
1007 MSIHANDLE hRecordProg = MsiCreateRecord(3);
1008 MsiRecordSetInteger(hRecordProg, 1, 1);
1009 MsiRecordSetInteger(hRecordProg, 2, 1);
1010 MsiRecordSetInteger(hRecordProg, 3, 0);
1011 MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRecordProg);
1012
1013 /* Prepare hRecordProg for progress messages. */
1014 MsiRecordSetInteger(hRecordProg, 1, 2);
1015 MsiRecordSetInteger(hRecordProg, 3, 0);
1016
1017 BOOL bRebootRequired = FALSE;
1018
1019 for (int i = 1 /*CommandLineToArgvW injects msiexec.exe as szArg[0]*/; i < nArgs; ++i)
1020 {
1021 DWORD dwResult = ERROR_SUCCESS;
1022
1023 if (wcsncmp(szArg[i], L"create=", 7) == 0)
1024 {
1025 /* Create an adapter with a given name and hardware ID. */
1026 LPWSTR szName = szArg[i] + 7;
1027 LPWSTR szHardwareId = wcschr(szName, L'|');
1028 if (szHardwareId == NULL)
1029 {
1030 goto invalid_argument;
1031 }
1032 szHardwareId[0] = 0;
1033 ++szHardwareId;
1034
1035 {
1036 /* Report the name of the adapter to installer. */
1037 MSIHANDLE hRecord = MsiCreateRecord(4);
1038 MsiRecordSetString(hRecord, 1, L"Creating adapter");
1039 MsiRecordSetString(hRecord, 2, szName);
1040 MsiRecordSetString(hRecord, 3, szHardwareId);
1041 int iResult = MsiProcessMessage(hInstall, INSTALLMESSAGE_ACTIONDATA, hRecord);
1042 MsiCloseHandle(hRecord);
1043 if (iResult == IDCANCEL)
1044 {
1045 uiResult = ERROR_INSTALL_USEREXIT;
1046 goto cleanup;
1047 }
1048 }
1049
1050 GUID guidAdapter;
1051 dwResult = tap_create_adapter(NULL, NULL, szHardwareId, &bRebootRequired, &guidAdapter);
1052 if (dwResult == ERROR_SUCCESS)
1053 {
1054 /* Set adapter name. May fail on some machines, but that is not critical - use
1055 * silent flag to mute messagebox and print error only to log */
1056 tap_set_adapter_name(&guidAdapter, szName, TRUE);
1057 }
1058 }
1059 else if (wcsncmp(szArg[i], L"deleteN=", 8) == 0)
1060 {
1061 /* Delete the adapter by name. */
1062 LPCWSTR szName = szArg[i] + 8;
1063
1064 {
1065 /* Report the name of the adapter to installer. */
1066 MSIHANDLE hRecord = MsiCreateRecord(3);
1067 MsiRecordSetString(hRecord, 1, L"Deleting adapter");
1068 MsiRecordSetString(hRecord, 2, szName);
1069 int iResult = MsiProcessMessage(hInstall, INSTALLMESSAGE_ACTIONDATA, hRecord);
1070 MsiCloseHandle(hRecord);
1071 if (iResult == IDCANCEL)
1072 {
1073 uiResult = ERROR_INSTALL_USEREXIT;
1074 goto cleanup;
1075 }
1076 }
1077
1078 /* Get existing adapters. */
1079 struct tap_adapter_node *pAdapterList = NULL;
1080 dwResult = tap_list_adapters(NULL, NULL, &pAdapterList);
1081 if (dwResult == ERROR_SUCCESS)
1082 {
1083 /* Does the adapter exist? */
1084 for (struct tap_adapter_node *pAdapter = pAdapterList; pAdapter != NULL;
1085 pAdapter = pAdapter->pNext)
1086 {
1087 if (wcsicmp(szName, pAdapter->szName) == 0)
1088 {
1089 /* Adapter found. */
1090 dwResult = tap_delete_adapter(NULL, &pAdapter->guid, &bRebootRequired);
1091 break;
1092 }
1093 }
1094
1095 tap_free_adapter_list(pAdapterList);
1096 }
1097 }
1098 else if (wcsncmp(szArg[i], L"delete=", 7) == 0)
1099 {
1100 /* Delete the adapter by GUID. */
1101 GUID guid;
1102 if (!parse_guid(szArg[i] + 7, &guid))
1103 {
1104 goto invalid_argument;
1105 }
1106 dwResult = tap_delete_adapter(NULL, &guid, &bRebootRequired);
1107 }
1108 else if (wcsncmp(szArg[i], L"enable=", 7) == 0)
1109 {
1110 /* Enable the adapter. */
1111 GUID guid;
1112 if (!parse_guid(szArg[i] + 7, &guid))
1113 {
1114 goto invalid_argument;
1115 }
1116 dwResult = tap_enable_adapter(NULL, &guid, TRUE, &bRebootRequired);
1117 }
1118 else if (wcsncmp(szArg[i], L"disable=", 8) == 0)
1119 {
1120 /* Disable the adapter. */
1121 GUID guid;
1122 if (!parse_guid(szArg[i] + 8, &guid))
1123 {
1124 goto invalid_argument;
1125 }
1126 dwResult = tap_enable_adapter(NULL, &guid, FALSE, &bRebootRequired);
1127 }
1128 else if (wcsncmp(szArg[i], L"tmpdir=", 7) == 0)
1129 {
1130 wcscpy_s(tmpDir, _countof(tmpDir), szArg[i] + 7);
1131 }
1132 else
1133 {
1134 goto invalid_argument;
1135 }
1136
1137 if (dwResult != ERROR_SUCCESS && !bIsCleanup /* Ignore errors in case of commit/rollback to do as much work as possible. */)
1138 {
1139 uiResult = ERROR_INSTALL_FAILURE;
1140 goto cleanup;
1141 }
1142
1143 /* Report progress and check for user cancellation. */
1144 MsiRecordSetInteger(hRecordProg, 2, MSICA_ADAPTER_TICK_SIZE);
1145 if (MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRecordProg) == IDCANCEL)
1146 {
1147 dwResult = ERROR_INSTALL_USEREXIT;
1148 goto cleanup;
1149 }
1150
1151 continue;
1152
1153invalid_argument:
1154 msg(M_NONFATAL, "%s: Ignoring invalid argument: %ls", __FUNCTION__, szArg[i]);
1155 }
1156
1157cleanup:
1158 if (bRebootRequired && wcslen(tmpDir) > 0)
1159 {
1160 CreateRebootFile(tmpDir);
1161 }
1162 MsiCloseHandle(hRecordProg);
1163 LocalFree(szArg);
1164cleanup_szSequence:
1165 free(szSequence);
1166cleanup_CoInitialize:
1167 if (bIsCoInitialized)
1168 {
1169 CoUninitialize();
1170 }
1171 return uiResult;
1172}
1173
1174UINT __stdcall
1175CheckAndScheduleReboot(_In_ MSIHANDLE hInstall)
1176{
1177#ifdef DLLEXP_EXPORT
1178#pragma comment(linker, DLLEXP_EXPORT)
1179#endif
1180
1181 debug_popup(__FUNCTION__);
1182
1183 BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL));
1184
1186
1187 /* get user-specific temp path, to where we create reboot indication file */
1188 WCHAR tempPath[MAX_PATH];
1189 GetTempPathW(MAX_PATH, tempPath);
1190
1191 /* check if reboot file exists */
1192 WCHAR path[MAX_PATH];
1193 swprintf_s(path, _countof(path), L"%s%s", tempPath, FILE_NEED_REBOOT);
1194 WIN32_FIND_DATA data = { 0 };
1195 HANDLE searchHandle = FindFirstFileW(path, &data);
1196 if (searchHandle != INVALID_HANDLE_VALUE)
1197 {
1198 msg(M_WARN, "%s: Reboot file exists, schedule reboot", __FUNCTION__);
1199
1200 FindClose(searchHandle);
1201 DeleteFileW(path);
1202
1203 MsiSetMode(hInstall, MSIRUNMODE_REBOOTATEND, TRUE);
1204 }
1205
1206 if (bIsCoInitialized)
1207 {
1208 CoUninitialize();
1209 }
1210 return ERROR_SUCCESS;
1211}
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:1036
DWORD tap_set_adapter_name(_In_ LPCGUID pguidAdapter, _In_ LPCWSTR szName, _In_ BOOL bSilent)
Sets adapter name.
Definition tap.c:963
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:1226
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)