In Windows, pipes, files and the console have to be accessed
[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 "tap-windows.h"
40 #include <Winsock2.h>
41
42 /**
43  * Need 'struct GNUNET_MessageHeader'.
44  */
45 #include "gnunet_common.h"
46
47 /**
48  * Need VPN message types.
49  */
50 #include "gnunet_protocols.h"
51
52 /**
53  * Should we print (interesting|debug) messages that can happen during
54  * normal operation?
55  */
56 #define DEBUG GNUNET_NO
57
58 /**
59  * Maximum size of a GNUnet message (GNUNET_SERVER_MAX_MESSAGE_SIZE)
60  */
61 #define MAX_SIZE 65536
62
63 /**
64  * Name or Path+Name of our driver in Unicode.
65  * The .sys and .cat files HAVE to be in the same location as this file!
66  */
67 #define INF_FILE "tapw32.inf"
68
69 /**
70  * Hardware ID used in the inf-file. 
71  * This might change over time, as openvpn advances their driver
72  */
73 #define HARDWARE_ID "TAP0901"
74
75 /**
76  * Component ID if our driver
77  */
78 #define TAP_WIN_COMPONENT_ID "tap0901"
79
80 /**
81  * Minimum major-id of the driver version we can work with
82  */
83 #define TAP_WIN_MIN_MAJOR 9
84
85 /**
86  * Minimum minor-id of the driver version we can work with. 
87  * v <= 7 has buggy IPv6.
88  * v == 8 is broken for small IPv4 Packets
89  */
90 #define TAP_WIN_MIN_MINOR 9
91
92 /**
93  * Time in seconds to wait for our virtual device to go up after telling it to do so.
94  * 
95  * openvpn doesn't specify a value, 4 seems sane for testing, even for openwrt
96  * (in fact, 4 was chosen by a fair dice roll...)
97  */
98 #define TAP32_POSTUP_WAITTIME 4
99
100 /**
101  * Location of the network interface list resides in registry.
102  * TODO: is this fixed on all version of windows? Checked with XP and 7
103  */
104 #define INTERFACE_REGISTRY_LOCATION "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
105
106 /**
107  * Our local process' PID. Used for creating a sufficiently unique additional 
108  * hardware ID for our device.
109  */
110 static char secondary_hwid[LINE_LEN / 2];
111
112 /**
113  * Device's visible Name, used to identify a network device in netsh.
114  * eg: "Local Area Connection 9"
115  */
116 static char device_visible_name[256];
117
118 /** 
119  * This is our own local instance of a virtual network interface
120  * It is (somewhat) equivalent to using tun/tap in unixoid systems
121  * 
122  * Upon initialization, we create such an device node.
123  * Upon termination, we remove it again.
124  * 
125  * If we crash this device might stay around.
126  */
127 static HDEVINFO DeviceInfo = INVALID_HANDLE_VALUE;
128
129 /**
130  * Registry Key we hand over to windows to spawn a new virtual interface
131  */
132 static SP_DEVINFO_DATA DeviceNode;
133
134 /**
135  * GUID of our virtual device in the form of 
136  * {12345678-1234-1234-1234-123456789abc} - in hex
137  */
138 static char device_guid[256];
139
140 /* Overlapped IO Begins here (warning: nasty!) */
141
142 /** 
143  * A IO Object + read/writebuffer + buffer-size for windows asynchronous IO handling
144  */
145 struct io_facility
146 {
147   DWORD handle_type;
148   HANDLE handle;
149   
150   BOOL path_open; // BOOL is winbool, NOT boolean!
151   int facility_state;
152   BOOL status;
153   
154   OVERLAPPED overlapped;
155   DWORD buffer_size;
156   unsigned char buffer[MAX_SIZE];
157 };
158
159 /** 
160  * Operlapped IO states for facility objects
161  */
162 #define IOSTATE_FAILED          -1 /* overlapped I/O has failed, stop processing */
163 #define IOSTATE_READY            0 /* overlapped I/O is ready for work */
164 #define IOSTATE_QUEUED           1 /* overlapped I/O has been queued */
165 #define IOSTATE_WAITING          3 /* overlapped I/O has finished, but is waiting for it's write-partner */
166
167 #if WINVER < 0x0600
168
169 /**
170  * inet_pton() wrapper for WSAStringToAddress()
171  *
172  * this is needed as long as we support WinXP, because only Vista+ support 
173  * inet_pton at all, and mingw does not yet offer inet_pton/ntop at all
174  * 
175  * @param af - IN - the aftype this address is supposed to be (v4/v6) 
176  * @param src - IN - the presentation form of the address
177  * @param dst - OUT - the numerical form of the address
178  * @return 0 on success, 1 on failure
179  */
180 int
181 inet_pton (int af, const char *src, void *dst)
182 {
183   struct sockaddr_storage addr;
184   int size = sizeof (addr);
185   char local_copy[INET6_ADDRSTRLEN + 1];
186
187   ZeroMemory (&addr, sizeof (addr));
188   /* stupid non-const API */
189   strncpy (local_copy, src, INET6_ADDRSTRLEN + 1);
190   local_copy[INET6_ADDRSTRLEN] = 0;
191
192   if (WSAStringToAddressA (local_copy, af, NULL, (struct sockaddr *) &addr, &size) == 0)
193     {
194       switch (af)
195         {
196         case AF_INET:
197           *(struct in_addr *) dst = ((struct sockaddr_in *) &addr)->sin_addr;
198           return 1;
199         case AF_INET6:
200           *(struct in6_addr *) dst = ((struct sockaddr_in6 *) &addr)->sin6_addr;
201           return 1;
202         }
203     }
204   return 0;
205 }
206 #endif
207
208 /**
209  * Wrapper for executing a shellcommand in windows.
210  * 
211  * @param command - the command + parameters to execute
212  * @return * exitcode of the program executed, 
213  *         * EINVAL (cmd/file not found)
214  *         * EPIPE (could not read STDOUT)
215  */
216 static int
217 execute_shellcommand (char * command)
218 {
219   FILE *pipe;
220
221   if (NULL == command ||
222       NULL == (pipe = _popen (command, "rt")))
223     return EINVAL;
224
225 #ifdef TESTING
226   {
227     char output[LINE_LEN];
228
229     printf ("executed command: %s", command);
230     while (NULL != fgets (output, sizeof (output), pipe))
231       printf (output);
232   }
233 #endif
234
235   if (!feof (pipe))
236     return EPIPE;
237
238   return _pclose (pipe);
239 }
240
241 /**
242  * @brief Sets the IPv6-Address given in address on the interface dev
243  *
244  * @param address the IPv6-Address
245  * @param prefix_len the length of the network-prefix
246  */
247 static void
248 set_address6 (const char *address, unsigned long prefix_len)
249 {
250   int ret = EINVAL;
251   char command[LINE_LEN];
252   struct sockaddr_in6 sa6;
253
254   /*
255    * parse the new address
256    */
257   memset (&sa6, 0, sizeof (struct sockaddr_in6));
258   sa6.sin6_family = AF_INET6;
259   if (1 != inet_pton (AF_INET6, address, &sa6.sin6_addr.s6_addr))
260     {
261       fprintf (stderr, "Failed to parse address `%s': %s\n", address,
262                strerror (errno));
263       exit (1);
264     }
265
266   /*
267    * prepare the command
268    */
269   snprintf (command, LINE_LEN,
270             "netsh interface ipv6 add address \"%s\" %s/%d",
271             device_visible_name, address, prefix_len);
272   /*
273    * Set the address
274    */
275   ret = execute_shellcommand (command);
276
277   /* Did it work?*/
278   if (0 != ret)
279     {
280       fprintf (stderr, "Setting IPv6 address failed: %s\n", strerror (ret));
281       exit (1); // FIXME: return error code, shut down interface / unload driver
282     }
283 }
284
285 /**
286  * @brief Sets the IPv4-Address given in address on the interface dev
287  *
288  * @param dev the interface to configure
289  * @param address the IPv4-Address
290  * @param mask the netmask
291  */
292 static void
293 set_address4 (const char *address, const char *mask)
294 {
295   int ret = EINVAL;
296   char command[LINE_LEN];
297
298   struct sockaddr_in addr;
299   addr.sin_family = AF_INET;
300
301   /*
302    * Parse the address
303    */
304   if (1 != inet_pton (AF_INET, address, &addr.sin_addr.s_addr))
305     {
306       fprintf (stderr, "Failed to parse address `%s': %s\n", address,
307                strerror (errno));
308       exit (1);
309     }
310
311   /*
312    * prepare the command
313    */
314   snprintf (command, LINE_LEN,
315             "netsh interface ipv4 add address \"%s\" %s %s",
316             device_visible_name, address, mask);
317   /*
318    * Set the address
319    */
320   ret = execute_shellcommand (command);
321
322   /* Did it work?*/
323   if (0 != ret)
324     {
325       fprintf (stderr, "Setting IPv4 address failed: %s\n", strerror (ret));
326       exit (1); // FIXME: return error code, shut down interface / unload driver
327     }
328 }
329
330 /**
331  * Setup a new virtual interface to use for tunneling. 
332  * 
333  * @return: TRUE if setup was successful, else FALSE
334  */
335 static boolean
336 setup_interface ()
337 {
338   /*
339    * where to find our inf-file. (+ the "full" path, after windows found")
340    * 
341    * We do not directly input all the props here, because openvpn will update
342    * these details over time.
343    */
344   char inf_file_path[MAX_PATH];
345   char hwidlist[LINE_LEN + 4];
346   char class_name[128];
347   GUID class_guid;
348   int str_lenth = 0;
349
350   /** 
351    * Set the device's hardware ID and add it to a list.
352    * This information will later on identify this device in registry. 
353    * 
354    * TODO: Currently we just use TAP0901 as HWID, 
355    * but we might want to add additional information
356    */
357   strncpy (hwidlist, HARDWARE_ID, LINE_LEN);
358   /**
359    * this is kind of over-complicated, but allows keeps things independent of 
360    * how the openvpn-hwid is actually stored. 
361    * 
362    * A HWID list is double-\0 terminated and \0 separated
363    */
364   str_lenth = strlen (hwidlist) + 1;
365   strncpy (&hwidlist[str_lenth], secondary_hwid, LINE_LEN - str_lenth);
366
367   /** 
368    * Locate the inf-file, we need to store it somewhere where the system can
369    * find it. A good choice would be CWD/PDW or %WINDIR$\system32\
370    * 
371    * TODO: How about win64 in the future? 
372    *       We need to use a different driver for amd64/i386 !
373    */
374   GetFullPathNameA (INF_FILE, MAX_PATH, inf_file_path, NULL);
375
376   /** 
377    * Bootstrap our device info using the drivers inf-file
378    */
379   if (!SetupDiGetINFClassA (inf_file_path,
380                             &class_guid,
381                             class_name, sizeof (class_name) / sizeof (char),
382                             NULL))
383     return FALSE;
384
385   /** 
386    * Collect all the other needed information... 
387    * let the system fill our this form 
388    */
389   DeviceInfo = SetupDiCreateDeviceInfoList (&class_guid, NULL);
390   if (DeviceInfo == INVALID_HANDLE_VALUE)
391     return FALSE;
392
393   DeviceNode.cbSize = sizeof (SP_DEVINFO_DATA);
394   if (!SetupDiCreateDeviceInfoA (DeviceInfo,
395                                  class_name,
396                                  &class_guid,
397                                  NULL,
398                                  NULL,
399                                  DICD_GENERATE_ID,
400                                  &DeviceNode))
401     return FALSE;
402
403   /* Deploy all the information collected into the registry */
404   if (!SetupDiSetDeviceRegistryPropertyA (DeviceInfo,
405                                           &DeviceNode,
406                                           SPDRP_HARDWAREID,
407                                           (LPBYTE) hwidlist,
408                                           (strlen (hwidlist) + 2) * sizeof (char)))
409     return FALSE;
410
411   /* Install our new class(=device) into the system */
412   if (!SetupDiCallClassInstaller (DIF_REGISTERDEVICE,
413                                   DeviceInfo,
414                                   &DeviceNode))
415     return FALSE;
416
417   return TRUE;
418 }
419
420 /**
421  * Remove our new virtual interface to use for tunneling. 
422  * This function must be called AFTER setup_interface!
423  * 
424  * @return: TRUE if destruction was successful, else FALSE
425  */
426 static boolean
427 remove_interface ()
428 {
429   SP_REMOVEDEVICE_PARAMS remove;
430
431   if (INVALID_HANDLE_VALUE == DeviceInfo)
432     return FALSE;
433
434   remove.ClassInstallHeader.cbSize = sizeof (SP_CLASSINSTALL_HEADER);
435   remove.HwProfile = 0;
436   remove.Scope = DI_REMOVEDEVICE_GLOBAL;
437   remove.ClassInstallHeader.InstallFunction = DIF_REMOVE;
438   /*
439    * 1. Prepare our existing device information set, and place the 
440    *    uninstall related information into the structure
441    */
442   if (!SetupDiSetClassInstallParamsA (DeviceInfo,
443                                       (PSP_DEVINFO_DATA) & DeviceNode,
444                                       &remove.ClassInstallHeader,
445                                       sizeof (remove)))
446     return FALSE;
447   /*
448    * 2. Uninstall the virtual interface using the class installer
449    */
450   if (!SetupDiCallClassInstaller (DIF_REMOVE,
451                                   DeviceInfo,
452                                   (PSP_DEVINFO_DATA) & DeviceNode))
453     return FALSE;
454
455   SetupDiDestroyDeviceInfoList (DeviceInfo);
456
457   return TRUE;
458 }
459
460 /**
461  * Do all the lookup necessary to retrieve the inteface's actual name
462  * off the registry. 
463  * 
464  * @return: TRUE if we were able to lookup the interface's name, else FALSE
465  */
466 static boolean
467 resolve_interface_name ()
468 {
469
470   SP_DEVINFO_LIST_DETAIL_DATA device_details;
471   char pnp_instance_id [MAX_DEVICE_ID_LEN];
472   HKEY adapter_key_handle;
473   LONG status;
474   DWORD len;
475   int i = 0;
476   boolean retval = FALSE;
477   char adapter[] = INTERFACE_REGISTRY_LOCATION;
478
479   /* We can obtain the PNP instance ID from our setupapi handle */
480   device_details.cbSize = sizeof (device_details);
481   if (CR_SUCCESS != CM_Get_Device_ID_ExA (DeviceNode.DevInst,
482                                           (PCHAR) pnp_instance_id,
483                                           MAX_DEVICE_ID_LEN,
484                                           0, //must be 0
485                                           NULL)) //hMachine, we are local
486     return FALSE;
487
488   /* Now we can use this ID to locate the correct networks interface in registry */
489   if (ERROR_SUCCESS != RegOpenKeyExA (
490                                       HKEY_LOCAL_MACHINE,
491                                       adapter,
492                                       0,
493                                       KEY_READ,
494                                       &adapter_key_handle))
495     return FALSE;
496
497   /* Of course there is a multitude of entries here, with arbitrary names, 
498    * thus we need to iterate through there.
499    */
500   while (!retval)
501     {
502       char instance_key[256];
503       char query_key [256];
504       HKEY instance_key_handle;
505       char pnpinstanceid_name[] = "PnpInstanceID";
506       char pnpinstanceid_value[256];
507       char adaptername_name[] = "Name";
508       DWORD data_type;
509
510       len = sizeof (adapter_key_handle);
511       /* optain a subkey of {4D36E972-E325-11CE-BFC1-08002BE10318} */
512       status = RegEnumKeyExA (
513                               adapter_key_handle,
514                               i,
515                               instance_key,
516                               &len,
517                               NULL,
518                               NULL,
519                               NULL,
520                               NULL);
521
522       /* this may fail due to one of two reasons: 
523        * we are at the end of the list*/
524       if (ERROR_NO_MORE_ITEMS == status)
525         break;
526       // * we found a broken registry key, continue with the next key.
527       if (ERROR_SUCCESS != status)
528         goto cleanup;
529
530       /* prepare our new query string: */
531       snprintf (query_key, 256, "%s\\%s\\Connection",
532                 INTERFACE_REGISTRY_LOCATION,
533                 instance_key);
534
535       /* look inside instance_key\\Connection */
536       status = RegOpenKeyExA (
537                               HKEY_LOCAL_MACHINE,
538                               query_key,
539                               0,
540                               KEY_READ,
541                               &instance_key_handle);
542
543       if (status != ERROR_SUCCESS)
544         continue;
545
546       /* now, read our PnpInstanceID */
547       len = sizeof (pnpinstanceid_value);
548       status = RegQueryValueExA (instance_key_handle,
549                                  pnpinstanceid_name,
550                                  NULL, //reserved, always NULL according to MSDN
551                                  &data_type,
552                                  (LPBYTE) pnpinstanceid_value,
553                                  &len);
554
555       if (status != ERROR_SUCCESS || data_type != REG_SZ)
556         goto cleanup;
557
558       /* compare the value we got to our devices PNPInstanceID*/
559       if (0 != strncmp (pnpinstanceid_value, pnp_instance_id,
560                         sizeof (pnpinstanceid_value) / sizeof (char)))
561         goto cleanup;
562
563       len = sizeof (device_visible_name);
564       status = RegQueryValueExA (
565                                  instance_key_handle,
566                                  adaptername_name,
567                                  NULL, //reserved, always NULL according to MSDN
568                                  &data_type,
569                                  (LPBYTE) device_visible_name,
570                                  &len);
571
572       if (status != ERROR_SUCCESS || data_type != REG_SZ)
573         goto cleanup;
574
575       /* 
576        * we have successfully found OUR instance, 
577        * save the device GUID before exiting
578        */
579
580       strncpy (device_guid, instance_key, 256);
581       retval = TRUE;
582
583 cleanup:
584       RegCloseKey (instance_key_handle);
585
586       ++i;
587     }
588
589   RegCloseKey (adapter_key_handle);
590
591   return retval;
592 }
593
594 static boolean
595 check_tapw32_version (HANDLE handle)
596 {
597   {
598     ULONG version[3];
599     DWORD len;
600     memset (&(version), 0, sizeof (version));
601
602
603     if (DeviceIoControl (handle, TAP_WIN_IOCTL_GET_VERSION,
604                          &version, sizeof (version),
605                          &version, sizeof (version), &len, NULL))
606       {
607 #ifdef TESTING
608         fprintf (stderr, "TAP-Windows Driver Version %d.%d %s",
609                  (int) version[0],
610                  (int) version[1],
611                  (version[2] ? "(DEBUG)" : ""));
612 #endif
613       }
614
615     if (version[0] != TAP_WIN_MIN_MAJOR || version[1] < TAP_WIN_MIN_MINOR)
616       {
617         fprintf (stderr, "ERROR:  This version of gnunet requires a TAP-Windows driver that is at least version %d.%d!\n",
618                  TAP_WIN_MIN_MAJOR,
619                  TAP_WIN_MIN_MINOR);
620         return FALSE;
621       }
622     return TRUE;
623   }
624 }
625
626 /**
627  * Creates a tun-interface called dev;
628  *
629  * @return the fd to the tun or -1 on error
630  */
631 static HANDLE
632 init_tun ()
633 {
634   char device_path[256];
635   HANDLE handle;
636
637   if (!setup_interface ())
638     {
639       errno = ENODEV;
640       return INVALID_HANDLE_VALUE;
641     }
642
643   if (!resolve_interface_name ())
644     {
645       errno = ENODEV;
646       return INVALID_HANDLE_VALUE;
647     }
648
649   /* Open Windows TAP-Windows adapter */
650   snprintf (device_path, sizeof (device_path), "%s%s%s",
651             USERMODEDEVICEDIR,
652             device_guid,
653             TAP_WIN_SUFFIX);
654
655   handle = CreateFile (
656                        device_path,
657                        GENERIC_READ | GENERIC_WRITE,
658                        0, /* was: FILE_SHARE_READ */
659                        0,
660                        OPEN_EXISTING,
661                        FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
662                        0
663                        );
664
665   if (handle == INVALID_HANDLE_VALUE)
666     {
667       fprintf (stderr, "CreateFile failed on TAP device: %s\n", device_path);
668       return handle;
669     }
670
671   /* get driver version info */
672   if (!check_tapw32_version (handle))
673     {
674       CloseHandle (handle);
675       return INVALID_HANDLE_VALUE;
676     }
677
678   /* TODO (opt?): get MTU-Size */
679
680   return handle;
681 }
682
683 static boolean
684 tun_up (HANDLE handle)
685 {
686   ULONG status = TRUE;
687   DWORD len;
688   if (DeviceIoControl (handle, TAP_WIN_IOCTL_SET_MEDIA_STATUS,
689                        &status, sizeof (status),
690                        &status, sizeof (status), &len, NULL))
691     {
692       fprintf (stderr, "The TAP-Windows driver ignored our request to set the interface UP (TAP_WIN_IOCTL_SET_MEDIA_STATUS DeviceIoControl call)!\n");
693       return FALSE;
694     }
695
696   /* Wait for the device to go UP, might take some time. */
697   Sleep ((TAP32_POSTUP_WAITTIME)*1000);
698
699   return TRUE;
700
701 }
702
703 static boolean
704 attempt_std_in (struct io_facility * std_in,
705                 struct io_facility * tap_write)
706 {
707
708   // We could use PeekConsoleInput() or WaitForSingleObject()
709   // however, the interwebs states that WaitForSingleObject with filehandles 
710   // might misbehave on some windows (unspecified which ones!).
711   // unfortunately, peekconsoleinput () just waits for KEYPRESS-event, which would never happen on a pipe or a file
712
713   // See:
714   // http://www.cplusplus.com/forum/windows/28837/
715   // http://stackoverflow.com/questions/4551644/using-overlapped-io-for-console-input
716   // http://cygwin.com/ml/cygwin/2012-05/msg00322.html
717
718   // possible soltion?
719   // http://stackoverflow.com/questions/3661106/overlapped-readfileex-on-child-process-redirected-stdout-never-fires
720
721   // we may read from STDIN, and no job was active
722   if (IOSTATE_READY == std_in->facility_state)
723     {
724
725     }
726     // we must complete a previous read from stdin, before doing more work
727   else if (IOSTATE_QUEUED == std_in->facility_state)
728     {
729       // there is some data to be read from STDIN! 
730       /*      if (PeekConsoleInput(stdin_handle,
731                                  &std_in->buffer[MAX_SIZE],
732                                  MAX_SIZE,
733                                  &std_in->buffer_size)){
734           
735           
736           
737               }*/
738       // else { // nothing to do, try again next time }
739     }
740
741   return TRUE;
742 }
743
744 static boolean
745 attempt_tap_read (struct io_facility * tap_read,
746                   struct io_facility * std_out)
747 {
748
749   if (IOSTATE_READY == tap_read->facility_state)
750     {
751       if (!ResetEvent (tap_read->overlapped.hEvent))
752         {
753           return FALSE;
754         }
755       tap_read->status = ReadFile (tap_read->handle,
756                                    &tap_read->buffer[MAX_SIZE],
757                                    MAX_SIZE,
758                                    &tap_read->buffer_size,
759                                    &tap_read->overlapped);
760
761       /* Check how the task is handled */
762       if (tap_read->status)
763         {/* async event processed immediately*/
764
765           /* reset event manually*/
766           if (!SetEvent (tap_read->overlapped.hEvent))
767             return FALSE;
768
769           /* we successfully read something from the TAP and now need to
770            * send it our via STDOUT. Is that possible at the moment? */
771           if (IOSTATE_READY == std_out->facility_state && 0 < tap_read->buffer_size)
772             { /* hand over this buffers content */
773               memcpy (std_out->buffer,
774                       tap_read->buffer,
775                       MAX_SIZE);
776               std_out->buffer_size = tap_read->buffer_size;
777               std_out->facility_state = IOSTATE_READY;
778             }
779           else if (0 < tap_read->buffer_size)
780             { /* If we have have read our buffer, wait for our write-partner*/
781               tap_read->facility_state = IOSTATE_WAITING;
782               // TODO: shall we attempt to fill our buffer or should we wait for our write-partner to finish?
783             }
784         }
785       else /* operation was either queued or failed*/
786         {
787           int err = GetLastError ();
788           if (ERROR_IO_PENDING == err)
789             { /* operation queued */
790               tap_read->facility_state = IOSTATE_QUEUED;
791             }
792           else
793             { /* error occurred, let the rest of the elements finish */
794               tap_read->path_open = FALSE;
795               tap_read->facility_state = IOSTATE_FAILED;
796             }
797         }
798     }
799     // We are queued and should check if the read has finished
800   else if (IOSTATE_QUEUED == tap_read->facility_state)
801     {
802       // there was an operation going on already, check if that has completed now.
803       tap_read->status = GetOverlappedResult (tap_read->handle,
804                                               &tap_read->overlapped,
805                                               &tap_read->buffer_size,
806                                               FALSE);
807       if (tap_read->status)
808         {/* successful return for a queued operation */
809           if (!ResetEvent (tap_read->overlapped.hEvent))
810             return FALSE;
811
812           /* we successfully read something from the TAP and now need to
813            * send it our via STDOUT. Is that possible at the moment? */
814           if (IOSTATE_READY == std_out->facility_state && 0 < tap_read->buffer_size)
815             { /* hand over this buffers content */
816               memcpy (std_out->buffer,
817                       tap_read->buffer,
818                       MAX_SIZE);
819               std_out->buffer_size = tap_read->buffer_size;
820               std_out->facility_state = IOSTATE_READY;
821               tap_read->facility_state = IOSTATE_READY;
822             }
823           else if (0 < tap_read->buffer_size)
824             { /* If we have have read our buffer, wait for our write-partner*/
825               tap_read->facility_state = IOSTATE_WAITING;
826               // TODO: shall we attempt to fill our buffer or should we wait for our write-partner to finish?
827             }
828         }
829       else
830         { /* operation still pending/queued or failed? */
831           int err = GetLastError ();
832           if (ERROR_IO_INCOMPLETE != err && ERROR_IO_PENDING != err)
833             { /* error occurred, let the rest of the elements finish */
834               tap_read->path_open = FALSE;
835               tap_read->facility_state = IOSTATE_FAILED;
836             }
837         }
838     }
839   return TRUE;
840 }
841
842 static boolean
843 attempt_tap_write (struct io_facility * tap_write,
844                    struct io_facility * std_in)
845 {
846   return TRUE;
847 }
848
849 static boolean
850 attempt_std_out (struct io_facility * std_out,
851                  struct io_facility * tap_read)
852 {
853   return TRUE;
854 }
855
856 /**
857  * Initialize a overlapped structure
858  * 
859  * @param elem the element to initilize
860  * @param initial_state the initial state for this instance
861  * @param signaled if the hEvent created should default to signaled or not
862  * @return true on success, else false
863  */
864 static boolean
865 initialize_io_facility (struct io_facility * elem,
866                                 BOOL initial_state,
867                                 BOOL signaled)
868 {
869
870   elem->path_open = TRUE;
871   elem->status = initial_state;
872   elem->handle_type = 0;
873   elem->handle = INVALID_HANDLE_VALUE;
874   elem->facility_state = 0;
875   elem->buffer_size = 0;
876   elem->overlapped.hEvent = CreateEvent (NULL, TRUE, signaled, NULL);
877   if (NULL == elem->overlapped.hEvent)
878     return FALSE;
879
880   return TRUE;
881 }
882
883 /**
884  * Start forwarding to and from the tunnel.
885  *
886  * @param fd_tun tunnel FD
887  */
888 static void
889 run (HANDLE tap_handle)
890 {
891   /* IO-Facility for reading from our virtual interface */
892   struct io_facility tap_read;
893   /* IO-Facility for writing to our virtual interface */
894   struct io_facility tap_write;
895   /* IO-Facility for reading from stdin */
896   struct io_facility std_in;
897   /* IO-Facility for writing to stdout */
898   struct io_facility std_out;
899
900   /* tun up: */
901   /* we do this HERE and not beforehand (in init_tun()), in contrast to openvpn
902    * to remove the need to flush the arp cache, handle DHCP and wrong IPs.
903    *  
904    * DHCP and such are all features we will never use in gnunet afaik.
905    * But for openvpn those are essential.
906    */
907   if (!tun_up (tap_handle))
908     goto teardown;
909
910   /* Initialize our overlapped IO structures*/
911   if (!(initialize_io_facility (&tap_read, TRUE, FALSE)
912         && initialize_io_facility (&tap_write, FALSE, TRUE)
913         && initialize_io_facility (&std_in, TRUE, FALSE)
914         && initialize_io_facility (&std_out, FALSE, TRUE)))
915     goto teardown;
916
917   /* Handles for STDIN and STDOUT */
918   std_in.handle = GetStdHandle (STD_INPUT_HANDLE);
919   std_out.handle = GetStdHandle (STD_OUTPUT_HANDLE);
920   tap_read.handle = tap_handle;
921   tap_write.handle = tap_handle;
922
923   /* 
924    * Find out the types of our handles. 
925    * This part is a problem, because in windows we need to handle files, 
926    * pipes and the console differently.
927    */
928   std_in.handle_type = GetFileType (std_in.handle);
929   std_out.handle_type = GetFileType (std_out.handle);
930   /* the tap handle is always a file, but we still set this for consistency */
931   tap_read.handle_type = FILE_TYPE_DISK;
932   tap_write.handle_type = FILE_TYPE_DISK;
933
934   //openvpn  
935   // Set Device to Subnet-Mode? 
936   // do we really need tun.c:2925 ?
937   // Why does openvpn assign IPv4's there??? Foobar??
938
939   // Setup should be complete here.
940   // If something is missing, check init.c:3400+
941
942   // mainloop:
943   // tunnel_point_to_point
944   // openvpn.c:62
945
946   while (std_in.path_open
947          || std_out.path_open
948          || tap_read.path_open
949          || tap_write.path_open)
950     {
951       /* perform READ from stdin if possible */
952       if ((std_in.path_open && tap_write.path_open)
953           || IOSTATE_QUEUED == std_in.facility_state)
954         if (!attempt_std_in (&std_in, &tap_write))
955           break;
956
957       /* perform READ from tap if possible */
958       if ((tap_read.path_open && std_out.path_open)
959           || IOSTATE_QUEUED == tap_read.facility_state)
960         if (!attempt_tap_read (&tap_read, &std_out))
961           break;
962
963       /* perform WRITE to tap if possible */
964       if (IOSTATE_READY == tap_write.facility_state && tap_write.path_open)
965         if (!attempt_tap_write (&tap_write, &std_in))
966           break;
967
968       /* perform WRITE to STDOUT if possible */
969       if (IOSTATE_READY == std_out.facility_state && std_out.path_open)
970         if (!attempt_std_out (&std_out, &tap_read))
971           break;
972
973       // check if any path is blocked
974     }
975 teardown:
976   ;
977   //init.c:3472
978 }
979
980 /**
981  * Open VPN tunnel interface.
982  *
983  * @param argc must be 6
984  * @param argv 0: binary name (gnunet-helper-vpn)
985  *             1: tunnel interface name (gnunet-vpn)
986  *             2: IPv6 address (::1), "-" to disable
987  *             3: IPv6 netmask length in bits (64), ignored if #2 is "-"
988  *             4: IPv4 address (1.2.3.4), "-" to disable
989  *             5: IPv4 netmask (255.255.0.0), ignored if #4 is "-"
990  */
991 int
992 main (int argc, char **argv)
993 {
994   char hwid[LINE_LEN];
995   HANDLE handle;
996   int global_ret;
997
998   if (6 != argc)
999     {
1000       fprintf (stderr, "Fatal: must supply 5 arguments!\n");
1001       return 1;
1002     }
1003
1004   strncpy (hwid, argv[1], LINE_LEN);
1005   hwid[LINE_LEN - 1] = '\0';
1006
1007   /* 
1008    * We use our PID for finding/resolving the control-panel name of our virtual 
1009    * device. PIDs are (of course) unique at runtime, thus we can safely use it 
1010    * as additional hardware-id for our device.
1011    */
1012   snprintf (secondary_hwid, LINE_LEN / 2, "%s-%d",
1013             hwid,
1014             _getpid ());
1015
1016   if (INVALID_HANDLE_VALUE == (handle = init_tun ()))
1017     {
1018       fprintf (stderr, "Fatal: could not initialize virtual-interface %s with IPv6 %s/%s and IPv4 %s/%s\n",
1019                hwid,
1020                argv[2],
1021                argv[3],
1022                argv[4],
1023                argv[5]);
1024       return 1;
1025     }
1026
1027   if (0 != strcmp (argv[2], "-"))
1028     {
1029       const char *address = argv[2];
1030       long prefix_len = atol (argv[3]);
1031
1032       if ((prefix_len < 1) || (prefix_len > 127))
1033         {
1034           fprintf (stderr, "Fatal: prefix_len out of range\n");
1035           global_ret = -1;
1036           goto cleanup;
1037         }
1038
1039       set_address6 (address, prefix_len);
1040     }
1041
1042   if (0 != strcmp (argv[4], "-"))
1043     {
1044       const char *address = argv[4];
1045       const char *mask = argv[5];
1046
1047       set_address4 (address, mask);
1048     }
1049
1050   //eventuell: 
1051   // tap_allow_nonadmin_access
1052   //tun.c:2023
1053
1054   run (handle);
1055   global_ret = 0;
1056 cleanup:
1057   remove_interface ();
1058
1059   return global_ret;
1060 }