Updates to Tomato RAF including NGINX && PHP
[tomato.git] / release / src / router / miniupnpd / minissdp.c
blobb3866ce09e9a04929fd7e857f964c11dbd4a219f
1 /* $Id: minissdp.c,v 1.48 2013/02/07 12:22:25 nanard Exp $ */
2 /* MiniUPnP project
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 */
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 "codelength.h"
29 /* SSDP ip/port */
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()
36 * param s socket
37 * param ifaddr ip v4 address
39 static int
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");
52 return -1;
55 return 0;
58 /* AddMulticastMembershipIPv6()
59 * param s socket (IPv6)
60 * To be improved to target specific network interfaces */
61 #ifdef ENABLE_IPV6
62 static int
63 AddMulticastMembershipIPv6(int s)
65 struct ipv6_mreq mr;
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
74 #endif
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");
78 return -1;
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");
84 return -1;
86 return 0;
88 #endif
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 */
93 int
94 OpenAndConfSSDPReceiveSocket(int ipv6)
96 int s;
97 struct sockaddr_storage sockname;
98 socklen_t sockname_len;
99 struct lan_addr_s * lan_addr;
100 int j = 1;
102 if( (s = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, 0)) < 0)
104 syslog(LOG_ERR, "%s: socket(udp): %m",
105 "OpenAndConfSSDPReceiveSocket");
106 return -1;
109 memset(&sockname, 0, sizeof(struct sockaddr_storage));
110 if(ipv6) {
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);
116 } else {
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" : "");
142 close(s);
143 return -1;
146 #ifdef ENABLE_IPV6
147 if(ipv6)
149 AddMulticastMembershipIPv6(s);
151 else
152 #endif
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)
158 syslog(LOG_WARNING,
159 "Failed to add multicast membership for interface %s",
160 lan_addr->str ? lan_addr->str : "NULL");
165 return s;
168 /* open the UDP socket used to send SSDP notifications to
169 * the multicast group reserved for them */
170 static int
171 OpenAndConfSSDPNotifySocket(in_addr_t addr)
173 int s;
174 unsigned char loopchar = 0;
175 int bcast = 1;
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");
186 return -1;
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");
194 close(s);
195 return -1;
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");
201 close(s);
202 return -1;
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");
213 close(s);
214 return -1;
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");
224 close(s);
225 return -1;
228 return s;
231 #ifdef ENABLE_IPV6
232 /* open the UDP socket used to send SSDP notifications to
233 * the multicast group reserved for them. IPv6 */
234 static int
235 OpenAndConfSSDPNotifySocketIPv6(unsigned int if_index)
237 int s;
238 unsigned int loop = 0;
240 s = socket(PF_INET6, SOCK_DGRAM, 0);
241 if(s < 0)
243 syslog(LOG_ERR, "socket(udp_notify IPv6): %m");
244 return -1;
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);
249 close(s);
250 return -1;
252 if(setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof(loop)) < 0)
254 syslog(LOG_ERR, "setsockopt(udp_notify, IPV6_MULTICAST_LOOP): %m");
255 close(s);
256 return -1;
258 return s;
260 #endif
263 OpenAndConfSSDPNotifySockets(int * sockets)
264 /*OpenAndConfSSDPNotifySockets(int * sockets,
265 struct lan_addr_s * lan_addr, int n_lan_addr)*/
267 int i;
268 struct lan_addr_s * lan_addr;
270 for(i=0, lan_addr = lan_addrs.lh_first;
271 lan_addr != NULL;
272 lan_addr = lan_addr->list.le_next)
274 sockets[i] = OpenAndConfSSDPNotifySocket(lan_addr->addr.s_addr);
275 if(sockets[i] < 0)
276 goto error;
277 i++;
278 #ifdef ENABLE_IPV6
279 sockets[i] = OpenAndConfSSDPNotifySocketIPv6(lan_addr->index);
280 if(sockets[i] < 0)
281 goto error;
282 i++;
283 #endif
285 return 0;
286 error:
287 while(--i >= 0)
289 close(sockets[i]);
290 sockets[i] = -1;
292 return -1;
296 * response from a LiveBox (Wanadoo)
297 HTTP/1.1 200 OK
298 CACHE-CONTROL: max-age=1800
299 DATE: Thu, 01 Jan 1970 04:03:23 GMT
300 EXT:
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
303 ST: upnp:rootdevice
304 USN: uuid:75802409-bccb-40e7-8e6c-fa095ecce13e::upnp:rootdevice
306 * response from a Linksys 802.11b :
307 HTTP/1.1 200 OK
308 Cache-Control:max-age=120
309 Location:http://192.168.5.1:5678/rootDesc.xml
310 Server:NT/5.0 UPnP/1.0
311 ST:upnp:rootdevice
312 USN:uuid:upnp-InternetGatewayDevice-1_0-0090a2777777::upnp:rootdevice
313 EXT:
316 /* not really an SSDP "announce" as it is the response
317 * to a SSDP "M-SEARCH" */
318 static void
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)
323 int l, n;
324 char buf[512];
325 char addr_str[64];
326 socklen_t addrlen;
327 int st_is_uuid;
328 #ifdef ENABLE_HTTP_DATE
329 char http_date[64];
330 time_t t;
331 struct tm tm;
333 time(&t);
334 gmtime_r(&t, &tm);
335 strftime(http_date, sizeof(http_date),
336 "%a, %d %b %Y %H:%M:%S GMT", &tm);
337 #endif
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
352 "DATE: %s\r\n"
353 #endif
354 "ST: %.*s%s\r\n"
355 "USN: %s%s%.*s%s\r\n"
356 "EXT:\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 */
363 "\r\n",
364 #ifdef ENABLE_HTTP_DATE
365 http_date,
366 #endif
367 st_len, st, suffix,
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,
375 addr, addrlen);
376 sockaddr_to_string(addr, addr_str, sizeof(addr_str));
377 syslog(LOG_INFO, "SSDP Announce %d bytes to %s ST: %.*s",n,
378 addr_str,
379 l, buf);
380 if(n < 0)
382 /* XXX handle EINTR, EAGAIN, EWOULDBLOCK */
383 syslog(LOG_ERR, "sendto(udp): %m");
387 #ifndef IGD_V2
388 #define IGD_VER 1
389 #define WANIPC_VER 1
390 #else
391 #define IGD_VER 2
392 #define WANIPC_VER 2
393 #endif
395 static struct {
396 const char * s;
397 const int version;
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},
409 #endif
410 #ifdef ENABLE_6FC_SERVICE
411 {"url:schemas-upnp-org:service:WANIPv6FirewallControl:", 1},
412 #endif
413 {0, 0}
416 static void
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)
423 char bufr[512];
424 int n, l;
426 l = snprintf(bufr, sizeof(bufr),
427 "NOTIFY * HTTP/1.1\r\n"
428 "HOST: %s:%d\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"
432 "NT: %s%s\r\n"
433 "USN: %s%s%s%s\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 */
439 "\r\n",
440 ipv6 ? "[" LL_SSDP_MCAST_ADDR "]" : SSDP_MCAST_ADDR,
441 SSDP_PORT,
442 lifetime,
443 host, port,
444 nt, suffix, /* NT: */
445 usn1, usn2, usn3, suffix, /* USN: */
446 upnp_bootid, upnp_bootid, upnp_configid );
447 if(l<0)
449 syslog(LOG_ERR, "SendSSDPNotifies() snprintf error");
450 return;
452 else if((unsigned int)l >= sizeof(bufr))
454 syslog(LOG_WARNING, "SendSSDPNotifies(): truncated output");
455 l = sizeof(bufr);
457 n = sendto(s, bufr, l, 0, dest,
458 #ifdef ENABLE_IPV6
459 ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)
460 #else
461 sizeof(struct sockaddr_in)
462 #endif
464 if(n < 0)
466 /* XXX handle EINTR, EAGAIN, EWOULDBLOCK */
467 syslog(LOG_ERR, "sendto(udp_notify=%d, %s): %m", s,
468 host ? host : "NULL");
470 else if(n != l)
472 syslog(LOG_NOTICE, "sendto() sent %d out of %d bytes", n, l);
476 static void
477 SendSSDPNotifies(int s, const char * host, unsigned short port,
478 unsigned int lifetime, int ipv6)
480 #ifdef ENABLE_IPV6
481 struct sockaddr_storage sockname;
482 #else
483 struct sockaddr_in sockname;
484 #endif
485 int i=0;
486 char ver_str[4];
488 memset(&sockname, 0, sizeof(sockname));
489 #ifdef ENABLE_IPV6
490 if(ipv6)
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));
497 else
498 #endif
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)
508 if(i==0)
509 ver_str[0] = '\0';
510 else
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: */
515 lifetime, ipv6);
516 if(i==0) /* rootdevice */
517 SendSSDPNotify(s, (struct sockaddr *)&sockname, host, port,
518 uuidvalue, "", /* NT: */
519 uuidvalue, "", "", /* ver_str, USN: */
520 lifetime, ipv6);
521 i++;
525 void
526 SendSSDPNotifies2(int * sockets,
527 unsigned short port,
528 unsigned int lifetime)
530 int i;
531 struct lan_addr_s * lan_addr;
532 for(i=0, lan_addr = lan_addrs.lh_first;
533 lan_addr != NULL;
534 lan_addr = lan_addr->list.le_next)
536 SendSSDPNotifies(sockets[i], lan_addr->str, port,
537 lifetime, 0);
538 i++;
539 #ifdef ENABLE_IPV6
540 SendSSDPNotifies(sockets[i], ipv6_addr_for_http_with_brackets, port,
541 lifetime, 1);
542 i++;
543 #endif
547 /* ProcessSSDPRequest()
548 * process SSDP M-SEARCH requests and responds to them */
549 void
550 ProcessSSDPRequest(int s, unsigned short port)
552 int n;
553 char bufr[1500];
554 socklen_t len_r;
555 #ifdef ENABLE_IPV6
556 struct sockaddr_storage sendername;
557 len_r = sizeof(struct sockaddr_storage);
558 #else
559 struct sockaddr_in sendername;
560 len_r = sizeof(struct sockaddr_in);
561 #endif
563 n = recvfrom(s, bufr, sizeof(bufr), 0,
564 (struct sockaddr *)&sendername, &len_r);
565 if(n < 0)
567 /* EAGAIN, EWOULDBLOCK, EINTR : silently ignore (try again next time)
568 * other errors : log to LOG_ERR */
569 if(errno != EAGAIN &&
570 errno != EWOULDBLOCK &&
571 errno != EINTR)
573 syslog(LOG_ERR, "recvfrom(udp): %m");
575 return;
577 ProcessSSDPData(s, bufr, n, (struct sockaddr *)&sendername, port);
581 void
582 ProcessSSDPData(int s, const char *bufr, int n,
583 const struct sockaddr * sender, unsigned short port) {
584 int i, l;
585 struct lan_addr_s * lan_addr = NULL;
586 const char * st = NULL;
587 int st_len = 0;
588 int st_ver = 0;
589 char sender_str[64];
590 char ver_str[4];
591 const char * announced_host = NULL;
592 #ifdef UPNP_STRICT
593 #ifdef ENABLE_IPV6
594 char announced_host_buf[64];
595 #endif
596 int mx_value = -1;
597 #endif
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);
602 if(lan_addr == NULL)
604 syslog(LOG_WARNING, "SSDP packet sender %s not from a LAN, ignoring",
605 sender_str);
606 return;
609 if(memcmp(bufr, "NOTIFY", 6) == 0)
611 /* ignore NOTIFY packets. We could log the sender and device type */
612 return;
614 else if(memcmp(bufr, "M-SEARCH", 8) == 0)
616 i = 0;
617 while(i < n)
619 while((i < n - 1) && (bufr[i] != '\r' || bufr[i+1] != '\n'))
620 i++;
621 i += 2;
622 if((i < n - 3) && (strncasecmp(bufr+i, "st:", 3) == 0))
624 st = bufr+i+3;
625 st_len = 0;
626 while((*st == ' ' || *st == '\t') && (st < bufr + n))
627 st++;
628 while(st[st_len]!='\r' && st[st_len]!='\n'
629 && (st + st_len < bufr + n))
630 st_len++;
631 l = st_len;
632 while(l > 0 && st[l-1] != ':')
633 l--;
634 st_ver = atoi(st+l);
635 syslog(LOG_DEBUG, "ST: %.*s (ver=%d)", st_len, st, st_ver);
636 /*j = 0;*/
637 /*while(bufr[i+j]!='\r') j++;*/
638 /*syslog(LOG_INFO, "%.*s", j, bufr+i);*/
640 #ifdef UPNP_STRICT
641 else if((i < n - 3) && (strncasecmp(bufr+i, "mx:", 3) == 0))
643 const char * mx;
644 int mx_len;
645 mx = bufr+i+3;
646 mx_len = 0;
647 while((*mx == ' ' || *mx == '\t') && (mx < bufr + n))
648 mx++;
649 while(mx[mx_len]!='\r' && mx[mx_len]!='\n'
650 && (mx + mx_len < bufr + n))
651 mx_len++;
652 mx_value = atoi(mx);
653 syslog(LOG_DEBUG, "MX: %.*s (value=%d)", mx_len, mx, mx_value);
655 #endif
657 #ifdef UPNP_STRICT
658 if(mx_value < 0) {
659 syslog(LOG_INFO, "ignoring SSDP packet missing MX: header");
660 return;
662 #endif
663 /*syslog(LOG_INFO, "SSDP M-SEARCH packet received from %s",
664 sender_str );*/
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");
676 return;
678 announced_host = lan_addr->str;
680 #ifdef ENABLE_IPV6
681 else
683 /* IPv6 address with brackets */
684 #ifdef UPNP_STRICT
685 int index;
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;
694 } else {
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';
703 } else {
704 syslog(LOG_NOTICE, "cannot suffix %s with ']'",
705 announced_host_buf);
707 announced_host = announced_host_buf;
708 } else {
709 syslog(LOG_NOTICE, "inet_ntop() failed %m");
710 announced_host = ipv6_addr_for_http_with_brackets;
713 #else
714 announced_host = ipv6_addr_for_http_with_brackets;
715 #endif
717 #endif
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))
723 #ifdef UPNP_STRICT
724 && (st_ver <= known_service_types[i].version)
725 /* only answer for service version lower or equal of supported one */
726 #endif
729 syslog(LOG_INFO, "Single search found");
730 SendSSDPAnnounce2(s, sender,
731 st, st_len, "",
732 announced_host, port);
733 break;
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++)
743 if(i==0)
744 ver_str[0] = '\0';
745 else
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);
765 else
767 syslog(LOG_INFO, "Invalid SSDP M-SEARCH from %s", sender_str);
770 else
772 syslog(LOG_NOTICE, "Unknown udp packet received from %s", sender_str);
776 static int
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,
780 int ipv6)
782 int n, l;
783 char bufr[512];
785 l = snprintf(bufr, sizeof(bufr),
786 "NOTIFY * HTTP/1.1\r\n"
787 "HOST: %s:%d\r\n"
788 "NT: %s%s\r\n"
789 "USN: %s%s%s%s\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 */
795 "\r\n",
796 ipv6 ? "[" LL_SSDP_MCAST_ADDR "]" : SSDP_MCAST_ADDR,
797 SSDP_PORT,
798 nt, suffix, /* NT: */
799 usn1, usn2, usn3, suffix, /* USN: */
800 upnp_bootid, upnp_bootid, upnp_configid);
801 if(l<0)
803 syslog(LOG_ERR, "SendSSDPbyebye() snprintf error");
804 return -1;
806 else if((unsigned int)l >= sizeof(bufr))
808 syslog(LOG_WARNING, "SendSSDPbyebye(): truncated output");
809 l = sizeof(bufr);
811 n = sendto(s, bufr, l, 0, dest,
812 #ifdef ENABLE_IPV6
813 ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)
814 #else
815 sizeof(struct sockaddr_in)
816 #endif
818 if(n < 0)
820 syslog(LOG_ERR, "sendto(udp_shutdown=%d): %m", s);
821 return -1;
823 else if(n != l)
825 syslog(LOG_NOTICE, "sendto() sent %d out of %d bytes", n, l);
826 return -1;
828 return 0;
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;
837 #ifdef ENABLE_IPV6
838 struct sockaddr_in6 sockname6;
839 #endif
840 int i, j;
841 char ver_str[4];
842 int ret = 0;
843 int ipv6 = 0;
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);
849 #ifdef ENABLE_IPV6
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));
854 #endif
856 for(j=0; j<n_sockets; j++)
858 #ifdef ENABLE_IPV6
859 ipv6 = j & 1;
860 #endif
861 for(i=0; known_service_types[i].s; i++)
863 if(i==0)
864 ver_str[0] = '\0';
865 else
866 snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version);
867 ret += SendSSDPbyebye(sockets[j],
868 #ifdef ENABLE_IPV6
869 ipv6 ? (struct sockaddr *)&sockname6 : (struct sockaddr *)&sockname,
870 #else
871 (struct sockaddr *)&sockname,
872 #endif
873 known_service_types[i].s, ver_str, /* NT: */
874 uuidvalue, "::", known_service_types[i].s, /* ver_str, USN: */
875 ipv6);
876 if(i==0) /* root device */
878 ret += SendSSDPbyebye(sockets[j],
879 #ifdef ENABLE_IPV6
880 ipv6 ? (struct sockaddr *)&sockname6 : (struct sockaddr *)&sockname,
881 #else
882 (struct sockaddr *)&sockname,
883 #endif
884 uuidvalue, "", /* NT: */
885 uuidvalue, "", "", /* ver_str, USN: */
886 ipv6);
890 return ret;
893 /* SubmitServicesToMiniSSDPD() :
894 * register services offered by MiniUPnPd to a running instance of
895 * MiniSSDPd */
897 SubmitServicesToMiniSSDPD(const char * host, unsigned short port) {
898 struct sockaddr_un addr;
899 int s;
900 unsigned char buffer[2048];
901 char strbuf[256];
902 unsigned char * p;
903 int i, l, n;
904 char ver_str[4];
906 s = socket(AF_UNIX, SOCK_STREAM, 0);
907 if(s < 0) {
908 syslog(LOG_ERR, "socket(unix): %m");
909 return -1;
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);
915 close(s);
916 return -1;
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 */
921 p = buffer + 1;
922 l = (int)strlen(known_service_types[i].s);
923 if(i > 0)
924 l++;
925 CODELENGTH(l, p);
926 memcpy(p, known_service_types[i].s, l);
927 if(i > 0)
928 p[l-1] = '1';
929 p += l;
930 if(i==0)
931 ver_str[0] = '\0';
932 else
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);
936 CODELENGTH(l, p);
937 memcpy(p, strbuf, l);
938 p += l;
939 l = (int)strlen(MINIUPNPD_SERVER_STRING);
940 CODELENGTH(l, p);
941 memcpy(p, MINIUPNPD_SERVER_STRING, l);
942 p += l;
943 l = snprintf(strbuf, sizeof(strbuf), "http://%s:%u" ROOTDESC_PATH,
944 host, (unsigned int)port);
945 CODELENGTH(l, p);
946 memcpy(p, strbuf, l);
947 p += l;
948 /* now write the encoded data */
949 n = p - buffer; /* bytes to send */
950 p = buffer; /* start */
951 while(n > 0) {
952 l = write(s, p, n);
953 if (l < 0) {
954 syslog(LOG_ERR, "write(): %m");
955 close(s);
956 return -1;
957 } else if (l == 0) {
958 syslog(LOG_ERR, "write() returned 0");
959 close(s);
960 return -1;
962 p += l;
963 n -= l;
966 close(s);
967 return 0;