2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
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.
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.
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.
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>
29 * @brief NAT-PMP support for the NAT library
31 * @author Milan Bouchet-Valat
38 #define ENABLE_STRNATPMPERR
39 #include <libnatpmp/natpmp.h>
42 #include "gnunet_common.h"
43 #include "gnunet_nat_lib.h"
47 #define LIFETIME_SECS 3600
48 #define COMMAND_WAIT_SECS 8
49 /* Component name for logging */
50 #define COMP_NAT_NATPMP _("NAT (NAT-PMP))")
65 struct GNUNET_NAT_NATPMP_Handle
67 const struct sockaddr *addr;
69 struct sockaddr *ext_addr;
75 enum NATPMP_state state;
76 struct natpmp_t natpmp;
81 log_val (const char *func, int ret)
84 if (ret == NATPMP_TRYAGAIN)
85 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
86 COMP_NAT_NATPMP, _("%s retry (%d)\n"), func, ret);
88 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
89 COMP_NAT_NATPMP, _("%s succeeded (%d)\n"), func, ret);
91 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
93 "%s failed. natpmp returned %d (%s); errno is %d (%s)\n",
94 func, ret, strnatpmperr (ret), errno, strerror (errno));
98 struct GNUNET_NAT_NATPMP_Handle *
99 GNUNET_NAT_NATPMP_init (const struct sockaddr *addr, socklen_t addrlen,
102 struct GNUNET_NAT_NATPMP_Handle *nat;
104 nat = GNUNET_malloc (sizeof (struct GNUNET_NAT_NATPMP_Handle));
105 nat->state = NATPMP_DISCOVER;
108 nat->addrlen = addrlen;
113 GNUNET_NAT_NATPMP_close (struct GNUNET_NAT_NATPMP_Handle *nat)
117 closenatpmp (&nat->natpmp);
123 can_send_command (const struct GNUNET_NAT_NATPMP_Handle *nat)
125 return time (NULL) >= nat->command_time;
129 set_command_time (struct GNUNET_NAT_NATPMP_Handle *nat)
131 nat->command_time = time (NULL) + COMMAND_WAIT_SECS;
135 GNUNET_NAT_NATPMP_pulse (struct GNUNET_NAT_NATPMP_Handle *nat, int is_enabled,
136 struct sockaddr **ext_addr)
139 char buf[INET6_ADDRSTRLEN];
141 struct sockaddr_in *v4;
142 struct sockaddr_in6 *v6;
145 /* Keep to NULL if address could not be found */
148 if (is_enabled && (nat->state == NATPMP_DISCOVER))
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);
159 if ((nat->state == NATPMP_RECV_PUB) && can_send_command (nat))
161 struct natpmpresp_t response;
162 const int val = readnatpmpresponseorretry (&nat->natpmp,
164 log_val ("readnatpmpresponseorretry", val);
167 if (NULL != nat->ext_addr)
169 GNUNET_free (nat->ext_addr);
170 nat->ext_addr = NULL;
173 if (response.pnu.publicaddress.family == AF_INET)
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));
182 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_NATPMP,
183 _("Found public IP address %s\n"),
185 &response.pnu.publicaddress.addr,
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)));
199 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_NATPMP,
200 _("Found public IP address %s\n"),
202 &response.pnu.publicaddress.addr6,
206 *ext_addr = nat->ext_addr;
207 nat->state = NATPMP_IDLE;
209 else if (val != NATPMP_TRYAGAIN)
211 nat->state = NATPMP_ERR;
215 if ((nat->state == NATPMP_IDLE) || (nat->state == NATPMP_ERR))
217 if (nat->is_mapped && !is_enabled)
218 nat->state = NATPMP_SEND_UNMAP;
221 if ((nat->state == NATPMP_SEND_UNMAP) && can_send_command (nat))
224 sendnewportmappingrequest (&nat->natpmp, NATPMP_PROTOCOL_TCP,
225 nat->port, nat->port,
227 log_val ("sendnewportmappingrequest", val);
228 nat->state = val < 0 ? NATPMP_ERR : NATPMP_RECV_UNMAP;
229 set_command_time (nat);
232 if (nat->state == NATPMP_RECV_UNMAP)
234 struct natpmpresp_t resp;
235 const int val = readnatpmpresponseorretry (&nat->natpmp, &resp);
236 log_val ("readnatpmpresponseorretry", val);
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);
245 nat->state = NATPMP_IDLE;
249 else if (val != NATPMP_TRYAGAIN)
251 nat->state = NATPMP_ERR;
255 if (nat->state == NATPMP_IDLE)
257 if (is_enabled && !nat->is_mapped && nat->has_discovered)
258 nat->state = NATPMP_SEND_MAP;
260 else if (nat->is_mapped && time (NULL) >= nat->renew_time)
261 nat->state = NATPMP_SEND_MAP;
264 if ((nat->state == NATPMP_SEND_MAP) && can_send_command (nat))
267 sendnewportmappingrequest (&nat->natpmp, NATPMP_PROTOCOL_TCP,
271 log_val ("sendnewportmappingrequest", val);
272 nat->state = val < 0 ? NATPMP_ERR : NATPMP_RECV_MAP;
273 set_command_time (nat);
276 if (nat->state == NATPMP_RECV_MAP)
278 struct natpmpresp_t resp;
279 const int val = readnatpmpresponseorretry (&nat->natpmp, &resp);
280 log_val ("readnatpmpresponseorretry", val);
283 nat->state = NATPMP_IDLE;
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);
290 else if (val != NATPMP_TRYAGAIN)
292 nat->state = NATPMP_ERR;
300 nat->is_mapped ? GNUNET_NAT_PORT_MAPPED : GNUNET_NAT_PORT_UNMAPPED;
303 case NATPMP_DISCOVER:
304 ret = GNUNET_NAT_PORT_UNMAPPED;
307 case NATPMP_RECV_PUB:
308 case NATPMP_SEND_MAP:
309 case NATPMP_RECV_MAP:
310 ret = GNUNET_NAT_PORT_MAPPING;
313 case NATPMP_SEND_UNMAP:
314 case NATPMP_RECV_UNMAP:
315 ret = GNUNET_NAT_PORT_UNMAPPING;
319 ret = GNUNET_NAT_PORT_ERROR;
325 /* end of natpmp.c */