Merge branch 'master' into search_all

Conflicts:
	miniupnpc/miniupnpc.c
This commit is contained in:
Thomas Bernard 2014-04-14 17:32:27 +02:00
commit 4e44ef642a
63 changed files with 2824 additions and 686 deletions

3
README
View File

@ -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

View File

@ -2,4 +2,5 @@
minissdpd
testcodelength
testminissdpd
listifaces
Makefile.bak

View File

@ -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

View File

@ -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

View File

@ -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 <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
@ -8,7 +11,9 @@
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#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; i<ifc.ifc_len; /*i += sizeof(struct ifreq)*/)
printf("off index fam name address\n");
for(i = 0, j = 0; i<ifc.ifc_len; j++)
{
//const struct ifreq * ifrp = &(ifc.ifc_req[j]);
/*const struct ifreq * ifrp = &(ifc.ifc_req[j]);*/
const struct ifreq * ifrp = (const struct ifreq *)(buf + i);
i += sizeof(ifrp->ifr_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;
}

View File

@ -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
{

View File

@ -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 <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
@ -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<n; i++)
printf("%02x ", resp[i]);
printf("\n");
nresp = resp[0];
p = resp + 1;
for(i = 0; i < (int)nresp; i++) {
if(p >= 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;

View File

@ -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:

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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;
}

View File

@ -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;

View File

@ -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)
{

View File

@ -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 <net/if.h>
#include <netdb.h>
#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))

View File

@ -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, "<!--", 4)))
{
p->xml += 3;
/* ignore comments */
do
{
p->xml++;
if ((p->xml + 3) >= p->xmlend)
return;
}
while(memcmp(p->xml, "-->", 3) != 0);
p->xml += 3;
}
else if((p->xml)[0]=='<' && (p->xml)[1]!='?')
{
i = 0; elementname = ++p->xml;
while( !IS_WHITE_SPACE(*p->xml)

View File

@ -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 */

View File

@ -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

View File

@ -9,6 +9,8 @@ testgetifstats
testupnpdescgen
testupnppermissions
testgetroute
testasyncsendto
testportinuse
netfilter/testiptcrdr
netfilter/testiptcrdr_dscp
netfilter/testiptcrdr_peer

View File

@ -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

View File

@ -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 $@ $^

View File

@ -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

View File

@ -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

View File

@ -33,3 +33,4 @@ use non blocking sockets everywhere :
- NAT-PMP => OK
- not needed for miniupnpdctl
implement port_in_use() for NetBSD and FreeBSD

281
miniupnpd/asyncsendto.c Normal file
View File

@ -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 <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/queue.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <errno.h>
#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;
}
}
}
}

38
miniupnpd/asyncsendto.h Normal file
View File

@ -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

View File

@ -12,6 +12,9 @@
#include <netinet/in.h>
#include <arpa/inet.h>
#if defined(__FreeBSD__) || defined(__DragonFly__)
#ifdef __DragonFly__
#define _KERNEL_STRUCTURES
#endif
#include <net/if_var.h>
#endif
#if defined(__DragonFly__)

View File

@ -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);
}
}
}

View File

@ -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 <syslog.h>
#include <signal.h>
#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"

View File

@ -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,

View File

@ -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}

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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, "<!--", 4)))
{
p->xml += 3;
/* ignore comments */
do
{
p->xml++;
if ((p->xml + 3) >= p->xmlend)
return;
}
while(memcmp(p->xml, "-->", 3) != 0);
p->xml += 3;
}
else if((p->xml)[0]=='<' && (p->xml)[1]!='?')
{
i = 0; elementname = ++p->xml;
while( !IS_WHITE_SPACE(*p->xml)

View File

@ -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<i; j++)
{
close(sockets[j]);
sockets[j] = -1;
}
return -1;
}
goto error;
i++;
}
return 0;
error:
while(--i >= 0)
{
close(sockets[i]);
sockets[i] = -1;
}
return -1;
}
static void FillPublicAddressResponse(unsigned char * resp, in_addr_t senderaddr)
@ -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,
&timestamp, 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,
&timestamp, 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),
&timestamp, 0, 0);
if(r<0)
break;
if(sscanf(desc, "NAT-PMP %u", &timestamp) == 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), &timestamp, 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", &timestamp) == 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)
{

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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 <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <time.h>
@ -52,12 +53,15 @@ POSSIBILITY OF SUCH DAMAGE.
#include <syslog.h>
#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*/

View File

@ -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 */

View File

@ -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,

View File

@ -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

View File

@ -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 <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
@ -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;
}

View File

@ -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, &timestamp,
&packets, &bytes);
r = get_pinhole_info((unsigned short)uid,
rem_host, sizeof(rem_host), &rem_port,
int_client, sizeof(int_client), &int_port,
&proto, &timestamp,
&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)
{

401
miniupnpd/portinuse.c Normal file
View File

@ -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 <err.h>
#include <stdlib.h>
#endif
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <syslog.h>
#include <errno.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#if defined(__OpenBSD__)
#include <sys/queue.h>
#include <kvm.h>
#include <fcntl.h>
#include <nlist.h>
#include <limits.h>
#include <net/route.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
#endif
#if defined(__DragonFly__) || defined(__FreeBSD__)
#include <sys/socketvar.h>
#include <sys/sysctl.h>
/* sys/socketvar.h must be included above the following headers */
#include <netinet/in_pcb.h>
#include <netinet/tcp_var.h>
#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 */

23
miniupnpd/portinuse.h Normal file
View File

@ -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

126
miniupnpd/testasyncsendto.c Normal file
View File

@ -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 <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <string.h>
#include <unistd.h>
#include <syslog.h>
#include <errno.h>
#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;
}

View File

@ -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 <stdio.h>
@ -10,6 +10,7 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#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;
}

54
miniupnpd/testportinuse.c Normal file
View File

@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <syslog.h>
#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;
}

View File

@ -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)
{

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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, "<http://", 8) != 0) {
p = h->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

View File

@ -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,

View File

@ -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 */

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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