longer timeout to make test work on slower machines, but the problem is still that...
[oweals/gnunet.git] / src / nat / natpmp.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  * This file has 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/natpmp.c
29  * @brief NAT-PMP support for the NAT library
30  *
31  * @author Milan Bouchet-Valat
32  */
33 #include <assert.h>
34 #include <errno.h>
35 #include <time.h>
36 #include <inttypes.h>
37
38 #define ENABLE_STRNATPMPERR
39 #include <libnatpmp/natpmp.h>
40
41 #include "platform.h"
42 #include "gnunet_common.h"
43 #include "gnunet_nat_lib.h"
44 #include "natpmp.h"
45
46 #define LIFETIME_SECS 3600
47 #define COMMAND_WAIT_SECS 8
48 /* Component name for logging */
49 #define COMP_NAT_NATPMP _("NAT (NAT-PMP))")
50
51 typedef enum
52 {
53   NATPMP_IDLE,
54   NATPMP_ERR,
55   NATPMP_DISCOVER,
56   NATPMP_RECV_PUB,
57   NATPMP_SEND_MAP,
58   NATPMP_RECV_MAP,
59   NATPMP_SEND_UNMAP,
60   NATPMP_RECV_UNMAP
61 }
62 NATPMP_state;
63
64 struct GNUNET_NAT_NATPMP_Handle
65 {
66   const struct sockaddr *addr;
67   socklen_t addrlen;
68   struct sockaddr *ext_addr;
69   int is_mapped;
70   int has_discovered;
71   int port;
72   time_t renew_time;
73   time_t command_time;
74   NATPMP_state state;
75   natpmp_t natpmp;
76 };
77
78 /**
79 ***
80 **/
81
82 static void
83 log_val (const char *func, int ret)
84 {
85 #ifdef DEBUG
86   if (ret == NATPMP_TRYAGAIN)
87     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_NATPMP, _("%s retry (%d)\n"), func, ret);
88 //    return;
89   if (ret >= 0)
90     GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_NATPMP, _("%s succeeded (%d)\n"), func, ret);
91   else
92     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_NATPMP,
93                 "%s failed.  natpmp returned %d (%s); errno is %d (%s)\n",
94                 func, ret, strnatpmperr (ret), errno, strerror (errno));
95 #else
96   return;
97 #endif
98 }
99
100 struct GNUNET_NAT_NATPMP_Handle *
101 GNUNET_NAT_NATPMP_init (const struct sockaddr *addr, socklen_t addrlen,
102                         u_short port)
103 {
104   struct GNUNET_NAT_NATPMP_Handle *nat;
105
106   nat = GNUNET_malloc (sizeof (struct GNUNET_NAT_NATPMP_Handle));
107   nat->state = NATPMP_DISCOVER;
108   nat->port = port;
109   nat->addr = addr;
110   nat->addrlen = addrlen;
111   nat->ext_addr = NULL;
112   return nat;
113 }
114
115 void
116 GNUNET_NAT_NATPMP_close (GNUNET_NAT_NATPMP_Handle * nat)
117 {
118   if (nat)
119     {
120       closenatpmp (&nat->natpmp);
121       GNUNET_free (nat);
122     }
123 }
124
125 static int
126 can_send_command (const struct GNUNET_NAT_NATPMP_Handle *nat)
127 {
128   return time (NULL) >= nat->command_time;
129 }
130
131 static void
132 set_command_time (struct GNUNET_NAT_NATPMP_Handle *nat)
133 {
134   nat->command_time = time (NULL) + COMMAND_WAIT_SECS;
135 }
136
137 int
138 GNUNET_NAT_NATPMP_pulse (struct GNUNET_NAT_NATPMP_Handle *nat, int is_enabled,
139                          struct sockaddr **ext_addr)
140 {
141   int ret;
142
143   /* Keep to NULL if address could not be found */
144   *ext_addr = NULL;
145
146   if (is_enabled && (nat->state == NATPMP_DISCOVER))
147     {
148       int val = initnatpmp (&nat->natpmp);
149       log_val ("initnatpmp", val);
150       val = sendpublicaddressrequest (&nat->natpmp);
151       log_val ("sendpublicaddressrequest", val);
152       nat->state = val < 0 ? NATPMP_ERR : NATPMP_RECV_PUB;
153       nat->has_discovered = 1;
154       set_command_time (nat);
155     }
156
157   if ((nat->state == NATPMP_RECV_PUB) && can_send_command (nat))
158     {
159       natpmpresp_t response;
160       const int val = readnatpmpresponseorretry (&nat->natpmp,
161                                                  &response);
162       log_val ("readnatpmpresponseorretry", val);
163       if (val >= 0)
164         {
165           if (nat->ext_addr)
166             {
167               GNUNET_free (nat->ext_addr);
168               nat->ext_addr = NULL;
169             }
170
171           if (response.pnu.publicaddress.family == AF_INET)
172             {
173               nat->ext_addr =
174                 GNUNET_malloc (sizeof (struct in_addr));
175               memcpy (nat->ext_addr, &response.pnu.publicaddress.addr,
176                       sizeof (struct in_addr));
177             }
178           else
179             {
180               nat->ext_addr =
181                 GNUNET_malloc (sizeof (struct in6_addr));
182               memcpy (nat->ext_addr, &response.pnu.publicaddress.addr6,
183                       (sizeof (struct in6_addr)));
184             }
185
186             *ext_addr = nat->ext_addr;
187 #ifdef DEBUG
188           GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_NATPMP,
189                       _("Found public IP address %s\n"),
190                       GNUNET_a2s (*ext_addr, sizeof (*ext_addr)));
191 #endif
192           nat->state = NATPMP_IDLE;
193         }
194       else if (val != NATPMP_TRYAGAIN)
195         {
196           nat->state = NATPMP_ERR;
197         }
198     }
199
200   if ((nat->state == NATPMP_IDLE) || (nat->state == NATPMP_ERR))
201     {
202       if (nat->is_mapped && !is_enabled)
203         nat->state = NATPMP_SEND_UNMAP;
204     }
205
206   if ((nat->state == NATPMP_SEND_UNMAP) && can_send_command (nat))
207     {
208       const int val =
209         sendnewportmappingrequest (&nat->natpmp, NATPMP_PROTOCOL_TCP,
210                                    nat->port, nat->port,
211                                    0);
212       log_val ("sendnewportmappingrequest", val);
213       nat->state = val < 0 ? NATPMP_ERR : NATPMP_RECV_UNMAP;
214       set_command_time (nat);
215     }
216
217   if (nat->state == NATPMP_RECV_UNMAP)
218     {
219       natpmpresp_t resp;
220       const int val = readnatpmpresponseorretry (&nat->natpmp, &resp);
221       log_val ("readnatpmpresponseorretry", val);
222       if (val >= 0)
223         {
224           const int p = resp.pnu.newportmapping.privateport;
225           GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_NATPMP,
226                       _("No longer forwarding port %d\n"), p);
227           if (nat->port == p)
228             {
229               nat->port = -1;
230               nat->state = NATPMP_IDLE;
231               nat->is_mapped = 0;
232             }
233         }
234       else if (val != NATPMP_TRYAGAIN)
235         {
236           nat->state = NATPMP_ERR;
237         }
238     }
239
240   if (nat->state == NATPMP_IDLE)
241     {
242       if (is_enabled && !nat->is_mapped && nat->has_discovered)
243         nat->state = NATPMP_SEND_MAP;
244
245       else if (nat->is_mapped && time (NULL) >= nat->renew_time)
246         nat->state = NATPMP_SEND_MAP;
247     }
248
249   if ((nat->state == NATPMP_SEND_MAP) && can_send_command (nat))
250     {
251       const int val =
252         sendnewportmappingrequest (&nat->natpmp, NATPMP_PROTOCOL_TCP,
253                                    nat->port,
254                                    nat->port,
255                                    LIFETIME_SECS);
256       log_val ("sendnewportmappingrequest", val);
257       nat->state = val < 0 ? NATPMP_ERR : NATPMP_RECV_MAP;
258       set_command_time (nat);
259     }
260
261   if (nat->state == NATPMP_RECV_MAP)
262     {
263       natpmpresp_t resp;
264       const int val = readnatpmpresponseorretry (&nat->natpmp, &resp);
265       log_val ("readnatpmpresponseorretry", val);
266       if (val >= 0)
267         {
268           nat->state = NATPMP_IDLE;
269           nat->is_mapped = 1;
270           nat->renew_time = time (NULL) + LIFETIME_SECS;
271           nat->port = resp.pnu.newportmapping.privateport;
272           GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_NATPMP,
273                       _("Port %d forwarded successfully\n"), nat->port);
274         }
275       else if (val != NATPMP_TRYAGAIN)
276         {
277           nat->state = NATPMP_ERR;
278         }
279     }
280
281   switch (nat->state)
282     {
283     case NATPMP_IDLE:
284       ret =
285         nat->is_mapped ? GNUNET_NAT_PORT_MAPPED : GNUNET_NAT_PORT_UNMAPPED;
286       break;
287
288     case NATPMP_DISCOVER:
289       ret = GNUNET_NAT_PORT_UNMAPPED;
290       break;
291
292     case NATPMP_RECV_PUB:
293     case NATPMP_SEND_MAP:
294     case NATPMP_RECV_MAP:
295       ret = GNUNET_NAT_PORT_MAPPING;
296       break;
297
298     case NATPMP_SEND_UNMAP:
299     case NATPMP_RECV_UNMAP:
300       ret = GNUNET_NAT_PORT_UNMAPPING;
301       break;
302
303     default:
304       ret = GNUNET_NAT_PORT_ERROR;
305       break;
306     }
307   return ret;
308 }