1 /* Copyright 2008-2019 Bernhard R. Fischer.
2 *
3 * This file is part of OnionCat.
4 *
5 * OnionCat is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3 of the License.
8 *
9 * OnionCat is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with OnionCat. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 /*! \file ocat_wintuntap.c
19 * This file contains the Windows code for accessing the OpenVPN TAP driver.
20 * This driver must be installed in order to run OnionCat on Windows.
21 *
22 * The source code of this file was originally written by Wolfgang Ginolas for
23 * his P2PVPN project (http://www.p2pvpn.org/) and was by his permission
24 * adapted (thanks) to the needs for OnionCat.
25 * \author Bernhard R. Fischer, <bf@abenteuerland.at>
26 * \date 2019/09/08
27 */
28
29 #ifdef __CYGWIN__
30
31 #include "ocat.h"
32
33 #include <windows.h>
34 #include <objbase.h>
35 #include <winioctl.h>
36
37
38 // this is the registry directory where the drivers reside in
39 #define ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
40 // this registry directory contains also information about network drivers
41 #define NETWORK_CONNECTIONS_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
42 const char *tap_component_id_[] = {"tap0901", "tapoas", "tap0801", NULL};
43
44 #define USERMODEDEVICEDIR "\\\\.\\Global\\"
45 #define TAPSUFFIX ".tap"
46
47 #define TAP_CONTROL_CODE(request,method) \
48 CTL_CODE (FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS)
49
50 #define TAP_IOCTL_GET_MAC TAP_CONTROL_CODE (1, METHOD_BUFFERED)
51 #define TAP_IOCTL_GET_VERSION TAP_CONTROL_CODE (2, METHOD_BUFFERED)
52 #define TAP_IOCTL_GET_MTU TAP_CONTROL_CODE (3, METHOD_BUFFERED)
53 #define TAP_IOCTL_GET_INFO TAP_CONTROL_CODE (4, METHOD_BUFFERED)
54 #define TAP_IOCTL_CONFIG_POINT_TO_POINT TAP_CONTROL_CODE (5, METHOD_BUFFERED)
55 #define TAP_IOCTL_SET_MEDIA_STATUS TAP_CONTROL_CODE (6, METHOD_BUFFERED)
56 #define TAP_IOCTL_CONFIG_DHCP_MASQ TAP_CONTROL_CODE (7, METHOD_BUFFERED)
57 #define TAP_IOCTL_GET_LOG_LINE TAP_CONTROL_CODE (8, METHOD_BUFFERED)
58 #define TAP_IOCTL_CONFIG_DHCP_SET_OPT TAP_CONTROL_CODE (9, METHOD_BUFFERED)
59
60
61 typedef struct TapData
62 {
63 HANDLE fd;
64 HANDLE read_event;
65 HANDLE write_event;
66 OVERLAPPED read_overlapped;
67 OVERLAPPED write_overlapped;
68 } TapData_t;
69
70
71 static TapData_t tapData_;
72
73
findTapDevice(char * deviceID,int deviceIDLen,char * deviceName,int deviceNameLen,const char * tap_component_id)74 int findTapDevice(char *deviceID, int deviceIDLen, char *deviceName, int deviceNameLen, const char *tap_component_id)
75 {
76 HKEY adapterKey, key;
77 int i;
78 DWORD len;
79 char keyI[1024], keyName[1024], componentId[256];
80
81 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, ADAPTER_KEY, 0, KEY_READ, &adapterKey) != ERROR_SUCCESS)
82 {
83 log_msg(LOG_ERR, "RegOpenKeyEx \"%s\" failed. Error = %ld", ADAPTER_KEY, GetLastError());
84 return -1;
85 }
86
87 deviceID[0] = '\0';
88 for (i = 0; deviceID[0] == '\0' &&
89 ERROR_SUCCESS == RegEnumKey(adapterKey, i, keyI, sizeof(keyI)); i++)
90 {
91 snprintf(keyName, sizeof(keyName), "%s\\%s", ADAPTER_KEY, keyI);
92
93 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName, 0, KEY_READ, &key) != ERROR_SUCCESS)
94 {
95 log_msg(LOG_ERR, "RegOpenKeyEx \"%s\" failed. Error = %ld", keyName, GetLastError());
96 return -1;
97 }
98
99 len = sizeof(componentId);
100 if ((RegQueryValueEx(key, "ComponentId", NULL, NULL, componentId, &len) == ERROR_SUCCESS)
101 && !strcmp(componentId, tap_component_id))
102 {
103 len = deviceIDLen;
104 RegQueryValueEx(key, "NetCfgInstanceId", NULL, NULL, deviceID, &len);
105 log_debug("ComponentId = \"%s\", NetCfgInstanceId = \"%s\"", componentId, deviceID);
106 }
107
108 RegCloseKey(key);
109 }
110
111 RegCloseKey(adapterKey);
112
113 if (!deviceID[0])
114 return -1;
115
116 snprintf(keyName, sizeof(keyName), "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, deviceID);
117 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName, 0, KEY_READ, &key) != ERROR_SUCCESS)
118 {
119 log_msg(LOG_ERR, "RegOpenKeyEx \"%s\" failed. Error = %ld", keyName, GetLastError());
120 return -1;
121 }
122
123 len = deviceNameLen;
124 if (RegQueryValueEx(key, "Name", NULL, NULL, deviceName, &len) != ERROR_SUCCESS)
125 {
126 log_msg(LOG_ERR, "RegQueryValueEx \"%s\" failed. Error = %ld", key, GetLastError());
127 RegCloseKey(key);
128 return -1;
129 }
130
131 RegCloseKey(key);
132 return 0;
133 }
134
135
136 /*! Open TAP driver. */
win_open_tun(char * dev,int s)137 int win_open_tun(char *dev, int s)
138 {
139 char deviceId[SIZE_256], deviceName[SIZE_256], tapPath[SIZE_256];
140 TapData_t *tapData = &tapData_;
141 DWORD len = 0;
142 int status, i;
143
144 for (i = 0; tap_component_id_[i] != NULL; i++)
145 if ((status = findTapDevice(deviceId, sizeof(deviceId), deviceName, sizeof(deviceName), tap_component_id_[i])) != -1)
146 break;
147
148 if (status == -1)
149 {
150 log_msg(LOG_ALERT, "could not find TAP driver with valid componentId. Probly not installed");
151 return -1;
152 }
153
154 log_debug("TAP found. deviceId = \"%s\", deviceName = \"%s\"", deviceId, deviceName);
155
156 snprintf(tapPath, sizeof(tapPath), "%s%s%s", USERMODEDEVICEDIR, deviceId, TAPSUFFIX);
157 log_debug("creating file at \"%s\"", tapPath);
158 tapData->fd = CreateFile( tapPath, GENERIC_READ | GENERIC_WRITE, 0, 0,
159 OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0 );
160
161 if (tapData->fd == INVALID_HANDLE_VALUE)
162 {
163 log_msg(LOG_ALERT, "CreateFile failed. Error = %ld", GetLastError());
164 return -1;
165 }
166
167 status = TRUE;
168 // FIXME: return value should be handled!
169 DeviceIoControl(tapData->fd, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof
170 (status), &status, sizeof (status), &len, NULL);
171
172 // FIXME: return value should be handled!
173 tapData->read_event = CreateEvent(NULL, FALSE, FALSE, NULL);
174 // FIXME: return value should be handled!
175 tapData->write_event = CreateEvent(NULL, FALSE, FALSE, NULL);
176
177 tapData->read_overlapped.Offset = 0;
178 tapData->read_overlapped.OffsetHigh = 0;
179 tapData->read_overlapped.hEvent = tapData->read_event;
180
181 tapData->write_overlapped.Offset = 0;
182 tapData->write_overlapped.OffsetHigh = 0;
183 tapData->write_overlapped.hEvent = tapData->write_event;
184
185 // set IPv6 address
186 // % netsh interface ipv6 add address "LAN-Verbindung 2" fd87:d87e:eb43:0:84:2100:0:8421
187 // add route
188 // % netsh interface ipv6 add route fd87:d87e:eb43::/48 "LAN-Verbindung 2"
189
190 strlcpy(dev, deviceName, s);
191 return 0;
192 }
193
194
195 /*! Close TAP driver. */
win_close_tun(void)196 int win_close_tun(void)
197 {
198 if (!CloseHandle(tapData_.fd))
199 {
200 log_msg(LOG_ERR, "CloseHandle failed. Error = %ld", GetLastError());
201 return -1;
202 }
203 return 0;
204 }
205
206
win_write_tun(const char * jb,int len)207 int win_write_tun(const char *jb, int len)
208 {
209 TapData_t *tapData = &tapData_;
210 DWORD written, err;
211
212 log_debug("WriteFile %d bytes", len);
213 if (!WriteFile(tapData->fd, jb, len, &written, &tapData->write_overlapped))
214 {
215 if ((err = GetLastError()) != ERROR_IO_PENDING)
216 {
217 log_msg(LOG_ERR, "error writing %ld", err);
218 return -1;
219 }
220 else
221 {
222 log_debug("IO_PENDING");
223 if (!GetOverlappedResult(tapData->fd, &tapData->write_overlapped, &written, FALSE))
224 {
225 err = GetLastError();
226 log_debug("GetOverlappedResult failed. Error = %ld", err);
227 if (err == ERROR_IO_INCOMPLETE)
228 {
229 log_debug("IO_COMPLETE, WaitForSingleObject");
230 if ((err = WaitForSingleObject(tapData->write_event, INFINITE)) == WAIT_FAILED)
231 log_msg(LOG_ERR, "WaitForSingleObject failed. Error = %ld", GetLastError());
232 else
233 log_debug("WaitForSingleObject returen %08lx", err);
234 }
235 written = -1;
236 }
237 log_debug("GetOverlappedResult(): written = %d", written);
238 }
239 }
240
241 return written;
242 }
243
244
245 /*! Read from TAP driver. */
win_read_tun(char * buf,int n)246 int win_read_tun(char *buf, int n)
247 {
248 TapData_t *tapData = &tapData_;
249 DWORD len, err;
250
251 log_debug("ReadFile max. %d bytes", n);
252 if (!ReadFile(tapData->fd, buf, n, &len, &tapData->read_overlapped))
253 {
254 // check if I/O is still pending
255 if ((err = GetLastError()) == ERROR_IO_PENDING)
256 {
257 for (err = WAIT_TIMEOUT; err == WAIT_TIMEOUT;)
258 {
259 log_debug("ReadFile pending...");
260 if ((err = WaitForSingleObject(tapData->read_event, SELECT_TIMEOUT * 1000)) == WAIT_FAILED)
261 log_msg(LOG_ERR, "WaitForSingleObject failed. Error = %ld", GetLastError());
262 log_debug("WaitForSingleObject returned %08lx", err);
263 }
264
265 if (!GetOverlappedResult(tapData->fd, &tapData->read_overlapped, &len, FALSE))
266 {
267 // GetOverlappedResult may fail if buffer was too small
268 err = GetLastError();
269 if (err == ERROR_IO_INCOMPLETE)
270 log_msg(LOG_WARNING, "GetOverlappedResult return INCOMPLETE...unhandled");
271 else
272 log_msg(LOG_WARNING, "GetOverlappedResult failed. Error = %ld", err);
273 }
274 else
275 log_debug("overlapped_read returned %ld bytes", len);
276 }
277 else
278 log_debug("ReadFile returned %ld bytes", err);
279 }
280
281 return len;
282 }
283
284 #if 0
285 #define BUFLEN 1500
286 int main()
287 {
288 char buf[BUFLEN];
289 int len, i;
290
291 memset(&tapData_, 0, sizeof(tapData_));
292
293 win_open_tun();
294 printf("opened....");
295
296 //win_write_tun(buf, 10);
297 for (;;)
298 {
299 len = win_read_tun(buf, BUFLEN);
300
301 printf("read %d bytes\n", len);
302 for (i = 0; i < len; i++)
303 printf("%02x ", buf[i] & 0xff);
304 printf("\n\n");
305 }
306
307 win_close_tun();
308
309
310
311 return 0;
312 }
313 #endif
314
315 #endif /* __CYGWIN__ */
316
317