gnunet-svn
[Top][All Lists]
Advanced

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

[GNUnet-SVN] r4099 - GNUnet/src/transports/upnp


From: grothoff
Subject: [GNUnet-SVN] r4099 - GNUnet/src/transports/upnp
Date: Fri, 29 Dec 2006 07:06:50 -0800 (PST)

Author: grothoff
Date: 2006-12-29 07:06:45 -0800 (Fri, 29 Dec 2006)
New Revision: 4099

Added:
   GNUnet/src/transports/upnp/init.c
   GNUnet/src/transports/upnp/ip.c
   GNUnet/src/transports/upnp/ip.h
   GNUnet/src/transports/upnp/upnpdemo.c
Modified:
   GNUnet/src/transports/upnp/Makefile.am
   GNUnet/src/transports/upnp/todo
   GNUnet/src/transports/upnp/upnp.c
   GNUnet/src/transports/upnp/upnp.h
   GNUnet/src/transports/upnp/xmlnode.c
Log:
code basically working (for my NAT box)

Modified: GNUnet/src/transports/upnp/Makefile.am
===================================================================
--- GNUnet/src/transports/upnp/Makefile.am      2006-12-29 14:43:29 UTC (rev 
4098)
+++ GNUnet/src/transports/upnp/Makefile.am      2006-12-29 15:06:45 UTC (rev 
4099)
@@ -13,22 +13,24 @@
 libgnunetupnp_la_SOURCES = \
  error.c error.h \
  init.c \
+ ip.c ip.h \
  util.c util.h \
  xmlnode.c xmlnode.h \
  upnp.c upnp.h 
 
 libgnunetupnp_la_LDFLAGS = \
- $(GTK_LIBS) @EXT_LIB_PATH@ @EXT_LIBS@ \
+ $(GTK_LIBS) @EXT_LIB_PATH@ @EXT_LIBS@ @LIBCURL@ \
  -export-dynamic \
  -version-info 0:0:0
 
 # FIXME: detect libxml2 path properly!
-libgnunetupnp_la_CFLAGS = \
+libgnunetupnp_la_CFLAGS =  @LIBCURL_CPPFLAGS@ \
  -I$(top_scrdir)/include \
  -I/usr/include/libxml2 \
  @GNUNETGTK_CFLAGS@ \
  @GTK_CFLAGS@ 
 
+
 libgnunetupnp_la_LIBADD = \
  @GTK_LIBS@ @EXT_LIB_PATH@ @EXT_LIBS@ \
  @GNUNETGTK_LIBS@ \
@@ -36,3 +38,33 @@
   -lgthread-2.0 \
  $(top_builddir)/src/util/libgnunetutil.la 
 
+
+noinst_PROGRAMS = \
+ upnpdemo
+
+upnpdemo_SOURCES = \
+ upnpdemo.c 
+upnpdemo_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la \
+ $(top_builddir)/src/util/loggers/libgnunetutil_logging.la \
+ $(top_builddir)/src/transports/upnp/libgnunetupnp.la \
+ $(top_builddir)/src/util/config_impl/libgnunetutil_config.la \
+ $(top_builddir)/src/util/cron/libgnunetutil_cron.la 
+
+
+
+check_PROGRAMS = \
+ upnptest 
+
+TESTS = $(check_PROGRAMS)
+
+upnptest_SOURCES = \
+ upnptest.c 
+upnptest_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la \
+ $(top_builddir)/src/util/loggers/libgnunetutil_logging.la \
+ $(top_builddir)/src/transports/upnp/libgnunetupnp.la \
+ $(top_builddir)/src/util/config_impl/libgnunetutil_config.la \
+ $(top_builddir)/src/util/cron/libgnunetutil_cron.la 
+
+

Added: GNUnet/src/transports/upnp/init.c
===================================================================
--- GNUnet/src/transports/upnp/init.c   2006-12-29 14:43:29 UTC (rev 4098)
+++ GNUnet/src/transports/upnp/init.c   2006-12-29 15:06:45 UTC (rev 4099)
@@ -0,0 +1,204 @@
+/*
+     This file is part of GNUnet
+     (C) 2006 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 2, 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 src/transports/upnp/init.c
+ * @brief API for UPnP access
+ * @author Christian Grothoff
+ */
+
+#include "platform.h"
+#include "gnunet_util.h"
+#include "gnunet_util_cron.h"
+#include "upnp.h"
+
+static struct GE_Context * ectx;
+
+static struct GC_Configuration * cfg;
+
+static struct CronManager * cron;
+
+static struct MUTEX * lock;
+
+typedef struct {
+  unsigned short port;
+  const char * proto;
+} PMap;
+
+static PMap * maps;
+
+static unsigned int maps_size;
+
+static struct PTHREAD * discovery;
+
+static int discovery_socket;
+
+/**
+ * Obtain the public/external IP address.
+ *
+ * @return SYSERR on error, OK on success
+ */
+static int gnunet_upnp_get_public_ip(IPaddr * address) {
+  const char *  ip;
+
+  ip = gaim_upnp_get_public_ip();
+  if (ip == NULL)
+    return SYSERR;
+  return get_host_by_name(ectx,
+                         ip,
+                         address);
+}
+
+static void kill_discovery() {
+  void * unused;
+
+  if (discovery != NULL) {
+    CLOSE(discovery_socket);
+    PTHREAD_JOIN(discovery, &unused);
+    discovery = NULL;
+  }
+}
+
+static void * discover_thread() {
+  gaim_upnp_discover(ectx, cfg, discovery_socket);
+  return NULL;
+}
+
+/**
+ * Periodically try to (re)discover UPnP access points.
+ */
+static void discover(void * unused) {
+  kill_discovery();
+  discovery_socket = SOCKET(AF_INET, SOCK_DGRAM, 0);
+  if (discovery_socket == -1)
+    return;
+  discovery = PTHREAD_CREATE(&discover_thread,
+                            NULL,
+                            1024 * 128);
+}
+
+/**
+ * Periodically repeat our requests for port mappings.
+ */
+static void portmap(void * unused) {
+  unsigned int i;
+
+  MUTEX_LOCK(lock);
+  for (i=0;i<maps_size;i++) 
+    gaim_upnp_change_port_mapping(ectx,
+                                 cfg,
+                                 NO,
+                                 maps[i].port,
+                                 maps[i].proto);  
+  MUTEX_UNLOCK(lock);
+}
+
+
+/**
+ * Get the external IP address for the local machine.
+ */
+void gnunet_upnp_init(struct GC_Configuration * c,
+                     struct GE_Context * e) {
+  ectx = e;
+  cfg = c;
+  cron = cron_create(ectx);
+  lock = MUTEX_CREATE(NO);
+  cron_start(cron);
+  cron_add_job(cron,
+              &discover,
+              0,
+              5 * cronMINUTES,
+              NULL);
+  cron_add_job(cron,
+              &portmap,
+              150 * cronSECONDS,
+              5 * cronMINUTES,
+              NULL);
+}
+
+/**
+ * Get the external IP address for the local machine.
+ *
+ * @return SYSERR on error, OK on success
+ */
+int gnunet_upnp_get_ip(unsigned short port,
+                      const char * protocol,
+                      IPaddr * address) {
+  unsigned int i;
+
+  MUTEX_LOCK(lock);
+  for (i=0;i<maps_size;i++) 
+    if ( (0 == strcmp(maps[i].proto, protocol)) &&
+        (maps[i].port == port) )
+      break;
+  if (i == maps_size) { 
+    /* new entry! */
+    GROW(maps,
+        maps_size,
+        maps_size + 1);
+    maps[i].proto = protocol;
+    maps[i].port = port;
+    gaim_upnp_change_port_mapping(ectx,
+                                 cfg,
+                                 YES,
+                                 port,
+                                 protocol);
+  }
+  MUTEX_UNLOCK(lock);
+  return gnunet_upnp_get_public_ip(address);
+}
+
+/**
+ * Shutdown UPNP.
+ */
+void gnunet_upnp_done() {
+  unsigned int i;
+
+  if (cron == NULL)
+    return; /* never used! */
+  for (i=0;i<maps_size;i++) 
+    gaim_upnp_change_port_mapping(ectx,
+                                 cfg,
+                                 NO,
+                                 maps[i].port,
+                                 maps[i].proto);  
+  cron_stop(cron);
+  cron_del_job(cron,
+              &discover,
+              5 * cronMINUTES,
+              NULL);
+  cron_del_job(cron,
+              &portmap,
+              5 * cronMINUTES,
+              NULL);
+  cron_destroy(cron);
+  kill_discovery();
+  cron = NULL;
+  MUTEX_DESTROY(lock);
+  lock = NULL;
+  GROW(maps,
+       maps_size,
+       0);
+  ectx = NULL;
+  cfg = NULL;
+}
+
+
+/* end of init.c */


Property changes on: GNUnet/src/transports/upnp/init.c
___________________________________________________________________
Name: svn:eol-style
   + native

Added: GNUnet/src/transports/upnp/ip.c
===================================================================
--- GNUnet/src/transports/upnp/ip.c     2006-12-29 14:43:29 UTC (rev 4098)
+++ GNUnet/src/transports/upnp/ip.c     2006-12-29 15:06:45 UTC (rev 4099)
@@ -0,0 +1,346 @@
+/*
+     This file is part of GNUnet.
+     (C) 2001, 2002, 2004, 2005, 2006 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 2, 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 transports/upnp/ip.c
+ * @brief code to determine the IP of the local machine
+ *
+ *
+ * Determine the IP of the local machine. We have many
+ * ways to get that IP:
+ * a) from the interface (ifconfig)
+ * b) via DNS from our HOSTNAME (environment)
+ * c) from the configuration (HOSTNAME specification or static IP)
+ *
+ * Which way applies depends on the OS, the configuration
+ * (dynDNS? static IP? NAT?) and at the end what the user
+ * needs.
+ *
+ * @author Christian Grothoff
+ * @author Tzvetan Horozov
+ */
+
+#include <stdlib.h>
+#include "platform.h"
+#include "gnunet_util.h"
+#include "ip.h"
+
+/* maximum length of hostname */
+#define MAX_HOSTNAME 1024
+
+/**
+ * Obtain the identity information for the current node
+ * (connection information), conInfo.
+ * @return SYSERR on failure, OK on success
+ */
+static int getAddressFromHostname(struct GE_Context * ectx,
+                                 IPaddr * identity) {
+  char hostname[MAX_HOSTNAME];
+  int ret;
+
+  if (0 != gethostname(hostname, MAX_HOSTNAME)) {
+    GE_LOG_STRERROR(ectx,
+                   GE_ERROR | GE_ADMIN | GE_USER | GE_BULK,
+                   "gethostname");
+    return SYSERR;
+  }
+  ret = get_host_by_name(ectx,
+                        hostname,
+                        identity);
+  return ret;
+}
+
+#if LINUX || SOMEBSD || MINGW
+#define MAX_INTERFACES 16
+static int getAddressFromIOCTL(struct GC_Configuration * cfg,
+                              struct GE_Context * ectx,
+                              IPaddr * identity) {
+  char * interfaces;
+#ifndef MINGW
+  struct ifreq ifr[MAX_INTERFACES];
+  struct ifconf ifc;
+  int sockfd,ifCount;
+#else
+  DWORD dwIP;
+#endif
+  int i;
+
+  if (-1 == GC_get_configuration_value_string(cfg,
+                                             "NETWORK",
+                                             "INTERFACE",
+                                             "eth0",
+                                             &interfaces)) {
+    GE_LOG(ectx,
+          GE_ERROR | GE_BULK | GE_USER,
+          _("No interface specified in section `%s' under `%s'!\n"),
+          "NETWORK",
+          "INTERFACE");
+    return SYSERR; /* that won't work! */
+  }
+#ifndef MINGW
+  sockfd = SOCKET(PF_INET, SOCK_DGRAM, 0);
+  if (sockfd == -1) {
+    FREE(interfaces);
+    GE_LOG_STRERROR(ectx,
+                   GE_ERROR | GE_ADMIN | GE_USER | GE_BULK,
+                   "socket");
+    return SYSERR;
+  }
+  memset(&ifc,
+        0,
+        sizeof(struct ifconf));
+  ifc.ifc_len = sizeof(ifr);
+  ifc.ifc_buf = (char*)&ifr;
+
+  if (ioctl(sockfd,
+           SIOCGIFCONF,
+           &ifc) == -1) {
+    GE_LOG_STRERROR(ectx,
+                   GE_WARNING | GE_ADMIN | GE_USER | GE_BULK,
+                   "ioctl");
+    if (0 != CLOSE(sockfd))
+      GE_LOG_STRERROR(ectx,
+                     GE_WARNING | GE_ADMIN | GE_BULK,
+                     "close");
+    FREE(interfaces);
+    return SYSERR;
+  }
+  ifCount = ifc.ifc_len / sizeof(struct ifreq);
+
+  /* first, try to find exatly matching interface */
+  for (i=0;i<ifCount;i++){
+    if (ioctl(sockfd, SIOCGIFADDR, &ifr[i]) != 0)
+       continue;
+    if (ioctl(sockfd, SIOCGIFFLAGS, &ifr[i]) != 0)
+       continue;
+    if (!(ifr[i].ifr_flags & IFF_UP))
+       continue;
+    if (strcmp((char*) interfaces,
+              (char*) ifr[i].ifr_name) != 0)
+      continue;
+    memcpy(identity,
+          &(((struct sockaddr_in *)&ifr[i].ifr_addr)->sin_addr),
+          sizeof(struct in_addr));
+    if (0 != CLOSE(sockfd))
+      GE_LOG_STRERROR(ectx,
+                     GE_WARNING | GE_ADMIN | GE_BULK,
+                     "close");
+    FREE(interfaces);
+    return OK;
+  }
+  GE_LOG(ectx,
+        GE_WARNING | GE_ADMIN | GE_USER | GE_BULK,
+        _("Could not find interface `%s' using `%s', "
+          "trying to find another interface.\n"),
+        interfaces,
+        "ioctl");
+  /* if no such interface exists, take any interface but loopback */
+  for (i=0;i<ifCount;i++){
+    if (ioctl(sockfd, SIOCGIFADDR, &ifr[i]) != 0)
+       continue;
+    if (ioctl(sockfd, SIOCGIFFLAGS, &ifr[i]) != 0)
+       continue;
+    if (!(ifr[i].ifr_flags & IFF_UP))
+       continue;
+    if (strncmp("lo",
+               (char*) ifr[i].ifr_name, 2) == 0)
+      continue;
+    memcpy(identity,
+          &(((struct sockaddr_in *)&ifr[i].ifr_addr)->sin_addr),
+          sizeof(struct in_addr));
+    if (0 != CLOSE(sockfd))
+      GE_LOG_STRERROR(ectx,
+                     GE_WARNING | GE_ADMIN | GE_BULK,
+                     "close");
+    FREE(interfaces);
+    return OK;
+  }
+
+  if (0 != CLOSE(sockfd))
+    GE_LOG_STRERROR(ectx,
+                   GE_WARNING | GE_ADMIN | GE_BULK,
+                   "close");
+  GE_LOG(ectx,
+        GE_WARNING | GE_USER | GE_BULK,
+        _("Could not obtain IP for interface `%s' using `%s'.\n"),
+        interfaces,
+        "ioctl");
+  FREE(interfaces);
+  return SYSERR;
+#else /* MinGW */
+
+  /* Win 98 or Win NT SP 4 */
+  if (GNGetIpAddrTable)
+  {
+    PMIB_IFTABLE pTable;
+    PMIB_IPADDRTABLE pAddrTable;
+    DWORD dwIfIdx;
+    unsigned int iAddrCount = 0;
+
+    dwIP = 0;
+
+    EnumNICs(&pTable, &pAddrTable);
+
+    for(dwIfIdx=0; dwIfIdx < pTable->dwNumEntries; dwIfIdx++) {
+      unsigned long long l;
+      BYTE bPhysAddr[MAXLEN_PHYSADDR];
+
+      l = _atoi64(interfaces);
+
+      memset(bPhysAddr, 0, MAXLEN_PHYSADDR);
+      memcpy(bPhysAddr,
+        pTable->table[dwIfIdx].bPhysAddr,
+        pTable->table[dwIfIdx].dwPhysAddrLen);
+
+      if (memcmp(bPhysAddr, &l, sizeof(l)) == 0) {
+        for(i = 0; i < pAddrTable->dwNumEntries; i++) {
+          if (pAddrTable->table[i].dwIndex
+             == pTable->table[dwIfIdx].dwIndex) {
+            iAddrCount++;
+            dwIP = pAddrTable->table[i].dwAddr;
+          }
+        }
+      }
+    }
+
+    if (! iAddrCount)
+      {
+      GE_LOG(ectx, GE_WARNING | GE_BULK | GE_USER,
+         _("Could not find an IP address for "
+           "interface `%s'.\n"),
+         interfaces);
+
+      GlobalFree(pTable);
+      GlobalFree(pAddrTable);
+      return SYSERR;
+    }
+    else if (iAddrCount > 1)
+      GE_LOG(ectx, GE_WARNING | GE_BULK | GE_USER,
+         _("There is more than one IP address specified"
+           " for interface `%s'.\nGNUnet will "
+           "use %u.%u.%u.%u.\n"),
+         interfaces,
+         PRIP(ntohl(dwIP)));
+
+    identity->addr = dwIP;
+
+    GlobalFree(pTable);
+    GlobalFree(pAddrTable);
+  }
+  else /* Win 95 */
+  {
+    SOCKET s;
+    HOSTENT *pHost;
+    SOCKADDR_IN theHost;
+
+    s = SOCKET(PF_INET, SOCK_STREAM, 0);
+    pHost = GETHOSTBYNAME("www.example.com");
+    if (! pHost) {
+      GE_LOG(ectx, GE_ERROR | GE_BULK | GE_USER,
+         _("Could not resolve `%s' to "
+           "determine our IP address: %s\n"),
+         "www.example.com",
+         STRERROR(errno));
+      return SYSERR;
+    }
+
+    theHost.sin_family = AF_INET;
+    theHost.sin_port = htons(80);
+    theHost.sin_addr.S_un.S_addr
+      = *((unsigned long *) pHost->h_addr_list[0]);
+    if (CONNECT(s,
+               (SOCKADDR *) &theHost,
+               sizeof(theHost)) == SOCKET_ERROR) {
+      GE_LOG_STRERROR(ectx, GE_ERROR | GE_BULK | GE_USER,
+                  "connect");
+      return SYSERR;
+    }
+
+    i = sizeof(theHost);
+    if (GETSOCKNAME(s,
+                   (SOCKADDR *) &theHost,
+                   &i) == SOCKET_ERROR) {
+      GE_LOG_STRERROR(ectx, GE_ERROR | GE_BULK | GE_USER,
+                  "getsockname");
+      return SYSERR;
+    }
+    closesocket(s);
+    identity->addr = theHost.sin_addr.S_un.S_addr;
+  }
+
+  GE_LOG(ectx, GE_DEBUG | GE_REQUEST | GE_USER,
+      _("GNUnet now uses the IP address %u.%u.%u.%u.\n"),
+      PRIP(ntohl(identity->addr)));
+
+  return OK;
+#endif
+}
+
+#endif
+
+/**
+ * Get the IP address for the local machine.
+ * @return NULL on error
+ */
+char * gaim_upnp_get_internal_ip(struct GC_Configuration * cfg,
+                                struct GE_Context * ectx) {
+  IPaddr address;
+  char * ipString;
+  int retval;
+  char buf[65];
+
+  retval = SYSERR;
+  if (GC_have_configuration_value(cfg,
+                                 "NETWORK",
+                                 "IP-LOCAL")) {        
+    ipString = NULL;
+    GC_get_configuration_value_string(cfg,
+                                     "NETWORK",
+                                     "IP-LOCAL",
+                                     "",
+                                     &ipString);
+    if (strlen(ipString) > 0) {
+      retval = get_host_by_name(ectx,
+                               ipString,
+                               &address);
+    }
+    FREE(ipString);
+  }
+#if LINUX || SOMEBSD || MINGW
+  if (retval == SYSERR)
+    if (OK == getAddressFromIOCTL(cfg,
+                                 ectx,
+                                 &address))
+      retval = OK;
+#endif
+  if (retval == SYSERR)
+    retval = getAddressFromHostname(ectx,
+                                   &address);
+  if (retval == SYSERR)
+    return NULL;
+  SNPRINTF(buf,
+          64,
+          "%u.%u.%u.%u",          
+          PRIP(ntohl(*(int*)&address)));
+  return STRDUP(buf);
+}
+
+
+/* end of ip.c */


Property changes on: GNUnet/src/transports/upnp/ip.c
___________________________________________________________________
Name: svn:eol-style
   + native

Added: GNUnet/src/transports/upnp/ip.h
===================================================================
--- GNUnet/src/transports/upnp/ip.h     2006-12-29 14:43:29 UTC (rev 4098)
+++ GNUnet/src/transports/upnp/ip.h     2006-12-29 15:06:45 UTC (rev 4099)
@@ -0,0 +1,40 @@
+/*
+     This file is part of GNUnet.
+     (C) 2001, 2002, 2003, 2004, 2005 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 2, 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 transports/upnp/ip.h
+ * @brief
+ *
+ * @author Christian Grothoff
+ */
+
+#ifndef IP_H
+#define IP_H
+
+
+/**
+ * Get the IP address for the local machine.
+ * @return NULL on error
+ */
+char * gaim_upnp_get_internal_ip(struct GC_Configuration * cfg,
+                                struct GE_Context * ectx);
+
+
+#endif


Property changes on: GNUnet/src/transports/upnp/ip.h
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: GNUnet/src/transports/upnp/todo
===================================================================
--- GNUnet/src/transports/upnp/todo     2006-12-29 14:43:29 UTC (rev 4098)
+++ GNUnet/src/transports/upnp/todo     2006-12-29 15:06:45 UTC (rev 4099)
@@ -1,4 +1,4 @@
-1) Look into upnp shutdown sequence (fix gaim_timeout_add!)
-2) adjust configure to check for libxml2
-3) integrate with transports -- tricky bit: making it optional!
-4) remove glib dependency from upnp code!
+1) write testcase
+2) integrate with transports -- tricky bit: making it optional!
+3) remove glib dependency from upnp code!
+4) adjust configure to check for libxml2

Modified: GNUnet/src/transports/upnp/upnp.c
===================================================================
--- GNUnet/src/transports/upnp/upnp.c   2006-12-29 14:43:29 UTC (rev 4098)
+++ GNUnet/src/transports/upnp/upnp.c   2006-12-29 15:06:45 UTC (rev 4099)
@@ -32,24 +32,24 @@
 
 #include <curl/curl.h>
 
-/***************************************************************
-** General Defines                                             *
-****************************************************************/
+/**
+ * The xmlnode code has a bunch of memory leaks which
+ * occur with malformed XML input (i.e. XML input is
+ * incomplete).  Without this extra check, the code
+ * would frequently try to parse incomplete XML --
+ * with it, only if the response from the NAT box is
+ * odd or incorrect.  These leaks should be fixed
+ * eventually (best way I can think of is to make
+ * a memory pool for the xmlnodes and blow it away
+ * completely at the end).
+ */
+#define TEST_FOR_LEAKS NO
+
 #define HTTP_OK "200 OK"
-#define DEFAULT_HTTP_PORT 80
-#define DISCOVERY_TIMEOUT 1000
-
-/***************************************************************
-** Discovery/Description Defines                               *
-****************************************************************/
 #define NUM_UDP_ATTEMPTS 2
-
-/* Address and port of an SSDP request used for discovery */
 #define HTTPMU_HOST_ADDRESS "239.255.255.250"
 #define HTTPMU_HOST_PORT 1900
-
 #define SEARCH_REQUEST_DEVICE "urn:schemas-upnp-org:service:%s"
-
 #define SEARCH_REQUEST_STRING \
        "M-SEARCH * HTTP/1.1\r\n" \
        "MX: 2\r\n" \
@@ -57,19 +57,12 @@
        "MAN: \"ssdp:discover\"\r\n" \
        "ST: urn:schemas-upnp-org:service:%s\r\n" \
        "\r\n"
-
 #define WAN_IP_CONN_SERVICE "WANIPConnection:1"
 #define WAN_PPP_CONN_SERVICE "WANPPPConnection:1"
-
-/******************************************************************
-** Action Defines                                                 *
-*******************************************************************/
-
-#define HTTP_POST_SOAP_ACTION \
-       "SOAPACTION: \"urn:schemas-upnp-org:service:%s#%s\"\r\n"        \
-       "CONTENT-TYPE: text/xml ; charset=\"utf-8\"\r\n" \
-       "CONTENT-LENGTH: %" G_GSIZE_FORMAT "\r\n\r\n%s"
-
+#define HTTP_POST_SOAP_HEADER \
+        "SOAPACTION: \"urn:schemas-upnp-org:service:%s#%s\""
+#define HTTP_POST_SIZE_HEADER \
+        "CONTENT-LENGTH: %" G_GSIZE_FORMAT ""
 #define SOAP_ACTION \
        "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" \
        "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"; " \
@@ -80,10 +73,8 @@
            "</u:%s>\r\n" \
          "</s:Body>\r\n" \
        "</s:Envelope>"
-
 #define PORT_MAPPING_LEASE_TIME "0"
 #define PORT_MAPPING_DESCRIPTION "GNUNET_UPNP_PORT_FORWARD"
-
 #define ADD_PORT_MAPPING_PARAMS \
        "<NewRemoteHost></NewRemoteHost>\r\n" \
        "<NewExternalPort>%i</NewExternalPort>\r\n" \
@@ -97,7 +88,6 @@
        "<NewLeaseDuration>" \
        PORT_MAPPING_LEASE_TIME \
        "</NewLeaseDuration>\r\n"
-
 #define DELETE_PORT_MAPPING_PARAMS \
        "<NewRemoteHost></NewRemoteHost>\r\n" \
        "<NewExternalPort>%i</NewExternalPort>\r\n" \
@@ -132,7 +122,6 @@
   "",
 };
 
-
 /**
  * This is the signature used for functions that act as a callback
  * to CURL.
@@ -147,7 +136,7 @@
 static gboolean
 gaim_upnp_compare_device(const xmlnode* device, 
                         const gchar* deviceType) {
-  xmlnode* deviceTypeNode = xmlnode_get_child(device, "deviceType");
+  xmlnode * deviceTypeNode = xmlnode_get_child(device, "deviceType");
   char * tmp;
   gboolean ret;
   
@@ -186,37 +175,30 @@
   xmlnode *xmlRootNode, *serviceTypeNode, *controlURLNode, *baseURLNode;
   char *tmp;
   
-  /* make sure we have a valid http response */
-  if(g_strstr_len(httpResponse, len, HTTP_OK) == NULL) {
-    gaim_debug_error("upnp",
-                    "parse_description_response(): Failed In HTTP_OK\n");
-    return NULL;
-  }
-  
   /* find the root of the xml document */
-  if((xmlRoot = g_strstr_len(httpResponse, len, "<root")) == NULL) {
-    gaim_debug_error("upnp",
-                    "parse_description_response(): Failed finding root\n");
-    return NULL;
-  }
-  
+  xmlRoot = g_strstr_len(httpResponse, len, "<root");
+  if (xmlRoot == NULL)
+    return NULL;  
+#if TEST_FOR_LEAKS
+  if (g_strstr_len(httpResponse, len, "</root") == NULL) 
+    return NULL;  
+#endif
+
   /* create the xml root node */
-  if((xmlRootNode = xmlnode_from_str(xmlRoot,
-                                    len - (xmlRoot - httpResponse))) == NULL) {
-    gaim_debug_error("upnp",
-                    "parse_description_response(): Could not parse xml root 
node\n");
-    return NULL;
-  }
+  xmlRootNode = xmlnode_from_str(xmlRoot,
+                                len - (xmlRoot - httpResponse));
+  if (xmlRootNode == NULL) 
+    return NULL;  
   
   /* get the baseURL of the device */
-  if((baseURLNode = xmlnode_get_child(xmlRootNode, "URLBase")) != NULL) {
+  baseURLNode = xmlnode_get_child(xmlRootNode, "URLBase");
+  if (baseURLNode != NULL) {
     baseURL = xmlnode_get_data(baseURLNode);
   } else {
     baseURL = g_strdup(httpURL);
   }
-  
-  /* get the serviceType child that has the service type as its data */
-  
+ 
+  /* get the serviceType child that has the service type as its data */  
   /* get urn:schemas-upnp-org:device:InternetGatewayDevice:1 and its 
devicelist */
   serviceTypeNode = xmlnode_get_child(xmlRootNode, "device");
   while(!gaim_upnp_compare_device(serviceTypeNode,
@@ -225,16 +207,12 @@
     serviceTypeNode = xmlnode_get_next_twin(serviceTypeNode);
   }
   if(serviceTypeNode == NULL) {
-    gaim_debug_error("upnp",
-                    "parse_description_response(): could not get 
serviceTypeNode 1\n");
     g_free(baseURL);
     xmlnode_free(xmlRootNode);
     return NULL;
   }
   serviceTypeNode = xmlnode_get_child(serviceTypeNode, "deviceList");
   if(serviceTypeNode == NULL) {
-    gaim_debug_error("upnp",
-                    "parse_description_response(): could not get 
serviceTypeNode 2\n");
     g_free(baseURL);
     xmlnode_free(xmlRootNode);
     return NULL;
@@ -248,16 +226,12 @@
     serviceTypeNode = xmlnode_get_next_twin(serviceTypeNode);
   }
   if(serviceTypeNode == NULL) {
-    gaim_debug_error("upnp",
-                    "parse_description_response(): could not get 
serviceTypeNode 3\n");
     g_free(baseURL);
     xmlnode_free(xmlRootNode);
     return NULL;
   }
   serviceTypeNode = xmlnode_get_child(serviceTypeNode, "deviceList");
   if(serviceTypeNode == NULL) {
-    gaim_debug_error("upnp",
-                    "parse_description_response(): could not get 
serviceTypeNode 4\n");
     g_free(baseURL);
     xmlnode_free(xmlRootNode);
     return NULL;
@@ -270,16 +244,12 @@
     serviceTypeNode = xmlnode_get_next_twin(serviceTypeNode);
   }
   if(serviceTypeNode == NULL) {
-    gaim_debug_error("upnp",
-                    "parse_description_response(): could not get 
serviceTypeNode 5\n");
     g_free(baseURL);
     xmlnode_free(xmlRootNode);
     return NULL;
   }
   serviceTypeNode = xmlnode_get_child(serviceTypeNode, "serviceList");
   if(serviceTypeNode == NULL) {
-    gaim_debug_error("upnp",
-                    "parse_description_response(): could not get 
serviceTypeNode 6\n");
     g_free(baseURL);
     xmlnode_free(xmlRootNode);
     return NULL;
@@ -294,9 +264,7 @@
   }
   
   g_free(service);
-  if(serviceTypeNode == NULL) {
-    gaim_debug_error("upnp",
-                    "parse_description_response(): could not get 
serviceTypeNode 7\n");
+  if (serviceTypeNode == NULL) {
     g_free(baseURL);
     xmlnode_free(xmlRootNode);
     return NULL;
@@ -305,8 +273,6 @@
   /* get the controlURL of the service */
   if((controlURLNode = xmlnode_get_child(serviceTypeNode,
                                         "controlURL")) == NULL) {
-    gaim_debug_error("upnp",
-                    "parse_description_response(): Could not find 
controlURL\n");
     g_free(baseURL);
     xmlnode_free(xmlRootNode);
     return NULL;
@@ -315,7 +281,23 @@
   tmp = xmlnode_get_data(controlURLNode);
   if(baseURL && !gaim_str_has_prefix(tmp, "http://";) &&
      !gaim_str_has_prefix(tmp, "HTTP://")) {
-    controlURL = g_strdup_printf("%s%s", baseURL, tmp);
+    if (tmp[0] == '/') {
+      size_t len;
+      const char * end;
+      /* absolute path */
+      end = strstr(&baseURL[strlen("http://";)],
+                  "/");
+      if (end == NULL)
+       len = strlen(&baseURL[strlen("http://";)]);
+      else
+       len = end - &baseURL[strlen("http://";)];
+      controlURL = g_strdup_printf("http://%.*s%s";,
+                                  len,
+                                  &baseURL[strlen("http://";)],
+                                  tmp);
+    } else {
+      controlURL = g_strdup_printf("%s%s", baseURL, tmp);
+    }
     g_free(tmp);
   } else{
     controlURL = tmp;
@@ -335,7 +317,6 @@
                      CURL * curl) {
   int ret;
 
-  ret = CURLE_OK;
   CURL_EASY_SETOPT(curl,
                   CURLOPT_FAILONERROR,
                   1);
@@ -355,8 +336,6 @@
   CURL_EASY_SETOPT(curl,
                   CURLOPT_NOSIGNAL,
                   1);
-  if (ret != CURLE_OK)
-    return SYSERR;
   return OK;
 }
 
@@ -366,28 +345,29 @@
                                           const gchar* actionParams, 
                                           GaimUtilFetchUrlCallback cb,
                                           gpointer cb_data) {
-  gchar * soapMessage;
   CURL * curl; 
-  gchar * postfields;
   int ret;
+  gchar * soapHeader;
+  gchar * sizeHeader;
+  gchar * soapMessage;
+  struct curl_slist * headers = NULL;
 
+  GE_ASSERT(NULL, cb != NULL);
   if (0 != curl_global_init(CURL_GLOBAL_WIN32)) 
     return SYSERR;
-  /* set the soap message */
+  /* set the soap message */  
   soapMessage = g_strdup_printf(SOAP_ACTION, 
                                actionName,
                                control_info.service_type, 
                                actionParams, 
                                actionName);
-  postfields = g_strdup_printf(HTTP_POST_SOAP_ACTION, 
+  soapHeader = g_strdup_printf(HTTP_POST_SOAP_HEADER,
                               control_info.service_type, 
-                              actionName,
-                              strlen(soapMessage),
-                              soapMessage);
-  g_free(soapMessage);
+                              actionName);
+  sizeHeader = g_strdup_printf(HTTP_POST_SIZE_HEADER,
+                              strlen(soapMessage));
   curl = curl_easy_init();
   setup_curl(proxy, curl);
-  ret = CURLE_OK;
   CURL_EASY_SETOPT(curl,
                   CURLOPT_URL,
                   control_info.control_url);
@@ -396,26 +376,45 @@
                   cb);
   CURL_EASY_SETOPT(curl,
                   CURLOPT_WRITEDATA,
-                  cb_data);
+                  cb_data); 
   CURL_EASY_SETOPT(curl,
                   CURLOPT_POST,
                   1);
+  headers = curl_slist_append(headers, 
+                             "CONTENT-TYPE: text/xml ; charset=\"utf-8\"");
+  headers = curl_slist_append(headers, 
+                             soapHeader);
+  headers = curl_slist_append(headers, 
+                             sizeHeader);
   CURL_EASY_SETOPT(curl,
+                  CURLOPT_HTTPHEADER,
+                  headers);
+  CURL_EASY_SETOPT(curl,
                   CURLOPT_POSTFIELDS,
-                  postfields);
+                  soapMessage);
+  CURL_EASY_SETOPT(curl,
+                  CURLOPT_POSTFIELDSIZE,
+                  strlen(soapMessage));
   if (ret == CURLE_OK)
     ret = curl_easy_perform(curl);
+#if 0
   if (ret != CURLE_OK)
     GE_LOG(NULL,
           GE_ERROR | GE_ADMIN | GE_DEVELOPER | GE_BULK,
-          _("%s failed at %s:%d: `%s'\n"),
+          _("%s failed for url `%s' and post-data `%s' at %s:%d: `%s'\n"),
           "curl_easy_perform",
+          control_info.control_url,
+          soapMessage,
           __FILE__,
           __LINE__,
           curl_easy_strerror(ret));
+#endif
+  curl_slist_free_all(headers);
   curl_easy_cleanup(curl);
   curl_global_cleanup();
-  g_free(postfields);
+  g_free(sizeHeader);
+  g_free(soapMessage);
+  g_free(soapHeader);
   if (ret != CURLE_OK)
     return SYSERR;
   return OK;
@@ -468,6 +467,15 @@
   return len;
 }
 
+
+static size_t
+ignore_response(void *url_data, 
+               size_t size,
+               size_t nmemb,                 
+               gpointer user_data) {
+  return size * nmemb;
+}
+
 /**
  * Process downloaded bits of service description.
  */
@@ -539,7 +547,8 @@
 
 int
 gaim_upnp_discover(struct GE_Context * ectx,
-                  struct GC_Configuration * cfg) {
+                  struct GC_Configuration * cfg,
+                  int sock) {
   char * proxy;
   struct hostent* hp;
   struct sockaddr_in server;
@@ -559,9 +568,7 @@
         sizeof(UPnPDiscoveryData));
   if (control_info.status == GAIM_UPNP_STATUS_DISCOVERING) 
     return NO;
-  dd.sock = SOCKET(AF_INET, SOCK_DGRAM, 0);
-  if (dd.sock == -1) 
-    return SYSERR;
+  dd.sock = sock;
   hp = gethostbyname(HTTPMU_HOST_ADDRESS);
   if (hp == NULL) {
     CLOSE(dd.sock);
@@ -602,17 +609,11 @@
     } while ( ((errno == EINTR) || (errno == EAGAIN)) &&
              (GNUNET_SHUTDOWN_TEST() == NO));
     g_free(sendMessage);    
-    if (sentSuccess) {
-      gaim_timeout_add(DISCOVERY_TIMEOUT,
-                      gaim_upnp_discover_timeout, 
-                      &dd);
+    if (sentSuccess) 
       break;
-    }
   }
-  if (sentSuccess == FALSE) {
-    CLOSE(dd.sock);
-    return SYSERR;
-  }
+  if (sentSuccess == FALSE) 
+    return SYSERR;  
 
   /* try to read response */
   do {
@@ -628,7 +629,6 @@
     }
   } while ( (errno == EINTR) &&
            (GNUNET_SHUTDOWN_TEST() == NO) );
-  CLOSE(dd.sock);
 
   /* parse the response, and see if it was a success */
   if (g_strstr_len(buf, buf_len, HTTP_OK) == NULL) 
@@ -648,7 +648,12 @@
     return SYSERR;    
   dd.full_url = g_strndup(startDescURL,
                          endDescURL - startDescURL); 
-  proxy = NULL; /* FIXME */
+  proxy = NULL; 
+  GC_get_configuration_value_string(cfg,
+                                   "GNUNETD",
+                                   "HTTP-PROXY",
+                                   "",
+                                   &proxy);
   ret = gaim_upnp_parse_description(proxy,
                                    &dd);  
   g_free(dd.full_url);
@@ -665,6 +670,7 @@
         dd.buf_len,
         0);
   }
+  FREE(proxy);
   return ret;
 }
 
@@ -712,13 +718,21 @@
                                    portmap, 
                                    protocol);
   }  
-  proxy = NULL; /* FIXME! */
+  proxy = NULL; 
+  GC_get_configuration_value_string(cfg,
+                                   "GNUNETD",
+                                   "HTTP-PROXY",
+                                   "",
+                                   &proxy);
   ret = gaim_upnp_generate_action_message_and_send(proxy,
                                                   action_name,
                                                   action_params,
-                                                  NULL,
+                                                  &ignore_response,
                                                   NULL);
   
   g_free(action_params);
+  FREE(proxy);
   return ret; 
 }
+
+/* end of upnp.c */

Modified: GNUnet/src/transports/upnp/upnp.h
===================================================================
--- GNUnet/src/transports/upnp/upnp.h   2006-12-29 14:43:29 UTC (rev 4098)
+++ GNUnet/src/transports/upnp/upnp.h   2006-12-29 15:06:45 UTC (rev 4099)
@@ -46,7 +46,8 @@
  * The result will be cached for further use.
  */
 int gaim_upnp_discover(struct GE_Context * ectx,
-                      struct GC_Configuration * cfg);
+                      struct GC_Configuration * cfg,
+                      int sock);
 
 /**
  * Gets the IP address from a UPnP enabled IGD that sits on the local

Added: GNUnet/src/transports/upnp/upnpdemo.c
===================================================================
--- GNUnet/src/transports/upnp/upnpdemo.c       2006-12-29 14:43:29 UTC (rev 
4098)
+++ GNUnet/src/transports/upnp/upnpdemo.c       2006-12-29 15:06:45 UTC (rev 
4099)
@@ -0,0 +1,66 @@
+/*
+     This file is part of GNUnet.
+     (C) 2006 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 2, 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 src/transports/upnp/upnpdemo.c
+ * @brief Demo for UPnP
+ * @author Christian Grothoff
+ */
+
+#include "gnunet_util.h"
+#include "gnunet_util_config_impl.h"
+#include "gnunet_transport_upnp.h"
+#include "gnunet_util_error_loggers.h"
+#include "platform.h"
+
+int main(int argc,
+        const char *argv[]) {
+  struct GE_Context * ectx;
+  struct GC_Configuration * cfg;
+  IPaddr addr;
+
+  ectx = GE_create_context_stderr(NO,
+                                 GE_WARNING | GE_ERROR | GE_FATAL |
+                                 GE_USER | GE_ADMIN | GE_DEVELOPER |
+                                 GE_IMMEDIATE | GE_BULK);
+  GE_setDefaultContext(ectx);
+  cfg = GC_create_C_impl();
+  GE_ASSERT(ectx, cfg != NULL);
+  os_init(ectx);
+  gnunet_upnp_init(cfg, ectx);
+
+  printf("Testing UPnP.  Press CTRL-C to abort.\n");
+  while (GNUNET_SHUTDOWN_TEST() == NO) {
+    if (OK == gnunet_upnp_get_ip(2086,
+                                "TCP",
+                                &addr)) {
+      printf("UPnP returned external IP %u.%u.%u.%u\n",
+            PRIP(ntohl(*(int*)&addr)));
+    } else {
+      printf("No UPnP response (yet).\n");
+    }
+    PTHREAD_SLEEP(2 * cronSECONDS);
+  }
+  gnunet_upnp_done();
+  GC_free(cfg);
+  GE_free_context(ectx);
+  return 0;
+}
+
+/* end of upnpdemo.c */


Property changes on: GNUnet/src/transports/upnp/upnpdemo.c
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: GNUnet/src/transports/upnp/xmlnode.c
===================================================================
--- GNUnet/src/transports/upnp/xmlnode.c        2006-12-29 14:43:29 UTC (rev 
4098)
+++ GNUnet/src/transports/upnp/xmlnode.c        2006-12-29 15:06:45 UTC (rev 
4099)
@@ -291,8 +291,10 @@
        char **names;
        char *parent_name, *child_name;
 
-       g_return_val_if_fail(parent != NULL, NULL);
-       g_return_val_if_fail(name != NULL, NULL);
+       if (parent == NULL)
+         return NULL;
+       if (name == NULL)
+         return NULL;
 
        names = g_strsplit(name, "/", 2);
        parent_name = names[0];
@@ -322,9 +324,10 @@
 {
        GString *str = NULL;
        xmlnode *c;
+       
+       if (node == NULL)
+         return NULL;
 
-       g_return_val_if_fail(node != NULL, NULL);
-
        for(c = node->child; c; c = c->next) {
                if(c->type == XMLNODE_TYPE_DATA) {
                        if(!str)
@@ -347,7 +350,8 @@
        char *node_name, *esc, *esc2, *tab = NULL;
        gboolean need_end = FALSE, pretty = formatting;
 
-       g_return_val_if_fail(node != NULL, NULL);
+       if (node == NULL)
+         return NULL;
 
        if(pretty && depth) {
                tab = g_strnfill(depth, '\t');





reply via email to

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