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