1 |
/* |
2 |
* PearPC |
3 |
* sysethtun.cc |
4 |
* |
5 |
* win32-specific ethernet-tunnel access |
6 |
* |
7 |
* Copyright (C) 2004 John Kelley (pearpc@kelley.ca) |
8 |
* Copyright (C) 2004 Stefan Weyergraf |
9 |
* |
10 |
* This program is free software; you can redistribute it and/or modify |
11 |
* it under the terms of the GNU General Public License version 2 as |
12 |
* published by the Free Software Foundation. |
13 |
* |
14 |
* This program is distributed in the hope that it will be useful, |
15 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 |
* GNU General Public License for more details. |
18 |
* |
19 |
* You should have received a copy of the GNU General Public License |
20 |
* along with this program; if not, write to the Free Software |
21 |
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
22 |
*/ |
23 |
|
24 |
// FIXME: What about multiple instances of Win32EthTunDevice? |
25 |
|
26 |
#include <errno.h> |
27 |
#include <stdio.h> |
28 |
#include <winsock.h> |
29 |
#include <windows.h> |
30 |
#include <Winioctl.h> |
31 |
|
32 |
#undef FASTCALL |
33 |
|
34 |
#ifdef HAVE_CONFIG_H |
35 |
#include "config.h" |
36 |
#endif |
37 |
|
38 |
#include "system/sysethtun.h" |
39 |
#include "tools/except.h" |
40 |
#include "tap_constants.h" |
41 |
#include "tools/snprintf.h" |
42 |
|
43 |
//minimum version that we will work with |
44 |
#define TAP_WIN32_MIN_MAJOR 7 |
45 |
#define TAP_WIN32_MIN_MINOR 1 |
46 |
|
47 |
#define printm(s...) ht_printf("[TAP-WIN32]: "s) |
48 |
#define MAX_PACKET_SIZE 16384 |
49 |
#define ERRORMSG_SIZE 1024 |
50 |
|
51 |
//simple function to translate an error code into a string |
52 |
static void getErrorString(char *out, int maxSize, DWORD error) |
53 |
{ |
54 |
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, |
55 |
NULL, |
56 |
error, |
57 |
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language |
58 |
(LPTSTR) out, |
59 |
maxSize, |
60 |
NULL); |
61 |
} |
62 |
//checks to see if a given NIC GUID is a TAP-WIN32 device |
63 |
static bool is_tap_win32_dev(const char *guid) |
64 |
{ |
65 |
HKEY netcard_key; |
66 |
LONG status; |
67 |
DWORD len; |
68 |
int i = 0; |
69 |
|
70 |
status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, ADAPTER_KEY, 0, KEY_READ, &netcard_key); |
71 |
|
72 |
if (status != ERROR_SUCCESS) { |
73 |
printm("Error opening registry key: %s\n", ADAPTER_KEY); |
74 |
return false; |
75 |
} |
76 |
|
77 |
while (true) { |
78 |
char enum_name[256]; |
79 |
char unit_string[256]; |
80 |
HKEY unit_key; |
81 |
char component_id_string[] = "ComponentId"; |
82 |
char component_id[256]; |
83 |
char net_cfg_instance_id_string[] = "NetCfgInstanceId"; |
84 |
char net_cfg_instance_id[256]; |
85 |
DWORD data_type; |
86 |
|
87 |
len = sizeof (enum_name); |
88 |
status = RegEnumKeyEx(netcard_key, i, enum_name, &len, NULL, NULL, NULL, NULL); |
89 |
if (status == ERROR_NO_MORE_ITEMS) |
90 |
break; |
91 |
else if (status != ERROR_SUCCESS) { |
92 |
printm("Error enumerating registry subkeys of key: %s\n", |
93 |
ADAPTER_KEY); |
94 |
return false; |
95 |
} |
96 |
|
97 |
ht_snprintf(unit_string, sizeof(unit_string), "%s\\%s", ADAPTER_KEY, enum_name); |
98 |
|
99 |
status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, unit_string, 0, KEY_READ, &unit_key); |
100 |
|
101 |
if (status != ERROR_SUCCESS) { |
102 |
printm("Error opening registry key: %s\n", unit_string); |
103 |
return false; |
104 |
} else { |
105 |
len = sizeof (component_id); |
106 |
status = RegQueryValueEx(unit_key, component_id_string, NULL, &data_type, (BYTE *)component_id, &len); |
107 |
if (!(status != ERROR_SUCCESS || data_type != REG_SZ)) { |
108 |
len = sizeof (net_cfg_instance_id); |
109 |
status = RegQueryValueEx(unit_key, net_cfg_instance_id_string, NULL, &data_type, (BYTE *)net_cfg_instance_id, &len); |
110 |
|
111 |
if (status == ERROR_SUCCESS && data_type == REG_SZ) { |
112 |
if (!strncmp(component_id, "tap", 3) |
113 |
&& !strcmp(net_cfg_instance_id, guid)) { |
114 |
RegCloseKey(unit_key); |
115 |
RegCloseKey(netcard_key); |
116 |
return true; |
117 |
} |
118 |
} |
119 |
} |
120 |
RegCloseKey(unit_key); |
121 |
} |
122 |
++i; |
123 |
} |
124 |
RegCloseKey(netcard_key); |
125 |
return false; |
126 |
} |
127 |
|
128 |
|
129 |
static int get_device_guid(char *name, int name_size, char *actual_name, int actual_name_size) |
130 |
{ |
131 |
LONG status; |
132 |
HKEY control_net_key; |
133 |
DWORD len; |
134 |
int i = 0; |
135 |
bool stop = false; |
136 |
|
137 |
status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, NETWORK_CONNECTIONS_KEY, 0, KEY_READ, &control_net_key); |
138 |
|
139 |
if (status != ERROR_SUCCESS) { |
140 |
printm("Error opening registry key: %s", NETWORK_CONNECTIONS_KEY); |
141 |
return 1; |
142 |
} |
143 |
|
144 |
while (!stop) { |
145 |
char enum_name[256]; |
146 |
char connection_string[256]; |
147 |
HKEY connection_key; |
148 |
char name_data[256]; |
149 |
DWORD name_type; |
150 |
const char name_string[] = "Name"; |
151 |
|
152 |
len = sizeof (enum_name); |
153 |
status = RegEnumKeyEx(control_net_key, i, enum_name, &len, NULL, NULL, NULL, NULL); |
154 |
|
155 |
if (status == ERROR_NO_MORE_ITEMS) |
156 |
break; |
157 |
else if (status != ERROR_SUCCESS) { |
158 |
printm("Error enumerating registry subkeys of key: %s", NETWORK_CONNECTIONS_KEY); |
159 |
return 1; |
160 |
} |
161 |
|
162 |
ht_snprintf(connection_string, sizeof(connection_string), "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, enum_name); |
163 |
status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, connection_string, 0, KEY_READ, &connection_key); |
164 |
|
165 |
if (status == ERROR_SUCCESS) { |
166 |
len = sizeof (name_data); |
167 |
status = RegQueryValueEx(connection_key, name_string, NULL, &name_type, (BYTE *)name_data, &len); |
168 |
|
169 |
if (status != ERROR_SUCCESS || name_type != REG_SZ) { |
170 |
printm("Error opening registry key: %s\\%s\\%s", NETWORK_CONNECTIONS_KEY, connection_string, name_string); |
171 |
return 1; |
172 |
} else { |
173 |
if (is_tap_win32_dev(enum_name)) { |
174 |
printm("Found TAP device named '%s'\n", name_data); |
175 |
ht_snprintf(name, name_size, "%s", enum_name); |
176 |
if (actual_name) |
177 |
ht_snprintf(actual_name, actual_name_size, "%s", name_data); |
178 |
stop = true; |
179 |
} |
180 |
} |
181 |
RegCloseKey(connection_key); |
182 |
} |
183 |
++i; |
184 |
} |
185 |
RegCloseKey(control_net_key); |
186 |
|
187 |
if (!stop) |
188 |
return 1; |
189 |
|
190 |
return 0; |
191 |
} |
192 |
|
193 |
class Win32EthTunDevice: public EthTunDevice { |
194 |
protected: |
195 |
HANDLE mFile; |
196 |
unsigned char mBuf[MAX_PACKET_SIZE]; |
197 |
DWORD mBuflen; |
198 |
OVERLAPPED mOverlapped; |
199 |
|
200 |
//sets the media status of the TAP device (cable connected or not) |
201 |
bool tap_set_status(ULONG status) |
202 |
{ |
203 |
DWORD len = 0; |
204 |
BOOL ret = DeviceIoControl(mFile, TAP_IOCTL_SET_MEDIA_STATUS, |
205 |
&status, sizeof (status), |
206 |
&status, sizeof (status), &len, NULL); |
207 |
if (!ret) { |
208 |
char errmsg[ERRORMSG_SIZE]; |
209 |
getErrorString(errmsg, sizeof errmsg, GetLastError()); |
210 |
printm("Failed: %s\n", errmsg); |
211 |
} |
212 |
return ret; |
213 |
} |
214 |
|
215 |
public: |
216 |
|
217 |
Win32EthTunDevice() |
218 |
{ |
219 |
} |
220 |
|
221 |
virtual ~Win32EthTunDevice() |
222 |
{ |
223 |
if (mFile != INVALID_HANDLE_VALUE) |
224 |
shutdownDevice(); |
225 |
} |
226 |
|
227 |
int initDevice() |
228 |
{ |
229 |
char device_path[256]; |
230 |
char device_guid[0x100]; |
231 |
int rc; |
232 |
HANDLE handle = NULL; |
233 |
|
234 |
printm("Enumerating TAP devices...\n"); |
235 |
rc = get_device_guid(device_guid, sizeof device_guid, NULL, 0); |
236 |
if (rc != 0) { |
237 |
throw MsgException("Could not locate any installed TAP-WIN32 devices."); |
238 |
} |
239 |
|
240 |
//Open Windows TAP-Win32 adapter |
241 |
ht_snprintf(device_path, sizeof device_path, "%s%s%s", |
242 |
USERMODEDEVICEDIR, |
243 |
device_guid, |
244 |
TAPSUFFIX); |
245 |
|
246 |
handle = CreateFile( |
247 |
device_path, |
248 |
GENERIC_READ | GENERIC_WRITE, |
249 |
0, |
250 |
0, |
251 |
OPEN_EXISTING, |
252 |
FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, |
253 |
0); |
254 |
|
255 |
if (handle == INVALID_HANDLE_VALUE || handle == NULL) { |
256 |
throw MsgException("Opening TAP connection failed"); |
257 |
} |
258 |
|
259 |
mFile = handle; |
260 |
mOverlapped.Offset = 0; |
261 |
mOverlapped.OffsetHigh = 0; |
262 |
mOverlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); |
263 |
|
264 |
//check TAP driver version against our minimum supported version |
265 |
{ |
266 |
ULONG info[3]; |
267 |
ULONG len; |
268 |
info[0] = info[1] = info[2] = 0; |
269 |
if (DeviceIoControl(handle, TAP_IOCTL_GET_VERSION, |
270 |
&info, sizeof (info), |
271 |
&info, sizeof (info), &len, NULL)) { |
272 |
printm("Driver Version %d.%d\n", |
273 |
(int) info[0], |
274 |
(int) info[1]); |
275 |
} else { |
276 |
if (DeviceIoControl(handle, OLD_TAP_IOCTL_GET_VERSION, |
277 |
&info, sizeof (info), |
278 |
&info, sizeof (info), &len, NULL)) { |
279 |
printm("Driver Version %d.%d %s.\n", |
280 |
(int) info[0], |
281 |
(int) info[1], |
282 |
(info[2] ? "(DEBUG) " : "")); |
283 |
|
284 |
} else { |
285 |
char errmsg[ERRORMSG_SIZE]; |
286 |
getErrorString(errmsg, sizeof errmsg, GetLastError()); |
287 |
throw MsgfException("Could not get driver version info: %s\n", errmsg); |
288 |
} |
289 |
} |
290 |
if (!(info[0] > TAP_WIN32_MIN_MAJOR || (info[0] == TAP_WIN32_MIN_MAJOR && info[1] >= TAP_WIN32_MIN_MINOR))) { |
291 |
throw MsgfException("ERROR: This version of PearPC requires a TAP-Win32 driver that is at least version %d.%d\n" |
292 |
"Please install an updated version from http://prdownloads.sourceforge.net/openvpn/openvpn-2.0_beta2-install.exe\n", |
293 |
TAP_WIN32_MIN_MAJOR, |
294 |
TAP_WIN32_MIN_MINOR); |
295 |
} |
296 |
} |
297 |
|
298 |
//connect our virtual cat5 cable to the TAP device |
299 |
if (!tap_set_status(TRUE)) { |
300 |
if (CloseHandle(handle) != 1) { |
301 |
printm("Error closing handle.\n"); |
302 |
} |
303 |
throw MsgfException("Setting Media Status to connected failed (handle is %d)\n", handle); |
304 |
} |
305 |
return 0; |
306 |
} |
307 |
|
308 |
int shutdownDevice() |
309 |
{ |
310 |
printm("Setting Media Status to disconnected.\n"); |
311 |
if (!tap_set_status(false)) { |
312 |
printm("Error disconnecting media.\n"); |
313 |
} |
314 |
printm("Closing TAP-WIN32 handle.\n"); |
315 |
CloseHandle(mFile); |
316 |
mFile = INVALID_HANDLE_VALUE; |
317 |
return 0; |
318 |
} |
319 |
|
320 |
virtual uint recvPacket(void *buf, uint size) |
321 |
{ |
322 |
if (mBuflen > size) { |
323 |
// no partial packets. drop it. |
324 |
mBuflen = 0; |
325 |
return 0; |
326 |
} |
327 |
memcpy(buf, mBuf, mBuflen); |
328 |
uint ret = mBuflen; |
329 |
mBuflen = 0; |
330 |
return ret; |
331 |
} |
332 |
|
333 |
virtual int waitRecvPacket() |
334 |
{ |
335 |
BOOL status; |
336 |
mOverlapped.Offset = 0; |
337 |
mOverlapped.OffsetHigh = 0; |
338 |
ResetEvent(mOverlapped.hEvent); |
339 |
status = ReadFile(mFile, mBuf, sizeof mBuf, &mBuflen, &mOverlapped); |
340 |
if (!status) { |
341 |
DWORD e = GetLastError(); |
342 |
if (e == ERROR_IO_PENDING) { |
343 |
WaitForSingleObject(mOverlapped.hEvent, INFINITE); |
344 |
if (!GetOverlappedResult(mFile, &mOverlapped, &mBuflen, FALSE)) { |
345 |
printm("You should never see this error\n"); |
346 |
} |
347 |
} else { |
348 |
char errmsg[ERRORMSG_SIZE]; |
349 |
getErrorString(errmsg, sizeof errmsg, e); |
350 |
printm("Bad read error: %s\n", errmsg); |
351 |
|
352 |
return EIO; |
353 |
} |
354 |
} |
355 |
return 0; |
356 |
} |
357 |
|
358 |
virtual uint sendPacket(void *buf, uint size) |
359 |
{ |
360 |
DWORD written; |
361 |
BOOL ret; |
362 |
OVERLAPPED wrov = {0}; |
363 |
ret = WriteFile(mFile, buf, size, &written, &wrov); |
364 |
if (!ret) { |
365 |
char errmsg[ERRORMSG_SIZE]; |
366 |
getErrorString(errmsg, sizeof errmsg, GetLastError()); |
367 |
printm("Sending of %d bytes failed (%d bytes sent): %s\n", size, written, errmsg); |
368 |
} |
369 |
return written; |
370 |
} |
371 |
|
372 |
virtual uint getWriteFramePrefix() |
373 |
{ |
374 |
return 0; |
375 |
} |
376 |
|
377 |
}; // end of Win32EthTunDevice |
378 |
|
379 |
EthTunDevice *createEthernetTunnel() |
380 |
{ |
381 |
return new Win32EthTunDevice(); |
382 |
} |