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 2, 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
37 #if 0 /* keep Emacsens' auto-indent happy */
47 #define ENABLE_STRNATPMPERR
48 #include <libnatpmp/natpmp.h>
51 #include "gnunet_common.h"
52 #include "gnunet_nat_lib.h"
55 #define LIFETIME_SECS 3600
56 #define COMMAND_WAIT_SECS 8
57 /* Component name for logging */
58 #define COMP_NAT_NATPMP _("NAT (NAT-PMP))")
73 struct GNUNET_NAT_NATPMP_Handle
75 const struct sockaddr *addr;
91 log_val (const char *func, int ret)
94 if (ret == NATPMP_TRYAGAIN)
95 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_NATPMP, _("%s retry (%d)\n"), func, ret);
98 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_NATPMP, _("%s succeeded (%d)\n"), func, ret);
100 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_NATPMP,
101 "%s failed. natpmp returned %d (%s); errno is %d (%s)\n",
102 func, ret, strnatpmperr (ret), errno, strerror (errno));
108 struct GNUNET_NAT_NATPMP_Handle *
109 GNUNET_NAT_NATPMP_init (const struct sockaddr *addr, socklen_t addrlen,
112 struct GNUNET_NAT_NATPMP_Handle *nat;
114 nat = GNUNET_malloc (sizeof (struct GNUNET_NAT_NATPMP_Handle));
115 nat->state = NATPMP_DISCOVER;
118 nat->addrlen = addrlen;
123 GNUNET_NAT_NATPMP_close (GNUNET_NAT_NATPMP_Handle * nat)
127 closenatpmp (&nat->natpmp);
133 can_send_command (const struct GNUNET_NAT_NATPMP_Handle *nat)
135 return time (NULL) >= nat->command_time;
139 set_command_time (struct GNUNET_NAT_NATPMP_Handle *nat)
141 nat->command_time = time (NULL) + COMMAND_WAIT_SECS;
145 GNUNET_NAT_NATPMP_pulse (struct GNUNET_NAT_NATPMP_Handle *nat, int is_enabled,
146 struct sockaddr **ext_addr)
150 /* Keep to NULL if address could not be found */
153 if (is_enabled && (nat->state == NATPMP_DISCOVER))
155 int val = initnatpmp (&nat->natpmp);
156 log_val ("initnatpmp", val);
157 val = sendpublicaddressrequest (&nat->natpmp);
158 log_val ("sendpublicaddressrequest", val);
159 nat->state = val < 0 ? NATPMP_ERR : NATPMP_RECV_PUB;
160 nat->has_discovered = 1;
161 set_command_time (nat);
164 if ((nat->state == NATPMP_RECV_PUB) && can_send_command (nat))
166 natpmpresp_t response;
167 const int val = readnatpmpresponseorretry (&nat->natpmp,
169 log_val ("readnatpmpresponseorretry", val);
173 GNUNET_malloc (sizeof (response.pnu.publicaddress.addr));
174 memcpy (*ext_addr, &response.pnu.publicaddress.addr,
175 (sizeof (response.pnu.publicaddress.addr)));
177 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_NATPMP,
178 _("Found public IP address %s\n"),
179 GNUNET_a2s (*ext_addr, sizeof (*ext_addr)));
181 nat->state = NATPMP_IDLE;
183 else if (val != NATPMP_TRYAGAIN)
185 nat->state = NATPMP_ERR;
189 if ((nat->state == NATPMP_IDLE) || (nat->state == NATPMP_ERR))
191 if (nat->is_mapped && !is_enabled)
192 nat->state = NATPMP_SEND_UNMAP;
195 if ((nat->state == NATPMP_SEND_UNMAP) && can_send_command (nat))
198 sendnewportmappingrequest (&nat->natpmp, NATPMP_PROTOCOL_TCP,
199 nat->port, nat->port,
201 log_val ("sendnewportmappingrequest", val);
202 nat->state = val < 0 ? NATPMP_ERR : NATPMP_RECV_UNMAP;
203 set_command_time (nat);
206 if (nat->state == NATPMP_RECV_UNMAP)
209 const int val = readnatpmpresponseorretry (&nat->natpmp, &resp);
210 log_val ("readnatpmpresponseorretry", val);
213 const int p = resp.pnu.newportmapping.privateport;
214 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_NATPMP,
215 _("No longer forwarding port %d\n"), p);
219 nat->state = NATPMP_IDLE;
223 else if (val != NATPMP_TRYAGAIN)
225 nat->state = NATPMP_ERR;
229 if (nat->state == NATPMP_IDLE)
231 if (is_enabled && !nat->is_mapped && nat->has_discovered)
232 nat->state = NATPMP_SEND_MAP;
234 else if (nat->is_mapped && time (NULL) >= nat->renew_time)
235 nat->state = NATPMP_SEND_MAP;
238 if ((nat->state == NATPMP_SEND_MAP) && can_send_command (nat))
241 sendnewportmappingrequest (&nat->natpmp, NATPMP_PROTOCOL_TCP,
245 log_val ("sendnewportmappingrequest", val);
246 nat->state = val < 0 ? NATPMP_ERR : NATPMP_RECV_MAP;
247 set_command_time (nat);
250 if (nat->state == NATPMP_RECV_MAP)
253 const int val = readnatpmpresponseorretry (&nat->natpmp, &resp);
254 log_val ("readnatpmpresponseorretry", val);
257 nat->state = NATPMP_IDLE;
259 nat->renew_time = time (NULL) + LIFETIME_SECS;
260 nat->port = resp.pnu.newportmapping.privateport;
261 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_NATPMP,
262 _("Port %d forwarded successfully\n"), nat->port);
264 else if (val != NATPMP_TRYAGAIN)
266 nat->state = NATPMP_ERR;
274 nat->is_mapped ? GNUNET_NAT_PORT_MAPPED : GNUNET_NAT_PORT_UNMAPPED;
277 case NATPMP_DISCOVER:
278 ret = GNUNET_NAT_PORT_UNMAPPED;
281 case NATPMP_RECV_PUB:
282 case NATPMP_SEND_MAP:
283 case NATPMP_RECV_MAP:
284 ret = GNUNET_NAT_PORT_MAPPING;
287 case NATPMP_SEND_UNMAP:
288 case NATPMP_RECV_UNMAP:
289 ret = GNUNET_NAT_PORT_UNMAPPING;
293 ret = GNUNET_NAT_PORT_ERROR;