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 */
305 if (inet_aton (ext_addr, &addr) != 0)
307 handle->ext_addr = GNUNET_malloc (sizeof (struct sockaddr_in));
308 handle->ext_addr->sa_family = AF_INET;
309 ((struct sockaddr_in *) handle->ext_addr)->sin_addr = addr;
311 else if (inet_pton (AF_INET6, ext_addr, &addr6) != 1)
313 handle->ext_addr = GNUNET_malloc (sizeof (struct sockaddr_in6));
314 handle->ext_addr->sa_family = AF_INET6;
315 ((struct sockaddr_in6 *) handle->ext_addr)->sin6_addr = addr6;
318 GNUNET_assert (GNUNET_YES);
321 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP,
322 _("Found public IP address %s\n"), ext_addr);
326 pulse_finish (handle);
330 * Check state of UPnP NAT: port redirection, external IP address.
333 * @param handle the handle for UPnP object
334 * @param is_enabled whether enable port redirection
335 * @param doPortCheck FIXME
336 * @param ext_addr pointer for returning external IP address.
337 * Will be set to NULL if address could not be found. Don't free the sockaddr.
340 GNUNET_NAT_UPNP_pulse (struct GNUNET_NAT_UPNP_Handle *handle,
341 int is_enabled, int doPortCheck)
343 /* Stop if we're already waiting for an action to complete */
344 if (handle->processing == GNUNET_YES)
347 if (is_enabled && (handle->state == UPNP_DISCOVER))
349 handle->processing = GNUNET_YES;
350 UPNP_discover_ (handle->iface, handle->addr, discover_cb,
354 if (handle->state == UPNP_IDLE)
356 if (handle->is_mapped && !is_enabled)
357 handle->state = UPNP_UNMAP;
360 if (is_enabled && handle->is_mapped && doPortCheck)
364 GNUNET_snprintf (portStr, sizeof (portStr), "%d", handle->port);
366 handle->processing = GNUNET_YES;
367 UPNP_get_specific_port_mapping_entry_ (handle->control_url,
368 handle->service_type, portStr,
369 "TCP", check_port_mapping_cb,
373 if (handle->state == UPNP_UNMAP)
376 GNUNET_snprintf (portStr, sizeof (portStr), "%d", handle->port);
378 handle->processing = GNUNET_YES;
379 UPNP_delete_port_mapping_ (handle->control_url,
380 handle->service_type, portStr, "TCP", NULL,
381 delete_port_mapping_cb, handle);
384 if (handle->state == UPNP_IDLE)
386 if (is_enabled && !handle->is_mapped)
387 handle->state = UPNP_MAP;
390 if (handle->state == UPNP_MAP)
392 if (!handle->control_url)
393 handle->is_mapped = 0;
398 GNUNET_snprintf (portStr, sizeof (portStr), "%d", handle->port);
399 GNUNET_snprintf (desc, sizeof (desc), "GNUnet at %d", handle->port);
401 handle->processing = GNUNET_YES;
402 UPNP_add_port_mapping_ (handle->control_url,
403 handle->service_type,
404 portStr, portStr, GNUNET_a2s (handle->addr,
406 desc, "TCP", NULL, add_port_mapping_cb,
411 if (handle->state != UPNP_DISCOVER)
413 handle->processing = GNUNET_YES;
414 UPNP_get_external_ip_address_ (handle->control_url,
415 handle->service_type,
416 get_ip_address_cb, handle);