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