dbe4ef643ce92bdb0badcf0a6396b460200a05aa
[oweals/gnunet.git] / src / nat / nat.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 2, or (at your
8      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      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /*
22  * Parts of this file have been adapted from the Transmission project:
23  * Originally licensed by the GPL version 2.
24  * Copyright (C) 2007-2009 Charles Kerr <charles@transmissionbt.com>
25  */
26
27 /**
28  * @file nat/nat.c
29  * @brief Library handling UPnP and NAT-PMP port forwarding and
30  *     external IP address retrieval
31  *
32  * @author Milan Bouchet-Valat
33  */
34
35 #ifdef __cplusplus
36 extern "C"
37 {
38 #if 0                           /* keep Emacsens' auto-indent happy */
39 }
40 #endif
41 #endif
42
43 #include <errno.h>
44 #include <string.h>
45 #include <stdio.h>
46
47 #include <sys/types.h>
48
49 #include "platform.h"
50 #include "gnunet_common.h"
51 #include "gnunet_util_lib.h"
52 #include "gnunet_nat_lib.h"
53 #include "natpmp.h"
54 #include "upnp.h"
55
56 /* Component name for logging */
57 #define COMP_NAT _("NAT")
58 #define DEBUG
59
60 struct GNUNET_NAT_Handle
61 {
62   int is_enabled;
63
64   GNUNET_NAT_port_forwarding natpmp_status;
65   GNUNET_NAT_port_forwarding upnp_status;
66
67   int should_change;
68   u_short public_port;
69
70   GNUNET_NAT_UPNP_Handle *upnp;
71   GNUNET_NAT_NATPMP_Handle *natpmp;
72
73   struct GNUNET_SCHEDULER_Handle *sched;
74   GNUNET_SCHEDULER_TaskIdentifier pulse_timer;
75
76   struct sockaddr *local_addr; /* LAN address as passed by the caller */
77   struct sockaddr *ext_addr; /* External address as reported by NAT box */
78   struct sockaddr *contact_addr; /* External address and port where paquets are redirected*/
79   GNUNET_NAT_AddressCallback callback;
80   void *callback_cls;
81 };
82
83 #ifdef DEBUG
84 static const char *
85 get_nat_state_str (int state)
86 {
87   switch (state)
88     {
89       /* we're in the process of trying to set up port forwarding */
90     case GNUNET_NAT_PORT_MAPPING:
91       return "Starting";
92
93       /* we've successfully forwarded the port */
94     case GNUNET_NAT_PORT_MAPPED:
95       return "Forwarded";
96
97       /* we're cancelling the port forwarding */
98     case GNUNET_NAT_PORT_UNMAPPING:
99       return "Stopping";
100
101       /* the port isn't forwarded */
102     case GNUNET_NAT_PORT_UNMAPPED:
103       return "Not forwarded";
104
105     case GNUNET_NAT_PORT_ERROR:
106       return "Redirection failed";
107     }
108
109   return "notfound";
110 }
111 #endif
112
113 static int
114 get_traversal_status (const GNUNET_NAT_Handle * s)
115 {
116   return MAX (s->natpmp_status, s->upnp_status);
117 }
118
119 /**
120  * Compare the sin(6)_addr fields of AF_INET or AF_INET(6) sockaddr.
121  * @param a first sockaddr
122  * @param second sockaddr
123  * @returns 0 if addresses are equal, non-null value otherwise */
124 int
125 GNUNET_NAT_cmp_addr (const struct sockaddr *a, const struct sockaddr *b)
126 {
127   if (!(a && b))
128     return -1;
129
130   if (a->sa_family == AF_INET && b->sa_family == AF_INET)
131     return memcmp (&(((struct sockaddr_in *) a)->sin_addr),
132                    &(((struct sockaddr_in *) b)->sin_addr),
133                    sizeof (struct in_addr));
134   else if (a->sa_family == AF_INET6 && b->sa_family == AF_INET6)
135     return memcmp (&(((struct sockaddr_in6 *) a)->sin6_addr),
136                    &(((struct sockaddr_in6 *) b)->sin6_addr),
137                    sizeof (struct in6_addr));
138   else
139     return -1;
140 }
141
142 /* Deal with a new IP address or port redirection:
143  * Send signals with the appropriate sockaddr (IP and port), free and changes
144  * or nullify the previous sockaddr. Change the port if needed.
145  */
146 static void
147 notify_change (GNUNET_NAT_Handle *nat, struct sockaddr *addr, int new_port_mapped)
148 {
149   static int port_mapped = GNUNET_NO;
150
151   /* Nothing to do. We already check in nat_pulse() that addr has changed */
152   if (new_port_mapped == port_mapped)
153     return;
154
155   port_mapped = new_port_mapped;
156
157   if (nat->contact_addr && nat->callback)
158     (*nat->callback) (nat->callback_cls, GNUNET_NO, (struct sockaddr *) &nat->contact_addr,
159                       sizeof (nat->contact_addr));
160
161   /* At this point, we're sure contact_addr has changed */
162   if (nat->contact_addr)
163     {
164       GNUNET_free (nat->contact_addr);
165       nat->contact_addr = NULL;
166     }
167
168   /* No address, don't signal a new one */
169   if (!addr)
170     {
171       if (nat->ext_addr)
172         GNUNET_free (nat->ext_addr);
173       nat->ext_addr = NULL;
174       return;
175     }
176   /* Copy the new address and use it */
177   else if (addr != nat->ext_addr)
178     {
179       if (nat->ext_addr)
180         GNUNET_free (nat->ext_addr);
181       nat->ext_addr = GNUNET_malloc (sizeof (*addr));
182       memcpy (nat->ext_addr, addr, sizeof (*addr));
183     }
184
185   /* Recreate the ext_addr:public_port bogus address to pass to the callback */
186   if (nat->ext_addr->sa_family == AF_INET)
187     {
188       struct sockaddr_in *tmp_addr;
189       tmp_addr = GNUNET_malloc (sizeof (struct sockaddr_in));
190       tmp_addr->sin_family = AF_INET;
191 #ifdef HAVE_SOCKADDR_IN_SIN_LEN
192       tmp_addr->sin_len = sizeof (struct sockaddr_in);
193 #endif
194       tmp_addr->sin_port = port_mapped ? htons (nat->public_port) : 0;
195       tmp_addr->sin_addr = ((struct sockaddr_in *) nat->ext_addr)->sin_addr;
196       nat->contact_addr = (struct sockaddr *) tmp_addr;
197       if (nat->callback)
198         (*nat->callback) (nat->callback_cls, GNUNET_YES, nat->contact_addr,
199                           sizeof (struct sockaddr_in));
200     }
201   else if (nat->ext_addr->sa_family == AF_INET6)
202     {
203       struct sockaddr_in6 *tmp_addr;
204       tmp_addr = GNUNET_malloc (sizeof (struct sockaddr_in6));
205       tmp_addr->sin6_family = AF_INET6;
206 #ifdef HAVE_SOCKADDR_IN_SIN_LEN
207       tmp_addr->sin6_len = sizeof (struct sockaddr_in6);
208 #endif
209       tmp_addr->sin6_port = port_mapped ? htons (nat->public_port) : 0;
210       tmp_addr->sin6_addr = ((struct sockaddr_in6 *) nat->ext_addr)->sin6_addr;
211       nat->contact_addr = (struct sockaddr *) tmp_addr;
212       if (nat->callback)
213         (*nat->callback) (nat->callback_cls, GNUNET_YES, nat->contact_addr,
214                           sizeof (struct sockaddr_in6));
215     }
216 }
217
218 static void
219 nat_pulse (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
220 {
221   GNUNET_NAT_Handle *nat = cls;
222   static int first_warning = GNUNET_YES;
223   int old_status;
224   int new_status;
225   int port_mapped;
226   struct sockaddr *ext_addr_upnp = NULL;
227   struct sockaddr *ext_addr_natpmp = NULL;
228
229   old_status = get_traversal_status (nat);
230
231   /* Only update the protocol that has been successful until now */
232   if (nat->upnp_status >= GNUNET_NAT_PORT_UNMAPPED)
233     nat->upnp_status =
234       GNUNET_NAT_UPNP_pulse (nat->upnp, nat->is_enabled, GNUNET_YES,
235                              &ext_addr_upnp);
236   else if (nat->natpmp_status >= GNUNET_NAT_PORT_UNMAPPED)
237     nat->natpmp_status =
238       GNUNET_NAT_NATPMP_pulse (nat->natpmp, nat->is_enabled,
239                                &ext_addr_natpmp);
240   else
241     {
242       nat->upnp_status =
243         GNUNET_NAT_UPNP_pulse (nat->upnp, nat->is_enabled, GNUNET_YES,
244                                &ext_addr_upnp);
245       nat->natpmp_status =
246         GNUNET_NAT_NATPMP_pulse (nat->natpmp, nat->is_enabled,
247                                  &ext_addr_natpmp);
248     }
249
250   new_status = get_traversal_status (nat);
251
252   if (old_status != new_status &&
253      (new_status == GNUNET_NAT_PORT_UNMAPPED || new_status == GNUNET_NAT_PORT_ERROR))
254     GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT,
255                      _("Port redirection failed: no UPnP or NAT-PMP routers supporting this feature found\n"));
256
257 #ifdef DEBUG
258   if (new_status != old_status)
259     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT,
260                      _("State changed from \"%s\" to \"%s\"\n"),
261                      get_nat_state_str (old_status),
262                      get_nat_state_str (new_status));
263 #endif
264
265   port_mapped = (new_status == GNUNET_NAT_PORT_MAPPED);
266   if (!(ext_addr_upnp || ext_addr_natpmp))
267     {
268       /* Address has just changed and we could not get it, or it's the first try */
269       if (nat->ext_addr || first_warning)
270         {
271           GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT,
272                       _("Could not determine external IP address\n"));
273           first_warning = GNUNET_NO;
274         }
275
276       notify_change (nat, NULL, port_mapped);
277     }
278   else if (ext_addr_upnp && GNUNET_NAT_cmp_addr (nat->ext_addr, ext_addr_upnp) != 0)
279     {
280       GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT,
281                   _("External IP address changed from %s to %s\n"),
282                   GNUNET_a2s (nat->ext_addr, sizeof (nat->ext_addr)),
283                   GNUNET_a2s (ext_addr_upnp, sizeof (ext_addr_upnp)));
284
285       notify_change (nat, ext_addr_upnp, port_mapped);
286     }
287   else if (ext_addr_natpmp && GNUNET_NAT_cmp_addr (nat->ext_addr, ext_addr_natpmp) != 0)
288     {
289       GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT,
290                   _("External IP address changed from %s to %s\n"),
291                   GNUNET_a2s (nat->ext_addr, sizeof (nat->ext_addr)),
292                   GNUNET_a2s (ext_addr_natpmp, sizeof (ext_addr_natpmp)));
293
294       notify_change (nat, ext_addr_natpmp, port_mapped);
295     }
296
297   nat->pulse_timer = GNUNET_SCHEDULER_add_delayed (nat->sched, GNUNET_NO,
298                                                    GNUNET_SCHEDULER_PRIORITY_DEFAULT,
299                                                    GNUNET_SCHEDULER_NO_TASK,
300                                                    GNUNET_TIME_UNIT_SECONDS,
301                                                    &nat_pulse, nat);
302 }
303
304 struct GNUNET_NAT_Handle *
305 GNUNET_NAT_register (struct GNUNET_SCHEDULER_Handle *sched,
306                      const struct sockaddr *addr, socklen_t addrlen,
307                      GNUNET_NAT_AddressCallback callback, void *callback_cls)
308 {
309   GNUNET_NAT_Handle *nat = GNUNET_malloc (sizeof (GNUNET_NAT_Handle));
310
311   if (addr)
312     {
313       GNUNET_assert (addr->sa_family == AF_INET
314                      || addr->sa_family == AF_INET6);
315       if (addr->sa_family == AF_INET)
316         {
317           nat->public_port = ntohs (((struct sockaddr_in *) addr)->sin_port);
318 //          ((struct sockaddr_in *) addr)->sin_port = 0;
319         }
320       else if (addr->sa_family == AF_INET6)
321         {
322           nat->public_port = ntohs (((struct sockaddr_in6 *) addr)->sin6_port);
323 //          ((struct sockaddr_in6 *) addr)->sin6_port = 0;
324         }
325     }
326
327   nat->should_change = GNUNET_YES;
328   nat->sched = sched;
329   nat->is_enabled = GNUNET_YES;
330   nat->upnp_status = GNUNET_NAT_PORT_UNMAPPED;
331   nat->natpmp_status = GNUNET_NAT_PORT_UNMAPPED;
332   nat->callback = callback;
333   nat->callback_cls = callback_cls;
334   nat->ext_addr = NULL;
335   nat->contact_addr = NULL;
336   nat->local_addr = GNUNET_malloc (addrlen);
337   memcpy (nat->local_addr, addr, addrlen);
338   nat->natpmp = GNUNET_NAT_NATPMP_init (nat->local_addr, addrlen, nat->public_port);
339   nat->upnp = GNUNET_NAT_UPNP_init (nat->local_addr, addrlen, nat->public_port);
340
341   nat->pulse_timer = GNUNET_SCHEDULER_add_delayed (sched, GNUNET_NO,
342                                                    GNUNET_SCHEDULER_PRIORITY_DEFAULT,
343                                                    GNUNET_SCHEDULER_NO_TASK,
344                                                    GNUNET_TIME_UNIT_SECONDS,
345                                                    &nat_pulse, nat);
346
347   return nat;
348 }
349
350 void
351 GNUNET_NAT_unregister (struct GNUNET_NAT_Handle *nat)
352 {
353   struct sockaddr *addr;
354   GNUNET_SCHEDULER_cancel (nat->sched, nat->pulse_timer);
355
356   nat->upnp_status =
357     GNUNET_NAT_UPNP_pulse (nat->upnp, GNUNET_NO, GNUNET_NO,
358                            &addr);
359   nat->natpmp_status =
360     GNUNET_NAT_NATPMP_pulse (nat->natpmp, GNUNET_NO,
361                              &addr);
362
363   GNUNET_NAT_NATPMP_close (nat->natpmp);
364   GNUNET_NAT_UPNP_close (nat->upnp);
365   if (nat->ext_addr)
366     GNUNET_free (nat->ext_addr);
367   GNUNET_free (nat);
368 }
369
370 #if 0                           /* keep Emacsens' auto-indent happy */
371 {
372 #endif
373 #ifdef __cplusplus
374 }
375 #endif