-
Notifications
You must be signed in to change notification settings - Fork 0
/
wintun.c
212 lines (182 loc) · 6.38 KB
/
wintun.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
/*
* OpenConnect (SSL + DTLS) VPN client
*
* Copyright © 2021 David Woodhouse.
*
* Author: David Woodhouse <[email protected]>
*
* 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>
#include <winsock2.h>
#include <ws2ipdef.h>
#include <iphlpapi.h>
#include <netioapi.h>
#include <errno.h>
#include <stdio.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)
{
int ret = init_wintun(vpninfo);
if (ret < 0)
return ret;
vpninfo->wintun_adapter = WintunCreateAdapter(WINTUN_POOL_NAME,
vpninfo->ifname_w, NULL, NULL);
if (vpninfo->wintun_adapter)
return 0;
ret = GetLastError();
char *errstr = openconnect__win32_strerror(ret);
vpn_progress(vpninfo, PRG_ERR, "Could not create Wintun adapter '%S': %s\n",
vpninfo->ifname_w, errstr);
free(errstr);
return (ret == ERROR_ACCESS_DENIED ? -EPERM : -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;
}
}
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;
}