Merge branch 'master' into search_all

Conflicts:
	miniupnpc/.gitignore
	miniupnpc/Makefile
	miniupnpc/miniupnpc.c
This commit is contained in:
Thomas Bernard 2014-11-13 11:02:28 +01:00
commit 9325bacbfc
118 changed files with 7134 additions and 1880 deletions

3
README
View File

@ -46,4 +46,5 @@ Thanks to :
* Peter Tatrai
* Leo Moll
* Daniel Becker
* Yonetani Tomokazu
* Yonetani Tomokazu
* Markus Stenberg

View File

@ -1,4 +1,15 @@
$Id: Changelog.txt,v 1.33 2014/02/03 15:45:07 nanard Exp $
$Id: Changelog.txt,v 1.36 2014/11/06 10:13:36 nanard Exp $
2014/11/06:
listen on only 1 IPv4 if only 1 interface is specified
also when ENABLE_IPV6 is not defined
2014/09/06:
freebsd-glue for Debian/kFreeBSD
use LDFLAGS when linking binary
2014/05/01:
listen on only 1 IPv4 if only 1 interface is specified
2014/02/03:
silently ignore EAGAIN, EWOULDBLOCK, EINTR of recv calls

View File

@ -1,4 +1,4 @@
# $Id: Makefile,v 1.18 2014/02/03 14:32:14 nanard Exp $
# $Id: Makefile,v 1.19 2014/06/10 10:00:18 nanard Exp $
# MiniUPnP project
# author: Thomas Bernard
# website: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
@ -53,7 +53,11 @@ ifneq ($(OS), Darwin)
endif
minissdpd: $(MINISSDPDOBJS)
$(CC) $(CFLAGS) -o $@ $(MINISSDPDOBJS)
if [ "$(DEB_BUILD_ARCH_OS)" = "kfreebsd" ] ; then \
$(CC) $(CFLAGS) $(LDFLAGS) -lfreebsd-glue -o $@ $(MINISSDPDOBJS) ; \
else \
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(MINISSDPDOBJS) ; \
fi
testminissdpd: $(TESTMINISSDPDOBJS)

View File

@ -1,4 +1,4 @@
/* $Id: minissdpd.c,v 1.37 2014/02/28 18:39:11 nanard Exp $ */
/* $Id: minissdpd.c,v 1.39 2014/11/06 10:13:06 nanard Exp $ */
/* MiniUPnP project
* (c) 2007-2014 Thomas Bernard
* website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
@ -261,6 +261,7 @@ processMSEARCH(int s, const char * st, int st_len,
ntohs(((const struct sockaddr_in *)addr)->sin_port),
st_len, st);
#endif
/* TODO : ignore packet if not coming from a LAN */
if(st_len==8 && (0==memcmp(st, "ssdp:all", 8))) {
/* send a response for all services */
for(serv = servicelisthead.lh_first;
@ -875,7 +876,9 @@ int main(int argc, char * * argv)
const char * sockpath = "/var/run/minissdpd.sock";
const char * pidfilename = "/var/run/minissdpd.pid";
int debug_flag = 0;
#ifdef ENABLE_IPV6
int ipv6 = 0;
#endif /* ENABLE_IPV6 */
int deltadev = 0;
struct sockaddr_in sendername;
socklen_t sendername_len;
@ -909,8 +912,12 @@ int main(int argc, char * * argv)
if(n_if_addr < 1)
{
fprintf(stderr,
"Usage: %s [-d] [-6] [-s socket] [-p pidfile] "
"-i <interface> [-i <interface2>] ...\n",
"Usage: %s [-d] "
#ifdef ENABLE_IPV6
"[-6] "
#endif
"[-s socket] [-p pidfile] "
"-i <interface> [-i <interface2>] ...\n",
argv[0]);
fprintf(stderr,
"\n <interface> is either an IPv4 address such as 192.168.1.42, or an\ninterface name such as eth0.\n");

View File

@ -1,7 +1,7 @@
/* $Id: openssdpsocket.c,v 1.12 2012/05/21 17:13:11 nanard Exp $ */
/* $Id: openssdpsocket.c,v 1.14 2014/11/06 10:13:36 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2012 Thomas Bernard
* (c) 2006-2014 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
@ -81,7 +81,9 @@ AddDropMulticastMembership(int s, const char * ifaddr, int ipv6, int drop)
#ifdef ENABLE_IPV6
struct ipv6_mreq mr;
unsigned int ifindex;
#endif
#else /* ENABLE_IPV6 */
(void)ipv6;
#endif /* ENABLE_IPV6 */
if(s <= 0)
return -1; /* nothing to do */
@ -115,10 +117,10 @@ AddDropMulticastMembership(int s, const char * ifaddr, int ipv6, int drop)
else
{
#endif
/* setting up imr structure */
imr.imr_multiaddr.s_addr = inet_addr(SSDP_MCAST_ADDR);
/*imr.imr_interface.s_addr = htonl(INADDR_ANY);*/
/*imr.imr_interface.s_addr = inet_addr(ifaddr);*/
/* setting up imr structure */
imr.imr_multiaddr.s_addr = inet_addr(SSDP_MCAST_ADDR);
/*imr.imr_interface.s_addr = htonl(INADDR_ANY);*/
/*imr.imr_interface.s_addr = inet_addr(ifaddr);*/
imr.imr_interface.s_addr = GetIfAddrIPv4(ifaddr);
if(imr.imr_interface.s_addr == INADDR_NONE)
{
@ -130,11 +132,11 @@ AddDropMulticastMembership(int s, const char * ifaddr, int ipv6, int drop)
if (setsockopt(s, IPPROTO_IP, drop ? IP_DROP_MEMBERSHIP : IP_ADD_MEMBERSHIP,
(void *)&imr, sizeof(struct ip_mreq)) < 0)
{
syslog(LOG_ERR, "setsockopt(udp, %s)(%s): %m",
syslog(LOG_ERR, "setsockopt(udp, %s)(%s): %m",
drop ? "IP_DROP_MEMBERSHIP" : "IP_ADD_MEMBERSHIP",
ifaddr);
return -1;
}
}
#ifdef ENABLE_IPV6
}
#endif
@ -182,27 +184,48 @@ OpenAndConfSSDPReceiveSocket(int n_listen_addr,
}
#endif
struct sockaddr_in6 * sa = (struct sockaddr_in6 *)&sockname;
sa->sin6_family = AF_INET6;
sa->sin6_port = htons(SSDP_PORT);
sa->sin6_family = AF_INET6;
sa->sin6_port = htons(SSDP_PORT);
sa->sin6_addr = in6addr_any;
sockname_len = sizeof(struct sockaddr_in6);
}
else
{
struct sockaddr_in * sa = (struct sockaddr_in *)&sockname;
sa->sin_family = AF_INET;
sa->sin_port = htons(SSDP_PORT);
sa->sin_addr.s_addr = htonl(INADDR_ANY);
sa->sin_family = AF_INET;
sa->sin_port = htons(SSDP_PORT);
if(n_listen_addr == 1)
{
sa->sin_addr.s_addr = GetIfAddrIPv4(listen_addr[0]);
if(sa->sin_addr.s_addr == INADDR_NONE)
{
syslog(LOG_ERR, "no IPv4 address for interface %s",
listen_addr[0]);
close(s);
return -1;
}
}
else
sa->sin_addr.s_addr = htonl(INADDR_ANY);
sockname_len = sizeof(struct sockaddr_in);
}
#else
memset(&sockname, 0, sizeof(struct sockaddr_in));
sockname.sin_family = AF_INET;
sockname.sin_port = htons(SSDP_PORT);
/* NOTE : it seems it doesnt work when binding on the specific address */
/*sockname.sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);*/
sockname.sin_addr.s_addr = htonl(INADDR_ANY);
/*sockname.sin_addr.s_addr = inet_addr(ifaddr);*/
if(n_listen_addr == 1)
{
sockname.sin_addr.s_addr = GetIfAddrIPv4(listen_addr[0]);
if(sockname.sin_addr.s_addr == INADDR_NONE)
{
syslog(LOG_ERR, "no IPv4 address for interface %s",
listen_addr[0]);
close(s);
return -1;
}
}
else
sockname.sin_addr.s_addr = htonl(INADDR_ANY);
sockname_len = sizeof(struct sockaddr_in);
#endif

3
miniupnpc-async/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.a
*.o
testasync

View File

@ -0,0 +1,4 @@
$Id: Changelog.txt,v 1.1 2009/11/12 14:04:33 nanard Exp $
initial version...

104
miniupnpc-async/Makefile Normal file
View File

@ -0,0 +1,104 @@
# $Id: Makefile,v 1.9 2014/11/04 22:25:00 nanard Exp $
# MiniUPnP Project
# http://miniupnp.free.fr/
# (c) 2005-2014 Thomas Bernard
# to install use :
# $ PREFIX=/tmp/dummylocation make install
# or
# $ INSTALLPREFIX=/usr/local make install
# or
# make install (will go to /usr/bin, /usr/lib, etc...)
CC = gcc
#AR = gar
CFLAGS = -O0 -g -DDEBUG
#CFLAGS = -O
CFLAGS += -fPIC
CFLAGS += -ansi
CFLAGS += -Wall -W
CFLAGS += -D_BSD_SOURCE
CFLAGS += -DUPNPC_USE_SELECT
INSTALL = install
#following libs are needed on Solaris
#LDLIBS=-lsocket -lnsl -lresolv
# APIVERSION is used to build SONAME
APIVERSION = 0
SRCS = miniupnpc-async.c parsessdpreply.c \
upnputils.c igd_desc_parse.c minixml.c \
upnpreplyparse.c \
testasync.c
LIBOBJS = miniupnpc-async.o parsessdpreply.o \
upnputils.o igd_desc_parse.o minixml.o \
upnpreplyparse.o
OBJS = $(patsubst %.c,%.o,$(SRCS))
# HEADERS to install
HEADERS = miniupnpc-async.h
LIBRARY = libminiupnpc-async.a
SHAREDLIBRARY = libminiupnpc-async.so
SONAME = $(SHAREDLIBRARY).$(APIVERSION)
EXECUTABLES = testasync
INSTALLPREFIX ?= $(PREFIX)/usr
INSTALLDIRINC = $(INSTALLPREFIX)/include/miniupnpc
INSTALLDIRLIB = $(INSTALLPREFIX)/lib
INSTALLDIRBIN = $(INSTALLPREFIX)/bin
.PHONY: install clean depend all installpythonmodule
all: $(LIBRARY) $(EXECUTABLES)
pythonmodule: $(LIBRARY) miniupnpcmodule.c setup.py
python setup.py build
touch $@
installpythonmodule: pythonmodule
python setup.py install
clean:
$(RM) $(LIBRARY) $(SHAREDLIBRARY) $(EXECUTABLES) $(OBJS)
# clean python stuff
$(RM) pythonmodule
$(RM) -r build/ dist/
#python setup.py clean
install: $(LIBRARY) $(SHAREDLIBRARY)
$(INSTALL) -d $(INSTALLDIRINC)
$(INSTALL) -m 644 $(HEADERS) $(INSTALLDIRINC)
$(INSTALL) -d $(INSTALLDIRLIB)
$(INSTALL) -m 644 $(LIBRARY) $(INSTALLDIRLIB)
$(INSTALL) -m 644 $(SHAREDLIBRARY) $(INSTALLDIRLIB)/$(SONAME)
$(INSTALL) -m 755 upnpc-shared $(INSTALLDIRBIN)/upnpc
ln -fs $(SONAME) $(INSTALLDIRLIB)/$(SHAREDLIBRARY)
cleaninstall:
$(RM) -r $(INSTALLDIRINC)
$(RM) $(INSTALLDIRLIB)/$(LIBRARY)
$(RM) $(INSTALLDIRLIB)/$(SHAREDLIBRARY)
depend:
makedepend -Y -- $(CFLAGS) -- $(SRCS) 2>/dev/null
$(LIBRARY): $(LIBOBJS)
$(AR) crs $@ $?
$(SHAREDLIBRARY): $(LIBOBJS)
$(CC) -shared -Wl,-soname,$(SONAME) -o $@ $^
upnpc-static: upnpc.o $(LIBRARY)
$(CC) -o $@ $^
upnpc-shared: upnpc.o $(SHAREDLIBRARY)
$(CC) -o $@ $^
#testasync: testasync.o libminiupnpc-async.a
testasync: testasync.o -lminiupnpc-async
# DO NOT DELETE THIS LINE -- make depend depends on it.
miniupnpc-async.o: miniupnpc-async.h declspec.h parsessdpreply.h upnputils.h
parsessdpreply.o: parsessdpreply.h
testasync.o: miniupnpc-async.h declspec.h

10
miniupnpc-async/README Normal file
View File

@ -0,0 +1,10 @@
(c) 2014 Thomas BERNARD
http://miniupnp.free.fr/
https://github.com/miniupnp/miniupnp
miniupnpc-async :
proof of concept of a UPnP IGD client using asynchronous socket calls
(ie non blocking sockets)
To be reimplemented using libevent2 (http://libevent.org/)

5
miniupnpc-async/config.h Normal file
View File

@ -0,0 +1,5 @@
/* $Id: config.h,v 1.1 2012/05/20 14:58:50 nanard Exp $ */
#ifndef __CONFIG_H__
#define __CONFIG_H__
#endif

View File

@ -0,0 +1,15 @@
#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

View File

@ -0,0 +1,120 @@
/* $Id: igd_desc_parse.c,v 1.15 2014/07/01 13:01:17 nanard Exp $ */
/* Project : miniupnp
* http://miniupnp.free.fr/
* Author : Thomas Bernard
* Copyright (c) 2005-2014 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution. */
#include "igd_desc_parse.h"
#include <stdio.h>
#include <string.h>
/* 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->tmp.controlurl[0] = '\0';
datas->tmp.eventsuburl[0] = '\0';
datas->tmp.scpdurl[0] = '\0';
datas->tmp.servicetype[0] = '\0';
}
}
#define COMPARE(str, cstr) (0==memcmp(str, cstr, sizeof(cstr) - 1))
/* 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(COMPARE(datas->tmp.servicetype,
"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:")) {
memcpy(&datas->CIF, &datas->tmp, sizeof(struct IGDdatas_service));
} else if(COMPARE(datas->tmp.servicetype,
"urn:schemas-upnp-org:service:WANIPv6FirewallControl:")) {
memcpy(&datas->IPv6FC, &datas->tmp, sizeof(struct IGDdatas_service));
} else if(COMPARE(datas->tmp.servicetype,
"urn:schemas-upnp-org:service:WANIPConnection:")
|| COMPARE(datas->tmp.servicetype,
"urn:schemas-upnp-org:service:WANPPPConnection:") ) {
if(datas->first.servicetype[0] == '\0') {
memcpy(&datas->first, &datas->tmp, sizeof(struct IGDdatas_service));
} else {
memcpy(&datas->second, &datas->tmp, sizeof(struct IGDdatas_service));
}
}
}
}
/* 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, "presentationURL") )
dstmember = datas->presentationurl;
else if( !strcmp(datas->cureltname, "serviceType") )
dstmember = datas->tmp.servicetype;
else if( !strcmp(datas->cureltname, "controlURL") )
dstmember = datas->tmp.controlurl;
else if( !strcmp(datas->cureltname, "eventSubURL") )
dstmember = datas->tmp.eventsuburl;
else if( !strcmp(datas->cureltname, "SCPDURL") )
dstmember = datas->tmp.scpdurl;
/* 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->CIF.devicetype);*/
printf(" serviceType = '%s'\n", d->CIF.servicetype);
printf(" controlURL = '%s'\n", d->CIF.controlurl);
printf(" eventSubURL = '%s'\n", d->CIF.eventsuburl);
printf(" SCPDURL = '%s'\n", d->CIF.scpdurl);
printf("primary WAN Connection Device (IP or PPP Connection):\n");
/*printf(" deviceType = '%s'\n", d->first.devicetype);*/
printf(" servicetype = '%s'\n", d->first.servicetype);
printf(" controlURL = '%s'\n", d->first.controlurl);
printf(" eventSubURL = '%s'\n", d->first.eventsuburl);
printf(" SCPDURL = '%s'\n", d->first.scpdurl);
printf("secondary WAN Connection Device (IP or PPP Connection):\n");
/*printf(" deviceType = '%s'\n", d->second.devicetype);*/
printf(" servicetype = '%s'\n", d->second.servicetype);
printf(" controlURL = '%s'\n", d->second.controlurl);
printf(" eventSubURL = '%s'\n", d->second.eventsuburl);
printf(" SCPDURL = '%s'\n", d->second.scpdurl);
printf("WAN IPv6 Firewall Control :\n");
/*printf(" deviceType = '%s'\n", d->IPv6FC.devicetype);*/
printf(" servicetype = '%s'\n", d->IPv6FC.servicetype);
printf(" controlURL = '%s'\n", d->IPv6FC.controlurl);
printf(" eventSubURL = '%s'\n", d->IPv6FC.eventsuburl);
printf(" SCPDURL = '%s'\n", d->IPv6FC.scpdurl);
}

View File

@ -0,0 +1,48 @@
/* $Id: igd_desc_parse.h,v 1.11 2012/10/16 16:49:02 nanard Exp $ */
/* Project : miniupnp
* http://miniupnp.free.fr/
* Author : Thomas Bernard
* Copyright (c) 2005-2010 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution.
* */
#ifndef IGD_DESC_PARSE_H_INCLUDED
#define IGD_DESC_PARSE_H_INCLUDED
/* Structure to store the result of the parsing of UPnP
* descriptions of Internet Gateway Devices */
#define MINIUPNPC_URL_MAXSIZE (128)
struct IGDdatas_service {
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];*/
};
struct IGDdatas {
char cureltname[MINIUPNPC_URL_MAXSIZE];
char urlbase[MINIUPNPC_URL_MAXSIZE];
char presentationurl[MINIUPNPC_URL_MAXSIZE];
int level;
/*int state;*/
/* "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" */
struct IGDdatas_service CIF;
/* "urn:schemas-upnp-org:service:WANIPConnection:1"
* "urn:schemas-upnp-org:service:WANPPPConnection:1" */
struct IGDdatas_service first;
/* if both WANIPConnection and WANPPPConnection are present */
struct IGDdatas_service second;
/* "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1" */
struct IGDdatas_service IPv6FC;
/* tmp */
struct IGDdatas_service tmp;
};
void IGDstartelt(void *, const char *, int);
void IGDendelt(void *, const char *, int);
void IGDdata(void *, const char *, int);
void printIGD(struct IGDdatas *);
#endif

View File

@ -0,0 +1,963 @@
/* $Id: miniupnpc-async.c,v 1.19 2014/11/07 12:05:40 nanard Exp $ */
/* miniupnpc-async
* Copyright (c) 2008-2014, Thomas BERNARD <miniupnp@free.fr>
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
*
* 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 <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <stdio.h>
#ifdef WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#include <io.h>
#define PRINT_SOCKET_ERROR printf
#define SOCKET_ERROR GetWSALastError()
#define WOULDBLOCK(err) (err == WSAEWOULDBLOCK)
#else
#include <unistd.h>
#include <errno.h>
#define closesocket close
#define PRINT_SOCKET_ERROR perror
#define SOCKET_ERROR errno
#define WOULDBLOCK(err) (err == EAGAIN || err == EWOULDBLOCK)
#endif
#include "miniupnpc-async.h"
#include "parsessdpreply.h"
#include "upnputils.h"
#include "minixml.h"
#include "igd_desc_parse.h"
#include "upnpreplyparse.h"
#ifndef MIN
#define MIN(x,y) (((x)<(y))?(x):(y))
#endif /* MIN */
#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 64
#endif /* MAXHOSTNAMELEN */
#define SSDP_PORT 1900
#define SSDP_MCAST_ADDR "239.255.255.250"
#define XSTR(s) STR(s)
#define STR(s) #s
#ifdef DEBUG
#define debug_printf(...) fprintf(stderr, __VA_ARGS__)
#else
#define debug_printf(...)
#endif
/* stuctures */
struct upnp_args {
const char * elt;
const char * val;
};
/* private functions */
static int upnpc_connect(upnpc_t * p, const char * url);
static int upnpc_send_request(upnpc_t * p);
/* parse_msearch_reply()
* 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
parse_msearch_reply(const char * reply, int size,
const char * * location, int * locationsize,
const char * * st, int * stsize)
{
int a, b, i;
i = 0; /* current character index */
a = i; /* start of the line */
b = 0; /* end of the "header" (position of the colon) */
while(i<size) {
switch(reply[i]) {
case ':':
if(b==0) {
b = i; /* end of the "header" */
}
break;
case '\x0a':
case '\x0d':
if(b!=0) {
/* skip the colon and white spaces */
do { b++; } while(reply[b]==' ' && b<size);
if(0==strncasecmp(reply+a, "location", 8)) {
*location = reply+b;
*locationsize = i-b;
} else if(0==strncasecmp(reply+a, "st", 2)) {
*st = reply+b;
*stsize = i-b;
}
b = 0;
}
a = i+1;
break;
default:
break;
}
i++;
}
}
static int upnpc_send_ssdp_msearch(upnpc_t * p, const char * device, unsigned int mx)
{
/* envoyer les packets de M-SEARCH discovery sur le socket ssdp */
int n;
char bufr[1024];
struct sockaddr_in addr;
static const char MSearchMsgFmt[] =
"M-SEARCH * HTTP/1.1\r\n"
"HOST: " SSDP_MCAST_ADDR ":" XSTR(SSDP_PORT) "\r\n"
"ST: %s\r\n"
"MAN: \"ssdp:discover\"\r\n"
"MX: %u\r\n"
"\r\n";
memset(&addr, 0, sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_port = htons(SSDP_PORT);
addr.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
n = snprintf(bufr, sizeof(bufr),
MSearchMsgFmt, device, mx);
debug_printf("upnpc_send_ssdp_msearch: %s", bufr);
n = sendto(p->ssdp_socket, bufr, n, 0,
(struct sockaddr *)&addr, sizeof(struct sockaddr_in));
if (n < 0) {
int err = SOCKET_ERROR;
if(err == EINTR || WOULDBLOCK(err)) {
debug_printf("upnpc_send_ssdp_msearch: should try again");
p->state = ESendSSDP;
return 0;
}
PRINT_SOCKET_ERROR("sendto");
return -1;
}
p->state = EReceiveSSDP;
return 0;
}
static int upnpc_set_root_desc_location(upnpc_t * p, const char * location, int locationsize)
{
char * tmp;
tmp = realloc(p->root_desc_location, locationsize + 1);
if(tmp == 0) {
return -1;
}
memcpy(tmp, location, locationsize);
tmp[locationsize] = '\0';
p->root_desc_location = tmp;
return 0;
}
static int upnpc_receive_and_parse_ssdp(upnpc_t * p)
{
int n;
char bufr[1024];
n = recv(p->ssdp_socket, bufr, sizeof(bufr), 0);
if (n<0) {
PRINT_SOCKET_ERROR("recv");
} else if (n==0) {
debug_printf("empty packet received\n");
} else {
const char * location = NULL;
int locationsize;
const char * st = NULL;
int stsize;
debug_printf("%.*s", n, bufr);
parse_msearch_reply(bufr, n, &location, &locationsize, &st, &stsize);
debug_printf("location = '%.*s'\n", locationsize, location);
debug_printf("st = '%.*s'\n", stsize, st);
if(location != NULL) {
if(upnpc_set_root_desc_location(p, location, locationsize) < 0) {
p->state = EError;
return -1;
}
p->state = EGetDescConnect;
upnpc_connect(p, p->root_desc_location);
} else {
/* or do nothing ? */
p->state = EError;
}
}
return 0;
}
static int
parseURL(const char * url,
char * hostname, unsigned short * port,
char * * path, unsigned int * scope_id)
{
char * p1, *p2, *p3;
if(!url)
return 0;
p1 = strstr(url, "://");
if(!p1)
return 0;
p1 += 3;
if( (url[0]!='h') || (url[1]!='t')
||(url[2]!='t') || (url[3]!='p'))
return 0;
memset(hostname, 0, MAXHOSTNAMELEN + 1);
if(*p1 == '[') {
/* IP v6 : http://[2a00:1450:8002::6a]/path/abc */
char * scope;
scope = strchr(p1, '%');
p2 = strchr(p1, ']');
if(p2 && scope && scope < p2 && scope_id) {
/* parse scope */
#ifdef IF_NAMESIZE
char tmp[IF_NAMESIZE];
int l;
scope++;
/* "%25" is just '%' in URL encoding */
if(scope[0] == '2' && scope[1] == '5')
scope += 2; /* skip "25" */
l = p2 - scope;
if(l >= IF_NAMESIZE)
l = IF_NAMESIZE - 1;
memcpy(tmp, scope, l);
tmp[l] = '\0';
*scope_id = if_nametoindex(tmp);
if(*scope_id == 0) {
*scope_id = (unsigned int)strtoul(tmp, NULL, 10);
}
#else
/* under windows, scope is numerical */
char tmp[8];
int l;
scope++;
/* "%25" is just '%' in URL encoding */
if(scope[0] == '2' && scope[1] == '5')
scope += 2; /* skip "25" */
l = p2 - scope;
if(l >= (int)sizeof(tmp))
l = sizeof(tmp) - 1;
memcpy(tmp, scope, l);
tmp[l] = '\0';
*scope_id = (unsigned int)strtoul(tmp, NULL, 10);
#endif
}
p3 = strchr(p1, '/');
if(p2 && p3) {
p2++;
strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1)));
if(*p2 == ':') {
*port = 0;
p2++;
while( (*p2 >= '0') && (*p2 <= '9')) {
*port *= 10;
*port += (unsigned short)(*p2 - '0');
p2++;
}
} else {
*port = 80;
}
*path = p3;
return 1;
}
}
p2 = strchr(p1, ':');
p3 = strchr(p1, '/');
if(!p3)
return 0;
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;
}
static int upnpc_connect(upnpc_t * p, const char * url)
{
int r;
char hostname[MAXHOSTNAMELEN+1];
unsigned short port;
char * path;
unsigned int scope_id;
struct sockaddr_in addr;
socklen_t addrlen;
if(p->root_desc_location == 0) {
p->state = EError;
return -1;
}
if(!parseURL(url/*p->root_desc_location*/, hostname, &port,
&path, &scope_id)) {
p->state = EError;
return -1;
}
p->http_socket = socket(PF_INET, SOCK_STREAM, 0);
if(p->http_socket < 0) {
PRINT_SOCKET_ERROR("socket");
p->state = EError;
return -1;
}
if(!set_non_blocking(p->http_socket)) {
/* TODO : ERROR */
}
memset(&addr, 0, sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
inet_pton(AF_INET, hostname, &(addr.sin_addr));
addr.sin_port = htons(port);
addrlen = sizeof(struct sockaddr_in);
do {
r = connect(p->http_socket, (struct sockaddr *)&addr, addrlen);
if(r < 0) {
if(errno == EINPROGRESS) {
/*p->state = EGetDescConnect;*/
return 0;
} else if(errno != EINTR) {
PRINT_SOCKET_ERROR("connect");
p->state = EError;
return -1;
}
}
} while(r < 0 && errno == EINTR);
if(p->state == EGetDescConnect) {
p->state = EGetDescRequest;
} else {
p->state = ESoapRequest;
}
upnpc_send_request(p);
return 0;
}
static int upnpc_complete_connect(upnpc_t * p)
{
socklen_t len;
int err;
len = sizeof(err);
if(getsockopt(p->http_socket, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
PRINT_SOCKET_ERROR("getsockopt");
p->state = EError;
return -1;
}
if(err != 0) {
debug_printf("connect failed %d\n", err);
p->state = EError;
return -1;
}
if(p->state == EGetDescConnect)
p->state = EGetDescRequest;
else
p->state = ESoapRequest;
upnpc_send_request(p);
return 0;
}
static int upnpc_send_request(upnpc_t * p)
{
ssize_t n;
static const char reqfmt[] = "GET %s HTTP/1.1\r\n"
"Host: %s:%hu\r\n"
"Connection: Close\r\n"
"User-Agent: MiniUPnPc-async\r\n"
"\r\n";
if(p->http_request == NULL) {
char hostname[MAXHOSTNAMELEN+1];
unsigned short port;
char * path;
unsigned int scope_id;
int len;
if(!parseURL(p->root_desc_location, hostname, &port,
&path, &scope_id)) {
p->state = EError;
return -1;
}
len = snprintf(NULL, 0, reqfmt, path, hostname, port);
p->http_request = malloc(len + 1);
if(p->http_request == NULL) {
p->state = EError;
return -1;
}
p->http_request_len = snprintf(p->http_request, len + 1,
reqfmt, path, hostname, port);
p->http_request_sent = 0;
}
n = send(p->http_socket, p->http_request + p->http_request_sent,
p->http_request_len - p->http_request_sent, 0/* flags */);
if(n < 0) {
PRINT_SOCKET_ERROR("send");
p->state = EError;
return -1;
} else {
debug_printf("sent %d bytes\n", (int)n);
/*if(n == 0) {
p->state = EError;
return -1;
}*/
p->http_request_sent += n;
if(p->http_request_sent >= p->http_request_len) {
/* all bytes sent */
#if 0
shutdown(p->http_socket, SHUT_WR); /* some routers don't like that */
#endif
free(p->http_request);
p->http_request = NULL;
p->http_request_len = 0;
if(p->state == EGetDescRequest)
p->state = EGetDescResponse;
else
p->state = ESoapResponse;
free(p->http_response);
p->http_response = NULL;
p->http_response_received = 0;
p->http_response_end_of_headers = 0;
/* get response */
}
}
return 0;
}
static int upnpc_parse_headers(upnpc_t * p)
{
/* search for CR LF CR LF (end of headers)
* recognize also LF LF */
int i = 0;
while(i < (p->http_response_received-1) &&
p->http_response_end_of_headers == 0) {
if(p->http_response[i] == '\r') {
i++;
if(p->http_response[i] == '\n') {
i++;
if(i < p->http_response_received && p->http_response[i] == '\r') {
i++;
if(i < p->http_response_received && p->http_response[i] == '\n') {
p->http_response_end_of_headers = i + 1;
}
}
}
} else if(p->http_response[i] == '\n') {
i++;
if(p->http_response[i] == '\n') {
p->http_response_end_of_headers = i + 1;
}
}
i++;
}
if(p->http_response_end_of_headers != 0) {
int colon = 0;
int linestart = 0;
int valuestart = 0;
p->http_response_code = -1;
for(i = 0; i < p->http_response_end_of_headers - 1; i++) {
if(linestart == 0) {
/* reading HTTP response code on the 1st line */
if(p->http_response[i] == ' ' && p->http_response_code < 0)
p->http_response_code = 0;
else if(p->http_response[i] >= '0' && p->http_response[i] <= '9') {
p->http_response_code = p->http_response_code * 10 + (p->http_response[i] - '0');
} else if(p->http_response[i] == ' ')
linestart = 1;
}
if(colon <= linestart && p->http_response[i] == ':') {
colon = i;
while(i < p->http_response_end_of_headers - 1 &&
(p->http_response[i+1] == ' ' || p->http_response[i+1] == '\t'))
i++;
valuestart = i + 1;
} else if(p->http_response[i + 1] == '\r' ||
p->http_response[i + 1] == '\n') {
if(colon > linestart && valuestart > colon) {
debug_printf("header='%.*s', value='%.*s'\n",
colon-linestart, p->http_response+linestart,
i+1-valuestart, p->http_response+valuestart);
if(0==strncasecmp(p->http_response+linestart, "content-length", colon-linestart)) {
p->http_response_content_length = atoi(p->http_response + valuestart);
debug_printf("Content-Length: %d\n", p->http_response_content_length);
if(p->http_response_content_length < 0) {
debug_printf("Content-Length overflow ? setting to 0\n");
p->http_response_content_length = 0;
}
} else if(0==strncasecmp(p->http_response+linestart, "transfer-encoding", colon-linestart)
&& 0==strncasecmp(p->http_response+valuestart, "chunked", 7)) {
debug_printf("Chunked transfer-encoding !\n");
p->http_response_chunked = 1;
}
}
/* find next line */
while((i < p->http_response_received) &&
(p->http_response[i]=='\r' || p->http_response[i] == '\n'))
i++;
linestart = i;
colon = linestart;
valuestart = 0;
}
}
}
return 0;
}
static char * build_url_string(const char * urlbase, const char * root_desc_url, const char * controlurl)
{
int l, n;
char * s;
const char * base;
char * p;
/* if controlurl is an absolute url, return it */
if(0 == memcmp("http://", controlurl, 7))
return strdup(controlurl);
base = (urlbase[0] == '\0') ? root_desc_url : urlbase;
n = strlen(base);
if(n > 7) {
p = strchr(base + 7, '/');
if(p)
n = p - base;
}
l = n + strlen(controlurl) + 1;
if(controlurl[0] != '/')
l++;
s = malloc(l);
if(s == NULL) return NULL;
memcpy(s, base, n);
if(controlurl[0] != '/')
s[n++] = '/';
memcpy(s + n, controlurl, l - n);
return s;
}
static int upnpc_get_response(upnpc_t * p)
{
ssize_t n;
ssize_t count;
char buffer[2048];
if(p->http_response_content_length > 0) {
count = p->http_response_content_length
+ p->http_response_end_of_headers
- p->http_response_received;
if(count > (ssize_t)sizeof(buffer)) count = sizeof(buffer);
} else {
count = sizeof(buffer);
}
debug_printf("recv(..., %d)\n", (int)count);
n = recv(p->http_socket, buffer, count, 0/* flags */);
if(n < 0) {
if(errno == EINTR || WOULDBLOCK(errno))
return 0; /* try again later */
PRINT_SOCKET_ERROR("read");
p->state = EError;
return -1;
} else if(n == 0) {
/* receiving finished */
debug_printf("%.*s\n", p->http_response_received, p->http_response);
close(p->http_socket);
p->http_socket = -1;
/* parse */
if(p->http_response_end_of_headers == 0) {
upnpc_parse_headers(p);
}
/* TODO : decode chunked transfer-encoding */
/* parse xml */
if(p->state == EGetDescResponse) {
struct IGDdatas igd;
struct xmlparser parser;
memset(&igd, 0, sizeof(struct IGDdatas));
memset(&parser, 0, sizeof(struct xmlparser));
parser.xmlstart = p->http_response + p->http_response_end_of_headers;
parser.xmlsize = p->http_response_received - p->http_response_end_of_headers;
parser.data = &igd;
parser.starteltfunc = IGDstartelt;
parser.endeltfunc = IGDendelt;
parser.datafunc = IGDdata;
parsexml(&parser);
#ifdef DEBUG
printIGD(&igd);
#endif /* DEBUG */
p->control_conn_url = build_url_string(igd.urlbase, p->root_desc_location, igd.first.controlurl);
p->control_cif_url = build_url_string(igd.urlbase, p->root_desc_location, igd.CIF.controlurl);
debug_printf("control_conn_url='%s'\n", p->control_conn_url);
debug_printf("control_cif_url='%s'\n", p->control_cif_url);
} else {
ClearNameValueList(&p->soap_response_data);
ParseNameValue(p->http_response + p->http_response_end_of_headers,
p->http_response_received - p->http_response_end_of_headers,
&p->soap_response_data);
}
free(p->http_response);
p->http_response = NULL;
p->http_response_received = 0;
p->http_response_end_of_headers = 0;
p->state = EReady;
} else {
/* receiving in progress */
debug_printf("received %d bytes:\n%.*s\n", (int)n, (int)n, buffer);
if(p->http_response == NULL) {
p->http_response = malloc(n);
if(p->http_response == NULL) {
debug_printf("failed to malloc %d bytes\n", (int)n);
p->state = EError;
return -1;
}
p->http_response_received = n;
memcpy(p->http_response, buffer, n);
} else {
char * tmp = realloc(p->http_response, p->http_response_received + n);
if(tmp == NULL) {
debug_printf("failed to realloc %d bytes\n", (int)(p->http_response_received + n));
p->state = EError;
return -1;
}
p->http_response = tmp;
memcpy(p->http_response + p->http_response_received, buffer, n);
p->http_response_received += n;
}
if(p->http_response_end_of_headers == 0) {
upnpc_parse_headers(p);
}
}
return 0;
}
#define SOAPPREFIX "s"
#define SERVICEPREFIX "u"
#define SERVICEPREFIX2 'u'
static int upnpc_build_soap_request(upnpc_t * p, const char * url,
const char * service,
const char * action,
const struct upnp_args * args, int arg_count)
{
char * body;
const char fmt_soap[] =
"<?xml version=\"1.0\"?>\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\">"
"%s"
"</" SERVICEPREFIX ":%s>"
"</" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>"
"\r\n";
int body_len;
const char fmt_http[] =
"POST %s HTTP/1.1\r\n"
"Host: %s%s\r\n"
"User-Agent: MiniUPnPc-async\r\n"
"Content-Length: %d\r\n"
"Content-Type: text/xml\r\n"
"SOAPAction: \"%s#%s\"\r\n"
"Connection: Close\r\n"
"Cache-Control: no-cache\r\n" /* ??? */
"Pragma: no-cache\r\n"
"\r\n"
"%s";
char hostname[MAXHOSTNAMELEN+1];
unsigned short port;
char * path;
unsigned int scope_id;
char portstr[8];
char * args_xml = NULL;
if(arg_count > 0) {
int i;
size_t l, n;
for(i = 0, l = 0; i < arg_count; i++) {
/* <ELT>VAL</ELT> */
l += strlen(args[i].elt) * 2 + strlen(args[i].val) + 5;
}
args_xml = malloc(++l);
if(args_xml == NULL) {
p->state = EError;
return -1;
}
for(i = 0, n = 0; i < arg_count && n < l; i++) {
/* <ELT>VAL</ELT> */
n += snprintf(args_xml + n, l - n, "<%s>%s</%s>",
args[i].elt, args[i].val, args[i].elt);
}
}
body_len = snprintf(NULL, 0, fmt_soap, action, service, args_xml?args_xml:"", action);
body = malloc(body_len + 1);
if(body == NULL) {
p->state = EError;
return -1;
}
if(snprintf(body, body_len + 1, fmt_soap, action, service, args_xml?args_xml:"", action) != body_len) {
debug_printf("snprintf() returned strange value...\n");
}
free(args_xml);
args_xml = NULL;
if(!parseURL(url, hostname, &port, &path, &scope_id)) {
p->state = EError;
return -1;
}
if(port != 80)
snprintf(portstr, sizeof(portstr), ":%hu", port);
else
portstr[0] = '\0';
p->http_request_len = snprintf(NULL, 0, fmt_http,
path/*url*/, hostname, portstr, body_len, service, action, body);
free(p->http_request);
p->http_request = malloc(p->http_request_len + 1);
if(snprintf(p->http_request, p->http_request_len + 1, fmt_http,
path/*url*/, hostname, portstr, body_len, service, action, body) != p->http_request_len) {
debug_printf("snprintf() returned strange value...\n");
}
free(body);
debug_printf("%s", p->http_request);
p->http_request_sent = 0;
return 0;
}
/* public functions */
int upnpc_init(upnpc_t * p, const char * multicastif)
{
int opt = 1;
struct sockaddr_in addr;
if(!p)
return UPNPC_ERR_INVALID_ARGS;
p->state = EError;
memset(p, 0, sizeof(upnpc_t)); /* clean everything */
/* open the socket for SSDP */
p->ssdp_socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(p->ssdp_socket < 0) {
return UPNPC_ERR_SOCKET_FAILED;
}
/* set REUSEADDR */
#ifdef WIN32
if(setsockopt(p->ssdp_socket, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof(opt)) < 0) {
#else
if(setsockopt(p->ssdp_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
#endif
/* non fatal error ! */
}
if(!set_non_blocking(p->ssdp_socket)) {
/* TODO log error */
}
/* receive address */
memset(&addr, 0, sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
/*addr.sin_port = htons(SSDP_PORT);*/
if(multicastif) {
struct in_addr mc_if;
mc_if.s_addr = inet_addr(multicastif);
addr.sin_addr.s_addr = mc_if.s_addr;
if(setsockopt(p->ssdp_socket, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0) {
PRINT_SOCKET_ERROR("setsockopt");
/* non fatal error ! */
}
}
/* bind the socket to the ssdp address in order to receive responses */
if(bind(p->ssdp_socket, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) != 0) {
close(p->ssdp_socket);
return UPNPC_ERR_BIND_FAILED;
}
p->state = EInit;
return UPNPC_OK;
}
int upnpc_finalize(upnpc_t * p)
{
if(!p) return UPNPC_ERR_INVALID_ARGS;
free(p->root_desc_location);
p->root_desc_location = NULL;
free(p->http_request);
p->http_request = NULL;
free(p->http_response);
p->http_response = NULL;
free(p->control_cif_url);
p->control_cif_url = NULL;
free(p->control_conn_url);
p->control_conn_url = NULL;
if(p->ssdp_socket >= 0) {
close(p->ssdp_socket);
p->ssdp_socket = -1;
}
if(p->http_socket >= 0) {
close(p->http_socket);
p->http_socket = -1;
}
ClearNameValueList(&p->soap_response_data);
p->state = EFinalized;
return UPNPC_OK;
}
int upnpc_get_external_ip_address(upnpc_t * p)
{
upnpc_build_soap_request(p, p->control_conn_url,
"urn:schemas-upnp-org:service:WANIPConnection:1",
"GetExternalIPAddress", NULL, 0);
p->state = ESoapConnect;
upnpc_connect(p, p->control_conn_url);
return 0;
}
int upnpc_get_link_layer_max_rate(upnpc_t * p)
{
upnpc_build_soap_request(p, p->control_cif_url,
"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
"GetCommonLinkProperties", NULL, 0);
p->state = ESoapConnect;
upnpc_connect(p, p->control_conn_url);
return 0;
}
int upnpc_add_port_mapping(upnpc_t * p,
const char * remote_host, unsigned short ext_port,
unsigned short int_port, const char * int_client,
const char * proto, const char * description,
unsigned int lease_duration)
{
struct upnp_args args[8];
char lease_duration_str[16];
char int_port_str[8];
char ext_port_str[8];
if(int_client == NULL || int_port == 0 || ext_port == 0 || proto == NULL)
return UPNPC_ERR_INVALID_ARGS;
snprintf(lease_duration_str, sizeof(lease_duration_str), "%u", lease_duration);
snprintf(int_port_str, sizeof(int_port_str), "%hu", int_port);
snprintf(ext_port_str, sizeof(ext_port_str), "%hu", ext_port);
args[0].elt = "NewRemoteHost";
args[0].val = remote_host?remote_host:"";
args[1].elt = "NewExternalPort";
args[1].val = ext_port_str;
args[2].elt = "NewProtocol";
args[2].val = proto;
args[3].elt = "NewInternalPort";
args[3].val = int_port_str;
args[4].elt = "NewInternalClient";
args[4].val = int_client;
args[5].elt = "NewEnabled";
args[5].val = "1";
args[6].elt = "NewPortMappingDescription";
args[6].val = description?description:"miniupnpc-async";
args[7].elt = "NewLeaseDuration";
args[7].val = lease_duration_str;
upnpc_build_soap_request(p, p->control_conn_url,
"urn:schemas-upnp-org:service:WANIPConnection:1",
"AddPortMapping",
args, 8);
p->state = ESoapConnect;
upnpc_connect(p, p->control_conn_url);
return 0;
}
#ifdef UPNPC_USE_SELECT
int upnpc_select_fds(upnpc_t * p, int * nfds, fd_set * readfds, fd_set * writefds)
{
int n = 0;
if(!p) return UPNPC_ERR_INVALID_ARGS;
switch(p->state) {
case ESendSSDP:
FD_SET(p->ssdp_socket, writefds);
if(*nfds < p->ssdp_socket)
*nfds = p->ssdp_socket;
n++;
break;
case EReceiveSSDP:
FD_SET(p->ssdp_socket, readfds);
if(*nfds < p->ssdp_socket)
*nfds = p->ssdp_socket;
n++;
break;
case EGetDescConnect:
case EGetDescRequest:
case ESoapConnect:
case ESoapRequest:
FD_SET(p->http_socket, writefds);
if(*nfds < p->http_socket)
*nfds = p->http_socket;
n++;
break;
case EGetDescResponse:
case ESoapResponse:
FD_SET(p->http_socket, readfds);
if(*nfds < p->http_socket)
*nfds = p->http_socket;
n++;
break;
default:
return 0;
}
return n;
}
#endif
static const char * devices_to_search[] = {
"urn:schemas-upnp-org:device:InternetGatewayDevice:1",
"urn:schemas-upnp-org:service:WANIPConnection:1",
"urn:schemas-upnp-org:service:WANPPPConnection:1",
"upnp:rootdevice",
0
};
int upnpc_process(upnpc_t * p)
{
/*
1) Envoyer les paquets de discovery SSDP
2) Recevoir et traiter les reponses
3) recup les descriptions
4) tester les etats
*/
if(!p) return UPNPC_ERR_INVALID_ARGS;
debug_printf("state=%d\n", (int)p->state);
switch(p->state) {
case EInit:
upnpc_send_ssdp_msearch(p, devices_to_search[0], 2);
break;
case ESendSSDP:
upnpc_send_ssdp_msearch(p, devices_to_search[0], 2);
break;
case EReceiveSSDP:
upnpc_receive_and_parse_ssdp(p);
break;
/*case EGetDesc:
upnpc_connect(p);
break;*/
case EGetDescConnect:
case ESoapConnect:
upnpc_complete_connect(p);
break;
case EGetDescRequest:
case ESoapRequest:
upnpc_send_request(p);
break;
case EGetDescResponse:
case ESoapResponse:
upnpc_get_response(p);
break;
default:
return UPNPC_ERR_UNKNOWN_STATE;
}
return UPNPC_OK;
}

View File

@ -0,0 +1,91 @@
/* $Id: miniupnpc-async.h,v 1.13 2014/11/07 11:25:52 nanard Exp $ */
/* miniupnpc-async
* Copyright (c) 2008-2014, Thomas BERNARD <miniupnp@free.fr>
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
*
* 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 MINIUPNPC_ASYNC_H_INCLUDED
#define MINIUPNPC_ASYNC_H_INCLUDED
#include "declspec.h"
#include "upnpreplyparse.h"
#ifdef __cplusplus
extern "C" {
#endif
#define UPNPC_OK 0
#define UPNPC_ERR_INVALID_ARGS (-1)
#define UPNPC_ERR_SOCKET_FAILED (-2)
#define UPNPC_ERR_BIND_FAILED (-3)
#define UPNPC_ERR_UNKNOWN_STATE (-4)
typedef struct {
enum {
EInit = 1,
ESendSSDP,
EReceiveSSDP,
/*EGetDesc,*/
EGetDescConnect,
EGetDescRequest,
EGetDescResponse,
EReady,
ESoapConnect,
ESoapRequest,
ESoapResponse,
EFinalized = 99,
EError = 1000
} state;
int ssdp_socket;
char * root_desc_location;
int http_socket;
char * http_request;
int http_request_len;
int http_request_sent;
char * http_response;
int http_response_received;
int http_response_end_of_headers;
int http_response_content_length;
int http_response_chunked;
int http_response_code;
char * control_cif_url;
char * control_conn_url;
struct NameValueParserData soap_response_data;
} upnpc_t;
int upnpc_init(upnpc_t * p, const char * multicastif);
int upnpc_finalize(upnpc_t * p);
int upnpc_get_external_ip_address(upnpc_t * p);
int upnpc_get_link_layer_max_rate(upnpc_t * p);
int upnpc_add_port_mapping(upnpc_t * p,
const char * remote_host, unsigned short ext_port,
unsigned short int_port, const char * int_client,
const char * proto, const char * description,
unsigned int lease_duration);
#ifdef UPNPC_USE_SELECT
int upnpc_select_fds(upnpc_t * p, int * nfds, fd_set * readfds, fd_set * writefds);
#endif /* UPNPC_USE_SELECT */
int upnpc_process(upnpc_t * p);
#ifdef __cplusplus
}
#endif
#endif /* MINIUPNPC_ASYNC_H_INCLUDED */

229
miniupnpc-async/minixml.c Normal file
View File

@ -0,0 +1,229 @@
/* $Id: minixml.c,v 1.11 2014/02/03 15:54:12 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-2014, 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 <string.h>
#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. */
static 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... */
static void parseelt(struct xmlparser * p)
{
int i;
const char * elementname;
while(p->xml < (p->xmlend - 1))
{
if((p->xml + 4) <= p->xmlend && (0 == memcmp(p->xml, "<!--", 4)))
{
p->xml += 3;
/* ignore comments */
do
{
p->xml++;
if ((p->xml + 3) >= p->xmlend)
return;
}
while(memcmp(p->xml, "-->", 3) != 0);
p->xml += 3;
}
else 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) )
{
i++; p->xml++;
if (p->xml >= p->xmlend)
return;
}
if(memcmp(p->xml, "<![CDATA[", 9) == 0)
{
/* CDATA handling */
p->xml += 9;
data = p->xml;
i = 0;
while(memcmp(p->xml, "]]>", 3) != 0)
{
i++; p->xml++;
if ((p->xml + 3) >= p->xmlend)
return;
}
if(i>0 && p->datafunc)
p->datafunc(p->data, data, i);
while(*p->xml!='<')
{
p->xml++;
if (p->xml >= p->xmlend)
return;
}
}
else
{
while(*p->xml!='<')
{
i++; p->xml++;
if ((p->xml + 1) >= p->xmlend)
return;
}
if(i>0 && p->datafunc && *(p->xml + 1) == '/')
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);
}

37
miniupnpc-async/minixml.h Normal file
View File

@ -0,0 +1,37 @@
/* $Id: minixml.h,v 1.7 2012/09/27 15:42:10 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_INCLUDED
#define MINIXML_H_INCLUDED
#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

View File

@ -0,0 +1,80 @@
/* $Id: parsessdpreply.c,v 1.2 2009/11/14 10:37:55 nanard Exp $ */
/* Project : miniupnp
* website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* Author : Thomas Bernard
* copyright (c) 2005-2009 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 <strings.h>
#include "parsessdpreply.h"
/* 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 */
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; j<b; j++)
{
putchar(reply[j]);
}
*/
}
break;
case '\x0a':
case '\x0d':
if(b!=0)
{
/*for(j=b+1; j<i; j++)
{
putchar(reply[j]);
}
putchar('\n');*/
do { b++; } while(reply[b]==' ');
if(0==strncasecmp(reply+a, "location", 8))
{
*location = reply+b;
*locationsize = i-b;
}
else if(0==strncasecmp(reply+a, "st", 2))
{
*st = reply+b;
*stsize = i-b;
}
b = 0;
}
a = i+1;
break;
default:
break;
}
i++;
}
}

View File

@ -0,0 +1,32 @@
/* $Id: parsessdpreply.h,v 1.1 2009/11/14 10:37:55 nanard Exp $ */
/* Project : miniupnp
* website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* Author : Thomas Bernard
* copyright (c) 2005-2009 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 __PARSESSDPREPLY_H__
#define __PARSESSDPREPLY_H__
/* 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 */
void
parseMSEARCHReply(const char * reply, int size,
const char * * location, int * locationsize,
const char * * st, int * stsize);
#endif

137
miniupnpc-async/testasync.c Normal file
View File

@ -0,0 +1,137 @@
/* $Id: testasync.c,v 1.14 2014/11/07 12:07:38 nanard Exp $ */
/* miniupnpc-async
* Copyright (c) 2008-2014, Thomas BERNARD <miniupnp@free.fr>
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
*
* 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 <stdio.h>
#include <string.h>
#include <sys/select.h>
/* compile with -DUPNPC_USE_SELECT to enable upnpc_select_fds() function */
#include "miniupnpc-async.h"
#include "upnpreplyparse.h"
enum methods {
EGetExternalIP,
EGetRates,
EAddPortMapping,
ENothing
};
int main(int argc, char * * argv)
{
int r, n;
upnpc_t upnp;
const char * multicastif = NULL;
enum methods next_method_to_call = EGetExternalIP;
enum methods last_method = ENothing;
if(argc>1)
multicastif = argv[1];
if((r = upnpc_init(&upnp, multicastif)) < 0) {
fprintf(stderr, "upnpc_init failed : %d", r);
return 1;
}
r = upnpc_process(&upnp);
printf("upnpc_process returned %d\n", r);
while((upnp.state != EReady) && (upnp.state != EError)) {
int nfds;
fd_set readfds;
fd_set writefds;
/*struct timeval timeout;*/
FD_ZERO(&readfds);
FD_ZERO(&writefds);
nfds = 0;
n = upnpc_select_fds(&upnp, &nfds, &readfds, &writefds);
if(n <= 0) {
printf("nothing to select()...\n");
break;
}
#if 0
timeout.tv_sec = 0;
timeout.tv_usec = 0;
#endif
#if DEBUG
printf("select(%d, ...);\n", nfds+1);
#endif /* DEBUG */
if(select(nfds+1, &readfds, &writefds, NULL, NULL/*&timeout*/) < 0) {
perror("select");
return 1;
}
r = upnpc_process(&upnp);
#if DEBUG
printf("upnpc_process returned %d\n", r);
#endif /* DEBUG */
if(r < 0)
break;
if(upnp.state == EReady) {
char * p;
printf("Process UPnP IGD Method results : HTTP %d\n", upnp.http_response_code);
if(upnp.http_response_code == 200) {
switch(last_method) {
case EGetExternalIP:
p = GetValueFromNameValueList(&upnp.soap_response_data, "NewExternalIPAddress");
printf("ExternalIPAddress = %s\n", p);
/* p = GetValueFromNameValueList(&pdata, "errorCode");*/
break;
case EGetRates:
p = GetValueFromNameValueList(&upnp.soap_response_data, "NewLayer1DownstreamMaxBitRate");
printf("DownStream MaxBitRate = %s\t", p);
p = GetValueFromNameValueList(&upnp.soap_response_data, "NewLayer1UpstreamMaxBitRate");
printf("UpStream MaxBitRate = %s\n", p);
break;
case EAddPortMapping:
printf("OK\n");
break;
case ENothing:
break;
}
} else {
printf("SOAP error :\n");
printf(" faultcode='%s'\n", GetValueFromNameValueList(&upnp.soap_response_data, "faultcode"));
printf(" faultstring='%s'\n", GetValueFromNameValueList(&upnp.soap_response_data, "faultstring"));
printf(" errorCode=%s\n", GetValueFromNameValueList(&upnp.soap_response_data, "errorCode"));
printf(" errorDescription='%s'\n", GetValueFromNameValueList(&upnp.soap_response_data, "errorDescription"));
}
if(next_method_to_call == ENothing)
break;
printf("Ready to call UPnP IGD methods\n");
last_method = next_method_to_call;
switch(next_method_to_call) {
case EGetExternalIP:
printf("GetExternalIPAddress\n");
upnpc_get_external_ip_address(&upnp);
next_method_to_call = EGetRates;
break;
case EGetRates:
printf("GetCommonLinkProperties\n");
upnpc_get_link_layer_max_rate(&upnp);
next_method_to_call = EAddPortMapping;
break;
case EAddPortMapping:
printf("AddPortMapping\n");
upnpc_add_port_mapping(&upnp,
NULL /* remote_host */, 40002 /* ext_port */,
42042 /* int_port */, "192.168.1.202" /* int_client */,
"TCP" /* proto */, "this is a test" /* description */,
0 /* lease duration */);
next_method_to_call = ENothing;
case ENothing:
break;
}
}
}
upnpc_finalize(&upnp);
return 0;
}

View File

@ -0,0 +1,184 @@
/* $Id: upnpreplyparse.c,v 1.18 2014/11/05 05:36:08 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2014 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "upnpreplyparse.h"
#include "minixml.h"
static void
NameValueParserStartElt(void * d, const char * name, int l)
{
struct NameValueParserData * data = (struct NameValueParserData *)d;
data->topelt = 1;
if(l>63)
l = 63;
memcpy(data->curelt, name, l);
data->curelt[l] = '\0';
data->cdata = NULL;
data->cdatalen = 0;
}
static void
NameValueParserEndElt(void * d, const char * name, int l)
{
struct NameValueParserData * data = (struct NameValueParserData *)d;
struct NameValue * nv;
(void)name;
(void)l;
if(!data->topelt)
return;
if(strcmp(data->curelt, "NewPortListing") != 0)
{
int l;
/* standard case. Limited to n chars strings */
l = data->cdatalen;
nv = malloc(sizeof(struct NameValue));
if(l>=(int)sizeof(nv->value))
l = sizeof(nv->value) - 1;
strncpy(nv->name, data->curelt, 64);
nv->name[63] = '\0';
if(data->cdata != NULL)
{
memcpy(nv->value, data->cdata, l);
nv->value[l] = '\0';
}
else
{
nv->value[0] = '\0';
}
nv->l_next = data->l_head; /* insert in list */
data->l_head = nv;
}
data->cdata = NULL;
data->cdatalen = 0;
data->topelt = 0;
}
static void
NameValueParserGetData(void * d, const char * datas, int l)
{
struct NameValueParserData * data = (struct NameValueParserData *)d;
if(strcmp(data->curelt, "NewPortListing") == 0)
{
/* specific case for NewPortListing which is a XML Document */
data->portListing = malloc(l + 1);
if(!data->portListing)
{
/* malloc error */
return;
}
memcpy(data->portListing, datas, l);
data->portListing[l] = '\0';
data->portListingLength = l;
}
else
{
/* standard case. */
data->cdata = datas;
data->cdatalen = l;
}
}
void
ParseNameValue(const char * buffer, int bufsize,
struct NameValueParserData * data)
{
struct xmlparser parser;
data->l_head = NULL;
data->portListing = NULL;
data->portListingLength = 0;
/* init xmlparser object */
parser.xmlstart = buffer;
parser.xmlsize = bufsize;
parser.data = data;
parser.starteltfunc = NameValueParserStartElt;
parser.endeltfunc = NameValueParserEndElt;
parser.datafunc = NameValueParserGetData;
parser.attfunc = 0;
parsexml(&parser);
}
void
ClearNameValueList(struct NameValueParserData * pdata)
{
struct NameValue * nv;
if(pdata->portListing)
{
free(pdata->portListing);
pdata->portListing = NULL;
pdata->portListingLength = 0;
}
while((nv = pdata->l_head) != NULL)
{
pdata->l_head = nv->l_next;
free(nv);
}
}
char *
GetValueFromNameValueList(struct NameValueParserData * pdata,
const char * Name)
{
struct NameValue * nv;
char * p = NULL;
for(nv = pdata->l_head;
(nv != NULL) && (p == NULL);
nv = nv->l_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.l_head;
nv != NULL;
nv = nv->l_next)
{
printf("%s = %s\n", nv->name, nv->value);
}
ClearNameValueList(&pdata);
}
#endif

View File

@ -0,0 +1,63 @@
/* $Id: upnpreplyparse.h,v 1.19 2014/10/27 16:33:19 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2013 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#ifndef UPNPREPLYPARSE_H_INCLUDED
#define UPNPREPLYPARSE_H_INCLUDED
#ifdef __cplusplus
extern "C" {
#endif
struct NameValue {
struct NameValue * l_next;
char name[64];
char value[128];
};
struct NameValueParserData {
struct NameValue * l_head;
char curelt[64];
char * portListing;
int portListingLength;
int topelt;
const char * cdata;
int cdatalen;
};
/* 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);
#if 0
/* GetValueFromNameValueListIgnoreNS() */
char *
GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata,
const char * Name);
#endif
/* DisplayNameValueList() */
#ifdef DEBUG
void
DisplayNameValueList(char * buffer, int bufsize);
#endif
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,87 @@
/* $Id: upnputils.c,v 1.1 2013/09/07 06:45:39 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2013 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#include "config.h"
#include <stdio.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#ifdef AF_LINK
#include <net/if_dl.h>
#endif
#include "upnputils.h"
int
sockaddr_to_string(const struct sockaddr * addr, char * str, size_t size)
{
char buffer[64];
unsigned short port = 0;
int n = -1;
switch(addr->sa_family)
{
case AF_INET6:
inet_ntop(addr->sa_family,
&((struct sockaddr_in6 *)addr)->sin6_addr,
buffer, sizeof(buffer));
port = ntohs(((struct sockaddr_in6 *)addr)->sin6_port);
n = snprintf(str, size, "[%s]:%hu", buffer, port);
break;
case AF_INET:
inet_ntop(addr->sa_family,
&((struct sockaddr_in *)addr)->sin_addr,
buffer, sizeof(buffer));
port = ntohs(((struct sockaddr_in *)addr)->sin_port);
n = snprintf(str, size, "%s:%hu", buffer, port);
break;
#ifdef AF_LINK
#if defined(__sun)
/* solaris does not seem to have link_ntoa */
/* #define link_ntoa _link_ntoa */
#define link_ntoa(x) "dummy-link_ntoa"
#endif
case AF_LINK:
{
struct sockaddr_dl * sdl = (struct sockaddr_dl *)addr;
n = snprintf(str, size, "index=%hu type=%d %s",
sdl->sdl_index, sdl->sdl_type,
link_ntoa(sdl));
}
break;
#endif
default:
n = snprintf(str, size, "unknown address family %d", addr->sa_family);
#if 0
n = snprintf(str, size, "unknown address family %d "
"%02x %02x %02x %02x %02x %02x %02x %02x",
addr->sa_family,
addr->sa_data[0], addr->sa_data[1], (unsigned)addr->sa_data[2], addr->sa_data[3],
addr->sa_data[4], addr->sa_data[5], (unsigned)addr->sa_data[6], addr->sa_data[7]);
#endif
}
return n;
}
int
set_non_blocking(int fd)
{
int flags = fcntl(fd, F_GETFL);
if(flags < 0)
return 0;
if(fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0)
return 0;
return 1;
}

View File

@ -0,0 +1,27 @@
/* $Id: upnputils.h,v 1.1 2013/09/07 06:45:39 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2011-2013 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#ifndef UPNPUTILS_H_INCLUDED
#define UPNPUTILS_H_INCLUDED
/**
* convert a struct sockaddr to a human readable string.
* [ipv6]:port or ipv4:port
* return the number of characters used (as snprintf)
*/
int
sockaddr_to_string(const struct sockaddr * addr, char * str, size_t size);
/**
* set the file description as non blocking
* return 0 in case of failure, 1 in case of success
*/
int
set_non_blocking(int fd);
#endif

3
miniupnpc-libevent/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.o
*.a
upnpc-libevent

View File

@ -0,0 +1,50 @@
# $Id: Makefile,v 1.2 2014/11/08 17:12:15 nanard Exp $
CFLAGS = -O0 -g -DDEBUG
# libevent debug
CFLAGS += -DUSE_DEBUG
CFLAGS += -fPIC
CFLAGS += -ansi
CFLAGS += -Wall -W
CFLAGS += -D_BSD_SOURCE
CFLAGS += -D_POSIX_C_SOURCE=1
LDFLAGS = -levent
LIB = libminiupnpc-ev.a
LIB_SRCS = miniupnpc-libevent.c minixml.c igd_desc_parse.c upnpreplyparse.c
SRCS = $(LIB_SRCS) upnpc-libevent.c
LIB_OBJS = $(patsubst %.c,%.o,$(LIB_SRCS))
OBJS = $(patsubst %.c,%.o,$(SRCS))
EXECUTABLE = upnpc-libevent
.PHONY: all clean depend
all: $(EXECUTABLE)
clean:
$(RM) $(OBJS)
$(RM) $(EXECUTABLE)
upnpc-libevent: upnpc-libevent.o $(LIB)
$(LIB): $(LIB_OBJS)
$(AR) crs $@ $?
depend:
makedepend -Y -- $(CFLAGS) -- $(SRCS) 2>/dev/null
# DO NOT DELETE THIS LINE -- make depend depends on it.
miniupnpc-libevent.o: miniupnpc-libevent.h declspec.h upnpreplyparse.h
miniupnpc-libevent.o: parsessdpreply.h minixml.h igd_desc_parse.h
minixml.o: minixml.h
igd_desc_parse.o: igd_desc_parse.h
upnpreplyparse.o: upnpreplyparse.h minixml.h
upnpc-libevent.o: miniupnpc-libevent.h declspec.h upnpreplyparse.h

View File

@ -0,0 +1,7 @@
miniupnpc-libevent :
UPnP IGD control point (ie client) using libevent
http://libevent.org
https://github.com/libevent/libevent

View File

@ -0,0 +1,15 @@
#ifndef DECLSPEC_H_DEFINED
#define DECLSPEC_H_DEFINED
#if defined(WIN32) && !defined(STATICLIB)
#ifdef MINIUPNP_EXPORTS
#define LIBSPEC __declspec(dllexport)
#else
#define LIBSPEC __declspec(dllimport)
#endif
#else
#define LIBSPEC
#endif
#endif /* DECLSPEC_H_DEFINED */

View File

@ -0,0 +1,120 @@
/* $Id: igd_desc_parse.c,v 1.15 2014/07/01 13:01:17 nanard Exp $ */
/* Project : miniupnp
* http://miniupnp.free.fr/
* Author : Thomas Bernard
* Copyright (c) 2005-2014 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution. */
#include "igd_desc_parse.h"
#include <stdio.h>
#include <string.h>
/* 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->tmp.controlurl[0] = '\0';
datas->tmp.eventsuburl[0] = '\0';
datas->tmp.scpdurl[0] = '\0';
datas->tmp.servicetype[0] = '\0';
}
}
#define COMPARE(str, cstr) (0==memcmp(str, cstr, sizeof(cstr) - 1))
/* 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(COMPARE(datas->tmp.servicetype,
"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:")) {
memcpy(&datas->CIF, &datas->tmp, sizeof(struct IGDdatas_service));
} else if(COMPARE(datas->tmp.servicetype,
"urn:schemas-upnp-org:service:WANIPv6FirewallControl:")) {
memcpy(&datas->IPv6FC, &datas->tmp, sizeof(struct IGDdatas_service));
} else if(COMPARE(datas->tmp.servicetype,
"urn:schemas-upnp-org:service:WANIPConnection:")
|| COMPARE(datas->tmp.servicetype,
"urn:schemas-upnp-org:service:WANPPPConnection:") ) {
if(datas->first.servicetype[0] == '\0') {
memcpy(&datas->first, &datas->tmp, sizeof(struct IGDdatas_service));
} else {
memcpy(&datas->second, &datas->tmp, sizeof(struct IGDdatas_service));
}
}
}
}
/* 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, "presentationURL") )
dstmember = datas->presentationurl;
else if( !strcmp(datas->cureltname, "serviceType") )
dstmember = datas->tmp.servicetype;
else if( !strcmp(datas->cureltname, "controlURL") )
dstmember = datas->tmp.controlurl;
else if( !strcmp(datas->cureltname, "eventSubURL") )
dstmember = datas->tmp.eventsuburl;
else if( !strcmp(datas->cureltname, "SCPDURL") )
dstmember = datas->tmp.scpdurl;
/* 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->CIF.devicetype);*/
printf(" serviceType = '%s'\n", d->CIF.servicetype);
printf(" controlURL = '%s'\n", d->CIF.controlurl);
printf(" eventSubURL = '%s'\n", d->CIF.eventsuburl);
printf(" SCPDURL = '%s'\n", d->CIF.scpdurl);
printf("primary WAN Connection Device (IP or PPP Connection):\n");
/*printf(" deviceType = '%s'\n", d->first.devicetype);*/
printf(" servicetype = '%s'\n", d->first.servicetype);
printf(" controlURL = '%s'\n", d->first.controlurl);
printf(" eventSubURL = '%s'\n", d->first.eventsuburl);
printf(" SCPDURL = '%s'\n", d->first.scpdurl);
printf("secondary WAN Connection Device (IP or PPP Connection):\n");
/*printf(" deviceType = '%s'\n", d->second.devicetype);*/
printf(" servicetype = '%s'\n", d->second.servicetype);
printf(" controlURL = '%s'\n", d->second.controlurl);
printf(" eventSubURL = '%s'\n", d->second.eventsuburl);
printf(" SCPDURL = '%s'\n", d->second.scpdurl);
printf("WAN IPv6 Firewall Control :\n");
/*printf(" deviceType = '%s'\n", d->IPv6FC.devicetype);*/
printf(" servicetype = '%s'\n", d->IPv6FC.servicetype);
printf(" controlURL = '%s'\n", d->IPv6FC.controlurl);
printf(" eventSubURL = '%s'\n", d->IPv6FC.eventsuburl);
printf(" SCPDURL = '%s'\n", d->IPv6FC.scpdurl);
}

View File

@ -0,0 +1,48 @@
/* $Id: igd_desc_parse.h,v 1.11 2012/10/16 16:49:02 nanard Exp $ */
/* Project : miniupnp
* http://miniupnp.free.fr/
* Author : Thomas Bernard
* Copyright (c) 2005-2010 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution.
* */
#ifndef IGD_DESC_PARSE_H_INCLUDED
#define IGD_DESC_PARSE_H_INCLUDED
/* Structure to store the result of the parsing of UPnP
* descriptions of Internet Gateway Devices */
#define MINIUPNPC_URL_MAXSIZE (128)
struct IGDdatas_service {
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];*/
};
struct IGDdatas {
char cureltname[MINIUPNPC_URL_MAXSIZE];
char urlbase[MINIUPNPC_URL_MAXSIZE];
char presentationurl[MINIUPNPC_URL_MAXSIZE];
int level;
/*int state;*/
/* "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" */
struct IGDdatas_service CIF;
/* "urn:schemas-upnp-org:service:WANIPConnection:1"
* "urn:schemas-upnp-org:service:WANPPPConnection:1" */
struct IGDdatas_service first;
/* if both WANIPConnection and WANPPPConnection are present */
struct IGDdatas_service second;
/* "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1" */
struct IGDdatas_service IPv6FC;
/* tmp */
struct IGDdatas_service tmp;
};
void IGDstartelt(void *, const char *, int);
void IGDendelt(void *, const char *, int);
void IGDdata(void *, const char *, int);
void printIGD(struct IGDdatas *);
#endif

View File

@ -0,0 +1,672 @@
/* $Id: miniupnpc-libevent.c,v 1.8 2014/11/13 09:15:23 nanard Exp $ */
/* miniupnpc-libevent
* Copyright (c) 2008-2014, Thomas BERNARD <miniupnp@free.fr>
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
*
* 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 <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <stdio.h>
#include <event2/event.h>
#include <event2/buffer.h>
/*#include <event2/bufferevent.h>*/
#include <event2/http.h>
#ifdef WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#include <io.h>
#define PRINT_SOCKET_ERROR printf
#define SOCKET_ERROR GetWSALastError()
#define WOULDBLOCK(err) (err == WSAEWOULDBLOCK)
#else
#include <unistd.h>
#include <errno.h>
#define closesocket close
#define PRINT_SOCKET_ERROR perror
#define SOCKET_ERROR errno
#define WOULDBLOCK(err) (err == EAGAIN || err == EWOULDBLOCK)
#endif
#include "miniupnpc-libevent.h"
#include "parsessdpreply.h"
#include "minixml.h"
#include "igd_desc_parse.h"
#include "upnpreplyparse.h"
#ifndef MIN
#define MIN(x,y) (((x)<(y))?(x):(y))
#endif /* MIN */
#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 64
#endif /* MAXHOSTNAMELEN */
#define SSDP_PORT 1900
#define SSDP_MCAST_ADDR "239.255.255.250"
#define XSTR(s) STR(s)
#define STR(s) #s
#ifdef DEBUG
#define debug_printf(...) fprintf(stderr, __VA_ARGS__)
#else
#define debug_printf(...)
#endif
/* stuctures */
struct upnp_args {
const char * elt;
const char * val;
};
/* private functions */
static int upnpc_get_desc(upnpc_t * p, const char * url);
static char * build_url_string(const char * urlbase, const char * root_desc_url, const char * controlurl);
/* data */
static const char * devices_to_search[] = {
"urn:schemas-upnp-org:device:InternetGatewayDevice:1",
"urn:schemas-upnp-org:service:WANIPConnection:1",
"urn:schemas-upnp-org:service:WANPPPConnection:1",
"upnp:rootdevice",
0
};
static void upnpc_conn_close_cb(struct evhttp_connection * conn, void * data)
{
upnpc_t * p = (upnpc_t *)data;
debug_printf("upnpc_get_desc_conn_close_cb %p %p\n", conn, p);
}
/* parse_msearch_reply()
* 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
parse_msearch_reply(const char * reply, int size,
const char * * location, int * locationsize,
const char * * st, int * stsize)
{
int a, b, i;
i = 0; /* current character index */
a = i; /* start of the line */
b = 0; /* end of the "header" (position of the colon) */
while(i<size) {
switch(reply[i]) {
case ':':
if(b==0) {
b = i; /* end of the "header" */
}
break;
case '\x0a':
case '\x0d':
if(b!=0) {
/* skip the colon and white spaces */
do { b++; } while(reply[b]==' ' && b<size);
if(0==strncasecmp(reply+a, "location", 8)) {
*location = reply+b;
*locationsize = i-b;
} else if(0==strncasecmp(reply+a, "st", 2)) {
*st = reply+b;
*stsize = i-b;
}
b = 0;
}
a = i+1;
break;
default:
break;
}
i++;
}
}
static void upnpc_send_ssdp_msearch(evutil_socket_t s, short events, upnpc_t * p)
{
/* envoyer les packets de M-SEARCH discovery sur le socket ssdp */
int n;
char bufr[1024];
struct sockaddr_in addr;
unsigned int mx = 2;
static const char MSearchMsgFmt[] =
"M-SEARCH * HTTP/1.1\r\n"
"HOST: " SSDP_MCAST_ADDR ":" XSTR(SSDP_PORT) "\r\n"
"ST: %s\r\n"
"MAN: \"ssdp:discover\"\r\n"
"MX: %u\r\n"
"\r\n";
(void)p;
(void)events;
memset(&addr, 0, sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_port = htons(SSDP_PORT);
addr.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
n = snprintf(bufr, sizeof(bufr),
MSearchMsgFmt, devices_to_search[0], mx);
debug_printf("upnpc_send_ssdp_msearch: %s", bufr);
n = sendto(s, bufr, n, 0,
(struct sockaddr *)&addr, sizeof(struct sockaddr_in));
if (n < 0) {
PRINT_SOCKET_ERROR("sendto");
}
}
static int upnpc_set_root_desc_location(upnpc_t * p, const char * location, int locationsize)
{
char * tmp;
tmp = realloc(p->root_desc_location, locationsize + 1);
if(tmp == 0) {
return -1;
}
memcpy(tmp, location, locationsize);
tmp[locationsize] = '\0';
p->root_desc_location = tmp;
return 0;
}
static void upnpc_receive_and_parse_ssdp(evutil_socket_t s, short events, upnpc_t * p)
{
char bufr[2048];
ssize_t len;
if(events == EV_TIMEOUT) {
/* nothing received ... */
debug_printf("upnpc_receive_and_parse_ssdp() TIMEOUT\n");
return;
}
len = recv(s, bufr, sizeof(bufr), 0);
debug_printf("input %d bytes\n", (int)len);
if(len < 0) {
PRINT_SOCKET_ERROR("recv");
} else if(len == 0) {
debug_printf("SSDP socket closed ?\n");
} else {
const char * location = NULL;
int locationsize;
const char * st = NULL;
int stsize;
debug_printf("%.*s", (int)len, bufr);
parse_msearch_reply(bufr, len, &location, &locationsize, &st, &stsize);
debug_printf("location = '%.*s'\n", locationsize, location);
debug_printf("st = '%.*s'\n", stsize, st);
if(location != NULL) {
if(upnpc_set_root_desc_location(p, location, locationsize) < 0) {
return;
}
upnpc_get_desc(p, p->root_desc_location);
} else {
/* or do nothing ? */
}
}
}
static int
parseURL(const char * url,
char * hostname, unsigned short * port,
char * * path, unsigned int * scope_id)
{
char * p1, *p2, *p3;
if(!url)
return 0;
p1 = strstr(url, "://");
if(!p1)
return 0;
p1 += 3;
if( (url[0]!='h') || (url[1]!='t')
||(url[2]!='t') || (url[3]!='p'))
return 0;
memset(hostname, 0, MAXHOSTNAMELEN + 1);
if(*p1 == '[') {
/* IP v6 : http://[2a00:1450:8002::6a]/path/abc */
char * scope;
scope = strchr(p1, '%');
p2 = strchr(p1, ']');
if(p2 && scope && scope < p2 && scope_id) {
/* parse scope */
#ifdef IF_NAMESIZE
char tmp[IF_NAMESIZE];
int l;
scope++;
/* "%25" is just '%' in URL encoding */
if(scope[0] == '2' && scope[1] == '5')
scope += 2; /* skip "25" */
l = p2 - scope;
if(l >= IF_NAMESIZE)
l = IF_NAMESIZE - 1;
memcpy(tmp, scope, l);
tmp[l] = '\0';
*scope_id = if_nametoindex(tmp);
if(*scope_id == 0) {
*scope_id = (unsigned int)strtoul(tmp, NULL, 10);
}
#else /* IF_NAMESIZE */
/* under windows, scope is numerical */
char tmp[8];
int l;
scope++;
/* "%25" is just '%' in URL encoding */
if(scope[0] == '2' && scope[1] == '5')
scope += 2; /* skip "25" */
l = p2 - scope;
if(l >= (int)sizeof(tmp))
l = sizeof(tmp) - 1;
memcpy(tmp, scope, l);
tmp[l] = '\0';
*scope_id = (unsigned int)strtoul(tmp, NULL, 10);
#endif /* IF_NAMESIZE */
}
p3 = strchr(p1, '/');
if(p2 && p3) {
p2++;
strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1)));
if(*p2 == ':') {
*port = 0;
p2++;
while( (*p2 >= '0') && (*p2 <= '9')) {
*port *= 10;
*port += (unsigned short)(*p2 - '0');
p2++;
}
} else {
*port = 80;
}
*path = p3;
return 1;
}
}
p2 = strchr(p1, ':');
p3 = strchr(p1, '/');
if(!p3)
return 0;
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;
}
static void upnpc_desc_received(struct evhttp_request * req, void * pvoid)
{
size_t len;
unsigned char * data;
struct evbuffer * input_buffer;
struct IGDdatas igd;
struct xmlparser parser;
upnpc_t * p = (upnpc_t *)pvoid;
input_buffer = evhttp_request_get_input_buffer(req);
len = evbuffer_get_length(input_buffer);
data = evbuffer_pullup(input_buffer, len);
debug_printf("upnpc_desc_received %d (%d bytes)\n", evhttp_request_get_response_code(req), (int)len);
debug_printf("%.*s\n", (int)len, (char *)data);
if(data == NULL)
return;
memset(&igd, 0, sizeof(struct IGDdatas));
memset(&parser, 0, sizeof(struct xmlparser));
parser.xmlstart = (char *)data;
parser.xmlsize = len;
parser.data = &igd;
parser.starteltfunc = IGDstartelt;
parser.endeltfunc = IGDendelt;
parser.datafunc = IGDdata;
parsexml(&parser);
#ifdef DEBUG
printIGD(&igd);
#endif /* DEBUG */
p->control_conn_url = build_url_string(igd.urlbase, p->root_desc_location, igd.first.controlurl);
p->control_cif_url = build_url_string(igd.urlbase, p->root_desc_location, igd.CIF.controlurl);
debug_printf("control_conn_url='%s'\n", p->control_conn_url);
debug_printf("control_cif_url='%s'\n", p->control_cif_url);
p->ready_cb(evhttp_request_get_response_code(req), p->cb_data);
}
static void upnpc_soap_response(struct evhttp_request * req, void * pvoid)
{
size_t len;
unsigned char * data;
struct evbuffer * input_buffer;
upnpc_t * p = (upnpc_t *)pvoid;
input_buffer = evhttp_request_get_input_buffer(req);
len = evbuffer_get_length(input_buffer);
data = evbuffer_pullup(input_buffer, len);
debug_printf("upnpc_soap_response %d (%d bytes)\n", evhttp_request_get_response_code(req), (int)len);
debug_printf("%.*s\n", (int)len, (char *)data);
if(data == NULL)
return;
ClearNameValueList(&p->soap_response_data);
ParseNameValue((char *)data, (int)len,
&p->soap_response_data);
p->soap_cb(evhttp_request_get_response_code(req), p->cb_data);
}
static int upnpc_get_desc(upnpc_t * p, const char * url)
{
char hostname[MAXHOSTNAMELEN+1];
unsigned short port;
char * path;
unsigned int scope_id;
struct evhttp_request * req;
struct evkeyvalq * headers;
if(p->root_desc_location == 0) {
return -1;
}
if(!parseURL(url/*p->root_desc_location*/, hostname, &port,
&path, &scope_id)) {
return -1;
}
if(p->desc_conn == NULL) {
p->desc_conn = evhttp_connection_base_new(p->base, NULL, hostname, port);
}
evhttp_connection_set_closecb(p->desc_conn, upnpc_conn_close_cb, p);
/*evhttp_connection_set_timeout(p->desc_conn, 600);*/
req = evhttp_request_new(upnpc_desc_received/*callback*/, p);
headers = evhttp_request_get_output_headers(req);
evhttp_add_header(headers, "Host", hostname);
evhttp_add_header(headers, "Connection", "close");
/*evhttp_add_header(headers, "User-Agent", "***");*/
evhttp_make_request(p->desc_conn, req, EVHTTP_REQ_GET, path);
return 0;
}
static char * build_url_string(const char * urlbase, const char * root_desc_url, const char * controlurl)
{
int l, n;
char * s;
const char * base;
char * p;
/* if controlurl is an absolute url, return it */
if(0 == memcmp("http://", controlurl, 7))
return strdup(controlurl);
base = (urlbase[0] == '\0') ? root_desc_url : urlbase;
n = strlen(base);
if(n > 7) {
p = strchr(base + 7, '/');
if(p)
n = p - base;
}
l = n + strlen(controlurl) + 1;
if(controlurl[0] != '/')
l++;
s = malloc(l);
if(s == NULL) return NULL;
memcpy(s, base, n);
if(controlurl[0] != '/')
s[n++] = '/';
memcpy(s + n, controlurl, l - n);
return s;
}
#define SOAPPREFIX "s"
#define SERVICEPREFIX "u"
#define SERVICEPREFIX2 'u'
static int upnpc_send_soap_request(upnpc_t * p, const char * url,
const char * service,
const char * method,
const struct upnp_args * args, int arg_count)
{
char action[128];
char * body;
const char fmt_soap[] =
"<?xml version=\"1.0\"?>\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\">"
"%s"
"</" SERVICEPREFIX ":%s>"
"</" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>"
"\r\n";
int body_len;
char hostname[MAXHOSTNAMELEN+1];
unsigned short port;
char * path;
unsigned int scope_id;
char portstr[8];
char * args_xml = NULL;
struct evhttp_request * req;
struct evkeyvalq * headers;
struct evbuffer * buffer;
if(arg_count > 0) {
int i;
size_t l, n;
for(i = 0, l = 0; i < arg_count; i++) {
/* <ELT>VAL</ELT> */
l += strlen(args[i].elt) * 2 + strlen(args[i].val) + 5;
}
args_xml = malloc(++l);
if(args_xml == NULL) {
return -1;
}
for(i = 0, n = 0; i < arg_count && n < l; i++) {
/* <ELT>VAL</ELT> */
n += snprintf(args_xml + n, l - n, "<%s>%s</%s>",
args[i].elt, args[i].val, args[i].elt);
}
}
body_len = snprintf(NULL, 0, fmt_soap, method, service, args_xml?args_xml:"", method);
body = malloc(body_len + 1);
if(body == NULL) {
return -1;
}
if(snprintf(body, body_len + 1, fmt_soap, method, service, args_xml?args_xml:"", method) != body_len) {
debug_printf("snprintf() returned strange value...\n");
}
free(args_xml);
args_xml = NULL;
if(!parseURL(url, hostname, &port, &path, &scope_id)) {
return -1;
}
if(port != 80)
snprintf(portstr, sizeof(portstr), ":%hu", port);
else
portstr[0] = '\0';
snprintf(action, sizeof(action), "%s#%s", service, method);
if(p->soap_conn == NULL) {
p->soap_conn = evhttp_connection_base_new(p->base, NULL, hostname, port);
}
req = evhttp_request_new(upnpc_soap_response, p);
headers = evhttp_request_get_output_headers(req);
buffer = evhttp_request_get_output_buffer(req);
evhttp_add_header(headers, "Host", hostname);
evhttp_add_header(headers, "SOAPAction", action);
evhttp_add_header(headers, "Content-Type", "text/xml");
/*evhttp_add_header(headers, "User-Agent", "***");*/
/*evhttp_add_header(headers, "Cache-Control", "no-cache");*/
/*evhttp_add_header(headers, "Pragma", "no-cache");*/
evbuffer_add(buffer, body, body_len);
evhttp_make_request(p->soap_conn, req, EVHTTP_REQ_POST, path);
free(body);
return 0;
}
/* public functions */
int upnpc_init(upnpc_t * p, struct event_base * base, const char * multicastif,
upnpc_callback_fn ready_cb, upnpc_callback_fn soap_cb, void * cb_data)
{
int opt = 1;
struct sockaddr_in addr;
struct timeval timeout;
if(p == NULL || base == NULL)
return UPNPC_ERR_INVALID_ARGS;
memset(p, 0, sizeof(upnpc_t)); /* clean everything */
p->base = base;
p->ready_cb = ready_cb;
p->soap_cb = soap_cb;
p->cb_data = cb_data;
/* open the socket for SSDP */
p->ssdp_socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(p->ssdp_socket < 0) {
return UPNPC_ERR_SOCKET_FAILED;
}
/* set REUSEADDR */
#ifdef WIN32
if(setsockopt(p->ssdp_socket, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof(opt)) < 0) {
#else /* WIN32 */
if(setsockopt(p->ssdp_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
#endif /* WIN32 */
/* non fatal error ! */
}
if(evutil_make_socket_nonblocking(p->ssdp_socket) < 0) {
debug_printf("evutil_make_socket_nonblocking FAILED\n");
}
/* receive address */
memset(&addr, 0, sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
/*addr.sin_port = htons(SSDP_PORT);*/
if(multicastif) {
struct in_addr mc_if;
mc_if.s_addr = inet_addr(multicastif);
addr.sin_addr.s_addr = mc_if.s_addr;
if(setsockopt(p->ssdp_socket, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0) {
PRINT_SOCKET_ERROR("setsockopt");
/* non fatal error ! */
}
}
/* bind the socket to the ssdp address in order to receive responses */
if(bind(p->ssdp_socket, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) != 0) {
close(p->ssdp_socket);
return UPNPC_ERR_BIND_FAILED;
}
/* event on SSDP */
p->ev_ssdp_recv = event_new(p->base, p->ssdp_socket,
EV_READ|EV_PERSIST,
(event_callback_fn)upnpc_receive_and_parse_ssdp, p);
timeout.tv_sec = 3;
timeout.tv_usec = 0;
if(event_add(p->ev_ssdp_recv, &timeout)) {
debug_printf("event_add FAILED\n");
}
p->ev_ssdp_writable = event_new(p->base, p->ssdp_socket,
EV_WRITE,
(event_callback_fn)upnpc_send_ssdp_msearch, p);
if(event_add(p->ev_ssdp_writable, NULL)) {
debug_printf("event_add FAILED\n");
}
return UPNPC_OK;
}
int upnpc_finalize(upnpc_t * p)
{
if(!p) return UPNPC_ERR_INVALID_ARGS;
free(p->root_desc_location);
p->root_desc_location = NULL;
free(p->control_cif_url);
p->control_cif_url = NULL;
free(p->control_conn_url);
p->control_conn_url = NULL;
if(p->ssdp_socket >= 0) {
close(p->ssdp_socket);
p->ssdp_socket = -1;
}
if(p->ev_ssdp_recv) {
event_free(p->ev_ssdp_recv);
p->ev_ssdp_recv = NULL;
}
if(p->ev_ssdp_writable) {
event_free(p->ev_ssdp_writable);
p->ev_ssdp_writable = NULL;
}
if(p->desc_conn) {
evhttp_connection_free(p->desc_conn);
p->desc_conn = NULL;
}
if(p->soap_conn) {
evhttp_connection_free(p->soap_conn);
p->soap_conn = NULL;
}
ClearNameValueList(&p->soap_response_data);
return UPNPC_OK;
}
int upnpc_get_external_ip_address(upnpc_t * p)
{
return upnpc_send_soap_request(p, p->control_conn_url,
"urn:schemas-upnp-org:service:WANIPConnection:1",
"GetExternalIPAddress", NULL, 0);
}
int upnpc_get_link_layer_max_rate(upnpc_t * p)
{
return upnpc_send_soap_request(p, p->control_cif_url,
"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
"GetCommonLinkProperties", NULL, 0);
}
int upnpc_add_port_mapping(upnpc_t * p,
const char * remote_host, unsigned short ext_port,
unsigned short int_port, const char * int_client,
const char * proto, const char * description,
unsigned int lease_duration)
{
struct upnp_args args[8];
char lease_duration_str[16];
char int_port_str[8];
char ext_port_str[8];
if(int_client == NULL || int_port == 0 || ext_port == 0 || proto == NULL)
return UPNPC_ERR_INVALID_ARGS;
snprintf(lease_duration_str, sizeof(lease_duration_str), "%u", lease_duration);
snprintf(int_port_str, sizeof(int_port_str), "%hu", int_port);
snprintf(ext_port_str, sizeof(ext_port_str), "%hu", ext_port);
args[0].elt = "NewRemoteHost";
args[0].val = remote_host?remote_host:"";
args[1].elt = "NewExternalPort";
args[1].val = ext_port_str;
args[2].elt = "NewProtocol";
args[2].val = proto;
args[3].elt = "NewInternalPort";
args[3].val = int_port_str;
args[4].elt = "NewInternalClient";
args[4].val = int_client;
args[5].elt = "NewEnabled";
args[5].val = "1";
args[6].elt = "NewPortMappingDescription";
args[6].val = description?description:"miniupnpc-libevent";
args[7].elt = "NewLeaseDuration";
args[7].val = lease_duration_str;
return upnpc_send_soap_request(p, p->control_conn_url,
"urn:schemas-upnp-org:service:WANIPConnection:1",
"AddPortMapping",
args, 8);
}

View File

@ -0,0 +1,79 @@
/* $Id: miniupnpc-libevent.h,v 1.3 2014/11/12 14:10:52 nanard Exp $ */
/* miniupnpc-libevent
* Copyright (c) 2008-2014, Thomas BERNARD <miniupnp@free.fr>
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
*
* 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 MINIUPNPC_LIBEVENT_H_INCLUDED
#define MINIUPNPC_LIBEVENT_H_INCLUDED
#include <event2/event.h>
#include "declspec.h"
#include "upnpreplyparse.h"
#ifdef __cplusplus
extern "C" {
#endif
#define UPNPC_OK 0
#define UPNPC_ERR_INVALID_ARGS (-1)
#define UPNPC_ERR_SOCKET_FAILED (-2)
#define UPNPC_ERR_BIND_FAILED (-3)
#define UPNPC_ERR_UNKNOWN_STATE (-4)
typedef void(* upnpc_callback_fn)(int, void *);
typedef struct {
struct event_base * base;
evutil_socket_t ssdp_socket;
struct event * ev_ssdp_recv;
struct event * ev_ssdp_writable;
char * root_desc_location;
struct evhttp_connection * desc_conn;
char * control_cif_url;
char * control_conn_url;
struct evhttp_connection * soap_conn;
struct NameValueParserData soap_response_data;
upnpc_callback_fn ready_cb;
upnpc_callback_fn soap_cb;
void * cb_data;
} upnpc_t;
int upnpc_init(upnpc_t * p, struct event_base * base, const char * multicastif,
upnpc_callback_fn ready_cb, upnpc_callback_fn soap_cb, void * cb_data);
int upnpc_finalize(upnpc_t * p);
int upnpc_get_external_ip_address(upnpc_t * p);
int upnpc_get_link_layer_max_rate(upnpc_t * p);
int upnpc_add_port_mapping(upnpc_t * p,
const char * remote_host, unsigned short ext_port,
unsigned short int_port, const char * int_client,
const char * proto, const char * description,
unsigned int lease_duration);
#ifdef UPNPC_USE_SELECT
int upnpc_select_fds(upnpc_t * p, int * nfds, fd_set * readfds, fd_set * writefds);
#endif /* UPNPC_USE_SELECT */
int upnpc_process(upnpc_t * p);
#ifdef __cplusplus
}
#endif
#endif /* MINIUPNPC_LIBEVENT_H_INCLUDED */

View File

@ -0,0 +1,229 @@
/* $Id: minixml.c,v 1.11 2014/02/03 15:54:12 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-2014, 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 <string.h>
#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. */
static 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... */
static void parseelt(struct xmlparser * p)
{
int i;
const char * elementname;
while(p->xml < (p->xmlend - 1))
{
if((p->xml + 4) <= p->xmlend && (0 == memcmp(p->xml, "<!--", 4)))
{
p->xml += 3;
/* ignore comments */
do
{
p->xml++;
if ((p->xml + 3) >= p->xmlend)
return;
}
while(memcmp(p->xml, "-->", 3) != 0);
p->xml += 3;
}
else 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) )
{
i++; p->xml++;
if (p->xml >= p->xmlend)
return;
}
if(memcmp(p->xml, "<![CDATA[", 9) == 0)
{
/* CDATA handling */
p->xml += 9;
data = p->xml;
i = 0;
while(memcmp(p->xml, "]]>", 3) != 0)
{
i++; p->xml++;
if ((p->xml + 3) >= p->xmlend)
return;
}
if(i>0 && p->datafunc)
p->datafunc(p->data, data, i);
while(*p->xml!='<')
{
p->xml++;
if (p->xml >= p->xmlend)
return;
}
}
else
{
while(*p->xml!='<')
{
i++; p->xml++;
if ((p->xml + 1) >= p->xmlend)
return;
}
if(i>0 && p->datafunc && *(p->xml + 1) == '/')
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);
}

View File

@ -0,0 +1,37 @@
/* $Id: minixml.h,v 1.7 2012/09/27 15:42:10 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_INCLUDED
#define MINIXML_H_INCLUDED
#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

View File

@ -0,0 +1,80 @@
/* $Id: parsessdpreply.c,v 1.2 2009/11/14 10:37:55 nanard Exp $ */
/* Project : miniupnp
* website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* Author : Thomas Bernard
* copyright (c) 2005-2009 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 <strings.h>
#include "parsessdpreply.h"
/* 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 */
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; j<b; j++)
{
putchar(reply[j]);
}
*/
}
break;
case '\x0a':
case '\x0d':
if(b!=0)
{
/*for(j=b+1; j<i; j++)
{
putchar(reply[j]);
}
putchar('\n');*/
do { b++; } while(reply[b]==' ');
if(0==strncasecmp(reply+a, "location", 8))
{
*location = reply+b;
*locationsize = i-b;
}
else if(0==strncasecmp(reply+a, "st", 2))
{
*st = reply+b;
*stsize = i-b;
}
b = 0;
}
a = i+1;
break;
default:
break;
}
i++;
}
}

View File

@ -0,0 +1,32 @@
/* $Id: parsessdpreply.h,v 1.1 2009/11/14 10:37:55 nanard Exp $ */
/* Project : miniupnp
* website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* Author : Thomas Bernard
* copyright (c) 2005-2009 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 __PARSESSDPREPLY_H__
#define __PARSESSDPREPLY_H__
/* 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 */
void
parseMSEARCHReply(const char * reply, int size,
const char * * location, int * locationsize,
const char * * st, int * stsize);
#endif

View File

@ -0,0 +1,133 @@
/* $Id: upnpc-libevent.c,v 1.5 2014/11/13 09:46:12 nanard Exp $ */
/* miniupnpc-libevent
* Copyright (c) 2008-2014, Thomas BERNARD <miniupnp@free.fr>
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
*
* 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 <stdio.h>
#include <string.h>
#include <signal.h>
#include "miniupnpc-libevent.h"
static struct event_base *base = NULL;
static void sighandler(int signal)
{
(void)signal;
/*printf("signal %d\n", signal);*/
event_base_loopbreak(base);
}
/* ready callback */
static void ready(int code, void * data)
{
upnpc_t * p = (upnpc_t *)data;
printf("READY ! %d %p\n", code, data);
/* 1st request */
upnpc_get_external_ip_address(p);
}
static enum { EGetExtIp = 0, EGetMaxRate, EAddPortMapping, EFinished } state = EGetExtIp;
/* soap callback */
static void soap(int code, void * data)
{
upnpc_t * p = (upnpc_t *)data;
printf("SOAP ! %d\n", code);
if(code == 200) {
switch(state) {
case EGetExtIp:
printf("ExternalIpAddres=%s\n", GetValueFromNameValueList(&p->soap_response_data, "NewExternalIPAddress"));
upnpc_get_link_layer_max_rate(p);
state = EGetMaxRate;
break;
case EGetMaxRate:
printf("DownStream MaxBitRate = %s\t", GetValueFromNameValueList(&p->soap_response_data, "NewLayer1DownstreamMaxBitRate"));
upnpc_add_port_mapping(p, NULL, 60001, 60002, "192.168.0.42", "TCP", "test port mapping", 0);
printf("UpStream MaxBitRate = %s\n", GetValueFromNameValueList(&p->soap_response_data, "NewLayer1UpstreamMaxBitRate"));
state = EAddPortMapping;
break;
case EAddPortMapping:
printf("OK!\n");
state = EFinished;
default:
event_base_loopbreak(base);
}
} else {
printf("SOAP error :\n");
printf(" faultcode='%s'\n", GetValueFromNameValueList(&p->soap_response_data, "faultcode"));
printf(" faultstring='%s'\n", GetValueFromNameValueList(&p->soap_response_data, "faultstring"));
printf(" errorCode=%s\n", GetValueFromNameValueList(&p->soap_response_data, "errorCode"));
printf(" errorDescription='%s'\n", GetValueFromNameValueList(&p->soap_response_data, "errorDescription"));
event_base_loopbreak(base);
}
}
/* program entry point */
int main(int argc, char * * argv)
{
struct sigaction sa;
upnpc_t upnp;
char * multicast_if = NULL;
if(argc > 1) {
multicast_if = argv[1];
}
memset(&sa, 0, sizeof(struct sigaction));
sa.sa_handler = sighandler;
if(sigaction(SIGINT, &sa, NULL) < 0) {
perror("sigaction");
}
#ifdef DEBUG
event_enable_debug_mode();
#if LIBEVENT_VERSION_NUMBER >= 0x02010100
event_enable_debug_logging(EVENT_DBG_ALL); /* Libevent 2.1.1 */
#endif /* LIBEVENT_VERSION_NUMBER >= 0x02010100 */
#endif /* DEBUG */
printf("Using libevent %s\n", event_get_version());
if(LIBEVENT_VERSION_NUMBER != event_get_version_number()) {
fprintf(stderr, "WARNING build using libevent %s", LIBEVENT_VERSION);
}
base = event_base_new();
if(base == NULL) {
fprintf(stderr, "event_base_new() failed\n");
return 1;
}
#ifdef DEBUG
printf("Using Libevent with backend method %s.\n",
event_base_get_method(base));
#endif /* DEBUG */
if(upnpc_init(&upnp, base, multicast_if, ready, soap, &upnp) != UPNPC_OK) {
fprintf(stderr, "upnpc_init() failed\n");
return 1;
}
event_base_dispatch(base); /* TODO : check return value */
printf("finishing...\n");
upnpc_finalize(&upnp);
event_base_free(base);
#if LIBEVENT_VERSION_NUMBER >= 0x02010100
libevent_global_shutdown(); /* Libevent 2.1.1 */
#endif
return 0;
}

View File

@ -0,0 +1,184 @@
/* $Id: upnpreplyparse.c,v 1.18 2014/11/05 05:36:08 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2014 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "upnpreplyparse.h"
#include "minixml.h"
static void
NameValueParserStartElt(void * d, const char * name, int l)
{
struct NameValueParserData * data = (struct NameValueParserData *)d;
data->topelt = 1;
if(l>63)
l = 63;
memcpy(data->curelt, name, l);
data->curelt[l] = '\0';
data->cdata = NULL;
data->cdatalen = 0;
}
static void
NameValueParserEndElt(void * d, const char * name, int l)
{
struct NameValueParserData * data = (struct NameValueParserData *)d;
struct NameValue * nv;
(void)name;
(void)l;
if(!data->topelt)
return;
if(strcmp(data->curelt, "NewPortListing") != 0)
{
int l;
/* standard case. Limited to n chars strings */
l = data->cdatalen;
nv = malloc(sizeof(struct NameValue));
if(l>=(int)sizeof(nv->value))
l = sizeof(nv->value) - 1;
strncpy(nv->name, data->curelt, 64);
nv->name[63] = '\0';
if(data->cdata != NULL)
{
memcpy(nv->value, data->cdata, l);
nv->value[l] = '\0';
}
else
{
nv->value[0] = '\0';
}
nv->l_next = data->l_head; /* insert in list */
data->l_head = nv;
}
data->cdata = NULL;
data->cdatalen = 0;
data->topelt = 0;
}
static void
NameValueParserGetData(void * d, const char * datas, int l)
{
struct NameValueParserData * data = (struct NameValueParserData *)d;
if(strcmp(data->curelt, "NewPortListing") == 0)
{
/* specific case for NewPortListing which is a XML Document */
data->portListing = malloc(l + 1);
if(!data->portListing)
{
/* malloc error */
return;
}
memcpy(data->portListing, datas, l);
data->portListing[l] = '\0';
data->portListingLength = l;
}
else
{
/* standard case. */
data->cdata = datas;
data->cdatalen = l;
}
}
void
ParseNameValue(const char * buffer, int bufsize,
struct NameValueParserData * data)
{
struct xmlparser parser;
data->l_head = NULL;
data->portListing = NULL;
data->portListingLength = 0;
/* init xmlparser object */
parser.xmlstart = buffer;
parser.xmlsize = bufsize;
parser.data = data;
parser.starteltfunc = NameValueParserStartElt;
parser.endeltfunc = NameValueParserEndElt;
parser.datafunc = NameValueParserGetData;
parser.attfunc = 0;
parsexml(&parser);
}
void
ClearNameValueList(struct NameValueParserData * pdata)
{
struct NameValue * nv;
if(pdata->portListing)
{
free(pdata->portListing);
pdata->portListing = NULL;
pdata->portListingLength = 0;
}
while((nv = pdata->l_head) != NULL)
{
pdata->l_head = nv->l_next;
free(nv);
}
}
char *
GetValueFromNameValueList(struct NameValueParserData * pdata,
const char * Name)
{
struct NameValue * nv;
char * p = NULL;
for(nv = pdata->l_head;
(nv != NULL) && (p == NULL);
nv = nv->l_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.l_head;
nv != NULL;
nv = nv->l_next)
{
printf("%s = %s\n", nv->name, nv->value);
}
ClearNameValueList(&pdata);
}
#endif

View File

@ -0,0 +1,63 @@
/* $Id: upnpreplyparse.h,v 1.19 2014/10/27 16:33:19 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2013 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#ifndef UPNPREPLYPARSE_H_INCLUDED
#define UPNPREPLYPARSE_H_INCLUDED
#ifdef __cplusplus
extern "C" {
#endif
struct NameValue {
struct NameValue * l_next;
char name[64];
char value[128];
};
struct NameValueParserData {
struct NameValue * l_head;
char curelt[64];
char * portListing;
int portListingLength;
int topelt;
const char * cdata;
int cdatalen;
};
/* 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);
#if 0
/* GetValueFromNameValueListIgnoreNS() */
char *
GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata,
const char * Name);
#endif
/* DisplayNameValueList() */
#ifdef DEBUG
void
DisplayNameValueList(char * buffer, int bufsize);
#endif
#ifdef __cplusplus
}
#endif
#endif

View File

@ -8,6 +8,7 @@ build/
Makefile.bak
miniupnpcstrings.h
pythonmodule
pythonmodule3
upnpc-shared
upnpc-static
minihttptestserver
@ -22,4 +23,6 @@ jnaerator-*.jar
miniupnpc.h.bak
testupnpreplyparse
validateupnpreplyparse
testportlistingparse
validateportlistingparse
listdevices

View File

@ -36,7 +36,7 @@ else (NOT WIN32)
endif (NOT WIN32)
if (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
add_definitions (-DMACOSX -D_DARWIN_C_SOURCE)
add_definitions (-D_DARWIN_C_SOURCE)
endif ()
# Set compiler specific build flags
@ -86,7 +86,7 @@ endif (NOT WIN32 AND NOT CMAKE_SYSTEM_NAME STREQUAL "AmigaOS")
if (WIN32)
set_source_files_properties (${MINIUPNPC_SOURCES} PROPERTIES
COMPILE_DEFINITIONS STATICLIB
COMPILE_DEFINITIONS MINIUPNP_STATICLIB
COMPILE_DEFINITIONS MINIUPNP_EXPORTS
)
endif (WIN32)
@ -165,6 +165,8 @@ install (FILES
igd_desc_parse.h
upnpreplyparse.h
upnperrors.h
miniupnpctypes.h
portlistingparse.h
declspec.h
DESTINATION include/miniupnpc
)

View File

@ -1,6 +1,24 @@
$Id: Changelog.txt,v 1.193 2014/02/05 17:26:45 nanard Exp $
$Id: Changelog.txt,v 1.199 2014/11/05 06:06:37 nanard Exp $
miniUPnP client Changelog.
2014/11/05:
simplified function GetUPNPUrls()
2014/09/11:
use remoteHost arg of DeletePortMapping
2014/09/06:
Fix python3 build
2014/07/01:
Fix parsing of IGD2 root descriptions
2014/06/10:
rename LIBSPEC to MINIUPNP_LIBSPEC
2014/05/15:
Add support for IGD2 AddAnyPortMapping and DeletePortMappingRange
2014/02/05:
handle EINPROGRESS after connect()

View File

@ -1,5 +1,5 @@
MiniUPnPc
Copyright (c) 2005-2011, Thomas BERNARD
Copyright (c) 2005-2014, Thomas BERNARD
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,4 +1,4 @@
# $Id: Makefile,v 1.105 2013/05/14 20:37:36 nanard Exp $
# $Id: Makefile,v 1.113 2014/11/01 10:37:32 nanard Exp $
# MiniUPnP Project
# http://miniupnp.free.fr/
# http://miniupnp.tuxfamily.org/
@ -59,9 +59,11 @@ APIVERSION = 11
SRCS = igd_desc_parse.c miniupnpc.c minixml.c minisoap.c miniwget.c \
upnpc.c upnpcommands.c upnpreplyparse.c testminixml.c \
minixmlvalid.c testupnpreplyparse.c minissdpc.c \
upnperrors.c testigddescparse.c testminiwget.c \
minixmlvalid.c testupnpreplyparse.c minissdpc.c \
upnperrors.c testigddescparse.c testminiwget.c \
connecthostport.c portlistingparse.c receivedata.c \
testportlistingparse.c miniupnpcmodule.c \
minihttptestserver.c \
listdevices.c
LIBOBJS = miniwget.o minixml.o igd_desc_parse.o minisoap.o \
@ -86,7 +88,7 @@ LIBRARY = libminiupnpc.a
ifeq ($(OS), Darwin)
SHAREDLIBRARY = libminiupnpc.dylib
SONAME = $(basename $(SHAREDLIBRARY)).$(APIVERSION).dylib
CFLAGS := -DMACOSX -D_DARWIN_C_SOURCE $(CFLAGS)
CFLAGS := -D_DARWIN_C_SOURCE $(CFLAGS)
else
ifeq ($(JARSUFFIX), win32)
SHAREDLIBRARY = miniupnpc.dll
@ -99,7 +101,7 @@ endif
EXECUTABLES = upnpc-static listdevices
EXECUTABLES_ADDTESTS = testminixml minixmlvalid testupnpreplyparse \
testigddescparse testminiwget
testigddescparse testminiwget testportlistingparse
TESTMINIXMLOBJS = minixml.o igd_desc_parse.o testminixml.o
@ -107,6 +109,8 @@ TESTMINIWGETOBJS = miniwget.o testminiwget.o connecthostport.o receivedata.o
TESTUPNPREPLYPARSE = testupnpreplyparse.o minixml.o upnpreplyparse.o
TESTPORTLISTINGPARSE = testportlistingparse.o minixml.o portlistingparse.o
TESTIGDDESCPARSE = testigddescparse.o igd_desc_parse.o minixml.o \
miniupnpc.o miniwget.o upnpcommands.o upnpreplyparse.o \
minisoap.o connecthostport.o receivedata.o \
@ -140,7 +144,8 @@ all: $(LIBRARY) $(EXECUTABLES)
test: check
check: validateminixml validateminiwget validateupnpreplyparse
check: validateminixml validateminiwget validateupnpreplyparse \
validateportlistingparse
everything: all $(EXECUTABLES_ADDTESTS)
@ -173,11 +178,18 @@ validateupnpreplyparse: testupnpreplyparse testupnpreplyparse.sh
./testupnpreplyparse.sh
touch $@
validateportlistingparse: testportlistingparse
@echo "portlistingparse validation test"
./testportlistingparse
touch $@
clean:
$(RM) $(LIBRARY) $(SHAREDLIBRARY) $(EXECUTABLES) $(OBJS) miniupnpcstrings.h
$(RM) $(EXECUTABLES_ADDTESTS)
# clean python stuff
$(RM) pythonmodule pythonmodule3
$(RM) validateminixml validateminiwget validateupnpreplyparse
$(RM) minihttptestserver
$(RM) -r build/ dist/
#python setup.py clean
# clean jnaerator stuff
@ -208,9 +220,9 @@ endif
$(INSTALL) -m 755 external-ip.sh $(DESTDIR)$(INSTALLDIRBIN)/external-ip
ifneq ($(OS), AmigaOS)
$(INSTALL) -d $(DESTDIR)$(INSTALLDIRMAN)/man3
$(INSTALL) man3/miniupnpc.3 $(DESTDIR)$(INSTALLDIRMAN)/man3/miniupnpc.3
$(INSTALL) -m 644 man3/miniupnpc.3 $(DESTDIR)$(INSTALLDIRMAN)/man3/miniupnpc.3
ifeq ($(OS), Linux)
gzip $(DESTDIR)$(INSTALLDIRMAN)/man3/miniupnpc.3
gzip -f $(DESTDIR)$(INSTALLDIRMAN)/man3/miniupnpc.3
endif
endif
@ -253,6 +265,8 @@ testupnpreplyparse: $(TESTUPNPREPLYPARSE)
testigddescparse: $(TESTIGDDESCPARSE)
testportlistingparse: $(TESTPORTLISTINGPARSE)
miniupnpcstrings.h: miniupnpcstrings.h.in updateminiupnpcstrings.sh VERSION
$(SH) updateminiupnpcstrings.sh
@ -320,4 +334,9 @@ upnperrors.o: igd_desc_parse.h
testigddescparse.o: igd_desc_parse.h minixml.h miniupnpc.h declspec.h
testminiwget.o: miniwget.h declspec.h
connecthostport.o: connecthostport.h
portlistingparse.o: portlistingparse.h declspec.h miniupnpctypes.h minixml.h
receivedata.o: receivedata.h
testportlistingparse.o: portlistingparse.h declspec.h miniupnpctypes.h
miniupnpcmodule.o: miniupnpc.h declspec.h igd_desc_parse.h upnpcommands.h
miniupnpcmodule.o: upnpreplyparse.h portlistingparse.h miniupnpctypes.h
miniupnpcmodule.o: upnperrors.h

View File

@ -50,11 +50,11 @@ dll/upnpc.o: upnpc.o
echo $@ generated with $<
.c.o:
$(CC) $(CFLAGS) -DSTATICLIB -c -o $@ $<
$(CC) $(CFLAGS) -DMINIUPNP_STATICLIB -c -o $@ $<
$(CC) $(CFLAGS) -DMINIUPNP_EXPORTS -c -o dll/$@ $<
upnpc.o:
$(CC) $(CFLAGS) -DSTATICLIB -c -o $@ $<
$(CC) $(CFLAGS) -DMINIUPNP_STATICLIB -c -o $@ $<
$(CC) $(CFLAGS) -c -o dll/$@ $<
# --enable-stdcall-fixup

View File

@ -3,16 +3,11 @@ Project web page: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
github: https://github.com/miniupnp/miniupnp
freecode: http://freecode.com/projects/miniupnp
Author: Thomas Bernard
Copyright (c) 2005-2012 Thomas Bernard
Copyright (c) 2005-2014 Thomas Bernard
This software is subject to the conditions detailed in the
LICENSE file provided within this distribution.
For the comfort of Win32 users, bsdqueue.h is included in the distribution.
Its licence is included in the header of the file.
bsdqueue.h is a copy of the sys/queue.h of an OpenBSD system.
* miniUPnP Client - miniUPnPc *
To compile, simply run 'gmake' (could be 'make' on your system).

View File

@ -1,531 +0,0 @@
/* $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_ */

View File

@ -1,4 +1,4 @@
/* $Id: connecthostport.c,v 1.12 2014/02/05 17:26:46 nanard Exp $ */
/* $Id: connecthostport.c,v 1.13 2014/03/31 12:36:36 nanard Exp $ */
/* Project : miniupnp
* Author : Thomas Bernard
* Copyright (c) 2010-2014 Thomas Bernard
@ -35,6 +35,7 @@
#ifndef USE_GETHOSTBYNAME
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#endif /* #ifndef USE_GETHOSTBYNAME */
#endif /* #else _WIN32 */

View File

@ -1,19 +1,19 @@
#ifndef DECLSPEC_H_INCLUDED
#define DECLSPEC_H_INCLUDED
#if defined(_WIN32) && !defined(STATICLIB)
#if defined(_WIN32) && !defined(MINIUPNP_STATICLIB)
/* for windows dll */
#ifdef MINIUPNP_EXPORTS
#define LIBSPEC __declspec(dllexport)
#define MINIUPNP_LIBSPEC __declspec(dllexport)
#else
#define LIBSPEC __declspec(dllimport)
#define MINIUPNP_LIBSPEC __declspec(dllimport)
#endif
#else
#if defined(__GNUC__) && __GNUC__ >= 4
/* fix dynlib for OS X 10.9.2 and Apple LLVM version 5.0 */
#define LIBSPEC __attribute__ ((visibility ("default")))
#define MINIUPNP_LIBSPEC __attribute__ ((visibility ("default")))
#else
#define LIBSPEC
#define MINIUPNP_LIBSPEC
#endif
#endif

View File

@ -1,8 +1,8 @@
/* $Id: igd_desc_parse.c,v 1.14 2011/04/11 09:19:24 nanard Exp $ */
/* $Id: igd_desc_parse.c,v 1.15 2014/07/01 13:01:17 nanard Exp $ */
/* Project : miniupnp
* http://miniupnp.free.fr/
* Author : Thomas Bernard
* Copyright (c) 2005-2010 Thomas Bernard
* Copyright (c) 2005-2014 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution. */
@ -26,6 +26,8 @@ void IGDstartelt(void * d, const char * name, int l)
}
}
#define COMPARE(str, cstr) (0==memcmp(str, cstr, sizeof(cstr) - 1))
/* End element handler :
* update nesting level counter and update parser state if
* service element is parsed */
@ -36,23 +38,16 @@ void IGDendelt(void * d, const char * name, int l)
/*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->tmp.servicetype,
"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1")) {
if(COMPARE(datas->tmp.servicetype,
"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:")) {
memcpy(&datas->CIF, &datas->tmp, sizeof(struct IGDdatas_service));
} else if(0==strcmp(datas->tmp.servicetype,
"urn:schemas-upnp-org:service:WANIPv6FirewallControl:1")) {
} else if(COMPARE(datas->tmp.servicetype,
"urn:schemas-upnp-org:service:WANIPv6FirewallControl:")) {
memcpy(&datas->IPv6FC, &datas->tmp, sizeof(struct IGDdatas_service));
} else if(0==strcmp(datas->tmp.servicetype,
"urn:schemas-upnp-org:service:WANIPConnection:1")
|| 0==strcmp(datas->tmp.servicetype,
"urn:schemas-upnp-org:service:WANPPPConnection:1") ) {
} else if(COMPARE(datas->tmp.servicetype,
"urn:schemas-upnp-org:service:WANIPConnection:")
|| COMPARE(datas->tmp.servicetype,
"urn:schemas-upnp-org:service:WANPPPConnection:") ) {
if(datas->first.servicetype[0] == '\0') {
memcpy(&datas->first, &datas->tmp, sizeof(struct IGDdatas_service));
} else {

View File

@ -1,7 +1,7 @@
/* $Id: minisoap.c,v 1.22 2012/01/21 13:30:31 nanard Exp $ */
/* $Id: minisoap.c,v 1.23 2014/11/04 22:31:55 nanard Exp $ */
/* Project : miniupnp
* Author : Thomas Bernard
* Copyright (c) 2005-2012 Thomas Bernard
* Copyright (c) 2005-2014 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution.
*
@ -96,7 +96,7 @@ int soapPostSubmit(int fd,
headerssize = snprintf(headerbuf, sizeof(headerbuf),
"POST %s HTTP/%s\r\n"
"Host: %s%s\r\n"
"User-Agent: " OS_STRING ", UPnP/1.0, MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n"
"User-Agent: " OS_STRING ", " UPNP_VERSION_STRING ", MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n"
"Content-Length: %d\r\n"
"Content-Type: text/xml\r\n"
"SOAPAction: \"%s\"\r\n"

View File

@ -1,4 +1,4 @@
/* $Id: miniupnpc.c,v 1.116 2014/01/31 14:09:03 nanard Exp $ */
/* $Id: miniupnpc.c,v 1.120 2014/11/05 06:06:38 nanard Exp $ */
/* Project : miniupnp
* Web : http://miniupnp.free.fr/
* Author : Thomas BERNARD
@ -6,7 +6,7 @@
* This software is subjet to the conditions detailed in the
* provided LICENSE file. */
#define __EXTENSIONS__ 1
#if !defined(MACOSX) && !defined(__sun)
#if !defined(__APPLE__) && !defined(__sun)
#if !defined(_XOPEN_SOURCE) && !defined(__OpenBSD__) && !defined(__NetBSD__)
#ifndef __cplusplus
#define _XOPEN_SOURCE 600
@ -17,7 +17,7 @@
#endif
#endif
#if !defined(__DragonFly__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(MACOSX) && !defined(_WIN32) && !defined(__CYGWIN__) && !defined(__sun)
#if !defined(__DragonFly__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__APPLE__) && !defined(_WIN32) && !defined(__CYGWIN__) && !defined(__sun) && !defined(__GNU__) && !defined(__FreeBSD_kernel__)
#define HAS_IP_MREQN
#endif
@ -70,6 +70,9 @@
/* Amiga OS specific stuff */
#define TIMEVAL struct timeval
#endif
#ifdef __GNU__
#define MAXHOSTNAMELEN 64
#endif
#if defined(HAS_IP_MREQN) && defined(NEED_STRUCT_IP_MREQN)
@ -106,7 +109,7 @@ struct ip_mreqn
#define SERVICEPREFIX2 'u'
/* root description parsing */
LIBSPEC void parserootdesc(const char * buffer, int bufsize, struct IGDdatas * data)
MINIUPNP_LIBSPEC void parserootdesc(const char * buffer, int bufsize, struct IGDdatas * data)
{
struct xmlparser parser;
/* xmlparser object */
@ -335,7 +338,7 @@ parseMSEARCHReply(const char * reply, int size,
* no devices was found.
* It is up to the caller to free the chained list
* delay is in millisecond (poll) */
LIBSPEC struct UPNPDev *
MINIUPNP_LIBSPEC struct UPNPDev *
upnpDiscoverDevices(const char * const deviceTypes[],
int delay, const char * multicastif,
const char * minissdpdsock, int sameport,
@ -765,7 +768,7 @@ upnpDiscoverDevice(const char * device, int delay, const char * multicastif,
/* freeUPNPDevlist() should be used to
* free the chained list returned by upnpDiscover() */
LIBSPEC void freeUPNPDevlist(struct UPNPDev * devlist)
MINIUPNP_LIBSPEC void freeUPNPDevlist(struct UPNPDev * devlist)
{
struct UPNPDev * next;
while(devlist)
@ -776,122 +779,106 @@ LIBSPEC void freeUPNPDevlist(struct UPNPDev * devlist)
}
}
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...
*/
LIBSPEC void
GetUPNPUrls(struct UPNPUrls * urls, struct IGDdatas * data,
const char * descURL, unsigned int scope_id)
static char *
build_absolute_url(const char * baseurl, const char * descURL,
const char * url, unsigned int scope_id)
{
int l, n;
char * s;
const char * base;
char * p;
int n1, n2, n3, n4;
#ifdef IF_NAMESIZE
char ifname[IF_NAMESIZE];
#else
char scope_str[8];
#endif
n1 = strlen(data->urlbase);
if(n1==0)
n1 = strlen(descURL);
if( (url[0] == 'h')
&&(url[1] == 't')
&&(url[2] == 't')
&&(url[3] == 'p')
&&(url[4] == ':')
&&(url[5] == '/')
&&(url[6] == '/'))
return strdup(url);
base = (baseurl[0] == '\0') ? descURL : baseurl;
n = strlen(base);
if(n > 7) {
p = strchr(base + 7, '/');
if(p)
n = p - base;
}
l = n + strlen(url) + 1;
if(url[0] != '/')
l++;
if(scope_id != 0) {
#ifdef IF_NAMESIZE
if(if_indextoname(scope_id, ifname)) {
n1 += 3 + strlen(ifname); /* 3 == strlen(%25) */
l += 3 + strlen(ifname); /* 3 == strlen(%25) */
}
#else
/* under windows, scope is numerical */
snprintf(scope_str, sizeof(scope_str), "%u", scope_id);
/* under windows, scope is numerical */
l += 3 + snprintf(scope_str, sizeof(scope_str), "%u", scope_id);
#endif
}
n1 += 2; /* 1 byte more for Null terminator, 1 byte for '/' if needed */
n2 = n1; n3 = n1; n4 = n1;
n1 += strlen(data->first.scpdurl);
n2 += strlen(data->first.controlurl);
n3 += strlen(data->CIF.controlurl);
n4 += strlen(data->IPv6FC.controlurl);
/* allocate memory to store URLs */
urls->ipcondescURL = (char *)malloc(n1);
urls->controlURL = (char *)malloc(n2);
urls->controlURL_CIF = (char *)malloc(n3);
urls->controlURL_6FC = (char *)malloc(n4);
/* strdup descURL */
urls->rootdescURL = strdup(descURL);
/* get description of 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';
s = malloc(l);
if(s == NULL) return NULL;
memcpy(s, base, n);
if(scope_id != 0) {
if(0 == memcmp(urls->ipcondescURL, "http://[fe80:", 13)) {
s[n] = '\0';
if(0 == memcmp(s, "http://[fe80:", 13)) {
/* this is a linklocal IPv6 address */
p = strchr(urls->ipcondescURL, ']');
p = strchr(s, ']');
if(p) {
/* insert %25<scope> into URL */
#ifdef IF_NAMESIZE
memmove(p + 3 + strlen(ifname), p, strlen(p) + 1);
memcpy(p, "%25", 3);
memcpy(p + 3, ifname, strlen(ifname));
n += 3 + strlen(ifname);
#else
memmove(p + 3 + strlen(scope_str), p, strlen(p) + 1);
memcpy(p, "%25", 3);
memcpy(p + 3, scope_str, strlen(scope_str));
n += 3 + strlen(scope_str);
#endif
}
}
}
strncpy(urls->controlURL, urls->ipcondescURL, n2);
strncpy(urls->controlURL_CIF, urls->ipcondescURL, n3);
strncpy(urls->controlURL_6FC, urls->ipcondescURL, n4);
if(url[0] != '/')
s[n++] = '/';
memcpy(s + n, url, l - n);
return s;
}
url_cpy_or_cat(urls->ipcondescURL, data->first.scpdurl, n1);
/* Prepare the Urls for usage...
*/
MINIUPNP_LIBSPEC void
GetUPNPUrls(struct UPNPUrls * urls, struct IGDdatas * data,
const char * descURL, unsigned int scope_id)
{
/* strdup descURL */
urls->rootdescURL = strdup(descURL);
url_cpy_or_cat(urls->controlURL, data->first.controlurl, n2);
url_cpy_or_cat(urls->controlURL_CIF, data->CIF.controlurl, n3);
url_cpy_or_cat(urls->controlURL_6FC, data->IPv6FC.controlurl, n4);
/* get description of WANIPConnection */
urls->ipcondescURL = build_absolute_url(data->urlbase, descURL,
data->first.scpdurl, scope_id);
urls->controlURL = build_absolute_url(data->urlbase, descURL,
data->first.controlurl, scope_id);
urls->controlURL_CIF = build_absolute_url(data->urlbase, descURL,
data->CIF.controlurl, scope_id);
urls->controlURL_6FC = build_absolute_url(data->urlbase, descURL,
data->IPv6FC.controlurl, scope_id);
#ifdef DEBUG
printf("urls->ipcondescURL='%s' %u n1=%d\n", urls->ipcondescURL,
(unsigned)strlen(urls->ipcondescURL), n1);
printf("urls->controlURL='%s' %u n2=%d\n", urls->controlURL,
(unsigned)strlen(urls->controlURL), n2);
printf("urls->controlURL_CIF='%s' %u n3=%d\n", urls->controlURL_CIF,
(unsigned)strlen(urls->controlURL_CIF), n3);
printf("urls->controlURL_6FC='%s' %u n4=%d\n", urls->controlURL_6FC,
(unsigned)strlen(urls->controlURL_6FC), n4);
printf("urls->ipcondescURL='%s'\n", urls->ipcondescURL);
printf("urls->controlURL='%s'\n", urls->controlURL);
printf("urls->controlURL_CIF='%s'\n", urls->controlURL_CIF);
printf("urls->controlURL_6FC='%s'\n", urls->controlURL_6FC);
#endif
}
LIBSPEC void
MINIUPNP_LIBSPEC void
FreeUPNPUrls(struct UPNPUrls * urls)
{
if(!urls)
@ -938,7 +925,7 @@ UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data)
* passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to
* free allocated memory.
*/
LIBSPEC int
MINIUPNP_LIBSPEC int
UPNP_GetValidIGD(struct UPNPDev * devlist,
struct UPNPUrls * urls,
struct IGDdatas * data,

View File

@ -24,7 +24,9 @@ EXPORTS
UPNP_GetExternalIPAddress
UPNP_GetLinkLayerMaxBitRates
UPNP_AddPortMapping
UPNP_AddAnyPortMapping
UPNP_DeletePortMapping
UPNP_DeletePortMappingRange
UPNP_GetPortMappingNumberOfEntries
UPNP_GetSpecificPortMappingEntry
UPNP_GetGenericPortMappingEntry

View File

@ -54,7 +54,7 @@ struct UPNPDev {
* 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 *
MINIUPNP_LIBSPEC struct UPNPDev *
upnpDiscover(int delay, const char * multicastif,
const char * minissdpdsock, int sameport,
int ipv6,
@ -81,12 +81,12 @@ upnpDiscoverDevices(const char * const deviceTypes[],
/* freeUPNPDevlist()
* free list returned by upnpDiscover() */
LIBSPEC void freeUPNPDevlist(struct UPNPDev * devlist);
MINIUPNP_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 *);
MINIUPNP_LIBSPEC void parserootdesc(const char *, int, struct IGDdatas *);
/* structure used to get fast access to urls
* controlURL: controlURL of the WANIPConnection
@ -114,7 +114,7 @@ struct UPNPUrls {
* passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to
* free allocated memory.
*/
LIBSPEC int
MINIUPNP_LIBSPEC int
UPNP_GetValidIGD(struct UPNPDev * devlist,
struct UPNPUrls * urls,
struct IGDdatas * data,
@ -125,21 +125,21 @@ UPNP_GetValidIGD(struct UPNPDev * devlist,
* return value :
* 0 - Not ok
* 1 - OK */
LIBSPEC int
MINIUPNP_LIBSPEC int
UPNP_GetIGDFromUrl(const char * rootdescurl,
struct UPNPUrls * urls,
struct IGDdatas * data,
char * lanaddr, int lanaddrlen);
LIBSPEC void
MINIUPNP_LIBSPEC void
GetUPNPUrls(struct UPNPUrls *, struct IGDdatas *,
const char *, unsigned int);
LIBSPEC void
MINIUPNP_LIBSPEC void
FreeUPNPUrls(struct UPNPUrls *);
/* return 0 or 1 */
LIBSPEC int UPNPIGD_IsConnected(struct UPNPUrls *, struct IGDdatas *);
MINIUPNP_LIBSPEC int UPNPIGD_IsConnected(struct UPNPUrls *, struct IGDdatas *);
#ifdef __cplusplus

View File

@ -1,4 +1,4 @@
/* $Id: miniupnpcmodule.c,v 1.22 2014/01/31 13:18:25 nanard Exp $*/
/* $Id: miniupnpcmodule.c,v 1.24 2014/06/10 09:48:11 nanard Exp $*/
/* Project : miniupnp
* Author : Thomas BERNARD
* website : http://miniupnp.tuxfamily.org/
@ -6,7 +6,7 @@
* This software is subjet to the conditions detailed in the
* provided LICENCE file. */
#include <Python.h>
#define STATICLIB
#define MINIUPNP_STATICLIB
#include "structmember.h"
#include "miniupnpc.h"
#include "upnpcommands.h"
@ -265,6 +265,42 @@ Py_END_ALLOW_THREADS
}
}
/* AddAnyPortMapping(externalPort, protocol, internalHost, internalPort, desc,
* remoteHost)
* protocol is 'UDP' or 'TCP' */
static PyObject *
UPnP_addanyportmapping(UPnPObject *self, PyObject *args)
{
char extPort[6];
unsigned short ePort;
char inPort[6];
unsigned short iPort;
char reservedPort[6];
const char * proto;
const char * host;
const char * desc;
const char * remoteHost;
const char * leaseDuration = "0";
int r;
if (!PyArg_ParseTuple(args, "HssHss", &ePort, &proto, &host, &iPort, &desc, &remoteHost))
return NULL;
Py_BEGIN_ALLOW_THREADS
sprintf(extPort, "%hu", ePort);
sprintf(inPort, "%hu", iPort);
r = UPNP_AddAnyPortMapping(self->urls.controlURL, self->data.first.servicetype,
extPort, inPort, host, desc, proto,
remoteHost, leaseDuration, reservedPort);
Py_END_ALLOW_THREADS
if(r==UPNPCOMMAND_SUCCESS) {
return Py_BuildValue("i", atoi(reservedPort));
} else {
/* TODO: have our own exception type ! */
PyErr_SetString(PyExc_Exception, strupnperror(r));
return NULL;
}
}
/* DeletePortMapping(extPort, proto, removeHost='')
* proto = 'UDP', 'TCP' */
static PyObject *
@ -291,6 +327,37 @@ Py_END_ALLOW_THREADS
}
}
/* DeletePortMappingRange(extPort, proto, removeHost='')
* proto = 'UDP', 'TCP' */
static PyObject *
UPnP_deleteportmappingrange(UPnPObject *self, PyObject *args)
{
char extPortStart[6];
unsigned short ePortStart;
char extPortEnd[6];
unsigned short ePortEnd;
const char * proto;
unsigned char manage;
char manageStr[1];
int r;
if(!PyArg_ParseTuple(args, "HHsb", &ePortStart, &ePortEnd, &proto, &manage))
return NULL;
Py_BEGIN_ALLOW_THREADS
sprintf(extPortStart, "%hu", ePortStart);
sprintf(extPortEnd, "%hu", ePortEnd);
sprintf(manageStr, "%hhu", manage);
r = UPNP_DeletePortMappingRange(self->urls.controlURL, self->data.first.servicetype,
extPortStart, extPortEnd, proto, manageStr);
Py_END_ALLOW_THREADS
if(r==UPNPCOMMAND_SUCCESS) {
Py_RETURN_TRUE;
} else {
/* TODO: have our own exception type ! */
PyErr_SetString(PyExc_Exception, strupnperror(r));
return NULL;
}
}
static PyObject *
UPnP_getportmappingnumberofentries(UPnPObject *self)
{
@ -429,9 +496,15 @@ static PyMethodDef UPnP_methods[] = {
{"addportmapping", (PyCFunction)UPnP_addportmapping, METH_VARARGS,
"add a port mapping"
},
{"addanyportmapping", (PyCFunction)UPnP_addanyportmapping, METH_VARARGS,
"add a port mapping, IGD to select alternative if necessary"
},
{"deleteportmapping", (PyCFunction)UPnP_deleteportmapping, METH_VARARGS,
"delete a port mapping"
},
{"deleteportmappingrange", (PyCFunction)UPnP_deleteportmappingrange, METH_VARARGS,
"delete a range of port mappings"
},
{"getportmappingnumberofentries", (PyCFunction)UPnP_getportmappingnumberofentries, METH_NOARGS,
"-- non standard --"
},
@ -526,7 +599,11 @@ initminiupnpc(void)
UPnPType.tp_new = PyType_GenericNew;
#endif
if (PyType_Ready(&UPnPType) < 0)
#if PY_MAJOR_VERSION >= 3
return 0;
#else
return;
#endif
#if PY_MAJOR_VERSION >= 3
m = PyModule_Create(&moduledef);
@ -537,7 +614,7 @@ initminiupnpc(void)
Py_INCREF(&UPnPType);
PyModule_AddObject(m, "UPnP", (PyObject *)&UPnPType);
#if PY_MAJOR_VERSION >= 3
return m;
#endif

View File

@ -4,4 +4,12 @@
#define OS_STRING "${CMAKE_SYSTEM_NAME}"
#define MINIUPNPC_VERSION_STRING "${MINIUPNPC_VERSION}"
#if 0
/* according to "UPnP Device Architecture 1.0" */
#define UPNP_VERSION_STRING "UPnP/1.0"
#else
/* according to "UPnP Device Architecture 1.1" */
#define UPNP_VERSION_STRING "UPnP/1.1"
#endif
#endif

View File

@ -1,8 +1,8 @@
/* $Id: miniupnpcstrings.h.in,v 1.4 2011/01/04 11:41:53 nanard Exp $ */
/* $Id: miniupnpcstrings.h.in,v 1.6 2014/11/04 22:31:55 nanard Exp $ */
/* Project: miniupnp
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* Author: Thomas Bernard
* Copyright (c) 2005-2011 Thomas Bernard
* Copyright (c) 2005-2014 Thomas Bernard
* This software is subjects to the conditions detailed
* in the LICENCE file provided within this distribution */
#ifndef MINIUPNPCSTRINGS_H_INCLUDED
@ -11,5 +11,13 @@
#define OS_STRING "OS/version"
#define MINIUPNPC_VERSION_STRING "version"
#if 0
/* according to "UPnP Device Architecture 1.0" */
#define UPNP_VERSION_STRING "UPnP/1.0"
#else
/* according to "UPnP Device Architecture 1.1" */
#define UPNP_VERSION_STRING "UPnP/1.1"
#endif
#endif

View File

@ -1,4 +1,4 @@
/* $Id: miniwget.c,v 1.61 2014/02/05 17:27:48 nanard Exp $ */
/* $Id: miniwget.c,v 1.65 2014/11/04 22:31:55 nanard Exp $ */
/* Project : miniupnp
* Website : http://miniupnp.free.fr/
* Author : Thomas Bernard
@ -15,7 +15,6 @@
#include <ws2tcpip.h>
#include <io.h>
#define MAXHOSTNAMELEN 64
#define MIN(x,y) (((x)<(y))?(x):(y))
#define snprintf _snprintf
#define socklen_t int
#ifndef strncasecmp
@ -40,9 +39,14 @@
#include <netdb.h>
#define closesocket close
#endif /* #else _WIN32 */
#if defined(__sun) || defined(sun)
#ifdef __GNU__
#define MAXHOSTNAMELEN 64
#endif /* __GNU__ */
#ifndef MIN
#define MIN(x,y) (((x)<(y))?(x):(y))
#endif
#endif /* MIN */
#include "miniupnpcstrings.h"
#include "miniwget.h"
@ -364,7 +368,7 @@ miniwget3(const char * host,
"GET %s HTTP/%s\r\n"
"Host: %s:%d\r\n"
"Connection: Close\r\n"
"User-Agent: " OS_STRING ", UPnP/1.0, MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n"
"User-Agent: " OS_STRING ", " UPNP_VERSION_STRING ", MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n"
"\r\n",
path, httpversion, host, port);

View File

@ -14,11 +14,11 @@
extern "C" {
#endif
LIBSPEC void * getHTTPResponse(int s, int * size);
MINIUPNP_LIBSPEC void * getHTTPResponse(int s, int * size);
LIBSPEC void * miniwget(const char *, int *, unsigned int);
MINIUPNP_LIBSPEC void * miniwget(const char *, int *, unsigned int);
LIBSPEC void * miniwget_getaddr(const char *, int *, char *, int, unsigned int);
MINIUPNP_LIBSPEC void * miniwget_getaddr(const char *, int *, char *, int, unsigned int);
int parseURL(const char *, char *, unsigned short *, char * *, unsigned int *);

View File

@ -41,7 +41,7 @@
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="_CRT_SECURE_NO_WARNINGS;WIN32;STATICLIB;DEBUG"
PreprocessorDefinitions="_CRT_SECURE_NO_WARNINGS;MINIUPNP_STATICLIB;DEBUG"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
@ -104,7 +104,7 @@
Name="VCCLCompilerTool"
Optimization="2"
EnableIntrinsicFunctions="true"
PreprocessorDefinitions="_CRT_SECURE_NO_WARNINGS;WIN32;STATICLIB"
PreprocessorDefinitions="_CRT_SECURE_NO_WARNINGS;MINIUPNP_STATICLIB"
RuntimeLibrary="2"
EnableFunctionLevelLinking="true"
UsePrecompiledHeader="0"
@ -198,10 +198,6 @@
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
<File
RelativePath="..\bsdqueue.h"
>
</File>
<File
RelativePath="..\connecthostport.h"
>

View File

@ -41,7 +41,7 @@
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;STATICLIB;DEBUG;_CRT_SECURE_NO_WARNINGS"
PreprocessorDefinitions="_DEBUG;_CONSOLE;MINIUPNP_STATICLIB;DEBUG;_CRT_SECURE_NO_WARNINGS"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
@ -115,7 +115,7 @@
Name="VCCLCompilerTool"
Optimization="2"
EnableIntrinsicFunctions="true"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;STATICLIB"
PreprocessorDefinitions="NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;MINIUPNP_STATICLIB"
RuntimeLibrary="2"
EnableFunctionLevelLinking="true"
UsePrecompiledHeader="0"

View File

@ -1,4 +1,4 @@
/* $Id: portlistingparse.c,v 1.6 2012/05/29 10:26:51 nanard Exp $ */
/* $Id: portlistingparse.c,v 1.7 2014/11/01 10:37:32 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2011 Thomas Bernard
@ -62,7 +62,8 @@ startelt(void * d, const char * name, int l)
{
struct PortMapping * pm;
pm = calloc(1, sizeof(struct PortMapping));
LIST_INSERT_HEAD( &(pdata->head), pm, entries);
pm->l_next = pdata->l_head; /* insert in list */
pdata->l_head = pm;
}
}
@ -82,7 +83,7 @@ data(void * d, const char * data, int l)
{
struct PortMapping * pm;
struct PortMappingParserData * pdata = (struct PortMappingParserData *)d;
pm = pdata->head.lh_first;
pm = pdata->l_head;
if(!pm)
return;
if(l > 63)
@ -134,7 +135,6 @@ ParsePortListing(const char * buffer, int bufsize,
struct xmlparser parser;
memset(pdata, 0, sizeof(struct PortMappingParserData));
LIST_INIT(&(pdata->head));
/* init xmlparser */
parser.xmlstart = buffer;
parser.xmlsize = bufsize;
@ -150,9 +150,10 @@ void
FreePortListing(struct PortMappingParserData * pdata)
{
struct PortMapping * pm;
while((pm = pdata->head.lh_first) != NULL)
while((pm = pdata->l_head) != NULL)
{
LIST_REMOVE(pm, entries);
/* remove from list */
pdata->l_head = pm->l_next;
free(pm);
}
}

View File

@ -1,4 +1,4 @@
/* $Id: portlistingparse.h,v 1.5 2012/01/21 13:30:33 nanard Exp $ */
/* $Id: portlistingparse.h,v 1.10 2014/11/01 10:37:32 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2011-2012 Thomas Bernard
@ -11,12 +11,6 @@
/* for the definition of UNSIGNED_INTEGER */
#include "miniupnpctypes.h"
#if defined(NO_SYS_QUEUE_H) || defined(_WIN32) || defined(__HAIKU__)
#include "bsdqueue.h"
#else
#include <sys/queue.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
@ -41,7 +35,7 @@ typedef enum { PortMappingEltNone,
NewLeaseTime } portMappingElt;
struct PortMapping {
LIST_ENTRY(PortMapping) entries;
struct PortMapping * l_next; /* list next element */
UNSIGNED_INTEGER leaseTime;
unsigned short externalPort;
unsigned short internalPort;
@ -53,15 +47,15 @@ struct PortMapping {
};
struct PortMappingParserData {
LIST_HEAD(portmappinglisthead, PortMapping) head;
struct PortMapping * l_head; /* list head */
portMappingElt curelt;
};
LIBSPEC void
MINIUPNP_LIBSPEC void
ParsePortListing(const char * buffer, int bufsize,
struct PortMappingParserData * pdata);
LIBSPEC void
MINIUPNP_LIBSPEC void
FreePortListing(struct PortMappingParserData * pdata);
#ifdef __cplusplus

View File

@ -1,6 +1,6 @@
#! /usr/bin/python
# $Id: setup.py,v 1.9 2012/05/23 08:50:10 nanard Exp $
# the MiniUPnP Project (c) 2007-2012 Thomas Bernard
# the MiniUPnP Project (c) 2007-2014 Thomas Bernard
# http://miniupnp.tuxfamily.org/ or http://miniupnp.free.fr/
#
# python script to build the miniupnpc module under unix
@ -10,9 +10,15 @@ from distutils.core import setup, Extension
from distutils import sysconfig
sysconfig.get_config_vars()["OPT"] = ''
sysconfig.get_config_vars()["CFLAGS"] = ''
setup(name="miniupnpc", version="1.7",
setup(name="miniupnpc",
version=open('VERSION').read().strip(),
author='Thomas BERNARD',
author_email='miniupnp@free.fr',
license=open('LICENSE').read(),
url='http://miniupnp.free.fr/',
description='miniUPnP client',
ext_modules=[
Extension(name="miniupnpc", sources=["miniupnpcmodule.c"],
extra_objects=["libminiupnpc.a"])
])
Extension(name="miniupnpc", sources=["miniupnpcmodule.c"],
extra_objects=["libminiupnpc.a"])
])

View File

@ -1,6 +1,6 @@
#! /usr/bin/python
# $Id: setupmingw32.py,v 1.8 2012/05/23 08:50:10 nanard Exp $
# the MiniUPnP Project (c) 2007-2012 Thomas Bernard
# the MiniUPnP Project (c) 2007-2014 Thomas Bernard
# http://miniupnp.tuxfamily.org/ or http://miniupnp.free.fr/
#
# python script to build the miniupnpc module under windows (using mingw32)
@ -9,10 +9,16 @@ from distutils.core import setup, Extension
from distutils import sysconfig
sysconfig.get_config_vars()["OPT"] = ''
sysconfig.get_config_vars()["CFLAGS"] = ''
setup(name="miniupnpc", version="1.7",
setup(name="miniupnpc",
version=open('VERSION').read().strip(),
author='Thomas BERNARD',
author_email='miniupnp@free.fr',
license=open('LICENSE').read(),
url='http://miniupnp.free.fr/',
description='miniUPnP client',
ext_modules=[
Extension(name="miniupnpc", sources=["miniupnpcmodule.c"],
libraries=["ws2_32", "iphlpapi"],
extra_objects=["libminiupnpc.a"])
])
Extension(name="miniupnpc", sources=["miniupnpcmodule.c"],
libraries=["ws2_32", "iphlpapi"],
extra_objects=["libminiupnpc.a"])
])

View File

@ -0,0 +1,151 @@
/* $Id: testportlistingparse.c,v 1.2 2014/11/01 10:37:32 nanard Exp $ */
/* Project : miniupnp
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* Author : Thomas Bernard
* Copyright (c) 2014 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution.
* */
#include <string.h>
#include <stdio.h>
#include "portlistingparse.h"
struct port_mapping {
unsigned int leasetime;
unsigned short externalport;
unsigned short internalport;
const char * remotehost;
const char * client;
const char * proto;
const char * desc;
unsigned char enabled;
};
/* return the number of differences */
int test(const char * portListingXml, int portListingXmlLen,
const struct port_mapping * ref, int count)
{
int i;
int r = 0;
struct PortMappingParserData data;
struct PortMapping * pm;
memset(&data, 0, sizeof(data));
ParsePortListing(portListingXml, portListingXmlLen, &data);
for(i = 0, pm = data.l_head;
(pm != NULL) && (i < count);
i++, pm = pm->l_next) {
printf("%2d %s %5hu->%s:%-5hu '%s' '%s' %u\n",
i, pm->protocol, pm->externalPort, pm->internalClient,
pm->internalPort,
pm->description, pm->remoteHost,
(unsigned)pm->leaseTime);
if(0 != strcmp(pm->protocol, ref[i].proto)) {
printf("protocol : '%s' != '%s'\n", pm->protocol, ref[i].proto);
r++;
}
if(pm->externalPort != ref[i].externalport) {
printf("externalPort : %hu != %hu\n",
pm->externalPort, ref[i].externalport);
r++;
}
if(0 != strcmp(pm->internalClient, ref[i].client)) {
printf("client : '%s' != '%s'\n",
pm->internalClient, ref[i].client);
r++;
}
if(pm->internalPort != ref[i].internalport) {
printf("internalPort : %hu != %hu\n",
pm->internalPort, ref[i].internalport);
r++;
}
if(0 != strcmp(pm->description, ref[i].desc)) {
printf("description : '%s' != '%s'\n",
pm->description, ref[i].desc);
r++;
}
if(0 != strcmp(pm->remoteHost, ref[i].remotehost)) {
printf("remoteHost : '%s' != '%s'\n",
pm->remoteHost, ref[i].remotehost);
r++;
}
if((unsigned)pm->leaseTime != ref[i].leasetime) {
printf("leaseTime : %u != %u\n",
(unsigned)pm->leaseTime, ref[i].leasetime);
r++;
}
if(pm->enabled != ref[i].enabled) {
printf("enabled : %d != %d\n",
(int)pm->enabled, (int)ref[i].enabled);
r++;
}
}
if((i != count) || (pm != NULL)) {
printf("count mismatch : i=%d count=%d pm=%p\n", i, count, pm);
r++;
}
FreePortListing(&data);
return r;
}
const char test_document[] =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<p:PortMappingList xmlns:p=\"urn:schemas-upnp-org:gw:WANIPConnection\"\n"
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" \n"
"xsi:schemaLocation=\"urn:schemas-upnp-org:gw:WANIPConnection "
"http://www.upnp.org/schemas/gw/WANIPConnection-v2.xsd\">\n"
" <p:PortMappingEntry>\n"
" <p:NewRemoteHost></p:NewRemoteHost>\n"
" <p:NewExternalPort>5002</p:NewExternalPort>\n"
" <p:NewProtocol>UDP</p:NewProtocol>\n"
" <p:NewInternalPort>4001</p:NewInternalPort>\n"
" <p:NewInternalClient>192.168.1.123</p:NewInternalClient>\n"
" <p:NewEnabled>1</p:NewEnabled>\n"
" <p:NewDescription>xxx</p:NewDescription>\n"
" <p:NewLeaseTime>0</p:NewLeaseTime>\n"
" </p:PortMappingEntry>\n"
" <p:PortMappingEntry>\n"
" <p:NewRemoteHost>202.233.2.1</p:NewRemoteHost>\n"
" <p:NewExternalPort>2345</p:NewExternalPort>\n"
" <p:NewProtocol>TCP</p:NewProtocol>\n"
" <p:NewInternalPort>2349</p:NewInternalPort>\n"
" <p:NewInternalClient>192.168.1.137</p:NewInternalClient>\n"
" <p:NewEnabled>1</p:NewEnabled>\n"
" <p:NewDescription>dooom</p:NewDescription>\n"
" <p:NewLeaseTime>346</p:NewLeaseTime>\n"
" </p:PortMappingEntry>\n"
" <p:PortMappingEntry>\n"
" <p:NewRemoteHost>134.231.2.11</p:NewRemoteHost>\n"
" <p:NewExternalPort>12345</p:NewExternalPort>\n"
" <p:NewProtocol>TCP</p:NewProtocol>\n"
" <p:NewInternalPort>12345</p:NewInternalPort>\n"
" <p:NewInternalClient>192.168.1.137</p:NewInternalClient>\n"
" <p:NewEnabled>1</p:NewEnabled>\n"
" <p:NewDescription>dooom A</p:NewDescription>\n"
" <p:NewLeaseTime>347</p:NewLeaseTime>\n"
" </p:PortMappingEntry>\n"
"</p:PortMappingList>";
#define PORT_MAPPINGS_COUNT 3
const struct port_mapping port_mappings[PORT_MAPPINGS_COUNT] = {
{347, 12345, 12345, "134.231.2.11", "192.168.1.137", "TCP", "dooom A", 1},
{346, 2345, 2349, "202.233.2.1", "192.168.1.137", "TCP", "dooom", 1},
{0, 5002, 4001, "", "192.168.1.123", "UDP", "xxx", 1}
};
/* --- main --- */
int main(void)
{
int r;
r = test(test_document, sizeof(test_document) - 1,
port_mappings, PORT_MAPPINGS_COUNT);
if(r == 0) {
printf("test of portlistingparse OK\n");
return 0;
} else {
printf("test FAILED (%d differences counted)\n", r);
return 1;
}
}

View File

@ -1,4 +1,4 @@
/* $Id: upnpc.c,v 1.102 2014/02/05 17:27:14 nanard Exp $ */
/* $Id: upnpc.c,v 1.105 2014/11/01 10:37:32 nanard Exp $ */
/* Project : miniupnp
* Author : Thomas Bernard
* Copyright (c) 2005-2014 Thomas Bernard
@ -174,7 +174,7 @@ static void NewListRedirections(struct UPNPUrls * urls,
if(r == UPNPCOMMAND_SUCCESS)
{
printf(" i protocol exPort->inAddr:inPort description remoteHost leaseTime\n");
for(pm = pdata.head.lh_first; pm != NULL; pm = pm->entries.le_next)
for(pm = pdata.l_head; pm != NULL; pm = pm->l_next)
{
printf("%2d %s %5hu->%s:%-5hu '%s' '%s' %u\n",
i, pm->protocol, pm->externalPort, pm->internalClient,
@ -199,7 +199,7 @@ static void NewListRedirections(struct UPNPUrls * urls,
&pdata);
if(r == UPNPCOMMAND_SUCCESS)
{
for(pm = pdata.head.lh_first; pm != NULL; pm = pm->entries.le_next)
for(pm = pdata.l_head; pm != NULL; pm = pm->l_next)
{
printf("%2d %s %5hu->%s:%-5hu '%s' '%s' %u\n",
i, pm->protocol, pm->externalPort, pm->internalClient,
@ -223,17 +223,19 @@ static void NewListRedirections(struct UPNPUrls * urls,
* 3 - Add port mapping
* 4 - get this port mapping from the IGD */
static void SetRedirectAndTest(struct UPNPUrls * urls,
struct IGDdatas * data,
const char * iaddr,
const char * iport,
const char * eport,
const char * proto,
const char * leaseDuration,
const char * description)
struct IGDdatas * data,
const char * iaddr,
const char * iport,
const char * eport,
const char * proto,
const char * leaseDuration,
const char * description,
int addAny)
{
char externalIPAddress[40];
char intClient[40];
char intPort[6];
char reservedPort[6];
char duration[16];
int r;
@ -249,31 +251,41 @@ static void SetRedirectAndTest(struct UPNPUrls * urls,
return;
}
UPNP_GetExternalIPAddress(urls->controlURL,
data->first.servicetype,
externalIPAddress);
if(externalIPAddress[0])
printf("ExternalIPAddress = %s\n", externalIPAddress);
else
printf("GetExternalIPAddress failed.\n");
r = UPNP_AddPortMapping(urls->controlURL, data->first.servicetype,
eport, iport, iaddr, description,
proto, 0, leaseDuration);
r = UPNP_GetExternalIPAddress(urls->controlURL,
data->first.servicetype,
externalIPAddress);
if(r!=UPNPCOMMAND_SUCCESS)
printf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n",
eport, iport, iaddr, r, strupnperror(r));
printf("GetExternalIPAddress failed.\n");
else
printf("ExternalIPAddress = %s\n", externalIPAddress);
if (addAny) {
r = UPNP_AddAnyPortMapping(urls->controlURL, data->first.servicetype,
eport, iport, iaddr, description,
proto, 0, leaseDuration, reservedPort);
if(r==UPNPCOMMAND_SUCCESS)
eport = reservedPort;
else
printf("AddAnyPortMapping(%s, %s, %s) failed with code %d (%s)\n",
eport, iport, iaddr, r, strupnperror(r));
} else {
r = UPNP_AddPortMapping(urls->controlURL, data->first.servicetype,
eport, iport, iaddr, description,
proto, 0, leaseDuration);
if(r!=UPNPCOMMAND_SUCCESS)
printf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n",
eport, iport, iaddr, r, strupnperror(r));
}
r = UPNP_GetSpecificPortMappingEntry(urls->controlURL,
data->first.servicetype,
eport, proto, NULL/*remoteHost*/,
intClient, intPort, NULL/*desc*/,
NULL/*enabled*/, duration);
data->first.servicetype,
eport, proto, NULL/*remoteHost*/,
intClient, intPort, NULL/*desc*/,
NULL/*enabled*/, duration);
if(r!=UPNPCOMMAND_SUCCESS)
printf("GetSpecificPortMappingEntry() failed with code %d (%s)\n",
r, strupnperror(r));
if(intClient[0]) {
else {
printf("InternalIP:Port = %s:%s\n", intClient, intPort);
printf("external %s:%s %s is redirected to internal %s:%s (duration=%s)\n",
externalIPAddress, eport, proto, intClient, intPort, duration);
@ -283,8 +295,9 @@ static void SetRedirectAndTest(struct UPNPUrls * urls,
static void
RemoveRedirect(struct UPNPUrls * urls,
struct IGDdatas * data,
const char * eport,
const char * proto)
const char * eport,
const char * proto,
const char * remoteHost)
{
int r;
if(!proto || !eport)
@ -298,10 +311,36 @@ RemoveRedirect(struct UPNPUrls * urls,
fprintf(stderr, "protocol invalid\n");
return;
}
r = UPNP_DeletePortMapping(urls->controlURL, data->first.servicetype, eport, proto, 0);
r = UPNP_DeletePortMapping(urls->controlURL, data->first.servicetype, eport, proto, remoteHost);
printf("UPNP_DeletePortMapping() returned : %d\n", r);
}
static void
RemoveRedirectRange(struct UPNPUrls * urls,
struct IGDdatas * data,
const char * ePortStart, char const * ePortEnd,
const char * proto, const char * manage)
{
int r;
if (!manage)
manage = "0";
if(!proto || !ePortStart || !ePortEnd)
{
fprintf(stderr, "invalid arguments\n");
return;
}
proto = protofix(proto);
if(!proto)
{
fprintf(stderr, "protocol invalid\n");
return;
}
r = UPNP_DeletePortMappingRange(urls->controlURL, data->first.servicetype, ePortStart, ePortEnd, proto, manage);
printf("UPNP_DeletePortMappingRange() returned : %d\n", r);
}
/* IGD:2, functions for service WANIPv6FirewallControl:1 */
static void GetFirewallStatus(struct UPNPUrls * urls, struct IGDdatas * data)
{
@ -546,10 +585,12 @@ int main(int argc, char ** argv)
|| (command == 'D' && commandargc<1))
{
fprintf(stderr, "Usage :\t%s [options] -a ip port external_port protocol [duration]\n\t\tAdd port redirection\n", argv[0]);
fprintf(stderr, " \t%s [options] -d external_port protocol [port2 protocol2] [...]\n\t\tDelete port redirection\n", argv[0]);
fprintf(stderr, " \t%s [options] -d external_port protocol <remote host>\n\t\tDelete port redirection\n", argv[0]);
fprintf(stderr, " \t%s [options] -s\n\t\tGet Connection status\n", argv[0]);
fprintf(stderr, " \t%s [options] -l\n\t\tList redirections\n", argv[0]);
fprintf(stderr, " \t%s [options] -L\n\t\tList redirections (using GetListOfPortMappings, IGD v2)\n", argv[0]);
fprintf(stderr, " \t%s [options] -L\n\t\tList redirections (using GetListOfPortMappings (for IGD:2 only)\n", argv[0]);
fprintf(stderr, " \t%s [options] -n ip port external_port protocol [duration]\n\t\tAdd (any) port redirection allowing IGD to use alternative external_port (for IGD:2 only)\n", argv[0]);
fprintf(stderr, " \t%s [options] -N external_port_start external_port_end protocol [manage]\n\t\tDelete range of port redirections (for IGD:2 only)\n", argv[0]);
fprintf(stderr, " \t%s [options] -r port1 protocol1 [port2 protocol2] [...]\n\t\tAdd all redirections to the current host\n", argv[0]);
fprintf(stderr, " \t%s [options] -A remote_ip remote_port internal_ip internal_port protocol lease_time\n\t\tAdd Pinhole (for IGD:2 only)\n", argv[0]);
fprintf(stderr, " \t%s [options] -U uniqueID new_lease_time\n\t\tUpdate Pinhole (for IGD:2 only)\n", argv[0]);
@ -631,16 +672,28 @@ int main(int argc, char ** argv)
break;
case 'a':
SetRedirectAndTest(&urls, &data,
commandargv[0], commandargv[1],
commandargv[2], commandargv[3],
(commandargc > 4)?commandargv[4]:"0",
description);
commandargv[0], commandargv[1],
commandargv[2], commandargv[3],
(commandargc > 4)?commandargv[4]:"0",
description, 0);
break;
case 'd':
for(i=0; i<commandargc; i+=2)
{
RemoveRedirect(&urls, &data, commandargv[i], commandargv[i+1]);
}
RemoveRedirect(&urls, &data, commandargv[0], commandargv[1],
commandargc > 2 ? commandargv[2] : NULL);
break;
case 'n': /* aNy */
SetRedirectAndTest(&urls, &data,
commandargv[0], commandargv[1],
commandargv[2], commandargv[3],
(commandargc > 4)?commandargv[4]:"0",
description, 1);
break;
case 'N':
if (commandargc < 3)
fprintf(stderr, "too few arguments\n");
RemoveRedirectRange(&urls, &data, commandargv[0], commandargv[1], commandargv[2],
commandargc > 3 ? commandargv[3] : NULL);
break;
case 's':
GetConnectionStatus(&urls, &data);
@ -650,9 +703,9 @@ int main(int argc, char ** argv)
{
/*printf("port %s protocol %s\n", argv[i], argv[i+1]);*/
SetRedirectAndTest(&urls, &data,
lanaddr, commandargv[i],
commandargv[i], commandargv[i+1], "0",
description);
lanaddr, commandargv[i],
commandargv[i], commandargv[i+1], "0",
description, 0);
}
break;
case 'A':

View File

@ -20,7 +20,7 @@ my_atoui(const char * s)
/*
* */
LIBSPEC UNSIGNED_INTEGER
MINIUPNP_LIBSPEC UNSIGNED_INTEGER
UPNP_GetTotalBytesSent(const char * controlURL,
const char * servicetype)
{
@ -44,7 +44,7 @@ UPNP_GetTotalBytesSent(const char * controlURL,
/*
* */
LIBSPEC UNSIGNED_INTEGER
MINIUPNP_LIBSPEC UNSIGNED_INTEGER
UPNP_GetTotalBytesReceived(const char * controlURL,
const char * servicetype)
{
@ -68,7 +68,7 @@ UPNP_GetTotalBytesReceived(const char * controlURL,
/*
* */
LIBSPEC UNSIGNED_INTEGER
MINIUPNP_LIBSPEC UNSIGNED_INTEGER
UPNP_GetTotalPacketsSent(const char * controlURL,
const char * servicetype)
{
@ -92,7 +92,7 @@ UPNP_GetTotalPacketsSent(const char * controlURL,
/*
* */
LIBSPEC UNSIGNED_INTEGER
MINIUPNP_LIBSPEC UNSIGNED_INTEGER
UPNP_GetTotalPacketsReceived(const char * controlURL,
const char * servicetype)
{
@ -116,7 +116,7 @@ UPNP_GetTotalPacketsReceived(const char * controlURL,
/* UPNP_GetStatusInfo() call the corresponding UPNP method
* returns the current status and uptime */
LIBSPEC int
MINIUPNP_LIBSPEC int
UPNP_GetStatusInfo(const char * controlURL,
const char * servicetype,
char * status,
@ -181,7 +181,7 @@ UPNP_GetStatusInfo(const char * controlURL,
/* UPNP_GetConnectionTypeInfo() call the corresponding UPNP method
* returns the connection type */
LIBSPEC int
MINIUPNP_LIBSPEC int
UPNP_GetConnectionTypeInfo(const char * controlURL,
const char * servicetype,
char * connectionType)
@ -224,7 +224,7 @@ UPNP_GetConnectionTypeInfo(const char * controlURL,
* One of the values can be null
* Note : GetLinkLayerMaxBitRates belongs to WANPPPConnection:1 only
* We can use the GetCommonLinkProperties from WANCommonInterfaceConfig:1 */
LIBSPEC int
MINIUPNP_LIBSPEC int
UPNP_GetLinkLayerMaxBitRates(const char * controlURL,
const char * servicetype,
unsigned int * bitrateDown,
@ -293,7 +293,7 @@ UPNP_GetLinkLayerMaxBitRates(const char * controlURL,
* 402 Invalid Args - See UPnP Device Architecture section on Control.
* 501 Action Failed - See UPnP Device Architecture section on Control.
*/
LIBSPEC int
MINIUPNP_LIBSPEC int
UPNP_GetExternalIPAddress(const char * controlURL,
const char * servicetype,
char * extIpAdd)
@ -333,15 +333,15 @@ UPNP_GetExternalIPAddress(const char * controlURL,
return ret;
}
LIBSPEC int
MINIUPNP_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,
const char * leaseDuration)
const char * extPort,
const char * inPort,
const char * inClient,
const char * desc,
const char * proto,
const char * remoteHost,
const char * leaseDuration)
{
struct UPNParg * AddPortMappingArgs;
char * buffer;
@ -394,7 +394,74 @@ UPNP_AddPortMapping(const char * controlURL, const char * servicetype,
return ret;
}
LIBSPEC int
MINIUPNP_LIBSPEC int
UPNP_AddAnyPortMapping(const char * controlURL, const char * servicetype,
const char * extPort,
const char * inPort,
const char * inClient,
const char * desc,
const char * proto,
const char * remoteHost,
const char * leaseDuration,
char * reservedPort)
{
struct UPNParg * AddPortMappingArgs;
char * buffer;
int bufsize;
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 = leaseDuration?leaseDuration:"0";
if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
"AddAnyPortMapping", AddPortMappingArgs,
&bufsize))) {
free(AddPortMappingArgs);
return UPNPCOMMAND_HTTP_ERROR;
}
ParseNameValue(buffer, bufsize, &pdata);
free(buffer); buffer = NULL;
resVal = GetValueFromNameValueList(&pdata, "errorCode");
if(resVal) {
ret = UPNPCOMMAND_UNKNOWN_ERROR;
sscanf(resVal, "%d", &ret);
} else {
char *p;
p = GetValueFromNameValueList(&pdata, "NewReservedPort");
if(p) {
strncpy(reservedPort, p, 6);
reservedPort[5] = '\0';
ret = UPNPCOMMAND_SUCCESS;
} else {
ret = UPNPCOMMAND_INVALID_RESPONSE;
}
}
ClearNameValueList(&pdata);
free(AddPortMappingArgs);
return ret;
}
MINIUPNP_LIBSPEC int
UPNP_DeletePortMapping(const char * controlURL, const char * servicetype,
const char * extPort, const char * proto,
const char * remoteHost)
@ -438,7 +505,53 @@ UPNP_DeletePortMapping(const char * controlURL, const char * servicetype,
return ret;
}
LIBSPEC int
MINIUPNP_LIBSPEC int
UPNP_DeletePortMappingRange(const char * controlURL, const char * servicetype,
const char * extPortStart, const char * extPortEnd,
const char * proto,
const char * manage)
{
struct UPNParg * DeletePortMappingArgs;
char * buffer;
int bufsize;
struct NameValueParserData pdata;
const char * resVal;
int ret;
if(!extPortStart || !extPortEnd || !proto || !manage)
return UPNPCOMMAND_INVALID_ARGS;
DeletePortMappingArgs = calloc(5, sizeof(struct UPNParg));
DeletePortMappingArgs[0].elt = "NewStartPort";
DeletePortMappingArgs[0].val = extPortStart;
DeletePortMappingArgs[1].elt = "NewEndPort";
DeletePortMappingArgs[1].val = extPortEnd;
DeletePortMappingArgs[2].elt = "NewProtocol";
DeletePortMappingArgs[2].val = proto;
DeletePortMappingArgs[3].elt = "NewManage";
DeletePortMappingArgs[3].val = manage;
if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
"DeletePortMappingRange",
DeletePortMappingArgs, &bufsize))) {
free(DeletePortMappingArgs);
return UPNPCOMMAND_HTTP_ERROR;
}
ParseNameValue(buffer, bufsize, &pdata);
free(buffer); buffer = NULL;
resVal = GetValueFromNameValueList(&pdata, "errorCode");
if(resVal) {
ret = UPNPCOMMAND_UNKNOWN_ERROR;
sscanf(resVal, "%d", &ret);
} else {
ret = UPNPCOMMAND_SUCCESS;
}
ClearNameValueList(&pdata);
free(DeletePortMappingArgs);
return ret;
}
MINIUPNP_LIBSPEC int
UPNP_GetGenericPortMappingEntry(const char * controlURL,
const char * servicetype,
const char * index,
@ -533,7 +646,7 @@ UPNP_GetGenericPortMappingEntry(const char * controlURL,
return r;
}
LIBSPEC int
MINIUPNP_LIBSPEC int
UPNP_GetPortMappingNumberOfEntries(const char * controlURL,
const char * servicetype,
unsigned int * numEntries)
@ -574,7 +687,7 @@ UPNP_GetPortMappingNumberOfEntries(const char * controlURL,
/* UPNP_GetSpecificPortMappingEntry retrieves an existing port mapping
* the result is returned in the intClient and intPort strings
* please provide 16 and 6 bytes of data */
LIBSPEC int
MINIUPNP_LIBSPEC int
UPNP_GetSpecificPortMappingEntry(const char * controlURL,
const char * servicetype,
const char * extPort,
@ -666,7 +779,7 @@ UPNP_GetSpecificPortMappingEntry(const char * controlURL,
* 733 InconsistantParameters - NewStartPort and NewEndPort values are not
* consistent.
*/
LIBSPEC int
MINIUPNP_LIBSPEC int
UPNP_GetListOfPortMappings(const char * controlURL,
const char * servicetype,
const char * startPort,
@ -748,7 +861,7 @@ UPNP_GetListOfPortMappings(const char * controlURL,
}
/* IGD:2, functions for service WANIPv6FirewallControl:1 */
LIBSPEC int
MINIUPNP_LIBSPEC int
UPNP_GetFirewallStatus(const char * controlURL,
const char * servicetype,
int * firewallEnabled,
@ -792,7 +905,7 @@ UPNP_GetFirewallStatus(const char * controlURL,
return ret;
}
LIBSPEC int
MINIUPNP_LIBSPEC int
UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype,
const char * remoteHost,
const char * remotePort,
@ -847,7 +960,7 @@ UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype
return ret;
}
LIBSPEC int
MINIUPNP_LIBSPEC int
UPNP_AddPinhole(const char * controlURL, const char * servicetype,
const char * remoteHost,
const char * remotePort,
@ -926,7 +1039,7 @@ UPNP_AddPinhole(const char * controlURL, const char * servicetype,
return ret;
}
LIBSPEC int
MINIUPNP_LIBSPEC int
UPNP_UpdatePinhole(const char * controlURL, const char * servicetype,
const char * uniqueID,
const char * leaseTime)
@ -968,7 +1081,7 @@ UPNP_UpdatePinhole(const char * controlURL, const char * servicetype,
return ret;
}
LIBSPEC int
MINIUPNP_LIBSPEC int
UPNP_DeletePinhole(const char * controlURL, const char * servicetype, const char * uniqueID)
{
/*struct NameValueParserData pdata;*/
@ -1007,7 +1120,7 @@ UPNP_DeletePinhole(const char * controlURL, const char * servicetype, const char
return ret;
}
LIBSPEC int
MINIUPNP_LIBSPEC int
UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype,
const char * uniqueID, int * isWorking)
{
@ -1052,7 +1165,7 @@ UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype,
return ret;
}
LIBSPEC int
MINIUPNP_LIBSPEC int
UPNP_GetPinholePackets(const char * controlURL, const char * servicetype,
const char * uniqueID, int * packets)
{

View File

@ -17,24 +17,25 @@
#define UPNPCOMMAND_UNKNOWN_ERROR (-1)
#define UPNPCOMMAND_INVALID_ARGS (-2)
#define UPNPCOMMAND_HTTP_ERROR (-3)
#define UPNPCOMMAND_INVALID_RESPONSE (-4)
#ifdef __cplusplus
extern "C" {
#endif
LIBSPEC UNSIGNED_INTEGER
MINIUPNP_LIBSPEC UNSIGNED_INTEGER
UPNP_GetTotalBytesSent(const char * controlURL,
const char * servicetype);
LIBSPEC UNSIGNED_INTEGER
MINIUPNP_LIBSPEC UNSIGNED_INTEGER
UPNP_GetTotalBytesReceived(const char * controlURL,
const char * servicetype);
LIBSPEC UNSIGNED_INTEGER
MINIUPNP_LIBSPEC UNSIGNED_INTEGER
UPNP_GetTotalPacketsSent(const char * controlURL,
const char * servicetype);
LIBSPEC UNSIGNED_INTEGER
MINIUPNP_LIBSPEC UNSIGNED_INTEGER
UPNP_GetTotalPacketsReceived(const char * controlURL,
const char * servicetype);
@ -43,7 +44,7 @@ UPNP_GetTotalPacketsReceived(const char * controlURL,
* Return values :
* UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
* or a UPnP Error code */
LIBSPEC int
MINIUPNP_LIBSPEC int
UPNP_GetStatusInfo(const char * controlURL,
const char * servicetype,
char * status,
@ -55,7 +56,7 @@ UPNP_GetStatusInfo(const char * controlURL,
* Return Values :
* UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
* or a UPnP Error code */
LIBSPEC int
MINIUPNP_LIBSPEC int
UPNP_GetConnectionTypeInfo(const char * controlURL,
const char * servicetype,
char * connectionType);
@ -71,7 +72,7 @@ UPNP_GetConnectionTypeInfo(const char * controlURL,
* 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
MINIUPNP_LIBSPEC int
UPNP_GetExternalIPAddress(const char * controlURL,
const char * servicetype,
char * extIpAdd);
@ -82,7 +83,7 @@ UPNP_GetExternalIPAddress(const char * controlURL,
* return values :
* UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
* or a UPnP Error Code. */
LIBSPEC int
MINIUPNP_LIBSPEC int
UPNP_GetLinkLayerMaxBitRates(const char* controlURL,
const char* servicetype,
unsigned int * bitrateDown,
@ -121,15 +122,49 @@ UPNP_GetLinkLayerMaxBitRates(const char* controlURL,
* due to conflict with other mechanisms.
* 732 WildCardNotPermittedInIntPort - The internal port cannot be wild-carded
*/
LIBSPEC int
MINIUPNP_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,
const char * leaseDuration);
const char * extPort,
const char * inPort,
const char * inClient,
const char * desc,
const char * proto,
const char * remoteHost,
const char * leaseDuration);
/* UPNP_AddAnyPortMapping()
* 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.
* 606 Action not authorized - The action requested REQUIRES authorization and
* the sender was not authorized.
* 715 WildCardNotPermittedInSrcIP - The source IP address cannot be
* wild-carded
* 716 WildCardNotPermittedInExtPort - The external port cannot be wild-carded
* 728 NoPortMapsAvailable - There are not enough free ports available to
* complete port mapping.
* 729 ConflictWithOtherMechanisms - Attempted port mapping is not allowed
* due to conflict with other mechanisms.
* 732 WildCardNotPermittedInIntPort - The internal port cannot be wild-carded
*/
MINIUPNP_LIBSPEC int
UPNP_AddAnyPortMapping(const char * controlURL, const char * servicetype,
const char * extPort,
const char * inPort,
const char * inClient,
const char * desc,
const char * proto,
const char * remoteHost,
const char * leaseDuration,
char * reservedPort);
/* UPNP_DeletePortMapping()
* Use same argument values as what was used for AddPortMapping().
@ -143,14 +178,33 @@ UPNP_AddPortMapping(const char * controlURL, const char * servicetype,
* 606 Action not authorized - The action requested REQUIRES authorization
* and the sender was not authorized.
* 714 NoSuchEntryInArray - The specified value does not exist in the array */
LIBSPEC int
MINIUPNP_LIBSPEC int
UPNP_DeletePortMapping(const char * controlURL, const char * servicetype,
const char * extPort, const char * proto,
const char * remoteHost);
const char * extPort, const char * proto,
const char * remoteHost);
/* UPNP_DeletePortRangeMapping()
* 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 :
* 606 Action not authorized - The action requested REQUIRES authorization
* and the sender was not authorized.
* 730 PortMappingNotFound - This error message is returned if no port
* mapping is found in the specified range.
* 733 InconsistentParameters - NewStartPort and NewEndPort values are not consistent. */
MINIUPNP_LIBSPEC int
UPNP_DeletePortMappingRange(const char * controlURL, const char * servicetype,
const char * extPortStart, const char * extPortEnd,
const char * proto,
const char * manage);
/* UPNP_GetPortMappingNumberOfEntries()
* not supported by all routers */
LIBSPEC int
MINIUPNP_LIBSPEC int
UPNP_GetPortMappingNumberOfEntries(const char* controlURL,
const char* servicetype,
unsigned int * num);
@ -178,7 +232,7 @@ UPNP_GetPortMappingNumberOfEntries(const char* controlURL,
* and the sender was not authorized.
* 714 NoSuchEntryInArray - The specified value does not exist in the array.
*/
LIBSPEC int
MINIUPNP_LIBSPEC int
UPNP_GetSpecificPortMappingEntry(const char * controlURL,
const char * servicetype,
const char * extPort,
@ -212,7 +266,7 @@ UPNP_GetSpecificPortMappingEntry(const char * controlURL,
* and the sender was not authorized.
* 713 SpecifiedArrayIndexInvalid - The specified array index is out of bounds
*/
LIBSPEC int
MINIUPNP_LIBSPEC int
UPNP_GetGenericPortMappingEntry(const char * controlURL,
const char * servicetype,
const char * index,
@ -234,7 +288,7 @@ UPNP_GetGenericPortMappingEntry(const char * controlURL,
* 733 InconsistantParameters - NewStartPort and NewEndPort values are not
* consistent.
*/
LIBSPEC int
MINIUPNP_LIBSPEC int
UPNP_GetListOfPortMappings(const char * controlURL,
const char * servicetype,
const char * startPort,
@ -244,13 +298,13 @@ UPNP_GetListOfPortMappings(const char * controlURL,
struct PortMappingParserData * data);
/* IGD:2, functions for service WANIPv6FirewallControl:1 */
LIBSPEC int
MINIUPNP_LIBSPEC int
UPNP_GetFirewallStatus(const char * controlURL,
const char * servicetype,
int * firewallEnabled,
int * inboundPinholeAllowed);
LIBSPEC int
MINIUPNP_LIBSPEC int
UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype,
const char * remoteHost,
const char * remotePort,
@ -259,7 +313,7 @@ UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype
const char * proto,
int * opTimeout);
LIBSPEC int
MINIUPNP_LIBSPEC int
UPNP_AddPinhole(const char * controlURL, const char * servicetype,
const char * remoteHost,
const char * remotePort,
@ -269,19 +323,19 @@ UPNP_AddPinhole(const char * controlURL, const char * servicetype,
const char * leaseTime,
char * uniqueID);
LIBSPEC int
MINIUPNP_LIBSPEC int
UPNP_UpdatePinhole(const char * controlURL, const char * servicetype,
const char * uniqueID,
const char * leaseTime);
LIBSPEC int
MINIUPNP_LIBSPEC int
UPNP_DeletePinhole(const char * controlURL, const char * servicetype, const char * uniqueID);
LIBSPEC int
MINIUPNP_LIBSPEC int
UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype,
const char * uniqueID, int * isWorking);
LIBSPEC int
MINIUPNP_LIBSPEC int
UPNP_GetPinholePackets(const char * controlURL, const char * servicetype,
const char * uniqueID, int * packets);

View File

@ -24,6 +24,9 @@ const char * strupnperror(int err)
case UPNPCOMMAND_INVALID_ARGS:
s = "Miniupnpc Invalid Arguments";
break;
case UPNPCOMMAND_INVALID_RESPONSE:
s = "Miniupnpc Invalid response";
break;
case UPNPDISCOVER_SOCKET_ERROR:
s = "Miniupnpc Socket error";
break;

View File

@ -17,7 +17,7 @@ extern "C" {
/* strupnperror()
* Return a string description of the UPnP error code
* or NULL for undefinded errors */
LIBSPEC const char * strupnperror(int err);
MINIUPNP_LIBSPEC const char * strupnperror(int err);
#ifdef __cplusplus
}

View File

@ -1,7 +1,7 @@
/* $Id: upnpreplyparse.c,v 1.15 2013/06/06 21:36:40 nanard Exp $ */
/* $Id: upnpreplyparse.c,v 1.17 2014/11/04 22:25:48 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2013 Thomas Bernard
* (c) 2006-2014 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
@ -53,7 +53,8 @@ NameValueParserEndElt(void * d, const char * name, int l)
{
nv->value[0] = '\0';
}
LIST_INSERT_HEAD( &(data->head), nv, entries);
nv->l_next = data->l_head; /* insert in list */
data->l_head = nv;
}
data->cdata = NULL;
data->cdatalen = 0;
@ -89,19 +90,19 @@ void
ParseNameValue(const char * buffer, int bufsize,
struct NameValueParserData * data)
{
struct xmlparser parser;
LIST_INIT(&(data->head));
struct xmlparser parser;
data->l_head = NULL;
data->portListing = NULL;
data->portListingLength = 0;
/* init xmlparser object */
parser.xmlstart = buffer;
parser.xmlsize = bufsize;
parser.data = data;
parser.starteltfunc = NameValueParserStartElt;
parser.endeltfunc = NameValueParserEndElt;
parser.datafunc = NameValueParserGetData;
/* init xmlparser object */
parser.xmlstart = buffer;
parser.xmlsize = bufsize;
parser.data = data;
parser.starteltfunc = NameValueParserStartElt;
parser.endeltfunc = NameValueParserEndElt;
parser.datafunc = NameValueParserGetData;
parser.attfunc = 0;
parsexml(&parser);
parsexml(&parser);
}
void
@ -114,9 +115,9 @@ ClearNameValueList(struct NameValueParserData * pdata)
pdata->portListing = NULL;
pdata->portListingLength = 0;
}
while((nv = pdata->head.lh_first) != NULL)
while((nv = pdata->l_head) != NULL)
{
LIST_REMOVE(nv, entries);
pdata->l_head = nv->l_next;
free(nv);
}
}
@ -127,9 +128,9 @@ GetValueFromNameValueList(struct NameValueParserData * pdata,
{
struct NameValue * nv;
char * p = NULL;
for(nv = pdata->head.lh_first;
for(nv = pdata->l_head;
(nv != NULL) && (p == NULL);
nv = nv->entries.le_next)
nv = nv->l_next)
{
if(strcmp(nv->name, Name) == 0)
p = nv->value;
@ -171,9 +172,9 @@ DisplayNameValueList(char * buffer, int bufsize)
struct NameValueParserData pdata;
struct NameValue * nv;
ParseNameValue(buffer, bufsize, &pdata);
for(nv = pdata.head.lh_first;
for(nv = pdata.l_head;
nv != NULL;
nv = nv->entries.le_next)
nv = nv->l_next)
{
printf("%s = %s\n", nv->name, nv->value);
}

View File

@ -1,4 +1,4 @@
/* $Id: upnpreplyparse.h,v 1.17 2013/06/06 21:36:40 nanard Exp $ */
/* $Id: upnpreplyparse.h,v 1.19 2014/10/27 16:33:19 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2013 Thomas Bernard
@ -8,25 +8,19 @@
#ifndef UPNPREPLYPARSE_H_INCLUDED
#define UPNPREPLYPARSE_H_INCLUDED
#if defined(NO_SYS_QUEUE_H) || defined(_WIN32) || defined(__HAIKU__)
#include "bsdqueue.h"
#else
#include <sys/queue.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
struct NameValue {
LIST_ENTRY(NameValue) entries;
char name[64];
char value[128];
struct NameValue * l_next;
char name[64];
char value[128];
};
struct NameValueParserData {
LIST_HEAD(listhead, NameValue) head;
char curelt[64];
struct NameValue * l_head;
char curelt[64];
char * portListing;
int portListingLength;
int topelt;

View File

@ -1,4 +1,60 @@
$Id: Changelog.txt,v 1.366 2014/03/24 11:03:50 nanard Exp $
$Id: Changelog.txt,v 1.383 2014/11/07 11:53:37 nanard Exp $
2014/11/07:
sockaddr_to_string() includes scope in IPv6 addresses
VERSION 1.9 : released on 2014/10/27
2014/10/23:
Properly implements NAT-PMP mapping removal according to RCF6886
2014/10/22:
Discard NAT-PMP packets coming from the WAN
Send SSDP announces to IPv6 link-local, site-local
and global multicast addresses
2014/10/21:
small modifications to compile with exotic C libraries
2014/10/14:
add comments in miniupnpd.conf regarding security
2014/09/25:
DeletePortMapping now checks for client IP in Securemode
2014/06/xx:
Various fixes :
e->ipv6.flags |= IP6T_F_PROTO; (netfilter)
fix natpmp.c byte order conversion
add small delay before SSDP response to prevent flooding
2014/05/22:
Add ipv6_bind_address (option "ipv6_listening_ip")
disable IPv6 when socket(PF_INTET6, ...) errors with EAFNOSUPPORT
Add IPV6 multicast membership only on selected "LAN" interfaces
2014/05/20:
be more strict when parsing LAN addresses / interface names
2014/05/19:
set source address for IPV6 packets sendto_schedule2() etc.
2014/05/15:
Fix deletePortMappingRange()
2014/04/21:
Fix PCP when request contain 0 IPv4 external address
Remove pointer casting in natpmp.c
2014/04/15:
rewrite iptables_*.sh scripts
2014/04/12:
Add FreeBSD support for CHECK_PORTINUSE
Add PCP support for CHECK_PORTINUSE
2014/04/09:
Add HTTPS support and skeleton of DeviceProtection implementation
2014/03/24:
start work to enable IPv6 PCP operations

View File

@ -1,4 +1,4 @@
# $Id: Makefile,v 1.80 2014/04/07 10:32:20 nanard Exp $
# $Id: Makefile,v 1.83 2014/04/18 08:24:41 nanard Exp $
# MiniUPnP project
# http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
# Author: Thomas Bernard
@ -34,32 +34,48 @@ STRIP = strip
OSNAME != uname -s
ARCH != uname -m
.ifndef FWNAME
.if exists(/usr/include/net/pfvar.h)
#.if exists(/usr/include/net/pfvar.h)
#FWNAME = pf
#.else
#FWNAME = ipf
#.endif
.if $(OSNAME) == "OpenBSD"
FWNAME = pf
.else
FWNAME = ipf
.endif
# better way to find if we are using ipf or pf
.if exists(/etc/rc.subr) && exists(/etc/rc.conf)
.if $(OSNAME) == "FreeBSD"
.if exists(/etc/rc.subr) && exists(/etc/rc.conf)
FWNAME != . /etc/rc.subr; . /etc/rc.conf; \
if checkyesno ipfilter_enable; then \
echo "ipf"; elif checkyesno pf_enable; then \
echo "pf"; else echo "ipfw"; fi
echo "pf"; elif checkyesno firewall_enable; then \
echo "ipfw"; else echo "pf"; fi
.else
FWNAME = pf
.endif
.endif
.if $(OSNAME) == "NetBSD"
.if exists(/etc/rc.subr) && exists(/etc/rc.conf)
FWNAME != . /etc/rc.subr; . /etc/rc.conf; \
if checkyesno ipfilter; then \
if checkyesno pf; then \
echo "pf"; elif checkyesno ipfilter; then \
echo "ipf"; else echo "pf"; fi
.else
FWNAME = pf
.endif
.endif
.if $(OSNAME) == "DragonFly"
.if exists(/etc/rc.subr) && exists(/etc/rc.conf)
FWNAME != . /etc/rc.subr; . /etc/rc.conf; \
if checkyesno ipfilter; then \
echo "ipf"; elif checkyesno pf_enable; then \
echo "pf"; else echo "ipfw"; fi
if checkyesno pf; then \
echo "pf"; elif checkyesno ipfilter; then \
echo "ipf"; else echo "pf"; fi
.else
FWNAME = pf
.endif
.endif
@ -138,6 +154,8 @@ LIBS = -lkvm
LIBS += -lsocket -lnsl -lkstat -lresolv
.endif
LIBS += -lssl -lcrypto
# set PREFIX variable to install in the wanted place
INSTALLBINDIR = $(PREFIX)/sbin
@ -213,7 +231,7 @@ testasyncsendto: config.h $(TESTASYNCSENDTOOBJS)
$(CC) $(CFLAGS) -o $@ $(TESTASYNCSENDTOOBJS)
testportinuse: config.h $(TESTPORTINUSEOBJS)
$(CC) $(CFLAGS) -o $@ $(TESTPORTINUSEOBJS) -lkvm
$(CC) $(CFLAGS) -o $@ $(TESTPORTINUSEOBJS) $(LIBS)
# gmake :
# $(CC) $(CFLAGS) -o $@ $^

View File

@ -1,4 +1,4 @@
# $Id: Makefile.linux,v 1.84 2014/03/24 10:43:25 nanard Exp $
# $Id: Makefile.linux,v 1.86 2014/04/09 07:22:28 nanard Exp $
# MiniUPnP project
# (c) 2006-2014 Thomas Bernard
# http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
@ -25,7 +25,7 @@
CFLAGS ?= -Os
CFLAGS += -fno-strict-aliasing
CFLAGS += -fno-common
CFLAGS += -D_GNU_SOURCE
CPPFLAGS += -D_GNU_SOURCE
CFLAGS += -Wall
CFLAGS += -Wextra -Wstrict-prototypes -Wdeclaration-after-statement
#CFLAGS += -Wno-missing-field-initializers
@ -34,6 +34,7 @@ CC ?= gcc
RM = rm -f
INSTALL = install
STRIP ?= strip
PKG_CONFIG ?= pkg-config
CP = cp
@ -54,39 +55,39 @@ NETFILTEROBJS = netfilter/iptcrdr.o netfilter/iptpinhole.o netfilter/nfct_get.o
ALLOBJS = $(BASEOBJS) $(LNXOBJS) $(NETFILTEROBJS)
PCFILE_FOUND := $(shell pkg-config --exists libiptc; echo $$?)
PCFILE_FOUND := $(shell $(PKG_CONFIG) --exists libiptc; echo $$?)
ifeq (${PCFILE_FOUND},0)
IPTABLESVERSION := $(shell pkg-config --modversion libiptc)
IPTABLESVERSION := $(shell $(PKG_CONFIG) --modversion libiptc)
IPTABLESVERSION1 := $(shell echo $(IPTABLESVERSION) | cut -d. -f1 )
IPTABLESVERSION2 := $(shell echo $(IPTABLESVERSION) | cut -d. -f2 )
IPTABLESVERSION3 := $(shell echo $(IPTABLESVERSION) | cut -d. -f3 )
# test if iptables version >= 1.4.3
TEST := $(shell [ \( \( $(IPTABLESVERSION1) -ge 1 \) -a \( $(IPTABLESVERSION2) -ge 4 \) \) -a \( $(IPTABLESVERSION3) -ge 3 \) ] && echo 1 )
ifeq ($(TEST), 1)
CFLAGS += -DIPTABLES_143
CPPFLAGS += -DIPTABLES_143
endif
CFLAGS += $(shell pkg-config --cflags libiptc)
LIBS += $(shell pkg-config --static --libs-only-l libiptc)
LDFLAGS += $(shell pkg-config --libs-only-L libiptc)
LDFLAGS += $(shell pkg-config --libs-only-other libiptc)
CFLAGS += $(shell $(PKG_CONFIG) --cflags libiptc)
LDLIBS += $(shell $(PKG_CONFIG) --static --libs-only-l libiptc)
LDFLAGS += $(shell $(PKG_CONFIG) --libs-only-L libiptc)
LDFLAGS += $(shell $(PKG_CONFIG) --libs-only-other libiptc)
else
ifeq "$(wildcard /etc/gentoo-release )" ""
LIBS ?= -liptc
LDLIBS ?= -liptc
else # gentoo
# the following is better, at least on gentoo with iptables 1.4.6
# see http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=1618
# and http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=2183
LIBS ?= -lip4tc
CFLAGS := -DIPTABLES_143 $(CFLAGS)
LDLIBS ?= -lip4tc
CPPFLAGS := -DIPTABLES_143 $(CPPFLAGS)
endif
ARCH ?= $(shell uname -m | grep -q "x86_64" && echo 64)
ifdef IPTABLESPATH
CFLAGS := $(CFLAGS) -I$(IPTABLESPATH)/include/
CPPFLAGS := $(CPPFLAGS) -I$(IPTABLESPATH)/include/
LDFLAGS := $(LDFLAFGS) -L$(IPTABLESPATH)/libiptc/
# get iptables version and set IPTABLES_143 macro if needed
ifeq ($(TARGET_OPENWRT),)
@ -97,12 +98,12 @@ IPTABLESVERSION3 := $(shell echo $(IPTABLESVERSION) | cut -d. -f3 )
# test if iptables version >= 1.4.3
TEST := $(shell [ \( \( $(IPTABLESVERSION1) -ge 1 \) -a \( $(IPTABLESVERSION2) -ge 4 \) \) -a \( $(IPTABLESVERSION3) -ge 3 \) ] && echo 1 )
ifeq ($(TEST), 1)
CFLAGS := $(CFLAGS) -DIPTABLES_143
CPPFLAGS := $(CPPFLAGS) -DIPTABLES_143
# the following sucks, but works
LIBS = $(IPTABLESPATH)/libiptc/.libs/libip4tc.o
#LIBS = $(IPTABLESPATH)/libiptc/.libs/libiptc.a
LDLIBS = $(IPTABLESPATH)/libiptc/.libs/libip4tc.o
#LDLIBS = $(IPTABLESPATH)/libiptc/.libs/libiptc.a
else # ifeq ($(TEST), 1)
LIBS = $(IPTABLESPATH)/libiptc/libiptc.a
LDLIBS = $(IPTABLESPATH)/libiptc/libiptc.a
endif # ifeq ($(TEST), 1)
else # ($(TARGET_OPENWRT),)
# openWRT :
@ -110,12 +111,12 @@ else # ($(TARGET_OPENWRT),)
# the following test has to be verified :
TEST := $(shell test -f /usr/include/iptables/internal.h && grep -q "\#define IPTABLES_VERSION" /usr/include/iptables/internal.h && echo 1)
ifeq ($(TEST), 1)
CFLAGS := $(CFLAGS) -DIPTABLES_143
LIBS = -liptc
CPPFLAGS := $(CPPFLAGS) -DIPTABLES_143
LDLIBS = -liptc
endif # ($(TEST), 1)
TEST_LIB := $(shell test -f /usr/lib$(ARCH)/libiptc.a && echo 1)
ifeq ($(TEST_LIB), 1)
LIBS = -liptc /usr/lib$(ARCH)/libiptc.a
LDLIBS = -liptc /usr/lib$(ARCH)/libiptc.a
endif # ($(TEST_LIB), 1)
endif # ($(TARGET_OPENWRT),)
else # ifdef IPTABLESPATH
@ -123,29 +124,31 @@ else # ifdef IPTABLESPATH
# the following test has to be verified :
TEST := $(shell test -f /usr/include/xtables.h && grep -q "XTABLES_VERSION_CODE" /usr/include/xtables.h && echo 1)
ifeq ($(TEST), 1)
CFLAGS := $(CFLAGS) -DIPTABLES_143
LIBS = -liptc
CPPFLAGS := $(CPPFLAGS) -DIPTABLES_143
LDLIBS = -liptc
TESTIP4TC := $(shell test -f /lib/libip4tc.so && echo 1)
ifeq ($(TESTIP4TC), 1)
LIBS := $(LIBS) -lip4tc
LDLIBS := $(LDLIBS) -lip4tc
endif # ($(TESTIP4TC), 1)
TESTIP6TC := $(shell test -f /lib/libip6tc.so && echo 1)
ifeq ($(TESTIP6TC), 1)
LIBS := $(LIBS) -lip6tc
LDLIBS := $(LDLIBS) -lip6tc
endif # ($(TESTIP6TC), 1)
endif # ($(TEST), 1)
endif # ifdef IPTABLESPATH
endif # ifdef PCFILE_FOUND
LIBS += -lnfnetlink
LDLIBS += -lnfnetlink
TEST := $(shell pkg-config --atleast-version=1.0.2 libnetfilter_conntrack && pkg-config --atleast-version=1.0.3 libmnl && echo 1)
TEST := $(shell $(PKG_CONFIG) --atleast-version=1.0.2 libnetfilter_conntrack && $(PKG_CONFIG) --atleast-version=1.0.3 libmnl && echo 1)
ifeq ($(TEST),1)
CFLAGS += -DUSE_NFCT
LIBS += $(shell pkg-config --static --libs-only-l libmnl)
LIBS += $(shell pkg-config --static --libs-only-l libnetfilter_conntrack)
CPPFLAGS += -DUSE_NFCT
LDLIBS += $(shell $(PKG_CONFIG) --static --libs-only-l libmnl)
LDLIBS += $(shell $(PKG_CONFIG) --static --libs-only-l libnetfilter_conntrack)
endif # ($(TEST),1)
LDLIBS += $(shell $(PKG_CONFIG) --static --libs-only-l libssl)
TESTUPNPDESCGENOBJS = testupnpdescgen.o upnpdescgen.o
EXECUTABLES = miniupnpd testupnpdescgen testgetifstats \
@ -181,7 +184,7 @@ install: miniupnpd miniupnpd.8 miniupnpd.conf genuuid \
$(INSTALL) linux/miniupnpd.init.d.script $(DESTDIR)$(PREFIX)/etc/init.d/miniupnpd
$(INSTALL) -d $(DESTDIR)$(MANINSTALLDIR)
$(INSTALL) --mode=0644 miniupnpd.8 $(DESTDIR)$(MANINSTALLDIR)
gzip $(DESTDIR)$(MANINSTALLDIR)/miniupnpd.8
gzip -f $(DESTDIR)$(MANINSTALLDIR)/miniupnpd.8
# genuuid is using the uuidgen CLI tool which is part of libuuid
# from the e2fsprogs
@ -193,7 +196,7 @@ else
sed -i -e "s/^uuid=[-0-9a-f]*/uuid=`($(STAGING_DIR_HOST)/bin/genuuid||$(STAGING_DIR_HOST)/bin/uuidgen||$(STAGING_DIR_HOST)/bin/uuid) 2>/dev/null`/" miniupnpd.conf
endif
miniupnpd: $(BASEOBJS) $(LNXOBJS) $(NETFILTEROBJS) $(LIBS)
miniupnpd: $(BASEOBJS) $(LNXOBJS) $(NETFILTEROBJS)
testupnpdescgen: $(TESTUPNPDESCGENOBJS)
@ -203,13 +206,13 @@ testupnppermissions: testupnppermissions.o upnppermissions.o
testgetifaddr: testgetifaddr.o getifaddr.o
testgetroute: testgetroute.o linux/getroute.o upnputils.o -lnfnetlink
testgetroute: testgetroute.o linux/getroute.o upnputils.o
testasyncsendto: testasyncsendto.o asyncsendto.o upnputils.o \
linux/getroute.o -lnfnetlink
linux/getroute.o
testportinuse: testportinuse.o portinuse.o getifaddr.o \
netfilter/iptcrdr.o $(LIBS)
netfilter/iptcrdr.o
miniupnpdctl: miniupnpdctl.o

View File

@ -5,7 +5,7 @@
#
# To compile with pf with OS X 10.7+, you need to specify
# path to XNU bsd sources :
# INCLUDES="-I.../xnu/bsd I.../xnu/libkern" make -f Makefile.macosx
# INCLUDES="-I.../xnu/bsd -I.../xnu/libkern" make -f Makefile.macosx
#
# To install use :
# $ PREFIX=/dummyinstalldir make -f Makefile.macosx install
@ -20,8 +20,6 @@ MV = mv
INSTALL = install
STRIP = strip
CFLAGS += -DMACOSX
# OSNAME and FWNAME are used for building OS or FW dependent code.
OSNAME = $(shell uname)
ARCH = $(shell uname -p)
@ -33,10 +31,11 @@ STD_OBJS = miniupnpd.o upnphttp.o upnpdescgen.o upnpsoap.o \
upnpredirect.o getifaddr.o daemonize.o upnpglobalvars.o \
options.o upnppermissions.o minissdp.o natpmp.o \
upnpevents.o getconnstatus.o upnputils.o \
asyncsendto.o portinuse.o pcpserver.o
MAC_OBJS = mac/getifstats.o bsd/ifacewatcher.o
upnppinhole.o asyncsendto.o portinuse.o pcpserver.o
MAC_OBJS = mac/getifstats.o bsd/ifacewatcher.o bsd/getroute.o
IPFW_OBJS = ipfw/ipfwrdr.o ipfw/ipfwaux.o
PF_OBJS = pf/obsdrdr.o pf/pfpinhole.o
PF_OBJS = pf/obsdrdr.o
# pf/pfpinhole.o # SHOULD be used, but doesn't compile on e.g. OS X 10.9.
MISC_OBJS = upnpreplyparse.o minixml.o
ALL_OBJS = $(STD_OBJS) $(MISC_OBJS) $(MAC_OBJS)
@ -52,7 +51,7 @@ TEST_GETIFSTATS_OBJS = testgetifstats.o mac/getifstats.o
TEST_UPNPPERMISSIONS_OBJS = testupnppermissions.o upnppermissions.o
TEST_GETIFADDR_OBJS = testgetifaddr.o getifaddr.o
TEST_PORTINUSE_OBJS = testportinuse.o portinuse.o getifaddr.o
TEST_ASYNCSENDTO_OBJS = testasyncsendto.o asyncsendto.o upnputils.o
TEST_ASYNCSENDTO_OBJS = testasyncsendto.o asyncsendto.o upnputils.o bsd/getroute.o
MINIUPNPDCTL_OBJS = miniupnpdctl.o
EXECUTABLES = miniupnpd testupnpdescgen testgetifstats \

View File

@ -2,17 +2,9 @@
=> Done (ifacewatcher 2011/05)
To be improved.
- improve logging.
- add support for IPv6 thanks to http://www.upnp.org/download/Annex%20A%20-%20IPv6.doc
=> work in progress
- send also NOTIFY packets in IPv6 => done (2012/04/06)
- make minissdpd know IPv6 also... => done
- improve NAT-PMP compliance
=> to be checked
- Clean Option parsing/processing.
=> done 2012/02/05
- Option to hide the windows Interconnection icon and pop-up - something to do with optionally having a blank presentation_url
Done (2009/02/13)
- Tomato's version used to dynamically include the local LAN IP and WAN IP as the windows icon name - if it was displayed...
- I just enabled my lease file to save the port mappings so I don't have to recreate them every time miniupnp gets restarted but instead what happens is the lease file gets recreated and the leases erased.
@ -27,10 +19,4 @@ support IGD v2 : http://upnp.org/specs/gw/igd2/
- netfilter : ok ?
- ipfw/ipf : TODO
use non blocking sockets everywhere :
- HTTP => OK
- SSDP => OK
- NAT-PMP => OK
- not needed for miniupnpdctl
implement port_in_use() for NetBSD and FreeBSD
implement port_in_use() for NetBSD

View File

@ -1 +1 @@
1.8
1.9

View File

@ -1,4 +1,4 @@
/* $Id: $ */
/* $Id: asyncsendto.c,v 1.6 2014/05/19 14:26:56 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2014 Thomas Bernard
@ -13,6 +13,8 @@
#include <string.h>
#include <syslog.h>
#include <errno.h>
#include <sys/uio.h>
#include <netinet/in.h>
#include "asyncsendto.h"
#include "upnputils.h"
@ -36,6 +38,7 @@ struct scheduled_send {
int flags;
const struct sockaddr *dest_addr;
socklen_t addrlen;
const struct sockaddr_in6 *src_addr;
char data[];
};
@ -45,21 +48,60 @@ static LIST_HEAD(listhead, scheduled_send) send_list = { NULL };
* ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
* const struct sockaddr *dest_addr, socklen_t addrlen);
*/
static ssize_t
send_from_to(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr_in6 *src_addr,
const struct sockaddr *dest_addr, socklen_t addrlen)
{
#ifdef IPV6_PKTINFO
if(src_addr) {
struct iovec iov;
struct in6_pktinfo ipi6;
uint8_t c[CMSG_SPACE(sizeof(ipi6))];
struct msghdr msg;
struct cmsghdr* cmsg;
iov.iov_base = (void *)buf;
iov.iov_len = len;
memset(&msg, 0, sizeof(msg));
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
ipi6.ipi6_addr = src_addr->sin6_addr;
ipi6.ipi6_ifindex = src_addr->sin6_scope_id;
msg.msg_control = c;
msg.msg_controllen = sizeof(c);
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = IPPROTO_IPV6;
cmsg->cmsg_type = IPV6_PKTINFO;
cmsg->cmsg_len = CMSG_LEN(sizeof(ipi6));
memcpy(CMSG_DATA(cmsg), &ipi6, sizeof(ipi6));
msg.msg_name = (void *)dest_addr;
msg.msg_namelen = addrlen;
return sendmsg(sockfd, &msg, flags);
} else {
#endif /* IPV6_PKTINFO */
return sendto(sockfd, buf, len, flags, dest_addr, addrlen);
#ifdef IPV6_PKTINFO
}
#endif /* IPV6_PKTINFO */
}
/* delay = milli seconds */
ssize_t
sendto_schedule(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen,
unsigned int delay)
sendto_schedule2(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen,
const struct sockaddr_in6 *src_addr,
unsigned int delay)
{
enum {ESCHEDULED, EWAITREADY, ESENDNOW} state;
ssize_t n;
size_t alloc_len;
struct timeval tv;
struct scheduled_send * elt;
if(delay == 0) {
/* first try to send at once */
n = sendto(sockfd, buf, len, flags, dest_addr, addrlen);
n = send_from_to(sockfd, buf, len, flags, src_addr, dest_addr, addrlen);
if(n >= 0)
return n;
else if(errno == EAGAIN || errno == EWOULDBLOCK) {
@ -80,10 +122,13 @@ sendto_schedule(int sockfd, const void *buf, size_t len, int flags,
return -1;
}
/* allocate enough space for structure + buffers */
elt = malloc(sizeof(struct scheduled_send) + len + addrlen);
alloc_len = sizeof(struct scheduled_send) + len + addrlen;
if(src_addr)
alloc_len += sizeof(struct sockaddr_in6);
elt = malloc(alloc_len);
if(elt == NULL) {
syslog(LOG_ERR, "malloc failed to allocate %u bytes",
(unsigned)(sizeof(struct scheduled_send) + len + addrlen));
(unsigned)alloc_len);
return -1;
}
elt->state = state;
@ -99,21 +144,35 @@ sendto_schedule(int sockfd, const void *buf, size_t len, int flags,
memcpy(elt->data, dest_addr, addrlen);
elt->dest_addr = (struct sockaddr *)elt->data;
elt->addrlen = addrlen;
memcpy(elt->data + addrlen, buf, len);
elt->buf = (void *)(elt->data + addrlen);
if(src_addr) {
elt->src_addr = (struct sockaddr_in6 *)(elt->data + addrlen);
memcpy((void *)elt->src_addr, src_addr, sizeof(struct sockaddr_in6));
elt->buf = (void *)(elt->data + addrlen + sizeof(struct sockaddr_in6));
} else {
elt->src_addr = NULL;
elt->buf = (void *)(elt->data + addrlen);
}
elt->len = len;
memcpy((void *)elt->buf, buf, len);
/* insert */
LIST_INSERT_HEAD( &send_list, elt, entries);
return 0;
}
/* try to send at once, and queue the packet if needed */
ssize_t
sendto_or_schedule(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen)
{
return sendto_schedule(sockfd, buf, len, flags, dest_addr, addrlen, 0);
return sendto_schedule2(sockfd, buf, len, flags, dest_addr, addrlen, NULL, 0);
}
ssize_t
sendto_or_schedule2(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen,
const struct sockaddr_in6 *src_addr)
{
return sendto_schedule2(sockfd, buf, len, flags, dest_addr, addrlen, src_addr, 0);
}
/* get_next_scheduled_send() return number of scheduled send in list */
@ -168,10 +227,14 @@ int try_sendto(fd_set * writefds)
next = elt->entries.le_next;
if((elt->state == ESENDNOW) ||
(elt->state == EWAITREADY && FD_ISSET(elt->sockfd, writefds))) {
#ifdef DEBUG
syslog(LOG_DEBUG, "%s: %d bytes on socket %d",
"try_sendto", (int)elt->len, elt->sockfd);
n = sendto(elt->sockfd, elt->buf, elt->len, elt->flags,
elt->dest_addr, elt->addrlen);
#endif
n = send_from_to(elt->sockfd, elt->buf, elt->len, elt->flags,
elt->src_addr, elt->dest_addr, elt->addrlen);
/*n = sendto(elt->sockfd, elt->buf, elt->len, elt->flags,
elt->dest_addr, elt->addrlen);*/
if(n < 0) {
if(errno == EINTR) {
/* retry at once */
@ -234,8 +297,10 @@ void finalize_sendto(void)
next = elt->entries.le_next;
syslog(LOG_DEBUG, "finalize_sendto(): %d bytes on socket %d",
(int)elt->len, elt->sockfd);
n = sendto(elt->sockfd, elt->buf, elt->len, elt->flags,
elt->dest_addr, elt->addrlen);
n = send_from_to(elt->sockfd, elt->buf, elt->len, elt->flags,
elt->src_addr, elt->dest_addr, elt->addrlen);
/*n = sendto(elt->sockfd, elt->buf, elt->len, elt->flags,
elt->dest_addr, elt->addrlen);*/
if(n < 0) {
if(errno==EAGAIN || errno==EWOULDBLOCK) {
FD_SET(elt->sockfd, &writefds);

View File

@ -11,9 +11,13 @@
/* sendto_schedule() : see sendto(2)
* schedule sendto() call after delay (milliseconds) */
ssize_t
sendto_schedule(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen,
unsigned int delay);
sendto_schedule2(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen,
const struct sockaddr_in6 *src_addr,
unsigned int delay);
#define sendto_schedule(sockfd, buf, len, flags, dest_addr, addrlen, delay) \
sendto_schedule2(sockfd, buf, len, flags, dest_addr, addrlen, NULL, delay)
/* sendto_schedule() : see sendto(2)
* try sendto() at once and schedule if EINTR/EAGAIN/EWOULDBLOCK */
@ -21,6 +25,13 @@ ssize_t
sendto_or_schedule(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
/* same as sendto_schedule() except it will try to set source address
* (for IPV6 only) */
ssize_t
sendto_or_schedule2(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen,
const struct sockaddr_in6 *src_addr);
/* get_next_scheduled_send()
* return number of scheduled sendto
* set next_send to timestamp to send next packet */

View File

@ -1,61 +1,32 @@
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* author: Ryan Wagoner and Thomas Bernard
* author: Gleb Smirnoff <glebius@FreeBSD.org>
* (c) 2006 Ryan Wagoner
* (c) 2014 Gleb Smirnoff
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#include <syslog.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#if defined(__FreeBSD__) || defined(__DragonFly__)
#ifdef __DragonFly__
#define _KERNEL_STRUCTURES
#endif
#include <net/if_var.h>
#endif
#if defined(__DragonFly__)
#include <net/pf/pfvar.h>
#else
#include <net/pfvar.h>
#endif
#include <kvm.h>
#include <fcntl.h>
#include <nlist.h>
#include <sys/queue.h>
#include <stdio.h>
#include <errno.h>
#include <ifaddrs.h>
#include <string.h>
#include <limits.h>
#include <syslog.h>
#ifdef ENABLE_GETIFSTATS_CACHING
#include <time.h>
#endif
#include "../getifstats.h"
#include "../config.h"
static struct nlist list[] = {
{"_ifnet", 0, 0, 0, 0},
{NULL,0, 0, 0, 0}
};
int
getifstats(const char * ifname, struct ifdata * data)
getifstats(const char *ifname, struct ifdata *data)
{
#if defined(__FreeBSD__) || defined(__DragonFly__)
struct ifnethead ifh;
#elif defined(__OpenBSD__) || defined(__NetBSD__)
struct ifnet_head ifh;
#else
#error "Dont know if I should use struct ifnethead or struct ifnet_head"
#endif
struct ifnet ifc;
struct ifnet *ifp;
kvm_t *kd;
ssize_t n;
char errstr[_POSIX2_LINE_MAX];
static struct ifaddrs *ifap, *ifa;
#ifdef ENABLE_GETIFSTATS_CACHING
static time_t cache_timestamp = 0;
static struct ifdata cache_data;
static time_t cache_timestamp;
time_t current_time;
#endif
if(!data)
@ -67,70 +38,40 @@ getifstats(const char * ifname, struct ifdata * data)
data->ibytes = 0;
if(!ifname || ifname[0]=='\0')
return -1;
#ifdef ENABLE_GETIFSTATS_CACHING
current_time = time(NULL);
if(current_time == ((time_t)-1)) {
syslog(LOG_ERR, "getifstats() : time() error : %m");
} else {
if(current_time < cache_timestamp + GETIFSTATS_CACHING_DURATION) {
memcpy(data, &cache_data, sizeof(struct ifdata));
return 0;
}
}
if (ifap != NULL &&
current_time < cache_timestamp + GETIFSTATS_CACHING_DURATION)
goto copy;
#endif
/*kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL);*/
kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errstr);
if(!kd)
{
syslog (LOG_ERR, "getifstats() : kvm_open(): %s", errstr);
return -1;
if (ifap != NULL) {
freeifaddrs(ifap);
ifap = NULL;
}
if(kvm_nlist(kd, list) < 0)
{
syslog(LOG_ERR, "getifstats() : kvm_nlist(): FAILED");
goto error;
}
if(!list[0].n_value)
{
syslog(LOG_ERR, "getifstats() : n_value(): FAILED");
goto error;
}
n = kvm_read(kd, list[0].n_value, &ifh, sizeof(ifh));
if(n<0)
{
syslog(LOG_ERR, "getifstats() : kvm_read(head): %s", kvm_geterr(kd));
goto error;
}
for(ifp = TAILQ_FIRST(&ifh); ifp; ifp = TAILQ_NEXT(&ifc, if_list))
{
n = kvm_read(kd, (u_long)ifp, &ifc, sizeof(ifc));
if(n<0)
{
syslog(LOG_ERR, "getifstats() : kvm_read(element): %s", kvm_geterr(kd));
goto error;
}
if(strcmp(ifname, ifc.if_xname) == 0)
{
/* found the right interface */
data->opackets = ifc.if_data.ifi_opackets;
data->ipackets = ifc.if_data.ifi_ipackets;
data->obytes = ifc.if_data.ifi_obytes;
data->ibytes = ifc.if_data.ifi_ibytes;
data->baudrate = ifc.if_data.ifi_baudrate;
kvm_close(kd);
if (getifaddrs(&ifap) != 0) {
syslog (LOG_ERR, "getifstats() : getifaddrs(): %s",
strerror(errno));
return (-1);
}
for (ifa = ifap; ifa; ifa = ifa->ifa_next)
if (ifa->ifa_addr->sa_family == AF_LINK &&
strcmp(ifa->ifa_name, ifname) == 0) {
#ifdef ENABLE_GETIFSTATS_CACHING
if(current_time!=((time_t)-1)) {
cache_timestamp = current_time;
memcpy(&cache_data, data, sizeof(struct ifdata));
}
cache_timestamp = current_time;
copy:
#endif
return 0; /* ok */
#define IFA_STAT(s) (((struct if_data *)ifa->ifa_data)->ifi_ ## s)
data->opackets = IFA_STAT(opackets);
data->ipackets = IFA_STAT(ipackets);
data->obytes = IFA_STAT(obytes);
data->ibytes = IFA_STAT(ibytes);
data->baudrate = IFA_STAT(baudrate);
return (0);
}
}
error:
kvm_close(kd);
return -1; /* not found or error */
}
return (-1);
}

View File

@ -1,4 +1,4 @@
/* $Id: ifacewatcher.c,v 1.6 2014/03/31 12:27:14 nanard Exp $ */
/* $Id: ifacewatcher.c,v 1.8 2014/04/18 08:23:51 nanard Exp $ */
/* Project MiniUPnP
* web : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2011 Thomas BERNARD
@ -73,6 +73,18 @@ ProcessInterfaceWatchNotify(int s)
syslog(LOG_DEBUG, " RTM_IFINFO: addrs=%x flags=%x index=%hu",
ifm->ifm_addrs, ifm->ifm_flags, ifm->ifm_index);
break;
case RTM_ADD: /* Add Route */
syslog(LOG_DEBUG, " RTM_ADD");
break;
case RTM_DELETE: /* Delete Route */
syslog(LOG_DEBUG, " RTM_DELETE");
break;
case RTM_CHANGE: /* Change Metrics or flags */
syslog(LOG_DEBUG, " RTM_CHANGE");
break;
case RTM_GET: /* Report Metrics */
syslog(LOG_DEBUG, " RTM_GET");
break;
#ifdef RTM_IFANNOUNCE
case RTM_IFANNOUNCE: /* iface arrival/departure */
ifanm = (struct if_announcemsghdr *)buf;

View File

@ -1,5 +1,5 @@
#! /bin/sh
# $Id: genconfig.sh,v 1.72 2014/03/10 10:17:17 nanard Exp $
# $Id: genconfig.sh,v 1.74 2014/04/09 07:21:00 nanard Exp $
# miniupnp daemon
# http://miniupnp.free.fr or http://miniupnp.tuxfamily.org/
# (c) 2006-2014 Thomas Bernard
@ -443,6 +443,9 @@ echo "" >> ${CONFIGFILE}
echo "#ifdef IGD_V2" >> ${CONFIGFILE}
echo "/* Enable DeviceProtection service (IGDv2) */" >> ${CONFIGFILE}
echo "#define ENABLE_DP_SERVICE" >> ${CONFIGFILE}
echo "/*#define ENABLE_HTTPS*/" >> ${CONFIGFILE}
echo "/*#define HTTPS_CERTFILE \"/path/to/certificate.pem\"*/" >> ${CONFIGFILE}
echo "/*#define HTTPS_KEYFILE \"/path/to/private.key\"*/" >> ${CONFIGFILE}
echo "" >> ${CONFIGFILE}
echo "/* Enable WANIPv6FirewallControl service (IGDv2). needs IPv6 */" >> ${CONFIGFILE}
echo "#ifdef ENABLE_IPV6" >> ${CONFIGFILE}
@ -507,6 +510,13 @@ else
fi
echo "" >> ${CONFIGFILE}
cat >> ${CONFIGFILE} <<EOF
#if defined(ENABLE_6FC_SERVICE) || (defined(ENABLE_PCP) && defined(ENABLE_IPV6))
#define ENABLE_UPNPPINHOLE
#endif
EOF
echo "#endif /* ${CONFIGMACRO} */" >> ${CONFIGFILE}
${MV} ${CONFIGFILE} ${CONFIGFILE_FINAL}

View File

@ -138,9 +138,8 @@ getifaddr(const char * ifname, char * buf, int len,
}
#ifdef ENABLE_PCP
/* XXX I don't know if this function should return
* IPv4 or IPv6 if both are enabled... */
int getifaddr_in6(const char * ifname, struct in6_addr * addr)
int getifaddr_in6(const char * ifname, int af, struct in6_addr * addr)
{
#if defined(ENABLE_IPV6) || defined(USE_GETIFADDRS)
struct ifaddrs * ifap;
@ -164,6 +163,8 @@ int getifaddr_in6(const char * ifname, struct in6_addr * addr)
continue;
if(ife->ifa_addr == NULL)
continue;
if (ife->ifa_addr->sa_family != af)
continue;
switch(ife->ifa_addr->sa_family)
{
case AF_INET:
@ -197,6 +198,8 @@ int getifaddr_in6(const char * ifname, struct in6_addr * addr)
#else /* defined(ENABLE_IPV6) || defined(USE_GETIFADDRS) */
/* IPv4 only */
struct in_addr addr4;
if(af != AF_INET)
return -1;
if(getifaddr(ifname, NULL, 0, &addr4, NULL) < 0)
return -1;
/* IPv4-mapped IPv6 address ::ffff:1.2.3.4 */

View File

@ -20,7 +20,7 @@ getifaddr(const char * ifname, char * buf, int len,
struct in_addr * addr, struct in_addr * mask);
int
getifaddr_in6(const char * ifname, struct in6_addr* addr);
getifaddr_in6(const char * ifname, int af, struct in6_addr* addr);
/* find a non link local IP v6 address for the interface.
* if ifname is NULL, look for all interfaces */

View File

@ -1,4 +1,4 @@
/* $Id: minissdp.c,v 1.62 2014/03/24 09:31:23 nanard Exp $ */
/* $Id: minissdp.c,v 1.72 2014/10/22 11:54:45 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2014 Thomas Bernard
@ -26,12 +26,22 @@
#include "getroute.h"
#include "asyncsendto.h"
#include "codelength.h"
#include "macros.h"
/* SSDP ip/port */
#define SSDP_PORT (1900)
#define SSDP_MCAST_ADDR ("239.255.255.250")
#define LL_SSDP_MCAST_ADDR "FF02::C"
#define SL_SSDP_MCAST_ADDR "FF05::C"
#define GL_SSDP_MCAST_ADDR "FF0E::C"
/* maximum lenght of SSDP packets we are generating
* (reception is done in a 1500byte buffer) */
#ifdef ENABLE_HTTPS
#define SSDP_PACKET_MAX_LEN 768
#else
#define SSDP_PACKET_MAX_LEN 512
#endif
/* AddMulticastMembership()
* param s socket
@ -58,21 +68,19 @@ AddMulticastMembership(int s, in_addr_t ifaddr)
/* AddMulticastMembershipIPv6()
* param s socket (IPv6)
* To be improved to target specific network interfaces */
* param ifindex : interface index (0 : All interfaces) */
#ifdef ENABLE_IPV6
static int
AddMulticastMembershipIPv6(int s)
AddMulticastMembershipIPv6(int s, unsigned int ifindex)
{
struct ipv6_mreq mr;
/*unsigned int ifindex;*/
memset(&mr, 0, sizeof(mr));
inet_pton(AF_INET6, LL_SSDP_MCAST_ADDR, &mr.ipv6mr_multiaddr);
/*mr.ipv6mr_interface = ifindex;*/
mr.ipv6mr_interface = 0; /* 0 : all interfaces */
mr.ipv6mr_interface = ifindex; /* 0 : all interfaces */
#ifndef IPV6_ADD_MEMBERSHIP
#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
#endif
inet_pton(AF_INET6, LL_SSDP_MCAST_ADDR, &mr.ipv6mr_multiaddr);
if(setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mr, sizeof(struct ipv6_mreq)) < 0)
{
syslog(LOG_ERR, "setsockopt(udp, IPV6_ADD_MEMBERSHIP): %m");
@ -84,6 +92,12 @@ AddMulticastMembershipIPv6(int s)
syslog(LOG_ERR, "setsockopt(udp, IPV6_ADD_MEMBERSHIP): %m");
return -1;
}
inet_pton(AF_INET6, GL_SSDP_MCAST_ADDR, &mr.ipv6mr_multiaddr);
if(setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mr, sizeof(struct ipv6_mreq)) < 0)
{
syslog(LOG_ERR, "setsockopt(udp, IPV6_ADD_MEMBERSHIP): %m");
return -1;
}
return 0;
}
#endif
@ -108,13 +122,18 @@ OpenAndConfSSDPReceiveSocket(int ipv6)
}
memset(&sockname, 0, sizeof(struct sockaddr_storage));
if(ipv6) {
#ifdef ENABLE_IPV6
if(ipv6)
{
struct sockaddr_in6 * saddr = (struct sockaddr_in6 *)&sockname;
saddr->sin6_family = AF_INET6;
saddr->sin6_port = htons(SSDP_PORT);
saddr->sin6_addr = in6addr_any;
saddr->sin6_addr = ipv6_bind_addr;
sockname_len = sizeof(struct sockaddr_in6);
} else {
}
else
#endif /* ENABLE_IPV6 */
{
struct sockaddr_in * saddr = (struct sockaddr_in *)&sockname;
saddr->sin_family = AF_INET;
saddr->sin_port = htons(SSDP_PORT);
@ -147,10 +166,14 @@ OpenAndConfSSDPReceiveSocket(int ipv6)
#ifdef ENABLE_IPV6
if(ipv6)
{
if(AddMulticastMembershipIPv6(s) < 0)
for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next)
{
syslog(LOG_WARNING,
"Failed to add IPv6 multicast membership");
if(AddMulticastMembershipIPv6(s, lan_addr->index) < 0)
{
syslog(LOG_WARNING,
"Failed to add IPv6 multicast membership for interface %s",
lan_addr->str ? lan_addr->str : "NULL");
}
}
}
else
@ -219,6 +242,10 @@ OpenAndConfSSDPNotifySocket(in_addr_t addr)
return -1;
}
/* bind() socket before using sendto() is not mandatory
* (sendto() will implicitly bind the socket when called on
* an unbound socket)
* here it is used to se a specific sending address */
memset(&sockname, 0, sizeof(struct sockaddr_in));
sockname.sin_family = AF_INET;
sockname.sin_addr.s_addr = addr; /*inet_addr(addr);*/
@ -241,6 +268,7 @@ OpenAndConfSSDPNotifySocketIPv6(unsigned int if_index)
{
int s;
unsigned int loop = 0;
struct sockaddr_in6 sockname;
s = socket(PF_INET6, SOCK_DGRAM, 0);
if(s < 0)
@ -260,6 +288,23 @@ OpenAndConfSSDPNotifySocketIPv6(unsigned int if_index)
close(s);
return -1;
}
/* bind() socket before using sendto() is not mandatory
* (sendto() will implicitly bind the socket when called on
* an unbound socket)
* but explicit bind permits to set port/scope_id/etc. */
memset(&sockname, 0, sizeof(sockname));
sockname.sin6_family = AF_INET6;
sockname.sin6_addr = in6addr_any;
/*sockname.sin6_port = htons(port);*/
/*sockname.sin6_scope_id = if_index;*/
if(bind(s, (struct sockaddr *)&sockname, sizeof(sockname)) < 0)
{
syslog(LOG_ERR, "bind(udp_notify IPv6): %m");
close(s);
return -1;
}
return s;
}
#endif
@ -336,11 +381,14 @@ EXT:
static void
SendSSDPResponse(int s, const struct sockaddr * addr,
const char * st, int st_len, const char * suffix,
const char * host, unsigned short port, const char * uuidvalue,
unsigned int delay)
const char * host, unsigned short http_port,
#ifdef ENABLE_HTTPS
unsigned short https_port,
#endif
const char * uuidvalue, unsigned int delay)
{
int l, n;
char buf[512];
char buf[SSDP_PACKET_MAX_LEN];
char addr_str[64];
socklen_t addrlen;
int st_is_uuid;
@ -375,6 +423,9 @@ SendSSDPResponse(int s, const struct sockaddr * addr,
"EXT:\r\n"
"SERVER: " MINIUPNPD_SERVER_STRING "\r\n"
"LOCATION: http://%s:%u" ROOTDESC_PATH "\r\n"
#ifdef ENABLE_HTTPS
"SECURELOCATION.UPNP.ORG: https://%s:%u" ROOTDESC_PATH "\r\n"
#endif
"OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */
"01-NLS: %u\r\n" /* same as BOOTID. UDA v1.1 */
"BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
@ -386,7 +437,10 @@ SendSSDPResponse(int s, const struct sockaddr * addr,
st_len, st, suffix,
uuidvalue, st_is_uuid ? "" : "::",
st_is_uuid ? 0 : st_len, st, suffix,
host, (unsigned int)port,
host, (unsigned int)http_port,
#ifdef ENABLE_HTTPS
host, (unsigned int)https_port,
#endif
upnp_bootid, upnp_bootid, upnp_configid);
if(l<0)
{
@ -396,8 +450,8 @@ SendSSDPResponse(int s, const struct sockaddr * addr,
}
else if((unsigned)l>=sizeof(buf))
{
syslog(LOG_WARNING, "%s: truncated output",
"SendSSDPResponse()");
syslog(LOG_WARNING, "%s: truncated output (%u>=%u)",
"SendSSDPResponse()", (unsigned)l, (unsigned)sizeof(buf));
l = sizeof(buf) - 1;
}
addrlen = (addr->sa_family == AF_INET6)
@ -447,24 +501,36 @@ static struct {
#ifdef ENABLE_6FC_SERVICE
{"url:schemas-upnp-org:service:WANIPv6FirewallControl:", 1, uuidvalue_wcd},
#endif
/* we might want to support urn:schemas-wifialliance-org:device:WFADevice:1
* urn:schemas-wifialliance-org:device:WFADevice:1
* in the future */
{0, 0, 0}
};
/* SendSSDPNotify() sends the SSDP NOTIFY to a specific
* destination, for a specific UPnP service or device */
static void
SendSSDPNotify(int s, const struct sockaddr * dest,
const char * host, unsigned short port,
SendSSDPNotify(int s, const struct sockaddr * dest, socklen_t dest_len,
const char * dest_str,
const char * host, unsigned short http_port,
#ifdef ENABLE_HTTPS
unsigned short https_port,
#endif
const char * nt, const char * suffix,
const char * usn1, const char * usn2, const char * usn3,
unsigned int lifetime, int ipv6)
unsigned int lifetime)
{
char bufr[512];
char bufr[SSDP_PACKET_MAX_LEN];
int n, l;
l = snprintf(bufr, sizeof(bufr),
"NOTIFY * HTTP/1.1\r\n"
"HOST: %s:%d\r\n"
"CACHE-CONTROL: max-age=%u\r\n"
"LOCATION: http://%s:%d" ROOTDESC_PATH"\r\n"
"LOCATION: http://%s:%u" ROOTDESC_PATH "\r\n"
#ifdef ENABLE_HTTPS
"SECURELOCATION.UPNP.ORG: https://%s:%u" ROOTDESC_PATH "\r\n"
#endif
"SERVER: " MINIUPNPD_SERVER_STRING "\r\n"
"NT: %s%s\r\n"
"USN: %s%s%s%s\r\n"
@ -474,139 +540,182 @@ SendSSDPNotify(int s, const struct sockaddr * dest,
"BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
"CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
"\r\n",
ipv6 ? "[" LL_SSDP_MCAST_ADDR "]" : SSDP_MCAST_ADDR,
SSDP_PORT,
lifetime,
host, port,
nt, suffix, /* NT: */
usn1, usn2, usn3, suffix, /* USN: */
upnp_bootid, upnp_bootid, upnp_configid );
if(l<0)
{
syslog(LOG_ERR, "SendSSDPNotify() snprintf error");
dest_str, SSDP_PORT, /* HOST: */
lifetime, /* CACHE-CONTROL: */
host, (unsigned int)http_port, /* LOCATION: */
#ifdef ENABLE_HTTPS
host, (unsigned int)https_port, /* SECURE-LOCATION: */
#endif
nt, suffix, /* NT: */
usn1, usn2, usn3, suffix, /* USN: */
upnp_bootid, /* 01-NLS: */
upnp_bootid, /* BOOTID.UPNP.ORG: */
upnp_configid ); /* CONFIGID.UPNP.ORG: */
if(l<0) {
syslog(LOG_ERR, "%s: snprintf error", "SendSSDPNotify()");
return;
}
else if((unsigned int)l >= sizeof(bufr))
{
syslog(LOG_WARNING, "SendSSDPNotify(): truncated output");
} else if((unsigned int)l >= sizeof(bufr)) {
syslog(LOG_WARNING, "%s: truncated output (%u>=%u)",
"SendSSDPNotify()", (unsigned)l, (unsigned)sizeof(bufr));
l = sizeof(bufr) - 1;
}
n = sendto_or_schedule(s, bufr, l, 0, dest,
#ifdef ENABLE_IPV6
ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)
#else
sizeof(struct sockaddr_in)
#endif
);
if(n < 0)
{
n = sendto_or_schedule(s, bufr, l, 0, dest, dest_len);
if(n < 0) {
syslog(LOG_ERR, "sendto(udp_notify=%d, %s): %m", s,
host ? host : "NULL");
}
else if(n != l)
{
} else if(n != l) {
syslog(LOG_NOTICE, "sendto() sent %d out of %d bytes", n, l);
}
/* Due to the unreliable nature of UDP, devices SHOULD send the entire
* set of discovery messages more than once with some delay between
* sets e.g. a few hundred milliseconds. To avoid network congestion
* discovery messages SHOULD NOT be sent more than three times. */
n = sendto_schedule(s, bufr, l, 0, dest,
#ifdef ENABLE_IPV6
ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in),
#else
sizeof(struct sockaddr_in),
#endif
250);
if(n < 0)
{
n = sendto_schedule(s, bufr, l, 0, dest, dest_len, 250);
if(n < 0) {
syslog(LOG_ERR, "sendto(udp_notify=%d, %s): %m", s,
host ? host : "NULL");
}
}
/* SendSSDPNotifies() send SSPD NOTIFY for a specific
* LAN (network interface) for all devices / services */
#ifdef ENABLE_HTTPS
static void
SendSSDPNotifies(int s, const char * host, unsigned short port,
SendSSDPNotifies(int s, const char * host, unsigned short http_port,
unsigned short https_port,
unsigned int lifetime, int ipv6)
#else
static void
SendSSDPNotifies(int s, const char * host, unsigned short http_port,
unsigned int lifetime, int ipv6)
#endif
{
#ifdef ENABLE_IPV6
struct sockaddr_storage sockname;
#else
static struct { const char * p1, * p2; } const mcast_addrs[] =
{ { LL_SSDP_MCAST_ADDR, "[" LL_SSDP_MCAST_ADDR "]" }, /* Link Local */
{ SL_SSDP_MCAST_ADDR, "[" SL_SSDP_MCAST_ADDR "]" }, /* Site Local */
{ GL_SSDP_MCAST_ADDR, "[" GL_SSDP_MCAST_ADDR "]" }, /* Global */
{ NULL, NULL } };
int j;
#else /* ENABLE_IPV6 */
struct sockaddr_in sockname;
#endif
int i=0;
#endif /* ENABLE_IPV6 */
socklen_t sockname_len;
const char * dest_str;
int i;
char ver_str[4];
#ifndef ENABLE_IPV6
UNUSED(ipv6);
#endif /* ENABLE_IPV6 */
memset(&sockname, 0, sizeof(sockname));
#ifdef ENABLE_IPV6
if(ipv6)
{
struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockname;
p->sin6_family = AF_INET6;
p->sin6_port = htons(SSDP_PORT);
inet_pton(AF_INET6, LL_SSDP_MCAST_ADDR, &(p->sin6_addr));
}
else
#endif
{
struct sockaddr_in *p = (struct sockaddr_in *)&sockname;
p->sin_family = AF_INET;
p->sin_port = htons(SSDP_PORT);
p->sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
}
while(known_service_types[i].s)
{
if(i==0)
ver_str[0] = '\0';
else
snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version);
SendSSDPNotify(s, (struct sockaddr *)&sockname, host, port,
known_service_types[i].s, ver_str, /* NT: */
known_service_types[i].uuid, "::",
known_service_types[i].s, /* ver_str, USN: */
lifetime, ipv6);
if(0==memcmp(known_service_types[i].s,
"urn:schemas-upnp-org:device", sizeof("urn:schemas-upnp-org:device")-1))
/* first iterate destinations for this LAN interface (only 1 for IPv4) */
for(j = 0; (mcast_addrs[j].p1 != 0 && ipv6) || j < 1; j++) {
if(ipv6) {
struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockname;
sockname_len = sizeof(struct sockaddr_in6);
p->sin6_family = AF_INET6;
p->sin6_port = htons(SSDP_PORT);
inet_pton(AF_INET6, mcast_addrs[j].p1, &(p->sin6_addr));
dest_str = mcast_addrs[j].p2;
/* UPnP Device Architecture 1.1 :
* Devices MUST multicast SSDP messages for each of the UPnP-enabled
* interfaces. The scope of multicast SSDP messages MUST be
* link local FF02::C if the message is sent from a link local address.
* If the message is sent from a global address it MUST be multicast
* using either global scope FF0E::C or site local scope FF05::C.
* In networks with complex topologies and overlapping sites, use of
* global scope is RECOMMENDED. */
} else {
#else /* ENABLE_IPV6 */
{
SendSSDPNotify(s, (struct sockaddr *)&sockname, host, port,
known_service_types[i].uuid, "", /* NT: */
known_service_types[i].uuid, "", "", /* ver_str, USN: */
lifetime, ipv6);
#endif /* ENABLE_IPV6 */
/* IPv4 */
struct sockaddr_in *p = (struct sockaddr_in *)&sockname;
sockname_len = sizeof(struct sockaddr_in);
p->sin_family = AF_INET;
p->sin_port = htons(SSDP_PORT);
p->sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
dest_str = SSDP_MCAST_ADDR;
}
i++;
}
/* iterate all services / devices */
for(i = 0; known_service_types[i].s; i++) {
if(i==0)
ver_str[0] = '\0';
else
snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version);
SendSSDPNotify(s, (struct sockaddr *)&sockname, sockname_len, dest_str,
host, http_port,
#ifdef ENABLE_HTTPS
https_port,
#endif
known_service_types[i].s, ver_str, /* NT: */
known_service_types[i].uuid, "::",
known_service_types[i].s, /* ver_str, USN: */
lifetime);
/* for devices, also send NOTIFY on the uuid */
if(0==memcmp(known_service_types[i].s,
"urn:schemas-upnp-org:device", sizeof("urn:schemas-upnp-org:device")-1)) {
SendSSDPNotify(s, (struct sockaddr *)&sockname, sockname_len, dest_str,
host, http_port,
#ifdef ENABLE_HTTPS
https_port,
#endif
known_service_types[i].uuid, "", /* NT: */
known_service_types[i].uuid, "", "", /* ver_str, USN: */
lifetime);
}
} /* for(i = 0; known_service_types[i].s; i++) */
#ifdef ENABLE_IPV6
} /* for(j = 0; (mcast_addrs[j].p1 != 0 && ipv6) || j < 1; j++) */
#endif /* ENABLE_IPV6 */
}
/* SendSSDPNotifies2() sends SSDP NOTIFY packets on all interfaces
* for all destinations, all devices / services */
void
SendSSDPNotifies2(int * sockets,
unsigned short port,
unsigned short http_port,
#ifdef ENABLE_HTTPS
unsigned short https_port,
#endif
unsigned int lifetime)
{
int i;
struct lan_addr_s * lan_addr;
for(i=0, lan_addr = lan_addrs.lh_first;
for(i = 0, lan_addr = lan_addrs.lh_first;
lan_addr != NULL;
lan_addr = lan_addr->list.le_next)
{
SendSSDPNotifies(sockets[i], lan_addr->str, port,
lan_addr = lan_addr->list.le_next) {
SendSSDPNotifies(sockets[i], lan_addr->str, http_port,
#ifdef ENABLE_HTTPS
https_port,
#endif
lifetime, 0);
i++;
#ifdef ENABLE_IPV6
if(sockets[i] >= 0)
{
SendSSDPNotifies(sockets[i], ipv6_addr_for_http_with_brackets, port,
if(sockets[i] >= 0) {
SendSSDPNotifies(sockets[i], ipv6_addr_for_http_with_brackets, http_port,
#ifdef ENABLE_HTTPS
https_port,
#endif
lifetime, 1);
}
i++;
#endif
#endif /* ENABLE_IPV6 */
}
}
/* ProcessSSDPRequest()
* process SSDP M-SEARCH requests and responds to them */
void
ProcessSSDPRequest(int s, unsigned short port)
#ifdef ENABLE_HTTPS
ProcessSSDPRequest(int s, unsigned short http_port, unsigned short https_port)
#else
ProcessSSDPRequest(int s, unsigned short http_port)
#endif
{
int n;
char bufr[1500];
@ -633,13 +742,28 @@ ProcessSSDPRequest(int s, unsigned short port)
}
return;
}
ProcessSSDPData(s, bufr, n, (struct sockaddr *)&sendername, port);
#ifdef ENABLE_HTTPS
ProcessSSDPData(s, bufr, n, (struct sockaddr *)&sendername,
http_port, https_port);
#else
ProcessSSDPData(s, bufr, n, (struct sockaddr *)&sendername,
http_port);
#endif
}
#ifdef ENABLE_HTTPS
void
ProcessSSDPData(int s, const char *bufr, int n,
const struct sockaddr * sender, unsigned short port) {
const struct sockaddr * sender,
unsigned short http_port, unsigned short https_port)
#else
void
ProcessSSDPData(int s, const char *bufr, int n,
const struct sockaddr * sender,
unsigned short http_port)
#endif
{
int i, l;
struct lan_addr_s * lan_addr = NULL;
const char * st = NULL;
@ -656,7 +780,7 @@ ProcessSSDPData(int s, const char *bufr, int n,
#if defined(UPNP_STRICT) || defined(DELAY_MSEARCH_RESPONSE)
int mx_value = -1;
#endif
unsigned int delay = 0;
unsigned int delay = 50; /* Non-zero default delay to prevent flooding */
/* UPnP Device Architecture v1.1. 1.3.3 Search response :
* Devices responding to a multicast M-SEARCH SHOULD wait a random period
* of time between 0 seconds and the number of seconds specified in the
@ -847,7 +971,10 @@ ProcessSSDPData(int s, const char *bufr, int n,
#else
known_service_types[i].s, l, ver_str,
#endif
announced_host, port,
announced_host, http_port,
#ifdef ENABLE_HTTPS
https_port,
#endif
known_service_types[i].uuid,
delay);
break;
@ -873,7 +1000,10 @@ ProcessSSDPData(int s, const char *bufr, int n,
l = (int)strlen(known_service_types[i].s);
SendSSDPResponse(s, sender,
known_service_types[i].s, l, ver_str,
announced_host, port,
announced_host, http_port,
#ifdef ENABLE_HTTPS
https_port,
#endif
known_service_types[i].uuid,
delay);
}
@ -882,17 +1012,29 @@ ProcessSSDPData(int s, const char *bufr, int n,
delay += delay_increment;
#endif
SendSSDPResponse(s, sender, uuidvalue_igd, strlen(uuidvalue_igd), "",
announced_host, port, uuidvalue_igd, delay);
announced_host, http_port,
#ifdef ENABLE_HTTPS
https_port,
#endif
uuidvalue_igd, delay);
#ifdef DELAY_MSEARCH_RESPONSE
delay += delay_increment;
#endif
SendSSDPResponse(s, sender, uuidvalue_wan, strlen(uuidvalue_wan), "",
announced_host, port, uuidvalue_wan, delay);
announced_host, http_port,
#ifdef ENABLE_HTTPS
https_port,
#endif
uuidvalue_wan, delay);
#ifdef DELAY_MSEARCH_RESPONSE
delay += delay_increment;
#endif
SendSSDPResponse(s, sender, uuidvalue_wcd, strlen(uuidvalue_wcd), "",
announced_host, port, uuidvalue_wcd, delay);
announced_host, http_port,
#ifdef ENABLE_HTTPS
https_port,
#endif
uuidvalue_wcd, delay);
}
/* responds to request by UUID value */
l = (int)strlen(uuidvalue_igd);
@ -905,22 +1047,31 @@ ProcessSSDPData(int s, const char *bufr, int n,
{
syslog(LOG_INFO, "ssdp:uuid (IGD) found");
SendSSDPResponse(s, sender, st, st_len, "",
announced_host, port, uuidvalue_igd,
delay);
announced_host, http_port,
#ifdef ENABLE_HTTPS
https_port,
#endif
uuidvalue_igd, delay);
}
else if(0 == memcmp(st, uuidvalue_wan, l))
{
syslog(LOG_INFO, "ssdp:uuid (WAN) found");
SendSSDPResponse(s, sender, st, st_len, "",
announced_host, port, uuidvalue_wan,
delay);
announced_host, http_port,
#ifdef ENABLE_HTTPS
https_port,
#endif
uuidvalue_wan, delay);
}
else if(0 == memcmp(st, uuidvalue_wcd, l))
{
syslog(LOG_INFO, "ssdp:uuid (WCD) found");
SendSSDPResponse(s, sender, st, st_len, "",
announced_host, port, uuidvalue_wcd,
delay);
announced_host, http_port,
#ifdef ENABLE_HTTPS
https_port,
#endif
uuidvalue_wcd, delay);
}
}
}
@ -936,13 +1087,13 @@ ProcessSSDPData(int s, const char *bufr, int n,
}
static int
SendSSDPbyebye(int s, const struct sockaddr * dest,
SendSSDPbyebye(int s, const struct sockaddr * dest, socklen_t destlen,
const char * dest_str,
const char * nt, const char * suffix,
const char * usn1, const char * usn2, const char * usn3,
int ipv6)
const char * usn1, const char * usn2, const char * usn3)
{
int n, l;
char bufr[512];
char bufr[SSDP_PACKET_MAX_LEN];
l = snprintf(bufr, sizeof(bufr),
"NOTIFY * HTTP/1.1\r\n"
@ -955,28 +1106,22 @@ SendSSDPbyebye(int s, const struct sockaddr * dest,
"BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
"CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
"\r\n",
ipv6 ? "[" LL_SSDP_MCAST_ADDR "]" : SSDP_MCAST_ADDR,
SSDP_PORT,
nt, suffix, /* NT: */
dest_str, SSDP_PORT, /* HOST : */
nt, suffix, /* NT: */
usn1, usn2, usn3, suffix, /* USN: */
upnp_bootid, upnp_bootid, upnp_configid);
if(l<0)
{
syslog(LOG_ERR, "SendSSDPbyebye() snprintf error");
syslog(LOG_ERR, "%s: snprintf error", "SendSSDPbyebye()");
return -1;
}
else if((unsigned int)l >= sizeof(bufr))
{
syslog(LOG_WARNING, "SendSSDPbyebye(): truncated output");
syslog(LOG_WARNING, "%s: truncated output (%u>=%u)",
"SendSSDPbyebye()", (unsigned)l, (unsigned)sizeof(bufr));
l = sizeof(bufr) - 1;
}
n = sendto_or_schedule(s, bufr, l, 0, dest,
#ifdef ENABLE_IPV6
ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)
#else
sizeof(struct sockaddr_in)
#endif
);
n = sendto_or_schedule(s, bufr, l, 0, dest, destlen);
if(n < 0)
{
syslog(LOG_ERR, "sendto(udp_shutdown=%d): %m", s);
@ -995,30 +1140,44 @@ SendSSDPbyebye(int s, const struct sockaddr * dest,
int
SendSSDPGoodbye(int * sockets, int n_sockets)
{
struct sockaddr_in sockname;
struct sockaddr_in sockname4;
#ifdef ENABLE_IPV6
struct sockaddr_in6 sockname6;
struct sockaddr * sockname;
socklen_t socknamelen;
int ipv6 = 0;
#endif
int i, j;
char ver_str[4];
int ret = 0;
int ipv6 = 0;
const char * dest_str;
memset(&sockname, 0, sizeof(struct sockaddr_in));
sockname.sin_family = AF_INET;
sockname.sin_port = htons(SSDP_PORT);
sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
memset(&sockname4, 0, sizeof(struct sockaddr_in));
sockname4.sin_family = AF_INET;
sockname4.sin_port = htons(SSDP_PORT);
sockname4.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
#ifdef ENABLE_IPV6
memset(&sockname6, 0, sizeof(struct sockaddr_in6));
sockname6.sin6_family = AF_INET6;
sockname6.sin6_port = htons(SSDP_PORT);
inet_pton(AF_INET6, LL_SSDP_MCAST_ADDR, &(sockname6.sin6_addr));
#else
dest_str = SSDP_MCAST_ADDR;
#endif
for(j=0; j<n_sockets; j++)
{
#ifdef ENABLE_IPV6
ipv6 = j & 1;
if(ipv6) {
dest_str = "[" LL_SSDP_MCAST_ADDR "]";
sockname = (struct sockaddr *)&sockname6;
socknamelen = sizeof(struct sockaddr_in6);
} else {
dest_str = SSDP_MCAST_ADDR;
sockname = (struct sockaddr *)&sockname4;
socknamelen = sizeof(struct sockaddr_in);
}
#endif
for(i=0; known_service_types[i].s; i++)
{
@ -1028,26 +1187,26 @@ SendSSDPGoodbye(int * sockets, int n_sockets)
snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version);
ret += SendSSDPbyebye(sockets[j],
#ifdef ENABLE_IPV6
ipv6 ? (struct sockaddr *)&sockname6 : (struct sockaddr *)&sockname,
sockname, socknamelen,
#else
(struct sockaddr *)&sockname,
(struct sockaddr *)&sockname4, sizeof(struct sockaddr_in),
#endif
dest_str,
known_service_types[i].s, ver_str, /* NT: */
known_service_types[i].uuid, "::",
known_service_types[i].s, /* ver_str, USN: */
ipv6);
known_service_types[i].s); /* ver_str, USN: */
if(0==memcmp(known_service_types[i].s,
"urn:schemas-upnp-org:device", sizeof("urn:schemas-upnp-org:device")-1))
{
ret += SendSSDPbyebye(sockets[j],
#ifdef ENABLE_IPV6
ipv6 ? (struct sockaddr *)&sockname6 : (struct sockaddr *)&sockname,
sockname, socknamelen,
#else
(struct sockaddr *)&sockname,
(struct sockaddr *)&sockname4, sizeof(struct sockaddr_in),
#endif
dest_str,
known_service_types[i].uuid, "", /* NT: */
known_service_types[i].uuid, "", "", /* ver_str, USN: */
ipv6);
known_service_types[i].uuid, "", ""); /* ver_str, USN: */
}
}
}

View File

@ -1,7 +1,7 @@
/* $Id: minissdp.h,v 1.10 2011/05/23 12:39:41 nanard Exp $ */
/* $Id: minissdp.h,v 1.12 2014/04/09 07:20:59 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2007 Thomas Bernard
* (c) 2006-2014 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#ifndef MINISSDP_H_INCLUDED
@ -14,28 +14,39 @@ OpenAndConfSSDPReceiveSocket(int ipv6);
int
OpenAndConfSSDPNotifySockets(int * sockets);
/*OpenAndConfSSDPNotifySockets(int * sockets,
struct lan_addr_s * lan_addr, int n_lan_addr);*/
/*void
SendSSDPNotifies(int s, const char * host, unsigned short port,
unsigned int lifetime);*/
#ifdef ENABLE_HTTPS
void
SendSSDPNotifies2(int * sockets,
unsigned short port,
unsigned short http_port,
unsigned short https_port,
unsigned int lifetime);
/*SendSSDPNotifies2(int * sockets, struct lan_addr_s * lan_addr, int n_lan_addr,
unsigned short port,
unsigned int lifetime);*/
#else
void
SendSSDPNotifies2(int * sockets,
unsigned short http_port,
unsigned int lifetime);
#endif
void
ProcessSSDPRequest(int s, unsigned short port);
/*ProcessSSDPRequest(int s, struct lan_addr_s * lan_addr, int n_lan_addr,
unsigned short port);*/
#ifdef ENABLE_HTTPS
ProcessSSDPRequest(int s,
unsigned short http_port, unsigned short https_port);
#else
ProcessSSDPRequest(int s, unsigned short http_port);
#endif
#ifdef ENABLE_HTTPS
void
ProcessSSDPData(int s, const char *bufr, int n,
const struct sockaddr * sendername, unsigned short port);
const struct sockaddr * sendername,
unsigned short http_port, unsigned short https_port);
#else
void
ProcessSSDPData(int s, const char *bufr, int n,
const struct sockaddr * sendername,
unsigned short http_port);
#endif
int
SendSSDPGoodbye(int * sockets, int n);

View File

@ -1,4 +1,4 @@
/* $Id: miniupnpd.c,v 1.190 2014/03/24 10:49:44 nanard Exp $ */
/* $Id: miniupnpd.c,v 1.199 2014/05/19 23:14:25 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2014 Thomas Bernard
@ -39,7 +39,7 @@
#include <sys/param.h>
#if defined(sun)
#include <kstat.h>
#else
#elif !defined(__linux__)
/* for BSD's sysctl */
#include <sys/sysctl.h>
#endif
@ -77,7 +77,7 @@
#ifdef USE_IFACEWATCHER
#include "ifacewatcher.h"
#endif
#ifdef ENABLE_6FC_SERVICE
#ifdef ENABLE_UPNPPINHOLE
#ifdef USE_NETFILTER
void init_iptpinhole(void);
#endif
@ -113,9 +113,9 @@ volatile sig_atomic_t should_send_public_address_change_notif = 0;
* setup the socket used to handle incoming HTTP connections. */
static int
#ifdef ENABLE_IPV6
OpenAndConfHTTPSocket(unsigned short port, int ipv6)
OpenAndConfHTTPSocket(unsigned short * port, int ipv6)
#else
OpenAndConfHTTPSocket(unsigned short port)
OpenAndConfHTTPSocket(unsigned short * port)
#endif
{
int s;
@ -128,13 +128,24 @@ OpenAndConfHTTPSocket(unsigned short port)
#endif
socklen_t listenname_len;
if( (s = socket(
s = socket(
#ifdef ENABLE_IPV6
ipv6 ? PF_INET6 : PF_INET,
ipv6 ? PF_INET6 : PF_INET,
#else
PF_INET,
PF_INET,
#endif
SOCK_STREAM, 0)) < 0)
SOCK_STREAM, 0);
#ifdef ENABLE_IPV6
if(s < 0 && ipv6 && errno == EAFNOSUPPORT)
{
/* the system doesn't support IPV6 */
syslog(LOG_WARNING, "socket(PF_INET6, ...) failed with EAFNOSUPPORT, disabling IPv6");
SETFLAG(IPV6DISABLEDMASK);
ipv6 = 0;
s = socket(PF_INET, SOCK_STREAM, 0);
}
#endif
if(s < 0)
{
syslog(LOG_ERR, "socket(http): %m");
return -1;
@ -163,20 +174,20 @@ OpenAndConfHTTPSocket(unsigned short port)
{
memset(&listenname6, 0, sizeof(struct sockaddr_in6));
listenname6.sin6_family = AF_INET6;
listenname6.sin6_port = htons(port);
listenname6.sin6_addr = in6addr_any;
listenname6.sin6_port = htons(*port);
listenname6.sin6_addr = ipv6_bind_addr;
listenname_len = sizeof(struct sockaddr_in6);
} else {
memset(&listenname4, 0, sizeof(struct sockaddr_in));
listenname4.sin_family = AF_INET;
listenname4.sin_port = htons(port);
listenname4.sin_port = htons(*port);
listenname4.sin_addr.s_addr = htonl(INADDR_ANY);
listenname_len = sizeof(struct sockaddr_in);
}
#else
memset(&listenname, 0, sizeof(struct sockaddr_in));
listenname.sin_family = AF_INET;
listenname.sin_port = htons(port);
listenname.sin_port = htons(*port);
listenname.sin_addr.s_addr = htonl(INADDR_ANY);
listenname_len = sizeof(struct sockaddr_in);
#endif
@ -201,11 +212,34 @@ OpenAndConfHTTPSocket(unsigned short port)
return -1;
}
if(*port == 0) {
#ifdef ENABLE_IPV6
if(ipv6) {
struct sockaddr_in6 sockinfo;
socklen_t len = sizeof(struct sockaddr_in6);
if (getsockname(s, (struct sockaddr *)&sockinfo, &len) < 0) {
syslog(LOG_ERR, "getsockname(): %m");
} else {
*port = ntohs(sockinfo.sin6_port);
}
} else {
#endif /* ENABLE_IPV6 */
struct sockaddr_in sockinfo;
socklen_t len = sizeof(struct sockaddr_in);
if (getsockname(s, (struct sockaddr *)&sockinfo, &len) < 0) {
syslog(LOG_ERR, "getsockname(): %m");
} else {
*port = ntohs(sockinfo.sin_port);
}
#ifdef ENABLE_IPV6
}
#endif /* ENABLE_IPV6 */
}
return s;
}
static struct upnphttp *
ProcessIncomingHTTP(int shttpl)
ProcessIncomingHTTP(int shttpl, const char * protocol)
{
int shttp;
socklen_t clientnamelen;
@ -229,13 +263,13 @@ ProcessIncomingHTTP(int shttpl)
char addr_str[64];
sockaddr_to_string((struct sockaddr *)&clientname, addr_str, sizeof(addr_str));
syslog(LOG_INFO, "HTTP connection from %s", addr_str);
syslog(LOG_INFO, "%s connection from %s", protocol, addr_str);
if(get_lan_for_peer((struct sockaddr *)&clientname) == NULL)
{
/* The peer is not a LAN ! */
syslog(LOG_WARNING,
"HTTP peer %s is not from a LAN, closing the connection",
addr_str);
"%s peer %s is not from a LAN, closing the connection",
protocol, addr_str);
close(shttp);
}
else
@ -632,6 +666,9 @@ struct runtime_vars {
/* LAN IP addresses for SSDP traffic and HTTP */
/* moved to global vars */
int port; /* HTTP Port */
#ifdef ENABLE_HTTPS
int https_port; /* HTTPS Port */
#endif
int notify_interval; /* seconds between SSDP announces */
/* unused rules cleaning related variables : */
int clean_ruleset_threshold; /* threshold for removing unused rules */
@ -670,6 +707,7 @@ parselanaddr(struct lan_addr_s * lan_addr, const char * str)
if(getifaddr(lan_addr->ifname, lan_addr->str, sizeof(lan_addr->str),
&lan_addr->addr, &lan_addr->mask) < 0)
goto parselan_error;
/*printf("%s => %s\n", lan_addr->ifname, lan_addr->str);*/
}
else
{
@ -741,7 +779,10 @@ parselanaddr(struct lan_addr_s * lan_addr, const char * str)
}
else
{
fprintf(stderr, "Warning: please specify LAN network interface by name instead of IPv4 address\n");
fprintf(stderr,
"Error: please specify LAN network interface by name instead of IPv4 address : %s\n",
str);
return -1;
}
#endif
return 0;
@ -830,9 +871,15 @@ init(int argc, char * * argv, struct runtime_vars * v)
/* set initial values */
SETFLAG(ENABLEUPNPMASK); /* UPnP is enabled by default */
#ifdef ENABLE_IPV6
ipv6_bind_addr = in6addr_any;
#endif /* ENABLE_IPV6 */
LIST_INIT(&lan_addrs);
v->port = -1;
#ifdef ENABLE_HTTPS
v->https_port = -1;
#endif
v->notify_interval = 30; /* seconds between SSDP announces */
v->clean_ruleset_threshold = 20;
v->clean_ruleset_interval = 0; /* interval between ruleset check. 0=disabled */
@ -866,15 +913,32 @@ init(int argc, char * * argv, struct runtime_vars * v)
}
if(parselanaddr(lan_addr, ary_options[i].value) != 0)
{
fprintf(stderr, "can't parse \"%s\" as valid lan address\n", ary_options[i].value);
fprintf(stderr, "can't parse \"%s\" as a valid "
#ifndef ENABLE_IPV6
"LAN address or "
#endif
"interface name\n", ary_options[i].value);
free(lan_addr);
break;
}
LIST_INSERT_HEAD(&lan_addrs, lan_addr, list);
break;
#ifdef ENABLE_IPV6
case UPNPIPV6_LISTENING_IP:
if (inet_pton(AF_INET6, ary_options[i].value, &ipv6_bind_addr) < 1)
{
fprintf(stderr, "can't parse \"%s\" as valid IPv6 listening address", ary_options[i].value);
}
break;
#endif /* ENABLE_IPV6 */
case UPNPPORT:
v->port = atoi(ary_options[i].value);
break;
#ifdef ENABLE_HTTPS
case UPNPHTTPSPORT:
v->https_port = atoi(ary_options[i].value);
break;
#endif
case UPNPBITRATE_UP:
upstream_bitrate = strtoul(ary_options[i].value, 0, 0);
break;
@ -984,6 +1048,10 @@ init(int argc, char * * argv, struct runtime_vars * v)
max_lifetime = 86400;
}
break;
case UPNPPCPALLOWTHIRDPARTY:
if(strcmp(ary_options[i].value, "yes") == 0)
SETFLAG(PCP_ALLOWTHIRDPARTYMASK);
break;
#endif
#ifdef PF_ENABLE_FILTER_RULES
case UPNPQUICKRULES:
@ -1126,6 +1194,16 @@ init(int argc, char * * argv, struct runtime_vars * v)
if(i+1 < argc)
v->port = atoi(argv[++i]);
else
fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
break;
#ifdef ENABLE_HTTPS
case 'H':
if(i+1 < argc)
v->https_port = atoi(argv[++i]);
else
fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
break;
#endif
#ifdef ENABLE_NFQUEUE
case 'Q':
if(i+1<argc)
@ -1148,8 +1226,6 @@ init(int argc, char * * argv, struct runtime_vars * v)
}
break;
#endif
fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
break;
case 'P':
if(i+1 < argc)
pidfilename = argv[++i];
@ -1187,7 +1263,11 @@ init(int argc, char * * argv, struct runtime_vars * v)
}
if(parselanaddr(lan_addr, argv[i]) != 0)
{
fprintf(stderr, "can't parse \"%s\" as valid lan address\n", argv[i]);
fprintf(stderr, "can't parse \"%s\" as a valid "
#ifndef ENABLE_IPV6
"LAN address or "
#endif
"interface name\n", argv[i]);
free(lan_addr);
break;
}
@ -1222,7 +1302,7 @@ init(int argc, char * * argv, struct runtime_vars * v)
}
if(parselanaddr(lan_addr, val) != 0)
{
fprintf(stderr, "can't parse \"%s\" as valid lan address\n", val);
fprintf(stderr, "can't parse \"%s\" as a valid LAN address or interface name\n", val);
free(lan_addr);
free(val);
break;
@ -1358,7 +1438,7 @@ init(int argc, char * * argv, struct runtime_vars * v)
syslog(LOG_ERR, "Failed to init redirection engine. EXITING");
return 1;
}
#ifdef ENABLE_6FC_SERVICE
#ifdef ENABLE_UPNPPINHOLE
#ifdef USE_NETFILTER
init_iptpinhole();
#endif
@ -1385,6 +1465,9 @@ print_usage:
"\t\t[-a listening_ip]"
#else
"\t\t[-a listening_ip ext_ip]"
#endif
#ifdef ENABLE_HTTPS
" [-H https_port]"
#endif
" [-p port] [-d]"
#if defined(USE_PF) || defined(USE_IPF)
@ -1453,6 +1536,12 @@ main(int argc, char * * argv)
#if defined(V6SOCKETS_ARE_V6ONLY) && defined(ENABLE_IPV6)
int shttpl_v4 = -1; /* socket for HTTP (ipv4 only) */
#endif
#ifdef ENABLE_HTTPS
int shttpsl = -1; /* socket for HTTPS */
#if defined(V6SOCKETS_ARE_V6ONLY) && defined(ENABLE_IPV6)
int shttpsl_v4 = -1; /* socket for HTTPS (ipv4 only) */
#endif
#endif /* ENABLE_HTTPS */
int sudp = -1; /* IP v4 socket for receiving SSDP */
#ifdef ENABLE_IPV6
int sudpv6 = -1; /* IP v6 socket for receiving SSDP */
@ -1490,12 +1579,16 @@ main(int argc, char * * argv)
struct rule_state * rule_list = 0;
struct timeval checktime = {0, 0};
struct lan_addr_s * lan_addr;
#ifdef ENABLE_6FC_SERVICE
#ifdef ENABLE_UPNPPINHOLE
unsigned int next_pinhole_ts;
#endif
if(init(argc, argv, &v) != 0)
return 1;
#ifdef ENABLE_HTTPS
if(init_ssl() < 0)
return 1;
#endif /* ENABLE_HTTPS */
/* count lan addrs */
addr_count = 0;
for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next)
@ -1545,36 +1638,56 @@ main(int argc, char * * argv)
if(GETFLAG(ENABLEUPNPMASK))
{
unsigned short listen_port;
listen_port = (v.port > 0) ? v.port : 0;
/* open socket for HTTP connections. Listen on the 1st LAN address */
#ifdef ENABLE_IPV6
shttpl = OpenAndConfHTTPSocket((v.port > 0) ? v.port : 0, 1);
shttpl = OpenAndConfHTTPSocket(&listen_port, 1);
#else /* ENABLE_IPV6 */
shttpl = OpenAndConfHTTPSocket((v.port > 0) ? v.port : 0);
shttpl = OpenAndConfHTTPSocket(&listen_port);
#endif /* ENABLE_IPV6 */
if(shttpl < 0)
{
syslog(LOG_ERR, "Failed to open socket for HTTP. EXITING");
return 1;
}
if(v.port <= 0) {
struct sockaddr_in sockinfo;
socklen_t len = sizeof(struct sockaddr_in);
if (getsockname(shttpl, (struct sockaddr *)&sockinfo, &len) < 0) {
syslog(LOG_ERR, "getsockname(): %m");
return 1;
}
v.port = ntohs(sockinfo.sin_port);
}
v.port = listen_port;
syslog(LOG_NOTICE, "HTTP listening on port %d", v.port);
#if defined(V6SOCKETS_ARE_V6ONLY) && defined(ENABLE_IPV6)
shttpl_v4 = OpenAndConfHTTPSocket(v.port, 0);
if(shttpl_v4 < 0)
if(!GETFLAG(IPV6DISABLEDMASK))
{
syslog(LOG_ERR, "Failed to open socket for HTTP on port %hu (IPv4). EXITING", v.port);
shttpl_v4 = OpenAndConfHTTPSocket(&listen_port, 0);
if(shttpl_v4 < 0)
{
syslog(LOG_ERR, "Failed to open socket for HTTP on port %hu (IPv4). EXITING", v.port);
return 1;
}
}
#endif /* V6SOCKETS_ARE_V6ONLY */
#ifdef ENABLE_HTTPS
/* https */
listen_port = (v.https_port > 0) ? v.https_port : 0;
#ifdef ENABLE_IPV6
shttpsl = OpenAndConfHTTPSocket(&listen_port, 1);
#else /* ENABLE_IPV6 */
shttpsl = OpenAndConfHTTPSocket(&listen_port);
#endif /* ENABLE_IPV6 */
if(shttpl < 0)
{
syslog(LOG_ERR, "Failed to open socket for HTTPS. EXITING");
return 1;
}
v.https_port = listen_port;
syslog(LOG_NOTICE, "HTTPS listening on port %d", v.https_port);
#if defined(V6SOCKETS_ARE_V6ONLY) && defined(ENABLE_IPV6)
shttpsl_v4 = OpenAndConfHTTPSocket(&listen_port, 0);
if(shttpsl_v4 < 0)
{
syslog(LOG_ERR, "Failed to open socket for HTTPS on port %hu (IPv4). EXITING", v.https_port);
return 1;
}
#endif /* V6SOCKETS_ARE_V6ONLY */
#endif /* ENABLE_HTTPS */
#ifdef ENABLE_IPV6
if(find_ipv6_addr(NULL, ipv6_addr_for_http_with_brackets, sizeof(ipv6_addr_for_http_with_brackets)) > 0) {
syslog(LOG_NOTICE, "HTTP IPv6 address given to control points : %s",
@ -1712,6 +1825,9 @@ main(int argc, char * * argv)
if (GETFLAG(ENABLEUPNPMASK))
SendSSDPNotifies2(snotify,
(unsigned short)v.port,
#ifdef ENABLE_HTTPS
(unsigned short)v.https_port,
#endif
v.notify_interval << 1);
memcpy(&lasttimeofday, &timeofday, sizeof(struct timeval));
timeout.tv_sec = v.notify_interval;
@ -1764,7 +1880,7 @@ main(int argc, char * * argv)
syslog(LOG_DEBUG, "setting timeout to %u sec",
(unsigned)timeout.tv_sec);
}
#ifdef ENABLE_6FC_SERVICE
#ifdef ENABLE_UPNPPINHOLE
/* Clean up expired IPv6 PinHoles */
next_pinhole_ts = 0;
upnp_clean_expired_pinholes(&next_pinhole_ts);
@ -1773,7 +1889,7 @@ main(int argc, char * * argv)
timeout.tv_sec = next_pinhole_ts - timeofday.tv_sec;
timeout.tv_usec = 0;
}
#endif
#endif /* ENABLE_UPNPPINHOLE */
/* select open sockets (SSDP, HTTP listen, and all HTTP soap sockets) */
FD_ZERO(&readset);
@ -1803,6 +1919,20 @@ main(int argc, char * * argv)
max_fd = MAX( max_fd, shttpl_v4);
}
#endif
#ifdef ENABLE_HTTPS
if (shttpsl >= 0)
{
FD_SET(shttpsl, &readset);
max_fd = MAX( max_fd, shttpsl);
}
#if defined(V6SOCKETS_ARE_V6ONLY) && defined(ENABLE_IPV6)
if (shttpsl_v4 >= 0)
{
FD_SET(shttpsl_v4, &readset);
max_fd = MAX( max_fd, shttpsl_v4);
}
#endif
#endif /* ENABLE_HTTPS */
#ifdef ENABLE_IPV6
if (sudpv6 >= 0)
{
@ -1999,19 +2129,50 @@ main(int argc, char * * argv)
len = ReceiveNATPMPOrPCPPacket(snatpmp[i],
(struct sockaddr *)&senderaddr,
&senderaddrlen,
NULL,
msg_buff, sizeof(msg_buff));
if (len < 1)
continue;
#ifdef ENABLE_PCP
if (msg_buff[0]==0) { /* version equals to 0 -> means NAT-PMP */
/* Check if the packet is coming from a LAN to enforce RFC6886 :
* The NAT gateway MUST NOT accept mapping requests destined to the NAT
* gateway's external IP address or received on its external network
* interface. Only packets received on the internal interface(s) with a
* destination address matching the internal address(es) of the NAT
* gateway should be allowed. */
/* TODO : move to ProcessIncomingNATPMPPacket() ? */
lan_addr = get_lan_for_peer((struct sockaddr *)&senderaddr);
if(lan_addr == NULL) {
char sender_str[64];
sockaddr_to_string((struct sockaddr *)&senderaddr, sender_str, sizeof(sender_str));
syslog(LOG_WARNING, "NAT-PMP packet sender %s not from a LAN, ignoring",
sender_str);
continue;
}
ProcessIncomingNATPMPPacket(snatpmp[i], msg_buff, len,
&senderaddr);
} else { /* everything else can be PCP */
ProcessIncomingPCPPacket(snatpmp[i], msg_buff, len,
(struct sockaddr *)&senderaddr);
(struct sockaddr *)&senderaddr, NULL);
}
#else
/* Check if the packet is coming from a LAN to enforce RFC6886 :
* The NAT gateway MUST NOT accept mapping requests destined to the NAT
* gateway's external IP address or received on its external network
* interface. Only packets received on the internal interface(s) with a
* destination address matching the internal address(es) of the NAT
* gateway should be allowed. */
/* TODO : move to ProcessIncomingNATPMPPacket() ? */
lan_addr = get_lan_for_peer((struct sockaddr *)&senderaddr);
if(lan_addr == NULL) {
char sender_str[64];
sockaddr_to_string((struct sockaddr *)&senderaddr, sender_str, sizeof(sender_str));
syslog(LOG_WARNING, "NAT-PMP packet sender %s not from a LAN, ignoring",
sender_str);
continue;
}
ProcessIncomingNATPMPPacket(snatpmp[i], msg_buff, len, &senderaddr);
#endif
}
@ -2024,29 +2185,40 @@ main(int argc, char * * argv)
unsigned char msg_buff[PCP_MAX_LEN];
struct sockaddr_in6 senderaddr;
socklen_t senderaddrlen;
struct sockaddr_in6 receiveraddr;
int len;
memset(msg_buff, 0, PCP_MAX_LEN);
senderaddrlen = sizeof(senderaddr);
len = ReceiveNATPMPOrPCPPacket(spcp_v6,
(struct sockaddr *)&senderaddr,
&senderaddrlen,
&receiveraddr,
msg_buff, sizeof(msg_buff));
if(len >= 1)
ProcessIncomingPCPPacket(spcp_v6, msg_buff, len,
(struct sockaddr *)&senderaddr);
(struct sockaddr *)&senderaddr,
&receiveraddr);
}
#endif
/* process SSDP packets */
if(sudp >= 0 && FD_ISSET(sudp, &readset))
{
/*syslog(LOG_INFO, "Received UDP Packet");*/
#ifdef ENABLE_HTTPS
ProcessSSDPRequest(sudp, (unsigned short)v.port, (unsigned short)v.https_port);
#else
ProcessSSDPRequest(sudp, (unsigned short)v.port);
#endif
}
#ifdef ENABLE_IPV6
if(sudpv6 >= 0 && FD_ISSET(sudpv6, &readset))
{
syslog(LOG_INFO, "Received UDP Packet (IPv6)");
#ifdef ENABLE_HTTPS
ProcessSSDPRequest(sudpv6, (unsigned short)v.port, (unsigned short)v.https_port);
#else
ProcessSSDPRequest(sudpv6, (unsigned short)v.port);
#endif
}
#endif
#ifdef USE_IFACEWATCHER
@ -2072,7 +2244,7 @@ main(int argc, char * * argv)
if(shttpl >= 0 && FD_ISSET(shttpl, &readset))
{
struct upnphttp * tmp;
tmp = ProcessIncomingHTTP(shttpl);
tmp = ProcessIncomingHTTP(shttpl, "HTTP");
if(tmp)
{
LIST_INSERT_HEAD(&upnphttphead, tmp, entries);
@ -2082,13 +2254,37 @@ main(int argc, char * * argv)
if(shttpl_v4 >= 0 && FD_ISSET(shttpl_v4, &readset))
{
struct upnphttp * tmp;
tmp = ProcessIncomingHTTP(shttpl_v4);
tmp = ProcessIncomingHTTP(shttpl_v4, "HTTP");
if(tmp)
{
LIST_INSERT_HEAD(&upnphttphead, tmp, entries);
}
}
#endif
#ifdef ENABLE_HTTPS
if(shttpsl >= 0 && FD_ISSET(shttpsl, &readset))
{
struct upnphttp * tmp;
tmp = ProcessIncomingHTTP(shttpsl, "HTTPS");
if(tmp)
{
InitSSL_upnphttp(tmp);
LIST_INSERT_HEAD(&upnphttphead, tmp, entries);
}
}
#if defined(V6SOCKETS_ARE_V6ONLY) && defined(ENABLE_IPV6)
if(shttpsl_v4 >= 0 && FD_ISSET(shttpsl_v4, &readset))
{
struct upnphttp * tmp;
tmp = ProcessIncomingHTTP(shttpsl_v4, "HTTPS");
if(tmp)
{
InitSSL_upnphttp(tmp);
LIST_INSERT_HEAD(&upnphttphead, tmp, entries);
}
}
#endif
#endif /* ENABLE_HTTPS */
#ifdef ENABLE_NFQUEUE
/* process NFQ packets */
if(nfqh >= 0 && FD_ISSET(nfqh, &readset))
@ -2199,6 +2395,9 @@ shutdown:
free(lan_addr);
}
#ifdef ENABLE_HTTPS
free_ssl();
#endif
#ifdef ENABLE_NATPMP
free(snatpmp);
#endif

View File

@ -1,5 +1,5 @@
# WAN network interface
ext_ifname=eth1
#ext_ifname=eth1
#ext_ifname=xl1
# if the WAN interface has several IP addresses, you
# can specify the one to use below
@ -15,10 +15,16 @@ ext_ifname=eth1
# address associated with the subnet follows. for example :
# listening_ip=192.168.0.1/24 88.22.44.13
#listening_ip=192.168.0.1/24
listening_ip=192.168.10.109/24
#listening_ip=10.5.0.0/16
#listening_ip=eth0
# CAUTION: mixing up WAN and LAN interfaces may introduce security risks!
# be sure to assign the correct interfaces to LAN and WAN and consider
# implementing UPnP permission rules at the bottom of this configuration file
# port for HTTP (descriptions and SOAP) traffic. set 0 for autoselect.
port=0
#http_port=0
# port for HTTPS. set to 0 for autoselect (default)
#https_port=0
# path to the unix socket used to communicate with MiniSSDPd
# If running, MiniSSDPd will manage M-SEARCH answering.
@ -26,15 +32,16 @@ port=0
#minissdpdsocket=/var/run/minissdpd.sock
# enable NAT-PMP support (default is no)
enable_natpmp=yes
#enable_natpmp=yes
# enable UPNP support (default is yes)
enable_upnp=yes
#enable_upnp=no
# PCP :
# configure minimal and maximal lifetime of the port mapping in seconds
# 120s and 86400s (24h) are suggested values from PCP-base
min_lifetime=120
max_lifetime=86400
#min_lifetime=120
#max_lifetime=86400
# chain names for netfilter (not used for pf or ipf).
# default is MINIUPNPD for both
@ -66,8 +73,9 @@ max_lifetime=86400
#model_url=http://miniupnp.free.fr/
# bitrates reported by daemon in bits per second
bitrate_up=1000000
bitrate_down=10000000
# by default miniupnpd tries to get WAN interface speed
#bitrate_up=1000000
#bitrate_down=10000000
# "secure" mode : when enabled, UPnP client are allowed to add mappings only
# to their IP.
@ -114,12 +122,12 @@ clean_ruleset_interval=600
#quickrules=no
# uuid : generate your own with "make genuuid"
uuid=3d3cec3a-8cf0-11e0-98ee-001a6bd2d07b
uuid=00000000-0000-0000-0000-000000000000
# serial and model number the daemon will report to clients
# in its XML description
serial=12345678
model_number=1
#serial=12345678
#model_number=1
# UPnP permission rules
# (allow|deny) (external port range) ip/mask (internal port range)
@ -128,6 +136,11 @@ model_number=1
# ip/mask format must be nn.nn.nn.nn/nn
# it is advised to only allow redirection of port above 1024
# and to finish the rule set with "deny 0-65535 0.0.0.0/0 0-65535"
# The following default ruleset allows specific LAN side IP addresses
# to request only ephemeral ports. it is recommended that users
# modify the IP ranges to match their own internal networks, and
# also consider implementing network-specific restrictions
# CAUTION: failure to enforce any rules may permit insecure requests to be made!
allow 1024-65535 192.168.0.0/24 1024-65535
allow 1024-65535 192.168.1.0/24 1024-65535
allow 1024-65535 192.168.0.0/23 22

View File

@ -1,4 +1,4 @@
/* $Id: natpmp.c,v 1.43 2014/03/24 10:49:45 nanard Exp $ */
/* $Id: natpmp.c,v 1.47 2014/05/19 12:51:52 nanard Exp $ */
/* MiniUPnP project
* (c) 2007-2014 Thomas Bernard
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
@ -14,6 +14,7 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/uio.h>
#include "macros.h"
#include "config.h"
@ -28,6 +29,35 @@
#ifdef ENABLE_NATPMP
#define INLINE static inline
/* theses macros are designed to read/write unsigned short/long int
* from an unsigned char array in network order (big endian).
* Avoid pointer casting, so avoid accessing unaligned memory, which
* can crash with some cpu's */
INLINE uint32_t readnu32(const uint8_t * p)
{
return (p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]);
}
#define READNU32(p) readnu32(p)
INLINE uint16_t readnu16(const uint8_t * p)
{
return (p[0] << 8 | p[1]);
}
#define READNU16(p) readnu16(p)
INLINE void writenu32(uint8_t * p, uint32_t n)
{
p[0] = (n & 0xff000000) >> 24;
p[1] = (n & 0xff0000) >> 16;
p[2] = (n & 0xff00) >> 8;
p[3] = n & 0xff;
}
#define WRITENU32(p, n) writenu32(p, n)
INLINE void writenu16(uint8_t * p, uint16_t n)
{
p[0] = (n & 0xff00) >> 8;
p[1] = n & 0xff;
}
#define WRITENU16(p, n) writenu16(p, n)
int OpenAndConfNATPMPSocket(in_addr_t addr)
{
@ -132,9 +162,63 @@ static void FillPublicAddressResponse(unsigned char * resp, in_addr_t senderaddr
*/
int ReceiveNATPMPOrPCPPacket(int s, struct sockaddr * senderaddr,
socklen_t * senderaddrlen,
struct sockaddr_in6 * receiveraddr,
unsigned char * msg_buff, size_t msg_buff_size)
{
#if IPV6_PKTINFO
struct iovec iov;
uint8_t c[1000];
struct msghdr msg;
int n;
struct cmsghdr *h;
iov.iov_base = msg_buff;
iov.iov_len = msg_buff_size;
memset(&msg, 0, sizeof(msg));
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_name = senderaddr;
msg.msg_namelen = *senderaddrlen;
msg.msg_control = c;
msg.msg_controllen = sizeof(c);
n = recvmsg(s, &msg, 0);
if(n < 0) {
/* EAGAIN, EWOULDBLOCK and EINTR : silently ignore (retry next time)
* other errors : log to LOG_ERR */
if(errno != EAGAIN &&
errno != EWOULDBLOCK &&
errno != EINTR) {
syslog(LOG_ERR, "recvmsg(natpmp): %m");
}
return n;
}
if(receiveraddr) {
memset(receiveraddr, 0, sizeof(struct sockaddr_in6));
}
if ((msg.msg_flags & MSG_TRUNC) || (msg.msg_flags & MSG_CTRUNC)) {
syslog(LOG_WARNING, "%s: truncated message",
"ReceiveNATPMPOrPCPPacket");
}
for(h = CMSG_FIRSTHDR(&msg); h;
h = CMSG_NXTHDR(&msg, h)) {
if(h->cmsg_level == IPPROTO_IPV6 && h->cmsg_type == IPV6_PKTINFO) {
char tmp[INET6_ADDRSTRLEN];
struct in6_pktinfo *ipi6 = (struct in6_pktinfo *)CMSG_DATA(h);
syslog(LOG_DEBUG, "%s: packet destination: %s scope_id=%u",
"ReceiveNATPMPOrPCPPacket",
inet_ntop(AF_INET6, &ipi6->ipi6_addr, tmp, sizeof(tmp)),
ipi6->ipi6_ifindex);
if(receiveraddr) {
receiveraddr->sin6_addr = ipi6->ipi6_addr;
receiveraddr->sin6_scope_id = ipi6->ipi6_ifindex;
receiveraddr->sin6_family = AF_INET6;
receiveraddr->sin6_port = htons(NATPMP_PORT);
}
}
}
#else
int n;
n = recvfrom(s, msg_buff, msg_buff_size, 0,
@ -150,6 +234,7 @@ int ReceiveNATPMPOrPCPPacket(int s, struct sockaddr * senderaddr,
}
return n;
}
#endif
return n;
}
@ -189,7 +274,7 @@ void ProcessIncomingNATPMPPacket(int s, unsigned char *msg_buff, int len,
/* setting response TIME STAMP :
* time elapsed since its port mapping table was initialized on
* startup or reset for any other reason */
*((uint32_t *)(resp+4)) = htonl(time(NULL) - startup_time);
WRITENU32(resp+4, time(NULL) - startup_time);
if(req[0] > 0) {
/* invalid version */
syslog(LOG_WARNING, "unsupported NAT-PMP version : %u",
@ -213,68 +298,65 @@ void ProcessIncomingNATPMPPacket(int s, unsigned char *msg_buff, int len,
unsigned short iport_old;
unsigned int timestamp;
iport = ntohs(*((uint16_t *)(req+4)));
eport = ntohs(*((uint16_t *)(req+6)));
lifetime = ntohl(*((uint32_t *)(req+8)));
iport = READNU16(req+4);
eport = READNU16(req+6);
lifetime = READNU32(req+8);
proto = (req[1]==1)?IPPROTO_UDP:IPPROTO_TCP;
syslog(LOG_INFO, "NAT-PMP port mapping request : "
"%hu->%s:%hu %s lifetime=%us",
eport, senderaddrstr, iport,
(req[1]==1)?"udp":"tcp", lifetime);
if(eport==0)
eport = iport;
/* TODO: accept port mapping if iport ok but eport not ok
* (and set eport correctly) */
if(lifetime == 0) {
/* remove the mapping */
if(iport == 0) {
/* remove all the mappings for this client */
int index = 0;
unsigned short eport2, iport2;
char iaddr2[16];
int proto2;
char desc[64];
while(get_redirect_rule_by_index(index, 0,
&eport2, iaddr2, sizeof(iaddr2),
&iport2, &proto2,
desc, sizeof(desc),
0, 0, &timestamp, 0, 0) >= 0) {
syslog(LOG_DEBUG, "%d %d %hu->'%s':%hu '%s'",
index, proto2, eport2, iaddr2, iport2, desc);
if(0 == strcmp(iaddr2, senderaddrstr)
&& 0 == memcmp(desc, "NAT-PMP", 7)) {
/* RFC6886 :
* A client MAY also send an explicit packet to request deletion of a
* mapping that is no longer needed. A client requests explicit
* deletion of a mapping by sending a message to the NAT gateway
* requesting the mapping, with the Requested Lifetime in Seconds set to
* zero. The Suggested External Port MUST be set to zero by the client
* on sending, and MUST be ignored by the gateway on reception. */
int index = 0;
unsigned short eport2, iport2;
char iaddr2[16];
int proto2;
char desc[64];
eport = 0; /* to indicate correct removing of port mapping */
while(get_redirect_rule_by_index(index, 0,
&eport2, iaddr2, sizeof(iaddr2),
&iport2, &proto2,
desc, sizeof(desc),
0, 0, &timestamp, 0, 0) >= 0) {
syslog(LOG_DEBUG, "%d %d %hu->'%s':%hu '%s'",
index, proto2, eport2, iaddr2, iport2, desc);
if(0 == strcmp(iaddr2, senderaddrstr)
&& 0 == memcmp(desc, "NAT-PMP", 7)) {
/* (iport == 0) => remove all the mappings for this client */
if((iport == 0) || ((iport == iport2) && (proto == proto2))) {
r = _upnp_delete_redir(eport2, proto2);
/* TODO : check return value */
if(r<0) {
syslog(LOG_ERR, "failed to remove port mapping");
index++;
if(r < 0) {
syslog(LOG_ERR, "Failed to remove NAT-PMP mapping eport %hu, protocol %s",
eport2, (proto2==IPPROTO_TCP)?"TCP":"UDP");
resp[3] = 2; /* Not Authorized/Refused */
break;
} else {
syslog(LOG_INFO, "NAT-PMP %s port %hu mapping removed",
proto2==IPPROTO_TCP?"TCP":"UDP", eport2);
index--;
}
} else {
index++;
}
}
} else {
/* To improve the interworking between nat-pmp and
* UPnP, we should check that we remove only NAT-PMP
* mappings */
r = _upnp_delete_redir(eport, proto);
/*syslog(LOG_DEBUG, "%hu %d r=%d", eport, proto, r);*/
if(r<0) {
syslog(LOG_ERR, "Failed to remove NAT-PMP mapping eport %hu, protocol %s",
eport, (proto==IPPROTO_TCP)?"TCP":"UDP");
resp[3] = 2; /* Not Authorized/Refused */
}
index++;
}
eport = 0; /* to indicate correct removing of port mapping */
} else if(iport==0) {
resp[3] = 2; /* Not Authorized/Refused */
} else { /* iport > 0 && lifetime > 0 */
unsigned short eport_first = 0;
int any_eport_allowed = 0;
char desc[64];
if(eport==0) /* if no suggested external port, use same a internal port */
eport = iport;
while(resp[3] == 0) {
if(eport_first == 0) { /* first time in loop */
eport_first = eport;
@ -347,9 +429,9 @@ void ProcessIncomingNATPMPPacket(int s, unsigned char *msg_buff, int len,
break;
}
}
*((uint16_t *)(resp+8)) = htons(iport); /* private port */
*((uint16_t *)(resp+10)) = htons(eport); /* public port */
*((uint32_t *)(resp+12)) = htonl(lifetime); /* Port Mapping lifetime */
WRITENU16(resp+8, iport); /* private port */
WRITENU16(resp+10, eport); /* public port */
WRITENU32(resp+12, lifetime); /* Port Mapping lifetime */
}
resplen = 16;
break;
@ -381,7 +463,7 @@ void SendNATPMPPublicAddressChangeNotification(int * sockets, int n_sockets)
/* seconds since "start of epoch" :
* time elapsed since the port mapping table was initialized on
* startup or reset for any other reason */
*((uint32_t *)(notif+4)) = htonl(time(NULL) - startup_time);
WRITENU32(notif+4, time(NULL) - startup_time);
#ifndef MULTIPLE_EXTERNAL_IP
FillPublicAddressResponse(notif, 0);
if(notif[3])

View File

@ -20,8 +20,10 @@
int OpenAndConfNATPMPSockets(int * sockets);
/* receiveraddr is only used with IPV6 sockets */
int ReceiveNATPMPOrPCPPacket(int s, struct sockaddr * senderaddr,
socklen_t * senderaddrlen,
struct sockaddr_in6 * receiveraddr,
unsigned char * msg_buff, size_t msg_buff_size);
void ProcessIncomingNATPMPPacket(int s, unsigned char * msg_buff, int len,

View File

@ -1,10 +1,13 @@
#! /bin/sh
# $Id: iptables_init.sh,v 1.5 2011/05/16 12:11:37 nanard Exp $
IPTABLES=/sbin/iptables
IPTABLES="`which iptables`" || exit 1
IP="`which ip`" || exit 1
#change this parameters :
EXTIF=eth0
EXTIP="`LC_ALL=C /sbin/ifconfig $EXTIF | grep 'inet ' | awk '{print $2}' | sed -e 's/.*://'`"
#EXTIF=eth0
EXTIF="`LC_ALL=C $IP -4 route | grep 'default' | sed -e 's/.*dev[[:space:]]*//' -e 's/[[:space:]].*//'`" || exit 1
EXTIP="`LC_ALL=C $IP -4 addr show $EXTIF | awk '/inet/ { print $2 }' | cut -d "/" -f 1`"
echo "External IP = $EXTIP"
#adding the MINIUPNPD chain for nat

View File

@ -3,11 +3,15 @@
# Improved Miniupnpd iptables init script.
# Checks for state of filter before doing anything..
EXTIF=eth0
IPTABLES=/sbin/iptables
EXTIP="`LC_ALL=C /sbin/ifconfig $EXTIF | grep 'inet ' | awk '{print $2}' | sed -e 's/.*://'`"
NDIRTY="`LC_ALL=C /sbin/iptables -t nat -L -n | grep 'MINIUPNPD' | awk '{printf $1}'`"
FDIRTY="`LC_ALL=C /sbin/iptables -t filter -L -n | grep 'MINIUPNPD' | awk '{printf $1}'`"
IPTABLES="`which iptables`" || exit 1
IP="`which ip`" || exit 1
#EXTIF=eth0
EXTIF="`LC_ALL=C $IP -4 route | grep 'default' | sed -e 's/.*dev[[:space:]]*//' -e 's/[[:space:]].*//'`" || exit 1
EXTIP="`LC_ALL=C $IP -4 addr show $EXTIF | awk '/inet/ { print $2 }' | cut -d "/" -f 1`"
NDIRTY="`LC_ALL=C $IPTABLES -t nat -L -n | awk '/MINIUPNPD/ {printf $1}'`"
FDIRTY="`LC_ALL=C $IPTABLES -t filter -L -n | awk '/MINIUPNPD/ {printf $1}'`"
echo "External IP = $EXTIP"
if [[ $NDIRTY = "MINIUPNPDChain" ]]; then

View File

@ -1,10 +1,12 @@
#! /bin/sh
# $Id: iptables_removeall.sh,v 1.5 2011/05/16 12:11:37 nanard Exp $
IPTABLES=/sbin/iptables
IPTABLES="`which iptables`" || exit 1
IP="`which ip`" || exit 1
#change this parameters :
EXTIF=eth0
EXTIP="`LC_ALL=C /sbin/ifconfig $EXTIF | grep 'inet ' | awk '{print $2}' | sed -e 's/.*://'`"
#EXTIF=eth0
EXTIF="`LC_ALL=C $IP -4 route | grep 'default' | sed -e 's/.*dev[[:space:]]*//' -e 's/[[:space:]].*//'`" || exit 1
EXTIP="`LC_ALL=C $IP -4 addr show $EXTIF | awk '/inet/ { print $2 }' | cut -d "/" -f 1`"
#removing the MINIUPNPD chain for nat
$IPTABLES -t nat -F MINIUPNPD

View File

@ -16,7 +16,7 @@
#include "iptpinhole.h"
#include "../upnpglobalvars.h"
#ifdef ENABLE_6FC_SERVICE
#ifdef ENABLE_UPNPPINHOLE
#include <xtables.h>
#include <libiptc/libip6tc.h>
@ -40,6 +40,7 @@ struct pinhole_t {
unsigned short dport;
unsigned short uid;
unsigned char proto;
char desc[];
};
void init_iptpinhole(void)
@ -56,15 +57,16 @@ void shutdown_iptpinhole(void)
static int
add_to_pinhole_list(struct in6_addr * saddr, unsigned short sport,
struct in6_addr * daddr, unsigned short dport,
int proto, unsigned int timestamp)
int proto, const char *desc, unsigned int timestamp)
{
struct pinhole_t * p;
p = calloc(1, sizeof(struct pinhole_t));
p = calloc(1, sizeof(struct pinhole_t) + strlen(desc) + 1);
if(!p) {
syslog(LOG_ERR, "add_to_pinhole_list calloc() error");
return -1;
}
strcpy(p->desc, desc);
memcpy(&p->saddr, saddr, sizeof(struct in6_addr));
p->sport = sport;
memcpy(&p->daddr, daddr, sizeof(struct in6_addr));
@ -195,7 +197,7 @@ ip6tables -t raw -I PREROUTING %d -p %s -i %s --sport %hu -d %s --dport %hu -j T
int add_pinhole(const char * ifname,
const char * rem_host, unsigned short rem_port,
const char * int_client, unsigned short int_port,
int proto, unsigned int timestamp)
int proto, const char * desc, unsigned int timestamp)
{
int uid;
struct ip6t_entry * e;
@ -204,6 +206,8 @@ int add_pinhole(const char * ifname,
e = calloc(1, sizeof(struct ip6t_entry));
e->ipv6.proto = proto;
if (proto)
e->ipv6.flags |= IP6T_F_PROTO;
if(ifname)
strncpy(e->ipv6.iniface, ifname, IFNAMSIZ);
@ -238,7 +242,7 @@ int add_pinhole(const char * ifname,
}
uid = add_to_pinhole_list(&e->ipv6.src, rem_port,
&e->ipv6.dst, int_port,
proto, timestamp);
proto, desc, timestamp);
free(e);
return uid;
}
@ -318,9 +322,12 @@ update_pinhole(unsigned short uid, unsigned int timestamp)
int
get_pinhole_info(unsigned short uid,
char * rem_host, int rem_hostlen, unsigned short * rem_port,
char * int_client, int int_clientlen, unsigned short * int_port,
int * proto, unsigned int * timestamp,
char * rem_host, int rem_hostlen,
unsigned short * rem_port,
char * int_client, int int_clientlen,
unsigned short * int_port,
int * proto, char * desc, int desclen,
unsigned int * timestamp,
u_int64_t * packets, u_int64_t * bytes)
{
struct pinhole_t * p;
@ -344,6 +351,8 @@ get_pinhole_info(unsigned short uid,
*proto = p->proto;
if(timestamp)
*timestamp = p->timestamp;
if (desc)
strncpy(desc, p->desc, desclen);
if(packets || bytes) {
/* theses informations need to be read from netfilter */
IP6TC_HANDLE h;
@ -377,6 +386,16 @@ get_pinhole_info(unsigned short uid,
return 0;
}
int get_pinhole_uid_by_index(int index)
{
struct pinhole_t * p;
for(p = pinhole_list.lh_first; p != NULL; p = p->entries.le_next)
if (!index--)
return p->uid;
return -1;
}
int
clean_pinhole_list(unsigned int * next_timestamp)
{
@ -407,5 +426,4 @@ clean_pinhole_list(unsigned int * next_timestamp)
return n;
}
#endif
#endif /* ENABLE_UPNPPINHOLE */

View File

@ -7,11 +7,13 @@
#ifndef IPTPINHOLE_H_INCLUDED
#define IPTPINHOLE_H_INCLUDED
#ifdef ENABLE_6FC_SERVICE
#ifdef ENABLE_UPNPPINHOLE
#include <sys/types.h>
int add_pinhole(const char * ifname,
const char * rem_host, unsigned short rem_port,
const char * int_client, unsigned short int_port,
int proto, unsigned int timestamp);
int proto, const char *desc, unsigned int timestamp);
int update_pinhole(unsigned short uid, unsigned int timestamp);
@ -20,12 +22,16 @@ int delete_pinhole(unsigned short uid);
int
get_pinhole_info(unsigned short uid,
char * rem_host, int rem_hostlen, unsigned short * rem_port,
char * int_client, int int_clientlen, unsigned short * int_port,
int * proto, unsigned int * timestamp,
char * int_client, int int_clientlen,
unsigned short * int_port,
int * proto, char * desc, int desclen,
unsigned int * timestamp,
u_int64_t * packets, u_int64_t * bytes);
int get_pinhole_uid_by_index(int index);
int clean_pinhole_list(unsigned int * next_timestamp);
#endif
#endif /* ENABLE_UPNPPINHOLE */
#endif

View File

@ -2,7 +2,7 @@
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* author: Ryan Wagoner
* (c) 2006-2013 Thomas Bernard
* (c) 2006-2014 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
@ -31,7 +31,14 @@ static const struct {
{ UPNPEXT_IFNAME, "ext_ifname" },
{ UPNPEXT_IP, "ext_ip" },
{ UPNPLISTENING_IP, "listening_ip" },
#ifdef ENABLE_IPV6
{ UPNPIPV6_LISTENING_IP, "ipv6_listening_ip" },
#endif /* ENABLE_IPV6 */
{ UPNPPORT, "port" },
{ UPNPPORT, "http_port" }, /* "port" and "http_port" are synonims */
#ifdef ENABLE_HTTPS
{ UPNPHTTPSPORT, "https_port" },
#endif /* ENABLE_HTTPS */
{ UPNPBITRATE_UP, "bitrate_up" },
{ UPNPBITRATE_DOWN, "bitrate_down" },
{ UPNPPRESENTATIONURL, "presentation_url" },
@ -61,6 +68,7 @@ static const struct {
#ifdef ENABLE_PCP
{ UPNPPCPMINLIFETIME, "min_lifetime"},
{ UPNPPCPMAXLIFETIME, "max_lifetime"},
{ UPNPPCPALLOWTHIRDPARTY, "pcp_allow_thirdparty"},
#endif
{ UPNPENABLE, "enable_upnp"},
#ifdef USE_PF
@ -136,8 +144,10 @@ readoptionsfile(const char * fname)
/* check for comments or empty lines */
if(name[0] == '#' || name[0] == '\0') continue;
len = strlen(name); /* length of the whole line excluding leading
* and ending white spaces */
/* check for UPnP permissions rule */
if(0 == memcmp(name, "allow", 5) || 0 == memcmp(name, "deny", 4))
if((len > 6) && (0 == memcmp(name, "allow", 5) || 0 == memcmp(name, "deny", 4)))
{
tmp = realloc(upnppermlist, sizeof(struct upnpperm) * (num_upnpperm+1));
if(tmp == NULL)
@ -163,7 +173,7 @@ readoptionsfile(const char * fname)
}
#ifdef PCP_SADSCP
/* check for DSCP values configuration */
if(0 == memcmp(name, "set_learn_dscp", sizeof("set_learn_dscp")-1) )
if((len > 15) && 0 == memcmp(name, "set_learn_dscp", sizeof("set_learn_dscp")-1) )
{
tmp = realloc(dscp_values_list, sizeof(struct dscp_values) * (num_dscp_values+1));
if(tmp == NULL)
@ -307,4 +317,3 @@ freeoptions(void)
}
#endif /* DISABLE_CONFIG_FILE */

View File

@ -2,7 +2,7 @@
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* author: Ryan Wagoner
* (c) 2006-2013 Thomas Bernard
* (c) 2006-2014 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
@ -18,7 +18,13 @@ enum upnpconfigoptions {
UPNPEXT_IFNAME = 1, /* ext_ifname */
UPNPEXT_IP, /* ext_ip */
UPNPLISTENING_IP, /* listening_ip */
UPNPPORT, /* "port" */
#ifdef ENABLE_IPV6
UPNPIPV6_LISTENING_IP, /* listening address for IPv6 */
#endif /* ENABLE_IPV6 */
UPNPPORT, /* "port" / "http_port" */
#ifdef ENABLE_HTTPS
UPNPHTTPSPORT, /* "https_port" */
#endif
UPNPBITRATE_UP, /* "bitrate_up" */
UPNPBITRATE_DOWN, /* "bitrate_down" */
UPNPPRESENTATIONURL, /* presentation_url */
@ -41,6 +47,7 @@ enum upnpconfigoptions {
UPNPENABLENATPMP, /* enable_natpmp */
UPNPPCPMINLIFETIME, /* minimum lifetime for PCP mapping */
UPNPPCPMAXLIFETIME, /* maximum lifetime for PCP mapping */
UPNPPCPALLOWTHIRDPARTY, /* allow third-party requests */
#ifdef USE_NETFILTER
UPNPFORWARDCHAIN,
UPNPNATCHAIN,

View File

@ -135,10 +135,10 @@ typedef enum pcp_options {
} pcp_options_t;
#ifdef WIN32
#ifdef _WIN32
#pragma warning (push)
#pragma warning (disable:4200)
#endif /* WIN32 */
#endif /* _WIN32 */
#pragma pack(push, 1)
@ -285,6 +285,6 @@ typedef struct pcp_filter_option {
#pragma pack(pop)
#ifdef WIN32
#ifdef _WIN32
#pragma warning (pop)
#endif /* WIN32 */
#endif /* _WIN32 */

Some files were not shown because too many files have changed in this diff Show More