-SUBDIRS = .
-
INCLUDES = -I$(top_srcdir)/src/include
if MINGW
libgnunetnat_la_SOURCES = \
upnp.c upnp.h \
- natpmp.c natpmp.h \
+ upnp-commands.c upnp-commands.h \
+ upnp-discover.c upnp-discover.h \
+ upnp-igd-parse.c upnp-igd-parse.h \
+ upnp-minixml.c upnp-minixml.h \
+ upnp-reply-parse.c upnp-reply-parse.h bsdqueue.h \
nat.c
libgnunetnat_la_CFLAGS = \
- -I$(top_scrdir)/include
+ -I$(top_scrdir)/include \
+ -DDEBUG_UPNP -g -O0
libgnunetnat_la_LIBADD = \
$(top_builddir)/src/util/libgnunetutil.la \
test_nat_LDADD = \
$(top_builddir)/src/nat/libgnunetnat.la \
- $(top_builddir)/src/util/libgnunetutil.la
-
+ $(top_builddir)/src/util/libgnunetutil.la \
+ @LIBCURL@
endif
--- /dev/null
+/*
+ This file is part of GNUnet.
+ (C) 2009, 2010 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file nat/bsd-queue.h
+ * @brief BSD implementation of simple lists
+ *
+ * @author Milan Bouchet-Valat
+ */
+
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef BSD_QUEUE_H
+#define BSD_QUEUE_H
+
+/*
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ */
+
+#ifdef QUEUE_MACRO_DEBUG
+#define _Q_INVALIDATE(a) (a) = ((void *)-1)
+#else
+#define _Q_INVALIDATE(a)
+#endif
+
+/*
+ * List definitions.
+ */
+#define LIST_HEAD(name, type) \
+struct name { \
+ struct type *lh_first; /* first element */ \
+}
+
+#define LIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define LIST_ENTRY(type) \
+struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+}
+
+/*
+ * List access methods
+ */
+#define LIST_FIRST(head) ((head)->lh_first)
+#define LIST_END(head) NULL
+#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
+#define LIST_NEXT(elm, field) ((elm)->field.le_next)
+
+#define LIST_FOREACH(var, head, field) \
+ for((var) = LIST_FIRST(head); \
+ (var)!= LIST_END(head); \
+ (var) = LIST_NEXT(var, field))
+
+/*
+ * List functions.
+ */
+#define LIST_INIT(head) do { \
+ LIST_FIRST(head) = LIST_END(head); \
+} while (0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do { \
+ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
+ (listelm)->field.le_next->field.le_prev = \
+ &(elm)->field.le_next; \
+ (listelm)->field.le_next = (elm); \
+ (elm)->field.le_prev = &(listelm)->field.le_next; \
+} while (0)
+
+#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ (elm)->field.le_next = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &(elm)->field.le_next; \
+} while (0)
+
+#define LIST_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.le_next = (head)->lh_first) != NULL) \
+ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+ (head)->lh_first = (elm); \
+ (elm)->field.le_prev = &(head)->lh_first; \
+} while (0)
+
+#define LIST_REMOVE(elm, field) do { \
+ if ((elm)->field.le_next != NULL) \
+ (elm)->field.le_next->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = (elm)->field.le_next; \
+ _Q_INVALIDATE((elm)->field.le_prev); \
+ _Q_INVALIDATE((elm)->field.le_next); \
+} while (0)
+
+#define LIST_REPLACE(elm, elm2, field) do { \
+ if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
+ (elm2)->field.le_next->field.le_prev = \
+ &(elm2)->field.le_next; \
+ (elm2)->field.le_prev = (elm)->field.le_prev; \
+ *(elm2)->field.le_prev = (elm2); \
+ _Q_INVALIDATE((elm)->field.le_prev); \
+ _Q_INVALIDATE((elm)->field.le_next); \
+} while (0)
+
+#endif
+
+/* end of bsd-queue.h */
/**
* Handle for UPnP operations.
*/
- GNUNET_NAT_UPNP_Handle *upnp;
+ struct GNUNET_NAT_UPNP_Handle *upnp;
/**
* Handle for NAT PMP operations.
*/
- GNUNET_NAT_NATPMP_Handle *natpmp;
+ struct GNUNET_NAT_NATPMP_Handle *natpmp;
/**
* Scheduler.
/**
* LAN address as passed by the caller
*/
- struct sockaddr *local_addr;
+ struct sockaddr *local_addr;
/**
- * External address as reported by NAT box
+ * External address as reported by found NAT box
*/
- struct sockaddr *ext_addr;
+ struct sockaddr *ext_addr;
+
+ /**
+ * External address as reported by each type of NAT box
+ */
+ struct sockaddr *ext_addr_upnp;
+ struct sockaddr *ext_addr_natpmp;
/**
* External address and port where packets are redirected
*/
- struct sockaddr *contact_addr;
+ struct sockaddr *contact_addr;
GNUNET_NAT_AddressCallback callback;
int port_mapped;
+ int old_status;
+
+ int new_status;
+
int did_warn;
+ int processing;
+
uint16_t public_port;
};
static int
-get_traversal_status (const struct GNUNET_NAT_Handle * s)
+get_traversal_status (const struct GNUNET_NAT_Handle *h)
{
- return MAX (s->natpmp_status, s->upnp_status);
+ return MAX (h->natpmp_status, h->upnp_status);
}
* @param b second sockaddr
* @return 0 if addresses are equal, non-null value otherwise */
int
-GNUNET_NAT_cmp_addr (const struct sockaddr *a,
- const struct sockaddr *b)
+GNUNET_NAT_cmp_addr (const struct sockaddr *a, const struct sockaddr *b)
{
if (!(a && b))
return -1;
- if ( (a->sa_family == AF_INET) && (b->sa_family == AF_INET) )
+ if ((a->sa_family == AF_INET) && (b->sa_family == AF_INET))
return memcmp (&(((struct sockaddr_in *) a)->sin_addr),
&(((struct sockaddr_in *) b)->sin_addr),
sizeof (struct in_addr));
- if ( (a->sa_family == AF_INET6) && (b->sa_family == AF_INET6) )
+ if ((a->sa_family == AF_INET6) && (b->sa_family == AF_INET6))
return memcmp (&(((struct sockaddr_in6 *) a)->sin6_addr),
&(((struct sockaddr_in6 *) b)->sin6_addr),
sizeof (struct in6_addr));
* or nullify the previous sockaddr. Change the port if needed.
*/
static void
-notify_change (struct GNUNET_NAT_Handle *nat,
- struct sockaddr *addr,
- size_t addrlen,
- int new_port_mapped)
+notify_change (struct GNUNET_NAT_Handle *h,
+ struct sockaddr *addr, size_t addrlen, int new_port_mapped)
{
- if (new_port_mapped == nat->port_mapped)
+ if (new_port_mapped == h->port_mapped)
return;
- nat->port_mapped = new_port_mapped;
-
- if ( (NULL != nat->contact_addr) &&
- (NULL != nat->callback) )
- nat->callback (nat->callback_cls,
- GNUNET_NO,
- nat->contact_addr,
- sizeof (nat->contact_addr));
- GNUNET_free_non_null (nat->contact_addr);
- nat->contact_addr = NULL;
- GNUNET_free_non_null (nat->ext_addr);
- nat->ext_addr = NULL;
+ h->port_mapped = new_port_mapped;
+
+ if ((NULL != h->contact_addr) && (NULL != h->callback))
+ h->callback (h->callback_cls,
+ GNUNET_NO, h->contact_addr, sizeof (h->contact_addr));
+ GNUNET_free_non_null (h->contact_addr);
+ h->contact_addr = NULL;
+ GNUNET_free_non_null (h->ext_addr);
+ h->ext_addr = NULL;
if (NULL == addr)
- return;
- nat->ext_addr = GNUNET_malloc (addrlen);
- memcpy (nat->ext_addr, addr, addrlen);
+ return;
+ h->ext_addr = GNUNET_malloc (addrlen);
+ memcpy (h->ext_addr, addr, addrlen);
/* Recreate the ext_addr:public_port bogus address to pass to the callback */
- if (nat->ext_addr->sa_family == AF_INET)
+ if (h->ext_addr->sa_family == AF_INET)
{
- struct sockaddr_in tmp_addr;
+ struct sockaddr_in *tmp_addr;
tmp_addr = GNUNET_malloc (sizeof (struct sockaddr_in));
tmp_addr->sin_family = AF_INET;
#ifdef HAVE_SOCKADDR_IN_SIN_LEN
tmp_addr->sin_len = sizeof (struct sockaddr_in);
#endif
- tmp_addr->sin_port = port_mapped ? htons (nat->public_port) : 0;
- tmp_addr->sin_addr = ((struct sockaddr_in *) nat->ext_addr)->sin_addr;
- nat->contact_addr = (struct sockaddr *) tmp_addr;
- if (NULL != nat->callback)
- nat->callback (nat->callback_cls,
- GNUNET_YES,
- nat->contact_addr,
- sizeof (struct sockaddr_in));
+ tmp_addr->sin_port = h->port_mapped ? htons (h->public_port) : 0;
+ tmp_addr->sin_addr = ((struct sockaddr_in *) h->ext_addr)->sin_addr;
+ h->contact_addr = (struct sockaddr *) tmp_addr;
+
+ if (NULL != h->callback)
+ h->callback (h->callback_cls,
+ GNUNET_YES,
+ h->contact_addr, sizeof (struct sockaddr_in));
}
- else if (nat->ext_addr->sa_family == AF_INET6)
+ else if (h->ext_addr->sa_family == AF_INET6)
{
struct sockaddr_in6 *tmp_addr;
#ifdef HAVE_SOCKADDR_IN_SIN_LEN
tmp_addr->sin6_len = sizeof (struct sockaddr_in6);
#endif
- tmp_addr->sin6_port = port_mapped ? htons (nat->public_port) : 0;
- tmp_addr->sin6_addr = ((struct sockaddr_in6 *) nat->ext_addr)->sin6_addr;
- nat->contact_addr = (struct sockaddr *) tmp_addr;
- if (NULL != nat->callback)
- nat->callback (nat->callback_cls,
- GNUNET_YES,
- nat->contact_addr,
- sizeof (struct sockaddr_in6));
+ tmp_addr->sin6_port = h->port_mapped ? htons (h->public_port) : 0;
+ tmp_addr->sin6_addr = ((struct sockaddr_in6 *) h->ext_addr)->sin6_addr;
+ h->contact_addr = (struct sockaddr *) tmp_addr;
+
+ if (NULL != h->callback)
+ h->callback (h->callback_cls,
+ GNUNET_YES,
+ h->contact_addr, sizeof (struct sockaddr_in6));
}
else
{
}
}
+static void nat_pulse (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc);
static void
-nat_pulse (void *cls,
- const struct GNUNET_SCHEDULER_TaskContext *tc)
+pulse_cb (struct GNUNET_NAT_Handle *h)
{
- struct GNUNET_NAT_Handle *nat = cls;
- int old_status;
- int new_status;
+ socklen_t addrlen;
int port_mapped;
- struct sockaddr *ext_addr_upnp = NULL;
- struct sockaddr *ext_addr_natpmp = NULL;
- nat->pulse_timer = GNUNET_SCHEDULER_NO_TASK;
- old_status = get_traversal_status (nat);
+ /* One of the protocols is still working, wait for it to complete */
+ if (h->processing)
+ return;
- /* Only update the protocol that has been successful until now */
- if (nat->upnp_status >= GNUNET_NAT_PORT_UNMAPPED)
- nat->upnp_status =
- GNUNET_NAT_UPNP_pulse (nat->upnp, nat->is_enabled, GNUNET_YES,
- &ext_addr_upnp);
- else if (nat->natpmp_status >= GNUNET_NAT_PORT_UNMAPPED)
- nat->natpmp_status =
- GNUNET_NAT_NATPMP_pulse (nat->natpmp, nat->is_enabled,
- &ext_addr_natpmp);
- else
- {
- /* try both */
- nat->upnp_status =
- GNUNET_NAT_UPNP_pulse (nat->upnp, nat->is_enabled, GNUNET_YES,
- &ext_addr_upnp);
- nat->natpmp_status =
- GNUNET_NAT_NATPMP_pulse (nat->natpmp, nat->is_enabled,
- &ext_addr_natpmp);
- }
- new_status = get_traversal_status (nat);
- if ( (old_status != new_status) &&
- ( (new_status == GNUNET_NAT_PORT_UNMAPPED) ||
- (new_status == GNUNET_NAT_PORT_ERROR) ) )
+ h->new_status = get_traversal_status (h);
+ if ((h->old_status != h->new_status) &&
+ ((h->new_status == GNUNET_NAT_PORT_UNMAPPED) ||
+ (h->new_status == GNUNET_NAT_PORT_ERROR)))
GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
- "NAT",
- _("Port redirection failed: no UPnP or NAT-PMP routers supporting this feature found\n"));
+ "NAT",
+ _
+ ("Port redirection failed: no UPnP or NAT-PMP routers supporting this feature found\n"));
#ifdef DEBUG
- if (new_status != old_status)
+ if (h->new_status != h->old_status)
GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "NAT",
_("State changed from `%s' to `%s'\n"),
- get_nat_state_str (old_status),
- get_nat_state_str (new_status));
+ get_nat_state_str (h->old_status),
+ get_nat_state_str (h->new_status));
#endif
- port_mapped = (new_status == GNUNET_NAT_PORT_MAPPED);
- if (!(ext_addr_upnp || ext_addr_natpmp))
+ port_mapped = (h->new_status == GNUNET_NAT_PORT_MAPPED);
+ if (!(h->ext_addr_upnp || h->ext_addr_natpmp))
{
- /* Address has just changed and we could not get it, or it's the first try */
- if ( (NULL != nat->ext_addr) ||
- (GNUNET_NO == nat->did_warn) )
+ /* Address has just changed and we could not get it, or it's the first try,
+ * and we're not waiting for a reply from UPnP or NAT-PMP */
+ if (((NULL != h->ext_addr) ||
+ (GNUNET_NO == h->did_warn)) && h->processing != 0)
{
- GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
- "NAT",
- _("Could not determine external IP address\n"));
- nat->did_warn = GNUNET_YES;
+ GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
+ "NAT",
+ _("Could not determine external IP address\n"));
+ h->did_warn = GNUNET_YES;
}
- notify_change (nat, NULL, port_mapped);
+ notify_change (h, NULL, 0, port_mapped);
}
- else if (ext_addr_upnp && GNUNET_NAT_cmp_addr (nat->ext_addr, ext_addr_upnp) != 0)
+ else if (h->ext_addr_upnp
+ && GNUNET_NAT_cmp_addr (h->ext_addr, h->ext_addr_upnp) != 0)
{
+ addrlen = h->ext_addr_upnp->sa_family == AF_INET ?
+ sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6);
GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
- "NAT",
- _("External IP address changed to %s\n"),
- GNUNET_a2s (ext_addr_upnp, sizeof (ext_addr_upnp)));
- notify_change (nat, ext_addr_upnp, port_mapped);
+ "NAT",
+ _("External IP address changed to %s\n"),
+ GNUNET_a2s (h->ext_addr_upnp, addrlen));
+ notify_change (h, h->ext_addr_upnp, addrlen, port_mapped);
}
- else if (ext_addr_natpmp && GNUNET_NAT_cmp_addr (nat->ext_addr, ext_addr_natpmp) != 0)
+ else if (h->ext_addr_natpmp
+ && GNUNET_NAT_cmp_addr (h->ext_addr, h->ext_addr_natpmp) != 0)
{
+ addrlen = h->ext_addr_natpmp->sa_family == AF_INET ?
+ sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6);
GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, "NAT",
- _("External IP address changed to `%s'\n"),
- GNUNET_a2s (ext_addr_natpmp, sizeof (ext_addr_natpmp)));
- notify_change (nat, ext_addr_natpmp, port_mapped);
+ _("External IP address changed to `%s'\n"),
+ GNUNET_a2s (h->ext_addr_natpmp, addrlen));
+ notify_change (h, h->ext_addr_natpmp, addrlen, port_mapped);
+ }
+
+ h->pulse_timer = GNUNET_SCHEDULER_add_delayed (h->sched,
+ GNUNET_TIME_UNIT_SECONDS,
+ &nat_pulse, h);
+}
+
+static void
+upnp_pulse_cb (int status, struct sockaddr *ext_addr, void *cls)
+{
+ struct GNUNET_NAT_Handle *h = cls;
+
+ h->upnp_status = status;
+ h->ext_addr_upnp = ext_addr;
+
+ h->processing--;
+ pulse_cb (h);
+}
+
+#if 0
+static void
+natpmp_pulse_cb (int status, struct sockaddr *ext_addr, void *cls)
+{
+ struct GNUNET_NAT_Handle *h = cls;
+
+ h->natpmp_status = status;
+ h->ext_addr_natpmp = ext_addr;
+
+ h->processing--;
+ pulse_cb (h);
+}
+#endif
+
+static void
+nat_pulse (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct GNUNET_NAT_Handle *h = cls;
+
+ /* Stop if we're already waiting for an action to complete */
+ if (h->processing)
+ return;
+
+ h->pulse_timer = GNUNET_SCHEDULER_NO_TASK;
+ h->old_status = get_traversal_status (h);
+
+ /* Only update the protocol that has been successful until now */
+ if (h->upnp_status >= GNUNET_NAT_PORT_UNMAPPED)
+ {
+ h->processing = 1;
+ GNUNET_NAT_UPNP_pulse (h->upnp, h->is_enabled, GNUNET_YES);
+
+ /* Wait for the callback to call pulse_cb() to handle changes */
+ return;
+ }
+ else if (h->natpmp_status >= GNUNET_NAT_PORT_UNMAPPED)
+ {
+ h->processing = 1;
+#if 0
+ GNUNET_NAT_NATPMP_pulse (h->natpmp, h->is_enabled);
+#endif
+ }
+ else /* try both */
+ {
+ h->processing = 2;
+
+ GNUNET_NAT_UPNP_pulse (h->upnp, h->is_enabled, GNUNET_YES);
+#if 0
+ GNUNET_NAT_NATPMP_pulse (h->natpmp, h->is_enabled, natpmp_pulse_cb, h);
+#endif
}
- nat->pulse_timer = GNUNET_SCHEDULER_add_delayed (nat->sched,
- GNUNET_TIME_UNIT_SECONDS,
- &nat_pulse, nat);
}
* @return NULL on error, otherwise handle that can be used to unregister
*/
struct GNUNET_NAT_Handle *
-GNUNET_NAT_register (struct GNUNET_SCHEDULER_Handle *sched,
- const struct sockaddr *addr, socklen_t addrlen,
+GNUNET_NAT_register (struct GNUNET_SCHEDULER_Handle
+ *sched,
+ const struct sockaddr *addr,
+ socklen_t addrlen,
GNUNET_NAT_AddressCallback callback, void *callback_cls)
{
- struct GNUNET_NAT_Handle *nat;
+ struct GNUNET_NAT_Handle *h;
+
+ h = GNUNET_malloc (sizeof (struct GNUNET_NAT_Handle));
- nat = GNUNET_malloc (sizeof (struct GNUNET_NAT_Handle));
if (addr)
{
- GNUNET_assert ( (addr->sa_family == AF_INET) ||
- (addr->sa_family == AF_INET6) );
- nat->local_addr = GNUNET_malloc (addrlen);
- memcpy (nat->local_addr, addr, addrlen);
+ GNUNET_assert ((addr->sa_family == AF_INET) ||
+ (addr->sa_family == AF_INET6));
+ h->local_addr = GNUNET_malloc (addrlen);
+ memcpy (h->local_addr, addr, addrlen);
if (addr->sa_family == AF_INET)
{
- nat->public_port = ntohs (((struct sockaddr_in *) addr)->sin_port);
- ((struct sockaddr_in *) nat->local_addr)->sin_port = 0;
+ h->public_port = ntohs (((struct sockaddr_in *) addr)->sin_port);
+ ((struct sockaddr_in *) h->local_addr)->sin_port = 0;
}
else if (addr->sa_family == AF_INET6)
{
- nat->public_port = ntohs (((struct sockaddr_in6 *) addr)->sin6_port);
- ((struct sockaddr_in6 *) nat->local_addr)->sin6_port = 0;
+ h->public_port = ntohs (((struct sockaddr_in6 *) addr)->sin6_port);
+ ((struct sockaddr_in6 *) h->local_addr)->sin6_port = 0;
}
}
- nat->should_change = GNUNET_YES;
- nat->sched = sched;
- nat->is_enabled = GNUNET_YES;
- nat->upnp_status = GNUNET_NAT_PORT_UNMAPPED;
- nat->natpmp_status = GNUNET_NAT_PORT_UNMAPPED;
- nat->callback = callback;
- nat->callback_cls = callback_cls;
- nat->natpmp = GNUNET_NAT_NATPMP_init (nat->local_addr, addrlen, nat->public_port);
- nat->upnp = GNUNET_NAT_UPNP_init (nat->local_addr, addrlen, nat->public_port);
- nat->pulse_timer = GNUNET_SCHEDULER_add_delayed (sched,
- GNUNET_TIME_UNIT_SECONDS,
- &nat_pulse, nat);
- return nat;
+ h->should_change = GNUNET_YES;
+ h->sched = sched;
+ h->is_enabled = GNUNET_YES;
+ h->upnp_status = GNUNET_NAT_PORT_UNMAPPED;
+ h->natpmp_status = GNUNET_NAT_PORT_UNMAPPED;
+ h->callback = callback;
+ h->callback_cls = callback_cls;
+ h->upnp =
+ GNUNET_NAT_UPNP_init (h->sched, h->local_addr, addrlen, h->public_port,
+ upnp_pulse_cb, h);
+#if 0
+ h->natpmp =
+ GNUNET_NAT_NATPMP_init (h->sched, h->local_addr, addrlen, h->public_port,
+ natpmp_pulse_cb, h);
+#endif
+ h->pulse_timer = GNUNET_SCHEDULER_add_delayed (sched,
+ GNUNET_TIME_UNIT_SECONDS,
+ &nat_pulse, h);
+ return h;
}
void
GNUNET_NAT_unregister (struct GNUNET_NAT_Handle *h)
{
- struct sockaddr *addr;
-
- GNUNET_SCHEDULER_cancel (h->sched,
- h->pulse_timer);
- h->upnp_status =
- GNUNET_NAT_UPNP_pulse (h->upnp,
- GNUNET_NO, GNUNET_NO,
- &addr);
- h->natpmp_status =
- GNUNET_NAT_NATPMP_pulse (h->natpmp, GNUNET_NO,
- &addr);
- GNUNET_NAT_NATPMP_close (h->natpmp);
+ GNUNET_NAT_UPNP_pulse (h->upnp, GNUNET_NO, GNUNET_NO);
GNUNET_NAT_UPNP_close (h->upnp);
+
+#if 0
+ GNUNET_NAT_NATPMP_pulse (h->natpmp, GNUNET_NO);
+ GNUNET_NAT_NATPMP_close (h->natpmp);
+#endif
+
+ GNUNET_SCHEDULER_cancel (h->sched, h->pulse_timer);
+
GNUNET_free_non_null (h->local_addr);
GNUNET_free_non_null (h->ext_addr);
+ GNUNET_free_non_null (h->ext_addr_upnp);
+ GNUNET_free_non_null (h->ext_addr_natpmp);
GNUNET_free (h);
}
/* end of nat.c */
-
* Used to communicate with the UPnP and NAT-PMP plugins
*/
enum GNUNET_NAT_PortState
- {
- GNUNET_NAT_PORT_ERROR,
+{
+ GNUNET_NAT_PORT_ERROR,
/**
* the port isn't forwarded
*/
- GNUNET_NAT_PORT_UNMAPPED,
+ GNUNET_NAT_PORT_UNMAPPED,
/**
* we're cancelling the port forwarding
*/
- GNUNET_NAT_PORT_UNMAPPING,
+ GNUNET_NAT_PORT_UNMAPPING,
/**
* we're in the process of trying to set up port forwarding
*/
- GNUNET_NAT_PORT_MAPPING,
+ GNUNET_NAT_PORT_MAPPING,
/**
* we've successfully forwarded the port
*/
- GNUNET_NAT_PORT_MAPPED
- };
+ GNUNET_NAT_PORT_MAPPED
+};
/**
* @param b second sockaddr
* @return 0 if addresses are equal, non-null value otherwise
*/
-int GNUNET_NAT_cmp_addr (const struct sockaddr *a,
- const struct sockaddr *b);
+int GNUNET_NAT_cmp_addr (const struct sockaddr *a, const struct sockaddr *b);
#endif
#include "platform.h"
#include "gnunet_common.h"
#include "gnunet_nat_lib.h"
+#include "nat.h"
#include "natpmp.h"
#define LIFETIME_SECS 3600
/* Component name for logging */
#define COMP_NAT_NATPMP _("NAT (NAT-PMP))")
-typedef enum
+enum NATPMP_state
{
NATPMP_IDLE,
NATPMP_ERR,
NATPMP_SEND_UNMAP,
NATPMP_RECV_UNMAP
}
-NATPMP_state;
+ ;
struct GNUNET_NAT_NATPMP_Handle
{
const struct sockaddr *addr;
socklen_t addrlen;
- struct sockaddr*ext_addr;
+ struct sockaddr *ext_addr;
int is_mapped;
int has_discovered;
int port;
time_t renew_time;
time_t command_time;
- NATPMP_state state;
- natpmp_t natpmp;
+ enum NATPMP_state state;
+ struct natpmp_t natpmp;
+ struct GNUNET_SCHEDULER_Handle *sched;
};
#ifdef DEBUG
if (ret == NATPMP_TRYAGAIN)
GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
- COMP_NAT_NATPMP, _("%s retry (%d)\n"),
- func, ret);
+ COMP_NAT_NATPMP, _("%s retry (%d)\n"), func, ret);
if (ret >= 0)
GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
- COMP_NAT_NATPMP, _("%s succeeded (%d)\n"),
- func, ret);
+ COMP_NAT_NATPMP, _("%s succeeded (%d)\n"), func, ret);
else
- GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
- COMP_NAT_NATPMP,
- "%s failed. natpmp returned %d (%s); errno is %d (%s)\n",
- func, ret,
- strnatpmperr (ret), errno, strerror (errno));
+ GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
+ COMP_NAT_NATPMP,
+ "%s failed. natpmp returned %d (%s); errno is %d (%s)\n",
+ func, ret, strnatpmperr (ret), errno, strerror (errno));
#endif
}
struct GNUNET_NAT_NATPMP_Handle *
-GNUNET_NAT_NATPMP_init (const struct sockaddr *addr, socklen_t addrlen,
+GNUNET_NAT_NATPMP_init (struct GNUNET_SCHEDULER_Handle *sched,
+ const struct sockaddr *addr, socklen_t addrlen,
u_short port)
{
struct GNUNET_NAT_NATPMP_Handle *nat;
nat = GNUNET_malloc (sizeof (struct GNUNET_NAT_NATPMP_Handle));
+ nat->sched = sched;
nat->state = NATPMP_DISCOVER;
nat->port = port;
nat->addr = addr;
}
void
-GNUNET_NAT_NATPMP_close (GNUNET_NAT_NATPMP_Handle * nat)
+GNUNET_NAT_NATPMP_close (struct GNUNET_NAT_NATPMP_Handle *nat)
{
if (nat)
{
if ((nat->state == NATPMP_RECV_PUB) && can_send_command (nat))
{
- natpmpresp_t response;
+ struct natpmpresp_t response;
const int val = readnatpmpresponseorretry (&nat->natpmp,
&response);
log_val ("readnatpmpresponseorretry", val);
if (response.pnu.publicaddress.family == AF_INET)
{
- v4 = GNUNET_malloc (sizeof (struct sockaddr_in));
- nat->ext_addr = (struct sockaddr*) v4;
- v4->sin_family = AF_INET;
- v4->sin_port = response.pnu.newportmapping.mappedpublicport;
+ v4 = GNUNET_malloc (sizeof (struct sockaddr_in));
+ nat->ext_addr = (struct sockaddr *) v4;
+ v4->sin_family = AF_INET;
+ v4->sin_port = response.pnu.newportmapping.mappedpublicport;
memcpy (&v4->sin_addr, &response.pnu.publicaddress.addr,
sizeof (struct in_addr));
#ifdef DEBUG
- GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_NATPMP,
- _("Found public IP address %s\n"),
- inet_ntop (AF_INET,
- &response.pnu.publicaddress.addr,
- buf,
- sizeof(buf)));
+ GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_NATPMP,
+ _("Found public IP address %s\n"),
+ inet_ntop (AF_INET,
+ &response.pnu.publicaddress.addr,
+ buf, sizeof (buf)));
#endif
}
else
{
v6 = GNUNET_malloc (sizeof (struct sockaddr_in6));
- nat->ext_addr = (struct sockaddr*) v6;
- v6->sin6_family = AF_INET6;
- v6->sin6_port = response.pnu.newportmapping.mappedpublicport;
- memcpy (&v6->sin6_addr,
- &response.pnu.publicaddress.addr6,
+ nat->ext_addr = (struct sockaddr *) v6;
+ v6->sin6_family = AF_INET6;
+ v6->sin6_port = response.pnu.newportmapping.mappedpublicport;
+ memcpy (&v6->sin6_addr,
+ &response.pnu.publicaddress.addr6,
(sizeof (struct in6_addr)));
#ifdef DEBUG
- GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_NATPMP,
- _("Found public IP address %s\n"),
- inet_ntop (AF_INET6,
- &response.pnu.publicaddress.addr6,
- buf,
- sizeof(buf)));
+ GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_NATPMP,
+ _("Found public IP address %s\n"),
+ inet_ntop (AF_INET6,
+ &response.pnu.publicaddress.addr6,
+ buf, sizeof (buf)));
#endif
- }
- *ext_addr = nat->ext_addr;
+ }
+ *ext_addr = nat->ext_addr;
nat->state = NATPMP_IDLE;
}
else if (val != NATPMP_TRYAGAIN)
if (nat->state == NATPMP_RECV_UNMAP)
{
- natpmpresp_t resp;
+ struct natpmpresp_t resp;
const int val = readnatpmpresponseorretry (&nat->natpmp, &resp);
log_val ("readnatpmpresponseorretry", val);
if (val >= 0)
{
const int p = resp.pnu.newportmapping.privateport;
GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_NATPMP,
- _("No longer forwarding port %d\n"), p);
+ _("No longer forwarding port %d\n"), p);
if (nat->port == p)
{
nat->port = -1;
if (nat->state == NATPMP_RECV_MAP)
{
- natpmpresp_t resp;
+ struct natpmpresp_t resp;
const int val = readnatpmpresponseorretry (&nat->natpmp, &resp);
log_val ("readnatpmpresponseorretry", val);
if (val >= 0)
nat->renew_time = time (NULL) + LIFETIME_SECS;
nat->port = resp.pnu.newportmapping.privateport;
GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_NATPMP,
- _("Port %d forwarded successfully\n"), nat->port);
+ _("Port %d forwarded successfully\n"), nat->port);
}
else if (val != NATPMP_TRYAGAIN)
{
struct GNUNET_NAT_NATPMP_Handle;
-struct GNUNET_NAT_NATPMP_Handle *
-GNUNET_NAT_NATPMP_init (const struct sockaddr *addr,
- socklen_t addrlen,
- unsigned short port);
+struct GNUNET_NAT_NATPMP_Handle *GNUNET_NAT_NATPMP_init (struct
+ GNUNET_SCHEDULER_Handle
+ *sched,
+ const struct sockaddr
+ *addr,
+ socklen_t addrlen,
+ unsigned short port);
-void GNUNET_NAT_NATPMP_close (struct GNUNET_NAT_NATPMP_Handle * nat);
+void GNUNET_NAT_NATPMP_close (struct GNUNET_NAT_NATPMP_Handle *nat);
-int GNUNET_NAT_NATPMP_pulse (struct GNUNET_NAT_NATPMP_Handle * nat,
- int is_enabled,
- struct sockaddr **ext_addr);
+int GNUNET_NAT_NATPMP_pulse (struct GNUNET_NAT_NATPMP_Handle *nat,
+ int is_enabled, struct sockaddr **ext_addr);
-#endif
+#endif
/* NATPMP_H */
#include "gnunet_nat_lib.h"
/* Time to wait before stopping NAT, in seconds */
-#define TIMEOUT 30
+#define TIMEOUT 60
struct addr_cls
{
const struct sockaddr *addr;
socklen_t addrlen;
};
-//typedef addr_cls addr_cls;
static void
addr_callback (void *cls, int add_remove,
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 addr_cls *data = cls;
data->addrlen = addrlen;
}
+ if (strcmp (name, "eth1") == 0 && addr->sa_family == AF_INET)
+ return GNUNET_SYSERR;
+
+ return GNUNET_OK;
+
+
if (isDefault && addr)
return GNUNET_SYSERR;
else
GNUNET_OS_network_interfaces_list (process_if, &data);
if (!data.addr)
{
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not find a valid interface address!\n");
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not find a valid interface address!\n");
exit (GNUNET_SYSERR);
}
else
((struct sockaddr_in6 *) addr)->sin6_port = htons (2086);
- GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Requesting NAT redirection from address %s...\n", GNUNET_a2s (addr, data.addrlen));
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Requesting NAT redirection from address %s...\n",
+ GNUNET_a2s (addr, data.addrlen));
nat = GNUNET_NAT_register (sched, addr, data.addrlen, addr_callback, NULL);
GNUNET_free (addr);
- GNUNET_SCHEDULER_add_delayed (sched,
- GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, TIMEOUT),
- stop, nat);
+ GNUNET_SCHEDULER_add_delayed (sched,
+ GNUNET_TIME_relative_multiply
+ (GNUNET_TIME_UNIT_SECONDS, TIMEOUT), stop,
+ nat);
}
int
--- /dev/null
+/*
+ This file is part of GNUnet.
+ (C) 2009, 2010 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/*
+ * Code in this file is originally based on the miniupnp library.
+ * Copyright (c) 2005-2009, Thomas BERNARD. All rights reserved.
+ *
+ * Original licence:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file nat/upnp-commands.c
+ * @brief Implementation of a basic set of UPnP commands
+ *
+ * @author Milan Bouchet-Valat
+ */
+
+#include "platform.h"
+#include "gnunet_util_lib.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "upnp-reply-parse.h"
+#include "upnp-igd-parse.h"
+#include "upnp-discover.h"
+#include "upnp-commands.h"
+
+#define SOAP_PREFIX "s"
+#define SERVICE_PREFIX "u"
+#define SERVICE_PREFIX2 'u'
+#define MAX_HOSTNAME_LEN 64
+
+#define PRINT_UPNP_ERROR(a, b) GNUNET_log_from(GNUNET_ERROR_TYPE_WARNING, "UPnP", _("%s failed at %s:%d: %s\n"), a, __FILE__, __LINE__, b);
+
+
+/**
+ * Private closure used by UPNP_command() and its callbacks.
+ */
+struct UPNP_command_cls
+{
+ /**
+ * Connection handle used for sending and receiving.
+ */
+ struct GNUNET_CONNECTION_Handle *s;
+
+ /**
+ * Transmission handle used for sending command.
+ */
+ struct GNUNET_CONNECTION_TransmitHandle *th;
+
+ /**
+ * HTML content to send to run command.
+ */
+ char *content;
+
+ /**
+ * Buffer where to copy received data to pass to caller.
+ */
+ char *buffer;
+
+ /**
+ * Size of buffer.
+ */
+ size_t buf_size;
+
+ /**
+ * User callback to trigger when done.
+ */
+ UPNP_command_cb_ caller_cb;
+
+ /**
+ * User closure to pass to caller_cb.
+ */
+ void *caller_cls;
+};
+
+/**
+ * Get the length of content included in an HTML line.
+ *
+ * @param p line to parse
+ * @param n size of p
+ * @return the length of the content
+ */
+static ssize_t
+get_content_len_from_line (const char *p, int n)
+{
+ static const char cont_len_str[] = "content-length";
+ const char *p2 = cont_len_str;
+ int a = 0;
+
+ while (*p2)
+ {
+ if (n == 0)
+ return -1;
+
+ if (*p2 != *p && *p2 != (*p + 32))
+ return -1;
+
+ p++;
+ p2++;
+ n--;
+ }
+
+ if (n == 0)
+ return -1;
+
+ if (*p != ':')
+ return -1;
+
+ p++;
+ n--;
+
+ while (*p == ' ')
+ {
+ if (n == 0)
+ return -1;
+
+ p++;
+ n--;
+ }
+
+ while (*p >= '0' && *p <= '9')
+ {
+ if (n == 0)
+ return -1;
+
+ a = (a * 10) + (*p - '0');
+ p++;
+ n--;
+ }
+
+ return a;
+}
+
+/**
+ * Get the respective lengths of content and header from an HTML reply.
+ *
+ * @param p HTML to parse
+ * @param n size of p
+ * @param content_len pointer to store content length to
+ * @param content_len pointer to store header length to
+ */
+static void
+get_content_and_header_len (const char *p, int n,
+ int *content_len, int *header_len)
+{
+ const char *line;
+ int line_len;
+ int r;
+
+ line = p;
+
+ while (line < p + n)
+ {
+ line_len = 0;
+
+ while (line[line_len] != '\r' && line[line_len] != '\r')
+ {
+ if (line + line_len >= p + n)
+ return;
+
+ line_len++;
+ }
+
+ r = get_content_len_from_line (line, line_len);
+
+ if (r > 0)
+ *content_len = r;
+
+ line = line + line_len + 2;
+
+ if (line[0] == '\r' && line[1] == '\n')
+ {
+ *header_len = (line - p) + 2;
+ return;
+ }
+ }
+}
+
+/**
+ * Receive reply of the device to our UPnP command.
+ *
+ * @param data closure from UPNP_command()
+ * @param buf struct UPNP_command_cls *cls
+ * @param available number of bytes in buf
+ * @param addr address of the sender
+ * @param addrlen size of addr
+ * @errCode value of errno
+ */
+static void
+UPNP_command_receiver (void *data,
+ const void *buf,
+ size_t available,
+ const struct sockaddr *addr,
+ socklen_t addrlen, int errCode)
+{
+ struct UPNP_command_cls *cls = data;
+ int content_len;
+ int header_len;
+
+ if (available > 0)
+ {
+ content_len = -1;
+ header_len = -1;
+ get_content_and_header_len (buf, available, &content_len, &header_len);
+
+ strncpy (cls->buffer, (char *) buf, cls->buf_size - 2);
+ cls->buffer[cls->buf_size - 2] = '\0';
+ }
+ else
+ {
+ cls->buffer[0] = '\0';
+ }
+
+ GNUNET_CONNECTION_destroy (cls->s, GNUNET_NO);
+
+ (*cls->caller_cb) (cls->buffer, cls->buf_size, cls->caller_cls);
+
+ GNUNET_free (cls->content);
+ GNUNET_free (cls);
+}
+
+/**
+ * Send UPnP command to device.
+ */
+static size_t
+UPNP_command_transmit (void *data, size_t size, void *buf)
+{
+ struct UPNP_command_cls *cls = data;
+ int n;
+ char *content = cls->content;
+
+ n = strlen (content);
+ memcpy (buf, content, size);
+
+ GNUNET_CONNECTION_receive (cls->s, cls->buf_size, GNUNET_TIME_UNIT_MINUTES,
+ UPNP_command_receiver, cls);
+
+ return n;
+}
+
+/**
+ * Parse a HTTP URL string to extract hostname, port and path it points to.
+ *
+ * @param url source string corresponding to URL
+ * @param hostname pointer where to store hostname (size of MAX_HOSTNAME_LEN+1)
+ * @param port pointer where to store port
+ * @param path pointer where to store path
+ *
+ * @return GNUNET_OK on success, GNUNET_SYSERR on failure
+ */
+int
+parse_url (const char *url, char *hostname, unsigned short *port, char **path)
+{
+ char *p1, *p2, *p3;
+
+ if (!url)
+ return GNUNET_SYSERR;
+
+ p1 = strstr (url, "://");
+
+ if (!p1)
+ return GNUNET_SYSERR;
+
+ p1 += 3;
+
+ if ((url[0] != 'h') || (url[1] != 't')
+ || (url[2] != 't') || (url[3] != 'p'))
+ return GNUNET_SYSERR;
+
+ p2 = strchr (p1, ':');
+ p3 = strchr (p1, '/');
+
+ if (!p3)
+ return GNUNET_SYSERR;
+
+ memset (hostname, 0, MAX_HOSTNAME_LEN + 1);
+
+ if (!p2 || (p2 > p3))
+ {
+ strncpy (hostname, p1, MIN (MAX_HOSTNAME_LEN, (int) (p3 - p1)));
+ *port = 80;
+ }
+ else
+ {
+ strncpy (hostname, p1, MIN (MAX_HOSTNAME_LEN, (int) (p2 - p1)));
+ *port = 0;
+ p2++;
+
+ while ((*p2 >= '0') && (*p2 <= '9'))
+ {
+ *port *= 10;
+ *port += (unsigned short) (*p2 - '0');
+ p2++;
+ }
+ }
+
+ *path = p3;
+ return GNUNET_OK;
+}
+
+/**
+ * Send UPnP command to the device identified by url and service.
+ *
+ * @param sched scheduler to use for network tasks
+ * @param url control URL of the device
+ * @param service type of the service corresponding to the command
+ * @param action action to send
+ * @param args arguments for action
+ * @param caller_cb user callback to trigger when done
+ * @param caller_cls closure to pass to caller_cb
+ */
+void
+UPNP_command_ (struct GNUNET_SCHEDULER_Handle *sched,
+ const char *url, const char *service,
+ const char *action, struct UPNP_Arg_ *args,
+ char *buffer, size_t buf_size,
+ UPNP_command_cb_ caller_cb, void *caller_cls)
+{
+ struct GNUNET_CONNECTION_Handle *s;
+ struct UPNP_command_cls *cls;
+ struct sockaddr_in dest;
+ struct sockaddr_in6 dest6;
+ char hostname[MAX_HOSTNAME_LEN + 1];
+ unsigned short port = 0;
+ char *path;
+ char soap_act[128];
+ char soap_body[2048];
+ int body_size;
+ char *content_buf;
+ int headers_size;
+ char port_str[8];
+
+ snprintf (soap_act, sizeof (soap_act), "%s#%s", service, action);
+
+ if (args == NULL)
+ {
+ snprintf (soap_body, sizeof (soap_body),
+ "<?xml version=\"1.0\"?>\r\n"
+ "<" SOAP_PREFIX ":Envelope "
+ "xmlns:" SOAP_PREFIX
+ "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
+ SOAP_PREFIX
+ ":encodingStyle=\"http://schema GNUNET_free (content_buf);s.xmlsoap.org/soap/encoding/\">"
+ "<" SOAP_PREFIX ":Body>" "<" SERVICE_PREFIX
+ ":%s xmlns:" SERVICE_PREFIX "=\"%s\">" "</"
+ SERVICE_PREFIX ":%s>" "</" SOAP_PREFIX
+ ":Body></" SOAP_PREFIX ":Envelope>" "\r\n",
+ action, service, action);
+ }
+ else
+ {
+ char *p;
+ const char *pe, *pv;
+ int soap_body_len;
+
+ soap_body_len = snprintf (soap_body, sizeof (soap_body),
+ "<?xml version=\"1.0\"?>\r\n"
+ "<" SOAP_PREFIX ":Envelope "
+ "xmlns:" SOAP_PREFIX
+ "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
+ SOAP_PREFIX
+ ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
+ "<" SOAP_PREFIX ":Body>" "<" SERVICE_PREFIX
+ ":%s xmlns:" SERVICE_PREFIX "=\"%s\">",
+ action, service);
+
+ p = soap_body + soap_body_len;
+
+ while (args->elt)
+ {
+ /* check that we are never overflowing the string... */
+ if (soap_body + sizeof (soap_body) <= p + 100)
+ {
+ GNUNET_assert (GNUNET_NO);
+ (*caller_cb) (buffer, 0, caller_cls);
+ return;
+ }
+ *(p++) = '<';
+ pe = args->elt;
+ while (*pe)
+ *(p++) = *(pe++);
+ *(p++) = '>';
+ if ((pv = args->val))
+ {
+ while (*pv)
+ *(p++) = *(pv++);
+ }
+ *(p++) = '<';
+ *(p++) = '/';
+ pe = args->elt;
+ while (*pe)
+ *(p++) = *(pe++);
+ *(p++) = '>';
+ args++;
+ }
+ *(p++) = '<';
+ *(p++) = '/';
+ *(p++) = SERVICE_PREFIX2;
+ *(p++) = ':';
+ pe = action;
+
+ while (*pe)
+ *(p++) = *(pe++);
+
+ strncpy (p, "></" SOAP_PREFIX ":Body></" SOAP_PREFIX ":Envelope>\r\n",
+ soap_body + sizeof (soap_body) - p);
+ }
+
+ if (GNUNET_OK != parse_url (url, hostname, &port, &path))
+ {
+ GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "UPnP",
+ "Invalid URL passed to UPNP_command(): %s\n", url);
+ return;
+ }
+
+
+ /* Test IPv4 address, else use IPv6 */
+ memset (&dest, 0, sizeof (dest));
+ memset (&dest6, 0, sizeof (dest6));
+
+ if (inet_pton (AF_INET, hostname, &dest.sin_addr) == 1)
+ {
+ dest.sin_family = AF_INET;
+ dest.sin_port = htons (port);
+#ifdef HAVE_SOCKADDR_IN_SIN_LEN
+ dest.sin_len = sizeof (dest);
+#endif
+
+ s = GNUNET_CONNECTION_create_from_sockaddr (sched, PF_INET,
+ (struct sockaddr *) &dest,
+ sizeof (dest));
+ }
+ else if (inet_pton (AF_INET6, hostname, &dest6.sin6_addr) == 1)
+ {
+ dest6.sin6_family = AF_INET6;
+ dest6.sin6_port = htons (port);
+#ifdef HAVE_SOCKADDR_IN_SIN_LEN
+ dest6.sin6_len = sizeof (dest6);
+#endif
+
+ s = GNUNET_CONNECTION_create_from_sockaddr (sched, PF_INET6,
+ (struct sockaddr *) &dest6,
+ sizeof (dest6));
+ }
+ else
+ {
+ GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, _("%s failed at %s:%d\n"),
+ "UPnP", "inet_pton", __FILE__, __LINE__);
+
+ (*caller_cb) (buffer, 0, caller_cls);
+ return;
+ }
+
+ body_size = (int) strlen (soap_body);
+ content_buf = GNUNET_malloc (512 + body_size);
+
+ /* We are not using keep-alive HTTP connections.
+ * HTTP/1.1 needs the header Connection: close to do that.
+ * This is the default with HTTP/1.0 */
+ /* Connection: Close is normally there only in HTTP/1.1 but who knows */
+ port_str[0] = '\0';
+
+ if (port != 80)
+ snprintf (port_str, sizeof (port_str), ":%hu", port);
+
+ headers_size = snprintf (content_buf, 512, "POST %s HTTP/1.1\r\n" "Host: %s%s\r\n" "User-Agent: GNU, UPnP/1.0, GNUnet/" PACKAGE_VERSION "\r\n" "Content-Length: %d\r\n" "Content-Type: text/xml\r\n" "SOAPAction: \"%s\"\r\n" "Connection: Close\r\n" "Cache-Control: no-cache\r\n" /* ??? */
+ "Pragma: no-cache\r\n"
+ "\r\n", path, hostname, port_str, body_size,
+ soap_act);
+ memcpy (content_buf + headers_size, soap_body, body_size);
+
+#ifdef DEBUG_UPNP
+ GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP",
+ "Sending command '%s' to '%s' (service '%s')\n",
+ action, url, service);
+#endif
+
+ cls = GNUNET_malloc (sizeof (struct UPNP_command_cls));
+ cls->s = s;
+ cls->content = content_buf;
+ cls->buffer = buffer;
+ cls->buf_size = buf_size;
+ cls->caller_cb = caller_cb;
+ cls->caller_cls = caller_cls;
+
+ cls->th =
+ GNUNET_CONNECTION_notify_transmit_ready (s, body_size + headers_size,
+ GNUNET_TIME_relative_multiply
+ (GNUNET_TIME_UNIT_SECONDS, 15),
+ &UPNP_command_transmit, cls);
+
+
+ if (cls->th == NULL)
+ {
+#ifdef DEBUG_UPNP
+ GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "UPnP",
+ "Error sending SOAP request at %s:%d\n", __FILE__,
+ __LINE__);
+#endif
+
+ (*caller_cb) (buffer, 0, caller_cls);
+
+ GNUNET_free (content_buf);
+ GNUNET_free (cls);
+ GNUNET_CONNECTION_destroy (s, GNUNET_NO);
+ return;
+ }
+}
+
+struct get_external_ip_address_cls
+{
+ UPNP_get_external_ip_address_cb_ caller_cb;
+ void *caller_cls;
+};
+
+static void
+get_external_ip_address_receiver (char *response, size_t received, void *data)
+{
+ struct get_external_ip_address_cls *cls = data;
+ struct UPNP_REPLY_NameValueList_ pdata;
+ char extIpAdd[128];
+ char *p;
+ int ret = UPNP_COMMAND_UNKNOWN_ERROR;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Response: %s", response);
+
+ UPNP_REPLY_parse_ (response, received, &pdata);
+ p = UPNP_REPLY_get_value_ (&pdata, "NewExternalIPAddress");
+ if (p)
+ {
+ strncpy (extIpAdd, p, 128);
+ extIpAdd[127] = '\0';
+ ret = UPNP_COMMAND_SUCCESS;
+ }
+ else
+ extIpAdd[0] = '\0';
+
+ p = UPNP_REPLY_get_value_ (&pdata, "errorCode");
+ if (p)
+ {
+ ret = UPNP_COMMAND_UNKNOWN_ERROR;
+ sscanf (p, "%d", &ret);
+ }
+ cls->caller_cb (ret, extIpAdd, cls->caller_cls);
+
+ UPNP_REPLY_free_ (&pdata);
+ GNUNET_free (response);
+ GNUNET_free (cls);
+}
+
+/* UPNP_get_external_ip_address_() call the corresponding UPNP method.
+ *
+ * Return values :
+ * 0 : SUCCESS
+ * NON ZERO : ERROR Either an UPnP error code or an unknown error.
+ *
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 501 Action Failed - See UPnP Device Architecture section on Control.
+ */
+void
+UPNP_get_external_ip_address_ (struct GNUNET_SCHEDULER_Handle *sched,
+ const char *control_url,
+ const char *service_type,
+ UPNP_get_external_ip_address_cb_ caller_cb,
+ void *caller_cls)
+{
+ struct get_external_ip_address_cls *cls;
+ char *buffer;
+
+ if (!control_url || !service_type)
+ caller_cb (UPNP_COMMAND_INVALID_ARGS, NULL, caller_cls);
+
+ cls = GNUNET_malloc (sizeof (struct get_external_ip_address_cls));
+ cls->caller_cb = caller_cb;
+ cls->caller_cls = caller_cls;
+
+ buffer = GNUNET_malloc (UPNP_COMMAND_BUFSIZE);
+
+ UPNP_command_ (sched, control_url, service_type, "GetExternalIPAddress",
+ NULL, buffer, UPNP_COMMAND_BUFSIZE,
+ (UPNP_command_cb_) get_external_ip_address_receiver, cls);
+}
+
+struct PortMapping_cls
+{
+ const char *control_url;
+ const char *service_type;
+ const char *ext_port;
+ const char *in_port;
+ const char *proto;
+ const char *remoteHost;
+ UPNP_port_mapping_cb_ caller_cb;
+ void *caller_cls;
+};
+
+static void
+add_delete_port_mapping_receiver (char *response, size_t received, void *data)
+{
+ struct PortMapping_cls *cls = data;
+ struct UPNP_REPLY_NameValueList_ pdata;
+ const char *resVal;
+ int ret;
+
+ UPNP_REPLY_parse_ (response, received, &pdata);
+ resVal = UPNP_REPLY_get_value_ (&pdata, "errorCode");
+ if (resVal)
+ {
+ ret = UPNP_COMMAND_UNKNOWN_ERROR;
+ sscanf (resVal, "%d", &ret);
+ }
+ else
+ {
+ ret = UPNP_COMMAND_SUCCESS;
+ }
+
+ cls->caller_cb (ret, cls->control_url, cls->service_type,
+ cls->ext_port, cls->in_port, cls->proto,
+ cls->remoteHost, cls->caller_cls);
+
+ UPNP_REPLY_free_ (&pdata);
+ GNUNET_free (response);
+ GNUNET_free (cls);
+}
+
+void
+UPNP_add_port_mapping_ (struct GNUNET_SCHEDULER_Handle *sched,
+ const char *control_url, const char *service_type,
+ const char *ext_port,
+ const char *in_port,
+ const char *inClient,
+ const char *desc,
+ const char *proto, const char *remoteHost,
+ UPNP_port_mapping_cb_ caller_cb, void *caller_cls)
+{
+ struct UPNP_Arg_ args[9];
+ struct PortMapping_cls *cls;
+ char *buffer;
+
+ if (!in_port || !inClient || !proto || !ext_port)
+ {
+ caller_cb (UPNP_COMMAND_INVALID_ARGS, control_url, service_type,
+ ext_port, in_port, proto, remoteHost, caller_cls);
+ return;
+ }
+
+ args[0].elt = "NewRemoteHost";
+ args[0].val = remoteHost;
+ args[1].elt = "NewExternalPort";
+ args[1].val = ext_port;
+ args[2].elt = "NewProtocol";
+ args[2].val = proto;
+ args[3].elt = "NewInternalPort";
+ args[3].val = in_port;
+ args[4].elt = "NewInternalClient";
+ args[4].val = inClient;
+ args[5].elt = "NewEnabled";
+ args[5].val = "1";
+ args[6].elt = "NewPortMappingDescription";
+ args[6].val = desc ? desc : "GNUnet";
+ args[7].elt = "NewLeaseDuration";
+ args[7].val = "0";
+ args[8].elt = NULL;
+ args[8].val = NULL;
+
+ cls = GNUNET_malloc (sizeof (struct PortMapping_cls));
+ cls->control_url = control_url;
+ cls->service_type = service_type;
+ cls->ext_port = ext_port;;
+ cls->in_port = in_port;
+ cls->proto = proto;
+ cls->remoteHost = remoteHost;
+ cls->caller_cb = caller_cb;
+ cls->caller_cls = caller_cls;
+
+ buffer = GNUNET_malloc (UPNP_COMMAND_BUFSIZE);
+
+ UPNP_command_ (sched, control_url, service_type, "AddPortMapping",
+ args, buffer, UPNP_COMMAND_BUFSIZE,
+ add_delete_port_mapping_receiver, cls);
+}
+
+void
+UPNP_delete_port_mapping_ (struct GNUNET_SCHEDULER_Handle *sched,
+ const char *control_url, const char *service_type,
+ const char *ext_port, const char *proto,
+ const char *remoteHost,
+ UPNP_port_mapping_cb_ caller_cb, void *caller_cls)
+{
+ struct UPNP_Arg_ args[4];
+ struct PortMapping_cls *cls;
+ char *buffer;
+
+ if (!ext_port || !proto)
+ {
+ caller_cb (UPNP_COMMAND_INVALID_ARGS, control_url, service_type,
+ ext_port, NULL, proto, remoteHost, caller_cls);
+ return;
+ }
+
+ args[0].elt = "NewRemoteHost";
+ args[0].val = remoteHost;
+ args[1].elt = "NewExternalPort";
+ args[1].val = ext_port;
+ args[2].elt = "NewProtocol";
+ args[2].val = proto;
+ args[3].elt = NULL;
+ args[3].val = NULL;
+
+ cls = GNUNET_malloc (sizeof (struct PortMapping_cls));
+ cls->control_url = control_url;
+ cls->service_type = service_type;
+ cls->ext_port = ext_port;
+ cls->in_port = "0";
+ cls->proto = proto;
+ cls->remoteHost = remoteHost;
+ cls->caller_cb = caller_cb;
+ cls->caller_cls = caller_cls;
+
+ buffer = GNUNET_malloc (UPNP_COMMAND_BUFSIZE);
+
+ UPNP_command_ (sched, control_url, service_type,
+ "DeletePortMapping",
+ args, buffer, UPNP_COMMAND_BUFSIZE,
+ add_delete_port_mapping_receiver, cls);
+}
+
+
+struct get_specific_port_mapping_entry_cls
+{
+ const char *control_url;
+ const char *service_type;
+ const char *ext_port;
+ const char *proto;
+ UPNP_port_mapping_cb_ caller_cb;
+ void *caller_cls;
+};
+
+static void
+get_specific_port_mapping_entry_receiver (char *response, size_t received,
+ void *data)
+{
+ struct PortMapping_cls *cls = data;
+ struct UPNP_REPLY_NameValueList_ pdata;
+ char *p;
+ char in_port[128];
+ char in_client[128];
+ int ret;
+
+ UPNP_REPLY_parse_ (response, received, &pdata);
+
+ p = UPNP_REPLY_get_value_ (&pdata, "NewInternalClient");
+ if (p)
+ {
+ strncpy (in_client, p, 128);
+ in_client[127] = '\0';
+ }
+ else
+ in_client[0] = '\0';
+
+ p = UPNP_REPLY_get_value_ (&pdata, "NewInternalPort");
+ if (p)
+ {
+ strncpy (in_port, p, 6);
+ in_port[5] = '\0';
+ }
+ else
+ in_port[0] = '\0';
+
+ p = UPNP_REPLY_get_value_ (&pdata, "errorCode");
+ if (p)
+ {
+ if (p)
+ {
+ ret = UPNP_COMMAND_UNKNOWN_ERROR;
+ sscanf (p, "%d", &ret);
+ }
+#if DEBUG_UPNP
+ PRINT_UPNP_ERROR ("GetSpecificPortMappingEntry", p);
+#endif
+ }
+
+ cls->caller_cb (ret, cls->control_url, cls->service_type,
+ cls->ext_port, cls->proto, in_port, in_client,
+ cls->caller_cls);
+
+ UPNP_REPLY_free_ (&pdata);
+ GNUNET_free (response);
+ GNUNET_free (cls);
+}
+
+/* UPNP_get_specific_port_mapping_entry _ retrieves an existing port mapping
+ * the result is returned in the in_client and in_port strings
+ * please provide 128 and 6 bytes of data */
+void
+UPNP_get_specific_port_mapping_entry_ (struct GNUNET_SCHEDULER_Handle *sched,
+ const char *control_url,
+ const char *service_type,
+ const char *ext_port,
+ const char *proto,
+ UPNP_get_specific_port_mapping_entry_cb_
+ caller_cb, void *caller_cls)
+{
+ struct UPNP_Arg_ args[4];
+ struct get_specific_port_mapping_entry_cls *cls;
+ char *buffer;
+
+ if (!ext_port || !proto)
+ {
+ caller_cb (UPNP_COMMAND_INVALID_ARGS, control_url, service_type,
+ ext_port, proto, NULL, NULL, caller_cls);
+ return;
+ }
+
+ args[0].elt = "NewRemoteHost";
+ args[0].val = NULL;
+ args[1].elt = "NewExternalPort";
+ args[1].val = ext_port;
+ args[2].elt = "NewProtocol";
+ args[2].val = proto;
+ args[3].elt = NULL;
+ args[3].val = NULL;
+
+ cls = GNUNET_malloc (sizeof (struct PortMapping_cls));
+ cls->control_url = control_url;
+ cls->service_type = service_type;
+ cls->ext_port = ext_port;
+ cls->proto = proto;
+ cls->caller_cb = caller_cb;
+ cls->caller_cls = caller_cls;
+
+ buffer = GNUNET_malloc (UPNP_COMMAND_BUFSIZE);
+
+ UPNP_command_ (sched, control_url, service_type,
+ "GetSpecificPortMappingEntry",
+ args, buffer, UPNP_COMMAND_BUFSIZE,
+ get_specific_port_mapping_entry_receiver, cls);
+}
--- /dev/null
+/*
+ This file is part of GNUnet.
+ (C) 2009, 2010 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/*
+ * Code in this file is originally based on the miniupnp library.
+ * Copyright (c) 2005-2009, Thomas BERNARD. All rights reserved.
+ *
+ * Original licence:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file nat/upnp-commands.h
+ * @brief Commands to control UPnP IGD devices
+ *
+ * @author Milan Bouchet-Valat
+ */
+#ifndef UPNP_COMMANDS_H
+#define UPNP_COMMANDS_H
+
+#include "platform.h"
+#include "gnunet_scheduler_lib.h"
+
+/**
+ * Generic UPnP error codes.
+ */
+#define UPNP_COMMAND_SUCCESS (0)
+#define UPNP_COMMAND_UNKNOWN_ERROR (-1)
+#define UPNP_COMMAND_INVALID_ARGS (-2)
+
+/**
+ * Size of the buffer used to store anwsers to UPnP commands.
+ */
+#define UPNP_COMMAND_BUFSIZE 4096
+
+/**
+ * Name-value pair containing an argumeny to a UPnP command.
+ */
+struct UPNP_Arg_
+{
+ const char *elt;
+ const char *val;
+};
+
+/**
+ * Callback for UPNP_command_().
+ *
+ * @param response the buffer passed to UPNP_command_(), filled with
+ * NULL-terminated content (if any)
+ * @param received length of the content received and stored in response
+ * @param cls closure passed to UPNP_command_()
+ */
+typedef void (*UPNP_command_cb_) (char *response, size_t received, void *cls);
+
+/**
+ * Send UPnP command to the device identified by url and service.
+ *
+ * @param sched scheduler to use for network tasks
+ * @param url control URL of the device
+ * @param service type of the service corresponding to the command
+ * @param action action to send
+ * @param args arguments for action
+ * @param caller_cb user callback to trigger when done
+ * @param caller_cls closure to pass to caller_cb
+ */
+void UPNP_command_ (struct GNUNET_SCHEDULER_Handle *sched,
+ const char *url, const char *service,
+ const char *action, struct UPNP_Arg_ *args,
+ char *buffer, size_t buf_size,
+ UPNP_command_cb_ caller_cb, void *caller_cls);
+
+/**
+ * Callback to UPNP_get_external_ip_address_().
+ *
+ * Possible UPnP Errors :
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 501 Action Failed - See UPnP Device Architecture section on Control.
+ *
+ * @param error GNUNET_OK on success, another value on error (see above)
+ * @param ext_ip_addr the external IP address reported by the device (IPv4 or v6)
+ * @param cls the closure passed to UPNP_get_external_ip_address_()
+ */
+typedef void (*UPNP_get_external_ip_address_cb_) (int error,
+ char *ext_ip_addr,
+ void *cls);
+
+/**
+ * Get the IP address associated with the WAN connection of the device.
+ * See UPNP_get_external_ip_address_cb_.
+ *
+ * @param sched the scheduler to use for networking
+ * @param control_url the control URL corresponding to service_type on the device
+ * @param service_type service type to call the command on
+ * @param caller_cb function to call when done
+ * @param cls closure passed to caller_cb
+ */
+void
+UPNP_get_external_ip_address_ (struct GNUNET_SCHEDULER_Handle *sched,
+ const char *control_url,
+ const char *service_type,
+ UPNP_get_external_ip_address_cb_ caller_cb,
+ void *caller_cls);
+
+/**
+ * Callback to UPNP_add_port_mapping_() and UPNP_delete_port_mapping_().
+ *
+ * Possible UPnP Errors with UPNP_add_port_mapping_():
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 501 Action Failed - See UPnP Device Architecture section on Control.
+ * 715 WildCardNotPermittedInSrcIP - The source IP address cannot be
+ * wild-carded
+ * 716 WildCardNotPermittedInext_port - The external port cannot be wild-carded
+ * 718 ConflictInMappingEntry - The port mapping entry specified conflicts
+ * with a mapping assigned previously to another client
+ * 724 SamePortValuesRequired - Internal and External port values
+ * must be the same
+ * 725 OnlyPermanentLeasesSupported - The NAT implementation only supports
+ * permanent lease times on port mappings
+ * 726 RemoteHostOnlySupportsWildcard - RemoteHost must be a wildcard
+ * and cannot be a specific IP address or DNS name
+ * 727 ExternalPortOnlySupportsWildcard - ExternalPort must be a wildcard and
+ * cannot be a specific port value
+ *
+ * Possible UPnP Errors with UPNP_delete_port_mapping_():
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 714 NoSuchEntryInArray - The specified value does not exist in the array
+ *
+ * @param error GNUNET_OK on success, another value on error (see above)
+ * @param control_url the control URL the command was called on
+ * @param service_type service the command was called on
+ * @param in_port port on the gateway on the LAN side which was requested
+ * @param in_client address in the LAN which was requested
+ * @param proto protocol for which port mapping was requested
+ * @param remote_host remote host for which port mapping was requested
+ * @param cls the closure passed to the command function
+ */
+typedef void (*UPNP_port_mapping_cb_) (int error,
+ const char *control_url,
+ const char *service_type,
+ const char *ext_port,
+ const char *inPort, const char *proto,
+ const char *remote_host, void *cls);
+
+
+/**
+ * Request opening a port on the IGD device.
+ * (remote_host is usually NULL because IGDs don't support it.)
+ *
+ * @param sched the scheduler to use for networking
+ * @param control_url the control URL corresponding to service_type on the device
+ * @param service_type service type to call the command on
+ * @param ext_port port that should be opened on the WAN side
+ * @param in_port port on the gateway on the LAN side which should map ext_port
+ * @param in_client address in the LAN to which packets should be redirected
+ * @param proto protocol for which to request port mapping
+ * @param remote_host remote host for which to request port mapping
+ * @param caller_cb function to call when done
+ * @param cls closure passed to caller_cb
+ */
+void
+UPNP_add_port_mapping_ (struct GNUNET_SCHEDULER_Handle *sched,
+ const char *control_url, const char *service_type,
+ const char *ext_port,
+ const char *in_port,
+ const char *in_client,
+ const char *desc,
+ const char *proto, const char *remote_host,
+ UPNP_port_mapping_cb_ caller_cb, void *caller_cls);
+
+/**
+ * Request closing a a port on the IGD device that was previously opened
+ * using UPNP_add_port_mapping_(). Use the same argument values that were
+ * used when opening the port.
+ * (remote_host is usually NULL because IGDs don't support it.)
+ *
+ * @param sched the scheduler to use for networking
+ * @param control_url the control URL the command was called on
+ * @param service_type service the command was called on
+ * @param in_port port on the gateway on the LAN side which was requested
+ * @param in_client address in the LAN which was requested
+ * @param proto protocol for which port mapping was requested
+ * @param remote_host remote host for which port mapping was requested
+ * @param caller_cb function to call when done
+ * @param cls closure passed to caller_cb
+ */
+void
+UPNP_delete_port_mapping_ (struct GNUNET_SCHEDULER_Handle *sched,
+ const char *control_url, const char *service_type,
+ const char *ext_port, const char *proto,
+ const char *remote_host,
+ UPNP_port_mapping_cb_ caller_cb, void *caller_cls);
+
+
+/**
+ * Callback to UPNP_get_specific_port_mapping_entry _().
+ *
+ * @param error GNUNET_OK if port is currently mapped, another value on error
+ * @param control_url the control URL the command was called on
+ * @param service_type service the command was called on
+ * @param in_port port on the gateway on the LAN side which was requested
+ * @param in_client address in the LAN which was requested
+ * @param proto protocol for which port mapping was requested
+ * @param remote_host remote host for which port mapping was requested
+ * @param cls the closure passed to the command function
+ */
+typedef void (*UPNP_get_specific_port_mapping_entry_cb_) (int error,
+ const char
+ *control_url,
+ const char
+ *service_type,
+ const char
+ *ext_port,
+ const char *proto,
+ const char *in_port,
+ const char
+ *in_client,
+ void *cls);
+
+/**
+ * Check that a port mapping set up with UPNP_add_port_mapping_()
+ * is alive.
+ *
+ * @param sched the scheduler to use for networking
+ * @param control_url the control URL the command was called on
+ * @param service_type service the command was called on
+ * @param in_port port on the gateway on the LAN side which was requested
+ * @param in_client address in the LAN which was requested
+ * @param proto protocol for which port mapping was requested
+ * @param remote_host remote host for which port mapping was requested
+ * @param caller_cb function to call when done
+ * @param cls closure passed to caller_cb
+ */
+void
+UPNP_get_specific_port_mapping_entry_ (struct GNUNET_SCHEDULER_Handle *sched,
+ const char *control_url,
+ const char *service_type,
+ const char *ext_port,
+ const char *proto,
+ UPNP_get_specific_port_mapping_entry_cb_
+ caller_cb, void *caller_cls);
+
+#endif
--- /dev/null
+/*
+ This file is part of GNUnet.
+ (C) 2009, 2010 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/*
+ * Code in this file is originally based on the miniupnp library.
+ * Copyright (c) 2005-2009, Thomas BERNARD. All rights reserved.
+ *
+ * Original licence:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file nat/upnp-discover.c
+ * @brief Look for UPnP IGD devices
+ *
+ * @author Milan Bouchet-Valat
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <curl/curl.h>
+
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "upnp-discover.h"
+#include "upnp-reply-parse.h"
+#include "upnp-igd-parse.h"
+#include "upnp-minixml.h"
+
+#define DISCOVER_BUFSIZE 512
+#define DESCRIPTION_BUFSIZE 2048
+#define CURL_EASY_SETOPT(c, a, b) do { ret = curl_easy_setopt(c, a, b); if (ret != CURLE_OK) GNUNET_log(GNUNET_ERROR_TYPE_WARNING, _("%s failed at %s:%d: `%s'\n"), "curl_easy_setopt", __FILE__, __LINE__, curl_easy_strerror(ret)); } while (0)
+#define PRINT_SOCKET_ERROR(a) GNUNET_log_from(GNUNET_ERROR_TYPE_WARNING, "UPnP", _("%s failed at %s:%d: '%s'\n"), a, __FILE__, __LINE__, strerror (errno));
+
+
+/**
+ * Callback function called when download is finished.
+ *
+ * @param data the contents of the downloaded file, or NULL
+ * @param cls closure passed via download_device_description()
+ */
+typedef void (*download_cb) (char *data, void *cls);
+
+/**
+ * Private closure used by download_device_description() and it's callbacks.
+ */
+struct download_cls
+{
+ /**
+ * Scheduler used for the download task.
+ */
+ struct GNUNET_SCHEDULER_Handle *sched;
+
+ /**
+ * curl_easy handle.
+ */
+ CURL *curl;
+
+ /**
+ * curl_multi handle.
+ */
+ CURLM *multi;
+
+ /**
+ * URL of the file to download.
+ */
+ char *url;
+
+ /**
+ * Time corresponding to timeout wanted by the caller.
+ */
+ struct GNUNET_TIME_Absolute end_time;
+
+ /**
+ * Buffer to store downloaded content.
+ */
+ char download_buffer[DESCRIPTION_BUFSIZE];
+
+ /**
+ * Size of the already downloaded content.
+ */
+ size_t download_pos;
+
+ /**
+ * User callback to trigger when done.
+ */
+ download_cb caller_cb;
+
+ /**
+ * User closure to pass to caller_cb.
+ */
+ void *caller_cls;
+};
+
+/**
+ * Clean up the state of CURL multi handle and that of
+ * the only easy handle it uses.
+ */
+static void
+download_clean_up (struct download_cls *cls)
+{
+ CURLMcode mret;
+
+ mret = curl_multi_cleanup (cls->multi);
+ if (mret != CURLM_OK)
+ GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "UPnP",
+ _("%s failed at %s:%d: `%s'\n"),
+ "curl_multi_cleanup", __FILE__, __LINE__,
+ curl_multi_strerror (mret));
+
+ curl_easy_cleanup (cls->curl);
+ GNUNET_free (cls);
+}
+
+/**
+ * Process downloaded bits by calling callback on each HELLO.
+ *
+ * @param ptr buffer with downloaded data
+ * @param size size of a record
+ * @param nmemb number of records downloaded
+ * @param ctx closure
+ * @return number of bytes that were processed (always size*nmemb)
+ */
+static size_t
+callback_download (void *ptr, size_t size, size_t nmemb, void *ctx)
+{
+ struct download_cls *cls = ctx;
+ const char *cbuf = ptr;
+ size_t total;
+ size_t cpy;
+
+ total = size * nmemb;
+ if (total == 0)
+ return total; /* ok, no data */
+
+ cpy = GNUNET_MIN (total, DESCRIPTION_BUFSIZE - cls->download_pos - 1);
+ memcpy (&cls->download_buffer[cls->download_pos], cbuf, cpy);
+ cbuf += cpy;
+ cls->download_pos += cpy;
+
+#if DEBUG_UPNP
+ GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP",
+ "Downloaded %d records of size %d, download position: %d\n",
+ size, nmemb, cls->download_pos);
+#endif
+
+ return total;
+}
+
+static void
+task_download (struct download_cls *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc);
+
+/**
+ * Ask CURL for the select set and then schedule the
+ * receiving task with the scheduler.
+ */
+static void
+download_prepare (struct download_cls *cls)
+{
+ CURLMcode mret;
+ fd_set rs;
+ fd_set ws;
+ fd_set es;
+ int max;
+ struct GNUNET_NETWORK_FDSet *grs;
+ struct GNUNET_NETWORK_FDSet *gws;
+ long timeout;
+ struct GNUNET_TIME_Relative rtime;
+
+ max = -1;
+ FD_ZERO (&rs);
+ FD_ZERO (&ws);
+ FD_ZERO (&es);
+ mret = curl_multi_fdset (cls->multi, &rs, &ws, &es, &max);
+ if (mret != CURLM_OK)
+ {
+ GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "UPnP",
+ _("%s failed at %s:%d: `%s'\n"),
+ "curl_multi_fdset", __FILE__, __LINE__,
+ curl_multi_strerror (mret));
+ download_clean_up (cls);
+ cls->caller_cb (NULL, cls->caller_cls);
+ return;
+ }
+ mret = curl_multi_timeout (cls->multi, &timeout);
+ if (mret != CURLM_OK)
+ {
+ GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "UPnP",
+ _("%s failed at %s:%d: `%s'\n"),
+ "curl_multi_timeout", __FILE__, __LINE__,
+ curl_multi_strerror (mret));
+ download_clean_up (cls);
+ cls->caller_cb (NULL, cls->caller_cls);
+ return;
+ }
+ rtime =
+ GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining
+ (cls->end_time),
+ GNUNET_TIME_relative_multiply
+ (GNUNET_TIME_UNIT_MILLISECONDS, timeout));
+ grs = GNUNET_NETWORK_fdset_create ();
+ gws = GNUNET_NETWORK_fdset_create ();
+ GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
+ GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
+
+ GNUNET_SCHEDULER_add_select (cls->sched,
+ GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+ GNUNET_SCHEDULER_NO_TASK,
+ rtime,
+ grs,
+ gws,
+ (GNUNET_SCHEDULER_Task) & task_download, cls);
+ GNUNET_NETWORK_fdset_destroy (gws);
+ GNUNET_NETWORK_fdset_destroy (grs);
+}
+
+/**
+ * Task that is run when we are ready to receive more data from the device.
+ *
+ * @param cls closure
+ * @param tc task context
+ */
+static void
+task_download (struct download_cls *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+
+ int running;
+ struct CURLMsg *msg;
+ CURLMcode mret;
+
+ if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
+ {
+#if DEBUG_UPNP
+ GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP",
+ "Shutdown requested while trying to download device description from `%s'\n",
+ cls->url);
+#endif
+ cls->caller_cb (NULL, cls->caller_cls);
+ download_clean_up (cls);
+ return;
+ }
+ if (GNUNET_TIME_absolute_get_remaining (cls->end_time).value == 0)
+ {
+ GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "UPnP",
+ _
+ ("Timeout trying to download UPnP device description from '%s'\n"),
+ cls->url);
+ cls->caller_cb (NULL, cls->caller_cls);
+ download_clean_up (cls);
+ return;
+ }
+
+ do
+ {
+ running = 0;
+ mret = curl_multi_perform (cls->multi, &running);
+
+ if (running == 0)
+ {
+ do
+ {
+ msg = curl_multi_info_read (cls->multi, &running);
+ GNUNET_break (msg != NULL);
+ if (msg == NULL)
+ break;
+
+ if ((msg->data.result != CURLE_OK) &&
+ (msg->data.result != CURLE_GOT_NOTHING))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ _("%s failed for `%s' at %s:%d: `%s'\n"),
+ "curl_multi_perform",
+ cls->url,
+ __FILE__,
+ __LINE__,
+ curl_easy_strerror (msg->data.result));
+ cls->caller_cb (NULL, cls->caller_cls);
+ }
+ else
+ {
+ GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP",
+ _
+ ("Download of device description `%s' completed.\n"),
+ cls->url);
+ cls->caller_cb (GNUNET_strdup (cls->download_buffer),
+ cls->caller_cls);
+ }
+
+ download_clean_up (cls);
+ return;
+ }
+ while ((running > 0));
+ }
+ }
+ while (mret == CURLM_CALL_MULTI_PERFORM);
+
+ if (mret != CURLM_OK)
+ {
+ GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, "UPnP",
+ _("%s failed at %s:%d: `%s'\n"),
+ "curl_multi_perform", __FILE__, __LINE__,
+ curl_multi_strerror (mret));
+ download_clean_up (cls);
+ cls->caller_cb (NULL, cls->caller_cls);
+ }
+
+ download_prepare (cls);
+}
+
+
+/**
+ * Download description from devices.
+ *
+ * @param sched the scheduler to use for the download task
+ * @param url URL of the file to download
+ * @param caller_cb user function to call when done
+ * @caller_cls closure to pass to caller_cb
+ */
+void
+download_device_description (struct GNUNET_SCHEDULER_Handle *sched,
+ char *url, download_cb caller_cb,
+ void *caller_cls)
+{
+ CURL *curl;
+ CURLM *multi;
+ CURLcode ret;
+ CURLMcode mret;
+ struct download_cls *cls;
+
+ cls = GNUNET_malloc (sizeof (struct download_cls));
+
+ curl = curl_easy_init ();
+ if (curl == NULL)
+ goto error;
+
+ CURL_EASY_SETOPT (curl, CURLOPT_WRITEFUNCTION, &callback_download);
+ if (ret != CURLE_OK)
+ goto error;
+
+ CURL_EASY_SETOPT (curl, CURLOPT_WRITEDATA, cls);
+ if (ret != CURLE_OK)
+ goto error;
+
+ CURL_EASY_SETOPT (curl, CURLOPT_FOLLOWLOCATION, 1);
+ CURL_EASY_SETOPT (curl, CURLOPT_MAXREDIRS, 4);
+ /* no need to abort if the above failed */
+ CURL_EASY_SETOPT (curl, CURLOPT_URL, url);
+ if (ret != CURLE_OK)
+ goto error;
+
+ CURL_EASY_SETOPT (curl, CURLOPT_FAILONERROR, 1);
+ CURL_EASY_SETOPT (curl, CURLOPT_BUFFERSIZE, DESCRIPTION_BUFSIZE);
+ CURL_EASY_SETOPT (curl, CURLOPT_USERAGENT, "GNUnet");
+ CURL_EASY_SETOPT (curl, CURLOPT_CONNECTTIMEOUT, 60L);
+ CURL_EASY_SETOPT (curl, CURLOPT_TIMEOUT, 60L);
+
+ multi = curl_multi_init ();
+ if (multi == NULL)
+ {
+ GNUNET_break (0);
+ /* clean_up (); */
+ return;
+ }
+ mret = curl_multi_add_handle (multi, curl);
+ if (mret != CURLM_OK)
+ {
+ GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "UPnP",
+ _("%s failed at %s:%d: `%s'\n"),
+ "curl_multi_add_handle", __FILE__, __LINE__,
+ curl_multi_strerror (mret));
+ mret = curl_multi_cleanup (multi);
+ if (mret != CURLM_OK)
+ GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "UPnP",
+ _("%s failed at %s:%d: `%s'\n"),
+ "curl_multi_cleanup", __FILE__, __LINE__,
+ curl_multi_strerror (mret));
+ goto error;
+ return;
+ }
+
+#if DEBUG_UPNP
+ GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP",
+ "Preparing to download device description from '%s'\n",
+ url);
+#endif
+
+ cls->sched = sched;
+ cls->curl = curl;
+ cls->multi = multi;
+ cls->url = url;
+ cls->end_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES);
+ memset (cls->download_buffer, 0, DESCRIPTION_BUFSIZE);
+ cls->download_pos = 0;
+ cls->caller_cb = caller_cb;
+ cls->caller_cls = caller_cls;
+ download_prepare (cls);
+ return;
+
+
+error:
+ GNUNET_break (0);
+ GNUNET_free (cls);
+ curl_easy_cleanup (curl);
+ caller_cb (NULL, caller_cls);
+}
+
+/**
+ * Parse SSDP packet received in reply to a M-SEARCH message.
+ *
+ * @param reply contents of the packet
+ * @param size length of reply
+ * @param location address of a pointer that will be set to the start
+ * of the "location" field
+ * @param location_size pointer where to store the length of the "location" field
+ * @param st pointer address of a pointer that will be set to the start
+ * of the "st" (search target) field
+ * @param st_size pointer where to store the length of the "st" field
+ * The strings are NOT null terminated */
+static void
+parse_msearch_reply (const char *reply, int size,
+ const char **location, int *location_size,
+ const char **st, int *st_size)
+{
+ int a, b, i;
+
+ i = 0;
+ b = 0;
+ /* Start of the line */
+ a = i;
+
+ while (i < size)
+ {
+ switch (reply[i])
+ {
+ case ':':
+ if (b == 0)
+ /* End of the "header" */
+ b = i;
+ break;
+ case '\x0a':
+ case '\x0d':
+ if (b != 0)
+ {
+ do
+ {
+ b++;
+ }
+ while (reply[b] == ' ');
+
+ if (0 == strncasecmp (reply + a, "location", 8))
+ {
+ *location = reply + b;
+ *location_size = i - b;
+ }
+ else if (0 == strncasecmp (reply + a, "st", 2))
+ {
+ *st = reply + b;
+ *st_size = i - b;
+ }
+
+ b = 0;
+ }
+
+ a = i + 1;
+ break;
+ default:
+ break;
+ }
+
+ i++;
+ }
+}
+
+/**
+ * Standard port for UPnP discovery (SSDP protocol).
+ */
+#define PORT 1900
+
+/**
+ * Convert a constant integer into a string.
+ */
+#define XSTR(s) STR(s)
+#define STR(s) #s
+
+/**
+ * Standard IPv4 multicast adress for UPnP discovery (SSDP protocol).
+ */
+#define UPNP_MCAST_ADDR "239.255.255.250"
+
+/**
+ * Standard IPv6 multicast adress for UPnP discovery (SSDP protocol).
+ */
+#define UPNP_MCAST_ADDR6 "FF02:0:0:0:0:0:0:F"
+
+/**
+ * Size of the buffer needed to store SSDP requests we send.
+ */
+#define UPNP_DISCOVER_BUFSIZE 1536
+
+/**
+ * Description of a UPnP device containing everything
+ * we may need to control it.
+ *
+ * Meant to be member of a chained list.
+ */
+struct UPNP_Dev_
+{
+ /**
+ * Next device in the list, if any.
+ */
+ struct UPNP_Dev_ *pNext;
+
+ /**
+ * Path to the file describing the device.
+ */
+ char *desc_url;
+
+ /**
+ * UPnP search target.
+ */
+ char *st;
+
+ /**
+ * Service type associated with the control_url for the device.
+ */
+ char *service_type;
+
+ /**
+ * URL to send commands to.
+ */
+ char *control_url;
+
+ /**
+ * Whether the device is currently connected to the WAN.
+ */
+ int is_connected;
+
+ /**
+ * IGD Data associated with the device.
+ */
+ struct UPNP_IGD_Data_ *data;
+};
+
+/**
+ * Private closure used by UPNP_discover() and its callbacks.
+ */
+struct UPNP_discover_cls
+{
+ /**
+ * Scheduler to use for networking tasks.
+ */
+ struct GNUNET_SCHEDULER_Handle *sched;
+
+ /**
+ * Remote address used for multicast emission and reception.
+ */
+ struct sockaddr *multicast_addr;
+
+ /**
+ * Network handle used to send and receive discovery messages.
+ */
+ struct GNUNET_NETWORK_Handle *sudp;
+
+ /**
+ * fdset used with sudp.
+ */
+ struct GNUNET_NETWORK_FDSet *fdset;
+
+ /**
+ * Connection handle used to download device description.
+ */
+ struct GNUNET_CONNECTION_Handle *s;
+
+ /**
+ * Transmission handle used with s.
+ */
+ struct GNUNET_CONNECTION_TransmitHandle *th;
+
+ /**
+ * Index of the UPnP device type we're currently sending discovery messages to.
+ */
+ int type_index;
+
+ /**
+ * List of discovered devices.
+ */
+ struct UPNP_Dev_ *dev_list;
+
+ /**
+ * Device we're currently fetching description from.
+ */
+ struct UPNP_Dev_ *current_dev;
+
+ /**
+ * User callback to trigger when done.
+ */
+ UPNP_discover_cb_ caller_cb;
+
+ /**
+ * Closure passed to caller_cb.
+ */
+ void *caller_cls;
+};
+
+/**
+ * Check that raw_url is absolute, and if not, use ref_url to resolve it:
+ * if is_desc_file is GNUNET_YES, the path to the parent of the file is used;
+ * if it is GNUNET_NO, ref_url will be considered as the base URL for raw URL.
+ *
+ * @param ref_url base URL for the device
+ * @param is_desc_file whether ref_url is a path to the description file
+ * @param raw_url a possibly relative URL
+ * @returns a new string with an absolute URL
+ */
+static char *
+get_absolute_url (const char *ref_url, int is_desc_file, const char *raw_url)
+{
+ char *final_url;
+
+ if ((raw_url[0] == 'h')
+ && (raw_url[1] == 't')
+ && (raw_url[2] == 't')
+ && (raw_url[3] == 'p')
+ && (raw_url[4] == ':') && (raw_url[5] == '/') && (raw_url[6] == '/'))
+ {
+ final_url = GNUNET_strdup (raw_url);
+ }
+ else
+ {
+ int n = strlen (raw_url);
+ int l = strlen (ref_url);
+ int cpy_len = l;
+ char *slash;
+
+ /* If base URL is a path to the description file, go one level higher */
+ if (is_desc_file == GNUNET_YES)
+ {
+ slash = strrchr (ref_url, '/');
+ cpy_len = slash - ref_url;
+ }
+
+ final_url = GNUNET_malloc (l + n + 1);
+
+ /* Add trailing slash to base URL if needed */
+ if (raw_url[0] != '/' && ref_url[cpy_len] != '\0')
+ final_url[cpy_len++] = '/';
+
+ strncpy (final_url, ref_url, cpy_len);
+ strcpy (final_url + cpy_len, raw_url);
+ final_url[cpy_len + n] = '\0';
+ }
+
+ return final_url;
+}
+
+
+/**
+ * Construct control URL for device from its description URL and
+ * UPNP_IGD_Data_ information. This involves resolving relative paths
+ * and choosing between Common Interface Config and interface-specific
+ * paths.
+ *
+ * @param desc_url URL to the description file of the device
+ * @param data IGD information obtained from the description file
+ * @returns a URL to control the IGD device, or the empty string
+ * in case of failure
+ */
+static char *
+format_control_urls (const char *desc_url, struct UPNP_IGD_Data_ *data)
+{
+ const char *ref_url;
+ int is_desc_file;
+
+ if (data->base_url[0] != '\0')
+ {
+ ref_url = data->base_url;
+ is_desc_file = GNUNET_NO;
+ }
+ else
+ {
+ ref_url = desc_url;
+ is_desc_file = GNUNET_YES;
+ }
+
+ if (data->control_url[0] != '\0')
+ return get_absolute_url (ref_url, is_desc_file, data->control_url);
+ else if (data->control_url_CIF[0] != '\0')
+ return get_absolute_url (ref_url, is_desc_file, data->control_url_CIF);
+ else
+ return GNUNET_strdup ("");
+}
+
+static void get_valid_igd (struct UPNP_discover_cls *cls);
+
+/**
+ * Called when "GetStatusInfo" command finishes. Check whether IGD device reports
+ * to be currently connected or not.
+ *
+ * @param response content of the UPnP message answered by the device
+ * @param received number of received bytes stored in response
+ * @param data closure from UPNP_discover()
+ */
+static void
+get_valid_igd_connected_cb (char *response, size_t received, void *data)
+{
+ struct UPNP_discover_cls *cls = data;
+ struct UPNP_REPLY_NameValueList_ pdata;
+ char *status;
+ char *error;
+
+ UPNP_REPLY_parse_ (response, received, &pdata);
+
+ status = UPNP_REPLY_get_value_ (&pdata, "NewConnectionStatus");
+ error = UPNP_REPLY_get_value_ (&pdata, "errorCode");
+
+ if (status)
+ cls->current_dev->is_connected = (strcmp ("Connected", status) == 0);
+ else
+ cls->current_dev->is_connected = GNUNET_NO;
+
+ if (error)
+ GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "UPnP",
+ _("Could not get UPnP device status: error %s\n"),
+ error);
+
+ GNUNET_free (response);
+ UPNP_REPLY_free_ (&pdata);
+
+ /* Go on to next device, or finish discovery process */
+ cls->current_dev = cls->current_dev->pNext;
+ get_valid_igd (cls);
+}
+
+/**
+ * Receive contents of the downloaded UPnP IGD description file,
+ * and fill UPNP_Dev_ and UPNP_IGD_Data_ structs with this data.
+ * Then, schedule UPnP command to check whether device is connected.
+ *
+ * @param desc UPnP IGD description (in XML)
+ * @data closure from UPNP_discover()
+ */
+static void
+get_valid_igd_receive (char *desc, void *data)
+{
+ struct UPNP_discover_cls *cls = data;
+ struct UPNP_IGD_Data_ *igd_data;
+ char *buffer;
+
+ if (!desc || strlen (desc) == 0)
+ {
+ GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "UPnP",
+ "Error getting IGD XML description at %s:%d\n",
+ __FILE__, __LINE__);
+
+ /* Skip device */
+ cls->current_dev->data = NULL;
+ cls->current_dev->is_connected = GNUNET_NO;
+ get_valid_igd (cls);
+ }
+
+ igd_data = GNUNET_malloc (sizeof (struct UPNP_IGD_Data_));
+ memset (igd_data, 0, sizeof (struct UPNP_IGD_Data_));
+ UPNP_IGD_parse_desc_ (desc, strlen (desc), igd_data);
+
+ cls->current_dev->control_url =
+ format_control_urls (cls->current_dev->desc_url, igd_data);
+
+ if (igd_data->service_type != '\0')
+ cls->current_dev->service_type = GNUNET_strdup (igd_data->service_type);
+ else if (igd_data->service_type_CIF != '\0')
+ cls->current_dev->service_type =
+ GNUNET_strdup (igd_data->service_type_CIF);
+ else
+ cls->current_dev->service_type = GNUNET_strdup ("");
+
+ cls->current_dev->data = igd_data;
+
+ /* Check whether device is connected */
+ buffer = GNUNET_malloc (UPNP_COMMAND_BUFSIZE);
+ UPNP_command_ (cls->sched,
+ cls->current_dev->control_url,
+ cls->current_dev->data->service_type,
+ "GetStatusInfo", NULL, buffer, UPNP_COMMAND_BUFSIZE,
+ get_valid_igd_connected_cb, cls);
+
+ GNUNET_free (desc);
+}
+
+/**
+ * Free a chained list of UPnP devices.
+ */
+static void
+free_dev_list (struct UPNP_Dev_ *devlist)
+{
+ struct UPNP_Dev_ *next;
+
+ while (devlist)
+ {
+ next = devlist->pNext;
+ GNUNET_free (devlist->control_url);
+ GNUNET_free (devlist->service_type);
+ GNUNET_free (devlist->desc_url);
+ GNUNET_free (devlist->data);
+ GNUNET_free (devlist->st);
+ GNUNET_free (devlist);
+ devlist = next;
+ }
+}
+
+/**
+ * Walk over the list of found devices looking for a connected IGD,
+ * if present, or at least a disconnected one.
+ */
+static void
+get_valid_igd (struct UPNP_discover_cls *cls)
+{
+ struct UPNP_Dev_ *dev;
+ int step;
+
+ /* No device was discovered */
+ if (!cls->dev_list)
+ {
+ cls->caller_cb (NULL, NULL, cls->caller_cls);
+
+ GNUNET_free (cls);
+ return;
+ }
+ /* We already walked over all devices, see what we got,
+ * and return the device with the best state we have. */
+ else if (cls->current_dev == NULL)
+ {
+ for (step = 1; step <= 3; step++)
+ {
+ for (dev = cls->dev_list; dev; dev = dev->pNext)
+ {
+#if DEBUG_UPNP
+ GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP",
+ "Found device: control_url: %s, service_type: %s\n",
+ dev->control_url, dev->service_type);
+#endif
+ /* Accept connected IGDs on step 1, non-connected IGDs
+ * on step 2, and other device types on step 3. */
+ if ((step == 1 && dev->is_connected)
+ || (step < 3 && 0 != strcmp (dev->service_type,
+ "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1")))
+ continue;
+
+ cls->caller_cb (dev->control_url,
+ dev->service_type, cls->caller_cls);
+
+ free_dev_list (cls->dev_list);
+ GNUNET_free (cls);
+ return;
+ }
+ }
+
+ /* We cannot reach this... */
+ GNUNET_assert (GNUNET_NO);
+ }
+
+ /* There are still devices to ask, go on */
+ download_device_description (cls->sched, cls->current_dev->desc_url,
+ get_valid_igd_receive, cls);
+}
+
+static const char *const discover_type_list[] = {
+ "urn:schemas-upnp-org:device:InternetGatewayDevice:1",
+ "urn:schemas-upnp-org:service:WANIPConnection:1",
+ "urn:schemas-upnp-org:service:WANPPPConnection:1",
+ "upnp:rootdevice",
+ NULL
+};
+
+static void
+discover_send (void *data, const struct GNUNET_SCHEDULER_TaskContext *tc);
+
+/**
+ * Handle response from device. Stop when all device types have been tried,
+ * and get their descriptions.
+ *
+ * @param data closure from UPNP_discover()
+ * @buf content of the reply
+ * @available number of bytes stored in buf
+ * @addr address of the sender
+ * @addrlen size of addr
+ * @param errCode value of errno
+ */
+static void
+discover_recv (void *data, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct UPNP_discover_cls *cls = data;
+ GNUNET_SCHEDULER_TaskIdentifier task_w;
+ struct UPNP_Dev_ *tmp;
+ socklen_t addrlen;
+ ssize_t received;
+ char buf[DISCOVER_BUFSIZE];
+ const char *desc_url = NULL;
+ int urlsize = 0;
+ const char *st = NULL;
+ int stsize = 0;
+
+ /* Free fdset that was used for this sned/receive operation */
+ GNUNET_NETWORK_fdset_destroy (cls->fdset);
+
+ if (cls->multicast_addr->sa_family == AF_INET)
+ addrlen = sizeof (struct sockaddr_in);
+ else
+ addrlen = sizeof (struct sockaddr_in6);
+
+ errno = 0;
+ received =
+ GNUNET_NETWORK_socket_recvfrom (cls->sudp, &buf, DISCOVER_BUFSIZE - 1,
+ (struct sockaddr *) cls->multicast_addr,
+ &addrlen);
+ if (received == GNUNET_SYSERR)
+ {
+ PRINT_SOCKET_ERROR ("GNUNET_NETWORK_socket_recvfrom");
+ }
+#if DEBUG_UPNP
+ else
+ {
+ GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP",
+ "Received %d bytes from %s\n", received,
+ GNUNET_a2s (cls->multicast_addr, addrlen));
+ }
+#endif
+
+ parse_msearch_reply (buf, received, &desc_url, &urlsize, &st, &stsize);
+
+ if (st && desc_url)
+ {
+ tmp = (struct UPNP_Dev_ *) GNUNET_malloc (sizeof (struct UPNP_Dev_));
+ tmp->pNext = cls->dev_list;
+
+ tmp->desc_url = GNUNET_malloc (urlsize + 1);
+ strncpy (tmp->desc_url, desc_url, urlsize);
+ tmp->desc_url[urlsize] = '\0';
+
+ tmp->st = GNUNET_malloc (stsize + 1);
+ strncpy (tmp->st, st, stsize);
+ tmp->st[stsize] = '\0';
+ cls->dev_list = tmp;
+#if DEBUG_UPNP
+ GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP",
+ "Found device %s when looking for type %s\n",
+ tmp->desc_url, tmp->st);
+#endif
+ }
+
+ /* Continue discovery until all types of devices have been tried */
+ if (discover_type_list[cls->type_index])
+ {
+ /* Send queries for each device type and wait for a possible reply.
+ * receiver callback takes care of trying another device type,
+ * and eventually calls the caller's callback. */
+ cls->fdset = GNUNET_NETWORK_fdset_create ();
+ GNUNET_NETWORK_fdset_zero (cls->fdset);
+ GNUNET_NETWORK_fdset_set (cls->fdset, cls->sudp);
+
+ task_w = GNUNET_SCHEDULER_add_select (cls->sched,
+ GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+ GNUNET_SCHEDULER_NO_TASK,
+ GNUNET_TIME_relative_multiply
+ (GNUNET_TIME_UNIT_SECONDS, 15),
+ NULL, cls->fdset, &discover_send,
+ cls);
+
+ GNUNET_SCHEDULER_add_select (cls->sched,
+ GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+ task_w,
+ GNUNET_TIME_relative_multiply
+ (GNUNET_TIME_UNIT_SECONDS, 5), cls->fdset,
+ NULL, &discover_recv, cls);
+ }
+ else
+ {
+ GNUNET_NETWORK_socket_close (cls->sudp);
+ GNUNET_free (cls->multicast_addr);
+ cls->current_dev = cls->dev_list;
+ get_valid_igd (cls);
+ }
+}
+
+/**
+ * Send the SSDP M-SEARCH packet.
+ *
+ * @param data closure from UPNP_discover()
+ * @param tc task context
+ */
+static void
+discover_send (void *data, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct UPNP_discover_cls *cls = data;
+ socklen_t addrlen;
+ ssize_t n, sent;
+ char buf[DISCOVER_BUFSIZE];
+ static const char msearch_msg[] =
+ "M-SEARCH * HTTP/1.1\r\n"
+ "HOST: " UPNP_MCAST_ADDR ":" XSTR (PORT) "\r\n"
+ "ST: %s\r\n" "MAN: \"ssdp:discover\"\r\n" "MX: 3\r\n" "\r\n";
+
+ if (cls->multicast_addr->sa_family == AF_INET)
+ addrlen = sizeof (struct sockaddr_in);
+ else
+ addrlen = sizeof (struct sockaddr_in6);
+
+ n =
+ snprintf (buf, DISCOVER_BUFSIZE, msearch_msg,
+ discover_type_list[cls->type_index++]);
+
+ errno = 0;
+ sent = GNUNET_NETWORK_socket_sendto (cls->sudp, buf, n,
+ (struct sockaddr *)
+ cls->multicast_addr, addrlen);
+ if (sent == GNUNET_SYSERR)
+ {
+ PRINT_SOCKET_ERROR ("GNUNET_NETWORK_socket_sendto");
+ }
+ else if (sent < n)
+ {
+ GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP",
+ "Could only send %d bytes to %s, needed %d bytes\n",
+ sent, GNUNET_a2s (cls->multicast_addr, addrlen), n);
+ }
+#if DEBUG_UPNP
+ else
+ {
+ GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP",
+ "Sent %d bytes to %s\n", sent,
+ GNUNET_a2s (cls->multicast_addr, addrlen));
+ }
+#endif
+}
+
+/**
+ * Search for UPnP Internet Gateway Devices (IGD) on a given network interface.
+ * If several devices are found, a device that is connected to the WAN
+ * is returned first (if any).
+ *
+ * @param sched scheduler to use for network tasks
+ * @param multicastif network interface to send discovery messages, or NULL
+ * @param addr address used to send messages on multicastif, or NULL
+ * @param caller_cb user function to call when done
+ * @param caller_cls closure to pass to caller_cb
+ */
+void
+UPNP_discover_ (struct GNUNET_SCHEDULER_Handle *sched,
+ const char *multicastif,
+ const struct sockaddr *addr,
+ UPNP_discover_cb_ caller_cb, void *caller_cls)
+{
+ int opt = 1;
+ int domain = PF_INET;
+ int if_index;
+ struct in6_addr any_addr = IN6ADDR_ANY_INIT;
+ struct sockaddr_in sockudp_r, sockudp_w;
+ struct sockaddr_in6 sockudp6_r, sockudp6_w;
+ GNUNET_SCHEDULER_TaskIdentifier task_w;
+ struct GNUNET_NETWORK_Handle *sudp;
+ struct UPNP_discover_cls *cls;
+
+
+ if (addr && addr->sa_family == AF_INET)
+ {
+ domain = PF_INET;
+ }
+ else if (addr && addr->sa_family == AF_INET6)
+ {
+ domain = PF_INET6;
+ }
+ else if (addr)
+ {
+ GNUNET_break (0);
+ caller_cb (NULL, NULL, caller_cls);
+ return;
+ }
+
+ errno = 0;
+ sudp = GNUNET_NETWORK_socket_create (domain, SOCK_DGRAM, 0);
+
+ if (sudp == NULL)
+ {
+ PRINT_SOCKET_ERROR ("GNUNET_NETWORK_socket_create");
+ caller_cb (NULL, NULL, caller_cls);
+ return;
+ }
+
+
+ cls = GNUNET_malloc (sizeof (struct UPNP_discover_cls));
+ cls->sched = sched;
+ cls->sudp = sudp;
+ cls->type_index = 0;
+ cls->dev_list = NULL;
+ cls->current_dev = NULL;
+ cls->caller_cb = caller_cb;
+ cls->caller_cls = caller_cls;
+
+
+ if (domain == PF_INET)
+ {
+ /* receive */
+ memset (&sockudp_r, 0, sizeof (struct sockaddr_in));
+ sockudp_r.sin_family = AF_INET;
+#ifdef HAVE_SOCKADDR_IN_SIN_LEN
+ sockudp_r.sin_len = sizeof (struct sockaddr_in);
+#endif
+ sockudp_r.sin_port = 0;
+ sockudp_r.sin_addr.s_addr = INADDR_ANY;
+
+ /* send */
+ memset (&sockudp_w, 0, sizeof (struct sockaddr_in));
+ sockudp_w.sin_family = AF_INET;
+ sockudp_w.sin_port = htons (PORT);
+ sockudp_w.sin_addr.s_addr = inet_addr (UPNP_MCAST_ADDR);
+#ifdef HAVE_SOCKADDR_IN_SIN_LEN
+ sockudp_w.sin_len = sizeof (struct sockaddr_in);
+#endif
+
+ cls->multicast_addr = GNUNET_malloc (sizeof (struct sockaddr_in));
+ memcpy (cls->multicast_addr, &sockudp_w, sizeof (struct sockaddr_in));
+ }
+ else
+ {
+ /* receive */
+ memcpy (&sockudp6_r, addr, sizeof (struct sockaddr_in6));
+ sockudp6_r.sin6_port = 0;
+ sockudp6_r.sin6_addr = any_addr;
+#ifdef HAVE_SOCKADDR_IN_SIN_LEN
+ sockudp6_r.sin6_len = sizeof (struct sockaddr_in6);
+#endif
+
+ /* send */
+ memset (&sockudp6_w, 0, sizeof (struct sockaddr_in6));
+ sockudp6_w.sin6_family = AF_INET6;
+ sockudp6_w.sin6_port = htons (PORT);
+ if (inet_pton (AF_INET6, UPNP_MCAST_ADDR6, &sockudp6_w.sin6_addr) != 1)
+ {
+ PRINT_SOCKET_ERROR ("inet_pton");
+ caller_cb (NULL, NULL, caller_cls);
+ return;
+ }
+#ifdef HAVE_SOCKADDR_IN_SIN_LEN
+ sockudp6_w.sin6_len = sizeof (struct sockaddr_in6);
+#endif
+
+ cls->multicast_addr = GNUNET_malloc (sizeof (struct sockaddr_in6));
+ memcpy (cls->multicast_addr, &sockudp6_w, sizeof (struct sockaddr_in6));
+ }
+
+ if (GNUNET_NETWORK_socket_setsockopt
+ (sudp, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)) == GNUNET_SYSERR)
+ {
+ PRINT_SOCKET_ERROR ("GNUNET_NETWORK_socket_setsockopt");
+ GNUNET_NETWORK_socket_close (sudp);
+ caller_cb (NULL, NULL, caller_cls);
+ return;
+ }
+
+ if (addr)
+ {
+ if (domain == PF_INET)
+ {
+ sockudp_r.sin_addr.s_addr =
+ ((struct sockaddr_in *) addr)->sin_addr.s_addr;
+ if (GNUNET_NETWORK_socket_setsockopt
+ (sudp, IPPROTO_IP, IP_MULTICAST_IF,
+ (const char *) &sockudp_r.sin_addr,
+ sizeof (struct in_addr)) == GNUNET_SYSERR)
+ {
+ PRINT_SOCKET_ERROR ("GNUNET_NETWORK_socket_setsockopt");
+ }
+ }
+ else
+ {
+ if (multicastif)
+ {
+ if_index = if_nametoindex (multicastif);
+ if (!if_index)
+ PRINT_SOCKET_ERROR ("if_nametoindex");
+
+ if (GNUNET_NETWORK_socket_setsockopt
+ (sudp, IPPROTO_IPV6, IPV6_MULTICAST_IF, &if_index,
+ sizeof (if_index)) == GNUNET_SYSERR)
+ {
+ PRINT_SOCKET_ERROR ("GNUNET_NETWORK_socket_setsockopt");
+ }
+ }
+
+ memcpy (&sockudp6_r.sin6_addr,
+ &((struct sockaddr_in6 *) addr)->sin6_addr,
+ sizeof (sockudp6_r.sin6_addr));
+ }
+ }
+
+ if (domain == PF_INET)
+ {
+ /* Bind to receive response before sending packet */
+ if (GNUNET_NETWORK_socket_bind
+ (sudp, (struct sockaddr *) &sockudp_r,
+ sizeof (struct sockaddr_in)) != GNUNET_OK)
+ {
+ PRINT_SOCKET_ERROR ("GNUNET_NETWORK_socket_bind");
+ GNUNET_NETWORK_socket_close (sudp);
+ GNUNET_free (cls->multicast_addr);
+ caller_cb (NULL, NULL, caller_cls);
+ return;
+ }
+ }
+ else
+ {
+ /* Bind to receive response before sending packet */
+ if (GNUNET_NETWORK_socket_bind
+ (sudp, (struct sockaddr *) &sockudp6_r,
+ sizeof (struct sockaddr_in6)) != GNUNET_OK)
+ {
+ PRINT_SOCKET_ERROR ("GNUNET_NETWORK_socket_bind");
+ GNUNET_free (cls->multicast_addr);
+ GNUNET_NETWORK_socket_close (sudp);
+ caller_cb (NULL, NULL, caller_cls);
+ return;
+ }
+ }
+
+ /* Send queries for each device type and wait for a possible reply.
+ * receiver callback takes care of trying another device type,
+ * and eventually calls the caller's callback. */
+ cls->fdset = GNUNET_NETWORK_fdset_create ();
+ GNUNET_NETWORK_fdset_zero (cls->fdset);
+ GNUNET_NETWORK_fdset_set (cls->fdset, sudp);
+
+ task_w = GNUNET_SCHEDULER_add_select (sched,
+ GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+ GNUNET_SCHEDULER_NO_TASK,
+ GNUNET_TIME_relative_multiply
+ (GNUNET_TIME_UNIT_SECONDS, 15), NULL,
+ cls->fdset, &discover_send, cls);
+
+ GNUNET_SCHEDULER_add_select (sched,
+ GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+ task_w,
+ GNUNET_TIME_relative_multiply
+ (GNUNET_TIME_UNIT_SECONDS, 15), cls->fdset,
+ NULL, &discover_recv, cls);
+}
--- /dev/null
+/*
+ This file is part of GNUnet.
+ (C) 2009, 2010 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/*
+ * Code in this file is originally based on the miniupnp library.
+ * Copyright (c) 2005-2009, Thomas BERNARD. All rights reserved.
+ *
+ * Original licence:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file nat/upnp-discover.h
+ * @brief Look for UPnP IGD devices
+ *
+ * @author Milan Bouchet-Valat
+ */
+#ifndef UPNPC_H
+#define UPNPC_H
+
+#include "platform.h"
+#include "gnunet_common.h"
+#include "gnunet_util_lib.h"
+#include "upnp-commands.h"
+
+typedef void (*UPNP_discover_cb_) (const char *control_urls,
+ const char *service_type, void *cls);
+
+/**
+ * Search for UPnP Internet Gateway Devices (IGD) on a given network interface.
+ * If several devices are found, a device that is connected to the WAN
+ * is returned first (if any).
+ *
+ * @param sched scheduler to use for network tasks
+ * @param multicastif network interface to send discovery messages, or NULL
+ * @param addr address used to send messages on multicastif, or NULL
+ * @param caller_cb user function to call when done
+ * @param caller_cls closure to pass to caller_cb
+ */
+void UPNP_discover_ (struct GNUNET_SCHEDULER_Handle *sched,
+ const char *multicastif,
+ const struct sockaddr *addr,
+ UPNP_discover_cb_ caller_cb, void *caller_cls);
+
+#endif
--- /dev/null
+/*
+ This file is part of GNUnet.
+ (C) 2009, 2010 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/*
+ * Code in this file is originally based on the miniupnp library.
+ * Copyright (c) 2005-2008, Thomas BERNARD. All rights reserved.
+ *
+ * Original licence:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file nat/upnp-igd-parse.h
+ * @brief Parser for XML descriptions of UPnP Internet Gateway Devices
+ *
+ * @author Milan Bouchet-Valat
+ */
+#include <stdio.h>
+#include <string.h>
+
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "upnp-minixml.h"
+#include "upnp-igd-parse.h"
+
+/**
+ * Start element handler: update nesting level counter
+ * and copy element name.
+ */
+static void
+start_elt (void *d, const char *name, int l)
+{
+ struct UPNP_IGD_Data_ *datas = (struct UPNP_IGD_Data_ *) d;
+
+ memcpy (datas->cur_elt_name, name, l);
+ datas->cur_elt_name[l] = '\0';
+ datas->level++;
+ if ((l == 7) && !memcmp (name, "service", l))
+ {
+ datas->control_url_tmp[0] = '\0';
+ datas->event_sub_url_tmp[0] = '\0';
+ datas->scpd_url_tmp[0] = '\0';
+ datas->service_type_tmp[0] = '\0';
+ }
+}
+
+/**
+ * End element handler: update nesting level counter
+ * and update parser state if service element is parsed.
+ */
+static void
+end_elt (void *d, const char *name, int l)
+{
+ struct UPNP_IGD_Data_ *datas = (struct UPNP_IGD_Data_ *) d;
+
+ datas->level--;
+
+ if ((l == 7) && !memcmp (name, "service", l))
+ {
+ if (0 == strcmp (datas->service_type_tmp,
+ "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1"))
+ {
+ memcpy (datas->control_url_CIF, datas->control_url_tmp,
+ MINIUPNPC_URL_MAXSIZE);
+ memcpy (datas->event_sub_url_CIF, datas->event_sub_url_tmp,
+ MINIUPNPC_URL_MAXSIZE);
+ memcpy (datas->scpd_url_CIF, datas->scpd_url_tmp,
+ MINIUPNPC_URL_MAXSIZE);
+ memcpy (datas->service_type_CIF, datas->service_type_tmp,
+ MINIUPNPC_URL_MAXSIZE);
+ }
+ else if (0 == strcmp (datas->service_type_tmp,
+ "urn:schemas-upnp-org:service:WANIPConnection:1")
+ || 0 == strcmp (datas->service_type_tmp,
+ "urn:schemas-upnp-org:service:WANPPPConnection:1"))
+ {
+ memcpy (datas->control_url, datas->control_url_tmp,
+ MINIUPNPC_URL_MAXSIZE);
+ memcpy (datas->event_sub_url, datas->event_sub_url_tmp,
+ MINIUPNPC_URL_MAXSIZE);
+ memcpy (datas->scpd_url, datas->scpd_url_tmp,
+ MINIUPNPC_URL_MAXSIZE);
+ memcpy (datas->service_type, datas->service_type_tmp,
+ MINIUPNPC_URL_MAXSIZE);
+ }
+ }
+}
+
+/**
+ * Data handler: copy data depending on the current
+ * element name and state.
+ */
+static void
+IGDdata (void *d, const char *data, int l)
+{
+ struct UPNP_IGD_Data_ *datas = (struct UPNP_IGD_Data_ *) d;
+ char *dstmember = NULL;
+
+ if (!strcmp (datas->cur_elt_name, "URLBase"))
+ dstmember = datas->base_url;
+ else if (!strcmp (datas->cur_elt_name, "serviceType"))
+ dstmember = datas->service_type_tmp;
+ else if (!strcmp (datas->cur_elt_name, "controlURL"))
+ dstmember = datas->control_url_tmp;
+ else if (!strcmp (datas->cur_elt_name, "eventSubURL"))
+ dstmember = datas->event_sub_url_tmp;
+ else if (!strcmp (datas->cur_elt_name, "SCPDURL"))
+ dstmember = datas->scpd_url_tmp;
+
+ /* Copy current element name into destination member */
+ if (dstmember)
+ {
+ if (l >= MINIUPNPC_URL_MAXSIZE)
+ l = MINIUPNPC_URL_MAXSIZE - 1;
+
+ memcpy (dstmember, data, l);
+ dstmember[l] = '\0';
+ }
+}
+
+#ifdef DEBUG_UPNP
+static void
+print_IGD (struct UPNP_IGD_Data_ *d)
+{
+ GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP",
+ "base_url = %s\n", d->base_url);
+ GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP",
+ "WAN Device (Common interface config) :\n"
+ " sevice_type = %s\n"
+ " control_url = %s\n"
+ " event_sub_url = %s\n"
+ " scpd_url = %s\n",
+ d->service_type_CIF,
+ d->control_url_CIF, d->event_sub_url_CIF, d->scpd_url_CIF);
+ GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP",
+ "WAN Connection Device (IP or PPP Connection):\n"
+ " service_type = %s\n"
+ " control_url = %s\n"
+ " event_sub_url = %s\n"
+ " scpd_url = %s\n",
+ d->service_type,
+ d->control_url, d->event_sub_url, d->scpd_url);
+}
+#endif
+
+/**
+ * Parse XML description of an IGD device into a UPNP_IGD_Data_ struct.
+ */
+void
+UPNP_IGD_parse_desc_ (const char *buffer, int buf_size,
+ struct UPNP_IGD_Data_ *data)
+{
+ struct UPNP_xml_parser_ parser;
+
+ parser.xml_start = buffer;
+ parser.xml_size = buf_size;
+ parser.cls = data;
+ parser.start_elt_func = start_elt;
+ parser.end_elt_func = end_elt;
+ parser.data_func = IGDdata;
+ parser.att_func = 0;
+
+ UPNP_parse_xml_ (&parser);
+
+#ifdef DEBUG_UPNP
+ print_IGD (data);
+#endif
+}
--- /dev/null
+/*
+ This file is part of GNUnet.
+ (C) 2009, 2010 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/*
+ * Code in this file is originally based on the miniupnp library.
+ * Copyright (c) 2005-2008, Thomas BERNARD. All rights reserved.
+ *
+ * Original licence:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file nat/upnp-igd-parse.h
+ * @brief Parser for XML descriptions of UPnP Internet Gateway Devices
+ *
+ * @author Milan Bouchet-Valat
+ */
+#ifndef UPNP_IGD_PARSE_H
+#define UPNP_IGD_PARSE_H
+
+#define MINIUPNPC_URL_MAXSIZE (128)
+
+/**
+ * Structure to store the result of the parsing of UPnP
+ * descriptions of Internet Gateway Devices.
+ */
+struct UPNP_IGD_Data_
+{
+ char cur_elt_name[MINIUPNPC_URL_MAXSIZE];
+ char base_url[MINIUPNPC_URL_MAXSIZE];
+ int level;
+
+ /* "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" */
+ char control_url_CIF[MINIUPNPC_URL_MAXSIZE];
+ char event_sub_url_CIF[MINIUPNPC_URL_MAXSIZE];
+ char scpd_url_CIF[MINIUPNPC_URL_MAXSIZE];
+ char service_type_CIF[MINIUPNPC_URL_MAXSIZE];
+
+ /* "urn:schemas-upnp-org:service:WANIPConnection:1"
+ * "urn:schemas-upnp-org:service:WANPPPConnection:1" */
+ char control_url[MINIUPNPC_URL_MAXSIZE];
+ char event_sub_url[MINIUPNPC_URL_MAXSIZE];
+ char scpd_url[MINIUPNPC_URL_MAXSIZE];
+ char service_type[MINIUPNPC_URL_MAXSIZE];
+
+ /* Used temporarily by the parser */
+ char control_url_tmp[MINIUPNPC_URL_MAXSIZE];
+ char event_sub_url_tmp[MINIUPNPC_URL_MAXSIZE];
+ char scpd_url_tmp[MINIUPNPC_URL_MAXSIZE];
+ char service_type_tmp[MINIUPNPC_URL_MAXSIZE];
+};
+
+/**
+ * Parse UPnP IGD XML description to a UPNP_IGD_Data_ structure.
+ */
+void
+UPNP_IGD_parse_desc_ (const char *buffer, int buf_size,
+ struct UPNP_IGD_Data_ *data);
+
+#endif
--- /dev/null
+/*
+ This file is part of GNUnet.
+ (C) 2009, 2010 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/*
+ * Code in this file is originally based on the miniupnp library.
+ * Copyright (c) 2005-2009, Thomas BERNARD. All rights reserved.
+ *
+ * Original licence:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file nat/minixml.c
+ * @brief Simple XML parser used by UPnP
+ *
+ * @author Milan Bouchet-Valat
+ */
+
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "upnp-minixml.h"
+
+/**
+ * Used to parse the argument list.
+ *
+ * @returns GNUNET_OK on success, or GNUNET_SYSERR if the end
+ * of the xmlbuffer is reached
+ */
+static int
+parse_att (struct UPNP_xml_parser_ *p)
+{
+ const char *att_name;
+ int att_name_len;
+ const char *att_value;
+ int att_value_len;
+ while (p->xml < p->xml_end)
+ {
+ if (*p->xml == '/' || *p->xml == '>')
+ return GNUNET_OK;
+ if (!IS_WHITE_SPACE (*p->xml))
+ {
+ char sep;
+ att_name = p->xml;
+ att_name_len = 0;
+ while (*p->xml != '=' && !IS_WHITE_SPACE (*p->xml))
+ {
+ att_name_len++;
+ p->xml++;
+ if (p->xml >= p->xml_end)
+ return GNUNET_SYSERR;
+ }
+ while (*(p->xml++) != '=')
+ {
+ if (p->xml >= p->xml_end)
+ return GNUNET_SYSERR;
+ }
+ while (IS_WHITE_SPACE (*p->xml))
+ {
+ p->xml++;
+ if (p->xml >= p->xml_end)
+ return GNUNET_SYSERR;
+ }
+ sep = *p->xml;
+ if (sep == '\'' || sep == '\"')
+ {
+ p->xml++;
+ if (p->xml >= p->xml_end)
+ return GNUNET_SYSERR;
+ att_value = p->xml;
+ att_value_len = 0;
+ while (*p->xml != sep)
+ {
+ att_value_len++;
+ p->xml++;
+ if (p->xml >= p->xml_end)
+ return GNUNET_SYSERR;
+ }
+ }
+ else
+ {
+ att_value = p->xml;
+ att_value_len = 0;
+ while (!IS_WHITE_SPACE (*p->xml)
+ && *p->xml != '>' && *p->xml != '/')
+ {
+ att_value_len++;
+ p->xml++;
+ if (p->xml >= p->xml_end)
+ return GNUNET_SYSERR;
+ }
+ }
+
+ if (p->att_func)
+ p->att_func (p->cls, att_name, att_name_len, att_value,
+ att_value_len);
+ }
+ p->xml++;
+ }
+ return GNUNET_SYSERR;
+}
+
+/**
+ * Parse the xml stream and call the callback
+ * functions when needed...
+ */
+void
+parse_elt (struct UPNP_xml_parser_ *p)
+{
+ int i;
+ const char *element_name;
+ while (p->xml < (p->xml_end - 1))
+ {
+ /* Element name */
+ if ((p->xml)[0] == '<' && (p->xml)[1] != '?')
+ {
+ i = 0;
+ element_name = ++p->xml;
+ while (!IS_WHITE_SPACE (*p->xml)
+ && (*p->xml != '>') && (*p->xml != '/'))
+ {
+ i++;
+ p->xml++;
+ if (p->xml >= p->xml_end)
+ return;
+ /* to ignore namespace : */
+ if (*p->xml == ':')
+ {
+ i = 0;
+ element_name = ++p->xml;
+ }
+ }
+
+ /* Start of element */
+ if (i > 0)
+ {
+ if (p->start_elt_func)
+ p->start_elt_func (p->cls, element_name, i);
+ if (parse_att (p) != GNUNET_OK)
+ return;
+ if (*p->xml != '/')
+ {
+ const char *data;
+ i = 0;
+ data = ++p->xml;
+ if (p->xml >= p->xml_end)
+ return;
+ while (IS_WHITE_SPACE (*p->xml))
+ {
+ p->xml++;
+ if (p->xml >= p->xml_end)
+ return;
+ }
+ while (*p->xml != '<')
+ {
+ i++;
+ p->xml++;
+ if (p->xml >= p->xml_end)
+ return;
+ }
+ if (i > 0 && p->data_func)
+ p->data_func (p->cls, data, i);
+ }
+ }
+ /* End of element */
+ else if (*p->xml == '/')
+ {
+ i = 0;
+ element_name = ++p->xml;
+ if (p->xml >= p->xml_end)
+ return;
+ while ((*p->xml != '>'))
+ {
+ i++;
+ p->xml++;
+ if (p->xml >= p->xml_end)
+ return;
+ }
+ if (p->end_elt_func)
+ p->end_elt_func (p->cls, element_name, i);
+ p->xml++;
+ }
+ }
+ else
+ {
+ p->xml++;
+ }
+ }
+}
+
+/**
+ * Parse XML content according to the values stored in the parser struct.
+ * The parser must be initialized before calling this function
+ */
+void
+UPNP_parse_xml_ (struct UPNP_xml_parser_ *parser)
+{
+ parser->xml = parser->xml_start;
+ parser->xml_end = parser->xml_start + parser->xml_size;
+ parse_elt (parser);
+}
--- /dev/null
+/*
+ This file is part of GNUnet.
+ (C) 2009, 2010 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/*
+ * Code in this file is originally based on the miniupnp library.
+ * Copyright (c) 2005-2008, Thomas BERNARD. All rights reserved.
+ *
+ * Original licence:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file nat/upnp-minixml.h
+ * @brief Simple XML parser used by UPnP
+ *
+ * @author Milan Bouchet-Valat
+ */
+
+#ifndef MINIXML_H
+#define MINIXML_H
+
+#define IS_WHITE_SPACE(c) ((c==' ') || (c=='\t') || (c=='\r') || (c=='\n'))
+
+/**
+ * Structure describing the contents and methods that should be
+ * used when running parse_xml();
+ *
+ * If a callback function pointer is set to NULL, the function
+ * is not called */
+struct UPNP_xml_parser_
+{
+ /**
+ * Pointer to the XML data to parse
+ */
+ const char *xml_start;
+
+ /**
+ * Pointer to the last character to parse (optional)
+ */
+ const char *xml_end;
+
+ /**
+ * Size of the data stored at xml_start
+ */
+ int xml_size;
+
+ /**
+ * Pointer to current character (private)
+ */
+ const char *xml;
+
+ /**
+ * Closure for user-provided callback functions
+ */
+ void *cls;
+
+ /**
+ * User function called when reaching the start of an XML element.
+ */
+ void (*start_elt_func) (void *cls, const char *elt, int elt_len);
+
+ /**
+ * User function called when reaching the end of an XML element.
+ */
+ void (*end_elt_func) (void *cls, const char *elt, int elt_len);
+
+ /**
+ * User function called when an XML element data is found.
+ */
+ void (*data_func) (void *cls, const char *data, int data_len);
+
+ /**
+ * User function called for every XML element attribute.
+ */
+ void (*att_func) (void *cls, const char *att_name, int att_name_len,
+ const char *att_value, int att_value_len);
+};
+
+/**
+ * Parse data provided to the xml_parser structure, using
+ * user-provided functions.
+ *
+ * The xmlparser structure must be initialized before the call;
+ * the following structure members have to be set:
+ * xml_start, xml_size, cls, *func.
+ * The xml member is for internal usage, xml_end is computed
+ * automatically.
+ *
+ * @param parser the structure used for parsing */
+void UPNP_parse_xml_ (struct UPNP_xml_parser_ *parser);
+
+#endif
--- /dev/null
+/*
+ This file is part of GNUnet.
+ (C) 2009, 2010 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/*
+ * Code in this file is originally inspired by the miniupnp library.
+ * Copyright (c) 2006, Thomas BERNARD. All rights reserved.
+ *
+ * Original licence:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file nat/upnp-reply-parse.c
+ * @brief Parser for XML replies to UPnP commands
+ *
+ * @author Milan Bouchet-Valat
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "upnp-minixml.h"
+#include "upnp-reply-parse.h"
+
+static void
+start_elt (void *d, const char *name, int l)
+{
+ struct UPNP_REPLY_NameValueList_ *data =
+ (struct UPNP_REPLY_NameValueList_ *) d;
+
+ if (l > 63)
+ l = 63;
+
+ memcpy (data->curelt, name, l);
+ data->curelt[l] = '\0';
+}
+
+static void
+get_data (void *d, const char *datas, int l)
+{
+ struct UPNP_REPLY_NameValueList_ *data =
+ (struct UPNP_REPLY_NameValueList_ *) d;
+ struct UPNP_REPLY_NameValue_ *nv;
+
+ nv = malloc (sizeof (struct UPNP_REPLY_NameValue_));
+
+ if (l > 63)
+ l = 63;
+
+ strncpy (nv->name, data->curelt, 64);
+ nv->name[63] = '\0';
+ memcpy (nv->value, datas, l);
+ nv->value[l] = '\0';
+
+ LIST_INSERT_HEAD (&(data->head), nv, entries);
+}
+
+void
+UPNP_REPLY_parse_ (const char *buffer, int buf_size,
+ struct UPNP_REPLY_NameValueList_ *data)
+{
+ struct UPNP_xml_parser_ parser;
+
+ LIST_INIT (&(data->head));
+
+ /* Init xml_parser object */
+ parser.xml_start = buffer;
+ parser.xml_size = buf_size;
+ parser.cls = data;
+ parser.start_elt_func = start_elt;
+ parser.end_elt_func = 0;
+ parser.data_func = get_data;
+ parser.att_func = 0;
+
+ UPNP_parse_xml_ (&parser);
+}
+
+void
+UPNP_REPLY_free_ (struct UPNP_REPLY_NameValueList_ *pdata)
+{
+ struct UPNP_REPLY_NameValue_ *nv;
+
+ while ((nv = pdata->head.lh_first) != NULL)
+ {
+ LIST_REMOVE (nv, entries);
+ GNUNET_free (nv);
+ }
+}
+
+char *
+UPNP_REPLY_get_value_ (struct UPNP_REPLY_NameValueList_ *pdata,
+ const char *Name)
+{
+ struct UPNP_REPLY_NameValue_ *nv;
+ char *p = NULL;
+
+ for (nv = pdata->head.lh_first;
+ (nv != NULL) && (p == NULL); nv = nv->entries.le_next)
+ {
+ if (strcmp (nv->name, Name) == 0)
+ p = nv->value;
+ }
+
+ return p;
+}
+
+#if DEBUG_UPNP
+void
+UPNP_REPLY_print_ (char *buffer, int buf_size)
+{
+ struct UPNP_REPLY_NameValueList_ pdata;
+ struct UPNP_REPLY_NameValue_ *nv;
+
+ UPNP_REPLY_parse_ (buffer, buf_size, &pdata);
+
+ for (nv = pdata.head.lh_first; nv != NULL; nv = nv->entries.le_next)
+ {
+ GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP",
+ "%s = %s", nv->name, nv->value);
+ }
+
+ UPNP_REPLY_free_ (&pdata);
+}
+#endif
--- /dev/null
+/*
+ This file is part of GNUnet.
+ (C) 2009, 2010 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/*
+ * Code in this file is originally inspired by the miniupnp library.
+ * Copyright (c) 2005-2009, Thomas BERNARD. All rights reserved.
+ *
+ * Original licence:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file nat/upnp-reply-parse.h
+ * @brief Parser for XML replies to UPnP commands
+ *
+ * @author Milan Bouchet-Valat
+ */
+
+#ifndef UPNP_PARSE_REPLY_H
+#define UPNP_PARSE_REPLY_H
+
+#include "bsdqueue.h"
+
+ /**
+ * Name-value pair used by UPNP_REPLY_NameValueList.
+ */
+struct UPNP_REPLY_NameValue_
+{
+ LIST_ENTRY (UPNP_REPLY_NameValue_) entries;
+ char name[64];
+ char value[64];
+};
+
+ /**
+ * Name-value list to store data parsed from a UPnP reply.
+ */
+struct UPNP_REPLY_NameValueList_
+{
+ LIST_HEAD (listhead, UPNP_REPLY_NameValue_) head;
+ char curelt[64];
+};
+
+ /**
+ * Parse UPnP XML reply to a name-value list.
+ */
+void
+UPNP_REPLY_parse_ (const char *buffer, int buf_size,
+ struct UPNP_REPLY_NameValueList_ *data);
+
+ /**
+ * Free name-value list obtained using UPNP_REPLY_parse().
+ */
+void UPNP_REPLY_free_ (struct UPNP_REPLY_NameValueList_ *pdata);
+
+ /**
+ * Get value corresponding to name from a name-value list.
+ */
+char *UPNP_REPLY_get_value_ (struct UPNP_REPLY_NameValueList_ *pdata,
+ const char *name);
+
+#if DEBUG_UPNP
+ /**
+ * Parse a UPnP XMl reply and print the result as names-value pairs.
+ */
+void UPNP_REPLY_print_ (char *buffer, int buf_size);
+#endif
+
+#endif
#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 */
struct GNUNET_NAT_UPNP_Handle
{
+ struct GNUNET_SCHEDULER_Handle *sched;
int hasDiscovered;
- struct UPNPUrls urls;
- struct IGDdatas data;
+ char *control_url;
+ char *service_type;
int port;
const struct sockaddr *addr;
socklen_t addrlen;
enum UPNP_State state;
struct sockaddr *ext_addr;
const 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;
if (addr && GNUNET_NAT_cmp_addr (upnp->addr, addr) == 0)
{
- upnp->iface = name; // BADNESS!
+ upnp->iface = name; // BADNESS!
return GNUNET_SYSERR;
}
}
-GNUNET_NAT_UPNP_Handle *
-GNUNET_NAT_UPNP_init (const struct sockaddr *addr,
- socklen_t addrlen,
- u_short port)
+struct GNUNET_NAT_UPNP_Handle *
+GNUNET_NAT_UPNP_init (struct GNUNET_SCHEDULER_Handle *sched,
+ const struct sockaddr *addr,
+ socklen_t addrlen,
+ u_short port,
+ GNUNET_NAT_UPNP_pulse_cb pulse_cb, void *pulse_cls)
{
- GNUNET_NAT_UPNP_Handle *upnp;
+ struct GNUNET_NAT_UPNP_Handle *handle;
+
+ handle = GNUNET_malloc (sizeof (struct GNUNET_NAT_UPNP_Handle));
+ handle->sched = sched;
+ 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;
- upnp = GNUNET_malloc (sizeof (GNUNET_NAT_UPNP_Handle));
- upnp->state = UPNP_DISCOVER;
- upnp->addr = addr;
- upnp->addrlen = addrlen;
- upnp->port = port;
/* Find the interface corresponding to the address,
* on which we should broadcast call for routers */
- 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 */
+ if (inet_aton (ext_addr, &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;
+ }
+ 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 ext_addr pointer for returning external IP address.
* Will be set to NULL if address could not be found. Don't free the sockaddr.
*/
-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->sched, 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->sched,
+ 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->sched, 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->sched, 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->sched, handle->control_url,
+ handle->service_type,
+ get_ip_address_cb, handle);
}
-
- return ret;
}
/*
This file is part of GNUnet.
- (C) 2009 Christian Grothoff (and other contributing authors)
+ (C) 2009, 2010 Christian Grothoff (and other contributing authors)
GNUnet is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
struct GNUNET_NAT_UPNP_Handle;
-struct GNUNET_NAT_UPNP_Handle *
-GNUNET_NAT_UPNP_init (const struct sockaddr *addr,
- socklen_t addrlen,
- unsigned short port);
+typedef void (*GNUNET_NAT_UPNP_pulse_cb) (int status,
+ struct sockaddr * ext_addr,
+ void *cls);
-void GNUNET_NAT_UPNP_close (struct GNUNET_NAT_UPNP_Handle * h);
+struct GNUNET_NAT_UPNP_Handle *GNUNET_NAT_UPNP_init (struct
+ GNUNET_SCHEDULER_Handle
+ *sched,
+ const struct sockaddr
+ *addr, socklen_t addrlen,
+ unsigned short port,
+ GNUNET_NAT_UPNP_pulse_cb
+ pulse_cb,
+ void *pulse_cls);
-int GNUNET_NAT_UPNP_pulse (struct GNUNET_NAT_UPNP_Handle *h,
- int is_enabled,
- int do_port_check,
- struct sockaddr **ext_addr);
+void GNUNET_NAT_UPNP_close (struct GNUNET_NAT_UPNP_Handle *h);
-#endif
+void GNUNET_NAT_UPNP_pulse (struct GNUNET_NAT_UPNP_Handle *h,
+ int is_enabled, int do_port_check);
+
+#endif
/* UPNP_H */