*
* @author Milan Bouchet-Valat
*/
+#include "platform.h"
+#include "gnunet_common.h"
+
#include <stdlib.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
-#include <miniupnp/miniupnpc.h>
-#include <miniupnp/upnpcommands.h>
-
-#include "platform.h"
-#include "gnunet_common.h"
#include "gnunet_nat_lib.h"
+#include "nat.h"
+#include "upnp-discover.h"
+#include "upnp-commands.h"
#include "upnp.h"
/* Component name for logging */
#define COMP_NAT_UPNP _("NAT (UPnP)")
-typedef enum
+enum UPNP_State
{
UPNP_IDLE,
UPNP_ERR,
UPNP_DISCOVER,
UPNP_MAP,
UPNP_UNMAP
-}
-UPNP_state;
+};
struct GNUNET_NAT_UPNP_Handle
{
int hasDiscovered;
- struct UPNPUrls urls;
- struct IGDdatas data;
+ char *control_url;
+ char *service_type;
int port;
const struct sockaddr *addr;
socklen_t addrlen;
unsigned int is_mapped;
- UPNP_state state;
+ enum UPNP_State state;
struct sockaddr *ext_addr;
- const char *iface;
+ char *iface;
+ int processing;
+ GNUNET_NAT_UPNP_pulse_cb pulse_cb;
+ void *pulse_cls;
};
static int
process_if (void *cls,
const char *name,
- int isDefault,
- const struct sockaddr *addr,
- socklen_t addrlen)
+ int isDefault, const struct sockaddr *addr, socklen_t addrlen)
{
struct GNUNET_NAT_UPNP_Handle *upnp = cls;
-
+ GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "UPNP found if `%s'\n", name);
if (addr && GNUNET_NAT_cmp_addr (upnp->addr, addr) == 0)
{
- upnp->iface = name;
+ upnp->iface = GNUNET_strdup(name); // BADNESS!
return GNUNET_SYSERR;
}
return GNUNET_OK;
}
-GNUNET_NAT_UPNP_Handle *
-GNUNET_NAT_UPNP_init (const struct sockaddr *addr, socklen_t addrlen,
- u_short port)
-{
- GNUNET_NAT_UPNP_Handle *upnp =
- GNUNET_malloc (sizeof (GNUNET_NAT_UPNP_Handle));
- upnp->state = UPNP_DISCOVER;
- upnp->addr = addr;
- upnp->addrlen = addrlen;
- upnp->port = port;
+struct GNUNET_NAT_UPNP_Handle *
+GNUNET_NAT_UPNP_init (const struct sockaddr *addr,
+ socklen_t addrlen,
+ u_short port,
+ GNUNET_NAT_UPNP_pulse_cb pulse_cb, void *pulse_cls)
+{
+ struct GNUNET_NAT_UPNP_Handle *handle;
+
+ handle = GNUNET_malloc (sizeof (struct GNUNET_NAT_UPNP_Handle));
+ handle->processing = GNUNET_NO;
+ handle->state = UPNP_DISCOVER;
+ handle->addr = addr;
+ handle->addrlen = addrlen;
+ handle->port = port;
+ handle->pulse_cb = pulse_cb;
+ handle->pulse_cls = pulse_cls;
+ handle->control_url = NULL;
+ handle->service_type = NULL;
/* Find the interface corresponding to the address,
* on which we should broadcast call for routers */
- upnp->iface = NULL;
- GNUNET_OS_network_interfaces_list (process_if, upnp);
- if (!upnp->iface)
- GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, COMP_NAT_UPNP, "Could not find an interface matching the wanted address.\n");
-
- return upnp;
+ GNUNET_OS_network_interfaces_list (&process_if, handle);
+ if (!handle->iface)
+ GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
+ COMP_NAT_UPNP,
+ "Could not find an interface matching the wanted address.\n");
+ return handle;
}
+
void
-GNUNET_NAT_UPNP_close (GNUNET_NAT_UPNP_Handle * handle)
+GNUNET_NAT_UPNP_close (struct GNUNET_NAT_UPNP_Handle *handle)
{
GNUNET_assert (!handle->is_mapped);
GNUNET_assert ((handle->state == UPNP_IDLE)
- || (handle->state == UPNP_ERR) || (handle->state == UPNP_DISCOVER));
+ || (handle->state == UPNP_ERR)
+ || (handle->state == UPNP_DISCOVER));
- if (handle->hasDiscovered)
- FreeUPNPUrls (&handle->urls);
+ GNUNET_free_non_null (handle->control_url);
+ GNUNET_free_non_null (handle->service_type);
GNUNET_free (handle);
}
+static void
+pulse_finish (struct GNUNET_NAT_UPNP_Handle *handle)
+{
+ enum GNUNET_NAT_PortState status;
+ handle->processing = GNUNET_NO;
+
+ switch (handle->state)
+ {
+ case UPNP_DISCOVER:
+ status = GNUNET_NAT_PORT_UNMAPPED;
+ break;
+
+ case UPNP_MAP:
+ status = GNUNET_NAT_PORT_MAPPING;
+ break;
+
+ case UPNP_UNMAP:
+ status = GNUNET_NAT_PORT_UNMAPPING;
+ break;
+
+ case UPNP_IDLE:
+ status =
+ handle->is_mapped ? GNUNET_NAT_PORT_MAPPED : GNUNET_NAT_PORT_UNMAPPED;
+ break;
+
+ default:
+ status = GNUNET_NAT_PORT_ERROR;
+ break;
+ }
+
+ handle->pulse_cb (status, handle->ext_addr, handle->pulse_cls);
+}
+
+static void
+discover_cb (const char *control_url, const char *service_type, void *cls)
+{
+ struct GNUNET_NAT_UPNP_Handle *handle = cls;
+
+ if (control_url)
+ {
+ GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
+ _("Found Internet Gateway Device \"%s\"\n"),
+ control_url);
+
+ GNUNET_free_non_null (handle->control_url);
+ GNUNET_free_non_null (handle->service_type);
+
+ handle->control_url = GNUNET_strdup (control_url);
+ handle->service_type = GNUNET_strdup (service_type);
+ handle->state = UPNP_IDLE;
+ handle->hasDiscovered = 1;
+ }
+ else
+ {
+ handle->control_url = NULL;
+ handle->service_type = NULL;
+ handle->state = UPNP_ERR;
+#ifdef DEBUG_UPNP
+ GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP,
+ "UPNP device discovery failed\n");
+#endif
+ }
+
+ pulse_finish (handle);
+}
+
+static void
+check_port_mapping_cb (int error, const char *control_url,
+ const char *service_type, const char *extPort,
+ const char *inPort, const char *proto,
+ const char *remoteHost, void *cls)
+{
+ struct GNUNET_NAT_UPNP_Handle *handle = cls;
+
+ if (error)
+ {
+ GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
+ _("Port %d isn't forwarded\n"), handle->port);
+ handle->is_mapped = GNUNET_NO;
+ }
+
+ pulse_finish (handle);
+}
+
+static void
+delete_port_mapping_cb (int error, const char *control_url,
+ const char *service_type, const char *extPort,
+ const char *inPort, const char *proto,
+ const char *remoteHost, void *cls)
+{
+ struct GNUNET_NAT_UPNP_Handle *handle = cls;
+
+ if (error)
+ GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
+ _
+ ("Could not stop port forwarding through \"%s\", service \"%s\": error %d\n"),
+ handle->control_url, handle->service_type, error);
+ else
+ {
+ GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
+ _
+ ("Stopped port forwarding through \"%s\", service \"%s\"\n"),
+ handle->control_url, handle->service_type);
+ handle->is_mapped = !error;
+ handle->state = UPNP_IDLE;
+ handle->port = -1;
+ }
+
+ pulse_finish (handle);
+}
+
+static void
+add_port_mapping_cb (int error, const char *control_url,
+ const char *service_type, const char *extPort,
+ const char *inPort, const char *proto,
+ const char *remoteHost, void *cls)
+{
+ struct GNUNET_NAT_UPNP_Handle *handle = cls;
+
+ if (error)
+ {
+ handle->is_mapped = GNUNET_NO;
+ handle->state = UPNP_ERR;
+ GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
+ _
+ ("Port forwarding through \"%s\", service \"%s\" failed with error %d\n"),
+ handle->control_url, handle->service_type, error);
+ return;
+ }
+ else
+ {
+ handle->is_mapped = GNUNET_NO;
+ handle->state = UPNP_IDLE;
+ GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
+ _("Port %d forwarded successfully\n"), handle->port);
+ }
+
+ pulse_finish (handle);
+}
+
+static void
+get_ip_address_cb (int error, char *ext_addr, void *cls)
+{
+ struct GNUNET_NAT_UPNP_Handle *handle = cls;
+
+ if (error)
+ {
+ if (handle->ext_addr)
+ {
+ GNUNET_free (handle->ext_addr);
+ handle->ext_addr = NULL;
+ }
+#ifdef DEBUG_UPNP
+ GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP,
+ "UPNP_get_external_ip_address_ failed (error %d)\n",
+ error);
+#endif
+ }
+ else
+ {
+ struct in_addr addr;
+ struct in6_addr addr6;
+
+ if (handle->ext_addr)
+ {
+ GNUNET_free (handle->ext_addr);
+ handle->ext_addr = NULL;
+ }
+
+ /* Try IPv4 and IPv6 as we don't know what's the format */
+#ifndef MINGW
+ if (inet_aton (ext_addr, &addr) != 0)
+#else
+ addr.S_un.S_addr = inet_addr(ext_addr);
+ if (addr.S_un.S_addr == INADDR_NONE)
+#endif
+ {
+ handle->ext_addr = GNUNET_malloc (sizeof (struct sockaddr_in));
+ handle->ext_addr->sa_family = AF_INET;
+ ((struct sockaddr_in *) handle->ext_addr)->sin_addr = addr;
+ }
+ else if (inet_pton (AF_INET6, ext_addr, &addr6) != 1)
+ {
+ handle->ext_addr = GNUNET_malloc (sizeof (struct sockaddr_in6));
+ handle->ext_addr->sa_family = AF_INET6;
+ ((struct sockaddr_in6 *) handle->ext_addr)->sin6_addr = addr6;
+ }
+ else
+ GNUNET_assert (GNUNET_YES);
+
+#ifdef DEBUG_UPNP
+ GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP,
+ _("Found public IP address %s\n"), ext_addr);
+#endif
+ }
+
+ pulse_finish (handle);
+}
+
/**
* Check state of UPnP NAT: port redirection, external IP address.
*
*
* @param handle the handle for UPnP object
* @param is_enabled whether enable port redirection
- * @param doPortCheck FIXME
- * @param ext_addr pointer for returning external IP address.
- * Will be set to NULL if address could not be found. Don't free the sockaddr.
+ * @param doPortCheck do port check
*/
-int
-GNUNET_NAT_UPNP_pulse (GNUNET_NAT_UPNP_Handle * handle, int is_enabled,
- int doPortCheck, struct sockaddr **ext_addr)
+void
+GNUNET_NAT_UPNP_pulse (struct GNUNET_NAT_UPNP_Handle *handle,
+ int is_enabled, int doPortCheck)
{
- int ret;
+ /* Stop if we're already waiting for an action to complete */
+ if (handle->processing == GNUNET_YES)
+ return;
if (is_enabled && (handle->state == UPNP_DISCOVER))
{
- struct UPNPDev *devlist;
- errno = 0;
- devlist = upnpDiscover (2000, handle->iface, handle->addr, NULL, 0);
- if (devlist == NULL)
- {
-#ifdef DEBUG
- GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP,
- "upnpDiscover failed (errno %d - %s)\n", errno,
- strerror (errno));
-#endif
- }
- errno = 0;
- if (UPNP_GetValidIGD (devlist, &handle->urls, &handle->data,
- NULL, 0))
- {
- GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
- _("Found Internet Gateway Device \"%s\"\n"),
- handle->urls.controlURL);
- handle->state = UPNP_IDLE;
- handle->hasDiscovered = 1;
- }
- else
- {
- handle->state = UPNP_ERR;
-#ifdef DEBUG
- GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP,
- "UPNP_GetValidIGD failed (errno %d - %s)\n",
- errno, strerror (errno));
-#endif
- }
- freeUPNPDevlist (devlist);
+ handle->processing = GNUNET_YES;
+ UPNP_discover_ (handle->iface, handle->addr, discover_cb,
+ handle);
}
if (handle->state == UPNP_IDLE)
if (is_enabled && handle->is_mapped && doPortCheck)
{
char portStr[8];
- char intPort[8];
- char intClient[128];
- int i;
GNUNET_snprintf (portStr, sizeof (portStr), "%d", handle->port);
- i = UPNP_GetSpecificPortMappingEntry (handle->urls.controlURL,
- handle->data.servicetype, portStr,
- "TCP", intClient, intPort);
- if (i != UPNPCOMMAND_SUCCESS)
- {
- GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
- _("Port %d isn't forwarded\n"), handle->port);
- handle->is_mapped = GNUNET_NO;
- }
+
+ handle->processing = GNUNET_YES;
+ UPNP_get_specific_port_mapping_entry_ (handle->control_url,
+ handle->service_type, portStr,
+ "TCP", check_port_mapping_cb,
+ handle);
}
if (handle->state == UPNP_UNMAP)
{
char portStr[16];
GNUNET_snprintf (portStr, sizeof (portStr), "%d", handle->port);
- UPNP_DeletePortMapping (handle->urls.controlURL,
- handle->data.servicetype, portStr, "TCP", NULL);
- GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
- _
- ("Stopping port forwarding through \"%s\", service \"%s\"\n"),
- handle->urls.controlURL, handle->data.servicetype);
- handle->is_mapped = 0;
- handle->state = UPNP_IDLE;
- handle->port = -1;
+
+ handle->processing = GNUNET_YES;
+ UPNP_delete_port_mapping_ (handle->control_url,
+ handle->service_type, portStr, "TCP", NULL,
+ delete_port_mapping_cb, handle);
}
if (handle->state == UPNP_IDLE)
if (handle->state == UPNP_MAP)
{
- int err = -1;
- errno = 0;
-
- if (!handle->urls.controlURL)
+ if (!handle->control_url)
handle->is_mapped = 0;
else
{
char desc[64];
GNUNET_snprintf (portStr, sizeof (portStr), "%d", handle->port);
GNUNET_snprintf (desc, sizeof (desc), "GNUnet at %d", handle->port);
- err = UPNP_AddPortMapping (handle->urls.controlURL,
- handle->data.servicetype,
- portStr, portStr, GNUNET_a2s (handle->addr, handle->addrlen),
- desc, "TCP", NULL);
- handle->is_mapped = !err;
- }
- GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
- _
- ("Port forwarding through \"%s\", service \"%s\"\n"),
- handle->urls.controlURL, handle->data.servicetype);
- if (handle->is_mapped)
- {
- GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
- _("Port %d forwarded successfully\n"), handle->port);
- handle->state = UPNP_IDLE;
- }
- else
- {
- GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
- "Port forwarding failed with error %d (errno %d - %s)\n",
- err, errno, strerror (errno));
- handle->state = UPNP_ERR;
- }
- }
-
- if (ext_addr && handle->state != UPNP_DISCOVER)
- {
- int err;
- char addr_str[128];
- struct in_addr addr;
- struct in6_addr addr6;
- /* Keep to NULL if address could not be found */
- *ext_addr = NULL;
- err = UPNP_GetExternalIPAddress (handle->urls.controlURL,
- handle->data.servicetype, addr_str);
- if (err == 0)
- {
- if (handle->ext_addr)
- {
- GNUNET_free (handle->ext_addr);
- handle->ext_addr = NULL;
- }
-
- /* Try IPv4 and IPv6 as we don't know what's the format */
- if (inet_aton (addr_str, &addr) != 0)
- {
- handle->ext_addr = GNUNET_malloc (sizeof (struct sockaddr_in));
- handle->ext_addr->sa_family = AF_INET;
- ((struct sockaddr_in *) handle->ext_addr)->sin_addr = addr;
- *ext_addr = handle->ext_addr;
- }
- else if (inet_pton (AF_INET6, addr_str, &addr6) != 1)
- {
- handle->ext_addr = GNUNET_malloc (sizeof (struct sockaddr_in6));
- handle->ext_addr->sa_family = AF_INET6;
- ((struct sockaddr_in6 *) handle->ext_addr)->sin6_addr = addr6;
- *ext_addr = handle->ext_addr;
- }
- else
- GNUNET_assert (GNUNET_YES);
-#ifdef DEBUG
- GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP,
- _("Found public IP address %s\n"),
- addr_str);
-#endif
- }
- else
- {
- *ext_addr = NULL;
- if (handle->ext_addr)
- {
- GNUNET_free (handle->ext_addr);
- handle->ext_addr = NULL;
- }
-#ifdef DEBUG
- GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP,
- "UPNP_GetExternalIPAddress failed (error %d)\n", err);
-#endif
+ handle->processing = GNUNET_YES;
+ UPNP_add_port_mapping_ (handle->control_url,
+ handle->service_type,
+ portStr, portStr, GNUNET_a2s (handle->addr,
+ handle->addrlen),
+ desc, "TCP", NULL, add_port_mapping_cb,
+ handle);
}
}
- switch (handle->state)
+ if (handle->state != UPNP_DISCOVER)
{
- case UPNP_DISCOVER:
- ret = GNUNET_NAT_PORT_UNMAPPED;
- break;
-
- case UPNP_MAP:
- ret = GNUNET_NAT_PORT_MAPPING;
- break;
-
- case UPNP_UNMAP:
- ret = GNUNET_NAT_PORT_UNMAPPING;
- break;
-
- case UPNP_IDLE:
- ret =
- handle->is_mapped ? GNUNET_NAT_PORT_MAPPED : GNUNET_NAT_PORT_UNMAPPED;
- break;
-
- default:
- ret = GNUNET_NAT_PORT_ERROR;
- break;
+ handle->processing = GNUNET_YES;
+ UPNP_get_external_ip_address_ (handle->control_url,
+ handle->service_type,
+ get_ip_address_cb, handle);
}
-
- return ret;
}