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