Tomato 1.26 beta(1766)
[tomato.git] / release / src / router / miniupnpd / minissdp.c
bloba6227579de042235cf13e43bbf370370060b2517
1 /* $Id: minissdp.c,v 1.17 2009/08/20 09:10:38 nanard Exp $ */
2 /* MiniUPnP project
3 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4 * (c) 2006-2009 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 "codelength.h"
24 /* SSDP ip/port */
25 #define SSDP_PORT (1900)
26 #define SSDP_MCAST_ADDR ("239.255.255.250")
28 static int
29 AddMulticastMembership(int s, in_addr_t ifaddr)
31 struct ip_mreq imr; /* Ip multicast membership */
33 /* setting up imr structure */
34 imr.imr_multiaddr.s_addr = inet_addr(SSDP_MCAST_ADDR);
35 /*imr.imr_interface.s_addr = htonl(INADDR_ANY);*/
36 imr.imr_interface.s_addr = ifaddr; /*inet_addr(ifaddr);*/
38 if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&imr, sizeof(struct ip_mreq)) < 0)
40 syslog(LOG_ERR, "setsockopt(udp, IP_ADD_MEMBERSHIP): %m");
41 return -1;
44 return 0;
47 /* Open and configure the socket listening for
48 * SSDP udp packets sent on 239.255.255.250 port 1900 */
49 int
50 OpenAndConfSSDPReceiveSocket()
52 int s;
53 int i;
54 int j = 1;
55 struct sockaddr_in sockname;
57 if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
59 syslog(LOG_ERR, "socket(udp): %m");
60 return -1;
63 memset(&sockname, 0, sizeof(struct sockaddr_in));
64 sockname.sin_family = AF_INET;
65 sockname.sin_port = htons(SSDP_PORT);
66 /* NOTE : it seems it doesnt work when binding on the specific address */
67 /*sockname.sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);*/
68 sockname.sin_addr.s_addr = htonl(INADDR_ANY);
69 /*sockname.sin_addr.s_addr = inet_addr(ifaddr);*/
71 if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &j, sizeof(j)) < 0)
73 syslog(LOG_WARNING, "setsockopt(udp, SO_REUSEADDR): %m");
77 if(bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
79 syslog(LOG_ERR, "bind(udp): %m");
80 close(s);
81 return -1;
84 i = n_lan_addr;
85 while(i>0)
87 i--;
88 if(AddMulticastMembership(s, lan_addr[i].addr.s_addr) < 0)
90 syslog(LOG_WARNING,
91 "Failed to add multicast membership for address %s",
92 lan_addr[i].str );
96 return s;
99 /* open the UDP socket used to send SSDP notifications to
100 * the multicast group reserved for them */
101 static int
102 OpenAndConfSSDPNotifySocket(in_addr_t addr)
104 int s;
105 unsigned char loopchar = 0;
106 int bcast = 1;
107 struct in_addr mc_if;
108 struct sockaddr_in sockname;
110 if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
112 syslog(LOG_ERR, "socket(udp_notify): %m");
113 return -1;
116 mc_if.s_addr = addr; /*inet_addr(addr);*/
118 if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loopchar, sizeof(loopchar)) < 0)
120 syslog(LOG_ERR, "setsockopt(udp_notify, IP_MULTICAST_LOOP): %m");
121 close(s);
122 return -1;
125 if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&mc_if, sizeof(mc_if)) < 0)
127 syslog(LOG_ERR, "setsockopt(udp_notify, IP_MULTICAST_IF): %m");
128 close(s);
129 return -1;
132 if(setsockopt(s, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bcast)) < 0)
134 syslog(LOG_ERR, "setsockopt(udp_notify, SO_BROADCAST): %m");
135 close(s);
136 return -1;
139 memset(&sockname, 0, sizeof(struct sockaddr_in));
140 sockname.sin_family = AF_INET;
141 sockname.sin_addr.s_addr = addr; /*inet_addr(addr);*/
143 if (bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
145 syslog(LOG_ERR, "bind(udp_notify): %m");
146 close(s);
147 return -1;
150 return s;
154 OpenAndConfSSDPNotifySockets(int * sockets)
155 /*OpenAndConfSSDPNotifySockets(int * sockets,
156 struct lan_addr_s * lan_addr, int n_lan_addr)*/
158 int i, j;
159 for(i=0; i<n_lan_addr; i++)
161 sockets[i] = OpenAndConfSSDPNotifySocket(lan_addr[i].addr.s_addr);
162 if(sockets[i] < 0)
164 for(j=0; j<i; j++)
166 close(sockets[j]);
167 sockets[j] = -1;
169 return -1;
172 return 0;
176 * response from a LiveBox (Wanadoo)
177 HTTP/1.1 200 OK
178 CACHE-CONTROL: max-age=1800
179 DATE: Thu, 01 Jan 1970 04:03:23 GMT
180 EXT:
181 LOCATION: http://192.168.0.1:49152/gatedesc.xml
182 SERVER: Linux/2.4.17, UPnP/1.0, Intel SDK for UPnP devices /1.2
183 ST: upnp:rootdevice
184 USN: uuid:75802409-bccb-40e7-8e6c-fa095ecce13e::upnp:rootdevice
186 * response from a Linksys 802.11b :
187 HTTP/1.1 200 OK
188 Cache-Control:max-age=120
189 Location:http://192.168.5.1:5678/rootDesc.xml
190 Server:NT/5.0 UPnP/1.0
191 ST:upnp:rootdevice
192 USN:uuid:upnp-InternetGatewayDevice-1_0-0090a2777777::upnp:rootdevice
193 EXT:
196 /* not really an SSDP "announce" as it is the response
197 * to a SSDP "M-SEARCH" */
198 static void
199 SendSSDPAnnounce2(int s, struct sockaddr_in sockname,
200 const char * st, int st_len, const char * suffix,
201 const char * host, unsigned short port)
203 int l, n;
204 char buf[512];
206 * follow guideline from document "UPnP Device Architecture 1.0"
207 * uppercase is recommended.
208 * DATE: is recommended
209 * SERVER: OS/ver UPnP/1.0 miniupnpd/1.0
210 * - check what to put in the 'Cache-Control' header
211 * */
212 l = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\n"
213 "CACHE-CONTROL: max-age=120\r\n"
214 /*"DATE: ...\r\n"*/
215 "ST: %.*s%s\r\n"
216 "USN: %s::%.*s%s\r\n"
217 "EXT:\r\n"
218 "SERVER: " MINIUPNPD_SERVER_STRING "\r\n"
219 "LOCATION: http://%s:%u" ROOTDESC_PATH "\r\n"
220 "\r\n",
221 st_len, st, suffix,
222 uuidvalue, st_len, st, suffix,
223 host, (unsigned int)port);
224 n = sendto(s, buf, l, 0,
225 (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
226 if(n < 0)
228 syslog(LOG_ERR, "sendto(udp): %m");
232 static const char * const known_service_types[] =
234 "upnp:rootdevice",
235 "urn:schemas-upnp-org:device:InternetGatewayDevice:",
236 "urn:schemas-upnp-org:device:WANConnectionDevice:",
237 "urn:schemas-upnp-org:device:WANDevice:",
238 "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:",
239 "urn:schemas-upnp-org:service:WANIPConnection:",
240 "urn:schemas-upnp-org:service:WANPPPConnection:",
241 "urn:schemas-upnp-org:service:Layer3Forwarding:",
245 static void
246 SendSSDPNotifies(int s, const char * host, unsigned short port,
247 unsigned int lifetime)
249 struct sockaddr_in sockname;
250 int l, n, i=0;
251 char bufr[512];
253 memset(&sockname, 0, sizeof(struct sockaddr_in));
254 sockname.sin_family = AF_INET;
255 sockname.sin_port = htons(SSDP_PORT);
256 sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
258 while(known_service_types[i])
260 l = snprintf(bufr, sizeof(bufr),
261 "NOTIFY * HTTP/1.1\r\n"
262 "HOST:%s:%d\r\n"
263 "Cache-Control:max-age=%u\r\n"
264 "Location:http://%s:%d" ROOTDESC_PATH"\r\n"
265 /*"Server:miniupnpd/1.0 UPnP/1.0\r\n"*/
266 "Server: " MINIUPNPD_SERVER_STRING "\r\n"
267 "NT:%s%s\r\n"
268 "USN:%s::%s%s\r\n"
269 "NTS:ssdp:alive\r\n"
270 "\r\n",
271 SSDP_MCAST_ADDR, SSDP_PORT,
272 lifetime,
273 host, port,
274 known_service_types[i], (i==0?"":"1"),
275 uuidvalue, known_service_types[i], (i==0?"":"1") );
276 if(l>=sizeof(bufr))
278 syslog(LOG_WARNING, "SendSSDPNotifies(): truncated output");
279 l = sizeof(bufr);
281 n = sendto(s, bufr, l, 0,
282 (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
283 if(n < 0)
285 syslog(LOG_ERR, "sendto(udp_notify=%d, %s): %m", s, host);
287 i++;
291 void
292 SendSSDPNotifies2(int * sockets,
293 unsigned short port,
294 unsigned int lifetime)
295 /*SendSSDPNotifies2(int * sockets, struct lan_addr_s * lan_addr, int n_lan_addr,
296 unsigned short port,
297 unsigned int lifetime)*/
299 int i;
300 for(i=0; i<n_lan_addr; i++)
302 SendSSDPNotifies(sockets[i], lan_addr[i].str, port, lifetime);
306 /* ProcessSSDPRequest()
307 * process SSDP M-SEARCH requests and responds to them */
308 void
309 ProcessSSDPRequest(int s, unsigned short port)
310 /*ProcessSSDPRequest(int s, struct lan_addr_s * lan_addr, int n_lan_addr,
311 unsigned short port)*/
313 int n;
314 char bufr[1500];
315 socklen_t len_r;
316 struct sockaddr_in sendername;
317 int i, l;
318 int lan_addr_index = 0;
319 char * st = 0;
320 int st_len = 0;
321 len_r = sizeof(struct sockaddr_in);
323 n = recvfrom(s, bufr, sizeof(bufr), 0,
324 (struct sockaddr *)&sendername, &len_r);
325 if(n < 0)
327 syslog(LOG_ERR, "recvfrom(udp): %m");
328 return;
331 if(memcmp(bufr, "NOTIFY", 6) == 0)
333 /* ignore NOTIFY packets. We could log the sender and device type */
334 return;
336 else if(memcmp(bufr, "M-SEARCH", 8) == 0)
338 i = 0;
339 while(i < n)
341 while((i < n - 1) && (bufr[i] != '\r' || bufr[i+1] != '\n'))
342 i++;
343 i += 2;
344 if((i < n - 3) && (strncasecmp(bufr+i, "st:", 3) == 0))
346 st = bufr+i+3;
347 st_len = 0;
348 while((*st == ' ' || *st == '\t') && (st < bufr + n))
349 st++;
350 while(st[st_len]!='\r' && st[st_len]!='\n'
351 && (st + st_len < bufr + n))
352 st_len++;
353 /*syslog(LOG_INFO, "ST: %.*s", st_len, st);*/
354 /*j = 0;*/
355 /*while(bufr[i+j]!='\r') j++;*/
356 /*syslog(LOG_INFO, "%.*s", j, bufr+i);*/
359 /*syslog(LOG_INFO, "SSDP M-SEARCH packet received from %s:%d",
360 inet_ntoa(sendername.sin_addr),
361 ntohs(sendername.sin_port) );*/
362 if(st && (st_len > 0))
364 /* TODO : doesnt answer at once but wait for a random time */
365 syslog(LOG_INFO, "SSDP M-SEARCH from %s:%d ST: %.*s",
366 inet_ntoa(sendername.sin_addr),
367 ntohs(sendername.sin_port),
368 st_len, st);
369 /* find in which sub network the client is */
370 for(i = 0; i<n_lan_addr; i++)
372 if( (sendername.sin_addr.s_addr & lan_addr[i].mask.s_addr)
373 == (lan_addr[i].addr.s_addr & lan_addr[i].mask.s_addr))
375 lan_addr_index = i;
376 break;
379 /* Responds to request with a device as ST header */
380 for(i = 0; known_service_types[i]; i++)
382 l = (int)strlen(known_service_types[i]);
383 if(l<=st_len && (0 == memcmp(st, known_service_types[i], l)))
385 SendSSDPAnnounce2(s, sendername,
386 st, st_len, "",
387 lan_addr[lan_addr_index].str, port);
388 break;
391 /* Responds to request with ST: ssdp:all */
392 /* strlen("ssdp:all") == 8 */
393 if(st_len==8 && (0 == memcmp(st, "ssdp:all", 8)))
395 for(i=0; known_service_types[i]; i++)
397 l = (int)strlen(known_service_types[i]);
398 SendSSDPAnnounce2(s, sendername,
399 known_service_types[i], l, i==0?"":"1",
400 lan_addr[lan_addr_index].str, port);
403 /* responds to request by UUID value */
404 l = (int)strlen(uuidvalue);
405 if(l==st_len && (0 == memcmp(st, uuidvalue, l)))
407 SendSSDPAnnounce2(s, sendername, st, st_len, "",
408 lan_addr[lan_addr_index].str, port);
411 else
413 syslog(LOG_INFO, "Invalid SSDP M-SEARCH from %s:%d",
414 inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port));
417 else
419 syslog(LOG_NOTICE, "Unknown udp packet received from %s:%d",
420 inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port));
424 /* This will broadcast ssdp:byebye notifications to inform
425 * the network that UPnP is going down. */
427 SendSSDPGoodbye(int * sockets, int n_sockets)
429 struct sockaddr_in sockname;
430 int n, l;
431 int i, j;
432 char bufr[512];
434 memset(&sockname, 0, sizeof(struct sockaddr_in));
435 sockname.sin_family = AF_INET;
436 sockname.sin_port = htons(SSDP_PORT);
437 sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
439 for(j=0; j<n_sockets; j++)
441 for(i=0; known_service_types[i]; i++)
443 l = snprintf(bufr, sizeof(bufr),
444 "NOTIFY * HTTP/1.1\r\n"
445 "HOST:%s:%d\r\n"
446 "NT:%s%s\r\n"
447 "USN:%s::%s%s\r\n"
448 "NTS:ssdp:byebye\r\n"
449 "\r\n",
450 SSDP_MCAST_ADDR, SSDP_PORT,
451 known_service_types[i], (i==0?"":"1"),
452 uuidvalue, known_service_types[i], (i==0?"":"1"));
453 n = sendto(sockets[j], bufr, l, 0,
454 (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
455 if(n < 0)
457 syslog(LOG_ERR, "sendto(udp_shutdown=%d): %m", sockets[j]);
458 return -1;
462 return 0;
465 /* SubmitServicesToMiniSSDPD() :
466 * register services offered by MiniUPnPd to a running instance of
467 * MiniSSDPd */
469 SubmitServicesToMiniSSDPD(const char * host, unsigned short port) {
470 struct sockaddr_un addr;
471 int s;
472 unsigned char buffer[2048];
473 char strbuf[256];
474 unsigned char * p;
475 int i, l;
477 s = socket(AF_UNIX, SOCK_STREAM, 0);
478 if(s < 0) {
479 syslog(LOG_ERR, "socket(unix): %m");
480 return -1;
482 addr.sun_family = AF_UNIX;
483 strncpy(addr.sun_path, minissdpdsocketpath, sizeof(addr.sun_path));
484 if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) {
485 syslog(LOG_ERR, "connect(\"%s\"): %m", minissdpdsocketpath);
486 return -1;
488 for(i = 0; known_service_types[i]; i++) {
489 buffer[0] = 4;
490 p = buffer + 1;
491 l = (int)strlen(known_service_types[i]);
492 if(i > 0)
493 l++;
494 CODELENGTH(l, p);
495 memcpy(p, known_service_types[i], l);
496 if(i > 0)
497 p[l-1] = '1';
498 p += l;
499 l = snprintf(strbuf, sizeof(strbuf), "%s::%s%s",
500 uuidvalue, known_service_types[i], (i==0)?"":"1");
501 CODELENGTH(l, p);
502 memcpy(p, strbuf, l);
503 p += l;
504 l = (int)strlen(MINIUPNPD_SERVER_STRING);
505 CODELENGTH(l, p);
506 memcpy(p, MINIUPNPD_SERVER_STRING, l);
507 p += l;
508 l = snprintf(strbuf, sizeof(strbuf), "http://%s:%u" ROOTDESC_PATH,
509 host, (unsigned int)port);
510 CODELENGTH(l, p);
511 memcpy(p, strbuf, l);
512 p += l;
513 if(write(s, buffer, p - buffer) < 0) {
514 syslog(LOG_ERR, "write(): %m");
515 return -1;
518 close(s);
519 return 0;