miniupnpd-1.8.20130730/Makefile010064400017500000024000000137221210445350200151060ustar00nanardstaff# $Id: Makefile,v 1.73 2013/02/06 13:11:45 nanard Exp $ # MiniUPnP project # http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ # Author: Thomas Bernard # # Makefile for miniupnpd (MiniUPnP daemon) # # This Makefile should work for *BSD and SunOS/Solaris. # On Mac OS X, use "bsdmake" to build. # This Makefile is NOT compatible with GNU Make. # Linux users, please use Makefile.linux : # make -f Makefile.linux # # options can be passed to genconfig.sh through CONFIG_OPTIONS : # $ CONFIG_OPTIONS="--ipv6 --igd2" make # CFLAGS ?= -pipe -Os #CFLAGS = -pipe -O -g -DDEBUG CFLAGS += -ansi CFLAGS += -Wall CFLAGS += -W CFLAGS += -Wstrict-prototypes #CFLAGS += -Wdeclaration-after-statement #CFLAGS += -Wno-missing-field-initializers CFLAGS += -fno-common CC ?= gcc RM = rm -f MV = mv INSTALL = install STRIP = strip # OSNAME and FWNAME are used for building OS or FW dependent code. OSNAME != uname -s ARCH != uname -m .ifndef FWNAME .if exists(/usr/include/net/pfvar.h) FWNAME = pf .else FWNAME = ipf .endif .endif # better way to find if we are using ipf or pf .if defined(/etc/rc.subr) && defined(/etc/rc.conf) .if $(OSNAME) == "FreeBSD" FWNAME != . /etc/rc.subr; . /etc/rc.conf; \ if checkyesno ipfilter_enable; then \ echo "ipf"; else echo "pf"; fi .endif .if $(OSNAME) == "NetBSD" FWNAME != . /etc/rc.subr; . /etc/rc.conf; \ if checkyesno ipfilter; then \ echo "ipf"; else echo "pf"; fi .endif .if $(OSNAME) == "DragonFly" FWNAME != . /etc/rc.subr; . /etc/rc.conf; \ if chechyesno ipfilter; then \ echo "ipf"; else echo "pf"; fi .endif .endif .if $(OSNAME) == "Darwin" FWNAME = ipfw .endif # Solaris specific CFLAGS .if $(OSNAME) == "SunOS" CFLAGS += -DSOLARIS2=`uname -r | cut -d. -f2` .if $(ARCH) == "amd64" CFLAGS += -m64 -mcmodel=kernel -mno-red-zone -ffreestanding .elif $(ARCH) == "sparc64" CFLAGS += -m64 -mcmodel=medlow .endif .endif STDOBJS = 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 upnputils.o getconnstatus.o \ upnppinhole.o BSDOBJS = bsd/getifstats.o bsd/ifacewatcher.o bsd/getroute.o SUNOSOBJS = solaris/getifstats.o bsd/ifacewatcher.o bsd/getroute.o MACOBJS = mac/getifstats.o bsd/ifacewatcher.o bsd/getroute.o PFOBJS = pf/obsdrdr.o pf/pfpinhole.o IPFOBJS = ipf/ipfrdr.o IPFWOBJS = ipfw/ipfwrdr.o ipfw/ipfwaux.o MISCOBJS = upnpreplyparse.o minixml.o ALLOBJS = $(STDOBJS) $(MISCOBJS) .if $(OSNAME) == "SunOS" ALLOBJS += $(SUNOSOBJS) TESTGETIFSTATSOBJS = testgetifstats.o solaris/getifstats.o TESTGETROUTEOBJS = testgetroute.o upnputils.o bsd/getroute.o .elif $(OSNAME) == "Darwin" ALLOBJS += $(MACOBJS) TESTGETIFSTATSOBJS = testgetifstats.o mac/getifstats.o TESTGETROUTEOBJS = testgetroute.o upnputils.o bsd/getroute.o .else ALLOBJS += $(BSDOBJS) TESTGETIFSTATSOBJS = testgetifstats.o bsd/getifstats.o TESTGETROUTEOBJS = testgetroute.o upnputils.o bsd/getroute.o .endif .if $(FWNAME) == "pf" ALLOBJS += $(PFOBJS) .elif $(FWNAME) == "ipfw" ALLOBJS += $(IPFWOBJS) .else ALLOBJS += $(IPFOBJS) .endif TESTUPNPDESCGENOBJS = testupnpdescgen.o upnpdescgen.o TESTUPNPPERMISSIONSOBJS = testupnppermissions.o upnppermissions.o TESTGETIFADDROBJS = testgetifaddr.o getifaddr.o MINIUPNPDCTLOBJS = miniupnpdctl.o EXECUTABLES = miniupnpd testupnpdescgen testgetifstats \ testupnppermissions miniupnpdctl \ testgetifaddr testgetroute .if $(OSNAME) == "Darwin" LIBS = .else LIBS = -lkvm .endif .if $(OSNAME) == "SunOS" LIBS += -lsocket -lnsl -lkstat -lresolv .endif # set PREFIX variable to install in the wanted place INSTALLBINDIR = $(PREFIX)/sbin INSTALLETCDIR = $(PREFIX)/etc # INSTALLMANDIR = $(PREFIX)/man INSTALLMANDIR = /usr/share/man all: $(EXECUTABLES) clean: $(RM) $(STDOBJS) $(BSDOBJS) $(SUNOSOBJS) $(MACOBJS) $(EXECUTABLES) \ testupnpdescgen.o \ $(MISCOBJS) config.h testgetifstats.o testupnppermissions.o \ miniupnpdctl.o testgetifaddr.o testgetroute.o \ $(PFOBJS) $(IPFOBJS) $(IPFWOBJS) install: miniupnpd genuuid $(STRIP) miniupnpd $(INSTALL) -d $(DESTDIR)$(INSTALLBINDIR) $(INSTALL) -m 555 miniupnpd $(DESTDIR)$(INSTALLBINDIR) $(INSTALL) -d $(DESTDIR)$(INSTALLETCDIR) $(INSTALL) -b miniupnpd.conf $(DESTDIR)$(INSTALLETCDIR) # TODO : install man page correctly # $(INSTALL) -d $(INSTALLMANDIR) # $(INSTALL) miniupnpd.8 $(INSTALLMANDIR)/cat8/miniupnpd.0 # genuuid is using the uuid cli tool available under OpenBSD 4.0 in # the uuid-1.5.0 package # any other cli tool returning a uuid on stdout should work. UUID != if which uuidgen 2>&1 > /dev/null; then \ echo `uuidgen` ; \ elif which uuid 2>&1 > /dev/null; then \ echo `uuid` ; \ else echo "00000000-0000-0000-0000-000000000000"; \ fi genuuid: $(MV) miniupnpd.conf miniupnpd.conf.before sed -e "s/^uuid=[-0-9a-fA-F]*/uuid=$(UUID)/" miniupnpd.conf.before > miniupnpd.conf $(RM) miniupnpd.conf.before depend: config.h mkdep $(ALLOBJS:.o=.c) testupnpdescgen.c testgetifstats.c \ testupnppermissions.c miniupnpdctl.c testgetifaddr.c \ testgetroute.c miniupnpd: config.h $(ALLOBJS) $(CC) $(CFLAGS) -o $@ $(ALLOBJS) $(LIBS) # BSDmake : # $(CC) $(CFLAGS) -o $@ $> $(LIBS) miniupnpdctl: config.h $(MINIUPNPDCTLOBJS) $(CC) $(CFLAGS) -o $@ $(MINIUPNPDCTLOBJS) testupnpdescgen: config.h $(TESTUPNPDESCGENOBJS) $(CC) $(CFLAGS) -o $@ $(TESTUPNPDESCGENOBJS) testgetifstats: config.h $(TESTGETIFSTATSOBJS) $(CC) $(CFLAGS) -o $@ $(TESTGETIFSTATSOBJS) $(LIBS) testgetifaddr: config.h $(TESTGETIFADDROBJS) $(CC) $(CFLAGS) -o $@ $(TESTGETIFADDROBJS) testupnppermissions: config.h $(TESTUPNPPERMISSIONSOBJS) $(CC) $(CFLAGS) -o $@ $(TESTUPNPPERMISSIONSOBJS) testgetroute: config.h $(TESTGETROUTEOBJS) $(CC) $(CFLAGS) -o $@ $(TESTGETROUTEOBJS) # gmake : # $(CC) $(CFLAGS) -o $@ $^ # BSDmake : # $(CC) $(CFLAGS) -o $@ $> config.h: genconfig.sh VERSION ./genconfig.sh $(CONFIG_OPTIONS) .SUFFIXES: .o .c .c.o: $(CC) $(CFLAGS) -c -o $@ $< # $(CC) $(CFLAGS) -c -o $(.TARGET) $(.IMPSRC) miniupnpd-1.8.20130730/Makefile.linux010064400017500000024000000255001214100740700162410ustar00nanardstaff# $Id: Makefile.linux,v 1.78 2013/05/03 09:30:33 nanard Exp $ # MiniUPnP project # (c) 2006-2013 Thomas Bernard # http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ # Author : Thomas Bernard # for use with GNU Make # # options can be passed to genconfig.sh through CONFIG_OPTIONS : # $ CONFIG_OPTIONS="--ipv6 --igd2" make -f Makefile.linux # # To install use : # $ DESTDIR=/dummyinstalldir make -f Makefile.linux install # or : # $ INSTALLPREFIX=/usr/local make -f Makefile.linux install # or : # $ make -f Makefile.linux install # (default INSTALLPREFIX is /usr) # # if your system hasn't iptables libiptc headers and binary correctly # installed, you need to get iptables sources from http://netfilter.org/ # ./configure them and build them then miniupnpd will build using : # $ IPTABLESPATH=/path/to/iptables-1.4.1 make -f Makefile.linux # #CFLAGS = -O -g -DDEBUG CFLAGS ?= -Os CFLAGS += -fno-strict-aliasing CFLAGS += -fno-common CFLAGS += -D_GNU_SOURCE CFLAGS += -Wall CFLAGS += -Wextra -Wstrict-prototypes -Wdeclaration-after-statement #CFLAGS += -Wno-missing-field-initializers #CFLAGS += -ansi # iptables headers does use typeof which is a gcc extension CC ?= gcc RM = rm -f INSTALL = install STRIP ?= strip CP = cp INSTALLPREFIX ?= $(PREFIX)/usr SBININSTALLDIR = $(INSTALLPREFIX)/sbin ETCINSTALLDIR = $(PREFIX)/etc/miniupnpd MANINSTALLDIR = $(INSTALLPREFIX)/share/man/man8 BASEOBJS = miniupnpd.o upnphttp.o upnpdescgen.o upnpsoap.o \ upnpreplyparse.o minixml.o \ upnpredirect.o getifaddr.o daemonize.o upnpglobalvars.o \ options.o upnppermissions.o minissdp.o natpmp.o \ upnpevents.o upnputils.o getconnstatus.o \ upnppinhole.o LNXOBJS = linux/getifstats.o linux/ifacewatcher.o linux/getroute.o NETFILTEROBJS = netfilter/iptcrdr.o netfilter/iptpinhole.o ALLOBJS = $(BASEOBJS) $(LNXOBJS) $(NETFILTEROBJS) PCFILE_FOUND := $(shell pkg-config --exists libiptc; echo $$?) ifeq (${PCFILE_FOUND},0) 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 endif CFLAGS += $(shell pkg-config --cflags libiptc) LIBS += $(shell pkg-config --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 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) endif ARCH ?= $(shell uname -m | grep -q "x86_64" && echo 64) ifdef IPTABLESPATH CFLAGS := $(CFLAGS) -I$(IPTABLESPATH)/include/ LDFLAGS := $(LDFLAFGS) -L$(IPTABLESPATH)/libiptc/ # get iptables version and set IPTABLES_143 macro if needed ifeq ($(TARGET_OPENWRT),) IPTABLESVERSION := $(shell grep "\#define VERSION" $(IPTABLESPATH)/config.h | tr -d \" |cut -d" " -f3 ) 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 := $(CFLAGS) -DIPTABLES_143 # the following sucks, but works LIBS = $(IPTABLESPATH)/libiptc/.libs/libip4tc.o #LIBS = $(IPTABLESPATH)/libiptc/.libs/libiptc.a else # ifeq ($(TEST), 1) LIBS = $(IPTABLESPATH)/libiptc/libiptc.a endif # ifeq ($(TEST), 1) else # ($(TARGET_OPENWRT),) # openWRT : # check for system-wide iptables files. Test if iptables version >= 1.4.3 # 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 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 endif # ($(TEST_LIB), 1) endif # ($(TARGET_OPENWRT),) else # ifdef IPTABLESPATH # IPTABLESPATH not defined # 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 TESTIP4TC := $(shell test -f /lib/libip4tc.so && echo 1) ifeq ($(TESTIP4TC), 1) LIBS := $(LIBS) -lip4tc endif # ($(TESTIP4TC), 1) TESTIP6TC := $(shell test -f /lib/libip6tc.so && echo 1) ifeq ($(TESTIP6TC), 1) LIBS := $(LIBS) -lip6tc endif # ($(TESTIP6TC), 1) endif # ($(TEST), 1) endif # ifdef IPTABLESPATH endif # ifdef PCFILE_FOUND LIBS += -lnfnetlink TESTUPNPDESCGENOBJS = testupnpdescgen.o upnpdescgen.o EXECUTABLES = miniupnpd testupnpdescgen testgetifstats \ testupnppermissions miniupnpdctl testgetifaddr \ testgetroute .PHONY: all clean install depend genuuid all: $(EXECUTABLES) clean: $(RM) $(ALLOBJS) $(RM) $(EXECUTABLES) $(RM) testupnpdescgen.o testgetifstats.o $(RM) testupnppermissions.o testgetifaddr.o $(RM) testgetroute.o $(RM) miniupnpdctl.o install: miniupnpd miniupnpd.8 miniupnpd.conf genuuid \ netfilter/iptables_init.sh netfilter/iptables_removeall.sh \ netfilter/ip6tables_init.sh netfilter/ip6tables_removeall.sh \ linux/miniupnpd.init.d.script $(STRIP) miniupnpd $(INSTALL) -d $(DESTDIR)$(SBININSTALLDIR) $(INSTALL) miniupnpd $(DESTDIR)$(SBININSTALLDIR) $(INSTALL) -d $(DESTDIR)$(ETCINSTALLDIR) $(INSTALL) netfilter/iptables_init.sh $(DESTDIR)$(ETCINSTALLDIR) $(INSTALL) netfilter/iptables_removeall.sh $(DESTDIR)$(ETCINSTALLDIR) $(INSTALL) netfilter/ip6tables_init.sh $(DESTDIR)$(ETCINSTALLDIR) $(INSTALL) netfilter/ip6tables_removeall.sh $(DESTDIR)$(ETCINSTALLDIR) $(INSTALL) --mode=0644 -b miniupnpd.conf $(DESTDIR)$(ETCINSTALLDIR) $(INSTALL) -d $(DESTDIR)$(PREFIX)/etc/init.d $(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 # genuuid is using the uuidgen CLI tool which is part of libuuid # from the e2fsprogs # 'cat /proc/sys/kernel/random/uuid' could be also used genuuid: ifeq ($(TARGET_OPENWRT),) sed -i -e "s/^uuid=[-0-9a-f]*/uuid=`(genuuid||uuidgen||uuid) 2>/dev/null`/" miniupnpd.conf 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) testupnpdescgen: $(TESTUPNPDESCGENOBJS) testgetifstats: testgetifstats.o linux/getifstats.o testupnppermissions: testupnppermissions.o upnppermissions.o testgetifaddr: testgetifaddr.o getifaddr.o testgetroute: testgetroute.o linux/getroute.o upnputils.o -lnfnetlink miniupnpdctl: miniupnpdctl.o config.h: genconfig.sh VERSION ./genconfig.sh $(CONFIG_OPTIONS) depend: config.h makedepend -f$(MAKEFILE_LIST) -Y \ $(ALLOBJS:.o=.c) $(TESTUPNPDESCGENOBJS:.o=.c) \ testgetifstats.c testupnppermissions.c testgetifaddr.c \ testgetroute.c miniupnpdctl.c 2>/dev/null # DO NOT DELETE miniupnpd.o: config.h macros.h upnpglobalvars.h upnppermissions.h miniupnpd.o: miniupnpdtypes.h upnphttp.h upnpdescgen.h miniupnpdpath.h miniupnpd.o: getifaddr.h upnpsoap.h options.h minissdp.h upnpredirect.h miniupnpd.o: upnppinhole.h daemonize.h upnpevents.h natpmp.h commonrdr.h miniupnpd.o: upnputils.h ifacewatcher.h upnphttp.o: config.h upnphttp.h upnpdescgen.h miniupnpdpath.h upnpsoap.h upnphttp.o: upnpevents.h upnputils.h upnpdescgen.o: config.h getifaddr.h upnpredirect.h upnpdescgen.h upnpdescgen.o: miniupnpdpath.h upnpglobalvars.h upnppermissions.h upnpdescgen.o: miniupnpdtypes.h upnpdescstrings.h upnpurns.h getconnstatus.h upnpsoap.o: macros.h config.h upnpglobalvars.h upnppermissions.h upnpsoap.o: miniupnpdtypes.h upnphttp.h upnpsoap.h upnpreplyparse.h upnpsoap.o: upnpredirect.h upnppinhole.h getifaddr.h getifstats.h upnpsoap.o: getconnstatus.h upnpurns.h upnpreplyparse.o: upnpreplyparse.h minixml.h minixml.o: minixml.h upnpredirect.o: macros.h config.h upnpredirect.h upnpglobalvars.h upnpredirect.o: upnppermissions.h miniupnpdtypes.h upnpevents.h upnpredirect.o: netfilter/iptcrdr.h commonrdr.h getifaddr.o: config.h getifaddr.h daemonize.o: daemonize.h config.h upnpglobalvars.o: config.h upnpglobalvars.h upnppermissions.h upnpglobalvars.o: miniupnpdtypes.h options.o: config.h options.h upnppermissions.h upnpglobalvars.h options.o: miniupnpdtypes.h upnppermissions.o: config.h upnppermissions.h minissdp.o: config.h upnpdescstrings.h miniupnpdpath.h upnphttp.h minissdp.o: upnpglobalvars.h upnppermissions.h miniupnpdtypes.h minissdp.h minissdp.o: upnputils.h getroute.h codelength.h natpmp.o: macros.h config.h natpmp.h upnpglobalvars.h upnppermissions.h natpmp.o: miniupnpdtypes.h getifaddr.h upnpredirect.h commonrdr.h upnputils.h upnpevents.o: config.h upnpevents.h miniupnpdpath.h upnpglobalvars.h upnpevents.o: upnppermissions.h miniupnpdtypes.h upnpdescgen.h upnputils.h upnputils.o: config.h upnputils.h upnpglobalvars.h upnppermissions.h upnputils.o: miniupnpdtypes.h getroute.h getconnstatus.o: getconnstatus.h getifaddr.h upnppinhole.o: macros.h config.h upnpredirect.h upnpglobalvars.h upnppinhole.o: upnppermissions.h miniupnpdtypes.h upnpevents.h upnppinhole.o: netfilter/iptpinhole.h linux/getifstats.o: config.h getifstats.h linux/ifacewatcher.o: config.h ifacewatcher.h config.h minissdp.h linux/ifacewatcher.o: miniupnpdtypes.h getifaddr.h upnpglobalvars.h linux/ifacewatcher.o: upnppermissions.h natpmp.h linux/getroute.o: getroute.h upnputils.h netfilter/iptcrdr.o: macros.h config.h netfilter/iptcrdr.h commonrdr.h netfilter/iptcrdr.o: config.h upnpglobalvars.h upnppermissions.h netfilter/iptcrdr.o: miniupnpdtypes.h netfilter/iptpinhole.o: config.h netfilter/iptpinhole.h upnpglobalvars.h netfilter/iptpinhole.o: upnppermissions.h config.h miniupnpdtypes.h testupnpdescgen.o: macros.h config.h upnpdescgen.h upnpdescgen.o: config.h getifaddr.h upnpredirect.h upnpdescgen.h upnpdescgen.o: miniupnpdpath.h upnpglobalvars.h upnppermissions.h upnpdescgen.o: miniupnpdtypes.h upnpdescstrings.h upnpurns.h getconnstatus.h testgetifstats.o: getifstats.h testupnppermissions.o: upnppermissions.h config.h testgetifaddr.o: getifaddr.h testgetroute.o: getroute.h upnputils.h upnpglobalvars.h upnppermissions.h testgetroute.o: config.h miniupnpdtypes.h miniupnpdctl.o: macros.h miniupnpd-1.8.20130730/upnphttp.c010064400017500000024000000616251210471677000155120ustar00nanardstaff/* $Id: upnphttp.c,v 1.86 2013/02/07 10:26:07 nanard Exp $ */ /* Project : miniupnp * Website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * Author : Thomas Bernard * Copyright (c) 2005-2012 Thomas Bernard * This software is subject to the conditions detailed in the * LICENCE file included in this distribution. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #ifdef ENABLE_HTTP_DATE #include #endif #include "upnphttp.h" #include "upnpdescgen.h" #include "miniupnpdpath.h" #include "upnpsoap.h" #include "upnpevents.h" #include "upnputils.h" struct upnphttp * New_upnphttp(int s) { struct upnphttp * ret; if(s<0) return NULL; ret = (struct upnphttp *)malloc(sizeof(struct upnphttp)); if(ret == NULL) return NULL; memset(ret, 0, sizeof(struct upnphttp)); ret->socket = s; if(!set_non_blocking(s)) syslog(LOG_WARNING, "New_upnphttp::set_non_blocking(): %m"); return ret; } void CloseSocket_upnphttp(struct upnphttp * h) { if(close(h->socket) < 0) { syslog(LOG_ERR, "CloseSocket_upnphttp: close(%d): %m", h->socket); } h->socket = -1; h->state = EToDelete; } void Delete_upnphttp(struct upnphttp * h) { if(h) { if(h->socket >= 0) CloseSocket_upnphttp(h); if(h->req_buf) free(h->req_buf); if(h->res_buf) free(h->res_buf); free(h); } } /* parse HttpHeaders of the REQUEST * This function is called after the \r\n\r\n character * sequence has been found in h->req_buf */ static void ParseHttpHeaders(struct upnphttp * h) { char * line; char * colon; char * p; int n; if((h->req_buf == NULL) || (h->req_contentoff <= 0)) return; line = h->req_buf; while(line < (h->req_buf + h->req_contentoff)) { colon = line; while(*colon != ':') { if(*colon == '\r' || *colon == '\n') { colon = NULL; /* no ':' character found on the line */ break; } colon++; } if(colon) { if(strncasecmp(line, "Content-Length", 14)==0) { p = colon; while(*p < '0' || *p > '9') p++; h->req_contentlen = atoi(p); if(h->req_contentlen < 0) { syslog(LOG_WARNING, "ParseHttpHeaders() invalid Content-Length %d", h->req_contentlen); h->req_contentlen = 0; } /*printf("*** Content-Lenght = %d ***\n", h->req_contentlen); printf(" readbufflen=%d contentoff = %d\n", h->req_buflen, h->req_contentoff);*/ } else if(strncasecmp(line, "SOAPAction", 10)==0) { p = colon; n = 0; while(*p == ':' || *p == ' ' || *p == '\t') p++; while(p[n]>=' ') n++; if((p[0] == '"' && p[n-1] == '"') || (p[0] == '\'' && p[n-1] == '\'')) { p++; n -= 2; } h->req_soapActionOff = p - h->req_buf; h->req_soapActionLen = n; } else if(strncasecmp(line, "accept-language", 15) == 0) { p = colon; n = 0; while(*p == ':' || *p == ' ' || *p == '\t') p++; while(p[n]>=' ') n++; syslog(LOG_DEBUG, "accept-language HTTP header : '%.*s'", n, p); /* keep only the 1st accepted language */ n = 0; while(p[n]>' ' && p[n] != ',') n++; if(n >= (int)sizeof(h->accept_language)) n = (int)sizeof(h->accept_language) - 1; memcpy(h->accept_language, p, n); h->accept_language[n] = '\0'; } else if(strncasecmp(line, "expect", 6) == 0) { p = colon; n = 0; while(*p == ':' || *p == ' ' || *p == '\t') p++; while(p[n]>=' ') n++; if(strncasecmp(p, "100-continue", 12) == 0) { h->respflags |= FLAG_CONTINUE; syslog(LOG_DEBUG, "\"Expect: 100-Continue\" header detected"); } } #ifdef ENABLE_EVENTS else if(strncasecmp(line, "Callback", 8)==0) { p = colon; while(*p != '<' && *p != '\r' ) p++; n = 0; while(p[n] != '>' && p[n] != '\r' ) n++; h->req_CallbackOff = p + 1 - h->req_buf; h->req_CallbackLen = MAX(0, n - 1); } else if(strncasecmp(line, "SID", 3)==0) { p = colon + 1; while(isspace(*p)) p++; n = 0; while(!isspace(p[n])) n++; h->req_SIDOff = p - h->req_buf; h->req_SIDLen = n; } /* Timeout: Seconds-nnnn */ /* TIMEOUT Recommended. Requested duration until subscription expires, either number of seconds or infinite. Recommendation by a UPnP Forum working committee. Defined by UPnP vendor. Consists of the keyword "Second-" followed (without an intervening space) by either an integer or the keyword "infinite". */ else if(strncasecmp(line, "Timeout", 7)==0) { p = colon + 1; while(isspace(*p)) p++; if(strncasecmp(p, "Second-", 7)==0) { h->req_Timeout = atoi(p+7); } } #ifdef UPNP_STRICT else if(strncasecmp(line, "nt", 2)==0) { p = colon + 1; while(isspace(*p)) p++; n = 0; while(!isspace(p[n])) n++; h->req_NTOff = p - h->req_buf; h->req_NTLen = n; } #endif #endif } /* the loop below won't run off the end of the buffer * because the buffer is guaranteed to contain the \r\n\r\n * character sequence */ while(!(line[0] == '\r' && line[1] == '\n')) line++; line += 2; } } /* very minimalistic 404 error message */ static void Send404(struct upnphttp * h) { static const char body404[] = "404 Not Found" "

Not Found

The requested URL was not found" " on this server.\r\n"; h->respflags = FLAG_HTML; BuildResp2_upnphttp(h, 404, "Not Found", body404, sizeof(body404) - 1); SendRespAndClose_upnphttp(h); } static void Send405(struct upnphttp * h) { static const char body405[] = "405 Method Not Allowed" "

Method Not Allowed

The HTTP Method " "is not allowed on this resource.\r\n"; h->respflags |= FLAG_HTML; BuildResp2_upnphttp(h, 405, "Method Not Allowed", body405, sizeof(body405) - 1); SendRespAndClose_upnphttp(h); } /* very minimalistic 501 error message */ static void Send501(struct upnphttp * h) { static const char body501[] = "501 Not Implemented" "

Not Implemented

The HTTP Method " "is not implemented by this server.\r\n"; h->respflags = FLAG_HTML; BuildResp2_upnphttp(h, 501, "Not Implemented", body501, sizeof(body501) - 1); SendRespAndClose_upnphttp(h); } /* findendheaders() find the \r\n\r\n character sequence and * return a pointer to it. * It returns NULL if not found */ static const char * findendheaders(const char * s, int len) { while(len-->3) { if(s[0]=='\r' && s[1]=='\n' && s[2]=='\r' && s[3]=='\n') return s; s++; } return NULL; } #ifdef HAS_DUMMY_SERVICE static void sendDummyDesc(struct upnphttp * h) { static const char xml_desc[] = "\r\n" "" " " " 1" " 0" " " " " " " "\r\n"; BuildResp_upnphttp(h, xml_desc, sizeof(xml_desc)-1); SendRespAndClose_upnphttp(h); } #endif /* Sends the description generated by the parameter */ static void sendXMLdesc(struct upnphttp * h, char * (f)(int *)) { char * desc; int len; desc = f(&len); if(!desc) { static const char error500[] = "Error 500" "Internal Server Error\r\n"; syslog(LOG_ERR, "Failed to generate XML description"); h->respflags = FLAG_HTML; BuildResp2_upnphttp(h, 500, "Internal Server Error", error500, sizeof(error500)-1); } else { BuildResp_upnphttp(h, desc, len); } SendRespAndClose_upnphttp(h); free(desc); } /* ProcessHTTPPOST_upnphttp() * executes the SOAP query if it is possible */ static void ProcessHTTPPOST_upnphttp(struct upnphttp * h) { if((h->req_buflen - h->req_contentoff) >= h->req_contentlen) { /* the request body is received */ if(h->req_soapActionOff > 0) { /* we can process the request */ syslog(LOG_INFO, "SOAPAction: %.*s", h->req_soapActionLen, h->req_buf + h->req_soapActionOff); ExecuteSoapAction(h, h->req_buf + h->req_soapActionOff, h->req_soapActionLen); } else { static const char err400str[] = "Bad request"; syslog(LOG_INFO, "No SOAPAction in HTTP headers"); h->respflags = FLAG_HTML; BuildResp2_upnphttp(h, 400, "Bad Request", err400str, sizeof(err400str) - 1); SendRespAndClose_upnphttp(h); } } else if(h->respflags & FLAG_CONTINUE) { /* Sending the 100 Continue response */ if(!h->res_buf) { h->res_buf = malloc(256); h->res_buf_alloclen = 256; } h->res_buflen = snprintf(h->res_buf, h->res_buf_alloclen, "%s 100 Continue\r\n\r\n", h->HttpVer); h->res_sent = 0; h->state = ESendingContinue; if(SendResp_upnphttp(h)) h->state = EWaitingForHttpContent; } else { /* waiting for remaining data */ h->state = EWaitingForHttpContent; } } #ifdef ENABLE_EVENTS /** * returns 0 if the callback header value is not valid * 1 if it is valid. */ static int checkCallbackURL(struct upnphttp * h) { char addrstr[48]; int ipv6; const char * p; unsigned int i; if(h->req_CallbackOff <= 0 || h->req_CallbackLen < 8) return 0; if(memcmp(h->req_buf + h->req_CallbackOff, "http://", 7) != 0) return 0; ipv6 = 0; i = 0; p = h->req_buf + h->req_CallbackOff + 7; if(*p == '[') { p++; ipv6 = 1; while(*p != ']' && i < (sizeof(addrstr)-1) && p < (h->req_buf + h->req_CallbackOff + h->req_CallbackLen)) addrstr[i++] = *(p++); } else { while(*p != '/' && *p != ':' && i < (sizeof(addrstr)-1) && p < (h->req_buf + h->req_CallbackOff + h->req_CallbackLen)) addrstr[i++] = *(p++); } addrstr[i] = '\0'; if(ipv6) { struct in6_addr addr; if(inet_pton(AF_INET6, addrstr, &addr) <= 0) return 0; #ifdef ENABLE_IPV6 if(!h->ipv6 || (0!=memcmp(&addr, &(h->clientaddr_v6), sizeof(struct in6_addr)))) return 0; #else return 0; #endif } else { struct in_addr addr; if(inet_pton(AF_INET, addrstr, &addr) <= 0) return 0; #ifdef ENABLE_IPV6 if(h->ipv6) { if(!IN6_IS_ADDR_V4MAPPED(&(h->clientaddr_v6))) return 0; if(0!=memcmp(&addr, ((const char *)&(h->clientaddr_v6) + 12), 4)) return 0; } else { if(0!=memcmp(&addr, &(h->clientaddr), sizeof(struct in_addr))) return 0; } #else if(0!=memcmp(&addr, &(h->clientaddr), sizeof(struct in_addr))) return 0; #endif } return 1; } static void ProcessHTTPSubscribe_upnphttp(struct upnphttp * h, const char * path) { const char * sid; syslog(LOG_DEBUG, "ProcessHTTPSubscribe %s", path); syslog(LOG_DEBUG, "Callback '%.*s' Timeout=%d", h->req_CallbackLen, h->req_buf + h->req_CallbackOff, h->req_Timeout); syslog(LOG_DEBUG, "SID '%.*s'", h->req_SIDLen, h->req_buf + h->req_SIDOff); if((h->req_CallbackOff <= 0) && (h->req_SIDOff <= 0)) { /* Missing or invalid CALLBACK : 412 Precondition Failed. * If CALLBACK header is missing or does not contain a valid HTTP URL, * the publisher must respond with HTTP error 412 Precondition Failed*/ BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0); SendRespAndClose_upnphttp(h); } else { /* - add to the subscriber list * - respond HTTP/x.x 200 OK * - Send the initial event message */ /* Server:, SID:; Timeout: Second-(xx|infinite) */ /* Check that the callback URL is on the same IP as * the request, and not on the internet, nor on ourself (DOS attack ?) */ if(h->req_CallbackOff > 0) { #ifdef UPNP_STRICT /* SID: and Callback: are incompatible */ if(h->req_SIDOff > 0) { syslog(LOG_WARNING, "Both Callback: and SID: in SUBSCRIBE"); BuildResp2_upnphttp(h, 400, "Incompatible header fields", 0, 0); /* "NT: upnp:event" header is mandatory */ } else if(h->req_NTOff <= 0 || h->req_NTLen != 10 || 0 != memcmp("upnp:event", h->req_buf + h->req_NTOff, 10)) { syslog(LOG_WARNING, "Invalid NT in SUBSCRIBE %.*s", h->req_NTLen, h->req_buf + h->req_NTOff); BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0); } else #endif if(checkCallbackURL(h)) { sid = upnpevents_addSubscriber(path, h->req_buf + h->req_CallbackOff, h->req_CallbackLen, h->req_Timeout); h->respflags = FLAG_TIMEOUT; if(sid) { syslog(LOG_DEBUG, "generated sid=%s", sid); h->respflags |= FLAG_SID; h->res_SID = sid; } BuildResp_upnphttp(h, 0, 0); } else { syslog(LOG_WARNING, "Invalid Callback in SUBSCRIBE %.*s", h->req_CallbackLen, h->req_buf + h->req_CallbackOff); BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0); } } else { /* subscription renew */ /* Invalid SID 412 Precondition Failed. If a SID does not correspond to a known, un-expired subscription, the publisher must respond with HTTP error 412 Precondition Failed. */ #ifdef UPNP_STRICT /* SID: and NT: headers are incompatibles */ if(h->req_NTOff > 0) { syslog(LOG_WARNING, "Both NT: and SID: in SUBSCRIBE"); BuildResp2_upnphttp(h, 400, "Incompatible header fields", 0, 0); } else #endif if(renewSubscription(h->req_buf + h->req_SIDOff, h->req_SIDLen, h->req_Timeout) < 0) { BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0); } else { h->respflags = FLAG_TIMEOUT; BuildResp_upnphttp(h, 0, 0); } } SendRespAndClose_upnphttp(h); } } static void ProcessHTTPUnSubscribe_upnphttp(struct upnphttp * h, const char * path) { syslog(LOG_DEBUG, "ProcessHTTPUnSubscribe %s", path); syslog(LOG_DEBUG, "SID '%.*s'", h->req_SIDLen, h->req_buf + h->req_SIDOff); /* Remove from the list */ #ifdef UPNP_STRICT if(h->req_SIDOff <= 0 || h->req_SIDLen == 0) { /* SID: header missing or empty */ BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0); } else if(h->req_CallbackOff > 0 || h->req_NTOff > 0) { /* no NT: or Callback: header must be present */ BuildResp2_upnphttp(h, 400, "Incompatible header fields", 0, 0); } else #endif if(upnpevents_removeSubscriber(h->req_buf + h->req_SIDOff, h->req_SIDLen) < 0) { BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0); } else { BuildResp_upnphttp(h, 0, 0); } SendRespAndClose_upnphttp(h); } #endif /* Parse and process Http Query * called once all the HTTP headers have been received, * so it is guaranteed that h->req_buf contains the \r\n\r\n * character sequence */ static void ProcessHttpQuery_upnphttp(struct upnphttp * h) { static const struct { const char * path; char * (* f)(int *); } path_desc[] = { { ROOTDESC_PATH, genRootDesc}, { WANIPC_PATH, genWANIPCn}, { WANCFG_PATH, genWANCfg}, #ifdef HAS_DUMMY_SERVICE { DUMMY_PATH, NULL}, #endif #ifdef ENABLE_L3F_SERVICE { L3F_PATH, genL3F}, #endif #ifdef ENABLE_6FC_SERVICE { WANIP6FC_PATH, gen6FC}, #endif #ifdef ENABLE_DP_SERVICE { DP_PATH, genDP}, #endif { NULL, NULL} }; char HttpCommand[16]; char HttpUrl[128]; char * HttpVer; char * p; int i; p = h->req_buf; if(!p) return; /* note : checking (*p != '\r') is enough to avoid runing off the * end of the buffer, because h->req_buf is guaranteed to contain * the \r\n\r\n character sequence */ for(i = 0; i<15 && *p != ' ' && *p != '\r'; i++) HttpCommand[i] = *(p++); HttpCommand[i] = '\0'; while(*p==' ') p++; for(i = 0; i<127 && *p != ' ' && *p != '\r'; i++) HttpUrl[i] = *(p++); HttpUrl[i] = '\0'; while(*p==' ') p++; HttpVer = h->HttpVer; for(i = 0; i<15 && *p != '\r'; i++) HttpVer[i] = *(p++); HttpVer[i] = '\0'; syslog(LOG_INFO, "HTTP REQUEST : %s %s (%s)", HttpCommand, HttpUrl, HttpVer); ParseHttpHeaders(h); if(strcmp("POST", HttpCommand) == 0) { h->req_command = EPost; ProcessHTTPPOST_upnphttp(h); } else if(strcmp("GET", HttpCommand) == 0) { h->req_command = EGet; for(i=0; path_desc[i].path; i++) { if(strcasecmp(path_desc[i].path, HttpUrl) == 0) { if(path_desc[i].f) sendXMLdesc(h, path_desc[i].f); else #ifdef HAS_DUMMY_SERVICE sendDummyDesc(h); #else continue; #endif return; } } if(0 == memcmp(HttpUrl, "/ctl/", 5)) { /* 405 Method Not Allowed * Allow: POST */ h->respflags = FLAG_ALLOW_POST; Send405(h); return; } #ifdef ENABLE_EVENTS if(0 == memcmp(HttpUrl, "/evt/", 5)) { /* 405 Method Not Allowed * Allow: SUBSCRIBE, UNSUBSCRIBE */ h->respflags = FLAG_ALLOW_SUB_UNSUB; Send405(h); return; } #endif syslog(LOG_NOTICE, "%s not found, responding ERROR 404", HttpUrl); Send404(h); } #ifdef ENABLE_EVENTS else if(strcmp("SUBSCRIBE", HttpCommand) == 0) { h->req_command = ESubscribe; ProcessHTTPSubscribe_upnphttp(h, HttpUrl); } else if(strcmp("UNSUBSCRIBE", HttpCommand) == 0) { h->req_command = EUnSubscribe; ProcessHTTPUnSubscribe_upnphttp(h, HttpUrl); } #else else if(strcmp("SUBSCRIBE", HttpCommand) == 0) { syslog(LOG_NOTICE, "SUBSCRIBE not implemented. ENABLE_EVENTS compile option disabled"); Send501(h); } #endif else { syslog(LOG_NOTICE, "Unsupported HTTP Command %s", HttpCommand); Send501(h); } } void Process_upnphttp(struct upnphttp * h) { char * h_tmp; char buf[2048]; int n; if(!h) return; switch(h->state) { case EWaitingForHttpRequest: n = recv(h->socket, buf, sizeof(buf), 0); if(n<0) { if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) { syslog(LOG_ERR, "recv (state0): %m"); h->state = EToDelete; } /* if errno is EAGAIN, EWOULDBLOCK or EINTR, try again later */ } else if(n==0) { syslog(LOG_WARNING, "HTTP Connection closed unexpectedly"); h->state = EToDelete; } else { const char * endheaders; /* if 1st arg of realloc() is null, * realloc behaves the same as malloc() */ h_tmp = (char *)realloc(h->req_buf, n + h->req_buflen + 1); if (h_tmp == NULL) { syslog(LOG_WARNING, "Unable to allocate new memory for h->req_buf)"); h->state = EToDelete; } else { h->req_buf = h_tmp; memcpy(h->req_buf + h->req_buflen, buf, n); h->req_buflen += n; h->req_buf[h->req_buflen] = '\0'; } /* search for the string "\r\n\r\n" */ endheaders = findendheaders(h->req_buf, h->req_buflen); if(endheaders) { /* at this point, the request buffer (h->req_buf) * is guaranteed to contain the \r\n\r\n character sequence */ h->req_contentoff = endheaders - h->req_buf + 4; ProcessHttpQuery_upnphttp(h); } } break; case EWaitingForHttpContent: n = recv(h->socket, buf, sizeof(buf), 0); if(n<0) { if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) { syslog(LOG_ERR, "recv (state1): %m"); h->state = EToDelete; } /* if errno is EAGAIN, EWOULDBLOCK or EINTR, try again later */ } else if(n==0) { syslog(LOG_WARNING, "HTTP Connection closed inexpectedly"); h->state = EToDelete; } else { void * tmp = realloc(h->req_buf, n + h->req_buflen); if(!tmp) { syslog(LOG_ERR, "memory allocation error %m"); h->state = EToDelete; } else { h->req_buf = tmp; memcpy(h->req_buf + h->req_buflen, buf, n); h->req_buflen += n; if((h->req_buflen - h->req_contentoff) >= h->req_contentlen) { ProcessHTTPPOST_upnphttp(h); } } } break; case ESendingContinue: if(SendResp_upnphttp(h)) h->state = EWaitingForHttpContent; break; case ESendingAndClosing: SendRespAndClose_upnphttp(h); break; default: syslog(LOG_WARNING, "Unexpected state: %d", h->state); } } static const char httpresphead[] = "%s %d %s\r\n" "Content-Type: %s\r\n" "Connection: close\r\n" "Content-Length: %d\r\n" "Server: " MINIUPNPD_SERVER_STRING "\r\n" "Ext:\r\n" ; /*"\r\n";*/ /* "\n" "" "" "" ""; */ /* with response code and response message * also allocate enough memory */ void BuildHeader_upnphttp(struct upnphttp * h, int respcode, const char * respmsg, int bodylen) { int templen; if(!h->res_buf || h->res_buf_alloclen < ((int)sizeof(httpresphead) + 256 + bodylen)) { if(h->res_buf) free(h->res_buf); templen = sizeof(httpresphead) + 256 + bodylen; h->res_buf = (char *)malloc(templen); if(!h->res_buf) { syslog(LOG_ERR, "malloc error in BuildHeader_upnphttp()"); return; } h->res_buf_alloclen = templen; } h->res_sent = 0; h->res_buflen = snprintf(h->res_buf, h->res_buf_alloclen, httpresphead, h->HttpVer, respcode, respmsg, (h->respflags&FLAG_HTML)?"text/html":"text/xml; charset=\"utf-8\"", bodylen); /* Content-Type MUST be 'text/xml; charset="utf-8"' according to UDA v1.1 */ /* Content-Type MUST be 'text/xml' according to UDA v1.0 */ /* Additional headers */ #ifdef ENABLE_HTTP_DATE { char http_date[64]; time_t t; struct tm tm; time(&t); gmtime_r(&t, &tm); /* %a and %b depend on locale */ strftime(http_date, sizeof(http_date), "%a, %d %b %Y %H:%M:%S GMT", &tm); h->res_buflen += snprintf(h->res_buf + h->res_buflen, h->res_buf_alloclen - h->res_buflen, "Date: %s\r\n", http_date); } #endif #ifdef ENABLE_EVENTS if(h->respflags & FLAG_TIMEOUT) { h->res_buflen += snprintf(h->res_buf + h->res_buflen, h->res_buf_alloclen - h->res_buflen, "Timeout: Second-"); if(h->req_Timeout) { h->res_buflen += snprintf(h->res_buf + h->res_buflen, h->res_buf_alloclen - h->res_buflen, "%d\r\n", h->req_Timeout); } else { h->res_buflen += snprintf(h->res_buf + h->res_buflen, h->res_buf_alloclen - h->res_buflen, "infinite\r\n"); } } if(h->respflags & FLAG_SID) { h->res_buflen += snprintf(h->res_buf + h->res_buflen, h->res_buf_alloclen - h->res_buflen, "SID: %s\r\n", h->res_SID); } #endif if(h->respflags & FLAG_ALLOW_POST) { h->res_buflen += snprintf(h->res_buf + h->res_buflen, h->res_buf_alloclen - h->res_buflen, "Allow: %s\r\n", "POST"); } else if(h->respflags & FLAG_ALLOW_SUB_UNSUB) { h->res_buflen += snprintf(h->res_buf + h->res_buflen, h->res_buf_alloclen - h->res_buflen, "Allow: %s\r\n", "SUBSCRIBE, UNSUBSCRIBE"); } if(h->accept_language[0] != '\0') { /* defaulting to "en" */ h->res_buflen += snprintf(h->res_buf + h->res_buflen, h->res_buf_alloclen - h->res_buflen, "Content-Language: %s\r\n", h->accept_language[0] == '*' ? "en" : h->accept_language); } h->res_buf[h->res_buflen++] = '\r'; h->res_buf[h->res_buflen++] = '\n'; if(h->res_buf_alloclen < (h->res_buflen + bodylen)) { char * tmp; tmp = (char *)realloc(h->res_buf, (h->res_buflen + bodylen)); if(tmp) { h->res_buf = tmp; h->res_buf_alloclen = h->res_buflen + bodylen; } else { syslog(LOG_ERR, "realloc error in BuildHeader_upnphttp()"); } } } void BuildResp2_upnphttp(struct upnphttp * h, int respcode, const char * respmsg, const char * body, int bodylen) { BuildHeader_upnphttp(h, respcode, respmsg, bodylen); if(body) memcpy(h->res_buf + h->res_buflen, body, bodylen); h->res_buflen += bodylen; } /* responding 200 OK ! */ void BuildResp_upnphttp(struct upnphttp * h, const char * body, int bodylen) { BuildResp2_upnphttp(h, 200, "OK", body, bodylen); } int SendResp_upnphttp(struct upnphttp * h) { ssize_t n; while (h->res_sent < h->res_buflen) { n = send(h->socket, h->res_buf + h->res_sent, h->res_buflen - h->res_sent, 0); if(n<0) { if(errno == EINTR) continue; /* try again immediatly */ if(errno == EAGAIN || errno == EWOULDBLOCK) { /* try again later */ return 0; } syslog(LOG_ERR, "send(res_buf): %m"); break; /* avoid infinite loop */ } else if(n == 0) { syslog(LOG_ERR, "send(res_buf): %d bytes sent (out of %d)", h->res_sent, h->res_buflen); break; } else { h->res_sent += n; } } return 1; /* finished */ } void SendRespAndClose_upnphttp(struct upnphttp * h) { ssize_t n; while (h->res_sent < h->res_buflen) { n = send(h->socket, h->res_buf + h->res_sent, h->res_buflen - h->res_sent, 0); if(n<0) { if(errno == EINTR) continue; /* try again immediatly */ if(errno == EAGAIN || errno == EWOULDBLOCK) { /* try again later */ h->state = ESendingAndClosing; return; } syslog(LOG_ERR, "send(res_buf): %m"); break; /* avoid infinite loop */ } else if(n == 0) { syslog(LOG_ERR, "send(res_buf): %d bytes sent (out of %d)", h->res_sent, h->res_buflen); break; } else { h->res_sent += n; } } CloseSocket_upnphttp(h); } miniupnpd-1.8.20130730/miniupnpd.c010064400017500000024000001374311217572255200156350ustar00nanardstaff/* $Id: miniupnpd.c,v 1.176 2013/06/13 13:21:29 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" /* Experimental support for NFQUEUE interfaces */ #ifdef ENABLE_NFQUEUE /* apt-get install libnetfilter-queue-dev */ #include #include #if 0 #include /* Defines verdicts (NF_ACCEPT, etc) */ #endif #include #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(sun) #include #else /* for BSD's sysctl */ #include #endif /* unix sockets */ #ifdef USE_MINIUPNPDCTL #include #endif #include "macros.h" #include "upnpglobalvars.h" #include "upnphttp.h" #include "upnpdescgen.h" #include "miniupnpdpath.h" #include "getifaddr.h" #include "upnpsoap.h" #include "options.h" #include "minissdp.h" #include "upnpredirect.h" #include "upnppinhole.h" #include "miniupnpdtypes.h" #include "daemonize.h" #include "upnpevents.h" #ifdef ENABLE_NATPMP #include "natpmp.h" #endif #include "commonrdr.h" #include "upnputils.h" #ifdef USE_IFACEWATCHER #include "ifacewatcher.h" #endif #ifdef ENABLE_6FC_SERVICE #ifdef USE_NETFILTER void init_iptpinhole(void); #endif #endif #ifndef DEFAULT_CONFIG #define DEFAULT_CONFIG "/etc/miniupnpd.conf" #endif #ifdef USE_MINIUPNPDCTL struct ctlelem { int socket; LIST_ENTRY(ctlelem) entries; }; #endif #ifdef ENABLE_NFQUEUE /* globals */ static struct nfq_handle *nfqHandle; static struct sockaddr_in ssdp; /* prototypes */ static int nfqueue_cb( struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfa, void *data) ; int identify_ip_protocol (char *payload); int get_udp_dst_port (char *payload); #endif /* variables used by signals */ static volatile sig_atomic_t quitting = 0; volatile sig_atomic_t should_send_public_address_change_notif = 0; /* OpenAndConfHTTPSocket() : * setup the socket used to handle incoming HTTP connections. */ static int OpenAndConfHTTPSocket(unsigned short port) { int s; int i = 1; #ifdef ENABLE_IPV6 struct sockaddr_in6 listenname; #else struct sockaddr_in listenname; #endif socklen_t listenname_len; if( (s = socket( #ifdef ENABLE_IPV6 PF_INET6, #else PF_INET, #endif SOCK_STREAM, 0)) < 0) { syslog(LOG_ERR, "socket(http): %m"); return -1; } if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0) { syslog(LOG_WARNING, "setsockopt(http, SO_REUSEADDR): %m"); } #if 0 /* enable this to force IPV6 only for IPV6 socket. * see http://www.ietf.org/rfc/rfc3493.txt section 5.3 */ if(setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &i, sizeof(i)) < 0) { syslog(LOG_WARNING, "setsockopt(http, IPV6_V6ONLY): %m"); } #endif if(!set_non_blocking(s)) { syslog(LOG_WARNING, "set_non_blocking(http): %m"); } #ifdef ENABLE_IPV6 memset(&listenname, 0, sizeof(struct sockaddr_in6)); listenname.sin6_family = AF_INET6; listenname.sin6_port = htons(port); listenname.sin6_addr = in6addr_any; listenname_len = sizeof(struct sockaddr_in6); #else listenname.sin_family = AF_INET; listenname.sin_port = htons(port); listenname.sin_addr.s_addr = htonl(INADDR_ANY); listenname_len = sizeof(struct sockaddr_in); #endif if(bind(s, (struct sockaddr *)&listenname, listenname_len) < 0) { syslog(LOG_ERR, "bind(http): %m"); close(s); return -1; } if(listen(s, 5) < 0) { syslog(LOG_ERR, "listen(http): %m"); close(s); return -1; } return s; } #ifdef ENABLE_NFQUEUE int identify_ip_protocol(char *payload) { return payload[9]; } /* * This function returns the destination port of the captured packet UDP */ int get_udp_dst_port(char *payload) { char *pkt_data_ptr = NULL; pkt_data_ptr = payload + sizeof(struct ip); /* Cast the UDP Header from the raw packet */ struct udphdr *udp = (struct udphdr *) pkt_data_ptr; /* get the dst port of the packet */ return(ntohs(udp->dest)); } static int OpenAndConfNFqueue(){ struct nfq_q_handle *myQueue; struct nfnl_handle *netlinkHandle; int fd = 0, e = 0; inet_pton(AF_INET, "239.255.255.250", &(ssdp.sin_addr)); /* Get a queue connection handle from the module */ if (!(nfqHandle = nfq_open())) { syslog(LOG_ERR, "Error in nfq_open(): %m"); return -1; } /* Unbind the handler from processing any IP packets Not totally sure why this is done, or if it's necessary... */ if ((e = nfq_unbind_pf(nfqHandle, AF_INET)) < 0) { syslog(LOG_ERR, "Error in nfq_unbind_pf(): %m"); return -1; } /* Bind this handler to process IP packets... */ if (nfq_bind_pf(nfqHandle, AF_INET) < 0) { syslog(LOG_ERR, "Error in nfq_bind_pf(): %m"); return -1; } /* Install a callback on queue -Q */ if (!(myQueue = nfq_create_queue(nfqHandle, nfqueue, &nfqueue_cb, NULL))) { syslog(LOG_ERR, "Error in nfq_create_queue(): %m"); return -1; } /* Turn on packet copy mode */ if (nfq_set_mode(myQueue, NFQNL_COPY_PACKET, 0xffff) < 0) { syslog(LOG_ERR, "Error setting packet copy mode (): %m"); return -1; } netlinkHandle = nfq_nfnlh(nfqHandle); fd = nfnl_fd(netlinkHandle); return fd; } static int nfqueue_cb( struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfa, void *data) { char *pkt; struct nfqnl_msg_packet_hdr *ph; ph = nfq_get_msg_packet_hdr(nfa); if ( ph ) { int id = 0, size = 0; id = ntohl(ph->packet_id); size = nfq_get_payload(nfa, &pkt); struct ip *iph = (struct ip *) pkt; int id_protocol = identify_ip_protocol(pkt); int dport = get_udp_dst_port(pkt); int x = sizeof (struct ip) + sizeof (struct udphdr); /* packets we are interested in are UDP multicast to 239.255.255.250:1900 * and start with a data string M-SEARCH */ if ( (dport == 1900) && (id_protocol == IPPROTO_UDP) && (ssdp.sin_addr.s_addr == iph->ip_dst.s_addr) ) { /* get the index that the packet came in on */ u_int32_t idx = nfq_get_indev(nfa); int i = 0; for ( ;i < n_nfqix ; i++) { if ( nfqix[i] == idx ) { struct udphdr *udp = (struct udphdr *) (pkt + sizeof(struct ip)); char *dd = pkt + x; struct sockaddr_in sendername; sendername.sin_family = AF_INET; sendername.sin_port = udp->source; sendername.sin_addr.s_addr = iph->ip_src.s_addr; /* printf("pkt found %s\n",dd);*/ ProcessSSDPData (sudp, dd, size - x, &sendername, (unsigned short) 5555); } } } nfq_set_verdict(qh, id, NF_ACCEPT, 0, NULL); } else { syslog(LOG_ERR,"nfq_get_msg_packet_hdr failed"); return 1; /* from nfqueue source: 0 = ok, >0 = soft error, <0 hard error */ } return 0; } static void ProcessNFQUEUE(int fd){ char buf[4096]; socklen_t len_r; struct sockaddr_in sendername; len_r = sizeof(struct sockaddr_in); int res = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sendername, &len_r); nfq_handle_packet(nfqHandle, buf, res); } #endif /* Functions used to communicate with miniupnpdctl */ #ifdef USE_MINIUPNPDCTL static int OpenAndConfCtlUnixSocket(const char * path) { struct sockaddr_un localun; int s; s = socket(AF_UNIX, SOCK_STREAM, 0); localun.sun_family = AF_UNIX; strncpy(localun.sun_path, path, sizeof(localun.sun_path)); if(bind(s, (struct sockaddr *)&localun, sizeof(struct sockaddr_un)) < 0) { syslog(LOG_ERR, "bind(sctl): %m"); close(s); s = -1; } else if(listen(s, 5) < 0) { syslog(LOG_ERR, "listen(sctl): %m"); close(s); s = -1; } return s; } static void write_upnphttp_details(int fd, struct upnphttp * e) { char buffer[256]; int len; write(fd, "HTTP :\n", 7); while(e) { len = snprintf(buffer, sizeof(buffer), "%d %d %s req_buf=%p(%dbytes) res_buf=%p(%dbytes alloc)\n", e->socket, e->state, e->HttpVer, e->req_buf, e->req_buflen, e->res_buf, e->res_buf_alloclen); write(fd, buffer, len); e = e->entries.le_next; } } static void write_ctlsockets_list(int fd, struct ctlelem * e) { char buffer[256]; int len; write(fd, "CTL :\n", 6); while(e) { len = snprintf(buffer, sizeof(buffer), "struct ctlelem: socket=%d\n", e->socket); write(fd, buffer, len); e = e->entries.le_next; } } #ifndef DISABLE_CONFIG_FILE static void write_option_list(int fd) { char buffer[256]; int len; unsigned int i; write(fd, "Options :\n", 10); for(i=0; iifname)) { /* not starting with a digit : suppose it is an interface name */ memcpy(lan_addr->ifname, str, n); lan_addr->ifname[n] = '\0'; if(getifaddr(lan_addr->ifname, lan_addr->str, sizeof(lan_addr->str), &lan_addr->addr, &lan_addr->mask) < 0) goto parselan_error; } else { if(n>15) goto parselan_error; memcpy(lan_addr->str, str, n); lan_addr->str[n] = '\0'; if(!inet_aton(lan_addr->str, &lan_addr->addr)) goto parselan_error; } if(*p == '/') { const char * q = ++p; while(*p && isdigit(*p)) p++; if(*p=='.') { while(*p && (*p=='.' || isdigit(*p))) p++; n = p - q; if(n>15) goto parselan_error; memcpy(tmp, q, n); tmp[n] = '\0'; if(!inet_aton(tmp, &lan_addr->mask)) goto parselan_error; } else { int nbits = atoi(q); if(nbits > 32 || nbits < 0) goto parselan_error; lan_addr->mask.s_addr = htonl(nbits ? (0xffffffffu << (32 - nbits)) : 0); } } else if(lan_addr->mask.s_addr == 0) { /* by default, networks are /24 */ lan_addr->mask.s_addr = htonl(0xffffff00u); } #ifdef MULTIPLE_EXTERNAL_IP /* skip spaces */ while(*p && isspace(*p)) p++; if(*p) { /* parse the exteral ip address to associate with this subnet */ n = 0; while(p[n] && !isspace(*p)) n++; if(n<=15) { memcpy(lan_addr->ext_ip_str, p, n); lan_addr->ext_ip_str[n] = '\0'; if(!inet_aton(lan_addr->ext_ip_str, &lan_addr->ext_ip_addr)) { /* error */ fprintf(stderr, "Error parsing address : %s\n", lan_addr->ext_ip_str); } } } #endif #ifdef ENABLE_IPV6 if(lan_addr->ifname[0] != '\0') { lan_addr->index = if_nametoindex(lan_addr->ifname); if(lan_addr->index == 0) fprintf(stderr, "Cannot get index for network interface %s", lan_addr->ifname); } else { fprintf(stderr, "Warning: please specify LAN network interface by name instead of IPv4 address\n"); } #endif return 0; parselan_error: fprintf(stderr, "Error parsing address/mask (or interface name) : %s\n", str); return -1; } /* fill uuidvalue_wan and uuidvalue_wcd based on uuidvalue_igd */ void complete_uuidvalues(void) { size_t len; len = strlen(uuidvalue_igd); memcpy(uuidvalue_wan, uuidvalue_igd, len+1); switch(uuidvalue_wan[len-1]) { case '9': uuidvalue_wan[len-1] = 'a'; break; case 'f': uuidvalue_wan[len-1] = '0'; break; default: uuidvalue_wan[len-1]++; } memcpy(uuidvalue_wcd, uuidvalue_wan, len+1); switch(uuidvalue_wcd[len-1]) { case '9': uuidvalue_wcd[len-1] = 'a'; break; case 'f': uuidvalue_wcd[len-1] = '0'; break; default: uuidvalue_wcd[len-1]++; } } /* init phase : * 1) read configuration file * 2) read command line arguments * 3) daemonize * 4) open syslog * 5) check and write pid file * 6) set startup time stamp * 7) compute presentation URL * 8) set signal handlers */ static int init(int argc, char * * argv, struct runtime_vars * v) { int i; int pid; int debug_flag = 0; int openlog_option; struct sigaction sa; /*const char * logfilename = 0;*/ const char * presurl = 0; #ifndef DISABLE_CONFIG_FILE int options_flag = 0; const char * optionsfile = DEFAULT_CONFIG; #endif /* DISABLE_CONFIG_FILE */ struct lan_addr_s * lan_addr; struct lan_addr_s * lan_addr2; /* only print usage if -h is used */ for(i=1; iport = -1; v->notify_interval = 30; /* seconds between SSDP announces */ v->clean_ruleset_threshold = 20; v->clean_ruleset_interval = 0; /* interval between ruleset check. 0=disabled */ #ifndef DISABLE_CONFIG_FILE /* read options file first since * command line arguments have final say */ if(readoptionsfile(optionsfile) < 0) { /* only error if file exists or using -f */ if(access(optionsfile, F_OK) == 0 || options_flag) fprintf(stderr, "Error reading configuration file %s\n", optionsfile); } else { for(i=0; i<(int)num_options; i++) { switch(ary_options[i].id) { case UPNPEXT_IFNAME: ext_if_name = ary_options[i].value; break; case UPNPEXT_IP: use_ext_ip_addr = ary_options[i].value; break; case UPNPLISTENING_IP: lan_addr = (struct lan_addr_s *) malloc(sizeof(struct lan_addr_s)); if (lan_addr == NULL) { fprintf(stderr, "malloc(sizeof(struct lan_addr_s)): %m"); break; } if(parselanaddr(lan_addr, ary_options[i].value) != 0) { fprintf(stderr, "can't parse \"%s\" as valid lan address\n", ary_options[i].value); free(lan_addr); break; } LIST_INSERT_HEAD(&lan_addrs, lan_addr, list); break; case UPNPPORT: v->port = atoi(ary_options[i].value); break; case UPNPBITRATE_UP: upstream_bitrate = strtoul(ary_options[i].value, 0, 0); break; case UPNPBITRATE_DOWN: downstream_bitrate = strtoul(ary_options[i].value, 0, 0); break; case UPNPPRESENTATIONURL: presurl = ary_options[i].value; break; case UPNPFRIENDLY_NAME: strncpy(friendly_name, ary_options[i].value, FRIENDLY_NAME_MAX_LEN); friendly_name[FRIENDLY_NAME_MAX_LEN-1] = '\0'; break; #ifdef USE_NETFILTER case UPNPFORWARDCHAIN: miniupnpd_forward_chain = ary_options[i].value; break; case UPNPNATCHAIN: miniupnpd_nat_chain = ary_options[i].value; break; #endif case UPNPNOTIFY_INTERVAL: v->notify_interval = atoi(ary_options[i].value); break; case UPNPSYSTEM_UPTIME: if(strcmp(ary_options[i].value, "yes") == 0) SETFLAG(SYSUPTIMEMASK); /*sysuptime = 1;*/ break; #if defined(USE_PF) || defined(USE_IPF) case UPNPPACKET_LOG: if(strcmp(ary_options[i].value, "yes") == 0) SETFLAG(LOGPACKETSMASK); /*logpackets = 1;*/ break; #endif case UPNPUUID: strncpy(uuidvalue_igd+5, ary_options[i].value, strlen(uuidvalue_igd+5) + 1); complete_uuidvalues(); break; case UPNPSERIAL: strncpy(serialnumber, ary_options[i].value, SERIALNUMBER_MAX_LEN); serialnumber[SERIALNUMBER_MAX_LEN-1] = '\0'; break; case UPNPMODEL_NUMBER: strncpy(modelnumber, ary_options[i].value, MODELNUMBER_MAX_LEN); modelnumber[MODELNUMBER_MAX_LEN-1] = '\0'; break; case UPNPCLEANTHRESHOLD: v->clean_ruleset_threshold = atoi(ary_options[i].value); break; case UPNPCLEANINTERVAL: v->clean_ruleset_interval = atoi(ary_options[i].value); break; #ifdef USE_PF case UPNPANCHOR: anchor_name = ary_options[i].value; break; case UPNPQUEUE: queue = ary_options[i].value; break; case UPNPTAG: tag = ary_options[i].value; break; #endif #ifdef ENABLE_NATPMP case UPNPENABLENATPMP: if(strcmp(ary_options[i].value, "yes") == 0) SETFLAG(ENABLENATPMPMASK); /*enablenatpmp = 1;*/ else if(atoi(ary_options[i].value)) SETFLAG(ENABLENATPMPMASK); /*enablenatpmp = atoi(ary_options[i].value);*/ break; #endif #ifdef PF_ENABLE_FILTER_RULES case UPNPQUICKRULES: if(strcmp(ary_options[i].value, "no") == 0) SETFLAG(PFNOQUICKRULESMASK); break; #endif case UPNPENABLE: if(strcmp(ary_options[i].value, "yes") != 0) CLEARFLAG(ENABLEUPNPMASK); break; case UPNPSECUREMODE: if(strcmp(ary_options[i].value, "yes") == 0) SETFLAG(SECUREMODEMASK); break; #ifdef ENABLE_LEASEFILE case UPNPLEASEFILE: lease_file = ary_options[i].value; break; #endif case UPNPMINISSDPDSOCKET: minissdpdsocketpath = ary_options[i].value; break; default: fprintf(stderr, "Unknown option in file %s\n", optionsfile); } } } #endif /* DISABLE_CONFIG_FILE */ /* command line arguments processing */ for(i=1; inotify_interval = atoi(argv[++i]); else fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]); break; case 'r': if(i+1 < argc) v->clean_ruleset_interval = atoi(argv[++i]); else fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]); break; case 'u': if(i+1 < argc) { strncpy(uuidvalue_igd+5, argv[++i], strlen(uuidvalue_igd+5) + 1); complete_uuidvalues(); } else fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]); break; case 'z': if(i+1 < argc) strncpy(friendly_name, argv[++i], FRIENDLY_NAME_MAX_LEN); else fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]); friendly_name[FRIENDLY_NAME_MAX_LEN-1] = '\0'; break; case 's': if(i+1 < argc) strncpy(serialnumber, argv[++i], SERIALNUMBER_MAX_LEN); else fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]); serialnumber[SERIALNUMBER_MAX_LEN-1] = '\0'; break; case 'm': if(i+1 < argc) strncpy(modelnumber, argv[++i], MODELNUMBER_MAX_LEN); else fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]); modelnumber[MODELNUMBER_MAX_LEN-1] = '\0'; break; #ifdef ENABLE_NATPMP case 'N': /*enablenatpmp = 1;*/ SETFLAG(ENABLENATPMPMASK); break; #endif case 'U': /*sysuptime = 1;*/ SETFLAG(SYSUPTIMEMASK); break; /*case 'l': logfilename = argv[++i]; break;*/ #if defined(USE_PF) || defined(USE_IPF) case 'L': /*logpackets = 1;*/ SETFLAG(LOGPACKETSMASK); break; #endif case 'S': SETFLAG(SECUREMODEMASK); break; case 'i': if(i+1 < argc) ext_if_name = argv[++i]; else fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]); break; #ifdef USE_PF case 'q': if(i+1 < argc) queue = argv[++i]; else fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]); break; case 'T': if(i+1 < argc) tag = argv[++i]; else fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]); break; #endif case 'p': if(i+1 < argc) v->port = atoi(argv[++i]); else #ifdef ENABLE_NFQUEUE case 'Q': if(i+1list.le_next) { if (0 == strncmp(lan_addr2->str, lan_addr->str, 15)) break; } if (lan_addr2 == NULL) LIST_INSERT_HEAD(&lan_addrs, lan_addr, list); } else fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]); #else if(i+2 < argc) { char *val=calloc((strlen(argv[i+1]) + strlen(argv[i+2]) + 1), sizeof(char)); if (val == NULL) { fprintf(stderr, "memory allocation error for listen address storage\n"); break; } sprintf(val, "%s %s", argv[i+1], argv[i+2]); lan_addr = (struct lan_addr_s *) malloc(sizeof(struct lan_addr_s)); if (lan_addr == NULL) { fprintf(stderr, "malloc(sizeof(struct lan_addr_s)): %m"); free(val); break; } if(parselanaddr(lan_addr, val) != 0) { fprintf(stderr, "can't parse \"%s\" as valid lan address\n", val); free(lan_addr); free(val); break; } /* check if we already have this address */ for(lan_addr2 = lan_addrs.lh_first; lan_addr2 != NULL; lan_addr2 = lan_addr2->list.le_next) { if (0 == strncmp(lan_addr2->str, lan_addr->str, 15)) break; } if (lan_addr2 == NULL) LIST_INSERT_HEAD(&lan_addrs, lan_addr, list); free(val); i+=2; } else fprintf(stderr, "Option -%c takes two arguments.\n", argv[i][1]); #endif break; case 'A': if(i+1 < argc) { void * tmp; tmp = realloc(upnppermlist, sizeof(struct upnpperm) * (num_upnpperm+1)); if(tmp == NULL) { fprintf(stderr, "memory allocation error for permission\n"); } else { upnppermlist = tmp; if(read_permission_line(upnppermlist + num_upnpperm, argv[++i]) >= 0) { num_upnpperm++; } else { fprintf(stderr, "Permission rule parsing error :\n%s\n", argv[i]); } } } else fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]); break; case 'f': i++; /* discarding, the config file is already read */ break; default: fprintf(stderr, "Unknown option: %s\n", argv[i]); } } if(!ext_if_name || !lan_addrs.lh_first) { /* bad configuration */ goto print_usage; } if(debug_flag) { pid = getpid(); } else { #ifdef USE_DAEMON if(daemon(0, 0)<0) { perror("daemon()"); } pid = getpid(); #else pid = daemonize(); #endif } openlog_option = LOG_PID|LOG_CONS; if(debug_flag) { openlog_option |= LOG_PERROR; /* also log on stderr */ } openlog("miniupnpd", openlog_option, LOG_MINIUPNPD); if(!debug_flag) { /* speed things up and ignore LOG_INFO and LOG_DEBUG */ setlogmask(LOG_UPTO(LOG_NOTICE)); } if(checkforrunning(pidfilename) < 0) { syslog(LOG_ERR, "MiniUPnPd is already running. EXITING"); return 1; } set_startup_time(GETFLAG(SYSUPTIMEMASK)); /* presentation url */ if(presurl) { strncpy(presentationurl, presurl, PRESENTATIONURL_MAX_LEN); presentationurl[PRESENTATIONURL_MAX_LEN-1] = '\0'; } else { snprintf(presentationurl, PRESENTATIONURL_MAX_LEN, "http://%s/", lan_addrs.lh_first->str); /*"http://%s:%d/", lan_addrs.lh_first->str, 80);*/ } /* set signal handler */ memset(&sa, 0, sizeof(struct sigaction)); sa.sa_handler = sigterm; if(sigaction(SIGTERM, &sa, NULL) < 0) { syslog(LOG_ERR, "Failed to set %s handler. EXITING", "SIGTERM"); return 1; } if(sigaction(SIGINT, &sa, NULL) < 0) { syslog(LOG_ERR, "Failed to set %s handler. EXITING", "SIGINT"); return 1; } sa.sa_handler = SIG_IGN; if(sigaction(SIGPIPE, &sa, NULL) < 0) { syslog(LOG_ERR, "Failed to ignore SIGPIPE signals"); } sa.sa_handler = sigusr1; if(sigaction(SIGUSR1, &sa, NULL) < 0) { syslog(LOG_NOTICE, "Failed to set %s handler", "SIGUSR1"); } if(init_redirect() < 0) { syslog(LOG_ERR, "Failed to init redirection engine. EXITING"); return 1; } #ifdef ENABLE_6FC_SERVICE #ifdef USE_NETFILTER init_iptpinhole(); #endif #endif if(writepidfile(pidfilename, pid) < 0) pidfilename = NULL; #ifdef ENABLE_LEASEFILE /*remove(lease_file);*/ syslog(LOG_INFO, "Reloading rules from lease file"); reload_from_lease_file(); #endif return 0; print_usage: fprintf(stderr, "Usage:\n\t" "%s " #ifndef DISABLE_CONFIG_FILE "[-f config_file] " #endif "[-i ext_ifname] [-o ext_ip]\n" #ifndef MULTIPLE_EXTERNAL_IP "\t\t[-a listening_ip]" #else "\t\t[-a listening_ip ext_ip]" #endif " [-p port] [-d]" #if defined(USE_PF) || defined(USE_IPF) " [-L]" #endif " [-U] [-S]" #ifdef ENABLE_NATPMP " [-N]" #endif "\n" /*"[-l logfile] " not functionnal */ "\t\t[-u uuid] [-s serial] [-m model_number] \n" "\t\t[-t notify_interval] [-P pid_filename] [-z fiendly_name]\n" "\t\t[-B down up] [-w url] [-r clean_ruleset_interval]\n" #ifdef USE_PF "\t\t[-q queue] [-T tag]\n" #endif #ifdef ENABLE_NFQUEUE "\t\t[-Q queue] [-n name]\n" #endif "\t\t[-A \"permission rule\"]\n" "\nNotes:\n\tThere can be one or several listening_ips.\n" "\tNotify interval is in seconds. Default is 30 seconds.\n" "\tDefault pid file is '%s'.\n" "\tDefault config file is '%s'.\n" "\tWith -d miniupnpd will run as a standard program.\n" #if defined(USE_PF) || defined(USE_IPF) "\t-L sets packet log in pf and ipf on.\n" #endif "\t-S sets \"secure\" mode : clients can only add mappings to their own ip\n" "\t-U causes miniupnpd to report system uptime instead " "of daemon uptime.\n" #ifdef ENABLE_NATPMP "\t-N enable NAT-PMP functionality.\n" #endif "\t-B sets bitrates reported by daemon in bits per second.\n" "\t-w sets the presentation url. Default is http address on port 80\n" #ifdef USE_PF "\t-q sets the ALTQ queue in pf.\n" "\t-T sets the tag name in pf.\n" #endif #ifdef ENABLE_NFQUEUE "\t-Q sets the queue number that is used by NFQUEUE.\n" "\t-n sets the name of the interface(s) that packets will arrive on.\n" #endif "\t-A use following syntax for permission rules :\n" "\t (allow|deny) (external port range) ip/mask (internal port range)\n" "\texamples :\n" "\t \"allow 1024-65535 192.168.1.0/24 1024-65535\"\n" "\t \"deny 0-65535 0.0.0.0/0 0-65535\"\n" "\t-h prints this help and quits.\n" "", argv[0], pidfilename, DEFAULT_CONFIG); return 1; } /* === main === */ /* process HTTP or SSDP requests */ int main(int argc, char * * argv) { int i; int shttpl = -1; /* socket for HTTP */ int sudp = -1; /* IP v4 socket for receiving SSDP */ #ifdef ENABLE_IPV6 int sudpv6 = -1; /* IP v6 socket for receiving SSDP */ #endif #ifdef ENABLE_NATPMP int * snatpmp = NULL; #endif #ifdef ENABLE_NFQUEUE int nfqh = -1; #endif #ifdef USE_IFACEWATCHER int sifacewatcher = -1; #endif int * snotify = NULL; int addr_count; LIST_HEAD(httplisthead, upnphttp) upnphttphead; struct upnphttp * e = 0; struct upnphttp * next; fd_set readset; /* for select() */ fd_set writeset; struct timeval timeout, timeofday, lasttimeofday = {0, 0}; int max_fd = -1; #ifdef USE_MINIUPNPDCTL int sctl = -1; LIST_HEAD(ctlstructhead, ctlelem) ctllisthead; struct ctlelem * ectl; struct ctlelem * ectlnext; #endif struct runtime_vars v; /* variables used for the unused-rule cleanup process */ struct rule_state * rule_list = 0; struct timeval checktime = {0, 0}; struct lan_addr_s * lan_addr; #ifdef ENABLE_6FC_SERVICE unsigned int next_pinhole_ts; #endif if(init(argc, argv, &v) != 0) return 1; /* count lan addrs */ addr_count = 0; for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next) addr_count++; if(addr_count > 0) { #ifndef ENABLE_IPV6 snotify = calloc(addr_count, sizeof(int)); #else /* one for IPv4, one for IPv6 */ snotify = calloc(addr_count * 2, sizeof(int)); #endif } #ifdef ENABLE_NATPMP if(addr_count > 0) { snatpmp = malloc(addr_count * sizeof(int)); for(i = 0; i < addr_count; i++) snatpmp[i] = -1; } #endif LIST_INIT(&upnphttphead); #ifdef USE_MINIUPNPDCTL LIST_INIT(&ctllisthead); #endif if( #ifdef ENABLE_NATPMP !GETFLAG(ENABLENATPMPMASK) && #endif !GETFLAG(ENABLEUPNPMASK) ) { syslog(LOG_ERR, "Why did you run me anyway?"); return 0; } syslog(LOG_INFO, "Starting%s%swith external interface %s", #ifdef ENABLE_NATPMP GETFLAG(ENABLENATPMPMASK) ? " NAT-PMP " : " ", #else " ", #endif GETFLAG(ENABLEUPNPMASK) ? "UPnP-IGD " : "", ext_if_name); if(GETFLAG(ENABLEUPNPMASK)) { /* open socket for HTTP connections. Listen on the 1st LAN address */ shttpl = OpenAndConfHTTPSocket((v.port > 0) ? v.port : 0); 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); } syslog(LOG_NOTICE, "HTTP listening on port %d", v.port); #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", ipv6_addr_for_http_with_brackets); } else { memcpy(ipv6_addr_for_http_with_brackets, "[::1]", 6); syslog(LOG_WARNING, "no HTTP IPv6 address"); } #endif /* open socket for SSDP connections */ sudp = OpenAndConfSSDPReceiveSocket(0); if(sudp < 0) { syslog(LOG_NOTICE, "Failed to open socket for receiving SSDP. Trying to use MiniSSDPd"); if(SubmitServicesToMiniSSDPD(lan_addrs.lh_first->str, v.port) < 0) { syslog(LOG_ERR, "Failed to connect to MiniSSDPd. EXITING"); return 1; } } #ifdef ENABLE_IPV6 sudpv6 = OpenAndConfSSDPReceiveSocket(1); if(sudpv6 < 0) { syslog(LOG_WARNING, "Failed to open socket for receiving SSDP (IP v6)."); } #endif /* open socket for sending notifications */ if(OpenAndConfSSDPNotifySockets(snotify) < 0) { syslog(LOG_ERR, "Failed to open sockets for sending SSDP notify " "messages. EXITING"); return 1; } #ifdef USE_IFACEWATCHER /* open socket for kernel notifications about new network interfaces */ if (sudp >= 0) { sifacewatcher = OpenAndConfInterfaceWatchSocket(); if (sifacewatcher < 0) { syslog(LOG_ERR, "Failed to open socket for receiving network interface notifications"); } } #endif } #ifdef ENABLE_NATPMP /* open socket for NAT PMP traffic */ if(GETFLAG(ENABLENATPMPMASK)) { if(OpenAndConfNATPMPSockets(snatpmp) < 0) { syslog(LOG_ERR, "Failed to open sockets for NAT PMP."); } else { syslog(LOG_NOTICE, "Listening for NAT-PMP traffic on port %u", NATPMP_PORT); } #if 0 ScanNATPMPforExpiration(); #endif } #endif /* for miniupnpdctl */ #ifdef USE_MINIUPNPDCTL sctl = OpenAndConfCtlUnixSocket("/var/run/miniupnpd.ctl"); #endif #ifdef ENABLE_NFQUEUE if ( nfqueue != -1 && n_nfqix > 0) { nfqh = OpenAndConfNFqueue(); if(nfqh < 0) { syslog(LOG_ERR, "Failed to open fd for NFQUEUE."); return 1; } else { syslog(LOG_NOTICE, "Opened NFQUEUE %d",nfqueue); } } #endif /* main loop */ while(!quitting) { /* Correct startup_time if it was set with a RTC close to 0 */ if((startup_time<60*60*24) && (time(NULL)>60*60*24)) { set_startup_time(GETFLAG(SYSUPTIMEMASK)); } /* send public address change notifications if needed */ if(should_send_public_address_change_notif) { syslog(LOG_DEBUG, "should send external iface address change notification(s)"); #ifdef ENABLE_NATPMP if(GETFLAG(ENABLENATPMPMASK)) SendNATPMPPublicAddressChangeNotification(snatpmp, addr_count); #endif #ifdef ENABLE_EVENTS if(GETFLAG(ENABLEUPNPMASK)) { upnp_event_var_change_notify(EWanIPC); } #endif should_send_public_address_change_notif = 0; } /* Check if we need to send SSDP NOTIFY messages and do it if * needed */ if(gettimeofday(&timeofday, 0) < 0) { syslog(LOG_ERR, "gettimeofday(): %m"); timeout.tv_sec = v.notify_interval; timeout.tv_usec = 0; } else { /* the comparaison is not very precise but who cares ? */ if(timeofday.tv_sec >= (lasttimeofday.tv_sec + v.notify_interval)) { if (GETFLAG(ENABLEUPNPMASK)) SendSSDPNotifies2(snotify, (unsigned short)v.port, v.notify_interval << 1); memcpy(&lasttimeofday, &timeofday, sizeof(struct timeval)); timeout.tv_sec = v.notify_interval; timeout.tv_usec = 0; } else { timeout.tv_sec = lasttimeofday.tv_sec + v.notify_interval - timeofday.tv_sec; if(timeofday.tv_usec > lasttimeofday.tv_usec) { timeout.tv_usec = 1000000 + lasttimeofday.tv_usec - timeofday.tv_usec; timeout.tv_sec--; } else { timeout.tv_usec = lasttimeofday.tv_usec - timeofday.tv_usec; } } } /* remove unused rules */ if( v.clean_ruleset_interval && (timeofday.tv_sec >= checktime.tv_sec + v.clean_ruleset_interval)) { if(rule_list) { remove_unused_rules(rule_list); rule_list = NULL; } else { rule_list = get_upnp_rules_state_list(v.clean_ruleset_threshold); } memcpy(&checktime, &timeofday, sizeof(struct timeval)); } /* Remove expired port mappings, based on UPnP IGD LeaseDuration * or NAT-PMP lifetime) */ if(nextruletoclean_timestamp && ((unsigned int)timeofday.tv_sec >= nextruletoclean_timestamp)) { syslog(LOG_DEBUG, "cleaning expired Port Mappings"); get_upnp_rules_state_list(0); } if(nextruletoclean_timestamp && ((unsigned int)timeout.tv_sec >= (nextruletoclean_timestamp - timeofday.tv_sec))) { timeout.tv_sec = nextruletoclean_timestamp - timeofday.tv_sec; timeout.tv_usec = 0; syslog(LOG_DEBUG, "setting timeout to %u sec", (unsigned)timeout.tv_sec); } #ifdef ENABLE_NATPMP #if 0 /* Remove expired NAT-PMP mappings */ while(nextnatpmptoclean_timestamp && (timeofday.tv_sec >= nextnatpmptoclean_timestamp + startup_time)) { /*syslog(LOG_DEBUG, "cleaning expired NAT-PMP mappings");*/ if(CleanExpiredNATPMP() < 0) { syslog(LOG_ERR, "CleanExpiredNATPMP() failed"); break; } } if(nextnatpmptoclean_timestamp && timeout.tv_sec >= (nextnatpmptoclean_timestamp + startup_time - timeofday.tv_sec)) { /*syslog(LOG_DEBUG, "setting timeout to %d sec", nextnatpmptoclean_timestamp + startup_time - timeofday.tv_sec);*/ timeout.tv_sec = nextnatpmptoclean_timestamp + startup_time - timeofday.tv_sec; timeout.tv_usec = 0; } #endif #endif #ifdef ENABLE_6FC_SERVICE /* Clean up expired IPv6 PinHoles */ next_pinhole_ts = 0; upnp_clean_expired_pinholes(&next_pinhole_ts); if(next_pinhole_ts && timeout.tv_sec >= (int)(next_pinhole_ts - timeofday.tv_sec)) { timeout.tv_sec = next_pinhole_ts - timeofday.tv_sec; timeout.tv_usec = 0; } #endif /* select open sockets (SSDP, HTTP listen, and all HTTP soap sockets) */ FD_ZERO(&readset); FD_ZERO(&writeset); if (sudp >= 0) { FD_SET(sudp, &readset); max_fd = MAX( max_fd, sudp); #ifdef USE_IFACEWATCHER if (sifacewatcher >= 0) { FD_SET(sifacewatcher, &readset); max_fd = MAX(max_fd, sifacewatcher); } #endif } if (shttpl >= 0) { FD_SET(shttpl, &readset); max_fd = MAX( max_fd, shttpl); } #ifdef ENABLE_IPV6 if (sudpv6 >= 0) { FD_SET(sudpv6, &readset); max_fd = MAX( max_fd, sudpv6); } #endif #ifdef ENABLE_NFQUEUE if (nfqh >= 0) { FD_SET(nfqh, &readset); max_fd = MAX( max_fd, nfqh); } #endif i = 0; /* active HTTP connections count */ for(e = upnphttphead.lh_first; e != NULL; e = e->entries.le_next) { if(e->socket >= 0) { if(e->state <= EWaitingForHttpContent) FD_SET(e->socket, &readset); else if(e->state == ESendingAndClosing) FD_SET(e->socket, &writeset); else continue; max_fd = MAX(max_fd, e->socket); i++; } } /* for debug */ #ifdef DEBUG if(i > 1) { syslog(LOG_DEBUG, "%d active incoming HTTP connections", i); } #endif #ifdef ENABLE_NATPMP for(i=0; i= 0) { FD_SET(snatpmp[i], &readset); max_fd = MAX( max_fd, snatpmp[i]); } } #endif #ifdef USE_MINIUPNPDCTL if(sctl >= 0) { FD_SET(sctl, &readset); max_fd = MAX( max_fd, sctl); } for(ectl = ctllisthead.lh_first; ectl; ectl = ectl->entries.le_next) { if(ectl->socket >= 0) { FD_SET(ectl->socket, &readset); max_fd = MAX( max_fd, ectl->socket); } } #endif #ifdef ENABLE_EVENTS upnpevents_selectfds(&readset, &writeset, &max_fd); #endif if(select(max_fd+1, &readset, &writeset, 0, &timeout) < 0) { if(quitting) goto shutdown; if(errno == EINTR) continue; /* interrupted by a signal, start again */ syslog(LOG_ERR, "select(all): %m"); syslog(LOG_ERR, "Failed to select open sockets. EXITING"); return 1; /* very serious cause of error */ } #ifdef USE_MINIUPNPDCTL for(ectl = ctllisthead.lh_first; ectl;) { ectlnext = ectl->entries.le_next; if((ectl->socket >= 0) && FD_ISSET(ectl->socket, &readset)) { char buf[256]; int l; l = read(ectl->socket, buf, sizeof(buf)); if(l > 0) { /*write(ectl->socket, buf, l);*/ write_command_line(ectl->socket, argc, argv); #ifndef DISABLE_CONFIG_FILE write_option_list(ectl->socket); #endif write_permlist(ectl->socket, upnppermlist, num_upnpperm); write_upnphttp_details(ectl->socket, upnphttphead.lh_first); write_ctlsockets_list(ectl->socket, ctllisthead.lh_first); write_ruleset_details(ectl->socket); #ifdef ENABLE_EVENTS write_events_details(ectl->socket); #endif /* close the socket */ close(ectl->socket); ectl->socket = -1; } else { close(ectl->socket); ectl->socket = -1; } } if(ectl->socket < 0) { LIST_REMOVE(ectl, entries); free(ectl); } ectl = ectlnext; } if((sctl >= 0) && FD_ISSET(sctl, &readset)) { int s; struct sockaddr_un clientname; struct ctlelem * tmp; socklen_t clientnamelen = sizeof(struct sockaddr_un); /*syslog(LOG_DEBUG, "sctl!");*/ s = accept(sctl, (struct sockaddr *)&clientname, &clientnamelen); syslog(LOG_DEBUG, "sctl! : '%s'", clientname.sun_path); tmp = malloc(sizeof(struct ctlelem)); if (tmp == NULL) { syslog(LOG_ERR, "Unable to allocate memory for ctlelem in main()"); close(s); } else { tmp->socket = s; LIST_INSERT_HEAD(&ctllisthead, tmp, entries); } } #endif #ifdef ENABLE_EVENTS upnpevents_processfds(&readset, &writeset); #endif #ifdef ENABLE_NATPMP /* process NAT-PMP packets */ for(i=0; i= 0) && FD_ISSET(snatpmp[i], &readset)) { ProcessIncomingNATPMPPacket(snatpmp[i]); } } #endif /* process SSDP packets */ if(sudp >= 0 && FD_ISSET(sudp, &readset)) { /*syslog(LOG_INFO, "Received UDP Packet");*/ ProcessSSDPRequest(sudp, (unsigned short)v.port); } #ifdef ENABLE_IPV6 if(sudpv6 >= 0 && FD_ISSET(sudpv6, &readset)) { syslog(LOG_INFO, "Received UDP Packet (IPv6)"); ProcessSSDPRequest(sudpv6, (unsigned short)v.port); } #endif #ifdef USE_IFACEWATCHER /* process kernel notifications */ if (sifacewatcher >= 0 && FD_ISSET(sifacewatcher, &readset)) ProcessInterfaceWatchNotify(sifacewatcher); #endif /* process active HTTP connections */ /* LIST_FOREACH macro is not available under linux */ for(e = upnphttphead.lh_first; e != NULL; e = e->entries.le_next) { if(e->socket >= 0) { if(FD_ISSET(e->socket, &readset) || FD_ISSET(e->socket, &writeset)) { Process_upnphttp(e); } } } /* process incoming HTTP connections */ if(shttpl >= 0 && FD_ISSET(shttpl, &readset)) { int shttp; socklen_t clientnamelen; #ifdef ENABLE_IPV6 struct sockaddr_storage clientname; clientnamelen = sizeof(struct sockaddr_storage); #else struct sockaddr_in clientname; clientnamelen = sizeof(struct sockaddr_in); #endif shttp = accept(shttpl, (struct sockaddr *)&clientname, &clientnamelen); if(shttp<0) { /* ignore EAGAIN, EWOULDBLOCK, EINTR, we just try again later */ if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) syslog(LOG_ERR, "accept(http): %m"); } else { struct upnphttp * tmp = 0; char addr_str[64]; sockaddr_to_string((struct sockaddr *)&clientname, addr_str, sizeof(addr_str)); syslog(LOG_INFO, "HTTP connection from %s", 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); close(shttp); } else { /* Create a new upnphttp object and add it to * the active upnphttp object list */ tmp = New_upnphttp(shttp); if(tmp) { #ifdef ENABLE_IPV6 if(clientname.ss_family == AF_INET) { tmp->clientaddr = ((struct sockaddr_in *)&clientname)->sin_addr; } else if(clientname.ss_family == AF_INET6) { struct sockaddr_in6 * addr = (struct sockaddr_in6 *)&clientname; if(IN6_IS_ADDR_V4MAPPED(&addr->sin6_addr)) { memcpy(&tmp->clientaddr, &addr->sin6_addr.s6_addr[12], 4); } else { tmp->ipv6 = 1; memcpy(&tmp->clientaddr_v6, &addr->sin6_addr, sizeof(struct in6_addr)); } } #else tmp->clientaddr = clientname.sin_addr; #endif LIST_INSERT_HEAD(&upnphttphead, tmp, entries); } else { syslog(LOG_ERR, "New_upnphttp() failed"); close(shttp); } } } } #ifdef ENABLE_NFQUEUE /* process NFQ packets */ if(nfqh >= 0 && FD_ISSET(nfqh, &readset)) { /* syslog(LOG_INFO, "Received NFQUEUE Packet");*/ ProcessNFQUEUE(nfqh); } #endif /* delete finished HTTP connections */ for(e = upnphttphead.lh_first; e != NULL; ) { next = e->entries.le_next; if(e->state >= EToDelete) { LIST_REMOVE(e, entries); Delete_upnphttp(e); } e = next; } } /* end of main loop */ shutdown: /* close out open sockets */ while(upnphttphead.lh_first != NULL) { e = upnphttphead.lh_first; LIST_REMOVE(e, entries); Delete_upnphttp(e); } if (sudp >= 0) close(sudp); if (shttpl >= 0) close(shttpl); #ifdef ENABLE_IPV6 if (sudpv6 >= 0) close(sudpv6); #endif #ifdef USE_IFACEWATCHER if(sifacewatcher >= 0) close(sifacewatcher); #endif #ifdef ENABLE_NATPMP for(i=0; i=0) { close(snatpmp[i]); snatpmp[i] = -1; } } #endif #ifdef USE_MINIUPNPDCTL if(sctl>=0) { close(sctl); sctl = -1; if(unlink("/var/run/miniupnpd.ctl") < 0) { syslog(LOG_ERR, "unlink() %m"); } } #endif if (GETFLAG(ENABLEUPNPMASK)) { #ifndef ENABLE_IPV6 if(SendSSDPGoodbye(snotify, addr_count) < 0) #else if(SendSSDPGoodbye(snotify, addr_count * 2) < 0) #endif { syslog(LOG_ERR, "Failed to broadcast good-bye notifications"); } #ifndef ENABLE_IPV6 for(i = 0; i < addr_count; i++) #else for(i = 0; i < addr_count * 2; i++) #endif close(snotify[i]); } if(pidfilename && (unlink(pidfilename) < 0)) { syslog(LOG_ERR, "Failed to remove pidfile %s: %m", pidfilename); } /* delete lists */ while(lan_addrs.lh_first != NULL) { lan_addr = lan_addrs.lh_first; LIST_REMOVE(lan_addrs.lh_first, list); free(lan_addr); } #ifdef ENABLE_NATPMP free(snatpmp); #endif free(snotify); closelog(); #ifndef DISABLE_CONFIG_FILE freeoptions(); #endif return 0; } miniupnpd-1.8.20130730/upnphttp.h010064400017500000024000000066351203340734000155060ustar00nanardstaff/* $Id: upnphttp.h,v 1.35 2012/10/03 21:03:50 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2012 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #ifndef UPNPHTTP_H_INCLUDED #define UPNPHTTP_H_INCLUDED #include #include #include "config.h" #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 /* server: HTTP header returned in all HTTP responses : */ #define MINIUPNPD_SERVER_STRING OS_VERSION " " UPNP_VERSION_STRING " MiniUPnPd/" MINIUPNPD_VERSION /* states : 0 - waiting for data to read 1 - waiting for HTTP Post Content. ... >= 100 - to be deleted */ enum httpStates { EWaitingForHttpRequest = 0, EWaitingForHttpContent, ESendingContinue, ESendingAndClosing, EToDelete = 100 }; enum httpCommands { EUnknown = 0, EGet, EPost, ESubscribe, EUnSubscribe }; struct upnphttp { int socket; struct in_addr clientaddr; /* client address */ #ifdef ENABLE_IPV6 int ipv6; struct in6_addr clientaddr_v6; #endif enum httpStates state; char HttpVer[16]; /* request */ char * req_buf; char accept_language[8]; int req_buflen; int req_contentlen; int req_contentoff; /* header length */ enum httpCommands req_command; int req_soapActionOff; int req_soapActionLen; #ifdef ENABLE_EVENTS int req_CallbackOff; /* For SUBSCRIBE */ int req_CallbackLen; int req_Timeout; int req_SIDOff; /* For UNSUBSCRIBE */ int req_SIDLen; const char * res_SID; #ifdef UPNP_STRICT int req_NTOff; int req_NTLen; #endif #endif int respflags; /* see FLAG_* constants below */ /* response */ char * res_buf; int res_buflen; int res_sent; int res_buf_alloclen; LIST_ENTRY(upnphttp) entries; }; /* Include the "Timeout:" header in response */ #define FLAG_TIMEOUT 0x01 /* Include the "SID:" header in response */ #define FLAG_SID 0x02 /* If set, the POST request included a "Expect: 100-continue" header */ #define FLAG_CONTINUE 0x40 /* If set, the Content-Type is set to text/xml, otherwise it is text/xml */ #define FLAG_HTML 0x80 /* If set, the corresponding Allow: header is set */ #define FLAG_ALLOW_POST 0x100 #define FLAG_ALLOW_SUB_UNSUB 0x200 /* New_upnphttp() */ struct upnphttp * New_upnphttp(int); /* CloseSocket_upnphttp() */ void CloseSocket_upnphttp(struct upnphttp *); /* Delete_upnphttp() */ void Delete_upnphttp(struct upnphttp *); /* Process_upnphttp() */ void Process_upnphttp(struct upnphttp *); /* BuildHeader_upnphttp() * build the header for the HTTP Response * also allocate the buffer for body data */ void BuildHeader_upnphttp(struct upnphttp * h, int respcode, const char * respmsg, int bodylen); /* BuildResp_upnphttp() * fill the res_buf buffer with the complete * HTTP 200 OK response from the body passed as argument */ void BuildResp_upnphttp(struct upnphttp *, const char *, int); /* BuildResp2_upnphttp() * same but with given response code/message */ void BuildResp2_upnphttp(struct upnphttp * h, int respcode, const char * respmsg, const char * body, int bodylen); int SendResp_upnphttp(struct upnphttp *); /* SendRespAndClose_upnphttp() */ void SendRespAndClose_upnphttp(struct upnphttp *); #endif miniupnpd-1.8.20130730/upnpdescgen.c010064400017500000024000001047771217572255200161540ustar00nanardstaff/* $Id: upnpdescgen.c,v 1.75 2013/07/30 06:55: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 */ #include #include #include #include "config.h" #ifdef ENABLE_EVENTS #include "getifaddr.h" #include "upnpredirect.h" #endif #include "upnpdescgen.h" #include "miniupnpdpath.h" #include "upnpglobalvars.h" #include "upnpdescstrings.h" #include "upnpurns.h" #include "getconnstatus.h" /* Event magical values codes */ #define CONNECTIONSTATUS_MAGICALVALUE (249) #define FIREWALLENABLED_MAGICALVALUE (250) #define INBOUNDPINHOLEALLOWED_MAGICALVALUE (251) #define SYSTEMUPDATEID_MAGICALVALUE (252) #define PORTMAPPINGNUMBEROFENTRIES_MAGICALVALUE (253) #define EXTERNALIPADDRESS_MAGICALVALUE (254) #define DEFAULTCONNECTIONSERVICE_MAGICALVALUE (255) static const char * const upnptypes[] = { "string", "boolean", "ui2", "ui4", "bin.base64" }; static const char * const upnpdefaultvalues[] = { 0, "IP_Routed"/*"Unconfigured"*/, /* 1 default value for ConnectionType */ "3600", /* 2 default value for PortMappingLeaseDuration */ }; static const char * const upnpallowedvalues[] = { 0, /* 0 */ "DSL", /* 1 */ "POTS", "Cable", "Ethernet", 0, "Up", /* 6 */ "Down", "Initializing", "Unavailable", 0, "TCP", /* 11 */ "UDP", 0, "Unconfigured", /* 14 */ "IP_Routed", "IP_Bridged", 0, "Unconfigured", /* 18 */ "Connecting", "Connected", "PendingDisconnect", "Disconnecting", "Disconnected", 0, "ERROR_NONE", /* 25 */ /* Optionals values : * ERROR_COMMAND_ABORTED * ERROR_NOT_ENABLED_FOR_INTERNET * ERROR_USER_DISCONNECT * ERROR_ISP_DISCONNECT * ERROR_IDLE_DISCONNECT * ERROR_FORCED_DISCONNECT * ERROR_NO_CARRIER * ERROR_IP_CONFIGURATION * ERROR_UNKNOWN */ 0, "", /* 27 */ 0 }; static const int upnpallowedranges[] = { 0, /* 1 PortMappingLeaseDuration */ 0, 604800, /* 3 InternalPort */ 1, 65535, /* 5 LeaseTime */ 1, 86400, /* 7 OutboundPinholeTimeout */ 100, 200, }; static const char * magicargname[] = { 0, "StartPort", "EndPort", "RemoteHost", "RemotePort", "InternalClient", "InternalPort", "IsWorking" }; static const char xmlver[] = "\r\n"; static const char root_service[] = "scpd xmlns=\"urn:schemas-upnp-org:service-1-0\""; static const char root_device[] = "root xmlns=\"urn:schemas-upnp-org:device-1-0\""; /* root Description of the UPnP Device * fixed to match UPnP_IGD_InternetGatewayDevice 1.0.pdf * Needs to be checked with UPnP-gw-InternetGatewayDevice-v2-Device.pdf * presentationURL is only "recommended" but the router doesn't appears * in "Network connections" in Windows XP if it is not present. */ static const struct XMLElt rootDesc[] = { /* 0 */ {root_device, INITHELPER(1,2)}, {"specVersion", INITHELPER(3,2)}, #if defined(ENABLE_L3F_SERVICE) || defined(HAS_DUMMY_SERVICE) || defined(ENABLE_DP_SERVICE) {"device", INITHELPER(5,13)}, #else {"device", INITHELPER(5,12)}, #endif {"/major", "1"}, {"/minor", "0"}, /* 5 */ {"/deviceType", DEVICE_TYPE_IGD}, /* urn:schemas-upnp-org:device:InternetGatewayDevice:1 or 2 */ {"/friendlyName", friendly_name/*ROOTDEV_FRIENDLYNAME*/}, /* required */ {"/manufacturer", ROOTDEV_MANUFACTURER}, /* required */ /* 8 */ {"/manufacturerURL", ROOTDEV_MANUFACTURERURL}, /* optional */ {"/modelDescription", ROOTDEV_MODELDESCRIPTION}, /* recommended */ {"/modelName", ROOTDEV_MODELNAME}, /* required */ {"/modelNumber", modelnumber}, {"/modelURL", ROOTDEV_MODELURL}, {"/serialNumber", serialnumber}, {"/UDN", uuidvalue_igd}, /* required */ /* see if /UPC is needed. */ #ifdef ENABLE_6FC_SERVICE #define SERVICES_OFFSET 63 #else #define SERVICES_OFFSET 58 #endif #if defined(ENABLE_L3F_SERVICE) || defined(HAS_DUMMY_SERVICE) || defined(ENABLE_DP_SERVICE) /* here we dening Services for the root device : * L3F and DUMMY and DeviceProtection */ #ifdef ENABLE_L3F_SERVICE #define NSERVICES1 1 #else #define NSERVICES1 0 #endif #ifdef HAS_DUMMY_SERVICE #define NSERVICES2 1 #else #define NSERVICES2 0 #endif #ifdef ENABLE_DP_SERVICE #define NSERVICES3 1 #else #define NSERVICES3 0 #endif #define NSERVICES (NSERVICES1+NSERVICES2+NSERVICES3) {"serviceList", INITHELPER(SERVICES_OFFSET,NSERVICES)}, {"deviceList", INITHELPER(18,1)}, {"/presentationURL", presentationurl}, /* recommended */ #else {"deviceList", INITHELPER(18,1)}, {"/presentationURL", presentationurl}, /* recommended */ {0,0}, #endif /* 18 */ {"device", INITHELPER(19,13)}, /* 19 */ {"/deviceType", DEVICE_TYPE_WAN}, /* required */ /* urn:schemas-upnp-org:device:WANDevice:1 or 2 */ {"/friendlyName", WANDEV_FRIENDLYNAME}, {"/manufacturer", WANDEV_MANUFACTURER}, {"/manufacturerURL", WANDEV_MANUFACTURERURL}, {"/modelDescription" , WANDEV_MODELDESCRIPTION}, {"/modelName", WANDEV_MODELNAME}, {"/modelNumber", WANDEV_MODELNUMBER}, {"/modelURL", WANDEV_MODELURL}, {"/serialNumber", serialnumber}, {"/UDN", uuidvalue_wan}, {"/UPC", WANDEV_UPC}, /* UPC (=12 digit barcode) is optional */ /* 30 */ {"serviceList", INITHELPER(32,1)}, {"deviceList", INITHELPER(38,1)}, /* 32 */ {"service", INITHELPER(33,5)}, /* 33 */ {"/serviceType", "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1"}, /*{"/serviceId", "urn:upnp-org:serviceId:WANCommonInterfaceConfig"}, */ {"/serviceId", "urn:upnp-org:serviceId:WANCommonIFC1"}, /* required */ {"/controlURL", WANCFG_CONTROLURL}, {"/eventSubURL", WANCFG_EVENTURL}, {"/SCPDURL", WANCFG_PATH}, /* 38 */ {"device", INITHELPER(39,12)}, /* 39 */ {"/deviceType", DEVICE_TYPE_WANC}, /* urn:schemas-upnp-org:device:WANConnectionDevice:1 or 2 */ {"/friendlyName", WANCDEV_FRIENDLYNAME}, {"/manufacturer", WANCDEV_MANUFACTURER}, {"/manufacturerURL", WANCDEV_MANUFACTURERURL}, {"/modelDescription", WANCDEV_MODELDESCRIPTION}, {"/modelName", WANCDEV_MODELNAME}, {"/modelNumber", WANCDEV_MODELNUMBER}, {"/modelURL", WANCDEV_MODELURL}, {"/serialNumber", serialnumber}, {"/UDN", uuidvalue_wcd}, {"/UPC", WANCDEV_UPC}, /* UPC (=12 digit Barcode) is optional */ #ifdef ENABLE_6FC_SERVICE {"serviceList", INITHELPER(51,2)}, #else {"serviceList", INITHELPER(51,1)}, #endif /* 51 */ {"service", INITHELPER(53,5)}, {"service", INITHELPER(58,5)}, /* 53 */ {"/serviceType", SERVICE_TYPE_WANIPC}, /* urn:schemas-upnp-org:service:WANIPConnection:2 for v2 */ {"/serviceId", SERVICE_ID_WANIPC}, /* urn:upnp-org:serviceId:WANIPConn1 or 2 */ {"/controlURL", WANIPC_CONTROLURL}, {"/eventSubURL", WANIPC_EVENTURL}, {"/SCPDURL", WANIPC_PATH}, #ifdef ENABLE_6FC_SERVICE /* 58 */ {"/serviceType", "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1"}, {"/serviceId", "urn:upnp-org:serviceId:WANIPv6FC1"}, {"/controlURL", WANIP6FC_CONTROLURL}, {"/eventSubURL", WANIP6FC_EVENTURL}, {"/SCPDURL", WANIP6FC_PATH}, #endif /* 58 / 63 = SERVICES_OFFSET*/ #if defined(HAS_DUMMY_SERVICE) || defined(ENABLE_L3F_SERVICE) || defined(ENABLE_DP_SERVICE) {"service", INITHELPER(SERVICES_OFFSET+2,5)}, {"service", INITHELPER(SERVICES_OFFSET+7,5)}, #endif #ifdef HAS_DUMMY_SERVICE /* 60 / 65 = SERVICES_OFFSET+2 */ {"/serviceType", "urn:schemas-dummy-com:service:Dummy:1"}, {"/serviceId", "urn:dummy-com:serviceId:dummy1"}, {"/controlURL", "/dummy"}, {"/eventSubURL", "/dummy"}, {"/SCPDURL", DUMMY_PATH}, #endif #ifdef ENABLE_L3F_SERVICE /* 60 / 65 = SERVICES_OFFSET+2 */ {"/serviceType", "urn:schemas-upnp-org:service:Layer3Forwarding:1"}, {"/serviceId", "urn:upnp-org:serviceId:Layer3Forwarding1"}, {"/controlURL", L3F_CONTROLURL}, /* The Layer3Forwarding service is only */ {"/eventSubURL", L3F_EVENTURL}, /* recommended, not mandatory */ {"/SCPDURL", L3F_PATH}, #endif #ifdef ENABLE_DP_SERVICE /* InternetGatewayDevice v2 : * it is RECOMMEDED that DeviceProtection service is implemented and applied. * If DeviceProtection is not implemented and applied, it is RECOMMENDED * that control points are able to access only actions and parameters defined * as Public role. */ /* 65 / 70 = SERVICES_OFFSET+7 */ {"/serviceType", "urn:schemas-upnp-org:service:DeviceProtection:1"}, {"/serviceId", "urn:upnp-org:serviceId:DeviceProtection1"}, {"/controlURL", DP_CONTROLURL}, {"/eventSubURL", DP_EVENTURL}, {"/SCPDURL", DP_PATH}, #endif {0, 0} }; /* WANIPCn.xml */ /* see UPnP_IGD_WANIPConnection 1.0.pdf static struct XMLElt scpdWANIPCn[] = { {root_service, {INITHELPER(1,2)}}, {0, {0}} }; */ static const struct argument AddPortMappingArgs[] = { {1, 11}, /* RemoteHost */ {1, 12}, /* ExternalPort */ {1, 14}, /* PortMappingProtocol */ {1, 13}, /* InternalPort */ {1, 15}, /* InternalClient */ {1, 9}, /* PortMappingEnabled */ {1, 16}, /* PortMappingDescription */ {1, 10}, /* PortMappingLeaseDuration */ {0, 0} }; #ifdef IGD_V2 static const struct argument AddAnyPortMappingArgs[] = { {1, 11}, /* RemoteHost */ {1, 12}, /* ExternalPort */ {1, 14}, /* PortMappingProtocol */ {1, 13}, /* InternalPort */ {1, 15}, /* InternalClient */ {1, 9}, /* PortMappingEnabled */ {1, 16}, /* PortMappingDescription */ {1, 10}, /* PortMappingLeaseDuration */ {2, 12}, /* NewReservedPort / ExternalPort */ {0, 0} }; static const struct argument DeletePortMappingRangeArgs[] = { {1|(1<<2), 12}, /* NewStartPort / ExternalPort */ {1|(2<<2), 12}, /* NewEndPort / ExternalPort */ {1, 14}, /* NewProtocol / PortMappingProtocol */ {1, 18}, /* NewManage / A_ARG_TYPE_Manage */ {0, 0} }; static const struct argument GetListOfPortMappingsArgs[] = { {1|(1<<2), 12}, /* NewStartPort / ExternalPort */ {1|(2<<2), 12}, /* NewEndPort / ExternalPort */ {1, 14}, /* NewProtocol / PortMappingProtocol */ {1, 18}, /* NewManage / A_ARG_TYPE_Manage */ {1, 8}, /* NewNumberOfPorts / PortMappingNumberOfEntries */ {2, 19}, /* NewPortListing / A_ARG_TYPE_PortListing */ {0, 0} }; #endif static const struct argument GetExternalIPAddressArgs[] = { {2, 7}, {0, 0} }; static const struct argument DeletePortMappingArgs[] = { {1, 11}, {1, 12}, {1, 14}, {0, 0} }; static const struct argument SetConnectionTypeArgs[] = { {1, 0}, {0, 0} }; static const struct argument GetConnectionTypeInfoArgs[] = { {2, 0}, {2, 1}, {0, 0} }; static const struct argument GetStatusInfoArgs[] = { {2, 2}, {2, 4}, {2, 3}, {0, 0} }; static const struct argument GetNATRSIPStatusArgs[] = { {2, 5}, {2, 6}, {0, 0} }; static const struct argument GetGenericPortMappingEntryArgs[] = { {1, 8}, {2, 11}, {2, 12}, {2, 14}, {2, 13}, {2, 15}, {2, 9}, {2, 16}, {2, 10}, {0, 0} }; static const struct argument GetSpecificPortMappingEntryArgs[] = { {1, 11}, {1, 12}, {1, 14}, {2, 13}, {2, 15}, {2, 9}, {2, 16}, {2, 10}, {0, 0} }; static const struct action WANIPCnActions[] = { {"SetConnectionType", SetConnectionTypeArgs}, /* R */ {"GetConnectionTypeInfo", GetConnectionTypeInfoArgs}, /* R */ {"RequestConnection", 0}, /* R */ /*{"RequestTermination", 0},*/ /* O */ {"ForceTermination", 0}, /* R */ /*{"SetAutoDisconnectTime", 0},*/ /* O */ /*{"SetIdleDisconnectTime", 0},*/ /* O */ /*{"SetWarnDisconnectDelay", 0}, */ /* O */ {"GetStatusInfo", GetStatusInfoArgs}, /* R */ /*GetAutoDisconnectTime*/ /* O */ /*GetIdleDisconnectTime*/ /* O */ /*GetWarnDisconnectDelay*/ /* O */ {"GetNATRSIPStatus", GetNATRSIPStatusArgs}, /* R */ {"GetGenericPortMappingEntry", GetGenericPortMappingEntryArgs}, /* R */ {"GetSpecificPortMappingEntry", GetSpecificPortMappingEntryArgs}, /* R */ {"AddPortMapping", AddPortMappingArgs}, /* R */ {"DeletePortMapping", DeletePortMappingArgs}, /* R */ {"GetExternalIPAddress", GetExternalIPAddressArgs}, /* R */ #ifdef IGD_V2 {"DeletePortMappingRange", DeletePortMappingRangeArgs}, /* R, IGD v2 */ {"GetListOfPortMappings", GetListOfPortMappingsArgs}, /* R, IGD v2 */ {"AddAnyPortMapping", AddAnyPortMappingArgs}, /* R, IGD v2 */ #endif #if 0 {"AddPortMapping", AddPortMappingArgs}, /* R */ {"GetExternalIPAddress", GetExternalIPAddressArgs}, /* R */ {"DeletePortMapping", DeletePortMappingArgs}, /* R */ {"SetConnectionType", SetConnectionTypeArgs}, /* R */ {"GetConnectionTypeInfo", GetConnectionTypeInfoArgs}, /* R */ {"RequestConnection", 0}, /* R */ {"ForceTermination", 0}, /* R */ {"GetStatusInfo", GetStatusInfoArgs}, /* R */ {"GetNATRSIPStatus", GetNATRSIPStatusArgs}, /* R */ {"GetGenericPortMappingEntry", GetGenericPortMappingEntryArgs}, /* R */ {"GetSpecificPortMappingEntry", GetSpecificPortMappingEntryArgs}, /* R */ /* added in v2 UPnP-gw-WANIPConnection-v2-Service.pdf */ #ifdef IGD_V2 {"AddAnyPortMapping", AddAnyPortMappingArgs}, {"DeletePortMappingRange", DeletePortMappingRangeArgs}, {"GetListOfPortMappings", GetListOfPortMappingsArgs}, #endif #endif {0, 0} }; /* R=Required, O=Optional */ /* ignore "warning: missing initializer" */ #pragma GCC diagnostic ignored "-Wmissing-field-initializers" static const struct stateVar WANIPCnVars[] = { /* 0 */ #if 0 {"ConnectionType", 0, 0/*1*/}, /* required */ {"PossibleConnectionTypes", 0|0x80, 0, 14, 15}, #endif {"ConnectionType", 0, 1, 14, 15}, /* required */ {"PossibleConnectionTypes", 0|0x80, 0, 0, 15}, /* Required * Allowed values : Unconfigured / IP_Routed / IP_Bridged */ {"ConnectionStatus", 0|0x80, 0/*1*/, 18, CONNECTIONSTATUS_MAGICALVALUE }, /* required */ /* Allowed Values : Unconfigured / Connecting(opt) / Connected * PendingDisconnect(opt) / Disconnecting (opt) * Disconnected */ {"Uptime", 3, 0}, /* Required */ {"LastConnectionError", 0, 0, 25}, /* required : */ /* Allowed Values : ERROR_NONE(req) / ERROR_COMMAND_ABORTED(opt) * ERROR_NOT_ENABLED_FOR_INTERNET(opt) * ERROR_USER_DISCONNECT(opt) * ERROR_ISP_DISCONNECT(opt) * ERROR_IDLE_DISCONNECT(opt) * ERROR_FORCED_DISCONNECT(opt) * ERROR_NO_CARRIER(opt) * ERROR_IP_CONFIGURATION(opt) * ERROR_UNKNOWN(opt) */ {"RSIPAvailable", 1, 0}, /* required */ {"NATEnabled", 1, 0}, /* required */ {"ExternalIPAddress", 0|0x80, 0, 0, EXTERNALIPADDRESS_MAGICALVALUE}, /* required. Default : empty string */ {"PortMappingNumberOfEntries", 2|0x80, 0, 0, PORTMAPPINGNUMBEROFENTRIES_MAGICALVALUE}, /* required >= 0 */ {"PortMappingEnabled", 1, 0}, /* Required */ /* 10 */ {"PortMappingLeaseDuration", 3, 2, 1}, /* required */ /* TODO : for IGD v2 : * * PortMappingLeaseDuration * ui4 * Vendor-defined * * 0 * 604800 * * */ {"RemoteHost", 0, 0}, /* required. Default : empty string */ {"ExternalPort", 2, 0}, /* required */ {"InternalPort", 2, 0, 3}, /* required */ {"PortMappingProtocol", 0, 0, 11}, /* required allowedValues: TCP/UDP */ {"InternalClient", 0, 0}, /* required */ {"PortMappingDescription", 0, 0}, /* required default: empty string */ /* added in v2 UPnP-gw-WANIPConnection-v2-Service.pdf */ #ifdef IGD_V2 {"SystemUpdateID", 3|0x80, 0, 0, SYSTEMUPDATEID_MAGICALVALUE}, {"A_ARG_TYPE_Manage", 1, 0}, {"A_ARG_TYPE_PortListing", 0, 0}, #endif {0, 0} }; static const struct serviceDesc scpdWANIPCn = { WANIPCnActions, WANIPCnVars }; /* WANCfg.xml */ /* See UPnP_IGD_WANCommonInterfaceConfig 1.0.pdf */ static const struct argument GetCommonLinkPropertiesArgs[] = { {2, 0}, {2, 1}, {2, 2}, {2, 3}, {0, 0} }; static const struct argument GetTotalBytesSentArgs[] = { {2, 4}, {0, 0} }; static const struct argument GetTotalBytesReceivedArgs[] = { {2, 5}, {0, 0} }; static const struct argument GetTotalPacketsSentArgs[] = { {2, 6}, {0, 0} }; static const struct argument GetTotalPacketsReceivedArgs[] = { {2, 7}, {0, 0} }; static const struct action WANCfgActions[] = { {"GetCommonLinkProperties", GetCommonLinkPropertiesArgs}, /* Required */ {"GetTotalBytesSent", GetTotalBytesSentArgs}, /* optional */ {"GetTotalBytesReceived", GetTotalBytesReceivedArgs}, /* optional */ {"GetTotalPacketsSent", GetTotalPacketsSentArgs}, /* optional */ {"GetTotalPacketsReceived", GetTotalPacketsReceivedArgs}, /* optional */ {0, 0} }; /* See UPnP_IGD_WANCommonInterfaceConfig 1.0.pdf */ static const struct stateVar WANCfgVars[] = { {"WANAccessType", 0, 0, 1}, /* Allowed Values : DSL / POTS / Cable / Ethernet * Default value : empty string */ {"Layer1UpstreamMaxBitRate", 3, 0}, {"Layer1DownstreamMaxBitRate", 3, 0}, {"PhysicalLinkStatus", 0|0x80, 0, 6, 6}, /* allowed values : * Up / Down / Initializing (optional) / Unavailable (optionnal) * no Default value * Evented */ {"TotalBytesSent", 3, 0}, /* Optional */ {"TotalBytesReceived", 3, 0}, /* Optional */ {"TotalPacketsSent", 3, 0}, /* Optional */ {"TotalPacketsReceived", 3, 0},/* Optional */ /*{"MaximumActiveConnections", 2, 0}, // allowed Range value // OPTIONAL */ /*{"WANAccessProvider", 0, 0},*/ /* Optional */ {0, 0} }; static const struct serviceDesc scpdWANCfg = { WANCfgActions, WANCfgVars }; #ifdef ENABLE_L3F_SERVICE /* Read UPnP_IGD_Layer3Forwarding_1.0.pdf */ static const struct argument SetDefaultConnectionServiceArgs[] = { {1, 0}, /* in */ {0, 0} }; static const struct argument GetDefaultConnectionServiceArgs[] = { {2, 0}, /* out */ {0, 0} }; static const struct action L3FActions[] = { {"SetDefaultConnectionService", SetDefaultConnectionServiceArgs}, /* Req */ {"GetDefaultConnectionService", GetDefaultConnectionServiceArgs}, /* Req */ {0, 0} }; static const struct stateVar L3FVars[] = { {"DefaultConnectionService", 0|0x80, 0, 0, DEFAULTCONNECTIONSERVICE_MAGICALVALUE}, /* Required */ {0, 0} }; static const struct serviceDesc scpdL3F = { L3FActions, L3FVars }; #endif #ifdef ENABLE_6FC_SERVICE /* see UPnP-gw-WANIPv6FirewallControl-v1-Service.pdf */ static const struct argument GetFirewallStatusArgs[] = { {2|0x80, 0}, /* OUT : FirewallEnabled */ {2|0x80, 6}, /* OUT : InboundPinholeAllowed */ {0, 0} }; static const struct argument GetOutboundPinholeTimeoutArgs[] = { {1|0x80|(3<<2), 1}, /* RemoteHost IN A_ARG_TYPE_IPv6Address */ {1|0x80|(4<<2), 2}, /* RemotePort IN A_ARG_TYPE_Port */ {1|0x80|(5<<2), 1}, /* InternalClient IN A_ARG_TYPE_IPv6Address */ {1|0x80|(6<<2), 2}, /* InternalPort IN A_ARG_TYPE_Port */ {1|0x80, 3}, /* Protocol IN A_ARG_TYPE_Protocol */ {2|0x80, 7}, /* OutboundPinholeTimeout OUT A_ARG_TYPE_OutboundPinholeTimeout */ {0, 0} }; static const struct argument AddPinholeArgs[] = { {1|0x80|(3<<2), 1}, /* RemoteHost IN A_ARG_TYPE_IPv6Address */ {1|0x80|(4<<2), 2}, /* RemotePort IN A_ARG_TYPE_Port */ {1|0x80|(5<<2), 1}, /* InternalClient IN A_ARG_TYPE_IPv6Address */ {1|0x80|(6<<2), 2}, /* InternalPort IN A_ARG_TYPE_Port */ {1|0x80, 3}, /* Protocol IN A_ARG_TYPE_Protocol */ {1|0x80, 5}, /* LeaseTime IN A_ARG_TYPE_LeaseTime */ {2|0x80, 4}, /* UniqueID OUT A_ARG_TYPE_UniqueID */ {0, 0} }; static const struct argument UpdatePinholeArgs[] = { {1|0x80, 4}, /* UniqueID IN A_ARG_TYPE_UniqueID */ {1, 5}, /* LeaseTime IN A_ARG_TYPE_LeaseTime */ {0, 0} }; static const struct argument DeletePinholeArgs[] = { {1|0x80, 4}, /* UniqueID IN A_ARG_TYPE_UniqueID */ {0, 0} }; static const struct argument GetPinholePacketsArgs[] = { {1|0x80, 4}, /* UniqueID IN A_ARG_TYPE_UniqueID */ {2|0x80, 9}, /* PinholePackets OUT A_ARG_TYPE_PinholePackets */ {0, 0} }; static const struct argument CheckPinholeWorkingArgs[] = { {1|0x80, 4}, /* UniqueID IN A_ARG_TYPE_UniqueID */ {2|0x80|(7<<2), 8}, /* IsWorking OUT A_ARG_TYPE_Boolean */ {0, 0} }; static const struct action IPv6FCActions[] = { {"GetFirewallStatus", GetFirewallStatusArgs}, /* Req */ {"GetOutboundPinholeTimeout", GetOutboundPinholeTimeoutArgs}, /* Opt */ {"AddPinhole", AddPinholeArgs}, /* Req */ {"UpdatePinhole", UpdatePinholeArgs}, /* Req */ {"DeletePinhole", DeletePinholeArgs}, /* Req */ {"GetPinholePackets", GetPinholePacketsArgs}, /* Req */ {"CheckPinholeWorking", CheckPinholeWorkingArgs}, /* Opt */ {0, 0} }; static const struct stateVar IPv6FCVars[] = { {"FirewallEnabled", 1|0x80, 0, 0, FIREWALLENABLED_MAGICALVALUE}, /* Required */ {"A_ARG_TYPE_IPv6Address", 0, 0, 0, 0}, /* Required */ {"A_ARG_TYPE_Port", 2, 0, 0, 0}, /* Required */ {"A_ARG_TYPE_Protocol", 2, 0, 0, 0}, /* Required */ /* 4 */ {"A_ARG_TYPE_UniqueID", 2, 0, 0, 0}, /* Required */ {"A_ARG_TYPE_LeaseTime", 3, 0, 5, 0}, /* Required */ {"InboundPinholeAllowed", 1|0x80, 0, 0, INBOUNDPINHOLEALLOWED_MAGICALVALUE}, /* Required */ {"A_ARG_TYPE_OutboundPinholeTimeout", 3, 0, 7, 0}, /* Optional */ /* 8 */ {"A_ARG_TYPE_Boolean", 1, 0, 0, 0}, /* Optional */ {"A_ARG_TYPE_PinholePackets", 3, 0, 0, 0}, /* Required */ {0, 0} }; static const struct serviceDesc scpd6FC = { IPv6FCActions, IPv6FCVars }; #endif #ifdef ENABLE_DP_SERVICE /* UPnP-gw-DeviceProtection-v1-Service.pdf */ static const struct action DPActions[] = { {"SendSetupMessage", 0}, {"GetSupportedProtocols", 0}, {"GetAssignedRoles", 0}, {0, 0} }; static const struct stateVar DPVars[] = { {"SetupReady", 1|0x80}, {"SupportedProtocols", 0}, {"A_ARG_TYPE_ACL", 0}, {"A_ARG_TYPE_IdentityList", 0}, {"A_ARG_TYPE_Identity", 0}, {"A_ARG_TYPE_Base64", 4}, {"A_ARG_TYPE_String", 0}, {0, 0} }; static const struct serviceDesc scpdDP = { DPActions, DPVars }; #endif /* strcat_str() * concatenate the string and use realloc to increase the * memory buffer if needed. */ static char * strcat_str(char * str, int * len, int * tmplen, const char * s2) { int s2len; int newlen; char * p; s2len = (int)strlen(s2); if(*tmplen <= (*len + s2len)) { if(s2len < 256) newlen = *tmplen + 256; else newlen = *tmplen + s2len + 1; p = (char *)realloc(str, newlen); if(p == NULL) /* handle a failure of realloc() */ return str; str = p; *tmplen = newlen; } /*strcpy(str + *len, s2); */ memcpy(str + *len, s2, s2len + 1); *len += s2len; return str; } /* strcat_char() : * concatenate a character and use realloc to increase the * size of the memory buffer if needed */ static char * strcat_char(char * str, int * len, int * tmplen, char c) { char * p; if(*tmplen <= (*len + 1)) { *tmplen += 256; p = (char *)realloc(str, *tmplen); if(p == NULL) /* handle a failure of realloc() */ { *tmplen -= 256; return str; } str = p; } str[*len] = c; (*len)++; return str; } /* strcat_int() * concatenate the string representation of the integer. * call strcat_char() */ static char * strcat_int(char * str, int * len, int * tmplen, int i) { char buf[16]; int j; if(i < 0) { str = strcat_char(str, len, tmplen, '-'); i = -i; } else if(i == 0) { /* special case for 0 */ str = strcat_char(str, len, tmplen, '0'); return str; } j = 0; while(i && j < (int)sizeof(buf)) { buf[j++] = '0' + (i % 10); i = i / 10; } while(j > 0) { str = strcat_char(str, len, tmplen, buf[--j]); } return str; } /* iterative subroutine using a small stack * This way, the progam stack usage is kept low */ static char * genXML(char * str, int * len, int * tmplen, const struct XMLElt * p) { unsigned short i, j; unsigned long k; int top; const char * eltname, *s; char c; struct { unsigned short i; unsigned short j; const char * eltname; } pile[16]; /* stack */ top = -1; i = 0; /* current node */ j = 1; /* i + number of nodes*/ for(;;) { eltname = p[i].eltname; if(!eltname) return str; if(eltname[0] == '/') { if(p[i].data && p[i].data[0]) { /*printf("<%s>%s<%s>\n", eltname+1, p[i].data, eltname); */ str = strcat_char(str, len, tmplen, '<'); str = strcat_str(str, len, tmplen, eltname+1); str = strcat_char(str, len, tmplen, '>'); str = strcat_str(str, len, tmplen, p[i].data); str = strcat_char(str, len, tmplen, '<'); str = strcat_str(str, len, tmplen, eltname); str = strcat_char(str, len, tmplen, '>'); } for(;;) { if(top < 0) return str; i = ++(pile[top].i); j = pile[top].j; /*printf(" pile[%d]\t%d %d\n", top, i, j); */ if(i==j) { /*printf("\n", pile[top].eltname); */ str = strcat_char(str, len, tmplen, '<'); str = strcat_char(str, len, tmplen, '/'); s = pile[top].eltname; for(c = *s; c > ' '; c = *(++s)) str = strcat_char(str, len, tmplen, c); str = strcat_char(str, len, tmplen, '>'); top--; } else break; } } else { /*printf("<%s>\n", eltname); */ str = strcat_char(str, len, tmplen, '<'); str = strcat_str(str, len, tmplen, eltname); str = strcat_char(str, len, tmplen, '>'); k = (unsigned long)p[i].data; i = k & 0xffff; j = i + (k >> 16); top++; /*printf(" +pile[%d]\t%d %d\n", top, i, j); */ pile[top].i = i; pile[top].j = j; pile[top].eltname = eltname; } } } /* genRootDesc() : * - Generate the root description of the UPnP device. * - the len argument is used to return the length of * the returned string. * - tmp_uuid argument is used to build the uuid string */ char * genRootDesc(int * len) { char * str; int tmplen; tmplen = 2048; str = (char *)malloc(tmplen); if(str == NULL) return NULL; * len = strlen(xmlver); /*strcpy(str, xmlver); */ memcpy(str, xmlver, *len + 1); str = genXML(str, len, &tmplen, rootDesc); str[*len] = '\0'; return str; } /* genServiceDesc() : * Generate service description with allowed methods and * related variables. */ static char * genServiceDesc(int * len, const struct serviceDesc * s) { int i, j; const struct action * acts; const struct stateVar * vars; const struct argument * args; const char * p; char * str; int tmplen; tmplen = 2048; str = (char *)malloc(tmplen); if(str == NULL) return NULL; /*strcpy(str, xmlver); */ *len = strlen(xmlver); memcpy(str, xmlver, *len + 1); acts = s->actionList; vars = s->serviceStateTable; str = strcat_char(str, len, &tmplen, '<'); str = strcat_str(str, len, &tmplen, root_service); str = strcat_char(str, len, &tmplen, '>'); str = strcat_str(str, len, &tmplen, "10"); i = 0; str = strcat_str(str, len, &tmplen, ""); while(acts[i].name) { str = strcat_str(str, len, &tmplen, ""); str = strcat_str(str, len, &tmplen, acts[i].name); str = strcat_str(str, len, &tmplen, ""); /* argument List */ args = acts[i].args; if(args) { str = strcat_str(str, len, &tmplen, ""); j = 0; while(args[j].dir) { str = strcat_str(str, len, &tmplen, ""); if((args[j].dir & 0x80) == 0) { str = strcat_str(str, len, &tmplen, "New"); } p = vars[args[j].relatedVar].name; if(args[j].dir & 0x7c) { /* use magic values ... */ str = strcat_str(str, len, &tmplen, magicargname[(args[j].dir & 0x7c) >> 2]); } else if(0 == memcmp(p, "PortMapping", 11) && 0 != memcmp(p + 11, "Description", 11)) { if(0 == memcmp(p + 11, "NumberOfEntries", 15)) { /* PortMappingNumberOfEntries */ #ifdef IGD_V2 if(0 == memcmp(acts[i].name, "GetListOfPortMappings", 22)) { str = strcat_str(str, len, &tmplen, "NumberOfPorts"); } else { str = strcat_str(str, len, &tmplen, "PortMappingIndex"); } #else str = strcat_str(str, len, &tmplen, "PortMappingIndex"); #endif } else { /* PortMappingEnabled * PortMappingLeaseDuration * PortMappingProtocol */ str = strcat_str(str, len, &tmplen, p + 11); } #ifdef IGD_V2 } else if(0 == memcmp(p, "A_ARG_TYPE_", 11)) { str = strcat_str(str, len, &tmplen, p + 11); } else if(0 == memcmp(p, "ExternalPort", 13) && args[j].dir == 2 && 0 == memcmp(acts[i].name, "AddAnyPortMapping", 18)) { str = strcat_str(str, len, &tmplen, "ReservedPort"); #endif } else { str = strcat_str(str, len, &tmplen, p); } str = strcat_str(str, len, &tmplen, ""); str = strcat_str(str, len, &tmplen, (args[j].dir&0x03)==1?"in":"out"); str = strcat_str(str, len, &tmplen, ""); str = strcat_str(str, len, &tmplen, p); str = strcat_str(str, len, &tmplen, ""); j++; } str = strcat_str(str, len, &tmplen,""); } str = strcat_str(str, len, &tmplen, ""); /*str = strcat_char(str, len, &tmplen, '\n'); // TEMP ! */ i++; } str = strcat_str(str, len, &tmplen, ""); i = 0; while(vars[i].name) { str = strcat_str(str, len, &tmplen, ""); str = strcat_str(str, len, &tmplen, vars[i].name); str = strcat_str(str, len, &tmplen, ""); str = strcat_str(str, len, &tmplen, upnptypes[vars[i].itype & 0x0f]); str = strcat_str(str, len, &tmplen, ""); if(vars[i].iallowedlist) { if((vars[i].itype & 0x0f) == 0) { /* string */ str = strcat_str(str, len, &tmplen, ""); for(j=vars[i].iallowedlist; upnpallowedvalues[j]; j++) { str = strcat_str(str, len, &tmplen, ""); str = strcat_str(str, len, &tmplen, upnpallowedvalues[j]); str = strcat_str(str, len, &tmplen, ""); } str = strcat_str(str, len, &tmplen, ""); } else { /* ui2 and ui4 */ str = strcat_str(str, len, &tmplen, ""); str = strcat_int(str, len, &tmplen, upnpallowedranges[vars[i].iallowedlist]); str = strcat_str(str, len, &tmplen, ""); str = strcat_int(str, len, &tmplen, upnpallowedranges[vars[i].iallowedlist+1]); str = strcat_str(str, len, &tmplen, ""); } } /*if(vars[i].defaultValue) */ if(vars[i].idefault) { str = strcat_str(str, len, &tmplen, ""); /*str = strcat_str(str, len, &tmplen, vars[i].defaultValue); */ str = strcat_str(str, len, &tmplen, upnpdefaultvalues[vars[i].idefault]); str = strcat_str(str, len, &tmplen, ""); } str = strcat_str(str, len, &tmplen, ""); /*str = strcat_char(str, len, &tmplen, '\n'); // TEMP ! */ i++; } str = strcat_str(str, len, &tmplen, ""); str[*len] = '\0'; return str; } /* genWANIPCn() : * Generate the WANIPConnection xml description */ char * genWANIPCn(int * len) { return genServiceDesc(len, &scpdWANIPCn); } /* genWANCfg() : * Generate the WANInterfaceConfig xml description. */ char * genWANCfg(int * len) { return genServiceDesc(len, &scpdWANCfg); } #ifdef ENABLE_L3F_SERVICE char * genL3F(int * len) { return genServiceDesc(len, &scpdL3F); } #endif #ifdef ENABLE_6FC_SERVICE char * gen6FC(int * len) { return genServiceDesc(len, &scpd6FC); } #endif #ifdef ENABLE_DP_SERVICE char * genDP(int * len) { return genServiceDesc(len, &scpdDP); } #endif #ifdef ENABLE_EVENTS static char * genEventVars(int * len, const struct serviceDesc * s) { char tmp[16]; const struct stateVar * v; char * str; int tmplen; tmplen = 512; str = (char *)malloc(tmplen); if(str == NULL) return NULL; *len = 0; v = s->serviceStateTable; str = strcat_str(str, len, &tmplen, ""); while(v->name) { if(v->itype & 0x80) { str = strcat_str(str, len, &tmplen, "<"); str = strcat_str(str, len, &tmplen, v->name); str = strcat_str(str, len, &tmplen, ">"); /*printf("<%s>", v->name);*/ switch(v->ieventvalue) { case 0: break; case CONNECTIONSTATUS_MAGICALVALUE: /* or get_wan_connection_status_str(ext_if_name) */ str = strcat_str(str, len, &tmplen, upnpallowedvalues[18 + get_wan_connection_status(ext_if_name)]); break; #ifdef ENABLE_6FC_SERVICE case FIREWALLENABLED_MAGICALVALUE: /* see 2.4.2 of UPnP-gw-WANIPv6FirewallControl-v1-Service.pdf */ snprintf(tmp, sizeof(tmp), "%d", ipv6fc_firewall_enabled); str = strcat_str(str, len, &tmplen, tmp); break; case INBOUNDPINHOLEALLOWED_MAGICALVALUE: /* see 2.4.3 of UPnP-gw-WANIPv6FirewallControl-v1-Service.pdf */ snprintf(tmp, sizeof(tmp), "%d", ipv6fc_inbound_pinhole_allowed); str = strcat_str(str, len, &tmplen, tmp); break; #endif #ifdef IGD_V2 case SYSTEMUPDATEID_MAGICALVALUE: /* Please read section 2.3.23 SystemUpdateID * of UPnP-gw-WANIPConnection-v2-Service.pdf */ snprintf(tmp, sizeof(tmp), "%d", 1/* system update id */); str = strcat_str(str, len, &tmplen, tmp); break; #endif case PORTMAPPINGNUMBEROFENTRIES_MAGICALVALUE: /* Port mapping number of entries magical value */ snprintf(tmp, sizeof(tmp), "%d", upnp_get_portmapping_number_of_entries()); str = strcat_str(str, len, &tmplen, tmp); break; case EXTERNALIPADDRESS_MAGICALVALUE: /* External ip address magical value */ if(use_ext_ip_addr) str = strcat_str(str, len, &tmplen, use_ext_ip_addr); else { char ext_ip_addr[INET_ADDRSTRLEN]; if(getifaddr(ext_if_name, ext_ip_addr, INET_ADDRSTRLEN, NULL, NULL) < 0) { str = strcat_str(str, len, &tmplen, "0.0.0.0"); } else { str = strcat_str(str, len, &tmplen, ext_ip_addr); } } break; case DEFAULTCONNECTIONSERVICE_MAGICALVALUE: /* DefaultConnectionService magical value */ str = strcat_str(str, len, &tmplen, uuidvalue_wcd); #ifdef IGD_V2 str = strcat_str(str, len, &tmplen, ":WANConnectionDevice:2,urn:upnp-org:serviceId:WANIPConn1"); #else str = strcat_str(str, len, &tmplen, ":WANConnectionDevice:1,urn:upnp-org:serviceId:WANIPConn1"); #endif break; default: str = strcat_str(str, len, &tmplen, upnpallowedvalues[v->ieventvalue]); } str = strcat_str(str, len, &tmplen, "name); str = strcat_str(str, len, &tmplen, ">"); /*printf("\n", v->name);*/ } v++; } str = strcat_str(str, len, &tmplen, ""); #if 0 printf("\n"); printf("\n"); printf("%d\n", tmplen); #endif str[*len] = '\0'; return str; } char * getVarsWANIPCn(int * l) { return genEventVars(l, &scpdWANIPCn); } char * getVarsWANCfg(int * l) { return genEventVars(l, &scpdWANCfg); } #ifdef ENABLE_L3F_SERVICE char * getVarsL3F(int * l) { return genEventVars(l, &scpdL3F); } #endif #ifdef ENABLE_6FC_SERVICE char * getVars6FC(int * l) { return genEventVars(l, &scpd6FC); } #endif #ifdef ENABLE_DP_SERVICE char * getVarsDP(int * l) { return genEventVars(l, &scpdDP); } #endif #endif /* ENABLE_EVENTS */ miniupnpd-1.8.20130730/upnpdescgen.h010064400017500000024000000045331203340734000161320ustar00nanardstaff/* $Id: upnpdescgen.h,v 1.24 2012/09/27 16:00:10 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2011 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #ifndef UPNPDESCGEN_H_INCLUDED #define UPNPDESCGEN_H_INCLUDED #include "config.h" /* for the root description * The child list reference is stored in "data" member using the * INITHELPER macro with index/nchild always in the * same order, whatever the endianness */ struct XMLElt { const char * eltname; /* begin with '/' if no child */ const char * data; /* Value */ }; /* for service description */ struct serviceDesc { const struct action * actionList; const struct stateVar * serviceStateTable; }; struct action { const char * name; const struct argument * args; }; struct argument { /* the name of the arg is obtained from the variable */ unsigned char dir; /* MSB : don't append "New" Flag, * 5 Medium bits : magic argument name index * 2 LSB : 1 = in, 2 = out */ unsigned char relatedVar; /* index of the related variable */ }; struct stateVar { const char * name; unsigned char itype; /* MSB: sendEvent flag, 7 LSB: index in upnptypes */ unsigned char idefault; /* default value */ unsigned char iallowedlist; /* index in allowed values list * or in allowed range list */ unsigned char ieventvalue; /* fixed value returned or magical values */ }; /* little endian * The code has now be tested on big endian architecture */ #define INITHELPER(i, n) ((char *)(((n)<<16)|(i))) /* char * genRootDesc(int *); * returns: NULL on error, string allocated on the heap */ char * genRootDesc(int * len); /* for the two following functions */ char * genWANIPCn(int * len); char * genWANCfg(int * len); #ifdef ENABLE_L3F_SERVICE char * genL3F(int * len); #endif #ifdef ENABLE_6FC_SERVICE char * gen6FC(int * len); #endif #ifdef ENABLE_DP_SERVICE char * genDP(int * len); #endif #ifdef ENABLE_EVENTS char * getVarsWANIPCn(int * len); char * getVarsWANCfg(int * len); #ifdef ENABLE_L3F_SERVICE char * getVarsL3F(int * len); #endif #ifdef ENABLE_6FC_SERVICE char * getVars6FC(int * len); #endif #ifdef ENABLE_DP_SERVICE char * getVarsDP(int * len); #endif #endif /* ENABLE_EVENTS */ #endif miniupnpd-1.8.20130730/miniupnpdpath.h010064400017500000024000000023511203340734000164730ustar00nanardstaff/* $Id: miniupnpdpath.h,v 1.9 2012/09/27 15:47:15 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2011 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #ifndef MINIUPNPDPATH_H_INCLUDED #define MINIUPNPDPATH_H_INCLUDED #include "config.h" /* Paths and other URLs in the miniupnpd http server */ #define ROOTDESC_PATH "/rootDesc.xml" #ifdef HAS_DUMMY_SERVICE #define DUMMY_PATH "/dummy.xml" #endif #define WANCFG_PATH "/WANCfg.xml" #define WANCFG_CONTROLURL "/ctl/CmnIfCfg" #define WANCFG_EVENTURL "/evt/CmnIfCfg" #define WANIPC_PATH "/WANIPCn.xml" #define WANIPC_CONTROLURL "/ctl/IPConn" #define WANIPC_EVENTURL "/evt/IPConn" #ifdef ENABLE_L3F_SERVICE #define L3F_PATH "/L3F.xml" #define L3F_CONTROLURL "/ctl/L3F" #define L3F_EVENTURL "/evt/L3F" #endif #ifdef ENABLE_6FC_SERVICE #define WANIP6FC_PATH "/WANIP6FC.xml" #define WANIP6FC_CONTROLURL "/ctl/IP6FCtl" #define WANIP6FC_EVENTURL "/evt/IP6FCtl" #endif #ifdef ENABLE_DP_SERVICE /* For DeviceProtection introduced in IGD v2 */ #define DP_PATH "/DP.xml" #define DP_CONTROLURL "/ctl/DP" #define DP_EVENTURL "/evt/DP" #endif #endif miniupnpd-1.8.20130730/testupnpdescgen.c010064400017500000024000000113441217572255200170370ustar00nanardstaff/* $Id: testupnpdescgen.c,v 1.30 2013/06/13 13:21:30 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 #include #include /* for mkdir */ #include #include #include #include "macros.h" #include "config.h" #include "upnpdescgen.h" char uuidvalue_igd[] = "uuid:12345678-0000-0000-0000-000000abcd01"; char uuidvalue_wan[] = "uuid:12345678-0000-0000-0000-000000abcd02"; char uuidvalue_wcd[] = "uuid:12345678-0000-0000-0000-000000abcd03"; char serialnumber[] = "12345678"; char modelnumber[] = "1"; char presentationurl[] = "http://192.168.0.1:8080/"; /*char presentationurl[] = "";*/ char friendly_name[] = OS_NAME " router"; char * use_ext_ip_addr = NULL; const char * ext_if_name = "eth0"; #ifdef ENABLE_6FC_SERVICE int ipv6fc_firewall_enabled = 1; int ipv6fc_inbound_pinhole_allowed = 1; #endif int getifaddr(const char * ifname, char * buf, int len) { UNUSED(ifname); strncpy(buf, "1.2.3.4", len); return 0; } int upnp_get_portmapping_number_of_entries(void) { return 42; } int get_wan_connection_status(const char * ifname) { UNUSED(ifname); return 2; } /* To be improved */ int xml_pretty_print(const char * s, int len, FILE * f) { int n = 0, i; int elt_close = 0; int c, indent = 0; if(!s) return n; while(len > 0) { c = *(s++); len--; switch(c) { case '<': if(len>0 && *s == '/') elt_close++; else if(len>0 && *s == '?') elt_close = 1; else elt_close = 0; if(elt_close!=1) { if(elt_close > 1) indent--; fputc('\n', f); n++; for(i=indent; i>0; i--) fputc(' ', f); n += indent; } fputc(c, f); n++; break; case '>': fputc(c, f); n++; if(elt_close==1) { /*fputc('\n', f); n++; */ /* elt_close = 0; */ if(indent > 0) indent--; } else if(elt_close == 0) indent++; break; case '\n': /* remove existing LF */ break; default: fputc(c, f); n++; } } return n; } /* stupid test */ const char * str1 = "Prefix123String"; const char * str2 = "123String"; void stupid_test(void) { printf("str1:'%s' str2:'%s'\n", str1, str2); printf("str1:%p str2:%p str2-str1:%ld\n", str1, str2, (long)(str2-str1)); } /* main */ int main(int argc, char * * argv) { char * rootDesc; int rootDescLen; char * s; int l; FILE * f; UNUSED(argc); UNUSED(argv); if(mkdir("testdescs", 0777) < 0) { if(errno != EEXIST) { perror("mkdir"); } } printf("Root Description :\n"); rootDesc = genRootDesc(&rootDescLen); xml_pretty_print(rootDesc, rootDescLen, stdout); f = fopen("testdescs/rootdesc.xml", "w"); if(f) { xml_pretty_print(rootDesc, rootDescLen, f); fclose(f); } free(rootDesc); printf("\n-------------\n"); printf("WANIPConnection Description :\n"); s = genWANIPCn(&l); xml_pretty_print(s, l, stdout); f = fopen("testdescs/wanipc_scpd.xml", "w"); if(f) { xml_pretty_print(s, l, f); fclose(f); } free(s); printf("\n-------------\n"); printf("WANConfig Description :\n"); s = genWANCfg(&l); xml_pretty_print(s, l, stdout); f = fopen("testdescs/wanconfig_scpd.xml", "w"); if(f) { xml_pretty_print(s, l, f); fclose(f); } free(s); printf("\n-------------\n"); #ifdef ENABLE_L3F_SERVICE printf("Layer3Forwarding service :\n"); s = genL3F(&l); xml_pretty_print(s, l, stdout); f = fopen("testdescs/l3f_scpd.xml", "w"); if(f) { xml_pretty_print(s, l, f); fclose(f); } free(s); printf("\n-------------\n"); #endif #ifdef ENABLE_6FC_SERVICE printf("WANIPv6FirewallControl service :\n"); s = gen6FC(&l); xml_pretty_print(s, l, stdout); f = fopen("testdescs/wanipv6fc_scpd.xml", "w"); if(f) { xml_pretty_print(s, l, f); fclose(f); } free(s); printf("\n-------------\n"); #endif #ifdef ENABLE_DP_SERVICE printf("DeviceProtection service :\n"); s = genDP(&l); xml_pretty_print(s, l, stdout); f = fopen("testdescs/dp_scpd.xml", "w"); if(f) { xml_pretty_print(s, l, f); fclose(f); } free(s); printf("\n-------------\n"); #endif #ifdef ENABLE_EVENTS s = getVarsWANIPCn(&l); xml_pretty_print(s, l, stdout); free(s); printf("\n-------------\n"); s = getVarsWANCfg(&l); xml_pretty_print(s, l, stdout); free(s); printf("\n-------------\n"); #ifdef ENABLE_L3F_SERVICE s = getVarsL3F(&l); xml_pretty_print(s, l, stdout); free(s); printf("\n-------------\n"); #ifdef ENABLE_6FC_SERVICE s = getVars6FC(&l); xml_pretty_print(s, l, stdout); free(s); printf("\n-------------\n"); #endif #ifdef ENABLE_DP_SERVICE s = getVarsDP(&l); xml_pretty_print(s, l, stdout); free(s); printf("\n-------------\n"); #endif #endif #endif /* stupid_test(); */ return 0; } miniupnpd-1.8.20130730/upnpsoap.c010064400017500000024000001553011217572255200154730ustar00nanardstaff/* $Id: upnpsoap.c,v 1.118 2013/06/13 13:21:30 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 #include #include #include #include #include #include #include #include #include #include #include #include "macros.h" #include "config.h" #include "upnpglobalvars.h" #include "upnphttp.h" #include "upnpsoap.h" #include "upnpreplyparse.h" #include "upnpredirect.h" #include "upnppinhole.h" #include "getifaddr.h" #include "getifstats.h" #include "getconnstatus.h" #include "upnpurns.h" static void BuildSendAndCloseSoapResp(struct upnphttp * h, const char * body, int bodylen) { static const char beforebody[] = "\r\n" "" ""; static const char afterbody[] = "" "\r\n"; BuildHeader_upnphttp(h, 200, "OK", sizeof(beforebody) - 1 + sizeof(afterbody) - 1 + bodylen ); memcpy(h->res_buf + h->res_buflen, beforebody, sizeof(beforebody) - 1); h->res_buflen += sizeof(beforebody) - 1; memcpy(h->res_buf + h->res_buflen, body, bodylen); h->res_buflen += bodylen; memcpy(h->res_buf + h->res_buflen, afterbody, sizeof(afterbody) - 1); h->res_buflen += sizeof(afterbody) - 1; SendRespAndClose_upnphttp(h); } static void GetConnectionTypeInfo(struct upnphttp * h, const char * action) { static const char resp[] = "" "IP_Routed" "IP_Routed" ""; UNUSED(action); BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1); } static void GetTotalBytesSent(struct upnphttp * h, const char * action) { int r; static const char resp[] = "" "%lu" ""; char body[512]; int bodylen; struct ifdata data; r = getifstats(ext_if_name, &data); bodylen = snprintf(body, sizeof(body), resp, action, "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1", r<0?0:data.obytes, action); BuildSendAndCloseSoapResp(h, body, bodylen); } static void GetTotalBytesReceived(struct upnphttp * h, const char * action) { int r; static const char resp[] = "" "%lu" ""; char body[512]; int bodylen; struct ifdata data; r = getifstats(ext_if_name, &data); bodylen = snprintf(body, sizeof(body), resp, action, "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1", r<0?0:data.ibytes, action); BuildSendAndCloseSoapResp(h, body, bodylen); } static void GetTotalPacketsSent(struct upnphttp * h, const char * action) { int r; static const char resp[] = "" "%lu" ""; char body[512]; int bodylen; struct ifdata data; r = getifstats(ext_if_name, &data); bodylen = snprintf(body, sizeof(body), resp, action, "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1", r<0?0:data.opackets, action); BuildSendAndCloseSoapResp(h, body, bodylen); } static void GetTotalPacketsReceived(struct upnphttp * h, const char * action) { int r; static const char resp[] = "" "%lu" ""; char body[512]; int bodylen; struct ifdata data; r = getifstats(ext_if_name, &data); bodylen = snprintf(body, sizeof(body), resp, action, "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1", r<0?0:data.ipackets, action); BuildSendAndCloseSoapResp(h, body, bodylen); } static void GetCommonLinkProperties(struct upnphttp * h, const char * action) { /* WANAccessType : set depending on the hardware : * DSL, POTS (plain old Telephone service), Cable, Ethernet */ static const char resp[] = "" /*"DSL"*/ "Cable" "%lu" "%lu" "%s" ""; char body[2048]; int bodylen; struct ifdata data; const char * status = "Up"; /* Up, Down (Required), * Initializing, Unavailable (Optional) */ char ext_ip_addr[INET_ADDRSTRLEN]; if((downstream_bitrate == 0) || (upstream_bitrate == 0)) { if(getifstats(ext_if_name, &data) >= 0) { if(downstream_bitrate == 0) downstream_bitrate = data.baudrate; if(upstream_bitrate == 0) upstream_bitrate = data.baudrate; } } if(getifaddr(ext_if_name, ext_ip_addr, INET_ADDRSTRLEN, NULL, NULL) < 0) { status = "Down"; } bodylen = snprintf(body, sizeof(body), resp, action, "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1", upstream_bitrate, downstream_bitrate, status, action); BuildSendAndCloseSoapResp(h, body, bodylen); } static void GetStatusInfo(struct upnphttp * h, const char * action) { static const char resp[] = "" "%s" "ERROR_NONE" "%ld" ""; char body[512]; int bodylen; time_t uptime; const char * status; /* ConnectionStatus possible values : * Unconfigured, Connecting, Connected, PendingDisconnect, * Disconnecting, Disconnected */ status = get_wan_connection_status_str(ext_if_name); uptime = (time(NULL) - startup_time); bodylen = snprintf(body, sizeof(body), resp, action, SERVICE_TYPE_WANIPC, status, (long)uptime, action); BuildSendAndCloseSoapResp(h, body, bodylen); } static void GetNATRSIPStatus(struct upnphttp * h, const char * action) { static const char resp[] = "" "0" "1" ""; UNUSED(action); /* 2.2.9. RSIPAvailable * This variable indicates if Realm-specific IP (RSIP) is available * as a feature on the InternetGatewayDevice. RSIP is being defined * in the NAT working group in the IETF to allow host-NATing using * a standard set of message exchanges. It also allows end-to-end * applications that otherwise break if NAT is introduced * (e.g. IPsec-based VPNs). * A gateway that does not support RSIP should set this variable to 0. */ BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1); } static void GetExternalIPAddress(struct upnphttp * h, const char * action) { static const char resp[] = "" "%s" ""; char body[512]; int bodylen; char ext_ip_addr[INET_ADDRSTRLEN]; /* Does that method need to work with IPv6 ? * There is usually no NAT with IPv6 */ #ifndef MULTIPLE_EXTERNAL_IP if(use_ext_ip_addr) { strncpy(ext_ip_addr, use_ext_ip_addr, INET_ADDRSTRLEN); } else if(getifaddr(ext_if_name, ext_ip_addr, INET_ADDRSTRLEN, NULL, NULL) < 0) { syslog(LOG_ERR, "Failed to get ip address for interface %s", ext_if_name); strncpy(ext_ip_addr, "0.0.0.0", INET_ADDRSTRLEN); } #else struct lan_addr_s * lan_addr; strncpy(ext_ip_addr, "0.0.0.0", INET_ADDRSTRLEN); for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next) { if( (h->clientaddr.s_addr & lan_addr->mask.s_addr) == (lan_addr->addr.s_addr & lan_addr->mask.s_addr)) { strncpy(ext_ip_addr, lan_addr->ext_ip_str, INET_ADDRSTRLEN); break; } } #endif bodylen = snprintf(body, sizeof(body), resp, action, SERVICE_TYPE_WANIPC, ext_ip_addr, action); BuildSendAndCloseSoapResp(h, body, bodylen); } /* AddPortMapping method of WANIPConnection Service * Ignored argument : NewEnabled */ static void AddPortMapping(struct upnphttp * h, const char * action) { int r; static const char resp[] = ""; struct NameValueParserData data; char * int_ip, * int_port, * ext_port, * protocol, * desc; char * leaseduration_str; unsigned int leaseduration; char * r_host; unsigned short iport, eport; struct hostent *hp; /* getbyhostname() */ char ** ptr; /* getbyhostname() */ struct in_addr result_ip;/*unsigned char result_ip[16];*/ /* inet_pton() */ ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data); int_ip = GetValueFromNameValueList(&data, "NewInternalClient"); if (!int_ip) { ClearNameValueList(&data); SoapError(h, 402, "Invalid Args"); return; } /* IGD 2 MUST support both wildcard and specific IP address values * for RemoteHost (only the wildcard value was REQUIRED in release 1.0) */ r_host = GetValueFromNameValueList(&data, "NewRemoteHost"); #ifndef SUPPORT_REMOTEHOST #ifdef UPNP_STRICT if (r_host && (strlen(r_host) > 0) && (0 != strcmp(r_host, "*"))) { ClearNameValueList(&data); SoapError(h, 726, "RemoteHostOnlySupportsWildcard"); return; } #endif #endif /* if ip not valid assume hostname and convert */ if (inet_pton(AF_INET, int_ip, &result_ip) <= 0) { hp = gethostbyname(int_ip); if(hp && hp->h_addrtype == AF_INET) { for(ptr = hp->h_addr_list; ptr && *ptr; ptr++) { int_ip = inet_ntoa(*((struct in_addr *) *ptr)); result_ip = *((struct in_addr *) *ptr); /* TODO : deal with more than one ip per hostname */ break; } } else { syslog(LOG_ERR, "Failed to convert hostname '%s' to ip address", int_ip); ClearNameValueList(&data); SoapError(h, 402, "Invalid Args"); return; } } /* check if NewInternalAddress is the client address */ if(GETFLAG(SECUREMODEMASK)) { if(h->clientaddr.s_addr != result_ip.s_addr) { syslog(LOG_INFO, "Client %s tried to redirect port to %s", inet_ntoa(h->clientaddr), int_ip); ClearNameValueList(&data); SoapError(h, 718, "ConflictInMappingEntry"); return; } } int_port = GetValueFromNameValueList(&data, "NewInternalPort"); ext_port = GetValueFromNameValueList(&data, "NewExternalPort"); protocol = GetValueFromNameValueList(&data, "NewProtocol"); desc = GetValueFromNameValueList(&data, "NewPortMappingDescription"); leaseduration_str = GetValueFromNameValueList(&data, "NewLeaseDuration"); if (!int_port || !ext_port || !protocol) { ClearNameValueList(&data); SoapError(h, 402, "Invalid Args"); return; } eport = (unsigned short)atoi(ext_port); iport = (unsigned short)atoi(int_port); leaseduration = leaseduration_str ? atoi(leaseduration_str) : 0; #ifdef IGD_V2 /* PortMappingLeaseDuration can be either a value between 1 and * 604800 seconds or the zero value (for infinite lease time). * Note that an infinite lease time can be only set by out-of-band * mechanisms like WWW-administration, remote management or local * management. * If a control point uses the value 0 to indicate an infinite lease * time mapping, it is REQUIRED that gateway uses the maximum value * instead (e.g. 604800 seconds) */ if(leaseduration == 0 || leaseduration > 604800) leaseduration = 604800; #endif syslog(LOG_INFO, "%s: ext port %hu to %s:%hu protocol %s for: %s leaseduration=%u rhost=%s", action, eport, int_ip, iport, protocol, desc, leaseduration, r_host ? r_host : "NULL"); r = upnp_redirect(r_host, eport, int_ip, iport, protocol, desc, leaseduration); ClearNameValueList(&data); /* possible error codes for AddPortMapping : * 402 - Invalid Args * 501 - Action Failed * 715 - Wildcard not permited in SrcAddr * 716 - Wildcard not permited in ExtPort * 718 - ConflictInMappingEntry * 724 - SamePortValuesRequired (deprecated in IGD v2) * 725 - OnlyPermanentLeasesSupported The NAT implementation only supports permanent lease times on port mappings (deprecated in IGD v2) * 726 - RemoteHostOnlySupportsWildcard RemoteHost must be a wildcard and cannot be a specific IP address or DNS name (deprecated in IGD v2) * 727 - ExternalPortOnlySupportsWildcard ExternalPort must be a wildcard and cannot be a specific port value (deprecated in IGD v2) * 728 - NoPortMapsAvailable There are not enough free prots available to complete the mapping (added in IGD v2) * 729 - ConflictWithOtherMechanisms (added in IGD v2) */ switch(r) { case 0: /* success */ BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1); break; case -2: /* already redirected */ case -3: /* not permitted */ SoapError(h, 718, "ConflictInMappingEntry"); break; default: SoapError(h, 501, "ActionFailed"); } } /* AddAnyPortMapping was added in WANIPConnection v2 */ static void AddAnyPortMapping(struct upnphttp * h, const char * action) { int r; static const char resp[] = "" "%hu" ""; char body[512]; int bodylen; struct NameValueParserData data; const char * int_ip, * int_port, * ext_port, * protocol, * desc; const char * r_host; unsigned short iport, eport; const char * leaseduration_str; unsigned int leaseduration; struct hostent *hp; /* getbyhostname() */ char ** ptr; /* getbyhostname() */ struct in_addr result_ip;/*unsigned char result_ip[16];*/ /* inet_pton() */ ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data); r_host = GetValueFromNameValueList(&data, "NewRemoteHost"); ext_port = GetValueFromNameValueList(&data, "NewExternalPort"); protocol = GetValueFromNameValueList(&data, "NewProtocol"); int_port = GetValueFromNameValueList(&data, "NewInternalPort"); int_ip = GetValueFromNameValueList(&data, "NewInternalClient"); /* NewEnabled */ desc = GetValueFromNameValueList(&data, "NewPortMappingDescription"); leaseduration_str = GetValueFromNameValueList(&data, "NewLeaseDuration"); leaseduration = leaseduration_str ? atoi(leaseduration_str) : 0; if(leaseduration == 0) leaseduration = 604800; if (!int_ip || !ext_port || !int_port) { ClearNameValueList(&data); SoapError(h, 402, "Invalid Args"); return; } eport = (unsigned short)atoi(ext_port); iport = (unsigned short)atoi(int_port); #ifndef SUPPORT_REMOTEHOST #ifdef UPNP_STRICT if (r_host && (strlen(r_host) > 0) && (0 != strcmp(r_host, "*"))) { ClearNameValueList(&data); SoapError(h, 726, "RemoteHostOnlySupportsWildcard"); return; } #endif #endif /* if ip not valid assume hostname and convert */ if (inet_pton(AF_INET, int_ip, &result_ip) <= 0) { hp = gethostbyname(int_ip); if(hp && hp->h_addrtype == AF_INET) { for(ptr = hp->h_addr_list; ptr && *ptr; ptr++) { int_ip = inet_ntoa(*((struct in_addr *) *ptr)); result_ip = *((struct in_addr *) *ptr); /* TODO : deal with more than one ip per hostname */ break; } } else { syslog(LOG_ERR, "Failed to convert hostname '%s' to ip address", int_ip); ClearNameValueList(&data); SoapError(h, 402, "Invalid Args"); return; } } /* check if NewInternalAddress is the client address */ if(GETFLAG(SECUREMODEMASK)) { if(h->clientaddr.s_addr != result_ip.s_addr) { syslog(LOG_INFO, "Client %s tried to redirect port to %s", inet_ntoa(h->clientaddr), int_ip); ClearNameValueList(&data); SoapError(h, 606, "Action not authorized"); return; } } /* TODO : accept a different external port * have some smart strategy to choose the port */ for(;;) { r = upnp_redirect(r_host, eport, int_ip, iport, protocol, desc, leaseduration); if(r==-2 && eport < 65535) { eport++; } else { break; } } ClearNameValueList(&data); switch(r) { case 0: /* success */ bodylen = snprintf(body, sizeof(body), resp, action, SERVICE_TYPE_WANIPC, eport, action); BuildSendAndCloseSoapResp(h, body, bodylen); break; case -2: /* already redirected */ SoapError(h, 718, "ConflictInMappingEntry"); break; case -3: /* not permitted */ SoapError(h, 606, "Action not authorized"); break; default: SoapError(h, 501, "ActionFailed"); } } static void GetSpecificPortMappingEntry(struct upnphttp * h, const char * action) { int r; static const char resp[] = "" "%u" "%s" "1" "%s" "%u" ""; char body[1024]; int bodylen; struct NameValueParserData data; const char * r_host, * ext_port, * protocol; unsigned short eport, iport; char int_ip[32]; char desc[64]; unsigned int leaseduration = 0; ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data); r_host = GetValueFromNameValueList(&data, "NewRemoteHost"); ext_port = GetValueFromNameValueList(&data, "NewExternalPort"); protocol = GetValueFromNameValueList(&data, "NewProtocol"); #ifdef UPNP_STRICT if(!ext_port || !protocol || !r_host) #else if(!ext_port || !protocol) #endif { ClearNameValueList(&data); SoapError(h, 402, "Invalid Args"); return; } #ifndef SUPPORT_REMOTEHOST #ifdef UPNP_STRICT if (r_host && (strlen(r_host) > 0) && (0 != strcmp(r_host, "*"))) { ClearNameValueList(&data); SoapError(h, 726, "RemoteHostOnlySupportsWildcard"); return; } #endif #endif eport = (unsigned short)atoi(ext_port); /* TODO : add r_host as an input parameter ... * We prevent several Port Mapping with same external port * but different remoteHost to be set up, so that is not * a priority. */ r = upnp_get_redirection_infos(eport, protocol, &iport, int_ip, sizeof(int_ip), desc, sizeof(desc), NULL, 0, &leaseduration); if(r < 0) { SoapError(h, 714, "NoSuchEntryInArray"); } else { syslog(LOG_INFO, "%s: rhost='%s' %s %s found => %s:%u desc='%s'", action, r_host ? r_host : "NULL", ext_port, protocol, int_ip, (unsigned int)iport, desc); bodylen = snprintf(body, sizeof(body), resp, action, SERVICE_TYPE_WANIPC, (unsigned int)iport, int_ip, desc, leaseduration, action); BuildSendAndCloseSoapResp(h, body, bodylen); } ClearNameValueList(&data); } static void DeletePortMapping(struct upnphttp * h, const char * action) { int r; static const char resp[] = "" ""; struct NameValueParserData data; const char * r_host, * ext_port, * protocol; unsigned short eport; ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data); r_host = GetValueFromNameValueList(&data, "NewRemoteHost"); ext_port = GetValueFromNameValueList(&data, "NewExternalPort"); protocol = GetValueFromNameValueList(&data, "NewProtocol"); #ifdef UPNP_STRICT if(!ext_port || !protocol || !r_host) #else if(!ext_port || !protocol) #endif { ClearNameValueList(&data); SoapError(h, 402, "Invalid Args"); return; } #ifndef SUPPORT_REMOTEHOST #ifdef UPNP_STRICT if (r_host && (strlen(r_host) > 0) && (0 != strcmp(r_host, "*"))) { ClearNameValueList(&data); SoapError(h, 726, "RemoteHostOnlySupportsWildcard"); return; } #endif #endif eport = (unsigned short)atoi(ext_port); /* TODO : if in secure mode, check the IP * Removing a redirection is not a security threat, * just an annoyance for the user using it. So this is not * a priority. */ syslog(LOG_INFO, "%s: external port: %hu, protocol: %s", action, eport, protocol); r = upnp_delete_redirection(eport, protocol); if(r < 0) { SoapError(h, 714, "NoSuchEntryInArray"); } else { BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1); } ClearNameValueList(&data); } /* DeletePortMappingRange was added in IGD spec v2 */ static void DeletePortMappingRange(struct upnphttp * h, const char * action) { int r = -1; static const char resp[] = "" ""; struct NameValueParserData data; const char * protocol; const char * startport_s, * endport_s; unsigned short startport, endport; /*int manage;*/ unsigned short * port_list; unsigned int i, number = 0; UNUSED(action); ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data); startport_s = GetValueFromNameValueList(&data, "NewStartPort"); endport_s = GetValueFromNameValueList(&data, "NewEndPort"); protocol = GetValueFromNameValueList(&data, "NewProtocol"); /*manage = atoi(GetValueFromNameValueList(&data, "NewManage"));*/ if(startport_s == NULL || endport_s == NULL || protocol == NULL) { SoapError(h, 402, "Invalid Args"); ClearNameValueList(&data); return; } startport = (unsigned short)atoi(startport_s); endport = (unsigned short)atoi(endport_s); /* possible errors : 606 - Action not authorized 730 - PortMappingNotFound 733 - InconsistentParameter */ if(startport > endport) { SoapError(h, 733, "InconsistentParameter"); ClearNameValueList(&data); return; } port_list = upnp_get_portmappings_in_range(startport, endport, protocol, &number); for(i = 0; i < number; i++) { r = upnp_delete_redirection(port_list[i], protocol); /* TODO : check return value for errors */ } free(port_list); BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1); ClearNameValueList(&data); } static void GetGenericPortMappingEntry(struct upnphttp * h, const char * action) { int r; static const char resp[] = "" "%s" "%u" "%s" "%u" "%s" "1" "%s" "%u" ""; long int index = 0; unsigned short eport, iport; const char * m_index; char * endptr; char protocol[4], iaddr[32]; char desc[64]; char rhost[40]; unsigned int leaseduration = 0; struct NameValueParserData data; ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data); m_index = GetValueFromNameValueList(&data, "NewPortMappingIndex"); if(!m_index) { ClearNameValueList(&data); SoapError(h, 402, "Invalid Args"); return; } errno = 0; /* To distinguish success/failure after call */ index = strtol(m_index, &endptr, 10); if((errno == ERANGE && (index == LONG_MAX || index == LONG_MIN)) || (errno != 0 && index == 0) || (m_index == endptr)) { /* should condition (*endptr != '\0') be also an error ? */ if(m_index == endptr) syslog(LOG_WARNING, "%s: no digits were found in <%s>", "GetGenericPortMappingEntry", "NewPortMappingIndex"); else syslog(LOG_WARNING, "%s: strtol('%s'): %m", "GetGenericPortMappingEntry", m_index); ClearNameValueList(&data); SoapError(h, 402, "Invalid Args"); return; } syslog(LOG_INFO, "%s: index=%d", action, (int)index); rhost[0] = '\0'; r = upnp_get_redirection_infos_by_index((int)index, &eport, protocol, &iport, iaddr, sizeof(iaddr), desc, sizeof(desc), rhost, sizeof(rhost), &leaseduration); if(r < 0) { SoapError(h, 713, "SpecifiedArrayIndexInvalid"); } else { int bodylen; char body[2048]; bodylen = snprintf(body, sizeof(body), resp, action, SERVICE_TYPE_WANIPC, rhost, (unsigned int)eport, protocol, (unsigned int)iport, iaddr, desc, leaseduration, action); BuildSendAndCloseSoapResp(h, body, bodylen); } ClearNameValueList(&data); } /* GetListOfPortMappings was added in the IGD v2 specification */ static void GetListOfPortMappings(struct upnphttp * h, const char * action) { static const char resp_start[] = "" "" ""; static const char list_start[] = ""; static const char list_end[] = ""; static const char entry[] = "" "%s" "%hu" "%s" "%hu" "%s" "1" "%s" "%u" ""; char * body; size_t bodyalloc; int bodylen; int r = -1; unsigned short iport; char int_ip[32]; char desc[64]; char rhost[64]; unsigned int leaseduration = 0; struct NameValueParserData data; const char * startport_s, * endport_s; unsigned short startport, endport; const char * protocol; /*int manage;*/ const char * number_s; int number; unsigned short * port_list; unsigned int i, list_size = 0; ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data); startport_s = GetValueFromNameValueList(&data, "NewStartPort"); endport_s = GetValueFromNameValueList(&data, "NewEndPort"); protocol = GetValueFromNameValueList(&data, "NewProtocol"); /*manage_s = GetValueFromNameValueList(&data, "NewManage");*/ number_s = GetValueFromNameValueList(&data, "NewNumberOfPorts"); if(startport_s == NULL || endport_s == NULL || protocol == NULL || number_s == NULL) { SoapError(h, 402, "Invalid Args"); ClearNameValueList(&data); return; } startport = (unsigned short)atoi(startport_s); endport = (unsigned short)atoi(endport_s); /*manage = atoi(manage_s);*/ number = atoi(number_s); if(number == 0) number = 1000; /* return up to 1000 mappings by default */ if(startport > endport) { SoapError(h, 733, "InconsistentParameter"); ClearNameValueList(&data); return; } /* build the PortMappingList xml document : 202.233.2.1 2345 TCP 2345 192.168.1.137 1 dooom 345 */ bodyalloc = 4096; body = malloc(bodyalloc); if(!body) { ClearNameValueList(&data); SoapError(h, 501, "ActionFailed"); return; } bodylen = snprintf(body, bodyalloc, resp_start, action, SERVICE_TYPE_WANIPC); if(bodylen < 0) { SoapError(h, 501, "ActionFailed"); free(body); return; } memcpy(body+bodylen, list_start, sizeof(list_start)); bodylen += (sizeof(list_start) - 1); port_list = upnp_get_portmappings_in_range(startport, endport, protocol, &list_size); /* loop through port mappings */ for(i = 0; number > 0 && i < list_size; i++) { /* have a margin of 1024 bytes to store the new entry */ if((unsigned int)bodylen + 1024 > bodyalloc) { char * body_sav = body; bodyalloc += 4096; body = realloc(body, bodyalloc); if(!body) { ClearNameValueList(&data); SoapError(h, 501, "ActionFailed"); free(body_sav); free(port_list); return; } } rhost[0] = '\0'; r = upnp_get_redirection_infos(port_list[i], protocol, &iport, int_ip, sizeof(int_ip), desc, sizeof(desc), rhost, sizeof(rhost), &leaseduration); if(r == 0) { bodylen += snprintf(body+bodylen, bodyalloc-bodylen, entry, rhost, port_list[i], protocol, iport, int_ip, desc, leaseduration); number--; } } free(port_list); port_list = NULL; memcpy(body+bodylen, list_end, sizeof(list_end)); bodylen += (sizeof(list_end) - 1); bodylen += snprintf(body+bodylen, bodyalloc-bodylen, resp_end, action); BuildSendAndCloseSoapResp(h, body, bodylen); free(body); ClearNameValueList(&data); } #ifdef ENABLE_L3F_SERVICE static void SetDefaultConnectionService(struct upnphttp * h, const char * action) { static const char resp[] = "" ""; struct NameValueParserData data; char * p; ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data); p = GetValueFromNameValueList(&data, "NewDefaultConnectionService"); if(p) { /* 720 InvalidDeviceUUID * 721 InvalidServiceID * 723 InvalidConnServiceSelection */ #ifdef UPNP_STRICT char * service; service = strchr(p, ','); if(0 != memcmp(uuidvalue_wcd, p, sizeof("uuid:00000000-0000-0000-0000-000000000000") - 1)) { SoapError(h, 720, "InvalidDeviceUUID"); } else if(service == NULL || 0 != strcmp(service+1, SERVICE_ID_WANIPC)) { SoapError(h, 721, "InvalidServiceID"); } else #endif { syslog(LOG_INFO, "%s(%s) : Ignored", action, p); BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1); } } else { /* missing argument */ SoapError(h, 402, "Invalid Args"); } ClearNameValueList(&data); } static void GetDefaultConnectionService(struct upnphttp * h, const char * action) { static const char resp[] = "" "%s:WANConnectionDevice:1," SERVICE_ID_WANIPC "" ""; /* example from UPnP_IGD_Layer3Forwarding 1.0.pdf : * uuid:44f5824f-c57d-418c-a131-f22b34e14111:WANConnectionDevice:1, * urn:upnp-org:serviceId:WANPPPConn1 */ char body[1024]; int bodylen; bodylen = snprintf(body, sizeof(body), resp, action, uuidvalue_wcd, action); BuildSendAndCloseSoapResp(h, body, bodylen); } #endif /* Added for compliance with WANIPConnection v2 */ static void SetConnectionType(struct upnphttp * h, const char * action) { const char * connection_type; struct NameValueParserData data; UNUSED(action); ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data); connection_type = GetValueFromNameValueList(&data, "NewConnectionType"); #ifdef UPNP_STRICT if(!connection_type) { ClearNameValueList(&data); SoapError(h, 402, "Invalid Args"); return; } #endif /* Unconfigured, IP_Routed, IP_Bridged */ ClearNameValueList(&data); /* always return a ReadOnly error */ SoapError(h, 731, "ReadOnly"); } /* Added for compliance with WANIPConnection v2 */ static void RequestConnection(struct upnphttp * h, const char * action) { UNUSED(action); SoapError(h, 606, "Action not authorized"); } /* Added for compliance with WANIPConnection v2 */ static void ForceTermination(struct upnphttp * h, const char * action) { UNUSED(action); SoapError(h, 606, "Action not authorized"); } /* If a control point calls QueryStateVariable on a state variable that is not buffered in memory within (or otherwise available from) the service, the service must return a SOAP fault with an errorCode of 404 Invalid Var. QueryStateVariable remains useful as a limited test tool but may not be part of some future versions of UPnP. */ static void QueryStateVariable(struct upnphttp * h, const char * action) { static const char resp[] = "" "%s" ""; char body[512]; int bodylen; struct NameValueParserData data; const char * var_name; ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data); /*var_name = GetValueFromNameValueList(&data, "QueryStateVariable"); */ /*var_name = GetValueFromNameValueListIgnoreNS(&data, "varName");*/ var_name = GetValueFromNameValueList(&data, "varName"); /*syslog(LOG_INFO, "QueryStateVariable(%.40s)", var_name); */ if(!var_name) { SoapError(h, 402, "Invalid Args"); } else if(strcmp(var_name, "ConnectionStatus") == 0) { const char * status; status = get_wan_connection_status_str(ext_if_name); bodylen = snprintf(body, sizeof(body), resp, action, "urn:schemas-upnp-org:control-1-0", status, action); BuildSendAndCloseSoapResp(h, body, bodylen); } #if 0 /* not usefull */ else if(strcmp(var_name, "ConnectionType") == 0) { bodylen = snprintf(body, sizeof(body), resp, "IP_Routed"); BuildSendAndCloseSoapResp(h, body, bodylen); } else if(strcmp(var_name, "LastConnectionError") == 0) { bodylen = snprintf(body, sizeof(body), resp, "ERROR_NONE"); BuildSendAndCloseSoapResp(h, body, bodylen); } #endif else if(strcmp(var_name, "PortMappingNumberOfEntries") == 0) { char strn[10]; snprintf(strn, sizeof(strn), "%i", upnp_get_portmapping_number_of_entries()); bodylen = snprintf(body, sizeof(body), resp, action, "urn:schemas-upnp-org:control-1-0", strn, action); BuildSendAndCloseSoapResp(h, body, bodylen); } else { syslog(LOG_NOTICE, "%s: Unknown: %s", action, var_name?var_name:""); SoapError(h, 404, "Invalid Var"); } ClearNameValueList(&data); } #ifdef ENABLE_6FC_SERVICE #ifndef ENABLE_IPV6 #error "ENABLE_6FC_SERVICE needs ENABLE_IPV6" #endif /* WANIPv6FirewallControl actions */ static void GetFirewallStatus(struct upnphttp * h, const char * action) { static const char resp[] = "" "%d" "%d" ""; char body[512]; int bodylen; bodylen = snprintf(body, sizeof(body), resp, action, "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1", ipv6fc_firewall_enabled, ipv6fc_inbound_pinhole_allowed, action); BuildSendAndCloseSoapResp(h, body, bodylen); } static int CheckStatus(struct upnphttp * h) { if (!ipv6fc_firewall_enabled) { SoapError(h, 702, "FirewallDisabled"); return 0; } else if(!ipv6fc_inbound_pinhole_allowed) { SoapError(h, 703, "InboundPinholeNotAllowed"); return 0; } else return 1; } #if 0 static int connecthostport(const char * host, unsigned short port, char * result) { int s, n; char hostname[INET6_ADDRSTRLEN]; char port_str[8], ifname[8], tmp[4]; struct addrinfo *ai, *p; struct addrinfo hints; memset(&hints, 0, sizeof(hints)); /* hints.ai_flags = AI_ADDRCONFIG; */ #ifdef AI_NUMERICSERV hints.ai_flags = AI_NUMERICSERV; #endif hints.ai_socktype = SOCK_STREAM; hints.ai_family = AF_UNSPEC; /* AF_INET, AF_INET6 or AF_UNSPEC */ /* hints.ai_protocol = IPPROTO_TCP; */ snprintf(port_str, sizeof(port_str), "%hu", port); strcpy(hostname, host); if(!strncmp(host, "fe80", 4)) { printf("Using an linklocal address\n"); strcpy(ifname, "%"); snprintf(tmp, sizeof(tmp), "%d", linklocal_index); strcat(ifname, tmp); strcat(hostname, ifname); printf("host: %s\n", hostname); } n = getaddrinfo(hostname, port_str, &hints, &ai); if(n != 0) { fprintf(stderr, "getaddrinfo() error : %s\n", gai_strerror(n)); return -1; } s = -1; for(p = ai; p; p = p->ai_next) { #ifdef DEBUG char tmp_host[256]; char tmp_service[256]; printf("ai_family=%d ai_socktype=%d ai_protocol=%d ai_addrlen=%d\n ", p->ai_family, p->ai_socktype, p->ai_protocol, p->ai_addrlen); getnameinfo(p->ai_addr, p->ai_addrlen, tmp_host, sizeof(tmp_host), tmp_service, sizeof(tmp_service), NI_NUMERICHOST | NI_NUMERICSERV); printf(" host=%s service=%s\n", tmp_host, tmp_service); #endif inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)p->ai_addr)->sin6_addr), result, INET6_ADDRSTRLEN); return 0; } freeaddrinfo(ai); } #endif /* Check the security policy right */ static int PinholeVerification(struct upnphttp * h, char * int_ip, unsigned short int_port) { int n; char senderAddr[INET6_ADDRSTRLEN]=""; struct addrinfo hints, *ai, *p; struct in6_addr result_ip; /* Pinhole InternalClient address must correspond to the action sender */ syslog(LOG_INFO, "Checking internal IP@ and port (Security policy purpose)"); hints.ai_socktype = SOCK_STREAM; hints.ai_family = AF_UNSPEC; /* if ip not valid assume hostname and convert */ if (inet_pton(AF_INET6, int_ip, &result_ip) <= 0) { n = getaddrinfo(int_ip, NULL, &hints, &ai); if(!n && ai->ai_family == AF_INET6) { for(p = ai; p; p = p->ai_next) { inet_ntop(AF_INET6, (struct in6_addr *) p, int_ip, sizeof(struct in6_addr)); result_ip = *((struct in6_addr *) p); /* TODO : deal with more than one ip per hostname */ break; } } else { syslog(LOG_ERR, "Failed to convert hostname '%s' to ip address", int_ip); SoapError(h, 402, "Invalid Args"); return -1; } freeaddrinfo(p); } if(inet_ntop(AF_INET6, &(h->clientaddr_v6), senderAddr, INET6_ADDRSTRLEN) == NULL) { syslog(LOG_ERR, "inet_ntop: %m"); } #ifdef DEBUG printf("\tPinholeVerification:\n\t\tCompare sender @: %s\n\t\t to intClient @: %s\n", senderAddr, int_ip); #endif if(strcmp(senderAddr, int_ip) != 0) if(h->clientaddr_v6.s6_addr != result_ip.s6_addr) { syslog(LOG_INFO, "Client %s tried to access pinhole for internal %s and is not authorized to do it", senderAddr, int_ip); SoapError(h, 606, "Action not authorized"); return 0; } /* Pinhole InternalPort must be greater than or equal to 1024 */ if (int_port < 1024) { syslog(LOG_INFO, "Client %s tried to access pinhole with port < 1024 and is not authorized to do it", senderAddr); SoapError(h, 606, "Action not authorized"); return 0; } return 1; } static void AddPinhole(struct upnphttp * h, const char * action) { int r; static const char resp[] = "" "%d" ""; char body[512]; int bodylen; struct NameValueParserData data; char * rem_host, * rem_port, * int_ip, * int_port, * protocol, * leaseTime; int uid = 0; unsigned short iport, rport; int ltime; long proto; char rem_ip[INET6_ADDRSTRLEN]; if(CheckStatus(h)==0) return; ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data); rem_host = GetValueFromNameValueList(&data, "RemoteHost"); rem_port = GetValueFromNameValueList(&data, "RemotePort"); int_ip = GetValueFromNameValueList(&data, "InternalClient"); int_port = GetValueFromNameValueList(&data, "InternalPort"); protocol = GetValueFromNameValueList(&data, "Protocol"); leaseTime = GetValueFromNameValueList(&data, "LeaseTime"); rport = (unsigned short)(rem_port ? atoi(rem_port) : 0); iport = (unsigned short)(int_port ? atoi(int_port) : 0); ltime = leaseTime ? atoi(leaseTime) : -1; errno = 0; proto = protocol ? strtol(protocol, NULL, 0) : -1; if(errno != 0 || proto > 65535 || proto < 0) { SoapError(h, 402, "Invalid Args"); goto clear_and_exit; } if(iport == 0) { SoapError(h, 706, "InternalPortWilcardingNotAllowed"); goto clear_and_exit; } /* In particular, [IGD2] RECOMMENDS that unauthenticated and * unauthorized control points are only allowed to invoke * this action with: * - InternalPort value greater than or equal to 1024, * - InternalClient value equals to the control point's IP address. * It is REQUIRED that InternalClient cannot be one of IPv6 * addresses used by the gateway. */ if(!int_ip || 0 == strlen(int_ip) || 0 == strcmp(int_ip, "*")) { SoapError(h, 708, "WildCardNotPermittedInSrcIP"); goto clear_and_exit; } /* I guess it is useless to convert int_ip to literal ipv6 address */ /* rem_host should be converted to literal ipv6 : */ if(rem_host) { struct addrinfo *ai, *p; struct addrinfo hints; int err; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_INET6; /*hints.ai_flags = */ /* hints.ai_protocol = proto; */ err = getaddrinfo(rem_host, rem_port, &hints, &ai); if(err == 0) { /* take the 1st IPv6 address */ for(p = ai; p; p = p->ai_next) { if(p->ai_family == AF_INET6) { inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)p->ai_addr)->sin6_addr), rem_ip, sizeof(rem_ip)); syslog(LOG_INFO, "resolved '%s' to '%s'", rem_host, rem_ip); rem_host = rem_ip; break; } } freeaddrinfo(ai); } else { syslog(LOG_WARNING, "AddPinhole : getaddrinfo(%s) : %s", rem_host, gai_strerror(err)); #if 0 SoapError(h, 402, "Invalid Args"); goto clear_and_exit; #endif } } if(proto == 65535) { SoapError(h, 707, "ProtocolWilcardingNotAllowed"); goto clear_and_exit; } if(proto != IPPROTO_UDP && proto != IPPROTO_TCP #ifdef IPPROTO_UDPITE && atoi(protocol) != IPPROTO_UDPLITE #endif ) { SoapError(h, 705, "ProtocolNotSupported"); goto clear_and_exit; } if(ltime < 1 || ltime > 86400) { syslog(LOG_WARNING, "%s: LeaseTime=%d not supported, (ip=%s)", action, ltime, int_ip); SoapError(h, 402, "Invalid Args"); goto clear_and_exit; } if(PinholeVerification(h, int_ip, iport) <= 0) goto clear_and_exit; syslog(LOG_INFO, "%s: (inbound) from [%s]:%hu to [%s]:%hu with proto %ld during %d sec", action, rem_host?rem_host:"any", rport, int_ip, iport, proto, ltime); /* In cases where the RemoteHost, RemotePort, InternalPort, * InternalClient and Protocol are the same than an existing pinhole, * but LeaseTime is different, the device MUST extend the existing * pinhole's lease time and return the UniqueID of the existing pinhole. */ r = upnp_add_inboundpinhole(rem_host, rport, int_ip, iport, proto, ltime, &uid); switch(r) { case 1: /* success */ bodylen = snprintf(body, sizeof(body), resp, action, "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1", uid, action); BuildSendAndCloseSoapResp(h, body, bodylen); break; case -1: /* not permitted */ SoapError(h, 701, "PinholeSpaceExhausted"); break; default: SoapError(h, 501, "ActionFailed"); break; } /* 606 Action not authorized * 701 PinholeSpaceExhausted * 702 FirewallDisabled * 703 InboundPinholeNotAllowed * 705 ProtocolNotSupported * 706 InternalPortWildcardingNotAllowed * 707 ProtocolWildcardingNotAllowed * 708 WildCardNotPermittedInSrcIP */ clear_and_exit: ClearNameValueList(&data); } static void UpdatePinhole(struct upnphttp * h, const char * action) { static const char resp[] = "" ""; struct NameValueParserData data; const char * uid_str, * leaseTime; char iaddr[INET6_ADDRSTRLEN]; unsigned short iport; int ltime; int uid; int n; if(CheckStatus(h)==0) return; ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data); uid_str = GetValueFromNameValueList(&data, "UniqueID"); leaseTime = GetValueFromNameValueList(&data, "NewLeaseTime"); uid = uid_str ? atoi(uid_str) : -1; ltime = leaseTime ? atoi(leaseTime) : -1; ClearNameValueList(&data); if(uid < 0 || uid > 65535 || ltime <= 0 || ltime > 86400) { SoapError(h, 402, "Invalid Args"); return; } /* Check that client is not updating an pinhole * it doesn't have access to, because of its public access */ n = upnp_get_pinhole_info(uid, NULL, 0, NULL, iaddr, sizeof(iaddr), &iport, NULL, NULL, NULL); if (n >= 0) { if(PinholeVerification(h, iaddr, iport) <= 0) return; } else if(n == -2) { SoapError(h, 704, "NoSuchEntry"); return; } else { SoapError(h, 501, "ActionFailed"); return; } syslog(LOG_INFO, "%s: (inbound) updating lease duration to %d for pinhole with ID: %d", action, ltime, uid); n = upnp_update_inboundpinhole(uid, ltime); if(n == -1) SoapError(h, 704, "NoSuchEntry"); else if(n < 0) SoapError(h, 501, "ActionFailed"); else BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1); } static void GetOutboundPinholeTimeout(struct upnphttp * h, const char * action) { int r; static const char resp[] = "" "%d" ""; char body[512]; int bodylen; struct NameValueParserData data; char * int_ip, * int_port, * rem_host, * rem_port, * protocol; int opt=0, proto=0; unsigned short iport, rport; if (!ipv6fc_firewall_enabled) { SoapError(h, 702, "FirewallDisabled"); return; } ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data); int_ip = GetValueFromNameValueList(&data, "InternalClient"); int_port = GetValueFromNameValueList(&data, "InternalPort"); rem_host = GetValueFromNameValueList(&data, "RemoteHost"); rem_port = GetValueFromNameValueList(&data, "RemotePort"); protocol = GetValueFromNameValueList(&data, "Protocol"); rport = (unsigned short)atoi(rem_port); iport = (unsigned short)atoi(int_port); proto = atoi(protocol); syslog(LOG_INFO, "%s: retrieving timeout for outbound pinhole from [%s]:%hu to [%s]:%hu protocol %s", action, int_ip, iport,rem_host, rport, protocol); /* TODO */ r = -1;/*upnp_check_outbound_pinhole(proto, &opt);*/ switch(r) { case 1: /* success */ bodylen = snprintf(body, sizeof(body), resp, action, "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1", opt, action); BuildSendAndCloseSoapResp(h, body, bodylen); break; case -5: /* Protocol not supported */ SoapError(h, 705, "ProtocolNotSupported"); break; default: SoapError(h, 501, "ActionFailed"); } ClearNameValueList(&data); } static void DeletePinhole(struct upnphttp * h, const char * action) { int n; static const char resp[] = "" ""; struct NameValueParserData data; const char * uid_str; char iaddr[INET6_ADDRSTRLEN]; int proto; unsigned short iport; unsigned int leasetime; int uid; if(CheckStatus(h)==0) return; ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data); uid_str = GetValueFromNameValueList(&data, "UniqueID"); uid = uid_str ? atoi(uid_str) : -1; ClearNameValueList(&data); if(uid < 0 || uid > 65535) { SoapError(h, 402, "Invalid Args"); return; } /* Check that client is not deleting an pinhole * it doesn't have access to, because of its public access */ n = upnp_get_pinhole_info(uid, NULL, 0, NULL, iaddr, sizeof(iaddr), &iport, &proto, &leasetime, NULL); if (n >= 0) { if(PinholeVerification(h, iaddr, iport) <= 0) return; } else if(n == -2) { SoapError(h, 704, "NoSuchEntry"); return; } else { SoapError(h, 501, "ActionFailed"); return; } n = upnp_delete_inboundpinhole(uid); if(n < 0) { syslog(LOG_INFO, "%s: (inbound) failed to remove pinhole with ID: %d", action, uid); SoapError(h, 501, "ActionFailed"); return; } syslog(LOG_INFO, "%s: (inbound) pinhole with ID %d successfully removed", action, uid); BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1); } static void CheckPinholeWorking(struct upnphttp * h, const char * action) { static const char resp[] = "" "%d" ""; char body[512]; int bodylen; int r; struct NameValueParserData data; const char * uid_str; int uid; char iaddr[INET6_ADDRSTRLEN]; unsigned short iport; unsigned int packets; if(CheckStatus(h)==0) return; ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data); uid_str = GetValueFromNameValueList(&data, "UniqueID"); uid = uid_str ? atoi(uid_str) : -1; ClearNameValueList(&data); if(uid < 0 || uid > 65535) { SoapError(h, 402, "Invalid Args"); return; } /* Check that client is not checking a pinhole * it doesn't have access to, because of its public access */ r = upnp_get_pinhole_info(uid, NULL, 0, NULL, iaddr, sizeof(iaddr), &iport, NULL, NULL, &packets); if (r >= 0) { if(PinholeVerification(h, iaddr, iport) <= 0) return ; if(packets == 0) { SoapError(h, 709, "NoPacketSent"); return; } bodylen = snprintf(body, sizeof(body), resp, action, "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1", 1, action); BuildSendAndCloseSoapResp(h, body, bodylen); } else if(r == -2) SoapError(h, 704, "NoSuchEntry"); else SoapError(h, 501, "ActionFailed"); } static void GetPinholePackets(struct upnphttp * h, const char * action) { static const char resp[] = "" "%u" ""; char body[512]; int bodylen; struct NameValueParserData data; const char * uid_str; int n; char iaddr[INET6_ADDRSTRLEN]; unsigned short iport; unsigned int packets = 0; int uid; int proto; unsigned int leasetime; if(CheckStatus(h)==0) return; ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data); uid_str = GetValueFromNameValueList(&data, "UniqueID"); uid = uid_str ? atoi(uid_str) : -1; ClearNameValueList(&data); if(uid < 0 || uid > 65535) { SoapError(h, 402, "Invalid Args"); return; } /* Check that client is not getting infos of a pinhole * it doesn't have access to, because of its public access */ n = upnp_get_pinhole_info(uid, NULL, 0, NULL, iaddr, sizeof(iaddr), &iport, &proto, &leasetime, &packets); if (n >= 0) { if(PinholeVerification(h, iaddr, iport)<=0) return ; } #if 0 else if(r == -4 || r == -1) { SoapError(h, 704, "NoSuchEntry"); } #endif bodylen = snprintf(body, sizeof(body), resp, action, "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1", packets, action); BuildSendAndCloseSoapResp(h, body, bodylen); } #endif /* Windows XP as client send the following requests : * GetConnectionTypeInfo * GetNATRSIPStatus * ? GetTotalBytesSent - WANCommonInterfaceConfig * ? GetTotalBytesReceived - idem * ? GetTotalPacketsSent - idem * ? GetTotalPacketsReceived - idem * GetCommonLinkProperties - idem * GetStatusInfo - WANIPConnection * GetExternalIPAddress * QueryStateVariable / ConnectionStatus! */ static const struct { const char * methodName; void (*methodImpl)(struct upnphttp *, const char *); } soapMethods[] = { /* WANCommonInterfaceConfig */ { "QueryStateVariable", QueryStateVariable}, { "GetTotalBytesSent", GetTotalBytesSent}, { "GetTotalBytesReceived", GetTotalBytesReceived}, { "GetTotalPacketsSent", GetTotalPacketsSent}, { "GetTotalPacketsReceived", GetTotalPacketsReceived}, { "GetCommonLinkProperties", GetCommonLinkProperties}, { "GetStatusInfo", GetStatusInfo}, /* WANIPConnection */ { "GetConnectionTypeInfo", GetConnectionTypeInfo }, { "GetNATRSIPStatus", GetNATRSIPStatus}, { "GetExternalIPAddress", GetExternalIPAddress}, { "AddPortMapping", AddPortMapping}, { "DeletePortMapping", DeletePortMapping}, { "GetGenericPortMappingEntry", GetGenericPortMappingEntry}, { "GetSpecificPortMappingEntry", GetSpecificPortMappingEntry}, /* Required in WANIPConnection:2 */ { "SetConnectionType", SetConnectionType}, { "RequestConnection", RequestConnection}, { "ForceTermination", ForceTermination}, { "AddAnyPortMapping", AddAnyPortMapping}, { "DeletePortMappingRange", DeletePortMappingRange}, { "GetListOfPortMappings", GetListOfPortMappings}, #ifdef ENABLE_L3F_SERVICE /* Layer3Forwarding */ { "SetDefaultConnectionService", SetDefaultConnectionService}, { "GetDefaultConnectionService", GetDefaultConnectionService}, #endif #ifdef ENABLE_6FC_SERVICE /* WANIPv6FirewallControl */ { "GetFirewallStatus", GetFirewallStatus}, /* Required */ { "AddPinhole", AddPinhole}, /* Required */ { "UpdatePinhole", UpdatePinhole}, /* Required */ { "GetOutboundPinholeTimeout", GetOutboundPinholeTimeout}, /* Optional */ { "DeletePinhole", DeletePinhole}, /* Required */ { "CheckPinholeWorking", CheckPinholeWorking}, /* Optional */ { "GetPinholePackets", GetPinholePackets}, /* Required */ #endif { 0, 0 } }; void ExecuteSoapAction(struct upnphttp * h, const char * action, int n) { char * p; char * p2; int i, len, methodlen; i = 0; p = strchr(action, '#'); if(p) { p++; p2 = strchr(p, '"'); if(p2) methodlen = p2 - p; else methodlen = n - (p - action); /*syslog(LOG_DEBUG, "SoapMethod: %.*s", methodlen, p);*/ while(soapMethods[i].methodName) { len = strlen(soapMethods[i].methodName); if(strncmp(p, soapMethods[i].methodName, len) == 0) { soapMethods[i].methodImpl(h, soapMethods[i].methodName); return; } i++; } syslog(LOG_NOTICE, "SoapMethod: Unknown: %.*s", methodlen, p); } SoapError(h, 401, "Invalid Action"); } /* Standard Errors: * * errorCode errorDescription Description * -------- ---------------- ----------- * 401 Invalid Action No action by that name at this service. * 402 Invalid Args Could be any of the following: not enough in args, * too many in args, no in arg by that name, * one or more in args are of the wrong data type. * 403 Out of Sync Out of synchronization. * 501 Action Failed May be returned in current state of service * prevents invoking that action. * 600-699 TBD Common action errors. Defined by UPnP Forum * Technical Committee. * 700-799 TBD Action-specific errors for standard actions. * Defined by UPnP Forum working committee. * 800-899 TBD Action-specific errors for non-standard actions. * Defined by UPnP vendor. */ void SoapError(struct upnphttp * h, int errCode, const char * errDesc) { static const char resp[] = "" "" "" "s:Client" "UPnPError" "" "" "%d" "%s" "" "" "" "" ""; char body[2048]; int bodylen; syslog(LOG_INFO, "Returning UPnPError %d: %s", errCode, errDesc); bodylen = snprintf(body, sizeof(body), resp, errCode, errDesc); BuildResp2_upnphttp(h, 500, "Internal Server Error", body, bodylen); SendRespAndClose_upnphttp(h); } miniupnpd-1.8.20130730/upnpsoap.h010064400017500000024000000012171203340734000154600ustar00nanardstaff/* $Id: upnpsoap.h,v 1.10 2012/09/27 15:46:18 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #ifndef UPNPSOAP_H_INCLUDED #define UPNPSOAP_H_INCLUDED /* ExecuteSoapAction(): * this method executes the requested Soap Action */ void ExecuteSoapAction(struct upnphttp *, const char *, int); /* SoapError(): * sends a correct SOAP error with an UPNPError code and * description */ void SoapError(struct upnphttp * h, int errCode, const char * errDesc); #endif miniupnpd-1.8.20130730/pf/obsdrdr.h010064400017500000024000000036731203340734100156700ustar00nanardstaff/* $Id: obsdrdr.h,v 1.21 2012/09/27 16:02:43 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #ifndef OBSDRDR_H_INCLUDED #define OBSDRDR_H_INCLUDED #include "../commonrdr.h" /* add_redirect_rule2() uses DIOCCHANGERULE ioctl * proto can take the values IPPROTO_UDP or IPPROTO_TCP */ int add_redirect_rule2(const char * ifname, const char * rhost, unsigned short eport, const char * iaddr, unsigned short iport, int proto, const char * desc, unsigned int timestamp); /* add_filter_rule2() uses DIOCCHANGERULE ioctl * proto can take the values IPPROTO_UDP or IPPROTO_TCP */ int add_filter_rule2(const char * ifname, const char * rhost, const char * iaddr, unsigned short eport, unsigned short iport, int proto, const char * desc); /* get_redirect_rule() gets internal IP and port from * interface, external port and protocl */ #if 0 int get_redirect_rule(const char * ifname, unsigned short eport, int proto, char * iaddr, int iaddrlen, unsigned short * iport, char * desc, int desclen, u_int64_t * packets, u_int64_t * bytes); int get_redirect_rule_by_index(int index, char * ifname, unsigned short * eport, char * iaddr, int iaddrlen, unsigned short * iport, int * proto, char * desc, int desclen, u_int64_t * packets, u_int64_t * bytes); #endif /* delete_redirect_rule() */ int delete_redirect_rule(const char * ifname, unsigned short eport, int proto); /* delete_filter_rule() */ int delete_filter_rule(const char * ifname, unsigned short eport, int proto); int clear_redirect_rules(void); #endif miniupnpd-1.8.20130730/pf/obsdrdr.c010064400017500000024000000537261174772535300157100ustar00nanardstaff/* $Id: obsdrdr.c,v 1.74 2012/05/01 09:20:43 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2012 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ /* * pf rules created (with ext_if = xl1) * - OpenBSD up to version 4.6 : * rdr pass on xl1 inet proto udp from any to any port = 54321 \ * label "test label" -> 192.168.0.141 port 12345 * or a rdr rule + a pass rule * * - OpenBSD starting from version 4.7 * match in on xl1 inet proto udp from any to any port 54321 \ * label "test label" rdr-to 192.168.0.141 port 12345 * or * pass in quick on xl1 inet proto udp from any to any port 54321 \ * label "test label" rdr-to 192.168.0.141 port 12345 * * * * Macros/#defines : * - PF_ENABLE_FILTER_RULES * If set, two rules are created : rdr + pass. Else a rdr/pass rule * is created. * - USE_IFNAME_IN_RULES * If set the interface name is set in the rule. * - PFRULE_INOUT_COUNTS * Must be set with OpenBSD version 3.8 and up. * - PFRULE_HAS_RTABLEID * Must be set with OpenBSD version 4.0 and up. * - PF_NEWSSTYLE * Must be set with OpenBSD version 4.7 and up. */ #include #include #include #include #include #include #include #ifdef __DragonFly__ #include #else #include #endif #include #include #include #include #include #include #include #include "../macros.h" #include "../config.h" #include "obsdrdr.h" #include "../upnpglobalvars.h" /* list too keep timestamps for port mappings having a lease duration */ struct timestamp_entry { struct timestamp_entry * next; unsigned int timestamp; unsigned short eport; short protocol; }; static struct timestamp_entry * timestamp_list = NULL; static unsigned int get_timestamp(unsigned short eport, int proto) { struct timestamp_entry * e; e = timestamp_list; while(e) { if(e->eport == eport && e->protocol == (short)proto) return e->timestamp; e = e->next; } return 0; } static void remove_timestamp_entry(unsigned short eport, int proto) { struct timestamp_entry * e; struct timestamp_entry * * p; p = ×tamp_list; e = *p; while(e) { if(e->eport == eport && e->protocol == (short)proto) { /* remove the entry */ *p = e->next; free(e); return; } p = &(e->next); e = *p; } } /* /dev/pf when opened */ int dev = -1; /* shutdown_redirect() : * close the /dev/pf device */ void shutdown_redirect(void) { if(close(dev)<0) syslog(LOG_ERR, "close(\"/dev/pf\"): %m"); dev = -1; } /* open the device */ int init_redirect(void) { struct pf_status status; if(dev>=0) shutdown_redirect(); dev = open("/dev/pf", O_RDWR); if(dev<0) { syslog(LOG_ERR, "open(\"/dev/pf\"): %m"); return -1; } if(ioctl(dev, DIOCGETSTATUS, &status)<0) { syslog(LOG_ERR, "DIOCGETSTATUS: %m"); return -1; } if(!status.running) { syslog(LOG_ERR, "pf is disabled"); return -1; } return 0; } #if TEST /* for debug */ int clear_redirect_rules(void) { struct pfioc_trans io; struct pfioc_trans_e ioe; if(dev<0) { syslog(LOG_ERR, "pf device is not open"); return -1; } memset(&ioe, 0, sizeof(ioe)); io.size = 1; io.esize = sizeof(ioe); io.array = &ioe; #ifndef PF_NEWSTYLE ioe.rs_num = PF_RULESET_RDR; #else ioe.type = PF_TRANS_RULESET; #endif strlcpy(ioe.anchor, anchor_name, MAXPATHLEN); if(ioctl(dev, DIOCXBEGIN, &io) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCXBEGIN, ...): %m"); goto error; } if(ioctl(dev, DIOCXCOMMIT, &io) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCXCOMMIT, ...): %m"); goto error; } return 0; error: return -1; } #endif /* add_redirect_rule2() : * create a rdr rule */ int add_redirect_rule2(const char * ifname, const char * rhost, unsigned short eport, const char * iaddr, unsigned short iport, int proto, const char * desc, unsigned int timestamp) { int r; struct pfioc_rule pcr; #ifndef PF_NEWSTYLE struct pfioc_pooladdr pp; struct pf_pooladdr *a; #endif if(dev<0) { syslog(LOG_ERR, "pf device is not open"); return -1; } r = 0; memset(&pcr, 0, sizeof(pcr)); strlcpy(pcr.anchor, anchor_name, MAXPATHLEN); #ifndef PF_NEWSTYLE memset(&pp, 0, sizeof(pp)); strlcpy(pp.anchor, anchor_name, MAXPATHLEN); if(ioctl(dev, DIOCBEGINADDRS, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCBEGINADDRS, ...): %m"); r = -1; } else { pcr.pool_ticket = pp.ticket; #else if(1) { pcr.rule.direction = PF_IN; /*pcr.rule.src.addr.type = PF_ADDR_NONE;*/ pcr.rule.src.addr.type = PF_ADDR_ADDRMASK; pcr.rule.dst.addr.type = PF_ADDR_ADDRMASK; pcr.rule.nat.addr.type = PF_ADDR_NONE; pcr.rule.rdr.addr.type = PF_ADDR_ADDRMASK; #endif pcr.rule.dst.port_op = PF_OP_EQ; pcr.rule.dst.port[0] = htons(eport); pcr.rule.dst.port[1] = htons(eport); #ifndef PF_NEWSTYLE pcr.rule.action = PF_RDR; #ifndef PF_ENABLE_FILTER_RULES pcr.rule.natpass = 1; #else pcr.rule.natpass = 0; #endif #else #ifndef PF_ENABLE_FILTER_RULES pcr.rule.action = PF_PASS; #else pcr.rule.action = PF_MATCH; #endif #endif pcr.rule.af = AF_INET; #ifdef USE_IFNAME_IN_RULES if(ifname) strlcpy(pcr.rule.ifname, ifname, IFNAMSIZ); #endif pcr.rule.proto = proto; pcr.rule.log = (GETFLAG(LOGPACKETSMASK))?1:0; /*logpackets;*/ #ifdef PFRULE_HAS_RTABLEID pcr.rule.rtableid = -1; /* first appeared in OpenBSD 4.0 */ #endif #ifdef PFRULE_HAS_ONRDOMAIN pcr.rule.onrdomain = -1; /* first appeared in OpenBSD 5.0 */ #endif pcr.rule.quick = 1; pcr.rule.keep_state = PF_STATE_NORMAL; if(tag) strlcpy(pcr.rule.tagname, tag, PF_TAG_NAME_SIZE); strlcpy(pcr.rule.label, desc, PF_RULE_LABEL_SIZE); if(rhost && rhost[0] != '\0' && rhost[0] != '*') { inet_pton(AF_INET, rhost, &pcr.rule.src.addr.v.a.addr.v4.s_addr); pcr.rule.src.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE); } #ifndef PF_NEWSTYLE pcr.rule.rpool.proxy_port[0] = iport; pcr.rule.rpool.proxy_port[1] = iport; TAILQ_INIT(&pcr.rule.rpool.list); a = calloc(1, sizeof(struct pf_pooladdr)); inet_pton(AF_INET, iaddr, &a->addr.v.a.addr.v4.s_addr); a->addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE); TAILQ_INSERT_TAIL(&pcr.rule.rpool.list, a, entries); memcpy(&pp.addr, a, sizeof(struct pf_pooladdr)); if(ioctl(dev, DIOCADDADDR, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCADDADDR, ...): %m"); r = -1; } else { #else pcr.rule.rdr.proxy_port[0] = iport; pcr.rule.rdr.proxy_port[1] = iport; inet_pton(AF_INET, iaddr, &pcr.rule.rdr.addr.v.a.addr.v4.s_addr); pcr.rule.rdr.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE); if(1) { #endif pcr.action = PF_CHANGE_GET_TICKET; if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m"); r = -1; } else { pcr.action = PF_CHANGE_ADD_TAIL; if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_ADD_TAIL: %m"); r = -1; } } } #ifndef PF_NEWSTYLE free(a); #endif } if(r == 0 && timestamp > 0) { struct timestamp_entry * tmp; tmp = malloc(sizeof(struct timestamp_entry)); if(tmp) { tmp->next = timestamp_list; tmp->timestamp = timestamp; tmp->eport = eport; tmp->protocol = (short)proto; timestamp_list = tmp; } } return r; } /* thanks to Seth Mos for this function */ int add_filter_rule2(const char * ifname, const char * rhost, const char * iaddr, unsigned short eport, unsigned short iport, int proto, const char * desc) { #ifndef PF_ENABLE_FILTER_RULES UNUSED(ifname); UNUSED(rhost); UNUSED(iaddr); UNUSED(eport); UNUSED(iport); UNUSED(proto); UNUSED(desc); return 0; #else int r; struct pfioc_rule pcr; #ifndef PF_NEWSTYLE struct pfioc_pooladdr pp; struct pf_pooladdr *a; #endif if(dev<0) { syslog(LOG_ERR, "pf device is not open"); return -1; } r = 0; memset(&pcr, 0, sizeof(pcr)); strlcpy(pcr.anchor, anchor_name, MAXPATHLEN); #ifndef PF_NEWSTYLE memset(&pp, 0, sizeof(pp)); strlcpy(pp.anchor, anchor_name, MAXPATHLEN); if(ioctl(dev, DIOCBEGINADDRS, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCBEGINADDRS, ...): %m"); r = -1; } else { pcr.pool_ticket = pp.ticket; #else if(1) { #endif pcr.rule.dst.port_op = PF_OP_EQ; pcr.rule.dst.port[0] = htons(eport); pcr.rule.direction = PF_IN; pcr.rule.action = PF_PASS; pcr.rule.af = AF_INET; #ifdef USE_IFNAME_IN_RULES if(ifname) strlcpy(pcr.rule.ifname, ifname, IFNAMSIZ); #endif pcr.rule.proto = proto; pcr.rule.quick = (GETFLAG(PFNOQUICKRULESMASK))?0:1; pcr.rule.log = (GETFLAG(LOGPACKETSMASK))?1:0; /*logpackets;*/ /* see the discussion on the forum : * http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=638 */ pcr.rule.flags = TH_SYN; pcr.rule.flagset = (TH_SYN|TH_ACK); #ifdef PFRULE_HAS_RTABLEID pcr.rule.rtableid = -1; /* first appeared in OpenBSD 4.0 */ #endif #ifdef PFRULE_HAS_ONRDOMAIN pcr.rule.onrdomain = -1; /* first appeared in OpenBSD 5.0 */ #endif pcr.rule.keep_state = 1; strlcpy(pcr.rule.label, desc, PF_RULE_LABEL_SIZE); if(queue) strlcpy(pcr.rule.qname, queue, PF_QNAME_SIZE); if(tag) strlcpy(pcr.rule.tagname, tag, PF_TAG_NAME_SIZE); if(rhost && rhost[0] != '\0' && rhost[0] != '*') { inet_pton(AF_INET, rhost, &pcr.rule.src.addr.v.a.addr.v4.s_addr); pcr.rule.src.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE); } #ifndef PF_NEWSTYLE pcr.rule.rpool.proxy_port[0] = eport; a = calloc(1, sizeof(struct pf_pooladdr)); inet_pton(AF_INET, iaddr, &a->addr.v.a.addr.v4.s_addr); a->addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE); memcpy(&pp.addr, a, sizeof(struct pf_pooladdr)); TAILQ_INIT(&pcr.rule.rpool.list); inet_pton(AF_INET, iaddr, &a->addr.v.a.addr.v4.s_addr); TAILQ_INSERT_TAIL(&pcr.rule.rpool.list, a, entries); /* we have any - any port = # keep state label */ /* we want any - iaddr port = # keep state label */ /* memcpy(&pcr.rule.dst, a, sizeof(struct pf_pooladdr)); */ memcpy(&pp.addr, a, sizeof(struct pf_pooladdr)); strlcpy(pcr.rule.label, desc, PF_RULE_LABEL_SIZE); if(ioctl(dev, DIOCADDADDR, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCADDADDR, ...): %m"); r = -1; } else { #else if(1) { #endif pcr.action = PF_CHANGE_GET_TICKET; if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m"); r = -1; } else { pcr.action = PF_CHANGE_ADD_TAIL; if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_ADD_TAIL: %m"); r = -1; } } } #ifndef PF_NEWSTYLE free(a); #endif } return r; #endif } /* get_redirect_rule() * return value : 0 success (found) * -1 = error or rule not found */ int get_redirect_rule(const char * ifname, unsigned short eport, int proto, char * iaddr, int iaddrlen, unsigned short * iport, char * desc, int desclen, char * rhost, int rhostlen, unsigned int * timestamp, u_int64_t * packets, u_int64_t * bytes) { int i, n; struct pfioc_rule pr; #ifndef PF_NEWSTYLE struct pfioc_pooladdr pp; #endif UNUSED(ifname); if(dev<0) { syslog(LOG_ERR, "pf device is not open"); return -1; } memset(&pr, 0, sizeof(pr)); strlcpy(pr.anchor, anchor_name, MAXPATHLEN); #ifndef PF_NEWSTYLE pr.rule.action = PF_RDR; #endif if(ioctl(dev, DIOCGETRULES, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m"); goto error; } n = pr.nr; for(i=0; i 0) { if (pr.rule.src.addr.v.a.addr.v4.s_addr == 0) { rhost[0] = '\0'; /* empty string */ } else { inet_ntop(AF_INET, &pr.rule.src.addr.v.a.addr.v4.s_addr, rhost, rhostlen); } } if(timestamp) *timestamp = get_timestamp(eport, proto); return 0; } } error: return -1; } int delete_redirect_rule(const char * ifname, unsigned short eport, int proto) { int i, n; struct pfioc_rule pr; UNUSED(ifname); if(dev<0) { syslog(LOG_ERR, "pf device is not open"); return -1; } memset(&pr, 0, sizeof(pr)); strlcpy(pr.anchor, anchor_name, MAXPATHLEN); #ifndef PF_NEWSTYLE pr.rule.action = PF_RDR; #endif if(ioctl(dev, DIOCGETRULES, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m"); goto error; } n = pr.nr; for(i=0; i= n) goto error; pr.nr = index; if(ioctl(dev, DIOCGETRULE, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m"); goto error; } *proto = pr.rule.proto; *eport = ntohs(pr.rule.dst.port[0]); #ifndef PF_NEWSTYLE *iport = pr.rule.rpool.proxy_port[0]; #else *iport = pr.rule.rdr.proxy_port[0]; #endif if(ifname) strlcpy(ifname, pr.rule.ifname, IFNAMSIZ); if(desc) strlcpy(desc, pr.rule.label, desclen); #ifdef PFRULE_INOUT_COUNTS if(packets) *packets = pr.rule.packets[0] + pr.rule.packets[1]; if(bytes) *bytes = pr.rule.bytes[0] + pr.rule.bytes[1]; #else if(packets) *packets = pr.rule.packets; if(bytes) *bytes = pr.rule.bytes; #endif #ifndef PF_NEWSTYLE memset(&pp, 0, sizeof(pp)); strlcpy(pp.anchor, anchor_name, MAXPATHLEN); pp.r_action = PF_RDR; pp.r_num = index; pp.ticket = pr.ticket; if(ioctl(dev, DIOCGETADDRS, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETADDRS, ...): %m"); goto error; } if(pp.nr != 1) { syslog(LOG_NOTICE, "No address associated with pf rule"); goto error; } pp.nr = 0; /* first */ if(ioctl(dev, DIOCGETADDR, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETADDR, ...): %m"); goto error; } inet_ntop(AF_INET, &pp.addr.addr.v.a.addr.v4.s_addr, iaddr, iaddrlen); #else inet_ntop(AF_INET, &pr.rule.rdr.addr.v.a.addr.v4.s_addr, iaddr, iaddrlen); #endif if(rhost && rhostlen > 0) { if (pr.rule.src.addr.v.a.addr.v4.s_addr == 0) { rhost[0] = '\0'; /* empty string */ } else { inet_ntop(AF_INET, &pr.rule.src.addr.v.a.addr.v4.s_addr, rhost, rhostlen); } } if(timestamp) *timestamp = get_timestamp(*eport, *proto); return 0; error: return -1; } /* return an (malloc'ed) array of "external" port for which there is * a port mapping. number is the size of the array */ unsigned short * get_portmappings_in_range(unsigned short startport, unsigned short endport, int proto, unsigned int * number) { unsigned short * array; unsigned int capacity; int i, n; unsigned short eport; struct pfioc_rule pr; *number = 0; if(dev<0) { syslog(LOG_ERR, "pf device is not open"); return NULL; } capacity = 128; array = calloc(capacity, sizeof(unsigned short)); if(!array) { syslog(LOG_ERR, "get_portmappings_in_range() : calloc error"); return NULL; } memset(&pr, 0, sizeof(pr)); strlcpy(pr.anchor, anchor_name, MAXPATHLEN); #ifndef PF_NEWSTYLE pr.rule.action = PF_RDR; #endif if(ioctl(dev, DIOCGETRULES, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m"); free(array); return NULL; } n = pr.nr; for(i=0; i= capacity) { /* need to increase the capacity of the array */ capacity += 128; array = realloc(array, sizeof(unsigned short)*capacity); if(!array) { syslog(LOG_ERR, "get_portmappings_in_range() : realloc(%lu) error", sizeof(unsigned short)*capacity); *number = 0; return NULL; } } array[*number] = eport; (*number)++; } } return array; } /* this function is only for testing */ #if TEST void list_rules(void) { char buf[32]; int i, n; struct pfioc_rule pr; #ifndef PF_NEWSTYLE struct pfioc_pooladdr pp; #endif if(dev<0) { perror("pf dev not open"); return ; } memset(&pr, 0, sizeof(pr)); strlcpy(pr.anchor, anchor_name, MAXPATHLEN); pr.rule.action = PF_RDR; if(ioctl(dev, DIOCGETRULES, &pr) < 0) perror("DIOCGETRULES"); printf("ticket = %d, nr = %d\n", pr.ticket, pr.nr); n = pr.nr; for(i=0; i %d:%d proto %d keep_state=%d action=%d\n", pr.rule.ifname, inet_ntop(AF_INET, &pr.rule.src.addr.v.a.addr.v4.s_addr, buf, 32), (int)ntohs(pr.rule.dst.port[0]), (int)ntohs(pr.rule.dst.port[1]), #ifndef PF_NEWSTYLE (int)pr.rule.rpool.proxy_port[0], (int)pr.rule.rpool.proxy_port[1], #else (int)pr.rule.rdr.proxy_port[0], (int)pr.rule.rdr.proxy_port[1], #endif (int)pr.rule.proto, (int)pr.rule.keep_state, (int)pr.rule.action); printf(" description: \"%s\"\n", pr.rule.label); #ifndef PF_NEWSTYLE memset(&pp, 0, sizeof(pp)); strlcpy(pp.anchor, anchor_name, MAXPATHLEN); pp.r_action = PF_RDR; pp.r_num = i; pp.ticket = pr.ticket; if(ioctl(dev, DIOCGETADDRS, &pp) < 0) perror("DIOCGETADDRS"); printf(" nb pool addr = %d ticket=%d\n", pp.nr, pp.ticket); /*if(ioctl(dev, DIOCGETRULE, &pr) < 0) perror("DIOCGETRULE"); */ pp.nr = 0; /* first */ if(ioctl(dev, DIOCGETADDR, &pp) < 0) perror("DIOCGETADDR"); /* addr.v.a.addr.v4.s_addr */ printf(" %s\n", inet_ntop(AF_INET, &pp.addr.addr.v.a.addr.v4.s_addr, buf, 32)); #else printf(" rule_flag=%08x action=%d direction=%d log=%d logif=%d " "quick=%d ifnot=%d af=%d type=%d code=%d rdr.port_op=%d rdr.opts=%d\n", pr.rule.rule_flag, pr.rule.action, pr.rule.direction, pr.rule.log, pr.rule.logif, pr.rule.quick, pr.rule.ifnot, pr.rule.af, pr.rule.type, pr.rule.code, pr.rule.rdr.port_op, pr.rule.rdr.opts); printf(" %s\n", inet_ntop(AF_INET, &pr.rule.rdr.addr.v.a.addr.v4.s_addr, buf, 32)); #endif } } #endif miniupnpd-1.8.20130730/pf/testobsdrdr.c010064400017500000024000000061201174361441300165600ustar00nanardstaff/* $Id: testobsdrdr.c,v 1.24 2012/04/18 19:42:03 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2012 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #include #include #include #include #include "obsdrdr.h" /*int logpackets = 1;*/ int runtime_flags = 0; const char * tag = 0; const char * anchor_name = "miniupnpd"; void list_rules(void); void list_eports_tcp(void) { unsigned short * port_list; unsigned int number = 0; unsigned int i; port_list = get_portmappings_in_range(0, 65535, IPPROTO_TCP, &number); printf("%u ports redirected (TCP) :", number); for(i = 0; i < number; i++) { printf(" %hu", port_list[i]); } printf("\n"); free(port_list); } void test_index(void) { char ifname[16/*IFNAMSIZ*/]; char iaddr[32]; char desc[64]; char rhost[32]; unsigned short iport = 0; unsigned short eport = 0; int proto = 0; unsigned int timestamp; ifname[0] = '\0'; iaddr[0] = '\0'; rhost[0] = '\0'; if(get_redirect_rule_by_index(0, ifname, &eport, iaddr, sizeof(iaddr), &iport, &proto, desc, sizeof(desc), rhost, sizeof(rhost), ×tamp, 0, 0) < 0) { printf("get.._by_index : no rule\n"); } else { printf("%s %u -> %s:%u proto %d\n", ifname, (unsigned int)eport, iaddr, (unsigned int)iport, proto); printf("description: \"%s\"\n", desc); } } int main(int arc, char * * argv) { char buf[32]; char desc[64]; char rhost[64]; /*char rhost[32];*/ unsigned short iport; unsigned int timestamp; u_int64_t packets = 0; u_int64_t bytes = 0; openlog("testobsdrdr", LOG_PERROR, LOG_USER); if(init_redirect() < 0) { fprintf(stderr, "init_redirect() failed\n"); return 1; } #if 0 add_redirect_rule("ep0", 12123, "192.168.1.23", 1234); add_redirect_rule2("ep0", 12155, "192.168.1.155", 1255, IPPROTO_TCP); #endif add_redirect_rule2("ep0", "8.8.8.8", 12123, "192.168.1.125", 1234, IPPROTO_UDP, "test description", 0); #if 0 add_redirect_rule2("em0", 12123, "127.1.2.3", 1234, IPPROTO_TCP, "test description tcp"); #endif list_rules(); list_eports_tcp(); if(get_redirect_rule("xl1", 4662, IPPROTO_TCP, buf, sizeof(buf), &iport, desc, sizeof(desc), rhost, sizeof(rhost), ×tamp, &packets, &bytes) < 0) printf("get_redirect_rule() failed\n"); else { printf("\n%s:%d '%s' packets=%llu bytes=%llu\n", buf, (int)iport, desc, packets, bytes); } if(delete_redirect_rule("ep0", 12123, IPPROTO_UDP) < 0) printf("delete_redirect_rule() failed\n"); else printf("delete_redirect_rule() succeded\n"); if(delete_redirect_rule("ep0", 12123, IPPROTO_UDP) < 0) printf("delete_redirect_rule() failed\n"); else printf("delete_redirect_rule() succeded\n"); #if 0 test_index(); clear_redirect_rules(); list_rules(); #endif return 0; } miniupnpd-1.8.20130730/pf/Makefile010064400017500000024000000010051174362375500155230ustar00nanardstaff# $Id: Makefile,v 1.4 2012/04/18 20:45:33 nanard Exp $ # made for GNU Make (and BSD make) CFLAGS = -Wall -g -ansi -DTEST EXECUTABLES = testobsdrdr testpfpinhole all: $(EXECUTABLES) clean: rm -f *.o $(EXECUTABLES) testobsdrdr: testobsdrdr.o obsdrdr.o $(CC) $(CFLAGS) -o $@ $> testpfpinhole: testpfpinhole.o obsdrdr.o pfpinhole.o $(CC) $(CFLAGS) -o $@ $> obsdrdr.o: obsdrdr.c obsdrdr.h pfpinhole.o: pfpinhole.c pfpinhole.h testobsdrdr.o: testobsdrdr.c obsdrdr.h testpfpinhole.o: testpfpinhole.c pfpinhole.h miniupnpd-1.8.20130730/bsd/Makefile010064400017500000024000000006621156543264100156700ustar00nanardstaff# $Id: Makefile,v 1.2 2011/05/20 09:34:25 nanard Exp $ # made for GNU Make CFLAGS = -Wall -g EXECUTABLES = testgetifstats testifacewatcher all: $(EXECUTABLES) clean: rm -f *.o $(EXECUTABLES) testobsdrdr.o: testobsdrdr.c obsdrdr.h testgetifstats: testgetifstats.o getifstats.o $(CC) $(CFLAGS) -o $@ $> -lkvm testifacewatcher: testifacewatcher.o ifacewatcher.o upnputils.o $(CC) $(CFLAGS) -o $@ $> upnputils.o: ../upnputils.c miniupnpd-1.8.20130730/bsd/getifstats.c010064400017500000024000000062261174772742000165570ustar00nanardstaff/* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * author: Ryan Wagoner and Thomas Bernard * (c) 2006 Ryan Wagoner * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #include #include #include #include #include #if defined(__FreeBSD__) || defined(__DragonFly__) #include #endif #if defined(__DragonFly__) #include #else #include #endif #include #include #include #include #include #include #include #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) { #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]; #ifdef ENABLE_GETIFSTATS_CACHING static time_t cache_timestamp = 0; static struct ifdata cache_data; time_t current_time; #endif if(!data) return -1; data->baudrate = 4200000; data->opackets = 0; data->ipackets = 0; data->obytes = 0; 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; } } #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(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); #ifdef ENABLE_GETIFSTATS_CACHING if(current_time!=((time_t)-1)) { cache_timestamp = current_time; memcpy(&cache_data, data, sizeof(struct ifdata)); } #endif return 0; /* ok */ } } error: kvm_close(kd); return -1; /* not found or error */ } miniupnpd-1.8.20130730/bsd/testgetifstats.c010064400017500000024000000013311172522177200174420ustar00nanardstaff/* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #include "../getifstats.h" int main(int argc, char * * argv) { int r; struct ifdata data; printf("usage: %s if_name\n", argv[0]); if(argc<2) return -1; r = getifstats(argv[1], &data); if(r<0) printf("getifstats() failed\n"); else { printf("ipackets = %10lu opackets = %10lu\n", data.ipackets, data.opackets); printf("ibytes = %10lu obytes = %10lu\n", data.ibytes, data.obytes); printf("baudrate = %10lu\n", data.baudrate); } return 0; } miniupnpd-1.8.20130730/solaris/getifstats.c010064400017500000024000000044161172522177200174550ustar00nanardstaff/* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * author: Ryan Wagoner and Thomas Bernard * (c) 2007 Darren Reed * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #include #include #include #include #include #include "../getifstats.h" int getifstats(const char * ifname, struct ifdata * data) { char buffer[64], *s; kstat_named_t *kn; kstat_ctl_t *kc; int instance; kstat_t *ksp; uint32_t cnt32; void *ptr; if (data == NULL) goto error; if (ifname == NULL || *ifname == '\0') goto error; s = (char *)ifname + strlen(ifname); s--; while ((s > ifname) && isdigit(*s)) s--; s++; instance = atoi(s); strlcpy(buffer, ifname, MIN(s - ifname + 1, 64)); kc = kstat_open(); if (kc != NULL) { ksp = kstat_lookup(kc, buffer, instance, (char *)ifname); if (ksp && (kstat_read(kc, ksp, NULL) != -1)) { /* found the right interface */ if (sizeof(long) == 8) { uint64_t cnt64; kn = kstat_data_lookup(ksp, "rbytes64"); if (kn != NULL) { data->ibytes = kn->value.i64; } kn = kstat_data_lookup(ksp, "ipackets64"); if (kn != NULL) { data->ipackets = kn->value.i64; } kn = kstat_data_lookup(ksp, "obytes64"); if (kn != NULL) { data->obytes = kn->value.i64; } kn = kstat_data_lookup(ksp, "opackets64"); if (kn != NULL) { data->opackets = kn->value.i64; } } else { kn = kstat_data_lookup(ksp, "rbytes"); if (kn != NULL) { data->ibytes = kn->value.i32; } kn = kstat_data_lookup(ksp, "ipackets"); if (kn != NULL) { data->ipackets = kn->value.i32; } kn = kstat_data_lookup(ksp, "obytes"); if (kn != NULL) { data->obytes = kn->value.i32; } kn = kstat_data_lookup(ksp, "opackets"); if (kn != NULL) { data->ipackets = kn->value.i32; } } kn = kstat_data_lookup(ksp, "ifspeed"); if (kn != NULL) { data->baudrate = kn->value.i32; } kstat_close(kc); return 0; /* ok */ } syslog(LOG_ERR, "kstat_lookup/read() failed: %m"); kstat_close(kc); return -1; } else { syslog(LOG_ERR, "kstat_open() failed: %m"); } error: return -1; /* not found or error */ } miniupnpd-1.8.20130730/getifstats.h010064400017500000024000000012231203340734000157650ustar00nanardstaff/* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2008 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #ifndef GETIFSTATS_H_INCLUDED #define GETIFSTATS_H_INCLUDED struct ifdata { unsigned long opackets; unsigned long ipackets; unsigned long obytes; unsigned long ibytes; unsigned long baudrate; }; /* getifstats() * Fill the ifdata structure with statistics for network interface ifname. * Return 0 in case of success, -1 for bad arguments or any error */ int getifstats(const char * ifname, struct ifdata * data); #endif miniupnpd-1.8.20130730/upnpredirect.h010064400017500000024000000071231203340734000163210ustar00nanardstaff/* $Id: upnpredirect.h,v 1.35 2012/09/27 15:47:15 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2012 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #ifndef UPNPREDIRECT_H_INCLUDED #define UPNPREDIRECT_H_INCLUDED /* for u_int64_t */ #include #include "config.h" #ifdef ENABLE_LEASEFILE int reload_from_lease_file(void); #endif /* upnp_redirect() * calls OS/fw dependant implementation of the redirection. * protocol should be the string "TCP" or "UDP" * returns: 0 on success * -1 failed to redirect * -2 already redirected * -3 permission check failed */ int upnp_redirect(const char * rhost, unsigned short eport, const char * iaddr, unsigned short iport, const char * protocol, const char * desc, unsigned int leaseduration); /* upnp_redirect_internal() * same as upnp_redirect() without any check */ int upnp_redirect_internal(const char * rhost, unsigned short eport, const char * iaddr, unsigned short iport, int proto, const char * desc, unsigned int timestamp); /* upnp_get_redirection_infos() * returns : 0 on success * -1 failed to get the port mapping entry or no entry exists */ int upnp_get_redirection_infos(unsigned short eport, const char * protocol, unsigned short * iport, char * iaddr, int iaddrlen, char * desc, int desclen, char * rhost, int rhostlen, unsigned int * leaseduration); /* upnp_get_redirection_infos_by_index() * returns : 0 on success * -1 failed to get the port mapping or index out of range */ int upnp_get_redirection_infos_by_index(int index, unsigned short * eport, char * protocol, unsigned short * iport, char * iaddr, int iaddrlen, char * desc, int desclen, char * rhost, int rhostlen, unsigned int * leaseduration); /* upnp_delete_redirection() * returns: 0 on success * -1 on failure*/ int upnp_delete_redirection(unsigned short eport, const char * protocol); /* _upnp_delete_redir() * same as above */ int _upnp_delete_redir(unsigned short eport, int proto); /* Periodic cleanup functions */ struct rule_state { u_int64_t packets; u_int64_t bytes; struct rule_state * next; unsigned short eport; unsigned char proto; unsigned char to_remove; }; /* return a linked list of all rules * or an empty list if there are not enough * As a "side effect", delete rules which are expired */ struct rule_state * get_upnp_rules_state_list(int max_rules_number_target); /* return the number of port mapping entries */ int upnp_get_portmapping_number_of_entries(void); /* remove_unused_rules() : * also free the list */ void remove_unused_rules(struct rule_state * list); /* upnp_get_portmappings_in_range() * return a list of all "external" ports for which a port * mapping exists */ unsigned short * upnp_get_portmappings_in_range(unsigned short startport, unsigned short endport, const char * protocol, unsigned int * number); /* stuff for responding to miniupnpdctl */ #ifdef USE_MINIUPNPDCTL void write_ruleset_details(int s); #endif #endif miniupnpd-1.8.20130730/upnpredirect.c010064400017500000024000000374661175004160600163350ustar00nanardstaff/* $Id: upnpredirect.c,v 1.80 2012/05/01 20:08:22 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2012 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #include #include #include #include #include #include #include #include #include #include #include "macros.h" #include "config.h" #include "upnpredirect.h" #include "upnpglobalvars.h" #include "upnpevents.h" #if defined(USE_NETFILTER) #include "netfilter/iptcrdr.h" #endif #if defined(USE_PF) #include "pf/obsdrdr.h" #endif #if defined(USE_IPF) #include "ipf/ipfrdr.h" #endif #if defined(USE_IPFW) #include "ipfw/ipfwrdr.h" #endif #ifdef USE_MINIUPNPDCTL #include #include #endif #ifdef ENABLE_LEASEFILE #include #endif /* from */ #ifndef PRIu64 #define PRIu64 "llu" #endif /* proto_atoi() * convert the string "UDP" or "TCP" to IPPROTO_UDP and IPPROTO_UDP */ static int proto_atoi(const char * protocol) { int proto = IPPROTO_TCP; if(strcmp(protocol, "UDP") == 0) proto = IPPROTO_UDP; return proto; } #ifdef ENABLE_LEASEFILE static int lease_file_add(unsigned short eport, const char * iaddr, unsigned short iport, int proto, const char * desc, unsigned int timestamp) { FILE * fd; if (lease_file == NULL) return 0; fd = fopen( lease_file, "a"); if (fd==NULL) { syslog(LOG_ERR, "could not open lease file: %s", lease_file); return -1; } fprintf(fd, "%s:%hu:%s:%hu:%u:%s\n", ((proto==IPPROTO_TCP)?"TCP":"UDP"), eport, iaddr, iport, timestamp, desc); fclose(fd); return 0; } static int lease_file_remove(unsigned short eport, int proto) { FILE* fd, *fdt; int tmp; char buf[512]; char str[32]; char tmpfilename[128]; int str_size, buf_size; if (lease_file == NULL) return 0; if (strlen( lease_file) + 7 > sizeof(tmpfilename)) { syslog(LOG_ERR, "Lease filename is too long"); return -1; } strncpy( tmpfilename, lease_file, sizeof(tmpfilename) ); strncat( tmpfilename, "XXXXXX", sizeof(tmpfilename) - strlen(tmpfilename)); fd = fopen( lease_file, "r"); if (fd==NULL) { return 0; } snprintf( str, sizeof(str), "%s:%u", ((proto==IPPROTO_TCP)?"TCP":"UDP"), eport); str_size = strlen(str); tmp = mkstemp(tmpfilename); if (tmp==-1) { fclose(fd); syslog(LOG_ERR, "could not open temporary lease file"); return -1; } fchmod(tmp, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); fdt = fdopen(tmp, "a"); buf[sizeof(buf)-1] = 0; while( fgets(buf, sizeof(buf)-1, fd) != NULL) { buf_size = strlen(buf); if (buf_size < str_size || strncmp(str, buf, str_size)!=0) { fwrite(buf, buf_size, 1, fdt); } } fclose(fdt); fclose(fd); if (rename(tmpfilename, lease_file) < 0) { syslog(LOG_ERR, "could not rename temporary lease file to %s", lease_file); remove(tmpfilename); } return 0; } /* reload_from_lease_file() * read lease_file and add the rules contained */ int reload_from_lease_file() { FILE * fd; char * p; unsigned short eport, iport; char * proto; char * iaddr; char * desc; char * rhost; unsigned int leaseduration; unsigned int timestamp; time_t current_time; char line[128]; int r; if(!lease_file) return -1; fd = fopen( lease_file, "r"); if (fd==NULL) { syslog(LOG_ERR, "could not open lease file: %s", lease_file); return -1; } if(unlink(lease_file) < 0) { syslog(LOG_WARNING, "could not unlink file %s : %m", lease_file); } current_time = time(NULL); while(fgets(line, sizeof(line), fd)) { syslog(LOG_DEBUG, "parsing lease file line '%s'", line); proto = line; p = strchr(line, ':'); if(!p) { syslog(LOG_ERR, "unrecognized data in lease file"); continue; } *(p++) = '\0'; iaddr = strchr(p, ':'); if(!iaddr) { syslog(LOG_ERR, "unrecognized data in lease file"); continue; } *(iaddr++) = '\0'; eport = (unsigned short)atoi(p); p = strchr(iaddr, ':'); if(!p) { syslog(LOG_ERR, "unrecognized data in lease file"); continue; } *(p++) = '\0'; iport = (unsigned short)atoi(p); p = strchr(p, ':'); if(!p) { syslog(LOG_ERR, "unrecognized data in lease file"); continue; } *(p++) = '\0'; desc = strchr(p, ':'); if(!desc) { syslog(LOG_ERR, "unrecognized data in lease file"); continue; } *(desc++) = '\0'; /*timestamp = (unsigned int)atoi(p);*/ timestamp = (unsigned int)strtoul(p, NULL, 10); /* trim description */ while(isspace(*desc)) desc++; p = desc; while(*(p+1)) p++; while(isspace(*p) && (p > desc)) *(p--) = '\0'; if(timestamp > 0) { if(timestamp <= (unsigned int)current_time) { syslog(LOG_NOTICE, "already expired lease in lease file"); continue; } else { leaseduration = timestamp - current_time; } } else { leaseduration = 0; /* default value */ } rhost = NULL; r = upnp_redirect(rhost, eport, iaddr, iport, proto, desc, leaseduration); if(r == -1) { syslog(LOG_ERR, "Failed to redirect %hu -> %s:%hu protocol %s", eport, iaddr, iport, proto); } else if(r == -2) { /* Add the redirection again to the lease file */ lease_file_add(eport, iaddr, iport, proto_atoi(proto), desc, timestamp); } } fclose(fd); return 0; } #endif /* upnp_redirect() * calls OS/fw dependant implementation of the redirection. * protocol should be the string "TCP" or "UDP" * returns: 0 on success * -1 failed to redirect * -2 already redirected * -3 permission check failed */ int upnp_redirect(const char * rhost, unsigned short eport, const char * iaddr, unsigned short iport, const char * protocol, const char * desc, unsigned int leaseduration) { int proto, r; char iaddr_old[32]; unsigned short iport_old; struct in_addr address; unsigned int timestamp; proto = proto_atoi(protocol); if(inet_aton(iaddr, &address) < 0) { syslog(LOG_ERR, "inet_aton(%s) : %m", iaddr); return -1; } if(!check_upnp_rule_against_permissions(upnppermlist, num_upnpperm, eport, address, iport)) { syslog(LOG_INFO, "redirection permission check failed for " "%hu->%s:%hu %s", eport, iaddr, iport, protocol); return -3; } r = get_redirect_rule(ext_if_name, eport, proto, iaddr_old, sizeof(iaddr_old), &iport_old, 0, 0, 0, 0, ×tamp, 0, 0); if(r == 0) { /* if existing redirect rule matches redirect request return success * xbox 360 does not keep track of the port it redirects and will * redirect another port when receiving ConflictInMappingEntry */ if(strcmp(iaddr, iaddr_old)==0 && iport==iport_old) { syslog(LOG_INFO, "ignoring redirect request as it matches existing redirect"); } else { syslog(LOG_INFO, "port %hu protocol %s already redirected to %s:%hu", eport, protocol, iaddr_old, iport_old); return -2; } } else { timestamp = (leaseduration > 0) ? time(NULL) + leaseduration : 0; syslog(LOG_INFO, "redirecting port %hu to %s:%hu protocol %s for: %s", eport, iaddr, iport, protocol, desc); return upnp_redirect_internal(rhost, eport, iaddr, iport, proto, desc, timestamp); } return 0; } int upnp_redirect_internal(const char * rhost, unsigned short eport, const char * iaddr, unsigned short iport, int proto, const char * desc, unsigned int timestamp) { /*syslog(LOG_INFO, "redirecting port %hu to %s:%hu protocol %s for: %s", eport, iaddr, iport, protocol, desc); */ if(add_redirect_rule2(ext_if_name, rhost, eport, iaddr, iport, proto, desc, timestamp) < 0) { return -1; } #ifdef ENABLE_LEASEFILE lease_file_add( eport, iaddr, iport, proto, desc, timestamp); #endif /* syslog(LOG_INFO, "creating pass rule to %s:%hu protocol %s for: %s", iaddr, iport, protocol, desc);*/ if(add_filter_rule2(ext_if_name, rhost, iaddr, eport, iport, proto, desc) < 0) { /* clean up the redirect rule */ #if !defined(__linux__) delete_redirect_rule(ext_if_name, eport, proto); #endif return -1; } if(timestamp > 0) { if(!nextruletoclean_timestamp || (timestamp < nextruletoclean_timestamp)) nextruletoclean_timestamp = timestamp; } #ifdef ENABLE_EVENTS /* the number of port mappings changed, we must * inform the subscribers */ upnp_event_var_change_notify(EWanIPC); #endif return 0; } /* Firewall independant code which call the FW dependant code. */ int upnp_get_redirection_infos(unsigned short eport, const char * protocol, unsigned short * iport, char * iaddr, int iaddrlen, char * desc, int desclen, char * rhost, int rhostlen, unsigned int * leaseduration) { int r; unsigned int timestamp; time_t current_time; if(desc && (desclen > 0)) desc[0] = '\0'; if(rhost && (rhostlen > 0)) rhost[0] = '\0'; r = get_redirect_rule(ext_if_name, eport, proto_atoi(protocol), iaddr, iaddrlen, iport, desc, desclen, rhost, rhostlen, ×tamp, 0, 0); if(r == 0 && timestamp > 0 && timestamp > (unsigned int)(current_time = time(NULL))) { *leaseduration = timestamp - current_time; } else { *leaseduration = 0; } return r; } int upnp_get_redirection_infos_by_index(int index, unsigned short * eport, char * protocol, unsigned short * iport, char * iaddr, int iaddrlen, char * desc, int desclen, char * rhost, int rhostlen, unsigned int * leaseduration) { /*char ifname[IFNAMSIZ];*/ int proto = 0; unsigned int timestamp; time_t current_time; if(desc && (desclen > 0)) desc[0] = '\0'; if(rhost && (rhostlen > 0)) rhost[0] = '\0'; if(get_redirect_rule_by_index(index, 0/*ifname*/, eport, iaddr, iaddrlen, iport, &proto, desc, desclen, rhost, rhostlen, ×tamp, 0, 0) < 0) return -1; else { current_time = time(NULL); *leaseduration = (timestamp > (unsigned int)current_time) ? (timestamp - current_time) : 0; if(proto == IPPROTO_TCP) memcpy(protocol, "TCP", 4); else memcpy(protocol, "UDP", 4); return 0; } } /* called from natpmp.c too */ int _upnp_delete_redir(unsigned short eport, int proto) { int r; #if defined(__linux__) r = delete_redirect_and_filter_rules(eport, proto); #else r = delete_redirect_rule(ext_if_name, eport, proto); delete_filter_rule(ext_if_name, eport, proto); #endif #ifdef ENABLE_LEASEFILE lease_file_remove( eport, proto); #endif #ifdef ENABLE_EVENTS upnp_event_var_change_notify(EWanIPC); #endif return r; } int upnp_delete_redirection(unsigned short eport, const char * protocol) { syslog(LOG_INFO, "removing redirect rule port %hu %s", eport, protocol); return _upnp_delete_redir(eport, proto_atoi(protocol)); } /* upnp_get_portmapping_number_of_entries() * TODO: improve this code. */ int upnp_get_portmapping_number_of_entries() { int n = 0, r = 0; unsigned short eport, iport; char protocol[4], iaddr[32], desc[64], rhost[32]; unsigned int leaseduration; do { protocol[0] = '\0'; iaddr[0] = '\0'; desc[0] = '\0'; r = upnp_get_redirection_infos_by_index(n, &eport, protocol, &iport, iaddr, sizeof(iaddr), desc, sizeof(desc), rhost, sizeof(rhost), &leaseduration); n++; } while(r==0); return (n-1); } /* functions used to remove unused rules * As a side effect, delete expired rules (based on LeaseDuration) */ struct rule_state * get_upnp_rules_state_list(int max_rules_number_target) { /*char ifname[IFNAMSIZ];*/ int proto; unsigned short iport; unsigned int timestamp; struct rule_state * tmp; struct rule_state * list = 0; struct rule_state * * p; int i = 0; time_t current_time; /*ifname[0] = '\0';*/ tmp = malloc(sizeof(struct rule_state)); if(!tmp) return 0; current_time = time(NULL); nextruletoclean_timestamp = 0; while(get_redirect_rule_by_index(i, /*ifname*/0, &tmp->eport, 0, 0, &iport, &proto, 0, 0, 0,0, ×tamp, &tmp->packets, &tmp->bytes) >= 0) { tmp->to_remove = 0; if(timestamp > 0) { /* need to remove this port mapping ? */ if(timestamp <= (unsigned int)current_time) tmp->to_remove = 1; else if((nextruletoclean_timestamp <= (unsigned int)current_time) || (timestamp < nextruletoclean_timestamp)) nextruletoclean_timestamp = timestamp; } tmp->proto = (short)proto; /* add tmp to list */ tmp->next = list; list = tmp; /* prepare next iteration */ i++; tmp = malloc(sizeof(struct rule_state)); if(!tmp) break; } free(tmp); /* remove the redirections that need to be removed */ for(p = &list, tmp = list; tmp; tmp = *p) { if(tmp->to_remove) { syslog(LOG_NOTICE, "remove port mapping %hu %s because it has expired", tmp->eport, (tmp->proto==IPPROTO_TCP)?"TCP":"UDP"); _upnp_delete_redir(tmp->eport, tmp->proto); *p = tmp->next; free(tmp); i--; } else { p = &(tmp->next); } } /* return empty list if not enough redirections */ if(i<=max_rules_number_target) while(list) { tmp = list; list = tmp->next; free(tmp); } /* return list */ return list; } void remove_unused_rules(struct rule_state * list) { char ifname[IFNAMSIZ]; unsigned short iport; struct rule_state * tmp; u_int64_t packets; u_int64_t bytes; unsigned int timestamp; int n = 0; while(list) { /* remove the rule if no traffic has used it */ if(get_redirect_rule(ifname, list->eport, list->proto, 0, 0, &iport, 0, 0, 0, 0, ×tamp, &packets, &bytes) >= 0) { if(packets == list->packets && bytes == list->bytes) { _upnp_delete_redir(list->eport, list->proto); n++; } } tmp = list; list = tmp->next; free(tmp); } if(n>0) syslog(LOG_NOTICE, "removed %d unused rules", n); } /* upnp_get_portmappings_in_range() * return a list of all "external" ports for which a port * mapping exists */ unsigned short * upnp_get_portmappings_in_range(unsigned short startport, unsigned short endport, const char * protocol, unsigned int * number) { int proto; proto = proto_atoi(protocol); if(!number) return NULL; return get_portmappings_in_range(startport, endport, proto, number); } /* stuff for miniupnpdctl */ #ifdef USE_MINIUPNPDCTL void write_ruleset_details(int s) { int proto = 0; unsigned short eport, iport; char desc[64]; char iaddr[32]; char rhost[32]; unsigned int timestamp; u_int64_t packets; u_int64_t bytes; int i = 0; char buffer[256]; int n; write(s, "Ruleset :\n", 10); while(get_redirect_rule_by_index(i, 0/*ifname*/, &eport, iaddr, sizeof(iaddr), &iport, &proto, desc, sizeof(desc), rhost, sizeof(rhost), ×tamp, &packets, &bytes) >= 0) { n = snprintf(buffer, sizeof(buffer), "%2d %s %s:%hu->%s:%hu " "'%s' %u %" PRIu64 " %" PRIu64 "\n", /*"'%s' %llu %llu\n",*/ i, proto==IPPROTO_TCP?"TCP":"UDP", rhost, eport, iaddr, iport, desc, timestamp, packets, bytes); write(s, buffer, n); i++; } } #endif miniupnpd-1.8.20130730/getifaddr.c010064400017500000024000000074171213677053100155600ustar00nanardstaff/* $Id: getifaddr.c,v 1.17 2013/04/27 15:40:09 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 #include #include #include #include #include #include #include #include #include #if defined(sun) #include #endif #include "config.h" #include "getifaddr.h" #if defined(USE_GETIFADDRS) || defined(ENABLE_IPV6) #include #endif int getifaddr(const char * ifname, char * buf, int len, struct in_addr * addr, struct in_addr * mask) { #ifndef USE_GETIFADDRS /* use ioctl SIOCGIFADDR. Works only for ip v4 */ /* SIOCGIFADDR struct ifreq * */ int s; struct ifreq ifr; int ifrlen; struct sockaddr_in * ifaddr; ifrlen = sizeof(ifr); if(!ifname || ifname[0]=='\0') return -1; s = socket(PF_INET, SOCK_DGRAM, 0); if(s < 0) { syslog(LOG_ERR, "socket(PF_INET, SOCK_DGRAM): %m"); return -1; } strncpy(ifr.ifr_name, ifname, IFNAMSIZ); if(ioctl(s, SIOCGIFADDR, &ifr, &ifrlen) < 0) { syslog(LOG_ERR, "ioctl(s, SIOCGIFADDR, ...): %m"); close(s); return -1; } ifaddr = (struct sockaddr_in *)&ifr.ifr_addr; if(addr) *addr = ifaddr->sin_addr; if(!inet_ntop(AF_INET, &ifaddr->sin_addr, buf, len)) { syslog(LOG_ERR, "inet_ntop(): %m"); close(s); return -1; } if(mask) { strncpy(ifr.ifr_name, ifname, IFNAMSIZ); if(ioctl(s, SIOCGIFNETMASK, &ifr, &ifrlen) < 0) { syslog(LOG_ERR, "ioctl(s, SIOCGIFNETMASK, ...): %m"); close(s); return -1; } #ifdef ifr_netmask *mask = ((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr; #else *mask = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr; #endif } close(s); #else /* ifndef USE_GETIFADDRS */ /* Works for all address families (both ip v4 and ip v6) */ struct ifaddrs * ifap; struct ifaddrs * ife; if(!ifname || ifname[0]=='\0') return -1; if(getifaddrs(&ifap)<0) { syslog(LOG_ERR, "getifaddrs: %m"); return -1; } for(ife = ifap; ife; ife = ife->ifa_next) { /* skip other interfaces if one was specified */ if(ifname && (0 != strcmp(ifname, ife->ifa_name))) continue; if(ife->ifa_addr == NULL) continue; switch(ife->ifa_addr->sa_family) { case AF_INET: inet_ntop(ife->ifa_addr->sa_family, &((struct sockaddr_in *)ife->ifa_addr)->sin_addr, buf, len); if(addr) *addr = ((struct sockaddr_in *)ife->ifa_addr)->sin_addr; if(mask) *mask = ((struct sockaddr_in *)ife->ifa_netmask)->sin_addr; break; /* case AF_INET6: inet_ntop(ife->ifa_addr->sa_family, &((struct sockaddr_in6 *)ife->ifa_addr)->sin6_addr, buf, len); */ } } freeifaddrs(ifap); #endif return 0; } #ifdef ENABLE_IPV6 int find_ipv6_addr(const char * ifname, char * dst, int n) { struct ifaddrs * ifap; struct ifaddrs * ife; const struct sockaddr_in6 * addr; char buf[64]; int r = 0; if(!dst) return -1; if(getifaddrs(&ifap)<0) { syslog(LOG_ERR, "getifaddrs: %m"); return -1; } for(ife = ifap; ife; ife = ife->ifa_next) { /* skip other interfaces if one was specified */ if(ifname && (0 != strcmp(ifname, ife->ifa_name))) continue; if(ife->ifa_addr == NULL) continue; if(ife->ifa_addr->sa_family == AF_INET6) { addr = (const struct sockaddr_in6 *)ife->ifa_addr; if(!IN6_IS_ADDR_LOOPBACK(&addr->sin6_addr) && !IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr)) { inet_ntop(ife->ifa_addr->sa_family, &addr->sin6_addr, buf, sizeof(buf)); /* add brackets */ snprintf(dst, n, "[%s]", buf); r = 1; } } } freeifaddrs(ifap); return r; } #endif miniupnpd-1.8.20130730/getifaddr.h010064400017500000024000000014521212532024700155470ustar00nanardstaff/* $Id: getifaddr.h,v 1.8 2013/03/23 10:46:54 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 GETIFADDR_H_INCLUDED #define GETIFADDR_H_INCLUDED struct in_addr; /* getifaddr() * take a network interface name and write the * ip v4 address as text in the buffer * returns: 0 success, -1 failure */ int getifaddr(const char * ifname, char * buf, int len, struct in_addr * addr, struct in_addr * mask); /* find a non link local IP v6 address for the interface. * if ifname is NULL, look for all interfaces */ int find_ipv6_addr(const char * ifname, char * dst, int n); #endif miniupnpd-1.8.20130730/daemonize.c010064400017500000024000000041451172522177100155750ustar00nanardstaff/* $Id: daemonize.c,v 1.13 2012/03/05 20:36:15 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #include #include #include #include #include #include #include #include #include "daemonize.h" #include "config.h" #ifndef USE_DAEMON int daemonize(void) { int pid, i; switch(fork()) { /* fork error */ case -1: perror("fork()"); exit(1); /* child process */ case 0: /* obtain a new process group */ if( (pid = setsid()) < 0) { perror("setsid()"); exit(1); } /* close all descriptors */ for (i=getdtablesize();i>=0;--i) close(i); i = open("/dev/null", O_RDWR); /* open stdin */ dup(i); /* stdout */ dup(i); /* stderr */ umask(027); chdir("/"); /* chdir to /tmp ? */ return pid; /* parent process */ default: exit(0); } } #endif int writepidfile(const char * fname, int pid) { char pidstring[16]; int pidstringlen; int pidfile; if(!fname || (strlen(fname) == 0)) return -1; if( (pidfile = open(fname, O_WRONLY|O_CREAT, 0644)) < 0) { syslog(LOG_ERR, "Unable to open pidfile for writing %s: %m", fname); return -1; } pidstringlen = snprintf(pidstring, sizeof(pidstring), "%d\n", pid); if(pidstringlen <= 0) { syslog(LOG_ERR, "Unable to write to pidfile %s: snprintf(): FAILED", fname); close(pidfile); return -1; } else { if(write(pidfile, pidstring, pidstringlen) < 0) syslog(LOG_ERR, "Unable to write to pidfile %s: %m", fname); } close(pidfile); return 0; } int checkforrunning(const char * fname) { char buffer[64]; int pidfile; pid_t pid; if(!fname || (strlen(fname) == 0)) return -1; if( (pidfile = open(fname, O_RDONLY)) < 0) return 0; memset(buffer, 0, 64); if(read(pidfile, buffer, 63)) { if( (pid = atol(buffer)) > 0) { if(!kill(pid, 0)) { close(pidfile); return -2; } } } close(pidfile); return 0; } miniupnpd-1.8.20130730/daemonize.h010064400017500000024000000015151203340734000155670ustar00nanardstaff/* $Id: daemonize.h,v 1.8 2012/09/27 16:00:44 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #ifndef DAEMONIZE_H_INCLUDED #define DAEMONIZE_H_INCLUDED #include "config.h" #ifndef USE_DAEMON /* daemonize() * "fork" to background, detach from terminal, etc... * returns: pid of the daemon, exits upon failure */ int daemonize(void); #endif /* writepidfile() * write the pid to a file */ int writepidfile(const char * fname, int pid); /* checkforrunning() * check for another instance running * returns: 0 only instance * -1 invalid filename * -2 another instance running */ int checkforrunning(const char * fname); #endif miniupnpd-1.8.20130730/upnpglobalvars.h010064400017500000024000000062321217572255200166700ustar00nanardstaff/* $Id: upnpglobalvars.h,v 1.35 2013/06/13 13:21:30 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2012 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #ifndef UPNPGLOBALVARS_H_INCLUDED #define UPNPGLOBALVARS_H_INCLUDED #include #include "upnppermissions.h" #include "miniupnpdtypes.h" #include "config.h" /* name of the network interface used to acces internet */ extern const char * ext_if_name; /* file to store all leases */ #ifdef ENABLE_LEASEFILE extern const char * lease_file; #endif /* forced ip address to use for this interface * when NULL, getifaddr() is used */ extern const char * use_ext_ip_addr; /* parameters to return to upnp client when asked */ extern unsigned long downstream_bitrate; extern unsigned long upstream_bitrate; /* statup time */ extern time_t startup_time; /* runtime boolean flags */ extern int runtime_flags; #define LOGPACKETSMASK 0x0001 #define SYSUPTIMEMASK 0x0002 #ifdef ENABLE_NATPMP #define ENABLENATPMPMASK 0x0004 #endif #define CHECKCLIENTIPMASK 0x0008 #define SECUREMODEMASK 0x0010 #define ENABLEUPNPMASK 0x0020 #ifdef PF_ENABLE_FILTER_RULES #define PFNOQUICKRULESMASK 0x0040 #endif #define SETFLAG(mask) runtime_flags |= mask #define GETFLAG(mask) (runtime_flags & mask) #define CLEARFLAG(mask) runtime_flags &= ~mask extern const char * pidfilename; extern char uuidvalue_igd[]; /* uuid of root device (IGD) */ extern char uuidvalue_wan[]; /* uuid of WAN Device */ extern char uuidvalue_wcd[]; /* uuid of WAN Connection Device */ #define SERIALNUMBER_MAX_LEN (10) extern char serialnumber[]; #define MODELNUMBER_MAX_LEN (48) extern char modelnumber[]; #define PRESENTATIONURL_MAX_LEN (64) extern char presentationurl[]; #define FRIENDLY_NAME_MAX_LEN (64) extern char friendly_name[]; /* UPnP permission rules : */ extern struct upnpperm * upnppermlist; extern unsigned int num_upnpperm; #ifdef ENABLE_NATPMP /* NAT-PMP */ #if 0 extern unsigned int nextnatpmptoclean_timestamp; extern unsigned short nextnatpmptoclean_eport; extern unsigned short nextnatpmptoclean_proto; #endif #endif /* For automatic removal of expired rules (with LeaseDuration) */ extern unsigned int nextruletoclean_timestamp; #ifdef USE_PF extern const char * anchor_name; /* queue and tag for PF rules */ extern const char * queue; extern const char * tag; #endif #ifdef USE_NETFILTER extern const char * miniupnpd_nat_chain; extern const char * miniupnpd_forward_chain; #ifdef ENABLE_6FC_SERVICE extern const char * miniupnpd_v6_filter_chain; #endif #endif #ifdef ENABLE_NFQUEUE extern int nfqueue; extern int n_nfqix; extern unsigned nfqix[]; #endif /* lan addresses to listen to SSDP traffic */ extern struct lan_addr_list lan_addrs; #ifdef ENABLE_IPV6 /* ipv6 address used for HTTP */ extern char ipv6_addr_for_http_with_brackets[64]; #endif extern const char * minissdpdsocketpath; /* BOOTID.UPNP.ORG and CONFIGID.UPNP.ORG */ extern unsigned int upnp_bootid; extern unsigned int upnp_configid; #ifdef ENABLE_6FC_SERVICE extern int ipv6fc_firewall_enabled; extern int ipv6fc_inbound_pinhole_allowed; #endif #endif miniupnpd-1.8.20130730/upnpglobalvars.c010064400017500000024000000054541217572255200166700ustar00nanardstaff/* $Id: upnpglobalvars.c,v 1.30 2013/06/13 13:21:30 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2012 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #include #include "config.h" #include "upnpglobalvars.h" /* network interface for internet */ const char * ext_if_name = 0; /* file to store leases */ #ifdef ENABLE_LEASEFILE const char* lease_file = 0; #endif /* forced ip address to use for this interface * when NULL, getifaddr() is used */ const char * use_ext_ip_addr = 0; /* LAN address */ /*const char * listen_addr = 0;*/ unsigned long downstream_bitrate = 0; unsigned long upstream_bitrate = 0; /* startup time */ time_t startup_time = 0; int runtime_flags = 0; const char * pidfilename = "/var/run/miniupnpd.pid"; char uuidvalue_igd[] = "uuid:00000000-0000-0000-0000-000000000000"; char uuidvalue_wan[] = "uuid:00000000-0000-0000-0000-000000000000"; char uuidvalue_wcd[] = "uuid:00000000-0000-0000-0000-000000000000"; char serialnumber[SERIALNUMBER_MAX_LEN] = "00000000"; char modelnumber[MODELNUMBER_MAX_LEN] = "1"; /* presentation url : * http://nnn.nnn.nnn.nnn:ppppp/ => max 30 bytes including terminating 0 */ char presentationurl[PRESENTATIONURL_MAX_LEN]; /* friendly name for root devices in XML description */ char friendly_name[FRIENDLY_NAME_MAX_LEN] = OS_NAME " router"; /* UPnP permission rules : */ struct upnpperm * upnppermlist = 0; unsigned int num_upnpperm = 0; #ifdef ENABLE_NATPMP /* NAT-PMP */ #if 0 unsigned int nextnatpmptoclean_timestamp = 0; unsigned short nextnatpmptoclean_eport = 0; unsigned short nextnatpmptoclean_proto = 0; #endif #endif /* For automatic removal of expired rules (with LeaseDuration) */ unsigned int nextruletoclean_timestamp = 0; #ifdef USE_PF const char * anchor_name = "miniupnpd"; const char * queue = 0; const char * tag = 0; #endif #ifdef USE_NETFILTER /* chain name to use, both in the nat table * and the filter table */ const char * miniupnpd_nat_chain = "MINIUPNPD"; const char * miniupnpd_forward_chain = "MINIUPNPD"; #ifdef ENABLE_6FC_SERVICE const char * miniupnpd_v6_filter_chain = "MINIUPNPD"; #endif #endif #ifdef ENABLE_NFQUEUE int nfqueue = -1; int n_nfqix = 0; unsigned nfqix[MAX_LAN_ADDR]; #endif struct lan_addr_list lan_addrs; #ifdef ENABLE_IPV6 /* ipv6 address used for HTTP */ char ipv6_addr_for_http_with_brackets[64]; #endif /* Path of the Unix socket used to communicate with MiniSSDPd */ const char * minissdpdsocketpath = "/var/run/minissdpd.sock"; /* BOOTID.UPNP.ORG and CONFIGID.UPNP.ORG */ unsigned int upnp_bootid = 1; unsigned int upnp_configid = 1337; #ifdef ENABLE_6FC_SERVICE int ipv6fc_firewall_enabled = 1; int ipv6fc_inbound_pinhole_allowed = 1; #endif miniupnpd-1.8.20130730/LICENSE010064400017500000024000000027101172522177100144570ustar00nanardstaffMiniUPnPd Copyright (c) 2006-2011, 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. miniupnpd-1.8.20130730/README010064400017500000024000000022221175244620700143330ustar00nanardstaffMiniUPnP project (c) 2006-2012 Thomas Bernard webpage: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ github: https://github.com/miniupnp/miniupnp freecode: http://freecode.com/projects/miniupnp contact: miniupnp@free.fr This directory contain the miniUPnP daemon software, aka miniUPnPd. This software is subject to the conditions detailed in the LICENSE file provided with this distribution. The miniUPnP daemon is an UPnP IGD (internet gateway device) which provide NAT traversal services to any UPnP enabled client on the network. See http://www.upnp.org/ for more details on UPnP. During the year 2011, support for IGD v2 has been added. In 2012, IGD v2 WANIPv6FirewallControl has been implemented. It is not yes enabled by default because it still needs testing. Support for the NAT Port Mapping Protocol (NAT-PMP) has been added. See information about NAT-PMP here : http://miniupnp.free.fr/nat-pmp.html Read the INSTALL files for instructions to compile, install and configure miniupnpd. Report bugs to miniupnp@free.fr on the web forum http://miniupnp.tuxfamily.org/forum/ or using https://github.com/miniupnp/miniupnp/issues Thomas Bernard miniupnpd-1.8.20130730/INSTALL010064400017500000024000000136731214624402300145060ustar00nanardstaffMiniUPnP project. (c) 2006-2013 Thomas Bernard Homepage : http://miniupnp.free.fr/ Mirror: http://miniupnp.tuxfamily.org/ github: https://github.com/miniupnp/miniupnp miniupnpd is still under active developpement. This documentation is likely to be a little outdated when you read it. So please go on the web forum http://miniupnp.tuxfamily.org/ if you need more information. ================================ *BSD/pf ================================= To Build and Install : - use BSD make to compile. - you can first 'make config.h' then edit config.h to your preferences and finally 'make' Alternatively to editing config.h, options can be passed to genconfig.sh For more details : > ./genconfig.sh -h - add "rdr-anchor miniupnpd" and "anchor miniupnpd" lines to /etc/pf.conf - some FreeBSD users reported that it is also necessary for them to explicitly allow udp traffic on 239.0.0.0/8 by adding the two following lines to /etc/pf.conf : pass out on $int_if from any to 239.0.0.0/8 keep state pass in on $int_if from any to 239.0.0.0/8 keep state - dont forget to " pfctl -f /etc/pf.conf " - you can check your modifications are taken into accout with "pfctl -s nat" and "pfctl -s rule". Look for the "rdr-anchor miniupnpd" and "anchor miniupnpd" lines. - install as root using : # make install or # PREFIX=/usr/local make install - run as root : The daemon needs rights to modify pf rules. edit the /etc/miniupnpd.conf file to set options. Almost all options are also available through command line switches. To stop the daemon use : > kill `cat /var/run/miniupnpd.pid` =========================== *BSD,*Solaris/ipf ============================= genconfig.sh and the Makefile try to detect wether ipf or pf should be used. If it fails, edit config.h and Makefile by hand. In Makefile, the FWNAME variable value should be pf or ipf. Installation steps are allmost the same as with pf. *Solaris users would be interested in reading informations from : http://blogs.sun.com/avalon/category/IPFilter ============================= Mac OS X/ipfw =============================== - To enable non standard compilation options, > ./genconfig.sh -h Or edit config.h after it has been generated by genconfig.sh - use 'bsdmake' or 'make -f Makefile.macosx' to build ============================ Linux/netfilter ============================== To Build and install : - make sure you have libiptc available on your system : if you are using debian, "apt-get install iptables-dev" Some versions of the iptables-dev package don't include the necessary files : read "how to get libiptc with its headers on debian" below. In anycase, libiptc is available in iptables sources packages from http://netfilter.org - edit and run netfilter/iptables_init.sh shell script. This script must allways be run before the daemon to set up intial rules and chains. - Build and edit the config.h file > make -f Makefile.linux config.h > vi config.h - Build the daemon > make -f Makefile.linux If not using iptables from your system, > IPTABLESPATH=/path/to/iptables-1.4.1 make -f Makefile.linux - install as root using : > make -f Makefile.linux install - A miniupnpd script should be installed to /etc/init.d and the configuration files to /etc/miniupnpd - anytime, you can use the netfilter/iptables_flush.sh script to flush all rules added by the daemon. - after killing the daemon, you can get back to iptables initial state by runing the netfilter/iptables_removeall.sh script. Don't forget to edit the script to your convinience. NOTE: a /etc/init.d/miniupnpd script will be installed. If it suits you, you can use is with start, stop or restart argument. # /etc/init.d/miniupnpd restart How to get libiptc with its headers on debian : (Note: that should be useless now that netfilter/tiny_nf_nat.h is included) - Use apt-get to get sources : > apt-get source iptables you should then have an iptables-x.x.x/ directory. - configure and compile : > cd iptables-x.x.x/ > ./configure --enable-static > make - it is now possible to compile miniupnpd using the following command : > IPTABLESPATH=/path/to/iptables-x.x.x make -f Makefile.linux =========================== Configuration ============================= Edit the /etc/miniupnpd.conf file to set options. Almost all options are also available through command line switches. Miniupnpd supports some kind of security check for allowing or disallowing redirection to be made. The UPnP permission rules are read from the miniupnpd.conf configuration file. When a new redirection is asked, permission rules are evaluated in top-down order and the first permission rule matched gives the answer : redirection allowed or denied. If no rule is matching, the redirection is allowed, so it is a good practice to have a "catch all" deny permission rule at the end of your mermission ruleset. Sample permission ruleset : allow 4662-4672 192.168.1.34/32 4662-4672 deny 0-65535 192.168.1.34/32 0-65535 allow 1024-65535 192.168.1.0/24 1024-65535 deny 0-65535 0.0.0.0/0 0-65535 With this ruleset, redirections are allowed only for host on the subnet 192.168.1.0/255.255.255.0 for the ports 1024 or above. There is an exception for the host 192.168.1.34 for which only redirections from/to port 4662 to 4672 are allowed. You can generate the uuid for your UPnP device with the uuidgen available under linux. The following following OpenBSD package is also providing a "uuid" tool : http://www.openbsd.org/4.0_packages/i386/uuid-1.5.0.tgz-long.html An web based uuid generator is also available : http://kruithof.xs4all.nl/uuid/uuidgen On linux systems, one could also use the command 'cat /proc/sys/kernel/random/uuid' to generate an uuid. More simple, use the genuuid makefile target : > make genuuid or > make -f Makefile.linux genuuid This target is needed by the "install" target, so it should be done automatically. To stop the daemon use : # kill `cat /var/run/miniupnpd.pid` or if your linux system use /etc/init.d/ # /etc/init.d/miniupnpd stop miniupnpd-1.8.20130730/Changelog.txt010064400017500000024000000666361217572255200161260ustar00nanardstaff$Id: Changelog.txt,v 1.345 2013/06/13 13:21:28 nanard Exp $ 2013/06/13: Have 3 UUID for the 3 devices (IGD, WAN Device, WAN Connection Device) 2013/06/06: update upnpreplyparse to allow larger values (128 chars instead of 64) 2013/06/05: check Service ID in SetDefaultConnectionService method Don't advertise WANPPPConnection in UPNP_STRICT mode 2013/05/29: Remove namespace from variable name elements in Events "propertyset" to comply with UPNP DeviceArchitecture v1.1. 2013/05/20: Adding support for IP Filter version 5.x 2013/05/16: refuses non integer values 2013/05/14: Update upnpreplyparse to take into account "empty" elements 2013/05/03: Use pkg-config under linux to find libiptc. Thanks to Olivier Langlois 2013/04/29: Add warning message when using IPv4 address for listening_ip with IPv6 enabled 2013/04/27: Uses ifr_addr if ifr_netmask is not defined in struct ifreq 2013/04/26: Correctly handle truncated snprintf() in SSDP code 2013/04/24: to avoid build race conditions, genconfig.sh now uses a temporary file 2013/04/20: use scope in get_lan_for_peer() for IPv6 addresses 2013/03/23: autodetect LAN interface netmask instead of defaulting to /24 2013/02/11: Use $(DESTDIR) in Makefile.linux. see https://github.com/miniupnp/miniupnp/issues/26 2013/02/07: Add DATE: header in SSDP packets Fix SSDP packets sent with uuid as ST: header to conform to UDA ignore SSDP packets missing the MX: header in UPNP_STRICT mode Added Ext: header to HTTP responses to conform to UDA Refactored SendSSDPNotifies() and SendSSDPGoodbye() and add missing ssdp:alive and ssdp:byebye with NT uuid value. VERSION 1.8 : released on 2013/02/06 2013/02/06: Check source address of incomining HTTP connections and SSDP packets in order to filter out WAN SSDP and HTTP trafic. Implement get_src_for_route_to() for *BSD fix 2 potential memory leaks in GetListOfPortMappings() 2013/01/29: upnphttp.c: Fix and comment the findendheaders() function upnphttp.c: remove strchr() call in ParseHttpHeaders() add comments to explain how buffer is checked before calls to ParseHttpHeaders() 2013/01/27: upnphttp.c: ParseHttpHeaders() now checks atoi() return 2012/12/11: More return value check for malloc() and realloc() 2012/10/23: minor modifications to linux/getroute.c and testgetroute.c 2012/10/04: updated DEFAULTCONNECTIONSERVICE_MAGICALVALUE for IGDv2 increased default buffer size for HTTP response More argument check for SOAP actions in UPNP_STRICT mode Better error checking after connect() in upnpevent 2012/10/03: Fix atoi() on null pointer in upnpsoap.c properly set service/device version in SSDP messages fix newSubscriber() for IP6FirewallControl and DeviceProtection services Enforce compliance for SUBSCRIBE messages (UPNP_STRICT mode) Enforce compliance for UNSUBSCRIBE messages (UPNP_STRICT mode) Ignore "-Wmissing-field-initializers" in upnpdescgen.c check size of h->res_buf before building HTTP response ENABLE_HTTP_DATE : add a Date: header to all HTTP responses 2012/09/27: Fixes with DISABLE_CONFIG_FILE and UPNP_STRICT UPC must be a 12 decimal digit code SetDefaultConnectionService() checks its argumnents in UPNP_STRICT mode Support for Accept-Language/Content-Language HTTP headers Content-Type is now text/xml; charset="utf-8" to conform with UDA v1.1 Support Expect: 100-continue for POST HTTP requests Manage services/devices versions in minissdp.c Rename all include guards to not clash with C99. (7.1.3 Reserved identifiers) 2012/09/20: Cleaning code in ipfw (Jardel Weyrich) 2012/09/18: Fixing a bug in clean_pinhole_list() under linux/netfilter 2012/09/15: Adding an informational message at startup 2012/08/24: Moved man page to section 8. miniupnpd.1 => miniupnpd.8 Added install of miniupnpd.8 man page in Makefile.linux 2012/08/10: improved SubmitServicesToMiniSSDPD() function fiability 2012/07/17: Add -A command line option to add permission rules 2012/07/14: Add -z command line option to change friendly name (thanks to Shawn Fisher) 2012/06/29: added DISABLE_CONFIG_FILE in options.h to disable miniupnpd.conf parsing Add command line parsing for clean_ruleset_interval option 2012/06/28: Only activate -L option for PF and IPF -a option takes two arguments with MULTIPLE_EXTERNAL_IP defined 2012/06/23: in UPNP_STRICT mode, the literal IPv6 address in "location:" of SSDP messages is the source address used to send the message 2012/06/08: Disable -ansi CFLAGS in Makefile.linux because recent iptables headers make use of typeof keyword which is a GCC extension. 2012/05/31: Improvements in autodetecting firewall under (Free)BSD 2012/05/28: Cleanup HTTP request handling. Answer 405 when relevant VERSION 1.7 : released the 2012/05/28 2012/05/28: clean linux/ifacewatcher.c set natpmp socket non blocking 2012/05/24: More solaris fixes 2012/05/21: Clean signal handling 2012/05/08: Clean expired IPv6 pinholes correctly. and also with linux/netfilter. 2012/05/07: Finalizing netfilter version of get_pinhole_info() 2012/05/01: Move IPv6FirewallControl related code from upnpredirect.c to upnppinhole.c Add netfilter implementation for delete_pinhole()/update_pinhole()/get_pinhole_info() 2012/04/30: Clean up settings of CFLAGS in Makefile's Remove Warnings caused by signed/unsigned integer comparaisons Also fix a couple of integer/pointer comparaisons. Add UNUSED(arg) macro to remove unused argument warning. Fix error handling in upnpevents.c (was causing segfault on Solaris !) 2012/04/26: Started to implement add_pinhole() for netfilter (linux) 2012/04/25: Fixed a bug in upnphttp that happened when POST is received in several recv() calls and realloc() is called so the buffer used is moved. 2012/04/23: Implement CheckPinholeWorking GetPinholePackets. WANIPv6FirewallControl UpdatePinhole still to be done. And also netfilter/ipf/ipfw versions 2012/04/20: Enough WANIPv6FirewallControl is implemented on pf so that AddPinhole() and DeletePinhole() works ! 2012/04/19: First working experiment of IPv6 "pinhole" with pf 2012/04/15: More C++ => ANSI C comments to compile with -ansi option Add command line arguments to genconfig.sh config script. 2012/04/12: Set TTL on SSDP Notify sockets (IPv4). TTL is set to 2 (recommendation from UPnP Device Architecture v1.1) 2012/04/06: Implementing IPv6 support : Send SSDP NOTIFY ssdp:alive and ssdp:goodbye messages in IPv6. Use UPnP/1.1 in SERVER: string as required in UPnP Device architecture 1.1. Allow LAN interface to be given as interface names, instead of interface IP addresses. It will allow IPv6 operations. fix linux/getifstats.c when bitrate is unknown 2012/03/31: Only remove pidfile if one was written in the first place. 2012/03/19: Fix ipfilter support (thanks dhowland https://github.com/dhowland) 2012/03/14: Changes to miniupnpd.init.d.script by Shawn Landden 2012/03/05: fixed reload_from_lease_file(). 2012/02/15: Change parselanaddr() function to allow 192.168.1.1/255.255.255.0 in configuration file. Change read_permission_line() to allow 192.168.1.1/255.255.255.0 in permission line (in configuration file). 2012/02/12: More syntax checks in upnppermissions.c 2012/02/11: Fix ipfw/Mac OS X specific source files to compile ok with -ansi flag 2012/02/09: Make HTTP listen socket non blocking (so accept() can't block) Make SSDP receive sockets non blocking use sockaddr_to_string() in SendSSDPAnnonce2 to handle IPv6 addresses 2012/02/06: Make HTTP (SOAP) sockets non blocking. 2012/02/05: Compile ok with -ansi flag. Save a few bytes in options.c using a string repository, instead of a fixed size buffer for each option value. 2012/02/04: Added friendly_name= option to config file 2012/02/03: Anchor name (PF) is now configurable through the config file with anchor= Added test of presence of /lib/libip4tc.so and /lib/libip6tc.so files in Makefile.linux in order to add -lip4tc and -lip6tc to LIBS accordingly. 2012/02/01: always handle EAGAIN, EWOULDBLOCK and EINTR after recv()/recvfrom() calls 2012/01/20: Always #include before #include (for OpenBSD) .onrdomain field was added in pf with OpenBSD 5.0. Add PFRULE_HAS_ONRDOMAIN 2012/01/02: Fixing netfilter/iptables_*.sh scripts for new ifconfig output format. getifaddr.c: added additional checks on structure returned by getifaddrs() Fixing Mac OS X makefile for installation 2011/11/18: avoid infinite loop in SendResp_upnphttp() in case of error Replaced SendResp_upnphttp() + CloseSocket_upnphttp() by SendRespAndClose_upnphttp() Tomato specifics in genconfig.sh 2011/07/30: netfilter : Added a tiny_nf_nat.h file to compile with iptables installed headers. include xtables.h instead of iptables.h VERSION 1.6 : released the 2011/07/25 2011/07/25: Update doc for version 1.6 2011/07/15: Fixing code with MULTIPLE_EXTERNAL_IP defined. 2011/06/27: IPv6 support for UPnP events. Security checks in UPnP events. 2011/06/22: Remote host for GetListOfPortMappings Remote host support for ipfw (tested on Mac OS X) 2011/06/20: support for iptables-1.4.11.1 2011/06/18: Remote host support for pf version 2011/06/04: Supporting RemoteHost (mandatory in IGD v2) 2011/06/03: Enabling events by default 2011/06/01: Fixing Timeout missing in SUBSCRIBE renewal responses (thanks to Pranesh Kulkarni) Added comments about changes between IGD v1 and IGD v2 2011/05/28: Description and leaseduration kept in ipfw version of the code. Fixing ipfw code after testing under Mac OS X 10.6.7 (darwin 10.7.0) 2011/05/27: Finishing and testing LeaseDuration support under OpenBSD. Changing NAT-PMP port mapping lifetime support to match lease duration support. NAT-PMP address change announce broadcasted to both port 5350 and 5351 to be compatible with client following the version of NAT PMP specification from 2008 or earlier. writepidfile() Overwrite file if already existing 2011/05/26: fix in linux/getifstats.c. See http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=2212 Implementation of LeaseDuration support. 2011/05/23: added get_wan_connection_status_str() 2011/05/20: adding ifacewatcher thanks to Alexey Osipov GET /DP.xml is now available. The description has to be completed. 2011/05/19: Add getconnstatus.c/.h. Dont always have ConnectionStatus to "Connected" Events for WANIPv6FirewallControll 2011/05/16: patches for gentoo linux. generation of the DeviceProtection service description. 2011/05/15: Making the SSDP receiving socket work in IPv6 ! 2011/05/14: Support for HTTP in both IPv6 and IPv4. IPv6 for SSDP receiving socket. 2011/05/13: add new options in genconfig.sh (IGD_V2, ENABLE_DP_SERVICE) add global vars ipv6fc_firewall_enabled and ipv6fc_inbound_pinhole_allowed have MACROS for magical values in upnpdescgen.c, add eventing vars for WanIPv6FirewallControl. applied 0001-Cosmetic-changes.patch(see http://miniupnp.tuxfamily.org/forum/viewtopic.php?t=764) applied 0002-Remove-lan-addresses-limit-by-changing-storage-type-.patch replaced some of the urn:schemas-upnp-org:device:* literal strings by macros. adding some support for IP v6. #define ENABLE_IPV6 added -fno-strict-aliasing to compile options. 2011/05/09: updating upnp descriptions for IGDv2 2011/05/07: Adding WANIPv6FirewallContro to upnp description 2011/04/30: adding a UPNP_STRICT config macro. Use it now for checking RemoteHost. ENABLE_6FC_SERVICE : add the implementations of WANIPv6FirewallControl actions 2011/04/11: preparing getifaddr() for IP v6 preparing SSDP stuff for IP v6. Trying to conform to UDA v1.1 2011/03/09: Some modifications thanks to Daniel Dickinson to improve OpenWRT build. Fixed some warnings. 2011/03/03: Added code to generate devices/services descriptions for IGD v2 (to be continued) 2011/03/02: improved netfilter/delete_redirect_and_filter_rules() in order to remove the right filter rule, even if it has another index than the nat rule. 2011/03/01: clean up an fixes to make netfilter/testiptcrdr compile 2011/02/21: Make "Makefile" work under Mac OS X with bsdmake. added get_portmappings_in_range() in ipfwrdr.c 2011/02/07: added get_portmappings_in_range() / upnp_get_portmappings_in_range() 2011/02/06: Implementation of GetListOfPortMappings 2011/01/27: Reverting "fixes" done in linux/iptables code the 2010/09/27. see http://miniupnp.tuxfamily.org/forum/viewtopic.php?t=741 2011/01/04: added MINIUPNPD_VERSION in config.h. Taken from VERSION file. VERSION 1.5 : released the 2011/01/01 2011/01/01: Started to implement some of the new methods from WANIPConnection v2 2010/09/27: Some fixes in the linux/iptables code when miniupnpd_nat_chain <> miniupnpd_forward_chain 2010/09/21: Patch to support nfqueue thanks to Colin McFarlane 2010/08/07: Update Mac OS X / ipfw stuff from Jardel Weyrich Fix in Makefile.linux for x86_64 2010/05/06: Bugfix un CleanNATPMPRules() : see http://miniupnp.tuxfamily.org/forum/viewtopic.php?t=640 2010/03/14: Fixing natpmp sockets. 2010/03/08: Fix Makefile.linux to compile properly under Mandriva/rh/Fedora with Iptables >= 1.4.3 Workaround for bad uptime when started with a bad time set. 2010/03/07: Tried to make a OpenBSD version 4.7 compatible code... still some issues. 2010/03/06: updates to testobsdrdr 2010/03/03: -lip4tc in Makefile.linux. 2010/02/15: some more error handling in set_startup_time() silencing some warnings 2010/01/14: Open Several sockets for NAT-PMP to make sure the source address of NAT-PMP replies is right. see http://miniupnp.tuxfamily.org/forum/viewtopic.php?t=609 2009/12/31: miniupnpdctl now output command line arguments. added a -h option to get help. improved help. 2009/12/22: using PRIu64 format to printf u_int64_t Fixing calls to get_redirect_rule_by_index() : ifname should be initialized. Add header lines to miniupnpdctl output 2009/11/06: implementing sending of ip address change notification when receiving the signal SIGUSR1 VERSION 1.4 : released the 2009/10/30 2009/10/10: Integrate IPfilter patch from Roy Marples. Fix Netfilter code for old netfilter : see http://miniupnp.tuxfamily.org/forum/viewtopic.php?t=584 trim the description string in reload_from_lease_file() 2009/09/21: Fixing unclosed raw sockets bug with netfilter code. 2009/09/04: Fixes in ipf code thanks to Roy Marples Enable DragonFly BSD Support thanks to Roy Marples. Allow packager to define default location of config file via CFLAGS Respect $DESTDIR when installing 2009/08/20: Adding some support for MacOS X and IPFW SO_REUSEADDR in minissdp.c for SSDP listening socket 2009/06/05: unlink lease file in reload_from_lease_file() 2009/05/16: Fixed a buffer overflow in ProcessSSDPRequest() 2009/05/11: improving genconfig.sh for NetBSD : detecting use of pf or ipf VERSION 1.3 : 2009/04/17: working support for iptables >= 1.4.3 2009/04/13: work to support iptables-1.4.3 and up 2009/04/10: fix in upnpevents_removeSubscriber() 2009/02/14: added reload_from_lease_file() 2009/02/13: Changes in upnpdescgen.c to allow to remove empty elements strcasecmp instead of strcmp on path comparaisons to allow bugged clients to work 2009/01/29: Some minor changes to Makefile improving Makefile.linux in order to build with iptables not properly installed on the system. 2009/01/23: Fixing upnpevents thanks to Justin Maggard 2008/10/15: getifstats() return -1 when supplied with bad arguments 2008/10/11: Fixed NAT-PMP response when IP not allocated to external interface 2008/10/09: adding testgetifaddr Reporting Unconnected status when the "external interface" has no IP address assigned. Also added some comments VERSION 1.2 : 2008/10/07: updating docs 2008/10/06: MiniUPnPd is now able to use MiniSSDPd to manage SSDP M-SEARCH answering 2008/10/03: You can now let miniupnpd choose itself the HTTP port used. 2008/10/01: Improvements in genconfig.sh for detecting ipf or pf (under FreeBSD) and improve debian/ubuntu stuff. custom chain name patch from : http://miniupnp.tuxfamily.org/forum/viewtopic.php?t=493 2008/08/24: added USE_IFNAME_IN_RULES macro that can be disabled in order to remove interface name from rules. 2008/07/10: Fixed compilation without ENABLE_L3F_SERVICE 2008/04/27: correct UNSUBSCRIBE processing 2008/04/25(bis): changed iptables_removeall.sh and iptables_init.sh in order to remove IP from the rules VERSION 1.1 : 2008/04/25: Eventing is allmost completly implemented 2008/04/24: Correct event handling ? 2008/04/08: enabling tag in PF rules. quick can be set off. 2008/03/13: implementing event notify 2008/03/11: fixing a command line parsing error 2008/03/09: optimisations in upnpsoap.c 2008/03/08: optimizing upnpsoap.c for size 2008/03/06: Worked on the Eventing : generating XML event notifications Send initial notification after subscribe Improved pretty print of testupnpdescgen Reduced Memory usage of upnpdescgen fixed a small bug in the description 2008/03/03: Fixed miniupnpd.c for compiling without natpmp support fixed presentationURL not there with L3F fixing lease file creation/modification 2008/02/25: Rewrite of Send501() and Send404() More work on events genconfig.sh autodetects pf/ipf 2008/02/24: Started to implement UPnP Events. do NOT use it at the moment ! 2008/02/21: Added support for the Layer3Forwarding Service added init_redirect() and shutdown_redirect() functions 2008/02/20: Removed Ext: HTTP header when useless enabled the dummy service by default to please windows XP ! 2008/02/07: upnp_enable patch by Nikos Mavrogiannopoulos. lease_file patch by Nikos Mavrogiannopoulos. 2008/01/29: some changes to Makefile.openwrt use daemon() - daemonize() is still available for systems lacking daemon() VERSION 1.0 : 2008/01/27: moved lan_addr to upnpglobalvars.h/.c Adding experimental multiple external IP support. 2008/01/22: removed dummy service from description to improve compatibility with emule client Add "secure mode". put runtime flags in the same variable 2008/01/14: Fixed a bug in options.c for the parsing of empty lines. 2008/01/03: Fixed CleanExpiredNATPMP() 2008/01/02: Adding a queue parameter for setting ALTQ in pf 2007/12/27: improving some stuff with the PF_ENABLE_FILTER_RULE. 2007/12/22: Adding a runtime option to enable/disable NAT-PMP 2007/12/20: Added a cache in linux getifstats(). Please enable by editing config.h 2007/12/14: Updating an existing NAT-PMP mapping now works 2007/12/13: NAT-PMP code now remove expired mappings TCP/UDP where swapped in NAT-PMP code 2007/12/04: Adding details to the error message for sendto(udp_notify) 2007/11/27: pf code doesn't generate filter rules by default anymore. The #ifdef PF_ENABLE_FILTER_RULES must be uncommented in config.h. 2007/11/02: moved some of the prototypes common to all firewalls to commonrdr.h Added functionalities to NAT-PMP 2007/11/01: Debugged NAT-PMP code 2007/10/28: Cleaning and improving NAT-PMP code 2007/10/25: improved the NAT-PMP experimental support updated README and INSTALL files 2007/10/24: Adding support for NAT-PMP (from apple !) 2007/10/11: Checking the commandline for errors. 2007/10/08: Improved the BSD/Solaris Makefile Merging last code from Darren Reed. Solaris/IPF should work now ! added a man page. 2007/10/07: Adding Darren Reed code for ipf. 2007/10/06: Adding SunOS support thanks to Darren Reed. Reorganizing os/firewall dependent code thanks to Darren Reed. 2007/09/27: linux make install support PREFIX variable 2007/09/25: reorganizing LAN sockets/address to improve multi LAN support. SSDP announces are sent to all configured networks. SSDP responses are "customized" by subnetwork. 2007/09/24: prototype code to remove unused rules miniupnpdctl now display current rules synchronised add_filter_rule2() prototype between pf and netfilter code. 2007/09/19: Correctly filling the Cache-control header in SSDP packets 2007/08/28: update PFRULE_INOUT_COUNTS detection for FreeBSD 2007/08/27: update version in genconfig.sh do not error when a duplicate redirection is requested. 2007/07/16: really fixed the compilation bug with linux>=2.6.22 2007/07/04: fixed an error in options.c that prevented to use packet_log option 2007/07/03: improved genconfig.sh fixed a compilation bug with linux>=2.6.22 2007/06/22: added PFRULE_INOUT_COUNTS macro to enable separate in/out packet and bytes counts in pf for OpenBSD >= 3.8 2007/06/15: removed a possible racecondition in writepidfile() 2007/06/12: improved genconfig.sh : no more "echo -e", use lsb_release when available 2007/06/11: get_redirect_rule*() functions now return some statistics about rule usage (bytes and packets) 2007/06/07: Fixed the get_redirect_desc() in the linux/netfilter code 2007/06/05: Clean up init code in miniupnpd.c Added a syslog message in SoapError() 2007/06/04: Now store redirection descriptions in the linux/netfilter code 2007/05/21: Answers to SSDP M-SEARCH requests with ST: ssdp:all added make install to Makefile.linux 2007/05/10: Fixed a bug int the DeletePortMapping linux/netfilter implementation It was allways the 1st rule that was deleted. 2007/04/26: Fixed config.h.openwrt 2007/04/16: added something in the INSTALL file about the FreeBSD send(udp_notify) problem fix (allowing 239.0.0.0/8 explicitely in pf.conf) 2007/03/30: added setsockopt(s, SOL_SOCKET, SO_BROADCAST ...) for broadcasting socket 2007/03/17: Fixed filter rule under linux : it was using wrong port ! thanks to Wesley W. Terpstra 2007/03/01: Moved some of the SSDP code from miniupnpd.c to minissdp.c 2007/02/28: creating miniupnpdctl 2007/02/26: use LOG_MINIUPNPD macro for openlog() simplify miniupndShutdown() 2007/02/09: improved genconfig.h Added stuff to change the pf rule "rdr" to "rdr pass" 2007/02/07: Corrected Bytes per seconds to bits per second. Ryan cleaned up comments and typos. Ryan cleaned up daemonize stuff. Ryan added possibility to configure model number and serial number 2007/01/30: ryan improved the robustness of most UPnP Soap methods I added a target in the Makefiles to properly generate an uuid using command line tools. Improved configuration file parsing. 2007/01/29: Adding uuid option in miniupnpd.conf 2007/01/27: Added upnppermissions stuff : adding some security to UPnP ! fixed XML description thanks to Ryan Wagoner improved QueryStateVariable thanks to Ryan Wagoner 2007/01/22: use getifaddr() for each GetExtenalIPAddress() Call. We can change the ip during execution without pb 2007/01/17: Lots of code cleanup 2007/01/12: Fixed a nasty bug in the linux/netfilter version of get_filter_rule() 2007/01/11: Improved the handling of the miniupnpd.conf file. added -f option to choose which config file to read. 2007/01/10: Fixed potential bugs with ClearNameValueList() 2007/01/08: All by Ryan Wagoner : - coding style and comments cleanup - using now option file miniupnpd.conf 2007/01/03: changed "xx active incoming HTTP connections" msg 2007/01/02: Patch from Ryan Wagoner : - no need to open sockets if we can't set the error handlers - format the usage so it fits nicely on a standard size terminal - fix up log_err message so they have the same format and you know what they are related to - use same "white space" style throughout - on shutdown no need to continue if opening socket or setsockopt fails 2006/12/14: reduce amount of log lines (keeping the same information) 2006/12/07: Fixed Makefiles fixed typos in logs version 1.0-RC1 released 2006/12/02: moved strings from upnpdescgen.c to upnpdescstrings.h for easier modification Server: HTTP header now comes from a #define added a compilation-time generated config.h 2006/11/30: minixml updated. should have no impact Added support for presentationURL with -w switch implemented getifstats() for linux. Added testgetifstats program improved error handling in getifstats() BSD 2006/11/26: no need to have miniupnpc sources to compile miniupnpd. Makefile.openwrt updated Closing sockets on exit thanks to Ryan Wagoner 2006/11/23: now handling signal SIGINT setting HTTP socket with REUSEADDR thanks to Ryan Wagoner daemon now tested on a Linksys WRT54G device running OpenWRT ! 2006/11/21: disabling rtableid in pf code. 2006/11/22: Also responds on M-SEARCH with the uuid 2006/11/20: gaining some space in upnpsoap.c 2006/11/19: Cleaning up code to comply with ANSI C89 2006/11/17: Linux version now deleting both nat and accept rules implemented -U option under Linux 2006/11/16: implemented delete_redirect_rule() for linux returning error 714 in DeletePortMapping() when needed 2006/11/12: The linux/netfilter version should now WORK ! fix in the writepidfile() function. open with a mode ! 2006/11/10: fixing the XML description generation for big endian machines working on the linux/netfilter port 2006/11/09: improved a lot the handling of HTTP error cases 2006/11/08: Tried to make the Makefile compatible with both BSDmake and GNUmake. It was hard because of $^ and $< 2006/11/07: Makefile compatible with BSD make make install target. getifstats.c compatible with both OpenBSD and FreeBSD. 2006/11/06: added getifstats.c for openBSD. May not work under FreeBSD ? now reports bytes/packets sent/received reporting bitrates possibility to report system uptime 2006/10/29: added a -L option to enable loggin (is off by default now). 2006/10/28: Patch by Ryan Wagoner to correct the XML description (was NewUpTime instead of NewUptime) and implement uptime. Trying to fix the memory leak. Added some comments added a -d option for debugging purpose Tnaks to valgrind (under linux!) I removed a small memory access error. 2006/10/27: Thanks to a patch sent by Michael van Tellingen, miniupnpd is now ignoring NOTIFY packets sent by other devices and is writing is own pid to /var/run/miniupnpd.pid 2006/10/23: Allways set sendEvents="no" in XML description (was causing pb with winXP as SUBSCRIBE is not implemented) 2006/10/22: added translation from hostname to IP in the AddPortMapping() method Thanks to Ryan Wagoner. 2006/10/18: Added an INSTALL file 2006/10/13: Added the possibility to change the notify interval 2006/09/29: Improved compliance of the XML Descriptions pretty print for testupnpdescgen 2006/09/25: improved the Error 404 response. Better serviceType and serviceId for dummy service... 2006/09/24: updating the XML description generator 2006/09/18: Thanks to Rick Richard, support for SSDP "alive" and "byebye" notifications was added. The -u options was also added. The SSDP response are now improved. The -o option is now working (to force a specific external IP address). The Soap Methods errors are correctly responded (401 Invalid Action) 2006/09/09: Added code to handle filter rules. Thanks to Seth Mos (pfsense.com) storing the descriptions in the label of the rule 2006/09/02: improved the generation of the XML descriptions. I still need to add allowed values to variables. 2006/07/29: filtering SSDP requests and responding with same ST: field 2006/07/25: Added a dummy description for the WANDevice 2006/07/20: Command line arguments processing Added possibility to listen internally on several interfaces miniupnpd-1.8.20130730/linux/getifstats.c010064400017500000024000000064251214100740700171300ustar00nanardstaff/* $Id: getifstats.c,v 1.12 2013/04/29 10:18:20 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 #include #include #include #include #include "../config.h" #include "../getifstats.h" #ifdef GET_WIRELESS_STATS #include #include #include #include #endif /* GET_WIRELESS_STATS */ /* that is the answer */ #define BAUDRATE_DEFAULT 4200000 int getifstats(const char * ifname, struct ifdata * data) { FILE *f; char line[512]; char * p; int i; int r = -1; char fname[64]; #ifdef ENABLE_GETIFSTATS_CACHING static time_t cache_timestamp = 0; static struct ifdata cache_data; time_t current_time; #endif /* ENABLE_GETIFSTATS_CACHING */ if(!data) return -1; data->baudrate = BAUDRATE_DEFAULT; data->opackets = 0; data->ipackets = 0; data->obytes = 0; 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) { /* return cached data */ memcpy(data, &cache_data, sizeof(struct ifdata)); return 0; } } #endif /* ENABLE_GETIFSTATS_CACHING */ f = fopen("/proc/net/dev", "r"); if(!f) { syslog(LOG_ERR, "getifstats() : cannot open /proc/net/dev : %m"); return -1; } /* discard the two header lines */ if(!fgets(line, sizeof(line), f) || !fgets(line, sizeof(line), f)) { syslog(LOG_ERR, "getifstats() : error reading /proc/net/dev : %m"); } while(fgets(line, sizeof(line), f)) { p = line; while(*p==' ') p++; i = 0; while(ifname[i] == *p) { p++; i++; } /* TODO : how to handle aliases ? */ if(ifname[i] || *p != ':') continue; p++; while(*p==' ') p++; data->ibytes = strtoul(p, &p, 0); while(*p==' ') p++; data->ipackets = strtoul(p, &p, 0); /* skip 6 columns */ for(i=6; i>0 && *p!='\0'; i--) { while(*p==' ') p++; while(*p!=' ' && *p) p++; } while(*p==' ') p++; data->obytes = strtoul(p, &p, 0); while(*p==' ') p++; data->opackets = strtoul(p, &p, 0); r = 0; break; } fclose(f); /* get interface speed */ snprintf(fname, sizeof(fname), "/sys/class/net/%s/speed", ifname); f = fopen(fname, "r"); if(f) { if(fgets(line, sizeof(line), f)) { i = atoi(line); /* 65535 means unknown */ if(i > 0 && i < 65535) data->baudrate = 1000000*i; } fclose(f); } else { syslog(LOG_WARNING, "cannot read %s file : %m", fname); } #ifdef GET_WIRELESS_STATS if(data->baudrate == BAUDRATE_DEFAULT) { struct iwreq iwr; int s; s = socket(AF_INET, SOCK_DGRAM, 0); if(s >= 0) { strncpy(iwr.ifr_name, ifname, IFNAMSIZ); if(ioctl(s, SIOCGIWRATE, &iwr) >= 0) { data->baudrate = iwr.u.bitrate.value; } close(s); } } #endif /* GET_WIRELESS_STATS */ #ifdef ENABLE_GETIFSTATS_CACHING if(r==0 && current_time!=((time_t)-1)) { /* cache the new data */ cache_timestamp = current_time; memcpy(&cache_data, data, sizeof(struct ifdata)); } #endif /* ENABLE_GETIFSTATS_CACHING */ return r; } miniupnpd-1.8.20130730/netfilter/iptcrdr.c010064400017500000024000000641651203340734100172650ustar00nanardstaff/* $Id: iptcrdr.c,v 1.50 2012/10/03 14:49:08 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2011 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #include #include #include #include #include #include #include #include #include #include #include #if IPTABLES_143 /* IPTABLES API version >= 1.4.3 */ /* added in order to compile on gentoo : * http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=2183 */ #define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); })) #define __must_be_array(a) \ BUILD_BUG_ON_ZERO(__builtin_types_compatible_p(typeof(a), typeof(&a[0]))) #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr)) #define LIST_POISON2 ((void *) 0x00200200 ) #if 0 #include #else #include "tiny_nf_nat.h" #endif #define ip_nat_multi_range nf_nat_multi_range #define ip_nat_range nf_nat_range #define IPTC_HANDLE struct iptc_handle * #else /* IPTABLES API version < 1.4.3 */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) #include #else #if 0 #include #else #include "tiny_nf_nat.h" #endif #endif #define IPTC_HANDLE iptc_handle_t #endif /* IPT_ALIGN was renamed XT_ALIGN in iptables-1.4.11 */ #ifndef IPT_ALIGN #define IPT_ALIGN XT_ALIGN #endif #include "../macros.h" #include "../config.h" #include "iptcrdr.h" #include "../upnpglobalvars.h" /* local functions declarations */ static int addnatrule(int proto, unsigned short eport, const char * iaddr, unsigned short iport, const char * rhost); static int add_filter_rule(int proto, const char * rhost, const char * iaddr, unsigned short iport); /* dummy init and shutdown functions */ int init_redirect(void) { return 0; } void shutdown_redirect(void) { return; } /* convert an ip address to string */ static int snprintip(char * dst, size_t size, uint32_t ip) { return snprintf(dst, size, "%u.%u.%u.%u", ip >> 24, (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff); } /* netfilter cannot store redirection descriptions, so we use our * own structure to store them */ struct rdr_desc { struct rdr_desc * next; unsigned int timestamp; unsigned short eport; short proto; char str[]; }; /* pointer to the chained list where descriptions are stored */ static struct rdr_desc * rdr_desc_list = 0; /* add a description to the list of redirection descriptions */ static void add_redirect_desc(unsigned short eport, int proto, const char * desc, unsigned int timestamp) { struct rdr_desc * p; size_t l; /* set a default description if none given */ if(!desc) desc = "miniupnpd"; l = strlen(desc) + 1; p = malloc(sizeof(struct rdr_desc) + l); if(p) { p->next = rdr_desc_list; p->timestamp = timestamp; p->eport = eport; p->proto = (short)proto; memcpy(p->str, desc, l); rdr_desc_list = p; } } /* delete a description from the list */ static void del_redirect_desc(unsigned short eport, int proto) { struct rdr_desc * p, * last; p = rdr_desc_list; last = 0; while(p) { if(p->eport == eport && p->proto == proto) { if(!last) rdr_desc_list = p->next; else last->next = p->next; free(p); return; } last = p; p = p->next; } } /* go through the list to find the description */ static void get_redirect_desc(unsigned short eport, int proto, char * desc, int desclen, unsigned int * timestamp) { struct rdr_desc * p; for(p = rdr_desc_list; p; p = p->next) { if(p->eport == eport && p->proto == (short)proto) { if(desc) strncpy(desc, p->str, desclen); if(timestamp) *timestamp = p->timestamp; return; } } /* if no description was found, return miniupnpd as default */ if(desc) strncpy(desc, "miniupnpd", desclen); if(timestamp) *timestamp = 0; } #if USE_INDEX_FROM_DESC_LIST static int get_redirect_desc_by_index(int index, unsigned short * eport, int * proto, char * desc, int desclen, unsigned int * timestamp) { int i = 0; struct rdr_desc * p; if(!desc || (desclen == 0)) return -1; for(p = rdr_desc_list; p; p = p->next, i++) { if(i == index) { *eport = p->eport; *proto = (int)p->proto; strncpy(desc, p->str, desclen); if(timestamp) *timestamp = p->timestamp; return 0; } } return -1; } #endif /* add_redirect_rule2() */ int add_redirect_rule2(const char * ifname, const char * rhost, unsigned short eport, const char * iaddr, unsigned short iport, int proto, const char * desc, unsigned int timestamp) { int r; UNUSED(ifname); r = addnatrule(proto, eport, iaddr, iport, rhost); if(r >= 0) add_redirect_desc(eport, proto, desc, timestamp); return r; } int add_filter_rule2(const char * ifname, const char * rhost, const char * iaddr, unsigned short eport, unsigned short iport, int proto, const char * desc) { UNUSED(ifname); UNUSED(eport); UNUSED(desc); return add_filter_rule(proto, rhost, iaddr, iport); } /* get_redirect_rule() * returns -1 if the rule is not found */ int get_redirect_rule(const char * ifname, unsigned short eport, int proto, char * iaddr, int iaddrlen, unsigned short * iport, char * desc, int desclen, char * rhost, int rhostlen, unsigned int * timestamp, u_int64_t * packets, u_int64_t * bytes) { int r = -1; IPTC_HANDLE h; const struct ipt_entry * e; const struct ipt_entry_target * target; const struct ip_nat_multi_range * mr; const struct ipt_entry_match *match; UNUSED(ifname); h = iptc_init("nat"); if(!h) { syslog(LOG_ERR, "get_redirect_rule() : " "iptc_init() failed : %s", iptc_strerror(errno)); return -1; } if(!iptc_is_chain(miniupnpd_nat_chain, h)) { syslog(LOG_ERR, "chain %s not found", miniupnpd_nat_chain); } else { #ifdef IPTABLES_143 for(e = iptc_first_rule(miniupnpd_nat_chain, h); e; e = iptc_next_rule(e, h)) #else for(e = iptc_first_rule(miniupnpd_nat_chain, &h); e; e = iptc_next_rule(e, &h)) #endif { if(proto==e->ip.proto) { match = (const struct ipt_entry_match *)&e->elems; if(0 == strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN)) { const struct ipt_tcp * info; info = (const struct ipt_tcp *)match->data; if(eport != info->dpts[0]) continue; } else { const struct ipt_udp * info; info = (const struct ipt_udp *)match->data; if(eport != info->dpts[0]) continue; } target = (void *)e + e->target_offset; /* target = ipt_get_target(e); */ mr = (const struct ip_nat_multi_range *)&target->data[0]; snprintip(iaddr, iaddrlen, ntohl(mr->range[0].min_ip)); *iport = ntohs(mr->range[0].min.all); get_redirect_desc(eport, proto, desc, desclen, timestamp); if(packets) *packets = e->counters.pcnt; if(bytes) *bytes = e->counters.bcnt; /* rhost */ if(e->ip.src.s_addr && rhost) { snprintip(rhost, rhostlen, ntohl(e->ip.src.s_addr)); } r = 0; break; } } } if(h) #ifdef IPTABLES_143 iptc_free(h); #else iptc_free(&h); #endif return r; } /* get_redirect_rule_by_index() * return -1 when the rule was not found */ int get_redirect_rule_by_index(int index, char * ifname, unsigned short * eport, char * iaddr, int iaddrlen, unsigned short * iport, int * proto, char * desc, int desclen, char * rhost, int rhostlen, unsigned int * timestamp, u_int64_t * packets, u_int64_t * bytes) { int r = -1; #if USE_INDEX_FROM_DESC_LIST r = get_redirect_desc_by_index(index, eport, proto, desc, desclen, timestamp); if (r==0) { r = get_redirect_rule(ifname, *eport, *proto, iaddr, iaddrlen, iport, 0, 0, packets, bytes); } #else int i = 0; IPTC_HANDLE h; const struct ipt_entry * e; const struct ipt_entry_target * target; const struct ip_nat_multi_range * mr; const struct ipt_entry_match *match; UNUSED(ifname); h = iptc_init("nat"); if(!h) { syslog(LOG_ERR, "get_redirect_rule_by_index() : " "iptc_init() failed : %s", iptc_strerror(errno)); return -1; } if(!iptc_is_chain(miniupnpd_nat_chain, h)) { syslog(LOG_ERR, "chain %s not found", miniupnpd_nat_chain); } else { #ifdef IPTABLES_143 for(e = iptc_first_rule(miniupnpd_nat_chain, h); e; e = iptc_next_rule(e, h)) #else for(e = iptc_first_rule(miniupnpd_nat_chain, &h); e; e = iptc_next_rule(e, &h)) #endif { if(i==index) { *proto = e->ip.proto; match = (const struct ipt_entry_match *)&e->elems; if(0 == strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN)) { const struct ipt_tcp * info; info = (const struct ipt_tcp *)match->data; *eport = info->dpts[0]; } else { const struct ipt_udp * info; info = (const struct ipt_udp *)match->data; *eport = info->dpts[0]; } target = (void *)e + e->target_offset; mr = (const struct ip_nat_multi_range *)&target->data[0]; snprintip(iaddr, iaddrlen, ntohl(mr->range[0].min_ip)); *iport = ntohs(mr->range[0].min.all); get_redirect_desc(*eport, *proto, desc, desclen, timestamp); if(packets) *packets = e->counters.pcnt; if(bytes) *bytes = e->counters.bcnt; /* rhost */ if(rhost && rhostlen > 0) { if(e->ip.src.s_addr) { snprintip(rhost, rhostlen, ntohl(e->ip.src.s_addr)); } else { rhost[0] = '\0'; } } r = 0; break; } i++; } } if(h) #ifdef IPTABLES_143 iptc_free(h); #else iptc_free(&h); #endif #endif return r; } /* delete_rule_and_commit() : * subfunction used in delete_redirect_and_filter_rules() */ static int delete_rule_and_commit(unsigned int index, IPTC_HANDLE h, const char * miniupnpd_chain, const char * logcaller) { int r = 0; #ifdef IPTABLES_143 if(!iptc_delete_num_entry(miniupnpd_chain, index, h)) #else if(!iptc_delete_num_entry(miniupnpd_chain, index, &h)) #endif { syslog(LOG_ERR, "%s() : iptc_delete_num_entry(): %s\n", logcaller, iptc_strerror(errno)); r = -1; } #ifdef IPTABLES_143 else if(!iptc_commit(h)) #else else if(!iptc_commit(&h)) #endif { syslog(LOG_ERR, "%s() : iptc_commit(): %s\n", logcaller, iptc_strerror(errno)); r = -1; } if(h) #ifdef IPTABLES_143 iptc_free(h); #else iptc_free(&h); #endif return r; } /* delete_redirect_and_filter_rules() */ int delete_redirect_and_filter_rules(unsigned short eport, int proto) { int r = -1; unsigned index = 0; unsigned i = 0; IPTC_HANDLE h; const struct ipt_entry * e; const struct ipt_entry_target * target; const struct ip_nat_multi_range * mr; const struct ipt_entry_match *match; unsigned short iport = 0; uint32_t iaddr = 0; h = iptc_init("nat"); if(!h) { syslog(LOG_ERR, "delete_redirect_and_filter_rules() : " "iptc_init() failed : %s", iptc_strerror(errno)); return -1; } /* First step : find the right nat rule */ if(!iptc_is_chain(miniupnpd_nat_chain, h)) { syslog(LOG_ERR, "chain %s not found", miniupnpd_nat_chain); } else { #ifdef IPTABLES_143 for(e = iptc_first_rule(miniupnpd_nat_chain, h); e; e = iptc_next_rule(e, h), i++) #else for(e = iptc_first_rule(miniupnpd_nat_chain, &h); e; e = iptc_next_rule(e, &h), i++) #endif { if(proto==e->ip.proto) { match = (const struct ipt_entry_match *)&e->elems; if(0 == strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN)) { const struct ipt_tcp * info; info = (const struct ipt_tcp *)match->data; if(eport != info->dpts[0]) continue; } else { const struct ipt_udp * info; info = (const struct ipt_udp *)match->data; if(eport != info->dpts[0]) continue; } /* get the index, the internal address and the internal port * of the rule */ index = i; target = (void *)e + e->target_offset; mr = (const struct ip_nat_multi_range *)&target->data[0]; iaddr = mr->range[0].min_ip; iport = ntohs(mr->range[0].min.all); r = 0; break; } } } if(h) #ifdef IPTABLES_143 iptc_free(h); #else iptc_free(&h); #endif if(r == 0) { syslog(LOG_INFO, "Trying to delete nat rule at index %u", index); /* Now delete both rules */ /* first delete the nat rule */ h = iptc_init("nat"); if(h) { r = delete_rule_and_commit(index, h, miniupnpd_nat_chain, "delete_redirect_rule"); } if((r == 0) && (h = iptc_init("filter"))) { i = 0; /* we must find the right index for the filter rule */ #ifdef IPTABLES_143 for(e = iptc_first_rule(miniupnpd_forward_chain, h); e; e = iptc_next_rule(e, h), i++) #else for(e = iptc_first_rule(miniupnpd_forward_chain, &h); e; e = iptc_next_rule(e, &h), i++) #endif { if(proto==e->ip.proto) { match = (const struct ipt_entry_match *)&e->elems; /*syslog(LOG_DEBUG, "filter rule #%u: %s %s", i, match->u.user.name, inet_ntoa(e->ip.dst));*/ if(0 == strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN)) { const struct ipt_tcp * info; info = (const struct ipt_tcp *)match->data; if(iport != info->dpts[0]) continue; } else { const struct ipt_udp * info; info = (const struct ipt_udp *)match->data; if(iport != info->dpts[0]) continue; } if(iaddr != e->ip.dst.s_addr) continue; index = i; break; } } syslog(LOG_INFO, "Trying to delete filter rule at index %u", index); r = delete_rule_and_commit(index, h, miniupnpd_forward_chain, "delete_filter_rule"); } } del_redirect_desc(eport, proto); return r; } /* ==================================== */ /* TODO : add the -m state --state NEW,ESTABLISHED,RELATED * only for the filter rule */ static struct ipt_entry_match * get_tcp_match(unsigned short dport) { struct ipt_entry_match *match; struct ipt_tcp * tcpinfo; size_t size; size = IPT_ALIGN(sizeof(struct ipt_entry_match)) + IPT_ALIGN(sizeof(struct ipt_tcp)); match = calloc(1, size); match->u.match_size = size; strncpy(match->u.user.name, "tcp", sizeof(match->u.user.name)); tcpinfo = (struct ipt_tcp *)match->data; tcpinfo->spts[0] = 0; /* all source ports */ tcpinfo->spts[1] = 0xFFFF; tcpinfo->dpts[0] = dport; /* specified destination port */ tcpinfo->dpts[1] = dport; return match; } static struct ipt_entry_match * get_udp_match(unsigned short dport) { struct ipt_entry_match *match; struct ipt_udp * udpinfo; size_t size; size = IPT_ALIGN(sizeof(struct ipt_entry_match)) + IPT_ALIGN(sizeof(struct ipt_udp)); match = calloc(1, size); match->u.match_size = size; strncpy(match->u.user.name, "udp", sizeof(match->u.user.name)); udpinfo = (struct ipt_udp *)match->data; udpinfo->spts[0] = 0; /* all source ports */ udpinfo->spts[1] = 0xFFFF; udpinfo->dpts[0] = dport; /* specified destination port */ udpinfo->dpts[1] = dport; return match; } static struct ipt_entry_target * get_dnat_target(const char * daddr, unsigned short dport) { struct ipt_entry_target * target; struct ip_nat_multi_range * mr; struct ip_nat_range * range; size_t size; size = IPT_ALIGN(sizeof(struct ipt_entry_target)) + IPT_ALIGN(sizeof(struct ip_nat_multi_range)); target = calloc(1, size); target->u.target_size = size; strncpy(target->u.user.name, "DNAT", sizeof(target->u.user.name)); /* one ip_nat_range already included in ip_nat_multi_range */ mr = (struct ip_nat_multi_range *)&target->data[0]; mr->rangesize = 1; range = &mr->range[0]; range->min_ip = range->max_ip = inet_addr(daddr); range->flags |= IP_NAT_RANGE_MAP_IPS; range->min.all = range->max.all = htons(dport); range->flags |= IP_NAT_RANGE_PROTO_SPECIFIED; return target; } /* iptc_init_verify_and_append() * return 0 on success, -1 on failure */ static int iptc_init_verify_and_append(const char * table, const char * miniupnpd_chain, struct ipt_entry * e, const char * logcaller) { IPTC_HANDLE h; h = iptc_init(table); if(!h) { syslog(LOG_ERR, "%s : iptc_init() error : %s\n", logcaller, iptc_strerror(errno)); return -1; } if(!iptc_is_chain(miniupnpd_chain, h)) { syslog(LOG_ERR, "%s : chain %s not found", logcaller, miniupnpd_chain); if(h) #ifdef IPTABLES_143 iptc_free(h); #else iptc_free(&h); #endif return -1; } /* iptc_insert_entry(miniupnpd_chain, e, n, h/&h) could also be used */ #ifdef IPTABLES_143 if(!iptc_append_entry(miniupnpd_chain, e, h)) #else if(!iptc_append_entry(miniupnpd_chain, e, &h)) #endif { syslog(LOG_ERR, "%s : iptc_append_entry() error : %s\n", logcaller, iptc_strerror(errno)); if(h) #ifdef IPTABLES_143 iptc_free(h); #else iptc_free(&h); #endif return -1; } #ifdef IPTABLES_143 if(!iptc_commit(h)) #else if(!iptc_commit(&h)) #endif { syslog(LOG_ERR, "%s : iptc_commit() error : %s\n", logcaller, iptc_strerror(errno)); if(h) #ifdef IPTABLES_143 iptc_free(h); #else iptc_free(&h); #endif return -1; } if(h) #ifdef IPTABLES_143 iptc_free(h); #else iptc_free(&h); #endif return 0; } /* add nat rule * iptables -t nat -A MINIUPNPD -p proto --dport eport -j DNAT --to iaddr:iport * */ static int addnatrule(int proto, unsigned short eport, const char * iaddr, unsigned short iport, const char * rhost) { int r = 0; struct ipt_entry * e; struct ipt_entry_match *match = NULL; struct ipt_entry_target *target = NULL; e = calloc(1, sizeof(struct ipt_entry)); e->ip.proto = proto; if(proto == IPPROTO_TCP) { match = get_tcp_match(eport); } else { match = get_udp_match(eport); } e->nfcache = NFC_IP_DST_PT; target = get_dnat_target(iaddr, iport); e->nfcache |= NFC_UNKNOWN; e = realloc(e, sizeof(struct ipt_entry) + match->u.match_size + target->u.target_size); memcpy(e->elems, match, match->u.match_size); memcpy(e->elems + match->u.match_size, target, target->u.target_size); e->target_offset = sizeof(struct ipt_entry) + match->u.match_size; e->next_offset = sizeof(struct ipt_entry) + match->u.match_size + target->u.target_size; /* remote host */ if(rhost && (rhost[0] != '\0') && (0 != strcmp(rhost, "*"))) { e->ip.src.s_addr = inet_addr(rhost); e->ip.smsk.s_addr = INADDR_NONE; } r = iptc_init_verify_and_append("nat", miniupnpd_nat_chain, e, "addnatrule()"); free(target); free(match); free(e); return r; } /* ================================= */ static struct ipt_entry_target * get_accept_target(void) { struct ipt_entry_target * target = NULL; size_t size; size = IPT_ALIGN(sizeof(struct ipt_entry_target)) + IPT_ALIGN(sizeof(int)); target = calloc(1, size); target->u.user.target_size = size; strncpy(target->u.user.name, "ACCEPT", sizeof(target->u.user.name)); return target; } /* add_filter_rule() * */ static int add_filter_rule(int proto, const char * rhost, const char * iaddr, unsigned short iport) { int r = 0; struct ipt_entry * e; struct ipt_entry_match *match = NULL; struct ipt_entry_target *target = NULL; e = calloc(1, sizeof(struct ipt_entry)); e->ip.proto = proto; if(proto == IPPROTO_TCP) { match = get_tcp_match(iport); } else { match = get_udp_match(iport); } e->nfcache = NFC_IP_DST_PT; e->ip.dst.s_addr = inet_addr(iaddr); e->ip.dmsk.s_addr = INADDR_NONE; target = get_accept_target(); e->nfcache |= NFC_UNKNOWN; e = realloc(e, sizeof(struct ipt_entry) + match->u.match_size + target->u.target_size); memcpy(e->elems, match, match->u.match_size); memcpy(e->elems + match->u.match_size, target, target->u.target_size); e->target_offset = sizeof(struct ipt_entry) + match->u.match_size; e->next_offset = sizeof(struct ipt_entry) + match->u.match_size + target->u.target_size; /* remote host */ if(rhost && (rhost[0] != '\0') && (0 != strcmp(rhost, "*"))) { e->ip.src.s_addr = inet_addr(rhost); e->ip.smsk.s_addr = INADDR_NONE; } r = iptc_init_verify_and_append("filter", miniupnpd_forward_chain, e, "add_filter_rule()"); free(target); free(match); free(e); return r; } /* return an (malloc'ed) array of "external" port for which there is * a port mapping. number is the size of the array */ unsigned short * get_portmappings_in_range(unsigned short startport, unsigned short endport, int proto, unsigned int * number) { unsigned short * array; unsigned int capacity; unsigned short eport; IPTC_HANDLE h; const struct ipt_entry * e; const struct ipt_entry_match *match; *number = 0; capacity = 128; array = calloc(capacity, sizeof(unsigned short)); if(!array) { syslog(LOG_ERR, "get_portmappings_in_range() : calloc error"); return NULL; } h = iptc_init("nat"); if(!h) { syslog(LOG_ERR, "get_redirect_rule_by_index() : " "iptc_init() failed : %s", iptc_strerror(errno)); free(array); return NULL; } if(!iptc_is_chain(miniupnpd_nat_chain, h)) { syslog(LOG_ERR, "chain %s not found", miniupnpd_nat_chain); free(array); array = NULL; } else { #ifdef IPTABLES_143 for(e = iptc_first_rule(miniupnpd_nat_chain, h); e; e = iptc_next_rule(e, h)) #else for(e = iptc_first_rule(miniupnpd_nat_chain, &h); e; e = iptc_next_rule(e, &h)) #endif { if(proto == e->ip.proto) { match = (const struct ipt_entry_match *)&e->elems; if(0 == strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN)) { const struct ipt_tcp * info; info = (const struct ipt_tcp *)match->data; eport = info->dpts[0]; } else { const struct ipt_udp * info; info = (const struct ipt_udp *)match->data; eport = info->dpts[0]; } if(startport <= eport && eport <= endport) { if(*number >= capacity) { /* need to increase the capacity of the array */ array = realloc(array, sizeof(unsigned short)*capacity); if(!array) { syslog(LOG_ERR, "get_portmappings_in_range() : realloc(%u) error", (unsigned)sizeof(unsigned short)*capacity); *number = 0; break; } array[*number] = eport; (*number)++; } } } } } if(h) #ifdef IPTABLES_143 iptc_free(h); #else iptc_free(&h); #endif return array; } /* ================================ */ #ifdef DEBUG static int print_match(const struct ipt_entry_match *match) { printf("match %s\n", match->u.user.name); if(0 == strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN)) { struct ipt_tcp * tcpinfo; tcpinfo = (struct ipt_tcp *)match->data; printf("srcport = %hu:%hu dstport = %hu:%hu\n", tcpinfo->spts[0], tcpinfo->spts[1], tcpinfo->dpts[0], tcpinfo->dpts[1]); } else if(0 == strncmp(match->u.user.name, "udp", IPT_FUNCTION_MAXNAMELEN)) { struct ipt_udp * udpinfo; udpinfo = (struct ipt_udp *)match->data; printf("srcport = %hu:%hu dstport = %hu:%hu\n", udpinfo->spts[0], udpinfo->spts[1], udpinfo->dpts[0], udpinfo->dpts[1]); } return 0; } static void print_iface(const char * iface, const unsigned char * mask, int invert) { unsigned i; if(mask[0] == 0) return; if(invert) printf("! "); for(i=0; i> 24, (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff); } /* for debug */ /* read the "filter" and "nat" tables */ int list_redirect_rule(const char * ifname) { IPTC_HANDLE h; const struct ipt_entry * e; const struct ipt_entry_target * target; const struct ip_nat_multi_range * mr; const char * target_str; char addr[16], mask[16]; (void)ifname; h = iptc_init("nat"); if(!h) { printf("iptc_init() error : %s\n", iptc_strerror(errno)); return -1; } if(!iptc_is_chain(miniupnpd_nat_chain, h)) { printf("chain %s not found\n", miniupnpd_nat_chain); #ifdef IPTABLES_143 iptc_free(h); #else iptc_free(&h); #endif return -1; } #ifdef IPTABLES_143 for(e = iptc_first_rule(miniupnpd_nat_chain, h); e; e = iptc_next_rule(e, h)) { target_str = iptc_get_target(e, h); #else for(e = iptc_first_rule(miniupnpd_nat_chain, &h); e; e = iptc_next_rule(e, &h)) { target_str = iptc_get_target(e, &h); #endif printf("===\n"); inet_ntop(AF_INET, &e->ip.src, addr, sizeof(addr)); inet_ntop(AF_INET, &e->ip.smsk, mask, sizeof(mask)); printf("src = %s%s/%s\n", (e->ip.invflags & IPT_INV_SRCIP)?"! ":"", /*inet_ntoa(e->ip.src), inet_ntoa(e->ip.smsk)*/ addr, mask); inet_ntop(AF_INET, &e->ip.dst, addr, sizeof(addr)); inet_ntop(AF_INET, &e->ip.dmsk, mask, sizeof(mask)); printf("dst = %s%s/%s\n", (e->ip.invflags & IPT_INV_DSTIP)?"! ":"", /*inet_ntoa(e->ip.dst), inet_ntoa(e->ip.dmsk)*/ addr, mask); /*printf("in_if = %s out_if = %s\n", e->ip.iniface, e->ip.outiface);*/ printf("in_if = "); print_iface(e->ip.iniface, e->ip.iniface_mask, e->ip.invflags & IPT_INV_VIA_IN); printf(" out_if = "); print_iface(e->ip.outiface, e->ip.outiface_mask, e->ip.invflags & IPT_INV_VIA_OUT); printf("\n"); printf("ip.proto = %s%d\n", (e->ip.invflags & IPT_INV_PROTO)?"! ":"", e->ip.proto); /* display matches stuff */ if(e->target_offset) { IPT_MATCH_ITERATE(e, print_match); /*printf("\n");*/ } printf("target = %s\n", target_str); target = (void *)e + e->target_offset; mr = (const struct ip_nat_multi_range *)&target->data[0]; printf("ips "); printip(ntohl(mr->range[0].min_ip)); printf(" "); printip(ntohl(mr->range[0].max_ip)); printf("\nports %hu %hu\n", ntohs(mr->range[0].min.all), ntohs(mr->range[0].max.all)); printf("flags = %x\n", mr->range[0].flags); } if(h) #ifdef IPTABLES_143 iptc_free(h); #else iptc_free(&h); #endif return 0; } #endif miniupnpd-1.8.20130730/netfilter/iptcrdr.h010064400017500000024000000017041203340734100172600ustar00nanardstaff/* $Id: iptcrdr.h,v 1.19 2012/09/27 16:02:43 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2011 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #ifndef IPTCRDR_H_INCLUDED #define IPTCRDR_H_INCLUDED #include "../commonrdr.h" int add_redirect_rule2(const char * ifname, const char * rhost, unsigned short eport, const char * iaddr, unsigned short iport, int proto, const char * desc, unsigned int timestamp); int add_filter_rule2(const char * ifname, const char * rhost, const char * iaddr, unsigned short eport, unsigned short iport, int proto, const char * desc); int delete_redirect_and_filter_rules(unsigned short eport, int proto); /* for debug */ int list_redirect_rule(const char * ifname); #endif miniupnpd-1.8.20130730/netfilter/iptables_display.sh010075500017500000024000000004461156421566500213470ustar00nanardstaff#! /bin/sh # $Id: iptables_display.sh,v 1.4 2011/05/16 12:11:37 nanard Exp $ IPTABLES=/sbin/iptables #display all chains relative to miniupnpd $IPTABLES -v -n -t nat -L PREROUTING $IPTABLES -v -n -t nat -L MINIUPNPD $IPTABLES -v -n -t filter -L FORWARD $IPTABLES -v -n -t filter -L MINIUPNPD miniupnpd-1.8.20130730/netfilter/iptables_flush.sh010075500017500000024000000003101156421566500210110ustar00nanardstaff#! /bin/sh # $Id: iptables_flush.sh,v 1.3 2011/05/16 12:11:37 nanard Exp $ IPTABLES=/sbin/iptables #flush all rules owned by miniupnpd $IPTABLES -t nat -F MINIUPNPD $IPTABLES -t filter -F MINIUPNPD miniupnpd-1.8.20130730/netfilter/iptables_init.sh010075500017500000024000000012041170030540100206120ustar00nanardstaff#! /bin/sh # $Id: iptables_init.sh,v 1.6 2012/01/02 09:55:20 nanard Exp $ IPTABLES=/sbin/iptables #change this parameters : EXTIF=eth0 EXTIP="`LC_ALL=C /sbin/ifconfig $EXTIF | grep 'inet ' | awk '{print $2}' | sed -e 's/.*://'`" echo "External IP = $EXTIP" #adding the MINIUPNPD chain for nat $IPTABLES -t nat -N MINIUPNPD #adding the rule to MINIUPNPD #$IPTABLES -t nat -A PREROUTING -d $EXTIP -i $EXTIF -j MINIUPNPD $IPTABLES -t nat -A PREROUTING -i $EXTIF -j MINIUPNPD #adding the MINIUPNPD chain for filter $IPTABLES -t filter -N MINIUPNPD #adding the rule to MINIUPNPD $IPTABLES -t filter -A FORWARD -i $EXTIF ! -o $EXTIF -j MINIUPNPD miniupnpd-1.8.20130730/netfilter/iptables_removeall.sh010075500017500000024000000012621170030540100216410ustar00nanardstaff#! /bin/sh # $Id: iptables_removeall.sh,v 1.6 2012/01/02 09:55:20 nanard Exp $ IPTABLES=/sbin/iptables #change this parameters : EXTIF=eth0 EXTIP="`LC_ALL=C /sbin/ifconfig $EXTIF | grep 'inet ' | awk '{print $2}' | sed -e 's/.*://'`" #removing the MINIUPNPD chain for nat $IPTABLES -t nat -F MINIUPNPD #rmeoving the rule to MINIUPNPD #$IPTABLES -t nat -D PREROUTING -d $EXTIP -i $EXTIF -j MINIUPNPD $IPTABLES -t nat -D PREROUTING -i $EXTIF -j MINIUPNPD $IPTABLES -t nat -X MINIUPNPD #removing the MINIUPNPD chain for filter $IPTABLES -t filter -F MINIUPNPD #adding the rule to MINIUPNPD $IPTABLES -t filter -D FORWARD -i $EXTIF ! -o $EXTIF -j MINIUPNPD $IPTABLES -t filter -X MINIUPNPD miniupnpd-1.8.20130730/netfilter/Makefile010064400017500000024000000040361174625650500171170ustar00nanardstaff# $Id: Makefile,v 1.6 2012/04/26 13:50:48 nanard Exp $ CFLAGS?=-Wall -g -D_GNU_SOURCE -DDEBUG -Wstrict-prototypes -Wdeclaration-after-statement -ansi CC = gcc #LIBS = -liptc LIBS = -lip4tc ARCH := $(shell uname -m | grep -q "x86_64" && echo 64) ifdef IPTABLESPATH CFLAGS := $(CFLAGS) -I$(IPTABLESPATH)/include/ LDFLAGS := $(LDFLAFGS) -L$(IPTABLESPATH)/libiptc/ # get iptables version and set IPTABLES_143 macro if needed IPTABLESVERSION := $(shell grep "\#define VERSION" $(IPTABLESPATH)/config.h | tr -d \" |cut -d" " -f3 ) 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 := $(CFLAGS) -DIPTABLES_143 # the following sucks, but works LIBS = $(IPTABLESPATH)/libiptc/.libs/libip4tc.o #LIBS = $(IPTABLESPATH)/libiptc/.libs/libiptc.a else LIBS = $(IPTABLESPATH)/libiptc/libiptc.a endif else # check for system-wide iptables files. Test if iptables version >= 1.4.3 #TEST := $(shell test -f /usr/include/iptables/internal.h && grep -q "\#define IPTABLES_VERSION" /usr/include/iptables/internal.h && echo 1) 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 TEST_LIB := $(shell test -f /usr/lib$(ARCH)/libiptc.a && echo 1) ifeq ($(TEST_LIB), 1) LIBS = -liptc /usr/lib$(ARCH)/libiptc.a endif endif endif all: iptcrdr.o testiptcrdr iptpinhole.o testiptpinhole clean: $(RM) *.o testiptcrdr testiptpinhole testiptcrdr: testiptcrdr.o iptcrdr.o upnpglobalvars.o $(LIBS) testiptpinhole: testiptpinhole.o iptpinhole.o upnpglobalvars.o $(LIBS) iptcrdr.o: iptcrdr.c iptcrdr.h iptpinhole.o: iptpinhole.c iptpinhole.h upnpglobalvars.o: ../upnpglobalvars.c ../upnpglobalvars.h $(CC) -c -o $@ $< miniupnpd-1.8.20130730/netfilter/testiptcrdr.c010064400017500000024000000035131174573053600201710ustar00nanardstaff/* $Id: testiptcrdr.c,v 1.18 2012/04/24 22:41:53 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2012 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #include #include #include #include "iptcrdr.h" #include "../commonrdr.h" #ifndef PRIu64 #define PRIu64 "llu" #endif int main(int argc, char ** argv) { unsigned short eport, iport; const char * iaddr; printf("Usage %s \n", argv[0]); if(argc<4) return -1; openlog("testiptcrdr", LOG_PERROR|LOG_CONS, LOG_LOCAL0); eport = (unsigned short)atoi(argv[1]); iaddr = argv[2]; iport = (unsigned short)atoi(argv[3]); #if 0 printf("trying to redirect port %hu to %s:%hu\n", eport, iaddr, iport); if(addnatrule(IPPROTO_TCP, eport, iaddr, iport) < 0) return -1; if(add_filter_rule(IPPROTO_TCP, iaddr, iport) < 0) return -1; #endif /* test */ { unsigned short p1, p2; char addr[16]; int proto2; char desc[256]; char rhost[256]; unsigned int timestamp; u_int64_t packets, bytes; desc[0] = '\0'; if(get_redirect_rule_by_index(0, "", &p1, addr, sizeof(addr), &p2, &proto2, desc, sizeof(desc), rhost, sizeof(rhost), ×tamp, &packets, &bytes) < 0) { printf("rule not found\n"); } else { printf("redirected port %hu to %s:%hu proto %d packets=%" PRIu64 " bytes=%" PRIu64 "\n", p1, addr, p2, proto2, packets, bytes); } } printf("trying to list nat rules :\n"); list_redirect_rule(argv[1]); printf("deleting\n"); delete_redirect_and_filter_rules(eport, IPPROTO_TCP); return 0; } miniupnpd-1.8.20130730/minixml.c010064400017500000024000000124031172522177000152720ustar00nanardstaff/* $Id: minixml.c,v 1.10 2012/03/05 19:42:47 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-2011, 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 #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)[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, "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); } miniupnpd-1.8.20130730/minixml.h010064400017500000024000000022331203340734000152670ustar00nanardstaff/* $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 miniupnpd-1.8.20130730/upnpreplyparse.c010064400017500000024000000101721215431657000167070ustar00nanardstaff/* $Id: upnpreplyparse.c,v 1.15 2013/06/06 21:36:40 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 #include #include #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'; } LIST_INSERT_HEAD( &(data->head), nv, entries); } 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; LIST_INIT(&(data->head)); 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->head.lh_first) != NULL) { LIST_REMOVE(nv, entries); free(nv); } } char * GetValueFromNameValueList(struct NameValueParserData * pdata, const char * Name) { struct NameValue * nv; char * p = NULL; for(nv = pdata->head.lh_first; (nv != NULL) && (p == NULL); nv = nv->entries.le_next) { if(strcmp(nv->name, Name) == 0) p = nv->value; } return p; } #if 0 /* useless now that minixml ignores namespaces by itself */ char * GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata, const char * Name) { struct NameValue * nv; char * p = NULL; char * pname; for(nv = pdata->head.lh_first; (nv != NULL) && (p == NULL); nv = nv->entries.le_next) { pname = strrchr(nv->name, ':'); if(pname) pname++; else pname = nv->name; if(strcmp(pname, Name)==0) p = nv->value; } return p; } #endif /* debug all-in-one function * do parsing then display to stdout */ #ifdef DEBUG void DisplayNameValueList(char * buffer, int bufsize) { struct NameValueParserData pdata; struct NameValue * nv; ParseNameValue(buffer, bufsize, &pdata); for(nv = pdata.head.lh_first; nv != NULL; nv = nv->entries.le_next) { printf("%s = %s\n", nv->name, nv->value); } ClearNameValueList(&pdata); } #endif miniupnpd-1.8.20130730/upnpreplyparse.h010064400017500000024000000027561215431657000167250ustar00nanardstaff/* $Id: upnpreplyparse.h,v 1.17 2013/06/06 21:36:40 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 #if defined(NO_SYS_QUEUE_H) || defined(_WIN32) || defined(__HAIKU__) #include "bsdqueue.h" #else #include #endif #ifdef __cplusplus extern "C" { #endif struct NameValue { LIST_ENTRY(NameValue) entries; char name[64]; char value[128]; }; struct NameValueParserData { LIST_HEAD(listhead, NameValue) 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 miniupnpd-1.8.20130730/testgetifstats.c010064400017500000024000000020601177217435300166760ustar00nanardstaff/* $Id: testgetifstats.c,v 1.6 2012/06/22 16:11:58 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #include #include #include "getifstats.h" #if defined(__sun) /* solaris 10 does not define LOG_PERROR */ #define LOG_PERROR 0 #endif int main(int argc, char **argv) { int r; struct ifdata data; if(argc<2) { fprintf(stderr, "usage : %s \n", argv[0]); return 1; } openlog("testgetifstats", LOG_CONS|LOG_PERROR, LOG_USER); memset(&data, 0, sizeof(data)); r = getifstats(argv[1], &data); printf("getifstats() returned %d\n", r); printf("stats for interface %s :\n", argv[1]); printf("bitrate = %lu\n", data.baudrate); printf(" input packets : %9lu\t input bytes : %9lu\n", data.ipackets, data.ibytes); printf("output packets : %9lu\toutput bytes : %9lu\n", data.opackets, data.obytes); return 0; } miniupnpd-1.8.20130730/upnpdescstrings.h010064400017500000024000000027641203340734000170560ustar00nanardstaff/* $Id: upnpdescstrings.h,v 1.8 2012/09/27 16:00:10 nanard Exp $ */ /* miniupnp project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2012 Thomas Bernard * This software is subject to the coditions detailed in * the LICENCE file provided within the distribution */ #ifndef UPNPDESCSTRINGS_H_INCLUDED #define UPNPDESCSTRINGS_H_INCLUDED #include "config.h" /* strings used in the root device xml description */ /*#define ROOTDEV_FRIENDLYNAME OS_NAME " router"*/ #define ROOTDEV_MANUFACTURER OS_NAME #define ROOTDEV_MANUFACTURERURL OS_URL #define ROOTDEV_MODELNAME OS_NAME " router" #define ROOTDEV_MODELDESCRIPTION OS_NAME " router" #define ROOTDEV_MODELURL OS_URL #define WANDEV_FRIENDLYNAME "WANDevice" #define WANDEV_MANUFACTURER "MiniUPnP" #define WANDEV_MANUFACTURERURL "http://miniupnp.free.fr/" #define WANDEV_MODELNAME "WAN Device" #define WANDEV_MODELDESCRIPTION "WAN Device" #define WANDEV_MODELNUMBER UPNP_VERSION #define WANDEV_MODELURL "http://miniupnp.free.fr/" #define WANDEV_UPC "000000000000" /* UPC is 12 digit (barcode) */ #define WANCDEV_FRIENDLYNAME "WANConnectionDevice" #define WANCDEV_MANUFACTURER WANDEV_MANUFACTURER #define WANCDEV_MANUFACTURERURL WANDEV_MANUFACTURERURL #define WANCDEV_MODELNAME "MiniUPnPd" #define WANCDEV_MODELDESCRIPTION "MiniUPnP daemon" #define WANCDEV_MODELNUMBER UPNP_VERSION #define WANCDEV_MODELURL "http://miniupnp.free.fr/" #define WANCDEV_UPC "000000000000" /* UPC is 12 digit (barcode) */ #endif miniupnpd-1.8.20130730/genconfig.sh010075500017500000024000000327571214100740700157550ustar00nanardstaff#! /bin/sh # $Id: genconfig.sh,v 1.63 2013/05/03 09:30:10 nanard Exp $ # miniupnp daemon # http://miniupnp.free.fr or http://miniupnp.tuxfamily.org/ # (c) 2006-2012 Thomas Bernard # This software is subject to the conditions detailed in the # LICENCE file provided within the distribution for argv; do case "$argv" in --ipv6) IPV6=1 ;; --igd2) IGD2=1 ;; --strict) STRICT=1 ;; --leasefile) LEASEFILE=1 ;; --help|-h) echo "Usage : $0 [options]" echo " --ipv6 enable IPv6" echo " --igd2 build an IGDv2 instead of an IGDv1" echo " --strict be more strict regarding compliance with UPnP specifications" echo " --leasefile enable lease file" exit 1 ;; *) echo "Option not recognized : $argv" echo "use -h option to display help" exit 1 ;; esac done RM="rm -f" MV="mv" CONFIGFILE="config.h.tmp" CONFIGFILE_FINAL="config.h" CONFIGMACRO="CONFIG_H_INCLUDED" # version reported in XML descriptions #UPNP_VERSION=20070827 UPNP_VERSION=`date +"%Y%m%d"` # Facility to syslog LOG_MINIUPNPD="LOG_DAEMON" # detecting the OS name and version OS_NAME=`uname -s` OS_VERSION=`uname -r` # pfSense special case if [ -f /etc/platform ]; then if [ `cat /etc/platform` = "pfSense" ]; then OS_NAME=pfSense OS_VERSION=`cat /etc/version` fi fi # OpenWRT special case if [ -f ./os.openwrt ]; then OS_NAME=OpenWRT OS_VERSION=$(cat ./os.openwrt) fi # AstLinux special case if [ -f ./os.astlinux ]; then OS_NAME=AstLinux OS_VERSION=$(cat ./os.astlinux) fi # Tomato USB special case if [ -f ../shared/tomato_version ]; then OS_NAME=Tomato OS_VERSION="Tomato $(cat ../shared/tomato_version)" fi ${RM} ${CONFIGFILE} echo "/* MiniUPnP Project" >> ${CONFIGFILE} echo " * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/" >> ${CONFIGFILE} echo " * (c) 2006-2012 Thomas Bernard" >> ${CONFIGFILE} echo " * generated by $0 on `date`" >> ${CONFIGFILE} echo " * using command line options $* */" >> ${CONFIGFILE} echo "#ifndef $CONFIGMACRO" >> ${CONFIGFILE} echo "#define $CONFIGMACRO" >> ${CONFIGFILE} echo "" >> ${CONFIGFILE} echo "#include " >> ${CONFIGFILE} echo "" >> ${CONFIGFILE} echo "#define MINIUPNPD_VERSION \"`cat VERSION`\"" >> ${CONFIGFILE} echo "" >> ${CONFIGFILE} echo "#define UPNP_VERSION \"$UPNP_VERSION\"" >> ${CONFIGFILE} # OS Specific stuff case $OS_NAME in OpenBSD) MAJORVER=`echo $OS_VERSION | cut -d. -f1` MINORVER=`echo $OS_VERSION | cut -d. -f2` #echo "OpenBSD majorversion=$MAJORVER minorversion=$MINORVER" # rtableid was introduced in OpenBSD 4.0 if [ $MAJORVER -ge 4 ]; then echo "#define PFRULE_HAS_RTABLEID" >> ${CONFIGFILE} fi # from the 3.8 version, packets and bytes counters are double : in/out if [ \( $MAJORVER -ge 4 \) -o \( $MAJORVER -eq 3 -a $MINORVER -ge 8 \) ]; then echo "#define PFRULE_INOUT_COUNTS" >> ${CONFIGFILE} fi # from the 4.7 version, new pf if [ \( $MAJORVER -ge 5 \) -o \( $MAJORVER -eq 4 -a $MINORVER -ge 7 \) ]; then echo "#define PF_NEWSTYLE" >> ${CONFIGFILE} fi # onrdomain was introduced in OpenBSD 5.0 if [ $MAJORVER -ge 5 ]; then echo "#define PFRULE_HAS_ONRDOMAIN" >> ${CONFIGFILE} fi FW=pf echo "#define USE_IFACEWATCHER 1" >> ${CONFIGFILE} OS_URL=http://www.openbsd.org/ ;; FreeBSD) VER=`grep '#define __FreeBSD_version' /usr/include/sys/param.h | awk '{print $3}'` if [ $VER -ge 700049 ]; then echo "#define PFRULE_INOUT_COUNTS" >> ${CONFIGFILE} fi # new way to see which one to use PF or IPF. # see http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=957 if [ -f /etc/rc.subr ] && [ -f /etc/rc.conf ] ; then # source file with handy subroutines like checkyesno . /etc/rc.subr # source config file so we can probe vars . /etc/rc.conf if checkyesno ipfilter_enable; then echo "Using ipf" FW=ipf elif checkyesno pf_enable; then echo "Using pf" FW=pf elif checkyesno firewall_enable; then echo "Using ifpw" FW=ipfw fi fi if [ -z $FW ] ; then echo "Could not detect usage of ipf, pf, ipfw. Compiling for pf by default" FW=pf fi echo "#define USE_IFACEWATCHER 1" >> ${CONFIGFILE} OS_URL=http://www.freebsd.org/ ;; pfSense) # we need to detect if PFRULE_INOUT_COUNTS macro is needed FW=pf echo "#define USE_IFACEWATCHER 1" >> ${CONFIGFILE} OS_URL=http://www.pfsense.com/ ;; NetBSD) if [ -f /etc/rc.subr ] && [ -f /etc/rc.conf ] ; then # source file with handy subroutines like checkyesno . /etc/rc.subr # source config file so we can probe vars . /etc/rc.conf if checkyesno pf; then FW=pf elif checkyesno ipfilter; then FW=ipf fi fi if [ -z $FW ] ; then echo "Could not detect ipf nor pf, defaulting to pf." FW=pf fi echo "#define USE_IFACEWATCHER 1" >> ${CONFIGFILE} OS_URL=http://www.netbsd.org/ ;; DragonFly) if [ -f /etc/rc.subr ] && [ -f /etc/rc.conf ] ; then # source file with handy subroutines like checkyesno . /etc/rc.subr # source config file so we can probe vars . /etc/rc.conf if checkyesno pf; then FW=pf elif checkyesno ipfilter; then FW=ipf fi fi if [ -z $FW ] ; then echo "Could not detect ipf nor pf, defaulting to pf." FW=pf fi echo "#define USE_IFACEWATCHER 1" >> ${CONFIGFILE} OS_URL=http://www.dragonflybsd.org/ ;; SunOS) echo "#define USE_IFACEWATCHER 1" >> ${CONFIGFILE} FW=ipf echo "#define LOG_PERROR 0" >> ${CONFIGFILE} echo "#define SOLARIS_KSTATS 1" >> ${CONFIGFILE} # solaris 10 does not define u_int64_t ? # but it does define uint64_t echo "typedef uint64_t u_int64_t;" >> ${CONFIGFILE} OS_URL=http://www.sun.com/solaris/ ;; Linux) OS_URL=http://www.kernel.org/ KERNVERA=`echo $OS_VERSION | awk -F. '{print $1}'` KERNVERB=`echo $OS_VERSION | awk -F. '{print $2}'` KERNVERC=`echo $OS_VERSION | awk -F. '{print $3}'` KERNVERD=`echo $OS_VERSION | awk -F. '{print $4}'` #echo "$KERNVERA.$KERNVERB.$KERNVERC.$KERNVERD" # Debian GNU/Linux special case if [ -f /etc/debian_version ]; then OS_NAME=Debian OS_VERSION=`cat /etc/debian_version` OS_URL=http://www.debian.org/ fi # same thing for Gentoo linux if [ -f /etc/gentoo-release ]; then OS_NAME=Gentoo OS_VERSION=`cat /etc/gentoo-release` OS_URL=http://www.gentoo.org/ fi # use lsb_release (Linux Standard Base) when available LSB_RELEASE=`which lsb_release` if [ 0 -eq $? ]; then OS_NAME=`${LSB_RELEASE} -i -s` OS_VERSION=`${LSB_RELEASE} -r -s` case $OS_NAME in Debian) OS_URL=http://www.debian.org/ OS_VERSION=`${LSB_RELEASE} -c -s` ;; Ubuntu) OS_URL=http://www.ubuntu.com/ OS_VERSION=`${LSB_RELEASE} -c -s` ;; Gentoo) OS_URL=http://www.gentoo.org/ ;; arch) OS_URL=http://www.archlinux.org/ OS_VERSION=`uname -r` ;; esac fi echo "#define USE_IFACEWATCHER 1" >> ${CONFIGFILE} FW=netfilter ;; OpenWRT) OS_URL=http://www.openwrt.org/ echo "#define USE_IFACEWATCHER 1" >> ${CONFIGFILE} FW=netfilter ;; AstLinux) OS_URL=http://www.astlinux.org/ echo "#define USE_IFACEWATCHER 1" >> ${CONFIGFILE} FW=netfilter ;; Tomato) OS_NAME=UPnP OS_URL=http://tomatousb.org/ echo "" >> ${CONFIGFILE} echo "#include " >> ${CONFIGFILE} echo "" >> ${CONFIGFILE} echo "#ifdef LINUX26" >> ${CONFIGFILE} echo "#define USE_IFACEWATCHER 1" >> ${CONFIGFILE} echo "#endif" >> ${CONFIGFILE} echo "#ifdef TCONFIG_IPV6" >> ${CONFIGFILE} echo "#define ENABLE_IPV6" >> ${CONFIGFILE} echo "#endif" >> ${CONFIGFILE} FW=netfilter ;; Darwin) echo "#define USE_IFACEWATCHER 1" >> ${CONFIGFILE} FW=ipfw OS_URL=http://developer.apple.com/macosx ;; *) echo "Unknown OS : $OS_NAME" echo "Please contact the author at http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/." exit 1 ;; esac case $FW in pf) echo "#define USE_PF 1" >> ${CONFIGFILE} ;; ipf) echo "#define USE_IPF 1" >> ${CONFIGFILE} ;; ipfw) echo "#define USE_IPFW 1" >> ${CONFIGFILE} ;; netfilter) echo "#define USE_NETFILTER 1" >> ${CONFIGFILE} ;; *) echo "Unknown Firewall/packet filtering software [$FW]" echo "Please contact the author at http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/." exit 1 ;; esac echo "Configuring compilation for [$OS_NAME] [$OS_VERSION] with [$FW] firewall software." echo "Please edit config.h for more compilation options." # define SUPPORT_REMOTEHOST if the FW related code really supports setting # a RemoteHost if [ \( "$FW" = "netfilter" \) -o \( "$FW" = "pf" \) -o \( "$FW" = "ipfw" \) ] ; then echo "#define SUPPORT_REMOTEHOST" >> ${CONFIGFILE} fi echo "" >> ${CONFIGFILE} echo "#define OS_NAME \"$OS_NAME\"" >> ${CONFIGFILE} echo "#define OS_VERSION \"$OS_NAME/$OS_VERSION\"" >> ${CONFIGFILE} echo "#define OS_URL \"${OS_URL}\"" >> ${CONFIGFILE} echo "" >> ${CONFIGFILE} echo "/* syslog facility to be used by miniupnpd */" >> ${CONFIGFILE} echo "#define LOG_MINIUPNPD ${LOG_MINIUPNPD}" >> ${CONFIGFILE} echo "" >> ${CONFIGFILE} echo "/* Uncomment the following line to allow miniupnpd to be" >> ${CONFIGFILE} echo " * controlled by miniupnpdctl */" >> ${CONFIGFILE} echo "/*#define USE_MINIUPNPDCTL*/" >> ${CONFIGFILE} echo "" >> ${CONFIGFILE} echo "/* Comment the following line to disable NAT-PMP operations */" >> ${CONFIGFILE} echo "#define ENABLE_NATPMP" >> ${CONFIGFILE} echo "" >> ${CONFIGFILE} echo "/* Uncomment the following line to enable generation of" >> ${CONFIGFILE} echo " * filter rules with pf */" >> ${CONFIGFILE} echo "/*#define PF_ENABLE_FILTER_RULES*/">> ${CONFIGFILE} echo "" >> ${CONFIGFILE} echo "/* Uncomment the following line to enable caching of results of" >> ${CONFIGFILE} echo " * the getifstats() function */" >> ${CONFIGFILE} echo "/*#define ENABLE_GETIFSTATS_CACHING*/" >> ${CONFIGFILE} echo "/* The cache duration is indicated in seconds */" >> ${CONFIGFILE} echo "#define GETIFSTATS_CACHING_DURATION 2" >> ${CONFIGFILE} echo "" >> ${CONFIGFILE} echo "/* Uncomment the following line to enable multiple external ip support */" >> ${CONFIGFILE} echo "/* note : That is EXPERIMENTAL, do not use that unless you know perfectly what you are doing */" >> ${CONFIGFILE} echo "/* Dynamic external ip adresses are not supported when this option is enabled." >> ${CONFIGFILE} echo " * Also note that you would need to configure your .conf file accordingly. */" >> ${CONFIGFILE} echo "/*#define MULTIPLE_EXTERNAL_IP*/" >> ${CONFIGFILE} echo "" >> ${CONFIGFILE} echo "/* Comment the following line to use home made daemonize() func instead" >> ${CONFIGFILE} echo " * of BSD daemon() */" >> ${CONFIGFILE} echo "#define USE_DAEMON" >> ${CONFIGFILE} echo "" >> ${CONFIGFILE} echo "/* Uncomment the following line to enable lease file support */" >> ${CONFIGFILE} if [ -n "$LEASEFILE" ] ; then echo "#define ENABLE_LEASEFILE" >> ${CONFIGFILE} else echo "/*#define ENABLE_LEASEFILE*/" >> ${CONFIGFILE} fi echo "" >> ${CONFIGFILE} echo "/* Define one or none of the two following macros in order to make some" >> ${CONFIGFILE} echo " * clients happy. It will change the XML Root Description of the IGD." >> ${CONFIGFILE} echo " * Enabling the Layer3Forwarding Service seems to be the more compatible" >> ${CONFIGFILE} echo " * option. */" >> ${CONFIGFILE} echo "/*#define HAS_DUMMY_SERVICE*/" >> ${CONFIGFILE} echo "#define ENABLE_L3F_SERVICE" >> ${CONFIGFILE} echo "" >> ${CONFIGFILE} echo "/* Enable IP v6 support */" >> ${CONFIGFILE} if [ -n "$IPV6" ]; then echo "#define ENABLE_IPV6" >> ${CONFIGFILE} else echo "/*#define ENABLE_IPV6*/" >> ${CONFIGFILE} fi echo "" >> ${CONFIGFILE} echo "/* Enable the support of IGD v2 specification." >> ${CONFIGFILE} echo " * This is not fully tested yet and can cause incompatibilities with some" >> ${CONFIGFILE} echo " * control points, so enable with care. */" >> ${CONFIGFILE} if [ -n "$IGD2" ]; then echo "#define IGD_V2" >> ${CONFIGFILE} else echo "/*#define IGD_V2*/" >> ${CONFIGFILE} fi echo "" >> ${CONFIGFILE} echo "#ifdef IGD_V2" >> ${CONFIGFILE} echo "/* Enable DeviceProtection service (IGDv2) */" >> ${CONFIGFILE} echo "#define ENABLE_DP_SERVICE" >> ${CONFIGFILE} echo "" >> ${CONFIGFILE} echo "/* Enable WANIPv6FirewallControl service (IGDv2). needs IPv6 */" >> ${CONFIGFILE} echo "#ifdef ENABLE_IPV6" >> ${CONFIGFILE} echo "#define ENABLE_6FC_SERVICE" >> ${CONFIGFILE} echo "#endif /* ENABLE_IPV6 */" >> ${CONFIGFILE} echo "#endif /* IGD_V2 */" >> ${CONFIGFILE} echo "" >> ${CONFIGFILE} echo "/* UPnP Events support. Working well enough to be enabled by default." >> ${CONFIGFILE} echo " * It can be disabled to save a few bytes. */" >> ${CONFIGFILE} echo "#define ENABLE_EVENTS" >> ${CONFIGFILE} echo "" >> ${CONFIGFILE} echo "/* include interface name in pf and ipf rules */" >> ${CONFIGFILE} echo "#define USE_IFNAME_IN_RULES" >> ${CONFIGFILE} echo "" >> ${CONFIGFILE} echo "/* Experimental NFQUEUE support. */" >> ${CONFIGFILE} echo "/*#define ENABLE_NFQUEUE*/" >> ${CONFIGFILE} echo "" >> ${CONFIGFILE} echo "/* Enable to make MiniUPnPd more strict about UPnP conformance" >> ${CONFIGFILE} echo " * and the messages it receives from control points */" >> ${CONFIGFILE} if [ -n "$STRICT" ] ; then echo "#define UPNP_STRICT" >> ${CONFIGFILE} else echo "/*#define UPNP_STRICT*/" >> ${CONFIGFILE} fi echo "" >> ${CONFIGFILE} echo "/* Add the optional Date: header in all HTTP responses */" >> ${CONFIGFILE} if [ -n "$STRICT" ] ; then echo "#define ENABLE_HTTP_DATE" >> ${CONFIGFILE} else echo "/*#define ENABLE_HTTP_DATE*/" >> ${CONFIGFILE} fi echo "" >> ${CONFIGFILE} echo "/* disable reading and parsing of config file (miniupnpd.conf) */" >> ${CONFIGFILE} echo "/*#define DISABLE_CONFIG_FILE*/" >> ${CONFIGFILE} echo "" >> ${CONFIGFILE} echo "#endif" >> ${CONFIGFILE} ${MV} ${CONFIGFILE} ${CONFIGFILE_FINAL} exit 0 miniupnpd-1.8.20130730/options.h010064400017500000024000000037571203340734000153210ustar00nanardstaff/* $Id: options.h,v 1.22 2012/09/27 15:47:15 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * author: Ryan Wagoner * (c) 2006-2012 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #ifndef OPTIONS_H_INCLUDED #define OPTIONS_H_INCLUDED #include "config.h" #ifndef DISABLE_CONFIG_FILE /* enum of option available in the miniupnpd.conf */ enum upnpconfigoptions { UPNP_INVALID = 0, UPNPEXT_IFNAME = 1, /* ext_ifname */ UPNPEXT_IP, /* ext_ip */ UPNPLISTENING_IP, /* listening_ip */ UPNPPORT, /* "port" */ UPNPBITRATE_UP, /* "bitrate_up" */ UPNPBITRATE_DOWN, /* "bitrate_down" */ UPNPPRESENTATIONURL, /* presentation_url */ UPNPFRIENDLY_NAME, /* "friendly_name" */ UPNPNOTIFY_INTERVAL, /* notify_interval */ UPNPSYSTEM_UPTIME, /* "system_uptime" */ UPNPPACKET_LOG, /* "packet_log" */ UPNPUUID, /* uuid */ UPNPSERIAL, /* serial */ UPNPMODEL_NUMBER, /* model_number */ UPNPCLEANTHRESHOLD, /* clean_ruleset_threshold */ UPNPCLEANINTERVAL, /* clean_ruleset_interval */ UPNPENABLENATPMP, /* enable_natpmp */ #ifdef USE_NETFILTER UPNPFORWARDCHAIN, UPNPNATCHAIN, #endif #ifdef USE_PF UPNPANCHOR, /* anchor */ UPNPQUEUE, /* queue */ UPNPTAG, /* tag */ #endif #ifdef PF_ENABLE_FILTER_RULES UPNPQUICKRULES, /* quickrules */ #endif UPNPSECUREMODE, /* secure_mode */ #ifdef ENABLE_LEASEFILE UPNPLEASEFILE, /* lease_file */ #endif UPNPMINISSDPDSOCKET, /* minissdpdsocket */ UPNPENABLE /* enable_upnp */ }; /* readoptionsfile() * parse and store the option file values * returns: 0 success, -1 failure */ int readoptionsfile(const char * fname); /* freeoptions() * frees memory allocated to option values */ void freeoptions(void); struct option { enum upnpconfigoptions id; const char * value; }; extern struct option * ary_options; extern unsigned int num_options; #endif /* DISABLE_CONFIG_FILE */ #endif /* OPTIONS_H_INCLUDED */ miniupnpd-1.8.20130730/options.c010064400017500000024000000127601177712566500153330ustar00nanardstaff/* $Id: options.c,v 1.26 2012/06/29 19:26:09 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * author: Ryan Wagoner * (c) 2006-2012 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #include #include #include #include #include "config.h" #include "options.h" #include "upnppermissions.h" #include "upnpglobalvars.h" #ifndef DISABLE_CONFIG_FILE struct option * ary_options = NULL; static char * string_repo = NULL; unsigned int num_options = 0; static const struct { enum upnpconfigoptions id; const char * name; } optionids[] = { { UPNPEXT_IFNAME, "ext_ifname" }, { UPNPEXT_IP, "ext_ip" }, { UPNPLISTENING_IP, "listening_ip" }, { UPNPPORT, "port" }, { UPNPBITRATE_UP, "bitrate_up" }, { UPNPBITRATE_DOWN, "bitrate_down" }, { UPNPPRESENTATIONURL, "presentation_url" }, { UPNPFRIENDLY_NAME, "friendly_name" }, { UPNPNOTIFY_INTERVAL, "notify_interval" }, { UPNPSYSTEM_UPTIME, "system_uptime" }, { UPNPPACKET_LOG, "packet_log" }, { UPNPUUID, "uuid"}, { UPNPSERIAL, "serial"}, { UPNPMODEL_NUMBER, "model_number"}, { UPNPCLEANTHRESHOLD, "clean_ruleset_threshold"}, { UPNPCLEANINTERVAL, "clean_ruleset_interval"}, #ifdef USE_NETFILTER { UPNPFORWARDCHAIN, "upnp_forward_chain"}, { UPNPNATCHAIN, "upnp_nat_chain"}, #endif #ifdef ENABLE_NATPMP { UPNPENABLENATPMP, "enable_natpmp"}, #endif { UPNPENABLE, "enable_upnp"}, #ifdef USE_PF { UPNPANCHOR, "anchor"}, { UPNPQUEUE, "queue"}, { UPNPTAG, "tag"}, #endif #ifdef PF_ENABLE_FILTER_RULES { UPNPQUICKRULES, "quickrules" }, #endif #ifdef ENABLE_LEASEFILE { UPNPLEASEFILE, "lease_file"}, #endif { UPNPMINISSDPDSOCKET, "minissdpdsocket"}, { UPNPSECUREMODE, "secure_mode"} }; int readoptionsfile(const char * fname) { FILE *hfile = NULL; char buffer[1024]; char *equals; char *name; char *value; char *t; int linenum = 0; unsigned int i; enum upnpconfigoptions id; size_t string_repo_len = 0; size_t len; void *tmp; if(!fname || (strlen(fname) == 0)) return -1; memset(buffer, 0, sizeof(buffer)); #ifdef DEBUG printf("Reading configuration from file %s\n", fname); #endif if(!(hfile = fopen(fname, "r"))) return -1; if(ary_options != NULL) { free(ary_options); num_options = 0; } while(fgets(buffer, sizeof(buffer), hfile)) { linenum++; t = strchr(buffer, '\n'); if(t) { *t = '\0'; t--; /* remove spaces at the end of the line */ while((t >= buffer) && isspace(*t)) { *t = '\0'; t--; } } /* skip leading whitespaces */ name = buffer; while(isspace(*name)) name++; /* check for comments or empty lines */ if(name[0] == '#' || name[0] == '\0') continue; /* check for UPnP permissions rule */ if(0 == memcmp(name, "allow", 5) || 0 == memcmp(name, "deny", 4)) { tmp = realloc(upnppermlist, sizeof(struct upnpperm) * (num_upnpperm+1)); if(tmp == NULL) { fprintf(stderr, "memory allocation error. Permission line in file %s line %d\n", fname, linenum); } else { upnppermlist = tmp; /* parse the rule */ if(read_permission_line(upnppermlist + num_upnpperm, name) >= 0) { num_upnpperm++; } else { fprintf(stderr, "parsing error file %s line %d : %s\n", fname, linenum, name); } } continue; } if(!(equals = strchr(name, '='))) { fprintf(stderr, "parsing error file %s line %d : %s\n", fname, linenum, name); continue; } /* remove ending whitespaces */ for(t=equals-1; t>name && isspace(*t); t--) *t = '\0'; *equals = '\0'; value = equals+1; /* skip leading whitespaces */ while(isspace(*value)) value++; id = UPNP_INVALID; for(i=0; i- or if there is only # one port in the range. # 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" 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 allow 12345 192.168.7.113/32 54321 deny 0-65535 0.0.0.0/0 0-65535 miniupnpd-1.8.20130730/upnppermissions.h010064400017500000024000000032171203340734000170730ustar00nanardstaff/* $Id: upnppermissions.h,v 1.9 2012/09/27 16:00:10 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #ifndef UPNPPERMISSIONS_H_INCLUDED #define UPNPPERMISSIONS_H_INCLUDED #include #include #include #include "config.h" /* UPnP permission rule samples: * allow 1024-65535 192.168.3.0/24 1024-65535 * deny 0-65535 192.168.1.125/32 0-65535 */ struct upnpperm { enum {UPNPPERM_ALLOW=1, UPNPPERM_DENY=2 } type; /* is it an allow or deny permission rule ? */ u_short eport_min, eport_max; /* external port range */ struct in_addr address, mask; /* ip/mask */ u_short iport_min, iport_max; /* internal port range */ }; /* read_permission_line() * returns: 0 line read okay * -1 error reading line * * line sample : * allow 1024-65535 192.168.3.0/24 1024-65535 * allow 22 192.168.4.33/32 22 * deny 0-65535 0.0.0.0/0 0-65535 */ int read_permission_line(struct upnpperm * perm, char * p); /* check_upnp_rule_against_permissions() * returns: 0 if the upnp rule should be rejected, * 1 if it could be accepted */ int check_upnp_rule_against_permissions(const struct upnpperm * permary, int n_perms, u_short eport, struct in_addr address, u_short iport); #ifdef USE_MINIUPNPDCTL void write_permlist(int fd, const struct upnpperm * permary, int nperms); #endif #endif miniupnpd-1.8.20130730/upnppermissions.c010064400017500000024000000120771172522177100171030ustar00nanardstaff/* $Id: upnppermissions.c,v 1.17 2012/02/15 22:43:34 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2012 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #include #include #include #include #include #include #include #include "config.h" #include "upnppermissions.h" /* read_permission_line() * parse the a permission line which format is : * (deny|allow) [0-9]+(-[0-9]+) ip/mask [0-9]+(-[0-9]+) * ip/mask is either 192.168.1.1/24 or 192.168.1.1/255.255.255.0 */ int read_permission_line(struct upnpperm * perm, char * p) { char * q; int n_bits; int i; /* first token: (allow|deny) */ while(isspace(*p)) p++; if(0 == memcmp(p, "allow", 5)) { perm->type = UPNPPERM_ALLOW; p += 5; } else if(0 == memcmp(p, "deny", 4)) { perm->type = UPNPPERM_DENY; p += 4; } else { return -1; } while(isspace(*p)) p++; /* second token: eport or eport_min-eport_max */ if(!isdigit(*p)) return -1; for(q = p; isdigit(*q); q++); if(*q=='-') { *q = '\0'; i = atoi(p); if(i > 65535) return -1; perm->eport_min = (u_short)i; q++; p = q; while(isdigit(*q)) q++; *q = '\0'; i = atoi(p); if(i > 65535) return -1; perm->eport_max = (u_short)i; if(perm->eport_min > perm->eport_max) return -1; } else if(isspace(*q)) { *q = '\0'; i = atoi(p); if(i > 65535) return -1; perm->eport_min = perm->eport_max = (u_short)i; } else { return -1; } p = q + 1; while(isspace(*p)) p++; /* third token: ip/mask */ if(!isdigit(*p)) return -1; for(q = p; isdigit(*q) || (*q == '.'); q++); if(*q=='/') { *q = '\0'; if(!inet_aton(p, &perm->address)) return -1; q++; p = q; while(isdigit(*q)) q++; if(*q == '.') { while(*q == '.' || isdigit(*q)) q++; if(!isspace(*q)) return -1; *q = '\0'; if(!inet_aton(p, &perm->mask)) return -1; } else if(!isspace(*q)) return -1; else { *q = '\0'; n_bits = atoi(p); if(n_bits > 32) return -1; perm->mask.s_addr = htonl(n_bits ? (0xffffffffu << (32 - n_bits)) : 0); } } else if(isspace(*q)) { *q = '\0'; if(!inet_aton(p, &perm->address)) return -1; perm->mask.s_addr = 0xffffffffu; } else { return -1; } p = q + 1; /* fourth token: iport or iport_min-iport_max */ while(isspace(*p)) p++; if(!isdigit(*p)) return -1; for(q = p; isdigit(*q); q++); if(*q=='-') { *q = '\0'; i = atoi(p); if(i > 65535) return -1; perm->iport_min = (u_short)i; q++; p = q; while(isdigit(*q)) q++; *q = '\0'; i = atoi(p); if(i > 65535) return -1; perm->iport_max = (u_short)i; if(perm->iport_min > perm->iport_max) return -1; } else if(isspace(*q) || *q == '\0') { *q = '\0'; i = atoi(p); if(i > 65535) return -1; perm->iport_min = perm->iport_max = (u_short)i; } else { return -1; } #ifdef DEBUG printf("perm rule added : %s %hu-%hu %08x/%08x %hu-%hu\n", (perm->type==UPNPPERM_ALLOW)?"allow":"deny", perm->eport_min, perm->eport_max, ntohl(perm->address.s_addr), ntohl(perm->mask.s_addr), perm->iport_min, perm->iport_max); #endif return 0; } #ifdef USE_MINIUPNPDCTL void write_permlist(int fd, const struct upnpperm * permary, int nperms) { int l; const struct upnpperm * perm; int i; char buf[128]; write(fd, "Permissions :\n", 14); for(i = 0; itype==UPNPPERM_ALLOW)?"allow":"deny", perm->eport_min, perm->eport_max, ntohl(perm->address.s_addr), ntohl(perm->mask.s_addr), perm->iport_min, perm->iport_max); if(l<0) return; write(fd, buf, l); } } #endif /* match_permission() * returns: 1 if eport, address, iport matches the permission rule * 0 if no match */ static int match_permission(const struct upnpperm * perm, u_short eport, struct in_addr address, u_short iport) { if( (eport < perm->eport_min) || (perm->eport_max < eport)) return 0; if( (iport < perm->iport_min) || (perm->iport_max < iport)) return 0; if( (address.s_addr & perm->mask.s_addr) != (perm->address.s_addr & perm->mask.s_addr) ) return 0; return 1; } int check_upnp_rule_against_permissions(const struct upnpperm * permary, int n_perms, u_short eport, struct in_addr address, u_short iport) { int i; for(i=0; i #include #include #include #include #include #include #include "upnppermissions.h" void print_upnpperm(const struct upnpperm * p) { switch(p->type) { case UPNPPERM_ALLOW: printf("allow "); break; case UPNPPERM_DENY: printf("deny "); break; default: printf("error ! "); } printf("%hu-%hu ", p->eport_min, p->eport_max); printf("%s/", inet_ntoa(p->address)); printf("%s ", inet_ntoa(p->mask)); printf("%hu-%hu", p->iport_min, p->iport_max); putchar('\n'); } int main(int argc, char * * argv) { int i, r; struct upnpperm p; if(argc < 2) { fprintf(stderr, "Usage: %s \"permission line\" [...]\n", argv[0]); fprintf(stderr, "Example: %s \"allow 1234 10.10.10.10/32 1234\"\n", argv[0]); return 1; } openlog("testupnppermissions", LOG_PERROR, LOG_USER); /* for(i=0; i #include #include #include #include #include #include #include #include #include #include "config.h" #include "upnpdescstrings.h" #include "miniupnpdpath.h" #include "upnphttp.h" #include "upnpglobalvars.h" #include "minissdp.h" #include "upnputils.h" #include "getroute.h" #include "codelength.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" /* AddMulticastMembership() * param s socket * param ifaddr ip v4 address */ static int AddMulticastMembership(int s, in_addr_t ifaddr) { struct ip_mreq imr; /* Ip multicast membership */ /* 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 = ifaddr; /*inet_addr(ifaddr);*/ if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&imr, sizeof(struct ip_mreq)) < 0) { syslog(LOG_ERR, "setsockopt(udp, IP_ADD_MEMBERSHIP): %m"); return -1; } return 0; } /* AddMulticastMembershipIPv6() * param s socket (IPv6) * To be improved to target specific network interfaces */ #ifdef ENABLE_IPV6 static int AddMulticastMembershipIPv6(int s) { 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 */ #ifndef IPV6_ADD_MEMBERSHIP #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP #endif 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; } inet_pton(AF_INET6, SL_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 /* Open and configure the socket listening for * SSDP udp packets sent on 239.255.255.250 port 1900 * SSDP v6 udp packets sent on FF02::C, or FF05::C, port 1900 */ int OpenAndConfSSDPReceiveSocket(int ipv6) { int s; struct sockaddr_storage sockname; socklen_t sockname_len; struct lan_addr_s * lan_addr; int j = 1; if( (s = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, 0)) < 0) { syslog(LOG_ERR, "%s: socket(udp): %m", "OpenAndConfSSDPReceiveSocket"); return -1; } memset(&sockname, 0, sizeof(struct sockaddr_storage)); 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; sockname_len = sizeof(struct sockaddr_in6); } else { struct sockaddr_in * saddr = (struct sockaddr_in *)&sockname; saddr->sin_family = AF_INET; saddr->sin_port = htons(SSDP_PORT); /* NOTE : it seems it doesnt work when binding on the specific address */ /*saddr->sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);*/ saddr->sin_addr.s_addr = htonl(INADDR_ANY); /*saddr->sin_addr.s_addr = inet_addr(ifaddr);*/ sockname_len = sizeof(struct sockaddr_in); } if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &j, sizeof(j)) < 0) { syslog(LOG_WARNING, "setsockopt(udp, SO_REUSEADDR): %m"); } if(!set_non_blocking(s)) { syslog(LOG_WARNING, "%s: set_non_blocking(): %m", "OpenAndConfSSDPReceiveSocket"); } if(bind(s, (struct sockaddr *)&sockname, sockname_len) < 0) { syslog(LOG_ERR, "%s: bind(udp%s): %m", "OpenAndConfSSDPReceiveSocket", ipv6 ? "6" : ""); close(s); return -1; } #ifdef ENABLE_IPV6 if(ipv6) { AddMulticastMembershipIPv6(s); } else #endif { for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next) { if(AddMulticastMembership(s, lan_addr->addr.s_addr) < 0) { syslog(LOG_WARNING, "Failed to add multicast membership for interface %s", lan_addr->str ? lan_addr->str : "NULL"); } } } return s; } /* open the UDP socket used to send SSDP notifications to * the multicast group reserved for them */ static int OpenAndConfSSDPNotifySocket(in_addr_t addr) { int s; unsigned char loopchar = 0; int bcast = 1; unsigned char ttl = 2; /* UDA v1.1 says : The TTL for the IP packet SHOULD default to 2 and SHOULD be configurable. */ /* TODO: Make TTL be configurable */ struct in_addr mc_if; struct sockaddr_in sockname; if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { syslog(LOG_ERR, "socket(udp_notify): %m"); return -1; } mc_if.s_addr = addr; /*inet_addr(addr);*/ if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loopchar, sizeof(loopchar)) < 0) { syslog(LOG_ERR, "setsockopt(udp_notify, IP_MULTICAST_LOOP): %m"); close(s); return -1; } if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&mc_if, sizeof(mc_if)) < 0) { syslog(LOG_ERR, "setsockopt(udp_notify, IP_MULTICAST_IF): %m"); close(s); return -1; } if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) { syslog(LOG_WARNING, "setsockopt(udp_notify, IP_MULTICAST_TTL,): %m"); } if(setsockopt(s, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bcast)) < 0) { syslog(LOG_ERR, "setsockopt(udp_notify, SO_BROADCAST): %m"); close(s); return -1; } memset(&sockname, 0, sizeof(struct sockaddr_in)); sockname.sin_family = AF_INET; sockname.sin_addr.s_addr = addr; /*inet_addr(addr);*/ if (bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0) { syslog(LOG_ERR, "bind(udp_notify): %m"); close(s); return -1; } return s; } #ifdef ENABLE_IPV6 /* open the UDP socket used to send SSDP notifications to * the multicast group reserved for them. IPv6 */ static int OpenAndConfSSDPNotifySocketIPv6(unsigned int if_index) { int s; unsigned int loop = 0; s = socket(PF_INET6, SOCK_DGRAM, 0); if(s < 0) { syslog(LOG_ERR, "socket(udp_notify IPv6): %m"); return -1; } if(setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &if_index, sizeof(if_index)) < 0) { syslog(LOG_ERR, "setsockopt(udp_notify IPv6, IPV6_MULTICAST_IF, %u): %m", if_index); close(s); return -1; } if(setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof(loop)) < 0) { syslog(LOG_ERR, "setsockopt(udp_notify, IPV6_MULTICAST_LOOP): %m"); close(s); return -1; } return s; } #endif int OpenAndConfSSDPNotifySockets(int * sockets) /*OpenAndConfSSDPNotifySockets(int * sockets, struct lan_addr_s * lan_addr, int n_lan_addr)*/ { int i; struct lan_addr_s * lan_addr; for(i=0, lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next) { sockets[i] = OpenAndConfSSDPNotifySocket(lan_addr->addr.s_addr); if(sockets[i] < 0) goto error; i++; #ifdef ENABLE_IPV6 sockets[i] = OpenAndConfSSDPNotifySocketIPv6(lan_addr->index); if(sockets[i] < 0) goto error; i++; #endif } return 0; error: while(--i >= 0) { close(sockets[i]); sockets[i] = -1; } return -1; } /* * response from a LiveBox (Wanadoo) HTTP/1.1 200 OK CACHE-CONTROL: max-age=1800 DATE: Thu, 01 Jan 1970 04:03:23 GMT EXT: LOCATION: http://192.168.0.1:49152/gatedesc.xml SERVER: Linux/2.4.17, UPnP/1.0, Intel SDK for UPnP devices /1.2 ST: upnp:rootdevice USN: uuid:75802409-bccb-40e7-8e6c-fa095ecce13e::upnp:rootdevice * response from a Linksys 802.11b : HTTP/1.1 200 OK Cache-Control:max-age=120 Location:http://192.168.5.1:5678/rootDesc.xml Server:NT/5.0 UPnP/1.0 ST:upnp:rootdevice USN:uuid:upnp-InternetGatewayDevice-1_0-0090a2777777::upnp:rootdevice EXT: */ /* Responds to a SSDP "M-SEARCH" * s : socket to use * addr : peer * st, st_len : ST: header * suffix : suffix for USN: header * host, port : our HTTP host, port */ 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) { int l, n; char buf[512]; char addr_str[64]; socklen_t addrlen; int st_is_uuid; #ifdef ENABLE_HTTP_DATE char http_date[64]; time_t t; struct tm tm; time(&t); gmtime_r(&t, &tm); strftime(http_date, sizeof(http_date), "%a, %d %b %Y %H:%M:%S GMT", &tm); #endif st_is_uuid = (st_len == (int)strlen(uuidvalue)) && (memcmp(uuidvalue, st, st_len) == 0); /* * follow guideline from document "UPnP Device Architecture 1.0" * uppercase is recommended. * DATE: is recommended * SERVER: OS/ver UPnP/1.0 miniupnpd/1.0 * - check what to put in the 'Cache-Control' header * * have a look at the document "UPnP Device Architecture v1.1 */ l = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\n" "CACHE-CONTROL: max-age=120\r\n" #ifdef ENABLE_HTTP_DATE "DATE: %s\r\n" #endif "ST: %.*s%s\r\n" "USN: %s%s%.*s%s\r\n" "EXT:\r\n" "SERVER: " MINIUPNPD_SERVER_STRING "\r\n" "LOCATION: http://%s:%u" ROOTDESC_PATH "\r\n" "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 */ "CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */ "\r\n", #ifdef ENABLE_HTTP_DATE http_date, #endif st_len, st, suffix, uuidvalue, st_is_uuid ? "" : "::", st_is_uuid ? 0 : st_len, st, suffix, host, (unsigned int)port, upnp_bootid, upnp_bootid, upnp_configid); if(l<0) { syslog(LOG_ERR, "%s: snprintf failed %m", "SendSSDPResponse()"); return; } else if((unsigned)l>=sizeof(buf)) { syslog(LOG_WARNING, "%s: truncated output", "SendSSDPResponse()"); l = sizeof(buf) - 1; } addrlen = (addr->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); n = sendto(s, buf, l, 0, addr, addrlen); sockaddr_to_string(addr, addr_str, sizeof(addr_str)); syslog(LOG_INFO, "SSDP Announce %d bytes to %s ST: %.*s",n, addr_str, l, buf); if(n < 0) { /* XXX handle EINTR, EAGAIN, EWOULDBLOCK */ syslog(LOG_ERR, "sendto(udp): %m"); } } #ifndef IGD_V2 #define IGD_VER 1 #define WANIPC_VER 1 #else #define IGD_VER 2 #define WANIPC_VER 2 #endif static struct { const char * s; const int version; const char * uuid; } const known_service_types[] = { {"upnp:rootdevice", 0, uuidvalue_igd}, {"urn:schemas-upnp-org:device:InternetGatewayDevice:", IGD_VER, uuidvalue_igd}, {"urn:schemas-upnp-org:device:WANConnectionDevice:", 1, uuidvalue_wcd}, {"urn:schemas-upnp-org:device:WANDevice:", 1, uuidvalue_wan}, {"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:", 1, uuidvalue_wan}, {"urn:schemas-upnp-org:service:WANIPConnection:", WANIPC_VER, uuidvalue_wcd}, #ifndef UPNP_STRICT /* We use WAN IP Connection, not PPP connection, * but buggy control points may try to use WanPPPConnection * anyway */ {"urn:schemas-upnp-org:service:WANPPPConnection:", 1, uuidvalue_wcd}, #endif #ifdef ENABLE_L3F_SERVICE {"urn:schemas-upnp-org:service:Layer3Forwarding:", 1, uuidvalue_igd}, #endif #ifdef ENABLE_6FC_SERVICE {"url:schemas-upnp-org:service:WANIPv6FirewallControl:", 1, uuidvalue_wcd}, #endif {0, 0, 0} }; static void SendSSDPNotify(int s, const struct sockaddr * dest, const char * host, unsigned short port, const char * nt, const char * suffix, const char * usn1, const char * usn2, const char * usn3, unsigned int lifetime, int ipv6) { char bufr[512]; 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" "SERVER: " MINIUPNPD_SERVER_STRING "\r\n" "NT: %s%s\r\n" "USN: %s%s%s%s\r\n" "NTS: ssdp:alive\r\n" "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */ "01-NLS: %u\r\n" /* same as BOOTID field. UDA v1.1 */ "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"); return; } else if((unsigned int)l >= sizeof(bufr)) { syslog(LOG_WARNING, "SendSSDPNotify(): truncated output"); l = sizeof(bufr) - 1; } n = sendto(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) { /* XXX handle EINTR, EAGAIN, EWOULDBLOCK */ syslog(LOG_ERR, "sendto(udp_notify=%d, %s): %m", s, host ? host : "NULL"); } else if(n != l) { syslog(LOG_NOTICE, "sendto() sent %d out of %d bytes", n, l); } } static void SendSSDPNotifies(int s, const char * host, unsigned short port, unsigned int lifetime, int ipv6) { #ifdef ENABLE_IPV6 struct sockaddr_storage sockname; #else struct sockaddr_in sockname; #endif int i=0; char ver_str[4]; 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)) { SendSSDPNotify(s, (struct sockaddr *)&sockname, host, port, known_service_types[i].uuid, "", /* NT: */ known_service_types[i].uuid, "", "", /* ver_str, USN: */ lifetime, ipv6); } i++; } } void SendSSDPNotifies2(int * sockets, unsigned short port, unsigned int lifetime) { int i; struct lan_addr_s * lan_addr; 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, lifetime, 0); i++; #ifdef ENABLE_IPV6 SendSSDPNotifies(sockets[i], ipv6_addr_for_http_with_brackets, port, lifetime, 1); i++; #endif } } /* ProcessSSDPRequest() * process SSDP M-SEARCH requests and responds to them */ void ProcessSSDPRequest(int s, unsigned short port) { int n; char bufr[1500]; socklen_t len_r; #ifdef ENABLE_IPV6 struct sockaddr_storage sendername; len_r = sizeof(struct sockaddr_storage); #else struct sockaddr_in sendername; len_r = sizeof(struct sockaddr_in); #endif n = recvfrom(s, bufr, sizeof(bufr), 0, (struct sockaddr *)&sendername, &len_r); if(n < 0) { /* EAGAIN, EWOULDBLOCK, EINTR : silently ignore (try again next time) * other errors : log to LOG_ERR */ if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) { syslog(LOG_ERR, "recvfrom(udp): %m"); } return; } ProcessSSDPData(s, bufr, n, (struct sockaddr *)&sendername, port); } void ProcessSSDPData(int s, const char *bufr, int n, const struct sockaddr * sender, unsigned short port) { int i, l; struct lan_addr_s * lan_addr = NULL; const char * st = NULL; int st_len = 0; int st_ver = 0; char sender_str[64]; char ver_str[4]; const char * announced_host = NULL; #ifdef UPNP_STRICT #ifdef ENABLE_IPV6 char announced_host_buf[64]; #endif int mx_value = -1; #endif /* get the string representation of the sender address */ sockaddr_to_string(sender, sender_str, sizeof(sender_str)); lan_addr = get_lan_for_peer(sender); if(lan_addr == NULL) { syslog(LOG_WARNING, "SSDP packet sender %s not from a LAN, ignoring", sender_str); return; } if(memcmp(bufr, "NOTIFY", 6) == 0) { /* ignore NOTIFY packets. We could log the sender and device type */ return; } else if(memcmp(bufr, "M-SEARCH", 8) == 0) { i = 0; while(i < n) { while((i < n - 1) && (bufr[i] != '\r' || bufr[i+1] != '\n')) i++; i += 2; if((i < n - 3) && (strncasecmp(bufr+i, "st:", 3) == 0)) { st = bufr+i+3; st_len = 0; while((*st == ' ' || *st == '\t') && (st < bufr + n)) st++; while(st[st_len]!='\r' && st[st_len]!='\n' && (st + st_len < bufr + n)) st_len++; l = st_len; while(l > 0 && st[l-1] != ':') l--; st_ver = atoi(st+l); syslog(LOG_DEBUG, "ST: %.*s (ver=%d)", st_len, st, st_ver); /*j = 0;*/ /*while(bufr[i+j]!='\r') j++;*/ /*syslog(LOG_INFO, "%.*s", j, bufr+i);*/ } #ifdef UPNP_STRICT else if((i < n - 3) && (strncasecmp(bufr+i, "mx:", 3) == 0)) { const char * mx; int mx_len; mx = bufr+i+3; mx_len = 0; while((*mx == ' ' || *mx == '\t') && (mx < bufr + n)) mx++; while(mx[mx_len]!='\r' && mx[mx_len]!='\n' && (mx + mx_len < bufr + n)) mx_len++; mx_value = atoi(mx); syslog(LOG_DEBUG, "MX: %.*s (value=%d)", mx_len, mx, mx_value); } #endif } #ifdef UPNP_STRICT if(mx_value < 0) { syslog(LOG_INFO, "ignoring SSDP packet missing MX: header"); return; } #endif /*syslog(LOG_INFO, "SSDP M-SEARCH packet received from %s", sender_str );*/ if(st && (st_len > 0)) { /* TODO : doesnt answer at once but wait for a random time */ syslog(LOG_INFO, "SSDP M-SEARCH from %s ST: %.*s", sender_str, st_len, st); /* find in which sub network the client is */ if(sender->sa_family == AF_INET) { if (lan_addr == NULL) { syslog(LOG_ERR, "Can't find in which sub network the client is"); return; } announced_host = lan_addr->str; } #ifdef ENABLE_IPV6 else { /* IPv6 address with brackets */ #ifdef UPNP_STRICT int index; struct in6_addr addr6; size_t addr6_len = sizeof(addr6); /* retrieve the IPv6 address which * will be used locally to reach sender */ memset(&addr6, 0, sizeof(addr6)); if(get_src_for_route_to (sender, &addr6, &addr6_len, &index) < 0) { syslog(LOG_WARNING, "get_src_for_route_to() failed, using %s", ipv6_addr_for_http_with_brackets); announced_host = ipv6_addr_for_http_with_brackets; } else { if(inet_ntop(AF_INET6, &addr6, announced_host_buf+1, sizeof(announced_host_buf) - 2)) { announced_host_buf[0] = '['; i = strlen(announced_host_buf); if(i < (int)sizeof(announced_host_buf) - 1) { announced_host_buf[i] = ']'; announced_host_buf[i+1] = '\0'; } else { syslog(LOG_NOTICE, "cannot suffix %s with ']'", announced_host_buf); } announced_host = announced_host_buf; } else { syslog(LOG_NOTICE, "inet_ntop() failed %m"); announced_host = ipv6_addr_for_http_with_brackets; } } #else announced_host = ipv6_addr_for_http_with_brackets; #endif } #endif /* Responds to request with a device as ST header */ for(i = 0; known_service_types[i].s; i++) { l = (int)strlen(known_service_types[i].s); if(l<=st_len && (0 == memcmp(st, known_service_types[i].s, l)) #ifdef UPNP_STRICT && (st_ver <= known_service_types[i].version) /* only answer for service version lower or equal of supported one */ #endif ) { syslog(LOG_INFO, "Single search found"); SendSSDPResponse(s, sender, st, st_len, "", announced_host, port, known_service_types[i].uuid); break; } } /* Responds to request with ST: ssdp:all */ /* strlen("ssdp:all") == 8 */ if(st_len==8 && (0 == memcmp(st, "ssdp:all", 8))) { syslog(LOG_INFO, "ssdp:all found"); 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); l = (int)strlen(known_service_types[i].s); SendSSDPResponse(s, sender, known_service_types[i].s, l, ver_str, announced_host, port, known_service_types[i].uuid); } /* also answer for uuid */ SendSSDPResponse(s, sender, uuidvalue_igd, strlen(uuidvalue_igd), "", announced_host, port, uuidvalue_igd); SendSSDPResponse(s, sender, uuidvalue_wan, strlen(uuidvalue_wan), "", announced_host, port, uuidvalue_wan); SendSSDPResponse(s, sender, uuidvalue_wcd, strlen(uuidvalue_wcd), "", announced_host, port, uuidvalue_wcd); } /* responds to request by UUID value */ l = (int)strlen(uuidvalue_igd); if(l==st_len) { if(0 == memcmp(st, uuidvalue_igd, l)) { syslog(LOG_INFO, "ssdp:uuid (IGD) found"); SendSSDPResponse(s, sender, st, st_len, "", announced_host, port, uuidvalue_igd); } 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); } 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); } } } else { syslog(LOG_INFO, "Invalid SSDP M-SEARCH from %s", sender_str); } } else { syslog(LOG_NOTICE, "Unknown udp packet received from %s", sender_str); } } static int SendSSDPbyebye(int s, const struct sockaddr * dest, const char * nt, const char * suffix, const char * usn1, const char * usn2, const char * usn3, int ipv6) { int n, l; char bufr[512]; l = snprintf(bufr, sizeof(bufr), "NOTIFY * HTTP/1.1\r\n" "HOST: %s:%d\r\n" "NT: %s%s\r\n" "USN: %s%s%s%s\r\n" "NTS: ssdp:byebye\r\n" "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */ "01-NLS: %u\r\n" /* same as BOOTID field. UDA v1.1 */ "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: */ usn1, usn2, usn3, suffix, /* USN: */ upnp_bootid, upnp_bootid, upnp_configid); if(l<0) { syslog(LOG_ERR, "SendSSDPbyebye() snprintf error"); return -1; } else if((unsigned int)l >= sizeof(bufr)) { syslog(LOG_WARNING, "SendSSDPbyebye(): truncated output"); l = sizeof(bufr) - 1; } n = sendto(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) { syslog(LOG_ERR, "sendto(udp_shutdown=%d): %m", s); return -1; } else if(n != l) { syslog(LOG_NOTICE, "sendto() sent %d out of %d bytes", n, l); return -1; } return 0; } /* This will broadcast ssdp:byebye notifications to inform * the network that UPnP is going down. */ int SendSSDPGoodbye(int * sockets, int n_sockets) { struct sockaddr_in sockname; #ifdef ENABLE_IPV6 struct sockaddr_in6 sockname6; #endif int i, j; char ver_str[4]; int ret = 0; int ipv6 = 0; 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); #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)); #endif for(j=0; j 0) l++; CODELENGTH(l, p); memcpy(p, known_service_types[i].s, l); if(i > 0) p[l-1] = '1'; p += l; if(i==0) ver_str[0] = '\0'; else snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version); l = snprintf(strbuf, sizeof(strbuf), "%s::%s%s", known_service_types[i].uuid, known_service_types[i].s, ver_str); if(l<0) { syslog(LOG_WARNING, "SubmitServicesToMiniSSDPD: snprintf %m"); continue; } else if((unsigned)l>=sizeof(strbuf)) { l = sizeof(strbuf) - 1; } CODELENGTH(l, p); memcpy(p, strbuf, l); p += l; l = (int)strlen(MINIUPNPD_SERVER_STRING); CODELENGTH(l, p); memcpy(p, MINIUPNPD_SERVER_STRING, l); p += l; l = snprintf(strbuf, sizeof(strbuf), "http://%s:%u" ROOTDESC_PATH, host, (unsigned int)port); if(l<0) { syslog(LOG_WARNING, "SubmitServicesToMiniSSDPD: snprintf %m"); continue; } else if((unsigned)l>=sizeof(strbuf)) { l = sizeof(strbuf) - 1; } CODELENGTH(l, p); memcpy(p, strbuf, l); p += l; /* now write the encoded data */ n = p - buffer; /* bytes to send */ p = buffer; /* start */ while(n > 0) { l = write(s, p, n); if (l < 0) { syslog(LOG_ERR, "write(): %m"); close(s); return -1; } else if (l == 0) { syslog(LOG_ERR, "write() returned 0"); close(s); return -1; } p += l; n -= l; } } close(s); return 0; } miniupnpd-1.8.20130730/miniupnpdctl.c010064400017500000024000000030061174772426300163330ustar00nanardstaff/* $Id: miniupnpdctl.c,v 1.10 2012/04/30 21:08:00 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2012 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #include #include #include #include #include #include #include #include "macros.h" #if 0 static void sighandler(int sig) { printf("received signal %d\n", sig); } #endif int main(int argc, char * * argv) { /*char test[] = "test!";*/ static const char command[] = "show all\n"; char buf[256]; int l; int s; struct sockaddr_un addr; UNUSED(argc); UNUSED(argv); /*signal(SIGINT, sighandler);*/ s = socket(AF_UNIX, SOCK_STREAM, 0); if(s<0) { perror("socket"); return 1; } addr.sun_family = AF_UNIX; strncpy(addr.sun_path, "/var/run/miniupnpd.ctl", sizeof(addr.sun_path)); if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) { perror("connect"); close(s); return 1; } printf("Connected.\n"); if(write(s, command, sizeof(command)) < 0) { perror("write"); close(s); return 1; } for(;;) { l = read(s, buf, sizeof(buf)); if(l<0) { perror("read"); break; } if(l==0) break; /*printf("%d bytes read\n", l);*/ fflush(stdout); if(write(fileno(stdout), buf, l) < 0) { perror("error writing to stdout"); } /*printf("\n");*/ } close(s); return 0; } miniupnpd-1.8.20130730/linux/miniupnpd.init.d.script010064400017500000024000000031501173213203200212120ustar00nanardstaff#!/bin/sh # $Id: miniupnpd.init.d.script,v 1.3 2012/03/14 22:09:53 nanard Exp $ # MiniUPnP project # author: Thomas Bernard # website: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ ### BEGIN INIT INFO # Provides: miniupnpd # Required-Start: $remote_fs $syslog # Required-Stop: $remote_fs $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: MiniUPnPd port-forwarding daemon ### END INIT INFO set -e MINIUPNPD=/usr/sbin/miniupnpd ARGS='-f /etc/miniupnpd/miniupnpd.conf' IPTABLES_CREATE=/etc/miniupnpd/iptables_init.sh IPTABLES_REMOVE=/etc/miniupnpd/iptables_removeall.sh test -f $MINIUPNPD || exit 0 . /lib/lsb/init-functions case "$1" in start) log_daemon_msg "Starting miniupnpd" "miniupnpd" $IPTABLES_CREATE > /dev/null 2>&1 start-stop-daemon --start --quiet --pidfile /var/run/miniupnpd.pid --startas $MINIUPNPD -- $ARGS $LSBNAMES log_end_msg $? ;; stop) log_daemon_msg "Stopping miniupnpd" "miniupnpd" start-stop-daemon --stop --quiet --pidfile /var/run/miniupnpd.pid log_end_msg $? $IPTABLES_REMOVE > /dev/null 2>&1 ;; restart|reload|force-reload) log_daemon_msg "Restarting miniupnpd" "miniupnpd" start-stop-daemon --stop --retry 5 --quiet --pidfile /var/run/miniupnpd.pid $IPTABLES_REMOVE > /dev/null 2>&1 $IPTABLES_CREATE > /dev/null 2>&1 start-stop-daemon --start --quiet --pidfile /var/run/miniupnpd.pid --startas $MINIUPNPD -- $ARGS $LSBNAMES log_end_msg $? ;; status) status_of_proc /usr/sbin/miniupnpd miniupnpd ;; *) log_action_msg "Usage: /etc/init.d/miniupnpd {start|stop|restart|reload|force-reload}" exit 2 ;; esac exit 0 miniupnpd-1.8.20130730/ipf/ipfrdr.c010064400017500000024000000440051214626452300156650ustar00nanardstaff/* $Id: ipfrdr.c,v 1.16 2013/05/20 00:07:47 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2007 Darren Reed * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #include #include /* * This is a workaround for troubles on FreeBSD, HPUX, OpenBSD. * Needed here because on some systems gets included by things * like */ #ifndef _KERNEL # define ADD_KERNEL # define _KERNEL # define KERNEL #endif #ifdef __OpenBSD__ struct file; #endif #include #ifdef ADD_KERNEL # undef _KERNEL # undef KERNEL #endif #include #include #include #include #include #if __FreeBSD_version >= 300000 # include #endif #include #include #include #include #ifndef TCP_PAWS_IDLE /* IRIX */ # include #endif #include #include #include #include #include #include #include #include #include #include #if !defined(__SVR4) && !defined(__svr4__) && defined(sun) # include #endif #include #include #include "../config.h" #include "netinet/ipl.h" #include "netinet/ip_compat.h" #include "netinet/ip_fil.h" #include "netinet/ip_nat.h" #include "netinet/ip_state.h" #ifndef __P # ifdef __STDC__ # define __P(x) x # else # define __P(x) () # endif #endif #ifndef __STDC__ # undef const # define const #endif #ifndef U_32_T # define U_32_T 1 # if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) || \ defined(__sgi) typedef u_int32_t u_32_t; # else # if defined(__alpha__) || defined(__alpha) || defined(_LP64) typedef unsigned int u_32_t; # else # if SOLARIS2 >= 6 typedef uint32_t u_32_t; # else typedef unsigned int u_32_t; # endif # endif # endif /* __NetBSD__ || __OpenBSD__ || __FreeBSD__ || __sgi */ #endif /* U_32_T */ #if defined(__NetBSD__) || defined(__OpenBSD__) || \ (_BSDI_VERSION >= 199701) || (__FreeBSD_version >= 300000) || \ SOLARIS || defined(__sgi) || defined(__osf__) || defined(linux) # include typedef int (* ioctlfunc_t) __P((int, ioctlcmd_t, ...)); #else typedef int (* ioctlfunc_t) __P((dev_t, ioctlcmd_t, void *)); #endif typedef void (* addfunc_t) __P((int, ioctlfunc_t, void *)); typedef int (* copyfunc_t) __P((void *, void *, size_t)); /* * SunOS4 */ #if defined(sun) && !defined(__SVR4) && !defined(__svr4__) extern int ioctl __P((int, int, void *)); #endif #include "../upnpglobalvars.h" /* group name */ static const char group_name[] = "miniupnpd"; static int dev = -1; static int dev_ipl = -1; /* IPFilter cannot store redirection descriptions, so we use our * own structure to store them */ struct rdr_desc { struct rdr_desc * next; unsigned short eport; int proto; unsigned int timestamp; char str[]; }; /* pointer to the chained list where descriptions are stored */ static struct rdr_desc * rdr_desc_list; static void add_redirect_desc(unsigned short eport, int proto, unsigned int timestamp, const char * desc) { struct rdr_desc * p; size_t l; if (desc != NULL) { l = strlen(desc) + 1; p = malloc(sizeof(struct rdr_desc) + l); if (p) { p->next = rdr_desc_list; p->eport = eport; p->proto = proto; p->timestamp = timestamp; memcpy(p->str, desc, l); rdr_desc_list = p; } } } static void del_redirect_desc(unsigned short eport, int proto) { struct rdr_desc * p, * last; last = NULL; for (p = rdr_desc_list; p; p = p->next) { if(p->eport == eport && p->proto == proto) { if (last == NULL) rdr_desc_list = p->next; else last->next = p->next; free(p); return; } } } static void get_redirect_desc(unsigned short eport, int proto, char * desc, int desclen, unsigned int * timestamp) { struct rdr_desc * p; if (desc == NULL || desclen == 0) return; for (p = rdr_desc_list; p; p = p->next) { if (p->eport == eport && p->proto == proto) { strncpy(desc, p->str, desclen); *timestamp = p->timestamp; return; } } return; } int init_redirect(void) { dev = open(IPNAT_NAME, O_RDWR); if (dev < 0) { syslog(LOG_ERR, "open(\"%s\"): %m", IPNAT_NAME); return -1; } dev_ipl = open(IPL_NAME, O_RDWR); if (dev_ipl < 0) { syslog(LOG_ERR, "open(\"%s\"): %m", IPL_NAME); return -1; } return 0; } void shutdown_redirect(void) { if (dev >= 0) { close(dev); dev = -1; } if (dev_ipl >= 0) { close(dev_ipl); dev = -1; } return; } int add_redirect_rule2(const char * ifname, const char * rhost, unsigned short eport, const char * iaddr, unsigned short iport, int proto, const char * desc, unsigned int timestamp) { struct ipnat ipnat; struct ipfobj obj; int r; if (dev < 0) { syslog(LOG_ERR, "%s not open", IPNAT_NAME); return -1; } memset(&obj, 0, sizeof(obj)); memset(&ipnat, 0, sizeof(ipnat)); ipnat.in_redir = NAT_REDIRECT; #if IPFILTER_VERSION >= 5000000 ipnat.in_pr[0] = proto; ipnat.in_pr[1] = proto; #else ipnat.in_p = proto; #endif if (proto == IPPROTO_TCP) ipnat.in_flags = IPN_TCP; if (proto == IPPROTO_UDP) ipnat.in_flags = IPN_UDP; ipnat.in_dcmp = FR_EQUAL; #if IPFILTER_VERSION >= 5000000 ipnat.in_dpmin = htons(eport); ipnat.in_dpmax = htons(eport); ipnat.in_dpnext = htons(iport); ipnat.in_v[0] = 4; ipnat.in_v[1] = 4; #else ipnat.in_pmin = htons(eport); ipnat.in_pmax = htons(eport); ipnat.in_pnext = htons(iport); ipnat.in_v = 4; #endif strlcpy(ipnat.in_tag.ipt_tag, group_name, IPFTAG_LEN); #ifdef USE_IFNAME_IN_RULES if (ifname) { #if IPFILTER_VERSION >= 5000000 /* XXX check for stack overflow ! */ ipnat.in_ifnames[0] = 0; ipnat.in_ifnames[1] = 0; strlcpy(ipnat.in_names, ifname, IFNAMSIZ); ipnat.in_namelen = strlen(ipnat.in_names) + 1; #else strlcpy(ipnat.in_ifnames[0], ifname, IFNAMSIZ); strlcpy(ipnat.in_ifnames[1], ifname, IFNAMSIZ); #endif } #endif if(rhost && rhost[0] != '\0' && rhost[0] != '*') { #if IPFILTER_VERSION >= 5000000 inet_pton(AF_INET, rhost, &ipnat.in_nsrc.na_addr[0].in4); /* in_nsrcip */ ipnat.in_nsrc.na_addr[1].in4.s_addr = 0xffffffff; /* in_nsrcmsk */ #else inet_pton(AF_INET, rhost, &ipnat.in_src[0].in4); ipnat.in_src[1].in4.s_addr = 0xffffffff; #endif } #if IPFILTER_VERSION >= 5000000 inet_pton(AF_INET, iaddr, &ipnat.in_ndst.na_addr[0].in4); /* in_ndstip */ ipnat.in_ndst.na_addr[1].in4.s_addr = 0xffffffff; /* in_ndstmsk */ #else inet_pton(AF_INET, iaddr, &ipnat.in_in[0].in4); ipnat.in_in[1].in4.s_addr = 0xffffffff; #endif obj.ipfo_rev = IPFILTER_VERSION; obj.ipfo_size = sizeof(ipnat); obj.ipfo_ptr = &ipnat; obj.ipfo_type = IPFOBJ_IPNAT; r = ioctl(dev, SIOCADNAT, &obj); if (r == -1) syslog(LOG_ERR, "ioctl(SIOCADNAT): %m"); else add_redirect_desc(eport, proto, timestamp, desc); return r; } /* get_redirect_rule() * return value : 0 success (found) * -1 = error or rule not found */ int get_redirect_rule(const char * ifname, unsigned short eport, int proto, char * iaddr, int iaddrlen, unsigned short * iport, char * desc, int desclen, char * rhost, int rhostlen, unsigned int * timestamp, u_int64_t * packets, u_int64_t * bytes) { ipfgeniter_t iter; ipfobj_t obj; ipnat_t ipn; int r; memset(&obj, 0, sizeof(obj)); obj.ipfo_rev = IPFILTER_VERSION; obj.ipfo_type = IPFOBJ_GENITER; obj.ipfo_size = sizeof(iter); obj.ipfo_ptr = &iter; iter.igi_type = IPFGENITER_IPNAT; #if IPFILTER_VERSION > 4011300 iter.igi_nitems = 1; #endif iter.igi_data = &ipn; if (dev < 0) { syslog(LOG_ERR, "%s not open", IPNAT_NAME); return -1; } r = -1; do { if (ioctl(dev, SIOCGENITER, &obj) == -1) { syslog(LOG_ERR, "ioctl(dev, SIOCGENITER): %m"); break; } #if IPFILTER_VERSION >= 5000000 if (eport == ntohs(ipn.in_dpmin) && eport == ntohs(ipn.in_dpmax) && strcmp(ipn.in_tag.ipt_tag, group_name) == 0 && ipn.in_pr[0] == proto) #else if (eport == ntohs(ipn.in_pmin) && eport == ntohs(ipn.in_pmax) && strcmp(ipn.in_tag.ipt_tag, group_name) == 0 && ipn.in_p == proto) #endif { strlcpy(desc, "", desclen); if (packets != NULL) *packets = 0; if (bytes != NULL) *bytes = 0; if (iport != NULL) #if IPFILTER_VERSION >= 5000000 *iport = ntohs(ipn.in_dpnext); #else *iport = ntohs(ipn.in_pnext); #endif if ((desc != NULL) && (timestamp != NULL)) get_redirect_desc(eport, proto, desc, desclen, timestamp); if ((rhost != NULL) && (rhostlen > 0)) #if IPFILTER_VERSION >= 5000000 inet_ntop(AF_INET, &ipn.in_nsrc.na_addr[0].in4, rhost, rhostlen); /* in_nsrcip */ #else inet_ntop(AF_INET, &ipn.in_src[0].in4, rhost, rhostlen); #endif #if IPFILTER_VERSION >= 5000000 inet_ntop(AF_INET, &ipn.in_ndst.na_addr[0].in4, iaddr, iaddrlen); /* in_ndstip */ #else inet_ntop(AF_INET, &ipn.in_in[0].in4, iaddr, iaddrlen); #endif r = 0; } } while (ipn.in_next != NULL); return r; } int get_redirect_rule_by_index(int index, char * ifname, unsigned short * eport, char * iaddr, int iaddrlen, unsigned short * iport, int * proto, char * desc, int desclen, char * rhost, int rhostlen, unsigned int * timestamp, u_int64_t * packets, u_int64_t * bytes) { ipfgeniter_t iter; ipfobj_t obj; ipnat_t ipn; int n, r; if (index < 0) return -1; if (dev < 0) { syslog(LOG_ERR, "%s not open", IPNAT_NAME); return -1; } memset(&obj, 0, sizeof(obj)); obj.ipfo_rev = IPFILTER_VERSION; obj.ipfo_ptr = &iter; obj.ipfo_size = sizeof(iter); obj.ipfo_type = IPFOBJ_GENITER; iter.igi_type = IPFGENITER_IPNAT; #if IPFILTER_VERSION > 4011300 iter.igi_nitems = 1; #endif iter.igi_data = &ipn; n = 0; r = -1; do { if (ioctl(dev, SIOCGENITER, &obj) == -1) { syslog(LOG_ERR, "%s:ioctl(SIOCGENITER): %m", "get_redirect_rule_by_index"); break; } if (strcmp(ipn.in_tag.ipt_tag, group_name) != 0) continue; if (index == n++) { #if IPFILTER_VERSION >= 5000000 *proto = ipn.in_pr[0]; *eport = ntohs(ipn.in_dpmax); *iport = ntohs(ipn.in_dpnext); #else *proto = ipn.in_p; *eport = ntohs(ipn.in_pmax); *iport = ntohs(ipn.in_pnext); #endif if (ifname) #if IPFILTER_VERSION >= 5000000 strlcpy(ifname, ipn.in_names + ipn.in_ifnames[0], IFNAMSIZ); #else strlcpy(ifname, ipn.in_ifnames[0], IFNAMSIZ); #endif if (packets != NULL) *packets = 0; if (bytes != NULL) *bytes = 0; if ((desc != NULL) && (timestamp != NULL)) get_redirect_desc(*eport, *proto, desc, desclen, timestamp); if ((rhost != NULL) && (rhostlen > 0)) #if IPFILTER_VERSION >= 5000000 inet_ntop(AF_INET, &ipn.in_nsrc.na_addr[0].in4, rhost, rhostlen); /* in_nsrcip */ #else inet_ntop(AF_INET, &ipn.in_src[0].in4, rhost, rhostlen); #endif #if IPFILTER_VERSION >= 5000000 inet_ntop(AF_INET, &ipn.in_ndst.na_addr[0].in4, iaddr, iaddrlen); /* in_ndstip */ #else inet_ntop(AF_INET, &ipn.in_in[0].in4, iaddr, iaddrlen); #endif r = 0; } } while (ipn.in_next != NULL); return r; } static int real_delete_redirect_rule(const char * ifname, unsigned short eport, int proto) { ipfgeniter_t iter; ipfobj_t obj; ipnat_t ipn; int r; memset(&obj, 0, sizeof(obj)); obj.ipfo_rev = IPFILTER_VERSION; obj.ipfo_type = IPFOBJ_GENITER; obj.ipfo_size = sizeof(iter); obj.ipfo_ptr = &iter; iter.igi_type = IPFGENITER_IPNAT; #if IPFILTER_VERSION > 4011300 iter.igi_nitems = 1; #endif iter.igi_data = &ipn; if (dev < 0) { syslog(LOG_ERR, "%s not open", IPNAT_NAME); return -1; } r = -1; do { if (ioctl(dev, SIOCGENITER, &obj) == -1) { syslog(LOG_ERR, "%s:ioctl(SIOCGENITER): %m", "delete_redirect_rule"); break; } #if IPFILTER_VERSION >= 5000000 if (eport == ntohs(ipn.in_dpmin) && eport == ntohs(ipn.in_dpmax) && strcmp(ipn.in_tag.ipt_tag, group_name) == 0 && ipn.in_pr[0] == proto) #else if (eport == ntohs(ipn.in_pmin) && eport == ntohs(ipn.in_pmax) && strcmp(ipn.in_tag.ipt_tag, group_name) == 0 && ipn.in_p == proto) #endif { obj.ipfo_rev = IPFILTER_VERSION; obj.ipfo_size = sizeof(ipn); obj.ipfo_ptr = &ipn; obj.ipfo_type = IPFOBJ_IPNAT; r = ioctl(dev, SIOCRMNAT, &obj); if (r == -1) syslog(LOG_ERR, "%s:ioctl(SIOCRMNAT): %m", "delete_redirect_rule"); /* Delete the desc even if the above failed */ del_redirect_desc(eport, proto); break; } } while (ipn.in_next != NULL); return r; } /* FIXME: For some reason, the iter isn't reset every other delete, * so we attempt 2 deletes. */ int delete_redirect_rule(const char * ifname, unsigned short eport, int proto) { int r; r = real_delete_redirect_rule(ifname, eport, proto); if (r == -1) r = real_delete_redirect_rule(ifname, eport, proto); return r; } /* thanks to Seth Mos for this function */ int add_filter_rule2(const char * ifname, const char * rhost, const char * iaddr, unsigned short eport, unsigned short iport, int proto, const char * desc) { ipfobj_t obj; frentry_t fr; fripf_t ipffr; int r; if (dev_ipl < 0) { syslog(LOG_ERR, "%s not open", IPL_NAME); return -1; } memset(&obj, 0, sizeof(obj)); memset(&fr, 0, sizeof(fr)); memset(&ipffr, 0, sizeof(ipffr)); fr.fr_flags = FR_PASS|FR_KEEPSTATE|FR_QUICK|FR_INQUE; if (GETFLAG(LOGPACKETSMASK)) fr.fr_flags |= FR_LOG|FR_LOGFIRST; #if IPFILTER_VERSION >= 5000000 fr.fr_family = PF_INET; #else fr.fr_v = 4; #endif fr.fr_type = FR_T_IPF; fr.fr_dun.fru_ipf = &ipffr; fr.fr_dsize = sizeof(ipffr); fr.fr_isc = (void *)-1; fr.fr_proto = proto; fr.fr_mproto = 0xff; fr.fr_dcmp = FR_EQUAL; fr.fr_dport = eport; #ifdef USE_IFNAME_IN_RULES if (ifname) { #if IPFILTER_VERSION >= 5000000 /* XXX check for stack overflow ! */ fr.fr_ifnames[0] = fr.fr_namelen; strlcpy(fr.fr_names + fr.fr_ifnames[0], ifname, IFNAMSIZ); fr.fr_namelen += strlen(ifname) + 1; #else strlcpy(fr.fr_ifnames[0], ifname, IFNAMSIZ); #endif } #endif #if IPFILTER_VERSION >= 5000000 /* XXX check for stack overflow ! */ fr.fr_group = fr.fr_namelen; strlcpy(fr.fr_names + fr.fr_group, group_name, FR_GROUPLEN); fr.fr_namelen += strlen(group_name) + 1; #else strlcpy(fr.fr_group, group_name, sizeof(fr.fr_group)); #endif if (proto == IPPROTO_TCP) { fr.fr_tcpf = TH_SYN; fr.fr_tcpfm = TH_SYN|TH_ACK|TH_RST|TH_FIN|TH_URG|TH_PUSH; } if(rhost && rhost[0] != '\0' && rhost[0] != '*') { inet_pton(AF_INET, rhost, &fr.fr_saddr); fr.fr_smask = 0xffffffff; } inet_pton(AF_INET, iaddr, &fr.fr_daddr); fr.fr_dmask = 0xffffffff; obj.ipfo_rev = IPFILTER_VERSION; obj.ipfo_ptr = &fr; obj.ipfo_size = sizeof(fr); r = ioctl(dev_ipl, SIOCINAFR, &obj); if (r == -1) { if (errno == ESRCH) syslog(LOG_ERR, "SIOCINAFR(missing 'head %s' rule?):%m", group_name); else syslog(LOG_ERR, "SIOCINAFR:%m"); } return r; } int delete_filter_rule(const char * ifname, unsigned short eport, int proto) { ipfobj_t wobj, dobj; ipfruleiter_t rule; u_long darray[1000]; u_long array[1000]; friostat_t fio; frentry_t *fp; int r; if (dev_ipl < 0) { syslog(LOG_ERR, "%s not open", IPL_NAME); return -1; } wobj.ipfo_rev = IPFILTER_VERSION; wobj.ipfo_type = IPFOBJ_IPFSTAT; wobj.ipfo_size = sizeof(fio); wobj.ipfo_ptr = &fio; if (ioctl(dev_ipl, SIOCGETFS, &wobj) == -1) { syslog(LOG_ERR, "ioctl(SIOCGETFS): %m"); return -1; } wobj.ipfo_rev = IPFILTER_VERSION; wobj.ipfo_ptr = &rule; wobj.ipfo_size = sizeof(rule); wobj.ipfo_type = IPFOBJ_IPFITER; fp = (frentry_t *)array; fp->fr_dun.fru_data = darray; fp->fr_dsize = sizeof(darray); rule.iri_inout = 0; rule.iri_active = fio.f_active; #if IPFILTER_VERSION > 4011300 rule.iri_nrules = 1; rule.iri_v = 4; #endif rule.iri_rule = fp; strlcpy(rule.iri_group, group_name, sizeof(rule.iri_group)); dobj.ipfo_rev = IPFILTER_VERSION; dobj.ipfo_size = sizeof(*fp); dobj.ipfo_type = IPFOBJ_FRENTRY; r = -1; do { memset(array, 0xff, sizeof(array)); if (ioctl(dev_ipl, SIOCIPFITER, &wobj) == -1) { syslog(LOG_ERR, "ioctl(SIOCIPFITER): %m"); break; } if (fp->fr_data != NULL) fp->fr_data = (char *)fp + sizeof(*fp); if ((fp->fr_type & ~FR_T_BUILTIN) == FR_T_IPF && fp->fr_dport == eport && fp->fr_proto == proto) { dobj.ipfo_ptr = fp; r = ioctl(dev_ipl, SIOCRMAFR, &dobj); if (r == -1) syslog(LOG_ERR, "ioctl(SIOCRMAFR): %m"); break; } } while (fp->fr_next != NULL); return r; } unsigned short * get_portmappings_in_range(unsigned short startport, unsigned short endport, int proto, unsigned int * number) { unsigned short * array; unsigned int capacity; unsigned short eport; ipfgeniter_t iter; ipfobj_t obj; ipnat_t ipn; *number = 0; if (dev < 0) { syslog(LOG_ERR, "%s not open", IPNAT_NAME); return NULL; } capacity = 128; array = calloc(capacity, sizeof(unsigned short)); if(!array) { syslog(LOG_ERR, "get_portmappings_in_range() : calloc error"); return NULL; } memset(&obj, 0, sizeof(obj)); obj.ipfo_rev = IPFILTER_VERSION; obj.ipfo_ptr = &iter; obj.ipfo_size = sizeof(iter); obj.ipfo_type = IPFOBJ_GENITER; iter.igi_type = IPFGENITER_IPNAT; #if IPFILTER_VERSION > 4011300 iter.igi_nitems = 1; #endif iter.igi_data = &ipn; do { if (ioctl(dev, SIOCGENITER, &obj) == -1) { syslog(LOG_ERR, "%s:ioctl(SIOCGENITER): %m", "get_portmappings_in_range"); break; } if (strcmp(ipn.in_tag.ipt_tag, group_name) != 0) continue; #if IPFILTER_VERSION >= 5000000 eport = ntohs(ipn.in_dpmin); if( (eport == ntohs(ipn.in_dpmax)) && (ipn.in_pr[0] == proto) && (startport <= eport) && (eport <= endport) ) #else eport = ntohs(ipn.in_pmin); if( (eport == ntohs(ipn.in_pmax)) && (ipn.in_p == proto) && (startport <= eport) && (eport <= endport) ) #endif { if(*number >= capacity) { /* need to increase the capacity of the array */ capacity += 128; array = realloc(array, sizeof(unsigned short)*capacity); if(!array) { syslog(LOG_ERR, "get_portmappings_in_range() : realloc(%lu) error", sizeof(unsigned short)*capacity); *number = 0; return NULL; } } array[*number] = eport; (*number)++; } } while (ipn.in_next != NULL); return array; } miniupnpd-1.8.20130730/ipf/ipfrdr.h010064400017500000024000000032441203340734100156620ustar00nanardstaff/* $Id: ipfrdr.h,v 1.6 2012/09/27 15:44:10 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2007 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #ifndef IPFRDR_H_INCLUDED #define IPFRDR_H_INCLUDED #include "../commonrdr.h" int add_redirect_rule2(const char * ifname, const char * rhost, unsigned short eport, const char * iaddr, unsigned short iport, int proto, const char * desc, unsigned int timestamp); int add_filter_rule2(const char * ifname, const char * rhost, const char * iaddr, unsigned short eport, unsigned short iport, int proto, const char * desc); /* get_redirect_rule() gets internal IP and port from * interface, external port and protocl */ #if 0 int get_redirect_rule(const char * ifname, unsigned short eport, int proto, char * iaddr, int iaddrlen, unsigned short * iport, char * desc, int desclen, u_int64_t * packets, u_int64_t * bytes); int get_redirect_rule_by_index(int index, char * ifname, unsigned short * eport, char * iaddr, int iaddrlen, unsigned short * iport, int * proto, char * desc, int desclen, u_int64_t * packets, u_int64_t * bytes); #endif /* delete_redirect_rule() */ int delete_redirect_rule(const char * ifname, unsigned short eport, int proto); /* delete_filter_rule() */ int delete_filter_rule(const char * ifname, unsigned short eport, int proto); int clear_redirect_rules(void); #endif miniupnpd-1.8.20130730/ipf/Makefile010064400017500000024000000006201214663314100156620ustar00nanardstaff# $Id: Makefile,v 1.2 2013/05/21 08:54:57 nanard Exp $ CC=gcc CFLAGS=-Wall -g -I. #CFLAGS=-Wall -g -I. -I../../../../ip_fil5.1.2/ #CFLAGS=-Wall -g -I. -I../../../../ip_fil4.1.35/ #CFLAGS=-Wall -g -I. -I../../../../ip_fil3.4.35/ all: testipfrdr clean: rm -f *.o testipfrdr testipfrdr: testipfrdr.o ipfrdr.o $(CC) -o $@ ${.ALLSRC} # $(CC) -o $@ $^ ipfrdr.o: ipfrdr.c testipfrdr.o: testipfrdr.c miniupnpd-1.8.20130730/ipf/testipfrdr.c010064400017500000024000000032421173213203200165500ustar00nanardstaff/* $Id: testipfrdr.c,v 1.4 2012/03/19 21:14:13 nanard Exp $ */ #include #include #include #include #include #include "ipfrdr.h" /* test program for ipfrdr.c */ int runtime_flags = 0; void list_eports_tcp(void) { unsigned short * port_list; unsigned int number = 0; unsigned int i; port_list = get_portmappings_in_range(0, 65535, IPPROTO_TCP, &number); printf("%u ports redirected (TCP) :", number); for(i = 0; i < number; i++) { printf(" %hu", port_list[i]); } printf("\n"); free(port_list); port_list = get_portmappings_in_range(0, 65535, IPPROTO_UDP, &number); printf("%u ports redirected (UDP) :", number); for(i = 0; i < number; i++) { printf(" %hu", port_list[i]); } printf("\n"); free(port_list); } int main(int argc, char * * argv) { char c; openlog("testipfrdrd", LOG_CONS|LOG_PERROR, LOG_USER); if(init_redirect() < 0) { fprintf(stderr, "init_redirect() failed\n"); return 1; } printf("List rdr ports :\n"); list_eports_tcp(); printf("Add redirection !\n"); add_redirect_rule2("xennet0", "*", 12345, "192.168.1.100", 54321, IPPROTO_UDP, "redirection description", 0); add_redirect_rule2("xennet0", "8.8.8.8", 12345, "192.168.1.100", 54321, IPPROTO_TCP, "redirection description", 0); printf("Check redirect rules with \"ipnat -l\" then press any key.\n"); c = getchar(); printf("List rdr ports :\n"); list_eports_tcp(); printf("Delete redirection !\n"); delete_redirect_rule("xennet0", 12345, IPPROTO_UDP); delete_redirect_rule("xennet0", 12345, IPPROTO_TCP); printf("List rdr ports :\n"); list_eports_tcp(); return 0; } miniupnpd-1.8.20130730/miniupnpdtypes.h010064400017500000024000000016411203340734000167040ustar00nanardstaff/* $Id: miniupnpdtypes.h,v 1.5 2012/09/27 15:47:15 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2012 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #ifndef MINIUPNPDTYPES_H_INCLUDED #define MINIUPNPDTYPES_H_INCLUDED #include "config.h" #include #include #include /* structure and list for storing lan addresses * with ascii representation and mask */ struct lan_addr_s { char ifname[IFNAMSIZ]; /* example: eth0 */ #ifdef ENABLE_IPV6 unsigned int index; /* use if_nametoindex() */ #endif char str[16]; /* example: 192.168.0.1 */ struct in_addr addr, mask; /* ip/mask */ #ifdef MULTIPLE_EXTERNAL_IP char ext_ip_str[16]; struct in_addr ext_ip_addr; #endif LIST_ENTRY(lan_addr_s) list; }; LIST_HEAD(lan_addr_list, lan_addr_s); #endif miniupnpd-1.8.20130730/bsdqueue.h010064400017500000024000000432671172522177000154530ustar00nanardstaff/* $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_ */ miniupnpd-1.8.20130730/miniupnpd.8010064400017500000024000000035371201574217700155570ustar00nanardstaff.TH MINIUPNPD 8 .SH NAME miniupnpd \- UPnP Internet Gateway Device Daemon .SH SYNOPSIS .B miniupnpd [\-f file] [\-i interface] [\-o address] [\-a address] [\-p port] [\-d] [\-L] [\-U] [\-u uuid] [\-s serial] [\-m model_number] [\-q queue] [\-t interval] [\-P file] [\-B down up] [\-w url] .SH DESCRIPTION miniupnpd act as a UPnP Internet Gateway Device. It is designed to run on the gateway between the internet and a NAT'ed LAN. It provides an interface, as defined in the UPnP standard, for enabling clients on the LAN to ask for port redirections. .SH OPTIONS .TP .B \-f file load the config from file. default is /etc/miniupnpd.conf. .TP .B \-i interface interface used to connect to the internet. .TP .B \-o address address used to connect to the internet. default address of the interface will be used if not specified. .TP .B \-a address address on the LAN. \-a option can by used multiple time if LAN is subdivised in several subnetworks. .TP .B \-p port port used for HTTP. .TP .B \-d debug mode : do not go to background, output messages on console and do not filter out low priority messages. .TP .B \-L set packet log in pf on .TP .B \-q queue set ALTQ queue in pf. filter rules must be enabled for this option to have any effect. .TP .B \-U report system uptime instead of daemon uptime to clients. .TP .B \-u uuid set the uuid of the UPnP Internet Gateway Device. .TP .B \-s serial serial number for the UPnP Internet Gateway Device. .TP .B \-m number model number for the UPnP Internet Gateway Device. .TP .B \-t interval SSDP notify interval in seconds : SSDP announce messages will be broadcasted at this interval. .TP .B \-P file pid file. default is /var/run/miniupnpd.pid .TP .B \-B down up download and upload bitrates reported to clients. .TP .B \-w url presentation url. default is first address on LAN, port 80. .SH "SEE ALSO" minissdpd(1) miniupnpc(3) .SH BUGS miniupnpd-1.8.20130730/natpmp.h010064400017500000024000000017401203340734000151130ustar00nanardstaff/* $Id: natpmp.h,v 1.9 2012/09/27 15:47:15 nanard Exp $ */ /* MiniUPnP project * author : Thomas Bernard * website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ */ #ifndef NATPMP_H_INCLUDED #define NATPMP_H_INCLUDED /* The NAT-PMP specification which can be found at the url : * http://files.dns-sd.org/draft-cheshire-nat-pmp.txt * draft version 3 of April 2008 * define 5351 as listening port for the gateway, * and the 224.0.0.1 port 5350 as the local link * multicast address for address change announces. * Previous versions of the specification defined 5351 * as the port for address change announces. */ #define NATPMP_PORT (5351) #define NATPMP_NOTIF_PORT (5350) #define NATPMP_NOTIF_ADDR ("224.0.0.1") int OpenAndConfNATPMPSockets(int * sockets); void ProcessIncomingNATPMPPacket(int s); #if 0 int ScanNATPMPforExpiration(void); int CleanExpiredNATPMP(void); #endif void SendNATPMPPublicAddressChangeNotification(int * sockets, int n_sockets); #endif miniupnpd-1.8.20130730/natpmp.c010064400017500000024000000331231212532024700151100ustar00nanardstaff/* $Id: natpmp.c,v 1.33 2013/03/23 10:46:55 nanard Exp $ */ /* MiniUPnP project * (c) 2007-2013 Thomas Bernard * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #include #include #include #include #include #include #include #include #include #include "macros.h" #include "config.h" #include "natpmp.h" #include "upnpglobalvars.h" #include "getifaddr.h" #include "upnpredirect.h" #include "commonrdr.h" #include "upnputils.h" #ifdef ENABLE_NATPMP int OpenAndConfNATPMPSocket(in_addr_t addr) { int snatpmp; int i = 1; snatpmp = socket(PF_INET, SOCK_DGRAM, 0/*IPPROTO_UDP*/); if(snatpmp<0) { syslog(LOG_ERR, "%s: socket(natpmp): %m", "OpenAndConfNATPMPSocket"); return -1; } if(setsockopt(snatpmp, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0) { syslog(LOG_WARNING, "%s: setsockopt(natpmp, SO_REUSEADDR): %m", "OpenAndConfNATPMPSocket"); } if(!set_non_blocking(snatpmp)) { syslog(LOG_WARNING, "%s: set_non_blocking(): %m", "OpenAndConfNATPMPSocket"); } { struct sockaddr_in natpmp_addr; memset(&natpmp_addr, 0, sizeof(natpmp_addr)); natpmp_addr.sin_family = AF_INET; natpmp_addr.sin_port = htons(NATPMP_PORT); /*natpmp_addr.sin_addr.s_addr = INADDR_ANY; */ natpmp_addr.sin_addr.s_addr = addr; if(bind(snatpmp, (struct sockaddr *)&natpmp_addr, sizeof(natpmp_addr)) < 0) { syslog(LOG_ERR, "bind(natpmp): %m"); close(snatpmp); return -1; } } return snatpmp; } int OpenAndConfNATPMPSockets(int * sockets) { int i, j; struct lan_addr_s * lan_addr; for(i = 0, lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next, i++) { sockets[i] = OpenAndConfNATPMPSocket(lan_addr->addr.s_addr); if(sockets[i] < 0) { for(j=0; jlist.le_next) { if( (senderaddr & lan_addr->mask.s_addr) == (lan_addr->addr.s_addr & lan_addr->mask.s_addr)) { memcpy(resp+8, &lan_addr->ext_ip_addr, sizeof(lan_addr->ext_ip_addr)); break; } } #endif } /** read the request from the socket, process it and then send the * response back. */ void ProcessIncomingNATPMPPacket(int s) { unsigned char req[32]; /* request udp packet */ unsigned char resp[32]; /* response udp packet */ int resplen; struct sockaddr_in senderaddr; socklen_t senderaddrlen = sizeof(senderaddr); int n; char senderaddrstr[16]; n = recvfrom(s, req, sizeof(req), 0, (struct sockaddr *)&senderaddr, &senderaddrlen); 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, "recvfrom(natpmp): %m"); } return; } if(!inet_ntop(AF_INET, &senderaddr.sin_addr, senderaddrstr, sizeof(senderaddrstr))) { syslog(LOG_ERR, "inet_ntop(natpmp): %m"); } syslog(LOG_INFO, "NAT-PMP request received from %s:%hu %dbytes", senderaddrstr, ntohs(senderaddr.sin_port), n); if(n<2 || ((((req[1]-1)&~1)==0) && n<12)) { syslog(LOG_WARNING, "discarding NAT-PMP request (too short) %dBytes", n); return; } if(req[1] & 128) { /* discarding NAT-PMP responses silently */ return; } memset(resp, 0, sizeof(resp)); resplen = 8; resp[1] = 128 + req[1]; /* response OPCODE is request OPCODE + 128 */ /* 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); if(req[0] > 0) { /* invalid version */ syslog(LOG_WARNING, "unsupported NAT-PMP version : %u", (unsigned)req[0]); resp[3] = 1; /* unsupported version */ } else switch(req[1]) { case 0: /* Public address request */ syslog(LOG_INFO, "NAT-PMP public address request"); FillPublicAddressResponse(resp, senderaddr.sin_addr.s_addr); resplen = 12; break; case 1: /* UDP port mapping request */ case 2: /* TCP port mapping request */ { unsigned short iport; /* private port */ unsigned short eport; /* public port */ uint32_t lifetime; /* lifetime=0 => remove port mapping */ int r; int proto; char iaddr_old[16]; 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))); 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, ×tamp, 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)) { r = _upnp_delete_redir(eport2, proto2); /* TODO : check return value */ if(r<0) { syslog(LOG_ERR, "failed to remove port mapping"); index++; } else { syslog(LOG_INFO, "NAT-PMP %s port %hu mapping removed", proto2==IPPROTO_TCP?"TCP":"UDP", eport2); } } 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 */ } } eport = 0; /* to indicate correct removing of port mapping */ } else if(iport==0 || !check_upnp_rule_against_permissions(upnppermlist, num_upnpperm, eport, senderaddr.sin_addr, iport)) { resp[3] = 2; /* Not Authorized/Refused */ } else do { r = get_redirect_rule(ext_if_name, eport, proto, iaddr_old, sizeof(iaddr_old), &iport_old, 0, 0, 0, 0, ×tamp, 0, 0); if(r==0) { if(strcmp(senderaddrstr, iaddr_old)==0 && iport==iport_old) { /* redirection allready existing */ syslog(LOG_INFO, "port %hu %s already redirected to %s:%hu, replacing", eport, (proto==IPPROTO_TCP)?"tcp":"udp", iaddr_old, iport_old); /* remove and then add again */ if(_upnp_delete_redir(eport, proto) < 0) { syslog(LOG_ERR, "failed to remove port mapping"); break; } } else { eport++; continue; } } { /* do the redirection */ char desc[64]; #if 0 timestamp = (unsigned)(time(NULL) - startup_time) + lifetime; snprintf(desc, sizeof(desc), "NAT-PMP %u", timestamp); #else timestamp = time(NULL) + lifetime; snprintf(desc, sizeof(desc), "NAT-PMP %hu %s", eport, (proto==IPPROTO_TCP)?"tcp":"udp"); #endif /* TODO : check return code */ if(upnp_redirect_internal(NULL, eport, senderaddrstr, iport, proto, desc, timestamp) < 0) { syslog(LOG_ERR, "Failed to add NAT-PMP %hu %s->%s:%hu '%s'", eport, (proto==IPPROTO_TCP)?"tcp":"udp", senderaddrstr, iport, desc); resp[3] = 3; /* Failure */ #if 0 } else if( !nextnatpmptoclean_eport || timestamp < nextnatpmptoclean_timestamp) { nextnatpmptoclean_timestamp = timestamp; nextnatpmptoclean_eport = eport; nextnatpmptoclean_proto = proto; #endif } break; } } while(r==0); *((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 */ } resplen = 16; break; default: resp[3] = 5; /* Unsupported OPCODE */ } n = sendto(s, resp, resplen, 0, (struct sockaddr *)&senderaddr, sizeof(senderaddr)); if(n<0) { syslog(LOG_ERR, "sendto(natpmp): %m"); } else if(n nextnatpmptoclean_timestamp) return ScanNATPMPforExpiration(); } /* remove redirection then search for next one:) */ if(_upnp_delete_redir(nextnatpmptoclean_eport, nextnatpmptoclean_proto)<0) return -1; syslog(LOG_INFO, "Expired NAT-PMP mapping port %hu %s removed", nextnatpmptoclean_eport, nextnatpmptoclean_proto==IPPROTO_TCP?"TCP":"UDP"); return ScanNATPMPforExpiration(); } #endif /* SendNATPMPPublicAddressChangeNotification() * should be called when the public IP address changed */ void SendNATPMPPublicAddressChangeNotification(int * sockets, int n_sockets) { struct sockaddr_in sockname; unsigned char notif[12]; int j, n; notif[0] = 0; /* vers */ notif[1] = 128; /* OP code */ notif[2] = 0; /* result code */ notif[3] = 0; /* result code */ /* 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); #ifndef MULTIPLE_EXTERNAL_IP FillPublicAddressResponse(notif, 0); if(notif[3]) { syslog(LOG_WARNING, "%s: cannot get public IP address, stopping", "SendNATPMPPublicAddressChangeNotification"); return; } #endif memset(&sockname, 0, sizeof(struct sockaddr_in)); sockname.sin_family = AF_INET; sockname.sin_addr.s_addr = inet_addr(NATPMP_NOTIF_ADDR); for(j=0; jlist.le_next; FillPublicAddressResponse(notif, lan_addr->addr.s_addr); } #endif /* Port to use in 2006 version of the NAT-PMP specification */ sockname.sin_port = htons(NATPMP_PORT); n = sendto(sockets[j], notif, 12, 0, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)); if(n < 0) { syslog(LOG_ERR, "%s: sendto(s_udp=%d): %m", "SendNATPMPPublicAddressChangeNotification", sockets[j]); return; } /* Port to use in 2008 version of the NAT-PMP specification */ sockname.sin_port = htons(NATPMP_NOTIF_PORT); n = sendto(sockets[j], notif, 12, 0, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)); if(n < 0) { syslog(LOG_ERR, "%s: sendto(s_udp=%d): %m", "SendNATPMPPublicAddressChangeNotification", sockets[j]); return; } } } #endif miniupnpd-1.8.20130730/commonrdr.h010064400017500000024000000031171203340734000156140ustar00nanardstaff/* $Id: commonrdr.h,v 1.8 2012/09/27 16:00:44 nanard Exp $ */ /* MiniUPnP project * (c) 2006-2011 Thomas Bernard * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #ifndef COMMONRDR_H_INCLUDED #define COMMONRDR_H_INCLUDED #include "config.h" /* init and shutdown functions */ int init_redirect(void); void shutdown_redirect(void); /* get_redirect_rule() gets internal IP and port from * interface, external port and protocl */ int get_redirect_rule(const char * ifname, unsigned short eport, int proto, char * iaddr, int iaddrlen, unsigned short * iport, char * desc, int desclen, char * rhost, int rhostlen, unsigned int * timestamp, u_int64_t * packets, u_int64_t * bytes); int get_redirect_rule_by_index(int index, char * ifname, unsigned short * eport, char * iaddr, int iaddrlen, unsigned short * iport, int * proto, char * desc, int desclen, char * rhost, int rhostlen, unsigned int * timestamp, u_int64_t * packets, u_int64_t * bytes); /* return an (malloc'ed) array of "external" port for which there is * a port mapping. number is the size of the array */ unsigned short * get_portmappings_in_range(unsigned short startport, unsigned short endport, int proto, unsigned int * number); #endif miniupnpd-1.8.20130730/upnpevents.h010064400017500000024000000022141203340734000160200ustar00nanardstaff/* $Id: upnpevents.h,v 1.10 2012/09/27 16:00:10 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2008-2011 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #ifndef UPNPEVENTS_H_INCLUDED #define UPNPEVENTS_H_INCLUDED #include "config.h" #ifdef ENABLE_EVENTS enum subscriber_service_enum { EWanCFG = 1, EWanIPC, #ifdef ENABLE_L3F_SERVICE EL3F, #endif #ifdef ENABLE_6FC_SERVICE E6FC, #endif #ifdef ENABLE_DP_SERVICE EDP, #endif }; void upnp_event_var_change_notify(enum subscriber_service_enum service); const char * upnpevents_addSubscriber(const char * eventurl, const char * callback, int callbacklen, int timeout); int upnpevents_removeSubscriber(const char * sid, int sidlen); int renewSubscription(const char * sid, int sidlen, int timeout); void upnpevents_selectfds(fd_set *readset, fd_set *writeset, int * max_fd); void upnpevents_processfds(fd_set *readset, fd_set *writeset); #ifdef USE_MINIUPNPDCTL void write_events_details(int s); #endif #endif #endif miniupnpd-1.8.20130730/upnpevents.c010064400017500000024000000335321217572255200160360ustar00nanardstaff/* $Id: upnpevents.c,v 1.27 2013/06/13 13:21:30 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2008-2013 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "upnpevents.h" #include "miniupnpdpath.h" #include "upnpglobalvars.h" #include "upnpdescgen.h" #include "upnputils.h" #ifdef ENABLE_EVENTS /*enum subscriber_service_enum { EWanCFG = 1, EWanIPC, EL3F };*/ /* stuctures definitions */ struct subscriber { LIST_ENTRY(subscriber) entries; struct upnp_event_notify * notify; time_t timeout; uint32_t seq; enum subscriber_service_enum service; char uuid[42]; char callback[]; }; struct upnp_event_notify { LIST_ENTRY(upnp_event_notify) entries; int s; /* socket */ enum { ECreated=1, EConnecting, ESending, EWaitingForResponse, EFinished, EError } state; struct subscriber * sub; char * buffer; int buffersize; int tosend; int sent; const char * path; #ifdef ENABLE_IPV6 int ipv6; char addrstr[48]; #else char addrstr[16]; #endif char portstr[8]; }; /* prototypes */ static void upnp_event_create_notify(struct subscriber * sub); /* Subscriber list */ LIST_HEAD(listhead, subscriber) subscriberlist = { NULL }; /* notify list */ LIST_HEAD(listheadnotif, upnp_event_notify) notifylist = { NULL }; /* create a new subscriber */ static struct subscriber * newSubscriber(const char * eventurl, const char * callback, int callbacklen) { struct subscriber * tmp; if(!eventurl || !callback || !callbacklen) return NULL; tmp = calloc(1, sizeof(struct subscriber)+callbacklen+1); if(!tmp) return NULL; if(strcmp(eventurl, WANCFG_EVENTURL)==0) tmp->service = EWanCFG; else if(strcmp(eventurl, WANIPC_EVENTURL)==0) tmp->service = EWanIPC; #ifdef ENABLE_L3F_SERVICE else if(strcmp(eventurl, L3F_EVENTURL)==0) tmp->service = EL3F; #endif #ifdef ENABLE_6FC_SERVICE else if(strcmp(eventurl, WANIP6FC_EVENTURL)==0) tmp->service = E6FC; #endif #ifdef ENABLE_DP_SERVICE else if(strcmp(eventurl, DP_EVENTURL)==0) tmp->service = EDP; #endif else { free(tmp); return NULL; } memcpy(tmp->callback, callback, callbacklen); tmp->callback[callbacklen] = '\0'; /* make a dummy uuid */ /* TODO: improve that */ strncpy(tmp->uuid, uuidvalue_igd, sizeof(tmp->uuid)); tmp->uuid[sizeof(tmp->uuid)-1] = '\0'; snprintf(tmp->uuid+37, 5, "%04lx", random() & 0xffff); return tmp; } /* creates a new subscriber and adds it to the subscriber list * also initiate 1st notify * TODO : add a check on the number of subscriber in order to * prevent memory overflow... */ const char * upnpevents_addSubscriber(const char * eventurl, const char * callback, int callbacklen, int timeout) { struct subscriber * tmp; /*static char uuid[42];*/ /* "uuid:00000000-0000-0000-0000-000000000000"; 5+36+1=42bytes */ syslog(LOG_DEBUG, "addSubscriber(%s, %.*s, %d)", eventurl, callbacklen, callback, timeout); /*strncpy(uuid, uuidvalue, sizeof(uuid)); uuid[sizeof(uuid)-1] = '\0';*/ tmp = newSubscriber(eventurl, callback, callbacklen); if(!tmp) return NULL; if(timeout) tmp->timeout = time(NULL) + timeout; LIST_INSERT_HEAD(&subscriberlist, tmp, entries); upnp_event_create_notify(tmp); return tmp->uuid; } /* renew a subscription (update the timeout) */ int renewSubscription(const char * sid, int sidlen, int timeout) { struct subscriber * sub; for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) { if((sidlen == 41) && (memcmp(sid, sub->uuid, 41) == 0)) { sub->timeout = (timeout ? time(NULL) + timeout : 0); return 0; } } return -1; } int upnpevents_removeSubscriber(const char * sid, int sidlen) { struct subscriber * sub; if(!sid) return -1; for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) { if((sidlen == 41) && (memcmp(sid, sub->uuid, 41) == 0)) { if(sub->notify) { sub->notify->sub = NULL; } LIST_REMOVE(sub, entries); free(sub); return 0; } } return -1; } /* notifies all subscriber of a number of port mapping change * or external ip address change */ void upnp_event_var_change_notify(enum subscriber_service_enum service) { struct subscriber * sub; for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) { if(sub->service == service && sub->notify == NULL) upnp_event_create_notify(sub); } } /* create and add the notify object to the list */ static void upnp_event_create_notify(struct subscriber * sub) { struct upnp_event_notify * obj; obj = calloc(1, sizeof(struct upnp_event_notify)); if(!obj) { syslog(LOG_ERR, "%s: calloc(): %m", "upnp_event_create_notify"); return; } obj->sub = sub; obj->state = ECreated; #ifdef ENABLE_IPV6 obj->s = socket((obj->sub->callback[7] == '[') ? PF_INET6 : PF_INET, SOCK_STREAM, 0); #else obj->s = socket(PF_INET, SOCK_STREAM, 0); #endif if(obj->s<0) { syslog(LOG_ERR, "%s: socket(): %m", "upnp_event_create_notify"); goto error; } if(!set_non_blocking(obj->s)) { syslog(LOG_ERR, "%s: set_non_blocking(): %m", "upnp_event_create_notify"); goto error; } if(sub) sub->notify = obj; LIST_INSERT_HEAD(¬ifylist, obj, entries); return; error: if(obj->s >= 0) close(obj->s); free(obj); } static void upnp_event_notify_connect(struct upnp_event_notify * obj) { unsigned int i; const char * p; unsigned short port; #ifdef ENABLE_IPV6 struct sockaddr_storage addr; #else struct sockaddr_in addr; #endif if(!obj) return; memset(&addr, 0, sizeof(addr)); i = 0; if(obj->sub == NULL) { obj->state = EError; return; } p = obj->sub->callback; p += 7; /* http:// */ #ifdef ENABLE_IPV6 if(*p == '[') { /* ip v6 */ p++; obj->ipv6 = 1; while(*p != ']' && i < (sizeof(obj->addrstr)-1)) obj->addrstr[i++] = *(p++); if(*p == ']') p++; } else { #endif while(*p != '/' && *p != ':' && i < (sizeof(obj->addrstr)-1)) obj->addrstr[i++] = *(p++); #ifdef ENABLE_IPV6 } #endif obj->addrstr[i] = '\0'; if(*p == ':') { obj->portstr[0] = *p; i = 1; p++; port = (unsigned short)atoi(p); while(*p != '/') { if(i<7) obj->portstr[i++] = *p; p++; } obj->portstr[i] = 0; } else { port = 80; obj->portstr[0] = '\0'; } obj->path = p; #ifdef ENABLE_IPV6 if(obj->ipv6) { struct sockaddr_in6 * sa = (struct sockaddr_in6 *)&addr; sa->sin6_family = AF_INET6; inet_pton(AF_INET6, obj->addrstr, &(sa->sin6_addr)); sa->sin6_port = htons(port); } else { struct sockaddr_in * sa = (struct sockaddr_in *)&addr; sa->sin_family = AF_INET; inet_pton(AF_INET, obj->addrstr, &(sa->sin_addr)); sa->sin_port = htons(port); } #else addr.sin_family = AF_INET; inet_aton(obj->addrstr, &addr.sin_addr); addr.sin_port = htons(port); #endif syslog(LOG_DEBUG, "%s: '%s' %hu '%s'", "upnp_event_notify_connect", obj->addrstr, port, obj->path); obj->state = EConnecting; if(connect(obj->s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { if(errno != EINPROGRESS && errno != EWOULDBLOCK) { syslog(LOG_ERR, "%s: connect(): %m", "upnp_event_notify_connect"); obj->state = EError; } } } static void upnp_event_prepare(struct upnp_event_notify * obj) { static const char notifymsg[] = "NOTIFY %s HTTP/1.1\r\n" "Host: %s%s\r\n" "Content-Type: text/xml\r\n" "Content-Length: %d\r\n" "NT: upnp:event\r\n" "NTS: upnp:propchange\r\n" "SID: %s\r\n" "SEQ: %u\r\n" "Connection: close\r\n" "Cache-Control: no-cache\r\n" "\r\n" "%.*s\r\n"; char * xml; int l; if(obj->sub == NULL) { obj->state = EError; return; } switch(obj->sub->service) { case EWanCFG: xml = getVarsWANCfg(&l); break; case EWanIPC: xml = getVarsWANIPCn(&l); break; #ifdef ENABLE_L3F_SERVICE case EL3F: xml = getVarsL3F(&l); break; #endif #ifdef ENABLE_6FC_SERVICE case E6FC: xml = getVars6FC(&l); break; #endif #ifdef ENABLE_DP_SERVICE case EDP: xml = getVarsDP(&l); break; #endif default: xml = NULL; l = 0; } obj->buffersize = 1024; obj->buffer = malloc(obj->buffersize); if(!obj->buffer) { syslog(LOG_ERR, "%s: malloc returned NULL", "upnp_event_prepare"); if(xml) { free(xml); } obj->state = EError; return; } obj->tosend = snprintf(obj->buffer, obj->buffersize, notifymsg, obj->path, obj->addrstr, obj->portstr, l+2, obj->sub->uuid, obj->sub->seq, l, xml); if(xml) { free(xml); xml = NULL; } obj->state = ESending; } static void upnp_event_send(struct upnp_event_notify * obj) { int i; syslog(LOG_DEBUG, "%s: sending event notify message to %s:%s", "upnp_event_send", obj->addrstr, obj->portstr); syslog(LOG_DEBUG, "%s: msg: %s", "upnp_event_send", obj->buffer + obj->sent); i = send(obj->s, obj->buffer + obj->sent, obj->tosend - obj->sent, 0); if(i<0) { if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) { syslog(LOG_NOTICE, "%s: send(): %m", "upnp_event_send"); obj->state = EError; return; } else { /* EAGAIN or EWOULDBLOCK or EINTR : no data sent */ i = 0; } } if(i != (obj->tosend - obj->sent)) syslog(LOG_NOTICE, "%s: %d bytes send out of %d", "upnp_event_send", i, obj->tosend - obj->sent); obj->sent += i; if(obj->sent == obj->tosend) obj->state = EWaitingForResponse; } static void upnp_event_recv(struct upnp_event_notify * obj) { int n; n = recv(obj->s, obj->buffer, obj->buffersize, 0); if(n<0) { if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) { syslog(LOG_ERR, "%s: recv(): %m", "upnp_event_recv"); obj->state = EError; } return; } syslog(LOG_DEBUG, "%s: (%dbytes) %.*s", "upnp_event_recv", n, n, obj->buffer); /* TODO : do something with the data recevied ? * right now, n (number of bytes received) is ignored * We may need to recv() more bytes. */ obj->state = EFinished; if(obj->sub) obj->sub->seq++; } static void upnp_event_process_notify(struct upnp_event_notify * obj) { int err; socklen_t len; switch(obj->state) { case EConnecting: /* now connected or failed to connect */ len = sizeof(err); if(getsockopt(obj->s, SOL_SOCKET, SO_ERROR, &err, &len) < 0) { syslog(LOG_ERR, "%s: getsockopt: %m", "upnp_event_process_notify"); obj->state = EError; break; } if(err != 0) { errno = err; syslog(LOG_WARNING, "%s: connect failed: %m", "upnp_event_process_notify"); obj->state = EError; break; } upnp_event_prepare(obj); if(obj->state == ESending) upnp_event_send(obj); break; case ESending: upnp_event_send(obj); break; case EWaitingForResponse: upnp_event_recv(obj); break; case EFinished: close(obj->s); obj->s = -1; break; default: syslog(LOG_ERR, "%s: unknown state", "upnp_event_process_notify"); } } void upnpevents_selectfds(fd_set *readset, fd_set *writeset, int * max_fd) { struct upnp_event_notify * obj; for(obj = notifylist.lh_first; obj != NULL; obj = obj->entries.le_next) { syslog(LOG_DEBUG, "upnpevents_selectfds: %p %d %d", obj, obj->state, obj->s); if(obj->s >= 0) { switch(obj->state) { case ECreated: upnp_event_notify_connect(obj); if(obj->state != EConnecting) break; case EConnecting: case ESending: FD_SET(obj->s, writeset); if(obj->s > *max_fd) *max_fd = obj->s; break; case EWaitingForResponse: FD_SET(obj->s, readset); if(obj->s > *max_fd) *max_fd = obj->s; break; default: ; } } } } void upnpevents_processfds(fd_set *readset, fd_set *writeset) { struct upnp_event_notify * obj; struct upnp_event_notify * next; struct subscriber * sub; struct subscriber * subnext; time_t curtime; for(obj = notifylist.lh_first; obj != NULL; obj = obj->entries.le_next) { syslog(LOG_DEBUG, "%s: %p %d %d %d %d", "upnpevents_processfds", obj, obj->state, obj->s, FD_ISSET(obj->s, readset), FD_ISSET(obj->s, writeset)); if(obj->s >= 0) { if(FD_ISSET(obj->s, readset) || FD_ISSET(obj->s, writeset)) upnp_event_process_notify(obj); } } obj = notifylist.lh_first; while(obj != NULL) { next = obj->entries.le_next; if(obj->state == EError || obj->state == EFinished) { if(obj->s >= 0) { close(obj->s); } if(obj->sub) obj->sub->notify = NULL; /* remove also the subscriber from the list if there was an error */ if(obj->state == EError && obj->sub) { LIST_REMOVE(obj->sub, entries); free(obj->sub); } if(obj->buffer) { free(obj->buffer); } LIST_REMOVE(obj, entries); free(obj); } obj = next; } /* remove timeouted subscribers */ curtime = time(NULL); for(sub = subscriberlist.lh_first; sub != NULL; ) { subnext = sub->entries.le_next; if(sub->timeout && curtime > sub->timeout && sub->notify == NULL) { LIST_REMOVE(sub, entries); free(sub); } sub = subnext; } } #ifdef USE_MINIUPNPDCTL void write_events_details(int s) { int n; char buff[80]; struct upnp_event_notify * obj; struct subscriber * sub; write(s, "Events details :\n", 17); for(obj = notifylist.lh_first; obj != NULL; obj = obj->entries.le_next) { n = snprintf(buff, sizeof(buff), " %p sub=%p state=%d s=%d\n", obj, obj->sub, obj->state, obj->s); write(s, buff, n); } write(s, "Subscribers :\n", 14); for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) { n = snprintf(buff, sizeof(buff), " %p timeout=%d seq=%u service=%d\n", sub, (int)sub->timeout, sub->seq, sub->service); write(s, buff, n); n = snprintf(buff, sizeof(buff), " notify=%p %s\n", sub->notify, sub->uuid); write(s, buff, n); n = snprintf(buff, sizeof(buff), " %s\n", sub->callback); write(s, buff, n); } } #endif #endif miniupnpd-1.8.20130730/codelength.h010064400017500000024000000021251203340733500157320ustar00nanardstaff/* $Id: codelength.h,v 1.4 2012/09/27 15:40:29 nanard Exp $ */ /* Project : miniupnp * Author : Thomas BERNARD * copyright (c) 2005-2011 Thomas Bernard * This software is subjet to the conditions detailed in the * provided LICENCE file. */ #ifndef CODELENGTH_H_INCLUDED #define CODELENGTH_H_INCLUDED /* Encode length by using 7bit per Byte : * Most significant bit of each byte specifies that the * following byte is part of the code */ #define DECODELENGTH(n, p) n = 0; \ do { n = (n << 7) | (*p & 0x7f); } \ while((*(p++)&0x80) && (n<(1<<25))); #define DECODELENGTH_CHECKLIMIT(n, p, p_limit) \ n = 0; \ do { \ if((p) >= (p_limit)) break; \ n = (n << 7) | (*(p) & 0x7f); \ } while((*((p)++)&0x80) && (n<(1<<25))); #define CODELENGTH(n, p) if(n>=268435456) *(p++) = (n >> 28) | 0x80; \ if(n>=2097152) *(p++) = (n >> 21) | 0x80; \ if(n>=16384) *(p++) = (n >> 14) | 0x80; \ if(n>=128) *(p++) = (n >> 7) | 0x80; \ *(p++) = n & 0x7f; #endif miniupnpd-1.8.20130730/testgetifaddr.c010064400017500000024000000021201213677042100164400ustar00nanardstaff/* $Id: testgetifaddr.c,v 1.7 2013/04/27 15:38:57 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 #include #include #include #include #include #include "getifaddr.h" #if defined(__sun) /* solaris 10 does not define LOG_PERROR */ #define LOG_PERROR 0 #endif int main(int argc, char * * argv) { char str_addr[64]; struct in_addr addr; struct in_addr mask; if(argc < 2) { fprintf(stderr, "Usage:\t%s interface_name\n", argv[0]); return 1; } openlog("testgetifaddr", LOG_CONS|LOG_PERROR, LOG_USER); if(getifaddr(argv[1], str_addr, sizeof(str_addr), &addr, &mask) < 0) { fprintf(stderr, "Cannot get address for interface %s.\n", argv[1]); return 1; } printf("Interface %s has IP address %s.\n", argv[1], str_addr); printf("addr=%s ", inet_ntoa(addr)); printf("mask=%s\n", inet_ntoa(mask)); return 0; } miniupnpd-1.8.20130730/Makefile.macosx010064400017500000024000000064531203340734000164020ustar00nanardstaff# MiniUPnP project # http://miniupnp.free.fr/ # Author: Thomas Bernard # This Makefile should work for MacOSX # # To install use : # $ PREFIX=/dummyinstalldir make -f Makefile.macosx install # or : # $ make -f Makefile.macosx install # CFLAGS = -Wall -O -g3 -DDEBUG #CFLAGS = -Wall -Os CC = gcc RM = rm -f MV = mv INSTALL = install STRIP = strip # OSNAME and FWNAME are used for building OS or FW dependent code. OSNAME = $(shell uname) ARCH = $(shell uname -p) FWNAME = ipfw 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 MAC_OBJS = mac/getifstats.o bsd/ifacewatcher.o IPFW_OBJS = ipfw/ipfwrdr.o ipfw/ipfwaux.o MISC_OBJS = upnpreplyparse.o minixml.o ALL_OBJS = $(STD_OBJS) $(MISC_OBJS) $(MAC_OBJS) $(IPFW_OBJS) TEST_UPNPDESCGEN_OBJS = testupnpdescgen.o upnpdescgen.o TEST_GETIFSTATS_OBJS = testgetifstats.o mac/getifstats.o TEST_UPNPPERMISSIONS_OBJS = testupnppermissions.o upnppermissions.o TEST_GETIFADDR_OBJS = testgetifaddr.o getifaddr.o MINIUPNPDCTL_OBJS = miniupnpdctl.o EXECUTABLES = miniupnpd testupnpdescgen testgetifstats \ testupnppermissions miniupnpdctl \ testgetifaddr LIBS = # set PREFIX variable to install in the wanted place INSTALL_BINDIR = $(PREFIX)/sbin INSTALL_ETCDIR = $(PREFIX)/etc/miniupnpd INSTALL_MANDIR = $(PREFIX)/share/man/man8 all: $(EXECUTABLES) clean: $(RM) $(ALL_OBJS) $(EXECUTABLES) \ testupnpdescgen.o testgetifstats.o testupnppermissions.o \ miniupnpdctl.o testgetifaddr.o config.h \ mac/org.tuxfamily.miniupnpd.plist install: miniupnpd genuuid genlaunchd $(STRIP) miniupnpd $(INSTALL) -d $(INSTALL_BINDIR) $(INSTALL) miniupnpd $(INSTALL_BINDIR) $(INSTALL) -d $(INSTALL_ETCDIR) $(INSTALL) -m 0644 -b miniupnpd.conf $(INSTALL_ETCDIR) $(INSTALL) -d $(INSTALL_MANDIR) $(INSTALL) miniupnpd.8 $(INSTALL_MANDIR) $(INSTALL) -d $(PREFIX)/Library/LaunchDaemons $(INSTALL) mac/org.tuxfamily.miniupnpd.plist $(PREFIX)/Library/LaunchDaemons #$(INSTALL) ipfw/ipfw_init.sh $(INSTALL_ETCDIR) #$(INSTALL) ipfw/ipfw_removeall.sh $(INSTALL_ETCDIR) genuuid: $(MV) miniupnpd.conf miniupnpd.conf.before sed -e "s/^uuid=[-0-9a-fA-F]*/uuid=`uuidgen 2>/dev/null`/" miniupnpd.conf.before > miniupnpd.conf $(RM) miniupnpd.conf.before genlaunchd: sed -e "s|INSTALLPREFIX|$(PREFIX)|g" mac/org.tuxfamily.miniupnpd.plist.before > mac/org.tuxfamily.miniupnpd.plist depend: config.h mkdep $(ALL_OBJS:.o=.c) testupnpdescgen.c testgetifstats.c \ testupnppermissions.c miniupnpdctl.c testgetifaddr.c miniupnpd: config.h $(ALL_OBJS) $(CC) $(CFLAGS) -o $@ $(ALL_OBJS) $(LIBS) miniupnpdctl: config.h $(MINIUPNPDCTL_OBJS) $(CC) $(CFLAGS) -o $@ $(MINIUPNPDCTL_OBJS) testupnpdescgen: config.h $(TEST_UPNPDESCGEN_OBJS) $(CC) $(CFLAGS) -o $@ $(TEST_UPNPDESCGEN_OBJS) testgetifstats: config.h $(TEST_GETIFSTATS_OBJS) $(CC) $(CFLAGS) -o $@ $(TEST_GETIFSTATS_OBJS) $(LIBS) testgetifaddr: config.h $(TEST_GETIFADDR_OBJS) $(CC) $(CFLAGS) -o $@ $(TEST_GETIFADDR_OBJS) testupnppermissions: config.h $(TEST_UPNPPERMISSIONS_OBJS) $(CC) $(CFLAGS) -o $@ $(TEST_UPNPPERMISSIONS_OBJS) config.h: genconfig.sh ./genconfig.sh .SUFFIXES: .o .c .c.o: $(CC) $(CFLAGS) -c -o $@ $< # $(CC) $(CFLAGS) -c -o $(.TARGET) $(.IMPSRC) miniupnpd-1.8.20130730/mac/Makefile010064400017500000024000000004061153032367000156450ustar00nanardstaff# $Id: Makefile,v 1.2 2011/02/20 23:43:41 nanard Exp $ # made for GNU Make CFLAGS = -Wall -g EXECUTABLES = testgetifstats all: $(EXECUTABLES) clean: rm -f *.o $(EXECUTABLES) testmacrdr.o: testmacrdr.c macrdr.h testgetifstats: testgetifstats.o getifstats.o miniupnpd-1.8.20130730/mac/getifstats.c010064400017500000024000000047371172522177200165470ustar00nanardstaff/* $Id: getifstats.c,v 1.7 2012/03/05 20:36:20 nanard Exp $ */ /* * MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2009 Jardel Weyrich * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #include #include #include #include #include #include #include #include #include #include #include "../getifstats.h" #include "../config.h" int getifstats(const char * ifname, struct ifdata * data) { int mib[] = { CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_IFLIST, if_nametoindex(ifname) }; const size_t mib_len = sizeof(mib) / sizeof(mib[0]); size_t needed; char *buf, *end, *p; struct if_msghdr *ifm; struct if_data ifdata; #ifdef ENABLE_GETIFSTATS_CACHING static time_t cache_timestamp = 0; static struct ifdata cache_data; time_t current_time; #endif if (data == NULL || ifname == NULL || ifname[0] == '\0') return -1; /* error */ data->baudrate = 4200000; data->opackets = 0; data->ipackets = 0; data->obytes = 0; data->ibytes = 0; #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; } } #endif if (sysctl(mib, mib_len, NULL, &needed, NULL, 0) == -1) { syslog(LOG_ERR, "sysctl(): %m"); return -1; } buf = (char *) malloc(needed); if (buf == NULL) return -1; /* error */ if (sysctl(mib, mib_len, buf, &needed, NULL, 0) == -1) { syslog(LOG_ERR, "sysctl(): %m"); free(buf); return -1; /* error */ } else { for (end = buf + needed, p = buf; p < end; p += ifm->ifm_msglen) { ifm = (struct if_msghdr *) p; if (ifm->ifm_type == RTM_IFINFO && ifm->ifm_data.ifi_type == IFT_ETHER) { ifdata = ifm->ifm_data; data->opackets = ifdata.ifi_opackets; data->ipackets = ifdata.ifi_ipackets; data->obytes = ifdata.ifi_obytes; data->ibytes = ifdata.ifi_ibytes; data->baudrate = ifdata.ifi_baudrate; free(buf); #ifdef ENABLE_GETIFSTATS_CACHING if (current_time!=((time_t)-1)) { cache_timestamp = current_time; memcpy(&cache_data, data, sizeof(struct ifdata)); } #endif return 0; /* found, ok */ } } } free(buf); return -1; /* not found or error */ } miniupnpd-1.8.20130730/mac/testgetifstats.c010064400017500000024000000014221172522177200174330ustar00nanardstaff/* $Id: testgetifstats.c,v 1.4 2012/03/05 20:36:20 nanard Exp $ */ /* * MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2009 Jardel Weyrich * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #include "../getifstats.h" int main(int argc, char * * argv) { int r; struct ifdata data; printf("usage: %s if_name\n", argv[0]); if (argc < 2) return -1; r = getifstats(argv[1], &data); if (r < 0) printf("getifstats() failed\n"); else { printf("ipackets = %10lu opackets = %10lu\n", data.ipackets, data.opackets); printf("ibytes = %10lu obytes = %10lu\n", data.ibytes, data.obytes); printf("baudrate = %10lu\n", data.baudrate); } return 0; } miniupnpd-1.8.20130730/mac/org.tuxfamily.miniupnpd.plist.before010064400017500000024000000007061170030524600233360ustar00nanardstaff miniupnpd org.tuxfamily.miniupnpd ProgramArguments INSTALLPREFIX/sbin/miniupnpd -f INSTALLPREFIX/etc/miniupnpd/miniupnpd.conf RunAtLoad miniupnpd-1.8.20130730/ipfw/Makefile010064400017500000024000000004131203340734100160450ustar00nanardstaff# $Id: Makefile,v 1.3 2012/09/20 12:46:01 nanard Exp $ CC=gcc CFLAGS=-Wall -g -I. RM=rm -f all: testipfwrdr clean: $(RM) *.o testipfwrdr testipfwrdr: testipfwrdr.o ipfwrdr.o ipfwaux.o $(CC) -o $@ $^ ipfwrdr.o: ipfwrdr.c ipfwaux.c testipfwrdr.o: testipfwrdr.c miniupnpd-1.8.20130730/ipfw/ipfwaux.c010064400017500000024000000044051202661001100162310ustar00nanardstaff/* * MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2009-2012 Jardel Weyrich * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include "ipfwaux.h" #include #include #include #include #include int ipfw_exec(int optname, void * optval, uintptr_t optlen) { static int sock = -1; int result; switch (optname) { case IP_FW_INIT: if (sock == -1) sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (sock < 0) { syslog(LOG_ERR, "socket(SOCK_RAW): %m"); return -1; } break; case IP_FW_TERM: if (sock != -1) close(sock); sock = -1; break; case IP_FW_ADD: case IP_FW_DEL: result = setsockopt(sock, IPPROTO_IP, optname, optval, optlen); if (result == -1) { syslog(LOG_ERR, "setsockopt(): %m"); return -1; } break; case IP_FW_GET: result = getsockopt(sock, IPPROTO_IP, optname, optval, (socklen_t *)optlen); if (result == -1) { syslog(LOG_ERR, "getsockopt(): %m"); return -1; } break; default: syslog(LOG_ERR, "unhandled option"); return -1; } return 0; } void ipfw_free_ruleset(struct ip_fw ** rules) { if (rules == NULL || *rules == NULL) return; free(*rules); *rules = NULL; } int ipfw_fetch_ruleset(struct ip_fw ** rules, int * total_fetched, int count) { int fetched; socklen_t size; if (rules == NULL || *total_fetched < 0 || count < 1) return -1; size = sizeof(struct ip_fw) * (*total_fetched + count); *rules = (struct ip_fw *)realloc(*rules, size); if (*rules == NULL) { syslog(LOG_ERR, "realloc(): %m"); return -1; } (*rules)->version = IP_FW_CURRENT_API_VERSION; if (ipfw_exec(IP_FW_GET, *rules, (uintptr_t)&size) < 0) return -1; fetched = *total_fetched; *total_fetched = size / sizeof(struct ip_fw); return *total_fetched - fetched; } int ipfw_validate_protocol(int value) { switch (value) { case IPPROTO_TCP: case IPPROTO_UDP: break; default: syslog(LOG_ERR, "invalid protocol"); return -1; } return 0; } int ipfw_validate_ifname(const char * const value) { int len = strlen(value); if (len < 2 || len > FW_IFNLEN) { syslog(LOG_ERR, "invalid interface name"); return -1; } return 0; } miniupnpd-1.8.20130730/ipfw/ipfwaux.h010064400017500000024000000014341203340734100162450ustar00nanardstaff/* $Id: ipfwaux.h,v 1.5 2012/09/20 12:46:01 nanard Exp $ */ /* * MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2009-2012 Jardel Weyrich * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #ifndef IPFWAUX_H #define IPFWAUX_H #include #include #include #define IP_FW_BASE (IP_FW_ADD - 5) #define IP_FW_INIT (IP_FW_BASE + 1) #define IP_FW_TERM (IP_FW_BASE + 2) int ipfw_exec(int optname, void * optval, uintptr_t optlen); void ipfw_free_ruleset(struct ip_fw ** rules); int ipfw_fetch_ruleset(struct ip_fw ** rules, int * total_fetched, int count); int ipfw_validate_protocol(int value); int ipfw_validate_ifname(const char * const value); #endif miniupnpd-1.8.20130730/ipfw/ipfwrdr.c010064400017500000024000000262001172522177100162400ustar00nanardstaff/* $Id: ipfwrdr.c,v 1.13 2012/03/05 20:36:19 nanard Exp $ */ /* * MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2009 Jardel Weyrich * (c) 2011-2012 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include "../config.h" #include #include #include /* This is a workaround for troubles on FreeBSD, HPUX, OpenBSD. Needed here because on some systems gets included by things like */ #ifndef _KERNEL # define ADD_KERNEL # define _KERNEL # define KERNEL #endif #ifdef __OpenBSD__ struct file; #endif #include #ifdef ADD_KERNEL # undef _KERNEL # undef KERNEL #endif #include #include #include #include #include #if __FreeBSD_version >= 300000 # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ipfwaux.h" #include "ipfwrdr.h" #include "../upnpglobalvars.h" /* init and shutdown functions */ int init_redirect(void) { return ipfw_exec(IP_FW_INIT, NULL, 0); } void shutdown_redirect(void) { ipfw_exec(IP_FW_TERM, NULL, 0); } /* ipfw cannot store descriptions and timestamp for port mappings so we keep * our own list in memory */ struct mapping_desc_time { struct mapping_desc_time * next; unsigned int timestamp; unsigned short eport; short proto; char desc[]; }; static struct mapping_desc_time * mappings_list = NULL; /* add an element to the port mappings descriptions & timestamp list */ static void add_desc_time(unsigned short eport, int proto, const char * desc, unsigned int timestamp) { struct mapping_desc_time * tmp; size_t l; if(!desc) desc = "miniupnpd"; l = strlen(desc) + 1; tmp = malloc(sizeof(struct mapping_desc_time) + l); if(tmp) { /* fill the element and insert it as head of the list */ tmp->next = mappings_list; tmp->timestamp = timestamp; tmp->eport = eport; tmp->proto = (short)proto; memcpy(tmp->desc, desc, l); mappings_list = tmp; } } /* remove an element to the port mappings descriptions & timestamp list */ static void del_desc_time(unsigned short eport, int proto) { struct mapping_desc_time * e; struct mapping_desc_time * * p; p = &mappings_list; e = *p; while(e) { if(e->eport == eport && e->proto == (short)proto) { *p = e->next; free(e); return; } else { p = &e->next; e = *p; } } } /* go through the list and find the description and timestamp */ static void get_desc_time(unsigned short eport, int proto, char * desc, int desclen, unsigned int * timestamp) { struct mapping_desc_time * e; for(e = mappings_list; e; e = e->next) { if(e->eport == eport && e->proto == (short)proto) { if(desc) strlcpy(desc, e->desc, desclen); if(timestamp) *timestamp = e->timestamp; return; } } } /* --- */ int add_redirect_rule2( const char * ifname, const char * rhost, unsigned short eport, const char * iaddr, unsigned short iport, int proto, const char * desc, unsigned int timestamp) { struct ip_fw rule; int r; if (ipfw_validate_protocol(proto) < 0) return -1; if (ipfw_validate_ifname(ifname) < 0) return -1; memset(&rule, 0, sizeof(struct ip_fw)); rule.version = IP_FW_CURRENT_API_VERSION; #if 0 rule.fw_number = 1000; /* rule number */ rule.context = (void *)desc; /* The description is kept in a separate list */ #endif rule.fw_prot = proto; /* protocol */ rule.fw_flg |= IP_FW_F_IIFACE; /* interfaces to check */ rule.fw_flg |= IP_FW_F_IIFNAME; /* interfaces to check by name */ rule.fw_flg |= (IP_FW_F_IN | IP_FW_F_OUT); /* packet direction */ rule.fw_flg |= IP_FW_F_FWD; /* forward action */ #ifdef USE_IFNAME_IN_RULES if (ifname != NULL) { strlcpy(rule.fw_in_if.fu_via_if.name, ifname, IFNAMSIZ); /* src interface */ rule.fw_in_if.fu_via_if.unit = -1; } #endif if (inet_aton(iaddr, &rule.fw_out_if.fu_via_ip) == 0) { syslog(LOG_ERR, "inet_aton(): %m"); return -1; } memcpy(&rule.fw_dst, &rule.fw_out_if.fu_via_ip, sizeof(struct in_addr)); memcpy(&rule.fw_fwd_ip.sin_addr, &rule.fw_out_if.fu_via_ip, sizeof(struct in_addr)); rule.fw_dmsk.s_addr = INADDR_BROADCAST; /* TODO check this */ IP_FW_SETNDSTP(&rule, 1); /* number of external ports */ rule.fw_uar.fw_pts[0] = eport; /* external port */ rule.fw_fwd_ip.sin_port = iport; /* internal port */ if (rhost && rhost[0] != '\0') { inet_aton(rhost, &rule.fw_src); rule.fw_smsk.s_addr = htonl(INADDR_NONE); } r = ipfw_exec(IP_FW_ADD, &rule, sizeof(rule)); if(r >= 0) add_desc_time(eport, proto, desc, timestamp); return r; } /* get_redirect_rule() * return value : 0 success (found) * -1 = error or rule not found */ int get_redirect_rule( const char * ifname, unsigned short eport, int proto, char * iaddr, int iaddrlen, unsigned short * iport, char * desc, int desclen, char * rhost, int rhostlen, unsigned int * timestamp, u_int64_t * packets, u_int64_t * bytes) { int i, count_rules, total_rules = 0; struct ip_fw * rules = NULL; if (ipfw_validate_protocol(proto) < 0) return -1; if (ipfw_validate_ifname(ifname) < 0) return -1; if (timestamp) *timestamp = 0; do { count_rules = ipfw_fetch_ruleset(&rules, &total_rules, 10); if (count_rules < 0) goto error; } while (count_rules == 10); for (i=0; ifw_prot && eport == ptr->fw_uar.fw_pts[0]) { if (packets != NULL) *packets = ptr->fw_pcnt; if (bytes != NULL) *bytes = ptr->fw_bcnt; if (iport != NULL) *iport = ptr->fw_fwd_ip.sin_port; if (iaddr != NULL && iaddrlen > 0) { /* looks like fw_out_if.fu_via_ip is zero */ /*if (inet_ntop(AF_INET, &ptr->fw_out_if.fu_via_ip, iaddr, iaddrlen) == NULL) {*/ if (inet_ntop(AF_INET, &ptr->fw_fwd_ip.sin_addr, iaddr, iaddrlen) == NULL) { syslog(LOG_ERR, "inet_ntop(): %m"); goto error; } } if (rhost != NULL && rhostlen > 0) { if (ptr->fw_src.s_addr == 0) rhost[0] = '\0'; else if (inet_ntop(AF_INET, &ptr->fw_src.s_addr, rhost, rhostlen) == NULL) { syslog(LOG_ERR, "inet_ntop(): %m"); goto error; } } /* And what if we found more than 1 matching rule? */ ipfw_free_ruleset(&rules); get_desc_time(eport, proto, desc, desclen, timestamp); return 0; } } error: if (rules != NULL) ipfw_free_ruleset(&rules); return -1; } int delete_redirect_rule( const char * ifname, unsigned short eport, int proto) { int i, count_rules, total_rules = 0; struct ip_fw * rules = NULL; if (ipfw_validate_protocol(proto) < 0) return -1; if (ipfw_validate_ifname(ifname) < 0) return -1; do { count_rules = ipfw_fetch_ruleset(&rules, &total_rules, 10); if (count_rules < 0) goto error; } while (count_rules == 10); for (i=0; ifw_prot && eport == ptr->fw_uar.fw_pts[0]) { if (ipfw_exec(IP_FW_DEL, (struct ip_fw *)ptr, sizeof(*ptr)) < 0) goto error; /* And what if we found more than 1 matching rule? */ ipfw_free_ruleset(&rules); del_desc_time(eport, proto); return 0; } } error: if (rules != NULL) ipfw_free_ruleset(&rules); return -1; } int add_filter_rule2( const char * ifname, const char * rhost, const char * iaddr, unsigned short eport, unsigned short iport, int proto, const char * desc) { return 0; /* nothing to do, always success */ } int delete_filter_rule( const char * ifname, unsigned short eport, int proto) { return 0; /* nothing to do, always success */ } int get_redirect_rule_by_index( int index, char * ifname, unsigned short * eport, char * iaddr, int iaddrlen, unsigned short * iport, int * proto, char * desc, int desclen, char * rhost, int rhostlen, unsigned int * timestamp, u_int64_t * packets, u_int64_t * bytes) { int total_rules = 0; struct ip_fw * rules = NULL; if (index < 0) /* TODO shouldn't we also validate the maximum? */ return -1; if(timestamp) *timestamp = 0; ipfw_fetch_ruleset(&rules, &total_rules, index + 1); if (total_rules > index) { const struct ip_fw const * ptr = &rules[index]; if (ptr->fw_prot == 0) /* invalid rule */ goto error; if (proto != NULL) *proto = ptr->fw_prot; if (eport != NULL) *eport = ptr->fw_uar.fw_pts[0]; if (iport != NULL) *iport = ptr->fw_fwd_ip.sin_port; if (ifname != NULL) strlcpy(ifname, ptr->fw_in_if.fu_via_if.name, IFNAMSIZ); if (packets != NULL) *packets = ptr->fw_pcnt; if (bytes != NULL) *bytes = ptr->fw_bcnt; if (iport != NULL) *iport = ptr->fw_fwd_ip.sin_port; if (iaddr != NULL && iaddrlen > 0) { /* looks like fw_out_if.fu_via_ip is zero */ /*if (inet_ntop(AF_INET, &ptr->fw_out_if.fu_via_ip, iaddr, iaddrlen) == NULL) {*/ if (inet_ntop(AF_INET, &ptr->fw_fwd_ip.sin_addr, iaddr, iaddrlen) == NULL) { syslog(LOG_ERR, "inet_ntop(): %m"); goto error; } } if (rhost != NULL && rhostlen > 0) { if (ptr->fw_src.s_addr == 0) rhost[0] = '\0'; else if (inet_ntop(AF_INET, &ptr->fw_src.s_addr, rhost, rhostlen) == NULL) { syslog(LOG_ERR, "inet_ntop(): %m"); goto error; } } ipfw_free_ruleset(&rules); get_desc_time(*eport, *proto, desc, desclen, timestamp); return 0; } error: if (rules != NULL) ipfw_free_ruleset(&rules); return -1; } /* upnp_get_portmappings_in_range() * return a list of all "external" ports for which a port * mapping exists */ unsigned short * get_portmappings_in_range(unsigned short startport, unsigned short endport, int proto, unsigned int * number) { unsigned short * array = NULL; unsigned int capacity = 128; int i, count_rules, total_rules = 0; struct ip_fw * rules = NULL; if (ipfw_validate_protocol(proto) < 0) return NULL; do { count_rules = ipfw_fetch_ruleset(&rules, &total_rules, 10); if (count_rules < 0) goto error; } while (count_rules == 10); array = calloc(capacity, sizeof(unsigned short)); if(!array) { syslog(LOG_ERR, "get_portmappings_in_range() : calloc error"); goto error; } *number = 0; for (i=0; ifw_uar.fw_pts[0]; if (proto == ptr->fw_prot && startport <= eport && eport <= endport) { if(*number >= capacity) { capacity += 128; array = realloc(array, sizeof(unsigned short)*capacity); if(!array) { syslog(LOG_ERR, "get_portmappings_in_range() : realloc(%lu) error", sizeof(unsigned short)*capacity); *number = 0; goto error; } } array[*number] = eport; (*number)++; } } error: if (rules != NULL) ipfw_free_ruleset(&rules); return array; } miniupnpd-1.8.20130730/ipfw/ipfwrdr.h010064400017500000024000000032121203340734100162330ustar00nanardstaff/* $Id: ipfwrdr.h,v 1.8 2012/09/27 15:44:10 nanard Exp $ */ /* * MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2009 Jardel Weyrich * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #ifndef IPFWRDR_H_INCLUDED #define IPFWRDR_H_INCLUDED #include "../commonrdr.h" int add_redirect_rule2( const char * ifname, /* src interface (external) */ const char * rhost, /* remote host (ip) */ unsigned short eport, /* src port (external) */ const char * iaddr, /* dst address (internal) */ unsigned short iport, /* dst port (internal) */ int proto, const char * desc, unsigned int timestamp); int add_filter_rule2( const char * ifname, const char * rhost, const char * iaddr, unsigned short eport, unsigned short iport, int proto, const char * desc); #if 0 /* * get_redirect_rule() gets internal IP and port from * interface, external port and protocl */ int get_redirect_rule( const char * ifname, unsigned short eport, int proto, char * iaddr, int iaddrlen, unsigned short * iport, char * desc, int desclen, u_int64_t * packets, u_int64_t * bytes); int get_redirect_rule_by_index( int index, char * ifname, unsigned short * eport, char * iaddr, int iaddrlen, unsigned short * iport, int * proto, char * desc, int desclen, u_int64_t * packets, u_int64_t * bytes); #endif /* * delete_redirect_rule() */ int delete_redirect_rule(const char * ifname, unsigned short eport, int proto); /* * delete_filter_rule() */ int delete_filter_rule(const char * ifname, unsigned short eport, int proto); int clear_redirect_rules(void); #endif miniupnpd-1.8.20130730/ipfw/testipfwrdr.c010064400017500000024000000050211160046616100171320ustar00nanardstaff/* $Id: testipfwrdr.c,v 1.7 2011/06/22 21:57:17 nanard Exp $ */ /* * MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2009-2011 Jardel Weyrich, Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #include #include #include #include "ipfwrdr.h" // test program for ipfwrdr.c static const char * ifname = "lo0"; static void list_port_mappings(void) { int i; unsigned short eport; char iaddr[16]; unsigned short iport; int proto; char desc[64]; char rhost[32]; unsigned int timestamp; u_int64_t packets, bytes; printf("== Port Mapping List ==\n"); for(i = 0;; i++) { iaddr[0] = '\0'; desc[0] = '\0'; eport = iport = 0; timestamp = 0; packets = bytes = 0; proto = -1; if(get_redirect_rule_by_index(i, 0/*ifname*/, &eport, iaddr, sizeof(iaddr), &iport, &proto, desc, sizeof(desc), rhost, sizeof(rhost), ×tamp, &packets, &bytes) < 0) break; printf("%2d - %5hu=>%15s:%5hu %d '%s' '%s' %u %" PRIu64 " %" PRIu64 "\n", i, eport, iaddr, iport, proto, desc, rhost, timestamp, packets, bytes); } printf("== %d Port Mapping%s ==\n", i, (i > 1)?"s":""); } int main(int argc, char * * argv) { unsigned int timestamp; char desc[64]; char addr[16]; char rhost[40]; unsigned short iport = 0; const char * in_rhost = "8.8.8.8"; desc[0] = '\0'; addr[0] = '\0'; openlog("testipfwrdrd", LOG_CONS | LOG_PERROR, LOG_USER); if(init_redirect() < 0) { fprintf(stderr, "init_redirect() failed.\n"); return 1; } list_port_mappings(); delete_redirect_rule(ifname, 2222, IPPROTO_TCP); delete_redirect_rule(ifname, 2223, IPPROTO_TCP); add_redirect_rule2(ifname, "", 2223, "10.1.1.17", 4445, IPPROTO_TCP, "test miniupnpd", time(NULL) + 60); add_redirect_rule2(ifname, in_rhost, 2222, "10.1.1.16", 4444, IPPROTO_TCP, "test miniupnpd", time(NULL) + 60); get_redirect_rule(ifname, 2222, IPPROTO_TCP, addr, sizeof(addr), &iport, desc, sizeof(desc), rhost, sizeof(rhost), ×tamp, NULL, NULL); printf("'%s' %s:%hu '%s' %u\n", rhost, addr, iport, desc, timestamp); list_port_mappings(); delete_redirect_rule(ifname, 2222, IPPROTO_TCP); delete_redirect_rule(ifname, 2223, IPPROTO_TCP); list_port_mappings(); shutdown_redirect(); return 0; } miniupnpd-1.8.20130730/VERSION010064400017500000024000000000041210445350200145030ustar00nanardstaff1.8 miniupnpd-1.8.20130730/netfilter/iptables_init_and_clean.sh010075500017500000024000000027711172522177100226260ustar00nanardstaff#! /bin/sh # $Id: iptables_init_and_clean.sh,v 1.3 2012/03/05 20:36:19 nanard Exp $ # 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}'`" echo "External IP = $EXTIP" if [[ $NDIRTY = "MINIUPNPDChain" ]]; then echo "Nat table dirty; Cleaning..." $IPTABLES -t nat -F MINIUPNPD elif [[ $NDIRTY = "Chain" ]]; then echo "Dirty NAT chain but no reference..? Fixsted." $IPTABLES -t nat -A PREROUTING -d $EXTIP -i $EXTIF -j MINIUPNPD $IPTABLES -t nat -F MINIUPNPD else echo "NAT table clean..initalizing.." $IPTABLES -t nat -N MINIUPNPD $IPTABLES -t nat -A PREROUTING -d $EXTIP -i $EXTIF -j MINIUPNPD fi if [[ $FDIRTY = "MINIUPNPDChain" ]]; then echo "Filter table dirty; Cleaning..." $IPTABLES -t filter -F MINIUPNPD elif [[ $FDIRTY = "Chain" ]]; then echo "Dirty filter chain but no reference..? Fixsted." $IPTABLES -t filter -I FORWARD 4 -i $EXTIF ! -o $EXTIF -j MINIUPNPD $IPTABLES -t filter -F MINIUPNPD else echo "Filter table clean..initalizing.." $IPTABLES -t filter -N MINIUPNPD $IPTABLES -t filter -I FORWARD 4 -i $EXTIF ! -o $EXTIF -j MINIUPNPD fi miniupnpd-1.8.20130730/upnputils.c010064400017500000024000000101461213653071000156540ustar00nanardstaff/* $Id: upnputils.c,v 1.7 2013/04/20 09:03:18 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 #include #include #include #include #include #include #include #include #ifdef AF_LINK #include #endif #include "upnputils.h" #include "upnpglobalvars.h" #ifdef ENABLE_IPV6 #include "getroute.h" #endif 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; } struct lan_addr_s * get_lan_for_peer(const struct sockaddr * peer) { struct lan_addr_s * lan_addr = NULL; #ifdef ENABLE_IPV6 if(peer->sa_family == AF_INET6) { struct sockaddr_in6 * peer6 = (struct sockaddr_in6 *)peer; if(IN6_IS_ADDR_V4MAPPED(&peer6->sin6_addr)) { struct in_addr peer_addr; memcpy(&peer_addr, &peer6->sin6_addr.s6_addr[12], 4); for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next) { if( (peer_addr.s_addr & lan_addr->mask.s_addr) == (lan_addr->addr.s_addr & lan_addr->mask.s_addr)) break; } } else { int index = -1; if(peer6->sin6_scope_id > 0) index = (int)peer6->sin6_scope_id; else { if(get_src_for_route_to(peer, NULL, NULL, &index) < 0) return NULL; } syslog(LOG_DEBUG, "%s looking for LAN interface index=%d", "get_lan_for_peer()", index); for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next) { syslog(LOG_DEBUG, "ifname=%s index=%u str=%s addr=%08x mask=%08x", lan_addr->ifname, lan_addr->index, lan_addr->str, ntohl(lan_addr->addr.s_addr), ntohl(lan_addr->mask.s_addr)); if(index == (int)lan_addr->index) break; } } } else if(peer->sa_family == AF_INET) { #endif for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next) { if( (((const struct sockaddr_in *)peer)->sin_addr.s_addr & lan_addr->mask.s_addr) == (lan_addr->addr.s_addr & lan_addr->mask.s_addr)) break; } #ifdef ENABLE_IPV6 } #endif if(lan_addr) syslog(LOG_DEBUG, "%s: found in LAN %s %s", "get_lan_for_peer()", lan_addr->ifname, lan_addr->str); else syslog(LOG_DEBUG, "%s: not found !", "get_lan_for_peer()"); return lan_addr; } miniupnpd-1.8.20130730/upnputils.h010064400017500000024000000015071210443263100156600ustar00nanardstaff/* $Id: upnputils.h,v 1.4 2013/02/06 10:50:04 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); /** * get the LAN which the peer belongs to */ struct lan_addr_s * get_lan_for_peer(const struct sockaddr * peer); #endif miniupnpd-1.8.20130730/upnpurns.h010064400017500000024000000022301203340734000155010ustar00nanardstaff/* $Id: upnpurns.h,v 1.3 2012/09/27 15:46:18 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2011 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #ifndef UPNPURNS_H_INCLUDED #define UPNPURNS_H_INCLUDED #include "config.h" #ifdef IGD_V2 /* IGD v2 */ #define DEVICE_TYPE_IGD "urn:schemas-upnp-org:device:InternetGatewayDevice:2" #define DEVICE_TYPE_WAN "urn:schemas-upnp-org:device:WANDevice:2" #define DEVICE_TYPE_WANC "urn:schemas-upnp-org:device:WANConnectionDevice:2" #define SERVICE_TYPE_WANIPC "urn:schemas-upnp-org:service:WANIPConnection:2" #define SERVICE_ID_WANIPC "urn:upnp-org:serviceId:WANIPConn1" #else /* IGD v1 */ #define DEVICE_TYPE_IGD "urn:schemas-upnp-org:device:InternetGatewayDevice:1" #define DEVICE_TYPE_WAN "urn:schemas-upnp-org:device:WANDevice:1" #define DEVICE_TYPE_WANC "urn:schemas-upnp-org:device:WANConnectionDevice:1" #define SERVICE_TYPE_WANIPC "urn:schemas-upnp-org:service:WANIPConnection:1" #define SERVICE_ID_WANIPC "urn:upnp-org:serviceId:WANIPConn1" #endif #endif miniupnpd-1.8.20130730/getconnstatus.c010064400017500000024000000031111212532024700165040ustar00nanardstaff/* $Id: getconnstatus.c,v 1.6 2013/03/23 10:46:54 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 */ #include #include #include #include "getconnstatus.h" #include "getifaddr.h" #define STATUS_UNCONFIGURED (0) #define STATUS_CONNECTING (1) #define STATUS_CONNECTED (2) #define STATUS_PENDINGDISCONNECT (3) #define STATUS_DISCONNECTING (4) #define STATUS_DISCONNECTED (5) /** * get the connection status * return values : * 0 - Unconfigured * 1 - Connecting * 2 - Connected * 3 - PendingDisconnect * 4 - Disconnecting * 5 - Disconnected */ int get_wan_connection_status(const char * ifname) { char addr[INET_ADDRSTRLEN]; int r; /* we need a better implementation here. * I'm afraid it should be device specific */ r = getifaddr(ifname, addr, INET_ADDRSTRLEN, NULL, NULL); return (r < 0) ? STATUS_DISCONNECTED : STATUS_CONNECTED; } /** * return the same value as get_wan_connection_status() * as a C string */ const char * get_wan_connection_status_str(const char * ifname) { int status; const char * str = NULL; status = get_wan_connection_status(ifname); switch(status) { case 0: str = "Unconfigured"; break; case 1: str = "Connecting"; break; case 2: str = "Connected"; break; case 3: str = "PendingDisconnect"; break; case 4: str = "Disconnecting"; break; case 5: str = "Disconnected"; break; } return str; } miniupnpd-1.8.20130730/getconnstatus.h010064400017500000024000000013521203340734000165140ustar00nanardstaff/* $Id: getconnstatus.h,v 1.4 2012/09/27 16:00:10 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2011 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #ifndef GETCONNSTATUS_H_INCLUDED #define GETCONNSTATUS_H_INCLUDED /** * get the connection status * return values : * 0 - Unconfigured * 1 - Connecting * 2 - Connected * 3 - PendingDisconnect * 4 - Disconnecting * 5 - Disconnected */ int get_wan_connection_status(const char * ifname); /** * return the same value as get_wan_connection_status() * as a C string */ const char * get_wan_connection_status_str(const char * ifname); #endif miniupnpd-1.8.20130730/ifacewatcher.h010064400017500000024000000041401203340734000162360ustar00nanardstaff/* $Id: ifacewatcher.h,v 1.4 2012/09/27 16:00:44 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2009 Thomas Bernard * * ifacewatcher.h * * This file implements dynamic serving of new network interfaces * which weren't available during daemon start. It also takes care * of interfaces which become unavailable. * * Copyright (c) 2011, Alexey Osipov * 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. */ #ifndef IFACEWATCHER_H_INCLUDED #define IFACEWATCHER_H_INCLUDED #include "config.h" #ifdef USE_IFACEWATCHER int OpenAndConfInterfaceWatchSocket(void); void ProcessInterfaceWatchNotify(int s); #endif #endif miniupnpd-1.8.20130730/bsd/ifacewatcher.c010064400017500000024000000065101175640135600170200ustar00nanardstaff/* $Id: ifacewatcher.c,v 1.5 2012/05/21 08:55:10 nanard Exp $ */ /* Project MiniUPnP * web : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2011 Thomas BERNARD * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include "../config.h" #include #include #include #include #include #include #include #if !defined(SA_LEN) #define SA_LEN(sa) (sa)->sa_len #endif #define SALIGN (sizeof(long) - 1) #define SA_RLEN(sa) ((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : (SALIGN + 1)) #include "../upnputils.h" #include "../upnpglobalvars.h" extern volatile sig_atomic_t should_send_public_address_change_notif; int OpenAndConfInterfaceWatchSocket(void) { int s; /*s = socket(PF_ROUTE, SOCK_RAW, AF_INET);*/ s = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC); /* The family parameter may be AF_UNSPEC which will provide routing informa- * tion for all address families, or can be restricted to a specific address * family by specifying which one is desired. There can be more than one * routing socket open per system. */ if(s < 0) { syslog(LOG_ERR, "OpenAndConfInterfaceWatchSocket socket: %m"); } return s; } void ProcessInterfaceWatchNotify(int s) { char buf[4096]; ssize_t len; char tmp[64]; struct rt_msghdr * rtm; struct if_msghdr * ifm; struct ifa_msghdr * ifam; #ifdef RTM_IFANNOUNCE struct if_announcemsghdr * ifanm; #endif char * p; struct sockaddr * sa; unsigned int ext_if_name_index = 0; len = recv(s, buf, sizeof(buf), 0); if(len < 0) { syslog(LOG_ERR, "ProcessInterfaceWatchNotify recv: %m"); return; } if(ext_if_name) { ext_if_name_index = if_nametoindex(ext_if_name); } rtm = (struct rt_msghdr *)buf; syslog(LOG_DEBUG, "%u rt_msg : msglen=%d version=%d type=%d", (unsigned)len, rtm->rtm_msglen, rtm->rtm_version, rtm->rtm_type); switch(rtm->rtm_type) { case RTM_IFINFO: /* iface going up/down etc. */ ifm = (struct if_msghdr *)buf; syslog(LOG_DEBUG, " RTM_IFINFO: addrs=%x flags=%x index=%hu", ifm->ifm_addrs, ifm->ifm_flags, ifm->ifm_index); break; #ifdef RTM_IFANNOUNCE case RTM_IFANNOUNCE: /* iface arrival/departure */ ifanm = (struct if_announcemsghdr *)buf; syslog(LOG_DEBUG, " RTM_IFANNOUNCE: index=%hu what=%hu ifname=%s", ifanm->ifan_index, ifanm->ifan_what, ifanm->ifan_name); break; #endif #ifdef RTM_IEEE80211 case RTM_IEEE80211: /* IEEE80211 wireless event */ syslog(LOG_DEBUG, " RTM_IEEE80211"); break; #endif case RTM_NEWADDR: /* address being added to iface */ ifam = (struct ifa_msghdr *)buf; syslog(LOG_DEBUG, " RTM_NEWADDR: addrs=%x flags=%x index=%hu", ifam->ifam_addrs, ifam->ifam_flags, ifam->ifam_index); p = buf + sizeof(struct ifa_msghdr); while(p < buf + len) { sa = (struct sockaddr *)p; sockaddr_to_string(sa, tmp, sizeof(tmp)); syslog(LOG_DEBUG, " %s", tmp); p += SA_RLEN(sa); } if(ifam->ifam_index == ext_if_name_index) { should_send_public_address_change_notif = 1; } break; case RTM_DELADDR: /* address being removed from iface */ ifam = (struct ifa_msghdr *)buf; if(ifam->ifam_index == ext_if_name_index) { should_send_public_address_change_notif = 1; } break; default: syslog(LOG_DEBUG, "unprocessed RTM message type=%d", rtm->rtm_type); } } miniupnpd-1.8.20130730/bsd/testifacewatcher.c010064400017500000024000000013051175640135600177150ustar00nanardstaff/* $Id: testifacewatcher.c,v 1.2 2012/05/21 08:55:10 nanard Exp $ */ #include int OpenAndConfInterfaceWatchSocket(void); void ProcessInterfaceWatchNotify(int s); const char * ext_if_name; volatile sig_atomic_t should_send_public_address_change_notif = 0; int main(int argc, char * * argv) { int s; ext_if_name = "ep0"; openlog("testifacewatcher", LOG_CONS|LOG_PERROR, LOG_USER); syslog(LOG_DEBUG, "test"); s = OpenAndConfInterfaceWatchSocket(); for(;;) { if(should_send_public_address_change_notif) { syslog(LOG_DEBUG, "should_send_public_address_change_notif !"); should_send_public_address_change_notif = 0; } ProcessInterfaceWatchNotify(s); } closelog(); return 0; } miniupnpd-1.8.20130730/linux/ifacewatcher.c010064400017500000024000000235161176053236400174130ustar00nanardstaff/* $Id: ifacewatcher.c,v 1.7 2012/05/27 22:16:10 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2012 Thomas Bernard * * ifacewatcher.c * * This file implements dynamic serving of new network interfaces * which weren't available during daemon start. It also takes care * of interfaces which become unavailable. * * Copyright (c) 2011, Alexey Osipov * 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 #include #include #include #include #include #include #include #include #include #include #include #include "../config.h" #ifdef USE_IFACEWATCHER #include "../ifacewatcher.h" #include "../minissdp.h" #include "../getifaddr.h" #include "../upnpglobalvars.h" #include "../natpmp.h" extern volatile sig_atomic_t should_send_public_address_change_notif; int OpenAndConfInterfaceWatchSocket(void) { int s; struct sockaddr_nl addr; s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (s == -1) { syslog(LOG_ERR, "socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE): %m"); return -1; } memset(&addr, 0, sizeof(addr)); addr.nl_family = AF_NETLINK; addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR; /*addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR;*/ if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { syslog(LOG_ERR, "bind(netlink): %m"); close(s); return -1; } return s; } #if 0 /* disabled at the moment */ int ProcessInterfaceUp(struct ifinfomsg *ifi) { struct lan_iface_s * lan_iface; struct lan_iface_s * lan_iface2; struct lan_addr_s * lan_addr; char ifname[IFNAMSIZ]; char ifstraddr[16]; struct in_addr ifaddr; /* check if we already have this iface */ for(lan_iface = lan_ifaces.lh_first; lan_iface != NULL; lan_iface = lan_iface->list.le_next) if (lan_iface->iface.index == ifi->ifi_index) break; if (lan_iface != NULL) return 0; if (if_indextoname(ifi->ifi_index, ifname) == NULL) { syslog(LOG_ERR, "if_indextoname(%d, ifname) failed", ifi->ifi_index); return -1; } if (getifaddr(ifname, ifstraddr, 16) < 0) { syslog(LOG_DEBUG, "getifaddr(%s, ifaddr, 16) failed", ifname); return 1; } if (inet_pton(AF_INET, ifstraddr, &ifaddr) != 1) { syslog(LOG_ERR, "inet_pton(AF_INET, \"%s\", &ifaddr) failed", ifstraddr); return -1; } /* check if this new interface has address which we need to listen to */ for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next) { if (lan_addr->addr.s_addr != ifaddr.s_addr) continue; syslog(LOG_INFO, "Interface up: %s (%s)", ifname, ifstraddr); /* adding new lan_iface entry */ lan_iface = (struct lan_iface_s *) malloc(sizeof(struct lan_iface_s)); if (lan_iface == NULL) { syslog(LOG_ERR, "malloc(sizeof(struct lan_iface_s): %m"); continue; } lan_iface->lan_addr = lan_addr; strncpy(lan_iface->iface.name, ifname, IFNAMSIZ); lan_iface->iface.index = ifi->ifi_index; lan_iface->iface.addr = ifaddr; lan_iface->snotify = -1; #ifdef ENABLE_NATPMP lan_iface->snatpmp = -1; #endif LIST_INSERT_HEAD(&lan_ifaces, lan_iface, list); /* adding multicast membership for SSDP */ if(AddMulticastMembership(sudp, ifaddr.s_addr, ifi->ifi_index) < 0) syslog(LOG_WARNING, "Failed to add multicast membership for interface %s (%s)", ifname, ifstraddr); else syslog(LOG_INFO, "Multicast membership added for %s (%s)", ifname, ifstraddr); /* create SSDP notify socket */ if (OpenAndConfSSDPNotifySocket(lan_iface) < 0) syslog(LOG_WARNING, "Failed to open SSDP notify socket for interface %s (%s)", ifname, ifstraddr); #ifdef ENABLE_NATPMP /* create NAT-PMP socket */ for(lan_iface2 = lan_ifaces.lh_first; lan_iface2 != NULL; lan_iface2 = lan_iface2->list.le_next) if (lan_iface2->lan_addr->addr.s_addr == lan_iface->lan_addr->addr.s_addr && lan_iface2->snatpmp >= 0) lan_iface->snatpmp = lan_iface2->snatpmp; if (lan_iface->snatpmp < 0) { lan_iface->snatpmp = OpenAndConfNATPMPSocket(ifaddr.s_addr); if (lan_iface->snatpmp < 0) syslog(LOG_ERR, "OpenAndConfNATPMPSocket(ifaddr.s_addr) failed for %s (%s)", ifname, ifstraddr); else syslog(LOG_INFO, "Listening for NAT-PMP connection on %s:%d", ifstraddr, NATPMP_PORT); } #endif } return 0; } int ProcessInterfaceDown(struct ifinfomsg *ifi) { struct lan_iface_s * lan_iface; struct lan_iface_s * lan_iface2; /* check if we have this iface */ for(lan_iface = lan_ifaces.lh_first; lan_iface != NULL; lan_iface = lan_iface->list.le_next) if (lan_iface->iface.index == ifi->ifi_index) break; if (lan_iface == NULL) return 0; /* one of our interfaces is going down, clean up */ syslog(LOG_INFO, "Interface down: %s", lan_iface->iface.name); /* remove multicast membership for SSDP */ if(DropMulticastMembership(sudp, lan_iface->lan_addr->addr.s_addr, lan_iface->iface.index) < 0) syslog(LOG_WARNING, "Failed to drop multicast membership for interface %s (%s)", lan_iface->iface.name, lan_iface->lan_addr->str); else syslog(LOG_INFO, "Multicast membership dropped for %s (%s)", lan_iface->iface.name, lan_iface->lan_addr->str); /* closing SSDP notify socket */ close(lan_iface->snotify); #ifdef ENABLE_NATPMP /* closing NAT-PMP socket if it's not used anymore */ for(lan_iface2 = lan_ifaces.lh_first; lan_iface2 != NULL; lan_iface2 = lan_iface2->list.le_next) if (lan_iface2 != lan_iface && lan_iface2->snatpmp == lan_iface->snatpmp) break; if (lan_iface2 == NULL) close(lan_iface->snatpmp); #endif /* del corresponding lan_iface entry */ LIST_REMOVE(lan_iface, list); free(lan_iface); return 0; } #endif void ProcessInterfaceWatchNotify(int s) { char buffer[4096]; struct iovec iov; struct msghdr hdr; struct nlmsghdr *nlhdr; struct ifinfomsg *ifi; struct ifaddrmsg *ifa; int len; struct rtattr *rth; int rtl; unsigned int ext_if_name_index = 0; iov.iov_base = buffer; iov.iov_len = sizeof(buffer); memset(&hdr, 0, sizeof(hdr)); hdr.msg_iov = &iov; hdr.msg_iovlen = 1; len = recvmsg(s, &hdr, 0); if (len < 0) { syslog(LOG_ERR, "recvmsg(s, &hdr, 0): %m"); return; } if(ext_if_name) { ext_if_name_index = if_nametoindex(ext_if_name); } for (nlhdr = (struct nlmsghdr *) buffer; NLMSG_OK (nlhdr, (unsigned int)len); nlhdr = NLMSG_NEXT (nlhdr, len)) { int is_del = 0; char address[48]; char ifname[IFNAMSIZ]; address[0] = '\0'; ifname[0] = '\0'; if (nlhdr->nlmsg_type == NLMSG_DONE) break; switch(nlhdr->nlmsg_type) { case RTM_DELLINK: is_del = 1; case RTM_NEWLINK: ifi = (struct ifinfomsg *) NLMSG_DATA(nlhdr); #if 0 if(is_del) { if(ProcessInterfaceDown(ifi) < 0) syslog(LOG_ERR, "ProcessInterfaceDown(ifi) failed"); } else { if(ProcessInterfaceUp(ifi) < 0) syslog(LOG_ERR, "ProcessInterfaceUp(ifi) failed"); } #endif break; case RTM_DELADDR: is_del = 1; case RTM_NEWADDR: /* see /usr/include/linux/netlink.h * and /usr/include/linux/rtnetlink.h */ ifa = (struct ifaddrmsg *) NLMSG_DATA(nlhdr); syslog(LOG_DEBUG, "%s %s index=%d fam=%d", "ProcessInterfaceWatchNotify", is_del ? "RTM_DELADDR" : "RTM_NEWADDR", ifa->ifa_index, ifa->ifa_family); for(rth = IFA_RTA(ifa), rtl = IFA_PAYLOAD(nlhdr); rtl && RTA_OK(rth, rtl); rth = RTA_NEXT(rth, rtl)) { char tmp[128]; memset(tmp, 0, sizeof(tmp)); switch(rth->rta_type) { case IFA_ADDRESS: case IFA_LOCAL: case IFA_BROADCAST: case IFA_ANYCAST: inet_ntop(ifa->ifa_family, RTA_DATA(rth), tmp, sizeof(tmp)); if(rth->rta_type == IFA_ADDRESS) strncpy(address, tmp, sizeof(address)); break; case IFA_LABEL: strncpy(tmp, RTA_DATA(rth), sizeof(tmp)); strncpy(ifname, tmp, sizeof(ifname)); break; case IFA_CACHEINFO: { struct ifa_cacheinfo *cache_info; cache_info = RTA_DATA(rth); snprintf(tmp, sizeof(tmp), "valid=%u prefered=%u", cache_info->ifa_valid, cache_info->ifa_prefered); } break; default: strncpy(tmp, "*unknown*", sizeof(tmp)); } syslog(LOG_DEBUG, " - %u - %s type=%d", ifa->ifa_index, tmp, rth->rta_type); } if(ifa->ifa_index == ext_if_name_index) { should_send_public_address_change_notif = 1; } break; default: syslog(LOG_DEBUG, "%s type %d ignored", "ProcessInterfaceWatchNotify", nlhdr->nlmsg_type); } } } #endif miniupnpd-1.8.20130730/netfilter/tiny_nf_nat.h010064400017500000024000000015361161500167400201300ustar00nanardstaff/* $Id: tiny_nf_nat.h,v 1.1 2011/07/30 13:14:36 nanard Exp $ */ /* Only what miniupnpd needs, until linux-libc-dev gains nf_nat.h */ #ifndef TINY_NF_NAT_H #define TINY_NF_NAT_H #include #define IP_NAT_RANGE_MAP_IPS 1 #define IP_NAT_RANGE_PROTO_SPECIFIED 2 #define IP_NAT_RANGE_PROTO_RANDOM 4 #define IP_NAT_RANGE_PERSISTENT 8 union nf_conntrack_man_proto { __be16 all; struct { __be16 port; } tcp; struct { __be16 port; } udp; struct { __be16 id; } icmp; struct { __be16 port; } dccp; struct { __be16 port; } sctp; struct { __be16 key; } gre; }; struct nf_nat_range { unsigned int flags; __be32 min_ip, max_ip; union nf_conntrack_man_proto min, max; }; struct nf_nat_multi_range_compat { unsigned int rangesize; struct nf_nat_range range[1]; }; #define nf_nat_multi_range nf_nat_multi_range_compat #endif /*TINY_NF_NAT_H*/ miniupnpd-1.8.20130730/pf/pfpinhole.c010064400017500000024000000227001175646165500162230ustar00nanardstaff/* $Id: pfpinhole.c,v 1.19 2012/05/21 15:47:57 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2012 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #include #include #include #include #include #include #ifdef __DragonFly__ #include #else #include #endif #include #include #include #include #include #include #include #include "../config.h" #include "pfpinhole.h" #include "../upnpglobalvars.h" /* the pass rules created by add_pinhole() are as follow : * * pass in quick on ep0 inet6 proto udp * from any to dead:beef::42:42 port = 8080 * flags S/SA keep state * label "pinhole-2 ts-4321000" * * with the label "pinhole-$uid ts-$timestamp" */ #ifdef ENABLE_6FC_SERVICE /* /dev/pf when opened */ extern int dev; static int next_uid = 1; #define PINEHOLE_LABEL_FORMAT "pinhole-%d ts-%u" 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 uid; struct pfioc_rule pcr; #ifndef PF_NEWSTYLE struct pfioc_pooladdr pp; #endif if(dev<0) { syslog(LOG_ERR, "pf device is not open"); return -1; } memset(&pcr, 0, sizeof(pcr)); strlcpy(pcr.anchor, anchor_name, MAXPATHLEN); #ifndef PF_NEWSTYLE memset(&pp, 0, sizeof(pp)); strlcpy(pp.anchor, anchor_name, MAXPATHLEN); if(ioctl(dev, DIOCBEGINADDRS, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCBEGINADDRS, ...): %m"); return -1; } else { pcr.pool_ticket = pp.ticket; #else { #endif pcr.rule.direction = PF_IN; pcr.rule.action = PF_PASS; pcr.rule.af = AF_INET6; #ifdef PF_NEWSTYLE pcr.rule.nat.addr.type = PF_ADDR_NONE; pcr.rule.rdr.addr.type = PF_ADDR_NONE; #endif #ifdef USE_IFNAME_IN_RULES if(ifname) strlcpy(pcr.rule.ifname, ifname, IFNAMSIZ); #endif pcr.rule.proto = proto; pcr.rule.quick = 1;/*(GETFLAG(PFNOQUICKRULESMASK))?0:1;*/ pcr.rule.log = (GETFLAG(LOGPACKETSMASK))?1:0; /*logpackets;*/ /* see the discussion on the forum : * http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=638 */ pcr.rule.flags = TH_SYN; pcr.rule.flagset = (TH_SYN|TH_ACK); #ifdef PFRULE_HAS_RTABLEID pcr.rule.rtableid = -1; /* first appeared in OpenBSD 4.0 */ #endif #ifdef PFRULE_HAS_ONRDOMAIN pcr.rule.onrdomain = -1; /* first appeared in OpenBSD 5.0 */ #endif pcr.rule.keep_state = 1; uid = next_uid; snprintf(pcr.rule.label, PF_RULE_LABEL_SIZE, PINEHOLE_LABEL_FORMAT, uid, timestamp); if(queue) strlcpy(pcr.rule.qname, queue, PF_QNAME_SIZE); if(tag) strlcpy(pcr.rule.tagname, tag, PF_TAG_NAME_SIZE); if(rem_port) { pcr.rule.src.port_op = PF_OP_EQ; pcr.rule.src.port[0] = htons(rem_port); } if(rem_host && rem_host[0] != '\0' && rem_host[0] != '*') { pcr.rule.src.addr.type = PF_ADDR_ADDRMASK; if(inet_pton(AF_INET6, rem_host, &pcr.rule.src.addr.v.a.addr.v6) != 1) { syslog(LOG_ERR, "inet_pton(%s) failed", rem_host); } memset(&pcr.rule.src.addr.v.a.mask.addr8, 255, 16); } pcr.rule.dst.port_op = PF_OP_EQ; pcr.rule.dst.port[0] = htons(int_port); pcr.rule.dst.addr.type = PF_ADDR_ADDRMASK; if(inet_pton(AF_INET6, int_client, &pcr.rule.dst.addr.v.a.addr.v6) != 1) { syslog(LOG_ERR, "inet_pton(%s) failed", int_client); } memset(&pcr.rule.dst.addr.v.a.mask.addr8, 255, 16); if(ifname) strlcpy(pcr.rule.ifname, ifname, IFNAMSIZ); pcr.action = PF_CHANGE_GET_TICKET; if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m"); return -1; } else { pcr.action = PF_CHANGE_ADD_TAIL; if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_ADD_TAIL: %m"); return -1; } } } if(++next_uid >= 65535) { next_uid = 1; } return uid; } int delete_pinhole(unsigned short uid) { int i, n; struct pfioc_rule pr; char label_start[PF_RULE_LABEL_SIZE]; char tmp_label[PF_RULE_LABEL_SIZE]; if(dev<0) { syslog(LOG_ERR, "pf device is not open"); return -1; } snprintf(label_start, sizeof(label_start), "pinhole-%hu", uid); memset(&pr, 0, sizeof(pr)); strlcpy(pr.anchor, anchor_name, MAXPATHLEN); #ifndef PF_NEWSTYLE pr.rule.action = PF_PASS; #endif if(ioctl(dev, DIOCGETRULES, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m"); return -1; } n = pr.nr; for(i=0; i= 0; i--) { pr.nr = i; if(ioctl(dev, DIOCGETRULE, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m"); return -1; } if(sscanf(pr.rule.label, PINEHOLE_LABEL_FORMAT, &uid, &ts) != 2) { syslog(LOG_INFO, "rule with label '%s' is not a IGD pinhole", pr.rule.label); continue; } if(ts <= (unsigned int)current_time) { syslog(LOG_INFO, "removing expired pinhole '%s'", pr.rule.label); pr.action = PF_CHANGE_GET_TICKET; if(ioctl(dev, DIOCCHANGERULE, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m"); return -1; } pr.action = PF_CHANGE_REMOVE; pr.nr = i; if(ioctl(dev, DIOCCHANGERULE, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_REMOVE: %m"); return -1; } n++; #ifndef PF_NEWSTYLE pr.rule.action = PF_PASS; #endif if(ioctl(dev, DIOCGETRULES, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m"); return -1; } } else { if(uid > max_uid) max_uid = uid; else if(uid < min_uid) min_uid = uid; if(ts < min_ts) min_ts = ts; } } if(next_timestamp && (min_ts != UINT_MAX)) *next_timestamp = min_ts; if(max_uid > 0) { if(((min_uid - 32000) <= next_uid) && (next_uid <= max_uid)) { next_uid = max_uid + 1; } if(next_uid >= 65535) { next_uid = 1; } } return n; /* number of rules removed */ } #endif /* ENABLE_IPV6 */ miniupnpd-1.8.20130730/pf/pfpinhole.h010064400017500000024000000020761203340734100162110ustar00nanardstaff/* $Id: pfpinhole.h,v 1.10 2012/09/27 15:44:10 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2012 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #ifndef PFPINHOLE_H_INCLUDED #define PFPINHOLE_H_INCLUDED #ifdef ENABLE_6FC_SERVICE 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 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, u_int64_t * packets, u_int64_t * bytes); int update_pinhole(unsigned short uid, unsigned int timestamp); int clean_pinhole_list(unsigned int * next_timestamp); #endif #endif miniupnpd-1.8.20130730/pf/testpfpinhole.c010064400017500000024000000043151174511016300171040ustar00nanardstaff/* $Id: testpfpinhole.c,v 1.10 2012/04/22 23:12:51 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2012 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #include #include #include #include #include "../config.h" #include "obsdrdr.h" #include "pfpinhole.h" int runtime_flags = 0; const char * tag = NULL; const char * anchor_name = "miniupnpd"; const char * queue = NULL; static int print_pinhole(int uid) { int r; char rem_host[64]; unsigned short rem_port; char int_client[64]; unsigned short int_port; int proto; unsigned int timestamp; u_int64_t packets, bytes; r = get_pinhole((unsigned short)uid, rem_host, sizeof(rem_host), &rem_port, int_client, sizeof(int_client), &int_port, &proto, ×tamp, &packets, &bytes); if(r < 0) { fprintf(stderr, "get_pinhole(%d) returned %d\n", uid, r); } else { printf("pinhole %d : [%s]:%hu => [%s]:%hu proto=%d ts=%u\n", uid, rem_host, rem_port, int_client, int_port, proto, timestamp); printf(" packets=%llu bytes=%llu\n", packets, bytes); } return r; } int main(int argc, char * *argv) { #ifndef ENABLE_IPV6 fprintf(stderr,"nothing to test, ENABLE_IPV6 is not defined in config.h\n"); return 1; #else int uid; int ret; openlog("testpfpinhole", LOG_PERROR, LOG_USER); if(init_redirect() < 0) { fprintf(stderr, "init_redirect() failed\n"); return 1; } uid = add_pinhole("ep0", "2001::1:2:3", 12345, "123::ff", 54321, IPPROTO_UDP, 424242); if(uid < 0) { fprintf(stderr, "add_pinhole() failed\n"); } printf("add_pinhole() returned %d\n", uid); uid = add_pinhole("ep0", NULL, 0, "dead:beef::42:42", 8080, IPPROTO_UDP, 4321000); if(uid < 0) { fprintf(stderr, "add_pinhole() failed\n"); } printf("add_pinhole() returned %d\n", uid); print_pinhole(1); print_pinhole(2); clean_pinhole_list(NULL); ret = delete_pinhole(1); printf("delete_pinhole() returned %d\n", ret); ret = delete_pinhole(2); printf("delete_pinhole() returned %d\n", ret); #endif return 0; } miniupnpd-1.8.20130730/netfilter/ip6tables_display.sh010075500017500000024000000003371174562262500214330ustar00nanardstaff#! /bin/sh # $Id: ip6tables_display.sh,v 1.1 2012/04/24 22:13:41 nanard Exp $ IPTABLES=/sbin/ip6tables #display all chains relative to miniupnpd $IPTABLES -v -n -t filter -L FORWARD $IPTABLES -v -n -t filter -L MINIUPNPD miniupnpd-1.8.20130730/netfilter/ip6tables_flush.sh010075500017500000024000000002541174562262500211050ustar00nanardstaff#! /bin/sh # $Id: ip6tables_flush.sh,v 1.1 2012/04/24 22:13:41 nanard Exp $ IPTABLES=/sbin/ip6tables #flush all rules owned by miniupnpd $IPTABLES -t filter -F MINIUPNPD miniupnpd-1.8.20130730/netfilter/ip6tables_init_and_clean.sh010075500017500000024000000014761174562262500227220ustar00nanardstaff#! /bin/sh # $Id: ip6tables_init_and_clean.sh,v 1.1 2012/04/24 22:13:41 nanard Exp $ # Improved Miniupnpd iptables init script. # Checks for state of filter before doing anything.. EXTIF=eth0 IPTABLES=/sbin/ip6tables FDIRTY="`LC_ALL=C /sbin/ip6tables -t filter -L -n | grep 'MINIUPNPD' | awk '{printf $1}'`" if [[ $FDIRTY = "MINIUPNPDChain" ]]; then echo "Filter table dirty; Cleaning..." $IPTABLES -t filter -F MINIUPNPD elif [[ $FDIRTY = "Chain" ]]; then echo "Dirty filter chain but no reference..? Fixsted." $IPTABLES -t filter -I FORWARD 4 -i $EXTIF ! -o $EXTIF -j MINIUPNPD $IPTABLES -t filter -F MINIUPNPD else echo "Filter table clean..initalizing.." $IPTABLES -t filter -N MINIUPNPD $IPTABLES -t filter -I FORWARD 4 -i $EXTIF ! -o $EXTIF -j MINIUPNPD fi miniupnpd-1.8.20130730/netfilter/ip6tables_init.sh010075500017500000024000000004641174562262500207320ustar00nanardstaff#! /bin/sh # $Id: ip6tables_init.sh,v 1.1 2012/04/24 22:13:41 nanard Exp $ IPTABLES=/sbin/ip6tables #change this parameters : EXTIF=eth0 #adding the MINIUPNPD chain for filter $IPTABLES -t filter -N MINIUPNPD #adding the rule to MINIUPNPD $IPTABLES -t filter -A FORWARD -i $EXTIF ! -o $EXTIF -j MINIUPNPD miniupnpd-1.8.20130730/netfilter/ip6tables_removeall.sh010075500017500000024000000005341174562262500217530ustar00nanardstaff#! /bin/sh # $Id: ip6tables_removeall.sh,v 1.1 2012/04/24 22:13:41 nanard Exp $ IPTABLES=/sbin/ip6tables #change this parameters : EXTIF=eth0 #removing the MINIUPNPD chain for filter $IPTABLES -t filter -F MINIUPNPD #adding the rule to MINIUPNPD $IPTABLES -t filter -D FORWARD -i $EXTIF ! -o $EXTIF -j MINIUPNPD $IPTABLES -t filter -X MINIUPNPD miniupnpd-1.8.20130730/netfilter/iptpinhole.c010064400017500000024000000244401203340734100177610ustar00nanardstaff/* $Id: iptpinhole.c,v 1.8 2012/09/18 08:29:17 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2012 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #include #include #include #include #include #include "../config.h" #include "iptpinhole.h" #include "../upnpglobalvars.h" #ifdef ENABLE_6FC_SERVICE #include #include #include "tiny_nf_nat.h" #define IP6TC_HANDLE struct ip6tc_handle * static int next_uid = 1; static LIST_HEAD(pinhole_list_t, pinhole_t) pinhole_list; static struct pinhole_t * get_pinhole(unsigned short uid); struct pinhole_t { struct in6_addr saddr; struct in6_addr daddr; LIST_ENTRY(pinhole_t) entries; unsigned int timestamp; unsigned short sport; unsigned short dport; unsigned short uid; unsigned char proto; }; void init_iptpinhole(void) { LIST_INIT(&pinhole_list); } void shutdown_iptpinhole(void) { /* TODO empty list */ } /* return uid */ 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) { struct pinhole_t * p; p = calloc(1, sizeof(struct pinhole_t)); if(!p) { syslog(LOG_ERR, "add_to_pinhole_list calloc() error"); return -1; } memcpy(&p->saddr, saddr, sizeof(struct in6_addr)); p->sport = sport; memcpy(&p->daddr, daddr, sizeof(struct in6_addr)); p->dport = dport; p->timestamp = timestamp; p->proto = (unsigned char)proto; LIST_INSERT_HEAD(&pinhole_list, p, entries); while(get_pinhole(next_uid) != NULL) { next_uid++; if(next_uid > 65535) next_uid = 1; } p->uid = next_uid; next_uid++; if(next_uid > 65535) next_uid = 1; return p->uid; } static struct pinhole_t * get_pinhole(unsigned short uid) { struct pinhole_t * p; for(p = pinhole_list.lh_first; p != NULL; p = p->entries.le_next) { if(p->uid == uid) return p; } return NULL; /* not found */ } /* new_match() * Allocate and set a new ip6t_entry_match structure * The caller must free() it after usage */ static struct ip6t_entry_match * new_match(int proto, unsigned short sport, unsigned short dport) { struct ip6t_entry_match *match; struct ip6t_tcp *info; /* TODO : use ip6t_udp if needed */ size_t size; const char * name; size = XT_ALIGN(sizeof(struct ip6t_entry_match)) + XT_ALIGN(sizeof(struct ip6t_tcp)); match = calloc(1, size); match->u.user.match_size = size; switch(proto) { case IPPROTO_TCP: name = "tcp"; break; case IPPROTO_UDP: name = "udp"; break; case IPPROTO_UDPLITE: name = "udplite"; break; default: name = NULL; } if(name) strncpy(match->u.user.name, name, sizeof(match->u.user.name)); else syslog(LOG_WARNING, "no name for protocol %d", proto); info = (struct ip6t_tcp *)match->data; if(sport) { info->spts[0] = sport; /* specified source port */ info->spts[1] = sport; } else { info->spts[0] = 0; /* all source ports */ info->spts[1] = 0xFFFF; } info->dpts[0] = dport; /* specified destination port */ info->dpts[1] = dport; return match; } static struct ip6t_entry_target * get_accept_target(void) { struct ip6t_entry_target * target = NULL; size_t size; size = XT_ALIGN(sizeof(struct ip6t_entry_target)) + XT_ALIGN(sizeof(int)); target = calloc(1, size); target->u.user.target_size = size; strncpy(target->u.user.name, "ACCEPT", sizeof(target->u.user.name)); return target; } static int ip6tc_init_verify_append(const char * table, const char * chain, struct ip6t_entry * e) { IP6TC_HANDLE h; h = ip6tc_init(table); if(!h) { syslog(LOG_ERR, "ip6tc_init error : %s", ip6tc_strerror(errno)); return -1; } if(!ip6tc_is_chain(chain, h)) { syslog(LOG_ERR, "chain %s not found", chain); goto error; } if(!ip6tc_append_entry(chain, e, h)) { syslog(LOG_ERR, "ip6tc_append_entry() error : %s", ip6tc_strerror(errno)); goto error; } if(!ip6tc_commit(h)) { syslog(LOG_ERR, "ip6tc_commit() error : %s", ip6tc_strerror(errno)); goto error; } return 0; /* ok */ error: ip6tc_free(h); return -1; } /* ip6tables -I %s %d -p %s -i %s -s %s --sport %hu -d %s --dport %hu -j ACCEPT ip6tables -I %s %d -p %s -i %s --sport %hu -d %s --dport %hu -j ACCEPT miniupnpd_forward_chain, line_number, proto, ext_if_name, raddr, rport, iaddr, iport ip6tables -t raw -I PREROUTING %d -p %s -i %s -s %s --sport %hu -d %s --dport %hu -j TRACE ip6tables -t raw -I PREROUTING %d -p %s -i %s --sport %hu -d %s --dport %hu -j TRACE */ 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 uid; struct ip6t_entry * e; struct ip6t_entry_match *match = NULL; struct ip6t_entry_target *target = NULL; e = calloc(1, sizeof(struct ip6t_entry)); e->ipv6.proto = proto; if(ifname) strncpy(e->ipv6.iniface, ifname, IFNAMSIZ); if(rem_host) { inet_pton(AF_INET6, rem_host, &e->ipv6.src); memset(&e->ipv6.smsk, 0xff, sizeof(e->ipv6.smsk)); } inet_pton(AF_INET6, int_client, &e->ipv6.dst); memset(&e->ipv6.dmsk, 0xff, sizeof(e->ipv6.dmsk)); /*e->nfcache = NFC_IP_DST_PT;*/ /*e->nfcache |= NFC_UNKNOWN;*/ match = new_match(proto, rem_port, int_port); target = get_accept_target(); e = realloc(e, sizeof(struct ip6t_entry) + match->u.match_size + target->u.target_size); memcpy(e->elems, match, match->u.match_size); memcpy(e->elems + match->u.match_size, target, target->u.target_size); e->target_offset = sizeof(struct ip6t_entry) + match->u.match_size; e->next_offset = sizeof(struct ip6t_entry) + match->u.match_size + target->u.target_size; free(match); free(target); if(ip6tc_init_verify_append("filter", miniupnpd_v6_filter_chain, e) < 0) { free(e); return -1; } uid = add_to_pinhole_list(&e->ipv6.src, rem_port, &e->ipv6.dst, int_port, proto, timestamp); free(e); return uid; } int delete_pinhole(unsigned short uid) { struct pinhole_t * p; IP6TC_HANDLE h; const struct ip6t_entry * e; const struct ip6t_entry_match *match = NULL; /*const struct ip6t_entry_target *target = NULL;*/ unsigned int index; p = get_pinhole(uid); if(!p) return -2; /* not found */ h = ip6tc_init("filter"); if(!h) { syslog(LOG_ERR, "ip6tc_init error : %s", ip6tc_strerror(errno)); return -1; } if(!ip6tc_is_chain(miniupnpd_v6_filter_chain, h)) { syslog(LOG_ERR, "chain %s not found", miniupnpd_v6_filter_chain); goto error; } index = 0; for(e = ip6tc_first_rule(miniupnpd_v6_filter_chain, h); e; e = ip6tc_next_rule(e, h)) { if((e->ipv6.proto == p->proto) && (0 == memcmp(&e->ipv6.src, &p->saddr, sizeof(e->ipv6.src))) && (0 == memcmp(&e->ipv6.dst, &p->daddr, sizeof(e->ipv6.dst)))) { const struct ip6t_tcp * info; match = (const struct ip6t_entry_match *)&e->elems; info = (const struct ip6t_tcp *)&match->data; if((info->spts[0] == p->sport) && (info->dpts[0] == p->dport)) { if(!ip6tc_delete_num_entry(miniupnpd_v6_filter_chain, index, h)) { syslog(LOG_ERR, "ip6tc_delete_num_entry(%s,%d,...): %s", miniupnpd_v6_filter_chain, index, ip6tc_strerror(errno)); goto error; } if(!ip6tc_commit(h)) { syslog(LOG_ERR, "ip6tc_commit(): %s", ip6tc_strerror(errno)); goto error; } ip6tc_free(h); LIST_REMOVE(p, entries); return 0; /* ok */ } } index++; } ip6tc_free(h); syslog(LOG_WARNING, "delete_pinhole() rule with PID=%hu not found", uid); return -2; /* not found */ error: ip6tc_free(h); return -1; } int update_pinhole(unsigned short uid, unsigned int timestamp) { struct pinhole_t * p; p = get_pinhole(uid); if(p) { p->timestamp = timestamp; return 0; } else { return -2; /* Not found */ } } 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, u_int64_t * packets, u_int64_t * bytes) { struct pinhole_t * p; p = get_pinhole(uid); if(!p) return -2; /* Not found */ if(rem_host) { if(inet_ntop(AF_INET6, &p->saddr, rem_host, rem_hostlen) == NULL) return -1; } if(rem_port) *rem_port = p->sport; if(int_client) { if(inet_ntop(AF_INET6, &p->daddr, int_client, int_clientlen) == NULL) return -1; } if(int_port) *int_port = p->dport; if(proto) *proto = p->proto; if(timestamp) *timestamp = p->timestamp; if(packets || bytes) { /* theses informations need to be read from netfilter */ IP6TC_HANDLE h; const struct ip6t_entry * e; const struct ip6t_entry_match * match; h = ip6tc_init("filter"); if(!h) { syslog(LOG_ERR, "ip6tc_init error : %s", ip6tc_strerror(errno)); return -1; } for(e = ip6tc_first_rule(miniupnpd_v6_filter_chain, h); e; e = ip6tc_next_rule(e, h)) { if((e->ipv6.proto == p->proto) && (0 == memcmp(&e->ipv6.src, &p->saddr, sizeof(e->ipv6.src))) && (0 == memcmp(&e->ipv6.dst, &p->daddr, sizeof(e->ipv6.dst)))) { const struct ip6t_tcp * info; match = (const struct ip6t_entry_match *)&e->elems; info = (const struct ip6t_tcp *)&match->data; if((info->spts[0] == p->sport) && (info->dpts[0] == p->dport)) { if(packets) *packets = e->counters.pcnt; if(bytes) *bytes = e->counters.bcnt; break; } } } ip6tc_free(h); } return 0; } int clean_pinhole_list(unsigned int * next_timestamp) { unsigned int min_ts = UINT_MAX; struct pinhole_t * p; time_t current_time; int n = 0; current_time = time(NULL); p = pinhole_list.lh_first; while(p != NULL) { if(p->timestamp <= (unsigned int)current_time) { unsigned short uid = p->uid; syslog(LOG_INFO, "removing expired pinhole with uid=%hu", uid); p = p->entries.le_next; if(delete_pinhole(uid) == 0) n++; else break; } else { if(p->timestamp < min_ts) min_ts = p->timestamp; p = p->entries.le_next; } } if(next_timestamp && (min_ts != UINT_MAX)) *next_timestamp = min_ts; return n; } #endif miniupnpd-1.8.20130730/netfilter/iptpinhole.h010064400017500000024000000020761203340734100177670ustar00nanardstaff/* $Id: iptpinhole.h,v 1.6 2012/09/27 15:44:10 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2012 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #ifndef IPTPINHOLE_H_INCLUDED #define IPTPINHOLE_H_INCLUDED #ifdef ENABLE_6FC_SERVICE 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 update_pinhole(unsigned short uid, unsigned int timestamp); 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, u_int64_t * packets, u_int64_t * bytes); int clean_pinhole_list(unsigned int * next_timestamp); #endif #endif miniupnpd-1.8.20130730/netfilter/testiptpinhole.c010064400017500000024000000012011174625127000206570ustar00nanardstaff/* $Id: testiptpinhole.c,v 1.1 2012/04/26 13:50:48 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2012 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #include #include #include #include "../config.h" #include "iptpinhole.h" #include "../commonrdr.h" int main(int argc, char * * argv) { int uid; openlog("testiptpinhole", LOG_PERROR|LOG_CONS, LOG_LOCAL0); uid = add_pinhole("eth0", NULL, 0, "ff::123", 54321, IPPROTO_TCP); return 0; } miniupnpd-1.8.20130730/macros.h010064400017500000024000000005711203340734000151010ustar00nanardstaff/* $Id: macros.h,v 1.2 2012/09/27 16:00:10 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2012 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #ifndef MACROS_H_INCLUDED #define MACROS_H_INCLUDED #define UNUSED(arg) (void)(arg) #endif miniupnpd-1.8.20130730/upnppinhole.c010064400017500000024000000335621175242533400161710ustar00nanardstaff/* $Id: upnppinhole.c,v 1.4 2012/05/08 20:41:45 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2012 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #include #include #include #include #include #include #include #include #include #include #include "macros.h" #include "config.h" #include "upnpredirect.h" #include "upnpglobalvars.h" #include "upnpevents.h" #if defined(USE_NETFILTER) #include "netfilter/iptpinhole.h" #endif #if defined(USE_PF) #include "pf/pfpinhole.h" #endif #if defined(USE_IPF) #endif #if defined(USE_IPFW) #endif #ifdef ENABLE_6FC_SERVICE #if 0 int upnp_check_outbound_pinhole(int proto, int * timeout) { int s, tmptimeout, tmptime_out; switch(proto) { case IPPROTO_UDP: s = retrieve_timeout("udp_timeout", timeout); return s; break; case IPPROTO_UDPLITE: s = retrieve_timeout("udp_timeout_stream", timeout); return s; break; case IPPROTO_TCP: s = retrieve_timeout("tcp_timeout_established", timeout); return s; break; case 65535: s = retrieve_timeout("udp_timeout", timeout); s = retrieve_timeout("udp_timeout_stream", &tmptimeout); s = retrieve_timeout("tcp_timeout_established", &tmptime_out); if(tmptimeout %s", cmd); syslog(LOG_INFO, " -> %s", cmd_raw); #endif /* TODO Add a better checking error.*/ if(system(cmd) < 0 || system(cmd_raw) < 0) { return 0; } srand(time(NULL)); snprintf(cuid, sizeof(cuid), "%.4d", rand()%c); *uid = atoi(cuid); printf("\t_add_ uid: %s\n", cuid); return 1; } #endif /* upnp_get_pinhole_info() * return values : * 0 OK * -1 Internal error * -2 NOT FOUND (no such entry) * .. * -42 Not implemented */ int upnp_get_pinhole_info(unsigned short uid, char * raddr, int raddrlen, unsigned short * rport, char * iaddr, int iaddrlen, unsigned short * iport, int * proto, unsigned int * leasetime, unsigned int * packets) { /* Call Firewall specific code to get IPv6 pinhole infos */ #if defined(USE_PF) || defined(USE_NETFILTER) int r; unsigned int timestamp; u_int64_t packets_tmp; /*u_int64_t bytes_tmp;*/ r = get_pinhole_info(uid, raddr, raddrlen, rport, iaddr, iaddrlen, iport, proto, leasetime ? ×tamp : NULL, packets ? &packets_tmp : NULL, NULL/*&bytes_tmp*/); if(r >= 0) { if(leasetime) { time_t current_time; current_time = time(NULL); if(timestamp > (unsigned int)current_time) *leasetime = timestamp - current_time; else *leasetime = 0; } if(packets) *packets = (unsigned int)packets_tmp; } return r; #else UNUSED(uid); UNUSED(raddr); UNUSED(raddrlen); UNUSED(rport); UNUSED(iaddr); UNUSED(iaddrlen); UNUSED(iport); UNUSED(proto); UNUSED(leasetime); UNUSED(packets); return -42; /* not implemented */ #endif } int upnp_update_inboundpinhole(unsigned short uid, unsigned int leasetime) { #if defined(USE_PF) || defined(USE_NETFILTER) unsigned int timestamp; timestamp = time(NULL) + leasetime; return update_pinhole(uid, timestamp); #else UNUSED(uid); UNUSED(leasetime); return -42; /* not implemented */ #endif } int upnp_delete_inboundpinhole(unsigned short uid) { #if defined(USE_PF) || defined(USE_NETFILTER) return delete_pinhole(uid); #else UNUSED(uid); return -1; #endif } #if 0 /* * Result: * 1: Found Result * -4: No result * -5: Result in another table * -6: Result in another chain * -7: Result in a chain not a rule */ int upnp_check_pinhole_working(const char * uid, char * eaddr, char * iaddr, unsigned short * eport, unsigned short * iport, char * protocol, int * rulenum_used) { /* TODO : to be implemented */ #if 0 FILE * fd; time_t expire = time(NULL); char buf[1024], filename[] = "/var/log/kern.log", expire_time[32]=""; int res = -4, str_len; str_len = strftime(expire_time, sizeof(expire_time), "%b %d %H:%M:%S", localtime(&expire)); fd = fopen(filename, "r"); if (fd==NULL) { syslog(LOG_ERR, "Get_rule: could not open file: %s", filename); return -1; } syslog(LOG_INFO, "Get_rule: Starting getting info in file %s for %s\n", filename, uid); buf[sizeof(buf)-1] = 0; while(fgets(buf, sizeof(buf)-1, fd) != NULL && res != 1) { //printf("line: %s\n", buf); char * r, * t, * c, * p; // looking for something like filter:FORWARD:rule: or filter:MINIUPNPD:rule: r = strstr(buf, ":rule:"); p = strstr(buf, ":policy:"); t = strstr(buf, "TRACE:"); // table pointeur t += 7; c = t + 7; // chain pointeur if(r) { printf("\t** Found %.*s\n", 24 ,t); char * src, * dst, * sport, * dport, * proto, * line; char time[15]="", src_addr[40], dst_addr[40], proto_tmp[8]; int proto_int; strncpy(time, buf, sizeof(time)); /*if(compare_time(time, expire_time)<0) { printf("\t\tNot corresponding time\n"); continue; }*/ line = r + 6; printf("\trule line = %d\n", atoi(line)); src = strstr(buf, "SRC="); src += 4; snprintf(src_addr, sizeof(src_addr), "%.*s", 39, src); #if 0 del_char(src_addr); add_char(src_addr); #endif dst = strstr(buf, "DST="); dst += 4; snprintf(dst_addr, sizeof(dst_addr), "%.*s", 39, dst); #if 0 del_char(dst_addr); add_char(dst_addr); #endif proto = strstr(buf, "PROTO="); proto += 6; proto_int = atoi(protocol); if(proto_int == IPPROTO_UDP) strcpy(proto_tmp, "UDP"); else if(proto_int == IPPROTO_TCP) strcpy(proto_tmp, "TCP"); #ifdef IPPROTO_UDPLITE else if(proto_int == IPPROTO_UDPLITE) strcpy(proto_tmp, "UDPLITE"); #endif else strcpy(proto_tmp, "UnsupportedProto"); // printf("\tCompare eaddr: %s // protocol: %s\n\t to addr: %s // protocol: %.*s\n", eaddr, proto_tmp, src_addr, strlen(proto_tmp), proto); // printf("\tCompare iaddr: %s // protocol: %s\n\t to addr: %s // protocol: %.*s\n", iaddr, proto_tmp, dst_addr, strlen(proto_tmp), proto); // TODO Check time // Check that the paquet found in trace correspond to the one we are looking for if( /*(strcmp(eaddr, src_addr) == 0) &&*/ (strcmp(iaddr, dst_addr) == 0) && (strncmp(proto_tmp, proto, strlen(proto_tmp))==0)) { sport = strstr(buf, "SPT="); sport += 4; dport = strstr(buf, "DPT="); dport += 4; printf("\tCompare eport: %hu\n\t to port: %d\n", *eport, atoi(sport)); printf("\tCompare iport: %hu\n\t to port: %d\n", *iport, atoi(dport)); if(/*eport != atoi(sport) &&*/ *iport != atoi(dport)) { printf("\t\tPort not corresponding\n"); continue; } printf("\ttable found: %.*s\n", 6, t); printf("\tchain found: %.*s\n", 9, c); // Check that the table correspond to the filter table if(strncmp(t, "filter", 6)==0) { // Check that the table correspond to the MINIUPNP table if(strncmp(c, "MINIUPNPD", 9)==0) { *rulenum_used = atoi(line); res = 1; } else { res = -6; continue; } } else { res = -5; continue; } } else { printf("Packet information not corresponding\n"); continue; } } if(!r && p) { printf("\t** Policy case\n"); char * src, * dst, * sport, * dport, * proto, * line; char time[15], src_addr[40], dst_addr[40], proto_tmp[8]; int proto_int; strncpy(time, buf, sizeof(time)); /*if(compare_time(time, expire_time)<0) { printf("\t\tNot corresponding time\n"); continue; }*/ line = p + 8; printf("\trule line = %d\n", atoi(line)); src = strstr(buf, "SRC="); src += 4; snprintf(src_addr, sizeof(src_addr), "%.*s", 39, src); #if 0 del_char(src_addr); add_char(src_addr); #endif dst = strstr(buf, "DST="); dst += 4; snprintf(dst_addr, sizeof(dst_addr), "%.*s", 39, dst); #if 0 del_char(dst_addr); add_char(dst_addr); #endif proto = strstr(buf, "PROTO="); proto += 6; proto_int = atoi(protocol); if(proto_int == IPPROTO_UDP) strcpy(proto_tmp, "UDP"); else if(proto_int == IPPROTO_TCP) strcpy(proto_tmp, "TCP"); #ifdef IPPROTO_UDPLITE else if(proto_int == IPPROTO_UDPLITE) strcpy(proto_tmp, "UDPLITE"); #endif else strcpy(proto_tmp, "UnsupportedProto"); // printf("\tCompare eaddr: %s // protocol: %s\n\t to addr: %s // protocol: %.*s\n", eaddr, proto_tmp, src_addr, strlen(proto_tmp), proto); // printf("\tCompare iaddr: %s // protocol: %s\n\t to addr: %s // protocol: %.*s\n", iaddr, proto_tmp, dst_addr, strlen(proto_tmp), proto); // Check that the paquet found in trace correspond to the one we are looking for if( (strcmp(eaddr, src_addr) == 0) && (strcmp(iaddr, dst_addr) == 0) && (strncmp(proto_tmp, proto, 5)==0)) { sport = strstr(buf, "SPT="); sport += 4; dport = strstr(buf, "DPT="); dport += 4; printf("\tCompare eport: %hu\n\t to port: %d\n", *eport, atoi(sport)); printf("\tCompare iport: %hu\n\t to port: %d\n", *iport, atoi(dport)); if(*eport != atoi(sport) && *iport != atoi(dport)) { printf("\t\tPort not corresponding\n"); continue; } else { printf("Find a corresponding policy trace in the chain: %.*s\n", 10, c); res = -7; continue; } } else continue; } } fclose(fd); return res; #else return -42; /* to be implemented */ #endif } #endif int upnp_clean_expired_pinholes(unsigned int * next_timestamp) { #if defined(USE_PF) || defined(USE_NETFILTER) return clean_pinhole_list(next_timestamp); #else UNUSED(next_timestamp); return 0; /* nothing to do */ #endif } #endif miniupnpd-1.8.20130730/upnppinhole.h010064400017500000024000000036521203340734000161610ustar00nanardstaff/* $Id: upnppinhole.h,v 1.3 2012/09/27 15:47:15 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2012 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #ifndef UPNPPINHOLE_H_INCLUDED #define UPNPPINHOLE_H_INCLUDED #include "config.h" #ifdef ENABLE_6FC_SERVICE /* functions to be used by WANIPv6_FirewallControl implementation */ #if 0 /* retrieve outbound pinhole timeout */ int upnp_check_outbound_pinhole(int proto, int * timeout); #endif /* add an inbound pinehole * return value : * 1 = success * -1 = Pinhole space exhausted * .. = error */ int upnp_add_inboundpinhole(const char * raddr, unsigned short rport, const char * iaddr, unsigned short iport, int proto, unsigned int leasetime, int * uid); /* * return values : * -1 not found * */ int upnp_get_pinhole_info(unsigned short uid, char * raddr, int raddrlen, unsigned short * rport, char * iaddr, int iaddrlen, unsigned short * iport, int * proto, unsigned int * leasetime, unsigned int * packets); /* update the lease time */ int upnp_update_inboundpinhole(unsigned short uid, unsigned int leasetime); /* remove the inbound pinhole */ int upnp_delete_inboundpinhole(unsigned short uid); /* ... */ #if 0 int upnp_check_pinhole_working(const char * uid, char * eaddr, char * iaddr, unsigned short * eport, unsigned short * iport, char * protocol, int * rulenum_used); #endif /* return the number of expired pinhole removed * write timestamp to next pinhole to exprire to next_timestamp * next_timestamp is left untouched if there is no pinhole lest */ int upnp_clean_expired_pinholes(unsigned int * next_timestamp); #endif /* ENABLE_6FC_SERVICE */ #endif miniupnpd-1.8.20130730/linux/getroute.c010064400017500000024000000105631210443263200166100ustar00nanardstaff/* $Id: getroute.c,v 1.4 2013/02/06 10:50:04 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 #include #include #include #include #include #include #include /*#include */ #include #include #include #include "../getroute.h" #include "../upnputils.h" int get_src_for_route_to(const struct sockaddr * dst, void * src, size_t * src_len, int * index) { int fd = -1; struct nlmsghdr *h; int status; struct { struct nlmsghdr n; struct rtmsg r; char buf[1024]; } req; struct sockaddr_nl nladdr; struct iovec iov = { .iov_base = (void*) &req.n, }; struct msghdr msg = { .msg_name = &nladdr, .msg_namelen = sizeof(nladdr), .msg_iov = &iov, .msg_iovlen = 1, }; const struct sockaddr_in * dst4; const struct sockaddr_in6 * dst6; memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); req.n.nlmsg_flags = NLM_F_REQUEST; req.n.nlmsg_type = RTM_GETROUTE; req.r.rtm_family = dst->sa_family; req.r.rtm_table = 0; req.r.rtm_protocol = 0; req.r.rtm_scope = 0; req.r.rtm_type = 0; req.r.rtm_src_len = 0; req.r.rtm_dst_len = 0; req.r.rtm_tos = 0; { char dst_str[128]; sockaddr_to_string(dst, dst_str, sizeof(dst_str)); syslog(LOG_DEBUG, "get_src_for_route_to (%s)", dst_str); } /* add address */ if(dst->sa_family == AF_INET) { dst4 = (const struct sockaddr_in *)dst; nfnl_addattr_l(&req.n, sizeof(req), RTA_DST, &dst4->sin_addr, 4); req.r.rtm_dst_len = 32; } else { dst6 = (const struct sockaddr_in6 *)dst; nfnl_addattr_l(&req.n, sizeof(req), RTA_DST, &dst6->sin6_addr, 16); req.r.rtm_dst_len = 128; } fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (fd < 0) { syslog(LOG_ERR, "socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE) : %m"); return -1; } memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; req.n.nlmsg_seq = 1; iov.iov_len = req.n.nlmsg_len; status = sendmsg(fd, &msg, 0); if (status < 0) { syslog(LOG_ERR, "sendmsg(rtnetlink) : %m"); goto error; } memset(&req, 0, sizeof(req)); for(;;) { iov.iov_len = sizeof(req); status = recvmsg(fd, &msg, 0); if(status < 0) { if (errno == EINTR || errno == EAGAIN) continue; syslog(LOG_ERR, "recvmsg(rtnetlink) %m"); goto error; } if(status == 0) { syslog(LOG_ERR, "recvmsg(rtnetlink) EOF"); goto error; } for (h = (struct nlmsghdr*)&req.n; status >= (int)sizeof(*h); ) { int len = h->nlmsg_len; int l = len - sizeof(*h); if (l<0 || len>status) { if (msg.msg_flags & MSG_TRUNC) { syslog(LOG_ERR, "Truncated message"); } syslog(LOG_ERR, "malformed message: len=%d", len); goto error; } if(nladdr.nl_pid != 0 || h->nlmsg_seq != 1/*seq*/) { syslog(LOG_ERR, "wrong seq = %d\n", h->nlmsg_seq); /* Don't forget to skip that message. */ status -= NLMSG_ALIGN(len); h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); continue; } if(h->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); syslog(LOG_ERR, "NLMSG_ERROR %d : %s", err->error, strerror(-err->error)); goto error; } if(h->nlmsg_type == RTM_NEWROUTE) { struct rtattr * rta; int len = h->nlmsg_len; len -= NLMSG_LENGTH(sizeof(struct rtmsg)); for(rta = RTM_RTA(NLMSG_DATA((h))); RTA_OK(rta, len); rta = RTA_NEXT(rta,len)) { unsigned char * data = RTA_DATA(rta); if(rta->rta_type == RTA_PREFSRC) { if(src_len && src) { if(*src_len < RTA_PAYLOAD(rta)) { syslog(LOG_WARNING, "cannot copy src: %u<%lu", (unsigned)*src_len, RTA_PAYLOAD(rta)); goto error; } *src_len = RTA_PAYLOAD(rta); memcpy(src, data, RTA_PAYLOAD(rta)); } } else if(rta->rta_type == RTA_OIF) { if(index) memcpy(index, data, sizeof(int)); } } close(fd); return 0; } status -= NLMSG_ALIGN(len); h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); } } syslog(LOG_WARNING, "get_src_for_route_to() : src not found"); error: if(fd >= 0) close(fd); return -1; } miniupnpd-1.8.20130730/bsd/getroute.c010064400017500000024000000060061210445350200162150ustar00nanardstaff/* $Id: getroute.c,v 1.3 2013/02/06 13:11:45 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 #include #include #include #include #include #include #include #ifdef AF_LINK #include #endif #include "../config.h" #include "../upnputils.h" int get_src_for_route_to(const struct sockaddr * dst, void * src, size_t * src_len, int * index) { int found = 0; int s; int l, i; char * p; struct sockaddr * sa; struct { struct rt_msghdr m_rtm; char m_space[512]; } m_rtmsg; #define rtm m_rtmsg.m_rtm if(dst == NULL) return -1; #ifdef __APPLE__ if(dst->sa_family == AF_INET6) { syslog(LOG_ERR, "Sorry, get_src_for_route_to() is known to fail with IPV6 on OS X..."); return -1; } #endif s = socket(PF_ROUTE, SOCK_RAW, dst->sa_family); if(s < 0) { syslog(LOG_ERR, "socket(PF_ROUTE) failed : %m"); return -1; } memset(&rtm, 0, sizeof(rtm)); rtm.rtm_type = RTM_GET; rtm.rtm_flags = RTF_UP; rtm.rtm_version = RTM_VERSION; rtm.rtm_seq = 1; rtm.rtm_addrs = RTA_DST; /* destination address */ memcpy(m_rtmsg.m_space, dst, sizeof(struct sockaddr)); rtm.rtm_msglen = sizeof(struct rt_msghdr) + sizeof(struct sockaddr); if(write(s, &m_rtmsg, rtm.rtm_msglen) < 0) { syslog(LOG_ERR, "write: %m"); close(s); return -1; } do { l = read(s, &m_rtmsg, sizeof(m_rtmsg)); if(l<0) { syslog(LOG_ERR, "read: %m"); close(s); return -1; } syslog(LOG_DEBUG, "read l=%d seq=%d pid=%d", l, rtm.rtm_seq, rtm.rtm_pid); } while(l > 0 && (rtm.rtm_pid != getpid() || rtm.rtm_seq != 1)); close(s); p = m_rtmsg.m_space; if(rtm.rtm_addrs) { for(i=1; i<0x8000; i <<= 1) { if(i & rtm.rtm_addrs) { char tmp[256] = { 0 }; sa = (struct sockaddr *)p; sockaddr_to_string(sa, tmp, sizeof(tmp)); syslog(LOG_DEBUG, "type=%d sa_len=%d sa_family=%d %s", i, sa->sa_len, sa->sa_family, tmp); if((i == RTA_DST || i == RTA_GATEWAY) && (src_len && src)) { size_t len = 0; void * paddr = NULL; if(sa->sa_family == AF_INET) { paddr = &((struct sockaddr_in *)sa)->sin_addr; len = sizeof(struct in_addr); } else if(sa->sa_family == AF_INET6) { paddr = &((struct sockaddr_in6 *)sa)->sin6_addr; len = sizeof(struct in6_addr); } if(paddr) { if(*src_len < len) { syslog(LOG_WARNING, "cannot copy src. %u<%u", (unsigned)*src_len, (unsigned)len); return -1; } memcpy(src, paddr, len); *src_len = len; found = 1; } } #ifdef AF_LINK if(sa->sa_family == AF_LINK) { struct sockaddr_dl * sdl = (struct sockaddr_dl *)sa; if(index) *index = sdl->sdl_index; } #endif p += sa->sa_len; } } } return found ? 0 : -1; } miniupnpd-1.8.20130730/getroute.h010064400017500000024000000007621210443263100154550ustar00nanardstaff/* $Id: getroute.h,v 1.3 2013/02/06 10:50:04 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 GETROUTE_H_INCLUDED #define GETROUTE_H_INCLUDED int get_src_for_route_to(const struct sockaddr * dst, void * src, size_t * src_len, int * index); #endif miniupnpd-1.8.20130730/testgetroute.c010064400017500000024000000044101210444361000163410ustar00nanardstaff/* $Id: testgetroute.c,v 1.5 2013/02/06 12:07:36 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 #include #include #include #include #include #include #include "getroute.h" #include "upnputils.h" #include "upnpglobalvars.h" #ifndef LOG_PERROR /* solaris does not define LOG_PERROR */ #define LOG_PERROR 0 #endif struct lan_addr_list lan_addrs; int main(int argc, char ** argv) { struct sockaddr_in dst4; struct sockaddr_in6 dst6; struct sockaddr * dst; void * src; size_t src_len; int r; int index = -1; memset(&dst4, 0, sizeof(dst4)); memset(&dst6, 0, sizeof(dst6)); dst = NULL; if(argc < 2) { fprintf(stderr, "usage: %s \n", argv[0]); fprintf(stderr, "both v4 and v6 IP addresses are supported.\n"); return 1; } openlog("testgetroute", LOG_CONS|LOG_PERROR, LOG_USER); r = inet_pton (AF_INET, argv[1], &dst4.sin_addr); if(r < 0) { syslog(LOG_ERR, "inet_pton(AF_INET, %s) : %m", argv[1]); closelog(); return 2; } if (r == 0) { r = inet_pton (AF_INET6, argv[1], &dst6.sin6_addr); if(r < 0) { syslog(LOG_ERR, "inet_pton(AF_INET6, %s) : %m", argv[1]); closelog(); return 2; } else if(r > 0) { dst6.sin6_family = AF_INET6; dst = (struct sockaddr *)&dst6; src = &dst6.sin6_addr; src_len = sizeof(dst6.sin6_addr); } else { /* r == 0 */ syslog(LOG_ERR, "%s is not a correct IPv4 or IPv6 address", argv[1]); closelog(); return 1; } } else { dst4.sin_family = AF_INET; dst = (struct sockaddr *)&dst4; src = &dst4.sin_addr; src_len = sizeof(dst4.sin_addr); } if (dst) { syslog(LOG_DEBUG, "calling get_src_for_route_to(%p, %p, %p(%u), %p)", dst, src, &src_len, (unsigned)src_len, &index); r = get_src_for_route_to (dst, src, &src_len, &index); syslog(LOG_DEBUG, "get_src_for_route_to() returned %d", r); if(r >= 0) { char src_str[128]; sockaddr_to_string(dst, src_str, sizeof(src_str)); syslog(LOG_DEBUG, "src=%s", src_str); syslog(LOG_DEBUG, "index=%d", index); } } closelog(); return 0; }