wintun.c 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. /*
  2. * OpenConnect (SSL + DTLS) VPN client
  3. *
  4. * Copyright © 2021 David Woodhouse.
  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. #include <winsock2.h>
  23. #include <ws2ipdef.h>
  24. #include <iphlpapi.h>
  25. #include <netioapi.h>
  26. #include <errno.h>
  27. #include <stdio.h>
  28. static WINTUN_CREATE_ADAPTER_FUNC WintunCreateAdapter;
  29. static WINTUN_DELETE_ADAPTER_FUNC WintunDeleteAdapter;
  30. static WINTUN_DELETE_POOL_DRIVER_FUNC WintunDeletePoolDriver;
  31. static WINTUN_ENUM_ADAPTERS_FUNC WintunEnumAdapters;
  32. static WINTUN_FREE_ADAPTER_FUNC WintunFreeAdapter;
  33. static WINTUN_OPEN_ADAPTER_FUNC WintunOpenAdapter;
  34. static WINTUN_GET_ADAPTER_LUID_FUNC WintunGetAdapterLUID;
  35. static WINTUN_GET_ADAPTER_NAME_FUNC WintunGetAdapterName;
  36. static WINTUN_SET_ADAPTER_NAME_FUNC WintunSetAdapterName;
  37. static WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC WintunGetRunningDriverVersion;
  38. static WINTUN_SET_LOGGER_FUNC WintunSetLogger;
  39. static WINTUN_START_SESSION_FUNC WintunStartSession;
  40. static WINTUN_END_SESSION_FUNC WintunEndSession;
  41. static WINTUN_GET_READ_WAIT_EVENT_FUNC WintunGetReadWaitEvent;
  42. static WINTUN_RECEIVE_PACKET_FUNC WintunReceivePacket;
  43. static WINTUN_RELEASE_RECEIVE_PACKET_FUNC WintunReleaseReceivePacket;
  44. static WINTUN_ALLOCATE_SEND_PACKET_FUNC WintunAllocateSendPacket;
  45. static WINTUN_SEND_PACKET_FUNC WintunSendPacket;
  46. static struct openconnect_info *logger_vpninfo;
  47. #define WINTUN_POOL_NAME L"OpenConnect"
  48. #define WINTUN_RING_CAPACITY 0x400000 /* 4 MiB */
  49. static void CALLBACK wintun_log_fn(WINTUN_LOGGER_LEVEL wlvl, const WCHAR *wmsg)
  50. {
  51. int lvl = (wlvl == WINTUN_LOG_INFO) ? PRG_INFO : PRG_ERR;
  52. /* Sadly, Wintun doesn't provide any context information in the callback */
  53. if (!logger_vpninfo)
  54. return;
  55. vpn_progress(logger_vpninfo, lvl, "%d: %S\n", wlvl, wmsg);
  56. }
  57. static int init_wintun(struct openconnect_info *vpninfo)
  58. {
  59. if (!vpninfo->wintun) {
  60. vpninfo->wintun = LoadLibraryExW(L"wintun.dll", NULL,
  61. LOAD_LIBRARY_SEARCH_APPLICATION_DIR);
  62. if (!vpninfo->wintun) {
  63. vpn_progress(vpninfo, PRG_DEBUG, _("Could not load wintun.dll\n"));
  64. return -ENOENT;
  65. }
  66. #define Resolve(Name) ((Name = (void *)GetProcAddress(vpninfo->wintun, #Name)) == NULL)
  67. if (Resolve(WintunCreateAdapter) || Resolve(WintunDeleteAdapter) ||
  68. Resolve(WintunDeletePoolDriver) || Resolve(WintunEnumAdapters) ||
  69. Resolve(WintunFreeAdapter) || Resolve(WintunOpenAdapter) ||
  70. Resolve(WintunGetAdapterLUID) || Resolve(WintunGetAdapterName) ||
  71. Resolve(WintunSetAdapterName) || Resolve(WintunGetRunningDriverVersion) ||
  72. Resolve(WintunSetLogger) || Resolve(WintunStartSession) ||
  73. Resolve(WintunEndSession) || Resolve(WintunGetReadWaitEvent) ||
  74. Resolve(WintunReceivePacket) || Resolve(WintunReleaseReceivePacket) ||
  75. Resolve(WintunAllocateSendPacket) || Resolve(WintunSendPacket)) {
  76. #undef Resolve
  77. vpn_progress(vpninfo, PRG_ERR, _("Could not resolve functions from wintun.dll\n"));
  78. FreeLibrary(vpninfo->wintun);
  79. vpninfo->wintun = NULL;
  80. return -EIO;
  81. }
  82. logger_vpninfo = vpninfo;
  83. WintunSetLogger(wintun_log_fn);
  84. }
  85. return 0;
  86. }
  87. int create_wintun(struct openconnect_info *vpninfo)
  88. {
  89. int ret = init_wintun(vpninfo);
  90. if (ret < 0)
  91. return ret;
  92. vpninfo->wintun_adapter = WintunCreateAdapter(WINTUN_POOL_NAME,
  93. vpninfo->ifname_w, NULL, NULL);
  94. if (vpninfo->wintun_adapter)
  95. return 0;
  96. ret = GetLastError();
  97. char *errstr = openconnect__win32_strerror(ret);
  98. vpn_progress(vpninfo, PRG_ERR, "Could not create Wintun adapter '%S': %s\n",
  99. vpninfo->ifname_w, errstr);
  100. free(errstr);
  101. return (ret == ERROR_ACCESS_DENIED ? -EPERM : -EIO);
  102. }
  103. intptr_t open_wintun(struct openconnect_info *vpninfo, char *guid, wchar_t *wname)
  104. {
  105. intptr_t ret;
  106. if (init_wintun(vpninfo))
  107. return 0;
  108. if (!vpninfo->wintun_adapter) {
  109. vpninfo->wintun_adapter = WintunOpenAdapter(WINTUN_POOL_NAME,
  110. wname);
  111. if (!vpninfo->wintun_adapter) {
  112. char *errstr = openconnect__win32_strerror(GetLastError());
  113. vpn_progress(vpninfo, PRG_ERR, "Could not open Wintun adapter '%S': %s\n",
  114. wname, errstr);
  115. free(errstr);
  116. ret = OPEN_TUN_SOFTFAIL;
  117. goto out;
  118. }
  119. }
  120. DWORD ver = WintunGetRunningDriverVersion();
  121. vpn_progress(vpninfo, PRG_DEBUG, _("Loaded Wintun v%lu.%lu\n"),
  122. (ver >> 16) & 0xff, ver & 0xff);
  123. vpninfo->wintun_session = WintunStartSession(vpninfo->wintun_adapter,
  124. WINTUN_RING_CAPACITY);
  125. if (!vpninfo->wintun_session) {
  126. char *errstr = openconnect__win32_strerror(GetLastError());
  127. vpn_progress(vpninfo, PRG_ERR, _("Failed to create Wintun session: %s\n"),
  128. errstr);
  129. free(errstr);
  130. ret = OPEN_TUN_HARDFAIL;
  131. goto out;
  132. }
  133. return 1;
  134. out:
  135. os_shutdown_wintun(vpninfo);
  136. return ret;
  137. }
  138. int os_read_wintun(struct openconnect_info *vpninfo, struct pkt *pkt)
  139. {
  140. DWORD tun_len;
  141. BYTE *tun_pkt = WintunReceivePacket(vpninfo->wintun_session,
  142. &tun_len);
  143. if (!tun_pkt) {
  144. DWORD err = GetLastError();
  145. if (err != ERROR_NO_MORE_ITEMS) {
  146. char *errstr = openconnect__win32_strerror(err);
  147. vpn_progress(vpninfo, PRG_ERR, _("Could not retrieve packet from Wintun adapter '%S': %s\n"),
  148. vpninfo->ifname_w, errstr);
  149. free(errstr);
  150. }
  151. return -1;
  152. }
  153. int ret = 0;
  154. if (tun_len <= pkt->len) {
  155. memcpy(pkt->data, tun_pkt, tun_len);
  156. pkt->len = tun_len;
  157. } else {
  158. vpn_progress(vpninfo, PRG_ERR, _("Drop oversized packet retrieved from Wintun adapter '%S' (%ld > %d)\n"),
  159. vpninfo->ifname_w, tun_len, pkt->len);
  160. ret = -1;
  161. }
  162. WintunReleaseReceivePacket(vpninfo->wintun_session, tun_pkt);
  163. return ret;
  164. }
  165. int os_write_wintun(struct openconnect_info *vpninfo, struct pkt *pkt)
  166. {
  167. BYTE *tun_pkt = WintunAllocateSendPacket(vpninfo->wintun_session,
  168. pkt->len);
  169. if (!tun_pkt) {
  170. DWORD err = GetLastError();
  171. char *errstr = openconnect__win32_strerror(err);
  172. vpn_progress(vpninfo, PRG_ERR, _("Could not send packet through Wintun adapter '%S': %s\n"),
  173. vpninfo->ifname_w, errstr);
  174. free(errstr);
  175. return -1;
  176. }
  177. memcpy(tun_pkt, pkt->data, pkt->len);
  178. WintunSendPacket(vpninfo->wintun_session, tun_pkt);
  179. return 0;
  180. }
  181. void os_shutdown_wintun(struct openconnect_info *vpninfo)
  182. {
  183. if (vpninfo->wintun_session) {
  184. WintunEndSession(vpninfo->wintun_session);
  185. vpninfo->wintun_session = NULL;
  186. }
  187. if (vpninfo->wintun_adapter) {
  188. WintunDeleteAdapter(vpninfo->wintun_adapter, FALSE, NULL);
  189. WintunFreeAdapter(vpninfo->wintun_adapter);
  190. vpninfo->wintun_adapter = NULL;
  191. }
  192. logger_vpninfo = NULL;
  193. FreeLibrary(vpninfo->wintun);
  194. vpninfo->wintun = NULL;
  195. }
  196. int setup_wintun_fd(struct openconnect_info *vpninfo, intptr_t tun_fd)
  197. {
  198. vpninfo->tun_rd_overlap.hEvent = WintunGetReadWaitEvent(vpninfo->wintun_session);
  199. monitor_read_fd(vpninfo, tun);
  200. vpninfo->tun_fh = (HANDLE)tun_fd;
  201. return 0;
  202. }