lib_srcs_gnutls = gnutls.c gnutls_tpm.c gnutls_tpm2.c
lib_srcs_openssl = openssl.c openssl-pkcs11.c
-lib_srcs_win32 = tun-win32.c sspi.c
+lib_srcs_win32 = wintun.c tun-win32.c sspi.c
lib_srcs_posix = tun.c
lib_srcs_gssapi = gssapi.c
lib_srcs_iconv = iconv.c
CloseHandle(vpninfo->ssl_event);
if (vpninfo->dtls_event)
CloseHandle(vpninfo->dtls_event);
+ free(vpninfo->ifname_w);
#endif
free(vpninfo->peer_addr);
free(vpninfo->ip_info.gateway_addr);
#define SECURITY_WIN32 1
#endif
#include <security.h>
+
+#ifndef _Out_cap_c_
+#define _Out_cap_c_(sz)
+#endif
+#ifndef _Ret_bytecount_
+#define _Ret_bytecount_(sz)
+#endif
+#include "wintun.h"
#else
#include <sys/types.h>
#include <sys/socket.h>
int ip6_fd;
#endif
#ifdef _WIN32
+ HMODULE wintun;
+ wchar_t *ifname_w;
+ WINTUN_ADAPTER_HANDLE wintun_adapter;
+ WINTUN_SESSION_HANDLE wintun_session;
+
HANDLE tun_fh;
OVERLAPPED tun_rd_overlap, tun_wr_overlap;
int tun_idx, tun_rd_pending;
int os_write_tun(struct openconnect_info *vpninfo, struct pkt *pkt);
intptr_t os_setup_tun(struct openconnect_info *vpninfo);
+#ifdef _WIN32
+#define OPEN_TUN_SOFTFAIL 0
+#define OPEN_TUN_HARDFAIL -1
+
+/* wintun.c */
+void os_shutdown_wintun(struct openconnect_info *vpninfo);
+int os_read_wintun(struct openconnect_info *vpninfo, struct pkt *pkt);
+int os_write_wintun(struct openconnect_info *vpninfo, struct pkt *pkt);
+intptr_t os_setup_wintun(struct openconnect_info *vpninfo);
+int setup_wintun_fd(struct openconnect_info *vpninfo, intptr_t tun_fd);
+intptr_t open_wintun(struct openconnect_info *vpninfo, char *guid, wchar_t *wname);
+int create_wintun(struct openconnect_info *vpninfo);
+#endif
+
/* {gnutls,openssl}-dtls.c */
int start_dtls_handshake(struct openconnect_info *vpninfo, int dtls_fd);
int dtls_try_handshake(struct openconnect_info *vpninfo);
char *ifname;
};
+#define OPEN_TUN_SOFTFAIL 0
+#define OPEN_TUN_HARDFAIL -1
+
#define __LIST_TAPS__
#include "../tun-win32.c"
-static intptr_t print_tun(struct openconnect_info *vpninfo, char *guid, wchar_t *wname)
+static intptr_t print_tun(struct openconnect_info *vpninfo, int type, char *guid, wchar_t *wname)
{
- printf("Found tun device '%S'\n", wname);
+ printf("Found %s device '%S' guid %s\n",
+ type ? "Wintun" : "Tap", wname, guid);
return 0;
}
int main(void)
{
- search_taps(NULL, print_tun, 1);
+ search_taps(NULL, print_tun);
return 0;
}
#define ADAPTERS_KEY CONTROL_KEY "Class\\" NETDEV_GUID
#define CONNECTIONS_KEY CONTROL_KEY "Network\\" NETDEV_GUID
-typedef intptr_t (tap_callback)(struct openconnect_info *vpninfo, char *idx, wchar_t *name);
+#define ADAPTER_TUNTAP 0
+#define ADAPTER_WINTUN 1
-static intptr_t search_taps(struct openconnect_info *vpninfo, tap_callback *cb, int all)
+typedef intptr_t (tap_callback)(struct openconnect_info *vpninfo, int type, char *idx, wchar_t *name);
+
+#define SEARCH_CONTINUE 0
+#define SEARCH_DONE 1
+
+static intptr_t search_taps(struct openconnect_info *vpninfo, tap_callback *cb)
{
LONG status;
HKEY adapters_key, hkey;
DWORD len, type;
+ int adapter_type;
char buf[40];
wchar_t name[40];
char keyname[strlen(CONNECTIONS_KEY) + sizeof(buf) + 1 + strlen("\\Connection")];
- int i = 0, found = 0;
- intptr_t ret = -1;
+ int i = 0;
+ intptr_t ret = OPEN_TUN_SOFTFAIL;
status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, ADAPTERS_KEY, 0,
KEY_READ, &adapters_key);
_("Error accessing registry key for network adapters\n"));
return -EIO;
}
- while (1) {
+ while (ret == OPEN_TUN_SOFTFAIL) {
len = sizeof(buf);
status = RegEnumKeyExA(adapters_key, i++, buf, &len,
NULL, NULL, NULL, NULL);
if (status) {
if (status != ERROR_NO_MORE_ITEMS)
- ret = -1;
+ ret = OPEN_TUN_HARDFAIL;
break;
}
RegCloseKey(hkey);
continue;
}
- if (strcmp(buf, TAP_COMPONENT_ID) &&
- strcmp(buf, "root\\" TAP_COMPONENT_ID)) {
- vpn_progress(vpninfo, PRG_TRACE,
- _("%s\\ComponentId is '%s' not '%s'\n"),
- keyname, buf, TAP_COMPONENT_ID);
+ if (!strcmp(buf, TAP_COMPONENT_ID) || !strcmp(buf, "root\\" TAP_COMPONENT_ID))
+ adapter_type = ADAPTER_TUNTAP;
+ else if (!strcmp(buf, "wintun"))
+ adapter_type = ADAPTER_WINTUN;
+ else {
+ vpn_progress(vpninfo, PRG_TRACE, _("%s\\ComponentId is unknown '%s'\n"),
+ keyname, buf);
RegCloseKey(hkey);
continue;
}
continue;
}
- found++;
-
- ret = cb(vpninfo, buf, name);
- if (!ret && !all)
- break;
+ ret = cb(vpninfo, adapter_type, buf, name);
}
RegCloseKey(adapters_key);
- if (!found)
- vpn_progress(vpninfo, PRG_ERR,
- _("No Windows-TAP adapters found. Is the driver installed?\n"));
-
return ret;
}
* going to manage for GetAdaptersInfo(). Give up. */
return buf_free(buf);
}
-
status = GetAdapterIndex((void *)buf->data, &idx);
buf_free(buf);
if (status == NO_ERROR) {
break;
}
}
-
free(adapters_buf);
return ret;
}
-static intptr_t open_tun(struct openconnect_info *vpninfo, char *guid, wchar_t *wname)
+static intptr_t open_tuntap(struct openconnect_info *vpninfo, char *guid, wchar_t *wname)
{
- struct oc_text_buf *namebuf = buf_alloc();
char devname[80];
HANDLE tun_fh;
ULONG data[3];
DWORD len;
- buf_append_from_utf16le(namebuf, wname);
- if (buf_error(namebuf)) {
- err_out:
- buf_free(namebuf);
- return -1;
- }
-
- if (vpninfo->ifname && strcmp(namebuf->data, vpninfo->ifname)) {
- vpn_progress(vpninfo, PRG_DEBUG,
- _("Ignoring non-matching TAP interface \"%s\"\n"),
- namebuf->data);
- goto err_out;
- }
-
snprintf(devname, sizeof(devname), DEVTEMPLATE, guid);
tun_fh = CreateFileA(devname, GENERIC_WRITE|GENERIC_READ, 0, 0,
OPEN_EXISTING,
if (tun_fh == INVALID_HANDLE_VALUE) {
vpn_progress(vpninfo, PRG_ERR, _("Failed to open %s\n"),
devname);
- goto err_out;
+ return OPEN_TUN_SOFTFAIL;
}
- vpn_progress(vpninfo, PRG_DEBUG, _("Opened tun device %s\n"), namebuf->data);
+ vpn_progress(vpninfo, PRG_DEBUG, _("Opened tun device %S\n"), wname);
if (!DeviceIoControl(tun_fh, TAP_IOCTL_GET_VERSION,
data, sizeof(&data), data, sizeof(data),
vpn_progress(vpninfo, PRG_ERR,
_("Failed to obtain TAP driver version: %s\n"), errstr);
free(errstr);
- goto err_out;
+ return OPEN_TUN_HARDFAIL;
}
if (data[0] < 9 || (data[0] == 9 && data[1] < 9)) {
vpn_progress(vpninfo, PRG_ERR,
_("Error: TAP-Windows driver v9.9 or greater is required (found %ld.%ld)\n"),
data[0], data[1]);
- goto err_out;
+ return -1;
}
vpn_progress(vpninfo, PRG_DEBUG, "TAP-Windows driver v%ld.%ld (%ld)\n",
data[0], data[1], data[2]);
vpn_progress(vpninfo, PRG_ERR,
_("Failed to set TAP IP addresses: %s\n"), errstr);
free(errstr);
- goto err_out;
+ return OPEN_TUN_HARDFAIL;
}
data[0] = 1;
vpn_progress(vpninfo, PRG_ERR,
_("Failed to set TAP media status: %s\n"), errstr);
free(errstr);
- goto err_out;
+ return OPEN_TUN_HARDFAIL;
}
+ return (intptr_t)tun_fh;
+}
+
+static intptr_t open_tun(struct openconnect_info *vpninfo, int adapter_type, char *guid, wchar_t *wname)
+{
+ 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 0;
+ }
+
+ if (adapter_type == ADAPTER_TUNTAP)
+ ret = open_tuntap(vpninfo, guid, wname);
+ else
+ ret = open_wintun(vpninfo, guid, wname);
+
+ if (ret == OPEN_TUN_SOFTFAIL || ret == OPEN_TUN_HARDFAIL)
+ return ret;
+
+ /*
+ * We have found the adapter and opened it successfully. Now set
+ * vpninfo->ifname accordingly, if necessary, and find $TUNIDX
+ * for the script to use to configure it.
+ */
if (!vpninfo->ifname) {
+ struct oc_text_buf *namebuf = buf_alloc();
+
+ buf_append_from_utf16le(namebuf, wname);
+ if (buf_error(namebuf)) {
+ vpn_progress(vpninfo, PRG_ERR, _("Could not convert interface name to UTF-8\n"));
+ os_shutdown_tun(vpninfo);
+
+ buf_free(namebuf);
+ return OPEN_TUN_HARDFAIL;
+ }
vpninfo->ifname = namebuf->data;
namebuf->data = NULL;
+ buf_free(namebuf);
}
- buf_free(namebuf);
get_adapter_index(vpninfo, guid);
- return (intptr_t)tun_fh;
+ vpn_progress(vpninfo, PRG_INFO, _("Using %s device '%s', index %d\n"),
+ adapter_type ? "Wintun" : "TAP-Windows",
+ vpninfo->ifname, vpninfo->tun_idx);
+
+ return ret;
}
intptr_t os_setup_tun(struct openconnect_info *vpninfo)
{
- return search_taps(vpninfo, open_tun, 0);
+ if (vpninfo->ifname) {
+ struct oc_text_buf *ifname_buf = buf_alloc();
+ buf_append_utf16le(ifname_buf, vpninfo->ifname);
+
+ if (buf_error(ifname_buf)) {
+ vpn_progress(vpninfo, PRG_ERR, _("Could not construct interface name\n"));
+ return buf_free(ifname_buf);
+ }
+
+ free(vpninfo->ifname_w);
+ vpninfo->ifname_w = (wchar_t *)ifname_buf->data;
+ ifname_buf->data = NULL;
+ buf_free(ifname_buf);
+ }
+
+ intptr_t ret = search_taps(vpninfo, open_tun);
+
+ if (ret == OPEN_TUN_SOFTFAIL) {
+ /* If an interface name was given, try creating a Wintun */
+ if (vpninfo->ifname && !create_wintun(vpninfo)) {
+ ret = search_taps(vpninfo, open_tun);
+
+ if (ret == OPEN_TUN_SOFTFAIL)
+ ret = OPEN_TUN_HARDFAIL;
+ if (ret == OPEN_TUN_HARDFAIL)
+ os_shutdown_wintun(vpninfo);
+ }
+ }
+
+ if (ret == OPEN_TUN_SOFTFAIL) {
+ vpn_progress(vpninfo, PRG_ERR,
+ _("No Windows-TAP adapters found. Is the driver installed?\n"));
+ if (!vpninfo->ifname)
+ vpn_progress(vpninfo, PRG_ERR,
+ _("Alternatively, specify an interface name to try creating it with Wintun\n"));
+ ret = OPEN_TUN_HARDFAIL;
+ }
+
+ return ret;
}
int os_read_tun(struct openconnect_info *vpninfo, struct pkt *pkt)
{
DWORD pkt_size;
+ if (vpninfo->wintun)
+ return os_read_wintun(vpninfo, pkt);
reread:
if (!vpninfo->tun_rd_pending &&
DWORD err;
char *errstr;
+ if (vpninfo->wintun)
+ return os_write_wintun(vpninfo, pkt);
+
if (WriteFile(vpninfo->tun_fh, pkt->data, pkt->len, &pkt_size, &vpninfo->tun_wr_overlap)) {
vpn_progress(vpninfo, PRG_TRACE,
_("Wrote %ld bytes to tun\n"), pkt_size);
void os_shutdown_tun(struct openconnect_info *vpninfo)
{
script_config_tun(vpninfo, "disconnect");
+
+ if (vpninfo->wintun) {
+ os_shutdown_wintun(vpninfo);
+ return;
+ }
+
CloseHandle(vpninfo->tun_fh);
vpninfo->tun_fh = NULL;
CloseHandle(vpninfo->tun_rd_overlap.hEvent);
ULONG data;
DWORD len;
+ if (vpninfo->wintun)
+ return setup_wintun_fd(vpninfo, tun_fd);
+
/* Toggle media status so that network location awareness picks up all the configuration
that occurred and properly assigns the network so the user can adjust firewall
settings. */
--- /dev/null
+/*
+ * OpenConnect (SSL + DTLS) VPN client
+ *
+ * Copyright © 2021 David Woodhouse.
+ *
+ * Author: David Woodhouse <dwmw2@infradead.org>
+ *
+ * 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>
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <winioctl.h>
+#include <winsock2.h>
+#include <ws2ipdef.h>
+#include <iphlpapi.h>
+#include <netioapi.h>
+
+#include <errno.h>
+#include <stdio.h>
+
+#include "openconnect-internal.h"
+
+static WINTUN_CREATE_ADAPTER_FUNC WintunCreateAdapter;
+static WINTUN_DELETE_ADAPTER_FUNC WintunDeleteAdapter;
+static WINTUN_DELETE_POOL_DRIVER_FUNC WintunDeletePoolDriver;
+static WINTUN_ENUM_ADAPTERS_FUNC WintunEnumAdapters;
+static WINTUN_FREE_ADAPTER_FUNC WintunFreeAdapter;
+static WINTUN_OPEN_ADAPTER_FUNC WintunOpenAdapter;
+static WINTUN_GET_ADAPTER_LUID_FUNC WintunGetAdapterLUID;
+static WINTUN_GET_ADAPTER_NAME_FUNC WintunGetAdapterName;
+static WINTUN_SET_ADAPTER_NAME_FUNC WintunSetAdapterName;
+static WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC WintunGetRunningDriverVersion;
+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 struct openconnect_info *logger_vpninfo;
+
+#define WINTUN_POOL_NAME L"OpenConnect"
+#define WINTUN_RING_SIZE 0x400000
+
+static CALLBACK void wintun_log_fn(WINTUN_LOGGER_LEVEL wlvl, const WCHAR *wmsg)
+{
+ int lvl = (wlvl == WINTUN_LOG_INFO) ? PRG_INFO : PRG_ERR;
+
+ /* Sadly, Wintun doesn't provide any context information in the callback */
+ if (!logger_vpninfo)
+ return;
+
+ vpn_progress(logger_vpninfo, lvl, "%d: %S\n", wlvl, wmsg);
+}
+
+static int init_wintun(struct openconnect_info *vpninfo)
+{
+ if (!vpninfo->wintun) {
+ vpninfo->wintun = LoadLibraryExW(L"wintun.dll", NULL,
+ LOAD_LIBRARY_SEARCH_APPLICATION_DIR |
+ LOAD_LIBRARY_SEARCH_SYSTEM32);
+ if (!vpninfo->wintun) {
+ vpn_progress(vpninfo, PRG_DEBUG, _("Could not load wintun.dll\n"));
+ return -ENOENT;
+ }
+#define Resolve(Name) ((Name = (void *)GetProcAddress(vpninfo->wintun, #Name)) == NULL)
+
+ if (Resolve(WintunCreateAdapter) || Resolve(WintunDeleteAdapter) ||
+ Resolve(WintunDeletePoolDriver) || Resolve(WintunEnumAdapters) ||
+ Resolve(WintunFreeAdapter) || Resolve(WintunOpenAdapter) ||
+ Resolve(WintunGetAdapterLUID) || Resolve(WintunGetAdapterName) ||
+ Resolve(WintunSetAdapterName) || Resolve(WintunGetRunningDriverVersion) ||
+ Resolve(WintunSetLogger) || Resolve(WintunStartSession) ||
+ Resolve(WintunEndSession) || Resolve(WintunGetReadWaitEvent) ||
+ Resolve(WintunReceivePacket) || Resolve(WintunReleaseReceivePacket) ||
+ Resolve(WintunAllocateSendPacket) || Resolve(WintunSendPacket)) {
+
+ vpn_progress(vpninfo, PRG_ERR, _("Could not resolve functions from wintun.dll\n"));
+ FreeLibrary(vpninfo->wintun);
+ vpninfo->wintun = NULL;
+ return -EIO;
+ }
+
+ logger_vpninfo = vpninfo;
+ WintunSetLogger(wintun_log_fn);
+ }
+
+ return 0;
+}
+
+int create_wintun(struct openconnect_info *vpninfo)
+{
+ if (init_wintun(vpninfo))
+ return -1;
+
+ vpninfo->wintun_adapter = WintunCreateAdapter(WINTUN_POOL_NAME,
+ vpninfo->ifname_w, NULL, NULL);
+ if (vpninfo->wintun_adapter)
+ return 0;
+
+ char *errstr = openconnect__win32_strerror(GetLastError());
+ vpn_progress(vpninfo, PRG_ERR, "Could not create Wintun adapter '%S': %s\n",
+ vpninfo->ifname_w, errstr);
+ free(errstr);
+ return -EIO;
+}
+
+intptr_t open_wintun(struct openconnect_info *vpninfo, char *guid, wchar_t *wname)
+{
+ intptr_t ret;
+
+ if (init_wintun(vpninfo))
+ return 0;
+
+ if (!vpninfo->wintun_adapter) {
+ vpninfo->wintun_adapter = WintunOpenAdapter(WINTUN_POOL_NAME,
+ wname);
+ if (!vpninfo->wintun_adapter) {
+ char *errstr = openconnect__win32_strerror(GetLastError());
+ vpn_progress(vpninfo, PRG_ERR, "Could not open Wintun adapter '%S': %s\n",
+ wname, errstr);
+ free(errstr);
+
+ ret = OPEN_TUN_SOFTFAIL;
+ goto out;
+ }
+ }
+
+ if (vpninfo->ip_info.addr) {
+ /*
+ * For now, vpnc-script-win.js depends on us setting the Legacy IP
+ * address on the interface. Which of course assumes there *is* a
+ * Legacy IP configuration not just IPv6. This is kind of horrid
+ * but stay compatible with it for now. In order to set the address
+ * up, we may first need to *remove* it from any other interface
+ * that has it, even if the other interface is down. Testing with
+ * a TAP-Windows interface and then Wintun was failing until I made
+ * it explicitly delete the IP address first. The later call to
+ * CreateUnicastIpAddressEntry() was apparently succeeding, but
+ * wasn't changing anything. Yay Windows!
+ */
+ MIB_UNICASTIPADDRESS_ROW AddressRow;
+ InitializeUnicastIpAddressEntry(&AddressRow);
+ WintunGetAdapterLUID(vpninfo->wintun_adapter, &AddressRow.InterfaceLuid);
+ AddressRow.Address.Ipv4.sin_family = AF_INET;
+ AddressRow.Address.Ipv4.sin_addr.S_un.S_addr = htonl(inet_addr(vpninfo->ip_info.addr));
+ AddressRow.OnLinkPrefixLength = 32;
+
+ PMIB_UNICASTIPADDRESS_TABLE pipTable = NULL;
+ DWORD LastError = GetUnicastIpAddressTable(AF_INET, &pipTable);
+ if (LastError == ERROR_SUCCESS) {
+ for (int i = 0; i < pipTable->NumEntries; i++) {
+ if (pipTable->Table[i].Address.Ipv4.sin_addr.S_un.S_addr ==
+ AddressRow.Address.Ipv4.sin_addr.S_un.S_addr) {
+ DeleteUnicastIpAddressEntry(&pipTable->Table[i]);
+ }
+ }
+ }
+
+ LastError = CreateUnicastIpAddressEntry(&AddressRow);
+ if (LastError != ERROR_SUCCESS) {
+ char *errstr = openconnect__win32_strerror(GetLastError());
+ vpn_progress(vpninfo, PRG_ERR,
+ _("Failed to set Legacy IP address on Wintun: %s\n"),
+ errstr);
+ free(errstr);
+
+ ret = OPEN_TUN_HARDFAIL;
+ goto out;
+ }
+ }
+
+ vpninfo->wintun_session = WintunStartSession(vpninfo->wintun_adapter, 0x400000);
+ if (!vpninfo->wintun_session) {
+ char *errstr = openconnect__win32_strerror(GetLastError());
+ vpn_progress(vpninfo, PRG_ERR, _("Failed to create Wintun session: %s"),
+ errstr);
+ free(errstr);
+
+ ret = OPEN_TUN_HARDFAIL;
+ goto out;
+ }
+
+ DWORD ver = WintunGetRunningDriverVersion();
+ vpn_progress(vpninfo, PRG_DEBUG, _("Loaded Wintun v%d.%d\n"),
+ (int)ver >> 16, (int)ver & 0xff);
+
+ return 1;
+ out:
+ os_shutdown_wintun(vpninfo);
+ return ret;
+}
+
+int os_read_wintun(struct openconnect_info *vpninfo, struct pkt *pkt)
+{
+ DWORD tun_len;
+ BYTE *tun_pkt = WintunReceivePacket(vpninfo->wintun_session,
+ &tun_len);
+ if (tun_pkt && tun_len < pkt->len) {
+ memcpy(pkt->data, tun_pkt, tun_len);
+ pkt->len = tun_len;
+ WintunReleaseReceivePacket(vpninfo->wintun_session, tun_pkt);
+ return 0;
+ }
+ return -1;
+}
+
+int os_write_wintun(struct openconnect_info *vpninfo, struct pkt *pkt)
+{
+ BYTE *tun_pkt = WintunAllocateSendPacket(vpninfo->wintun_session,
+ pkt->len);
+ if (tun_pkt) {
+ memcpy(tun_pkt, pkt->data, pkt->len);
+ WintunSendPacket(vpninfo->wintun_session, tun_pkt);
+ return 0;
+ }
+ return -1;
+}
+
+void os_shutdown_wintun(struct openconnect_info *vpninfo)
+{
+ if (vpninfo->wintun_session) {
+ WintunEndSession(vpninfo->wintun_session);
+ vpninfo->wintun_session = NULL;
+ }
+ if (vpninfo->wintun_adapter) {
+ BOOL rr;
+ WintunDeleteAdapter(vpninfo->wintun_adapter, FALSE, &rr);
+ vpninfo->wintun_adapter = NULL;
+ }
+ logger_vpninfo = NULL;
+ FreeLibrary(vpninfo->wintun);
+ vpninfo->wintun = NULL;
+}
+
+int setup_wintun_fd(struct openconnect_info *vpninfo, intptr_t tun_fd)
+{
+ vpninfo->tun_rd_overlap.hEvent = WintunGetReadWaitEvent(vpninfo->wintun_session);
+ monitor_read_fd(vpninfo, tun);
+ vpninfo->tun_fh = (HANDLE)tun_fd;
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 OR MIT
+ *
+ * Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
+ */
+
+#pragma once
+
+#include <windows.h>
+#include <ipexport.h>
+#include <ifdef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * A handle representing Wintun adapter
+ */
+typedef void *WINTUN_ADAPTER_HANDLE;
+
+/**
+ * Maximum pool name length including zero terminator
+ */
+#define WINTUN_MAX_POOL 256
+
+/**
+ * Creates a new Wintun adapter.
+ *
+ * @param Pool Name of the adapter pool. Zero-terminated string of up to WINTUN_MAX_POOL-1 characters.
+ *
+ * @param Name The requested name of the adapter. Zero-terminated string of up to MAX_ADAPTER_NAME-1
+ * characters.
+ *
+ * @param RequestedGUID The GUID of the created network adapter, which then influences NLA generation deterministically.
+ * If it is set to NULL, the GUID is chosen by the system at random, and hence a new NLA entry is
+ * created for each new adapter. It is called "requested" GUID because the API it uses is
+ * completely undocumented, and so there could be minor interesting complications with its usage.
+ *
+ * @param RebootRequired Optional pointer to a boolean flag to be set to TRUE in case SetupAPI suggests a reboot.
+ *
+ * @return If the function succeeds, the return value is the adapter handle. Must be released with WintunFreeAdapter. If
+ * the function fails, the return value is NULL. To get extended error information, call GetLastError.
+ */
+typedef _Return_type_success_(return != NULL) WINTUN_ADAPTER_HANDLE(WINAPI *WINTUN_CREATE_ADAPTER_FUNC)(
+ _In_z_ const WCHAR *Pool,
+ _In_z_ const WCHAR *Name,
+ _In_opt_ const GUID *RequestedGUID,
+ _Out_opt_ BOOL *RebootRequired);
+
+/**
+ * Opens an existing Wintun adapter.
+ *
+ * @param Pool Name of the adapter pool. Zero-terminated string of up to WINTUN_MAX_POOL-1 characters.
+ *
+ * @param Name Adapter name. Zero-terminated string of up to MAX_ADAPTER_NAME-1 characters.
+ *
+ * @return If the function succeeds, the return value is adapter handle. Must be released with WintunFreeAdapter. If the
+ * function fails, the return value is NULL. To get extended error information, call GetLastError. Possible
+ * errors include the following:
+ * ERROR_FILE_NOT_FOUND if adapter with given name is not found;
+ * ERROR_ALREADY_EXISTS if adapter is found but not a Wintun-class or not a member of the pool
+ */
+typedef _Return_type_success_(return != NULL)
+ WINTUN_ADAPTER_HANDLE(WINAPI *WINTUN_OPEN_ADAPTER_FUNC)(_In_z_ const WCHAR *Pool, _In_z_ const WCHAR *Name);
+
+
+/**
+ * Deletes a Wintun adapter.
+ *
+ * @param Adapter Adapter handle obtained with WintunOpenAdapter or WintunCreateAdapter.
+ *
+ * @param ForceCloseSessions Force close adapter handles that may be in use by other processes. Only set this to TRUE
+ * with extreme care, as this is resource intensive and may put processes into an undefined
+ * or unpredictable state. Most users should set this to FALSE.
+ *
+ * @param RebootRequired Optional pointer to a boolean flag to be set to TRUE in case SetupAPI suggests a reboot.
+ *
+ * @return If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To
+ * get extended error information, call GetLastError.
+ */
+typedef _Return_type_success_(return != FALSE) BOOL(WINAPI *WINTUN_DELETE_ADAPTER_FUNC)(
+ _In_ WINTUN_ADAPTER_HANDLE Adapter,
+ _In_ BOOL ForceCloseSessions,
+ _Out_opt_ BOOL *RebootRequired);
+
+/**
+ * Called by WintunEnumAdapters for each adapter in the pool.
+ *
+ * @param Adapter Adapter handle, which will be freed when this function returns.
+ *
+ * @param Param An application-defined value passed to the WintunEnumAdapters.
+ *
+ * @return Non-zero to continue iterating adapters; zero to stop.
+ */
+typedef BOOL(CALLBACK *WINTUN_ENUM_CALLBACK)(_In_ WINTUN_ADAPTER_HANDLE Adapter, _In_ LPARAM Param);
+
+/**
+ * Enumerates all Wintun adapters.
+ *
+ * @param Pool Name of the adapter pool. Zero-terminated string of up to WINTUN_MAX_POOL-1 characters.
+ *
+ * @param Callback Callback function. To continue enumeration, the callback function must return TRUE; to stop
+ * enumeration, it must return FALSE.
+ *
+ * @param Param An application-defined value to be passed to the callback function.
+ *
+ * @return If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To
+ * get extended error information, call GetLastError.
+ */
+typedef _Return_type_success_(return != FALSE) BOOL(
+ WINAPI *WINTUN_ENUM_ADAPTERS_FUNC)(_In_z_ const WCHAR *Pool, _In_ WINTUN_ENUM_CALLBACK Callback, _In_ LPARAM Param);
+
+/**
+ * Releases Wintun adapter resources.
+ *
+ * @param Adapter Adapter handle obtained with WintunOpenAdapter or WintunCreateAdapter.
+ */
+typedef void(WINAPI *WINTUN_FREE_ADAPTER_FUNC)(_In_ WINTUN_ADAPTER_HANDLE Adapter);
+
+/**
+ * Deletes all Wintun adapters in a pool and if there are no more adapters in any other pools, also removes Wintun
+ * from the driver store, usually called by uninstallers.
+ *
+ * @param Pool Name of the adapter pool. Zero-terminated string of up to WINTUN_MAX_POOL-1 characters.
+ *
+ * @param RebootRequired Optional pointer to a boolean flag to be set to TRUE in case SetupAPI suggests a reboot.
+ *
+ * @return If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To
+ * get extended error information, call GetLastError.
+ */
+typedef _Return_type_success_(return != FALSE)
+ BOOL(WINAPI *WINTUN_DELETE_POOL_DRIVER_FUNC)(_In_z_ const WCHAR *Pool, _Out_opt_ BOOL *RebootRequired);
+
+/**
+ * Returns the LUID of the adapter.
+ *
+ * @param Adapter Adapter handle obtained with WintunOpenAdapter or WintunCreateAdapter
+ *
+ * @param Luid Pointer to LUID to receive adapter LUID.
+ */
+typedef void(WINAPI *WINTUN_GET_ADAPTER_LUID_FUNC)(_In_ WINTUN_ADAPTER_HANDLE Adapter, _Out_ NET_LUID *Luid);
+
+/**
+ * Returns the name of the Wintun adapter.
+ *
+ * @param Adapter Adapter handle obtained with WintunOpenAdapter or WintunCreateAdapter
+ *
+ * @param Name Pointer to a string to receive adapter name
+ *
+ * @return If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To
+ * get extended error information, call GetLastError.
+ */
+typedef _Return_type_success_(return != FALSE) BOOL(WINAPI *WINTUN_GET_ADAPTER_NAME_FUNC)(
+ _In_ WINTUN_ADAPTER_HANDLE Adapter,
+ _Out_cap_c_(MAX_ADAPTER_NAME) WCHAR *Name);
+
+/**
+ * Sets name of the Wintun adapter.
+ *
+ * @param Adapter Adapter handle obtained with WintunOpenAdapter or WintunCreateAdapter
+ *
+ * @param Name Adapter name. Zero-terminated string of up to MAX_ADAPTER_NAME-1 characters.
+ *
+ * @return If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To
+ * get extended error information, call GetLastError.
+ */
+typedef _Return_type_success_(return != FALSE)
+ BOOL(WINAPI *WINTUN_SET_ADAPTER_NAME_FUNC)(_In_ WINTUN_ADAPTER_HANDLE Adapter, _In_z_ const WCHAR *Name);
+
+/**
+ * Determines the version of the Wintun driver currently loaded.
+ *
+ * @return If the function succeeds, the return value is the version number. If the function fails, the return value is
+ * zero. To get extended error information, call GetLastError. Possible errors include the following:
+ * ERROR_FILE_NOT_FOUND Wintun not loaded
+ */
+typedef DWORD(WINAPI *WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC)(void);
+
+/**
+ * Determines the level of logging, passed to WINTUN_LOGGER_CALLBACK.
+ */
+typedef enum
+{
+ WINTUN_LOG_INFO, /**< Informational */
+ WINTUN_LOG_WARN, /**< Warning */
+ WINTUN_LOG_ERR /**< Error */
+} WINTUN_LOGGER_LEVEL;
+
+/**
+ * Called by internal logger to report diagnostic messages
+ *
+ * @param Level Message level.
+ *
+ * @param Message Message text.
+ */
+typedef void(CALLBACK *WINTUN_LOGGER_CALLBACK)(_In_ WINTUN_LOGGER_LEVEL Level, _In_z_ const WCHAR *Message);
+
+/**
+ * Sets logger callback function.
+ *
+ * @param NewLogger Pointer to callback function to use as a new global logger. NewLogger may be called from various
+ * threads concurrently. Should the logging require serialization, you must handle serialization in
+ * NewLogger. Set to NULL to disable.
+ */
+typedef void(WINAPI *WINTUN_SET_LOGGER_FUNC)(_In_ WINTUN_LOGGER_CALLBACK NewLogger);
+
+/**
+ * Minimum ring capacity.
+ */
+#define WINTUN_MIN_RING_CAPACITY 0x20000 /* 128kiB */
+
+/**
+ * Maximum ring capacity.
+ */
+#define WINTUN_MAX_RING_CAPACITY 0x4000000 /* 64MiB */
+
+/**
+ * A handle representing Wintun session
+ */
+typedef void *WINTUN_SESSION_HANDLE;
+
+/**
+ * Starts Wintun session.
+ *
+ * @param Adapter Adapter handle obtained with WintunOpenAdapter or WintunCreateAdapter
+ *
+ * @param Capacity Rings capacity. Must be between WINTUN_MIN_RING_CAPACITY and WINTUN_MAX_RING_CAPACITY (incl.)
+ * Must be a power of two.
+ *
+ * @return Wintun session handle. Must be released with WintunEndSession. If the function fails, the return value is
+ * NULL. To get extended error information, call GetLastError.
+ */
+typedef _Return_type_success_(return != NULL)
+ WINTUN_SESSION_HANDLE(WINAPI *WINTUN_START_SESSION_FUNC)(_In_ WINTUN_ADAPTER_HANDLE Adapter, _In_ DWORD Capacity);
+
+/**
+ * Ends Wintun session.
+ *
+ * @param Session Wintun session handle obtained with WintunStartSession
+ */
+typedef void(WINAPI *WINTUN_END_SESSION_FUNC)(_In_ WINTUN_SESSION_HANDLE Session);
+
+/**
+ * Gets Wintun session's read-wait event handle.
+ *
+ * @param Session Wintun session handle obtained with WintunStartSession
+ *
+ * @return Pointer to receive event handle to wait for available data when reading. Should
+ * WintunReceivePackets return ERROR_NO_MORE_ITEMS (after spinning on it for a while under heavy
+ * load), wait for this event to become signaled before retrying WintunReceivePackets. Do not call
+ * CloseHandle on this event - it is managed by the session.
+ */
+typedef HANDLE(WINAPI *WINTUN_GET_READ_WAIT_EVENT_FUNC)(_In_ WINTUN_SESSION_HANDLE Session);
+
+/**
+ * Maximum IP packet size
+ */
+#define WINTUN_MAX_IP_PACKET_SIZE 0xFFFF
+
+/**
+ * Retrieves one or packet. After the packet content is consumed, call WintunReleaseReceivePacket with Packet returned
+ * from this function to release internal buffer. This function is thread-safe.
+ *
+ * @param Session Wintun session handle obtained with WintunStartSession
+ *
+ * @param PacketSize Pointer to receive packet size.
+ *
+ * @return Pointer to layer 3 IPv4 or IPv6 packet. Client may modify its content at will. If the function fails, the
+ * return value is NULL. To get extended error information, call GetLastError. Possible errors include the
+ * following:
+ * ERROR_HANDLE_EOF Wintun adapter is terminating;
+ * ERROR_NO_MORE_ITEMS Wintun buffer is exhausted;
+ * ERROR_INVALID_DATA Wintun buffer is corrupt
+ */
+typedef _Return_type_success_(return != NULL) _Ret_bytecount_(*PacketSize) BYTE *(
+ WINAPI *WINTUN_RECEIVE_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _Out_ DWORD *PacketSize);
+
+/**
+ * Releases internal buffer after the received packet has been processed by the client. This function is thread-safe.
+ *
+ * @param Session Wintun session handle obtained with WintunStartSession
+ *
+ * @param Packet Packet obtained with WintunReceivePacket
+ */
+typedef void(WINAPI *WINTUN_RELEASE_RECEIVE_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ const BYTE *Packet);
+
+/**
+ * Allocates memory for a packet to send. After the memory is filled with packet data, call WintunSendPacket to send
+ * and release internal buffer. WintunAllocateSendPacket is thread-safe and the WintunAllocateSendPacket order of
+ * calls define the packet sending order.
+ *
+ * @param Session Wintun session handle obtained with WintunStartSession
+ *
+ * @param PacketSize Exact packet size. Must be less or equal to WINTUN_MAX_IP_PACKET_SIZE.
+ *
+ * @return Returns pointer to memory where to prepare layer 3 IPv4 or IPv6 packet for sending. If the function fails,
+ * the return value is NULL. To get extended error information, call GetLastError. Possible errors include the
+ * following:
+ * ERROR_HANDLE_EOF Wintun adapter is terminating;
+ * ERROR_BUFFER_OVERFLOW Wintun buffer is full;
+ */
+typedef _Return_type_success_(return != NULL) _Ret_bytecount_(PacketSize) BYTE *(
+ WINAPI *WINTUN_ALLOCATE_SEND_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ DWORD PacketSize);
+
+/**
+ * Sends the packet and releases internal buffer. WintunSendPacket is thread-safe, but the WintunAllocateSendPacket
+ * order of calls define the packet sending order. This means the packet is not guaranteed to be sent in the
+ * WintunSendPacket yet.
+ *
+ * @param Session Wintun session handle obtained with WintunStartSession
+ *
+ * @param Packet Packet obtained with WintunAllocateSendPacket
+ */
+typedef void(WINAPI *WINTUN_SEND_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ const BYTE *Packet);
+
+#ifdef __cplusplus
+}
+#endif
<li>Restore compatibility with newer Cisco servers, by no longer sending them the <tt>X-AnyConnect-Platform</tt> header (<a href="https://gitlab.com/openconnect/openconnect/-/issues/101">#101</a>, <a href="https://gitlab.com/openconnect/openconnect/-/merge_requests/175">!175</a>)</li>
<li>Add support for PPP-based protocols, currently over TLS only (<a href="https://gitlab.com/openconnect/openconnect/-/merge_requests/165">!165</a>).</li>
<li>Add support for two PPP-based protocols, F5 with <tt>--protocol=f5</tt> and Fortinet with <tt>--protocol=fortinet</tt> (<a href="https://gitlab.com/openconnect/openconnect/-/merge_requests/169">!169</a>).</li>
+ <li>Add support for <a href="https://www.wintun.net/">Wintun</a> Layer 3 TUN driver under Windows.</li>
</ul><br/>
</li>
<li><b><a href="ftp://ftp.infradead.org/pub/openconnect/openconnect-8.10.tar.gz">OpenConnect v8.10</a></b>