aboutsummaryrefslogtreecommitdiff
path: root/src/vpn
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/vpn
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/vpn')
-rw-r--r--src/vpn/Makefile.am20
-rw-r--r--src/vpn/gnunet-helper-vpn-windows.c1610
2 files changed, 2 insertions, 1628 deletions
diff --git a/src/vpn/Makefile.am b/src/vpn/Makefile.am
index d1f74d35b..3ad9b1a54 100644
--- a/src/vpn/Makefile.am
+++ b/src/vpn/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 -I$(top_builddir)/src/include 2AM_CPPFLAGS = -I$(top_srcdir)/src/include -I$(top_builddir)/src/include
3 3
4if MINGW
5 WINFLAGS = -Wl,--no-undefined,--export-all-symbols
6 VPNBIN = gnunet-helper-vpn
7endif
8
9if USE_COVERAGE 4if USE_COVERAGE
10 AM_CFLAGS = --coverage -O0 5 AM_CFLAGS = --coverage -O0
11endif 6endif
@@ -39,20 +34,9 @@ libexec_PROGRAMS = \
39bin_PROGRAMS = \ 34bin_PROGRAMS = \
40 gnunet-vpn 35 gnunet-vpn
41 36
42if MINGW 37gnunet_helper_vpn_SOURCES = \
43 gnunet_helper_vpn_LDFLAGS = \ 38gnunet-helper-vpn.c
44 -no-undefined -Wl,--export-all-symbols
45
46 gnunet_helper_vpn_LDADD = \
47 -lsetupapi -lnewdev -lshell32 -liconv -lstdc++ \
48 -lcomdlg32 -lgdi32 -liphlpapi
49 39
50 gnunet_helper_vpn_SOURCES = \
51 gnunet-helper-vpn-windows.c
52else
53 gnunet_helper_vpn_SOURCES = \
54 gnunet-helper-vpn.c
55endif
56gnunet_service_vpn_SOURCES = \ 40gnunet_service_vpn_SOURCES = \
57 gnunet-service-vpn.c 41 gnunet-service-vpn.c
58gnunet_service_vpn_LDADD = \ 42gnunet_service_vpn_LDADD = \
diff --git a/src/vpn/gnunet-helper-vpn-windows.c b/src/vpn/gnunet-helper-vpn-windows.c
deleted file mode 100644
index afa4fa139..000000000
--- a/src/vpn/gnunet-helper-vpn-windows.c
+++ /dev/null
@@ -1,1610 +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 vpn/gnunet-helper-vpn-windows.c
22 * @brief the helper for the VPN 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 writte 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,
396 "FATAL: removing IPv6 address failed: %s\n",
397 strerror(ret));
398}
399
400
401/**
402 * @brief Sets the IPv4-Address given in address on the interface dev
403 *
404 * @param address the IPv4-Address
405 * @param mask the netmask
406 */
407static int
408set_address4(const char *address, const char *mask)
409{
410 int ret = EINVAL;
411 char command[LINE_LEN];
412
413 struct sockaddr_in addr;
414
415 addr.sin_family = AF_INET;
416
417 /*
418 * Parse the address
419 */
420 if (1 != inet_pton(AF_INET, address, &addr.sin_addr.s_addr))
421 {
422 fprintf(stderr, "ERROR: Failed to parse address `%s': %s\n", address,
423 strerror(errno));
424 return -1;
425 }
426 // Set Device to Subnet-Mode? do we really need openvpn/tun.c:2925 ?
427
428 /*
429 * prepare the command
430 */
431 snprintf(command, LINE_LEN,
432 "netsh interface ipv4 add address \"%s\" %s %s store=active",
433 device_visible_name, address, mask);
434 /*
435 * Set the address
436 */
437 ret = execute_shellcommand(command);
438
439 /* Did it work?*/
440 if (0 != ret)
441 fprintf(stderr,
442 "FATAL: Setting IPv4 address failed: %s\n",
443 strerror(ret));
444 return ret;
445}
446
447
448/**
449 * @brief Removes the IPv4-Address given in address from the interface dev
450 *
451 * @param address the IPv4-Address
452 */
453static void
454remove_address4(const char *address)
455{
456 char command[LINE_LEN];
457 int ret = EINVAL;
458
459 // sanity checking was already done in set_address4
460
461 /*
462 * prepare the command
463 */
464 snprintf(command, LINE_LEN,
465 "netsh interface ipv4 delete address \"%s\" gateway=all store=persistent",
466 device_visible_name);
467 /*
468 * Set the address
469 */
470 ret = execute_shellcommand(command);
471
472 /* Did it work?*/
473 if (0 != ret)
474 fprintf(stderr, "FATAL: removing IPv4 address failed: %s\n", strerror(ret));
475}
476
477
478/**
479 * Setup a new virtual interface to use for tunneling.
480 *
481 * @return: TRUE if setup was successful, else FALSE
482 */
483static BOOL
484setup_interface()
485{
486 /*
487 * where to find our inf-file. (+ the "full" path, after windows found")
488 *
489 * We do not directly input all the props here, because openvpn will update
490 * these details over time.
491 */
492 char inf_file_path[MAX_PATH];
493 char * temp_inf_filename;
494 char hwidlist[LINE_LEN + 4];
495 char class_name[128];
496 GUID class_guid;
497 int str_length = 0;
498
499 /**
500 * Set the device's hardware ID and add it to a list.
501 * This information will later on identify this device in registry.
502 */
503 str_len = GNUNET_strlcpy(hwidlist,
504 HARDWARE_ID,
505 sizeof(hwidList)) + 1;
506 /**
507 * this is kind of over-complicated, but allows keeps things independent of
508 * how the openvpn-hwid is actually stored.
509 *
510 * A HWID list is double-\0 terminated and \0 separated
511 */
512 str_len += GNUNET_strlcpy(&hwidlist[str_length],
513 secondary_hwid,
514 sizeof(hwidlist) - str_len) + 1;
515 GNUNET_assert(str_len < sizeof(hwidlist));
516 hwidlist[str_len] = '\0';
517 ++str_len;
518
519 /**
520 * Locate the inf-file, we need to store it somewhere where the system can
521 * find it. We need to pick the correct driver for win32/win64.
522 */
523 if (is_win64())
524 GetFullPathNameA(INF_FILE64, MAX_PATH, inf_file_path, &temp_inf_filename);
525 else
526 GetFullPathNameA(INF_FILE, MAX_PATH, inf_file_path, &temp_inf_filename);
527
528 fprintf(stderr, "INFO: Located our driver's .inf file at %s\n", inf_file_path);
529 /**
530 * Bootstrap our device info using the drivers inf-file
531 */
532 if (!SetupDiGetINFClassA(inf_file_path,
533 &class_guid,
534 class_name, sizeof(class_name) / sizeof(char),
535 NULL))
536 return FALSE;
537
538 /**
539 * Collect all the other needed information...
540 * let the system fill our this form
541 */
542 DeviceInfo = SetupDiCreateDeviceInfoList(&class_guid, NULL);
543 if (DeviceInfo == INVALID_HANDLE_VALUE)
544 return FALSE;
545
546 DeviceNode.cbSize = sizeof(SP_DEVINFO_DATA);
547 if (!SetupDiCreateDeviceInfoA(DeviceInfo,
548 class_name,
549 &class_guid,
550 NULL,
551 0,
552 DICD_GENERATE_ID,
553 &DeviceNode))
554 return FALSE;
555
556 /* Deploy all the information collected into the registry */
557 if (!SetupDiSetDeviceRegistryPropertyA(DeviceInfo,
558 &DeviceNode,
559 SPDRP_HARDWAREID,
560 (LPBYTE)hwidlist,
561 str_length * sizeof(char)))
562 return FALSE;
563
564 /* Install our new class(=device) into the system */
565 if (!SetupDiCallClassInstaller(DIF_REGISTERDEVICE,
566 DeviceInfo,
567 &DeviceNode))
568 return FALSE;
569
570 /* This system call tends to take a while (several seconds!) on
571 "modern" Windoze systems */
572 if (!UpdateDriverForPlugAndPlayDevicesA(NULL,
573 secondary_hwid,
574 inf_file_path,
575 INSTALLFLAG_FORCE | INSTALLFLAG_NONINTERACTIVE,
576 NULL)) //reboot required? NEVER!
577 return FALSE;
578
579 fprintf(stderr, "DEBUG: successfully created a network device\n");
580 return TRUE;
581}
582
583
584/**
585 * Remove our new virtual interface to use for tunneling.
586 * This function must be called AFTER setup_interface!
587 *
588 * @return: TRUE if destruction was successful, else FALSE
589 */
590static BOOL
591remove_interface()
592{
593 SP_REMOVEDEVICE_PARAMS remove;
594
595 if (INVALID_HANDLE_VALUE == DeviceInfo)
596 return FALSE;
597
598 remove.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
599 remove.HwProfile = 0;
600 remove.Scope = DI_REMOVEDEVICE_GLOBAL;
601 remove.ClassInstallHeader.InstallFunction = DIF_REMOVE;
602 /*
603 * 1. Prepare our existing device information set, and place the
604 * uninstall related information into the structure
605 */
606 if (!SetupDiSetClassInstallParamsA(DeviceInfo,
607 (PSP_DEVINFO_DATA)&DeviceNode,
608 &remove.ClassInstallHeader,
609 sizeof(remove)))
610 return FALSE;
611 /*
612 * 2. Uninstall the virtual interface using the class installer
613 */
614 if (!SetupDiCallClassInstaller(DIF_REMOVE,
615 DeviceInfo,
616 (PSP_DEVINFO_DATA)&DeviceNode))
617 return FALSE;
618
619 SetupDiDestroyDeviceInfoList(DeviceInfo);
620
621 fprintf(stderr, "DEBUG: removed interface successfully\n");
622
623 return TRUE;
624}
625
626
627/**
628 * Do all the lookup necessary to retrieve the inteface's actual name
629 * off the registry.
630 *
631 * @return: TRUE if we were able to lookup the interface's name, else FALSE
632 */
633static BOOL
634resolve_interface_name()
635{
636 SP_DEVINFO_LIST_DETAIL_DATA device_details;
637 char pnp_instance_id [MAX_DEVICE_ID_LEN];
638 HKEY adapter_key_handle;
639 LONG status;
640 DWORD len;
641 int i = 0;
642 int retrys;
643 BOOL retval = FALSE;
644 char adapter[] = INTERFACE_REGISTRY_LOCATION;
645
646 /* We can obtain the PNP instance ID from our setupapi handle */
647 device_details.cbSize = sizeof(device_details);
648 if (CR_SUCCESS != CM_Get_Device_ID_ExA(DeviceNode.DevInst,
649 (PCHAR)pnp_instance_id,
650 MAX_DEVICE_ID_LEN,
651 0, //must be 0
652 NULL)) //hMachine, we are local
653 return FALSE;
654
655 fprintf(stderr, "DEBUG: Resolving interface name for network device %s\n", pnp_instance_id);
656
657 /* Registry is incredibly slow, retry for up to 30 seconds to allow registry to refresh */
658 for (retrys = 0; retrys < 120 && !retval; retrys++)
659 {
660 /* sleep for 250ms*/
661 Sleep(250);
662
663 /* Now we can use this ID to locate the correct networks interface in registry */
664 if (ERROR_SUCCESS != RegOpenKeyExA(
665 HKEY_LOCAL_MACHINE,
666 adapter,
667 0,
668 KEY_READ,
669 &adapter_key_handle))
670 return FALSE;
671
672 /* Of course there is a multitude of entries here, with arbitrary names,
673 * thus we need to iterate through there.
674 */
675 while (!retval)
676 {
677 char instance_key[256];
678 char query_key [256];
679 HKEY instance_key_handle;
680 char pnpinstanceid_name[] = "PnpInstanceID";
681 char pnpinstanceid_value[256];
682 char adaptername_name[] = "Name";
683 DWORD data_type;
684
685 len = 256 * sizeof(char);
686 /* optain a subkey of {4D36E972-E325-11CE-BFC1-08002BE10318} */
687 status = RegEnumKeyExA(
688 adapter_key_handle,
689 i,
690 instance_key,
691 &len,
692 NULL,
693 NULL,
694 NULL,
695 NULL);
696
697 /* this may fail due to one of two reasons:
698 * we are at the end of the list*/
699 if (ERROR_NO_MORE_ITEMS == status)
700 break;
701 // * we found a broken registry key, continue with the next key.
702 if (ERROR_SUCCESS != status)
703 goto cleanup;
704
705 /* prepare our new query string: */
706 snprintf(query_key, 256, "%s\\%s\\Connection",
707 adapter,
708 instance_key);
709
710 /* look inside instance_key\\Connection */
711 if (ERROR_SUCCESS != RegOpenKeyExA(
712 HKEY_LOCAL_MACHINE,
713 query_key,
714 0,
715 KEY_READ,
716 &instance_key_handle))
717 goto cleanup;
718
719 /* now, read our PnpInstanceID */
720 len = sizeof(pnpinstanceid_value);
721 status = RegQueryValueExA(instance_key_handle,
722 pnpinstanceid_name,
723 NULL, //reserved, always NULL according to MSDN
724 &data_type,
725 (LPBYTE)pnpinstanceid_value,
726 &len);
727
728 if (status != ERROR_SUCCESS || data_type != REG_SZ)
729 goto cleanup;
730
731 /* compare the value we got to our devices PNPInstanceID*/
732 if (0 != strncmp(pnpinstanceid_value, pnp_instance_id,
733 sizeof(pnpinstanceid_value) / sizeof(char)))
734 goto cleanup;
735
736 len = sizeof(device_visible_name);
737 status = RegQueryValueExA(
738 instance_key_handle,
739 adaptername_name,
740 NULL, //reserved, always NULL according to MSDN
741 &data_type,
742 (LPBYTE)device_visible_name,
743 &len);
744
745 if (status != ERROR_SUCCESS || data_type != REG_SZ)
746 goto cleanup;
747
748 /*
749 * we have successfully found OUR instance,
750 * save the device GUID before exiting
751 */
752 GNUNET_strlcpy(device_guid, instance_key, sizeof(device_guid));
753 retval = TRUE;
754 fprintf(stderr, "DEBUG: Interface Name lookup succeeded on retry %d, got \"%s\" %s\n", retrys, device_visible_name, device_guid);
755
756cleanup:
757 RegCloseKey(instance_key_handle);
758
759 ++i;
760 }
761
762 RegCloseKey(adapter_key_handle);
763 }
764 return retval;
765}
766
767
768/**
769 * Determines the version of the installed TAP32 driver and checks if it's sufficiently new for GNUNET
770 *
771 * @param handle the handle to our tap device
772 * @return TRUE if the version is sufficient, else FALSE
773 */
774static BOOL
775check_tapw32_version(HANDLE handle)
776{
777 ULONG version[3];
778 DWORD len;
779
780 memset(&(version), 0, sizeof(version));
781
782 if (DeviceIoControl(handle, TAP_WIN_IOCTL_GET_VERSION,
783 &version, sizeof(version),
784 &version, sizeof(version), &len, NULL))
785 fprintf(stderr, "INFO: TAP-Windows Driver Version %d.%d %s\n",
786 (int)version[0],
787 (int)version[1],
788 (version[2] ? "(DEBUG)" : ""));
789
790 if ((version[0] != TAP_WIN_MIN_MAJOR) ||
791 (version[1] < TAP_WIN_MIN_MINOR))
792 {
793 fprintf(stderr, "FATAL: This version of gnunet requires a TAP-Windows driver that is at least version %d.%d\n",
794 TAP_WIN_MIN_MAJOR,
795 TAP_WIN_MIN_MINOR);
796 return FALSE;
797 }
798
799 return TRUE;
800}
801
802
803/**
804 * Creates a tun-interface called dev;
805 *
806 * @return the fd to the tun or -1 on error
807 */
808static HANDLE
809init_tun()
810{
811 char device_path[256];
812 HANDLE handle;
813
814 if (!setup_interface())
815 {
816 errno = ENODEV;
817 return INVALID_HANDLE_VALUE;
818 }
819
820 if (!resolve_interface_name())
821 {
822 errno = ENODEV;
823 return INVALID_HANDLE_VALUE;
824 }
825
826 /* Open Windows TAP-Windows adapter */
827 snprintf(device_path, sizeof(device_path), "%s%s%s",
828 USERMODEDEVICEDIR,
829 device_guid,
830 TAP_WIN_SUFFIX);
831
832 handle = CreateFile(
833 device_path,
834 GENERIC_READ | GENERIC_WRITE,
835 0, /* was: FILE_SHARE_READ */
836 0,
837 OPEN_EXISTING,
838 FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
839 0
840 );
841
842 if (INVALID_HANDLE_VALUE == handle)
843 {
844 fprintf(stderr, "FATAL: CreateFile failed on TAP device: %s\n", device_path);
845 return handle;
846 }
847
848 /* get driver version info */
849 if (!check_tapw32_version(handle))
850 {
851 CloseHandle(handle);
852 return INVALID_HANDLE_VALUE;
853 }
854
855 /* TODO (opt?): get MTU-Size */
856
857 fprintf(stderr, "DEBUG: successfully opened TAP device\n");
858 return handle;
859}
860
861
862/**
863 * Brings a TAP device up and sets it to connected state.
864 *
865 * @param handle the handle to our TAP device
866 * @return True if the operation succeeded, else false
867 */
868static BOOL
869tun_up(HANDLE handle)
870{
871 ULONG status = TRUE;
872 DWORD len;
873
874 if (!DeviceIoControl(handle, TAP_WIN_IOCTL_SET_MEDIA_STATUS,
875 &status, sizeof(status),
876 &status, sizeof(status), &len, NULL))
877 {
878 fprintf(stderr, "FATAL: TAP driver ignored request to UP interface (DeviceIoControl call)\n");
879 return FALSE;
880 }
881
882 /* Wait for the device to go UP, might take some time. */
883 Sleep(TAP32_POSTUP_WAITTIME * 1000);
884 fprintf(stderr, "DEBUG: successfully set TAP device to UP\n");
885
886 return TRUE;
887}
888
889
890/**
891 * Attempts to read off an input facility (tap or named pipe) in overlapped mode.
892 *
893 * 1.
894 * If the input facility is in IOSTATE_READY, it will issue a new read operation to the
895 * input handle. Then it goes into IOSTATE_QUEUED state.
896 * In case the read succeeded instantly the input facility enters 3.
897 *
898 * 2.
899 * If the input facility is in IOSTATE_QUEUED state, it will check if the queued read has finished already.
900 * If it has finished, go to state 3.
901 * If it has failed, set IOSTATE_FAILED
902 *
903 * 3.
904 * If the output facility is in state IOSTATE_READY, the read-buffer is copied to the output buffer.
905 * The input facility enters state IOSTATE_READY
906 * The output facility enters state IOSTATE_READY
907 * If the output facility is in state IOSTATE_QUEUED, the input facility enters IOSTATE_WAITING
908 *
909 * IOSTATE_WAITING is reset by the output facility, once it has completed.
910 *
911 * @param input_facility input named pipe or file to work with.
912 * @param output_facility output pipe or file to hand over data to.
913 * @return false if an event reset was impossible (OS error), else true
914 */
915static BOOL
916attempt_read_tap(struct io_facility * input_facility,
917 struct io_facility * output_facility)
918{
919 struct GNUNET_MessageHeader * hdr;
920 unsigned short size;
921
922 switch (input_facility->facility_state)
923 {
924 case IOSTATE_READY:
925 {
926 if (!ResetEvent(input_facility->overlapped.hEvent))
927 {
928 return FALSE;
929 }
930
931 input_facility->buffer_size = 0;
932
933 /* Check how the task is handled */
934 if (ReadFile(input_facility->handle,
935 input_facility->buffer,
936 sizeof(input_facility->buffer) - sizeof(struct GNUNET_MessageHeader),
937 &input_facility->buffer_size,
938 &input_facility->overlapped))
939 { /* async event processed immediately*/
940 /* reset event manually*/
941 if (!SetEvent(input_facility->overlapped.hEvent))
942 return FALSE;
943
944 fprintf(stderr, "DEBUG: tap read succeeded immediately\n");
945
946 /* we successfully read something from the TAP and now need to
947 * send it our via STDOUT. Is that possible at the moment? */
948 if ((IOSTATE_READY == output_facility->facility_state ||
949 IOSTATE_WAITING == output_facility->facility_state)
950 && (0 < input_facility->buffer_size))
951 { /* hand over this buffers content and apply message header for gnunet */
952 hdr = (struct GNUNET_MessageHeader *)output_facility->buffer;
953 size = input_facility->buffer_size + sizeof(struct GNUNET_MessageHeader);
954
955 GNUNET_memcpy(output_facility->buffer + sizeof(struct GNUNET_MessageHeader),
956 input_facility->buffer,
957 input_facility->buffer_size);
958
959 output_facility->buffer_size = size;
960 hdr->size = htons(size);
961 hdr->type = htons(GNUNET_MESSAGE_TYPE_VPN_HELPER);
962 output_facility->facility_state = IOSTATE_READY;
963 }
964 else if (0 < input_facility->buffer_size)
965 /* If we have have read our buffer, wait for our write-partner*/
966 input_facility->facility_state = IOSTATE_WAITING;
967 }
968 else /* operation was either queued or failed*/
969 {
970 int err = GetLastError();
971 if (ERROR_IO_PENDING == err)
972 { /* operation queued */
973 input_facility->facility_state = IOSTATE_QUEUED;
974 }
975 else
976 { /* error occurred, let the rest of the elements finish */
977 input_facility->path_open = FALSE;
978 input_facility->facility_state = IOSTATE_FAILED;
979 if (IOSTATE_WAITING == output_facility->facility_state)
980 output_facility->path_open = FALSE;
981
982 fprintf(stderr, "FATAL: Read from handle failed, allowing write to finish\n");
983 }
984 }
985 }
986 return TRUE;
987
988 // We are queued and should check if the read has finished
989 case IOSTATE_QUEUED:
990 {
991 // there was an operation going on already, check if that has completed now.
992
993 if (GetOverlappedResult(input_facility->handle,
994 &input_facility->overlapped,
995 &input_facility->buffer_size,
996 FALSE))
997 { /* successful return for a queued operation */
998 if (!ResetEvent(input_facility->overlapped.hEvent))
999 return FALSE;
1000
1001 fprintf(stderr, "DEBUG: tap read succeeded delayed\n");
1002
1003 /* we successfully read something from the TAP and now need to
1004 * send it our via STDOUT. Is that possible at the moment? */
1005 if ((IOSTATE_READY == output_facility->facility_state ||
1006 IOSTATE_WAITING == output_facility->facility_state)
1007 && 0 < input_facility->buffer_size)
1008 { /* hand over this buffers content and apply message header for gnunet */
1009 hdr = (struct GNUNET_MessageHeader *)output_facility->buffer;
1010 size = input_facility->buffer_size + sizeof(struct GNUNET_MessageHeader);
1011
1012 GNUNET_memcpy(output_facility->buffer + sizeof(struct GNUNET_MessageHeader),
1013 input_facility->buffer,
1014 input_facility->buffer_size);
1015
1016 output_facility->buffer_size = size;
1017 hdr->size = htons(size);
1018 hdr->type = htons(GNUNET_MESSAGE_TYPE_VPN_HELPER);
1019 output_facility->facility_state = IOSTATE_READY;
1020 input_facility->facility_state = IOSTATE_READY;
1021 }
1022 else if (0 < input_facility->buffer_size)
1023 { /* If we have have read our buffer, wait for our write-partner*/
1024 input_facility->facility_state = IOSTATE_WAITING;
1025 // TODO: shall we attempt to fill our buffer or should we wait for our write-partner to finish?
1026 }
1027 }
1028 else
1029 { /* operation still pending/queued or failed? */
1030 int err = GetLastError();
1031 if ((ERROR_IO_INCOMPLETE != err) && (ERROR_IO_PENDING != err))
1032 { /* error occurred, let the rest of the elements finish */
1033 input_facility->path_open = FALSE;
1034 input_facility->facility_state = IOSTATE_FAILED;
1035 if (IOSTATE_WAITING == output_facility->facility_state)
1036 output_facility->path_open = FALSE;
1037 fprintf(stderr, "FATAL: Read from handle failed, allowing write to finish\n");
1038 }
1039 }
1040 }
1041 return TRUE;
1042
1043 case IOSTATE_RESUME:
1044 hdr = (struct GNUNET_MessageHeader *)output_facility->buffer;
1045 size = input_facility->buffer_size + sizeof(struct GNUNET_MessageHeader);
1046
1047 GNUNET_memcpy(output_facility->buffer + sizeof(struct GNUNET_MessageHeader),
1048 input_facility->buffer,
1049 input_facility->buffer_size);
1050
1051 output_facility->buffer_size = size;
1052 hdr->size = htons(size);
1053 hdr->type = htons(GNUNET_MESSAGE_TYPE_VPN_HELPER);
1054 output_facility->facility_state = IOSTATE_READY;
1055 input_facility->facility_state = IOSTATE_READY;
1056 return TRUE;
1057
1058 default:
1059 return TRUE;
1060 }
1061}
1062
1063
1064/**
1065 * Attempts to read off an input facility (tap or named pipe) in overlapped mode.
1066 *
1067 * 1.
1068 * If the input facility is in IOSTATE_READY, it will issue a new read operation to the
1069 * input handle. Then it goes into IOSTATE_QUEUED state.
1070 * In case the read succeeded instantly the input facility enters 3.
1071 *
1072 * 2.
1073 * If the input facility is in IOSTATE_QUEUED state, it will check if the queued read has finished already.
1074 * If it has finished, go to state 3.
1075 * If it has failed, set IOSTATE_FAILED
1076 *
1077 * 3.
1078 * If the facility is finished with ready
1079 * The read-buffer is copied to the output buffer, except for the GNUNET_MessageHeader.
1080 * The input facility enters state IOSTATE_READY
1081 * The output facility enters state IOSTATE_READY
1082 * If the output facility is in state IOSTATE_QUEUED, the input facility enters IOSTATE_WAITING
1083 *
1084 * IOSTATE_WAITING is reset by the output facility, once it has completed.
1085 *
1086 * @param input_facility input named pipe or file to work with.
1087 * @param output_facility output pipe or file to hand over data to.
1088 * @return false if an event reset was impossible (OS error), else true
1089 */
1090static BOOL
1091attempt_read_stdin(struct io_facility * input_facility,
1092 struct io_facility * output_facility)
1093{
1094 struct GNUNET_MessageHeader * hdr;
1095
1096 switch (input_facility->facility_state)
1097 {
1098 case IOSTATE_READY:
1099 {
1100 input_facility->buffer_size = 0;
1101
1102partial_read_iostate_ready:
1103 if (!ResetEvent(input_facility->overlapped.hEvent))
1104 return FALSE;
1105
1106 /* Check how the task is handled */
1107 if (ReadFile(input_facility->handle,
1108 input_facility->buffer + input_facility->buffer_size,
1109 sizeof(input_facility->buffer) - input_facility->buffer_size,
1110 &input_facility->buffer_size_processed,
1111 &input_facility->overlapped))
1112 { /* async event processed immediately*/
1113 hdr = (struct GNUNET_MessageHeader *)input_facility->buffer;
1114
1115 /* reset event manually*/
1116 if (!SetEvent(input_facility->overlapped.hEvent))
1117 return FALSE;
1118
1119 fprintf(stderr, "DEBUG: stdin read succeeded immediately\n");
1120 input_facility->buffer_size += input_facility->buffer_size_processed;
1121
1122 if (ntohs(hdr->type) != GNUNET_MESSAGE_TYPE_VPN_HELPER ||
1123 ntohs(hdr->size) > sizeof(input_facility->buffer))
1124 {
1125 fprintf(stderr, "WARNING: Protocol violation, got GNUnet Message type %h, size %h\n", ntohs(hdr->type), ntohs(hdr->size));
1126 input_facility->facility_state = IOSTATE_READY;
1127 return TRUE;
1128 }
1129 /* we got the a part of a packet */
1130 if (ntohs(hdr->size) > input_facility->buffer_size)
1131 goto partial_read_iostate_ready;
1132
1133 /* have we read more than 0 bytes of payload? (sizeread > header)*/
1134 if (input_facility->buffer_size > sizeof(struct GNUNET_MessageHeader) &&
1135 ((IOSTATE_READY == output_facility->facility_state) ||
1136 (IOSTATE_WAITING == output_facility->facility_state)))
1137 { /* we successfully read something from the TAP and now need to
1138 * send it our via STDOUT. Is that possible at the moment? */
1139 /* hand over this buffers content and strip gnunet message header */
1140 GNUNET_memcpy(output_facility->buffer,
1141 input_facility->buffer + sizeof(struct GNUNET_MessageHeader),
1142 input_facility->buffer_size - sizeof(struct GNUNET_MessageHeader));
1143 output_facility->buffer_size = input_facility->buffer_size - sizeof(struct GNUNET_MessageHeader);
1144 output_facility->facility_state = IOSTATE_READY;
1145 input_facility->facility_state = IOSTATE_READY;
1146 }
1147 else if (input_facility->buffer_size > sizeof(struct GNUNET_MessageHeader))
1148 /* If we have have read our buffer, wait for our write-partner*/
1149 input_facility->facility_state = IOSTATE_WAITING;
1150 else /* we read nothing */
1151 input_facility->facility_state = IOSTATE_READY;
1152 }
1153 else /* operation was either queued or failed*/
1154 {
1155 int err = GetLastError();
1156 if (ERROR_IO_PENDING == err) /* operation queued */
1157 input_facility->facility_state = IOSTATE_QUEUED;
1158 else
1159 { /* error occurred, let the rest of the elements finish */
1160 input_facility->path_open = FALSE;
1161 input_facility->facility_state = IOSTATE_FAILED;
1162 if (IOSTATE_WAITING == output_facility->facility_state)
1163 output_facility->path_open = FALSE;
1164
1165 fprintf(stderr, "FATAL: Read from handle failed, allowing write to finish\n");
1166 }
1167 }
1168 }
1169 return TRUE;
1170
1171 // We are queued and should check if the read has finished
1172 case IOSTATE_QUEUED:
1173 {
1174 // there was an operation going on already, check if that has completed now.
1175 if (GetOverlappedResult(input_facility->handle,
1176 &input_facility->overlapped,
1177 &input_facility->buffer_size_processed,
1178 FALSE))
1179 { /* successful return for a queued operation */
1180 hdr = (struct GNUNET_MessageHeader *)input_facility->buffer;
1181
1182 if (!ResetEvent(input_facility->overlapped.hEvent))
1183 return FALSE;
1184
1185 fprintf(stderr, "DEBUG: stdin read succeeded delayed\n");
1186 input_facility->buffer_size += input_facility->buffer_size_processed;
1187
1188 if ((ntohs(hdr->type) != GNUNET_MESSAGE_TYPE_VPN_HELPER) ||
1189 (ntohs(hdr->size) > sizeof(input_facility->buffer)))
1190 {
1191 fprintf(stderr, "WARNING: Protocol violation, got GNUnet Message type %h, size %h\n", ntohs(hdr->type), ntohs(hdr->size));
1192 input_facility->facility_state = IOSTATE_READY;
1193 return TRUE;
1194 }
1195 /* we got the a part of a packet */
1196 if (ntohs(hdr->size) > input_facility->buffer_size)
1197 ;
1198 goto partial_read_iostate_ready;
1199
1200 /* we successfully read something from the TAP and now need to
1201 * send it our via STDOUT. Is that possible at the moment? */
1202 if ((IOSTATE_READY == output_facility->facility_state ||
1203 IOSTATE_WAITING == output_facility->facility_state)
1204 && input_facility->buffer_size > sizeof(struct GNUNET_MessageHeader))
1205 { /* hand over this buffers content and strip gnunet message header */
1206 GNUNET_memcpy(output_facility->buffer,
1207 input_facility->buffer + sizeof(struct GNUNET_MessageHeader),
1208 input_facility->buffer_size - sizeof(struct GNUNET_MessageHeader));
1209 output_facility->buffer_size = input_facility->buffer_size - sizeof(struct GNUNET_MessageHeader);
1210 output_facility->facility_state = IOSTATE_READY;
1211 input_facility->facility_state = IOSTATE_READY;
1212 }
1213 else if (input_facility->buffer_size > sizeof(struct GNUNET_MessageHeader))
1214 input_facility->facility_state = IOSTATE_WAITING;
1215 else
1216 input_facility->facility_state = IOSTATE_READY;
1217 }
1218 else
1219 { /* operation still pending/queued or failed? */
1220 int err = GetLastError();
1221 if ((ERROR_IO_INCOMPLETE != err) && (ERROR_IO_PENDING != err))
1222 { /* error occurred, let the rest of the elements finish */
1223 input_facility->path_open = FALSE;
1224 input_facility->facility_state = IOSTATE_FAILED;
1225 if (IOSTATE_WAITING == output_facility->facility_state)
1226 output_facility->path_open = FALSE;
1227 fprintf(stderr, "FATAL: Read from handle failed, allowing write to finish\n");
1228 }
1229 }
1230 }
1231 return TRUE;
1232
1233 case IOSTATE_RESUME: /* Our buffer was filled already but our write facility was busy. */
1234 GNUNET_memcpy(output_facility->buffer,
1235 input_facility->buffer + sizeof(struct GNUNET_MessageHeader),
1236 input_facility->buffer_size - sizeof(struct GNUNET_MessageHeader));
1237 output_facility->buffer_size = input_facility->buffer_size - sizeof(struct GNUNET_MessageHeader);
1238 output_facility->facility_state = IOSTATE_READY;
1239 input_facility->facility_state = IOSTATE_READY;
1240 return TRUE;
1241
1242 default:
1243 return TRUE;
1244 }
1245}
1246
1247
1248/**
1249 * Attempts to write to an output facility (tap or named pipe) in overlapped mode.
1250 *
1251 * TODO: high level description
1252 *
1253 * @param output_facility output pipe or file to hand over data to.
1254 * @param input_facility input named pipe or file to work with.
1255 * @return false if an event reset was impossible (OS error), else true
1256 */
1257static BOOL
1258attempt_write(struct io_facility * output_facility,
1259 struct io_facility * input_facility)
1260{
1261 switch (output_facility->facility_state)
1262 {
1263 case IOSTATE_READY:
1264 output_facility->buffer_size_written = 0;
1265
1266continue_partial_write:
1267 if (!ResetEvent(output_facility->overlapped.hEvent))
1268 return FALSE;
1269
1270 /* Check how the task was handled */
1271 if (WriteFile(output_facility->handle,
1272 output_facility->buffer + output_facility->buffer_size_written,
1273 output_facility->buffer_size - output_facility->buffer_size_written,
1274 &output_facility->buffer_size_processed,
1275 &output_facility->overlapped))
1276 {/* async event processed immediately*/
1277 fprintf(stderr, "DEBUG: write succeeded immediately\n");
1278 output_facility->buffer_size_written += output_facility->buffer_size_processed;
1279
1280 /* reset event manually*/
1281 if (!SetEvent(output_facility->overlapped.hEvent))
1282 return FALSE;
1283
1284 /* partial write */
1285 if (output_facility->buffer_size_written < output_facility->buffer_size)
1286 goto continue_partial_write;
1287
1288 /* we are now waiting for our buffer to be filled*/
1289 output_facility->facility_state = IOSTATE_WAITING;
1290
1291 /* we successfully wrote something and now need to reset our reader */
1292 if (IOSTATE_WAITING == input_facility->facility_state)
1293 input_facility->facility_state = IOSTATE_RESUME;
1294 else if (IOSTATE_FAILED == input_facility->facility_state)
1295 output_facility->path_open = FALSE;
1296 }
1297 else /* operation was either queued or failed*/
1298 {
1299 int err = GetLastError();
1300 if (ERROR_IO_PENDING == err)
1301 { /* operation queued */
1302 output_facility->facility_state = IOSTATE_QUEUED;
1303 }
1304 else
1305 { /* error occurred, close this path */
1306 output_facility->path_open = FALSE;
1307 output_facility->facility_state = IOSTATE_FAILED;
1308 fprintf(stderr, "FATAL: Write to handle failed, exiting\n");
1309 }
1310 }
1311 return TRUE;
1312
1313 case IOSTATE_QUEUED:
1314 // there was an operation going on already, check if that has completed now.
1315
1316 if (GetOverlappedResult(output_facility->handle,
1317 &output_facility->overlapped,
1318 &output_facility->buffer_size_processed,
1319 FALSE))
1320 {/* successful return for a queued operation */
1321 if (!ResetEvent(output_facility->overlapped.hEvent))
1322 return FALSE;
1323
1324 fprintf(stderr, "DEBUG: write succeeded delayed\n");
1325 output_facility->buffer_size_written += output_facility->buffer_size_processed;
1326
1327 /* partial write */
1328 if (output_facility->buffer_size_written < output_facility->buffer_size)
1329 goto continue_partial_write;
1330
1331 /* we are now waiting for our buffer to be filled*/
1332 output_facility->facility_state = IOSTATE_WAITING;
1333
1334 /* we successfully wrote something and now need to reset our reader */
1335 if (IOSTATE_WAITING == input_facility->facility_state)
1336 input_facility->facility_state = IOSTATE_RESUME;
1337 else if (IOSTATE_FAILED == input_facility->facility_state)
1338 output_facility->path_open = FALSE;
1339 }
1340 else
1341 { /* operation still pending/queued or failed? */
1342 int err = GetLastError();
1343 if ((ERROR_IO_INCOMPLETE != err) && (ERROR_IO_PENDING != err))
1344 { /* error occurred, close this path */
1345 output_facility->path_open = FALSE;
1346 output_facility->facility_state = IOSTATE_FAILED;
1347 fprintf(stderr, "FATAL: Write to handle failed, exiting\n");
1348 }
1349 }
1350
1351 default:
1352 return TRUE;
1353 }
1354}
1355
1356
1357/**
1358 * Initialize a overlapped structure
1359 *
1360 * @param elem the element to initilize
1361 * @param initial_state the initial state for this instance
1362 * @param signaled if the hEvent created should default to signaled or not
1363 * @return true on success, else false
1364 */
1365static BOOL
1366initialize_io_facility(struct io_facility * elem,
1367 int initial_state,
1368 BOOL signaled)
1369{
1370 elem->path_open = TRUE;
1371 elem->handle = INVALID_HANDLE_VALUE;
1372 elem->facility_state = initial_state;
1373 elem->buffer_size = 0;
1374 elem->overlapped.hEvent = CreateEvent(NULL, TRUE, signaled, NULL);
1375 if (NULL == elem->overlapped.hEvent)
1376 return FALSE;
1377
1378 return TRUE;
1379}
1380
1381
1382/**
1383 * Start forwarding to and from the tunnel.
1384 *
1385 * @param tap_handle device handle for interacting with the Virtual interface
1386 */
1387static void
1388run(HANDLE tap_handle)
1389{
1390 /* IO-Facility for reading from our virtual interface */
1391 struct io_facility tap_read;
1392 /* IO-Facility for writing to our virtual interface */
1393 struct io_facility tap_write;
1394 /* IO-Facility for reading from stdin */
1395 struct io_facility std_in;
1396 /* IO-Facility for writing to stdout */
1397 struct io_facility std_out;
1398
1399 HANDLE parent_std_in_handle = GetStdHandle(STD_INPUT_HANDLE);
1400 HANDLE parent_std_out_handle = GetStdHandle(STD_OUTPUT_HANDLE);
1401
1402 /* tun up: */
1403 /* we do this HERE and not beforehand (in init_tun()), in contrast to openvpn
1404 * to remove the need to flush the arp cache, handle DHCP and wrong IPs.
1405 *
1406 * DHCP and such are all features we will never use in gnunet afaik.
1407 * But for openvpn those are essential.
1408 */
1409 if ((privilege_testing) || (!tun_up(tap_handle)))
1410 goto teardown_final;
1411
1412 /* Initialize our overlapped IO structures*/
1413 if (!(initialize_io_facility(&tap_read, IOSTATE_READY, FALSE)
1414 && initialize_io_facility(&tap_write, IOSTATE_WAITING, TRUE)
1415 && initialize_io_facility(&std_in, IOSTATE_READY, FALSE)
1416 && initialize_io_facility(&std_out, IOSTATE_WAITING, TRUE)))
1417 goto teardown_final;
1418
1419 /* Handles for STDIN and STDOUT */
1420 tap_read.handle = tap_handle;
1421 tap_write.handle = tap_handle;
1422
1423#ifdef DEBUG_TO_CONSOLE
1424 /* Debug output to console STDIN/STDOUT*/
1425 std_in.handle = parent_std_in_handle;
1426 std_out.handle = parent_std_out_handle;
1427#else
1428 fprintf(stderr, "DEBUG: reopening stdin/out for overlapped IO\n");
1429 /*
1430 * Find out the types of our handles.
1431 * This part is a problem, because in windows we need to handle files,
1432 * pipes and the console differently.
1433 */
1434 if ((FILE_TYPE_PIPE != GetFileType(parent_std_in_handle)) ||
1435 (FILE_TYPE_PIPE != GetFileType(parent_std_out_handle)))
1436 {
1437 fprintf(stderr, "ERROR: stdin/stdout must be named pipes\n");
1438 goto teardown;
1439 }
1440
1441 std_in.handle = ReOpenFile(parent_std_in_handle,
1442 GENERIC_READ,
1443 FILE_SHARE_WRITE | FILE_SHARE_READ,
1444 FILE_FLAG_OVERLAPPED);
1445
1446 if (INVALID_HANDLE_VALUE == std_in.handle)
1447 {
1448 fprintf(stderr, "FATAL: Could not reopen stdin for in overlapped mode, has to be a named pipe\n");
1449 goto teardown;
1450 }
1451
1452 std_out.handle = ReOpenFile(parent_std_out_handle,
1453 GENERIC_WRITE,
1454 FILE_SHARE_READ,
1455 FILE_FLAG_OVERLAPPED);
1456
1457 if (INVALID_HANDLE_VALUE == std_out.handle)
1458 {
1459 fprintf(stderr, "FATAL: Could not reopen stdout for in overlapped mode, has to be a named pipe\n");
1460 goto teardown;
1461 }
1462#endif
1463
1464 fprintf(stderr, "DEBUG: mainloop has begun\n");
1465
1466 while (std_out.path_open || tap_write.path_open)
1467 {
1468 /* perform READ from stdin if possible */
1469 if (std_in.path_open && (!attempt_read_stdin(&std_in, &tap_write)))
1470 break;
1471
1472 /* perform READ from tap if possible */
1473 if (tap_read.path_open && (!attempt_read_tap(&tap_read, &std_out)))
1474 break;
1475
1476 /* perform WRITE to tap if possible */
1477 if (tap_write.path_open && (!attempt_write(&tap_write, &std_in)))
1478 break;
1479
1480 /* perform WRITE to STDOUT if possible */
1481 if (std_out.path_open && (!attempt_write(&std_out, &tap_read)))
1482 break;
1483 }
1484
1485 fprintf(stderr, "DEBUG: teardown initiated\n");
1486teardown:
1487 CancelIo(tap_handle);
1488 CancelIo(std_in.handle);
1489 CancelIo(std_out.handle);
1490teardown_final:
1491 CloseHandle(tap_handle);
1492}
1493
1494
1495/**
1496 * Open VPN tunnel interface.
1497 *
1498 * @param argc must be 6
1499 * @param argv 0: binary name (gnunet-helper-vpn)
1500 * [1: dryrun/testrun (does not execute mainloop)]
1501 * 2: tunnel interface prefix (gnunet-vpn)
1502 * 3: IPv6 address (::1), "-" to disable
1503 * 4: IPv6 netmask length in bits (64), ignored if #2 is "-"
1504 * 5: IPv4 address (1.2.3.4), "-" to disable
1505 * 6: IPv4 netmask (255.255.0.0), ignored if #4 is "-"
1506 */
1507int
1508main(int argc, char **argv)
1509{
1510 char hwid[LINE_LEN];
1511 HANDLE handle;
1512 int global_ret = 0;
1513 BOOL have_ip4 = FALSE;
1514 BOOL have_ip6 = FALSE;
1515
1516 if (argc > 1 && 0 == strcmp(argv[1], "-d"))
1517 {
1518 privilege_testing = TRUE;
1519 fprintf(stderr,
1520 "%s",
1521 "DEBUG: Running binary in privilege testing mode.");
1522 argv++;
1523 argc--;
1524 }
1525
1526 if (6 != argc)
1527 {
1528 fprintf(stderr,
1529 "%s",
1530 "FATAL: must supply 5 arguments\nUsage:\ngnunet-helper-vpn [-d] <if name prefix> <address6 or \"-\"> <netbits6> <address4 or \"-\"> <netmask4>\n");
1531 return 1;
1532 }
1533
1534 GNUNET_strlcpy(hwid, argv[1], sizeof(hwid));
1535
1536 /*
1537 * We use our PID for finding/resolving the control-panel name of our virtual
1538 * device. PIDs are (of course) unique at runtime, thus we can safely use it
1539 * as additional hardware-id for our device.
1540 */
1541 snprintf(secondary_hwid, LINE_LEN / 2, "%s-%d",
1542 hwid,
1543 _getpid());
1544
1545 if (INVALID_HANDLE_VALUE == (handle = init_tun()))
1546 {
1547 fprintf(stderr, "FATAL: could not initialize virtual-interface %s with IPv6 %s/%s and IPv4 %s/%s\n",
1548 hwid,
1549 argv[2],
1550 argv[3],
1551 argv[4],
1552 argv[5]);
1553 global_ret = -1;
1554 goto cleanup;
1555 }
1556
1557 fprintf(stderr, "DEBUG: Setting IPs, if needed\n");
1558 if (0 != strcmp(argv[2], "-"))
1559 {
1560 const char *address = argv[2];
1561 long prefix_len = atol(argv[3]);
1562
1563 if ((prefix_len < 1) || (prefix_len > 127))
1564 {
1565 fprintf(stderr, "FATAL: ipv6 prefix_len out of range\n");
1566 global_ret = -1;
1567 goto cleanup;
1568 }
1569
1570 fprintf(stderr, "DEBUG: Setting IP6 address: %s/%d\n", address, prefix_len);
1571 if (0 != (global_ret = set_address6(address, prefix_len)))
1572 goto cleanup;
1573
1574 have_ip6 = TRUE;
1575 }
1576
1577 if (0 != strcmp(argv[4], "-"))
1578 {
1579 const char *address = argv[4];
1580 const char *mask = argv[5];
1581
1582 fprintf(stderr, "DEBUG: Setting IP4 address: %s/%s\n", address, mask);
1583 if (0 != (global_ret = set_address4(address, mask)))
1584 goto cleanup;
1585
1586 have_ip4 = TRUE;
1587 }
1588
1589 run(handle);
1590cleanup:
1591
1592 if (have_ip4)
1593 {
1594 const char *address = argv[4];
1595 fprintf(stderr, "DEBUG: Removing IP4 address\n");
1596 remove_address4(address);
1597 }
1598 if (have_ip6)
1599 {
1600 const char *address = argv[2];
1601 fprintf(stderr, "DEBUG: Removing IP6 address\n");
1602 remove_address6(address);
1603 }
1604
1605 fprintf(stderr, "DEBUG: removing interface\n");
1606 remove_interface();
1607 fprintf(stderr, "DEBUG: graceful exit completed\n");
1608
1609 return global_ret;
1610}