Add PPTP runtime and GUI
[tomato.git] / release / src / router / miniupnpd / minissdp.c
blob378d842b1b3eccd3196618ae165286d7fa35b0b1
1 /* $Id: minissdp.c,v 1.27 2011/05/23 12:39:41 nanard Exp $ */
2 /* MiniUPnP project
3 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4 * (c) 2006-2011 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 <string.h>
10 #include <unistd.h>
11 #include <sys/socket.h>
12 #include <sys/un.h>
13 #include <netinet/in.h>
14 #include <arpa/inet.h>
15 #include <syslog.h>
16 #include "config.h"
17 #include "upnpdescstrings.h"
18 #include "miniupnpdpath.h"
19 #include "upnphttp.h"
20 #include "upnpglobalvars.h"
21 #include "minissdp.h"
22 #include "upnputils.h"
23 #include "codelength.h"
25 /* SSDP ip/port */
26 #define SSDP_PORT (1900)
27 #define SSDP_MCAST_ADDR ("239.255.255.250")
28 #define LL_SSDP_MCAST_ADDR ("FF02::C")
29 #define SL_SSDP_MCAST_ADDR ("FF05::C")
31 /* AddMulticastMembership()
32 * param s socket
33 * param ifaddr ip v4 address
35 static int
36 AddMulticastMembership(int s, in_addr_t ifaddr)
38 struct ip_mreq imr; /* Ip multicast membership */
40 /* setting up imr structure */
41 imr.imr_multiaddr.s_addr = inet_addr(SSDP_MCAST_ADDR);
42 /*imr.imr_interface.s_addr = htonl(INADDR_ANY);*/
43 imr.imr_interface.s_addr = ifaddr; /*inet_addr(ifaddr);*/
45 if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&imr, sizeof(struct ip_mreq)) < 0)
47 syslog(LOG_ERR, "setsockopt(udp, IP_ADD_MEMBERSHIP): %m");
48 return -1;
51 return 0;
54 /* AddMulticastMembershipIPv6()
55 * param s socket (IPv6)
56 * To be improved to target specific network interfaces */
57 #ifdef ENABLE_IPV6
58 static int
59 AddMulticastMembershipIPv6(int s)
61 struct ipv6_mreq mr;
62 /*unsigned int ifindex;*/
64 memset(&mr, 0, sizeof(mr));
65 inet_pton(AF_INET6, LL_SSDP_MCAST_ADDR, &mr.ipv6mr_multiaddr);
66 /*mr.ipv6mr_interface = ifindex;*/
67 mr.ipv6mr_interface = 0; /* 0 : all interfaces */
68 #ifndef IPV6_ADD_MEMBERSHIP
69 #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
70 #endif
71 if(setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mr, sizeof(struct ipv6_mreq)) < 0)
73 syslog(LOG_ERR, "setsockopt(udp, IPV6_ADD_MEMBERSHIP): %m");
74 return -1;
76 inet_pton(AF_INET6, SL_SSDP_MCAST_ADDR, &mr.ipv6mr_multiaddr);
77 if(setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mr, sizeof(struct ipv6_mreq)) < 0)
79 syslog(LOG_ERR, "setsockopt(udp, IPV6_ADD_MEMBERSHIP): %m");
80 return -1;
82 return 0;
84 #endif
86 /* Open and configure the socket listening for
87 * SSDP udp packets sent on 239.255.255.250 port 1900
88 * SSDP v6 udp packets sent on FF02::C, or FF05::C, port 1900 */
89 int
90 OpenAndConfSSDPReceiveSocket(int ipv6)
92 int s;
93 struct sockaddr_storage sockname;
94 socklen_t sockname_len;
95 struct lan_addr_s * lan_addr;
96 int j = 1;
98 if( (s = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, 0)) < 0)
100 syslog(LOG_ERR, "socket(udp): %m");
101 return -1;
104 memset(&sockname, 0, sizeof(struct sockaddr_storage));
105 if(ipv6) {
106 struct sockaddr_in6 * saddr = (struct sockaddr_in6 *)&sockname;
107 saddr->sin6_family = AF_INET6;
108 saddr->sin6_port = htons(SSDP_PORT);
109 saddr->sin6_addr = in6addr_any;
110 sockname_len = sizeof(struct sockaddr_in6);
111 } else {
112 struct sockaddr_in * saddr = (struct sockaddr_in *)&sockname;
113 saddr->sin_family = AF_INET;
114 saddr->sin_port = htons(SSDP_PORT);
115 /* NOTE : it seems it doesnt work when binding on the specific address */
116 /*saddr->sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);*/
117 saddr->sin_addr.s_addr = htonl(INADDR_ANY);
118 /*saddr->sin_addr.s_addr = inet_addr(ifaddr);*/
119 sockname_len = sizeof(struct sockaddr_in);
122 if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &j, sizeof(j)) < 0)
124 syslog(LOG_WARNING, "setsockopt(udp, SO_REUSEADDR): %m");
128 if(bind(s, (struct sockaddr *)&sockname, sockname_len) < 0)
130 syslog(LOG_ERR, "bind(udp%s): %m", ipv6 ? "6" : "");
131 close(s);
132 return -1;
135 #ifdef ENABLE_IPV6
136 if(ipv6)
138 AddMulticastMembershipIPv6(s);
140 else
141 #endif
143 for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next)
145 if(AddMulticastMembership(s, lan_addr->addr.s_addr) < 0)
147 syslog(LOG_WARNING,
148 "Failed to add multicast membership for interface %s",
149 lan_addr->str);
154 return s;
157 /* open the UDP socket used to send SSDP notifications to
158 * the multicast group reserved for them */
159 static int
160 OpenAndConfSSDPNotifySocket(in_addr_t addr)
162 int s;
163 unsigned char loopchar = 0;
164 int bcast = 1;
165 struct in_addr mc_if;
166 struct sockaddr_in sockname;
168 if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
170 syslog(LOG_ERR, "socket(udp_notify): %m");
171 return -1;
174 mc_if.s_addr = addr; /*inet_addr(addr);*/
176 if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loopchar, sizeof(loopchar)) < 0)
178 syslog(LOG_ERR, "setsockopt(udp_notify, IP_MULTICAST_LOOP): %m");
179 close(s);
180 return -1;
183 if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&mc_if, sizeof(mc_if)) < 0)
185 syslog(LOG_ERR, "setsockopt(udp_notify, IP_MULTICAST_IF): %m");
186 close(s);
187 return -1;
190 if(setsockopt(s, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bcast)) < 0)
192 syslog(LOG_ERR, "setsockopt(udp_notify, SO_BROADCAST): %m");
193 close(s);
194 return -1;
197 memset(&sockname, 0, sizeof(struct sockaddr_in));
198 sockname.sin_family = AF_INET;
199 sockname.sin_addr.s_addr = addr; /*inet_addr(addr);*/
201 if (bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
203 syslog(LOG_ERR, "bind(udp_notify): %m");
204 close(s);
205 return -1;
208 return s;
212 OpenAndConfSSDPNotifySockets(int * sockets)
213 /*OpenAndConfSSDPNotifySockets(int * sockets,
214 struct lan_addr_s * lan_addr, int n_lan_addr)*/
216 int i, j;
217 struct lan_addr_s * lan_addr;
219 for(i=0, lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next, i++)
221 sockets[i] = OpenAndConfSSDPNotifySocket(lan_addr->addr.s_addr);
222 if(sockets[i] < 0)
224 for(j=0; j<i; j++)
226 close(sockets[j]);
227 sockets[j] = -1;
229 return -1;
232 return 0;
236 * response from a LiveBox (Wanadoo)
237 HTTP/1.1 200 OK
238 CACHE-CONTROL: max-age=1800
239 DATE: Thu, 01 Jan 1970 04:03:23 GMT
240 EXT:
241 LOCATION: http://192.168.0.1:49152/gatedesc.xml
242 SERVER: Linux/2.4.17, UPnP/1.0, Intel SDK for UPnP devices /1.2
243 ST: upnp:rootdevice
244 USN: uuid:75802409-bccb-40e7-8e6c-fa095ecce13e::upnp:rootdevice
246 * response from a Linksys 802.11b :
247 HTTP/1.1 200 OK
248 Cache-Control:max-age=120
249 Location:http://192.168.5.1:5678/rootDesc.xml
250 Server:NT/5.0 UPnP/1.0
251 ST:upnp:rootdevice
252 USN:uuid:upnp-InternetGatewayDevice-1_0-0090a2777777::upnp:rootdevice
253 EXT:
256 /* not really an SSDP "announce" as it is the response
257 * to a SSDP "M-SEARCH" */
258 static void
259 SendSSDPAnnounce2(int s, const struct sockaddr * addr,
260 const char * st, int st_len, const char * suffix,
261 const char * host, unsigned short port)
263 int l, n;
264 char buf[512];
265 socklen_t addrlen;
267 * follow guideline from document "UPnP Device Architecture 1.0"
268 * uppercase is recommended.
269 * DATE: is recommended
270 * SERVER: OS/ver UPnP/1.0 miniupnpd/1.0
271 * - check what to put in the 'Cache-Control' header
273 * have a look at the document "UPnP Device Architecture v1.1 */
274 l = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\n"
275 "CACHE-CONTROL: max-age=120\r\n"
276 /*"DATE: ...\r\n"*/
277 "ST: %.*s%s\r\n"
278 "USN: %s::%.*s%s\r\n"
279 "EXT:\r\n"
280 "SERVER: " MINIUPNPD_SERVER_STRING "\r\n"
281 "LOCATION: http://%s:%u" ROOTDESC_PATH "\r\n"
282 "OPT: \"http://schemas.upnp.org/upnp/1/0/\";\r\n" /* UDA v1.1 */
283 "01-NLS: %u\r\n" /* same as BOOTID. UDA v1.1 */
284 "BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
285 "CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
286 "\r\n",
287 st_len, st, suffix,
288 uuidvalue, st_len, st, suffix,
289 host, (unsigned int)port,
290 upnp_bootid, upnp_bootid, upnp_configid);
291 addrlen = (addr->sa_family == AF_INET6)
292 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
293 n = sendto(s, buf, l, 0,
294 addr, addrlen);
295 syslog(LOG_INFO, "SSDP Announce %d bytes to %s:%d ST: %.*s",n,
296 inet_ntoa(((const struct sockaddr_in *)addr)->sin_addr),
297 ntohs(((const struct sockaddr_in *)addr)->sin_port),
298 l, buf);
299 if(n < 0)
301 syslog(LOG_ERR, "sendto(udp): %m");
305 static const char * const known_service_types[] =
307 "upnp:rootdevice",
308 "urn:schemas-upnp-org:device:InternetGatewayDevice:",
309 "urn:schemas-upnp-org:device:WANConnectionDevice:",
310 "urn:schemas-upnp-org:device:WANDevice:",
311 "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:",
312 "urn:schemas-upnp-org:service:WANIPConnection:",
313 "urn:schemas-upnp-org:service:WANPPPConnection:",
314 #ifdef ENABLE_L3F_SERVICE
315 "urn:schemas-upnp-org:service:Layer3Forwarding:",
316 #endif
317 #ifdef ENABLE_6FC_SERVICE
318 "url:schemas-upnp-org:service:WANIPv6FirewallControl:",
319 #endif
323 static void
324 SendSSDPNotifies(int s, const char * host, unsigned short port,
325 unsigned int lifetime)
327 struct sockaddr_in sockname;
328 int l, n, i=0;
329 char bufr[512];
331 memset(&sockname, 0, sizeof(struct sockaddr_in));
332 sockname.sin_family = AF_INET;
333 sockname.sin_port = htons(SSDP_PORT);
334 sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
336 while(known_service_types[i])
338 l = snprintf(bufr, sizeof(bufr),
339 "NOTIFY * HTTP/1.1\r\n"
340 "HOST: %s:%d\r\n"
341 "CACHE-CONTROL: max-age=%u\r\n"
342 "lOCATION: http://%s:%d" ROOTDESC_PATH"\r\n"
343 "SERVER: " MINIUPNPD_SERVER_STRING "\r\n"
344 "NT: %s%s\r\n"
345 "USN: %s::%s%s\r\n"
346 "NTS: ssdp:alive\r\n"
347 "OPT: \"http://schemas.upnp.org/upnp/1/0/\";\r\n" /* UDA v1.1 */
348 "01-NLS: %u\r\n" /* same as BOOTID field. UDA v1.1 */
349 "BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
350 "CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
351 "\r\n",
352 SSDP_MCAST_ADDR, SSDP_PORT,
353 lifetime,
354 host, port,
355 known_service_types[i], (i==0?"":"1"),
356 uuidvalue, known_service_types[i], (i==0?"":"1"),
357 upnp_bootid, upnp_bootid, upnp_configid );
358 if(l>=sizeof(bufr))
360 syslog(LOG_WARNING, "SendSSDPNotifies(): truncated output");
361 l = sizeof(bufr);
363 n = sendto(s, bufr, l, 0,
364 (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
365 if(n < 0)
367 syslog(LOG_ERR, "sendto(udp_notify=%d, %s): %m", s, host);
369 i++;
373 void
374 SendSSDPNotifies2(int * sockets,
375 unsigned short port,
376 unsigned int lifetime)
378 int i;
379 struct lan_addr_s * lan_addr;
380 for(i=0, lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next, i++)
382 SendSSDPNotifies(sockets[i], lan_addr->str, port, lifetime);
386 /* ProcessSSDPRequest()
387 * process SSDP M-SEARCH requests and responds to them */
388 void
389 ProcessSSDPRequest(int s, unsigned short port)
391 int n;
392 char bufr[1500];
393 socklen_t len_r;
394 #ifdef ENABLE_IPV6
395 struct sockaddr_storage sendername;
396 len_r = sizeof(struct sockaddr_storage);
397 #else
398 struct sockaddr_in sendername;
399 len_r = sizeof(struct sockaddr_in);
400 #endif
402 n = recvfrom(s, bufr, sizeof(bufr), 0,
403 (struct sockaddr *)&sendername, &len_r);
404 if(n < 0)
406 syslog(LOG_ERR, "recvfrom(udp): %m");
407 return;
409 ProcessSSDPData(s, bufr, n, (struct sockaddr *)&sendername, port);
413 void
414 ProcessSSDPData(int s, const char *bufr, int n,
415 const struct sockaddr * sender, unsigned short port) {
416 int i, l;
417 struct lan_addr_s * lan_addr = NULL;
418 const char * st = NULL;
419 int st_len = 0;
420 char sender_str[64];
421 const char * announced_host = NULL;
423 /* get the string representation of the sender address */
424 sockaddr_to_string(sender, sender_str, sizeof(sender_str));
426 if(memcmp(bufr, "NOTIFY", 6) == 0)
428 /* ignore NOTIFY packets. We could log the sender and device type */
429 return;
431 else if(memcmp(bufr, "M-SEARCH", 8) == 0)
433 i = 0;
434 while(i < n)
436 while((i < n - 1) && (bufr[i] != '\r' || bufr[i+1] != '\n'))
437 i++;
438 i += 2;
439 if((i < n - 3) && (strncasecmp(bufr+i, "st:", 3) == 0))
441 st = bufr+i+3;
442 st_len = 0;
443 while((*st == ' ' || *st == '\t') && (st < bufr + n))
444 st++;
445 while(st[st_len]!='\r' && st[st_len]!='\n'
446 && (st + st_len < bufr + n))
447 st_len++;
448 /*syslog(LOG_INFO, "ST: %.*s", st_len, st);*/
449 /*j = 0;*/
450 /*while(bufr[i+j]!='\r') j++;*/
451 /*syslog(LOG_INFO, "%.*s", j, bufr+i);*/
454 /*syslog(LOG_INFO, "SSDP M-SEARCH packet received from %s",
455 sender_str );*/
456 if(st && (st_len > 0))
458 /* TODO : doesnt answer at once but wait for a random time */
459 syslog(LOG_INFO, "SSDP M-SEARCH from %s ST: %.*s",
460 sender_str, st_len, st);
461 /* find in which sub network the client is */
462 if(sender->sa_family == AF_INET)
464 for(lan_addr = lan_addrs.lh_first;
465 lan_addr != NULL;
466 lan_addr = lan_addr->list.le_next)
468 if( (((const struct sockaddr_in *)sender)->sin_addr.s_addr & lan_addr->mask.s_addr)
469 == (lan_addr->addr.s_addr & lan_addr->mask.s_addr))
470 break;
472 if (lan_addr == NULL)
474 syslog(LOG_ERR, "Can't find in which sub network the client is");
475 return;
477 announced_host = lan_addr->str;
479 #ifdef ENABLE_IPV6
480 else
482 /* IPv6 address with brackets */
483 announced_host = ipv6_addr_for_http_with_brackets;
485 #endif
486 /* Responds to request with a device as ST header */
487 for(i = 0; known_service_types[i]; i++)
489 l = (int)strlen(known_service_types[i]);
490 if(l<=st_len && (0 == memcmp(st, known_service_types[i], l)))
492 syslog(LOG_INFO, "Single search found");
493 SendSSDPAnnounce2(s, sender,
494 st, st_len, "",
495 announced_host, port);
496 break;
499 /* Responds to request with ST: ssdp:all */
500 /* strlen("ssdp:all") == 8 */
501 if(st_len==8 && (0 == memcmp(st, "ssdp:all", 8)))
503 syslog(LOG_INFO, "ssdp:all found");
504 for(i=0; known_service_types[i]; i++)
506 l = (int)strlen(known_service_types[i]);
507 SendSSDPAnnounce2(s, sender,
508 known_service_types[i], l, i==0?"":"1",
509 announced_host, port);
512 /* responds to request by UUID value */
513 l = (int)strlen(uuidvalue);
514 if(l==st_len && (0 == memcmp(st, uuidvalue, l)))
516 syslog(LOG_INFO, "ssdp:uuid found");
517 SendSSDPAnnounce2(s, sender, st, st_len, "",
518 announced_host, port);
521 else
523 syslog(LOG_INFO, "Invalid SSDP M-SEARCH from %s", sender_str);
526 else
528 syslog(LOG_NOTICE, "Unknown udp packet received from %s", sender_str);
532 /* This will broadcast ssdp:byebye notifications to inform
533 * the network that UPnP is going down. */
535 SendSSDPGoodbye(int * sockets, int n_sockets)
537 struct sockaddr_in sockname;
538 int n, l;
539 int i, j;
540 char bufr[512];
542 memset(&sockname, 0, sizeof(struct sockaddr_in));
543 sockname.sin_family = AF_INET;
544 sockname.sin_port = htons(SSDP_PORT);
545 sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
547 for(j=0; j<n_sockets; j++)
549 for(i=0; known_service_types[i]; i++)
551 l = snprintf(bufr, sizeof(bufr),
552 "NOTIFY * HTTP/1.1\r\n"
553 "HOST: %s:%d\r\n"
554 "NT: %s%s\r\n"
555 "USN: %s::%s%s\r\n"
556 "NTS: ssdp:byebye\r\n"
557 "OPT: \"http://schemas.upnp.org/upnp/1/0/\";\r\n" /* UDA v1.1 */
558 "01-NLS: %u\r\n" /* same as BOOTID field. UDA v1.1 */
559 "BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
560 "CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
561 "\r\n",
562 SSDP_MCAST_ADDR, SSDP_PORT,
563 known_service_types[i], (i==0?"":"1"),
564 uuidvalue, known_service_types[i], (i==0?"":"1"),
565 upnp_bootid, upnp_bootid, upnp_configid);
566 n = sendto(sockets[j], bufr, l, 0,
567 (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
568 if(n < 0)
570 syslog(LOG_ERR, "SendSSDPGoodbye: sendto(udp_shutdown=%d): %m",
571 sockets[j]);
572 return -1;
576 return 0;
579 /* SubmitServicesToMiniSSDPD() :
580 * register services offered by MiniUPnPd to a running instance of
581 * MiniSSDPd */
583 SubmitServicesToMiniSSDPD(const char * host, unsigned short port) {
584 struct sockaddr_un addr;
585 int s;
586 unsigned char buffer[2048];
587 char strbuf[256];
588 unsigned char * p;
589 int i, l;
591 s = socket(AF_UNIX, SOCK_STREAM, 0);
592 if(s < 0) {
593 syslog(LOG_ERR, "socket(unix): %m");
594 return -1;
596 addr.sun_family = AF_UNIX;
597 strncpy(addr.sun_path, minissdpdsocketpath, sizeof(addr.sun_path));
598 if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) {
599 syslog(LOG_ERR, "connect(\"%s\"): %m", minissdpdsocketpath);
600 return -1;
602 for(i = 0; known_service_types[i]; i++) {
603 buffer[0] = 4;
604 p = buffer + 1;
605 l = (int)strlen(known_service_types[i]);
606 if(i > 0)
607 l++;
608 CODELENGTH(l, p);
609 memcpy(p, known_service_types[i], l);
610 if(i > 0)
611 p[l-1] = '1';
612 p += l;
613 l = snprintf(strbuf, sizeof(strbuf), "%s::%s%s",
614 uuidvalue, known_service_types[i], (i==0)?"":"1");
615 CODELENGTH(l, p);
616 memcpy(p, strbuf, l);
617 p += l;
618 l = (int)strlen(MINIUPNPD_SERVER_STRING);
619 CODELENGTH(l, p);
620 memcpy(p, MINIUPNPD_SERVER_STRING, l);
621 p += l;
622 l = snprintf(strbuf, sizeof(strbuf), "http://%s:%u" ROOTDESC_PATH,
623 host, (unsigned int)port);
624 CODELENGTH(l, p);
625 memcpy(p, strbuf, l);
626 p += l;
627 if(write(s, buffer, p - buffer) < 0) {
628 syslog(LOG_ERR, "write(): %m");
629 return -1;
632 close(s);
633 return 0;