tun-win32.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688
  1. /*
  2. * OpenConnect (SSL + DTLS) VPN client
  3. *
  4. * Copyright © 2008-2015 Intel Corporation.
  5. *
  6. * Author: David Woodhouse <dwmw2@infradead.org>
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU Lesser General Public License
  10. * version 2.1, as published by the Free Software Foundation.
  11. *
  12. * This program is distributed in the hope that it will be useful, but
  13. * WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. */
  17. #include <config.h>
  18. #include "openconnect-internal.h"
  19. #define WIN32_LEAN_AND_MEAN
  20. #include <windows.h>
  21. #include <winioctl.h>
  22. /* must precede iphlpapi.h, per https://docs.microsoft.com/en-us/windows/win32/api/iptypes/ns-iptypes-ip_adapter_addresses_lh */
  23. #include <winsock2.h>
  24. #include <ws2ipdef.h>
  25. #include <iphlpapi.h>
  26. #include <netioapi.h>
  27. #include <errno.h>
  28. #include <stdio.h>
  29. /*
  30. * TAP-Windows support inspired by http://i3.cs.berkeley.edu/ (v0.2) with
  31. * permission.
  32. */
  33. #define _TAP_IOCTL(nr) CTL_CODE(FILE_DEVICE_UNKNOWN, nr, METHOD_BUFFERED, \
  34. FILE_ANY_ACCESS)
  35. #define TAP_IOCTL_GET_MAC _TAP_IOCTL(1)
  36. #define TAP_IOCTL_GET_VERSION _TAP_IOCTL(2)
  37. #define TAP_IOCTL_GET_MTU _TAP_IOCTL(3)
  38. #define TAP_IOCTL_GET_INFO _TAP_IOCTL(4)
  39. #define TAP_IOCTL_CONFIG_POINT_TO_POINT _TAP_IOCTL(5)
  40. #define TAP_IOCTL_SET_MEDIA_STATUS _TAP_IOCTL(6)
  41. #define TAP_IOCTL_CONFIG_DHCP_MASQ _TAP_IOCTL(7)
  42. #define TAP_IOCTL_GET_LOG_LINE _TAP_IOCTL(8)
  43. #define TAP_IOCTL_CONFIG_DHCP_SET_OPT _TAP_IOCTL(9)
  44. #define TAP_IOCTL_CONFIG_TUN _TAP_IOCTL(10)
  45. #define TAP_COMPONENT_ID "tap0901"
  46. #define DEVTEMPLATE "\\\\.\\Global\\%s.tap"
  47. #define NETDEV_GUID "{4D36E972-E325-11CE-BFC1-08002BE10318}"
  48. #define CONTROL_KEY "SYSTEM\\CurrentControlSet\\Control\\"
  49. #define ADAPTERS_KEY CONTROL_KEY "Class\\" NETDEV_GUID
  50. #define CONNECTIONS_KEY CONTROL_KEY "Network\\" NETDEV_GUID
  51. #define ADAPTER_TUNTAP 0
  52. #define ADAPTER_WINTUN 1
  53. typedef intptr_t (tap_callback)(struct openconnect_info *vpninfo, int type, char *idx, wchar_t *name);
  54. #define SEARCH_CONTINUE 0
  55. #define SEARCH_DONE 1
  56. static intptr_t search_taps(struct openconnect_info *vpninfo, tap_callback *cb)
  57. {
  58. LONG status;
  59. HKEY adapters_key, hkey;
  60. DWORD len, type;
  61. int adapter_type;
  62. char buf[40];
  63. wchar_t name[40];
  64. char keyname[strlen(CONNECTIONS_KEY) + sizeof(buf) + 1 + strlen("\\Connection")];
  65. int i = 0;
  66. intptr_t ret = OPEN_TUN_SOFTFAIL;
  67. status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, ADAPTERS_KEY, 0,
  68. KEY_READ, &adapters_key);
  69. if (status) {
  70. vpn_progress(vpninfo, PRG_ERR,
  71. _("Error accessing registry key for network adapters\n"));
  72. return -EIO;
  73. }
  74. while (ret == OPEN_TUN_SOFTFAIL) {
  75. len = sizeof(buf);
  76. status = RegEnumKeyExA(adapters_key, i++, buf, &len,
  77. NULL, NULL, NULL, NULL);
  78. if (status) {
  79. if (status != ERROR_NO_MORE_ITEMS)
  80. ret = OPEN_TUN_HARDFAIL;
  81. break;
  82. }
  83. snprintf(keyname, sizeof(keyname), "%s\\%s",
  84. ADAPTERS_KEY, buf);
  85. status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, keyname, 0,
  86. KEY_QUERY_VALUE, &hkey);
  87. if (status)
  88. continue;
  89. len = sizeof(buf);
  90. status = RegQueryValueExA(hkey, "ComponentId", NULL, &type,
  91. (unsigned char *)buf, &len);
  92. if (status || type != REG_SZ) {
  93. vpn_progress(vpninfo, PRG_TRACE,
  94. _("Cannot read %s\\%s or is not string\n"),
  95. keyname, "ComponentId");
  96. RegCloseKey(hkey);
  97. continue;
  98. }
  99. if (!strcmp(buf, TAP_COMPONENT_ID) || !strcmp(buf, "root\\" TAP_COMPONENT_ID))
  100. adapter_type = ADAPTER_TUNTAP;
  101. else if (!strcmp(buf, "wintun"))
  102. adapter_type = ADAPTER_WINTUN;
  103. else {
  104. vpn_progress(vpninfo, PRG_TRACE, _("%s\\ComponentId is unknown '%s'\n"),
  105. keyname, buf);
  106. RegCloseKey(hkey);
  107. continue;
  108. }
  109. vpn_progress(vpninfo, PRG_TRACE, _("Found %s at %s\n"),
  110. buf, keyname);
  111. len = sizeof(buf);
  112. status = RegQueryValueExA(hkey, "NetCfgInstanceId", NULL,
  113. &type, (unsigned char *)buf, &len);
  114. RegCloseKey(hkey);
  115. if (status || type != REG_SZ)
  116. continue;
  117. snprintf(keyname, sizeof(keyname), "%s\\%s\\Connection",
  118. CONNECTIONS_KEY, buf);
  119. status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, keyname, 0,
  120. KEY_QUERY_VALUE, &hkey);
  121. if (status) {
  122. vpn_progress(vpninfo, PRG_DEBUG,
  123. _("Cannot open registry key %s\n"),
  124. keyname);
  125. continue;
  126. }
  127. len = sizeof(name);
  128. status = RegQueryValueExW(hkey, L"Name", NULL, &type,
  129. (unsigned char *)name, &len);
  130. RegCloseKey(hkey);
  131. if (status || type != REG_SZ) {
  132. vpn_progress(vpninfo, PRG_INFO,
  133. _("Cannot read registry key %s\\%s or is not string\n"),
  134. keyname, "Name");
  135. continue;
  136. }
  137. ret = cb(vpninfo, adapter_type, buf, name);
  138. }
  139. RegCloseKey(adapters_key);
  140. return ret;
  141. }
  142. #ifndef __LIST_TAPS__
  143. static int get_adapter_index(struct openconnect_info *vpninfo, char *guid)
  144. {
  145. struct oc_text_buf *buf = buf_alloc();
  146. IP_ADAPTER_INFO *adapter;
  147. void *adapters_buf;
  148. ULONG idx;
  149. DWORD status;
  150. int ret = -EINVAL;
  151. vpninfo->tun_idx = -1;
  152. buf_append_utf16le(buf, "\\device\\tcpip_");
  153. buf_append_utf16le(buf, guid);
  154. if (buf_error(buf)) {
  155. /* If we didn't manage to malloc for this, we're never
  156. * going to manage for GetAdaptersInfo(). Give up. */
  157. return buf_free(buf);
  158. }
  159. status = GetAdapterIndex((void *)buf->data, &idx);
  160. buf_free(buf);
  161. if (status == NO_ERROR) {
  162. vpninfo->tun_idx = idx;
  163. return 0;
  164. } else {
  165. char *errstr = openconnect__win32_strerror(status);
  166. vpn_progress(vpninfo, PRG_INFO,
  167. _("GetAdapterIndex() failed: %s\nFalling back to GetAdaptersInfo()\n"),
  168. errstr);
  169. free(errstr);
  170. }
  171. idx = 0;
  172. status = GetAdaptersInfo(NULL, &idx);
  173. if (status != ERROR_BUFFER_OVERFLOW)
  174. return -EIO;
  175. adapters_buf = malloc(idx);
  176. if (!adapters_buf)
  177. return -ENOMEM;
  178. status = GetAdaptersInfo(adapters_buf, &idx);
  179. if (status != NO_ERROR) {
  180. char *errstr = openconnect__win32_strerror(status);
  181. vpn_progress(vpninfo, PRG_ERR, _("GetAdaptersInfo() failed: %s\n"), errstr);
  182. free(errstr);
  183. free(adapters_buf);
  184. return -EIO;
  185. }
  186. for (adapter = adapters_buf; adapter; adapter = adapter->Next) {
  187. if (!strcmp(adapter->AdapterName, guid)) {
  188. vpninfo->tun_idx = adapter->Index;
  189. ret = 0;
  190. break;
  191. }
  192. }
  193. free(adapters_buf);
  194. return ret;
  195. }
  196. static int check_address_conflicts(struct openconnect_info *vpninfo)
  197. {
  198. ULONG bufSize = 15000;
  199. PIP_ADAPTER_ADDRESSES addresses = NULL;
  200. DWORD LastError;
  201. int ret = 0;
  202. struct in_addr our_ipv4_addr;
  203. struct in6_addr our_ipv6_addr;
  204. if (vpninfo->ip_info.addr && !inet_aton(vpninfo->ip_info.addr, &our_ipv4_addr))
  205. return -EINVAL;
  206. if (vpninfo->ip_info.addr6 && !inet_pton(AF_INET6, vpninfo->ip_info.addr6, &our_ipv6_addr))
  207. return -EINVAL;
  208. /* XX: repeat twice, because it may tell us we need a bigger buffer */
  209. for (int tries=0; tries<2; tries++) {
  210. free(addresses);
  211. addresses = malloc(bufSize);
  212. if (!addresses)
  213. return -ENOMEM;
  214. /* AF_UNSPEC means both Legacy IP and IPv6 */
  215. LastError = GetAdaptersAddresses(AF_UNSPEC,
  216. GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST,
  217. NULL, addresses, &bufSize);
  218. if (LastError != ERROR_BUFFER_OVERFLOW)
  219. break;
  220. }
  221. if (LastError != ERROR_SUCCESS) {
  222. char *errstr = openconnect__win32_strerror(LastError);
  223. vpn_progress(vpninfo, PRG_ERR, _("GetAdaptersAddresses() failed: %s\n"), errstr);
  224. ret = -EIO;
  225. goto out;
  226. }
  227. for (; addresses; addresses=addresses->Next) {
  228. /* XX: skip "our own" adapter */
  229. if (addresses->IfIndex == vpninfo->tun_idx)
  230. continue;
  231. for (PIP_ADAPTER_UNICAST_ADDRESS ua = addresses->FirstUnicastAddress;
  232. ua; ua=ua->Next) {
  233. struct sockaddr *a = ua->Address.lpSockaddr;
  234. union {
  235. struct sockaddr_in s4;
  236. struct sockaddr_in6 s6;
  237. } *sa = (void *)a;
  238. int needs_reclaim = 0;
  239. if (a->sa_family == AF_INET) {
  240. if (vpninfo->ip_info.addr && our_ipv4_addr.s_addr == sa->s4.sin_addr.s_addr)
  241. needs_reclaim = 1;
  242. } else if (a->sa_family == AF_INET6) {
  243. if (vpninfo->ip_info.addr6 && !memcmp(&our_ipv6_addr, &sa->s6.sin6_addr, sizeof(sa->s6.sin6_addr)))
  244. needs_reclaim = 1;
  245. }
  246. if (needs_reclaim) {
  247. if (addresses->OperStatus == IfOperStatusUp) {
  248. /* XX: Interface is up. We shouldn't steal its address */
  249. vpn_progress(vpninfo, PRG_ERR,
  250. _("Adapter \"%S\" / %ld is UP and using our IPv%d address. Cannot resolve.\n"),
  251. addresses->FriendlyName, addresses->IfIndex, a->sa_family == AF_INET ? 4 : 6);
  252. ret = -ENOENT;
  253. goto out;
  254. } else {
  255. /* XX: Interface is down */
  256. vpn_progress(vpninfo, PRG_ERR,
  257. _("Adapter \"%S\" / %ld is DOWN and using our IPv%d address. We will reclaim the address from it.\n"),
  258. addresses->FriendlyName, addresses->IfIndex, a->sa_family == AF_INET ? 4 : 6);
  259. /* XX: In order to use DeleteUnicastIpAddressEntry(), we bizarrely have to iterate through a
  260. * whole different table of IP addresses to find the right row. I previously tried
  261. * faking/synthesizing the requisite MIB_UNICASTIPADDRESS_TABLE rows, and it did not
  262. * work.
  263. */
  264. int found = 0;
  265. PMIB_UNICASTIPADDRESS_TABLE pipTable = NULL;
  266. LastError = GetUnicastIpAddressTable(AF_UNSPEC, &pipTable);
  267. if (LastError != ERROR_SUCCESS) {
  268. char *errstr = openconnect__win32_strerror(LastError);
  269. vpn_progress(vpninfo, PRG_ERR, _("GetUnicastIpAddressTable() failed: %s\n"), errstr);
  270. ret = -EIO;
  271. goto out;
  272. }
  273. for (int i = 0; i < pipTable->NumEntries; i++) {
  274. if (pipTable->Table[i].Address.si_family == AF_INET && a->sa_family == AF_INET) {
  275. if (sa->s4.sin_addr.s_addr == pipTable->Table[i].Address.Ipv4.sin_addr.s_addr)
  276. found = 1;
  277. } else if (pipTable->Table[i].Address.si_family == AF_INET6 && a->sa_family == AF_INET6) {
  278. if (!memcmp(&sa->s6.sin6_addr, &pipTable->Table[i].Address.Ipv6, sizeof(sa->s6.sin6_addr)))
  279. found = 1;
  280. }
  281. if (found) {
  282. LastError = DeleteUnicastIpAddressEntry(&pipTable->Table[i]);
  283. break;
  284. }
  285. }
  286. FreeMibTable(pipTable);
  287. if (LastError != NO_ERROR) {
  288. char *errstr = openconnect__win32_strerror(LastError);
  289. vpn_progress(vpninfo, PRG_ERR, _("DeleteUnicastIpAddressEntry() failed: %s\n"), errstr);
  290. ret = -EIO;
  291. goto out;
  292. }
  293. if (!found) {
  294. vpn_progress(vpninfo, PRG_ERR,
  295. _("GetUnicastIpAddressTable() did not find matching address to reclaim\n"));
  296. /* ret = -EIO;
  297. goto out; */
  298. }
  299. }
  300. }
  301. }
  302. }
  303. out:
  304. free(addresses);
  305. return ret;
  306. }
  307. static intptr_t open_tuntap(struct openconnect_info *vpninfo, char *guid, wchar_t *wname)
  308. {
  309. char devname[80];
  310. HANDLE tun_fh;
  311. ULONG data[3];
  312. DWORD len;
  313. snprintf(devname, sizeof(devname), DEVTEMPLATE, guid);
  314. tun_fh = CreateFileA(devname, GENERIC_WRITE|GENERIC_READ, 0, 0,
  315. OPEN_EXISTING,
  316. FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
  317. 0);
  318. if (tun_fh == INVALID_HANDLE_VALUE) {
  319. vpn_progress(vpninfo, PRG_ERR, _("Failed to open %s\n"),
  320. devname);
  321. return OPEN_TUN_SOFTFAIL;
  322. }
  323. vpn_progress(vpninfo, PRG_DEBUG, _("Opened tun device %S\n"), wname);
  324. if (!DeviceIoControl(tun_fh, TAP_IOCTL_GET_VERSION,
  325. data, sizeof(data), data, sizeof(data),
  326. &len, NULL)) {
  327. char *errstr = openconnect__win32_strerror(GetLastError());
  328. vpn_progress(vpninfo, PRG_ERR,
  329. _("Failed to obtain TAP driver version: %s\n"), errstr);
  330. free(errstr);
  331. return OPEN_TUN_HARDFAIL;
  332. }
  333. if (data[0] < 9 || (data[0] == 9 && data[1] < 9)) {
  334. vpn_progress(vpninfo, PRG_ERR,
  335. _("Error: TAP-Windows driver v9.9 or greater is required (found %ld.%ld)\n"),
  336. data[0], data[1]);
  337. return -1;
  338. }
  339. vpn_progress(vpninfo, PRG_DEBUG, "TAP-Windows driver v%ld.%ld (%ld)\n",
  340. data[0], data[1], data[2]);
  341. data[0] = inet_addr(vpninfo->ip_info.addr);
  342. /* Set network and mask both to 0.0.0.0. It's not about routing;
  343. * it just ensures that the TAP driver fakes ARP responses for
  344. * *everything* we throw at it, and we can just configure them
  345. * as on-link routes. */
  346. data[1] = 0;
  347. data[2] = 0;
  348. if (!DeviceIoControl(tun_fh, TAP_IOCTL_CONFIG_TUN,
  349. data, sizeof(data), data, sizeof(data),
  350. &len, NULL)) {
  351. char *errstr = openconnect__win32_strerror(GetLastError());
  352. vpn_progress(vpninfo, PRG_ERR,
  353. _("Failed to set TAP IP addresses: %s\n"), errstr);
  354. free(errstr);
  355. return OPEN_TUN_HARDFAIL;
  356. }
  357. data[0] = 1;
  358. if (!DeviceIoControl(tun_fh, TAP_IOCTL_SET_MEDIA_STATUS,
  359. data, sizeof(data[0]), data, sizeof(data[0]),
  360. &len, NULL)) {
  361. char *errstr = openconnect__win32_strerror(GetLastError());
  362. vpn_progress(vpninfo, PRG_ERR,
  363. _("Failed to set TAP media status: %s\n"), errstr);
  364. free(errstr);
  365. return OPEN_TUN_HARDFAIL;
  366. }
  367. return (intptr_t)tun_fh;
  368. }
  369. static intptr_t open_tun(struct openconnect_info *vpninfo, int adapter_type, char *guid, wchar_t *wname)
  370. {
  371. intptr_t ret = -1;
  372. if (vpninfo->ifname_w && wcscmp(wname, vpninfo->ifname_w)) {
  373. vpn_progress(vpninfo, PRG_DEBUG,
  374. _("Ignoring non-matching interface \"%S\"\n"),
  375. wname);
  376. return 0;
  377. }
  378. if (adapter_type == ADAPTER_TUNTAP)
  379. ret = open_tuntap(vpninfo, guid, wname);
  380. else
  381. ret = open_wintun(vpninfo, guid, wname);
  382. if (ret == OPEN_TUN_SOFTFAIL || ret == OPEN_TUN_HARDFAIL)
  383. return ret;
  384. /*
  385. * We have found the adapter and opened it successfully. Now set
  386. * vpninfo->ifname accordingly, if necessary, and find $TUNIDX
  387. * for the script to use to configure it.
  388. */
  389. if (!vpninfo->ifname) {
  390. struct oc_text_buf *namebuf = buf_alloc();
  391. buf_append_from_utf16le(namebuf, wname);
  392. if (buf_error(namebuf)) {
  393. vpn_progress(vpninfo, PRG_ERR, _("Could not convert interface name to UTF-8\n"));
  394. os_shutdown_tun(vpninfo);
  395. buf_free(namebuf);
  396. return OPEN_TUN_HARDFAIL;
  397. }
  398. vpninfo->ifname = namebuf->data;
  399. namebuf->data = NULL;
  400. buf_free(namebuf);
  401. }
  402. get_adapter_index(vpninfo, guid);
  403. vpn_progress(vpninfo, adapter_type ? PRG_ERR : PRG_INFO,
  404. _("Using %s device '%s', index %d\n"),
  405. adapter_type ? "Wintun" : "TAP-Windows",
  406. vpninfo->ifname, vpninfo->tun_idx);
  407. if (adapter_type == ADAPTER_WINTUN)
  408. vpn_progress(vpninfo, PRG_ERR,
  409. _("WARNING: Support for Wintun is experimental and may be unstable. If you\n"
  410. " encounter problems, install the TAP-Windows driver instead. See\n"
  411. " %s\n"),
  412. "https://www.infradead.org/openconnect/building.html");
  413. return ret;
  414. }
  415. static intptr_t create_ifname_w(struct openconnect_info *vpninfo,
  416. const char *ifname)
  417. {
  418. struct oc_text_buf *ifname_buf = buf_alloc();
  419. buf_append_utf16le(ifname_buf, ifname);
  420. if (buf_error(ifname_buf)) {
  421. vpn_progress(vpninfo, PRG_ERR, _("Could not construct interface name\n"));
  422. return buf_free(ifname_buf);
  423. }
  424. free(vpninfo->ifname_w);
  425. vpninfo->ifname_w = (wchar_t *)ifname_buf->data;
  426. ifname_buf->data = NULL;
  427. buf_free(ifname_buf);
  428. return 0;
  429. }
  430. intptr_t os_setup_tun(struct openconnect_info *vpninfo)
  431. {
  432. intptr_t ret;
  433. if (vpninfo->ifname) {
  434. ret = create_ifname_w(vpninfo, vpninfo->ifname);
  435. if (ret)
  436. return ret;
  437. }
  438. ret = search_taps(vpninfo, open_tun);
  439. if (ret == OPEN_TUN_SOFTFAIL) {
  440. if (!vpninfo->ifname_w) {
  441. ret = create_ifname_w(vpninfo, vpninfo->hostname);
  442. if (ret)
  443. return ret;
  444. }
  445. /* Try creating a Wintun instead of TAP */
  446. int retw = create_wintun(vpninfo);
  447. if (!retw) {
  448. ret = search_taps(vpninfo, open_tun);
  449. if (ret == OPEN_TUN_SOFTFAIL)
  450. ret = OPEN_TUN_HARDFAIL;
  451. if (ret == OPEN_TUN_HARDFAIL)
  452. os_shutdown_wintun(vpninfo);
  453. } else if (retw == -EPERM) {
  454. ret = OPEN_TUN_HARDFAIL;
  455. vpn_progress(vpninfo, PRG_ERR,
  456. _("Access denied creating Wintun adapter. Are you running with Administrator privileges?\n"));
  457. }
  458. }
  459. if (ret == OPEN_TUN_SOFTFAIL) {
  460. vpn_progress(vpninfo, PRG_ERR,
  461. _("Neither Windows-TAP nor Wintun adapters were found. Is the driver installed?\n"));
  462. ret = OPEN_TUN_HARDFAIL;
  463. }
  464. if (check_address_conflicts(vpninfo) < 0)
  465. ret = OPEN_TUN_HARDFAIL; /* already complained about it */
  466. return ret;
  467. }
  468. int os_read_tun(struct openconnect_info *vpninfo, struct pkt *pkt)
  469. {
  470. DWORD pkt_size;
  471. if (vpninfo->wintun)
  472. return os_read_wintun(vpninfo, pkt);
  473. reread:
  474. if (!vpninfo->tun_rd_pending &&
  475. !ReadFile(vpninfo->tun_fh, pkt->data, pkt->len, &pkt_size,
  476. &vpninfo->tun_rd_overlap)) {
  477. DWORD err = GetLastError();
  478. if (err == ERROR_IO_PENDING)
  479. vpninfo->tun_rd_pending = 1;
  480. else if (err == ERROR_OPERATION_ABORTED) {
  481. vpninfo->quit_reason = "TAP device aborted";
  482. vpn_progress(vpninfo, PRG_ERR,
  483. _("TAP device aborted connectivity. Disconnecting.\n"));
  484. return -1;
  485. } else {
  486. char *errstr = openconnect__win32_strerror(err);
  487. vpn_progress(vpninfo, PRG_ERR,
  488. _("Failed to read from TAP device: %s\n"),
  489. errstr);
  490. free(errstr);
  491. }
  492. return -1;
  493. } else if (!GetOverlappedResult(vpninfo->tun_fh,
  494. &vpninfo->tun_rd_overlap, &pkt_size,
  495. FALSE)) {
  496. DWORD err = GetLastError();
  497. if (err != ERROR_IO_INCOMPLETE) {
  498. char *errstr = openconnect__win32_strerror(err);
  499. vpninfo->tun_rd_pending = 0;
  500. vpn_progress(vpninfo, PRG_ERR,
  501. _("Failed to complete read from TAP device: %s\n"),
  502. errstr);
  503. free(errstr);
  504. goto reread;
  505. }
  506. return -1;
  507. }
  508. /* Either a straight ReadFile() or a subsequent GetOverlappedResult()
  509. succeeded... */
  510. vpninfo->tun_rd_pending = 0;
  511. pkt->len = pkt_size;
  512. return 0;
  513. }
  514. int os_write_tun(struct openconnect_info *vpninfo, struct pkt *pkt)
  515. {
  516. DWORD pkt_size = 0;
  517. DWORD err;
  518. char *errstr;
  519. if (vpninfo->wintun)
  520. return os_write_wintun(vpninfo, pkt);
  521. if (WriteFile(vpninfo->tun_fh, pkt->data, pkt->len, &pkt_size, &vpninfo->tun_wr_overlap)) {
  522. vpn_progress(vpninfo, PRG_TRACE,
  523. _("Wrote %ld bytes to tun\n"), pkt_size);
  524. return 0;
  525. }
  526. err = GetLastError();
  527. if (err == ERROR_IO_PENDING) {
  528. /* Theoretically we should let the mainloop handle this blocking,
  529. but that's non-trivial and it doesn't ever seem to happen in
  530. practice anyway. */
  531. vpn_progress(vpninfo, PRG_TRACE,
  532. _("Waiting for tun write...\n"));
  533. if (GetOverlappedResult(vpninfo->tun_fh, &vpninfo->tun_wr_overlap, &pkt_size, TRUE)) {
  534. vpn_progress(vpninfo, PRG_TRACE,
  535. _("Wrote %ld bytes to tun after waiting\n"), pkt_size);
  536. return 0;
  537. }
  538. err = GetLastError();
  539. }
  540. errstr = openconnect__win32_strerror(err);
  541. vpn_progress(vpninfo, PRG_ERR,
  542. _("Failed to write to TAP device: %s\n"), errstr);
  543. free(errstr);
  544. return -1;
  545. }
  546. void os_shutdown_tun(struct openconnect_info *vpninfo)
  547. {
  548. script_config_tun(vpninfo, "disconnect");
  549. if (vpninfo->wintun) {
  550. os_shutdown_wintun(vpninfo);
  551. return;
  552. }
  553. CloseHandle(vpninfo->tun_fh);
  554. vpninfo->tun_fh = NULL;
  555. CloseHandle(vpninfo->tun_rd_overlap.hEvent);
  556. vpninfo->tun_rd_overlap.hEvent = NULL;
  557. }
  558. int openconnect_setup_tun_fd(struct openconnect_info *vpninfo, intptr_t tun_fd)
  559. {
  560. ULONG data;
  561. DWORD len;
  562. if (vpninfo->wintun)
  563. return setup_wintun_fd(vpninfo, tun_fd);
  564. /* Toggle media status so that network location awareness picks up all the configuration
  565. that occurred and properly assigns the network so the user can adjust firewall
  566. settings. */
  567. for (data = 0; data <= 1; data++) {
  568. if (!DeviceIoControl((HANDLE)tun_fd, TAP_IOCTL_SET_MEDIA_STATUS,
  569. &data, sizeof(data), &data, sizeof(data), &len, NULL)) {
  570. char *errstr = openconnect__win32_strerror(GetLastError());
  571. vpn_progress(vpninfo, PRG_ERR,
  572. _("Failed to set TAP media status: %s\n"), errstr);
  573. free(errstr);
  574. return -1;
  575. }
  576. }
  577. vpninfo->tun_fh = (HANDLE)tun_fd;
  578. vpninfo->tun_rd_overlap.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  579. monitor_read_fd(vpninfo, tun);
  580. return 0;
  581. }
  582. int openconnect_setup_tun_script(struct openconnect_info *vpninfo,
  583. const char *tun_script)
  584. {
  585. vpn_progress(vpninfo, PRG_ERR,
  586. _("Spawning tunnel scripts is not yet supported on Windows\n"));
  587. return -1;
  588. }
  589. #endif /* __LIST_TAPS__ */