2 This file is part of GNUnet.
3 (C) 2010, 2012 Christian Grothoff
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
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 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
22 * @file vpn/gnunet-helper-vpn-windows.c
23 * @brief the helper for the VPN service in win32 builds.
24 * Opens a virtual network-interface, sends data received on the if to stdout,
25 * sends data received on stdin to the interface
26 * @author Christian M. Fuchs
28 * The following list of people have reviewed this code and considered
29 * it safe since the last modification (if you reviewed it, please
30 * have your name added to the list):
38 #include <ddk/cfgmgr32.h>
42 * Need 'struct GNUNET_MessageHeader'.
44 #include "gnunet_common.h"
47 * Need VPN message types.
49 #include "gnunet_protocols.h"
52 * Should we print (interesting|debug) messages that can happen during
55 #define DEBUG GNUNET_NO
58 * Maximum size of a GNUnet message (GNUNET_SERVER_MAX_MESSAGE_SIZE)
60 #define MAX_SIZE 65536
63 * Name or Path+Name of our driver in Unicode.
64 * The .sys and .cat files HAVE to be in the same location as this file!
66 #define INF_FILE _T("tapw32.inf")
69 * Hardware ID used in the inf-file.
70 * This might change over time, as openvpn advances their driver
72 #define HARDWARE_ID _T("TAP0901")
75 * Location of the network interface list resides in registry.
76 * TODO: is this fixed on all version of windows? Checked with XP and 7
78 #define INTERFACE_REGISTRY_LOCATION "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
81 * Our local process' PID. Used for creating a sufficiently unique additional
82 * hardware ID for our device.
84 static TCHAR secondary_hwid[LINE_LEN / 2];
87 * Device's visible Name, used to identify a network device in netsh.
88 * eg: "Local Area Connection 9"
90 static TCHAR device_visible_name[256];
93 * This is our own local instance of a virtual network interface
94 * It is (somewhat) equivalent to using tun/tap in unixoid systems
96 * Upon initialization, we create such an device node.
97 * Upon termination, we remove it again.
99 * If we crash this device might stay around.
101 static HDEVINFO DeviceInfo = INVALID_HANDLE_VALUE;
104 * Registry Key we hand over to windows to spawn a new virtual interface
106 static SP_DEVINFO_DATA DeviceNode;
109 * Class-tag of our virtual device
111 static TCHAR class[128];
114 * GUID of our virtual device in the form of
115 * {12345678-1234-1234-1234-123456789abc} - in hex
120 * @brief Sets the IPv6-Address given in address on the interface dev
122 * @param address the IPv6-Address
123 * @param prefix_len the length of the network-prefix
126 set_address6 (const char *address, unsigned long prefix_len)
131 * parse the new address
135 * Get the index of the if
148 * Add the UP and RUNNING flags
154 fprintf (stderr, "close failed: %s\n", strerror (errno));
160 * @brief Sets the IPv4-Address given in address on the interface dev
162 * @param dev the interface to configure
163 * @param address the IPv4-Address
164 * @param mask the netmask
167 set_address4 (const char *dev, const char *address, const char *mask)
195 * Add the UP and RUNNING flags
201 fprintf (stderr, "close failed: %s\n", strerror (errno));
208 * Setup a new virtual interface to use for tunneling.
210 * @return: TRUE if setup was successful, else FALSE
216 * where to find our inf-file. (+ the "full" path, after windows found")
218 * We do not directly input all the props here, because openvpn will update
219 * these details over time.
221 TCHAR inf_file_path[MAX_PATH];
222 TCHAR hwidlist[LINE_LEN + 4];
228 * Set the device's hardware ID and add it to a list.
229 * This information will later on identify this device in registry.
231 * TODO: Currently we just use TAP0901 as HWID,
232 * but we might want to add additional information
234 _tcsncpy (hwidlist, HARDWARE_ID, LINE_LEN);
236 * this is kind of over-complicated, but allows keeps things independent of
237 * how the openvpn-hwid is actually stored.
239 * A HWID list is double-\0 terminated and \0 separated
241 str_lenth = _tcslen (hwidlist) + 1 ;
242 _tcsncpy (&hwidlist[str_lenth], secondary_hwid, LINE_LEN - str_lenth);
245 * Locate the inf-file, we need to store it somewhere where the system can
246 * find it. A good choice would be CWD/PDW or %WINDIR$\system32\
248 * TODO: How about win64 in the future?
249 * We need to use a different driver for amd64/i386 !
251 GetFullPathName (INF_FILE, MAX_PATH, inf_file_path, NULL);
254 * Bootstrap our device info using the drivers inf-file
256 if (!SetupDiGetINFClass (inf_file_path,
258 class, sizeof (class) / sizeof (TCHAR),
263 * Collect all the other needed information...
264 * let the system fill our this form
266 DeviceInfo = SetupDiCreateDeviceInfoList (&guid, NULL);
267 if (DeviceInfo == INVALID_HANDLE_VALUE)
270 DeviceNode.cbSize = sizeof (SP_DEVINFO_DATA);
271 if (! SetupDiCreateDeviceInfo (DeviceInfo,
280 /* Deploy all the information collected into the registry */
281 if (!SetupDiSetDeviceRegistryProperty (DeviceInfo,
285 (lstrlen (hwidlist) + 2) * sizeof (TCHAR)))
288 /* Install our new class(=device) into the system */
289 if (! SetupDiCallClassInstaller (DIF_REGISTERDEVICE,
299 * Remove our new virtual interface to use for tunneling.
300 * This function must be called AFTER setup_interface!
302 * @return: TRUE if destruction was successful, else FALSE
307 SP_REMOVEDEVICE_PARAMS remove;
309 if (INVALID_HANDLE_VALUE == DeviceInfo)
312 remove.ClassInstallHeader.cbSize = sizeof (SP_CLASSINSTALL_HEADER);
313 remove.HwProfile = 0;
314 remove.Scope = DI_REMOVEDEVICE_GLOBAL;
315 remove.ClassInstallHeader.InstallFunction = DIF_REMOVE;
317 * 1. Prepare our existing device information set, and place the
318 * uninstall related information into the structure
320 if (! SetupDiSetClassInstallParams (DeviceInfo,
321 (PSP_DEVINFO_DATA) &DeviceNode,
322 &remove.ClassInstallHeader,
326 * 2. Uninstall the virtual interface using the class installer
328 if (! SetupDiCallClassInstaller (DIF_REMOVE,
330 (PSP_DEVINFO_DATA) &DeviceNode))
333 SetupDiDestroyDeviceInfoList(DeviceInfo);
339 * Do all the lookup necessary to retrieve the inteface's actual name
342 * @return: TRUE if we were able to lookup the interface's name, else FALSE
345 resolve_interface_name ()
348 SP_DEVINFO_LIST_DETAIL_DATA device_details;
349 TCHAR pnp_instance_id [MAX_DEVICE_ID_LEN];
350 HKEY adapter_key_handle;
354 boolean retval=FALSE;
355 TCHAR adapter[] = _T (INTERFACE_REGISTRY_LOCATION);
357 /* We can obtain the PNP instance ID from our setupapi handle */
358 device_details.cbSize = sizeof (device_details);
359 if (CR_SUCCESS != CM_Get_Device_ID_Ex (DeviceNode.DevInst,
360 (PWSTR) pnp_instance_id,
363 NULL)) //hMachine, we are local
366 /* Now we can use this ID to locate the correct networks interface in registry */
367 if (ERROR_SUCCESS != RegOpenKeyEx (
372 &adapter_key_handle))
375 /* Of course there is a multitude of entries here, with arbitrary names,
376 * thus we need to iterate through there.
378 while (FALSE == retval)
380 TCHAR instance_key[256];
381 TCHAR query_key [256];
382 HKEY instance_key_handle;
383 TCHAR pnpinstanceid_name[] = _T("PnpInstanceID");
384 TCHAR pnpinstanceid_value[256];
385 TCHAR adaptername_name[] = _T("Name");
388 len = sizeof (adapter_key_handle);
389 /* optain a subkey of {4D36E972-E325-11CE-BFC1-08002BE10318} */
390 status = RegEnumKeyEx (
400 /* this may fail due to one of two reasons:
401 * we are at the end of the list*/
402 if (ERROR_NO_MORE_ITEMS == status)
404 // * we found a broken registry key, continue with the next key.
405 if (ERROR_SUCCESS != status)
408 /* prepare our new querty string: */
409 _sntprintf (query_key, 256, _T ("%s\\%s\\Connection"),
410 _T (INTERFACE_REGISTRY_LOCATION),
413 /* look inside instance_key\\Connection */
414 status = RegOpenKeyEx (
419 &instance_key_handle);
421 if (status != ERROR_SUCCESS)
424 /* now, read our PnpInstanceID */
425 len = sizeof (pnpinstanceid_value);
426 status = RegQueryValueEx (instance_key_handle,
428 NULL, //reserved, always NULL according to MSDN
430 (LPBYTE) pnpinstanceid_value,
433 if (status != ERROR_SUCCESS || data_type != REG_SZ)
436 /* compare the value we got to our devices PNPInstanceID*/
437 if ( 0 != _tcsncmp (pnpinstanceid_value, pnp_instance_id,
438 sizeof (pnpinstanceid_value)/sizeof(TCHAR)))
441 len = sizeof (device_visible_name);
442 status = RegQueryValueEx (
445 NULL, //reserved, always NULL according to MSDN
447 (LPBYTE) device_visible_name,
450 if (status == ERROR_SUCCESS && data_type == REG_SZ)
455 RegCloseKey (instance_key_handle);
460 RegCloseKey (adapter_key_handle);
466 * Creates a tun-interface called dev;
468 * @param hwid is asumed to point to a TCHAR[LINE_LEN]
469 * if *dev == '\\0', uses the name supplied by the kernel;
470 * @return the fd to the tun or -1 on error
473 init_tun (TCHAR *hwid)
483 if (! setup_interface()){
488 if (! resolve_interface_name()){
498 * Start forwarding to and from the tunnel.
500 * @param fd_tun tunnel FD
506 * The buffer filled by reading from fd_tun
508 unsigned char buftun[MAX_SIZE];
509 ssize_t buftun_size = 0;
510 unsigned char *buftun_read = NULL;
513 * The buffer filled by reading from stdin
515 unsigned char bufin[MAX_SIZE];
516 ssize_t bufin_size = 0;
517 ssize_t bufin_rpos = 0;
518 unsigned char *bufin_read = NULL;
519 /* Hello, I am a stub function! I did my job, yay me! */
525 * Open VPN tunnel interface.
527 * @param argc must be 6
528 * @param argv 0: binary name (gnunet-helper-vpn)
529 * 1: tunnel interface name (gnunet-vpn)
530 * 2: IPv6 address (::1), "-" to disable
531 * 3: IPv6 netmask length in bits (64), ignored if #2 is "-"
532 * 4: IPv4 address (1.2.3.4), "-" to disable
533 * 5: IPv4 netmask (255.255.0.0), ignored if #4 is "-"
536 main (int argc, char **argv)
538 TCHAR hwid[LINE_LEN];
539 TCHAR pid_as_string[LINE_LEN / 4];
545 fprintf (stderr, "Fatal: must supply 5 arguments!\n");
549 strncpy (hwid, argv[1], LINE_LEN);
550 hwid[LINE_LEN - 1] = _T('\0');
553 * We use our PID for finding/resolving the control-panel name of our virtual
554 * device. PIDs are (of course) unique at runtime, thus we can safely use it
555 * as additional hardware-id for our device.
557 _itot(_getpid(), pid_as_string, 10);
558 strncpy (secondary_hwid, hwid, LINE_LEN);
559 strncat (secondary_hwid, pid_as_string, LINE_LEN);
561 if (-1 == (fd_tun = init_tun (hwid)))
563 fprintf (stderr, "Fatal: could not initialize virtual-interface %s with IPv6 %s/%s and IPv4 %s/%s\n",
572 if (0 != strcmp (argv[2], "-"))
574 const char *address = argv[2];
575 long prefix_len = atol (argv[3]);
577 if ((prefix_len < 1) || (prefix_len > 127))
579 fprintf (stderr, "Fatal: prefix_len out of range\n");
583 set_address6 (address, prefix_len);
586 if (0 != strcmp (argv[4], "-"))
588 const char *address = argv[4];
589 const char *mask = argv[5];
591 set_address4 (NULL, address, mask);
594 if (setup_interface ())
600 uid_t uid = getuid ();
601 if (0 != setresuid (uid, uid, uid))
603 fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno));
609 /*if (SIG_ERR == signal (SIGPIPE, SIG_IGN))
611 fprintf (stderr, "Failed to protect against SIGPIPE: %s\n",
613 // no exit, we might as well die with SIGPIPE should it ever happen