version 1.1.1.1, 2012/02/21 23:16:02
|
version 1.1.1.3, 2013/07/22 00:32:35
|
Line 1
|
Line 1
|
/* $Id$ */ |
/* $Id$ */ |
/* MiniUPnP project |
/* MiniUPnP project |
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ |
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ |
* (c) 2006-2010 Thomas Bernard | * (c) 2006-2013 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 */ |
|
|
#include <stdio.h> |
#include <stdio.h> |
|
#include <stdlib.h> |
#include <string.h> |
#include <string.h> |
#include <unistd.h> |
#include <unistd.h> |
#include <sys/socket.h> |
#include <sys/socket.h> |
#include <sys/un.h> |
#include <sys/un.h> |
#include <netinet/in.h> |
#include <netinet/in.h> |
#include <arpa/inet.h> |
#include <arpa/inet.h> |
|
#include <errno.h> |
#include <syslog.h> |
#include <syslog.h> |
|
|
#include "config.h" |
#include "config.h" |
#include "upnpdescstrings.h" |
#include "upnpdescstrings.h" |
#include "miniupnpdpath.h" |
#include "miniupnpdpath.h" |
#include "upnphttp.h" |
#include "upnphttp.h" |
#include "upnpglobalvars.h" |
#include "upnpglobalvars.h" |
#include "minissdp.h" |
#include "minissdp.h" |
|
#include "upnputils.h" |
|
#include "getroute.h" |
#include "codelength.h" |
#include "codelength.h" |
|
|
/* SSDP ip/port */ |
/* SSDP ip/port */ |
#define SSDP_PORT (1900) |
#define SSDP_PORT (1900) |
/* Prototypes */ |
|
void ProcessSSDPData(int s, char *bufr, struct sockaddr_in sendername, int n, unsigned short port) ; |
|
|
|
|
|
#define SSDP_MCAST_ADDR ("239.255.255.250") |
#define SSDP_MCAST_ADDR ("239.255.255.250") |
|
#define LL_SSDP_MCAST_ADDR "FF02::C" |
|
#define SL_SSDP_MCAST_ADDR "FF05::C" |
|
|
|
/* AddMulticastMembership() |
|
* param s socket |
|
* param ifaddr ip v4 address |
|
*/ |
static int |
static int |
AddMulticastMembership(int s, in_addr_t ifaddr) |
AddMulticastMembership(int s, in_addr_t ifaddr) |
{ |
{ |
Line 38 AddMulticastMembership(int s, in_addr_t ifaddr)
|
Line 45 AddMulticastMembership(int s, in_addr_t ifaddr)
|
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 = htonl(INADDR_ANY);*/ |
/*imr.imr_interface.s_addr = htonl(INADDR_ANY);*/ |
imr.imr_interface.s_addr = ifaddr; /*inet_addr(ifaddr);*/ |
imr.imr_interface.s_addr = ifaddr; /*inet_addr(ifaddr);*/ |
| |
if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&imr, sizeof(struct ip_mreq)) < 0) |
if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&imr, sizeof(struct ip_mreq)) < 0) |
{ |
{ |
syslog(LOG_ERR, "setsockopt(udp, IP_ADD_MEMBERSHIP): %m"); |
syslog(LOG_ERR, "setsockopt(udp, IP_ADD_MEMBERSHIP): %m"); |
Line 48 AddMulticastMembership(int s, in_addr_t ifaddr)
|
Line 55 AddMulticastMembership(int s, in_addr_t ifaddr)
|
return 0; |
return 0; |
} |
} |
|
|
/* Open and configure the socket listening for | /* AddMulticastMembershipIPv6() |
* SSDP udp packets sent on 239.255.255.250 port 1900 */ | * param s socket (IPv6) |
| * To be improved to target specific network interfaces */ |
| #ifdef ENABLE_IPV6 |
| static int |
| AddMulticastMembershipIPv6(int s) |
| { |
| struct ipv6_mreq mr; |
| /*unsigned int ifindex;*/ |
| |
| memset(&mr, 0, sizeof(mr)); |
| inet_pton(AF_INET6, LL_SSDP_MCAST_ADDR, &mr.ipv6mr_multiaddr); |
| /*mr.ipv6mr_interface = ifindex;*/ |
| mr.ipv6mr_interface = 0; /* 0 : all interfaces */ |
| #ifndef IPV6_ADD_MEMBERSHIP |
| #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP |
| #endif |
| 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; |
| } |
| return 0; |
| } |
| #endif |
| |
| /* Open and configure the socket listening for |
| * 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 |
int |
OpenAndConfSSDPReceiveSocket() | OpenAndConfSSDPReceiveSocket(int ipv6) |
{ |
{ |
int s; |
int s; |
int i; | struct sockaddr_storage sockname; |
| socklen_t sockname_len; |
| struct lan_addr_s * lan_addr; |
int j = 1; |
int j = 1; |
struct sockaddr_in sockname; | |
| if( (s = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, 0)) < 0) |
if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) | |
{ |
{ |
syslog(LOG_ERR, "socket(udp): %m"); | syslog(LOG_ERR, "%s: socket(udp): %m", |
| "OpenAndConfSSDPReceiveSocket"); |
return -1; |
return -1; |
} | } |
| |
memset(&sockname, 0, sizeof(struct sockaddr_in)); | |
sockname.sin_family = AF_INET; | |
sockname.sin_port = htons(SSDP_PORT); | |
/* NOTE : it seems it doesnt work when binding on the specific address */ | |
/*sockname.sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);*/ | |
sockname.sin_addr.s_addr = htonl(INADDR_ANY); | |
/*sockname.sin_addr.s_addr = inet_addr(ifaddr);*/ | |
|
|
|
memset(&sockname, 0, sizeof(struct sockaddr_storage)); |
|
if(ipv6) { |
|
struct sockaddr_in6 * saddr = (struct sockaddr_in6 *)&sockname; |
|
saddr->sin6_family = AF_INET6; |
|
saddr->sin6_port = htons(SSDP_PORT); |
|
saddr->sin6_addr = in6addr_any; |
|
sockname_len = sizeof(struct sockaddr_in6); |
|
} else { |
|
struct sockaddr_in * saddr = (struct sockaddr_in *)&sockname; |
|
saddr->sin_family = AF_INET; |
|
saddr->sin_port = htons(SSDP_PORT); |
|
/* NOTE : it seems it doesnt work when binding on the specific address */ |
|
/*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); |
|
} |
|
|
if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &j, sizeof(j)) < 0) |
if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &j, sizeof(j)) < 0) |
{ |
{ |
syslog(LOG_WARNING, "setsockopt(udp, SO_REUSEADDR): %m"); |
syslog(LOG_WARNING, "setsockopt(udp, SO_REUSEADDR): %m"); |
} |
} |
|
|
|
if(!set_non_blocking(s)) |
|
{ |
|
syslog(LOG_WARNING, "%s: set_non_blocking(): %m", |
|
"OpenAndConfSSDPReceiveSocket"); |
|
} |
|
|
if(bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0) | if(bind(s, (struct sockaddr *)&sockname, sockname_len) < 0) |
{ |
{ |
syslog(LOG_ERR, "bind(udp): %m"); | syslog(LOG_ERR, "%s: bind(udp%s): %m", |
| "OpenAndConfSSDPReceiveSocket", ipv6 ? "6" : ""); |
close(s); |
close(s); |
return -1; |
return -1; |
} | } |
|
|
i = n_lan_addr; | #ifdef ENABLE_IPV6 |
while(i>0) | if(ipv6) |
{ |
{ |
i--; | AddMulticastMembershipIPv6(s); |
if(AddMulticastMembership(s, lan_addr[i].addr.s_addr) < 0) | } |
| else |
| #endif |
| { |
| for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next) |
{ |
{ |
syslog(LOG_WARNING, | if(AddMulticastMembership(s, lan_addr->addr.s_addr) < 0) |
"Failed to add multicast membership for address %s", | { |
lan_addr[i].str ); | syslog(LOG_WARNING, |
| "Failed to add multicast membership for interface %s", |
| lan_addr->str ? lan_addr->str : "NULL"); |
| } |
} |
} |
} |
} |
|
|
Line 108 OpenAndConfSSDPNotifySocket(in_addr_t addr)
|
Line 173 OpenAndConfSSDPNotifySocket(in_addr_t addr)
|
int s; |
int s; |
unsigned char loopchar = 0; |
unsigned char loopchar = 0; |
int bcast = 1; |
int bcast = 1; |
|
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 */ |
struct in_addr mc_if; |
struct in_addr mc_if; |
struct sockaddr_in sockname; |
struct sockaddr_in sockname; |
| |
if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) |
if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) |
{ |
{ |
syslog(LOG_ERR, "socket(udp_notify): %m"); |
syslog(LOG_ERR, "socket(udp_notify): %m"); |
Line 132 OpenAndConfSSDPNotifySocket(in_addr_t addr)
|
Line 201 OpenAndConfSSDPNotifySocket(in_addr_t addr)
|
close(s); |
close(s); |
return -1; |
return -1; |
} |
} |
| |
| if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) |
| { |
| syslog(LOG_WARNING, "setsockopt(udp_notify, IP_MULTICAST_TTL,): %m"); |
| } |
| |
if(setsockopt(s, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bcast)) < 0) |
if(setsockopt(s, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bcast)) < 0) |
{ |
{ |
syslog(LOG_ERR, "setsockopt(udp_notify, SO_BROADCAST): %m"); |
syslog(LOG_ERR, "setsockopt(udp_notify, SO_BROADCAST): %m"); |
Line 154 OpenAndConfSSDPNotifySocket(in_addr_t addr)
|
Line 228 OpenAndConfSSDPNotifySocket(in_addr_t addr)
|
return s; |
return s; |
} |
} |
|
|
|
#ifdef ENABLE_IPV6 |
|
/* open the UDP socket used to send SSDP notifications to |
|
* the multicast group reserved for them. IPv6 */ |
|
static int |
|
OpenAndConfSSDPNotifySocketIPv6(unsigned int if_index) |
|
{ |
|
int s; |
|
unsigned int loop = 0; |
|
|
|
s = socket(PF_INET6, SOCK_DGRAM, 0); |
|
if(s < 0) |
|
{ |
|
syslog(LOG_ERR, "socket(udp_notify IPv6): %m"); |
|
return -1; |
|
} |
|
if(setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &if_index, sizeof(if_index)) < 0) |
|
{ |
|
syslog(LOG_ERR, "setsockopt(udp_notify IPv6, IPV6_MULTICAST_IF, %u): %m", if_index); |
|
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; |
|
} |
|
return s; |
|
} |
|
#endif |
|
|
int |
int |
OpenAndConfSSDPNotifySockets(int * sockets) |
OpenAndConfSSDPNotifySockets(int * sockets) |
/*OpenAndConfSSDPNotifySockets(int * sockets, |
/*OpenAndConfSSDPNotifySockets(int * sockets, |
struct lan_addr_s * lan_addr, int n_lan_addr)*/ |
struct lan_addr_s * lan_addr, int n_lan_addr)*/ |
{ |
{ |
int i, j; | int i; |
for(i=0; i<n_lan_addr; i++) | struct lan_addr_s * lan_addr; |
| |
| for(i=0, lan_addr = lan_addrs.lh_first; |
| lan_addr != NULL; |
| lan_addr = lan_addr->list.le_next) |
{ |
{ |
sockets[i] = OpenAndConfSSDPNotifySocket(lan_addr[i].addr.s_addr); | sockets[i] = OpenAndConfSSDPNotifySocket(lan_addr->addr.s_addr); |
if(sockets[i] < 0) |
if(sockets[i] < 0) |
{ | goto error; |
for(j=0; j<i; j++) | i++; |
{ | #ifdef ENABLE_IPV6 |
close(sockets[j]); | sockets[i] = OpenAndConfSSDPNotifySocketIPv6(lan_addr->index); |
sockets[j] = -1; | if(sockets[i] < 0) |
} | goto error; |
return -1; | i++; |
} | #endif |
} |
} |
return 0; |
return 0; |
|
error: |
|
while(--i >= 0) |
|
{ |
|
close(sockets[i]); |
|
sockets[i] = -1; |
|
} |
|
return -1; |
} |
} |
|
|
/* |
/* |
Line 200 EXT:
|
Line 316 EXT:
|
/* not really an SSDP "announce" as it is the response |
/* not really an SSDP "announce" as it is the response |
* to a SSDP "M-SEARCH" */ |
* to a SSDP "M-SEARCH" */ |
static void |
static void |
SendSSDPAnnounce2(int s, struct sockaddr_in sockname, | SendSSDPAnnounce2(int s, const struct sockaddr * addr, |
const char * st, int st_len, const char * suffix, |
const char * st, int st_len, const char * suffix, |
const char * host, unsigned short port) |
const char * host, unsigned short port) |
{ |
{ |
int l, n; |
int l, n; |
char buf[512]; |
char buf[512]; |
/* | char addr_str[64]; |
| socklen_t addrlen; |
| /* |
* follow guideline from document "UPnP Device Architecture 1.0" |
* follow guideline from document "UPnP Device Architecture 1.0" |
* uppercase is recommended. |
* uppercase is recommended. |
* DATE: is recommended |
* DATE: is recommended |
* SERVER: OS/ver UPnP/1.0 miniupnpd/1.0 |
* SERVER: OS/ver UPnP/1.0 miniupnpd/1.0 |
* - check what to put in the 'Cache-Control' header | * - check what to put in the 'Cache-Control' header |
* */ | * |
| * have a look at the document "UPnP Device Architecture v1.1 */ |
l = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\n" |
l = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\n" |
"CACHE-CONTROL: max-age=120\r\n" |
"CACHE-CONTROL: max-age=120\r\n" |
/*"DATE: ...\r\n"*/ |
/*"DATE: ...\r\n"*/ |
Line 221 SendSSDPAnnounce2(int s, struct sockaddr_in sockname,
|
Line 340 SendSSDPAnnounce2(int s, struct sockaddr_in sockname,
|
"EXT:\r\n" |
"EXT:\r\n" |
"SERVER: " MINIUPNPD_SERVER_STRING "\r\n" |
"SERVER: " MINIUPNPD_SERVER_STRING "\r\n" |
"LOCATION: http://%s:%u" ROOTDESC_PATH "\r\n" |
"LOCATION: http://%s:%u" ROOTDESC_PATH "\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. UDA v1.1 */ |
|
"BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */ |
|
"CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */ |
"\r\n", |
"\r\n", |
st_len, st, suffix, |
st_len, st, suffix, |
uuidvalue, st_len, st, suffix, |
uuidvalue, st_len, st, suffix, |
host, (unsigned int)port); | host, (unsigned int)port, |
| upnp_bootid, upnp_bootid, upnp_configid); |
| addrlen = (addr->sa_family == AF_INET6) |
| ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); |
n = sendto(s, buf, l, 0, |
n = sendto(s, buf, l, 0, |
(struct sockaddr *)&sockname, sizeof(struct sockaddr_in) ); | addr, addrlen); |
syslog(LOG_INFO, "SSDP Announce %d bytes to %s:%d ST: %.*s",n, | sockaddr_to_string(addr, addr_str, sizeof(addr_str)); |
inet_ntoa(sockname.sin_addr), | syslog(LOG_INFO, "SSDP Announce %d bytes to %s ST: %.*s",n, |
ntohs(sockname.sin_port), | addr_str, |
l, buf); |
l, buf); |
if(n < 0) |
if(n < 0) |
{ |
{ |
|
/* XXX handle EINTR, EAGAIN, EWOULDBLOCK */ |
syslog(LOG_ERR, "sendto(udp): %m"); |
syslog(LOG_ERR, "sendto(udp): %m"); |
} |
} |
} |
} |
|
|
static const char * const known_service_types[] = | #ifndef IGD_V2 |
| #define IGD_VER 1 |
| #define WANIPC_VER 1 |
| #else |
| #define IGD_VER 2 |
| #define WANIPC_VER 2 |
| #endif |
| |
| static struct { |
| const char * s; |
| const int version; |
| } const known_service_types[] = |
{ |
{ |
"upnp:rootdevice", | {"upnp:rootdevice", 0}, |
"urn:schemas-upnp-org:device:InternetGatewayDevice:", | {"urn:schemas-upnp-org:device:InternetGatewayDevice:", IGD_VER}, |
"urn:schemas-upnp-org:device:WANConnectionDevice:", | {"urn:schemas-upnp-org:device:WANConnectionDevice:", 1}, |
"urn:schemas-upnp-org:device:WANDevice:", | {"urn:schemas-upnp-org:device:WANDevice:", 1}, |
"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:", | {"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:", 1}, |
"urn:schemas-upnp-org:service:WANIPConnection:", | {"urn:schemas-upnp-org:service:WANIPConnection:", WANIPC_VER}, |
"urn:schemas-upnp-org:service:WANPPPConnection:", | {"urn:schemas-upnp-org:service:WANPPPConnection:", 1}, |
"urn:schemas-upnp-org:service:Layer3Forwarding:", | #ifdef ENABLE_L3F_SERVICE |
0 | {"urn:schemas-upnp-org:service:Layer3Forwarding:", 1}, |
| #endif |
| #ifdef ENABLE_6FC_SERVICE |
| {"url:schemas-upnp-org:service:WANIPv6FirewallControl:", 1}, |
| #endif |
| {0, 0} |
}; |
}; |
|
|
static void |
static void |
SendSSDPNotifies(int s, const char * host, unsigned short port, |
SendSSDPNotifies(int s, const char * host, unsigned short port, |
unsigned int lifetime) | unsigned int lifetime, int ipv6) |
{ |
{ |
|
#ifdef ENABLE_IPV6 |
|
struct sockaddr_storage sockname; |
|
#else |
struct sockaddr_in sockname; |
struct sockaddr_in sockname; |
|
#endif |
int l, n, i=0; |
int l, n, i=0; |
char bufr[512]; |
char bufr[512]; |
|
char ver_str[4]; |
|
|
memset(&sockname, 0, sizeof(struct sockaddr_in)); | memset(&sockname, 0, sizeof(sockname)); |
sockname.sin_family = AF_INET; | #ifdef ENABLE_IPV6 |
sockname.sin_port = htons(SSDP_PORT); | if(ipv6) |
sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR); | { |
| struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockname; |
| p->sin6_family = AF_INET6; |
| p->sin6_port = htons(SSDP_PORT); |
| inet_pton(AF_INET6, LL_SSDP_MCAST_ADDR, &(p->sin6_addr)); |
| } |
| else |
| #endif |
| { |
| struct sockaddr_in *p = (struct sockaddr_in *)&sockname; |
| p->sin_family = AF_INET; |
| p->sin_port = htons(SSDP_PORT); |
| p->sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR); |
| } |
|
|
while(known_service_types[i]) | while(known_service_types[i].s) |
{ |
{ |
l = snprintf(bufr, sizeof(bufr), | if(i==0) |
"NOTIFY * HTTP/1.1\r\n" | ver_str[0] = '\0'; |
"HOST:%s:%d\r\n" | else |
"Cache-Control:max-age=%u\r\n" | snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version); |
"Location:http://%s:%d" ROOTDESC_PATH"\r\n" | l = snprintf(bufr, sizeof(bufr), |
/*"Server:miniupnpd/1.0 UPnP/1.0\r\n"*/ | "NOTIFY * HTTP/1.1\r\n" |
"Server: " MINIUPNPD_SERVER_STRING "\r\n" | "HOST: %s:%d\r\n" |
"NT:%s%s\r\n" | "CACHE-CONTROL: max-age=%u\r\n" |
"USN:%s::%s%s\r\n" | "lOCATION: http://%s:%d" ROOTDESC_PATH"\r\n" |
"NTS:ssdp:alive\r\n" | "SERVER: " MINIUPNPD_SERVER_STRING "\r\n" |
"\r\n", | "NT: %s%s\r\n" |
SSDP_MCAST_ADDR, SSDP_PORT, | "USN: %s::%s%s\r\n" |
lifetime, | "NTS: ssdp:alive\r\n" |
host, port, | "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */ |
known_service_types[i], (i==0?"":"1"), | "01-NLS: %u\r\n" /* same as BOOTID field. UDA v1.1 */ |
uuidvalue, known_service_types[i], (i==0?"":"1") ); | "BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */ |
if(l>=sizeof(bufr)) | "CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */ |
| "\r\n", |
| ipv6 ? "[" LL_SSDP_MCAST_ADDR "]" : SSDP_MCAST_ADDR, |
| SSDP_PORT, |
| lifetime, |
| host, port, |
| known_service_types[i].s, ver_str, |
| uuidvalue, known_service_types[i].s, ver_str, |
| upnp_bootid, upnp_bootid, upnp_configid ); |
| if(l<0) |
{ |
{ |
|
syslog(LOG_ERR, "SendSSDPNotifies() snprintf error"); |
|
continue; |
|
} |
|
if((unsigned int)l >= sizeof(bufr)) |
|
{ |
syslog(LOG_WARNING, "SendSSDPNotifies(): truncated output"); |
syslog(LOG_WARNING, "SendSSDPNotifies(): truncated output"); |
l = sizeof(bufr); |
l = sizeof(bufr); |
} |
} |
n = sendto(s, bufr, l, 0, |
n = sendto(s, bufr, l, 0, |
(struct sockaddr *)&sockname, sizeof(struct sockaddr_in) ); | (struct sockaddr *)&sockname, |
| #ifdef ENABLE_IPV6 |
| ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in) |
| #else |
| sizeof(struct sockaddr_in) |
| #endif |
| ); |
if(n < 0) |
if(n < 0) |
{ |
{ |
syslog(LOG_ERR, "sendto(udp_notify=%d, %s): %m", s, host); | /* XXX handle EINTR, EAGAIN, EWOULDBLOCK */ |
| syslog(LOG_ERR, "sendto(udp_notify=%d, %s): %m", s, |
| host ? host : "NULL"); |
} |
} |
i++; |
i++; |
} |
} |
Line 300 void
|
Line 483 void
|
SendSSDPNotifies2(int * sockets, |
SendSSDPNotifies2(int * sockets, |
unsigned short port, |
unsigned short port, |
unsigned int lifetime) |
unsigned int lifetime) |
/*SendSSDPNotifies2(int * sockets, struct lan_addr_s * lan_addr, int n_lan_addr, |
|
unsigned short port, |
|
unsigned int lifetime)*/ |
|
{ |
{ |
int i; |
int i; |
for(i=0; i<n_lan_addr; i++) | struct lan_addr_s * lan_addr; |
| for(i=0, lan_addr = lan_addrs.lh_first; |
| lan_addr != NULL; |
| lan_addr = lan_addr->list.le_next) |
{ |
{ |
SendSSDPNotifies(sockets[i], lan_addr[i].str, port, lifetime); | SendSSDPNotifies(sockets[i], lan_addr->str, port, |
| lifetime, 0); |
| i++; |
| #ifdef ENABLE_IPV6 |
| SendSSDPNotifies(sockets[i], ipv6_addr_for_http_with_brackets, port, |
| lifetime, 1); |
| i++; |
| #endif |
} |
} |
} |
} |
|
|
Line 319 ProcessSSDPRequest(int s, unsigned short port)
|
Line 509 ProcessSSDPRequest(int s, unsigned short port)
|
int n; |
int n; |
char bufr[1500]; |
char bufr[1500]; |
socklen_t len_r; |
socklen_t len_r; |
|
#ifdef ENABLE_IPV6 |
|
struct sockaddr_storage sendername; |
|
len_r = sizeof(struct sockaddr_storage); |
|
#else |
struct sockaddr_in sendername; |
struct sockaddr_in sendername; |
len_r = sizeof(struct sockaddr_in); |
len_r = sizeof(struct sockaddr_in); |
|
#endif |
|
|
n = recvfrom(s, bufr, sizeof(bufr), 0, |
n = recvfrom(s, bufr, sizeof(bufr), 0, |
(struct sockaddr *)&sendername, &len_r); |
(struct sockaddr *)&sendername, &len_r); |
if(n < 0) |
if(n < 0) |
{ |
{ |
syslog(LOG_ERR, "recvfrom(udp): %m"); | /* EAGAIN, EWOULDBLOCK, EINTR : silently ignore (try again next time) |
| * other errors : log to LOG_ERR */ |
| if(errno != EAGAIN && |
| errno != EWOULDBLOCK && |
| errno != EINTR) |
| { |
| syslog(LOG_ERR, "recvfrom(udp): %m"); |
| } |
return; |
return; |
} |
} |
ProcessSSDPData(s, bufr, sendername, n, port); | ProcessSSDPData(s, bufr, n, (struct sockaddr *)&sendername, port); |
|
|
} |
} |
|
|
void ProcessSSDPData(int s, char *bufr, struct sockaddr_in sendername, int n, unsigned short port) { | void |
| ProcessSSDPData(int s, const char *bufr, int n, |
| const struct sockaddr * sender, unsigned short port) { |
int i, l; |
int i, l; |
int lan_addr_index = 0; | struct lan_addr_s * lan_addr = NULL; |
char * st = 0; | const char * st = NULL; |
int st_len = 0; |
int st_len = 0; |
|
int st_ver = 0; |
|
char sender_str[64]; |
|
char ver_str[4]; |
|
const char * announced_host = NULL; |
|
#ifdef ENABLE_IPV6 |
|
#ifdef UPNP_STRICT |
|
char announced_host_buf[64]; |
|
#endif |
|
#endif |
|
|
|
/* get the string representation of the sender address */ |
|
sockaddr_to_string(sender, sender_str, sizeof(sender_str)); |
|
lan_addr = get_lan_for_peer(sender); |
|
if(lan_addr == NULL) |
|
{ |
|
syslog(LOG_WARNING, "SSDP packet sender %s not from a LAN, ignoring", |
|
sender_str); |
|
return; |
|
} |
|
|
if(memcmp(bufr, "NOTIFY", 6) == 0) |
if(memcmp(bufr, "NOTIFY", 6) == 0) |
{ |
{ |
Line 362 void ProcessSSDPData(int s, char *bufr, struct sockadd
|
Line 584 void ProcessSSDPData(int s, char *bufr, struct sockadd
|
while(st[st_len]!='\r' && st[st_len]!='\n' |
while(st[st_len]!='\r' && st[st_len]!='\n' |
&& (st + st_len < bufr + n)) |
&& (st + st_len < bufr + n)) |
st_len++; |
st_len++; |
/*syslog(LOG_INFO, "ST: %.*s", st_len, st);*/ | l = st_len; |
| while(l > 0 && st[l-1] != ':') |
| l--; |
| st_ver = atoi(st+l); |
| syslog(LOG_DEBUG, "ST: %.*s (ver=%d)", st_len, st, st_ver); |
/*j = 0;*/ |
/*j = 0;*/ |
/*while(bufr[i+j]!='\r') j++;*/ |
/*while(bufr[i+j]!='\r') j++;*/ |
/*syslog(LOG_INFO, "%.*s", j, bufr+i);*/ |
/*syslog(LOG_INFO, "%.*s", j, bufr+i);*/ |
} |
} |
} |
} |
/*syslog(LOG_INFO, "SSDP M-SEARCH packet received from %s:%d", | /*syslog(LOG_INFO, "SSDP M-SEARCH packet received from %s", |
inet_ntoa(sendername.sin_addr), | sender_str );*/ |
ntohs(sendername.sin_port) );*/ | |
if(st && (st_len > 0)) |
if(st && (st_len > 0)) |
{ |
{ |
/* TODO : doesnt answer at once but wait for a random time */ |
/* TODO : doesnt answer at once but wait for a random time */ |
syslog(LOG_INFO, "SSDP M-SEARCH from %s:%d ST: %.*s", | syslog(LOG_INFO, "SSDP M-SEARCH from %s ST: %.*s", |
inet_ntoa(sendername.sin_addr), | sender_str, st_len, st); |
ntohs(sendername.sin_port), | |
st_len, st); | |
/* find in which sub network the client is */ |
/* find in which sub network the client is */ |
for(i = 0; i<n_lan_addr; i++) | if(sender->sa_family == AF_INET) |
{ |
{ |
if( (sendername.sin_addr.s_addr & lan_addr[i].mask.s_addr) | if (lan_addr == NULL) |
== (lan_addr[i].addr.s_addr & lan_addr[i].mask.s_addr)) | |
{ |
{ |
lan_addr_index = i; | syslog(LOG_ERR, "Can't find in which sub network the client is"); |
break; | return; |
} |
} |
|
announced_host = lan_addr->str; |
} |
} |
|
#ifdef ENABLE_IPV6 |
|
else |
|
{ |
|
/* IPv6 address with brackets */ |
|
#ifdef UPNP_STRICT |
|
int index; |
|
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)); |
|
if(get_src_for_route_to (sender, &addr6, &addr6_len, &index) < 0) { |
|
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; |
|
} else { |
|
if(inet_ntop(AF_INET6, &addr6, |
|
announced_host_buf+1, |
|
sizeof(announced_host_buf) - 2)) { |
|
announced_host_buf[0] = '['; |
|
i = strlen(announced_host_buf); |
|
if(i < (int)sizeof(announced_host_buf) - 1) { |
|
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 |
|
announced_host = ipv6_addr_for_http_with_brackets; |
|
#endif |
|
} |
|
#endif |
/* Responds to request with a device as ST header */ |
/* Responds to request with a device as ST header */ |
for(i = 0; known_service_types[i]; i++) | for(i = 0; known_service_types[i].s; i++) |
{ |
{ |
l = (int)strlen(known_service_types[i]); | l = (int)strlen(known_service_types[i].s); |
if(l<=st_len && (0 == memcmp(st, known_service_types[i], l))) | 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 |
| ) |
{ |
{ |
syslog(LOG_INFO, "Single search found"); |
syslog(LOG_INFO, "Single search found"); |
SendSSDPAnnounce2(s, sendername, | SendSSDPAnnounce2(s, sender, |
st, st_len, "", |
st, st_len, "", |
lan_addr[lan_addr_index].str, port); | announced_host, port); |
break; |
break; |
} |
} |
} |
} |
Line 406 void ProcessSSDPData(int s, char *bufr, struct sockadd
|
Line 672 void ProcessSSDPData(int s, char *bufr, struct sockadd
|
if(st_len==8 && (0 == memcmp(st, "ssdp:all", 8))) |
if(st_len==8 && (0 == memcmp(st, "ssdp:all", 8))) |
{ |
{ |
syslog(LOG_INFO, "ssdp:all found"); |
syslog(LOG_INFO, "ssdp:all found"); |
for(i=0; known_service_types[i]; i++) | for(i=0; known_service_types[i].s; i++) |
{ |
{ |
l = (int)strlen(known_service_types[i]); | if(i==0) |
SendSSDPAnnounce2(s, sendername, | ver_str[0] = '\0'; |
known_service_types[i], l, i==0?"":"1", | else |
lan_addr[lan_addr_index].str, port); | snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version); |
| l = (int)strlen(known_service_types[i].s); |
| SendSSDPAnnounce2(s, sender, |
| known_service_types[i].s, l, ver_str, |
| announced_host, port); |
} |
} |
|
/* also answer for uuid */ |
|
SendSSDPAnnounce2(s, sender, uuidvalue, strlen(uuidvalue), "", |
|
announced_host, port); |
} |
} |
/* responds to request by UUID value */ |
/* responds to request by UUID value */ |
l = (int)strlen(uuidvalue); |
l = (int)strlen(uuidvalue); |
if(l==st_len && (0 == memcmp(st, uuidvalue, l))) |
if(l==st_len && (0 == memcmp(st, uuidvalue, l))) |
{ |
{ |
syslog(LOG_INFO, "ssdp:uuid found"); |
syslog(LOG_INFO, "ssdp:uuid found"); |
SendSSDPAnnounce2(s, sendername, st, st_len, "", | SendSSDPAnnounce2(s, sender, st, st_len, "", |
lan_addr[lan_addr_index].str, port); | announced_host, port); |
} |
} |
} |
} |
else |
else |
{ |
{ |
syslog(LOG_INFO, "Invalid SSDP M-SEARCH from %s:%d", | syslog(LOG_INFO, "Invalid SSDP M-SEARCH from %s", sender_str); |
inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port)); | |
} |
} |
} |
} |
else |
else |
{ |
{ |
syslog(LOG_NOTICE, "Unknown udp packet received from %s:%d", | syslog(LOG_NOTICE, "Unknown udp packet received from %s", sender_str); |
inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port)); | |
} |
} |
} |
} |
|
|
/* This will broadcast ssdp:byebye notifications to inform | /* This will broadcast ssdp:byebye notifications to inform |
* the network that UPnP is going down. */ |
* the network that UPnP is going down. */ |
int |
int |
SendSSDPGoodbye(int * sockets, int n_sockets) |
SendSSDPGoodbye(int * sockets, int n_sockets) |
{ |
{ |
struct sockaddr_in sockname; |
struct sockaddr_in sockname; |
|
#ifdef ENABLE_IPV6 |
|
struct sockaddr_in6 sockname6; |
|
#endif |
int n, l; |
int n, l; |
int i, j; |
int i, j; |
char bufr[512]; |
char bufr[512]; |
|
char ver_str[4]; |
|
int ret = 0; |
|
int ipv6 = 0; |
|
|
memset(&sockname, 0, sizeof(struct sockaddr_in)); |
memset(&sockname, 0, sizeof(struct sockaddr_in)); |
sockname.sin_family = AF_INET; |
sockname.sin_family = AF_INET; |
sockname.sin_port = htons(SSDP_PORT); |
sockname.sin_port = htons(SSDP_PORT); |
sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR); |
sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR); |
|
#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)); |
|
#endif |
|
|
for(j=0; j<n_sockets; j++) |
for(j=0; j<n_sockets; j++) |
{ |
{ |
for(i=0; known_service_types[i]; i++) | #ifdef ENABLE_IPV6 |
| ipv6 = j & 1; |
| #endif |
| 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); |
l = snprintf(bufr, sizeof(bufr), |
l = snprintf(bufr, sizeof(bufr), |
"NOTIFY * HTTP/1.1\r\n" | "NOTIFY * HTTP/1.1\r\n" |
"HOST:%s:%d\r\n" | "HOST: %s:%d\r\n" |
"NT:%s%s\r\n" | "NT: %s%s\r\n" |
"USN:%s::%s%s\r\n" | "USN: %s::%s%s\r\n" |
"NTS:ssdp:byebye\r\n" | "NTS: ssdp:byebye\r\n" |
"\r\n", | "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */ |
SSDP_MCAST_ADDR, SSDP_PORT, | "01-NLS: %u\r\n" /* same as BOOTID field. UDA v1.1 */ |
known_service_types[i], (i==0?"":"1"), | "BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */ |
uuidvalue, known_service_types[i], (i==0?"":"1")); | "CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */ |
| "\r\n", |
| ipv6 ? "[" LL_SSDP_MCAST_ADDR "]" : SSDP_MCAST_ADDR, |
| SSDP_PORT, |
| known_service_types[i].s, ver_str, |
| uuidvalue, known_service_types[i].s, ver_str, |
| upnp_bootid, upnp_bootid, upnp_configid); |
n = sendto(sockets[j], bufr, l, 0, |
n = sendto(sockets[j], bufr, l, 0, |
(struct sockaddr *)&sockname, sizeof(struct sockaddr_in) ); | #ifdef ENABLE_IPV6 |
| ipv6 ? (struct sockaddr *)&sockname6 : (struct sockaddr *)&sockname, |
| ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in) |
| #else |
| (struct sockaddr *)&sockname, |
| sizeof(struct sockaddr_in) |
| #endif |
| ); |
if(n < 0) |
if(n < 0) |
{ |
{ |
syslog(LOG_ERR, "SendSSDPGoodbye: sendto(udp_shutdown=%d): %m", |
syslog(LOG_ERR, "SendSSDPGoodbye: sendto(udp_shutdown=%d): %m", |
sockets[j]); |
sockets[j]); |
return -1; | ret = -1; |
} |
} |
} |
} |
} |
} |
return 0; | return ret; |
} |
} |
|
|
/* SubmitServicesToMiniSSDPD() : |
/* SubmitServicesToMiniSSDPD() : |
Line 488 SubmitServicesToMiniSSDPD(const char * host, unsigned
|
Line 791 SubmitServicesToMiniSSDPD(const char * host, unsigned
|
unsigned char buffer[2048]; |
unsigned char buffer[2048]; |
char strbuf[256]; |
char strbuf[256]; |
unsigned char * p; |
unsigned char * p; |
int i, l; | int i, l, n; |
| char ver_str[4]; |
|
|
s = socket(AF_UNIX, SOCK_STREAM, 0); |
s = socket(AF_UNIX, SOCK_STREAM, 0); |
if(s < 0) { |
if(s < 0) { |
Line 499 SubmitServicesToMiniSSDPD(const char * host, unsigned
|
Line 803 SubmitServicesToMiniSSDPD(const char * host, unsigned
|
strncpy(addr.sun_path, minissdpdsocketpath, sizeof(addr.sun_path)); |
strncpy(addr.sun_path, minissdpdsocketpath, sizeof(addr.sun_path)); |
if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) { |
if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) { |
syslog(LOG_ERR, "connect(\"%s\"): %m", minissdpdsocketpath); |
syslog(LOG_ERR, "connect(\"%s\"): %m", minissdpdsocketpath); |
|
close(s); |
return -1; |
return -1; |
} |
} |
for(i = 0; known_service_types[i]; i++) { | for(i = 0; known_service_types[i].s; i++) { |
buffer[0] = 4; | buffer[0] = 4; /* request type 4 : submit service */ |
| /* 4 strings following : ST (service type), USN, Server, Location */ |
p = buffer + 1; |
p = buffer + 1; |
l = (int)strlen(known_service_types[i]); | l = (int)strlen(known_service_types[i].s); |
if(i > 0) |
if(i > 0) |
l++; |
l++; |
CODELENGTH(l, p); |
CODELENGTH(l, p); |
memcpy(p, known_service_types[i], l); | memcpy(p, known_service_types[i].s, l); |
if(i > 0) |
if(i > 0) |
p[l-1] = '1'; |
p[l-1] = '1'; |
p += l; |
p += l; |
l = snprintf(strbuf, sizeof(strbuf), "%s::%s%s", | if(i==0) |
uuidvalue, known_service_types[i], (i==0)?"":"1"); | ver_str[0] = '\0'; |
| else |
| snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version); |
| l = snprintf(strbuf, sizeof(strbuf), "%s::%s%s", |
| uuidvalue, known_service_types[i].s, ver_str); |
CODELENGTH(l, p); |
CODELENGTH(l, p); |
memcpy(p, strbuf, l); |
memcpy(p, strbuf, l); |
p += l; |
p += l; |
Line 526 SubmitServicesToMiniSSDPD(const char * host, unsigned
|
Line 836 SubmitServicesToMiniSSDPD(const char * host, unsigned
|
CODELENGTH(l, p); |
CODELENGTH(l, p); |
memcpy(p, strbuf, l); |
memcpy(p, strbuf, l); |
p += l; |
p += l; |
if(write(s, buffer, p - buffer) < 0) { | /* now write the encoded data */ |
syslog(LOG_ERR, "write(): %m"); | n = p - buffer; /* bytes to send */ |
return -1; | p = buffer; /* start */ |
| while(n > 0) { |
| l = write(s, p, n); |
| if (l < 0) { |
| 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; |
} |
} |
} |
} |
close(s); |
close(s); |