GNUnet 0.26.2-32-gd298f7855
 
Loading...
Searching...
No Matches
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
104
105
112static void
114{
115 struct GNUNET_NAT_ExternalHandle *eh = cls;
116 ssize_t ret;
117 struct in_addr addr;
118
119 eh->task = NULL;
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
158static 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 ==
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 }
202 eh->eip = GNUNET_process_create ();
205 eh->eip,
209 STDOUT_FILENO)));
210 if ( (GNUNET_OK !=
212 "external-ip")) ||
213 (GNUNET_OK !=
215 {
217 eh->eip = NULL;
221 eh);
222 return eh;
223 }
227 eh->r,
229 eh);
230 return eh;
231}
232
233
239void
241{
242 if (NULL != eh->eip)
243 {
245 SIGKILL);
248 true,
249 NULL,
250 NULL));
252 }
253 if (NULL != eh->opipe)
254 {
256 eh->opipe = NULL;
257 }
258 if (NULL != eh->task)
259 {
261 eh->task = NULL;
262 }
263 GNUNET_free (eh);
264}
265
266
267/* ************************* upnpc calling ************************ */
268
269
331
332
338static void
339do_refresh (void *cls);
340
341
348static void
349process_map_output (void *cls, const char *line);
350
351
357static void
359{
360 char pstr[6];
361
362 GNUNET_snprintf (pstr, sizeof(pstr), "%u", (unsigned int) mini->port);
364 mini,
366 "upnpc",
367 "upnpc",
368 "-r",
369 pstr,
370 mini->is_tcp ? "tcp" : "udp",
371 NULL);
372 if (NULL == mini->map_cmd)
373 {
374 mini->ac (mini->ac_cls,
376 NULL,
377 0,
379 return;
380 }
381}
382
383
391static void
392process_refresh_output (void *cls, const char *line)
393{
394 struct GNUNET_NAT_MiniHandle *mini = cls;
395 char pstr[9];
396 const char *s;
397 unsigned int nport;
398 struct in_addr exip;
399
400 if (NULL == line)
401 {
403 mini->refresh_cmd = NULL;
404 if (GNUNET_NO == mini->found)
405 {
406 /* mapping disappeared, try to re-create */
407 if (GNUNET_YES == mini->did_map)
408 {
409 mini->ac (mini->ac_cls,
410 GNUNET_NO,
411 (const struct sockaddr *) &mini->current_addr,
412 sizeof(mini->current_addr),
414 mini->did_map = GNUNET_NO;
415 }
416 run_upnpc_r (mini);
417 }
418 return;
419 }
420 if (! mini->did_map)
421 return; /* never mapped, won't find our mapping anyway */
422
423 /* we're looking for output of the form:
424 * "ExternalIPAddress = 12.134.41.124" */
425
426 s = strstr (line, "ExternalIPAddress = ");
427 if (NULL != s)
428 {
429 s += strlen ("ExternalIPAddress = ");
430 if (1 != inet_pton (AF_INET, s, &exip))
431 return; /* skip */
432 if (exip.s_addr == mini->current_addr.sin_addr.s_addr)
433 return; /* no change */
434 /* update mapping */
435 mini->ac (mini->ac_cls,
436 GNUNET_NO,
437 (const struct sockaddr *) &mini->current_addr,
438 sizeof(mini->current_addr),
440 mini->current_addr.sin_addr = exip;
441 mini->ac (mini->ac_cls,
443 (const struct sockaddr *) &mini->current_addr,
444 sizeof(mini->current_addr),
446 return;
447 }
448 /*
449 * we're looking for output of the form:
450 *
451 * "0 TCP 3000->192.168.2.150:3000 'libminiupnpc' ''"
452 * "1 UDP 3001->192.168.2.150:3001 'libminiupnpc' ''"
453 *
454 * the pattern we look for is:
455 *
456 * "%s TCP PORT->STRING:OURPORT *" or
457 * "%s UDP PORT->STRING:OURPORT *"
458 */GNUNET_snprintf (pstr, sizeof(pstr), ":%u ", mini->port);
459 if (NULL == (s = strstr (line, "->")))
460 return; /* skip */
461 if (NULL == strstr (s, pstr))
462 return; /* skip */
463 if (1 != sscanf (line,
464 (mini->is_tcp) ? "%*u TCP %u->%*s:%*u %*s"
465 : "%*u UDP %u->%*s:%*u %*s",
466 &nport))
467 return; /* skip */
468 mini->found = GNUNET_YES;
469 if (nport == ntohs (mini->current_addr.sin_port))
470 return; /* no change */
471
472 /* external port changed, update mapping */
473 mini->ac (mini->ac_cls,
474 GNUNET_NO,
475 (const struct sockaddr *) &mini->current_addr,
476 sizeof(mini->current_addr),
478 mini->current_addr.sin_port = htons ((uint16_t) nport);
479 mini->ac (mini->ac_cls,
481 (const struct sockaddr *) &mini->current_addr,
482 sizeof(mini->current_addr),
484}
485
486
487static void
488do_refresh (void *cls)
489{
490 struct GNUNET_NAT_MiniHandle *mini = cls;
491 int ac;
492
493 mini->refresh_task =
496 "Running `upnpc' to check if our mapping still exists\n");
497 mini->found = GNUNET_NO;
498 ac = GNUNET_NO;
499 if (NULL != mini->map_cmd)
500 {
501 /* took way too long, abort it! */
503 mini->map_cmd = NULL;
504 ac = GNUNET_YES;
505 }
506 if (NULL != mini->refresh_cmd)
507 {
508 /* took way too long, abort it! */
510 mini->refresh_cmd = NULL;
511 ac = GNUNET_YES;
512 }
514 mini,
516 "upnpc",
517 "upnpc",
518 "-l",
519 NULL);
520 if (GNUNET_YES == ac)
521 mini->ac (mini->ac_cls,
523 NULL,
524 0,
526}
527
528
535static void
536process_map_output (void *cls, const char *line)
537{
538 struct GNUNET_NAT_MiniHandle *mini = cls;
539 const char *ipaddr;
540 char *ipa;
541 const char *pstr;
542 unsigned int port;
543
544 if (NULL == line)
545 {
547 mini->map_cmd = NULL;
548 if (GNUNET_YES != mini->did_map)
549 mini->ac (mini->ac_cls,
551 NULL,
552 0,
554 if (NULL == mini->refresh_task)
555 mini->refresh_task =
557 return;
558 }
559 /*
560 * The upnpc output we're after looks like this:
561 *
562 * "external 87.123.42.204:3000 TCP is redirected to internal 192.168.2.150:3000"
563 */if ((NULL == (ipaddr = strstr (line, " "))) ||
564 (NULL == (pstr = strstr (ipaddr, ":"))) ||
565 (1 != sscanf (pstr + 1, "%u", &port)))
566 {
567 return; /* skip line */
568 }
569 ipa = GNUNET_strdup (ipaddr + 1);
570 strstr (ipa, ":")[0] = '\0';
571 if (1 != inet_pton (AF_INET, ipa, &mini->current_addr.sin_addr))
572 {
573 GNUNET_free (ipa);
574 return; /* skip line */
575 }
576 GNUNET_free (ipa);
577
578 mini->current_addr.sin_port = htons (port);
579 mini->current_addr.sin_family = AF_INET;
580#if HAVE_SOCKADDR_IN_SIN_LEN
581 mini->current_addr.sin_len = sizeof(struct sockaddr_in);
582#endif
583 mini->did_map = GNUNET_YES;
584 mini->ac (mini->ac_cls,
586 (const struct sockaddr *) &mini->current_addr,
587 sizeof(mini->current_addr),
589}
590
591
607 int is_tcp,
609 void *ac_cls)
610{
612
614 {
615 LOG (GNUNET_ERROR_TYPE_INFO, _ ("`upnpc' command not found\n"));
617 return NULL;
618 }
619 LOG (GNUNET_ERROR_TYPE_DEBUG, "Running `upnpc' to install mapping\n");
621 ret->ac = ac;
622 ret->ac_cls = ac_cls;
623 ret->is_tcp = is_tcp;
624 ret->port = port;
625 ret->refresh_task =
628 return ret;
629}
630
631
638static void
639process_unmap_output (void *cls, const char *line)
640{
641 struct GNUNET_NAT_MiniHandle *mini = cls;
642
643 if (NULL == line)
644 {
645 LOG (GNUNET_ERROR_TYPE_DEBUG, "UPnP unmap done\n");
647 mini->unmap_cmd = NULL;
648 GNUNET_free (mini);
649 return;
650 }
651 /* we don't really care about the output... */
652}
653
654
655void
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 int ret
Final status code.
Definition gnunet-arm.c:93
static uint16_t port
Port number.
Definition gnunet-bcd.c:146
static char * line
Desired phone line (string to be converted to a hash).
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.
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.
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_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 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.
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:1702
struct GNUNET_DISK_PipeHandle * GNUNET_DISK_pipe(enum GNUNET_DISK_PipeFlags pf)
Creates an interprocess channel.
Definition disk.c:1523
enum GNUNET_GenericReturnValue GNUNET_DISK_pipe_close(struct GNUNET_DISK_PipeHandle *p)
Closes an interprocess channel.
Definition disk.c:1670
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:704
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:1617
@ 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_assert(cond)
Use this for fatal errors that cannot be handled.
#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_Process * GNUNET_process_create(void)
Create a process handle.
Definition os_process.c:446
enum GNUNET_GenericReturnValue GNUNET_process_set_command(struct GNUNET_Process *p, const char *command)
Set the command to start a process.
Definition os_process.c:925
enum GNUNET_GenericReturnValue GNUNET_process_wait(struct GNUNET_Process *proc, bool blocking, enum GNUNET_OS_ProcessStatusType *type, unsigned long *code)
Wait for a process to terminate.
void GNUNET_process_destroy(struct GNUNET_Process *proc)
Cleans up process structure contents (OS-dependent) and deallocates it.
Definition os_process.c:350
void GNUNET_OS_command_stop(struct GNUNET_OS_CommandHandle *cmd)
Stop/kill a command.
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.
#define GNUNET_process_option_std_inheritance(flags)
Set flags about standard inheritance options.
enum GNUNET_GenericReturnValue GNUNET_process_start(struct GNUNET_Process *proc)
Start a process.
Definition os_process.c:563
#define GNUNET_process_set_options(proc,...)
Set the requested options for the process.
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.
#define GNUNET_process_option_inherit_wpipe(wpipe, child_fd)
Have child process inherit a pipe for writing.
enum GNUNET_GenericReturnValue GNUNET_process_kill(struct GNUNET_Process *proc, int sig)
Sends a signal to the process.
Definition os_process.c:302
@ GNUNET_OS_INHERIT_STD_NONE
No standard streams should be inherited.
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
void * GNUNET_SCHEDULER_cancel(struct GNUNET_SCHEDULER_Task *task)
Cancel the task with the specified identifier.
Definition scheduler.c:986
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:1310
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:1283
#define GNUNET_TIME_UNIT_FOREVER_REL
Constant used to specify "forever".
#define _(String)
GNU gettext support macro.
Definition platform.h:179
Handle used to access files (and pipes).
Handle used to manage a pipe.
Definition disk.c:69
Opaque handle to cancel "GNUNET_NAT_mini_get_external_ipv4" operation.
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.
struct GNUNET_Process * eip
Handle to external-ip process.
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.
Entry in list of pending tasks.
Definition scheduler.c:141