]> www.infradead.org Git - users/dwmw2/openconnect.git/commitdiff
Verbose reporting on reading adapter name failure. Added test to exercise wintun...
authorMarios Paouris <mspaourh@gmail.com>
Mon, 17 Jun 2024 06:10:03 +0000 (09:10 +0300)
committerMarios Paouris <mspaourh@gmail.com>
Sat, 6 Jul 2024 11:47:24 +0000 (14:47 +0300)
Signed-off-by: Marios Paouris <mspaourh@gmail.com>
.gitlab-ci.yml
Makefile.am
tests/Makefile.am
tests/list-taps.c
tests/wintun-names.c [new file with mode: 0644]
tun-win32.c

index fdd3f504491d662f5460c37cffb1c5488597386e..d086b1e87d8b997dcc45f7c5a09daecceff3fe29 100644 (file)
@@ -603,7 +603,8 @@ MinGW32/GnuTLS:
   - make -j4
 # Setup wine path so tests won't fail due to unresolved dll dependencies
   - export WINEPATH=/usr/i686-w64-mingw32/sys-root/mingw/bin\;.
-  - make VERBOSE=1 -j4 check
+# Wintun tests cannot be run under wine, wintun cannot create the interface on wine. 
+  - make VERBOSE=1 XFAIL_TESTS="wintun-names.exe" -j4 check
   tags:
   - saas-linux-small-amd64
   except:
@@ -626,7 +627,8 @@ MinGW32/OpenSSL:
   - make -j4
 # Setup wine path so tests won't fail due to unresolved dll dependencies
   - export WINEPATH=/usr/i686-w64-mingw32/sys-root/mingw/bin\;.
-  - make VERBOSE=1 -j4 check
+# Wintun tests cannot be run under wine, wintun cannot create the interface on wine. 
+  - make VERBOSE=1 XFAIL_TESTS="wintun-names.exe" -j4 check
   tags:
   - saas-linux-small-amd64
   except:
@@ -650,7 +652,8 @@ MinGW64/GnuTLS:
   - make -j4
 # Setup wine path so tests won't fail due to unresolved dll dependencies
   - export WINEPATH=/usr/x86_64-w64-mingw32/sys-root/mingw/bin\;.
-  - make VERBOSE=1 -j4 check
+# Wintun tests cannot be run under wine, wintun cannot create the interface on wine. 
+  - make VERBOSE=1 XFAIL_TESTS="wintun-names.exe" -j4 check
   tags:
   - saas-linux-small-amd64
   except:
@@ -674,7 +677,8 @@ MinGW64/OpenSSL:
   - make -j4
 # Setup wine path so tests won't fail due to unresolved dll dependencies
   - export WINEPATH=/usr/x86_64-w64-mingw32/sys-root/mingw/bin\;.
-  - make VERBOSE=1 -j4 check
+# Wintun tests cannot be run under wine, wintun cannot create the interface on wine. 
+  - make VERBOSE=1 XFAIL_TESTS="wintun-names.exe" -j4 check
   tags:
   - saas-linux-small-amd64
   except:
index b23e08127263f3cdef6fa6f3c1cf36dd91b3ea0b..e7ead12e9c39262dd3fed794a3e329ce2eb51d84 100644 (file)
@@ -1,6 +1,7 @@
 SUBDIRS = tests
 # We kind of want openconnect to be built before we try to test it
-check-recursive: openconnect$(EXEEXT)
+# We also want to have wintun library when running wintun tests, if applicable
+check-recursive: openconnect$(EXEEXT) $(WINTUN_DLL)
 # And even *building* some of tests/*.c needs libopenconnect
 install-recursive: libopenconnect.la
 all-recursive: libopenconnect.la
index 7a63da8c23694f979836306e553ba463e096153b..c03671f29ff3e5d08353259176dd7047cca213b9 100644 (file)
@@ -151,9 +151,25 @@ TESTS_ENVIRONMENT = srcdir="$(srcdir)" \
 
 C_TESTS = lzstest seqtest buftest
 
+DISTCLEANFILES =
+
 if OPENCONNECT_WIN32
 C_TESTS += list-taps
-endif
+
+list_taps_SOURCES = list-taps.c
+
+if OPENCONNECT_WINTUN
+C_TESTS += wintun-names
+
+wintun_names_SOURCES = wintun-names.c
+
+#list wintun as a script so it is "rebuilt" (copied from top-level) when the tests run
+nodist_check_SCRIPTS = $(WINTUN_DLL)
+
+WINTUN_DLL = .libs/wintun.dll
+DISTCLEANFILES += $(WINTUN_DLL)
+endif # OPENCONNECT_WINTUN
+endif # OPENCONNECT_WIN32
 
 if CHECK_DTLS
 C_TESTS += bad_dtls_test
@@ -183,6 +199,9 @@ OPENSSL = openssl
 OSSLARGS = -in $(firstword $|) -out $@ -passout pass:password
 OSSLARGSP12 = -inkey $(firstword $|) -out $@ -in $${KEYFILE%-key-pkcs8.pem}-cert.pem -passout pass:$${PASSWORD:-password}
 
+.libs/wintun.dll: ../.libs/wintun.dll
+       cp -f ../$@ $@
+
 # Strictly speaking this is only PKCS#1 for RSA. For EC it's probably
 # best described as RFC5915§4, and no idea what defines it for DSA.
 $(certsdir)/user-key-pkcs1.pem:
index e01e8313089a856151a46bb5411497d3c9dcf2f1..0ea5e1de27453b3c7541230ce79732002930cd98 100644 (file)
@@ -29,6 +29,10 @@ struct openconnect_info {
        char *ifname;
 };
 
+/* don't link linkopenconnect in this test, just for this function
+ * it won't get loaded under wine when cross compiling anyway */
+#define openconnect__win32_strerror(err) "(Actual error text not present in tests)"
+
 #define OPEN_TUN_SOFTFAIL 0
 #define OPEN_TUN_HARDFAIL -1
 
diff --git a/tests/wintun-names.c b/tests/wintun-names.c
new file mode 100644 (file)
index 0000000..496b258
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ * OpenConnect (SSL + DTLS) VPN client
+ *
+ * Copyright © 2019 David Woodhouse
+ * Copyright © 2024 Marios Paouris
+ *
+ * Authors: David Woodhouse <dwmw2@infradead.org>
+ *          Marios Paouris <mspaourh@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ */
+
+#include <config.h>
+
+#include <stdint.h>
+#include <stdio.h>
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#define __OPENCONNECT_INTERNAL_H__
+
+#define PRG_ERR                0
+#define PRG_INFO       1
+#define PRG_DEBUG      2
+#define PRG_TRACE      3
+
+#define vpn_progress(v, d, ...) do { \
+  if ( d < PRG_TRACE) { \
+    printf(__VA_ARGS__); \
+  } \
+} while (0);
+
+#define _(x) x
+
+struct openconnect_info {
+       char *ifname;
+};
+
+/* don't link linkopenconnect in this test, just for this function
+ * it won't get loaded under wine when cross compiling anyway */
+#define openconnect__win32_strerror(err) "(Actual error text not present in tests)"
+
+#define OPEN_TUN_SOFTFAIL 0
+#define OPEN_TUN_HARDFAIL -1
+
+#define WINTUN_TUNNEL_TYPE L"OpenConnect"
+
+#define VALID_WINTUN_HANDLE 7782
+#define __LIST_TAPS__
+
+#include "../tun-win32.c"
+#include "../wintun.h"
+
+static WINTUN_CREATE_ADAPTER_FUNC *WintunCreateAdapter;
+static WINTUN_CLOSE_ADAPTER_FUNC *WintunCloseAdapter;
+static WINTUN_OPEN_ADAPTER_FUNC *WintunOpenAdapter;
+static WINTUN_GET_ADAPTER_LUID_FUNC *WintunGetAdapterLUID;
+static WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC *WintunGetRunningDriverVersion;
+static WINTUN_DELETE_DRIVER_FUNC *WintunDeleteDriver;
+static WINTUN_SET_LOGGER_FUNC *WintunSetLogger;
+static WINTUN_START_SESSION_FUNC *WintunStartSession;
+static WINTUN_END_SESSION_FUNC *WintunEndSession;
+static WINTUN_GET_READ_WAIT_EVENT_FUNC *WintunGetReadWaitEvent;
+static WINTUN_RECEIVE_PACKET_FUNC *WintunReceivePacket;
+static WINTUN_RELEASE_RECEIVE_PACKET_FUNC *WintunReleaseReceivePacket;
+static WINTUN_ALLOCATE_SEND_PACKET_FUNC *WintunAllocateSendPacket;
+static WINTUN_SEND_PACKET_FUNC *WintunSendPacket;
+
+static HMODULE
+InitializeWintun(void)
+{
+    HMODULE Wintun =
+        LoadLibraryExW(L"wintun.dll", NULL, LOAD_LIBRARY_SEARCH_APPLICATION_DIR | LOAD_LIBRARY_SEARCH_SYSTEM32);
+    if (!Wintun)
+        return NULL;
+#define X(Name) ((*(FARPROC *)&Name = GetProcAddress(Wintun, #Name)) == NULL)
+    if (X(WintunCreateAdapter) || X(WintunCloseAdapter) || X(WintunOpenAdapter) || X(WintunGetAdapterLUID) ||
+        X(WintunGetRunningDriverVersion) || X(WintunDeleteDriver) || X(WintunSetLogger) || X(WintunStartSession) ||
+        X(WintunEndSession) || X(WintunGetReadWaitEvent) || X(WintunReceivePacket) || X(WintunReleaseReceivePacket) ||
+        X(WintunAllocateSendPacket) || X(WintunSendPacket))
+#undef X
+    {
+        DWORD LastError = GetLastError();
+        FreeLibrary(Wintun);
+        SetLastError(LastError);
+        return NULL;
+    }
+    return Wintun;
+}
+
+static void CALLBACK
+ConsoleLogger(_In_ WINTUN_LOGGER_LEVEL Level, _In_ DWORD64 Timestamp, _In_z_ const WCHAR *LogLine)
+{
+    WCHAR LevelMarker;
+    switch (Level)
+    {
+    case WINTUN_LOG_INFO:
+        LevelMarker = L'+';
+        break;
+    case WINTUN_LOG_WARN:
+        LevelMarker = L'-';
+        break;
+    case WINTUN_LOG_ERR:
+        LevelMarker = L'!';
+        break;
+    default:
+        return;
+    }
+    fwprintf(
+        stdout,
+        L"[%c] %ls\n",
+        LevelMarker,
+        LogLine);
+}
+
+static void
+Log(_In_ WINTUN_LOGGER_LEVEL Level, _In_z_ const WCHAR *Format, ...)
+{
+    WCHAR LogLine[0x200];
+    va_list args;
+    va_start(args, Format);
+    _vsnwprintf_s(LogLine, _countof(LogLine), _TRUNCATE, Format, args);
+    va_end(args);
+    ConsoleLogger(Level, (DWORD64) 0, LogLine);
+}
+
+WINTUN_ADAPTER_HANDLE create_wintun_adapter(PWSTR adapterName)
+{
+    DWORD LastError;
+
+    GUID ExampleGuid = { 0xdeadbeef, 0xbaad, 0xcafe, { 0x77, 0x82, 0x07, 0x07, 0x82, 0xab, 0xcd, 0xef } };
+    WINTUN_ADAPTER_HANDLE Adapter = WintunCreateAdapter(adapterName, WINTUN_TUNNEL_TYPE, &ExampleGuid);
+    if (!Adapter)
+    {
+        LastError = GetLastError();
+        Log(WINTUN_LOG_ERR, L"Failed to create adapter (%lu)", LastError);
+        goto cleanupWintun;
+    }
+
+    DWORD Version = WintunGetRunningDriverVersion();
+
+    NETIO_STATUS ret;
+    NET_LUID luid;
+    MIB_IF_ROW2 row;
+    memset(&row, 0, sizeof(MIB_IF_ROW2));
+
+    WintunGetAdapterLUID(Adapter, &luid);
+
+    row.InterfaceLuid = luid;
+
+    ret = GetIfEntry2(&row);
+
+    if (ret == 0) {
+        Log(WINTUN_LOG_INFO, L"Adapter Alias is: %ls", row.Alias);
+    }
+
+    WINTUN_SESSION_HANDLE Session = WintunStartSession(Adapter, 0x400000);
+
+    if (!Session)
+    {
+       LastError = GetLastError();
+       Log(WINTUN_LOG_ERR, L"Failed to create session (%lu)", LastError);
+        goto cleanupAdapter;
+    }
+    else {
+        ; /*Log(WINTUN_LOG_INFO, L"Wintun session started");*/
+    }
+
+    LastError = ERROR_SUCCESS;
+
+    WintunEndSession(Session);
+    /*Log(WINTUN_LOG_INFO, L"Wintun session ended");*/
+       return Adapter;
+
+cleanupAdapter:
+    WintunCloseAdapter(Adapter);
+    /*Log(WINTUN_LOG_INFO, L"Wintun adapter closed");*/
+cleanupWintun:
+    return NULL;
+}
+
+wchar_t * currentAdapterName;
+
+static intptr_t check_tun(struct openconnect_info *vpninfo, int type, char *guid, wchar_t *wname)
+{
+       if (currentAdapterName != 0) {
+               if ( ! wcscmp(currentAdapterName, wname) ) {
+                       printf("Found %s device '%S' guid %s\n",
+                       type ? "Wintun" : "Tap", wname, guid);
+                       return VALID_WINTUN_HANDLE;
+               }
+       }
+       return 0;
+}
+
+int main(void)
+{
+    HMODULE Wintun = InitializeWintun();
+    if (!Wintun) {
+        DWORD LastError = GetLastError();
+        Log(WINTUN_LOG_ERR, L"Failed to initialize Wintun (%lu)", LastError);
+        return LastError;
+    }
+
+    WintunSetLogger(ConsoleLogger);
+    Log(WINTUN_LOG_INFO, L"Wintun library loaded");
+
+    Log(WINTUN_LOG_INFO, L"MAX_ADAPTER_NAME is: %d", MAX_ADAPTER_NAME);
+
+    DWORD ret = 0;
+    WINTUN_ADAPTER_HANDLE adapter;
+    WCHAR adapterName[MAX_ADAPTER_NAME];
+
+    memset(adapterName, 0, sizeof(adapterName));
+
+    wsprintfW(adapterName, L"testAdapterNameForRunningLoops_");
+    int add = 0;
+    int len = 0;
+
+    do {
+        wsprintfW(adapterName, L"%s%d", adapterName, add);
+        add = (add+1)%10;
+        len = wcslen(adapterName);
+        Log(WINTUN_LOG_INFO, L"len=%3d name=%ls", len, adapterName);
+        adapter = create_wintun_adapter(adapterName);
+
+               if (adapter == NULL)
+                       ret = OPEN_TUN_HARDFAIL;
+               else {
+                       currentAdapterName = adapterName;
+                       ret = search_taps(NULL, check_tun);
+                       WintunCloseAdapter(adapter);
+                       Log(WINTUN_LOG_INFO, L"Wintun adapter closed");
+               }
+    } while ( (len < (MAX_ADAPTER_NAME - 1)) && (ret == VALID_WINTUN_HANDLE) );
+
+    FreeLibrary(Wintun);
+
+    if (ret == VALID_WINTUN_HANDLE) /* last test was succesful*/
+        return 0;
+
+    return 1;
+}
index 749072c2e75e360399989fcd187e053fb01c3103..f69aa11265159e413273772cb9cfdb1bce72d84a 100644 (file)
@@ -111,9 +111,21 @@ static intptr_t search_taps(struct openconnect_info *vpninfo, tap_callback *cb)
                status = RegQueryValueExA(hkey, "ComponentId", NULL, &type,
                                          (unsigned char *)buf, &len);
                if (status || type != REG_SZ) {
-                       vpn_progress(vpninfo, PRG_TRACE,
-                                    _("Cannot read %s\\%s or is not string\n"),
-                                    keyname, "ComponentId");
+                       if (status == ERROR_FILE_NOT_FOUND) {
+                               vpn_progress(vpninfo, PRG_TRACE,
+                                       _("Cannot read registry key %s\\%s: value not found\n"),
+                                       keyname, "ComponentId");
+                       }
+                       else if (type != REG_SZ) {
+                               vpn_progress(vpninfo, PRG_TRACE,
+                                       _("Cannot read registry key %s\\%s: value is not a string (%ld)\n"),
+                                       keyname, "ComponentId", type);
+                       }
+                       else {
+                               vpn_progress(vpninfo, PRG_TRACE,
+                                       _("Cannot read registry key %s\\%s or is not string: %s (%ld)\n"),
+                                       keyname, "ComponentId", openconnect__win32_strerror(status), status);
+                       }
                        RegCloseKey(hkey);
                        continue;
                }
@@ -157,9 +169,26 @@ static intptr_t search_taps(struct openconnect_info *vpninfo, tap_callback *cb)
                                         (unsigned char *)name, &len);
                RegCloseKey(hkey);
                if (status || type != REG_SZ) {
-                       vpn_progress(vpninfo, PRG_INFO,
-                                    _("Cannot read registry key %s\\%s or is not string\n"),
-                                    keyname, "Name");
+                       if (status == ERROR_FILE_NOT_FOUND) {
+                               vpn_progress(vpninfo, PRG_INFO,
+                                       _("Cannot read registry key %s\\%s: value not found\n"),
+                                       keyname, "Name");
+                       }
+                       else if (status == ERROR_MORE_DATA) {
+                               vpn_progress(vpninfo, PRG_INFO,
+                                       _("Cannot read registry key %s\\%s: character buffer too small: %llu chars required\n"),
+                                       keyname, "Name", (long long unsigned int)(len / sizeof(wchar_t)));
+                       }
+                       else if (type != REG_SZ) {
+                               vpn_progress(vpninfo, PRG_INFO,
+                                       _("Cannot read registry key %s\\%s: value is not a string (%ld)\n"),
+                                       keyname, "Name", type);
+                       }
+                       else {
+                               vpn_progress(vpninfo, PRG_INFO,
+                                       _("Cannot read registry key %s\\%s: %s (%ld)\n"),
+                                       keyname, "Name", openconnect__win32_strerror(status), status);
+                       }
                        continue;
                }