diff --git a/README b/README index 96b37df..2c13e6d 100644 --- a/README +++ b/README @@ -40,7 +40,10 @@ Thanks to : * Alexey Osipov * Alexey Kuznetsov * Chiaki Ishikawa + * David Kerr * Jardel Weyrich * Leah X. Schmidt * Peter Tatrai * Leo Moll + * Daniel Becker + * Yonetani Tomokazu diff --git a/minissdpd/.gitignore b/minissdpd/.gitignore index 9c8b266..6e1b69d 100644 --- a/minissdpd/.gitignore +++ b/minissdpd/.gitignore @@ -2,4 +2,5 @@ minissdpd testcodelength testminissdpd +listifaces Makefile.bak diff --git a/minissdpd/Changelog.txt b/minissdpd/Changelog.txt index fa17abd..0cf152a 100644 --- a/minissdpd/Changelog.txt +++ b/minissdpd/Changelog.txt @@ -1,4 +1,8 @@ -$Id: Changelog.txt,v 1.32 2013/08/19 16:40:55 nanard Exp $ +$Id: Changelog.txt,v 1.33 2014/02/03 15:45:07 nanard Exp $ + +2014/02/03: + silently ignore EAGAIN, EWOULDBLOCK, EINTR of recv calls + Discover devices on the network at startup 2013/08/19: Translate README in english diff --git a/minissdpd/Makefile b/minissdpd/Makefile index e924acb..99c2691 100644 --- a/minissdpd/Makefile +++ b/minissdpd/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.17 2012/05/02 10:26:50 nanard Exp $ +# $Id: Makefile,v 1.18 2014/02/03 14:32:14 nanard Exp $ # MiniUPnP project # author: Thomas Bernard # website: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ @@ -59,6 +59,8 @@ testminissdpd: $(TESTMINISSDPDOBJS) testcodelength: testcodelength.o +listifaces: listifaces.o upnputils.o + depend: makedepend -f$(MAKEFILE_LIST) -Y \ $(ALLOBJS:.o=.c) 2>/dev/null diff --git a/minissdpd/listifaces.c b/minissdpd/listifaces.c index 14124ef..f893d6e 100644 --- a/minissdpd/listifaces.c +++ b/minissdpd/listifaces.c @@ -1,4 +1,7 @@ -/* $Id: listifaces.c,v 1.4 2007/09/23 16:59:02 nanard Exp $ */ +/* $Id: listifaces.c,v 1.6 2014/02/03 14:32:14 nanard Exp $ */ +/* (c) 2006-2014 Thomas BERNARD + * http://miniupnp.free.fr/ http://miniupnp.tuxfamily.org/ + */ #include #include #include @@ -8,7 +11,9 @@ #include #include #include +#include "upnputils.h" +/* hexdump */ void printhex(const unsigned char * p, int n) { int i; @@ -27,20 +32,28 @@ void printhex(const unsigned char * p, int n) } } -void listifaces() +/* List network interfaces */ +void listifaces(void) { struct ifconf ifc; - char * buf = 0; - int buflen = sizeof(struct ifreq)*20; - /*[sizeof(struct ifreq)*8];*/ + char * buf = NULL; + int buflen; int s, i; int j; char saddr[256/*INET_ADDRSTRLEN*/]; +#ifdef __linux__ + buflen = sizeof(struct ifreq)*10; +#else + buflen = 0; +#endif /*s = socket(PF_INET, SOCK_DGRAM, 0);*/ s = socket(AF_INET, SOCK_DGRAM, 0); do { +#ifdef __linux__ buflen += buflen; - buf = realloc(buf, buflen); +#endif + if(buflen > 0) + buf = realloc(buf, buflen); ifc.ifc_len = buflen; ifc.ifc_buf = (caddr_t)buf; if(ioctl(s, SIOCGIFCONF, &ifc) < 0) @@ -50,22 +63,39 @@ void listifaces() free(buf); return; } - printf("%d - %d - %d\n", buflen, ifc.ifc_len, (int)sizeof(struct ifreq)); - printf(" %d\n", IFNAMSIZ); - printf(" %d %d\n", (int)sizeof(struct sockaddr), (int)sizeof(struct sockaddr_in) ); - } while(buflen == ifc.ifc_len); + printf("buffer length=%d - buffer used=%d - sizeof(struct ifreq)=%d\n", + buflen, ifc.ifc_len, (int)sizeof(struct ifreq)); + printf("IFNAMSIZ=%d ", IFNAMSIZ); + printf("sizeof(struct sockaddr)=%d sizeof(struct sockaddr_in)=%d\n", + (int)sizeof(struct sockaddr), (int)sizeof(struct sockaddr_in) ); +#ifndef __linux__ + if(buflen == 0) + buflen = ifc.ifc_len; + else + break; + } while(1); +#else + } while(buflen <= ifc.ifc_len); +#endif printhex((const unsigned char *)ifc.ifc_buf, ifc.ifc_len); - j = 0; - for(i=0; iifr_name) + 16;//ifrp->ifr_addr.sa_len; /*inet_ntop(AF_INET, &(((struct sockaddr_in *)&(ifrp->ifr_addr))->sin_addr), saddr, sizeof(saddr));*/ saddr[0] = '\0'; - inet_ntop(ifrp->ifr_addr.sa_family, &(ifrp->ifr_addr.sa_data[2]), saddr, sizeof(saddr)); - printf("%2d %d %d %s %s\n", j, ifrp->ifr_addr.sa_family, -1/*ifrp->ifr_addr.sa_len*/, ifrp->ifr_name, saddr); - j++; + /* inet_ntop(ifrp->ifr_addr.sa_family, &(ifrp->ifr_addr.sa_data[2]), saddr, sizeof(saddr)); */ + sockaddr_to_string(&ifrp->ifr_addr, saddr, sizeof(saddr)); + printf("0x%03x %2d %2d %-16s %s\n", i, j, ifrp->ifr_addr.sa_family, ifrp->ifr_name, saddr); + /*ifrp->ifr_addr.sa_len is only available on BSD */ +#ifdef __linux__ + i += sizeof(struct ifreq); +#else + if(ifrp->ifr_addr.sa_len == 0) + break; + i += IFNAMSIZ + ifrp->ifr_addr.sa_len; +#endif } free(buf); close(s); @@ -73,6 +103,8 @@ void listifaces() int main(int argc, char * * argv) { + (void)argc; + (void)argv; listifaces(); return 0; } diff --git a/minissdpd/minissdpd.c b/minissdpd/minissdpd.c index 520a6c5..5e79293 100644 --- a/minissdpd/minissdpd.c +++ b/minissdpd/minissdpd.c @@ -1,6 +1,6 @@ -/* $Id: minissdpd.c,v 1.35 2012/05/21 17:13:11 nanard Exp $ */ +/* $Id: minissdpd.c,v 1.37 2014/02/28 18:39:11 nanard Exp $ */ /* MiniUPnP project - * (c) 2007-2012 Thomas Bernard + * (c) 2007-2014 Thomas Bernard * website : 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 */ @@ -224,6 +224,7 @@ SendSSDPMSEARCHResponse(int s, const struct sockaddr * sockname, n = sendto(s, buf, l, 0, sockname, sockname_len ); if(n < 0) { + /* XXX handle EINTR, EAGAIN, EWOULDBLOCK */ syslog(LOG_ERR, "sendto(udp): %m"); } } @@ -282,8 +283,10 @@ processMSEARCH(int s, const char * st, int st_len, } } else { /* find matching services */ + /* remove version at the end of the ST string */ if(st[st_len-2]==':' && isdigit(st[st_len-1])) st_len -= 2; + /* answer for each matching service */ for(serv = servicelisthead.lh_first; serv; serv = serv->entries.le_next) { @@ -338,6 +341,7 @@ ParseSSDPPacket(int s, const char * p, ssize_t n, unsigned int lifetime = 180; /* 3 minutes by default */ const char * st = NULL; int st_len = 0; + memset(headers, 0, sizeof(headers)); for(methodlen = 0; methodlen < n && (isalpha(p[methodlen]) || p[methodlen]=='-'); @@ -346,6 +350,12 @@ ParseSSDPPacket(int s, const char * p, ssize_t n, method = METHOD_MSEARCH; else if(methodlen==6 && 0==memcmp(p, "NOTIFY", 6)) method = METHOD_NOTIFY; + else if(methodlen==4 && 0==memcmp(p, "HTTP", 4)) { + /* answer to a M-SEARCH => process it as a NOTIFY + * with NTS: ssdp:alive */ + method = METHOD_NOTIFY; + nts = NTS_SSDP_ALIVE; + } linestart = p; while(linestart < p + n - 2) { /* start parsing the line : detect line end */ @@ -435,6 +445,8 @@ ParseSSDPPacket(int s, const char * p, ssize_t n, } else if(l==2 && 0==strncasecmp(linestart, "st", 2)) { st = valuestart; st_len = m; + if(method == METHOD_NOTIFY) + i = HEADER_NT; /* it was a M-SEARCH response */ } if(i>=0) { headers[i].p = valuestart; @@ -559,16 +571,16 @@ void processRequest(struct reqelem * req) syslog(LOG_WARNING, "bad request (length encoding)"); goto error; } - if(l == 0) { + if(l == 0 && type != 3) { syslog(LOG_WARNING, "bad request (length=0)"); goto error; } syslog(LOG_INFO, "(s=%d) request type=%d str='%.*s'", req->socket, type, l, p); switch(type) { - case 1: - case 2: - case 3: + case 1: /* request by type */ + case 2: /* request by USN (unique id) */ + case 3: /* everything */ while(d && (nrep < 255)) { if(d->t < t) { syslog(LOG_INFO, "outdated device"); @@ -633,17 +645,20 @@ void processRequest(struct reqelem * req) } } rbuf[0] = nrep; + syslog(LOG_DEBUG, "(s=%d) response : %d device%s", + req->socket, nrep, (nrep > 1) ? "s" : ""); if(write(req->socket, rbuf, rp - rbuf) < 0) { syslog(LOG_ERR, "(s=%d) write: %m", req->socket); goto error; } break; - case 4: + case 4: /* submit service */ newserv = malloc(sizeof(struct service)); if(!newserv) { syslog(LOG_ERR, "cannot allocate memory"); goto error; } + memset(newserv, 0, sizeof(struct service)); /* set pointers to NULL */ if(containsForbiddenChars(p, l)) { syslog(LOG_ERR, "bad request (st contains forbidden chars)"); goto error; @@ -779,6 +794,60 @@ sigterm(int sig) /*errno = save_errno;*/ } +#define PORT 1900 +#define XSTR(s) STR(s) +#define STR(s) #s +#define UPNP_MCAST_ADDR "239.255.255.250" +/* for IPv6 */ +#define UPNP_MCAST_LL_ADDR "FF02::C" /* link-local */ +#define UPNP_MCAST_SL_ADDR "FF05::C" /* site-local */ + +/* send the M-SEARCH request for all devices */ +void ssdpDiscoverAll(int s, int ipv6) +{ + static const char MSearchMsgFmt[] = + "M-SEARCH * HTTP/1.1\r\n" + "HOST: %s:" XSTR(PORT) "\r\n" + "ST: ssdp:all\r\n" + "MAN: \"ssdp:discover\"\r\n" + "MX: %u\r\n" + "\r\n"; + char bufr[512]; + int n; + int mx = 3; + int linklocal = 1; + struct sockaddr_storage sockudp_w; + + { + n = snprintf(bufr, sizeof(bufr), + MSearchMsgFmt, + ipv6 ? + (linklocal ? "[" UPNP_MCAST_LL_ADDR "]" : "[" UPNP_MCAST_SL_ADDR "]") + : UPNP_MCAST_ADDR, mx); + memset(&sockudp_w, 0, sizeof(struct sockaddr_storage)); + if(ipv6) { + struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_w; + p->sin6_family = AF_INET6; + p->sin6_port = htons(PORT); + inet_pton(AF_INET6, + linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR, + &(p->sin6_addr)); + } else { + struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_w; + p->sin_family = AF_INET; + p->sin_port = htons(PORT); + p->sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR); + } + + n = sendto(s, bufr, n, 0, (const struct sockaddr *)&sockudp_w, + ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)); + if (n < 0) { + /* XXX : EINTR EWOULDBLOCK EAGAIN */ + syslog(LOG_ERR, "sendto: %m"); + } + } +} + /* main(): program entry point */ int main(int argc, char * * argv) { @@ -957,6 +1026,11 @@ int main(int argc, char * * argv) writepidfile(pidfilename, pid); + /* send M-SEARCH ssdp:all Requests */ + ssdpDiscoverAll(s_ssdp, 0); + if(s_ssdp6 >= 0) + ssdpDiscoverAll(s_ssdp6, 1); + /* Main loop */ while(!quitting) { @@ -997,7 +1071,10 @@ int main(int argc, char * * argv) (struct sockaddr *)&sendername6, &sendername6_len); if(n<0) { - syslog(LOG_ERR, "recvfrom: %m"); + /* 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: %m"); } else { @@ -1028,7 +1105,10 @@ int main(int argc, char * * argv) (struct sockaddr *)&sendername, &sendername_len); if(n<0) { - syslog(LOG_ERR, "recvfrom: %m"); + /* 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: %m"); } else { diff --git a/minissdpd/testminissdpd.c b/minissdpd/testminissdpd.c index c06cf50..5670783 100644 --- a/minissdpd/testminissdpd.c +++ b/minissdpd/testminissdpd.c @@ -1,12 +1,13 @@ -/* $Id: testminissdpd.c,v 1.7 2012/05/02 10:28:25 nanard Exp $ */ +/* $Id: testminissdpd.c,v 1.8 2014/02/28 18:38:21 nanard Exp $ */ /* Project : miniupnp * website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * Author : Thomas BERNARD - * copyright (c) 2005-2007 Thomas Bernard + * copyright (c) 2005-2014 Thomas Bernard * This software is subjet to the conditions detailed in the * provided LICENCE file. */ #include #include +#include #include #include #include @@ -21,30 +22,64 @@ void printresponse(const unsigned char * resp, int n) int i, l; unsigned int nresp; const unsigned char * p; + if(n == 0) + return; for(i=0; i= resp + n) + goto error; /*l = *(p++);*/ DECODELENGTH(l, p); + if(p + l > resp + n) + goto error; printf("%d - %.*s\n", i, l, p); p += l; + if(p >= resp + n) + goto error; /*l = *(p++);*/ DECODELENGTH(l, p); + if(p + l > resp + n) + goto error; printf(" %.*s\n", l, p); p += l; + if(p >= resp + n) + goto error; /*l = *(p++);*/ DECODELENGTH(l, p); + if(p + l > resp + n) + goto error; printf(" %.*s\n", l, p); p += l; } + return; +error: + printf("*** WARNING : TRUNCATED RESPONSE ***\n"); } #define SENDCOMMAND(command, size) write(s, command, size); \ printf("Command written type=%u\n", (unsigned)command[0]); +int connect_unix_socket(const char * sockpath) +{ + int s; + struct sockaddr_un addr; + + s = socket(AF_UNIX, SOCK_STREAM, 0); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, sockpath, sizeof(addr.sun_path)); + if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) { + fprintf(stderr, "connecting to %s\n", addr.sun_path); + perror("connect"); + exit(1); + } + printf("Connected.\n"); + return s; +} + /* test program for minissdpd */ int main(int argc, char * * argv) @@ -52,10 +87,12 @@ main(int argc, char * * argv) char command1[] = "\x01\x00urn:schemas-upnp-org:device:InternetGatewayDevice"; char command2[] = "\x02\x00uuid:fc4ec57e-b051-11db-88f8-0060085db3f6::upnp:rootdevice"; char command3[] = { 0x03, 0x00 }; - struct sockaddr_un addr; + char command4[] = "\x04\x00test:test:test"; + char bad_command[] = { 0xff, 0xff }; + char overflow[] = { 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; int s; int i; - unsigned char buf[2048]; + unsigned char buf[16*1024]; ssize_t n; const char * sockpath = "/var/run/minissdpd.sock"; @@ -65,30 +102,51 @@ main(int argc, char * * argv) } command1[1] = sizeof(command1) - 3; command2[1] = sizeof(command2) - 3; - s = socket(AF_UNIX, SOCK_STREAM, 0); - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, sockpath, sizeof(addr.sun_path)); - if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) { - fprintf(stderr, "connecting to %s\n", addr.sun_path); - perror("connect"); - return 1; - } - printf("Connected.\n"); + command4[1] = sizeof(command4) - 3; + s = connect_unix_socket(sockpath); SENDCOMMAND(command1, sizeof(command1) - 1); n = read(s, buf, sizeof(buf)); printf("Response received %d bytes\n", (int)n); printresponse(buf, n); + if(n == 0) { + close(s); + s = connect_unix_socket(sockpath); + } SENDCOMMAND(command2, sizeof(command2) - 1); n = read(s, buf, sizeof(buf)); printf("Response received %d bytes\n", (int)n); printresponse(buf, n); + if(n == 0) { + close(s); + s = connect_unix_socket(sockpath); + } SENDCOMMAND(command3, sizeof(command3)); n = read(s, buf, sizeof(buf)); printf("Response received %d bytes\n", (int)n); printresponse(buf, n); + if(n == 0) { + close(s); + s = connect_unix_socket(sockpath); + } + + SENDCOMMAND(command4, sizeof(command4)); + + SENDCOMMAND(bad_command, sizeof(bad_command)); + n = read(s, buf, sizeof(buf)); + printf("Response received %d bytes\n", (int)n); + printresponse(buf, n); + if(n == 0) { + close(s); + s = connect_unix_socket(sockpath); + } + + SENDCOMMAND(overflow, sizeof(overflow)); + n = read(s, buf, sizeof(buf)); + printf("Response received %d bytes\n", (int)n); + printresponse(buf, n); close(s); return 0; diff --git a/miniupnpc/Changelog.txt b/miniupnpc/Changelog.txt index 180015a..53e9a11 100644 --- a/miniupnpc/Changelog.txt +++ b/miniupnpc/Changelog.txt @@ -1,6 +1,12 @@ -$Id: Changelog.txt,v 1.191 2014/01/31 13:18:24 nanard Exp $ +$Id: Changelog.txt,v 1.193 2014/02/05 17:26:45 nanard Exp $ miniUPnP client Changelog. +2014/02/05: + handle EINPROGRESS after connect() + +2014/02/03: + minixml now handle XML comments + VERSION 1.9 : released 2014/01/31 2014/01/31: diff --git a/miniupnpc/Makefile b/miniupnpc/Makefile index 409a555..49868cd 100644 --- a/miniupnpc/Makefile +++ b/miniupnpc/Makefile @@ -3,7 +3,7 @@ # http://miniupnp.free.fr/ # http://miniupnp.tuxfamily.org/ # https://github.com/miniupnp/miniupnp -# (c) 2005-2013 Thomas Bernard +# (c) 2005-2014 Thomas Bernard # to install use : # $ make DESTDIR=/tmp/dummylocation install # or @@ -176,7 +176,8 @@ validateupnpreplyparse: testupnpreplyparse testupnpreplyparse.sh clean: $(RM) $(LIBRARY) $(SHAREDLIBRARY) $(EXECUTABLES) $(OBJS) miniupnpcstrings.h # clean python stuff - $(RM) pythonmodule pythonmodule3 validateminixml validateminiwget + $(RM) pythonmodule pythonmodule3 + $(RM) validateminixml validateminiwget validateupnpreplyparse $(RM) -r build/ dist/ #python setup.py clean # clean jnaerator stuff diff --git a/miniupnpc/connecthostport.c b/miniupnpc/connecthostport.c index aabc7a6..98f7253 100644 --- a/miniupnpc/connecthostport.c +++ b/miniupnpc/connecthostport.c @@ -1,7 +1,7 @@ -/* $Id: connecthostport.c,v 1.11 2013/08/01 21:21:25 nanard Exp $ */ +/* $Id: connecthostport.c,v 1.12 2014/02/05 17:26:46 nanard Exp $ */ /* Project : miniupnp * Author : Thomas Bernard - * Copyright (c) 2010-2013 Thomas Bernard + * Copyright (c) 2010-2014 Thomas Bernard * This software is subject to the conditions detailed in the * LICENCE file provided in this distribution. */ @@ -109,7 +109,10 @@ int connecthostport(const char * host, unsigned short port, dest.sin_port = htons(port); n = connect(s, (struct sockaddr *)&dest, sizeof(struct sockaddr_in)); #ifdef MINIUPNPC_IGNORE_EINTR - while(n < 0 && errno == EINTR) + /* EINTR The system call was interrupted by a signal that was caught + * EINPROGRESS The socket is nonblocking and the connection cannot + * be completed immediately. */ + while(n < 0 && (errno == EINTR || errno = EINPROGRESS)) { socklen_t len; fd_set wset; @@ -203,7 +206,10 @@ int connecthostport(const char * host, unsigned short port, #endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */ n = connect(s, p->ai_addr, p->ai_addrlen); #ifdef MINIUPNPC_IGNORE_EINTR - while(n < 0 && errno == EINTR) + /* EINTR The system call was interrupted by a signal that was caught + * EINPROGRESS The socket is nonblocking and the connection cannot + * be completed immediately. */ + while(n < 0 && (errno == EINTR || errno == EINPROGRESS)) { socklen_t len; fd_set wset; diff --git a/miniupnpc/declspec.h b/miniupnpc/declspec.h index f66b6d7..7729969 100644 --- a/miniupnpc/declspec.h +++ b/miniupnpc/declspec.h @@ -2,13 +2,19 @@ #define DECLSPEC_H_INCLUDED #if defined(_WIN32) && !defined(STATICLIB) + /* for windows dll */ #ifdef MINIUPNP_EXPORTS #define LIBSPEC __declspec(dllexport) #else #define LIBSPEC __declspec(dllimport) #endif #else - #define LIBSPEC + #if defined(__GNUC__) && __GNUC__ >= 4 + /* fix dynlib for OS X 10.9.2 and Apple LLVM version 5.0 */ + #define LIBSPEC __attribute__ ((visibility ("default"))) + #else + #define LIBSPEC + #endif #endif #endif diff --git a/miniupnpc/minihttptestserver.c b/miniupnpc/minihttptestserver.c index b719361..fb77965 100644 --- a/miniupnpc/minihttptestserver.c +++ b/miniupnpc/minihttptestserver.c @@ -1,7 +1,7 @@ -/* $Id: minihttptestserver.c,v 1.13 2012/05/29 13:03:07 nanard Exp $ */ +/* $Id: minihttptestserver.c,v 1.16 2014/04/01 15:08:28 nanard Exp $ */ /* Project : miniUPnP * Author : Thomas Bernard - * Copyright (c) 2011-2012 Thomas Bernard + * Copyright (c) 2011-2014 Thomas Bernard * This software is subject to the conditions detailed in the * LICENCE file provided in this distribution. * */ @@ -28,20 +28,20 @@ volatile sig_atomic_t child_to_wait_for = 0; */ void handle_signal_chld(int sig) { - printf("handle_signal_chld(%d)\n", sig); + (void)sig; + /* printf("handle_signal_chld(%d)\n", sig); */ ++child_to_wait_for; } /** * signal handler for SIGINT (CRTL C) */ -#if 0 void handle_signal_int(int sig) { - printf("handle_signal_int(%d)\n", sig); + (void)sig; + /* printf("handle_signal_int(%d)\n", sig); */ quit = 1; } -#endif /** * build a text/plain content of the specified length @@ -94,7 +94,8 @@ void build_crap(char * p, int n) * build chunked response. * return a malloc'ed buffer */ -char * build_chunked_response(int content_length, int * response_len) { +char * build_chunked_response(int content_length, int * response_len) +{ char * response_buffer; char * content_buffer; int buffer_length; @@ -141,7 +142,115 @@ char * build_chunked_response(int content_length, int * response_len) { return response_buffer; } -enum modes { MODE_INVALID, MODE_CHUNKED, MODE_ADDCRAP, MODE_NORMAL }; +/* favicon.ico generator */ +#ifdef OLD_HEADER +#define FAVICON_LENGTH (6 + 16 + 12 + 8 + 32 * 4) +#else +#define FAVICON_LENGTH (6 + 16 + 40 + 8 + 32 * 4) +#endif +void build_favicon_content(char * p, int n) +{ + int i; + if(n < FAVICON_LENGTH) + return; + /* header : 6 bytes */ + *p++ = 0; + *p++ = 0; + *p++ = 1; /* type : ICO */ + *p++ = 0; + *p++ = 1; /* number of images in file */ + *p++ = 0; + /* image directory (1 entry) : 16 bytes */ + *p++ = 16; /* width */ + *p++ = 16; /* height */ + *p++ = 2; /* number of colors in the palette. 0 = no palette */ + *p++ = 0; /* reserved */ + *p++ = 1; /* color planes */ + *p++ = 0; /* " */ + *p++ = 1; /* bpp */ + *p++ = 0; /* " */ +#ifdef OLD_HEADER + *p++ = 12 + 8 + 32 * 4; /* bmp size */ +#else + *p++ = 40 + 8 + 32 * 4; /* bmp size */ +#endif + *p++ = 0; /* " */ + *p++ = 0; /* " */ + *p++ = 0; /* " */ + *p++ = 6 + 16; /* bmp offset */ + *p++ = 0; /* " */ + *p++ = 0; /* " */ + *p++ = 0; /* " */ + /* BMP */ +#ifdef OLD_HEADER + /* BITMAPCOREHEADER */ + *p++ = 12; /* size of this header */ + *p++ = 0; /* " */ + *p++ = 0; /* " */ + *p++ = 0; /* " */ + *p++ = 16; /* width */ + *p++ = 0; /* " */ + *p++ = 16 * 2; /* height x 2 ! */ + *p++ = 0; /* " */ + *p++ = 1; /* color planes */ + *p++ = 0; /* " */ + *p++ = 1; /* bpp */ + *p++ = 0; /* " */ +#else + /* BITMAPINFOHEADER */ + *p++ = 40; /* size of this header */ + *p++ = 0; /* " */ + *p++ = 0; /* " */ + *p++ = 0; /* " */ + *p++ = 16; /* width */ + *p++ = 0; /* " */ + *p++ = 0; /* " */ + *p++ = 0; /* " */ + *p++ = 16 * 2; /* height x 2 ! */ + *p++ = 0; /* " */ + *p++ = 0; /* " */ + *p++ = 0; /* " */ + *p++ = 1; /* color planes */ + *p++ = 0; /* " */ + *p++ = 1; /* bpp */ + *p++ = 0; /* " */ + /* compression method, image size, ppm x, ppm y */ + /* colors in the palette ? */ + /* important colors */ + for(i = 4 * 6; i > 0; --i) + *p++ = 0; +#endif + /* palette */ + *p++ = 0; /* b */ + *p++ = 0; /* g */ + *p++ = 0; /* r */ + *p++ = 0; /* reserved */ + *p++ = 255; /* b */ + *p++ = 255; /* g */ + *p++ = 255; /* r */ + *p++ = 0; /* reserved */ + /* pixel data */ + for(i = 16; i > 0; --i) { + if(i & 1) { + *p++ = 0125; + *p++ = 0125; + } else { + *p++ = 0252; + *p++ = 0252; + } + *p++ = 0; + *p++ = 0; + } + /* Opacity MASK */ + for(i = 16 * 4; i > 0; --i) { + *p++ = 0; + } +} + +enum modes { + MODE_INVALID, MODE_CHUNKED, MODE_ADDCRAP, MODE_NORMAL, MODE_FAVICON +}; + const struct { const enum modes mode; const char * text; @@ -149,6 +258,7 @@ const struct { {MODE_CHUNKED, "chunked"}, {MODE_ADDCRAP, "addcrap"}, {MODE_NORMAL, "normal"}, + {MODE_FAVICON, "favicon.ico"}, {MODE_INVALID, NULL} }; @@ -201,6 +311,8 @@ void handle_http_connection(int c) request_buffer + request_len, sizeof(request_buffer) - request_len); if(n < 0) { + if(errno == EINTR) + continue; perror("read"); return; } else if(n==0) { @@ -219,6 +331,7 @@ void handle_http_connection(int c) } if(!headers_found) { /* error */ + printf("no HTTP header found in the request\n"); return; } printf("headers :\n%.*s", request_len, request_buffer); @@ -292,6 +405,8 @@ void handle_http_connection(int c) case MODE_ADDCRAP: response_len = content_length+256; response_buffer = malloc(response_len); + if(!response_buffer) + break; n = snprintf(response_buffer, response_len, "HTTP/1.1 200 OK\r\n" "Server: minihttptestserver\r\n" @@ -303,9 +418,27 @@ void handle_http_connection(int c) build_content(response_buffer + n, content_length); build_crap(response_buffer + n + content_length, CRAP_LENGTH); break; + case MODE_FAVICON: + content_length = FAVICON_LENGTH; + response_len = content_length + 256; + response_buffer = malloc(response_len); + if(!response_buffer) + break; + n = snprintf(response_buffer, response_len, + "HTTP/1.1 200 OK\r\n" + "Server: minihttptestserver\r\n" + "Content-Type: image/vnd.microsoft.icon\r\n" + "Content-Length: %d\r\n" + "\r\n", content_length); + /* image/x-icon */ + build_favicon_content(response_buffer + n, content_length); + response_len = content_length + n; + break; default: response_len = content_length+256; response_buffer = malloc(response_len); + if(!response_buffer) + break; n = snprintf(response_buffer, response_len, "HTTP/1.1 200 OK\r\n" "Server: minihttptestserver\r\n" @@ -338,6 +471,7 @@ int main(int argc, char * * argv) { int child = 0; int status; const char * expected_file_name = NULL; + struct sigaction sa; for(i = 1; i < argc; i++) { if(argv[i][0] == '-') { @@ -364,10 +498,21 @@ int main(int argc, char * * argv) { } srand(time(NULL)); - signal(SIGCHLD, handle_signal_chld); -#if 0 - signal(SIGINT, handle_signal_int); -#endif + + memset(&sa, 0, sizeof(struct sigaction)); + + /*signal(SIGCHLD, handle_signal_chld);*/ + sa.sa_handler = handle_signal_chld; + if(sigaction(SIGCHLD, &sa, NULL) < 0) { + perror("sigaction"); + return 1; + } + /*signal(SIGINT, handle_signal_int);*/ + sa.sa_handler = handle_signal_int; + if(sigaction(SIGINT, &sa, NULL) < 0) { + perror("sigaction"); + return 1; + } s = socket(ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0); if(s < 0) { @@ -442,12 +587,12 @@ int main(int argc, char * * argv) { } --child_to_wait_for; } - /* TODO : add a select() call in order to handle the case - * when a signal is caught */ client_addrlen = sizeof(struct sockaddr_storage); c = accept(s, (struct sockaddr *)&client_addr, &client_addrlen); if(c < 0) { + if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) + continue; perror("accept"); return 1; } diff --git a/miniupnpc/minissdpc.c b/miniupnpc/minissdpc.c index a585ab9..22f94a6 100644 --- a/miniupnpc/minissdpc.c +++ b/miniupnpc/minissdpc.c @@ -46,7 +46,7 @@ getDevicesFromMiniSSDPD(const char * devtype, const char * socketpath) { struct UPNPDev * tmp; struct UPNPDev * devlist = NULL; - unsigned char buffer[2048]; + unsigned char buffer[4*1024]; /* is that enough ? */ ssize_t n; unsigned char * p; unsigned char * url; diff --git a/miniupnpc/miniupnpc.c b/miniupnpc/miniupnpc.c index 9c61be1..3eca05a 100644 --- a/miniupnpc/miniupnpc.c +++ b/miniupnpc/miniupnpc.c @@ -1,4 +1,4 @@ -/* $Id: miniupnpc.c,v 1.113 2013/10/07 10:04:56 nanard Exp $ */ +/* $Id: miniupnpc.c,v 1.116 2014/01/31 14:09:03 nanard Exp $ */ /* Project : miniupnp * Web : http://miniupnp.free.fr/ * Author : Thomas BERNARD @@ -533,7 +533,8 @@ upnpDiscoverDevices(const char * const deviceTypes[], } } - /* Avant d'envoyer le paquet on bind pour recevoir la reponse */ + /* Before sending the packed, we first "bind" in order to be able + * to receive the response */ if (bind(sudp, (const struct sockaddr *)&sockudp_r, ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) != 0) { diff --git a/miniupnpc/miniwget.c b/miniupnpc/miniwget.c index a75f55b..813db93 100644 --- a/miniupnpc/miniwget.c +++ b/miniupnpc/miniwget.c @@ -1,8 +1,8 @@ -/* $Id: miniwget.c,v 1.60 2013/10/07 10:03:16 nanard Exp $ */ +/* $Id: miniwget.c,v 1.61 2014/02/05 17:27:48 nanard Exp $ */ /* Project : miniupnp * Website : http://miniupnp.free.fr/ * Author : Thomas Bernard - * Copyright (c) 2005-2013 Thomas Bernard + * Copyright (c) 2005-2014 Thomas Bernard * This software is subject to the conditions detailed in the * LICENCE file provided in this distribution. */ @@ -39,9 +39,6 @@ #include #include #define closesocket close -/* defining MINIUPNPC_IGNORE_EINTR enable the ignore of interruptions - * during the connect() call */ -#define MINIUPNPC_IGNORE_EINTR #endif /* #else _WIN32 */ #if defined(__sun) || defined(sun) #define MIN(x,y) (((x)<(y))?(x):(y)) diff --git a/miniupnpc/minixml.c b/miniupnpc/minixml.c index d3f7d06..1f22273 100644 --- a/miniupnpc/minixml.c +++ b/miniupnpc/minixml.c @@ -1,10 +1,10 @@ -/* $Id: minixml.c,v 1.9 2011/02/07 13:44:57 nanard Exp $ */ +/* $Id: minixml.c,v 1.10 2012/03/05 19:42:47 nanard Exp $ */ /* minixml.c : the minimum size a xml parser can be ! */ /* Project : miniupnp * webpage: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * Author : Thomas Bernard -Copyright (c) 2005-2011, Thomas BERNARD +Copyright (c) 2005-2014, Thomas BERNARD All rights reserved. Redistribution and use in source and binary forms, with or without @@ -113,7 +113,20 @@ static void parseelt(struct xmlparser * p) const char * elementname; while(p->xml < (p->xmlend - 1)) { - if((p->xml)[0]=='<' && (p->xml)[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) diff --git a/miniupnpc/upnpc.c b/miniupnpc/upnpc.c index 9c9979a..70c9a54 100644 --- a/miniupnpc/upnpc.c +++ b/miniupnpc/upnpc.c @@ -1,7 +1,7 @@ -/* $Id: upnpc.c,v 1.101 2014/01/31 13:18:25 nanard Exp $ */ +/* $Id: upnpc.c,v 1.102 2014/02/05 17:27:14 nanard Exp $ */ /* Project : miniupnp * Author : Thomas Bernard - * Copyright (c) 2005-2013 Thomas Bernard + * Copyright (c) 2005-2014 Thomas Bernard * This software is subject to the conditions detailed in the * LICENCE file provided in this distribution. */ @@ -500,7 +500,7 @@ int main(int argc, char ** argv) return -1; } #endif - printf("upnpc : miniupnpc library test client. (c) 2005-2013 Thomas Bernard\n"); + printf("upnpc : miniupnpc library test client. (c) 2005-2014 Thomas Bernard\n"); printf("Go to http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/\n" "for more information.\n"); /* command line processing */ diff --git a/miniupnpc/upnpcommands.h b/miniupnpc/upnpcommands.h index dc3f23f..93d9f3d 100644 --- a/miniupnpc/upnpcommands.h +++ b/miniupnpc/upnpcommands.h @@ -1,7 +1,7 @@ -/* $Id: upnpcommands.h,v 1.26 2014/01/31 13:18:26 nanard Exp $ */ +/* $Id: upnpcommands.h,v 1.27 2014/02/17 15:38:26 nanard Exp $ */ /* Miniupnp project : http://miniupnp.free.fr/ * Author : Thomas Bernard - * Copyright (c) 2005-2011 Thomas Bernard + * Copyright (c) 2005-2014 Thomas Bernard * This software is subject to the conditions detailed in the * LICENCE file provided within this distribution */ #ifndef UPNPCOMMANDS_H_INCLUDED @@ -100,6 +100,8 @@ UPNP_GetLinkLayerMaxBitRates(const char* controlURL, * errorCode errorDescription (short) - Description (long) * 402 Invalid Args - See UPnP Device Architecture section on Control. * 501 Action Failed - See UPnP Device Architecture section on Control. + * 606 Action not authorized - The action requested REQUIRES authorization and + * the sender was not authorized. * 715 WildCardNotPermittedInSrcIP - The source IP address cannot be * wild-carded * 716 WildCardNotPermittedInExtPort - The external port cannot be wild-carded @@ -112,7 +114,13 @@ UPNP_GetLinkLayerMaxBitRates(const char* controlURL, * 726 RemoteHostOnlySupportsWildcard - RemoteHost must be a wildcard * and cannot be a specific IP address or DNS name * 727 ExternalPortOnlySupportsWildcard - ExternalPort must be a wildcard and - * cannot be a specific port value */ + * cannot be a specific port value + * 728 NoPortMapsAvailable - There are not enough free ports available to + * complete port mapping. + * 729 ConflictWithOtherMechanisms - Attempted port mapping is not allowed + * due to conflict with other mechanisms. + * 732 WildCardNotPermittedInIntPort - The internal port cannot be wild-carded + */ LIBSPEC int UPNP_AddPortMapping(const char * controlURL, const char * servicetype, const char * extPort, @@ -132,6 +140,8 @@ UPNP_AddPortMapping(const char * controlURL, const char * servicetype, * * List of possible UPnP errors for DeletePortMapping : * 402 Invalid Args - See UPnP Device Architecture section on Control. + * 606 Action not authorized - The action requested REQUIRES authorization + * and the sender was not authorized. * 714 NoSuchEntryInArray - The specified value does not exist in the array */ LIBSPEC int UPNP_DeletePortMapping(const char * controlURL, const char * servicetype, @@ -159,7 +169,15 @@ UPNP_GetPortMappingNumberOfEntries(const char* controlURL, * * return value : * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR - * or a UPnP Error Code. */ + * or a UPnP Error Code. + * + * List of possible UPnP errors for _GetSpecificPortMappingEntry : + * 402 Invalid Args - See UPnP Device Architecture section on Control. + * 501 Action Failed - See UPnP Device Architecture section on Control. + * 606 Action not authorized - The action requested REQUIRES authorization + * and the sender was not authorized. + * 714 NoSuchEntryInArray - The specified value does not exist in the array. + */ LIBSPEC int UPNP_GetSpecificPortMappingEntry(const char * controlURL, const char * servicetype, @@ -190,6 +208,8 @@ UPNP_GetSpecificPortMappingEntry(const char * controlURL, * * Possible UPNP Error codes : * 402 Invalid Args - See UPnP Device Architecture section on Control. + * 606 Action not authorized - The action requested REQUIRES authorization + * and the sender was not authorized. * 713 SpecifiedArrayIndexInvalid - The specified array index is out of bounds */ LIBSPEC int diff --git a/miniupnpd/.gitignore b/miniupnpd/.gitignore index f97b1ce..dd37154 100644 --- a/miniupnpd/.gitignore +++ b/miniupnpd/.gitignore @@ -9,6 +9,8 @@ testgetifstats testupnpdescgen testupnppermissions testgetroute +testasyncsendto +testportinuse netfilter/testiptcrdr netfilter/testiptcrdr_dscp netfilter/testiptcrdr_peer diff --git a/miniupnpd/Changelog.txt b/miniupnpd/Changelog.txt index f2761ea..1e65718 100644 --- a/miniupnpd/Changelog.txt +++ b/miniupnpd/Changelog.txt @@ -1,4 +1,53 @@ -$Id: Changelog.txt,v 1.346 2013/12/13 13:41:52 nanard Exp $ +$Id: Changelog.txt,v 1.366 2014/03/24 11:03:50 nanard Exp $ + +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 @@ -136,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 4f085fd..d24806b 100644 --- a/miniupnpd/Makefile +++ b/miniupnpd/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.74 2013/12/13 15:28:34 nanard Exp $ +# $Id: Makefile,v 1.80 2014/04/07 10:32:20 nanard Exp $ # MiniUPnP project # http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ # Author: Thomas Bernard @@ -17,7 +17,7 @@ CFLAGS ?= -pipe -Os #CFLAGS = -pipe -O -g -DDEBUG -CFLAGS += -ansi +#CFLAGS += -ansi CFLAGS += -Wall CFLAGS += -W CFLAGS += -Wstrict-prototypes @@ -39,14 +39,14 @@ FWNAME = pf .else FWNAME = ipf .endif -.endif # better way to find if we are using ipf or pf -.if defined(/etc/rc.subr) && defined(/etc/rc.conf) +.if 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"; else echo "pf"; fi + echo "ipf"; elif checkyesno pf_enable; then \ + echo "pf"; else echo "ipfw"; fi .endif .if $(OSNAME) == "NetBSD" @@ -57,13 +57,18 @@ FWNAME != . /etc/rc.subr; . /etc/rc.conf; \ .if $(OSNAME) == "DragonFly" FWNAME != . /etc/rc.subr; . /etc/rc.conf; \ - if chechyesno ipfilter; then \ - echo "ipf"; else echo "pf"; fi + if checkyesno ipfilter; then \ + echo "ipf"; elif checkyesno pf_enable; then \ + echo "pf"; else echo "ipfw"; fi .endif .endif .if $(OSNAME) == "Darwin" -FWNAME = ipfw +# 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 @@ -80,7 +85,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 + 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 @@ -116,10 +121,14 @@ 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 + testgetifaddr testgetroute testasyncsendto \ + testportinuse + .if $(OSNAME) == "Darwin" LIBS = .else @@ -142,13 +151,14 @@ clean: $(RM) $(STDOBJS) $(BSDOBJS) $(SUNOSOBJS) $(MACOBJS) $(EXECUTABLES) \ testupnpdescgen.o \ $(MISCOBJS) config.h testgetifstats.o testupnppermissions.o \ - miniupnpdctl.o testgetifaddr.o testgetroute.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 555 miniupnpd $(DESTDIR)$(INSTALLBINDIR) + $(INSTALL) -m 755 miniupnpd $(DESTDIR)$(INSTALLBINDIR) $(INSTALL) -d $(DESTDIR)$(INSTALLETCDIR) $(INSTALL) -b miniupnpd.conf $(DESTDIR)$(INSTALLETCDIR) # TODO : install man page correctly @@ -173,7 +183,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) @@ -185,19 +195,25 @@ miniupnpdctl: config.h $(MINIUPNPDCTLOBJS) $(CC) $(CFLAGS) -o $@ $(MINIUPNPDCTLOBJS) testupnpdescgen: config.h $(TESTUPNPDESCGENOBJS) - $(CC) $(CFLAGS) -o $@ $(TESTUPNPDESCGENOBJS) + $(CC) $(CFLAGS) -o $@ $(TESTUPNPDESCGENOBJS) $(LIBS) testgetifstats: config.h $(TESTGETIFSTATSOBJS) $(CC) $(CFLAGS) -o $@ $(TESTGETIFSTATSOBJS) $(LIBS) testgetifaddr: config.h $(TESTGETIFADDROBJS) - $(CC) $(CFLAGS) -o $@ $(TESTGETIFADDROBJS) + $(CC) $(CFLAGS) -o $@ $(TESTGETIFADDROBJS) $(LIBS) testupnppermissions: config.h $(TESTUPNPPERMISSIONSOBJS) - $(CC) $(CFLAGS) -o $@ $(TESTUPNPPERMISSIONSOBJS) + $(CC) $(CFLAGS) -o $@ $(TESTUPNPPERMISSIONSOBJS) $(LIBS) testgetroute: config.h $(TESTGETROUTEOBJS) - $(CC) $(CFLAGS) -o $@ $(TESTGETROUTEOBJS) + $(CC) $(CFLAGS) -o $@ $(TESTGETROUTEOBJS) $(LIBS) + +testasyncsendto: config.h $(TESTASYNCSENDTOOBJS) + $(CC) $(CFLAGS) -o $@ $(TESTASYNCSENDTOOBJS) + +testportinuse: config.h $(TESTPORTINUSEOBJS) + $(CC) $(CFLAGS) -o $@ $(TESTPORTINUSEOBJS) -lkvm # gmake : # $(CC) $(CFLAGS) -o $@ $^ diff --git a/miniupnpd/Makefile.linux b/miniupnpd/Makefile.linux index a9bab57..d1f7f3b 100644 --- a/miniupnpd/Makefile.linux +++ b/miniupnpd/Makefile.linux @@ -1,4 +1,4 @@ -# $Id: Makefile.linux,v 1.81 2014/01/27 10:06:58 nanard Exp $ +# $Id: Makefile.linux,v 1.84 2014/03/24 10:43:25 nanard Exp $ # MiniUPnP project # (c) 2006-2014 Thomas Bernard # http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ @@ -43,11 +43,11 @@ 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 \ - upnppinhole.o pcplearndscp.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 @@ -150,7 +150,7 @@ TESTUPNPDESCGENOBJS = testupnpdescgen.o upnpdescgen.o EXECUTABLES = miniupnpd testupnpdescgen testgetifstats \ testupnppermissions miniupnpdctl testgetifaddr \ - testgetroute + testgetroute testasyncsendto testportinuse .PHONY: all clean install depend genuuid @@ -161,7 +161,7 @@ clean: $(RM) $(EXECUTABLES) $(RM) testupnpdescgen.o testgetifstats.o $(RM) testupnppermissions.o testgetifaddr.o - $(RM) testgetroute.o + $(RM) testgetroute.o testasyncsendto.o $(RM) miniupnpdctl.o install: miniupnpd miniupnpd.8 miniupnpd.conf genuuid \ @@ -205,6 +205,12 @@ testgetifaddr: testgetifaddr.o getifaddr.o 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 @@ -214,15 +220,16 @@ depend: config.h makedepend -f$(MAKEFILE_LIST) -Y \ $(ALLOBJS:.o=.c) $(TESTUPNPDESCGENOBJS:.o=.c) \ testgetifstats.c testupnppermissions.c testgetifaddr.c \ - testgetroute.c miniupnpdctl.c 2>/dev/null + 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 natpmp.h pcpserver.h -miniupnpd.o: commonrdr.h upnputils.h ifacewatcher.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 @@ -234,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 @@ -246,12 +256,14 @@ options.o: miniupnpdtypes.h upnppermissions.o: config.h upnppermissions.h minissdp.o: config.h upnpdescstrings.h miniupnpdpath.h upnphttp.h minissdp.o: upnpglobalvars.h upnppermissions.h miniupnpdtypes.h minissdp.h -minissdp.o: upnputils.h getroute.h codelength.h +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 pcp_msg_struct.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 @@ -262,6 +274,7 @@ 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 @@ -279,7 +292,9 @@ upnpdescgen.o: miniupnpdpath.h upnpglobalvars.h upnppermissions.h upnpdescgen.o: miniupnpdtypes.h upnpdescstrings.h upnpurns.h getconnstatus.h testgetifstats.o: getifstats.h testupnppermissions.o: upnppermissions.h config.h -testgetifaddr.o: getifaddr.h +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 7520124..7d821ff 100644 --- a/miniupnpd/Makefile.macosx +++ b/miniupnpd/Makefile.macosx @@ -32,7 +32,8 @@ 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 + upnpevents.o getconnstatus.o upnputils.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 @@ -50,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 = @@ -115,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/asyncsendto.c b/miniupnpd/asyncsendto.c new file mode 100644 index 0000000..a50718b --- /dev/null +++ b/miniupnpd/asyncsendto.c @@ -0,0 +1,281 @@ +/* $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 */ + +#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; + 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); + */ + +/* delay = milli seconds */ +ssize_t +sendto_schedule(int sockfd, const void *buf, size_t len, int flags, + const struct sockaddr *dest_addr, socklen_t addrlen, + unsigned int delay) +{ + enum {ESCHEDULED, EWAITREADY, ESENDNOW} state; + ssize_t n; + struct timeval tv; + struct scheduled_send * elt; + + if(delay == 0) { + /* first try to send at once */ + n = sendto(sockfd, buf, len, flags, dest_addr, addrlen); + 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 */ + elt = malloc(sizeof(struct scheduled_send) + len + addrlen); + if(elt == NULL) { + syslog(LOG_ERR, "malloc failed to allocate %u bytes", + (unsigned)(sizeof(struct scheduled_send) + len + addrlen)); + 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; + memcpy(elt->data + addrlen, buf, len); + elt->buf = (void *)(elt->data + addrlen); + elt->len = len; + /* insert */ + LIST_INSERT_HEAD( &send_list, elt, entries); + return 0; +} + + +/* try to send at once, and queue the packet if needed */ +ssize_t +sendto_or_schedule(int sockfd, const void *buf, size_t len, int flags, + const struct sockaddr *dest_addr, socklen_t addrlen) +{ + return sendto_schedule(sockfd, buf, len, flags, dest_addr, addrlen, 0); +} + +/* 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))) { + syslog(LOG_DEBUG, "%s: %d bytes on socket %d", + "try_sendto", (int)elt->len, elt->sockfd); + n = sendto(elt->sockfd, elt->buf, elt->len, elt->flags, + elt->dest_addr, elt->addrlen); + 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 = 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; + } + } + } +} + diff --git a/miniupnpd/asyncsendto.h b/miniupnpd/asyncsendto.h new file mode 100644 index 0000000..60c04de --- /dev/null +++ b/miniupnpd/asyncsendto.h @@ -0,0 +1,38 @@ +/* $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 ASYNCSENDTO_H_INCLUDED +#define ASYNCSENDTO_H_INCLUDED + +/* sendto_schedule() : see sendto(2) + * schedule sendto() call after delay (milliseconds) */ +ssize_t +sendto_schedule(int sockfd, const void *buf, size_t len, int flags, + const struct sockaddr *dest_addr, socklen_t addrlen, + unsigned int delay); + +/* sendto_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); + +/* 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 diff --git a/miniupnpd/bsd/getifstats.c b/miniupnpd/bsd/getifstats.c index 69b8eeb..7238a46 100644 --- a/miniupnpd/bsd/getifstats.c +++ b/miniupnpd/bsd/getifstats.c @@ -12,6 +12,9 @@ #include #include #if defined(__FreeBSD__) || defined(__DragonFly__) +#ifdef __DragonFly__ +#define _KERNEL_STRUCTURES +#endif #include #endif #if defined(__DragonFly__) diff --git a/miniupnpd/bsd/getroute.c b/miniupnpd/bsd/getroute.c index e72f716..e9eda1d 100644 --- a/miniupnpd/bsd/getroute.c +++ b/miniupnpd/bsd/getroute.c @@ -1,4 +1,4 @@ -/* $Id: getroute.c,v 1.3 2013/02/06 13:11:45 nanard Exp $ */ +/* $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 @@ -83,7 +83,7 @@ get_src_for_route_to(const struct sockaddr * dst, sa = (struct sockaddr *)p; sockaddr_to_string(sa, tmp, sizeof(tmp)); syslog(LOG_DEBUG, "type=%d sa_len=%d sa_family=%d %s", - i, sa->sa_len, sa->sa_family, tmp); + i, SA_LEN(sa), sa->sa_family, tmp); if((i == RTA_DST || i == RTA_GATEWAY) && (src_len && src)) { size_t len = 0; @@ -113,7 +113,7 @@ get_src_for_route_to(const struct sockaddr * dst, *index = sdl->sdl_index; } #endif - p += sa->sa_len; + p += SA_LEN(sa); } } } diff --git a/miniupnpd/bsd/ifacewatcher.c b/miniupnpd/bsd/ifacewatcher.c index 67d64f4..0756f10 100644 --- a/miniupnpd/bsd/ifacewatcher.c +++ b/miniupnpd/bsd/ifacewatcher.c @@ -1,4 +1,4 @@ -/* $Id: ifacewatcher.c,v 1.5 2012/05/21 08:55:10 nanard Exp $ */ +/* $Id: ifacewatcher.c,v 1.6 2014/03/31 12:27:14 nanard Exp $ */ /* Project MiniUPnP * web : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2011 Thomas BERNARD @@ -15,12 +15,8 @@ #include #include -#if !defined(SA_LEN) -#define SA_LEN(sa) (sa)->sa_len -#endif - #define SALIGN (sizeof(long) - 1) -#define SA_RLEN(sa) ((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : (SALIGN + 1)) +#define SA_RLEN(sa) (SA_LEN(sa) ? ((SA_LEN(sa) + SALIGN) & ~SALIGN) : (SALIGN + 1)) #include "../upnputils.h" #include "../upnpglobalvars.h" diff --git a/miniupnpd/commonrdr.h b/miniupnpd/commonrdr.h index dd5cf62..7c0bd7c 100644 --- a/miniupnpd/commonrdr.h +++ b/miniupnpd/commonrdr.h @@ -1,6 +1,6 @@ -/* $Id: commonrdr.h,v 1.7 2011/06/22 20:34:39 nanard Exp $ */ +/* $Id: commonrdr.h,v 1.9 2014/02/11 09:36:15 nanard Exp $ */ /* MiniUPnP project - * (c) 2006-2011 Thomas Bernard + * (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 */ @@ -10,6 +10,9 @@ #include "config.h" /* init and shutdown functions */ +/* init_redirect() return values : + * 0 : OK + * -1 : error */ int init_redirect(void); @@ -17,7 +20,10 @@ void shutdown_redirect(void); /* get_redirect_rule() gets internal IP and port from - * interface, external port and protocl + * 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, @@ -27,6 +33,10 @@ get_redirect_rule(const char * ifname, unsigned short eport, int proto, 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, diff --git a/miniupnpd/genconfig.sh b/miniupnpd/genconfig.sh index 2bf59db..1089876 100755 --- a/miniupnpd/genconfig.sh +++ b/miniupnpd/genconfig.sh @@ -1,8 +1,8 @@ #! /bin/sh -# $Id: genconfig.sh,v 1.65 2013/12/13 14:07:08 nanard Exp $ +# $Id: genconfig.sh,v 1.72 2014/03/10 10:17:17 nanard Exp $ # miniupnp daemon # http://miniupnp.free.fr or http://miniupnp.tuxfamily.org/ -# (c) 2006-2013 Thomas Bernard +# (c) 2006-2014 Thomas Bernard # This software is subject to the conditions detailed in the # LICENCE file provided within the distribution @@ -13,11 +13,8 @@ case "$argv" in --strict) STRICT=1 ;; --leasefile) LEASEFILE=1 ;; --vendorcfg) VENDORCFG=1 ;; - --pcp) PCP=1 ;; - --pcp-peer) - PCP=1 - PCP_PEER=1 - ;; + --pcp-peer) PCP_PEER=1 ;; + --portinuse) PORTINUSE=1 ;; --help|-h) echo "Usage : $0 [options]" echo " --ipv6 enable IPv6" @@ -25,8 +22,8 @@ case "$argv" in echo " --strict be more strict regarding compliance with UPnP specifications" echo " --leasefile enable lease file" echo " --vendorcfg enable configuration of manufacturer info" - echo " --pcp enable PCP" echo " --pcp-peer enable PCP PEER operation" + echo " --portinuse enable port in use check" exit 1 ;; *) @@ -83,7 +80,7 @@ ${RM} ${CONFIGFILE} echo "/* MiniUPnP Project" >> ${CONFIGFILE} echo " * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/" >> ${CONFIGFILE} -echo " * (c) 2006-2013 Thomas Bernard" >> ${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} @@ -120,6 +117,7 @@ case $OS_NAME in 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}'` @@ -150,12 +148,14 @@ case $OS_NAME in 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 @@ -313,6 +313,11 @@ case $FW in ;; 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." @@ -341,15 +346,9 @@ echo "/* Comment the following line to disable NAT-PMP operations */" >> ${CONFI echo "#define ENABLE_NATPMP" >> ${CONFIGFILE} echo "" >> ${CONFIGFILE} -if [ -n "$PCP" ]; then echo "/* Comment the following line to disable PCP operations */" >> ${CONFIGFILE} echo "#define ENABLE_PCP" >> ${CONFIGFILE} echo "" >> ${CONFIGFILE} -else -echo "/* Uncomment the following line to enable PCP operations */" >> ${CONFIGFILE} -echo "/*#define ENABLE_PCP*/" >> ${CONFIGFILE} -echo "" >> ${CONFIGFILE} -fi echo "#ifdef ENABLE_PCP" >> ${CONFIGFILE} if [ -n "$PCP_PEER" ]; then @@ -398,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} @@ -414,6 +421,15 @@ else 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} @@ -457,6 +473,12 @@ else 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} @@ -465,6 +487,14 @@ else 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} @@ -477,7 +507,7 @@ else fi echo "" >> ${CONFIGFILE} -echo "#endif" >> ${CONFIGFILE} +echo "#endif /* ${CONFIGMACRO} */" >> ${CONFIGFILE} ${MV} ${CONFIGFILE} ${CONFIGFILE_FINAL} diff --git a/miniupnpd/getifaddr.c b/miniupnpd/getifaddr.c index 1430336..f9e2c6d 100644 --- a/miniupnpd/getifaddr.c +++ b/miniupnpd/getifaddr.c @@ -1,7 +1,7 @@ /* $Id: getifaddr.c,v 1.19 2013/12/13 14:28:40 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * (c) 2006-2013 Thomas Bernard + * (c) 2006-2014 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ @@ -47,6 +47,19 @@ getifaddr(const char * ifname, char * buf, int len, 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"); @@ -55,11 +68,14 @@ getifaddr(const char * ifname, char * buf, int len, } ifaddr = (struct sockaddr_in *)&ifr.ifr_addr; if(addr) *addr = ifaddr->sin_addr; - if(!inet_ntop(AF_INET, &ifaddr->sin_addr, buf, len)) + if(buf) { - syslog(LOG_ERR, "inet_ntop(): %m"); - close(s); - return -1; + if(!inet_ntop(AF_INET, &ifaddr->sin_addr, buf, len)) + { + syslog(LOG_ERR, "inet_ntop(): %m"); + close(s); + return -1; + } } if(mask) { @@ -99,9 +115,12 @@ getifaddr(const char * ifname, char * buf, int len, switch(ife->ifa_addr->sa_family) { case AF_INET: - inet_ntop(ife->ifa_addr->sa_family, - &((struct sockaddr_in *)ife->ifa_addr)->sin_addr, - buf, len); + if(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; @@ -119,9 +138,17 @@ getifaddr(const char * ifname, char * buf, int len, } #ifdef ENABLE_PCP -int getifaddr_in6(const char * ifname, struct in6_addr * addr){ +/* XXX I don't know if this function should return + * IPv4 or IPv6 if both are enabled... */ +int getifaddr_in6(const char * ifname, struct in6_addr * addr) +{ +#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; @@ -130,9 +157,8 @@ int getifaddr_in6(const char * ifname, struct in6_addr * addr){ syslog(LOG_ERR, "getifaddrs: %m"); return -1; } - for(ife = ifap; ife; ife = ife->ifa_next) + for(ife = ifap; ife && !found; ife = ife->ifa_next) { - int found = 0; /* skip other interfaces if one was specified */ if(ifname && (0 != strcmp(ifname, ife->ifa_name))) continue; @@ -141,38 +167,47 @@ int getifaddr_in6(const char * ifname, struct in6_addr * addr){ switch(ife->ifa_addr->sa_family) { case AF_INET: -#if 0 - addr->s6_addr32[0]=0; - addr->s6_addr32[1]=0; - addr->s6_addr32[2]=htonl(0xffff); - addr->s6_addr32[3]=((struct sockaddr_in *)ife->ifa_addr)->sin_addr.s_addr; -#endif + /* 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); - /*inet_ntop(ife->ifa_addr->sa_family, - &((struct sockaddr_in *)ife->ifa_addr)->sin_addr, - buf, len);*/ + 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: - if(!IN6_IS_ADDR_LOOPBACK(addr) - && !IN6_IS_ADDR_LINKLOCAL(addr)) { - memcpy(addr->s6_addr, &((struct sockaddr_in6 *)ife->ifa_addr)->sin6_addr, 16); + 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; - } - if (found) { - break; +#endif /* ENABLE_IPV6 */ } } freeifaddrs(ifap); + return (found ? 0 : -1); +#else /* defined(ENABLE_IPV6) || defined(USE_GETIFADDRS) */ + /* IPv4 only */ + struct in_addr addr4; + 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 diff --git a/miniupnpd/minissdp.c b/miniupnpd/minissdp.c index a67c94b..3e6534c 100644 --- a/miniupnpd/minissdp.c +++ b/miniupnpd/minissdp.c @@ -1,7 +1,7 @@ -/* $Id: minissdp.c,v 1.54 2013/06/15 12:50:10 nanard Exp $ */ +/* $Id: minissdp.c,v 1.62 2014/03/24 09:31:23 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * (c) 2006-2013 Thomas Bernard + * (c) 2006-2014 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ @@ -24,6 +24,7 @@ #include "minissdp.h" #include "upnputils.h" #include "getroute.h" +#include "asyncsendto.h" #include "codelength.h" /* SSDP ip/port */ @@ -146,7 +147,11 @@ OpenAndConfSSDPReceiveSocket(int ipv6) #ifdef ENABLE_IPV6 if(ipv6) { - AddMulticastMembershipIPv6(s); + if(AddMulticastMembershipIPv6(s) < 0) + { + syslog(LOG_WARNING, + "Failed to add IPv6 multicast membership"); + } } else #endif @@ -276,9 +281,16 @@ OpenAndConfSSDPNotifySockets(int * sockets) goto error; i++; #ifdef ENABLE_IPV6 - sockets[i] = OpenAndConfSSDPNotifySocketIPv6(lan_addr->index); - if(sockets[i] < 0) - goto error; + if(GETFLAG(IPV6DISABLEDMASK)) + { + sockets[i] = -1; + } + else + { + sockets[i] = OpenAndConfSSDPNotifySocketIPv6(lan_addr->index); + if(sockets[i] < 0) + goto error; + } i++; #endif } @@ -319,11 +331,13 @@ EXT: * 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 port, const char * uuidvalue) + const char * host, unsigned short port, const char * uuidvalue, + unsigned int delay) { int l, n; char buf[512]; @@ -388,16 +402,16 @@ SendSSDPResponse(int s, const struct sockaddr * addr, } addrlen = (addr->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); - n = sendto(s, buf, l, 0, - addr, addrlen); + n = sendto_schedule(s, buf, l, 0, + addr, addrlen, delay); sockaddr_to_string(addr, addr_str, sizeof(addr_str)); - syslog(LOG_INFO, "SSDP Announce %d bytes to %s ST: %.*s",n, - addr_str, - l, buf); + syslog(LOG_DEBUG, "%s: %d bytes to %s ST: %.*s", + "SendSSDPResponse()", + n, addr_str, l, buf); if(n < 0) { - /* XXX handle EINTR, EAGAIN, EWOULDBLOCK */ - syslog(LOG_ERR, "sendto(udp): %m"); + syslog(LOG_ERR, "%s: sendto(udp): %m", + "SendSSDPResponse()"); } } @@ -477,7 +491,7 @@ SendSSDPNotify(int s, const struct sockaddr * dest, syslog(LOG_WARNING, "SendSSDPNotify(): truncated output"); l = sizeof(bufr) - 1; } - n = sendto(s, bufr, l, 0, dest, + n = sendto_or_schedule(s, bufr, l, 0, dest, #ifdef ENABLE_IPV6 ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in) #else @@ -486,7 +500,6 @@ SendSSDPNotify(int s, const struct sockaddr * dest, ); if(n < 0) { - /* XXX handle EINTR, EAGAIN, EWOULDBLOCK */ syslog(LOG_ERR, "sendto(udp_notify=%d, %s): %m", s, host ? host : "NULL"); } @@ -494,6 +507,22 @@ SendSSDPNotify(int s, const struct sockaddr * dest, { syslog(LOG_NOTICE, "sendto() sent %d out of %d bytes", n, l); } + /* Due to the unreliable nature of UDP, devices SHOULD send the entire + * set of discovery messages more than once with some delay between + * sets e.g. a few hundred milliseconds. To avoid network congestion + * discovery messages SHOULD NOT be sent more than three times. */ + n = sendto_schedule(s, bufr, l, 0, dest, +#ifdef ENABLE_IPV6 + ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in), +#else + sizeof(struct sockaddr_in), +#endif + 250); + if(n < 0) + { + syslog(LOG_ERR, "sendto(udp_notify=%d, %s): %m", s, + host ? host : "NULL"); + } } static void @@ -564,8 +593,11 @@ SendSSDPNotifies2(int * sockets, lifetime, 0); i++; #ifdef ENABLE_IPV6 - SendSSDPNotifies(sockets[i], ipv6_addr_for_http_with_brackets, port, - lifetime, 1); + if(sockets[i] >= 0) + { + SendSSDPNotifies(sockets[i], ipv6_addr_for_http_with_brackets, port, + lifetime, 1); + } i++; #endif } @@ -620,8 +652,20 @@ ProcessSSDPData(int s, const char *bufr, int n, #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)); @@ -664,7 +708,7 @@ ProcessSSDPData(int s, const char *bufr, int n, /*while(bufr[i+j]!='\r') j++;*/ /*syslog(LOG_INFO, "%.*s", j, bufr+i);*/ } -#ifdef UPNP_STRICT +#if defined(UPNP_STRICT) || defined(DELAY_MSEARCH_RESPONSE) else if((i < n - 3) && (strncasecmp(bufr+i, "mx:", 3) == 0)) { const char * mx; @@ -682,16 +726,32 @@ ProcessSSDPData(int s, const char *bufr, int n, #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)) { - /* TODO : doesnt answer at once but wait for a random time */ syslog(LOG_INFO, "SSDP M-SEARCH from %s ST: %.*s", sender_str, st_len, st); /* find in which sub network the client is */ @@ -753,11 +813,43 @@ ProcessSSDPData(int s, const char *bufr, int n, #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, port, - known_service_types[i].uuid); + known_service_types[i].uuid, + delay); break; } } @@ -765,9 +857,15 @@ ProcessSSDPData(int s, const char *bufr, int n, /* 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 @@ -776,37 +874,53 @@ ProcessSSDPData(int s, const char *bufr, int n, SendSSDPResponse(s, sender, known_service_types[i].s, l, ver_str, announced_host, port, - known_service_types[i].uuid); + 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, port, uuidvalue_igd); + announced_host, port, uuidvalue_igd, delay); +#ifdef DELAY_MSEARCH_RESPONSE + delay += delay_increment; +#endif SendSSDPResponse(s, sender, uuidvalue_wan, strlen(uuidvalue_wan), "", - announced_host, port, uuidvalue_wan); + announced_host, port, uuidvalue_wan, delay); +#ifdef DELAY_MSEARCH_RESPONSE + delay += delay_increment; +#endif SendSSDPResponse(s, sender, uuidvalue_wcd, strlen(uuidvalue_wcd), "", - announced_host, port, uuidvalue_wcd); + announced_host, port, 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, port, uuidvalue_igd); + announced_host, port, uuidvalue_igd, + delay); } else if(0 == memcmp(st, uuidvalue_wan, l)) { syslog(LOG_INFO, "ssdp:uuid (WAN) found"); SendSSDPResponse(s, sender, st, st_len, "", - announced_host, port, uuidvalue_wan); + announced_host, port, uuidvalue_wan, + delay); } else if(0 == memcmp(st, uuidvalue_wcd, l)) { syslog(LOG_INFO, "ssdp:uuid (WCD) found"); SendSSDPResponse(s, sender, st, st_len, "", - announced_host, port, uuidvalue_wcd); + announced_host, port, uuidvalue_wcd, + delay); } } } @@ -856,7 +970,7 @@ SendSSDPbyebye(int s, const struct sockaddr * dest, syslog(LOG_WARNING, "SendSSDPbyebye(): truncated output"); l = sizeof(bufr) - 1; } - n = sendto(s, bufr, l, 0, dest, + n = sendto_or_schedule(s, bufr, l, 0, dest, #ifdef ENABLE_IPV6 ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in) #else @@ -1013,6 +1127,8 @@ SubmitServicesToMiniSSDPD(const char * host, unsigned short port) { while(n > 0) { l = write(s, p, n); if (l < 0) { + if(errno == EINTR) + continue; syslog(LOG_ERR, "write(): %m"); close(s); return -1; diff --git a/miniupnpd/miniupnpd.c b/miniupnpd/miniupnpd.c index 42313b4..151d79a 100644 --- a/miniupnpd/miniupnpd.c +++ b/miniupnpd/miniupnpd.c @@ -1,7 +1,7 @@ -/* $Id: miniupnpd.c,v 1.178 2013/12/13 14:10:02 nanard Exp $ */ +/* $Id: miniupnpd.c,v 1.190 2014/03/24 10:49:44 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * (c) 2006-2013 Thomas Bernard + * (c) 2006-2014 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ @@ -63,6 +63,7 @@ #include "miniupnpdtypes.h" #include "daemonize.h" #include "upnpevents.h" +#include "asyncsendto.h" #ifdef ENABLE_NATPMP #include "natpmp.h" #ifdef ENABLE_PCP @@ -111,12 +112,17 @@ 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 listenname; + struct sockaddr_in6 listenname6; + struct sockaddr_in listenname4; #else struct sockaddr_in listenname; #endif @@ -124,7 +130,7 @@ OpenAndConfHTTPSocket(unsigned short port) if( (s = socket( #ifdef ENABLE_IPV6 - PF_INET6, + ipv6 ? PF_INET6 : PF_INET, #else PF_INET, #endif @@ -153,19 +159,35 @@ OpenAndConfHTTPSocket(unsigned short port) } #ifdef ENABLE_IPV6 - memset(&listenname, 0, sizeof(struct sockaddr_in6)); - listenname.sin6_family = AF_INET6; - listenname.sin6_port = htons(port); - listenname.sin6_addr = in6addr_any; - listenname_len = sizeof(struct sockaddr_in6); + if(ipv6) + { + memset(&listenname6, 0, sizeof(struct sockaddr_in6)); + listenname6.sin6_family = AF_INET6; + listenname6.sin6_port = htons(port); + listenname6.sin6_addr = in6addr_any; + 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); @@ -181,6 +203,85 @@ OpenAndConfHTTPSocket(unsigned short port) return s; } + +static struct upnphttp * +ProcessIncomingHTTP(int shttpl) +{ + int shttp; + socklen_t clientnamelen; +#ifdef ENABLE_IPV6 + struct sockaddr_storage clientname; + clientnamelen = sizeof(struct sockaddr_storage); +#else + struct sockaddr_in clientname; + clientnamelen = sizeof(struct sockaddr_in); +#endif + shttp = accept(shttpl, (struct sockaddr *)&clientname, &clientnamelen); + if(shttp<0) + { + /* ignore EAGAIN, EWOULDBLOCK, EINTR, we just try again later */ + if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) + syslog(LOG_ERR, "accept(http): %m"); + } + else + { + struct upnphttp * tmp = 0; + char addr_str[64]; + + sockaddr_to_string((struct sockaddr *)&clientname, addr_str, sizeof(addr_str)); + syslog(LOG_INFO, "HTTP connection from %s", addr_str); + if(get_lan_for_peer((struct sockaddr *)&clientname) == NULL) + { + /* The peer is not a LAN ! */ + syslog(LOG_WARNING, + "HTTP peer %s is not from a LAN, closing the connection", + addr_str); + close(shttp); + } + else + { + /* Create a new upnphttp object and add it to + * the active upnphttp object list */ + tmp = New_upnphttp(shttp); + if(tmp) + { +#ifdef ENABLE_IPV6 + if(clientname.ss_family == AF_INET) + { + tmp->clientaddr = ((struct sockaddr_in *)&clientname)->sin_addr; + } + else if(clientname.ss_family == AF_INET6) + { + struct sockaddr_in6 * addr = (struct sockaddr_in6 *)&clientname; + if(IN6_IS_ADDR_V4MAPPED(&addr->sin6_addr)) + { + memcpy(&tmp->clientaddr, + &addr->sin6_addr.s6_addr[12], + 4); + } + else + { + tmp->ipv6 = 1; + memcpy(&tmp->clientaddr_v6, + &addr->sin6_addr, + sizeof(struct in6_addr)); + } + } +#else + tmp->clientaddr = clientname.sin_addr; +#endif + return tmp; + } + else + { + syslog(LOG_ERR, "New_upnphttp() failed"); + close(shttp); + } + } + } + return NULL; +} + #ifdef ENABLE_NFQUEUE int identify_ip_protocol(char *payload) { @@ -586,6 +687,7 @@ parselanaddr(struct lan_addr_s * lan_addr, const char * str) p++; if(*p=='.') { + /* parse mask in /255.255.255.0 format */ while(*p && (*p=='.' || isdigit(*p))) p++; n = p - q; @@ -598,6 +700,7 @@ parselanaddr(struct lan_addr_s * lan_addr, const char * str) } else { + /* it is a /24 format */ int nbits = atoi(q); if(nbits > 32 || nbits < 0) goto parselan_error; @@ -685,7 +788,10 @@ void complete_uuidvalues(void) * 5) check and write pid file * 6) set startup time stamp * 7) compute presentation URL - * 8) set signal handlers */ + * 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) { @@ -906,12 +1012,14 @@ init(int argc, char * * argv, struct runtime_vars * v) optionsfile); } } - /* if lifetimes ae inverse*/ +#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 */ @@ -1241,6 +1349,10 @@ init(int argc, char * * argv, struct runtime_vars * v) 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"); @@ -1338,12 +1450,18 @@ 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 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; + 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; @@ -1429,7 +1547,11 @@ main(int argc, char * * argv) { /* open socket for HTTP connections. Listen on the 1st LAN address */ +#ifdef ENABLE_IPV6 + shttpl = OpenAndConfHTTPSocket((v.port > 0) ? v.port : 0, 1); +#else /* ENABLE_IPV6 */ shttpl = OpenAndConfHTTPSocket((v.port > 0) ? v.port : 0); +#endif /* ENABLE_IPV6 */ if(shttpl < 0) { syslog(LOG_ERR, "Failed to open socket for HTTP. EXITING"); @@ -1445,13 +1567,22 @@ main(int argc, char * * argv) v.port = ntohs(sockinfo.sin_port); } syslog(LOG_NOTICE, "HTTP listening on port %d", v.port); +#if defined(V6SOCKETS_ARE_V6ONLY) && defined(ENABLE_IPV6) + shttpl_v4 = OpenAndConfHTTPSocket(v.port, 0); + if(shttpl_v4 < 0) + { + syslog(LOG_ERR, "Failed to open socket for HTTP on port %hu (IPv4). EXITING", v.port); + return 1; + } +#endif /* V6SOCKETS_ARE_V6ONLY */ #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"); + syslog(LOG_WARNING, "no HTTP IPv6 address, disabling IPv6"); + SETFLAG(IPV6DISABLEDMASK); } #endif @@ -1466,10 +1597,13 @@ main(int argc, char * * argv) } } #ifdef ENABLE_IPV6 - sudpv6 = OpenAndConfSSDPReceiveSocket(1); - if(sudpv6 < 0) + if(!GETFLAG(IPV6DISABLEDMASK)) { - syslog(LOG_WARNING, "Failed to open socket for receiving SSDP (IP v6)."); + sudpv6 = OpenAndConfSSDPReceiveSocket(1); + if(sudpv6 < 0) + { + syslog(LOG_WARNING, "Failed to open socket for receiving SSDP (IP v6)."); + } } #endif @@ -1513,13 +1647,14 @@ main(int argc, char * * argv) syslog(LOG_NOTICE, "Listening for NAT-PMP traffic on port %u", NATPMP_PORT); } -#endif -#if 0 - ScanNATPMPforExpiration(); #endif } #endif +#if defined(ENABLE_IPV6) && defined(ENABLE_PCP) + spcp_v6 = OpenAndConfPCPv6Socket(); +#endif + /* for miniupnpdctl */ #ifdef USE_MINIUPNPDCTL sctl = OpenAndConfCtlUnixSocket("/var/run/miniupnpd.ctl"); @@ -1548,7 +1683,7 @@ main(int argc, char * * argv) /* send public address change notifications if needed */ if(should_send_public_address_change_notif) { - syslog(LOG_DEBUG, "should send external iface address change notification(s)"); + syslog(LOG_INFO, "should send external iface address change notification(s)"); #ifdef ENABLE_NATPMP if(GETFLAG(ENABLENATPMPMASK)) SendNATPMPPublicAddressChangeNotification(snatpmp, addr_count); @@ -1629,28 +1764,6 @@ main(int argc, char * * argv) syslog(LOG_DEBUG, "setting timeout to %u sec", (unsigned)timeout.tv_sec); } -#ifdef ENABLE_NATPMP -#if 0 - /* Remove expired NAT-PMP mappings */ - while(nextnatpmptoclean_timestamp - && (timeofday.tv_sec >= nextnatpmptoclean_timestamp + startup_time)) - { - /*syslog(LOG_DEBUG, "cleaning expired NAT-PMP mappings");*/ - if(CleanExpiredNATPMP() < 0) { - syslog(LOG_ERR, "CleanExpiredNATPMP() failed"); - break; - } - } - if(nextnatpmptoclean_timestamp - && timeout.tv_sec >= (nextnatpmptoclean_timestamp + startup_time - timeofday.tv_sec)) - { - /*syslog(LOG_DEBUG, "setting timeout to %d sec", - nextnatpmptoclean_timestamp + startup_time - timeofday.tv_sec);*/ - timeout.tv_sec = nextnatpmptoclean_timestamp + startup_time - timeofday.tv_sec; - timeout.tv_usec = 0; - } -#endif -#endif #ifdef ENABLE_6FC_SERVICE /* Clean up expired IPv6 PinHoles */ next_pinhole_ts = 0; @@ -1683,6 +1796,13 @@ main(int argc, char * * argv) 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_IPV6 if (sudpv6 >= 0) { @@ -1729,6 +1849,12 @@ main(int argc, char * * argv) } } #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); @@ -1748,6 +1874,38 @@ main(int argc, char * * argv) 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; @@ -1756,6 +1914,10 @@ main(int argc, char * * argv) 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;) { @@ -1830,19 +1992,23 @@ main(int argc, char * * argv) { unsigned char msg_buff[PCP_MAX_LEN]; struct sockaddr_in senderaddr; + socklen_t senderaddrlen; int len; memset(msg_buff, 0, PCP_MAX_LEN); - len = ReceiveNATPMPOrPCPPacket(snatpmp[i], &senderaddr, - msg_buff, sizeof(msg_buff)); + senderaddrlen = sizeof(senderaddr); + len = ReceiveNATPMPOrPCPPacket(snatpmp[i], + (struct sockaddr *)&senderaddr, + &senderaddrlen, + 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); + &senderaddr); } else { /* everything else can be PCP */ ProcessIncomingPCPPacket(snatpmp[i], msg_buff, len, - &senderaddr); + (struct sockaddr *)&senderaddr); } #else @@ -1850,6 +2016,25 @@ main(int argc, char * * argv) #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; + int len; + memset(msg_buff, 0, PCP_MAX_LEN); + senderaddrlen = sizeof(senderaddr); + len = ReceiveNATPMPOrPCPPacket(spcp_v6, + (struct sockaddr *)&senderaddr, + &senderaddrlen, + msg_buff, sizeof(msg_buff)); + if(len >= 1) + ProcessIncomingPCPPacket(spcp_v6, msg_buff, len, + (struct sockaddr *)&senderaddr); + } #endif /* process SSDP packets */ if(sudp >= 0 && FD_ISSET(sudp, &readset)) @@ -1886,79 +2071,24 @@ main(int argc, char * * argv) /* process incoming HTTP connections */ if(shttpl >= 0 && FD_ISSET(shttpl, &readset)) { - int shttp; - socklen_t clientnamelen; -#ifdef ENABLE_IPV6 - struct sockaddr_storage clientname; - clientnamelen = sizeof(struct sockaddr_storage); -#else - struct sockaddr_in clientname; - clientnamelen = sizeof(struct sockaddr_in); -#endif - shttp = accept(shttpl, (struct sockaddr *)&clientname, &clientnamelen); - if(shttp<0) + struct upnphttp * tmp; + tmp = ProcessIncomingHTTP(shttpl); + if(tmp) { - /* ignore EAGAIN, EWOULDBLOCK, EINTR, we just try again later */ - if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) - syslog(LOG_ERR, "accept(http): %m"); - } - else - { - struct upnphttp * tmp = 0; - char addr_str[64]; - - sockaddr_to_string((struct sockaddr *)&clientname, addr_str, sizeof(addr_str)); - syslog(LOG_INFO, "HTTP connection from %s", addr_str); - if(get_lan_for_peer((struct sockaddr *)&clientname) == NULL) - { - /* The peer is not a LAN ! */ - syslog(LOG_WARNING, - "HTTP peer %s is not from a LAN, closing the connection", - addr_str); - close(shttp); - } - else - { - /* Create a new upnphttp object and add it to - * the active upnphttp object list */ - tmp = New_upnphttp(shttp); - if(tmp) - { -#ifdef ENABLE_IPV6 - if(clientname.ss_family == AF_INET) - { - tmp->clientaddr = ((struct sockaddr_in *)&clientname)->sin_addr; - } - else if(clientname.ss_family == AF_INET6) - { - struct sockaddr_in6 * addr = (struct sockaddr_in6 *)&clientname; - if(IN6_IS_ADDR_V4MAPPED(&addr->sin6_addr)) - { - memcpy(&tmp->clientaddr, - &addr->sin6_addr.s6_addr[12], - 4); - } - else - { - tmp->ipv6 = 1; - memcpy(&tmp->clientaddr_v6, - &addr->sin6_addr, - sizeof(struct in6_addr)); - } - } -#else - tmp->clientaddr = clientname.sin_addr; -#endif - LIST_INSERT_HEAD(&upnphttphead, tmp, entries); - } - else - { - syslog(LOG_ERR, "New_upnphttp() failed"); - close(shttp); - } - } + 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); + if(tmp) + { + LIST_INSERT_HEAD(&upnphttphead, tmp, entries); + } + } +#endif #ifdef ENABLE_NFQUEUE /* process NFQ packets */ if(nfqh >= 0 && FD_ISSET(nfqh, &readset)) @@ -1982,6 +2112,22 @@ main(int argc, char * * argv) } /* 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) { @@ -1992,6 +2138,9 @@ shutdown: 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 @@ -2007,6 +2156,13 @@ shutdown: } } #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) { @@ -2021,14 +2177,6 @@ shutdown: if (GETFLAG(ENABLEUPNPMASK)) { -#ifndef ENABLE_IPV6 - if(SendSSDPGoodbye(snotify, addr_count) < 0) -#else - if(SendSSDPGoodbye(snotify, addr_count * 2) < 0) -#endif - { - syslog(LOG_ERR, "Failed to broadcast good-bye notifications"); - } #ifndef ENABLE_IPV6 for(i = 0; i < addr_count; i++) #else @@ -2037,6 +2185,7 @@ shutdown: close(snotify[i]); } + /* remove pidfile */ if(pidfilename && (unlink(pidfilename) < 0)) { syslog(LOG_ERR, "Failed to remove pidfile %s: %m", pidfilename); diff --git a/miniupnpd/minixml.c b/miniupnpd/minixml.c index d3f7d06..1f22273 100644 --- a/miniupnpd/minixml.c +++ b/miniupnpd/minixml.c @@ -1,10 +1,10 @@ -/* $Id: minixml.c,v 1.9 2011/02/07 13:44:57 nanard Exp $ */ +/* $Id: minixml.c,v 1.10 2012/03/05 19:42:47 nanard Exp $ */ /* minixml.c : the minimum size a xml parser can be ! */ /* Project : miniupnp * webpage: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * Author : Thomas Bernard -Copyright (c) 2005-2011, Thomas BERNARD +Copyright (c) 2005-2014, Thomas BERNARD All rights reserved. Redistribution and use in source and binary forms, with or without @@ -113,7 +113,20 @@ static void parseelt(struct xmlparser * p) const char * elementname; while(p->xml < (p->xmlend - 1)) { - if((p->xml)[0]=='<' && (p->xml)[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) diff --git a/miniupnpd/natpmp.c b/miniupnpd/natpmp.c index d1efd27..c0d1996 100644 --- a/miniupnpd/natpmp.c +++ b/miniupnpd/natpmp.c @@ -1,6 +1,6 @@ -/* $Id: natpmp.c,v 1.35 2013/12/13 14:07:08 nanard Exp $ */ +/* $Id: natpmp.c,v 1.43 2014/03/24 10:49:45 nanard Exp $ */ /* MiniUPnP project - * (c) 2007-2013 Thomas Bernard + * (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 */ @@ -23,9 +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; @@ -33,13 +36,13 @@ int OpenAndConfNATPMPSocket(in_addr_t addr) snatpmp = socket(PF_INET, SOCK_DGRAM, 0/*IPPROTO_UDP*/); if(snatpmp<0) { - syslog(LOG_ERR, "%s: socket(natpmp): %m", + 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(natpmp, SO_REUSEADDR): %m", + syslog(LOG_WARNING, "%s: setsockopt(SO_REUSEADDR): %m", "OpenAndConfNATPMPSocket"); } if(!set_non_blocking(snatpmp)) @@ -56,7 +59,8 @@ int OpenAndConfNATPMPSocket(in_addr_t addr) natpmp_addr.sin_addr.s_addr = addr; if(bind(snatpmp, (struct sockaddr *)&natpmp_addr, sizeof(natpmp_addr)) < 0) { - syslog(LOG_ERR, "bind(natpmp): %m"); + syslog(LOG_ERR, "%s: bind(): %m", + "OpenAndConfNATPMPSocket"); close(snatpmp); return -1; } @@ -66,22 +70,25 @@ int OpenAndConfNATPMPSocket(in_addr_t addr) int OpenAndConfNATPMPSockets(int * sockets) { - int i, j; + 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, i++) + 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) - { - for(j=0; j= 0) + { + close(sockets[i]); + sockets[i] = -1; + } + return -1; } static void FillPublicAddressResponse(unsigned char * resp, in_addr_t senderaddr) @@ -123,15 +130,15 @@ static void FillPublicAddressResponse(unsigned char * resp, in_addr_t senderaddr * The sender information is stored in senderaddr. * Returns number of bytes recevied, even if number is negative. */ -int ReceiveNATPMPOrPCPPacket(int s, struct sockaddr_in* senderaddr, - unsigned char *msg_buff, size_t msg_buff_size) +int ReceiveNATPMPOrPCPPacket(int s, struct sockaddr * senderaddr, + socklen_t * senderaddrlen, + unsigned char * msg_buff, size_t msg_buff_size) { - socklen_t senderaddrlen = sizeof(*senderaddr); int n; n = recvfrom(s, msg_buff, msg_buff_size, 0, - (struct sockaddr *)senderaddr, &senderaddrlen); + senderaddr, senderaddrlen); if(n<0) { /* EAGAIN, EWOULDBLOCK and EINTR : silently ignore (retry next time) @@ -262,32 +269,64 @@ void ProcessIncomingNATPMPPacket(int s, unsigned char *msg_buff, int len, } } eport = 0; /* to indicate correct removing of port mapping */ - } else if(iport==0 - || !check_upnp_rule_against_permissions(upnppermlist, num_upnpperm, eport, senderaddr->sin_addr, iport)) { + } else if(iport==0) { resp[3] = 2; /* Not Authorized/Refused */ - } else do { - r = get_redirect_rule(ext_if_name, eport, proto, - iaddr_old, sizeof(iaddr_old), - &iport_old, 0, 0, 0, 0, - ×tamp, 0, 0); - if(r==0) { - if(strcmp(senderaddrstr, iaddr_old)==0 - && iport==iport_old) { - /* redirection allready existing */ - syslog(LOG_INFO, "port %hu %s already redirected to %s:%hu, replacing", - eport, (proto==IPPROTO_TCP)?"tcp":"udp", iaddr_old, iport_old); - /* remove and then add again */ - if(_upnp_delete_redir(eport, proto) < 0) { - syslog(LOG_ERR, "failed to remove port mapping"); - break; + } else { /* 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 */ } - } else { + break; + } + if(!check_upnp_rule_against_permissions(upnppermlist, num_upnpperm, eport, senderaddr->sin_addr, iport)) { eport++; + if(eport == 0) eport++; /* skip port zero */ continue; } - } - { /* do the redirection */ - char desc[64]; + 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; @@ -304,17 +343,10 @@ void ProcessIncomingNATPMPPacket(int s, unsigned char *msg_buff, int len, syslog(LOG_ERR, "Failed to add NAT-PMP %hu %s->%s:%hu '%s'", eport, (proto==IPPROTO_TCP)?"tcp":"udp", senderaddrstr, iport, desc); resp[3] = 3; /* Failure */ -#if 0 - } else if( !nextnatpmptoclean_eport - || timestamp < nextnatpmptoclean_timestamp) { - nextnatpmptoclean_timestamp = timestamp; - nextnatpmptoclean_eport = eport; - nextnatpmptoclean_proto = proto; -#endif } break; } - } while(r==0); + } *((uint16_t *)(resp+8)) = htons(iport); /* private port */ *((uint16_t *)(resp+10)) = htons(eport); /* public port */ *((uint32_t *)(resp+12)) = htonl(lifetime); /* Port Mapping lifetime */ @@ -324,7 +356,7 @@ void ProcessIncomingNATPMPPacket(int s, unsigned char *msg_buff, int len, default: resp[3] = 5; /* Unsupported OPCODE */ } - n = sendto(s, resp, resplen, 0, + n = sendto_or_schedule(s, resp, resplen, 0, (struct sockaddr *)senderaddr, sizeof(*senderaddr)); if(n<0) { syslog(LOG_ERR, "sendto(natpmp): %m"); @@ -334,67 +366,6 @@ void ProcessIncomingNATPMPPacket(int s, unsigned char *msg_buff, int len, } } -#if 0 -/* iterate through the redirection list to find those who came - * from NAT-PMP and select the first to expire */ -int ScanNATPMPforExpiration() -{ - char desc[64]; - unsigned short iport, eport; - int proto; - int r, i; - unsigned timestamp; - nextnatpmptoclean_eport = 0; - nextnatpmptoclean_timestamp = 0; - for(i = 0; ; i++) { - r = get_redirect_rule_by_index(i, 0, &eport, 0, 0, - &iport, &proto, desc, sizeof(desc), - ×tamp, 0, 0); - if(r<0) - break; - if(sscanf(desc, "NAT-PMP %u", ×tamp) == 1) { - if( !nextnatpmptoclean_eport - || timestamp < nextnatpmptoclean_timestamp) { - nextnatpmptoclean_eport = eport; - nextnatpmptoclean_proto = proto; - nextnatpmptoclean_timestamp = timestamp; - syslog(LOG_DEBUG, "set nextnatpmptoclean_timestamp to %u", timestamp); - } - } - } - return 0; -} - -/* remove the next redirection that is expired - */ -int CleanExpiredNATPMP() -{ - char desc[64]; - unsigned timestamp; - unsigned short iport; - if(get_redirect_rule(ext_if_name, nextnatpmptoclean_eport, - nextnatpmptoclean_proto, - 0, 0, - &iport, desc, sizeof(desc), ×tamp, 0, 0) < 0) - return ScanNATPMPforExpiration(); - /* check desc - this is important since we keep expiration time as part - * of the desc. - * If the rule is renewed, timestamp and nextnatpmptoclean_timestamp - * can be different. In that case, the rule must not be removed ! */ - if(sscanf(desc, "NAT-PMP %u", ×tamp) == 1) { - if(timestamp > nextnatpmptoclean_timestamp) - return ScanNATPMPforExpiration(); - } - /* remove redirection then search for next one:) */ - if(_upnp_delete_redir(nextnatpmptoclean_eport, nextnatpmptoclean_proto)<0) - return -1; - syslog(LOG_INFO, "Expired NAT-PMP mapping port %hu %s removed", - nextnatpmptoclean_eport, - nextnatpmptoclean_proto==IPPROTO_TCP?"TCP":"UDP"); - return ScanNATPMPforExpiration(); -} -#endif - /* SendNATPMPPublicAddressChangeNotification() * should be called when the public IP address changed */ void SendNATPMPPublicAddressChangeNotification(int * sockets, int n_sockets) @@ -439,7 +410,7 @@ void SendNATPMPPublicAddressChangeNotification(int * sockets, int n_sockets) #endif /* Port to use in 2006 version of the NAT-PMP specification */ sockname.sin_port = htons(NATPMP_PORT); - n = sendto(sockets[j], notif, 12, 0, + n = sendto_or_schedule(sockets[j], notif, 12, 0, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)); if(n < 0) { @@ -449,7 +420,7 @@ void SendNATPMPPublicAddressChangeNotification(int * sockets, int n_sockets) } /* Port to use in 2008 version of the NAT-PMP specification */ sockname.sin_port = htons(NATPMP_NOTIF_PORT); - n = sendto(sockets[j], notif, 12, 0, + n = sendto_or_schedule(sockets[j], notif, 12, 0, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)); if(n < 0) { diff --git a/miniupnpd/natpmp.h b/miniupnpd/natpmp.h index b8a73bf..2f6091b 100644 --- a/miniupnpd/natpmp.h +++ b/miniupnpd/natpmp.h @@ -1,4 +1,4 @@ -/* $Id: natpmp.h,v 1.8 2011/05/27 21:36:22 nanard Exp $ */ +/* $Id: natpmp.h,v 1.12 2014/03/24 10:49:46 nanard Exp $ */ /* MiniUPnP project * author : Thomas Bernard * website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ @@ -20,17 +20,12 @@ int OpenAndConfNATPMPSockets(int * sockets); -int ReceiveNATPMPOrPCPPacket(int s, struct sockaddr_in* senderaddr, - unsigned char *msg_buff, size_t msg_buff_size); +int ReceiveNATPMPOrPCPPacket(int s, struct sockaddr * senderaddr, + socklen_t * senderaddrlen, + unsigned char * msg_buff, size_t msg_buff_size); -void ProcessIncomingNATPMPPacket(int s, unsigned char *msg_buff, int len, - struct sockaddr_in *senderaddr); - -#if 0 -int ScanNATPMPforExpiration(void); - -int CleanExpiredNATPMP(void); -#endif +void ProcessIncomingNATPMPPacket(int s, unsigned char * msg_buff, int len, + struct sockaddr_in * senderaddr); void SendNATPMPPublicAddressChangeNotification(int * sockets, int n_sockets); 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/pcp_msg_struct.h b/miniupnpd/pcp_msg_struct.h index bb557bc..0d5f912 100644 --- a/miniupnpd/pcp_msg_struct.h +++ b/miniupnpd/pcp_msg_struct.h @@ -178,12 +178,12 @@ typedef struct pcp_options_hdr { /* same for both request and response */ typedef struct pcp_map_v2 { uint32_t nonce[3]; - uint8_t protocol; + uint8_t protocol; /* 6 = TCP, 17 = UDP, 0 = 'all protocols' */ 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 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; diff --git a/miniupnpd/pcpserver.c b/miniupnpd/pcpserver.c index bfae5e8..bcc701a 100644 --- a/miniupnpd/pcpserver.c +++ b/miniupnpd/pcpserver.c @@ -1,4 +1,4 @@ -/* $Id: pcpserver.c,v 1.5 2014/01/27 10:06:08 nanard Exp $ */ +/* $Id: pcpserver.c,v 1.26 2014/03/24 13:08:52 nanard Exp $ */ /* MiniUPnP project * Website : http://miniupnp.free.fr/ * Author : Peter Tatrai @@ -43,6 +43,7 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #include #include #include @@ -52,12 +53,15 @@ POSSIBILITY OF SUCH DAMAGE. #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 "pcp_msg_struct.h" #ifdef PCP_PEER @@ -82,6 +86,7 @@ typedef struct pcp_info { 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 */ @@ -114,7 +119,7 @@ typedef struct pcp_info { uint8_t is_peer_op; int thirdp_present; /* indicate presence of the options */ int pfailure_present; - char senderaddrstr[INET_ADDRSTRLEN]; + char senderaddrstr[INET_ADDRSTRLEN]; /* only if IPv4 sender */ } pcp_info_t; @@ -173,10 +178,10 @@ int get_dscp_value(pcp_info_t *pcp_msg_info) { * result code is assigned to pcp_msg_info->result_code to indicate * what kind of error occurred */ -static int parseCommonRequestHeader(pcp_request_t *common_req, pcp_info_t *pcp_msg_info) +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->opcode = common_req->r_opcode & 0x7f; pcp_msg_info->lifetime = ntohl(common_req->req_lifetime); pcp_msg_info->int_ip = &common_req->ip; @@ -198,7 +203,7 @@ static int parseCommonRequestHeader(pcp_request_t *common_req, pcp_info_t *pcp_m } #ifdef DEBUG -static void printMAPOpcodeVersion1(pcp_map_v1_t *map_buf) +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"); @@ -209,19 +214,21 @@ static void printMAPOpcodeVersion1(pcp_map_v1_t *map_buf) &map_buf->ext_ip, map_addr, INET6_ADDRSTRLEN)); } -static void printMAPOpcodeVersion2(pcp_map_v2_t *map_buf) +static void printMAPOpcodeVersion2(const pcp_map_v2_t *map_buf) { char map_addr[INET6_ADDRSTRLEN]; - syslog(LOG_DEBUG, "PCP MAP: v2 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, + 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(pcp_map_v1_t *map_v1, +static int parsePCPMAP_version1(const pcp_map_v1_t *map_v1, pcp_info_t *pcp_msg_info) { pcp_msg_info->is_map_op = 1; @@ -239,10 +246,11 @@ static int parsePCPMAP_version1(pcp_map_v1_t *map_v1, return 0; } -static int parsePCPMAP_version2(pcp_map_v2_t *map_v2, +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); @@ -280,15 +288,17 @@ 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. \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, + syslog(LOG_DEBUG, "PCP PEER: v2 Opcode specific information."); + syslog(LOG_DEBUG, "nonce: \t%08x%08x%08x", + map_buf->nonce[0], map_buf->nonce[1], map_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 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, + 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 port: \t\t %d\n", ntohs(peer_buf->peer_port) ); + syslog(LOG_DEBUG, "PEER port: \t%d", ntohs(peer_buf->peer_port) ); } #endif /* DEBUG */ @@ -324,6 +334,7 @@ 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); @@ -534,6 +545,7 @@ static int parsePCPOptions(void* pcp_buf, int* remainingSize, static int CheckExternalAddress(pcp_info_t* pcp_msg_info) { + /* can contain a IPv4-mapped IPv6 address */ static struct in6_addr external_addr; if(use_ext_ip_addr) { @@ -552,13 +564,14 @@ static int CheckExternalAddress(pcp_info_t* pcp_msg_info) pcp_msg_info->result_code = PCP_ERR_NETWORK_FAILURE; return -1; } + /* how do we know which address we need ? IPv6 or IPv4 ? */ if(getifaddr_in6(ext_if_name, &external_addr) < 0) { pcp_msg_info->result_code = PCP_ERR_NETWORK_FAILURE; return -1; } } - if (IN6_IS_ADDR_UNSPECIFIED(pcp_msg_info->ext_ip)) { + if (pcp_msg_info->ext_ip == NULL || IN6_IS_ADDR_UNSPECIFIED(pcp_msg_info->ext_ip)) { pcp_msg_info->ext_ip = &external_addr; @@ -664,6 +677,7 @@ static int CreatePCPPeer(pcp_info_t *pcp_msg_info) /* Create Peer Mapping */ { char desc[64]; + char proto_str[8]; char peerip_s[INET_ADDRSTRLEN], extip_s[INET_ADDRSTRLEN]; time_t timestamp = time(NULL) + pcp_msg_info->lifetime; @@ -671,8 +685,19 @@ static int CreatePCPPeer(pcp_info_t *pcp_msg_info) eport = pcp_msg_info->int_port; } - snprintf(desc, sizeof(desc), "PCP %hu %s", - eport, (proto==IPPROTO_TCP)?"tcp":"udp"); + switch(proto) { + case IPPROTO_TCP: + snprintf(proto_str, sizeof(proto_str), "TCP"); + break; + case IPPROTO_UDP: + snprintf(proto_str, sizeof(proto_str), "UDP"); + break; + default: + snprintf(proto_str, sizeof(proto_str), "%d", proto); + } + snprintf(desc, sizeof(desc), "PCP %hu %s %08x%08x%08x", + eport, proto_str, + pcp_msg_info->nonce[0], pcp_msg_info->nonce[1], pcp_msg_info->nonce[2]); inet_satop((struct sockaddr*)&peerip, peerip_s, sizeof(peerip_s)); inet_satop((struct sockaddr*)&extip, extip_s, sizeof(extip_s)); @@ -684,7 +709,7 @@ static int CreatePCPPeer(pcp_info_t *pcp_msg_info) pcp_msg_info->senderaddrstr, pcp_msg_info->int_port, proto, desc, timestamp) < 0 ) { syslog(LOG_ERR, "PCP: failed to add flowp upstream mapping %s %s:%hu->%s:%hu '%s'", - (pcp_msg_info->protocol==IPPROTO_TCP)?"TCP":"UDP", + proto_str, pcp_msg_info->senderaddrstr, pcp_msg_info->int_port, peerip_s, @@ -700,7 +725,7 @@ static int CreatePCPPeer(pcp_info_t *pcp_msg_info) peerip_s, pcp_msg_info->peer_port, proto, desc, timestamp) < 0 ) { syslog(LOG_ERR, "PCP: failed to add flowp downstream mapping %s %s:%hu->%s:%hu '%s'", - (pcp_msg_info->protocol==IPPROTO_TCP)?"TCP":"UDP", + proto_str, pcp_msg_info->senderaddrstr, pcp_msg_info->int_port, peerip_s, @@ -798,18 +823,75 @@ static void DeletePCPPeer(pcp_info_t *pcp_msg_info) } #endif /* PCP_PEER */ +/* 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 scenarios than just NAT44 */ static void CreatePCPMap(pcp_info_t *pcp_msg_info) { char desc[64]; + char proto_str[8]; char iaddr_old[INET_ADDRSTRLEN]; uint16_t iport_old; - unsigned int timestamp = time(NULL) + pcp_msg_info->lifetime; + uint16_t eport_first = 0; + int any_eport_allowed = 0; + unsigned int timestamp; int r=0; if (pcp_msg_info->ext_port == 0) { pcp_msg_info->ext_port = pcp_msg_info->int_port; } 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 */ + if (any_eport_allowed == 0) { /* all eports rejected by permissions */ + pcp_msg_info->result_code = PCP_ERR_NOT_AUTHORIZED; + } else { /* at least one eport allowed (but none available) */ + pcp_msg_info->result_code = PCP_ERR_NO_RESOURCES; + } + return; + } + if ((IN6_IS_ADDR_V4MAPPED(pcp_msg_info->int_ip) && + (!check_upnp_rule_against_permissions(upnppermlist, + num_upnpperm, pcp_msg_info->ext_port, + ((struct in_addr*)pcp_msg_info->int_ip->s6_addr)[3], + pcp_msg_info->int_port)))) { + if (pcp_msg_info->pfailure_present) { + pcp_msg_info->result_code = PCP_ERR_CANNOT_PROVIDE_EXTERNAL; + return; + } + 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; r = get_redirect_rule(ext_if_name, pcp_msg_info->ext_port, pcp_msg_info->protocol, @@ -827,28 +909,41 @@ static void CreatePCPMap(pcp_info_t *pcp_msg_info) return; } } 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) { + pcp_msg_info->result_code = PCP_ERR_CANNOT_PROVIDE_EXTERNAL; + return; } } pcp_msg_info->ext_port++; + if (pcp_msg_info->ext_port == 0) { /* skip port zero */ + pcp_msg_info->ext_port++; + } } } while (r==0); - if ((pcp_msg_info->ext_port == 0) || - (IN6_IS_ADDR_V4MAPPED(pcp_msg_info->int_ip) && - (!check_upnp_rule_against_permissions(upnppermlist, - num_upnpperm, pcp_msg_info->ext_port, - ((struct in_addr*)pcp_msg_info->int_ip->s6_addr)[3], - pcp_msg_info->int_port)))) { - pcp_msg_info->result_code = PCP_ERR_CANNOT_PROVIDE_EXTERNAL; - return; - } + timestamp = time(NULL) + pcp_msg_info->lifetime; - snprintf(desc, sizeof(desc), "PCP %hu %s", + switch(pcp_msg_info->protocol) { + case IPPROTO_TCP: + snprintf(proto_str, sizeof(proto_str), "TCP"); + break; + case IPPROTO_UDP: + snprintf(proto_str, sizeof(proto_str), "UDP"); + break; + default: + snprintf(proto_str, sizeof(proto_str), "%d", pcp_msg_info->protocol); + } + snprintf(desc, sizeof(desc), "PCP %hu %s %08x%08x%08x", pcp_msg_info->ext_port, - (pcp_msg_info->protocol==IPPROTO_TCP)?"tcp":"udp"); + proto_str, + pcp_msg_info->nonce[0], pcp_msg_info->nonce[1], pcp_msg_info->nonce[2]); if(upnp_redirect_internal(NULL, pcp_msg_info->ext_port, @@ -859,15 +954,17 @@ static void CreatePCPMap(pcp_info_t *pcp_msg_info) timestamp) < 0) { syslog(LOG_ERR, "PCP MAP: Failed to add mapping %s %hu->%s:%hu '%s'", - (pcp_msg_info->protocol==IPPROTO_TCP)?"TCP":"UDP", + proto_str, pcp_msg_info->ext_port, pcp_msg_info->senderaddrstr, pcp_msg_info->int_port, desc); + pcp_msg_info->result_code = PCP_ERR_NO_RESOURCES; + } else { syslog(LOG_INFO, "PCP MAP: added mapping %s %hu->%s:%hu '%s'", - (pcp_msg_info->protocol==IPPROTO_TCP)?"TCP":"UDP", + proto_str, pcp_msg_info->ext_port, pcp_msg_info->senderaddrstr, pcp_msg_info->int_port, @@ -926,7 +1023,8 @@ static int ValidatePCPMsg(pcp_info_t *pcp_msg_info) return 0; } - if (pcp_msg_info->protocol == 0 && pcp_msg_info->int_port !=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; } @@ -959,9 +1057,8 @@ static int processPCPRequest(void * req, int req_size, pcp_info_t *pcp_msg_info) int remainingSize; int processedSize; - pcp_request_t* common_req; - pcp_map_v1_t* map_v1; - pcp_map_v2_t* map_v2; + 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; @@ -977,11 +1074,16 @@ static int processPCPRequest(void * req, int req_size, pcp_info_t *pcp_msg_info) processedSize = 0; /* discard request that exceeds maximal length, - or that is shorter than 3 + or that is shorter than PCP_MIN_LEN (=24) or that is not the multiple of 4 */ - if (req_size < PCP_MIN_LEN) + 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); @@ -989,19 +1091,17 @@ static int processPCPRequest(void * req, int req_size, pcp_info_t *pcp_msg_info) return 1; /* send response */ } - /* first print out info from common request header */ - common_req = (pcp_request_t*)req; - - if (parseCommonRequestHeader(common_req, pcp_msg_info) ) { + /* first parse request header */ + if (parseCommonRequestHeader((pcp_request_t*)req, pcp_msg_info) ) { return 1; } remainingSize -= sizeof(pcp_request_t); processedSize += sizeof(pcp_request_t); - if (common_req->ver == 1) { - - switch ( common_req->r_opcode & 0x7F ) { + 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); @@ -1077,9 +1177,14 @@ static int processPCPRequest(void * req, int req_size, pcp_info_t *pcp_msg_info) break; } - } else if (common_req->ver == 2) { - - switch ( common_req->r_opcode & 0x7F) { + } 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); @@ -1282,32 +1387,51 @@ static void createPCPResponse(unsigned char *response, pcp_info_t *pcp_msg_info) } int ProcessIncomingPCPPacket(int s, unsigned char *buff, int len, - struct sockaddr_in *senderaddr) + const struct sockaddr * senderaddr) { 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(!inet_ntop(AF_INET, &senderaddr->sin_addr, - pcp_msg_info.senderaddrstr, - sizeof(pcp_msg_info.senderaddrstr))) { - syslog(LOG_ERR, "inet_ntop(pcpserver): %m"); + if(senderaddr->sa_family == AF_INET) { + const struct sockaddr_in * senderaddr_v4; + senderaddr_v4 = (const struct sockaddr_in *)senderaddr; + if(!inet_ntop(AF_INET, &senderaddr_v4->sin_addr, + pcp_msg_info.senderaddrstr, + sizeof(pcp_msg_info.senderaddrstr))) { + syslog(LOG_ERR, "inet_ntop(pcpserver): %m"); + } } - syslog(LOG_DEBUG, "PCP request received from %s:%hu %dbytes", - pcp_msg_info.senderaddrstr, ntohs(senderaddr->sin_port), len); + 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; } + lan_addr = get_lan_for_peer(senderaddr); + if(lan_addr == NULL) { + syslog(LOG_WARNING, "SSDP packet sender %s not from a LAN, ignoring", + addr_str); + return 0; + } + if (processPCPRequest(buff, len, &pcp_msg_info) ) { createPCPResponse(buff, &pcp_msg_info); - len = (len + 3) & ~3; /* round up resp. length to multiple of 4 */ - len = sendto(s, buff, len, 0, - (struct sockaddr *)senderaddr, sizeof(struct sockaddr_in)); + 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_schedule(s, buff, len, 0, senderaddr, + (senderaddr->sa_family == AF_INET) ? + sizeof(struct sockaddr_in) : + sizeof(struct sockaddr_in6) ); if( len < 0 ) { syslog(LOG_ERR, "sendto(pcpserver): %m"); } @@ -1315,4 +1439,44 @@ int ProcessIncomingPCPPacket(int s, unsigned char *buff, int len, 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 + 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 = in6addr_any; + 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*/ diff --git a/miniupnpd/pcpserver.h b/miniupnpd/pcpserver.h index cc295f3..bd03b91 100644 --- a/miniupnpd/pcpserver.h +++ b/miniupnpd/pcpserver.h @@ -1,4 +1,4 @@ -/* $Id: pcpserver.h,v 1.2 2013/12/13 15:48:39 nanard Exp $ */ +/* $Id: pcpserver.h,v 1.3 2014/03/24 10:49:46 nanard Exp $ */ /* MiniUPnP project * Website : http://miniupnp.free.fr/ * Author : Peter Tatrai @@ -36,12 +36,17 @@ POSSIBILITY OF SUCH DAMAGE. #define PCP_MIN_LEN 24 #define PCP_MAX_LEN 1100 -struct sockaddr_in; +struct sockaddr; /* * returns 0 upon success 1 otherwise */ int ProcessIncomingPCPPacket(int s, unsigned char *msg_buff, int len, - struct sockaddr_in *senderaddr); + const struct sockaddr *senderaddr); + +/* + * returns the socket + */ +int OpenAndConfPCPv6Socket(void); #endif /* PCPSERVER_H_INCLUDED */ diff --git a/miniupnpd/pf/obsdrdr.c b/miniupnpd/pf/obsdrdr.c index 04c2ddb..349f68e 100644 --- a/miniupnpd/pf/obsdrdr.c +++ b/miniupnpd/pf/obsdrdr.c @@ -1,7 +1,7 @@ -/* $Id: obsdrdr.c,v 1.74 2012/05/01 09:20:43 nanard Exp $ */ +/* $Id: obsdrdr.c,v 1.80 2014/03/06 13:02:46 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * (c) 2006-2012 Thomas Bernard + * (c) 2006-2014 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ @@ -9,15 +9,19 @@ * pf rules created (with ext_if = xl1) * - OpenBSD up to version 4.6 : * rdr pass on xl1 inet proto udp from any to any port = 54321 \ - * label "test label" -> 192.168.0.141 port 12345 - * or a rdr rule + a pass rule + * 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.141 port 12345 + * 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.141 port 12345 + * label "test label" rdr-to 192.168.0.42 port 12345 * * * @@ -176,6 +180,45 @@ clear_redirect_rules(void) 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() : @@ -347,8 +390,11 @@ add_filter_rule2(const char * ifname, struct pfioc_rule pcr; #ifndef PF_NEWSTYLE struct pfioc_pooladdr pp; - struct pf_pooladdr *a; #endif +#ifndef USE_IFNAME_IN_RULES + UNUSED(ifname); +#endif + UNUSED(eport); if(dev<0) { syslog(LOG_ERR, "pf device is not open"); return -1; @@ -372,9 +418,8 @@ add_filter_rule2(const char * ifname, if(1) { #endif - pcr.rule.dst.port_op = PF_OP_EQ; - pcr.rule.dst.port[0] = htons(eport); + pcr.rule.dst.port[0] = htons(iport); pcr.rule.direction = PF_IN; pcr.rule.action = PF_PASS; pcr.rule.af = AF_INET; @@ -407,33 +452,16 @@ add_filter_rule2(const char * ifname, inet_pton(AF_INET, rhost, &pcr.rule.src.addr.v.a.addr.v4.s_addr); pcr.rule.src.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE); } -#ifndef PF_NEWSTYLE - pcr.rule.rpool.proxy_port[0] = eport; - a = calloc(1, sizeof(struct pf_pooladdr)); - inet_pton(AF_INET, iaddr, &a->addr.v.a.addr.v4.s_addr); - a->addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE); - memcpy(&pp.addr, a, sizeof(struct pf_pooladdr)); - TAILQ_INIT(&pcr.rule.rpool.list); - inet_pton(AF_INET, iaddr, &a->addr.v.a.addr.v4.s_addr); - TAILQ_INSERT_TAIL(&pcr.rule.rpool.list, a, entries); - - /* we have any - any port = # keep state label */ /* we want any - iaddr port = # keep state label */ - /* memcpy(&pcr.rule.dst, a, sizeof(struct pf_pooladdr)); */ - - memcpy(&pp.addr, a, sizeof(struct pf_pooladdr)); - strlcpy(pcr.rule.label, desc, PF_RULE_LABEL_SIZE); - if(ioctl(dev, DIOCADDADDR, &pp) < 0) - { - syslog(LOG_ERR, "ioctl(dev, DIOCADDADDR, ...): %m"); - r = -1; - } - else - { -#else + 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) { -#endif pcr.action = PF_CHANGE_GET_TICKET; if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0) { @@ -450,9 +478,6 @@ add_filter_rule2(const char * ifname, } } } -#ifndef PF_NEWSTYLE - free(a); -#endif } return r; #endif @@ -575,8 +600,10 @@ error: return -1; } -int -delete_redirect_rule(const char * ifname, unsigned short eport, int proto) +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; @@ -614,6 +641,44 @@ delete_redirect_rule(const char * ifname, unsigned short eport, int proto) #endif && (pr.rule.proto == proto) ) { + /* retrieve iport in order to remove filter rule */ +#ifndef PF_NEWSTYLE + if(iport) *iport = pr.rule.rpool.proxy_port[0]; + if(iaddr) + { + /* retrieve internal address */ + struct pfioc_pooladdr pp; + 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) + { + 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; + } + *iaddr = pp.addr.addr.v.a.addr.v4.s_addr; + } +#else + if(iport) *iport = pr.rule.rdr.proxy_port[0]; + if(iaddr) + { + /* retrieve internal address */ + *iaddr = pr.rule.rdr.addr.v.a.addr.v4.s_addr; + } +#endif pr.action = PF_CHANGE_GET_TICKET; if(ioctl(dev, DIOCCHANGERULE, &pr) < 0) { @@ -636,14 +701,23 @@ error: } int -delete_filter_rule(const char * ifname, unsigned short eport, int proto) +delete_redirect_rule(const char * ifname, unsigned short eport, + int proto) +{ + return priv_delete_redirect_rule(ifname, eport, proto, NULL, NULL); +} + +static int +priv_delete_filter_rule(const char * ifname, unsigned short iport, + int proto, in_addr_t iaddr) { #ifndef PF_ENABLE_FILTER_RULES - UNUSED(ifname); UNUSED(eport); UNUSED(proto); + UNUSED(ifname); UNUSED(iport); UNUSED(proto); return 0; #else int i, n; struct pfioc_rule pr; + UNUSED(ifname); if(dev<0) { syslog(LOG_ERR, "pf device is not open"); return -1; @@ -665,8 +739,16 @@ delete_filter_rule(const char * ifname, unsigned short eport, int proto) syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m"); goto error; } - if( (eport == ntohs(pr.rule.dst.port[0])) - && (pr.rule.proto == proto) ) +#ifdef TEST +syslog(LOG_DEBUG, "%2d port=%hu proto=%d addr=%8x", + i, ntohs(pr.rule.dst.port[0]), pr.rule.proto, + pr.rule.dst.addr.v.a.addr.v4.s_addr); +/*pr.rule.dst.addr.v.a.mask.v4.s_addr*/ +#endif + if( (iport == ntohs(pr.rule.dst.port[0])) + && (pr.rule.proto == proto) && + (iaddr == pr.rule.dst.addr.v.a.addr.v4.s_addr) + ) { pr.action = PF_CHANGE_GET_TICKET; if(ioctl(dev, DIOCCHANGERULE, &pr) < 0) @@ -689,6 +771,21 @@ error: #endif } +int +delete_redirect_and_filter_rules(const char * ifname, unsigned short eport, + int proto) +{ + int r; + unsigned short iport; + in_addr_t iaddr; + r = priv_delete_redirect_rule(ifname, eport, proto, &iport, &iaddr); + if(r == 0) + { + r = priv_delete_filter_rule(ifname, iport, proto, iaddr); + } + return r; +} + int get_redirect_rule_by_index(int index, char * ifname, unsigned short * eport, diff --git a/miniupnpd/pf/obsdrdr.h b/miniupnpd/pf/obsdrdr.h index 59225fd..3defa8e 100644 --- a/miniupnpd/pf/obsdrdr.h +++ b/miniupnpd/pf/obsdrdr.h @@ -1,7 +1,7 @@ -/* $Id: obsdrdr.h,v 1.20 2012/03/05 20:36:20 nanard Exp $ */ +/* $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 Thomas Bernard + * (c) 2006-2014 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ @@ -52,13 +52,18 @@ get_redirect_rule_by_index(int index, int delete_redirect_rule(const char * ifname, unsigned short eport, int proto); -/* delete_filter_rule() +/* delete_redirect_and_filter_rules() */ int -delete_filter_rule(const char * ifname, unsigned short eport, int proto); +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 diff --git a/miniupnpd/pf/testobsdrdr.c b/miniupnpd/pf/testobsdrdr.c index 7d288c6..13d9ee5 100644 --- a/miniupnpd/pf/testobsdrdr.c +++ b/miniupnpd/pf/testobsdrdr.c @@ -1,10 +1,11 @@ -/* $Id: testobsdrdr.c,v 1.24 2012/04/18 19:42:03 nanard Exp $ */ +/* $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-2012 Thomas Bernard + * (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 @@ -17,6 +18,7 @@ int runtime_flags = 0; const char * tag = 0; const char * anchor_name = "miniupnpd"; +const char * queue = NULL; void list_rules(void); @@ -67,7 +69,7 @@ test_index(void) } int -main(int arc, char * * argv) +main(int argc, char * * argv) { char buf[32]; char desc[64]; @@ -77,6 +79,12 @@ main(int arc, char * * argv) 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) @@ -88,12 +96,15 @@ main(int arc, char * * argv) add_redirect_rule("ep0", 12123, "192.168.1.23", 1234); add_redirect_rule2("ep0", 12155, "192.168.1.155", 1255, IPPROTO_TCP); #endif - add_redirect_rule2("ep0", "8.8.8.8", 12123, "192.168.1.125", 1234, - IPPROTO_UDP, "test description", 0); -#if 0 - add_redirect_rule2("em0", 12123, "127.1.2.3", 1234, - IPPROTO_TCP, "test description tcp"); -#endif + 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(); @@ -113,20 +124,20 @@ main(int arc, char * * argv) if(delete_redirect_rule("ep0", 12123, IPPROTO_UDP) < 0) printf("delete_redirect_rule() failed\n"); - else - printf("delete_redirect_rule() succeded\n"); if(delete_redirect_rule("ep0", 12123, IPPROTO_UDP) < 0) printf("delete_redirect_rule() failed\n"); - else - printf("delete_redirect_rule() succeded\n"); -#if 0 + if(delete_redirect_and_filter_rules("em0", 12123, IPPROTO_TCP) < 0) + printf("delete_redirect_and_filter_rules() failed\n"); + test_index(); - clear_redirect_rules(); - list_rules(); -#endif + if(clear) { + clear_redirect_rules(); + clear_filter_rules(); + } + /*list_rules();*/ return 0; } diff --git a/miniupnpd/pf/testpfpinhole.c b/miniupnpd/pf/testpfpinhole.c index e09199c..ec754e2 100644 --- a/miniupnpd/pf/testpfpinhole.c +++ b/miniupnpd/pf/testpfpinhole.c @@ -1,7 +1,7 @@ -/* $Id: testpfpinhole.c,v 1.10 2012/04/22 23:12:51 nanard Exp $ */ +/* $Id: testpfpinhole.c,v 1.11 2014/02/28 16:49:15 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * (c) 2012 Thomas Bernard + * (c) 2012-2014 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ @@ -21,6 +21,7 @@ const char * tag = NULL; const char * anchor_name = "miniupnpd"; const char * queue = NULL; +#ifdef ENABLE_IPV6 static int print_pinhole(int uid) { int r; @@ -32,11 +33,11 @@ static int print_pinhole(int uid) unsigned int timestamp; u_int64_t packets, bytes; - r = get_pinhole((unsigned short)uid, - rem_host, sizeof(rem_host), &rem_port, - int_client, sizeof(int_client), &int_port, - &proto, ×tamp, - &packets, &bytes); + r = get_pinhole_info((unsigned short)uid, + rem_host, sizeof(rem_host), &rem_port, + int_client, sizeof(int_client), &int_port, + &proto, ×tamp, + &packets, &bytes); if(r < 0) { fprintf(stderr, "get_pinhole(%d) returned %d\n", uid, r); } else { @@ -47,6 +48,7 @@ static int print_pinhole(int uid) } return r; } +#endif int main(int argc, char * *argv) { diff --git a/miniupnpd/portinuse.c b/miniupnpd/portinuse.c new file mode 100644 index 0000000..7556559 --- /dev/null +++ b/miniupnpd/portinuse.c @@ -0,0 +1,401 @@ +/* $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__) || 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; + + 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 */ 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 new file mode 100644 index 0000000..7006bf2 --- /dev/null +++ b/miniupnpd/testasyncsendto.c @@ -0,0 +1,126 @@ +/* $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, (long)next_send.tv_sec, (long)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, (long)timeout.tv_sec, (long)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; +} + diff --git a/miniupnpd/testgetifaddr.c b/miniupnpd/testgetifaddr.c index 4c31907..8045b89 100644 --- a/miniupnpd/testgetifaddr.c +++ b/miniupnpd/testgetifaddr.c @@ -1,7 +1,7 @@ /* $Id: testgetifaddr.c,v 1.7 2013/04/27 15:38:57 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * (c) 2006-2013 Thomas Bernard + * (c) 2006-2014 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include @@ -10,6 +10,7 @@ #include #include #include +#include "config.h" #include "getifaddr.h" #if defined(__sun) @@ -21,6 +22,10 @@ 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; @@ -34,5 +39,16 @@ int main(int argc, char * * argv) { 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; } diff --git a/miniupnpd/testportinuse.c b/miniupnpd/testportinuse.c new file mode 100644 index 0000000..4e2625e --- /dev/null +++ b/miniupnpd/testportinuse.c @@ -0,0 +1,54 @@ +/* $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; +} diff --git a/miniupnpd/testupnpdescgen.c b/miniupnpd/testupnpdescgen.c index c2cd028..ee30a8b 100644 --- a/miniupnpd/testupnpdescgen.c +++ b/miniupnpd/testupnpdescgen.c @@ -1,7 +1,7 @@ -/* $Id: testupnpdescgen.c,v 1.30 2013/06/13 13:21:30 nanard Exp $ */ +/* $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-2013 Thomas Bernard + * (c) 2006-2014 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ @@ -38,10 +38,7 @@ char model_url[] = ROOTDEV_MODELURL; char * use_ext_ip_addr = NULL; const char * ext_if_name = "eth0"; -#ifdef ENABLE_6FC_SERVICE -int ipv6fc_firewall_enabled = 1; -int ipv6fc_inbound_pinhole_allowed = 1; -#endif +int runtime_flags = 0; int getifaddr(const char * ifname, char * buf, int len, struct in_addr * addr, struct in_addr * mask) { diff --git a/miniupnpd/upnpdescgen.c b/miniupnpd/upnpdescgen.c index f88ea0b..5e5ebfb 100644 --- a/miniupnpd/upnpdescgen.c +++ b/miniupnpd/upnpdescgen.c @@ -1,7 +1,7 @@ -/* $Id: upnpdescgen.c,v 1.74 2013/06/13 13:21:30 nanard Exp $ */ +/* $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-2013 Thomas Bernard + * (c) 2006-2014 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ @@ -1176,13 +1176,13 @@ genEventVars(int * len, const struct serviceDesc * s) case FIREWALLENABLED_MAGICALVALUE: /* see 2.4.2 of UPnP-gw-WANIPv6FirewallControl-v1-Service.pdf */ snprintf(tmp, sizeof(tmp), "%d", - ipv6fc_firewall_enabled); + 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", - ipv6fc_inbound_pinhole_allowed); + GETFLAG(IPV6FCINBOUNDDISALLOWEDMASK) ? 0 : 1); str = strcat_str(str, len, &tmplen, tmp); break; #endif diff --git a/miniupnpd/upnpevents.c b/miniupnpd/upnpevents.c index 660ccc0..628aff8 100644 --- a/miniupnpd/upnpevents.c +++ b/miniupnpd/upnpevents.c @@ -1,7 +1,7 @@ -/* $Id: upnpevents.c,v 1.27 2013/06/13 13:21:30 nanard Exp $ */ +/* $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-2013 Thomas Bernard + * (c) 2008-2014 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ @@ -149,6 +149,11 @@ 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; } @@ -192,6 +197,7 @@ 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) { @@ -210,6 +216,22 @@ upnp_event_create_notify(struct subscriber * sub) 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"); @@ -233,9 +255,12 @@ upnp_event_notify_connect(struct upnp_event_notify * obj) 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)); @@ -283,23 +308,28 @@ upnp_event_notify_connect(struct upnp_event_notify * obj) 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, sizeof(addr)) < 0) { + if(connect(obj->s, (struct sockaddr *)&addr, addrlen) < 0) { if(errno != EINPROGRESS && errno != EWOULDBLOCK) { - syslog(LOG_ERR, "%s: connect(): %m", "upnp_event_notify_connect"); + syslog(LOG_ERR, "%s: connect(%d, %s, %u): %m", + "upnp_event_notify_connect", obj->s, + obj->addrstr, addrlen); obj->state = EError; } } @@ -377,7 +407,7 @@ static void upnp_event_send(struct upnp_event_notify * obj) { int i; - syslog(LOG_DEBUG, "%s: sending event notify message to %s:%s", + 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); @@ -439,7 +469,9 @@ upnp_event_process_notify(struct upnp_event_notify * obj) } if(err != 0) { errno = err; - syslog(LOG_WARNING, "%s: connect failed: %m", "upnp_event_process_notify"); + syslog(LOG_WARNING, "%s: connect(%s%s): %m", + "upnp_event_process_notify", + obj->addrstr, obj->portstr); obj->state = EError; break; } @@ -535,6 +567,8 @@ void upnpevents_processfds(fd_set *readset, fd_set *writeset) 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); } diff --git a/miniupnpd/upnpglobalvars.c b/miniupnpd/upnpglobalvars.c index 152a689..9750f04 100644 --- a/miniupnpd/upnpglobalvars.c +++ b/miniupnpd/upnpglobalvars.c @@ -1,7 +1,7 @@ -/* $Id: upnpglobalvars.c,v 1.32 2013/12/13 14:07:09 nanard Exp $ */ +/* $Id: upnpglobalvars.c,v 1.35 2014/03/10 11:04:53 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * (c) 2006-2012 Thomas Bernard + * (c) 2006-2014 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ @@ -24,17 +24,17 @@ const char* lease_file = 0; * when NULL, getifaddr() is used */ const char * use_ext_ip_addr = 0; -/* LAN address */ -/*const char * listen_addr = 0;*/ - unsigned long downstream_bitrate = 0; unsigned long upstream_bitrate = 0; /* startup time */ time_t startup_time = 0; +#ifdef ENABLE_PCP +/* for PCP */ unsigned long int min_lifetime = 120; unsigned long int max_lifetime = 86400; +#endif int runtime_flags = 0; @@ -75,18 +75,10 @@ char model_url[MODEL_URL_MAX_LEN] = ROOTDEV_MODELURL; struct upnpperm * upnppermlist = 0; unsigned int num_upnpperm = 0; -#ifdef ENABLE_NATPMP -/* NAT-PMP */ -#if 0 -unsigned int nextnatpmptoclean_timestamp = 0; -unsigned short nextnatpmptoclean_eport = 0; -unsigned short nextnatpmptoclean_proto = 0; -#endif #ifdef PCP_SADSCP struct dscp_values* dscp_values_list = 0; unsigned int num_dscp_values = 0; #endif /*PCP_SADSCP*/ -#endif /* For automatic removal of expired rules (with LeaseDuration) */ unsigned int nextruletoclean_timestamp = 0; @@ -127,8 +119,3 @@ const char * minissdpdsocketpath = "/var/run/minissdpd.sock"; unsigned int upnp_bootid = 1; unsigned int upnp_configid = 1337; -#ifdef ENABLE_6FC_SERVICE -int ipv6fc_firewall_enabled = 1; -int ipv6fc_inbound_pinhole_allowed = 1; -#endif - diff --git a/miniupnpd/upnpglobalvars.h b/miniupnpd/upnpglobalvars.h index 80ea147..bbfccef 100644 --- a/miniupnpd/upnpglobalvars.h +++ b/miniupnpd/upnpglobalvars.h @@ -1,7 +1,7 @@ -/* $Id: upnpglobalvars.h,v 1.35 2013/06/13 13:21:30 nanard Exp $ */ +/* $Id: upnpglobalvars.h,v 1.38 2014/03/10 11:04:53 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * (c) 2006-2012 Thomas Bernard + * (c) 2006-2014 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ @@ -51,6 +51,13 @@ extern int runtime_flags; #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 #define SETFLAG(mask) runtime_flags |= mask #define GETFLAG(mask) (runtime_flags & mask) @@ -95,18 +102,10 @@ extern char model_url[]; extern struct upnpperm * upnppermlist; extern unsigned int num_upnpperm; -#ifdef ENABLE_NATPMP -/* NAT-PMP */ -#if 0 -extern unsigned int nextnatpmptoclean_timestamp; -extern unsigned short nextnatpmptoclean_eport; -extern unsigned short nextnatpmptoclean_proto; -#endif #ifdef PCP_SADSCP extern struct dscp_values* dscp_values_list; extern unsigned int num_dscp_values; #endif -#endif /* For automatic removal of expired rules (with LeaseDuration) */ extern unsigned int nextruletoclean_timestamp; @@ -147,10 +146,5 @@ extern const char * minissdpdsocketpath; extern unsigned int upnp_bootid; extern unsigned int upnp_configid; -#ifdef ENABLE_6FC_SERVICE -extern int ipv6fc_firewall_enabled; -extern int ipv6fc_inbound_pinhole_allowed; -#endif - #endif diff --git a/miniupnpd/upnphttp.c b/miniupnpd/upnphttp.c index 31456e3..97d8d44 100644 --- a/miniupnpd/upnphttp.c +++ b/miniupnpd/upnphttp.c @@ -1,8 +1,8 @@ -/* $Id: upnphttp.c,v 1.86 2013/02/07 10:26:07 nanard Exp $ */ +/* $Id: upnphttp.c,v 1.87 2014/03/14 21:26:01 nanard Exp $ */ /* Project : miniupnp * Website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * Author : Thomas Bernard - * Copyright (c) 2005-2012 Thomas Bernard + * Copyright (c) 2005-2014 Thomas Bernard * This software is subject to the conditions detailed in the * LICENCE file included in this distribution. * */ @@ -162,14 +162,22 @@ ParseHttpHeaders(struct upnphttp * h) #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] != '>' && p[n] != '\r' ) + while(p[n] != '\r') n++; - h->req_CallbackOff = p + 1 - h->req_buf; - h->req_CallbackLen = MAX(0, n - 1); + 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) { @@ -374,6 +382,9 @@ ProcessHTTPPOST_upnphttp(struct upnphttp * h) #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. */ @@ -385,56 +396,76 @@ checkCallbackURL(struct upnphttp * h) const char * p; unsigned int i; - if(h->req_CallbackOff <= 0 || h->req_CallbackLen < 8) +start_again: + if(h->req_CallbackOff <= 0 || h->req_CallbackLen < 10) return 0; - if(memcmp(h->req_buf + h->req_CallbackOff, "http://", 7) != 0) - return 0; - ipv6 = 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 + 7; + p = h->req_buf + h->req_CallbackOff + 8; if(*p == '[') { p++; ipv6 = 1; - while(*p != ']' && i < (sizeof(addrstr)-1) + while(*p != ']' && *p != '>' && i < (sizeof(addrstr)-1) && p < (h->req_buf + h->req_CallbackOff + h->req_CallbackLen)) addrstr[i++] = *(p++); } else { - while(*p != '/' && *p != ':' && i < (sizeof(addrstr)-1) + 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) - return 0; -#ifdef ENABLE_IPV6 + goto invalid; if(!h->ipv6 || (0!=memcmp(&addr, &(h->clientaddr_v6), sizeof(struct in6_addr)))) - return 0; + goto invalid; #else - return 0; + goto invalid; #endif } else { struct in_addr addr; if(inet_pton(AF_INET, addrstr, &addr) <= 0) - return 0; + goto invalid; #ifdef ENABLE_IPV6 if(h->ipv6) { if(!IN6_IS_ADDR_V4MAPPED(&(h->clientaddr_v6))) - return 0; + goto invalid; if(0!=memcmp(&addr, ((const char *)&(h->clientaddr_v6) + 12), 4)) - return 0; + goto invalid; } else { if(0!=memcmp(&addr, &(h->clientaddr), sizeof(struct in_addr))) - return 0; + goto invalid; } #else if(0!=memcmp(&addr, &(h->clientaddr), sizeof(struct in_addr))) - return 0; + 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 diff --git a/miniupnpd/upnppermissions.c b/miniupnpd/upnppermissions.c index 0fd43f7..ad02d8c 100644 --- a/miniupnpd/upnppermissions.c +++ b/miniupnpd/upnppermissions.c @@ -1,7 +1,7 @@ -/* $Id: upnppermissions.c,v 1.17 2012/02/15 22:43:34 nanard Exp $ */ +/* $Id: upnppermissions.c,v 1.18 2014/03/07 10:43:29 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * (c) 2006-2012 Thomas Bernard + * (c) 2006-2014 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ @@ -223,6 +223,23 @@ match_permission(const struct upnpperm * perm, 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, diff --git a/miniupnpd/upnppermissions.h b/miniupnpd/upnppermissions.h index dea551e..933968e 100644 --- a/miniupnpd/upnppermissions.h +++ b/miniupnpd/upnppermissions.h @@ -1,7 +1,7 @@ -/* $Id: upnppermissions.h,v 1.7 2007/02/28 18:13:18 nanard Exp $ */ +/* $Id: upnppermissions.h,v 1.10 2014/03/07 10:43:29 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * (c) 2006 Thomas Bernard + * (c) 2006-2014 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ diff --git a/miniupnpd/upnpredirect.c b/miniupnpd/upnpredirect.c index 7b398ec..5a50c6d 100644 --- a/miniupnpd/upnpredirect.c +++ b/miniupnpd/upnpredirect.c @@ -1,7 +1,7 @@ -/* $Id: upnpredirect.c,v 1.80 2012/05/01 20:08:22 nanard Exp $ */ +/* $Id: upnpredirect.c,v 1.82 2014/02/28 20:18:35 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * (c) 2006-2012 Thomas Bernard + * (c) 2006-2014 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ @@ -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", @@ -419,6 +426,8 @@ _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); diff --git a/miniupnpd/upnpsoap.c b/miniupnpd/upnpsoap.c index a8023c0..21fc33b 100644 --- a/miniupnpd/upnpsoap.c +++ b/miniupnpd/upnpsoap.c @@ -1,7 +1,7 @@ -/* $Id: upnpsoap.c,v 1.119 2013/08/19 16:16:00 nanard Exp $ */ +/* $Id: upnpsoap.c,v 1.122 2014/03/10 11:04:53 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * (c) 2006-2013 Thomas Bernard + * (c) 2006-2014 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ @@ -1220,19 +1220,21 @@ GetFirewallStatus(struct upnphttp * h, const char * action) bodylen = snprintf(body, sizeof(body), resp, action, "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1", - ipv6fc_firewall_enabled, ipv6fc_inbound_pinhole_allowed, action); + GETFLAG(IPV6FCFWDISABLEDMASK) ? 0 : 1, + GETFLAG(IPV6FCINBOUNDDISALLOWEDMASK) ? 0 : 1, + action); BuildSendAndCloseSoapResp(h, body, bodylen); } static int CheckStatus(struct upnphttp * h) { - if (!ipv6fc_firewall_enabled) + if (GETFLAG(IPV6FCFWDISABLEDMASK)) { SoapError(h, 702, "FirewallDisabled"); return 0; } - else if(!ipv6fc_inbound_pinhole_allowed) + else if(GETFLAG(IPV6FCINBOUNDDISALLOWEDMASK)) { SoapError(h, 703, "InboundPinholeNotAllowed"); return 0; @@ -1604,7 +1606,7 @@ GetOutboundPinholeTimeout(struct upnphttp * h, const char * action) int opt=0, proto=0; unsigned short iport, rport; - if (!ipv6fc_firewall_enabled) + if (GETFLAG(IPV6FCFWDISABLEDMASK)) { SoapError(h, 702, "FirewallDisabled"); return; @@ -1910,6 +1912,10 @@ ExecuteSoapAction(struct upnphttp * h, const char * action, int n) len = strlen(soapMethods[i].methodName); if(strncmp(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; } diff --git a/miniupnpd/upnputils.c b/miniupnpd/upnputils.c index 23ad242..33ebc66 100644 --- a/miniupnpd/upnputils.c +++ b/miniupnpd/upnputils.c @@ -1,7 +1,7 @@ -/* $Id: upnputils.c,v 1.7 2013/04/20 09:03:18 nanard Exp $ */ +/* $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-2013 Thomas Bernard + * (c) 2006-2014 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ @@ -93,6 +93,7 @@ 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) @@ -153,11 +154,15 @@ get_lan_for_peer(const struct sockaddr * peer) } #endif - if(lan_addr) - syslog(LOG_DEBUG, "%s: found in LAN %s %s", - "get_lan_for_peer()", lan_addr->ifname, lan_addr->str); - else - syslog(LOG_DEBUG, "%s: not found !", "get_lan_for_peer()"); + 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; } diff --git a/miniupnpd/upnputils.h b/miniupnpd/upnputils.h index 26e2c0b..a25398e 100644 --- a/miniupnpd/upnputils.h +++ b/miniupnpd/upnputils.h @@ -1,4 +1,4 @@ -/* $Id: upnputils.h,v 1.4 2013/02/06 10:50:04 nanard Exp $ */ +/* $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 @@ -29,5 +29,30 @@ set_non_blocking(int fd); 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