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