-options to play with
[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 <tchar.h>
36 #include <windows.h>
37 #include <setupapi.h>
38 #include "platform.h"
39
40 /**
41  * Need 'struct GNUNET_MessageHeader'.
42  */
43 #include "gnunet_common.h"
44
45 /**
46  * Need VPN message types.
47  */
48 #include "gnunet_protocols.h"
49
50 /**
51  * Should we print (interesting|debug) messages that can happen during
52  * normal operation?
53  */
54 #define DEBUG GNUNET_NO
55
56 /**
57  * Maximum size of a GNUnet message (GNUNET_SERVER_MAX_MESSAGE_SIZE)
58  */
59 #define MAX_SIZE 65536
60
61 /**
62  * Name or Path+Name of our driver in Unicode.
63  * The .sys and .cat files HAVE to be in the same location as this file!
64  */
65 #define INF_FILE _T("tapw32.inf")
66
67 /**
68  * Hardware ID used in the inf-file. 
69  * This might change over time, as openvpn advances their driver
70  */
71 #define HARDWARE_ID _T("TAP0901")
72
73 /** 
74  * This is our own local instance of a virtual network interface
75  * It is (somewhat) equivalent to using tun/tap in unixoid systems
76  * 
77  * Upon initialization, we create such an device node.
78  * Upon termination, we remove it again.
79  * 
80  * If we crash this device might stay around.
81  */
82 static HDEVINFO DeviceInfo = INVALID_HANDLE_VALUE;
83
84 /**
85  * Registry Key we hand over to windows to spawn a new virtual interface
86  */
87 static SP_DEVINFO_DATA DeviceNode;
88
89 /**
90  * Class-tag of our virtual device
91  */
92 static TCHAR class[128];
93
94 /**
95  * GUID of our virtual device in the form of 
96  * {12345678-1234-1234-1234-123456789abc} - in hex
97  */
98 static GUID guid;
99
100 /**
101  * @brief Sets the IPv6-Address given in address on the interface dev
102  *
103  * @param address the IPv6-Address
104  * @param prefix_len the length of the network-prefix
105  */
106 static void
107 set_address6 (const char *address, unsigned long prefix_len)
108 {
109   int fd = -1;
110
111   /*
112    * parse the new address
113    */
114
115   /*
116    * Get the index of the if
117    */
118
119   /*
120    * Set the address
121    */
122
123   /*
124    * Get the flags
125    */
126
127
128   /*
129    * Add the UP and RUNNING flags
130    */
131
132
133   if (0 != close (fd))
134     {
135       fprintf (stderr, "close failed: %s\n", strerror (errno));
136       exit (1);
137     }
138 }
139
140 /**
141  * @brief Sets the IPv4-Address given in address on the interface dev
142  *
143  * @param dev the interface to configure
144  * @param address the IPv4-Address
145  * @param mask the netmask
146  */
147 static void
148 set_address4 (const char *dev, const char *address, const char *mask)
149 {
150   int fd = -1;
151
152   /*
153    * Parse the address
154    */
155
156   /*
157    * Set the address
158    */
159
160   /*
161    * Parse the netmask
162    */
163
164
165   /*
166    * Set the netmask
167    */
168
169
170   /*
171    * Get the flags
172    */
173
174
175   /*
176    * Add the UP and RUNNING flags
177    */
178
179
180   if (0 != close (fd))
181     {
182       fprintf (stderr, "close failed: %s\n", strerror (errno));
183       (void) close (fd);
184       exit (1);
185     }
186 }
187
188
189 /**
190  * Setup a new virtual interface to use for tunneling. 
191  * 
192  * @return: TRUE if setup was successful, else FALSE
193  */
194 static boolean
195 setup_interface ()
196 {
197   /*
198    * where to find our inf-file. (+ the "full" path, after windows found")
199    * 
200    * We do not directly input all the props here, because openvpn will update
201    * these details over time.
202    */
203   TCHAR InfFilePath[MAX_PATH];
204   TCHAR hwIdList[LINE_LEN + 4];
205
206   /** 
207    * Locate the inf-file, we need to store it somewhere where the system can
208    * find it. A good choice would be CWD/PDW or %WINDIR$\system32\
209    * 
210    * TODO: Find a more sane way to do this!
211    * TODO: How about win64 in the future? 
212    */
213   GetFullPathName (INF_FILE, MAX_PATH, InfFilePath, NULL);
214
215   /** 
216    * Set the device's hardware ID and add it to a list.
217    * This information will later on identify this device in registry. 
218    * 
219    * TODO: Currently we just use TAP0901 as HWID, 
220    * but we might want to add additional information
221    */
222   strncpy (hwIdList, HARDWARE_ID, LINE_LEN);
223
224   /** 
225    * Bootstrap our device info using the drivers inf-file
226    */
227   if (!SetupDiGetINFClass (InfFilePath,
228                            &guid,
229                            class, sizeof (class) / sizeof (TCHAR),
230                            NULL))
231       return FALSE;
232   
233   /** 
234    * Collect all the other needed information... 
235    * let the system fill our this form 
236    */
237   DeviceInfo = SetupDiCreateDeviceInfoList (&guid, NULL);
238   if (DeviceInfo == INVALID_HANDLE_VALUE)
239       goto cleanup;
240   
241   DeviceNode.cbSize = sizeof (SP_DEVINFO_DATA);
242   if (!SetupDiCreateDeviceInfo (DeviceInfo,
243                                 class,
244                                 &guid,
245                                 NULL,
246                                 NULL,
247                                 DICD_GENERATE_ID,
248                                 &DeviceNode))
249       goto cleanup;
250   
251   /* Deploy all the information collected into the registry */
252   if (!SetupDiSetDeviceRegistryProperty (DeviceInfo,
253                                          &DeviceNode,
254                                          SPDRP_HARDWAREID,
255                                          (LPBYTE) hwIdList,
256                                          (lstrlen (hwIdList) + 2) * sizeof (TCHAR)))
257       goto cleanup;
258   
259   /* Install our new class(=device) into the system */
260   if (SetupDiCallClassInstaller (DIF_REGISTERDEVICE,
261                                  DeviceInfo,
262                                  &DeviceNode))
263       return TRUE;
264   
265   //disabled for debug-reasons...
266 cleanup:
267 //  GNUNET_free(DeviceInfo);
268   return FALSE;
269
270 }
271
272
273 /**
274  * Remove our new virtual interface to use for tunneling. 
275  * This function must be called AFTER setup_interface!
276  * 
277  * @return: TRUE if destruction was successful, else FALSE
278  */
279 static boolean
280 remove_interface ()
281 {
282   SP_REMOVEDEVICE_PARAMS remove;
283   
284   if (INVALID_HANDLE_VALUE == DeviceInfo)
285     return FALSE;
286   
287   remove.ClassInstallHeader.cbSize = sizeof (SP_CLASSINSTALL_HEADER);
288   remove.HwProfile = 0;
289   remove.Scope = DI_REMOVEDEVICE_GLOBAL;
290   remove.ClassInstallHeader.InstallFunction = DIF_REMOVE;
291   /*
292    * 1. Prepare our existing device information set, and place the 
293    *    uninstall related information into the structure
294    */
295   if (! SetupDiSetClassInstallParams (DeviceInfo,
296                                       (PSP_DEVINFO_DATA) &DeviceNode,
297                                       &remove.ClassInstallHeader,
298                                       sizeof (remove)))
299     return FALSE;
300   /*
301    * 2. Uninstall the virtual interface using the class installer
302    */
303   if (! SetupDiCallClassInstaller (DIF_REMOVE, 
304                                    DeviceInfo, 
305                                    (PSP_DEVINFO_DATA) &DeviceNode))
306     return FALSE;
307   return TRUE;
308 }
309
310 /**
311  * Creates a tun-interface called dev;
312  *
313  * @param hwid is asumed to point to a TCHAR[LINE_LEN]
314  *        if *dev == '\\0', uses the name supplied by the kernel;
315  * @return the fd to the tun or -1 on error
316  */
317 static int
318 init_tun (TCHAR *hwid)
319 {
320   int fd;
321
322   if (NULL == hwid)
323     {
324       errno = EINVAL;
325       return -1;
326     }
327
328   if (FALSE == setup_interface()){
329       errno = ENODEV;
330       return -1;
331     }
332   
333   
334
335   return fd;
336 }
337
338 /**
339  * Start forwarding to and from the tunnel.
340  *
341  * @param fd_tun tunnel FD
342  */
343 static void
344 run (int fd_tun)
345 {
346   /*
347    * The buffer filled by reading from fd_tun
348    */
349   unsigned char buftun[MAX_SIZE];
350   ssize_t buftun_size = 0;
351   unsigned char *buftun_read = NULL;
352
353   /*
354    * The buffer filled by reading from stdin
355    */
356   unsigned char bufin[MAX_SIZE];
357   ssize_t bufin_size = 0;
358   ssize_t bufin_rpos = 0;
359   unsigned char *bufin_read = NULL;
360   /* Hello, I am a stub function! I did my job, yay me! */
361
362 }
363
364
365 /**
366  * Open VPN tunnel interface.
367  *
368  * @param argc must be 6
369  * @param argv 0: binary name (gnunet-helper-vpn)
370  *             1: tunnel interface name (gnunet-vpn) (unused, can be used as additional HWID in the future)
371  *             2: IPv6 address (::1), "-" to disable
372  *             3: IPv6 netmask length in bits (64), ignored if #2 is "-"
373  *             4: IPv4 address (1.2.3.4), "-" to disable
374  *             5: IPv4 netmask (255.255.0.0), ignored if #4 is "-"
375  */
376 int
377 main (int argc, char **argv)
378 {
379   TCHAR hwid[LINE_LEN];
380   int fd_tun;
381   int global_ret;
382
383   if (6 != argc)
384     {
385       fprintf (stderr, "Fatal: must supply 5 arguments!\n");
386       return 1;
387     }
388
389    strncpy (hwid, argv[1], LINE_LEN);
390    hwid[LINE_LEN - 1] = _T('\0');
391
392   if (-1 == (fd_tun = init_tun (hwid)))
393     {
394       fprintf (stderr, "Fatal: could not initialize virtual-interface %s with IPv6 %s/%s and IPv4 %s/%s\n",
395                hwid,
396                argv[2],
397                argv[3],
398                argv[4],
399                argv[5]);
400       return 1;
401     }
402
403   if (0 != strcmp (argv[2], "-"))
404     {
405       const char *address = argv[2];
406       long prefix_len = atol (argv[3]);
407
408       if ((prefix_len < 1) || (prefix_len > 127))
409         {
410           fprintf (stderr, "Fatal: prefix_len out of range\n");
411           return 1;
412         }
413
414       set_address6 (address, prefix_len);
415     }
416
417   if (0 != strcmp (argv[4], "-"))
418     {
419       const char *address = argv[4];
420       const char *mask = argv[5];
421
422       set_address4 (NULL, address, mask);
423     }
424
425   if (setup_interface ())
426     {
427       ;
428     }
429
430   /*
431   uid_t uid = getuid ();
432   if (0 != setresuid (uid, uid, uid))
433   {
434     fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno));
435     global_ret = 2;
436     goto cleanup;
437   }
438    */
439
440   /*if (SIG_ERR == signal (SIGPIPE, SIG_IGN))
441   {
442     fprintf (stderr, "Failed to protect against SIGPIPE: %s\n",
443              strerror (errno));
444     // no exit, we might as well die with SIGPIPE should it ever happen 
445   }
446    */
447   //run (fd_tun);
448   global_ret = 0;
449 cleanup:
450   //close (fd_tun);
451   return global_ret;
452 }