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