123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688 |
- /*
- * OpenConnect (SSL + DTLS) VPN client
- *
- * Copyright © 2008-2015 Intel Corporation.
- *
- * 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>
- #include "openconnect-internal.h"
- #define WIN32_LEAN_AND_MEAN
- #include <windows.h>
- #include <winioctl.h>
- /* must precede iphlpapi.h, per https://docs.microsoft.com/en-us/windows/win32/api/iptypes/ns-iptypes-ip_adapter_addresses_lh */
- #include <winsock2.h>
- #include <ws2ipdef.h>
- #include <iphlpapi.h>
- #include <netioapi.h>
- #include <errno.h>
- #include <stdio.h>
- /*
- * TAP-Windows support inspired by http://i3.cs.berkeley.edu/ (v0.2) with
- * permission.
- */
- #define _TAP_IOCTL(nr) CTL_CODE(FILE_DEVICE_UNKNOWN, nr, METHOD_BUFFERED, \
- FILE_ANY_ACCESS)
- #define TAP_IOCTL_GET_MAC _TAP_IOCTL(1)
- #define TAP_IOCTL_GET_VERSION _TAP_IOCTL(2)
- #define TAP_IOCTL_GET_MTU _TAP_IOCTL(3)
- #define TAP_IOCTL_GET_INFO _TAP_IOCTL(4)
- #define TAP_IOCTL_CONFIG_POINT_TO_POINT _TAP_IOCTL(5)
- #define TAP_IOCTL_SET_MEDIA_STATUS _TAP_IOCTL(6)
- #define TAP_IOCTL_CONFIG_DHCP_MASQ _TAP_IOCTL(7)
- #define TAP_IOCTL_GET_LOG_LINE _TAP_IOCTL(8)
- #define TAP_IOCTL_CONFIG_DHCP_SET_OPT _TAP_IOCTL(9)
- #define TAP_IOCTL_CONFIG_TUN _TAP_IOCTL(10)
- #define TAP_COMPONENT_ID "tap0901"
- #define DEVTEMPLATE "\\\\.\\Global\\%s.tap"
- #define NETDEV_GUID "{4D36E972-E325-11CE-BFC1-08002BE10318}"
- #define CONTROL_KEY "SYSTEM\\CurrentControlSet\\Control\\"
- #define ADAPTERS_KEY CONTROL_KEY "Class\\" NETDEV_GUID
- #define CONNECTIONS_KEY CONTROL_KEY "Network\\" NETDEV_GUID
- #define ADAPTER_TUNTAP 0
- #define ADAPTER_WINTUN 1
- 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;
- intptr_t ret = OPEN_TUN_SOFTFAIL;
- status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, ADAPTERS_KEY, 0,
- KEY_READ, &adapters_key);
- if (status) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Error accessing registry key for network adapters\n"));
- return -EIO;
- }
- 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 = OPEN_TUN_HARDFAIL;
- break;
- }
- snprintf(keyname, sizeof(keyname), "%s\\%s",
- ADAPTERS_KEY, buf);
- status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, keyname, 0,
- KEY_QUERY_VALUE, &hkey);
- if (status)
- continue;
- len = sizeof(buf);
- 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");
- RegCloseKey(hkey);
- continue;
- }
- 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;
- }
- vpn_progress(vpninfo, PRG_TRACE, _("Found %s at %s\n"),
- buf, keyname);
- len = sizeof(buf);
- status = RegQueryValueExA(hkey, "NetCfgInstanceId", NULL,
- &type, (unsigned char *)buf, &len);
- RegCloseKey(hkey);
- if (status || type != REG_SZ)
- continue;
- snprintf(keyname, sizeof(keyname), "%s\\%s\\Connection",
- CONNECTIONS_KEY, buf);
- status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, keyname, 0,
- KEY_QUERY_VALUE, &hkey);
- if (status) {
- vpn_progress(vpninfo, PRG_DEBUG,
- _("Cannot open registry key %s\n"),
- keyname);
- continue;
- }
- len = sizeof(name);
- status = RegQueryValueExW(hkey, L"Name", NULL, &type,
- (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");
- continue;
- }
- ret = cb(vpninfo, adapter_type, buf, name);
- }
- RegCloseKey(adapters_key);
- return ret;
- }
- #ifndef __LIST_TAPS__
- static int get_adapter_index(struct openconnect_info *vpninfo, char *guid)
- {
- struct oc_text_buf *buf = buf_alloc();
- IP_ADAPTER_INFO *adapter;
- void *adapters_buf;
- ULONG idx;
- DWORD status;
- int ret = -EINVAL;
- vpninfo->tun_idx = -1;
- buf_append_utf16le(buf, "\\device\\tcpip_");
- buf_append_utf16le(buf, guid);
- if (buf_error(buf)) {
- /* If we didn't manage to malloc for this, we're never
- * 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;
- return 0;
- } else {
- char *errstr = openconnect__win32_strerror(status);
- vpn_progress(vpninfo, PRG_INFO,
- _("GetAdapterIndex() failed: %s\nFalling back to GetAdaptersInfo()\n"),
- errstr);
- free(errstr);
- }
- idx = 0;
- status = GetAdaptersInfo(NULL, &idx);
- if (status != ERROR_BUFFER_OVERFLOW)
- return -EIO;
- adapters_buf = malloc(idx);
- if (!adapters_buf)
- return -ENOMEM;
- status = GetAdaptersInfo(adapters_buf, &idx);
- if (status != NO_ERROR) {
- char *errstr = openconnect__win32_strerror(status);
- vpn_progress(vpninfo, PRG_ERR, _("GetAdaptersInfo() failed: %s\n"), errstr);
- free(errstr);
- free(adapters_buf);
- return -EIO;
- }
- for (adapter = adapters_buf; adapter; adapter = adapter->Next) {
- if (!strcmp(adapter->AdapterName, guid)) {
- vpninfo->tun_idx = adapter->Index;
- ret = 0;
- break;
- }
- }
- free(adapters_buf);
- return ret;
- }
- static int check_address_conflicts(struct openconnect_info *vpninfo)
- {
- ULONG bufSize = 15000;
- PIP_ADAPTER_ADDRESSES addresses = NULL;
- DWORD LastError;
- int ret = 0;
- struct in_addr our_ipv4_addr;
- struct in6_addr our_ipv6_addr;
- if (vpninfo->ip_info.addr && !inet_aton(vpninfo->ip_info.addr, &our_ipv4_addr))
- return -EINVAL;
- if (vpninfo->ip_info.addr6 && !inet_pton(AF_INET6, vpninfo->ip_info.addr6, &our_ipv6_addr))
- return -EINVAL;
- /* XX: repeat twice, because it may tell us we need a bigger buffer */
- for (int tries=0; tries<2; tries++) {
- free(addresses);
- addresses = malloc(bufSize);
- if (!addresses)
- return -ENOMEM;
- /* AF_UNSPEC means both Legacy IP and IPv6 */
- LastError = GetAdaptersAddresses(AF_UNSPEC,
- GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST,
- NULL, addresses, &bufSize);
- if (LastError != ERROR_BUFFER_OVERFLOW)
- break;
- }
- if (LastError != ERROR_SUCCESS) {
- char *errstr = openconnect__win32_strerror(LastError);
- vpn_progress(vpninfo, PRG_ERR, _("GetAdaptersAddresses() failed: %s\n"), errstr);
- ret = -EIO;
- goto out;
- }
- for (; addresses; addresses=addresses->Next) {
- /* XX: skip "our own" adapter */
- if (addresses->IfIndex == vpninfo->tun_idx)
- continue;
- for (PIP_ADAPTER_UNICAST_ADDRESS ua = addresses->FirstUnicastAddress;
- ua; ua=ua->Next) {
- struct sockaddr *a = ua->Address.lpSockaddr;
- union {
- struct sockaddr_in s4;
- struct sockaddr_in6 s6;
- } *sa = (void *)a;
- int needs_reclaim = 0;
- if (a->sa_family == AF_INET) {
- if (vpninfo->ip_info.addr && our_ipv4_addr.s_addr == sa->s4.sin_addr.s_addr)
- needs_reclaim = 1;
- } else if (a->sa_family == AF_INET6) {
- if (vpninfo->ip_info.addr6 && !memcmp(&our_ipv6_addr, &sa->s6.sin6_addr, sizeof(sa->s6.sin6_addr)))
- needs_reclaim = 1;
- }
- if (needs_reclaim) {
- if (addresses->OperStatus == IfOperStatusUp) {
- /* XX: Interface is up. We shouldn't steal its address */
- vpn_progress(vpninfo, PRG_ERR,
- _("Adapter \"%S\" / %ld is UP and using our IPv%d address. Cannot resolve.\n"),
- addresses->FriendlyName, addresses->IfIndex, a->sa_family == AF_INET ? 4 : 6);
- ret = -ENOENT;
- goto out;
- } else {
- /* XX: Interface is down */
- vpn_progress(vpninfo, PRG_ERR,
- _("Adapter \"%S\" / %ld is DOWN and using our IPv%d address. We will reclaim the address from it.\n"),
- addresses->FriendlyName, addresses->IfIndex, a->sa_family == AF_INET ? 4 : 6);
- /* XX: In order to use DeleteUnicastIpAddressEntry(), we bizarrely have to iterate through a
- * whole different table of IP addresses to find the right row. I previously tried
- * faking/synthesizing the requisite MIB_UNICASTIPADDRESS_TABLE rows, and it did not
- * work.
- */
- int found = 0;
- PMIB_UNICASTIPADDRESS_TABLE pipTable = NULL;
- LastError = GetUnicastIpAddressTable(AF_UNSPEC, &pipTable);
- if (LastError != ERROR_SUCCESS) {
- char *errstr = openconnect__win32_strerror(LastError);
- vpn_progress(vpninfo, PRG_ERR, _("GetUnicastIpAddressTable() failed: %s\n"), errstr);
- ret = -EIO;
- goto out;
- }
- for (int i = 0; i < pipTable->NumEntries; i++) {
- if (pipTable->Table[i].Address.si_family == AF_INET && a->sa_family == AF_INET) {
- if (sa->s4.sin_addr.s_addr == pipTable->Table[i].Address.Ipv4.sin_addr.s_addr)
- found = 1;
- } else if (pipTable->Table[i].Address.si_family == AF_INET6 && a->sa_family == AF_INET6) {
- if (!memcmp(&sa->s6.sin6_addr, &pipTable->Table[i].Address.Ipv6, sizeof(sa->s6.sin6_addr)))
- found = 1;
- }
- if (found) {
- LastError = DeleteUnicastIpAddressEntry(&pipTable->Table[i]);
- break;
- }
- }
- FreeMibTable(pipTable);
- if (LastError != NO_ERROR) {
- char *errstr = openconnect__win32_strerror(LastError);
- vpn_progress(vpninfo, PRG_ERR, _("DeleteUnicastIpAddressEntry() failed: %s\n"), errstr);
- ret = -EIO;
- goto out;
- }
- if (!found) {
- vpn_progress(vpninfo, PRG_ERR,
- _("GetUnicastIpAddressTable() did not find matching address to reclaim\n"));
- /* ret = -EIO;
- goto out; */
- }
- }
- }
- }
- }
- out:
- free(addresses);
- return ret;
- }
- static intptr_t open_tuntap(struct openconnect_info *vpninfo, char *guid, wchar_t *wname)
- {
- char devname[80];
- HANDLE tun_fh;
- ULONG data[3];
- DWORD len;
- snprintf(devname, sizeof(devname), DEVTEMPLATE, guid);
- tun_fh = CreateFileA(devname, GENERIC_WRITE|GENERIC_READ, 0, 0,
- OPEN_EXISTING,
- 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;
- }
- 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),
- &len, NULL)) {
- char *errstr = openconnect__win32_strerror(GetLastError());
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to obtain TAP driver version: %s\n"), errstr);
- free(errstr);
- 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]);
- return -1;
- }
- vpn_progress(vpninfo, PRG_DEBUG, "TAP-Windows driver v%ld.%ld (%ld)\n",
- data[0], data[1], data[2]);
- data[0] = inet_addr(vpninfo->ip_info.addr);
- /* Set network and mask both to 0.0.0.0. It's not about routing;
- * it just ensures that the TAP driver fakes ARP responses for
- * *everything* we throw at it, and we can just configure them
- * as on-link routes. */
- data[1] = 0;
- data[2] = 0;
- if (!DeviceIoControl(tun_fh, TAP_IOCTL_CONFIG_TUN,
- data, sizeof(data), data, sizeof(data),
- &len, NULL)) {
- char *errstr = openconnect__win32_strerror(GetLastError());
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to set TAP IP addresses: %s\n"), errstr);
- free(errstr);
- return OPEN_TUN_HARDFAIL;
- }
- data[0] = 1;
- if (!DeviceIoControl(tun_fh, TAP_IOCTL_SET_MEDIA_STATUS,
- data, sizeof(data[0]), data, sizeof(data[0]),
- &len, NULL)) {
- char *errstr = openconnect__win32_strerror(GetLastError());
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to set TAP media status: %s\n"), errstr);
- free(errstr);
- 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);
- }
- get_adapter_index(vpninfo, guid);
- vpn_progress(vpninfo, adapter_type ? PRG_ERR : PRG_INFO,
- _("Using %s device '%s', index %d\n"),
- adapter_type ? "Wintun" : "TAP-Windows",
- vpninfo->ifname, vpninfo->tun_idx);
- if (adapter_type == ADAPTER_WINTUN)
- vpn_progress(vpninfo, PRG_ERR,
- _("WARNING: Support for Wintun is experimental and may be unstable. If you\n"
- " encounter problems, install the TAP-Windows driver instead. See\n"
- " %s\n"),
- "https://www.infradead.org/openconnect/building.html");
- return ret;
- }
- static intptr_t create_ifname_w(struct openconnect_info *vpninfo,
- const char *ifname)
- {
- struct oc_text_buf *ifname_buf = buf_alloc();
- buf_append_utf16le(ifname_buf, 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);
- return 0;
- }
- intptr_t os_setup_tun(struct openconnect_info *vpninfo)
- {
- intptr_t ret;
- if (vpninfo->ifname) {
- ret = create_ifname_w(vpninfo, vpninfo->ifname);
- if (ret)
- return ret;
- }
- ret = search_taps(vpninfo, open_tun);
- if (ret == OPEN_TUN_SOFTFAIL) {
- if (!vpninfo->ifname_w) {
- ret = create_ifname_w(vpninfo, vpninfo->hostname);
- if (ret)
- return ret;
- }
- /* Try creating a Wintun instead of TAP */
- int retw = create_wintun(vpninfo);
- if (!retw) {
- ret = search_taps(vpninfo, open_tun);
- if (ret == OPEN_TUN_SOFTFAIL)
- ret = OPEN_TUN_HARDFAIL;
- if (ret == OPEN_TUN_HARDFAIL)
- os_shutdown_wintun(vpninfo);
- } 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"));
- }
- }
- if (ret == OPEN_TUN_SOFTFAIL) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Neither Windows-TAP nor Wintun adapters were found. Is the driver installed?\n"));
- ret = OPEN_TUN_HARDFAIL;
- }
- if (check_address_conflicts(vpninfo) < 0)
- ret = OPEN_TUN_HARDFAIL; /* already complained about it */
- 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 &&
- !ReadFile(vpninfo->tun_fh, pkt->data, pkt->len, &pkt_size,
- &vpninfo->tun_rd_overlap)) {
- DWORD err = GetLastError();
- if (err == ERROR_IO_PENDING)
- vpninfo->tun_rd_pending = 1;
- else if (err == ERROR_OPERATION_ABORTED) {
- vpninfo->quit_reason = "TAP device aborted";
- vpn_progress(vpninfo, PRG_ERR,
- _("TAP device aborted connectivity. Disconnecting.\n"));
- return -1;
- } else {
- char *errstr = openconnect__win32_strerror(err);
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to read from TAP device: %s\n"),
- errstr);
- free(errstr);
- }
- return -1;
- } else if (!GetOverlappedResult(vpninfo->tun_fh,
- &vpninfo->tun_rd_overlap, &pkt_size,
- FALSE)) {
- DWORD err = GetLastError();
- if (err != ERROR_IO_INCOMPLETE) {
- char *errstr = openconnect__win32_strerror(err);
- vpninfo->tun_rd_pending = 0;
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to complete read from TAP device: %s\n"),
- errstr);
- free(errstr);
- goto reread;
- }
- return -1;
- }
- /* Either a straight ReadFile() or a subsequent GetOverlappedResult()
- succeeded... */
- vpninfo->tun_rd_pending = 0;
- pkt->len = pkt_size;
- return 0;
- }
- int os_write_tun(struct openconnect_info *vpninfo, struct pkt *pkt)
- {
- DWORD pkt_size = 0;
- 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);
- return 0;
- }
- err = GetLastError();
- if (err == ERROR_IO_PENDING) {
- /* Theoretically we should let the mainloop handle this blocking,
- but that's non-trivial and it doesn't ever seem to happen in
- practice anyway. */
- vpn_progress(vpninfo, PRG_TRACE,
- _("Waiting for tun write...\n"));
- if (GetOverlappedResult(vpninfo->tun_fh, &vpninfo->tun_wr_overlap, &pkt_size, TRUE)) {
- vpn_progress(vpninfo, PRG_TRACE,
- _("Wrote %ld bytes to tun after waiting\n"), pkt_size);
- return 0;
- }
- err = GetLastError();
- }
- errstr = openconnect__win32_strerror(err);
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to write to TAP device: %s\n"), errstr);
- free(errstr);
- return -1;
- }
- 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);
- vpninfo->tun_rd_overlap.hEvent = NULL;
- }
- int openconnect_setup_tun_fd(struct openconnect_info *vpninfo, intptr_t tun_fd)
- {
- 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. */
- for (data = 0; data <= 1; data++) {
- if (!DeviceIoControl((HANDLE)tun_fd, TAP_IOCTL_SET_MEDIA_STATUS,
- &data, sizeof(data), &data, sizeof(data), &len, NULL)) {
- char *errstr = openconnect__win32_strerror(GetLastError());
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to set TAP media status: %s\n"), errstr);
- free(errstr);
- return -1;
- }
- }
- vpninfo->tun_fh = (HANDLE)tun_fd;
- vpninfo->tun_rd_overlap.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
- monitor_read_fd(vpninfo, tun);
- return 0;
- }
- int openconnect_setup_tun_script(struct openconnect_info *vpninfo,
- const char *tun_script)
- {
- vpn_progress(vpninfo, PRG_ERR,
- _("Spawning tunnel scripts is not yet supported on Windows\n"));
- return -1;
- }
- #endif /* __LIST_TAPS__ */
|