[Openvpn-devel,v2] openvpnserv: Fix writing messages to the event log

Message ID 20250917090557.25414-1-gert@greenie.muc.de
State New
Headers show
Series [Openvpn-devel,v2] openvpnserv: Fix writing messages to the event log | expand

Commit Message

Gert Doering Sept. 17, 2025, 9:05 a.m. UTC
From: Lev Stipakov <lev@openvpn.net>

There are two problems with the current implementation:

 - due to the code bug, we never display actual error message
corresponding to the Windows error code. We use
FORMAT_MESSAGE_ALLOCATE_BUFFER, in which case we must pass
a pointer to the LPTSTR, not the LPTSTR itself.

 - The error is not displayed in the "General" tab, which is very confusing.
One needs to go to the "Details" tab to see what is wrong.

This commit solves both problems. We now display a proper error
message in addition to the text provided by the service ("what went wrong").
While on it, remove trailing symbols ín a safer way.

To display the message in "General" tab, we create a registered message file
(openvpnservmsg.dll), which contains message template. Note that this requires
changes to the installer - we need to install the new DLL and
add a registry entry.

GitHub: https://github.com/OpenVPN/openvpn/issues/842

Change-Id: I423c9880def0eb479abb72bef2e8034a73cf5905
Signed-off-by: Lev Stipakov <lev@openvpn.net>
Acked-by: Selva Nair <selva.nair@gmail.com>
---

This change was reviewed on Gerrit and approved by at least one
developer. I request to merge it to master.

Gerrit URL: https://gerrit.openvpn.net/c/openvpn/+/1188
This mail reflects revision 2 of this Change.

Acked-by according to Gerrit (reflected above):
Selva Nair <selva.nair@gmail.com>

Comments

Gert Doering Sept. 17, 2025, 4:26 p.m. UTC | #1
So this has a bit of a complicated journey - the initial patch was
gerrit 1183 for 2.6, which had v1..v3, and then this one for master,
which only got up to v2 but is now the same code change, essentially.

I have tested a 2.6 installer with this, and it makes really nice 
log entries in the event log - so definitely a MUST HAVE.

HEADS UP: when building installers with "autoconf" on mingw, this will
not currently produce the necessary .dll to proceed.  I'll send a patch
to add this.  The official way releases are built is "cmake with msvc"
or "cmake with mingw", and both produce the new .dll - and for "just
build a test openvpn.exe with configure/mingw" this is not critical
(which is what I usually do).

Your patch has been applied to the master branch.

commit 06919a60ae61d6d88546b23b52092f742599a8ae
Author: Lev Stipakov
Date:   Wed Sep 17 11:05:48 2025 +0200

     openvpnserv: Fix writing messages to the event log

     Signed-off-by: Lev Stipakov <lev@openvpn.net>
     Acked-by: Selva Nair <selva.nair@gmail.com>
     Gerrit URL: https://gerrit.openvpn.net/c/openvpn/+/1188
     Message-Id: <20250917090557.25414-1-gert@greenie.muc.de>
     URL: https://sourceforge.net/p/openvpn/mailman/message/59234559/
     Signed-off-by: Gert Doering <gert@greenie.muc.de>


--
kind regards,

Gert Doering

Patch

diff --git a/src/openvpnserv/CMakeLists.txt b/src/openvpnserv/CMakeLists.txt
index 7e3efb2..82099f1 100644
--- a/src/openvpnserv/CMakeLists.txt
+++ b/src/openvpnserv/CMakeLists.txt
@@ -6,11 +6,14 @@ 
 
 add_executable(openvpnserv)
 
+set(MC_GEN_DIR ${CMAKE_CURRENT_BINARY_DIR}/mc)
+
 target_include_directories(openvpnserv PRIVATE
     ${CMAKE_CURRENT_BINARY_DIR}/../../
     ../../include/
     ../openvpn/
     ../compat/
+    ${MC_GEN_DIR}
     )
 target_sources(openvpnserv PRIVATE
     common.c
@@ -33,3 +36,34 @@ 
     target_compile_options(openvpnserv PRIVATE -municode)
     target_link_options(openvpnserv PRIVATE -municode)
 endif ()
+
+# below we generate a DLL which contains an event source for event log messages from eventmsg.mc template
+file(MAKE_DIRECTORY ${MC_GEN_DIR})
+set(MC_FILE ${CMAKE_CURRENT_SOURCE_DIR}/eventmsg.mc)
+
+find_program(MC_COMPILER NAMES mc mc.exe x86_64-w64-mingw32-windmc i686-w64-mingw32-windmc windmc)
+
+if (NOT MC_COMPILER)
+    message(FATAL_ERROR "No message compiler found.")
+endif()
+
+add_custom_command(
+    OUTPUT ${MC_GEN_DIR}/eventmsg.rc ${MC_GEN_DIR}/eventmsg.h
+    COMMAND ${MC_COMPILER} -U -h ${MC_GEN_DIR} -r ${MC_GEN_DIR} ${MC_FILE}
+    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/eventmsg.mc
+    VERBATIM
+    )
+
+# generate rc file for DLL and header for the service binary
+add_custom_target(msg_mc_gen ALL DEPENDS ${MC_GEN_DIR}/eventmsg.rc ${MC_GEN_DIR}/eventmsg.h)
+
+add_library(openvpnservmsg SHARED ${MC_GEN_DIR}/eventmsg.rc)
+
+if (MSVC)
+    set_target_properties(openvpnservmsg PROPERTIES LINK_FLAGS "/NOENTRY")
+else()
+    target_link_options(openvpnservmsg PRIVATE "-Wl,--no-entry")
+endif()
+
+add_dependencies(openvpnservmsg msg_mc_gen)
+add_dependencies(openvpnserv msg_mc_gen)
diff --git a/src/openvpnserv/common.c b/src/openvpnserv/common.c
index a42c65d..e975cc7 100644
--- a/src/openvpnserv/common.c
+++ b/src/openvpnserv/common.c
@@ -22,6 +22,7 @@ 
 
 #include "service.h"
 #include "validate.h"
+#include "eventmsg.h"
 
 LPCWSTR service_instance = L"";
 static wchar_t win_sys_path[MAX_PATH];
@@ -203,25 +204,29 @@ 
     LPWSTR tmp = NULL;
 
     error = GetLastError();
-    len = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM
-                            | FORMAT_MESSAGE_ARGUMENT_ARRAY,
-                        NULL, error, LANG_NEUTRAL, tmp, 0, NULL);
+    len = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM
+                             | FORMAT_MESSAGE_IGNORE_INSERTS,
+                         NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&tmp, 0, NULL);
 
-    if (len == 0 || (long)_countof(buf) < (long)len + 14)
+    if (!len || !tmp)
     {
-        buf[0] = L'\0';
-    }
-    else
-    {
-        tmp[wcslen(tmp) - 2] = L'\0'; /* remove CR/LF characters */
-        swprintf(buf, _countof(buf), L"%ls (0x%x)", tmp, error);
+        swprintf(buf, _countof(buf), L"Unknown error (0x%lx)", error);
+        if (tmp)
+        {
+            LocalFree(tmp);
+        }
+        return buf;
     }
 
-    if (tmp)
+    /* trim trailing CR / LF / spaces safely */
+    while (len && (tmp[len - 1] == L'\r' || tmp[len - 1] == L'\n' || tmp[len - 1] == L' '))
     {
-        LocalFree(tmp);
+        tmp[--len] = L'\0';
     }
 
+    swprintf(buf, _countof(buf), L"%ls (0x%lx)", tmp, error);
+
+    LocalFree(tmp);
     return buf;
 }
 
@@ -253,8 +258,14 @@ 
 
         const WCHAR *mesg[] = { msg[0], msg[1] };
         ReportEvent(hEventSource,
-                    flags & MSG_FLAGS_ERROR ? EVENTLOG_ERROR_TYPE : EVENTLOG_INFORMATION_TYPE, 0, 0,
-                    NULL, 2, 0, mesg, NULL);
+                    flags & MSG_FLAGS_ERROR ? EVENTLOG_ERROR_TYPE : EVENTLOG_INFORMATION_TYPE,
+                    0,
+                    EVT_TEXT_2,
+                    NULL,
+                    2,
+                    0,
+                    mesg,
+                    NULL);
         DeregisterEventSource(hEventSource);
     }
 
diff --git a/src/openvpnserv/eventmsg.mc b/src/openvpnserv/eventmsg.mc
new file mode 100644
index 0000000..722a30e
--- /dev/null
+++ b/src/openvpnserv/eventmsg.mc
@@ -0,0 +1,23 @@ 
+MessageIdTypedef=DWORD
+
+SeverityNames=(Success=0x0:STATUS_SEVERITY_SUCCESS
+    Informational=0x1:STATUS_SEVERITY_INFORMATIONAL
+    Warning=0x2:STATUS_SEVERITY_WARNING
+    Error=0x3:STATUS_SEVERITY_ERROR
+    )
+
+FacilityNames=(System=0x0:FACILITY_SYSTEM
+    Runtime=0x2:FACILITY_RUNTIME
+    Stubs=0x3:FACILITY_STUBS
+    Io=0x4:FACILITY_IO_ERROR_CODE
+)
+
+LanguageNames=(English=0x409:MSG00409)
+
+MessageId=0x1
+Severity=Error
+Facility=Runtime
+SymbolicName=EVT_TEXT_2
+Language=English
+%1%n%2
+.