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