MiniDLNA 1.0.19 cvs 2011-03-15
[tomato.git] / release / src / router / minidlna / minissdp.c
blob2d0827f9cdebfa37568ef4a51661be185fc7d238
1 /* $Id: minissdp.c,v 1.14 2011/03/15 02:20:58 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 "minissdp.h"
45 #include "log.h"
47 /* SSDP ip/port */
48 #define SSDP_PORT (1900)
49 #define SSDP_MCAST_ADDR ("239.255.255.250")
51 static int
52 AddMulticastMembership(int s, in_addr_t ifaddr/*const char * ifaddr*/)
54 struct ip_mreq imr; /* Ip multicast membership */
56 /* setting up imr structure */
57 imr.imr_multiaddr.s_addr = inet_addr(SSDP_MCAST_ADDR);
58 /*imr.imr_interface.s_addr = htonl(INADDR_ANY);*/
59 imr.imr_interface.s_addr = ifaddr; /*inet_addr(ifaddr);*/
61 if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&imr, sizeof(struct ip_mreq)) < 0)
63 DPRINTF(E_ERROR, L_SSDP, "setsockopt(udp, IP_ADD_MEMBERSHIP): %s\n", strerror(errno));
64 return -1;
67 return 0;
70 int
71 OpenAndConfSSDPReceiveSocket()
73 int s;
74 int i = 1;
75 struct sockaddr_in sockname;
77 if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
79 DPRINTF(E_ERROR, L_SSDP, "socket(udp): %s\n", strerror(errno));
80 return -1;
83 if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0)
85 DPRINTF(E_WARN, L_SSDP, "setsockopt(udp, SO_REUSEADDR): %s\n", strerror(errno));
88 memset(&sockname, 0, sizeof(struct sockaddr_in));
89 sockname.sin_family = AF_INET;
90 sockname.sin_port = htons(SSDP_PORT);
91 /* NOTE : it seems it doesnt work when binding on the specific address */
92 /*sockname.sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);*/
93 sockname.sin_addr.s_addr = htonl(INADDR_ANY);
94 /*sockname.sin_addr.s_addr = inet_addr(ifaddr);*/
96 if(bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
98 DPRINTF(E_ERROR, L_SSDP, "bind(udp): %s\n", strerror(errno));
99 close(s);
100 return -1;
103 i = n_lan_addr;
104 while(i>0)
106 i--;
107 if(AddMulticastMembership(s, lan_addr[i].addr.s_addr) < 0)
109 DPRINTF(E_WARN, L_SSDP,
110 "Failed to add multicast membership for address %s\n",
111 lan_addr[i].str );
115 return s;
118 /* open the UDP socket used to send SSDP notifications to
119 * the multicast group reserved for them */
120 static int
121 OpenAndConfSSDPNotifySocket(in_addr_t addr)
123 int s;
124 unsigned char loopchar = 0;
125 int bcast = 1;
126 struct in_addr mc_if;
127 struct sockaddr_in sockname;
129 if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
131 DPRINTF(E_ERROR, L_SSDP, "socket(udp_notify): %s\n", strerror(errno));
132 return -1;
135 mc_if.s_addr = addr; /*inet_addr(addr);*/
137 if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loopchar, sizeof(loopchar)) < 0)
139 DPRINTF(E_ERROR, L_SSDP, "setsockopt(udp_notify, IP_MULTICAST_LOOP): %s\n", strerror(errno));
140 close(s);
141 return -1;
144 if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&mc_if, sizeof(mc_if)) < 0)
146 DPRINTF(E_ERROR, L_SSDP, "setsockopt(udp_notify, IP_MULTICAST_IF): %s\n", strerror(errno));
147 close(s);
148 return -1;
151 if(setsockopt(s, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bcast)) < 0)
153 DPRINTF(E_ERROR, L_SSDP, "setsockopt(udp_notify, SO_BROADCAST): %s\n", strerror(errno));
154 close(s);
155 return -1;
158 memset(&sockname, 0, sizeof(struct sockaddr_in));
159 sockname.sin_family = AF_INET;
160 sockname.sin_addr.s_addr = addr; /*inet_addr(addr);*/
162 if (bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
164 DPRINTF(E_ERROR, L_SSDP, "bind(udp_notify): %s\n", strerror(errno));
165 close(s);
166 return -1;
169 return s;
173 OpenAndConfSSDPNotifySockets(int * sockets)
175 int i, j;
176 for(i=0; i<n_lan_addr; i++)
178 sockets[i] = OpenAndConfSSDPNotifySocket(lan_addr[i].addr.s_addr);
179 if(sockets[i] < 0)
181 for(j=0; j<i; j++)
183 close(sockets[j]);
184 sockets[j] = -1;
186 return -1;
189 return 0;
193 * response from a LiveBox (Wanadoo)
194 HTTP/1.1 200 OK
195 CACHE-CONTROL: max-age=1800
196 DATE: Thu, 01 Jan 1970 04:03:23 GMT
197 EXT:
198 LOCATION: http://192.168.0.1:49152/gatedesc.xml
199 SERVER: Linux/2.4.17, UPnP/1.0, Intel SDK for UPnP devices /1.2
200 ST: upnp:rootdevice
201 USN: uuid:75802409-bccb-40e7-8e6c-fa095ecce13e::upnp:rootdevice
203 * response from a Linksys 802.11b :
204 HTTP/1.1 200 OK
205 Cache-Control:max-age=120
206 Location:http://192.168.5.1:5678/rootDesc.xml
207 Server:NT/5.0 UPnP/1.0
208 ST:upnp:rootdevice
209 USN:uuid:upnp-InternetGatewayDevice-1_0-0090a2777777::upnp:rootdevice
210 EXT:
213 static const char * const known_service_types[] =
215 uuidvalue,
216 "upnp:rootdevice",
217 "urn:schemas-upnp-org:device:MediaServer:",
218 "urn:schemas-upnp-org:service:ContentDirectory:",
219 "urn:schemas-upnp-org:service:ConnectionManager:",
220 "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:",
224 /* not really an SSDP "announce" as it is the response
225 * to a SSDP "M-SEARCH" */
226 static void
227 SendSSDPAnnounce2(int s, struct sockaddr_in sockname, int st_no,
228 const char * host, unsigned short port)
230 int l, n;
231 char buf[512];
232 /* TODO :
233 * follow guideline from document "UPnP Device Architecture 1.0"
234 * put in uppercase.
235 * DATE: is recommended
236 * SERVER: OS/ver UPnP/1.0 minidlna/1.0
237 * - check what to put in the 'Cache-Control' header
238 * */
239 char szTime[30];
240 time_t tTime = time(NULL);
241 strftime(szTime, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&tTime));
243 l = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\n"
244 "CACHE-CONTROL: max-age=%u\r\n"
245 "DATE: %s\r\n"
246 "ST: %s%s\r\n"
247 "USN: %s%s%s%s\r\n"
248 "EXT:\r\n"
249 "SERVER: " MINIDLNA_SERVER_STRING "\r\n"
250 "LOCATION: http://%s:%u" ROOTDESC_PATH "\r\n"
251 "Content-Length: 0\r\n"
252 "\r\n",
253 (runtime_vars.notify_interval<<1)+10,
254 szTime,
255 known_service_types[st_no], (st_no>1?"1":""),
256 uuidvalue, (st_no>0?"::":""), (st_no>0?known_service_types[st_no]:""), (st_no>1?"1":""),
257 host, (unsigned int)port);
258 //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Sending M-SEARCH response:\n%s", buf);
259 n = sendto(s, buf, l, 0,
260 (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
261 if(n < 0)
263 DPRINTF(E_ERROR, L_SSDP, "sendto(udp): %s\n", strerror(errno));
267 static void
268 SendSSDPNotifies(int s, const char * host, unsigned short port,
269 unsigned int lifetime)
271 struct sockaddr_in sockname;
272 int l, n, dup, i=0;
273 char bufr[512];
275 memset(&sockname, 0, sizeof(struct sockaddr_in));
276 sockname.sin_family = AF_INET;
277 sockname.sin_port = htons(SSDP_PORT);
278 sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
280 for( dup=0; dup<2; dup++ )
282 if( dup )
283 usleep(200000);
284 i=0;
285 while(known_service_types[i])
287 l = snprintf(bufr, sizeof(bufr),
288 "NOTIFY * HTTP/1.1\r\n"
289 "HOST:%s:%d\r\n"
290 "CACHE-CONTROL:max-age=%u\r\n"
291 "LOCATION:http://%s:%d" ROOTDESC_PATH"\r\n"
292 "SERVER: " MINIDLNA_SERVER_STRING "\r\n"
293 "NT:%s%s\r\n"
294 "USN:%s%s%s%s\r\n"
295 "NTS:ssdp:alive\r\n"
296 "\r\n",
297 SSDP_MCAST_ADDR, SSDP_PORT,
298 lifetime,
299 host, port,
300 known_service_types[i], (i>1?"1":""),
301 uuidvalue, (i>0?"::":""), (i>0?known_service_types[i]:""), (i>1?"1":"") );
302 if(l>=sizeof(bufr))
304 DPRINTF(E_WARN, L_SSDP, "SendSSDPNotifies(): truncated output\n");
305 l = sizeof(bufr);
307 //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Sending NOTIFY:\n%s", bufr);
308 n = sendto(s, bufr, l, 0,
309 (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
310 if(n < 0)
312 DPRINTF(E_ERROR, L_SSDP, "sendto(udp_notify=%d, %s): %s\n", s, host, strerror(errno));
314 i++;
319 void
320 SendSSDPNotifies2(int * sockets,
321 unsigned short port,
322 unsigned int lifetime)
323 /*SendSSDPNotifies2(int * sockets, struct lan_addr_s * lan_addr, int n_lan_addr,
324 unsigned short port,
325 unsigned int lifetime)*/
327 int i;
328 DPRINTF(E_DEBUG, L_SSDP, "Sending SSDP notifies\n");
329 for(i=0; i<n_lan_addr; i++)
331 SendSSDPNotifies(sockets[i], lan_addr[i].str, port, lifetime);
335 /* ProcessSSDPRequest()
336 * process SSDP M-SEARCH requests and responds to them */
337 void
338 ProcessSSDPRequest(int s, unsigned short port)
339 /*ProcessSSDPRequest(int s, struct lan_addr_s * lan_addr, int n_lan_addr,
340 unsigned short port)*/
342 int n;
343 char bufr[1500];
344 socklen_t len_r;
345 struct sockaddr_in sendername;
346 int i, l;
347 int lan_addr_index = 0;
348 char * st = NULL, * mx = NULL, * man = NULL, * mx_end = NULL;
349 int st_len = 0, mx_len = 0, man_len = 0, mx_val = 0;
350 len_r = sizeof(struct sockaddr_in);
352 n = recvfrom(s, bufr, sizeof(bufr), 0,
353 (struct sockaddr *)&sendername, &len_r);
354 if(n < 0)
356 DPRINTF(E_ERROR, L_SSDP, "recvfrom(udp): %s\n", strerror(errno));
357 return;
360 if(memcmp(bufr, "NOTIFY", 6) == 0)
362 /* ignore NOTIFY packets. We could log the sender and device type */
363 return;
365 else if(memcmp(bufr, "M-SEARCH", 8) == 0)
367 //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Received SSDP request:\n%.*s", n, bufr);
368 for(i=0; i < n; i++)
370 if( bufr[i] == '*' )
371 break;
373 if( !strcasestr(bufr+i, "HTTP/1.1") )
375 return;
377 while(i < n)
379 while((i < n - 1) && (bufr[i] != '\r' || bufr[i+1] != '\n'))
380 i++;
381 i += 2;
382 if((i < n - 3) && (strncasecmp(bufr+i, "ST:", 3) == 0))
384 st = bufr+i+3;
385 st_len = 0;
386 while(*st == ' ' || *st == '\t') st++;
387 while(st[st_len]!='\r' && st[st_len]!='\n') st_len++;
389 else if(strncasecmp(bufr+i, "MX:", 3) == 0)
391 mx = bufr+i+3;
392 mx_len = 0;
393 while(*mx == ' ' || *mx == '\t') mx++;
394 while(mx[mx_len]!='\r' && mx[mx_len]!='\n') mx_len++;
395 mx_val = strtol(mx, &mx_end, 10);
397 else if(strncasecmp(bufr+i, "MAN:", 4) == 0)
399 man = bufr+i+4;
400 man_len = 0;
401 while(*man == ' ' || *man == '\t') man++;
402 while(man[man_len]!='\r' && man[man_len]!='\n') man_len++;
405 /*DPRINTF(E_INFO, L_SSDP, "SSDP M-SEARCH packet received from %s:%d\n",
406 inet_ntoa(sendername.sin_addr),
407 ntohs(sendername.sin_port) );*/
408 if( ntohs(sendername.sin_port) <= 1024 || ntohs(sendername.sin_port) == 1900 )
410 DPRINTF(E_INFO, L_SSDP, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad source port %d]\n",
411 inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port));
413 else if( !man || (strncmp(man, "\"ssdp:discover\"", 15) != 0) )
415 DPRINTF(E_INFO, L_SSDP, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad MAN header %.*s]\n",
416 inet_ntoa(sendername.sin_addr), man_len, man);
418 else if( !mx || mx == mx_end || mx_val < 0 ) {
419 DPRINTF(E_INFO, L_SSDP, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad MX header %.*s]\n",
420 inet_ntoa(sendername.sin_addr), mx_len, mx);
422 else if( st && (st_len > 0) )
424 /* find in which sub network the client is */
425 for(i = 0; i<n_lan_addr; i++)
427 if( (sendername.sin_addr.s_addr & lan_addr[i].mask.s_addr)
428 == (lan_addr[i].addr.s_addr & lan_addr[i].mask.s_addr))
430 lan_addr_index = i;
431 break;
434 if( i == n_lan_addr )
436 DPRINTF(E_DEBUG, L_SSDP, "Ignoring SSDP M-SEARCH on other interface [%s]\n",
437 inet_ntoa(sendername.sin_addr));
438 return;
440 DPRINTF(E_INFO, L_SSDP, "SSDP M-SEARCH from %s:%d ST: %.*s, MX: %.*s, MAN: %.*s\n",
441 inet_ntoa(sendername.sin_addr),
442 ntohs(sendername.sin_port),
443 st_len, st, mx_len, mx, man_len, man);
444 /* Responds to request with a device as ST header */
445 for(i = 0; known_service_types[i]; i++)
447 l = strlen(known_service_types[i]);
448 if(l<=st_len && (0 == memcmp(st, known_service_types[i], l)))
450 /* Check version number - must always be 1 currently. */
451 if( (st[st_len-2] == ':') && (atoi(st+st_len-1) != 1) )
452 break;
453 usleep(random()>>20);
454 SendSSDPAnnounce2(s, sendername,
456 lan_addr[lan_addr_index].str, port);
457 break;
460 /* Responds to request with ST: ssdp:all */
461 /* strlen("ssdp:all") == 8 */
462 if(st_len==8 && (0 == memcmp(st, "ssdp:all", 8)))
464 for(i=0; known_service_types[i]; i++)
466 l = (int)strlen(known_service_types[i]);
467 SendSSDPAnnounce2(s, sendername,
469 lan_addr[lan_addr_index].str, port);
473 else
475 DPRINTF(E_INFO, L_SSDP, "Invalid SSDP M-SEARCH from %s:%d\n",
476 inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port));
479 else
481 DPRINTF(E_WARN, L_SSDP, "Unknown udp packet received from %s:%d\n",
482 inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port));
486 /* This will broadcast ssdp:byebye notifications to inform
487 * the network that UPnP is going down. */
489 SendSSDPGoodbye(int * sockets, int n_sockets)
491 struct sockaddr_in sockname;
492 int n, l;
493 int i, j;
494 char bufr[512];
496 memset(&sockname, 0, sizeof(struct sockaddr_in));
497 sockname.sin_family = AF_INET;
498 sockname.sin_port = htons(SSDP_PORT);
499 sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
501 for(j=0; j<n_sockets; j++)
503 for(i=0; known_service_types[i]; i++)
505 l = snprintf(bufr, sizeof(bufr),
506 "NOTIFY * HTTP/1.1\r\n"
507 "HOST:%s:%d\r\n"
508 "NT:%s%s\r\n"
509 "USN:%s%s%s%s\r\n"
510 "NTS:ssdp:byebye\r\n"
511 "\r\n",
512 SSDP_MCAST_ADDR, SSDP_PORT,
513 known_service_types[i], (i>1?"1":""),
514 uuidvalue, (i>0?"::":""), (i>0?known_service_types[i]:""), (i>1?"1":"") );
515 //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Sending NOTIFY:\n%s", bufr);
516 n = sendto(sockets[j], bufr, l, 0,
517 (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
518 if(n < 0)
520 DPRINTF(E_ERROR, L_SSDP, "sendto(udp_shutdown=%d): %s\n", sockets[j], strerror(errno));
521 return -1;
525 return 0;