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.
39 #include <sys/socket.h>
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
45 #include "minidlnapath.h"
47 #include "upnpglobalvars.h"
48 #include "upnpreplyparse.h"
49 #include "getifaddr.h"
51 #include "codelength.h"
56 #define SSDP_PORT (1900)
57 #define SSDP_MCAST_ADDR ("239.255.255.250")
60 AddMulticastMembership(int s
, struct lan_addr_s
*iface
)
63 #ifdef HAVE_STRUCT_IP_MREQN
64 struct ip_mreqn imr
; /* Ip multicast membership */
65 /* setting up imr structure */
66 memset(&imr
, '\0', sizeof(imr
));
67 imr
.imr_multiaddr
.s_addr
= inet_addr(SSDP_MCAST_ADDR
);
68 imr
.imr_ifindex
= iface
->ifindex
;
70 struct ip_mreq imr
; /* Ip multicast membership */
71 /* setting up imr structure */
72 memset(&imr
, '\0', sizeof(imr
));
73 imr
.imr_multiaddr
.s_addr
= inet_addr(SSDP_MCAST_ADDR
);
74 imr
.imr_interface
.s_addr
= iface
->addr
.s_addr
;
76 /* Setting the socket options will guarantee, tha we will only receive
77 * multicast traffic on a specific Interface.
78 * In addition the kernel is instructed to send an igmp message (choose
79 * mcast group) on the specific interface/subnet. */
80 ret
= setsockopt(s
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
, (void *)&imr
, sizeof(imr
));
81 if (ret
< 0 && errno
!= EADDRINUSE
)
83 DPRINTF(E_ERROR
, L_SSDP
, "setsockopt(udp, IP_ADD_MEMBERSHIP): %s\n",
91 /* Open and configure the socket listening for
92 * SSDP udp packets sent on 239.255.255.250 port 1900 */
94 OpenAndConfSSDPReceiveSocket(void)
98 struct sockaddr_in sockname
;
100 s
= socket(PF_INET
, SOCK_DGRAM
, 0);
103 DPRINTF(E_ERROR
, L_SSDP
, "socket(udp): %s\n", strerror(errno
));
107 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &i
, sizeof(i
)) < 0)
108 DPRINTF(E_WARN
, L_SSDP
, "setsockopt(udp, SO_REUSEADDR): %s\n", strerror(errno
));
110 if (setsockopt(s
, IPPROTO_IP
, IP_PKTINFO
, &i
, sizeof(i
)) < 0)
111 DPRINTF(E_WARN
, L_SSDP
, "setsockopt(udp, IP_PKTINFO): %s\n", strerror(errno
));
113 memset(&sockname
, 0, sizeof(struct sockaddr_in
));
114 sockname
.sin_family
= AF_INET
;
115 sockname
.sin_port
= htons(SSDP_PORT
);
116 /* NOTE: Binding a socket to a UDP multicast address means, that we just want
117 * to receive datagramms send to this multicast address.
118 * To specify the local nics we want to use we have to use setsockopt,
119 * see AddMulticastMembership(...). */
120 sockname
.sin_addr
.s_addr
= inet_addr(SSDP_MCAST_ADDR
);
122 if (bind(s
, (struct sockaddr
*)&sockname
, sizeof(struct sockaddr_in
)) < 0)
124 DPRINTF(E_ERROR
, L_SSDP
, "bind(udp): %s\n", strerror(errno
));
132 /* open the UDP socket used to send SSDP notifications to
133 * the multicast group reserved for them */
135 OpenAndConfSSDPNotifySocket(struct lan_addr_s
*iface
)
138 unsigned char loopchar
= 0;
141 struct in_addr mc_if
;
142 struct sockaddr_in sockname
;
144 s
= socket(PF_INET
, SOCK_DGRAM
, 0);
147 DPRINTF(E_ERROR
, L_SSDP
, "socket(udp_notify): %s\n", strerror(errno
));
151 mc_if
.s_addr
= iface
->addr
.s_addr
;
153 if (setsockopt(s
, IPPROTO_IP
, IP_MULTICAST_LOOP
, (char *)&loopchar
, sizeof(loopchar
)) < 0)
155 DPRINTF(E_ERROR
, L_SSDP
, "setsockopt(udp_notify, IP_MULTICAST_LOOP): %s\n", strerror(errno
));
160 if (setsockopt(s
, IPPROTO_IP
, IP_MULTICAST_IF
, (char *)&mc_if
, sizeof(mc_if
)) < 0)
162 DPRINTF(E_ERROR
, L_SSDP
, "setsockopt(udp_notify, IP_MULTICAST_IF): %s\n", strerror(errno
));
167 setsockopt(s
, IPPROTO_IP
, IP_MULTICAST_TTL
, &ttl
, sizeof(ttl
));
169 if (setsockopt(s
, SOL_SOCKET
, SO_BROADCAST
, &bcast
, sizeof(bcast
)) < 0)
171 DPRINTF(E_ERROR
, L_SSDP
, "setsockopt(udp_notify, SO_BROADCAST): %s\n", strerror(errno
));
176 memset(&sockname
, 0, sizeof(struct sockaddr_in
));
177 sockname
.sin_family
= AF_INET
;
178 sockname
.sin_addr
.s_addr
= iface
->addr
.s_addr
;
180 if (bind(s
, (struct sockaddr
*)&sockname
, sizeof(struct sockaddr_in
)) < 0)
182 DPRINTF(E_ERROR
, L_SSDP
, "bind(udp_notify): %s\n", strerror(errno
));
187 if (AddMulticastMembership(sssdp
, iface
) < 0)
189 DPRINTF(E_WARN
, L_SSDP
, "Failed to add multicast membership for address %s\n",
196 static const char * const known_service_types
[] =
200 "urn:schemas-upnp-org:device:MediaServer:",
201 "urn:schemas-upnp-org:service:ContentDirectory:",
202 "urn:schemas-upnp-org:service:ConnectionManager:",
203 "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:",
210 struct timespec sleep_time
;
212 sleep_time
.tv_sec
= 0;
213 sleep_time
.tv_nsec
= usecs
* 1000;
214 nanosleep(&sleep_time
, NULL
);
217 /* not really an SSDP "announce" as it is the response
218 * to a SSDP "M-SEARCH" */
220 SendSSDPResponse(int s
, struct sockaddr_in sockname
, int st_no
,
221 const char *host
, unsigned short port
)
226 time_t tm
= time(NULL
);
229 * follow guideline from document "UPnP Device Architecture 1.0"
230 * uppercase is recommended.
231 * DATE: is recommended
232 * SERVER: OS/ver UPnP/1.0 minidlna/1.0
233 * - check what to put in the 'Cache-Control' header
235 strftime(tmstr
, sizeof(tmstr
), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&tm
));
236 l
= snprintf(buf
, sizeof(buf
), "HTTP/1.1 200 OK\r\n"
237 "CACHE-CONTROL: max-age=%u\r\n"
242 "SERVER: " MINIDLNA_SERVER_STRING
"\r\n"
243 "LOCATION: http://%s:%u" ROOTDESC_PATH
"\r\n"
244 "Content-Length: 0\r\n"
246 (runtime_vars
.notify_interval
<<1)+10,
248 known_service_types
[st_no
],
249 (st_no
> 1 ? "1" : ""),
251 (st_no
> 0 ? "::" : ""),
252 (st_no
> 0 ? known_service_types
[st_no
] : ""),
253 (st_no
> 1 ? "1" : ""),
254 host
, (unsigned int)port
);
255 DPRINTF(E_DEBUG
, L_SSDP
, "Sending M-SEARCH response to %s:%d ST: %s\n",
256 inet_ntoa(sockname
.sin_addr
), ntohs(sockname
.sin_port
),
257 known_service_types
[st_no
]);
258 n
= sendto(s
, buf
, l
, 0,
259 (struct sockaddr
*)&sockname
, sizeof(struct sockaddr_in
) );
261 DPRINTF(E_ERROR
, L_SSDP
, "sendto(udp): %s\n", strerror(errno
));
265 SendSSDPNotifies(int s
, const char *host
, unsigned short port
,
266 unsigned int interval
)
268 struct sockaddr_in sockname
;
270 unsigned int lifetime
;
273 memset(&sockname
, 0, sizeof(struct sockaddr_in
));
274 sockname
.sin_family
= AF_INET
;
275 sockname
.sin_port
= htons(SSDP_PORT
);
276 sockname
.sin_addr
.s_addr
= inet_addr(SSDP_MCAST_ADDR
);
277 lifetime
= (interval
<< 1) + 10;
279 for (dup
= 0; dup
< 2; dup
++)
284 while (known_service_types
[i
])
286 l
= snprintf(bufr
, sizeof(bufr
),
287 "NOTIFY * HTTP/1.1\r\n"
289 "CACHE-CONTROL:max-age=%u\r\n"
290 "LOCATION:http://%s:%d" ROOTDESC_PATH
"\r\n"
291 "SERVER: " MINIDLNA_SERVER_STRING
"\r\n"
296 SSDP_MCAST_ADDR
, SSDP_PORT
,
299 known_service_types
[i
],
303 (i
> 0 ? known_service_types
[i
] : ""),
305 if (l
>= sizeof(bufr
))
307 DPRINTF(E_WARN
, L_SSDP
, "SendSSDPNotifies(): truncated output\n");
310 DPRINTF(E_MAXDEBUG
, L_SSDP
, "Sending ssdp:alive [%d]\n", s
);
311 n
= sendto(s
, bufr
, l
, 0,
312 (struct sockaddr
*)&sockname
, sizeof(struct sockaddr_in
));
314 DPRINTF(E_ERROR
, L_SSDP
, "sendto(udp_notify=%d, %s): %s\n", s
, host
, strerror(errno
));
321 ParseUPnPClient(char *location
)
324 struct sockaddr_in dest
;
325 int s
, n
, do_headers
= 0, nread
= 0;
327 char *addr
, *path
, *port_str
;
329 char *off
= NULL
, *p
;
330 int content_len
= sizeof(buf
);
331 struct NameValueParserData xml
;
332 struct client_cache_s
*client
;
334 char *model
, *serial
, *name
;
336 if (strncmp(location
, "http://", 7) != 0)
339 port_str
= strsep(&path
, "/");
342 addr
= strsep(&port_str
, ":");
345 port
= strtol(port_str
, NULL
, 10);
350 memset(&dest
, '\0', sizeof(dest
));
351 if (!inet_aton(addr
, &dest
.sin_addr
))
353 /* Check if the client is already in cache */
354 dest
.sin_family
= AF_INET
;
355 dest
.sin_port
= htons(port
);
357 s
= socket(PF_INET
, SOCK_STREAM
, 0);
363 setsockopt(s
, SOL_SOCKET
, SO_RCVTIMEO
, &tv
, sizeof(tv
));
364 setsockopt(s
, SOL_SOCKET
, SO_SNDTIMEO
, &tv
, sizeof(tv
));
366 if (connect(s
, (struct sockaddr
*)&dest
, sizeof(struct sockaddr_in
)) < 0)
369 n
= snprintf(buf
, sizeof(buf
), "GET /%s HTTP/1.0\r\n"
370 "HOST: %s:%ld\r\n\r\n",
372 if (write(s
, buf
, n
) < 1)
375 while ((n
= read(s
, buf
+nread
, sizeof(buf
)-nread
-1)) > 0)
382 while (!off
&& (n
-- > 0))
384 if (p
[0] == '\r' && p
[1] == '\n' && p
[2] == '\r' && p
[3] == '\n')
397 if (strncmp(p
, "HTTP/", 5) != 0)
399 while (*p
!= ' ' && *p
!= '\t')
401 /* If we don't get a 200 status, ignore it */
402 if (strtol(p
, NULL
, 10) != 200)
404 p
= strcasestr(p
, "Content-Length:");
406 content_len
= strtol(p
+15, NULL
, 10);
409 if ((buf
+ nread
- off
) >= content_len
)
417 ParseNameValue(off
, nread
, &xml
, 0);
418 model
= GetValueFromNameValueList(&xml
, "modelName");
419 serial
= GetValueFromNameValueList(&xml
, "serialNumber");
420 name
= GetValueFromNameValueList(&xml
, "friendlyName");
424 DPRINTF(E_DEBUG
, L_SSDP
, "Model: %s\n", model
);
425 for (i
= 0; client_types
[i
].name
; i
++)
427 if (client_types
[i
].match_type
!= EModelName
)
429 if (strstr(model
, client_types
[i
].match
) != NULL
)
436 /* Special Samsung handling. It's very hard to tell Series A from B */
437 if (type
> 0 && client_types
[type
].type
== ESamsungSeriesB
)
441 DPRINTF(E_DEBUG
, L_SSDP
, "Serial: %s\n", serial
);
442 /* The Series B I saw was 20081224DMR. Series A should be older than that. */
443 if (atoi(serial
) < 20081201)
452 if (type
== 0 && name
!= NULL
)
454 for (i
= 0; client_types
[i
].name
; i
++)
456 if (client_types
[i
].match_type
!= EFriendlyNameSSDP
)
458 if (strcmp(name
, client_types
[i
].match
) == 0)
466 ClearNameValueList(&xml
);
469 /* Add this client to the cache if it's not there already. */
470 client
= SearchClientCache(dest
.sin_addr
, 1);
473 AddClientCache(dest
.sin_addr
, type
);
477 client
->type
= &client_types
[type
];
478 client
->age
= time(NULL
);
482 /* ProcessSSDPRequest()
483 * process SSDP M-SEARCH requests and responds to them */
485 ProcessSSDPRequest(int s
, unsigned short port
)
489 struct sockaddr_in sendername
;
491 char *st
= NULL
, *mx
= NULL
, *man
= NULL
, *mx_end
= NULL
;
494 char cmbuf
[CMSG_SPACE(sizeof(struct in_pktinfo
))];
495 struct iovec iovec
= {
497 .iov_len
= sizeof(bufr
)-1
500 .msg_name
= &sendername
,
501 .msg_namelen
= sizeof(struct sockaddr_in
),
504 .msg_control
= cmbuf
,
505 .msg_controllen
= sizeof(cmbuf
)
508 n
= recvmsg(s
, &mh
, 0);
510 socklen_t len_r
= sizeof(struct sockaddr_in
);
512 n
= recvfrom(s
, bufr
, sizeof(bufr
)-1, 0,
513 (struct sockaddr
*)&sendername
, &len_r
);
517 DPRINTF(E_ERROR
, L_SSDP
, "recvfrom(udp): %s\n", strerror(errno
));
523 if (memcmp(bufr
, "NOTIFY", 6) == 0)
525 char *loc
= NULL
, *srv
= NULL
, *nts
= NULL
, *nt
= NULL
;
527 //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Received SSDP notify:\n%.*s", n, bufr);
528 for (i
= 0; i
< n
; i
++)
533 if (strcasestrc(bufr
+i
, "HTTP/1.1", '\r') == NULL
)
537 while ((i
< n
) && (bufr
[i
] != '\r' || bufr
[i
+1] != '\n'))
540 if (strncasecmp(bufr
+i
, "SERVER:", 7) == 0)
543 while (*srv
== ' ' || *srv
== '\t')
546 else if (strncasecmp(bufr
+i
, "LOCATION:", 9) == 0)
549 while (*loc
== ' ' || *loc
== '\t')
551 while (loc
[loc_len
]!='\r' && loc
[loc_len
]!='\n')
554 else if (strncasecmp(bufr
+i
, "NTS:", 4) == 0)
557 while (*nts
== ' ' || *nts
== '\t')
560 else if (strncasecmp(bufr
+i
, "NT:", 3) == 0)
563 while(*nt
== ' ' || *nt
== '\t')
567 if (!loc
|| !srv
|| !nt
|| !nts
|| (strncmp(nts
, "ssdp:alive", 10) != 0) ||
568 (strncmp(nt
, "urn:schemas-upnp-org:device:MediaRenderer", 41) != 0))
571 if ((strncmp(srv
, "Allegro-Software-RomPlug", 24) == 0) || /* Roku */
572 (strstr(loc
, "SamsungMRDesc.xml") != NULL
) || /* Samsung TV */
573 (strstrc(srv
, "DigiOn DiXiM", '\r') != NULL
)) /* Marantz Receiver */
575 /* Check if the client is already in cache */
576 struct client_cache_s
*client
= SearchClientCache(sendername
.sin_addr
, 1);
579 if (client
->type
->type
< EStandardDLNA150
&&
580 client
->type
->type
!= ESamsungSeriesA
)
582 client
->age
= time(NULL
);
586 ParseUPnPClient(loc
);
589 else if (memcmp(bufr
, "M-SEARCH", 8) == 0)
591 int st_len
= 0, mx_len
= 0, mx_val
= 0;
592 //DPRINTF(E_DEBUG, L_SSDP, "Received SSDP request:\n%.*s\n", n, bufr);
593 for (i
= 0; i
< n
; i
++)
598 if (strcasestrc(bufr
+i
, "HTTP/1.1", '\r') == NULL
)
602 while ((i
< n
) && (bufr
[i
] != '\r' || bufr
[i
+1] != '\n'))
605 if (strncasecmp(bufr
+i
, "ST:", 3) == 0)
609 while (*st
== ' ' || *st
== '\t')
611 while (st
[st_len
]!='\r' && st
[st_len
]!='\n')
614 else if (strncasecmp(bufr
+i
, "MX:", 3) == 0)
618 while (*mx
== ' ' || *mx
== '\t')
620 while (mx
[mx_len
]!='\r' && mx
[mx_len
]!='\n')
622 mx_val
= strtol(mx
, &mx_end
, 10);
624 else if (strncasecmp(bufr
+i
, "MAN:", 4) == 0)
628 while (*man
== ' ' || *man
== '\t')
630 while (man
[man_len
]!='\r' && man
[man_len
]!='\n')
634 /*DPRINTF(E_INFO, L_SSDP, "SSDP M-SEARCH packet received from %s:%d\n",
635 inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port) );*/
636 if (GETFLAG(DLNA_STRICT_MASK
) && (ntohs(sendername
.sin_port
) <= 1024 || ntohs(sendername
.sin_port
) == 1900))
638 DPRINTF(E_INFO
, L_SSDP
, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad source port %d]\n",
639 inet_ntoa(sendername
.sin_addr
), ntohs(sendername
.sin_port
));
641 else if (!man
|| (strncmp(man
, "\"ssdp:discover\"", 15) != 0))
643 DPRINTF(E_INFO
, L_SSDP
, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad %s header '%.*s']\n",
644 inet_ntoa(sendername
.sin_addr
), "MAN", man_len
, man
);
646 else if (!mx
|| mx
== mx_end
|| mx_val
< 0)
648 DPRINTF(E_INFO
, L_SSDP
, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad %s header '%.*s']\n",
649 inet_ntoa(sendername
.sin_addr
), "MX", mx_len
, mx
);
651 else if (st
&& (st_len
> 0))
655 char host
[40] = "127.0.0.1";
656 struct cmsghdr
*cmsg
;
658 /* find the interface we received the msg from */
659 for (cmsg
= CMSG_FIRSTHDR(&mh
); cmsg
; cmsg
= CMSG_NXTHDR(&mh
, cmsg
))
662 struct in_pktinfo
*pi
;
663 /* ignore the control headers that don't match what we want */
664 if (cmsg
->cmsg_level
!= IPPROTO_IP
||
665 cmsg
->cmsg_type
!= IP_PKTINFO
)
668 pi
= (struct in_pktinfo
*)CMSG_DATA(cmsg
);
669 addr
= pi
->ipi_spec_dst
;
670 inet_ntop(AF_INET
, &addr
, host
, sizeof(host
));
675 /* find in which sub network the client is */
676 for (i
= 0; i
< n_lan_addr
; i
++)
678 if((sendername
.sin_addr
.s_addr
& lan_addr
[i
].mask
.s_addr
) ==
679 (lan_addr
[i
].addr
.s_addr
& lan_addr
[i
].mask
.s_addr
))
687 DPRINTF(E_DEBUG
, L_SSDP
, "Ignoring SSDP M-SEARCH on other interface [%s]\n",
688 inet_ntoa(sendername
.sin_addr
));
691 host
= lan_addr
[iface
].str
;
693 DPRINTF(E_DEBUG
, L_SSDP
, "SSDP M-SEARCH from %s:%d ST: %.*s, MX: %.*s, MAN: %.*s\n",
694 inet_ntoa(sendername
.sin_addr
),
695 ntohs(sendername
.sin_port
),
696 st_len
, st
, mx_len
, mx
, man_len
, man
);
697 /* Responds to request with a device as ST header */
698 for (i
= 0; known_service_types
[i
]; i
++)
700 l
= strlen(known_service_types
[i
]);
701 if ((l
> st_len
) || (memcmp(st
, known_service_types
[i
], l
) != 0))
705 /* Check version number - we only support 1. */
706 if ((st
[l
-1] == ':') && (st
[l
] == '1'))
717 DPRINTF(E_MAXDEBUG
, L_SSDP
,
718 "Ignoring SSDP M-SEARCH with bad extra data '%c' [%s]\n",
719 st
[l
], inet_ntoa(sendername
.sin_addr
));
725 _usleep(random()>>20);
726 SendSSDPResponse(s
, sendername
, i
,
730 /* Responds to request with ST: ssdp:all */
731 /* strlen("ssdp:all") == 8 */
732 if ((st_len
== 8) && (memcmp(st
, "ssdp:all", 8) == 0))
734 for (i
=0; known_service_types
[i
]; i
++)
736 l
= strlen(known_service_types
[i
]);
737 SendSSDPResponse(s
, sendername
, i
,
744 DPRINTF(E_INFO
, L_SSDP
, "Invalid SSDP M-SEARCH from %s:%d\n",
745 inet_ntoa(sendername
.sin_addr
), ntohs(sendername
.sin_port
));
748 else if (memcmp(bufr
, "YOUKU-NOTIFY", 12) == 0)
754 DPRINTF(E_WARN
, L_SSDP
, "Unknown udp packet received from %s:%d\n",
755 inet_ntoa(sendername
.sin_addr
), ntohs(sendername
.sin_port
));
759 /* This will broadcast ssdp:byebye notifications to inform
760 * the network that UPnP is going down. */
762 SendSSDPGoodbyes(int s
)
764 struct sockaddr_in sockname
;
770 memset(&sockname
, 0, sizeof(struct sockaddr_in
));
771 sockname
.sin_family
= AF_INET
;
772 sockname
.sin_port
= htons(SSDP_PORT
);
773 sockname
.sin_addr
.s_addr
= inet_addr(SSDP_MCAST_ADDR
);
775 for (dup
= 0; dup
< 2; dup
++)
777 for (i
= 0; known_service_types
[i
]; i
++)
779 l
= snprintf(bufr
, sizeof(bufr
),
780 "NOTIFY * HTTP/1.1\r\n"
784 "NTS:ssdp:byebye\r\n"
786 SSDP_MCAST_ADDR
, SSDP_PORT
,
787 known_service_types
[i
],
788 (i
> 1 ? "1" : ""), uuidvalue
,
790 (i
> 0 ? known_service_types
[i
] : ""),
792 DPRINTF(E_MAXDEBUG
, L_SSDP
, "Sending ssdp:byebye [%d]\n", s
);
793 n
= sendto(s
, bufr
, l
, 0,
794 (struct sockaddr
*)&sockname
, sizeof(struct sockaddr_in
) );
797 DPRINTF(E_ERROR
, L_SSDP
, "sendto(udp_shutdown=%d): %s\n", s
, strerror(errno
));
806 /* SubmitServicesToMiniSSDPD() :
807 * register services offered by MiniUPnPd to a running instance of
810 SubmitServicesToMiniSSDPD(const char *host
, unsigned short port
)
812 struct sockaddr_un addr
;
814 unsigned char buffer
[2048];
819 s
= socket(AF_UNIX
, SOCK_STREAM
, 0);
822 DPRINTF(E_ERROR
, L_SSDP
, "socket(unix): %s", strerror(errno
));
825 addr
.sun_family
= AF_UNIX
;
826 strncpyt(addr
.sun_path
, minissdpdsocketpath
, sizeof(addr
.sun_path
));
827 if (connect(s
, (struct sockaddr
*)&addr
, sizeof(struct sockaddr_un
)) < 0)
829 DPRINTF(E_ERROR
, L_SSDP
, "connect(\"%s\"): %s",
830 minissdpdsocketpath
, strerror(errno
));
834 for (i
= 0; known_service_types
[i
]; i
++)
838 l
= strlen(known_service_types
[i
]);
842 memcpy(p
, known_service_types
[i
], l
);
846 l
= snprintf(strbuf
, sizeof(strbuf
), "%s::%s%s",
847 uuidvalue
, known_service_types
[i
], (i
==0)?"":"1");
849 memcpy(p
, strbuf
, l
);
851 l
= strlen(MINIDLNA_SERVER_STRING
);
853 memcpy(p
, MINIDLNA_SERVER_STRING
, l
);
855 l
= snprintf(strbuf
, sizeof(strbuf
), "http://%s:%u" ROOTDESC_PATH
,
856 host
, (unsigned int)port
);
858 memcpy(p
, strbuf
, l
);
860 if(write(s
, buffer
, p
- buffer
) < 0)
862 DPRINTF(E_ERROR
, L_SSDP
, "write(): %s", strerror(errno
));