]> www.infradead.org Git - users/dwmw2/openconnect.git/commitdiff
Use hostname as Wintun ifname (if ifname not specified), v2
authorMarios Paouris <mspaourh@gmail.com>
Mon, 7 Oct 2024 05:53:35 +0000 (08:53 +0300)
committerMarios Paouris <mspaourh@gmail.com>
Tue, 5 Nov 2024 07:16:50 +0000 (09:16 +0200)
The intention for the commit 48bd28aa was a bit different
from what was actually implemented.

Although it states that "Instead, we should use the VPN server's hostname
as a sane default interface name with Wintun, and only attempt to use
TAP-Windows as a fallback in the case where Wintun can't be initialized.",
it first tries with an empty interface name, which uses the first available
interface found, whether it is tap or tun, and if that fails then creates
the same default with the server name, which will prioritize wintun over
tap.

Instead, implement the following flow:

If the user did specify an interface name:
  - Try to find an adapter with the specified name (whether it's tun
    or tap) and use it.
  - If no adapter found, try to create a wintun adapter. If wintun is
    not available then bail out.
If the user did not specify an interface name:
  - Generate a default interface name based on the server URL.
  - If the generated interface already exists don't try to use it
    and fallback to using the first available adapter.
  - If the generated interface doesn't exist, try to create a wintun
    adapter. If wintun in not available then fallback to using the first
    available adapter.

See https://gitlab.com/openconnect/openconnect-gui/-/issues/357#note_1758999655
and https://gitlab.com/openconnect/openconnect/-/issues/699#note_1762029017

Signed-off-by: Marios Paouris <mspaourh@gmail.com>
tests/list-taps.c
tests/wintun-names.c
tun-win32.c
wintun.c

index c63ab07dfe8492f50e89f066ded45c309bb2ad21..0d1cd051f3c7cb7fb9a026be70f5d24480e843bb 100644 (file)
@@ -27,6 +27,7 @@
 
 struct openconnect_info {
        char *ifname;
+       wchar_t *ifname_w;
 };
 
 /* don't link linkopenconnect in this test, just for this function
@@ -51,13 +52,14 @@ int main(void)
 {
        intptr_t ret;
        struct oc_adapter_info *list = NULL;
+       struct openconnect_info empty_info = { NULL, NULL};
 
        list = get_adapter_list(NULL);
 
        if (!list)
                return 1;
 
-       search_taps(NULL, list, print_tun);
+       search_taps(&empty_info, list, print_tun);
 
        free_adapter_list(list);
        return 0;
index ab9a073b5f693cdf9009d6bee74a36c98f05665f..8ac9e4b7adb7c60f3b85b1de85bdb1315c5245c5 100644 (file)
@@ -43,6 +43,7 @@
 
 struct openconnect_info {
        char *ifname;
+    wchar_t *ifname_w;
 };
 
 /* don't link linkopenconnect in this test, just for this function
@@ -210,6 +211,8 @@ int main(void)
 {
     _setmode(_fileno(stdout), _O_U16TEXT);
 
+    struct openconnect_info empty_info = { NULL, NULL};
+
     HMODULE Wintun = InitializeWintun();
     if (!Wintun) {
         DWORD LastError = GetLastError();
@@ -244,7 +247,7 @@ int main(void)
         if (adapter) {
             list = get_adapter_list(NULL);
             if (list) {
-                ret = check_tun(NULL, list, adapterName);
+                ret = check_tun(&empty_info, list, adapterName);
                 free_adapter_list(list);
             }
 
@@ -274,7 +277,7 @@ int main(void)
     if (adapter) {
         list = get_adapter_list(NULL);
         if (list) {
-            ret = check_tun(NULL, list, adapterName);
+            ret = check_tun(&empty_info, list, adapterName);
             free_adapter_list(list);
         }
 
index c09756b274542e62ed9c1bd9a1935b129b13e96b..39ce0da7a695cf6392aef54398a252eabebbb32f 100644 (file)
@@ -93,6 +93,24 @@ static void free_adapter_list(struct oc_adapter_info *first)
        }
 }
 
+static struct oc_adapter_info * adapter_alloc(struct openconnect_info *vpninfo, int type, const wchar_t* name, const char *guid)
+{
+       struct oc_adapter_info *new = malloc(sizeof(struct oc_adapter_info));
+
+       if (!new) {
+               vpn_progress(vpninfo, PRG_ERR,
+                                       _("Cannot allocate memory for adapter info\n"));
+               return NULL;
+       }
+
+       new->type = type;
+       new->ifname = wcsdup(name);
+       new->guid = strdup(guid);
+       new->next = NULL;
+
+       return new;
+}
+
 static struct oc_adapter_info * find_adapter_by_name(struct openconnect_info *vpninfo, struct oc_adapter_info *adapter_list, wchar_t *wname)
 {
        struct oc_adapter_info *this = adapter_list;
@@ -112,14 +130,44 @@ static intptr_t search_taps(struct openconnect_info *vpninfo, struct oc_adapter_
 {
        struct oc_adapter_info *this = adapter_list;
        intptr_t ret = OPEN_TUN_SOFTFAIL;
+       int wintun_failed_loading = 0;
 
        while (this && ret == OPEN_TUN_SOFTFAIL) {
                if (this->type != ADAPTER_NONE) {
-                       ret = cb(vpninfo, this->type, this->guid, this->ifname);
+                       if (vpninfo->ifname_w && wcscmp(this->ifname, vpninfo->ifname_w)) {
+                               vpn_progress(vpninfo, PRG_DEBUG,
+                                               _("Ignoring non-matching interface \"%S\"\n"),
+                                               this->ifname);
+                               ret = OPEN_TUN_SOFTFAIL; /* keep searching */
+                       }
+                       else {
+                               ret = cb(vpninfo, this->type, this->guid, this->ifname);
+
+                               if (!vpninfo->ifname_w ) {
+                                       /* we are not searching for a specific adapter */
+
+                                       if (this->type == ADAPTER_WINTUN && ret == OPEN_TUN_SOFTFAIL) {
+                                               /* keep track of wintun failing to load */
+                                               wintun_failed_loading = 1;
+                                       }
+
+                                       if (ret == OPEN_TUN_HARDFAIL) {
+                                               /* the adapter failed to open */
+                                               ret = OPEN_TUN_SOFTFAIL; /* keep searching; we might be able to open a TAP */
+                                       }
+                               }
+                       }
                }
                this = this->next;
        }
 
+       if (ret == OPEN_TUN_SOFTFAIL && (!vpninfo->ifname_w) && wintun_failed_loading) {
+               /* wintun failed to load at least once when searching for any usable adapter
+                  notify the caller of a hard failure to force them not to try a wintun fallback
+               */
+               ret = OPEN_TUN_HARDFAIL;
+       }
+
        return ret;
 }
 
@@ -261,21 +309,14 @@ static struct oc_adapter_info * get_adapter_list(struct openconnect_info *vpninf
                        continue;
                }
 
-               struct oc_adapter_info *new = malloc(sizeof(struct oc_adapter_info));
+               struct oc_adapter_info *new = adapter_alloc(vpninfo, adapter_type, name, buf);
 
                if (!new) {
-                       vpn_progress(vpninfo, PRG_ERR,
-                                    _("Cannot allocate memory for adapter info\n"));
                        free_adapter_list(first);
                        first = NULL;
                        break;
                }
 
-               new->type = adapter_type;
-               new->ifname = wcsdup(name);
-               new->guid = strdup(buf);
-               new->next = NULL;
-
                if (last) {
                        last->next = new;
                }
@@ -292,6 +333,42 @@ static struct oc_adapter_info * get_adapter_list(struct openconnect_info *vpninf
 
 #ifndef __LIST_TAPS__
 
+static struct oc_adapter_info * create_wintun_adapter(struct openconnect_info *vpninfo, tap_callback *cb, intptr_t *fh)
+{
+       struct oc_adapter_info *ret = NULL;
+
+       int retw = create_wintun(vpninfo);
+       if (!retw) {
+               char wintun_adapter_guid[40];
+               /* we have a wintun adapter. Get its guid and return adapter info */
+               if (! get_wintun_adapter_guid(vpninfo, wintun_adapter_guid, sizeof(wintun_adapter_guid))) {
+                       ret = adapter_alloc(vpninfo, ADAPTER_WINTUN, vpninfo->ifname_w, wintun_adapter_guid);
+               }
+       } else if (retw == -EPERM) {
+               vpn_progress(vpninfo, PRG_ERR,
+                                       _("Access denied creating Wintun adapter. Are you running with Administrator privileges?\n"));
+       }
+       /* no need to worry about other return codes for retw (-ENOENT or -EIO) - we will return NULL */
+
+       if ( ret ) {
+               /* we have a wintun adapter. Open it via the callback */
+               intptr_t retcb = cb(vpninfo, ret->type, ret->guid, ret->ifname);
+
+               if (retcb == OPEN_TUN_SOFTFAIL || retcb == OPEN_TUN_HARDFAIL) {
+                       os_shutdown_wintun(vpninfo);
+                       free_adapter_list(ret);
+                       ret = NULL;
+               }
+               *fh = retcb;
+       }
+       else {
+               os_shutdown_wintun(vpninfo);
+               *fh = OPEN_TUN_SOFTFAIL;
+       }
+
+       return ret;
+}
+
 static int get_adapter_index(struct openconnect_info *vpninfo, char *guid)
 {
        struct oc_text_buf *buf = buf_alloc();
@@ -489,9 +566,11 @@ static intptr_t open_tuntap(struct openconnect_info *vpninfo, char *guid, wchar_
                             FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
                             0);
        if (tun_fh == INVALID_HANDLE_VALUE) {
-               vpn_progress(vpninfo, PRG_ERR, _("Failed to open %s\n"),
-                            devname);
-               return OPEN_TUN_SOFTFAIL;
+               char *errstr = openconnect__win32_strerror(GetLastError());
+               vpn_progress(vpninfo, PRG_ERR, _("Failed to open %s: %s\n"),
+                            devname, errstr);
+               free(errstr);
+               return OPEN_TUN_HARDFAIL;
 
        }
        vpn_progress(vpninfo, PRG_DEBUG, _("Opened tun device %S\n"), wname);
@@ -553,13 +632,6 @@ static intptr_t open_tun(struct openconnect_info *vpninfo, int adapter_type, cha
 {
        intptr_t ret = -1;
 
-       if (vpninfo->ifname_w && wcscmp(wname, vpninfo->ifname_w)) {
-               vpn_progress(vpninfo, PRG_DEBUG,
-                            _("Ignoring non-matching interface \"%S\"\n"),
-                            wname);
-               return OPEN_TUN_SOFTFAIL;
-       }
-
        if (adapter_type == ADAPTER_TUNTAP)
                ret = open_tuntap(vpninfo, guid, wname);
        else
@@ -624,6 +696,13 @@ static intptr_t create_ifname_w(struct openconnect_info *vpninfo,
        return 0;
 }
 
+static void clear_ifname_w(struct openconnect_info *vpninfo)
+{
+       free(vpninfo->ifname_w);
+       vpninfo->ifname_w = NULL;
+}
+
+
 intptr_t os_setup_tun(struct openconnect_info *vpninfo)
 {
        intptr_t ret;
@@ -635,42 +714,65 @@ intptr_t os_setup_tun(struct openconnect_info *vpninfo)
                goto safe_return;
 
        if (vpninfo->ifname) {
+               /* the user specified an interface name; try to find an existing adapter */
                ret = create_ifname_w(vpninfo, vpninfo->ifname);
                if (ret)
                        goto safe_return;
-       }
 
-       ret = search_taps(vpninfo, list, open_tun);
+               ret = search_taps(vpninfo, list, open_tun);
 
-       if (ret == OPEN_TUN_SOFTFAIL) {
-               if (!vpninfo->ifname_w) {
-                       ret = create_ifname_w(vpninfo, vpninfo->hostname);
-                       if (ret)
-                               goto safe_return;
-               }
-
-               /* Try creating a Wintun instead of TAP */
-               int retw = create_wintun(vpninfo);
-               if (!retw) {
-                       char wintun_adapter_guid[40];
-                       /* we have a wintun adapter. Get its guid and open it via the callback */
-                       if (get_wintun_adapter_guid(vpninfo, wintun_adapter_guid, sizeof(wintun_adapter_guid))) {
-                               ret = OPEN_TUN_HARDFAIL;
+               if (ret == OPEN_TUN_SOFTFAIL) {
+                       /* no adapter was found; Try creating a Wintun adapter */
+                       struct oc_adapter_info * new = create_wintun_adapter(vpninfo, open_tun, &ret);
+                       if ( new ) {
+                               /* add the new adapter to the beginning of the list so it can be freed */
+                               new->next = list;
+                               list = new;
                        }
                        else {
-                               ret = open_tun(vpninfo, ADAPTER_WINTUN, wintun_adapter_guid, vpninfo->ifname_w);
-
-                               if (ret == OPEN_TUN_SOFTFAIL)
-                                       ret = OPEN_TUN_HARDFAIL;
+                               clear_ifname_w(vpninfo);
+                               ret = OPEN_TUN_HARDFAIL;
                        }
+               }
+               else if (ret == OPEN_TUN_HARDFAIL) {
+                       clear_ifname_w(vpninfo);
+               }
+       }
+       else {
+               /* the user did not specify an interface name; try create a wintun default based on hostname */
+               ret = create_ifname_w(vpninfo, vpninfo->hostname);
+               if (ret)
+                       goto safe_return;
+
+               /* check if the adapter already exists */
+               struct oc_adapter_info * new = find_adapter_by_name(vpninfo, list, vpninfo->ifname_w);
+
+               if (new) {
+                       /* don't create a wintun adapter with a default name;
+             * unfortunately, wintun will rename an existing adapter when creating an adapter with the same name
+             * see https://git.zx2c4.com/wintun/tree/api/adapter.c?id=41624504341307f7f55afe72e86d5d8c76f81c0e#n292
+                        */
+                       vpn_progress(vpninfo, PRG_INFO,
+                                               _("Adapter %S already exists. Cannot use it as a default wintun adapter.\n"), 
+                                               vpninfo->ifname_w);
+               }
 
-                       if (ret == OPEN_TUN_HARDFAIL) {
-                               os_shutdown_wintun(vpninfo);
+               if ( !new ) {
+                       new = create_wintun_adapter(vpninfo, open_tun, &ret);
+                       if ( new ) {
+                               /* add the new adapter to the beginning of the list so it can be freed */
+                               new->next = list;
+                               list = new;
                        }
-               } else if (retw == -EPERM) {
-                       ret = OPEN_TUN_HARDFAIL;
-                       vpn_progress(vpninfo, PRG_ERR,
-                                    _("Access denied creating Wintun adapter. Are you running with Administrator privileges?\n"));
+               }
+               else {
+                       new = NULL;
+               }
+
+               if ( !new ) {
+                       /* could not create wintun adapter; cleanup ifname_w and fallback to the first available tap */
+                       clear_ifname_w(vpninfo);
+                       ret = search_taps(vpninfo, list, open_tun);
                }
        }
 
index 9b13062fa718597d15e815ce461337b281b663b0..8900d73f1adf734793b922b0b7dfe2ddbfdb1365 100644 (file)
--- a/wintun.c
+++ b/wintun.c
@@ -143,7 +143,7 @@ intptr_t open_wintun(struct openconnect_info *vpninfo, char *guid, wchar_t *wnam
        intptr_t ret;
 
        if (init_wintun(vpninfo))
-               return 0;
+               return OPEN_TUN_SOFTFAIL;
 
        if (!vpninfo->wintun_adapter) {
                vpninfo->wintun_adapter = WintunOpenAdapter(wname);
@@ -153,7 +153,7 @@ intptr_t open_wintun(struct openconnect_info *vpninfo, char *guid, wchar_t *wnam
                                     wname, errstr);
                        free(errstr);
 
-                       ret = OPEN_TUN_SOFTFAIL;
+                       ret = OPEN_TUN_HARDFAIL;
                        goto out;
                }
        }