--- embedaddon/miniupnpd/minissdp.c 2012/05/29 12:55:57 1.1.1.2 +++ embedaddon/miniupnpd/minissdp.c 2013/07/22 00:32:35 1.1.1.3 @@ -1,18 +1,21 @@ -/* $Id: minissdp.c,v 1.1.1.2 2012/05/29 12:55:57 misho Exp $ */ +/* $Id: minissdp.c,v 1.1.1.3 2013/07/22 00:32:35 misho Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * (c) 2006-2011 Thomas Bernard + * (c) 2006-2013 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include +#include #include #include #include #include #include #include +#include #include + #include "config.h" #include "upnpdescstrings.h" #include "miniupnpdpath.h" @@ -20,13 +23,14 @@ #include "upnpglobalvars.h" #include "minissdp.h" #include "upnputils.h" +#include "getroute.h" #include "codelength.h" /* SSDP ip/port */ #define SSDP_PORT (1900) #define SSDP_MCAST_ADDR ("239.255.255.250") -#define LL_SSDP_MCAST_ADDR ("FF02::C") -#define SL_SSDP_MCAST_ADDR ("FF05::C") +#define LL_SSDP_MCAST_ADDR "FF02::C" +#define SL_SSDP_MCAST_ADDR "FF05::C" /* AddMulticastMembership() * param s socket @@ -41,7 +45,7 @@ AddMulticastMembership(int s, in_addr_t ifaddr) imr.imr_multiaddr.s_addr = inet_addr(SSDP_MCAST_ADDR); /*imr.imr_interface.s_addr = htonl(INADDR_ANY);*/ imr.imr_interface.s_addr = ifaddr; /*inet_addr(ifaddr);*/ - + if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&imr, sizeof(struct ip_mreq)) < 0) { syslog(LOG_ERR, "setsockopt(udp, IP_ADD_MEMBERSHIP): %m"); @@ -83,7 +87,7 @@ AddMulticastMembershipIPv6(int s) } #endif -/* Open and configure the socket listening for +/* 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 @@ -97,7 +101,8 @@ OpenAndConfSSDPReceiveSocket(int ipv6) if( (s = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, 0)) < 0) { - syslog(LOG_ERR, "socket(udp): %m"); + syslog(LOG_ERR, "%s: socket(udp): %m", + "OpenAndConfSSDPReceiveSocket"); return -1; } @@ -124,10 +129,16 @@ OpenAndConfSSDPReceiveSocket(int ipv6) 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, sockname_len) < 0) { - syslog(LOG_ERR, "bind(udp%s): %m", ipv6 ? "6" : ""); + syslog(LOG_ERR, "%s: bind(udp%s): %m", + "OpenAndConfSSDPReceiveSocket", ipv6 ? "6" : ""); close(s); return -1; } @@ -145,8 +156,8 @@ OpenAndConfSSDPReceiveSocket(int ipv6) if(AddMulticastMembership(s, lan_addr->addr.s_addr) < 0) { syslog(LOG_WARNING, - "Failed to add multicast membership for interface %s", - lan_addr->str); + "Failed to add multicast membership for interface %s", + lan_addr->str ? lan_addr->str : "NULL"); } } } @@ -162,9 +173,13 @@ OpenAndConfSSDPNotifySocket(in_addr_t addr) int s; unsigned char loopchar = 0; 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 sockaddr_in sockname; - + if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { syslog(LOG_ERR, "socket(udp_notify): %m"); @@ -186,7 +201,12 @@ OpenAndConfSSDPNotifySocket(in_addr_t addr) close(s); 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) { syslog(LOG_ERR, "setsockopt(udp_notify, SO_BROADCAST): %m"); @@ -208,28 +228,68 @@ OpenAndConfSSDPNotifySocket(in_addr_t addr) 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 OpenAndConfSSDPNotifySockets(int * sockets) /*OpenAndConfSSDPNotifySockets(int * sockets, struct lan_addr_s * lan_addr, int n_lan_addr)*/ { - int i, j; + int i; struct lan_addr_s * lan_addr; - for(i=0, lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next, i++) + for(i=0, lan_addr = lan_addrs.lh_first; + lan_addr != NULL; + lan_addr = lan_addr->list.le_next) { sockets[i] = OpenAndConfSSDPNotifySocket(lan_addr->addr.s_addr); if(sockets[i] < 0) - { - for(j=0; jindex); + if(sockets[i] < 0) + goto error; + i++; +#endif } return 0; +error: + while(--i >= 0) + { + close(sockets[i]); + sockets[i] = -1; + } + return -1; } /* @@ -262,13 +322,14 @@ SendSSDPAnnounce2(int s, const struct sockaddr * addr, { int l, n; char buf[512]; + char addr_str[64]; socklen_t addrlen; - /* + /* * follow guideline from document "UPnP Device Architecture 1.0" * uppercase is recommended. * DATE: is recommended * 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" @@ -279,7 +340,7 @@ SendSSDPAnnounce2(int s, const struct sockaddr * addr, "EXT:\r\n" "SERVER: " MINIUPNPD_SERVER_STRING "\r\n" "LOCATION: http://%s:%u" ROOTDESC_PATH "\r\n" - "OPT: \"http://schemas.upnp.org/upnp/1/0/\";\r\n" /* UDA v1.1 */ + "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 */ @@ -292,50 +353,84 @@ SendSSDPAnnounce2(int s, const struct sockaddr * addr, ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); n = sendto(s, buf, l, 0, addr, addrlen); - syslog(LOG_INFO, "SSDP Announce %d bytes to %s:%d ST: %.*s",n, - inet_ntoa(((const struct sockaddr_in *)addr)->sin_addr), - ntohs(((const struct sockaddr_in *)addr)->sin_port), + sockaddr_to_string(addr, addr_str, sizeof(addr_str)); + syslog(LOG_INFO, "SSDP Announce %d bytes to %s ST: %.*s",n, + addr_str, l, buf); if(n < 0) { + /* XXX handle EINTR, EAGAIN, EWOULDBLOCK */ syslog(LOG_ERR, "sendto(udp): %m"); } } -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", - "urn:schemas-upnp-org:device:InternetGatewayDevice:", - "urn:schemas-upnp-org:device:WANConnectionDevice:", - "urn:schemas-upnp-org:device:WANDevice:", - "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:", - "urn:schemas-upnp-org:service:WANIPConnection:", - "urn:schemas-upnp-org:service:WANPPPConnection:", + {"upnp:rootdevice", 0}, + {"urn:schemas-upnp-org:device:InternetGatewayDevice:", IGD_VER}, + {"urn:schemas-upnp-org:device:WANConnectionDevice:", 1}, + {"urn:schemas-upnp-org:device:WANDevice:", 1}, + {"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:", 1}, + {"urn:schemas-upnp-org:service:WANIPConnection:", WANIPC_VER}, + {"urn:schemas-upnp-org:service:WANPPPConnection:", 1}, #ifdef ENABLE_L3F_SERVICE - "urn:schemas-upnp-org:service:Layer3Forwarding:", + {"urn:schemas-upnp-org:service:Layer3Forwarding:", 1}, #endif #ifdef ENABLE_6FC_SERVICE - "url:schemas-upnp-org:service:WANIPv6FirewallControl:", + {"url:schemas-upnp-org:service:WANIPv6FirewallControl:", 1}, #endif - 0 + {0, 0} }; static void 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; +#endif int l, n, i=0; char bufr[512]; + char ver_str[4]; - memset(&sockname, 0, sizeof(struct sockaddr_in)); - sockname.sin_family = AF_INET; - sockname.sin_port = htons(SSDP_PORT); - sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR); + memset(&sockname, 0, sizeof(sockname)); +#ifdef ENABLE_IPV6 + if(ipv6) + { + 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) + ver_str[0] = '\0'; + else + snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version); + l = snprintf(bufr, sizeof(bufr), "NOTIFY * HTTP/1.1\r\n" "HOST: %s:%d\r\n" "CACHE-CONTROL: max-age=%u\r\n" @@ -344,27 +439,41 @@ SendSSDPNotifies(int s, const char * host, unsigned sh "NT: %s%s\r\n" "USN: %s::%s%s\r\n" "NTS: ssdp:alive\r\n" - "OPT: \"http://schemas.upnp.org/upnp/1/0/\";\r\n" /* UDA v1.1 */ + "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", - SSDP_MCAST_ADDR, SSDP_PORT, + ipv6 ? "[" LL_SSDP_MCAST_ADDR "]" : SSDP_MCAST_ADDR, + SSDP_PORT, lifetime, host, port, - known_service_types[i], (i==0?"":"1"), - uuidvalue, known_service_types[i], (i==0?"":"1"), + known_service_types[i].s, ver_str, + uuidvalue, known_service_types[i].s, ver_str, upnp_bootid, upnp_bootid, upnp_configid ); - if(l>=sizeof(bufr)) + if(l<0) { + syslog(LOG_ERR, "SendSSDPNotifies() snprintf error"); + continue; + } + if((unsigned int)l >= sizeof(bufr)) + { syslog(LOG_WARNING, "SendSSDPNotifies(): truncated output"); l = sizeof(bufr); } 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) { - 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++; } @@ -377,9 +486,18 @@ SendSSDPNotifies2(int * sockets, { int i; struct lan_addr_s * lan_addr; - for(i=0, lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next, i++) + for(i=0, lan_addr = lan_addrs.lh_first; + lan_addr != NULL; + lan_addr = lan_addr->list.le_next) { - SendSSDPNotifies(sockets[i], lan_addr->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 } } @@ -403,7 +521,14 @@ ProcessSSDPRequest(int s, unsigned short port) (struct sockaddr *)&sendername, &len_r); 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; } ProcessSSDPData(s, bufr, n, (struct sockaddr *)&sendername, port); @@ -417,11 +542,25 @@ ProcessSSDPData(int s, const char *bufr, int n, struct lan_addr_s * lan_addr = NULL; const char * st = NULL; 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) { @@ -445,7 +584,11 @@ ProcessSSDPData(int s, const char *bufr, int n, while(st[st_len]!='\r' && st[st_len]!='\n' && (st + st_len < bufr + n)) 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;*/ /*while(bufr[i+j]!='\r') j++;*/ /*syslog(LOG_INFO, "%.*s", j, bufr+i);*/ @@ -461,14 +604,6 @@ ProcessSSDPData(int s, const char *bufr, int n, /* find in which sub network the client is */ if(sender->sa_family == AF_INET) { - for(lan_addr = lan_addrs.lh_first; - lan_addr != NULL; - lan_addr = lan_addr->list.le_next) - { - if( (((const struct sockaddr_in *)sender)->sin_addr.s_addr & lan_addr->mask.s_addr) - == (lan_addr->addr.s_addr & lan_addr->mask.s_addr)) - break; - } if (lan_addr == NULL) { syslog(LOG_ERR, "Can't find in which sub network the client is"); @@ -480,14 +615,50 @@ ProcessSSDPData(int s, const char *bufr, int n, 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 */ - 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(l<=st_len && (0 == memcmp(st, known_service_types[i], l))) + 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 + ) { syslog(LOG_INFO, "Single search found"); SendSSDPAnnounce2(s, sender, @@ -501,13 +672,20 @@ ProcessSSDPData(int s, const char *bufr, int n, if(st_len==8 && (0 == memcmp(st, "ssdp:all", 8))) { 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) + ver_str[0] = '\0'; + else + 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], l, i==0?"":"1", + 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 */ l = (int)strlen(uuidvalue); @@ -529,51 +707,78 @@ ProcessSSDPData(int s, const char *bufr, int n, } } -/* This will broadcast ssdp:byebye notifications to inform +/* This will broadcast ssdp:byebye notifications to inform * the network that UPnP is going down. */ int SendSSDPGoodbye(int * sockets, int n_sockets) { struct sockaddr_in sockname; +#ifdef ENABLE_IPV6 + struct sockaddr_in6 sockname6; +#endif int n, l; int i, j; char bufr[512]; + char ver_str[4]; + int ret = 0; + int ipv6 = 0; memset(&sockname, 0, sizeof(struct sockaddr_in)); sockname.sin_family = AF_INET; sockname.sin_port = htons(SSDP_PORT); 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 0) l++; CODELENGTH(l, p); - memcpy(p, known_service_types[i], l); + memcpy(p, known_service_types[i].s, l); if(i > 0) p[l-1] = '1'; p += l; - l = snprintf(strbuf, sizeof(strbuf), "%s::%s%s", - uuidvalue, known_service_types[i], (i==0)?"":"1"); + if(i==0) + 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); memcpy(p, strbuf, l); p += l; @@ -624,9 +836,22 @@ SubmitServicesToMiniSSDPD(const char * host, unsigned CODELENGTH(l, p); memcpy(p, strbuf, l); p += l; - if(write(s, buffer, p - buffer) < 0) { - syslog(LOG_ERR, "write(): %m"); - return -1; + /* 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) { + 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);