minidlna: update to 1.1.5
[tomato.git] / release / src / router / minidlna / minissdp.c
blob77890765a2c79efe8d28ef3866c3958451499fe4
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 /* Setting the socket options will guarantee, tha we will only receive
77 * multicast traffic on a specific Interface.
78 * In addition the kernel is instructed to send an igmp message (choose
79 * mcast group) on the specific interface/subnet. */
80 ret = setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&imr, sizeof(imr));
81 if (ret < 0 && errno != EADDRINUSE)
83 DPRINTF(E_ERROR, L_SSDP, "setsockopt(udp, IP_ADD_MEMBERSHIP): %s\n",
84 strerror(errno));
85 return -1;
88 return 0;
91 /* Open and configure the socket listening for
92 * SSDP udp packets sent on 239.255.255.250 port 1900 */
93 int
94 OpenAndConfSSDPReceiveSocket(void)
96 int s;
97 int i = 1;
98 struct sockaddr_in sockname;
100 s = socket(PF_INET, SOCK_DGRAM, 0);
101 if (s < 0)
103 DPRINTF(E_ERROR, L_SSDP, "socket(udp): %s\n", strerror(errno));
104 return -1;
107 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0)
108 DPRINTF(E_WARN, L_SSDP, "setsockopt(udp, SO_REUSEADDR): %s\n", strerror(errno));
109 #ifdef __linux__
110 if (setsockopt(s, IPPROTO_IP, IP_PKTINFO, &i, sizeof(i)) < 0)
111 DPRINTF(E_WARN, L_SSDP, "setsockopt(udp, IP_PKTINFO): %s\n", strerror(errno));
112 #endif
113 memset(&sockname, 0, sizeof(struct sockaddr_in));
114 sockname.sin_family = AF_INET;
115 sockname.sin_port = htons(SSDP_PORT);
116 /* NOTE: Binding a socket to a UDP multicast address means, that we just want
117 * to receive datagramms send to this multicast address.
118 * To specify the local nics we want to use we have to use setsockopt,
119 * see AddMulticastMembership(...). */
120 sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
122 if (bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
124 DPRINTF(E_ERROR, L_SSDP, "bind(udp): %s\n", strerror(errno));
125 close(s);
126 return -1;
129 return s;
132 /* open the UDP socket used to send SSDP notifications to
133 * the multicast group reserved for them */
135 OpenAndConfSSDPNotifySocket(struct lan_addr_s *iface)
137 int s;
138 unsigned char loopchar = 0;
139 int bcast = 1;
140 uint8_t ttl = 4;
141 struct in_addr mc_if;
142 struct sockaddr_in sockname;
144 s = socket(PF_INET, SOCK_DGRAM, 0);
145 if (s < 0)
147 DPRINTF(E_ERROR, L_SSDP, "socket(udp_notify): %s\n", strerror(errno));
148 return -1;
151 mc_if.s_addr = iface->addr.s_addr;
153 if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loopchar, sizeof(loopchar)) < 0)
155 DPRINTF(E_ERROR, L_SSDP, "setsockopt(udp_notify, IP_MULTICAST_LOOP): %s\n", strerror(errno));
156 close(s);
157 return -1;
160 if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&mc_if, sizeof(mc_if)) < 0)
162 DPRINTF(E_ERROR, L_SSDP, "setsockopt(udp_notify, IP_MULTICAST_IF): %s\n", strerror(errno));
163 close(s);
164 return -1;
167 setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
169 if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bcast)) < 0)
171 DPRINTF(E_ERROR, L_SSDP, "setsockopt(udp_notify, SO_BROADCAST): %s\n", strerror(errno));
172 close(s);
173 return -1;
176 memset(&sockname, 0, sizeof(struct sockaddr_in));
177 sockname.sin_family = AF_INET;
178 sockname.sin_addr.s_addr = iface->addr.s_addr;
180 if (bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
182 DPRINTF(E_ERROR, L_SSDP, "bind(udp_notify): %s\n", strerror(errno));
183 close(s);
184 return -1;
187 if (AddMulticastMembership(sssdp, iface) < 0)
189 DPRINTF(E_WARN, L_SSDP, "Failed to add multicast membership for address %s\n",
190 iface->str);
193 return s;
196 static const char * const known_service_types[] =
198 uuidvalue,
199 "upnp:rootdevice",
200 "urn:schemas-upnp-org:device:MediaServer:",
201 "urn:schemas-upnp-org:service:ContentDirectory:",
202 "urn:schemas-upnp-org:service:ConnectionManager:",
203 "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:",
207 static void
208 _usleep(long usecs)
210 struct timespec sleep_time;
212 sleep_time.tv_sec = 0;
213 sleep_time.tv_nsec = usecs * 1000;
214 nanosleep(&sleep_time, NULL);
217 /* not really an SSDP "announce" as it is the response
218 * to a SSDP "M-SEARCH" */
219 static void
220 SendSSDPResponse(int s, struct sockaddr_in sockname, int st_no,
221 const char *host, unsigned short port)
223 int l, n;
224 char buf[512];
225 char tmstr[30];
226 time_t tm = time(NULL);
229 * follow guideline from document "UPnP Device Architecture 1.0"
230 * uppercase is recommended.
231 * DATE: is recommended
232 * SERVER: OS/ver UPnP/1.0 minidlna/1.0
233 * - check what to put in the 'Cache-Control' header
234 * */
235 strftime(tmstr, sizeof(tmstr), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&tm));
236 l = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\n"
237 "CACHE-CONTROL: max-age=%u\r\n"
238 "DATE: %s\r\n"
239 "ST: %s%s\r\n"
240 "USN: %s%s%s%s\r\n"
241 "EXT:\r\n"
242 "SERVER: " MINIDLNA_SERVER_STRING "\r\n"
243 "LOCATION: http://%s:%u" ROOTDESC_PATH "\r\n"
244 "Content-Length: 0\r\n"
245 "\r\n",
246 (runtime_vars.notify_interval<<1)+10,
247 tmstr,
248 known_service_types[st_no],
249 (st_no > 1 ? "1" : ""),
250 uuidvalue,
251 (st_no > 0 ? "::" : ""),
252 (st_no > 0 ? known_service_types[st_no] : ""),
253 (st_no > 1 ? "1" : ""),
254 host, (unsigned int)port);
255 DPRINTF(E_DEBUG, L_SSDP, "Sending M-SEARCH response to %s:%d ST: %s\n",
256 inet_ntoa(sockname.sin_addr), ntohs(sockname.sin_port),
257 known_service_types[st_no]);
258 n = sendto(s, buf, l, 0,
259 (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
260 if (n < 0)
261 DPRINTF(E_ERROR, L_SSDP, "sendto(udp): %s\n", strerror(errno));
264 void
265 SendSSDPNotifies(int s, const char *host, unsigned short port,
266 unsigned int interval)
268 struct sockaddr_in sockname;
269 int l, n, dup, i=0;
270 unsigned int lifetime;
271 char bufr[512];
273 memset(&sockname, 0, sizeof(struct sockaddr_in));
274 sockname.sin_family = AF_INET;
275 sockname.sin_port = htons(SSDP_PORT);
276 sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
277 lifetime = (interval << 1) + 10;
279 for (dup = 0; dup < 2; dup++)
281 if (dup)
282 _usleep(200000);
283 i = 0;
284 while (known_service_types[i])
286 l = snprintf(bufr, sizeof(bufr),
287 "NOTIFY * HTTP/1.1\r\n"
288 "HOST:%s:%d\r\n"
289 "CACHE-CONTROL:max-age=%u\r\n"
290 "LOCATION:http://%s:%d" ROOTDESC_PATH"\r\n"
291 "SERVER: " MINIDLNA_SERVER_STRING "\r\n"
292 "NT:%s%s\r\n"
293 "USN:%s%s%s%s\r\n"
294 "NTS:ssdp:alive\r\n"
295 "\r\n",
296 SSDP_MCAST_ADDR, SSDP_PORT,
297 lifetime,
298 host, port,
299 known_service_types[i],
300 (i > 1 ? "1" : ""),
301 uuidvalue,
302 (i > 0 ? "::" : ""),
303 (i > 0 ? known_service_types[i] : ""),
304 (i > 1 ? "1" : ""));
305 if (l >= sizeof(bufr))
307 DPRINTF(E_WARN, L_SSDP, "SendSSDPNotifies(): truncated output\n");
308 l = sizeof(bufr);
310 DPRINTF(E_MAXDEBUG, L_SSDP, "Sending ssdp:alive [%d]\n", s);
311 n = sendto(s, bufr, l, 0,
312 (struct sockaddr *)&sockname, sizeof(struct sockaddr_in));
313 if (n < 0)
314 DPRINTF(E_ERROR, L_SSDP, "sendto(udp_notify=%d, %s): %s\n", s, host, strerror(errno));
315 i++;
320 static void
321 ParseUPnPClient(char *location)
323 char buf[8192];
324 struct sockaddr_in dest;
325 int s, n, do_headers = 0, nread = 0;
326 struct timeval tv;
327 char *addr, *path, *port_str;
328 long port = 80;
329 char *off = NULL, *p;
330 int content_len = sizeof(buf);
331 struct NameValueParserData xml;
332 struct client_cache_s *client;
333 int type = 0;
334 char *model, *serial, *name;
336 if (strncmp(location, "http://", 7) != 0)
337 return;
338 path = location + 7;
339 port_str = strsep(&path, "/");
340 if (!path)
341 return;
342 addr = strsep(&port_str, ":");
343 if (port_str)
345 port = strtol(port_str, NULL, 10);
346 if (!port)
347 port = 80;
350 memset(&dest, '\0', sizeof(dest));
351 if (!inet_aton(addr, &dest.sin_addr))
352 return;
353 /* Check if the client is already in cache */
354 dest.sin_family = AF_INET;
355 dest.sin_port = htons(port);
357 s = socket(PF_INET, SOCK_STREAM, 0);
358 if (s < 0)
359 return;
361 tv.tv_sec = 0;
362 tv.tv_usec = 500000;
363 setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
364 setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
366 if (connect(s, (struct sockaddr*)&dest, sizeof(struct sockaddr_in)) < 0)
367 goto close;
369 n = snprintf(buf, sizeof(buf), "GET /%s HTTP/1.0\r\n"
370 "HOST: %s:%ld\r\n\r\n",
371 path, addr, port);
372 if (write(s, buf, n) < 1)
373 goto close;
375 while ((n = read(s, buf+nread, sizeof(buf)-nread-1)) > 0)
377 nread += n;
378 buf[nread] = '\0';
379 n = nread - 4;
380 p = buf;
382 while (!off && (n-- > 0))
384 if (p[0] == '\r' && p[1] == '\n' && p[2] == '\r' && p[3] == '\n')
386 off = p + 4;
387 do_headers = 1;
389 p++;
391 if (!off)
392 continue;
394 if (do_headers)
396 p = buf;
397 if (strncmp(p, "HTTP/", 5) != 0)
398 goto close;
399 while (*p != ' ' && *p != '\t')
400 p++;
401 /* If we don't get a 200 status, ignore it */
402 if (strtol(p, NULL, 10) != 200)
403 goto close;
404 p = strcasestr(p, "Content-Length:");
405 if (p)
406 content_len = strtol(p+15, NULL, 10);
407 do_headers = 0;
409 if ((buf + nread - off) >= content_len)
410 break;
412 close:
413 close(s);
414 if (!off)
415 return;
416 nread -= off - buf;
417 ParseNameValue(off, nread, &xml, 0);
418 model = GetValueFromNameValueList(&xml, "modelName");
419 serial = GetValueFromNameValueList(&xml, "serialNumber");
420 name = GetValueFromNameValueList(&xml, "friendlyName");
421 if (model)
423 int i;
424 DPRINTF(E_DEBUG, L_SSDP, "Model: %s\n", model);
425 for (i = 0; client_types[i].name; i++)
427 if (client_types[i].match_type != EModelName)
428 continue;
429 if (strstr(model, client_types[i].match) != NULL)
431 type = i;
432 break;
436 /* Special Samsung handling. It's very hard to tell Series A from B */
437 if (type > 0 && client_types[type].type == ESamsungSeriesB)
439 if (serial)
441 DPRINTF(E_DEBUG, L_SSDP, "Serial: %s\n", serial);
442 /* The Series B I saw was 20081224DMR. Series A should be older than that. */
443 if (atoi(serial) < 20081201)
444 type = 0;
446 else
448 type = 0;
452 if (type == 0 && name != NULL)
454 for (i = 0; client_types[i].name; i++)
456 if (client_types[i].match_type != EFriendlyNameSSDP)
457 continue;
458 if (strcmp(name, client_types[i].match) == 0)
460 type = i;
461 break;
466 ClearNameValueList(&xml);
467 if (!type)
468 return;
469 /* Add this client to the cache if it's not there already. */
470 client = SearchClientCache(dest.sin_addr, 1);
471 if (!client)
473 AddClientCache(dest.sin_addr, type);
475 else
477 client->type = &client_types[type];
478 client->age = time(NULL);
482 /* ProcessSSDPRequest()
483 * process SSDP M-SEARCH requests and responds to them */
484 void
485 ProcessSSDPRequest(int s, unsigned short port)
487 int n;
488 char bufr[1500];
489 struct sockaddr_in sendername;
490 int i;
491 char *st = NULL, *mx = NULL, *man = NULL, *mx_end = NULL;
492 int man_len = 0;
493 #ifdef __linux__
494 char cmbuf[CMSG_SPACE(sizeof(struct in_pktinfo))];
495 struct iovec iovec = {
496 .iov_base = bufr,
497 .iov_len = sizeof(bufr)-1
499 struct msghdr mh = {
500 .msg_name = &sendername,
501 .msg_namelen = sizeof(struct sockaddr_in),
502 .msg_iov = &iovec,
503 .msg_iovlen = 1,
504 .msg_control = cmbuf,
505 .msg_controllen = sizeof(cmbuf)
508 n = recvmsg(s, &mh, 0);
509 #else
510 socklen_t len_r = sizeof(struct sockaddr_in);
512 n = recvfrom(s, bufr, sizeof(bufr)-1, 0,
513 (struct sockaddr *)&sendername, &len_r);
514 #endif
515 if (n < 0)
517 DPRINTF(E_ERROR, L_SSDP, "recvfrom(udp): %s\n", strerror(errno));
518 return;
520 bufr[n] = '\0';
521 n -= 2;
523 if (memcmp(bufr, "NOTIFY", 6) == 0)
525 char *loc = NULL, *srv = NULL, *nts = NULL, *nt = NULL;
526 int loc_len = 0;
527 //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Received SSDP notify:\n%.*s", n, bufr);
528 for (i = 0; i < n; i++)
530 if( bufr[i] == '*' )
531 break;
533 if (strcasestrc(bufr+i, "HTTP/1.1", '\r') == NULL)
534 return;
535 while (i < n)
537 while ((i < n) && (bufr[i] != '\r' || bufr[i+1] != '\n'))
538 i++;
539 i += 2;
540 if (strncasecmp(bufr+i, "SERVER:", 7) == 0)
542 srv = bufr+i+7;
543 while (*srv == ' ' || *srv == '\t')
544 srv++;
546 else if (strncasecmp(bufr+i, "LOCATION:", 9) == 0)
548 loc = bufr+i+9;
549 while (*loc == ' ' || *loc == '\t')
550 loc++;
551 while (loc[loc_len]!='\r' && loc[loc_len]!='\n')
552 loc_len++;
554 else if (strncasecmp(bufr+i, "NTS:", 4) == 0)
556 nts = bufr+i+4;
557 while (*nts == ' ' || *nts == '\t')
558 nts++;
560 else if (strncasecmp(bufr+i, "NT:", 3) == 0)
562 nt = bufr+i+3;
563 while(*nt == ' ' || *nt == '\t')
564 nt++;
567 if (!loc || !srv || !nt || !nts || (strncmp(nts, "ssdp:alive", 10) != 0) ||
568 (strncmp(nt, "urn:schemas-upnp-org:device:MediaRenderer", 41) != 0))
569 return;
570 loc[loc_len] = '\0';
571 if ((strncmp(srv, "Allegro-Software-RomPlug", 24) == 0) || /* Roku */
572 (strstr(loc, "SamsungMRDesc.xml") != NULL) || /* Samsung TV */
573 (strstrc(srv, "DigiOn DiXiM", '\r') != NULL)) /* Marantz Receiver */
575 /* Check if the client is already in cache */
576 struct client_cache_s *client = SearchClientCache(sendername.sin_addr, 1);
577 if (client)
579 if (client->type->type < EStandardDLNA150 &&
580 client->type->type != ESamsungSeriesA)
582 client->age = time(NULL);
583 return;
586 ParseUPnPClient(loc);
589 else if (memcmp(bufr, "M-SEARCH", 8) == 0)
591 int st_len = 0, mx_len = 0, mx_val = 0;
592 //DPRINTF(E_DEBUG, L_SSDP, "Received SSDP request:\n%.*s\n", n, bufr);
593 for (i = 0; i < n; i++)
595 if (bufr[i] == '*')
596 break;
598 if (strcasestrc(bufr+i, "HTTP/1.1", '\r') == NULL)
599 return;
600 while (i < n)
602 while ((i < n) && (bufr[i] != '\r' || bufr[i+1] != '\n'))
603 i++;
604 i += 2;
605 if (strncasecmp(bufr+i, "ST:", 3) == 0)
607 st = bufr+i+3;
608 st_len = 0;
609 while (*st == ' ' || *st == '\t')
610 st++;
611 while (st[st_len]!='\r' && st[st_len]!='\n')
612 st_len++;
614 else if (strncasecmp(bufr+i, "MX:", 3) == 0)
616 mx = bufr+i+3;
617 mx_len = 0;
618 while (*mx == ' ' || *mx == '\t')
619 mx++;
620 while (mx[mx_len]!='\r' && mx[mx_len]!='\n')
621 mx_len++;
622 mx_val = strtol(mx, &mx_end, 10);
624 else if (strncasecmp(bufr+i, "MAN:", 4) == 0)
626 man = bufr+i+4;
627 man_len = 0;
628 while (*man == ' ' || *man == '\t')
629 man++;
630 while (man[man_len]!='\r' && man[man_len]!='\n')
631 man_len++;
634 /*DPRINTF(E_INFO, L_SSDP, "SSDP M-SEARCH packet received from %s:%d\n",
635 inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port) );*/
636 if (GETFLAG(DLNA_STRICT_MASK) && (ntohs(sendername.sin_port) <= 1024 || ntohs(sendername.sin_port) == 1900))
638 DPRINTF(E_INFO, L_SSDP, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad source port %d]\n",
639 inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port));
641 else if (!man || (strncmp(man, "\"ssdp:discover\"", 15) != 0))
643 DPRINTF(E_INFO, L_SSDP, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad %s header '%.*s']\n",
644 inet_ntoa(sendername.sin_addr), "MAN", man_len, man);
646 else if (!mx || mx == mx_end || mx_val < 0)
648 DPRINTF(E_INFO, L_SSDP, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad %s header '%.*s']\n",
649 inet_ntoa(sendername.sin_addr), "MX", mx_len, mx);
651 else if (st && (st_len > 0))
653 int l;
654 #ifdef __linux__
655 char host[40] = "127.0.0.1";
656 struct cmsghdr *cmsg;
658 /* find the interface we received the msg from */
659 for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg))
661 struct in_addr addr;
662 struct in_pktinfo *pi;
663 /* ignore the control headers that don't match what we want */
664 if (cmsg->cmsg_level != IPPROTO_IP ||
665 cmsg->cmsg_type != IP_PKTINFO)
666 continue;
668 pi = (struct in_pktinfo *)CMSG_DATA(cmsg);
669 addr = pi->ipi_spec_dst;
670 inet_ntop(AF_INET, &addr, host, sizeof(host));
672 #else
673 const char *host;
674 int iface = 0;
675 /* find in which sub network the client is */
676 for (i = 0; i < n_lan_addr; i++)
678 if((sendername.sin_addr.s_addr & lan_addr[i].mask.s_addr) ==
679 (lan_addr[i].addr.s_addr & lan_addr[i].mask.s_addr))
681 iface = i;
682 break;
685 if (n_lan_addr == i)
687 DPRINTF(E_DEBUG, L_SSDP, "Ignoring SSDP M-SEARCH on other interface [%s]\n",
688 inet_ntoa(sendername.sin_addr));
689 return;
691 host = lan_addr[iface].str;
692 #endif
693 DPRINTF(E_DEBUG, L_SSDP, "SSDP M-SEARCH from %s:%d ST: %.*s, MX: %.*s, MAN: %.*s\n",
694 inet_ntoa(sendername.sin_addr),
695 ntohs(sendername.sin_port),
696 st_len, st, mx_len, mx, man_len, man);
697 /* Responds to request with a device as ST header */
698 for (i = 0; known_service_types[i]; i++)
700 l = strlen(known_service_types[i]);
701 if ((l > st_len) || (memcmp(st, known_service_types[i], l) != 0))
702 continue;
703 if (st_len != l)
705 /* Check version number - we only support 1. */
706 if ((st[l-1] == ':') && (st[l] == '1'))
707 l++;
708 while (l < st_len)
710 if (isdigit(st[l]))
711 break;
712 if (isspace(st[l]))
714 l++;
715 continue;
717 DPRINTF(E_MAXDEBUG, L_SSDP,
718 "Ignoring SSDP M-SEARCH with bad extra data '%c' [%s]\n",
719 st[l], inet_ntoa(sendername.sin_addr));
720 break;
722 if (l != st_len)
723 break;
725 _usleep(random()>>20);
726 SendSSDPResponse(s, sendername, i,
727 host, port);
728 return;
730 /* Responds to request with ST: ssdp:all */
731 /* strlen("ssdp:all") == 8 */
732 if ((st_len == 8) && (memcmp(st, "ssdp:all", 8) == 0))
734 for (i=0; known_service_types[i]; i++)
736 l = strlen(known_service_types[i]);
737 SendSSDPResponse(s, sendername, i,
738 host, port);
742 else
744 DPRINTF(E_INFO, L_SSDP, "Invalid SSDP M-SEARCH from %s:%d\n",
745 inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port));
748 else if (memcmp(bufr, "YOUKU-NOTIFY", 12) == 0)
750 return;
752 else
754 DPRINTF(E_WARN, L_SSDP, "Unknown udp packet received from %s:%d\n",
755 inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port));
759 /* This will broadcast ssdp:byebye notifications to inform
760 * the network that UPnP is going down. */
762 SendSSDPGoodbyes(int s)
764 struct sockaddr_in sockname;
765 int n, l;
766 int i;
767 int dup, ret = 0;
768 char bufr[512];
770 memset(&sockname, 0, sizeof(struct sockaddr_in));
771 sockname.sin_family = AF_INET;
772 sockname.sin_port = htons(SSDP_PORT);
773 sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
775 for (dup = 0; dup < 2; dup++)
777 for (i = 0; known_service_types[i]; i++)
779 l = snprintf(bufr, sizeof(bufr),
780 "NOTIFY * HTTP/1.1\r\n"
781 "HOST:%s:%d\r\n"
782 "NT:%s%s\r\n"
783 "USN:%s%s%s%s\r\n"
784 "NTS:ssdp:byebye\r\n"
785 "\r\n",
786 SSDP_MCAST_ADDR, SSDP_PORT,
787 known_service_types[i],
788 (i > 1 ? "1" : ""), uuidvalue,
789 (i > 0 ? "::" : ""),
790 (i > 0 ? known_service_types[i] : ""),
791 (i > 1 ? "1" : ""));
792 DPRINTF(E_MAXDEBUG, L_SSDP, "Sending ssdp:byebye [%d]\n", s);
793 n = sendto(s, bufr, l, 0,
794 (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
795 if (n < 0)
797 DPRINTF(E_ERROR, L_SSDP, "sendto(udp_shutdown=%d): %s\n", s, strerror(errno));
798 ret = -1;
799 break;
803 return ret;
806 /* SubmitServicesToMiniSSDPD() :
807 * register services offered by MiniUPnPd to a running instance of
808 * MiniSSDPd */
810 SubmitServicesToMiniSSDPD(const char *host, unsigned short port)
812 struct sockaddr_un addr;
813 int s;
814 unsigned char buffer[2048];
815 char strbuf[256];
816 unsigned char *p;
817 int i, l;
819 s = socket(AF_UNIX, SOCK_STREAM, 0);
820 if (s < 0)
822 DPRINTF(E_ERROR, L_SSDP, "socket(unix): %s", strerror(errno));
823 return -1;
825 addr.sun_family = AF_UNIX;
826 strncpyt(addr.sun_path, minissdpdsocketpath, sizeof(addr.sun_path));
827 if (connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0)
829 DPRINTF(E_ERROR, L_SSDP, "connect(\"%s\"): %s",
830 minissdpdsocketpath, strerror(errno));
831 close(s);
832 return -1;
834 for (i = 0; known_service_types[i]; i++)
836 buffer[0] = 4;
837 p = buffer + 1;
838 l = strlen(known_service_types[i]);
839 if (i > 0)
840 l++;
841 CODELENGTH(l, p);
842 memcpy(p, known_service_types[i], l);
843 if (i > 0)
844 p[l-1] = '1';
845 p += l;
846 l = snprintf(strbuf, sizeof(strbuf), "%s::%s%s",
847 uuidvalue, known_service_types[i], (i==0)?"":"1");
848 CODELENGTH(l, p);
849 memcpy(p, strbuf, l);
850 p += l;
851 l = strlen(MINIDLNA_SERVER_STRING);
852 CODELENGTH(l, p);
853 memcpy(p, MINIDLNA_SERVER_STRING, l);
854 p += l;
855 l = snprintf(strbuf, sizeof(strbuf), "http://%s:%u" ROOTDESC_PATH,
856 host, (unsigned int)port);
857 CODELENGTH(l, p);
858 memcpy(p, strbuf, l);
859 p += l;
860 if(write(s, buffer, p - buffer) < 0)
862 DPRINTF(E_ERROR, L_SSDP, "write(): %s", strerror(errno));
863 close(s);
864 return -1;
867 close(s);
868 return 0;