Merge branch 'sendto_schedule'

This commit is contained in:
Thomas Bernard 2014-02-25 11:47:30 +01:00
commit 34d0954718
12 changed files with 567 additions and 41 deletions

View File

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

View File

@ -80,7 +80,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
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 +116,11 @@ 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
EXECUTABLES = miniupnpd testupnpdescgen testgetifstats \
testupnppermissions miniupnpdctl \
testgetifaddr testgetroute
testgetifaddr testgetroute testasyncsendto
.if $(OSNAME) == "Darwin"
LIBS =
.else
@ -142,7 +143,7 @@ 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 \
$(PFOBJS) $(IPFOBJS) $(IPFWOBJS)
install: miniupnpd genuuid
@ -199,6 +200,9 @@ testupnppermissions: config.h $(TESTUPNPPERMISSIONSOBJS)
testgetroute: config.h $(TESTGETROUTEOBJS)
$(CC) $(CFLAGS) -o $@ $(TESTGETROUTEOBJS)
testasyncsendto: config.h $(TESTASYNCSENDTOOBJS)
$(CC) $(CFLAGS) -o $@ $(TESTASYNCSENDTOOBJS)
# gmake :
# $(CC) $(CFLAGS) -o $@ $^
# BSDmake :

View File

@ -47,7 +47,7 @@ BASEOBJS = 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 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
.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,8 @@ testgetifaddr: testgetifaddr.o getifaddr.o
testgetroute: testgetroute.o linux/getroute.o upnputils.o -lnfnetlink
testasyncsendto: testasyncsendto.o asyncsendto.o upnputils.o
miniupnpdctl: miniupnpdctl.o
config.h: genconfig.sh VERSION
@ -214,7 +216,7 @@ 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 miniupnpdctl.c 2>/dev/null
# DO NOT DELETE

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
MAC_OBJS = mac/getifstats.o bsd/ifacewatcher.o
IPFW_OBJS = ipfw/ipfwrdr.o ipfw/ipfwaux.o
PF_OBJS = pf/obsdrdr.o pf/pfpinhole.o

219
miniupnpd/asyncsendto.c Normal file
View File

@ -0,0 +1,219 @@
/* $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"
/* 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)
{
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, "try_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 == 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;
}
/* uncatched error */
/* remove from the list */
LIST_REMOVE(elt, entries);
free(elt);
return n;
} else {
/* remove from the list */
LIST_REMOVE(elt, entries);
free(elt);
}
}
}
return 0;
}
/* empty the list */
void finalize_sendto(void)
{
ssize_t n;
struct scheduled_send * elt;
struct scheduled_send * next;
/* TODO : improve with a select() and a short timeout */
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) {
syslog(LOG_WARNING, "sendto(): %m");
}
/* remove from the list */
LIST_REMOVE(elt, entries);
free(elt);
}
}

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

@ -2,7 +2,7 @@
# $Id: genconfig.sh,v 1.69 2014/02/24 18:41:25 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
@ -83,7 +83,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}
@ -471,6 +471,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}

View File

@ -24,6 +24,7 @@
#include "minissdp.h"
#include "upnputils.h"
#include "getroute.h"
#include "asyncsendto.h"
#include "codelength.h"
/* SSDP ip/port */
@ -330,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];
@ -399,15 +402,14 @@ 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);
if(n < 0)
{
/* XXX handle EINTR, EAGAIN, EWOULDBLOCK */
syslog(LOG_ERR, "sendto(udp): %m");
}
}
@ -488,7 +490,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
@ -497,7 +499,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");
}
@ -505,6 +506,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
@ -634,8 +651,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));
@ -678,7 +707,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;
@ -696,16 +725,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 */
@ -789,6 +834,12 @@ ProcessSSDPData(int s, const char *bufr, int n,
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, "",
@ -796,7 +847,8 @@ ProcessSSDPData(int s, const char *bufr, int n,
known_service_types[i].s, l, ver_str,
#endif
announced_host, port,
known_service_types[i].uuid);
known_service_types[i].uuid,
delay);
break;
}
}
@ -804,9 +856,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
@ -815,37 +873,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);
}
}
}
@ -895,7 +969,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

View File

@ -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
@ -705,7 +706,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)
{
@ -1263,6 +1267,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");
@ -1749,6 +1757,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;
@ -1757,6 +1797,9 @@ main(int argc, char * * argv)
syslog(LOG_ERR, "Failed to select open sockets. EXITING");
return 1; /* very serious cause of error */
}
if(try_sendto(&writeset) < 0) {
syslog(LOG_ERR, "try_sendto: %m");
}
#ifdef USE_MINIUPNPDCTL
for(ectl = ctllisthead.lh_first; ectl;)
{
@ -1983,6 +2026,21 @@ main(int argc, char * * argv)
} /* end of main loop */
shutdown:
/* 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)
{
@ -2022,14 +2080,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
@ -2038,6 +2088,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,6 +1,6 @@
/* $Id: natpmp.c,v 1.36 2014/02/01 17:17:35 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,6 +23,7 @@
#include "upnpredirect.h"
#include "commonrdr.h"
#include "upnputils.h"
#include "asyncsendto.h"
#ifdef ENABLE_NATPMP
@ -324,7 +325,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");
@ -378,7 +379,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)
{
@ -388,7 +389,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

@ -58,6 +58,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "upnpredirect.h"
#include "commonrdr.h"
#include "getifaddr.h"
#include "asyncsendto.h"
#include "pcp_msg_struct.h"
#ifdef PCP_PEER
@ -1325,7 +1326,7 @@ int ProcessIncomingPCPPacket(int s, unsigned char *buff, int len,
len = PCP_MIN_LEN;
else
len = (len + 3) & ~3; /* round up resp. length to multiple of 4 */
len = sendto(s, buff, len, 0,
len = sendto_or_schedule(s, buff, len, 0,
(struct sockaddr *)senderaddr, sizeof(struct sockaddr_in));
if( len < 0 ) {
syslog(LOG_ERR, "sendto(pcpserver): %m");

126
miniupnpd/testasyncsendto.c Normal file
View File

@ -0,0 +1,126 @@
/* $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 <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, &addr, sizeof(addr)) < 0) {
syslog(LOG_ERR, "bind(): %m");
close(s);
return 1;
}
memset(&dest_addr, 0, sizeof(struct sockaddr_in));
dest_addr.sin_family = AF_INET;
dest_addr.sin_addr.s_addr = inet_addr(DEST_IP);
dest_addr.sin_port = htons(DEST_PORT);
n = sendto_or_schedule(s, "1234", 4, 0,
(struct sockaddr *)&dest_addr, sizeof(dest_addr));
syslog(LOG_DEBUG, "sendto_or_schedule : %d", (int)n);
n = sendto_schedule(s, "1234", 4, 0,
(struct sockaddr *)&dest_addr, sizeof(dest_addr),
4400);
syslog(LOG_DEBUG, "sendto_schedule : %d", (int)n);
n = sendto_schedule(s, "1234", 4, 0,
(struct sockaddr *)&dest_addr, sizeof(dest_addr),
3000);
syslog(LOG_DEBUG, "sendto_schedule : %d", (int)n);
while ((i = get_next_scheduled_send(&next_send)) > 0) {
fd_set writefds;
int max_fd;
struct timeval timeout;
struct timeval now;
syslog(LOG_DEBUG, "get_next_scheduled_send : %d next_send=%ld.%06ld",
i, next_send.tv_sec, next_send.tv_usec);
FD_ZERO(&writefds);
max_fd = 0;
gettimeofday(&now, NULL);
i = get_sendto_fds(&writefds, &max_fd, &now);
if(now.tv_sec > next_send.tv_sec ||
(now.tv_sec == next_send.tv_sec && now.tv_usec >= next_send.tv_usec)) {
if(i > 0) {
/* dont wait */
timeout.tv_sec = 0;
} else {
/* wait 10sec :) */
timeout.tv_sec = 10;
}
timeout.tv_usec = 0;
} else {
/* ... */
timeout.tv_sec = (next_send.tv_sec - now.tv_sec);
timeout.tv_usec = (next_send.tv_usec - now.tv_usec);
if(timeout.tv_usec < 0) {
timeout.tv_usec += 1000000;
timeout.tv_sec--;
}
}
syslog(LOG_DEBUG, "get_sendto_fds() returned %d", i);
syslog(LOG_DEBUG, "select(%d, NULL, xx, NULL, %ld.%06ld)",
max_fd, timeout.tv_sec, timeout.tv_usec);
i = select(max_fd, NULL, &writefds, NULL, &timeout);
if(i < 0) {
syslog(LOG_ERR, "select: %m");
if(errno != EINTR)
break;
} else if(try_sendto(&writefds) < 0) {
syslog(LOG_ERR, "try_sendto: %m");
break;
}
}
close(s);
return 0;
}
int main(int argc, char * * argv)
{
int r;
(void)argc;
(void)argv;
openlog("testasyncsendto", LOG_CONS|LOG_PERROR, LOG_USER);
r = test();
closelog();
return r;
}