only for headers
[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   int is_mapped;
69   int has_discovered;
70   int port;
71   time_t renew_time;
72   time_t command_time;
73   NATPMP_state state;
74   natpmp_t natpmp;
75 };
76
77 /**
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, COMP_NAT_NATPMP, _("%s retry (%d)\n"), func, ret);
87 //    return;
88   if (ret >= 0)
89     GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_NATPMP, _("%s succeeded (%d)\n"), func, ret);
90   else
91     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_NATPMP,
92                 "%s failed.  natpmp returned %d (%s); errno is %d (%s)\n",
93                 func, ret, strnatpmperr (ret), errno, strerror (errno));
94 #else
95   return;
96 #endif
97 }
98
99 struct GNUNET_NAT_NATPMP_Handle *
100 GNUNET_NAT_NATPMP_init (const struct sockaddr *addr, socklen_t addrlen,
101                         u_short port)
102 {
103   struct GNUNET_NAT_NATPMP_Handle *nat;
104
105   nat = GNUNET_malloc (sizeof (struct GNUNET_NAT_NATPMP_Handle));
106   nat->state = NATPMP_DISCOVER;
107   nat->port = port;
108   nat->addr = addr;
109   nat->addrlen = addrlen;
110   return nat;
111 }
112
113 void
114 GNUNET_NAT_NATPMP_close (GNUNET_NAT_NATPMP_Handle * nat)
115 {
116   if (nat)
117     {
118       closenatpmp (&nat->natpmp);
119       GNUNET_free (nat);
120     }
121 }
122
123 static int
124 can_send_command (const struct GNUNET_NAT_NATPMP_Handle *nat)
125 {
126   return time (NULL) >= nat->command_time;
127 }
128
129 static void
130 set_command_time (struct GNUNET_NAT_NATPMP_Handle *nat)
131 {
132   nat->command_time = time (NULL) + COMMAND_WAIT_SECS;
133 }
134
135 int
136 GNUNET_NAT_NATPMP_pulse (struct GNUNET_NAT_NATPMP_Handle *nat, int is_enabled,
137                          struct sockaddr **ext_addr)
138 {
139   int ret;
140
141   /* Keep to NULL if address could not be found */
142   *ext_addr = NULL;
143
144   if (is_enabled && (nat->state == NATPMP_DISCOVER))
145     {
146       int val = initnatpmp (&nat->natpmp);
147       log_val ("initnatpmp", val);
148       val = sendpublicaddressrequest (&nat->natpmp);
149       log_val ("sendpublicaddressrequest", val);
150       nat->state = val < 0 ? NATPMP_ERR : NATPMP_RECV_PUB;
151       nat->has_discovered = 1;
152       set_command_time (nat);
153     }
154
155   if ((nat->state == NATPMP_RECV_PUB) && can_send_command (nat))
156     {
157       natpmpresp_t response;
158       const int val = readnatpmpresponseorretry (&nat->natpmp,
159                                                  &response);
160       log_val ("readnatpmpresponseorretry", val);
161       if (val >= 0)
162         {
163           *ext_addr =
164             GNUNET_malloc (sizeof (response.pnu.publicaddress.addr));
165           memcpy (*ext_addr, &response.pnu.publicaddress.addr,
166                   (sizeof (response.pnu.publicaddress.addr)));
167 #ifdef DEBUG
168           GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_NATPMP,
169                       _("Found public IP address %s\n"),
170                       GNUNET_a2s (*ext_addr, sizeof (*ext_addr)));
171 #endif
172           nat->state = NATPMP_IDLE;
173         }
174       else if (val != NATPMP_TRYAGAIN)
175         {
176           nat->state = NATPMP_ERR;
177         }
178     }
179
180   if ((nat->state == NATPMP_IDLE) || (nat->state == NATPMP_ERR))
181     {
182       if (nat->is_mapped && !is_enabled)
183         nat->state = NATPMP_SEND_UNMAP;
184     }
185
186   if ((nat->state == NATPMP_SEND_UNMAP) && can_send_command (nat))
187     {
188       const int val =
189         sendnewportmappingrequest (&nat->natpmp, NATPMP_PROTOCOL_TCP,
190                                    nat->port, nat->port,
191                                    0);
192       log_val ("sendnewportmappingrequest", val);
193       nat->state = val < 0 ? NATPMP_ERR : NATPMP_RECV_UNMAP;
194       set_command_time (nat);
195     }
196
197   if (nat->state == NATPMP_RECV_UNMAP)
198     {
199       natpmpresp_t resp;
200       const int val = readnatpmpresponseorretry (&nat->natpmp, &resp);
201       log_val ("readnatpmpresponseorretry", val);
202       if (val >= 0)
203         {
204           const int p = resp.pnu.newportmapping.privateport;
205           GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_NATPMP,
206                       _("No longer forwarding port %d\n"), p);
207           if (nat->port == p)
208             {
209               nat->port = -1;
210               nat->state = NATPMP_IDLE;
211               nat->is_mapped = 0;
212             }
213         }
214       else if (val != NATPMP_TRYAGAIN)
215         {
216           nat->state = NATPMP_ERR;
217         }
218     }
219
220   if (nat->state == NATPMP_IDLE)
221     {
222       if (is_enabled && !nat->is_mapped && nat->has_discovered)
223         nat->state = NATPMP_SEND_MAP;
224
225       else if (nat->is_mapped && time (NULL) >= nat->renew_time)
226         nat->state = NATPMP_SEND_MAP;
227     }
228
229   if ((nat->state == NATPMP_SEND_MAP) && can_send_command (nat))
230     {
231       const int val =
232         sendnewportmappingrequest (&nat->natpmp, NATPMP_PROTOCOL_TCP,
233                                    nat->port,
234                                    nat->port,
235                                    LIFETIME_SECS);
236       log_val ("sendnewportmappingrequest", val);
237       nat->state = val < 0 ? NATPMP_ERR : NATPMP_RECV_MAP;
238       set_command_time (nat);
239     }
240
241   if (nat->state == NATPMP_RECV_MAP)
242     {
243       natpmpresp_t resp;
244       const int val = readnatpmpresponseorretry (&nat->natpmp, &resp);
245       log_val ("readnatpmpresponseorretry", val);
246       if (val >= 0)
247         {
248           nat->state = NATPMP_IDLE;
249           nat->is_mapped = 1;
250           nat->renew_time = time (NULL) + LIFETIME_SECS;
251           nat->port = resp.pnu.newportmapping.privateport;
252           GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_NATPMP,
253                       _("Port %d forwarded successfully\n"), nat->port);
254         }
255       else if (val != NATPMP_TRYAGAIN)
256         {
257           nat->state = NATPMP_ERR;
258         }
259     }
260
261   switch (nat->state)
262     {
263     case NATPMP_IDLE:
264       ret =
265         nat->is_mapped ? GNUNET_NAT_PORT_MAPPED : GNUNET_NAT_PORT_UNMAPPED;
266       break;
267
268     case NATPMP_DISCOVER:
269       ret = GNUNET_NAT_PORT_UNMAPPED;
270       break;
271
272     case NATPMP_RECV_PUB:
273     case NATPMP_SEND_MAP:
274     case NATPMP_RECV_MAP:
275       ret = GNUNET_NAT_PORT_MAPPING;
276       break;
277
278     case NATPMP_SEND_UNMAP:
279     case NATPMP_RECV_UNMAP:
280       ret = GNUNET_NAT_PORT_UNMAPPING;
281       break;
282
283     default:
284       ret = GNUNET_NAT_PORT_ERROR;
285       break;
286     }
287   return ret;
288 }