From b94248cf8b37caaea9436d9973a6641eaeca90cb Mon Sep 17 00:00:00 2001 From: Moon Date: Sun, 25 Oct 2009 09:44:36 +0000 Subject: [PATCH] initial NAT lib commit (UPnP and NAT-PMP support) --- src/Makefile.am | 1 + src/include/gnunet_nat_lib.h | 99 ++++ src/nat/Makefile.am | 44 ++ src/nat/libnatpmp/Makefile.am | 16 + src/nat/libnatpmp/README | 4 + src/nat/libnatpmp/declspec.h | 14 + src/nat/libnatpmp/getgateway.c | 530 +++++++++++++++++ src/nat/libnatpmp/getgateway.h | 32 + src/nat/libnatpmp/natpmp.c | 405 +++++++++++++ src/nat/libnatpmp/natpmp.h | 200 +++++++ src/nat/miniupnp/Makefile.am | 31 + src/nat/miniupnp/README | 3 + src/nat/miniupnp/bsdqueue.h | 531 +++++++++++++++++ src/nat/miniupnp/codelength.h | 23 + src/nat/miniupnp/declspec.h | 14 + src/nat/miniupnp/igd_desc_parse.c | 126 ++++ src/nat/miniupnp/igd_desc_parse.h | 47 ++ src/nat/miniupnp/minisoap.c | 106 ++++ src/nat/miniupnp/minisoap.h | 14 + src/nat/miniupnp/minissdpc.c | 111 ++++ src/nat/miniupnp/minissdpc.h | 14 + src/nat/miniupnp/miniupnpc.c | 890 ++++++++++++++++++++++++++++ src/nat/miniupnp/miniupnpc.h | 120 ++++ src/nat/miniupnp/miniupnpcstrings.h | 14 + src/nat/miniupnp/miniwget.c | 224 +++++++ src/nat/miniupnp/miniwget.h | 28 + src/nat/miniupnp/minixml.c | 200 +++++++ src/nat/miniupnp/minixml.h | 37 ++ src/nat/miniupnp/upnpcommands.c | 606 +++++++++++++++++++ src/nat/miniupnp/upnpcommands.h | 189 ++++++ src/nat/miniupnp/upnpreplyparse.c | 122 ++++ src/nat/miniupnp/upnpreplyparse.h | 60 ++ src/nat/nat.c | 375 ++++++++++++ src/nat/natpmp.c | 297 ++++++++++ src/nat/natpmp.h | 44 ++ src/nat/upnp.c | 357 +++++++++++ src/nat/upnp.h | 43 ++ 37 files changed, 5971 insertions(+) create mode 100644 src/include/gnunet_nat_lib.h create mode 100644 src/nat/Makefile.am create mode 100644 src/nat/libnatpmp/Makefile.am create mode 100644 src/nat/libnatpmp/README create mode 100644 src/nat/libnatpmp/declspec.h create mode 100644 src/nat/libnatpmp/getgateway.c create mode 100644 src/nat/libnatpmp/getgateway.h create mode 100644 src/nat/libnatpmp/natpmp.c create mode 100644 src/nat/libnatpmp/natpmp.h create mode 100644 src/nat/miniupnp/Makefile.am create mode 100644 src/nat/miniupnp/README create mode 100644 src/nat/miniupnp/bsdqueue.h create mode 100644 src/nat/miniupnp/codelength.h create mode 100644 src/nat/miniupnp/declspec.h create mode 100644 src/nat/miniupnp/igd_desc_parse.c create mode 100644 src/nat/miniupnp/igd_desc_parse.h create mode 100644 src/nat/miniupnp/minisoap.c create mode 100644 src/nat/miniupnp/minisoap.h create mode 100644 src/nat/miniupnp/minissdpc.c create mode 100644 src/nat/miniupnp/minissdpc.h create mode 100644 src/nat/miniupnp/miniupnpc.c create mode 100644 src/nat/miniupnp/miniupnpc.h create mode 100644 src/nat/miniupnp/miniupnpcstrings.h create mode 100644 src/nat/miniupnp/miniwget.c create mode 100644 src/nat/miniupnp/miniwget.h create mode 100644 src/nat/miniupnp/minixml.c create mode 100644 src/nat/miniupnp/minixml.h create mode 100644 src/nat/miniupnp/upnpcommands.c create mode 100644 src/nat/miniupnp/upnpcommands.h create mode 100644 src/nat/miniupnp/upnpreplyparse.c create mode 100644 src/nat/miniupnp/upnpreplyparse.h create mode 100644 src/nat/nat.c create mode 100644 src/nat/natpmp.c create mode 100644 src/nat/natpmp.h create mode 100644 src/nat/upnp.c create mode 100644 src/nat/upnp.h diff --git a/src/Makefile.am b/src/Makefile.am index 9cb49e2cc..d89e4ca09 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -13,6 +13,7 @@ SUBDIRS = \ arm \ fragmentation \ hello \ + nat \ peerinfo \ statistics \ datacache \ diff --git a/src/include/gnunet_nat_lib.h b/src/include/gnunet_nat_lib.h new file mode 100644 index 000000000..f637bbd48 --- /dev/null +++ b/src/include/gnunet_nat_lib.h @@ -0,0 +1,99 @@ +/* + This file is part of GNUnet. + (C) 2007, 2008, 2009 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 include/gnunet_nat_lib.h + * @brief Library handling UPnP and NAT-PMP port forwarding and + * external IP address retrieval + * + * @author Milan Bouchet-Valat + */ + +#ifndef GNUNET_NAT_LIB_H +#define GNUNET_NAT_LIB_H 1 + +#include "platform.h" +#include "gnunet_util_lib.h" +#include "upnp.h" +#include "natpmp.h" + +#include + +/* Used to communicate with the UPnP and NAT-PMP plugins */ +typedef enum +{ + GNUNET_NAT_PORT_ERROR, + GNUNET_NAT_PORT_UNMAPPED, + GNUNET_NAT_PORT_UNMAPPING, + GNUNET_NAT_PORT_MAPPING, + GNUNET_NAT_PORT_MAPPED +} +GNUNET_NAT_port_forwarding; + +/** + * Signature of the callback passed to GNUNET_NAT_register. + * + * @cls closure + * @add_remove GNUNET_YES to mean the new public IP address, GNUNET_NO to mean + * the previous (now invalid) one + * @addr either the previous or the new public IP address + * @addrlen actual lenght of the address + */ +typedef void (*GNUNET_NAT_AddressCallback) (void *cls, int add_remove, + const struct sockaddr * addr, + socklen_t addrlen); + +typedef struct GNUNET_NAT_Handle GNUNET_NAT_Handle; + +/** + * Attempt to enable port redirection and detect public IP address contacting + * UPnP or NAT-PMP routers on the local network. Use @addr to specify to which + * of the local host's addresses should the external port be mapped. The port + * is taken from the corresponding sockaddr_in[6] field. + * + * @sched the sheduler used in the program + * @addr the local address packets should be redirected to + * @addrlen actual lenght of the address + * @callback function to call everytime the public IP address changes + * @callback_cls closure for @callback + */ +struct GNUNET_NAT_Handle *GNUNET_NAT_register (struct GNUNET_SCHEDULER_Handle + *sched, + const struct sockaddr *addr, + socklen_t addrlen, + GNUNET_NAT_AddressCallback + callback, void *callback_cls); + +/** + * Stop port redirection and public IP address detection for the given handle. + * This frees the handle, after having sent the needed commands to close open ports. + * + * @h the handle to stop + */ +void GNUNET_NAT_unregister (struct GNUNET_NAT_Handle *h); + +/** + * Compare the sin(6)_addr fields of AF_INET or AF_INET(6) sockaddr. + * @param a first sockaddr + * @param second sockaddr + * @returns 0 if addresses are equal, non-null value otherwise */ +int GNUNET_NAT_cmp_addr (const struct sockaddr *a, const struct sockaddr *b); + +#endif /* GNUNET_NAT_LIB_H */ diff --git a/src/nat/Makefile.am b/src/nat/Makefile.am new file mode 100644 index 000000000..dc881a302 --- /dev/null +++ b/src/nat/Makefile.am @@ -0,0 +1,44 @@ +SUBDIRS = miniupnp libnatpmp + +INCLUDES = -I$(top_srcdir)/src/include + +if MINGW + WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols +endif + +if USE_COVERAGE + AM_CFLAGS = -fprofile-arcs -ftest-coverage +endif + +lib_LTLIBRARIES = libgnunetnat.la + +libgnunetnat_la_SOURCES = \ + upnp.c upnp.h \ + natpmp.c natpmp.h \ + nat.c nat.h + +libgnunetnat_la_CFLAGS = \ + -I$(top_scrdir)/include + +libgnunetnat_la_LIBADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(GN_LIBINTL) @EXT_LIBS@ + +libgnunetnat_la_LDFLAGS = \ + $(GN_LIB_LDFLAGS) $(WINFLAGS) \ + -version-info 0:0:0 + +check_PROGRAMS = \ + test-nat + +TESTS = $(check_PROGRAMS) + +test_nat_SOURCES = \ + test_nat.c + +test_nat_LDADD = \ + $(top_builddir)/src/nat/libgnunetnat.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/nat/miniupnp/libminiupnp.a \ + $(top_builddir)/src/nat/libnatpmp/libnatpmp.a + diff --git a/src/nat/libnatpmp/Makefile.am b/src/nat/libnatpmp/Makefile.am new file mode 100644 index 000000000..923ab169d --- /dev/null +++ b/src/nat/libnatpmp/Makefile.am @@ -0,0 +1,16 @@ +noinst_LIBRARIES = libnatpmp.a + +AM_CPPFLAGS = -DENABLE_STRNATPMPERR + +libnatpmp_a_SOURCES = \ + getgateway.c \ + natpmp.c + +noinst_HEADERS = \ + declspec.h \ + getgateway.h \ + natpmp.h + +extra_DIST = \ + README \ + LICENSE diff --git a/src/nat/libnatpmp/README b/src/nat/libnatpmp/README new file mode 100644 index 000000000..50fdd1093 --- /dev/null +++ b/src/nat/libnatpmp/README @@ -0,0 +1,4 @@ +libnatpmp is written by Thomas Bernard. +Its homepage is http://miniupnp.tuxfamily.org/libnatpmp.html +This code is from the libnatpmp-20090310 snapshot + diff --git a/src/nat/libnatpmp/declspec.h b/src/nat/libnatpmp/declspec.h new file mode 100644 index 000000000..6c817977a --- /dev/null +++ b/src/nat/libnatpmp/declspec.h @@ -0,0 +1,14 @@ +#ifndef __DECLSPEC_H__ +#define __DECLSPEC_H__ + +#if defined(WIN32) && !defined(STATICLIB) +#ifdef NATPMP_EXPORTS +#define LIBSPEC __declspec(dllexport) +#else +#define LIBSPEC __declspec(dllimport) +#endif +#else +#define LIBSPEC +#endif + +#endif diff --git a/src/nat/libnatpmp/getgateway.c b/src/nat/libnatpmp/getgateway.c new file mode 100644 index 000000000..10e0f1e41 --- /dev/null +++ b/src/nat/libnatpmp/getgateway.c @@ -0,0 +1,530 @@ +/* $Id: getgateway.c,v 1.13 2009/03/10 10:15:31 nanard Exp $ */ +/* libnatpmp + * Copyright (c) 2007-2008, Thomas BERNARD + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include +#include +#include +#ifndef WIN32 +#include +#endif +#include +/* There is no portable method to get the default route gateway. + * So below are three differents functions implementing this. + * Parsing /proc/net/route is for linux. + * sysctl is the way to access such informations on BSD systems. + * Many systems should provide route information through raw PF_ROUTE + * sockets. */ +#ifdef __linux__ +#define USE_PROC_NET_ROUTE +#undef USE_SOCKET_ROUTE +#undef USE_SYSCTL_NET_ROUTE +#endif + +#ifdef BSD +#undef USE_PROC_NET_ROUTE +#define USE_SOCKET_ROUTE +#undef USE_SYSCTL_NET_ROUTE +#endif + +#ifdef __APPLE__ +#undef USE_PROC_NET_ROUTE +#undef USE_SOCKET_ROUTE +#define USE_SYSCTL_NET_ROUTE +#endif + +#if (defined(sun) && defined(__SVR4)) +#undef USE_PROC_NET_ROUTE +#define USE_SOCKET_ROUTE +#undef USE_SYSCTL_NET_ROUTE +#endif + +#ifdef WIN32 +#undef USE_PROC_NET_ROUTE +#undef USE_SOCKET_ROUTE +#undef USE_SYSCTL_NET_ROUTE +#define USE_WIN32_CODE +#endif + +#ifdef USE_PROC_NET_ROUTE +#include +#endif +#ifdef USE_SYSCTL_NET_ROUTE +#include +#include +#include +#include +#endif +#ifdef USE_SOCKET_ROUTE +#include +#include +#include +#include +#include +#endif +#ifdef WIN32 +#include +#include +#define MAX_KEY_LENGTH 255 +#define MAX_VALUE_LENGTH 16383 +#endif +#include "getgateway.h" + +#ifndef WIN32 +#define SUCCESS (0) +#define FAILED (-1) +#endif + +#ifdef USE_PROC_NET_ROUTE +int +getdefaultgateway (int *af, u_int8_t addr[16]) +{ + unsigned int tmp; + u_int8_t d[16]; + char buf[256]; + int line = 0; + FILE *f; + char *p; + int i; + f = fopen ("/proc/net/route", "r"); + if (!f) + return FAILED; + while (fgets (buf, sizeof (buf), f)) + { + if (line > 0) + { + p = buf; + while (*p && !isspace (*p)) + p++; + while (*p && isspace (*p)) + p++; + for (i = 0; i < 16; i++) + { + if (sscanf (p, "%2X", &tmp) < 1) + d[i] = tmp; + else + break; + } + + if (i == 8) /* IPv4 address on 8 hex chars */ + { + /* Move the 32 bits address to the end of the array */ + for (i = 4; i < 16; i++) + d[i] = 0; + + for (i = 0; i < 4; i++) + { + d[i+12] = d[i]; + d[i] = 0; + } + memcpy (addr, d, 16 * sizeof (u_int8_t)); + *af = AF_INET; + fclose (f); + return SUCCESS; + } + else if (i == 16) /* IPv6 address on 16 hex chars, + * or IPv4 address padded with 0 */ + { + memcpy (addr, d, 16 * sizeof (u_int8_t)); + /* Check at what byte the actual address starts */ + for (i = 0; i <= 12; i++) + if (addr[i]) break; + + if (i == 12) + { + *af = AF_INET; + fclose (f); + return SUCCESS; + } + else if (i == 0) + { + *af = AF_INET6; + fclose (f); + return SUCCESS; + } + } + } + line++; + } + /* default route not found ! */ + if (f) + fclose (f); + return FAILED; +} +#endif /* #ifdef USE_PROC_NET_ROUTE */ + + +#ifdef USE_SYSCTL_NET_ROUTE + +#define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) + +int +getdefaultgateway (int *af, u_int8_t addr[16]) +{ +#if 0 + /* net.route.0.inet.dump.0.0 ? */ + int mib[] = { CTL_NET, PF_ROUTE, 0, AF_INET, + NET_RT_DUMP, 0, 0 /*tableid */ + }; +#endif + /* net.route.0.inet.flags.gateway */ + int mib[] = { CTL_NET, PF_ROUTE, 0, AF_INET, + NET_RT_FLAGS, RTF_GATEWAY + }; + size_t l; + char *buf, *p; + struct rt_msghdr *rt; + struct sockaddr *sa; + struct sockaddr *sa_tab[RTAX_MAX]; + int i; + int r = FAILED; + if (sysctl (mib, sizeof (mib) / sizeof (int), 0, &l, 0, 0) < 0) + { + return FAILED; + } + if (l > 0) + { + buf = malloc (l); + if (sysctl (mib, sizeof (mib) / sizeof (int), buf, &l, 0, 0) < 0) + { + free (buf); + return FAILED; + } + for (p = buf; p < buf + l; p += rt->rtm_msglen) + { + rt = (struct rt_msghdr *) p; + sa = (struct sockaddr *) (rt + 1); + for (i = 0; i < RTAX_MAX; i++) + { + if (rt->rtm_addrs & (1 << i)) + { + sa_tab[i] = sa; + sa = + (struct sockaddr *) ((char *) sa + ROUNDUP (sa->sa_len)); + } + else + { + sa_tab[i] = NULL; + } + } + if (((rt->rtm_addrs & (RTA_DST | RTA_GATEWAY)) == + (RTA_DST | RTA_GATEWAY)) + && sa_tab[RTAX_DST]->sa_family == AF_INET + && sa_tab[RTAX_GATEWAY]->sa_family == AF_INET) + { + if (((struct sockaddr_in *) sa_tab[RTAX_DST])->sin_addr. + s_addr == 0) + { + *addr = + ((struct sockaddr_in *) (sa_tab[RTAX_GATEWAY]))->sin_addr. + s_addr; + *af = AF_INET; + r = SUCCESS; + } + } + else if (((rt->rtm_addrs & (RTA_DST | RTA_GATEWAY)) == + (RTA_DST | RTA_GATEWAY)) + && sa_tab[RTAX_DST]->sa_family == AF_INET6 + && sa_tab[RTAX_GATEWAY]->sa_family == AF_INET6) + { + if (((struct sockaddr_in6 *) sa_tab[RTAX_DST])->sin6_addr == 0) + { + *addr = + ((struct sockaddr_in6 *) (sa_tab[RTAX_GATEWAY]))->sin6_addr; + *af = AF_INET6; + r = SUCCESS; + } + } + } + free (buf); + } + return r; +} +#endif /* #ifdef USE_SYSCTL_NET_ROUTE */ + + +#ifdef USE_SOCKET_ROUTE +/* Thanks to Darren Kenny for this code */ +#define NEXTADDR(w, u) \ + if (rtm_addrs & (w)) {\ + l = sizeof(struct sockaddr); memmove(cp, &(u), l); cp += l;\ + } + +#define rtm m_rtmsg.m_rtm + +struct +{ + struct rt_msghdr m_rtm; + char m_space[512]; +} m_rtmsg; + +int +getdefaultgateway (int *af, u_int8_t addr[16]) +{ + int s, seq, l, rtm_addrs, i; + pid_t pid; + struct sockaddr so_dst, so_mask; + char *cp = m_rtmsg.m_space; + struct sockaddr *gate = NULL, *sa; + struct rt_msghdr *msg_hdr; + + pid = getpid (); + seq = 0; + rtm_addrs = RTA_DST | RTA_NETMASK; + + memset (&so_dst, 0, sizeof (so_dst)); + memset (&so_mask, 0, sizeof (so_mask)); + memset (&rtm, 0, sizeof (struct rt_msghdr)); + + rtm.rtm_type = RTM_GET; + rtm.rtm_flags = RTF_UP | RTF_GATEWAY; + rtm.rtm_version = RTM_VERSION; + rtm.rtm_seq = ++seq; + rtm.rtm_addrs = rtm_addrs; + + so_dst.sa_family = AF_INET; + so_mask.sa_family = AF_INET; + + NEXTADDR (RTA_DST, so_dst); + NEXTADDR (RTA_NETMASK, so_mask); + + rtm.rtm_msglen = l = cp - (char *) &m_rtmsg; + + s = socket (PF_ROUTE, SOCK_RAW, 0); + + if (write (s, (char *) &m_rtmsg, l) < 0) + { + close (s); + return FAILED; + } + + do + { + l = read (s, (char *) &m_rtmsg, sizeof (m_rtmsg)); + } + while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid)); + + close (s); + + msg_hdr = &rtm; + + cp = ((char *) (msg_hdr + 1)); + if (msg_hdr->rtm_addrs) + { + for (i = 1; i; i <<= 1) + if (i & msg_hdr->rtm_addrs) + { + sa = (struct sockaddr *) cp; + if (i == RTA_GATEWAY) + gate = sa; + + cp += sizeof (struct sockaddr); + } + } + else + { + return FAILED; + } + + + if (gate != NULL && ((struct sockaddr_in *) gate)->sa_family == AF_INET) + { + *addr = ((struct sockaddr_in *) gate)->sin_addr.s_addr; + *af = AF_INET; + return SUCCESS; + } + else if (gate != NULL && ((struct sockaddr_in *) gate)->sa_family == AF_INET6) + { + *addr = ((struct sockaddr_in6 *) gate)->sin6_addr.s6_addr; + *af = AF_INET6; + return SUCCESS; + } + else + { + return FAILED; + } +} +#endif /* #ifdef USE_SOCKET_ROUTE */ + +#ifdef USE_WIN32_CODE +int +getdefaultgateway (int *af, u_int8_t addr[16]) +{ + HKEY networkCardsKey; + HKEY networkCardKey; + HKEY interfacesKey; + HKEY interfaceKey; + DWORD i = 0; + DWORD numSubKeys = 0; + TCHAR keyName[MAX_KEY_LENGTH]; + DWORD keyNameLength = MAX_KEY_LENGTH; + TCHAR keyValue[MAX_VALUE_LENGTH]; + DWORD keyValueLength = MAX_VALUE_LENGTH; + DWORD keyValueType = REG_SZ; + TCHAR gatewayValue[MAX_VALUE_LENGTH]; + DWORD gatewayValueLength = MAX_VALUE_LENGTH; + DWORD gatewayValueType = REG_MULTI_SZ; + int done = 0; + + char networkCardsPath[] = + "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards"; + char interfacesPath[] = + "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces"; + + // The windows registry lists its primary network devices in the following location: + // HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkCards + // + // Each network device has its own subfolder, named with an index, with various properties: + // -NetworkCards + // -5 + // -Description = Broadcom 802.11n Network Adapter + // -ServiceName = {E35A72F8-5065-4097-8DFE-C7790774EE4D} + // -8 + // -Description = Marvell Yukon 88E8058 PCI-E Gigabit Ethernet Controller + // -ServiceName = {86226414-5545-4335-A9D1-5BD7120119AD} + // + // The above service name is the name of a subfolder within: + // HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces + // + // There may be more subfolders in this interfaces path than listed in the network cards path above: + // -Interfaces + // -{3a539854-6a70-11db-887c-806e6f6e6963} + // -DhcpIPAddress = 0.0.0.0 + // -[more] + // -{E35A72F8-5065-4097-8DFE-C7790774EE4D} + // -DhcpIPAddress = 10.0.1.4 + // -DhcpDefaultGateway = 10.0.1.1 + // -[more] + // -{86226414-5545-4335-A9D1-5BD7120119AD} + // -DhcpIpAddress = 10.0.1.5 + // -DhcpDefaultGateay = 10.0.1.1 + // -[more] + // + // In order to extract this information, we enumerate each network card, and extract the ServiceName value. + // This is then used to open the interface subfolder, and attempt to extract a DhcpDefaultGateway value. + // Once one is found, we're done. + // + // It may be possible to simply enumerate the interface folders until we find one with a DhcpDefaultGateway value. + // However, the technique used is the technique most cited on the web, and we assume it to be more correct. + + if (ERROR_SUCCESS != RegOpenKeyEx (HKEY_LOCAL_MACHINE, // Open registry key or predifined key + networkCardsPath, // Name of registry subkey to open + 0, // Reserved - must be zero + KEY_READ, // Mask - desired access rights + &networkCardsKey)) // Pointer to output key + { + // Unable to open network cards keys + return -1; + } + + if (ERROR_SUCCESS != RegOpenKeyEx (HKEY_LOCAL_MACHINE, // Open registry key or predefined key + interfacesPath, // Name of registry subkey to open + 0, // Reserved - must be zero + KEY_READ, // Mask - desired access rights + &interfacesKey)) // Pointer to output key + { + // Unable to open interfaces key + RegCloseKey (networkCardsKey); + return -1; + } + + // Figure out how many subfolders are within the NetworkCards folder + RegQueryInfoKey (networkCardsKey, NULL, NULL, NULL, &numSubKeys, NULL, NULL, + NULL, NULL, NULL, NULL, NULL); + + //printf( "Number of subkeys: %u\n", (unsigned int)numSubKeys); + + // Enumrate through each subfolder within the NetworkCards folder + for (i = 0; i < numSubKeys && !done; i++) + { + keyNameLength = MAX_KEY_LENGTH; + if (ERROR_SUCCESS == RegEnumKeyEx (networkCardsKey, // Open registry key + i, // Index of subkey to retrieve + keyName, // Buffer that receives the name of the subkey + &keyNameLength, // Variable that receives the size of the above buffer + NULL, // Reserved - must be NULL + NULL, // Buffer that receives the class string + NULL, // Variable that receives the size of the above buffer + NULL)) // Variable that receives the last write time of subkey + { + if (RegOpenKeyEx + (networkCardsKey, keyName, 0, KEY_READ, + &networkCardKey) == ERROR_SUCCESS) + { + keyValueLength = MAX_VALUE_LENGTH; + if (ERROR_SUCCESS == RegQueryValueEx (networkCardKey, // Open registry key + "ServiceName", // Name of key to query + NULL, // Reserved - must be NULL + &keyValueType, // Receives value type + keyValue, // Receives value + &keyValueLength)) // Receives value length in bytes + { + //printf("keyValue: %s\n", keyValue); + + if (RegOpenKeyEx + (interfacesKey, keyValue, 0, KEY_READ, + &interfaceKey) == ERROR_SUCCESS) + { + gatewayValueLength = MAX_VALUE_LENGTH; + if (ERROR_SUCCESS == RegQueryValueEx (interfaceKey, // Open registry key + "DhcpDefaultGateway", // Name of key to query + NULL, // Reserved - must be NULL + &gatewayValueType, // Receives value type + gatewayValue, // Receives value + &gatewayValueLength)) // Receives value length in bytes + { + // Check to make sure it's a string + if (gatewayValueType == REG_MULTI_SZ + || gatewayValueType == REG_SZ) + { + //printf("gatewayValue: %s\n", gatewayValue); + done = 1; + } + } + else if (ERROR_SUCCESS == RegQueryValueEx (interfaceKey, // Open registry key + "DefaultGateway", // Name of key to query + NULL, // Reserved - must be NULL + &gatewayValueType, // Receives value type + gatewayValue, // Receives value + &gatewayValueLength)) // Receives value length in bytes + { + // Check to make sure it's a string + if (gatewayValueType == REG_MULTI_SZ + || gatewayValueType == REG_SZ) + { + //printf("gatewayValue: %s\n", gatewayValue); + done = 1; + } + } + RegCloseKey (interfaceKey); + } + } + RegCloseKey (networkCardKey); + } + } + } + + RegCloseKey (interfacesKey); + RegCloseKey (networkCardsKey); + + if (done) + { + *addr = inet_addr (gatewayValue); + return 0; + } + + return -1; +} +#endif /* #ifdef USE_WIN32_CODE */ diff --git a/src/nat/libnatpmp/getgateway.h b/src/nat/libnatpmp/getgateway.h new file mode 100644 index 000000000..a9b2c82a9 --- /dev/null +++ b/src/nat/libnatpmp/getgateway.h @@ -0,0 +1,32 @@ +/* $Id: getgateway.h,v 1.3 2008/07/02 22:33:06 nanard Exp $ */ +/* libnatpmp + * Copyright (c) 2007, Thomas BERNARD + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#ifndef __GETGATEWAY_H__ +#define __GETGATEWAY_H__ + +#ifdef WIN32 +#include +#define in_addr_t uint32_t +#endif +#include "declspec.h" + +/* getdefaultgateway() : + * addr must point to an array of at least 16 u_int8 elements + * return value : + * 0 : success + * -1 : failure */ +LIBSPEC int getdefaultgateway (int *af, u_int8_t addr[16]); + +#endif diff --git a/src/nat/libnatpmp/natpmp.c b/src/nat/libnatpmp/natpmp.c new file mode 100644 index 000000000..6a94acca3 --- /dev/null +++ b/src/nat/libnatpmp/natpmp.c @@ -0,0 +1,405 @@ +/* $Id: natpmp.c,v 1.8 2008/07/02 22:33:06 nanard Exp $ */ +/* libnatpmp + * Copyright (c) 2007-2008, Thomas BERNARD + * http://miniupnp.free.fr/libnatpmp.html + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include +#include +#include +#ifdef WIN32 +#include +#include +#include +#define EWOULDBLOCK WSAEWOULDBLOCK +#define ECONNREFUSED WSAECONNREFUSED +#else +#include +#include +#include +#include +#include +#define closesocket close +#endif +#include "natpmp.h" +#include "getgateway.h" + +int +initnatpmp (natpmp_t * p) +{ +#ifdef WIN32 + u_long ioctlArg = 1; +#else + int flags; +#endif + int domain = AF_INET; + int gw_domain; + struct sockaddr_in addr; + struct sockaddr_in6 addr6; + + if (!p) + return NATPMP_ERR_INVALIDARGS; + + if (p->addr) + domain = (p->addr->sa_family == AF_INET) ? PF_INET : PF_INET6; + + memset (p, 0, sizeof (natpmp_t)); + p->s = socket (domain, SOCK_DGRAM, 0); + if (p->s < 0) + return NATPMP_ERR_SOCKETERROR; + /* If addr has been set, use it, else get the default from connect() */ + if (p->addr && bind (p->s, p->addr, p->addrlen) < 0) + return NATPMP_ERR_BINDERROR; +#ifdef WIN32 + if (ioctlsocket (p->s, FIONBIO, &ioctlArg) == SOCKET_ERROR) + return NATPMP_ERR_FCNTLERROR; +#else + if ((flags = fcntl (p->s, F_GETFL, 0)) < 0) + return NATPMP_ERR_FCNTLERROR; + if (fcntl (p->s, F_SETFL, flags | O_NONBLOCK) < 0) + return NATPMP_ERR_FCNTLERROR; +#endif + + if (getdefaultgateway (&gw_domain, p->gateway) < 0) + return NATPMP_ERR_CANNOTGETGATEWAY; + + if (domain != gw_domain) + return NATPMP_ERR_ADDRERROR; + + if (domain == AF_INET) + { + memset (&addr, 0, sizeof (addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons (NATPMP_PORT); + memcpy (&addr.sin_addr.s_addr, p->gateway, 4 * sizeof (u_int8_t)); +#ifdef HAVE_SOCKADDR_IN_SIN_LEN + addr.sin_len = sizeof (addr); +#endif + if (connect (p->s, (struct sockaddr *) &addr, sizeof (addr)) < 0) + return NATPMP_ERR_CONNECTERR; + } + else + { + memset (&addr6, 0, sizeof (addr6)); + addr6.sin6_family = AF_INET6; + addr6.sin6_port = htons (NATPMP_PORT); + memcpy (addr6.sin6_addr.s6_addr, p->gateway, 16 * sizeof (u_int8_t)); +#ifdef HAVE_SOCKADDR_IN_SIN_LEN + addr6.sin6_len = sizeof (addr6); +#endif + if (connect (p->s, (struct sockaddr *) &addr6, sizeof (addr6)) < 0) + return NATPMP_ERR_CONNECTERR; + } + + return 0; +} + +int +closenatpmp (natpmp_t * p) +{ + if (!p) + return NATPMP_ERR_INVALIDARGS; + if (closesocket (p->s) < 0) + return NATPMP_ERR_CLOSEERR; + return 0; +} + +static int +sendpendingrequest (natpmp_t * p) +{ + int r; +/* struct sockaddr_in addr;*/ + if (!p) + return NATPMP_ERR_INVALIDARGS; +/* memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(NATPMP_PORT); + addr.sin_addr.s_addr = p->gateway; + r = (int)sendto(p->s, p->pending_request, p->pending_request_len, 0, + (struct sockaddr *)&addr, sizeof(addr));*/ + r = (int) send (p->s, p->pending_request, p->pending_request_len, 0); + return (r < 0) ? NATPMP_ERR_SENDERR : r; +} + +static int +sendnatpmprequest (natpmp_t * p) +{ + int n; + if (!p) + return NATPMP_ERR_INVALIDARGS; + /* TODO : check if no request is allready pending */ + p->has_pending_request = 1; + p->try_number = 1; + n = sendpendingrequest (p); + gettimeofday (&p->retry_time, NULL); // check errors ! + p->retry_time.tv_usec += 250000; /* add 250ms */ + if (p->retry_time.tv_usec >= 1000000) + { + p->retry_time.tv_usec -= 1000000; + p->retry_time.tv_sec++; + } + return n; +} + +int +getnatpmprequesttimeout (natpmp_t * p, struct timeval *timeout) +{ + struct timeval now; + if (!p || !timeout) + return NATPMP_ERR_INVALIDARGS; + if (!p->has_pending_request) + return NATPMP_ERR_NOPENDINGREQ; + if (gettimeofday (&now, NULL) < 0) + return NATPMP_ERR_GETTIMEOFDAYERR; + timeout->tv_sec = p->retry_time.tv_sec - now.tv_sec; + timeout->tv_usec = p->retry_time.tv_usec - now.tv_usec; + if (timeout->tv_usec < 0) + { + timeout->tv_usec += 1000000; + timeout->tv_sec--; + } + return 0; +} + +int +sendpublicaddressrequest (natpmp_t * p) +{ + if (!p) + return NATPMP_ERR_INVALIDARGS; + //static const unsigned char request[] = { 0, 0 }; + p->pending_request[0] = 0; + p->pending_request[1] = 0; + p->pending_request_len = 2; + // TODO: return 0 instead of sizeof(request) ?? + return sendnatpmprequest (p); +} + +int +sendnewportmappingrequest (natpmp_t * p, int protocol, + uint16_t privateport, uint16_t publicport, + uint32_t lifetime) +{ + if (!p + || (protocol != NATPMP_PROTOCOL_TCP && protocol != NATPMP_PROTOCOL_UDP)) + return NATPMP_ERR_INVALIDARGS; + p->pending_request[0] = 0; + p->pending_request[1] = protocol; + p->pending_request[2] = 0; + p->pending_request[3] = 0; + *((uint16_t *) (p->pending_request + 4)) = htons (privateport); + *((uint16_t *) (p->pending_request + 6)) = htons (publicport); + *((uint32_t *) (p->pending_request + 8)) = htonl (lifetime); + p->pending_request_len = 12; + return sendnatpmprequest (p); +} + +static int +readnatpmpresponse (natpmp_t * p, natpmpresp_t * response) +{ + unsigned char buf[16]; + struct sockaddr_storage addr; + socklen_t addrlen = sizeof (addr); + int n; + if (!p) + return NATPMP_ERR_INVALIDARGS; + n = recvfrom (p->s, buf, sizeof (buf), 0, + (struct sockaddr *) &addr, &addrlen); + if (n < 0) + switch (errno) + { + /*case EAGAIN: */ + case EWOULDBLOCK: + n = NATPMP_TRYAGAIN; + break; + case ECONNREFUSED: + n = NATPMP_ERR_NOGATEWAYSUPPORT; + break; + default: + n = NATPMP_ERR_RECVFROM; + } + /* check that addr is correct (= gateway) */ + else if (addr.ss_family == AF_INET && memcmp (&((struct sockaddr_in *) &addr)->sin_addr.s_addr, p->gateway, 4 * sizeof (u_int8_t)) == 0) + n = NATPMP_ERR_WRONGPACKETSOURCE; + else if (addr.ss_family == AF_INET6 && memcmp (((struct sockaddr_in6 *) &addr)->sin6_addr.s6_addr, p->gateway, 16 * sizeof (u_int8_t)) == 0) + n = NATPMP_ERR_WRONGPACKETSOURCE; + else + { + response->resultcode = ntohs (*((uint16_t *) (buf + 2))); + response->epoch = ntohl (*((uint32_t *) (buf + 4))); + if (buf[0] != 0) + n = NATPMP_ERR_UNSUPPORTEDVERSION; + else if (buf[1] < 128 || buf[1] > 130) + n = NATPMP_ERR_UNSUPPORTEDOPCODE; + else if (response->resultcode != 0) + { + switch (response->resultcode) + { + case 1: + n = NATPMP_ERR_UNSUPPORTEDVERSION; + break; + case 2: + n = NATPMP_ERR_NOTAUTHORIZED; + break; + case 3: + n = NATPMP_ERR_NETWORKFAILURE; + break; + case 4: + n = NATPMP_ERR_OUTOFRESOURCES; + break; + case 5: + n = NATPMP_ERR_UNSUPPORTEDOPCODE; + break; + default: + n = NATPMP_ERR_UNDEFINEDERROR; + } + } + else + { + response->type = buf[1] & 0x7f; + if (buf[1] == 128) + //response->publicaddress.addr = *((uint32_t *)(buf + 8)); + response->pnu.publicaddress.addr.s_addr = + *((uint32_t *) (buf + 8)); + else + { + response->pnu.newportmapping.privateport = + ntohs (*((uint16_t *) (buf + 8))); + response->pnu.newportmapping.mappedpublicport = + ntohs (*((uint16_t *) (buf + 10))); + response->pnu.newportmapping.lifetime = + ntohl (*((uint32_t *) (buf + 12))); + } + n = 0; + } + } + return n; +} + +int +readnatpmpresponseorretry (natpmp_t * p, natpmpresp_t * response) +{ + int n; + if (!p || !response) + return NATPMP_ERR_INVALIDARGS; + if (!p->has_pending_request) + return NATPMP_ERR_NOPENDINGREQ; + n = readnatpmpresponse (p, response); + if (n < 0) + { + if (n == NATPMP_TRYAGAIN) + { + struct timeval now; + gettimeofday (&now, NULL); // check errors ! + if (timercmp (&now, &p->retry_time, >=)) + { + int delay, r; + if (p->try_number >= 9) + { + return NATPMP_ERR_NOGATEWAYSUPPORT; + } + /*printf("retry! %d\n", p->try_number); */ + delay = 250 * (1 << p->try_number); // ms + /*for(i=0; itry_number; i++) + delay += delay; */ + p->retry_time.tv_sec += (delay / 1000); + p->retry_time.tv_usec += (delay % 1000) * 1000; + if (p->retry_time.tv_usec >= 1000000) + { + p->retry_time.tv_usec -= 1000000; + p->retry_time.tv_sec++; + } + p->try_number++; + r = sendpendingrequest (p); + if (r < 0) + return r; + } + } + } + else + { + p->has_pending_request = 0; + } + return n; +} + +#ifdef ENABLE_STRNATPMPERR +const char * +strnatpmperr (int r) +{ + const char *s; + switch (r) + { + case NATPMP_ERR_INVALIDARGS: + s = "invalid arguments"; + break; + case NATPMP_ERR_SOCKETERROR: + s = "socket() failed"; + break; + case NATPMP_ERR_CANNOTGETGATEWAY: + s = "cannot get default gateway ip address"; + break; + case NATPMP_ERR_CLOSEERR: +#ifdef WIN32 + s = "closesocket() failed"; +#else + s = "close() failed"; +#endif + break; + case NATPMP_ERR_RECVFROM: + s = "recvfrom() failed"; + break; + case NATPMP_ERR_NOPENDINGREQ: + s = "no pending request"; + break; + case NATPMP_ERR_NOGATEWAYSUPPORT: + s = "the gateway does not support nat-pmp"; + break; + case NATPMP_ERR_CONNECTERR: + s = "connect() failed"; + break; + case NATPMP_ERR_WRONGPACKETSOURCE: + s = "packet not received from the default gateway"; + break; + case NATPMP_ERR_SENDERR: + s = "send() failed"; + break; + case NATPMP_ERR_FCNTLERROR: + s = "fcntl() failed"; + break; + case NATPMP_ERR_GETTIMEOFDAYERR: + s = "gettimeofday() failed"; + break; + case NATPMP_ERR_UNSUPPORTEDVERSION: + s = "unsupported nat-pmp version error from server"; + break; + case NATPMP_ERR_UNSUPPORTEDOPCODE: + s = "unsupported nat-pmp opcode error from server"; + break; + case NATPMP_ERR_UNDEFINEDERROR: + s = "undefined nat-pmp server error"; + break; + case NATPMP_ERR_NOTAUTHORIZED: + s = "not authorized"; + break; + case NATPMP_ERR_NETWORKFAILURE: + s = "network failure"; + break; + case NATPMP_ERR_OUTOFRESOURCES: + s = "nat-pmp server out of resources"; + break; + default: + s = "Unknown libnatpmp error"; + } + return s; +} +#endif diff --git a/src/nat/libnatpmp/natpmp.h b/src/nat/libnatpmp/natpmp.h new file mode 100644 index 000000000..70d903101 --- /dev/null +++ b/src/nat/libnatpmp/natpmp.h @@ -0,0 +1,200 @@ +/* $Id: natpmp.h,v 1.11 2009/02/27 22:38:05 nanard Exp $ */ +/* libnatpmp + * Copyright (c) 2007-2008, Thomas BERNARD + * http://miniupnp.free.fr/libnatpmp.html + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#ifndef __NATPMP_H__ +#define __NATPMP_H__ + +/* NAT-PMP Port as defined by the NAT-PMP draft */ +#define NATPMP_PORT (5351) + +#include +#include +#ifdef WIN32 +#include +#include +#define in_addr_t uint32_t +#include "declspec.h" +#else +#define LIBSPEC +#include +#include +#endif + +typedef struct +{ + int s; /* socket */ + struct sockaddr *addr; + socklen_t addrlen; + u_int8_t gateway[16]; /* default gateway (IPv4 or IPv6) */ + int has_pending_request; + unsigned char pending_request[12]; + int pending_request_len; + int try_number; + struct timeval retry_time; +} natpmp_t; + +typedef struct +{ + uint16_t type; /* NATPMP_RESPTYPE_* */ + uint16_t resultcode; /* NAT-PMP response code */ + uint32_t epoch; /* Seconds since start of epoch */ + union + { + struct + { + //in_addr_t addr; + struct in_addr addr; + } publicaddress; + struct + { + uint16_t privateport; + uint16_t mappedpublicport; + uint32_t lifetime; + } newportmapping; + } pnu; +} natpmpresp_t; + +/* possible values for type field of natpmpresp_t */ +#define NATPMP_RESPTYPE_PUBLICADDRESS (0) +#define NATPMP_RESPTYPE_UDPPORTMAPPING (1) +#define NATPMP_RESPTYPE_TCPPORTMAPPING (2) + +/* Values to pass to sendnewportmappingrequest() */ +#define NATPMP_PROTOCOL_UDP (1) +#define NATPMP_PROTOCOL_TCP (2) + +/* return values */ +/* NATPMP_ERR_INVALIDARGS : invalid arguments passed to the function */ +#define NATPMP_ERR_INVALIDARGS (-1) +/* NATPMP_ERR_SOCKETERROR : socket() failed. check errno for details */ +#define NATPMP_ERR_SOCKETERROR (-2) +/* NATPMP_ERR_CANNOTGETGATEWAY : can't get default gateway IP */ +#define NATPMP_ERR_CANNOTGETGATEWAY (-3) +/* NATPMP_ERR_CLOSEERR : close() failed. check errno for details */ +#define NATPMP_ERR_CLOSEERR (-4) +/* NATPMP_ERR_RECVFROM : recvfrom() failed. check errno for details */ +#define NATPMP_ERR_RECVFROM (-5) +/* NATPMP_ERR_NOPENDINGREQ : readnatpmpresponseorretry() called while + * no NAT-PMP request was pending */ +#define NATPMP_ERR_NOPENDINGREQ (-6) +/* NATPMP_ERR_NOGATEWAYSUPPORT : the gateway does not support NAT-PMP */ +#define NATPMP_ERR_NOGATEWAYSUPPORT (-7) +/* NATPMP_ERR_CONNECTERR : connect() failed. check errno for details */ +#define NATPMP_ERR_CONNECTERR (-8) +/* NATPMP_ERR_WRONGPACKETSOURCE : packet not received from the network gateway */ +#define NATPMP_ERR_WRONGPACKETSOURCE (-9) +/* NATPMP_ERR_SENDERR : send() failed. check errno for details */ +#define NATPMP_ERR_SENDERR (-10) +/* NATPMP_ERR_FCNTLERROR : fcntl() failed. check errno for details */ +#define NATPMP_ERR_FCNTLERROR (-11) +/* NATPMP_ERR_GETTIMEOFDAYERR : gettimeofday() failed. check errno for details */ +#define NATPMP_ERR_GETTIMEOFDAYERR (-12) +/* NATPMP_ERR_BINDERROR : bind() failed. check errno for details */ +#define NATPMP_ERR_BINDERROR (-13) +/* NATPMP_ERR_ADDRERROR : gateway does not use the same inet protocol as the passed address */ +#define NATPMP_ERR_ADDRERROR (-14) + +/* */ +#define NATPMP_ERR_UNSUPPORTEDVERSION (-15) +#define NATPMP_ERR_UNSUPPORTEDOPCODE (-16) + +/* Errors from the server : */ +#define NATPMP_ERR_UNDEFINEDERROR (-49) +#define NATPMP_ERR_NOTAUTHORIZED (-51) +#define NATPMP_ERR_NETWORKFAILURE (-52) +#define NATPMP_ERR_OUTOFRESOURCES (-53) + +/* NATPMP_TRYAGAIN : no data available for the moment. try again later */ +#define NATPMP_TRYAGAIN (-100) + +/* initnatpmp() + * initialize a natpmp_t object + * Return values : + * 0 = OK + * NATPMP_ERR_INVALIDARGS + * NATPMP_ERR_SOCKETERROR + * NATPMP_ERR_FCNTLERROR + * NATPMP_ERR_CANNOTGETGATEWAY + * NATPMP_ERR_CONNECTERR */ +LIBSPEC int initnatpmp (natpmp_t * p); + +/* closenatpmp() + * close resources associated with a natpmp_t object + * Return values : + * 0 = OK + * NATPMP_ERR_INVALIDARGS + * NATPMP_ERR_CLOSEERR */ +LIBSPEC int closenatpmp (natpmp_t * p); + +/* sendpublicaddressrequest() + * send a public address NAT-PMP request to the network gateway + * Return values : + * 2 = OK (size of the request) + * NATPMP_ERR_INVALIDARGS + * NATPMP_ERR_SENDERR */ +LIBSPEC int sendpublicaddressrequest (natpmp_t * p); + +/* sendnewportmappingrequest() + * send a new port mapping NAT-PMP request to the network gateway + * Arguments : + * protocol is either NATPMP_PROTOCOL_TCP or NATPMP_PROTOCOL_UDP, + * lifetime is in seconds. + * To remove a port mapping, set lifetime to zero. + * To remove all port mappings to the host, set lifetime and both ports + * to zero. + * Return values : + * 12 = OK (size of the request) + * NATPMP_ERR_INVALIDARGS + * NATPMP_ERR_SENDERR */ +LIBSPEC int sendnewportmappingrequest (natpmp_t * p, int protocol, + uint16_t privateport, + uint16_t publicport, + uint32_t lifetime); + +/* getnatpmprequesttimeout() + * fills the timeval structure with the timeout duration of the + * currently pending NAT-PMP request. + * Return values : + * 0 = OK + * NATPMP_ERR_INVALIDARGS + * NATPMP_ERR_GETTIMEOFDAYERR + * NATPMP_ERR_NOPENDINGREQ */ +LIBSPEC int getnatpmprequesttimeout (natpmp_t * p, struct timeval *timeout); + +/* readnatpmpresponseorretry() + * fills the natpmpresp_t structure if possible + * Return values : + * 0 = OK + * NATPMP_TRYAGAIN + * NATPMP_ERR_INVALIDARGS + * NATPMP_ERR_NOPENDINGREQ + * NATPMP_ERR_NOGATEWAYSUPPORT + * NATPMP_ERR_RECVFROM + * NATPMP_ERR_WRONGPACKETSOURCE + * NATPMP_ERR_UNSUPPORTEDVERSION + * NATPMP_ERR_UNSUPPORTEDOPCODE + * NATPMP_ERR_NOTAUTHORIZED + * NATPMP_ERR_NETWORKFAILURE + * NATPMP_ERR_OUTOFRESOURCES + * NATPMP_ERR_UNSUPPORTEDOPCODE + * NATPMP_ERR_UNDEFINEDERROR */ +LIBSPEC int readnatpmpresponseorretry (natpmp_t * p, natpmpresp_t * response); + +#ifdef ENABLE_STRNATPMPERR +LIBSPEC const char *strnatpmperr (int t); +#endif + +#endif diff --git a/src/nat/miniupnp/Makefile.am b/src/nat/miniupnp/Makefile.am new file mode 100644 index 000000000..cd5e8c7b8 --- /dev/null +++ b/src/nat/miniupnp/Makefile.am @@ -0,0 +1,31 @@ +noinst_LIBRARIES = libminiupnp.a + +AM_CPPFLAGS = -DNDEBUG + +libminiupnp_a_SOURCES = \ + igd_desc_parse.c \ + minisoap.c \ + minissdpc.c \ + miniupnpc.c \ + miniwget.c \ + minixml.c \ + upnpcommands.c \ + upnpreplyparse.c + +noinst_HEADERS = \ + bsdqueue.h \ + codelength.h \ + declspec.h \ + igd_desc_parse.h \ + minisoap.h \ + minissdpc.h \ + miniupnpc.h \ + miniupnpcstrings.h \ + miniwget.h \ + minixml.h \ + upnpcommands.h \ + upnpreplyparse.h + +extra_DIST = \ + README \ + LICENSE diff --git a/src/nat/miniupnp/README b/src/nat/miniupnp/README new file mode 100644 index 000000000..45aec9084 --- /dev/null +++ b/src/nat/miniupnp/README @@ -0,0 +1,3 @@ +MiniUPnP is written by Thomas Bernard. +Its homepage is http://miniupnp.free.fr/ +This is from miniupnpc-1.3.tar.gz diff --git a/src/nat/miniupnp/bsdqueue.h b/src/nat/miniupnp/bsdqueue.h new file mode 100644 index 000000000..f763172c4 --- /dev/null +++ b/src/nat/miniupnp/bsdqueue.h @@ -0,0 +1,531 @@ +/* $OpenBSD: queue.h,v 1.31 2005/11/25 08:06:25 otto Exp $ */ +/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ + +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +/* + * This file defines five types of data structures: singly-linked lists, + * lists, simple queues, tail queues, and circular queues. + * + * + * A singly-linked list is headed by a single forward pointer. The elements + * are singly linked for minimum space and pointer manipulation overhead at + * the expense of O(n) removal for arbitrary elements. New elements can be + * added to the list after an existing element or at the head of the list. + * Elements being removed from the head of the list should use the explicit + * macro for this purpose for optimum efficiency. A singly-linked list may + * only be traversed in the forward direction. Singly-linked lists are ideal + * for applications with large datasets and few or no removals or for + * implementing a LIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A simple queue is headed by a pair of pointers, one the head of the + * list and the other to the tail of the list. The elements are singly + * linked to save space, so elements can only be removed from the + * head of the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the + * list. A simple queue may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * A circle queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the list. + * A circle queue may be traversed in either direction, but has a more + * complex end of list detection. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +#ifdef QUEUE_MACRO_DEBUG +#define _Q_INVALIDATE(a) (a) = ((void *)-1) +#else +#define _Q_INVALIDATE(a) +#endif + +/* + * Singly-linked List definitions. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#ifdef SLIST_ENTRY +#undef SLIST_ENTRY +#endif + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List access methods. + */ +#define SLIST_FIRST(head) ((head)->slh_first) +#define SLIST_END(head) NULL +#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_FOREACH(var, head, field) \ + for((var) = SLIST_FIRST(head); \ + (var) != SLIST_END(head); \ + (var) = SLIST_NEXT(var, field)) + +#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \ + for ((varp) = &SLIST_FIRST((head)); \ + ((var) = *(varp)) != SLIST_END(head); \ + (varp) = &SLIST_NEXT((var), field)) + +/* + * Singly-linked List functions. + */ +#define SLIST_INIT(head) { \ + SLIST_FIRST(head) = SLIST_END(head); \ +} + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + (elm)->field.sle_next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = (elm); \ +} while (0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.sle_next = (head)->slh_first; \ + (head)->slh_first = (elm); \ +} while (0) + +#define SLIST_REMOVE_NEXT(head, elm, field) do { \ + (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \ +} while (0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + (head)->slh_first = (head)->slh_first->field.sle_next; \ +} while (0) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->slh_first; \ + \ + while (curelm->field.sle_next != (elm)) \ + curelm = curelm->field.sle_next; \ + curelm->field.sle_next = \ + curelm->field.sle_next->field.sle_next; \ + _Q_INVALIDATE((elm)->field.sle_next); \ + } \ +} while (0) + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List access methods + */ +#define LIST_FIRST(head) ((head)->lh_first) +#define LIST_END(head) NULL +#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_FOREACH(var, head, field) \ + for((var) = LIST_FIRST(head); \ + (var)!= LIST_END(head); \ + (var) = LIST_NEXT(var, field)) + +/* + * List functions. + */ +#define LIST_INIT(head) do { \ + LIST_FIRST(head) = LIST_END(head); \ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} while (0) + +#define LIST_REMOVE(elm, field) do { \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ + _Q_INVALIDATE((elm)->field.le_prev); \ + _Q_INVALIDATE((elm)->field.le_next); \ +} while (0) + +#define LIST_REPLACE(elm, elm2, field) do { \ + if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ + (elm2)->field.le_next->field.le_prev = \ + &(elm2)->field.le_next; \ + (elm2)->field.le_prev = (elm)->field.le_prev; \ + *(elm2)->field.le_prev = (elm2); \ + _Q_INVALIDATE((elm)->field.le_prev); \ + _Q_INVALIDATE((elm)->field.le_next); \ +} while (0) + +/* + * Simple queue definitions. + */ +#define SIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqh_first; /* first element */ \ + struct type **sqh_last; /* addr of last next element */ \ +} + +#define SIMPLEQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } + +#define SIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqe_next; /* next element */ \ +} + +/* + * Simple queue access methods. + */ +#define SIMPLEQ_FIRST(head) ((head)->sqh_first) +#define SIMPLEQ_END(head) NULL +#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) +#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) + +#define SIMPLEQ_FOREACH(var, head, field) \ + for((var) = SIMPLEQ_FIRST(head); \ + (var) != SIMPLEQ_END(head); \ + (var) = SIMPLEQ_NEXT(var, field)) + +/* + * Simple queue functions. + */ +#define SIMPLEQ_INIT(head) do { \ + (head)->sqh_first = NULL; \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (0) + +#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (head)->sqh_first = (elm); \ +} while (0) + +#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqe_next = NULL; \ + *(head)->sqh_last = (elm); \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (0) + +#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (listelm)->field.sqe_next = (elm); \ +} while (0) + +#define SIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (0) + +/* + * Tail queue definitions. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} + +/* + * tail queue access methods + */ +#define TAILQ_FIRST(head) ((head)->tqh_first) +#define TAILQ_END(head) NULL +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) +/* XXX */ +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) +#define TAILQ_EMPTY(head) \ + (TAILQ_FIRST(head) == TAILQ_END(head)) + +#define TAILQ_FOREACH(var, head, field) \ + for((var) = TAILQ_FIRST(head); \ + (var) != TAILQ_END(head); \ + (var) = TAILQ_NEXT(var, field)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for((var) = TAILQ_LAST(head, headname); \ + (var) != TAILQ_END(head); \ + (var) = TAILQ_PREV(var, headname, field)) + +/* + * Tail queue functions. + */ +#define TAILQ_INIT(head) do { \ + (head)->tqh_first = NULL; \ + (head)->tqh_last = &(head)->tqh_first; \ +} while (0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ + (head)->tqh_first->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} while (0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + (elm)->field.tqe_next = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_REMOVE(head, elm, field) do { \ + if (((elm)->field.tqe_next) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ + _Q_INVALIDATE((elm)->field.tqe_prev); \ + _Q_INVALIDATE((elm)->field.tqe_next); \ +} while (0) + +#define TAILQ_REPLACE(head, elm, elm2, field) do { \ + if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ + (elm2)->field.tqe_next->field.tqe_prev = \ + &(elm2)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm2)->field.tqe_next; \ + (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ + *(elm2)->field.tqe_prev = (elm2); \ + _Q_INVALIDATE((elm)->field.tqe_prev); \ + _Q_INVALIDATE((elm)->field.tqe_next); \ +} while (0) + +/* + * Circular queue definitions. + */ +#define CIRCLEQ_HEAD(name, type) \ +struct name { \ + struct type *cqh_first; /* first element */ \ + struct type *cqh_last; /* last element */ \ +} + +#define CIRCLEQ_HEAD_INITIALIZER(head) \ + { CIRCLEQ_END(&head), CIRCLEQ_END(&head) } + +#define CIRCLEQ_ENTRY(type) \ +struct { \ + struct type *cqe_next; /* next element */ \ + struct type *cqe_prev; /* previous element */ \ +} + +/* + * Circular queue access methods + */ +#define CIRCLEQ_FIRST(head) ((head)->cqh_first) +#define CIRCLEQ_LAST(head) ((head)->cqh_last) +#define CIRCLEQ_END(head) ((void *)(head)) +#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) +#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) +#define CIRCLEQ_EMPTY(head) \ + (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head)) + +#define CIRCLEQ_FOREACH(var, head, field) \ + for((var) = CIRCLEQ_FIRST(head); \ + (var) != CIRCLEQ_END(head); \ + (var) = CIRCLEQ_NEXT(var, field)) + +#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ + for((var) = CIRCLEQ_LAST(head); \ + (var) != CIRCLEQ_END(head); \ + (var) = CIRCLEQ_PREV(var, field)) + +/* + * Circular queue functions. + */ +#define CIRCLEQ_INIT(head) do { \ + (head)->cqh_first = CIRCLEQ_END(head); \ + (head)->cqh_last = CIRCLEQ_END(head); \ +} while (0) + +#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm)->field.cqe_next; \ + (elm)->field.cqe_prev = (listelm); \ + if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \ + (head)->cqh_last = (elm); \ + else \ + (listelm)->field.cqe_next->field.cqe_prev = (elm); \ + (listelm)->field.cqe_next = (elm); \ +} while (0) + +#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm); \ + (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ + if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \ + (head)->cqh_first = (elm); \ + else \ + (listelm)->field.cqe_prev->field.cqe_next = (elm); \ + (listelm)->field.cqe_prev = (elm); \ +} while (0) + +#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.cqe_next = (head)->cqh_first; \ + (elm)->field.cqe_prev = CIRCLEQ_END(head); \ + if ((head)->cqh_last == CIRCLEQ_END(head)) \ + (head)->cqh_last = (elm); \ + else \ + (head)->cqh_first->field.cqe_prev = (elm); \ + (head)->cqh_first = (elm); \ +} while (0) + +#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.cqe_next = CIRCLEQ_END(head); \ + (elm)->field.cqe_prev = (head)->cqh_last; \ + if ((head)->cqh_first == CIRCLEQ_END(head)) \ + (head)->cqh_first = (elm); \ + else \ + (head)->cqh_last->field.cqe_next = (elm); \ + (head)->cqh_last = (elm); \ +} while (0) + +#define CIRCLEQ_REMOVE(head, elm, field) do { \ + if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \ + (head)->cqh_last = (elm)->field.cqe_prev; \ + else \ + (elm)->field.cqe_next->field.cqe_prev = \ + (elm)->field.cqe_prev; \ + if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \ + (head)->cqh_first = (elm)->field.cqe_next; \ + else \ + (elm)->field.cqe_prev->field.cqe_next = \ + (elm)->field.cqe_next; \ + _Q_INVALIDATE((elm)->field.cqe_prev); \ + _Q_INVALIDATE((elm)->field.cqe_next); \ +} while (0) + +#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \ + if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \ + CIRCLEQ_END(head)) \ + (head).cqh_last = (elm2); \ + else \ + (elm2)->field.cqe_next->field.cqe_prev = (elm2); \ + if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \ + CIRCLEQ_END(head)) \ + (head).cqh_first = (elm2); \ + else \ + (elm2)->field.cqe_prev->field.cqe_next = (elm2); \ + _Q_INVALIDATE((elm)->field.cqe_prev); \ + _Q_INVALIDATE((elm)->field.cqe_next); \ +} while (0) + +#endif /* !_SYS_QUEUE_H_ */ diff --git a/src/nat/miniupnp/codelength.h b/src/nat/miniupnp/codelength.h new file mode 100644 index 000000000..8a5f49517 --- /dev/null +++ b/src/nat/miniupnp/codelength.h @@ -0,0 +1,23 @@ +/* $Id: codelength.h,v 1.1 2008/10/06 22:04:06 nanard Exp $ */ +/* Project : miniupnp + * Author : Thomas BERNARD + * copyright (c) 2005-2008 Thomas Bernard + * This software is subjet to the conditions detailed in the + * provided LICENCE file. */ +#ifndef __CODELENGTH_H__ +#define __CODELENGTH_H__ + +/* Encode length by using 7bit per Byte : + * Most significant bit of each byte specifies that the + * following byte is part of the code */ +#define DECODELENGTH(n, p) n = 0; \ + do { n = (n << 7) | (*p & 0x7f); } \ + while(*(p++)&0x80); + +#define CODELENGTH(n, p) if(n>=268435456) *(p++) = (n >> 28) | 0x80; \ + if(n>=2097152) *(p++) = (n >> 21) | 0x80; \ + if(n>=16384) *(p++) = (n >> 14) | 0x80; \ + if(n>=128) *(p++) = (n >> 7) | 0x80; \ + *(p++) = n & 0x7f; + +#endif diff --git a/src/nat/miniupnp/declspec.h b/src/nat/miniupnp/declspec.h new file mode 100644 index 000000000..c064bded2 --- /dev/null +++ b/src/nat/miniupnp/declspec.h @@ -0,0 +1,14 @@ +#ifndef __DECLSPEC_H__ +#define __DECLSPEC_H__ + +#if defined(WIN32) && !defined(STATICLIB) +#ifdef MINIUPNP_EXPORTS +#define LIBSPEC __declspec(dllexport) +#else +#define LIBSPEC __declspec(dllimport) +#endif +#else +#define LIBSPEC +#endif + +#endif diff --git a/src/nat/miniupnp/igd_desc_parse.c b/src/nat/miniupnp/igd_desc_parse.c new file mode 100644 index 000000000..deb53152c --- /dev/null +++ b/src/nat/miniupnp/igd_desc_parse.c @@ -0,0 +1,126 @@ +/* $Id: igd_desc_parse.c,v 1.8 2008/04/23 11:51:06 nanard Exp $ */ +/* Project : miniupnp + * http://miniupnp.free.fr/ + * Author : Thomas Bernard + * Copyright (c) 2005-2008 Thomas Bernard + * This software is subject to the conditions detailed in the + * LICENCE file provided in this distribution. + * */ +#include "igd_desc_parse.h" +#include +#include + +/* TODO : rewrite this code so it correctly handle descriptions with + * both WANIPConnection and/or WANPPPConnection */ + +/* Start element handler : + * update nesting level counter and copy element name */ +void +IGDstartelt (void *d, const char *name, int l) +{ + struct IGDdatas *datas = (struct IGDdatas *) d; + memcpy (datas->cureltname, name, l); + datas->cureltname[l] = '\0'; + datas->level++; + if ((l == 7) && !memcmp (name, "service", l)) + { + datas->controlurl_tmp[0] = '\0'; + datas->eventsuburl_tmp[0] = '\0'; + datas->scpdurl_tmp[0] = '\0'; + datas->servicetype_tmp[0] = '\0'; + } +} + +/* End element handler : + * update nesting level counter and update parser state if + * service element is parsed */ +void +IGDendelt (void *d, const char *name, int l) +{ + struct IGDdatas *datas = (struct IGDdatas *) d; + datas->level--; + /*printf("endelt %2d %.*s\n", datas->level, l, name); */ + if ((l == 7) && !memcmp (name, "service", l)) + { + /* + if( datas->state < 1 + && !strcmp(datas->servicetype, + // "urn:schemas-upnp-org:service:WANIPConnection:1") ) + "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1")) + datas->state ++; + */ + if (0 == strcmp (datas->servicetype_tmp, + "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1")) + { + memcpy (datas->controlurl_CIF, datas->controlurl_tmp, + MINIUPNPC_URL_MAXSIZE); + memcpy (datas->eventsuburl_CIF, datas->eventsuburl_tmp, + MINIUPNPC_URL_MAXSIZE); + memcpy (datas->scpdurl_CIF, datas->scpdurl_tmp, + MINIUPNPC_URL_MAXSIZE); + memcpy (datas->servicetype_CIF, datas->servicetype_tmp, + MINIUPNPC_URL_MAXSIZE); + } + else if (0 == strcmp (datas->servicetype_tmp, + "urn:schemas-upnp-org:service:WANIPConnection:1") + || 0 == strcmp (datas->servicetype_tmp, + "urn:schemas-upnp-org:service:WANPPPConnection:1")) + { + memcpy (datas->controlurl, datas->controlurl_tmp, + MINIUPNPC_URL_MAXSIZE); + memcpy (datas->eventsuburl, datas->eventsuburl_tmp, + MINIUPNPC_URL_MAXSIZE); + memcpy (datas->scpdurl, datas->scpdurl_tmp, MINIUPNPC_URL_MAXSIZE); + memcpy (datas->servicetype, datas->servicetype_tmp, + MINIUPNPC_URL_MAXSIZE); + } + } +} + +/* Data handler : + * copy data depending on the current element name and state */ +void +IGDdata (void *d, const char *data, int l) +{ + struct IGDdatas *datas = (struct IGDdatas *) d; + char *dstmember = 0; + /*printf("%2d %s : %.*s\n", + datas->level, datas->cureltname, l, data); */ + if (!strcmp (datas->cureltname, "URLBase")) + dstmember = datas->urlbase; + else if (!strcmp (datas->cureltname, "serviceType")) + dstmember = datas->servicetype_tmp; + else if (!strcmp (datas->cureltname, "controlURL")) + dstmember = datas->controlurl_tmp; + else if (!strcmp (datas->cureltname, "eventSubURL")) + dstmember = datas->eventsuburl_tmp; + else if (!strcmp (datas->cureltname, "SCPDURL")) + dstmember = datas->scpdurl_tmp; +/* else if( !strcmp(datas->cureltname, "deviceType") ) + dstmember = datas->devicetype_tmp;*/ + if (dstmember) + { + if (l >= MINIUPNPC_URL_MAXSIZE) + l = MINIUPNPC_URL_MAXSIZE - 1; + memcpy (dstmember, data, l); + dstmember[l] = '\0'; + } +} + +void +printIGD (struct IGDdatas *d) +{ + printf ("urlbase = %s\n", d->urlbase); + printf ("WAN Device (Common interface config) :\n"); + /*printf(" deviceType = %s\n", d->devicetype_CIF); */ + printf (" serviceType = %s\n", d->servicetype_CIF); + printf (" controlURL = %s\n", d->controlurl_CIF); + printf (" eventSubURL = %s\n", d->eventsuburl_CIF); + printf (" SCPDURL = %s\n", d->scpdurl_CIF); + printf ("WAN Connection Device (IP or PPP Connection):\n"); + /*printf(" deviceType = %s\n", d->devicetype); */ + printf (" servicetype = %s\n", d->servicetype); + printf (" controlURL = %s\n", d->controlurl); + printf (" eventSubURL = %s\n", d->eventsuburl); + printf (" SCPDURL = %s\n", d->scpdurl); +} diff --git a/src/nat/miniupnp/igd_desc_parse.h b/src/nat/miniupnp/igd_desc_parse.h new file mode 100644 index 000000000..5e7e24b86 --- /dev/null +++ b/src/nat/miniupnp/igd_desc_parse.h @@ -0,0 +1,47 @@ +/* $Id: igd_desc_parse.h,v 1.6 2008/04/23 11:51:07 nanard Exp $ */ +/* Project : miniupnp + * http://miniupnp.free.fr/ + * Author : Thomas Bernard + * Copyright (c) 2005-2008 Thomas Bernard + * This software is subject to the conditions detailed in the + * LICENCE file provided in this distribution. + * */ +#ifndef __IGD_DESC_PARSE_H__ +#define __IGD_DESC_PARSE_H__ + +/* Structure to store the result of the parsing of UPnP + * descriptions of Internet Gateway Devices */ +#define MINIUPNPC_URL_MAXSIZE (128) +struct IGDdatas +{ + char cureltname[MINIUPNPC_URL_MAXSIZE]; + char urlbase[MINIUPNPC_URL_MAXSIZE]; + int level; + /*int state; */ + /* "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" */ + char controlurl_CIF[MINIUPNPC_URL_MAXSIZE]; + char eventsuburl_CIF[MINIUPNPC_URL_MAXSIZE]; + char scpdurl_CIF[MINIUPNPC_URL_MAXSIZE]; + char servicetype_CIF[MINIUPNPC_URL_MAXSIZE]; + /*char devicetype_CIF[MINIUPNPC_URL_MAXSIZE]; */ + /* "urn:schemas-upnp-org:service:WANIPConnection:1" + * "urn:schemas-upnp-org:service:WANPPPConnection:1" */ + char controlurl[MINIUPNPC_URL_MAXSIZE]; + char eventsuburl[MINIUPNPC_URL_MAXSIZE]; + char scpdurl[MINIUPNPC_URL_MAXSIZE]; + char servicetype[MINIUPNPC_URL_MAXSIZE]; + /*char devicetype[MINIUPNPC_URL_MAXSIZE]; */ + /* tmp */ + char controlurl_tmp[MINIUPNPC_URL_MAXSIZE]; + char eventsuburl_tmp[MINIUPNPC_URL_MAXSIZE]; + char scpdurl_tmp[MINIUPNPC_URL_MAXSIZE]; + char servicetype_tmp[MINIUPNPC_URL_MAXSIZE]; + /*char devicetype_tmp[MINIUPNPC_URL_MAXSIZE]; */ +}; + +void IGDstartelt (void *, const char *, int); +void IGDendelt (void *, const char *, int); +void IGDdata (void *, const char *, int); +void printIGD (struct IGDdatas *); + +#endif diff --git a/src/nat/miniupnp/minisoap.c b/src/nat/miniupnp/minisoap.c new file mode 100644 index 000000000..a020f9a64 --- /dev/null +++ b/src/nat/miniupnp/minisoap.c @@ -0,0 +1,106 @@ +/* $Id: minisoap.c,v 1.16 2008/10/11 16:39:29 nanard Exp $ */ +/* Project : miniupnp + * Author : Thomas Bernard + * Copyright (c) 2005 Thomas Bernard + * This software is subject to the conditions detailed in the + * LICENCE file provided in this distribution. + * + * Minimal SOAP implementation for UPnP protocol. + */ +#include +#include +#ifdef WIN32 +#include +#include +#define snprintf _snprintf +#else +#include +#include +#include +#endif +#include "minisoap.h" +#include "miniupnpcstrings.h" + +/* only for malloc */ +#include + +#ifdef WIN32 +#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError()); +#else +#define PRINT_SOCKET_ERROR(x) perror(x) +#endif + +/* httpWrite sends the headers and the body to the socket + * and returns the number of bytes sent */ +static int +httpWrite (int fd, const char *body, int bodysize, + const char *headers, int headerssize) +{ + int n = 0; + /*n = write(fd, headers, headerssize); */ + /*if(bodysize>0) + n += write(fd, body, bodysize); */ + /* Note : my old linksys router only took into account + * soap request that are sent into only one packet */ + char *p; + /* TODO: AVOID MALLOC */ + p = malloc (headerssize + bodysize); + if (!p) + return 0; + memcpy (p, headers, headerssize); + memcpy (p + headerssize, body, bodysize); + /*n = write(fd, p, headerssize+bodysize); */ + n = send (fd, p, headerssize + bodysize, 0); + if (n < 0) + { + PRINT_SOCKET_ERROR ("send"); + } + /* disable send on the socket */ + /* draytek routers dont seems to like that... */ +#if 0 +#ifdef WIN32 + if (shutdown (fd, SD_SEND) < 0) + { +#else + if (shutdown (fd, SHUT_WR) < 0) + { /*SD_SEND */ +#endif + PRINT_SOCKET_ERROR ("shutdown"); + } +#endif + free (p); + return n; +} + +/* self explanatory */ +int +soapPostSubmit (int fd, + const char *url, + const char *host, + unsigned short port, const char *action, const char *body) +{ + int bodysize; + char headerbuf[512]; + int headerssize; + char portstr[8]; + bodysize = (int) strlen (body); + /* We are not using keep-alive HTTP connections. + * HTTP/1.1 needs the header Connection: close to do that. + * This is the default with HTTP/1.0 */ + /* Connection: Close is normally there only in HTTP/1.1 but who knows */ + portstr[0] = '\0'; + if (port != 80) + snprintf (portstr, sizeof (portstr), ":%hu", port); + headerssize = snprintf (headerbuf, sizeof (headerbuf), + "POST %s HTTP/1.1\r\n" +/* "POST %s HTTP/1.0\r\n"*/ + "Host: %s%s\r\n" "User-Agent: " OS_STRING ", UPnP/1.0, MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n" "Content-Length: %d\r\n" "Content-Type: text/xml\r\n" "SOAPAction: \"%s\"\r\n" "Connection: Close\r\n" "Cache-Control: no-cache\r\n" /* ??? */ + "Pragma: no-cache\r\n" + "\r\n", url, host, portstr, bodysize, action); +#ifdef DEBUG + printf ("SOAP request : headersize=%d bodysize=%d\n", + headerssize, bodysize); + /*printf("%s", headerbuf); */ +#endif + return httpWrite (fd, body, bodysize, headerbuf, headerssize); +} diff --git a/src/nat/miniupnp/minisoap.h b/src/nat/miniupnp/minisoap.h new file mode 100644 index 000000000..2505e4c20 --- /dev/null +++ b/src/nat/miniupnp/minisoap.h @@ -0,0 +1,14 @@ +/* $Id: minisoap.h,v 1.3 2006/11/19 22:32:34 nanard Exp $ */ +/* Project : miniupnp + * Author : Thomas Bernard + * Copyright (c) 2005 Thomas Bernard + * This software is subject to the conditions detailed in the + * LICENCE file provided in this distribution. */ +#ifndef __MINISOAP_H__ +#define __MINISOAP_H__ + +/*int httpWrite(int, const char *, int, const char *);*/ +int soapPostSubmit (int, const char *, const char *, unsigned short, + const char *, const char *); + +#endif diff --git a/src/nat/miniupnp/minissdpc.c b/src/nat/miniupnp/minissdpc.c new file mode 100644 index 000000000..3fe89ef94 --- /dev/null +++ b/src/nat/miniupnp/minissdpc.c @@ -0,0 +1,111 @@ +/* $Id: minissdpc.c,v 1.7 2008/12/18 17:45:48 nanard Exp $ */ +/* Project : miniupnp + * Author : Thomas BERNARD + * copyright (c) 2005-2008 Thomas Bernard + * This software is subjet to the conditions detailed in the + * provided LICENCE file. */ +/*#include */ +#include +#include +#include +#include +#include +#ifdef WIN32 +#include +#include +#include +#else +#include +#include +#endif + +#include "minissdpc.h" +#include "miniupnpc.h" + +#include "codelength.h" + +struct UPNPDev * +getDevicesFromMiniSSDPD (const char *devtype, const char *socketpath) +{ + struct UPNPDev *tmp; + struct UPNPDev *devlist = NULL; + unsigned char buffer[2048]; + ssize_t n; + unsigned char *p; + unsigned char *url; + unsigned int i; + unsigned int urlsize, stsize, usnsize, l; + int s; + struct sockaddr_un addr; + + s = socket (AF_UNIX, SOCK_STREAM, 0); + if (s < 0) + { + /*syslog(LOG_ERR, "socket(unix): %m"); */ + perror ("socket(unix)"); + return NULL; + } + addr.sun_family = AF_UNIX; + strncpy (addr.sun_path, socketpath, sizeof (addr.sun_path)); + if (connect (s, (struct sockaddr *) &addr, sizeof (struct sockaddr_un)) < 0) + { + /*syslog(LOG_WARNING, "connect(\"%s\"): %m", socketpath); */ + close (s); + return NULL; + } + stsize = strlen (devtype); + buffer[0] = 1; /* request type 1 : request devices/services by type */ + p = buffer + 1; + l = stsize; + CODELENGTH (l, p); + memcpy (p, devtype, stsize); + p += stsize; + if (write (s, buffer, p - buffer) < 0) + { + /*syslog(LOG_ERR, "write(): %m"); */ + perror ("minissdpc.c: write()"); + close (s); + return NULL; + } + n = read (s, buffer, sizeof (buffer)); + if (n <= 0) + { + perror ("minissdpc.c: read()"); + close (s); + return NULL; + } + p = buffer + 1; + for (i = 0; i < buffer[0]; i++) + { + if (p + 2 >= buffer + sizeof (buffer)) + break; + DECODELENGTH (urlsize, p); + if (p + urlsize + 2 >= buffer + sizeof (buffer)) + break; + url = p; + p += urlsize; + DECODELENGTH (stsize, p); + if (p + stsize + 2 >= buffer + sizeof (buffer)) + break; + tmp = + (struct UPNPDev *) malloc (sizeof (struct UPNPDev) + urlsize + + stsize); + tmp->pNext = devlist; + tmp->descURL = tmp->buffer; + tmp->st = tmp->buffer + 1 + urlsize; + memcpy (tmp->buffer, url, urlsize); + tmp->buffer[urlsize] = '\0'; + memcpy (tmp->buffer + urlsize + 1, p, stsize); + p += stsize; + tmp->buffer[urlsize + 1 + stsize] = '\0'; + devlist = tmp; + /* added for compatibility with recent versions of MiniSSDPd + * >= 2007/12/19 */ + DECODELENGTH (usnsize, p); + p += usnsize; + if (p > buffer + sizeof (buffer)) + break; + } + close (s); + return devlist; +} diff --git a/src/nat/miniupnp/minissdpc.h b/src/nat/miniupnp/minissdpc.h new file mode 100644 index 000000000..f6c7db547 --- /dev/null +++ b/src/nat/miniupnp/minissdpc.h @@ -0,0 +1,14 @@ +/* $Id: minissdpc.h,v 1.1 2007/08/31 15:15:33 nanard Exp $ */ +/* Project: miniupnp + * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ + * Author: Thomas Bernard + * Copyright (c) 2005-2007 Thomas Bernard + * This software is subjects to the conditions detailed + * in the LICENCE file provided within this distribution */ +#ifndef __MINISSDPC_H__ +#define __MINISSDPC_H__ + +struct UPNPDev *getDevicesFromMiniSSDPD (const char *devtype, + const char *socketpath); + +#endif diff --git a/src/nat/miniupnp/miniupnpc.c b/src/nat/miniupnp/miniupnpc.c new file mode 100644 index 000000000..412c8ec25 --- /dev/null +++ b/src/nat/miniupnp/miniupnpc.c @@ -0,0 +1,890 @@ +/* $Id: miniupnpc.c,v 1.57 2008/12/18 17:46:36 nanard Exp $ */ +/* Project : miniupnp + * Author : Thomas BERNARD + * copyright (c) 2005-2007 Thomas Bernard + * This software is subjet to the conditions detailed in the + * provided LICENCE file. */ +#include +#include +#include +#ifdef WIN32 +/* Win32 Specific includes and defines */ +#include +#include +#include +#include +#define snprintf _snprintf +#if defined(_MSC_VER) && (_MSC_VER >= 1400) +#define strncasecmp _memicmp +#else +#define strncasecmp memicmp +#endif +#define MAXHOSTNAMELEN 64 +#else +/* Standard POSIX includes */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define closesocket close +#endif +#include "miniupnpc.h" +#include "minissdpc.h" +#include "miniwget.h" +#include "minisoap.h" +#include "minixml.h" +#include "upnpcommands.h" + +#ifdef WIN32 +#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError()); +#else +#define PRINT_SOCKET_ERROR(x) perror(x) +#endif + +#define SOAPPREFIX "s" +#define SERVICEPREFIX "u" +#define SERVICEPREFIX2 'u' + +/* root description parsing */ +void +parserootdesc (const char *buffer, int bufsize, struct IGDdatas *data) +{ + struct xmlparser parser; + /* xmlparser object */ + parser.xmlstart = buffer; + parser.xmlsize = bufsize; + parser.data = data; + parser.starteltfunc = IGDstartelt; + parser.endeltfunc = IGDendelt; + parser.datafunc = IGDdata; + parser.attfunc = 0; + parsexml (&parser); +#ifdef DEBUG + printIGD (data); +#endif +} + +/* Content-length: nnn */ +static int +getcontentlenfromline (const char *p, int n) +{ + static const char contlenstr[] = "content-length"; + const char *p2 = contlenstr; + int a = 0; + while (*p2) + { + if (n == 0) + return -1; + if (*p2 != *p && *p2 != (*p + 32)) + return -1; + p++; + p2++; + n--; + } + if (n == 0) + return -1; + if (*p != ':') + return -1; + p++; + n--; + while (*p == ' ') + { + if (n == 0) + return -1; + p++; + n--; + } + while (*p >= '0' && *p <= '9') + { + if (n == 0) + return -1; + a = (a * 10) + (*p - '0'); + p++; + n--; + } + return a; +} + +static void +getContentLengthAndHeaderLength (char *p, int n, + int *contentlen, int *headerlen) +{ + char *line; + int linelen; + int r; + line = p; + while (line < p + n) + { + linelen = 0; + while (line[linelen] != '\r' && line[linelen] != '\r') + { + if (line + linelen >= p + n) + return; + linelen++; + } + r = getcontentlenfromline (line, linelen); + if (r > 0) + *contentlen = r; + line = line + linelen + 2; + if (line[0] == '\r' && line[1] == '\n') + { + *headerlen = (line - p) + 2; + return; + } + } +} + +/* simpleUPnPcommand : + * not so simple ! + * return values : + * 0 - OK + * -1 - error */ +int +simpleUPnPcommand (int s, const char *url, const char *service, + const char *action, struct UPNParg *args, + char *buffer, int *bufsize) +{ + struct sockaddr_in dest; + struct sockaddr_in6 dest6; + char hostname[MAXHOSTNAMELEN + 1]; + unsigned short port = 0; + char *path; + char soapact[128]; + char soapbody[2048]; + char *buf; + int buffree; + int n; + int err; + int contentlen, headerlen; /* for the response */ + snprintf (soapact, sizeof (soapact), "%s#%s", service, action); + if (args == NULL) + { + /*soapbodylen = */ snprintf (soapbody, sizeof (soapbody), + "\r\n" + "<" SOAPPREFIX ":Envelope " + "xmlns:" SOAPPREFIX + "=\"http://schemas.xmlsoap.org/soap/envelope/\" " + SOAPPREFIX + ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" + "<" SOAPPREFIX ":Body>" "<" SERVICEPREFIX + ":%s xmlns:" SERVICEPREFIX "=\"%s\">" "" "" "\r\n", + action, service, action); + } + else + { + char *p; + const char *pe, *pv; + int soapbodylen; + soapbodylen = snprintf (soapbody, sizeof (soapbody), + "\r\n" + "<" SOAPPREFIX ":Envelope " + "xmlns:" SOAPPREFIX + "=\"http://schemas.xmlsoap.org/soap/envelope/\" " + SOAPPREFIX + ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" + "<" SOAPPREFIX ":Body>" "<" SERVICEPREFIX + ":%s xmlns:" SERVICEPREFIX "=\"%s\">", action, + service); + p = soapbody + soapbodylen; + while (args->elt) + { + /* check that we are never overflowing the string... */ + if (soapbody + sizeof (soapbody) <= p + 100) + { + /* we keep a margin of at least 100 bytes */ + *bufsize = 0; + return -1; + } + *(p++) = '<'; + pe = args->elt; + while (*pe) + *(p++) = *(pe++); + *(p++) = '>'; + if ((pv = args->val)) + { + while (*pv) + *(p++) = *(pv++); + } + *(p++) = '<'; + *(p++) = '/'; + pe = args->elt; + while (*pe) + *(p++) = *(pe++); + *(p++) = '>'; + args++; + } + *(p++) = '<'; + *(p++) = '/'; + *(p++) = SERVICEPREFIX2; + *(p++) = ':'; + pe = action; + while (*pe) + *(p++) = *(pe++); + strncpy (p, ">\r\n", + soapbody + sizeof (soapbody) - p); + } + if (!parseURL (url, hostname, &port, &path)) + return -1; + + if (s < 0) + { + /* Test IPv4 address, else use IPv6 */ + if (inet_pton (AF_INET, hostname, &dest.sin_addr) == 1) + { + dest.sin_family = AF_INET; + dest.sin_port = htons (port); +#ifdef HAVE_SOCKADDR_IN_SIN_LEN + dest.sin_len = sizeof (dest); +#endif + if ((s = socket (PF_INET, SOCK_STREAM, 0)) < 0) + { + PRINT_SOCKET_ERROR ("socket"); + *bufsize = 0; + return -1; + } + err = connect (s, (struct sockaddr *) &dest, sizeof (dest)); + } + else if (inet_pton (AF_INET6, hostname, &dest6.sin6_addr) == 1) + { + dest6.sin6_family = AF_INET6; + dest6.sin6_port = htons (port); + dest6.sin6_flowinfo = 0; +#ifdef HAVE_SOCKADDR_IN_SIN_LEN + dest6.sin6_len = sizeof (dest6); +#endif + if ((s = socket (PF_INET6, SOCK_STREAM, 0)) < 0) + { + PRINT_SOCKET_ERROR ("socket"); + *bufsize = 0; + return -1; + } + err = connect (s, (struct sockaddr *) &dest6, sizeof (dest6)); + } + else + { + PRINT_SOCKET_ERROR ("inet_pton"); + closesocket (s); + *bufsize = 0; + return -1; + } + + if (err < 0) + { + PRINT_SOCKET_ERROR ("connect"); + closesocket (s); + *bufsize = 0; + return -1; + } + } + n = soapPostSubmit (s, path, hostname, port, soapact, soapbody); + if (n <= 0) + { +#ifdef DEBUG + printf ("Error sending SOAP request\n"); +#endif + closesocket (s); + return -1; + } + + contentlen = -1; + headerlen = -1; + buf = buffer; + buffree = *bufsize; + *bufsize = 0; + while ((n = ReceiveData (s, buf, buffree, 5000)) > 0) + { + buffree -= n; + buf += n; + *bufsize += n; + getContentLengthAndHeaderLength (buffer, *bufsize, + &contentlen, &headerlen); +#ifdef DEBUG + printf ("received n=%dbytes bufsize=%d ContLen=%d HeadLen=%d\n", + n, *bufsize, contentlen, headerlen); +#endif + /* break if we received everything */ + if (contentlen > 0 && headerlen > 0 + && *bufsize >= contentlen + headerlen) + break; + } + + closesocket (s); + return 0; +} + +/* parseMSEARCHReply() + * the last 4 arguments are filled during the parsing : + * - location/locationsize : "location:" field of the SSDP reply packet + * - st/stsize : "st:" field of the SSDP reply packet. + * The strings are NOT null terminated */ +static void +parseMSEARCHReply (const char *reply, int size, + const char **location, int *locationsize, + const char **st, int *stsize) +{ + int a, b, i; + i = 0; + a = i; /* start of the line */ + b = 0; + while (i < size) + { + switch (reply[i]) + { + case ':': + if (b == 0) + { + b = i; /* end of the "header" */ + /*for(j=a; jsa_family == AF_INET) + domain = PF_INET; + else if (addr && addr->sa_family == AF_INET6) + domain = PF_INET6; + else if (addr) + return NULL; + + /* fallback to direct discovery */ +#ifdef WIN32 + sudp = socket (domain, SOCK_DGRAM, IPPROTO_UDP); +#else + sudp = socket (domain, SOCK_DGRAM, 0); +#endif + if (sudp < 0) + { + PRINT_SOCKET_ERROR ("socket"); + return NULL; + } + + if (domain == PF_INET) + { + /* receive */ + memset (&sockudp_r, 0, sizeof (struct sockaddr_in)); + sockudp_r.sin_family = AF_INET; +#ifdef HAVE_SOCKADDR_IN_SIN_LEN + sockudp_r.sin_len = sizeof (struct sockaddr_in); +#endif + if (sameport) + sockudp_r.sin_port = htons (PORT); + sockudp_r.sin_addr.s_addr = INADDR_ANY; + /* send */ + memset (&sockudp_w, 0, sizeof (struct sockaddr_in)); + sockudp_w.sin_family = AF_INET; + sockudp_w.sin_port = htons (PORT); + sockudp_w.sin_addr.s_addr = inet_addr (UPNP_MCAST_ADDR); +#ifdef HAVE_SOCKADDR_IN_SIN_LEN + sockudp_w.sin_len = sizeof (struct sockaddr_in); +#endif + } + else + { + /* receive */ + memcpy (&sockudp6_r, addr, sizeof (struct sockaddr_in6)); + if (sameport) + sockudp6_r.sin6_port = htons (PORT); + else + sockudp6_r.sin6_port = 0; + sockudp6_r.sin6_addr = any_addr; +#ifdef HAVE_SOCKADDR_IN_SIN_LEN + sockudp6_r.sin6_len = sizeof (struct sockaddr_in6); +#endif + /* send */ + memset (&sockudp6_w, 0, sizeof (struct sockaddr_in6)); + sockudp6_w.sin6_family = AF_INET6; + sockudp6_w.sin6_port = htons (PORT); + inet_pton (AF_INET6, UPNP_MCAST_ADDR6, &sockudp6_w.sin6_addr); +#ifdef HAVE_SOCKADDR_IN_SIN_LEN + sockudp6_w.sin6_len = sizeof (struct sockaddr_in6); +#endif + } + +#ifdef WIN32 + if (setsockopt + (sudp, SOL_SOCKET, SO_REUSEADDR, (const char *) &opt, sizeof (opt)) < 0) +#else + if (setsockopt (sudp, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)) < 0) +#endif + { + PRINT_SOCKET_ERROR ("setsockopt"); + return NULL; + } + + if (addr) + { + if (domain == PF_INET) + { + sockudp_r.sin_addr.s_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr; + if (setsockopt + (sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *) &sockudp_r.sin_addr, + sizeof (struct in_addr)) < 0) + { + PRINT_SOCKET_ERROR ("setsockopt"); + } + + /* Bind to receive response before sending packet */ + if (bind (sudp, (struct sockaddr *) &sockudp_r, sizeof (struct sockaddr_in)) + != 0) + { + PRINT_SOCKET_ERROR ("bind"); + closesocket (sudp); + return NULL; + } + } + else + { + if (multicastif && !(if_index = if_nametoindex (multicastif))) + PRINT_SOCKET_ERROR ("if_nametoindex"); + + if (setsockopt + (sudp, IPPROTO_IPV6, IPV6_MULTICAST_IF, &if_index, sizeof (if_index)) < 0) + { + PRINT_SOCKET_ERROR ("setsockopt"); + } + + /* Bind to receive response before sending packet */ + memcpy (&sockudp6_r.sin6_addr, &((struct sockaddr_in6 *) addr)->sin6_addr, + sizeof (sockudp6_r.sin6_addr)); + if (bind (sudp, (struct sockaddr *) &sockudp6_r, sizeof (struct sockaddr_in6)) + != 0) + { + PRINT_SOCKET_ERROR ("bind"); + closesocket (sudp); + return NULL; + } + } + } + + /* receiving SSDP response packet */ + for (n = 0;;) + { + if (n == 0) + { + /* sending the SSDP M-SEARCH packet */ + n = snprintf (bufr, sizeof (bufr), + MSearchMsgFmt, deviceList[deviceIndex++]); + /*printf("Sending %s", bufr); */ + if (domain == PF_INET) + n = sendto (sudp, bufr, n, 0, + (struct sockaddr *) &sockudp_w, + sizeof (struct sockaddr_in)); + else + n = sendto (sudp, bufr, n, 0, + (struct sockaddr *) &sockudp6_w, + sizeof (struct sockaddr_in6)); + + if (n < 0) + { + PRINT_SOCKET_ERROR ("sendto"); + closesocket (sudp); + return devlist; + } + } + /* Waiting for SSDP REPLY packet to M-SEARCH */ + n = ReceiveData (sudp, bufr, sizeof (bufr), delay); + + if (n < 0) + { + /* error */ + closesocket (sudp); + return devlist; + } + else if (n == 0) + { + /* no data or Time Out */ + if (devlist || (deviceList[deviceIndex] == 0)) + { + /* no more device type to look for... */ + closesocket (sudp); + return devlist; + } + } + else + { + const char *descURL = NULL; + int urlsize = 0; + const char *st = NULL; + int stsize = 0; + /*printf("%d byte(s) :\n%s\n", n, bufr); *//* affichage du message */ + parseMSEARCHReply (bufr, n, &descURL, &urlsize, &st, &stsize); + if (st && descURL) + { + /*printf("M-SEARCH Reply:\nST: %.*s\nLocation: %.*s\n", + stsize, st, urlsize, descURL); */ + tmp = + (struct UPNPDev *) malloc (sizeof (struct UPNPDev) + urlsize + + stsize); + tmp->pNext = devlist; + tmp->descURL = tmp->buffer; + tmp->st = tmp->buffer + 1 + urlsize; + memcpy (tmp->buffer, descURL, urlsize); + tmp->buffer[urlsize] = '\0'; + memcpy (tmp->buffer + urlsize + 1, st, stsize); + tmp->buffer[urlsize + 1 + stsize] = '\0'; + devlist = tmp; + } + } + } +} + +/* freeUPNPDevlist() should be used to + * free the chained list returned by upnpDiscover() */ +void +freeUPNPDevlist (struct UPNPDev *devlist) +{ + struct UPNPDev *next; + while (devlist) + { + next = devlist->pNext; + free (devlist); + devlist = next; + } +} + +static void +url_cpy_or_cat (char *dst, const char *src, int n) +{ + if ((src[0] == 'h') + && (src[1] == 't') + && (src[2] == 't') + && (src[3] == 'p') + && (src[4] == ':') && (src[5] == '/') && (src[6] == '/')) + { + strncpy (dst, src, n); + } + else + { + int l = strlen (dst); + if (src[0] != '/') + dst[l++] = '/'; + if (l <= n) + strncpy (dst + l, src, n - l); + } +} + +/* Prepare the Urls for usage... + */ +void +GetUPNPUrls (struct UPNPUrls *urls, struct IGDdatas *data, + const char *descURL) +{ + char *p; + int n1, n2, n3; + n1 = strlen (data->urlbase); + if (n1 == 0) + n1 = strlen (descURL); + n1 += 2; /* 1 byte more for Null terminator, 1 byte for '/' if needed */ + n2 = n1; + n3 = n1; + n1 += strlen (data->scpdurl); + n2 += strlen (data->controlurl); + n3 += strlen (data->controlurl_CIF); + + urls->ipcondescURL = (char *) malloc (n1); + urls->controlURL = (char *) malloc (n2); + urls->controlURL_CIF = (char *) malloc (n3); + /* maintenant on chope la desc du WANIPConnection */ + if (data->urlbase[0] != '\0') + strncpy (urls->ipcondescURL, data->urlbase, n1); + else + strncpy (urls->ipcondescURL, descURL, n1); + p = strchr (urls->ipcondescURL + 7, '/'); + if (p) + p[0] = '\0'; + strncpy (urls->controlURL, urls->ipcondescURL, n2); + strncpy (urls->controlURL_CIF, urls->ipcondescURL, n3); + + url_cpy_or_cat (urls->ipcondescURL, data->scpdurl, n1); + + url_cpy_or_cat (urls->controlURL, data->controlurl, n2); + + url_cpy_or_cat (urls->controlURL_CIF, data->controlurl_CIF, n3); + +#ifdef DEBUG + printf ("urls->ipcondescURL='%s' %d n1=%d\n", urls->ipcondescURL, + strlen (urls->ipcondescURL), n1); + printf ("urls->controlURL='%s' %d n2=%d\n", urls->controlURL, + strlen (urls->controlURL), n2); + printf ("urls->controlURL_CIF='%s' %d n3=%d\n", urls->controlURL_CIF, + strlen (urls->controlURL_CIF), n3); +#endif +} + +void +FreeUPNPUrls (struct UPNPUrls *urls) +{ + if (!urls) + return; + free (urls->controlURL); + urls->controlURL = 0; + free (urls->ipcondescURL); + urls->ipcondescURL = 0; + free (urls->controlURL_CIF); + urls->controlURL_CIF = 0; +} + + +int +ReceiveData (int socket, char *data, int length, int timeout) +{ + int n; +#ifndef WIN32 + struct pollfd fds[1]; /* for the poll */ + fds[0].fd = socket; + fds[0].events = POLLIN; + n = poll (fds, 1, timeout); + if (n < 0) + { + PRINT_SOCKET_ERROR ("poll"); + return -1; + } + else if (n == 0) + { + return 0; + } +#else + fd_set socketSet; + TIMEVAL timeval; + FD_ZERO (&socketSet); + FD_SET (socket, &socketSet); + timeval.tv_sec = timeout / 1000; + timeval.tv_usec = (timeout % 1000) * 1000; + /*n = select(0, &socketSet, NULL, NULL, &timeval); */ + n = select (FD_SETSIZE, &socketSet, NULL, NULL, &timeval); + if (n < 0) + { + PRINT_SOCKET_ERROR ("select"); + return -1; + } + else if (n == 0) + { + return 0; + } +#endif + n = recv (socket, data, length, 0); + if (n < 0) + { + PRINT_SOCKET_ERROR ("recv"); + } + return n; +} + +int +UPNPIGD_IsConnected (struct UPNPUrls *urls, struct IGDdatas *data) +{ + char status[64]; + unsigned int uptime; + status[0] = '\0'; + UPNP_GetStatusInfo (urls->controlURL, data->servicetype, + status, &uptime, NULL); + if (0 == strcmp ("Connected", status)) + { + return 1; + } + else + return 0; +} + + +/* UPNP_GetValidIGD() : + * return values : + * 0 = NO IGD found + * 1 = A valid connected IGD has been found + * 2 = A valid IGD has been found but it reported as + * not connected + * 3 = an UPnP device has been found but was not recognized as an IGD + * + * In any non zero return case, the urls and data structures + * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to + * free allocated memory. + */ +int +UPNP_GetValidIGD (struct UPNPDev *devlist, + struct UPNPUrls *urls, + struct IGDdatas *data, char *lanaddr, int lanaddrlen) +{ + char *descXML; + int descXMLsize = 0; + struct UPNPDev *dev; + int ndev = 0; + int state; /* state 1 : IGD connected. State 2 : IGD. State 3 : anything */ + if (!devlist) + { +#ifdef DEBUG + printf ("Empty devlist\n"); +#endif + return 0; + } + for (state = 1; state <= 3; state++) + { + for (dev = devlist; dev; dev = dev->pNext) + { + /* we should choose an internet gateway device. + * with st == urn:schemas-upnp-org:device:InternetGatewayDevice:1 */ + descXML = miniwget_getaddr (dev->descURL, &descXMLsize, + lanaddr, lanaddrlen); + if (descXML) + { + ndev++; + memset (data, 0, sizeof (struct IGDdatas)); + memset (urls, 0, sizeof (struct UPNPUrls)); + parserootdesc (descXML, descXMLsize, data); + free (descXML); + descXML = NULL; + if (0 == strcmp (data->servicetype_CIF, + "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1") + || state >= 3) + { + GetUPNPUrls (urls, data, dev->descURL); + +#ifdef DEBUG + printf ("UPNPIGD_IsConnected(%s) = %d\n", + urls->controlURL, UPNPIGD_IsConnected (urls, data)); +#endif + if ((state >= 2) || UPNPIGD_IsConnected (urls, data)) + return state; + FreeUPNPUrls (urls); + } + memset (data, 0, sizeof (struct IGDdatas)); + } +#ifdef DEBUG + else + { + printf ("error getting XML description %s\n", dev->descURL); + } +#endif + } + } + return 0; +} + +/* UPNP_GetIGDFromUrl() + * Used when skipping the discovery process. + * return value : + * 0 - Not ok + * 1 - OK */ +int +UPNP_GetIGDFromUrl (const char *rootdescurl, + struct UPNPUrls *urls, + struct IGDdatas *data, char *lanaddr, int lanaddrlen) +{ + char *descXML; + int descXMLsize = 0; + descXML = miniwget_getaddr (rootdescurl, &descXMLsize, lanaddr, lanaddrlen); + if (descXML) + { + memset (data, 0, sizeof (struct IGDdatas)); + memset (urls, 0, sizeof (struct UPNPUrls)); + parserootdesc (descXML, descXMLsize, data); + free (descXML); + descXML = NULL; + GetUPNPUrls (urls, data, rootdescurl); + return 1; + } + else + { + return 0; + } +} diff --git a/src/nat/miniupnp/miniupnpc.h b/src/nat/miniupnp/miniupnpc.h new file mode 100644 index 000000000..8aa047b64 --- /dev/null +++ b/src/nat/miniupnp/miniupnpc.h @@ -0,0 +1,120 @@ +/* $Id: miniupnpc.h,v 1.18 2008/09/25 18:02:50 nanard Exp $ */ +/* Project: miniupnp + * http://miniupnp.free.fr/ + * Author: Thomas Bernard + * Copyright (c) 2005-2006 Thomas Bernard + * This software is subjects to the conditions detailed + * in the LICENCE file provided within this distribution */ +#ifndef __MINIUPNPC_H__ +#define __MINIUPNPC_H__ + +#include "declspec.h" +#include "igd_desc_parse.h" + +#ifdef WIN32 +#include +#else +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Structures definitions : */ + struct UPNParg + { + const char *elt; + const char *val; + }; + + int simpleUPnPcommand (int s, const char *, const char *, + const char *, struct UPNParg *, char *, int *); + + struct UPNPDev + { + struct UPNPDev *pNext; + char *descURL; + char *st; + char buffer[2]; + }; + +/* upnpDiscover() + * discover UPnP devices on the network. + * The discovered devices are returned as a chained list. + * It is up to the caller to free the list with freeUPNPDevlist(). + * delay (in millisecond) is the maximum time for waiting any device + * response. + * If available, device list will be obtained from MiniSSDPd. + * Default path for minissdpd socket will be used if minissdpdsock argument + * is NULL. + * If multicastif is not NULL, it will be used instead of the default + * multicast interface for sending SSDP discover packets. + * If sameport is not null, SSDP packets will be sent from the source port + * 1900 (same as destination port) otherwise system assign a source port. */ + LIBSPEC struct UPNPDev *upnpDiscover (int delay, const char *multicastif, const struct sockaddr *addr, + const char *minissdpdsock, int sameport); +/* freeUPNPDevlist() + * free list returned by upnpDiscover() */ + LIBSPEC void freeUPNPDevlist (struct UPNPDev *devlist); + +/* parserootdesc() : + * parse root XML description of a UPnP device and fill the IGDdatas + * structure. */ + LIBSPEC void parserootdesc (const char *, int, struct IGDdatas *); + +/* structure used to get fast access to urls + * controlURL: controlURL of the WANIPConnection + * ipcondescURL: url of the description of the WANIPConnection + * controlURL_CIF: controlURL of the WANCommonInterfaceConfig + */ + struct UPNPUrls + { + char *controlURL; + char *ipcondescURL; + char *controlURL_CIF; + }; + +/* UPNP_GetValidIGD() : + * return values : + * 0 = NO IGD found + * 1 = A valid connected IGD has been found + * 2 = A valid IGD has been found but it reported as + * not connected + * 3 = an UPnP device has been found but was not recognized as an IGD + * + * In any non zero return case, the urls and data structures + * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to + * free allocated memory. + */ + LIBSPEC int + UPNP_GetValidIGD (struct UPNPDev *devlist, + struct UPNPUrls *urls, + struct IGDdatas *data, char *lanaddr, int lanaddrlen); + +/* UPNP_GetIGDFromUrl() + * Used when skipping the discovery process. + * return value : + * 0 - Not ok + * 1 - OK */ + LIBSPEC int + UPNP_GetIGDFromUrl (const char *rootdescurl, + struct UPNPUrls *urls, + struct IGDdatas *data, char *lanaddr, int lanaddrlen); + + LIBSPEC void GetUPNPUrls (struct UPNPUrls *, struct IGDdatas *, + const char *); + + LIBSPEC void FreeUPNPUrls (struct UPNPUrls *); + +/* Reads data from the specified socket. + * Returns the number of bytes read if successful, zero if no bytes were + * read or if we timed out. Returns negative if there was an error. */ + int ReceiveData (int socket, char *data, int length, int timeout); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/nat/miniupnp/miniupnpcstrings.h b/src/nat/miniupnp/miniupnpcstrings.h new file mode 100644 index 000000000..3245e1f69 --- /dev/null +++ b/src/nat/miniupnp/miniupnpcstrings.h @@ -0,0 +1,14 @@ +/* $Id: miniupnpcstrings.h,v 1.2 2008/10/14 17:39:04 nanard Exp $ */ +/* Project: miniupnp + * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ + * Author: Thomas Bernard + * Copyright (c) 2005-2008 Thomas Bernard + * This software is subjects to the conditions detailed + * in the LICENCE file provided within this distribution */ +#ifndef __MINIUPNPCSTRINGS_H__ +#define __MINIUPNPCSTRINGS_H__ + +#define OS_STRING "Debian/4.0" +#define MINIUPNPC_VERSION_STRING "1.2" + +#endif diff --git a/src/nat/miniupnp/miniwget.c b/src/nat/miniupnp/miniwget.c new file mode 100644 index 000000000..38d5610d8 --- /dev/null +++ b/src/nat/miniupnp/miniwget.c @@ -0,0 +1,224 @@ +/* $Id: miniwget.c,v 1.22 2009/02/28 10:36:35 nanard Exp $ */ +/* Project : miniupnp + * Author : Thomas Bernard + * Copyright (c) 2005 Thomas Bernard + * This software is subject to the conditions detailed in the + * LICENCE file provided in this distribution. + * */ +#include +#include +#include +#include "miniupnpc.h" +#ifdef WIN32 +#include +#include +#define MAXHOSTNAMELEN 64 +#define MIN(x,y) (((x)<(y))?(x):(y)) +#define snprintf _snprintf +#define herror +#define socklen_t int +#else +#include +#include +#include +#include +#include +#include +#define closesocket close +#endif +#if defined(__sun) || defined(sun) +#define MIN(x,y) (((x)<(y))?(x):(y)) +#endif + +#include "miniupnpcstrings.h" + +/* miniwget2() : + * */ +static void * +miniwget2 (const char *url, const char *host, + unsigned short port, const char *path, + int *size, char *addr_str, int addr_str_len) +{ + char buf[2048]; + int s; + struct sockaddr_in dest; + struct hostent *hp; + *size = 0; + hp = gethostbyname (host); + if (hp == NULL) + { + herror (host); + return NULL; + } + /* memcpy((char *)&dest.sin_addr, hp->h_addr, hp->h_length); */ + memcpy (&dest.sin_addr, hp->h_addr, sizeof (dest.sin_addr)); + memset (dest.sin_zero, 0, sizeof (dest.sin_zero)); + s = socket (PF_INET, SOCK_STREAM, 0); + if (s < 0) + { + perror ("socket"); + return NULL; + } + dest.sin_family = AF_INET; + dest.sin_port = htons (port); + if (connect (s, (struct sockaddr *) &dest, sizeof (struct sockaddr_in)) < 0) + { + perror ("connect"); + closesocket (s); + return NULL; + } + + /* get address for caller ! */ + if (addr_str) + { + struct sockaddr_in saddr; + socklen_t len; + + len = sizeof (saddr); + getsockname (s, (struct sockaddr *) &saddr, &len); +#ifndef WIN32 + inet_ntop (AF_INET, &saddr.sin_addr, addr_str, addr_str_len); +#else + /* using INT WINAPI WSAAddressToStringA(LPSOCKADDR, DWORD, LPWSAPROTOCOL_INFOA, LPSTR, LPDWORD); + * But his function make a string with the port : nn.nn.nn.nn:port */ +/* if(WSAAddressToStringA((SOCKADDR *)&saddr, sizeof(saddr), + NULL, addr_str, (DWORD *)&addr_str_len)) + { + printf("WSAAddressToStringA() failed : %d\n", WSAGetLastError()); + }*/ + strncpy (addr_str, inet_ntoa (saddr.sin_addr), addr_str_len); +#endif +#ifdef DEBUG + printf ("address miniwget : %s\n", addr_str); +#endif + } + + snprintf (buf, sizeof (buf), + "GET %s HTTP/1.1\r\n" + "Host: %s:%d\r\n" + "Connection: Close\r\n" + "User-Agent: " OS_STRING ", UPnP/1.0, MiniUPnPc/" + MINIUPNPC_VERSION_STRING "\r\n" "\r\n", path, host, port); + /*write(s, buf, strlen(buf)); */ + send (s, buf, strlen (buf), 0); + { + int n, headers = 1; + char *respbuffer = NULL; + int allreadyread = 0; + /*while((n = recv(s, buf, 2048, 0)) > 0) */ + while ((n = ReceiveData (s, buf, 2048, 5000)) > 0) + { + if (headers) + { + int i = 0; + while (i < n - 3) + { + if (buf[i] == '\r' && buf[i + 1] == '\n' + && buf[i + 2] == '\r' && buf[i + 3] == '\n') + { + headers = 0; /* end */ + if (i < n - 4) + { + respbuffer = (char *) realloc ((void *) respbuffer, + allreadyread + (n - i - + 4)); + memcpy (respbuffer + allreadyread, buf + i + 4, + n - i - 4); + allreadyread += (n - i - 4); + } + break; + } + i++; + } + } + else + { + respbuffer = (char *) realloc ((void *) respbuffer, + allreadyread + n); + memcpy (respbuffer + allreadyread, buf, n); + allreadyread += n; + } + } + *size = allreadyread; +#ifdef DEBUG + printf ("%d bytes read\n", *size); +#endif + closesocket (s); + return respbuffer; + } +} + +/* parseURL() + * arguments : + * url : source string not modified + * hostname : hostname destination string (size of MAXHOSTNAMELEN+1) + * port : port (destination) + * path : pointer to the path part of the URL + * + * Return values : + * 0 - Failure + * 1 - Success */ +int +parseURL (const char *url, char *hostname, unsigned short *port, char **path) +{ + char *p1, *p2, *p3; + p1 = strstr (url, "://"); + if (!p1) + return 0; + p1 += 3; + if ((url[0] != 'h') || (url[1] != 't') + || (url[2] != 't') || (url[3] != 'p')) + return 0; + p2 = strchr (p1, ':'); + p3 = strchr (p1, '/'); + if (!p3) + return 0; + memset (hostname, 0, MAXHOSTNAMELEN + 1); + if (!p2 || (p2 > p3)) + { + strncpy (hostname, p1, MIN (MAXHOSTNAMELEN, (int) (p3 - p1))); + *port = 80; + } + else + { + strncpy (hostname, p1, MIN (MAXHOSTNAMELEN, (int) (p2 - p1))); + *port = 0; + p2++; + while ((*p2 >= '0') && (*p2 <= '9')) + { + *port *= 10; + *port += (unsigned short) (*p2 - '0'); + p2++; + } + } + *path = p3; + return 1; +} + +void * +miniwget (const char *url, int *size) +{ + unsigned short port; + char *path; + /* protocol://host:port/chemin */ + char hostname[MAXHOSTNAMELEN + 1]; + *size = 0; + if (!parseURL (url, hostname, &port, &path)) + return NULL; + return miniwget2 (url, hostname, port, path, size, 0, 0); +} + +void * +miniwget_getaddr (const char *url, int *size, char *addr, int addrlen) +{ + unsigned short port; + char *path; + /* protocol://host:port/chemin */ + char hostname[MAXHOSTNAMELEN + 1]; + *size = 0; + if (addr) + addr[0] = '\0'; + if (!parseURL (url, hostname, &port, &path)) + return NULL; + return miniwget2 (url, hostname, port, path, size, addr, addrlen); +} diff --git a/src/nat/miniupnp/miniwget.h b/src/nat/miniupnp/miniwget.h new file mode 100644 index 000000000..04e91df64 --- /dev/null +++ b/src/nat/miniupnp/miniwget.h @@ -0,0 +1,28 @@ +/* $Id: miniwget.h,v 1.5 2007/01/29 20:27:23 nanard Exp $ */ +/* Project : miniupnp + * Author : Thomas Bernard + * Copyright (c) 2005 Thomas Bernard + * This software is subject to the conditions detailed in the + * LICENCE file provided in this distribution. + * */ +#ifndef __MINIWGET_H__ +#define __MINIWGET_H__ + +#include "declspec.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + LIBSPEC void *miniwget (const char *, int *); + + LIBSPEC void *miniwget_getaddr (const char *, int *, char *, int); + + int parseURL (const char *, char *, unsigned short *, char **); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/nat/miniupnp/minixml.c b/src/nat/miniupnp/minixml.c new file mode 100644 index 000000000..094118cfa --- /dev/null +++ b/src/nat/miniupnp/minixml.c @@ -0,0 +1,200 @@ +/* $Id: minixml.c,v 1.6 2007/05/15 18:14:08 nanard Exp $ */ +/* minixml.c : the minimum size a xml parser can be ! */ +/* Project : miniupnp + * webpage: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ + * Author : Thomas Bernard + +Copyright (c) 2005-2007, Thomas BERNARD +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ +#include "minixml.h" + +/* parseatt : used to parse the argument list + * return 0 (false) in case of success and -1 (true) if the end + * of the xmlbuffer is reached. */ +int +parseatt (struct xmlparser *p) +{ + const char *attname; + int attnamelen; + const char *attvalue; + int attvaluelen; + while (p->xml < p->xmlend) + { + if (*p->xml == '/' || *p->xml == '>') + return 0; + if (!IS_WHITE_SPACE (*p->xml)) + { + char sep; + attname = p->xml; + attnamelen = 0; + while (*p->xml != '=' && !IS_WHITE_SPACE (*p->xml)) + { + attnamelen++; + p->xml++; + if (p->xml >= p->xmlend) + return -1; + } + while (*(p->xml++) != '=') + { + if (p->xml >= p->xmlend) + return -1; + } + while (IS_WHITE_SPACE (*p->xml)) + { + p->xml++; + if (p->xml >= p->xmlend) + return -1; + } + sep = *p->xml; + if (sep == '\'' || sep == '\"') + { + p->xml++; + if (p->xml >= p->xmlend) + return -1; + attvalue = p->xml; + attvaluelen = 0; + while (*p->xml != sep) + { + attvaluelen++; + p->xml++; + if (p->xml >= p->xmlend) + return -1; + } + } + else + { + attvalue = p->xml; + attvaluelen = 0; + while (!IS_WHITE_SPACE (*p->xml) + && *p->xml != '>' && *p->xml != '/') + { + attvaluelen++; + p->xml++; + if (p->xml >= p->xmlend) + return -1; + } + } + /*printf("%.*s='%.*s'\n", + attnamelen, attname, attvaluelen, attvalue); */ + if (p->attfunc) + p->attfunc (p->data, attname, attnamelen, attvalue, attvaluelen); + } + p->xml++; + } + return -1; +} + +/* parseelt parse the xml stream and + * call the callback functions when needed... */ +void +parseelt (struct xmlparser *p) +{ + int i; + const char *elementname; + while (p->xml < (p->xmlend - 1)) + { + if ((p->xml)[0] == '<' && (p->xml)[1] != '?') + { + i = 0; + elementname = ++p->xml; + while (!IS_WHITE_SPACE (*p->xml) + && (*p->xml != '>') && (*p->xml != '/')) + { + i++; + p->xml++; + if (p->xml >= p->xmlend) + return; + /* to ignore namespace : */ + if (*p->xml == ':') + { + i = 0; + elementname = ++p->xml; + } + } + if (i > 0) + { + if (p->starteltfunc) + p->starteltfunc (p->data, elementname, i); + if (parseatt (p)) + return; + if (*p->xml != '/') + { + const char *data; + i = 0; + data = ++p->xml; + if (p->xml >= p->xmlend) + return; + while (IS_WHITE_SPACE (*p->xml)) + { + p->xml++; + if (p->xml >= p->xmlend) + return; + } + while (*p->xml != '<') + { + i++; + p->xml++; + if (p->xml >= p->xmlend) + return; + } + if (i > 0 && p->datafunc) + p->datafunc (p->data, data, i); + } + } + else if (*p->xml == '/') + { + i = 0; + elementname = ++p->xml; + if (p->xml >= p->xmlend) + return; + while ((*p->xml != '>')) + { + i++; + p->xml++; + if (p->xml >= p->xmlend) + return; + } + if (p->endeltfunc) + p->endeltfunc (p->data, elementname, i); + p->xml++; + } + } + else + { + p->xml++; + } + } +} + +/* the parser must be initialized before calling this function */ +void +parsexml (struct xmlparser *parser) +{ + parser->xml = parser->xmlstart; + parser->xmlend = parser->xmlstart + parser->xmlsize; + parseelt (parser); +} diff --git a/src/nat/miniupnp/minixml.h b/src/nat/miniupnp/minixml.h new file mode 100644 index 000000000..6ffb987ac --- /dev/null +++ b/src/nat/miniupnp/minixml.h @@ -0,0 +1,37 @@ +/* $Id: minixml.h,v 1.6 2006/11/30 11:47:21 nanard Exp $ */ +/* minimal xml parser + * + * Project : miniupnp + * Website : http://miniupnp.free.fr/ + * Author : Thomas Bernard + * Copyright (c) 2005 Thomas Bernard + * This software is subject to the conditions detailed in the + * LICENCE file provided in this distribution. + * */ +#ifndef __MINIXML_H__ +#define __MINIXML_H__ +#define IS_WHITE_SPACE(c) ((c==' ') || (c=='\t') || (c=='\r') || (c=='\n')) + +/* if a callback function pointer is set to NULL, + * the function is not called */ +struct xmlparser +{ + const char *xmlstart; + const char *xmlend; + const char *xml; /* pointer to current character */ + int xmlsize; + void *data; + void (*starteltfunc) (void *, const char *, int); + void (*endeltfunc) (void *, const char *, int); + void (*datafunc) (void *, const char *, int); + void (*attfunc) (void *, const char *, int, const char *, int); +}; + +/* parsexml() + * the xmlparser structure must be initialized before the call + * the following structure members have to be initialized : + * xmlstart, xmlsize, data, *func + * xml is for internal usage, xmlend is computed automatically */ +void parsexml (struct xmlparser *); + +#endif diff --git a/src/nat/miniupnp/upnpcommands.c b/src/nat/miniupnp/upnpcommands.c new file mode 100644 index 000000000..7a342540a --- /dev/null +++ b/src/nat/miniupnp/upnpcommands.c @@ -0,0 +1,606 @@ +/* $Id: upnpcommands.c,v 1.24 2009/04/17 21:21:19 nanard Exp $ */ +/* Project : miniupnp + * Author : Thomas Bernard + * Copyright (c) 2005-2009 Thomas Bernard + * This software is subject to the conditions detailed in the + * LICENCE file provided in this distribution. + * */ +#include +#include +#include +#include "upnpcommands.h" +#include "miniupnpc.h" + +static UNSIGNED_INTEGER +my_atoui (const char *s) +{ + return s ? ((UNSIGNED_INTEGER) STRTOUI (s, NULL, 0)) : 0; +} + +/* + * */ +UNSIGNED_INTEGER +UPNP_GetTotalBytesSent (const char *controlURL, const char *servicetype) +{ + struct NameValueParserData pdata; + char buffer[4096]; + int bufsize = 4096; + unsigned int r = 0; + char *p; + simpleUPnPcommand (-1, controlURL, servicetype, "GetTotalBytesSent", 0, + buffer, &bufsize); + ParseNameValue (buffer, bufsize, &pdata); + /*DisplayNameValueList(buffer, bufsize); */ + p = GetValueFromNameValueList (&pdata, "NewTotalBytesSent"); + r = my_atoui (p); + ClearNameValueList (&pdata); + return r; +} + +/* + * */ +UNSIGNED_INTEGER +UPNP_GetTotalBytesReceived (const char *controlURL, const char *servicetype) +{ + struct NameValueParserData pdata; + char buffer[4096]; + int bufsize = 4096; + unsigned int r = 0; + char *p; + simpleUPnPcommand (-1, controlURL, servicetype, "GetTotalBytesReceived", 0, + buffer, &bufsize); + ParseNameValue (buffer, bufsize, &pdata); + /*DisplayNameValueList(buffer, bufsize); */ + p = GetValueFromNameValueList (&pdata, "NewTotalBytesReceived"); + r = my_atoui (p); + ClearNameValueList (&pdata); + return r; +} + +/* + * */ +UNSIGNED_INTEGER +UPNP_GetTotalPacketsSent (const char *controlURL, const char *servicetype) +{ + struct NameValueParserData pdata; + char buffer[4096]; + int bufsize = 4096; + unsigned int r = 0; + char *p; + simpleUPnPcommand (-1, controlURL, servicetype, "GetTotalPacketsSent", 0, + buffer, &bufsize); + ParseNameValue (buffer, bufsize, &pdata); + /*DisplayNameValueList(buffer, bufsize); */ + p = GetValueFromNameValueList (&pdata, "NewTotalPacketsSent"); + r = my_atoui (p); + ClearNameValueList (&pdata); + return r; +} + +/* + * */ +UNSIGNED_INTEGER +UPNP_GetTotalPacketsReceived (const char *controlURL, const char *servicetype) +{ + struct NameValueParserData pdata; + char buffer[4096]; + int bufsize = 4096; + unsigned int r = 0; + char *p; + simpleUPnPcommand (-1, controlURL, servicetype, "GetTotalPacketsReceived", + 0, buffer, &bufsize); + ParseNameValue (buffer, bufsize, &pdata); + /*DisplayNameValueList(buffer, bufsize); */ + p = GetValueFromNameValueList (&pdata, "NewTotalPacketsReceived"); + r = my_atoui (p); + ClearNameValueList (&pdata); + return r; +} + +/* UPNP_GetStatusInfo() call the corresponding UPNP method + * returns the current status and uptime */ +int +UPNP_GetStatusInfo (const char *controlURL, + const char *servicetype, + char *status, unsigned int *uptime, char *lastconnerror) +{ + struct NameValueParserData pdata; + char buffer[4096]; + int bufsize = 4096; + char *p; + char *up; + char *err; + int ret = UPNPCOMMAND_UNKNOWN_ERROR; + + if (!status && !uptime) + return UPNPCOMMAND_INVALID_ARGS; + + simpleUPnPcommand (-1, controlURL, servicetype, "GetStatusInfo", 0, buffer, + &bufsize); + ParseNameValue (buffer, bufsize, &pdata); + /*DisplayNameValueList(buffer, bufsize); */ + up = GetValueFromNameValueList (&pdata, "NewUptime"); + p = GetValueFromNameValueList (&pdata, "NewConnectionStatus"); + err = GetValueFromNameValueList (&pdata, "NewLastConnectionError"); + if (p && up) + ret = UPNPCOMMAND_SUCCESS; + + if (status) + { + if (p) + { + strncpy (status, p, 64); + status[63] = '\0'; + } + else + status[0] = '\0'; + } + + if (uptime) + { + if (up) + sscanf (up, "%u", uptime); + else + uptime = 0; + } + + if (lastconnerror) + { + if (err) + { + strncpy (lastconnerror, err, 64); + lastconnerror[63] = '\0'; + } + else + lastconnerror[0] = '\0'; + } + + p = GetValueFromNameValueList (&pdata, "errorCode"); + if (p) + { + ret = UPNPCOMMAND_UNKNOWN_ERROR; + sscanf (p, "%d", &ret); + } + ClearNameValueList (&pdata); + return ret; +} + +/* UPNP_GetConnectionTypeInfo() call the corresponding UPNP method + * returns the connection type */ +int +UPNP_GetConnectionTypeInfo (const char *controlURL, + const char *servicetype, char *connectionType) +{ + struct NameValueParserData pdata; + char buffer[4096]; + int bufsize = 4096; + char *p; + int ret = UPNPCOMMAND_UNKNOWN_ERROR; + + if (!connectionType) + return UPNPCOMMAND_INVALID_ARGS; + + simpleUPnPcommand (-1, controlURL, servicetype, + "GetConnectionTypeInfo", 0, buffer, &bufsize); + ParseNameValue (buffer, bufsize, &pdata); + p = GetValueFromNameValueList (&pdata, "NewConnectionType"); + /*p = GetValueFromNameValueList(&pdata, "NewPossibleConnectionTypes"); */ + /* PossibleConnectionTypes will have several values.... */ + if (p) + { + strncpy (connectionType, p, 64); + connectionType[63] = '\0'; + ret = UPNPCOMMAND_SUCCESS; + } + else + connectionType[0] = '\0'; + p = GetValueFromNameValueList (&pdata, "errorCode"); + if (p) + { + ret = UPNPCOMMAND_UNKNOWN_ERROR; + sscanf (p, "%d", &ret); + } + ClearNameValueList (&pdata); + return ret; +} + +/* UPNP_GetLinkLayerMaxBitRate() call the corresponding UPNP method. + * Returns 2 values: Downloadlink bandwidth and Uplink bandwidth. + * One of the values can be null + * Note : GetLinkLayerMaxBitRates belongs to WANPPPConnection:1 only + * We can use the GetCommonLinkProperties from WANCommonInterfaceConfig:1 */ +int +UPNP_GetLinkLayerMaxBitRates (const char *controlURL, const char *servicetype, + unsigned int *bitrateDown, + unsigned int *bitrateUp) +{ + struct NameValueParserData pdata; + char buffer[4096]; + int bufsize = 4096; + int ret = UPNPCOMMAND_UNKNOWN_ERROR; + char *down; + char *up; + char *p; + + if (!bitrateDown && !bitrateUp) + return UPNPCOMMAND_INVALID_ARGS; + + /* shouldn't we use GetCommonLinkProperties ? */ + simpleUPnPcommand (-1, controlURL, servicetype, + "GetCommonLinkProperties", 0, buffer, &bufsize); + /*"GetLinkLayerMaxBitRates", 0, buffer, &bufsize); */ + /*DisplayNameValueList(buffer, bufsize); */ + ParseNameValue (buffer, bufsize, &pdata); + /*down = GetValueFromNameValueList(&pdata, "NewDownstreamMaxBitRate"); */ + /*up = GetValueFromNameValueList(&pdata, "NewUpstreamMaxBitRate"); */ + down = GetValueFromNameValueList (&pdata, "NewLayer1DownstreamMaxBitRate"); + up = GetValueFromNameValueList (&pdata, "NewLayer1UpstreamMaxBitRate"); + /*GetValueFromNameValueList(&pdata, "NewWANAccessType"); */ + /*GetValueFromNameValueList(&pdata, "NewPhysicalLinkSatus"); */ + if (down && up) + ret = UPNPCOMMAND_SUCCESS; + + if (bitrateDown) + { + if (down) + sscanf (down, "%u", bitrateDown); + else + *bitrateDown = 0; + } + + if (bitrateUp) + { + if (up) + sscanf (up, "%u", bitrateUp); + else + *bitrateUp = 0; + } + p = GetValueFromNameValueList (&pdata, "errorCode"); + if (p) + { + ret = UPNPCOMMAND_UNKNOWN_ERROR; + sscanf (p, "%d", &ret); + } + ClearNameValueList (&pdata); + return ret; +} + + +/* UPNP_GetExternalIPAddress() call the corresponding UPNP method. + * if the third arg is not null the value is copied to it. + * at least 128 bytes must be available + * + * Return values : + * 0 : SUCCESS + * NON ZERO : ERROR Either an UPnP error code or an unknown error. + * + * 402 Invalid Args - See UPnP Device Architecture section on Control. + * 501 Action Failed - See UPnP Device Architecture section on Control. + */ +int +UPNP_GetExternalIPAddress (const char *controlURL, + const char *servicetype, char *extIpAdd) +{ + struct NameValueParserData pdata; + char buffer[4096]; + int bufsize = 4096; + char *p; + int ret = UPNPCOMMAND_UNKNOWN_ERROR; + + if (!extIpAdd || !controlURL || !servicetype) + return UPNPCOMMAND_INVALID_ARGS; + + simpleUPnPcommand (-1, controlURL, servicetype, "GetExternalIPAddress", 0, + buffer, &bufsize); + /*DisplayNameValueList(buffer, bufsize); */ + ParseNameValue (buffer, bufsize, &pdata); + /*printf("external ip = %s\n", GetValueFromNameValueList(&pdata, "NewExternalIPAddress") ); */ + p = GetValueFromNameValueList (&pdata, "NewExternalIPAddress"); + if (p) + { + strncpy (extIpAdd, p, 128); + extIpAdd[127] = '\0'; + ret = UPNPCOMMAND_SUCCESS; + } + else + extIpAdd[0] = '\0'; + + p = GetValueFromNameValueList (&pdata, "errorCode"); + if (p) + { + ret = UPNPCOMMAND_UNKNOWN_ERROR; + sscanf (p, "%d", &ret); + } + + ClearNameValueList (&pdata); + return ret; +} + +int +UPNP_AddPortMapping (const char *controlURL, const char *servicetype, + const char *extPort, + const char *inPort, + const char *inClient, + const char *desc, + const char *proto, const char *remoteHost) +{ + struct UPNParg *AddPortMappingArgs; + char buffer[4096]; + int bufsize = 4096; + struct NameValueParserData pdata; + const char *resVal; + int ret; + + if (!inPort || !inClient || !proto || !extPort) + return UPNPCOMMAND_INVALID_ARGS; + + AddPortMappingArgs = calloc (9, sizeof (struct UPNParg)); + AddPortMappingArgs[0].elt = "NewRemoteHost"; + AddPortMappingArgs[0].val = remoteHost; + AddPortMappingArgs[1].elt = "NewExternalPort"; + AddPortMappingArgs[1].val = extPort; + AddPortMappingArgs[2].elt = "NewProtocol"; + AddPortMappingArgs[2].val = proto; + AddPortMappingArgs[3].elt = "NewInternalPort"; + AddPortMappingArgs[3].val = inPort; + AddPortMappingArgs[4].elt = "NewInternalClient"; + AddPortMappingArgs[4].val = inClient; + AddPortMappingArgs[5].elt = "NewEnabled"; + AddPortMappingArgs[5].val = "1"; + AddPortMappingArgs[6].elt = "NewPortMappingDescription"; + AddPortMappingArgs[6].val = desc ? desc : "libminiupnpc"; + AddPortMappingArgs[7].elt = "NewLeaseDuration"; + AddPortMappingArgs[7].val = "0"; + simpleUPnPcommand (-1, controlURL, servicetype, "AddPortMapping", + AddPortMappingArgs, buffer, &bufsize); + /*DisplayNameValueList(buffer, bufsize); */ + /*buffer[bufsize] = '\0'; */ + /*puts(buffer); */ + ParseNameValue (buffer, bufsize, &pdata); + resVal = GetValueFromNameValueList (&pdata, "errorCode"); + if (resVal) + { + /* printf("AddPortMapping errorCode = '%s'\n", resVal); */ + ret = UPNPCOMMAND_UNKNOWN_ERROR; + sscanf (resVal, "%d", &ret); + } + else + { + ret = UPNPCOMMAND_SUCCESS; + } + ClearNameValueList (&pdata); + free (AddPortMappingArgs); + return ret; +} + +int +UPNP_DeletePortMapping (const char *controlURL, const char *servicetype, + const char *extPort, const char *proto, + const char *remoteHost) +{ + /*struct NameValueParserData pdata; */ + struct UPNParg *DeletePortMappingArgs; + char buffer[4096]; + int bufsize = 4096; + struct NameValueParserData pdata; + const char *resVal; + int ret; + + if (!extPort || !proto) + return UPNPCOMMAND_INVALID_ARGS; + + DeletePortMappingArgs = calloc (4, sizeof (struct UPNParg)); + DeletePortMappingArgs[0].elt = "NewRemoteHost"; + DeletePortMappingArgs[0].val = remoteHost; + DeletePortMappingArgs[1].elt = "NewExternalPort"; + DeletePortMappingArgs[1].val = extPort; + DeletePortMappingArgs[2].elt = "NewProtocol"; + DeletePortMappingArgs[2].val = proto; + simpleUPnPcommand (-1, controlURL, servicetype, + "DeletePortMapping", + DeletePortMappingArgs, buffer, &bufsize); + /*DisplayNameValueList(buffer, bufsize); */ + ParseNameValue (buffer, bufsize, &pdata); + resVal = GetValueFromNameValueList (&pdata, "errorCode"); + if (resVal) + { + ret = UPNPCOMMAND_UNKNOWN_ERROR; + sscanf (resVal, "%d", &ret); + } + else + { + ret = UPNPCOMMAND_SUCCESS; + } + ClearNameValueList (&pdata); + free (DeletePortMappingArgs); + return ret; +} + +int +UPNP_GetGenericPortMappingEntry (const char *controlURL, + const char *servicetype, + const char *index, + char *extPort, + char *intClient, + char *intPort, + char *protocol, + char *desc, + char *enabled, char *rHost, char *duration) +{ + struct NameValueParserData pdata; + struct UPNParg *GetPortMappingArgs; + char buffer[4096]; + int bufsize = 4096; + char *p; + int r = UPNPCOMMAND_UNKNOWN_ERROR; + if (!index) + return UPNPCOMMAND_INVALID_ARGS; + intClient[0] = '\0'; + intPort[0] = '\0'; + GetPortMappingArgs = calloc (2, sizeof (struct UPNParg)); + GetPortMappingArgs[0].elt = "NewPortMappingIndex"; + GetPortMappingArgs[0].val = index; + simpleUPnPcommand (-1, controlURL, servicetype, + "GetGenericPortMappingEntry", + GetPortMappingArgs, buffer, &bufsize); + ParseNameValue (buffer, bufsize, &pdata); + p = GetValueFromNameValueList (&pdata, "NewRemoteHost"); + if (p && rHost) + { + strncpy (rHost, p, 64); + rHost[63] = '\0'; + } + p = GetValueFromNameValueList (&pdata, "NewExternalPort"); + if (p && extPort) + { + strncpy (extPort, p, 6); + extPort[5] = '\0'; + r = UPNPCOMMAND_SUCCESS; + } + p = GetValueFromNameValueList (&pdata, "NewProtocol"); + if (p && protocol) + { + strncpy (protocol, p, 4); + protocol[3] = '\0'; + } + p = GetValueFromNameValueList (&pdata, "NewInternalClient"); + if (p && intClient) + { + strncpy (intClient, p, 128); + intClient[127] = '\0'; + r = 0; + } + p = GetValueFromNameValueList (&pdata, "NewInternalPort"); + if (p && intPort) + { + strncpy (intPort, p, 6); + intPort[5] = '\0'; + } + p = GetValueFromNameValueList (&pdata, "NewEnabled"); + if (p && enabled) + { + strncpy (enabled, p, 4); + enabled[3] = '\0'; + } + p = GetValueFromNameValueList (&pdata, "NewPortMappingDescription"); + if (p && desc) + { + strncpy (desc, p, 80); + desc[79] = '\0'; + } + p = GetValueFromNameValueList (&pdata, "NewLeaseDuration"); + if (p && duration) + { + strncpy (duration, p, 16); + duration[15] = '\0'; + } + p = GetValueFromNameValueList (&pdata, "errorCode"); + if (p) + { + r = UPNPCOMMAND_UNKNOWN_ERROR; + sscanf (p, "%d", &r); + } + ClearNameValueList (&pdata); + free (GetPortMappingArgs); + return r; +} + +int +UPNP_GetPortMappingNumberOfEntries (const char *controlURL, + const char *servicetype, + unsigned int *numEntries) +{ + struct NameValueParserData pdata; + char buffer[4096]; + int bufsize = 4096; + char *p; + int ret = UPNPCOMMAND_UNKNOWN_ERROR; + simpleUPnPcommand (-1, controlURL, servicetype, + "GetPortMappingNumberOfEntries", 0, buffer, &bufsize); +#ifdef DEBUG + DisplayNameValueList (buffer, bufsize); +#endif + ParseNameValue (buffer, bufsize, &pdata); + + p = GetValueFromNameValueList (&pdata, "NewPortMappingNumberOfEntries"); + if (numEntries && p) + { + *numEntries = 0; + sscanf (p, "%u", numEntries); + ret = UPNPCOMMAND_SUCCESS; + } + + p = GetValueFromNameValueList (&pdata, "errorCode"); + if (p) + { + ret = UPNPCOMMAND_UNKNOWN_ERROR; + sscanf (p, "%d", &ret); + } + + ClearNameValueList (&pdata); + return ret; +} + +/* UPNP_GetSpecificPortMappingEntry retrieves an existing port mapping + * the result is returned in the intClient and intPort strings + * please provide 128 and 6 bytes of data */ +int +UPNP_GetSpecificPortMappingEntry (const char *controlURL, + const char *servicetype, + const char *extPort, + const char *proto, + char *intClient, char *intPort) +{ + struct NameValueParserData pdata; + struct UPNParg *GetPortMappingArgs; + char buffer[4096]; + int bufsize = 4096; + char *p; + int ret = UPNPCOMMAND_UNKNOWN_ERROR; + + if (!intPort || !intClient || !extPort || !proto) + return UPNPCOMMAND_INVALID_ARGS; + + GetPortMappingArgs = calloc (4, sizeof (struct UPNParg)); + GetPortMappingArgs[0].elt = "NewRemoteHost"; + GetPortMappingArgs[1].elt = "NewExternalPort"; + GetPortMappingArgs[1].val = extPort; + GetPortMappingArgs[2].elt = "NewProtocol"; + GetPortMappingArgs[2].val = proto; + simpleUPnPcommand (-1, controlURL, servicetype, + "GetSpecificPortMappingEntry", + GetPortMappingArgs, buffer, &bufsize); + /*fd = simpleUPnPcommand(fd, controlURL, data.servicetype, "GetSpecificPortMappingEntry", AddPortMappingArgs, buffer, &bufsize); */ + /*DisplayNameValueList(buffer, bufsize); */ + ParseNameValue (buffer, bufsize, &pdata); + + p = GetValueFromNameValueList (&pdata, "NewInternalClient"); + if (p) + { + strncpy (intClient, p, 128); + intClient[127] = '\0'; + ret = UPNPCOMMAND_SUCCESS; + } + else + intClient[0] = '\0'; + + p = GetValueFromNameValueList (&pdata, "NewInternalPort"); + if (p) + { + strncpy (intPort, p, 6); + intPort[5] = '\0'; + } + else + intPort[0] = '\0'; + + p = GetValueFromNameValueList (&pdata, "errorCode"); + if (p) + { + ret = UPNPCOMMAND_UNKNOWN_ERROR; + sscanf (p, "%d", &ret); + } + + ClearNameValueList (&pdata); + free (GetPortMappingArgs); + return ret; +} diff --git a/src/nat/miniupnp/upnpcommands.h b/src/nat/miniupnp/upnpcommands.h new file mode 100644 index 000000000..fa1d604ae --- /dev/null +++ b/src/nat/miniupnp/upnpcommands.h @@ -0,0 +1,189 @@ +/* $Id: upnpcommands.h,v 1.17 2009/04/17 21:21:19 nanard Exp $ */ +/* Miniupnp project : http://miniupnp.free.fr/ + * Author : Thomas Bernard + * Copyright (c) 2005-2008 Thomas Bernard + * This software is subject to the conditions detailed in the + * LICENCE file provided within this distribution */ +#ifndef __UPNPCOMMANDS_H__ +#define __UPNPCOMMANDS_H__ + +#include "upnpreplyparse.h" +#include "declspec.h" + +/* MiniUPnPc return codes : */ +#define UPNPCOMMAND_SUCCESS (0) +#define UPNPCOMMAND_UNKNOWN_ERROR (-1) +#define UPNPCOMMAND_INVALID_ARGS (-2) + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L) +#define UNSIGNED_INTEGER unsigned long long +#define STRTOUI strtoull +#else +#define UNSIGNED_INTEGER unsigned int +#define STRTOUI strtoul +#endif + + LIBSPEC UNSIGNED_INTEGER + UPNP_GetTotalBytesSent (const char *controlURL, const char *servicetype); + + LIBSPEC UNSIGNED_INTEGER + UPNP_GetTotalBytesReceived (const char *controlURL, + const char *servicetype); + + LIBSPEC UNSIGNED_INTEGER + UPNP_GetTotalPacketsSent (const char *controlURL, + const char *servicetype); + + LIBSPEC UNSIGNED_INTEGER + UPNP_GetTotalPacketsReceived (const char *controlURL, + const char *servicetype); + +/* UPNP_GetStatusInfo() + * status and lastconnerror are 64 byte buffers + * Return values : + * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR + * or a UPnP Error code */ + LIBSPEC int + UPNP_GetStatusInfo (const char *controlURL, + const char *servicetype, + char *status, + unsigned int *uptime, char *lastconnerror); + +/* UPNP_GetConnectionTypeInfo() + * argument connectionType is a 64 character buffer + * Return Values : + * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR + * or a UPnP Error code */ + LIBSPEC int + UPNP_GetConnectionTypeInfo (const char *controlURL, + const char *servicetype, + char *connectionType); + +/* UPNP_GetExternalIPAddress() call the corresponding UPNP method. + * if the third arg is not null the value is copied to it. + * at least 128 bytes must be available + * + * Return values : + * 0 : SUCCESS + * NON ZERO : ERROR Either an UPnP error code or an unknown error. + * + * possible UPnP Errors : + * 402 Invalid Args - See UPnP Device Architecture section on Control. + * 501 Action Failed - See UPnP Device Architecture section on Control. */ + LIBSPEC int + UPNP_GetExternalIPAddress (const char *controlURL, + const char *servicetype, char *extIpAdd); + +/* UPNP_GetLinkLayerMaxBitRates() + * call WANCommonInterfaceConfig:1#GetCommonLinkProperties + * + * return values : + * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR + * or a UPnP Error Code. */ + LIBSPEC int + UPNP_GetLinkLayerMaxBitRates (const char *controlURL, + const char *servicetype, + unsigned int *bitrateDown, + unsigned int *bitrateUp); + +/* UPNP_AddPortMapping() + * if desc is NULL, it will be defaulted to "libminiupnpc" + * remoteHost is usually NULL because IGD don't support it. + * + * Return values : + * 0 : SUCCESS + * NON ZERO : ERROR. Either an UPnP error code or an unknown error. + * + * List of possible UPnP errors for AddPortMapping : + * errorCode errorDescription (short) - Description (long) + * 402 Invalid Args - See UPnP Device Architecture section on Control. + * 501 Action Failed - See UPnP Device Architecture section on Control. + * 715 WildCardNotPermittedInSrcIP - The source IP address cannot be + * wild-carded + * 716 WildCardNotPermittedInExtPort - The external port cannot be wild-carded + * 718 ConflictInMappingEntry - The port mapping entry specified conflicts + * with a mapping assigned previously to another client + * 724 SamePortValuesRequired - Internal and External port values + * must be the same + * 725 OnlyPermanentLeasesSupported - The NAT implementation only supports + * permanent lease times on port mappings + * 726 RemoteHostOnlySupportsWildcard - RemoteHost must be a wildcard + * and cannot be a specific IP address or DNS name + * 727 ExternalPortOnlySupportsWildcard - ExternalPort must be a wildcard and + * cannot be a specific port value */ + LIBSPEC int + UPNP_AddPortMapping (const char *controlURL, const char *servicetype, + const char *extPort, + const char *inPort, + const char *inClient, + const char *desc, + const char *proto, const char *remoteHost); + +/* UPNP_DeletePortMapping() + * Use same argument values as what was used for AddPortMapping(). + * remoteHost is usually NULL because IGD don't support it. + * Return Values : + * 0 : SUCCESS + * NON ZERO : error. Either an UPnP error code or an undefined error. + * + * List of possible UPnP errors for DeletePortMapping : + * 402 Invalid Args - See UPnP Device Architecture section on Control. + * 714 NoSuchEntryInArray - The specified value does not exist in the array */ + LIBSPEC int + UPNP_DeletePortMapping (const char *controlURL, const char *servicetype, + const char *extPort, const char *proto, + const char *remoteHost); + +/* UPNP_GetPortMappingNumberOfEntries() + * not supported by all routers */ + LIBSPEC int + UPNP_GetPortMappingNumberOfEntries (const char *controlURL, + const char *servicetype, + unsigned int *num); + +/* UPNP_GetSpecificPortMappingEntry retrieves an existing port mapping + * the result is returned in the intClient and intPort strings + * please provide 128 and 6 bytes of data + * + * return value : + * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR + * or a UPnP Error Code. */ + LIBSPEC int + UPNP_GetSpecificPortMappingEntry (const char *controlURL, + const char *servicetype, + const char *extPort, + const char *proto, + char *intClient, char *intPort); + +/* UPNP_GetGenericPortMappingEntry() + * + * return value : + * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR + * or a UPnP Error Code. + * + * Possible UPNP Error codes : + * 402 Invalid Args - See UPnP Device Architecture section on Control. + * 713 SpecifiedArrayIndexInvalid - The specified array index is out of bounds + */ + LIBSPEC int + UPNP_GetGenericPortMappingEntry (const char *controlURL, + const char *servicetype, + const char *index, + char *extPort, + char *intClient, + char *intPort, + char *protocol, + char *desc, + char *enabled, + char *rHost, char *duration); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/nat/miniupnp/upnpreplyparse.c b/src/nat/miniupnp/upnpreplyparse.c new file mode 100644 index 000000000..9aa895d1d --- /dev/null +++ b/src/nat/miniupnp/upnpreplyparse.c @@ -0,0 +1,122 @@ +/* $Id: upnpreplyparse.c,v 1.10 2008/02/21 13:05:27 nanard Exp $ */ +/* MiniUPnP project + * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ + * (c) 2006 Thomas Bernard + * This software is subject to the conditions detailed + * in the LICENCE file provided within the distribution */ + +#include +#include +#include + +#include "upnpreplyparse.h" +#include "minixml.h" + +static void +NameValueParserStartElt (void *d, const char *name, int l) +{ + struct NameValueParserData *data = (struct NameValueParserData *) d; + if (l > 63) + l = 63; + memcpy (data->curelt, name, l); + data->curelt[l] = '\0'; +} + +static void +NameValueParserGetData (void *d, const char *datas, int l) +{ + struct NameValueParserData *data = (struct NameValueParserData *) d; + struct NameValue *nv; + nv = malloc (sizeof (struct NameValue)); + if (l > 63) + l = 63; + strncpy (nv->name, data->curelt, 64); + nv->name[63] = '\0'; + memcpy (nv->value, datas, l); + nv->value[l] = '\0'; + LIST_INSERT_HEAD (&(data->head), nv, entries); +} + +void +ParseNameValue (const char *buffer, int bufsize, + struct NameValueParserData *data) +{ + struct xmlparser parser; + LIST_INIT (&(data->head)); + /* init xmlparser object */ + parser.xmlstart = buffer; + parser.xmlsize = bufsize; + parser.data = data; + parser.starteltfunc = NameValueParserStartElt; + parser.endeltfunc = 0; + parser.datafunc = NameValueParserGetData; + parser.attfunc = 0; + parsexml (&parser); +} + +void +ClearNameValueList (struct NameValueParserData *pdata) +{ + struct NameValue *nv; + while ((nv = pdata->head.lh_first) != NULL) + { + LIST_REMOVE (nv, entries); + free (nv); + } +} + +char * +GetValueFromNameValueList (struct NameValueParserData *pdata, + const char *Name) +{ + struct NameValue *nv; + char *p = NULL; + for (nv = pdata->head.lh_first; + (nv != NULL) && (p == NULL); nv = nv->entries.le_next) + { + if (strcmp (nv->name, Name) == 0) + p = nv->value; + } + return p; +} + +#if 0 +/* useless now that minixml ignores namespaces by itself */ +char * +GetValueFromNameValueListIgnoreNS (struct NameValueParserData *pdata, + const char *Name) +{ + struct NameValue *nv; + char *p = NULL; + char *pname; + for (nv = pdata->head.lh_first; + (nv != NULL) && (p == NULL); nv = nv->entries.le_next) + { + pname = strrchr (nv->name, ':'); + if (pname) + pname++; + else + pname = nv->name; + if (strcmp (pname, Name) == 0) + p = nv->value; + } + return p; +} +#endif + +/* debug all-in-one function + * do parsing then display to stdout */ +#ifdef DEBUG +void +DisplayNameValueList (char *buffer, int bufsize) +{ + struct NameValueParserData pdata; + struct NameValue *nv; + ParseNameValue (buffer, bufsize, &pdata); + for (nv = pdata.head.lh_first; nv != NULL; nv = nv->entries.le_next) + { + printf ("%s = %s\n", nv->name, nv->value); + } + ClearNameValueList (&pdata); +} +#endif diff --git a/src/nat/miniupnp/upnpreplyparse.h b/src/nat/miniupnp/upnpreplyparse.h new file mode 100644 index 000000000..a1c8a9431 --- /dev/null +++ b/src/nat/miniupnp/upnpreplyparse.h @@ -0,0 +1,60 @@ +/* $Id: upnpreplyparse.h,v 1.8 2008/02/21 13:05:27 nanard Exp $ */ +/* MiniUPnP project + * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ + * (c) 2006 Thomas Bernard + * This software is subject to the conditions detailed + * in the LICENCE file provided within the distribution */ + +#ifndef __UPNPREPLYPARSE_H__ +#define __UPNPREPLYPARSE_H__ + +#if defined(NO_SYS_QUEUE_H) || defined(WIN32) +#include "bsdqueue.h" +#else +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + + struct NameValue + { + LIST_ENTRY (NameValue) entries; + char name[64]; + char value[64]; + }; + + struct NameValueParserData + { + LIST_HEAD (listhead, NameValue) head; + char curelt[64]; + }; + +/* ParseNameValue() */ + void + ParseNameValue (const char *buffer, int bufsize, + struct NameValueParserData *data); + +/* ClearNameValueList() */ + void ClearNameValueList (struct NameValueParserData *pdata); + +/* GetValueFromNameValueList() */ + char *GetValueFromNameValueList (struct NameValueParserData *pdata, + const char *Name); + +/* GetValueFromNameValueListIgnoreNS() */ + char *GetValueFromNameValueListIgnoreNS (struct NameValueParserData *pdata, + const char *Name); + +/* DisplayNameValueList() */ +#ifdef DEBUG + void DisplayNameValueList (char *buffer, int bufsize); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/nat/nat.c b/src/nat/nat.c new file mode 100644 index 000000000..dbe4ef643 --- /dev/null +++ b/src/nat/nat.c @@ -0,0 +1,375 @@ +/* + This file is part of GNUnet. + (C) 2009 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. +*/ + +/* + * Parts of this file have been adapted from the Transmission project: + * Originally licensed by the GPL version 2. + * Copyright (C) 2007-2009 Charles Kerr + */ + +/** + * @file nat/nat.c + * @brief Library handling UPnP and NAT-PMP port forwarding and + * external IP address retrieval + * + * @author Milan Bouchet-Valat + */ + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + +#include +#include +#include + +#include + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_util_lib.h" +#include "gnunet_nat_lib.h" +#include "natpmp.h" +#include "upnp.h" + +/* Component name for logging */ +#define COMP_NAT _("NAT") +#define DEBUG + +struct GNUNET_NAT_Handle +{ + int is_enabled; + + GNUNET_NAT_port_forwarding natpmp_status; + GNUNET_NAT_port_forwarding upnp_status; + + int should_change; + u_short public_port; + + GNUNET_NAT_UPNP_Handle *upnp; + GNUNET_NAT_NATPMP_Handle *natpmp; + + struct GNUNET_SCHEDULER_Handle *sched; + GNUNET_SCHEDULER_TaskIdentifier pulse_timer; + + struct sockaddr *local_addr; /* LAN address as passed by the caller */ + struct sockaddr *ext_addr; /* External address as reported by NAT box */ + struct sockaddr *contact_addr; /* External address and port where paquets are redirected*/ + GNUNET_NAT_AddressCallback callback; + void *callback_cls; +}; + +#ifdef DEBUG +static const char * +get_nat_state_str (int state) +{ + switch (state) + { + /* we're in the process of trying to set up port forwarding */ + case GNUNET_NAT_PORT_MAPPING: + return "Starting"; + + /* we've successfully forwarded the port */ + case GNUNET_NAT_PORT_MAPPED: + return "Forwarded"; + + /* we're cancelling the port forwarding */ + case GNUNET_NAT_PORT_UNMAPPING: + return "Stopping"; + + /* the port isn't forwarded */ + case GNUNET_NAT_PORT_UNMAPPED: + return "Not forwarded"; + + case GNUNET_NAT_PORT_ERROR: + return "Redirection failed"; + } + + return "notfound"; +} +#endif + +static int +get_traversal_status (const GNUNET_NAT_Handle * s) +{ + return MAX (s->natpmp_status, s->upnp_status); +} + +/** + * Compare the sin(6)_addr fields of AF_INET or AF_INET(6) sockaddr. + * @param a first sockaddr + * @param second sockaddr + * @returns 0 if addresses are equal, non-null value otherwise */ +int +GNUNET_NAT_cmp_addr (const struct sockaddr *a, const struct sockaddr *b) +{ + if (!(a && b)) + return -1; + + if (a->sa_family == AF_INET && b->sa_family == AF_INET) + return memcmp (&(((struct sockaddr_in *) a)->sin_addr), + &(((struct sockaddr_in *) b)->sin_addr), + sizeof (struct in_addr)); + else if (a->sa_family == AF_INET6 && b->sa_family == AF_INET6) + return memcmp (&(((struct sockaddr_in6 *) a)->sin6_addr), + &(((struct sockaddr_in6 *) b)->sin6_addr), + sizeof (struct in6_addr)); + else + return -1; +} + +/* Deal with a new IP address or port redirection: + * Send signals with the appropriate sockaddr (IP and port), free and changes + * or nullify the previous sockaddr. Change the port if needed. + */ +static void +notify_change (GNUNET_NAT_Handle *nat, struct sockaddr *addr, int new_port_mapped) +{ + static int port_mapped = GNUNET_NO; + + /* Nothing to do. We already check in nat_pulse() that addr has changed */ + if (new_port_mapped == port_mapped) + return; + + port_mapped = new_port_mapped; + + if (nat->contact_addr && nat->callback) + (*nat->callback) (nat->callback_cls, GNUNET_NO, (struct sockaddr *) &nat->contact_addr, + sizeof (nat->contact_addr)); + + /* At this point, we're sure contact_addr has changed */ + if (nat->contact_addr) + { + GNUNET_free (nat->contact_addr); + nat->contact_addr = NULL; + } + + /* No address, don't signal a new one */ + if (!addr) + { + if (nat->ext_addr) + GNUNET_free (nat->ext_addr); + nat->ext_addr = NULL; + return; + } + /* Copy the new address and use it */ + else if (addr != nat->ext_addr) + { + if (nat->ext_addr) + GNUNET_free (nat->ext_addr); + nat->ext_addr = GNUNET_malloc (sizeof (*addr)); + memcpy (nat->ext_addr, addr, sizeof (*addr)); + } + + /* Recreate the ext_addr:public_port bogus address to pass to the callback */ + if (nat->ext_addr->sa_family == AF_INET) + { + struct sockaddr_in *tmp_addr; + tmp_addr = GNUNET_malloc (sizeof (struct sockaddr_in)); + tmp_addr->sin_family = AF_INET; +#ifdef HAVE_SOCKADDR_IN_SIN_LEN + tmp_addr->sin_len = sizeof (struct sockaddr_in); +#endif + tmp_addr->sin_port = port_mapped ? htons (nat->public_port) : 0; + tmp_addr->sin_addr = ((struct sockaddr_in *) nat->ext_addr)->sin_addr; + nat->contact_addr = (struct sockaddr *) tmp_addr; + if (nat->callback) + (*nat->callback) (nat->callback_cls, GNUNET_YES, nat->contact_addr, + sizeof (struct sockaddr_in)); + } + else if (nat->ext_addr->sa_family == AF_INET6) + { + struct sockaddr_in6 *tmp_addr; + tmp_addr = GNUNET_malloc (sizeof (struct sockaddr_in6)); + tmp_addr->sin6_family = AF_INET6; +#ifdef HAVE_SOCKADDR_IN_SIN_LEN + tmp_addr->sin6_len = sizeof (struct sockaddr_in6); +#endif + tmp_addr->sin6_port = port_mapped ? htons (nat->public_port) : 0; + tmp_addr->sin6_addr = ((struct sockaddr_in6 *) nat->ext_addr)->sin6_addr; + nat->contact_addr = (struct sockaddr *) tmp_addr; + if (nat->callback) + (*nat->callback) (nat->callback_cls, GNUNET_YES, nat->contact_addr, + sizeof (struct sockaddr_in6)); + } +} + +static void +nat_pulse (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_NAT_Handle *nat = cls; + static int first_warning = GNUNET_YES; + int old_status; + int new_status; + int port_mapped; + struct sockaddr *ext_addr_upnp = NULL; + struct sockaddr *ext_addr_natpmp = NULL; + + old_status = get_traversal_status (nat); + + /* Only update the protocol that has been successful until now */ + if (nat->upnp_status >= GNUNET_NAT_PORT_UNMAPPED) + nat->upnp_status = + GNUNET_NAT_UPNP_pulse (nat->upnp, nat->is_enabled, GNUNET_YES, + &ext_addr_upnp); + else if (nat->natpmp_status >= GNUNET_NAT_PORT_UNMAPPED) + nat->natpmp_status = + GNUNET_NAT_NATPMP_pulse (nat->natpmp, nat->is_enabled, + &ext_addr_natpmp); + else + { + nat->upnp_status = + GNUNET_NAT_UPNP_pulse (nat->upnp, nat->is_enabled, GNUNET_YES, + &ext_addr_upnp); + nat->natpmp_status = + GNUNET_NAT_NATPMP_pulse (nat->natpmp, nat->is_enabled, + &ext_addr_natpmp); + } + + new_status = get_traversal_status (nat); + + if (old_status != new_status && + (new_status == GNUNET_NAT_PORT_UNMAPPED || new_status == GNUNET_NAT_PORT_ERROR)) + GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT, + _("Port redirection failed: no UPnP or NAT-PMP routers supporting this feature found\n")); + +#ifdef DEBUG + if (new_status != old_status) + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT, + _("State changed from \"%s\" to \"%s\"\n"), + get_nat_state_str (old_status), + get_nat_state_str (new_status)); +#endif + + port_mapped = (new_status == GNUNET_NAT_PORT_MAPPED); + if (!(ext_addr_upnp || ext_addr_natpmp)) + { + /* Address has just changed and we could not get it, or it's the first try */ + if (nat->ext_addr || first_warning) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT, + _("Could not determine external IP address\n")); + first_warning = GNUNET_NO; + } + + notify_change (nat, NULL, port_mapped); + } + else if (ext_addr_upnp && GNUNET_NAT_cmp_addr (nat->ext_addr, ext_addr_upnp) != 0) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT, + _("External IP address changed from %s to %s\n"), + GNUNET_a2s (nat->ext_addr, sizeof (nat->ext_addr)), + GNUNET_a2s (ext_addr_upnp, sizeof (ext_addr_upnp))); + + notify_change (nat, ext_addr_upnp, port_mapped); + } + else if (ext_addr_natpmp && GNUNET_NAT_cmp_addr (nat->ext_addr, ext_addr_natpmp) != 0) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT, + _("External IP address changed from %s to %s\n"), + GNUNET_a2s (nat->ext_addr, sizeof (nat->ext_addr)), + GNUNET_a2s (ext_addr_natpmp, sizeof (ext_addr_natpmp))); + + notify_change (nat, ext_addr_natpmp, port_mapped); + } + + nat->pulse_timer = GNUNET_SCHEDULER_add_delayed (nat->sched, GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_DEFAULT, + GNUNET_SCHEDULER_NO_TASK, + GNUNET_TIME_UNIT_SECONDS, + &nat_pulse, nat); +} + +struct GNUNET_NAT_Handle * +GNUNET_NAT_register (struct GNUNET_SCHEDULER_Handle *sched, + const struct sockaddr *addr, socklen_t addrlen, + GNUNET_NAT_AddressCallback callback, void *callback_cls) +{ + GNUNET_NAT_Handle *nat = GNUNET_malloc (sizeof (GNUNET_NAT_Handle)); + + if (addr) + { + GNUNET_assert (addr->sa_family == AF_INET + || addr->sa_family == AF_INET6); + if (addr->sa_family == AF_INET) + { + nat->public_port = ntohs (((struct sockaddr_in *) addr)->sin_port); +// ((struct sockaddr_in *) addr)->sin_port = 0; + } + else if (addr->sa_family == AF_INET6) + { + nat->public_port = ntohs (((struct sockaddr_in6 *) addr)->sin6_port); +// ((struct sockaddr_in6 *) addr)->sin6_port = 0; + } + } + + nat->should_change = GNUNET_YES; + nat->sched = sched; + nat->is_enabled = GNUNET_YES; + nat->upnp_status = GNUNET_NAT_PORT_UNMAPPED; + nat->natpmp_status = GNUNET_NAT_PORT_UNMAPPED; + nat->callback = callback; + nat->callback_cls = callback_cls; + nat->ext_addr = NULL; + nat->contact_addr = NULL; + nat->local_addr = GNUNET_malloc (addrlen); + memcpy (nat->local_addr, addr, addrlen); + nat->natpmp = GNUNET_NAT_NATPMP_init (nat->local_addr, addrlen, nat->public_port); + nat->upnp = GNUNET_NAT_UPNP_init (nat->local_addr, addrlen, nat->public_port); + + nat->pulse_timer = GNUNET_SCHEDULER_add_delayed (sched, GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_DEFAULT, + GNUNET_SCHEDULER_NO_TASK, + GNUNET_TIME_UNIT_SECONDS, + &nat_pulse, nat); + + return nat; +} + +void +GNUNET_NAT_unregister (struct GNUNET_NAT_Handle *nat) +{ + struct sockaddr *addr; + GNUNET_SCHEDULER_cancel (nat->sched, nat->pulse_timer); + + nat->upnp_status = + GNUNET_NAT_UPNP_pulse (nat->upnp, GNUNET_NO, GNUNET_NO, + &addr); + nat->natpmp_status = + GNUNET_NAT_NATPMP_pulse (nat->natpmp, GNUNET_NO, + &addr); + + GNUNET_NAT_NATPMP_close (nat->natpmp); + GNUNET_NAT_UPNP_close (nat->upnp); + if (nat->ext_addr) + GNUNET_free (nat->ext_addr); + GNUNET_free (nat); +} + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif diff --git a/src/nat/natpmp.c b/src/nat/natpmp.c new file mode 100644 index 000000000..335228767 --- /dev/null +++ b/src/nat/natpmp.c @@ -0,0 +1,297 @@ +/* + This file is part of GNUnet. + (C) 2009 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. +*/ + +/* + * This file has been adapted from the Transmission project: + * Originally licensed by the GPL version 2. + * Copyright (C) 2007-2009 Charles Kerr + */ + +/** + * @file nat/natpmp.c + * @brief NAT-PMP support for the NAT library + * + * @author Milan Bouchet-Valat + */ + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + +#include +#include +#include +#include + +#define ENABLE_STRNATPMPERR +#include + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_nat_lib.h" +#include "natpmp.h" + +#define LIFETIME_SECS 3600 +#define COMMAND_WAIT_SECS 8 +/* Component name for logging */ +#define COMP_NAT_NATPMP _("NAT (NAT-PMP))") + +typedef enum +{ + NATPMP_IDLE, + NATPMP_ERR, + NATPMP_DISCOVER, + NATPMP_RECV_PUB, + NATPMP_SEND_MAP, + NATPMP_RECV_MAP, + NATPMP_SEND_UNMAP, + NATPMP_RECV_UNMAP +} +NATPMP_state; + +struct GNUNET_NAT_NATPMP_Handle +{ + const struct sockaddr *addr; + socklen_t addrlen; + int is_mapped; + int has_discovered; + int port; + time_t renew_time; + time_t command_time; + NATPMP_state state; + natpmp_t natpmp; +}; + +/** +*** +**/ + +static void +log_val (const char *func, int ret) +{ +#ifdef DEBUG + if (ret == NATPMP_TRYAGAIN) + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_NATPMP, _("%s retry (%d)\n"), func, ret); +// return; + if (ret >= 0) + GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_NATPMP, _("%s succeeded (%d)\n"), func, ret); + else + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_NATPMP, + "%s failed. natpmp returned %d (%s); errno is %d (%s)\n", + func, ret, strnatpmperr (ret), errno, strerror (errno)); +#else + return; +#endif +} + +struct GNUNET_NAT_NATPMP_Handle * +GNUNET_NAT_NATPMP_init (const struct sockaddr *addr, socklen_t addrlen, + u_short port) +{ + struct GNUNET_NAT_NATPMP_Handle *nat; + + nat = GNUNET_malloc (sizeof (struct GNUNET_NAT_NATPMP_Handle)); + nat->state = NATPMP_DISCOVER; + nat->port = port; + nat->addr = addr; + nat->addrlen = addrlen; + return nat; +} + +void +GNUNET_NAT_NATPMP_close (GNUNET_NAT_NATPMP_Handle * nat) +{ + if (nat) + { + closenatpmp (&nat->natpmp); + GNUNET_free (nat); + } +} + +static int +can_send_command (const struct GNUNET_NAT_NATPMP_Handle *nat) +{ + return time (NULL) >= nat->command_time; +} + +static void +set_command_time (struct GNUNET_NAT_NATPMP_Handle *nat) +{ + nat->command_time = time (NULL) + COMMAND_WAIT_SECS; +} + +int +GNUNET_NAT_NATPMP_pulse (struct GNUNET_NAT_NATPMP_Handle *nat, int is_enabled, + struct sockaddr **ext_addr) +{ + int ret; + + /* Keep to NULL if address could not be found */ + *ext_addr = NULL; + + if (is_enabled && (nat->state == NATPMP_DISCOVER)) + { + int val = initnatpmp (&nat->natpmp); + log_val ("initnatpmp", val); + val = sendpublicaddressrequest (&nat->natpmp); + log_val ("sendpublicaddressrequest", val); + nat->state = val < 0 ? NATPMP_ERR : NATPMP_RECV_PUB; + nat->has_discovered = 1; + set_command_time (nat); + } + + if ((nat->state == NATPMP_RECV_PUB) && can_send_command (nat)) + { + natpmpresp_t response; + const int val = readnatpmpresponseorretry (&nat->natpmp, + &response); + log_val ("readnatpmpresponseorretry", val); + if (val >= 0) + { + *ext_addr = + GNUNET_malloc (sizeof (response.pnu.publicaddress.addr)); + memcpy (*ext_addr, &response.pnu.publicaddress.addr, + (sizeof (response.pnu.publicaddress.addr))); +#ifdef DEBUG + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_NATPMP, + _("Found public IP address %s\n"), + GNUNET_a2s (*ext_addr, sizeof (*ext_addr))); +#endif + nat->state = NATPMP_IDLE; + } + else if (val != NATPMP_TRYAGAIN) + { + nat->state = NATPMP_ERR; + } + } + + if ((nat->state == NATPMP_IDLE) || (nat->state == NATPMP_ERR)) + { + if (nat->is_mapped && !is_enabled) + nat->state = NATPMP_SEND_UNMAP; + } + + if ((nat->state == NATPMP_SEND_UNMAP) && can_send_command (nat)) + { + const int val = + sendnewportmappingrequest (&nat->natpmp, NATPMP_PROTOCOL_TCP, + nat->port, nat->port, + 0); + log_val ("sendnewportmappingrequest", val); + nat->state = val < 0 ? NATPMP_ERR : NATPMP_RECV_UNMAP; + set_command_time (nat); + } + + if (nat->state == NATPMP_RECV_UNMAP) + { + natpmpresp_t resp; + const int val = readnatpmpresponseorretry (&nat->natpmp, &resp); + log_val ("readnatpmpresponseorretry", val); + if (val >= 0) + { + const int p = resp.pnu.newportmapping.privateport; + GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_NATPMP, + _("No longer forwarding port %d\n"), p); + if (nat->port == p) + { + nat->port = -1; + nat->state = NATPMP_IDLE; + nat->is_mapped = 0; + } + } + else if (val != NATPMP_TRYAGAIN) + { + nat->state = NATPMP_ERR; + } + } + + if (nat->state == NATPMP_IDLE) + { + if (is_enabled && !nat->is_mapped && nat->has_discovered) + nat->state = NATPMP_SEND_MAP; + + else if (nat->is_mapped && time (NULL) >= nat->renew_time) + nat->state = NATPMP_SEND_MAP; + } + + if ((nat->state == NATPMP_SEND_MAP) && can_send_command (nat)) + { + const int val = + sendnewportmappingrequest (&nat->natpmp, NATPMP_PROTOCOL_TCP, + nat->port, + nat->port, + LIFETIME_SECS); + log_val ("sendnewportmappingrequest", val); + nat->state = val < 0 ? NATPMP_ERR : NATPMP_RECV_MAP; + set_command_time (nat); + } + + if (nat->state == NATPMP_RECV_MAP) + { + natpmpresp_t resp; + const int val = readnatpmpresponseorretry (&nat->natpmp, &resp); + log_val ("readnatpmpresponseorretry", val); + if (val >= 0) + { + nat->state = NATPMP_IDLE; + nat->is_mapped = 1; + nat->renew_time = time (NULL) + LIFETIME_SECS; + nat->port = resp.pnu.newportmapping.privateport; + GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_NATPMP, + _("Port %d forwarded successfully\n"), nat->port); + } + else if (val != NATPMP_TRYAGAIN) + { + nat->state = NATPMP_ERR; + } + } + + switch (nat->state) + { + case NATPMP_IDLE: + ret = + nat->is_mapped ? GNUNET_NAT_PORT_MAPPED : GNUNET_NAT_PORT_UNMAPPED; + break; + + case NATPMP_DISCOVER: + ret = GNUNET_NAT_PORT_UNMAPPED; + break; + + case NATPMP_RECV_PUB: + case NATPMP_SEND_MAP: + case NATPMP_RECV_MAP: + ret = GNUNET_NAT_PORT_MAPPING; + break; + + case NATPMP_SEND_UNMAP: + case NATPMP_RECV_UNMAP: + ret = GNUNET_NAT_PORT_UNMAPPING; + break; + + default: + ret = GNUNET_NAT_PORT_ERROR; + break; + } + return ret; +} diff --git a/src/nat/natpmp.h b/src/nat/natpmp.h new file mode 100644 index 000000000..9acb46b9c --- /dev/null +++ b/src/nat/natpmp.h @@ -0,0 +1,44 @@ +/* + This file is part of GNUnet. + (C) 2009 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 nat/natpmp.h + * @brief NAT-PMP support for the NAT library + * + * @author Milan Bouchet-Valat + */ + +#ifndef NATPMP_H +#define NATPMP_H 1 + +#include "platform.h" + +typedef struct GNUNET_NAT_NATPMP_Handle GNUNET_NAT_NATPMP_Handle; + +GNUNET_NAT_NATPMP_Handle *GNUNET_NAT_NATPMP_init (const struct sockaddr *addr, + socklen_t addrlen, + unsigned short port); + +void GNUNET_NAT_NATPMP_close (GNUNET_NAT_NATPMP_Handle * nat); + +int GNUNET_NAT_NATPMP_pulse (GNUNET_NAT_NATPMP_Handle * nat, int is_enabled, + struct sockaddr **ext_addr); + +#endif /* NATPMP_H */ diff --git a/src/nat/upnp.c b/src/nat/upnp.c new file mode 100644 index 000000000..05c774edd --- /dev/null +++ b/src/nat/upnp.c @@ -0,0 +1,357 @@ +/* + This file is part of GNUnet. + (C) 2009 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. +*/ + +/* + * This file has been adapted from the Transmission project: + * Originally licensed by the GPL version 2. + * Copyright (C) 2007-2009 Charles Kerr + */ + +/** + * @file nat/upnp.c + * @brief UPnP support for the NAT library + * + * @author Milan Bouchet-Valat + */ + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + +#include +#include +#include +#include + +#include +#include + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_nat_lib.h" +#include "upnp.h" + +/* Component name for logging */ +#define COMP_NAT_UPNP _("NAT (UPnP)") + +typedef enum +{ + UPNP_IDLE, + UPNP_ERR, + UPNP_DISCOVER, + UPNP_MAP, + UPNP_UNMAP +} +UPNP_state; + +struct GNUNET_NAT_UPNP_Handle +{ + int hasDiscovered; + struct UPNPUrls urls; + struct IGDdatas data; + int port; + const struct sockaddr *addr; + socklen_t addrlen; + unsigned int is_mapped; + UPNP_state state; + struct sockaddr *ext_addr; + const char *iface; +}; + +static int +process_if (void *cls, + const char *name, + int isDefault, + const struct sockaddr *addr, + socklen_t addrlen) +{ + struct GNUNET_NAT_UPNP_Handle *upnp = cls; + + if (addr && GNUNET_NAT_cmp_addr (upnp->addr, addr) == 0) + { + upnp->iface = name; + return GNUNET_SYSERR; + } + + return GNUNET_OK; +} + +GNUNET_NAT_UPNP_Handle * +GNUNET_NAT_UPNP_init (const struct sockaddr *addr, socklen_t addrlen, + u_short port) +{ + GNUNET_NAT_UPNP_Handle *upnp = + GNUNET_malloc (sizeof (GNUNET_NAT_UPNP_Handle)); + + upnp->state = UPNP_DISCOVER; + upnp->addr = addr; + upnp->addrlen = addrlen; + upnp->port = port; + + /* Find the interface corresponding to the address, + * on which we should broadcast call for routers */ + upnp->iface = NULL; + GNUNET_OS_network_interfaces_list (process_if, upnp); + if (!upnp->iface) + GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, COMP_NAT_UPNP, "Could not find an interface matching the wanted address.\n"); + + return upnp; +} + +void +GNUNET_NAT_UPNP_close (GNUNET_NAT_UPNP_Handle * handle) +{ + GNUNET_assert (!handle->is_mapped); + GNUNET_assert ((handle->state == UPNP_IDLE) + || (handle->state == UPNP_ERR) || (handle->state == UPNP_DISCOVER)); + + if (handle->hasDiscovered) + FreeUPNPUrls (&handle->urls); + GNUNET_free (handle); +} + +/** + * Check state of UPnP NAT: port redirection, external IP address. + * + * + * @param handle the handle for UPnP object + * @param is_enabled whether enable port redirection + * @param ext_addr pointer for returning external IP address. + * Will be set to NULL if address could not be found. Don't free the sockaddr. + */ +int +GNUNET_NAT_UPNP_pulse (GNUNET_NAT_UPNP_Handle * handle, int is_enabled, + int doPortCheck, struct sockaddr **ext_addr) +{ + int ret; + + if (is_enabled && (handle->state == UPNP_DISCOVER)) + { + struct UPNPDev *devlist; + errno = 0; + devlist = upnpDiscover (2000, handle->iface, handle->addr, NULL, 0); + if (devlist == NULL) + { +#ifdef DEBUG + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP, + "upnpDiscover failed (errno %d - %s)\n", errno, + strerror (errno)); +#endif + } + errno = 0; + if (UPNP_GetValidIGD (devlist, &handle->urls, &handle->data, + NULL, 0)) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP, + _("Found Internet Gateway Device \"%s\"\n"), + handle->urls.controlURL); + handle->state = UPNP_IDLE; + handle->hasDiscovered = 1; + } + else + { + handle->state = UPNP_ERR; +#ifdef DEBUG + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP, + "UPNP_GetValidIGD failed (errno %d - %s)\n", + errno, strerror (errno)); +#endif + } + freeUPNPDevlist (devlist); + } + + if (handle->state == UPNP_IDLE) + { + if (handle->is_mapped && !is_enabled) + handle->state = UPNP_UNMAP; + } + + if (is_enabled && handle->is_mapped && doPortCheck) + { + char portStr[8]; + char intPort[8]; + char intClient[128]; + int i; + + GNUNET_snprintf (portStr, sizeof (portStr), "%d", handle->port); + i = UPNP_GetSpecificPortMappingEntry (handle->urls.controlURL, + handle->data.servicetype, portStr, + "TCP", intClient, intPort); + if (i != UPNPCOMMAND_SUCCESS) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP, + _("Port %d isn't forwarded\n"), handle->port); + handle->is_mapped = GNUNET_NO; + } + } + + if (handle->state == UPNP_UNMAP) + { + char portStr[16]; + GNUNET_snprintf (portStr, sizeof (portStr), "%d", handle->port); + UPNP_DeletePortMapping (handle->urls.controlURL, + handle->data.servicetype, portStr, "TCP", NULL); + GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP, + _ + ("Stopping port forwarding through \"%s\", service \"%s\"\n"), + handle->urls.controlURL, handle->data.servicetype); + handle->is_mapped = 0; + handle->state = UPNP_IDLE; + handle->port = -1; + } + + if (handle->state == UPNP_IDLE) + { + if (is_enabled && !handle->is_mapped) + handle->state = UPNP_MAP; + } + + if (handle->state == UPNP_MAP) + { + int err = -1; + errno = 0; + + if (!handle->urls.controlURL || !handle->data.servicetype) + handle->is_mapped = 0; + else + { + char portStr[16]; + char desc[64]; + GNUNET_snprintf (portStr, sizeof (portStr), "%d", handle->port); + GNUNET_snprintf (desc, sizeof (desc), "GNUnet at %d", handle->port); + err = UPNP_AddPortMapping (handle->urls.controlURL, + handle->data.servicetype, + portStr, portStr, GNUNET_a2s (handle->addr, handle->addrlen), + desc, "TCP", NULL); + handle->is_mapped = !err; + } + GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP, + _ + ("Port forwarding through \"%s\", service \"%s\"\n"), + handle->urls.controlURL, handle->data.servicetype); + if (handle->is_mapped) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP, + _("Port %d forwarded successfully\n"), handle->port); + handle->state = UPNP_IDLE; + } + else + { + GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP, + "Port forwarding failed with error %d (errno %d - %s)\n", + err, errno, strerror (errno)); + handle->state = UPNP_ERR; + } + } + + if (ext_addr && handle->state != UPNP_DISCOVER) + { + int err; + char addr_str[128]; + struct in_addr addr; + struct in6_addr addr6; + + /* Keep to NULL if address could not be found */ + *ext_addr = NULL; + err = UPNP_GetExternalIPAddress (handle->urls.controlURL, + handle->data.servicetype, addr_str); + if (err == 0) + { + if (handle->ext_addr) + { + GNUNET_free (handle->ext_addr); + handle->ext_addr = NULL; + } + + /* Try IPv4 and IPv6 as we don't know what's the format */ + if (inet_aton (addr_str, &addr) != 0) + { + handle->ext_addr = GNUNET_malloc (sizeof (struct sockaddr_in)); + handle->ext_addr->sa_family = AF_INET; + ((struct sockaddr_in *) handle->ext_addr)->sin_addr = addr; + *ext_addr = handle->ext_addr; + } + else if (inet_pton (AF_INET6, addr_str, &addr6) != 1) + { + handle->ext_addr = GNUNET_malloc (sizeof (struct sockaddr_in6)); + handle->ext_addr->sa_family = AF_INET6; + ((struct sockaddr_in6 *) handle->ext_addr)->sin6_addr = addr6; + *ext_addr = handle->ext_addr; + } + else + GNUNET_assert (GNUNET_YES); +#ifdef DEBUG + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP, + _("Found public IP address %s\n"), + addr_str); +#endif + } + else + { + *ext_addr = NULL; + if (handle->ext_addr) + { + GNUNET_free (handle->ext_addr); + handle->ext_addr = NULL; + } +#ifdef DEBUG + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP, + "UPNP_GetExternalIPAddress failed (error %d)\n", err); +#endif + } + } + + switch (handle->state) + { + case UPNP_DISCOVER: + ret = GNUNET_NAT_PORT_UNMAPPED; + break; + + case UPNP_MAP: + ret = GNUNET_NAT_PORT_MAPPING; + break; + + case UPNP_UNMAP: + ret = GNUNET_NAT_PORT_UNMAPPING; + break; + + case UPNP_IDLE: + ret = + handle->is_mapped ? GNUNET_NAT_PORT_MAPPED : GNUNET_NAT_PORT_UNMAPPED; + break; + + default: + ret = GNUNET_NAT_PORT_ERROR; + break; + } + + return ret; +} + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif diff --git a/src/nat/upnp.h b/src/nat/upnp.h new file mode 100644 index 000000000..18bba4c12 --- /dev/null +++ b/src/nat/upnp.h @@ -0,0 +1,43 @@ +/* + This file is part of GNUnet. + (C) 2009 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 nat/upnp.h + * @brief UPnP support for the NAT library + * + * @author Milan Bouchet-Valat + */ + +#ifndef UPNP_H +#define UPNP_H 1 + +#include "platform.h" + +typedef struct GNUNET_NAT_UPNP_Handle GNUNET_NAT_UPNP_Handle; + +GNUNET_NAT_UPNP_Handle *GNUNET_NAT_UPNP_init (const struct sockaddr *addr, + socklen_t addrlen, + unsigned short port); + +void GNUNET_NAT_UPNP_close (GNUNET_NAT_UPNP_Handle *); + +int GNUNET_NAT_UPNP_pulse (GNUNET_NAT_UPNP_Handle *, int is_enabled, + int do_port_check, struct sockaddr **ext_addr); +#endif /* UPNP_H */ -- 2.25.1