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