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
34 #include "gnunet_common.h"
41 #include "gnunet_nat_lib.h"
43 #include "upnp-discover.h"
44 #include "upnp-commands.h"
47 /* Component name for logging */
48 #define COMP_NAT_UPNP _("NAT (UPnP)")
59 struct GNUNET_NAT_UPNP_Handle
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 (const struct sockaddr *addr,
97 GNUNET_NAT_UPNP_pulse_cb pulse_cb, void *pulse_cls)
99 struct GNUNET_NAT_UPNP_Handle *handle;
101 handle = GNUNET_malloc (sizeof (struct GNUNET_NAT_UPNP_Handle));
102 handle->processing = GNUNET_NO;
103 handle->state = UPNP_DISCOVER;
105 handle->addrlen = addrlen;
107 handle->pulse_cb = pulse_cb;
108 handle->pulse_cls = pulse_cls;
109 handle->control_url = NULL;
110 handle->service_type = NULL;
112 /* Find the interface corresponding to the address,
113 * on which we should broadcast call for routers */
114 GNUNET_OS_network_interfaces_list (&process_if, handle);
116 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
118 "Could not find an interface matching the wanted address.\n");
124 GNUNET_NAT_UPNP_close (struct GNUNET_NAT_UPNP_Handle *handle)
126 GNUNET_assert (!handle->is_mapped);
127 GNUNET_assert ((handle->state == UPNP_IDLE)
128 || (handle->state == UPNP_ERR)
129 || (handle->state == UPNP_DISCOVER));
131 GNUNET_free_non_null (handle->control_url);
132 GNUNET_free_non_null (handle->service_type);
133 GNUNET_free (handle);
137 pulse_finish (struct GNUNET_NAT_UPNP_Handle *handle)
139 enum GNUNET_NAT_PortState status;
140 handle->processing = GNUNET_NO;
142 switch (handle->state)
145 status = GNUNET_NAT_PORT_UNMAPPED;
149 status = GNUNET_NAT_PORT_MAPPING;
153 status = GNUNET_NAT_PORT_UNMAPPING;
158 handle->is_mapped ? GNUNET_NAT_PORT_MAPPED : GNUNET_NAT_PORT_UNMAPPED;
162 status = GNUNET_NAT_PORT_ERROR;
166 handle->pulse_cb (status, handle->ext_addr, handle->pulse_cls);
170 discover_cb (const char *control_url, const char *service_type, void *cls)
172 struct GNUNET_NAT_UPNP_Handle *handle = cls;
176 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
177 _("Found Internet Gateway Device \"%s\"\n"),
180 GNUNET_free_non_null (handle->control_url);
181 GNUNET_free_non_null (handle->service_type);
183 handle->control_url = GNUNET_strdup (control_url);
184 handle->service_type = GNUNET_strdup (service_type);
185 handle->state = UPNP_IDLE;
186 handle->hasDiscovered = 1;
190 handle->control_url = NULL;
191 handle->service_type = NULL;
192 handle->state = UPNP_ERR;
194 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP,
195 "UPNP device discovery failed\n");
199 pulse_finish (handle);
203 check_port_mapping_cb (int error, const char *control_url,
204 const char *service_type, const char *extPort,
205 const char *inPort, const char *proto,
206 const char *remoteHost, void *cls)
208 struct GNUNET_NAT_UPNP_Handle *handle = cls;
212 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
213 _("Port %d isn't forwarded\n"), handle->port);
214 handle->is_mapped = GNUNET_NO;
217 pulse_finish (handle);
221 delete_port_mapping_cb (int error, const char *control_url,
222 const char *service_type, const char *extPort,
223 const char *inPort, const char *proto,
224 const char *remoteHost, void *cls)
226 struct GNUNET_NAT_UPNP_Handle *handle = cls;
229 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
231 ("Could not stop port forwarding through \"%s\", service \"%s\": error %d\n"),
232 handle->control_url, handle->service_type, error);
235 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
237 ("Stopped port forwarding through \"%s\", service \"%s\"\n"),
238 handle->control_url, handle->service_type);
239 handle->is_mapped = !error;
240 handle->state = UPNP_IDLE;
244 pulse_finish (handle);
248 add_port_mapping_cb (int error, const char *control_url,
249 const char *service_type, const char *extPort,
250 const char *inPort, const char *proto,
251 const char *remoteHost, void *cls)
253 struct GNUNET_NAT_UPNP_Handle *handle = cls;
257 handle->is_mapped = GNUNET_NO;
258 handle->state = UPNP_ERR;
259 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
261 ("Port forwarding through \"%s\", service \"%s\" failed with error %d\n"),
262 handle->control_url, handle->service_type, error);
267 handle->is_mapped = GNUNET_NO;
268 handle->state = UPNP_IDLE;
269 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
270 _("Port %d forwarded successfully\n"), handle->port);
273 pulse_finish (handle);
277 get_ip_address_cb (int error, char *ext_addr, void *cls)
279 struct GNUNET_NAT_UPNP_Handle *handle = cls;
283 if (handle->ext_addr)
285 GNUNET_free (handle->ext_addr);
286 handle->ext_addr = NULL;
289 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP,
290 "UPNP_get_external_ip_address_ failed (error %d)\n",
297 struct in6_addr addr6;
299 if (handle->ext_addr)
301 GNUNET_free (handle->ext_addr);
302 handle->ext_addr = NULL;
305 /* Try IPv4 and IPv6 as we don't know what's the format */
307 if (inet_aton (ext_addr, &addr) != 0)
309 addr.S_un.S_addr = inet_addr(ext_addr);
310 if (addr.S_un.S_addr == INADDR_NONE)
313 handle->ext_addr = GNUNET_malloc (sizeof (struct sockaddr_in));
314 handle->ext_addr->sa_family = AF_INET;
315 ((struct sockaddr_in *) handle->ext_addr)->sin_addr = addr;
317 else if (inet_pton (AF_INET6, ext_addr, &addr6) != 1)
319 handle->ext_addr = GNUNET_malloc (sizeof (struct sockaddr_in6));
320 handle->ext_addr->sa_family = AF_INET6;
321 ((struct sockaddr_in6 *) handle->ext_addr)->sin6_addr = addr6;
324 GNUNET_assert (GNUNET_YES);
327 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP,
328 _("Found public IP address %s\n"), ext_addr);
332 pulse_finish (handle);
336 * Check state of UPnP NAT: port redirection, external IP address.
339 * @param handle the handle for UPnP object
340 * @param is_enabled whether enable port redirection
341 * @param doPortCheck do port check
344 GNUNET_NAT_UPNP_pulse (struct GNUNET_NAT_UPNP_Handle *handle,
345 int is_enabled, int doPortCheck)
347 /* Stop if we're already waiting for an action to complete */
348 if (handle->processing == GNUNET_YES)
351 if (is_enabled && (handle->state == UPNP_DISCOVER))
353 handle->processing = GNUNET_YES;
354 UPNP_discover_ (handle->iface, handle->addr, discover_cb,
358 if (handle->state == UPNP_IDLE)
360 if (handle->is_mapped && !is_enabled)
361 handle->state = UPNP_UNMAP;
364 if (is_enabled && handle->is_mapped && doPortCheck)
368 GNUNET_snprintf (portStr, sizeof (portStr), "%d", handle->port);
370 handle->processing = GNUNET_YES;
371 UPNP_get_specific_port_mapping_entry_ (handle->control_url,
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->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->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->control_url,
419 handle->service_type,
420 get_ip_address_cb, handle);