1: /* $Id: minissdp.c,v 1.1.1.3 2013/07/22 00:32:35 misho 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 */
7:
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>
18:
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"
28:
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"
34:
35: /* AddMulticastMembership()
36: * param s socket
37: * param ifaddr ip v4 address
38: */
39: static int
40: AddMulticastMembership(int s, in_addr_t ifaddr)
41: {
42: struct ip_mreq imr; /* Ip multicast membership */
43:
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);*/
48:
49: if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&imr, sizeof(struct ip_mreq)) < 0)
50: {
51: syslog(LOG_ERR, "setsockopt(udp, IP_ADD_MEMBERSHIP): %m");
52: return -1;
53: }
54:
55: return 0;
56: }
57:
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)
64: {
65: struct ipv6_mreq mr;
66: /*unsigned int ifindex;*/
67:
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)
76: {
77: syslog(LOG_ERR, "setsockopt(udp, IPV6_ADD_MEMBERSHIP): %m");
78: return -1;
79: }
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)
82: {
83: syslog(LOG_ERR, "setsockopt(udp, IPV6_ADD_MEMBERSHIP): %m");
84: return -1;
85: }
86: return 0;
87: }
88: #endif
89:
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)
95: {
96: int s;
97: struct sockaddr_storage sockname;
98: socklen_t sockname_len;
99: struct lan_addr_s * lan_addr;
100: int j = 1;
101:
102: if( (s = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, 0)) < 0)
103: {
104: syslog(LOG_ERR, "%s: socket(udp): %m",
105: "OpenAndConfSSDPReceiveSocket");
106: return -1;
107: }
108:
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);
125: }
126:
127: if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &j, sizeof(j)) < 0)
128: {
129: syslog(LOG_WARNING, "setsockopt(udp, SO_REUSEADDR): %m");
130: }
131:
132: if(!set_non_blocking(s))
133: {
134: syslog(LOG_WARNING, "%s: set_non_blocking(): %m",
135: "OpenAndConfSSDPReceiveSocket");
136: }
137:
138: if(bind(s, (struct sockaddr *)&sockname, sockname_len) < 0)
139: {
140: syslog(LOG_ERR, "%s: bind(udp%s): %m",
141: "OpenAndConfSSDPReceiveSocket", ipv6 ? "6" : "");
142: close(s);
143: return -1;
144: }
145:
146: #ifdef ENABLE_IPV6
147: if(ipv6)
148: {
149: AddMulticastMembershipIPv6(s);
150: }
151: else
152: #endif
153: {
154: for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next)
155: {
156: if(AddMulticastMembership(s, lan_addr->addr.s_addr) < 0)
157: {
158: syslog(LOG_WARNING,
159: "Failed to add multicast membership for interface %s",
160: lan_addr->str ? lan_addr->str : "NULL");
161: }
162: }
163: }
164:
165: return s;
166: }
167:
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)
172: {
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;
182:
183: if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
184: {
185: syslog(LOG_ERR, "socket(udp_notify): %m");
186: return -1;
187: }
188:
189: mc_if.s_addr = addr; /*inet_addr(addr);*/
190:
191: if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loopchar, sizeof(loopchar)) < 0)
192: {
193: syslog(LOG_ERR, "setsockopt(udp_notify, IP_MULTICAST_LOOP): %m");
194: close(s);
195: return -1;
196: }
197:
198: if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&mc_if, sizeof(mc_if)) < 0)
199: {
200: syslog(LOG_ERR, "setsockopt(udp_notify, IP_MULTICAST_IF): %m");
201: close(s);
202: return -1;
203: }
204:
205: if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0)
206: {
207: syslog(LOG_WARNING, "setsockopt(udp_notify, IP_MULTICAST_TTL,): %m");
208: }
209:
210: if(setsockopt(s, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bcast)) < 0)
211: {
212: syslog(LOG_ERR, "setsockopt(udp_notify, SO_BROADCAST): %m");
213: close(s);
214: return -1;
215: }
216:
217: memset(&sockname, 0, sizeof(struct sockaddr_in));
218: sockname.sin_family = AF_INET;
219: sockname.sin_addr.s_addr = addr; /*inet_addr(addr);*/
220:
221: if (bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
222: {
223: syslog(LOG_ERR, "bind(udp_notify): %m");
224: close(s);
225: return -1;
226: }
227:
228: return s;
229: }
230:
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)
236: {
237: int s;
238: unsigned int loop = 0;
239:
240: s = socket(PF_INET6, SOCK_DGRAM, 0);
241: if(s < 0)
242: {
243: syslog(LOG_ERR, "socket(udp_notify IPv6): %m");
244: return -1;
245: }
246: if(setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &if_index, sizeof(if_index)) < 0)
247: {
248: syslog(LOG_ERR, "setsockopt(udp_notify IPv6, IPV6_MULTICAST_IF, %u): %m", if_index);
249: close(s);
250: return -1;
251: }
252: if(setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof(loop)) < 0)
253: {
254: syslog(LOG_ERR, "setsockopt(udp_notify, IPV6_MULTICAST_LOOP): %m");
255: close(s);
256: return -1;
257: }
258: return s;
259: }
260: #endif
261:
262: int
263: OpenAndConfSSDPNotifySockets(int * sockets)
264: /*OpenAndConfSSDPNotifySockets(int * sockets,
265: struct lan_addr_s * lan_addr, int n_lan_addr)*/
266: {
267: int i;
268: struct lan_addr_s * lan_addr;
269:
270: for(i=0, lan_addr = lan_addrs.lh_first;
271: lan_addr != NULL;
272: lan_addr = lan_addr->list.le_next)
273: {
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
284: }
285: return 0;
286: error:
287: while(--i >= 0)
288: {
289: close(sockets[i]);
290: sockets[i] = -1;
291: }
292: return -1;
293: }
294:
295: /*
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
305:
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:
314: */
315:
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)
322: {
323: int l, n;
324: char buf[512];
325: char addr_str[64];
326: socklen_t addrlen;
327: /*
328: * follow guideline from document "UPnP Device Architecture 1.0"
329: * uppercase is recommended.
330: * DATE: is recommended
331: * SERVER: OS/ver UPnP/1.0 miniupnpd/1.0
332: * - check what to put in the 'Cache-Control' header
333: *
334: * have a look at the document "UPnP Device Architecture v1.1 */
335: l = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\n"
336: "CACHE-CONTROL: max-age=120\r\n"
337: /*"DATE: ...\r\n"*/
338: "ST: %.*s%s\r\n"
339: "USN: %s::%.*s%s\r\n"
340: "EXT:\r\n"
341: "SERVER: " MINIUPNPD_SERVER_STRING "\r\n"
342: "LOCATION: http://%s:%u" ROOTDESC_PATH "\r\n"
343: "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */
344: "01-NLS: %u\r\n" /* same as BOOTID. UDA v1.1 */
345: "BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
346: "CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
347: "\r\n",
348: st_len, st, suffix,
349: uuidvalue, st_len, st, suffix,
350: host, (unsigned int)port,
351: upnp_bootid, upnp_bootid, upnp_configid);
352: addrlen = (addr->sa_family == AF_INET6)
353: ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
354: n = sendto(s, buf, l, 0,
355: addr, addrlen);
356: sockaddr_to_string(addr, addr_str, sizeof(addr_str));
357: syslog(LOG_INFO, "SSDP Announce %d bytes to %s ST: %.*s",n,
358: addr_str,
359: l, buf);
360: if(n < 0)
361: {
362: /* XXX handle EINTR, EAGAIN, EWOULDBLOCK */
363: syslog(LOG_ERR, "sendto(udp): %m");
364: }
365: }
366:
367: #ifndef IGD_V2
368: #define IGD_VER 1
369: #define WANIPC_VER 1
370: #else
371: #define IGD_VER 2
372: #define WANIPC_VER 2
373: #endif
374:
375: static struct {
376: const char * s;
377: const int version;
378: } const known_service_types[] =
379: {
380: {"upnp:rootdevice", 0},
381: {"urn:schemas-upnp-org:device:InternetGatewayDevice:", IGD_VER},
382: {"urn:schemas-upnp-org:device:WANConnectionDevice:", 1},
383: {"urn:schemas-upnp-org:device:WANDevice:", 1},
384: {"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:", 1},
385: {"urn:schemas-upnp-org:service:WANIPConnection:", WANIPC_VER},
386: {"urn:schemas-upnp-org:service:WANPPPConnection:", 1},
387: #ifdef ENABLE_L3F_SERVICE
388: {"urn:schemas-upnp-org:service:Layer3Forwarding:", 1},
389: #endif
390: #ifdef ENABLE_6FC_SERVICE
391: {"url:schemas-upnp-org:service:WANIPv6FirewallControl:", 1},
392: #endif
393: {0, 0}
394: };
395:
396: static void
397: SendSSDPNotifies(int s, const char * host, unsigned short port,
398: unsigned int lifetime, int ipv6)
399: {
400: #ifdef ENABLE_IPV6
401: struct sockaddr_storage sockname;
402: #else
403: struct sockaddr_in sockname;
404: #endif
405: int l, n, i=0;
406: char bufr[512];
407: char ver_str[4];
408:
409: memset(&sockname, 0, sizeof(sockname));
410: #ifdef ENABLE_IPV6
411: if(ipv6)
412: {
413: struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockname;
414: p->sin6_family = AF_INET6;
415: p->sin6_port = htons(SSDP_PORT);
416: inet_pton(AF_INET6, LL_SSDP_MCAST_ADDR, &(p->sin6_addr));
417: }
418: else
419: #endif
420: {
421: struct sockaddr_in *p = (struct sockaddr_in *)&sockname;
422: p->sin_family = AF_INET;
423: p->sin_port = htons(SSDP_PORT);
424: p->sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
425: }
426:
427: while(known_service_types[i].s)
428: {
429: if(i==0)
430: ver_str[0] = '\0';
431: else
432: snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version);
433: l = snprintf(bufr, sizeof(bufr),
434: "NOTIFY * HTTP/1.1\r\n"
435: "HOST: %s:%d\r\n"
436: "CACHE-CONTROL: max-age=%u\r\n"
437: "lOCATION: http://%s:%d" ROOTDESC_PATH"\r\n"
438: "SERVER: " MINIUPNPD_SERVER_STRING "\r\n"
439: "NT: %s%s\r\n"
440: "USN: %s::%s%s\r\n"
441: "NTS: ssdp:alive\r\n"
442: "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */
443: "01-NLS: %u\r\n" /* same as BOOTID field. UDA v1.1 */
444: "BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
445: "CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
446: "\r\n",
447: ipv6 ? "[" LL_SSDP_MCAST_ADDR "]" : SSDP_MCAST_ADDR,
448: SSDP_PORT,
449: lifetime,
450: host, port,
451: known_service_types[i].s, ver_str,
452: uuidvalue, known_service_types[i].s, ver_str,
453: upnp_bootid, upnp_bootid, upnp_configid );
454: if(l<0)
455: {
456: syslog(LOG_ERR, "SendSSDPNotifies() snprintf error");
457: continue;
458: }
459: if((unsigned int)l >= sizeof(bufr))
460: {
461: syslog(LOG_WARNING, "SendSSDPNotifies(): truncated output");
462: l = sizeof(bufr);
463: }
464: n = sendto(s, bufr, l, 0,
465: (struct sockaddr *)&sockname,
466: #ifdef ENABLE_IPV6
467: ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)
468: #else
469: sizeof(struct sockaddr_in)
470: #endif
471: );
472: if(n < 0)
473: {
474: /* XXX handle EINTR, EAGAIN, EWOULDBLOCK */
475: syslog(LOG_ERR, "sendto(udp_notify=%d, %s): %m", s,
476: host ? host : "NULL");
477: }
478: i++;
479: }
480: }
481:
482: void
483: SendSSDPNotifies2(int * sockets,
484: unsigned short port,
485: unsigned int lifetime)
486: {
487: int i;
488: struct lan_addr_s * lan_addr;
489: for(i=0, lan_addr = lan_addrs.lh_first;
490: lan_addr != NULL;
491: lan_addr = lan_addr->list.le_next)
492: {
493: SendSSDPNotifies(sockets[i], lan_addr->str, port,
494: lifetime, 0);
495: i++;
496: #ifdef ENABLE_IPV6
497: SendSSDPNotifies(sockets[i], ipv6_addr_for_http_with_brackets, port,
498: lifetime, 1);
499: i++;
500: #endif
501: }
502: }
503:
504: /* ProcessSSDPRequest()
505: * process SSDP M-SEARCH requests and responds to them */
506: void
507: ProcessSSDPRequest(int s, unsigned short port)
508: {
509: int n;
510: char bufr[1500];
511: socklen_t len_r;
512: #ifdef ENABLE_IPV6
513: struct sockaddr_storage sendername;
514: len_r = sizeof(struct sockaddr_storage);
515: #else
516: struct sockaddr_in sendername;
517: len_r = sizeof(struct sockaddr_in);
518: #endif
519:
520: n = recvfrom(s, bufr, sizeof(bufr), 0,
521: (struct sockaddr *)&sendername, &len_r);
522: if(n < 0)
523: {
524: /* EAGAIN, EWOULDBLOCK, EINTR : silently ignore (try again next time)
525: * other errors : log to LOG_ERR */
526: if(errno != EAGAIN &&
527: errno != EWOULDBLOCK &&
528: errno != EINTR)
529: {
530: syslog(LOG_ERR, "recvfrom(udp): %m");
531: }
532: return;
533: }
534: ProcessSSDPData(s, bufr, n, (struct sockaddr *)&sendername, port);
535:
536: }
537:
538: void
539: ProcessSSDPData(int s, const char *bufr, int n,
540: const struct sockaddr * sender, unsigned short port) {
541: int i, l;
542: struct lan_addr_s * lan_addr = NULL;
543: const char * st = NULL;
544: int st_len = 0;
545: int st_ver = 0;
546: char sender_str[64];
547: char ver_str[4];
548: const char * announced_host = NULL;
549: #ifdef ENABLE_IPV6
550: #ifdef UPNP_STRICT
551: char announced_host_buf[64];
552: #endif
553: #endif
554:
555: /* get the string representation of the sender address */
556: sockaddr_to_string(sender, sender_str, sizeof(sender_str));
557: lan_addr = get_lan_for_peer(sender);
558: if(lan_addr == NULL)
559: {
560: syslog(LOG_WARNING, "SSDP packet sender %s not from a LAN, ignoring",
561: sender_str);
562: return;
563: }
564:
565: if(memcmp(bufr, "NOTIFY", 6) == 0)
566: {
567: /* ignore NOTIFY packets. We could log the sender and device type */
568: return;
569: }
570: else if(memcmp(bufr, "M-SEARCH", 8) == 0)
571: {
572: i = 0;
573: while(i < n)
574: {
575: while((i < n - 1) && (bufr[i] != '\r' || bufr[i+1] != '\n'))
576: i++;
577: i += 2;
578: if((i < n - 3) && (strncasecmp(bufr+i, "st:", 3) == 0))
579: {
580: st = bufr+i+3;
581: st_len = 0;
582: while((*st == ' ' || *st == '\t') && (st < bufr + n))
583: st++;
584: while(st[st_len]!='\r' && st[st_len]!='\n'
585: && (st + st_len < bufr + n))
586: st_len++;
587: l = st_len;
588: while(l > 0 && st[l-1] != ':')
589: l--;
590: st_ver = atoi(st+l);
591: syslog(LOG_DEBUG, "ST: %.*s (ver=%d)", st_len, st, st_ver);
592: /*j = 0;*/
593: /*while(bufr[i+j]!='\r') j++;*/
594: /*syslog(LOG_INFO, "%.*s", j, bufr+i);*/
595: }
596: }
597: /*syslog(LOG_INFO, "SSDP M-SEARCH packet received from %s",
598: sender_str );*/
599: if(st && (st_len > 0))
600: {
601: /* TODO : doesnt answer at once but wait for a random time */
602: syslog(LOG_INFO, "SSDP M-SEARCH from %s ST: %.*s",
603: sender_str, st_len, st);
604: /* find in which sub network the client is */
605: if(sender->sa_family == AF_INET)
606: {
607: if (lan_addr == NULL)
608: {
609: syslog(LOG_ERR, "Can't find in which sub network the client is");
610: return;
611: }
612: announced_host = lan_addr->str;
613: }
614: #ifdef ENABLE_IPV6
615: else
616: {
617: /* IPv6 address with brackets */
618: #ifdef UPNP_STRICT
619: int index;
620: struct in6_addr addr6;
621: size_t addr6_len = sizeof(addr6);
622: /* retrieve the IPv6 address which
623: * will be used locally to reach sender */
624: memset(&addr6, 0, sizeof(addr6));
625: if(get_src_for_route_to (sender, &addr6, &addr6_len, &index) < 0) {
626: syslog(LOG_WARNING, "get_src_for_route_to() failed, using %s", ipv6_addr_for_http_with_brackets);
627: announced_host = ipv6_addr_for_http_with_brackets;
628: } else {
629: if(inet_ntop(AF_INET6, &addr6,
630: announced_host_buf+1,
631: sizeof(announced_host_buf) - 2)) {
632: announced_host_buf[0] = '[';
633: i = strlen(announced_host_buf);
634: if(i < (int)sizeof(announced_host_buf) - 1) {
635: announced_host_buf[i] = ']';
636: announced_host_buf[i+1] = '\0';
637: } else {
638: syslog(LOG_NOTICE, "cannot suffix %s with ']'",
639: announced_host_buf);
640: }
641: announced_host = announced_host_buf;
642: } else {
643: syslog(LOG_NOTICE, "inet_ntop() failed %m");
644: announced_host = ipv6_addr_for_http_with_brackets;
645: }
646: }
647: #else
648: announced_host = ipv6_addr_for_http_with_brackets;
649: #endif
650: }
651: #endif
652: /* Responds to request with a device as ST header */
653: for(i = 0; known_service_types[i].s; i++)
654: {
655: l = (int)strlen(known_service_types[i].s);
656: if(l<=st_len && (0 == memcmp(st, known_service_types[i].s, l))
657: #ifdef UPNP_STRICT
658: && (st_ver <= known_service_types[i].version)
659: /* only answer for service version lower or equal of supported one */
660: #endif
661: )
662: {
663: syslog(LOG_INFO, "Single search found");
664: SendSSDPAnnounce2(s, sender,
665: st, st_len, "",
666: announced_host, port);
667: break;
668: }
669: }
670: /* Responds to request with ST: ssdp:all */
671: /* strlen("ssdp:all") == 8 */
672: if(st_len==8 && (0 == memcmp(st, "ssdp:all", 8)))
673: {
674: syslog(LOG_INFO, "ssdp:all found");
675: for(i=0; known_service_types[i].s; i++)
676: {
677: if(i==0)
678: ver_str[0] = '\0';
679: else
680: snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version);
681: l = (int)strlen(known_service_types[i].s);
682: SendSSDPAnnounce2(s, sender,
683: known_service_types[i].s, l, ver_str,
684: announced_host, port);
685: }
686: /* also answer for uuid */
687: SendSSDPAnnounce2(s, sender, uuidvalue, strlen(uuidvalue), "",
688: announced_host, port);
689: }
690: /* responds to request by UUID value */
691: l = (int)strlen(uuidvalue);
692: if(l==st_len && (0 == memcmp(st, uuidvalue, l)))
693: {
694: syslog(LOG_INFO, "ssdp:uuid found");
695: SendSSDPAnnounce2(s, sender, st, st_len, "",
696: announced_host, port);
697: }
698: }
699: else
700: {
701: syslog(LOG_INFO, "Invalid SSDP M-SEARCH from %s", sender_str);
702: }
703: }
704: else
705: {
706: syslog(LOG_NOTICE, "Unknown udp packet received from %s", sender_str);
707: }
708: }
709:
710: /* This will broadcast ssdp:byebye notifications to inform
711: * the network that UPnP is going down. */
712: int
713: SendSSDPGoodbye(int * sockets, int n_sockets)
714: {
715: struct sockaddr_in sockname;
716: #ifdef ENABLE_IPV6
717: struct sockaddr_in6 sockname6;
718: #endif
719: int n, l;
720: int i, j;
721: char bufr[512];
722: char ver_str[4];
723: int ret = 0;
724: int ipv6 = 0;
725:
726: memset(&sockname, 0, sizeof(struct sockaddr_in));
727: sockname.sin_family = AF_INET;
728: sockname.sin_port = htons(SSDP_PORT);
729: sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
730: #ifdef ENABLE_IPV6
731: memset(&sockname6, 0, sizeof(struct sockaddr_in6));
732: sockname6.sin6_family = AF_INET6;
733: sockname6.sin6_port = htons(SSDP_PORT);
734: inet_pton(AF_INET6, LL_SSDP_MCAST_ADDR, &(sockname6.sin6_addr));
735: #endif
736:
737: for(j=0; j<n_sockets; j++)
738: {
739: #ifdef ENABLE_IPV6
740: ipv6 = j & 1;
741: #endif
742: for(i=0; known_service_types[i].s; i++)
743: {
744: if(i==0)
745: ver_str[0] = '\0';
746: else
747: snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version);
748: l = snprintf(bufr, sizeof(bufr),
749: "NOTIFY * HTTP/1.1\r\n"
750: "HOST: %s:%d\r\n"
751: "NT: %s%s\r\n"
752: "USN: %s::%s%s\r\n"
753: "NTS: ssdp:byebye\r\n"
754: "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */
755: "01-NLS: %u\r\n" /* same as BOOTID field. UDA v1.1 */
756: "BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
757: "CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
758: "\r\n",
759: ipv6 ? "[" LL_SSDP_MCAST_ADDR "]" : SSDP_MCAST_ADDR,
760: SSDP_PORT,
761: known_service_types[i].s, ver_str,
762: uuidvalue, known_service_types[i].s, ver_str,
763: upnp_bootid, upnp_bootid, upnp_configid);
764: n = sendto(sockets[j], bufr, l, 0,
765: #ifdef ENABLE_IPV6
766: ipv6 ? (struct sockaddr *)&sockname6 : (struct sockaddr *)&sockname,
767: ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)
768: #else
769: (struct sockaddr *)&sockname,
770: sizeof(struct sockaddr_in)
771: #endif
772: );
773: if(n < 0)
774: {
775: syslog(LOG_ERR, "SendSSDPGoodbye: sendto(udp_shutdown=%d): %m",
776: sockets[j]);
777: ret = -1;
778: }
779: }
780: }
781: return ret;
782: }
783:
784: /* SubmitServicesToMiniSSDPD() :
785: * register services offered by MiniUPnPd to a running instance of
786: * MiniSSDPd */
787: int
788: SubmitServicesToMiniSSDPD(const char * host, unsigned short port) {
789: struct sockaddr_un addr;
790: int s;
791: unsigned char buffer[2048];
792: char strbuf[256];
793: unsigned char * p;
794: int i, l, n;
795: char ver_str[4];
796:
797: s = socket(AF_UNIX, SOCK_STREAM, 0);
798: if(s < 0) {
799: syslog(LOG_ERR, "socket(unix): %m");
800: return -1;
801: }
802: addr.sun_family = AF_UNIX;
803: strncpy(addr.sun_path, minissdpdsocketpath, sizeof(addr.sun_path));
804: if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) {
805: syslog(LOG_ERR, "connect(\"%s\"): %m", minissdpdsocketpath);
806: close(s);
807: return -1;
808: }
809: for(i = 0; known_service_types[i].s; i++) {
810: buffer[0] = 4; /* request type 4 : submit service */
811: /* 4 strings following : ST (service type), USN, Server, Location */
812: p = buffer + 1;
813: l = (int)strlen(known_service_types[i].s);
814: if(i > 0)
815: l++;
816: CODELENGTH(l, p);
817: memcpy(p, known_service_types[i].s, l);
818: if(i > 0)
819: p[l-1] = '1';
820: p += l;
821: if(i==0)
822: ver_str[0] = '\0';
823: else
824: snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version);
825: l = snprintf(strbuf, sizeof(strbuf), "%s::%s%s",
826: uuidvalue, known_service_types[i].s, ver_str);
827: CODELENGTH(l, p);
828: memcpy(p, strbuf, l);
829: p += l;
830: l = (int)strlen(MINIUPNPD_SERVER_STRING);
831: CODELENGTH(l, p);
832: memcpy(p, MINIUPNPD_SERVER_STRING, l);
833: p += l;
834: l = snprintf(strbuf, sizeof(strbuf), "http://%s:%u" ROOTDESC_PATH,
835: host, (unsigned int)port);
836: CODELENGTH(l, p);
837: memcpy(p, strbuf, l);
838: p += l;
839: /* now write the encoded data */
840: n = p - buffer; /* bytes to send */
841: p = buffer; /* start */
842: while(n > 0) {
843: l = write(s, p, n);
844: if (l < 0) {
845: syslog(LOG_ERR, "write(): %m");
846: close(s);
847: return -1;
848: } else if (l == 0) {
849: syslog(LOG_ERR, "write() returned 0");
850: close(s);
851: return -1;
852: }
853: p += l;
854: n -= l;
855: }
856: }
857: close(s);
858: return 0;
859: }
860:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>