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