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-2022 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 #elif defined(_MSC_VER)
24 #include <config-msvc.h>
25 #endif
26 #include <winsock2.h> /* Must be included _before_ <windows.h> */
27 
28 #include "openvpnmsica.h"
29 #include "msica_arg.h"
30 #include "msiex.h"
31 
32 #include "../tapctl/basic.h"
33 #include "../tapctl/error.h"
34 #include "../tapctl/tap.h"
35 
36 #include <windows.h>
37 #include <iphlpapi.h>
38 #include <malloc.h>
39 #include <memory.h>
40 #include <msiquery.h>
41 #include <shellapi.h>
42 #include <shlwapi.h>
43 #include <stdbool.h>
44 #include <stdlib.h>
45 #include <tchar.h>
46 #include <setupapi.h>
47 #include <newdev.h>
48 #include <initguid.h>
49 #include <devguid.h>
50 
51 #ifdef _MSC_VER
52 #pragma comment(lib, "advapi32.lib")
53 #pragma comment(lib, "iphlpapi.lib")
54 #pragma comment(lib, "shell32.lib")
55 #pragma comment(lib, "shlwapi.lib")
56 #pragma comment(lib, "version.lib")
57 #endif
58 
59 
64 #define MSICA_ADAPTER_TICK_SIZE (16*1024)
66 #define FILE_NEED_REBOOT L".ovpn_need_reboot"
67 #define CMP_OVPN_DCO_INF L"CMP_ovpn_dco.inf"
68 #define ACTION_ADD_DRIVER L"AddDriver"
69 #define ACTION_DELETE_DRIVER L"DeleteDriver"
70 #define ACTION_NOOP L"Noop"
71 #define FILE_OVPN_DCO_INF L"ovpn-dco.inf"
72 #define OVPN_DCO_HWID L"ovpn-dco"
73 
85 static UINT
87  _In_ MSIHANDLE hInstall,
88  _In_z_ LPCTSTR szProperty,
89  _In_ struct msica_arg_seq *seq)
90 {
91  UINT uiResult;
92  LPTSTR szSequence = msica_arg_seq_join(seq);
93  uiResult = MsiSetProperty(hInstall, szProperty, szSequence);
94  free(szSequence);
95  if (uiResult != ERROR_SUCCESS)
96  {
97  SetLastError(uiResult); /* MSDN does not mention MsiSetProperty() to set GetLastError(). But we do have an error code. Set last error manually. */
98  msg(M_NONFATAL | M_ERRNO, "%s: MsiSetProperty(\"%" PRIsLPTSTR "\") failed", __FUNCTION__, szProperty);
99  return uiResult;
100  }
101  return ERROR_SUCCESS;
102 }
103 
104 
105 #ifdef _DEBUG
106 
114 static void
115 _debug_popup(_In_z_ LPCSTR szFunctionName)
116 {
117  TCHAR szTitle[0x100], szMessage[0x100+MAX_PATH], szProcessPath[MAX_PATH];
118 
119  /* Compose pop-up title. The dialog title will contain function name to ease the process
120  * locating. Mind that Visual Studio displays window titles on the process list. */
121  _stprintf_s(szTitle, _countof(szTitle), TEXT("%hs v%") TEXT(PRIsLPTSTR),
122  szFunctionName, TEXT(PACKAGE_VERSION));
123 
124  /* Get process name. */
125  GetModuleFileName(NULL, szProcessPath, _countof(szProcessPath));
126  LPCTSTR szProcessName = _tcsrchr(szProcessPath, TEXT('\\'));
127  szProcessName = szProcessName ? szProcessName + 1 : szProcessPath;
128 
129  /* Compose the pop-up message. */
130  _stprintf_s(
131  szMessage, _countof(szMessage),
132  TEXT("The %") TEXT(PRIsLPTSTR) TEXT(" process (PID: %u) has started to execute the %hs")
133  TEXT(" custom action.\r\n")
134  TEXT("\r\n")
135  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")
136  TEXT("\r\n")
137  TEXT("If you are not debugging this custom action, you can safely ignore this message."),
138  szProcessName,
139  GetCurrentProcessId(),
140  szFunctionName);
141 
142  MessageBox(NULL, szMessage, szTitle, MB_OK);
143 }
144 
145 #define debug_popup(f) _debug_popup(f)
146 #else /* ifdef _DEBUG */
147 #define debug_popup(f)
148 #endif /* ifdef _DEBUG */
149 
150 
161 static UINT
162 set_openvpnserv_state(_In_ MSIHANDLE hInstall)
163 {
164  UINT uiResult;
165 
166  /* Get Service Control Manager handle. */
167  SC_HANDLE hSCManager = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT);
168  if (hSCManager == NULL)
169  {
170  uiResult = GetLastError();
171  msg(M_NONFATAL | M_ERRNO, "%s: OpenSCManager() failed", __FUNCTION__);
172  return uiResult;
173  }
174 
175  /* Get OpenVPNService service handle. */
176  SC_HANDLE hService = OpenService(hSCManager, TEXT("OpenVPNService"), SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG);
177  if (hService == NULL)
178  {
179  uiResult = GetLastError();
180  if (uiResult == ERROR_SERVICE_DOES_NOT_EXIST)
181  {
182  /* This is not actually an error. */
183  goto cleanup_OpenSCManager;
184  }
185  msg(M_NONFATAL | M_ERRNO, "%s: OpenService(\"OpenVPNService\") failed", __FUNCTION__);
186  goto cleanup_OpenSCManager;
187  }
188 
189  /* Query service status. */
190  SERVICE_STATUS_PROCESS ssp;
191  DWORD dwBufSize;
192  if (QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp, sizeof(ssp), &dwBufSize))
193  {
194  switch (ssp.dwCurrentState)
195  {
196  case SERVICE_START_PENDING:
197  case SERVICE_RUNNING:
198  case SERVICE_STOP_PENDING:
199  case SERVICE_PAUSE_PENDING:
200  case SERVICE_PAUSED:
201  case SERVICE_CONTINUE_PENDING:
202  {
203  /* Service is started (kind of). Set OPENVPNSERVICE property to service PID. */
204  TCHAR szPID[10 /*MAXDWORD in decimal*/ + 1 /*terminator*/];
205  _stprintf_s(
206  szPID, _countof(szPID),
207  TEXT("%u"),
208  ssp.dwProcessId);
209 
210  uiResult = MsiSetProperty(hInstall, TEXT("OPENVPNSERVICE"), szPID);
211  if (uiResult != ERROR_SUCCESS)
212  {
213  SetLastError(uiResult); /* MSDN does not mention MsiSetProperty() to set GetLastError(). But we do have an error code. Set last error manually. */
214  msg(M_NONFATAL | M_ERRNO, "%s: MsiSetProperty(\"OPENVPNSERVICE\") failed", __FUNCTION__);
215  }
216 
217  /* We know user is using the service. Skip auto-start setting check. */
218  goto cleanup_OpenService;
219  }
220  break;
221  }
222  }
223  else
224  {
225  uiResult = GetLastError();
226  msg(M_NONFATAL | M_ERRNO, "%s: QueryServiceStatusEx(\"OpenVPNService\") failed", __FUNCTION__);
227  }
228 
229  /* Service is not started. Is it set to auto-start? */
230  /* MSDN describes the maximum buffer size for QueryServiceConfig() to be 8kB. */
231  /* This is small enough to fit on stack. */
232  BYTE _buffer_8k[8192];
233  LPQUERY_SERVICE_CONFIG pQsc = (LPQUERY_SERVICE_CONFIG)_buffer_8k;
234  dwBufSize = sizeof(_buffer_8k);
235  if (!QueryServiceConfig(hService, pQsc, dwBufSize, &dwBufSize))
236  {
237  uiResult = GetLastError();
238  msg(M_NONFATAL | M_ERRNO, "%s: QueryServiceStatusEx(\"QueryServiceConfig\") failed", __FUNCTION__);
239  goto cleanup_OpenService;
240  }
241 
242  if (pQsc->dwStartType <= SERVICE_AUTO_START)
243  {
244  /* Service is set to auto-start. Set OPENVPNSERVICE property to its path. */
245  uiResult = MsiSetProperty(hInstall, TEXT("OPENVPNSERVICE"), pQsc->lpBinaryPathName);
246  if (uiResult != ERROR_SUCCESS)
247  {
248  SetLastError(uiResult); /* MSDN does not mention MsiSetProperty() to set GetLastError(). But we do have an error code. Set last error manually. */
249  msg(M_NONFATAL | M_ERRNO, "%s: MsiSetProperty(\"OPENVPNSERVICE\") failed", __FUNCTION__);
250  goto cleanup_OpenService;
251  }
252  }
253 
254  uiResult = ERROR_SUCCESS;
255 
256 cleanup_OpenService:
257  CloseServiceHandle(hService);
258 cleanup_OpenSCManager:
259  CloseServiceHandle(hSCManager);
260  return uiResult;
261 }
262 
263 
264 static void
266  _In_ MSIHANDLE hInstall,
267  _In_z_ LPCTSTR szzHardwareIDs,
268  _In_z_ LPCTSTR szAdaptersPropertyName,
269  _In_z_ LPCTSTR szActiveAdaptersPropertyName)
270 {
271  UINT uiResult;
272 
273  /* Get network adapters with given hardware ID. */
274  struct tap_adapter_node *pAdapterList = NULL;
275  uiResult = tap_list_adapters(NULL, szzHardwareIDs, &pAdapterList);
276  if (uiResult != ERROR_SUCCESS)
277  {
278  return;
279  }
280  else if (pAdapterList == NULL)
281  {
282  /* No adapters - no fun. */
283  return;
284  }
285 
286  /* Get IPv4/v6 info for all network adapters. Actually, we're interested in link status only: up/down? */
287  PIP_ADAPTER_ADDRESSES pAdapterAdresses = NULL;
288  ULONG ulAdapterAdressesSize = 16*1024;
289  for (size_t iteration = 0; iteration < 2; iteration++)
290  {
291  pAdapterAdresses = (PIP_ADAPTER_ADDRESSES)malloc(ulAdapterAdressesSize);
292  if (pAdapterAdresses == NULL)
293  {
294  msg(M_NONFATAL, "%s: malloc(%u) failed", __FUNCTION__, ulAdapterAdressesSize);
295  uiResult = ERROR_OUTOFMEMORY; goto cleanup_pAdapterList;
296  }
297 
298  ULONG ulResult = GetAdaptersAddresses(
299  AF_UNSPEC,
300  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,
301  NULL,
302  pAdapterAdresses,
303  &ulAdapterAdressesSize);
304 
305  if (ulResult == ERROR_SUCCESS)
306  {
307  break;
308  }
309 
310  free(pAdapterAdresses);
311  if (ulResult != ERROR_BUFFER_OVERFLOW)
312  {
313  SetLastError(ulResult); /* MSDN does not mention GetAdaptersAddresses() to set GetLastError(). But we do have an error code. Set last error manually. */
314  msg(M_NONFATAL | M_ERRNO, "%s: GetAdaptersAddresses() failed", __FUNCTION__);
315  uiResult = ulResult; goto cleanup_pAdapterList;
316  }
317  }
318 
319  /* Count adapters. */
320  size_t adapter_count = 0;
321  for (struct tap_adapter_node *pAdapter = pAdapterList; pAdapter; pAdapter = pAdapter->pNext)
322  {
323  adapter_count++;
324  }
325 
326  /* Prepare semicolon delimited list of TAP adapter ID(s) and active TAP adapter ID(s). */
327  LPTSTR
328  szAdapters = (LPTSTR)malloc(adapter_count * (38 /*GUID*/ + 1 /*separator/terminator*/) * sizeof(TCHAR)),
329  szAdaptersTail = szAdapters;
330  if (szAdapters == NULL)
331  {
332  msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, adapter_count * (38 /*GUID*/ + 1 /*separator/terminator*/) * sizeof(TCHAR));
333  uiResult = ERROR_OUTOFMEMORY; goto cleanup_pAdapterAdresses;
334  }
335 
336  LPTSTR
337  szAdaptersActive = (LPTSTR)malloc(adapter_count * (38 /*GUID*/ + 1 /*separator/terminator*/) * sizeof(TCHAR)),
338  szAdaptersActiveTail = szAdaptersActive;
339  if (szAdaptersActive == NULL)
340  {
341  msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, adapter_count * (38 /*GUID*/ + 1 /*separator/terminator*/) * sizeof(TCHAR));
342  uiResult = ERROR_OUTOFMEMORY; goto cleanup_szAdapters;
343  }
344 
345  for (struct tap_adapter_node *pAdapter = pAdapterList; pAdapter; pAdapter = pAdapter->pNext)
346  {
347  /* Convert adapter GUID to UTF-16 string. (LPOLESTR defaults to LPWSTR) */
348  LPOLESTR szAdapterId = NULL;
349  StringFromIID((REFIID)&pAdapter->guid, &szAdapterId);
350 
351  /* Append to the list of TAP adapter ID(s). */
352  if (szAdapters < szAdaptersTail)
353  {
354  *(szAdaptersTail++) = TEXT(';');
355  }
356  memcpy(szAdaptersTail, szAdapterId, 38 * sizeof(TCHAR));
357  szAdaptersTail += 38;
358 
359  /* If this adapter is active (connected), add it to the list of active TAP adapter ID(s). */
360  for (PIP_ADAPTER_ADDRESSES p = pAdapterAdresses; p; p = p->Next)
361  {
362  OLECHAR szId[38 /*GUID*/ + 1 /*terminator*/];
363  GUID guid;
364  if (MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, p->AdapterName, -1, szId, _countof(szId)) > 0
365  && SUCCEEDED(IIDFromString(szId, &guid))
366  && memcmp(&guid, &pAdapter->guid, sizeof(GUID)) == 0)
367  {
368  if (p->OperStatus == IfOperStatusUp)
369  {
370  /* This TAP adapter is active (connected). */
371  if (szAdaptersActive < szAdaptersActiveTail)
372  {
373  *(szAdaptersActiveTail++) = TEXT(';');
374  }
375  memcpy(szAdaptersActiveTail, szAdapterId, 38 * sizeof(TCHAR));
376  szAdaptersActiveTail += 38;
377  }
378  break;
379  }
380  }
381  CoTaskMemFree(szAdapterId);
382  }
383  szAdaptersTail [0] = 0;
384  szAdaptersActiveTail[0] = 0;
385 
386  /* Set Installer properties. */
387  uiResult = MsiSetProperty(hInstall, szAdaptersPropertyName, szAdapters);
388  if (uiResult != ERROR_SUCCESS)
389  {
390  SetLastError(uiResult); /* MSDN does not mention MsiSetProperty() to set GetLastError(). But we do have an error code. Set last error manually. */
391  msg(M_NONFATAL | M_ERRNO, "%s: MsiSetProperty(\"%s\") failed", __FUNCTION__, szAdaptersPropertyName);
392  goto cleanup_szAdaptersActive;
393  }
394  uiResult = MsiSetProperty(hInstall, szActiveAdaptersPropertyName, szAdaptersActive);
395  if (uiResult != ERROR_SUCCESS)
396  {
397  SetLastError(uiResult); /* MSDN does not mention MsiSetProperty() to set GetLastError(). But we do have an error code. Set last error manually. */
398  msg(M_NONFATAL | M_ERRNO, "%s: MsiSetProperty(\"%s\") failed", __FUNCTION__, szActiveAdaptersPropertyName);
399  goto cleanup_szAdaptersActive;
400  }
401 
402 cleanup_szAdaptersActive:
403  free(szAdaptersActive);
404 cleanup_szAdapters:
405  free(szAdapters);
406 cleanup_pAdapterAdresses:
407  free(pAdapterAdresses);
408 cleanup_pAdapterList:
409  tap_free_adapter_list(pAdapterList);
410 }
411 
412 
413 UINT __stdcall
414 FindSystemInfo(_In_ MSIHANDLE hInstall)
415 {
416 #ifdef _MSC_VER
417 #pragma comment(linker, DLLEXP_EXPORT)
418 #endif
419 
420  debug_popup(__FUNCTION__);
421 
422  BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL));
423 
425 
426  set_openvpnserv_state(hInstall);
428  hInstall,
429  TEXT("root\\") TEXT(TAP_WIN_COMPONENT_ID) TEXT("\0") TEXT(TAP_WIN_COMPONENT_ID) TEXT("\0"),
430  TEXT("TAPWINDOWS6ADAPTERS"),
431  TEXT("ACTIVETAPWINDOWS6ADAPTERS"));
433  hInstall,
434  TEXT("Wintun") TEXT("\0"),
435  TEXT("WINTUNADAPTERS"),
436  TEXT("ACTIVEWINTUNADAPTERS"));
438  hInstall,
439  TEXT("ovpn-dco") TEXT("\0"),
440  TEXT("OVPNDCOAPTERS"),
441  TEXT("ACTIVEOVPNDCOADAPTERS"));
442 
443  if (bIsCoInitialized)
444  {
445  CoUninitialize();
446  }
447  return ERROR_SUCCESS;
448 }
449 
450 
451 UINT __stdcall
452 CloseOpenVPNGUI(_In_ MSIHANDLE hInstall)
453 {
454 #ifdef _MSC_VER
455 #pragma comment(linker, DLLEXP_EXPORT)
456 #endif
457  UNREFERENCED_PARAMETER(hInstall); /* This CA is does not interact with MSI session (report errors, access properties, tables, etc.). */
458 
459  debug_popup(__FUNCTION__);
460 
461  /* Find OpenVPN GUI window. */
462  HWND hWnd = FindWindow(TEXT("OpenVPN-GUI"), NULL);
463  if (hWnd)
464  {
465  /* Ask it to close and wait for 100ms. Unfortunately, this will succeed only for recent OpenVPN GUI that do not run elevated. */
466  SendMessage(hWnd, WM_CLOSE, 0, 0);
467  Sleep(100);
468  }
469 
470  return ERROR_SUCCESS;
471 }
472 
473 
474 UINT __stdcall
475 StartOpenVPNGUI(_In_ MSIHANDLE hInstall)
476 {
477 #ifdef _MSC_VER
478 #pragma comment(linker, DLLEXP_EXPORT)
479 #endif
480 
481  debug_popup(__FUNCTION__);
482 
483  UINT uiResult;
484  BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL));
485 
487 
488  /* Create and populate a MSI record. */
489  MSIHANDLE hRecord = MsiCreateRecord(1);
490  if (!hRecord)
491  {
492  uiResult = ERROR_INVALID_HANDLE;
493  msg(M_NONFATAL, "%s: MsiCreateRecord failed", __FUNCTION__);
494  goto cleanup_CoInitialize;
495  }
496  uiResult = MsiRecordSetString(hRecord, 0, TEXT("\"[#bin.openvpn_gui.exe]\""));
497  if (uiResult != ERROR_SUCCESS)
498  {
499  SetLastError(uiResult); /* MSDN does not mention MsiRecordSetString() to set GetLastError(). But we do have an error code. Set last error manually. */
500  msg(M_NONFATAL | M_ERRNO, "%s: MsiRecordSetString failed", __FUNCTION__);
501  goto cleanup_MsiCreateRecord;
502  }
503 
504  /* Format string. */
505  TCHAR szStackBuf[MAX_PATH];
506  DWORD dwPathSize = _countof(szStackBuf);
507  LPTSTR szPath = szStackBuf;
508  uiResult = MsiFormatRecord(hInstall, hRecord, szPath, &dwPathSize);
509  if (uiResult == ERROR_MORE_DATA)
510  {
511  /* Allocate buffer on heap (+1 for terminator), and retry. */
512  szPath = (LPTSTR)malloc((++dwPathSize) * sizeof(TCHAR));
513  if (szPath == NULL)
514  {
515  msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwPathSize * sizeof(TCHAR));
516  uiResult = ERROR_OUTOFMEMORY; goto cleanup_MsiCreateRecord;
517  }
518 
519  uiResult = MsiFormatRecord(hInstall, hRecord, szPath, &dwPathSize);
520  }
521  if (uiResult != ERROR_SUCCESS)
522  {
523  SetLastError(uiResult); /* MSDN does not mention MsiFormatRecord() to set GetLastError(). But we do have an error code. Set last error manually. */
524  msg(M_NONFATAL | M_ERRNO, "%s: MsiFormatRecord failed", __FUNCTION__);
525  goto cleanup_malloc_szPath;
526  }
527 
528  /* Launch the OpenVPN GUI. */
529  SHELLEXECUTEINFO sei = {
530  .cbSize = sizeof(SHELLEXECUTEINFO),
531  .fMask = SEE_MASK_FLAG_NO_UI, /* Don't show error UI, we'll display it. */
532  .lpFile = szPath,
533  .nShow = SW_SHOWNORMAL
534  };
535  if (!ShellExecuteEx(&sei))
536  {
537  uiResult = GetLastError();
538  msg(M_NONFATAL | M_ERRNO, "%s: ShellExecuteEx(%s) failed", __FUNCTION__, szPath);
539  goto cleanup_malloc_szPath;
540  }
541 
542  uiResult = ERROR_SUCCESS;
543 
544 cleanup_malloc_szPath:
545  if (szPath != szStackBuf)
546  {
547  free(szPath);
548  }
549 cleanup_MsiCreateRecord:
550  MsiCloseHandle(hRecord);
551 cleanup_CoInitialize:
552  if (bIsCoInitialized)
553  {
554  CoUninitialize();
555  }
556  return uiResult;
557 }
558 
559 
580 static DWORD
582  _Inout_ struct msica_arg_seq *seq,
583  _Inout_opt_ struct msica_arg_seq *seqRollback,
584  _In_z_ LPCTSTR szDisplayName,
585  _In_z_ LPCTSTR szHardwareId,
586  _Inout_ int *iTicks)
587 {
588  /* Get existing network adapters. */
589  struct tap_adapter_node *pAdapterList = NULL;
590  DWORD dwResult = tap_list_adapters(NULL, NULL, &pAdapterList);
591  if (dwResult != ERROR_SUCCESS)
592  {
593  return dwResult;
594  }
595 
596  /* Does adapter exist? */
597  for (struct tap_adapter_node *pAdapterOther = pAdapterList;; pAdapterOther = pAdapterOther->pNext)
598  {
599  if (pAdapterOther == NULL)
600  {
601  /* No adapter with a same name found. */
602  TCHAR szArgument[10 /*create=""|deleteN=""*/ + MAX_PATH /*szDisplayName*/ + 1 /*|*/ + MAX_PATH /*szHardwareId*/ + 1 /*terminator*/];
603 
604  /* InstallTUNTAPAdapters will create the adapter. */
605  _stprintf_s(
606  szArgument, _countof(szArgument),
607  TEXT("create=\"%.*s|%.*s\""),
608  MAX_PATH, szDisplayName,
609  MAX_PATH, szHardwareId);
610  msica_arg_seq_add_tail(seq, szArgument);
611 
612  if (seqRollback)
613  {
614  /* InstallTUNTAPAdaptersRollback will delete the adapter. */
615  _stprintf_s(
616  szArgument, _countof(szArgument),
617  TEXT("deleteN=\"%.*s\""),
618  MAX_PATH, szDisplayName);
619  msica_arg_seq_add_head(seqRollback, szArgument);
620  }
621 
622  *iTicks += MSICA_ADAPTER_TICK_SIZE;
623  break;
624  }
625  else if (_tcsicmp(szDisplayName, pAdapterOther->szName) == 0)
626  {
627  /* Adapter with a same name found. */
628  for (LPCTSTR hwid = pAdapterOther->szzHardwareIDs;; hwid += _tcslen(hwid) + 1)
629  {
630  if (hwid[0] == 0)
631  {
632  /* This adapter has a different hardware ID. */
633  msg(M_NONFATAL, "%s: Adapter with name \"%" PRIsLPTSTR "\" already exists", __FUNCTION__, pAdapterOther->szName);
634  dwResult = ERROR_ALREADY_EXISTS;
635  goto cleanup_pAdapterList;
636  }
637  else if (_tcsicmp(hwid, szHardwareId) == 0)
638  {
639  /* This is an adapter with the requested hardware ID. We already have what we want! */
640  break;
641  }
642  }
643  break; /* Adapter names are unique. There should be no other adapter with this name. */
644  }
645  }
646 
647 cleanup_pAdapterList:
648  tap_free_adapter_list(pAdapterList);
649  return dwResult;
650 }
651 
652 
680 static DWORD
682  _Inout_ struct msica_arg_seq *seq,
683  _Inout_opt_ struct msica_arg_seq *seqCommit,
684  _Inout_opt_ struct msica_arg_seq *seqRollback,
685  _In_z_ LPCTSTR szDisplayName,
686  _In_z_ LPCTSTR szzHardwareIDs,
687  _Inout_ int *iTicks)
688 {
689  /* Get adapters with given hardware ID. */
690  struct tap_adapter_node *pAdapterList = NULL;
691  DWORD dwResult = tap_list_adapters(NULL, szzHardwareIDs, &pAdapterList);
692  if (dwResult != ERROR_SUCCESS)
693  {
694  return dwResult;
695  }
696 
697  /* Does adapter exist? */
698  for (struct tap_adapter_node *pAdapter = pAdapterList; pAdapter != NULL; pAdapter = pAdapter->pNext)
699  {
700  if (_tcsicmp(szDisplayName, pAdapter->szName) == 0)
701  {
702  /* Adapter found. */
703  TCHAR szArgument[8 /*disable=|enable=|delete=*/ + 38 /*{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}*/ + 1 /*terminator*/];
704  if (seqCommit && seqRollback)
705  {
706  /* UninstallTUNTAPAdapters will disable the adapter. */
707  _stprintf_s(
708  szArgument, _countof(szArgument),
709  TEXT("disable=") TEXT(PRIXGUID),
710  PRIGUID_PARAM(pAdapter->guid));
711  msica_arg_seq_add_tail(seq, szArgument);
712 
713  /* UninstallTUNTAPAdaptersRollback will re-enable the adapter. */
714  _stprintf_s(
715  szArgument, _countof(szArgument),
716  TEXT("enable=") TEXT(PRIXGUID),
717  PRIGUID_PARAM(pAdapter->guid));
718  msica_arg_seq_add_head(seqRollback, szArgument);
719 
720  /* UninstallTUNTAPAdaptersCommit will delete the adapter. */
721  _stprintf_s(
722  szArgument, _countof(szArgument),
723  TEXT("delete=") TEXT(PRIXGUID),
724  PRIGUID_PARAM(pAdapter->guid));
725  msica_arg_seq_add_tail(seqCommit, szArgument);
726  }
727  else
728  {
729  /* UninstallTUNTAPAdapters will delete the adapter. */
730  _stprintf_s(
731  szArgument, _countof(szArgument),
732  TEXT("delete=") TEXT(PRIXGUID),
733  PRIGUID_PARAM(pAdapter->guid));
734  msica_arg_seq_add_tail(seq, szArgument);
735  }
736 
737  iTicks += MSICA_ADAPTER_TICK_SIZE;
738  break; /* Adapter names are unique. There should be no other adapter with this name. */
739  }
740  }
741 
742  tap_free_adapter_list(pAdapterList);
743  return dwResult;
744 }
745 
746 
747 UINT __stdcall
748 EvaluateTUNTAPAdapters(_In_ MSIHANDLE hInstall)
749 {
750 #ifdef _MSC_VER
751 #pragma comment(linker, DLLEXP_EXPORT)
752 #endif
753 
754  debug_popup(__FUNCTION__);
755 
756  UINT uiResult;
757  BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL));
758 
760 
761  struct msica_arg_seq
762  seqInstall,
763  seqInstallCommit,
764  seqInstallRollback,
765  seqUninstall,
766  seqUninstallCommit,
767  seqUninstallRollback;
768  msica_arg_seq_init(&seqInstall);
769  msica_arg_seq_init(&seqInstallCommit);
770  msica_arg_seq_init(&seqInstallRollback);
771  msica_arg_seq_init(&seqUninstall);
772  msica_arg_seq_init(&seqUninstallCommit);
773  msica_arg_seq_init(&seqUninstallRollback);
774 
775  /* Check rollback state. */
776  bool bRollbackEnabled = MsiEvaluateCondition(hInstall, TEXT("RollbackDisabled")) != MSICONDITION_TRUE;
777 
778  /* Open MSI database. */
779  MSIHANDLE hDatabase = MsiGetActiveDatabase(hInstall);
780  if (hDatabase == 0)
781  {
782  msg(M_NONFATAL, "%s: MsiGetActiveDatabase failed", __FUNCTION__);
783  uiResult = ERROR_INVALID_HANDLE;
784  goto cleanup_exec_seq;
785  }
786 
787  /* Check if TUNTAPAdapter table exists. If it doesn't exist, there's nothing to do. */
788  switch (MsiDatabaseIsTablePersistent(hDatabase, TEXT("TUNTAPAdapter")))
789  {
790  case MSICONDITION_FALSE:
791  case MSICONDITION_TRUE: break;
792 
793  default:
794  uiResult = ERROR_SUCCESS;
795  goto cleanup_hDatabase;
796  }
797 
798  /* Prepare a query to get a list/view of adapters. */
799  MSIHANDLE hViewST = 0;
800  LPCTSTR szQuery = TEXT("SELECT `Adapter`,`DisplayName`,`Condition`,`Component_`,`HardwareId` FROM `TUNTAPAdapter`");
801  uiResult = MsiDatabaseOpenView(hDatabase, szQuery, &hViewST);
802  if (uiResult != ERROR_SUCCESS)
803  {
804  SetLastError(uiResult); /* MSDN does not mention MsiDatabaseOpenView() to set GetLastError(). But we do have an error code. Set last error manually. */
805  msg(M_NONFATAL | M_ERRNO, "%s: MsiDatabaseOpenView(\"%" PRIsLPTSTR "\") failed", __FUNCTION__, szQuery);
806  goto cleanup_hDatabase;
807  }
808 
809  /* Execute query! */
810  uiResult = MsiViewExecute(hViewST, 0);
811  if (uiResult != ERROR_SUCCESS)
812  {
813  SetLastError(uiResult); /* MSDN does not mention MsiViewExecute() to set GetLastError(). But we do have an error code. Set last error manually. */
814  msg(M_NONFATAL | M_ERRNO, "%s: MsiViewExecute(\"%" PRIsLPTSTR "\") failed", __FUNCTION__, szQuery);
815  goto cleanup_hViewST;
816  }
817 
818  /* Create a record to report progress with. */
819  MSIHANDLE hRecordProg = MsiCreateRecord(2);
820  if (!hRecordProg)
821  {
822  uiResult = ERROR_INVALID_HANDLE;
823  msg(M_NONFATAL, "%s: MsiCreateRecord failed", __FUNCTION__);
824  goto cleanup_hViewST_close;
825  }
826 
827  for (;; )
828  {
829  /* Fetch one record from the view. */
830  MSIHANDLE hRecord = 0;
831  uiResult = MsiViewFetch(hViewST, &hRecord);
832  if (uiResult == ERROR_NO_MORE_ITEMS)
833  {
834  uiResult = ERROR_SUCCESS;
835  break;
836  }
837  else if (uiResult != ERROR_SUCCESS)
838  {
839  SetLastError(uiResult); /* MSDN does not mention MsiViewFetch() to set GetLastError(). But we do have an error code. Set last error manually. */
840  msg(M_NONFATAL | M_ERRNO, "%s: MsiViewFetch failed", __FUNCTION__);
841  goto cleanup_hRecordProg;
842  }
843 
844  INSTALLSTATE iInstalled, iAction;
845  {
846  /* Read adapter component ID (`Component_` is field #4). */
847  LPTSTR szValue = NULL;
848  uiResult = msi_get_record_string(hRecord, 4, &szValue);
849  if (uiResult != ERROR_SUCCESS)
850  {
851  goto cleanup_hRecord;
852  }
853 
854  /* Get the component state. */
855  uiResult = MsiGetComponentState(hInstall, szValue, &iInstalled, &iAction);
856  if (uiResult != ERROR_SUCCESS)
857  {
858  SetLastError(uiResult); /* MSDN does not mention MsiGetComponentState() to set GetLastError(). But we do have an error code. Set last error manually. */
859  msg(M_NONFATAL | M_ERRNO, "%s: MsiGetComponentState(\"%" PRIsLPTSTR "\") failed", __FUNCTION__, szValue);
860  free(szValue);
861  goto cleanup_hRecord;
862  }
863  free(szValue);
864  }
865 
866  /* Get adapter display name (`DisplayName` is field #2). */
867  LPTSTR szDisplayName = NULL;
868  uiResult = msi_format_field(hInstall, hRecord, 2, &szDisplayName);
869  if (uiResult != ERROR_SUCCESS)
870  {
871  goto cleanup_hRecord;
872  }
873  /* `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". */
874  LPTSTR szDisplayNameEx = _tcschr(szDisplayName, TEXT('|'));
875  szDisplayNameEx = szDisplayNameEx != NULL ? szDisplayNameEx + 1 : szDisplayName;
876 
877  /* Get adapter hardware ID (`HardwareId` is field #5). */
878  TCHAR szzHardwareIDs[0x100] = { 0 };
879  {
880  LPTSTR szHwId = NULL;
881  uiResult = msi_get_record_string(hRecord, 5, &szHwId);
882  if (uiResult != ERROR_SUCCESS)
883  {
884  goto cleanup_szDisplayName;
885  }
886  memcpy_s(szzHardwareIDs, sizeof(szzHardwareIDs) - 2*sizeof(TCHAR) /*requires double zero termination*/, szHwId, _tcslen(szHwId)*sizeof(TCHAR));
887  free(szHwId);
888  }
889 
890  if (iAction > INSTALLSTATE_BROKEN)
891  {
892  int iTicks = 0;
893 
894  if (iAction >= INSTALLSTATE_LOCAL)
895  {
896  /* Read and evaluate adapter condition (`Condition` is field #3). */
897  LPTSTR szValue = NULL;
898  uiResult = msi_get_record_string(hRecord, 3, &szValue);
899  if (uiResult != ERROR_SUCCESS)
900  {
901  goto cleanup_szDisplayName;
902  }
903 #ifdef __GNUC__
904 /*
905  * warning: enumeration value ‘MSICONDITION_TRUE’ not handled in switch
906  * warning: enumeration value ‘MSICONDITION_NONE’ not handled in switch
907  */
908 #pragma GCC diagnostic push
909 #pragma GCC diagnostic ignored "-Wswitch"
910 #endif
911  switch (MsiEvaluateCondition(hInstall, szValue))
912  {
913  case MSICONDITION_FALSE:
914  free(szValue);
915  goto cleanup_szDisplayName;
916 
917  case MSICONDITION_ERROR:
918  uiResult = ERROR_INVALID_FIELD;
919  msg(M_NONFATAL | M_ERRNO, "%s: MsiEvaluateCondition(\"%" PRIsLPTSTR "\") failed", __FUNCTION__, szValue);
920  free(szValue);
921  goto cleanup_szDisplayName;
922  }
923 #ifdef __GNUC__
924 #pragma GCC diagnostic pop
925 #endif
926  free(szValue);
927 
928  /* Component is or should be installed. Schedule adapter creation. */
930  &seqInstall,
931  bRollbackEnabled ? &seqInstallRollback : NULL,
932  szDisplayNameEx,
933  szzHardwareIDs,
934  &iTicks) != ERROR_SUCCESS)
935  {
936  uiResult = ERROR_INSTALL_FAILED;
937  goto cleanup_szDisplayName;
938  }
939  }
940  else
941  {
942  /* Component is installed, but should be degraded to advertised/removed. Schedule adapter deletition.
943  *
944  * Note: On adapter removal (product is being uninstalled), we tolerate dwResult error.
945  * Better a partial uninstallation than no uninstallation at all.
946  */
948  &seqUninstall,
949  bRollbackEnabled ? &seqUninstallCommit : NULL,
950  bRollbackEnabled ? &seqUninstallRollback : NULL,
951  szDisplayNameEx,
952  szzHardwareIDs,
953  &iTicks);
954  }
955 
956  /* Arrange the amount of tick space to add to the progress indicator.
957  * Do this within the loop to poll for user cancellation. */
958  MsiRecordSetInteger(hRecordProg, 1, 3 /* OP3 = Add ticks to the expected total number of progress of the progress bar */);
959  MsiRecordSetInteger(hRecordProg, 2, iTicks);
960  if (MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRecordProg) == IDCANCEL)
961  {
962  uiResult = ERROR_INSTALL_USEREXIT;
963  goto cleanup_szDisplayName;
964  }
965  }
966 
967 cleanup_szDisplayName:
968  free(szDisplayName);
969 cleanup_hRecord:
970  MsiCloseHandle(hRecord);
971  if (uiResult != ERROR_SUCCESS)
972  {
973  goto cleanup_hRecordProg;
974  }
975  }
976 
977  /* save path to user's temp dir to be used later by deferred actions */
978  TCHAR tmpDir[MAX_PATH];
979  GetTempPath(MAX_PATH, tmpDir);
980 
981  TCHAR str[MAX_PATH + 7];
982  _stprintf_s(str, _countof(str), TEXT("tmpdir=%") TEXT(PRIsLPTSTR), tmpDir);
983  msica_arg_seq_add_tail(&seqInstall, str);
984  msica_arg_seq_add_tail(&seqInstallCommit, str);
985  msica_arg_seq_add_tail(&seqInstallRollback, str);
986  msica_arg_seq_add_tail(&seqUninstall, str);
987  msica_arg_seq_add_tail(&seqUninstallCommit, str);
988  msica_arg_seq_add_tail(&seqUninstallRollback, str);
989 
990  /* Store deferred custom action parameters. */
991  if ((uiResult = setup_sequence(hInstall, TEXT("InstallTUNTAPAdapters" ), &seqInstall )) != ERROR_SUCCESS
992  || (uiResult = setup_sequence(hInstall, TEXT("InstallTUNTAPAdaptersCommit" ), &seqInstallCommit )) != ERROR_SUCCESS
993  || (uiResult = setup_sequence(hInstall, TEXT("InstallTUNTAPAdaptersRollback" ), &seqInstallRollback )) != ERROR_SUCCESS
994  || (uiResult = setup_sequence(hInstall, TEXT("UninstallTUNTAPAdapters" ), &seqUninstall )) != ERROR_SUCCESS
995  || (uiResult = setup_sequence(hInstall, TEXT("UninstallTUNTAPAdaptersCommit" ), &seqUninstallCommit )) != ERROR_SUCCESS
996  || (uiResult = setup_sequence(hInstall, TEXT("UninstallTUNTAPAdaptersRollback"), &seqUninstallRollback)) != ERROR_SUCCESS)
997  {
998  goto cleanup_hRecordProg;
999  }
1000 
1001  uiResult = ERROR_SUCCESS;
1002 
1003 cleanup_hRecordProg:
1004  MsiCloseHandle(hRecordProg);
1005 cleanup_hViewST_close:
1006  MsiViewClose(hViewST);
1007 cleanup_hViewST:
1008  MsiCloseHandle(hViewST);
1009 cleanup_hDatabase:
1010  MsiCloseHandle(hDatabase);
1011 cleanup_exec_seq:
1012  msica_arg_seq_free(&seqInstall);
1013  msica_arg_seq_free(&seqInstallCommit);
1014  msica_arg_seq_free(&seqInstallRollback);
1015  msica_arg_seq_free(&seqUninstall);
1016  msica_arg_seq_free(&seqUninstallCommit);
1017  msica_arg_seq_free(&seqUninstallRollback);
1018  if (bIsCoInitialized)
1019  {
1020  CoUninitialize();
1021  }
1022  return uiResult;
1023 }
1024 
1025 
1035 static BOOL
1037  _In_z_ LPCWSTR szArg,
1038  _Out_ GUID *guid)
1039 {
1040  if (swscanf_s(szArg, _L(PRIXGUID), PRIGUID_PARAM_REF(*guid)) != 11)
1041  {
1042  msg(M_NONFATAL | M_ERRNO, "%s: swscanf_s(\"%ls\") failed", __FUNCTION__, szArg);
1043  return FALSE;
1044  }
1045  return TRUE;
1046 }
1047 
1048 
1057 static void
1058 CreateRebootFile(_In_z_ LPCWSTR szTmpDir)
1059 {
1060  WCHAR path[MAX_PATH];
1061  swprintf_s(path, _countof(path), L"%s%s", szTmpDir, FILE_NEED_REBOOT);
1062 
1063  msg(M_WARN, "%s: Reboot required, create reboot indication file \"%ls\"", __FUNCTION__, path);
1064 
1065  HANDLE file = CreateFileW(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
1066  if (file == INVALID_HANDLE_VALUE)
1067  {
1068  msg(M_NONFATAL | M_ERRNO, "%s: CreateFile(\"%ls\") failed", __FUNCTION__, path);
1069  }
1070  else
1071  {
1072  CloseHandle(file);
1073  }
1074 }
1075 
1076 UINT __stdcall
1077 ProcessDeferredAction(_In_ MSIHANDLE hInstall)
1078 {
1079 #ifdef _MSC_VER
1080 #pragma comment(linker, DLLEXP_EXPORT)
1081 #endif
1082 
1083  debug_popup(__FUNCTION__);
1084 
1085  UINT uiResult;
1086  BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL));
1087  WCHAR tmpDir[MAX_PATH] = {0};
1088 
1090 
1091  BOOL bIsCleanup = MsiGetMode(hInstall, MSIRUNMODE_COMMIT) || MsiGetMode(hInstall, MSIRUNMODE_ROLLBACK);
1092 
1093  /* Get sequence arguments. Always Unicode as CommandLineToArgvW() is available as Unicode-only. */
1094  LPWSTR szSequence = NULL;
1095  uiResult = msi_get_string(hInstall, L"CustomActionData", &szSequence);
1096  if (uiResult != ERROR_SUCCESS)
1097  {
1098  goto cleanup_CoInitialize;
1099  }
1100  int nArgs;
1101  LPWSTR *szArg = CommandLineToArgvW(szSequence, &nArgs);
1102  if (szArg == NULL)
1103  {
1104  uiResult = GetLastError();
1105  msg(M_NONFATAL | M_ERRNO, "%s: CommandLineToArgvW(\"%ls\") failed", __FUNCTION__, szSequence);
1106  goto cleanup_szSequence;
1107  }
1108 
1109  /* Tell the installer to use explicit progress messages. */
1110  MSIHANDLE hRecordProg = MsiCreateRecord(3);
1111  MsiRecordSetInteger(hRecordProg, 1, 1);
1112  MsiRecordSetInteger(hRecordProg, 2, 1);
1113  MsiRecordSetInteger(hRecordProg, 3, 0);
1114  MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRecordProg);
1115 
1116  /* Prepare hRecordProg for progress messages. */
1117  MsiRecordSetInteger(hRecordProg, 1, 2);
1118  MsiRecordSetInteger(hRecordProg, 3, 0);
1119 
1120  BOOL bRebootRequired = FALSE;
1121 
1122  for (int i = 1 /*CommandLineToArgvW injects msiexec.exe as szArg[0]*/; i < nArgs; ++i)
1123  {
1124  DWORD dwResult = ERROR_SUCCESS;
1125 
1126  if (wcsncmp(szArg[i], L"create=", 7) == 0)
1127  {
1128  /* Create an adapter with a given name and hardware ID. */
1129  LPWSTR szName = szArg[i] + 7;
1130  LPWSTR szHardwareId = wcschr(szName, L'|');
1131  if (szHardwareId == NULL)
1132  {
1133  goto invalid_argument;
1134  }
1135  szHardwareId[0] = 0;
1136  ++szHardwareId;
1137 
1138  {
1139  /* Report the name of the adapter to installer. */
1140  MSIHANDLE hRecord = MsiCreateRecord(4);
1141  MsiRecordSetString(hRecord, 1, TEXT("Creating adapter"));
1142  MsiRecordSetString(hRecord, 2, szName);
1143  MsiRecordSetString(hRecord, 3, szHardwareId);
1144  int iResult = MsiProcessMessage(hInstall, INSTALLMESSAGE_ACTIONDATA, hRecord);
1145  MsiCloseHandle(hRecord);
1146  if (iResult == IDCANCEL)
1147  {
1148  uiResult = ERROR_INSTALL_USEREXIT;
1149  goto cleanup;
1150  }
1151  }
1152 
1153  GUID guidAdapter;
1154  dwResult = tap_create_adapter(NULL, NULL, szHardwareId, &bRebootRequired, &guidAdapter);
1155  if (dwResult == ERROR_SUCCESS)
1156  {
1157  /* Set adapter name. May fail on some machines, but that is not critical - use silent
1158  * flag to mute messagebox and print error only to log */
1159  tap_set_adapter_name(&guidAdapter, szName, TRUE);
1160  }
1161  }
1162  else if (wcsncmp(szArg[i], L"deleteN=", 8) == 0)
1163  {
1164  /* Delete the adapter by name. */
1165  LPCWSTR szName = szArg[i] + 8;
1166 
1167  {
1168  /* Report the name of the adapter to installer. */
1169  MSIHANDLE hRecord = MsiCreateRecord(3);
1170  MsiRecordSetString(hRecord, 1, TEXT("Deleting adapter"));
1171  MsiRecordSetString(hRecord, 2, szName);
1172  int iResult = MsiProcessMessage(hInstall, INSTALLMESSAGE_ACTIONDATA, hRecord);
1173  MsiCloseHandle(hRecord);
1174  if (iResult == IDCANCEL)
1175  {
1176  uiResult = ERROR_INSTALL_USEREXIT;
1177  goto cleanup;
1178  }
1179  }
1180 
1181  /* Get existing adapters. */
1182  struct tap_adapter_node *pAdapterList = NULL;
1183  dwResult = tap_list_adapters(NULL, NULL, &pAdapterList);
1184  if (dwResult == ERROR_SUCCESS)
1185  {
1186  /* Does the adapter exist? */
1187  for (struct tap_adapter_node *pAdapter = pAdapterList; pAdapter != NULL; pAdapter = pAdapter->pNext)
1188  {
1189  if (_tcsicmp(szName, pAdapter->szName) == 0)
1190  {
1191  /* Adapter found. */
1192  dwResult = tap_delete_adapter(NULL, &pAdapter->guid, &bRebootRequired);
1193  break;
1194  }
1195  }
1196 
1197  tap_free_adapter_list(pAdapterList);
1198  }
1199  }
1200  else if (wcsncmp(szArg[i], L"delete=", 7) == 0)
1201  {
1202  /* Delete the adapter by GUID. */
1203  GUID guid;
1204  if (!parse_guid(szArg[i] + 7, &guid))
1205  {
1206  goto invalid_argument;
1207  }
1208  dwResult = tap_delete_adapter(NULL, &guid, &bRebootRequired);
1209  }
1210  else if (wcsncmp(szArg[i], L"enable=", 7) == 0)
1211  {
1212  /* Enable the adapter. */
1213  GUID guid;
1214  if (!parse_guid(szArg[i] + 7, &guid))
1215  {
1216  goto invalid_argument;
1217  }
1218  dwResult = tap_enable_adapter(NULL, &guid, TRUE, &bRebootRequired);
1219  }
1220  else if (wcsncmp(szArg[i], L"disable=", 8) == 0)
1221  {
1222  /* Disable the adapter. */
1223  GUID guid;
1224  if (!parse_guid(szArg[i] + 8, &guid))
1225  {
1226  goto invalid_argument;
1227  }
1228  dwResult = tap_enable_adapter(NULL, &guid, FALSE, &bRebootRequired);
1229  }
1230  else if (wcsncmp(szArg[i], L"tmpdir=", 7) == 0)
1231  {
1232  wcscpy_s(tmpDir, _countof(tmpDir), szArg[i] + 7);
1233  }
1234  else
1235  {
1236  goto invalid_argument;
1237  }
1238 
1239  if (dwResult != ERROR_SUCCESS && !bIsCleanup /* Ignore errors in case of commit/rollback to do as much work as possible. */)
1240  {
1241  uiResult = ERROR_INSTALL_FAILURE;
1242  goto cleanup;
1243  }
1244 
1245  /* Report progress and check for user cancellation. */
1246  MsiRecordSetInteger(hRecordProg, 2, MSICA_ADAPTER_TICK_SIZE);
1247  if (MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRecordProg) == IDCANCEL)
1248  {
1249  dwResult = ERROR_INSTALL_USEREXIT;
1250  goto cleanup;
1251  }
1252 
1253  continue;
1254 
1255 invalid_argument:
1256  msg(M_NONFATAL, "%s: Ignoring invalid argument: %ls", __FUNCTION__, szArg[i]);
1257  }
1258 
1259 cleanup:
1260  if (bRebootRequired && wcslen(tmpDir) > 0)
1261  {
1262  CreateRebootFile(tmpDir);
1263  }
1264  MsiCloseHandle(hRecordProg);
1265  LocalFree(szArg);
1266 cleanup_szSequence:
1267  free(szSequence);
1268 cleanup_CoInitialize:
1269  if (bIsCoInitialized)
1270  {
1271  CoUninitialize();
1272  }
1273  return uiResult;
1274 }
1275 
1276 UINT __stdcall
1277 CheckAndScheduleReboot(_In_ MSIHANDLE hInstall)
1278 {
1279 #ifdef _MSC_VER
1280 #pragma comment(linker, DLLEXP_EXPORT)
1281 #endif
1282 
1283  debug_popup(__FUNCTION__);
1284 
1285  BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL));
1286 
1288 
1289  /* get user-specific temp path, to where we create reboot indication file */
1290  WCHAR tempPath[MAX_PATH];
1291  GetTempPathW(MAX_PATH, tempPath);
1292 
1293  /* check if reboot file exists */
1294  WCHAR path[MAX_PATH];
1295  swprintf_s(path, _countof(path), L"%s%s", tempPath, FILE_NEED_REBOOT);
1296  WIN32_FIND_DATA data = { 0 };
1297  HANDLE searchHandle = FindFirstFileW(path, &data);
1298  if (searchHandle != INVALID_HANDLE_VALUE)
1299  {
1300  msg(M_WARN, "%s: Reboot file exists, schedule reboot", __FUNCTION__);
1301 
1302  FindClose(searchHandle);
1303  DeleteFileW(path);
1304 
1305  MsiSetMode(hInstall, MSIRUNMODE_REBOOTATEND, TRUE);
1306  }
1307 
1308  if (bIsCoInitialized)
1309  {
1310  CoUninitialize();
1311  }
1312  return ERROR_SUCCESS;
1313 }
1314 
1315 static BOOL
1316 IsInstalling(_In_ INSTALLSTATE InstallState, _In_ INSTALLSTATE ActionState)
1317 {
1318  return INSTALLSTATE_LOCAL == ActionState || INSTALLSTATE_SOURCE == ActionState
1319  || (INSTALLSTATE_DEFAULT == ActionState
1320  && (INSTALLSTATE_LOCAL == InstallState || INSTALLSTATE_SOURCE == InstallState));
1321 }
1322 
1323 static BOOL
1324 IsReInstalling(_In_ INSTALLSTATE InstallState, _In_ INSTALLSTATE ActionState)
1325 {
1326  return (INSTALLSTATE_LOCAL == ActionState || INSTALLSTATE_SOURCE == ActionState
1327  || INSTALLSTATE_DEFAULT == ActionState)
1328  && (INSTALLSTATE_LOCAL == InstallState || INSTALLSTATE_SOURCE == InstallState);
1329 }
1330 
1331 static BOOL
1332 IsUninstalling(_In_ INSTALLSTATE InstallState, _In_ INSTALLSTATE ActionState)
1333 {
1334  return (INSTALLSTATE_ABSENT == ActionState || INSTALLSTATE_REMOVED == ActionState)
1335  && (INSTALLSTATE_LOCAL == InstallState || INSTALLSTATE_SOURCE == InstallState);
1336 }
1337 
1338 UINT __stdcall
1339 EvaluateDriver(_In_ MSIHANDLE hInstall)
1340 {
1341 #ifdef _MSC_VER
1342 #pragma comment(linker, DLLEXP_EXPORT)
1343 #endif
1344 
1345  debug_popup(__FUNCTION__);
1346 
1347  UINT ret;
1348  BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL));
1349 
1351 
1352  INSTALLSTATE InstallState, ActionState;
1353  ret = MsiGetComponentStateW(hInstall, CMP_OVPN_DCO_INF, &InstallState, &ActionState);
1354  if (ret != ERROR_SUCCESS)
1355  {
1356  SetLastError(ret);
1357  msg(M_NONFATAL | M_ERRNO, "%s: MsiGetComponentState(\"%ls\") failed", __FUNCTION__, CMP_OVPN_DCO_INF);
1358  goto cleanup;
1359  }
1360 
1361  /* get user-specific temp path, to where we create reboot indication file */
1362  WCHAR tempPath[MAX_PATH];
1363  GetTempPathW(MAX_PATH, tempPath);
1364 
1365  WCHAR pathToInf[MAX_PATH];
1366  DWORD pathLen = _countof(pathToInf);
1367  ret = MsiGetPropertyW(hInstall, L"OVPNDCO", pathToInf, &pathLen);
1368  if (ret != ERROR_SUCCESS)
1369  {
1370  SetLastError(ret);
1371  msg(M_NONFATAL | M_ERRNO, "%s: MsiGetProperty failed", __FUNCTION__);
1372  goto cleanup;
1373  }
1374 
1375  WCHAR action[0x400];
1376  if ((IsReInstalling(InstallState, ActionState) || IsInstalling(InstallState, ActionState)))
1377  {
1378  swprintf_s(action, _countof(action), L"%s|%s%s|%s", ACTION_ADD_DRIVER, pathToInf, FILE_OVPN_DCO_INF, tempPath);
1379  }
1380  else if (IsUninstalling(InstallState, ActionState))
1381  {
1382  swprintf_s(action, _countof(action), L"%s|%s%s|%s", ACTION_DELETE_DRIVER, pathToInf, FILE_OVPN_DCO_INF, tempPath);
1383  }
1384  else
1385  {
1386  swprintf_s(action, _countof(action), L"%s||", ACTION_NOOP);
1387  }
1388 
1389  ret = MsiSetPropertyW(hInstall, L"OvpnDcoProcess", action);
1390 
1391 cleanup:
1392  if (bIsCoInitialized)
1393  {
1394  CoUninitialize();
1395  }
1396  return ret;
1397 }
1398 
1399 static BOOL
1400 GetPublishedDriverName(_In_z_ LPCWSTR hwid, _Out_writes_z_(len) LPWSTR publishedName, _In_ DWORD len)
1401 {
1402  wcscpy_s(publishedName, len, L"");
1403 
1404  HDEVINFO devInfoSet = SetupDiGetClassDevsW(&GUID_DEVCLASS_NET, NULL, NULL, 0);
1405  if (!devInfoSet)
1406  {
1407  msg(M_NONFATAL | M_ERRNO, "%s: SetupDiGetClassDevsW failed", __FUNCTION__);
1408  return FALSE;
1409  }
1410  BOOL res = FALSE;
1411  if (!SetupDiBuildDriverInfoList(devInfoSet, NULL, SPDIT_CLASSDRIVER))
1412  {
1413  msg(M_NONFATAL | M_ERRNO, "%s: SetupDiBuildDriverInfoList failed", __FUNCTION__);
1414  goto cleanupDeviceInfoSet;
1415  }
1416  for (DWORD idx = 0;; ++idx)
1417  {
1418  SP_DRVINFO_DATA_W drvInfo = { .cbSize = sizeof(drvInfo) };
1419  if (!SetupDiEnumDriverInfoW(devInfoSet, NULL, SPDIT_CLASSDRIVER, idx, &drvInfo))
1420  {
1421  if (GetLastError() == ERROR_NO_MORE_ITEMS)
1422  {
1423  break;
1424  }
1425  msg(M_NONFATAL | M_ERRNO, "%s: SetupDiEnumDriverInfoW failed", __FUNCTION__);
1426  goto cleanupDriverInfoList;
1427  }
1428  DWORD size;
1429  if (SetupDiGetDriverInfoDetailW(devInfoSet, NULL, &drvInfo, NULL, 0, &size) || GetLastError() != ERROR_INSUFFICIENT_BUFFER)
1430  {
1431  msg(M_NONFATAL | M_ERRNO, "%s: SetupDiGetDriverInfoDetailW failed", __FUNCTION__);
1432  goto cleanupDriverInfoList;
1433  }
1434  PSP_DRVINFO_DETAIL_DATA_W drvDetails = calloc(1, size);
1435  if (!drvDetails)
1436  {
1437  msg(M_NONFATAL, "%s: calloc(1, %u) failed", __FUNCTION__, size);
1438  goto cleanupDriverInfoList;
1439  }
1440  drvDetails->cbSize = sizeof(*drvDetails);
1441  if (!SetupDiGetDriverInfoDetailW(devInfoSet, NULL, &drvInfo, drvDetails, size, &size))
1442  {
1443  msg(M_NONFATAL | M_ERRNO, "%s: SetupDiGetDriverInfoDetailW failed", __FUNCTION__);
1444  free(drvDetails);
1445  goto cleanupDriverInfoList;
1446  }
1447  if (wcscmp(hwid, drvDetails->HardwareID) == 0)
1448  {
1449  PathStripPathW(drvDetails->InfFileName);
1450  wcscpy_s(publishedName, len, drvDetails->InfFileName);
1451  free(drvDetails);
1452  res = TRUE;
1453  break;
1454  }
1455  free(drvDetails);
1456  }
1457 
1458 cleanupDriverInfoList:
1459  SetupDiDestroyDriverInfoList(devInfoSet, NULL, SPDIT_CLASSDRIVER);
1460 cleanupDeviceInfoSet:
1461  SetupDiDestroyDeviceInfoList(devInfoSet);
1462  return res;
1463 }
1464 
1465 static void
1466 DeleteDriver(_In_z_ LPCWSTR pathToTmp)
1467 {
1468  /* get list of adapters for hwid */
1469  struct tap_adapter_node *pAdapterList = NULL;
1470  DWORD ret = tap_list_adapters(NULL, OVPN_DCO_HWID, &pAdapterList);
1471  if (ret != ERROR_SUCCESS)
1472  {
1473  msg(M_NONFATAL, "%s", "Failed to get adapter list: %d", __FUNCTION__, ret);
1474  }
1475 
1476  /* delete all adapters */
1477  BOOL rebootRequired = FALSE;
1478  for (struct tap_adapter_node *pAdapter = pAdapterList; pAdapter != NULL; pAdapter = pAdapter->pNext)
1479  {
1480  tap_delete_adapter(NULL, &pAdapter->guid, &rebootRequired);
1481  }
1482 
1483  /* delete driver */
1484  WCHAR publishedName[MAX_PATH] = { 0 };
1485  if (GetPublishedDriverName(OVPN_DCO_HWID, publishedName, _countof(publishedName)))
1486  {
1487  if (!SetupUninstallOEMInfW(publishedName, 0, NULL))
1488  {
1489  msg(M_NONFATAL | M_ERRNO, "%s: SetupUninstallOEMInfW(\"%ls\") failed", __FUNCTION__, publishedName);
1490  }
1491  }
1492 
1493  if (rebootRequired)
1494  {
1495  CreateRebootFile(pathToTmp);
1496  }
1497 }
1498 
1499 static void
1500 AddDriver(_In_z_ LPCWSTR pathToInf, _In_z_ LPCWSTR pathToTmp)
1501 {
1502  /* copy driver to driver store */
1503  if (!SetupCopyOEMInfW(pathToInf, NULL, SPOST_PATH, 0, NULL, 0, NULL, NULL))
1504  {
1505  msg(M_NONFATAL | M_ERRNO, "%s: SetupCopyOEMInf(\"%ls\") failed", __FUNCTION__, pathToInf);
1506  return;
1507  }
1508 
1509  /* update driver for existing devices (if any) */
1510  BOOL rebootRequired = FALSE;
1511  if (!UpdateDriverForPlugAndPlayDevicesW(NULL, OVPN_DCO_HWID, pathToInf, INSTALLFLAG_NONINTERACTIVE | INSTALLFLAG_FORCE, &rebootRequired))
1512  {
1513  /* ERROR_NO_SUCH_DEVINST means that no devices exist, which is normal case - device (adapter) is created at later stage */
1514  if (GetLastError() != ERROR_NO_SUCH_DEVINST)
1515  {
1516  msg(M_NONFATAL | M_ERRNO, "%s: UpdateDriverForPlugAndPlayDevices(\"%ls\", \"%ls\") failed", __FUNCTION__, OVPN_DCO_HWID, pathToInf);
1517  return;
1518  }
1519  }
1520  if (rebootRequired)
1521  {
1522  CreateRebootFile(pathToTmp);
1523  }
1524 }
1525 
1526 UINT __stdcall
1527 ProcessDriver(_In_ MSIHANDLE hInstall)
1528 {
1529 #ifdef _MSC_VER
1530 #pragma comment(linker, DLLEXP_EXPORT)
1531 #endif
1532 
1533  debug_popup(__FUNCTION__);
1534 
1535  UINT ret = 0;
1536  BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL));
1537 
1539 
1540  LPWSTR customData = NULL;
1541  ret = msi_get_string(hInstall, L"CustomActionData", &customData);
1542  if (ret != ERROR_SUCCESS)
1543  {
1544  goto cleanup;
1545  }
1546 
1547  int i = 0;
1548  WCHAR action[0x400] = { 0 };
1549  WCHAR pathToInf[MAX_PATH] = { 0 };
1550  WCHAR pathToTmp[MAX_PATH] = { 0 };
1551 
1552  WCHAR *pos = NULL;
1553  WCHAR *token = wcstok_s(customData, L"|", &pos);
1554  /* action|path_to_inf_file|path_to_tmp_dir */
1555  while (token)
1556  {
1557  switch (i++)
1558  {
1559  case 0:
1560  wcscpy_s(action, _countof(action), token);
1561  break;
1562 
1563  case 1:
1564  wcscpy_s(pathToInf, _countof(pathToInf), token);
1565  break;
1566 
1567  case 2:
1568  wcscpy_s(pathToTmp, _countof(pathToTmp), token);
1569  break;
1570  }
1571  token = wcstok_s(NULL, L"|", &pos);
1572  }
1573 
1574  if (wcscmp(action, ACTION_ADD_DRIVER) == 0)
1575  {
1576  AddDriver(pathToInf, pathToTmp);
1577  }
1578  else if (wcscmp(action, ACTION_DELETE_DRIVER) == 0)
1579  {
1580  DeleteDriver(pathToTmp);
1581  }
1582 
1583 cleanup:
1584  free(customData);
1585  if (bIsCoInitialized)
1586  {
1587  CoUninitialize();
1588  }
1589  return ret;
1590 }
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:100
tap_adapter_node::guid
GUID guid
Definition: tap.h:138
EvaluateDriver
UINT __stdcall EvaluateDriver(_In_ MSIHANDLE hInstall)
Check what operation shall be performed on ovpn-dco driver and set data value (path to inf and user t...
Definition: openvpnmsica.c:1339
IsInstalling
static BOOL IsInstalling(_In_ INSTALLSTATE InstallState, _In_ INSTALLSTATE ActionState)
Definition: openvpnmsica.c:1316
M_FATAL
#define M_FATAL
Definition: error.h:95
ACTION_ADD_DRIVER
#define ACTION_ADD_DRIVER
Definition: openvpnmsica.c:68
M_NONFATAL
#define M_NONFATAL
Definition: error.h:96
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:96
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:414
msica_arg_seq
Argument sequence.
Definition: msica_arg.h:48
config-msvc.h
PACKAGE_VERSION
#define PACKAGE_VERSION
Definition: config.h:504
pos
static int pos(char c)
Definition: base64.c:107
openvpnmsica.h
GUID_DEVCLASS_NET
const static GUID GUID_DEVCLASS_NET
Definition: tun.c:61
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:1277
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:748
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:40
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:98
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:1007
CreateRebootFile
static void CreateRebootFile(_In_z_ LPCWSTR szTmpDir)
Create empty file in user's temp directory.
Definition: openvpnmsica.c:1058
DeleteDriver
static void DeleteDriver(_In_z_ LPCWSTR pathToTmp)
Definition: openvpnmsica.c:1466
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:1357
M_WARN
#define M_WARN
Definition: error.h:97
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:57
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:214
ProcessDriver
UINT __stdcall ProcessDriver(_In_ MSIHANDLE hInstall)
Install or uninstall ovpn-dco driver, removing all adapters using that driver.
Definition: openvpnmsica.c:1527
_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:1036
_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:581
ACTION_DELETE_DRIVER
#define ACTION_DELETE_DRIVER
Definition: openvpnmsica.c:69
ACTION_NOOP
#define ACTION_NOOP
Definition: openvpnmsica.c:70
CloseOpenVPNGUI
UINT __stdcall CloseOpenVPNGUI(_In_ MSIHANDLE hInstall)
Find OpenVPN GUI window and send it a WM_CLOSE message.
Definition: openvpnmsica.c:452
_Inout_opt_
#define _Inout_opt_
Definition: basic.h:54
ProcessDeferredAction
UINT __stdcall ProcessDeferredAction(_In_ MSIHANDLE hInstall)
Perform scheduled deferred action.
Definition: openvpnmsica.c:1077
AddDriver
static void AddDriver(_In_z_ LPCWSTR pathToInf, _In_z_ LPCWSTR pathToTmp)
Definition: openvpnmsica.c:1500
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:78
StartOpenVPNGUI
UINT __stdcall StartOpenVPNGUI(_In_ MSIHANDLE hInstall)
Launches OpenVPN GUI.
Definition: openvpnmsica.c:475
debug_popup
#define debug_popup(f)
Definition: openvpnmsica.c:147
find_adapters
static void find_adapters(_In_ MSIHANDLE hInstall, _In_z_ LPCTSTR szzHardwareIDs, _In_z_ LPCTSTR szAdaptersPropertyName, _In_z_ LPCTSTR szActiveAdaptersPropertyName)
Definition: openvpnmsica.c:265
IsUninstalling
static BOOL IsUninstalling(_In_ INSTALLSTATE InstallState, _In_ INSTALLSTATE ActionState)
Definition: openvpnmsica.c:1332
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
MSICA_ADAPTER_TICK_SIZE
#define MSICA_ADAPTER_TICK_SIZE
Local constants.
Definition: openvpnmsica.c:64
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:681
OVPN_DCO_HWID
#define OVPN_DCO_HWID
Definition: openvpnmsica.c:72
_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:1017
msica_arg_seq_init
void msica_arg_seq_init(_Inout_ struct msica_arg_seq *seq)
Initializes argument sequence.
Definition: msica_arg.c:36
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:725
FILE_OVPN_DCO_INF
#define FILE_OVPN_DCO_INF
Definition: openvpnmsica.c:71
tap_set_adapter_name
DWORD tap_set_adapter_name(_In_ LPCGUID pguidAdapter, _In_ LPCTSTR szName, _In_ BOOL bSilent)
Sets adapter name.
Definition: tap.c:1065
FILE_NEED_REBOOT
#define FILE_NEED_REBOOT
Definition: openvpnmsica.c:66
msica_arg_seq_free
void msica_arg_seq_free(_Inout_ struct msica_arg_seq *seq)
Frees argument sequence.
Definition: msica_arg.c:44
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:86
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:1148
TAP_WIN_COMPONENT_ID
#define TAP_WIN_COMPONENT_ID
Definition: config.h:546
IsReInstalling
static BOOL IsReInstalling(_In_ INSTALLSTATE InstallState, _In_ INSTALLSTATE ActionState)
Definition: openvpnmsica.c:1324
msg
#define msg(flags,...)
Definition: error.h:150
GetPublishedDriverName
static BOOL GetPublishedDriverName(_In_z_ LPCWSTR hwid, _Out_writes_z_(len) LPWSTR publishedName, _In_ DWORD len)
Definition: openvpnmsica.c:1400
CMP_OVPN_DCO_INF
#define CMP_OVPN_DCO_INF
Definition: openvpnmsica.c:67
set_openvpnserv_state
static UINT set_openvpnserv_state(_In_ MSIHANDLE hInstall)
Detects if the OpenVPNService service is in use (running or paused) and sets OPENVPNSERVICE to the se...
Definition: openvpnmsica.c:162