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 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 
27 #include "openvpnmsica.h"
28 #include "../tapctl/error.h"
29 
30 #include <windows.h>
31 #include <msi.h>
32 #include <msiquery.h>
33 #ifdef _MSC_VER
34 #pragma comment(lib, "msi.lib")
35 #endif
36 #include <stdio.h>
37 #include <tchar.h>
38 
39 
40 DWORD openvpnmsica_thread_data_idx = TLS_OUT_OF_INDEXES;
41 
42 
46 BOOL WINAPI
48  _In_ HINSTANCE hinstDLL,
49  _In_ DWORD dwReason,
50  _In_ LPVOID lpReserved)
51 {
52  UNREFERENCED_PARAMETER(hinstDLL);
53  UNREFERENCED_PARAMETER(lpReserved);
54 
55  switch (dwReason)
56  {
57  case DLL_PROCESS_ATTACH:
58  /* Allocate thread local storage index. */
59  openvpnmsica_thread_data_idx = TlsAlloc();
60  if (openvpnmsica_thread_data_idx == TLS_OUT_OF_INDEXES)
61  {
62  return FALSE;
63  }
64  /* Fall through. */
65 
66  case DLL_THREAD_ATTACH:
67  {
68  /* Create thread local storage data. */
69  struct openvpnmsica_thread_data *s = (struct openvpnmsica_thread_data *)calloc(1, sizeof(struct openvpnmsica_thread_data));
70  if (s == NULL)
71  {
72  return FALSE;
73  }
74 
75  TlsSetValue(openvpnmsica_thread_data_idx, s);
76  break;
77  }
78 
79  case DLL_PROCESS_DETACH:
80  if (openvpnmsica_thread_data_idx != TLS_OUT_OF_INDEXES)
81  {
82  /* Free thread local storage data and index. */
83  free(TlsGetValue(openvpnmsica_thread_data_idx));
85  }
86  break;
87 
88  case DLL_THREAD_DETACH:
89  /* Free thread local storage data. */
90  free(TlsGetValue(openvpnmsica_thread_data_idx));
91  break;
92  }
93 
94  return TRUE;
95 }
96 
97 
98 bool
99 dont_mute(unsigned int flags)
100 {
101  UNREFERENCED_PARAMETER(flags);
102 
103  return true;
104 }
105 
106 
107 void
108 x_msg_va(const unsigned int flags, const char *format, va_list arglist)
109 {
110  /* Secure last error before it is overridden. */
111  DWORD dwResult = (flags & M_ERRNO) != 0 ? GetLastError() : ERROR_SUCCESS;
112 
114  if (s->hInstall == 0)
115  {
116  /* No MSI session, no fun. */
117  return;
118  }
119 
120  /* Prepare the message record. The record will contain up to four fields. */
121  MSIHANDLE hRecordProg = MsiCreateRecord(4);
122 
123  {
124  /* Field 2: The message string. */
125  char szBufStack[128];
126  int iResultLen = vsnprintf(szBufStack, _countof(szBufStack), format, arglist);
127  if (iResultLen < _countof(szBufStack))
128  {
129  /* Use from stack. */
130  MsiRecordSetStringA(hRecordProg, 2, szBufStack);
131  }
132  else
133  {
134  /* Allocate on heap and retry. */
135  char *szMessage = (char *)malloc(++iResultLen * sizeof(char));
136  if (szMessage != NULL)
137  {
138  vsnprintf(szMessage, iResultLen, format, arglist);
139  MsiRecordSetStringA(hRecordProg, 2, szMessage);
140  free(szMessage);
141  }
142  else
143  {
144  /* Use stack variant anyway, but make sure it's zero-terminated. */
145  szBufStack[_countof(szBufStack) - 1] = 0;
146  MsiRecordSetStringA(hRecordProg, 2, szBufStack);
147  }
148  }
149  }
150 
151  if ((flags & M_ERRNO) == 0)
152  {
153  /* Field 1: MSI Error Code */
154  MsiRecordSetInteger(hRecordProg, 1, ERROR_MSICA);
155  }
156  else
157  {
158  /* Field 1: MSI Error Code */
159  MsiRecordSetInteger(hRecordProg, 1, ERROR_MSICA_ERRNO);
160 
161  /* Field 3: The Windows error number. */
162  MsiRecordSetInteger(hRecordProg, 3, dwResult);
163 
164  /* Field 4: The Windows error description. */
165  LPTSTR szErrMessage = NULL;
166  if (FormatMessage(
167  FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,
168  0,
169  dwResult,
170  0,
171  (LPTSTR)&szErrMessage,
172  0,
173  NULL) && szErrMessage)
174  {
175  /* Trim trailing whitespace. Set terminator after the last non-whitespace character. This prevents excessive trailing line breaks. */
176  for (size_t i = 0, i_last = 0;; i++)
177  {
178  if (szErrMessage[i])
179  {
180  if (!_istspace(szErrMessage[i]))
181  {
182  i_last = i + 1;
183  }
184  }
185  else
186  {
187  szErrMessage[i_last] = 0;
188  break;
189  }
190  }
191  MsiRecordSetString(hRecordProg, 4, szErrMessage);
192  LocalFree(szErrMessage);
193  }
194  }
195 
196  MsiProcessMessage(s->hInstall, INSTALLMESSAGE_ERROR, hRecordProg);
197  MsiCloseHandle(hRecordProg);
198 }
Thread local storage data.
Definition: openvpnmsica.h:39
DWORD openvpnmsica_thread_data_idx
MSI session handle thread local storage index.
Definition: dllmain.c:40
#define ERROR_MSICA_ERRNO
Definition: openvpnmsica.h:33
void x_msg_va(const unsigned int flags, const char *format, va_list arglist)
Definition: dllmain.c:108
#define ERROR_MSICA
Definition: openvpnmsica.h:32
list flags
#define malloc
Definition: cmocka.c:1795
#define _In_
Definition: basic.h:34
#define vsnprintf
#define M_ERRNO
Definition: error.h:99
bool dont_mute(unsigned int flags)
Check muting filter.
Definition: dllmain.c:99
#define free
Definition: cmocka.c:1850
BOOL WINAPI DllMain(_In_ HINSTANCE hinstDLL, _In_ DWORD dwReason, _In_ LPVOID lpReserved)
DLL entry point.
Definition: dllmain.c:47