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