diff --git a/README b/README index f3519b7..2c13e6d 100644 --- a/README +++ b/README @@ -40,6 +40,7 @@ Thanks to : * Alexey Osipov * Alexey Kuznetsov * Chiaki Ishikawa + * David Kerr * Jardel Weyrich * Leah X. Schmidt * Peter Tatrai diff --git a/miniupnpd/.gitignore b/miniupnpd/.gitignore index d6c6eb9..dd37154 100644 --- a/miniupnpd/.gitignore +++ b/miniupnpd/.gitignore @@ -10,6 +10,7 @@ testupnpdescgen testupnppermissions testgetroute testasyncsendto +testportinuse netfilter/testiptcrdr netfilter/testiptcrdr_dscp netfilter/testiptcrdr_peer diff --git a/miniupnpd/Changelog.txt b/miniupnpd/Changelog.txt index eefa138..1e65718 100644 --- a/miniupnpd/Changelog.txt +++ b/miniupnpd/Changelog.txt @@ -185,6 +185,9 @@ VERSION 1.8 : released on 2013/02/06 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 diff --git a/miniupnpd/Makefile b/miniupnpd/Makefile index 142cb95..fccf079 100644 --- a/miniupnpd/Makefile +++ b/miniupnpd/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.76 2014/03/10 10:26:15 nanard Exp $ +# $Id: Makefile,v 1.76.2.2 2014/03/14 10:49:08 nanard Exp $ # MiniUPnP project # http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ # Author: Thomas Bernard @@ -83,7 +83,7 @@ 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 + 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 @@ -120,10 +120,13 @@ 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 + testgetifaddr testgetroute testasyncsendto \ + testportinuse + .if $(OSNAME) == "Darwin" LIBS = .else @@ -147,6 +150,7 @@ clean: 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 @@ -177,7 +181,7 @@ genuuid: depend: config.h mkdep $(ALLOBJS:.o=.c) testupnpdescgen.c testgetifstats.c \ testupnppermissions.c miniupnpdctl.c testgetifaddr.c \ - testgetroute.c + testgetroute.c testportinuse.c testasyncsendto.c miniupnpd: config.h $(ALLOBJS) $(CC) $(CFLAGS) -o $@ $(ALLOBJS) $(LIBS) @@ -206,6 +210,9 @@ testgetroute: config.h $(TESTGETROUTEOBJS) testasyncsendto: config.h $(TESTASYNCSENDTOOBJS) $(CC) $(CFLAGS) -o $@ $(TESTASYNCSENDTOOBJS) +testportinuse: config.h $(TESTPORTINUSEOBJS) + $(CC) $(CFLAGS) -o $@ $(TESTPORTINUSEOBJS) -lkvm + # gmake : # $(CC) $(CFLAGS) -o $@ $^ # BSDmake : diff --git a/miniupnpd/Makefile.linux b/miniupnpd/Makefile.linux index a162db3..d1f7f3b 100644 --- a/miniupnpd/Makefile.linux +++ b/miniupnpd/Makefile.linux @@ -43,7 +43,7 @@ ETCINSTALLDIR = $(PREFIX)/etc/miniupnpd MANINSTALLDIR = $(INSTALLPREFIX)/share/man/man8 BASEOBJS = miniupnpd.o upnphttp.o upnpdescgen.o upnpsoap.o \ - upnpreplyparse.o minixml.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 \ @@ -150,7 +150,7 @@ TESTUPNPDESCGENOBJS = testupnpdescgen.o upnpdescgen.o EXECUTABLES = miniupnpd testupnpdescgen testgetifstats \ testupnppermissions miniupnpdctl testgetifaddr \ - testgetroute testasyncsendto + testgetroute testasyncsendto testportinuse .PHONY: all clean install depend genuuid @@ -208,6 +208,9 @@ testgetroute: testgetroute.o linux/getroute.o upnputils.o -lnfnetlink testasyncsendto: testasyncsendto.o asyncsendto.o upnputils.o \ linux/getroute.o -lnfnetlink +testportinuse: testportinuse.o portinuse.o getifaddr.o \ + netfilter/iptcrdr.o $(LIBS) + miniupnpdctl: miniupnpdctl.o config.h: genconfig.sh VERSION @@ -217,7 +220,8 @@ depend: config.h makedepend -f$(MAKEFILE_LIST) -Y \ $(ALLOBJS:.o=.c) $(TESTUPNPDESCGENOBJS:.o=.c) \ testgetifstats.c testupnppermissions.c testgetifaddr.c \ - testgetroute.c testasyncsendto.c miniupnpdctl.c 2>/dev/null + testgetroute.c testasyncsendto.c testportinuse.c \ + miniupnpdctl.c 2>/dev/null # DO NOT DELETE @@ -237,8 +241,11 @@ 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 +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 @@ -252,11 +259,11 @@ 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: asyncsendto.h -pcpserver.o: config.h pcpserver.h natpmp.h macros.h upnpglobalvars.h -pcpserver.o: upnppermissions.h miniupnpdtypes.h pcplearndscp.h upnpredirect.h -pcpserver.o: commonrdr.h getifaddr.h asyncsendto.h upnputils.h -pcpserver.o: pcp_msg_struct.h netfilter/iptcrdr.h commonrdr.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 @@ -289,4 +296,5 @@ 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 diff --git a/miniupnpd/Makefile.macosx b/miniupnpd/Makefile.macosx index 27280c6..7d821ff 100644 --- a/miniupnpd/Makefile.macosx +++ b/miniupnpd/Makefile.macosx @@ -33,7 +33,7 @@ STD_OBJS = miniupnpd.o upnphttp.o upnpdescgen.o upnpsoap.o \ upnpredirect.o getifaddr.o daemonize.o upnpglobalvars.o \ options.o upnppermissions.o minissdp.o natpmp.o \ upnpevents.o getconnstatus.o upnputils.o \ - asyncsendto.o + asyncsendto.o portinuse.o pcpserver.o MAC_OBJS = mac/getifstats.o bsd/ifacewatcher.o IPFW_OBJS = ipfw/ipfwrdr.o ipfw/ipfwaux.o PF_OBJS = pf/obsdrdr.o pf/pfpinhole.o @@ -51,11 +51,13 @@ 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 MINIUPNPDCTL_OBJS = miniupnpdctl.o EXECUTABLES = miniupnpd testupnpdescgen testgetifstats \ testupnppermissions miniupnpdctl \ - testgetifaddr + testgetifaddr testportinuse testasyncsendto LIBS = @@ -116,6 +118,11 @@ testgetifaddr: config.h $(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 diff --git a/miniupnpd/TODO b/miniupnpd/TODO index c34678d..eeb796a 100644 --- a/miniupnpd/TODO +++ b/miniupnpd/TODO @@ -33,3 +33,4 @@ use non blocking sockets everywhere : - NAT-PMP => OK - not needed for miniupnpdctl +implement port_in_use() for NetBSD and FreeBSD diff --git a/miniupnpd/genconfig.sh b/miniupnpd/genconfig.sh index 13a54e6..1089876 100755 --- a/miniupnpd/genconfig.sh +++ b/miniupnpd/genconfig.sh @@ -14,6 +14,7 @@ case "$argv" in --leasefile) LEASEFILE=1 ;; --vendorcfg) VENDORCFG=1 ;; --pcp-peer) PCP_PEER=1 ;; + --portinuse) PORTINUSE=1 ;; --help|-h) echo "Usage : $0 [options]" echo " --ipv6 enable IPv6" @@ -22,6 +23,7 @@ case "$argv" in 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 ;; *) @@ -395,6 +397,14 @@ else 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} @@ -497,7 +507,7 @@ else fi echo "" >> ${CONFIGFILE} -echo "#endif" >> ${CONFIGFILE} +echo "#endif /* ${CONFIGMACRO} */" >> ${CONFIGFILE} ${MV} ${CONFIGFILE} ${CONFIGFILE_FINAL} diff --git a/miniupnpd/natpmp.c b/miniupnpd/natpmp.c index 694e26c..c0d1996 100644 --- a/miniupnpd/natpmp.c +++ b/miniupnpd/natpmp.c @@ -23,10 +23,12 @@ #include "upnpredirect.h" #include "commonrdr.h" #include "upnputils.h" +#include "portinuse.h" #include "asyncsendto.h" #ifdef ENABLE_NATPMP + int OpenAndConfNATPMPSocket(in_addr_t addr) { int snatpmp; @@ -294,6 +296,15 @@ void ProcessIncomingNATPMPPacket(int s, unsigned char *msg_buff, int len, 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, diff --git a/miniupnpd/netfilter/iptcrdr.c b/miniupnpd/netfilter/iptcrdr.c index 6c8bf79..e5951e8 100644 --- a/miniupnpd/netfilter/iptcrdr.c +++ b/miniupnpd/netfilter/iptcrdr.c @@ -284,6 +284,22 @@ get_redirect_rule(const char * ifname, unsigned short eport, int proto, 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; @@ -301,18 +317,18 @@ get_redirect_rule(const char * ifname, unsigned short eport, int proto, iptc_strerror(errno)); return -1; } - if(!iptc_is_chain(miniupnpd_nat_chain, h)) + if(!iptc_is_chain(nat_chain_name, h)) { - syslog(LOG_ERR, "chain %s not found", miniupnpd_nat_chain); + syslog(LOG_ERR, "chain %s not found", nat_chain_name); } else { #ifdef IPTABLES_143 - for(e = iptc_first_rule(miniupnpd_nat_chain, h); + for(e = iptc_first_rule(nat_chain_name, h); e; e = iptc_next_rule(e, h)) #else - for(e = iptc_first_rule(miniupnpd_nat_chain, &h); + for(e = iptc_first_rule(nat_chain_name, &h); e; e = iptc_next_rule(e, &h)) #endif diff --git a/miniupnpd/netfilter/iptcrdr.h b/miniupnpd/netfilter/iptcrdr.h index 3d00ebe..21d5405 100644 --- a/miniupnpd/netfilter/iptcrdr.h +++ b/miniupnpd/netfilter/iptcrdr.h @@ -49,6 +49,13 @@ get_peer_rule_by_index(int index, 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 diff --git a/miniupnpd/portinuse.c b/miniupnpd/portinuse.c new file mode 100644 index 0000000..78ef8fb --- /dev/null +++ b/miniupnpd/portinuse.c @@ -0,0 +1,301 @@ +/* $Id: $ */ +/* 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__) +#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__) +#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; + + 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 __NetBSD__ */ +/* #elif __FreeBSD__ */ +/* TODO : FreeBSD / NetBSD / Darwin (OS X) / Solaris code */ +#else +#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 */ diff --git a/miniupnpd/portinuse.h b/miniupnpd/portinuse.h new file mode 100644 index 0000000..01842f6 --- /dev/null +++ b/miniupnpd/portinuse.h @@ -0,0 +1,23 @@ +/* $Id: $ */ +/* 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 diff --git a/miniupnpd/testasyncsendto.c b/miniupnpd/testasyncsendto.c index e413bd7..7006bf2 100644 --- a/miniupnpd/testasyncsendto.c +++ b/miniupnpd/testasyncsendto.c @@ -72,7 +72,7 @@ int test(void) 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); + i, (long)next_send.tv_sec, (long)next_send.tv_usec); FD_ZERO(&writefds); max_fd = 0; gettimeofday(&now, NULL); @@ -98,7 +98,7 @@ int test(void) } 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); + max_fd, (long)timeout.tv_sec, (long)timeout.tv_usec); i = select(max_fd, NULL, &writefds, NULL, &timeout); if(i < 0) { syslog(LOG_ERR, "select: %m"); diff --git a/miniupnpd/testportinuse.c b/miniupnpd/testportinuse.c new file mode 100644 index 0000000..59977f8 --- /dev/null +++ b/miniupnpd/testportinuse.c @@ -0,0 +1,56 @@ +/* $Id: testportinuse.c,v 1.1.2.5 2014/03/14 11:12:08 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 CHECK_PORTINUSE +#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 */ +#endif /* CHECK_PORTINUSE */ + +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; +} diff --git a/miniupnpd/upnpredirect.c b/miniupnpd/upnpredirect.c index 0b7e8a3..5a50c6d 100644 --- a/miniupnpd/upnpredirect.c +++ b/miniupnpd/upnpredirect.c @@ -23,6 +23,7 @@ #include "upnpredirect.h" #include "upnpglobalvars.h" #include "upnpevents.h" +#include "portinuse.h" #if defined(USE_NETFILTER) #include "netfilter/iptcrdr.h" #endif @@ -294,6 +295,12 @@ upnp_redirect(const char * rhost, unsigned short eport, 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",