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
38 #include <miniupnp/miniupnpc.h>
39 #include <miniupnp/upnpcommands.h>
42 #include "gnunet_common.h"
43 #include "gnunet_nat_lib.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;
73 process_if (void *cls,
76 const struct sockaddr *addr,
79 struct GNUNET_NAT_UPNP_Handle *upnp = cls;
81 if (addr && GNUNET_NAT_cmp_addr (upnp->addr, addr) == 0)
83 upnp->iface = name; // BADNESS!
91 GNUNET_NAT_UPNP_Handle *
92 GNUNET_NAT_UPNP_init (const struct sockaddr *addr,
96 GNUNET_NAT_UPNP_Handle *upnp;
98 upnp = GNUNET_malloc (sizeof (GNUNET_NAT_UPNP_Handle));
99 upnp->state = UPNP_DISCOVER;
101 upnp->addrlen = addrlen;
103 /* Find the interface corresponding to the address,
104 * on which we should broadcast call for routers */
105 GNUNET_OS_network_interfaces_list (&process_if, upnp);
107 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
109 "Could not find an interface matching the wanted address.\n");
115 GNUNET_NAT_UPNP_close (GNUNET_NAT_UPNP_Handle * handle)
117 GNUNET_assert (!handle->is_mapped);
118 GNUNET_assert ((handle->state == UPNP_IDLE)
119 || (handle->state == UPNP_ERR) || (handle->state == UPNP_DISCOVER));
121 if (handle->hasDiscovered)
122 FreeUPNPUrls (&handle->urls);
123 GNUNET_free (handle);
127 * Check state of UPnP NAT: port redirection, external IP address.
130 * @param handle the handle for UPnP object
131 * @param is_enabled whether enable port redirection
132 * @param doPortCheck FIXME
133 * @param ext_addr pointer for returning external IP address.
134 * Will be set to NULL if address could not be found. Don't free the sockaddr.
137 GNUNET_NAT_UPNP_pulse (GNUNET_NAT_UPNP_Handle * handle, int is_enabled,
138 int doPortCheck, struct sockaddr **ext_addr)
142 if (is_enabled && (handle->state == UPNP_DISCOVER))
144 struct UPNPDev *devlist;
146 devlist = upnpDiscover (2000, handle->iface, handle->addr, NULL, 0);
150 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP,
151 "upnpDiscover failed (errno %d - %s)\n", errno,
156 if (UPNP_GetValidIGD (devlist, &handle->urls, &handle->data,
159 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
160 _("Found Internet Gateway Device \"%s\"\n"),
161 handle->urls.controlURL);
162 handle->state = UPNP_IDLE;
163 handle->hasDiscovered = 1;
167 handle->state = UPNP_ERR;
169 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP,
170 "UPNP_GetValidIGD failed (errno %d - %s)\n",
171 errno, strerror (errno));
174 freeUPNPDevlist (devlist);
177 if (handle->state == UPNP_IDLE)
179 if (handle->is_mapped && !is_enabled)
180 handle->state = UPNP_UNMAP;
183 if (is_enabled && handle->is_mapped && doPortCheck)
190 GNUNET_snprintf (portStr, sizeof (portStr), "%d", handle->port);
191 i = UPNP_GetSpecificPortMappingEntry (handle->urls.controlURL,
192 handle->data.servicetype, portStr,
193 "TCP", intClient, intPort);
194 if (i != UPNPCOMMAND_SUCCESS)
196 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
197 _("Port %d isn't forwarded\n"), handle->port);
198 handle->is_mapped = GNUNET_NO;
202 if (handle->state == UPNP_UNMAP)
205 GNUNET_snprintf (portStr, sizeof (portStr), "%d", handle->port);
206 UPNP_DeletePortMapping (handle->urls.controlURL,
207 handle->data.servicetype, portStr, "TCP", NULL);
208 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
210 ("Stopping port forwarding through \"%s\", service \"%s\"\n"),
211 handle->urls.controlURL, handle->data.servicetype);
212 handle->is_mapped = 0;
213 handle->state = UPNP_IDLE;
217 if (handle->state == UPNP_IDLE)
219 if (is_enabled && !handle->is_mapped)
220 handle->state = UPNP_MAP;
223 if (handle->state == UPNP_MAP)
228 if (!handle->urls.controlURL)
229 handle->is_mapped = 0;
234 GNUNET_snprintf (portStr, sizeof (portStr), "%d", handle->port);
235 GNUNET_snprintf (desc, sizeof (desc), "GNUnet at %d", handle->port);
236 err = UPNP_AddPortMapping (handle->urls.controlURL,
237 handle->data.servicetype,
238 portStr, portStr, GNUNET_a2s (handle->addr, handle->addrlen),
240 handle->is_mapped = !err;
242 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
244 ("Port forwarding through \"%s\", service \"%s\"\n"),
245 handle->urls.controlURL, handle->data.servicetype);
246 if (handle->is_mapped)
248 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
249 _("Port %d forwarded successfully\n"), handle->port);
250 handle->state = UPNP_IDLE;
254 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
255 "Port forwarding failed with error %d (errno %d - %s)\n",
256 err, errno, strerror (errno));
257 handle->state = UPNP_ERR;
261 if (ext_addr && handle->state != UPNP_DISCOVER)
266 struct in6_addr addr6;
268 /* Keep to NULL if address could not be found */
270 err = UPNP_GetExternalIPAddress (handle->urls.controlURL,
271 handle->data.servicetype, addr_str);
274 if (handle->ext_addr)
276 GNUNET_free (handle->ext_addr);
277 handle->ext_addr = NULL;
280 /* Try IPv4 and IPv6 as we don't know what's the format */
281 if (inet_aton (addr_str, &addr) != 0)
283 handle->ext_addr = GNUNET_malloc (sizeof (struct sockaddr_in));
284 handle->ext_addr->sa_family = AF_INET;
285 ((struct sockaddr_in *) handle->ext_addr)->sin_addr = addr;
286 *ext_addr = handle->ext_addr;
288 else if (inet_pton (AF_INET6, addr_str, &addr6) != 1)
290 handle->ext_addr = GNUNET_malloc (sizeof (struct sockaddr_in6));
291 handle->ext_addr->sa_family = AF_INET6;
292 ((struct sockaddr_in6 *) handle->ext_addr)->sin6_addr = addr6;
293 *ext_addr = handle->ext_addr;
296 GNUNET_assert (GNUNET_YES);
298 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP,
299 _("Found public IP address %s\n"),
306 if (handle->ext_addr)
308 GNUNET_free (handle->ext_addr);
309 handle->ext_addr = NULL;
312 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP,
313 "UPNP_GetExternalIPAddress failed (error %d)\n", err);
318 switch (handle->state)
321 ret = GNUNET_NAT_PORT_UNMAPPED;
325 ret = GNUNET_NAT_PORT_MAPPING;
329 ret = GNUNET_NAT_PORT_UNMAPPING;
334 handle->is_mapped ? GNUNET_NAT_PORT_MAPPED : GNUNET_NAT_PORT_UNMAPPED;
338 ret = GNUNET_NAT_PORT_ERROR;