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-2024 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
38DWORD openvpnmsica_thread_data_idx = TLS_OUT_OF_INDEXES;
39
40
44BOOL 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. */
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
96bool
97dont_mute(unsigned int flags)
98{
99 UNREFERENCED_PARAMETER(flags);
100
101 return true;
102}
103
104
105void
106x_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}
bool dont_mute(unsigned int flags)
Check muting filter.
Definition dllmain.c:97
BOOL WINAPI DllMain(_In_ HINSTANCE hinstDLL, _In_ DWORD dwReason, _In_ LPVOID lpReserved)
DLL entry point.
Definition dllmain.c:45
void x_msg_va(const unsigned int flags, const char *format, va_list arglist)
Definition dllmain.c:106
DWORD openvpnmsica_thread_data_idx
MSI session handle thread local storage index.
Definition dllmain.c:38
#define M_WARN
Definition error.h:91
#define M_ERRNO
Definition error.h:94
#define ERROR_MSICA
#define ERROR_MSICA_ERRNO
Thread local storage data.
MSIHANDLE hInstall
Handle to the installation session.
#define _In_
Definition basic.h:42