"Fossies" - the Fresh Open Source Software Archive

Member "minidlna-1.3.0/minissdp.c" (24 Nov 2020, 23907 Bytes) of package /linux/privat/minidlna-1.3.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "minissdp.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.2.1_vs_1.3.0.

    1 /* MiniDLNA media server
    2  * This file is part of MiniDLNA.
    3  *
    4  * The code herein is based on the MiniUPnP Project.
    5  * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
    6  *
    7  * Copyright (c) 2006, Thomas Bernard
    8  * All rights reserved.
    9  *
   10  * Redistribution and use in source and binary forms, with or without
   11  * modification, are permitted provided that the following conditions are met:
   12  *     * Redistributions of source code must retain the above copyright
   13  *       notice, this list of conditions and the following disclaimer.
   14  *     * Redistributions in binary form must reproduce the above copyright
   15  *       notice, this list of conditions and the following disclaimer in the
   16  *       documentation and/or other materials provided with the distribution.
   17  *     * The name of the author may not be used to endorse or promote products
   18  *       derived from this software without specific prior written permission.
   19  *
   20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   23  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
   24  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   30  * POSSIBILITY OF SUCH DAMAGE.
   31  */
   32 #include "config.h"
   33 
   34 #include <stdio.h>
   35 #include <stdlib.h>
   36 #include <string.h>
   37 #include <ctype.h>
   38 #include <unistd.h>
   39 #include <sys/socket.h>
   40 #include <sys/un.h>
   41 #include <netinet/in.h>
   42 #include <arpa/inet.h>
   43 #include <errno.h>
   44 
   45 #include "event.h"
   46 #include "minidlnapath.h"
   47 #include "upnphttp.h"
   48 #include "upnpglobalvars.h"
   49 #include "upnpreplyparse.h"
   50 #include "getifaddr.h"
   51 #include "minissdp.h"
   52 #include "codelength.h"
   53 #include "utils.h"
   54 #include "log.h"
   55 
   56 /* SSDP ip/port */
   57 #define SSDP_PORT (1900)
   58 #define SSDP_MCAST_ADDR ("239.255.255.250")
   59 
   60 static int
   61 AddMulticastMembership(int s, struct lan_addr_s *iface)
   62 {
   63     int ret;
   64 #ifdef HAVE_STRUCT_IP_MREQN
   65     struct ip_mreqn imr;    /* Ip multicast membership */
   66     /* setting up imr structure */
   67     memset(&imr, '\0', sizeof(imr));
   68     imr.imr_multiaddr.s_addr = inet_addr(SSDP_MCAST_ADDR);
   69     imr.imr_ifindex = iface->ifindex;
   70 #else
   71     struct ip_mreq imr; /* Ip multicast membership */
   72     /* setting up imr structure */
   73     memset(&imr, '\0', sizeof(imr));
   74     imr.imr_multiaddr.s_addr = inet_addr(SSDP_MCAST_ADDR);
   75     imr.imr_interface.s_addr = iface->addr.s_addr;
   76 #endif
   77     /* Setting the socket options will guarantee, tha we will only receive
   78      * multicast traffic on a specific Interface.
   79      * In addition the kernel is instructed to send an igmp message (choose
   80      * mcast group) on the specific interface/subnet. */
   81     ret = setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&imr, sizeof(imr));
   82     if (ret < 0 && errno != EADDRINUSE)
   83     {
   84         DPRINTF(E_ERROR, L_SSDP, "setsockopt(udp, IP_ADD_MEMBERSHIP): %s\n",
   85             strerror(errno));
   86         return -1;
   87     }
   88 
   89     return 0;
   90 }
   91 
   92 /* Open and configure the socket listening for 
   93  * SSDP udp packets sent on 239.255.255.250 port 1900 */
   94 int
   95 OpenAndConfSSDPReceiveSocket(void)
   96 {
   97     int s;
   98     int i = 1;
   99     struct sockaddr_in sockname;
  100     
  101     s = socket(PF_INET, SOCK_DGRAM, 0);
  102     if (s < 0)
  103     {
  104         DPRINTF(E_ERROR, L_SSDP, "socket(udp): %s\n", strerror(errno));
  105         return -1;
  106     }   
  107 
  108     if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0)
  109         DPRINTF(E_WARN, L_SSDP, "setsockopt(udp, SO_REUSEADDR): %s\n", strerror(errno));
  110 #ifdef __linux__
  111     if (setsockopt(s, IPPROTO_IP, IP_PKTINFO, &i, sizeof(i)) < 0)
  112         DPRINTF(E_WARN, L_SSDP, "setsockopt(udp, IP_PKTINFO): %s\n", strerror(errno));
  113 #endif
  114     memset(&sockname, 0, sizeof(struct sockaddr_in));
  115     sockname.sin_family = AF_INET;
  116     sockname.sin_port = htons(SSDP_PORT);
  117 #ifdef __linux__
  118     /* NOTE: Binding a socket to a UDP multicast address means, that we just want
  119      * to receive datagramms send to this multicast address.
  120      * To specify the local nics we want to use we have to use setsockopt,
  121      * see AddMulticastMembership(...). */
  122     sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
  123 #else
  124     /* NOTE: Binding to SSDP_MCAST_ADDR on Darwin & *BSD causes NOTIFY replies are
  125      * sent from SSDP_MCAST_ADDR what forces some clients to ignore subsequent
  126      * unsolicited NOTIFY packets from the real interface address. */
  127     sockname.sin_addr.s_addr = htonl(INADDR_ANY);
  128 #endif
  129 
  130     if (bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
  131     {
  132         DPRINTF(E_ERROR, L_SSDP, "bind(udp): %s\n", strerror(errno));
  133         close(s);
  134         return -1;
  135     }
  136 
  137     return s;
  138 }
  139 
  140 /* open the UDP socket used to send SSDP notifications to
  141  * the multicast group reserved for them */
  142 int
  143 OpenAndConfSSDPNotifySocket(struct lan_addr_s *iface)
  144 {
  145     int s;
  146     unsigned char loopchar = 0;
  147     uint8_t ttl = 4;
  148 #ifdef HAVE_STRUCT_IP_MREQN
  149     struct ip_mreqn imr;
  150 #else
  151     struct in_addr mc_if;
  152 #endif
  153     struct sockaddr_in sockname;
  154     
  155     s = socket(PF_INET, SOCK_DGRAM, 0);
  156     if (s < 0)
  157     {
  158         DPRINTF(E_ERROR, L_SSDP, "socket(udp_notify): %s\n", strerror(errno));
  159         return -1;
  160     }
  161 
  162     if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loopchar, sizeof(loopchar)) < 0)
  163     {
  164         DPRINTF(E_ERROR, L_SSDP, "setsockopt(udp_notify, IP_MULTICAST_LOOP): %s\n", strerror(errno));
  165         close(s);
  166         return -1;
  167     }
  168 
  169 #ifdef HAVE_STRUCT_IP_MREQN
  170     imr.imr_address = iface->addr;
  171     imr.imr_ifindex = iface->ifindex;
  172     if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &imr, sizeof(imr)) < 0)
  173 #else
  174     mc_if = iface->addr;
  175     if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &mc_if, sizeof(mc_if)) < 0)
  176 #endif
  177     {
  178         DPRINTF(E_ERROR, L_SSDP, "setsockopt(udp_notify, IP_MULTICAST_IF): %s\n", strerror(errno));
  179         close(s);
  180         return -1;
  181     }
  182 
  183     setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
  184 
  185     memset(&sockname, 0, sizeof(struct sockaddr_in));
  186     sockname.sin_family = AF_INET;
  187     sockname.sin_addr.s_addr = iface->addr.s_addr;
  188 
  189     if (bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
  190     {
  191         DPRINTF(E_ERROR, L_SSDP, "bind(udp_notify): %s\n", strerror(errno));
  192         close(s);
  193         return -1;
  194     }
  195 
  196     if (AddMulticastMembership(sssdp, iface) < 0)
  197     {
  198         DPRINTF(E_WARN, L_SSDP, "Failed to add multicast membership for address %s\n", 
  199             iface->str);
  200     }
  201 
  202     return s;
  203 }
  204 
  205 static const char * const known_service_types[] =
  206 {
  207     uuidvalue,
  208     "upnp:rootdevice",
  209     "urn:schemas-upnp-org:device:MediaServer:",
  210     "urn:schemas-upnp-org:service:ContentDirectory:",
  211     "urn:schemas-upnp-org:service:ConnectionManager:",
  212     "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:",
  213     0
  214 };
  215 
  216 static void
  217 _usleep(long min, long max)
  218 {
  219     struct timespec sleep_time;
  220     long usecs = min + rand() / (RAND_MAX / (max - min + 1) + 1);
  221 
  222     sleep_time.tv_sec = 0;
  223     sleep_time.tv_nsec = usecs * 1000;
  224     nanosleep(&sleep_time, NULL);
  225 }
  226 
  227 /* not really an SSDP "announce" as it is the response
  228  * to a SSDP "M-SEARCH" */
  229 static void
  230 SendSSDPResponse(int s, struct sockaddr_in sockname, int st_no,
  231          const char *host, unsigned short port, socklen_t len_r)
  232 {
  233     int l, n;
  234     char buf[512];
  235     char tmstr[30];
  236     time_t tm = time(NULL);
  237 
  238     /*
  239      * follow guideline from document "UPnP Device Architecture 1.0"
  240      * uppercase is recommended.
  241      * DATE: is recommended
  242      * SERVER: OS/ver UPnP/1.0 minidlna/1.0
  243      * - check what to put in the 'Cache-Control' header 
  244      * */
  245     strftime(tmstr, sizeof(tmstr), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&tm));
  246     l = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\n"
  247         "CACHE-CONTROL: max-age=%u\r\n"
  248         "DATE: %s\r\n"
  249         "ST: %s%s\r\n"
  250         "USN: %s%s%s%s\r\n"
  251         "EXT:\r\n"
  252         "SERVER: " MINIDLNA_SERVER_STRING "\r\n"
  253         "LOCATION: http://%s:%u" ROOTDESC_PATH "\r\n"
  254         "Content-Length: 0\r\n"
  255         "\r\n",
  256         (runtime_vars.notify_interval<<1)+10,
  257         tmstr,
  258         known_service_types[st_no],
  259         (st_no > 1 ? "1" : ""),
  260         uuidvalue,
  261         (st_no > 0 ? "::" : ""),
  262         (st_no > 0 ? known_service_types[st_no] : ""),
  263         (st_no > 1 ? "1" : ""),
  264         host, (unsigned int)port);
  265     DPRINTF(E_DEBUG, L_SSDP, "Sending M-SEARCH response to %s:%d ST: %s\n",
  266         inet_ntoa(sockname.sin_addr), ntohs(sockname.sin_port),
  267         known_service_types[st_no]);
  268     n = sendto(s, buf, l, 0,
  269                (struct sockaddr *)&sockname, len_r);
  270     if (n < 0)
  271         DPRINTF(E_ERROR, L_SSDP, "sendto(udp): %s\n", strerror(errno));
  272 }
  273 
  274 void
  275 SendSSDPNotifies(int s, const char *host, unsigned short port,
  276                  unsigned int interval)
  277 {
  278     struct sockaddr_in sockname;
  279     int l, n, dup, i=0;
  280     unsigned int lifetime;
  281     char bufr[512];
  282 
  283     memset(&sockname, 0, sizeof(struct sockaddr_in));
  284     sockname.sin_family = AF_INET;
  285     sockname.sin_port = htons(SSDP_PORT);
  286     sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
  287     lifetime = (interval << 1) + 10;
  288 
  289     for (dup = 0; dup < 2; dup++)
  290     {
  291         if (dup)
  292             _usleep(150000, 250000);
  293         i = 0;
  294         while (known_service_types[i])
  295         {
  296             l = snprintf(bufr, sizeof(bufr), 
  297                     "NOTIFY * HTTP/1.1\r\n"
  298                     "HOST:%s:%d\r\n"
  299                     "CACHE-CONTROL:max-age=%u\r\n"
  300                     "LOCATION:http://%s:%d" ROOTDESC_PATH"\r\n"
  301                     "SERVER: " MINIDLNA_SERVER_STRING "\r\n"
  302                     "NT:%s%s\r\n"
  303                     "USN:%s%s%s%s\r\n"
  304                     "NTS:ssdp:alive\r\n"
  305                     "\r\n",
  306                     SSDP_MCAST_ADDR, SSDP_PORT,
  307                     lifetime,
  308                     host, port,
  309                     known_service_types[i],
  310                     (i > 1 ? "1" : ""),
  311                     uuidvalue,
  312                     (i > 0 ? "::" : ""),
  313                     (i > 0 ? known_service_types[i] : ""),
  314                     (i > 1 ? "1" : ""));
  315             if (l >= sizeof(bufr))
  316             {
  317                 DPRINTF(E_WARN, L_SSDP, "SendSSDPNotifies(): truncated output\n");
  318                 l = sizeof(bufr);
  319             }
  320             DPRINTF(E_MAXDEBUG, L_SSDP, "Sending ssdp:alive [%d]\n", s);
  321             n = sendto(s, bufr, l, 0,
  322                 (struct sockaddr *)&sockname, sizeof(struct sockaddr_in));
  323             if (n < 0)
  324                 DPRINTF(E_ERROR, L_SSDP, "sendto(udp_notify=%d, %s): %s\n", s, host, strerror(errno));
  325             i++;
  326         }
  327     }
  328 }
  329 
  330 static void
  331 ParseUPnPClient(char *location)
  332 {
  333     char buf[8192];
  334     struct sockaddr_in dest;
  335     int s, n, do_headers = 0, nread = 0;
  336     struct timeval tv;
  337     char *addr, *path, *port_str;
  338     long port = 80;
  339     char *off = NULL, *p;
  340     int content_len = sizeof(buf);
  341     struct NameValueParserData xml;
  342     struct client_cache_s *client;
  343     int type = 0;
  344     char *model, *serial, *name;
  345 
  346     if (strncmp(location, "http://", 7) != 0)
  347         return;
  348     path = location + 7;
  349     port_str = strsep(&path, "/");
  350     if (!path)
  351         return;
  352     addr = strsep(&port_str, ":");
  353     if (port_str)
  354     {
  355         port = strtol(port_str, NULL, 10);
  356         if (!port)
  357             port = 80;
  358     }
  359 
  360     memset(&dest, '\0', sizeof(dest));
  361     if (!inet_aton(addr, &dest.sin_addr))
  362         return;
  363     /* Check if the client is already in cache */
  364     dest.sin_family = AF_INET;
  365     dest.sin_port = htons(port);
  366 
  367     s = socket(PF_INET, SOCK_STREAM, 0);
  368     if (s < 0)
  369         return;
  370 
  371     tv.tv_sec = 0;
  372     tv.tv_usec = 500000;
  373     setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
  374     setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
  375 
  376     if (connect(s, (struct sockaddr*)&dest, sizeof(struct sockaddr_in)) < 0)
  377         goto close;
  378 
  379     n = snprintf(buf, sizeof(buf), "GET /%s HTTP/1.0\r\n"
  380                                    "HOST: %s:%ld\r\n\r\n",
  381                                    path, addr, port);
  382     if (write(s, buf, n) < 1)
  383         goto close;
  384 
  385     while ((n = read(s, buf+nread, sizeof(buf)-nread-1)) > 0)
  386     {
  387         nread += n;
  388         buf[nread] = '\0';
  389         n = nread - 4;
  390         p = buf;
  391 
  392         while (!off && (n-- > 0))
  393         {
  394             if (p[0] == '\r' && p[1] == '\n' && p[2] == '\r' && p[3] == '\n')
  395             {
  396                 off = p + 4;
  397                 do_headers = 1;
  398             }
  399             p++;
  400         }
  401         if (!off)
  402             continue;
  403 
  404         if (do_headers)
  405         {
  406             p = buf;
  407             if (strncmp(p, "HTTP/", 5) != 0)
  408                 goto close;
  409             while (*p != ' ' && *p != '\t')
  410                 p++;
  411             /* If we don't get a 200 status, ignore it */
  412             if (strtol(p, NULL, 10) != 200)
  413                 goto close;
  414             p = strcasestr(p, "Content-Length:");
  415             if (p)
  416                 content_len = strtol(p+15, NULL, 10);
  417             do_headers = 0;
  418         }
  419         if ((buf + nread - off) >= content_len)
  420             break;
  421     }
  422 close:
  423     close(s);
  424     if (!off)
  425         return;
  426     nread -= off - buf;
  427     ParseNameValue(off, nread, &xml, 0);
  428     model = GetValueFromNameValueList(&xml, "modelName");
  429     serial = GetValueFromNameValueList(&xml, "serialNumber");
  430     name = GetValueFromNameValueList(&xml, "friendlyName");
  431     if (model)
  432     {
  433         int i;
  434         DPRINTF(E_DEBUG, L_SSDP, "Model: %s\n", model);
  435         for (i = 0; client_types[i].name; i++)
  436         {
  437             if (client_types[i].match_type != EModelName)
  438                 continue;
  439             if (strstr(model, client_types[i].match) != NULL)
  440             {
  441                 type = i;
  442                 break;
  443             }
  444         }
  445 
  446         /* Special Samsung handling.  It's very hard to tell Series A from B */
  447         if (type > 0 && client_types[type].type == ESamsungSeriesB)
  448         {
  449             if (serial)
  450             {
  451                 DPRINTF(E_DEBUG, L_SSDP, "Serial: %s\n", serial);
  452                 /* The Series B I saw was 20081224DMR.  Series A should be older than that. */
  453                 if (atoi(serial) < 20081201)
  454                     type = 0;
  455             }
  456             else
  457             {
  458                 type = 0;
  459             }
  460         }
  461 
  462         if (type == 0 && name != NULL)
  463         {
  464             for (i = 0; client_types[i].name; i++)
  465             {
  466                 if (client_types[i].match_type != EFriendlyNameSSDP)
  467                     continue;
  468                 if (strcmp(name, client_types[i].match) == 0)
  469                 {
  470                     type = i;
  471                     break;
  472                 }
  473             }
  474         }
  475     }
  476     ClearNameValueList(&xml);
  477     if (!type)
  478         return;
  479     /* Add this client to the cache if it's not there already. */
  480     client = SearchClientCache(dest.sin_addr, 1);
  481     if (!client)
  482     {
  483         AddClientCache(dest.sin_addr, type);
  484     }
  485     else
  486     {
  487         client->type = &client_types[type];
  488         client->age = time(NULL);
  489     }
  490 }
  491 
  492 /* ProcessSSDPRequest()
  493  * process SSDP M-SEARCH requests and responds to them */
  494 void
  495 ProcessSSDPRequest(struct event *ev)
  496 {
  497     int s = ev->fd;
  498     int n;
  499     char bufr[1500];
  500     struct sockaddr_in sendername;
  501     int i;
  502     char *st = NULL, *mx = NULL, *man = NULL, *mx_end = NULL;
  503     int man_len = 0;
  504     socklen_t len_r = sizeof(struct sockaddr_in);
  505 #ifdef __linux__
  506     char cmbuf[CMSG_SPACE(sizeof(struct in_pktinfo))];
  507     struct iovec iovec = {
  508         .iov_base = bufr,
  509         .iov_len = sizeof(bufr)-1
  510     };
  511     struct msghdr mh = {
  512         .msg_name = &sendername,
  513         .msg_namelen = sizeof(struct sockaddr_in),
  514         .msg_iov = &iovec,
  515         .msg_iovlen = 1,
  516         .msg_control = cmbuf,
  517         .msg_controllen = sizeof(cmbuf)
  518     };
  519 
  520     n = recvmsg(s, &mh, 0);
  521 #else
  522 
  523     n = recvfrom(s, bufr, sizeof(bufr)-1, 0,
  524                  (struct sockaddr *)&sendername, &len_r);
  525     len_r = MIN(len_r, sizeof(struct sockaddr_in));
  526 #endif
  527     if (n < 0)
  528     {
  529         DPRINTF(E_ERROR, L_SSDP, "recvfrom(udp): %s\n", strerror(errno));
  530         return;
  531     }
  532     bufr[n] = '\0';
  533     n -= 2;
  534 
  535     if (memcmp(bufr, "NOTIFY", 6) == 0)
  536     {
  537         char *loc = NULL, *srv = NULL, *nts = NULL, *nt = NULL;
  538         int loc_len = 0;
  539         //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Received SSDP notify:\n%.*s", n, bufr);
  540         for (i = 0; i < n; i++)
  541         {
  542             if( bufr[i] == '*' )
  543                 break;
  544         }
  545         if (strcasestrc(bufr+i, "HTTP/1.1", '\r') == NULL)
  546             return;
  547         while (i < n)
  548         {
  549             while ((i < n) && (bufr[i] != '\r' || bufr[i+1] != '\n'))
  550                 i++;
  551             i += 2;
  552             if (strncasecmp(bufr+i, "SERVER:", 7) == 0)
  553             {
  554                 srv = bufr+i+7;
  555                 while (*srv == ' ' || *srv == '\t')
  556                     srv++;
  557             }
  558             else if (strncasecmp(bufr+i, "LOCATION:", 9) == 0)
  559             {
  560                 loc = bufr+i+9;
  561                 while (*loc == ' ' || *loc == '\t')
  562                     loc++;
  563                 while (loc[loc_len]!='\r' && loc[loc_len]!='\n')
  564                     loc_len++;
  565             }
  566             else if (strncasecmp(bufr+i, "NTS:", 4) == 0)
  567             {
  568                 nts = bufr+i+4;
  569                 while (*nts == ' ' || *nts == '\t')
  570                     nts++;
  571             }
  572             else if (strncasecmp(bufr+i, "NT:", 3) == 0)
  573             {
  574                 nt = bufr+i+3;
  575                 while(*nt == ' ' || *nt == '\t')
  576                     nt++;
  577             }
  578         }
  579         if (!loc || !srv || !nt || !nts || (strncmp(nts, "ssdp:alive", 10) != 0) ||
  580             (strncmp(nt, "urn:schemas-upnp-org:device:MediaRenderer", 41) != 0))
  581             return;
  582         loc[loc_len] = '\0';
  583         if ((strncmp(srv, "Allegro-Software-RomPlug", 24) == 0) || /* Roku */
  584             (strstr(loc, "SamsungMRDesc.xml") != NULL) || /* Samsung TV */
  585             (strstrc(srv, "DigiOn DiXiM", '\r') != NULL)) /* Marantz Receiver */
  586         {
  587             /* Check if the client is already in cache */
  588             struct client_cache_s *client = SearchClientCache(sendername.sin_addr, 1);
  589             if (client)
  590             {
  591                 if (client->type->type < EStandardDLNA150 &&
  592                     client->type->type != ESamsungSeriesA)
  593                 {
  594                     client->age = time(NULL);
  595                     return;
  596                 }
  597             }
  598             ParseUPnPClient(loc);
  599         }
  600     }
  601     else if (memcmp(bufr, "M-SEARCH", 8) == 0)
  602     {
  603         int st_len = 0, mx_len = 0, mx_val = 0;
  604         //DPRINTF(E_DEBUG, L_SSDP, "Received SSDP request:\n%.*s\n", n, bufr);
  605         for (i = 0; i < n; i++)
  606         {
  607             if (bufr[i] == '*')
  608                 break;
  609         }
  610         if (strcasestrc(bufr+i, "HTTP/1.1", '\r') == NULL)
  611             return;
  612         while (i < n)
  613         {
  614             while ((i < n) && (bufr[i] != '\r' || bufr[i+1] != '\n'))
  615                 i++;
  616             i += 2;
  617             if (strncasecmp(bufr+i, "ST:", 3) == 0)
  618             {
  619                 st = bufr+i+3;
  620                 st_len = 0;
  621                 while (*st == ' ' || *st == '\t')
  622                     st++;
  623                 while (st[st_len]!='\r' && st[st_len]!='\n')
  624                     st_len++;
  625             }
  626             else if (strncasecmp(bufr+i, "MX:", 3) == 0)
  627             {
  628                 mx = bufr+i+3;
  629                 mx_len = 0;
  630                 while (*mx == ' ' || *mx == '\t')
  631                     mx++;
  632                 while (mx[mx_len]!='\r' && mx[mx_len]!='\n')
  633                     mx_len++;
  634                 mx_val = strtol(mx, &mx_end, 10);
  635             }
  636             else if (strncasecmp(bufr+i, "MAN:", 4) == 0)
  637             {
  638                 man = bufr+i+4;
  639                 man_len = 0;
  640                 while (*man == ' ' || *man == '\t')
  641                     man++;
  642                 while (man[man_len]!='\r' && man[man_len]!='\n')
  643                     man_len++;
  644             }
  645         }
  646         /*DPRINTF(E_INFO, L_SSDP, "SSDP M-SEARCH packet received from %s:%d\n",
  647             inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port) );*/
  648         if (GETFLAG(DLNA_STRICT_MASK) && (ntohs(sendername.sin_port) <= 1024 || ntohs(sendername.sin_port) == 1900))
  649         {
  650             DPRINTF(E_INFO, L_SSDP, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad source port %d]\n",
  651                 inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port));
  652         }
  653         else if (!man || (strncmp(man, "\"ssdp:discover\"", 15) != 0))
  654         {
  655             DPRINTF(E_INFO, L_SSDP, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad %s header '%.*s']\n",
  656                 inet_ntoa(sendername.sin_addr), "MAN", man_len, man);
  657         }
  658         else if (!mx || mx == mx_end || mx_val < 0)
  659         {
  660             DPRINTF(E_INFO, L_SSDP, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad %s header '%.*s']\n",
  661                 inet_ntoa(sendername.sin_addr), "MX", mx_len, mx);
  662         }
  663         else if (st && (st_len > 0))
  664         {
  665             int l;
  666 #ifdef __linux__
  667             char host[40] = "127.0.0.1";
  668             struct cmsghdr *cmsg;
  669 
  670             /* find the interface we received the msg from */
  671             for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg))
  672             {
  673                 struct in_addr addr;
  674                 struct in_pktinfo *pi;
  675                 /* ignore the control headers that don't match what we want */
  676                 if (cmsg->cmsg_level != IPPROTO_IP ||
  677                     cmsg->cmsg_type != IP_PKTINFO)
  678                     continue;
  679 
  680                 pi = (struct in_pktinfo *)CMSG_DATA(cmsg);
  681                 addr = pi->ipi_spec_dst;
  682                 inet_ntop(AF_INET, &addr, host, sizeof(host));
  683                 for (i = 0; i < n_lan_addr; i++)
  684                 {
  685                     if (pi->ipi_ifindex == lan_addr[i].ifindex)
  686                         break;
  687                 }
  688             }
  689 #else
  690             const char *host;
  691             int iface = 0;
  692             /* find in which sub network the client is */
  693             for (i = 0; i < n_lan_addr; i++)
  694             {
  695                 if((sendername.sin_addr.s_addr & lan_addr[i].mask.s_addr) ==
  696                    (lan_addr[i].addr.s_addr & lan_addr[i].mask.s_addr))
  697                 {
  698                     iface = i;
  699                     break;
  700                 }
  701             }
  702             host = lan_addr[iface].str;
  703 #endif
  704             if (n_lan_addr == i)
  705             {
  706                 DPRINTF(E_DEBUG, L_SSDP, "Ignoring SSDP M-SEARCH on other interface [%s]\n",
  707                     inet_ntoa(sendername.sin_addr));
  708                 return;
  709             }
  710             DPRINTF(E_DEBUG, L_SSDP, "SSDP M-SEARCH from %s:%d ST: %.*s, MX: %.*s, MAN: %.*s\n",
  711                 inet_ntoa(sendername.sin_addr),
  712                 ntohs(sendername.sin_port),
  713                 st_len, st, mx_len, mx, man_len, man);
  714             /* Responds to request with a device as ST header */
  715             for (i = 0; known_service_types[i]; i++)
  716             {
  717                 l = strlen(known_service_types[i]);
  718                 if ((l > st_len) || (memcmp(st, known_service_types[i], l) != 0))
  719                     continue;
  720                 if (st_len != l)
  721                 {
  722                     /* Check version number - we only support 1. */
  723                     if ((st[l-1] == ':') && (st[l] == '1'))
  724                         l++;
  725                     while (l < st_len)
  726                     {
  727                         if (isdigit(st[l]))
  728                             break;
  729                         if (isspace(st[l]))
  730                         {
  731                             l++;
  732                             continue;
  733                         }
  734                         DPRINTF(E_MAXDEBUG, L_SSDP,
  735                             "Ignoring SSDP M-SEARCH with bad extra data '%c' [%s]\n",
  736                             st[l], inet_ntoa(sendername.sin_addr));
  737                         break;
  738                     }
  739                     if (l != st_len)
  740                         break;
  741                 }
  742                 _usleep(13000, 20000);
  743                 SendSSDPResponse(s, sendername, i, host,
  744                     (unsigned short)runtime_vars.port, len_r);
  745                 return;
  746             }
  747             /* Responds to request with ST: ssdp:all */
  748             /* strlen("ssdp:all") == 8 */
  749             if ((st_len == 8) && (memcmp(st, "ssdp:all", 8) == 0))
  750             {
  751                 _usleep(13000, 30000);
  752                 for (i=0; known_service_types[i]; i++)
  753                 {
  754                     l = strlen(known_service_types[i]);
  755                     SendSSDPResponse(s, sendername, i, host,
  756                         (unsigned short)runtime_vars.port,
  757                         len_r);
  758                 }
  759             }
  760         }
  761         else
  762         {
  763             DPRINTF(E_INFO, L_SSDP, "Invalid SSDP M-SEARCH from %s:%d\n",
  764                 inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port));
  765         }
  766     }
  767     else if (memcmp(bufr, "YOUKU-NOTIFY", 12) == 0)
  768     {
  769         return;
  770     }
  771     else
  772     {
  773         DPRINTF(E_WARN, L_SSDP, "Unknown udp packet received from %s:%d\n",
  774             inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port));
  775     }
  776 }
  777 
  778 /* This will broadcast ssdp:byebye notifications to inform 
  779  * the network that UPnP is going down. */
  780 int
  781 SendSSDPGoodbyes(int s)
  782 {
  783     struct sockaddr_in sockname;
  784     int n, l;
  785     int i;
  786     int dup, ret = 0;
  787     char bufr[512];
  788 
  789     memset(&sockname, 0, sizeof(struct sockaddr_in));
  790     sockname.sin_family = AF_INET;
  791     sockname.sin_port = htons(SSDP_PORT);
  792     sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
  793 
  794     for (dup = 0; dup < 2; dup++)
  795     {
  796         for (i = 0; known_service_types[i]; i++)
  797         {
  798             l = snprintf(bufr, sizeof(bufr),
  799                     "NOTIFY * HTTP/1.1\r\n"
  800                     "HOST:%s:%d\r\n"
  801                     "NT:%s%s\r\n"
  802                     "USN:%s%s%s%s\r\n"
  803                     "NTS:ssdp:byebye\r\n"
  804                     "\r\n",
  805                     SSDP_MCAST_ADDR, SSDP_PORT,
  806                     known_service_types[i],
  807                     (i > 1 ? "1" : ""), uuidvalue,
  808                     (i > 0 ? "::" : ""),
  809                     (i > 0 ? known_service_types[i] : ""),
  810                     (i > 1 ? "1" : ""));
  811             DPRINTF(E_MAXDEBUG, L_SSDP, "Sending ssdp:byebye [%d]\n", s);
  812             n = sendto(s, bufr, l, 0,
  813                        (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
  814             if (n < 0)
  815             {
  816                 DPRINTF(E_ERROR, L_SSDP, "sendto(udp_shutdown=%d): %s\n", s, strerror(errno));
  817                 ret = -1;
  818                 break;
  819             }
  820         }
  821     }
  822     return ret;
  823 }
  824 
  825 /* SubmitServicesToMiniSSDPD() :
  826  * register services offered by MiniUPnPd to a running instance of
  827  * MiniSSDPd */
  828 int
  829 SubmitServicesToMiniSSDPD(const char *host, unsigned short port)
  830 {
  831     struct sockaddr_un addr;
  832     int s;
  833     unsigned char buffer[2048];
  834     char strbuf[256];
  835     unsigned char *p;
  836     int i, l;
  837 
  838     s = socket(AF_UNIX, SOCK_STREAM, 0);
  839     if (s < 0)
  840     {
  841         DPRINTF(E_ERROR, L_SSDP, "socket(unix): %s", strerror(errno));
  842         return -1;
  843     }
  844     addr.sun_family = AF_UNIX;
  845     strncpyt(addr.sun_path, minissdpdsocketpath, sizeof(addr.sun_path));
  846     if (connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0)
  847     {
  848         DPRINTF(E_ERROR, L_SSDP, "connect(\"%s\"): %s",
  849             minissdpdsocketpath, strerror(errno));
  850         close(s);
  851         return -1;
  852     }
  853     for (i = 0; known_service_types[i]; i++)
  854     {
  855         buffer[0] = 4;
  856         p = buffer + 1;
  857         l = strlen(known_service_types[i]);
  858         if (i > 0)
  859             l++;
  860         CODELENGTH(l, p);
  861         memcpy(p, known_service_types[i], l);
  862         if (i > 0)
  863             p[l-1] = '1';
  864         p += l;
  865         l = snprintf(strbuf, sizeof(strbuf), "%s::%s%s", 
  866                      uuidvalue, known_service_types[i], (i==0)?"":"1");
  867         CODELENGTH(l, p);
  868         memcpy(p, strbuf, l);
  869         p += l;
  870         l = strlen(MINIDLNA_SERVER_STRING);
  871         CODELENGTH(l, p);
  872         memcpy(p, MINIDLNA_SERVER_STRING, l);
  873         p += l;
  874         l = snprintf(strbuf, sizeof(strbuf), "http://%s:%u" ROOTDESC_PATH,
  875                      host, (unsigned int)port);
  876         CODELENGTH(l, p);
  877         memcpy(p, strbuf, l);
  878         p += l;
  879         if(write(s, buffer, p - buffer) < 0)
  880         {
  881             DPRINTF(E_ERROR, L_SSDP, "write(): %s", strerror(errno));
  882             close(s);
  883             return -1;
  884         }
  885     }
  886     close(s);
  887     return 0;
  888 }
  889