Passes packets. Configuration isn't working properly.
There's lots of memcpy here as Wintun *really* wants us to use its pool.
More thought required for that one, as it can't even handle stripping
packet headers so even if we decrypt directly into its buffer that
wouldn't be enough.
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
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) {
vpninfo->tun_idx = idx;
+ vpn_progress(vpninfo, PRG_ERR, "Tun idx %d for %s\n", vpninfo->tun_idx, guid);
return 0;
} else {
char *errstr = openconnect__win32_strerror(status);
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;
+ }
+ vpn_progress(vpninfo, PRG_ERR, "Got %S type %d\n", wname, adapter_type);
+ 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;
+ 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"));
+ 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 <iphlpapi.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;
+ vpn_progress(vpninfo, PRG_INFO, "Opening %S\n", wname);
+ if (init_wintun(vpninfo))
+ return 0;
+
+ if (!vpninfo->wintun_adapter) {
+ vpninfo->wintun_adapter = WintunOpenAdapter(WINTUN_POOL_NAME,
+ vpninfo->ifname_w);
+ if (!vpninfo->wintun_adapter) {
+ char *errstr = openconnect__win32_strerror(GetLastError());
+ vpn_progress(vpninfo, PRG_ERR, "Could not open Wintun adapter '%S': %s\n",
+ vpninfo->ifname_w, errstr);
+ free(errstr);
+
+ ret = OPEN_TUN_SOFTFAIL;
+ 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;
+ }
+
+ vpn_progress(vpninfo, PRG_DEBUG, "Got adapter %p sess %p\n",
+ vpninfo->wintun_adapter,
+ vpninfo->wintun_session);
+
+ 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) {
+ WintunFreeAdapter(vpninfo->wintun_adapter);
+ 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 = 1;
+ 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