minisspd: check that peer is from a LAN

Change the list of LAN addresses/interfaces (code taken from miniupnpd)
Check that the peer is from a LAN for each SSDP packet
This commit is contained in:
Thomas Bernard 2014-11-28 17:38:54 +01:00
parent 186c504070
commit 63179a1f7f
14 changed files with 911 additions and 120 deletions

View File

@ -1,8 +1,10 @@
$Id: Changelog.txt,v 1.36 2014/11/06 10:13:36 nanard Exp $ $Id: Changelog.txt,v 1.37 2014/11/28 16:20:57 nanard Exp $
2014/11/28: 2014/11/28:
revert "listen on only 1 IPv4 if only 1 interface is specified" revert "listen on only 1 IPv4 if only 1 interface is specified"
because it prevents broadcast messages to be received because it prevents broadcast messages to be received
Change the list of LAN addresses/interfaces (code taken from miniupnpd)
Check that the peer is from a LAN for each SSDP packet
2014/11/06: 2014/11/06:
listen on only 1 IPv4 if only 1 interface is specified listen on only 1 IPv4 if only 1 interface is specified

View File

@ -1,4 +1,4 @@
# $Id: Makefile,v 1.19 2014/06/10 10:00:18 nanard Exp $ # $Id: Makefile,v 1.21 2014/11/28 16:20:57 nanard Exp $
# MiniUPnP project # MiniUPnP project
# author: Thomas Bernard # author: Thomas Bernard
# website: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ # website: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
@ -11,7 +11,7 @@
# make install (miniupnpd will be put in /usr/sbin) # make install (miniupnpd will be put in /usr/sbin)
# #
# install target is made for linux... sorry BSD users... # install target is made for linux... sorry BSD users...
#CFLAGS = -g #CFLAGS = -g -O0
CFLAGS ?= -Os CFLAGS ?= -Os
CFLAGS += -Wall CFLAGS += -Wall
CFLAGS += -W -Wstrict-prototypes CFLAGS += -W -Wstrict-prototypes
@ -23,9 +23,17 @@ RM = rm -f
INSTALL = install INSTALL = install
OS = $(shell uname -s) OS = $(shell uname -s)
ifeq ($(OS), Linux)
LDLIBS += -lnfnetlink
endif
ifeq ($(DEB_BUILD_ARCH_OS), kfreebsd)
LDLIBS += -lfreebsd-glue
endif
#EXECUTABLES = minissdpd testminissdpd listifaces #EXECUTABLES = minissdpd testminissdpd listifaces
EXECUTABLES = minissdpd testminissdpd testcodelength EXECUTABLES = minissdpd testminissdpd testcodelength
MINISSDPDOBJS = minissdpd.o openssdpsocket.o daemonize.o upnputils.o ifacewatch.o MINISSDPDOBJS = minissdpd.o openssdpsocket.o daemonize.o upnputils.o \
ifacewatch.o getroute.o getifaddr.o
TESTMINISSDPDOBJS = testminissdpd.o TESTMINISSDPDOBJS = testminissdpd.o
ALLOBJS = $(MINISSDPDOBJS) $(TESTMINISSDPDOBJS) testcodelength.o ALLOBJS = $(MINISSDPDOBJS) $(TESTMINISSDPDOBJS) testcodelength.o
@ -53,11 +61,6 @@ ifneq ($(OS), Darwin)
endif endif
minissdpd: $(MINISSDPDOBJS) minissdpd: $(MINISSDPDOBJS)
if [ "$(DEB_BUILD_ARCH_OS)" = "kfreebsd" ] ; then \
$(CC) $(CFLAGS) $(LDFLAGS) -lfreebsd-glue -o $@ $(MINISSDPDOBJS) ; \
else \
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(MINISSDPDOBJS) ; \
fi
testminissdpd: $(TESTMINISSDPDOBJS) testminissdpd: $(TESTMINISSDPDOBJS)
@ -71,10 +74,12 @@ depend:
# DO NOT DELETE # DO NOT DELETE
minissdpd.o: config.h upnputils.h openssdpsocket.h daemonize.h codelength.h minissdpd.o: config.h getifaddr.h upnputils.h openssdpsocket.h
minissdpd.o: ifacewatch.h minissdpd.o: minissdpdtypes.h daemonize.h codelength.h ifacewatch.h
openssdpsocket.o: config.h openssdpsocket.h openssdpsocket.o: config.h openssdpsocket.h minissdpdtypes.h upnputils.h
daemonize.o: daemonize.h config.h daemonize.o: daemonize.h config.h
upnputils.o: config.h upnputils.h upnputils.o: config.h upnputils.h getroute.h minissdpdtypes.h
ifacewatch.o: config.h openssdpsocket.h ifacewatch.o: config.h openssdpsocket.h minissdpdtypes.h upnputils.h
getroute.o: getroute.h upnputils.h
getifaddr.o: config.h getifaddr.h
testcodelength.o: codelength.h testcodelength.o: codelength.h

260
minissdpd/getifaddr.c Normal file
View File

@ -0,0 +1,260 @@
/* $Id: getifaddr.c,v 1.23 2014/05/06 14:40:53 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2014 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#include <stdio.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#if defined(sun)
#include <sys/sockio.h>
#endif
#include "config.h"
#include "getifaddr.h"
#if defined(USE_GETIFADDRS) || defined(ENABLE_IPV6) || defined(ENABLE_PCP)
#include <ifaddrs.h>
#endif
int
getifaddr(const char * ifname, char * buf, int len,
struct in_addr * addr, struct in_addr * mask)
{
#ifndef USE_GETIFADDRS
/* use ioctl SIOCGIFADDR. Works only for ip v4 */
/* SIOCGIFADDR struct ifreq * */
int s;
struct ifreq ifr;
int ifrlen;
struct sockaddr_in * ifaddr;
ifrlen = sizeof(ifr);
if(!ifname || ifname[0]=='\0')
return -1;
s = socket(PF_INET, SOCK_DGRAM, 0);
if(s < 0)
{
syslog(LOG_ERR, "socket(PF_INET, SOCK_DGRAM): %m");
return -1;
}
strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
if(ioctl(s, SIOCGIFFLAGS, &ifr, &ifrlen) < 0)
{
syslog(LOG_DEBUG, "ioctl(s, SIOCGIFFLAGS, ...): %m");
close(s);
return -1;
}
if ((ifr.ifr_flags & IFF_UP) == 0)
{
syslog(LOG_DEBUG, "network interface %s is down", ifname);
close(s);
return -1;
}
strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
if(ioctl(s, SIOCGIFADDR, &ifr, &ifrlen) < 0)
{
syslog(LOG_ERR, "ioctl(s, SIOCGIFADDR, ...): %m");
close(s);
return -1;
}
ifaddr = (struct sockaddr_in *)&ifr.ifr_addr;
if(addr) *addr = ifaddr->sin_addr;
if(buf)
{
if(!inet_ntop(AF_INET, &ifaddr->sin_addr, buf, len))
{
syslog(LOG_ERR, "inet_ntop(): %m");
close(s);
return -1;
}
}
if(mask)
{
strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
if(ioctl(s, SIOCGIFNETMASK, &ifr, &ifrlen) < 0)
{
syslog(LOG_ERR, "ioctl(s, SIOCGIFNETMASK, ...): %m");
close(s);
return -1;
}
#ifdef ifr_netmask
*mask = ((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr;
#else
*mask = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr;
#endif
}
close(s);
#else /* ifndef USE_GETIFADDRS */
/* Works for all address families (both ip v4 and ip v6) */
struct ifaddrs * ifap;
struct ifaddrs * ife;
if(!ifname || ifname[0]=='\0')
return -1;
if(getifaddrs(&ifap)<0)
{
syslog(LOG_ERR, "getifaddrs: %m");
return -1;
}
for(ife = ifap; ife; ife = ife->ifa_next)
{
/* skip other interfaces if one was specified */
if(ifname && (0 != strcmp(ifname, ife->ifa_name)))
continue;
if(ife->ifa_addr == NULL)
continue;
switch(ife->ifa_addr->sa_family)
{
case AF_INET:
if(buf)
{
inet_ntop(ife->ifa_addr->sa_family,
&((struct sockaddr_in *)ife->ifa_addr)->sin_addr,
buf, len);
}
if(addr) *addr = ((struct sockaddr_in *)ife->ifa_addr)->sin_addr;
if(mask) *mask = ((struct sockaddr_in *)ife->ifa_netmask)->sin_addr;
break;
/*
case AF_INET6:
inet_ntop(ife->ifa_addr->sa_family,
&((struct sockaddr_in6 *)ife->ifa_addr)->sin6_addr,
buf, len);
*/
}
}
freeifaddrs(ifap);
#endif
return 0;
}
#ifdef ENABLE_PCP
int getifaddr_in6(const char * ifname, int af, struct in6_addr * addr)
{
#if defined(ENABLE_IPV6) || defined(USE_GETIFADDRS)
struct ifaddrs * ifap;
struct ifaddrs * ife;
#ifdef ENABLE_IPV6
const struct sockaddr_in6 * tmpaddr;
#endif /* ENABLE_IPV6 */
int found = 0;
if(!ifname || ifname[0]=='\0')
return -1;
if(getifaddrs(&ifap)<0)
{
syslog(LOG_ERR, "getifaddrs: %m");
return -1;
}
for(ife = ifap; ife && !found; ife = ife->ifa_next)
{
/* skip other interfaces if one was specified */
if(ifname && (0 != strcmp(ifname, ife->ifa_name)))
continue;
if(ife->ifa_addr == NULL)
continue;
if (ife->ifa_addr->sa_family != af)
continue;
switch(ife->ifa_addr->sa_family)
{
case AF_INET:
/* IPv4-mapped IPv6 address ::ffff:1.2.3.4 */
memset(addr->s6_addr, 0, 10);
addr->s6_addr[10] = 0xff;
addr->s6_addr[11] = 0xff;
memcpy(addr->s6_addr + 12,
&(((struct sockaddr_in *)ife->ifa_addr)->sin_addr.s_addr),
4);
found = 1;
break;
#ifdef ENABLE_IPV6
case AF_INET6:
tmpaddr = (const struct sockaddr_in6 *)ife->ifa_addr;
if(!IN6_IS_ADDR_LOOPBACK(&tmpaddr->sin6_addr)
&& !IN6_IS_ADDR_LINKLOCAL(&tmpaddr->sin6_addr))
{
memcpy(addr->s6_addr,
&tmpaddr->sin6_addr,
16);
found = 1;
}
break;
#endif /* ENABLE_IPV6 */
}
}
freeifaddrs(ifap);
return (found ? 0 : -1);
#else /* defined(ENABLE_IPV6) || defined(USE_GETIFADDRS) */
/* IPv4 only */
struct in_addr addr4;
if(af != AF_INET)
return -1;
if(getifaddr(ifname, NULL, 0, &addr4, NULL) < 0)
return -1;
/* IPv4-mapped IPv6 address ::ffff:1.2.3.4 */
memset(addr->s6_addr, 0, 10);
addr->s6_addr[10] = 0xff;
addr->s6_addr[11] = 0xff;
memcpy(addr->s6_addr + 12, &addr4.s_addr, 4);
return 0;
#endif
}
#endif /* ENABLE_PCP */
#ifdef ENABLE_IPV6
int
find_ipv6_addr(const char * ifname,
char * dst, int n)
{
struct ifaddrs * ifap;
struct ifaddrs * ife;
const struct sockaddr_in6 * addr;
char buf[64];
int r = 0;
if(!dst)
return -1;
if(getifaddrs(&ifap)<0)
{
syslog(LOG_ERR, "getifaddrs: %m");
return -1;
}
for(ife = ifap; ife; ife = ife->ifa_next)
{
/* skip other interfaces if one was specified */
if(ifname && (0 != strcmp(ifname, ife->ifa_name)))
continue;
if(ife->ifa_addr == NULL)
continue;
if(ife->ifa_addr->sa_family == AF_INET6)
{
addr = (const struct sockaddr_in6 *)ife->ifa_addr;
if(!IN6_IS_ADDR_LOOPBACK(&addr->sin6_addr)
&& !IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr))
{
inet_ntop(ife->ifa_addr->sa_family,
&addr->sin6_addr,
buf, sizeof(buf));
/* add brackets */
snprintf(dst, n, "[%s]", buf);
r = 1;
}
}
}
freeifaddrs(ifap);
return r;
}
#endif

32
minissdpd/getifaddr.h Normal file
View File

@ -0,0 +1,32 @@
/* $Id: getifaddr.h,v 1.10 2014/05/06 14:40:53 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2013 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#ifndef GETIFADDR_H_INCLUDED
#define GETIFADDR_H_INCLUDED
struct in_addr;
struct in6_addr;
/* getifaddr()
* take a network interface name and write the
* ip v4 address as text in the buffer
* returns: 0 success, -1 failure */
int
getifaddr(const char * ifname, char * buf, int len,
struct in_addr * addr, struct in_addr * mask);
int
getifaddr_in6(const char * ifname, int af, struct in6_addr* addr);
/* find a non link local IP v6 address for the interface.
* if ifname is NULL, look for all interfaces */
int
find_ipv6_addr(const char * ifname,
char * dst, int n);
#endif

276
minissdpd/getroute.c Normal file
View File

@ -0,0 +1,276 @@
/* $Id: getroute.c,v 1.3 2014/11/28 16:30:37 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 <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#ifdef __linux__
/*#include <linux/in_route.h>*/
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <libnfnetlink/libnfnetlink.h>
#else /* __linux__ */
#include <net/if.h>
#include <net/route.h>
#include <netinet/in.h>
#ifdef AF_LINK
#include <net/if_dl.h>
#endif /* AF_LINK */
#endif /* __linux__ */
#include "getroute.h"
#include "upnputils.h"
int
get_src_for_route_to(const struct sockaddr * dst,
void * src, size_t * src_len,
int * index)
{
#if __linux__
int fd = -1;
struct nlmsghdr *h;
int status;
struct {
struct nlmsghdr n;
struct rtmsg r;
char buf[1024];
} req;
struct sockaddr_nl nladdr;
struct iovec iov = {
.iov_base = (void*) &req.n,
};
struct msghdr msg = {
.msg_name = &nladdr,
.msg_namelen = sizeof(nladdr),
.msg_iov = &iov,
.msg_iovlen = 1,
};
const struct sockaddr_in * dst4;
const struct sockaddr_in6 * dst6;
memset(&req, 0, sizeof(req));
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
req.n.nlmsg_flags = NLM_F_REQUEST;
req.n.nlmsg_type = RTM_GETROUTE;
req.r.rtm_family = dst->sa_family;
req.r.rtm_table = 0;
req.r.rtm_protocol = 0;
req.r.rtm_scope = 0;
req.r.rtm_type = 0;
req.r.rtm_src_len = 0;
req.r.rtm_dst_len = 0;
req.r.rtm_tos = 0;
{
char dst_str[128];
sockaddr_to_string(dst, dst_str, sizeof(dst_str));
syslog(LOG_DEBUG, "get_src_for_route_to (%s)", dst_str);
}
/* add address */
if(dst->sa_family == AF_INET) {
dst4 = (const struct sockaddr_in *)dst;
nfnl_addattr_l(&req.n, sizeof(req), RTA_DST, &dst4->sin_addr, 4);
req.r.rtm_dst_len = 32;
} else {
dst6 = (const struct sockaddr_in6 *)dst;
nfnl_addattr_l(&req.n, sizeof(req), RTA_DST, &dst6->sin6_addr, 16);
req.r.rtm_dst_len = 128;
}
fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (fd < 0) {
syslog(LOG_ERR, "socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE) : %m");
return -1;
}
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
req.n.nlmsg_seq = 1;
iov.iov_len = req.n.nlmsg_len;
status = sendmsg(fd, &msg, 0);
if (status < 0) {
syslog(LOG_ERR, "sendmsg(rtnetlink) : %m");
goto error;
}
memset(&req, 0, sizeof(req));
for(;;) {
iov.iov_len = sizeof(req);
status = recvmsg(fd, &msg, 0);
if(status < 0) {
if (errno == EINTR || errno == EAGAIN)
continue;
syslog(LOG_ERR, "recvmsg(rtnetlink) %m");
goto error;
}
if(status == 0) {
syslog(LOG_ERR, "recvmsg(rtnetlink) EOF");
goto error;
}
for (h = (struct nlmsghdr*)&req.n; status >= (int)sizeof(*h); ) {
int len = h->nlmsg_len;
int l = len - sizeof(*h);
if (l<0 || len>status) {
if (msg.msg_flags & MSG_TRUNC) {
syslog(LOG_ERR, "Truncated message");
}
syslog(LOG_ERR, "malformed message: len=%d", len);
goto error;
}
if(nladdr.nl_pid != 0 || h->nlmsg_seq != 1/*seq*/) {
syslog(LOG_ERR, "wrong seq = %d\n", h->nlmsg_seq);
/* Don't forget to skip that message. */
status -= NLMSG_ALIGN(len);
h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
continue;
}
if(h->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
syslog(LOG_ERR, "NLMSG_ERROR %d : %s", err->error, strerror(-err->error));
goto error;
}
if(h->nlmsg_type == RTM_NEWROUTE) {
struct rtattr * rta;
int len = h->nlmsg_len;
len -= NLMSG_LENGTH(sizeof(struct rtmsg));
for(rta = RTM_RTA(NLMSG_DATA((h))); RTA_OK(rta, len); rta = RTA_NEXT(rta,len)) {
unsigned char * data = RTA_DATA(rta);
if(rta->rta_type == RTA_PREFSRC) {
if(src_len && src) {
if(*src_len < RTA_PAYLOAD(rta)) {
syslog(LOG_WARNING, "cannot copy src: %u<%lu",
(unsigned)*src_len, RTA_PAYLOAD(rta));
goto error;
}
*src_len = RTA_PAYLOAD(rta);
memcpy(src, data, RTA_PAYLOAD(rta));
}
} else if(rta->rta_type == RTA_OIF) {
if(index)
memcpy(index, data, sizeof(int));
}
}
close(fd);
return 0;
}
status -= NLMSG_ALIGN(len);
h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
}
}
syslog(LOG_WARNING, "get_src_for_route_to() : src not found");
error:
if(fd >= 0)
close(fd);
return -1;
#else /* __linux__ */
int found = 0;
int s;
int l, i;
char * p;
struct sockaddr * sa;
struct {
struct rt_msghdr m_rtm;
char m_space[512];
} m_rtmsg;
#define rtm m_rtmsg.m_rtm
if(dst == NULL)
return -1;
#ifdef __APPLE__
if(dst->sa_family == AF_INET6) {
syslog(LOG_ERR, "Sorry, get_src_for_route_to() is known to fail with IPV6 on OS X...");
return -1;
}
#endif
s = socket(PF_ROUTE, SOCK_RAW, dst->sa_family);
if(s < 0) {
syslog(LOG_ERR, "socket(PF_ROUTE) failed : %m");
return -1;
}
memset(&rtm, 0, sizeof(rtm));
rtm.rtm_type = RTM_GET;
rtm.rtm_flags = RTF_UP;
rtm.rtm_version = RTM_VERSION;
rtm.rtm_seq = 1;
rtm.rtm_addrs = RTA_DST; /* destination address */
memcpy(m_rtmsg.m_space, dst, sizeof(struct sockaddr));
rtm.rtm_msglen = sizeof(struct rt_msghdr) + sizeof(struct sockaddr);
if(write(s, &m_rtmsg, rtm.rtm_msglen) < 0) {
syslog(LOG_ERR, "write: %m");
close(s);
return -1;
}
do {
l = read(s, &m_rtmsg, sizeof(m_rtmsg));
if(l<0) {
syslog(LOG_ERR, "read: %m");
close(s);
return -1;
}
syslog(LOG_DEBUG, "read l=%d seq=%d pid=%d",
l, rtm.rtm_seq, rtm.rtm_pid);
} while(l > 0 && (rtm.rtm_pid != getpid() || rtm.rtm_seq != 1));
close(s);
p = m_rtmsg.m_space;
if(rtm.rtm_addrs) {
for(i=1; i<0x8000; i <<= 1) {
if(i & rtm.rtm_addrs) {
char tmp[256] = { 0 };
sa = (struct sockaddr *)p;
sockaddr_to_string(sa, tmp, sizeof(tmp));
syslog(LOG_DEBUG, "type=%d sa_len=%d sa_family=%d %s",
i, SA_LEN(sa), sa->sa_family, tmp);
if((i == RTA_DST || i == RTA_GATEWAY) &&
(src_len && src)) {
size_t len = 0;
void * paddr = NULL;
if(sa->sa_family == AF_INET) {
paddr = &((struct sockaddr_in *)sa)->sin_addr;
len = sizeof(struct in_addr);
} else if(sa->sa_family == AF_INET6) {
paddr = &((struct sockaddr_in6 *)sa)->sin6_addr;
len = sizeof(struct in6_addr);
}
if(paddr) {
if(*src_len < len) {
syslog(LOG_WARNING, "cannot copy src. %u<%u",
(unsigned)*src_len, (unsigned)len);
return -1;
}
memcpy(src, paddr, len);
*src_len = len;
found = 1;
}
}
#ifdef AF_LINK
if(sa->sa_family == AF_LINK) {
struct sockaddr_dl * sdl = (struct sockaddr_dl *)sa;
if(index)
*index = sdl->sdl_index;
}
#endif
p += SA_LEN(sa);
}
}
}
return found ? 0 : -1;
#endif /* __linux__ */
}

17
minissdpd/getroute.h Normal file
View File

@ -0,0 +1,17 @@
/* $Id: getroute.h,v 1.3 2013/02/06 10:50:04 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2013 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#ifndef GETROUTE_H_INCLUDED
#define GETROUTE_H_INCLUDED
int
get_src_for_route_to(const struct sockaddr * dst,
void * src, size_t * src_len,
int * index);
#endif

View File

@ -1,4 +1,4 @@
/* $Id: ifacewatch.c,v 1.13 2012/05/21 17:13:11 nanard Exp $ */ /* $Id: ifacewatch.c,v 1.15 2014/11/28 16:30:37 nanard Exp $ */
/* MiniUPnP project /* MiniUPnP project
* (c) 2011-2012 Thomas Bernard * (c) 2011-2012 Thomas Bernard
* website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
@ -28,6 +28,9 @@
#include "openssdpsocket.h" #include "openssdpsocket.h"
#include "upnputils.h" #include "upnputils.h"
#include "minissdpdtypes.h"
extern struct lan_addr_list lan_addrs;
#ifndef __linux__ #ifndef __linux__
#if defined(__OpenBSD__) || defined(__FreeBSD__) #if defined(__OpenBSD__) || defined(__FreeBSD__)
@ -81,11 +84,10 @@ OpenAndConfInterfaceWatchSocket(void)
* Process the message and add/drop multicast membership if needed * Process the message and add/drop multicast membership if needed
*/ */
int int
ProcessInterfaceWatch(int s, int s_ssdp, int s_ssdp6, ProcessInterfaceWatch(int s, int s_ssdp, int s_ssdp6)
int n_if_addr, const char * * if_addr)
{ {
struct lan_addr_s * lan_addr;
ssize_t len; ssize_t len;
int i;
char buffer[4096]; char buffer[4096];
#ifdef __linux__ #ifdef __linux__
struct iovec iov; struct iovec iov;
@ -166,14 +168,14 @@ ProcessInterfaceWatch(int s, int s_ssdp, int s_ssdp6,
syslog(LOG_INFO, "%s: %s/%d %s", syslog(LOG_INFO, "%s: %s/%d %s",
is_del ? "RTM_DELADDR" : "RTM_NEWADDR", is_del ? "RTM_DELADDR" : "RTM_NEWADDR",
address, ifa->ifa_prefixlen, ifname); address, ifa->ifa_prefixlen, ifname);
for(i = 0; i < n_if_addr; i++) { for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next) {
if((0 == strcmp(address, if_addr[i])) || if((0 == strcmp(address, lan_addr->str)) ||
(0 == strcmp(ifname, if_addr[i])) || (0 == strcmp(ifname, lan_addr->ifname)) ||
(ifa->ifa_index == if_nametoindex(if_addr[i]))) { (ifa->ifa_index == lan_addr->index)) {
if(ifa->ifa_family == AF_INET && address[0] != '\0') if(ifa->ifa_family == AF_INET)
AddDropMulticastMembership(s_ssdp, address, 0, is_del); AddDropMulticastMembership(s_ssdp, lan_addr, 0, is_del);
else if(ifa->ifa_family == AF_INET6) else if(ifa->ifa_family == AF_INET6)
AddDropMulticastMembership(s_ssdp6, if_addr[i], 1, is_del); AddDropMulticastMembership(s_ssdp6, lan_addr, 1, is_del);
break; break;
} }
} }
@ -299,14 +301,14 @@ ProcessInterfaceWatch(int s, int s_ssdp, int s_ssdp6,
syslog(LOG_INFO, "%s: %s/%d %s", syslog(LOG_INFO, "%s: %s/%d %s",
is_del ? "RTM_DELADDR" : "RTM_NEWADDR", is_del ? "RTM_DELADDR" : "RTM_NEWADDR",
address, prefixlen, ifname); address, prefixlen, ifname);
for(i = 0; i < n_if_addr; i++) { for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next) {
if(0 == strcmp(address, if_addr[i]) || if((0 == strcmp(address, lan_addr->str)) ||
0 == strcmp(ifname, if_addr[i]) || (0 == strcmp(ifname, lan_addr->ifname)) ||
ifam->ifam_index == if_nametoindex(if_addr[i])) { (ifam->ifam_index == lan_addr->index)) {
if(family == AF_INET && address[0] != '\0') if(family == AF_INET)
AddDropMulticastMembership(s_ssdp, address, 0, is_del); AddDropMulticastMembership(s_ssdp, lan_addr, 0, is_del);
else if(family == AF_INET6) else if(family == AF_INET6)
AddDropMulticastMembership(s_ssdp6, if_addr[i], 1, is_del); AddDropMulticastMembership(s_ssdp6, lan_addr, 1, is_del);
break; break;
} }
} }

View File

@ -1,7 +1,7 @@
/* $Id: ifacewatch.h,v 1.1 2011/07/29 15:21:13 nanard Exp $ */ /* $Id: ifacewatch.h,v 1.4 2014/11/28 16:20:57 nanard Exp $ */
/* MiniUPnP project /* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2011 Thomas Bernard * (c) 2011-2014 Thomas Bernard
* This software is subject to the conditions detailed * This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */ * in the LICENCE file provided within the distribution */
@ -12,7 +12,6 @@ int
OpenAndConfInterfaceWatchSocket(void); OpenAndConfInterfaceWatchSocket(void);
int int
ProcessInterfaceWatch(int s, int s_ssdp, int s_ssdp6, ProcessInterfaceWatch(int s, int s_ssdp, int s_ssdp6);
int n_if_addr, const char * * if_addr);
#endif #endif

View File

@ -1,4 +1,4 @@
/* $Id: minissdpd.c,v 1.40 2014/11/28 14:31:44 nanard Exp $ */ /* $Id: minissdpd.c,v 1.41 2014/11/28 16:20:57 nanard Exp $ */
/* MiniUPnP project /* MiniUPnP project
* (c) 2007-2014 Thomas Bernard * (c) 2007-2014 Thomas Bernard
* website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
@ -30,11 +30,13 @@
#include <pwd.h> #include <pwd.h>
#include <grp.h> #include <grp.h>
#include "getifaddr.h"
#include "upnputils.h" #include "upnputils.h"
#include "openssdpsocket.h" #include "openssdpsocket.h"
#include "daemonize.h" #include "daemonize.h"
#include "codelength.h" #include "codelength.h"
#include "ifacewatch.h" #include "ifacewatch.h"
#include "minissdpdtypes.h"
/* current request management stucture */ /* current request management stucture */
struct reqelem { struct reqelem {
@ -42,7 +44,7 @@ struct reqelem {
LIST_ENTRY(reqelem) entries; LIST_ENTRY(reqelem) entries;
}; };
/* divice data structures */ /* device data structures */
struct header { struct header {
const char * p; /* string pointer */ const char * p; /* string pointer */
int l; /* string length */ int l; /* string length */
@ -70,6 +72,95 @@ struct device * devlist = 0;
unsigned int upnp_bootid = 1; unsigned int upnp_bootid = 1;
unsigned int upnp_configid = 1337; unsigned int upnp_configid = 1337;
/* LAN interfaces/addresses */
struct lan_addr_list lan_addrs;
/* functions */
/* parselanaddr()
* parse address with mask
* ex: 192.168.1.1/24 or 192.168.1.1/255.255.255.0
*
* Can also use the interface name (ie eth0)
*
* return value :
* 0 : ok
* -1 : error */
static int
parselanaddr(struct lan_addr_s * lan_addr, const char * str)
{
const char * p;
int n;
char tmp[16];
memset(lan_addr, 0, sizeof(struct lan_addr_s));
p = str;
while(*p && *p != '/' && !isspace(*p))
p++;
n = p - str;
if(!isdigit(str[0]) && n < (int)sizeof(lan_addr->ifname)) {
/* not starting with a digit : suppose it is an interface name */
memcpy(lan_addr->ifname, str, n);
lan_addr->ifname[n] = '\0';
if(getifaddr(lan_addr->ifname, lan_addr->str, sizeof(lan_addr->str),
&lan_addr->addr, &lan_addr->mask) < 0)
goto parselan_error;
/*printf("%s => %s\n", lan_addr->ifname, lan_addr->str);*/
} else {
if(n>15)
goto parselan_error;
memcpy(lan_addr->str, str, n);
lan_addr->str[n] = '\0';
if(!inet_aton(lan_addr->str, &lan_addr->addr))
goto parselan_error;
}
if(*p == '/') {
const char * q = ++p;
while(*p && isdigit(*p))
p++;
if(*p=='.') {
/* parse mask in /255.255.255.0 format */
while(*p && (*p=='.' || isdigit(*p)))
p++;
n = p - q;
if(n>15)
goto parselan_error;
memcpy(tmp, q, n);
tmp[n] = '\0';
if(!inet_aton(tmp, &lan_addr->mask))
goto parselan_error;
} else {
/* it is a /24 format */
int nbits = atoi(q);
if(nbits > 32 || nbits < 0)
goto parselan_error;
lan_addr->mask.s_addr = htonl(nbits ? (0xffffffffu << (32 - nbits)) : 0);
}
} else if(lan_addr->mask.s_addr == 0) {
/* by default, networks are /24 */
lan_addr->mask.s_addr = htonl(0xffffff00u);
}
#ifdef ENABLE_IPV6
if(lan_addr->ifname[0] != '\0') {
lan_addr->index = if_nametoindex(lan_addr->ifname);
if(lan_addr->index == 0)
fprintf(stderr, "Cannot get index for network interface %s",
lan_addr->ifname);
} else {
fprintf(stderr,
"Error: please specify LAN network interface by name instead of IPv4 address : %s\n",
str);
return -1;
}
#endif /* ENABLE_IPV6 */
return 0;
parselan_error:
fprintf(stderr, "Error parsing address/mask (or interface name) : %s\n",
str);
return -1;
}
static const char * static const char *
nts_to_str(int nts) nts_to_str(int nts)
{ {
@ -260,8 +351,7 @@ processMSEARCH(int s, const char * st, int st_len,
inet_ntoa(((const struct sockaddr_in *)addr)->sin_addr), inet_ntoa(((const struct sockaddr_in *)addr)->sin_addr),
ntohs(((const struct sockaddr_in *)addr)->sin_port), ntohs(((const struct sockaddr_in *)addr)->sin_port),
st_len, st); st_len, st);
#endif #endif /* ENABLE_IPV6 */
/* TODO : ignore packet if not coming from a LAN */
if(st_len==8 && (0==memcmp(st, "ssdp:all", 8))) { if(st_len==8 && (0==memcmp(st, "ssdp:all", 8))) {
/* send a response for all services */ /* send a response for all services */
for(serv = servicelisthead.lh_first; for(serv = servicelisthead.lh_first;
@ -343,6 +433,16 @@ ParseSSDPPacket(int s, const char * p, ssize_t n,
const char * st = NULL; const char * st = NULL;
int st_len = 0; int st_len = 0;
/* first check from what subnet is the sender */
if(get_lan_for_peer(addr) == NULL) {
char addr_str[64];
sockaddr_to_string(addr, addr_str, sizeof(addr_str));
syslog(LOG_WARNING, "peer %s is not from a LAN",
addr_str);
return 0;
}
/* do the parsing */
memset(headers, 0, sizeof(headers)); memset(headers, 0, sizeof(headers));
for(methodlen = 0; for(methodlen = 0;
methodlen < n && (isalpha(p[methodlen]) || p[methodlen]=='-'); methodlen < n && (isalpha(p[methodlen]) || p[methodlen]=='-');
@ -870,8 +970,7 @@ int main(int argc, char * * argv)
struct reqelem * req; struct reqelem * req;
struct reqelem * reqnext; struct reqelem * reqnext;
fd_set readfds; fd_set readfds;
const char * if_addr[MAX_IF_ADDR]; struct lan_addr_s * lan_addr;
int n_if_addr = 0;
int i; int i;
const char * sockpath = "/var/run/minissdpd.sock"; const char * sockpath = "/var/run/minissdpd.sock";
const char * pidfilename = "/var/run/minissdpd.pid"; const char * pidfilename = "/var/run/minissdpd.pid";
@ -889,15 +988,22 @@ int main(int argc, char * * argv)
LIST_INIT(&reqlisthead); LIST_INIT(&reqlisthead);
LIST_INIT(&servicelisthead); LIST_INIT(&servicelisthead);
LIST_INIT(&lan_addrs);
/* process command line */ /* process command line */
for(i=1; i<argc; i++) for(i=1; i<argc; i++)
{ {
if(0==strcmp(argv[i], "-i")) { if(0==strcmp(argv[i], "-i")) {
if(n_if_addr < MAX_IF_ADDR) lan_addr = malloc(sizeof(struct lan_addr_s));
if_addr[n_if_addr++] = argv[++i]; if(lan_addr == NULL) {
else fprintf(stderr, "malloc(%d) FAILED\n", (int)sizeof(struct lan_addr_s));
syslog(LOG_WARNING, "Max number of interface address set to %d, " break;
"ignoring %s", MAX_IF_ADDR, argv[++i]); }
if(parselanaddr(lan_addr, argv[++i]) != 0) {
fprintf(stderr, "can't parse \"%s\" as a valid address or interface name\n", argv[i]);
free(lan_addr);
} else {
LIST_INSERT_HEAD(&lan_addrs, lan_addr, list);
}
} else if(0==strcmp(argv[i], "-d")) } else if(0==strcmp(argv[i], "-d"))
debug_flag = 1; debug_flag = 1;
else if(0==strcmp(argv[i], "-s")) else if(0==strcmp(argv[i], "-s"))
@ -909,7 +1015,7 @@ int main(int argc, char * * argv)
ipv6 = 1; ipv6 = 1;
#endif /* ENABLE_IPV6 */ #endif /* ENABLE_IPV6 */
} }
if(n_if_addr < 1) if(lan_addrs.lh_first == NULL)
{ {
fprintf(stderr, fprintf(stderr,
"Usage: %s [-d] " "Usage: %s [-d] "
@ -920,7 +1026,8 @@ int main(int argc, char * * argv)
"-i <interface> [-i <interface2>] ...\n", "-i <interface> [-i <interface2>] ...\n",
argv[0]); argv[0]);
fprintf(stderr, fprintf(stderr,
"\n <interface> is either an IPv4 address such as 192.168.1.42, or an\ninterface name such as eth0.\n"); "\n <interface> is either an IPv4 address with mask such as\n"
" 192.168.1.42/255.255.255.0, or an interface name such as eth0.\n");
fprintf(stderr, fprintf(stderr,
"\n By default, socket will be open as %s\n" "\n By default, socket will be open as %s\n"
"and pid written to file %s\n", "and pid written to file %s\n",
@ -959,7 +1066,7 @@ int main(int argc, char * * argv)
/* open route/interface config changes socket */ /* open route/interface config changes socket */
s_ifacewatch = OpenAndConfInterfaceWatchSocket(); s_ifacewatch = OpenAndConfInterfaceWatchSocket();
/* open UDP socket(s) for receiving SSDP packets */ /* open UDP socket(s) for receiving SSDP packets */
s_ssdp = OpenAndConfSSDPReceiveSocket(n_if_addr, if_addr, 0); s_ssdp = OpenAndConfSSDPReceiveSocket(0);
if(s_ssdp < 0) if(s_ssdp < 0)
{ {
syslog(LOG_ERR, "Cannot open socket for receiving SSDP messages, exiting"); syslog(LOG_ERR, "Cannot open socket for receiving SSDP messages, exiting");
@ -968,7 +1075,7 @@ int main(int argc, char * * argv)
} }
#ifdef ENABLE_IPV6 #ifdef ENABLE_IPV6
if(ipv6) { if(ipv6) {
s_ssdp6 = OpenAndConfSSDPReceiveSocket(n_if_addr, if_addr, 1); s_ssdp6 = OpenAndConfSSDPReceiveSocket(1);
if(s_ssdp6 < 0) if(s_ssdp6 < 0)
{ {
syslog(LOG_ERR, "Cannot open socket for receiving SSDP messages (IPv6), exiting"); syslog(LOG_ERR, "Cannot open socket for receiving SSDP messages (IPv6), exiting");
@ -1183,9 +1290,10 @@ int main(int argc, char * * argv)
} }
/* processing route/network interface config changes */ /* processing route/network interface config changes */
if((s_ifacewatch >= 0) && FD_ISSET(s_ifacewatch, &readfds)) { if((s_ifacewatch >= 0) && FD_ISSET(s_ifacewatch, &readfds)) {
ProcessInterfaceWatch(s_ifacewatch, s_ssdp, s_ssdp6, n_if_addr, if_addr); ProcessInterfaceWatch(s_ifacewatch, s_ssdp, s_ssdp6);
} }
} }
syslog(LOG_DEBUG, "quitting...");
/* closing and cleaning everything */ /* closing and cleaning everything */
quit: quit:
@ -1209,6 +1317,18 @@ quit:
close(s_ifacewatch); close(s_ifacewatch);
s_ifacewatch = -1; s_ifacewatch = -1;
} }
/* empty LAN interface/address list */
while(lan_addrs.lh_first != NULL) {
lan_addr = lan_addrs.lh_first;
LIST_REMOVE(lan_addrs.lh_first, list);
free(lan_addr);
}
/* empty device list */
while(devlist != NULL) {
struct device * next = devlist->next;
free(devlist);
devlist = next;
}
if(unlink(pidfilename) < 0) if(unlink(pidfilename) < 0)
syslog(LOG_ERR, "unlink(%s): %m", pidfilename); syslog(LOG_ERR, "unlink(%s): %m", pidfilename);
closelog(); closelog();

View File

@ -0,0 +1,28 @@
/* $Id: minissdpdtypes.h,v 1.1 2014/11/28 16:20:58 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2014 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#ifndef MINISSDPDTYPES_H_INCLUDED
#define MINISSDPDTYPES_H_INCLUDED
#include "config.h"
#include <netinet/in.h>
#include <net/if.h>
#include <sys/queue.h>
/* structure and list for storing lan addresses
* with ascii representation and mask */
struct lan_addr_s {
char ifname[IFNAMSIZ]; /* example: eth0 */
#ifdef ENABLE_IPV6
unsigned int index; /* use if_nametoindex() */
#endif /* ENABLE_IPV6 */
char str[16]; /* example: 192.168.0.1 */
struct in_addr addr, mask; /* ip/mask */
LIST_ENTRY(lan_addr_s) list;
};
LIST_HEAD(lan_addr_list, lan_addr_s);
#endif /* MINISSDPDTYPES_H_INCLUDED */

View File

@ -1,4 +1,4 @@
/* $Id: openssdpsocket.c,v 1.14 2014/11/06 10:13:36 nanard Exp $ */ /* $Id: openssdpsocket.c,v 1.15 2014/11/28 16:20:58 nanard Exp $ */
/* MiniUPnP project /* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2014 Thomas Bernard * (c) 2006-2014 Thomas Bernard
@ -18,6 +18,9 @@
#include "openssdpsocket.h" #include "openssdpsocket.h"
#include "upnputils.h" #include "upnputils.h"
#include "minissdpdtypes.h"
extern struct lan_addr_list lan_addrs;
/* SSDP ip/port */ /* SSDP ip/port */
#define SSDP_PORT (1900) #define SSDP_PORT (1900)
@ -26,44 +29,6 @@
#define LL_SSDP_MCAST_ADDR ("FF02::C") #define LL_SSDP_MCAST_ADDR ("FF02::C")
#define SL_SSDP_MCAST_ADDR ("FF05::C") #define SL_SSDP_MCAST_ADDR ("FF05::C")
/**
* Get the IPv4 address from a string
* representing the address or the interface name
*/
static in_addr_t
GetIfAddrIPv4(const char * ifaddr)
{
in_addr_t addr;
int s;
struct ifreq ifr;
int ifrlen;
/* let's suppose ifaddr is a IPv4 address
* such as 192.168.1.1 */
addr = inet_addr(ifaddr);
if(addr != INADDR_NONE)
return addr;
/* let's suppose the ifaddr was in fact an interface name
* such as eth0 */
s = socket(PF_INET, SOCK_DGRAM, 0);
if(s < 0) {
syslog(LOG_ERR, "socket(PF_INET, SOCK_DGRAM): %m");
return INADDR_NONE;
}
memset(&ifr, 0, sizeof(struct ifreq));
strncpy(ifr.ifr_name, ifaddr, IFNAMSIZ);
if(ioctl(s, SIOCGIFADDR, &ifr, &ifrlen) < 0) {
syslog(LOG_ERR, "ioctl(s, SIOCGIFADDR, ...): %m");
close(s);
return INADDR_NONE;
}
syslog(LOG_DEBUG, "GetIfAddrIPv4(%s) = %s", ifaddr,
inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr));
addr = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr;
close(s);
return addr;
}
/** /**
* Add the multicast membership for SSDP on the interface * Add the multicast membership for SSDP on the interface
* @param s the socket * @param s the socket
@ -71,12 +36,11 @@ GetIfAddrIPv4(const char * ifaddr)
* @param ipv6 IPv6 or IPv4 * @param ipv6 IPv6 or IPv4
* return -1 on error, 0 on success */ * return -1 on error, 0 on success */
int int
AddDropMulticastMembership(int s, const char * ifaddr, int ipv6, int drop) AddDropMulticastMembership(int s, struct lan_addr_s * lan_addr, int ipv6, int drop)
{ {
struct ip_mreq imr; /* Ip multicast membership */ struct ip_mreq imr; /* Ip multicast membership */
#ifdef ENABLE_IPV6 #ifdef ENABLE_IPV6
struct ipv6_mreq mr; struct ipv6_mreq mr;
unsigned int ifindex;
#else /* ENABLE_IPV6 */ #else /* ENABLE_IPV6 */
(void)ipv6; (void)ipv6;
#endif /* ENABLE_IPV6 */ #endif /* ENABLE_IPV6 */
@ -86,17 +50,16 @@ AddDropMulticastMembership(int s, const char * ifaddr, int ipv6, int drop)
#ifdef ENABLE_IPV6 #ifdef ENABLE_IPV6
if(ipv6) if(ipv6)
{ {
ifindex = if_nametoindex(ifaddr);
memset(&mr, 0, sizeof(mr)); memset(&mr, 0, sizeof(mr));
inet_pton(AF_INET6, LL_SSDP_MCAST_ADDR, &mr.ipv6mr_multiaddr); inet_pton(AF_INET6, LL_SSDP_MCAST_ADDR, &mr.ipv6mr_multiaddr);
mr.ipv6mr_interface = ifindex; mr.ipv6mr_interface = lan_addr->index;
if(setsockopt(s, IPPROTO_IPV6, drop ? IPV6_LEAVE_GROUP : IPV6_JOIN_GROUP, if(setsockopt(s, IPPROTO_IPV6, drop ? IPV6_LEAVE_GROUP : IPV6_JOIN_GROUP,
&mr, sizeof(struct ipv6_mreq)) < 0) &mr, sizeof(struct ipv6_mreq)) < 0)
{ {
syslog(LOG_ERR, "setsockopt(udp, %s)(%s, %s): %m", syslog(LOG_ERR, "setsockopt(udp, %s)(%s, %s): %m",
drop ? "IPV6_LEAVE_GROUP" : "IPV6_JOIN_GROUP", drop ? "IPV6_LEAVE_GROUP" : "IPV6_JOIN_GROUP",
LL_SSDP_MCAST_ADDR, LL_SSDP_MCAST_ADDR,
ifaddr); lan_addr->ifname);
return -1; return -1;
} }
inet_pton(AF_INET6, SL_SSDP_MCAST_ADDR, &mr.ipv6mr_multiaddr); inet_pton(AF_INET6, SL_SSDP_MCAST_ADDR, &mr.ipv6mr_multiaddr);
@ -106,7 +69,7 @@ AddDropMulticastMembership(int s, const char * ifaddr, int ipv6, int drop)
syslog(LOG_ERR, "setsockopt(udp, %s)(%s, %s): %m", syslog(LOG_ERR, "setsockopt(udp, %s)(%s, %s): %m",
drop ? "IPV6_LEAVE_GROUP" : "IPV6_JOIN_GROUP", drop ? "IPV6_LEAVE_GROUP" : "IPV6_JOIN_GROUP",
SL_SSDP_MCAST_ADDR, SL_SSDP_MCAST_ADDR,
ifaddr); lan_addr->ifname);
return -1; return -1;
} }
} }
@ -115,11 +78,11 @@ AddDropMulticastMembership(int s, const char * ifaddr, int ipv6, int drop)
#endif /* ENABLE_IPV6 */ #endif /* ENABLE_IPV6 */
/* setting up imr structure */ /* setting up imr structure */
imr.imr_multiaddr.s_addr = inet_addr(SSDP_MCAST_ADDR); imr.imr_multiaddr.s_addr = inet_addr(SSDP_MCAST_ADDR);
imr.imr_interface.s_addr = GetIfAddrIPv4(ifaddr); imr.imr_interface.s_addr = lan_addr->addr.s_addr;
if(imr.imr_interface.s_addr == INADDR_NONE) if(imr.imr_interface.s_addr == INADDR_NONE)
{ {
syslog(LOG_ERR, "no IPv4 address for interface %s", syslog(LOG_ERR, "no IPv4 address for interface %s",
ifaddr); lan_addr->ifname);
return -1; return -1;
} }
@ -128,7 +91,7 @@ AddDropMulticastMembership(int s, const char * ifaddr, int ipv6, int drop)
{ {
syslog(LOG_ERR, "setsockopt(udp, %s)(%s): %m", syslog(LOG_ERR, "setsockopt(udp, %s)(%s): %m",
drop ? "IP_DROP_MEMBERSHIP" : "IP_ADD_MEMBERSHIP", drop ? "IP_DROP_MEMBERSHIP" : "IP_ADD_MEMBERSHIP",
ifaddr); lan_addr->ifname);
return -1; return -1;
} }
#ifdef ENABLE_IPV6 #ifdef ENABLE_IPV6
@ -139,9 +102,7 @@ AddDropMulticastMembership(int s, const char * ifaddr, int ipv6, int drop)
} }
int int
OpenAndConfSSDPReceiveSocket(int n_listen_addr, OpenAndConfSSDPReceiveSocket(int ipv6)
const char * * listen_addr,
int ipv6)
{ {
int s; int s;
int opt = 1; int opt = 1;
@ -151,6 +112,7 @@ OpenAndConfSSDPReceiveSocket(int n_listen_addr,
struct sockaddr_in sockname; struct sockaddr_in sockname;
#endif /* ENABLE_IPV6 */ #endif /* ENABLE_IPV6 */
socklen_t sockname_len; socklen_t sockname_len;
struct lan_addr_s * lan_addr;
#ifndef ENABLE_IPV6 #ifndef ENABLE_IPV6
if(ipv6) { if(ipv6) {
@ -246,14 +208,13 @@ OpenAndConfSSDPReceiveSocket(int n_listen_addr,
return -1; return -1;
} }
while(n_listen_addr>0) for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next)
{ {
n_listen_addr--; if(AddDropMulticastMembership(s, lan_addr, ipv6, 0) < 0)
if(AddDropMulticastMembership(s, listen_addr[n_listen_addr], ipv6, 0) < 0)
{ {
syslog(LOG_WARNING, "Failed to add IPv%d multicast membership for interface %s.", syslog(LOG_WARNING, "Failed to add IPv%d multicast membership for interface %s.",
ipv6 ? 6 : 4, ipv6 ? 6 : 4,
listen_addr[n_listen_addr] ); lan_addr->ifname);
} }
} }

View File

@ -1,34 +1,32 @@
/* $Id: openssdpsocket.h,v 1.4 2011/07/29 15:21:13 nanard Exp $ */ /* $Id: openssdpsocket.h,v 1.6 2014/11/28 16:20:58 nanard Exp $ */
/* MiniUPnP project /* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2006-2011 Thomas Bernard * (c) 2006-2014 Thomas Bernard
* This software is subject to the conditions detailed * This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */ * in the LICENCE file provided within the distribution */
#ifndef OPENSSDPSOCKET_H_INCLUDED #ifndef OPENSSDPSOCKET_H_INCLUDED
#define OPENSSDPSOCKET_H_INCLUDED #define OPENSSDPSOCKET_H_INCLUDED
#include "minissdpdtypes.h"
/** /**
* Open a socket and configure it for receiving SSDP packets * Open a socket and configure it for receiving SSDP packets
* *
* @param n_listen_addr size of listen_addr array
* @param listen_addr array of address (or interface names) to listen
* @param ipv6 open INET6 or INET socket * @param ipv6 open INET6 or INET socket
* @return socket * @return socket
*/ */
int int
OpenAndConfSSDPReceiveSocket(int n_if_addr, OpenAndConfSSDPReceiveSocket(int ipv6);
const char * * if_addr,
int ipv6);
/** /**
* Add or Drop the multicast membership for SSDP on the interface * Add or Drop the multicast membership for SSDP on the interface
* @param s the socket * @param s the socket
* @param ifaddr the IPv4 address or interface name * @param lan_addr the LAN address or interface name
* @param ipv6 IPv6 or IPv4 * @param ipv6 IPv6 or IPv4
* @param drop 0 to add, 1 to drop * @param drop 0 to add, 1 to drop
* return -1 on error, 0 on success */ * return -1 on error, 0 on success */
int int
AddDropMulticastMembership(int s, const char * ifaddr, int ipv6, int drop); AddDropMulticastMembership(int s, struct lan_addr_s * lan_addr, int ipv6, int drop);
#endif #endif

View File

@ -1,7 +1,7 @@
/* $Id: upnputils.c,v 1.1 2013/09/07 06:45:39 nanard Exp $ */ /* $Id: upnputils.c,v 1.2 2014/11/28 16:20:58 nanard Exp $ */
/* MiniUPnP project /* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * 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 * This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */ * in the LICENCE file provided within the distribution */
@ -21,6 +21,10 @@
#endif #endif
#include "upnputils.h" #include "upnputils.h"
#include "getroute.h"
#include "minissdpdtypes.h"
extern struct lan_addr_list lan_addrs;
int int
sockaddr_to_string(const struct sockaddr * addr, char * str, size_t size) sockaddr_to_string(const struct sockaddr * addr, char * str, size_t size)
@ -85,3 +89,84 @@ set_non_blocking(int fd)
return 1; return 1;
} }
struct lan_addr_s *
get_lan_for_peer(const struct sockaddr * peer)
{
struct lan_addr_s * lan_addr = NULL;
#ifdef DEBUG
char dbg_str[64];
#endif /* DEBUG */
#ifdef ENABLE_IPV6
if(peer->sa_family == AF_INET6)
{
struct sockaddr_in6 * peer6 = (struct sockaddr_in6 *)peer;
if(IN6_IS_ADDR_V4MAPPED(&peer6->sin6_addr))
{
struct in_addr peer_addr;
memcpy(&peer_addr, &peer6->sin6_addr.s6_addr[12], 4);
for(lan_addr = lan_addrs.lh_first;
lan_addr != NULL;
lan_addr = lan_addr->list.le_next)
{
if( (peer_addr.s_addr & lan_addr->mask.s_addr)
== (lan_addr->addr.s_addr & lan_addr->mask.s_addr))
break;
}
}
else
{
int index = -1;
if(peer6->sin6_scope_id > 0)
index = (int)peer6->sin6_scope_id;
else
{
if(get_src_for_route_to(peer, NULL, NULL, &index) < 0)
return NULL;
}
syslog(LOG_DEBUG, "%s looking for LAN interface index=%d",
"get_lan_for_peer()", index);
for(lan_addr = lan_addrs.lh_first;
lan_addr != NULL;
lan_addr = lan_addr->list.le_next)
{
syslog(LOG_DEBUG,
"ifname=%s index=%u str=%s addr=%08x mask=%08x",
lan_addr->ifname, lan_addr->index,
lan_addr->str,
ntohl(lan_addr->addr.s_addr),
ntohl(lan_addr->mask.s_addr));
if(index == (int)lan_addr->index)
break;
}
}
}
else if(peer->sa_family == AF_INET)
{
#endif /* ENABLE_IPV6 */
for(lan_addr = lan_addrs.lh_first;
lan_addr != NULL;
lan_addr = lan_addr->list.le_next)
{
if( (((const struct sockaddr_in *)peer)->sin_addr.s_addr & lan_addr->mask.s_addr)
== (lan_addr->addr.s_addr & lan_addr->mask.s_addr))
break;
}
#ifdef ENABLE_IPV6
}
#endif /* ENABLE_IPV6 */
#ifdef DEBUG
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);
}
#endif /* DEBUG */
return lan_addr;
}

View File

@ -1,7 +1,7 @@
/* $Id: upnputils.h,v 1.1 2013/09/07 06:45:39 nanard Exp $ */ /* $Id: upnputils.h,v 1.2 2014/11/28 16:20:07 nanard Exp $ */
/* MiniUPnP project /* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2011-2013 Thomas Bernard * (c) 2011-2014 Thomas Bernard
* This software is subject to the conditions detailed * This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */ * in the LICENCE file provided within the distribution */
@ -23,5 +23,11 @@ sockaddr_to_string(const struct sockaddr * addr, char * str, size_t size);
int int
set_non_blocking(int fd); set_non_blocking(int fd);
/**
* get the LAN which the peer belongs to
*/
struct lan_addr_s *
get_lan_for_peer(const struct sockaddr * peer);
#endif #endif