2024-01-15 00:01:16 +00:00
|
|
|
/* $Id: minissdp.c,v 1.107 2024/01/15 00:20:21 nanard Exp $ */
|
2017-05-27 08:25:53 +00:00
|
|
|
/* vim: tabstop=4 shiftwidth=4 noexpandtab
|
|
|
|
* MiniUPnP project
|
2018-01-16 01:06:46 +00:00
|
|
|
* http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
|
2024-01-15 00:01:16 +00:00
|
|
|
* (c) 2006-2024 Thomas Bernard
|
2011-09-28 19:13:20 +00:00
|
|
|
* This software is subject to the conditions detailed
|
|
|
|
* in the LICENCE file provided within the distribution */
|
|
|
|
|
|
|
|
#include <stdio.h>
|
2012-09-28 09:13:48 +00:00
|
|
|
#include <stdlib.h>
|
2011-09-28 19:13:20 +00:00
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/un.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <arpa/inet.h>
|
2012-02-02 17:45:43 +00:00
|
|
|
#include <errno.h>
|
2011-09-28 19:13:20 +00:00
|
|
|
#include <syslog.h>
|
2012-06-23 23:41:47 +00:00
|
|
|
|
2017-05-24 09:04:53 +00:00
|
|
|
#ifdef IP_RECVIF
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/uio.h>
|
|
|
|
#include <net/if.h>
|
|
|
|
#include <net/if_dl.h>
|
|
|
|
#endif
|
|
|
|
|
2011-09-28 19:13:20 +00:00
|
|
|
#include "config.h"
|
2017-03-13 12:01:00 +00:00
|
|
|
#if defined(ENABLE_IPV6) && defined(UPNP_STRICT)
|
|
|
|
#include <ifaddrs.h>
|
|
|
|
#endif /* defined(ENABLE_IPV6) && defined(UPNP_STRICT) */
|
|
|
|
|
2011-09-28 19:13:20 +00:00
|
|
|
#include "upnpdescstrings.h"
|
|
|
|
#include "miniupnpdpath.h"
|
|
|
|
#include "upnphttp.h"
|
|
|
|
#include "upnpglobalvars.h"
|
|
|
|
#include "minissdp.h"
|
|
|
|
#include "upnputils.h"
|
2012-06-23 23:41:47 +00:00
|
|
|
#include "getroute.h"
|
2014-02-25 10:15:30 +00:00
|
|
|
#include "asyncsendto.h"
|
2011-09-28 19:13:20 +00:00
|
|
|
#include "codelength.h"
|
2014-10-06 12:44:23 +00:00
|
|
|
#include "macros.h"
|
2011-09-28 19:13:20 +00:00
|
|
|
|
2017-12-12 09:42:54 +00:00
|
|
|
#ifndef MIN
|
|
|
|
#define MIN(x,y) (((x)<(y))?(x):(y))
|
|
|
|
#endif /* MIN */
|
|
|
|
|
2011-09-28 19:13:20 +00:00
|
|
|
/* SSDP ip/port */
|
|
|
|
#define SSDP_PORT (1900)
|
|
|
|
#define SSDP_MCAST_ADDR ("239.255.255.250")
|
2012-04-06 17:30:19 +00:00
|
|
|
#define LL_SSDP_MCAST_ADDR "FF02::C"
|
|
|
|
#define SL_SSDP_MCAST_ADDR "FF05::C"
|
2014-05-21 23:12:06 +00:00
|
|
|
#define GL_SSDP_MCAST_ADDR "FF0E::C"
|
2011-09-28 19:13:20 +00:00
|
|
|
|
|
|
|
/* AddMulticastMembership()
|
2015-08-25 18:33:47 +00:00
|
|
|
* param s socket
|
|
|
|
* param lan_addr lan address
|
2011-09-28 19:13:20 +00:00
|
|
|
*/
|
|
|
|
static int
|
2015-08-25 18:33:47 +00:00
|
|
|
AddMulticastMembership(int s, struct lan_addr_s * lan_addr)
|
2011-09-28 19:13:20 +00:00
|
|
|
{
|
2015-08-25 18:33:47 +00:00
|
|
|
#ifndef HAVE_IP_MREQN
|
|
|
|
/* The ip_mreqn structure appeared in Linux 2.4. */
|
|
|
|
struct ip_mreq imr; /* Ip multicast membership */
|
|
|
|
#else /* HAVE_IP_MREQN */
|
2015-08-21 14:05:26 +00:00
|
|
|
struct ip_mreqn imr; /* Ip multicast membership */
|
2015-08-25 18:33:47 +00:00
|
|
|
#endif /* HAVE_IP_MREQN */
|
2011-09-28 19:13:20 +00:00
|
|
|
|
2019-05-02 10:09:28 +00:00
|
|
|
/* setting up imr structure */
|
|
|
|
imr.imr_multiaddr.s_addr = inet_addr(SSDP_MCAST_ADDR);
|
|
|
|
/*imr.imr_interface.s_addr = htonl(INADDR_ANY);*/
|
2015-08-25 18:33:47 +00:00
|
|
|
#ifndef HAVE_IP_MREQN
|
|
|
|
imr.imr_interface.s_addr = lan_addr->addr.s_addr;
|
|
|
|
#else /* HAVE_IP_MREQN */
|
2019-05-02 10:09:28 +00:00
|
|
|
imr.imr_address.s_addr = lan_addr->addr.s_addr;
|
2015-08-21 14:05:26 +00:00
|
|
|
#ifndef MULTIPLE_EXTERNAL_IP
|
2015-08-25 18:33:47 +00:00
|
|
|
#ifdef ENABLE_IPV6
|
|
|
|
imr.imr_ifindex = lan_addr->index;
|
|
|
|
#else /* ENABLE_IPV6 */
|
2019-05-02 10:09:28 +00:00
|
|
|
imr.imr_ifindex = if_nametoindex(lan_addr->ifname);
|
2015-08-25 18:33:47 +00:00
|
|
|
#endif /* ENABLE_IPV6 */
|
|
|
|
#else /* MULTIPLE_EXTERNAL_IP */
|
2019-05-02 10:09:28 +00:00
|
|
|
imr.imr_ifindex = 0;
|
2015-08-25 18:33:47 +00:00
|
|
|
#endif /* MULTIPLE_EXTERNAL_IP */
|
|
|
|
#endif /* HAVE_IP_MREQN */
|
2012-03-01 01:44:38 +00:00
|
|
|
|
2019-05-02 10:28:52 +00:00
|
|
|
if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&imr, sizeof(imr)) < 0)
|
2011-09-28 19:13:20 +00:00
|
|
|
{
|
2019-05-02 10:09:28 +00:00
|
|
|
syslog(LOG_ERR, "setsockopt(udp, IP_ADD_MEMBERSHIP): %m");
|
2011-09-28 19:13:20 +00:00
|
|
|
return -1;
|
2019-05-02 10:09:28 +00:00
|
|
|
}
|
2011-09-28 19:13:20 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* AddMulticastMembershipIPv6()
|
|
|
|
* param s socket (IPv6)
|
2014-05-22 08:17:06 +00:00
|
|
|
* param ifindex : interface index (0 : All interfaces) */
|
2011-09-28 19:13:20 +00:00
|
|
|
#ifdef ENABLE_IPV6
|
|
|
|
static int
|
2014-05-22 08:17:06 +00:00
|
|
|
AddMulticastMembershipIPv6(int s, unsigned int ifindex)
|
2011-09-28 19:13:20 +00:00
|
|
|
{
|
|
|
|
struct ipv6_mreq mr;
|
|
|
|
|
|
|
|
memset(&mr, 0, sizeof(mr));
|
2014-05-22 08:17:06 +00:00
|
|
|
mr.ipv6mr_interface = ifindex; /* 0 : all interfaces */
|
2011-09-28 19:13:20 +00:00
|
|
|
#ifndef IPV6_ADD_MEMBERSHIP
|
|
|
|
#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
|
|
|
|
#endif
|
2014-05-22 08:12:26 +00:00
|
|
|
inet_pton(AF_INET6, LL_SSDP_MCAST_ADDR, &mr.ipv6mr_multiaddr);
|
2011-09-28 19:13:20 +00:00
|
|
|
if(setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mr, sizeof(struct ipv6_mreq)) < 0)
|
|
|
|
{
|
|
|
|
syslog(LOG_ERR, "setsockopt(udp, IPV6_ADD_MEMBERSHIP): %m");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
inet_pton(AF_INET6, SL_SSDP_MCAST_ADDR, &mr.ipv6mr_multiaddr);
|
|
|
|
if(setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mr, sizeof(struct ipv6_mreq)) < 0)
|
|
|
|
{
|
|
|
|
syslog(LOG_ERR, "setsockopt(udp, IPV6_ADD_MEMBERSHIP): %m");
|
|
|
|
return -1;
|
|
|
|
}
|
2014-05-21 23:12:06 +00:00
|
|
|
inet_pton(AF_INET6, GL_SSDP_MCAST_ADDR, &mr.ipv6mr_multiaddr);
|
|
|
|
if(setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mr, sizeof(struct ipv6_mreq)) < 0)
|
|
|
|
{
|
|
|
|
syslog(LOG_ERR, "setsockopt(udp, IPV6_ADD_MEMBERSHIP): %m");
|
|
|
|
return -1;
|
|
|
|
}
|
2011-09-28 19:13:20 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-03-13 12:01:00 +00:00
|
|
|
|
|
|
|
#if defined(ENABLE_IPV6) && defined(UPNP_STRICT)
|
|
|
|
static int get_link_local_addr(unsigned scope_id, struct in6_addr * addr6)
|
|
|
|
{
|
|
|
|
struct ifaddrs * ifap;
|
|
|
|
struct ifaddrs * ife;
|
|
|
|
if(getifaddrs(&ifap)<0) {
|
|
|
|
syslog(LOG_ERR, "getifaddrs: %m");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
for(ife = ifap; ife != NULL; ife = ife->ifa_next) {
|
|
|
|
if(ife->ifa_addr == NULL) continue;
|
|
|
|
if(ife->ifa_addr->sa_family != AF_INET6) continue;
|
|
|
|
if(!IN6_IS_ADDR_LINKLOCAL(&(((const struct sockaddr_in6 *)ife->ifa_addr)->sin6_addr))) continue;
|
|
|
|
if(scope_id != if_nametoindex(ife->ifa_name)) continue;
|
|
|
|
memcpy(addr6, &(((const struct sockaddr_in6 *)ife->ifa_addr)->sin6_addr), sizeof(struct in6_addr));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
freeifaddrs(ifap);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif /* defined(ENABLE_IPV6) && defined(UPNP_STRICT) */
|
|
|
|
|
2012-03-01 01:44:38 +00:00
|
|
|
/* Open and configure the socket listening for
|
2011-09-28 19:13:20 +00:00
|
|
|
* SSDP udp packets sent on 239.255.255.250 port 1900
|
|
|
|
* SSDP v6 udp packets sent on FF02::C, or FF05::C, port 1900 */
|
|
|
|
int
|
|
|
|
OpenAndConfSSDPReceiveSocket(int ipv6)
|
|
|
|
{
|
|
|
|
int s;
|
|
|
|
struct sockaddr_storage sockname;
|
|
|
|
socklen_t sockname_len;
|
|
|
|
struct lan_addr_s * lan_addr;
|
2017-05-24 09:04:20 +00:00
|
|
|
const int on = 1;
|
2011-09-28 19:13:20 +00:00
|
|
|
|
|
|
|
if( (s = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, 0)) < 0)
|
|
|
|
{
|
2012-02-09 20:33:24 +00:00
|
|
|
syslog(LOG_ERR, "%s: socket(udp): %m",
|
|
|
|
"OpenAndConfSSDPReceiveSocket");
|
2011-09-28 19:13:20 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(&sockname, 0, sizeof(struct sockaddr_storage));
|
2014-05-20 12:43:47 +00:00
|
|
|
#ifdef ENABLE_IPV6
|
|
|
|
if(ipv6)
|
|
|
|
{
|
2011-09-28 19:13:20 +00:00
|
|
|
struct sockaddr_in6 * saddr = (struct sockaddr_in6 *)&sockname;
|
|
|
|
saddr->sin6_family = AF_INET6;
|
|
|
|
saddr->sin6_port = htons(SSDP_PORT);
|
2014-05-20 12:43:47 +00:00
|
|
|
saddr->sin6_addr = ipv6_bind_addr;
|
2011-09-28 19:13:20 +00:00
|
|
|
sockname_len = sizeof(struct sockaddr_in6);
|
2014-05-20 12:43:47 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif /* ENABLE_IPV6 */
|
|
|
|
{
|
2011-09-28 19:13:20 +00:00
|
|
|
struct sockaddr_in * saddr = (struct sockaddr_in *)&sockname;
|
|
|
|
saddr->sin_family = AF_INET;
|
|
|
|
saddr->sin_port = htons(SSDP_PORT);
|
2018-01-09 01:32:39 +00:00
|
|
|
/* NOTE : it seems it doesn't work when binding on the specific address */
|
2011-09-28 19:13:20 +00:00
|
|
|
/*saddr->sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);*/
|
|
|
|
saddr->sin_addr.s_addr = htonl(INADDR_ANY);
|
|
|
|
/*saddr->sin_addr.s_addr = inet_addr(ifaddr);*/
|
|
|
|
sockname_len = sizeof(struct sockaddr_in);
|
|
|
|
}
|
|
|
|
|
2017-05-24 09:04:20 +00:00
|
|
|
if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
|
2011-09-28 19:13:20 +00:00
|
|
|
{
|
|
|
|
syslog(LOG_WARNING, "setsockopt(udp, SO_REUSEADDR): %m");
|
|
|
|
}
|
2021-04-21 23:19:57 +00:00
|
|
|
if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)) < 0)
|
|
|
|
{
|
|
|
|
syslog(LOG_WARNING, "setsockopt(udp, SO_REUSEPORT): %m");
|
|
|
|
}
|
2017-05-24 09:04:53 +00:00
|
|
|
#ifdef IP_RECVIF
|
|
|
|
/* BSD */
|
|
|
|
if(!ipv6) {
|
|
|
|
if(setsockopt(s, IPPROTO_IP, IP_RECVIF, &on, sizeof(on)) < 0)
|
|
|
|
{
|
|
|
|
syslog(LOG_WARNING, "setsockopt(udp, IP_RECVIF): %m");
|
|
|
|
}
|
|
|
|
}
|
2019-09-24 10:25:51 +00:00
|
|
|
#elif defined(IP_PKTINFO) /* IP_RECVIF */
|
2017-05-24 09:04:53 +00:00
|
|
|
/* Linux */
|
|
|
|
if(!ipv6) {
|
|
|
|
if(setsockopt(s, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)) < 0)
|
|
|
|
{
|
|
|
|
syslog(LOG_WARNING, "setsockopt(udp, IP_PKTINFO): %m");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* IP_PKTINFO */
|
|
|
|
#if defined(ENABLE_IPV6) && defined(IPV6_RECVPKTINFO)
|
|
|
|
if(ipv6) {
|
|
|
|
if(setsockopt(s, IPPROTO_IP, IPV6_RECVPKTINFO, &on, sizeof(on)) < 0)
|
|
|
|
{
|
|
|
|
syslog(LOG_WARNING, "setsockopt(udp, IPV6_RECVPKTINFO): %m");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* defined(ENABLE_IPV6) && defined(IPV6_RECVPKTINFO) */
|
2011-09-28 19:13:20 +00:00
|
|
|
|
2012-02-09 20:33:24 +00:00
|
|
|
if(!set_non_blocking(s))
|
|
|
|
{
|
|
|
|
syslog(LOG_WARNING, "%s: set_non_blocking(): %m",
|
|
|
|
"OpenAndConfSSDPReceiveSocket");
|
|
|
|
}
|
2011-09-28 19:13:20 +00:00
|
|
|
|
2015-08-26 07:46:05 +00:00
|
|
|
#if defined(SO_BINDTODEVICE) && !defined(MULTIPLE_EXTERNAL_IP)
|
|
|
|
/* One and only one LAN interface */
|
|
|
|
if(lan_addrs.lh_first != NULL && lan_addrs.lh_first->list.le_next == NULL
|
2018-04-22 19:21:58 +00:00
|
|
|
&& lan_addrs.lh_first->ifname[0] != '\0')
|
2015-08-21 14:05:26 +00:00
|
|
|
{
|
2015-08-26 07:46:05 +00:00
|
|
|
if(setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE,
|
|
|
|
lan_addrs.lh_first->ifname,
|
|
|
|
strlen(lan_addrs.lh_first->ifname)) < 0)
|
2015-08-26 08:04:23 +00:00
|
|
|
syslog(LOG_WARNING, "%s: setsockopt(udp%s, SO_BINDTODEVICE, %s): %m",
|
|
|
|
"OpenAndConfSSDPReceiveSocket", ipv6 ? "6" : "",
|
|
|
|
lan_addrs.lh_first->ifname);
|
2015-08-21 14:05:26 +00:00
|
|
|
}
|
2015-08-26 07:46:05 +00:00
|
|
|
#endif /* defined(SO_BINDTODEVICE) && !defined(MULTIPLE_EXTERNAL_IP) */
|
2015-08-21 14:05:26 +00:00
|
|
|
|
2011-09-28 19:13:20 +00:00
|
|
|
if(bind(s, (struct sockaddr *)&sockname, sockname_len) < 0)
|
|
|
|
{
|
2012-02-09 20:33:24 +00:00
|
|
|
syslog(LOG_ERR, "%s: bind(udp%s): %m",
|
|
|
|
"OpenAndConfSSDPReceiveSocket", ipv6 ? "6" : "");
|
2011-09-28 19:13:20 +00:00
|
|
|
close(s);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef ENABLE_IPV6
|
|
|
|
if(ipv6)
|
|
|
|
{
|
2014-05-22 08:22:04 +00:00
|
|
|
for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next)
|
2014-02-03 09:42:38 +00:00
|
|
|
{
|
2014-05-22 08:22:04 +00:00
|
|
|
if(AddMulticastMembershipIPv6(s, lan_addr->index) < 0)
|
|
|
|
{
|
|
|
|
syslog(LOG_WARNING,
|
|
|
|
"Failed to add IPv6 multicast membership for interface %s",
|
2018-03-13 10:53:33 +00:00
|
|
|
strlen(lan_addr->str) ? lan_addr->str : "NULL");
|
2014-05-22 08:22:04 +00:00
|
|
|
}
|
2014-02-03 09:42:38 +00:00
|
|
|
}
|
2011-09-28 19:13:20 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next)
|
|
|
|
{
|
2015-08-25 18:33:47 +00:00
|
|
|
if(AddMulticastMembership(s, lan_addr) < 0)
|
2011-09-28 19:13:20 +00:00
|
|
|
{
|
|
|
|
syslog(LOG_WARNING,
|
2012-03-01 01:44:38 +00:00
|
|
|
"Failed to add multicast membership for interface %s",
|
2015-06-24 06:42:12 +00:00
|
|
|
strlen(lan_addr->str) ? lan_addr->str : "NULL");
|
2011-09-28 19:13:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* open the UDP socket used to send SSDP notifications to
|
|
|
|
* the multicast group reserved for them */
|
|
|
|
static int
|
2019-05-02 10:28:52 +00:00
|
|
|
OpenAndConfSSDPNotifySocket(struct lan_addr_s * lan_addr)
|
2011-09-28 19:13:20 +00:00
|
|
|
{
|
|
|
|
int s;
|
|
|
|
unsigned char loopchar = 0;
|
|
|
|
int bcast = 1;
|
2012-04-12 21:37:59 +00:00
|
|
|
unsigned char ttl = 2; /* UDA v1.1 says :
|
|
|
|
The TTL for the IP packet SHOULD default to 2 and
|
|
|
|
SHOULD be configurable. */
|
|
|
|
/* TODO: Make TTL be configurable */
|
2019-05-02 10:28:52 +00:00
|
|
|
#ifndef HAVE_IP_MREQN
|
2011-09-28 19:13:20 +00:00
|
|
|
struct in_addr mc_if;
|
2019-05-02 10:28:52 +00:00
|
|
|
#else /* HAVE_IP_MREQN */
|
|
|
|
struct ip_mreqn mc_if;
|
|
|
|
#endif /* HAVE_IP_MREQN */
|
2011-09-28 19:13:20 +00:00
|
|
|
struct sockaddr_in sockname;
|
2012-03-01 01:44:38 +00:00
|
|
|
|
2011-09-28 19:13:20 +00:00
|
|
|
if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
|
|
|
|
{
|
|
|
|
syslog(LOG_ERR, "socket(udp_notify): %m");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2019-05-02 10:28:52 +00:00
|
|
|
#ifndef HAVE_IP_MREQN
|
|
|
|
mc_if.s_addr = lan_addr->addr.s_addr; /*inet_addr(addr);*/
|
|
|
|
#else /* HAVE_IP_MREQN */
|
|
|
|
mc_if.imr_address.s_addr = lan_addr->addr.s_addr; /*inet_addr(addr);*/
|
|
|
|
#ifdef ENABLE_IPV6
|
|
|
|
mc_if.imr_ifindex = lan_addr->index;
|
|
|
|
#else /* ENABLE_IPV6 */
|
|
|
|
mc_if.imr_ifindex = if_nametoindex(lan_addr->ifname);
|
|
|
|
#endif /* ENABLE_IPV6 */
|
|
|
|
#endif /* HAVE_IP_MREQN */
|
2011-09-28 19:13:20 +00:00
|
|
|
|
|
|
|
if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loopchar, sizeof(loopchar)) < 0)
|
|
|
|
{
|
|
|
|
syslog(LOG_ERR, "setsockopt(udp_notify, IP_MULTICAST_LOOP): %m");
|
|
|
|
close(s);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&mc_if, sizeof(mc_if)) < 0)
|
|
|
|
{
|
|
|
|
syslog(LOG_ERR, "setsockopt(udp_notify, IP_MULTICAST_IF): %m");
|
|
|
|
close(s);
|
|
|
|
return -1;
|
|
|
|
}
|
2012-03-01 01:44:38 +00:00
|
|
|
|
2012-04-12 21:37:59 +00:00
|
|
|
if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0)
|
|
|
|
{
|
|
|
|
syslog(LOG_WARNING, "setsockopt(udp_notify, IP_MULTICAST_TTL,): %m");
|
|
|
|
}
|
|
|
|
|
2011-09-28 19:13:20 +00:00
|
|
|
if(setsockopt(s, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bcast)) < 0)
|
|
|
|
{
|
|
|
|
syslog(LOG_ERR, "setsockopt(udp_notify, SO_BROADCAST): %m");
|
|
|
|
close(s);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-10-22 09:43:42 +00:00
|
|
|
/* bind() socket before using sendto() is not mandatory
|
|
|
|
* (sendto() will implicitly bind the socket when called on
|
|
|
|
* an unbound socket)
|
|
|
|
* here it is used to se a specific sending address */
|
2011-09-28 19:13:20 +00:00
|
|
|
memset(&sockname, 0, sizeof(struct sockaddr_in));
|
2019-05-02 10:09:28 +00:00
|
|
|
sockname.sin_family = AF_INET;
|
2019-05-02 10:28:52 +00:00
|
|
|
sockname.sin_addr.s_addr = lan_addr->addr.s_addr; /*inet_addr(addr);*/
|
2011-09-28 19:13:20 +00:00
|
|
|
|
2019-05-02 10:09:28 +00:00
|
|
|
if (bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
|
2011-09-28 19:13:20 +00:00
|
|
|
{
|
|
|
|
syslog(LOG_ERR, "bind(udp_notify): %m");
|
|
|
|
close(s);
|
|
|
|
return -1;
|
2019-05-02 10:09:28 +00:00
|
|
|
}
|
2011-09-28 19:13:20 +00:00
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2012-04-06 17:30:19 +00:00
|
|
|
#ifdef ENABLE_IPV6
|
|
|
|
/* open the UDP socket used to send SSDP notifications to
|
|
|
|
* the multicast group reserved for them. IPv6 */
|
|
|
|
static int
|
2019-05-02 10:28:52 +00:00
|
|
|
OpenAndConfSSDPNotifySocketIPv6(struct lan_addr_s * lan_addr)
|
2012-04-06 17:30:19 +00:00
|
|
|
{
|
|
|
|
int s;
|
|
|
|
unsigned int loop = 0;
|
2016-02-19 13:20:31 +00:00
|
|
|
/* UDA 2.0 : The hop limit of each IP packet for a Site-Local scope
|
|
|
|
* multicast message SHALL be configurable and SHOULD default to 10 */
|
|
|
|
int hop_limit = 10;
|
2014-05-23 15:03:56 +00:00
|
|
|
struct sockaddr_in6 sockname;
|
2012-04-06 17:30:19 +00:00
|
|
|
|
|
|
|
s = socket(PF_INET6, SOCK_DGRAM, 0);
|
|
|
|
if(s < 0)
|
|
|
|
{
|
|
|
|
syslog(LOG_ERR, "socket(udp_notify IPv6): %m");
|
|
|
|
return -1;
|
|
|
|
}
|
2019-05-02 10:28:52 +00:00
|
|
|
if(setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &lan_addr->index, sizeof(lan_addr->index)) < 0)
|
2012-04-06 17:30:19 +00:00
|
|
|
{
|
2019-05-02 10:28:52 +00:00
|
|
|
syslog(LOG_ERR, "setsockopt(udp_notify IPv6, IPV6_MULTICAST_IF, %u): %m", lan_addr->index);
|
2012-04-06 17:30:19 +00:00
|
|
|
close(s);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if(setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof(loop)) < 0)
|
|
|
|
{
|
|
|
|
syslog(LOG_ERR, "setsockopt(udp_notify, IPV6_MULTICAST_LOOP): %m");
|
|
|
|
close(s);
|
|
|
|
return -1;
|
|
|
|
}
|
2016-02-19 13:20:31 +00:00
|
|
|
if(setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hop_limit, sizeof(hop_limit)) < 0)
|
|
|
|
{
|
|
|
|
syslog(LOG_ERR, "setsockopt(udp_notify, IPV6_MULTICAST_HOPS): %m");
|
|
|
|
close(s);
|
|
|
|
return -1;
|
|
|
|
}
|
2014-05-23 15:03:56 +00:00
|
|
|
|
2014-10-22 09:43:42 +00:00
|
|
|
/* bind() socket before using sendto() is not mandatory
|
|
|
|
* (sendto() will implicitly bind the socket when called on
|
|
|
|
* an unbound socket)
|
|
|
|
* but explicit bind permits to set port/scope_id/etc. */
|
2014-05-23 15:03:56 +00:00
|
|
|
memset(&sockname, 0, sizeof(sockname));
|
|
|
|
sockname.sin6_family = AF_INET6;
|
|
|
|
sockname.sin6_addr = in6addr_any;
|
2014-10-22 09:43:42 +00:00
|
|
|
/*sockname.sin6_port = htons(port);*/
|
2014-05-23 15:03:56 +00:00
|
|
|
/*sockname.sin6_scope_id = if_index;*/
|
|
|
|
if(bind(s, (struct sockaddr *)&sockname, sizeof(sockname)) < 0)
|
|
|
|
{
|
|
|
|
syslog(LOG_ERR, "bind(udp_notify IPv6): %m");
|
|
|
|
close(s);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-04-06 17:30:19 +00:00
|
|
|
return s;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2011-09-28 19:13:20 +00:00
|
|
|
int
|
|
|
|
OpenAndConfSSDPNotifySockets(int * sockets)
|
|
|
|
/*OpenAndConfSSDPNotifySockets(int * sockets,
|
|
|
|
struct lan_addr_s * lan_addr, int n_lan_addr)*/
|
|
|
|
{
|
2012-04-06 17:30:19 +00:00
|
|
|
int i;
|
2011-09-28 19:13:20 +00:00
|
|
|
struct lan_addr_s * lan_addr;
|
|
|
|
|
2012-04-06 17:30:19 +00:00
|
|
|
for(i=0, lan_addr = lan_addrs.lh_first;
|
|
|
|
lan_addr != NULL;
|
|
|
|
lan_addr = lan_addr->list.le_next)
|
2011-09-28 19:13:20 +00:00
|
|
|
{
|
2019-05-02 10:28:52 +00:00
|
|
|
sockets[i] = OpenAndConfSSDPNotifySocket(lan_addr);
|
2011-09-28 19:13:20 +00:00
|
|
|
if(sockets[i] < 0)
|
2012-04-06 17:30:19 +00:00
|
|
|
goto error;
|
|
|
|
i++;
|
|
|
|
#ifdef ENABLE_IPV6
|
2014-03-13 10:26:53 +00:00
|
|
|
if(GETFLAG(IPV6DISABLEDMASK))
|
2014-02-06 09:55:28 +00:00
|
|
|
{
|
2014-03-13 10:26:53 +00:00
|
|
|
sockets[i] = -1;
|
2014-02-06 09:55:28 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-05-02 10:28:52 +00:00
|
|
|
sockets[i] = OpenAndConfSSDPNotifySocketIPv6(lan_addr);
|
2014-03-13 10:26:53 +00:00
|
|
|
if(sockets[i] < 0)
|
|
|
|
goto error;
|
2014-02-06 09:55:28 +00:00
|
|
|
}
|
2012-04-06 17:30:19 +00:00
|
|
|
i++;
|
|
|
|
#endif
|
2011-09-28 19:13:20 +00:00
|
|
|
}
|
|
|
|
return 0;
|
2012-04-06 17:30:19 +00:00
|
|
|
error:
|
|
|
|
while(--i >= 0)
|
|
|
|
{
|
|
|
|
close(sockets[i]);
|
|
|
|
sockets[i] = -1;
|
|
|
|
}
|
|
|
|
return -1;
|
2011-09-28 19:13:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* response from a LiveBox (Wanadoo)
|
|
|
|
HTTP/1.1 200 OK
|
|
|
|
CACHE-CONTROL: max-age=1800
|
|
|
|
DATE: Thu, 01 Jan 1970 04:03:23 GMT
|
|
|
|
EXT:
|
|
|
|
LOCATION: http://192.168.0.1:49152/gatedesc.xml
|
|
|
|
SERVER: Linux/2.4.17, UPnP/1.0, Intel SDK for UPnP devices /1.2
|
|
|
|
ST: upnp:rootdevice
|
|
|
|
USN: uuid:75802409-bccb-40e7-8e6c-fa095ecce13e::upnp:rootdevice
|
|
|
|
|
|
|
|
* response from a Linksys 802.11b :
|
|
|
|
HTTP/1.1 200 OK
|
|
|
|
Cache-Control:max-age=120
|
|
|
|
Location:http://192.168.5.1:5678/rootDesc.xml
|
|
|
|
Server:NT/5.0 UPnP/1.0
|
|
|
|
ST:upnp:rootdevice
|
|
|
|
USN:uuid:upnp-InternetGatewayDevice-1_0-0090a2777777::upnp:rootdevice
|
|
|
|
EXT:
|
|
|
|
*/
|
|
|
|
|
2013-06-11 18:03:15 +00:00
|
|
|
/* Responds to a SSDP "M-SEARCH"
|
|
|
|
* s : socket to use
|
|
|
|
* addr : peer
|
|
|
|
* st, st_len : ST: header
|
|
|
|
* suffix : suffix for USN: header
|
|
|
|
* host, port : our HTTP host, port
|
2014-02-25 10:15:30 +00:00
|
|
|
* delay : in milli-seconds
|
2013-06-11 18:03:15 +00:00
|
|
|
*/
|
2011-09-28 19:13:20 +00:00
|
|
|
static void
|
2013-06-11 18:03:15 +00:00
|
|
|
SendSSDPResponse(int s, const struct sockaddr * addr,
|
|
|
|
const char * st, int st_len, const char * suffix,
|
2014-04-20 16:05:22 +00:00
|
|
|
const char * host, unsigned short http_port,
|
2014-04-09 13:35:06 +00:00
|
|
|
#ifdef ENABLE_HTTPS
|
|
|
|
unsigned short https_port,
|
|
|
|
#endif
|
|
|
|
const char * uuidvalue, unsigned int delay)
|
2011-09-28 19:13:20 +00:00
|
|
|
{
|
|
|
|
int l, n;
|
2014-04-09 13:36:38 +00:00
|
|
|
char buf[SSDP_PACKET_MAX_LEN];
|
2012-02-09 20:33:24 +00:00
|
|
|
char addr_str[64];
|
2011-09-28 19:13:20 +00:00
|
|
|
socklen_t addrlen;
|
2013-02-07 09:59:31 +00:00
|
|
|
int st_is_uuid;
|
2013-02-07 09:56:38 +00:00
|
|
|
#ifdef ENABLE_HTTP_DATE
|
|
|
|
char http_date[64];
|
|
|
|
time_t t;
|
|
|
|
struct tm tm;
|
|
|
|
|
|
|
|
time(&t);
|
|
|
|
gmtime_r(&t, &tm);
|
|
|
|
strftime(http_date, sizeof(http_date),
|
|
|
|
"%a, %d %b %Y %H:%M:%S GMT", &tm);
|
|
|
|
#endif
|
|
|
|
|
2013-02-07 09:59:31 +00:00
|
|
|
st_is_uuid = (st_len == (int)strlen(uuidvalue)) &&
|
|
|
|
(memcmp(uuidvalue, st, st_len) == 0);
|
2012-03-01 01:44:38 +00:00
|
|
|
/*
|
2011-09-28 19:13:20 +00:00
|
|
|
* follow guideline from document "UPnP Device Architecture 1.0"
|
|
|
|
* uppercase is recommended.
|
|
|
|
* DATE: is recommended
|
|
|
|
* SERVER: OS/ver UPnP/1.0 miniupnpd/1.0
|
CACHE-CONTROL: max-age=1800 in M-SEARCH response
to be compliant with UPnP UDA 1.0, 1.1 and 2.0
fixes #698
UDA 1.0 1.2.3 Discovery: Search: Response (p21) :
CACHE-CONTROL
Required. Must have max-age directive that specifies number of seconds
the advertisement is valid. After this duration, control points should
assume the device (or service) is no longer available. Should be greater
than or equal to 1800 seconds (30 minutes), although exceptions are defined
in the text above. Specified by UPnP vendor. Integer.
UDA 1.1 1.3.3 Search response (p34) :
CACHE-CONTROL
REQUIRED. Field value MUST have the max-age directive (“max-age=”) followed
by an integer that specifies the number of seconds the advertisement
is valid. After this duration, control points SHOULD assume the device
(or service) is no longer available; as long as a control point has
received at least one advertisement that is still valid from a root
device, any of its embedded devices or any of its services, then the
control point can assume that all are available. The number of seconds
SHOULD be greater than or equal to 1800 seconds (30 minutes), although
exceptions are defined in the text above. Specified by UPnP vendor.
Other directives MUST NOT be sent and MUST be ignored when received.
UDA 2.0 1.3.3 Search response (p40) :
CACHE-CONTROL
Required. Field value shall have the max-age directive (“max-age=”) followed
by an integer that specifies the number of seconds the advertisement
is valid. After this duration, control points should assume the device
(or service) is no longer available; as long as a control point has
received at least one advertisement that is still valid from a root
device, any of its embedded devices or any of its services, then the
control point can assume that all are available. The number of seconds
should be greater than or equal to 1800 seconds (30 minutes), although
exceptions are defined in the text above. Specified by UPnP vendor.
Other directives shall not be sent and shall be ignored when received.
2024-01-14 23:15:32 +00:00
|
|
|
* CACHE-CONTROL: Should be greater than or equal to 1800 seconds
|
|
|
|
*/
|
2011-09-28 19:13:20 +00:00
|
|
|
l = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\n"
|
CACHE-CONTROL: max-age=1800 in M-SEARCH response
to be compliant with UPnP UDA 1.0, 1.1 and 2.0
fixes #698
UDA 1.0 1.2.3 Discovery: Search: Response (p21) :
CACHE-CONTROL
Required. Must have max-age directive that specifies number of seconds
the advertisement is valid. After this duration, control points should
assume the device (or service) is no longer available. Should be greater
than or equal to 1800 seconds (30 minutes), although exceptions are defined
in the text above. Specified by UPnP vendor. Integer.
UDA 1.1 1.3.3 Search response (p34) :
CACHE-CONTROL
REQUIRED. Field value MUST have the max-age directive (“max-age=”) followed
by an integer that specifies the number of seconds the advertisement
is valid. After this duration, control points SHOULD assume the device
(or service) is no longer available; as long as a control point has
received at least one advertisement that is still valid from a root
device, any of its embedded devices or any of its services, then the
control point can assume that all are available. The number of seconds
SHOULD be greater than or equal to 1800 seconds (30 minutes), although
exceptions are defined in the text above. Specified by UPnP vendor.
Other directives MUST NOT be sent and MUST be ignored when received.
UDA 2.0 1.3.3 Search response (p40) :
CACHE-CONTROL
Required. Field value shall have the max-age directive (“max-age=”) followed
by an integer that specifies the number of seconds the advertisement
is valid. After this duration, control points should assume the device
(or service) is no longer available; as long as a control point has
received at least one advertisement that is still valid from a root
device, any of its embedded devices or any of its services, then the
control point can assume that all are available. The number of seconds
should be greater than or equal to 1800 seconds (30 minutes), although
exceptions are defined in the text above. Specified by UPnP vendor.
Other directives shall not be sent and shall be ignored when received.
2024-01-14 23:15:32 +00:00
|
|
|
"CACHE-CONTROL: max-age=1800\r\n"
|
2013-02-07 09:56:38 +00:00
|
|
|
#ifdef ENABLE_HTTP_DATE
|
|
|
|
"DATE: %s\r\n"
|
|
|
|
#endif
|
2011-09-28 19:13:20 +00:00
|
|
|
"ST: %.*s%s\r\n"
|
2013-02-07 09:59:31 +00:00
|
|
|
"USN: %s%s%.*s%s\r\n"
|
2011-09-28 19:13:20 +00:00
|
|
|
"EXT:\r\n"
|
|
|
|
"SERVER: " MINIUPNPD_SERVER_STRING "\r\n"
|
2015-09-14 10:10:15 +00:00
|
|
|
#ifndef RANDOMIZE_URLS
|
2011-09-28 19:13:20 +00:00
|
|
|
"LOCATION: http://%s:%u" ROOTDESC_PATH "\r\n"
|
2014-04-09 13:35:06 +00:00
|
|
|
#ifdef ENABLE_HTTPS
|
|
|
|
"SECURELOCATION.UPNP.ORG: https://%s:%u" ROOTDESC_PATH "\r\n"
|
2015-09-14 10:10:15 +00:00
|
|
|
#endif /* ENABLE_HTTPS */
|
|
|
|
#else /* RANDOMIZE_URLS */
|
|
|
|
"LOCATION: http://%s:%u/%s" ROOTDESC_PATH "\r\n"
|
|
|
|
#ifdef ENABLE_HTTPS
|
|
|
|
"SECURELOCATION.UPNP.ORG: https://%s:%u/%s" ROOTDESC_PATH "\r\n"
|
|
|
|
#endif /* ENABLE_HTTPS */
|
|
|
|
#endif /* RANDOMIZE_URLS */
|
2012-04-06 17:30:19 +00:00
|
|
|
"OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */
|
2011-09-28 19:13:20 +00:00
|
|
|
"01-NLS: %u\r\n" /* same as BOOTID. UDA v1.1 */
|
|
|
|
"BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
|
|
|
|
"CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
|
|
|
|
"\r\n",
|
2013-02-07 09:56:38 +00:00
|
|
|
#ifdef ENABLE_HTTP_DATE
|
2021-05-22 21:30:05 +00:00
|
|
|
http_date, /* DATE: */
|
2013-02-07 09:56:38 +00:00
|
|
|
#endif
|
2021-05-22 21:30:05 +00:00
|
|
|
st_len, st, suffix, /* ST: */
|
|
|
|
uuidvalue, st_is_uuid ? "" : "::", /* USN: 2/5 */
|
|
|
|
st_is_uuid ? 0 : st_len, st, suffix, /* USN: 3/5 */
|
2021-05-21 22:16:40 +00:00
|
|
|
#ifdef DYNAMIC_OS_VERSION
|
2021-05-22 21:30:05 +00:00
|
|
|
os_version, /* SERVER: */
|
2021-05-21 22:16:40 +00:00
|
|
|
#endif
|
2021-05-22 21:30:05 +00:00
|
|
|
host, (unsigned int)http_port, /* LOCATION: */
|
2015-09-14 10:10:15 +00:00
|
|
|
#ifdef RANDOMIZE_URLS
|
2021-05-22 21:30:05 +00:00
|
|
|
random_url, /* LOCATION: 3/3 */
|
2015-09-14 10:10:15 +00:00
|
|
|
#endif /* RANDOMIZE_URLS */
|
2014-04-09 13:35:06 +00:00
|
|
|
#ifdef ENABLE_HTTPS
|
2021-05-22 21:30:05 +00:00
|
|
|
host, (unsigned int)https_port, /* SECURELOCATION.UPNP.ORG */
|
2015-09-14 10:10:15 +00:00
|
|
|
#ifdef RANDOMIZE_URLS
|
2021-05-22 21:30:05 +00:00
|
|
|
random_url, /* SECURELOCATION.UPNP.ORG 3/3 */
|
2015-09-14 10:10:15 +00:00
|
|
|
#endif /* RANDOMIZE_URLS */
|
|
|
|
#endif /* ENABLE_HTTPS */
|
2021-05-22 21:30:05 +00:00
|
|
|
upnp_bootid, /* 01-NLS: */
|
|
|
|
upnp_bootid, /* BOOTID.UPNP.ORG: */
|
|
|
|
upnp_configid); /* CONFIGID.UPNP.ORG: */
|
2013-04-26 15:18:28 +00:00
|
|
|
if(l<0)
|
|
|
|
{
|
2013-06-11 18:03:15 +00:00
|
|
|
syslog(LOG_ERR, "%s: snprintf failed %m",
|
|
|
|
"SendSSDPResponse()");
|
2013-04-26 15:18:28 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if((unsigned)l>=sizeof(buf))
|
|
|
|
{
|
2014-04-09 13:36:38 +00:00
|
|
|
syslog(LOG_WARNING, "%s: truncated output (%u>=%u)",
|
|
|
|
"SendSSDPResponse()", (unsigned)l, (unsigned)sizeof(buf));
|
2013-04-26 15:18:28 +00:00
|
|
|
l = sizeof(buf) - 1;
|
|
|
|
}
|
2011-09-28 19:13:20 +00:00
|
|
|
addrlen = (addr->sa_family == AF_INET6)
|
|
|
|
? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
|
2014-02-25 10:15:30 +00:00
|
|
|
n = sendto_schedule(s, buf, l, 0,
|
|
|
|
addr, addrlen, delay);
|
2012-02-09 20:33:24 +00:00
|
|
|
sockaddr_to_string(addr, addr_str, sizeof(addr_str));
|
2014-03-24 09:33:52 +00:00
|
|
|
syslog(LOG_DEBUG, "%s: %d bytes to %s ST: %.*s",
|
|
|
|
"SendSSDPResponse()",
|
|
|
|
n, addr_str, l, buf);
|
2011-09-28 19:13:20 +00:00
|
|
|
if(n < 0)
|
|
|
|
{
|
2014-03-24 09:33:52 +00:00
|
|
|
syslog(LOG_ERR, "%s: sendto(udp): %m",
|
|
|
|
"SendSSDPResponse()");
|
2011-09-28 19:13:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-28 09:13:48 +00:00
|
|
|
static struct {
|
|
|
|
const char * s;
|
|
|
|
const int version;
|
2013-06-13 14:03:36 +00:00
|
|
|
const char * uuid;
|
2012-09-28 09:13:48 +00:00
|
|
|
} const known_service_types[] =
|
2011-09-28 19:13:20 +00:00
|
|
|
{
|
2013-06-13 14:03:36 +00:00
|
|
|
{"upnp:rootdevice", 0, uuidvalue_igd},
|
2015-12-12 02:08:18 +00:00
|
|
|
#ifdef IGD_V2
|
|
|
|
{"urn:schemas-upnp-org:device:InternetGatewayDevice:", 2, uuidvalue_igd},
|
|
|
|
{"urn:schemas-upnp-org:device:WANConnectionDevice:", 2, uuidvalue_wcd},
|
|
|
|
{"urn:schemas-upnp-org:device:WANDevice:", 2, uuidvalue_wan},
|
|
|
|
{"urn:schemas-upnp-org:service:WANIPConnection:", 2, uuidvalue_wcd},
|
2023-06-29 09:36:49 +00:00
|
|
|
#ifdef ENABLE_DP_SERVICE
|
2015-12-12 02:08:18 +00:00
|
|
|
{"urn:schemas-upnp-org:service:DeviceProtection:", 1, uuidvalue_igd},
|
2023-06-29 09:36:49 +00:00
|
|
|
#endif
|
2015-12-12 02:08:18 +00:00
|
|
|
#ifdef ENABLE_6FC_SERVICE
|
|
|
|
{"urn:schemas-upnp-org:service:WANIPv6FirewallControl:", 1, uuidvalue_wcd},
|
|
|
|
#endif
|
2015-12-12 07:47:06 +00:00
|
|
|
#else /* IGD_V2 */
|
|
|
|
/* IGD v1 */
|
2015-12-12 02:08:18 +00:00
|
|
|
{"urn:schemas-upnp-org:device:InternetGatewayDevice:", 1, uuidvalue_igd},
|
2013-06-13 14:03:36 +00:00
|
|
|
{"urn:schemas-upnp-org:device:WANConnectionDevice:", 1, uuidvalue_wcd},
|
|
|
|
{"urn:schemas-upnp-org:device:WANDevice:", 1, uuidvalue_wan},
|
2015-12-12 02:08:18 +00:00
|
|
|
{"urn:schemas-upnp-org:service:WANIPConnection:", 1, uuidvalue_wcd},
|
2015-12-12 07:47:06 +00:00
|
|
|
#endif /* IGD_V2 */
|
2013-06-13 14:03:36 +00:00
|
|
|
{"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:", 1, uuidvalue_wan},
|
2015-12-13 14:57:49 +00:00
|
|
|
#ifdef ADVERTISE_WANPPPCONN
|
2013-06-05 09:19:23 +00:00
|
|
|
/* We use WAN IP Connection, not PPP connection,
|
|
|
|
* but buggy control points may try to use WanPPPConnection
|
|
|
|
* anyway */
|
2013-06-13 14:03:36 +00:00
|
|
|
{"urn:schemas-upnp-org:service:WANPPPConnection:", 1, uuidvalue_wcd},
|
2015-12-13 14:57:49 +00:00
|
|
|
#endif /* ADVERTISE_WANPPPCONN */
|
2011-09-28 19:13:20 +00:00
|
|
|
#ifdef ENABLE_L3F_SERVICE
|
2013-06-13 14:03:36 +00:00
|
|
|
{"urn:schemas-upnp-org:service:Layer3Forwarding:", 1, uuidvalue_igd},
|
2015-12-12 07:47:06 +00:00
|
|
|
#endif /* ENABLE_L3F_SERVICE */
|
2014-04-20 16:39:40 +00:00
|
|
|
/* we might want to support urn:schemas-wifialliance-org:device:WFADevice:1
|
|
|
|
* urn:schemas-wifialliance-org:device:WFADevice:1
|
|
|
|
* in the future */
|
2013-06-13 14:03:36 +00:00
|
|
|
{0, 0, 0}
|
2011-09-28 19:13:20 +00:00
|
|
|
};
|
|
|
|
|
2014-10-22 10:11:34 +00:00
|
|
|
/* SendSSDPNotify() sends the SSDP NOTIFY to a specific
|
|
|
|
* destination, for a specific UPnP service or device */
|
2013-02-07 12:27:09 +00:00
|
|
|
static void
|
2014-05-22 08:12:26 +00:00
|
|
|
SendSSDPNotify(int s, const struct sockaddr * dest, socklen_t dest_len,
|
|
|
|
const char * dest_str,
|
2014-04-20 16:05:22 +00:00
|
|
|
const char * host, unsigned short http_port,
|
2014-04-09 13:35:06 +00:00
|
|
|
#ifdef ENABLE_HTTPS
|
|
|
|
unsigned short https_port,
|
|
|
|
#endif
|
2013-02-07 12:27:09 +00:00
|
|
|
const char * nt, const char * suffix,
|
|
|
|
const char * usn1, const char * usn2, const char * usn3,
|
2014-05-22 08:12:26 +00:00
|
|
|
unsigned int lifetime)
|
2013-02-07 12:27:09 +00:00
|
|
|
{
|
2014-04-09 13:36:38 +00:00
|
|
|
char bufr[SSDP_PACKET_MAX_LEN];
|
2013-02-07 12:27:09 +00:00
|
|
|
int n, l;
|
|
|
|
|
|
|
|
l = snprintf(bufr, sizeof(bufr),
|
|
|
|
"NOTIFY * HTTP/1.1\r\n"
|
|
|
|
"HOST: %s:%d\r\n"
|
|
|
|
"CACHE-CONTROL: max-age=%u\r\n"
|
2015-09-14 10:10:15 +00:00
|
|
|
#ifndef RANDOMIZE_URLS
|
2014-04-09 13:35:06 +00:00
|
|
|
"LOCATION: http://%s:%u" ROOTDESC_PATH "\r\n"
|
|
|
|
#ifdef ENABLE_HTTPS
|
|
|
|
"SECURELOCATION.UPNP.ORG: https://%s:%u" ROOTDESC_PATH "\r\n"
|
2015-09-14 10:10:15 +00:00
|
|
|
#endif /* ENABLE_HTTPS */
|
|
|
|
#else /* RANDOMIZE_URLS */
|
|
|
|
"LOCATION: http://%s:%u/%s" ROOTDESC_PATH "\r\n"
|
|
|
|
#ifdef ENABLE_HTTPS
|
|
|
|
"SECURELOCATION.UPNP.ORG: https://%s:%u/%s" ROOTDESC_PATH "\r\n"
|
|
|
|
#endif /* ENABLE_HTTPS */
|
|
|
|
#endif /* RANDOMIZE_URLS */
|
2013-02-07 12:27:09 +00:00
|
|
|
"SERVER: " MINIUPNPD_SERVER_STRING "\r\n"
|
|
|
|
"NT: %s%s\r\n"
|
|
|
|
"USN: %s%s%s%s\r\n"
|
|
|
|
"NTS: ssdp:alive\r\n"
|
|
|
|
"OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */
|
|
|
|
"01-NLS: %u\r\n" /* same as BOOTID field. UDA v1.1 */
|
|
|
|
"BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
|
|
|
|
"CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
|
|
|
|
"\r\n",
|
2014-05-22 08:12:26 +00:00
|
|
|
dest_str, SSDP_PORT, /* HOST: */
|
|
|
|
lifetime, /* CACHE-CONTROL: */
|
|
|
|
host, (unsigned int)http_port, /* LOCATION: */
|
2015-09-14 10:10:15 +00:00
|
|
|
#ifdef RANDOMIZE_URLS
|
|
|
|
random_url,
|
|
|
|
#endif /* RANDOMIZE_URLS */
|
2014-04-09 13:35:06 +00:00
|
|
|
#ifdef ENABLE_HTTPS
|
2014-05-22 08:12:26 +00:00
|
|
|
host, (unsigned int)https_port, /* SECURE-LOCATION: */
|
2015-09-14 10:10:15 +00:00
|
|
|
#ifdef RANDOMIZE_URLS
|
|
|
|
random_url,
|
|
|
|
#endif /* RANDOMIZE_URLS */
|
|
|
|
#endif /* ENABLE_HTTPS */
|
2021-05-21 22:16:40 +00:00
|
|
|
#ifdef DYNAMIC_OS_VERSION
|
2021-05-22 21:30:05 +00:00
|
|
|
os_version, /* SERVER: */
|
2021-05-21 22:16:40 +00:00
|
|
|
#endif
|
2014-05-22 08:12:26 +00:00
|
|
|
nt, suffix, /* NT: */
|
|
|
|
usn1, usn2, usn3, suffix, /* USN: */
|
|
|
|
upnp_bootid, /* 01-NLS: */
|
|
|
|
upnp_bootid, /* BOOTID.UPNP.ORG: */
|
|
|
|
upnp_configid ); /* CONFIGID.UPNP.ORG: */
|
2014-10-22 10:11:34 +00:00
|
|
|
if(l<0) {
|
2014-04-09 13:35:06 +00:00
|
|
|
syslog(LOG_ERR, "%s: snprintf error", "SendSSDPNotify()");
|
2013-02-07 12:27:09 +00:00
|
|
|
return;
|
2014-10-22 10:11:34 +00:00
|
|
|
} else if((unsigned int)l >= sizeof(bufr)) {
|
2014-04-09 13:36:38 +00:00
|
|
|
syslog(LOG_WARNING, "%s: truncated output (%u>=%u)",
|
|
|
|
"SendSSDPNotify()", (unsigned)l, (unsigned)sizeof(bufr));
|
2013-04-26 15:18:28 +00:00
|
|
|
l = sizeof(bufr) - 1;
|
2013-02-07 12:27:09 +00:00
|
|
|
}
|
2014-05-22 08:12:26 +00:00
|
|
|
n = sendto_or_schedule(s, bufr, l, 0, dest, dest_len);
|
2014-10-22 10:11:34 +00:00
|
|
|
if(n < 0) {
|
2013-02-07 12:27:09 +00:00
|
|
|
syslog(LOG_ERR, "sendto(udp_notify=%d, %s): %m", s,
|
|
|
|
host ? host : "NULL");
|
2014-10-22 10:11:34 +00:00
|
|
|
} else if(n != l) {
|
2013-02-07 12:27:09 +00:00
|
|
|
syslog(LOG_NOTICE, "sendto() sent %d out of %d bytes", n, l);
|
|
|
|
}
|
2014-02-25 10:37:36 +00:00
|
|
|
/* 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. */
|
2014-05-22 08:12:26 +00:00
|
|
|
n = sendto_schedule(s, bufr, l, 0, dest, dest_len, 250);
|
2014-10-22 10:11:34 +00:00
|
|
|
if(n < 0) {
|
2014-02-25 10:37:36 +00:00
|
|
|
syslog(LOG_ERR, "sendto(udp_notify=%d, %s): %m", s,
|
|
|
|
host ? host : "NULL");
|
|
|
|
}
|
2013-02-07 12:27:09 +00:00
|
|
|
}
|
|
|
|
|
2014-10-22 10:11:34 +00:00
|
|
|
/* SendSSDPNotifies() send SSPD NOTIFY for a specific
|
|
|
|
* LAN (network interface) for all devices / services */
|
2014-04-09 13:35:06 +00:00
|
|
|
#ifdef ENABLE_HTTPS
|
2014-04-20 16:05:22 +00:00
|
|
|
static void
|
|
|
|
SendSSDPNotifies(int s, const char * host, unsigned short http_port,
|
2014-04-09 13:35:06 +00:00
|
|
|
unsigned short https_port,
|
2012-04-06 17:30:19 +00:00
|
|
|
unsigned int lifetime, int ipv6)
|
2014-04-20 16:05:22 +00:00
|
|
|
#else
|
|
|
|
static void
|
|
|
|
SendSSDPNotifies(int s, const char * host, unsigned short http_port,
|
|
|
|
unsigned int lifetime, int ipv6)
|
|
|
|
#endif
|
2011-09-28 19:13:20 +00:00
|
|
|
{
|
2012-04-06 17:30:19 +00:00
|
|
|
#ifdef ENABLE_IPV6
|
|
|
|
struct sockaddr_storage sockname;
|
2016-02-19 13:20:45 +00:00
|
|
|
/* UDA 1.1 AnnexA and UDA 2.0 only allow/define the use of
|
|
|
|
* Link-Local and Site-Local multicast scopes */
|
2014-10-22 11:57:24 +00:00
|
|
|
static struct { const char * p1, * p2; } const mcast_addrs[] =
|
2014-10-22 10:11:34 +00:00
|
|
|
{ { LL_SSDP_MCAST_ADDR, "[" LL_SSDP_MCAST_ADDR "]" }, /* Link Local */
|
|
|
|
{ SL_SSDP_MCAST_ADDR, "[" SL_SSDP_MCAST_ADDR "]" }, /* Site Local */
|
2016-02-19 13:20:45 +00:00
|
|
|
#ifndef UPNP_STRICT
|
2014-10-22 10:11:34 +00:00
|
|
|
{ GL_SSDP_MCAST_ADDR, "[" GL_SSDP_MCAST_ADDR "]" }, /* Global */
|
2016-02-19 13:20:45 +00:00
|
|
|
#endif /* ! UPNP_STRICT */
|
2014-05-23 15:05:15 +00:00
|
|
|
{ NULL, NULL } };
|
|
|
|
int j;
|
2014-10-22 11:57:24 +00:00
|
|
|
#else /* ENABLE_IPV6 */
|
2011-09-28 19:13:20 +00:00
|
|
|
struct sockaddr_in sockname;
|
2014-10-22 11:57:24 +00:00
|
|
|
#endif /* ENABLE_IPV6 */
|
2014-05-22 08:12:26 +00:00
|
|
|
socklen_t sockname_len;
|
|
|
|
const char * dest_str;
|
2014-10-22 10:10:30 +00:00
|
|
|
int i;
|
2012-10-04 22:21:00 +00:00
|
|
|
char ver_str[4];
|
2014-10-06 12:44:23 +00:00
|
|
|
#ifndef ENABLE_IPV6
|
|
|
|
UNUSED(ipv6);
|
2014-10-22 10:11:34 +00:00
|
|
|
#endif /* ENABLE_IPV6 */
|
2011-09-28 19:13:20 +00:00
|
|
|
|
2012-04-06 17:30:19 +00:00
|
|
|
memset(&sockname, 0, sizeof(sockname));
|
|
|
|
#ifdef ENABLE_IPV6
|
2014-10-22 10:11:34 +00:00
|
|
|
/* first iterate destinations for this LAN interface (only 1 for IPv4) */
|
2014-05-23 15:05:15 +00:00
|
|
|
for(j = 0; (mcast_addrs[j].p1 != 0 && ipv6) || j < 1; j++) {
|
2014-10-22 10:10:30 +00:00
|
|
|
if(ipv6) {
|
|
|
|
struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockname;
|
|
|
|
sockname_len = sizeof(struct sockaddr_in6);
|
|
|
|
p->sin6_family = AF_INET6;
|
|
|
|
p->sin6_port = htons(SSDP_PORT);
|
|
|
|
inet_pton(AF_INET6, mcast_addrs[j].p1, &(p->sin6_addr));
|
|
|
|
dest_str = mcast_addrs[j].p2;
|
|
|
|
/* UPnP Device Architecture 1.1 :
|
|
|
|
* Devices MUST multicast SSDP messages for each of the UPnP-enabled
|
|
|
|
* interfaces. The scope of multicast SSDP messages MUST be
|
|
|
|
* link local FF02::C if the message is sent from a link local address.
|
|
|
|
* If the message is sent from a global address it MUST be multicast
|
|
|
|
* using either global scope FF0E::C or site local scope FF05::C.
|
|
|
|
* In networks with complex topologies and overlapping sites, use of
|
|
|
|
* global scope is RECOMMENDED. */
|
|
|
|
} else {
|
|
|
|
#else /* ENABLE_IPV6 */
|
2013-06-14 17:10:59 +00:00
|
|
|
{
|
2014-10-22 10:10:30 +00:00
|
|
|
#endif /* ENABLE_IPV6 */
|
|
|
|
/* IPv4 */
|
|
|
|
struct sockaddr_in *p = (struct sockaddr_in *)&sockname;
|
|
|
|
sockname_len = sizeof(struct sockaddr_in);
|
|
|
|
p->sin_family = AF_INET;
|
|
|
|
p->sin_port = htons(SSDP_PORT);
|
|
|
|
p->sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
|
|
|
|
dest_str = SSDP_MCAST_ADDR;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* iterate all services / devices */
|
|
|
|
for(i = 0; known_service_types[i].s; i++) {
|
|
|
|
if(i==0)
|
|
|
|
ver_str[0] = '\0';
|
|
|
|
else
|
|
|
|
snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version);
|
2014-05-22 08:12:26 +00:00
|
|
|
SendSSDPNotify(s, (struct sockaddr *)&sockname, sockname_len, dest_str,
|
|
|
|
host, http_port,
|
2014-04-09 13:35:06 +00:00
|
|
|
#ifdef ENABLE_HTTPS
|
|
|
|
https_port,
|
|
|
|
#endif
|
2014-10-22 10:10:30 +00:00
|
|
|
known_service_types[i].s, ver_str, /* NT: */
|
|
|
|
known_service_types[i].uuid, "::",
|
|
|
|
known_service_types[i].s, /* ver_str, USN: */
|
2014-05-22 08:12:26 +00:00
|
|
|
lifetime);
|
2014-10-22 10:10:30 +00:00
|
|
|
/* for devices, also send NOTIFY on the uuid */
|
2020-05-29 16:01:39 +00:00
|
|
|
if(i > 0 && /* only known_service_types[0].s is shorter than "urn:schemas-upnp-org:device" */
|
|
|
|
0==memcmp(known_service_types[i].s,
|
2014-10-22 10:10:30 +00:00
|
|
|
"urn:schemas-upnp-org:device", sizeof("urn:schemas-upnp-org:device")-1)) {
|
|
|
|
SendSSDPNotify(s, (struct sockaddr *)&sockname, sockname_len, dest_str,
|
|
|
|
host, http_port,
|
|
|
|
#ifdef ENABLE_HTTPS
|
|
|
|
https_port,
|
2014-05-23 15:05:15 +00:00
|
|
|
#endif
|
2014-10-22 10:10:30 +00:00
|
|
|
known_service_types[i].uuid, "", /* NT: */
|
|
|
|
known_service_types[i].uuid, "", "", /* ver_str, USN: */
|
|
|
|
lifetime);
|
|
|
|
}
|
|
|
|
} /* for(i = 0; known_service_types[i].s; i++) */
|
|
|
|
#ifdef ENABLE_IPV6
|
|
|
|
} /* for(j = 0; (mcast_addrs[j].p1 != 0 && ipv6) || j < 1; j++) */
|
|
|
|
#endif /* ENABLE_IPV6 */
|
2011-09-28 19:13:20 +00:00
|
|
|
}
|
|
|
|
|
2014-10-22 10:11:34 +00:00
|
|
|
/* SendSSDPNotifies2() sends SSDP NOTIFY packets on all interfaces
|
|
|
|
* for all destinations, all devices / services */
|
2011-09-28 19:13:20 +00:00
|
|
|
void
|
|
|
|
SendSSDPNotifies2(int * sockets,
|
2014-04-20 16:05:22 +00:00
|
|
|
unsigned short http_port,
|
2014-04-09 13:35:06 +00:00
|
|
|
#ifdef ENABLE_HTTPS
|
|
|
|
unsigned short https_port,
|
|
|
|
#endif
|
2011-09-28 19:13:20 +00:00
|
|
|
unsigned int lifetime)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct lan_addr_s * lan_addr;
|
2014-10-22 10:11:34 +00:00
|
|
|
for(i = 0, lan_addr = lan_addrs.lh_first;
|
2012-04-06 17:30:19 +00:00
|
|
|
lan_addr != NULL;
|
2014-10-22 10:11:34 +00:00
|
|
|
lan_addr = lan_addr->list.le_next) {
|
2014-04-20 16:05:22 +00:00
|
|
|
SendSSDPNotifies(sockets[i], lan_addr->str, http_port,
|
2014-04-09 13:35:06 +00:00
|
|
|
#ifdef ENABLE_HTTPS
|
|
|
|
https_port,
|
|
|
|
#endif
|
2012-04-06 17:30:19 +00:00
|
|
|
lifetime, 0);
|
|
|
|
i++;
|
|
|
|
#ifdef ENABLE_IPV6
|
2014-10-22 10:11:34 +00:00
|
|
|
if(sockets[i] >= 0) {
|
2014-04-20 16:05:22 +00:00
|
|
|
SendSSDPNotifies(sockets[i], ipv6_addr_for_http_with_brackets, http_port,
|
2014-04-09 13:35:06 +00:00
|
|
|
#ifdef ENABLE_HTTPS
|
|
|
|
https_port,
|
|
|
|
#endif
|
2014-02-06 09:55:28 +00:00
|
|
|
lifetime, 1);
|
|
|
|
}
|
2012-04-06 17:30:19 +00:00
|
|
|
i++;
|
2014-10-22 10:11:34 +00:00
|
|
|
#endif /* ENABLE_IPV6 */
|
2011-09-28 19:13:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ProcessSSDPRequest()
|
|
|
|
* process SSDP M-SEARCH requests and responds to them */
|
|
|
|
void
|
2014-04-09 13:35:06 +00:00
|
|
|
#ifdef ENABLE_HTTPS
|
2014-04-20 16:05:22 +00:00
|
|
|
ProcessSSDPRequest(int s, unsigned short http_port, unsigned short https_port)
|
2014-04-09 13:35:06 +00:00
|
|
|
#else
|
2014-04-20 16:05:22 +00:00
|
|
|
ProcessSSDPRequest(int s, unsigned short http_port)
|
2014-04-09 13:35:06 +00:00
|
|
|
#endif
|
2011-09-28 19:13:20 +00:00
|
|
|
{
|
|
|
|
int n;
|
|
|
|
char bufr[1500];
|
|
|
|
#ifdef ENABLE_IPV6
|
|
|
|
struct sockaddr_storage sendername;
|
|
|
|
#else
|
|
|
|
struct sockaddr_in sendername;
|
|
|
|
#endif
|
2017-05-24 09:04:53 +00:00
|
|
|
int source_ifindex = -1;
|
2019-09-24 10:25:51 +00:00
|
|
|
#if defined(IP_RECVIF) || defined(IP_PKTINFO)
|
2017-05-24 09:04:53 +00:00
|
|
|
#ifdef IP_RECVIF
|
|
|
|
char cmbuf[CMSG_SPACE(sizeof(struct sockaddr_dl))];
|
2019-09-24 10:25:51 +00:00
|
|
|
#else /* IP_PKTINFO */
|
|
|
|
char cmbuf[CMSG_SPACE(sizeof(struct in_pktinfo))];
|
|
|
|
#endif
|
2017-05-24 09:04:53 +00:00
|
|
|
struct iovec iovec = {
|
|
|
|
.iov_base = bufr,
|
|
|
|
.iov_len = sizeof(bufr)
|
|
|
|
};
|
|
|
|
struct msghdr mh = {
|
|
|
|
.msg_name = &sendername,
|
|
|
|
.msg_namelen = sizeof(sendername),
|
|
|
|
.msg_iov = &iovec,
|
|
|
|
.msg_iovlen = 1,
|
|
|
|
.msg_control = cmbuf,
|
|
|
|
.msg_controllen = sizeof(cmbuf)
|
|
|
|
};
|
|
|
|
struct cmsghdr *cmptr;
|
2011-09-28 19:13:20 +00:00
|
|
|
|
2017-05-24 09:04:53 +00:00
|
|
|
n = recvmsg(s, &mh, 0);
|
|
|
|
#else
|
|
|
|
socklen_t len_r;
|
|
|
|
len_r = sizeof(sendername);
|
2011-09-28 19:13:20 +00:00
|
|
|
n = recvfrom(s, bufr, sizeof(bufr), 0,
|
|
|
|
(struct sockaddr *)&sendername, &len_r);
|
2017-05-24 09:04:53 +00:00
|
|
|
#endif /* defined(IP_RECVIF) || defined(IP_PKTINFO) */
|
2011-09-28 19:13:20 +00:00
|
|
|
if(n < 0)
|
|
|
|
{
|
2012-02-02 17:45:43 +00:00
|
|
|
/* EAGAIN, EWOULDBLOCK, EINTR : silently ignore (try again next time)
|
|
|
|
* other errors : log to LOG_ERR */
|
|
|
|
if(errno != EAGAIN &&
|
|
|
|
errno != EWOULDBLOCK &&
|
|
|
|
errno != EINTR)
|
|
|
|
{
|
2023-05-10 23:19:22 +00:00
|
|
|
#if defined(IP_RECVIF) || defined(IP_PKTINFO)
|
|
|
|
syslog(LOG_ERR, "recvmsg(udp): %m");
|
|
|
|
#else
|
2012-02-02 17:45:43 +00:00
|
|
|
syslog(LOG_ERR, "recvfrom(udp): %m");
|
2023-05-10 23:19:22 +00:00
|
|
|
#endif
|
2012-02-02 17:45:43 +00:00
|
|
|
}
|
2011-09-28 19:13:20 +00:00
|
|
|
return;
|
|
|
|
}
|
2017-05-24 09:04:53 +00:00
|
|
|
|
|
|
|
#if defined(IP_RECVIF) || defined(IP_PKTINFO)
|
|
|
|
for(cmptr = CMSG_FIRSTHDR(&mh); cmptr != NULL; cmptr = CMSG_NXTHDR(&mh, cmptr))
|
|
|
|
{
|
|
|
|
syslog(LOG_DEBUG, "level=%d type=%d", cmptr->cmsg_level, cmptr->cmsg_type);
|
2019-09-24 10:25:51 +00:00
|
|
|
#ifdef IP_RECVIF
|
|
|
|
if(cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
|
|
|
|
{
|
|
|
|
struct sockaddr_dl *sdl; /* fields : len, family, index, type, nlen, alen, slen, data */
|
|
|
|
sdl = (struct sockaddr_dl *)CMSG_DATA(cmptr);
|
|
|
|
syslog(LOG_DEBUG, "sdl_index = %d %s", sdl->sdl_index, link_ntoa(sdl));
|
|
|
|
source_ifindex = sdl->sdl_index;
|
|
|
|
}
|
|
|
|
#elif defined(IP_PKTINFO) /* IP_RECVIF */
|
2017-05-24 09:04:53 +00:00
|
|
|
if(cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
|
|
|
|
{
|
|
|
|
struct in_pktinfo * pi; /* fields : ifindex, spec_dst, addr */
|
|
|
|
pi = (struct in_pktinfo *)CMSG_DATA(cmptr);
|
|
|
|
syslog(LOG_DEBUG, "ifindex = %u %s", pi->ipi_ifindex, inet_ntoa(pi->ipi_spec_dst));
|
|
|
|
source_ifindex = pi->ipi_ifindex;
|
|
|
|
}
|
|
|
|
#endif /* IP_PKTINFO */
|
|
|
|
#if defined(ENABLE_IPV6) && defined(IPV6_RECVPKTINFO)
|
|
|
|
if(cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == IPV6_RECVPKTINFO)
|
|
|
|
{
|
|
|
|
struct in6_pktinfo * pi6; /* fields : ifindex, addr */
|
|
|
|
pi6 = (struct in6_pktinfo *)CMSG_DATA(cmptr);
|
|
|
|
syslog(LOG_DEBUG, "ifindex = %u", pi6->ipi6_ifindex);
|
|
|
|
source_ifindex = pi6->ipi6_ifindex;
|
|
|
|
}
|
|
|
|
#endif /* defined(ENABLE_IPV6) && defined(IPV6_RECVPKTINFO) */
|
|
|
|
}
|
|
|
|
#endif /* defined(IP_RECVIF) || defined(IP_PKTINFO) */
|
2014-04-09 13:35:06 +00:00
|
|
|
#ifdef ENABLE_HTTPS
|
2017-05-24 22:20:03 +00:00
|
|
|
ProcessSSDPData(s, bufr, n, (struct sockaddr *)&sendername, source_ifindex,
|
2014-04-20 16:05:22 +00:00
|
|
|
http_port, https_port);
|
2014-04-09 13:35:06 +00:00
|
|
|
#else
|
2017-05-24 22:20:03 +00:00
|
|
|
ProcessSSDPData(s, bufr, n, (struct sockaddr *)&sendername, source_ifindex,
|
2014-04-20 16:05:22 +00:00
|
|
|
http_port);
|
2014-04-09 13:35:06 +00:00
|
|
|
#endif
|
2011-09-28 19:13:20 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2014-04-20 16:05:22 +00:00
|
|
|
#ifdef ENABLE_HTTPS
|
2011-09-28 19:13:20 +00:00
|
|
|
void
|
|
|
|
ProcessSSDPData(int s, const char *bufr, int n,
|
2017-05-24 22:20:03 +00:00
|
|
|
const struct sockaddr * sender, int source_if,
|
2014-04-20 16:05:22 +00:00
|
|
|
unsigned short http_port, unsigned short https_port)
|
2014-04-09 13:35:06 +00:00
|
|
|
#else
|
2014-04-20 16:05:22 +00:00
|
|
|
void
|
|
|
|
ProcessSSDPData(int s, const char *bufr, int n,
|
2017-05-24 22:20:03 +00:00
|
|
|
const struct sockaddr * sender, int source_if,
|
2014-04-20 16:05:22 +00:00
|
|
|
unsigned short http_port)
|
2014-04-09 13:35:06 +00:00
|
|
|
#endif
|
|
|
|
{
|
2011-09-28 19:13:20 +00:00
|
|
|
int i, l;
|
|
|
|
struct lan_addr_s * lan_addr = NULL;
|
|
|
|
const char * st = NULL;
|
|
|
|
int st_len = 0;
|
2012-09-28 09:13:48 +00:00
|
|
|
int st_ver = 0;
|
2011-09-28 19:13:20 +00:00
|
|
|
char sender_str[64];
|
2012-10-04 22:21:00 +00:00
|
|
|
char ver_str[4];
|
2011-09-28 19:13:20 +00:00
|
|
|
const char * announced_host = NULL;
|
2012-06-23 23:41:47 +00:00
|
|
|
#ifdef UPNP_STRICT
|
2013-02-07 10:00:15 +00:00
|
|
|
#ifdef ENABLE_IPV6
|
2012-06-23 23:41:47 +00:00
|
|
|
char announced_host_buf[64];
|
2012-09-28 09:04:04 +00:00
|
|
|
#endif
|
2014-02-25 10:16:34 +00:00
|
|
|
#endif
|
|
|
|
#if defined(UPNP_STRICT) || defined(DELAY_MSEARCH_RESPONSE)
|
2013-02-07 10:00:15 +00:00
|
|
|
int mx_value = -1;
|
2012-06-23 23:41:47 +00:00
|
|
|
#endif
|
2014-08-01 10:30:55 +00:00
|
|
|
unsigned int delay = 50; /* Non-zero default delay to prevent flooding */
|
2014-02-25 10:15:30 +00:00
|
|
|
/* 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. */
|
2017-12-12 09:42:54 +00:00
|
|
|
char atoi_buffer[8];
|
2011-09-28 19:13:20 +00:00
|
|
|
|
|
|
|
/* get the string representation of the sender address */
|
|
|
|
sockaddr_to_string(sender, sender_str, sizeof(sender_str));
|
2013-02-06 14:20:04 +00:00
|
|
|
lan_addr = get_lan_for_peer(sender);
|
2020-04-28 22:03:54 +00:00
|
|
|
if(source_if > 0)
|
2017-05-24 22:20:03 +00:00
|
|
|
{
|
|
|
|
if(lan_addr != NULL)
|
|
|
|
{
|
2020-04-28 22:03:54 +00:00
|
|
|
#ifndef MULTIPLE_EXTERNAL_IP
|
|
|
|
if(lan_addr->index != (unsigned)source_if && lan_addr->index != 0
|
|
|
|
&& !(lan_addr->add_indexes & (1UL << (source_if - 1))))
|
|
|
|
#else
|
2018-04-12 08:49:53 +00:00
|
|
|
if(lan_addr->index != (unsigned)source_if && lan_addr->index != 0)
|
2020-04-28 22:03:54 +00:00
|
|
|
#endif
|
2017-05-24 22:20:03 +00:00
|
|
|
{
|
|
|
|
syslog(LOG_WARNING, "interface index not matching %u != %d", lan_addr->index, source_if);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* use the interface index */
|
|
|
|
for(lan_addr = lan_addrs.lh_first;
|
|
|
|
lan_addr != NULL;
|
|
|
|
lan_addr = lan_addr->list.le_next)
|
|
|
|
{
|
|
|
|
if(lan_addr->index == (unsigned)source_if)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-02-06 14:20:04 +00:00
|
|
|
if(lan_addr == NULL)
|
|
|
|
{
|
2017-05-24 22:20:03 +00:00
|
|
|
syslog(LOG_WARNING, "SSDP packet sender %s (if_index=%d) not from a LAN, ignoring",
|
|
|
|
sender_str, source_if);
|
2013-02-06 14:20:04 +00:00
|
|
|
return;
|
|
|
|
}
|
2011-09-28 19:13:20 +00:00
|
|
|
|
|
|
|
if(memcmp(bufr, "NOTIFY", 6) == 0)
|
|
|
|
{
|
|
|
|
/* ignore NOTIFY packets. We could log the sender and device type */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if(memcmp(bufr, "M-SEARCH", 8) == 0)
|
|
|
|
{
|
|
|
|
i = 0;
|
|
|
|
while(i < n)
|
|
|
|
{
|
|
|
|
while((i < n - 1) && (bufr[i] != '\r' || bufr[i+1] != '\n'))
|
|
|
|
i++;
|
|
|
|
i += 2;
|
|
|
|
if((i < n - 3) && (strncasecmp(bufr+i, "st:", 3) == 0))
|
|
|
|
{
|
|
|
|
st = bufr+i+3;
|
|
|
|
st_len = 0;
|
2018-12-05 23:11:21 +00:00
|
|
|
while((st < bufr + n) && (*st == ' ' || *st == '\t'))
|
2011-09-28 19:13:20 +00:00
|
|
|
st++;
|
2017-12-12 09:42:54 +00:00
|
|
|
while((st + st_len < bufr + n)
|
|
|
|
&& (st[st_len]!='\r' && st[st_len]!='\n'))
|
2011-09-28 19:13:20 +00:00
|
|
|
st_len++;
|
2012-09-28 09:13:48 +00:00
|
|
|
l = st_len;
|
|
|
|
while(l > 0 && st[l-1] != ':')
|
|
|
|
l--;
|
2017-12-12 09:42:54 +00:00
|
|
|
memset(atoi_buffer, 0, sizeof(atoi_buffer));
|
|
|
|
memcpy(atoi_buffer, st + l, MIN((int)(sizeof(atoi_buffer) - 1), st_len - l));
|
|
|
|
st_ver = atoi(atoi_buffer);
|
2012-09-28 09:13:48 +00:00
|
|
|
syslog(LOG_DEBUG, "ST: %.*s (ver=%d)", st_len, st, st_ver);
|
2011-09-28 19:13:20 +00:00
|
|
|
/*j = 0;*/
|
|
|
|
/*while(bufr[i+j]!='\r') j++;*/
|
|
|
|
/*syslog(LOG_INFO, "%.*s", j, bufr+i);*/
|
|
|
|
}
|
2014-02-25 10:16:34 +00:00
|
|
|
#if defined(UPNP_STRICT) || defined(DELAY_MSEARCH_RESPONSE)
|
2013-02-07 10:00:15 +00:00
|
|
|
else if((i < n - 3) && (strncasecmp(bufr+i, "mx:", 3) == 0))
|
|
|
|
{
|
|
|
|
const char * mx;
|
|
|
|
int mx_len;
|
|
|
|
mx = bufr+i+3;
|
|
|
|
mx_len = 0;
|
2017-12-12 09:42:54 +00:00
|
|
|
while((mx < bufr + n) && (*mx == ' ' || *mx == '\t'))
|
2013-02-07 10:00:15 +00:00
|
|
|
mx++;
|
2017-12-12 09:42:54 +00:00
|
|
|
while((mx + mx_len < bufr + n)
|
|
|
|
&& (mx[mx_len]!='\r' && mx[mx_len]!='\n'))
|
2013-02-07 10:00:15 +00:00
|
|
|
mx_len++;
|
2017-12-12 09:42:54 +00:00
|
|
|
memset(atoi_buffer, 0, sizeof(atoi_buffer));
|
|
|
|
memcpy(atoi_buffer, mx, MIN((int)(sizeof(atoi_buffer) - 1), mx_len));
|
|
|
|
mx_value = atoi(atoi_buffer);
|
2013-02-07 10:00:15 +00:00
|
|
|
syslog(LOG_DEBUG, "MX: %.*s (value=%d)", mx_len, mx, mx_value);
|
|
|
|
}
|
2016-12-23 11:12:49 +00:00
|
|
|
#endif /* defined(UPNP_STRICT) || defined(DELAY_MSEARCH_RESPONSE) */
|
|
|
|
#if defined(UPNP_STRICT)
|
|
|
|
/* Fix UDA-1.2.10 Man header empty or invalid */
|
2019-10-05 22:17:52 +00:00
|
|
|
else if((i < n - 4) && (strncasecmp(bufr+i, "man:", 4) == 0))
|
2016-12-23 11:12:49 +00:00
|
|
|
{
|
|
|
|
const char * man;
|
|
|
|
int man_len;
|
|
|
|
man = bufr+i+4;
|
|
|
|
man_len = 0;
|
2017-12-12 09:42:54 +00:00
|
|
|
while((man < bufr + n) && (*man == ' ' || *man == '\t'))
|
2016-12-23 11:12:49 +00:00
|
|
|
man++;
|
2017-12-12 09:42:54 +00:00
|
|
|
while((man + man_len < bufr + n)
|
|
|
|
&& (man[man_len]!='\r' && man[man_len]!='\n'))
|
2016-12-23 11:12:49 +00:00
|
|
|
man_len++;
|
2017-12-12 09:42:54 +00:00
|
|
|
if((man_len < 15) || (strncmp(man, "\"ssdp:discover\"", 15) != 0)) {
|
2016-12-23 11:12:49 +00:00
|
|
|
syslog(LOG_INFO, "ignoring SSDP packet MAN empty or invalid header");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* defined(UPNP_STRICT) */
|
2013-02-07 10:00:15 +00:00
|
|
|
}
|
|
|
|
#ifdef UPNP_STRICT
|
2014-02-25 10:16:34 +00:00
|
|
|
/* 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. */
|
2013-02-07 10:00:15 +00:00
|
|
|
if(mx_value < 0) {
|
|
|
|
syslog(LOG_INFO, "ignoring SSDP packet missing MX: header");
|
|
|
|
return;
|
2014-02-25 10:16:34 +00:00
|
|
|
} 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;
|
2011-09-28 19:13:20 +00:00
|
|
|
}
|
2013-02-07 10:00:15 +00:00
|
|
|
#endif
|
2011-09-28 19:13:20 +00:00
|
|
|
/*syslog(LOG_INFO, "SSDP M-SEARCH packet received from %s",
|
|
|
|
sender_str );*/
|
|
|
|
if(st && (st_len > 0))
|
|
|
|
{
|
|
|
|
syslog(LOG_INFO, "SSDP M-SEARCH from %s ST: %.*s",
|
|
|
|
sender_str, st_len, st);
|
|
|
|
/* find in which sub network the client is */
|
2020-06-05 20:39:59 +00:00
|
|
|
#ifdef ENABLE_IPV6
|
|
|
|
if((sender->sa_family == AF_INET) ||
|
|
|
|
(sender->sa_family == AF_INET6 &&
|
|
|
|
IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)sender)->sin6_addr)))
|
|
|
|
#else
|
2011-09-28 19:13:20 +00:00
|
|
|
if(sender->sa_family == AF_INET)
|
2020-06-05 20:39:59 +00:00
|
|
|
#endif
|
2011-09-28 19:13:20 +00:00
|
|
|
{
|
|
|
|
if (lan_addr == NULL)
|
|
|
|
{
|
2015-01-20 13:13:18 +00:00
|
|
|
syslog(LOG_ERR,
|
|
|
|
"Can't find in which sub network the client %s is",
|
|
|
|
sender_str);
|
2011-09-28 19:13:20 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
announced_host = lan_addr->str;
|
|
|
|
}
|
|
|
|
#ifdef ENABLE_IPV6
|
2020-06-05 20:39:59 +00:00
|
|
|
else if(sender->sa_family == AF_INET6)
|
2011-09-28 19:13:20 +00:00
|
|
|
{
|
|
|
|
/* IPv6 address with brackets */
|
2012-06-23 23:41:47 +00:00
|
|
|
#ifdef UPNP_STRICT
|
2013-02-06 14:06:59 +00:00
|
|
|
int index;
|
2012-06-23 23:41:47 +00:00
|
|
|
struct in6_addr addr6;
|
|
|
|
size_t addr6_len = sizeof(addr6);
|
|
|
|
/* retrieve the IPv6 address which
|
|
|
|
* will be used locally to reach sender */
|
|
|
|
memset(&addr6, 0, sizeof(addr6));
|
2024-05-15 23:50:00 +00:00
|
|
|
/* UPnP Device Architecture 2.0 - Annex A - IP version 6 support
|
|
|
|
* p112 A2.3 :
|
|
|
|
* a) Devices and control points SHALL use only the Link-Local
|
|
|
|
* unicast address as the source address and when specifying
|
|
|
|
* a literal IP address in LOCATION URLs in all multicast
|
|
|
|
* messages that are multicast to the Link-Local scope FF02::C
|
|
|
|
* for SSDP and FF02::130 for multicast eventing.
|
|
|
|
* f) Devices and control points SHALL use an acquired ULA or
|
|
|
|
* GUA in all multicast messages as the source address and
|
|
|
|
* when specifying a literal IP address in LOCATION URLs
|
|
|
|
* that are multicast to the Site-Local scope addresses of
|
|
|
|
* either FF05::C or FF05::130 */
|
2017-03-13 12:01:00 +00:00
|
|
|
if(IN6_IS_ADDR_LINKLOCAL(&(((struct sockaddr_in6 *)sender)->sin6_addr))) {
|
|
|
|
get_link_local_addr(((struct sockaddr_in6 *)sender)->sin6_scope_id, &addr6);
|
|
|
|
} else if(get_src_for_route_to (sender, &addr6, &addr6_len, &index) < 0) {
|
2012-06-23 23:41:47 +00:00
|
|
|
syslog(LOG_WARNING, "get_src_for_route_to() failed, using %s", ipv6_addr_for_http_with_brackets);
|
|
|
|
announced_host = ipv6_addr_for_http_with_brackets;
|
2017-03-13 12:01:00 +00:00
|
|
|
}
|
|
|
|
if(announced_host == NULL) {
|
2024-05-15 23:50:00 +00:00
|
|
|
/* convert addr6 to string with brackets */
|
2012-06-23 23:41:47 +00:00
|
|
|
if(inet_ntop(AF_INET6, &addr6,
|
|
|
|
announced_host_buf+1,
|
|
|
|
sizeof(announced_host_buf) - 2)) {
|
|
|
|
announced_host_buf[0] = '[';
|
|
|
|
i = strlen(announced_host_buf);
|
2012-06-28 19:00:49 +00:00
|
|
|
if(i < (int)sizeof(announced_host_buf) - 1) {
|
2012-06-23 23:41:47 +00:00
|
|
|
announced_host_buf[i] = ']';
|
|
|
|
announced_host_buf[i+1] = '\0';
|
|
|
|
} else {
|
|
|
|
syslog(LOG_NOTICE, "cannot suffix %s with ']'",
|
|
|
|
announced_host_buf);
|
|
|
|
}
|
|
|
|
announced_host = announced_host_buf;
|
|
|
|
} else {
|
|
|
|
syslog(LOG_NOTICE, "inet_ntop() failed %m");
|
|
|
|
announced_host = ipv6_addr_for_http_with_brackets;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
2011-09-28 19:13:20 +00:00
|
|
|
announced_host = ipv6_addr_for_http_with_brackets;
|
2012-06-23 23:41:47 +00:00
|
|
|
#endif
|
2011-09-28 19:13:20 +00:00
|
|
|
}
|
|
|
|
#endif
|
2020-06-05 20:39:59 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
syslog(LOG_ERR,
|
|
|
|
"Unknown address family %d for client %s",
|
|
|
|
sender->sa_family, sender_str);
|
|
|
|
return;
|
|
|
|
}
|
2011-09-28 19:13:20 +00:00
|
|
|
/* Responds to request with a device as ST header */
|
2012-09-28 09:13:48 +00:00
|
|
|
for(i = 0; known_service_types[i].s; i++)
|
2011-09-28 19:13:20 +00:00
|
|
|
{
|
2012-09-28 09:13:48 +00:00
|
|
|
l = (int)strlen(known_service_types[i].s);
|
|
|
|
if(l<=st_len && (0 == memcmp(st, known_service_types[i].s, l))
|
|
|
|
#ifdef UPNP_STRICT
|
|
|
|
&& (st_ver <= known_service_types[i].version)
|
|
|
|
/* only answer for service version lower or equal of supported one */
|
|
|
|
#endif
|
|
|
|
)
|
2011-09-28 19:13:20 +00:00
|
|
|
{
|
2014-02-25 10:45:51 +00:00
|
|
|
/* 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
|
2011-09-28 19:13:20 +00:00
|
|
|
syslog(LOG_INFO, "Single search found");
|
2014-02-25 10:16:34 +00:00
|
|
|
#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
|
2013-06-11 18:03:15 +00:00
|
|
|
SendSSDPResponse(s, sender,
|
2014-02-25 10:45:51 +00:00
|
|
|
#ifdef SSDP_RESPOND_SAME_VERSION
|
2013-06-11 18:03:15 +00:00
|
|
|
st, st_len, "",
|
2014-02-25 10:45:51 +00:00
|
|
|
#else
|
|
|
|
known_service_types[i].s, l, ver_str,
|
|
|
|
#endif
|
2014-04-20 16:05:22 +00:00
|
|
|
announced_host, http_port,
|
2014-04-09 13:35:06 +00:00
|
|
|
#ifdef ENABLE_HTTPS
|
|
|
|
https_port,
|
|
|
|
#endif
|
2014-02-25 10:15:30 +00:00
|
|
|
known_service_types[i].uuid,
|
|
|
|
delay);
|
2011-09-28 19:13:20 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Responds to request with ST: ssdp:all */
|
|
|
|
/* strlen("ssdp:all") == 8 */
|
|
|
|
if(st_len==8 && (0 == memcmp(st, "ssdp:all", 8)))
|
|
|
|
{
|
2014-02-25 10:16:34 +00:00
|
|
|
#ifdef DELAY_MSEARCH_RESPONSE
|
|
|
|
unsigned int delay_increment = (mx_value * 1000) / 15;
|
|
|
|
#endif
|
2011-09-28 19:13:20 +00:00
|
|
|
syslog(LOG_INFO, "ssdp:all found");
|
2012-09-28 09:13:48 +00:00
|
|
|
for(i=0; known_service_types[i].s; i++)
|
2011-09-28 19:13:20 +00:00
|
|
|
{
|
2014-02-25 10:16:34 +00:00
|
|
|
#ifdef DELAY_MSEARCH_RESPONSE
|
|
|
|
delay += delay_increment;
|
|
|
|
#endif
|
2012-10-04 22:21:00 +00:00
|
|
|
if(i==0)
|
|
|
|
ver_str[0] = '\0';
|
|
|
|
else
|
|
|
|
snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version);
|
2012-09-28 09:13:48 +00:00
|
|
|
l = (int)strlen(known_service_types[i].s);
|
2013-06-11 18:03:15 +00:00
|
|
|
SendSSDPResponse(s, sender,
|
|
|
|
known_service_types[i].s, l, ver_str,
|
2014-04-20 16:05:22 +00:00
|
|
|
announced_host, http_port,
|
2014-04-09 13:35:06 +00:00
|
|
|
#ifdef ENABLE_HTTPS
|
|
|
|
https_port,
|
|
|
|
#endif
|
2014-02-25 10:15:30 +00:00
|
|
|
known_service_types[i].uuid,
|
|
|
|
delay);
|
2011-09-28 19:13:20 +00:00
|
|
|
}
|
2012-09-28 09:13:48 +00:00
|
|
|
/* also answer for uuid */
|
2014-02-25 10:16:34 +00:00
|
|
|
#ifdef DELAY_MSEARCH_RESPONSE
|
|
|
|
delay += delay_increment;
|
|
|
|
#endif
|
2013-06-13 14:03:36 +00:00
|
|
|
SendSSDPResponse(s, sender, uuidvalue_igd, strlen(uuidvalue_igd), "",
|
2014-04-20 16:05:22 +00:00
|
|
|
announced_host, http_port,
|
2014-04-09 13:35:06 +00:00
|
|
|
#ifdef ENABLE_HTTPS
|
|
|
|
https_port,
|
|
|
|
#endif
|
|
|
|
uuidvalue_igd, delay);
|
2014-02-25 10:16:34 +00:00
|
|
|
#ifdef DELAY_MSEARCH_RESPONSE
|
|
|
|
delay += delay_increment;
|
|
|
|
#endif
|
2013-06-13 14:03:36 +00:00
|
|
|
SendSSDPResponse(s, sender, uuidvalue_wan, strlen(uuidvalue_wan), "",
|
2014-04-20 16:05:22 +00:00
|
|
|
announced_host, http_port,
|
2014-04-09 13:35:06 +00:00
|
|
|
#ifdef ENABLE_HTTPS
|
|
|
|
https_port,
|
|
|
|
#endif
|
|
|
|
uuidvalue_wan, delay);
|
2014-02-25 10:16:34 +00:00
|
|
|
#ifdef DELAY_MSEARCH_RESPONSE
|
|
|
|
delay += delay_increment;
|
|
|
|
#endif
|
2013-06-13 14:03:36 +00:00
|
|
|
SendSSDPResponse(s, sender, uuidvalue_wcd, strlen(uuidvalue_wcd), "",
|
2014-04-20 16:05:22 +00:00
|
|
|
announced_host, http_port,
|
2014-04-09 13:35:06 +00:00
|
|
|
#ifdef ENABLE_HTTPS
|
|
|
|
https_port,
|
|
|
|
#endif
|
|
|
|
uuidvalue_wcd, delay);
|
2011-09-28 19:13:20 +00:00
|
|
|
}
|
|
|
|
/* responds to request by UUID value */
|
2013-06-13 14:03:36 +00:00
|
|
|
l = (int)strlen(uuidvalue_igd);
|
|
|
|
if(l==st_len)
|
2011-09-28 19:13:20 +00:00
|
|
|
{
|
2014-02-25 10:16:34 +00:00
|
|
|
#ifdef DELAY_MSEARCH_RESPONSE
|
|
|
|
delay = random() / (1 + RAND_MAX / (1000 * mx_value));
|
|
|
|
#endif
|
2013-06-13 14:03:36 +00:00
|
|
|
if(0 == memcmp(st, uuidvalue_igd, l))
|
|
|
|
{
|
|
|
|
syslog(LOG_INFO, "ssdp:uuid (IGD) found");
|
|
|
|
SendSSDPResponse(s, sender, st, st_len, "",
|
2014-04-20 16:05:22 +00:00
|
|
|
announced_host, http_port,
|
2014-04-09 13:35:06 +00:00
|
|
|
#ifdef ENABLE_HTTPS
|
|
|
|
https_port,
|
|
|
|
#endif
|
|
|
|
uuidvalue_igd, delay);
|
2013-06-13 14:03:36 +00:00
|
|
|
}
|
|
|
|
else if(0 == memcmp(st, uuidvalue_wan, l))
|
|
|
|
{
|
|
|
|
syslog(LOG_INFO, "ssdp:uuid (WAN) found");
|
|
|
|
SendSSDPResponse(s, sender, st, st_len, "",
|
2014-04-20 16:05:22 +00:00
|
|
|
announced_host, http_port,
|
2014-04-09 13:35:06 +00:00
|
|
|
#ifdef ENABLE_HTTPS
|
|
|
|
https_port,
|
|
|
|
#endif
|
|
|
|
uuidvalue_wan, delay);
|
2013-06-13 14:03:36 +00:00
|
|
|
}
|
|
|
|
else if(0 == memcmp(st, uuidvalue_wcd, l))
|
|
|
|
{
|
|
|
|
syslog(LOG_INFO, "ssdp:uuid (WCD) found");
|
|
|
|
SendSSDPResponse(s, sender, st, st_len, "",
|
2014-04-20 16:05:22 +00:00
|
|
|
announced_host, http_port,
|
2014-04-09 13:35:06 +00:00
|
|
|
#ifdef ENABLE_HTTPS
|
|
|
|
https_port,
|
|
|
|
#endif
|
|
|
|
uuidvalue_wcd, delay);
|
2013-06-13 14:03:36 +00:00
|
|
|
}
|
2011-09-28 19:13:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
syslog(LOG_INFO, "Invalid SSDP M-SEARCH from %s", sender_str);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
syslog(LOG_NOTICE, "Unknown udp packet received from %s", sender_str);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-07 12:27:09 +00:00
|
|
|
static int
|
2014-05-23 10:07:39 +00:00
|
|
|
SendSSDPbyebye(int s, const struct sockaddr * dest, socklen_t destlen,
|
|
|
|
const char * dest_str,
|
2013-02-07 12:27:09 +00:00
|
|
|
const char * nt, const char * suffix,
|
2014-05-23 10:07:39 +00:00
|
|
|
const char * usn1, const char * usn2, const char * usn3)
|
2013-02-07 12:27:09 +00:00
|
|
|
{
|
|
|
|
int n, l;
|
2014-04-09 13:36:38 +00:00
|
|
|
char bufr[SSDP_PACKET_MAX_LEN];
|
2013-02-07 12:27:09 +00:00
|
|
|
|
|
|
|
l = snprintf(bufr, sizeof(bufr),
|
|
|
|
"NOTIFY * HTTP/1.1\r\n"
|
|
|
|
"HOST: %s:%d\r\n"
|
|
|
|
"NT: %s%s\r\n"
|
|
|
|
"USN: %s%s%s%s\r\n"
|
|
|
|
"NTS: ssdp:byebye\r\n"
|
|
|
|
"OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */
|
|
|
|
"01-NLS: %u\r\n" /* same as BOOTID field. UDA v1.1 */
|
|
|
|
"BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
|
|
|
|
"CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
|
|
|
|
"\r\n",
|
2014-05-23 10:07:39 +00:00
|
|
|
dest_str, SSDP_PORT, /* HOST : */
|
|
|
|
nt, suffix, /* NT: */
|
2013-02-07 12:27:09 +00:00
|
|
|
usn1, usn2, usn3, suffix, /* USN: */
|
2021-05-22 21:30:05 +00:00
|
|
|
upnp_bootid, /* 01-NLS: */
|
|
|
|
upnp_bootid, /* BOOTID.UPNP.ORG: */
|
|
|
|
upnp_configid); /* CONFIGID.UPNP.ORG: */
|
2013-02-07 12:27:09 +00:00
|
|
|
if(l<0)
|
|
|
|
{
|
2014-04-09 13:36:38 +00:00
|
|
|
syslog(LOG_ERR, "%s: snprintf error", "SendSSDPbyebye()");
|
2013-02-07 12:27:09 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if((unsigned int)l >= sizeof(bufr))
|
|
|
|
{
|
2014-04-09 13:36:38 +00:00
|
|
|
syslog(LOG_WARNING, "%s: truncated output (%u>=%u)",
|
|
|
|
"SendSSDPbyebye()", (unsigned)l, (unsigned)sizeof(bufr));
|
2013-04-26 15:18:28 +00:00
|
|
|
l = sizeof(bufr) - 1;
|
2013-02-07 12:27:09 +00:00
|
|
|
}
|
2014-05-23 10:07:39 +00:00
|
|
|
n = sendto_or_schedule(s, bufr, l, 0, dest, destlen);
|
2013-02-07 12:27:09 +00:00
|
|
|
if(n < 0)
|
|
|
|
{
|
|
|
|
syslog(LOG_ERR, "sendto(udp_shutdown=%d): %m", s);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if(n != l)
|
|
|
|
{
|
|
|
|
syslog(LOG_NOTICE, "sendto() sent %d out of %d bytes", n, l);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-03-01 01:44:38 +00:00
|
|
|
/* This will broadcast ssdp:byebye notifications to inform
|
2011-09-28 19:13:20 +00:00
|
|
|
* the network that UPnP is going down. */
|
|
|
|
int
|
|
|
|
SendSSDPGoodbye(int * sockets, int n_sockets)
|
|
|
|
{
|
2014-05-23 10:07:39 +00:00
|
|
|
struct sockaddr_in sockname4;
|
2012-04-06 17:30:19 +00:00
|
|
|
#ifdef ENABLE_IPV6
|
|
|
|
struct sockaddr_in6 sockname6;
|
2014-05-23 10:07:39 +00:00
|
|
|
struct sockaddr * sockname;
|
|
|
|
socklen_t socknamelen;
|
2014-10-06 12:44:23 +00:00
|
|
|
int ipv6 = 0;
|
2012-04-06 17:30:19 +00:00
|
|
|
#endif
|
2011-09-28 19:13:20 +00:00
|
|
|
int i, j;
|
2012-10-04 22:21:00 +00:00
|
|
|
char ver_str[4];
|
2012-02-09 20:33:24 +00:00
|
|
|
int ret = 0;
|
2014-05-23 10:07:39 +00:00
|
|
|
const char * dest_str;
|
2011-09-28 19:13:20 +00:00
|
|
|
|
2019-05-02 10:09:28 +00:00
|
|
|
memset(&sockname4, 0, sizeof(struct sockaddr_in));
|
|
|
|
sockname4.sin_family = AF_INET;
|
|
|
|
sockname4.sin_port = htons(SSDP_PORT);
|
|
|
|
sockname4.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
|
2012-04-06 17:30:19 +00:00
|
|
|
#ifdef ENABLE_IPV6
|
|
|
|
memset(&sockname6, 0, sizeof(struct sockaddr_in6));
|
|
|
|
sockname6.sin6_family = AF_INET6;
|
|
|
|
sockname6.sin6_port = htons(SSDP_PORT);
|
|
|
|
inet_pton(AF_INET6, LL_SSDP_MCAST_ADDR, &(sockname6.sin6_addr));
|
2014-05-23 10:07:39 +00:00
|
|
|
#else
|
|
|
|
dest_str = SSDP_MCAST_ADDR;
|
2012-04-06 17:30:19 +00:00
|
|
|
#endif
|
2011-09-28 19:13:20 +00:00
|
|
|
|
|
|
|
for(j=0; j<n_sockets; j++)
|
|
|
|
{
|
2015-04-28 07:01:30 +00:00
|
|
|
if(sockets[j] < 0)
|
|
|
|
continue;
|
2012-04-06 17:30:19 +00:00
|
|
|
#ifdef ENABLE_IPV6
|
|
|
|
ipv6 = j & 1;
|
2014-05-23 10:07:39 +00:00
|
|
|
if(ipv6) {
|
|
|
|
dest_str = "[" LL_SSDP_MCAST_ADDR "]";
|
|
|
|
sockname = (struct sockaddr *)&sockname6;
|
|
|
|
socknamelen = sizeof(struct sockaddr_in6);
|
|
|
|
} else {
|
|
|
|
dest_str = SSDP_MCAST_ADDR;
|
|
|
|
sockname = (struct sockaddr *)&sockname4;
|
|
|
|
socknamelen = sizeof(struct sockaddr_in);
|
|
|
|
}
|
2012-04-06 17:30:19 +00:00
|
|
|
#endif
|
2012-09-28 09:13:48 +00:00
|
|
|
for(i=0; known_service_types[i].s; i++)
|
2011-09-28 19:13:20 +00:00
|
|
|
{
|
2012-10-04 22:21:00 +00:00
|
|
|
if(i==0)
|
|
|
|
ver_str[0] = '\0';
|
|
|
|
else
|
|
|
|
snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version);
|
2013-02-07 12:27:09 +00:00
|
|
|
ret += SendSSDPbyebye(sockets[j],
|
2012-04-06 17:53:26 +00:00
|
|
|
#ifdef ENABLE_IPV6
|
2014-05-23 10:07:39 +00:00
|
|
|
sockname, socknamelen,
|
2012-04-06 17:53:26 +00:00
|
|
|
#else
|
2014-05-23 10:07:39 +00:00
|
|
|
(struct sockaddr *)&sockname4, sizeof(struct sockaddr_in),
|
2012-04-06 17:53:26 +00:00
|
|
|
#endif
|
2014-05-23 10:07:39 +00:00
|
|
|
dest_str,
|
2013-02-07 12:27:09 +00:00
|
|
|
known_service_types[i].s, ver_str, /* NT: */
|
2013-06-13 14:03:36 +00:00
|
|
|
known_service_types[i].uuid, "::",
|
2014-05-23 10:07:39 +00:00
|
|
|
known_service_types[i].s); /* ver_str, USN: */
|
2020-06-02 07:08:59 +00:00
|
|
|
if(i > 0 && /* only known_service_types[0].s is shorter than "urn:schemas-upnp-org:device" */
|
|
|
|
0==memcmp(known_service_types[i].s,
|
2013-06-15 12:51:28 +00:00
|
|
|
"urn:schemas-upnp-org:device", sizeof("urn:schemas-upnp-org:device")-1))
|
2011-09-28 19:13:20 +00:00
|
|
|
{
|
2013-02-07 12:27:09 +00:00
|
|
|
ret += SendSSDPbyebye(sockets[j],
|
|
|
|
#ifdef ENABLE_IPV6
|
2014-05-23 10:07:39 +00:00
|
|
|
sockname, socknamelen,
|
2013-02-07 12:27:09 +00:00
|
|
|
#else
|
2014-05-23 10:07:39 +00:00
|
|
|
(struct sockaddr *)&sockname4, sizeof(struct sockaddr_in),
|
2013-02-07 12:27:09 +00:00
|
|
|
#endif
|
2014-05-23 10:07:39 +00:00
|
|
|
dest_str,
|
2013-06-15 12:51:28 +00:00
|
|
|
known_service_types[i].uuid, "", /* NT: */
|
2014-05-23 10:07:39 +00:00
|
|
|
known_service_types[i].uuid, "", ""); /* ver_str, USN: */
|
2011-09-28 19:13:20 +00:00
|
|
|
}
|
2019-05-02 10:09:28 +00:00
|
|
|
}
|
2011-09-28 19:13:20 +00:00
|
|
|
}
|
2012-02-09 20:33:24 +00:00
|
|
|
return ret;
|
2011-09-28 19:13:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* SubmitServicesToMiniSSDPD() :
|
|
|
|
* register services offered by MiniUPnPd to a running instance of
|
|
|
|
* MiniSSDPd */
|
|
|
|
int
|
|
|
|
SubmitServicesToMiniSSDPD(const char * host, unsigned short port) {
|
|
|
|
struct sockaddr_un addr;
|
|
|
|
int s;
|
|
|
|
unsigned char buffer[2048];
|
|
|
|
char strbuf[256];
|
|
|
|
unsigned char * p;
|
2012-08-21 17:33:26 +00:00
|
|
|
int i, l, n;
|
2012-10-04 22:21:00 +00:00
|
|
|
char ver_str[4];
|
2011-09-28 19:13:20 +00:00
|
|
|
|
|
|
|
s = socket(AF_UNIX, SOCK_STREAM, 0);
|
|
|
|
if(s < 0) {
|
|
|
|
syslog(LOG_ERR, "socket(unix): %m");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
addr.sun_family = AF_UNIX;
|
|
|
|
strncpy(addr.sun_path, minissdpdsocketpath, sizeof(addr.sun_path));
|
2015-06-24 05:30:41 +00:00
|
|
|
addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
|
2011-09-28 19:13:20 +00:00
|
|
|
if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) {
|
|
|
|
syslog(LOG_ERR, "connect(\"%s\"): %m", minissdpdsocketpath);
|
2012-08-21 17:33:26 +00:00
|
|
|
close(s);
|
2011-09-28 19:13:20 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2012-09-28 09:13:48 +00:00
|
|
|
for(i = 0; known_service_types[i].s; i++) {
|
2012-08-21 17:33:26 +00:00
|
|
|
buffer[0] = 4; /* request type 4 : submit service */
|
2012-10-04 22:21:00 +00:00
|
|
|
if(i==0)
|
|
|
|
ver_str[0] = '\0';
|
|
|
|
else
|
|
|
|
snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version);
|
2018-02-03 17:14:31 +00:00
|
|
|
/* 4 strings following : ST (service type), USN, Server, Location */
|
|
|
|
p = buffer + 1;
|
|
|
|
l = snprintf(strbuf, sizeof(strbuf), "%s%s",
|
|
|
|
known_service_types[i].s, ver_str);
|
|
|
|
if(l<0) {
|
|
|
|
syslog(LOG_WARNING, "SubmitServicesToMiniSSDPD: snprintf %m");
|
|
|
|
continue;
|
|
|
|
} else if((unsigned)l>=sizeof(strbuf)) {
|
|
|
|
l = sizeof(strbuf) - 1;
|
|
|
|
}
|
|
|
|
CODELENGTH(l, p);
|
|
|
|
memcpy(p, strbuf, l);
|
|
|
|
p += l;
|
2012-03-01 01:44:38 +00:00
|
|
|
l = snprintf(strbuf, sizeof(strbuf), "%s::%s%s",
|
2013-06-13 14:03:36 +00:00
|
|
|
known_service_types[i].uuid, known_service_types[i].s, ver_str);
|
2013-04-26 15:18:28 +00:00
|
|
|
if(l<0) {
|
|
|
|
syslog(LOG_WARNING, "SubmitServicesToMiniSSDPD: snprintf %m");
|
|
|
|
continue;
|
|
|
|
} else if((unsigned)l>=sizeof(strbuf)) {
|
|
|
|
l = sizeof(strbuf) - 1;
|
|
|
|
}
|
2011-09-28 19:13:20 +00:00
|
|
|
CODELENGTH(l, p);
|
|
|
|
memcpy(p, strbuf, l);
|
|
|
|
p += l;
|
2021-05-21 22:16:40 +00:00
|
|
|
#ifdef DYNAMIC_OS_VERSION
|
|
|
|
l = snprintf(strbuf, sizeof(strbuf), MINIUPNPD_SERVER_STRING,
|
|
|
|
os_version);
|
|
|
|
if(l<0) {
|
|
|
|
syslog(LOG_WARNING, "SubmitServicesToMiniSSDPD: snprintf %m");
|
|
|
|
continue;
|
|
|
|
} else if((unsigned)l>=sizeof(strbuf)) {
|
|
|
|
l = sizeof(strbuf) - 1;
|
|
|
|
}
|
|
|
|
CODELENGTH(l, p);
|
|
|
|
memcpy(p, strbuf, l);
|
|
|
|
#else
|
2011-09-28 19:13:20 +00:00
|
|
|
l = (int)strlen(MINIUPNPD_SERVER_STRING);
|
|
|
|
CODELENGTH(l, p);
|
|
|
|
memcpy(p, MINIUPNPD_SERVER_STRING, l);
|
2021-05-21 22:16:40 +00:00
|
|
|
#endif
|
2011-09-28 19:13:20 +00:00
|
|
|
p += l;
|
|
|
|
l = snprintf(strbuf, sizeof(strbuf), "http://%s:%u" ROOTDESC_PATH,
|
|
|
|
host, (unsigned int)port);
|
2013-04-26 15:18:28 +00:00
|
|
|
if(l<0) {
|
|
|
|
syslog(LOG_WARNING, "SubmitServicesToMiniSSDPD: snprintf %m");
|
|
|
|
continue;
|
|
|
|
} else if((unsigned)l>=sizeof(strbuf)) {
|
|
|
|
l = sizeof(strbuf) - 1;
|
|
|
|
}
|
2011-09-28 19:13:20 +00:00
|
|
|
CODELENGTH(l, p);
|
|
|
|
memcpy(p, strbuf, l);
|
|
|
|
p += l;
|
2012-08-21 17:33:26 +00:00
|
|
|
/* now write the encoded data */
|
|
|
|
n = p - buffer; /* bytes to send */
|
|
|
|
p = buffer; /* start */
|
|
|
|
while(n > 0) {
|
|
|
|
l = write(s, p, n);
|
|
|
|
if (l < 0) {
|
2014-02-26 01:02:34 +00:00
|
|
|
if(errno == EINTR)
|
|
|
|
continue;
|
2012-08-21 17:33:26 +00:00
|
|
|
syslog(LOG_ERR, "write(): %m");
|
|
|
|
close(s);
|
|
|
|
return -1;
|
|
|
|
} else if (l == 0) {
|
|
|
|
syslog(LOG_ERR, "write() returned 0");
|
|
|
|
close(s);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
p += l;
|
|
|
|
n -= l;
|
2011-09-28 19:13:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
close(s);
|
2018-02-03 17:14:31 +00:00
|
|
|
syslog(LOG_DEBUG, "%d service submitted to MiniSSDPd", i);
|
2011-09-28 19:13:20 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|