Miniupnpd: update to 2.0
[tomato.git] / release / src-rt-6.x.4708 / router / miniupnpd / minissdp.c
blob94027d527263dda15430fe52c15b647711bd5e4f
1 /* $Id: minissdp.c,v 1.84 2016/02/20 19:08:40 nanard Exp $ */
2 /* MiniUPnP project
3 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4 * (c) 2006-2016 Thomas Bernard
5 * This software is subject to the conditions detailed
6 * in the LICENCE file provided within the distribution */
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <unistd.h>
12 #include <sys/socket.h>
13 #include <sys/un.h>
14 #include <netinet/in.h>
15 #include <arpa/inet.h>
16 #include <errno.h>
17 #include <syslog.h>
19 #include "config.h"
20 #include "upnpdescstrings.h"
21 #include "miniupnpdpath.h"
22 #include "upnphttp.h"
23 #include "upnpglobalvars.h"
24 #include "minissdp.h"
25 #include "upnputils.h"
26 #include "getroute.h"
27 #include "asyncsendto.h"
28 #include "codelength.h"
29 #include "macros.h"
31 /* SSDP ip/port */
32 #define SSDP_PORT (1900)
33 #define SSDP_MCAST_ADDR ("239.255.255.250")
34 #define LL_SSDP_MCAST_ADDR "FF02::C"
35 #define SL_SSDP_MCAST_ADDR "FF05::C"
36 #define GL_SSDP_MCAST_ADDR "FF0E::C"
38 /* AddMulticastMembership()
39 * param s socket
40 * param lan_addr lan address
42 static int
43 AddMulticastMembership(int s, struct lan_addr_s * lan_addr)
45 #ifndef HAVE_IP_MREQN
46 /* The ip_mreqn structure appeared in Linux 2.4. */
47 struct ip_mreq imr; /* Ip multicast membership */
48 #else /* HAVE_IP_MREQN */
49 struct ip_mreqn imr; /* Ip multicast membership */
50 #endif /* HAVE_IP_MREQN */
52 /* setting up imr structure */
53 imr.imr_multiaddr.s_addr = inet_addr(SSDP_MCAST_ADDR);
54 /*imr.imr_interface.s_addr = htonl(INADDR_ANY);*/
55 #ifndef HAVE_IP_MREQN
56 imr.imr_interface.s_addr = lan_addr->addr.s_addr;
57 #else /* HAVE_IP_MREQN */
58 imr.imr_address.s_addr = lan_addr->addr.s_addr;
59 #ifndef MULTIPLE_EXTERNAL_IP
60 #ifdef ENABLE_IPV6
61 imr.imr_ifindex = lan_addr->index;
62 #else /* ENABLE_IPV6 */
63 imr.imr_ifindex = if_nametoindex(lan_addr->ifname);
64 #endif /* ENABLE_IPV6 */
65 #else /* MULTIPLE_EXTERNAL_IP */
66 imr.imr_ifindex = 0;
67 #endif /* MULTIPLE_EXTERNAL_IP */
68 #endif /* HAVE_IP_MREQN */
70 #ifndef HAVE_IP_MREQN
71 if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&imr, sizeof(struct ip_mreq)) < 0)
72 #else /* HAVE_IP_MREQN */
73 if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&imr, sizeof(struct ip_mreqn)) < 0)
74 #endif /* HAVE_IP_MREQN */
76 syslog(LOG_ERR, "setsockopt(udp, IP_ADD_MEMBERSHIP): %m");
77 return -1;
80 return 0;
83 /* AddMulticastMembershipIPv6()
84 * param s socket (IPv6)
85 * param ifindex : interface index (0 : All interfaces) */
86 #ifdef ENABLE_IPV6
87 static int
88 AddMulticastMembershipIPv6(int s, unsigned int ifindex)
90 struct ipv6_mreq mr;
92 memset(&mr, 0, sizeof(mr));
93 mr.ipv6mr_interface = ifindex; /* 0 : all interfaces */
94 #ifndef IPV6_ADD_MEMBERSHIP
95 #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
96 #endif
97 inet_pton(AF_INET6, LL_SSDP_MCAST_ADDR, &mr.ipv6mr_multiaddr);
98 if(setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mr, sizeof(struct ipv6_mreq)) < 0)
100 syslog(LOG_ERR, "setsockopt(udp, IPV6_ADD_MEMBERSHIP): %m");
101 return -1;
103 inet_pton(AF_INET6, SL_SSDP_MCAST_ADDR, &mr.ipv6mr_multiaddr);
104 if(setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mr, sizeof(struct ipv6_mreq)) < 0)
106 syslog(LOG_ERR, "setsockopt(udp, IPV6_ADD_MEMBERSHIP): %m");
107 return -1;
109 inet_pton(AF_INET6, GL_SSDP_MCAST_ADDR, &mr.ipv6mr_multiaddr);
110 if(setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mr, sizeof(struct ipv6_mreq)) < 0)
112 syslog(LOG_ERR, "setsockopt(udp, IPV6_ADD_MEMBERSHIP): %m");
113 return -1;
115 return 0;
117 #endif
119 /* Open and configure the socket listening for
120 * SSDP udp packets sent on 239.255.255.250 port 1900
121 * SSDP v6 udp packets sent on FF02::C, or FF05::C, port 1900 */
123 OpenAndConfSSDPReceiveSocket(int ipv6)
125 int s;
126 struct sockaddr_storage sockname;
127 socklen_t sockname_len;
128 struct lan_addr_s * lan_addr;
129 int j = 1;
131 if( (s = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, 0)) < 0)
133 syslog(LOG_ERR, "%s: socket(udp): %m",
134 "OpenAndConfSSDPReceiveSocket");
135 return -1;
138 memset(&sockname, 0, sizeof(struct sockaddr_storage));
139 #ifdef ENABLE_IPV6
140 if(ipv6)
142 struct sockaddr_in6 * saddr = (struct sockaddr_in6 *)&sockname;
143 saddr->sin6_family = AF_INET6;
144 saddr->sin6_port = htons(SSDP_PORT);
145 saddr->sin6_addr = ipv6_bind_addr;
146 sockname_len = sizeof(struct sockaddr_in6);
148 else
149 #endif /* ENABLE_IPV6 */
151 struct sockaddr_in * saddr = (struct sockaddr_in *)&sockname;
152 saddr->sin_family = AF_INET;
153 saddr->sin_port = htons(SSDP_PORT);
154 /* NOTE : it seems it doesnt work when binding on the specific address */
155 /*saddr->sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);*/
156 saddr->sin_addr.s_addr = htonl(INADDR_ANY);
157 /*saddr->sin_addr.s_addr = inet_addr(ifaddr);*/
158 sockname_len = sizeof(struct sockaddr_in);
161 if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &j, sizeof(j)) < 0)
163 syslog(LOG_WARNING, "setsockopt(udp, SO_REUSEADDR): %m");
166 if(!set_non_blocking(s))
168 syslog(LOG_WARNING, "%s: set_non_blocking(): %m",
169 "OpenAndConfSSDPReceiveSocket");
172 #if defined(SO_BINDTODEVICE) && !defined(MULTIPLE_EXTERNAL_IP)
173 /* One and only one LAN interface */
174 if(lan_addrs.lh_first != NULL && lan_addrs.lh_first->list.le_next == NULL
175 && strlen(lan_addrs.lh_first->ifname) > 0)
177 if(setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE,
178 lan_addrs.lh_first->ifname,
179 strlen(lan_addrs.lh_first->ifname)) < 0)
180 syslog(LOG_WARNING, "%s: setsockopt(udp%s, SO_BINDTODEVICE, %s): %m",
181 "OpenAndConfSSDPReceiveSocket", ipv6 ? "6" : "",
182 lan_addrs.lh_first->ifname);
184 #endif /* defined(SO_BINDTODEVICE) && !defined(MULTIPLE_EXTERNAL_IP) */
186 if(bind(s, (struct sockaddr *)&sockname, sockname_len) < 0)
188 syslog(LOG_ERR, "%s: bind(udp%s): %m",
189 "OpenAndConfSSDPReceiveSocket", ipv6 ? "6" : "");
190 close(s);
191 return -1;
194 #ifdef ENABLE_IPV6
195 if(ipv6)
197 for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next)
199 if(AddMulticastMembershipIPv6(s, lan_addr->index) < 0)
201 syslog(LOG_WARNING,
202 "Failed to add IPv6 multicast membership for interface %s",
203 lan_addr->str ? lan_addr->str : "NULL");
207 else
208 #endif
210 for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next)
212 if(AddMulticastMembership(s, lan_addr) < 0)
214 syslog(LOG_WARNING,
215 "Failed to add multicast membership for interface %s",
216 strlen(lan_addr->str) ? lan_addr->str : "NULL");
221 return s;
224 /* open the UDP socket used to send SSDP notifications to
225 * the multicast group reserved for them */
226 static int
227 OpenAndConfSSDPNotifySocket(in_addr_t addr)
229 int s;
230 unsigned char loopchar = 0;
231 int bcast = 1;
232 unsigned char ttl = 2; /* UDA v1.1 says :
233 The TTL for the IP packet SHOULD default to 2 and
234 SHOULD be configurable. */
235 /* TODO: Make TTL be configurable */
236 struct in_addr mc_if;
237 struct sockaddr_in sockname;
239 if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
241 syslog(LOG_ERR, "socket(udp_notify): %m");
242 return -1;
245 mc_if.s_addr = addr; /*inet_addr(addr);*/
247 if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loopchar, sizeof(loopchar)) < 0)
249 syslog(LOG_ERR, "setsockopt(udp_notify, IP_MULTICAST_LOOP): %m");
250 close(s);
251 return -1;
254 if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&mc_if, sizeof(mc_if)) < 0)
256 syslog(LOG_ERR, "setsockopt(udp_notify, IP_MULTICAST_IF): %m");
257 close(s);
258 return -1;
261 if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0)
263 syslog(LOG_WARNING, "setsockopt(udp_notify, IP_MULTICAST_TTL,): %m");
266 if(setsockopt(s, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bcast)) < 0)
268 syslog(LOG_ERR, "setsockopt(udp_notify, SO_BROADCAST): %m");
269 close(s);
270 return -1;
273 /* bind() socket before using sendto() is not mandatory
274 * (sendto() will implicitly bind the socket when called on
275 * an unbound socket)
276 * here it is used to se a specific sending address */
277 memset(&sockname, 0, sizeof(struct sockaddr_in));
278 sockname.sin_family = AF_INET;
279 sockname.sin_addr.s_addr = addr; /*inet_addr(addr);*/
281 if (bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
283 syslog(LOG_ERR, "bind(udp_notify): %m");
284 close(s);
285 return -1;
288 return s;
291 #ifdef ENABLE_IPV6
292 /* open the UDP socket used to send SSDP notifications to
293 * the multicast group reserved for them. IPv6 */
294 static int
295 OpenAndConfSSDPNotifySocketIPv6(unsigned int if_index)
297 int s;
298 unsigned int loop = 0;
299 /* UDA 2.0 : The hop limit of each IP packet for a Site-Local scope
300 * multicast message SHALL be configurable and SHOULD default to 10 */
301 int hop_limit = 10;
302 struct sockaddr_in6 sockname;
304 s = socket(PF_INET6, SOCK_DGRAM, 0);
305 if(s < 0)
307 syslog(LOG_ERR, "socket(udp_notify IPv6): %m");
308 return -1;
310 if(setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &if_index, sizeof(if_index)) < 0)
312 syslog(LOG_ERR, "setsockopt(udp_notify IPv6, IPV6_MULTICAST_IF, %u): %m", if_index);
313 close(s);
314 return -1;
316 if(setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof(loop)) < 0)
318 syslog(LOG_ERR, "setsockopt(udp_notify, IPV6_MULTICAST_LOOP): %m");
319 close(s);
320 return -1;
322 if(setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hop_limit, sizeof(hop_limit)) < 0)
324 syslog(LOG_ERR, "setsockopt(udp_notify, IPV6_MULTICAST_HOPS): %m");
325 close(s);
326 return -1;
329 /* bind() socket before using sendto() is not mandatory
330 * (sendto() will implicitly bind the socket when called on
331 * an unbound socket)
332 * but explicit bind permits to set port/scope_id/etc. */
333 memset(&sockname, 0, sizeof(sockname));
334 sockname.sin6_family = AF_INET6;
335 sockname.sin6_addr = in6addr_any;
336 /*sockname.sin6_port = htons(port);*/
337 /*sockname.sin6_scope_id = if_index;*/
338 if(bind(s, (struct sockaddr *)&sockname, sizeof(sockname)) < 0)
340 syslog(LOG_ERR, "bind(udp_notify IPv6): %m");
341 close(s);
342 return -1;
345 return s;
347 #endif
350 OpenAndConfSSDPNotifySockets(int * sockets)
351 /*OpenAndConfSSDPNotifySockets(int * sockets,
352 struct lan_addr_s * lan_addr, int n_lan_addr)*/
354 int i;
355 struct lan_addr_s * lan_addr;
357 for(i=0, lan_addr = lan_addrs.lh_first;
358 lan_addr != NULL;
359 lan_addr = lan_addr->list.le_next)
361 sockets[i] = OpenAndConfSSDPNotifySocket(lan_addr->addr.s_addr);
362 if(sockets[i] < 0)
363 goto error;
364 i++;
365 #ifdef ENABLE_IPV6
366 if(GETFLAG(IPV6DISABLEDMASK))
368 sockets[i] = -1;
370 else
372 sockets[i] = OpenAndConfSSDPNotifySocketIPv6(lan_addr->index);
373 if(sockets[i] < 0)
374 goto error;
376 i++;
377 #endif
379 return 0;
380 error:
381 while(--i >= 0)
383 close(sockets[i]);
384 sockets[i] = -1;
386 return -1;
390 * response from a LiveBox (Wanadoo)
391 HTTP/1.1 200 OK
392 CACHE-CONTROL: max-age=1800
393 DATE: Thu, 01 Jan 1970 04:03:23 GMT
394 EXT:
395 LOCATION: http://192.168.0.1:49152/gatedesc.xml
396 SERVER: Linux/2.4.17, UPnP/1.0, Intel SDK for UPnP devices /1.2
397 ST: upnp:rootdevice
398 USN: uuid:75802409-bccb-40e7-8e6c-fa095ecce13e::upnp:rootdevice
400 * response from a Linksys 802.11b :
401 HTTP/1.1 200 OK
402 Cache-Control:max-age=120
403 Location:http://192.168.5.1:5678/rootDesc.xml
404 Server:NT/5.0 UPnP/1.0
405 ST:upnp:rootdevice
406 USN:uuid:upnp-InternetGatewayDevice-1_0-0090a2777777::upnp:rootdevice
407 EXT:
410 /* Responds to a SSDP "M-SEARCH"
411 * s : socket to use
412 * addr : peer
413 * st, st_len : ST: header
414 * suffix : suffix for USN: header
415 * host, port : our HTTP host, port
416 * delay : in milli-seconds
418 static void
419 SendSSDPResponse(int s, const struct sockaddr * addr,
420 const char * st, int st_len, const char * suffix,
421 const char * host, unsigned short http_port,
422 #ifdef ENABLE_HTTPS
423 unsigned short https_port,
424 #endif
425 const char * uuidvalue, unsigned int delay)
427 int l, n;
428 char buf[SSDP_PACKET_MAX_LEN];
429 char addr_str[64];
430 socklen_t addrlen;
431 int st_is_uuid;
432 #ifdef ENABLE_HTTP_DATE
433 char http_date[64];
434 time_t t;
435 struct tm tm;
437 time(&t);
438 gmtime_r(&t, &tm);
439 strftime(http_date, sizeof(http_date),
440 "%a, %d %b %Y %H:%M:%S GMT", &tm);
441 #endif
443 st_is_uuid = (st_len == (int)strlen(uuidvalue)) &&
444 (memcmp(uuidvalue, st, st_len) == 0);
446 * follow guideline from document "UPnP Device Architecture 1.0"
447 * uppercase is recommended.
448 * DATE: is recommended
449 * SERVER: OS/ver UPnP/1.0 miniupnpd/1.0
450 * - check what to put in the 'Cache-Control' header
452 * have a look at the document "UPnP Device Architecture v1.1 */
453 l = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\n"
454 "CACHE-CONTROL: max-age=120\r\n"
455 #ifdef ENABLE_HTTP_DATE
456 "DATE: %s\r\n"
457 #endif
458 "ST: %.*s%s\r\n"
459 "USN: %s%s%.*s%s\r\n"
460 "EXT:\r\n"
461 "SERVER: " MINIUPNPD_SERVER_STRING "\r\n"
462 "LOCATION: http://%s:%u" ROOTDESC_PATH "\r\n"
463 #ifdef ENABLE_HTTPS
464 "SECURELOCATION.UPNP.ORG: https://%s:%u" ROOTDESC_PATH "\r\n"
465 #endif
466 "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */
467 "01-NLS: %u\r\n" /* same as BOOTID. UDA v1.1 */
468 "BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
469 "CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
470 "\r\n",
471 #ifdef ENABLE_HTTP_DATE
472 http_date,
473 #endif
474 st_len, st, suffix,
475 uuidvalue, st_is_uuid ? "" : "::",
476 st_is_uuid ? 0 : st_len, st, suffix,
477 host, (unsigned int)http_port,
478 #ifdef ENABLE_HTTPS
479 host, (unsigned int)https_port,
480 #endif
481 upnp_bootid, upnp_bootid, upnp_configid);
482 if(l<0)
484 syslog(LOG_ERR, "%s: snprintf failed %m",
485 "SendSSDPResponse()");
486 return;
488 else if((unsigned)l>=sizeof(buf))
490 syslog(LOG_WARNING, "%s: truncated output (%u>=%u)",
491 "SendSSDPResponse()", (unsigned)l, (unsigned)sizeof(buf));
492 l = sizeof(buf) - 1;
494 addrlen = (addr->sa_family == AF_INET6)
495 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
496 n = sendto_schedule(s, buf, l, 0,
497 addr, addrlen, delay);
498 sockaddr_to_string(addr, addr_str, sizeof(addr_str));
499 syslog(LOG_DEBUG, "%s: %d bytes to %s ST: %.*s",
500 "SendSSDPResponse()",
501 n, addr_str, l, buf);
502 if(n < 0)
504 syslog(LOG_ERR, "%s: sendto(udp): %m",
505 "SendSSDPResponse()");
509 static struct {
510 const char * s;
511 const int version;
512 const char * uuid;
513 } const known_service_types[] =
515 {"upnp:rootdevice", 0, uuidvalue_igd},
516 #ifdef IGD_V2
517 {"urn:schemas-upnp-org:device:InternetGatewayDevice:", 2, uuidvalue_igd},
518 {"urn:schemas-upnp-org:device:WANConnectionDevice:", 2, uuidvalue_wcd},
519 {"urn:schemas-upnp-org:device:WANDevice:", 2, uuidvalue_wan},
520 {"urn:schemas-upnp-org:service:WANIPConnection:", 2, uuidvalue_wcd},
521 {"urn:schemas-upnp-org:service:DeviceProtection:", 1, uuidvalue_igd},
522 #ifdef ENABLE_6FC_SERVICE
523 {"urn:schemas-upnp-org:service:WANIPv6FirewallControl:", 1, uuidvalue_wcd},
524 #endif
525 #else /* IGD_V2 */
526 /* IGD v1 */
527 {"urn:schemas-upnp-org:device:InternetGatewayDevice:", 1, uuidvalue_igd},
528 {"urn:schemas-upnp-org:device:WANConnectionDevice:", 1, uuidvalue_wcd},
529 {"urn:schemas-upnp-org:device:WANDevice:", 1, uuidvalue_wan},
530 {"urn:schemas-upnp-org:service:WANIPConnection:", 1, uuidvalue_wcd},
531 #endif /* IGD_V2 */
532 {"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:", 1, uuidvalue_wan},
533 #ifdef ADVERTISE_WANPPPCONN
534 /* We use WAN IP Connection, not PPP connection,
535 * but buggy control points may try to use WanPPPConnection
536 * anyway */
537 {"urn:schemas-upnp-org:service:WANPPPConnection:", 1, uuidvalue_wcd},
538 #endif /* ADVERTISE_WANPPPCONN */
539 #ifdef ENABLE_L3F_SERVICE
540 {"urn:schemas-upnp-org:service:Layer3Forwarding:", 1, uuidvalue_igd},
541 #endif /* ENABLE_L3F_SERVICE */
542 /* we might want to support urn:schemas-wifialliance-org:device:WFADevice:1
543 * urn:schemas-wifialliance-org:device:WFADevice:1
544 * in the future */
545 {0, 0, 0}
548 /* SendSSDPNotify() sends the SSDP NOTIFY to a specific
549 * destination, for a specific UPnP service or device */
550 static void
551 SendSSDPNotify(int s, const struct sockaddr * dest, socklen_t dest_len,
552 const char * dest_str,
553 const char * host, unsigned short http_port,
554 #ifdef ENABLE_HTTPS
555 unsigned short https_port,
556 #endif
557 const char * nt, const char * suffix,
558 const char * usn1, const char * usn2, const char * usn3,
559 unsigned int lifetime)
561 char bufr[SSDP_PACKET_MAX_LEN];
562 int n, l;
564 l = snprintf(bufr, sizeof(bufr),
565 "NOTIFY * HTTP/1.1\r\n"
566 "HOST: %s:%d\r\n"
567 "CACHE-CONTROL: max-age=%u\r\n"
568 "LOCATION: http://%s:%u" ROOTDESC_PATH "\r\n"
569 #ifdef ENABLE_HTTPS
570 "SECURELOCATION.UPNP.ORG: https://%s:%u" ROOTDESC_PATH "\r\n"
571 #endif
572 "SERVER: " MINIUPNPD_SERVER_STRING "\r\n"
573 "NT: %s%s\r\n"
574 "USN: %s%s%s%s\r\n"
575 "NTS: ssdp:alive\r\n"
576 "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */
577 "01-NLS: %u\r\n" /* same as BOOTID field. UDA v1.1 */
578 "BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
579 "CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
580 "\r\n",
581 dest_str, SSDP_PORT, /* HOST: */
582 lifetime, /* CACHE-CONTROL: */
583 host, (unsigned int)http_port, /* LOCATION: */
584 #ifdef ENABLE_HTTPS
585 host, (unsigned int)https_port, /* SECURE-LOCATION: */
586 #endif
587 nt, suffix, /* NT: */
588 usn1, usn2, usn3, suffix, /* USN: */
589 upnp_bootid, /* 01-NLS: */
590 upnp_bootid, /* BOOTID.UPNP.ORG: */
591 upnp_configid ); /* CONFIGID.UPNP.ORG: */
592 if(l<0) {
593 syslog(LOG_ERR, "%s: snprintf error", "SendSSDPNotify()");
594 return;
595 } else if((unsigned int)l >= sizeof(bufr)) {
596 syslog(LOG_WARNING, "%s: truncated output (%u>=%u)",
597 "SendSSDPNotify()", (unsigned)l, (unsigned)sizeof(bufr));
598 l = sizeof(bufr) - 1;
600 n = sendto_or_schedule(s, bufr, l, 0, dest, dest_len);
601 if(n < 0) {
602 syslog(LOG_ERR, "sendto(udp_notify=%d, %s): %m", s,
603 host ? host : "NULL");
604 } else if(n != l) {
605 syslog(LOG_NOTICE, "sendto() sent %d out of %d bytes", n, l);
607 /* Due to the unreliable nature of UDP, devices SHOULD send the entire
608 * set of discovery messages more than once with some delay between
609 * sets e.g. a few hundred milliseconds. To avoid network congestion
610 * discovery messages SHOULD NOT be sent more than three times. */
611 n = sendto_schedule(s, bufr, l, 0, dest, dest_len, 250);
612 if(n < 0) {
613 syslog(LOG_ERR, "sendto(udp_notify=%d, %s): %m", s,
614 host ? host : "NULL");
618 /* SendSSDPNotifies() send SSPD NOTIFY for a specific
619 * LAN (network interface) for all devices / services */
620 #ifdef ENABLE_HTTPS
621 static void
622 SendSSDPNotifies(int s, const char * host, unsigned short http_port,
623 unsigned short https_port,
624 unsigned int lifetime, int ipv6)
625 #else
626 static void
627 SendSSDPNotifies(int s, const char * host, unsigned short http_port,
628 unsigned int lifetime, int ipv6)
629 #endif
631 #ifdef ENABLE_IPV6
632 struct sockaddr_storage sockname;
633 /* UDA 1.1 AnnexA and UDA 2.0 only allow/define the use of
634 * Link-Local and Site-Local multicast scopes */
635 static struct { const char * p1, * p2; } const mcast_addrs[] =
636 { { LL_SSDP_MCAST_ADDR, "[" LL_SSDP_MCAST_ADDR "]" }, /* Link Local */
637 { SL_SSDP_MCAST_ADDR, "[" SL_SSDP_MCAST_ADDR "]" }, /* Site Local */
638 #ifndef UPNP_STRICT
639 { GL_SSDP_MCAST_ADDR, "[" GL_SSDP_MCAST_ADDR "]" }, /* Global */
640 #endif /* ! UPNP_STRICT */
641 { NULL, NULL } };
642 int j;
643 #else /* ENABLE_IPV6 */
644 struct sockaddr_in sockname;
645 #endif /* ENABLE_IPV6 */
646 socklen_t sockname_len;
647 const char * dest_str;
648 int i;
649 char ver_str[4];
650 #ifndef ENABLE_IPV6
651 UNUSED(ipv6);
652 #endif /* ENABLE_IPV6 */
654 memset(&sockname, 0, sizeof(sockname));
655 #ifdef ENABLE_IPV6
656 /* first iterate destinations for this LAN interface (only 1 for IPv4) */
657 for(j = 0; (mcast_addrs[j].p1 != 0 && ipv6) || j < 1; j++) {
658 if(ipv6) {
659 struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockname;
660 sockname_len = sizeof(struct sockaddr_in6);
661 p->sin6_family = AF_INET6;
662 p->sin6_port = htons(SSDP_PORT);
663 inet_pton(AF_INET6, mcast_addrs[j].p1, &(p->sin6_addr));
664 dest_str = mcast_addrs[j].p2;
665 /* UPnP Device Architecture 1.1 :
666 * Devices MUST multicast SSDP messages for each of the UPnP-enabled
667 * interfaces. The scope of multicast SSDP messages MUST be
668 * link local FF02::C if the message is sent from a link local address.
669 * If the message is sent from a global address it MUST be multicast
670 * using either global scope FF0E::C or site local scope FF05::C.
671 * In networks with complex topologies and overlapping sites, use of
672 * global scope is RECOMMENDED. */
673 } else {
674 #else /* ENABLE_IPV6 */
676 #endif /* ENABLE_IPV6 */
677 /* IPv4 */
678 struct sockaddr_in *p = (struct sockaddr_in *)&sockname;
679 sockname_len = sizeof(struct sockaddr_in);
680 p->sin_family = AF_INET;
681 p->sin_port = htons(SSDP_PORT);
682 p->sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
683 dest_str = SSDP_MCAST_ADDR;
686 /* iterate all services / devices */
687 for(i = 0; known_service_types[i].s; i++) {
688 if(i==0)
689 ver_str[0] = '\0';
690 else
691 snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version);
692 SendSSDPNotify(s, (struct sockaddr *)&sockname, sockname_len, dest_str,
693 host, http_port,
694 #ifdef ENABLE_HTTPS
695 https_port,
696 #endif
697 known_service_types[i].s, ver_str, /* NT: */
698 known_service_types[i].uuid, "::",
699 known_service_types[i].s, /* ver_str, USN: */
700 lifetime);
701 /* for devices, also send NOTIFY on the uuid */
702 if(0==memcmp(known_service_types[i].s,
703 "urn:schemas-upnp-org:device", sizeof("urn:schemas-upnp-org:device")-1)) {
704 SendSSDPNotify(s, (struct sockaddr *)&sockname, sockname_len, dest_str,
705 host, http_port,
706 #ifdef ENABLE_HTTPS
707 https_port,
708 #endif
709 known_service_types[i].uuid, "", /* NT: */
710 known_service_types[i].uuid, "", "", /* ver_str, USN: */
711 lifetime);
713 } /* for(i = 0; known_service_types[i].s; i++) */
714 #ifdef ENABLE_IPV6
715 } /* for(j = 0; (mcast_addrs[j].p1 != 0 && ipv6) || j < 1; j++) */
716 #endif /* ENABLE_IPV6 */
719 /* SendSSDPNotifies2() sends SSDP NOTIFY packets on all interfaces
720 * for all destinations, all devices / services */
721 void
722 SendSSDPNotifies2(int * sockets,
723 unsigned short http_port,
724 #ifdef ENABLE_HTTPS
725 unsigned short https_port,
726 #endif
727 unsigned int lifetime)
729 int i;
730 struct lan_addr_s * lan_addr;
731 for(i = 0, lan_addr = lan_addrs.lh_first;
732 lan_addr != NULL;
733 lan_addr = lan_addr->list.le_next) {
734 SendSSDPNotifies(sockets[i], lan_addr->str, http_port,
735 #ifdef ENABLE_HTTPS
736 https_port,
737 #endif
738 lifetime, 0);
739 i++;
740 #ifdef ENABLE_IPV6
741 if(sockets[i] >= 0) {
742 SendSSDPNotifies(sockets[i], ipv6_addr_for_http_with_brackets, http_port,
743 #ifdef ENABLE_HTTPS
744 https_port,
745 #endif
746 lifetime, 1);
748 i++;
749 #endif /* ENABLE_IPV6 */
753 /* ProcessSSDPRequest()
754 * process SSDP M-SEARCH requests and responds to them */
755 void
756 #ifdef ENABLE_HTTPS
757 ProcessSSDPRequest(int s, unsigned short http_port, unsigned short https_port)
758 #else
759 ProcessSSDPRequest(int s, unsigned short http_port)
760 #endif
762 int n;
763 char bufr[1500];
764 socklen_t len_r;
765 #ifdef ENABLE_IPV6
766 struct sockaddr_storage sendername;
767 len_r = sizeof(struct sockaddr_storage);
768 #else
769 struct sockaddr_in sendername;
770 len_r = sizeof(struct sockaddr_in);
771 #endif
773 n = recvfrom(s, bufr, sizeof(bufr), 0,
774 (struct sockaddr *)&sendername, &len_r);
775 if(n < 0)
777 /* EAGAIN, EWOULDBLOCK, EINTR : silently ignore (try again next time)
778 * other errors : log to LOG_ERR */
779 if(errno != EAGAIN &&
780 errno != EWOULDBLOCK &&
781 errno != EINTR)
783 syslog(LOG_ERR, "recvfrom(udp): %m");
785 return;
787 #ifdef ENABLE_HTTPS
788 ProcessSSDPData(s, bufr, n, (struct sockaddr *)&sendername,
789 http_port, https_port);
790 #else
791 ProcessSSDPData(s, bufr, n, (struct sockaddr *)&sendername,
792 http_port);
793 #endif
797 #ifdef ENABLE_HTTPS
798 void
799 ProcessSSDPData(int s, const char *bufr, int n,
800 const struct sockaddr * sender,
801 unsigned short http_port, unsigned short https_port)
802 #else
803 void
804 ProcessSSDPData(int s, const char *bufr, int n,
805 const struct sockaddr * sender,
806 unsigned short http_port)
807 #endif
809 int i, l;
810 struct lan_addr_s * lan_addr = NULL;
811 const char * st = NULL;
812 int st_len = 0;
813 int st_ver = 0;
814 char sender_str[64];
815 char ver_str[4];
816 const char * announced_host = NULL;
817 #ifdef UPNP_STRICT
818 #ifdef ENABLE_IPV6
819 char announced_host_buf[64];
820 #endif
821 #endif
822 #if defined(UPNP_STRICT) || defined(DELAY_MSEARCH_RESPONSE)
823 int mx_value = -1;
824 #endif
825 unsigned int delay = 50; /* Non-zero default delay to prevent flooding */
826 /* UPnP Device Architecture v1.1. 1.3.3 Search response :
827 * Devices responding to a multicast M-SEARCH SHOULD wait a random period
828 * of time between 0 seconds and the number of seconds specified in the
829 * MX field value of the search request before responding, in order to
830 * avoid flooding the requesting control point with search responses
831 * from multiple devices. If the search request results in the need for
832 * a multiple part response from the device, those multiple part
833 * responses SHOULD be spread at random intervals through the time period
834 * from 0 to the number of seconds specified in the MX header field. */
836 /* get the string representation of the sender address */
837 sockaddr_to_string(sender, sender_str, sizeof(sender_str));
838 lan_addr = get_lan_for_peer(sender);
839 if(lan_addr == NULL)
841 syslog(LOG_WARNING, "SSDP packet sender %s not from a LAN, ignoring",
842 sender_str);
843 return;
846 if(memcmp(bufr, "NOTIFY", 6) == 0)
848 /* ignore NOTIFY packets. We could log the sender and device type */
849 return;
851 else if(memcmp(bufr, "M-SEARCH", 8) == 0)
853 i = 0;
854 while(i < n)
856 while((i < n - 1) && (bufr[i] != '\r' || bufr[i+1] != '\n'))
857 i++;
858 i += 2;
859 if((i < n - 3) && (strncasecmp(bufr+i, "st:", 3) == 0))
861 st = bufr+i+3;
862 st_len = 0;
863 while((*st == ' ' || *st == '\t') && (st < bufr + n))
864 st++;
865 while(st[st_len]!='\r' && st[st_len]!='\n'
866 && (st + st_len < bufr + n))
867 st_len++;
868 l = st_len;
869 while(l > 0 && st[l-1] != ':')
870 l--;
871 st_ver = atoi(st+l);
872 syslog(LOG_DEBUG, "ST: %.*s (ver=%d)", st_len, st, st_ver);
873 /*j = 0;*/
874 /*while(bufr[i+j]!='\r') j++;*/
875 /*syslog(LOG_INFO, "%.*s", j, bufr+i);*/
877 #if defined(UPNP_STRICT) || defined(DELAY_MSEARCH_RESPONSE)
878 else if((i < n - 3) && (strncasecmp(bufr+i, "mx:", 3) == 0))
880 const char * mx;
881 int mx_len;
882 mx = bufr+i+3;
883 mx_len = 0;
884 while((*mx == ' ' || *mx == '\t') && (mx < bufr + n))
885 mx++;
886 while(mx[mx_len]!='\r' && mx[mx_len]!='\n'
887 && (mx + mx_len < bufr + n))
888 mx_len++;
889 mx_value = atoi(mx);
890 syslog(LOG_DEBUG, "MX: %.*s (value=%d)", mx_len, mx, mx_value);
892 #endif
894 #ifdef UPNP_STRICT
895 /* For multicast M-SEARCH requests, if the search request does
896 * not contain an MX header field, the device MUST silently
897 * discard and ignore the search request. */
898 if(mx_value < 0) {
899 syslog(LOG_INFO, "ignoring SSDP packet missing MX: header");
900 return;
901 } else if(mx_value > 5) {
902 /* If the MX header field specifies a field value greater
903 * than 5, the device SHOULD assume that it contained the
904 * value 5 or less. */
905 mx_value = 5;
907 #elif defined(DELAY_MSEARCH_RESPONSE)
908 if(mx_value < 0) {
909 mx_value = 1;
910 } else if(mx_value > 5) {
911 /* If the MX header field specifies a field value greater
912 * than 5, the device SHOULD assume that it contained the
913 * value 5 or less. */
914 mx_value = 5;
916 #endif
917 /*syslog(LOG_INFO, "SSDP M-SEARCH packet received from %s",
918 sender_str );*/
919 if(st && (st_len > 0))
921 syslog(LOG_INFO, "SSDP M-SEARCH from %s ST: %.*s",
922 sender_str, st_len, st);
923 /* find in which sub network the client is */
924 if(sender->sa_family == AF_INET)
926 if (lan_addr == NULL)
928 syslog(LOG_ERR,
929 "Can't find in which sub network the client %s is",
930 sender_str);
931 return;
933 announced_host = lan_addr->str;
935 #ifdef ENABLE_IPV6
936 else
938 /* IPv6 address with brackets */
939 #ifdef UPNP_STRICT
940 int index;
941 struct in6_addr addr6;
942 size_t addr6_len = sizeof(addr6);
943 /* retrieve the IPv6 address which
944 * will be used locally to reach sender */
945 memset(&addr6, 0, sizeof(addr6));
946 if(get_src_for_route_to (sender, &addr6, &addr6_len, &index) < 0) {
947 syslog(LOG_WARNING, "get_src_for_route_to() failed, using %s", ipv6_addr_for_http_with_brackets);
948 announced_host = ipv6_addr_for_http_with_brackets;
949 } else {
950 if(inet_ntop(AF_INET6, &addr6,
951 announced_host_buf+1,
952 sizeof(announced_host_buf) - 2)) {
953 announced_host_buf[0] = '[';
954 i = strlen(announced_host_buf);
955 if(i < (int)sizeof(announced_host_buf) - 1) {
956 announced_host_buf[i] = ']';
957 announced_host_buf[i+1] = '\0';
958 } else {
959 syslog(LOG_NOTICE, "cannot suffix %s with ']'",
960 announced_host_buf);
962 announced_host = announced_host_buf;
963 } else {
964 syslog(LOG_NOTICE, "inet_ntop() failed %m");
965 announced_host = ipv6_addr_for_http_with_brackets;
968 #else
969 announced_host = ipv6_addr_for_http_with_brackets;
970 #endif
972 #endif
973 /* Responds to request with a device as ST header */
974 for(i = 0; known_service_types[i].s; i++)
976 l = (int)strlen(known_service_types[i].s);
977 if(l<=st_len && (0 == memcmp(st, known_service_types[i].s, l))
978 #ifdef UPNP_STRICT
979 && (st_ver <= known_service_types[i].version)
980 /* only answer for service version lower or equal of supported one */
981 #endif
984 /* SSDP_RESPOND_SAME_VERSION :
985 * response is urn:schemas-upnp-org:service:WANIPConnection:1 when
986 * M-SEARCH included urn:schemas-upnp-org:service:WANIPConnection:1
987 * else the implemented versions is included in the response
989 * From UPnP Device Architecture v1.1 :
990 * 1.3.2 [...] Updated versions of device and service types
991 * are REQUIRED to be fully backward compatible with
992 * previous versions. Devices MUST respond to M-SEARCH
993 * requests for any supported version. For example, if a
994 * device implements “urn:schemas-upnporg:service:xyz:2”,
995 * it MUST respond to search requests for both that type
996 * and “urn:schemas-upnp-org:service:xyz:1”. The response
997 * MUST specify the same version as was contained in the
998 * search request. [...] */
999 #ifndef SSDP_RESPOND_SAME_VERSION
1000 if(i==0)
1001 ver_str[0] = '\0';
1002 else
1003 snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version);
1004 #endif
1005 syslog(LOG_INFO, "Single search found");
1006 #ifdef DELAY_MSEARCH_RESPONSE
1007 delay = random() / (1 + RAND_MAX / (1000 * mx_value));
1008 #ifdef DEBUG
1009 syslog(LOG_DEBUG, "mx=%dsec delay=%ums", mx_value, delay);
1010 #endif
1011 #endif
1012 SendSSDPResponse(s, sender,
1013 #ifdef SSDP_RESPOND_SAME_VERSION
1014 st, st_len, "",
1015 #else
1016 known_service_types[i].s, l, ver_str,
1017 #endif
1018 announced_host, http_port,
1019 #ifdef ENABLE_HTTPS
1020 https_port,
1021 #endif
1022 known_service_types[i].uuid,
1023 delay);
1024 break;
1027 /* Responds to request with ST: ssdp:all */
1028 /* strlen("ssdp:all") == 8 */
1029 if(st_len==8 && (0 == memcmp(st, "ssdp:all", 8)))
1031 #ifdef DELAY_MSEARCH_RESPONSE
1032 unsigned int delay_increment = (mx_value * 1000) / 15;
1033 #endif
1034 syslog(LOG_INFO, "ssdp:all found");
1035 for(i=0; known_service_types[i].s; i++)
1037 #ifdef DELAY_MSEARCH_RESPONSE
1038 delay += delay_increment;
1039 #endif
1040 if(i==0)
1041 ver_str[0] = '\0';
1042 else
1043 snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version);
1044 l = (int)strlen(known_service_types[i].s);
1045 SendSSDPResponse(s, sender,
1046 known_service_types[i].s, l, ver_str,
1047 announced_host, http_port,
1048 #ifdef ENABLE_HTTPS
1049 https_port,
1050 #endif
1051 known_service_types[i].uuid,
1052 delay);
1054 /* also answer for uuid */
1055 #ifdef DELAY_MSEARCH_RESPONSE
1056 delay += delay_increment;
1057 #endif
1058 SendSSDPResponse(s, sender, uuidvalue_igd, strlen(uuidvalue_igd), "",
1059 announced_host, http_port,
1060 #ifdef ENABLE_HTTPS
1061 https_port,
1062 #endif
1063 uuidvalue_igd, delay);
1064 #ifdef DELAY_MSEARCH_RESPONSE
1065 delay += delay_increment;
1066 #endif
1067 SendSSDPResponse(s, sender, uuidvalue_wan, strlen(uuidvalue_wan), "",
1068 announced_host, http_port,
1069 #ifdef ENABLE_HTTPS
1070 https_port,
1071 #endif
1072 uuidvalue_wan, delay);
1073 #ifdef DELAY_MSEARCH_RESPONSE
1074 delay += delay_increment;
1075 #endif
1076 SendSSDPResponse(s, sender, uuidvalue_wcd, strlen(uuidvalue_wcd), "",
1077 announced_host, http_port,
1078 #ifdef ENABLE_HTTPS
1079 https_port,
1080 #endif
1081 uuidvalue_wcd, delay);
1083 /* responds to request by UUID value */
1084 l = (int)strlen(uuidvalue_igd);
1085 if(l==st_len)
1087 #ifdef DELAY_MSEARCH_RESPONSE
1088 delay = random() / (1 + RAND_MAX / (1000 * mx_value));
1089 #endif
1090 if(0 == memcmp(st, uuidvalue_igd, l))
1092 syslog(LOG_INFO, "ssdp:uuid (IGD) found");
1093 SendSSDPResponse(s, sender, st, st_len, "",
1094 announced_host, http_port,
1095 #ifdef ENABLE_HTTPS
1096 https_port,
1097 #endif
1098 uuidvalue_igd, delay);
1100 else if(0 == memcmp(st, uuidvalue_wan, l))
1102 syslog(LOG_INFO, "ssdp:uuid (WAN) found");
1103 SendSSDPResponse(s, sender, st, st_len, "",
1104 announced_host, http_port,
1105 #ifdef ENABLE_HTTPS
1106 https_port,
1107 #endif
1108 uuidvalue_wan, delay);
1110 else if(0 == memcmp(st, uuidvalue_wcd, l))
1112 syslog(LOG_INFO, "ssdp:uuid (WCD) found");
1113 SendSSDPResponse(s, sender, st, st_len, "",
1114 announced_host, http_port,
1115 #ifdef ENABLE_HTTPS
1116 https_port,
1117 #endif
1118 uuidvalue_wcd, delay);
1122 else
1124 syslog(LOG_INFO, "Invalid SSDP M-SEARCH from %s", sender_str);
1127 else
1129 syslog(LOG_NOTICE, "Unknown udp packet received from %s", sender_str);
1133 static int
1134 SendSSDPbyebye(int s, const struct sockaddr * dest, socklen_t destlen,
1135 const char * dest_str,
1136 const char * nt, const char * suffix,
1137 const char * usn1, const char * usn2, const char * usn3)
1139 int n, l;
1140 char bufr[SSDP_PACKET_MAX_LEN];
1142 l = snprintf(bufr, sizeof(bufr),
1143 "NOTIFY * HTTP/1.1\r\n"
1144 "HOST: %s:%d\r\n"
1145 "NT: %s%s\r\n"
1146 "USN: %s%s%s%s\r\n"
1147 "NTS: ssdp:byebye\r\n"
1148 "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */
1149 "01-NLS: %u\r\n" /* same as BOOTID field. UDA v1.1 */
1150 "BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
1151 "CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
1152 "\r\n",
1153 dest_str, SSDP_PORT, /* HOST : */
1154 nt, suffix, /* NT: */
1155 usn1, usn2, usn3, suffix, /* USN: */
1156 upnp_bootid, upnp_bootid, upnp_configid);
1157 if(l<0)
1159 syslog(LOG_ERR, "%s: snprintf error", "SendSSDPbyebye()");
1160 return -1;
1162 else if((unsigned int)l >= sizeof(bufr))
1164 syslog(LOG_WARNING, "%s: truncated output (%u>=%u)",
1165 "SendSSDPbyebye()", (unsigned)l, (unsigned)sizeof(bufr));
1166 l = sizeof(bufr) - 1;
1168 n = sendto_or_schedule(s, bufr, l, 0, dest, destlen);
1169 if(n < 0)
1171 syslog(LOG_ERR, "sendto(udp_shutdown=%d): %m", s);
1172 return -1;
1174 else if(n != l)
1176 syslog(LOG_NOTICE, "sendto() sent %d out of %d bytes", n, l);
1177 return -1;
1179 return 0;
1182 /* This will broadcast ssdp:byebye notifications to inform
1183 * the network that UPnP is going down. */
1185 SendSSDPGoodbye(int * sockets, int n_sockets)
1187 struct sockaddr_in sockname4;
1188 #ifdef ENABLE_IPV6
1189 struct sockaddr_in6 sockname6;
1190 struct sockaddr * sockname;
1191 socklen_t socknamelen;
1192 int ipv6 = 0;
1193 #endif
1194 int i, j;
1195 char ver_str[4];
1196 int ret = 0;
1197 const char * dest_str;
1199 memset(&sockname4, 0, sizeof(struct sockaddr_in));
1200 sockname4.sin_family = AF_INET;
1201 sockname4.sin_port = htons(SSDP_PORT);
1202 sockname4.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
1203 #ifdef ENABLE_IPV6
1204 memset(&sockname6, 0, sizeof(struct sockaddr_in6));
1205 sockname6.sin6_family = AF_INET6;
1206 sockname6.sin6_port = htons(SSDP_PORT);
1207 inet_pton(AF_INET6, LL_SSDP_MCAST_ADDR, &(sockname6.sin6_addr));
1208 #else
1209 dest_str = SSDP_MCAST_ADDR;
1210 #endif
1212 for(j=0; j<n_sockets; j++)
1214 if(sockets[j] < 0)
1215 continue;
1216 #ifdef ENABLE_IPV6
1217 ipv6 = j & 1;
1218 if(ipv6) {
1219 dest_str = "[" LL_SSDP_MCAST_ADDR "]";
1220 sockname = (struct sockaddr *)&sockname6;
1221 socknamelen = sizeof(struct sockaddr_in6);
1222 } else {
1223 dest_str = SSDP_MCAST_ADDR;
1224 sockname = (struct sockaddr *)&sockname4;
1225 socknamelen = sizeof(struct sockaddr_in);
1227 #endif
1228 for(i=0; known_service_types[i].s; i++)
1230 if(i==0)
1231 ver_str[0] = '\0';
1232 else
1233 snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version);
1234 ret += SendSSDPbyebye(sockets[j],
1235 #ifdef ENABLE_IPV6
1236 sockname, socknamelen,
1237 #else
1238 (struct sockaddr *)&sockname4, sizeof(struct sockaddr_in),
1239 #endif
1240 dest_str,
1241 known_service_types[i].s, ver_str, /* NT: */
1242 known_service_types[i].uuid, "::",
1243 known_service_types[i].s); /* ver_str, USN: */
1244 if(0==memcmp(known_service_types[i].s,
1245 "urn:schemas-upnp-org:device", sizeof("urn:schemas-upnp-org:device")-1))
1247 ret += SendSSDPbyebye(sockets[j],
1248 #ifdef ENABLE_IPV6
1249 sockname, socknamelen,
1250 #else
1251 (struct sockaddr *)&sockname4, sizeof(struct sockaddr_in),
1252 #endif
1253 dest_str,
1254 known_service_types[i].uuid, "", /* NT: */
1255 known_service_types[i].uuid, "", ""); /* ver_str, USN: */
1259 return ret;
1262 /* SubmitServicesToMiniSSDPD() :
1263 * register services offered by MiniUPnPd to a running instance of
1264 * MiniSSDPd */
1266 SubmitServicesToMiniSSDPD(const char * host, unsigned short port) {
1267 struct sockaddr_un addr;
1268 int s;
1269 unsigned char buffer[2048];
1270 char strbuf[256];
1271 unsigned char * p;
1272 int i, l, n;
1273 char ver_str[4];
1275 s = socket(AF_UNIX, SOCK_STREAM, 0);
1276 if(s < 0) {
1277 syslog(LOG_ERR, "socket(unix): %m");
1278 return -1;
1280 addr.sun_family = AF_UNIX;
1281 strncpy(addr.sun_path, minissdpdsocketpath, sizeof(addr.sun_path));
1282 addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
1283 if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) {
1284 syslog(LOG_ERR, "connect(\"%s\"): %m", minissdpdsocketpath);
1285 close(s);
1286 return -1;
1288 for(i = 0; known_service_types[i].s; i++) {
1289 buffer[0] = 4; /* request type 4 : submit service */
1290 /* 4 strings following : ST (service type), USN, Server, Location */
1291 p = buffer + 1;
1292 l = (int)strlen(known_service_types[i].s);
1293 if(i > 0)
1294 l++;
1295 CODELENGTH(l, p);
1296 memcpy(p, known_service_types[i].s, l);
1297 if(i > 0)
1298 p[l-1] = '1';
1299 p += l;
1300 if(i==0)
1301 ver_str[0] = '\0';
1302 else
1303 snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version);
1304 l = snprintf(strbuf, sizeof(strbuf), "%s::%s%s",
1305 known_service_types[i].uuid, known_service_types[i].s, ver_str);
1306 if(l<0) {
1307 syslog(LOG_WARNING, "SubmitServicesToMiniSSDPD: snprintf %m");
1308 continue;
1309 } else if((unsigned)l>=sizeof(strbuf)) {
1310 l = sizeof(strbuf) - 1;
1312 CODELENGTH(l, p);
1313 memcpy(p, strbuf, l);
1314 p += l;
1315 l = (int)strlen(MINIUPNPD_SERVER_STRING);
1316 CODELENGTH(l, p);
1317 memcpy(p, MINIUPNPD_SERVER_STRING, l);
1318 p += l;
1319 l = snprintf(strbuf, sizeof(strbuf), "http://%s:%u" ROOTDESC_PATH,
1320 host, (unsigned int)port);
1321 if(l<0) {
1322 syslog(LOG_WARNING, "SubmitServicesToMiniSSDPD: snprintf %m");
1323 continue;
1324 } else if((unsigned)l>=sizeof(strbuf)) {
1325 l = sizeof(strbuf) - 1;
1327 CODELENGTH(l, p);
1328 memcpy(p, strbuf, l);
1329 p += l;
1330 /* now write the encoded data */
1331 n = p - buffer; /* bytes to send */
1332 p = buffer; /* start */
1333 while(n > 0) {
1334 l = write(s, p, n);
1335 if (l < 0) {
1336 if(errno == EINTR)
1337 continue;
1338 syslog(LOG_ERR, "write(): %m");
1339 close(s);
1340 return -1;
1341 } else if (l == 0) {
1342 syslog(LOG_ERR, "write() returned 0");
1343 close(s);
1344 return -1;
1346 p += l;
1347 n -= l;
1350 close(s);
1351 return 0;