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 UPnP support for the NAT library
31 * @author Milan Bouchet-Valat
39 #include "gnunet_common.h"
40 #include "gnunet_nat_lib.h"
42 #include "upnp-discover.h"
43 #include "upnp-commands.h"
46 /* Component name for logging */
47 #define COMP_NAT_UPNP _("NAT (UPnP)")
58 struct GNUNET_NAT_UPNP_Handle
64 const struct sockaddr *addr;
66 unsigned int is_mapped;
67 enum UPNP_State state;
68 struct sockaddr *ext_addr;
71 GNUNET_NAT_UPNP_pulse_cb pulse_cb;
76 process_if (void *cls,
78 int isDefault, const struct sockaddr *addr, socklen_t addrlen)
80 struct GNUNET_NAT_UPNP_Handle *upnp = cls;
82 if (addr && GNUNET_NAT_cmp_addr (upnp->addr, addr) == 0)
84 upnp->iface = name; // BADNESS!
92 struct GNUNET_NAT_UPNP_Handle *
93 GNUNET_NAT_UPNP_init (const struct sockaddr *addr,
96 GNUNET_NAT_UPNP_pulse_cb pulse_cb, void *pulse_cls)
98 struct GNUNET_NAT_UPNP_Handle *handle;
100 handle = GNUNET_malloc (sizeof (struct GNUNET_NAT_UPNP_Handle));
101 handle->processing = GNUNET_NO;
102 handle->state = UPNP_DISCOVER;
104 handle->addrlen = addrlen;
106 handle->pulse_cb = pulse_cb;
107 handle->pulse_cls = pulse_cls;
108 handle->control_url = NULL;
109 handle->service_type = NULL;
111 /* Find the interface corresponding to the address,
112 * on which we should broadcast call for routers */
113 GNUNET_OS_network_interfaces_list (&process_if, handle);
115 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
117 "Could not find an interface matching the wanted address.\n");
123 GNUNET_NAT_UPNP_close (struct GNUNET_NAT_UPNP_Handle *handle)
125 GNUNET_assert (!handle->is_mapped);
126 GNUNET_assert ((handle->state == UPNP_IDLE)
127 || (handle->state == UPNP_ERR)
128 || (handle->state == UPNP_DISCOVER));
130 GNUNET_free_non_null (handle->control_url);
131 GNUNET_free_non_null (handle->service_type);
132 GNUNET_free (handle);
136 pulse_finish (struct GNUNET_NAT_UPNP_Handle *handle)
138 enum GNUNET_NAT_PortState status;
139 handle->processing = GNUNET_NO;
141 switch (handle->state)
144 status = GNUNET_NAT_PORT_UNMAPPED;
148 status = GNUNET_NAT_PORT_MAPPING;
152 status = GNUNET_NAT_PORT_UNMAPPING;
157 handle->is_mapped ? GNUNET_NAT_PORT_MAPPED : GNUNET_NAT_PORT_UNMAPPED;
161 status = GNUNET_NAT_PORT_ERROR;
165 handle->pulse_cb (status, handle->ext_addr, handle->pulse_cls);
169 discover_cb (const char *control_url, const char *service_type, void *cls)
171 struct GNUNET_NAT_UPNP_Handle *handle = cls;
175 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
176 _("Found Internet Gateway Device \"%s\"\n"),
179 GNUNET_free_non_null (handle->control_url);
180 GNUNET_free_non_null (handle->service_type);
182 handle->control_url = GNUNET_strdup (control_url);
183 handle->service_type = GNUNET_strdup (service_type);
184 handle->state = UPNP_IDLE;
185 handle->hasDiscovered = 1;
189 handle->control_url = NULL;
190 handle->service_type = NULL;
191 handle->state = UPNP_ERR;
193 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP,
194 "UPNP device discovery failed\n");
198 pulse_finish (handle);
202 check_port_mapping_cb (int error, const char *control_url,
203 const char *service_type, const char *extPort,
204 const char *inPort, const char *proto,
205 const char *remoteHost, void *cls)
207 struct GNUNET_NAT_UPNP_Handle *handle = cls;
211 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
212 _("Port %d isn't forwarded\n"), handle->port);
213 handle->is_mapped = GNUNET_NO;
216 pulse_finish (handle);
220 delete_port_mapping_cb (int error, const char *control_url,
221 const char *service_type, const char *extPort,
222 const char *inPort, const char *proto,
223 const char *remoteHost, void *cls)
225 struct GNUNET_NAT_UPNP_Handle *handle = cls;
228 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
230 ("Could not stop port forwarding through \"%s\", service \"%s\": error %d\n"),
231 handle->control_url, handle->service_type, error);
234 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
236 ("Stopped port forwarding through \"%s\", service \"%s\"\n"),
237 handle->control_url, handle->service_type);
238 handle->is_mapped = !error;
239 handle->state = UPNP_IDLE;
243 pulse_finish (handle);
247 add_port_mapping_cb (int error, const char *control_url,
248 const char *service_type, const char *extPort,
249 const char *inPort, const char *proto,
250 const char *remoteHost, void *cls)
252 struct GNUNET_NAT_UPNP_Handle *handle = cls;
256 handle->is_mapped = GNUNET_NO;
257 handle->state = UPNP_ERR;
258 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
260 ("Port forwarding through \"%s\", service \"%s\" failed with error %d\n"),
261 handle->control_url, handle->service_type, error);
266 handle->is_mapped = GNUNET_NO;
267 handle->state = UPNP_IDLE;
268 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
269 _("Port %d forwarded successfully\n"), handle->port);
272 pulse_finish (handle);
276 get_ip_address_cb (int error, char *ext_addr, void *cls)
278 struct GNUNET_NAT_UPNP_Handle *handle = cls;
282 if (handle->ext_addr)
284 GNUNET_free (handle->ext_addr);
285 handle->ext_addr = NULL;
288 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP,
289 "UPNP_get_external_ip_address_ failed (error %d)\n",
296 struct in6_addr addr6;
298 if (handle->ext_addr)
300 GNUNET_free (handle->ext_addr);
301 handle->ext_addr = NULL;
304 /* Try IPv4 and IPv6 as we don't know what's the format */
306 if (inet_aton (ext_addr, &addr) != 0)
308 addr.S_un.S_addr = inet_addr(ext_addr);
309 if (addr.S_un.S_addr == INADDR_NONE)
312 handle->ext_addr = GNUNET_malloc (sizeof (struct sockaddr_in));
313 handle->ext_addr->sa_family = AF_INET;
314 ((struct sockaddr_in *) handle->ext_addr)->sin_addr = addr;
316 else if (inet_pton (AF_INET6, ext_addr, &addr6) != 1)
318 handle->ext_addr = GNUNET_malloc (sizeof (struct sockaddr_in6));
319 handle->ext_addr->sa_family = AF_INET6;
320 ((struct sockaddr_in6 *) handle->ext_addr)->sin6_addr = addr6;
323 GNUNET_assert (GNUNET_YES);
326 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP,
327 _("Found public IP address %s\n"), ext_addr);
331 pulse_finish (handle);
335 * Check state of UPnP NAT: port redirection, external IP address.
338 * @param handle the handle for UPnP object
339 * @param is_enabled whether enable port redirection
340 * @param doPortCheck FIXME
341 * @param ext_addr pointer for returning external IP address.
342 * Will be set to NULL if address could not be found. Don't free the sockaddr.
345 GNUNET_NAT_UPNP_pulse (struct GNUNET_NAT_UPNP_Handle *handle,
346 int is_enabled, int doPortCheck)
348 /* Stop if we're already waiting for an action to complete */
349 if (handle->processing == GNUNET_YES)
352 if (is_enabled && (handle->state == UPNP_DISCOVER))
354 handle->processing = GNUNET_YES;
355 UPNP_discover_ (handle->iface, handle->addr, discover_cb,
359 if (handle->state == UPNP_IDLE)
361 if (handle->is_mapped && !is_enabled)
362 handle->state = UPNP_UNMAP;
365 if (is_enabled && handle->is_mapped && doPortCheck)
369 GNUNET_snprintf (portStr, sizeof (portStr), "%d", handle->port);
371 handle->processing = GNUNET_YES;
372 UPNP_get_specific_port_mapping_entry_ (handle->control_url,
373 handle->service_type, portStr,
374 "TCP", check_port_mapping_cb,
378 if (handle->state == UPNP_UNMAP)
381 GNUNET_snprintf (portStr, sizeof (portStr), "%d", handle->port);
383 handle->processing = GNUNET_YES;
384 UPNP_delete_port_mapping_ (handle->control_url,
385 handle->service_type, portStr, "TCP", NULL,
386 delete_port_mapping_cb, handle);
389 if (handle->state == UPNP_IDLE)
391 if (is_enabled && !handle->is_mapped)
392 handle->state = UPNP_MAP;
395 if (handle->state == UPNP_MAP)
397 if (!handle->control_url)
398 handle->is_mapped = 0;
403 GNUNET_snprintf (portStr, sizeof (portStr), "%d", handle->port);
404 GNUNET_snprintf (desc, sizeof (desc), "GNUnet at %d", handle->port);
406 handle->processing = GNUNET_YES;
407 UPNP_add_port_mapping_ (handle->control_url,
408 handle->service_type,
409 portStr, portStr, GNUNET_a2s (handle->addr,
411 desc, "TCP", NULL, add_port_mapping_cb,
416 if (handle->state != UPNP_DISCOVER)
418 handle->processing = GNUNET_YES;
419 UPNP_get_external_ip_address_ (handle->control_url,
420 handle->service_type,
421 get_ip_address_cb, handle);