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