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 (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(%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 break;
715 }
716 else if (uiResult != ERROR_SUCCESS)
717 {
718 SetLastError(uiResult); /* MSDN does not mention MsiViewFetch() to set GetLastError().
719 But we do have an error code. Set last error manually. */
720 msg(M_NONFATAL | M_ERRNO, "%s: MsiViewFetch failed", __FUNCTION__);
721 goto cleanup_hRecordProg;
722 }
723
724 INSTALLSTATE iInstalled, iAction;
725 {
726 /* Read adapter component ID (`Component_` is field #4). */
727 LPWSTR szValue = NULL;
728 uiResult = msi_get_record_string(hRecord, 4, &szValue);
729 if (uiResult != ERROR_SUCCESS)
730 {
731 goto cleanup_hRecord;
732 }
733
734 /* Get the component state. */
735 uiResult = MsiGetComponentState(hInstall, szValue, &iInstalled, &iAction);
736 if (uiResult != ERROR_SUCCESS)
737 {
738 SetLastError(uiResult); /* MSDN does not mention MsiGetComponentState() to set
739 GetLastError(). But we do have an error code. Set last
740 error manually. */
741 msg(M_NONFATAL | M_ERRNO, "%s: MsiGetComponentState(\"%ls\") failed", __FUNCTION__,
742 szValue);
743 free(szValue);
744 goto cleanup_hRecord;
745 }
746 free(szValue);
747 }
748
749 /* Get adapter display name (`DisplayName` is field #2). */
750 LPWSTR szDisplayName = NULL;
751 uiResult = msi_format_field(hInstall, hRecord, 2, &szDisplayName);
752 if (uiResult != ERROR_SUCCESS)
753 {
754 goto cleanup_hRecord;
755 }
756 /* `DisplayName` field type is
757 * [Filename](https://docs.microsoft.com/en-us/windows/win32/msi/filename), which is either
758 * "8.3|long name" or "8.3". */
759 LPWSTR szDisplayNameEx = wcschr(szDisplayName, L'|');
760 szDisplayNameEx = szDisplayNameEx != NULL ? szDisplayNameEx + 1 : szDisplayName;
761
762 /* Get adapter hardware ID (`HardwareId` is field #5). */
763 WCHAR szzHardwareIDs[0x100] = { 0 };
764 {
765 LPWSTR szHwId = NULL;
766 uiResult = msi_get_record_string(hRecord, 5, &szHwId);
767 if (uiResult != ERROR_SUCCESS)
768 {
769 goto cleanup_szDisplayName;
770 }
771 memcpy_s(szzHardwareIDs,
772 sizeof(szzHardwareIDs)
773 - 2 * sizeof(WCHAR) /*requires double zero termination*/,
774 szHwId, wcslen(szHwId) * sizeof(WCHAR));
775 free(szHwId);
776 }
777
778 if (iAction > INSTALLSTATE_BROKEN)
779 {
780 int iTicks = 0;
781
782 if (iAction >= INSTALLSTATE_LOCAL)
783 {
784 /* Read and evaluate adapter condition (`Condition` is field #3). */
785 LPWSTR szValue = NULL;
786 uiResult = msi_get_record_string(hRecord, 3, &szValue);
787 if (uiResult != ERROR_SUCCESS)
788 {
789 goto cleanup_szDisplayName;
790 }
791#if defined(__GNUC__) || defined(__clang__)
792/*
793 * warning: enumeration value ‘MSICONDITION_TRUE’ not handled in switch
794 * warning: enumeration value ‘MSICONDITION_NONE’ not handled in switch
795 */
796#pragma GCC diagnostic push
797#pragma GCC diagnostic ignored "-Wswitch"
798#endif
799 switch (MsiEvaluateCondition(hInstall, szValue))
800 {
801 case MSICONDITION_FALSE:
802 free(szValue);
803 goto cleanup_szDisplayName;
804
805 case MSICONDITION_ERROR:
806 uiResult = ERROR_INVALID_FIELD;
807 msg(M_NONFATAL | M_ERRNO, "%s: MsiEvaluateCondition(\"%ls\") failed",
808 __FUNCTION__, szValue);
809 free(szValue);
810 goto cleanup_szDisplayName;
811 }
812#if defined(__GNUC__) || defined(__clang__)
813#pragma GCC diagnostic pop
814#endif
815 free(szValue);
816
817 /* Component is or should be installed. Schedule adapter creation. */
818 if (schedule_adapter_create(&seqInstall,
819 bRollbackEnabled ? &seqInstallRollback : NULL,
820 szDisplayNameEx, szzHardwareIDs, &iTicks)
821 != ERROR_SUCCESS)
822 {
823 uiResult = ERROR_INSTALL_FAILED;
824 goto cleanup_szDisplayName;
825 }
826 }
827 else
828 {
829 /* Component is installed, but should be degraded to advertised/removed. Schedule
830 * adapter deletition.
831 *
832 * Note: On adapter removal (product is being uninstalled), we tolerate dwResult
833 * error. Better a partial uninstallation than no uninstallation at all.
834 */
835 schedule_adapter_delete(&seqUninstall,
836 bRollbackEnabled ? &seqUninstallCommit : NULL,
837 bRollbackEnabled ? &seqUninstallRollback : NULL,
838 szDisplayNameEx, szzHardwareIDs, &iTicks);
839 }
840
841 /* Arrange the amount of tick space to add to the progress indicator.
842 * Do this within the loop to poll for user cancellation. */
843 MsiRecordSetInteger(hRecordProg, 1, 3 /* OP3 = Add ticks to the expected total number of progress of the progress bar */);
844 MsiRecordSetInteger(hRecordProg, 2, iTicks);
845 if (MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRecordProg) == IDCANCEL)
846 {
847 uiResult = ERROR_INSTALL_USEREXIT;
848 goto cleanup_szDisplayName;
849 }
850 }
851
852cleanup_szDisplayName:
853 free(szDisplayName);
854cleanup_hRecord:
855 MsiCloseHandle(hRecord);
856 if (uiResult != ERROR_SUCCESS)
857 {
858 goto cleanup_hRecordProg;
859 }
860 }
861
862 /* save path to user's temp dir to be used later by deferred actions */
863 WCHAR tmpDir[MAX_PATH];
864 GetTempPath(MAX_PATH, tmpDir);
865
866 WCHAR str[MAX_PATH + 7];
867 swprintf_s(str, _countof(str), L"tmpdir=%ls", tmpDir);
868 msica_arg_seq_add_tail(&seqInstall, str);
869 msica_arg_seq_add_tail(&seqInstallCommit, str);
870 msica_arg_seq_add_tail(&seqInstallRollback, str);
871 msica_arg_seq_add_tail(&seqUninstall, str);
872 msica_arg_seq_add_tail(&seqUninstallCommit, str);
873 msica_arg_seq_add_tail(&seqUninstallRollback, str);
874
875 /* Store deferred custom action parameters. */
876 if ((uiResult = setup_sequence(hInstall, L"InstallTUNTAPAdapters", &seqInstall))
877 != ERROR_SUCCESS
878 || (uiResult = setup_sequence(hInstall, L"InstallTUNTAPAdaptersCommit", &seqInstallCommit))
879 != ERROR_SUCCESS
880 || (uiResult =
881 setup_sequence(hInstall, L"InstallTUNTAPAdaptersRollback", &seqInstallRollback))
882 != ERROR_SUCCESS
883 || (uiResult = setup_sequence(hInstall, L"UninstallTUNTAPAdapters", &seqUninstall))
884 != ERROR_SUCCESS
885 || (uiResult =
886 setup_sequence(hInstall, L"UninstallTUNTAPAdaptersCommit", &seqUninstallCommit))
887 != ERROR_SUCCESS
888 || (uiResult =
889 setup_sequence(hInstall, L"UninstallTUNTAPAdaptersRollback", &seqUninstallRollback))
890 != ERROR_SUCCESS)
891 {
892 goto cleanup_hRecordProg;
893 }
894
895 uiResult = ERROR_SUCCESS;
896
897cleanup_hRecordProg:
898 MsiCloseHandle(hRecordProg);
899cleanup_hViewST_close:
900 MsiViewClose(hViewST);
901cleanup_hViewST:
902 MsiCloseHandle(hViewST);
903cleanup_hDatabase:
904 MsiCloseHandle(hDatabase);
905cleanup_exec_seq:
906 msica_arg_seq_free(&seqInstall);
907 msica_arg_seq_free(&seqInstallCommit);
908 msica_arg_seq_free(&seqInstallRollback);
909 msica_arg_seq_free(&seqUninstall);
910 msica_arg_seq_free(&seqUninstallCommit);
911 msica_arg_seq_free(&seqUninstallRollback);
912 if (bIsCoInitialized)
913 {
914 CoUninitialize();
915 }
916 return uiResult;
917}
918
919
929static BOOL
930parse_guid(_In_z_ LPCWSTR szArg, _Out_ GUID *guid)
931{
932 if (swscanf_s(szArg, _L(PRIXGUID), PRIGUID_PARAM_REF(*guid)) != 11)
933 {
934 msg(M_NONFATAL | M_ERRNO, "%s: swscanf_s(\"%ls\") failed", __FUNCTION__, szArg);
935 return FALSE;
936 }
937 return TRUE;
938}
939
940
949static void
950CreateRebootFile(_In_z_ LPCWSTR szTmpDir)
951{
952 WCHAR path[MAX_PATH];
953 swprintf_s(path, _countof(path), L"%s%s", szTmpDir, FILE_NEED_REBOOT);
954
955 msg(M_WARN, "%s: Reboot required, create reboot indication file \"%ls\"", __FUNCTION__, path);
956
957 HANDLE file =
958 CreateFileW(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
959 if (file == INVALID_HANDLE_VALUE)
960 {
961 msg(M_NONFATAL | M_ERRNO, "%s: CreateFile(\"%ls\") failed", __FUNCTION__, path);
962 }
963 else
964 {
965 CloseHandle(file);
966 }
967}
968
969UINT __stdcall
970ProcessDeferredAction(_In_ MSIHANDLE hInstall)
971{
972#ifdef DLLEXP_EXPORT
973#pragma comment(linker, DLLEXP_EXPORT)
974#endif
975
976 debug_popup(__FUNCTION__);
977
978 UINT uiResult;
979 BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL));
980 WCHAR tmpDir[MAX_PATH] = { 0 };
981
983
984 BOOL bIsCleanup =
985 MsiGetMode(hInstall, MSIRUNMODE_COMMIT) || MsiGetMode(hInstall, MSIRUNMODE_ROLLBACK);
986
987 /* Get sequence arguments. Always Unicode as CommandLineToArgvW() is available as Unicode-only.
988 */
989 LPWSTR szSequence = NULL;
990 uiResult = msi_get_string(hInstall, L"CustomActionData", &szSequence);
991 if (uiResult != ERROR_SUCCESS)
992 {
993 goto cleanup_CoInitialize;
994 }
995 int nArgs;
996 LPWSTR *szArg = CommandLineToArgvW(szSequence, &nArgs);
997 if (szArg == NULL)
998 {
999 uiResult = GetLastError();
1000 msg(M_NONFATAL | M_ERRNO, "%s: CommandLineToArgvW(\"%ls\") failed", __FUNCTION__,
1001 szSequence);
1002 goto cleanup_szSequence;
1003 }
1004
1005 /* Tell the installer to use explicit progress messages. */
1006 MSIHANDLE hRecordProg = MsiCreateRecord(3);
1007 MsiRecordSetInteger(hRecordProg, 1, 1);
1008 MsiRecordSetInteger(hRecordProg, 2, 1);
1009 MsiRecordSetInteger(hRecordProg, 3, 0);
1010 MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRecordProg);
1011
1012 /* Prepare hRecordProg for progress messages. */
1013 MsiRecordSetInteger(hRecordProg, 1, 2);
1014 MsiRecordSetInteger(hRecordProg, 3, 0);
1015
1016 BOOL bRebootRequired = FALSE;
1017
1018 for (int i = 1 /*CommandLineToArgvW injects msiexec.exe as szArg[0]*/; i < nArgs; ++i)
1019 {
1020 DWORD dwResult = ERROR_SUCCESS;
1021
1022 if (wcsncmp(szArg[i], L"create=", 7) == 0)
1023 {
1024 /* Create an adapter with a given name and hardware ID. */
1025 LPWSTR szName = szArg[i] + 7;
1026 LPWSTR szHardwareId = wcschr(szName, L'|');
1027 if (szHardwareId == NULL)
1028 {
1029 goto invalid_argument;
1030 }
1031 szHardwareId[0] = 0;
1032 ++szHardwareId;
1033
1034 {
1035 /* Report the name of the adapter to installer. */
1036 MSIHANDLE hRecord = MsiCreateRecord(4);
1037 MsiRecordSetString(hRecord, 1, L"Creating adapter");
1038 MsiRecordSetString(hRecord, 2, szName);
1039 MsiRecordSetString(hRecord, 3, szHardwareId);
1040 int iResult = MsiProcessMessage(hInstall, INSTALLMESSAGE_ACTIONDATA, hRecord);
1041 MsiCloseHandle(hRecord);
1042 if (iResult == IDCANCEL)
1043 {
1044 uiResult = ERROR_INSTALL_USEREXIT;
1045 goto cleanup;
1046 }
1047 }
1048
1049 GUID guidAdapter;
1050 dwResult = tap_create_adapter(NULL, NULL, szHardwareId, &bRebootRequired, &guidAdapter);
1051 if (dwResult == ERROR_SUCCESS)
1052 {
1053 /* Set adapter name. May fail on some machines, but that is not critical - use
1054 * silent flag to mute messagebox and print error only to log */
1055 tap_set_adapter_name(&guidAdapter, szName, TRUE);
1056 }
1057 }
1058 else if (wcsncmp(szArg[i], L"deleteN=", 8) == 0)
1059 {
1060 /* Delete the adapter by name. */
1061 LPCWSTR szName = szArg[i] + 8;
1062
1063 {
1064 /* Report the name of the adapter to installer. */
1065 MSIHANDLE hRecord = MsiCreateRecord(3);
1066 MsiRecordSetString(hRecord, 1, L"Deleting adapter");
1067 MsiRecordSetString(hRecord, 2, szName);
1068 int iResult = MsiProcessMessage(hInstall, INSTALLMESSAGE_ACTIONDATA, hRecord);
1069 MsiCloseHandle(hRecord);
1070 if (iResult == IDCANCEL)
1071 {
1072 uiResult = ERROR_INSTALL_USEREXIT;
1073 goto cleanup;
1074 }
1075 }
1076
1077 /* Get existing adapters. */
1078 struct tap_adapter_node *pAdapterList = NULL;
1079 dwResult = tap_list_adapters(NULL, NULL, &pAdapterList);
1080 if (dwResult == ERROR_SUCCESS)
1081 {
1082 /* Does the adapter exist? */
1083 for (struct tap_adapter_node *pAdapter = pAdapterList; pAdapter != NULL;
1084 pAdapter = pAdapter->pNext)
1085 {
1086 if (_wcsicmp(szName, pAdapter->szName) == 0)
1087 {
1088 /* Adapter found. */
1089 dwResult = tap_delete_adapter(NULL, &pAdapter->guid, &bRebootRequired);
1090 break;
1091 }
1092 }
1093
1094 tap_free_adapter_list(pAdapterList);
1095 }
1096 }
1097 else if (wcsncmp(szArg[i], L"delete=", 7) == 0)
1098 {
1099 /* Delete the adapter by GUID. */
1100 GUID guid;
1101 if (!parse_guid(szArg[i] + 7, &guid))
1102 {
1103 goto invalid_argument;
1104 }
1105 dwResult = tap_delete_adapter(NULL, &guid, &bRebootRequired);
1106 }
1107 else if (wcsncmp(szArg[i], L"enable=", 7) == 0)
1108 {
1109 /* Enable the adapter. */
1110 GUID guid;
1111 if (!parse_guid(szArg[i] + 7, &guid))
1112 {
1113 goto invalid_argument;
1114 }
1115 dwResult = tap_enable_adapter(NULL, &guid, TRUE, &bRebootRequired);
1116 }
1117 else if (wcsncmp(szArg[i], L"disable=", 8) == 0)
1118 {
1119 /* Disable the adapter. */
1120 GUID guid;
1121 if (!parse_guid(szArg[i] + 8, &guid))
1122 {
1123 goto invalid_argument;
1124 }
1125 dwResult = tap_enable_adapter(NULL, &guid, FALSE, &bRebootRequired);
1126 }
1127 else if (wcsncmp(szArg[i], L"tmpdir=", 7) == 0)
1128 {
1129 wcscpy_s(tmpDir, _countof(tmpDir), szArg[i] + 7);
1130 }
1131 else
1132 {
1133 goto invalid_argument;
1134 }
1135
1136 if (dwResult != ERROR_SUCCESS && !bIsCleanup /* Ignore errors in case of commit/rollback to do as much work as possible. */)
1137 {
1138 uiResult = ERROR_INSTALL_FAILURE;
1139 goto cleanup;
1140 }
1141
1142 /* Report progress and check for user cancellation. */
1143 MsiRecordSetInteger(hRecordProg, 2, MSICA_ADAPTER_TICK_SIZE);
1144 if (MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRecordProg) == IDCANCEL)
1145 {
1146 dwResult = ERROR_INSTALL_USEREXIT;
1147 goto cleanup;
1148 }
1149
1150 continue;
1151
1152invalid_argument:
1153 msg(M_NONFATAL, "%s: Ignoring invalid argument: %ls", __FUNCTION__, szArg[i]);
1154 }
1155
1156cleanup:
1157 if (bRebootRequired && wcslen(tmpDir) > 0)
1158 {
1159 CreateRebootFile(tmpDir);
1160 }
1161 MsiCloseHandle(hRecordProg);
1162 LocalFree(szArg);
1163cleanup_szSequence:
1164 free(szSequence);
1165cleanup_CoInitialize:
1166 if (bIsCoInitialized)
1167 {
1168 CoUninitialize();
1169 }
1170 return uiResult;
1171}
1172
1173UINT __stdcall
1174CheckAndScheduleReboot(_In_ MSIHANDLE hInstall)
1175{
1176#ifdef DLLEXP_EXPORT
1177#pragma comment(linker, DLLEXP_EXPORT)
1178#endif
1179
1180 debug_popup(__FUNCTION__);
1181
1182 BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL));
1183
1185
1186 /* get user-specific temp path, to where we create reboot indication file */
1187 WCHAR tempPath[MAX_PATH];
1188 GetTempPathW(MAX_PATH, tempPath);
1189
1190 /* check if reboot file exists */
1191 WCHAR path[MAX_PATH];
1192 swprintf_s(path, _countof(path), L"%s%s", tempPath, FILE_NEED_REBOOT);
1193 WIN32_FIND_DATA data = { 0 };
1194 HANDLE searchHandle = FindFirstFileW(path, &data);
1195 if (searchHandle != INVALID_HANDLE_VALUE)
1196 {
1197 msg(M_WARN, "%s: Reboot file exists, schedule reboot", __FUNCTION__);
1198
1199 FindClose(searchHandle);
1200 DeleteFileW(path);
1201
1202 MsiSetMode(hInstall, MSIRUNMODE_REBOOTATEND, TRUE);
1203 }
1204
1205 if (bIsCoInitialized)
1206 {
1207 CoUninitialize();
1208 }
1209 return ERROR_SUCCESS;
1210}
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:1039
DWORD tap_set_adapter_name(_In_ LPCGUID pguidAdapter, _In_ LPCWSTR szName, _In_ BOOL bSilent)
Sets adapter name.
Definition tap.c:966
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:665
DWORD tap_delete_adapter(_In_opt_ HWND hwndParent, _In_ LPCGUID pguidAdapter, _Inout_ LPBOOL pbRebootRequired)
Deletes an adapter.
Definition tap.c:907
void tap_free_adapter_list(_In_ struct tap_adapter_node *pAdapterList)
Frees a list of network adapters.
Definition tap.c:1229
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:915
#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)