1 /* $Id: minissdp.c,v 1.18 2011/05/03 06:14:25 jmaggard Exp $ */
3 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
5 * Copyright (c) 2006, Thomas Bernard
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.
34 #include <sys/socket.h>
35 #include <netinet/in.h>
36 #include <arpa/inet.h>
40 #include "upnpdescstrings.h"
41 #include "minidlnapath.h"
43 #include "upnpglobalvars.h"
44 #include "upnpreplyparse.h"
45 #include "getifaddr.h"
50 #define SSDP_PORT (1900)
51 #define SSDP_MCAST_ADDR ("239.255.255.250")
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
));
73 OpenAndConfSSDPReceiveSocket()
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
));
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
));
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",
120 /* open the UDP socket used to send SSDP notifications to
121 * the multicast group reserved for them */
123 OpenAndConfSSDPNotifySocket(in_addr_t addr
)
126 unsigned char loopchar
= 0;
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
));
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
));
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
));
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
));
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
));
175 OpenAndConfSSDPNotifySockets(int * sockets
)
178 for(i
=0; i
<n_lan_addr
; i
++)
180 sockets
[i
] = OpenAndConfSSDPNotifySocket(lan_addr
[i
].addr
.s_addr
);
195 * response from a LiveBox (Wanadoo)
197 CACHE-CONTROL: max-age=1800
198 DATE: Thu, 01 Jan 1970 04:03:23 GMT
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
203 USN: uuid:75802409-bccb-40e7-8e6c-fa095ecce13e::upnp:rootdevice
205 * response from a Linksys 802.11b :
207 Cache-Control:max-age=120
208 Location:http://192.168.5.1:5678/rootDesc.xml
209 Server:NT/5.0 UPnP/1.0
211 USN:uuid:upnp-InternetGatewayDevice-1_0-0090a2777777::upnp:rootdevice
215 static const char * const known_service_types
[] =
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:",
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" */
239 SendSSDPAnnounce2(int s
, struct sockaddr_in sockname
, int st_no
,
240 const char * host
, unsigned short port
)
245 * follow guideline from document "UPnP Device Architecture 1.0"
247 * DATE: is recommended
248 * SERVER: OS/ver UPnP/1.0 minidlna/1.0
249 * - check what to put in the 'Cache-Control' header
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"
261 "SERVER: " MINIDLNA_SERVER_STRING
"\r\n"
262 "LOCATION: http://%s:%u" ROOTDESC_PATH
"\r\n"
263 "Content-Length: 0\r\n"
265 (runtime_vars
.notify_interval
<<1)+10,
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
) );
275 DPRINTF(E_ERROR
, L_SSDP
, "sendto(udp): %s\n", strerror(errno
));
280 SendSSDPNotifies(int s
, const char * host
, unsigned short port
,
281 unsigned int lifetime
)
283 struct sockaddr_in sockname
;
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
++ )
297 while(known_service_types
[i
])
299 l
= snprintf(bufr
, sizeof(bufr
),
300 "NOTIFY * HTTP/1.1\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"
309 SSDP_MCAST_ADDR
, SSDP_PORT
,
312 known_service_types
[i
], (i
>1?"1":""),
313 uuidvalue
, (i
>0?"::":""), (i
>0?known_service_types
[i
]:""), (i
>1?"1":"") );
316 DPRINTF(E_WARN
, L_SSDP
, "SendSSDPNotifies(): truncated output\n");
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
) );
324 DPRINTF(E_ERROR
, L_SSDP
, "sendto(udp_notify=%d, %s): %s\n", s
, host
, strerror(errno
));
332 SendSSDPNotifies2(int * sockets
,
334 unsigned int lifetime
)
335 /*SendSSDPNotifies2(int * sockets, struct lan_addr_s * lan_addr, int n_lan_addr,
337 unsigned int lifetime)*/
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
);
348 ParseUPnPClient(char *location
)
351 struct sockaddr_in dest
;
352 int s
, n
, do_headers
= 0, nread
= 0;
354 char *addr
, *path
, *port_str
;
356 char *off
= NULL
, *p
;
357 int content_len
= sizeof(buf
);
358 struct NameValueParserData xml
;
360 enum client_types type
= -1;
364 if (strncmp(location
, "http://", 7) != 0)
367 port_str
= strsep(&path
, "/");
370 addr
= strsep(&port_str
, ":");
373 port
= strtol(port_str
, NULL
, 10);
378 memset(&dest
, '\0', sizeof(dest
));
379 if (!inet_aton(addr
, &dest
.sin_addr
))
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);
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 )
397 n
= snprintf(buf
, sizeof(buf
), "GET /%s HTTP/1.0\r\n"
398 "HOST: %s:%ld\r\n\r\n",
400 if( write(s
, buf
, n
) < 1 )
403 while( (n
= read(s
, buf
+nread
, sizeof(buf
)-nread
-1)) > 0 )
410 while( !off
&& n
-- > 0 )
412 if(p
[0]=='\r' && p
[1]=='\n' && p
[2]=='\r' && p
[3]=='\n')
425 if( strncmp(p
, "HTTP/", 5) != 0 )
427 while(*p
!= ' ' && *p
!= '\t') p
++;
428 /* If we don't get a 200 status, ignore it */
429 if( strtol(p
, NULL
, 10) != 200 )
431 if( (p
= strcasestr(p
, "Content-Length:")) )
432 content_len
= strtol(p
+15, NULL
, 10);
435 if( buf
+ nread
- off
>= content_len
)
443 ParseNameValue(off
, nread
, &xml
);
444 model
= GetValueFromNameValueList(&xml
, "modelName");
447 DPRINTF(E_DEBUG
, L_SSDP
, "Model: %s\n", model
);
448 if (strstr(model
, "Roku SoundBridge"))
450 type
= ERokuSoundBridge
;
451 flags
|= FLAG_AUDIO_ONLY
;
457 client
= SearchClientCache(dest
.sin_addr
, 1);
458 /* Add this client to the cache if it's not there already. */
461 for( client
=0; client
<CLIENT_CACHE_SLOTS
; client
++ )
463 if( clients
[client
].addr
.s_addr
)
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
);
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 */
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)*/
489 struct sockaddr_in sendername
;
491 char *st
= NULL
, *mx
= NULL
, *man
= NULL
, *mx_end
= NULL
, *loc
= NULL
, *srv
= NULL
;
493 len_r
= sizeof(struct sockaddr_in
);
495 n
= recvfrom(s
, bufr
, sizeof(bufr
)-1, 0,
496 (struct sockaddr
*)&sendername
, &len_r
);
499 DPRINTF(E_ERROR
, L_SSDP
, "recvfrom(udp): %s\n", strerror(errno
));
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);
513 if( !strcasestr(bufr
+i
, "HTTP/1.1") )
519 while((i
< n
- 2) && (bufr
[i
] != '\r' || bufr
[i
+1] != '\n'))
522 if(strncasecmp(bufr
+i
, "SERVER:", 7) == 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)
533 while(*loc
== ' ' || *loc
== '\t') loc
++;
534 while(loc
[loc_len
]!='\r' && loc
[loc_len
]!='\n') loc_len
++;
537 else if(strncasecmp(bufr
+i
, "NTS:", 4) == 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))
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
);
558 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, bufr);
571 if( !strcasestr(bufr
+i
, "HTTP/1.1") )
577 while((i
< n
- 2) && (bufr
[i
] != '\r' || bufr
[i
+1] != '\n'))
580 if(strncasecmp(bufr
+i
, "ST:", 3) == 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)
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)
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) )
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
))
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
));
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) )
653 _usleep(random()>>20);
654 SendSSDPAnnounce2(s
, sendername
,
656 lan_addr
[lan_addr_index
].str
, port
);
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
);
675 DPRINTF(E_INFO
, L_SSDP
, "Invalid SSDP M-SEARCH from %s:%d\n",
676 inet_ntoa(sendername
.sin_addr
), ntohs(sendername
.sin_port
));
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
;
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"
710 "NTS:ssdp:byebye\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
) );
720 DPRINTF(E_ERROR
, L_SSDP
, "sendto(udp_shutdown=%d): %s\n", sockets
[j
], strerror(errno
));