aboutsummaryrefslogtreecommitdiff
path: root/src/exit
diff options
context:
space:
mode:
authorng0 <ng0@n0.is>2019-09-10 16:59:32 +0000
committerng0 <ng0@n0.is>2019-09-10 16:59:32 +0000
commit04b6df21cd281e8cd540139f8d9ae85defc1961c (patch)
tree6357199445df8d5c0c631bc8f10aef838b1f9f1e /src/exit
parent483b0139a218a5f8a8311bda3eb23bcd88f57688 (diff)
downloadgnunet-04b6df21cd281e8cd540139f8d9ae85defc1961c.tar.gz
gnunet-04b6df21cd281e8cd540139f8d9ae85defc1961c.zip
remove CYGWIN codeblocks, drop vendored Windows openvpn, drop win32 specific files.
configures and builds okay. testsuite wasn't checked, will be checked. diff including the plibc removal is now around 14370 lines of code less.
Diffstat (limited to 'src/exit')
-rw-r--r--src/exit/Makefile.am19
-rw-r--r--src/exit/gnunet-helper-exit-windows.c1699
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
2AM_CPPFLAGS = -I$(top_srcdir)/src/include 2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3 3
4if MINGW
5 WINFLAGS = -Wl,--no-undefined,--export-all-symbols
6 EXITBIN = gnunet-helper-exit
7endif
8
9if USE_COVERAGE 4if USE_COVERAGE
10 AM_CFLAGS = --coverage -O0 5 AM_CFLAGS = --coverage -O0
11endif 6endif
@@ -32,20 +27,10 @@ libexec_PROGRAMS = \
32 gnunet-daemon-exit \ 27 gnunet-daemon-exit \
33 $(EXITBIN) 28 $(EXITBIN)
34 29
35if MINGW
36 gnunet_helper_exit_LDFLAGS = \
37 -no-undefined -Wl,--export-all-symbols
38 30
39 gnunet_helper_exit_LDADD = \ 31gnunet_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
45else
46 gnunet_helper_exit_SOURCES = \
47 gnunet-helper-exit.c
48endif
49gnunet_daemon_exit_SOURCES = \ 34gnunet_daemon_exit_SOURCES = \
50 gnunet-daemon-exit.c exit.h 35 gnunet-daemon-exit.c exit.h
51gnunet_daemon_exit_LDADD = \ 36gnunet_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 */
77static 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 */
131static 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 */
137static 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 */
148static HDEVINFO DeviceInfo = INVALID_HANDLE_VALUE;
149
150/**
151 * Registry Key we hand over to windows to spawn a new virtual interface
152 */
153static 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 */
159static char device_guid[256];
160
161
162/**
163 * Possible states of an IO facility.
164 */
165enum 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 */
197struct 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 */
242WINBASEAPI HANDLE WINAPI ReOpenFile(HANDLE, DWORD, DWORD, DWORD);
243
244/**
245 * IsWow64Process definition for our is_win64, as this is a kernel function
246 */
247typedef 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 */
262size_t
263GNUNET_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 */
281BOOL
282is_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 */
307static int
308execute_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 */
333static int
334set_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 */
375static void
376remove_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 */
405static int
406set_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 */
450static void
451remove_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 */
480static BOOL
481setup_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 */
588static BOOL
589remove_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 */
631static BOOL
632resolve_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
754cleanup:
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 */
772static BOOL
773check_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 */
806static HANDLE
807init_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 */
866static BOOL
867tun_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 */
913static BOOL
914attempt_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 */
1088static BOOL
1089attempt_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
1100partial_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 */
1255static BOOL
1256attempt_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
1264continue_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 */
1363static BOOL
1364initialize_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 */
1385static void
1386run(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
1484teardown:
1485
1486 CancelIo(tap_handle);
1487 CancelIo(std_in.handle);
1488 CancelIo(std_out.handle);
1489
1490teardown_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 */
1508int
1509main(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);
1667cleanup:
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}