MiniDLNA: update from 1.1.2 to 1.1.4
[tomato.git] / release / src-rt-6.x.4708 / router / minidlna / minissdp.c
blobba9a861bf24c24d5ce9e90567f61c5aed29ba6d5
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 "config.h"
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>
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, struct lan_addr_s *iface)
62 int ret;
63 #ifdef HAVE_STRUCT_IP_MREQN
64 struct ip_mreqn imr; /* Ip multicast membership */
65 /* setting up imr structure */
66 memset(&imr, '\0', sizeof(imr));
67 imr.imr_multiaddr.s_addr = inet_addr(SSDP_MCAST_ADDR);
68 imr.imr_ifindex = iface->ifindex;
69 #else
70 struct ip_mreq imr; /* Ip multicast membership */
71 /* setting up imr structure */
72 memset(&imr, '\0', sizeof(imr));
73 imr.imr_multiaddr.s_addr = inet_addr(SSDP_MCAST_ADDR);
74 imr.imr_interface.s_addr = iface->addr.s_addr;
75 #endif
76 ret = setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&imr, sizeof(imr));
77 if (ret < 0 && errno != EADDRINUSE)
79 DPRINTF(E_ERROR, L_SSDP, "setsockopt(udp, IP_ADD_MEMBERSHIP): %s\n",
80 strerror(errno));
81 return -1;
84 return 0;
87 /* Open and configure the socket listening for
88 * SSDP udp packets sent on 239.255.255.250 port 1900 */
89 int
90 OpenAndConfSSDPReceiveSocket(void)
92 int s;
93 int i = 1;
94 struct sockaddr_in sockname;
96 s = socket(PF_INET, SOCK_DGRAM, 0);
97 if (s < 0)
99 DPRINTF(E_ERROR, L_SSDP, "socket(udp): %s\n", strerror(errno));
100 return -1;
103 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0)
104 DPRINTF(E_WARN, L_SSDP, "setsockopt(udp, SO_REUSEADDR): %s\n", strerror(errno));
106 memset(&sockname, 0, sizeof(struct sockaddr_in));
107 sockname.sin_family = AF_INET;
108 sockname.sin_port = htons(SSDP_PORT);
109 /* NOTE : it seems it doesnt work when binding on the specific address */
110 sockname.sin_addr.s_addr = htonl(INADDR_ANY);
112 if (bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
114 DPRINTF(E_ERROR, L_SSDP, "bind(udp): %s\n", strerror(errno));
115 close(s);
116 return -1;
119 return s;
122 /* open the UDP socket used to send SSDP notifications to
123 * the multicast group reserved for them */
125 OpenAndConfSSDPNotifySocket(struct lan_addr_s *iface)
127 int s;
128 unsigned char loopchar = 0;
129 int bcast = 1;
130 uint8_t ttl = 4;
131 struct in_addr mc_if;
132 struct sockaddr_in sockname;
134 s = socket(PF_INET, SOCK_DGRAM, 0);
135 if (s < 0)
137 DPRINTF(E_ERROR, L_SSDP, "socket(udp_notify): %s\n", strerror(errno));
138 return -1;
141 mc_if.s_addr = iface->addr.s_addr;
143 if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loopchar, sizeof(loopchar)) < 0)
145 DPRINTF(E_ERROR, L_SSDP, "setsockopt(udp_notify, IP_MULTICAST_LOOP): %s\n", strerror(errno));
146 close(s);
147 return -1;
150 if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&mc_if, sizeof(mc_if)) < 0)
152 DPRINTF(E_ERROR, L_SSDP, "setsockopt(udp_notify, IP_MULTICAST_IF): %s\n", strerror(errno));
153 close(s);
154 return -1;
157 setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
159 if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bcast)) < 0)
161 DPRINTF(E_ERROR, L_SSDP, "setsockopt(udp_notify, SO_BROADCAST): %s\n", strerror(errno));
162 close(s);
163 return -1;
166 memset(&sockname, 0, sizeof(struct sockaddr_in));
167 sockname.sin_family = AF_INET;
168 sockname.sin_addr.s_addr = iface->addr.s_addr;
170 if (bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
172 DPRINTF(E_ERROR, L_SSDP, "bind(udp_notify): %s\n", strerror(errno));
173 close(s);
174 return -1;
177 if (AddMulticastMembership(sssdp, iface) < 0)
179 DPRINTF(E_WARN, L_SSDP, "Failed to add multicast membership for address %s\n",
180 iface->str);
183 return s;
186 static const char * const known_service_types[] =
188 uuidvalue,
189 "upnp:rootdevice",
190 "urn:schemas-upnp-org:device:MediaServer:",
191 "urn:schemas-upnp-org:service:ContentDirectory:",
192 "urn:schemas-upnp-org:service:ConnectionManager:",
193 "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:",
197 static void
198 _usleep(long usecs)
200 struct timespec sleep_time;
202 sleep_time.tv_sec = 0;
203 sleep_time.tv_nsec = usecs * 1000;
204 nanosleep(&sleep_time, NULL);
207 /* not really an SSDP "announce" as it is the response
208 * to a SSDP "M-SEARCH" */
209 static void
210 SendSSDPResponse(int s, struct sockaddr_in sockname, int st_no,
211 const char *host, unsigned short port)
213 int l, n;
214 char buf[512];
215 char tmstr[30];
216 time_t tm = time(NULL);
219 * follow guideline from document "UPnP Device Architecture 1.0"
220 * uppercase is recommended.
221 * DATE: is recommended
222 * SERVER: OS/ver UPnP/1.0 minidlna/1.0
223 * - check what to put in the 'Cache-Control' header
224 * */
225 strftime(tmstr, sizeof(tmstr), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&tm));
226 l = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\n"
227 "CACHE-CONTROL: max-age=%u\r\n"
228 "DATE: %s\r\n"
229 "ST: %s%s\r\n"
230 "USN: %s%s%s%s\r\n"
231 "EXT:\r\n"
232 "SERVER: " MINIDLNA_SERVER_STRING "\r\n"
233 "LOCATION: http://%s:%u" ROOTDESC_PATH "\r\n"
234 "Content-Length: 0\r\n"
235 "\r\n",
236 (runtime_vars.notify_interval<<1)+10,
237 tmstr,
238 known_service_types[st_no],
239 (st_no > 1 ? "1" : ""),
240 uuidvalue,
241 (st_no > 0 ? "::" : ""),
242 (st_no > 0 ? known_service_types[st_no] : ""),
243 (st_no > 1 ? "1" : ""),
244 host, (unsigned int)port);
245 DPRINTF(E_DEBUG, L_SSDP, "Sending M-SEARCH response to %s:%d ST: %s\n",
246 inet_ntoa(sockname.sin_addr), ntohs(sockname.sin_port),
247 known_service_types[st_no]);
248 n = sendto(s, buf, l, 0,
249 (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
250 if (n < 0)
251 DPRINTF(E_ERROR, L_SSDP, "sendto(udp): %s\n", strerror(errno));
254 void
255 SendSSDPNotifies(int s, const char *host, unsigned short port,
256 unsigned int interval)
258 struct sockaddr_in sockname;
259 int l, n, dup, i=0;
260 unsigned int lifetime;
261 char bufr[512];
263 memset(&sockname, 0, sizeof(struct sockaddr_in));
264 sockname.sin_family = AF_INET;
265 sockname.sin_port = htons(SSDP_PORT);
266 sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
267 lifetime = (interval << 1) + 10;
269 for (dup = 0; dup < 2; dup++)
271 if (dup)
272 _usleep(200000);
273 i = 0;
274 while (known_service_types[i])
276 l = snprintf(bufr, sizeof(bufr),
277 "NOTIFY * HTTP/1.1\r\n"
278 "HOST:%s:%d\r\n"
279 "CACHE-CONTROL:max-age=%u\r\n"
280 "LOCATION:http://%s:%d" ROOTDESC_PATH"\r\n"
281 "SERVER: " MINIDLNA_SERVER_STRING "\r\n"
282 "NT:%s%s\r\n"
283 "USN:%s%s%s%s\r\n"
284 "NTS:ssdp:alive\r\n"
285 "\r\n",
286 SSDP_MCAST_ADDR, SSDP_PORT,
287 lifetime,
288 host, port,
289 known_service_types[i],
290 (i > 1 ? "1" : ""),
291 uuidvalue,
292 (i > 0 ? "::" : ""),
293 (i > 0 ? known_service_types[i] : ""),
294 (i > 1 ? "1" : ""));
295 if (l >= sizeof(bufr))
297 DPRINTF(E_WARN, L_SSDP, "SendSSDPNotifies(): truncated output\n");
298 l = sizeof(bufr);
300 DPRINTF(E_MAXDEBUG, L_SSDP, "Sending ssdp:alive [%d]\n", s);
301 n = sendto(s, bufr, l, 0,
302 (struct sockaddr *)&sockname, sizeof(struct sockaddr_in));
303 if (n < 0)
304 DPRINTF(E_ERROR, L_SSDP, "sendto(udp_notify=%d, %s): %s\n", s, host, strerror(errno));
305 i++;
310 void
311 ParseUPnPClient(char *location)
313 char buf[8192];
314 struct sockaddr_in dest;
315 int s, n, do_headers = 0, nread = 0;
316 struct timeval tv;
317 char *addr, *path, *port_str;
318 long port = 80;
319 char *off = NULL, *p;
320 int content_len = sizeof(buf);
321 struct NameValueParserData xml;
322 struct client_cache_s *client;
323 int type = 0;
324 char *model, *serial, *name;
326 if (strncmp(location, "http://", 7) != 0)
327 return;
328 path = location + 7;
329 port_str = strsep(&path, "/");
330 if (!path)
331 return;
332 addr = strsep(&port_str, ":");
333 if (port_str)
335 port = strtol(port_str, NULL, 10);
336 if (!port)
337 port = 80;
340 memset(&dest, '\0', sizeof(dest));
341 if (!inet_aton(addr, &dest.sin_addr))
342 return;
343 /* Check if the client is already in cache */
344 dest.sin_family = AF_INET;
345 dest.sin_port = htons(port);
347 s = socket(PF_INET, SOCK_STREAM, 0);
348 if (s < 0)
349 return;
351 tv.tv_sec = 0;
352 tv.tv_usec = 500000;
353 setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
354 setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
356 if (connect(s, (struct sockaddr*)&dest, sizeof(struct sockaddr_in)) < 0)
357 goto close;
359 n = snprintf(buf, sizeof(buf), "GET /%s HTTP/1.0\r\n"
360 "HOST: %s:%ld\r\n\r\n",
361 path, addr, port);
362 if (write(s, buf, n) < 1)
363 goto close;
365 while ((n = read(s, buf+nread, sizeof(buf)-nread-1)) > 0)
367 nread += n;
368 buf[nread] = '\0';
369 n = nread - 4;
370 p = buf;
372 while (!off && (n-- > 0))
374 if (p[0] == '\r' && p[1] == '\n' && p[2] == '\r' && p[3] == '\n')
376 off = p + 4;
377 do_headers = 1;
379 p++;
381 if (!off)
382 continue;
384 if (do_headers)
386 p = buf;
387 if (strncmp(p, "HTTP/", 5) != 0)
388 goto close;
389 while (*p != ' ' && *p != '\t')
390 p++;
391 /* If we don't get a 200 status, ignore it */
392 if (strtol(p, NULL, 10) != 200)
393 goto close;
394 p = strcasestr(p, "Content-Length:");
395 if (p)
396 content_len = strtol(p+15, NULL, 10);
397 do_headers = 0;
399 if ((buf + nread - off) >= content_len)
400 break;
402 close:
403 close(s);
404 if (!off)
405 return;
406 nread -= off - buf;
407 ParseNameValue(off, nread, &xml, 0);
408 model = GetValueFromNameValueList(&xml, "modelName");
409 serial = GetValueFromNameValueList(&xml, "serialNumber");
410 name = GetValueFromNameValueList(&xml, "friendlyName");
411 if (model)
413 int i;
414 DPRINTF(E_DEBUG, L_SSDP, "Model: %s\n", model);
415 for (i = 0; client_types[i].name; i++)
417 if (client_types[i].match_type != EModelName)
418 continue;
419 if (strstr(model, client_types[i].match) != NULL)
421 type = i;
422 break;
426 /* Special Samsung handling. It's very hard to tell Series A from B */
427 if (type > 0 && client_types[type].type == ESamsungSeriesB)
429 if (serial)
431 DPRINTF(E_DEBUG, L_SSDP, "Serial: %s\n", serial);
432 /* The Series B I saw was 20081224DMR. Series A should be older than that. */
433 if (atoi(serial) < 20081201)
434 type = 0;
436 else
438 type = 0;
442 if (type == 0 && name != NULL)
444 for (i = 0; client_types[i].name; i++)
446 if (client_types[i].match_type != EFriendlyNameSSDP)
447 continue;
448 if (strcmp(name, client_types[i].match) == 0)
450 type = i;
451 break;
456 ClearNameValueList(&xml);
457 if (!type)
458 return;
459 /* Add this client to the cache if it's not there already. */
460 client = SearchClientCache(dest.sin_addr, 1);
461 if (!client)
463 AddClientCache(dest.sin_addr, type);
465 else
467 client->type = &client_types[type];
468 client->age = time(NULL);
472 /* ProcessSSDPRequest()
473 * process SSDP M-SEARCH requests and responds to them */
474 void
475 ProcessSSDPRequest(int s, unsigned short port)
477 int n;
478 char bufr[1500];
479 socklen_t len_r;
480 struct sockaddr_in sendername;
481 int i;
482 char *st = NULL, *mx = NULL, *man = NULL, *mx_end = NULL;
483 int man_len = 0;
484 len_r = sizeof(struct sockaddr_in);
486 n = recvfrom(s, bufr, sizeof(bufr)-1, 0,
487 (struct sockaddr *)&sendername, &len_r);
488 if (n < 0)
490 DPRINTF(E_ERROR, L_SSDP, "recvfrom(udp): %s\n", strerror(errno));
491 return;
493 bufr[n] = '\0';
494 n -= 2;
496 if (memcmp(bufr, "NOTIFY", 6) == 0)
498 char *loc = NULL, *srv = NULL, *nts = NULL, *nt = NULL;
499 int loc_len = 0;
500 //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Received SSDP notify:\n%.*s", n, bufr);
501 for (i = 0; i < n; i++)
503 if( bufr[i] == '*' )
504 break;
506 if (strcasestrc(bufr+i, "HTTP/1.1", '\r') == NULL)
507 return;
508 while (i < n)
510 while ((i < n) && (bufr[i] != '\r' || bufr[i+1] != '\n'))
511 i++;
512 i += 2;
513 if (strncasecmp(bufr+i, "SERVER:", 7) == 0)
515 srv = bufr+i+7;
516 while (*srv == ' ' || *srv == '\t')
517 srv++;
519 else if (strncasecmp(bufr+i, "LOCATION:", 9) == 0)
521 loc = bufr+i+9;
522 while (*loc == ' ' || *loc == '\t')
523 loc++;
524 while (loc[loc_len]!='\r' && loc[loc_len]!='\n')
525 loc_len++;
527 else if (strncasecmp(bufr+i, "NTS:", 4) == 0)
529 nts = bufr+i+4;
530 while (*nts == ' ' || *nts == '\t')
531 nts++;
533 else if (strncasecmp(bufr+i, "NT:", 3) == 0)
535 nt = bufr+i+3;
536 while(*nt == ' ' || *nt == '\t')
537 nt++;
540 if (!loc || !srv || !nt || !nts || (strncmp(nts, "ssdp:alive", 10) != 0) ||
541 (strncmp(nt, "urn:schemas-upnp-org:device:MediaRenderer", 41) != 0))
542 return;
543 loc[loc_len] = '\0';
544 if ((strncmp(srv, "Allegro-Software-RomPlug", 24) == 0) || /* Roku */
545 (strstr(loc, "SamsungMRDesc.xml") != NULL) || /* Samsung TV */
546 (strstrc(srv, "DigiOn DiXiM", '\r') != NULL)) /* Marantz Receiver */
548 /* Check if the client is already in cache */
549 struct client_cache_s *client = SearchClientCache(sendername.sin_addr, 1);
550 if (client)
552 if (client->type->type < EStandardDLNA150 &&
553 client->type->type != ESamsungSeriesA)
555 client->age = time(NULL);
556 return;
559 ParseUPnPClient(loc);
562 else if (memcmp(bufr, "M-SEARCH", 8) == 0)
564 int st_len = 0, mx_len = 0, mx_val = 0;
565 //DPRINTF(E_DEBUG, L_SSDP, "Received SSDP request:\n%.*s\n", n, bufr);
566 for (i = 0; i < n; i++)
568 if (bufr[i] == '*')
569 break;
571 if (strcasestrc(bufr+i, "HTTP/1.1", '\r') == NULL)
572 return;
573 while (i < n)
575 while ((i < n) && (bufr[i] != '\r' || bufr[i+1] != '\n'))
576 i++;
577 i += 2;
578 if (strncasecmp(bufr+i, "ST:", 3) == 0)
580 st = bufr+i+3;
581 st_len = 0;
582 while (*st == ' ' || *st == '\t')
583 st++;
584 while (st[st_len]!='\r' && st[st_len]!='\n')
585 st_len++;
587 else if (strncasecmp(bufr+i, "MX:", 3) == 0)
589 mx = bufr+i+3;
590 mx_len = 0;
591 while (*mx == ' ' || *mx == '\t')
592 mx++;
593 while (mx[mx_len]!='\r' && mx[mx_len]!='\n')
594 mx_len++;
595 mx_val = strtol(mx, &mx_end, 10);
597 else if (strncasecmp(bufr+i, "MAN:", 4) == 0)
599 man = bufr+i+4;
600 man_len = 0;
601 while (*man == ' ' || *man == '\t')
602 man++;
603 while (man[man_len]!='\r' && man[man_len]!='\n')
604 man_len++;
607 /*DPRINTF(E_INFO, L_SSDP, "SSDP M-SEARCH packet received from %s:%d\n",
608 inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port) );*/
609 if (GETFLAG(DLNA_STRICT_MASK) && (ntohs(sendername.sin_port) <= 1024 || ntohs(sendername.sin_port) == 1900))
611 DPRINTF(E_INFO, L_SSDP, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad source port %d]\n",
612 inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port));
614 else if (!man || (strncmp(man, "\"ssdp:discover\"", 15) != 0))
616 DPRINTF(E_INFO, L_SSDP, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad %s header '%.*s']\n",
617 inet_ntoa(sendername.sin_addr), "MAN", man_len, man);
619 else if (!mx || mx == mx_end || mx_val < 0)
621 DPRINTF(E_INFO, L_SSDP, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad %s header '%.*s']\n",
622 inet_ntoa(sendername.sin_addr), "MX", mx_len, mx);
624 else if (st && (st_len > 0))
626 int l;
627 int iface = 0;
628 /* find in which sub network the client is */
629 for (i = 0; i < n_lan_addr; i++)
631 if((sendername.sin_addr.s_addr & lan_addr[i].mask.s_addr) ==
632 (lan_addr[i].addr.s_addr & lan_addr[i].mask.s_addr))
634 iface = i;
635 break;
638 if (n_lan_addr == i)
640 DPRINTF(E_DEBUG, L_SSDP, "Ignoring SSDP M-SEARCH on other interface [%s]\n",
641 inet_ntoa(sendername.sin_addr));
642 return;
644 DPRINTF(E_DEBUG, L_SSDP, "SSDP M-SEARCH from %s:%d ST: %.*s, MX: %.*s, MAN: %.*s\n",
645 inet_ntoa(sendername.sin_addr),
646 ntohs(sendername.sin_port),
647 st_len, st, mx_len, mx, man_len, man);
648 /* Responds to request with a device as ST header */
649 for (i = 0; known_service_types[i]; i++)
651 l = strlen(known_service_types[i]);
652 if ((l > st_len) || (memcmp(st, known_service_types[i], l) != 0))
653 continue;
654 if (st_len != l)
656 /* Check version number - we only support 1. */
657 if ((st[l-1] == ':') && (st[l] == '1'))
658 l++;
659 while (l < st_len)
661 if (isdigit(st[l]))
662 break;
663 if (isspace(st[l]))
665 l++;
666 continue;
668 DPRINTF(E_MAXDEBUG, L_SSDP,
669 "Ignoring SSDP M-SEARCH with bad extra data '%c' [%s]\n",
670 st[l], inet_ntoa(sendername.sin_addr));
671 break;
673 if (l != st_len)
674 break;
676 _usleep(random()>>20);
677 SendSSDPResponse(s, sendername, i,
678 lan_addr[iface].str, port);
679 return;
681 /* Responds to request with ST: ssdp:all */
682 /* strlen("ssdp:all") == 8 */
683 if ((st_len == 8) && (memcmp(st, "ssdp:all", 8) == 0))
685 for (i=0; known_service_types[i]; i++)
687 l = strlen(known_service_types[i]);
688 SendSSDPResponse(s, sendername, i,
689 lan_addr[iface].str, port);
693 else
695 DPRINTF(E_INFO, L_SSDP, "Invalid SSDP M-SEARCH from %s:%d\n",
696 inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port));
699 else if (memcmp(bufr, "YOUKU-NOTIFY", 12) == 0)
701 return;
703 else
705 DPRINTF(E_WARN, L_SSDP, "Unknown udp packet received from %s:%d\n",
706 inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port));
710 /* This will broadcast ssdp:byebye notifications to inform
711 * the network that UPnP is going down. */
713 SendSSDPGoodbyes(int s)
715 struct sockaddr_in sockname;
716 int n, l;
717 int i;
718 int dup, ret = 0;
719 char bufr[512];
721 memset(&sockname, 0, sizeof(struct sockaddr_in));
722 sockname.sin_family = AF_INET;
723 sockname.sin_port = htons(SSDP_PORT);
724 sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
726 for (dup = 0; dup < 2; dup++)
728 for (i = 0; known_service_types[i]; i++)
730 l = snprintf(bufr, sizeof(bufr),
731 "NOTIFY * HTTP/1.1\r\n"
732 "HOST:%s:%d\r\n"
733 "NT:%s%s\r\n"
734 "USN:%s%s%s%s\r\n"
735 "NTS:ssdp:byebye\r\n"
736 "\r\n",
737 SSDP_MCAST_ADDR, SSDP_PORT,
738 known_service_types[i],
739 (i > 1 ? "1" : ""), uuidvalue,
740 (i > 0 ? "::" : ""),
741 (i > 0 ? known_service_types[i] : ""),
742 (i > 1 ? "1" : ""));
743 DPRINTF(E_MAXDEBUG, L_SSDP, "Sending ssdp:byebye [%d]\n", s);
744 n = sendto(s, bufr, l, 0,
745 (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
746 if (n < 0)
748 DPRINTF(E_ERROR, L_SSDP, "sendto(udp_shutdown=%d): %s\n", s, strerror(errno));
749 ret = -1;
750 break;
754 return ret;
757 /* SubmitServicesToMiniSSDPD() :
758 * register services offered by MiniUPnPd to a running instance of
759 * MiniSSDPd */
761 SubmitServicesToMiniSSDPD(const char *host, unsigned short port)
763 struct sockaddr_un addr;
764 int s;
765 unsigned char buffer[2048];
766 char strbuf[256];
767 unsigned char *p;
768 int i, l;
770 s = socket(AF_UNIX, SOCK_STREAM, 0);
771 if (s < 0)
773 DPRINTF(E_ERROR, L_SSDP, "socket(unix): %s", strerror(errno));
774 return -1;
776 addr.sun_family = AF_UNIX;
777 strncpyt(addr.sun_path, minissdpdsocketpath, sizeof(addr.sun_path));
778 if (connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0)
780 DPRINTF(E_ERROR, L_SSDP, "connect(\"%s\"): %s",
781 minissdpdsocketpath, strerror(errno));
782 close(s);
783 return -1;
785 for (i = 0; known_service_types[i]; i++)
787 buffer[0] = 4;
788 p = buffer + 1;
789 l = strlen(known_service_types[i]);
790 if (i > 0)
791 l++;
792 CODELENGTH(l, p);
793 memcpy(p, known_service_types[i], l);
794 if (i > 0)
795 p[l-1] = '1';
796 p += l;
797 l = snprintf(strbuf, sizeof(strbuf), "%s::%s%s",
798 uuidvalue, known_service_types[i], (i==0)?"":"1");
799 CODELENGTH(l, p);
800 memcpy(p, strbuf, l);
801 p += l;
802 l = strlen(MINIDLNA_SERVER_STRING);
803 CODELENGTH(l, p);
804 memcpy(p, MINIDLNA_SERVER_STRING, l);
805 p += l;
806 l = snprintf(strbuf, sizeof(strbuf), "http://%s:%u" ROOTDESC_PATH,
807 host, (unsigned int)port);
808 CODELENGTH(l, p);
809 memcpy(p, strbuf, l);
810 p += l;
811 if(write(s, buffer, p - buffer) < 0)
813 DPRINTF(E_ERROR, L_SSDP, "write(): %s", strerror(errno));
814 close(s);
815 return -1;
818 close(s);
819 return 0;