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
101void
102x_msg_va(const unsigned int flags, const char *format, va_list arglist)
103{
104 /* Secure last error before it is overridden. */
105 DWORD dwResult = (flags & M_ERRNO) != 0 ? GetLastError() : ERROR_SUCCESS;
106
107 struct openvpnmsica_thread_data *s =
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 > 0 && (unsigned int)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 if (szMessage != NULL)
132 {
133 vsnprintf(szMessage, iResultLen, format, arglist);
134 MsiRecordSetStringA(hRecordProg, 2, szMessage);
135 free(szMessage);
136 }
137 else
138 {
139 /* Use stack variant anyway, but make sure it's zero-terminated. */
140 szBufStack[_countof(szBufStack) - 1] = 0;
141 MsiRecordSetStringA(hRecordProg, 2, szBufStack);
142 }
143 }
144 }
145
146 if ((flags & M_ERRNO) == 0)
147 {
148 /* Field 1: MSI Error Code */
149 MsiRecordSetInteger(hRecordProg, 1, ERROR_MSICA);
150 }
151 else
152 {
153 /* Field 1: MSI Error Code */
154 MsiRecordSetInteger(hRecordProg, 1, ERROR_MSICA_ERRNO);
155
156 /* Field 3: The Windows error number. */
157 MsiRecordSetInteger(hRecordProg, 3, dwResult);
158
159 /* Field 4: The Windows error description. */
160 LPWSTR szErrMessage = NULL;
161 if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER
162 | FORMAT_MESSAGE_IGNORE_INSERTS,
163 0, dwResult, 0, (LPWSTR)&szErrMessage, 0, NULL)
164 && szErrMessage)
165 {
166 /* Trim trailing whitespace. Set terminator after the last non-whitespace character.
167 * This prevents excessive trailing line breaks. */
168 for (size_t i = 0, i_last = 0;; i++)
169 {
170 if (szErrMessage[i])
171 {
172 if (!iswspace(szErrMessage[i]))
173 {
174 i_last = i + 1;
175 }
176 }
177 else
178 {
179 szErrMessage[i_last] = 0;
180 break;
181 }
182 }
183 MsiRecordSetString(hRecordProg, 4, szErrMessage);
184 LocalFree(szErrMessage);
185 }
186 }
187
188 MsiProcessMessage(s->hInstall, (flags & M_WARN) ? INSTALLMESSAGE_INFO : INSTALLMESSAGE_ERROR,
189 hRecordProg);
190 MsiCloseHandle(hRecordProg);
191}
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:102
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