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
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.
36 #include <sys/socket.h>
38 #include <netinet/in.h>
39 #include <arpa/inet.h>
43 #include "upnpdescstrings.h"
44 #include "minidlnapath.h"
46 #include "upnpglobalvars.h"
47 #include "upnpreplyparse.h"
48 #include "getifaddr.h"
50 #include "codelength.h"
55 #define SSDP_PORT (1900)
56 #define SSDP_MCAST_ADDR ("239.255.255.250")
59 AddMulticastMembership(int s
, in_addr_t ifaddr
)
61 struct ip_mreq imr
; /* Ip multicast membership */
63 /* setting up imr structure */
64 imr
.imr_multiaddr
.s_addr
= inet_addr(SSDP_MCAST_ADDR
);
65 /*imr.imr_interface.s_addr = htonl(INADDR_ANY);*/
66 imr
.imr_interface
.s_addr
= ifaddr
; /*inet_addr(ifaddr);*/
68 if (setsockopt(s
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
, (void *)&imr
, sizeof(struct ip_mreq
)) < 0)
70 DPRINTF(E_ERROR
, L_SSDP
, "setsockopt(udp, IP_ADD_MEMBERSHIP): %s\n", strerror(errno
));
77 /* Open and configure the socket listening for
78 * SSDP udp packets sent on 239.255.255.250 port 1900 */
80 OpenAndConfSSDPReceiveSocket()
84 struct sockaddr_in sockname
;
86 if( (s
= socket(PF_INET
, SOCK_DGRAM
, 0)) < 0)
88 DPRINTF(E_ERROR
, L_SSDP
, "socket(udp): %s\n", strerror(errno
));
92 if(setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &i
, sizeof(i
)) < 0)
94 DPRINTF(E_WARN
, L_SSDP
, "setsockopt(udp, SO_REUSEADDR): %s\n", strerror(errno
));
97 memset(&sockname
, 0, sizeof(struct sockaddr_in
));
98 sockname
.sin_family
= AF_INET
;
99 sockname
.sin_port
= htons(SSDP_PORT
);
100 /* NOTE : it seems it doesnt work when binding on the specific address */
101 /*sockname.sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);*/
102 sockname
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
103 /*sockname.sin_addr.s_addr = inet_addr(ifaddr);*/
105 if(bind(s
, (struct sockaddr
*)&sockname
, sizeof(struct sockaddr_in
)) < 0)
107 DPRINTF(E_ERROR
, L_SSDP
, "bind(udp): %s\n", strerror(errno
));
116 if(AddMulticastMembership(s
, lan_addr
[i
].addr
.s_addr
) < 0)
118 DPRINTF(E_WARN
, L_SSDP
,
119 "Failed to add multicast membership for address %s\n",
127 /* open the UDP socket used to send SSDP notifications to
128 * the multicast group reserved for them */
130 OpenAndConfSSDPNotifySocket(in_addr_t addr
)
133 unsigned char loopchar
= 0;
135 struct in_addr mc_if
;
136 struct sockaddr_in sockname
;
138 if( (s
= socket(PF_INET
, SOCK_DGRAM
, 0)) < 0)
140 DPRINTF(E_ERROR
, L_SSDP
, "socket(udp_notify): %s\n", strerror(errno
));
144 mc_if
.s_addr
= addr
; /*inet_addr(addr);*/
146 if(setsockopt(s
, IPPROTO_IP
, IP_MULTICAST_LOOP
, (char *)&loopchar
, sizeof(loopchar
)) < 0)
148 DPRINTF(E_ERROR
, L_SSDP
, "setsockopt(udp_notify, IP_MULTICAST_LOOP): %s\n", strerror(errno
));
153 if(setsockopt(s
, IPPROTO_IP
, IP_MULTICAST_IF
, (char *)&mc_if
, sizeof(mc_if
)) < 0)
155 DPRINTF(E_ERROR
, L_SSDP
, "setsockopt(udp_notify, IP_MULTICAST_IF): %s\n", strerror(errno
));
160 if(setsockopt(s
, SOL_SOCKET
, SO_BROADCAST
, &bcast
, sizeof(bcast
)) < 0)
162 DPRINTF(E_ERROR
, L_SSDP
, "setsockopt(udp_notify, SO_BROADCAST): %s\n", strerror(errno
));
167 memset(&sockname
, 0, sizeof(struct sockaddr_in
));
168 sockname
.sin_family
= AF_INET
;
169 sockname
.sin_addr
.s_addr
= addr
; /*inet_addr(addr);*/
171 if (bind(s
, (struct sockaddr
*)&sockname
, sizeof(struct sockaddr_in
)) < 0)
173 DPRINTF(E_ERROR
, L_SSDP
, "bind(udp_notify): %s\n", strerror(errno
));
182 OpenAndConfSSDPNotifySockets(int * sockets
)
185 for(i
=0; i
<n_lan_addr
; i
++)
187 sockets
[i
] = OpenAndConfSSDPNotifySocket(lan_addr
[i
].addr
.s_addr
);
202 * response from a LiveBox (Wanadoo)
204 CACHE-CONTROL: max-age=1800
205 DATE: Thu, 01 Jan 1970 04:03:23 GMT
207 LOCATION: http://192.168.0.1:49152/gatedesc.xml
208 SERVER: Linux/2.4.17, UPnP/1.0, Intel SDK for UPnP devices /1.2
210 USN: uuid:75802409-bccb-40e7-8e6c-fa095ecce13e::upnp:rootdevice
212 * response from a Linksys 802.11b :
214 Cache-Control:max-age=120
215 Location:http://192.168.5.1:5678/rootDesc.xml
216 Server:NT/5.0 UPnP/1.0
218 USN:uuid:upnp-InternetGatewayDevice-1_0-0090a2777777::upnp:rootdevice
222 static const char * const known_service_types
[] =
226 "urn:schemas-upnp-org:device:MediaServer:",
227 "urn:schemas-upnp-org:service:ContentDirectory:",
228 "urn:schemas-upnp-org:service:ConnectionManager:",
229 "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:",
236 struct timespec sleep_time
;
238 sleep_time
.tv_sec
= 0;
239 sleep_time
.tv_nsec
= usecs
* 1000;
240 nanosleep(&sleep_time
, NULL
);
243 /* not really an SSDP "announce" as it is the response
244 * to a SSDP "M-SEARCH" */
246 SendSSDPAnnounce2(int s
, struct sockaddr_in sockname
, int st_no
,
247 const char * host
, unsigned short port
)
252 * follow guideline from document "UPnP Device Architecture 1.0"
253 * uppercase is recommended.
254 * DATE: is recommended
255 * SERVER: OS/ver UPnP/1.0 minidlna/1.0
256 * - check what to put in the 'Cache-Control' header
259 time_t tTime
= time(NULL
);
260 strftime(szTime
, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&tTime
));
262 l
= snprintf(buf
, sizeof(buf
), "HTTP/1.1 200 OK\r\n"
263 "CACHE-CONTROL: max-age=%u\r\n"
268 "SERVER: " MINIDLNA_SERVER_STRING
"\r\n"
269 "LOCATION: http://%s:%u" ROOTDESC_PATH
"\r\n"
270 "Content-Length: 0\r\n"
272 (runtime_vars
.notify_interval
<<1)+10,
274 known_service_types
[st_no
], (st_no
>1?"1":""),
275 uuidvalue
, (st_no
>0?"::":""), (st_no
>0?known_service_types
[st_no
]:""), (st_no
>1?"1":""),
276 host
, (unsigned int)port
);
277 //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Sending M-SEARCH response:\n%s", buf);
278 n
= sendto(s
, buf
, l
, 0,
279 (struct sockaddr
*)&sockname
, sizeof(struct sockaddr_in
) );
282 DPRINTF(E_ERROR
, L_SSDP
, "sendto(udp): %s\n", strerror(errno
));
287 SendSSDPNotifies(int s
, const char * host
, unsigned short port
,
288 unsigned int lifetime
)
290 struct sockaddr_in sockname
;
294 memset(&sockname
, 0, sizeof(struct sockaddr_in
));
295 sockname
.sin_family
= AF_INET
;
296 sockname
.sin_port
= htons(SSDP_PORT
);
297 sockname
.sin_addr
.s_addr
= inet_addr(SSDP_MCAST_ADDR
);
299 for( dup
=0; dup
<2; dup
++ )
304 while(known_service_types
[i
])
306 l
= snprintf(bufr
, sizeof(bufr
),
307 "NOTIFY * HTTP/1.1\r\n"
309 "CACHE-CONTROL:max-age=%u\r\n"
310 "LOCATION:http://%s:%d" ROOTDESC_PATH
"\r\n"
311 "SERVER: " MINIDLNA_SERVER_STRING
"\r\n"
316 SSDP_MCAST_ADDR
, SSDP_PORT
,
319 known_service_types
[i
], (i
>1?"1":""),
320 uuidvalue
, (i
>0?"::":""), (i
>0?known_service_types
[i
]:""), (i
>1?"1":"") );
323 DPRINTF(E_WARN
, L_SSDP
, "SendSSDPNotifies(): truncated output\n");
326 //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Sending NOTIFY:\n%s", bufr);
327 n
= sendto(s
, bufr
, l
, 0,
328 (struct sockaddr
*)&sockname
, sizeof(struct sockaddr_in
) );
331 DPRINTF(E_ERROR
, L_SSDP
, "sendto(udp_notify=%d, %s): %s\n", s
, host
, strerror(errno
));
339 SendSSDPNotifies2(int * sockets
,
341 unsigned int lifetime
)
342 /*SendSSDPNotifies2(int * sockets, struct lan_addr_s * lan_addr, int n_lan_addr,
344 unsigned int lifetime)*/
347 DPRINTF(E_DEBUG
, L_SSDP
, "Sending SSDP notifies\n");
348 for(i
=0; i
<n_lan_addr
; i
++)
350 SendSSDPNotifies(sockets
[i
], lan_addr
[i
].str
, port
, lifetime
);
355 ParseUPnPClient(char *location
)
358 struct sockaddr_in dest
;
359 int s
, n
, do_headers
= 0, nread
= 0;
361 char *addr
, *path
, *port_str
;
363 char *off
= NULL
, *p
;
364 int content_len
= sizeof(buf
);
365 struct NameValueParserData xml
;
367 enum client_types type
= 0;
369 char *model
, *serial
, *name
;
371 if (strncmp(location
, "http://", 7) != 0)
374 port_str
= strsep(&path
, "/");
377 addr
= strsep(&port_str
, ":");
380 port
= strtol(port_str
, NULL
, 10);
385 memset(&dest
, '\0', sizeof(dest
));
386 if (!inet_aton(addr
, &dest
.sin_addr
))
388 /* Check if the client is already in cache */
389 dest
.sin_family
= AF_INET
;
390 dest
.sin_port
= htons(port
);
392 s
= socket(PF_INET
, SOCK_STREAM
, 0);
398 setsockopt(s
, SOL_SOCKET
, SO_RCVTIMEO
, &tv
, sizeof(tv
));
399 setsockopt(s
, SOL_SOCKET
, SO_SNDTIMEO
, &tv
, sizeof(tv
));
401 if( connect(s
, (struct sockaddr
*)&dest
, sizeof(struct sockaddr_in
)) < 0 )
404 n
= snprintf(buf
, sizeof(buf
), "GET /%s HTTP/1.0\r\n"
405 "HOST: %s:%ld\r\n\r\n",
407 if( write(s
, buf
, n
) < 1 )
410 while( (n
= read(s
, buf
+nread
, sizeof(buf
)-nread
-1)) > 0 )
417 while( !off
&& n
-- > 0 )
419 if(p
[0]=='\r' && p
[1]=='\n' && p
[2]=='\r' && p
[3]=='\n')
432 if( strncmp(p
, "HTTP/", 5) != 0 )
434 while(*p
!= ' ' && *p
!= '\t') p
++;
435 /* If we don't get a 200 status, ignore it */
436 if( strtol(p
, NULL
, 10) != 200 )
438 if( (p
= strcasestr(p
, "Content-Length:")) )
439 content_len
= strtol(p
+15, NULL
, 10);
442 if( buf
+ nread
- off
>= content_len
)
450 ParseNameValue(off
, nread
, &xml
);
451 model
= GetValueFromNameValueList(&xml
, "modelName");
452 serial
= GetValueFromNameValueList(&xml
, "serialNumber");
453 name
= GetValueFromNameValueList(&xml
, "friendlyName");
456 DPRINTF(E_DEBUG
, L_SSDP
, "Model: %s\n", model
);
457 if( strstr(model
, "Roku SoundBridge") )
459 type
= ERokuSoundBridge
;
460 flags
|= FLAG_MS_PFS
;
461 flags
|= FLAG_AUDIO_ONLY
;
462 flags
|= FLAG_MIME_WAV_WAV
;
464 else if( strcmp(model
, "Samsung DTV DMR") == 0 && serial
)
466 DPRINTF(E_DEBUG
, L_SSDP
, "Serial: %s\n", serial
);
467 /* The Series B I saw was 20081224DMR. Series A should be older than that. */
468 if( atoi(serial
) > 20081200 )
470 type
= ESamsungSeriesB
;
471 flags
|= FLAG_SAMSUNG
;
473 flags
|= FLAG_NO_RESIZE
;
478 if( name
&& (strcmp(name
, "marantz DMP") == 0) )
482 flags
|= FLAG_MIME_WAV_WAV
;
486 ClearNameValueList(&xml
);
489 /* Add this client to the cache if it's not there already. */
490 client
= SearchClientCache(dest
.sin_addr
, 1);
493 for( client
=0; client
<CLIENT_CACHE_SLOTS
; client
++ )
495 if( clients
[client
].addr
.s_addr
)
497 get_remote_mac(dest
.sin_addr
, clients
[client
].mac
);
498 clients
[client
].addr
= dest
.sin_addr
;
499 DPRINTF(E_DEBUG
, L_SSDP
, "Added client [%d/%s/%02X:%02X:%02X:%02X:%02X:%02X] to cache slot %d.\n",
500 type
, inet_ntoa(clients
[client
].addr
),
501 clients
[client
].mac
[0], clients
[client
].mac
[1], clients
[client
].mac
[2],
502 clients
[client
].mac
[3], clients
[client
].mac
[4], clients
[client
].mac
[5], client
);
506 clients
[client
].type
= type
;
507 clients
[client
].flags
= flags
;
508 clients
[client
].age
= time(NULL
);
511 /* ProcessSSDPRequest()
512 * process SSDP M-SEARCH requests and responds to them */
514 ProcessSSDPRequest(int s
, unsigned short port
)
515 /*ProcessSSDPRequest(int s, struct lan_addr_s * lan_addr, int n_lan_addr,
516 unsigned short port)*/
521 struct sockaddr_in sendername
;
523 char *st
= NULL
, *mx
= NULL
, *man
= NULL
, *mx_end
= NULL
;
525 len_r
= sizeof(struct sockaddr_in
);
527 n
= recvfrom(s
, bufr
, sizeof(bufr
)-1, 0,
528 (struct sockaddr
*)&sendername
, &len_r
);
531 DPRINTF(E_ERROR
, L_SSDP
, "recvfrom(udp): %s\n", strerror(errno
));
536 if(memcmp(bufr
, "NOTIFY", 6) == 0)
538 char *loc
= NULL
, *srv
= NULL
, *nts
= NULL
, *nt
= NULL
;
540 //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Received SSDP notify:\n%.*s", n, bufr);
546 if( !strcasestr(bufr
+i
, "HTTP/1.1") )
552 while((i
< n
- 2) && (bufr
[i
] != '\r' || bufr
[i
+1] != '\n'))
555 if(strncasecmp(bufr
+i
, "SERVER:", 7) == 0)
558 while(*srv
== ' ' || *srv
== '\t') srv
++;
560 else if(strncasecmp(bufr
+i
, "LOCATION:", 9) == 0)
563 while(*loc
== ' ' || *loc
== '\t') loc
++;
564 while(loc
[loc_len
]!='\r' && loc
[loc_len
]!='\n') loc_len
++;
566 else if(strncasecmp(bufr
+i
, "NTS:", 4) == 0)
569 while(*nts
== ' ' || *nts
== '\t') nts
++;
571 else if(strncasecmp(bufr
+i
, "NT:", 3) == 0)
574 while(*nt
== ' ' || *nt
== '\t') nt
++;
577 if( !loc
|| !srv
|| !nt
|| !nts
|| (strncmp(nts
, "ssdp:alive", 10) != 0) ||
578 (strncmp(nt
, "urn:schemas-upnp-org:device:MediaRenderer", 41) != 0) )
583 if( (strncmp(srv
, "Allegro-Software-RomPlug", 24) == 0) || /* Roku */
584 (strstr(loc
, "SamsungMRDesc.xml") != NULL
) || /* Samsung TV */
585 (strstrc(srv
, "DigiOn DiXiM", '\r') != NULL
) ) /* Marantz Receiver */
587 /* Check if the client is already in cache */
588 i
= SearchClientCache(sendername
.sin_addr
, 1);
591 if( clients
[i
].type
< EStandardDLNA150
&&
592 clients
[i
].type
!= ESamsungSeriesA
)
594 clients
[i
].age
= time(NULL
);
598 ParseUPnPClient(loc
);
602 else if(memcmp(bufr
, "M-SEARCH", 8) == 0)
604 int st_len
= 0, mx_len
= 0, mx_val
= 0;
605 //DPRINTF(E_DEBUG, L_SSDP, "Received SSDP request:\n%.*s\n", n, bufr);
611 if( !strcasestr(bufr
+i
, "HTTP/1.1") )
617 while((i
< n
- 2) && (bufr
[i
] != '\r' || bufr
[i
+1] != '\n'))
620 if(strncasecmp(bufr
+i
, "ST:", 3) == 0)
624 while(*st
== ' ' || *st
== '\t') st
++;
625 while(st
[st_len
]!='\r' && st
[st_len
]!='\n') st_len
++;
627 else if(strncasecmp(bufr
+i
, "MX:", 3) == 0)
631 while(*mx
== ' ' || *mx
== '\t') mx
++;
632 while(mx
[mx_len
]!='\r' && mx
[mx_len
]!='\n') mx_len
++;
633 mx_val
= strtol(mx
, &mx_end
, 10);
635 else if(strncasecmp(bufr
+i
, "MAN:", 4) == 0)
639 while(*man
== ' ' || *man
== '\t') man
++;
640 while(man
[man_len
]!='\r' && man
[man_len
]!='\n') man_len
++;
643 /*DPRINTF(E_INFO, L_SSDP, "SSDP M-SEARCH packet received from %s:%d\n",
644 inet_ntoa(sendername.sin_addr),
645 ntohs(sendername.sin_port) );*/
646 if( GETFLAG(DLNA_STRICT_MASK
) && (ntohs(sendername
.sin_port
) <= 1024 || ntohs(sendername
.sin_port
) == 1900) )
648 DPRINTF(E_INFO
, L_SSDP
, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad source port %d]\n",
649 inet_ntoa(sendername
.sin_addr
), ntohs(sendername
.sin_port
));
651 else if( !man
|| (strncmp(man
, "\"ssdp:discover\"", 15) != 0) )
653 DPRINTF(E_INFO
, L_SSDP
, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad MAN header %.*s]\n",
654 inet_ntoa(sendername
.sin_addr
), man_len
, man
);
656 else if( !mx
|| mx
== mx_end
|| mx_val
< 0 ) {
657 DPRINTF(E_INFO
, L_SSDP
, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad MX header %.*s]\n",
658 inet_ntoa(sendername
.sin_addr
), mx_len
, mx
);
660 else if( st
&& (st_len
> 0) )
663 int lan_addr_index
= 0;
664 /* find in which sub network the client is */
665 for(i
= 0; i
<n_lan_addr
; i
++)
667 if( (sendername
.sin_addr
.s_addr
& lan_addr
[i
].mask
.s_addr
)
668 == (lan_addr
[i
].addr
.s_addr
& lan_addr
[i
].mask
.s_addr
))
674 if( i
== n_lan_addr
)
676 DPRINTF(E_DEBUG
, L_SSDP
, "Ignoring SSDP M-SEARCH on other interface [%s]\n",
677 inet_ntoa(sendername
.sin_addr
));
680 DPRINTF(E_INFO
, L_SSDP
, "SSDP M-SEARCH from %s:%d ST: %.*s, MX: %.*s, MAN: %.*s\n",
681 inet_ntoa(sendername
.sin_addr
),
682 ntohs(sendername
.sin_port
),
683 st_len
, st
, mx_len
, mx
, man_len
, man
);
684 /* Responds to request with a device as ST header */
685 for(i
= 0; known_service_types
[i
]; i
++)
687 l
= strlen(known_service_types
[i
]);
688 if(l
<=st_len
&& (0 == memcmp(st
, known_service_types
[i
], l
)))
690 /* Check version number - must always be 1 currently. */
691 if( (st
[st_len
-2] == ':') && (atoi(st
+st_len
-1) != 1) )
693 _usleep(random()>>20);
694 SendSSDPAnnounce2(s
, sendername
,
696 lan_addr
[lan_addr_index
].str
, port
);
700 /* Responds to request with ST: ssdp:all */
701 /* strlen("ssdp:all") == 8 */
702 if(st_len
==8 && (0 == memcmp(st
, "ssdp:all", 8)))
704 for(i
=0; known_service_types
[i
]; i
++)
706 l
= (int)strlen(known_service_types
[i
]);
707 SendSSDPAnnounce2(s
, sendername
,
709 lan_addr
[lan_addr_index
].str
, port
);
715 DPRINTF(E_INFO
, L_SSDP
, "Invalid SSDP M-SEARCH from %s:%d\n",
716 inet_ntoa(sendername
.sin_addr
), ntohs(sendername
.sin_port
));
721 DPRINTF(E_WARN
, L_SSDP
, "Unknown udp packet received from %s:%d\n",
722 inet_ntoa(sendername
.sin_addr
), ntohs(sendername
.sin_port
));
726 /* This will broadcast ssdp:byebye notifications to inform
727 * the network that UPnP is going down. */
729 SendSSDPGoodbye(int * sockets
, int n_sockets
)
731 struct sockaddr_in sockname
;
736 memset(&sockname
, 0, sizeof(struct sockaddr_in
));
737 sockname
.sin_family
= AF_INET
;
738 sockname
.sin_port
= htons(SSDP_PORT
);
739 sockname
.sin_addr
.s_addr
= inet_addr(SSDP_MCAST_ADDR
);
741 for(j
=0; j
<n_sockets
; j
++)
743 for(i
=0; known_service_types
[i
]; i
++)
745 l
= snprintf(bufr
, sizeof(bufr
),
746 "NOTIFY * HTTP/1.1\r\n"
750 "NTS:ssdp:byebye\r\n"
752 SSDP_MCAST_ADDR
, SSDP_PORT
,
753 known_service_types
[i
], (i
>1?"1":""),
754 uuidvalue
, (i
>0?"::":""), (i
>0?known_service_types
[i
]:""), (i
>1?"1":"") );
755 //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Sending NOTIFY:\n%s", bufr);
756 n
= sendto(sockets
[j
], bufr
, l
, 0,
757 (struct sockaddr
*)&sockname
, sizeof(struct sockaddr_in
) );
760 DPRINTF(E_ERROR
, L_SSDP
, "sendto(udp_shutdown=%d): %s\n", sockets
[j
], strerror(errno
));
768 /* SubmitServicesToMiniSSDPD() :
769 * register services offered by MiniUPnPd to a running instance of
772 SubmitServicesToMiniSSDPD(const char * host
, unsigned short port
) {
773 struct sockaddr_un addr
;
775 unsigned char buffer
[2048];
780 s
= socket(AF_UNIX
, SOCK_STREAM
, 0);
782 DPRINTF(E_ERROR
, L_SSDP
, "socket(unix): %s", strerror(errno
));
785 addr
.sun_family
= AF_UNIX
;
786 strncpy(addr
.sun_path
, minissdpdsocketpath
, sizeof(addr
.sun_path
));
787 if(connect(s
, (struct sockaddr
*)&addr
, sizeof(struct sockaddr_un
)) < 0) {
788 DPRINTF(E_ERROR
, L_SSDP
, "connect(\"%s\"): %s",
789 minissdpdsocketpath
, strerror(errno
));
792 for(i
= 0; known_service_types
[i
]; i
++) {
795 l
= (int)strlen(known_service_types
[i
]);
799 memcpy(p
, known_service_types
[i
], l
);
803 l
= snprintf(strbuf
, sizeof(strbuf
), "%s::%s%s",
804 uuidvalue
, known_service_types
[i
], (i
==0)?"":"1");
806 memcpy(p
, strbuf
, l
);
808 l
= (int)strlen(MINIDLNA_SERVER_STRING
);
810 memcpy(p
, MINIDLNA_SERVER_STRING
, l
);
812 l
= snprintf(strbuf
, sizeof(strbuf
), "http://%s:%u" ROOTDESC_PATH
,
813 host
, (unsigned int)port
);
815 memcpy(p
, strbuf
, l
);
817 if(write(s
, buffer
, p
- buffer
) < 0) {
818 DPRINTF(E_ERROR
, L_SSDP
, "write(): %s", strerror(errno
));