1 |
dpavlin |
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 |
|
|
} |