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.
37 #include <sys/socket.h>
39 #include <netinet/in.h>
40 #include <arpa/inet.h>
44 #include "upnpdescstrings.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
, in_addr_t ifaddr
)
62 struct ip_mreq imr
; /* Ip multicast membership */
64 /* setting up imr structure */
65 imr
.imr_multiaddr
.s_addr
= inet_addr(SSDP_MCAST_ADDR
);
66 /*imr.imr_interface.s_addr = htonl(INADDR_ANY);*/
67 imr
.imr_interface
.s_addr
= ifaddr
; /*inet_addr(ifaddr);*/
69 if (setsockopt(s
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
, (void *)&imr
, sizeof(struct ip_mreq
)) < 0)
71 DPRINTF(E_ERROR
, L_SSDP
, "setsockopt(udp, IP_ADD_MEMBERSHIP): %s\n", strerror(errno
));
78 /* Open and configure the socket listening for
79 * SSDP udp packets sent on 239.255.255.250 port 1900 */
81 OpenAndConfSSDPReceiveSocket()
85 struct sockaddr_in sockname
;
87 if( (s
= socket(PF_INET
, SOCK_DGRAM
, 0)) < 0)
89 DPRINTF(E_ERROR
, L_SSDP
, "socket(udp): %s\n", strerror(errno
));
93 if(setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &i
, sizeof(i
)) < 0)
95 DPRINTF(E_WARN
, L_SSDP
, "setsockopt(udp, SO_REUSEADDR): %s\n", strerror(errno
));
98 memset(&sockname
, 0, sizeof(struct sockaddr_in
));
99 sockname
.sin_family
= AF_INET
;
100 sockname
.sin_port
= htons(SSDP_PORT
);
101 /* NOTE : it seems it doesnt work when binding on the specific address */
102 /*sockname.sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);*/
103 sockname
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
104 /*sockname.sin_addr.s_addr = inet_addr(ifaddr);*/
106 if(bind(s
, (struct sockaddr
*)&sockname
, sizeof(struct sockaddr_in
)) < 0)
108 DPRINTF(E_ERROR
, L_SSDP
, "bind(udp): %s\n", strerror(errno
));
117 if(AddMulticastMembership(s
, lan_addr
[i
].addr
.s_addr
) < 0)
119 DPRINTF(E_WARN
, L_SSDP
,
120 "Failed to add multicast membership for address %s\n",
128 /* open the UDP socket used to send SSDP notifications to
129 * the multicast group reserved for them */
131 OpenAndConfSSDPNotifySocket(in_addr_t addr
)
134 unsigned char loopchar
= 0;
137 struct in_addr mc_if
;
138 struct sockaddr_in sockname
;
140 if( (s
= socket(PF_INET
, SOCK_DGRAM
, 0)) < 0)
142 DPRINTF(E_ERROR
, L_SSDP
, "socket(udp_notify): %s\n", strerror(errno
));
146 mc_if
.s_addr
= addr
; /*inet_addr(addr);*/
148 if(setsockopt(s
, IPPROTO_IP
, IP_MULTICAST_LOOP
, (char *)&loopchar
, sizeof(loopchar
)) < 0)
150 DPRINTF(E_ERROR
, L_SSDP
, "setsockopt(udp_notify, IP_MULTICAST_LOOP): %s\n", strerror(errno
));
155 if(setsockopt(s
, IPPROTO_IP
, IP_MULTICAST_IF
, (char *)&mc_if
, sizeof(mc_if
)) < 0)
157 DPRINTF(E_ERROR
, L_SSDP
, "setsockopt(udp_notify, IP_MULTICAST_IF): %s\n", strerror(errno
));
162 setsockopt(s
, IPPROTO_IP
, IP_MULTICAST_TTL
, &ttl
, sizeof(ttl
));
164 if(setsockopt(s
, SOL_SOCKET
, SO_BROADCAST
, &bcast
, sizeof(bcast
)) < 0)
166 DPRINTF(E_ERROR
, L_SSDP
, "setsockopt(udp_notify, SO_BROADCAST): %s\n", strerror(errno
));
171 memset(&sockname
, 0, sizeof(struct sockaddr_in
));
172 sockname
.sin_family
= AF_INET
;
173 sockname
.sin_addr
.s_addr
= addr
; /*inet_addr(addr);*/
175 if (bind(s
, (struct sockaddr
*)&sockname
, sizeof(struct sockaddr_in
)) < 0)
177 DPRINTF(E_ERROR
, L_SSDP
, "bind(udp_notify): %s\n", strerror(errno
));
186 OpenAndConfSSDPNotifySockets(int * sockets
)
189 for(i
=0; i
<n_lan_addr
; i
++)
191 sockets
[i
] = OpenAndConfSSDPNotifySocket(lan_addr
[i
].addr
.s_addr
);
206 * response from a LiveBox (Wanadoo)
208 CACHE-CONTROL: max-age=1800
209 DATE: Thu, 01 Jan 1970 04:03:23 GMT
211 LOCATION: http://192.168.0.1:49152/gatedesc.xml
212 SERVER: Linux/2.4.17, UPnP/1.0, Intel SDK for UPnP devices /1.2
214 USN: uuid:75802409-bccb-40e7-8e6c-fa095ecce13e::upnp:rootdevice
216 * response from a Linksys 802.11b :
218 Cache-Control:max-age=120
219 Location:http://192.168.5.1:5678/rootDesc.xml
220 Server:NT/5.0 UPnP/1.0
222 USN:uuid:upnp-InternetGatewayDevice-1_0-0090a2777777::upnp:rootdevice
226 static const char * const known_service_types
[] =
230 "urn:schemas-upnp-org:device:MediaServer:",
231 "urn:schemas-upnp-org:service:ContentDirectory:",
232 "urn:schemas-upnp-org:service:ConnectionManager:",
233 "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:",
240 struct timespec sleep_time
;
242 sleep_time
.tv_sec
= 0;
243 sleep_time
.tv_nsec
= usecs
* 1000;
244 nanosleep(&sleep_time
, NULL
);
247 /* not really an SSDP "announce" as it is the response
248 * to a SSDP "M-SEARCH" */
250 SendSSDPAnnounce2(int s
, struct sockaddr_in sockname
, int st_no
,
251 const char * host
, unsigned short port
)
256 * follow guideline from document "UPnP Device Architecture 1.0"
257 * uppercase is recommended.
258 * DATE: is recommended
259 * SERVER: OS/ver UPnP/1.0 minidlna/1.0
260 * - check what to put in the 'Cache-Control' header
263 time_t tTime
= time(NULL
);
264 strftime(szTime
, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&tTime
));
266 l
= snprintf(buf
, sizeof(buf
), "HTTP/1.1 200 OK\r\n"
267 "CACHE-CONTROL: max-age=%u\r\n"
272 "SERVER: " MINIDLNA_SERVER_STRING
"\r\n"
273 "LOCATION: http://%s:%u" ROOTDESC_PATH
"\r\n"
274 "Content-Length: 0\r\n"
276 (runtime_vars
.notify_interval
<<1)+10,
278 known_service_types
[st_no
], (st_no
>1?"1":""),
279 uuidvalue
, (st_no
>0?"::":""), (st_no
>0?known_service_types
[st_no
]:""), (st_no
>1?"1":""),
280 host
, (unsigned int)port
);
281 //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Sending M-SEARCH response:\n%s", buf);
282 n
= sendto(s
, buf
, l
, 0,
283 (struct sockaddr
*)&sockname
, sizeof(struct sockaddr_in
) );
286 DPRINTF(E_ERROR
, L_SSDP
, "sendto(udp): %s\n", strerror(errno
));
291 SendSSDPNotifies(int s
, const char * host
, unsigned short port
,
292 unsigned int lifetime
)
294 struct sockaddr_in sockname
;
298 memset(&sockname
, 0, sizeof(struct sockaddr_in
));
299 sockname
.sin_family
= AF_INET
;
300 sockname
.sin_port
= htons(SSDP_PORT
);
301 sockname
.sin_addr
.s_addr
= inet_addr(SSDP_MCAST_ADDR
);
303 for( dup
=0; dup
<2; dup
++ )
308 while(known_service_types
[i
])
310 l
= snprintf(bufr
, sizeof(bufr
),
311 "NOTIFY * HTTP/1.1\r\n"
313 "CACHE-CONTROL:max-age=%u\r\n"
314 "LOCATION:http://%s:%d" ROOTDESC_PATH
"\r\n"
315 "SERVER: " MINIDLNA_SERVER_STRING
"\r\n"
320 SSDP_MCAST_ADDR
, SSDP_PORT
,
323 known_service_types
[i
], (i
>1?"1":""),
324 uuidvalue
, (i
>0?"::":""), (i
>0?known_service_types
[i
]:""), (i
>1?"1":"") );
327 DPRINTF(E_WARN
, L_SSDP
, "SendSSDPNotifies(): truncated output\n");
330 //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Sending NOTIFY:\n%s", bufr);
331 n
= sendto(s
, bufr
, l
, 0,
332 (struct sockaddr
*)&sockname
, sizeof(struct sockaddr_in
) );
335 DPRINTF(E_ERROR
, L_SSDP
, "sendto(udp_notify=%d, %s): %s\n", s
, host
, strerror(errno
));
343 SendSSDPNotifies2(int * sockets
,
345 unsigned int lifetime
)
346 /*SendSSDPNotifies2(int * sockets, struct lan_addr_s * lan_addr, int n_lan_addr,
348 unsigned int lifetime)*/
351 DPRINTF(E_DEBUG
, L_SSDP
, "Sending SSDP notifies\n");
352 for(i
=0; i
<n_lan_addr
; i
++)
354 SendSSDPNotifies(sockets
[i
], lan_addr
[i
].str
, port
, lifetime
);
359 ParseUPnPClient(char *location
)
362 struct sockaddr_in dest
;
363 int s
, n
, do_headers
= 0, nread
= 0;
365 char *addr
, *path
, *port_str
;
367 char *off
= NULL
, *p
;
368 int content_len
= sizeof(buf
);
369 struct NameValueParserData xml
;
371 enum client_types type
= 0;
373 char *model
, *serial
, *name
;
375 if (strncmp(location
, "http://", 7) != 0)
378 port_str
= strsep(&path
, "/");
381 addr
= strsep(&port_str
, ":");
384 port
= strtol(port_str
, NULL
, 10);
389 memset(&dest
, '\0', sizeof(dest
));
390 if (!inet_aton(addr
, &dest
.sin_addr
))
392 /* Check if the client is already in cache */
393 dest
.sin_family
= AF_INET
;
394 dest
.sin_port
= htons(port
);
396 s
= socket(PF_INET
, SOCK_STREAM
, 0);
402 setsockopt(s
, SOL_SOCKET
, SO_RCVTIMEO
, &tv
, sizeof(tv
));
403 setsockopt(s
, SOL_SOCKET
, SO_SNDTIMEO
, &tv
, sizeof(tv
));
405 if( connect(s
, (struct sockaddr
*)&dest
, sizeof(struct sockaddr_in
)) < 0 )
408 n
= snprintf(buf
, sizeof(buf
), "GET /%s HTTP/1.0\r\n"
409 "HOST: %s:%ld\r\n\r\n",
411 if( write(s
, buf
, n
) < 1 )
414 while( (n
= read(s
, buf
+nread
, sizeof(buf
)-nread
-1)) > 0 )
421 while( !off
&& n
-- > 0 )
423 if(p
[0]=='\r' && p
[1]=='\n' && p
[2]=='\r' && p
[3]=='\n')
436 if( strncmp(p
, "HTTP/", 5) != 0 )
438 while(*p
!= ' ' && *p
!= '\t') p
++;
439 /* If we don't get a 200 status, ignore it */
440 if( strtol(p
, NULL
, 10) != 200 )
442 if( (p
= strcasestr(p
, "Content-Length:")) )
443 content_len
= strtol(p
+15, NULL
, 10);
446 if( buf
+ nread
- off
>= content_len
)
454 ParseNameValue(off
, nread
, &xml
);
455 model
= GetValueFromNameValueList(&xml
, "modelName");
456 serial
= GetValueFromNameValueList(&xml
, "serialNumber");
457 name
= GetValueFromNameValueList(&xml
, "friendlyName");
460 DPRINTF(E_DEBUG
, L_SSDP
, "Model: %s\n", model
);
461 if( strstr(model
, "Roku SoundBridge") )
463 type
= ERokuSoundBridge
;
464 flags
|= FLAG_MS_PFS
;
465 flags
|= FLAG_AUDIO_ONLY
;
466 flags
|= FLAG_MIME_WAV_WAV
;
468 else if( strcmp(model
, "Samsung DTV DMR") == 0 && serial
)
470 DPRINTF(E_DEBUG
, L_SSDP
, "Serial: %s\n", serial
);
471 /* The Series B I saw was 20081224DMR. Series A should be older than that. */
472 if( atoi(serial
) > 20081200 )
474 type
= ESamsungSeriesB
;
475 flags
|= FLAG_SAMSUNG
;
477 flags
|= FLAG_NO_RESIZE
;
482 if( name
&& (strcmp(name
, "marantz DMP") == 0) )
486 flags
|= FLAG_MIME_WAV_WAV
;
490 ClearNameValueList(&xml
);
493 /* Add this client to the cache if it's not there already. */
494 client
= SearchClientCache(dest
.sin_addr
, 1);
497 for( client
=0; client
<CLIENT_CACHE_SLOTS
; client
++ )
499 if( clients
[client
].addr
.s_addr
)
501 get_remote_mac(dest
.sin_addr
, clients
[client
].mac
);
502 clients
[client
].addr
= dest
.sin_addr
;
503 DPRINTF(E_DEBUG
, L_SSDP
, "Added client [%d/%s/%02X:%02X:%02X:%02X:%02X:%02X] to cache slot %d.\n",
504 type
, inet_ntoa(clients
[client
].addr
),
505 clients
[client
].mac
[0], clients
[client
].mac
[1], clients
[client
].mac
[2],
506 clients
[client
].mac
[3], clients
[client
].mac
[4], clients
[client
].mac
[5], client
);
510 clients
[client
].type
= type
;
511 clients
[client
].flags
= flags
;
512 clients
[client
].age
= time(NULL
);
515 /* ProcessSSDPRequest()
516 * process SSDP M-SEARCH requests and responds to them */
518 ProcessSSDPRequest(int s
, unsigned short port
)
523 struct sockaddr_in sendername
;
525 char *st
= NULL
, *mx
= NULL
, *man
= NULL
, *mx_end
= NULL
;
527 len_r
= sizeof(struct sockaddr_in
);
529 n
= recvfrom(s
, bufr
, sizeof(bufr
)-1, 0,
530 (struct sockaddr
*)&sendername
, &len_r
);
533 DPRINTF(E_ERROR
, L_SSDP
, "recvfrom(udp): %s\n", strerror(errno
));
538 if(memcmp(bufr
, "NOTIFY", 6) == 0)
540 char *loc
= NULL
, *srv
= NULL
, *nts
= NULL
, *nt
= NULL
;
542 //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Received SSDP notify:\n%.*s", n, bufr);
548 if( !strcasestrc(bufr
+i
, "HTTP/1.1", '\r') )
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
);
601 else if(memcmp(bufr
, "M-SEARCH", 8) == 0)
603 int st_len
= 0, mx_len
= 0, mx_val
= 0;
604 //DPRINTF(E_DEBUG, L_SSDP, "Received SSDP request:\n%.*s\n", n, bufr);
610 if( !strcasestrc(bufr
+i
, "HTTP/1.1", '\r') )
614 while((i
< n
- 2) && (bufr
[i
] != '\r' || bufr
[i
+1] != '\n'))
617 if(strncasecmp(bufr
+i
, "ST:", 3) == 0)
621 while(*st
== ' ' || *st
== '\t') st
++;
622 while(st
[st_len
]!='\r' && st
[st_len
]!='\n') st_len
++;
624 else if(strncasecmp(bufr
+i
, "MX:", 3) == 0)
628 while(*mx
== ' ' || *mx
== '\t') mx
++;
629 while(mx
[mx_len
]!='\r' && mx
[mx_len
]!='\n') mx_len
++;
630 mx_val
= strtol(mx
, &mx_end
, 10);
632 else if(strncasecmp(bufr
+i
, "MAN:", 4) == 0)
636 while(*man
== ' ' || *man
== '\t') man
++;
637 while(man
[man_len
]!='\r' && man
[man_len
]!='\n') man_len
++;
640 /*DPRINTF(E_INFO, L_SSDP, "SSDP M-SEARCH packet received from %s:%d\n",
641 inet_ntoa(sendername.sin_addr),
642 ntohs(sendername.sin_port) );*/
643 if( GETFLAG(DLNA_STRICT_MASK
) && (ntohs(sendername
.sin_port
) <= 1024 || ntohs(sendername
.sin_port
) == 1900) )
645 DPRINTF(E_INFO
, L_SSDP
, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad source port %d]\n",
646 inet_ntoa(sendername
.sin_addr
), ntohs(sendername
.sin_port
));
648 else if( !man
|| (strncmp(man
, "\"ssdp:discover\"", 15) != 0) )
650 DPRINTF(E_INFO
, L_SSDP
, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad %s header '%.*s']\n",
651 inet_ntoa(sendername
.sin_addr
), "MAN", man_len
, man
);
653 else if( !mx
|| mx
== mx_end
|| mx_val
< 0 ) {
654 DPRINTF(E_INFO
, L_SSDP
, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad %s header '%.*s']\n",
655 inet_ntoa(sendername
.sin_addr
), "MX", mx_len
, mx
);
657 else if( st
&& (st_len
> 0) )
660 int lan_addr_index
= 0;
661 /* find in which sub network the client is */
662 for(i
= 0; i
<n_lan_addr
; i
++)
664 if( (sendername
.sin_addr
.s_addr
& lan_addr
[i
].mask
.s_addr
)
665 == (lan_addr
[i
].addr
.s_addr
& lan_addr
[i
].mask
.s_addr
))
671 if( i
== n_lan_addr
)
673 DPRINTF(E_DEBUG
, L_SSDP
, "Ignoring SSDP M-SEARCH on other interface [%s]\n",
674 inet_ntoa(sendername
.sin_addr
));
677 DPRINTF(E_INFO
, L_SSDP
, "SSDP M-SEARCH from %s:%d ST: %.*s, MX: %.*s, MAN: %.*s\n",
678 inet_ntoa(sendername
.sin_addr
),
679 ntohs(sendername
.sin_port
),
680 st_len
, st
, mx_len
, mx
, man_len
, man
);
681 /* Responds to request with a device as ST header */
682 for(i
= 0; known_service_types
[i
]; i
++)
684 l
= strlen(known_service_types
[i
]);
685 if( l
<= st_len
&& (memcmp(st
, known_service_types
[i
], l
) == 0))
689 /* Check version number - must always be 1 currently. */
690 if( (st
[l
-1] == ':') && (st
[l
] == '1') )
694 if( !isspace(st
[l
]) )
696 DPRINTF(E_DEBUG
, L_SSDP
, "Ignoring SSDP M-SEARCH with bad extra data [%s]\n",
697 inet_ntoa(sendername
.sin_addr
));
705 _usleep(random()>>20);
706 SendSSDPAnnounce2(s
, sendername
,
708 lan_addr
[lan_addr_index
].str
, port
);
712 /* Responds to request with ST: ssdp:all */
713 /* strlen("ssdp:all") == 8 */
714 if(st_len
==8 && (0 == memcmp(st
, "ssdp:all", 8)))
716 for(i
=0; known_service_types
[i
]; i
++)
718 l
= (int)strlen(known_service_types
[i
]);
719 SendSSDPAnnounce2(s
, sendername
,
721 lan_addr
[lan_addr_index
].str
, port
);
727 DPRINTF(E_INFO
, L_SSDP
, "Invalid SSDP M-SEARCH from %s:%d\n",
728 inet_ntoa(sendername
.sin_addr
), ntohs(sendername
.sin_port
));
733 DPRINTF(E_WARN
, L_SSDP
, "Unknown udp packet received from %s:%d\n",
734 inet_ntoa(sendername
.sin_addr
), ntohs(sendername
.sin_port
));
738 /* This will broadcast ssdp:byebye notifications to inform
739 * the network that UPnP is going down. */
741 SendSSDPGoodbye(int * sockets
, int n_sockets
)
743 struct sockaddr_in sockname
;
749 memset(&sockname
, 0, sizeof(struct sockaddr_in
));
750 sockname
.sin_family
= AF_INET
;
751 sockname
.sin_port
= htons(SSDP_PORT
);
752 sockname
.sin_addr
.s_addr
= inet_addr(SSDP_MCAST_ADDR
);
754 for (dup
= 0; dup
< 2; dup
++)
756 for(j
=0; j
<n_sockets
; j
++)
758 for(i
=0; known_service_types
[i
]; i
++)
760 l
= snprintf(bufr
, sizeof(bufr
),
761 "NOTIFY * HTTP/1.1\r\n"
765 "NTS:ssdp:byebye\r\n"
767 SSDP_MCAST_ADDR
, SSDP_PORT
,
768 known_service_types
[i
], (i
>1?"1":""),
769 uuidvalue
, (i
>0?"::":""), (i
>0?known_service_types
[i
]:""), (i
>1?"1":"") );
770 //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Sending NOTIFY:\n%s", bufr);
771 n
= sendto(sockets
[j
], bufr
, l
, 0,
772 (struct sockaddr
*)&sockname
, sizeof(struct sockaddr_in
) );
775 DPRINTF(E_ERROR
, L_SSDP
, "sendto(udp_shutdown=%d): %s\n", sockets
[j
], strerror(errno
));
785 /* SubmitServicesToMiniSSDPD() :
786 * register services offered by MiniUPnPd to a running instance of
789 SubmitServicesToMiniSSDPD(const char * host
, unsigned short port
) {
790 struct sockaddr_un addr
;
792 unsigned char buffer
[2048];
797 s
= socket(AF_UNIX
, SOCK_STREAM
, 0);
799 DPRINTF(E_ERROR
, L_SSDP
, "socket(unix): %s", strerror(errno
));
802 addr
.sun_family
= AF_UNIX
;
803 strncpy(addr
.sun_path
, minissdpdsocketpath
, sizeof(addr
.sun_path
));
804 if(connect(s
, (struct sockaddr
*)&addr
, sizeof(struct sockaddr_un
)) < 0) {
805 DPRINTF(E_ERROR
, L_SSDP
, "connect(\"%s\"): %s",
806 minissdpdsocketpath
, strerror(errno
));
809 for(i
= 0; known_service_types
[i
]; i
++) {
812 l
= (int)strlen(known_service_types
[i
]);
816 memcpy(p
, known_service_types
[i
], l
);
820 l
= snprintf(strbuf
, sizeof(strbuf
), "%s::%s%s",
821 uuidvalue
, known_service_types
[i
], (i
==0)?"":"1");
823 memcpy(p
, strbuf
, l
);
825 l
= (int)strlen(MINIDLNA_SERVER_STRING
);
827 memcpy(p
, MINIDLNA_SERVER_STRING
, l
);
829 l
= snprintf(strbuf
, sizeof(strbuf
), "http://%s:%u" ROOTDESC_PATH
,
830 host
, (unsigned int)port
);
832 memcpy(p
, strbuf
, l
);
834 if(write(s
, buffer
, p
- buffer
) < 0) {
835 DPRINTF(E_ERROR
, L_SSDP
, "write(): %s", strerror(errno
));