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'); _______________________________________________ GNUnet-SVN mailing list GNUnet-SVN@gnu.org http://lists.gnu.org/mailman/listinfo/gnunet-svn