MiniDLNA update: 1.0.19.1 to 1.0.20
[tomato.git] / release / src / router / minidlna / minissdp.c
blob3f14ccbbc717f3013c965ea98199eb0dc87346f1
1 /* $Id: minissdp.c,v 1.18 2011/05/03 06:14:25 jmaggard Exp $ */
2 /* MiniUPnP project
3 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
5 * Copyright (c) 2006, Thomas Bernard
6 * All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * * The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <sys/socket.h>
35 #include <netinet/in.h>
36 #include <arpa/inet.h>
37 #include <errno.h>
39 #include "config.h"
40 #include "upnpdescstrings.h"
41 #include "minidlnapath.h"
42 #include "upnphttp.h"
43 #include "upnpglobalvars.h"
44 #include "upnpreplyparse.h"
45 #include "getifaddr.h"
46 #include "minissdp.h"
47 #include "log.h"
49 /* SSDP ip/port */
50 #define SSDP_PORT (1900)
51 #define SSDP_MCAST_ADDR ("239.255.255.250")
53 static int
54 AddMulticastMembership(int s, in_addr_t ifaddr/*const char * ifaddr*/)
56 struct ip_mreq imr; /* Ip multicast membership */
58 /* setting up imr structure */
59 imr.imr_multiaddr.s_addr = inet_addr(SSDP_MCAST_ADDR);
60 /*imr.imr_interface.s_addr = htonl(INADDR_ANY);*/
61 imr.imr_interface.s_addr = ifaddr; /*inet_addr(ifaddr);*/
63 if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&imr, sizeof(struct ip_mreq)) < 0)
65 DPRINTF(E_ERROR, L_SSDP, "setsockopt(udp, IP_ADD_MEMBERSHIP): %s\n", strerror(errno));
66 return -1;
69 return 0;
72 int
73 OpenAndConfSSDPReceiveSocket()
75 int s;
76 int i = 1;
77 struct sockaddr_in sockname;
79 if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
81 DPRINTF(E_ERROR, L_SSDP, "socket(udp): %s\n", strerror(errno));
82 return -1;
85 if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0)
87 DPRINTF(E_WARN, L_SSDP, "setsockopt(udp, SO_REUSEADDR): %s\n", strerror(errno));
90 memset(&sockname, 0, sizeof(struct sockaddr_in));
91 sockname.sin_family = AF_INET;
92 sockname.sin_port = htons(SSDP_PORT);
93 /* NOTE : it seems it doesnt work when binding on the specific address */
94 /*sockname.sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);*/
95 sockname.sin_addr.s_addr = htonl(INADDR_ANY);
96 /*sockname.sin_addr.s_addr = inet_addr(ifaddr);*/
98 if(bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
100 DPRINTF(E_ERROR, L_SSDP, "bind(udp): %s\n", strerror(errno));
101 close(s);
102 return -1;
105 i = n_lan_addr;
106 while(i>0)
108 i--;
109 if(AddMulticastMembership(s, lan_addr[i].addr.s_addr) < 0)
111 DPRINTF(E_WARN, L_SSDP,
112 "Failed to add multicast membership for address %s\n",
113 lan_addr[i].str );
117 return s;
120 /* open the UDP socket used to send SSDP notifications to
121 * the multicast group reserved for them */
122 static int
123 OpenAndConfSSDPNotifySocket(in_addr_t addr)
125 int s;
126 unsigned char loopchar = 0;
127 int bcast = 1;
128 struct in_addr mc_if;
129 struct sockaddr_in sockname;
131 if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
133 DPRINTF(E_ERROR, L_SSDP, "socket(udp_notify): %s\n", strerror(errno));
134 return -1;
137 mc_if.s_addr = addr; /*inet_addr(addr);*/
139 if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loopchar, sizeof(loopchar)) < 0)
141 DPRINTF(E_ERROR, L_SSDP, "setsockopt(udp_notify, IP_MULTICAST_LOOP): %s\n", strerror(errno));
142 close(s);
143 return -1;
146 if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&mc_if, sizeof(mc_if)) < 0)
148 DPRINTF(E_ERROR, L_SSDP, "setsockopt(udp_notify, IP_MULTICAST_IF): %s\n", strerror(errno));
149 close(s);
150 return -1;
153 if(setsockopt(s, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bcast)) < 0)
155 DPRINTF(E_ERROR, L_SSDP, "setsockopt(udp_notify, SO_BROADCAST): %s\n", strerror(errno));
156 close(s);
157 return -1;
160 memset(&sockname, 0, sizeof(struct sockaddr_in));
161 sockname.sin_family = AF_INET;
162 sockname.sin_addr.s_addr = addr; /*inet_addr(addr);*/
164 if (bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
166 DPRINTF(E_ERROR, L_SSDP, "bind(udp_notify): %s\n", strerror(errno));
167 close(s);
168 return -1;
171 return s;
175 OpenAndConfSSDPNotifySockets(int * sockets)
177 int i, j;
178 for(i=0; i<n_lan_addr; i++)
180 sockets[i] = OpenAndConfSSDPNotifySocket(lan_addr[i].addr.s_addr);
181 if(sockets[i] < 0)
183 for(j=0; j<i; j++)
185 close(sockets[j]);
186 sockets[j] = -1;
188 return -1;
191 return 0;
195 * response from a LiveBox (Wanadoo)
196 HTTP/1.1 200 OK
197 CACHE-CONTROL: max-age=1800
198 DATE: Thu, 01 Jan 1970 04:03:23 GMT
199 EXT:
200 LOCATION: http://192.168.0.1:49152/gatedesc.xml
201 SERVER: Linux/2.4.17, UPnP/1.0, Intel SDK for UPnP devices /1.2
202 ST: upnp:rootdevice
203 USN: uuid:75802409-bccb-40e7-8e6c-fa095ecce13e::upnp:rootdevice
205 * response from a Linksys 802.11b :
206 HTTP/1.1 200 OK
207 Cache-Control:max-age=120
208 Location:http://192.168.5.1:5678/rootDesc.xml
209 Server:NT/5.0 UPnP/1.0
210 ST:upnp:rootdevice
211 USN:uuid:upnp-InternetGatewayDevice-1_0-0090a2777777::upnp:rootdevice
212 EXT:
215 static const char * const known_service_types[] =
217 uuidvalue,
218 "upnp:rootdevice",
219 "urn:schemas-upnp-org:device:MediaServer:",
220 "urn:schemas-upnp-org:service:ContentDirectory:",
221 "urn:schemas-upnp-org:service:ConnectionManager:",
222 "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:",
226 static void
227 _usleep(long usecs)
229 struct timespec sleep_time;
231 sleep_time.tv_sec = 0;
232 sleep_time.tv_nsec = usecs * 1000;
233 nanosleep(&sleep_time, NULL);
236 /* not really an SSDP "announce" as it is the response
237 * to a SSDP "M-SEARCH" */
238 static void
239 SendSSDPAnnounce2(int s, struct sockaddr_in sockname, int st_no,
240 const char * host, unsigned short port)
242 int l, n;
243 char buf[512];
244 /* TODO :
245 * follow guideline from document "UPnP Device Architecture 1.0"
246 * put in uppercase.
247 * DATE: is recommended
248 * SERVER: OS/ver UPnP/1.0 minidlna/1.0
249 * - check what to put in the 'Cache-Control' header
250 * */
251 char szTime[30];
252 time_t tTime = time(NULL);
253 strftime(szTime, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&tTime));
255 l = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\n"
256 "CACHE-CONTROL: max-age=%u\r\n"
257 "DATE: %s\r\n"
258 "ST: %s%s\r\n"
259 "USN: %s%s%s%s\r\n"
260 "EXT:\r\n"
261 "SERVER: " MINIDLNA_SERVER_STRING "\r\n"
262 "LOCATION: http://%s:%u" ROOTDESC_PATH "\r\n"
263 "Content-Length: 0\r\n"
264 "\r\n",
265 (runtime_vars.notify_interval<<1)+10,
266 szTime,
267 known_service_types[st_no], (st_no>1?"1":""),
268 uuidvalue, (st_no>0?"::":""), (st_no>0?known_service_types[st_no]:""), (st_no>1?"1":""),
269 host, (unsigned int)port);
270 //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Sending M-SEARCH response:\n%s", buf);
271 n = sendto(s, buf, l, 0,
272 (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
273 if(n < 0)
275 DPRINTF(E_ERROR, L_SSDP, "sendto(udp): %s\n", strerror(errno));
279 static void
280 SendSSDPNotifies(int s, const char * host, unsigned short port,
281 unsigned int lifetime)
283 struct sockaddr_in sockname;
284 int l, n, dup, i=0;
285 char bufr[512];
287 memset(&sockname, 0, sizeof(struct sockaddr_in));
288 sockname.sin_family = AF_INET;
289 sockname.sin_port = htons(SSDP_PORT);
290 sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
292 for( dup=0; dup<2; dup++ )
294 if( dup )
295 _usleep(200000);
296 i=0;
297 while(known_service_types[i])
299 l = snprintf(bufr, sizeof(bufr),
300 "NOTIFY * HTTP/1.1\r\n"
301 "HOST:%s:%d\r\n"
302 "CACHE-CONTROL:max-age=%u\r\n"
303 "LOCATION:http://%s:%d" ROOTDESC_PATH"\r\n"
304 "SERVER: " MINIDLNA_SERVER_STRING "\r\n"
305 "NT:%s%s\r\n"
306 "USN:%s%s%s%s\r\n"
307 "NTS:ssdp:alive\r\n"
308 "\r\n",
309 SSDP_MCAST_ADDR, SSDP_PORT,
310 lifetime,
311 host, port,
312 known_service_types[i], (i>1?"1":""),
313 uuidvalue, (i>0?"::":""), (i>0?known_service_types[i]:""), (i>1?"1":"") );
314 if(l>=sizeof(bufr))
316 DPRINTF(E_WARN, L_SSDP, "SendSSDPNotifies(): truncated output\n");
317 l = sizeof(bufr);
319 //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Sending NOTIFY:\n%s", bufr);
320 n = sendto(s, bufr, l, 0,
321 (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
322 if(n < 0)
324 DPRINTF(E_ERROR, L_SSDP, "sendto(udp_notify=%d, %s): %s\n", s, host, strerror(errno));
326 i++;
331 void
332 SendSSDPNotifies2(int * sockets,
333 unsigned short port,
334 unsigned int lifetime)
335 /*SendSSDPNotifies2(int * sockets, struct lan_addr_s * lan_addr, int n_lan_addr,
336 unsigned short port,
337 unsigned int lifetime)*/
339 int i;
340 DPRINTF(E_DEBUG, L_SSDP, "Sending SSDP notifies\n");
341 for(i=0; i<n_lan_addr; i++)
343 SendSSDPNotifies(sockets[i], lan_addr[i].str, port, lifetime);
347 void
348 ParseUPnPClient(char *location)
350 char buf[8192];
351 struct sockaddr_in dest;
352 int s, n, do_headers = 0, nread = 0;
353 struct timeval tv;
354 char *addr, *path, *port_str;
355 long port = 80;
356 char *off = NULL, *p;
357 int content_len = sizeof(buf);
358 struct NameValueParserData xml;
359 int client;
360 enum client_types type = -1;
361 uint32_t flags = 0;
362 char *model;
364 if (strncmp(location, "http://", 7) != 0)
365 return;
366 path = location + 7;
367 port_str = strsep(&path, "/");
368 if (!path)
369 return;
370 addr = strsep(&port_str, ":");
371 if (port_str)
373 port = strtol(port_str, NULL, 10);
374 if (!port)
375 port = 80;
378 memset(&dest, '\0', sizeof(dest));
379 if (!inet_aton(addr, &dest.sin_addr))
380 return;
381 /* Check if the client is already in cache */
382 dest.sin_family = AF_INET;
383 dest.sin_port = htons(port);
385 s = socket(PF_INET, SOCK_STREAM, 0);
386 if( s < 0 )
387 return;
389 tv.tv_sec = 0;
390 tv.tv_usec = 500000;
391 setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
392 setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
394 if( connect(s, (struct sockaddr*)&dest, sizeof(struct sockaddr_in)) < 0 )
395 goto close;
397 n = snprintf(buf, sizeof(buf), "GET /%s HTTP/1.0\r\n"
398 "HOST: %s:%ld\r\n\r\n",
399 path, addr, port);
400 if( write(s, buf, n) < 1 )
401 goto close;
403 while( (n = read(s, buf+nread, sizeof(buf)-nread-1)) > 0 )
405 nread += n;
406 buf[nread] = '\0';
407 n = nread;
408 p = buf;
410 while( !off && n-- > 0 )
412 if(p[0]=='\r' && p[1]=='\n' && p[2]=='\r' && p[3]=='\n')
414 off = p + 4;
415 do_headers = 1;
417 p++;
419 if( !off )
420 continue;
422 if( do_headers )
424 p = buf;
425 if( strncmp(p, "HTTP/", 5) != 0 )
426 goto close;
427 while(*p != ' ' && *p != '\t') p++;
428 /* If we don't get a 200 status, ignore it */
429 if( strtol(p, NULL, 10) != 200 )
430 goto close;
431 if( (p = strcasestr(p, "Content-Length:")) )
432 content_len = strtol(p+15, NULL, 10);
433 do_headers = 0;
435 if( buf + nread - off >= content_len )
436 break;
438 close:
439 close(s);
440 if( !off )
441 return;
442 nread -= off - buf;
443 ParseNameValue(off, nread, &xml);
444 model = GetValueFromNameValueList(&xml, "modelName");
445 if( model )
447 DPRINTF(E_DEBUG, L_SSDP, "Model: %s\n", model);
448 if (strstr(model, "Roku SoundBridge"))
450 type = ERokuSoundBridge;
451 flags |= FLAG_AUDIO_ONLY;
455 if( type < 0 )
456 return;
457 client = SearchClientCache(dest.sin_addr, 1);
458 /* Add this client to the cache if it's not there already. */
459 if( client < 0 )
461 for( client=0; client<CLIENT_CACHE_SLOTS; client++ )
463 if( clients[client].addr.s_addr )
464 continue;
465 get_remote_mac(dest.sin_addr, clients[client].mac);
466 clients[client].addr = dest.sin_addr;
467 DPRINTF(E_DEBUG, L_SSDP, "Added client [%d/%s/%02X:%02X:%02X:%02X:%02X:%02X] to cache slot %d.\n",
468 type, inet_ntoa(clients[client].addr),
469 clients[client].mac[0], clients[client].mac[1], clients[client].mac[2],
470 clients[client].mac[3], clients[client].mac[4], clients[client].mac[5], client);
471 break;
474 clients[client].type = type;
475 clients[client].flags = flags;
476 clients[client].age = time(NULL);
479 /* ProcessSSDPRequest()
480 * process SSDP M-SEARCH requests and responds to them */
481 void
482 ProcessSSDPRequest(int s, unsigned short port)
483 /*ProcessSSDPRequest(int s, struct lan_addr_s * lan_addr, int n_lan_addr,
484 unsigned short port)*/
486 int n;
487 char bufr[1500];
488 socklen_t len_r;
489 struct sockaddr_in sendername;
490 int i;
491 char *st = NULL, *mx = NULL, *man = NULL, *mx_end = NULL, *loc = NULL, *srv = NULL;
492 int man_len = 0;
493 len_r = sizeof(struct sockaddr_in);
495 n = recvfrom(s, bufr, sizeof(bufr)-1, 0,
496 (struct sockaddr *)&sendername, &len_r);
497 if(n < 0)
499 DPRINTF(E_ERROR, L_SSDP, "recvfrom(udp): %s\n", strerror(errno));
500 return;
502 bufr[n] = '\0';
504 if(memcmp(bufr, "NOTIFY", 6) == 0)
506 int loc_len = 0, srv_len = 0;
507 //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Received SSDP notify:\n%.*s", n, bufr);
508 for(i=0; i < n; i++)
510 if( bufr[i] == '*' )
511 break;
513 if( !strcasestr(bufr+i, "HTTP/1.1") )
515 return;
517 while(i < n)
519 while((i < n - 2) && (bufr[i] != '\r' || bufr[i+1] != '\n'))
520 i++;
521 i += 2;
522 if(strncasecmp(bufr+i, "SERVER:", 7) == 0)
524 srv = bufr+i+7;
525 srv_len = 0;
526 while(*srv == ' ' || *srv == '\t') srv++;
527 while(srv[srv_len]!='\r' && srv[srv_len]!='\n') srv_len++;
529 else if(strncasecmp(bufr+i, "LOCATION:", 9) == 0)
531 loc = bufr+i+9;
532 loc_len = 0;
533 while(*loc == ' ' || *loc == '\t') loc++;
534 while(loc[loc_len]!='\r' && loc[loc_len]!='\n') loc_len++;
535 loc[loc_len] = '\0';
537 else if(strncasecmp(bufr+i, "NTS:", 4) == 0)
539 man = bufr+i+4;
540 man_len = 0;
541 while(*man == ' ' || *man == '\t') man++;
542 while(man[man_len]!='\r' && man[man_len]!='\n') man_len++;
545 if (!loc || !srv || !man || (strncmp(man, "ssdp:alive", man_len) != 0))
547 return;
549 if (strncmp(srv, "Allegro-Software-RomPlug", 24) == 0)
551 /* Check if the client is already in cache */
552 i = SearchClientCache(sendername.sin_addr, 1);
553 if( i >= 0 && clients[i].type < EStandardDLNA150 )
555 clients[i].age = time(NULL);
556 return;
558 ParseUPnPClient(loc);
560 return;
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, bufr);
566 for(i=0; i < n; i++)
568 if( bufr[i] == '*' )
569 break;
571 if( !strcasestr(bufr+i, "HTTP/1.1") )
573 return;
575 while(i < n)
577 while((i < n - 2) && (bufr[i] != '\r' || bufr[i+1] != '\n'))
578 i++;
579 i += 2;
580 if(strncasecmp(bufr+i, "ST:", 3) == 0)
582 st = bufr+i+3;
583 st_len = 0;
584 while(*st == ' ' || *st == '\t') st++;
585 while(st[st_len]!='\r' && st[st_len]!='\n') 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') mx++;
592 while(mx[mx_len]!='\r' && mx[mx_len]!='\n') mx_len++;
593 mx_val = strtol(mx, &mx_end, 10);
595 else if(strncasecmp(bufr+i, "MAN:", 4) == 0)
597 man = bufr+i+4;
598 man_len = 0;
599 while(*man == ' ' || *man == '\t') man++;
600 while(man[man_len]!='\r' && man[man_len]!='\n') man_len++;
603 /*DPRINTF(E_INFO, L_SSDP, "SSDP M-SEARCH packet received from %s:%d\n",
604 inet_ntoa(sendername.sin_addr),
605 ntohs(sendername.sin_port) );*/
606 if( ntohs(sendername.sin_port) <= 1024 || ntohs(sendername.sin_port) == 1900 )
608 DPRINTF(E_INFO, L_SSDP, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad source port %d]\n",
609 inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port));
611 else if( !man || (strncmp(man, "\"ssdp:discover\"", 15) != 0) )
613 DPRINTF(E_INFO, L_SSDP, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad MAN header %.*s]\n",
614 inet_ntoa(sendername.sin_addr), man_len, man);
616 else if( !mx || mx == mx_end || mx_val < 0 ) {
617 DPRINTF(E_INFO, L_SSDP, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad MX header %.*s]\n",
618 inet_ntoa(sendername.sin_addr), mx_len, mx);
620 else if( st && (st_len > 0) )
622 int l;
623 int lan_addr_index = 0;
624 /* find in which sub network the client is */
625 for(i = 0; i<n_lan_addr; i++)
627 if( (sendername.sin_addr.s_addr & lan_addr[i].mask.s_addr)
628 == (lan_addr[i].addr.s_addr & lan_addr[i].mask.s_addr))
630 lan_addr_index = i;
631 break;
634 if( i == n_lan_addr )
636 DPRINTF(E_DEBUG, L_SSDP, "Ignoring SSDP M-SEARCH on other interface [%s]\n",
637 inet_ntoa(sendername.sin_addr));
638 return;
640 DPRINTF(E_INFO, L_SSDP, "SSDP M-SEARCH from %s:%d ST: %.*s, MX: %.*s, MAN: %.*s\n",
641 inet_ntoa(sendername.sin_addr),
642 ntohs(sendername.sin_port),
643 st_len, st, mx_len, mx, man_len, man);
644 /* Responds to request with a device as ST header */
645 for(i = 0; known_service_types[i]; i++)
647 l = strlen(known_service_types[i]);
648 if(l<=st_len && (0 == memcmp(st, known_service_types[i], l)))
650 /* Check version number - must always be 1 currently. */
651 if( (st[st_len-2] == ':') && (atoi(st+st_len-1) != 1) )
652 break;
653 _usleep(random()>>20);
654 SendSSDPAnnounce2(s, sendername,
656 lan_addr[lan_addr_index].str, port);
657 break;
660 /* Responds to request with ST: ssdp:all */
661 /* strlen("ssdp:all") == 8 */
662 if(st_len==8 && (0 == memcmp(st, "ssdp:all", 8)))
664 for(i=0; known_service_types[i]; i++)
666 l = (int)strlen(known_service_types[i]);
667 SendSSDPAnnounce2(s, sendername,
669 lan_addr[lan_addr_index].str, port);
673 else
675 DPRINTF(E_INFO, L_SSDP, "Invalid SSDP M-SEARCH from %s:%d\n",
676 inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port));
679 else
681 DPRINTF(E_WARN, L_SSDP, "Unknown udp packet received from %s:%d\n",
682 inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port));
686 /* This will broadcast ssdp:byebye notifications to inform
687 * the network that UPnP is going down. */
689 SendSSDPGoodbye(int * sockets, int n_sockets)
691 struct sockaddr_in sockname;
692 int n, l;
693 int i, j;
694 char bufr[512];
696 memset(&sockname, 0, sizeof(struct sockaddr_in));
697 sockname.sin_family = AF_INET;
698 sockname.sin_port = htons(SSDP_PORT);
699 sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
701 for(j=0; j<n_sockets; j++)
703 for(i=0; known_service_types[i]; i++)
705 l = snprintf(bufr, sizeof(bufr),
706 "NOTIFY * HTTP/1.1\r\n"
707 "HOST:%s:%d\r\n"
708 "NT:%s%s\r\n"
709 "USN:%s%s%s%s\r\n"
710 "NTS:ssdp:byebye\r\n"
711 "\r\n",
712 SSDP_MCAST_ADDR, SSDP_PORT,
713 known_service_types[i], (i>1?"1":""),
714 uuidvalue, (i>0?"::":""), (i>0?known_service_types[i]:""), (i>1?"1":"") );
715 //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Sending NOTIFY:\n%s", bufr);
716 n = sendto(sockets[j], bufr, l, 0,
717 (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
718 if(n < 0)
720 DPRINTF(E_ERROR, L_SSDP, "sendto(udp_shutdown=%d): %s\n", sockets[j], strerror(errno));
721 return -1;
725 return 0;