last XDG change: use GNUNET_RUNTIME_DIR instead of /tmp for UNIXPATHs by default
[oweals/gnunet.git] / src / vpn / gnunet-vpn.c
1 /*
2      This file is part of GNUnet.
3      (C) 2012 Christian Grothoff (and other contributing authors)
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 src/vpn/gnunet-vpn.c
23  * @brief Tool to manually request VPN tunnels to be created
24  * @author Christian Grothoff
25  */
26
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet_vpn_service.h"
30
31
32 /**
33  * Handle to vpn service.
34  */
35 static struct GNUNET_VPN_Handle *handle;
36
37 /**
38  * Opaque redirection request handle.
39  */
40 static struct GNUNET_VPN_RedirectionRequest *request;
41
42 /**
43  * Option -p: destination peer identity for service
44  */
45 static char *peer_id;
46
47 /**
48  * Option -s: service name (hash to get service descriptor)
49  */
50 static char *service_name;
51
52 /**
53  * Option -i: target IP
54  */
55 static char *target_ip;
56
57 /**
58  * Option -4: IPv4 requested.
59  */
60 static int ipv4;
61
62 /**
63  * Option -6: IPv6 requested.
64  */
65 static int ipv6;
66
67 /**
68  * Option -t: TCP requested.
69  */
70 static int tcp;
71
72 /**
73  * Option -u: UDP requested.
74  */
75 static int udp;
76
77 /**
78  * Selected level of verbosity.
79  */
80 static int verbosity;
81
82 /**
83  * Global return value.
84  */
85 static int ret;
86
87 /**
88  * Option '-d': duration of the mapping
89  */
90 static struct GNUNET_TIME_Relative duration = { 5 * 60 * 1000} ;
91
92
93 /**
94  * Shutdown.
95  */
96 static void
97 do_disconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
98 {
99   if (NULL != request)
100   {
101     GNUNET_VPN_cancel_request (request);
102     request = NULL;
103   }
104   if (NULL != handle)
105   {
106     GNUNET_VPN_disconnect (handle);
107     handle = NULL;
108   }
109   GNUNET_free_non_null (peer_id);
110   GNUNET_free_non_null (service_name);
111   GNUNET_free_non_null (target_ip);
112 }
113
114
115 /**
116  * Callback invoked from the VPN service once a redirection is
117  * available.  Provides the IP address that can now be used to
118  * reach the requested destination.
119  *
120  * @param cls closure
121  * @param af address family, AF_INET or AF_INET6; AF_UNSPEC on error;
122  *                will match 'result_af' from the request
123  * @param address IP address (struct in_addr or struct in_addr6, depending on 'af')
124  *                that the VPN allocated for the redirection;
125  *                traffic to this IP will now be redirected to the
126  *                specified target peer; NULL on error
127  */
128 static void
129 allocation_cb (void *cls,
130                int af,
131                const void *address)
132 {
133   char buf[INET6_ADDRSTRLEN];
134
135   request = NULL;
136   switch (af)
137   {
138   case AF_INET6:
139   case AF_INET:
140     FPRINTF (stdout,
141              "%s\n",
142              inet_ntop (af, address, buf, sizeof (buf)));
143     break;
144   case AF_UNSPEC:
145     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
146                 _("Error creating tunnel\n"));
147     ret = 1;
148     break;
149   default:
150     break;
151   }
152   GNUNET_SCHEDULER_shutdown ();
153 }
154
155
156 /**
157  * Main function that will be run by the scheduler.
158  *
159  * @param cls closure
160  * @param args remaining command-line arguments
161  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
162  * @param cfg configuration
163  */
164 static void
165 run (void *cls, char *const *args, const char *cfgfile,
166      const struct GNUNET_CONFIGURATION_Handle *cfg)
167 {
168   int dst_af;
169   int req_af;
170   struct GNUNET_PeerIdentity peer;
171   struct GNUNET_HashCode sd;
172   const void *addr;
173   struct in_addr v4;
174   struct in6_addr v6;
175   uint8_t protocol;
176   struct GNUNET_TIME_Absolute etime;
177
178   etime = GNUNET_TIME_relative_to_absolute (duration);
179   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
180                                 &do_disconnect, NULL);
181   handle = GNUNET_VPN_connect (cfg);
182   if (NULL == handle)
183     goto error;
184   req_af = AF_UNSPEC;
185   if (ipv4)
186   {
187     if (ipv6)
188     {
189       FPRINTF (stderr, _("Option `%s' makes no sense with option `%s'.\n"),
190                "-4", "-6");
191       goto error;
192     }
193     req_af = AF_INET;
194   }
195   if (ipv6)
196     req_af = AF_INET6;
197
198   if (NULL == target_ip)
199   {
200     if (NULL == service_name)
201     {
202       FPRINTF (stderr, _("Option `%s' or `%s' is required.\n"),
203                "-i", "-s");
204       goto error;
205     }
206     if (NULL == peer_id)
207     {
208       FPRINTF (stderr, _("Option `%s' is required when using option `%s'.\n"),
209                "-p", "-s");
210       goto error;
211     }
212     if (! (tcp | udp) )
213     {
214       FPRINTF (stderr, _("Option `%s' or `%s' is required when using option `%s'.\n"),
215                "-t", "-u", "-s");
216       goto error;
217     }
218     if (tcp & udp)
219     {
220       FPRINTF (stderr, _("Option `%s' makes no sense with option `%s'.\n"),
221                "-t", "-u");
222       goto error;
223     }
224     if (tcp)
225       protocol = IPPROTO_TCP;
226     if (udp)
227       protocol = IPPROTO_UDP;
228     if (GNUNET_OK !=
229         GNUNET_CRYPTO_ecc_public_sign_key_from_string (peer_id,
230                                                        strlen (peer_id),
231                                                        &peer.public_key))
232     {
233       FPRINTF (stderr, _("`%s' is not a valid peer identifier.\n"),
234                peer_id);
235       goto error;
236     }
237     GNUNET_CRYPTO_hash (service_name,
238                         strlen (service_name),
239                         &sd);
240     request = GNUNET_VPN_redirect_to_peer (handle,
241                                            req_af,
242                                            protocol,
243                                            &peer,
244                                            &sd,
245                                            etime,
246                                            &allocation_cb, NULL);
247   }
248   else
249   {
250     if (1 != inet_pton (AF_INET6, target_ip, &v6))
251     {
252       if (1 != inet_pton (AF_INET, target_ip, &v4))
253       {
254         FPRINTF (stderr, _("`%s' is not a valid IP address.\n"),
255                  target_ip);
256         goto error;
257       }
258       else
259       {
260         dst_af = AF_INET;
261         addr = &v4;
262       }
263     }
264     else
265     {
266       dst_af = AF_INET6;
267       addr = &v6;
268     }
269     request = GNUNET_VPN_redirect_to_ip (handle,
270                                          req_af,
271                                          dst_af,
272                                          addr,
273                                          etime,
274                                          &allocation_cb, NULL);
275   }
276   return;
277
278  error:
279   GNUNET_SCHEDULER_shutdown ();
280   ret = 1;
281 }
282
283
284 int
285 main (int argc, char *const *argv)
286 {
287   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
288     {'4', "ipv4", NULL,
289      gettext_noop ("request that result should be an IPv4 address"),
290      0, &GNUNET_GETOPT_set_one, &ipv4},
291     {'6', "ipv6", NULL,
292      gettext_noop ("request that result should be an IPv6 address"),
293      0, &GNUNET_GETOPT_set_one, &ipv6},
294     {'d', "duration", "TIME",
295      gettext_noop ("how long should the mapping be valid for new tunnels?"),
296      1, &GNUNET_GETOPT_set_relative_time, &duration},
297     {'i', "ip", "IP",
298      gettext_noop ("destination IP for the tunnel"),
299      1, &GNUNET_GETOPT_set_string, &target_ip},
300     {'p', "peer", "PEERID",
301      gettext_noop ("peer offering the service we would like to access"),
302      1, &GNUNET_GETOPT_set_string, &peer_id},
303     {'s', "service", "NAME",
304      gettext_noop ("name of the service we would like to access"),
305      1, &GNUNET_GETOPT_set_string, &service_name},
306     {'t', "tcp", NULL,
307      gettext_noop ("service is offered via TCP"),
308      0, &GNUNET_GETOPT_set_one, &tcp},
309     {'u', "udp", NULL,
310      gettext_noop ("service is offered via UDP"),
311      0, &GNUNET_GETOPT_set_one, &udp},
312
313     GNUNET_GETOPT_OPTION_VERBOSE (&verbosity),
314     GNUNET_GETOPT_OPTION_END
315   };
316   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
317     return 2;
318
319   ret = (GNUNET_OK ==
320          GNUNET_PROGRAM_run (argc, argv, "gnunet-vpn",
321                              gettext_noop
322                              ("Setup tunnels via VPN."), options,
323                               &run, NULL)) ? ret : 1;
324   GNUNET_free ((void *) argv);
325   return ret;
326 }
327
328
329 /* end of gnunet-vpn.c */