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