first batch of license fixes (boring)
[oweals/gnunet.git] / src / nat / gnunet-service-nat_helper.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2009, 2010, 2011, 2016 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your 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      Affero General Public License for more details.
14 */
15
16 /**
17  * @file nat/gnunet-service-nat_helper.c
18  * @brief runs the gnunet-helper-nat-server
19  * @author Milan Bouchet-Valat
20  * @author Christian Grothoff
21  */
22 #include "platform.h"
23 #include "gnunet_util_lib.h"
24 #include "gnunet-service-nat_helper.h"
25
26
27 /**
28  * Information we keep per NAT helper process.
29  */
30 struct HelperContext
31 {
32
33   /**
34    * IP address we pass to the NAT helper.
35    */
36   struct in_addr internal_address;
37
38   /**
39    * Function to call if we receive a reversal request.
40    */
41   GN_ReversalCallback cb;
42
43   /**
44    * Closure for @e cb.
45    */
46   void *cb_cls;
47
48   /**
49    * How long do we wait for restarting a crashed gnunet-helper-nat-server?
50    */
51   struct GNUNET_TIME_Relative server_retry_delay;
52
53   /**
54    * ID of select gnunet-helper-nat-server stdout read task
55    */
56   struct GNUNET_SCHEDULER_Task *server_read_task;
57
58   /**
59    * The process id of the server process (if behind NAT)
60    */
61   struct GNUNET_OS_Process *server_proc;
62
63   /**
64    * stdout pipe handle for the gnunet-helper-nat-server process
65    */
66   struct GNUNET_DISK_PipeHandle *server_stdout;
67
68   /**
69    * stdout file handle (for reading) for the gnunet-helper-nat-server process
70    */
71   const struct GNUNET_DISK_FileHandle *server_stdout_handle;
72 };
73
74
75 /**
76  * Task that restarts the gnunet-helper-nat-server process after a crash
77  * after a certain delay.
78  *
79  * @param cls a `struct HelperContext`
80  */
81 static void
82 restart_nat_server (void *cls);
83
84
85 /**
86  * Try again starting the helper later
87  *
88  * @param h context of the helper
89  */
90 static void
91 try_again (struct HelperContext *h)
92 {
93   GNUNET_assert (NULL == h->server_read_task);
94   h->server_retry_delay
95     = GNUNET_TIME_STD_BACKOFF (h->server_retry_delay);
96   h->server_read_task
97     = GNUNET_SCHEDULER_add_delayed (h->server_retry_delay,
98                                     &restart_nat_server,
99                                     h);
100 }
101
102
103 /**
104  * We have been notified that gnunet-helper-nat-server has written
105  * something to stdout.  Handle the output, then reschedule this
106  * function to be called again once more is available.
107  *
108  * @param cls the `struct HelperContext`
109  */
110 static void
111 nat_server_read (void *cls)
112 {
113   struct HelperContext *h = cls;
114   char mybuf[40];
115   ssize_t bytes;
116   int port;
117   const char *port_start;
118   struct sockaddr_in sin_addr;
119
120   h->server_read_task = NULL;
121   memset (mybuf,
122           0,
123           sizeof (mybuf));
124   bytes
125     = GNUNET_DISK_file_read (h->server_stdout_handle,
126                              mybuf,
127                              sizeof (mybuf));
128   if (bytes < 1)
129   {
130     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
131                 "Finished reading from server stdout with code: %d\n",
132                 (int) bytes);
133     if (0 != GNUNET_OS_process_kill (h->server_proc,
134                                      GNUNET_TERM_SIG))
135       GNUNET_log_from_strerror (GNUNET_ERROR_TYPE_WARNING,
136                                 "nat",
137                                 "kill");
138     GNUNET_OS_process_wait (h->server_proc);
139     GNUNET_OS_process_destroy (h->server_proc);
140     h->server_proc = NULL;
141     GNUNET_DISK_pipe_close (h->server_stdout);
142     h->server_stdout = NULL;
143     h->server_stdout_handle = NULL;
144     try_again (h);
145     return;
146   }
147
148   port_start = NULL;
149   for (size_t i = 0; i < sizeof (mybuf); i++)
150   {
151     if (mybuf[i] == '\n')
152     {
153       mybuf[i] = '\0';
154       break;
155     }
156     if ((mybuf[i] == ':') && (i + 1 < sizeof (mybuf)))
157     {
158       mybuf[i] = '\0';
159       port_start = &mybuf[i + 1];
160     }
161   }
162
163   /* construct socket address of sender */
164   memset (&sin_addr,
165           0,
166           sizeof (sin_addr));
167   sin_addr.sin_family = AF_INET;
168 #if HAVE_SOCKADDR_IN_SIN_LEN
169   sin_addr.sin_len = sizeof (sin_addr);
170 #endif
171   if ( (NULL == port_start) ||
172        (1 != SSCANF (port_start,
173                      "%d",
174                      &port)) ||
175        (-1 == inet_pton (AF_INET,
176                          mybuf,
177                          &sin_addr.sin_addr)))
178   {
179     /* should we restart gnunet-helper-nat-server? */
180     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
181                 _("gnunet-helper-nat-server generated malformed address `%s'\n"),
182                 mybuf);
183     h->server_read_task
184       = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
185                                         h->server_stdout_handle,
186                                         &nat_server_read,
187                                         h);
188     return;
189   }
190   sin_addr.sin_port = htons ((uint16_t) port);
191   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
192               "gnunet-helper-nat-server read: %s:%d\n",
193               mybuf,
194               port);
195   h->cb (h->cb_cls,
196          &sin_addr);
197   h->server_read_task
198     = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
199                                       h->server_stdout_handle,
200                                       &nat_server_read,
201                                       h);
202 }
203
204
205 /**
206  * Task that restarts the gnunet-helper-nat-server process after a crash
207  * after a certain delay.
208  *
209  * @param cls a `struct HelperContext`
210  */
211 static void
212 restart_nat_server (void *cls)
213 {
214   struct HelperContext *h = cls;
215   char *binary;
216   char ia[INET_ADDRSTRLEN];
217
218   h->server_read_task = NULL;
219   GNUNET_assert (NULL !=
220                  inet_ntop (AF_INET,
221                             &h->internal_address,
222                             ia,
223                             sizeof (ia)));
224   /* Start the server process */
225   binary
226     = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-nat-server");
227   if (GNUNET_YES !=
228       GNUNET_OS_check_helper_binary (binary,
229                                      GNUNET_YES,
230                                      ia))
231   {
232     /* move instantly to max delay, as this is unlikely to be fixed */
233     h->server_retry_delay
234       = GNUNET_TIME_STD_EXPONENTIAL_BACKOFF_THRESHOLD;
235     GNUNET_free (binary);
236     try_again (h);
237     return;
238   }
239   h->server_stdout
240     = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES,
241                         GNUNET_NO, GNUNET_YES);
242   if (NULL == h->server_stdout)
243   {
244     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
245                          "pipe");
246     GNUNET_free (binary);
247     try_again (h);
248     return;
249   }
250   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
251               "Starting `%s' at `%s'\n",
252               "gnunet-helper-nat-server",
253               ia);
254   h->server_proc
255     = GNUNET_OS_start_process (GNUNET_NO,
256                                0,
257                                NULL,
258                                h->server_stdout,
259                                NULL,
260                                binary,
261                                "gnunet-helper-nat-server",
262                                ia,
263                                NULL);
264   GNUNET_free (binary);
265   if (NULL == h->server_proc)
266   {
267     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
268                 _("Failed to start %s\n"),
269                 "gnunet-helper-nat-server");
270     GNUNET_DISK_pipe_close (h->server_stdout);
271     h->server_stdout = NULL;
272     try_again (h);
273     return;
274   }
275   /* Close the write end of the read pipe */
276   GNUNET_DISK_pipe_close_end (h->server_stdout,
277                               GNUNET_DISK_PIPE_END_WRITE);
278   h->server_stdout_handle
279     = GNUNET_DISK_pipe_handle (h->server_stdout,
280                                GNUNET_DISK_PIPE_END_READ);
281   h->server_read_task
282     = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
283                                       h->server_stdout_handle,
284                                       &nat_server_read,
285                                       h);
286 }
287
288
289 /**
290  * Start the gnunet-helper-nat-server and process incoming
291  * requests.
292  *
293  * @param internal_address
294  * @param cb function to call if we receive a request
295  * @param cb_cls closure for @a cb
296  * @return NULL on error
297  */
298 struct HelperContext *
299 GN_start_gnunet_nat_server_ (const struct in_addr *internal_address,
300                              GN_ReversalCallback cb,
301                              void *cb_cls)
302 {
303   struct HelperContext *h;
304
305   h = GNUNET_new (struct HelperContext);
306   h->cb = cb;
307   h->cb_cls = cb_cls;
308   h->internal_address = *internal_address;
309   restart_nat_server (h);
310   if (NULL == h->server_stdout)
311   {
312     GN_stop_gnunet_nat_server_ (h);
313     return NULL;
314   }
315   return h;
316 }
317
318
319 /**
320  * Start the gnunet-helper-nat-server and process incoming
321  * requests.
322  *
323  * @param h helper context to stop
324  */
325 void
326 GN_stop_gnunet_nat_server_ (struct HelperContext *h)
327 {
328   if (NULL != h->server_read_task)
329   {
330     GNUNET_SCHEDULER_cancel (h->server_read_task);
331     h->server_read_task = NULL;
332   }
333   if (NULL != h->server_proc)
334   {
335     if (0 != GNUNET_OS_process_kill (h->server_proc,
336                                      GNUNET_TERM_SIG))
337       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
338                            "kill");
339     GNUNET_OS_process_wait (h->server_proc);
340     GNUNET_OS_process_destroy (h->server_proc);
341     h->server_proc = NULL;
342     GNUNET_DISK_pipe_close (h->server_stdout);
343     h->server_stdout = NULL;
344     h->server_stdout_handle = NULL;
345   }
346   if (NULL != h->server_stdout)
347   {
348     GNUNET_DISK_pipe_close (h->server_stdout);
349     h->server_stdout = NULL;
350     h->server_stdout_handle = NULL;
351   }
352   GNUNET_free (h);
353 }
354
355
356 /**
357  * We want to connect to a peer that is behind NAT.  Run the
358  * gnunet-helper-nat-client to send dummy ICMP responses to cause
359  * that peer to connect to us (connection reversal).
360  *
361  * @param internal_address out internal address to use
362  * @param internal_port port to use
363  * @param remote_v4 the address of the peer (IPv4-only)
364  * @return #GNUNET_SYSERR on error,
365  *         #GNUNET_OK otherwise
366  */
367 int
368 GN_request_connection_reversal (const struct in_addr *internal_address,
369                                 uint16_t internal_port,
370                                 const struct in_addr *remote_v4)
371 {
372   char intv4[INET_ADDRSTRLEN];
373   char remv4[INET_ADDRSTRLEN];
374   char port_as_string[6];
375   struct GNUNET_OS_Process *proc;
376   char *binary;
377
378   if (NULL == inet_ntop (AF_INET,
379                          internal_address,
380                          intv4,
381                          INET_ADDRSTRLEN))
382   {
383     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
384                          "inet_ntop");
385     return GNUNET_SYSERR;
386   }
387   if (NULL == inet_ntop (AF_INET,
388                          remote_v4,
389                          remv4,
390                          INET_ADDRSTRLEN))
391   {
392     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
393                          "inet_ntop");
394     return GNUNET_SYSERR;
395   }
396   GNUNET_snprintf (port_as_string,
397                    sizeof (port_as_string),
398                    "%d",
399                    internal_port);
400   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
401               "Running gnunet-helper-nat-client %s %s %u\n",
402               intv4,
403               remv4,
404               internal_port);
405   binary
406     = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-nat-client");
407   proc
408     = GNUNET_OS_start_process (GNUNET_NO,
409                                0,
410                                NULL,
411                                NULL,
412                                NULL,
413                                binary,
414                                "gnunet-helper-nat-client",
415                                intv4,
416                                remv4,
417                                port_as_string,
418                                NULL);
419   GNUNET_free (binary);
420   if (NULL == proc)
421     return GNUNET_SYSERR;
422   /* we know that the gnunet-helper-nat-client will terminate virtually
423    * instantly */
424   GNUNET_OS_process_wait (proc);
425   GNUNET_OS_process_destroy (proc);
426   return GNUNET_OK;
427 }
428
429
430 /* end of gnunet-service-nat_helper.c */