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