gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] r13254 - gnunet/src/nat


From: gnunet
Subject: [GNUnet-SVN] r13254 - gnunet/src/nat
Date: Sat, 9 Oct 2010 15:53:47 +0200

Author: moon
Date: 2010-10-09 15:53:47 +0200 (Sat, 09 Oct 2010)
New Revision: 13254

Added:
   gnunet/src/nat/bsdqueue.h
   gnunet/src/nat/upnp-commands.c
   gnunet/src/nat/upnp-commands.h
   gnunet/src/nat/upnp-discover.c
   gnunet/src/nat/upnp-discover.h
   gnunet/src/nat/upnp-igd-parse.c
   gnunet/src/nat/upnp-igd-parse.h
   gnunet/src/nat/upnp-minixml.c
   gnunet/src/nat/upnp-minixml.h
   gnunet/src/nat/upnp-reply-parse.c
   gnunet/src/nat/upnp-reply-parse.h
Modified:
   gnunet/src/nat/Makefile.am
   gnunet/src/nat/nat.c
   gnunet/src/nat/nat.h
   gnunet/src/nat/natpmp.c
   gnunet/src/nat/natpmp.h
   gnunet/src/nat/test_nat.c
   gnunet/src/nat/upnp.c
   gnunet/src/nat/upnp.h
Log:
rework UPnP code to use GNUnet scheduler and network API
disable NAT-PMP support for now



Modified: gnunet/src/nat/Makefile.am
===================================================================
--- gnunet/src/nat/Makefile.am  2010-10-08 17:55:16 UTC (rev 13253)
+++ gnunet/src/nat/Makefile.am  2010-10-09 13:53:47 UTC (rev 13254)
@@ -1,5 +1,3 @@
-SUBDIRS = .
-
 INCLUDES = -I$(top_srcdir)/src/include
 
 if MINGW
@@ -18,11 +16,16 @@
 
 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 \
@@ -46,6 +49,6 @@
 
 test_nat_LDADD = \
  $(top_builddir)/src/nat/libgnunetnat.la \
- $(top_builddir)/src/util/libgnunetutil.la
-
+ $(top_builddir)/src/util/libgnunetutil.la \
+ @LIBCURL@
 endif

Added: gnunet/src/nat/bsdqueue.h
===================================================================
--- gnunet/src/nat/bsdqueue.h                           (rev 0)
+++ gnunet/src/nat/bsdqueue.h   2010-10-09 13:53:47 UTC (rev 13254)
@@ -0,0 +1,157 @@
+/*
+     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 */

Modified: gnunet/src/nat/nat.c
===================================================================
--- gnunet/src/nat/nat.c        2010-10-08 17:55:16 UTC (rev 13253)
+++ gnunet/src/nat/nat.c        2010-10-09 13:53:47 UTC (rev 13254)
@@ -46,12 +46,12 @@
   /**
    * 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.
@@ -61,17 +61,23 @@
   /**
    * 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;
 
@@ -92,8 +98,14 @@
 
   int port_mapped;
 
+  int old_status;
+
+  int new_status;
+
   int did_warn;
 
+  int processing;
+
   uint16_t public_port;
 
 };
@@ -122,9 +134,9 @@
 
 
 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);
 }
 
 
@@ -134,16 +146,15 @@
  * @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));
@@ -157,50 +168,45 @@
  * 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;
+  h->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;
+  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;
 
@@ -209,14 +215,14 @@
 #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
     {
@@ -224,90 +230,143 @@
     }
 }
 
+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);
     }
-  nat->pulse_timer = GNUNET_SCHEDULER_add_delayed (nat->sched, 
-                                                   GNUNET_TIME_UNIT_SECONDS,
-                                                   &nat_pulse, nat);
+
+  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
+    }
+}
+
+
 /**
  * Attempt to enable port redirection and detect public IP address contacting
  * UPnP or NAT-PMP routers on the local network. Use addr to specify to which
@@ -322,43 +381,52 @@
  * @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;
 
-  nat = GNUNET_malloc (sizeof (struct GNUNET_NAT_Handle));
+  h = 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;
 }
 
 
@@ -371,23 +439,21 @@
 void
 GNUNET_NAT_unregister (struct GNUNET_NAT_Handle *h)
 {
-  struct sockaddr *addr;
+  GNUNET_NAT_UPNP_pulse (h->upnp, GNUNET_NO, GNUNET_NO);
+  GNUNET_NAT_UPNP_close (h->upnp);
 
-  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);
+#if 0
+  GNUNET_NAT_NATPMP_pulse (h->natpmp, GNUNET_NO);
   GNUNET_NAT_NATPMP_close (h->natpmp);
-  GNUNET_NAT_UPNP_close (h->upnp);
+#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 */
-

Modified: gnunet/src/nat/nat.h
===================================================================
--- gnunet/src/nat/nat.h        2010-10-08 17:55:16 UTC (rev 13253)
+++ gnunet/src/nat/nat.h        2010-10-09 13:53:47 UTC (rev 13254)
@@ -34,29 +34,29 @@
  * 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
+};
 
 
 /**
@@ -66,8 +66,7 @@
  * @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

Modified: gnunet/src/nat/natpmp.c
===================================================================
--- gnunet/src/nat/natpmp.c     2010-10-08 17:55:16 UTC (rev 13253)
+++ gnunet/src/nat/natpmp.c     2010-10-09 13:53:47 UTC (rev 13254)
@@ -41,6 +41,7 @@
 #include "platform.h"
 #include "gnunet_common.h"
 #include "gnunet_nat_lib.h"
+#include "nat.h"
 #include "natpmp.h"
 
 #define LIFETIME_SECS 3600
@@ -48,7 +49,7 @@
 /* Component name for logging */
 #define COMP_NAT_NATPMP _("NAT (NAT-PMP))")
 
-typedef enum
+enum NATPMP_state
 {
   NATPMP_IDLE,
   NATPMP_ERR,
@@ -59,20 +60,21 @@
   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;
 };
 
 
@@ -82,28 +84,27 @@
 #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;
@@ -112,7 +113,7 @@
 }
 
 void
-GNUNET_NAT_NATPMP_close (GNUNET_NAT_NATPMP_Handle * nat)
+GNUNET_NAT_NATPMP_close (struct GNUNET_NAT_NATPMP_Handle *nat)
 {
   if (nat)
     {
@@ -160,7 +161,7 @@
 
   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);
@@ -174,40 +175,38 @@
 
           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)
@@ -235,14 +234,14 @@
 
   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;
@@ -279,7 +278,7 @@
 
   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)
@@ -289,7 +288,7 @@
           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)
         {

Modified: gnunet/src/nat/natpmp.h
===================================================================
--- gnunet/src/nat/natpmp.h     2010-10-08 17:55:16 UTC (rev 13253)
+++ gnunet/src/nat/natpmp.h     2010-10-09 13:53:47 UTC (rev 13254)
@@ -32,16 +32,18 @@
 
 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 */

Modified: gnunet/src/nat/test_nat.c
===================================================================
--- gnunet/src/nat/test_nat.c   2010-10-08 17:55:16 UTC (rev 13253)
+++ gnunet/src/nat/test_nat.c   2010-10-09 13:53:47 UTC (rev 13254)
@@ -41,14 +41,13 @@
 #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,
@@ -73,9 +72,7 @@
 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;
 
@@ -85,6 +82,12 @@
       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
@@ -107,7 +110,8 @@
   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);
     }
 
@@ -120,14 +124,17 @@
   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

Added: gnunet/src/nat/upnp-commands.c
===================================================================
--- gnunet/src/nat/upnp-commands.c                              (rev 0)
+++ gnunet/src/nat/upnp-commands.c      2010-10-09 13:53:47 UTC (rev 13254)
@@ -0,0 +1,880 @@
+/*
+     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);
+}

Added: gnunet/src/nat/upnp-commands.h
===================================================================
--- gnunet/src/nat/upnp-commands.h                              (rev 0)
+++ gnunet/src/nat/upnp-commands.h      2010-10-09 13:53:47 UTC (rev 13254)
@@ -0,0 +1,281 @@
+/*
+     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

Added: gnunet/src/nat/upnp-discover.c
===================================================================
--- gnunet/src/nat/upnp-discover.c                              (rev 0)
+++ gnunet/src/nat/upnp-discover.c      2010-10-09 13:53:47 UTC (rev 13254)
@@ -0,0 +1,1285 @@
+/*
+     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);
+}

Added: gnunet/src/nat/upnp-discover.h
===================================================================
--- gnunet/src/nat/upnp-discover.h                              (rev 0)
+++ gnunet/src/nat/upnp-discover.h      2010-10-09 13:53:47 UTC (rev 13254)
@@ -0,0 +1,84 @@
+/*
+     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

Added: gnunet/src/nat/upnp-igd-parse.c
===================================================================
--- gnunet/src/nat/upnp-igd-parse.c                             (rev 0)
+++ gnunet/src/nat/upnp-igd-parse.c     2010-10-09 13:53:47 UTC (rev 13254)
@@ -0,0 +1,207 @@
+/*
+     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
+}

Added: gnunet/src/nat/upnp-igd-parse.h
===================================================================
--- gnunet/src/nat/upnp-igd-parse.h                             (rev 0)
+++ gnunet/src/nat/upnp-igd-parse.h     2010-10-09 13:53:47 UTC (rev 13254)
@@ -0,0 +1,99 @@
+/*
+     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

Added: gnunet/src/nat/upnp-minixml.c
===================================================================
--- gnunet/src/nat/upnp-minixml.c                               (rev 0)
+++ gnunet/src/nat/upnp-minixml.c       2010-10-09 13:53:47 UTC (rev 13254)
@@ -0,0 +1,239 @@
+/*
+     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);
+}

Added: gnunet/src/nat/upnp-minixml.h
===================================================================
--- gnunet/src/nat/upnp-minixml.h                               (rev 0)
+++ gnunet/src/nat/upnp-minixml.h       2010-10-09 13:53:47 UTC (rev 13254)
@@ -0,0 +1,131 @@
+/*
+     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

Added: gnunet/src/nat/upnp-reply-parse.c
===================================================================
--- gnunet/src/nat/upnp-reply-parse.c                           (rev 0)
+++ gnunet/src/nat/upnp-reply-parse.c   2010-10-09 13:53:47 UTC (rev 13254)
@@ -0,0 +1,166 @@
+/*
+     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

Added: gnunet/src/nat/upnp-reply-parse.h
===================================================================
--- gnunet/src/nat/upnp-reply-parse.h                           (rev 0)
+++ gnunet/src/nat/upnp-reply-parse.h   2010-10-09 13:53:47 UTC (rev 13254)
@@ -0,0 +1,107 @@
+/*
+     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

Modified: gnunet/src/nat/upnp.c
===================================================================
--- gnunet/src/nat/upnp.c       2010-10-08 17:55:16 UTC (rev 13253)
+++ gnunet/src/nat/upnp.c       2010-10-09 13:53:47 UTC (rev 13254)
@@ -35,12 +35,12 @@
 #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 */
@@ -57,9 +57,10 @@
 
 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;
@@ -67,20 +68,21 @@
   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;
     }
 
@@ -88,41 +90,245 @@
 }
 
 
-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;
 
-  upnp = GNUNET_malloc (sizeof (GNUNET_NAT_UPNP_Handle));
-  upnp->state = UPNP_DISCOVER;
-  upnp->addr = addr;
-  upnp->addrlen = addrlen;
-  upnp->port = port;
+  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;
+
   /* 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.
  * 
@@ -133,45 +339,19 @@
  * @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)
@@ -183,35 +363,26 @@
   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)
@@ -222,10 +393,7 @@
 
   if (handle->state == UPNP_MAP)
     {
-      int err = -1;
-      errno = 0;
-
-      if (!handle->urls.controlURL)
+      if (!handle->control_url)
         handle->is_mapped = 0;
       else
         {
@@ -233,111 +401,22 @@
           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
+          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);
         }
-      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
-        }
     }
 
-  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;
 }

Modified: gnunet/src/nat/upnp.h
===================================================================
--- gnunet/src/nat/upnp.h       2010-10-08 17:55:16 UTC (rev 13253)
+++ gnunet/src/nat/upnp.h       2010-10-09 13:53:47 UTC (rev 13254)
@@ -1,6 +1,6 @@
 /*
      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
@@ -32,17 +32,24 @@
 
 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 */




reply via email to

[Prev in Thread] Current Thread [Next in Thread]