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-2026 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#if defined(__GNUC__) || defined(__clang__)
102#pragma GCC diagnostic push
103#pragma GCC diagnostic ignored "-Wsign-compare"
104#endif
105
106void
107x_msg_va(const unsigned int flags, const char *format, va_list arglist)
108{
109 /* Secure last error before it is overridden. */
110 DWORD dwResult = (flags & M_ERRNO) != 0 ? GetLastError() : ERROR_SUCCESS;
111
112 struct openvpnmsica_thread_data *s =
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 LPWSTR szErrMessage = NULL;
166 if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER
167 | FORMAT_MESSAGE_IGNORE_INSERTS,
168 0, dwResult, 0, (LPWSTR)&szErrMessage, 0, NULL)
169 && szErrMessage)
170 {
171 /* Trim trailing whitespace. Set terminator after the last non-whitespace character.
172 * This prevents excessive trailing line breaks. */
173 for (size_t i = 0, i_last = 0;; i++)
174 {
175 if (szErrMessage[i])
176 {
177 if (!iswspace(szErrMessage[i]))
178 {
179 i_last = i + 1;
180 }
181 }
182 else
183 {
184 szErrMessage[i_last] = 0;
185 break;
186 }
187 }
188 MsiRecordSetString(hRecordProg, 4, szErrMessage);
189 LocalFree(szErrMessage);
190 }
191 }
192
193 MsiProcessMessage(s->hInstall, (flags & M_WARN) ? INSTALLMESSAGE_INFO : INSTALLMESSAGE_ERROR,
194 hRecordProg);
195 MsiCloseHandle(hRecordProg);
196}
197
198#if defined(__GNUC__) || defined(__clang__)
199#pragma GCC diagnostic pop
200#endif
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:107
DWORD openvpnmsica_thread_data_idx
MSI session handle thread local storage index.
Definition dllmain.c:37
#define M_WARN
Definition error.h:92
#define M_ERRNO
Definition error.h:95
#define ERROR_MSICA
#define ERROR_MSICA_ERRNO
Thread local storage data.
MSIHANDLE hInstall
Handle to the installation session.
#define _In_
Definition basic.h:41