GNUnet  0.11.x
gnunet-service-nat_mini.c
Go to the documentation of this file.
1 /*
2  This file is part of GNUnet.
3  Copyright (C) 2011-2014, 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 
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_nat_service.h"
30 #include "nat.h"
31 
32 #define LOG(kind, ...) GNUNET_log_from (kind, "nat", __VA_ARGS__)
33 
37 #define MAP_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
38 
42 #define UNMAP_TIMEOUT \
43  GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
44 
48 #define MAP_REFRESH_FREQ \
49  GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
50 
51 
52 /* ************************* external-ip calling ************************ */
53 
58 {
63 
67  void *cb_cls;
68 
73 
78 
83 
87  const struct GNUNET_DISK_FileHandle *r;
88 
92  size_t off;
93 
97  char buf[17];
98 
103 };
104 
105 
112 static void
114 {
115  struct GNUNET_NAT_ExternalHandle *eh = cls;
116  ssize_t ret;
117  struct in_addr addr;
118 
119  eh->task = NULL;
120  ret = GNUNET_DISK_file_read (eh->r,
121  &eh->buf[eh->off],
122  sizeof(eh->buf) - eh->off);
123  if (ret > 0)
124  {
125  /* try to read more */
126  eh->off += ret;
128  eh->r,
130  eh);
131  return;
132  }
134  if ((eh->off > 7) && (eh->buf[eh->off - 1] == '\n'))
135  {
136  eh->buf[eh->off - 1] = '\0';
137  if (1 == inet_pton (AF_INET, eh->buf, &addr))
138  {
139  if (0 == addr.s_addr)
140  eh->ret =
142  else
144  }
145  }
146  eh->cb (eh->cb_cls,
147  (GNUNET_NAT_ERROR_SUCCESS == eh->ret) ? &addr : NULL,
148  eh->ret);
150 }
151 
152 
158 static void
160 {
161  struct GNUNET_NAT_ExternalHandle *eh = cls;
162 
163  eh->task = NULL;
164  eh->cb (eh->cb_cls, NULL, eh->ret);
165  GNUNET_free (eh);
166 }
167 
168 
178 {
179  struct GNUNET_NAT_ExternalHandle *eh;
180 
182  eh->cb = cb;
183  eh->cb_cls = cb_cls;
185  if (GNUNET_SYSERR ==
186  GNUNET_OS_check_helper_binary ("external-ip", GNUNET_NO, NULL))
187  {
188  LOG (GNUNET_ERROR_TYPE_INFO, _ ("`external-ip' command not found\n"));
191  return eh;
192  }
194  "Running `external-ip' to determine our external IP\n");
196  if (NULL == eh->opipe)
197  {
200  return eh;
201  }
203  NULL,
204  eh->opipe,
205  NULL,
206  "external-ip",
207  "external-ip",
208  NULL);
209  if (NULL == eh->eip)
210  {
214  return eh;
215  }
219  eh->r,
221  eh);
222  return eh;
223 }
224 
225 
231 void
233 {
234  if (NULL != eh->eip)
235  {
236  (void) GNUNET_OS_process_kill (eh->eip, SIGKILL);
239  }
240  if (NULL != eh->opipe)
241  {
243  eh->opipe = NULL;
244  }
245  if (NULL != eh->task)
246  {
248  eh->task = NULL;
249  }
250  GNUNET_free (eh);
251 }
252 
253 
254 /* ************************* upnpc calling ************************ */
255 
256 
261 {
266 
270  void *ac_cls;
271 
276 
281 
286 
290  struct sockaddr_in current_addr;
291 
297 
301  int is_tcp;
302 
306  int did_map;
307 
311  int found;
312 
316  uint16_t port;
317 };
318 
319 
325 static void
326 do_refresh (void *cls);
327 
328 
335 static void
336 process_map_output (void *cls, const char *line);
337 
338 
344 static void
346 {
347  char pstr[6];
348 
349  GNUNET_snprintf (pstr, sizeof(pstr), "%u", (unsigned int) mini->port);
351  mini,
352  MAP_TIMEOUT,
353  "upnpc",
354  "upnpc",
355  "-r",
356  pstr,
357  mini->is_tcp ? "tcp" : "udp",
358  NULL);
359  if (NULL == mini->map_cmd)
360  {
361  mini->ac (mini->ac_cls,
363  NULL,
364  0,
366  return;
367  }
368 }
369 
370 
378 static void
379 process_refresh_output (void *cls, const char *line)
380 {
381  struct GNUNET_NAT_MiniHandle *mini = cls;
382  char pstr[9];
383  const char *s;
384  unsigned int nport;
385  struct in_addr exip;
386 
387  if (NULL == line)
388  {
390  mini->refresh_cmd = NULL;
391  if (GNUNET_NO == mini->found)
392  {
393  /* mapping disappeared, try to re-create */
394  if (GNUNET_YES == mini->did_map)
395  {
396  mini->ac (mini->ac_cls,
397  GNUNET_NO,
398  (const struct sockaddr *) &mini->current_addr,
399  sizeof(mini->current_addr),
401  mini->did_map = GNUNET_NO;
402  }
403  run_upnpc_r (mini);
404  }
405  return;
406  }
407  if (! mini->did_map)
408  return; /* never mapped, won't find our mapping anyway */
409 
410  /* we're looking for output of the form:
411  * "ExternalIPAddress = 12.134.41.124" */
412 
413  s = strstr (line, "ExternalIPAddress = ");
414  if (NULL != s)
415  {
416  s += strlen ("ExternalIPAddress = ");
417  if (1 != inet_pton (AF_INET, s, &exip))
418  return; /* skip */
419  if (exip.s_addr == mini->current_addr.sin_addr.s_addr)
420  return; /* no change */
421  /* update mapping */
422  mini->ac (mini->ac_cls,
423  GNUNET_NO,
424  (const struct sockaddr *) &mini->current_addr,
425  sizeof(mini->current_addr),
427  mini->current_addr.sin_addr = exip;
428  mini->ac (mini->ac_cls,
429  GNUNET_YES,
430  (const struct sockaddr *) &mini->current_addr,
431  sizeof(mini->current_addr),
433  return;
434  }
435  /*
436  * we're looking for output of the form:
437  *
438  * "0 TCP 3000->192.168.2.150:3000 'libminiupnpc' ''"
439  * "1 UDP 3001->192.168.2.150:3001 'libminiupnpc' ''"
440  *
441  * the pattern we look for is:
442  *
443  * "%s TCP PORT->STRING:OURPORT *" or
444  * "%s UDP PORT->STRING:OURPORT *"
445  */GNUNET_snprintf (pstr, sizeof(pstr), ":%u ", mini->port);
446  if (NULL == (s = strstr (line, "->")))
447  return; /* skip */
448  if (NULL == strstr (s, pstr))
449  return; /* skip */
450  if (1 != sscanf (line,
451  (mini->is_tcp) ? "%*u TCP %u->%*s:%*u %*s"
452  : "%*u UDP %u->%*s:%*u %*s",
453  &nport))
454  return; /* skip */
455  mini->found = GNUNET_YES;
456  if (nport == ntohs (mini->current_addr.sin_port))
457  return; /* no change */
458 
459  /* external port changed, update mapping */
460  mini->ac (mini->ac_cls,
461  GNUNET_NO,
462  (const struct sockaddr *) &mini->current_addr,
463  sizeof(mini->current_addr),
465  mini->current_addr.sin_port = htons ((uint16_t) nport);
466  mini->ac (mini->ac_cls,
467  GNUNET_YES,
468  (const struct sockaddr *) &mini->current_addr,
469  sizeof(mini->current_addr),
471 }
472 
473 
479 static void
480 do_refresh (void *cls)
481 {
482  struct GNUNET_NAT_MiniHandle *mini = cls;
483  int ac;
484 
485  mini->refresh_task =
488  "Running `upnpc' to check if our mapping still exists\n");
489  mini->found = GNUNET_NO;
490  ac = GNUNET_NO;
491  if (NULL != mini->map_cmd)
492  {
493  /* took way too long, abort it! */
495  mini->map_cmd = NULL;
496  ac = GNUNET_YES;
497  }
498  if (NULL != mini->refresh_cmd)
499  {
500  /* took way too long, abort it! */
502  mini->refresh_cmd = NULL;
503  ac = GNUNET_YES;
504  }
506  mini,
507  MAP_TIMEOUT,
508  "upnpc",
509  "upnpc",
510  "-l",
511  NULL);
512  if (GNUNET_YES == ac)
513  mini->ac (mini->ac_cls,
515  NULL,
516  0,
518 }
519 
520 
527 static void
528 process_map_output (void *cls, const char *line)
529 {
530  struct GNUNET_NAT_MiniHandle *mini = cls;
531  const char *ipaddr;
532  char *ipa;
533  const char *pstr;
534  unsigned int port;
535 
536  if (NULL == line)
537  {
539  mini->map_cmd = NULL;
540  if (GNUNET_YES != mini->did_map)
541  mini->ac (mini->ac_cls,
543  NULL,
544  0,
546  if (NULL == mini->refresh_task)
547  mini->refresh_task =
549  return;
550  }
551  /*
552  * The upnpc output we're after looks like this:
553  *
554  * "external 87.123.42.204:3000 TCP is redirected to internal 192.168.2.150:3000"
555  */if ((NULL == (ipaddr = strstr (line, " "))) ||
556  (NULL == (pstr = strstr (ipaddr, ":"))) ||
557  (1 != sscanf (pstr + 1, "%u", &port)))
558  {
559  return; /* skip line */
560  }
561  ipa = GNUNET_strdup (ipaddr + 1);
562  strstr (ipa, ":")[0] = '\0';
563  if (1 != inet_pton (AF_INET, ipa, &mini->current_addr.sin_addr))
564  {
565  GNUNET_free (ipa);
566  return; /* skip line */
567  }
568  GNUNET_free (ipa);
569 
570  mini->current_addr.sin_port = htons (port);
571  mini->current_addr.sin_family = AF_INET;
572 #if HAVE_SOCKADDR_IN_SIN_LEN
573  mini->current_addr.sin_len = sizeof(struct sockaddr_in);
574 #endif
575  mini->did_map = GNUNET_YES;
576  mini->ac (mini->ac_cls,
577  GNUNET_YES,
578  (const struct sockaddr *) &mini->current_addr,
579  sizeof(mini->current_addr),
581 }
582 
583 
597 struct GNUNET_NAT_MiniHandle *
599  int is_tcp,
601  void *ac_cls)
602 {
603  struct GNUNET_NAT_MiniHandle *ret;
604 
605  if (GNUNET_SYSERR == GNUNET_OS_check_helper_binary ("upnpc", GNUNET_NO, NULL))
606  {
607  LOG (GNUNET_ERROR_TYPE_INFO, _ ("`upnpc' command not found\n"));
609  return NULL;
610  }
611  LOG (GNUNET_ERROR_TYPE_DEBUG, "Running `upnpc' to install mapping\n");
612  ret = GNUNET_new (struct GNUNET_NAT_MiniHandle);
613  ret->ac = ac;
614  ret->ac_cls = ac_cls;
615  ret->is_tcp = is_tcp;
616  ret->port = port;
617  ret->refresh_task =
619  run_upnpc_r (ret);
620  return ret;
621 }
622 
623 
630 static void
631 process_unmap_output (void *cls, const char *line)
632 {
633  struct GNUNET_NAT_MiniHandle *mini = cls;
634 
635  if (NULL == line)
636  {
637  LOG (GNUNET_ERROR_TYPE_DEBUG, "UPnP unmap done\n");
639  mini->unmap_cmd = NULL;
640  GNUNET_free (mini);
641  return;
642  }
643  /* we don't really care about the output... */
644 }
645 
646 
655 void
657 {
658  char pstr[6];
659 
660  if (NULL != mini->refresh_task)
661  {
663  mini->refresh_task = NULL;
664  }
665  if (NULL != mini->refresh_cmd)
666  {
668  mini->refresh_cmd = NULL;
669  }
670  if (NULL != mini->map_cmd)
671  {
673  mini->map_cmd = NULL;
674  }
675  if (GNUNET_NO == mini->did_map)
676  {
677  GNUNET_free (mini);
678  return;
679  }
680  mini->ac (mini->ac_cls,
681  GNUNET_NO,
682  (const struct sockaddr *) &mini->current_addr,
683  sizeof(mini->current_addr),
685  /* Note: oddly enough, deletion uses the external port whereas
686  * addition uses the internal port; this rarely matters since they
687  * often are the same, but it might... */
688  GNUNET_snprintf (pstr,
689  sizeof(pstr),
690  "%u",
691  (unsigned int) ntohs (mini->current_addr.sin_port));
693  "Unmapping port %u with UPnP\n",
694  ntohs (mini->current_addr.sin_port));
696  mini,
698  "upnpc",
699  "upnpc",
700  "-d",
701  pstr,
702  mini->is_tcp ? "tcp" : "udp",
703  NULL);
704 }
705 
706 
707 /* end of gnunet-service-nat_mini.c */
static void process_unmap_output(void *cls, const char *line)
Process output from our &#39;unmap&#39; command.
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:678
struct GNUNET_NAT_MiniHandle * GNUNET_NAT_mini_map_start(uint16_t port, int is_tcp, GNUNET_NAT_MiniAddressCallback ac, void *ac_cls)
Start mapping the given port using (mini)upnpc.
Opaque handle to cancel "GNUNET_NAT_mini_get_external_ipv4" operation.
#define MAP_REFRESH_FREQ
How often do we check for changes in the mapping?
void GNUNET_OS_process_destroy(struct GNUNET_OS_Process *proc)
Cleans up process structure contents (OS-dependent) and deallocates it.
Definition: os_priority.c:287
struct GNUNET_OS_CommandHandle * refresh_cmd
Command used to refresh our map information.
Handle to a mapping created with upnpc.
`upnpc&#39; command failed to establish port mapping
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:732
static void do_refresh(void *cls)
Run "upnpc -l" to find out if our mapping changed.
enum GNUNET_NAT_StatusCode ret
Error code for better debugging and user feedback.
#define LOG(kind,...)
`upnpc&#39; command took too long, process killed
int is_tcp
Are we mapping TCP or UDP?
struct GNUNET_OS_Process * eip
Handle to external-ip process.
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:1667
static void process_map_output(void *cls, const char *line)
Process the output from the "upnpc -r" command.
int GNUNET_OS_check_helper_binary(const char *binary, int check_suid, const char *params)
Check whether an executable exists and possibly if the suid bit is set on the file.
#define GNUNET_new(type)
Allocate a struct or union of the given type.
struct GNUNET_SCHEDULER_Task * task
Read task.
struct GNUNET_OS_CommandHandle * unmap_cmd
Command used to remove the mapping.
#define MAP_TIMEOUT
How long do we give upnpc to create a mapping?
#define GNUNET_strdup(a)
Wrapper around GNUNET_xstrdup_.
GNUNET_NAT_IPCallback cb
Function to call with the result.
#define GNUNET_break(cond)
Use this for internal assertion violations that are not fatal (can be handled) but should not occur...
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:1672
`external-ip&#39; command output invalid
#define _(String)
GNU gettext support macro.
Definition: platform.h:184
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.
void GNUNET_NAT_mini_map_stop(struct GNUNET_NAT_MiniHandle *mini)
Remove a mapping created with (mini)upnpc.
int did_map
Did we succeed with creating a mapping?
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:1269
static char * line
Desired phone line (string to be converted to a hash).
No standard streams should be inherited.
Definition: gnunet_os_lib.h:73
struct GNUNET_SCHEDULER_Task * GNUNET_SCHEDULER_add_now(GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
Schedule a new task to be run as soon as possible.
Definition: scheduler.c:1296
static void signal_external_ip_error(void *cls)
(Asynchronously) signal error invoking external-ip to client.
GNUNET_NAT_StatusCode
Error Types for the NAT subsystem (which can then later be converted/resolved to a string) ...
#define GNUNET_TIME_UNIT_FOREVER_REL
Constant used to specify "forever".
The writing-end of a pipe.
The reading-end of a pipe.
struct GNUNET_SCHEDULER_Task * refresh_task
We check the mapping periodically to see if it still works.
void * ac_cls
Closure for ac.
struct GNUNET_DISK_PipeHandle * GNUNET_DISK_pipe(enum GNUNET_DISK_PipeFlags pf)
Creates an interprocess channel.
Definition: disk.c:1456
struct GNUNET_NAT_ExternalHandle * GNUNET_NAT_mini_get_external_ipv4_(GNUNET_NAT_IPCallback cb, void *cb_cls)
Try to get the external IPv4 address of this peer.
uint16_t port
Which port are we mapping?
void(* GNUNET_NAT_MiniAddressCallback)(void *cls, int add_remove, const struct sockaddr *addr, socklen_t addrlen, enum GNUNET_NAT_StatusCode result)
Signature of the callback passed to GNUNET_NAT_register() for a function to call whenever our set of ...
size_t off
Number of bytes in buf that are valid.
static struct GNUNET_MQ_Envelope * ac
Handle to current GNUNET_PEERINFO_add_peer() operation.
struct GNUNET_OS_CommandHandle * GNUNET_OS_command_run(GNUNET_OS_LineProcessor proc, void *proc_cls, struct GNUNET_TIME_Relative timeout, const char *binary,...)
Run the given command line and call the given function for each line of the output.
Definition: os_priority.c:1144
`external-ip&#39; command not found
static void read_external_ipv4(void *cls)
Read the output of external-ip into buf.
const struct GNUNET_DISK_FileHandle * r
Read handle of opipe.
void(* GNUNET_NAT_IPCallback)(void *cls, const struct in_addr *addr, enum GNUNET_NAT_StatusCode result)
Signature of a callback that is given an IP address.
int GNUNET_DISK_pipe_close(struct GNUNET_DISK_PipeHandle *p)
Closes an interprocess channel.
Definition: disk.c:1634
int 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:1562
Messages for interaction with gnunet-nat-server and gnunet-nat-service.
int GNUNET_OS_process_wait(struct GNUNET_OS_Process *proc)
Wait for a process to terminate.
Definition: os_priority.c:983
static uint16_t port
Port number.
Definition: gnunet-bcd.c:82
struct GNUNET_DISK_PipeHandle * opipe
Handle to stdout pipe of external-ip.
Handle to a command.
Definition: os_priority.c:1002
static void process_refresh_output(void *cls, const char *line)
Process the output from "upnpc -l" to see if our external mapping changed.
struct GNUNET_OS_CommandHandle * map_cmd
Command used to install the map.
Failed to run upnpc command.
int found
Did we find our mapping during refresh scan?
Configure both pipe ends for blocking operations if set.
Entry in list of pending tasks.
Definition: scheduler.c:134
"no valid address was returned by `external-ip&#39;"
void GNUNET_OS_command_stop(struct GNUNET_OS_CommandHandle *cmd)
Stop/kill a command.
Definition: os_priority.c:1059
GNUNET_NAT_MiniAddressCallback ac
Function to call on mapping changes.
char buf[17]
Destination of our read operation (output of &#39;external-ip&#39;).
struct sockaddr_in current_addr
Our current external mapping (if we have one).
static void run_upnpc_r(struct GNUNET_NAT_MiniHandle *mini)
Run "upnpc -r" to map our internal port.
Handle used to access files (and pipes).
Handle used to manage a pipe.
Definition: disk.c:68
Failed to run external-ip command.
void GNUNET_NAT_mini_get_external_ipv4_cancel_(struct GNUNET_NAT_ExternalHandle *eh)
Cancel operation.
void * cb_cls
Closure for cb.
#define GNUNET_free(ptr)
Wrapper around free.
int GNUNET_OS_process_kill(struct GNUNET_OS_Process *proc, int sig)
Sends a signal to the process.
Definition: os_priority.c:225
#define UNMAP_TIMEOUT
How long do we give upnpc to remove a mapping?
void * GNUNET_SCHEDULER_cancel(struct GNUNET_SCHEDULER_Task *task)
Cancel the task with the specified identifier.
Definition: scheduler.c:972