[Top][All Lists]
[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*)𝔦
+
+ 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');
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [GNUnet-SVN] r4099 - GNUnet/src/transports/upnp,
grothoff <=