miniupnpd-1.8.20140523/Makefile010064400017500000024000000154651232477504100151260ustar00nanardstaff# $Id: Makefile,v 1.84 2014/04/20 16:32:17 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 .if $(OSNAME) == "OpenBSD" FWNAME = pf .endif # better way to find if we are using ipf or pf .if exists(/etc/rc.subr) && exists(/etc/rc.conf) .if $(OSNAME) == "FreeBSD" FWNAME != . /etc/rc.subr; . /etc/rc.conf; \ if checkyesno ipfilter_enable; then \ echo "ipf"; elif checkyesno pf_enable; then \ echo "pf"; else echo "ipfw"; fi .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 checkyesno ipfilter; then \ echo "ipf"; elif checkyesno pf_enable; then \ echo "pf"; else echo "ipfw"; fi .endif .endif .if $(OSNAME) == "Darwin" # Firewall is ipfw up to OS X 10.6 Snow Leopard # and pf since OS X 10.7 Lion (Darwin 11.0) FWNAME != [ `uname -r | cut -d. -f1` -ge 11 ] && echo "pf" || echo "ipfw" .endif .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 pcpserver.o \ upnpevents.o upnputils.o getconnstatus.o \ upnppinhole.o asyncsendto.o portinuse.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 TESTASYNCSENDTOOBJS = testasyncsendto.o asyncsendto.o upnputils.o bsd/getroute.o TESTPORTINUSEOBJS = testportinuse.o portinuse.o getifaddr.o EXECUTABLES = miniupnpd testupnpdescgen testgetifstats \ testupnppermissions miniupnpdctl \ testgetifaddr testgetroute testasyncsendto \ testportinuse .if $(OSNAME) == "Darwin" LIBS = .else LIBS = -lkvm .endif .if $(OSNAME) == "SunOS" LIBS += -lsocket -lnsl -lkstat -lresolv .endif LIBS += -lssl -lcrypto # 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 testasyncsendto.o \ testportinuse.o \ $(PFOBJS) $(IPFOBJS) $(IPFWOBJS) install: miniupnpd genuuid $(STRIP) miniupnpd $(INSTALL) -d $(DESTDIR)$(INSTALLBINDIR) $(INSTALL) -m 755 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 testportinuse.c testasyncsendto.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) $(LIBS) testgetifstats: config.h $(TESTGETIFSTATSOBJS) $(CC) $(CFLAGS) -o $@ $(TESTGETIFSTATSOBJS) $(LIBS) testgetifaddr: config.h $(TESTGETIFADDROBJS) $(CC) $(CFLAGS) -o $@ $(TESTGETIFADDROBJS) $(LIBS) testupnppermissions: config.h $(TESTUPNPPERMISSIONSOBJS) $(CC) $(CFLAGS) -o $@ $(TESTUPNPPERMISSIONSOBJS) $(LIBS) testgetroute: config.h $(TESTGETROUTEOBJS) $(CC) $(CFLAGS) -o $@ $(TESTGETROUTEOBJS) $(LIBS) testasyncsendto: config.h $(TESTASYNCSENDTOOBJS) $(CC) $(CFLAGS) -o $@ $(TESTASYNCSENDTOOBJS) testportinuse: config.h $(TESTPORTINUSEOBJS) $(CC) $(CFLAGS) -o $@ $(TESTPORTINUSEOBJS) $(LIBS) # 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.20140523/Makefile.linux010064400017500000024000000306471233217235100162550ustar00nanardstaff# $Id: Makefile.linux,v 1.87 2014/05/06 14:42:08 nanard Exp $ # MiniUPnP project # (c) 2006-2014 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 CPPFLAGS += -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 PKG_CONFIG ?= pkg-config 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 portinuse.o \ upnpredirect.o getifaddr.o daemonize.o upnpglobalvars.o \ options.o upnppermissions.o minissdp.o natpmp.o pcpserver.o \ upnpevents.o upnputils.o getconnstatus.o \ upnppinhole.o pcplearndscp.o asyncsendto.o LNXOBJS = linux/getifstats.o linux/ifacewatcher.o linux/getroute.o NETFILTEROBJS = netfilter/iptcrdr.o netfilter/iptpinhole.o netfilter/nfct_get.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) CPPFLAGS += -DIPTABLES_143 endif CFLAGS += $(shell $(PKG_CONFIG) --cflags libiptc) LDLIBS += $(shell $(PKG_CONFIG) --static --libs-only-l libiptc) LDFLAGS += $(shell $(PKG_CONFIG) --libs-only-L libiptc) LDFLAGS += $(shell $(PKG_CONFIG) --libs-only-other libiptc) else ifeq "$(wildcard /etc/gentoo-release )" "" LDLIBS ?= -liptc else # gentoo # the following is better, at least on gentoo with iptables 1.4.6 # see http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=1618 # and http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=2183 LDLIBS ?= -lip4tc CPPFLAGS := -DIPTABLES_143 $(CPPFLAGS) endif ARCH ?= $(shell uname -m | grep -q "x86_64" && echo 64) ifdef IPTABLESPATH CPPFLAGS := $(CPPFLAGS) -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) CPPFLAGS := $(CPPFLAGS) -DIPTABLES_143 # the following sucks, but works LDLIBS = $(IPTABLESPATH)/libiptc/.libs/libip4tc.o #LDLIBS = $(IPTABLESPATH)/libiptc/.libs/libiptc.a else # ifeq ($(TEST), 1) LDLIBS = $(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) CPPFLAGS := $(CPPFLAGS) -DIPTABLES_143 LDLIBS = -liptc endif # ($(TEST), 1) TEST_LIB := $(shell test -f /usr/lib$(ARCH)/libiptc.a && echo 1) ifeq ($(TEST_LIB), 1) LDLIBS = -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) CPPFLAGS := $(CPPFLAGS) -DIPTABLES_143 LDLIBS = -liptc TESTIP4TC := $(shell test -f /lib/libip4tc.so && echo 1) ifeq ($(TESTIP4TC), 1) LDLIBS := $(LDLIBS) -lip4tc endif # ($(TESTIP4TC), 1) TESTIP6TC := $(shell test -f /lib/libip6tc.so && echo 1) ifeq ($(TESTIP6TC), 1) LDLIBS := $(LDLIBS) -lip6tc endif # ($(TESTIP6TC), 1) endif # ($(TEST), 1) endif # ifdef IPTABLESPATH endif # ifdef PCFILE_FOUND LDLIBS += -lnfnetlink TEST := $(shell $(PKG_CONFIG) --atleast-version=1.0.2 libnetfilter_conntrack && $(PKG_CONFIG) --atleast-version=1.0.3 libmnl && echo 1) ifeq ($(TEST),1) CPPFLAGS += -DUSE_NFCT LDLIBS += $(shell $(PKG_CONFIG) --static --libs-only-l libmnl) LDLIBS += $(shell $(PKG_CONFIG) --static --libs-only-l libnetfilter_conntrack) endif # ($(TEST),1) LDLIBS += $(shell $(PKG_CONFIG) --static --libs-only-l libssl) TESTUPNPDESCGENOBJS = testupnpdescgen.o upnpdescgen.o EXECUTABLES = miniupnpd testupnpdescgen testgetifstats \ testupnppermissions miniupnpdctl testgetifaddr \ testgetroute testasyncsendto testportinuse .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 testasyncsendto.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) 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 testasyncsendto: testasyncsendto.o asyncsendto.o upnputils.o \ linux/getroute.o testportinuse: testportinuse.o portinuse.o getifaddr.o \ netfilter/iptcrdr.o 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 testasyncsendto.c testportinuse.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 asyncsendto.h natpmp.h miniupnpd.o: pcpserver.h commonrdr.h 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 portinuse.o: macros.h config.h upnpglobalvars.h upnppermissions.h portinuse.o: miniupnpdtypes.h getifaddr.h portinuse.h netfilter/iptcrdr.h portinuse.o: commonrdr.h upnpredirect.o: macros.h config.h upnpredirect.h upnpglobalvars.h upnpredirect.o: upnppermissions.h miniupnpdtypes.h upnpevents.h portinuse.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 upnpdescstrings.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 asyncsendto.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 natpmp.o: portinuse.h asyncsendto.h pcpserver.o: config.h pcpserver.h macros.h upnpglobalvars.h upnppermissions.h pcpserver.o: miniupnpdtypes.h pcplearndscp.h upnpredirect.h commonrdr.h pcpserver.o: getifaddr.h asyncsendto.h pcp_msg_struct.h netfilter/iptcrdr.h pcpserver.o: commonrdr.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 pcplearndscp.o: config.h upnpglobalvars.h upnppermissions.h miniupnpdtypes.h pcplearndscp.o: pcplearndscp.h asyncsendto.o: asyncsendto.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 upnpdescstrings.h testupnpdescgen.o: getifaddr.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: config.h getifaddr.h testgetroute.o: getroute.h upnputils.h upnpglobalvars.h upnppermissions.h testgetroute.o: config.h miniupnpdtypes.h testasyncsendto.o: miniupnpdtypes.h config.h upnputils.h asyncsendto.h testportinuse.o: macros.h config.h portinuse.h miniupnpdctl.o: macros.h miniupnpd-1.8.20140523/upnphttp.c010064400017500000024000000757421232172236100155120ustar00nanardstaff/* $Id: upnphttp.c,v 1.92 2014/04/11 08:15:43 nanard Exp $ */ /* Project : miniupnp * Website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * Author : Thomas Bernard * Copyright (c) 2005-2014 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" #ifdef ENABLE_HTTPS #include #include #include static SSL_CTX *ssl_ctx = NULL; #ifndef HTTPS_CERTFILE #define HTTPS_CERTFILE "/etc/ssl/certs/ssl-cert-snakeoil.pem" #endif #ifndef HTTPS_KEYFILE #define HTTPS_KEYFILE "/etc/ssl/private/ssl-cert-snakeoil.key" #endif static void syslogsslerr(void) { unsigned long err; char buffer[256]; while((err = ERR_get_error()) != 0) { syslog(LOG_ERR, "%s", ERR_error_string(err, buffer)); } } static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) { syslog(LOG_DEBUG, "verify_callback(%d, %p)", preverify_ok, ctx); return preverify_ok; } int init_ssl(void) { SSL_METHOD *method; SSL_library_init(); SSL_load_error_strings(); method = TLSv1_server_method(); if(method == NULL) { syslog(LOG_ERR, "TLSv1_server_method() failed"); syslogsslerr(); return -1; } ssl_ctx = SSL_CTX_new(method); if(ssl_ctx == NULL) { syslog(LOG_ERR, "SSL_CTX_new() failed"); syslogsslerr(); return -1; } /* set the local certificate */ if(!SSL_CTX_use_certificate_file(ssl_ctx, HTTPS_CERTFILE, SSL_FILETYPE_PEM)) { syslog(LOG_ERR, "SSL_CTX_use_certificate_file(%s) failed", HTTPS_CERTFILE); syslogsslerr(); return -1; } /* set the private key */ if(!SSL_CTX_use_PrivateKey_file(ssl_ctx, HTTPS_KEYFILE, SSL_FILETYPE_PEM)) { syslog(LOG_ERR, "SSL_CTX_use_PrivateKey_file(%s) failed", HTTPS_KEYFILE); syslogsslerr(); return -1; } /* verify private key */ if(!SSL_CTX_check_private_key(ssl_ctx)) { syslog(LOG_ERR, "SSL_CTX_check_private_key() failed"); syslogsslerr(); return -1; } /*SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE, verify_callback);*/ SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, verify_callback); /*SSL_CTX_set_verify_depth(depth);*/ syslog(LOG_INFO, "using %s", SSLeay_version(SSLEAY_VERSION)); return 0; } void free_ssl(void) { /* free context */ if(ssl_ctx != NULL) { SSL_CTX_free(ssl_ctx); ssl_ctx = NULL; } ERR_remove_state(0); ENGINE_cleanup(); CONF_modules_unload(1); ERR_free_strings(); EVP_cleanup(); CRYPTO_cleanup_all_ex_data(); } #endif /* ENABLE_HTTPS */ 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; } #ifdef ENABLE_HTTPS void InitSSL_upnphttp(struct upnphttp * h) { int r; h->ssl = SSL_new(ssl_ctx); if(h->ssl == NULL) { syslog(LOG_ERR, "SSL_new() failed"); syslogsslerr(); abort(); } if(!SSL_set_fd(h->ssl, h->socket)) { syslog(LOG_ERR, "SSL_set_fd() failed"); syslogsslerr(); abort(); } r = SSL_accept(h->ssl); /* start the handshaking */ if(r < 0) { int err; err = SSL_get_error(h->ssl, r); syslog(LOG_DEBUG, "SSL_accept() returned %d, SSL_get_error() %d", r, err); if(err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) { syslog(LOG_ERR, "SSL_accept() failed"); syslogsslerr(); abort(); } } } #endif /* ENABLE_HTTPS */ void CloseSocket_upnphttp(struct upnphttp * h) { /* SSL_shutdown() ? */ 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) { #ifdef ENABLE_HTTPS if(h->ssl) SSL_free(h->ssl); #endif 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) { /* The Callback can contain several urls : * If there is more than one URL, when the service sends * events, it will try these URLs in order until one * succeeds. One or more URLs each enclosed by angle * brackets ("<" and ">") */ p = colon; while(*p != '<' && *p != '\r' ) p++; n = 0; while(p[n] != '\r') n++; while(n > 0 && p[n] != '>') n--; /* found last > character */ h->req_CallbackOff = p - 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 /** * checkCallbackURL() * check that url is on originating IP * extract first correct URL * 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; start_again: if(h->req_CallbackOff <= 0 || h->req_CallbackLen < 10) return 0; if(memcmp(h->req_buf + h->req_CallbackOff, "req_buf + h->req_CallbackOff + 1; goto invalid; } /* extract host from url to addrstr[] */ i = 0; p = h->req_buf + h->req_CallbackOff + 8; if(*p == '[') { p++; ipv6 = 1; while(*p != ']' && *p != '>' && i < (sizeof(addrstr)-1) && p < (h->req_buf + h->req_CallbackOff + h->req_CallbackLen)) addrstr[i++] = *(p++); } else { ipv6 = 0; while(*p != '/' && *p != ':' && *p != '>' && i < (sizeof(addrstr)-1) && p < (h->req_buf + h->req_CallbackOff + h->req_CallbackLen)) addrstr[i++] = *(p++); } addrstr[i] = '\0'; /* check addrstr */ if(ipv6) { #ifdef ENABLE_IPV6 struct in6_addr addr; if(inet_pton(AF_INET6, addrstr, &addr) <= 0) goto invalid; if(!h->ipv6 || (0!=memcmp(&addr, &(h->clientaddr_v6), sizeof(struct in6_addr)))) goto invalid; #else goto invalid; #endif } else { struct in_addr addr; if(inet_pton(AF_INET, addrstr, &addr) <= 0) goto invalid; #ifdef ENABLE_IPV6 if(h->ipv6) { if(!IN6_IS_ADDR_V4MAPPED(&(h->clientaddr_v6))) goto invalid; if(0!=memcmp(&addr, ((const char *)&(h->clientaddr_v6) + 12), 4)) goto invalid; } else { if(0!=memcmp(&addr, &(h->clientaddr), sizeof(struct in_addr))) goto invalid; } #else if(0!=memcmp(&addr, &(h->clientaddr), sizeof(struct in_addr))) goto invalid; #endif } /* select only the good callback url */ while(p < h->req_buf + h->req_CallbackOff + h->req_CallbackLen && *p != '>') p++; h->req_CallbackOff++; /* skip initial '<' */ h->req_CallbackLen = (int)(p - h->req_buf - h->req_CallbackOff); return 1; invalid: while(p < h->req_buf + h->req_CallbackOff + h->req_CallbackLen && *p != '>') p++; if(*p != '>') return 0; while(p < h->req_buf + h->req_CallbackOff + h->req_CallbackLen && *p != '<') p++; if(*p != '<') return 0; h->req_CallbackLen -= (int)(p - h->req_buf - h->req_CallbackOff); h->req_CallbackOff = (int)(p - h->req_buf); goto start_again; } 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: #ifdef ENABLE_HTTPS if(h->ssl) { n = SSL_read(h->ssl, buf, sizeof(buf)); } else { n = recv(h->socket, buf, sizeof(buf), 0); } #else n = recv(h->socket, buf, sizeof(buf), 0); #endif if(n<0) { #ifdef ENABLE_HTTPS if(h->ssl) { int err; err = SSL_get_error(h->ssl, n); if(err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) { syslog(LOG_ERR, "SSL_read() failed"); syslogsslerr(); h->state = EToDelete; } } else { #endif 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 */ #ifdef ENABLE_HTTPS } #endif } 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: #ifdef ENABLE_HTTPS if(h->ssl) { n = SSL_read(h->ssl, buf, sizeof(buf)); } else { n = recv(h->socket, buf, sizeof(buf), 0); } #else n = recv(h->socket, buf, sizeof(buf), 0); #endif if(n<0) { #ifdef ENABLE_HTTPS if(h->ssl) { int err; err = SSL_get_error(h->ssl, n); if(err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) { syslog(LOG_ERR, "SSL_read() failed"); syslogsslerr(); h->state = EToDelete; } } else { #endif 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 */ #ifdef ENABLE_HTTPS } #endif } 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) { #ifdef ENABLE_HTTPS if(h->ssl) { n = SSL_write(h->ssl, h->res_buf + h->res_sent, h->res_buflen - h->res_sent); } else { n = send(h->socket, h->res_buf + h->res_sent, h->res_buflen - h->res_sent, 0); } #else n = send(h->socket, h->res_buf + h->res_sent, h->res_buflen - h->res_sent, 0); #endif if(n<0) { #ifdef ENABLE_HTTPS if(h->ssl) { int err; err = SSL_get_error(h->ssl, n); if(err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { /* try again later */ return 0; } syslog(LOG_ERR, "SSL_write() failed"); syslogsslerr(); break; } else { #endif 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 */ #ifdef ENABLE_HTTPS } #endif } 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) { #ifdef ENABLE_HTTPS if(h->ssl) { n = SSL_write(h->ssl, h->res_buf + h->res_sent, h->res_buflen - h->res_sent); } else { n = send(h->socket, h->res_buf + h->res_sent, h->res_buflen - h->res_sent, 0); } #else n = send(h->socket, h->res_buf + h->res_sent, h->res_buflen - h->res_sent, 0); #endif if(n<0) { #ifdef ENABLE_HTTPS if(h->ssl) { int err; err = SSL_get_error(h->ssl, n); if(err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { /* try again later */ h->state = ESendingAndClosing; return; } syslog(LOG_ERR, "SSL_write() failed"); syslogsslerr(); break; /* avoid infinite loop */ } else { #endif 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 */ #ifdef ENABLE_HTTPS } #endif } 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.20140523/miniupnpd.c010064400017500000024000001653061233732667100156420ustar00nanardstaff/* $Id: miniupnpd.c,v 1.200 2014/05/22 07:56:34 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2014 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include "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" #include "asyncsendto.h" #ifdef ENABLE_NATPMP #include "natpmp.h" #ifdef ENABLE_PCP #include "pcpserver.h" #else #define PCP_MAX_LEN 32 #endif #endif #include "commonrdr.h" #include "upnputils.h" #ifdef USE_IFACEWATCHER #include "ifacewatcher.h" #endif #ifdef ENABLE_UPNPPINHOLE #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 #ifdef ENABLE_IPV6 OpenAndConfHTTPSocket(unsigned short * port, int ipv6) #else OpenAndConfHTTPSocket(unsigned short * port) #endif { int s; int i = 1; #ifdef ENABLE_IPV6 struct sockaddr_in6 listenname6; struct sockaddr_in listenname4; #else struct sockaddr_in listenname; #endif socklen_t listenname_len; s = socket( #ifdef ENABLE_IPV6 ipv6 ? PF_INET6 : PF_INET, #else PF_INET, #endif SOCK_STREAM, 0); #ifdef ENABLE_IPV6 if(s < 0 && ipv6 && errno == EAFNOSUPPORT) { /* the system doesn't support IPV6 */ syslog(LOG_WARNING, "socket(PF_INET6, ...) failed with EAFNOSUPPORT, disabling IPv6"); SETFLAG(IPV6DISABLEDMASK); ipv6 = 0; s = socket(PF_INET, SOCK_STREAM, 0); } #endif if(s < 0) { syslog(LOG_ERR, "socket(http): %m"); return -1; } 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 if(ipv6) { memset(&listenname6, 0, sizeof(struct sockaddr_in6)); listenname6.sin6_family = AF_INET6; listenname6.sin6_port = htons(*port); listenname6.sin6_addr = ipv6_bind_addr; listenname_len = sizeof(struct sockaddr_in6); } else { memset(&listenname4, 0, sizeof(struct sockaddr_in)); listenname4.sin_family = AF_INET; listenname4.sin_port = htons(*port); listenname4.sin_addr.s_addr = htonl(INADDR_ANY); listenname_len = sizeof(struct sockaddr_in); } #else memset(&listenname, 0, sizeof(struct sockaddr_in)); listenname.sin_family = AF_INET; listenname.sin_port = htons(*port); listenname.sin_addr.s_addr = htonl(INADDR_ANY); listenname_len = sizeof(struct sockaddr_in); #endif #ifdef ENABLE_IPV6 if(bind(s, ipv6 ? (struct sockaddr *)&listenname6 : (struct sockaddr *)&listenname4, listenname_len) < 0) #else if(bind(s, (struct sockaddr *)&listenname, listenname_len) < 0) #endif { 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; } if(*port == 0) { #ifdef ENABLE_IPV6 if(ipv6) { struct sockaddr_in6 sockinfo; socklen_t len = sizeof(struct sockaddr_in6); if (getsockname(s, (struct sockaddr *)&sockinfo, &len) < 0) { syslog(LOG_ERR, "getsockname(): %m"); } else { *port = ntohs(sockinfo.sin6_port); } } else { #endif /* ENABLE_IPV6 */ struct sockaddr_in sockinfo; socklen_t len = sizeof(struct sockaddr_in); if (getsockname(s, (struct sockaddr *)&sockinfo, &len) < 0) { syslog(LOG_ERR, "getsockname(): %m"); } else { *port = ntohs(sockinfo.sin_port); } #ifdef ENABLE_IPV6 } #endif /* ENABLE_IPV6 */ } return s; } static struct upnphttp * ProcessIncomingHTTP(int shttpl, const char * protocol) { 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, "%s connection from %s", protocol, addr_str); if(get_lan_for_peer((struct sockaddr *)&clientname) == NULL) { /* The peer is not a LAN ! */ syslog(LOG_WARNING, "%s peer %s is not from a LAN, closing the connection", protocol, 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 return tmp; } else { syslog(LOG_ERR, "New_upnphttp() failed"); close(shttp); } } } return NULL; } #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; /*printf("%s => %s\n", lan_addr->ifname, lan_addr->str);*/ } 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=='.') { /* parse mask in /255.255.255.0 format */ 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 { /* it is a /24 format */ 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, "Error: please specify LAN network interface by name instead of IPv4 address : %s\n", str); return -1; } #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 * 9) init random generator (srandom()) * 10) init redirection engine * 11) reload mapping from leasefile */ 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; #ifdef ENABLE_HTTPS v->https_port = -1; #endif v->notify_interval = 30; /* seconds between SSDP announces */ v->clean_ruleset_threshold = 20; v->clean_ruleset_interval = 0; /* interval between ruleset check. 0=disabled */ #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 a valid " #ifndef ENABLE_IPV6 "LAN address or " #endif "interface name\n", ary_options[i].value); free(lan_addr); break; } LIST_INSERT_HEAD(&lan_addrs, lan_addr, list); break; #ifdef ENABLE_IPV6 case UPNPIPV6_LISTENING_IP: if (inet_pton(AF_INET6, ary_options[i].value, &ipv6_bind_addr) < 1) { fprintf(stderr, "can't parse \"%s\" as valid IPv6 listening address", ary_options[i].value); } break; #endif /* ENABLE_IPV6 */ case UPNPPORT: v->port = atoi(ary_options[i].value); break; #ifdef ENABLE_HTTPS case UPNPHTTPSPORT: v->https_port = atoi(ary_options[i].value); break; #endif case UPNPBITRATE_UP: upstream_bitrate = strtoul(ary_options[i].value, 0, 0); break; case UPNPBITRATE_DOWN: downstream_bitrate = strtoul(ary_options[i].value, 0, 0); break; case UPNPPRESENTATIONURL: presurl = ary_options[i].value; break; #ifdef ENABLE_MANUFACTURER_INFO_CONFIGURATION case UPNPFRIENDLY_NAME: strncpy(friendly_name, ary_options[i].value, FRIENDLY_NAME_MAX_LEN); friendly_name[FRIENDLY_NAME_MAX_LEN-1] = '\0'; break; case UPNPMANUFACTURER_NAME: strncpy(manufacturer_name, ary_options[i].value, MANUFACTURER_NAME_MAX_LEN); manufacturer_name[MANUFACTURER_NAME_MAX_LEN-1] = '\0'; break; case UPNPMANUFACTURER_URL: strncpy(manufacturer_url, ary_options[i].value, MANUFACTURER_URL_MAX_LEN); manufacturer_url[MANUFACTURER_URL_MAX_LEN-1] = '\0'; break; case UPNPMODEL_NAME: strncpy(model_name, ary_options[i].value, MODEL_NAME_MAX_LEN); model_name[MODEL_NAME_MAX_LEN-1] = '\0'; break; case UPNPMODEL_DESCRIPTION: strncpy(model_description, ary_options[i].value, MODEL_DESCRIPTION_MAX_LEN); model_description[MODEL_DESCRIPTION_MAX_LEN-1] = '\0'; break; case UPNPMODEL_URL: strncpy(model_url, ary_options[i].value, MODEL_URL_MAX_LEN); model_url[MODEL_URL_MAX_LEN-1] = '\0'; break; #endif #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 ENABLE_PCP case UPNPPCPMINLIFETIME: min_lifetime = atoi(ary_options[i].value); if (min_lifetime > 120 ) { min_lifetime = 120; } break; case UPNPPCPMAXLIFETIME: max_lifetime = atoi(ary_options[i].value); if (max_lifetime > 86400 ) { max_lifetime = 86400; } break; case UPNPPCPALLOWTHIRDPARTY: if(strcmp(ary_options[i].value, "yes") == 0) SETFLAG(PCP_ALLOWTHIRDPARTYMASK); break; #endif #ifdef PF_ENABLE_FILTER_RULES case UPNPQUICKRULES: 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); } } #ifdef ENABLE_PCP /* if lifetimes are inverse */ if (min_lifetime >= max_lifetime) { fprintf(stderr, "Minimum lifetime (%lu) is greater than or equal to maximum lifetime (%lu).\n", min_lifetime, max_lifetime); fprintf(stderr, "Check your configuration file.\n"); return 1; } #endif } #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; #ifdef ENABLE_MANUFACTURER_INFO_CONFIGURATION 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; #endif 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 fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]); break; #ifdef ENABLE_HTTPS case 'H': if(i+1 < argc) v->https_port = atoi(argv[++i]); else fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]); break; #endif #ifdef ENABLE_NFQUEUE case 'Q': if(i+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 a valid LAN address or interface name\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"); } /* initialize random number generator */ srandom((unsigned int)time(NULL)); /* initialize redirection engine (and pinholes) */ if(init_redirect() < 0) { syslog(LOG_ERR, "Failed to init redirection engine. EXITING"); return 1; } #ifdef ENABLE_UPNPPINHOLE #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 #ifdef ENABLE_HTTPS " [-H https_port]" #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] " #ifdef ENABLE_MANUFACTURER_INFO_CONFIGURATION "[-z fiendly_name]\n" #endif "\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 */ #if defined(V6SOCKETS_ARE_V6ONLY) && defined(ENABLE_IPV6) int shttpl_v4 = -1; /* socket for HTTP (ipv4 only) */ #endif #ifdef ENABLE_HTTPS int shttpsl = -1; /* socket for HTTPS */ #if defined(V6SOCKETS_ARE_V6ONLY) && defined(ENABLE_IPV6) int shttpsl_v4 = -1; /* socket for HTTPS (ipv4 only) */ #endif #endif /* ENABLE_HTTPS */ int sudp = -1; /* IP v4 socket for receiving SSDP */ #ifdef ENABLE_IPV6 int sudpv6 = -1; /* IP v6 socket for receiving SSDP */ #endif #ifdef ENABLE_NATPMP int * snatpmp = NULL; /* also used for PCP */ #endif #if defined(ENABLE_IPV6) && defined(ENABLE_PCP) int spcp_v6 = -1; #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_UPNPPINHOLE unsigned int next_pinhole_ts; #endif if(init(argc, argv, &v) != 0) return 1; #ifdef ENABLE_HTTPS if(init_ssl() < 0) return 1; #endif /* ENABLE_HTTPS */ /* count lan addrs */ addr_count = 0; for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next) 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 #ifdef ENABLE_PCP GETFLAG(ENABLENATPMPMASK) ? " NAT-PMP/PCP " : " ", #else GETFLAG(ENABLENATPMPMASK) ? " NAT-PMP " : " ", #endif #else " ", #endif GETFLAG(ENABLEUPNPMASK) ? "UPnP-IGD " : "", ext_if_name); if(GETFLAG(ENABLEUPNPMASK)) { unsigned short listen_port; listen_port = (v.port > 0) ? v.port : 0; /* open socket for HTTP connections. Listen on the 1st LAN address */ #ifdef ENABLE_IPV6 shttpl = OpenAndConfHTTPSocket(&listen_port, 1); #else /* ENABLE_IPV6 */ shttpl = OpenAndConfHTTPSocket(&listen_port); #endif /* ENABLE_IPV6 */ if(shttpl < 0) { syslog(LOG_ERR, "Failed to open socket for HTTP. EXITING"); return 1; } v.port = listen_port; syslog(LOG_NOTICE, "HTTP listening on port %d", v.port); #if defined(V6SOCKETS_ARE_V6ONLY) && defined(ENABLE_IPV6) if(!GETFLAG(IPV6DISABLEDMASK)) { shttpl_v4 = OpenAndConfHTTPSocket(&listen_port, 0); if(shttpl_v4 < 0) { syslog(LOG_ERR, "Failed to open socket for HTTP on port %hu (IPv4). EXITING", v.port); return 1; } } #endif /* V6SOCKETS_ARE_V6ONLY */ #ifdef ENABLE_HTTPS /* https */ listen_port = (v.https_port > 0) ? v.https_port : 0; #ifdef ENABLE_IPV6 shttpsl = OpenAndConfHTTPSocket(&listen_port, 1); #else /* ENABLE_IPV6 */ shttpsl = OpenAndConfHTTPSocket(&listen_port); #endif /* ENABLE_IPV6 */ if(shttpl < 0) { syslog(LOG_ERR, "Failed to open socket for HTTPS. EXITING"); return 1; } v.https_port = listen_port; syslog(LOG_NOTICE, "HTTPS listening on port %d", v.https_port); #if defined(V6SOCKETS_ARE_V6ONLY) && defined(ENABLE_IPV6) shttpsl_v4 = OpenAndConfHTTPSocket(&listen_port, 0); if(shttpsl_v4 < 0) { syslog(LOG_ERR, "Failed to open socket for HTTPS on port %hu (IPv4). EXITING", v.https_port); return 1; } #endif /* V6SOCKETS_ARE_V6ONLY */ #endif /* ENABLE_HTTPS */ #ifdef ENABLE_IPV6 if(find_ipv6_addr(NULL, ipv6_addr_for_http_with_brackets, sizeof(ipv6_addr_for_http_with_brackets)) > 0) { syslog(LOG_NOTICE, "HTTP IPv6 address given to control points : %s", ipv6_addr_for_http_with_brackets); } else { memcpy(ipv6_addr_for_http_with_brackets, "[::1]", 6); syslog(LOG_WARNING, "no HTTP IPv6 address, disabling IPv6"); SETFLAG(IPV6DISABLEDMASK); } #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 if(!GETFLAG(IPV6DISABLEDMASK)) { 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) #ifdef ENABLE_PCP { syslog(LOG_ERR, "Failed to open sockets for NAT-PMP/PCP."); } else { syslog(LOG_NOTICE, "Listening for NAT-PMP/PCP traffic on port %u", NATPMP_PORT); } #else { syslog(LOG_ERR, "Failed to open sockets for NAT PMP."); } else { syslog(LOG_NOTICE, "Listening for NAT-PMP traffic on port %u", NATPMP_PORT); } #endif } #endif #if defined(ENABLE_IPV6) && defined(ENABLE_PCP) spcp_v6 = OpenAndConfPCPv6Socket(); #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_INFO, "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, #ifdef ENABLE_HTTPS (unsigned short)v.https_port, #endif v.notify_interval << 1); memcpy(&lasttimeofday, &timeofday, sizeof(struct timeval)); timeout.tv_sec = v.notify_interval; 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_UPNPPINHOLE /* 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 /* ENABLE_UPNPPINHOLE */ /* 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); } #if defined(V6SOCKETS_ARE_V6ONLY) && defined(ENABLE_IPV6) if (shttpl_v4 >= 0) { FD_SET(shttpl_v4, &readset); max_fd = MAX( max_fd, shttpl_v4); } #endif #ifdef ENABLE_HTTPS if (shttpsl >= 0) { FD_SET(shttpsl, &readset); max_fd = MAX( max_fd, shttpsl); } #if defined(V6SOCKETS_ARE_V6ONLY) && defined(ENABLE_IPV6) if (shttpsl_v4 >= 0) { FD_SET(shttpsl_v4, &readset); max_fd = MAX( max_fd, shttpsl_v4); } #endif #endif /* ENABLE_HTTPS */ #ifdef ENABLE_IPV6 if (sudpv6 >= 0) { 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 #if defined(ENABLE_IPV6) && defined(ENABLE_PCP) if(spcp_v6 >= 0) { FD_SET(spcp_v6, &readset); max_fd = MAX(max_fd, spcp_v6); } #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 /* queued "sendto" */ { struct timeval next_send; i = get_next_scheduled_send(&next_send); if(i > 0) { #ifdef DEBUG syslog(LOG_DEBUG, "%d queued sendto", i); #endif i = get_sendto_fds(&writeset, &max_fd, &timeofday); if(timeofday.tv_sec > next_send.tv_sec || (timeofday.tv_sec == next_send.tv_sec && timeofday.tv_usec >= next_send.tv_usec)) { if(i > 0) { timeout.tv_sec = 0; timeout.tv_usec = 0; } } else { struct timeval tmp_timeout; tmp_timeout.tv_sec = (next_send.tv_sec - timeofday.tv_sec); tmp_timeout.tv_usec = (next_send.tv_usec - timeofday.tv_usec); if(tmp_timeout.tv_usec < 0) { tmp_timeout.tv_usec += 1000000; tmp_timeout.tv_sec--; } if(timeout.tv_sec > tmp_timeout.tv_sec || (timeout.tv_sec == tmp_timeout.tv_sec && timeout.tv_usec > tmp_timeout.tv_usec)) { timeout.tv_sec = tmp_timeout.tv_sec; timeout.tv_usec = tmp_timeout.tv_usec; } } } } 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 */ } i = try_sendto(&writeset); if(i < 0) { syslog(LOG_ERR, "try_sendto failed to send %d packets", -i); } #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)) { unsigned char msg_buff[PCP_MAX_LEN]; struct sockaddr_in senderaddr; socklen_t senderaddrlen; int len; memset(msg_buff, 0, PCP_MAX_LEN); senderaddrlen = sizeof(senderaddr); len = ReceiveNATPMPOrPCPPacket(snatpmp[i], (struct sockaddr *)&senderaddr, &senderaddrlen, NULL, msg_buff, sizeof(msg_buff)); if (len < 1) continue; #ifdef ENABLE_PCP if (msg_buff[0]==0) { /* version equals to 0 -> means NAT-PMP */ ProcessIncomingNATPMPPacket(snatpmp[i], msg_buff, len, &senderaddr); } else { /* everything else can be PCP */ ProcessIncomingPCPPacket(snatpmp[i], msg_buff, len, (struct sockaddr *)&senderaddr, NULL); } #else ProcessIncomingNATPMPPacket(snatpmp[i], msg_buff, len, &senderaddr); #endif } } #endif #if defined(ENABLE_IPV6) && defined(ENABLE_PCP) /* in IPv6, only PCP is supported, not NAT-PMP */ if(spcp_v6 >= 0 && FD_ISSET(spcp_v6, &readset)) { unsigned char msg_buff[PCP_MAX_LEN]; struct sockaddr_in6 senderaddr; socklen_t senderaddrlen; struct sockaddr_in6 receiveraddr; int len; memset(msg_buff, 0, PCP_MAX_LEN); senderaddrlen = sizeof(senderaddr); len = ReceiveNATPMPOrPCPPacket(spcp_v6, (struct sockaddr *)&senderaddr, &senderaddrlen, &receiveraddr, msg_buff, sizeof(msg_buff)); if(len >= 1) ProcessIncomingPCPPacket(spcp_v6, msg_buff, len, (struct sockaddr *)&senderaddr, &receiveraddr); } #endif /* process SSDP packets */ if(sudp >= 0 && FD_ISSET(sudp, &readset)) { /*syslog(LOG_INFO, "Received UDP Packet");*/ #ifdef ENABLE_HTTPS ProcessSSDPRequest(sudp, (unsigned short)v.port, (unsigned short)v.https_port); #else ProcessSSDPRequest(sudp, (unsigned short)v.port); #endif } #ifdef ENABLE_IPV6 if(sudpv6 >= 0 && FD_ISSET(sudpv6, &readset)) { syslog(LOG_INFO, "Received UDP Packet (IPv6)"); #ifdef ENABLE_HTTPS ProcessSSDPRequest(sudpv6, (unsigned short)v.port, (unsigned short)v.https_port); #else ProcessSSDPRequest(sudpv6, (unsigned short)v.port); #endif } #endif #ifdef USE_IFACEWATCHER /* 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)) { struct upnphttp * tmp; tmp = ProcessIncomingHTTP(shttpl, "HTTP"); if(tmp) { LIST_INSERT_HEAD(&upnphttphead, tmp, entries); } } #if defined(V6SOCKETS_ARE_V6ONLY) && defined(ENABLE_IPV6) if(shttpl_v4 >= 0 && FD_ISSET(shttpl_v4, &readset)) { struct upnphttp * tmp; tmp = ProcessIncomingHTTP(shttpl_v4, "HTTP"); if(tmp) { LIST_INSERT_HEAD(&upnphttphead, tmp, entries); } } #endif #ifdef ENABLE_HTTPS if(shttpsl >= 0 && FD_ISSET(shttpsl, &readset)) { struct upnphttp * tmp; tmp = ProcessIncomingHTTP(shttpsl, "HTTPS"); if(tmp) { InitSSL_upnphttp(tmp); LIST_INSERT_HEAD(&upnphttphead, tmp, entries); } } #if defined(V6SOCKETS_ARE_V6ONLY) && defined(ENABLE_IPV6) if(shttpsl_v4 >= 0 && FD_ISSET(shttpsl_v4, &readset)) { struct upnphttp * tmp; tmp = ProcessIncomingHTTP(shttpsl_v4, "HTTPS"); if(tmp) { InitSSL_upnphttp(tmp); LIST_INSERT_HEAD(&upnphttphead, tmp, entries); } } #endif #endif /* ENABLE_HTTPS */ #ifdef ENABLE_NFQUEUE /* process NFQ packets */ if(nfqh >= 0 && FD_ISSET(nfqh, &readset)) { /* 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: syslog(LOG_NOTICE, "shutting down MiniUPnPd"); /* send good-bye */ 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"); } } /* try to send pending packets */ finalize_sendto(); /* 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); #if defined(V6SOCKETS_ARE_V6ONLY) && defined(ENABLE_IPV6) if (shttpl_v4 >= 0) close(shttpl_v4); #endif #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 #if defined(ENABLE_IPV6) && defined(ENABLE_PCP) if(spcp_v6 >= 0) { close(spcp_v6); spcp_v6 = -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 for(i = 0; i < addr_count; i++) #else for(i = 0; i < addr_count * 2; i++) #endif close(snotify[i]); } /* remove pidfile */ 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_HTTPS free_ssl(); #endif #ifdef ENABLE_NATPMP free(snatpmp); #endif free(snotify); closelog(); #ifndef DISABLE_CONFIG_FILE freeoptions(); #endif return 0; } miniupnpd-1.8.20140523/upnphttp.h010064400017500000024000000073411232125352300155040ustar00nanardstaff/* $Id: upnphttp.h,v 1.38 2014/04/09 14:08:13 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2014 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #ifndef UPNPHTTP_H_INCLUDED #define UPNPHTTP_H_INCLUDED #include #include #include "config.h" #ifdef ENABLE_HTTPS #include #endif /* ENABLE_HTTPS */ #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 /* ENABLE_IPV6 */ #ifdef ENABLE_HTTPS SSL * ssl; #endif /* ENABLE_HTTPS */ 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 #ifdef ENABLE_HTTPS int init_ssl(void); void free_ssl(void); #endif /* ENABLE_HTTPS */ /* New_upnphttp() */ struct upnphttp * New_upnphttp(int); #ifdef ENABLE_HTTPS void InitSSL_upnphttp(struct upnphttp *); #endif /* ENABLE_HTTPS */ /* 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.20140523/upnpdescgen.c010064400017500000024000001060411230731613500161300ustar00nanardstaff/* $Id: upnpdescgen.c,v 1.77 2014/03/10 11:04:53 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2014 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #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 */ #ifdef ENABLE_MANUFACTURER_INFO_CONFIGURATION {"/friendlyName", friendly_name/*ROOTDEV_FRIENDLYNAME*/}, /* required */ {"/manufacturer", manufacturer_name/*ROOTDEV_MANUFACTURER*/}, /* required */ /* 8 */ {"/manufacturerURL", manufacturer_url/*ROOTDEV_MANUFACTURERURL*/}, /* optional */ {"/modelDescription", model_description/*ROOTDEV_MODELDESCRIPTION*/}, /* recommended */ {"/modelName", model_name/*ROOTDEV_MODELNAME*/}, /* required */ {"/modelNumber", modelnumber}, {"/modelURL", model_url/*ROOTDEV_MODELURL*/}, #else {"/friendlyName", 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}, #endif {"/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", GETFLAG(IPV6FCFWDISABLEDMASK) ? 0 : 1); 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", GETFLAG(IPV6FCINBOUNDDISALLOWEDMASK) ? 0 : 1); 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.20140523/upnpdescgen.h010064400017500000024000000045331203340734000161330ustar00nanardstaff/* $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.20140523/miniupnpdpath.h010064400017500000024000000023511203340734000164740ustar00nanardstaff/* $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.20140523/testupnpdescgen.c010064400017500000024000000120531230731613500170270ustar00nanardstaff/* $Id: testupnpdescgen.c,v 1.32 2014/03/10 11:04:52 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2014 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #include #include /* for mkdir */ #include #include #include #include "macros.h" #include "config.h" #include "upnpdescgen.h" #include "upnpdescstrings.h" #include "getifaddr.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[] = "";*/ #ifdef ENABLE_MANUFACTURER_INFO_CONFIGURATION char friendly_name[] = OS_NAME " router"; char manufacturer_name[] = ROOTDEV_MANUFACTURER; char manufacturer_url[] = ROOTDEV_MANUFACTURERURL; char model_name[] = ROOTDEV_MODELNAME; char model_description[] = ROOTDEV_MODELDESCRIPTION; char model_url[] = ROOTDEV_MODELURL; #endif char * use_ext_ip_addr = NULL; const char * ext_if_name = "eth0"; int runtime_flags = 0; int getifaddr(const char * ifname, char * buf, int len, struct in_addr * addr, struct in_addr * mask) { UNUSED(ifname); UNUSED(addr); UNUSED(mask); 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.20140523/upnpsoap.c010064400017500000024000001662311233511165600154730ustar00nanardstaff/* $Id: upnpsoap.c,v 1.127 2014/05/15 10:15:04 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2014 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #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"); /* TODO : be compliant with IGD spec for updating existing port mappings. See "WANIPConnection:1 Service Template Version 1.01" 2.2.20.PortMappingDescription : Overwriting Previous / Existing Port Mappings: If the RemoteHost, ExternalPort, PortMappingProtocol and InternalClient are exactly the same as an existing mapping, the existing mapping values for InternalPort, PortMappingDescription, PortMappingEnabled and PortMappingLeaseDuration are overwritten. */ 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; } syslog(LOG_INFO, "%s: deleting external ports: %hu-%hu, protocol: %s", action, startport, endport, protocol); port_list = upnp_get_portmappings_in_range(startport, endport, protocol, &number); if(number == 0) { SoapError(h, 730, "PortMappingNotFound"); ClearNameValueList(&data); return; } for(i = 0; i < number; i++) { r = upnp_delete_redirection(port_list[i], protocol); syslog(LOG_INFO, "%s: deleting external port: %hu, protocol: %s: %s", action, port_list[i], protocol, r < 0 ? "failed" : "ok"); } 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", GETFLAG(IPV6FCFWDISABLEDMASK) ? 0 : 1, GETFLAG(IPV6FCINBOUNDDISALLOWEDMASK) ? 0 : 1, action); BuildSendAndCloseSoapResp(h, body, bodylen); } static int CheckStatus(struct upnphttp * h) { if (GETFLAG(IPV6FCFWDISABLEDMASK)) { SoapError(h, 702, "FirewallDisabled"); return 0; } else if(GETFLAG(IPV6FCINBOUNDDISALLOWEDMASK)) { 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, "IGD2 pinhole", 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, /* proto */ NULL, 0, /* desc, desclen */ 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 (GETFLAG(IPV6FCFWDISABLEDMASK)) { 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, NULL, 0, /* desc, desclen */ &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, /* proto */ NULL, 0, /* desc, desclen */ 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, NULL, 0, /* desc, desclen */ &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 #ifdef ENABLE_DP_SERVICE static void SendSetupMessage(struct upnphttp * h, const char * action) { static const char resp[] = "" "%s" ""; char body[1024]; int bodylen; struct NameValueParserData data; const char * ProtocolType; /* string */ const char * InMessage; /* base64 */ const char * OutMessage = ""; /* base64 */ ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data); ProtocolType = GetValueFromNameValueList(&data, "NewProtocolType"); /* string */ InMessage = GetValueFromNameValueList(&data, "NewInMessage"); /* base64 */ if(ProtocolType == NULL || InMessage == NULL) { ClearNameValueList(&data); SoapError(h, 402, "Invalid Args"); return; } /*if(strcmp(ProtocolType, "DeviceProtection:1") != 0)*/ if(strcmp(ProtocolType, "WPS") != 0) { ClearNameValueList(&data); SoapError(h, 600, "Argument Value Invalid"); /* 703 ? */ return; } /* TODO : put here code for WPS */ bodylen = snprintf(body, sizeof(body), resp, action, "urn:schemas-upnp-org:service:DeviceProtection:1", OutMessage, action); BuildSendAndCloseSoapResp(h, body, bodylen); ClearNameValueList(&data); } static void GetSupportedProtocols(struct upnphttp * h, const char * action) { static const char resp[] = "" "%s" ""; char body[1024]; int bodylen; const char * ProtocolList = "\n" "" "WPS" "PKCS5" ""; bodylen = snprintf(body, sizeof(body), resp, action, "urn:schemas-upnp-org:service:DeviceProtection:1", ProtocolList, action); BuildSendAndCloseSoapResp(h, body, bodylen); } static void GetAssignedRoles(struct upnphttp * h, const char * action) { static const char resp[] = "" "%s" ""; char body[1024]; int bodylen; const char * RoleList = "Public"; /* list of roles separated by spaces */ #ifdef ENABLE_HTTPS if(h->ssl != NULL) { /* we should get the Roles of the session (based on client certificate) */ X509 * peercert; peercert = SSL_get_peer_certificate(h->ssl); if(peercert != NULL) { RoleList = "Admin Basic"; X509_free(peercert); } } #endif bodylen = snprintf(body, sizeof(body), resp, action, "urn:schemas-upnp-org:service:DeviceProtection:1", RoleList, 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 #ifdef ENABLE_DP_SERVICE /* DeviceProtection */ { "SendSetupMessage", SendSetupMessage}, /* Required */ { "GetSupportedProtocols", GetSupportedProtocols}, /* Required */ { "GetAssignedRoles", GetAssignedRoles}, /* 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((len == methodlen) && memcmp(p, soapMethods[i].methodName, len) == 0) { #ifdef DEBUG syslog(LOG_DEBUG, "Remote Call of SoapMethod '%s'\n", soapMethods[i].methodName); #endif 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.20140523/upnpsoap.h010064400017500000024000000012171203340734000154610ustar00nanardstaff/* $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.20140523/pf/obsdrdr.h010064400017500000024000000040561230606440100156640ustar00nanardstaff/* $Id: obsdrdr.h,v 1.23 2014/03/06 12:24:33 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2014 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #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_redirect_and_filter_rules() */ int delete_redirect_and_filter_rules(const char * ifname, unsigned short eport, int proto); #ifdef TEST int clear_redirect_rules(void); int clear_filter_rules(void); #endif #endif miniupnpd-1.8.20140523/pf/obsdrdr.c010064400017500000024000000622461232333661500156740ustar00nanardstaff/* $Id: obsdrdr.c,v 1.82 2014/04/15 23:15:25 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2014 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ /* * 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 \ * keep state label "test label" -> 192.168.0.42 port 12345 * or a rdr rule + a pass rule : * rdr quick on xl1 inet proto udp from any to any port = 54321 \ * keep state label "test label" -> 192.168.0.42 port 12345 * pass in quick on xl1 inet proto udp from any to 192.168.0.42 port = 12345 \ * flags S/SA keep state label "test label" * * - 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.42 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.42 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 #ifdef MACOSX #define PRIVATE 1 #endif #include #endif #include #include #include #include #include #include #include #include "../macros.h" #include "../config.h" #include "obsdrdr.h" #include "../upnpglobalvars.h" #ifndef USE_PF #error "USE_PF macro is undefined, check consistency between config.h and Makefile" #else /* 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; } int clear_filter_rules(void) { #ifndef PF_ENABLE_FILTER_RULES return 0; #else 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_FILTER; #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 } #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 #ifdef MACOSX pcr.rule.dst.xport.range.op = PF_OP_EQ; pcr.rule.dst.xport.range.port[0] = htons(eport); pcr.rule.dst.xport.range.port[1] = htons(eport); #else pcr.rule.dst.port_op = PF_OP_EQ; pcr.rule.dst.port[0] = htons(eport); pcr.rule.dst.port[1] = htons(eport); #endif #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; #endif #ifndef USE_IFNAME_IN_RULES UNUSED(ifname); #endif UNUSED(eport); 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(iport); 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); } /* we want any - iaddr port = # keep state label */ inet_pton(AF_INET, iaddr, &pcr.rule.dst.addr.v.a.addr.v4.s_addr); pcr.rule.dst.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); #endif if(1) { 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; } } } } 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; } static int priv_delete_redirect_rule(const char * ifname, unsigned short eport, int proto, unsigned short * iport, in_addr_t * iaddr) { 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; #ifdef MACOSX *eport = ntohs(pr.rule.dst.xport.range.port[0]); #else *eport = ntohs(pr.rule.dst.port[0]); #endif #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 /* TEST */ #endif /* USE_PF */ miniupnpd-1.8.20140523/pf/testobsdrdr.c010064400017500000024000000070651230607076700165760ustar00nanardstaff/* $Id: testobsdrdr.c,v 1.28 2014/03/06 13:02:47 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2014 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #include #include #include #include #include #include "obsdrdr.h" /*int logpackets = 1;*/ int runtime_flags = 0; const char * tag = 0; const char * anchor_name = "miniupnpd"; const char * queue = NULL; 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 argc, 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; int clear = 0; if(argc > 1) { if(0 == strcmp(argv[1], "--clear") || 0 == strcmp(argv[1], "-c")) clear = 1; } 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 if(add_redirect_rule2("ep0", "8.8.8.8", 12123, "192.168.1.125", 1234, IPPROTO_UDP, "test description", 0) < 0) printf("add_redirect_rule2() #3 failed\n"); if(add_redirect_rule2("em0", NULL, 12123, "127.1.2.3", 1234, IPPROTO_TCP, "test description tcp", 0) < 0) printf("add_redirect_rule2() #4 failed\n"); if(add_filter_rule2("em0", NULL, "127.1.2.3", 12123, 1234, IPPROTO_TCP, "test description tcp") < 0) printf("add_filter_rule2() #1 failed\n"); 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"); if(delete_redirect_rule("ep0", 12123, IPPROTO_UDP) < 0) printf("delete_redirect_rule() failed\n"); if(delete_redirect_and_filter_rules("em0", 12123, IPPROTO_TCP) < 0) printf("delete_redirect_and_filter_rules() failed\n"); test_index(); if(clear) { clear_redirect_rules(); clear_filter_rules(); } /*list_rules();*/ return 0; } miniupnpd-1.8.20140523/pf/Makefile010064400017500000024000000010051230413673100155070ustar00nanardstaff# $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.20140523/bsd/Makefile010064400017500000024000000006621156543264100156710ustar00nanardstaff# $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.20140523/bsd/getifstats.c010064400017500000024000000063151231377315700165550ustar00nanardstaff/* 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__) #ifdef __DragonFly__ #define _KERNEL_STRUCTURES #endif #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.20140523/bsd/testgetifstats.c010064400017500000024000000013311172522177200174430ustar00nanardstaff/* 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.20140523/solaris/getifstats.c010064400017500000024000000044161172522177200174560ustar00nanardstaff/* 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.20140523/getifstats.h010064400017500000024000000012231203340734000157660ustar00nanardstaff/* 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.20140523/upnpredirect.h010064400017500000024000000071231203340734000163220ustar00nanardstaff/* $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.20140523/upnpredirect.c010064400017500000024000000415401231526242000163170ustar00nanardstaff/* $Id: upnpredirect.c,v 1.84 2014/03/28 12:03:28 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2014 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #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" #include "portinuse.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; } #ifdef CHECK_PORTINUSE } else if (port_in_use(ext_if_name, eport, proto, iaddr, iport) > 0) { syslog(LOG_INFO, "port %hu protocol %s already in use", eport, protocol); return -2; #endif /* CHECK_PORTINUSE */ } 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); #elif defined(USE_PF) r = delete_redirect_and_filter_rules(ext_if_name, 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; } #ifdef PCP_PEER i=0; while(get_peer_rule_by_index(i, /*ifname*/0, &tmp->eport, 0, 0, &iport, &proto, 0, 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; } #endif 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.20140523/getifaddr.c010064400017500000024000000136341233217235100155510ustar00nanardstaff/* $Id: getifaddr.c,v 1.23 2014/05/06 14:40:53 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2014 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #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) || defined(ENABLE_PCP) #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, SIOCGIFFLAGS, &ifr, &ifrlen) < 0) { syslog(LOG_DEBUG, "ioctl(s, SIOCGIFFLAGS, ...): %m"); close(s); return -1; } if ((ifr.ifr_flags & IFF_UP) == 0) { syslog(LOG_DEBUG, "network interface %s is down", ifname); close(s); 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(buf) { 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: if(buf) { 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_PCP int getifaddr_in6(const char * ifname, int af, struct in6_addr * addr) { #if defined(ENABLE_IPV6) || defined(USE_GETIFADDRS) struct ifaddrs * ifap; struct ifaddrs * ife; #ifdef ENABLE_IPV6 const struct sockaddr_in6 * tmpaddr; #endif /* ENABLE_IPV6 */ int found = 0; if(!ifname || ifname[0]=='\0') return -1; if(getifaddrs(&ifap)<0) { syslog(LOG_ERR, "getifaddrs: %m"); return -1; } for(ife = ifap; ife && !found; 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) continue; switch(ife->ifa_addr->sa_family) { case AF_INET: /* IPv4-mapped IPv6 address ::ffff:1.2.3.4 */ memset(addr->s6_addr, 0, 10); addr->s6_addr[10] = 0xff; addr->s6_addr[11] = 0xff; memcpy(addr->s6_addr + 12, &(((struct sockaddr_in *)ife->ifa_addr)->sin_addr.s_addr), 4); found = 1; break; #ifdef ENABLE_IPV6 case AF_INET6: tmpaddr = (const struct sockaddr_in6 *)ife->ifa_addr; if(!IN6_IS_ADDR_LOOPBACK(&tmpaddr->sin6_addr) && !IN6_IS_ADDR_LINKLOCAL(&tmpaddr->sin6_addr)) { memcpy(addr->s6_addr, &tmpaddr->sin6_addr, 16); found = 1; } break; #endif /* ENABLE_IPV6 */ } } freeifaddrs(ifap); return (found ? 0 : -1); #else /* defined(ENABLE_IPV6) || defined(USE_GETIFADDRS) */ /* IPv4 only */ struct in_addr addr4; if(af != AF_INET) return -1; if(getifaddr(ifname, NULL, 0, &addr4, NULL) < 0) return -1; /* IPv4-mapped IPv6 address ::ffff:1.2.3.4 */ memset(addr->s6_addr, 0, 10); addr->s6_addr[10] = 0xff; addr->s6_addr[11] = 0xff; memcpy(addr->s6_addr + 12, &addr4.s_addr, 4); return 0; #endif } #endif /* ENABLE_PCP */ #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.20140523/getifaddr.h010064400017500000024000000016041233217235100155500ustar00nanardstaff/* $Id: getifaddr.h,v 1.10 2014/05/06 14:40:53 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; struct in6_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); int getifaddr_in6(const char * ifname, int af, struct in6_addr* addr); /* find a non link local IP v6 address for the interface. * if ifname is NULL, look for all interfaces */ int find_ipv6_addr(const char * ifname, char * dst, int n); #endif miniupnpd-1.8.20140523/daemonize.c010064400017500000024000000041451172522177100155760ustar00nanardstaff/* $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.20140523/daemonize.h010064400017500000024000000015151203340734000155700ustar00nanardstaff/* $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.20140523/upnpglobalvars.h010064400017500000024000000074071233732635300166750ustar00nanardstaff/* $Id: upnpglobalvars.h,v 1.41 2014/05/22 07:51:08 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2014 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #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; extern unsigned long int min_lifetime; extern unsigned long int max_lifetime; /* 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 #ifdef ENABLE_IPV6 #define IPV6DISABLEDMASK 0x0080 #endif #ifdef ENABLE_6FC_SERVICE #define IPV6FCFWDISABLEDMASK 0x0100 #define IPV6FCINBOUNDDISALLOWEDMASK 0x0200 #endif #ifdef ENABLE_PCP #define PCP_ALLOWTHIRDPARTYMASK 0x0400 #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[]; #ifdef ENABLE_MANUFACTURER_INFO_CONFIGURATION #define FRIENDLY_NAME_MAX_LEN (64) extern char friendly_name[]; #define MANUFACTURER_NAME_MAX_LEN (64) extern char manufacturer_name[]; #define MANUFACTURER_URL_MAX_LEN (64) extern char manufacturer_url[]; #define MODEL_NAME_MAX_LEN (64) extern char model_name[]; #define MODEL_DESCRIPTION_MAX_LEN (64) extern char model_description[]; #define MODEL_URL_MAX_LEN (64) extern char model_url[]; #endif /* UPnP permission rules : */ extern struct upnpperm * upnppermlist; extern unsigned int num_upnpperm; #ifdef PCP_SADSCP extern struct dscp_values* dscp_values_list; extern unsigned int num_dscp_values; #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_peer_chain; extern const char * miniupnpd_forward_chain; #ifdef ENABLE_UPNPPINHOLE 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]; /* address used to bind local services */ extern struct in6_addr ipv6_bind_addr; #endif extern const char * minissdpdsocketpath; /* BOOTID.UPNP.ORG and CONFIGID.UPNP.ORG */ extern unsigned int upnp_bootid; extern unsigned int upnp_configid; #endif miniupnpd-1.8.20140523/upnpglobalvars.c010064400017500000024000000070031233732635300166600ustar00nanardstaff/* $Id: upnpglobalvars.c,v 1.37 2014/05/22 07:51:08 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2014 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #include #include "config.h" #include "upnpglobalvars.h" #include "upnpdescstrings.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; unsigned long downstream_bitrate = 0; unsigned long upstream_bitrate = 0; /* startup time */ time_t startup_time = 0; #ifdef ENABLE_PCP /* for PCP */ unsigned long int min_lifetime = 120; unsigned long int max_lifetime = 86400; #endif 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]; #ifdef ENABLE_MANUFACTURER_INFO_CONFIGURATION /* friendly name for root devices in XML description */ char friendly_name[FRIENDLY_NAME_MAX_LEN] = OS_NAME " router"; /* manufacturer name for root devices in XML description */ char manufacturer_name[MANUFACTURER_NAME_MAX_LEN] = ROOTDEV_MANUFACTURER; /* manufacturer url for root devices in XML description */ char manufacturer_url[MANUFACTURER_URL_MAX_LEN] = ROOTDEV_MANUFACTURERURL; /* model name for root devices in XML description */ char model_name[MODEL_NAME_MAX_LEN] = ROOTDEV_MODELNAME; /* model description for root devices in XML description */ char model_description[MODEL_DESCRIPTION_MAX_LEN] = ROOTDEV_MODELDESCRIPTION; /* model url for root devices in XML description */ char model_url[MODEL_URL_MAX_LEN] = ROOTDEV_MODELURL; #endif /* UPnP permission rules : */ struct upnpperm * upnppermlist = 0; unsigned int num_upnpperm = 0; #ifdef PCP_SADSCP struct dscp_values* dscp_values_list = 0; unsigned int num_dscp_values = 0; #endif /*PCP_SADSCP*/ /* 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_peer_chain = "MINIUPNPD-PCP-PEER"; const char * miniupnpd_forward_chain = "MINIUPNPD"; #ifdef ENABLE_UPNPPINHOLE 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]; /* address used to bind local services */ struct in6_addr ipv6_bind_addr; #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; miniupnpd-1.8.20140523/LICENSE010064400017500000024000000027101172522177100144600ustar00nanardstaffMiniUPnPd 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.20140523/README010064400017500000024000000024101227143716500143330ustar00nanardstaffMiniUPnP project (c) 2006-2014 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 NAT-PMP is the precursor of Port Control Protocol (PCP). In 2013, support for PCP has been added too. Read the INSTALL files for instructions to compile, install and configure miniupnpd on your system. 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.20140523/INSTALL010064400017500000024000000150721227143716500145140ustar00nanardstaffMiniUPnP project. (c) 2006-2014 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" or/and "anchor miniupnpd" lines to /etc/pf.conf (Since OpenBSD 4.7, rdr-anchor lines are no longer used and should be removed, leaving only the anchor lines). - 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" (if applicable) and/or "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' (if available) or 'make -f Makefile.macosx' to build ============================== Mac OS X/pf ================================ Starting with Mac OS X 10.7 Lion, pf replaced ipfw as the OS X firewall. also bsdmake is not available anymore. Make sure you have installed the Xcode commande line tools (from the Xcode Preferences menu or using 'xcode-select --install' command) You'll need to download xnu sources : https://github.com/opensource-apple/xnu > INCLUDES="-I.../xnu/bsd -I.../xnu/libkern" make -f Makefile.macosx ============================ 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 requested, permission rules are evaluated in top-down order and the first permission rule matched gives the response : 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 permission 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 is done automatically during install. 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.20140523/Changelog.txt010064400017500000024000000730611233766342000161130ustar00nanardstaff$Id: Changelog.txt,v 1.375 2014/05/23 15:19:25 nanard Exp $ 2014/05/22: Add ipv6_bind_address (option "ipv6_listening_ip") disable IPv6 when socket(PF_INTET6, ...) errors with EAFNOSUPPORT Add IPV6 multicast membership only on selected "LAN" interfaces 2014/05/20: be more strict when parsing LAN addresses / interface names 2014/05/19: set source address for IPV6 packets sendto_schedule2() etc. 2014/05/15: Fix deletePortMappingRange() 2014/04/21: Fix PCP when request contain 0 IPv4 external address Remove pointer casting in natpmp.c 2014/04/15: rewrite iptables_*.sh scripts 2014/04/12: Add FreeBSD support for CHECK_PORTINUSE Add PCP support for CHECK_PORTINUSE 2014/04/09: Add HTTPS support and skeleton of DeviceProtection implementation 2014/03/24: start work to enable IPv6 PCP operations 2014/03/14: reject renewal of subscribtion that already timeouted Support for multiple URL in Callback: header (SUBSCRIBE) 2014/03/13: fix getifaddr_in6() (used for PCP) implement permissions with PCP Map fix upnp_event_notify_connect() when ENABLE_IPV6 is set 2014/03/10: Enable PCP by default. Work in IPv6 on system where PF_INET6 are restricted to IPv6 only change ipv6_enabled/ipv6fc_inbound_pinhole_allowed/ipv6fc_firewall_enabled global vars to flags in runtime_flags 2014/03/09: IPv6 support in testgetifaddr 2014/03/07: NAT-PMP search an allowed eport instead of returning an error if the original eport is not allowed. 2014/03/06: Fix add_filter_rule2() for pf. 2014/02/28: log message when shutting down natpmp : avoid hang when all external ports in use 2014/02/25: add implementation of scheduled sendto (asyncsendto) in order to retry failed sendto() calls or schedule sending of packets 2014/02/24: Defaulting to SSDP_RESPOND_SAME_VERSION 2014/02/11: Fix PCP Map renewal 2014/02/06: possibility to disable ipv6 at runtime 2014/02/03: PCP : Add support for ANNOUNCE requests minixml now handle XML comments 2013/12/16: Attempt to compile with OS X/pf 2013/12/13: Make all manufacturer info configurable thanks to Leo Moll Merge PCP support (see https://github.com/miniupnp/miniupnp) 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/07/10: Detect port in use - patch by David Kerr 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.20140523/linux/getifstats.c010064400017500000024000000064251214100740700171310ustar00nanardstaff/* $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.20140523/netfilter/iptcrdr.c010064400017500000024000001152101231526243000172530ustar00nanardstaff/* $Id: iptcrdr.c,v 1.52 2014/03/28 12:03:36 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 #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); static int addpeernatrule(int proto, const char * eaddr, unsigned short eport, const char * iaddr, unsigned short iport, const char * rhost, unsigned short rport); static int addpeerdscprule(int proto, unsigned char dscp, const char * iaddr, unsigned short iport, const char * rhost, unsigned short rport); /* 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; } /* add_redirect_rule2() */ int add_peer_redirect_rule2(const char * ifname, const char * rhost, unsigned short rport, const char * eaddr, unsigned short eport, const char * iaddr, unsigned short iport, int proto, const char * desc, unsigned int timestamp) { int r; UNUSED(ifname); r = addpeernatrule(proto, eaddr, eport, iaddr, iport, rhost, rport); if(r >= 0) add_redirect_desc(eport, proto, desc, timestamp); return r; } int add_peer_dscp_rule2(const char * ifname, const char * rhost, unsigned short rport, unsigned char dscp, const char * iaddr, unsigned short iport, int proto, const char * desc, unsigned int timestamp) { int r; UNUSED(ifname); UNUSED(desc); UNUSED(timestamp); r = addpeerdscprule(proto, dscp, iaddr, iport, rhost, rport); /* if(r >= 0) add_redirect_desc(dscp, 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) { return get_nat_redirect_rule(miniupnpd_nat_chain, ifname, eport, proto, iaddr, iaddrlen, iport, desc, desclen, rhost, rhostlen, timestamp, packets, bytes); } int get_nat_redirect_rule(const char * nat_chain_name, 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(nat_chain_name, h)) { syslog(LOG_ERR, "chain %s not found", nat_chain_name); } else { #ifdef IPTABLES_143 for(e = iptc_first_rule(nat_chain_name, h); e; e = iptc_next_rule(e, h)) #else for(e = iptc_first_rule(nat_chain_name, &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; } /* get_peer_rule_by_index() * return -1 when the rule was not found */ int get_peer_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 short * rport, unsigned int * timestamp, u_int64_t * packets, u_int64_t * bytes) { int r = -1; #if USE_INDEX_FROM_DESC_LIST && 0 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_peer_rule_by_index() : " "iptc_init() failed : %s", iptc_strerror(errno)); return -1; } if(!iptc_is_chain(miniupnpd_peer_chain, h)) { syslog(LOG_ERR, "chain %s not found", miniupnpd_peer_chain); } else { #ifdef IPTABLES_143 for(e = iptc_first_rule(miniupnpd_peer_chain, h); e; e = iptc_next_rule(e, h)) #else for(e = iptc_first_rule(miniupnpd_peer_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; if (rport) *rport = info->dpts[0]; if (iport) *iport = info->spts[0]; } else { const struct ipt_udp * info; info = (const struct ipt_udp *)match->data; if (rport) *rport = info->dpts[0]; if (iport) *iport = info->spts[0]; } target = (void *)e + e->target_offset; mr = (const struct ip_nat_multi_range *)&target->data[0]; *eport = 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.dst.s_addr) { snprintip(rhost, rhostlen, ntohl(e->ip.dst.s_addr)); } else { rhost[0] = '\0'; } } if(iaddr && iaddrlen > 0) { if(e->ip.src.s_addr) { snprintip(iaddr, iaddrlen, 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, r2 = -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; 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"); h = NULL; break; } } } if(h) #ifdef IPTABLES_143 iptc_free(h); #else iptc_free(&h); #endif } /*delete PEER rule*/ if((h = iptc_init("nat"))) { i = 0; /* we must find the right index for the filter rule */ #ifdef IPTABLES_143 for(e = iptc_first_rule(miniupnpd_peer_chain, h); e; e = iptc_next_rule(e, h), i++) #else for(e = iptc_first_rule(miniupnpd_peer_chain, &h); e; e = iptc_next_rule(e, &h), i++) #endif { if(proto==e->ip.proto) { target = (void *)e + e->target_offset; mr = (const struct ip_nat_multi_range *)&target->data[0]; if (eport != ntohs(mr->range[0].min.all)) { continue; } iaddr = e->ip.src.s_addr; 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; iport = info->spts[0]; } else { const struct ipt_udp * info; info = (const struct ipt_udp *)match->data; iport = info->dpts[0]; } index = i; syslog(LOG_INFO, "Trying to delete peer rule at index %u", index); r2 = delete_rule_and_commit(index, h, miniupnpd_peer_chain, "delete_peer_rule"); h = NULL; break; } } } if(h) #ifdef IPTABLES_143 iptc_free(h); #else iptc_free(&h); #endif /*delete DSCP rule*/ if((r2==0)&&(h = iptc_init("mangle"))) { i = 0; index = -1; /* we must find the right index for the filter rule */ #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; /*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->spts[0]) continue; } else { const struct ipt_udp * info; info = (const struct ipt_udp *)match->data; if(iport != info->spts[0]) continue; } if(iaddr != e->ip.src.s_addr) continue; index = i; syslog(LOG_INFO, "Trying to delete dscp rule at index %u", index); r2 = delete_rule_and_commit(index, h, miniupnpd_nat_chain, "delete_dscp_rule"); h = NULL; break; } } if (h) #ifdef IPTABLES_143 iptc_free(h); #else iptc_free(&h); #endif } del_redirect_desc(eport, proto); return r*r2; } /* ==================================== */ /* 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, unsigned short sport) { 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; if (sport == 0) { tcpinfo->spts[0] = 0; /* all source ports */ tcpinfo->spts[1] = 0xFFFF; } else { tcpinfo->spts[0] = sport; /* specified source port */ tcpinfo->spts[1] = sport; } if (dport == 0) { tcpinfo->dpts[0] = 0; /* all destination ports */ tcpinfo->dpts[1] = 0xFFFF; } else { tcpinfo->dpts[0] = dport; /* specified destination port */ tcpinfo->dpts[1] = dport; } return match; } static struct ipt_entry_match * get_udp_match(unsigned short dport, unsigned short sport) { 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; if (sport == 0) { udpinfo->spts[0] = 0; /* all source ports */ udpinfo->spts[1] = 0xFFFF; } else { udpinfo->spts[0] = sport; /* specified source port */ udpinfo->spts[1] = sport; } if (dport == 0) { udpinfo->dpts[0] = 0; /* all destination ports */ udpinfo->dpts[1] = 0xFFFF; } else { 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; } static struct ipt_entry_target * get_snat_target(const char * saddr, unsigned short sport) { 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, "SNAT", 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(saddr); range->flags |= IP_NAT_RANGE_MAP_IPS; range->min.all = range->max.all = htons(sport); range->flags |= IP_NAT_RANGE_PROTO_SPECIFIED; return target; } static struct ipt_entry_target * get_dscp_target(unsigned char dscp) { struct ipt_entry_target * target; struct xt_DSCP_info * di; size_t size; size = IPT_ALIGN(sizeof(struct ipt_entry_target)) + IPT_ALIGN(sizeof(struct xt_DSCP_info)); target = calloc(1, size); target->u.target_size = size; strncpy(target->u.user.name, "DSCP", sizeof(target->u.user.name)); /* one ip_nat_range already included in ip_nat_multi_range */ di = (struct xt_DSCP_info *)&target->data[0]; di->dscp=dscp; 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, 0); } else { match = get_udp_match(eport, 0); } 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; } /* iptables -t nat -A MINIUPNPD-PCP-PEER -s iaddr -d rhost * -p proto --sport iport --dport rport -j SNAT * --to-source ext_ip:eport */ static int addpeernatrule(int proto, const char * eaddr, unsigned short eport, const char * iaddr, unsigned short iport, const char * rhost, unsigned short rport) { 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; /* TODO: Fill port matches and SNAT */ if(proto == IPPROTO_TCP) { match = get_tcp_match(rport, iport); } else { match = get_udp_match(rport, iport); } e->nfcache = NFC_IP_DST_PT | NFC_IP_SRC_PT; target = get_snat_target(eaddr, eport); 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; /* internal host */ if(iaddr && (iaddr[0] != '\0') && (0 != strcmp(iaddr, "*"))) { e->ip.src.s_addr = inet_addr(iaddr); e->ip.smsk.s_addr = INADDR_NONE; } /* remote host */ if(rhost && (rhost[0] != '\0') && (0 != strcmp(rhost, "*"))) { e->ip.dst.s_addr = inet_addr(rhost); e->ip.dmsk.s_addr = INADDR_NONE; } r = iptc_init_verify_and_append("nat", miniupnpd_peer_chain, e, "addpeernatrule()"); free(target); free(match); free(e); return r; } /* iptables -t mangle -A MINIUPNPD -s iaddr -d rhost * -p proto --sport iport --dport rport -j DSCP * --set-dscp 0xXXXX */ static int addpeerdscprule(int proto, unsigned char dscp, const char * iaddr, unsigned short iport, const char * rhost, unsigned short rport) { 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; /* TODO: Fill port matches and SNAT */ if(proto == IPPROTO_TCP) { match = get_tcp_match(rport, iport); } else { match = get_udp_match(rport, iport); } e->nfcache = NFC_IP_DST_PT | NFC_IP_SRC_PT; target = get_dscp_target(dscp); 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; /* internal host */ if(iaddr && (iaddr[0] != '\0') && (0 != strcmp(iaddr, "*"))) { e->ip.src.s_addr = inet_addr(iaddr); e->ip.smsk.s_addr = INADDR_NONE; } /* remote host */ if(rhost && (rhost[0] != '\0') && (0 != strcmp(rhost, "*"))) { e->ip.dst.s_addr = inet_addr(rhost); e->ip.dmsk.s_addr = INADDR_NONE; } r = iptc_init_verify_and_append("mangle", miniupnpd_nat_chain, e, "addpeerDSCPrule()"); 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,0); } else { match = get_udp_match(iport,0); } 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.20140523/netfilter/iptcrdr.h010064400017500000024000000047061231526243100172700ustar00nanardstaff/* $Id: iptcrdr.h,v 1.21 2014/03/28 12:03:37 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_peer_redirect_rule2(const char * ifname, const char * rhost, unsigned short rport, const char * eaddr, 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); int add_peer_dscp_rule2(const char * ifname, const char * rhost, unsigned short rport, unsigned char dscp, const char * iaddr, unsigned short iport, int proto, const char * desc, unsigned int timestamp); int get_nat_ext_addr(struct sockaddr* src, struct sockaddr *dst, uint8_t proto, struct sockaddr* ret_ext); int get_peer_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 short * rport, unsigned int * timestamp, u_int64_t * packets, u_int64_t * bytes); int get_nat_redirect_rule(const char * nat_chain_name, 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); /* for debug */ int list_redirect_rule(const char * ifname); #endif miniupnpd-1.8.20140523/netfilter/iptables_display.sh010075500017500000024000000006401225257600600213360ustar00nanardstaff#! /bin/sh # $Id: iptables_display.sh,v 1.5 2013/12/13 12:15:24 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 nat -L POSTROUTING $IPTABLES -v -n -t nat -L MINIUPNPD-PCP-PEER $IPTABLES -v -n -t mangle -L MINIUPNPD $IPTABLES -v -n -t filter -L FORWARD $IPTABLES -v -n -t filter -L MINIUPNPD miniupnpd-1.8.20140523/netfilter/iptables_flush.sh010075500017500000024000000004201225257600600210060ustar00nanardstaff#! /bin/sh # $Id: iptables_flush.sh,v 1.4 2013/12/13 12:15:24 nanard Exp $ IPTABLES=/sbin/iptables #flush all rules owned by miniupnpd $IPTABLES -t nat -F MINIUPNPD $IPTABLES -t nat -F MINIUPNPD-PCP-PEER $IPTABLES -t filter -F MINIUPNPD $IPTABLES -t mangle -F MINIUPNPD miniupnpd-1.8.20140523/netfilter/iptables_init.sh010075500017500000024000000020511232333656000206300ustar00nanardstaff#! /bin/sh # $Id: iptables_init.sh,v 1.8 2014/04/15 13:45:08 nanard Exp $ IPTABLES="`which iptables`" || exit 1 IP="`which ip`" || exit 1 #change this parameters : #EXTIF=eth0 EXTIF="`LC_ALL=C $IP -4 route | grep 'default' | sed -e 's/.*dev[[:space:]]*//' -e 's/[[:space:]].*//'`" || exit 1 EXTIP="`LC_ALL=C $IP -4 addr show $EXTIF | awk '/inet/ { print $2 }' | cut -d "/" -f 1`" echo "External IP = $EXTIP" #adding the MINIUPNPD chain for nat $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 mangle $IPTABLES -t mangle -N MINIUPNPD $IPTABLES -t mangle -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 #adding the MINIUPNPD chain for nat $IPTABLES -t nat -N MINIUPNPD-PCP-PEER $IPTABLES -t nat -A POSTROUTING -o $EXTIF -j MINIUPNPD-PCP-PEER miniupnpd-1.8.20140523/netfilter/iptables_removeall.sh010075500017500000024000000023241232333656000216560ustar00nanardstaff#! /bin/sh # $Id: iptables_removeall.sh,v 1.8 2014/04/15 13:45:08 nanard Exp $ IPTABLES="`which iptables`" || exit 1 IP="`which ip`" || exit 1 #change this parameters : #EXTIF=eth0 EXTIF="`LC_ALL=C $IP -4 route | grep 'default' | sed -e 's/.*dev[[:space:]]*//' -e 's/[[:space:]].*//'`" || exit 1 EXTIP="`LC_ALL=C $IP -4 addr show $EXTIF | awk '/inet/ { print $2 }' | cut -d "/" -f 1`" #removing the MINIUPNPD chain for nat $IPTABLES -t nat -F MINIUPNPD #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 mangle $IPTABLES -t mangle -F MINIUPNPD $IPTABLES -t mangle -D PREROUTING -i $EXTIF -j MINIUPNPD $IPTABLES -t mangle -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 #removing the MINIUPNPD-PCP-PEER chain for nat $IPTABLES -t nat -F MINIUPNPD-PCP-PEER #removing the rule to MINIUPNPD-PCP-PEER $IPTABLES -t nat -D POSTROUTING -o $EXTIF -j MINIUPNPD-PCP-PEER $IPTABLES -t nat -X MINIUPNPD-PCP-PEER miniupnpd-1.8.20140523/netfilter/Makefile010064400017500000024000000047631225260365300171200ustar00nanardstaff# $Id: Makefile,v 1.7 2013/12/13 13:10:48 nanard Exp $ CFLAGS?=-Wall -g -D_GNU_SOURCE -DDEBUG -Wstrict-prototypes -Wdeclaration-after-statement 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 LIBS += /lib/libip4tc.so /lib/libip6tc.so all: iptcrdr.o testiptcrdr iptpinhole.o \ testiptcrdr_peer testiptcrdr_dscp test_nfct_get # testiptpinhole clean: $(RM) *.o testiptcrdr testiptpinhole testiptcrdr_peer test_nfct_get \ testiptcrdr_dscp testiptcrdr: testiptcrdr.o upnpglobalvars.o $(LIBS) testiptcrdr_peer: testiptcrdr_peer.o upnpglobalvars.o $(LIBS) testiptcrdr_dscp: testiptcrdr_dscp.o upnpglobalvars.o $(LIBS) testiptpinhole: testiptpinhole.o iptpinhole.o upnpglobalvars.o $(LIBS) test_nfct_get: test_nfct_get.o test_nfct_get.o -lmnl -lnetfilter_conntrack test_nfct_get.o: test_nfct_get.c testiptcrdr_peer.o: testiptcrdr_peer.c testiptcrdr_dscp.o: testiptcrdr_dscp.c iptcrdr.o: iptcrdr.c iptcrdr.h iptpinhole.o: iptpinhole.c iptpinhole.h upnpglobalvars.o: ../upnpglobalvars.c ../upnpglobalvars.h $(CC) -c -o $@ $< miniupnpd-1.8.20140523/netfilter/testiptcrdr.c010064400017500000024000000035121225260742500201630ustar00nanardstaff/* $Id: testiptcrdr.c,v 1.19 2013/12/13 13:40:42 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.c" #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]); printf("trying to redirect port %hu to %s:%hu\n", eport, iaddr, iport); if(addnatrule(IPPROTO_TCP, eport, iaddr, iport, NULL) < 0) return -1; if(add_filter_rule(IPPROTO_TCP, NULL, iaddr, iport) < 0) return -1; /* 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.20140523/minixml.c010064400017500000024000000130041227446335500153000ustar00nanardstaff/* $Id: minixml.c,v 1.11 2014/02/03 15:54:12 nanard Exp $ */ /* minixml.c : the minimum size a xml parser can be ! */ /* Project : miniupnp * webpage: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * Author : Thomas Bernard Copyright (c) 2005-2014, Thomas BERNARD All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include "minixml.h" /* parseatt : used to parse the argument list * return 0 (false) in case of success and -1 (true) if the end * of the xmlbuffer is reached. */ static int parseatt(struct xmlparser * p) { const char * attname; int attnamelen; const char * attvalue; int attvaluelen; while(p->xml < p->xmlend) { if(*p->xml=='/' || *p->xml=='>') return 0; if( !IS_WHITE_SPACE(*p->xml) ) { char sep; attname = p->xml; attnamelen = 0; while(*p->xml!='=' && !IS_WHITE_SPACE(*p->xml) ) { attnamelen++; p->xml++; if(p->xml >= p->xmlend) return -1; } while(*(p->xml++) != '=') { if(p->xml >= p->xmlend) return -1; } while(IS_WHITE_SPACE(*p->xml)) { p->xml++; if(p->xml >= p->xmlend) return -1; } sep = *p->xml; if(sep=='\'' || sep=='\"') { p->xml++; if(p->xml >= p->xmlend) return -1; attvalue = p->xml; attvaluelen = 0; while(*p->xml != sep) { attvaluelen++; p->xml++; if(p->xml >= p->xmlend) return -1; } } else { attvalue = p->xml; attvaluelen = 0; while( !IS_WHITE_SPACE(*p->xml) && *p->xml != '>' && *p->xml != '/') { attvaluelen++; p->xml++; if(p->xml >= p->xmlend) return -1; } } /*printf("%.*s='%.*s'\n", attnamelen, attname, attvaluelen, attvalue);*/ if(p->attfunc) p->attfunc(p->data, attname, attnamelen, attvalue, attvaluelen); } p->xml++; } return -1; } /* parseelt parse the xml stream and * call the callback functions when needed... */ static void parseelt(struct xmlparser * p) { int i; const char * elementname; while(p->xml < (p->xmlend - 1)) { if((p->xml + 4) <= p->xmlend && (0 == memcmp(p->xml, "", 3) != 0); p->xml += 3; } else if((p->xml)[0]=='<' && (p->xml)[1]!='?') { i = 0; elementname = ++p->xml; while( !IS_WHITE_SPACE(*p->xml) && (*p->xml!='>') && (*p->xml!='/') ) { i++; p->xml++; if (p->xml >= p->xmlend) return; /* to ignore namespace : */ if(*p->xml==':') { i = 0; elementname = ++p->xml; } } if(i>0) { if(p->starteltfunc) p->starteltfunc(p->data, elementname, i); if(parseatt(p)) return; if(*p->xml!='/') { const char * data; i = 0; data = ++p->xml; if (p->xml >= p->xmlend) return; while( IS_WHITE_SPACE(*p->xml) ) { i++; p->xml++; if (p->xml >= p->xmlend) return; } if(memcmp(p->xml, "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.20140523/minixml.h010064400017500000024000000022331203340734000152700ustar00nanardstaff/* $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.20140523/upnpreplyparse.c010064400017500000024000000101721215431657000167100ustar00nanardstaff/* $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.20140523/upnpreplyparse.h010064400017500000024000000027561215431657000167260ustar00nanardstaff/* $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.20140523/testgetifstats.c010064400017500000024000000020601177217435300166770ustar00nanardstaff/* $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.20140523/upnpdescstrings.h010064400017500000024000000027601225260126000170530ustar00nanardstaff/* $Id: upnpdescstrings.h,v 1.9 2013/12/13 12:50: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 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.20140523/genconfig.sh010075500017500000024000000414231233510361000157420ustar00nanardstaff#! /bin/sh # $Id: genconfig.sh,v 1.76 2014/05/15 09:22:54 nanard Exp $ # miniupnp daemon # http://miniupnp.free.fr or http://miniupnp.tuxfamily.org/ # (c) 2006-2014 Thomas Bernard # This software is subject to the conditions detailed in the # LICENCE file provided within the distribution for argv; do case "$argv" in --ipv6) IPV6=1 ;; --igd2) IGD2=1 ;; --strict) STRICT=1 ;; --leasefile) LEASEFILE=1 ;; --vendorcfg) VENDORCFG=1 ;; --pcp-peer) PCP_PEER=1 ;; --portinuse) PORTINUSE=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" echo " --vendorcfg enable configuration of manufacturer info" echo " --pcp-peer enable PCP PEER operation" echo " --portinuse enable port in use check" 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-2014 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/ V6SOCKETS_ARE_V6ONLY=`sysctl -n net.inet6.ip6.v6only` ;; 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/ V6SOCKETS_ARE_V6ONLY=`sysctl -n net.inet6.ip6.v6only` ;; 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/ V6SOCKETS_ARE_V6ONLY=`sysctl -n net.inet6.ip6.v6only` ;; 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) MAJORVER=`echo $OS_VERSION | cut -d. -f1` echo "#define USE_IFACEWATCHER 1" >> ${CONFIGFILE} # OS X switched to pf since 10.7 Lion (Darwin 11.0) if [ $MAJORVER -ge 11 ] ; then FW=pf echo "#define PFRULE_INOUT_COUNTS" >> ${CONFIGFILE} else FW=ipfw fi 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 # set V6SOCKETS_ARE_V6ONLY to 0 if it was not set above if [ -z "$V6SOCKETS_ARE_V6ONLY" ] ; then V6SOCKETS_ARE_V6ONLY=0 fi 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 "/* Comment the following line to disable PCP operations */" >> ${CONFIGFILE} echo "#define ENABLE_PCP" >> ${CONFIGFILE} echo "" >> ${CONFIGFILE} echo "#ifdef ENABLE_PCP" >> ${CONFIGFILE} if [ -n "$PCP_PEER" ]; then echo "/* Comment the following line to disable PCP PEER operation */" >> ${CONFIGFILE} echo "#define PCP_PEER" >> ${CONFIGFILE} else echo "/* Uncomment the following line to enable PCP PEER operation */" >> ${CONFIGFILE} echo "/*#define PCP_PEER*/" >> ${CONFIGFILE} fi echo "#ifdef PCP_PEER" >> ${CONFIGFILE} echo "/*#define PCP_FLOWP*/" >> ${CONFIGFILE} echo "#endif /*PCP_PEER*/" >> ${CONFIGFILE} echo "/*#define PCP_SADSCP*/" >> ${CONFIGFILE} echo "#endif /*ENABLE_PCP*/" >> ${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 "/* Uncomment the following line to enable port in use check */" >> ${CONFIGFILE} if [ -n "$PORTINUSE" ]; then echo "#define CHECK_PORTINUSE" >> ${CONFIGFILE} else echo "/*#define CHECK_PORTINUSE*/" >> ${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 "/* Define V6SOCKETS_ARE_V6ONLY if AF_INET6 sockets are restricted" >> ${CONFIGFILE} echo " * to IPv6 communications only. */" >> ${CONFIGFILE} if [ $V6SOCKETS_ARE_V6ONLY -eq 1 ] ; then echo "#define V6SOCKETS_ARE_V6ONLY" >> ${CONFIGFILE} else echo "/*#define V6SOCKETS_ARE_V6ONLY*/" >> ${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 "/*#define ENABLE_HTTPS*/" >> ${CONFIGFILE} echo "/*#define HTTPS_CERTFILE \"/path/to/certificate.pem\"*/" >> ${CONFIGFILE} echo "/*#define HTTPS_KEYFILE \"/path/to/private.key\"*/" >> ${CONFIGFILE} echo "" >> ${CONFIGFILE} echo "/* Enable WANIPv6FirewallControl service (IGDv2). needs IPv6 */" >> ${CONFIGFILE} echo "#ifdef ENABLE_IPV6" >> ${CONFIGFILE} 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 "/* If SSDP_RESPOND_SAME_VERSION is defined, the M-SEARCH response" >> ${CONFIGFILE} echo " * include the same device version as was contained in the search" >> ${CONFIGFILE} echo " * request. It conforms to UPnP DA v1.1 */" >> ${CONFIGFILE} echo "#define SSDP_RESPOND_SAME_VERSION" >> ${CONFIGFILE} 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 "/* Wait a little before answering M-SEARCH request */" >> ${CONFIGFILE} if [ -n "$STRICT" ] ; then echo "#define DELAY_MSEARCH_RESPONSE" >> ${CONFIGFILE} else echo "/*#define DELAY_MSEARCH_RESPONSE*/" >> ${CONFIGFILE} fi echo "" >> ${CONFIGFILE} echo "/* disable reading and parsing of config file (miniupnpd.conf) */" >> ${CONFIGFILE} echo "/*#define DISABLE_CONFIG_FILE*/" >> ${CONFIGFILE} echo "" >> ${CONFIGFILE} echo "/* Uncomment the following line to configure all manufacturer infos through miniupnpd.conf */" >> ${CONFIGFILE} if [ -n "$VENDORCFG" ] ; then echo "#define ENABLE_MANUFACTURER_INFO_CONFIGURATION" >> ${CONFIGFILE} else echo "/*#define ENABLE_MANUFACTURER_INFO_CONFIGURATION*/" >> ${CONFIGFILE} fi echo "" >> ${CONFIGFILE} cat >> ${CONFIGFILE} <> ${CONFIGFILE} ${MV} ${CONFIGFILE} ${CONFIGFILE_FINAL} exit 0 miniupnpd-1.8.20140523/options.h010064400017500000024000000051471233732635300153300ustar00nanardstaff/* $Id: options.h,v 1.26 2014/05/22 07:52:45 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * author: Ryan Wagoner * (c) 2006-2014 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 */ #ifdef ENABLE_IPV6 UPNPIPV6_LISTENING_IP, /* listening address for IPv6 */ #endif /* ENABLE_IPV6 */ UPNPPORT, /* "port" / "http_port" */ #ifdef ENABLE_HTTPS UPNPHTTPSPORT, /* "https_port" */ #endif UPNPBITRATE_UP, /* "bitrate_up" */ UPNPBITRATE_DOWN, /* "bitrate_down" */ UPNPPRESENTATIONURL, /* presentation_url */ #ifdef ENABLE_MANUFACTURER_INFO_CONFIGURATION UPNPFRIENDLY_NAME, /* "friendly_name" */ UPNPMANUFACTURER_NAME, /* "manufacturer_name" */ UPNPMANUFACTURER_URL, /* "manufacturer_url" */ UPNPMODEL_NAME, /* "model_name" */ UPNPMODEL_DESCRIPTION, /* "model_description" */ UPNPMODEL_URL, /* "model_url" */ #endif 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 */ UPNPPCPMINLIFETIME, /* minimum lifetime for PCP mapping */ UPNPPCPMAXLIFETIME, /* maximum lifetime for PCP mapping */ UPNPPCPALLOWTHIRDPARTY, /* allow third-party requests */ #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.20140523/options.c010064400017500000024000000165621233732635300153260ustar00nanardstaff/* $Id: options.c,v 1.32 2014/05/22 07:52:45 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * author: Ryan Wagoner * (c) 2006-2014 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" #ifdef PCP_SADSCP #include "pcplearndscp.h" #endif /* PCP_SADSPC */ #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" }, #ifdef ENABLE_IPV6 { UPNPIPV6_LISTENING_IP, "ipv6_listening_ip" }, #endif /* ENABLE_IPV6 */ { UPNPPORT, "port" }, { UPNPPORT, "http_port" }, /* "port" and "http_port" are synonims */ #ifdef ENABLE_HTTPS { UPNPHTTPSPORT, "https_port" }, #endif /* ENABLE_HTTPS */ { UPNPBITRATE_UP, "bitrate_up" }, { UPNPBITRATE_DOWN, "bitrate_down" }, { UPNPPRESENTATIONURL, "presentation_url" }, #ifdef ENABLE_MANUFACTURER_INFO_CONFIGURATION { UPNPFRIENDLY_NAME, "friendly_name" }, { UPNPMANUFACTURER_NAME, "manufacturer_name" }, { UPNPMANUFACTURER_URL, "manufacturer_url" }, { UPNPMODEL_NAME, "model_name" }, { UPNPMODEL_DESCRIPTION, "model_description" }, { UPNPMODEL_URL, "model_url" }, #endif { 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 #ifdef ENABLE_PCP { UPNPPCPMINLIFETIME, "min_lifetime"}, { UPNPPCPMAXLIFETIME, "max_lifetime"}, { UPNPPCPALLOWTHIRDPARTY, "pcp_allow_thirdparty"}, #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; len = strlen(name); /* length of the whole line excluding leading * and ending white spaces */ /* check for UPnP permissions rule */ if((len > 6) && (0 == memcmp(name, "allow", 5) || 0 == memcmp(name, "deny", 4))) { tmp = realloc(upnppermlist, sizeof(struct upnpperm) * (num_upnpperm+1)); if(tmp == NULL) { 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; } #ifdef PCP_SADSCP /* check for DSCP values configuration */ if((len > 15) && 0 == memcmp(name, "set_learn_dscp", sizeof("set_learn_dscp")-1) ) { tmp = realloc(dscp_values_list, sizeof(struct dscp_values) * (num_dscp_values+1)); if(tmp == NULL) { fprintf(stderr, "memory allocation error. DSCP line in file %s line %d\n", fname, linenum); } else { dscp_values_list = tmp; /* parse the rule */ if(read_learn_dscp_line(dscp_values_list + num_dscp_values, name) >= 0) { num_dscp_values++; } else { fprintf(stderr, "parsing error file %s line %d : %s\n", fname, linenum, name); } } continue; } #endif /* PCP_SADSCP */ 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.20140523/upnppermissions.h010064400017500000024000000032251231030526100170700ustar00nanardstaff/* $Id: upnppermissions.h,v 1.11 2014/03/13 10:11:25 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2014 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #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.20140523/upnppermissions.c010064400017500000024000000130131231030526100170570ustar00nanardstaff/* $Id: upnppermissions.c,v 1.19 2014/03/13 10:11:24 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2014 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #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; } #if 0 /* match_permission_internal() * returns: 1 if address, iport matches the permission rule * 0 if no match */ static int match_permission_internal(const struct upnpperm * perm, struct in_addr address, u_short iport) { 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; } #endif 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 "asyncsendto.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" #define GL_SSDP_MCAST_ADDR "FF0E::C" /* maximum lenght of SSDP packets we are generating * (reception is done in a 1500byte buffer) */ #ifdef ENABLE_HTTPS #define SSDP_PACKET_MAX_LEN 768 #else #define SSDP_PACKET_MAX_LEN 512 #endif /* AddMulticastMembership() * param s socket * 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) * param ifindex : interface index (0 : All interfaces) */ #ifdef ENABLE_IPV6 static int AddMulticastMembershipIPv6(int s, unsigned int ifindex) { struct ipv6_mreq mr; memset(&mr, 0, sizeof(mr)); mr.ipv6mr_interface = ifindex; /* 0 : all interfaces */ #ifndef IPV6_ADD_MEMBERSHIP #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP #endif inet_pton(AF_INET6, LL_SSDP_MCAST_ADDR, &mr.ipv6mr_multiaddr); if(setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mr, sizeof(struct ipv6_mreq)) < 0) { syslog(LOG_ERR, "setsockopt(udp, IPV6_ADD_MEMBERSHIP): %m"); 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; } inet_pton(AF_INET6, GL_SSDP_MCAST_ADDR, &mr.ipv6mr_multiaddr); if(setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mr, sizeof(struct ipv6_mreq)) < 0) { syslog(LOG_ERR, "setsockopt(udp, IPV6_ADD_MEMBERSHIP): %m"); return -1; } return 0; } #endif /* 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)); #ifdef ENABLE_IPV6 if(ipv6) { struct sockaddr_in6 * saddr = (struct sockaddr_in6 *)&sockname; saddr->sin6_family = AF_INET6; saddr->sin6_port = htons(SSDP_PORT); saddr->sin6_addr = ipv6_bind_addr; sockname_len = sizeof(struct sockaddr_in6); } else #endif /* ENABLE_IPV6 */ { 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) { for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next) { if(AddMulticastMembershipIPv6(s, lan_addr->index) < 0) { syslog(LOG_WARNING, "Failed to add IPv6 multicast membership for interface %s", lan_addr->str ? lan_addr->str : "NULL"); } } } else #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 if(GETFLAG(IPV6DISABLEDMASK)) { sockets[i] = -1; } else { 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 * delay : in milli-seconds */ static void SendSSDPResponse(int s, const struct sockaddr * addr, const char * st, int st_len, const char * suffix, const char * host, unsigned short http_port, #ifdef ENABLE_HTTPS unsigned short https_port, #endif const char * uuidvalue, unsigned int delay) { int l, n; char buf[SSDP_PACKET_MAX_LEN]; 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" #ifdef ENABLE_HTTPS "SECURELOCATION.UPNP.ORG: https://%s:%u" ROOTDESC_PATH "\r\n" #endif "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */ "01-NLS: %u\r\n" /* same as BOOTID. UDA v1.1 */ "BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */ "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)http_port, #ifdef ENABLE_HTTPS host, (unsigned int)https_port, #endif 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 (%u>=%u)", "SendSSDPResponse()", (unsigned)l, (unsigned)sizeof(buf)); l = sizeof(buf) - 1; } addrlen = (addr->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); n = sendto_schedule(s, buf, l, 0, addr, addrlen, delay); sockaddr_to_string(addr, addr_str, sizeof(addr_str)); syslog(LOG_DEBUG, "%s: %d bytes to %s ST: %.*s", "SendSSDPResponse()", n, addr_str, l, buf); if(n < 0) { syslog(LOG_ERR, "%s: sendto(udp): %m", "SendSSDPResponse()"); } } #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 /* we might want to support urn:schemas-wifialliance-org:device:WFADevice:1 * urn:schemas-wifialliance-org:device:WFADevice:1 * in the future */ {0, 0, 0} }; static void SendSSDPNotify(int s, const struct sockaddr * dest, socklen_t dest_len, const char * dest_str, const char * host, unsigned short http_port, #ifdef ENABLE_HTTPS unsigned short https_port, #endif const char * nt, const char * suffix, const char * usn1, const char * usn2, const char * usn3, unsigned int lifetime) { char bufr[SSDP_PACKET_MAX_LEN]; int n, l; l = snprintf(bufr, sizeof(bufr), "NOTIFY * HTTP/1.1\r\n" "HOST: %s:%d\r\n" "CACHE-CONTROL: max-age=%u\r\n" "LOCATION: http://%s:%u" ROOTDESC_PATH "\r\n" #ifdef ENABLE_HTTPS "SECURELOCATION.UPNP.ORG: https://%s:%u" ROOTDESC_PATH "\r\n" #endif "SERVER: " MINIUPNPD_SERVER_STRING "\r\n" "NT: %s%s\r\n" "USN: %s%s%s%s\r\n" "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", dest_str, SSDP_PORT, /* HOST: */ lifetime, /* CACHE-CONTROL: */ host, (unsigned int)http_port, /* LOCATION: */ #ifdef ENABLE_HTTPS host, (unsigned int)https_port, /* SECURE-LOCATION: */ #endif nt, suffix, /* NT: */ usn1, usn2, usn3, suffix, /* USN: */ upnp_bootid, /* 01-NLS: */ upnp_bootid, /* BOOTID.UPNP.ORG: */ upnp_configid ); /* CONFIGID.UPNP.ORG: */ if(l<0) { syslog(LOG_ERR, "%s: snprintf error", "SendSSDPNotify()"); return; } else if((unsigned int)l >= sizeof(bufr)) { syslog(LOG_WARNING, "%s: truncated output (%u>=%u)", "SendSSDPNotify()", (unsigned)l, (unsigned)sizeof(bufr)); l = sizeof(bufr) - 1; } n = sendto_or_schedule(s, bufr, l, 0, dest, dest_len); if(n < 0) { syslog(LOG_ERR, "sendto(udp_notify=%d, %s): %m", s, host ? host : "NULL"); } else if(n != l) { syslog(LOG_NOTICE, "sendto() sent %d out of %d bytes", n, l); } /* Due to the unreliable nature of UDP, devices SHOULD send the entire * set of discovery messages more than once with some delay between * sets e.g. a few hundred milliseconds. To avoid network congestion * discovery messages SHOULD NOT be sent more than three times. */ n = sendto_schedule(s, bufr, l, 0, dest, dest_len, 250); if(n < 0) { syslog(LOG_ERR, "sendto(udp_notify=%d, %s): %m", s, host ? host : "NULL"); } } #ifdef ENABLE_HTTPS static void SendSSDPNotifies(int s, const char * host, unsigned short http_port, unsigned short https_port, unsigned int lifetime, int ipv6) #else static void SendSSDPNotifies(int s, const char * host, unsigned short http_port, unsigned int lifetime, int ipv6) #endif { #ifdef ENABLE_IPV6 struct sockaddr_storage sockname; #else struct sockaddr_in sockname; #endif socklen_t sockname_len; const char * dest_str; 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; sockname_len = sizeof(struct sockaddr_in6); p->sin6_family = AF_INET6; p->sin6_port = htons(SSDP_PORT); inet_pton(AF_INET6, LL_SSDP_MCAST_ADDR, &(p->sin6_addr)); dest_str = "[" LL_SSDP_MCAST_ADDR "]"; /* TODO : also send to SL_SSDP_MCAST_ADDR and GL_SSDP_MCAST_ADDR ? */ /* UPnP Device Architecture 1.1 : * Devices MUST multicast SSDP messages for each of the UPnP-enabled * interfaces. The scope of multicast SSDP messages MUST be * link local FF02::C if the message is sent from a link local address. * If the message is sent from a global address it MUST be multicast * using either global scope FF0E::C or site local scope FF05::C. * In networks with complex topologies and overlapping sites, use of * global scope is RECOMMENDED. */ } else #endif { struct sockaddr_in *p = (struct sockaddr_in *)&sockname; sockname_len = sizeof(struct sockaddr_in); p->sin_family = AF_INET; p->sin_port = htons(SSDP_PORT); p->sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR); dest_str = SSDP_MCAST_ADDR; } 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, sockname_len, dest_str, host, http_port, #ifdef ENABLE_HTTPS https_port, #endif known_service_types[i].s, ver_str, /* NT: */ known_service_types[i].uuid, "::", known_service_types[i].s, /* ver_str, USN: */ lifetime); if(0==memcmp(known_service_types[i].s, "urn:schemas-upnp-org:device", sizeof("urn:schemas-upnp-org:device")-1)) { SendSSDPNotify(s, (struct sockaddr *)&sockname, sockname_len, dest_str, host, http_port, #ifdef ENABLE_HTTPS https_port, #endif known_service_types[i].uuid, "", /* NT: */ known_service_types[i].uuid, "", "", /* ver_str, USN: */ lifetime); } i++; } } void SendSSDPNotifies2(int * sockets, unsigned short http_port, #ifdef ENABLE_HTTPS unsigned short https_port, #endif unsigned int lifetime) { int i; struct lan_addr_s * lan_addr; for(i=0, lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next) { SendSSDPNotifies(sockets[i], lan_addr->str, http_port, #ifdef ENABLE_HTTPS https_port, #endif lifetime, 0); i++; #ifdef ENABLE_IPV6 if(sockets[i] >= 0) { SendSSDPNotifies(sockets[i], ipv6_addr_for_http_with_brackets, http_port, #ifdef ENABLE_HTTPS https_port, #endif lifetime, 1); } i++; #endif } } /* ProcessSSDPRequest() * process SSDP M-SEARCH requests and responds to them */ void #ifdef ENABLE_HTTPS ProcessSSDPRequest(int s, unsigned short http_port, unsigned short https_port) #else ProcessSSDPRequest(int s, unsigned short http_port) #endif { int n; char bufr[1500]; 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; } #ifdef ENABLE_HTTPS ProcessSSDPData(s, bufr, n, (struct sockaddr *)&sendername, http_port, https_port); #else ProcessSSDPData(s, bufr, n, (struct sockaddr *)&sendername, http_port); #endif } #ifdef ENABLE_HTTPS void ProcessSSDPData(int s, const char *bufr, int n, const struct sockaddr * sender, unsigned short http_port, unsigned short https_port) #else void ProcessSSDPData(int s, const char *bufr, int n, const struct sockaddr * sender, unsigned short http_port) #endif { int i, l; struct lan_addr_s * lan_addr = NULL; const char * st = NULL; 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 #endif #if defined(UPNP_STRICT) || defined(DELAY_MSEARCH_RESPONSE) int mx_value = -1; #endif unsigned int delay = 0; /* UPnP Device Architecture v1.1. 1.3.3 Search response : * Devices responding to a multicast M-SEARCH SHOULD wait a random period * of time between 0 seconds and the number of seconds specified in the * MX field value of the search request before responding, in order to * avoid flooding the requesting control point with search responses * from multiple devices. If the search request results in the need for * a multiple part response from the device, those multiple part * responses SHOULD be spread at random intervals through the time period * from 0 to the number of seconds specified in the MX header field. */ /* 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);*/ } #if defined(UPNP_STRICT) || defined(DELAY_MSEARCH_RESPONSE) 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 /* For multicast M-SEARCH requests, if the search request does * not contain an MX header field, the device MUST silently * discard and ignore the search request. */ if(mx_value < 0) { syslog(LOG_INFO, "ignoring SSDP packet missing MX: header"); return; } else if(mx_value > 5) { /* If the MX header field specifies a field value greater * than 5, the device SHOULD assume that it contained the * value 5 or less. */ mx_value = 5; } #elif defined(DELAY_MSEARCH_RESPONSE) if(mx_value < 0) { mx_value = 1; } else if(mx_value > 5) { /* If the MX header field specifies a field value greater * than 5, the device SHOULD assume that it contained the * value 5 or less. */ mx_value = 5; } #endif /*syslog(LOG_INFO, "SSDP M-SEARCH packet received from %s", sender_str );*/ if(st && (st_len > 0)) { 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 ) { /* SSDP_RESPOND_SAME_VERSION : * response is urn:schemas-upnp-org:service:WANIPConnection:1 when * M-SEARCH included urn:schemas-upnp-org:service:WANIPConnection:1 * else the implemented versions is included in the response * * From UPnP Device Architecture v1.1 : * 1.3.2 [...] Updated versions of device and service types * are REQUIRED to be fully backward compatible with * previous versions. Devices MUST respond to M-SEARCH * requests for any supported version. For example, if a * device implements “urn:schemas-upnporg:service:xyz:2”, * it MUST respond to search requests for both that type * and “urn:schemas-upnp-org:service:xyz:1”. The response * MUST specify the same version as was contained in the * search request. [...] */ #ifndef SSDP_RESPOND_SAME_VERSION if(i==0) ver_str[0] = '\0'; else snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version); #endif syslog(LOG_INFO, "Single search found"); #ifdef DELAY_MSEARCH_RESPONSE delay = random() / (1 + RAND_MAX / (1000 * mx_value)); #ifdef DEBUG syslog(LOG_DEBUG, "mx=%dsec delay=%ums", mx_value, delay); #endif #endif SendSSDPResponse(s, sender, #ifdef SSDP_RESPOND_SAME_VERSION st, st_len, "", #else known_service_types[i].s, l, ver_str, #endif announced_host, http_port, #ifdef ENABLE_HTTPS https_port, #endif known_service_types[i].uuid, delay); break; } } /* Responds to request with ST: ssdp:all */ /* strlen("ssdp:all") == 8 */ if(st_len==8 && (0 == memcmp(st, "ssdp:all", 8))) { #ifdef DELAY_MSEARCH_RESPONSE unsigned int delay_increment = (mx_value * 1000) / 15; #endif syslog(LOG_INFO, "ssdp:all found"); for(i=0; known_service_types[i].s; i++) { #ifdef DELAY_MSEARCH_RESPONSE delay += delay_increment; #endif 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, http_port, #ifdef ENABLE_HTTPS https_port, #endif known_service_types[i].uuid, delay); } /* also answer for uuid */ #ifdef DELAY_MSEARCH_RESPONSE delay += delay_increment; #endif SendSSDPResponse(s, sender, uuidvalue_igd, strlen(uuidvalue_igd), "", announced_host, http_port, #ifdef ENABLE_HTTPS https_port, #endif uuidvalue_igd, delay); #ifdef DELAY_MSEARCH_RESPONSE delay += delay_increment; #endif SendSSDPResponse(s, sender, uuidvalue_wan, strlen(uuidvalue_wan), "", announced_host, http_port, #ifdef ENABLE_HTTPS https_port, #endif uuidvalue_wan, delay); #ifdef DELAY_MSEARCH_RESPONSE delay += delay_increment; #endif SendSSDPResponse(s, sender, uuidvalue_wcd, strlen(uuidvalue_wcd), "", announced_host, http_port, #ifdef ENABLE_HTTPS https_port, #endif uuidvalue_wcd, delay); } /* responds to request by UUID value */ l = (int)strlen(uuidvalue_igd); if(l==st_len) { #ifdef DELAY_MSEARCH_RESPONSE delay = random() / (1 + RAND_MAX / (1000 * mx_value)); #endif if(0 == memcmp(st, uuidvalue_igd, l)) { syslog(LOG_INFO, "ssdp:uuid (IGD) found"); SendSSDPResponse(s, sender, st, st_len, "", announced_host, http_port, #ifdef ENABLE_HTTPS https_port, #endif uuidvalue_igd, delay); } else if(0 == memcmp(st, uuidvalue_wan, l)) { syslog(LOG_INFO, "ssdp:uuid (WAN) found"); SendSSDPResponse(s, sender, st, st_len, "", announced_host, http_port, #ifdef ENABLE_HTTPS https_port, #endif uuidvalue_wan, delay); } else if(0 == memcmp(st, uuidvalue_wcd, l)) { syslog(LOG_INFO, "ssdp:uuid (WCD) found"); SendSSDPResponse(s, sender, st, st_len, "", announced_host, http_port, #ifdef ENABLE_HTTPS https_port, #endif uuidvalue_wcd, delay); } } } 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, socklen_t destlen, const char * dest_str, const char * nt, const char * suffix, const char * usn1, const char * usn2, const char * usn3) { int n, l; char bufr[SSDP_PACKET_MAX_LEN]; 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", dest_str, SSDP_PORT, /* HOST : */ nt, suffix, /* NT: */ usn1, usn2, usn3, suffix, /* USN: */ upnp_bootid, upnp_bootid, upnp_configid); if(l<0) { syslog(LOG_ERR, "%s: snprintf error", "SendSSDPbyebye()"); return -1; } else if((unsigned int)l >= sizeof(bufr)) { syslog(LOG_WARNING, "%s: truncated output (%u>=%u)", "SendSSDPbyebye()", (unsigned)l, (unsigned)sizeof(bufr)); l = sizeof(bufr) - 1; } n = sendto_or_schedule(s, bufr, l, 0, dest, destlen); 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 sockname4; #ifdef ENABLE_IPV6 struct sockaddr_in6 sockname6; struct sockaddr * sockname; socklen_t socknamelen; #endif int i, j; char ver_str[4]; int ret = 0; int ipv6 = 0; const char * dest_str; memset(&sockname4, 0, sizeof(struct sockaddr_in)); sockname4.sin_family = AF_INET; sockname4.sin_port = htons(SSDP_PORT); sockname4.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR); #ifdef ENABLE_IPV6 memset(&sockname6, 0, sizeof(struct sockaddr_in6)); sockname6.sin6_family = AF_INET6; sockname6.sin6_port = htons(SSDP_PORT); inet_pton(AF_INET6, LL_SSDP_MCAST_ADDR, &(sockname6.sin6_addr)); #else dest_str = SSDP_MCAST_ADDR; #endif for(j=0; j 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) { if(errno == EINTR) continue; 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.20140523/miniupnpdctl.c010064400017500000024000000030061174772426300163340ustar00nanardstaff/* $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.20140523/linux/miniupnpd.init.d.script010064400017500000024000000031501173213203200212130ustar00nanardstaff#!/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.20140523/ipf/ipfrdr.c010064400017500000024000000440051214626452300156660ustar00nanardstaff/* $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.20140523/ipf/ipfrdr.h010064400017500000024000000032441203340734100156630ustar00nanardstaff/* $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.20140523/ipf/Makefile010064400017500000024000000006201214663314100156630ustar00nanardstaff# $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.20140523/ipf/testipfrdr.c010064400017500000024000000032421173213203200165510ustar00nanardstaff/* $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.20140523/miniupnpdtypes.h010064400017500000024000000016411203340734000167050ustar00nanardstaff/* $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.20140523/bsdqueue.h010064400017500000024000000432671172522177000154540ustar00nanardstaff/* $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.20140523/miniupnpd.8010064400017500000024000000035371201574217700155600ustar00nanardstaff.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.20140523/natpmp.h010064400017500000024000000024621233640450700151260ustar00nanardstaff/* $Id: natpmp.h,v 1.13 2014/05/19 13:38:03 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); /* receiveraddr is only used with IPV6 sockets */ int ReceiveNATPMPOrPCPPacket(int s, struct sockaddr * senderaddr, socklen_t * senderaddrlen, struct sockaddr_in6 * receiveraddr, unsigned char * msg_buff, size_t msg_buff_size); void ProcessIncomingNATPMPPacket(int s, unsigned char * msg_buff, int len, struct sockaddr_in * senderaddr); void SendNATPMPPublicAddressChangeNotification(int * sockets, int n_sockets); #endif miniupnpd-1.8.20140523/natpmp.c010064400017500000024000000370671233640450700151320ustar00nanardstaff/* $Id: natpmp.c,v 1.48 2014/05/19 13:38:03 nanard Exp $ */ /* MiniUPnP project * (c) 2007-2014 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 #include "macros.h" #include "config.h" #include "natpmp.h" #include "upnpglobalvars.h" #include "getifaddr.h" #include "upnpredirect.h" #include "commonrdr.h" #include "upnputils.h" #include "portinuse.h" #include "asyncsendto.h" #ifdef ENABLE_NATPMP #define INLINE static inline /* theses macros are designed to read/write unsigned short/long int * from an unsigned char array in network order (big endian). * Avoid pointer casting, so avoid accessing unaligned memory, which * can crash with some cpu's */ INLINE uint32_t readnu32(const uint8_t * p) { return (p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]); } #define READNU32(p) readnu32(p) INLINE uint16_t readnu16(const uint8_t * p) { return (p[0] << 8 | p[1]); } #define READNU16(p) readnu16(p) INLINE void writenu32(uint8_t * p, uint32_t n) { p[0] = (n & 0xff000000) >> 24; p[1] = (n & 0xff0000) >> 16; p[2] = (n & 0xff00) >> 8; p[3] = n & 0xff; } #define WRITENU32(p, n) writenu32(p, n) INLINE void writenu16(uint8_t * p, uint16_t n) { p[0] = (n < 0xff00) >> 8; p[1] = n & 0xff; } #define WRITENU16(p, n) writenu16(p, n) int OpenAndConfNATPMPSocket(in_addr_t addr) { int snatpmp; int i = 1; snatpmp = socket(PF_INET, SOCK_DGRAM, 0/*IPPROTO_UDP*/); if(snatpmp<0) { syslog(LOG_ERR, "%s: socket(): %m", "OpenAndConfNATPMPSocket"); return -1; } if(setsockopt(snatpmp, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0) { syslog(LOG_WARNING, "%s: setsockopt(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, "%s: bind(): %m", "OpenAndConfNATPMPSocket"); close(snatpmp); return -1; } } return snatpmp; } int OpenAndConfNATPMPSockets(int * sockets) { 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] = OpenAndConfNATPMPSocket(lan_addr->addr.s_addr); if(sockets[i] < 0) goto error; i++; } return 0; error: while(--i >= 0) { close(sockets[i]); sockets[i] = -1; } return -1; } static void FillPublicAddressResponse(unsigned char * resp, in_addr_t senderaddr) { #ifndef MULTIPLE_EXTERNAL_IP char tmp[16]; UNUSED(senderaddr); if(use_ext_ip_addr) { inet_pton(AF_INET, use_ext_ip_addr, resp+8); } else { if(!ext_if_name || ext_if_name[0]=='\0') { resp[3] = 3; /* Network Failure (e.g. NAT box itself * has not obtained a DHCP lease) */ } else if(getifaddr(ext_if_name, tmp, INET_ADDRSTRLEN, NULL, NULL) < 0) { syslog(LOG_ERR, "Failed to get IP for interface %s", ext_if_name); resp[3] = 3; /* Network Failure (e.g. NAT box itself * has not obtained a DHCP lease) */ } else { inet_pton(AF_INET, tmp, resp+8); /* ok */ } } #else struct lan_addr_s * lan_addr; for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.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 } /* * Receives NATPMP and PCP packets and stores them in msg_buff. * The sender information is stored in senderaddr. * Returns number of bytes recevied, even if number is negative. */ int ReceiveNATPMPOrPCPPacket(int s, struct sockaddr * senderaddr, socklen_t * senderaddrlen, struct sockaddr_in6 * receiveraddr, unsigned char * msg_buff, size_t msg_buff_size) { #if IPV6_PKTINFO struct iovec iov; uint8_t c[1000]; struct msghdr msg; int n; struct cmsghdr *h; iov.iov_base = msg_buff; iov.iov_len = msg_buff_size; memset(&msg, 0, sizeof(msg)); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_name = senderaddr; msg.msg_namelen = *senderaddrlen; msg.msg_control = c; msg.msg_controllen = sizeof(c); n = recvmsg(s, &msg, 0); if(n < 0) { /* EAGAIN, EWOULDBLOCK and EINTR : silently ignore (retry next time) * other errors : log to LOG_ERR */ if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) { syslog(LOG_ERR, "recvmsg(natpmp): %m"); } return n; } if(receiveraddr) { memset(receiveraddr, 0, sizeof(struct sockaddr_in6)); } if ((msg.msg_flags & MSG_TRUNC) || (msg.msg_flags & MSG_CTRUNC)) { syslog(LOG_WARNING, "%s: truncated message", "ReceiveNATPMPOrPCPPacket"); } for(h = CMSG_FIRSTHDR(&msg); h; h = CMSG_NXTHDR(&msg, h)) { if(h->cmsg_level == IPPROTO_IPV6 && h->cmsg_type == IPV6_PKTINFO) { char tmp[INET6_ADDRSTRLEN]; struct in6_pktinfo *ipi6 = (struct in6_pktinfo *)CMSG_DATA(h); syslog(LOG_DEBUG, "%s: packet destination: %s scope_id=%u", "ReceiveNATPMPOrPCPPacket", inet_ntop(AF_INET6, &ipi6->ipi6_addr, tmp, sizeof(tmp)), ipi6->ipi6_ifindex); if(receiveraddr) { receiveraddr->sin6_addr = ipi6->ipi6_addr; receiveraddr->sin6_scope_id = ipi6->ipi6_ifindex; receiveraddr->sin6_family = AF_INET6; receiveraddr->sin6_port = htons(NATPMP_PORT); } } } #else int n; n = recvfrom(s, msg_buff, msg_buff_size, 0, 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 n; } #endif return n; } /** read the request from the socket, process it and then send the * response back. */ void ProcessIncomingNATPMPPacket(int s, unsigned char *msg_buff, int len, struct sockaddr_in *senderaddr) { unsigned char *req=msg_buff; /* request udp packet */ unsigned char resp[32]; /* response udp packet */ int resplen; int n = len; char senderaddrstr[16]; 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 */ WRITENU32(resp+4, 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 = READNU16(req+4); eport = READNU16(req+6); lifetime = READNU32(req+8); proto = (req[1]==1)?IPPROTO_UDP:IPPROTO_TCP; syslog(LOG_INFO, "NAT-PMP port mapping request : " "%hu->%s:%hu %s lifetime=%us", eport, senderaddrstr, iport, (req[1]==1)?"udp":"tcp", lifetime); if(eport==0) eport = iport; /* TODO: accept port mapping if iport ok but eport not ok * (and set eport correctly) */ if(lifetime == 0) { /* remove the mapping */ if(iport == 0) { /* remove all the mappings for this client */ int index = 0; unsigned short eport2, iport2; char iaddr2[16]; int proto2; char desc[64]; while(get_redirect_rule_by_index(index, 0, &eport2, iaddr2, sizeof(iaddr2), &iport2, &proto2, desc, sizeof(desc), 0, 0, ×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) { resp[3] = 2; /* Not Authorized/Refused */ } else { /* iport > 0 && lifetime > 0 */ unsigned short eport_first = 0; int any_eport_allowed = 0; char desc[64]; while(resp[3] == 0) { if(eport_first == 0) { /* first time in loop */ eport_first = eport; } else if(eport == eport_first) { /* no eport available */ if(any_eport_allowed == 0) { /* all eports rejected by permissions */ syslog(LOG_ERR, "No allowed eport for NAT-PMP %hu %s->%s:%hu", eport, (proto==IPPROTO_TCP)?"tcp":"udp", senderaddrstr, iport); resp[3] = 2; /* Not Authorized/Refused */ } else { /* at least one eport allowed (but none available) */ syslog(LOG_ERR, "Failed to find available eport for NAT-PMP %hu %s->%s:%hu", eport, (proto==IPPROTO_TCP)?"tcp":"udp", senderaddrstr, iport); resp[3] = 4; /* Out of resources */ } break; } if(!check_upnp_rule_against_permissions(upnppermlist, num_upnpperm, eport, senderaddr->sin_addr, iport)) { eport++; if(eport == 0) eport++; /* skip port zero */ continue; } any_eport_allowed = 1; /* at lease one eport is allowed */ #ifdef CHECK_PORTINUSE if (port_in_use(ext_if_name, eport, proto, senderaddrstr, iport) > 0) { syslog(LOG_INFO, "port %hu protocol %s already in use", eport, (proto==IPPROTO_TCP)?"tcp":"udp"); eport++; if(eport == 0) eport++; /* skip port zero */ continue; } #endif 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++; if(eport == 0) eport++; /* skip port zero */ continue; } } /* do the redirection */ #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 */ } break; } } WRITENU16(resp+8, iport); /* private port */ WRITENU16(resp+10, eport); /* public port */ WRITENU32(resp+12, lifetime); /* Port Mapping lifetime */ } resplen = 16; break; default: resp[3] = 5; /* Unsupported OPCODE */ } n = sendto_or_schedule(s, resp, resplen, 0, (struct sockaddr *)senderaddr, sizeof(*senderaddr)); if(n<0) { syslog(LOG_ERR, "sendto(natpmp): %m"); } else if(nlist.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_or_schedule(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_or_schedule(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.20140523/commonrdr.h010064400017500000024000000035111230307336000156140ustar00nanardstaff/* $Id: commonrdr.h,v 1.9 2014/02/11 09:36:15 nanard Exp $ */ /* MiniUPnP project * (c) 2006-2014 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 */ /* init_redirect() return values : * 0 : OK * -1 : error */ int init_redirect(void); void shutdown_redirect(void); /* get_redirect_rule() gets internal IP and port from * interface, external port and protocol * return value : * 0 success (rule 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); /* get_redirect_rule_by_index() * return values : * 0 success (rule found) * -1 error or rule 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); /* 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.20140523/upnpevents.h010064400017500000024000000022141203340734000160210ustar00nanardstaff/* $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.20140523/upnpevents.c010064400017500000024000000357641231070225400160340ustar00nanardstaff/* $Id: upnpevents.c,v 1.30 2014/03/14 22:26:07 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2008-2014 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)) { #ifdef UPNP_STRICT /* check if the subscription already timeouted */ if(sub->timeout && time(NULL) > sub->timeout) continue; #endif 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; /*struct timeval sock_timeout;*/ 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 0 /* does not work for non blocking connect() */ /* set timeout to 3 seconds */ sock_timeout.tv_sec = 3; sock_timeout.tv_usec = 0; if(setsockopt(obj->s, SOL_SOCKET, SO_RCVTIMEO, &sock_timeout, sizeof(struct timeval)) < 0) { syslog(LOG_WARNING, "%s: setsockopt(SO_RCVTIMEO): %m", "upnp_event_create_notify"); } sock_timeout.tv_sec = 3; sock_timeout.tv_usec = 0; if(setsockopt(obj->s, SOL_SOCKET, SO_SNDTIMEO, &sock_timeout, sizeof(struct timeval)) < 0) { syslog(LOG_WARNING, "%s: setsockopt(SO_SNDTIMEO): %m", "upnp_event_create_notify"); } #endif /* set socket non blocking */ 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; socklen_t addrlen; #else struct sockaddr_in addr; socklen_t addrlen; #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); addrlen = sizeof(struct sockaddr_in6); } 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); addrlen = sizeof(struct sockaddr_in); } #else addr.sin_family = AF_INET; inet_aton(obj->addrstr, &addr.sin_addr); addr.sin_port = htons(port); addrlen = sizeof(struct sockaddr_in); #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, addrlen) < 0) { if(errno != EINPROGRESS && errno != EWOULDBLOCK) { syslog(LOG_ERR, "%s: connect(%d, %s, %u): %m", "upnp_event_notify_connect", obj->s, obj->addrstr, addrlen); 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(%s%s): %m", "upnp_event_process_notify", obj->addrstr, obj->portstr); 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) { syslog(LOG_INFO, "subscriber timeouted : %u > %u SID=%s", (unsigned)curtime, (unsigned)sub->timeout, sub->uuid); 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.20140523/codelength.h010064400017500000024000000021251203340733500157330ustar00nanardstaff/* $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.20140523/testgetifaddr.c010064400017500000024000000027331230717300600164470ustar00nanardstaff/* $Id: testgetifaddr.c,v 1.8 2014/03/09 23:09:36 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2014 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #include #include #include #include #include #include "config.h" #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; #ifdef ENABLE_IPV6 int r; char str_addr6[64]; #endif 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)); #ifdef ENABLE_IPV6 r = find_ipv6_addr(argv[1], str_addr6, sizeof(str_addr6)); if(r < 0) { fprintf(stderr, "find_ipv6_addr() failed\n"); return 1; } else if(r == 0) { printf("Interface %s has no IPv6 address.\n", argv[1]); } else { printf("Interface %s has IPv6 address %s.\n", argv[1], str_addr6); } #endif return 0; } miniupnpd-1.8.20140523/Makefile.macosx010064400017500000024000000104411233511165600164020ustar00nanardstaff# MiniUPnP project # http://miniupnp.free.fr/ # Author: Thomas Bernard # This Makefile should work for MacOSX # # To compile with pf with OS X 10.7+, you need to specify # path to XNU bsd sources : # INCLUDES="-I.../xnu/bsd -I.../xnu/libkern" make -f Makefile.macosx # # 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 #better use clang ! RM = rm -f MV = mv INSTALL = install STRIP = strip CFLAGS += -DMACOSX # OSNAME and FWNAME are used for building OS or FW dependent code. OSNAME = $(shell uname) ARCH = $(shell uname -p) # Firewall is ipfw up to OS X 10.6 Snow Leopard # and pf since OS X 10.7 Lion (Darwin 11.0) FWNAME = $(shell [ `uname -r | cut -d. -f1` -ge 11 ] && echo "pf" || echo "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 \ upnppinhole.o asyncsendto.o portinuse.o pcpserver.o MAC_OBJS = mac/getifstats.o bsd/ifacewatcher.o bsd/getroute.o IPFW_OBJS = ipfw/ipfwrdr.o ipfw/ipfwaux.o PF_OBJS = pf/obsdrdr.o # pf/pfpinhole.o # SHOULD be used, but doesn't compile on e.g. OS X 10.9. MISC_OBJS = upnpreplyparse.o minixml.o ALL_OBJS = $(STD_OBJS) $(MISC_OBJS) $(MAC_OBJS) ifeq ($(FWNAME), ipfw) ALL_OBJS += $(IPFW_OBJS) else ALL_OBJS += $(PF_OBJS) CFLAGS += -DPF endif 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 TEST_PORTINUSE_OBJS = testportinuse.o portinuse.o getifaddr.o TEST_ASYNCSENDTO_OBJS = testasyncsendto.o asyncsendto.o upnputils.o bsd/getroute.o MINIUPNPDCTL_OBJS = miniupnpdctl.o EXECUTABLES = miniupnpd testupnpdescgen testgetifstats \ testupnppermissions miniupnpdctl \ testgetifaddr testportinuse testasyncsendto 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) testasyncsendto: config.h $(TEST_ASYNCSENDTO_OBJS) $(CC) $(CFLAGS) -o $@ $(TEST_ASYNCSENDTO_OBJS) testportinuse: config.h $(TEST_PORTINUSE_OBJS) $(CC) $(CFLAGS) -o $@ $(TEST_PORTINUSE_OBJS) config.h: genconfig.sh ./genconfig.sh .SUFFIXES: .o .c .c.o: $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $< # $(CC) $(CFLAGS) -c -o $(.TARGET) $(.IMPSRC) miniupnpd-1.8.20140523/mac/Makefile010064400017500000024000000004061153032367000156460ustar00nanardstaff# $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.20140523/mac/getifstats.c010064400017500000024000000047371172522177200165500ustar00nanardstaff/* $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.20140523/mac/testgetifstats.c010064400017500000024000000014221172522177200174340ustar00nanardstaff/* $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.20140523/mac/org.tuxfamily.miniupnpd.plist.before010064400017500000024000000007061170030524600233370ustar00nanardstaff miniupnpd org.tuxfamily.miniupnpd ProgramArguments INSTALLPREFIX/sbin/miniupnpd -f INSTALLPREFIX/etc/miniupnpd/miniupnpd.conf RunAtLoad miniupnpd-1.8.20140523/ipfw/Makefile010064400017500000024000000004131203340734100160460ustar00nanardstaff# $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.20140523/ipfw/ipfwaux.c010064400017500000024000000044051202661001100162320ustar00nanardstaff/* * 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.20140523/ipfw/ipfwaux.h010064400017500000024000000014341203340734100162460ustar00nanardstaff/* $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.20140523/ipfw/ipfwrdr.c010064400017500000024000000262001172522177100162410ustar00nanardstaff/* $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.20140523/ipfw/ipfwrdr.h010064400017500000024000000032121203340734100162340ustar00nanardstaff/* $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.20140523/ipfw/testipfwrdr.c010064400017500000024000000050211160046616100171330ustar00nanardstaff/* $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.20140523/VERSION010064400017500000024000000000041210445350200145040ustar00nanardstaff1.8 miniupnpd-1.8.20140523/netfilter/iptables_init_and_clean.sh010075500017500000024000000031721232333656000226210ustar00nanardstaff#! /bin/sh # $Id: iptables_init_and_clean.sh,v 1.5 2014/04/15 13:45:08 nanard Exp $ # Improved Miniupnpd iptables init script. # Checks for state of filter before doing anything.. IPTABLES="`which iptables`" || exit 1 IP="`which ip`" || exit 1 #EXTIF=eth0 EXTIF="`LC_ALL=C $IP -4 route | grep 'default' | sed -e 's/.*dev[[:space:]]*//' -e 's/[[:space:]].*//'`" || exit 1 EXTIP="`LC_ALL=C $IP -4 addr show $EXTIF | awk '/inet/ { print $2 }' | cut -d "/" -f 1`" NDIRTY="`LC_ALL=C $IPTABLES -t nat -L -n | awk '/MINIUPNPD/ {printf $1}'`" FDIRTY="`LC_ALL=C $IPTABLES -t filter -L -n | awk '/MINIUPNPD/ {printf $1}'`" echo "External IP = $EXTIP" if [[ $NDIRTY = "MINIUPNPDChain" ]]; then 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.20140523/upnputils.c010064400017500000024000000103411227446705200156650ustar00nanardstaff/* $Id: upnputils.c,v 1.8 2014/02/05 17:00:26 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2014 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include "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; char dbg_str[64]; #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 sockaddr_to_string(peer, dbg_str, sizeof(dbg_str)); if(lan_addr) { syslog(LOG_DEBUG, "%s: %s found in LAN %s %s", "get_lan_for_peer()", dbg_str, lan_addr->ifname, lan_addr->str); } else { syslog(LOG_DEBUG, "%s: %s not found !", "get_lan_for_peer()", dbg_str); } return lan_addr; } miniupnpd-1.8.20140523/upnputils.h010064400017500000024000000025401231626017100156620ustar00nanardstaff/* $Id: upnputils.h,v 1.6 2014/03/31 12:32:57 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); /** * define portability macros */ #if defined(__sun) static size_t _sa_len(const struct sockaddr *addr) { if (addr->sa_family == AF_INET) return (sizeof(struct sockaddr_in)); else if (addr->sa_family == AF_INET6) return (sizeof(struct sockaddr_in6)); else return (sizeof(struct sockaddr)); } # define SA_LEN(sa) (_sa_len(sa)) #else #if !defined(SA_LEN) # define SA_LEN(sa) ((sa)->sa_len) #endif #endif #ifndef MAX # define MAX(a,b) (((a)>(b))?(a):(b)) #endif #endif miniupnpd-1.8.20140523/upnpurns.h010064400017500000024000000022301203340734000155020ustar00nanardstaff/* $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.20140523/getconnstatus.c010064400017500000024000000031111212532024700165050ustar00nanardstaff/* $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.20140523/getconnstatus.h010064400017500000024000000013521203340734000165150ustar00nanardstaff/* $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.20140523/ifacewatcher.h010064400017500000024000000041401203340734000162370ustar00nanardstaff/* $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.20140523/bsd/ifacewatcher.c010064400017500000024000000071151232416042700170140ustar00nanardstaff/* $Id: ifacewatcher.c,v 1.8 2014/04/18 08:23:51 nanard Exp $ */ /* Project MiniUPnP * web : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2011 Thomas BERNARD * 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 #define SALIGN (sizeof(long) - 1) #define SA_RLEN(sa) (SA_LEN(sa) ? ((SA_LEN(sa) + 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; case RTM_ADD: /* Add Route */ syslog(LOG_DEBUG, " RTM_ADD"); break; case RTM_DELETE: /* Delete Route */ syslog(LOG_DEBUG, " RTM_DELETE"); break; case RTM_CHANGE: /* Change Metrics or flags */ syslog(LOG_DEBUG, " RTM_CHANGE"); break; case RTM_GET: /* Report Metrics */ syslog(LOG_DEBUG, " RTM_GET"); break; #ifdef RTM_IFANNOUNCE case RTM_IFANNOUNCE: /* iface arrival/departure */ ifanm = (struct if_announcemsghdr *)buf; 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.20140523/bsd/testifacewatcher.c010064400017500000024000000013051175640135600177160ustar00nanardstaff/* $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.20140523/linux/ifacewatcher.c010064400017500000024000000235161176053236400174140ustar00nanardstaff/* $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.20140523/netfilter/tiny_nf_nat.h010064400017500000024000000015361161500167400201310ustar00nanardstaff/* $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.20140523/pf/pfpinhole.c010064400017500000024000000233721233523013700162110ustar00nanardstaff/* $Id: pfpinhole.c,v 1.22 2014/05/15 21:23:43 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 #ifdef MACOSX #define PRIVATE 1 #endif #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_UPNPPINHOLE /* /dev/pf when opened */ extern int dev; static int next_uid = 1; #define PINEHOLE_LABEL_FORMAT "pinhole-%d ts-%u: %s" #define PINEHOLE_LABEL_FORMAT_SKIPDESC "pinhole-%d ts-%u: %*s" int add_pinhole(const char * ifname, const char * rem_host, unsigned short rem_port, const char * int_client, unsigned short int_port, int proto, const char * desc, 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, desc); 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_SKIPDESC, &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_UPNPPINHOLE */ miniupnpd-1.8.20140523/pf/pfpinhole.h010064400017500000024000000022261233510417500162130ustar00nanardstaff/* $Id: pfpinhole.h,v 1.12 2014/05/15 09:27:21 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_UPNPPINHOLE int add_pinhole(const char * ifname, const char * rem_host, unsigned short rem_port, const char * int_client, unsigned short int_port, int proto, const char * desc, 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, char * desc, int desclen, 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 /* ENABLE_UPNPPINHOLE */ #endif miniupnpd-1.8.20140523/pf/testpfpinhole.c010064400017500000024000000046441233523013700171120ustar00nanardstaff/* $Id: testpfpinhole.c,v 1.12 2014/05/15 21:23:43 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2012-2014 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; #ifdef ENABLE_IPV6 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; char desc[64]; r = get_pinhole_info((unsigned short)uid, rem_host, sizeof(rem_host), &rem_port, int_client, sizeof(int_client), &int_port, &proto, desc, sizeof(desc), ×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(" desc='%s'\n", desc); printf(" packets=%llu bytes=%llu\n", packets, bytes); } return r; } #endif 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, "description test 1", 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, "description test 2", 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.20140523/netfilter/ip6tables_display.sh010075500017500000024000000003371174562262500214340ustar00nanardstaff#! /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.20140523/netfilter/ip6tables_flush.sh010075500017500000024000000002541174562262500211060ustar00nanardstaff#! /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.20140523/netfilter/ip6tables_init_and_clean.sh010075500017500000024000000014761174562262500227230ustar00nanardstaff#! /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.20140523/netfilter/ip6tables_init.sh010075500017500000024000000004641174562262500207330ustar00nanardstaff#! /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.20140523/netfilter/ip6tables_removeall.sh010075500017500000024000000005341174562262500217540ustar00nanardstaff#! /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.20140523/netfilter/iptpinhole.c010064400017500000024000000253161233511166000177660ustar00nanardstaff/* $Id: iptpinhole.c,v 1.10 2014/05/15 10:15:05 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_UPNPPINHOLE #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; char desc[]; }; 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, const char *desc, unsigned int timestamp) { struct pinhole_t * p; p = calloc(1, sizeof(struct pinhole_t) + strlen(desc) + 1); if(!p) { syslog(LOG_ERR, "add_to_pinhole_list calloc() error"); return -1; } strcpy(p->desc, desc); memcpy(&p->saddr, saddr, sizeof(struct in6_addr)); p->sport = sport; memcpy(&p->daddr, daddr, sizeof(struct in6_addr)); 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, const char * desc, 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, desc, 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, char * desc, int desclen, 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 (desc) strncpy(desc, p->desc, desclen); 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 get_pinhole_uid_by_index(int index) { struct pinhole_t * p; for(p = pinhole_list.lh_first; p != NULL; p = p->entries.le_next) if (!index--) return p->uid; return -1; } int clean_pinhole_list(unsigned int * next_timestamp) { 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 /* ENABLE_UPNPPINHOLE */ miniupnpd-1.8.20140523/netfilter/iptpinhole.h010064400017500000024000000023201233511166000177610ustar00nanardstaff/* $Id: iptpinhole.h,v 1.8 2014/05/15 10:15:05 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_UPNPPINHOLE int add_pinhole(const char * ifname, const char * rem_host, unsigned short rem_port, const char * int_client, unsigned short int_port, int proto, const char *desc, 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, char * desc, int desclen, unsigned int * timestamp, u_int64_t * packets, u_int64_t * bytes); int get_pinhole_uid_by_index(int index); int clean_pinhole_list(unsigned int * next_timestamp); #endif /* ENABLE_UPNPPINHOLE */ #endif miniupnpd-1.8.20140523/netfilter/testiptpinhole.c010064400017500000024000000012011174625127000206600ustar00nanardstaff/* $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.20140523/macros.h010064400017500000024000000005711203340734000151020ustar00nanardstaff/* $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.20140523/upnppinhole.c010064400017500000024000000347011233511165600161630ustar00nanardstaff/* $Id: upnppinhole.c,v 1.6 2014/05/15 10:15:05 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" #include "upnppinhole.h" #ifdef __APPLE__ /* XXX - Apple version of PF API seems to differ from what * pf/pfpinhole.c expects so don't use that at least.. */ #ifdef USE_PF #undef USE_PF #endif /* USE_PF */ #endif /* __APPLE__ */ #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_UPNPPINHOLE #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= 0 ? 1 : -1; #else return -42; /* not implemented */ #endif } #if 0 int upnp_add_inboundpinhole_internal(const char * raddr, unsigned short rport, const char * iaddr, unsigned short iport, const char * proto, int * uid) { int c = 9999; char cmd[256], cmd_raw[256], cuid[42]; #if 0 static const char cmdval_full_udptcp[] = "ip6tables -I %s %d -p %s -i %s -s %s --sport %hu -d %s --dport %hu -j ACCEPT"; static const char cmdval_udptcp[] = "ip6tables -I %s %d -p %s -i %s --sport %hu -d %s --dport %hu -j ACCEPT"; static const char cmdval_full_udplite[] = "ip6tables -I %s %d -p %s -i %s -s %s -d %s -j ACCEPT"; static const char cmdval_udplite[] = "ip6tables -I %s %d -p %s -i %s -d %s -j ACCEPT"; // raw table command static const char cmdval_full_udptcp_raw[] = "ip6tables -t raw -I PREROUTING %d -p %s -i %s -s %s --sport %hu -d %s --dport %hu -j TRACE"; static const char cmdval_udptcp_raw[] = "ip6tables -t raw -I PREROUTING %d -p %s -i %s --sport %hu -d %s --dport %hu -j TRACE"; static const char cmdval_full_udplite_raw[] = "ip6tables -t raw -I PREROUTING %d -p %s -i %s -s %s -d %s -j TRACE"; static const char cmdval_udplite_raw[] = "ip6tables -t raw -I PREROUTING %d -p %s -i %s -d %s -j TRACE"; #endif /*printf("%s\n", raddr);*/ if(raddr!=NULL) { #ifdef IPPROTO_UDPLITE if(atoi(proto) == IPPROTO_UDPLITE) { /* snprintf(cmd, sizeof(cmd), cmdval_full_udplite, miniupnpd_forward_chain, line_number, proto, ext_if_name, raddr, iaddr); snprintf(cmd_raw, sizeof(cmd_raw), cmdval_full_udplite_raw, line_number, proto, ext_if_name, raddr, iaddr);*/ } else #endif { /* snprintf(cmd, sizeof(cmd), cmdval_full_udptcp, miniupnpd_forward_chain, line_number, proto, ext_if_name, raddr, rport, iaddr, iport); snprintf(cmd_raw, sizeof(cmd_raw), cmdval_full_udptcp_raw, line_number, proto, ext_if_name, raddr, rport, iaddr, iport);*/ } } else { #ifdef IPPROTO_UDPLITE if(atoi(proto) == IPPROTO_UDPLITE) { /*snprintf(cmd, sizeof(cmd), cmdval_udplite, miniupnpd_forward_chain, line_number, proto, ext_if_name, iaddr); snprintf(cmd_raw, sizeof(cmd_raw), cmdval_udplite_raw, line_number, proto, ext_if_name, iaddr);*/ } else #endif { /*snprintf(cmd, sizeof(cmd), cmdval_udptcp, miniupnpd_forward_chain, line_number, proto, ext_if_name, rport, iaddr, iport); snprintf(cmd_raw, sizeof(cmd_raw), cmdval_udptcp_raw, line_number, proto, ext_if_name, rport, iaddr, iport); */ } } #ifdef DEBUG syslog(LOG_INFO, "Adding following ip6tables rule:"); syslog(LOG_INFO, " -> %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, char * desc, int desclen, 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, desc, desclen, 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(desc); UNUSED(desclen); UNUSED(leasetime); UNUSED(packets); return -42; /* not implemented */ #endif } int upnp_get_pinhole_uid_by_index(int index) { #if defined (USE_NETFILTER) return get_pinhole_uid_by_index(index); #else UNUSED(index); return -42; #endif /* defined (USE_NETFILTER) */ } 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 /* ENABLE_UPNPPINHOLE */ miniupnpd-1.8.20140523/upnppinhole.h010064400017500000024000000042421233511165600161650ustar00nanardstaff/* $Id: upnppinhole.h,v 1.5 2014/05/15 10:15:05 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_UPNPPINHOLE /* 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, char * desc, 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, char * desc, int desclen, unsigned int * leasetime, unsigned int * packets); /* * return values: * -1 = not found * 0 .. 65535 = uid of the rule for the index */ int upnp_get_pinhole_uid_by_index(int index); /* 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_UPNPPINHOLE */ #endif /* !UPNPPINHOLE_H_INCLUDED */ miniupnpd-1.8.20140523/linux/getroute.c010064400017500000024000000105631210443263200166110ustar00nanardstaff/* $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.20140523/bsd/getroute.c010064400017500000024000000060061231625750300162260ustar00nanardstaff/* $Id: getroute.c,v 1.4 2014/03/31 12:27:14 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_LEN(sa), 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_LEN(sa); } } } return found ? 0 : -1; } miniupnpd-1.8.20140523/getroute.h010064400017500000024000000007621210443263100154560ustar00nanardstaff/* $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.20140523/testgetroute.c010064400017500000024000000044101210444361000163420ustar00nanardstaff/* $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; } miniupnpd-1.8.20140523/netfilter/nfct_get.c010064400017500000024000000144121225260353000173770ustar00nanardstaff#include #include #include #include #include #include #ifdef USE_NFCT #include #include #include struct data_cb_s { struct sockaddr_storage * ext; uint8_t found; }; static int data_cb(const struct nlmsghdr *nlh, void *data) { struct nf_conntrack *ct; struct data_cb_s * d = (struct data_cb_s*) data; struct sockaddr_in* ext4 = (struct sockaddr_in*) d->ext; ct = nfct_new(); if (ct == NULL) return MNL_CB_OK; nfct_nlmsg_parse(nlh, ct); if (data) { ext4->sin_addr.s_addr = nfct_get_attr_u32(ct, ATTR_REPL_IPV4_DST); ext4->sin_port = nfct_get_attr_u16(ct, ATTR_REPL_PORT_DST); } d->found = 1; nfct_destroy(ct); return MNL_CB_OK; } int get_nat_ext_addr(struct sockaddr* src, struct sockaddr *dst, uint8_t proto, struct sockaddr_storage* ret_ext) { struct mnl_socket *nl; struct nlmsghdr *nlh; struct nfgenmsg *nfh; char buf[MNL_SOCKET_BUFFER_SIZE]; unsigned int seq, portid; struct nf_conntrack *ct; int ret; struct data_cb_s data; if ((!src)&&(!dst)) { return 0; } if (src->sa_family != dst->sa_family) { return 0; } nl = mnl_socket_open(NETLINK_NETFILTER); if (nl == NULL) { // perror("mnl_socket_open"); goto free_nl; } if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { // perror("mnl_socket_bind"); goto free_nl; } portid = mnl_socket_get_portid(nl); memset(buf, 0, sizeof(buf)); nlh = mnl_nlmsg_put_header(buf); nlh->nlmsg_type = (NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_GET; nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK; nlh->nlmsg_seq = seq = time(NULL); nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg)); nfh->nfgen_family = src->sa_family; nfh->version = NFNETLINK_V0; nfh->res_id = 0; ct = nfct_new(); if (ct == NULL) { goto free_nl; } nfct_set_attr_u8(ct, ATTR_L3PROTO, src->sa_family); if (src->sa_family == AF_INET) { struct sockaddr_in *src4 = (struct sockaddr_in *)src; struct sockaddr_in *dst4 = (struct sockaddr_in *)dst; nfct_set_attr_u32(ct, ATTR_IPV4_SRC, src4->sin_addr.s_addr); nfct_set_attr_u32(ct, ATTR_IPV4_DST, dst4->sin_addr.s_addr); nfct_set_attr_u16(ct, ATTR_PORT_SRC, src4->sin_port); nfct_set_attr_u16(ct, ATTR_PORT_DST, dst4->sin_port); } else if (src->sa_family == AF_INET6) { struct sockaddr_in6 *src6 = (struct sockaddr_in6 *)src; struct sockaddr_in6 *dst6 = (struct sockaddr_in6 *)dst; nfct_set_attr(ct, ATTR_IPV6_SRC, &src6->sin6_addr); nfct_set_attr(ct, ATTR_IPV6_DST, &dst6->sin6_addr); nfct_set_attr_u16(ct, ATTR_PORT_SRC, src6->sin6_port); nfct_set_attr_u16(ct, ATTR_PORT_DST, dst6->sin6_port); } nfct_set_attr_u8(ct, ATTR_L4PROTO, proto); nfct_nlmsg_build(nlh, ct); ret = mnl_socket_sendto(nl, nlh, nlh->nlmsg_len); if (ret == -1) { goto free_ct; } ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); data.ext = ret_ext; data.found = 0; while (ret > 0) { ret = mnl_cb_run(buf, ret, seq, portid, data_cb, &data); if (ret <= MNL_CB_STOP) break; ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); } free_ct: nfct_destroy(ct); free_nl: mnl_socket_close(nl); return data.found; } #else #define DST "dst=" #define DST_PORT "dport=" #define SRC "src=" #define SRC_PORT "sport=" #define IP_CONNTRACK_LOCATION "/proc/net/ip_conntrack" #define NF_CONNTRACK_LOCATION "/proc/net/nf_conntrack" int get_nat_ext_addr(struct sockaddr* src, struct sockaddr *dst, uint8_t proto, struct sockaddr_storage* ret_ext) { FILE *f; int af; if (!src) return -2; af = src->sa_family; if ((f = fopen(NF_CONNTRACK_LOCATION, "r")) == NULL) { if ((f = fopen(IP_CONNTRACK_LOCATION, "r")) == NULL) { printf("could not read info about connections from the kernel, " "make sure netfilter is enabled in kernel or by modules.\n"); return -1; } } while (!feof(f)) { char line[256], *str; memset(line, 0, sizeof(line)); str = fgets(line, sizeof(line), f); if (line[0] != 0) { char *token, *saveptr; int j; uint8_t src_f, src_port_f, dst_f, dst_port_f; src_f=src_port_f=dst_f=dst_port_f=0; for (j = 1; ; j++, str = NULL) { token = strtok_r(str, " ", &saveptr); if (token == NULL) break; if ((j==2)&&(af!=atoi(token))) break; if ((j==4)&&(proto!=atoi(token))) break; if (j<=4) continue; if (strncmp(token, SRC, sizeof(SRC) - 1) == 0) { char *srcip = token + sizeof(SRC) - 1; uint32_t buf[4]; memset(buf,0,sizeof(buf)); if (inet_pton(af, srcip, buf)!=1) break; if (af==AF_INET) { struct sockaddr_in *src4=(struct sockaddr_in*)src; if (!src_f) { if (src4->sin_addr.s_addr != buf[0]) break; src_f = 1; } } } if (strncmp(token, SRC_PORT, sizeof(SRC_PORT) - 1) == 0) { char *src_port = token + sizeof(SRC_PORT) - 1; uint16_t port=atoi(src_port); if (af==AF_INET) { struct sockaddr_in *src4=(struct sockaddr_in*)src; if (!src_port_f) { if (ntohs(src4->sin_port) != port) break; src_port_f = 1; } } } if (strncmp(token, DST, sizeof(DST) - 1) == 0) { char *dstip = token + sizeof(DST) - 1; uint32_t buf[4]; memset(buf,0,sizeof(buf)); if (inet_pton(af, dstip, buf)!=1) break; if (af==AF_INET) { struct sockaddr_in *dst4=(struct sockaddr_in*)dst; if (!dst_f) { if (dst4->sin_addr.s_addr != buf[0]) break; dst_f = 1; } else { struct sockaddr_in*ret4=(struct sockaddr_in*)ret_ext; ret_ext->ss_family = AF_INET; ret4->sin_addr.s_addr = buf[0]; } } } if (strncmp(token, DST_PORT, sizeof(DST_PORT)-1) == 0) { char *dst_port = token + sizeof(DST_PORT) - 1; uint16_t port=atoi(dst_port); if (af==AF_INET) { struct sockaddr_in *dst4=(struct sockaddr_in*)dst; if (!dst_port_f) { if (ntohs(dst4->sin_port) != port) break; dst_port_f = 1; } else { struct sockaddr_in*ret4=(struct sockaddr_in*)ret_ext; ret_ext->ss_family = AF_INET; ret4->sin_port = htons(port); } } } } if (src_f && src_port_f && dst_f && dst_port_f) { fclose(f); return 1; } } } fclose(f); return 0; } #endif miniupnpd-1.8.20140523/netfilter/test_nfct_get.c010064400017500000024000000024661225260353000204440ustar00nanardstaff#include "nfct_get.c" int main(int argc, char *argv[]) { struct sockaddr_storage src, dst, ext; char buff[INET6_ADDRSTRLEN]; if (argc!=5) return 0; if (1 != inet_pton(AF_INET, argv[1], &((struct sockaddr_in*)&src)->sin_addr)) { if (1 != inet_pton(AF_INET6, argv[1], &((struct sockaddr_in6*) &src)->sin6_addr)) { perror("bad input param"); } else { ((struct sockaddr_in6*)(&src))->sin6_port = htons(atoi(argv[2])); src.ss_family = AF_INET6; } } else { ((struct sockaddr_in*)(&src))->sin_port = htons(atoi(argv[2])); src.ss_family = AF_INET; } if (1 != inet_pton(AF_INET, argv[3], &((struct sockaddr_in*)&dst)->sin_addr)) { if (1 != inet_pton(AF_INET6, argv[3], &((struct sockaddr_in6*) &dst)->sin6_addr)) { perror("bad input param"); } else { ((struct sockaddr_in6*)(&dst))->sin6_port = htons(atoi(argv[4])); dst.ss_family = AF_INET6; } } else { ((struct sockaddr_in*)(&dst))->sin_port = htons(atoi(argv[4])); dst.ss_family = AF_INET; } if (get_nat_ext_addr((struct sockaddr*)&src, (struct sockaddr*)&dst, IPPROTO_TCP, &ext)) { printf("Ext address %s:%d\n", inet_ntop(src.ss_family, &((struct sockaddr_in*)&ext)->sin_addr, buff, sizeof(buff)), ntohs(((struct sockaddr_in*)(&ext))->sin_port)); } else { printf("no entry\n"); } return 0; } miniupnpd-1.8.20140523/netfilter/testiptcrdr_dscp.c010064400017500000024000000034071225260353000211700ustar00nanardstaff/* $Id: testiptcrdr_dscp.c,v 1.1 2013/12/13 13:10:48 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" #include "iptcrdr.c" #ifndef PRIu64 #define PRIu64 "llu" #endif int main(int argc, char ** argv) { unsigned char dscp; unsigned short iport, rport; const char * iaddr, *rhost; printf("Usage %s \n", argv[0]); if(argc<6) return -1; openlog("testiptcrdr_peer", LOG_PERROR|LOG_CONS, LOG_LOCAL0); dscp = (unsigned short)atoi(argv[1]); iaddr = argv[2]; iport = (unsigned short)atoi(argv[3]); rhost = argv[4]; rport = (unsigned short)atoi(argv[5]); #if 1 if(addpeerdscprule(IPPROTO_TCP, dscp, iaddr, iport, rhost, rport) < 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.20140523/netfilter/testiptcrdr_peer.c010064400017500000024000000035571225260353000212000ustar00nanardstaff/* $Id: testiptcrdr_peer.c,v 1.1 2013/12/13 13:10:48 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" #include "iptcrdr.c" #ifndef PRIu64 #define PRIu64 "llu" #endif int main(int argc, char ** argv) { unsigned short eport, iport, rport; const char * eaddr, *iaddr, *rhost; printf("Usage %s \n", argv[0]); if(argc<6) return -1; openlog("testiptcrdr_peer", LOG_PERROR|LOG_CONS, LOG_LOCAL0); eaddr = argv[1]; eport = (unsigned short)atoi(argv[2]); iaddr = argv[3]; iport = (unsigned short)atoi(argv[4]); rhost = argv[5]; rport = (unsigned short)atoi(argv[6]); #if 1 printf("trying to redirect port %hu to %s:%hu\n", eport, iaddr, iport); if(addpeernatrule(IPPROTO_TCP, eaddr, eport, iaddr, iport, rhost, rport) < 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.20140523/pcpserver.h010064400017500000024000000041611233640450700156360ustar00nanardstaff/* $Id: pcpserver.h,v 1.4 2014/05/19 13:38:04 nanard Exp $ */ /* MiniUPnP project * Website : http://miniupnp.free.fr/ * Author : Peter Tatrai Copyright (c) 2013 by Cisco Systems, Inc. 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 PCPSERVER_H_INCLUDED #define PCPSERVER_H_INCLUDED #define PCP_MIN_LEN 24 #define PCP_MAX_LEN 1100 struct sockaddr; /* * receiveraddr is only used for IPV6 * * returns 0 upon success 1 otherwise */ int ProcessIncomingPCPPacket(int s, unsigned char *msg_buff, int len, const struct sockaddr *senderaddr, const struct sockaddr_in6 *receiveraddr); /* * returns the socket */ int OpenAndConfPCPv6Socket(void); #endif /* PCPSERVER_H_INCLUDED */ miniupnpd-1.8.20140523/pcpserver.c010064400017500000024000001413441233732635300156410ustar00nanardstaff/* $Id: pcpserver.c,v 1.36 2014/05/22 07:51:08 nanard Exp $ */ /* MiniUPnP project * Website : http://miniupnp.free.fr/ * Author : Peter Tatrai Copyright (c) 2013 by Cisco Systems, Inc. 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. */ /* Current assumptions: - IPv4 is always NATted (internal -> external) - IPv6 is always firewalled (this may need some work, NAT6* do exist) - we make the judgement based on (in order, picking first one available): - third party adress - internal client address TODO : handle NAT46, NAT64, NPT66. In addition, beyond FW/NAT choice, should also add for proxy (=as much as possible transparent pass-through to one or more servers). TODO: IPv6 permission handling (for the time being, we just assume anyone on IPv6 is a good guy, but fixing that would include upnppermissions rewrite to be AF neutral). */ #include "config.h" #ifdef ENABLE_PCP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pcpserver.h" #include "natpmp.h" #include "macros.h" #include "upnpglobalvars.h" #include "pcplearndscp.h" #include "upnpredirect.h" #include "commonrdr.h" #include "getifaddr.h" #include "asyncsendto.h" #include "upnputils.h" #include "portinuse.h" #include "pcp_msg_struct.h" #ifdef ENABLE_UPNPPINHOLE #include "upnppinhole.h" #endif /* ENABLE_UPNPPINHOLE */ #ifdef PCP_PEER /* TODO make this platform independent */ #ifdef USE_NETFILTER #include "netfilter/iptcrdr.h" #else #error "PCP Peer is only supported with NETFILTER" #endif /* USE_NETFILTER */ #endif /* PCP_PEER */ /* server specific information */ struct pcp_server_info { uint8_t server_version; }; /* default server settings, highest version supported is the default */ static struct pcp_server_info this_server_info = {2}; /* structure holding information from PCP msg*/ /* all variables are in host byte order except IP addresses */ typedef struct pcp_info { uint8_t version; uint8_t opcode; uint8_t result_code; uint32_t lifetime; /* lifetime of the mapping */ uint32_t epochtime; /* both MAP and PEER opcode specific information */ uint32_t nonce[3]; /* random value generated by client */ uint8_t protocol; uint16_t int_port; const struct in6_addr *int_ip; /* in network order */ uint16_t ext_port; const struct in6_addr *ext_ip; /* Suggested external IP in network order*/ /* PEER specific information */ #ifdef PCP_PEER uint16_t peer_port; const struct in6_addr *peer_ip; /* Destination IP in network order */ #endif /* PCP_PEER */ #ifdef PCP_SADSCP /* SADSCP specific information */ uint8_t delay_tolerance; uint8_t loss_tolerance; uint8_t jitter_tolerance; uint8_t app_name_len; const char* app_name; uint8_t sadscp_dscp; uint8_t matched_name; int8_t is_sadscp_op; #endif #ifdef PCP_FLOWP uint8_t dscp_up; uint8_t dscp_down; int flowp_present; #endif uint8_t is_map_op; uint8_t is_peer_op; const struct in6_addr *thirdp_ip; const struct in6_addr *mapped_ip; char mapped_str[INET6_ADDRSTRLEN]; int pfailure_present; struct in6_addr sender_ip; int is_fw; /* is this firewall operation? if not, nat. */ char desc[64]; } pcp_info_t; #ifdef PCP_SADSCP int get_dscp_value(pcp_info_t *pcp_msg_info) { unsigned int ind; for (ind = 0; ind < num_dscp_values; ind++) { if ((dscp_values_list[ind].app_name) && (!strcmp(dscp_values_list[ind].app_name, pcp_msg_info->app_name)) && (pcp_msg_info->delay_tolerance == dscp_values_list[ind].delay) && (pcp_msg_info->loss_tolerance == dscp_values_list[ind].loss) && (pcp_msg_info->jitter_tolerance == dscp_values_list[ind].jitter) ) { pcp_msg_info->sadscp_dscp = dscp_values_list[ind].dscp_value; pcp_msg_info->matched_name = 1; return 0; } else if ((pcp_msg_info->app_name_len==0) && (dscp_values_list[ind].app_name_len==0) && (pcp_msg_info->delay_tolerance == dscp_values_list[ind].delay) && (pcp_msg_info->loss_tolerance == dscp_values_list[ind].loss) && (pcp_msg_info->jitter_tolerance == dscp_values_list[ind].jitter) ) { pcp_msg_info->sadscp_dscp = dscp_values_list[ind].dscp_value; pcp_msg_info->matched_name = 0; return 0; } else if ((dscp_values_list[ind].app_name_len==0) && (pcp_msg_info->delay_tolerance == dscp_values_list[ind].delay) && (pcp_msg_info->loss_tolerance == dscp_values_list[ind].loss) && (pcp_msg_info->jitter_tolerance == dscp_values_list[ind].jitter) ) { pcp_msg_info->sadscp_dscp = dscp_values_list[ind].dscp_value; pcp_msg_info->matched_name = 0; return 0; } } //if nothing matched return Default value i.e. 0 pcp_msg_info->sadscp_dscp = 0; pcp_msg_info->matched_name = 0; return 0; } #endif /* * Function extracting information from common_req (common request header) * into pcp_msg_info. * @return : when no problem occurred 0 is returned, 1 otherwise and appropriate * result code is assigned to pcp_msg_info->result_code to indicate * what kind of error occurred */ static int parseCommonRequestHeader(const pcp_request_t *common_req, pcp_info_t *pcp_msg_info) { pcp_msg_info->version = common_req->ver ; pcp_msg_info->opcode = common_req->r_opcode & 0x7f; pcp_msg_info->lifetime = ntohl(common_req->req_lifetime); pcp_msg_info->int_ip = &common_req->ip; pcp_msg_info->mapped_ip = &common_req->ip; if ( (common_req->ver > this_server_info.server_version) ) { pcp_msg_info->result_code = PCP_ERR_UNSUPP_VERSION; return 1; } if (pcp_msg_info->lifetime > max_lifetime ) { pcp_msg_info->lifetime = max_lifetime; } if ( (pcp_msg_info->lifetime < min_lifetime) && (pcp_msg_info->lifetime != 0) ) { pcp_msg_info->lifetime = min_lifetime; } return 0; } #ifdef DEBUG static void printMAPOpcodeVersion1(const pcp_map_v1_t *map_buf) { char map_addr[INET6_ADDRSTRLEN]; syslog(LOG_DEBUG, "PCP MAP: v1 Opcode specific information. \n"); syslog(LOG_DEBUG, "MAP protocol: \t\t %d\n",map_buf->protocol ); syslog(LOG_DEBUG, "MAP int port: \t\t %d\n", ntohs(map_buf->int_port) ); syslog(LOG_DEBUG, "MAP ext port: \t\t %d\n", ntohs(map_buf->ext_port) ); syslog(LOG_DEBUG, "MAP Ext IP: \t\t %s\n", inet_ntop(AF_INET6, &map_buf->ext_ip, map_addr, INET6_ADDRSTRLEN)); } static void printMAPOpcodeVersion2(const pcp_map_v2_t *map_buf) { char map_addr[INET6_ADDRSTRLEN]; syslog(LOG_DEBUG, "PCP MAP: v2 Opcode specific information."); syslog(LOG_DEBUG, "MAP nonce: \t%08x%08x%08x", map_buf->nonce[0], map_buf->nonce[1], map_buf->nonce[2]); syslog(LOG_DEBUG, "MAP protocol:\t%d", map_buf->protocol); syslog(LOG_DEBUG, "MAP int port:\t%d", ntohs(map_buf->int_port)); syslog(LOG_DEBUG, "MAP ext port:\t%d", ntohs(map_buf->ext_port)); syslog(LOG_DEBUG, "MAP Ext IP: \t%s", inet_ntop(AF_INET6, &map_buf->ext_ip, map_addr, INET6_ADDRSTRLEN)); } #endif /* DEBUG */ static int parsePCPMAP_version1(const pcp_map_v1_t *map_v1, pcp_info_t *pcp_msg_info) { pcp_msg_info->is_map_op = 1; pcp_msg_info->protocol = map_v1->protocol; pcp_msg_info->int_port = ntohs(map_v1->int_port); pcp_msg_info->ext_port = ntohs(map_v1->ext_port); pcp_msg_info->ext_ip = &(map_v1->ext_ip); if (pcp_msg_info->protocol == 0 && pcp_msg_info->int_port !=0 ){ syslog(LOG_ERR, "PCP MAP: Protocol was ZERO, but internal port has non-ZERO value."); pcp_msg_info->result_code = PCP_ERR_MALFORMED_REQUEST; return 1; } return 0; } static int parsePCPMAP_version2(const pcp_map_v2_t *map_v2, pcp_info_t *pcp_msg_info) { pcp_msg_info->is_map_op = 1; memcpy(pcp_msg_info->nonce, map_v2->nonce, 12); pcp_msg_info->protocol = map_v2->protocol; pcp_msg_info->int_port = ntohs(map_v2->int_port); pcp_msg_info->ext_port = ntohs(map_v2->ext_port); pcp_msg_info->ext_ip = &(map_v2->ext_ip); if (pcp_msg_info->protocol == 0 && pcp_msg_info->int_port !=0 ) { syslog(LOG_ERR, "PCP MAP: Protocol was ZERO, but internal port has non-ZERO value."); pcp_msg_info->result_code = PCP_ERR_MALFORMED_REQUEST; return PCP_ERR_MALFORMED_REQUEST; } return 0; } #ifdef PCP_PEER #ifdef DEBUG static void printPEEROpcodeVersion1(pcp_peer_v1_t *peer_buf) { char ext_addr[INET6_ADDRSTRLEN]; char peer_addr[INET6_ADDRSTRLEN]; syslog(LOG_DEBUG, "PCP PEER: v1 Opcode specific information. \n"); syslog(LOG_DEBUG, "Protocol: \t\t %d\n",peer_buf->protocol ); syslog(LOG_DEBUG, "Internal port: \t\t %d\n", ntohs(peer_buf->int_port) ); syslog(LOG_DEBUG, "External IP: \t\t %s\n", inet_ntop(AF_INET6, &peer_buf->ext_ip, ext_addr,INET6_ADDRSTRLEN)); syslog(LOG_DEBUG, "External port port: \t\t %d\n", ntohs(peer_buf->ext_port) ); syslog(LOG_DEBUG, "PEER IP: \t\t %s\n", inet_ntop(AF_INET6, &peer_buf->peer_ip, peer_addr,INET6_ADDRSTRLEN)); syslog(LOG_DEBUG, "PEER port port: \t\t %d\n", ntohs(peer_buf->peer_port) ); } static void printPEEROpcodeVersion2(pcp_peer_v2_t *peer_buf) { char ext_addr[INET6_ADDRSTRLEN]; char peer_addr[INET6_ADDRSTRLEN]; syslog(LOG_DEBUG, "PCP PEER: v2 Opcode specific information."); syslog(LOG_DEBUG, "nonce: \t%08x%08x%08x", peer_buf->nonce[0], peer_buf->nonce[1], peer_buf->nonce[2]); syslog(LOG_DEBUG, "Protocol: \t%d",peer_buf->protocol ); syslog(LOG_DEBUG, "Internal port:\t%d", ntohs(peer_buf->int_port) ); syslog(LOG_DEBUG, "External IP: \t%s", inet_ntop(AF_INET6, &peer_buf->ext_ip, ext_addr,INET6_ADDRSTRLEN)); syslog(LOG_DEBUG, "External port:\t%d", ntohs(peer_buf->ext_port) ); syslog(LOG_DEBUG, "PEER IP: \t%s", inet_ntop(AF_INET6, &peer_buf->peer_ip, peer_addr,INET6_ADDRSTRLEN)); syslog(LOG_DEBUG, "PEER port: \t%d", ntohs(peer_buf->peer_port) ); } #endif /* DEBUG */ /* * Function extracting information from peer_buf to pcp_msg_info * @return : when no problem occurred 0 is returned, 1 otherwise */ static int parsePCPPEER_version1(pcp_peer_v1_t *peer_buf, \ pcp_info_t *pcp_msg_info) { pcp_msg_info->is_peer_op = 1; pcp_msg_info->protocol = peer_buf->protocol; pcp_msg_info->int_port = ntohs(peer_buf->int_port); pcp_msg_info->ext_port = ntohs(peer_buf->ext_port); pcp_msg_info->peer_port = ntohs(peer_buf->peer_port); pcp_msg_info->ext_ip = &peer_buf->ext_ip; pcp_msg_info->peer_ip = &peer_buf->peer_ip; if (pcp_msg_info->protocol == 0 && pcp_msg_info->int_port !=0 ){ syslog(LOG_ERR, "PCP PEER: protocol was ZERO, but internal port has non-ZERO value."); pcp_msg_info->result_code = PCP_ERR_MALFORMED_REQUEST; return 1; } return 0; } /* * Function extracting information from peer_buf to pcp_msg_info * @return : when no problem occurred 0 is returned, 1 otherwise */ static int parsePCPPEER_version2(pcp_peer_v2_t *peer_buf, \ pcp_info_t *pcp_msg_info) { pcp_msg_info->is_peer_op = 1; memcpy(pcp_msg_info->nonce, peer_buf->nonce, 12); pcp_msg_info->protocol = peer_buf->protocol; pcp_msg_info->int_port = ntohs(peer_buf->int_port); pcp_msg_info->ext_port = ntohs(peer_buf->ext_port); pcp_msg_info->peer_port = ntohs(peer_buf->peer_port); pcp_msg_info->ext_ip = &peer_buf->ext_ip; pcp_msg_info->peer_ip = &peer_buf->peer_ip; if (pcp_msg_info->protocol == 0 && pcp_msg_info->int_port !=0 ){ syslog(LOG_ERR, "PCP PEER: protocol was ZERO, but internal port has non-ZERO value."); pcp_msg_info->result_code = PCP_ERR_MALFORMED_REQUEST; return 1; } return 0; } #endif /* PCP_PEER */ #ifdef PCP_SADSCP #ifdef DEBUG static void printSADSCPOpcode(pcp_sadscp_req_t *sadscp) { unsigned char sadscp_tol; sadscp_tol = sadscp->tolerance_fields; syslog(LOG_DEBUG, "PCP SADSCP: Opcode specific information.\n"); syslog(LOG_DEBUG, "Delay tolerance %d \n", (sadscp_tol>>6)&3 ); syslog(LOG_DEBUG, "Loss tolerance %d \n", (sadscp_tol>>4)&3); syslog(LOG_DEBUG, "Jitter tolerance %d \n", (sadscp_tol>>2)&3); syslog(LOG_DEBUG, "RRR %d \n", sadscp_tol&3); syslog(LOG_DEBUG, "AppName Length %d \n", sadscp->app_name_length); if (sadscp->app_name) { syslog(LOG_DEBUG, "Application name %.*s \n", sadscp->app_name_length, sadscp->app_name); } } #endif //DEBUG static int parseSADSCP(pcp_sadscp_req_t *sadscp, pcp_info_t *pcp_msg_info) { pcp_msg_info->delay_tolerance = (sadscp->tolerance_fields>>6)&3; pcp_msg_info->loss_tolerance = (sadscp->tolerance_fields>>4)&3; pcp_msg_info->jitter_tolerance = (sadscp->tolerance_fields>>2)&3; if (pcp_msg_info->delay_tolerance == 3 ) { pcp_msg_info->result_code = PCP_ERR_MALFORMED_REQUEST; return 1; } if ( pcp_msg_info->loss_tolerance == 3 ) { pcp_msg_info->result_code = PCP_ERR_MALFORMED_REQUEST; return 1; } if ( pcp_msg_info->jitter_tolerance == 3 ) { pcp_msg_info->result_code = PCP_ERR_MALFORMED_REQUEST; return 1; } pcp_msg_info->app_name = sadscp->app_name; pcp_msg_info->app_name_len = sadscp->app_name_length; return 0; } #endif static int parsePCPOption(void* pcp_buf, int remain, pcp_info_t *pcp_msg_info) { #ifdef DEBUG char third_addr[INET6_ADDRSTRLEN]; #endif unsigned short option_length; pcp_3rd_party_option_t* opt_3rd; #ifdef PCP_FLOWP pcp_flow_priority_option_t* opt_flp; #endif pcp_filter_option_t* opt_filter; pcp_prefer_fail_option_t* opt_prefail; pcp_options_hdr_t* opt_hdr; opt_hdr = (pcp_options_hdr_t*)pcp_buf; /* Do centralized option sanity checks here. */ if (remain < (int)sizeof(*opt_hdr)) { pcp_msg_info->result_code = PCP_ERR_MALFORMED_OPTION; return 0; } option_length = ntohs(opt_hdr->len) + 4; if (remain < option_length) { pcp_msg_info->result_code = PCP_ERR_MALFORMED_OPTION; return 0; } switch (opt_hdr->code) { case PCP_OPTION_3RD_PARTY: opt_3rd = (pcp_3rd_party_option_t*)pcp_buf; if ( option_length != sizeof(*opt_3rd) ) { pcp_msg_info->result_code = PCP_ERR_MALFORMED_OPTION; return 0; } #ifdef DEBUG syslog(LOG_DEBUG, "PCP OPTION: \t Third party \n"); syslog(LOG_DEBUG, "Third PARTY IP: \t %s\n", inet_ntop(AF_INET6, &(opt_3rd->ip), third_addr, INET6_ADDRSTRLEN)); #endif if (pcp_msg_info->thirdp_ip ) { syslog(LOG_ERR, "PCP: THIRD PARTY OPTION was already present. \n"); pcp_msg_info->result_code = PCP_ERR_MALFORMED_OPTION; return 0; } else { pcp_msg_info->thirdp_ip = &opt_3rd->ip; pcp_msg_info->mapped_ip = &opt_3rd->ip; } break; case PCP_OPTION_PREF_FAIL: opt_prefail = (pcp_prefer_fail_option_t*)pcp_buf; if ( option_length != sizeof(*opt_prefail) ) { pcp_msg_info->result_code = PCP_ERR_MALFORMED_OPTION; return 0; } #ifdef DEBUG syslog(LOG_DEBUG, "PCP OPTION: \t Prefer failure \n"); #endif if (pcp_msg_info->opcode != PCP_OPCODE_MAP) { syslog(LOG_DEBUG, "PCP: Unsupported OPTION for given OPCODE.\n"); pcp_msg_info->result_code = PCP_ERR_MALFORMED_REQUEST; } if (pcp_msg_info->pfailure_present != 0 ) { syslog(LOG_DEBUG, "PCP: PREFER FAILURE OPTION was already present. \n"); pcp_msg_info->result_code = PCP_ERR_MALFORMED_OPTION; } else { pcp_msg_info->pfailure_present = 1; } break; case PCP_OPTION_FILTER: /* TODO fully implement filter */ opt_filter = (pcp_filter_option_t*)pcp_buf; if ( option_length != sizeof(*opt_filter) ) { pcp_msg_info->result_code = PCP_ERR_MALFORMED_OPTION; return 0; } #ifdef DEBUG syslog(LOG_DEBUG, "PCP OPTION: \t Filter\n"); #endif if (pcp_msg_info->opcode != PCP_OPCODE_MAP) { syslog(LOG_ERR, "PCP: Unsupported OPTION for given OPCODE.\n"); pcp_msg_info->result_code = PCP_ERR_MALFORMED_REQUEST; return 0; } break; #ifdef PCP_FLOWP case PCP_OPTION_FLOW_PRIORITY: #ifdef DEBUG syslog(LOG_DEBUG, "PCP OPTION: \t Flow priority\n"); #endif opt_flp = (pcp_flow_priority_option_t*)pcp_buf; if ( option_length != sizeof (*flp) ) { syslog(LOG_ERR, "PCP: Error processing DSCP. sizeof %d and remaining %d . flow len %d \n", (int)sizeof(pcp_flow_priority_option_t), remain, opt_flp->len); pcp_msg_info->result_code = PCP_ERR_MALFORMED_OPTION; return 0; } #ifdef DEBUG syslog(LOG_DEBUG, "DSCP UP: \t %d \n", opt_flp->dscp_up); syslog(LOG_DEBUG, "DSCP DOWN: \t %d \n", opt_flp->dscp_down); #endif pcp_msg_info->dscp_up = opt_flp->dscp_up; pcp_msg_info->dscp_down = opt_flp->dscp_down; pcp_msg_info->flowp_present = 1; break; #endif default: if (opt_hdr->code < 128) { syslog(LOG_ERR, "PCP: Unrecognized mandatory PCP OPTION: %d \n", opt_hdr->code); /* Mandatory to understand */ pcp_msg_info->result_code = PCP_ERR_UNSUPP_OPTION; remain = 0; break; } /* TODO - log optional not understood options? */ break; } return option_length; } static void parsePCPOptions(void* pcp_buf, int remain, pcp_info_t *pcp_msg_info) { int option_length; while (remain > 0) { option_length = parsePCPOption(pcp_buf, remain, pcp_msg_info); if (!option_length) break; remain -= option_length; pcp_buf += option_length; } } /* CheckExternalAddress() * Check that suggested external address in request match a real external * IP address. * Suggested address can also be 0 IPv4 or IPv6 address. * (see http://tools.ietf.org/html/rfc6887#section-10 ) * return values : * 0 : check is OK * -1 : check failed */ static int CheckExternalAddress(pcp_info_t* pcp_msg_info) { /* can contain a IPv4-mapped IPv6 address */ static struct in6_addr external_addr; int af; af = IN6_IS_ADDR_V4MAPPED(pcp_msg_info->mapped_ip) ? AF_INET : AF_INET6; pcp_msg_info->is_fw = af == AF_INET6; if (pcp_msg_info->is_fw) { external_addr = *pcp_msg_info->mapped_ip; } else { /* TODO : be able to handle case with multiple * external addresses */ if(use_ext_ip_addr) { if (inet_pton(AF_INET, use_ext_ip_addr, ((uint32_t*)external_addr.s6_addr)+3) == 1) { ((uint32_t*)external_addr.s6_addr)[0] = 0; ((uint32_t*)external_addr.s6_addr)[1] = 0; ((uint32_t*)external_addr.s6_addr)[2] = htonl(0xFFFF); } else if (inet_pton(AF_INET6, use_ext_ip_addr, external_addr.s6_addr) != 1) { pcp_msg_info->result_code = PCP_ERR_NETWORK_FAILURE; return -1; } } else { if(!ext_if_name || ext_if_name[0]=='\0') { pcp_msg_info->result_code = PCP_ERR_NETWORK_FAILURE; return -1; } if(getifaddr_in6(ext_if_name, af, &external_addr) < 0) { pcp_msg_info->result_code = PCP_ERR_NETWORK_FAILURE; return -1; } } } if (pcp_msg_info->ext_ip == NULL || IN6_IS_ADDR_UNSPECIFIED(pcp_msg_info->ext_ip) || (IN6_IS_ADDR_V4MAPPED(pcp_msg_info->ext_ip) && ((uint32_t *)pcp_msg_info->ext_ip->s6_addr)[3] == INADDR_ANY)) { /* no suggested external address : use real external address */ pcp_msg_info->ext_ip = &external_addr; return 0; } if (!IN6_ARE_ADDR_EQUAL(pcp_msg_info->ext_ip, &external_addr)) { syslog(LOG_ERR, "PCP: External IP in request didn't match interface IP \n"); #ifdef DEBUG { char s[INET6_ADDRSTRLEN]; syslog(LOG_DEBUG, "Interface IP %s \n", inet_ntop(AF_INET6, &external_addr.s6_addr, s, sizeof(s))); syslog(LOG_DEBUG, "IP in the PCP request %s \n", inet_ntop(AF_INET6, pcp_msg_info->ext_ip, s, sizeof(s))); } #endif if (pcp_msg_info->pfailure_present) { pcp_msg_info->result_code = PCP_ERR_CANNOT_PROVIDE_EXTERNAL; return -1; } else { pcp_msg_info->ext_ip = &external_addr; } } return 0; } static const char* inet_n46top(const struct in6_addr* in, char* buf, size_t buf_len) { if (IN6_IS_ADDR_V4MAPPED(in)) { return inet_ntop(AF_INET, ((uint32_t*)(in->s6_addr))+3, buf, buf_len); } else { return inet_ntop(AF_INET6, in, buf, buf_len); } } #ifdef PCP_PEER static void FillSA(struct sockaddr *sa, const struct in6_addr *in6, uint16_t port) { if (IN6_IS_ADDR_V4MAPPED(in6)) { struct sockaddr_in *sa4 = (struct sockaddr_in *)sa; sa4->sin_family = AF_INET; sa4->sin_addr.s_addr = ((uint32_t*)(in6)->s6_addr)[3]; sa4->sin_port = htons(port); } else { struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa; sa6->sin6_family = AF_INET6; sa6->sin6_addr = *in6; sa6->sin6_port = htons(port); } } static const char* inet_satop(struct sockaddr* sa, char* buf, size_t buf_len) { if (sa->sa_family == AF_INET) { return inet_ntop(AF_INET, &(((struct sockaddr_in*)sa)->sin_addr), buf, buf_len); } else { return inet_ntop(AF_INET6, &(((struct sockaddr_in6*)sa)->sin6_addr), buf, buf_len); } } static int CreatePCPPeer_NAT(pcp_info_t *pcp_msg_info) { struct sockaddr_storage intip; struct sockaddr_storage peerip; struct sockaddr_storage extip; struct sockaddr_storage ret_extip; uint8_t proto = pcp_msg_info->protocol; uint16_t eport = pcp_msg_info->ext_port; /* public port */ char peerip_s[INET6_ADDRSTRLEN], extip_s[INET6_ADDRSTRLEN]; time_t timestamp = time(NULL) + pcp_msg_info->lifetime; int r; FillSA((struct sockaddr*)&intip, pcp_msg_info->mapped_ip, pcp_msg_info->int_port); FillSA((struct sockaddr*)&peerip, pcp_msg_info->peer_ip, pcp_msg_info->peer_port); FillSA((struct sockaddr*)&extip, pcp_msg_info->ext_ip, eport); inet_satop((struct sockaddr*)&peerip, peerip_s, sizeof(peerip_s)); inet_satop((struct sockaddr*)&extip, extip_s, sizeof(extip_s)); /* check if connection with given peer exists, if it was */ /* already established use this external port */ if (get_nat_ext_addr( (struct sockaddr*)&intip, (struct sockaddr*)&peerip, proto, (struct sockaddr*)&ret_extip) == 1) { if (ret_extip.ss_family == AF_INET) { struct sockaddr_in* ret_ext4 = (struct sockaddr_in*)&ret_extip; uint16_t ret_eport = ntohs(ret_ext4->sin_port); eport = ret_eport; } else if (ret_extip.ss_family == AF_INET6) { struct sockaddr_in6* ret_ext6 = (struct sockaddr_in6*)&ret_extip; uint16_t ret_eport = ntohs(ret_ext6->sin6_port); eport = ret_eport; } else { return PCP_ERR_CANNOT_PROVIDE_EXTERNAL; } } /* Create Peer Mapping */ if (eport == 0) { eport = pcp_msg_info->int_port; } #ifdef PCP_FLOWP if (pcp_msg_info->flowp_present && pcp_msg_info->dscp_up) { if (add_peer_dscp_rule2(ext_if_name, peerip_s, pcp_msg_info->peer_port, pcp_msg_info->dscp_up, pcp_msg_info->mapped_str, pcp_msg_info->int_port, proto, pcp_msg_info->desc, timestamp) < 0 ) { syslog(LOG_ERR, "PCP: failed to add flowp upstream mapping %s:%hu->%s:%hu '%s'", pcp_msg_info->mapped_str, pcp_msg_info->int_port, peerip_s, pcp_msg_info->peer_port, pcp_msg_info->desc); return PCP_ERR_NO_RESOURCES; } } if (pcp_msg_info->flowp_present && pcp_msg_info->dscp_down) { if (add_peer_dscp_rule2(ext_if_name, pcp_msg_info->mapped_str, pcp_msg_info->int_port, pcp_msg_info->dscp_down, peerip_s, pcp_msg_info->peer_port, proto, pcp_msg_info->desc, timestamp) < 0 ) { syslog(LOG_ERR, "PCP: failed to add flowp downstream mapping %s:%hu->%s:%hu '%s'", pcp_msg_info->mapped_str, pcp_msg_info->int_port, peerip_s, pcp_msg_info->peer_port, pcp_msg_info->desc); pcp_msg_info->result_code = PCP_ERR_NO_RESOURCES; return; } } #endif r = add_peer_redirect_rule2(ext_if_name, peerip_s, pcp_msg_info->peer_port, extip_s, eport, pcp_msg_info->mapped_str, pcp_msg_info->int_port, pcp_msg_info->protocol, pcp_msg_info->desc, timestamp); if (r < 0) return PCP_ERR_NO_RESOURCES; pcp_msg_info->ext_port = eport; return PCP_SUCCESS; } static void CreatePCPPeer(pcp_info_t *pcp_msg_info) { char peerip_s[INET6_ADDRSTRLEN]; int r = -1; if (!inet_n46top(pcp_msg_info->peer_ip, peerip_s, sizeof(peerip_s))) { syslog(LOG_ERR, "inet_n46top(peer_ip): %m"); return; } if (pcp_msg_info->is_fw) { #if 0 /* Someday, something like this is available.. and we're ready! */ #ifdef ENABLE_UPNPPINHOLE pcp_msg_info->ext_port = pcp_msg_info->int_port; r = upnp_add_outbound_pinhole(peerip_s, pcp_msg_info->peer_port, pcp_msg_info->mapped_str, pcp_msg_info->int_port, pcp_msg_info->protocol, pcp_msg_info->desc, pcp_msg_info->lifetime, NULL); #endif /* ENABLE_UPNPPINHOLE */ #else r = PCP_ERR_UNSUPP_OPCODE; #endif /* 0 */ } else { r = CreatePCPPeer_NAT(pcp_msg_info); } /* TODO: add upnp function for PI */ pcp_msg_info->result_code = r; syslog(LOG_ERR, "PCP PEER: %s peer mapping %s %s:%hu(%hu)->%s:%hu '%s'", r == PCP_SUCCESS ? "added" : "failed to add", (pcp_msg_info->protocol==IPPROTO_TCP)?"TCP":"UDP", pcp_msg_info->mapped_str, pcp_msg_info->int_port, pcp_msg_info->ext_port, peerip_s, pcp_msg_info->peer_port, pcp_msg_info->desc); } static void DeletePCPPeer(pcp_info_t *pcp_msg_info) { uint16_t iport = pcp_msg_info->int_port; /* private port */ uint16_t rport = pcp_msg_info->peer_port; /* private port */ uint8_t proto = pcp_msg_info->protocol; char rhost[INET6_ADDRSTRLEN]; int r = -1; /* remove requested mappings for this client */ int index = 0; unsigned short eport2, iport2, rport2; char iaddr2[INET6_ADDRSTRLEN], rhost2[INET6_ADDRSTRLEN]; int proto2; char desc[64]; unsigned int timestamp; #if 0 int uid; #endif /* 0 */ if (pcp_msg_info->is_fw) { pcp_msg_info->result_code = PCP_ERR_UNSUPP_OPCODE; return; } inet_n46top((struct in6_addr*)pcp_msg_info->peer_ip, rhost, sizeof(rhost)); for (index = 0 ; (!pcp_msg_info->is_fw && get_peer_rule_by_index(index, 0, &eport2, iaddr2, sizeof(iaddr2), &iport2, &proto2, desc, sizeof(desc), rhost2, sizeof(rhost2), &rport2, ×tamp, 0, 0) >= 0) #if 0 /* Some day if outbound pinholes are supported.. */ || (pcp_msg_info->is_fw && (uid=upnp_get_pinhole_uid_by_index(index))>=0 && upnp_get_pinhole_info((unsigned short)uid, rhost2, sizeof(rhost2), &rport2, iaddr2, sizeof(iaddr2), &iport2, &proto2, desc, sizeof(desc), ×tamp, NULL) > 0) #endif /* 0 */ ; index++) if((0 == strcmp(iaddr2, pcp_msg_info->mapped_str)) && (0 == strcmp(rhost2, rhost)) && (proto2==proto) && 0 == strcmp(desc, pcp_msg_info->desc) && (iport2==iport) && (rport2==rport)) { if (!pcp_msg_info->is_fw) r = _upnp_delete_redir(eport2, proto2); #if 0 else r = upnp_delete_outboundpinhole(uid); #endif /* 0 */ if(r<0) { syslog(LOG_ERR, "PCP PEER: failed to remove peer mapping"); } else { syslog(LOG_INFO, "PCP PEER: %s port %hu peer mapping removed", proto2==IPPROTO_TCP?"TCP":"UDP", eport2); } return; } if (r==-1) { syslog(LOG_ERR, "PCP PEER: Failed to find PCP mapping internal port %hu, protocol %s", iport, (pcp_msg_info->protocol == IPPROTO_TCP)?"TCP":"UDP"); pcp_msg_info->result_code = PCP_ERR_NO_RESOURCES; } } #endif /* PCP_PEER */ static int CreatePCPMap_NAT(pcp_info_t *pcp_msg_info) { int r = 0; char iaddr_old[INET6_ADDRSTRLEN]; uint16_t iport_old, eport_first = 0; int any_eport_allowed = 0; unsigned int timestamp = time(NULL) + pcp_msg_info->lifetime; if (pcp_msg_info->ext_port == 0) { pcp_msg_info->ext_port = pcp_msg_info->int_port; } /* TODO: Support non-TCP/UDP */ if (pcp_msg_info->ext_port == 0) { return PCP_ERR_MALFORMED_REQUEST; } do { if (eport_first == 0) { /* first time in loop */ eport_first = pcp_msg_info->ext_port; } else if (pcp_msg_info->ext_port == eport_first) { /* no eport available */ /* all eports rejected by permissions? */ if (any_eport_allowed == 0) return PCP_ERR_NOT_AUTHORIZED; /* at least one eport allowed (but none available) */ return PCP_ERR_NO_RESOURCES; } if ((IN6_IS_ADDR_V4MAPPED(pcp_msg_info->mapped_ip) && (!check_upnp_rule_against_permissions(upnppermlist, num_upnpperm, pcp_msg_info->ext_port, ((struct in_addr*)pcp_msg_info->mapped_ip->s6_addr)[3], pcp_msg_info->int_port)))) { if (pcp_msg_info->pfailure_present) { return PCP_ERR_CANNOT_PROVIDE_EXTERNAL; } pcp_msg_info->ext_port++; if (pcp_msg_info->ext_port == 0) { /* skip port zero */ pcp_msg_info->ext_port++; } continue; } any_eport_allowed = 1; #ifdef CHECK_PORTINUSE if (port_in_use(ext_if_name, pcp_msg_info->ext_port, pcp_msg_info->protocol, pcp_msg_info->mapped_str, pcp_msg_info->int_port) > 0) { syslog(LOG_INFO, "port %hu protocol %s already in use", pcp_msg_info->ext_port, (pcp_msg_info->protocol==IPPROTO_TCP)?"tcp":"udp"); pcp_msg_info->ext_port++; if (pcp_msg_info->ext_port == 0) { /* skip port zero */ pcp_msg_info->ext_port++; } continue; } #endif r = get_redirect_rule(ext_if_name, pcp_msg_info->ext_port, pcp_msg_info->protocol, iaddr_old, sizeof(iaddr_old), &iport_old, 0, 0, 0, 0, ×tamp, 0, 0); if(r==0) { if((strcmp(pcp_msg_info->mapped_str, iaddr_old)!=0) || (pcp_msg_info->int_port != iport_old)) { /* redirection already existing */ if (pcp_msg_info->pfailure_present) { return PCP_ERR_CANNOT_PROVIDE_EXTERNAL; } } else { syslog(LOG_INFO, "port %hu %s already redirected to %s:%hu, replacing", pcp_msg_info->ext_port, (pcp_msg_info->protocol==IPPROTO_TCP)?"tcp":"udp", iaddr_old, iport_old); /* remove and then add again */ if (_upnp_delete_redir(pcp_msg_info->ext_port, pcp_msg_info->protocol)==0) { break; } else if (pcp_msg_info->pfailure_present) { return PCP_ERR_CANNOT_PROVIDE_EXTERNAL; } } pcp_msg_info->ext_port++; if (pcp_msg_info->ext_port == 0) { /* skip port zero */ pcp_msg_info->ext_port++; } } } while (r==0); r = upnp_redirect_internal(NULL, pcp_msg_info->ext_port, pcp_msg_info->mapped_str, pcp_msg_info->int_port, pcp_msg_info->protocol, pcp_msg_info->desc, timestamp); if (r < 0) return PCP_ERR_NO_RESOURCES; return PCP_SUCCESS; } static int CreatePCPMap_FW(pcp_info_t *pcp_msg_info) { #ifdef ENABLE_UPNPPINHOLE int uid; int r = upnp_add_inboundpinhole(NULL, 0, pcp_msg_info->mapped_str, pcp_msg_info->int_port, pcp_msg_info->protocol, pcp_msg_info->desc, pcp_msg_info->lifetime, &uid); if (r < 0) return PCP_ERR_NO_RESOURCES; return PCP_SUCCESS; #else return PCP_ERR_NO_RESOURCES; #endif /* ENABLE_UPNPPINHOLE */ } /* internal external PCP remote peer actual remote peer * -------- ------- --------------- ------------------ * IPv4 firewall IPv4 IPv4 IPv4 IPv4 * IPv6 firewall IPv6 IPv6 IPv6 IPv6 * NAT44 IPv4 IPv4 IPv4 IPv4 * NAT46 IPv4 IPv6 IPv4 IPv6 * NAT64 IPv6 IPv4 IPv6 IPv4 * NPTv6 IPv6 IPv6 IPv6 IPv6 * * Address Families with MAP and PEER * * The 'internal' address is implicitly the same as the source IP * address of the PCP request, except when the THIRD_PARTY option is * used. * * The 'external' address is the Suggested External Address field of the * MAP or PEER request, and its address family is usually the same as * the 'internal' address family, except when technologies like NAT64 * are used. * * The 'remote peer' address is the remote peer IP address of the PEER * request or the FILTER option of the MAP request, and is always the * same address family as the 'internal' address, even when NAT64 is * used. In NAT64, the IPv6 PCP client is not necessarily aware of the * NAT64 or aware of the actual IPv4 address of the remote peer, so it * expresses the IPv6 address from its perspective. */ /* TODO: Support more than basic NAT44 / IPv6 firewall cases. */ static void CreatePCPMap(pcp_info_t *pcp_msg_info) { int r; if (pcp_msg_info->is_fw) r = CreatePCPMap_FW(pcp_msg_info); else r = CreatePCPMap_NAT(pcp_msg_info); pcp_msg_info->result_code = r; syslog(LOG_ERR, "PCP MAP: %s mapping %s %hu->%s:%hu '%s'", r == PCP_SUCCESS ? "added" : "failed to add", (pcp_msg_info->protocol==IPPROTO_TCP)?"TCP":"UDP", pcp_msg_info->ext_port, pcp_msg_info->mapped_str, pcp_msg_info->int_port, pcp_msg_info->desc); } static void DeletePCPMap(pcp_info_t *pcp_msg_info) { uint16_t iport = pcp_msg_info->int_port; /* private port */ uint8_t proto = pcp_msg_info->protocol; int r=-1; /* remove the mapping */ /* remove all the mappings for this client */ int index; unsigned short eport2, iport2; char iaddr2[16]; int proto2; char desc[64]; unsigned int timestamp; #ifdef ENABLE_UPNPPINHOLE int uid = -1; #endif /* ENABLE_UPNPPINHOLE */ /* iterate through all rules and delete the requested ones */ for (index = 0 ; (!pcp_msg_info->is_fw && get_redirect_rule_by_index(index, 0, &eport2, iaddr2, sizeof(iaddr2), &iport2, &proto2, desc, sizeof(desc), 0, 0, ×tamp, 0, 0) >= 0) #ifdef ENABLE_UPNPPINHOLE || (pcp_msg_info->is_fw && (uid=upnp_get_pinhole_uid_by_index(index))>=0 && upnp_get_pinhole_info((unsigned short)uid, NULL, 0, NULL, iaddr2, sizeof(iaddr2), &iport2, &proto2, desc, sizeof(desc), ×tamp, NULL) > 0) #endif /* ENABLE_UPNPPINHOLE */ ; index++) if(0 == strcmp(iaddr2, pcp_msg_info->mapped_str) && (proto2==proto) && 0 == strcmp(desc, pcp_msg_info->desc) && ((iport2==iport) || (iport==0))) { if (!pcp_msg_info->is_fw) { r = _upnp_delete_redir(eport2, proto2); } else { #ifdef ENABLE_UPNPPINHOLE r = upnp_delete_inboundpinhole(uid); #endif /* ENABLE_UPNPPINHOLE */ } break; } if (r >= 0) { syslog(LOG_INFO, "PCP: %s port %hu mapping removed", proto2==IPPROTO_TCP?"TCP":"UDP", eport2); } else { syslog(LOG_ERR, "Failed to remove PCP mapping internal port %hu, protocol %s", iport, (pcp_msg_info->protocol == IPPROTO_TCP)?"TCP":"UDP"); pcp_msg_info->result_code = PCP_ERR_NO_RESOURCES; } } static int ValidatePCPMsg(pcp_info_t *pcp_msg_info) { if (pcp_msg_info->result_code) { return 0; } /* RFC 6887, section 8.2: MUST return address mismatch if NAT * in middle. */ if (memcmp(pcp_msg_info->int_ip, &pcp_msg_info->sender_ip, sizeof(pcp_msg_info->sender_ip)) != 0) { pcp_msg_info->result_code = PCP_ERR_ADDRESS_MISMATCH; return 0; } if (pcp_msg_info->thirdp_ip) { if (!GETFLAG(PCP_ALLOWTHIRDPARTYMASK)) { pcp_msg_info->result_code = PCP_ERR_UNSUPP_OPTION; return 0; } /* RFC687, section 13.1 - if sender ip == THIRD_PARTY, * it's an error. */ if (memcmp(pcp_msg_info->thirdp_ip, &pcp_msg_info->sender_ip, sizeof(pcp_msg_info->sender_ip)) == 0) { pcp_msg_info->result_code = PCP_ERR_MALFORMED_REQUEST; return 0; } } /* Produce mapped_str for future use. */ if (!inet_n46top(pcp_msg_info->mapped_ip, pcp_msg_info->mapped_str, sizeof(pcp_msg_info->mapped_str))) { syslog(LOG_ERR, "inet_ntop(pcpserver): %m"); return 0; } /* protocol zero means 'all protocols' : internal port MUST be zero */ if (pcp_msg_info->protocol == 0 && pcp_msg_info->int_port != 0) { pcp_msg_info->result_code = PCP_ERR_MALFORMED_REQUEST; return 0; } if (pcp_msg_info->pfailure_present) { if ( (IN6_IS_ADDR_UNSPECIFIED(pcp_msg_info->ext_ip) || ((IN6_IS_ADDR_V4MAPPED(pcp_msg_info->ext_ip)) && (((uint32_t*)pcp_msg_info->ext_ip->s6_addr)[3] == 0))) && (pcp_msg_info->ext_port == 0) ) { pcp_msg_info->result_code = PCP_ERR_MALFORMED_OPTION; return 0; } } if (CheckExternalAddress(pcp_msg_info)) { return 0; } /* Fill in the desc that describes uniquely what flow we're * dealing with (same code used in both create + delete of * MAP/PEER) */ switch (pcp_msg_info->opcode) { case PCP_OPCODE_MAP: case PCP_OPCODE_PEER: snprintf(pcp_msg_info->desc, sizeof(pcp_msg_info->desc), "PCP %s %08x%08x%08x", pcp_msg_info->opcode == PCP_OPCODE_MAP ? "MAP":"PEER", pcp_msg_info->nonce[0], pcp_msg_info->nonce[1], pcp_msg_info->nonce[2]); break; } return 1; } /* * return value indicates whether the request is valid or not. * Based on the return value simple response can be formed. */ static int processPCPRequest(void * req, int req_size, pcp_info_t *pcp_msg_info) { int remainingSize; const pcp_map_v1_t* map_v1; const pcp_map_v2_t* map_v2; #ifdef PCP_PEER pcp_peer_v1_t* peer_v1; pcp_peer_v2_t* peer_v2; #endif #ifdef PCP_SADSCP pcp_sadscp_req_t* sadscp; #endif /* start with PCP_SUCCESS as result code, if everything is OK value will be unchanged */ pcp_msg_info->result_code = PCP_SUCCESS; remainingSize = req_size; /* discard request that exceeds maximal length, or that is shorter than PCP_MIN_LEN (=24) or that is not the multiple of 4 */ if (req_size < 3) return 0; /* ignore msg */ if (req_size < PCP_MIN_LEN) { pcp_msg_info->result_code = PCP_ERR_MALFORMED_REQUEST; return 1; /* send response */ } if ( (req_size > PCP_MAX_LEN) || ( (req_size & 3) != 0)) { syslog(LOG_ERR, "PCP: Size of PCP packet(%d) is larger than %d bytes or " "the size is not multiple of 4.\n", req_size, PCP_MAX_LEN); pcp_msg_info->result_code = PCP_ERR_MALFORMED_REQUEST; return 1; /* send response */ } /* first parse request header */ if (parseCommonRequestHeader((pcp_request_t*)req, pcp_msg_info) ) { return 1; } remainingSize -= sizeof(pcp_request_t); req += sizeof(pcp_request_t); if (pcp_msg_info->version == 1) { /* legacy PCP version 1 support */ switch (pcp_msg_info->opcode) { case PCP_OPCODE_MAP: remainingSize -= sizeof(pcp_map_v1_t); if (remainingSize < 0) { pcp_msg_info->result_code = PCP_ERR_MALFORMED_REQUEST; return pcp_msg_info->result_code; } map_v1 = (pcp_map_v1_t*)req; #ifdef DEBUG printMAPOpcodeVersion1(map_v1); #endif /* DEBUG */ if ( parsePCPMAP_version1(map_v1, pcp_msg_info) ) { return pcp_msg_info->result_code; } req += sizeof(pcp_map_v1_t); parsePCPOptions(req, remainingSize, pcp_msg_info); if (ValidatePCPMsg(pcp_msg_info)) { if (pcp_msg_info->lifetime == 0) { DeletePCPMap(pcp_msg_info); } else { CreatePCPMap(pcp_msg_info); } } else { syslog(LOG_ERR, "PCP: Invalid PCP v1 MAP message."); } break; #ifdef PCP_PEER case PCP_OPCODE_PEER: remainingSize -= sizeof(pcp_peer_v1_t); if (remainingSize < 0) { pcp_msg_info->result_code = PCP_ERR_MALFORMED_REQUEST; return pcp_msg_info->result_code; } peer_v1 = (pcp_peer_v1_t*)req; #ifdef DEBUG printPEEROpcodeVersion1(peer_v1); #endif /* DEBUG */ if ( parsePCPPEER_version1(peer_v1, pcp_msg_info) ) { return pcp_msg_info->result_code; } req += sizeof(pcp_peer_v1_t); parsePCPOptions(req, remainingSize, pcp_msg_info); if (ValidatePCPMsg(pcp_msg_info)) { if (pcp_msg_info->lifetime == 0) { DeletePCPPeer(pcp_msg_info); } else { CreatePCPPeer(pcp_msg_info); } } else { syslog(LOG_ERR, "PCP: Invalid PCP v1 PEER message."); } break; #endif /* PCP_PEER */ default: pcp_msg_info->result_code = PCP_ERR_UNSUPP_OPCODE; break; } } else if (pcp_msg_info->version == 2) { /* RFC 6887 PCP support * http://tools.ietf.org/html/rfc6887 */ switch (pcp_msg_info->opcode) { case PCP_OPCODE_ANNOUNCE: /* should check PCP Client's IP Address in request */ /* see http://tools.ietf.org/html/rfc6887#section-14.1 */ break; case PCP_OPCODE_MAP: remainingSize -= sizeof(pcp_map_v2_t); if (remainingSize < 0) { pcp_msg_info->result_code = PCP_ERR_MALFORMED_REQUEST; return pcp_msg_info->result_code; } map_v2 = (pcp_map_v2_t*)req; #ifdef DEBUG printMAPOpcodeVersion2(map_v2); #endif /* DEBUG */ if (parsePCPMAP_version2(map_v2, pcp_msg_info) ) { return pcp_msg_info->result_code; } req += sizeof(pcp_map_v2_t); parsePCPOptions(req, remainingSize, pcp_msg_info); if (ValidatePCPMsg(pcp_msg_info)) { if (pcp_msg_info->lifetime == 0) { DeletePCPMap(pcp_msg_info); } else { CreatePCPMap(pcp_msg_info); } } else { syslog(LOG_ERR, "PCP: Invalid PCP v2 MAP message."); } break; #ifdef PCP_PEER case PCP_OPCODE_PEER: remainingSize -= sizeof(pcp_peer_v2_t); if (remainingSize < 0) { pcp_msg_info->result_code = PCP_ERR_MALFORMED_REQUEST; return pcp_msg_info->result_code; } peer_v2 = (pcp_peer_v2_t*)req; #ifdef DEBUG printPEEROpcodeVersion2(peer_v2); #endif /* DEBUG */ parsePCPPEER_version2(peer_v2, pcp_msg_info); req += sizeof(pcp_peer_v2_t); if (pcp_msg_info->result_code != 0) { return pcp_msg_info->result_code; } parsePCPOptions(req, remainingSize, pcp_msg_info); if (ValidatePCPMsg(pcp_msg_info)) { if (pcp_msg_info->lifetime == 0) { DeletePCPPeer(pcp_msg_info); } else { CreatePCPPeer(pcp_msg_info); } } else { syslog(LOG_ERR, "PCP: Invalid PCP v2 PEER message."); } break; #endif /* PCP_PEER */ #ifdef PCP_SADSCP case PCP_OPCODE_SADSCP: remainingSize -= sizeof(pcp_sadscp_req_t); if (remainingSize < 0) { pcp_msg_info->result_code = PCP_ERR_MALFORMED_REQUEST; return pcp_msg_info->result_code; } sadscp = (pcp_sadscp_req_t*)req; req += sizeof(pcp_sadscp_req_t); if (sadscp->app_name_length > remainingSize) { pcp_msg_info->result_code = PCP_ERR_MALFORMED_OPTION; } #ifdef DEBUG printSADSCPOpcode(sadscp); #endif if (parseSADSCP(sadscp, pcp_msg_info)) { return pcp_msg_info->result_code; } get_dscp_value(pcp_msg_info); break; #endif default: pcp_msg_info->result_code = PCP_ERR_UNSUPP_OPCODE; break; } } else { pcp_msg_info->result_code = PCP_ERR_UNSUPP_VERSION; return pcp_msg_info->result_code; } return 1; } static void createPCPResponse(unsigned char *response, pcp_info_t *pcp_msg_info) { pcp_response_t *resp = (pcp_response_t*)response; resp->reserved = 0; resp->reserved1[0]=0; resp->reserved1[1]=0; resp->reserved1[2]=0; if (pcp_msg_info->result_code == PCP_ERR_UNSUPP_VERSION ) { /* highest supported version */ resp->ver = this_server_info.server_version; } else { resp->ver = pcp_msg_info->version; } resp->r_opcode |= 0x80; resp->result_code = pcp_msg_info->result_code; resp->epochtime = htonl(time(NULL) - startup_time); switch (pcp_msg_info->result_code) { /*long lifetime errors*/ case PCP_ERR_UNSUPP_VERSION: case PCP_ERR_NOT_AUTHORIZED: case PCP_ERR_MALFORMED_REQUEST: case PCP_ERR_UNSUPP_OPCODE: case PCP_ERR_UNSUPP_OPTION: case PCP_ERR_MALFORMED_OPTION: case PCP_ERR_UNSUPP_PROTOCOL: case PCP_ERR_ADDRESS_MISMATCH: case PCP_ERR_CANNOT_PROVIDE_EXTERNAL: case PCP_ERR_EXCESSIVE_REMOTE_PEERS: resp->lifetime = 0; break; case PCP_ERR_NETWORK_FAILURE: case PCP_ERR_NO_RESOURCES: case PCP_ERR_USER_EX_QUOTA: resp->lifetime = htonl(30); break; case PCP_SUCCESS: default: resp->lifetime = htonl(pcp_msg_info->lifetime); break; } if (resp->r_opcode == 0x81) { /* MAP response */ if (resp->ver == 1 ) { pcp_map_v1_t *mapr = (pcp_map_v1_t *)resp->next_data; mapr->ext_ip = *pcp_msg_info->ext_ip; mapr->ext_port = htons(pcp_msg_info->ext_port); mapr->int_port = htons(pcp_msg_info->int_port); } else if (resp->ver == 2 ) { pcp_map_v2_t *mapr = (pcp_map_v2_t *)resp->next_data; mapr->ext_ip = *pcp_msg_info->ext_ip; mapr->ext_port = htons(pcp_msg_info->ext_port); mapr->int_port = htons(pcp_msg_info->int_port); } } #ifdef PCP_PEER else if (resp->r_opcode == 0x82) { /* PEER response */ if (resp->ver == 1 ){ pcp_peer_v1_t* peer_resp = (pcp_peer_v1_t*)resp->next_data; peer_resp->ext_port = htons(pcp_msg_info->ext_port); peer_resp->int_port = htons(pcp_msg_info->int_port); peer_resp->peer_port = htons(pcp_msg_info->peer_port); peer_resp->ext_ip = *pcp_msg_info->ext_ip; } else if (resp->ver == 2 ){ pcp_peer_v2_t* peer_resp = (pcp_peer_v2_t*)resp->next_data; peer_resp->ext_port = htons(pcp_msg_info->ext_port); peer_resp->int_port = htons(pcp_msg_info->int_port); peer_resp->peer_port = htons(pcp_msg_info->peer_port); peer_resp->ext_ip = *pcp_msg_info->ext_ip; } } #endif /* PCP_PEER */ #ifdef PCP_SADSCP else if (resp->r_opcode == 0x83) { /*SADSCP response*/ pcp_sadscp_resp_t *sadscp_resp = (pcp_sadscp_resp_t*)resp->next_data; sadscp_resp->a_r_dscp_value = pcp_msg_info->matched_name<<7; sadscp_resp->a_r_dscp_value &= ~(1<<6); sadscp_resp->a_r_dscp_value |= (pcp_msg_info->sadscp_dscp & PCP_SADSCP_MASK); memset(sadscp_resp->reserved, 0, sizeof(sadscp_resp->reserved)); } #endif /* PCP_SADSCP */ } int ProcessIncomingPCPPacket(int s, unsigned char *buff, int len, const struct sockaddr *senderaddr, const struct sockaddr_in6 *receiveraddr) { pcp_info_t pcp_msg_info; struct lan_addr_s * lan_addr; char addr_str[64]; memset(&pcp_msg_info, 0, sizeof(pcp_info_t)); if(senderaddr->sa_family == AF_INET) { const struct sockaddr_in * senderaddr_v4 = (const struct sockaddr_in *)senderaddr; pcp_msg_info.sender_ip.s6_addr[11] = 0xff; pcp_msg_info.sender_ip.s6_addr[10] = 0xff; memcpy(pcp_msg_info.sender_ip.s6_addr+12, &senderaddr_v4->sin_addr, 4); } else if(senderaddr->sa_family == AF_INET6) { const struct sockaddr_in6 * senderaddr_v6 = (const struct sockaddr_in6 *)senderaddr; pcp_msg_info.sender_ip = senderaddr_v6->sin6_addr; } else { syslog(LOG_WARNING, "unknown PCP packet sender address family %d", senderaddr->sa_family); return 0; } if(sockaddr_to_string(senderaddr, addr_str, sizeof(addr_str))) syslog(LOG_DEBUG, "PCP request received from %s %dbytes", addr_str, len); if(buff[1] & 128) { /* discarding PCP responses silently */ return 0; } /* If we're in allow third party-mode, we probably don't care * about locality either. Let's hope firewall is ok. */ if (!GETFLAG(PCP_ALLOWTHIRDPARTYMASK)) { lan_addr = get_lan_for_peer(senderaddr); if(lan_addr == NULL) { syslog(LOG_WARNING, "PCP packet sender %s not from a LAN, ignoring", addr_str); return 0; } } if (processPCPRequest(buff, len, &pcp_msg_info) ) { createPCPResponse(buff, &pcp_msg_info); if(len < PCP_MIN_LEN) len = PCP_MIN_LEN; else len = (len + 3) & ~3; /* round up resp. length to multiple of 4 */ len = sendto_or_schedule2(s, buff, len, 0, senderaddr, (senderaddr->sa_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6), receiveraddr); if( len < 0 ) { syslog(LOG_ERR, "sendto(pcpserver): %m"); } } return 0; } #ifdef ENABLE_IPV6 int OpenAndConfPCPv6Socket(void) { int s; int i = 1; struct sockaddr_in6 addr; s = socket(PF_INET6, SOCK_DGRAM, 0/*IPPROTO_UDP*/); if(s < 0) { syslog(LOG_ERR, "%s: socket(): %m", "OpenAndConfPCPv6Socket"); return -1; } if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0) { syslog(LOG_WARNING, "%s: setsockopt(SO_REUSEADDR): %m", "OpenAndConfPCPv6Socket"); } #ifdef IPV6_V6ONLY /* 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, "%s: setsockopt(IPV6_V6ONLY): %m", "OpenAndConfPCPv6Socket"); } #endif #ifdef IPV6_RECVPKTINFO /* see RFC3542 */ if(setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, &i, sizeof(i)) < 0) { syslog(LOG_WARNING, "%s: setsockopt(IPV6_RECVPKTINFO): %m", "OpenAndConfPCPv6Socket"); } #endif if(!set_non_blocking(s)) { syslog(LOG_WARNING, "%s: set_non_blocking(): %m", "OpenAndConfPCPv6Socket"); } memset(&addr, 0, sizeof(addr)); addr.sin6_family = AF_INET6; addr.sin6_port = htons(NATPMP_PORT); addr.sin6_addr = ipv6_bind_addr; if(bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { syslog(LOG_ERR, "%s: bind(): %m", "OpenAndConfPCPv6Socket"); close(s); return -1; } return s; } #endif /*ENABLE_IPV6*/ #endif /*ENABLE_PCP*/ miniupnpd-1.8.20140523/pcplearndscp.h010064400017500000024000000037251225262601300163030ustar00nanardstaff/* $Id: pcplearndscp.h,v 1.2 2013/12/13 15:47:23 nanard Exp $ */ /* MiniUPnP project * Website : http://miniupnp.free.fr/ * Author : Miroslav Bagljas Copyright (c) 2013 by Cisco Systems, Inc. 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 PCPLEARNDSCP_H_INCLUDED #define PCPLEARNDSCP_H_INCLUDED struct dscp_values { char *app_name; unsigned int app_name_len; unsigned char delay; unsigned char loss; unsigned char jitter; unsigned char dscp_value; }; /* #set_learn_dscp "Webex" 1 1 1 34 */ int read_learn_dscp_line(struct dscp_values *dscpvalues, char *p); #endif /* PCPLEARNDSCP_H_INCLUDED */ miniupnpd-1.8.20140523/pcplearndscp.c010064400017500000024000000156411225260506500163020ustar00nanardstaff/* $Id: pcplearndscp.c,v 1.1 2013/12/13 13:23:01 nanard Exp $ */ /* MiniUPnP project * Website : http://miniupnp.free.fr/ * Author : Miroslav Bagljas Copyright (c) 2013 by Cisco Systems, Inc. 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 "config.h" #include #include #include #include #include #include "upnpglobalvars.h" #include "pcplearndscp.h" #ifdef PCP_SADSCP void print_dscp(void) { unsigned int i; for (i=0; i < num_dscp_values; i++){ syslog(LOG_DEBUG, "Appname %*.s, del %d, loss %d, jitter %d, dscp %d", dscp_values_list[i].app_name_len, dscp_values_list[i].app_name, dscp_values_list[i].delay, dscp_values_list[i].loss, dscp_values_list[i].jitter, dscp_values_list[i].dscp_value ); } } int read_learn_dscp_line(struct dscp_values *dscpvalues, char *p) { char * q; size_t len; unsigned int sizeof_first_token = sizeof("set_learn_dscp") - 1; int af_value; int cs_value; /* first token: (set_learn_dscp) skip it */ while(isspace(*p)) p++; if(0 == memcmp(p, "set_learn_dscp", sizeof_first_token)) { p += sizeof_first_token; } else { return -1; } while(isspace(*p)) p++; /* second token: name of the application */ // if if(!(*p == '"')) return -1; p++; for(q = p; !(*q == '"'); q++); len = q - p; if (len != 0) { dscpvalues->app_name = strndup(p, len); } else { dscpvalues->app_name = NULL; } dscpvalues->app_name_len = len; p = q + 1; /* third token: delay */ while(isspace(*p)) p++; if(!isdigit(*p)) goto exit_err_and_cleanup; for(q = p; isdigit(*q); q++); if(isspace(*q)) { *q = '\0'; dscpvalues->delay = (unsigned char)atoi(p); if (dscpvalues->delay >= 3) { fprintf(stderr, "Wrong delay value %d in \n", dscpvalues->delay); fprintf(stderr, "Delay can be from set {0,1,2} 0=low delay, 1=medium delay, 2=high delay\n"); goto exit_err_and_cleanup; } } else { goto exit_err_and_cleanup; } p = q + 1; /* fourth token: loss */ while(isspace(*p)) p++; if(!isdigit(*p)) goto exit_err_and_cleanup; for(q = p; isdigit(*q); q++); if(isspace(*q)) { *q = '\0'; dscpvalues->loss = (unsigned char)atoi(p); if (dscpvalues->loss >= 3) { fprintf(stderr, "Wrong loss value %d \n", dscpvalues->loss); fprintf(stderr, "Delay can be from set {0,1,2} 0=low loss, 1=medium loss, 2=high loss\n"); goto exit_err_and_cleanup; } } else { goto exit_err_and_cleanup; } p = q + 1; /* fifth token: jitter */ while(isspace(*p)) p++; if(!isdigit(*p)) goto exit_err_and_cleanup; for(q = p; isdigit(*q); q++); if(isspace(*q)) { *q = '\0'; dscpvalues->jitter = (unsigned char)atoi(p); if (dscpvalues->jitter >= 3) { fprintf(stderr, "Wrong jitter value %d \n", dscpvalues->jitter); fprintf(stderr, "Delay can be from set {0,1,2} 0=low jitter, 1=medium jitter, 2=high jitter \n"); goto exit_err_and_cleanup; } } else { goto exit_err_and_cleanup; } p = q + 1; while(isspace(*p)) p++; /*{ }*/ p = q + 1; /* sixth token: DSCP value */ while(isspace(*p)) p++; if(!isdigit(*p) && !( toupper(*p) == 'A' && toupper(*(p+1)) == 'F') && !( toupper(*p) == 'C' && toupper(*(p+1)) == 'S') && !( toupper(*p) == 'E' && toupper(*(p+1)) == 'F') ) goto exit_err_and_cleanup; // for(q = p; isdigit(*q) || (toupper(*q) == 'A') || (toupper(*q) == 'F'); q++); for(q = p; isdigit(*q) || isalpha(*q); q++); if(isspace(*q) || *q == '\0') { *q = '\0'; if (toupper(*p) == 'A' && toupper(*(p+1)) == 'F'){ p = p+2; if (*p == '\0') { dscpvalues->dscp_value = 0; } else if (!isdigit(*p)) { goto exit_err_and_cleanup; } else { af_value = atoi(p); switch(af_value) { case 11: dscpvalues->dscp_value = 10; break; case 12: dscpvalues->dscp_value = 12; break; case 13: dscpvalues->dscp_value = 14; break; case 21: dscpvalues->dscp_value = 18; break; case 22: dscpvalues->dscp_value = 20; break; case 23: dscpvalues->dscp_value = 22; break; case 31: dscpvalues->dscp_value = 26; break; case 32: dscpvalues->dscp_value = 28; break; case 33: dscpvalues->dscp_value = 30; break; case 41: dscpvalues->dscp_value = 34; break; case 42: dscpvalues->dscp_value = 36; break; case 43: dscpvalues->dscp_value = 38; break; default: fprintf(stderr, "Unknown AF value %u \n", af_value); goto exit_err_and_cleanup; } } } else if (toupper(*p) == 'C' && toupper(*(p+1)) == 'S'){ p=p+2; if (*p == '\0') { dscpvalues->dscp_value = 0; } else if (!isdigit(*p)) { fprintf(stderr, "Not digit after CS but %c \n", *p); goto exit_err_and_cleanup; } else { cs_value = atoi(p); switch(cs_value) { case 1: dscpvalues->dscp_value = 8; break; case 2: dscpvalues->dscp_value = 16; break; case 3: dscpvalues->dscp_value = 24; break; case 4: dscpvalues->dscp_value = 32; break; case 5: dscpvalues->dscp_value = 40; break; case 6: dscpvalues->dscp_value = 48; break; case 7: dscpvalues->dscp_value = 56; break; default: fprintf(stderr, "Unknown CS value %d \n", cs_value); goto exit_err_and_cleanup; } } } else if (toupper(*p) == 'E' && toupper(*(p+1)) == 'F'){ dscpvalues->dscp_value = 46; } else { dscpvalues->dscp_value = (unsigned char)atoi(p); } } else { goto exit_err_and_cleanup; } return 0; exit_err_and_cleanup: free(dscpvalues->app_name); dscpvalues->app_name = NULL; dscpvalues->app_name_len = 0; return -1; } #endif miniupnpd-1.8.20140523/pcp_msg_struct.h010064400017500000024000000232601231377315600166660ustar00nanardstaff/* $Id: pcp_msg_struct.h,v 1.4 2014/03/24 08:25:52 nanard Exp $ */ /* MiniUPnP project * Website : http://miniupnp.free.fr/ * Author : Peter Tatrai Copyright (c) 2013 by Cisco Systems, Inc. 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. */ #define PCP_OPCODE_ANNOUNCE 0 #define PCP_OPCODE_MAP 1 #define PCP_OPCODE_PEER 2 #ifdef PCP_SADSCP #define PCP_OPCODE_SADSCP 3 #endif /* Possible response codes sent by server, as a result of client request*/ #define PCP_SUCCESS 0 #define PCP_ERR_UNSUPP_VERSION 1 /** The version number at the start of the PCP Request * header is not recognized by this PCP server. This is a long * lifetime error. This document describes PCP version 2. */ #define PCP_ERR_NOT_AUTHORIZED 2 /**The requested operation is disabled for this PCP * client, or the PCP client requested an operation that cannot be * fulfilled by the PCP server's security policy. This is a long * lifetime error. */ #define PCP_ERR_MALFORMED_REQUEST 3 /**The request could not be successfully parsed. * This is a long lifetime error. */ #define PCP_ERR_UNSUPP_OPCODE 4 /** Unsupported Opcode. This is a long lifetime error. */ #define PCP_ERR_UNSUPP_OPTION 5 /**Unsupported Option. This error only occurs if the * Option is in the mandatory-to-process range. This is a long * lifetime error. */ #define PCP_ERR_MALFORMED_OPTION 6 /**Malformed Option (e.g., appears too many times, * invalid length). This is a long lifetime error. */ #define PCP_ERR_NETWORK_FAILURE 7 /**The PCP server or the device it controls are * experiencing a network failure of some sort (e.g., has not * obtained an External IP address). This is a short lifetime error. */ #define PCP_ERR_NO_RESOURCES 8 /**Request is well-formed and valid, but the server has * insufficient resources to complete the requested operation at this * time. For example, the NAT device cannot create more mappings at * this time, is short of CPU cycles or memory, or is unable to * handle the request due to some other temporary condition. The * same request may succeed in the future. This is a system-wide * error, different from USER_EX_QUOTA. This can be used as a catch- * all error, should no other error message be suitable. This is a * short lifetime error. */ #define PCP_ERR_UNSUPP_PROTOCOL 9 /**Unsupported transport protocol, e.g. SCTP in a * NAT that handles only UDP and TCP. This is a long lifetime error. */ #define PCP_ERR_USER_EX_QUOTA 10 /** This attempt to create a new mapping would exceed * this subscriber's port quota. This is a short lifetime error. */ #define PCP_ERR_CANNOT_PROVIDE_EXTERNAL 11 /** The suggested external port and/or * external address cannot be provided. This error MUST only be * returned for: * * MAP requests that included the PREFER_FAILURE Option * (normal MAP requests will return an available external port) * * MAP requests for the SCTP protocol (PREFER_FAILURE is implied) * * PEER requests */ #define PCP_ERR_ADDRESS_MISMATCH 12 /** The source IP address of the request packet does * not match the contents of the PCP Client's IP Address field, due * to an unexpected NAT on the path between the PCP client and the * PCP-controlled NAT or firewall. This is a long lifetime error. */ #define PCP_ERR_EXCESSIVE_REMOTE_PEERS 13 /** The PCP server was not able to create the * filters in this request. This result code MUST only be returned * if the MAP request contained the FILTER Option. See Section 13.3 * for processing information. This is a long lifetime error. */ typedef enum pcp_options { PCP_OPTION_3RD_PARTY = 1, PCP_OPTION_PREF_FAIL = 2, PCP_OPTION_FILTER = 3, #ifdef PCP_FLOWP PCP_OPTION_FLOW_PRIORITY = 4, /*TODO: change it to correct value*/ #endif } pcp_options_t; #ifdef WIN32 #pragma warning (push) #pragma warning (disable:4200) #endif /* WIN32 */ #pragma pack(push, 1) /* PCP common request header*/ typedef struct pcp_request { uint8_t ver; uint8_t r_opcode; uint16_t reserved; uint32_t req_lifetime; struct in6_addr ip; /* ipv4 will be represented by the ipv4 mapped ipv6 */ uint8_t next_data[0]; } pcp_request_t; /* PCP common response header*/ typedef struct pcp_response { uint8_t ver; uint8_t r_opcode; /* R indicates Request (0) or Response (1) Opcode is 7 bit value specifying operation MAP or PEER */ uint8_t reserved; /* reserved bits, must be 0 on transmission and must be ignored on reception */ uint8_t result_code; /* */ uint32_t lifetime; /* an unsigned 32-bit integer, in seconds {0, 2^32-1}*/ uint32_t epochtime; /* epoch indicates how long has PCP server had its current mappings it increases by 1 every second */ uint32_t reserved1[3];/* For requests that were successfully parsed this must be sent as 0 */ uint8_t next_data[0]; } pcp_response_t; typedef struct pcp_options_hdr { uint8_t code; /* Most significant bit indicates if this option is mandatory (0) or optional (1) */ uint8_t reserved; /* MUST be set to 0 on transmission and MUST be ignored on reception */ uint16_t len; /* indicates the length of the enclosed data in octets (see RFC) */ uint8_t next_data[0]; /* */ } pcp_options_hdr_t; /* same for both request and response */ typedef struct pcp_map_v2 { uint32_t nonce[3]; uint8_t protocol; /* 6 = TCP, 17 = UDP, 0 = 'all protocols' */ uint8_t reserved[3]; uint16_t int_port; /* 0 indicates 'all ports' */ uint16_t ext_port; /* suggested external port */ struct in6_addr ext_ip; /* suggested external IP address * ipv4 will be represented by the ipv4 mapped ipv6 */ uint8_t next_data[0]; } pcp_map_v2_t; /* same for both request and response */ typedef struct pcp_map_v1 { uint8_t protocol; uint8_t reserved[3]; uint16_t int_port; uint16_t ext_port; struct in6_addr ext_ip; /* ipv4 will be represented by the ipv4 mapped ipv6 */ uint8_t next_data[0]; } pcp_map_v1_t; /* same for both request and response */ typedef struct pcp_peer_v1 { uint8_t protocol; uint8_t reserved[3]; uint16_t int_port; uint16_t ext_port; struct in6_addr ext_ip; /* ipv4 will be represented by the ipv4 mapped ipv6 */ uint16_t peer_port; uint16_t reserved1; struct in6_addr peer_ip; uint8_t next_data[0]; } pcp_peer_v1_t; /* same for both request and response */ typedef struct pcp_peer_v2 { uint32_t nonce[3]; uint8_t protocol; uint8_t reserved[3]; uint16_t int_port; uint16_t ext_port; struct in6_addr ext_ip; /* ipv4 will be represented by the ipv4 mapped ipv6 */ uint16_t peer_port; uint16_t reserved1; struct in6_addr peer_ip; uint8_t next_data[0]; } pcp_peer_v2_t; #ifdef PCP_SADSCP typedef struct pcp_sadscp_req { uint32_t nonce[3]; uint8_t tolerance_fields; uint8_t app_name_length; char app_name[0]; } pcp_sadscp_req_t; typedef struct pcp_sadscp_resp { uint32_t nonce[3]; #define PCP_SADSCP_MASK ((1<<6)-1) uint8_t a_r_dscp_value; uint8_t reserved[3]; } pcp_sadscp_resp_t; #endif typedef struct pcp_prefer_fail_option { uint8_t option; uint8_t reserved; uint16_t len; uint8_t next_data[0]; } pcp_prefer_fail_option_t; typedef struct pcp_3rd_party_option{ uint8_t option; uint8_t reserved; uint16_t len; struct in6_addr ip; uint8_t next_data[0]; } pcp_3rd_party_option_t; #ifdef PCP_FLOWP typedef struct pcp_flow_priority_option{ uint8_t option; uint8_t reserved; uint16_t len; uint8_t dscp_up; uint8_t dscp_down; #define PCP_DSCP_MASK ((1<<6)-1) uint8_t reserved2; /* most significant bit is used for response */ uint8_t response_bit; uint8_t next_data[0]; } pcp_flow_priority_option_t; #endif typedef struct pcp_filter_option { uint8_t option; uint8_t reserved1; uint16_t len; uint8_t reserved2; uint8_t prefix_len; uint16_t peer_port; struct in6_addr peer_ip; }pcp_filter_option_t; #pragma pack(pop) #ifdef WIN32 #pragma warning (pop) #endif /* WIN32 */ miniupnpd-1.8.20140523/asyncsendto.c010064400017500000024000000232631233641226000161510ustar00nanardstaff/* $Id: asyncsendto.c,v 1.6 2014/05/19 14:26:56 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2014 Thomas Bernard * 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 "asyncsendto.h" #include "upnputils.h" /* state diagram for a packet : * * | * V * -> ESCHEDULED -> ESENDNOW -> sent * ^ | * | V * EWAITREADY -> sent */ struct scheduled_send { LIST_ENTRY(scheduled_send) entries; struct timeval ts; enum {ESCHEDULED=1, EWAITREADY=2, ESENDNOW=3} state; int sockfd; const void * buf; size_t len; int flags; const struct sockaddr *dest_addr; socklen_t addrlen; const struct sockaddr_in6 *src_addr; char data[]; }; static LIST_HEAD(listhead, scheduled_send) send_list = { NULL }; /* * ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, * const struct sockaddr *dest_addr, socklen_t addrlen); */ static ssize_t send_from_to(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr_in6 *src_addr, const struct sockaddr *dest_addr, socklen_t addrlen) { #ifdef IPV6_PKTINFO if(src_addr) { struct iovec iov; struct in6_pktinfo ipi6; uint8_t c[CMSG_SPACE(sizeof(ipi6))]; struct msghdr msg; struct cmsghdr* cmsg; iov.iov_base = (void *)buf; iov.iov_len = len; memset(&msg, 0, sizeof(msg)); msg.msg_iov = &iov; msg.msg_iovlen = 1; ipi6.ipi6_addr = src_addr->sin6_addr; ipi6.ipi6_ifindex = src_addr->sin6_scope_id; msg.msg_control = c; msg.msg_controllen = sizeof(c); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = IPPROTO_IPV6; cmsg->cmsg_type = IPV6_PKTINFO; cmsg->cmsg_len = CMSG_LEN(sizeof(ipi6)); memcpy(CMSG_DATA(cmsg), &ipi6, sizeof(ipi6)); msg.msg_name = (void *)dest_addr; msg.msg_namelen = addrlen; return sendmsg(sockfd, &msg, flags); } else { #endif /* IPV6_PKTINFO */ return sendto(sockfd, buf, len, flags, dest_addr, addrlen); #ifdef IPV6_PKTINFO } #endif /* IPV6_PKTINFO */ } /* delay = milli seconds */ ssize_t sendto_schedule2(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen, const struct sockaddr_in6 *src_addr, unsigned int delay) { enum {ESCHEDULED, EWAITREADY, ESENDNOW} state; ssize_t n; size_t alloc_len; struct timeval tv; struct scheduled_send * elt; if(delay == 0) { /* first try to send at once */ n = send_from_to(sockfd, buf, len, flags, src_addr, dest_addr, addrlen); if(n >= 0) return n; else if(errno == EAGAIN || errno == EWOULDBLOCK) { /* use select() on this socket */ state = EWAITREADY; } else if(errno == EINTR) { state = ESENDNOW; } else { /* uncatched error */ return n; } } else { state = ESCHEDULED; } /* schedule */ if(gettimeofday(&tv, 0) < 0) { return -1; } /* allocate enough space for structure + buffers */ alloc_len = sizeof(struct scheduled_send) + len + addrlen; if(src_addr) alloc_len += sizeof(struct sockaddr_in6); elt = malloc(alloc_len); if(elt == NULL) { syslog(LOG_ERR, "malloc failed to allocate %u bytes", (unsigned)alloc_len); return -1; } elt->state = state; /* time the packet should be sent */ elt->ts.tv_sec = tv.tv_sec + (delay / 1000); elt->ts.tv_usec = tv.tv_usec + (delay % 1000) * 1000; if(elt->ts.tv_usec > 1000000) { elt->ts.tv_sec++; elt->ts.tv_usec -= 1000000; } elt->sockfd = sockfd; elt->flags = flags; memcpy(elt->data, dest_addr, addrlen); elt->dest_addr = (struct sockaddr *)elt->data; elt->addrlen = addrlen; if(src_addr) { elt->src_addr = (struct sockaddr_in6 *)(elt->data + addrlen); memcpy((void *)elt->src_addr, src_addr, sizeof(struct sockaddr_in6)); elt->buf = (void *)(elt->data + addrlen + sizeof(struct sockaddr_in6)); } else { elt->src_addr = NULL; elt->buf = (void *)(elt->data + addrlen); } elt->len = len; memcpy((void *)elt->buf, buf, len); /* insert */ LIST_INSERT_HEAD( &send_list, elt, entries); return 0; } /* try to send at once, and queue the packet if needed */ ssize_t sendto_or_schedule(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) { return sendto_schedule2(sockfd, buf, len, flags, dest_addr, addrlen, NULL, 0); } ssize_t sendto_or_schedule2(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen, const struct sockaddr_in6 *src_addr) { return sendto_schedule2(sockfd, buf, len, flags, dest_addr, addrlen, src_addr, 0); } /* get_next_scheduled_send() return number of scheduled send in list */ int get_next_scheduled_send(struct timeval * next_send) { int n = 0; struct scheduled_send * elt; if(next_send == NULL) return -1; for(elt = send_list.lh_first; elt != NULL; elt = elt->entries.le_next) { if(n == 0 || (elt->ts.tv_sec < next_send->tv_sec) || (elt->ts.tv_sec == next_send->tv_sec && elt->ts.tv_usec < next_send->tv_usec)) { next_send->tv_sec = elt->ts.tv_sec; next_send->tv_usec = elt->ts.tv_usec; } n++; } return n; } /* update writefds for select() call * return the number of packets to try to send at once */ int get_sendto_fds(fd_set * writefds, int * max_fd, const struct timeval * now) { int n = 0; struct scheduled_send * elt; for(elt = send_list.lh_first; elt != NULL; elt = elt->entries.le_next) { if(elt->state == EWAITREADY) { /* last sendto() call returned EAGAIN/EWOULDBLOCK */ FD_SET(elt->sockfd, writefds); if(elt->sockfd > *max_fd) *max_fd = elt->sockfd; n++; } else if((elt->ts.tv_sec < now->tv_sec) || (elt->ts.tv_sec == now->tv_sec && elt->ts.tv_usec <= now->tv_usec)) { /* we waited long enough, now send ! */ elt->state = ESENDNOW; n++; } } return n; } /* executed sendto() when needed */ int try_sendto(fd_set * writefds) { int ret = 0; ssize_t n; struct scheduled_send * elt; struct scheduled_send * next; for(elt = send_list.lh_first; elt != NULL; elt = next) { next = elt->entries.le_next; if((elt->state == ESENDNOW) || (elt->state == EWAITREADY && FD_ISSET(elt->sockfd, writefds))) { #ifdef DEBUG syslog(LOG_DEBUG, "%s: %d bytes on socket %d", "try_sendto", (int)elt->len, elt->sockfd); #endif n = send_from_to(elt->sockfd, elt->buf, elt->len, elt->flags, elt->src_addr, elt->dest_addr, elt->addrlen); /*n = sendto(elt->sockfd, elt->buf, elt->len, elt->flags, elt->dest_addr, elt->addrlen);*/ if(n < 0) { if(errno == EINTR) { /* retry at once */ elt->state = ESENDNOW; continue; } else if(errno == EAGAIN || errno == EWOULDBLOCK) { /* retry once the socket is ready for writing */ elt->state = EWAITREADY; continue; } else { char addr_str[64]; /* uncatched error */ if(sockaddr_to_string(elt->dest_addr, addr_str, sizeof(addr_str)) <= 0) addr_str[0] = '\0'; syslog(LOG_ERR, "%s(sock=%d, len=%u, dest=%s): sendto: %m", "try_sendto", elt->sockfd, (unsigned)elt->len, addr_str); ret--; } } else if((int)n != (int)elt->len) { syslog(LOG_WARNING, "%s: %d bytes sent out of %d", "try_sendto", (int)n, (int)elt->len); } /* remove from the list */ LIST_REMOVE(elt, entries); free(elt); } } return ret; } /* maximum execution time for finalize_sendto() in milliseconds */ #define FINALIZE_SENDTO_DELAY (500) /* empty the list */ void finalize_sendto(void) { ssize_t n; struct scheduled_send * elt; struct scheduled_send * next; fd_set writefds; struct timeval deadline; struct timeval now; struct timeval timeout; int max_fd; if(gettimeofday(&deadline, NULL) < 0) { syslog(LOG_ERR, "gettimeofday: %m"); return; } deadline.tv_usec += FINALIZE_SENDTO_DELAY*1000; if(deadline.tv_usec > 1000000) { deadline.tv_sec++; deadline.tv_usec -= 1000000; } while(send_list.lh_first) { FD_ZERO(&writefds); max_fd = -1; for(elt = send_list.lh_first; elt != NULL; elt = next) { next = elt->entries.le_next; syslog(LOG_DEBUG, "finalize_sendto(): %d bytes on socket %d", (int)elt->len, elt->sockfd); n = send_from_to(elt->sockfd, elt->buf, elt->len, elt->flags, elt->src_addr, elt->dest_addr, elt->addrlen); /*n = sendto(elt->sockfd, elt->buf, elt->len, elt->flags, elt->dest_addr, elt->addrlen);*/ if(n < 0) { if(errno==EAGAIN || errno==EWOULDBLOCK) { FD_SET(elt->sockfd, &writefds); if(elt->sockfd > max_fd) max_fd = elt->sockfd; continue; } syslog(LOG_WARNING, "finalize_sendto(): socket=%d sendto: %m", elt->sockfd); } /* remove from the list */ LIST_REMOVE(elt, entries); free(elt); } /* check deadline */ if(gettimeofday(&now, NULL) < 0) { syslog(LOG_ERR, "gettimeofday: %m"); return; } if(now.tv_sec > deadline.tv_sec || (now.tv_sec == deadline.tv_sec && now.tv_usec > deadline.tv_usec)) { /* deadline ! */ while((elt = send_list.lh_first) != NULL) { LIST_REMOVE(elt, entries); free(elt); } return; } /* compute timeout value */ timeout.tv_sec = deadline.tv_sec - now.tv_sec; timeout.tv_usec = deadline.tv_usec - now.tv_usec; if(timeout.tv_usec < 0) { timeout.tv_sec--; timeout.tv_usec += 1000000; } if(max_fd >= 0) { if(select(max_fd + 1, NULL, &writefds, NULL, &timeout) < 0) { syslog(LOG_ERR, "select: %m"); return; } } } } miniupnpd-1.8.20140523/asyncsendto.h010064400017500000024000000034601233641155200161560ustar00nanardstaff/* $Id: asyncsendto.h,v 1.2 2014/05/19 14:21:10 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2014 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #ifndef ASYNCSENDTO_H_INCLUDED #define ASYNCSENDTO_H_INCLUDED /* sendto_schedule() : see sendto(2) * schedule sendto() call after delay (milliseconds) */ ssize_t sendto_schedule2(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen, const struct sockaddr_in6 *src_addr, unsigned int delay); #define sendto_schedule(sockfd, buf, len, flags, dest_addr, addrlen, delay) \ sendto_schedule2(sockfd, buf, len, flags, dest_addr, addrlen, NULL, delay) /* sendto_schedule() : see sendto(2) * try sendto() at once and schedule if EINTR/EAGAIN/EWOULDBLOCK */ ssize_t sendto_or_schedule(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); /* same as sendto_schedule() except it will try to set source address * (for IPV6 only) */ ssize_t sendto_or_schedule2(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen, const struct sockaddr_in6 *src_addr); /* get_next_scheduled_send() * return number of scheduled sendto * set next_send to timestamp to send next packet */ int get_next_scheduled_send(struct timeval * next_send); /* execute sendto() for needed packets */ int try_sendto(fd_set * writefds); /* set writefds before select() */ int get_sendto_fds(fd_set * writefds, int * max_fd, const struct timeval * now); /* empty the list */ void finalize_sendto(void); #endif miniupnpd-1.8.20140523/testasyncsendto.c010064400017500000024000000066641230307367600170670ustar00nanardstaff/* $Id: testasyncsendto.c,v 1.2 2014/02/25 11:00:14 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2014 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #include #include #include #include #include #include #include #include #include "miniupnpdtypes.h" #include "upnputils.h" #include "asyncsendto.h" struct lan_addr_list lan_addrs; #define DEST_IP "239.255.255.250" #define DEST_PORT 1900 /* ssize_t sendto_schedule(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen, unsigned int delay) */ int test(void) { int s; ssize_t n; int i; struct sockaddr_in addr; struct sockaddr_in dest_addr; struct timeval next_send; if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { syslog(LOG_ERR, "socket(): %m"); return 1; } set_non_blocking(s); memset(&addr, 0, sizeof(struct sockaddr_in)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; if(bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { syslog(LOG_ERR, "bind(): %m"); close(s); return 1; } memset(&dest_addr, 0, sizeof(struct sockaddr_in)); dest_addr.sin_family = AF_INET; dest_addr.sin_addr.s_addr = inet_addr(DEST_IP); dest_addr.sin_port = htons(DEST_PORT); n = sendto_or_schedule(s, "1234", 4, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); syslog(LOG_DEBUG, "sendto_or_schedule : %d", (int)n); n = sendto_schedule(s, "1234", 4, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr), 4400); syslog(LOG_DEBUG, "sendto_schedule : %d", (int)n); n = sendto_schedule(s, "1234", 4, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr), 3000); syslog(LOG_DEBUG, "sendto_schedule : %d", (int)n); while ((i = get_next_scheduled_send(&next_send)) > 0) { fd_set writefds; int max_fd; struct timeval timeout; struct timeval now; syslog(LOG_DEBUG, "get_next_scheduled_send : %d next_send=%ld.%06ld", i, next_send.tv_sec, next_send.tv_usec); FD_ZERO(&writefds); max_fd = 0; gettimeofday(&now, NULL); i = get_sendto_fds(&writefds, &max_fd, &now); if(now.tv_sec > next_send.tv_sec || (now.tv_sec == next_send.tv_sec && now.tv_usec >= next_send.tv_usec)) { if(i > 0) { /* dont wait */ timeout.tv_sec = 0; } else { /* wait 10sec :) */ timeout.tv_sec = 10; } timeout.tv_usec = 0; } else { /* ... */ timeout.tv_sec = (next_send.tv_sec - now.tv_sec); timeout.tv_usec = (next_send.tv_usec - now.tv_usec); if(timeout.tv_usec < 0) { timeout.tv_usec += 1000000; timeout.tv_sec--; } } syslog(LOG_DEBUG, "get_sendto_fds() returned %d", i); syslog(LOG_DEBUG, "select(%d, NULL, xx, NULL, %ld.%06ld)", max_fd, timeout.tv_sec, timeout.tv_usec); i = select(max_fd, NULL, &writefds, NULL, &timeout); if(i < 0) { syslog(LOG_ERR, "select: %m"); if(errno != EINTR) break; } else if(try_sendto(&writefds) < 0) { syslog(LOG_ERR, "try_sendto: %m"); break; } } close(s); return 0; } int main(int argc, char * * argv) { int r; (void)argc; (void)argv; openlog("testasyncsendto", LOG_CONS|LOG_PERROR, LOG_USER); r = test(); closelog(); return r; } miniupnpd-1.8.20140523/portinuse.c010064400017500000024000000250611232477504200156540ustar00nanardstaff/* $Id: portinuse.c,v 1.4 2014/04/20 16:46:42 nanard Exp $ */ /* MiniUPnP project * (c) 2007-2014 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 */ #if defined(__DragonFly__) || defined(__FreeBSD__) #include #include #endif #include #include #include #include #include #include #include #include #include #include #if defined(__OpenBSD__) #include #include #include #include #include #include #include #include #include #endif #if defined(__DragonFly__) || defined(__FreeBSD__) #include #include /* sys/socketvar.h must be included above the following headers */ #include #include #endif #include "macros.h" #include "config.h" #include "upnpglobalvars.h" #include "getifaddr.h" #include "portinuse.h" #if defined(USE_NETFILTER) #include "netfilter/iptcrdr.h" #endif #ifdef CHECK_PORTINUSE #if defined(USE_NETFILTER) /* Hardcoded for now. Ideally would come from .conf file */ char *chains_to_check[] = { "PREROUTING" , 0 }; #endif int port_in_use(const char *if_name, unsigned eport, int proto, const char *iaddr, unsigned iport) { int found = 0; char ip_addr_str[INET_ADDRSTRLEN]; struct in_addr ip_addr; #ifdef __linux__ /* linux code */ char line[256]; FILE *f; const char * tcpfile = "/proc/net/tcp"; const char * udpfile = "/proc/net/udp"; #endif if(getifaddr(if_name, ip_addr_str, INET_ADDRSTRLEN, &ip_addr, NULL) < 0) { ip_addr.s_addr = 0; ip_addr_str[0] = '\0'; } syslog(LOG_DEBUG, "Check protocol %s for port %d on ext_if %s %s, %08X", (proto==IPPROTO_TCP)?"tcp":"udp", eport, if_name, ip_addr_str, (unsigned)ip_addr.s_addr); /* Phase 1 : check for local sockets (would be listed by netstat) */ #if defined(__linux__) f = fopen((proto==IPPROTO_TCP)?tcpfile:udpfile, "r"); if (!f) { syslog(LOG_ERR, "cannot open %s", (proto==IPPROTO_TCP)?tcpfile:udpfile); return -1; } while (fgets(line, 255, f)) { char eaddr[68]; unsigned tmp_port; if (sscanf(line, "%*d: %64[0-9A-Fa-f]:%x %*x:%*x %*x %*x:%*x " "%*x:%*x %*x %*d %*d %*llu", eaddr, &tmp_port) == 2 ) { /* TODO add IPV6 support if enabled * Presently assumes IPV4 */ #ifdef DEBUG syslog(LOG_DEBUG, "port_in_use check port %d and address %s", tmp_port, eaddr); #endif if (tmp_port == eport) { char tmp_addr[4]; struct in_addr *tmp_ip_addr = (struct in_addr *)tmp_addr; if (sscanf(eaddr,"%2hhx%2hhx%2hhx%2hhx", &tmp_addr[3],&tmp_addr[2],&tmp_addr[1],&tmp_addr[0]) == 4) { if (tmp_ip_addr->s_addr == 0 || tmp_ip_addr->s_addr == ip_addr.s_addr) { found++; break; /* don't care how many, just that we found at least one */ } } } } } fclose(f); #elif defined(__OpenBSD__) static struct nlist list[] = { #if 0 {"_tcpstat", 0, 0, 0, 0}, {"_udpstat", 0, 0, 0, 0}, {"_tcbinfo", 0, 0, 0, 0}, {"_udbinfo", 0, 0, 0, 0}, #endif {"_tcbtable", 0, 0, 0, 0}, {"_udbtable", 0, 0, 0, 0}, {NULL,0, 0, 0, 0} }; char errstr[_POSIX2_LINE_MAX]; kvm_t *kd; ssize_t n; struct inpcbtable table; struct inpcb *next; struct inpcb inpcb; kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errstr); if(!kd) { syslog(LOG_ERR, "%s: kvm_openfiles(): %s", "portinuse()", errstr); return -1; } if(kvm_nlist(kd, list) < 0) { syslog(LOG_ERR, "%s: kvm_nlist(): %s", "portinuse()", kvm_geterr(kd)); kvm_close(kd); return -1; } n = kvm_read(kd, list[(proto==IPPROTO_TCP)?0:1].n_value, &table, sizeof(table)); if(n < 0) { syslog(LOG_ERR, "%s: kvm_read(): %s", "portinuse()", kvm_geterr(kd)); kvm_close(kd); return -1; } next = CIRCLEQ_FIRST(&table.inpt_queue); /*TAILQ_FIRST(&table.inpt_queue);*/ while(next != NULL) { if(((u_long)next & 3) != 0) break; n = kvm_read(kd, (u_long)next, &inpcb, sizeof(inpcb)); if(n < 0) { syslog(LOG_ERR, "kvm_read(): %s", kvm_geterr(kd)); break; } next = CIRCLEQ_NEXT(&inpcb, inp_queue); /*TAILQ_NEXT(&inpcb, inp_queue);*/ /* skip IPv6 sockets */ if((inpcb.inp_flags & INP_IPV6) != 0) continue; #ifdef DEBUG syslog(LOG_DEBUG, "%08lx:%hu %08lx:%hu", (u_long)inpcb.inp_laddr.s_addr, ntohs(inpcb.inp_lport), (u_long)inpcb.inp_faddr.s_addr, ntohs(inpcb.inp_fport)); #endif if(eport == (unsigned)ntohs(inpcb.inp_lport)) { if(inpcb.inp_laddr.s_addr == INADDR_ANY || inpcb.inp_laddr.s_addr == ip_addr.s_addr) { found++; break; /* don't care how many, just that we found at least one */ } } } kvm_close(kd); #elif defined(__DragonFly__) const char *varname; struct xinpcb *xip; struct xtcpcb *xtp; struct inpcb *inp; void *buf = NULL; void *so_begin, *so_end; size_t len; switch (proto) { case IPPROTO_TCP: varname = "net.inet.tcp.pcblist"; break; case IPPROTO_UDP: varname = "net.inet.udp.pcblist"; break; default: syslog(LOG_ERR, "port_in_use() unknown proto=%d", proto); return -1; } if (sysctlbyname(varname, NULL, &len, NULL, 0) < 0) { syslog(LOG_ERR, "sysctlbyname(%s, NULL, ...): %m", varname); return -1; } buf = malloc(len); if (buf == NULL) { syslog(LOG_ERR, "malloc(%u) failed", (unsigned)len); return -1; } if (sysctlbyname(varname, buf, &len, NULL, 0) < 0) { syslog(LOG_ERR, "sysctlbyname(%s, buf, ...): %m", varname); free(buf); return -1; } so_begin = buf; so_end = (uint8_t *)buf + len; for (so_begin = buf, so_end = (uint8_t *)so_begin + len; (uint8_t *)so_begin + sizeof(size_t) < (uint8_t *)so_end && (uint8_t *)so_begin + *(size_t *)so_begin <= (uint8_t *)so_end; so_begin = (uint8_t *)so_begin + *(size_t *)so_begin) { switch (proto) { case IPPROTO_TCP: xtp = (struct xtcpcb *)so_begin; if (xtp->xt_len != sizeof *xtp) { syslog(LOG_WARNING, "struct xtcpcb size mismatch; %ld vs %ld", (long)xtp->xt_len, sizeof *xtp); free(buf); return -1; } inp = &xtp->xt_inp; break; case IPPROTO_UDP: xip = (struct xinpcb *)so_begin; if (xip->xi_len != sizeof *xip) { syslog(LOG_WARNING, "struct xinpcb size mismatch : %ld vs %ld", (long)xip->xi_len, sizeof *xip); free(buf); return -1; } inp = &xip->xi_inp; break; default: abort(); } /* no support for IPv6 */ if ((inp->inp_vflag & INP_IPV6) != 0) continue; syslog(LOG_DEBUG, "%08lx:%hu %08lx:%hu <=> %hu %08lx:%hu", (u_long)inp->inp_laddr.s_addr, ntohs(inp->inp_lport), (u_long)inp->inp_faddr.s_addr, ntohs(inp->inp_fport), eport, (u_long)ip_addr.s_addr, iport ); if (eport == (unsigned)ntohs(inp->inp_lport)) { if (inp->inp_laddr.s_addr == INADDR_ANY || inp->inp_laddr.s_addr == ip_addr.s_addr) { found++; break; /* don't care how many, just that we found at least one */ } } } if (buf) { free(buf); buf = NULL; } #elif defined(__FreeBSD__) const char *varname; struct xinpgen *xig, *exig; struct xinpcb *xip; struct xtcpcb *xtp; struct inpcb *inp; void *buf = NULL; size_t len; switch (proto) { case IPPROTO_TCP: varname = "net.inet.tcp.pcblist"; break; case IPPROTO_UDP: varname = "net.inet.udp.pcblist"; break; default: syslog(LOG_ERR, "port_in_use() unknown proto=%d", proto); return -1; } if (sysctlbyname(varname, NULL, &len, NULL, 0) < 0) { syslog(LOG_ERR, "sysctlbyname(%s, NULL, ...): %m", varname); return -1; } buf = malloc(len); if (buf == NULL) { syslog(LOG_ERR, "malloc(%u) failed", (unsigned)len); return -1; } if (sysctlbyname(varname, buf, &len, NULL, 0) < 0) { syslog(LOG_ERR, "sysctlbyname(%s, buf, ...): %m", varname); free(buf); return -1; } xig = (struct xinpgen *)buf; exig = (struct xinpgen *)(void *)((char *)buf + len - sizeof *exig); if (xig->xig_len != sizeof *xig) { syslog(LOG_WARNING, "struct xinpgen size mismatch; %ld vs %ld", (long)xig->xig_len, sizeof *xig); free(buf); return -1; } if (exig->xig_len != sizeof *exig) { syslog(LOG_WARNING, "struct xinpgen size mismatch; %ld vs %ld", (long)exig->xig_len, sizeof *exig); free(buf); return -1; } while (1) { xig = (struct xinpgen *)(void *)((char *)xig + xig->xig_len); if (xig >= exig) break; switch (proto) { case IPPROTO_TCP: xtp = (struct xtcpcb *)xig; if (xtp->xt_len != sizeof *xtp) { syslog(LOG_WARNING, "struct xtcpcb size mismatch; %ld vs %ld", (long)xtp->xt_len, sizeof *xtp); free(buf); return -1; } inp = &xtp->xt_inp; break; case IPPROTO_UDP: xip = (struct xinpcb *)xig; if (xip->xi_len != sizeof *xip) { syslog(LOG_WARNING, "struct xinpcb size mismatch : %ld vs %ld", (long)xip->xi_len, sizeof *xip); free(buf); return -1; } inp = &xip->xi_inp; break; default: abort(); } /* no support for IPv6 */ if ((inp->inp_vflag & INP_IPV6) != 0) continue; syslog(LOG_DEBUG, "%08lx:%hu %08lx:%hu <=> %hu %08lx:%hu", (u_long)inp->inp_laddr.s_addr, ntohs(inp->inp_lport), (u_long)inp->inp_faddr.s_addr, ntohs(inp->inp_fport), eport, (u_long)ip_addr.s_addr, iport ); if (eport == (unsigned)ntohs(inp->inp_lport)) { if (inp->inp_laddr.s_addr == INADDR_ANY || inp->inp_laddr.s_addr == ip_addr.s_addr) { found++; break; /* don't care how many, just that we found at least one */ } } } if (buf) { free(buf); buf = NULL; } /* #elif __NetBSD__ */ #else /* TODO : NetBSD / Darwin (OS X) / Solaris code */ #error "No port_in_use() implementation available for this OS" #endif /* Phase 2 : check existing mappings * TODO : implement for pf/ipfw/etc. */ #if defined(USE_NETFILTER) if (!found) { char iaddr_old[16]; unsigned short iport_old; int i; for (i = 0; chains_to_check[i]; i++) { if (get_nat_redirect_rule(chains_to_check[i], if_name, eport, proto, iaddr_old, sizeof(iaddr_old), &iport_old, 0, 0, 0, 0, 0, 0, 0) == 0) { syslog(LOG_DEBUG, "port_in_use check port %d on nat chain %s redirected to %s port %d", eport, chains_to_check[i], iaddr_old, iport_old); if (!(strcmp(iaddr, iaddr_old)==0 && iport==iport_old)) { /* only "in use" if redirected to somewhere else */ found++; break; /* don't care how many, just that we found at least one */ } } } } #else /* USE_NETFILTER */ UNUSED(iport); UNUSED(iaddr); #endif /* USE_NETFILTER */ return found; } #endif /* CHECK_PORTINUSE */ miniupnpd-1.8.20140523/portinuse.h010064400017500000024000000012471231526242000156500ustar00nanardstaff/* $Id: portinuse.h,v 1.2 2014/03/28 12:03:28 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2014 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #ifndef __PORTINUSE_H__ #define __PORTINUSE_H__ #ifdef CHECK_PORTINUSE /* portinuse() * determine wether a port is already in use * on a given interface. * returns: 0 not in use, > 0 in use * -1 in case of error */ int port_in_use(const char *if_name, unsigned port, int proto, const char *iaddr, unsigned iport); #endif /* CHECK_PORTINUSE */ #endif miniupnpd-1.8.20140523/testportinuse.c010064400017500000024000000027261231625750300165550ustar00nanardstaff/* $Id: testportinuse.c,v 1.3 2014/03/28 12:13:17 nanard Exp $ */ /* MiniUPnP project * (c) 2014 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 "macros.h" #include "config.h" #include "portinuse.h" #ifdef USE_NETFILTER const char * miniupnpd_nat_chain = "MINIUPNPD"; const char * miniupnpd_peer_chain = "MINIUPNPD-PCP-PEER"; const char * miniupnpd_forward_chain = "MINIUPNPD"; #endif /* USE_NETFILTER */ int main(int argc, char * * argv) { #ifndef CHECK_PORTINUSE UNUSED(argc); UNUSED(argv); printf("CHECK_PORTINUSE is not defined.\n"); #else /* CHECK_PORTINUSE */ int r; const char * if_name; unsigned eport; int proto; const char * iaddr; unsigned iport; if(argc <= 5) { fprintf(stderr, "usage: %s if_name eport (tcp|udp) iaddr iport\n", argv[0]); return 1; } openlog("testportinuse", LOG_CONS|LOG_PERROR, LOG_USER); if_name = argv[1]; eport = (unsigned)atoi(argv[2]); proto = (0==strcmp(argv[3], "tcp"))?IPPROTO_TCP:IPPROTO_UDP; iaddr = argv[4]; iport = (unsigned)atoi(argv[5]); r = port_in_use(if_name, eport, proto, iaddr, iport); printf("port_in_use(%s, %u, %d, %s, %u) returned %d\n", if_name, eport, proto, iaddr, iport, r); closelog(); #endif /* CHECK_PORTINUSE */ return 0; }