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