GNUnet  0.20.0
gnunet-service-nat_helper.c
Go to the documentation of this file.
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  SPDX-License-Identifier: AGPL3.0-or-later
19  */
20 
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
30 
31 
36 {
40  struct in_addr internal_address;
41 
46 
50  void *cb_cls;
51 
56 
61 
66 
71 
76 
81 };
82 
83 
90 static void
91 restart_nat_server (void *cls);
92 
93 
99 static void
101 {
102  GNUNET_assert (NULL == h->server_read_task);
103  h->server_retry_delay = GNUNET_TIME_STD_BACKOFF (h->server_retry_delay);
104  h->server_read_task = GNUNET_SCHEDULER_add_delayed (h->server_retry_delay,
106  h);
107 }
108 
109 
117 static void
118 nat_server_read (void *cls)
119 {
120  struct HelperContext *h = cls;
121  char mybuf[40];
122  ssize_t bytes;
123  int port;
124  const char *port_start;
125  struct sockaddr_in sin_addr;
126 
127  h->server_read_task = NULL;
128  memset (mybuf, 0, sizeof(mybuf));
129  bytes =
130  GNUNET_DISK_file_read (h->server_stdout_handle, mybuf, sizeof(mybuf));
131  if (bytes < 1)
132  {
134  "Finished reading from server stdout with code: %d\n",
135  (int) bytes);
136  if (0 != GNUNET_OS_process_kill (h->server_proc, GNUNET_TERM_SIG))
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, 0, sizeof(sin_addr));
165  sin_addr.sin_family = AF_INET;
166 #if HAVE_SOCKADDR_IN_SIN_LEN
167  sin_addr.sin_len = sizeof(sin_addr);
168 #endif
169  if ((NULL == port_start) || (1 != sscanf (port_start, "%d", &port)) ||
170  (-1 == inet_pton (AF_INET, mybuf, &sin_addr.sin_addr)))
171  {
172  /* should we restart gnunet-helper-nat-server? */
174  _ (
175  "gnunet-helper-nat-server generated malformed address `%s'\n"),
176  mybuf);
177  h->server_read_task =
179  h->server_stdout_handle,
181  h);
182  return;
183  }
184  sin_addr.sin_port = htons ((uint16_t) port);
186  "gnunet-helper-nat-server read: %s:%d\n",
187  mybuf,
188  port);
189  h->cb (h->cb_cls, &sin_addr);
190  h->server_read_task =
192  h->server_stdout_handle,
194  h);
195 }
196 
197 
204 static void
206 {
207  struct HelperContext *h = cls;
208  char *binary;
209  char ia[INET_ADDRSTRLEN];
210 
211  h->server_read_task = NULL;
212  GNUNET_assert (NULL !=
213  inet_ntop (AF_INET, &h->internal_address, ia, sizeof(ia)));
214  /* Start the server process */
215  binary = GNUNET_OS_get_suid_binary_path (h->cfg, "gnunet-helper-nat-server");
217  {
218  /* move instantly to max delay, as this is unlikely to be fixed */
219  h->server_retry_delay = GNUNET_TIME_STD_EXPONENTIAL_BACKOFF_THRESHOLD;
220  GNUNET_free (binary);
221  try_again (h);
222  return;
223  }
224  h->server_stdout =
226  if (NULL == h->server_stdout)
227  {
229  GNUNET_free (binary);
230  try_again (h);
231  return;
232  }
234  "Starting `%s' at `%s'\n",
235  "gnunet-helper-nat-server",
236  ia);
238  NULL,
239  h->server_stdout,
240  NULL,
241  binary,
242  "gnunet-helper-nat-server",
243  ia,
244  NULL);
245  GNUNET_free (binary);
246  if (NULL == h->server_proc)
247  {
249  _ ("Failed to start %s\n"),
250  "gnunet-helper-nat-server");
251  GNUNET_DISK_pipe_close (h->server_stdout);
252  h->server_stdout = NULL;
253  try_again (h);
254  return;
255  }
256  /* Close the write end of the read pipe */
258  h->server_stdout_handle =
260  h->server_read_task =
262  h->server_stdout_handle,
264  h);
265 }
266 
267 
268 struct HelperContext *
271  void *cb_cls,
272  const struct GNUNET_CONFIGURATION_Handle *cfg)
273 {
274  struct HelperContext *h;
275 
276  h = GNUNET_new (struct HelperContext);
277  h->cb = cb;
278  h->cb_cls = cb_cls;
279  h->internal_address = *internal_address;
280  h->cfg = cfg;
282  if (NULL == h->server_stdout)
283  {
285  return NULL;
286  }
287  return h;
288 }
289 
290 
297 void
299 {
300  if (NULL != h->server_read_task)
301  {
302  GNUNET_SCHEDULER_cancel (h->server_read_task);
303  h->server_read_task = NULL;
304  }
305  if (NULL != h->server_proc)
306  {
307  if (0 != GNUNET_OS_process_kill (h->server_proc, GNUNET_TERM_SIG))
309  GNUNET_OS_process_wait (h->server_proc);
310  GNUNET_OS_process_destroy (h->server_proc);
311  h->server_proc = NULL;
312  GNUNET_DISK_pipe_close (h->server_stdout);
313  h->server_stdout = NULL;
314  h->server_stdout_handle = NULL;
315  }
316  if (NULL != h->server_stdout)
317  {
318  GNUNET_DISK_pipe_close (h->server_stdout);
319  h->server_stdout = NULL;
320  h->server_stdout_handle = NULL;
321  }
322  GNUNET_free (h);
323 }
324 
325 
338 int
340  uint16_t internal_port,
341  const struct in_addr *remote_v4,
342  const struct GNUNET_CONFIGURATION_Handle *cfg)
343 {
344  char intv4[INET_ADDRSTRLEN];
345  char remv4[INET_ADDRSTRLEN];
346  char port_as_string[6];
347  struct GNUNET_OS_Process *proc;
348  char *binary;
349 
350  if (NULL == inet_ntop (AF_INET, internal_address, intv4, INET_ADDRSTRLEN))
351  {
353  return GNUNET_SYSERR;
354  }
355  if (NULL == inet_ntop (AF_INET, remote_v4, remv4, INET_ADDRSTRLEN))
356  {
358  return GNUNET_SYSERR;
359  }
360  GNUNET_snprintf (port_as_string,
361  sizeof(port_as_string),
362  "%d",
363  internal_port);
365  "Running gnunet-helper-nat-client %s %s %u\n",
366  intv4,
367  remv4,
368  internal_port);
369  binary = GNUNET_OS_get_suid_binary_path (cfg, "gnunet-helper-nat-client");
371  NULL,
372  NULL,
373  NULL,
374  binary,
375  "gnunet-helper-nat-client",
376  intv4,
377  remv4,
378  port_as_string,
379  NULL);
380  GNUNET_free (binary);
381  if (NULL == proc)
382  return GNUNET_SYSERR;
383  /* we know that the gnunet-helper-nat-client will terminate virtually
384  * instantly */
385  GNUNET_OS_process_wait (proc);
387  return GNUNET_OK;
388 }
389 
390 
391 /* end of gnunet-service-nat_helper.c */
static const struct GNUNET_CONFIGURATION_Handle * cfg
Configuration we are using.
Definition: gnunet-abd.c:36
static struct GNUNET_ARM_Handle * h
Connection with ARM.
Definition: gnunet-arm.c:99
static uint16_t port
Port number.
Definition: gnunet-bcd.c:147
int GN_request_connection_reversal(const struct in_addr *internal_address, uint16_t internal_port, const struct in_addr *remote_v4, const struct GNUNET_CONFIGURATION_Handle *cfg)
We want to connect to a peer that is behind NAT.
static void nat_server_read(void *cls)
We have been notified that gnunet-helper-nat-server has written something to stdout.
void GN_stop_gnunet_nat_server_(struct HelperContext *h)
Start the gnunet-helper-nat-server and process incoming requests.
static void try_again(struct HelperContext *h)
Try again starting the helper later.
static void restart_nat_server(void *cls)
Task that restarts the gnunet-helper-nat-server process after a crash after a certain delay.
struct HelperContext * GN_start_gnunet_nat_server_(const struct in_addr *internal_address, GN_ReversalCallback cb, void *cb_cls, const struct GNUNET_CONFIGURATION_Handle *cfg)
Start the gnunet-helper-nat-server and process incoming requests.
runs the gnunet-helper-nat-server
void(* GN_ReversalCallback)(void *cls, const struct sockaddr_in *ra)
Function called whenever we get a connection reversal request from another peer.
enum GNUNET_GenericReturnValue GNUNET_DISK_pipe_close(struct GNUNET_DISK_PipeHandle *p)
Closes an interprocess channel.
Definition: disk.c:1587
ssize_t GNUNET_DISK_file_read(const struct GNUNET_DISK_FileHandle *h, void *result, size_t len)
Read the contents of a binary file into a buffer.
Definition: disk.c:622
const struct GNUNET_DISK_FileHandle * GNUNET_DISK_pipe_handle(const struct GNUNET_DISK_PipeHandle *p, enum GNUNET_DISK_PipeEnd n)
Get the handle to a particular pipe end.
Definition: disk.c:1617
enum GNUNET_GenericReturnValue GNUNET_DISK_pipe_close_end(struct GNUNET_DISK_PipeHandle *p, enum GNUNET_DISK_PipeEnd end)
Closes one half of an interprocess channel.
Definition: disk.c:1534
struct GNUNET_DISK_PipeHandle * GNUNET_DISK_pipe(enum GNUNET_DISK_PipeFlags pf)
Creates an interprocess channel.
Definition: disk.c:1444
@ GNUNET_DISK_PF_BLOCKING_RW
Configure both pipe ends for blocking operations if set.
@ GNUNET_DISK_PIPE_END_WRITE
The writing-end of a pipe.
@ GNUNET_DISK_PIPE_END_READ
The reading-end of a pipe.
#define GNUNET_log(kind,...)
@ GNUNET_OK
@ GNUNET_YES
@ GNUNET_SYSERR
#define GNUNET_log_from_strerror(level, component, cmd)
Log an error message at log-level 'level' that indicates a failure of the command 'cmd' with the mess...
#define GNUNET_assert(cond)
Use this for fatal errors that cannot be handled.
#define GNUNET_log_strerror(level, cmd)
Log an error message at log-level 'level' that indicates a failure of the command 'cmd' with the mess...
@ GNUNET_ERROR_TYPE_WARNING
@ GNUNET_ERROR_TYPE_ERROR
@ GNUNET_ERROR_TYPE_DEBUG
int GNUNET_snprintf(char *buf, size_t size, const char *format,...) __attribute__((format(printf
Like snprintf, just aborts if the buffer is of insufficient size.
#define GNUNET_new(type)
Allocate a struct or union of the given type.
#define GNUNET_free(ptr)
Wrapper around free.
char * GNUNET_OS_get_suid_binary_path(const struct GNUNET_CONFIGURATION_Handle *cfg, const char *progname)
Given the name of a helper, service or daemon binary construct the full path to the binary using the ...
struct GNUNET_OS_Process * GNUNET_OS_start_process(enum GNUNET_OS_InheritStdioFlags std_inheritance, struct GNUNET_DISK_PipeHandle *pipe_stdin, struct GNUNET_DISK_PipeHandle *pipe_stdout, struct GNUNET_DISK_PipeHandle *pipe_stderr, const char *filename,...)
Start a process.
Definition: os_priority.c:620
void GNUNET_OS_process_destroy(struct GNUNET_OS_Process *proc)
Cleans up process structure contents (OS-dependent) and deallocates it.
Definition: os_priority.c:260
enum GNUNET_GenericReturnValue GNUNET_OS_check_helper_binary(const char *binary, bool check_suid, const char *params)
Check whether an executable exists and possibly if the suid bit is set on the file.
int GNUNET_OS_process_kill(struct GNUNET_OS_Process *proc, int sig)
Sends a signal to the process.
Definition: os_priority.c:210
enum GNUNET_GenericReturnValue GNUNET_OS_process_wait(struct GNUNET_OS_Process *proc)
Wait for a process to terminate.
Definition: os_priority.c:871
@ GNUNET_OS_INHERIT_STD_NONE
No standard streams should be inherited.
Definition: gnunet_os_lib.h:77
struct GNUNET_SCHEDULER_Task * GNUNET_SCHEDULER_add_read_file(struct GNUNET_TIME_Relative delay, const struct GNUNET_DISK_FileHandle *rfd, GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
Schedule a new task to be run with a specified delay or when the specified file descriptor is ready f...
Definition: scheduler.c:1656
void * GNUNET_SCHEDULER_cancel(struct GNUNET_SCHEDULER_Task *task)
Cancel the task with the specified identifier.
Definition: scheduler.c:975
struct GNUNET_SCHEDULER_Task * GNUNET_SCHEDULER_add_delayed(struct GNUNET_TIME_Relative delay, GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
Schedule a new task to be run with a specified delay.
Definition: scheduler.c:1272
#define GNUNET_TIME_UNIT_FOREVER_REL
Constant used to specify "forever".
#define GNUNET_TIME_STD_EXPONENTIAL_BACKOFF_THRESHOLD
Threshold after which exponential backoff should not increase (15 m).
#define GNUNET_TIME_STD_BACKOFF(r)
Perform our standard exponential back-off calculation, starting at 1 ms and then going by a factor of...
#define _(String)
GNU gettext support macro.
Definition: platform.h:178
#define GNUNET_TERM_SIG
The termination signal.
Definition: platform.h:234
const struct GNUNET_CONFIGURATION_Handle * cfg
The configuration that we are using.
Definition: arm_api.c:112
Handle used to access files (and pipes).
Handle used to manage a pipe.
Definition: disk.c:68
Entry in list of pending tasks.
Definition: scheduler.c:136
Time for relative time used by GNUnet, in microseconds.
Information we keep per NAT helper process.
struct GNUNET_DISK_PipeHandle * server_stdout
stdout pipe handle for the gnunet-helper-nat-server process
const struct GNUNET_CONFIGURATION_Handle * cfg
Handle to the GNUnet configuration.
struct GNUNET_OS_Process * server_proc
The process id of the server process (if behind NAT)
struct GNUNET_TIME_Relative server_retry_delay
How long do we wait for restarting a crashed gnunet-helper-nat-server?
struct GNUNET_SCHEDULER_Task * server_read_task
ID of select gnunet-helper-nat-server stdout read task.
struct in_addr internal_address
IP address we pass to the NAT helper.
const struct GNUNET_DISK_FileHandle * server_stdout_handle
stdout file handle (for reading) for the gnunet-helper-nat-server process
GN_ReversalCallback cb
Function to call if we receive a reversal request.
void * cb_cls
Closure for cb.