fixing segv
[oweals/gnunet.git] / src / nat / nat.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2010 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 3, 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 #include "platform.h"
35 #include "gnunet_util_lib.h"
36 #include "gnunet_nat_lib.h"
37 #include "nat.h"
38 #include "natpmp.h"
39 #include "upnp.h"
40
41 /**
42  * Handle for active NAT registrations.
43  */
44 struct GNUNET_NAT_Handle
45 {
46   /**
47    * Handle for UPnP operations.
48    */
49   struct GNUNET_NAT_UPNP_Handle *upnp;
50
51   /**
52    * Handle for NAT PMP operations.
53    */
54   struct GNUNET_NAT_NATPMP_Handle *natpmp;
55
56   /**
57    * Scheduler.
58    */
59   struct GNUNET_SCHEDULER_Handle *sched;
60
61   /**
62    * LAN address as passed by the caller 
63    */
64   struct sockaddr *local_addr;
65
66   /**
67    * External address as reported by found NAT box 
68    */
69   struct sockaddr *ext_addr;
70
71   /**
72    * External address as reported by each type of NAT box 
73    */
74   struct sockaddr *ext_addr_upnp;
75   struct sockaddr *ext_addr_natpmp;
76
77   /**
78    * External address and port where packets are redirected
79    */
80   struct sockaddr *contact_addr;
81
82   GNUNET_NAT_AddressCallback callback;
83
84   /**
85    * Closure for 'callback'.
86    */
87   void *callback_cls;
88
89   GNUNET_SCHEDULER_TaskIdentifier pulse_timer;
90
91   enum GNUNET_NAT_PortState natpmp_status;
92
93   enum GNUNET_NAT_PortState upnp_status;
94
95   int is_enabled;
96
97   int should_change;
98
99   int port_mapped;
100
101   int old_status;
102
103   int new_status;
104
105   int did_warn;
106
107   int processing;
108
109   uint16_t public_port;
110
111 };
112
113 #ifdef DEBUG
114 static const char *
115 get_nat_state_str (enum GNUNET_NAT_PortState state)
116 {
117   switch (state)
118     {
119     case GNUNET_NAT_PORT_MAPPING:
120       return "Starting";
121     case GNUNET_NAT_PORT_MAPPED:
122       return "Forwarded";
123     case GNUNET_NAT_PORT_UNMAPPING:
124       return "Stopping";
125     case GNUNET_NAT_PORT_UNMAPPED:
126       return "Not forwarded";
127     case GNUNET_NAT_PORT_ERROR:
128       return "Redirection failed";
129     default:
130       return "not found";
131     }
132 }
133 #endif
134
135
136 static int
137 get_traversal_status (const struct GNUNET_NAT_Handle *h)
138 {
139   return MAX (h->natpmp_status, h->upnp_status);
140 }
141
142
143 /**
144  * Compare the sin(6)_addr fields of AF_INET or AF_INET(6) sockaddr.
145  * @param a first sockaddr
146  * @param b second sockaddr
147  * @return 0 if addresses are equal, non-null value otherwise */
148 int
149 GNUNET_NAT_cmp_addr (const struct sockaddr *a, const struct sockaddr *b)
150 {
151   if (!(a && b))
152     return -1;
153   if ((a->sa_family == AF_INET) && (b->sa_family == AF_INET))
154     return memcmp (&(((struct sockaddr_in *) a)->sin_addr),
155                    &(((struct sockaddr_in *) b)->sin_addr),
156                    sizeof (struct in_addr));
157   if ((a->sa_family == AF_INET6) && (b->sa_family == AF_INET6))
158     return memcmp (&(((struct sockaddr_in6 *) a)->sin6_addr),
159                    &(((struct sockaddr_in6 *) b)->sin6_addr),
160                    sizeof (struct in6_addr));
161   return -1;
162 }
163
164
165 /**
166  * Deal with a new IP address or port redirection:
167  * Send signals with the appropriate sockaddr (IP and port), free and changes
168  * or nullify the previous sockaddr. Change the port if needed.
169  */
170 static void
171 notify_change (struct GNUNET_NAT_Handle *h,
172                struct sockaddr *addr, size_t addrlen, int new_port_mapped)
173 {
174   if (new_port_mapped == h->port_mapped)
175     return;
176   h->port_mapped = new_port_mapped;
177
178   if ((NULL != h->contact_addr) && (NULL != h->callback))
179     h->callback (h->callback_cls,
180                  GNUNET_NO, h->contact_addr, sizeof (h->contact_addr));
181   GNUNET_free_non_null (h->contact_addr);
182   h->contact_addr = NULL;
183   GNUNET_free_non_null (h->ext_addr);
184   h->ext_addr = NULL;
185   if (NULL == addr)
186     return;
187   h->ext_addr = GNUNET_malloc (addrlen);
188   memcpy (h->ext_addr, addr, addrlen);
189
190   /* Recreate the ext_addr:public_port bogus address to pass to the callback */
191   if (h->ext_addr->sa_family == AF_INET)
192     {
193       struct sockaddr_in *tmp_addr;
194
195       tmp_addr = GNUNET_malloc (sizeof (struct sockaddr_in));
196       tmp_addr->sin_family = AF_INET;
197 #ifdef HAVE_SOCKADDR_IN_SIN_LEN
198       tmp_addr->sin_len = sizeof (struct sockaddr_in);
199 #endif
200       tmp_addr->sin_port = h->port_mapped ? htons (h->public_port) : 0;
201       tmp_addr->sin_addr = ((struct sockaddr_in *) h->ext_addr)->sin_addr;
202       h->contact_addr = (struct sockaddr *) tmp_addr;
203
204       if (NULL != h->callback)
205         h->callback (h->callback_cls,
206                      GNUNET_YES,
207                      h->contact_addr, sizeof (struct sockaddr_in));
208     }
209   else if (h->ext_addr->sa_family == AF_INET6)
210     {
211       struct sockaddr_in6 *tmp_addr;
212
213       tmp_addr = GNUNET_malloc (sizeof (struct sockaddr_in6));
214       tmp_addr->sin6_family = AF_INET6;
215 #ifdef HAVE_SOCKADDR_IN_SIN_LEN
216       tmp_addr->sin6_len = sizeof (struct sockaddr_in6);
217 #endif
218       tmp_addr->sin6_port = h->port_mapped ? htons (h->public_port) : 0;
219       tmp_addr->sin6_addr = ((struct sockaddr_in6 *) h->ext_addr)->sin6_addr;
220       h->contact_addr = (struct sockaddr *) tmp_addr;
221
222       if (NULL != h->callback)
223         h->callback (h->callback_cls,
224                      GNUNET_YES,
225                      h->contact_addr, sizeof (struct sockaddr_in6));
226     }
227   else
228     {
229       GNUNET_break (0);
230     }
231 }
232
233 static void nat_pulse (void *cls,
234                        const struct GNUNET_SCHEDULER_TaskContext *tc);
235
236 static void
237 pulse_cb (struct GNUNET_NAT_Handle *h)
238 {
239   socklen_t addrlen;
240   int port_mapped;
241
242   /* One of the protocols is still working, wait for it to complete */
243   if (h->processing)
244     return;
245
246   h->new_status = get_traversal_status (h);
247   if ((h->old_status != h->new_status) &&
248       ((h->new_status == GNUNET_NAT_PORT_UNMAPPED) ||
249        (h->new_status == GNUNET_NAT_PORT_ERROR)))
250     GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
251                      "NAT",
252                      _
253                      ("Port redirection failed: no UPnP or NAT-PMP routers supporting this feature found\n"));
254 #ifdef DEBUG
255   if (h->new_status != h->old_status)
256     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "NAT",
257                      _("State changed from `%s' to `%s'\n"),
258                      get_nat_state_str (h->old_status),
259                      get_nat_state_str (h->new_status));
260 #endif
261
262   port_mapped = (h->new_status == GNUNET_NAT_PORT_MAPPED);
263   if (!(h->ext_addr_upnp || h->ext_addr_natpmp))
264     {
265       /* Address has just changed and we could not get it, or it's the first try,
266        * and we're not waiting for a reply from UPnP or NAT-PMP */
267       if (((NULL != h->ext_addr) ||
268            (GNUNET_NO == h->did_warn)) && h->processing != 0)
269         {
270           GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
271                            "NAT",
272                            _("Could not determine external IP address\n"));
273           h->did_warn = GNUNET_YES;
274         }
275       notify_change (h, NULL, 0, port_mapped);
276     }
277   else if (h->ext_addr_upnp
278            && GNUNET_NAT_cmp_addr (h->ext_addr, h->ext_addr_upnp) != 0)
279     {
280       addrlen = h->ext_addr_upnp->sa_family == AF_INET ?
281         sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6);
282       GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
283                        "NAT",
284                        _("External IP address changed to %s\n"),
285                        GNUNET_a2s (h->ext_addr_upnp, addrlen));
286       notify_change (h, h->ext_addr_upnp, addrlen, port_mapped);
287     }
288   else if (h->ext_addr_natpmp
289            && GNUNET_NAT_cmp_addr (h->ext_addr, h->ext_addr_natpmp) != 0)
290     {
291       addrlen = h->ext_addr_natpmp->sa_family == AF_INET ?
292         sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6);
293       GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, "NAT",
294                        _("External IP address changed to `%s'\n"),
295                        GNUNET_a2s (h->ext_addr_natpmp, addrlen));
296       notify_change (h, h->ext_addr_natpmp, addrlen, port_mapped);
297     }
298
299   h->pulse_timer = GNUNET_SCHEDULER_add_delayed (h->sched,
300                                                  GNUNET_TIME_UNIT_SECONDS,
301                                                  &nat_pulse, h);
302 }
303
304 static void
305 upnp_pulse_cb (int status, struct sockaddr *ext_addr, void *cls)
306 {
307   struct GNUNET_NAT_Handle *h = cls;
308
309   h->upnp_status = status;
310   h->ext_addr_upnp = ext_addr;
311
312   h->processing--;
313   pulse_cb (h);
314 }
315
316 #if 0
317 static void
318 natpmp_pulse_cb (int status, struct sockaddr *ext_addr, void *cls)
319 {
320   struct GNUNET_NAT_Handle *h = cls;
321
322   h->natpmp_status = status;
323   h->ext_addr_natpmp = ext_addr;
324
325   h->processing--;
326   pulse_cb (h);
327 }
328 #endif
329
330 static void
331 nat_pulse (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
332 {
333   struct GNUNET_NAT_Handle *h = cls;
334
335   /* Stop if we're already waiting for an action to complete */
336   h->pulse_timer = GNUNET_SCHEDULER_NO_TASK;
337   if (h->processing)
338     return;
339   h->old_status = get_traversal_status (h);
340
341   /* Only update the protocol that has been successful until now */
342   if (h->upnp_status >= GNUNET_NAT_PORT_UNMAPPED)
343     {
344       h->processing = 1;
345       GNUNET_NAT_UPNP_pulse (h->upnp, h->is_enabled, GNUNET_YES);
346
347       /* Wait for the callback to call pulse_cb() to handle changes */
348       return;
349     }
350   else if (h->natpmp_status >= GNUNET_NAT_PORT_UNMAPPED)
351     {
352       h->processing = 1;
353 #if 0
354       GNUNET_NAT_NATPMP_pulse (h->natpmp, h->is_enabled);
355 #endif
356     }
357   else                          /* try both */
358     {
359       h->processing = 2;
360
361       GNUNET_NAT_UPNP_pulse (h->upnp, h->is_enabled, GNUNET_YES);
362 #if 0
363       GNUNET_NAT_NATPMP_pulse (h->natpmp, h->is_enabled, &natpmp_pulse_cb, h);
364 #endif
365     }
366 }
367
368
369 /**
370  * Attempt to enable port redirection and detect public IP address contacting
371  * UPnP or NAT-PMP routers on the local network. Use addr to specify to which
372  * of the local host's addresses should the external port be mapped. The port
373  * is taken from the corresponding sockaddr_in[6] field.
374  *
375  * @param sched the sheduler used in the program
376  * @param addr the local address packets should be redirected to
377  * @param addrlen actual lenght of the address
378  * @param callback function to call everytime the public IP address changes
379  * @param callback_cls closure for callback
380  * @return NULL on error, otherwise handle that can be used to unregister 
381  */
382 struct GNUNET_NAT_Handle *
383 GNUNET_NAT_register (struct GNUNET_SCHEDULER_Handle
384                      *sched,
385                      const struct sockaddr *addr,
386                      socklen_t addrlen,
387                      GNUNET_NAT_AddressCallback callback, void *callback_cls)
388 {
389   struct GNUNET_NAT_Handle *h;
390
391   h = GNUNET_malloc (sizeof (struct GNUNET_NAT_Handle));
392
393   if (addr)
394     {
395       GNUNET_assert ((addr->sa_family == AF_INET) ||
396                      (addr->sa_family == AF_INET6));
397       h->local_addr = GNUNET_malloc (addrlen);
398       memcpy (h->local_addr, addr, addrlen);
399       if (addr->sa_family == AF_INET)
400         {
401           h->public_port = ntohs (((struct sockaddr_in *) addr)->sin_port);
402           ((struct sockaddr_in *) h->local_addr)->sin_port = 0;
403         }
404       else if (addr->sa_family == AF_INET6)
405         {
406           h->public_port = ntohs (((struct sockaddr_in6 *) addr)->sin6_port);
407           ((struct sockaddr_in6 *) h->local_addr)->sin6_port = 0;
408         }
409     }
410   h->should_change = GNUNET_YES;
411   h->sched = sched;
412   h->is_enabled = GNUNET_YES;
413   h->upnp_status = GNUNET_NAT_PORT_UNMAPPED;
414   h->natpmp_status = GNUNET_NAT_PORT_UNMAPPED;
415   h->callback = callback;
416   h->callback_cls = callback_cls;
417   h->upnp =
418     GNUNET_NAT_UPNP_init (h->sched, h->local_addr, addrlen, h->public_port,
419                           &upnp_pulse_cb, h);
420 #if 0
421   h->natpmp =
422     GNUNET_NAT_NATPMP_init (h->sched, h->local_addr, addrlen, h->public_port,
423                             &natpmp_pulse_cb, h);
424 #endif
425   h->pulse_timer = GNUNET_SCHEDULER_add_delayed (sched,
426                                                  GNUNET_TIME_UNIT_SECONDS,
427                                                  &nat_pulse, h);
428   return h;
429 }
430
431
432 /**
433  * Stop port redirection and public IP address detection for the given handle.
434  * This frees the handle, after having sent the needed commands to close open ports.
435  *
436  * @param h the handle to stop
437  */
438 void
439 GNUNET_NAT_unregister (struct GNUNET_NAT_Handle *h)
440 {
441   GNUNET_NAT_UPNP_pulse (h->upnp, GNUNET_NO, GNUNET_NO);
442   GNUNET_NAT_UPNP_close (h->upnp);
443
444 #if 0
445   GNUNET_NAT_NATPMP_pulse (h->natpmp, GNUNET_NO);
446   GNUNET_NAT_NATPMP_close (h->natpmp);
447 #endif
448
449   if (GNUNET_SCHEDULER_NO_TASK != h->pulse_timer)
450     GNUNET_SCHEDULER_cancel (h->sched, h->pulse_timer);
451
452   GNUNET_free_non_null (h->local_addr);
453   GNUNET_free_non_null (h->ext_addr);
454   GNUNET_free_non_null (h->ext_addr_upnp);
455   GNUNET_free_non_null (h->ext_addr_natpmp);
456   GNUNET_free (h);
457 }
458
459 /* end of nat.c */