Updates to Tomato RAF including NGINX && PHP
[tomato.git] / release / src / router / minidlna / minissdp.c
blob81ae23e4a1ebc9dc5d2c2ab989b696838c5edcd9
1 /* MiniDLNA media server
2 * This file is part of MiniDLNA.
4 * The code herein is based on the MiniUPnP Project.
5 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
7 * Copyright (c) 2006, Thomas Bernard
8 * All rights reserved.
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.
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.
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <ctype.h>
36 #include <unistd.h>
37 #include <sys/socket.h>
38 #include <sys/un.h>
39 #include <netinet/in.h>
40 #include <arpa/inet.h>
41 #include <errno.h>
43 #include "config.h"
44 #include "upnpdescstrings.h"
45 #include "minidlnapath.h"
46 #include "upnphttp.h"
47 #include "upnpglobalvars.h"
48 #include "upnpreplyparse.h"
49 #include "getifaddr.h"
50 #include "minissdp.h"
51 #include "codelength.h"
52 #include "utils.h"
53 #include "log.h"
55 /* SSDP ip/port */
56 #define SSDP_PORT (1900)
57 #define SSDP_MCAST_ADDR ("239.255.255.250")
59 static int
60 AddMulticastMembership(int s, in_addr_t ifaddr)
62 struct ip_mreq imr; /* Ip multicast membership */
64 /* setting up imr structure */
65 imr.imr_multiaddr.s_addr = inet_addr(SSDP_MCAST_ADDR);
66 /*imr.imr_interface.s_addr = htonl(INADDR_ANY);*/
67 imr.imr_interface.s_addr = ifaddr; /*inet_addr(ifaddr);*/
69 if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&imr, sizeof(struct ip_mreq)) < 0)
71 DPRINTF(E_ERROR, L_SSDP, "setsockopt(udp, IP_ADD_MEMBERSHIP): %s\n", strerror(errno));
72 return -1;
75 return 0;
78 /* Open and configure the socket listening for
79 * SSDP udp packets sent on 239.255.255.250 port 1900 */
80 int
81 OpenAndConfSSDPReceiveSocket()
83 int s;
84 int i = 1;
85 struct sockaddr_in sockname;
87 if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
89 DPRINTF(E_ERROR, L_SSDP, "socket(udp): %s\n", strerror(errno));
90 return -1;
93 if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0)
95 DPRINTF(E_WARN, L_SSDP, "setsockopt(udp, SO_REUSEADDR): %s\n", strerror(errno));
98 memset(&sockname, 0, sizeof(struct sockaddr_in));
99 sockname.sin_family = AF_INET;
100 sockname.sin_port = htons(SSDP_PORT);
101 /* NOTE : it seems it doesnt work when binding on the specific address */
102 /*sockname.sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);*/
103 sockname.sin_addr.s_addr = htonl(INADDR_ANY);
104 /*sockname.sin_addr.s_addr = inet_addr(ifaddr);*/
106 if(bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
108 DPRINTF(E_ERROR, L_SSDP, "bind(udp): %s\n", strerror(errno));
109 close(s);
110 return -1;
113 i = n_lan_addr;
114 while(i>0)
116 i--;
117 if(AddMulticastMembership(s, lan_addr[i].addr.s_addr) < 0)
119 DPRINTF(E_WARN, L_SSDP,
120 "Failed to add multicast membership for address %s\n",
121 lan_addr[i].str );
125 return s;
128 /* open the UDP socket used to send SSDP notifications to
129 * the multicast group reserved for them */
130 static int
131 OpenAndConfSSDPNotifySocket(in_addr_t addr)
133 int s;
134 unsigned char loopchar = 0;
135 int bcast = 1;
136 uint8_t ttl = 4;
137 struct in_addr mc_if;
138 struct sockaddr_in sockname;
140 if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
142 DPRINTF(E_ERROR, L_SSDP, "socket(udp_notify): %s\n", strerror(errno));
143 return -1;
146 mc_if.s_addr = addr; /*inet_addr(addr);*/
148 if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loopchar, sizeof(loopchar)) < 0)
150 DPRINTF(E_ERROR, L_SSDP, "setsockopt(udp_notify, IP_MULTICAST_LOOP): %s\n", strerror(errno));
151 close(s);
152 return -1;
155 if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&mc_if, sizeof(mc_if)) < 0)
157 DPRINTF(E_ERROR, L_SSDP, "setsockopt(udp_notify, IP_MULTICAST_IF): %s\n", strerror(errno));
158 close(s);
159 return -1;
162 setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
164 if(setsockopt(s, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bcast)) < 0)
166 DPRINTF(E_ERROR, L_SSDP, "setsockopt(udp_notify, SO_BROADCAST): %s\n", strerror(errno));
167 close(s);
168 return -1;
171 memset(&sockname, 0, sizeof(struct sockaddr_in));
172 sockname.sin_family = AF_INET;
173 sockname.sin_addr.s_addr = addr; /*inet_addr(addr);*/
175 if (bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
177 DPRINTF(E_ERROR, L_SSDP, "bind(udp_notify): %s\n", strerror(errno));
178 close(s);
179 return -1;
182 return s;
186 OpenAndConfSSDPNotifySockets(int * sockets)
188 int i, j;
189 for(i=0; i<n_lan_addr; i++)
191 sockets[i] = OpenAndConfSSDPNotifySocket(lan_addr[i].addr.s_addr);
192 if(sockets[i] < 0)
194 for(j=0; j<i; j++)
196 close(sockets[j]);
197 sockets[j] = -1;
199 return -1;
202 return 0;
206 * response from a LiveBox (Wanadoo)
207 HTTP/1.1 200 OK
208 CACHE-CONTROL: max-age=1800
209 DATE: Thu, 01 Jan 1970 04:03:23 GMT
210 EXT:
211 LOCATION: http://192.168.0.1:49152/gatedesc.xml
212 SERVER: Linux/2.4.17, UPnP/1.0, Intel SDK for UPnP devices /1.2
213 ST: upnp:rootdevice
214 USN: uuid:75802409-bccb-40e7-8e6c-fa095ecce13e::upnp:rootdevice
216 * response from a Linksys 802.11b :
217 HTTP/1.1 200 OK
218 Cache-Control:max-age=120
219 Location:http://192.168.5.1:5678/rootDesc.xml
220 Server:NT/5.0 UPnP/1.0
221 ST:upnp:rootdevice
222 USN:uuid:upnp-InternetGatewayDevice-1_0-0090a2777777::upnp:rootdevice
223 EXT:
226 static const char * const known_service_types[] =
228 uuidvalue,
229 "upnp:rootdevice",
230 "urn:schemas-upnp-org:device:MediaServer:",
231 "urn:schemas-upnp-org:service:ContentDirectory:",
232 "urn:schemas-upnp-org:service:ConnectionManager:",
233 "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:",
237 static void
238 _usleep(long usecs)
240 struct timespec sleep_time;
242 sleep_time.tv_sec = 0;
243 sleep_time.tv_nsec = usecs * 1000;
244 nanosleep(&sleep_time, NULL);
247 /* not really an SSDP "announce" as it is the response
248 * to a SSDP "M-SEARCH" */
249 static void
250 SendSSDPAnnounce2(int s, struct sockaddr_in sockname, int st_no,
251 const char * host, unsigned short port)
253 int l, n;
254 char buf[512];
256 * follow guideline from document "UPnP Device Architecture 1.0"
257 * uppercase is recommended.
258 * DATE: is recommended
259 * SERVER: OS/ver UPnP/1.0 minidlna/1.0
260 * - check what to put in the 'Cache-Control' header
261 * */
262 char szTime[30];
263 time_t tTime = time(NULL);
264 strftime(szTime, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&tTime));
266 l = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\n"
267 "CACHE-CONTROL: max-age=%u\r\n"
268 "DATE: %s\r\n"
269 "ST: %s%s\r\n"
270 "USN: %s%s%s%s\r\n"
271 "EXT:\r\n"
272 "SERVER: " MINIDLNA_SERVER_STRING "\r\n"
273 "LOCATION: http://%s:%u" ROOTDESC_PATH "\r\n"
274 "Content-Length: 0\r\n"
275 "\r\n",
276 (runtime_vars.notify_interval<<1)+10,
277 szTime,
278 known_service_types[st_no], (st_no>1?"1":""),
279 uuidvalue, (st_no>0?"::":""), (st_no>0?known_service_types[st_no]:""), (st_no>1?"1":""),
280 host, (unsigned int)port);
281 //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Sending M-SEARCH response:\n%s", buf);
282 n = sendto(s, buf, l, 0,
283 (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
284 if(n < 0)
286 DPRINTF(E_ERROR, L_SSDP, "sendto(udp): %s\n", strerror(errno));
290 static void
291 SendSSDPNotifies(int s, const char * host, unsigned short port,
292 unsigned int lifetime)
294 struct sockaddr_in sockname;
295 int l, n, dup, i=0;
296 char bufr[512];
298 memset(&sockname, 0, sizeof(struct sockaddr_in));
299 sockname.sin_family = AF_INET;
300 sockname.sin_port = htons(SSDP_PORT);
301 sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
303 for( dup=0; dup<2; dup++ )
305 if( dup )
306 _usleep(200000);
307 i=0;
308 while(known_service_types[i])
310 l = snprintf(bufr, sizeof(bufr),
311 "NOTIFY * HTTP/1.1\r\n"
312 "HOST:%s:%d\r\n"
313 "CACHE-CONTROL:max-age=%u\r\n"
314 "LOCATION:http://%s:%d" ROOTDESC_PATH"\r\n"
315 "SERVER: " MINIDLNA_SERVER_STRING "\r\n"
316 "NT:%s%s\r\n"
317 "USN:%s%s%s%s\r\n"
318 "NTS:ssdp:alive\r\n"
319 "\r\n",
320 SSDP_MCAST_ADDR, SSDP_PORT,
321 lifetime,
322 host, port,
323 known_service_types[i], (i>1?"1":""),
324 uuidvalue, (i>0?"::":""), (i>0?known_service_types[i]:""), (i>1?"1":"") );
325 if(l>=sizeof(bufr))
327 DPRINTF(E_WARN, L_SSDP, "SendSSDPNotifies(): truncated output\n");
328 l = sizeof(bufr);
330 //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Sending NOTIFY:\n%s", bufr);
331 n = sendto(s, bufr, l, 0,
332 (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
333 if(n < 0)
335 DPRINTF(E_ERROR, L_SSDP, "sendto(udp_notify=%d, %s): %s\n", s, host, strerror(errno));
337 i++;
342 void
343 SendSSDPNotifies2(int * sockets,
344 unsigned short port,
345 unsigned int lifetime)
346 /*SendSSDPNotifies2(int * sockets, struct lan_addr_s * lan_addr, int n_lan_addr,
347 unsigned short port,
348 unsigned int lifetime)*/
350 int i;
351 DPRINTF(E_DEBUG, L_SSDP, "Sending SSDP notifies\n");
352 for(i=0; i<n_lan_addr; i++)
354 SendSSDPNotifies(sockets[i], lan_addr[i].str, port, lifetime);
358 void
359 ParseUPnPClient(char *location)
361 char buf[8192];
362 struct sockaddr_in dest;
363 int s, n, do_headers = 0, nread = 0;
364 struct timeval tv;
365 char *addr, *path, *port_str;
366 long port = 80;
367 char *off = NULL, *p;
368 int content_len = sizeof(buf);
369 struct NameValueParserData xml;
370 int client;
371 enum client_types type = 0;
372 uint32_t flags = 0;
373 char *model, *serial, *name;
375 if (strncmp(location, "http://", 7) != 0)
376 return;
377 path = location + 7;
378 port_str = strsep(&path, "/");
379 if (!path)
380 return;
381 addr = strsep(&port_str, ":");
382 if (port_str)
384 port = strtol(port_str, NULL, 10);
385 if (!port)
386 port = 80;
389 memset(&dest, '\0', sizeof(dest));
390 if (!inet_aton(addr, &dest.sin_addr))
391 return;
392 /* Check if the client is already in cache */
393 dest.sin_family = AF_INET;
394 dest.sin_port = htons(port);
396 s = socket(PF_INET, SOCK_STREAM, 0);
397 if( s < 0 )
398 return;
400 tv.tv_sec = 0;
401 tv.tv_usec = 500000;
402 setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
403 setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
405 if( connect(s, (struct sockaddr*)&dest, sizeof(struct sockaddr_in)) < 0 )
406 goto close;
408 n = snprintf(buf, sizeof(buf), "GET /%s HTTP/1.0\r\n"
409 "HOST: %s:%ld\r\n\r\n",
410 path, addr, port);
411 if( write(s, buf, n) < 1 )
412 goto close;
414 while( (n = read(s, buf+nread, sizeof(buf)-nread-1)) > 0 )
416 nread += n;
417 buf[nread] = '\0';
418 n = nread;
419 p = buf;
421 while( !off && n-- > 0 )
423 if(p[0]=='\r' && p[1]=='\n' && p[2]=='\r' && p[3]=='\n')
425 off = p + 4;
426 do_headers = 1;
428 p++;
430 if( !off )
431 continue;
433 if( do_headers )
435 p = buf;
436 if( strncmp(p, "HTTP/", 5) != 0 )
437 goto close;
438 while(*p != ' ' && *p != '\t') p++;
439 /* If we don't get a 200 status, ignore it */
440 if( strtol(p, NULL, 10) != 200 )
441 goto close;
442 if( (p = strcasestr(p, "Content-Length:")) )
443 content_len = strtol(p+15, NULL, 10);
444 do_headers = 0;
446 if( buf + nread - off >= content_len )
447 break;
449 close:
450 close(s);
451 if( !off )
452 return;
453 nread -= off - buf;
454 ParseNameValue(off, nread, &xml);
455 model = GetValueFromNameValueList(&xml, "modelName");
456 serial = GetValueFromNameValueList(&xml, "serialNumber");
457 name = GetValueFromNameValueList(&xml, "friendlyName");
458 if( model )
460 DPRINTF(E_DEBUG, L_SSDP, "Model: %s\n", model);
461 if( strstr(model, "Roku SoundBridge") )
463 type = ERokuSoundBridge;
464 flags |= FLAG_MS_PFS;
465 flags |= FLAG_AUDIO_ONLY;
466 flags |= FLAG_MIME_WAV_WAV;
468 else if( strcmp(model, "Samsung DTV DMR") == 0 && serial )
470 DPRINTF(E_DEBUG, L_SSDP, "Serial: %s\n", serial);
471 /* The Series B I saw was 20081224DMR. Series A should be older than that. */
472 if( atoi(serial) > 20081200 )
474 type = ESamsungSeriesB;
475 flags |= FLAG_SAMSUNG;
476 flags |= FLAG_DLNA;
477 flags |= FLAG_NO_RESIZE;
480 else
482 if( name && (strcmp(name, "marantz DMP") == 0) )
484 type = EMarantzDMP;
485 flags |= FLAG_DLNA;
486 flags |= FLAG_MIME_WAV_WAV;
490 ClearNameValueList(&xml);
491 if( !type )
492 return;
493 /* Add this client to the cache if it's not there already. */
494 client = SearchClientCache(dest.sin_addr, 1);
495 if( client < 0 )
497 for( client=0; client<CLIENT_CACHE_SLOTS; client++ )
499 if( clients[client].addr.s_addr )
500 continue;
501 get_remote_mac(dest.sin_addr, clients[client].mac);
502 clients[client].addr = dest.sin_addr;
503 DPRINTF(E_DEBUG, L_SSDP, "Added client [%d/%s/%02X:%02X:%02X:%02X:%02X:%02X] to cache slot %d.\n",
504 type, inet_ntoa(clients[client].addr),
505 clients[client].mac[0], clients[client].mac[1], clients[client].mac[2],
506 clients[client].mac[3], clients[client].mac[4], clients[client].mac[5], client);
507 break;
510 clients[client].type = type;
511 clients[client].flags = flags;
512 clients[client].age = time(NULL);
515 /* ProcessSSDPRequest()
516 * process SSDP M-SEARCH requests and responds to them */
517 void
518 ProcessSSDPRequest(int s, unsigned short port)
520 int n;
521 char bufr[1500];
522 socklen_t len_r;
523 struct sockaddr_in sendername;
524 int i;
525 char *st = NULL, *mx = NULL, *man = NULL, *mx_end = NULL;
526 int man_len = 0;
527 len_r = sizeof(struct sockaddr_in);
529 n = recvfrom(s, bufr, sizeof(bufr)-1, 0,
530 (struct sockaddr *)&sendername, &len_r);
531 if(n < 0)
533 DPRINTF(E_ERROR, L_SSDP, "recvfrom(udp): %s\n", strerror(errno));
534 return;
536 bufr[n] = '\0';
538 if(memcmp(bufr, "NOTIFY", 6) == 0)
540 char *loc = NULL, *srv = NULL, *nts = NULL, *nt = NULL;
541 int loc_len = 0;
542 //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Received SSDP notify:\n%.*s", n, bufr);
543 for(i=0; i < n; i++)
545 if( bufr[i] == '*' )
546 break;
548 if( !strcasestrc(bufr+i, "HTTP/1.1", '\r') )
549 return;
550 while(i < n)
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)
557 srv = bufr+i+7;
558 while(*srv == ' ' || *srv == '\t') srv++;
560 else if(strncasecmp(bufr+i, "LOCATION:", 9) == 0)
562 loc = bufr+i+9;
563 while(*loc == ' ' || *loc == '\t') loc++;
564 while(loc[loc_len]!='\r' && loc[loc_len]!='\n') loc_len++;
566 else if(strncasecmp(bufr+i, "NTS:", 4) == 0)
568 nts = bufr+i+4;
569 while(*nts == ' ' || *nts == '\t') nts++;
571 else if(strncasecmp(bufr+i, "NT:", 3) == 0)
573 nt = bufr+i+3;
574 while(*nt == ' ' || *nt == '\t') nt++;
577 if( !loc || !srv || !nt || !nts || (strncmp(nts, "ssdp:alive", 10) != 0) ||
578 (strncmp(nt, "urn:schemas-upnp-org:device:MediaRenderer", 41) != 0) )
580 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 */
587 /* Check if the client is already in cache */
588 i = SearchClientCache(sendername.sin_addr, 1);
589 if( i >= 0 )
591 if( clients[i].type < EStandardDLNA150 &&
592 clients[i].type != ESamsungSeriesA )
594 clients[i].age = time(NULL);
595 return;
598 ParseUPnPClient(loc);
601 else if(memcmp(bufr, "M-SEARCH", 8) == 0)
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++)
607 if( bufr[i] == '*' )
608 break;
610 if( !strcasestrc(bufr+i, "HTTP/1.1", '\r') )
611 return;
612 while(i < n)
614 while((i < n - 2) && (bufr[i] != '\r' || bufr[i+1] != '\n'))
615 i++;
616 i += 2;
617 if(strncasecmp(bufr+i, "ST:", 3) == 0)
619 st = bufr+i+3;
620 st_len = 0;
621 while(*st == ' ' || *st == '\t') st++;
622 while(st[st_len]!='\r' && st[st_len]!='\n') st_len++;
624 else if(strncasecmp(bufr+i, "MX:", 3) == 0)
626 mx = bufr+i+3;
627 mx_len = 0;
628 while(*mx == ' ' || *mx == '\t') mx++;
629 while(mx[mx_len]!='\r' && mx[mx_len]!='\n') mx_len++;
630 mx_val = strtol(mx, &mx_end, 10);
632 else if(strncasecmp(bufr+i, "MAN:", 4) == 0)
634 man = bufr+i+4;
635 man_len = 0;
636 while(*man == ' ' || *man == '\t') man++;
637 while(man[man_len]!='\r' && man[man_len]!='\n') man_len++;
640 /*DPRINTF(E_INFO, L_SSDP, "SSDP M-SEARCH packet received from %s:%d\n",
641 inet_ntoa(sendername.sin_addr),
642 ntohs(sendername.sin_port) );*/
643 if( GETFLAG(DLNA_STRICT_MASK) && (ntohs(sendername.sin_port) <= 1024 || ntohs(sendername.sin_port) == 1900) )
645 DPRINTF(E_INFO, L_SSDP, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad source port %d]\n",
646 inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port));
648 else if( !man || (strncmp(man, "\"ssdp:discover\"", 15) != 0) )
650 DPRINTF(E_INFO, L_SSDP, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad %s header '%.*s']\n",
651 inet_ntoa(sendername.sin_addr), "MAN", man_len, man);
653 else if( !mx || mx == mx_end || mx_val < 0 ) {
654 DPRINTF(E_INFO, L_SSDP, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad %s header '%.*s']\n",
655 inet_ntoa(sendername.sin_addr), "MX", mx_len, mx);
657 else if( st && (st_len > 0) )
659 int l;
660 int lan_addr_index = 0;
661 /* find in which sub network the client is */
662 for(i = 0; i<n_lan_addr; i++)
664 if( (sendername.sin_addr.s_addr & lan_addr[i].mask.s_addr)
665 == (lan_addr[i].addr.s_addr & lan_addr[i].mask.s_addr))
667 lan_addr_index = i;
668 break;
671 if( i == n_lan_addr )
673 DPRINTF(E_DEBUG, L_SSDP, "Ignoring SSDP M-SEARCH on other interface [%s]\n",
674 inet_ntoa(sendername.sin_addr));
675 return;
677 DPRINTF(E_INFO, L_SSDP, "SSDP M-SEARCH from %s:%d ST: %.*s, MX: %.*s, MAN: %.*s\n",
678 inet_ntoa(sendername.sin_addr),
679 ntohs(sendername.sin_port),
680 st_len, st, mx_len, mx, man_len, man);
681 /* Responds to request with a device as ST header */
682 for(i = 0; known_service_types[i]; i++)
684 l = strlen(known_service_types[i]);
685 if( l <= st_len && (memcmp(st, known_service_types[i], l) == 0))
687 if( st_len != l )
689 /* Check version number - must always be 1 currently. */
690 if( (st[l-1] == ':') && (st[l] == '1') )
691 l++;
692 while( l < st_len )
694 if( !isspace(st[l]) )
696 DPRINTF(E_DEBUG, L_SSDP, "Ignoring SSDP M-SEARCH with bad extra data [%s]\n",
697 inet_ntoa(sendername.sin_addr));
698 break;
700 l++;
702 if( l != st_len )
703 break;
705 _usleep(random()>>20);
706 SendSSDPAnnounce2(s, sendername,
708 lan_addr[lan_addr_index].str, port);
709 break;
712 /* Responds to request with ST: ssdp:all */
713 /* strlen("ssdp:all") == 8 */
714 if(st_len==8 && (0 == memcmp(st, "ssdp:all", 8)))
716 for(i=0; known_service_types[i]; i++)
718 l = (int)strlen(known_service_types[i]);
719 SendSSDPAnnounce2(s, sendername,
721 lan_addr[lan_addr_index].str, port);
725 else
727 DPRINTF(E_INFO, L_SSDP, "Invalid SSDP M-SEARCH from %s:%d\n",
728 inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port));
731 else
733 DPRINTF(E_WARN, L_SSDP, "Unknown udp packet received from %s:%d\n",
734 inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port));
738 /* This will broadcast ssdp:byebye notifications to inform
739 * the network that UPnP is going down. */
741 SendSSDPGoodbye(int * sockets, int n_sockets)
743 struct sockaddr_in sockname;
744 int n, l;
745 int i, j;
746 int dup, ret = 0;
747 char bufr[512];
749 memset(&sockname, 0, sizeof(struct sockaddr_in));
750 sockname.sin_family = AF_INET;
751 sockname.sin_port = htons(SSDP_PORT);
752 sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
754 for (dup = 0; dup < 2; dup++)
756 for(j=0; j<n_sockets; j++)
758 for(i=0; known_service_types[i]; i++)
760 l = snprintf(bufr, sizeof(bufr),
761 "NOTIFY * HTTP/1.1\r\n"
762 "HOST:%s:%d\r\n"
763 "NT:%s%s\r\n"
764 "USN:%s%s%s%s\r\n"
765 "NTS:ssdp:byebye\r\n"
766 "\r\n",
767 SSDP_MCAST_ADDR, SSDP_PORT,
768 known_service_types[i], (i>1?"1":""),
769 uuidvalue, (i>0?"::":""), (i>0?known_service_types[i]:""), (i>1?"1":"") );
770 //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Sending NOTIFY:\n%s", bufr);
771 n = sendto(sockets[j], bufr, l, 0,
772 (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
773 if(n < 0)
775 DPRINTF(E_ERROR, L_SSDP, "sendto(udp_shutdown=%d): %s\n", sockets[j], strerror(errno));
776 ret = -1;
777 break;
782 return ret;
785 /* SubmitServicesToMiniSSDPD() :
786 * register services offered by MiniUPnPd to a running instance of
787 * MiniSSDPd */
789 SubmitServicesToMiniSSDPD(const char * host, unsigned short port) {
790 struct sockaddr_un addr;
791 int s;
792 unsigned char buffer[2048];
793 char strbuf[256];
794 unsigned char * p;
795 int i, l;
797 s = socket(AF_UNIX, SOCK_STREAM, 0);
798 if(s < 0) {
799 DPRINTF(E_ERROR, L_SSDP, "socket(unix): %s", strerror(errno));
800 return -1;
802 addr.sun_family = AF_UNIX;
803 strncpy(addr.sun_path, minissdpdsocketpath, sizeof(addr.sun_path));
804 if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) {
805 DPRINTF(E_ERROR, L_SSDP, "connect(\"%s\"): %s",
806 minissdpdsocketpath, strerror(errno));
807 return -1;
809 for(i = 0; known_service_types[i]; i++) {
810 buffer[0] = 4;
811 p = buffer + 1;
812 l = (int)strlen(known_service_types[i]);
813 if(i > 0)
814 l++;
815 CODELENGTH(l, p);
816 memcpy(p, known_service_types[i], l);
817 if(i > 0)
818 p[l-1] = '1';
819 p += l;
820 l = snprintf(strbuf, sizeof(strbuf), "%s::%s%s",
821 uuidvalue, known_service_types[i], (i==0)?"":"1");
822 CODELENGTH(l, p);
823 memcpy(p, strbuf, l);
824 p += l;
825 l = (int)strlen(MINIDLNA_SERVER_STRING);
826 CODELENGTH(l, p);
827 memcpy(p, MINIDLNA_SERVER_STRING, l);
828 p += l;
829 l = snprintf(strbuf, sizeof(strbuf), "http://%s:%u" ROOTDESC_PATH,
830 host, (unsigned int)port);
831 CODELENGTH(l, p);
832 memcpy(p, strbuf, l);
833 p += l;
834 if(write(s, buffer, p - buffer) < 0) {
835 DPRINTF(E_ERROR, L_SSDP, "write(): %s", strerror(errno));
836 return -1;
839 close(s);
840 return 0;