1 /* $Id: minissdp.c,v 1.14 2011/03/15 02:20:58 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"
48 #define SSDP_PORT (1900)
49 #define SSDP_MCAST_ADDR ("239.255.255.250")
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
));
71 OpenAndConfSSDPReceiveSocket()
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
));
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
));
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",
118 /* open the UDP socket used to send SSDP notifications to
119 * the multicast group reserved for them */
121 OpenAndConfSSDPNotifySocket(in_addr_t addr
)
124 unsigned char loopchar
= 0;
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
));
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
));
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
));
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
));
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
));
173 OpenAndConfSSDPNotifySockets(int * sockets
)
176 for(i
=0; i
<n_lan_addr
; i
++)
178 sockets
[i
] = OpenAndConfSSDPNotifySocket(lan_addr
[i
].addr
.s_addr
);
193 * response from a LiveBox (Wanadoo)
195 CACHE-CONTROL: max-age=1800
196 DATE: Thu, 01 Jan 1970 04:03:23 GMT
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
201 USN: uuid:75802409-bccb-40e7-8e6c-fa095ecce13e::upnp:rootdevice
203 * response from a Linksys 802.11b :
205 Cache-Control:max-age=120
206 Location:http://192.168.5.1:5678/rootDesc.xml
207 Server:NT/5.0 UPnP/1.0
209 USN:uuid:upnp-InternetGatewayDevice-1_0-0090a2777777::upnp:rootdevice
213 static const char * const known_service_types
[] =
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" */
227 SendSSDPAnnounce2(int s
, struct sockaddr_in sockname
, int st_no
,
228 const char * host
, unsigned short port
)
233 * follow guideline from document "UPnP Device Architecture 1.0"
235 * DATE: is recommended
236 * SERVER: OS/ver UPnP/1.0 minidlna/1.0
237 * - check what to put in the 'Cache-Control' header
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"
249 "SERVER: " MINIDLNA_SERVER_STRING
"\r\n"
250 "LOCATION: http://%s:%u" ROOTDESC_PATH
"\r\n"
251 "Content-Length: 0\r\n"
253 (runtime_vars
.notify_interval
<<1)+10,
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
) );
263 DPRINTF(E_ERROR
, L_SSDP
, "sendto(udp): %s\n", strerror(errno
));
268 SendSSDPNotifies(int s
, const char * host
, unsigned short port
,
269 unsigned int lifetime
)
271 struct sockaddr_in sockname
;
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
++ )
285 while(known_service_types
[i
])
287 l
= snprintf(bufr
, sizeof(bufr
),
288 "NOTIFY * HTTP/1.1\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"
297 SSDP_MCAST_ADDR
, SSDP_PORT
,
300 known_service_types
[i
], (i
>1?"1":""),
301 uuidvalue
, (i
>0?"::":""), (i
>0?known_service_types
[i
]:""), (i
>1?"1":"") );
304 DPRINTF(E_WARN
, L_SSDP
, "SendSSDPNotifies(): truncated output\n");
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
) );
312 DPRINTF(E_ERROR
, L_SSDP
, "sendto(udp_notify=%d, %s): %s\n", s
, host
, strerror(errno
));
320 SendSSDPNotifies2(int * sockets
,
322 unsigned int lifetime
)
323 /*SendSSDPNotifies2(int * sockets, struct lan_addr_s * lan_addr, int n_lan_addr,
325 unsigned int lifetime)*/
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 */
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)*/
345 struct sockaddr_in sendername
;
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
);
356 DPRINTF(E_ERROR
, L_SSDP
, "recvfrom(udp): %s\n", strerror(errno
));
360 if(memcmp(bufr
, "NOTIFY", 6) == 0)
362 /* ignore NOTIFY packets. We could log the sender and device type */
365 else if(memcmp(bufr
, "M-SEARCH", 8) == 0)
367 //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Received SSDP request:\n%.*s", n, bufr);
373 if( !strcasestr(bufr
+i
, "HTTP/1.1") )
379 while((i
< n
- 1) && (bufr
[i
] != '\r' || bufr
[i
+1] != '\n'))
382 if((i
< n
- 3) && (strncasecmp(bufr
+i
, "ST:", 3) == 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)
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)
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
))
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
));
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) )
453 usleep(random()>>20);
454 SendSSDPAnnounce2(s
, sendername
,
456 lan_addr
[lan_addr_index
].str
, port
);
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
);
475 DPRINTF(E_INFO
, L_SSDP
, "Invalid SSDP M-SEARCH from %s:%d\n",
476 inet_ntoa(sendername
.sin_addr
), ntohs(sendername
.sin_port
));
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
;
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"
510 "NTS:ssdp:byebye\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
) );
520 DPRINTF(E_ERROR
, L_SSDP
, "sendto(udp_shutdown=%d): %s\n", sockets
[j
], strerror(errno
));