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
60 struct GNUNET_SCHEDULER_Handle *sched;
65 const struct sockaddr *addr;
67 unsigned int is_mapped;
68 enum UPNP_State state;
69 struct sockaddr *ext_addr;
72 GNUNET_NAT_UPNP_pulse_cb pulse_cb;
77 process_if (void *cls,
79 int isDefault, const struct sockaddr *addr, socklen_t addrlen)
81 struct GNUNET_NAT_UPNP_Handle *upnp = cls;
83 if (addr && GNUNET_NAT_cmp_addr (upnp->addr, addr) == 0)
85 upnp->iface = name; // BADNESS!
93 struct GNUNET_NAT_UPNP_Handle *
94 GNUNET_NAT_UPNP_init (struct GNUNET_SCHEDULER_Handle *sched,
95 const struct sockaddr *addr,
98 GNUNET_NAT_UPNP_pulse_cb pulse_cb, void *pulse_cls)
100 struct GNUNET_NAT_UPNP_Handle *handle;
102 handle = GNUNET_malloc (sizeof (struct GNUNET_NAT_UPNP_Handle));
103 handle->sched = sched;
104 handle->processing = GNUNET_NO;
105 handle->state = UPNP_DISCOVER;
107 handle->addrlen = addrlen;
109 handle->pulse_cb = pulse_cb;
110 handle->pulse_cls = pulse_cls;
111 handle->control_url = NULL;
112 handle->service_type = NULL;
114 /* Find the interface corresponding to the address,
115 * on which we should broadcast call for routers */
116 GNUNET_OS_network_interfaces_list (&process_if, handle);
118 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
120 "Could not find an interface matching the wanted address.\n");
126 GNUNET_NAT_UPNP_close (struct GNUNET_NAT_UPNP_Handle *handle)
128 GNUNET_assert (!handle->is_mapped);
129 GNUNET_assert ((handle->state == UPNP_IDLE)
130 || (handle->state == UPNP_ERR)
131 || (handle->state == UPNP_DISCOVER));
133 GNUNET_free_non_null (handle->control_url);
134 GNUNET_free_non_null (handle->service_type);
135 GNUNET_free (handle);
139 pulse_finish (struct GNUNET_NAT_UPNP_Handle *handle)
141 enum GNUNET_NAT_PortState status;
142 handle->processing = GNUNET_NO;
144 switch (handle->state)
147 status = GNUNET_NAT_PORT_UNMAPPED;
151 status = GNUNET_NAT_PORT_MAPPING;
155 status = GNUNET_NAT_PORT_UNMAPPING;
160 handle->is_mapped ? GNUNET_NAT_PORT_MAPPED : GNUNET_NAT_PORT_UNMAPPED;
164 status = GNUNET_NAT_PORT_ERROR;
168 handle->pulse_cb (status, handle->ext_addr, handle->pulse_cls);
172 discover_cb (const char *control_url, const char *service_type, void *cls)
174 struct GNUNET_NAT_UPNP_Handle *handle = cls;
178 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
179 _("Found Internet Gateway Device \"%s\"\n"),
182 GNUNET_free_non_null (handle->control_url);
183 GNUNET_free_non_null (handle->service_type);
185 handle->control_url = GNUNET_strdup (control_url);
186 handle->service_type = GNUNET_strdup (service_type);
187 handle->state = UPNP_IDLE;
188 handle->hasDiscovered = 1;
192 handle->control_url = NULL;
193 handle->service_type = NULL;
194 handle->state = UPNP_ERR;
196 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP,
197 "UPNP device discovery failed\n");
201 pulse_finish (handle);
205 check_port_mapping_cb (int error, const char *control_url,
206 const char *service_type, const char *extPort,
207 const char *inPort, const char *proto,
208 const char *remoteHost, void *cls)
210 struct GNUNET_NAT_UPNP_Handle *handle = cls;
214 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
215 _("Port %d isn't forwarded\n"), handle->port);
216 handle->is_mapped = GNUNET_NO;
219 pulse_finish (handle);
223 delete_port_mapping_cb (int error, const char *control_url,
224 const char *service_type, const char *extPort,
225 const char *inPort, const char *proto,
226 const char *remoteHost, void *cls)
228 struct GNUNET_NAT_UPNP_Handle *handle = cls;
231 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
233 ("Could not stop port forwarding through \"%s\", service \"%s\": error %d\n"),
234 handle->control_url, handle->service_type, error);
237 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
239 ("Stopped port forwarding through \"%s\", service \"%s\"\n"),
240 handle->control_url, handle->service_type);
241 handle->is_mapped = !error;
242 handle->state = UPNP_IDLE;
246 pulse_finish (handle);
250 add_port_mapping_cb (int error, const char *control_url,
251 const char *service_type, const char *extPort,
252 const char *inPort, const char *proto,
253 const char *remoteHost, void *cls)
255 struct GNUNET_NAT_UPNP_Handle *handle = cls;
259 handle->is_mapped = GNUNET_NO;
260 handle->state = UPNP_ERR;
261 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
263 ("Port forwarding through \"%s\", service \"%s\" failed with error %d\n"),
264 handle->control_url, handle->service_type, error);
269 handle->is_mapped = GNUNET_NO;
270 handle->state = UPNP_IDLE;
271 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
272 _("Port %d forwarded successfully\n"), handle->port);
275 pulse_finish (handle);
279 get_ip_address_cb (int error, char *ext_addr, void *cls)
281 struct GNUNET_NAT_UPNP_Handle *handle = cls;
285 if (handle->ext_addr)
287 GNUNET_free (handle->ext_addr);
288 handle->ext_addr = NULL;
291 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP,
292 "UPNP_get_external_ip_address_ failed (error %d)\n",
299 struct in6_addr addr6;
301 if (handle->ext_addr)
303 GNUNET_free (handle->ext_addr);
304 handle->ext_addr = NULL;
307 /* Try IPv4 and IPv6 as we don't know what's the format */
308 if (inet_aton (ext_addr, &addr) != 0)
310 handle->ext_addr = GNUNET_malloc (sizeof (struct sockaddr_in));
311 handle->ext_addr->sa_family = AF_INET;
312 ((struct sockaddr_in *) handle->ext_addr)->sin_addr = addr;
314 else if (inet_pton (AF_INET6, ext_addr, &addr6) != 1)
316 handle->ext_addr = GNUNET_malloc (sizeof (struct sockaddr_in6));
317 handle->ext_addr->sa_family = AF_INET6;
318 ((struct sockaddr_in6 *) handle->ext_addr)->sin6_addr = addr6;
321 GNUNET_assert (GNUNET_YES);
324 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP,
325 _("Found public IP address %s\n"), ext_addr);
329 pulse_finish (handle);
333 * Check state of UPnP NAT: port redirection, external IP address.
336 * @param handle the handle for UPnP object
337 * @param is_enabled whether enable port redirection
338 * @param doPortCheck FIXME
339 * @param ext_addr pointer for returning external IP address.
340 * Will be set to NULL if address could not be found. Don't free the sockaddr.
343 GNUNET_NAT_UPNP_pulse (struct GNUNET_NAT_UPNP_Handle *handle,
344 int is_enabled, int doPortCheck)
346 /* Stop if we're already waiting for an action to complete */
347 if (handle->processing == GNUNET_YES)
350 if (is_enabled && (handle->state == UPNP_DISCOVER))
352 handle->processing = GNUNET_YES;
353 UPNP_discover_ (handle->sched, handle->iface, handle->addr, discover_cb,
357 if (handle->state == UPNP_IDLE)
359 if (handle->is_mapped && !is_enabled)
360 handle->state = UPNP_UNMAP;
363 if (is_enabled && handle->is_mapped && doPortCheck)
367 GNUNET_snprintf (portStr, sizeof (portStr), "%d", handle->port);
369 handle->processing = GNUNET_YES;
370 UPNP_get_specific_port_mapping_entry_ (handle->sched,
372 handle->service_type, portStr,
373 "TCP", check_port_mapping_cb,
377 if (handle->state == UPNP_UNMAP)
380 GNUNET_snprintf (portStr, sizeof (portStr), "%d", handle->port);
382 handle->processing = GNUNET_YES;
383 UPNP_delete_port_mapping_ (handle->sched, handle->control_url,
384 handle->service_type, portStr, "TCP", NULL,
385 delete_port_mapping_cb, handle);
388 if (handle->state == UPNP_IDLE)
390 if (is_enabled && !handle->is_mapped)
391 handle->state = UPNP_MAP;
394 if (handle->state == UPNP_MAP)
396 if (!handle->control_url)
397 handle->is_mapped = 0;
402 GNUNET_snprintf (portStr, sizeof (portStr), "%d", handle->port);
403 GNUNET_snprintf (desc, sizeof (desc), "GNUnet at %d", handle->port);
405 handle->processing = GNUNET_YES;
406 UPNP_add_port_mapping_ (handle->sched, handle->control_url,
407 handle->service_type,
408 portStr, portStr, GNUNET_a2s (handle->addr,
410 desc, "TCP", NULL, add_port_mapping_cb,
415 if (handle->state != UPNP_DISCOVER)
417 handle->processing = GNUNET_YES;
418 UPNP_get_external_ip_address_ (handle->sched, handle->control_url,
419 handle->service_type,
420 get_ip_address_cb, handle);