OpenVPN
dllmain.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-2023 Simon Rozman <simon@rozman.si>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2
9  * as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 
25 #include "openvpnmsica.h"
26 #include "../tapctl/error.h"
27 
28 #include <windows.h>
29 #include <msi.h>
30 #include <msiquery.h>
31 #ifdef _MSC_VER
32 #pragma comment(lib, "msi.lib")
33 #endif
34 #include <stdio.h>
35 #include <tchar.h>
36 
37 
38 DWORD openvpnmsica_thread_data_idx = TLS_OUT_OF_INDEXES;
39 
40 
44 BOOL WINAPI
46  _In_ HINSTANCE hinstDLL,
47  _In_ DWORD dwReason,
48  _In_ LPVOID lpReserved)
49 {
50  UNREFERENCED_PARAMETER(hinstDLL);
51  UNREFERENCED_PARAMETER(lpReserved);
52 
53  switch (dwReason)
54  {
55  case DLL_PROCESS_ATTACH:
56  /* Allocate thread local storage index. */
57  openvpnmsica_thread_data_idx = TlsAlloc();
58  if (openvpnmsica_thread_data_idx == TLS_OUT_OF_INDEXES)
59  {
60  return FALSE;
61  }
62  /* Fall through. */
63 
64  case DLL_THREAD_ATTACH:
65  {
66  /* Create thread local storage data. */
67  struct openvpnmsica_thread_data *s = (struct openvpnmsica_thread_data *)calloc(1, sizeof(struct openvpnmsica_thread_data));
68  if (s == NULL)
69  {
70  return FALSE;
71  }
72 
73  TlsSetValue(openvpnmsica_thread_data_idx, s);
74  break;
75  }
76 
77  case DLL_PROCESS_DETACH:
78  if (openvpnmsica_thread_data_idx != TLS_OUT_OF_INDEXES)
79  {
80  /* Free thread local storage data and index. */
81  free(TlsGetValue(openvpnmsica_thread_data_idx));
83  }
84  break;
85 
86  case DLL_THREAD_DETACH:
87  /* Free thread local storage data. */
88  free(TlsGetValue(openvpnmsica_thread_data_idx));
89  break;
90  }
91 
92  return TRUE;
93 }
94 
95 
96 bool
97 dont_mute(unsigned int flags)
98 {
99  UNREFERENCED_PARAMETER(flags);
100 
101  return true;
102 }
103 
104 
105 void
106 x_msg_va(const unsigned int flags, const char *format, va_list arglist)
107 {
108  /* Secure last error before it is overridden. */
109  DWORD dwResult = (flags & M_ERRNO) != 0 ? GetLastError() : ERROR_SUCCESS;
110 
112  if (s->hInstall == 0)
113  {
114  /* No MSI session, no fun. */
115  return;
116  }
117 
118  /* Prepare the message record. The record will contain up to four fields. */
119  MSIHANDLE hRecordProg = MsiCreateRecord(4);
120 
121  {
122  /* Field 2: The message string. */
123  char szBufStack[128];
124  int iResultLen = vsnprintf(szBufStack, _countof(szBufStack), format, arglist);
125  if (iResultLen < _countof(szBufStack))
126  {
127  /* Use from stack. */
128  MsiRecordSetStringA(hRecordProg, 2, szBufStack);
129  }
130  else
131  {
132  /* Allocate on heap and retry. */
133  char *szMessage = (char *)malloc(++iResultLen * sizeof(char));
134  if (szMessage != NULL)
135  {
136  vsnprintf(szMessage, iResultLen, format, arglist);
137  MsiRecordSetStringA(hRecordProg, 2, szMessage);
138  free(szMessage);
139  }
140  else
141  {
142  /* Use stack variant anyway, but make sure it's zero-terminated. */
143  szBufStack[_countof(szBufStack) - 1] = 0;
144  MsiRecordSetStringA(hRecordProg, 2, szBufStack);
145  }
146  }
147  }
148 
149  if ((flags & M_ERRNO) == 0)
150  {
151  /* Field 1: MSI Error Code */
152  MsiRecordSetInteger(hRecordProg, 1, ERROR_MSICA);
153  }
154  else
155  {
156  /* Field 1: MSI Error Code */
157  MsiRecordSetInteger(hRecordProg, 1, ERROR_MSICA_ERRNO);
158 
159  /* Field 3: The Windows error number. */
160  MsiRecordSetInteger(hRecordProg, 3, dwResult);
161 
162  /* Field 4: The Windows error description. */
163  LPTSTR szErrMessage = NULL;
164  if (FormatMessage(
165  FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,
166  0,
167  dwResult,
168  0,
169  (LPTSTR)&szErrMessage,
170  0,
171  NULL) && szErrMessage)
172  {
173  /* Trim trailing whitespace. Set terminator after the last non-whitespace character. This prevents excessive trailing line breaks. */
174  for (size_t i = 0, i_last = 0;; i++)
175  {
176  if (szErrMessage[i])
177  {
178  if (!_istspace(szErrMessage[i]))
179  {
180  i_last = i + 1;
181  }
182  }
183  else
184  {
185  szErrMessage[i_last] = 0;
186  break;
187  }
188  }
189  MsiRecordSetString(hRecordProg, 4, szErrMessage);
190  LocalFree(szErrMessage);
191  }
192  }
193 
194  MsiProcessMessage(s->hInstall, (flags & M_WARN) ? INSTALLMESSAGE_INFO : INSTALLMESSAGE_ERROR, hRecordProg);
195  MsiCloseHandle(hRecordProg);
196 }
M_ERRNO
#define M_ERRNO
Definition: error.h:100
openvpnmsica.h
x_msg_va
void x_msg_va(const unsigned int flags, const char *format, va_list arglist)
Definition: dllmain.c:106
M_WARN
#define M_WARN
Definition: error.h:97
openvpnmsica_thread_data::hInstall
MSIHANDLE hInstall
Definition: openvpnmsica.h:41
_In_
#define _In_
Definition: basic.h:42
ERROR_MSICA_ERRNO
#define ERROR_MSICA_ERRNO
Definition: openvpnmsica.h:33
ERROR_MSICA
#define ERROR_MSICA
Definition: openvpnmsica.h:32
DllMain
BOOL WINAPI DllMain(_In_ HINSTANCE hinstDLL, _In_ DWORD dwReason, _In_ LPVOID lpReserved)
DLL entry point.
Definition: dllmain.c:45
config.h
openvpnmsica_thread_data
Thread local storage data.
Definition: openvpnmsica.h:39
dont_mute
bool dont_mute(unsigned int flags)
Check muting filter.
Definition: dllmain.c:97
openvpnmsica_thread_data_idx
DWORD openvpnmsica_thread_data_idx
MSI session handle thread local storage index.
Definition: dllmain.c:38