Annotation of embedaddon/miniupnpd/minissdp.c, revision 1.1.1.1
1.1 misho 1: /* $Id: minissdp.c,v 1.19 2010/09/21 15:31:02 nanard Exp $ */
2: /* MiniUPnP project
3: * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4: * (c) 2006-2010 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 <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"
23:
24: /* SSDP ip/port */
25: #define SSDP_PORT (1900)
26: /* Prototypes */
27: void ProcessSSDPData(int s, char *bufr, struct sockaddr_in sendername, int n, unsigned short port) ;
28:
29:
30: #define SSDP_MCAST_ADDR ("239.255.255.250")
31:
32: static int
33: AddMulticastMembership(int s, in_addr_t ifaddr)
34: {
35: struct ip_mreq imr; /* Ip multicast membership */
36:
37: /* setting up imr structure */
38: imr.imr_multiaddr.s_addr = inet_addr(SSDP_MCAST_ADDR);
39: /*imr.imr_interface.s_addr = htonl(INADDR_ANY);*/
40: imr.imr_interface.s_addr = ifaddr; /*inet_addr(ifaddr);*/
41:
42: if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&imr, sizeof(struct ip_mreq)) < 0)
43: {
44: syslog(LOG_ERR, "setsockopt(udp, IP_ADD_MEMBERSHIP): %m");
45: return -1;
46: }
47:
48: return 0;
49: }
50:
51: /* Open and configure the socket listening for
52: * SSDP udp packets sent on 239.255.255.250 port 1900 */
53: int
54: OpenAndConfSSDPReceiveSocket()
55: {
56: int s;
57: int i;
58: int j = 1;
59: struct sockaddr_in sockname;
60:
61: if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
62: {
63: syslog(LOG_ERR, "socket(udp): %m");
64: return -1;
65: }
66:
67: memset(&sockname, 0, sizeof(struct sockaddr_in));
68: sockname.sin_family = AF_INET;
69: sockname.sin_port = htons(SSDP_PORT);
70: /* NOTE : it seems it doesnt work when binding on the specific address */
71: /*sockname.sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);*/
72: sockname.sin_addr.s_addr = htonl(INADDR_ANY);
73: /*sockname.sin_addr.s_addr = inet_addr(ifaddr);*/
74:
75: if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &j, sizeof(j)) < 0)
76: {
77: syslog(LOG_WARNING, "setsockopt(udp, SO_REUSEADDR): %m");
78: }
79:
80:
81: if(bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
82: {
83: syslog(LOG_ERR, "bind(udp): %m");
84: close(s);
85: return -1;
86: }
87:
88: i = n_lan_addr;
89: while(i>0)
90: {
91: i--;
92: if(AddMulticastMembership(s, lan_addr[i].addr.s_addr) < 0)
93: {
94: syslog(LOG_WARNING,
95: "Failed to add multicast membership for address %s",
96: lan_addr[i].str );
97: }
98: }
99:
100: return s;
101: }
102:
103: /* open the UDP socket used to send SSDP notifications to
104: * the multicast group reserved for them */
105: static int
106: OpenAndConfSSDPNotifySocket(in_addr_t addr)
107: {
108: int s;
109: unsigned char loopchar = 0;
110: int bcast = 1;
111: struct in_addr mc_if;
112: struct sockaddr_in sockname;
113:
114: if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
115: {
116: syslog(LOG_ERR, "socket(udp_notify): %m");
117: return -1;
118: }
119:
120: mc_if.s_addr = addr; /*inet_addr(addr);*/
121:
122: if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loopchar, sizeof(loopchar)) < 0)
123: {
124: syslog(LOG_ERR, "setsockopt(udp_notify, IP_MULTICAST_LOOP): %m");
125: close(s);
126: return -1;
127: }
128:
129: if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&mc_if, sizeof(mc_if)) < 0)
130: {
131: syslog(LOG_ERR, "setsockopt(udp_notify, IP_MULTICAST_IF): %m");
132: close(s);
133: return -1;
134: }
135:
136: if(setsockopt(s, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bcast)) < 0)
137: {
138: syslog(LOG_ERR, "setsockopt(udp_notify, SO_BROADCAST): %m");
139: close(s);
140: return -1;
141: }
142:
143: memset(&sockname, 0, sizeof(struct sockaddr_in));
144: sockname.sin_family = AF_INET;
145: sockname.sin_addr.s_addr = addr; /*inet_addr(addr);*/
146:
147: if (bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
148: {
149: syslog(LOG_ERR, "bind(udp_notify): %m");
150: close(s);
151: return -1;
152: }
153:
154: return s;
155: }
156:
157: int
158: OpenAndConfSSDPNotifySockets(int * sockets)
159: /*OpenAndConfSSDPNotifySockets(int * sockets,
160: struct lan_addr_s * lan_addr, int n_lan_addr)*/
161: {
162: int i, j;
163: for(i=0; i<n_lan_addr; i++)
164: {
165: sockets[i] = OpenAndConfSSDPNotifySocket(lan_addr[i].addr.s_addr);
166: if(sockets[i] < 0)
167: {
168: for(j=0; j<i; j++)
169: {
170: close(sockets[j]);
171: sockets[j] = -1;
172: }
173: return -1;
174: }
175: }
176: return 0;
177: }
178:
179: /*
180: * response from a LiveBox (Wanadoo)
181: HTTP/1.1 200 OK
182: CACHE-CONTROL: max-age=1800
183: DATE: Thu, 01 Jan 1970 04:03:23 GMT
184: EXT:
185: LOCATION: http://192.168.0.1:49152/gatedesc.xml
186: SERVER: Linux/2.4.17, UPnP/1.0, Intel SDK for UPnP devices /1.2
187: ST: upnp:rootdevice
188: USN: uuid:75802409-bccb-40e7-8e6c-fa095ecce13e::upnp:rootdevice
189:
190: * response from a Linksys 802.11b :
191: HTTP/1.1 200 OK
192: Cache-Control:max-age=120
193: Location:http://192.168.5.1:5678/rootDesc.xml
194: Server:NT/5.0 UPnP/1.0
195: ST:upnp:rootdevice
196: USN:uuid:upnp-InternetGatewayDevice-1_0-0090a2777777::upnp:rootdevice
197: EXT:
198: */
199:
200: /* not really an SSDP "announce" as it is the response
201: * to a SSDP "M-SEARCH" */
202: static void
203: SendSSDPAnnounce2(int s, struct sockaddr_in sockname,
204: const char * st, int st_len, const char * suffix,
205: const char * host, unsigned short port)
206: {
207: int l, n;
208: char buf[512];
209: /*
210: * follow guideline from document "UPnP Device Architecture 1.0"
211: * uppercase is recommended.
212: * DATE: is recommended
213: * SERVER: OS/ver UPnP/1.0 miniupnpd/1.0
214: * - check what to put in the 'Cache-Control' header
215: * */
216: l = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\n"
217: "CACHE-CONTROL: max-age=120\r\n"
218: /*"DATE: ...\r\n"*/
219: "ST: %.*s%s\r\n"
220: "USN: %s::%.*s%s\r\n"
221: "EXT:\r\n"
222: "SERVER: " MINIUPNPD_SERVER_STRING "\r\n"
223: "LOCATION: http://%s:%u" ROOTDESC_PATH "\r\n"
224: "\r\n",
225: st_len, st, suffix,
226: uuidvalue, st_len, st, suffix,
227: host, (unsigned int)port);
228: n = sendto(s, buf, l, 0,
229: (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
230: syslog(LOG_INFO, "SSDP Announce %d bytes to %s:%d ST: %.*s",n,
231: inet_ntoa(sockname.sin_addr),
232: ntohs(sockname.sin_port),
233: l, buf);
234: if(n < 0)
235: {
236: syslog(LOG_ERR, "sendto(udp): %m");
237: }
238: }
239:
240: static const char * const known_service_types[] =
241: {
242: "upnp:rootdevice",
243: "urn:schemas-upnp-org:device:InternetGatewayDevice:",
244: "urn:schemas-upnp-org:device:WANConnectionDevice:",
245: "urn:schemas-upnp-org:device:WANDevice:",
246: "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:",
247: "urn:schemas-upnp-org:service:WANIPConnection:",
248: "urn:schemas-upnp-org:service:WANPPPConnection:",
249: "urn:schemas-upnp-org:service:Layer3Forwarding:",
250: 0
251: };
252:
253: static void
254: SendSSDPNotifies(int s, const char * host, unsigned short port,
255: unsigned int lifetime)
256: {
257: struct sockaddr_in sockname;
258: int l, n, i=0;
259: char bufr[512];
260:
261: memset(&sockname, 0, sizeof(struct sockaddr_in));
262: sockname.sin_family = AF_INET;
263: sockname.sin_port = htons(SSDP_PORT);
264: sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
265:
266: while(known_service_types[i])
267: {
268: l = snprintf(bufr, sizeof(bufr),
269: "NOTIFY * HTTP/1.1\r\n"
270: "HOST:%s:%d\r\n"
271: "Cache-Control:max-age=%u\r\n"
272: "Location:http://%s:%d" ROOTDESC_PATH"\r\n"
273: /*"Server:miniupnpd/1.0 UPnP/1.0\r\n"*/
274: "Server: " MINIUPNPD_SERVER_STRING "\r\n"
275: "NT:%s%s\r\n"
276: "USN:%s::%s%s\r\n"
277: "NTS:ssdp:alive\r\n"
278: "\r\n",
279: SSDP_MCAST_ADDR, SSDP_PORT,
280: lifetime,
281: host, port,
282: known_service_types[i], (i==0?"":"1"),
283: uuidvalue, known_service_types[i], (i==0?"":"1") );
284: if(l>=sizeof(bufr))
285: {
286: syslog(LOG_WARNING, "SendSSDPNotifies(): truncated output");
287: l = sizeof(bufr);
288: }
289: n = sendto(s, bufr, l, 0,
290: (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
291: if(n < 0)
292: {
293: syslog(LOG_ERR, "sendto(udp_notify=%d, %s): %m", s, host);
294: }
295: i++;
296: }
297: }
298:
299: void
300: SendSSDPNotifies2(int * sockets,
301: unsigned short port,
302: unsigned int lifetime)
303: /*SendSSDPNotifies2(int * sockets, struct lan_addr_s * lan_addr, int n_lan_addr,
304: unsigned short port,
305: unsigned int lifetime)*/
306: {
307: int i;
308: for(i=0; i<n_lan_addr; i++)
309: {
310: SendSSDPNotifies(sockets[i], lan_addr[i].str, port, lifetime);
311: }
312: }
313:
314: /* ProcessSSDPRequest()
315: * process SSDP M-SEARCH requests and responds to them */
316: void
317: ProcessSSDPRequest(int s, unsigned short port)
318: {
319: int n;
320: char bufr[1500];
321: socklen_t len_r;
322: struct sockaddr_in sendername;
323: len_r = sizeof(struct sockaddr_in);
324:
325: n = recvfrom(s, bufr, sizeof(bufr), 0,
326: (struct sockaddr *)&sendername, &len_r);
327: if(n < 0)
328: {
329: syslog(LOG_ERR, "recvfrom(udp): %m");
330: return;
331: }
332: ProcessSSDPData(s, bufr, sendername, n, port);
333:
334: }
335:
336: void ProcessSSDPData(int s, char *bufr, struct sockaddr_in sendername, int n, unsigned short port) {
337: int i, l;
338: int lan_addr_index = 0;
339: char * st = 0;
340: int st_len = 0;
341:
342:
343: if(memcmp(bufr, "NOTIFY", 6) == 0)
344: {
345: /* ignore NOTIFY packets. We could log the sender and device type */
346: return;
347: }
348: else if(memcmp(bufr, "M-SEARCH", 8) == 0)
349: {
350: i = 0;
351: while(i < n)
352: {
353: while((i < n - 1) && (bufr[i] != '\r' || bufr[i+1] != '\n'))
354: i++;
355: i += 2;
356: if((i < n - 3) && (strncasecmp(bufr+i, "st:", 3) == 0))
357: {
358: st = bufr+i+3;
359: st_len = 0;
360: while((*st == ' ' || *st == '\t') && (st < bufr + n))
361: st++;
362: while(st[st_len]!='\r' && st[st_len]!='\n'
363: && (st + st_len < bufr + n))
364: st_len++;
365: /*syslog(LOG_INFO, "ST: %.*s", st_len, st);*/
366: /*j = 0;*/
367: /*while(bufr[i+j]!='\r') j++;*/
368: /*syslog(LOG_INFO, "%.*s", j, bufr+i);*/
369: }
370: }
371: /*syslog(LOG_INFO, "SSDP M-SEARCH packet received from %s:%d",
372: inet_ntoa(sendername.sin_addr),
373: ntohs(sendername.sin_port) );*/
374: if(st && (st_len > 0))
375: {
376: /* TODO : doesnt answer at once but wait for a random time */
377: syslog(LOG_INFO, "SSDP M-SEARCH from %s:%d ST: %.*s",
378: inet_ntoa(sendername.sin_addr),
379: ntohs(sendername.sin_port),
380: st_len, st);
381: /* find in which sub network the client is */
382: for(i = 0; i<n_lan_addr; i++)
383: {
384: if( (sendername.sin_addr.s_addr & lan_addr[i].mask.s_addr)
385: == (lan_addr[i].addr.s_addr & lan_addr[i].mask.s_addr))
386: {
387: lan_addr_index = i;
388: break;
389: }
390: }
391: /* Responds to request with a device as ST header */
392: for(i = 0; known_service_types[i]; i++)
393: {
394: l = (int)strlen(known_service_types[i]);
395: if(l<=st_len && (0 == memcmp(st, known_service_types[i], l)))
396: {
397: syslog(LOG_INFO, "Single search found");
398: SendSSDPAnnounce2(s, sendername,
399: st, st_len, "",
400: lan_addr[lan_addr_index].str, port);
401: break;
402: }
403: }
404: /* Responds to request with ST: ssdp:all */
405: /* strlen("ssdp:all") == 8 */
406: if(st_len==8 && (0 == memcmp(st, "ssdp:all", 8)))
407: {
408: syslog(LOG_INFO, "ssdp:all found");
409: for(i=0; known_service_types[i]; i++)
410: {
411: l = (int)strlen(known_service_types[i]);
412: SendSSDPAnnounce2(s, sendername,
413: known_service_types[i], l, i==0?"":"1",
414: lan_addr[lan_addr_index].str, port);
415: }
416: }
417: /* responds to request by UUID value */
418: l = (int)strlen(uuidvalue);
419: if(l==st_len && (0 == memcmp(st, uuidvalue, l)))
420: {
421: syslog(LOG_INFO, "ssdp:uuid found");
422: SendSSDPAnnounce2(s, sendername, st, st_len, "",
423: lan_addr[lan_addr_index].str, port);
424: }
425: }
426: else
427: {
428: syslog(LOG_INFO, "Invalid SSDP M-SEARCH from %s:%d",
429: inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port));
430: }
431: }
432: else
433: {
434: syslog(LOG_NOTICE, "Unknown udp packet received from %s:%d",
435: inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port));
436: }
437: }
438:
439: /* This will broadcast ssdp:byebye notifications to inform
440: * the network that UPnP is going down. */
441: int
442: SendSSDPGoodbye(int * sockets, int n_sockets)
443: {
444: struct sockaddr_in sockname;
445: int n, l;
446: int i, j;
447: char bufr[512];
448:
449: memset(&sockname, 0, sizeof(struct sockaddr_in));
450: sockname.sin_family = AF_INET;
451: sockname.sin_port = htons(SSDP_PORT);
452: sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
453:
454: for(j=0; j<n_sockets; j++)
455: {
456: for(i=0; known_service_types[i]; i++)
457: {
458: l = snprintf(bufr, sizeof(bufr),
459: "NOTIFY * HTTP/1.1\r\n"
460: "HOST:%s:%d\r\n"
461: "NT:%s%s\r\n"
462: "USN:%s::%s%s\r\n"
463: "NTS:ssdp:byebye\r\n"
464: "\r\n",
465: SSDP_MCAST_ADDR, SSDP_PORT,
466: known_service_types[i], (i==0?"":"1"),
467: uuidvalue, known_service_types[i], (i==0?"":"1"));
468: n = sendto(sockets[j], bufr, l, 0,
469: (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
470: if(n < 0)
471: {
472: syslog(LOG_ERR, "SendSSDPGoodbye: sendto(udp_shutdown=%d): %m",
473: sockets[j]);
474: return -1;
475: }
476: }
477: }
478: return 0;
479: }
480:
481: /* SubmitServicesToMiniSSDPD() :
482: * register services offered by MiniUPnPd to a running instance of
483: * MiniSSDPd */
484: int
485: SubmitServicesToMiniSSDPD(const char * host, unsigned short port) {
486: struct sockaddr_un addr;
487: int s;
488: unsigned char buffer[2048];
489: char strbuf[256];
490: unsigned char * p;
491: int i, l;
492:
493: s = socket(AF_UNIX, SOCK_STREAM, 0);
494: if(s < 0) {
495: syslog(LOG_ERR, "socket(unix): %m");
496: return -1;
497: }
498: addr.sun_family = AF_UNIX;
499: strncpy(addr.sun_path, minissdpdsocketpath, sizeof(addr.sun_path));
500: if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) {
501: syslog(LOG_ERR, "connect(\"%s\"): %m", minissdpdsocketpath);
502: return -1;
503: }
504: for(i = 0; known_service_types[i]; i++) {
505: buffer[0] = 4;
506: p = buffer + 1;
507: l = (int)strlen(known_service_types[i]);
508: if(i > 0)
509: l++;
510: CODELENGTH(l, p);
511: memcpy(p, known_service_types[i], l);
512: if(i > 0)
513: p[l-1] = '1';
514: p += l;
515: l = snprintf(strbuf, sizeof(strbuf), "%s::%s%s",
516: uuidvalue, known_service_types[i], (i==0)?"":"1");
517: CODELENGTH(l, p);
518: memcpy(p, strbuf, l);
519: p += l;
520: l = (int)strlen(MINIUPNPD_SERVER_STRING);
521: CODELENGTH(l, p);
522: memcpy(p, MINIUPNPD_SERVER_STRING, l);
523: p += l;
524: l = snprintf(strbuf, sizeof(strbuf), "http://%s:%u" ROOTDESC_PATH,
525: host, (unsigned int)port);
526: CODELENGTH(l, p);
527: memcpy(p, strbuf, l);
528: p += l;
529: if(write(s, buffer, p - buffer) < 0) {
530: syslog(LOG_ERR, "write(): %m");
531: return -1;
532: }
533: }
534: close(s);
535: return 0;
536: }
537:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>