Miniupnpd: update to 1.9 (20150430)
[tomato.git] / release / src-rt-6.x.4708 / router / miniupnpd / minissdp.c
blob92f7b75270b0cebcc75ce5ada8cd7b199542ff7f
1 /* $Id: minissdp.c,v 1.74 2015/04/30 08:59:51 nanard Exp $ */
2 /* MiniUPnP project
3 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4 * (c) 2006-2015 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 /* maximum lenght of SSDP packets we are generating
39 * (reception is done in a 1500byte buffer) */
40 #ifdef ENABLE_HTTPS
41 #define SSDP_PACKET_MAX_LEN 768
42 #else
43 #define SSDP_PACKET_MAX_LEN 512
44 #endif
46 /* AddMulticastMembership()
47 * param s socket
48 * param ifaddr ip v4 address
50 static int
51 AddMulticastMembership(int s, in_addr_t ifaddr)
53 struct ip_mreq imr; /* Ip multicast membership */
55 /* setting up imr structure */
56 imr.imr_multiaddr.s_addr = inet_addr(SSDP_MCAST_ADDR);
57 /*imr.imr_interface.s_addr = htonl(INADDR_ANY);*/
58 imr.imr_interface.s_addr = ifaddr; /*inet_addr(ifaddr);*/
60 if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&imr, sizeof(struct ip_mreq)) < 0)
62 syslog(LOG_ERR, "setsockopt(udp, IP_ADD_MEMBERSHIP): %m");
63 return -1;
66 return 0;
69 /* AddMulticastMembershipIPv6()
70 * param s socket (IPv6)
71 * param ifindex : interface index (0 : All interfaces) */
72 #ifdef ENABLE_IPV6
73 static int
74 AddMulticastMembershipIPv6(int s, unsigned int ifindex)
76 struct ipv6_mreq mr;
78 memset(&mr, 0, sizeof(mr));
79 mr.ipv6mr_interface = ifindex; /* 0 : all interfaces */
80 #ifndef IPV6_ADD_MEMBERSHIP
81 #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
82 #endif
83 inet_pton(AF_INET6, LL_SSDP_MCAST_ADDR, &mr.ipv6mr_multiaddr);
84 if(setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mr, sizeof(struct ipv6_mreq)) < 0)
86 syslog(LOG_ERR, "setsockopt(udp, IPV6_ADD_MEMBERSHIP): %m");
87 return -1;
89 inet_pton(AF_INET6, SL_SSDP_MCAST_ADDR, &mr.ipv6mr_multiaddr);
90 if(setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mr, sizeof(struct ipv6_mreq)) < 0)
92 syslog(LOG_ERR, "setsockopt(udp, IPV6_ADD_MEMBERSHIP): %m");
93 return -1;
95 inet_pton(AF_INET6, GL_SSDP_MCAST_ADDR, &mr.ipv6mr_multiaddr);
96 if(setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mr, sizeof(struct ipv6_mreq)) < 0)
98 syslog(LOG_ERR, "setsockopt(udp, IPV6_ADD_MEMBERSHIP): %m");
99 return -1;
101 return 0;
103 #endif
105 /* Open and configure the socket listening for
106 * SSDP udp packets sent on 239.255.255.250 port 1900
107 * SSDP v6 udp packets sent on FF02::C, or FF05::C, port 1900 */
109 OpenAndConfSSDPReceiveSocket(int ipv6)
111 int s;
112 struct sockaddr_storage sockname;
113 socklen_t sockname_len;
114 struct lan_addr_s * lan_addr;
115 int j = 1;
117 if( (s = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, 0)) < 0)
119 syslog(LOG_ERR, "%s: socket(udp): %m",
120 "OpenAndConfSSDPReceiveSocket");
121 return -1;
124 memset(&sockname, 0, sizeof(struct sockaddr_storage));
125 #ifdef ENABLE_IPV6
126 if(ipv6)
128 struct sockaddr_in6 * saddr = (struct sockaddr_in6 *)&sockname;
129 saddr->sin6_family = AF_INET6;
130 saddr->sin6_port = htons(SSDP_PORT);
131 saddr->sin6_addr = ipv6_bind_addr;
132 sockname_len = sizeof(struct sockaddr_in6);
134 else
135 #endif /* ENABLE_IPV6 */
137 struct sockaddr_in * saddr = (struct sockaddr_in *)&sockname;
138 saddr->sin_family = AF_INET;
139 saddr->sin_port = htons(SSDP_PORT);
140 /* NOTE : it seems it doesnt work when binding on the specific address */
141 /*saddr->sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);*/
142 saddr->sin_addr.s_addr = htonl(INADDR_ANY);
143 /*saddr->sin_addr.s_addr = inet_addr(ifaddr);*/
144 sockname_len = sizeof(struct sockaddr_in);
147 if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &j, sizeof(j)) < 0)
149 syslog(LOG_WARNING, "setsockopt(udp, SO_REUSEADDR): %m");
152 if(!set_non_blocking(s))
154 syslog(LOG_WARNING, "%s: set_non_blocking(): %m",
155 "OpenAndConfSSDPReceiveSocket");
158 if(bind(s, (struct sockaddr *)&sockname, sockname_len) < 0)
160 syslog(LOG_ERR, "%s: bind(udp%s): %m",
161 "OpenAndConfSSDPReceiveSocket", ipv6 ? "6" : "");
162 close(s);
163 return -1;
166 #ifdef ENABLE_IPV6
167 if(ipv6)
169 for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next)
171 if(AddMulticastMembershipIPv6(s, lan_addr->index) < 0)
173 syslog(LOG_WARNING,
174 "Failed to add IPv6 multicast membership for interface %s",
175 lan_addr->str ? lan_addr->str : "NULL");
179 else
180 #endif
182 for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next)
184 if(AddMulticastMembership(s, lan_addr->addr.s_addr) < 0)
186 syslog(LOG_WARNING,
187 "Failed to add multicast membership for interface %s",
188 lan_addr->str ? lan_addr->str : "NULL");
193 return s;
196 /* open the UDP socket used to send SSDP notifications to
197 * the multicast group reserved for them */
198 static int
199 OpenAndConfSSDPNotifySocket(in_addr_t addr)
201 int s;
202 unsigned char loopchar = 0;
203 int bcast = 1;
204 unsigned char ttl = 2; /* UDA v1.1 says :
205 The TTL for the IP packet SHOULD default to 2 and
206 SHOULD be configurable. */
207 /* TODO: Make TTL be configurable */
208 struct in_addr mc_if;
209 struct sockaddr_in sockname;
211 if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
213 syslog(LOG_ERR, "socket(udp_notify): %m");
214 return -1;
217 mc_if.s_addr = addr; /*inet_addr(addr);*/
219 if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loopchar, sizeof(loopchar)) < 0)
221 syslog(LOG_ERR, "setsockopt(udp_notify, IP_MULTICAST_LOOP): %m");
222 close(s);
223 return -1;
226 if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&mc_if, sizeof(mc_if)) < 0)
228 syslog(LOG_ERR, "setsockopt(udp_notify, IP_MULTICAST_IF): %m");
229 close(s);
230 return -1;
233 if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0)
235 syslog(LOG_WARNING, "setsockopt(udp_notify, IP_MULTICAST_TTL,): %m");
238 if(setsockopt(s, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bcast)) < 0)
240 syslog(LOG_ERR, "setsockopt(udp_notify, SO_BROADCAST): %m");
241 close(s);
242 return -1;
245 /* bind() socket before using sendto() is not mandatory
246 * (sendto() will implicitly bind the socket when called on
247 * an unbound socket)
248 * here it is used to se a specific sending address */
249 memset(&sockname, 0, sizeof(struct sockaddr_in));
250 sockname.sin_family = AF_INET;
251 sockname.sin_addr.s_addr = addr; /*inet_addr(addr);*/
253 if (bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
255 syslog(LOG_ERR, "bind(udp_notify): %m");
256 close(s);
257 return -1;
260 return s;
263 #ifdef ENABLE_IPV6
264 /* open the UDP socket used to send SSDP notifications to
265 * the multicast group reserved for them. IPv6 */
266 static int
267 OpenAndConfSSDPNotifySocketIPv6(unsigned int if_index)
269 int s;
270 unsigned int loop = 0;
271 struct sockaddr_in6 sockname;
273 s = socket(PF_INET6, SOCK_DGRAM, 0);
274 if(s < 0)
276 syslog(LOG_ERR, "socket(udp_notify IPv6): %m");
277 return -1;
279 if(setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &if_index, sizeof(if_index)) < 0)
281 syslog(LOG_ERR, "setsockopt(udp_notify IPv6, IPV6_MULTICAST_IF, %u): %m", if_index);
282 close(s);
283 return -1;
285 if(setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof(loop)) < 0)
287 syslog(LOG_ERR, "setsockopt(udp_notify, IPV6_MULTICAST_LOOP): %m");
288 close(s);
289 return -1;
292 /* bind() socket before using sendto() is not mandatory
293 * (sendto() will implicitly bind the socket when called on
294 * an unbound socket)
295 * but explicit bind permits to set port/scope_id/etc. */
296 memset(&sockname, 0, sizeof(sockname));
297 sockname.sin6_family = AF_INET6;
298 sockname.sin6_addr = in6addr_any;
299 /*sockname.sin6_port = htons(port);*/
300 /*sockname.sin6_scope_id = if_index;*/
301 if(bind(s, (struct sockaddr *)&sockname, sizeof(sockname)) < 0)
303 syslog(LOG_ERR, "bind(udp_notify IPv6): %m");
304 close(s);
305 return -1;
308 return s;
310 #endif
313 OpenAndConfSSDPNotifySockets(int * sockets)
314 /*OpenAndConfSSDPNotifySockets(int * sockets,
315 struct lan_addr_s * lan_addr, int n_lan_addr)*/
317 int i;
318 struct lan_addr_s * lan_addr;
320 for(i=0, lan_addr = lan_addrs.lh_first;
321 lan_addr != NULL;
322 lan_addr = lan_addr->list.le_next)
324 sockets[i] = OpenAndConfSSDPNotifySocket(lan_addr->addr.s_addr);
325 if(sockets[i] < 0)
326 goto error;
327 i++;
328 #ifdef ENABLE_IPV6
329 if(GETFLAG(IPV6DISABLEDMASK))
331 sockets[i] = -1;
333 else
335 sockets[i] = OpenAndConfSSDPNotifySocketIPv6(lan_addr->index);
336 if(sockets[i] < 0)
337 goto error;
339 i++;
340 #endif
342 return 0;
343 error:
344 while(--i >= 0)
346 close(sockets[i]);
347 sockets[i] = -1;
349 return -1;
353 * response from a LiveBox (Wanadoo)
354 HTTP/1.1 200 OK
355 CACHE-CONTROL: max-age=1800
356 DATE: Thu, 01 Jan 1970 04:03:23 GMT
357 EXT:
358 LOCATION: http://192.168.0.1:49152/gatedesc.xml
359 SERVER: Linux/2.4.17, UPnP/1.0, Intel SDK for UPnP devices /1.2
360 ST: upnp:rootdevice
361 USN: uuid:75802409-bccb-40e7-8e6c-fa095ecce13e::upnp:rootdevice
363 * response from a Linksys 802.11b :
364 HTTP/1.1 200 OK
365 Cache-Control:max-age=120
366 Location:http://192.168.5.1:5678/rootDesc.xml
367 Server:NT/5.0 UPnP/1.0
368 ST:upnp:rootdevice
369 USN:uuid:upnp-InternetGatewayDevice-1_0-0090a2777777::upnp:rootdevice
370 EXT:
373 /* Responds to a SSDP "M-SEARCH"
374 * s : socket to use
375 * addr : peer
376 * st, st_len : ST: header
377 * suffix : suffix for USN: header
378 * host, port : our HTTP host, port
379 * delay : in milli-seconds
381 static void
382 SendSSDPResponse(int s, const struct sockaddr * addr,
383 const char * st, int st_len, const char * suffix,
384 const char * host, unsigned short http_port,
385 #ifdef ENABLE_HTTPS
386 unsigned short https_port,
387 #endif
388 const char * uuidvalue, unsigned int delay)
390 int l, n;
391 char buf[SSDP_PACKET_MAX_LEN];
392 char addr_str[64];
393 socklen_t addrlen;
394 int st_is_uuid;
395 #ifdef ENABLE_HTTP_DATE
396 char http_date[64];
397 time_t t;
398 struct tm tm;
400 time(&t);
401 gmtime_r(&t, &tm);
402 strftime(http_date, sizeof(http_date),
403 "%a, %d %b %Y %H:%M:%S GMT", &tm);
404 #endif
406 st_is_uuid = (st_len == (int)strlen(uuidvalue)) &&
407 (memcmp(uuidvalue, st, st_len) == 0);
409 * follow guideline from document "UPnP Device Architecture 1.0"
410 * uppercase is recommended.
411 * DATE: is recommended
412 * SERVER: OS/ver UPnP/1.0 miniupnpd/1.0
413 * - check what to put in the 'Cache-Control' header
415 * have a look at the document "UPnP Device Architecture v1.1 */
416 l = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\n"
417 "CACHE-CONTROL: max-age=120\r\n"
418 #ifdef ENABLE_HTTP_DATE
419 "DATE: %s\r\n"
420 #endif
421 "ST: %.*s%s\r\n"
422 "USN: %s%s%.*s%s\r\n"
423 "EXT:\r\n"
424 "SERVER: " MINIUPNPD_SERVER_STRING "\r\n"
425 "LOCATION: http://%s:%u" ROOTDESC_PATH "\r\n"
426 #ifdef ENABLE_HTTPS
427 "SECURELOCATION.UPNP.ORG: https://%s:%u" ROOTDESC_PATH "\r\n"
428 #endif
429 "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */
430 "01-NLS: %u\r\n" /* same as BOOTID. UDA v1.1 */
431 "BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
432 "CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
433 "\r\n",
434 #ifdef ENABLE_HTTP_DATE
435 http_date,
436 #endif
437 st_len, st, suffix,
438 uuidvalue, st_is_uuid ? "" : "::",
439 st_is_uuid ? 0 : st_len, st, suffix,
440 host, (unsigned int)http_port,
441 #ifdef ENABLE_HTTPS
442 host, (unsigned int)https_port,
443 #endif
444 upnp_bootid, upnp_bootid, upnp_configid);
445 if(l<0)
447 syslog(LOG_ERR, "%s: snprintf failed %m",
448 "SendSSDPResponse()");
449 return;
451 else if((unsigned)l>=sizeof(buf))
453 syslog(LOG_WARNING, "%s: truncated output (%u>=%u)",
454 "SendSSDPResponse()", (unsigned)l, (unsigned)sizeof(buf));
455 l = sizeof(buf) - 1;
457 addrlen = (addr->sa_family == AF_INET6)
458 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
459 n = sendto_schedule(s, buf, l, 0,
460 addr, addrlen, delay);
461 sockaddr_to_string(addr, addr_str, sizeof(addr_str));
462 syslog(LOG_DEBUG, "%s: %d bytes to %s ST: %.*s",
463 "SendSSDPResponse()",
464 n, addr_str, l, buf);
465 if(n < 0)
467 syslog(LOG_ERR, "%s: sendto(udp): %m",
468 "SendSSDPResponse()");
472 #ifndef IGD_V2
473 #define IGD_VER 1
474 #define WANIPC_VER 1
475 #else
476 #define IGD_VER 2
477 #define WANIPC_VER 2
478 #endif
480 static struct {
481 const char * s;
482 const int version;
483 const char * uuid;
484 } const known_service_types[] =
486 {"upnp:rootdevice", 0, uuidvalue_igd},
487 {"urn:schemas-upnp-org:device:InternetGatewayDevice:", IGD_VER, uuidvalue_igd},
488 {"urn:schemas-upnp-org:device:WANConnectionDevice:", 1, uuidvalue_wcd},
489 {"urn:schemas-upnp-org:device:WANDevice:", 1, uuidvalue_wan},
490 {"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:", 1, uuidvalue_wan},
491 {"urn:schemas-upnp-org:service:WANIPConnection:", WANIPC_VER, uuidvalue_wcd},
492 #ifndef UPNP_STRICT
493 /* We use WAN IP Connection, not PPP connection,
494 * but buggy control points may try to use WanPPPConnection
495 * anyway */
496 {"urn:schemas-upnp-org:service:WANPPPConnection:", 1, uuidvalue_wcd},
497 #endif
498 #ifdef ENABLE_L3F_SERVICE
499 {"urn:schemas-upnp-org:service:Layer3Forwarding:", 1, uuidvalue_igd},
500 #endif
501 #ifdef ENABLE_6FC_SERVICE
502 {"url:schemas-upnp-org:service:WANIPv6FirewallControl:", 1, uuidvalue_wcd},
503 #endif
504 /* we might want to support urn:schemas-wifialliance-org:device:WFADevice:1
505 * urn:schemas-wifialliance-org:device:WFADevice:1
506 * in the future */
507 {0, 0, 0}
510 /* SendSSDPNotify() sends the SSDP NOTIFY to a specific
511 * destination, for a specific UPnP service or device */
512 static void
513 SendSSDPNotify(int s, const struct sockaddr * dest, socklen_t dest_len,
514 const char * dest_str,
515 const char * host, unsigned short http_port,
516 #ifdef ENABLE_HTTPS
517 unsigned short https_port,
518 #endif
519 const char * nt, const char * suffix,
520 const char * usn1, const char * usn2, const char * usn3,
521 unsigned int lifetime)
523 char bufr[SSDP_PACKET_MAX_LEN];
524 int n, l;
526 l = snprintf(bufr, sizeof(bufr),
527 "NOTIFY * HTTP/1.1\r\n"
528 "HOST: %s:%d\r\n"
529 "CACHE-CONTROL: max-age=%u\r\n"
530 "LOCATION: http://%s:%u" ROOTDESC_PATH "\r\n"
531 #ifdef ENABLE_HTTPS
532 "SECURELOCATION.UPNP.ORG: https://%s:%u" ROOTDESC_PATH "\r\n"
533 #endif
534 "SERVER: " MINIUPNPD_SERVER_STRING "\r\n"
535 "NT: %s%s\r\n"
536 "USN: %s%s%s%s\r\n"
537 "NTS: ssdp:alive\r\n"
538 "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */
539 "01-NLS: %u\r\n" /* same as BOOTID field. UDA v1.1 */
540 "BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
541 "CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
542 "\r\n",
543 dest_str, SSDP_PORT, /* HOST: */
544 lifetime, /* CACHE-CONTROL: */
545 host, (unsigned int)http_port, /* LOCATION: */
546 #ifdef ENABLE_HTTPS
547 host, (unsigned int)https_port, /* SECURE-LOCATION: */
548 #endif
549 nt, suffix, /* NT: */
550 usn1, usn2, usn3, suffix, /* USN: */
551 upnp_bootid, /* 01-NLS: */
552 upnp_bootid, /* BOOTID.UPNP.ORG: */
553 upnp_configid ); /* CONFIGID.UPNP.ORG: */
554 if(l<0) {
555 syslog(LOG_ERR, "%s: snprintf error", "SendSSDPNotify()");
556 return;
557 } else if((unsigned int)l >= sizeof(bufr)) {
558 syslog(LOG_WARNING, "%s: truncated output (%u>=%u)",
559 "SendSSDPNotify()", (unsigned)l, (unsigned)sizeof(bufr));
560 l = sizeof(bufr) - 1;
562 n = sendto_or_schedule(s, bufr, l, 0, dest, dest_len);
563 if(n < 0) {
564 syslog(LOG_ERR, "sendto(udp_notify=%d, %s): %m", s,
565 host ? host : "NULL");
566 } else if(n != l) {
567 syslog(LOG_NOTICE, "sendto() sent %d out of %d bytes", n, l);
569 /* Due to the unreliable nature of UDP, devices SHOULD send the entire
570 * set of discovery messages more than once with some delay between
571 * sets e.g. a few hundred milliseconds. To avoid network congestion
572 * discovery messages SHOULD NOT be sent more than three times. */
573 n = sendto_schedule(s, bufr, l, 0, dest, dest_len, 250);
574 if(n < 0) {
575 syslog(LOG_ERR, "sendto(udp_notify=%d, %s): %m", s,
576 host ? host : "NULL");
580 /* SendSSDPNotifies() send SSPD NOTIFY for a specific
581 * LAN (network interface) for all devices / services */
582 #ifdef ENABLE_HTTPS
583 static void
584 SendSSDPNotifies(int s, const char * host, unsigned short http_port,
585 unsigned short https_port,
586 unsigned int lifetime, int ipv6)
587 #else
588 static void
589 SendSSDPNotifies(int s, const char * host, unsigned short http_port,
590 unsigned int lifetime, int ipv6)
591 #endif
593 #ifdef ENABLE_IPV6
594 struct sockaddr_storage sockname;
595 static struct { const char * p1, * p2; } const mcast_addrs[] =
596 { { LL_SSDP_MCAST_ADDR, "[" LL_SSDP_MCAST_ADDR "]" }, /* Link Local */
597 { SL_SSDP_MCAST_ADDR, "[" SL_SSDP_MCAST_ADDR "]" }, /* Site Local */
598 { GL_SSDP_MCAST_ADDR, "[" GL_SSDP_MCAST_ADDR "]" }, /* Global */
599 { NULL, NULL } };
600 int j;
601 #else /* ENABLE_IPV6 */
602 struct sockaddr_in sockname;
603 #endif /* ENABLE_IPV6 */
604 socklen_t sockname_len;
605 const char * dest_str;
606 int i;
607 char ver_str[4];
608 #ifndef ENABLE_IPV6
609 UNUSED(ipv6);
610 #endif /* ENABLE_IPV6 */
612 memset(&sockname, 0, sizeof(sockname));
613 #ifdef ENABLE_IPV6
614 /* first iterate destinations for this LAN interface (only 1 for IPv4) */
615 for(j = 0; (mcast_addrs[j].p1 != 0 && ipv6) || j < 1; j++) {
616 if(ipv6) {
617 struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockname;
618 sockname_len = sizeof(struct sockaddr_in6);
619 p->sin6_family = AF_INET6;
620 p->sin6_port = htons(SSDP_PORT);
621 inet_pton(AF_INET6, mcast_addrs[j].p1, &(p->sin6_addr));
622 dest_str = mcast_addrs[j].p2;
623 /* UPnP Device Architecture 1.1 :
624 * Devices MUST multicast SSDP messages for each of the UPnP-enabled
625 * interfaces. The scope of multicast SSDP messages MUST be
626 * link local FF02::C if the message is sent from a link local address.
627 * If the message is sent from a global address it MUST be multicast
628 * using either global scope FF0E::C or site local scope FF05::C.
629 * In networks with complex topologies and overlapping sites, use of
630 * global scope is RECOMMENDED. */
631 } else {
632 #else /* ENABLE_IPV6 */
634 #endif /* ENABLE_IPV6 */
635 /* IPv4 */
636 struct sockaddr_in *p = (struct sockaddr_in *)&sockname;
637 sockname_len = sizeof(struct sockaddr_in);
638 p->sin_family = AF_INET;
639 p->sin_port = htons(SSDP_PORT);
640 p->sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
641 dest_str = SSDP_MCAST_ADDR;
644 /* iterate all services / devices */
645 for(i = 0; known_service_types[i].s; i++) {
646 if(i==0)
647 ver_str[0] = '\0';
648 else
649 snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version);
650 SendSSDPNotify(s, (struct sockaddr *)&sockname, sockname_len, dest_str,
651 host, http_port,
652 #ifdef ENABLE_HTTPS
653 https_port,
654 #endif
655 known_service_types[i].s, ver_str, /* NT: */
656 known_service_types[i].uuid, "::",
657 known_service_types[i].s, /* ver_str, USN: */
658 lifetime);
659 /* for devices, also send NOTIFY on the uuid */
660 if(0==memcmp(known_service_types[i].s,
661 "urn:schemas-upnp-org:device", sizeof("urn:schemas-upnp-org:device")-1)) {
662 SendSSDPNotify(s, (struct sockaddr *)&sockname, sockname_len, dest_str,
663 host, http_port,
664 #ifdef ENABLE_HTTPS
665 https_port,
666 #endif
667 known_service_types[i].uuid, "", /* NT: */
668 known_service_types[i].uuid, "", "", /* ver_str, USN: */
669 lifetime);
671 } /* for(i = 0; known_service_types[i].s; i++) */
672 #ifdef ENABLE_IPV6
673 } /* for(j = 0; (mcast_addrs[j].p1 != 0 && ipv6) || j < 1; j++) */
674 #endif /* ENABLE_IPV6 */
677 /* SendSSDPNotifies2() sends SSDP NOTIFY packets on all interfaces
678 * for all destinations, all devices / services */
679 void
680 SendSSDPNotifies2(int * sockets,
681 unsigned short http_port,
682 #ifdef ENABLE_HTTPS
683 unsigned short https_port,
684 #endif
685 unsigned int lifetime)
687 int i;
688 struct lan_addr_s * lan_addr;
689 for(i = 0, lan_addr = lan_addrs.lh_first;
690 lan_addr != NULL;
691 lan_addr = lan_addr->list.le_next) {
692 SendSSDPNotifies(sockets[i], lan_addr->str, http_port,
693 #ifdef ENABLE_HTTPS
694 https_port,
695 #endif
696 lifetime, 0);
697 i++;
698 #ifdef ENABLE_IPV6
699 if(sockets[i] >= 0) {
700 SendSSDPNotifies(sockets[i], ipv6_addr_for_http_with_brackets, http_port,
701 #ifdef ENABLE_HTTPS
702 https_port,
703 #endif
704 lifetime, 1);
706 i++;
707 #endif /* ENABLE_IPV6 */
711 /* ProcessSSDPRequest()
712 * process SSDP M-SEARCH requests and responds to them */
713 void
714 #ifdef ENABLE_HTTPS
715 ProcessSSDPRequest(int s, unsigned short http_port, unsigned short https_port)
716 #else
717 ProcessSSDPRequest(int s, unsigned short http_port)
718 #endif
720 int n;
721 char bufr[1500];
722 socklen_t len_r;
723 #ifdef ENABLE_IPV6
724 struct sockaddr_storage sendername;
725 len_r = sizeof(struct sockaddr_storage);
726 #else
727 struct sockaddr_in sendername;
728 len_r = sizeof(struct sockaddr_in);
729 #endif
731 n = recvfrom(s, bufr, sizeof(bufr), 0,
732 (struct sockaddr *)&sendername, &len_r);
733 if(n < 0)
735 /* EAGAIN, EWOULDBLOCK, EINTR : silently ignore (try again next time)
736 * other errors : log to LOG_ERR */
737 if(errno != EAGAIN &&
738 errno != EWOULDBLOCK &&
739 errno != EINTR)
741 syslog(LOG_ERR, "recvfrom(udp): %m");
743 return;
745 #ifdef ENABLE_HTTPS
746 ProcessSSDPData(s, bufr, n, (struct sockaddr *)&sendername,
747 http_port, https_port);
748 #else
749 ProcessSSDPData(s, bufr, n, (struct sockaddr *)&sendername,
750 http_port);
751 #endif
755 #ifdef ENABLE_HTTPS
756 void
757 ProcessSSDPData(int s, const char *bufr, int n,
758 const struct sockaddr * sender,
759 unsigned short http_port, unsigned short https_port)
760 #else
761 void
762 ProcessSSDPData(int s, const char *bufr, int n,
763 const struct sockaddr * sender,
764 unsigned short http_port)
765 #endif
767 int i, l;
768 struct lan_addr_s * lan_addr = NULL;
769 const char * st = NULL;
770 int st_len = 0;
771 int st_ver = 0;
772 char sender_str[64];
773 char ver_str[4];
774 const char * announced_host = NULL;
775 #ifdef UPNP_STRICT
776 #ifdef ENABLE_IPV6
777 char announced_host_buf[64];
778 #endif
779 #endif
780 #if defined(UPNP_STRICT) || defined(DELAY_MSEARCH_RESPONSE)
781 int mx_value = -1;
782 #endif
783 unsigned int delay = 50; /* Non-zero default delay to prevent flooding */
784 /* UPnP Device Architecture v1.1. 1.3.3 Search response :
785 * Devices responding to a multicast M-SEARCH SHOULD wait a random period
786 * of time between 0 seconds and the number of seconds specified in the
787 * MX field value of the search request before responding, in order to
788 * avoid flooding the requesting control point with search responses
789 * from multiple devices. If the search request results in the need for
790 * a multiple part response from the device, those multiple part
791 * responses SHOULD be spread at random intervals through the time period
792 * from 0 to the number of seconds specified in the MX header field. */
794 /* get the string representation of the sender address */
795 sockaddr_to_string(sender, sender_str, sizeof(sender_str));
796 lan_addr = get_lan_for_peer(sender);
797 if(lan_addr == NULL)
799 syslog(LOG_WARNING, "SSDP packet sender %s not from a LAN, ignoring",
800 sender_str);
801 return;
804 if(memcmp(bufr, "NOTIFY", 6) == 0)
806 /* ignore NOTIFY packets. We could log the sender and device type */
807 return;
809 else if(memcmp(bufr, "M-SEARCH", 8) == 0)
811 i = 0;
812 while(i < n)
814 while((i < n - 1) && (bufr[i] != '\r' || bufr[i+1] != '\n'))
815 i++;
816 i += 2;
817 if((i < n - 3) && (strncasecmp(bufr+i, "st:", 3) == 0))
819 st = bufr+i+3;
820 st_len = 0;
821 while((*st == ' ' || *st == '\t') && (st < bufr + n))
822 st++;
823 while(st[st_len]!='\r' && st[st_len]!='\n'
824 && (st + st_len < bufr + n))
825 st_len++;
826 l = st_len;
827 while(l > 0 && st[l-1] != ':')
828 l--;
829 st_ver = atoi(st+l);
830 syslog(LOG_DEBUG, "ST: %.*s (ver=%d)", st_len, st, st_ver);
831 /*j = 0;*/
832 /*while(bufr[i+j]!='\r') j++;*/
833 /*syslog(LOG_INFO, "%.*s", j, bufr+i);*/
835 #if defined(UPNP_STRICT) || defined(DELAY_MSEARCH_RESPONSE)
836 else if((i < n - 3) && (strncasecmp(bufr+i, "mx:", 3) == 0))
838 const char * mx;
839 int mx_len;
840 mx = bufr+i+3;
841 mx_len = 0;
842 while((*mx == ' ' || *mx == '\t') && (mx < bufr + n))
843 mx++;
844 while(mx[mx_len]!='\r' && mx[mx_len]!='\n'
845 && (mx + mx_len < bufr + n))
846 mx_len++;
847 mx_value = atoi(mx);
848 syslog(LOG_DEBUG, "MX: %.*s (value=%d)", mx_len, mx, mx_value);
850 #endif
852 #ifdef UPNP_STRICT
853 /* For multicast M-SEARCH requests, if the search request does
854 * not contain an MX header field, the device MUST silently
855 * discard and ignore the search request. */
856 if(mx_value < 0) {
857 syslog(LOG_INFO, "ignoring SSDP packet missing MX: header");
858 return;
859 } else if(mx_value > 5) {
860 /* If the MX header field specifies a field value greater
861 * than 5, the device SHOULD assume that it contained the
862 * value 5 or less. */
863 mx_value = 5;
865 #elif defined(DELAY_MSEARCH_RESPONSE)
866 if(mx_value < 0) {
867 mx_value = 1;
868 } else if(mx_value > 5) {
869 /* If the MX header field specifies a field value greater
870 * than 5, the device SHOULD assume that it contained the
871 * value 5 or less. */
872 mx_value = 5;
874 #endif
875 /*syslog(LOG_INFO, "SSDP M-SEARCH packet received from %s",
876 sender_str );*/
877 if(st && (st_len > 0))
879 syslog(LOG_INFO, "SSDP M-SEARCH from %s ST: %.*s",
880 sender_str, st_len, st);
881 /* find in which sub network the client is */
882 if(sender->sa_family == AF_INET)
884 if (lan_addr == NULL)
886 syslog(LOG_ERR,
887 "Can't find in which sub network the client %s is",
888 sender_str);
889 return;
891 announced_host = lan_addr->str;
893 #ifdef ENABLE_IPV6
894 else
896 /* IPv6 address with brackets */
897 #ifdef UPNP_STRICT
898 int index;
899 struct in6_addr addr6;
900 size_t addr6_len = sizeof(addr6);
901 /* retrieve the IPv6 address which
902 * will be used locally to reach sender */
903 memset(&addr6, 0, sizeof(addr6));
904 if(get_src_for_route_to (sender, &addr6, &addr6_len, &index) < 0) {
905 syslog(LOG_WARNING, "get_src_for_route_to() failed, using %s", ipv6_addr_for_http_with_brackets);
906 announced_host = ipv6_addr_for_http_with_brackets;
907 } else {
908 if(inet_ntop(AF_INET6, &addr6,
909 announced_host_buf+1,
910 sizeof(announced_host_buf) - 2)) {
911 announced_host_buf[0] = '[';
912 i = strlen(announced_host_buf);
913 if(i < (int)sizeof(announced_host_buf) - 1) {
914 announced_host_buf[i] = ']';
915 announced_host_buf[i+1] = '\0';
916 } else {
917 syslog(LOG_NOTICE, "cannot suffix %s with ']'",
918 announced_host_buf);
920 announced_host = announced_host_buf;
921 } else {
922 syslog(LOG_NOTICE, "inet_ntop() failed %m");
923 announced_host = ipv6_addr_for_http_with_brackets;
926 #else
927 announced_host = ipv6_addr_for_http_with_brackets;
928 #endif
930 #endif
931 /* Responds to request with a device as ST header */
932 for(i = 0; known_service_types[i].s; i++)
934 l = (int)strlen(known_service_types[i].s);
935 if(l<=st_len && (0 == memcmp(st, known_service_types[i].s, l))
936 #ifdef UPNP_STRICT
937 && (st_ver <= known_service_types[i].version)
938 /* only answer for service version lower or equal of supported one */
939 #endif
942 /* SSDP_RESPOND_SAME_VERSION :
943 * response is urn:schemas-upnp-org:service:WANIPConnection:1 when
944 * M-SEARCH included urn:schemas-upnp-org:service:WANIPConnection:1
945 * else the implemented versions is included in the response
947 * From UPnP Device Architecture v1.1 :
948 * 1.3.2 [...] Updated versions of device and service types
949 * are REQUIRED to be fully backward compatible with
950 * previous versions. Devices MUST respond to M-SEARCH
951 * requests for any supported version. For example, if a
952 * device implements “urn:schemas-upnporg:service:xyz:2”,
953 * it MUST respond to search requests for both that type
954 * and “urn:schemas-upnp-org:service:xyz:1”. The response
955 * MUST specify the same version as was contained in the
956 * search request. [...] */
957 #ifndef SSDP_RESPOND_SAME_VERSION
958 if(i==0)
959 ver_str[0] = '\0';
960 else
961 snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version);
962 #endif
963 syslog(LOG_INFO, "Single search found");
964 #ifdef DELAY_MSEARCH_RESPONSE
965 delay = random() / (1 + RAND_MAX / (1000 * mx_value));
966 #ifdef DEBUG
967 syslog(LOG_DEBUG, "mx=%dsec delay=%ums", mx_value, delay);
968 #endif
969 #endif
970 SendSSDPResponse(s, sender,
971 #ifdef SSDP_RESPOND_SAME_VERSION
972 st, st_len, "",
973 #else
974 known_service_types[i].s, l, ver_str,
975 #endif
976 announced_host, http_port,
977 #ifdef ENABLE_HTTPS
978 https_port,
979 #endif
980 known_service_types[i].uuid,
981 delay);
982 break;
985 /* Responds to request with ST: ssdp:all */
986 /* strlen("ssdp:all") == 8 */
987 if(st_len==8 && (0 == memcmp(st, "ssdp:all", 8)))
989 #ifdef DELAY_MSEARCH_RESPONSE
990 unsigned int delay_increment = (mx_value * 1000) / 15;
991 #endif
992 syslog(LOG_INFO, "ssdp:all found");
993 for(i=0; known_service_types[i].s; i++)
995 #ifdef DELAY_MSEARCH_RESPONSE
996 delay += delay_increment;
997 #endif
998 if(i==0)
999 ver_str[0] = '\0';
1000 else
1001 snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version);
1002 l = (int)strlen(known_service_types[i].s);
1003 SendSSDPResponse(s, sender,
1004 known_service_types[i].s, l, ver_str,
1005 announced_host, http_port,
1006 #ifdef ENABLE_HTTPS
1007 https_port,
1008 #endif
1009 known_service_types[i].uuid,
1010 delay);
1012 /* also answer for uuid */
1013 #ifdef DELAY_MSEARCH_RESPONSE
1014 delay += delay_increment;
1015 #endif
1016 SendSSDPResponse(s, sender, uuidvalue_igd, strlen(uuidvalue_igd), "",
1017 announced_host, http_port,
1018 #ifdef ENABLE_HTTPS
1019 https_port,
1020 #endif
1021 uuidvalue_igd, delay);
1022 #ifdef DELAY_MSEARCH_RESPONSE
1023 delay += delay_increment;
1024 #endif
1025 SendSSDPResponse(s, sender, uuidvalue_wan, strlen(uuidvalue_wan), "",
1026 announced_host, http_port,
1027 #ifdef ENABLE_HTTPS
1028 https_port,
1029 #endif
1030 uuidvalue_wan, delay);
1031 #ifdef DELAY_MSEARCH_RESPONSE
1032 delay += delay_increment;
1033 #endif
1034 SendSSDPResponse(s, sender, uuidvalue_wcd, strlen(uuidvalue_wcd), "",
1035 announced_host, http_port,
1036 #ifdef ENABLE_HTTPS
1037 https_port,
1038 #endif
1039 uuidvalue_wcd, delay);
1041 /* responds to request by UUID value */
1042 l = (int)strlen(uuidvalue_igd);
1043 if(l==st_len)
1045 #ifdef DELAY_MSEARCH_RESPONSE
1046 delay = random() / (1 + RAND_MAX / (1000 * mx_value));
1047 #endif
1048 if(0 == memcmp(st, uuidvalue_igd, l))
1050 syslog(LOG_INFO, "ssdp:uuid (IGD) found");
1051 SendSSDPResponse(s, sender, st, st_len, "",
1052 announced_host, http_port,
1053 #ifdef ENABLE_HTTPS
1054 https_port,
1055 #endif
1056 uuidvalue_igd, delay);
1058 else if(0 == memcmp(st, uuidvalue_wan, l))
1060 syslog(LOG_INFO, "ssdp:uuid (WAN) found");
1061 SendSSDPResponse(s, sender, st, st_len, "",
1062 announced_host, http_port,
1063 #ifdef ENABLE_HTTPS
1064 https_port,
1065 #endif
1066 uuidvalue_wan, delay);
1068 else if(0 == memcmp(st, uuidvalue_wcd, l))
1070 syslog(LOG_INFO, "ssdp:uuid (WCD) found");
1071 SendSSDPResponse(s, sender, st, st_len, "",
1072 announced_host, http_port,
1073 #ifdef ENABLE_HTTPS
1074 https_port,
1075 #endif
1076 uuidvalue_wcd, delay);
1080 else
1082 syslog(LOG_INFO, "Invalid SSDP M-SEARCH from %s", sender_str);
1085 else
1087 syslog(LOG_NOTICE, "Unknown udp packet received from %s", sender_str);
1091 static int
1092 SendSSDPbyebye(int s, const struct sockaddr * dest, socklen_t destlen,
1093 const char * dest_str,
1094 const char * nt, const char * suffix,
1095 const char * usn1, const char * usn2, const char * usn3)
1097 int n, l;
1098 char bufr[SSDP_PACKET_MAX_LEN];
1100 l = snprintf(bufr, sizeof(bufr),
1101 "NOTIFY * HTTP/1.1\r\n"
1102 "HOST: %s:%d\r\n"
1103 "NT: %s%s\r\n"
1104 "USN: %s%s%s%s\r\n"
1105 "NTS: ssdp:byebye\r\n"
1106 "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */
1107 "01-NLS: %u\r\n" /* same as BOOTID field. UDA v1.1 */
1108 "BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
1109 "CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
1110 "\r\n",
1111 dest_str, SSDP_PORT, /* HOST : */
1112 nt, suffix, /* NT: */
1113 usn1, usn2, usn3, suffix, /* USN: */
1114 upnp_bootid, upnp_bootid, upnp_configid);
1115 if(l<0)
1117 syslog(LOG_ERR, "%s: snprintf error", "SendSSDPbyebye()");
1118 return -1;
1120 else if((unsigned int)l >= sizeof(bufr))
1122 syslog(LOG_WARNING, "%s: truncated output (%u>=%u)",
1123 "SendSSDPbyebye()", (unsigned)l, (unsigned)sizeof(bufr));
1124 l = sizeof(bufr) - 1;
1126 n = sendto_or_schedule(s, bufr, l, 0, dest, destlen);
1127 if(n < 0)
1129 syslog(LOG_ERR, "sendto(udp_shutdown=%d): %m", s);
1130 return -1;
1132 else if(n != l)
1134 syslog(LOG_NOTICE, "sendto() sent %d out of %d bytes", n, l);
1135 return -1;
1137 return 0;
1140 /* This will broadcast ssdp:byebye notifications to inform
1141 * the network that UPnP is going down. */
1143 SendSSDPGoodbye(int * sockets, int n_sockets)
1145 struct sockaddr_in sockname4;
1146 #ifdef ENABLE_IPV6
1147 struct sockaddr_in6 sockname6;
1148 struct sockaddr * sockname;
1149 socklen_t socknamelen;
1150 int ipv6 = 0;
1151 #endif
1152 int i, j;
1153 char ver_str[4];
1154 int ret = 0;
1155 const char * dest_str;
1157 memset(&sockname4, 0, sizeof(struct sockaddr_in));
1158 sockname4.sin_family = AF_INET;
1159 sockname4.sin_port = htons(SSDP_PORT);
1160 sockname4.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
1161 #ifdef ENABLE_IPV6
1162 memset(&sockname6, 0, sizeof(struct sockaddr_in6));
1163 sockname6.sin6_family = AF_INET6;
1164 sockname6.sin6_port = htons(SSDP_PORT);
1165 inet_pton(AF_INET6, LL_SSDP_MCAST_ADDR, &(sockname6.sin6_addr));
1166 #else
1167 dest_str = SSDP_MCAST_ADDR;
1168 #endif
1170 for(j=0; j<n_sockets; j++)
1172 if(sockets[j] < 0)
1173 continue;
1174 #ifdef ENABLE_IPV6
1175 ipv6 = j & 1;
1176 if(ipv6) {
1177 dest_str = "[" LL_SSDP_MCAST_ADDR "]";
1178 sockname = (struct sockaddr *)&sockname6;
1179 socknamelen = sizeof(struct sockaddr_in6);
1180 } else {
1181 dest_str = SSDP_MCAST_ADDR;
1182 sockname = (struct sockaddr *)&sockname4;
1183 socknamelen = sizeof(struct sockaddr_in);
1185 #endif
1186 for(i=0; known_service_types[i].s; i++)
1188 if(i==0)
1189 ver_str[0] = '\0';
1190 else
1191 snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version);
1192 ret += SendSSDPbyebye(sockets[j],
1193 #ifdef ENABLE_IPV6
1194 sockname, socknamelen,
1195 #else
1196 (struct sockaddr *)&sockname4, sizeof(struct sockaddr_in),
1197 #endif
1198 dest_str,
1199 known_service_types[i].s, ver_str, /* NT: */
1200 known_service_types[i].uuid, "::",
1201 known_service_types[i].s); /* ver_str, USN: */
1202 if(0==memcmp(known_service_types[i].s,
1203 "urn:schemas-upnp-org:device", sizeof("urn:schemas-upnp-org:device")-1))
1205 ret += SendSSDPbyebye(sockets[j],
1206 #ifdef ENABLE_IPV6
1207 sockname, socknamelen,
1208 #else
1209 (struct sockaddr *)&sockname4, sizeof(struct sockaddr_in),
1210 #endif
1211 dest_str,
1212 known_service_types[i].uuid, "", /* NT: */
1213 known_service_types[i].uuid, "", ""); /* ver_str, USN: */
1217 return ret;
1220 /* SubmitServicesToMiniSSDPD() :
1221 * register services offered by MiniUPnPd to a running instance of
1222 * MiniSSDPd */
1224 SubmitServicesToMiniSSDPD(const char * host, unsigned short port) {
1225 struct sockaddr_un addr;
1226 int s;
1227 unsigned char buffer[2048];
1228 char strbuf[256];
1229 unsigned char * p;
1230 int i, l, n;
1231 char ver_str[4];
1233 s = socket(AF_UNIX, SOCK_STREAM, 0);
1234 if(s < 0) {
1235 syslog(LOG_ERR, "socket(unix): %m");
1236 return -1;
1238 addr.sun_family = AF_UNIX;
1239 strncpy(addr.sun_path, minissdpdsocketpath, sizeof(addr.sun_path));
1240 if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) {
1241 syslog(LOG_ERR, "connect(\"%s\"): %m", minissdpdsocketpath);
1242 close(s);
1243 return -1;
1245 for(i = 0; known_service_types[i].s; i++) {
1246 buffer[0] = 4; /* request type 4 : submit service */
1247 /* 4 strings following : ST (service type), USN, Server, Location */
1248 p = buffer + 1;
1249 l = (int)strlen(known_service_types[i].s);
1250 if(i > 0)
1251 l++;
1252 CODELENGTH(l, p);
1253 memcpy(p, known_service_types[i].s, l);
1254 if(i > 0)
1255 p[l-1] = '1';
1256 p += l;
1257 if(i==0)
1258 ver_str[0] = '\0';
1259 else
1260 snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version);
1261 l = snprintf(strbuf, sizeof(strbuf), "%s::%s%s",
1262 known_service_types[i].uuid, known_service_types[i].s, ver_str);
1263 if(l<0) {
1264 syslog(LOG_WARNING, "SubmitServicesToMiniSSDPD: snprintf %m");
1265 continue;
1266 } else if((unsigned)l>=sizeof(strbuf)) {
1267 l = sizeof(strbuf) - 1;
1269 CODELENGTH(l, p);
1270 memcpy(p, strbuf, l);
1271 p += l;
1272 l = (int)strlen(MINIUPNPD_SERVER_STRING);
1273 CODELENGTH(l, p);
1274 memcpy(p, MINIUPNPD_SERVER_STRING, l);
1275 p += l;
1276 l = snprintf(strbuf, sizeof(strbuf), "http://%s:%u" ROOTDESC_PATH,
1277 host, (unsigned int)port);
1278 if(l<0) {
1279 syslog(LOG_WARNING, "SubmitServicesToMiniSSDPD: snprintf %m");
1280 continue;
1281 } else if((unsigned)l>=sizeof(strbuf)) {
1282 l = sizeof(strbuf) - 1;
1284 CODELENGTH(l, p);
1285 memcpy(p, strbuf, l);
1286 p += l;
1287 /* now write the encoded data */
1288 n = p - buffer; /* bytes to send */
1289 p = buffer; /* start */
1290 while(n > 0) {
1291 l = write(s, p, n);
1292 if (l < 0) {
1293 if(errno == EINTR)
1294 continue;
1295 syslog(LOG_ERR, "write(): %m");
1296 close(s);
1297 return -1;
1298 } else if (l == 0) {
1299 syslog(LOG_ERR, "write() returned 0");
1300 close(s);
1301 return -1;
1303 p += l;
1304 n -= l;
1307 close(s);
1308 return 0;