Annotation of embedaddon/miniupnpd/minissdp.c, revision 1.1.1.3
1.1.1.3 ! misho 1: /* $Id: minissdp.c,v 1.44 2013/02/06 10:50:04 nanard Exp $ */
1.1 misho 2: /* MiniUPnP project
3: * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
1.1.1.3 ! misho 4: * (c) 2006-2013 Thomas Bernard
1.1 misho 5: * This software is subject to the conditions detailed
6: * in the LICENCE file provided within the distribution */
7:
8: #include <stdio.h>
1.1.1.3 ! misho 9: #include <stdlib.h>
1.1 misho 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>
1.1.1.3 ! misho 16: #include <errno.h>
1.1 misho 17: #include <syslog.h>
1.1.1.3 ! misho 18:
1.1 misho 19: #include "config.h"
20: #include "upnpdescstrings.h"
21: #include "miniupnpdpath.h"
22: #include "upnphttp.h"
23: #include "upnpglobalvars.h"
24: #include "minissdp.h"
1.1.1.2 misho 25: #include "upnputils.h"
1.1.1.3 ! misho 26: #include "getroute.h"
1.1 misho 27: #include "codelength.h"
28:
29: /* SSDP ip/port */
30: #define SSDP_PORT (1900)
31: #define SSDP_MCAST_ADDR ("239.255.255.250")
1.1.1.3 ! misho 32: #define LL_SSDP_MCAST_ADDR "FF02::C"
! 33: #define SL_SSDP_MCAST_ADDR "FF05::C"
1.1 misho 34:
1.1.1.2 misho 35: /* AddMulticastMembership()
36: * param s socket
37: * param ifaddr ip v4 address
38: */
1.1 misho 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);*/
1.1.1.3 ! misho 48:
1.1 misho 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:
1.1.1.2 misho 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:
1.1.1.3 ! misho 90: /* Open and configure the socket listening for
1.1.1.2 misho 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 */
1.1 misho 93: int
1.1.1.2 misho 94: OpenAndConfSSDPReceiveSocket(int ipv6)
1.1 misho 95: {
96: int s;
1.1.1.2 misho 97: struct sockaddr_storage sockname;
98: socklen_t sockname_len;
99: struct lan_addr_s * lan_addr;
1.1 misho 100: int j = 1;
1.1.1.2 misho 101:
102: if( (s = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, 0)) < 0)
1.1 misho 103: {
1.1.1.3 ! misho 104: syslog(LOG_ERR, "%s: socket(udp): %m",
! 105: "OpenAndConfSSDPReceiveSocket");
1.1 misho 106: return -1;
1.1.1.2 misho 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: }
1.1 misho 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:
1.1.1.3 ! misho 132: if(!set_non_blocking(s))
! 133: {
! 134: syslog(LOG_WARNING, "%s: set_non_blocking(): %m",
! 135: "OpenAndConfSSDPReceiveSocket");
! 136: }
1.1 misho 137:
1.1.1.2 misho 138: if(bind(s, (struct sockaddr *)&sockname, sockname_len) < 0)
1.1 misho 139: {
1.1.1.3 ! misho 140: syslog(LOG_ERR, "%s: bind(udp%s): %m",
! 141: "OpenAndConfSSDPReceiveSocket", ipv6 ? "6" : "");
1.1 misho 142: close(s);
143: return -1;
1.1.1.2 misho 144: }
1.1 misho 145:
1.1.1.2 misho 146: #ifdef ENABLE_IPV6
147: if(ipv6)
148: {
149: AddMulticastMembershipIPv6(s);
150: }
151: else
152: #endif
1.1 misho 153: {
1.1.1.2 misho 154: for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next)
1.1 misho 155: {
1.1.1.2 misho 156: if(AddMulticastMembership(s, lan_addr->addr.s_addr) < 0)
157: {
158: syslog(LOG_WARNING,
1.1.1.3 ! misho 159: "Failed to add multicast membership for interface %s",
! 160: lan_addr->str ? lan_addr->str : "NULL");
1.1.1.2 misho 161: }
1.1 misho 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;
1.1.1.3 ! misho 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 */
1.1 misho 180: struct in_addr mc_if;
181: struct sockaddr_in sockname;
1.1.1.3 ! misho 182:
1.1 misho 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: }
1.1.1.3 ! misho 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:
1.1 misho 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:
1.1.1.3 ! misho 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:
1.1 misho 262: int
263: OpenAndConfSSDPNotifySockets(int * sockets)
264: /*OpenAndConfSSDPNotifySockets(int * sockets,
265: struct lan_addr_s * lan_addr, int n_lan_addr)*/
266: {
1.1.1.3 ! misho 267: int i;
1.1.1.2 misho 268: struct lan_addr_s * lan_addr;
269:
1.1.1.3 ! misho 270: for(i=0, lan_addr = lan_addrs.lh_first;
! 271: lan_addr != NULL;
! 272: lan_addr = lan_addr->list.le_next)
1.1 misho 273: {
1.1.1.2 misho 274: sockets[i] = OpenAndConfSSDPNotifySocket(lan_addr->addr.s_addr);
1.1 misho 275: if(sockets[i] < 0)
1.1.1.3 ! misho 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
1.1 misho 284: }
285: return 0;
1.1.1.3 ! misho 286: error:
! 287: while(--i >= 0)
! 288: {
! 289: close(sockets[i]);
! 290: sockets[i] = -1;
! 291: }
! 292: return -1;
1.1 misho 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
1.1.1.2 misho 319: SendSSDPAnnounce2(int s, const struct sockaddr * addr,
1.1 misho 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];
1.1.1.3 ! misho 325: char addr_str[64];
1.1.1.2 misho 326: socklen_t addrlen;
1.1.1.3 ! misho 327: /*
1.1 misho 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
1.1.1.3 ! misho 332: * - check what to put in the 'Cache-Control' header
1.1.1.2 misho 333: *
334: * have a look at the document "UPnP Device Architecture v1.1 */
1.1 misho 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"
1.1.1.3 ! misho 343: "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */
1.1.1.2 misho 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 */
1.1 misho 347: "\r\n",
348: st_len, st, suffix,
349: uuidvalue, st_len, st, suffix,
1.1.1.2 misho 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);
1.1 misho 354: n = sendto(s, buf, l, 0,
1.1.1.2 misho 355: addr, addrlen);
1.1.1.3 ! misho 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,
1.1 misho 359: l, buf);
360: if(n < 0)
361: {
1.1.1.3 ! misho 362: /* XXX handle EINTR, EAGAIN, EWOULDBLOCK */
1.1 misho 363: syslog(LOG_ERR, "sendto(udp): %m");
364: }
365: }
366:
1.1.1.3 ! misho 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},
1.1.1.2 misho 387: #ifdef ENABLE_L3F_SERVICE
1.1.1.3 ! misho 388: {"urn:schemas-upnp-org:service:Layer3Forwarding:", 1},
1.1.1.2 misho 389: #endif
390: #ifdef ENABLE_6FC_SERVICE
1.1.1.3 ! misho 391: {"url:schemas-upnp-org:service:WANIPv6FirewallControl:", 1},
1.1.1.2 misho 392: #endif
1.1.1.3 ! misho 393: {0, 0}
1.1 misho 394: };
395:
396: static void
397: SendSSDPNotifies(int s, const char * host, unsigned short port,
1.1.1.3 ! misho 398: unsigned int lifetime, int ipv6)
1.1 misho 399: {
1.1.1.3 ! misho 400: #ifdef ENABLE_IPV6
! 401: struct sockaddr_storage sockname;
! 402: #else
1.1 misho 403: struct sockaddr_in sockname;
1.1.1.3 ! misho 404: #endif
1.1 misho 405: int l, n, i=0;
406: char bufr[512];
1.1.1.3 ! misho 407: char ver_str[4];
1.1 misho 408:
1.1.1.3 ! misho 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: }
1.1 misho 426:
1.1.1.3 ! misho 427: while(known_service_types[i].s)
1.1 misho 428: {
1.1.1.3 ! misho 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),
1.1.1.2 misho 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"
1.1.1.3 ! misho 442: "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */
1.1.1.2 misho 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",
1.1.1.3 ! misho 447: ipv6 ? "[" LL_SSDP_MCAST_ADDR "]" : SSDP_MCAST_ADDR,
! 448: SSDP_PORT,
1.1.1.2 misho 449: lifetime,
450: host, port,
1.1.1.3 ! misho 451: known_service_types[i].s, ver_str,
! 452: uuidvalue, known_service_types[i].s, ver_str,
1.1.1.2 misho 453: upnp_bootid, upnp_bootid, upnp_configid );
1.1.1.3 ! misho 454: if(l<0)
! 455: {
! 456: syslog(LOG_ERR, "SendSSDPNotifies() snprintf error");
! 457: continue;
! 458: }
! 459: if((unsigned int)l >= sizeof(bufr))
1.1 misho 460: {
461: syslog(LOG_WARNING, "SendSSDPNotifies(): truncated output");
462: l = sizeof(bufr);
463: }
464: n = sendto(s, bufr, l, 0,
1.1.1.3 ! misho 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: );
1.1 misho 472: if(n < 0)
473: {
1.1.1.3 ! misho 474: /* XXX handle EINTR, EAGAIN, EWOULDBLOCK */
! 475: syslog(LOG_ERR, "sendto(udp_notify=%d, %s): %m", s,
! 476: host ? host : "NULL");
1.1 misho 477: }
478: i++;
479: }
480: }
481:
482: void
483: SendSSDPNotifies2(int * sockets,
484: unsigned short port,
485: unsigned int lifetime)
486: {
487: int i;
1.1.1.2 misho 488: struct lan_addr_s * lan_addr;
1.1.1.3 ! misho 489: for(i=0, lan_addr = lan_addrs.lh_first;
! 490: lan_addr != NULL;
! 491: lan_addr = lan_addr->list.le_next)
1.1 misho 492: {
1.1.1.3 ! misho 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
1.1 misho 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;
1.1.1.2 misho 512: #ifdef ENABLE_IPV6
513: struct sockaddr_storage sendername;
514: len_r = sizeof(struct sockaddr_storage);
515: #else
1.1 misho 516: struct sockaddr_in sendername;
517: len_r = sizeof(struct sockaddr_in);
1.1.1.2 misho 518: #endif
1.1 misho 519:
520: n = recvfrom(s, bufr, sizeof(bufr), 0,
521: (struct sockaddr *)&sendername, &len_r);
522: if(n < 0)
523: {
1.1.1.3 ! misho 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: }
1.1 misho 532: return;
533: }
1.1.1.2 misho 534: ProcessSSDPData(s, bufr, n, (struct sockaddr *)&sendername, port);
1.1 misho 535:
536: }
537:
1.1.1.2 misho 538: void
539: ProcessSSDPData(int s, const char *bufr, int n,
540: const struct sockaddr * sender, unsigned short port) {
1.1 misho 541: int i, l;
1.1.1.2 misho 542: struct lan_addr_s * lan_addr = NULL;
543: const char * st = NULL;
1.1 misho 544: int st_len = 0;
1.1.1.3 ! misho 545: int st_ver = 0;
1.1.1.2 misho 546: char sender_str[64];
1.1.1.3 ! misho 547: char ver_str[4];
1.1.1.2 misho 548: const char * announced_host = NULL;
1.1.1.3 ! misho 549: #ifdef ENABLE_IPV6
! 550: #ifdef UPNP_STRICT
! 551: char announced_host_buf[64];
! 552: #endif
! 553: #endif
1.1 misho 554:
1.1.1.2 misho 555: /* get the string representation of the sender address */
556: sockaddr_to_string(sender, sender_str, sizeof(sender_str));
1.1.1.3 ! misho 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: }
1.1 misho 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++;
1.1.1.3 ! misho 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);
1.1 misho 592: /*j = 0;*/
593: /*while(bufr[i+j]!='\r') j++;*/
594: /*syslog(LOG_INFO, "%.*s", j, bufr+i);*/
595: }
596: }
1.1.1.2 misho 597: /*syslog(LOG_INFO, "SSDP M-SEARCH packet received from %s",
598: sender_str );*/
1.1 misho 599: if(st && (st_len > 0))
600: {
601: /* TODO : doesnt answer at once but wait for a random time */
1.1.1.2 misho 602: syslog(LOG_INFO, "SSDP M-SEARCH from %s ST: %.*s",
603: sender_str, st_len, st);
1.1 misho 604: /* find in which sub network the client is */
1.1.1.2 misho 605: if(sender->sa_family == AF_INET)
1.1 misho 606: {
1.1.1.2 misho 607: if (lan_addr == NULL)
608: {
609: syslog(LOG_ERR, "Can't find in which sub network the client is");
610: return;
1.1 misho 611: }
1.1.1.2 misho 612: announced_host = lan_addr->str;
613: }
614: #ifdef ENABLE_IPV6
615: else
616: {
617: /* IPv6 address with brackets */
1.1.1.3 ! misho 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
1.1.1.2 misho 648: announced_host = ipv6_addr_for_http_with_brackets;
1.1.1.3 ! misho 649: #endif
1.1 misho 650: }
1.1.1.2 misho 651: #endif
1.1 misho 652: /* Responds to request with a device as ST header */
1.1.1.3 ! misho 653: for(i = 0; known_service_types[i].s; i++)
1.1 misho 654: {
1.1.1.3 ! misho 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: )
1.1 misho 662: {
663: syslog(LOG_INFO, "Single search found");
1.1.1.2 misho 664: SendSSDPAnnounce2(s, sender,
1.1 misho 665: st, st_len, "",
1.1.1.2 misho 666: announced_host, port);
1.1 misho 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");
1.1.1.3 ! misho 675: for(i=0; known_service_types[i].s; i++)
1.1 misho 676: {
1.1.1.3 ! misho 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);
1.1.1.2 misho 682: SendSSDPAnnounce2(s, sender,
1.1.1.3 ! misho 683: known_service_types[i].s, l, ver_str,
1.1.1.2 misho 684: announced_host, port);
1.1 misho 685: }
1.1.1.3 ! misho 686: /* also answer for uuid */
! 687: SendSSDPAnnounce2(s, sender, uuidvalue, strlen(uuidvalue), "",
! 688: announced_host, port);
1.1 misho 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");
1.1.1.2 misho 695: SendSSDPAnnounce2(s, sender, st, st_len, "",
696: announced_host, port);
1.1 misho 697: }
698: }
699: else
700: {
1.1.1.2 misho 701: syslog(LOG_INFO, "Invalid SSDP M-SEARCH from %s", sender_str);
1.1 misho 702: }
703: }
704: else
705: {
1.1.1.2 misho 706: syslog(LOG_NOTICE, "Unknown udp packet received from %s", sender_str);
1.1 misho 707: }
708: }
709:
1.1.1.3 ! misho 710: /* This will broadcast ssdp:byebye notifications to inform
1.1 misho 711: * the network that UPnP is going down. */
712: int
713: SendSSDPGoodbye(int * sockets, int n_sockets)
714: {
715: struct sockaddr_in sockname;
1.1.1.3 ! misho 716: #ifdef ENABLE_IPV6
! 717: struct sockaddr_in6 sockname6;
! 718: #endif
1.1 misho 719: int n, l;
720: int i, j;
721: char bufr[512];
1.1.1.3 ! misho 722: char ver_str[4];
! 723: int ret = 0;
! 724: int ipv6 = 0;
1.1 misho 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);
1.1.1.3 ! misho 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
1.1 misho 736:
737: for(j=0; j<n_sockets; j++)
738: {
1.1.1.3 ! misho 739: #ifdef ENABLE_IPV6
! 740: ipv6 = j & 1;
! 741: #endif
! 742: for(i=0; known_service_types[i].s; i++)
1.1 misho 743: {
1.1.1.3 ! misho 744: if(i==0)
! 745: ver_str[0] = '\0';
! 746: else
! 747: snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version);
1.1 misho 748: l = snprintf(bufr, sizeof(bufr),
1.1.1.2 misho 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"
1.1.1.3 ! misho 754: "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */
1.1.1.2 misho 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",
1.1.1.3 ! misho 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,
1.1.1.2 misho 763: upnp_bootid, upnp_bootid, upnp_configid);
1.1 misho 764: n = sendto(sockets[j], bufr, l, 0,
1.1.1.3 ! misho 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: );
1.1 misho 773: if(n < 0)
774: {
775: syslog(LOG_ERR, "SendSSDPGoodbye: sendto(udp_shutdown=%d): %m",
776: sockets[j]);
1.1.1.3 ! misho 777: ret = -1;
1.1 misho 778: }
779: }
780: }
1.1.1.3 ! misho 781: return ret;
1.1 misho 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;
1.1.1.3 ! misho 794: int i, l, n;
! 795: char ver_str[4];
1.1 misho 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);
1.1.1.3 ! misho 806: close(s);
1.1 misho 807: return -1;
808: }
1.1.1.3 ! misho 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 */
1.1 misho 812: p = buffer + 1;
1.1.1.3 ! misho 813: l = (int)strlen(known_service_types[i].s);
1.1 misho 814: if(i > 0)
815: l++;
816: CODELENGTH(l, p);
1.1.1.3 ! misho 817: memcpy(p, known_service_types[i].s, l);
1.1 misho 818: if(i > 0)
819: p[l-1] = '1';
820: p += l;
1.1.1.3 ! misho 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);
1.1 misho 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;
1.1.1.3 ! misho 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;
1.1 misho 855: }
856: }
857: close(s);
858: return 0;
859: }
860:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>