reworked vpn-helper to now use regular(!) char, instead of wchar,
[oweals/gnunet.git] / src / vpn / gnunet-helper-vpn-windows.c
1 /*
2      This file is part of GNUnet.
3      (C) 2010, 2012 Christian Grothoff
4
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.
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      General Public License for more details.
14
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.
19  */
20
21 /**
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
27  *
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):
31  *
32  */
33
34 #include <stdio.h>
35 #include <windows.h>
36 #include <setupapi.h>
37 #include <ddk/cfgmgr32.h>
38 #include "platform.h"
39 #include <Winsock2.h>
40
41 /**
42  * Need 'struct GNUNET_MessageHeader'.
43  */
44 #include "gnunet_common.h"
45
46 /**
47  * Need VPN message types.
48  */
49 #include "gnunet_protocols.h"
50
51 /**
52  * Should we print (interesting|debug) messages that can happen during
53  * normal operation?
54  */
55 #define DEBUG GNUNET_NO
56
57 /**
58  * Maximum size of a GNUnet message (GNUNET_SERVER_MAX_MESSAGE_SIZE)
59  */
60 #define MAX_SIZE 65536
61
62 /**
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!
65  */
66 #define INF_FILE "tapw32.inf"
67
68 /**
69  * Hardware ID used in the inf-file. 
70  * This might change over time, as openvpn advances their driver
71  */
72 #define HARDWARE_ID "TAP0901"
73
74 /**
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
77  */
78 #define INTERFACE_REGISTRY_LOCATION "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
79
80 /**
81  * Our local process' PID. Used for creating a sufficiently unique additional 
82  * hardware ID for our device.
83  */
84 static char secondary_hwid[LINE_LEN / 2];
85
86 /**
87  * Device's visible Name, used to identify a network device in netsh.
88  * eg: "Local Area Connection 9"
89  */
90 static char device_visible_name[256];
91
92 /** 
93  * This is our own local instance of a virtual network interface
94  * It is (somewhat) equivalent to using tun/tap in unixoid systems
95  * 
96  * Upon initialization, we create such an device node.
97  * Upon termination, we remove it again.
98  * 
99  * If we crash this device might stay around.
100  */
101 static HDEVINFO DeviceInfo = INVALID_HANDLE_VALUE;
102
103 /**
104  * Registry Key we hand over to windows to spawn a new virtual interface
105  */
106 static SP_DEVINFO_DATA DeviceNode;
107
108 /**
109  * Class-tag of our virtual device
110  */
111 static char class[128];
112
113 /**
114  * GUID of our virtual device in the form of 
115  * {12345678-1234-1234-1234-123456789abc} - in hex
116  */
117 static GUID guid;
118
119
120 /**
121  * inet_pton() wrapper for WSAStringToAddress()
122  *
123  * this is needed as long as we support WinXP, because only Vista+ support 
124  * inet_pton at all, and mingw does not yet offer inet_pton/ntop at all
125  * 
126  * @param af - IN - the aftype this address is supposed to be (v4/v6) 
127  * @param src - IN - the presentation form of the address
128  * @param dst - OUT - the numerical form of the address
129  * @return 0 on success, 1 on failure
130  */
131 #if WINVER >= 0x0600
132 int inet_pton (int af, const char *src, void *dst);
133 #else
134 int
135 inet_pton (int af, const char *src, void *dst)
136 {
137   struct sockaddr_storage addr;
138   int size = sizeof (addr);
139   char local_copy[INET6_ADDRSTRLEN + 1];
140
141   ZeroMemory (&addr, sizeof (addr));
142   /* stupid non-const API */
143   strncpy (local_copy, src, INET6_ADDRSTRLEN + 1);
144   local_copy[INET6_ADDRSTRLEN] = 0;
145
146   if (WSAStringToAddressA (local_copy, af, NULL, (struct sockaddr *) &addr, &size) == 0)
147     {
148       switch (af)
149         {
150         case AF_INET:
151           *(struct in_addr *) dst = ((struct sockaddr_in *) &addr)->sin_addr;
152           return 1;
153         case AF_INET6:
154           *(struct in6_addr *) dst = ((struct sockaddr_in6 *) &addr)->sin6_addr;
155           return 1;
156         }
157     }
158   return 0;
159 }
160 #endif
161 /**
162  * Wrapper for executing a shellcommand in windows.
163  * 
164  * @param command - the command + parameters to execute
165  * @return * exitcode of the program executed, 
166  *         * EINVAL (cmd/file not found)
167  *         * EPIPE (could not read STDOUT)
168  */
169 static int
170 execute_shellcommand (char * command)
171 {
172   FILE *pipe;
173
174   if (NULL == command ||
175       NULL == (pipe = _popen (command, "rt")))
176     return EINVAL;
177
178 #ifdef TESTING
179   {
180     char output[LINE_LEN];
181
182     printf ("executed command: %s", command);
183     while (NULL != fgets (output, sizeof (output), pipe))
184       printf (output);
185   }
186 #endif
187
188   if (!feof (pipe))
189     return EPIPE;
190
191   return _pclose (pipe);
192 }
193
194 /**
195  * @brief Sets the IPv6-Address given in address on the interface dev
196  *
197  * @param address the IPv6-Address
198  * @param prefix_len the length of the network-prefix
199  */
200 static void
201 set_address6 (const char *address, unsigned long prefix_len)
202 {
203   int ret = EINVAL;
204   char command[LINE_LEN];
205   struct sockaddr_in6 sa6;
206
207   /*
208    * parse the new address
209    */
210   memset (&sa6, 0, sizeof (struct sockaddr_in6));
211   sa6.sin6_family = AF_INET6;
212   if (1 != inet_pton (AF_INET6, address, &sa6.sin6_addr.s6_addr))
213     {
214       fprintf (stderr, "Failed to parse address `%s': %s\n", address,
215                strerror (errno));
216       exit (1);
217     }
218
219   /*
220    * prepare the command
221    */
222   snprintf (command, LINE_LEN,
223               "netsh interface ipv6 add address \"%s\" %s/%d",
224               device_visible_name, address, prefix_len);
225   /*
226    * Set the address
227    */
228   ret = execute_shellcommand (command);
229
230   /* Did it work?*/
231   if (0 != ret)
232     {
233       fprintf (stderr, "Setting IPv6 address failed: %s\n", strerror (ret));
234       exit (1); // FIXME: return error code, shut down interface / unload driver
235     }
236 }
237
238 /**
239  * @brief Sets the IPv4-Address given in address on the interface dev
240  *
241  * @param dev the interface to configure
242  * @param address the IPv4-Address
243  * @param mask the netmask
244  */
245 static void
246 set_address4 (const char *address, const char *mask)
247 {
248   int ret = EINVAL;
249   char command[LINE_LEN];
250
251   struct sockaddr_in addr;
252   addr.sin_family = AF_INET;
253
254   /*
255    * Parse the address
256    */
257   if (1 != inet_pton (AF_INET, address, &addr.sin_addr.s_addr))
258     {
259       fprintf (stderr, "Failed to parse address `%s': %s\n", address,
260                strerror (errno));
261       exit (1);
262     }
263
264   /*
265    * prepare the command
266    */
267   snprintf (command, LINE_LEN,
268               "netsh interface ipv4 add address \"%s\" %s %s",
269               device_visible_name, address, mask);
270   /*
271    * Set the address
272    */
273   ret = execute_shellcommand (command);
274
275   /* Did it work?*/
276   if (0 != ret)
277     {
278       fprintf (stderr, "Setting IPv4 address failed: %s\n", strerror (ret));
279       exit (1); // FIXME: return error code, shut down interface / unload driver
280     }
281 }
282
283 /**
284  * Setup a new virtual interface to use for tunneling. 
285  * 
286  * @return: TRUE if setup was successful, else FALSE
287  */
288 static boolean
289 setup_interface ()
290 {
291   /*
292    * where to find our inf-file. (+ the "full" path, after windows found")
293    * 
294    * We do not directly input all the props here, because openvpn will update
295    * these details over time.
296    */
297   char inf_file_path[MAX_PATH];
298   char hwidlist[LINE_LEN + 4];
299
300   int str_lenth = 0;
301
302
303   /** 
304    * Set the device's hardware ID and add it to a list.
305    * This information will later on identify this device in registry. 
306    * 
307    * TODO: Currently we just use TAP0901 as HWID, 
308    * but we might want to add additional information
309    */
310   strncpy (hwidlist, HARDWARE_ID, LINE_LEN);
311   /**
312    * this is kind of over-complicated, but allows keeps things independent of 
313    * how the openvpn-hwid is actually stored. 
314    * 
315    * A HWID list is double-\0 terminated and \0 separated
316    */
317   str_lenth = strlen (hwidlist) + 1;
318   strncpy (&hwidlist[str_lenth], secondary_hwid, LINE_LEN - str_lenth);
319
320   /** 
321    * Locate the inf-file, we need to store it somewhere where the system can
322    * find it. A good choice would be CWD/PDW or %WINDIR$\system32\
323    * 
324    * TODO: How about win64 in the future? 
325    *       We need to use a different driver for amd64/i386 !
326    */
327   GetFullPathNameA (INF_FILE, MAX_PATH, inf_file_path, NULL);
328
329   /** 
330    * Bootstrap our device info using the drivers inf-file
331    */
332   if (!SetupDiGetINFClassA (inf_file_path,
333                            &guid,
334                            class, sizeof (class) / sizeof (char),
335                            NULL))
336     return FALSE;
337
338   /** 
339    * Collect all the other needed information... 
340    * let the system fill our this form 
341    */
342   DeviceInfo = SetupDiCreateDeviceInfoList (&guid, NULL);
343   if (DeviceInfo == INVALID_HANDLE_VALUE)
344     return FALSE;
345
346   DeviceNode.cbSize = sizeof (SP_DEVINFO_DATA);
347   if (!SetupDiCreateDeviceInfoA (DeviceInfo,
348                                 class,
349                                 &guid,
350                                 NULL,
351                                 NULL,
352                                 DICD_GENERATE_ID,
353                                 &DeviceNode))
354     return FALSE;
355
356   /* Deploy all the information collected into the registry */
357   if (!SetupDiSetDeviceRegistryPropertyA (DeviceInfo,
358                                          &DeviceNode,
359                                          SPDRP_HARDWAREID,
360                                          (LPBYTE) hwidlist,
361                                          (strlen (hwidlist) + 2) * sizeof (char)))
362     return FALSE;
363
364   /* Install our new class(=device) into the system */
365   if (!SetupDiCallClassInstaller (DIF_REGISTERDEVICE,
366                                   DeviceInfo,
367                                   &DeviceNode))
368     return FALSE;
369
370   return TRUE;
371 }
372
373 /**
374  * Remove our new virtual interface to use for tunneling. 
375  * This function must be called AFTER setup_interface!
376  * 
377  * @return: TRUE if destruction was successful, else FALSE
378  */
379 static boolean
380 remove_interface ()
381 {
382   SP_REMOVEDEVICE_PARAMS remove;
383
384   if (INVALID_HANDLE_VALUE == DeviceInfo)
385     return FALSE;
386
387   remove.ClassInstallHeader.cbSize = sizeof (SP_CLASSINSTALL_HEADER);
388   remove.HwProfile = 0;
389   remove.Scope = DI_REMOVEDEVICE_GLOBAL;
390   remove.ClassInstallHeader.InstallFunction = DIF_REMOVE;
391   /*
392    * 1. Prepare our existing device information set, and place the 
393    *    uninstall related information into the structure
394    */
395   if (!SetupDiSetClassInstallParamsA (DeviceInfo,
396                                      (PSP_DEVINFO_DATA) & DeviceNode,
397                                      &remove.ClassInstallHeader,
398                                      sizeof (remove)))
399     return FALSE;
400   /*
401    * 2. Uninstall the virtual interface using the class installer
402    */
403   if (!SetupDiCallClassInstaller (DIF_REMOVE,
404                                   DeviceInfo,
405                                   (PSP_DEVINFO_DATA) & DeviceNode))
406     return FALSE;
407
408   SetupDiDestroyDeviceInfoList (DeviceInfo);
409
410   return TRUE;
411 }
412
413 /**
414  * Do all the lookup necessary to retrieve the inteface's actual name
415  * off the registry. 
416  * 
417  * @return: TRUE if we were able to lookup the interface's name, else FALSE
418  */
419 static boolean
420 resolve_interface_name ()
421 {
422
423   SP_DEVINFO_LIST_DETAIL_DATA device_details;
424   char pnp_instance_id [MAX_DEVICE_ID_LEN];
425   HKEY adapter_key_handle;
426   LONG status;
427   DWORD len;
428   int i = 0;
429   boolean retval = FALSE;
430   char adapter[] = INTERFACE_REGISTRY_LOCATION;
431
432   /* We can obtain the PNP instance ID from our setupapi handle */
433   device_details.cbSize = sizeof (device_details);
434   if (CR_SUCCESS != CM_Get_Device_ID_ExA (DeviceNode.DevInst,
435                                          (PCHAR) pnp_instance_id,
436                                          MAX_DEVICE_ID_LEN,
437                                          0, //must be 0
438                                          NULL)) //hMachine, we are local
439     return FALSE;
440
441   /* Now we can use this ID to locate the correct networks interface in registry */
442   if (ERROR_SUCCESS != RegOpenKeyExA (
443                                      HKEY_LOCAL_MACHINE,
444                                      adapter,
445                                      0,
446                                      KEY_READ,
447                                      &adapter_key_handle))
448     return FALSE;
449
450   /* Of course there is a multitude of entries here, with arbitrary names, 
451    * thus we need to iterate through there.
452    */
453   while (!retval)
454     {
455       char instance_key[256];
456       char query_key [256];
457       HKEY instance_key_handle;
458       char pnpinstanceid_name[] = "PnpInstanceID";
459       char pnpinstanceid_value[256];
460       char adaptername_name[] = "Name";
461       DWORD data_type;
462
463       len = sizeof (adapter_key_handle);
464       /* optain a subkey of {4D36E972-E325-11CE-BFC1-08002BE10318} */
465       status = RegEnumKeyExA (
466                              adapter_key_handle,
467                              i,
468                              instance_key,
469                              &len,
470                              NULL,
471                              NULL,
472                              NULL,
473                              NULL);
474
475       /* this may fail due to one of two reasons: 
476        * we are at the end of the list*/
477       if (ERROR_NO_MORE_ITEMS == status)
478         break;
479       // * we found a broken registry key, continue with the next key.
480       if (ERROR_SUCCESS != status)
481         goto cleanup;
482
483       /* prepare our new query string: */
484       snprintf (query_key, 256, "%s\\%s\\Connection",
485                   INTERFACE_REGISTRY_LOCATION,
486                   instance_key);
487
488       /* look inside instance_key\\Connection */
489       status = RegOpenKeyExA (
490                              HKEY_LOCAL_MACHINE,
491                              query_key,
492                              0,
493                              KEY_READ,
494                              &instance_key_handle);
495
496       if (status != ERROR_SUCCESS)
497         continue;
498
499       /* now, read our PnpInstanceID */
500       len = sizeof (pnpinstanceid_value);
501       status = RegQueryValueExA (instance_key_handle,
502                                 pnpinstanceid_name,
503                                 NULL, //reserved, always NULL according to MSDN
504                                 &data_type,
505                                 (LPBYTE) pnpinstanceid_value,
506                                 &len);
507
508       if (status != ERROR_SUCCESS || data_type != REG_SZ)
509         goto cleanup;
510
511       /* compare the value we got to our devices PNPInstanceID*/
512       if (0 != strncmp (pnpinstanceid_value, pnp_instance_id,
513                          sizeof (pnpinstanceid_value) / sizeof (char)))
514         goto cleanup;
515
516       len = sizeof (device_visible_name);
517       status = RegQueryValueExA (
518                                 instance_key_handle,
519                                 adaptername_name,
520                                 NULL, //reserved, always NULL according to MSDN
521                                 &data_type,
522                                 (LPBYTE) device_visible_name,
523                                 &len);
524
525       if (status == ERROR_SUCCESS && data_type == REG_SZ)
526         {
527           retval = TRUE;
528         }
529 cleanup:
530       RegCloseKey (instance_key_handle);
531
532       ++i;
533     }
534
535   RegCloseKey (adapter_key_handle);
536
537   return retval;
538 }
539
540 /**
541  * Creates a tun-interface called dev;
542  *
543  * @return the fd to the tun or -1 on error
544  */
545 static int
546 init_tun ()
547 {
548   int fd = -1;
549
550   if (!setup_interface ())
551     {
552       errno = ENODEV;
553       return -1;
554     }
555
556   if (!resolve_interface_name ())
557     {
558       errno = ENODEV;
559       return -1;
560     }
561
562   //openvpn
563   /* get driver MTU */
564   // tun.c:2869
565   
566   /* tun up: */
567   // tun.c: 3024
568   
569   return fd;
570 }
571
572 /**
573  * Start forwarding to and from the tunnel.
574  *
575  * @param fd_tun tunnel FD
576  */
577 static void
578 run (int fd_tun)
579 {
580   /*
581    * The buffer filled by reading from fd_tun
582    */
583   unsigned char buftun[MAX_SIZE];
584   ssize_t buftun_size = 0;
585   unsigned char *buftun_read = NULL;
586
587   /*
588    * The buffer filled by reading from stdin
589    */
590   unsigned char bufin[MAX_SIZE];
591   ssize_t bufin_size = 0;
592   ssize_t bufin_rpos = 0;
593   unsigned char *bufin_read = NULL;
594   /* Hello, I am a stub function! I did my job, yay me! */
595   
596   //openvpn  
597   
598   // mainloop:
599   // tunnel_point_to_point
600   //openvpn.c:62
601   
602   /* setup ansync IO */
603   //forward.c: 1515
604
605   
606   //teardown:
607   //init.c:3472
608 }
609
610 /**
611  * Open VPN tunnel interface.
612  *
613  * @param argc must be 6
614  * @param argv 0: binary name (gnunet-helper-vpn)
615  *             1: tunnel interface name (gnunet-vpn)
616  *             2: IPv6 address (::1), "-" to disable
617  *             3: IPv6 netmask length in bits (64), ignored if #2 is "-"
618  *             4: IPv4 address (1.2.3.4), "-" to disable
619  *             5: IPv4 netmask (255.255.0.0), ignored if #4 is "-"
620  */
621 int
622 main (int argc, char **argv)
623 {
624   char hwid[LINE_LEN];
625   int fd_tun;
626   int global_ret;
627
628   if (6 != argc)
629     {
630       fprintf (stderr, "Fatal: must supply 5 arguments!\n");
631       return 1;
632     }
633
634   strncpy (hwid, argv[1], LINE_LEN);
635   hwid[LINE_LEN - 1] = '\0';
636
637   /* 
638    * We use our PID for finding/resolving the control-panel name of our virtual 
639    * device. PIDs are (of course) unique at runtime, thus we can safely use it 
640    * as additional hardware-id for our device.
641    */
642   snprintf (secondary_hwid, LINE_LEN / 2, "%s-%d",
643               hwid,
644               _getpid ());
645
646   if (-1 == (fd_tun = init_tun ()))
647     {
648       fprintf (stderr, "Fatal: could not initialize virtual-interface %s with IPv6 %s/%s and IPv4 %s/%s\n",
649                hwid,
650                argv[2],
651                argv[3],
652                argv[4],
653                argv[5]);
654       return 1;
655     }
656
657   if (0 != strcmp (argv[2], "-"))
658     {
659       const char *address = argv[2];
660       long prefix_len = atol (argv[3]);
661
662       if ((prefix_len < 1) || (prefix_len > 127))
663         {
664           fprintf (stderr, "Fatal: prefix_len out of range\n");
665           global_ret = -1;
666           goto cleanup;
667         }
668
669       set_address6 (address, prefix_len);
670     }
671
672   if (0 != strcmp (argv[4], "-"))
673     {
674       const char *address = argv[4];
675       const char *mask = argv[5];
676
677       set_address4 (address, mask);
678     }
679
680   //eventuell: 
681   // tap_allow_nonadmin_access
682   //tun.c:2023
683
684   run (fd_tun);
685   global_ret = 0;
686 cleanup:
687   remove_interface ();
688
689   return global_ret;
690 }