diff options
Diffstat (limited to 'src/exit')
-rw-r--r-- | src/exit/Makefile.am | 19 | ||||
-rw-r--r-- | src/exit/gnunet-helper-exit-windows.c | 1699 |
2 files changed, 2 insertions, 1716 deletions
diff --git a/src/exit/Makefile.am b/src/exit/Makefile.am index ea4f08c73..b7286349d 100644 --- a/src/exit/Makefile.am +++ b/src/exit/Makefile.am | |||
@@ -1,11 +1,6 @@ | |||
1 | # This Makefile.am is in the public domain | 1 | # This Makefile.am is in the public domain |
2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include | 2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include |
3 | 3 | ||
4 | if MINGW | ||
5 | WINFLAGS = -Wl,--no-undefined,--export-all-symbols | ||
6 | EXITBIN = gnunet-helper-exit | ||
7 | endif | ||
8 | |||
9 | if USE_COVERAGE | 4 | if USE_COVERAGE |
10 | AM_CFLAGS = --coverage -O0 | 5 | AM_CFLAGS = --coverage -O0 |
11 | endif | 6 | endif |
@@ -32,20 +27,10 @@ libexec_PROGRAMS = \ | |||
32 | gnunet-daemon-exit \ | 27 | gnunet-daemon-exit \ |
33 | $(EXITBIN) | 28 | $(EXITBIN) |
34 | 29 | ||
35 | if MINGW | ||
36 | gnunet_helper_exit_LDFLAGS = \ | ||
37 | -no-undefined -Wl,--export-all-symbols | ||
38 | 30 | ||
39 | gnunet_helper_exit_LDADD = \ | 31 | gnunet_helper_exit_SOURCES = \ |
40 | -lsetupapi -lnewdev -lshell32 -liconv -lstdc++ \ | 32 | gnunet-helper-exit.c |
41 | -lcomdlg32 -lgdi32 -liphlpapi | ||
42 | 33 | ||
43 | gnunet_helper_exit_SOURCES = \ | ||
44 | gnunet-helper-exit-windows.c | ||
45 | else | ||
46 | gnunet_helper_exit_SOURCES = \ | ||
47 | gnunet-helper-exit.c | ||
48 | endif | ||
49 | gnunet_daemon_exit_SOURCES = \ | 34 | gnunet_daemon_exit_SOURCES = \ |
50 | gnunet-daemon-exit.c exit.h | 35 | gnunet-daemon-exit.c exit.h |
51 | gnunet_daemon_exit_LDADD = \ | 36 | gnunet_daemon_exit_LDADD = \ |
diff --git a/src/exit/gnunet-helper-exit-windows.c b/src/exit/gnunet-helper-exit-windows.c deleted file mode 100644 index 0ffd28148..000000000 --- a/src/exit/gnunet-helper-exit-windows.c +++ /dev/null | |||
@@ -1,1699 +0,0 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2010, 2012 Christian Grothoff | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | /** | ||
21 | * @file exit/gnunet-helper-exit-windows.c | ||
22 | * @brief the helper for the EXIT service in win32 builds. | ||
23 | * Opens a virtual network-interface, sends data received on the if to stdout, | ||
24 | * sends data received on stdin to the interface | ||
25 | * @author Christian M. Fuchs | ||
26 | * | ||
27 | * The following list of people have reviewed this code and considered | ||
28 | * it safe since the last modification (if you reviewed it, please | ||
29 | * have your name added to the list): | ||
30 | * | ||
31 | */ | ||
32 | |||
33 | #include <stdio.h> | ||
34 | #include <Winsock2.h> | ||
35 | #include <windows.h> | ||
36 | #include <setupapi.h> | ||
37 | #ifndef __MINGW64_VERSION_MAJOR | ||
38 | #include <ddk/cfgmgr32.h> | ||
39 | #include <ddk/newdev.h> | ||
40 | #else | ||
41 | #include <cfgmgr32.h> | ||
42 | #include <newdev.h> | ||
43 | #endif | ||
44 | #include <time.h> | ||
45 | #include "platform.h" | ||
46 | #include "tap-windows.h" | ||
47 | /** | ||
48 | * Need 'struct GNUNET_HashCode' and 'struct GNUNET_PeerIdentity'. | ||
49 | */ | ||
50 | #include "gnunet_crypto_lib.h" | ||
51 | /** | ||
52 | * Need 'struct GNUNET_MessageHeader'. | ||
53 | */ | ||
54 | #include "gnunet_common.h" | ||
55 | |||
56 | /** | ||
57 | * Need VPN message types. | ||
58 | */ | ||
59 | #include "gnunet_protocols.h" | ||
60 | |||
61 | /** | ||
62 | * Should we print (interesting|debug) messages that can happen during | ||
63 | * normal operation? | ||
64 | */ | ||
65 | #define DEBUG GNUNET_NO | ||
66 | |||
67 | #if DEBUG | ||
68 | /* FIXME: define with varargs... */ | ||
69 | #define LOG_DEBUG(msg) fprintf(stderr, "%s", msg); | ||
70 | #else | ||
71 | #define LOG_DEBUG(msg) do {} while (0) | ||
72 | #endif | ||
73 | |||
74 | /** | ||
75 | * Will this binary be run in permissions testing mode? | ||
76 | */ | ||
77 | static boolean privilege_testing = FALSE; | ||
78 | |||
79 | /** | ||
80 | * Maximum size of a GNUnet message (GNUNET_MAX_MESSAGE_SIZE) | ||
81 | */ | ||
82 | #define MAX_SIZE 65536 | ||
83 | |||
84 | /** | ||
85 | * Name or Path+Name of our win32 driver. | ||
86 | * The .sys and .cat files HAVE to be in the same location as this file! | ||
87 | */ | ||
88 | #define INF_FILE "share/gnunet/openvpn-tap32/tapw32/OemWin2k.inf" | ||
89 | |||
90 | /** | ||
91 | * Name or Path+Name of our win64 driver. | ||
92 | * The .sys and .cat files HAVE to be in the same location as this file! | ||
93 | */ | ||
94 | #define INF_FILE64 "share/gnunet/openvpn-tap32/tapw64/OemWin2k.inf" | ||
95 | |||
96 | /** | ||
97 | * Hardware ID used in the inf-file. | ||
98 | * This might change over time, as openvpn advances their driver | ||
99 | */ | ||
100 | #define HARDWARE_ID "tap0901" | ||
101 | |||
102 | /** | ||
103 | * Minimum major-id of the driver version we can work with | ||
104 | */ | ||
105 | #define TAP_WIN_MIN_MAJOR 9 | ||
106 | |||
107 | /** | ||
108 | * Minimum minor-id of the driver version we can work with. | ||
109 | * v <= 7 has buggy IPv6. | ||
110 | * v == 8 is broken for small IPv4 Packets | ||
111 | */ | ||
112 | #define TAP_WIN_MIN_MINOR 9 | ||
113 | |||
114 | /** | ||
115 | * Time in seconds to wait for our virtual device to go up after telling it to do so. | ||
116 | * | ||
117 | * openvpn doesn't specify a value, 4 seems sane for testing, even for openwrt | ||
118 | * (in fact, 4 was chosen by a fair dice roll...) | ||
119 | */ | ||
120 | #define TAP32_POSTUP_WAITTIME 4 | ||
121 | |||
122 | /** | ||
123 | * Location of the network interface list resides in registry. | ||
124 | */ | ||
125 | #define INTERFACE_REGISTRY_LOCATION "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}" | ||
126 | |||
127 | /** | ||
128 | * Our local process' PID. Used for creating a sufficiently unique additional | ||
129 | * hardware ID for our device. | ||
130 | */ | ||
131 | static char secondary_hwid[LINE_LEN / 2]; | ||
132 | |||
133 | /** | ||
134 | * Device's visible Name, used to identify a network device in netsh. | ||
135 | * eg: "Local Area Connection 9" | ||
136 | */ | ||
137 | static char device_visible_name[256]; | ||
138 | |||
139 | /** | ||
140 | * This is our own local instance of a virtual network interface | ||
141 | * It is (somewhat) equivalent to using tun/tap in unixoid systems | ||
142 | * | ||
143 | * Upon initialization, we create such an device node. | ||
144 | * Upon termination, we remove it again. | ||
145 | * | ||
146 | * If we crash this device might stay around. | ||
147 | */ | ||
148 | static HDEVINFO DeviceInfo = INVALID_HANDLE_VALUE; | ||
149 | |||
150 | /** | ||
151 | * Registry Key we hand over to windows to spawn a new virtual interface | ||
152 | */ | ||
153 | static SP_DEVINFO_DATA DeviceNode; | ||
154 | |||
155 | /** | ||
156 | * GUID of our virtual device in the form of | ||
157 | * {12345678-1234-1234-1234-123456789abc} - in hex | ||
158 | */ | ||
159 | static char device_guid[256]; | ||
160 | |||
161 | |||
162 | /** | ||
163 | * Possible states of an IO facility. | ||
164 | */ | ||
165 | enum IO_State { | ||
166 | /** | ||
167 | * overlapped I/O is ready for work | ||
168 | */ | ||
169 | IOSTATE_READY = 0, | ||
170 | |||
171 | /** | ||
172 | * overlapped I/O has been queued | ||
173 | */ | ||
174 | IOSTATE_QUEUED, | ||
175 | |||
176 | /** | ||
177 | * overlapped I/O has finished, but is waiting for it's write-partner | ||
178 | */ | ||
179 | IOSTATE_WAITING, | ||
180 | |||
181 | /** | ||
182 | * there is a full buffer waiting | ||
183 | */ | ||
184 | IOSTATE_RESUME, | ||
185 | |||
186 | /** | ||
187 | * Operlapped IO states for facility objects | ||
188 | * overlapped I/O has failed, stop processing | ||
189 | */ | ||
190 | IOSTATE_FAILED | ||
191 | }; | ||
192 | |||
193 | |||
194 | /** | ||
195 | * A IO Object + read/writebuffer + buffer-size for windows asynchronous IO handling | ||
196 | */ | ||
197 | struct io_facility { | ||
198 | /** | ||
199 | * The mode the state machine associated with this object is in. | ||
200 | */ | ||
201 | enum IO_State facility_state; | ||
202 | |||
203 | /** | ||
204 | * If the path is open or blocked in general (used for quickly checking) | ||
205 | */ | ||
206 | BOOL path_open; // BOOL is winbool (int), NOT boolean (unsigned char)! | ||
207 | |||
208 | /** | ||
209 | * Windows Object-Handle (used for accessing TAP and STDIN/STDOUT) | ||
210 | */ | ||
211 | HANDLE handle; | ||
212 | |||
213 | /** | ||
214 | * Overlaped IO structure used for asynchronous IO in windows. | ||
215 | */ | ||
216 | OVERLAPPED overlapped; | ||
217 | |||
218 | /** | ||
219 | * Buffer for reading things to and writing from... | ||
220 | */ | ||
221 | unsigned char buffer[MAX_SIZE]; | ||
222 | |||
223 | /** | ||
224 | * How much of this buffer was used when reading or how much data can be written | ||
225 | */ | ||
226 | DWORD buffer_size; | ||
227 | |||
228 | /** | ||
229 | * Amount of data actually written or read by readfile/writefile. | ||
230 | */ | ||
231 | DWORD buffer_size_processed; | ||
232 | |||
233 | /** | ||
234 | * How much of this buffer we have written in total | ||
235 | */ | ||
236 | DWORD buffer_size_written; | ||
237 | }; | ||
238 | |||
239 | /** | ||
240 | * ReOpenFile is only available as of XP SP2 and 2003 SP1 | ||
241 | */ | ||
242 | WINBASEAPI HANDLE WINAPI ReOpenFile(HANDLE, DWORD, DWORD, DWORD); | ||
243 | |||
244 | /** | ||
245 | * IsWow64Process definition for our is_win64, as this is a kernel function | ||
246 | */ | ||
247 | typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL); | ||
248 | |||
249 | |||
250 | /** | ||
251 | * Like strlcpy but portable. The given string @a src is copied until its null | ||
252 | * byte or until @a n - 1 bytes have been read. The destination buffer is | ||
253 | * guaranteed to be null-terminated. | ||
254 | * | ||
255 | * @param dst destination of the copy (must be @a n bytes long) | ||
256 | * @param src source of the copy (at most @a n - 1 bytes will be read) | ||
257 | * @param n the length of the string to copy, including its terminating null | ||
258 | * byte | ||
259 | * @return the length of the string that was copied, excluding the terminating | ||
260 | * null byte | ||
261 | */ | ||
262 | size_t | ||
263 | GNUNET_strlcpy(char *dst, const char *src, size_t n) | ||
264 | { | ||
265 | size_t ret; | ||
266 | size_t slen; | ||
267 | |||
268 | GNUNET_assert(0 != n); | ||
269 | slen = strnlen(src, n - 1); | ||
270 | memcpy(dst, src, slen); | ||
271 | dst[slen] = '\0'; | ||
272 | return slen; | ||
273 | } | ||
274 | |||
275 | |||
276 | /** | ||
277 | * Determines if the host OS is win32 or win64 | ||
278 | * | ||
279 | * @return true if | ||
280 | */ | ||
281 | BOOL | ||
282 | is_win64() | ||
283 | { | ||
284 | #if defined(_WIN64) | ||
285 | //this is a win64 binary, | ||
286 | return TRUE; | ||
287 | #elif defined(_WIN32) | ||
288 | //this is a 32bit binary, and we need to check if we are running in WOW64 | ||
289 | BOOL success = FALSE; | ||
290 | BOOL on_wow64 = FALSE; | ||
291 | LPFN_ISWOW64PROCESS IsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandle("kernel32"), "IsWow64Process"); | ||
292 | |||
293 | if (NULL != IsWow64Process) | ||
294 | success = IsWow64Process(GetCurrentProcess(), &on_wow64); | ||
295 | |||
296 | return success && on_wow64; | ||
297 | #endif | ||
298 | } | ||
299 | /** | ||
300 | * Wrapper for executing a shellcommand in windows. | ||
301 | * | ||
302 | * @param command - the command + parameters to execute | ||
303 | * @return * exitcode of the program executed, | ||
304 | * * EINVAL (cmd/file not found) | ||
305 | * * EPIPE (could not read STDOUT) | ||
306 | */ | ||
307 | static int | ||
308 | execute_shellcommand(const char *command) | ||
309 | { | ||
310 | FILE *pipe; | ||
311 | |||
312 | if ((NULL == command) || | ||
313 | (NULL == (pipe = _popen(command, "rt")))) | ||
314 | return EINVAL; | ||
315 | |||
316 | #if DEBUG | ||
317 | fprintf(stderr, "DEBUG: Command output: \n"); | ||
318 | char output[LINE_LEN]; | ||
319 | while (NULL != fgets(output, sizeof(output), pipe)) | ||
320 | fprintf(stderr, "%s", output); | ||
321 | #endif | ||
322 | |||
323 | return _pclose(pipe); | ||
324 | } | ||
325 | |||
326 | |||
327 | /** | ||
328 | * @brief Sets the IPv6-Address given in address on the interface dev | ||
329 | * | ||
330 | * @param address the IPv6-Address | ||
331 | * @param prefix_len the length of the network-prefix | ||
332 | */ | ||
333 | static int | ||
334 | set_address6(const char *address, unsigned long prefix_len) | ||
335 | { | ||
336 | int ret = EINVAL; | ||
337 | char command[LINE_LEN]; | ||
338 | struct sockaddr_in6 sa6; | ||
339 | |||
340 | /* | ||
341 | * parse the new address | ||
342 | */ | ||
343 | memset(&sa6, 0, sizeof(struct sockaddr_in6)); | ||
344 | sa6.sin6_family = AF_INET6; | ||
345 | if (1 != inet_pton(AF_INET6, address, &sa6.sin6_addr.s6_addr)) | ||
346 | { | ||
347 | fprintf(stderr, "ERROR: Failed to parse address `%s': %s\n", address, | ||
348 | strerror(errno)); | ||
349 | return -1; | ||
350 | } | ||
351 | |||
352 | /* | ||
353 | * prepare the command | ||
354 | */ | ||
355 | snprintf(command, LINE_LEN, | ||
356 | "netsh interface ipv6 add address \"%s\" %s/%d store=active", | ||
357 | device_visible_name, address, prefix_len); | ||
358 | /* | ||
359 | * Set the address | ||
360 | */ | ||
361 | ret = execute_shellcommand(command); | ||
362 | |||
363 | /* Did it work?*/ | ||
364 | if (0 != ret) | ||
365 | fprintf(stderr, "FATAL: Setting IPv6 address failed: %s\n", strerror(ret)); | ||
366 | return ret; | ||
367 | } | ||
368 | |||
369 | |||
370 | /** | ||
371 | * @brief Removes the IPv6-Address given in address from the interface dev | ||
372 | * | ||
373 | * @param address the IPv4-Address | ||
374 | */ | ||
375 | static void | ||
376 | remove_address6(const char *address) | ||
377 | { | ||
378 | char command[LINE_LEN]; | ||
379 | int ret = EINVAL; | ||
380 | |||
381 | // sanity checking was already done in set_address6 | ||
382 | /* | ||
383 | * prepare the command | ||
384 | */ | ||
385 | snprintf(command, LINE_LEN, | ||
386 | "netsh interface ipv6 delete address \"%s\" store=persistent", | ||
387 | device_visible_name); | ||
388 | /* | ||
389 | * Set the address | ||
390 | */ | ||
391 | ret = execute_shellcommand(command); | ||
392 | |||
393 | /* Did it work?*/ | ||
394 | if (0 != ret) | ||
395 | fprintf(stderr, "FATAL: removing IPv6 address failed: %s\n", strerror(ret)); | ||
396 | } | ||
397 | |||
398 | |||
399 | /** | ||
400 | * @brief Sets the IPv4-Address given in address on the interface dev | ||
401 | * | ||
402 | * @param address the IPv4-Address | ||
403 | * @param mask the netmask | ||
404 | */ | ||
405 | static int | ||
406 | set_address4(const char *address, const char *mask) | ||
407 | { | ||
408 | int ret = EINVAL; | ||
409 | char command[LINE_LEN]; | ||
410 | |||
411 | struct sockaddr_in addr; | ||
412 | |||
413 | addr.sin_family = AF_INET; | ||
414 | |||
415 | /* | ||
416 | * Parse the address | ||
417 | */ | ||
418 | if (1 != inet_pton(AF_INET, address, &addr.sin_addr.s_addr)) | ||
419 | { | ||
420 | fprintf(stderr, "ERROR: Failed to parse address `%s': %s\n", address, | ||
421 | strerror(errno)); | ||
422 | return -1; | ||
423 | } | ||
424 | // Set Device to Subnet-Mode? | ||
425 | // do we really need tun.c:2925 ? | ||
426 | |||
427 | /* | ||
428 | * prepare the command | ||
429 | */ | ||
430 | snprintf(command, LINE_LEN, | ||
431 | "netsh interface ipv4 add address \"%s\" %s %s store=active", | ||
432 | device_visible_name, address, mask); | ||
433 | /* | ||
434 | * Set the address | ||
435 | */ | ||
436 | ret = execute_shellcommand(command); | ||
437 | |||
438 | /* Did it work?*/ | ||
439 | if (0 != ret) | ||
440 | fprintf(stderr, "FATAL: Setting IPv4 address failed: %s\n", strerror(ret)); | ||
441 | return ret; | ||
442 | } | ||
443 | |||
444 | |||
445 | /** | ||
446 | * @brief Removes the IPv4-Address given in address from the interface dev | ||
447 | * | ||
448 | * @param address the IPv4-Address | ||
449 | */ | ||
450 | static void | ||
451 | remove_address4(const char *address) | ||
452 | { | ||
453 | char command[LINE_LEN]; | ||
454 | int ret = EINVAL; | ||
455 | |||
456 | // sanity checking was already done in set_address4 | ||
457 | |||
458 | /* | ||
459 | * prepare the command | ||
460 | */ | ||
461 | snprintf(command, LINE_LEN, | ||
462 | "netsh interface ipv4 delete address \"%s\" gateway=all store=persistent", | ||
463 | device_visible_name); | ||
464 | /* | ||
465 | * Set the address | ||
466 | */ | ||
467 | ret = execute_shellcommand(command); | ||
468 | |||
469 | /* Did it work?*/ | ||
470 | if (0 != ret) | ||
471 | fprintf(stderr, "FATAL: removing IPv4 address failed: %s\n", strerror(ret)); | ||
472 | } | ||
473 | |||
474 | |||
475 | /** | ||
476 | * Setup a new virtual interface to use for tunneling. | ||
477 | * | ||
478 | * @return: TRUE if setup was successful, else FALSE | ||
479 | */ | ||
480 | static BOOL | ||
481 | setup_interface() | ||
482 | { | ||
483 | /* | ||
484 | * where to find our inf-file. (+ the "full" path, after windows found") | ||
485 | * | ||
486 | * We do not directly input all the props here, because openvpn will update | ||
487 | * these details over time. | ||
488 | */ | ||
489 | char inf_file_path[MAX_PATH]; | ||
490 | char * temp_inf_filename; | ||
491 | char hwidlist[LINE_LEN + 4]; | ||
492 | char class_name[128]; | ||
493 | GUID class_guid; | ||
494 | int str_length = 0; | ||
495 | |||
496 | /** | ||
497 | * Set the device's hardware ID and add it to a list. | ||
498 | * This information will later on identify this device in registry. | ||
499 | */ | ||
500 | str_length = GNUNET_strlcpy(hwidlist, | ||
501 | HARDWARE_ID, | ||
502 | sizeof(hwidlist)) + 1; | ||
503 | /** | ||
504 | * this is kind of over-complicated, but allows keeps things independent of | ||
505 | * how the openvpn-hwid is actually stored. | ||
506 | * | ||
507 | * A HWID list is double-\0 terminated and \0 separated | ||
508 | */ | ||
509 | str_length = strlen(hwidlist) + 1; | ||
510 | str_length += GNUNET_strlcpy(&hwidlist[str_length], | ||
511 | secondary_hwid, | ||
512 | sizeof(hwidlist) - str_length) + 1; | ||
513 | GNUNET_assert(str_length < sizeof(hwidlist)); | ||
514 | hwidlist[str_length] = '\0'; | ||
515 | ++str_length; | ||
516 | |||
517 | /** | ||
518 | * Locate the inf-file, we need to store it somewhere where the system can | ||
519 | * find it. We need to pick the correct driver for win32/win64. | ||
520 | */ | ||
521 | if (is_win64()) | ||
522 | GetFullPathNameA(INF_FILE64, MAX_PATH, inf_file_path, &temp_inf_filename); | ||
523 | else | ||
524 | GetFullPathNameA(INF_FILE, MAX_PATH, inf_file_path, &temp_inf_filename); | ||
525 | |||
526 | fprintf(stderr, "INFO: Located our driver's .inf file at %s\n", inf_file_path); | ||
527 | /** | ||
528 | * Bootstrap our device info using the drivers inf-file | ||
529 | */ | ||
530 | if (!SetupDiGetINFClassA(inf_file_path, | ||
531 | &class_guid, | ||
532 | class_name, sizeof(class_name) / sizeof(char), | ||
533 | NULL)) | ||
534 | return FALSE; | ||
535 | |||
536 | /** | ||
537 | * Collect all the other needed information... | ||
538 | * let the system fill our this form | ||
539 | */ | ||
540 | DeviceInfo = SetupDiCreateDeviceInfoList(&class_guid, NULL); | ||
541 | if (DeviceInfo == INVALID_HANDLE_VALUE) | ||
542 | return FALSE; | ||
543 | |||
544 | DeviceNode.cbSize = sizeof(SP_DEVINFO_DATA); | ||
545 | if (!SetupDiCreateDeviceInfoA(DeviceInfo, | ||
546 | class_name, | ||
547 | &class_guid, | ||
548 | NULL, | ||
549 | 0, | ||
550 | DICD_GENERATE_ID, | ||
551 | &DeviceNode)) | ||
552 | return FALSE; | ||
553 | |||
554 | /* Deploy all the information collected into the registry */ | ||
555 | if (!SetupDiSetDeviceRegistryPropertyA(DeviceInfo, | ||
556 | &DeviceNode, | ||
557 | SPDRP_HARDWAREID, | ||
558 | (LPBYTE)hwidlist, | ||
559 | str_length * sizeof(char))) | ||
560 | return FALSE; | ||
561 | |||
562 | /* Install our new class(=device) into the system */ | ||
563 | if (!SetupDiCallClassInstaller(DIF_REGISTERDEVICE, | ||
564 | DeviceInfo, | ||
565 | &DeviceNode)) | ||
566 | return FALSE; | ||
567 | |||
568 | /* This system call tends to take a while (several seconds!) on | ||
569 | "modern" Windoze systems */ | ||
570 | if (!UpdateDriverForPlugAndPlayDevicesA(NULL, | ||
571 | secondary_hwid, | ||
572 | inf_file_path, | ||
573 | INSTALLFLAG_FORCE | INSTALLFLAG_NONINTERACTIVE, | ||
574 | NULL)) //reboot required? NEVER! | ||
575 | return FALSE; | ||
576 | |||
577 | fprintf(stderr, "DEBUG: successfully created a network device\n"); | ||
578 | return TRUE; | ||
579 | } | ||
580 | |||
581 | |||
582 | /** | ||
583 | * Remove our new virtual interface to use for tunneling. | ||
584 | * This function must be called AFTER setup_interface! | ||
585 | * | ||
586 | * @return: TRUE if destruction was successful, else FALSE | ||
587 | */ | ||
588 | static BOOL | ||
589 | remove_interface() | ||
590 | { | ||
591 | SP_REMOVEDEVICE_PARAMS remove; | ||
592 | |||
593 | if (INVALID_HANDLE_VALUE == DeviceInfo) | ||
594 | return FALSE; | ||
595 | |||
596 | remove.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER); | ||
597 | remove.HwProfile = 0; | ||
598 | remove.Scope = DI_REMOVEDEVICE_GLOBAL; | ||
599 | remove.ClassInstallHeader.InstallFunction = DIF_REMOVE; | ||
600 | /* | ||
601 | * 1. Prepare our existing device information set, and place the | ||
602 | * uninstall related information into the structure | ||
603 | */ | ||
604 | if (!SetupDiSetClassInstallParamsA(DeviceInfo, | ||
605 | (PSP_DEVINFO_DATA)&DeviceNode, | ||
606 | &remove.ClassInstallHeader, | ||
607 | sizeof(remove))) | ||
608 | return FALSE; | ||
609 | /* | ||
610 | * 2. Uninstall the virtual interface using the class installer | ||
611 | */ | ||
612 | if (!SetupDiCallClassInstaller(DIF_REMOVE, | ||
613 | DeviceInfo, | ||
614 | (PSP_DEVINFO_DATA)&DeviceNode)) | ||
615 | return FALSE; | ||
616 | |||
617 | SetupDiDestroyDeviceInfoList(DeviceInfo); | ||
618 | |||
619 | fprintf(stderr, "DEBUG: removed interface successfully\n"); | ||
620 | |||
621 | return TRUE; | ||
622 | } | ||
623 | |||
624 | |||
625 | /** | ||
626 | * Do all the lookup necessary to retrieve the inteface's actual name | ||
627 | * off the registry. | ||
628 | * | ||
629 | * @return: TRUE if we were able to lookup the interface's name, else FALSE | ||
630 | */ | ||
631 | static BOOL | ||
632 | resolve_interface_name() | ||
633 | { | ||
634 | SP_DEVINFO_LIST_DETAIL_DATA device_details; | ||
635 | char pnp_instance_id [MAX_DEVICE_ID_LEN]; | ||
636 | HKEY adapter_key_handle; | ||
637 | LONG status; | ||
638 | DWORD len; | ||
639 | int i = 0; | ||
640 | int retrys; | ||
641 | BOOL retval = FALSE; | ||
642 | char adapter[] = INTERFACE_REGISTRY_LOCATION; | ||
643 | |||
644 | /* We can obtain the PNP instance ID from our setupapi handle */ | ||
645 | device_details.cbSize = sizeof(device_details); | ||
646 | if (CR_SUCCESS != CM_Get_Device_ID_ExA(DeviceNode.DevInst, | ||
647 | (PCHAR)pnp_instance_id, | ||
648 | MAX_DEVICE_ID_LEN, | ||
649 | 0, //must be 0 | ||
650 | NULL)) //hMachine, we are local | ||
651 | return FALSE; | ||
652 | |||
653 | fprintf(stderr, "DEBUG: Resolving interface name for network device %s\n", pnp_instance_id); | ||
654 | |||
655 | /* Registry is incredibly slow, retry for up to 30 seconds to allow registry to refresh */ | ||
656 | for (retrys = 0; retrys < 120 && !retval; retrys++) | ||
657 | { | ||
658 | /* sleep for 250ms*/ | ||
659 | Sleep(250); | ||
660 | |||
661 | /* Now we can use this ID to locate the correct networks interface in registry */ | ||
662 | if (ERROR_SUCCESS != RegOpenKeyExA( | ||
663 | HKEY_LOCAL_MACHINE, | ||
664 | adapter, | ||
665 | 0, | ||
666 | KEY_READ, | ||
667 | &adapter_key_handle)) | ||
668 | return FALSE; | ||
669 | |||
670 | /* Of course there is a multitude of entries here, with arbitrary names, | ||
671 | * thus we need to iterate through there. | ||
672 | */ | ||
673 | while (!retval) | ||
674 | { | ||
675 | char instance_key[256]; | ||
676 | char query_key [256]; | ||
677 | HKEY instance_key_handle; | ||
678 | char pnpinstanceid_name[] = "PnpInstanceID"; | ||
679 | char pnpinstanceid_value[256]; | ||
680 | char adaptername_name[] = "Name"; | ||
681 | DWORD data_type; | ||
682 | |||
683 | len = 256 * sizeof(char); | ||
684 | /* optain a subkey of {4D36E972-E325-11CE-BFC1-08002BE10318} */ | ||
685 | status = RegEnumKeyExA( | ||
686 | adapter_key_handle, | ||
687 | i, | ||
688 | instance_key, | ||
689 | &len, | ||
690 | NULL, | ||
691 | NULL, | ||
692 | NULL, | ||
693 | NULL); | ||
694 | |||
695 | /* this may fail due to one of two reasons: | ||
696 | * we are at the end of the list*/ | ||
697 | if (ERROR_NO_MORE_ITEMS == status) | ||
698 | break; | ||
699 | // * we found a broken registry key, continue with the next key. | ||
700 | if (ERROR_SUCCESS != status) | ||
701 | goto cleanup; | ||
702 | |||
703 | /* prepare our new query string: */ | ||
704 | snprintf(query_key, 256, "%s\\%s\\Connection", | ||
705 | adapter, | ||
706 | instance_key); | ||
707 | |||
708 | /* look inside instance_key\\Connection */ | ||
709 | if (ERROR_SUCCESS != RegOpenKeyExA( | ||
710 | HKEY_LOCAL_MACHINE, | ||
711 | query_key, | ||
712 | 0, | ||
713 | KEY_READ, | ||
714 | &instance_key_handle)) | ||
715 | goto cleanup; | ||
716 | |||
717 | /* now, read our PnpInstanceID */ | ||
718 | len = sizeof(pnpinstanceid_value); | ||
719 | status = RegQueryValueExA(instance_key_handle, | ||
720 | pnpinstanceid_name, | ||
721 | NULL, //reserved, always NULL according to MSDN | ||
722 | &data_type, | ||
723 | (LPBYTE)pnpinstanceid_value, | ||
724 | &len); | ||
725 | |||
726 | if (status != ERROR_SUCCESS || data_type != REG_SZ) | ||
727 | goto cleanup; | ||
728 | |||
729 | /* compare the value we got to our devices PNPInstanceID*/ | ||
730 | if (0 != strncmp(pnpinstanceid_value, pnp_instance_id, | ||
731 | sizeof(pnpinstanceid_value) / sizeof(char))) | ||
732 | goto cleanup; | ||
733 | |||
734 | len = sizeof(device_visible_name); | ||
735 | status = RegQueryValueExA( | ||
736 | instance_key_handle, | ||
737 | adaptername_name, | ||
738 | NULL, //reserved, always NULL according to MSDN | ||
739 | &data_type, | ||
740 | (LPBYTE)device_visible_name, | ||
741 | &len); | ||
742 | |||
743 | if (status != ERROR_SUCCESS || data_type != REG_SZ) | ||
744 | goto cleanup; | ||
745 | |||
746 | /* | ||
747 | * we have successfully found OUR instance, | ||
748 | * save the device GUID before exiting | ||
749 | */ | ||
750 | GNUNET_strlcpy(device_guid, instance_key, sizeof(device_guid)); | ||
751 | retval = TRUE; | ||
752 | fprintf(stderr, "DEBUG: Interface Name lookup succeeded on retry %d, got \"%s\" %s\n", retrys, device_visible_name, device_guid); | ||
753 | |||
754 | cleanup: | ||
755 | RegCloseKey(instance_key_handle); | ||
756 | |||
757 | ++i; | ||
758 | } | ||
759 | |||
760 | RegCloseKey(adapter_key_handle); | ||
761 | } | ||
762 | return retval; | ||
763 | } | ||
764 | |||
765 | |||
766 | /** | ||
767 | * Determines the version of the installed TAP32 driver and checks if it's sufficiently new for GNUNET | ||
768 | * | ||
769 | * @param handle the handle to our tap device | ||
770 | * @return TRUE if the version is sufficient, else FALSE | ||
771 | */ | ||
772 | static BOOL | ||
773 | check_tapw32_version(HANDLE handle) | ||
774 | { | ||
775 | ULONG version[3]; | ||
776 | DWORD len; | ||
777 | |||
778 | memset(&(version), 0, sizeof(version)); | ||
779 | |||
780 | if (DeviceIoControl(handle, TAP_WIN_IOCTL_GET_VERSION, | ||
781 | &version, sizeof(version), | ||
782 | &version, sizeof(version), &len, NULL)) | ||
783 | fprintf(stderr, "INFO: TAP-Windows Driver Version %d.%d %s\n", | ||
784 | (int)version[0], | ||
785 | (int)version[1], | ||
786 | (version[2] ? "(DEBUG)" : "")); | ||
787 | |||
788 | if ((version[0] != TAP_WIN_MIN_MAJOR) || | ||
789 | (version[1] < TAP_WIN_MIN_MINOR)) | ||
790 | { | ||
791 | fprintf(stderr, "FATAL: This version of gnunet requires a TAP-Windows driver that is at least version %d.%d\n", | ||
792 | TAP_WIN_MIN_MAJOR, | ||
793 | TAP_WIN_MIN_MINOR); | ||
794 | return FALSE; | ||
795 | } | ||
796 | |||
797 | return TRUE; | ||
798 | } | ||
799 | |||
800 | |||
801 | /** | ||
802 | * Creates a tun-interface called dev; | ||
803 | * | ||
804 | * @return the fd to the tun or -1 on error | ||
805 | */ | ||
806 | static HANDLE | ||
807 | init_tun() | ||
808 | { | ||
809 | char device_path[256]; | ||
810 | HANDLE handle; | ||
811 | |||
812 | if (!setup_interface()) | ||
813 | { | ||
814 | errno = ENODEV; | ||
815 | return INVALID_HANDLE_VALUE; | ||
816 | } | ||
817 | |||
818 | if (!resolve_interface_name()) | ||
819 | { | ||
820 | errno = ENODEV; | ||
821 | return INVALID_HANDLE_VALUE; | ||
822 | } | ||
823 | |||
824 | /* Open Windows TAP-Windows adapter */ | ||
825 | snprintf(device_path, sizeof(device_path), "%s%s%s", | ||
826 | USERMODEDEVICEDIR, | ||
827 | device_guid, | ||
828 | TAP_WIN_SUFFIX); | ||
829 | |||
830 | handle = CreateFile( | ||
831 | device_path, | ||
832 | GENERIC_READ | GENERIC_WRITE, | ||
833 | 0, /* was: FILE_SHARE_READ */ | ||
834 | 0, | ||
835 | OPEN_EXISTING, | ||
836 | FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, | ||
837 | 0 | ||
838 | ); | ||
839 | |||
840 | if (INVALID_HANDLE_VALUE == handle) | ||
841 | { | ||
842 | fprintf(stderr, "FATAL: CreateFile failed on TAP device: %s\n", device_path); | ||
843 | return handle; | ||
844 | } | ||
845 | |||
846 | /* get driver version info */ | ||
847 | if (!check_tapw32_version(handle)) | ||
848 | { | ||
849 | CloseHandle(handle); | ||
850 | return INVALID_HANDLE_VALUE; | ||
851 | } | ||
852 | |||
853 | /* TODO (opt?): get MTU-Size */ | ||
854 | |||
855 | fprintf(stderr, "DEBUG: successfully opened TAP device\n"); | ||
856 | return handle; | ||
857 | } | ||
858 | |||
859 | |||
860 | /** | ||
861 | * Brings a TAP device up and sets it to connected state. | ||
862 | * | ||
863 | * @param handle the handle to our TAP device | ||
864 | * @return True if the operation succeeded, else false | ||
865 | */ | ||
866 | static BOOL | ||
867 | tun_up(HANDLE handle) | ||
868 | { | ||
869 | ULONG status = TRUE; | ||
870 | DWORD len; | ||
871 | |||
872 | if (!DeviceIoControl(handle, TAP_WIN_IOCTL_SET_MEDIA_STATUS, | ||
873 | &status, sizeof(status), | ||
874 | &status, sizeof(status), &len, NULL)) | ||
875 | { | ||
876 | fprintf(stderr, "FATAL: TAP driver ignored request to UP interface (DeviceIoControl call)\n"); | ||
877 | return FALSE; | ||
878 | } | ||
879 | |||
880 | /* Wait for the device to go UP, might take some time. */ | ||
881 | Sleep(TAP32_POSTUP_WAITTIME * 1000); | ||
882 | fprintf(stderr, "DEBUG: successfully set TAP device to UP\n"); | ||
883 | |||
884 | return TRUE; | ||
885 | } | ||
886 | |||
887 | |||
888 | /** | ||
889 | * Attempts to read off an input facility (tap or named pipe) in overlapped mode. | ||
890 | * | ||
891 | * 1. | ||
892 | * If the input facility is in IOSTATE_READY, it will issue a new read operation to the | ||
893 | * input handle. Then it goes into IOSTATE_QUEUED state. | ||
894 | * In case the read succeeded instantly the input facility enters 3. | ||
895 | * | ||
896 | * 2. | ||
897 | * If the input facility is in IOSTATE_QUEUED state, it will check if the queued read has finished already. | ||
898 | * If it has finished, go to state 3. | ||
899 | * If it has failed, set IOSTATE_FAILED | ||
900 | * | ||
901 | * 3. | ||
902 | * If the output facility is in state IOSTATE_READY, the read-buffer is copied to the output buffer. | ||
903 | * The input facility enters state IOSTATE_READY | ||
904 | * The output facility enters state IOSTATE_READY | ||
905 | * If the output facility is in state IOSTATE_QUEUED, the input facility enters IOSTATE_WAITING | ||
906 | * | ||
907 | * IOSTATE_WAITING is reset by the output facility, once it has completed. | ||
908 | * | ||
909 | * @param input_facility input named pipe or file to work with. | ||
910 | * @param output_facility output pipe or file to hand over data to. | ||
911 | * @return false if an event reset was impossible (OS error), else true | ||
912 | */ | ||
913 | static BOOL | ||
914 | attempt_read_tap(struct io_facility * input_facility, | ||
915 | struct io_facility * output_facility) | ||
916 | { | ||
917 | struct GNUNET_MessageHeader * hdr; | ||
918 | unsigned short size; | ||
919 | |||
920 | switch (input_facility->facility_state) | ||
921 | { | ||
922 | case IOSTATE_READY: | ||
923 | { | ||
924 | if (!ResetEvent(input_facility->overlapped.hEvent)) | ||
925 | { | ||
926 | return FALSE; | ||
927 | } | ||
928 | |||
929 | input_facility->buffer_size = 0; | ||
930 | |||
931 | /* Check how the task is handled */ | ||
932 | if (ReadFile(input_facility->handle, | ||
933 | input_facility->buffer, | ||
934 | sizeof(input_facility->buffer) - sizeof(struct GNUNET_MessageHeader), | ||
935 | &input_facility->buffer_size, | ||
936 | &input_facility->overlapped)) | ||
937 | { /* async event processed immediately*/ | ||
938 | /* reset event manually*/ | ||
939 | if (!SetEvent(input_facility->overlapped.hEvent)) | ||
940 | return FALSE; | ||
941 | |||
942 | fprintf(stderr, "DEBUG: tap read succeeded immediately\n"); | ||
943 | |||
944 | /* we successfully read something from the TAP and now need to | ||
945 | * send it our via STDOUT. Is that possible at the moment? */ | ||
946 | if ((IOSTATE_READY == output_facility->facility_state || | ||
947 | IOSTATE_WAITING == output_facility->facility_state) | ||
948 | && (0 < input_facility->buffer_size)) | ||
949 | { /* hand over this buffers content and apply message header for gnunet */ | ||
950 | hdr = (struct GNUNET_MessageHeader *)output_facility->buffer; | ||
951 | size = input_facility->buffer_size + sizeof(struct GNUNET_MessageHeader); | ||
952 | |||
953 | GNUNET_memcpy(output_facility->buffer + sizeof(struct GNUNET_MessageHeader), | ||
954 | input_facility->buffer, | ||
955 | input_facility->buffer_size); | ||
956 | |||
957 | output_facility->buffer_size = size; | ||
958 | hdr->size = htons(size); | ||
959 | hdr->type = htons(GNUNET_MESSAGE_TYPE_VPN_HELPER); | ||
960 | output_facility->facility_state = IOSTATE_READY; | ||
961 | } | ||
962 | else if (0 < input_facility->buffer_size) | ||
963 | /* If we have have read our buffer, wait for our write-partner*/ | ||
964 | input_facility->facility_state = IOSTATE_WAITING; | ||
965 | } | ||
966 | else /* operation was either queued or failed*/ | ||
967 | { | ||
968 | int err = GetLastError(); | ||
969 | if (ERROR_IO_PENDING == err) | ||
970 | { /* operation queued */ | ||
971 | input_facility->facility_state = IOSTATE_QUEUED; | ||
972 | } | ||
973 | else | ||
974 | { /* error occurred, let the rest of the elements finish */ | ||
975 | input_facility->path_open = FALSE; | ||
976 | input_facility->facility_state = IOSTATE_FAILED; | ||
977 | if (IOSTATE_WAITING == output_facility->facility_state) | ||
978 | output_facility->path_open = FALSE; | ||
979 | |||
980 | fprintf(stderr, "FATAL: Read from handle failed, allowing write to finish\n"); | ||
981 | } | ||
982 | } | ||
983 | } | ||
984 | return TRUE; | ||
985 | |||
986 | // We are queued and should check if the read has finished | ||
987 | case IOSTATE_QUEUED: | ||
988 | { | ||
989 | // there was an operation going on already, check if that has completed now. | ||
990 | |||
991 | if (GetOverlappedResult(input_facility->handle, | ||
992 | &input_facility->overlapped, | ||
993 | &input_facility->buffer_size, | ||
994 | FALSE)) | ||
995 | { /* successful return for a queued operation */ | ||
996 | if (!ResetEvent(input_facility->overlapped.hEvent)) | ||
997 | return FALSE; | ||
998 | |||
999 | fprintf(stderr, "DEBUG: tap read succeeded delayed\n"); | ||
1000 | |||
1001 | /* we successfully read something from the TAP and now need to | ||
1002 | * send it our via STDOUT. Is that possible at the moment? */ | ||
1003 | if ((IOSTATE_READY == output_facility->facility_state || | ||
1004 | IOSTATE_WAITING == output_facility->facility_state) | ||
1005 | && 0 < input_facility->buffer_size) | ||
1006 | { /* hand over this buffers content and apply message header for gnunet */ | ||
1007 | hdr = (struct GNUNET_MessageHeader *)output_facility->buffer; | ||
1008 | size = input_facility->buffer_size + sizeof(struct GNUNET_MessageHeader); | ||
1009 | |||
1010 | GNUNET_memcpy(output_facility->buffer + sizeof(struct GNUNET_MessageHeader), | ||
1011 | input_facility->buffer, | ||
1012 | input_facility->buffer_size); | ||
1013 | |||
1014 | output_facility->buffer_size = size; | ||
1015 | hdr->size = htons(size); | ||
1016 | hdr->type = htons(GNUNET_MESSAGE_TYPE_VPN_HELPER); | ||
1017 | output_facility->facility_state = IOSTATE_READY; | ||
1018 | input_facility->facility_state = IOSTATE_READY; | ||
1019 | } | ||
1020 | else if (0 < input_facility->buffer_size) | ||
1021 | { /* If we have have read our buffer, wait for our write-partner*/ | ||
1022 | input_facility->facility_state = IOSTATE_WAITING; | ||
1023 | // TODO: shall we attempt to fill our buffer or should we wait for our write-partner to finish? | ||
1024 | } | ||
1025 | } | ||
1026 | else | ||
1027 | { /* operation still pending/queued or failed? */ | ||
1028 | int err = GetLastError(); | ||
1029 | if ((ERROR_IO_INCOMPLETE != err) && (ERROR_IO_PENDING != err)) | ||
1030 | { /* error occurred, let the rest of the elements finish */ | ||
1031 | input_facility->path_open = FALSE; | ||
1032 | input_facility->facility_state = IOSTATE_FAILED; | ||
1033 | if (IOSTATE_WAITING == output_facility->facility_state) | ||
1034 | output_facility->path_open = FALSE; | ||
1035 | fprintf(stderr, "FATAL: Read from handle failed, allowing write to finish\n"); | ||
1036 | } | ||
1037 | } | ||
1038 | } | ||
1039 | return TRUE; | ||
1040 | |||
1041 | case IOSTATE_RESUME: | ||
1042 | hdr = (struct GNUNET_MessageHeader *)output_facility->buffer; | ||
1043 | size = input_facility->buffer_size + sizeof(struct GNUNET_MessageHeader); | ||
1044 | |||
1045 | GNUNET_memcpy(output_facility->buffer + sizeof(struct GNUNET_MessageHeader), | ||
1046 | input_facility->buffer, | ||
1047 | input_facility->buffer_size); | ||
1048 | |||
1049 | output_facility->buffer_size = size; | ||
1050 | hdr->size = htons(size); | ||
1051 | hdr->type = htons(GNUNET_MESSAGE_TYPE_VPN_HELPER); | ||
1052 | output_facility->facility_state = IOSTATE_READY; | ||
1053 | input_facility->facility_state = IOSTATE_READY; | ||
1054 | return TRUE; | ||
1055 | |||
1056 | default: | ||
1057 | return TRUE; | ||
1058 | } | ||
1059 | } | ||
1060 | |||
1061 | |||
1062 | /** | ||
1063 | * Attempts to read off an input facility (tap or named pipe) in overlapped mode. | ||
1064 | * | ||
1065 | * 1. | ||
1066 | * If the input facility is in IOSTATE_READY, it will issue a new read operation to the | ||
1067 | * input handle. Then it goes into IOSTATE_QUEUED state. | ||
1068 | * In case the read succeeded instantly the input facility enters 3. | ||
1069 | * | ||
1070 | * 2. | ||
1071 | * If the input facility is in IOSTATE_QUEUED state, it will check if the queued read has finished already. | ||
1072 | * If it has finished, go to state 3. | ||
1073 | * If it has failed, set IOSTATE_FAILED | ||
1074 | * | ||
1075 | * 3. | ||
1076 | * If the facility is finished with ready | ||
1077 | * The read-buffer is copied to the output buffer, except for the GNUNET_MessageHeader. | ||
1078 | * The input facility enters state IOSTATE_READY | ||
1079 | * The output facility enters state IOSTATE_READY | ||
1080 | * If the output facility is in state IOSTATE_QUEUED, the input facility enters IOSTATE_WAITING | ||
1081 | * | ||
1082 | * IOSTATE_WAITING is reset by the output facility, once it has completed. | ||
1083 | * | ||
1084 | * @param input_facility input named pipe or file to work with. | ||
1085 | * @param output_facility output pipe or file to hand over data to. | ||
1086 | * @return false if an event reset was impossible (OS error), else true | ||
1087 | */ | ||
1088 | static BOOL | ||
1089 | attempt_read_stdin(struct io_facility * input_facility, | ||
1090 | struct io_facility * output_facility) | ||
1091 | { | ||
1092 | struct GNUNET_MessageHeader * hdr; | ||
1093 | |||
1094 | switch (input_facility->facility_state) | ||
1095 | { | ||
1096 | case IOSTATE_READY: | ||
1097 | { | ||
1098 | input_facility->buffer_size = 0; | ||
1099 | |||
1100 | partial_read_iostate_ready: | ||
1101 | if (!ResetEvent(input_facility->overlapped.hEvent)) | ||
1102 | return FALSE; | ||
1103 | |||
1104 | /* Check how the task is handled */ | ||
1105 | if (ReadFile(input_facility->handle, | ||
1106 | input_facility->buffer + input_facility->buffer_size, | ||
1107 | sizeof(input_facility->buffer) - input_facility->buffer_size, | ||
1108 | &input_facility->buffer_size_processed, | ||
1109 | &input_facility->overlapped)) | ||
1110 | { /* async event processed immediately*/ | ||
1111 | hdr = (struct GNUNET_MessageHeader *)input_facility->buffer; | ||
1112 | |||
1113 | /* reset event manually*/ | ||
1114 | if (!SetEvent(input_facility->overlapped.hEvent)) | ||
1115 | return FALSE; | ||
1116 | |||
1117 | fprintf(stderr, "DEBUG: stdin read succeeded immediately\n"); | ||
1118 | input_facility->buffer_size += input_facility->buffer_size_processed; | ||
1119 | |||
1120 | if (ntohs(hdr->type) != GNUNET_MESSAGE_TYPE_VPN_HELPER || | ||
1121 | ntohs(hdr->size) > sizeof(input_facility->buffer)) | ||
1122 | { | ||
1123 | fprintf(stderr, "WARNING: Protocol violation, got GNUnet Message type %h, size %h\n", ntohs(hdr->type), ntohs(hdr->size)); | ||
1124 | input_facility->facility_state = IOSTATE_READY; | ||
1125 | return TRUE; | ||
1126 | } | ||
1127 | /* we got the a part of a packet */ | ||
1128 | if (ntohs(hdr->size) > input_facility->buffer_size) | ||
1129 | goto partial_read_iostate_ready; | ||
1130 | |||
1131 | /* have we read more than 0 bytes of payload? (sizeread > header)*/ | ||
1132 | if (input_facility->buffer_size > sizeof(struct GNUNET_MessageHeader) && | ||
1133 | ((IOSTATE_READY == output_facility->facility_state) || | ||
1134 | (IOSTATE_WAITING == output_facility->facility_state))) | ||
1135 | { /* we successfully read something from the TAP and now need to | ||
1136 | * send it our via STDOUT. Is that possible at the moment? */ | ||
1137 | /* hand over this buffers content and strip gnunet message header */ | ||
1138 | GNUNET_memcpy(output_facility->buffer, | ||
1139 | input_facility->buffer + sizeof(struct GNUNET_MessageHeader), | ||
1140 | input_facility->buffer_size - sizeof(struct GNUNET_MessageHeader)); | ||
1141 | output_facility->buffer_size = input_facility->buffer_size - sizeof(struct GNUNET_MessageHeader); | ||
1142 | output_facility->facility_state = IOSTATE_READY; | ||
1143 | input_facility->facility_state = IOSTATE_READY; | ||
1144 | } | ||
1145 | else if (input_facility->buffer_size > sizeof(struct GNUNET_MessageHeader)) | ||
1146 | /* If we have have read our buffer, wait for our write-partner*/ | ||
1147 | input_facility->facility_state = IOSTATE_WAITING; | ||
1148 | else /* we read nothing */ | ||
1149 | input_facility->facility_state = IOSTATE_READY; | ||
1150 | } | ||
1151 | else /* operation was either queued or failed*/ | ||
1152 | { | ||
1153 | int err = GetLastError(); | ||
1154 | if (ERROR_IO_PENDING == err) /* operation queued */ | ||
1155 | input_facility->facility_state = IOSTATE_QUEUED; | ||
1156 | else | ||
1157 | { /* error occurred, let the rest of the elements finish */ | ||
1158 | input_facility->path_open = FALSE; | ||
1159 | input_facility->facility_state = IOSTATE_FAILED; | ||
1160 | if (IOSTATE_WAITING == output_facility->facility_state) | ||
1161 | output_facility->path_open = FALSE; | ||
1162 | |||
1163 | fprintf(stderr, "FATAL: Read from handle failed, allowing write to finish\n"); | ||
1164 | } | ||
1165 | } | ||
1166 | } | ||
1167 | return TRUE; | ||
1168 | |||
1169 | // We are queued and should check if the read has finished | ||
1170 | case IOSTATE_QUEUED: | ||
1171 | { | ||
1172 | // there was an operation going on already, check if that has completed now. | ||
1173 | if (GetOverlappedResult(input_facility->handle, | ||
1174 | &input_facility->overlapped, | ||
1175 | &input_facility->buffer_size_processed, | ||
1176 | FALSE)) | ||
1177 | { /* successful return for a queued operation */ | ||
1178 | hdr = (struct GNUNET_MessageHeader *)input_facility->buffer; | ||
1179 | |||
1180 | if (!ResetEvent(input_facility->overlapped.hEvent)) | ||
1181 | return FALSE; | ||
1182 | |||
1183 | fprintf(stderr, "DEBUG: stdin read succeeded delayed\n"); | ||
1184 | input_facility->buffer_size += input_facility->buffer_size_processed; | ||
1185 | |||
1186 | if ((ntohs(hdr->type) != GNUNET_MESSAGE_TYPE_VPN_HELPER) || | ||
1187 | (ntohs(hdr->size) > sizeof(input_facility->buffer))) | ||
1188 | { | ||
1189 | fprintf(stderr, "WARNING: Protocol violation, got GNUnet Message type %h, size %h\n", ntohs(hdr->type), ntohs(hdr->size)); | ||
1190 | input_facility->facility_state = IOSTATE_READY; | ||
1191 | return TRUE; | ||
1192 | } | ||
1193 | /* we got the a part of a packet */ | ||
1194 | if (ntohs(hdr->size) > input_facility->buffer_size) | ||
1195 | ; | ||
1196 | goto partial_read_iostate_ready; | ||
1197 | |||
1198 | /* we successfully read something from the TAP and now need to | ||
1199 | * send it our via STDOUT. Is that possible at the moment? */ | ||
1200 | if ((IOSTATE_READY == output_facility->facility_state || | ||
1201 | IOSTATE_WAITING == output_facility->facility_state) | ||
1202 | && input_facility->buffer_size > sizeof(struct GNUNET_MessageHeader)) | ||
1203 | { /* hand over this buffers content and strip gnunet message header */ | ||
1204 | GNUNET_memcpy(output_facility->buffer, | ||
1205 | input_facility->buffer + sizeof(struct GNUNET_MessageHeader), | ||
1206 | input_facility->buffer_size - sizeof(struct GNUNET_MessageHeader)); | ||
1207 | output_facility->buffer_size = input_facility->buffer_size - sizeof(struct GNUNET_MessageHeader); | ||
1208 | output_facility->facility_state = IOSTATE_READY; | ||
1209 | input_facility->facility_state = IOSTATE_READY; | ||
1210 | } | ||
1211 | else if (input_facility->buffer_size > sizeof(struct GNUNET_MessageHeader)) | ||
1212 | input_facility->facility_state = IOSTATE_WAITING; | ||
1213 | else | ||
1214 | input_facility->facility_state = IOSTATE_READY; | ||
1215 | } | ||
1216 | else | ||
1217 | { /* operation still pending/queued or failed? */ | ||
1218 | int err = GetLastError(); | ||
1219 | if ((ERROR_IO_INCOMPLETE != err) && (ERROR_IO_PENDING != err)) | ||
1220 | { /* error occurred, let the rest of the elements finish */ | ||
1221 | input_facility->path_open = FALSE; | ||
1222 | input_facility->facility_state = IOSTATE_FAILED; | ||
1223 | if (IOSTATE_WAITING == output_facility->facility_state) | ||
1224 | output_facility->path_open = FALSE; | ||
1225 | fprintf(stderr, "FATAL: Read from handle failed, allowing write to finish\n"); | ||
1226 | } | ||
1227 | } | ||
1228 | } | ||
1229 | return TRUE; | ||
1230 | |||
1231 | case IOSTATE_RESUME: /* Our buffer was filled already but our write facility was busy. */ | ||
1232 | GNUNET_memcpy(output_facility->buffer, | ||
1233 | input_facility->buffer + sizeof(struct GNUNET_MessageHeader), | ||
1234 | input_facility->buffer_size - sizeof(struct GNUNET_MessageHeader)); | ||
1235 | output_facility->buffer_size = input_facility->buffer_size - sizeof(struct GNUNET_MessageHeader); | ||
1236 | output_facility->facility_state = IOSTATE_READY; | ||
1237 | input_facility->facility_state = IOSTATE_READY; | ||
1238 | return TRUE; | ||
1239 | |||
1240 | default: | ||
1241 | return TRUE; | ||
1242 | } | ||
1243 | } | ||
1244 | |||
1245 | |||
1246 | /** | ||
1247 | * Attempts to write to an output facility (tap or named pipe) in overlapped mode. | ||
1248 | * | ||
1249 | * TODO: high level description | ||
1250 | * | ||
1251 | * @param output_facility output pipe or file to hand over data to. | ||
1252 | * @param input_facility input named pipe or file to work with. | ||
1253 | * @return false if an event reset was impossible (OS error), else true | ||
1254 | */ | ||
1255 | static BOOL | ||
1256 | attempt_write(struct io_facility * output_facility, | ||
1257 | struct io_facility * input_facility) | ||
1258 | { | ||
1259 | switch (output_facility->facility_state) | ||
1260 | { | ||
1261 | case IOSTATE_READY: | ||
1262 | output_facility->buffer_size_written = 0; | ||
1263 | |||
1264 | continue_partial_write: | ||
1265 | if (!ResetEvent(output_facility->overlapped.hEvent)) | ||
1266 | return FALSE; | ||
1267 | |||
1268 | /* Check how the task was handled */ | ||
1269 | if (WriteFile(output_facility->handle, | ||
1270 | output_facility->buffer + output_facility->buffer_size_written, | ||
1271 | output_facility->buffer_size - output_facility->buffer_size_written, | ||
1272 | &output_facility->buffer_size_processed, | ||
1273 | &output_facility->overlapped)) | ||
1274 | {/* async event processed immediately*/ | ||
1275 | fprintf(stderr, "DEBUG: write succeeded immediately\n"); | ||
1276 | output_facility->buffer_size_written += output_facility->buffer_size_processed; | ||
1277 | |||
1278 | /* reset event manually*/ | ||
1279 | if (!SetEvent(output_facility->overlapped.hEvent)) | ||
1280 | return FALSE; | ||
1281 | |||
1282 | /* partial write */ | ||
1283 | if (output_facility->buffer_size_written < output_facility->buffer_size) | ||
1284 | goto continue_partial_write; | ||
1285 | |||
1286 | /* we are now waiting for our buffer to be filled*/ | ||
1287 | output_facility->facility_state = IOSTATE_WAITING; | ||
1288 | |||
1289 | /* we successfully wrote something and now need to reset our reader */ | ||
1290 | if (IOSTATE_WAITING == input_facility->facility_state) | ||
1291 | input_facility->facility_state = IOSTATE_RESUME; | ||
1292 | else if (IOSTATE_FAILED == input_facility->facility_state) | ||
1293 | output_facility->path_open = FALSE; | ||
1294 | } | ||
1295 | else /* operation was either queued or failed*/ | ||
1296 | { | ||
1297 | int err = GetLastError(); | ||
1298 | if (ERROR_IO_PENDING == err) | ||
1299 | { /* operation queued */ | ||
1300 | output_facility->facility_state = IOSTATE_QUEUED; | ||
1301 | } | ||
1302 | else | ||
1303 | { /* error occurred, close this path */ | ||
1304 | output_facility->path_open = FALSE; | ||
1305 | output_facility->facility_state = IOSTATE_FAILED; | ||
1306 | fprintf(stderr, "FATAL: Write to handle failed, exiting\n"); | ||
1307 | } | ||
1308 | } | ||
1309 | return TRUE; | ||
1310 | |||
1311 | case IOSTATE_QUEUED: | ||
1312 | // there was an operation going on already, check if that has completed now. | ||
1313 | |||
1314 | if (GetOverlappedResult(output_facility->handle, | ||
1315 | &output_facility->overlapped, | ||
1316 | &output_facility->buffer_size_processed, | ||
1317 | FALSE)) | ||
1318 | {/* successful return for a queued operation */ | ||
1319 | if (!ResetEvent(output_facility->overlapped.hEvent)) | ||
1320 | return FALSE; | ||
1321 | |||
1322 | fprintf(stderr, "DEBUG: write succeeded delayed\n"); | ||
1323 | output_facility->buffer_size_written += output_facility->buffer_size_processed; | ||
1324 | |||
1325 | /* partial write */ | ||
1326 | if (output_facility->buffer_size_written < output_facility->buffer_size) | ||
1327 | goto continue_partial_write; | ||
1328 | |||
1329 | /* we are now waiting for our buffer to be filled*/ | ||
1330 | output_facility->facility_state = IOSTATE_WAITING; | ||
1331 | |||
1332 | /* we successfully wrote something and now need to reset our reader */ | ||
1333 | if (IOSTATE_WAITING == input_facility->facility_state) | ||
1334 | input_facility->facility_state = IOSTATE_RESUME; | ||
1335 | else if (IOSTATE_FAILED == input_facility->facility_state) | ||
1336 | output_facility->path_open = FALSE; | ||
1337 | } | ||
1338 | else | ||
1339 | { /* operation still pending/queued or failed? */ | ||
1340 | int err = GetLastError(); | ||
1341 | if ((ERROR_IO_INCOMPLETE != err) && (ERROR_IO_PENDING != err)) | ||
1342 | { /* error occurred, close this path */ | ||
1343 | output_facility->path_open = FALSE; | ||
1344 | output_facility->facility_state = IOSTATE_FAILED; | ||
1345 | fprintf(stderr, "FATAL: Write to handle failed, exiting\n"); | ||
1346 | } | ||
1347 | } | ||
1348 | |||
1349 | default: | ||
1350 | return TRUE; | ||
1351 | } | ||
1352 | } | ||
1353 | |||
1354 | |||
1355 | /** | ||
1356 | * Initialize a overlapped structure | ||
1357 | * | ||
1358 | * @param elem the element to initilize | ||
1359 | * @param initial_state the initial state for this instance | ||
1360 | * @param signaled if the hEvent created should default to signaled or not | ||
1361 | * @return true on success, else false | ||
1362 | */ | ||
1363 | static BOOL | ||
1364 | initialize_io_facility(struct io_facility * elem, | ||
1365 | int initial_state, | ||
1366 | BOOL signaled) | ||
1367 | { | ||
1368 | elem->path_open = TRUE; | ||
1369 | elem->handle = INVALID_HANDLE_VALUE; | ||
1370 | elem->facility_state = initial_state; | ||
1371 | elem->buffer_size = 0; | ||
1372 | elem->overlapped.hEvent = CreateEvent(NULL, TRUE, signaled, NULL); | ||
1373 | if (NULL == elem->overlapped.hEvent) | ||
1374 | return FALSE; | ||
1375 | |||
1376 | return TRUE; | ||
1377 | } | ||
1378 | |||
1379 | |||
1380 | /** | ||
1381 | * Start forwarding to and from the tunnel. | ||
1382 | * | ||
1383 | * @param tap_handle device handle for interacting with the Virtual interface | ||
1384 | */ | ||
1385 | static void | ||
1386 | run(HANDLE tap_handle) | ||
1387 | { | ||
1388 | /* IO-Facility for reading from our virtual interface */ | ||
1389 | struct io_facility tap_read; | ||
1390 | /* IO-Facility for writing to our virtual interface */ | ||
1391 | struct io_facility tap_write; | ||
1392 | /* IO-Facility for reading from stdin */ | ||
1393 | struct io_facility std_in; | ||
1394 | /* IO-Facility for writing to stdout */ | ||
1395 | struct io_facility std_out; | ||
1396 | |||
1397 | HANDLE parent_std_in_handle = GetStdHandle(STD_INPUT_HANDLE); | ||
1398 | HANDLE parent_std_out_handle = GetStdHandle(STD_OUTPUT_HANDLE); | ||
1399 | |||
1400 | /* tun up: */ | ||
1401 | /* we do this HERE and not beforehand (in init_tun()), in contrast to openvpn | ||
1402 | * to remove the need to flush the arp cache, handle DHCP and wrong IPs. | ||
1403 | * | ||
1404 | * DHCP and such are all features we will never use in gnunet afaik. | ||
1405 | * But for openvpn those are essential. | ||
1406 | */ | ||
1407 | if ((privilege_testing) || (!tun_up(tap_handle))) | ||
1408 | goto teardown_final; | ||
1409 | |||
1410 | /* Initialize our overlapped IO structures*/ | ||
1411 | if (!(initialize_io_facility(&tap_read, IOSTATE_READY, FALSE) | ||
1412 | && initialize_io_facility(&tap_write, IOSTATE_WAITING, TRUE) | ||
1413 | && initialize_io_facility(&std_in, IOSTATE_READY, FALSE) | ||
1414 | && initialize_io_facility(&std_out, IOSTATE_WAITING, TRUE))) | ||
1415 | goto teardown_final; | ||
1416 | |||
1417 | /* Handles for STDIN and STDOUT */ | ||
1418 | tap_read.handle = tap_handle; | ||
1419 | tap_write.handle = tap_handle; | ||
1420 | |||
1421 | #ifdef DEBUG_TO_CONSOLE | ||
1422 | /* Debug output to console STDIN/STDOUT*/ | ||
1423 | std_in.handle = parent_std_in_handle; | ||
1424 | std_out.handle = parent_std_out_handle; | ||
1425 | #else | ||
1426 | fprintf(stderr, "DEBUG: reopening stdin/out for overlapped IO\n"); | ||
1427 | /* | ||
1428 | * Find out the types of our handles. | ||
1429 | * This part is a problem, because in windows we need to handle files, | ||
1430 | * pipes and the console differently. | ||
1431 | */ | ||
1432 | if ((FILE_TYPE_PIPE != GetFileType(parent_std_in_handle)) || | ||
1433 | (FILE_TYPE_PIPE != GetFileType(parent_std_out_handle))) | ||
1434 | { | ||
1435 | fprintf(stderr, "ERROR: stdin/stdout must be named pipes\n"); | ||
1436 | goto teardown; | ||
1437 | } | ||
1438 | |||
1439 | std_in.handle = ReOpenFile(parent_std_in_handle, | ||
1440 | GENERIC_READ, | ||
1441 | FILE_SHARE_WRITE | FILE_SHARE_READ, | ||
1442 | FILE_FLAG_OVERLAPPED); | ||
1443 | |||
1444 | if (INVALID_HANDLE_VALUE == std_in.handle) | ||
1445 | { | ||
1446 | fprintf(stderr, "FATAL: Could not reopen stdin for in overlapped mode, has to be a named pipe\n"); | ||
1447 | goto teardown; | ||
1448 | } | ||
1449 | |||
1450 | std_out.handle = ReOpenFile(parent_std_out_handle, | ||
1451 | GENERIC_WRITE, | ||
1452 | FILE_SHARE_READ, | ||
1453 | FILE_FLAG_OVERLAPPED); | ||
1454 | |||
1455 | if (INVALID_HANDLE_VALUE == std_out.handle) | ||
1456 | { | ||
1457 | fprintf(stderr, "FATAL: Could not reopen stdout for in overlapped mode, has to be a named pipe\n"); | ||
1458 | goto teardown; | ||
1459 | } | ||
1460 | #endif | ||
1461 | |||
1462 | fprintf(stderr, "DEBUG: mainloop has begun\n"); | ||
1463 | |||
1464 | while (std_out.path_open || tap_write.path_open) | ||
1465 | { | ||
1466 | /* perform READ from stdin if possible */ | ||
1467 | if (std_in.path_open && (!attempt_read_stdin(&std_in, &tap_write))) | ||
1468 | break; | ||
1469 | |||
1470 | /* perform READ from tap if possible */ | ||
1471 | if (tap_read.path_open && (!attempt_read_tap(&tap_read, &std_out))) | ||
1472 | break; | ||
1473 | |||
1474 | /* perform WRITE to tap if possible */ | ||
1475 | if (tap_write.path_open && (!attempt_write(&tap_write, &std_in))) | ||
1476 | break; | ||
1477 | |||
1478 | /* perform WRITE to STDOUT if possible */ | ||
1479 | if (std_out.path_open && (!attempt_write(&std_out, &tap_read))) | ||
1480 | break; | ||
1481 | } | ||
1482 | fprintf(stderr, "DEBUG: teardown initiated\n"); | ||
1483 | |||
1484 | teardown: | ||
1485 | |||
1486 | CancelIo(tap_handle); | ||
1487 | CancelIo(std_in.handle); | ||
1488 | CancelIo(std_out.handle); | ||
1489 | |||
1490 | teardown_final: | ||
1491 | |||
1492 | CloseHandle(tap_handle); | ||
1493 | } | ||
1494 | |||
1495 | |||
1496 | /** | ||
1497 | * Open VPN tunnel interface. | ||
1498 | * | ||
1499 | * @param argc must be 6 | ||
1500 | * @param argv 0: binary name ("gnunet-helper-exit") | ||
1501 | * 1: tunnel interface name ("gnunet-exit") | ||
1502 | * 2: IPv4 "physical" interface name ("eth0"), or "-" to not do IPv4 NAT | ||
1503 | * 3: IPv6 address ("::1"), or "-" to skip IPv6 | ||
1504 | * 4: IPv6 netmask length in bits ("64") [ignored if #4 is "-"] | ||
1505 | * 5: IPv4 address ("1.2.3.4"), or "-" to skip IPv4 | ||
1506 | * 6: IPv4 netmask ("255.255.0.0") [ignored if #4 is "-"] | ||
1507 | */ | ||
1508 | int | ||
1509 | main(int argc, char **argv) | ||
1510 | { | ||
1511 | char hwid[LINE_LEN]; | ||
1512 | HANDLE handle; | ||
1513 | int global_ret = 1; | ||
1514 | int local_ret = EINVAL; | ||
1515 | BOOL have_ip4 = FALSE; | ||
1516 | BOOL have_ip6 = FALSE; | ||
1517 | BOOL have_nat44 = FALSE; | ||
1518 | |||
1519 | if ((1 < argc) && (0 != strcmp(argv[1], "-d"))) | ||
1520 | { | ||
1521 | privilege_testing = TRUE; | ||
1522 | fprintf(stderr, | ||
1523 | "%s", | ||
1524 | "DEBUG: Running binary in privilege testing mode."); | ||
1525 | argv++; | ||
1526 | argc--; | ||
1527 | } | ||
1528 | |||
1529 | if (6 != argc) | ||
1530 | { | ||
1531 | fprintf(stderr, | ||
1532 | "%s", | ||
1533 | "FATAL: must supply 6 arguments\nUsage:\ngnunet-helper-exit [-d] <if name prefix> <uplink-interface name> <address6 or \"-\"> <netbits6> <address4 or \"-\"> <netmask4>\n"); | ||
1534 | return 1; | ||
1535 | } | ||
1536 | |||
1537 | GNUNET_strlcpy(hwid, argv[1], sizeof(hwid)); | ||
1538 | |||
1539 | /* | ||
1540 | * We use our PID for finding/resolving the control-panel name of our virtual | ||
1541 | * device. PIDs are (of course) unique at runtime, thus we can safely use it | ||
1542 | * as additional hardware-id for our device. | ||
1543 | */ | ||
1544 | snprintf(secondary_hwid, LINE_LEN / 2, "%s-%d", | ||
1545 | hwid, | ||
1546 | _getpid()); | ||
1547 | |||
1548 | if (INVALID_HANDLE_VALUE == (handle = init_tun())) | ||
1549 | { | ||
1550 | fprintf(stderr, "FATAL: could not initialize virtual-interface %s with IPv6 %s/%s and IPv4 %s/%s\n", | ||
1551 | hwid, | ||
1552 | argv[3], | ||
1553 | argv[4], | ||
1554 | argv[5], | ||
1555 | argv[6]); | ||
1556 | global_ret = -1; | ||
1557 | goto cleanup; | ||
1558 | } | ||
1559 | |||
1560 | fprintf(stderr, "DEBUG: Setting IPs, if needed\n"); | ||
1561 | if (0 != strcmp(argv[3], "-")) | ||
1562 | { | ||
1563 | char command[LINE_LEN]; | ||
1564 | const char *address = argv[3]; | ||
1565 | long prefix_len = atol(argv[4]); | ||
1566 | |||
1567 | if ((prefix_len < 1) || (prefix_len > 127)) | ||
1568 | { | ||
1569 | fprintf(stderr, "FATAL: ipv6 prefix_len out of range\n"); | ||
1570 | global_ret = -1; | ||
1571 | goto cleanup; | ||
1572 | } | ||
1573 | |||
1574 | fprintf(stderr, "DEBUG: Setting IP6 address: %s/%d\n", address, prefix_len); | ||
1575 | if (0 != (global_ret = set_address6(address, prefix_len))) | ||
1576 | goto cleanup; | ||
1577 | |||
1578 | have_ip6 = TRUE; | ||
1579 | |||
1580 | /* install our the windows NAT module*/ | ||
1581 | fprintf(stderr, "DEBUG: Setting IPv6 Forwarding for internal and external interface.\n"); | ||
1582 | /* outside interface (maybe that's already set) */ | ||
1583 | snprintf(command, LINE_LEN, | ||
1584 | "netsh interface ipv6 set interface interface=\"%s\" metric=1 forwarding=enabled store=active", | ||
1585 | argv[2]); | ||
1586 | local_ret = execute_shellcommand(command); | ||
1587 | if (0 != local_ret) | ||
1588 | { | ||
1589 | fprintf(stderr, "FATAL: Could not enable forwarding via netsh: %s\n", strerror(local_ret)); | ||
1590 | goto cleanup; | ||
1591 | } | ||
1592 | /* internal interface */ | ||
1593 | snprintf(command, LINE_LEN, | ||
1594 | "netsh interface ipv6 set interface interface=\"%s\" metric=1 forwarding=enabled advertise=enabled store=active", | ||
1595 | device_visible_name); | ||
1596 | local_ret = execute_shellcommand(command); | ||
1597 | if (0 != local_ret) | ||
1598 | { | ||
1599 | fprintf(stderr, "FATAL: Could not enable forwarding via netsh: %s\n", strerror(local_ret)); | ||
1600 | goto cleanup; | ||
1601 | } | ||
1602 | /* we can keep IPv6 forwarding around, as all interfaces have | ||
1603 | * their forwarding mode reset to false at bootup. */ | ||
1604 | } | ||
1605 | |||
1606 | if (0 != strcmp(argv[5], "-")) | ||
1607 | { | ||
1608 | const char *address = argv[5]; | ||
1609 | const char *mask = argv[6]; | ||
1610 | |||
1611 | fprintf(stderr, "DEBUG: Setting IP4 address: %s/%s\n", address, mask); | ||
1612 | if (0 != (global_ret = set_address4(address, mask))) | ||
1613 | goto cleanup; | ||
1614 | |||
1615 | // setup NAPT, if possible | ||
1616 | /* MS has REMOVED the routing/nat capabilities from Vista+, thus | ||
1617 | * we can not setup NAT like in XP or on the server. Actually the | ||
1618 | * the only feasible solution seems to be to use | ||
1619 | * Internet Connection Sharing, which introduces a horde of problems | ||
1620 | * such as sending out rogue-RAs on the external interface in an ipv6 | ||
1621 | * network. | ||
1622 | * Thus, below stuff ONLY works on | ||
1623 | * WinXP SP3 | ||
1624 | * Win Server 2003 SP1+ | ||
1625 | * Win Server 2008 | ||
1626 | * ... | ||
1627 | */ | ||
1628 | have_ip4 = TRUE; | ||
1629 | if (0 != strcmp(argv[2], "-")) | ||
1630 | { | ||
1631 | char command[LINE_LEN]; | ||
1632 | |||
1633 | /* install our the windows NAT module*/ | ||
1634 | fprintf(stderr, "DEBUG: Adding NAPT/Masquerading between external IF %s and mine.\n", argv[2]); | ||
1635 | local_ret = execute_shellcommand("netsh routing ip nat install"); | ||
1636 | if (0 != local_ret) | ||
1637 | { | ||
1638 | fprintf(stderr, "FATAL: Could not install NAPT support via Netsh: %s\n", strerror(local_ret)); | ||
1639 | goto cleanup; | ||
1640 | } | ||
1641 | /* external IF */ | ||
1642 | snprintf(command, LINE_LEN, | ||
1643 | "netsh routing ip nat add interface \"%s\" full", /*full = NAPT (addr+port)*/ | ||
1644 | argv[2]); | ||
1645 | local_ret = execute_shellcommand(command); | ||
1646 | if (0 != local_ret) | ||
1647 | { | ||
1648 | fprintf(stderr, "FATAL: IPv4-NAPT on external interface failed: %s\n", strerror(local_ret)); | ||
1649 | goto cleanup; | ||
1650 | } | ||
1651 | /* private/internal/virtual IF */ | ||
1652 | snprintf(command, LINE_LEN, | ||
1653 | "netsh routing ip nat add interface \"%s\" private", | ||
1654 | device_visible_name); | ||
1655 | local_ret = execute_shellcommand(command); | ||
1656 | if (0 != local_ret) | ||
1657 | { | ||
1658 | fprintf(stderr, "FATAL: IPv4-NAPT on internal interface failed: %s\n", strerror(local_ret)); | ||
1659 | goto cleanup; | ||
1660 | |||
1661 | have_nat44 = TRUE; | ||
1662 | } | ||
1663 | } | ||
1664 | } | ||
1665 | |||
1666 | run(handle); | ||
1667 | cleanup: | ||
1668 | |||
1669 | if (have_ip4) | ||
1670 | { | ||
1671 | const char *address = argv[5]; | ||
1672 | if (have_nat44) | ||
1673 | { | ||
1674 | char command[LINE_LEN]; | ||
1675 | fprintf(stderr, "DEBUG: removing IP4 NAPT from virtual interface \n"); | ||
1676 | snprintf(command, LINE_LEN, | ||
1677 | "netsh routing ip nat del interface \"%s\"", | ||
1678 | device_visible_name); | ||
1679 | local_ret = execute_shellcommand(command); | ||
1680 | if (0 != local_ret) | ||
1681 | fprintf(stderr, "WARNING: Could not remove IPv4-NAPT from internal interface, hopefully this will have no effect in future runs: %s\n", strerror(local_ret)); | ||
1682 | } | ||
1683 | |||
1684 | fprintf(stderr, "DEBUG: Removing IP4 address\n"); | ||
1685 | remove_address4(address); | ||
1686 | } | ||
1687 | if (have_ip6) | ||
1688 | { | ||
1689 | const char *address = argv[3]; | ||
1690 | fprintf(stderr, "DEBUG: Removing IP6 address\n"); | ||
1691 | remove_address6(address); | ||
1692 | } | ||
1693 | |||
1694 | fprintf(stderr, "DEBUG: removing interface\n"); | ||
1695 | remove_interface(); | ||
1696 | fprintf(stderr, "DEBUG: graceful exit completed\n"); | ||
1697 | |||
1698 | return global_ret; | ||
1699 | } | ||