source: titan/minidlna-1.0.22/minissdp.c @ 13567

Last change on this file since 13567 was 13567, checked in by obi, 11 years ago

[titan] add minidlna-1.0.22 first step

File size: 22.3 KB
Line 
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 <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <unistd.h>
36#include <sys/socket.h>
37#include <sys/un.h>
38#include <netinet/in.h>
39#include <arpa/inet.h>
40#include <errno.h>
41
42#include "config.h"
43#include "upnpdescstrings.h"
44#include "minidlnapath.h"
45#include "upnphttp.h"
46#include "upnpglobalvars.h"
47#include "upnpreplyparse.h"
48#include "getifaddr.h"
49#include "minissdp.h"
50#include "codelength.h"
51#include "utils.h"
52#include "log.h"
53
54/* SSDP ip/port */
55#define SSDP_PORT (1900)
56#define SSDP_MCAST_ADDR ("239.255.255.250")
57
58static int
59AddMulticastMembership(int s, in_addr_t ifaddr)
60{
61        struct ip_mreq imr;     /* Ip multicast membership */
62
63        /* setting up imr structure */
64        imr.imr_multiaddr.s_addr = inet_addr(SSDP_MCAST_ADDR);
65        /*imr.imr_interface.s_addr = htonl(INADDR_ANY);*/
66        imr.imr_interface.s_addr = ifaddr;      /*inet_addr(ifaddr);*/
67       
68        if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&imr, sizeof(struct ip_mreq)) < 0)
69        {
70                DPRINTF(E_ERROR, L_SSDP, "setsockopt(udp, IP_ADD_MEMBERSHIP): %s\n", strerror(errno));
71                return -1;
72        }
73
74        return 0;
75}
76
77/* Open and configure the socket listening for
78 * SSDP udp packets sent on 239.255.255.250 port 1900 */
79int
80OpenAndConfSSDPReceiveSocket()
81{
82        int s;
83        int i = 1;
84        struct sockaddr_in sockname;
85       
86        if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
87        {
88                DPRINTF(E_ERROR, L_SSDP, "socket(udp): %s\n", strerror(errno));
89                return -1;
90        }       
91
92        if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0)
93        {
94                DPRINTF(E_WARN, L_SSDP, "setsockopt(udp, SO_REUSEADDR): %s\n", strerror(errno));
95        }
96       
97        memset(&sockname, 0, sizeof(struct sockaddr_in));
98        sockname.sin_family = AF_INET;
99        sockname.sin_port = htons(SSDP_PORT);
100        /* NOTE : it seems it doesnt work when binding on the specific address */
101        /*sockname.sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);*/
102        sockname.sin_addr.s_addr = htonl(INADDR_ANY);
103        /*sockname.sin_addr.s_addr = inet_addr(ifaddr);*/
104
105        if(bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
106        {
107                DPRINTF(E_ERROR, L_SSDP, "bind(udp): %s\n", strerror(errno));
108                close(s);
109                return -1;
110        }
111
112        i = n_lan_addr;
113        while(i>0)
114        {
115                i--;
116                if(AddMulticastMembership(s, lan_addr[i].addr.s_addr) < 0)
117                {
118                        DPRINTF(E_WARN, L_SSDP,
119                               "Failed to add multicast membership for address %s\n",
120                               lan_addr[i].str );
121                }
122        }
123
124        return s;
125}
126
127/* open the UDP socket used to send SSDP notifications to
128 * the multicast group reserved for them */
129static int
130OpenAndConfSSDPNotifySocket(in_addr_t addr)
131{
132        int s;
133        unsigned char loopchar = 0;
134        int bcast = 1;
135        struct in_addr mc_if;
136        struct sockaddr_in sockname;
137       
138        if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
139        {
140                DPRINTF(E_ERROR, L_SSDP, "socket(udp_notify): %s\n", strerror(errno));
141                return -1;
142        }
143
144        mc_if.s_addr = addr;    /*inet_addr(addr);*/
145
146        if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loopchar, sizeof(loopchar)) < 0)
147        {
148                DPRINTF(E_ERROR, L_SSDP, "setsockopt(udp_notify, IP_MULTICAST_LOOP): %s\n", strerror(errno));
149                close(s);
150                return -1;
151        }
152
153        if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&mc_if, sizeof(mc_if)) < 0)
154        {
155                DPRINTF(E_ERROR, L_SSDP, "setsockopt(udp_notify, IP_MULTICAST_IF): %s\n", strerror(errno));
156                close(s);
157                return -1;
158        }
159       
160        if(setsockopt(s, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bcast)) < 0)
161        {
162                DPRINTF(E_ERROR, L_SSDP, "setsockopt(udp_notify, SO_BROADCAST): %s\n", strerror(errno));
163                close(s);
164                return -1;
165        }
166
167        memset(&sockname, 0, sizeof(struct sockaddr_in));
168        sockname.sin_family = AF_INET;
169        sockname.sin_addr.s_addr = addr;        /*inet_addr(addr);*/
170
171        if (bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
172        {
173                DPRINTF(E_ERROR, L_SSDP, "bind(udp_notify): %s\n", strerror(errno));
174                close(s);
175                return -1;
176        }
177
178        return s;
179}
180
181int
182OpenAndConfSSDPNotifySockets(int * sockets)
183{
184        int i, j;
185        for(i=0; i<n_lan_addr; i++)
186        {
187                sockets[i] = OpenAndConfSSDPNotifySocket(lan_addr[i].addr.s_addr);
188                if(sockets[i] < 0)
189                {
190                        for(j=0; j<i; j++)
191                        {
192                                close(sockets[j]);
193                                sockets[j] = -1;
194                        }
195                        return -1;
196                }
197        }
198        return 0;
199}
200
201/*
202 * response from a LiveBox (Wanadoo)
203HTTP/1.1 200 OK
204CACHE-CONTROL: max-age=1800
205DATE: Thu, 01 Jan 1970 04:03:23 GMT
206EXT:
207LOCATION: http://192.168.0.1:49152/gatedesc.xml
208SERVER: Linux/2.4.17, UPnP/1.0, Intel SDK for UPnP devices /1.2
209ST: upnp:rootdevice
210USN: uuid:75802409-bccb-40e7-8e6c-fa095ecce13e::upnp:rootdevice
211
212 * response from a Linksys 802.11b :
213HTTP/1.1 200 OK
214Cache-Control:max-age=120
215Location:http://192.168.5.1:5678/rootDesc.xml
216Server:NT/5.0 UPnP/1.0
217ST:upnp:rootdevice
218USN:uuid:upnp-InternetGatewayDevice-1_0-0090a2777777::upnp:rootdevice
219EXT:
220 */
221
222static const char * const known_service_types[] =
223{
224        uuidvalue,
225        "upnp:rootdevice",
226        "urn:schemas-upnp-org:device:MediaServer:",
227        "urn:schemas-upnp-org:service:ContentDirectory:",
228        "urn:schemas-upnp-org:service:ConnectionManager:",
229        "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:",
230        0
231};
232
233static void
234_usleep(long usecs)
235{
236        struct timespec sleep_time;
237
238        sleep_time.tv_sec = 0;
239        sleep_time.tv_nsec = usecs * 1000;
240        nanosleep(&sleep_time, NULL);
241}
242
243/* not really an SSDP "announce" as it is the response
244 * to a SSDP "M-SEARCH" */
245static void
246SendSSDPAnnounce2(int s, struct sockaddr_in sockname, int st_no,
247                  const char * host, unsigned short port)
248{
249        int l, n;
250        char buf[512];
251        /*
252         * follow guideline from document "UPnP Device Architecture 1.0"
253         * uppercase is recommended.
254         * DATE: is recommended
255         * SERVER: OS/ver UPnP/1.0 minidlna/1.0
256         * - check what to put in the 'Cache-Control' header
257         * */
258        char   szTime[30];
259        time_t tTime = time(NULL);
260        strftime(szTime, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&tTime));
261
262        l = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\n"
263                "CACHE-CONTROL: max-age=%u\r\n"
264                "DATE: %s\r\n"
265                "ST: %s%s\r\n"
266                "USN: %s%s%s%s\r\n"
267                "EXT:\r\n"
268                "SERVER: " MINIDLNA_SERVER_STRING "\r\n"
269                "LOCATION: http://%s:%u" ROOTDESC_PATH "\r\n"
270                "Content-Length: 0\r\n"
271                "\r\n",
272                (runtime_vars.notify_interval<<1)+10,
273                szTime,
274                known_service_types[st_no], (st_no>1?"1":""),
275                uuidvalue, (st_no>0?"::":""), (st_no>0?known_service_types[st_no]:""), (st_no>1?"1":""),
276                host, (unsigned int)port);
277        //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Sending M-SEARCH response:\n%s", buf);
278        n = sendto(s, buf, l, 0,
279                   (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
280        if(n < 0)
281        {
282                DPRINTF(E_ERROR, L_SSDP, "sendto(udp): %s\n", strerror(errno));
283        }
284}
285
286static void
287SendSSDPNotifies(int s, const char * host, unsigned short port,
288                 unsigned int lifetime)
289{
290        struct sockaddr_in sockname;
291        int l, n, dup, i=0;
292        char bufr[512];
293
294        memset(&sockname, 0, sizeof(struct sockaddr_in));
295        sockname.sin_family = AF_INET;
296        sockname.sin_port = htons(SSDP_PORT);
297        sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
298
299        for( dup=0; dup<2; dup++ )
300        {
301                if( dup )
302                        _usleep(200000);
303                i=0;
304                while(known_service_types[i])
305                {
306                        l = snprintf(bufr, sizeof(bufr),
307                                        "NOTIFY * HTTP/1.1\r\n"
308                                        "HOST:%s:%d\r\n"
309                                        "CACHE-CONTROL:max-age=%u\r\n"
310                                        "LOCATION:http://%s:%d" ROOTDESC_PATH"\r\n"
311                                        "SERVER: " MINIDLNA_SERVER_STRING "\r\n"
312                                        "NT:%s%s\r\n"
313                                        "USN:%s%s%s%s\r\n"
314                                        "NTS:ssdp:alive\r\n"
315                                        "\r\n",
316                                        SSDP_MCAST_ADDR, SSDP_PORT,
317                                        lifetime,
318                                        host, port,
319                                        known_service_types[i], (i>1?"1":""),
320                                        uuidvalue, (i>0?"::":""), (i>0?known_service_types[i]:""), (i>1?"1":"") );
321                        if(l>=sizeof(bufr))
322                        {
323                                DPRINTF(E_WARN, L_SSDP, "SendSSDPNotifies(): truncated output\n");
324                                l = sizeof(bufr);
325                        }
326                        //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Sending NOTIFY:\n%s", bufr);
327                        n = sendto(s, bufr, l, 0,
328                                (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
329                        if(n < 0)
330                        {
331                                DPRINTF(E_ERROR, L_SSDP, "sendto(udp_notify=%d, %s): %s\n", s, host, strerror(errno));
332                        }
333                        i++;
334                }
335        }
336}
337
338void
339SendSSDPNotifies2(int * sockets,
340                  unsigned short port,
341                  unsigned int lifetime)
342/*SendSSDPNotifies2(int * sockets, struct lan_addr_s * lan_addr, int n_lan_addr,
343                  unsigned short port,
344                  unsigned int lifetime)*/
345{
346        int i;
347        DPRINTF(E_DEBUG, L_SSDP, "Sending SSDP notifies\n");
348        for(i=0; i<n_lan_addr; i++)
349        {
350                SendSSDPNotifies(sockets[i], lan_addr[i].str, port, lifetime);
351        }
352}
353
354void
355ParseUPnPClient(char *location)
356{
357        char buf[8192];
358        struct sockaddr_in dest;
359        int s, n, do_headers = 0, nread = 0;
360        struct timeval tv;
361        char *addr, *path, *port_str;
362        long port = 80;
363        char *off = NULL, *p;
364        int content_len = sizeof(buf);
365        struct NameValueParserData xml;
366        int client;
367        enum client_types type = 0;
368        uint32_t flags = 0;
369        char *model, *serial, *name;
370
371        if (strncmp(location, "http://", 7) != 0)
372                return;
373        path = location + 7;
374        port_str = strsep(&path, "/");
375        if (!path)
376                return;
377        addr = strsep(&port_str, ":");
378        if (port_str)
379        {
380                port = strtol(port_str, NULL, 10);
381                if (!port)
382                        port = 80;
383        }
384
385        memset(&dest, '\0', sizeof(dest));
386        if (!inet_aton(addr, &dest.sin_addr))
387                return;
388        /* Check if the client is already in cache */
389        dest.sin_family = AF_INET;
390        dest.sin_port = htons(port);
391
392        s = socket(PF_INET, SOCK_STREAM, 0);
393        if( s < 0 )
394                return;
395
396        tv.tv_sec = 0;
397        tv.tv_usec = 500000;
398        setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
399        setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
400
401        if( connect(s, (struct sockaddr*)&dest, sizeof(struct sockaddr_in)) < 0 )
402                goto close;
403
404        n = snprintf(buf, sizeof(buf), "GET /%s HTTP/1.0\r\n"
405                                       "HOST: %s:%ld\r\n\r\n",
406                                       path, addr, port);
407        if( write(s, buf, n) < 1 )
408                goto close;
409
410        while( (n = read(s, buf+nread, sizeof(buf)-nread-1)) > 0 )
411        {
412                nread += n;
413                buf[nread] = '\0';
414                n = nread;
415                p = buf;
416
417                while( !off && n-- > 0 )
418                {
419                        if(p[0]=='\r' && p[1]=='\n' && p[2]=='\r' && p[3]=='\n')
420                        {
421                                off = p + 4;
422                                do_headers = 1;
423                        }
424                        p++;
425                }
426                if( !off )
427                        continue;
428
429                if( do_headers )
430                {
431                        p = buf;
432                        if( strncmp(p, "HTTP/", 5) != 0 )
433                                goto close;
434                        while(*p != ' ' && *p != '\t') p++;
435                        /* If we don't get a 200 status, ignore it */
436                        if( strtol(p, NULL, 10) != 200 )
437                                goto close;
438                        if( (p = strcasestr(p, "Content-Length:")) )
439                                content_len = strtol(p+15, NULL, 10);
440                        do_headers = 0;
441                }
442                if( buf + nread - off >= content_len )
443                        break;
444        }
445close:
446        close(s);
447        if( !off )
448                return;
449        nread -= off - buf;
450        ParseNameValue(off, nread, &xml);
451        model = GetValueFromNameValueList(&xml, "modelName");
452        serial = GetValueFromNameValueList(&xml, "serialNumber");
453        name = GetValueFromNameValueList(&xml, "friendlyName");
454        if( model )
455        {
456                DPRINTF(E_DEBUG, L_SSDP, "Model: %s\n", model);
457                if( strstr(model, "Roku SoundBridge") )
458                {
459                        type = ERokuSoundBridge;
460                        flags |= FLAG_MS_PFS;
461                        flags |= FLAG_AUDIO_ONLY;
462                        flags |= FLAG_MIME_WAV_WAV;
463                }
464                else if( strcmp(model, "Samsung DTV DMR") == 0 && serial )
465                {
466                        DPRINTF(E_DEBUG, L_SSDP, "Serial: %s\n", serial);
467                        /* The Series B I saw was 20081224DMR.  Series A should be older than that. */
468                        if( atoi(serial) > 20081200 )
469                        {
470                                type = ESamsungSeriesB;
471                                flags |= FLAG_SAMSUNG;
472                                flags |= FLAG_DLNA;
473                                flags |= FLAG_NO_RESIZE;
474                        }
475                }
476                else
477                {
478                        if( name && (strcmp(name, "marantz DMP") == 0) )
479                        {
480                                type = EMarantzDMP;
481                                flags |= FLAG_DLNA;
482                                flags |= FLAG_MIME_WAV_WAV;
483                        }
484                }
485        }
486        ClearNameValueList(&xml);
487        if( !type )
488                return;
489        /* Add this client to the cache if it's not there already. */
490        client = SearchClientCache(dest.sin_addr, 1);
491        if( client < 0 )
492        {
493                for( client=0; client<CLIENT_CACHE_SLOTS; client++ )
494                {
495                        if( clients[client].addr.s_addr )
496                                continue;
497                        get_remote_mac(dest.sin_addr, clients[client].mac);
498                        clients[client].addr = dest.sin_addr;
499                        DPRINTF(E_DEBUG, L_SSDP, "Added client [%d/%s/%02X:%02X:%02X:%02X:%02X:%02X] to cache slot %d.\n",
500                                                 type, inet_ntoa(clients[client].addr),
501                                                 clients[client].mac[0], clients[client].mac[1], clients[client].mac[2],
502                                                 clients[client].mac[3], clients[client].mac[4], clients[client].mac[5], client);
503                        break;
504                }
505        }
506        clients[client].type = type;
507        clients[client].flags = flags;
508        clients[client].age = time(NULL);
509}
510
511/* ProcessSSDPRequest()
512 * process SSDP M-SEARCH requests and responds to them */
513void
514ProcessSSDPRequest(int s, unsigned short port)
515/*ProcessSSDPRequest(int s, struct lan_addr_s * lan_addr, int n_lan_addr,
516                   unsigned short port)*/
517{
518        int n;
519        char bufr[1500];
520        socklen_t len_r;
521        struct sockaddr_in sendername;
522        int i;
523        char *st = NULL, *mx = NULL, *man = NULL, *mx_end = NULL;
524        int man_len = 0;
525        len_r = sizeof(struct sockaddr_in);
526
527        n = recvfrom(s, bufr, sizeof(bufr)-1, 0,
528                     (struct sockaddr *)&sendername, &len_r);
529        if(n < 0)
530        {
531                DPRINTF(E_ERROR, L_SSDP, "recvfrom(udp): %s\n", strerror(errno));
532                return;
533        }
534        bufr[n] = '\0';
535
536        if(memcmp(bufr, "NOTIFY", 6) == 0)
537        {
538                char *loc = NULL, *srv = NULL, *nts = NULL, *nt = NULL;
539                int loc_len = 0;
540                //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Received SSDP notify:\n%.*s", n, bufr);
541                for(i=0; i < n; i++)
542                {
543                        if( bufr[i] == '*' )
544                                break;
545                }
546                if( !strcasestr(bufr+i, "HTTP/1.1") )
547                {
548                        return;
549                }
550                while(i < n)
551                {
552                        while((i < n - 2) && (bufr[i] != '\r' || bufr[i+1] != '\n'))
553                                i++;
554                        i += 2;
555                        if(strncasecmp(bufr+i, "SERVER:", 7) == 0)
556                        {
557                                srv = bufr+i+7;
558                                while(*srv == ' ' || *srv == '\t') srv++;
559                        }
560                        else if(strncasecmp(bufr+i, "LOCATION:", 9) == 0)
561                        {
562                                loc = bufr+i+9;
563                                while(*loc == ' ' || *loc == '\t') loc++;
564                                while(loc[loc_len]!='\r' && loc[loc_len]!='\n') loc_len++;
565                        }
566                        else if(strncasecmp(bufr+i, "NTS:", 4) == 0)
567                        {
568                                nts = bufr+i+4;
569                                while(*nts == ' ' || *nts == '\t') nts++;
570                        }
571                        else if(strncasecmp(bufr+i, "NT:", 3) == 0)
572                        {
573                                nt = bufr+i+3;
574                                while(*nt == ' ' || *nt == '\t') nt++;
575                        }
576                }
577                if( !loc || !srv || !nt || !nts || (strncmp(nts, "ssdp:alive", 10) != 0) ||
578                    (strncmp(nt, "urn:schemas-upnp-org:device:MediaRenderer", 41) != 0) )
579                {
580                        return;
581                }
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                        i = SearchClientCache(sendername.sin_addr, 1);
589                        if( i >= 0 )
590                        {
591                                if( clients[i].type < EStandardDLNA150 &&
592                                    clients[i].type != ESamsungSeriesA )
593                                {
594                                        clients[i].age = time(NULL);
595                                        return;
596                                }
597                        }
598                        ParseUPnPClient(loc);
599                }
600                return;
601        }
602        else if(memcmp(bufr, "M-SEARCH", 8) == 0)
603        {
604                int st_len = 0, mx_len = 0, mx_val = 0;
605                //DPRINTF(E_DEBUG, L_SSDP, "Received SSDP request:\n%.*s\n", n, bufr);
606                for(i=0; i < n; i++)
607                {
608                        if( bufr[i] == '*' )
609                                break;
610                }
611                if( !strcasestr(bufr+i, "HTTP/1.1") )
612                {
613                        return;
614                }
615                while(i < n)
616                {
617                        while((i < n - 2) && (bufr[i] != '\r' || bufr[i+1] != '\n'))
618                                i++;
619                        i += 2;
620                        if(strncasecmp(bufr+i, "ST:", 3) == 0)
621                        {
622                                st = bufr+i+3;
623                                st_len = 0;
624                                while(*st == ' ' || *st == '\t') st++;
625                                while(st[st_len]!='\r' && st[st_len]!='\n') st_len++;
626                        }
627                        else if(strncasecmp(bufr+i, "MX:", 3) == 0)
628                        {
629                                mx = bufr+i+3;
630                                mx_len = 0;
631                                while(*mx == ' ' || *mx == '\t') mx++;
632                                while(mx[mx_len]!='\r' && mx[mx_len]!='\n') mx_len++;
633                                mx_val = strtol(mx, &mx_end, 10);
634                        }
635                        else if(strncasecmp(bufr+i, "MAN:", 4) == 0)
636                        {
637                                man = bufr+i+4;
638                                man_len = 0;
639                                while(*man == ' ' || *man == '\t') man++;
640                                while(man[man_len]!='\r' && man[man_len]!='\n') man_len++;
641                        }
642                }
643                /*DPRINTF(E_INFO, L_SSDP, "SSDP M-SEARCH packet received from %s:%d\n",
644                   inet_ntoa(sendername.sin_addr),
645                   ntohs(sendername.sin_port) );*/
646                if( GETFLAG(DLNA_STRICT_MASK) && (ntohs(sendername.sin_port) <= 1024 || ntohs(sendername.sin_port) == 1900) )
647                {
648                        DPRINTF(E_INFO, L_SSDP, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad source port %d]\n",
649                           inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port));
650                }
651                else if( !man || (strncmp(man, "\"ssdp:discover\"", 15) != 0) )
652                {
653                        DPRINTF(E_INFO, L_SSDP, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad MAN header %.*s]\n",
654                           inet_ntoa(sendername.sin_addr), man_len, man);
655                }
656                else if( !mx || mx == mx_end || mx_val < 0 ) {
657                        DPRINTF(E_INFO, L_SSDP, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad MX header %.*s]\n",
658                           inet_ntoa(sendername.sin_addr), mx_len, mx);
659                }
660                else if( st && (st_len > 0) )
661                {
662                        int l;
663                        int lan_addr_index = 0;
664                        /* find in which sub network the client is */
665                        for(i = 0; i<n_lan_addr; i++)
666                        {
667                                if( (sendername.sin_addr.s_addr & lan_addr[i].mask.s_addr)
668                                   == (lan_addr[i].addr.s_addr & lan_addr[i].mask.s_addr))
669                                {
670                                        lan_addr_index = i;
671                                        break;
672                                }
673                        }
674                        if( i == n_lan_addr )
675                        {
676                                DPRINTF(E_DEBUG, L_SSDP, "Ignoring SSDP M-SEARCH on other interface [%s]\n",
677                                        inet_ntoa(sendername.sin_addr));
678                                return;
679                        }
680                        DPRINTF(E_INFO, L_SSDP, "SSDP M-SEARCH from %s:%d ST: %.*s, MX: %.*s, MAN: %.*s\n",
681                           inet_ntoa(sendername.sin_addr),
682                           ntohs(sendername.sin_port),
683                           st_len, st, mx_len, mx, man_len, man);
684                        /* Responds to request with a device as ST header */
685                        for(i = 0; known_service_types[i]; i++)
686                        {
687                                l = strlen(known_service_types[i]);
688                                if(l<=st_len && (0 == memcmp(st, known_service_types[i], l)))
689                                {
690                                        /* Check version number - must always be 1 currently. */
691                                        if( (st[st_len-2] == ':') && (atoi(st+st_len-1) != 1) )
692                                                break;
693                                        _usleep(random()>>20);
694                                        SendSSDPAnnounce2(s, sendername,
695                                                          i,
696                                                          lan_addr[lan_addr_index].str, port);
697                                        break;
698                                }
699                        }
700                        /* Responds to request with ST: ssdp:all */
701                        /* strlen("ssdp:all") == 8 */
702                        if(st_len==8 && (0 == memcmp(st, "ssdp:all", 8)))
703                        {
704                                for(i=0; known_service_types[i]; i++)
705                                {
706                                        l = (int)strlen(known_service_types[i]);
707                                        SendSSDPAnnounce2(s, sendername,
708                                                          i,
709                                                          lan_addr[lan_addr_index].str, port);
710                                }
711                        }
712                }
713                else
714                {
715                        DPRINTF(E_INFO, L_SSDP, "Invalid SSDP M-SEARCH from %s:%d\n",
716                           inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port));
717                }
718        }
719        else
720        {
721                DPRINTF(E_WARN, L_SSDP, "Unknown udp packet received from %s:%d\n",
722                       inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port));
723        }
724}
725
726/* This will broadcast ssdp:byebye notifications to inform
727 * the network that UPnP is going down. */
728int
729SendSSDPGoodbye(int * sockets, int n_sockets)
730{
731        struct sockaddr_in sockname;
732        int n, l;
733        int i, j;
734        char bufr[512];
735
736        memset(&sockname, 0, sizeof(struct sockaddr_in));
737        sockname.sin_family = AF_INET;
738        sockname.sin_port = htons(SSDP_PORT);
739        sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
740
741        for(j=0; j<n_sockets; j++)
742        {
743                for(i=0; known_service_types[i]; i++)
744                {
745                        l = snprintf(bufr, sizeof(bufr),
746                                     "NOTIFY * HTTP/1.1\r\n"
747                                     "HOST:%s:%d\r\n"
748                                     "NT:%s%s\r\n"
749                                     "USN:%s%s%s%s\r\n"
750                                     "NTS:ssdp:byebye\r\n"
751                                     "\r\n",
752                                     SSDP_MCAST_ADDR, SSDP_PORT,
753                                     known_service_types[i], (i>1?"1":""),
754                                     uuidvalue, (i>0?"::":""), (i>0?known_service_types[i]:""), (i>1?"1":"") );
755                        //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Sending NOTIFY:\n%s", bufr);
756                        n = sendto(sockets[j], bufr, l, 0,
757                                   (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
758                        if(n < 0)
759                        {
760                                DPRINTF(E_ERROR, L_SSDP, "sendto(udp_shutdown=%d): %s\n", sockets[j], strerror(errno));
761                                return -1;
762                        }
763                }
764        }
765        return 0;
766}
767
768/* SubmitServicesToMiniSSDPD() :
769 * register services offered by MiniUPnPd to a running instance of
770 * MiniSSDPd */
771int
772SubmitServicesToMiniSSDPD(const char * host, unsigned short port) {
773        struct sockaddr_un addr;
774        int s;
775        unsigned char buffer[2048];
776        char strbuf[256];
777        unsigned char * p;
778        int i, l;
779
780        s = socket(AF_UNIX, SOCK_STREAM, 0);
781        if(s < 0) {
782                DPRINTF(E_ERROR, L_SSDP, "socket(unix): %s", strerror(errno));
783                return -1;
784        }
785        addr.sun_family = AF_UNIX;
786        strncpy(addr.sun_path, minissdpdsocketpath, sizeof(addr.sun_path));
787        if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) {
788                DPRINTF(E_ERROR, L_SSDP, "connect(\"%s\"): %s",
789                        minissdpdsocketpath, strerror(errno));
790                return -1;
791        }
792        for(i = 0; known_service_types[i]; i++) {
793                buffer[0] = 4;
794                p = buffer + 1;
795                l = (int)strlen(known_service_types[i]);
796                if(i > 0)
797                        l++;
798                CODELENGTH(l, p);
799                memcpy(p, known_service_types[i], l);
800                if(i > 0)
801                        p[l-1] = '1';
802                p += l;
803                l = snprintf(strbuf, sizeof(strbuf), "%s::%s%s",
804                             uuidvalue, known_service_types[i], (i==0)?"":"1");
805                CODELENGTH(l, p);
806                memcpy(p, strbuf, l);
807                p += l;
808                l = (int)strlen(MINIDLNA_SERVER_STRING);
809                CODELENGTH(l, p);
810                memcpy(p, MINIDLNA_SERVER_STRING, l);
811                p += l;
812                l = snprintf(strbuf, sizeof(strbuf), "http://%s:%u" ROOTDESC_PATH,
813                             host, (unsigned int)port);
814                CODELENGTH(l, p);
815                memcpy(p, strbuf, l);
816                p += l;
817                if(write(s, buffer, p - buffer) < 0) {
818                        DPRINTF(E_ERROR, L_SSDP, "write(): %s", strerror(errno));
819                        return -1;
820                }
821        }
822        close(s);
823        return 0;
824}
825
Note: See TracBrowser for help on using the repository browser.