GNUnet  0.19.5
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 
474 static void
475 do_refresh (void *cls)
476 {
477  struct GNUNET_NAT_MiniHandle *mini = cls;
478  int ac;
479 
480  mini->refresh_task =
483  "Running `upnpc' to check if our mapping still exists\n");
484  mini->found = GNUNET_NO;
485  ac = GNUNET_NO;
486  if (NULL != mini->map_cmd)
487  {
488  /* took way too long, abort it! */
490  mini->map_cmd = NULL;
491  ac = GNUNET_YES;
492  }
493  if (NULL != mini->refresh_cmd)
494  {
495  /* took way too long, abort it! */
497  mini->refresh_cmd = NULL;
498  ac = GNUNET_YES;
499  }
501  mini,
502  MAP_TIMEOUT,
503  "upnpc",
504  "upnpc",
505  "-l",
506  NULL);
507  if (GNUNET_YES == ac)
508  mini->ac (mini->ac_cls,
510  NULL,
511  0,
513 }
514 
515 
522 static void
523 process_map_output (void *cls, const char *line)
524 {
525  struct GNUNET_NAT_MiniHandle *mini = cls;
526  const char *ipaddr;
527  char *ipa;
528  const char *pstr;
529  unsigned int port;
530 
531  if (NULL == line)
532  {
534  mini->map_cmd = NULL;
535  if (GNUNET_YES != mini->did_map)
536  mini->ac (mini->ac_cls,
538  NULL,
539  0,
541  if (NULL == mini->refresh_task)
542  mini->refresh_task =
544  return;
545  }
546  /*
547  * The upnpc output we're after looks like this:
548  *
549  * "external 87.123.42.204:3000 TCP is redirected to internal 192.168.2.150:3000"
550  */if ((NULL == (ipaddr = strstr (line, " "))) ||
551  (NULL == (pstr = strstr (ipaddr, ":"))) ||
552  (1 != sscanf (pstr + 1, "%u", &port)))
553  {
554  return; /* skip line */
555  }
556  ipa = GNUNET_strdup (ipaddr + 1);
557  strstr (ipa, ":")[0] = '\0';
558  if (1 != inet_pton (AF_INET, ipa, &mini->current_addr.sin_addr))
559  {
560  GNUNET_free (ipa);
561  return; /* skip line */
562  }
563  GNUNET_free (ipa);
564 
565  mini->current_addr.sin_port = htons (port);
566  mini->current_addr.sin_family = AF_INET;
567 #if HAVE_SOCKADDR_IN_SIN_LEN
568  mini->current_addr.sin_len = sizeof(struct sockaddr_in);
569 #endif
570  mini->did_map = GNUNET_YES;
571  mini->ac (mini->ac_cls,
572  GNUNET_YES,
573  (const struct sockaddr *) &mini->current_addr,
574  sizeof(mini->current_addr),
576 }
577 
578 
592 struct GNUNET_NAT_MiniHandle *
594  int is_tcp,
596  void *ac_cls)
597 {
598  struct GNUNET_NAT_MiniHandle *ret;
599 
600  if (GNUNET_SYSERR == GNUNET_OS_check_helper_binary ("upnpc", GNUNET_NO, NULL))
601  {
602  LOG (GNUNET_ERROR_TYPE_INFO, _ ("`upnpc' command not found\n"));
604  return NULL;
605  }
606  LOG (GNUNET_ERROR_TYPE_DEBUG, "Running `upnpc' to install mapping\n");
608  ret->ac = ac;
609  ret->ac_cls = ac_cls;
610  ret->is_tcp = is_tcp;
611  ret->port = port;
612  ret->refresh_task =
614  run_upnpc_r (ret);
615  return ret;
616 }
617 
618 
625 static void
626 process_unmap_output (void *cls, const char *line)
627 {
628  struct GNUNET_NAT_MiniHandle *mini = cls;
629 
630  if (NULL == line)
631  {
632  LOG (GNUNET_ERROR_TYPE_DEBUG, "UPnP unmap done\n");
634  mini->unmap_cmd = NULL;
635  GNUNET_free (mini);
636  return;
637  }
638  /* we don't really care about the output... */
639 }
640 
641 
642 void
644 {
645  char pstr[6];
646 
647  if (NULL != mini->refresh_task)
648  {
650  mini->refresh_task = NULL;
651  }
652  if (NULL != mini->refresh_cmd)
653  {
655  mini->refresh_cmd = NULL;
656  }
657  if (NULL != mini->map_cmd)
658  {
660  mini->map_cmd = NULL;
661  }
662  if (GNUNET_NO == mini->did_map)
663  {
664  GNUNET_free (mini);
665  return;
666  }
667  mini->ac (mini->ac_cls,
668  GNUNET_NO,
669  (const struct sockaddr *) &mini->current_addr,
670  sizeof(mini->current_addr),
672  /* Note: oddly enough, deletion uses the external port whereas
673  * addition uses the internal port; this rarely matters since they
674  * often are the same, but it might... */
675  GNUNET_snprintf (pstr,
676  sizeof(pstr),
677  "%u",
678  (unsigned int) ntohs (mini->current_addr.sin_port));
680  "Unmapping port %u with UPnP\n",
681  ntohs (mini->current_addr.sin_port));
683  mini,
685  "upnpc",
686  "upnpc",
687  "-d",
688  pstr,
689  mini->is_tcp ? "tcp" : "udp",
690  NULL);
691 }
692 
693 
694 /* end of gnunet-service-nat_mini.c */
static int ret
Return value of the commandline.
Definition: gnunet-abd.c:81
static uint16_t port
Port number.
Definition: gnunet-bcd.c:147
static char * line
Desired phone line (string to be converted to a hash).
static struct GNUNET_MQ_Envelope * ac
Handle to current GNUNET_PEERINFO_add_peer() operation.
static void run_upnpc_r(struct GNUNET_NAT_MiniHandle *mini)
Run "upnpc -r" to map our internal port.
void GNUNET_NAT_mini_get_external_ipv4_cancel_(struct GNUNET_NAT_ExternalHandle *eh)
Cancel operation.
static void process_map_output(void *cls, const char *line)
Process the output from the "upnpc -r" command.
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.
static void signal_external_ip_error(void *cls)
(Asynchronously) signal error invoking external-ip to client.
#define MAP_TIMEOUT
How long do we give upnpc to create a mapping?
#define MAP_REFRESH_FREQ
How often do we check for changes in the mapping?
void GNUNET_NAT_mini_map_stop(struct GNUNET_NAT_MiniHandle *mini)
Remove a mapping created with (mini)upnpc.
static void process_refresh_output(void *cls, const char *line)
Process the output from "upnpc -l" to see if our external mapping changed.
static void do_refresh(void *cls)
Run "upnpc -l" to find out if our mapping changed.
#define LOG(kind,...)
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.
#define UNMAP_TIMEOUT
How long do we give upnpc to remove a mapping?
static void process_unmap_output(void *cls, const char *line)
Process output from our 'unmap' command.
static void read_external_ipv4(void *cls)
Read the output of external-ip into buf.
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 ...
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.
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.
@ GNUNET_OK
@ GNUNET_YES
@ GNUNET_NO
@ GNUNET_SYSERR
#define GNUNET_break(cond)
Use this for internal assertion violations that are not fatal (can be handled) but should not occur.
@ GNUNET_ERROR_TYPE_DEBUG
@ GNUNET_ERROR_TYPE_INFO
#define GNUNET_strdup(a)
Wrapper around GNUNET_xstrdup_.
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.
GNUNET_NAT_StatusCode
Error Types for the NAT subsystem (which can then later be converted/resolved to a string)
@ GNUNET_NAT_ERROR_UPNPC_NOT_FOUND
upnpc command not found
@ GNUNET_NAT_ERROR_UPNPC_TIMEOUT
‘upnpc’ command took too long, process killed
@ GNUNET_NAT_ERROR_SUCCESS
Just the default.
@ GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_OUTPUT_INVALID
‘external-ip’ command output invalid
@ GNUNET_NAT_ERROR_UPNPC_FAILED
Failed to run upnpc command.
@ GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_FAILED
Failed to run external-ip command.
@ GNUNET_NAT_ERROR_UPNPC_PORTMAP_FAILED
‘upnpc’ command failed to establish port mapping
@ GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_NOT_FOUND
‘external-ip’ command not found
@ GNUNET_NAT_ERROR_IPC_FAILURE
IPC Failure.
@ GNUNET_NAT_ERROR_EXTERNAL_IP_ADDRESS_INVALID
"no valid address was returned by `external-ip'"
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:1014
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_command_stop(struct GNUNET_OS_CommandHandle *cmd)
Stop/kill a command.
Definition: os_priority.c:940
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_now(GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
Schedule a new task to be run as soon as possible.
Definition: scheduler.c:1299
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".
Messages for interaction with gnunet-nat-server and gnunet-nat-service.
#define _(String)
GNU gettext support macro.
Definition: platform.h:178
Handle used to access files (and pipes).
Handle used to manage a pipe.
Definition: disk.c:68
Opaque handle to cancel "GNUNET_NAT_mini_get_external_ipv4" operation.
struct GNUNET_OS_Process * eip
Handle to external-ip process.
size_t off
Number of bytes in buf that are valid.
GNUNET_NAT_IPCallback cb
Function to call with the result.
struct GNUNET_DISK_PipeHandle * opipe
Handle to stdout pipe of external-ip.
const struct GNUNET_DISK_FileHandle * r
Read handle of opipe.
enum GNUNET_NAT_StatusCode ret
Error code for better debugging and user feedback.
char buf[17]
Destination of our read operation (output of 'external-ip').
struct GNUNET_SCHEDULER_Task * task
Read task.
Handle to a mapping created with upnpc.
struct GNUNET_OS_CommandHandle * unmap_cmd
Command used to remove the mapping.
struct GNUNET_OS_CommandHandle * map_cmd
Command used to install the map.
struct GNUNET_OS_CommandHandle * refresh_cmd
Command used to refresh our map information.
int did_map
Did we succeed with creating a mapping?
void * ac_cls
Closure for ac.
uint16_t port
Which port are we mapping?
int is_tcp
Are we mapping TCP or UDP?
GNUNET_NAT_MiniAddressCallback ac
Function to call on mapping changes.
struct GNUNET_SCHEDULER_Task * refresh_task
We check the mapping periodically to see if it still works.
int found
Did we find our mapping during refresh scan?
struct sockaddr_in current_addr
Our current external mapping (if we have one).
Handle to a command.
Definition: os_priority.c:891
Entry in list of pending tasks.
Definition: scheduler.c:136