1 /* $Id: minissdp.c,v 1.48 2013/02/07 12:22:25 nanard Exp $ */
3 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4 * (c) 2006-2013 Thomas Bernard
5 * This software is subject to the conditions detailed
6 * in the LICENCE file provided within the distribution */
12 #include <sys/socket.h>
14 #include <netinet/in.h>
15 #include <arpa/inet.h>
20 #include "upnpdescstrings.h"
21 #include "miniupnpdpath.h"
23 #include "upnpglobalvars.h"
25 #include "upnputils.h"
27 #include "codelength.h"
30 #define SSDP_PORT (1900)
31 #define SSDP_MCAST_ADDR ("239.255.255.250")
32 #define LL_SSDP_MCAST_ADDR "FF02::C"
33 #define SL_SSDP_MCAST_ADDR "FF05::C"
35 /* AddMulticastMembership()
37 * param ifaddr ip v4 address
40 AddMulticastMembership(int s
, in_addr_t ifaddr
)
42 struct ip_mreq imr
; /* Ip multicast membership */
44 /* setting up imr structure */
45 imr
.imr_multiaddr
.s_addr
= inet_addr(SSDP_MCAST_ADDR
);
46 /*imr.imr_interface.s_addr = htonl(INADDR_ANY);*/
47 imr
.imr_interface
.s_addr
= ifaddr
; /*inet_addr(ifaddr);*/
49 if (setsockopt(s
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
, (void *)&imr
, sizeof(struct ip_mreq
)) < 0)
51 syslog(LOG_ERR
, "setsockopt(udp, IP_ADD_MEMBERSHIP): %m");
58 /* AddMulticastMembershipIPv6()
59 * param s socket (IPv6)
60 * To be improved to target specific network interfaces */
63 AddMulticastMembershipIPv6(int s
)
66 /*unsigned int ifindex;*/
68 memset(&mr
, 0, sizeof(mr
));
69 inet_pton(AF_INET6
, LL_SSDP_MCAST_ADDR
, &mr
.ipv6mr_multiaddr
);
70 /*mr.ipv6mr_interface = ifindex;*/
71 mr
.ipv6mr_interface
= 0; /* 0 : all interfaces */
72 #ifndef IPV6_ADD_MEMBERSHIP
73 #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
75 if(setsockopt(s
, IPPROTO_IPV6
, IPV6_ADD_MEMBERSHIP
, &mr
, sizeof(struct ipv6_mreq
)) < 0)
77 syslog(LOG_ERR
, "setsockopt(udp, IPV6_ADD_MEMBERSHIP): %m");
80 inet_pton(AF_INET6
, SL_SSDP_MCAST_ADDR
, &mr
.ipv6mr_multiaddr
);
81 if(setsockopt(s
, IPPROTO_IPV6
, IPV6_ADD_MEMBERSHIP
, &mr
, sizeof(struct ipv6_mreq
)) < 0)
83 syslog(LOG_ERR
, "setsockopt(udp, IPV6_ADD_MEMBERSHIP): %m");
90 /* Open and configure the socket listening for
91 * SSDP udp packets sent on 239.255.255.250 port 1900
92 * SSDP v6 udp packets sent on FF02::C, or FF05::C, port 1900 */
94 OpenAndConfSSDPReceiveSocket(int ipv6
)
97 struct sockaddr_storage sockname
;
98 socklen_t sockname_len
;
99 struct lan_addr_s
* lan_addr
;
102 if( (s
= socket(ipv6
? PF_INET6
: PF_INET
, SOCK_DGRAM
, 0)) < 0)
104 syslog(LOG_ERR
, "%s: socket(udp): %m",
105 "OpenAndConfSSDPReceiveSocket");
109 memset(&sockname
, 0, sizeof(struct sockaddr_storage
));
111 struct sockaddr_in6
* saddr
= (struct sockaddr_in6
*)&sockname
;
112 saddr
->sin6_family
= AF_INET6
;
113 saddr
->sin6_port
= htons(SSDP_PORT
);
114 saddr
->sin6_addr
= in6addr_any
;
115 sockname_len
= sizeof(struct sockaddr_in6
);
117 struct sockaddr_in
* saddr
= (struct sockaddr_in
*)&sockname
;
118 saddr
->sin_family
= AF_INET
;
119 saddr
->sin_port
= htons(SSDP_PORT
);
120 /* NOTE : it seems it doesnt work when binding on the specific address */
121 /*saddr->sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);*/
122 saddr
->sin_addr
.s_addr
= htonl(INADDR_ANY
);
123 /*saddr->sin_addr.s_addr = inet_addr(ifaddr);*/
124 sockname_len
= sizeof(struct sockaddr_in
);
127 if(setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &j
, sizeof(j
)) < 0)
129 syslog(LOG_WARNING
, "setsockopt(udp, SO_REUSEADDR): %m");
132 if(!set_non_blocking(s
))
134 syslog(LOG_WARNING
, "%s: set_non_blocking(): %m",
135 "OpenAndConfSSDPReceiveSocket");
138 if(bind(s
, (struct sockaddr
*)&sockname
, sockname_len
) < 0)
140 syslog(LOG_ERR
, "%s: bind(udp%s): %m",
141 "OpenAndConfSSDPReceiveSocket", ipv6
? "6" : "");
149 AddMulticastMembershipIPv6(s
);
154 for(lan_addr
= lan_addrs
.lh_first
; lan_addr
!= NULL
; lan_addr
= lan_addr
->list
.le_next
)
156 if(AddMulticastMembership(s
, lan_addr
->addr
.s_addr
) < 0)
159 "Failed to add multicast membership for interface %s",
160 lan_addr
->str
? lan_addr
->str
: "NULL");
168 /* open the UDP socket used to send SSDP notifications to
169 * the multicast group reserved for them */
171 OpenAndConfSSDPNotifySocket(in_addr_t addr
)
174 unsigned char loopchar
= 0;
176 unsigned char ttl
= 2; /* UDA v1.1 says :
177 The TTL for the IP packet SHOULD default to 2 and
178 SHOULD be configurable. */
179 /* TODO: Make TTL be configurable */
180 struct in_addr mc_if
;
181 struct sockaddr_in sockname
;
183 if( (s
= socket(PF_INET
, SOCK_DGRAM
, 0)) < 0)
185 syslog(LOG_ERR
, "socket(udp_notify): %m");
189 mc_if
.s_addr
= addr
; /*inet_addr(addr);*/
191 if(setsockopt(s
, IPPROTO_IP
, IP_MULTICAST_LOOP
, (char *)&loopchar
, sizeof(loopchar
)) < 0)
193 syslog(LOG_ERR
, "setsockopt(udp_notify, IP_MULTICAST_LOOP): %m");
198 if(setsockopt(s
, IPPROTO_IP
, IP_MULTICAST_IF
, (char *)&mc_if
, sizeof(mc_if
)) < 0)
200 syslog(LOG_ERR
, "setsockopt(udp_notify, IP_MULTICAST_IF): %m");
205 if(setsockopt(s
, IPPROTO_IP
, IP_MULTICAST_TTL
, &ttl
, sizeof(ttl
)) < 0)
207 syslog(LOG_WARNING
, "setsockopt(udp_notify, IP_MULTICAST_TTL,): %m");
210 if(setsockopt(s
, SOL_SOCKET
, SO_BROADCAST
, &bcast
, sizeof(bcast
)) < 0)
212 syslog(LOG_ERR
, "setsockopt(udp_notify, SO_BROADCAST): %m");
217 memset(&sockname
, 0, sizeof(struct sockaddr_in
));
218 sockname
.sin_family
= AF_INET
;
219 sockname
.sin_addr
.s_addr
= addr
; /*inet_addr(addr);*/
221 if (bind(s
, (struct sockaddr
*)&sockname
, sizeof(struct sockaddr_in
)) < 0)
223 syslog(LOG_ERR
, "bind(udp_notify): %m");
232 /* open the UDP socket used to send SSDP notifications to
233 * the multicast group reserved for them. IPv6 */
235 OpenAndConfSSDPNotifySocketIPv6(unsigned int if_index
)
238 unsigned int loop
= 0;
240 s
= socket(PF_INET6
, SOCK_DGRAM
, 0);
243 syslog(LOG_ERR
, "socket(udp_notify IPv6): %m");
246 if(setsockopt(s
, IPPROTO_IPV6
, IPV6_MULTICAST_IF
, &if_index
, sizeof(if_index
)) < 0)
248 syslog(LOG_ERR
, "setsockopt(udp_notify IPv6, IPV6_MULTICAST_IF, %u): %m", if_index
);
252 if(setsockopt(s
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, &loop
, sizeof(loop
)) < 0)
254 syslog(LOG_ERR
, "setsockopt(udp_notify, IPV6_MULTICAST_LOOP): %m");
263 OpenAndConfSSDPNotifySockets(int * sockets
)
264 /*OpenAndConfSSDPNotifySockets(int * sockets,
265 struct lan_addr_s * lan_addr, int n_lan_addr)*/
268 struct lan_addr_s
* lan_addr
;
270 for(i
=0, lan_addr
= lan_addrs
.lh_first
;
272 lan_addr
= lan_addr
->list
.le_next
)
274 sockets
[i
] = OpenAndConfSSDPNotifySocket(lan_addr
->addr
.s_addr
);
279 sockets
[i
] = OpenAndConfSSDPNotifySocketIPv6(lan_addr
->index
);
296 * response from a LiveBox (Wanadoo)
298 CACHE-CONTROL: max-age=1800
299 DATE: Thu, 01 Jan 1970 04:03:23 GMT
301 LOCATION: http://192.168.0.1:49152/gatedesc.xml
302 SERVER: Linux/2.4.17, UPnP/1.0, Intel SDK for UPnP devices /1.2
304 USN: uuid:75802409-bccb-40e7-8e6c-fa095ecce13e::upnp:rootdevice
306 * response from a Linksys 802.11b :
308 Cache-Control:max-age=120
309 Location:http://192.168.5.1:5678/rootDesc.xml
310 Server:NT/5.0 UPnP/1.0
312 USN:uuid:upnp-InternetGatewayDevice-1_0-0090a2777777::upnp:rootdevice
316 /* not really an SSDP "announce" as it is the response
317 * to a SSDP "M-SEARCH" */
319 SendSSDPAnnounce2(int s
, const struct sockaddr
* addr
,
320 const char * st
, int st_len
, const char * suffix
,
321 const char * host
, unsigned short port
)
328 #ifdef ENABLE_HTTP_DATE
335 strftime(http_date
, sizeof(http_date
),
336 "%a, %d %b %Y %H:%M:%S GMT", &tm
);
339 st_is_uuid
= (st_len
== (int)strlen(uuidvalue
)) &&
340 (memcmp(uuidvalue
, st
, st_len
) == 0);
342 * follow guideline from document "UPnP Device Architecture 1.0"
343 * uppercase is recommended.
344 * DATE: is recommended
345 * SERVER: OS/ver UPnP/1.0 miniupnpd/1.0
346 * - check what to put in the 'Cache-Control' header
348 * have a look at the document "UPnP Device Architecture v1.1 */
349 l
= snprintf(buf
, sizeof(buf
), "HTTP/1.1 200 OK\r\n"
350 "CACHE-CONTROL: max-age=120\r\n"
351 #ifdef ENABLE_HTTP_DATE
355 "USN: %s%s%.*s%s\r\n"
357 "SERVER: " MINIUPNPD_SERVER_STRING
"\r\n"
358 "LOCATION: http://%s:%u" ROOTDESC_PATH
"\r\n"
359 "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */
360 "01-NLS: %u\r\n" /* same as BOOTID. UDA v1.1 */
361 "BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
362 "CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
364 #ifdef ENABLE_HTTP_DATE
368 uuidvalue
, st_is_uuid
? "" : "::",
369 st_is_uuid
? 0 : st_len
, st
, suffix
,
370 host
, (unsigned int)port
,
371 upnp_bootid
, upnp_bootid
, upnp_configid
);
372 addrlen
= (addr
->sa_family
== AF_INET6
)
373 ? sizeof(struct sockaddr_in6
) : sizeof(struct sockaddr_in
);
374 n
= sendto(s
, buf
, l
, 0,
376 sockaddr_to_string(addr
, addr_str
, sizeof(addr_str
));
377 syslog(LOG_INFO
, "SSDP Announce %d bytes to %s ST: %.*s",n
,
382 /* XXX handle EINTR, EAGAIN, EWOULDBLOCK */
383 syslog(LOG_ERR
, "sendto(udp): %m");
398 } const known_service_types
[] =
400 {"upnp:rootdevice", 0},
401 {"urn:schemas-upnp-org:device:InternetGatewayDevice:", IGD_VER
},
402 {"urn:schemas-upnp-org:device:WANConnectionDevice:", 1},
403 {"urn:schemas-upnp-org:device:WANDevice:", 1},
404 {"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:", 1},
405 {"urn:schemas-upnp-org:service:WANIPConnection:", WANIPC_VER
},
406 {"urn:schemas-upnp-org:service:WANPPPConnection:", 1},
407 #ifdef ENABLE_L3F_SERVICE
408 {"urn:schemas-upnp-org:service:Layer3Forwarding:", 1},
410 #ifdef ENABLE_6FC_SERVICE
411 {"url:schemas-upnp-org:service:WANIPv6FirewallControl:", 1},
417 SendSSDPNotify(int s
, const struct sockaddr
* dest
,
418 const char * host
, unsigned short port
,
419 const char * nt
, const char * suffix
,
420 const char * usn1
, const char * usn2
, const char * usn3
,
421 unsigned int lifetime
, int ipv6
)
426 l
= snprintf(bufr
, sizeof(bufr
),
427 "NOTIFY * HTTP/1.1\r\n"
429 "CACHE-CONTROL: max-age=%u\r\n"
430 "LOCATION: http://%s:%d" ROOTDESC_PATH
"\r\n"
431 "SERVER: " MINIUPNPD_SERVER_STRING
"\r\n"
434 "NTS: ssdp:alive\r\n"
435 "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */
436 "01-NLS: %u\r\n" /* same as BOOTID field. UDA v1.1 */
437 "BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
438 "CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
440 ipv6
? "[" LL_SSDP_MCAST_ADDR
"]" : SSDP_MCAST_ADDR
,
444 nt
, suffix
, /* NT: */
445 usn1
, usn2
, usn3
, suffix
, /* USN: */
446 upnp_bootid
, upnp_bootid
, upnp_configid
);
449 syslog(LOG_ERR
, "SendSSDPNotifies() snprintf error");
452 else if((unsigned int)l
>= sizeof(bufr
))
454 syslog(LOG_WARNING
, "SendSSDPNotifies(): truncated output");
457 n
= sendto(s
, bufr
, l
, 0, dest
,
459 ipv6
? sizeof(struct sockaddr_in6
) : sizeof(struct sockaddr_in
)
461 sizeof(struct sockaddr_in
)
466 /* XXX handle EINTR, EAGAIN, EWOULDBLOCK */
467 syslog(LOG_ERR
, "sendto(udp_notify=%d, %s): %m", s
,
468 host
? host
: "NULL");
472 syslog(LOG_NOTICE
, "sendto() sent %d out of %d bytes", n
, l
);
477 SendSSDPNotifies(int s
, const char * host
, unsigned short port
,
478 unsigned int lifetime
, int ipv6
)
481 struct sockaddr_storage sockname
;
483 struct sockaddr_in sockname
;
488 memset(&sockname
, 0, sizeof(sockname
));
492 struct sockaddr_in6
* p
= (struct sockaddr_in6
*)&sockname
;
493 p
->sin6_family
= AF_INET6
;
494 p
->sin6_port
= htons(SSDP_PORT
);
495 inet_pton(AF_INET6
, LL_SSDP_MCAST_ADDR
, &(p
->sin6_addr
));
500 struct sockaddr_in
*p
= (struct sockaddr_in
*)&sockname
;
501 p
->sin_family
= AF_INET
;
502 p
->sin_port
= htons(SSDP_PORT
);
503 p
->sin_addr
.s_addr
= inet_addr(SSDP_MCAST_ADDR
);
506 while(known_service_types
[i
].s
)
511 snprintf(ver_str
, sizeof(ver_str
), "%d", known_service_types
[i
].version
);
512 SendSSDPNotify(s
, (struct sockaddr
*)&sockname
, host
, port
,
513 known_service_types
[i
].s
, ver_str
, /* NT: */
514 uuidvalue
, "::", known_service_types
[i
].s
, /* ver_str, USN: */
516 if(i
==0) /* rootdevice */
517 SendSSDPNotify(s
, (struct sockaddr
*)&sockname
, host
, port
,
518 uuidvalue
, "", /* NT: */
519 uuidvalue
, "", "", /* ver_str, USN: */
526 SendSSDPNotifies2(int * sockets
,
528 unsigned int lifetime
)
531 struct lan_addr_s
* lan_addr
;
532 for(i
=0, lan_addr
= lan_addrs
.lh_first
;
534 lan_addr
= lan_addr
->list
.le_next
)
536 SendSSDPNotifies(sockets
[i
], lan_addr
->str
, port
,
540 SendSSDPNotifies(sockets
[i
], ipv6_addr_for_http_with_brackets
, port
,
547 /* ProcessSSDPRequest()
548 * process SSDP M-SEARCH requests and responds to them */
550 ProcessSSDPRequest(int s
, unsigned short port
)
556 struct sockaddr_storage sendername
;
557 len_r
= sizeof(struct sockaddr_storage
);
559 struct sockaddr_in sendername
;
560 len_r
= sizeof(struct sockaddr_in
);
563 n
= recvfrom(s
, bufr
, sizeof(bufr
), 0,
564 (struct sockaddr
*)&sendername
, &len_r
);
567 /* EAGAIN, EWOULDBLOCK, EINTR : silently ignore (try again next time)
568 * other errors : log to LOG_ERR */
569 if(errno
!= EAGAIN
&&
570 errno
!= EWOULDBLOCK
&&
573 syslog(LOG_ERR
, "recvfrom(udp): %m");
577 ProcessSSDPData(s
, bufr
, n
, (struct sockaddr
*)&sendername
, port
);
582 ProcessSSDPData(int s
, const char *bufr
, int n
,
583 const struct sockaddr
* sender
, unsigned short port
) {
585 struct lan_addr_s
* lan_addr
= NULL
;
586 const char * st
= NULL
;
591 const char * announced_host
= NULL
;
594 char announced_host_buf
[64];
599 /* get the string representation of the sender address */
600 sockaddr_to_string(sender
, sender_str
, sizeof(sender_str
));
601 lan_addr
= get_lan_for_peer(sender
);
604 syslog(LOG_WARNING
, "SSDP packet sender %s not from a LAN, ignoring",
609 if(memcmp(bufr
, "NOTIFY", 6) == 0)
611 /* ignore NOTIFY packets. We could log the sender and device type */
614 else if(memcmp(bufr
, "M-SEARCH", 8) == 0)
619 while((i
< n
- 1) && (bufr
[i
] != '\r' || bufr
[i
+1] != '\n'))
622 if((i
< n
- 3) && (strncasecmp(bufr
+i
, "st:", 3) == 0))
626 while((*st
== ' ' || *st
== '\t') && (st
< bufr
+ n
))
628 while(st
[st_len
]!='\r' && st
[st_len
]!='\n'
629 && (st
+ st_len
< bufr
+ n
))
632 while(l
> 0 && st
[l
-1] != ':')
635 syslog(LOG_DEBUG
, "ST: %.*s (ver=%d)", st_len
, st
, st_ver
);
637 /*while(bufr[i+j]!='\r') j++;*/
638 /*syslog(LOG_INFO, "%.*s", j, bufr+i);*/
641 else if((i
< n
- 3) && (strncasecmp(bufr
+i
, "mx:", 3) == 0))
647 while((*mx
== ' ' || *mx
== '\t') && (mx
< bufr
+ n
))
649 while(mx
[mx_len
]!='\r' && mx
[mx_len
]!='\n'
650 && (mx
+ mx_len
< bufr
+ n
))
653 syslog(LOG_DEBUG
, "MX: %.*s (value=%d)", mx_len
, mx
, mx_value
);
659 syslog(LOG_INFO
, "ignoring SSDP packet missing MX: header");
663 /*syslog(LOG_INFO, "SSDP M-SEARCH packet received from %s",
665 if(st
&& (st_len
> 0))
667 /* TODO : doesnt answer at once but wait for a random time */
668 syslog(LOG_INFO
, "SSDP M-SEARCH from %s ST: %.*s",
669 sender_str
, st_len
, st
);
670 /* find in which sub network the client is */
671 if(sender
->sa_family
== AF_INET
)
673 if (lan_addr
== NULL
)
675 syslog(LOG_ERR
, "Can't find in which sub network the client is");
678 announced_host
= lan_addr
->str
;
683 /* IPv6 address with brackets */
686 struct in6_addr addr6
;
687 size_t addr6_len
= sizeof(addr6
);
688 /* retrieve the IPv6 address which
689 * will be used locally to reach sender */
690 memset(&addr6
, 0, sizeof(addr6
));
691 if(get_src_for_route_to (sender
, &addr6
, &addr6_len
, &index
) < 0) {
692 syslog(LOG_WARNING
, "get_src_for_route_to() failed, using %s", ipv6_addr_for_http_with_brackets
);
693 announced_host
= ipv6_addr_for_http_with_brackets
;
695 if(inet_ntop(AF_INET6
, &addr6
,
696 announced_host_buf
+1,
697 sizeof(announced_host_buf
) - 2)) {
698 announced_host_buf
[0] = '[';
699 i
= strlen(announced_host_buf
);
700 if(i
< (int)sizeof(announced_host_buf
) - 1) {
701 announced_host_buf
[i
] = ']';
702 announced_host_buf
[i
+1] = '\0';
704 syslog(LOG_NOTICE
, "cannot suffix %s with ']'",
707 announced_host
= announced_host_buf
;
709 syslog(LOG_NOTICE
, "inet_ntop() failed %m");
710 announced_host
= ipv6_addr_for_http_with_brackets
;
714 announced_host
= ipv6_addr_for_http_with_brackets
;
718 /* Responds to request with a device as ST header */
719 for(i
= 0; known_service_types
[i
].s
; i
++)
721 l
= (int)strlen(known_service_types
[i
].s
);
722 if(l
<=st_len
&& (0 == memcmp(st
, known_service_types
[i
].s
, l
))
724 && (st_ver
<= known_service_types
[i
].version
)
725 /* only answer for service version lower or equal of supported one */
729 syslog(LOG_INFO
, "Single search found");
730 SendSSDPAnnounce2(s
, sender
,
732 announced_host
, port
);
736 /* Responds to request with ST: ssdp:all */
737 /* strlen("ssdp:all") == 8 */
738 if(st_len
==8 && (0 == memcmp(st
, "ssdp:all", 8)))
740 syslog(LOG_INFO
, "ssdp:all found");
741 for(i
=0; known_service_types
[i
].s
; i
++)
746 snprintf(ver_str
, sizeof(ver_str
), "%d", known_service_types
[i
].version
);
747 l
= (int)strlen(known_service_types
[i
].s
);
748 SendSSDPAnnounce2(s
, sender
,
749 known_service_types
[i
].s
, l
, ver_str
,
750 announced_host
, port
);
752 /* also answer for uuid */
753 SendSSDPAnnounce2(s
, sender
, uuidvalue
, strlen(uuidvalue
), "",
754 announced_host
, port
);
756 /* responds to request by UUID value */
757 l
= (int)strlen(uuidvalue
);
758 if(l
==st_len
&& (0 == memcmp(st
, uuidvalue
, l
)))
760 syslog(LOG_INFO
, "ssdp:uuid found");
761 SendSSDPAnnounce2(s
, sender
, st
, st_len
, "",
762 announced_host
, port
);
767 syslog(LOG_INFO
, "Invalid SSDP M-SEARCH from %s", sender_str
);
772 syslog(LOG_NOTICE
, "Unknown udp packet received from %s", sender_str
);
777 SendSSDPbyebye(int s
, const struct sockaddr
* dest
,
778 const char * nt
, const char * suffix
,
779 const char * usn1
, const char * usn2
, const char * usn3
,
785 l
= snprintf(bufr
, sizeof(bufr
),
786 "NOTIFY * HTTP/1.1\r\n"
790 "NTS: ssdp:byebye\r\n"
791 "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */
792 "01-NLS: %u\r\n" /* same as BOOTID field. UDA v1.1 */
793 "BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
794 "CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
796 ipv6
? "[" LL_SSDP_MCAST_ADDR
"]" : SSDP_MCAST_ADDR
,
798 nt
, suffix
, /* NT: */
799 usn1
, usn2
, usn3
, suffix
, /* USN: */
800 upnp_bootid
, upnp_bootid
, upnp_configid
);
803 syslog(LOG_ERR
, "SendSSDPbyebye() snprintf error");
806 else if((unsigned int)l
>= sizeof(bufr
))
808 syslog(LOG_WARNING
, "SendSSDPbyebye(): truncated output");
811 n
= sendto(s
, bufr
, l
, 0, dest
,
813 ipv6
? sizeof(struct sockaddr_in6
) : sizeof(struct sockaddr_in
)
815 sizeof(struct sockaddr_in
)
820 syslog(LOG_ERR
, "sendto(udp_shutdown=%d): %m", s
);
825 syslog(LOG_NOTICE
, "sendto() sent %d out of %d bytes", n
, l
);
831 /* This will broadcast ssdp:byebye notifications to inform
832 * the network that UPnP is going down. */
834 SendSSDPGoodbye(int * sockets
, int n_sockets
)
836 struct sockaddr_in sockname
;
838 struct sockaddr_in6 sockname6
;
845 memset(&sockname
, 0, sizeof(struct sockaddr_in
));
846 sockname
.sin_family
= AF_INET
;
847 sockname
.sin_port
= htons(SSDP_PORT
);
848 sockname
.sin_addr
.s_addr
= inet_addr(SSDP_MCAST_ADDR
);
850 memset(&sockname6
, 0, sizeof(struct sockaddr_in6
));
851 sockname6
.sin6_family
= AF_INET6
;
852 sockname6
.sin6_port
= htons(SSDP_PORT
);
853 inet_pton(AF_INET6
, LL_SSDP_MCAST_ADDR
, &(sockname6
.sin6_addr
));
856 for(j
=0; j
<n_sockets
; j
++)
861 for(i
=0; known_service_types
[i
].s
; i
++)
866 snprintf(ver_str
, sizeof(ver_str
), "%d", known_service_types
[i
].version
);
867 ret
+= SendSSDPbyebye(sockets
[j
],
869 ipv6
? (struct sockaddr
*)&sockname6
: (struct sockaddr
*)&sockname
,
871 (struct sockaddr
*)&sockname
,
873 known_service_types
[i
].s
, ver_str
, /* NT: */
874 uuidvalue
, "::", known_service_types
[i
].s
, /* ver_str, USN: */
876 if(i
==0) /* root device */
878 ret
+= SendSSDPbyebye(sockets
[j
],
880 ipv6
? (struct sockaddr
*)&sockname6
: (struct sockaddr
*)&sockname
,
882 (struct sockaddr
*)&sockname
,
884 uuidvalue
, "", /* NT: */
885 uuidvalue
, "", "", /* ver_str, USN: */
893 /* SubmitServicesToMiniSSDPD() :
894 * register services offered by MiniUPnPd to a running instance of
897 SubmitServicesToMiniSSDPD(const char * host
, unsigned short port
) {
898 struct sockaddr_un addr
;
900 unsigned char buffer
[2048];
906 s
= socket(AF_UNIX
, SOCK_STREAM
, 0);
908 syslog(LOG_ERR
, "socket(unix): %m");
911 addr
.sun_family
= AF_UNIX
;
912 strncpy(addr
.sun_path
, minissdpdsocketpath
, sizeof(addr
.sun_path
));
913 if(connect(s
, (struct sockaddr
*)&addr
, sizeof(struct sockaddr_un
)) < 0) {
914 syslog(LOG_ERR
, "connect(\"%s\"): %m", minissdpdsocketpath
);
918 for(i
= 0; known_service_types
[i
].s
; i
++) {
919 buffer
[0] = 4; /* request type 4 : submit service */
920 /* 4 strings following : ST (service type), USN, Server, Location */
922 l
= (int)strlen(known_service_types
[i
].s
);
926 memcpy(p
, known_service_types
[i
].s
, l
);
933 snprintf(ver_str
, sizeof(ver_str
), "%d", known_service_types
[i
].version
);
934 l
= snprintf(strbuf
, sizeof(strbuf
), "%s::%s%s",
935 uuidvalue
, known_service_types
[i
].s
, ver_str
);
937 memcpy(p
, strbuf
, l
);
939 l
= (int)strlen(MINIUPNPD_SERVER_STRING
);
941 memcpy(p
, MINIUPNPD_SERVER_STRING
, l
);
943 l
= snprintf(strbuf
, sizeof(strbuf
), "http://%s:%u" ROOTDESC_PATH
,
944 host
, (unsigned int)port
);
946 memcpy(p
, strbuf
, l
);
948 /* now write the encoded data */
949 n
= p
- buffer
; /* bytes to send */
950 p
= buffer
; /* start */
954 syslog(LOG_ERR
, "write(): %m");
958 syslog(LOG_ERR
, "write() returned 0");