GNUnet  0.10.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 
62 
66  void *cb_cls;
67 
72 
77 
82 
86  const struct GNUNET_DISK_FileHandle *r;
87 
91  size_t off;
92 
96  char buf[17];
97 
102 };
103 
104 
111 static void
113 {
114  struct GNUNET_NAT_ExternalHandle *eh = cls;
115  ssize_t ret;
116  struct in_addr addr;
117 
118  eh->task = NULL;
119  ret = GNUNET_DISK_file_read(eh->r,
120  &eh->buf[eh->off],
121  sizeof(eh->buf) - eh->off);
122  if (ret > 0)
123  {
124  /* try to read more */
125  eh->off += ret;
127  eh->r,
129  eh);
130  return;
131  }
133  if ((eh->off > 7) && (eh->buf[eh->off - 1] == '\n'))
134  {
135  eh->buf[eh->off - 1] = '\0';
136  if (1 == inet_pton(AF_INET, eh->buf, &addr))
137  {
138  if (0 == addr.s_addr)
139  eh->ret =
141  else
143  }
144  }
145  eh->cb(eh->cb_cls,
146  (GNUNET_NAT_ERROR_SUCCESS == eh->ret) ? &addr : NULL,
147  eh->ret);
149 }
150 
151 
157 static void
159 {
160  struct GNUNET_NAT_ExternalHandle *eh = cls;
161 
162  eh->task = NULL;
163  eh->cb(eh->cb_cls, NULL, eh->ret);
164  GNUNET_free(eh);
165 }
166 
167 
177 {
178  struct GNUNET_NAT_ExternalHandle *eh;
179 
181  eh->cb = cb;
182  eh->cb_cls = cb_cls;
184  if (GNUNET_SYSERR ==
185  GNUNET_OS_check_helper_binary("external-ip", GNUNET_NO, NULL))
186  {
187  LOG(GNUNET_ERROR_TYPE_INFO, _("`external-ip' command not found\n"));
190  return eh;
191  }
193  "Running `external-ip' to determine our external IP\n");
195  if (NULL == eh->opipe)
196  {
199  return eh;
200  }
202  0,
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 
265 
269  void *ac_cls;
270 
275 
280 
285 
289  struct sockaddr_in current_addr;
290 
296 
300  int is_tcp;
301 
305  int did_map;
306 
310  int found;
311 
315  uint16_t port;
316 };
317 
318 
324 static void
325 do_refresh(void *cls);
326 
327 
334 static void
335 process_map_output(void *cls, const char *line);
336 
337 
343 static void
345 {
346  char pstr[6];
347 
348  GNUNET_snprintf(pstr, sizeof(pstr), "%u", (unsigned int)mini->port);
350  mini,
351  MAP_TIMEOUT,
352  "upnpc",
353  "upnpc",
354  "-r",
355  pstr,
356  mini->is_tcp ? "tcp" : "udp",
357  NULL);
358  if (NULL == mini->map_cmd)
359  {
360  mini->ac(mini->ac_cls,
362  NULL,
363  0,
365  return;
366  }
367 }
368 
369 
377 static void
378 process_refresh_output(void *cls, const char *line)
379 {
380  struct GNUNET_NAT_MiniHandle *mini = cls;
381  char pstr[9];
382  const char *s;
383  unsigned int nport;
384  struct in_addr exip;
385 
386  if (NULL == line)
387  {
389  mini->refresh_cmd = NULL;
390  if (GNUNET_NO == mini->found)
391  {
392  /* mapping disappeared, try to re-create */
393  if (GNUNET_YES == mini->did_map)
394  {
395  mini->ac(mini->ac_cls,
396  GNUNET_NO,
397  (const struct sockaddr *)&mini->current_addr,
398  sizeof(mini->current_addr),
400  mini->did_map = GNUNET_NO;
401  }
402  run_upnpc_r(mini);
403  }
404  return;
405  }
406  if (!mini->did_map)
407  return; /* never mapped, won't find our mapping anyway */
408 
409  /* we're looking for output of the form:
410  * "ExternalIPAddress = 12.134.41.124" */
411 
412  s = strstr(line, "ExternalIPAddress = ");
413  if (NULL != s)
414  {
415  s += strlen("ExternalIPAddress = ");
416  if (1 != inet_pton(AF_INET, s, &exip))
417  return; /* skip */
418  if (exip.s_addr == mini->current_addr.sin_addr.s_addr)
419  return; /* no change */
420  /* update mapping */
421  mini->ac(mini->ac_cls,
422  GNUNET_NO,
423  (const struct sockaddr *)&mini->current_addr,
424  sizeof(mini->current_addr),
426  mini->current_addr.sin_addr = exip;
427  mini->ac(mini->ac_cls,
428  GNUNET_YES,
429  (const struct sockaddr *)&mini->current_addr,
430  sizeof(mini->current_addr),
432  return;
433  }
434  /*
435  * we're looking for output of the form:
436  *
437  * "0 TCP 3000->192.168.2.150:3000 'libminiupnpc' ''"
438  * "1 UDP 3001->192.168.2.150:3001 'libminiupnpc' ''"
439  *
440  * the pattern we look for is:
441  *
442  * "%s TCP PORT->STRING:OURPORT *" or
443  * "%s UDP PORT->STRING:OURPORT *"
444  */
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  */
556  if ((NULL == (ipaddr = strstr(line, " "))) ||
557  (NULL == (pstr = strstr(ipaddr, ":"))) ||
558  (1 != sscanf(pstr + 1, "%u", &port)))
559  {
560  return; /* skip line */
561  }
562  ipa = GNUNET_strdup(ipaddr + 1);
563  strstr(ipa, ":")[0] = '\0';
564  if (1 != inet_pton(AF_INET, ipa, &mini->current_addr.sin_addr))
565  {
566  GNUNET_free(ipa);
567  return; /* skip line */
568  }
569  GNUNET_free(ipa);
570 
571  mini->current_addr.sin_port = htons(port);
572  mini->current_addr.sin_family = AF_INET;
573 #if HAVE_SOCKADDR_IN_SIN_LEN
574  mini->current_addr.sin_len = sizeof(struct sockaddr_in);
575 #endif
576  mini->did_map = GNUNET_YES;
577  mini->ac(mini->ac_cls,
578  GNUNET_YES,
579  (const struct sockaddr *)&mini->current_addr,
580  sizeof(mini->current_addr),
582 }
583 
584 
598 struct GNUNET_NAT_MiniHandle *
600  int is_tcp,
602  void *ac_cls)
603 {
604  struct GNUNET_NAT_MiniHandle *ret;
605 
607  {
608  LOG(GNUNET_ERROR_TYPE_INFO, _("`upnpc' command not found\n"));
610  return NULL;
611  }
612  LOG(GNUNET_ERROR_TYPE_DEBUG, "Running `upnpc' to install mapping\n");
613  ret = GNUNET_new(struct GNUNET_NAT_MiniHandle);
614  ret->ac = ac;
615  ret->ac_cls = ac_cls;
616  ret->is_tcp = is_tcp;
617  ret->port = port;
618  ret->refresh_task =
620  run_upnpc_r(ret);
621  return ret;
622 }
623 
624 
631 static void
632 process_unmap_output(void *cls, const char *line)
633 {
634  struct GNUNET_NAT_MiniHandle *mini = cls;
635 
636  if (NULL == line)
637  {
638  LOG(GNUNET_ERROR_TYPE_DEBUG, "UPnP unmap done\n");
640  mini->unmap_cmd = NULL;
641  GNUNET_free(mini);
642  return;
643  }
644  /* we don't really care about the output... */
645 }
646 
647 
656 void
658 {
659  char pstr[6];
660 
661  if (NULL != mini->refresh_task)
662  {
664  mini->refresh_task = NULL;
665  }
666  if (NULL != mini->refresh_cmd)
667  {
669  mini->refresh_cmd = NULL;
670  }
671  if (NULL != mini->map_cmd)
672  {
674  mini->map_cmd = NULL;
675  }
676  if (GNUNET_NO == mini->did_map)
677  {
678  GNUNET_free(mini);
679  return;
680  }
681  mini->ac(mini->ac_cls,
682  GNUNET_NO,
683  (const struct sockaddr *)&mini->current_addr,
684  sizeof(mini->current_addr),
686  /* Note: oddly enough, deletion uses the external port whereas
687  * addition uses the internal port; this rarely matters since they
688  * often are the same, but it might... */
689  GNUNET_snprintf(pstr,
690  sizeof(pstr),
691  "%u",
692  (unsigned int)ntohs(mini->current_addr.sin_port));
694  "Unmapping port %u with UPnP\n",
695  ntohs(mini->current_addr.sin_port));
697  mini,
699  "upnpc",
700  "upnpc",
701  "-d",
702  pstr,
703  mini->is_tcp ? "tcp" : "udp",
704  NULL);
705 }
706 
707 
708 /* 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_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:286
struct GNUNET_OS_CommandHandle * refresh_cmd
Command used to refresh our map information.
Handle to a mapping created with upnpc.
int GNUNET_snprintf(char *buf, size_t size, const char *format,...)
Like snprintf, just aborts if the buffer is of insufficient size.
`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:729
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.
struct GNUNET_OS_Process * GNUNET_OS_start_process(int pipe_control, 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:687
#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.
#define GNUNET_NO
Definition: gnunet_common.h:78
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:1615
#define GNUNET_OK
Named constants for return values.
Definition: gnunet_common.h:75
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:1744
`external-ip&#39; command output invalid
#define _(String)
GNU gettext support macro.
Definition: platform.h:181
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:1237
static char * line
Desired phone line (string to be converted to a hash).
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:1264
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(int blocking_read, int blocking_write, int inherit_read, int inherit_write)
Creates an interprocess channel.
Definition: disk.c:1518
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.
#define GNUNET_SYSERR
Definition: gnunet_common.h:76
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:1160
`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:1706
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:1635
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:1000
static uint16_t port
Port number.
Definition: gnunet-bcd.c:81
struct GNUNET_DISK_PipeHandle * opipe
Handle to stdout pipe of external-ip.
Handle to a command.
Definition: os_priority.c:1019
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?
Entry in list of pending tasks.
Definition: scheduler.c:131
"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:1075
#define GNUNET_YES
Definition: gnunet_common.h:77
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:224
#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:956