2 This file is part of GNUnet.
3 Copyright (C) 2009, 2010, 2011, 2016 GNUnet e.V.
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.
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.
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., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
22 * @file nat/gnunet-service-nat_helper.c
23 * @brief runs the gnunet-helper-nat-server
24 * @author Milan Bouchet-Valat
25 * @author Christian Grothoff
28 #include "gnunet_util_lib.h"
29 #include "gnunet-service-nat_helper.h"
31 #define LOG(kind,...) GNUNET_log_from (kind, "nat", __VA_ARGS__)
34 * Information we keep per NAT helper process.
40 * IP address we pass to the NAT helper.
42 const char *internal_address;
45 * Function to call if we receive a reversal request.
47 GN_ReversalCallback cb;
55 * How long do we wait for restarting a crashed gnunet-helper-nat-server?
57 struct GNUNET_TIME_Relative server_retry_delay;
60 * ID of select gnunet-helper-nat-server stdout read task
62 struct GNUNET_SCHEDULER_Task *server_read_task;
65 * The process id of the server process (if behind NAT)
67 struct GNUNET_OS_Process *server_proc;
70 * stdout pipe handle for the gnunet-helper-nat-server process
72 struct GNUNET_DISK_PipeHandle *server_stdout;
75 * stdout file handle (for reading) for the gnunet-helper-nat-server process
77 const struct GNUNET_DISK_FileHandle *server_stdout_handle;
82 * Task that restarts the gnunet-helper-nat-server process after a crash
83 * after a certain delay.
85 * @param cls a `struct HelperContext`
88 restart_nat_server (void *cls);
92 * Try again starting the helper later
94 * @param h context of the helper
97 try_again (struct HelperContext *h)
99 GNUNET_assert (NULL == h->server_read_task);
100 h->server_retry_delay
101 = GNUNET_TIME_STD_BACKOFF (h->server_retry_delay);
103 = GNUNET_SCHEDULER_add_delayed (h->server_retry_delay,
110 * We have been notified that gnunet-helper-nat-server has written
111 * something to stdout. Handle the output, then reschedule this
112 * function to be called again once more is available.
114 * @param cls the `struct HelperContext`
117 nat_server_read (void *cls)
119 struct HelperContext *h = cls;
123 const char *port_start;
124 struct sockaddr_in sin_addr;
126 h->server_read_task = NULL;
131 = GNUNET_DISK_file_read (h->server_stdout_handle,
136 LOG (GNUNET_ERROR_TYPE_DEBUG,
137 "Finished reading from server stdout with code: %d\n",
139 if (0 != GNUNET_OS_process_kill (h->server_proc,
141 GNUNET_log_from_strerror (GNUNET_ERROR_TYPE_WARNING,
144 GNUNET_OS_process_wait (h->server_proc);
145 GNUNET_OS_process_destroy (h->server_proc);
146 h->server_proc = NULL;
147 GNUNET_DISK_pipe_close (h->server_stdout);
148 h->server_stdout = NULL;
149 h->server_stdout_handle = NULL;
155 for (size_t i = 0; i < sizeof (mybuf); i++)
157 if (mybuf[i] == '\n')
162 if ((mybuf[i] == ':') && (i + 1 < sizeof (mybuf)))
165 port_start = &mybuf[i + 1];
169 /* construct socket address of sender */
173 sin_addr.sin_family = AF_INET;
174 #if HAVE_SOCKADDR_IN_SIN_LEN
175 sin_addr.sin_len = sizeof (sin_addr);
177 if ( (NULL == port_start) ||
178 (1 != SSCANF (port_start,
181 (-1 == inet_pton (AF_INET,
183 &sin_addr.sin_addr)))
185 /* should we restart gnunet-helper-nat-server? */
186 LOG (GNUNET_ERROR_TYPE_WARNING,
188 _("gnunet-helper-nat-server generated malformed address `%s'\n"),
191 = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
192 h->server_stdout_handle,
197 sin_addr.sin_port = htons ((uint16_t) port);
198 LOG (GNUNET_ERROR_TYPE_DEBUG,
199 "gnunet-helper-nat-server read: %s:%d\n",
205 = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
206 h->server_stdout_handle,
213 * Task that restarts the gnunet-helper-nat-server process after a crash
214 * after a certain delay.
216 * @param cls a `struct HelperContext`
219 restart_nat_server (void *cls)
221 struct HelperContext *h = cls;
224 h->server_read_task = NULL;
226 = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES,
227 GNUNET_NO, GNUNET_YES);
228 if (NULL == h->server_stdout)
230 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
235 LOG (GNUNET_ERROR_TYPE_DEBUG,
236 "Starting `%s' at `%s'\n",
237 "gnunet-helper-nat-server",
238 h->internal_address);
239 /* Start the server process */
241 = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-nat-server");
243 = GNUNET_OS_start_process (GNUNET_NO,
249 "gnunet-helper-nat-server",
252 GNUNET_free (binary);
253 if (NULL == h->server_proc)
255 LOG (GNUNET_ERROR_TYPE_WARNING,
257 _("Failed to start %s\n"),
258 "gnunet-helper-nat-server");
259 GNUNET_DISK_pipe_close (h->server_stdout);
260 h->server_stdout = NULL;
264 /* Close the write end of the read pipe */
265 GNUNET_DISK_pipe_close_end (h->server_stdout,
266 GNUNET_DISK_PIPE_END_WRITE);
267 h->server_stdout_handle
268 = GNUNET_DISK_pipe_handle (h->server_stdout,
269 GNUNET_DISK_PIPE_END_READ);
271 = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
272 h->server_stdout_handle,
279 * Start the gnunet-helper-nat-server and process incoming
282 * @param internal_address
283 * @param cb function to call if we receive a request
284 * @param cb_cls closure for @a cb
285 * @return NULL on error
287 struct HelperContext *
288 GN_start_gnunet_nat_server_ (const char *internal_address,
289 GN_ReversalCallback cb,
292 struct HelperContext *h;
294 h = GNUNET_new (struct HelperContext);
299 if (NULL == h->server_stdout)
301 GN_stop_gnunet_nat_server_ (h);
309 * Start the gnunet-helper-nat-server and process incoming
312 * @param h helper context to stop
315 GN_stop_gnunet_nat_server_ (struct HelperContext *h)
317 if (NULL != h->server_read_task)
319 GNUNET_SCHEDULER_cancel (h->server_read_task);
320 h->server_read_task = NULL;
322 if (NULL != h->server_proc)
324 if (0 != GNUNET_OS_process_kill (h->server_proc,
326 GNUNET_log_from_strerror (GNUNET_ERROR_TYPE_WARNING,
329 GNUNET_OS_process_wait (h->server_proc);
330 GNUNET_OS_process_destroy (h->server_proc);
331 h->server_proc = NULL;
332 GNUNET_DISK_pipe_close (h->server_stdout);
333 h->server_stdout = NULL;
334 h->server_stdout_handle = NULL;
336 if (NULL != h->server_stdout)
338 GNUNET_DISK_pipe_close (h->server_stdout);
339 h->server_stdout = NULL;
340 h->server_stdout_handle = NULL;
347 * We want to connect to a peer that is behind NAT. Run the
348 * gnunet-helper-nat-client to send dummy ICMP responses to cause
349 * that peer to connect to us (connection reversal).
351 * @param internal_address out internal address to use
352 * @param sa the address of the peer (IPv4-only)
353 * @return #GNUNET_SYSERR on error,
354 * #GNUNET_OK otherwise
357 GN_request_connection_reversal (const char *internal_address,
358 const struct sockaddr_in *sa)
360 char inet4[INET_ADDRSTRLEN];
361 char port_as_string[6];
362 struct GNUNET_OS_Process *proc;
365 GNUNET_assert (sa->sin_family == AF_INET);
366 if (NULL == inet_ntop (AF_INET,
371 GNUNET_log_from_strerror (GNUNET_ERROR_TYPE_WARNING,
374 return GNUNET_SYSERR;
376 GNUNET_snprintf (port_as_string,
377 sizeof (port_as_string),
379 ntohs (sa->sin_port));
380 LOG (GNUNET_ERROR_TYPE_DEBUG,
381 _("Running gnunet-helper-nat-client %s %s %u\n"),
384 ntohs (sa->sin_port));
386 = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-nat-client");
388 = GNUNET_OS_start_process (GNUNET_NO,
394 "gnunet-helper-nat-client",
399 GNUNET_free (binary);
401 return GNUNET_SYSERR;
402 /* we know that the gnunet-helper-nat-client will terminate virtually
404 GNUNET_OS_process_wait (proc);
405 GNUNET_OS_process_destroy (proc);
410 /* end of gnunet-service-nat_helper.c */