1: /* $Id: minissdpd.c,v 1.1.1.1 2023/09/27 11:25:11 misho Exp $ */
2: /* vim: tabstop=4 shiftwidth=4 noexpandtab
3: * MiniUPnP project
4: * (c) 2007-2022 Thomas Bernard
5: * website : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
6: * This software is subject to the conditions detailed
7: * in the LICENCE file provided within the distribution */
8:
9: #include "config.h"
10:
11: #include <stdlib.h>
12: #include <stdio.h>
13: #include <string.h>
14: #include <signal.h>
15: #include <errno.h>
16: #include <sys/time.h>
17: #include <sys/types.h>
18: #include <sys/socket.h>
19: #include <unistd.h>
20: #include <netinet/in.h>
21: #include <arpa/inet.h>
22: #include <syslog.h>
23: #include <ctype.h>
24: #include <time.h>
25: #include <sys/queue.h>
26: /* for chmod : */
27: #include <sys/stat.h>
28: /* unix sockets */
29: #include <sys/un.h>
30: /* for getpwnam() and getgrnam() */
31: #if 0
32: #include <pwd.h>
33: #include <grp.h>
34: #endif
35:
36: /* LOG_PERROR does not exist on Solaris */
37: #ifndef LOG_PERROR
38: #define LOG_PERROR 0
39: #endif /* LOG_PERROR */
40:
41: #include "getifaddr.h"
42: #include "upnputils.h"
43: #include "openssdpsocket.h"
44: #include "daemonize.h"
45: #include "codelength.h"
46: #include "ifacewatch.h"
47: #include "minissdpdtypes.h"
48: #include "asyncsendto.h"
49:
50: #define SET_MAX(max, x) if((x) > (max)) (max) = (x)
51: #ifndef MIN
52: #define MIN(x,y) (((x)<(y))?(x):(y))
53: #endif
54:
55: /* current request management structure */
56: struct reqelem {
57: int socket;
58: int is_notify; /* has subscribed to notifications */
59: LIST_ENTRY(reqelem) entries;
60: unsigned char * output_buffer;
61: int output_buffer_offset;
62: int output_buffer_len;
63: };
64:
65: /* device data structures */
66: struct header {
67: const char * p; /* string pointer */
68: int l; /* string length */
69: };
70:
71: #define HEADER_NT 0
72: #define HEADER_USN 1
73: #define HEADER_LOCATION 2
74:
75: struct device {
76: struct device * next;
77: time_t t; /* validity time */
78: struct header headers[3]; /* NT, USN and LOCATION headers */
79: char data[];
80: };
81:
82: /* Services stored for answering to M-SEARCH */
83: struct service {
84: char * st; /* Service type */
85: char * usn; /* Unique identifier */
86: char * server; /* Server string */
87: char * location; /* URL */
88: LIST_ENTRY(service) entries;
89: };
90: LIST_HEAD(servicehead, service) servicelisthead;
91:
92: #define NTS_SSDP_ALIVE 1
93: #define NTS_SSDP_BYEBYE 2
94: #define NTS_SSDP_UPDATE 3
95:
96: /* request types */
97: enum request_type {
98: MINISSDPD_GET_VERSION = 0,
99: MINISSDPD_SEARCH_TYPE = 1,
100: MINISSDPD_SEARCH_USN = 2,
101: MINISSDPD_SEARCH_ALL = 3,
102: MINISSDPD_SUBMIT = 4,
103: MINISSDPD_NOTIF = 5
104: };
105:
106: /* discovered device list kept in memory */
107: struct device * devlist = 0;
108:
109: /* bootid and configid */
110: unsigned int upnp_bootid = 1;
111: unsigned int upnp_configid = 1337;
112:
113: /* LAN interfaces/addresses */
114: struct lan_addr_list lan_addrs;
115:
116: /* connected clients */
117: LIST_HEAD(reqstructhead, reqelem) reqlisthead;
118:
119: /* functions prototypes */
120:
121: #define NOTIF_NEW 1
122: #define NOTIF_UPDATE 2
123: #define NOTIF_REMOVE 3
124: static void
125: sendNotifications(int notif_type, const struct device * dev, const struct service * serv);
126:
127: /* functions */
128:
129: /* parselanaddr()
130: * parse address with mask
131: * ex: 192.168.1.1/24 or 192.168.1.1/255.255.255.0
132: *
133: * Can also use the interface name (ie eth0)
134: *
135: * return value :
136: * 0 : ok
137: * -1 : error */
138: static int
139: parselanaddr(struct lan_addr_s * lan_addr, const char * str)
140: {
141: const char * p;
142: int n;
143: char tmp[16];
144:
145: memset(lan_addr, 0, sizeof(struct lan_addr_s));
146: p = str;
147: while(*p && *p != '/' && !isspace(*p))
148: p++;
149: n = p - str;
150: if(!isdigit(str[0]) && n < (int)sizeof(lan_addr->ifname)) {
151: /* not starting with a digit : suppose it is an interface name */
152: memcpy(lan_addr->ifname, str, n);
153: lan_addr->ifname[n] = '\0';
154: if(getifaddr(lan_addr->ifname, lan_addr->str, sizeof(lan_addr->str),
155: &lan_addr->addr, &lan_addr->mask) < 0)
156: goto parselan_error;
157: /*printf("%s => %s\n", lan_addr->ifname, lan_addr->str);*/
158: } else {
159: if(n>15)
160: goto parselan_error;
161: memcpy(lan_addr->str, str, n);
162: lan_addr->str[n] = '\0';
163: if(!inet_aton(lan_addr->str, &lan_addr->addr))
164: goto parselan_error;
165: }
166: if(*p == '/') {
167: const char * q = ++p;
168: while(*p && isdigit(*p))
169: p++;
170: if(*p=='.') {
171: /* parse mask in /255.255.255.0 format */
172: while(*p && (*p=='.' || isdigit(*p)))
173: p++;
174: n = p - q;
175: if(n>15)
176: goto parselan_error;
177: memcpy(tmp, q, n);
178: tmp[n] = '\0';
179: if(!inet_aton(tmp, &lan_addr->mask))
180: goto parselan_error;
181: } else {
182: /* it is a /24 format */
183: int nbits = atoi(q);
184: if(nbits > 32 || nbits < 0)
185: goto parselan_error;
186: lan_addr->mask.s_addr = htonl(nbits ? (0xffffffffu << (32 - nbits)) : 0);
187: }
188: } else if(lan_addr->mask.s_addr == 0) {
189: /* by default, networks are /24 */
190: lan_addr->mask.s_addr = htonl(0xffffff00u);
191: }
192: #ifdef ENABLE_IPV6
193: if(lan_addr->ifname[0] != '\0') {
194: lan_addr->index = if_nametoindex(lan_addr->ifname);
195: if(lan_addr->index == 0)
196: fprintf(stderr, "Cannot get index for network interface %s\n",
197: lan_addr->ifname);
198: } else {
199: fprintf(stderr,
200: "Error: please specify LAN network interface by name instead of IPv4 address : %s\n",
201: str);
202: return -1;
203: }
204: #endif /* ENABLE_IPV6 */
205: return 0;
206: parselan_error:
207: fprintf(stderr, "Error parsing address/mask (or interface name) : %s\n",
208: str);
209: return -1;
210: }
211:
212: static int
213: write_buffer(struct reqelem * req)
214: {
215: if(req->output_buffer && req->output_buffer_len > 0) {
216: int n = write(req->socket,
217: req->output_buffer + req->output_buffer_offset,
218: req->output_buffer_len);
219: if(n >= 0) {
220: req->output_buffer_offset += n;
221: req->output_buffer_len -= n;
222: } else if(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) {
223: return 0;
224: }
225: return n;
226: } else {
227: return 0;
228: }
229: }
230:
231: static int
232: add_to_buffer(struct reqelem * req, const unsigned char * data, int len)
233: {
234: unsigned char * tmp;
235: if(req->output_buffer_offset > 0) {
236: memmove(req->output_buffer, req->output_buffer + req->output_buffer_offset, req->output_buffer_len);
237: req->output_buffer_offset = 0;
238: }
239: tmp = realloc(req->output_buffer, req->output_buffer_len + len);
240: if(tmp == NULL) {
241: syslog(LOG_ERR, "%s: failed to allocate %d bytes",
242: __func__, req->output_buffer_len + len);
243: return -1;
244: }
245: req->output_buffer = tmp;
246: memcpy(req->output_buffer + req->output_buffer_len, data, len);
247: req->output_buffer_len += len;
248: return len;
249: }
250:
251: static int
252: write_or_buffer(struct reqelem * req, const unsigned char * data, int len)
253: {
254: if(write_buffer(req) < 0)
255: return -1;
256: if(req->output_buffer && req->output_buffer_len > 0) {
257: return add_to_buffer(req, data, len);
258: } else {
259: int n = write(req->socket, data, len);
260: if(n == len)
261: return len;
262: if(n < 0) {
263: if(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) {
264: n = add_to_buffer(req, data, len);
265: if(n < 0) return n;
266: } else {
267: return n;
268: }
269: } else {
270: n = add_to_buffer(req, data + n, len - n);
271: if(n < 0) return n;
272: }
273: }
274: return len;
275: }
276:
277: static const char *
278: nts_to_str(int nts)
279: {
280: switch(nts)
281: {
282: case NTS_SSDP_ALIVE:
283: return "ssdp:alive";
284: case NTS_SSDP_BYEBYE:
285: return "ssdp:byebye";
286: case NTS_SSDP_UPDATE:
287: return "ssdp:update";
288: }
289: return "unknown";
290: }
291:
292: /* updateDevice() :
293: * adds or updates the device to the list.
294: * return value :
295: * 0 : the device was updated (or nothing done)
296: * 1 : the device was new */
297: static int
298: updateDevice(const struct header * headers, time_t t)
299: {
300: struct device ** pp = &devlist;
301: struct device * p = *pp; /* = devlist; */
302: while(p)
303: {
304: if( p->headers[HEADER_NT].l == headers[HEADER_NT].l
305: && (0==memcmp(p->headers[HEADER_NT].p, headers[HEADER_NT].p, headers[HEADER_NT].l))
306: && p->headers[HEADER_USN].l == headers[HEADER_USN].l
307: && (0==memcmp(p->headers[HEADER_USN].p, headers[HEADER_USN].p, headers[HEADER_USN].l)) )
308: {
309: /*printf("found! %d\n", (int)(t - p->t));*/
310: syslog(LOG_DEBUG, "device updated : %.*s", headers[HEADER_USN].l, headers[HEADER_USN].p);
311: p->t = t;
312: /* update Location ! */
313: if(headers[HEADER_LOCATION].l > p->headers[HEADER_LOCATION].l)
314: {
315: struct device * tmp;
316: tmp = realloc(p, sizeof(struct device)
317: + headers[0].l+headers[1].l+headers[2].l);
318: if(!tmp) /* allocation error */
319: {
320: syslog(LOG_ERR, "updateDevice() : memory allocation error");
321: *pp = p->next; /* remove "p" from the list */
322: free(p);
323: return 0;
324: }
325: p = tmp;
326: *pp = p;
327: }
328: memcpy(p->data + p->headers[0].l + p->headers[1].l,
329: headers[2].p, headers[2].l);
330: /* TODO : check p->headers[HEADER_LOCATION].l */
331: return 0;
332: }
333: pp = &p->next;
334: p = *pp; /* p = p->next; */
335: }
336: syslog(LOG_INFO, "new device discovered : %.*s",
337: headers[HEADER_USN].l, headers[HEADER_USN].p);
338: /* add */
339: {
340: char * pc;
341: int i;
342: p = malloc( sizeof(struct device)
343: + headers[0].l+headers[1].l+headers[2].l );
344: if(!p) {
345: syslog(LOG_ERR, "updateDevice(): cannot allocate memory");
346: return -1;
347: }
348: p->next = devlist;
349: p->t = t;
350: pc = p->data;
351: for(i = 0; i < 3; i++)
352: {
353: p->headers[i].p = pc;
354: p->headers[i].l = headers[i].l;
355: memcpy(pc, headers[i].p, headers[i].l);
356: pc += headers[i].l;
357: }
358: devlist = p;
359: sendNotifications(NOTIF_NEW, p, NULL);
360: }
361: return 1;
362: }
363:
364: /* removeDevice() :
365: * remove a device from the list
366: * return value :
367: * 0 : no device removed
368: * -1 : device removed */
369: static int
370: removeDevice(const struct header * headers)
371: {
372: struct device ** pp = &devlist;
373: struct device * p = *pp; /* = devlist */
374: while(p)
375: {
376: if( p->headers[HEADER_NT].l == headers[HEADER_NT].l
377: && (0==memcmp(p->headers[HEADER_NT].p, headers[HEADER_NT].p, headers[HEADER_NT].l))
378: && p->headers[HEADER_USN].l == headers[HEADER_USN].l
379: && (0==memcmp(p->headers[HEADER_USN].p, headers[HEADER_USN].p, headers[HEADER_USN].l)) )
380: {
381: syslog(LOG_INFO, "remove device : %.*s", headers[HEADER_USN].l, headers[HEADER_USN].p);
382: sendNotifications(NOTIF_REMOVE, p, NULL);
383: *pp = p->next;
384: free(p);
385: return -1;
386: }
387: pp = &p->next;
388: p = *pp; /* p = p->next; */
389: }
390: syslog(LOG_WARNING, "device not found for removing : %.*s", headers[HEADER_USN].l, headers[HEADER_USN].p);
391: return 0;
392: }
393:
394: /* sent notifications to client having subscribed */
395: static void
396: sendNotifications(int notif_type, const struct device * dev, const struct service * serv)
397: {
398: struct reqelem * req;
399: unsigned int m;
400: unsigned char rbuf[RESPONSE_BUFFER_SIZE];
401: unsigned char * rp;
402:
403: for(req = reqlisthead.lh_first; req; req = req->entries.le_next) {
404: if(!req->is_notify) continue;
405: rbuf[0] = '\xff'; /* special code for notifications */
406: rbuf[1] = (unsigned char)notif_type;
407: rbuf[2] = 0;
408: rp = rbuf + 3;
409: if(dev) {
410: /* response :
411: * 1 - Location
412: * 2 - NT (device/service type)
413: * 3 - usn */
414: m = dev->headers[HEADER_LOCATION].l;
415: CODELENGTH(m, rp);
416: memcpy(rp, dev->headers[HEADER_LOCATION].p, dev->headers[HEADER_LOCATION].l);
417: rp += dev->headers[HEADER_LOCATION].l;
418: m = dev->headers[HEADER_NT].l;
419: CODELENGTH(m, rp);
420: memcpy(rp, dev->headers[HEADER_NT].p, dev->headers[HEADER_NT].l);
421: rp += dev->headers[HEADER_NT].l;
422: m = dev->headers[HEADER_USN].l;
423: CODELENGTH(m, rp);
424: memcpy(rp, dev->headers[HEADER_USN].p, dev->headers[HEADER_USN].l);
425: rp += dev->headers[HEADER_USN].l;
426: rbuf[2]++;
427: }
428: if(serv) {
429: /* response :
430: * 1 - Location
431: * 2 - NT (device/service type)
432: * 3 - usn */
433: m = strlen(serv->location);
434: CODELENGTH(m, rp);
435: memcpy(rp, serv->location, m);
436: rp += m;
437: m = strlen(serv->st);
438: CODELENGTH(m, rp);
439: memcpy(rp, serv->st, m);
440: rp += m;
441: m = strlen(serv->usn);
442: CODELENGTH(m, rp);
443: memcpy(rp, serv->usn, m);
444: rp += m;
445: rbuf[2]++;
446: }
447: if(rbuf[2] > 0) {
448: if(write_or_buffer(req, rbuf, rp - rbuf) < 0) {
449: syslog(LOG_ERR, "(s=%d) write: %m", req->socket);
450: /*goto error;*/
451: }
452: }
453: }
454: }
455:
456: /* SendSSDPMSEARCHResponse() :
457: * build and send response to M-SEARCH SSDP packets. */
458: static void
459: SendSSDPMSEARCHResponse(int s, const struct sockaddr * sockname,
460: const char * st, size_t st_len, const char * usn,
461: const char * server, const char * location)
462: {
463: int l, n;
464: char buf[1024];
465: socklen_t sockname_len;
466: /*
467: * follow guideline from document "UPnP Device Architecture 1.0"
468: * uppercase is recommended.
469: * DATE: is recommended
470: * SERVER: OS/ver UPnP/1.0 miniupnpd/1.0
471: * - check what to put in the 'Cache-Control' header
472: *
473: * have a look at the document "UPnP Device Architecture v1.1 */
474: l = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\n"
475: "CACHE-CONTROL: max-age=120\r\n"
476: /*"DATE: ...\r\n"*/
477: "ST: %.*s\r\n"
478: "USN: %s\r\n"
479: "EXT:\r\n"
480: "SERVER: %s\r\n"
481: "LOCATION: %s\r\n"
482: "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */
483: "01-NLS: %u\r\n" /* same as BOOTID. UDA v1.1 */
484: "BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
485: "CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
486: "\r\n",
487: (int)st_len, st, usn,
488: server, location,
489: upnp_bootid, upnp_bootid, upnp_configid);
490: #ifdef ENABLE_IPV6
491: sockname_len = (sockname->sa_family == PF_INET6)
492: ? sizeof(struct sockaddr_in6)
493: : sizeof(struct sockaddr_in);
494: #else /* ENABLE_IPV6 */
495: sockname_len = sizeof(struct sockaddr_in);
496: #endif /* ENABLE_IPV6 */
497: n = sendto_or_schedule(s, buf, l, 0, sockname, sockname_len);
498: if(n < 0) {
499: syslog(LOG_ERR, "%s: sendto(udp): %m", __func__);
500: }
501: }
502:
503: /* Process M-SEARCH requests */
504: static void
505: processMSEARCH(int s, const char * st, size_t st_len,
506: const struct sockaddr * addr)
507: {
508: struct service * serv;
509: #ifdef ENABLE_IPV6
510: char buf[64];
511: #endif /* ENABLE_IPV6 */
512:
513: if(!st || st_len==0)
514: return;
515: #ifdef ENABLE_IPV6
516: sockaddr_to_string(addr, buf, sizeof(buf));
517: syslog(LOG_INFO, "SSDP M-SEARCH from %s ST:%.*s",
518: buf, (int)st_len, st);
519: #else /* ENABLE_IPV6 */
520: syslog(LOG_INFO, "SSDP M-SEARCH from %s:%d ST: %.*s",
521: inet_ntoa(((const struct sockaddr_in *)addr)->sin_addr),
522: ntohs(((const struct sockaddr_in *)addr)->sin_port),
523: (int)st_len, st);
524: #endif /* ENABLE_IPV6 */
525: if(st_len==8 && (0==memcmp(st, "ssdp:all", 8))) {
526: /* send a response for all services */
527: for(serv = servicelisthead.lh_first;
528: serv;
529: serv = serv->entries.le_next) {
530: SendSSDPMSEARCHResponse(s, addr,
531: serv->st, strlen(serv->st), serv->usn,
532: serv->server, serv->location);
533: }
534: } else if(st_len > 5 && (0==memcmp(st, "uuid:", 5))) {
535: /* find a matching UUID value */
536: for(serv = servicelisthead.lh_first;
537: serv;
538: serv = serv->entries.le_next) {
539: if(0 == strncmp(serv->usn, st, st_len)) {
540: SendSSDPMSEARCHResponse(s, addr,
541: serv->st, strlen(serv->st), serv->usn,
542: serv->server, serv->location);
543: }
544: }
545: } else {
546: size_t l;
547: int st_ver = 0;
548: char atoi_buffer[8];
549:
550: /* remove version at the end of the ST string */
551: for (l = st_len; l > 0; l--) {
552: if (st[l-1] == ':') {
553: memset(atoi_buffer, 0, sizeof(atoi_buffer));
554: memcpy(atoi_buffer, st + l, MIN((sizeof(atoi_buffer) - 1), st_len - l));
555: st_ver = atoi(atoi_buffer);
556: break;
557: }
558: }
559: if (l == 0)
560: l = st_len;
561: /* answer for each matching service */
562: /* From UPnP Device Architecture v1.1 :
563: * 1.3.2 [...] Updated versions of device and service types
564: * are REQUIRED to be full backward compatible with
565: * previous versions. Devices MUST respond to M-SEARCH
566: * requests for any supported version. For example, if a
567: * device implements “urn:schemas-upnporg:service:xyz:2”,
568: * it MUST respond to search requests for both that type
569: * and “urn:schemas-upnp-org:service:xyz:1”. The response
570: * MUST specify the same version as was contained in the
571: * search request. [...] */
572: for(serv = servicelisthead.lh_first;
573: serv;
574: serv = serv->entries.le_next) {
575: if(0 == strncmp(serv->st, st, l)) {
576: syslog(LOG_DEBUG, "Found matching service : %s %s (v %d)", serv->st, serv->location, st_ver);
577: SendSSDPMSEARCHResponse(s, addr,
578: st, st_len, serv->usn,
579: serv->server, serv->location);
580: }
581: }
582: }
583: }
584:
585: /**
586: * helper function.
587: * reject any non ASCII or non printable character.
588: */
589: static int
590: containsForbiddenChars(const unsigned char * p, int len)
591: {
592: while(len > 0) {
593: if(*p < ' ' || *p >= '\x7f')
594: return 1;
595: p++;
596: len--;
597: }
598: return 0;
599: }
600:
601: #define METHOD_MSEARCH 1
602: #define METHOD_NOTIFY 2
603:
604: /* ParseSSDPPacket() :
605: * parse a received SSDP Packet and call
606: * updateDevice() or removeDevice() as needed
607: * return value :
608: * -1 : a device was removed
609: * 0 : no device removed nor added
610: * 1 : a device was added. */
611: static int
612: ParseSSDPPacket(int s, const char * p, ssize_t n,
613: const struct sockaddr * addr,
614: const char * searched_device)
615: {
616: const char * linestart;
617: const char * lineend;
618: const char * nameend;
619: const char * valuestart;
620: struct header headers[3];
621: int i, r = 0;
622: int methodlen;
623: int nts = -1;
624: int method = -1;
625: unsigned int lifetime = 180; /* 3 minutes by default */
626: const char * st = NULL;
627: int st_len = 0;
628:
629: /* first check from what subnet is the sender */
630: if(get_lan_for_peer(addr) == NULL) {
631: char addr_str[64];
632: sockaddr_to_string(addr, addr_str, sizeof(addr_str));
633: syslog(LOG_WARNING, "peer %s is not from a LAN",
634: addr_str);
635: return 0;
636: }
637:
638: /* do the parsing */
639: memset(headers, 0, sizeof(headers));
640: for(methodlen = 0;
641: methodlen < n && (isalpha(p[methodlen]) || p[methodlen]=='-');
642: methodlen++);
643: if(methodlen==8 && 0==memcmp(p, "M-SEARCH", 8))
644: method = METHOD_MSEARCH;
645: else if(methodlen==6 && 0==memcmp(p, "NOTIFY", 6))
646: method = METHOD_NOTIFY;
647: else if(methodlen==4 && 0==memcmp(p, "HTTP", 4)) {
648: /* answer to a M-SEARCH => process it as a NOTIFY
649: * with NTS: ssdp:alive */
650: method = METHOD_NOTIFY;
651: nts = NTS_SSDP_ALIVE;
652: }
653: linestart = p;
654: while(linestart < p + n - 2) {
655: /* start parsing the line : detect line end */
656: lineend = linestart;
657: while(lineend < p + n && *lineend != '\n' && *lineend != '\r')
658: lineend++;
659: /*printf("line: '%.*s'\n", lineend - linestart, linestart);*/
660: /* detect name end : ':' character */
661: nameend = linestart;
662: while(nameend < lineend && *nameend != ':')
663: nameend++;
664: /* detect value */
665: if(nameend < lineend)
666: valuestart = nameend + 1;
667: else
668: valuestart = nameend;
669: /* trim spaces */
670: while(valuestart < lineend && isspace(*valuestart))
671: valuestart++;
672: /* suppress leading " if needed */
673: if(valuestart < lineend && *valuestart=='\"')
674: valuestart++;
675: if(nameend > linestart && valuestart < lineend) {
676: int l = nameend - linestart; /* header name length */
677: int m = lineend - valuestart; /* header value length */
678: /* suppress tailing spaces */
679: while(m>0 && isspace(valuestart[m-1]))
680: m--;
681: /* suppress tailing ' if needed */
682: if(m>0 && valuestart[m-1] == '\"')
683: m--;
684: i = -1;
685: /*printf("--%.*s: (%d)%.*s--\n", l, linestart,
686: m, m, valuestart);*/
687: if(l==2 && 0==strncasecmp(linestart, "nt", 2))
688: i = HEADER_NT;
689: else if(l==3 && 0==strncasecmp(linestart, "usn", 3))
690: i = HEADER_USN;
691: else if(l==3 && 0==strncasecmp(linestart, "nts", 3)) {
692: if(m==10 && 0==strncasecmp(valuestart, "ssdp:alive", 10))
693: nts = NTS_SSDP_ALIVE;
694: else if(m==11 && 0==strncasecmp(valuestart, "ssdp:byebye", 11))
695: nts = NTS_SSDP_BYEBYE;
696: else if(m==11 && 0==strncasecmp(valuestart, "ssdp:update", 11))
697: nts = NTS_SSDP_UPDATE;
698: }
699: else if(l==8 && 0==strncasecmp(linestart, "location", 8))
700: i = HEADER_LOCATION;
701: else if(l==13 && 0==strncasecmp(linestart, "cache-control", 13)) {
702: /* parse "name1=value1, name_alone, name2=value2" string */
703: const char * name = valuestart; /* name */
704: const char * val; /* value */
705: int rem = m; /* remaining bytes to process */
706: while(rem > 0) {
707: val = name;
708: while(val < name + rem && *val != '=' && *val != ',')
709: val++;
710: if(val >= name + rem)
711: break;
712: if(*val == '=') {
713: while(val < name + rem && (*val == '=' || isspace(*val)))
714: val++;
715: if(val >= name + rem)
716: break;
717: if(0==strncasecmp(name, "max-age", 7))
718: lifetime = (unsigned int)strtoul(val, 0, 0);
719: /* move to the next name=value pair */
720: while(rem > 0 && *name != ',') {
721: rem--;
722: name++;
723: }
724: /* skip spaces */
725: while(rem > 0 && (*name == ',' || isspace(*name))) {
726: rem--;
727: name++;
728: }
729: } else {
730: rem -= (val - name);
731: name = val;
732: while(rem > 0 && (*name == ',' || isspace(*name))) {
733: rem--;
734: name++;
735: }
736: }
737: }
738: /*syslog(LOG_DEBUG, "**%.*s**%u", m, valuestart, lifetime);*/
739: } else if(l==2 && 0==strncasecmp(linestart, "st", 2)) {
740: st = valuestart;
741: st_len = m;
742: if(method == METHOD_NOTIFY)
743: i = HEADER_NT; /* it was a M-SEARCH response */
744: }
745: if(i>=0) {
746: headers[i].p = valuestart;
747: headers[i].l = m;
748: }
749: }
750: linestart = lineend;
751: while((linestart < p + n) && (*linestart == '\n' || *linestart == '\r'))
752: linestart++;
753: }
754: #if 0
755: printf("NTS=%d\n", nts);
756: for(i=0; i<3; i++) {
757: if(headers[i].p)
758: printf("%d-'%.*s'\n", i, headers[i].l, headers[i].p);
759: }
760: #endif
761: syslog(LOG_DEBUG,"SSDP request: '%.*s' (%d) %s %s=%.*s",
762: methodlen, p, method, nts_to_str(nts),
763: (method==METHOD_NOTIFY)?"nt":"st",
764: (method==METHOD_NOTIFY)?headers[HEADER_NT].l:st_len,
765: (method==METHOD_NOTIFY)?headers[HEADER_NT].p:st);
766: switch(method) {
767: case METHOD_NOTIFY:
768: if(nts==NTS_SSDP_ALIVE || nts==NTS_SSDP_UPDATE) {
769: if(headers[HEADER_NT].p && headers[HEADER_USN].p && headers[HEADER_LOCATION].p) {
770: /* filter if needed */
771: if(searched_device &&
772: 0 != memcmp(headers[HEADER_NT].p, searched_device, headers[HEADER_NT].l))
773: break;
774: r = updateDevice(headers, time(NULL) + lifetime);
775: } else {
776: syslog(LOG_WARNING, "missing header nt=%p usn=%p location=%p",
777: headers[HEADER_NT].p, headers[HEADER_USN].p,
778: headers[HEADER_LOCATION].p);
779: }
780: } else if(nts==NTS_SSDP_BYEBYE) {
781: if(headers[HEADER_NT].p && headers[HEADER_USN].p) {
782: r = removeDevice(headers);
783: } else {
784: syslog(LOG_WARNING, "missing header nt=%p usn=%p",
785: headers[HEADER_NT].p, headers[HEADER_USN].p);
786: }
787: }
788: break;
789: case METHOD_MSEARCH:
790: processMSEARCH(s, st, st_len, addr);
791: break;
792: default:
793: {
794: char addr_str[64];
795: sockaddr_to_string(addr, addr_str, sizeof(addr_str));
796: syslog(LOG_WARNING, "method %.*s, don't know what to do (from %s)",
797: methodlen, p, addr_str);
798: }
799: }
800: return r;
801: }
802:
803: /* OpenUnixSocket()
804: * open the unix socket and call bind() and listen()
805: * return -1 in case of error */
806: static int
807: OpenUnixSocket(const char * path)
808: {
809: struct sockaddr_un addr;
810: int s;
811: int rv;
812: s = socket(AF_UNIX, SOCK_STREAM, 0);
813: if(s < 0)
814: {
815: syslog(LOG_ERR, "socket(AF_UNIX): %m");
816: return -1;
817: }
818: /* unlink the socket pseudo file before binding */
819: rv = unlink(path);
820: if(rv < 0 && errno != ENOENT)
821: {
822: syslog(LOG_ERR, "unlink(unixsocket, \"%s\"): %m", path);
823: close(s);
824: return -1;
825: }
826: addr.sun_family = AF_UNIX;
827: strncpy(addr.sun_path, path, sizeof(addr.sun_path));
828: if(bind(s, (struct sockaddr *)&addr,
829: sizeof(struct sockaddr_un)) < 0)
830: {
831: syslog(LOG_ERR, "bind(unixsocket, \"%s\"): %m", path);
832: close(s);
833: return -1;
834: }
835: else if(listen(s, 5) < 0)
836: {
837: syslog(LOG_ERR, "listen(unixsocket): %m");
838: close(s);
839: return -1;
840: }
841: /* Change rights so everyone can communicate with us */
842: if(chmod(path, 0666) < 0)
843: {
844: syslog(LOG_WARNING, "chmod(\"%s\"): %m", path);
845: }
846: return s;
847: }
848:
849: static ssize_t processRequestSub(struct reqelem * req, const unsigned char * buf, ssize_t n);
850:
851: /* processRequest() :
852: * process the request coming from a unix socket */
853: void processRequest(struct reqelem * req)
854: {
855: ssize_t n, r;
856: unsigned char buf[2048];
857: const unsigned char * p;
858:
859: n = read(req->socket, buf, sizeof(buf));
860: if(n<0) {
861: if(errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
862: return; /* try again later */
863: syslog(LOG_ERR, "(s=%d) processRequest(): read(): %m", req->socket);
864: goto error;
865: }
866: if(n==0) {
867: syslog(LOG_INFO, "(s=%d) request connection closed", req->socket);
868: goto error;
869: }
870: p = buf;
871: while (n > 0)
872: {
873: r = processRequestSub(req, p, n);
874: if (r < 0)
875: goto error;
876: p += r;
877: n -= r;
878: }
879: return;
880: error:
881: close(req->socket);
882: req->socket = -1;
883: }
884:
885: static ssize_t processRequestSub(struct reqelem * req, const unsigned char * buf, ssize_t n)
886: {
887: unsigned int l, m;
888: unsigned int baselen; /* without the version */
889: const unsigned char * p;
890: enum request_type type;
891: struct device * d = devlist;
892: unsigned char rbuf[RESPONSE_BUFFER_SIZE];
893: unsigned char * rp;
894: unsigned char nrep = 0;
895: time_t t;
896: struct service * newserv = NULL;
897: struct service * serv;
898:
899: t = time(NULL);
900: type = buf[0];
901: p = buf + 1;
902: DECODELENGTH_CHECKLIMIT(l, p, buf + n);
903: if(l > (unsigned)(buf+n-p)) {
904: syslog(LOG_WARNING, "bad request (length encoding l=%u n=%u)",
905: l, (unsigned)n);
906: goto error;
907: }
908: if(l == 0 && type != MINISSDPD_SEARCH_ALL
909: && type != MINISSDPD_GET_VERSION && type != MINISSDPD_NOTIF) {
910: syslog(LOG_WARNING, "bad request (length=0, type=%d)", type);
911: goto error;
912: }
913: syslog(LOG_INFO, "(s=%d) request type=%d str='%.*s'",
914: req->socket, type, l, p);
915: switch(type) {
916: case MINISSDPD_GET_VERSION:
917: rp = rbuf;
918: CODELENGTH((sizeof(MINISSDPD_VERSION) - 1), rp);
919: memcpy(rp, MINISSDPD_VERSION, sizeof(MINISSDPD_VERSION) - 1);
920: rp += (sizeof(MINISSDPD_VERSION) - 1);
921: if(write_or_buffer(req, rbuf, rp - rbuf) < 0) {
922: syslog(LOG_ERR, "(s=%d) write: %m", req->socket);
923: goto error;
924: }
925: p += l;
926: break;
927: case MINISSDPD_SEARCH_TYPE: /* request by type */
928: case MINISSDPD_SEARCH_USN: /* request by USN (unique id) */
929: case MINISSDPD_SEARCH_ALL: /* everything */
930: rp = rbuf+1;
931: /* From UPnP Device Architecture v1.1 :
932: * 1.3.2 [...] Updated versions of device and service types
933: * are REQUIRED to be full backward compatible with
934: * previous versions. Devices MUST respond to M-SEARCH
935: * requests for any supported version. For example, if a
936: * device implements “urn:schemas-upnporg:service:xyz:2”,
937: * it MUST respond to search requests for both that type
938: * and “urn:schemas-upnp-org:service:xyz:1”. The response
939: * MUST specify the same version as was contained in the
940: * search request. [...] */
941: baselen = l; /* remove the version */
942: while(baselen > 0) {
943: if(p[baselen-1] == ':')
944: break;
945: if(!(p[baselen-1] >= '0' && p[baselen-1] <= '9'))
946: break;
947: baselen--;
948: }
949: while(d && (nrep < 255)) {
950: if(d->t < t) {
951: syslog(LOG_INFO, "outdated device");
952: } else {
953: /* test if we can put more responses in the buffer */
954: if(d->headers[HEADER_LOCATION].l + d->headers[HEADER_NT].l
955: + d->headers[HEADER_USN].l + 6
956: + (rp - rbuf) >= (int)sizeof(rbuf))
957: break;
958: if( (type==MINISSDPD_SEARCH_TYPE && 0==memcmp(d->headers[HEADER_NT].p, p, baselen))
959: ||(type==MINISSDPD_SEARCH_USN && 0==memcmp(d->headers[HEADER_USN].p, p, l))
960: ||(type==MINISSDPD_SEARCH_ALL) ) {
961: /* response :
962: * 1 - Location
963: * 2 - NT (device/service type)
964: * 3 - usn */
965: m = d->headers[HEADER_LOCATION].l;
966: CODELENGTH(m, rp);
967: memcpy(rp, d->headers[HEADER_LOCATION].p, d->headers[HEADER_LOCATION].l);
968: rp += d->headers[HEADER_LOCATION].l;
969: m = d->headers[HEADER_NT].l;
970: CODELENGTH(m, rp);
971: memcpy(rp, d->headers[HEADER_NT].p, d->headers[HEADER_NT].l);
972: rp += d->headers[HEADER_NT].l;
973: m = d->headers[HEADER_USN].l;
974: CODELENGTH(m, rp);
975: memcpy(rp, d->headers[HEADER_USN].p, d->headers[HEADER_USN].l);
976: rp += d->headers[HEADER_USN].l;
977: nrep++;
978: }
979: }
980: d = d->next;
981: }
982: /* Also look in service list */
983: for(serv = servicelisthead.lh_first;
984: serv && (nrep < 255);
985: serv = serv->entries.le_next) {
986: /* test if we can put more responses in the buffer */
987: if(strlen(serv->location) + strlen(serv->st)
988: + strlen(serv->usn) + 6 + (rp - rbuf) >= sizeof(rbuf))
989: break;
990: if( (type==MINISSDPD_SEARCH_TYPE && 0==strncmp(serv->st, (const char *)p, l))
991: ||(type==MINISSDPD_SEARCH_USN && 0==strncmp(serv->usn, (const char *)p, l))
992: ||(type==MINISSDPD_SEARCH_ALL) ) {
993: /* response :
994: * 1 - Location
995: * 2 - NT (device/service type)
996: * 3 - usn */
997: m = strlen(serv->location);
998: CODELENGTH(m, rp);
999: memcpy(rp, serv->location, m);
1000: rp += m;
1001: m = strlen(serv->st);
1002: CODELENGTH(m, rp);
1003: memcpy(rp, serv->st, m);
1004: rp += m;
1005: m = strlen(serv->usn);
1006: CODELENGTH(m, rp);
1007: memcpy(rp, serv->usn, m);
1008: rp += m;
1009: nrep++;
1010: }
1011: }
1012: rbuf[0] = nrep;
1013: syslog(LOG_DEBUG, "(s=%d) response : %d device%s",
1014: req->socket, nrep, (nrep > 1) ? "s" : "");
1015: if(write_or_buffer(req, rbuf, rp - rbuf) < 0) {
1016: syslog(LOG_ERR, "(s=%d) write: %m", req->socket);
1017: goto error;
1018: }
1019: p += l;
1020: break;
1021: case MINISSDPD_SUBMIT: /* submit service */
1022: newserv = malloc(sizeof(struct service));
1023: if(!newserv) {
1024: syslog(LOG_ERR, "cannot allocate memory");
1025: goto error;
1026: }
1027: memset(newserv, 0, sizeof(struct service)); /* set pointers to NULL */
1028: if(containsForbiddenChars(p, l)) {
1029: syslog(LOG_ERR, "bad request (st contains forbidden chars)");
1030: goto error;
1031: }
1032: newserv->st = malloc(l + 1);
1033: if(!newserv->st) {
1034: syslog(LOG_ERR, "cannot allocate memory");
1035: goto error;
1036: }
1037: memcpy(newserv->st, p, l);
1038: newserv->st[l] = '\0';
1039: p += l;
1040: if(p >= buf + n) {
1041: syslog(LOG_WARNING, "bad request (missing usn)");
1042: goto error;
1043: }
1044: DECODELENGTH_CHECKLIMIT(l, p, buf + n);
1045: if(l > (unsigned)(buf+n-p)) {
1046: syslog(LOG_WARNING, "bad request (length encoding)");
1047: goto error;
1048: }
1049: if(containsForbiddenChars(p, l)) {
1050: syslog(LOG_ERR, "bad request (usn contains forbidden chars)");
1051: goto error;
1052: }
1053: syslog(LOG_INFO, "usn='%.*s'", l, p);
1054: newserv->usn = malloc(l + 1);
1055: if(!newserv->usn) {
1056: syslog(LOG_ERR, "cannot allocate memory");
1057: goto error;
1058: }
1059: memcpy(newserv->usn, p, l);
1060: newserv->usn[l] = '\0';
1061: p += l;
1062: DECODELENGTH_CHECKLIMIT(l, p, buf + n);
1063: if(l > (unsigned)(buf+n-p)) {
1064: syslog(LOG_WARNING, "bad request (length encoding)");
1065: goto error;
1066: }
1067: if(containsForbiddenChars(p, l)) {
1068: syslog(LOG_ERR, "bad request (server contains forbidden chars)");
1069: goto error;
1070: }
1071: syslog(LOG_INFO, "server='%.*s'", l, p);
1072: newserv->server = malloc(l + 1);
1073: if(!newserv->server) {
1074: syslog(LOG_ERR, "cannot allocate memory");
1075: goto error;
1076: }
1077: memcpy(newserv->server, p, l);
1078: newserv->server[l] = '\0';
1079: p += l;
1080: DECODELENGTH_CHECKLIMIT(l, p, buf + n);
1081: if(l > (unsigned)(buf+n-p)) {
1082: syslog(LOG_WARNING, "bad request (length encoding)");
1083: goto error;
1084: }
1085: if(containsForbiddenChars(p, l)) {
1086: syslog(LOG_ERR, "bad request (location contains forbidden chars)");
1087: goto error;
1088: }
1089: syslog(LOG_INFO, "location='%.*s'", l, p);
1090: newserv->location = malloc(l + 1);
1091: if(!newserv->location) {
1092: syslog(LOG_ERR, "cannot allocate memory");
1093: goto error;
1094: }
1095: memcpy(newserv->location, p, l);
1096: newserv->location[l] = '\0';
1097: p += l;
1098: /* look in service list for duplicate */
1099: for(serv = servicelisthead.lh_first;
1100: serv;
1101: serv = serv->entries.le_next) {
1102: if(0 == strcmp(newserv->usn, serv->usn)
1103: && 0 == strcmp(newserv->st, serv->st)) {
1104: syslog(LOG_INFO, "Service already in the list. Updating...");
1105: free(newserv->st);
1106: free(newserv->usn);
1107: free(serv->server);
1108: serv->server = newserv->server;
1109: free(serv->location);
1110: serv->location = newserv->location;
1111: free(newserv);
1112: newserv = NULL;
1113: return (p - buf);
1114: }
1115: }
1116: /* Inserting new service */
1117: LIST_INSERT_HEAD(&servicelisthead, newserv, entries);
1118: sendNotifications(NOTIF_NEW, NULL, newserv);
1119: newserv = NULL;
1120: break;
1121: case MINISSDPD_NOTIF: /* switch socket to notify */
1122: rbuf[0] = '\0';
1123: if(write_or_buffer(req, rbuf, 1) < 0) {
1124: syslog(LOG_ERR, "(s=%d) write: %m", req->socket);
1125: goto error;
1126: }
1127: req->is_notify = 1;
1128: p += l;
1129: break;
1130: default:
1131: syslog(LOG_WARNING, "Unknown request type %d", type);
1132: rbuf[0] = '\0';
1133: if(write_or_buffer(req, rbuf, 1) < 0) {
1134: syslog(LOG_ERR, "(s=%d) write: %m", req->socket);
1135: goto error;
1136: }
1137: }
1138: return (p - buf);
1139: error:
1140: if(newserv) {
1141: free(newserv->st);
1142: free(newserv->usn);
1143: free(newserv->server);
1144: free(newserv->location);
1145: free(newserv);
1146: newserv = NULL;
1147: }
1148: return -1;
1149: }
1150:
1151: static volatile sig_atomic_t quitting = 0;
1152: /* SIGTERM signal handler */
1153: static void
1154: sigterm(int sig)
1155: {
1156: (void)sig;
1157: /*int save_errno = errno;*/
1158: /*signal(sig, SIG_IGN);*/
1159: #if 0
1160: /* calling syslog() is forbidden in a signal handler according to
1161: * signal(3) */
1162: syslog(LOG_NOTICE, "received signal %d, good-bye", sig);
1163: #endif
1164: quitting = 1;
1165: /*errno = save_errno;*/
1166: }
1167:
1168: #define PORT 1900
1169: #define XSTR(s) STR(s)
1170: #define STR(s) #s
1171: #define UPNP_MCAST_ADDR "239.255.255.250"
1172: /* for IPv6 */
1173: #define UPNP_MCAST_LL_ADDR "FF02::C" /* link-local */
1174: #define UPNP_MCAST_SL_ADDR "FF05::C" /* site-local */
1175:
1176: /* send the M-SEARCH request for devices
1177: * either all devices (third argument is NULL or "*") or a specific one */
1178: static void ssdpDiscover(int s, int ipv6, const char * search)
1179: {
1180: static const char MSearchMsgFmt[] =
1181: "M-SEARCH * HTTP/1.1\r\n"
1182: "HOST: %s:" XSTR(PORT) "\r\n"
1183: "ST: %s\r\n"
1184: "MAN: \"ssdp:discover\"\r\n"
1185: "MX: %u\r\n"
1186: "\r\n";
1187: char bufr[512];
1188: int n;
1189: int mx = 3;
1190: int linklocal = 1;
1191: struct sockaddr_storage sockudp_w;
1192:
1193: {
1194: n = snprintf(bufr, sizeof(bufr),
1195: MSearchMsgFmt,
1196: ipv6 ?
1197: (linklocal ? "[" UPNP_MCAST_LL_ADDR "]" : "[" UPNP_MCAST_SL_ADDR "]")
1198: : UPNP_MCAST_ADDR,
1199: (search ? search : "ssdp:all"), mx);
1200: memset(&sockudp_w, 0, sizeof(struct sockaddr_storage));
1201: if(ipv6) {
1202: struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_w;
1203: p->sin6_family = AF_INET6;
1204: p->sin6_port = htons(PORT);
1205: inet_pton(AF_INET6,
1206: linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR,
1207: &(p->sin6_addr));
1208: } else {
1209: struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_w;
1210: p->sin_family = AF_INET;
1211: p->sin_port = htons(PORT);
1212: p->sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);
1213: }
1214:
1215: n = sendto_or_schedule(s, bufr, n, 0, (const struct sockaddr *)&sockudp_w,
1216: ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));
1217: if (n < 0) {
1218: syslog(LOG_ERR, "%s: sendto(s=%d, ipv6=%d): %m", __func__, s, ipv6);
1219: }
1220: }
1221: }
1222:
1223: /* main(): program entry point */
1224: int main(int argc, char * * argv)
1225: {
1226: int ret = 0;
1227: #ifndef NO_BACKGROUND_NO_PIDFILE
1228: int pid;
1229: #endif
1230: struct sigaction sa;
1231: char buf[1500];
1232: ssize_t n;
1233: int s_ssdp = -1; /* udp socket receiving ssdp packets */
1234: #ifdef ENABLE_IPV6
1235: int s_ssdp6 = -1; /* udp socket receiving ssdp packets IPv6*/
1236: #else /* ENABLE_IPV6 */
1237: #define s_ssdp6 (-1)
1238: #endif /* ENABLE_IPV6 */
1239: int s_unix = -1; /* unix socket communicating with clients */
1240: int s_ifacewatch = -1; /* socket to receive Route / network interface config changes */
1241: struct reqelem * req;
1242: struct reqelem * reqnext;
1243: fd_set readfds;
1244: fd_set writefds;
1245: struct timeval now;
1246: int max_fd;
1247: struct lan_addr_s * lan_addr;
1248: int i;
1249: const char * sockpath = "/var/run/minissdpd.sock";
1250: #ifndef NO_BACKGROUND_NO_PIDFILE
1251: const char * pidfilename = "/var/run/minissdpd.pid";
1252: #endif
1253: int debug_flag = 0;
1254: #ifdef ENABLE_IPV6
1255: int ipv6 = 0;
1256: #endif /* ENABLE_IPV6 */
1257: int deltadev = 0;
1258: struct sockaddr_in sendername;
1259: socklen_t sendername_len;
1260: #ifdef ENABLE_IPV6
1261: struct sockaddr_in6 sendername6;
1262: socklen_t sendername6_len;
1263: #endif /* ENABLE_IPV6 */
1264: unsigned char ttl = 2; /* UDA says it should default to 2 */
1265: const char * searched_device = NULL; /* if not NULL, search/filter a specific device type */
1266: int opt;
1267:
1268: LIST_INIT(&reqlisthead);
1269: LIST_INIT(&servicelisthead);
1270: LIST_INIT(&lan_addrs);
1271: /* process command line */
1272: #define OPTSTRING "d6i:s:p:t:f:"
1273: while ((opt = getopt(argc, argv, "di:s:t:f:"
1274: #ifdef ENABLE_IPV6
1275: "6"
1276: #endif
1277: #ifndef NO_BACKGROUND_NO_PIDFILE
1278: "p:"
1279: #endif
1280:
1281: )) != -1)
1282: {
1283: switch(opt)
1284: {
1285: case 'd':
1286: debug_flag = 1;
1287: break;
1288: #ifdef ENABLE_IPV6
1289: case '6':
1290: ipv6 = 1;
1291: break;
1292: #endif /* ENABLE_IPV6 */
1293: case 'i':
1294: lan_addr = malloc(sizeof(struct lan_addr_s));
1295: if(lan_addr == NULL) {
1296: fprintf(stderr, "malloc(%d) FAILED\n", (int)sizeof(struct lan_addr_s));
1297: break;
1298: }
1299: if(parselanaddr(lan_addr, optarg) != 0) {
1300: fprintf(stderr, "can't parse \"%s\" as a valid "
1301: #ifndef ENABLE_IPV6
1302: "address or "
1303: #endif
1304: "interface name\n", optarg);
1305: free(lan_addr);
1306: } else {
1307: LIST_INSERT_HEAD(&lan_addrs, lan_addr, list);
1308: }
1309: break;
1310: case 's':
1311: sockpath = optarg;
1312: break;
1313: #ifndef NO_BACKGROUND_NO_PIDFILE
1314: case 'p':
1315: pidfilename = optarg;
1316: break;
1317: #endif
1318: case 't':
1319: ttl = (unsigned char)atoi(optarg);
1320: break;
1321: case 'f':
1322: searched_device = optarg;
1323: break;
1324: }
1325: }
1326: if(lan_addrs.lh_first == NULL)
1327: {
1328: fprintf(stderr,
1329: "Usage: %s [-d] "
1330: #ifdef ENABLE_IPV6
1331: "[-6] "
1332: #endif /* ENABLE_IPV6 */
1333: "[-s socket] "
1334: #ifndef NO_BACKGROUND_NO_PIDFILE
1335: "[-p pidfile] "
1336: #endif
1337: "[-t TTL] "
1338: "[-f device] "
1339: "-i <interface> [-i <interface2>] ...\n",
1340: argv[0]);
1341: fprintf(stderr,
1342: "\n <interface> is "
1343: #ifndef ENABLE_IPV6
1344: "either an IPv4 address with mask such as\n"
1345: " 192.168.1.42/255.255.255.0, or "
1346: #endif
1347: "an interface name such as eth0.\n");
1348: fprintf(stderr,
1349: "\n By default, socket will be open as %s\n"
1350: #ifndef NO_BACKGROUND_NO_PIDFILE
1351: " and pid written to file %s\n",
1352: sockpath, pidfilename
1353: #else
1354: ,sockpath
1355: #endif
1356: );
1357: return 1;
1358: }
1359:
1360: /* open log */
1361: openlog("minissdpd",
1362: LOG_CONS|LOG_PID|(debug_flag?LOG_PERROR:0),
1363: LOG_MINISSDPD);
1364: if(!debug_flag) /* speed things up and ignore LOG_INFO and LOG_DEBUG */
1365: setlogmask(LOG_UPTO(LOG_NOTICE));
1366:
1367: #ifndef NO_BACKGROUND_NO_PIDFILE
1368: if(checkforrunning(pidfilename) < 0)
1369: {
1370: syslog(LOG_ERR, "MiniSSDPd is already running. EXITING");
1371: return 1;
1372: }
1373: #endif
1374:
1375: upnp_bootid = (unsigned int)time(NULL);
1376:
1377: /* set signal handlers */
1378: memset(&sa, 0, sizeof(struct sigaction));
1379: sa.sa_handler = sigterm;
1380: if(sigaction(SIGTERM, &sa, NULL))
1381: {
1382: syslog(LOG_ERR, "Failed to set SIGTERM handler. EXITING");
1383: ret = 1;
1384: goto quit;
1385: }
1386: if(sigaction(SIGINT, &sa, NULL))
1387: {
1388: syslog(LOG_ERR, "Failed to set SIGINT handler. EXITING");
1389: ret = 1;
1390: goto quit;
1391: }
1392: /* open route/interface config changes socket */
1393: s_ifacewatch = OpenAndConfInterfaceWatchSocket();
1394: /* open UDP socket(s) for receiving SSDP packets */
1395: s_ssdp = OpenAndConfSSDPReceiveSocket(0, ttl);
1396: if(s_ssdp < 0)
1397: {
1398: syslog(LOG_ERR, "Cannot open socket for receiving SSDP messages, exiting");
1399: ret = 1;
1400: goto quit;
1401: }
1402: #ifdef ENABLE_IPV6
1403: if(ipv6) {
1404: s_ssdp6 = OpenAndConfSSDPReceiveSocket(1, ttl);
1405: if(s_ssdp6 < 0)
1406: {
1407: syslog(LOG_ERR, "Cannot open socket for receiving SSDP messages (IPv6), exiting");
1408: ret = 1;
1409: goto quit;
1410: }
1411: }
1412: #endif /* ENABLE_IPV6 */
1413: /* Open Unix socket to communicate with other programs on
1414: * the same machine */
1415: s_unix = OpenUnixSocket(sockpath);
1416: if(s_unix < 0)
1417: {
1418: syslog(LOG_ERR, "Cannot open unix socket for communicating with clients. Exiting");
1419: ret = 1;
1420: goto quit;
1421: }
1422:
1423: /* drop privileges */
1424: #if 0
1425: /* if we drop privileges, how to unlink(/var/run/minissdpd.sock) ? */
1426: if(getuid() == 0) {
1427: struct passwd * user;
1428: struct group * group;
1429: user = getpwnam("nobody");
1430: if(!user) {
1431: syslog(LOG_ERR, "getpwnam(\"%s\") : %m", "nobody");
1432: ret = 1;
1433: goto quit;
1434: }
1435: group = getgrnam("nogroup");
1436: if(!group) {
1437: syslog(LOG_ERR, "getgrnam(\"%s\") : %m", "nogroup");
1438: ret = 1;
1439: goto quit;
1440: }
1441: if(setgid(group->gr_gid) < 0) {
1442: syslog(LOG_ERR, "setgit(%d) : %m", group->gr_gid);
1443: ret = 1;
1444: goto quit;
1445: }
1446: if(setuid(user->pw_uid) < 0) {
1447: syslog(LOG_ERR, "setuid(%d) : %m", user->pw_uid);
1448: ret = 1;
1449: goto quit;
1450: }
1451: }
1452: #endif
1453:
1454: #ifndef NO_BACKGROUND_NO_PIDFILE
1455: /* daemonize or in any case get pid ! */
1456: if(debug_flag)
1457: pid = getpid();
1458: else {
1459: #ifdef USE_DAEMON
1460: if(daemon(0, 0) < 0)
1461: perror("daemon()");
1462: pid = getpid();
1463: #else /* USE_DAEMON */
1464: pid = daemonize();
1465: #endif /* USE_DAEMON */
1466: }
1467:
1468: writepidfile(pidfilename, pid);
1469: #endif
1470:
1471: /* send M-SEARCH ssdp:all Requests */
1472: if(s_ssdp >= 0) {
1473: for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next) {
1474: #ifndef HAVE_IP_MREQN
1475: struct in_addr mc_if;
1476:
1477: mc_if.s_addr = lan_addr->addr.s_addr; /*inet_addr(addr);*/
1478: #else
1479: struct ip_mreqn mc_if;
1480:
1481: mc_if.imr_address.s_addr = lan_addr->addr.s_addr; /*inet_addr(addr);*/
1482: #ifdef ENABLE_IPV6
1483: mc_if.imr_ifindex = lan_addr->index;
1484: #else /* ENABLE_IPV6 */
1485: mc_if.imr_ifindex = if_nametoindex(lan_addr->ifname);
1486: #endif /* ENABLE_IPV6 */
1487: #endif /* HAVE_IP_MREQN */
1488: if(setsockopt(s_ssdp, IPPROTO_IP, IP_MULTICAST_IF, &mc_if, sizeof(mc_if)) < 0) {
1489: syslog(LOG_WARNING, "setsockopt(IP_MULTICAST_IF): %m");
1490: }
1491: ssdpDiscover(s_ssdp, 0, searched_device);
1492: /* XXX if ssdpDiscover() doesn't send the SSDP packet at once,
1493: * we should wait here */
1494: }
1495: }
1496: if(s_ssdp6 >= 0)
1497: ssdpDiscover(s_ssdp6, 1, searched_device);
1498:
1499: /* Main loop */
1500: while(!quitting) {
1501: /* fill readfds fd_set */
1502: FD_ZERO(&readfds);
1503: FD_ZERO(&writefds);
1504:
1505: FD_SET(s_unix, &readfds);
1506: max_fd = s_unix;
1507: if(s_ssdp >= 0) {
1508: FD_SET(s_ssdp, &readfds);
1509: SET_MAX(max_fd, s_ssdp);
1510: }
1511: #ifdef ENABLE_IPV6
1512: if(s_ssdp6 >= 0) {
1513: FD_SET(s_ssdp6, &readfds);
1514: SET_MAX(max_fd, s_ssdp6);
1515: }
1516: #endif /* ENABLE_IPV6 */
1517: if(s_ifacewatch >= 0) {
1518: FD_SET(s_ifacewatch, &readfds);
1519: SET_MAX(max_fd, s_ifacewatch);
1520: }
1521: for(req = reqlisthead.lh_first; req; req = req->entries.le_next) {
1522: if(req->socket >= 0) {
1523: FD_SET(req->socket, &readfds);
1524: SET_MAX(max_fd, req->socket);
1525: }
1526: if(req->output_buffer_len > 0) {
1527: FD_SET(req->socket, &writefds);
1528: SET_MAX(max_fd, req->socket);
1529: }
1530: }
1531: gettimeofday(&now, NULL);
1532: i = get_sendto_fds(&writefds, &max_fd, &now);
1533: /* select call */
1534: if(select(max_fd + 1, &readfds, &writefds, 0, 0) < 0) {
1535: if(errno != EINTR) {
1536: syslog(LOG_ERR, "select: %m");
1537: break; /* quit */
1538: }
1539: continue; /* try again */
1540: }
1541: if(try_sendto(&writefds) < 0) {
1542: syslog(LOG_ERR, "try_sendto: %m");
1543: break;
1544: }
1545: #ifdef ENABLE_IPV6
1546: if((s_ssdp6 >= 0) && FD_ISSET(s_ssdp6, &readfds))
1547: {
1548: sendername6_len = sizeof(struct sockaddr_in6);
1549: n = recvfrom(s_ssdp6, buf, sizeof(buf), 0,
1550: (struct sockaddr *)&sendername6, &sendername6_len);
1551: if(n<0)
1552: {
1553: /* EAGAIN, EWOULDBLOCK, EINTR : silently ignore (try again next time)
1554: * other errors : log to LOG_ERR */
1555: if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
1556: syslog(LOG_ERR, "recvfrom: %m");
1557: }
1558: else
1559: {
1560: /* Parse and process the packet received */
1561: /*printf("%.*s", n, buf);*/
1562: i = ParseSSDPPacket(s_ssdp6, buf, n,
1563: (struct sockaddr *)&sendername6, searched_device);
1564: syslog(LOG_DEBUG, "** i=%d deltadev=%d **", i, deltadev);
1565: if(i==0 || (i*deltadev < 0))
1566: {
1567: if(deltadev > 0)
1568: syslog(LOG_NOTICE, "%d new devices added", deltadev);
1569: else if(deltadev < 0)
1570: syslog(LOG_NOTICE, "%d devices removed (good-bye!)", -deltadev);
1571: deltadev = i;
1572: }
1573: else if((i*deltadev) >= 0)
1574: {
1575: deltadev += i;
1576: }
1577: }
1578: }
1579: #endif /* ENABLE_IPV6 */
1580: if((s_ssdp >= 0) && FD_ISSET(s_ssdp, &readfds))
1581: {
1582: sendername_len = sizeof(struct sockaddr_in);
1583: n = recvfrom(s_ssdp, buf, sizeof(buf), 0,
1584: (struct sockaddr *)&sendername, &sendername_len);
1585: if(n<0)
1586: {
1587: /* EAGAIN, EWOULDBLOCK, EINTR : silently ignore (try again next time)
1588: * other errors : log to LOG_ERR */
1589: if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
1590: syslog(LOG_ERR, "recvfrom: %m");
1591: }
1592: else
1593: {
1594: /* Parse and process the packet received */
1595: /*printf("%.*s", n, buf);*/
1596: i = ParseSSDPPacket(s_ssdp, buf, n,
1597: (struct sockaddr *)&sendername, searched_device);
1598: syslog(LOG_DEBUG, "** i=%d deltadev=%d **", i, deltadev);
1599: if(i==0 || (i*deltadev < 0))
1600: {
1601: if(deltadev > 0)
1602: syslog(LOG_NOTICE, "%d new devices added", deltadev);
1603: else if(deltadev < 0)
1604: syslog(LOG_NOTICE, "%d devices removed (good-bye!)", -deltadev);
1605: deltadev = i;
1606: }
1607: else if((i*deltadev) >= 0)
1608: {
1609: deltadev += i;
1610: }
1611: }
1612: }
1613: /* processing unix socket requests */
1614: for(req = reqlisthead.lh_first; req;) {
1615: reqnext = req->entries.le_next;
1616: if((req->socket >= 0) && FD_ISSET(req->socket, &readfds)) {
1617: processRequest(req);
1618: }
1619: if((req->socket >= 0) && FD_ISSET(req->socket, &writefds)) {
1620: write_buffer(req);
1621: }
1622: if(req->socket < 0) {
1623: LIST_REMOVE(req, entries);
1624: free(req->output_buffer);
1625: free(req);
1626: }
1627: req = reqnext;
1628: }
1629: /* processing new requests */
1630: if(FD_ISSET(s_unix, &readfds))
1631: {
1632: struct reqelem * tmp;
1633: int s = accept(s_unix, NULL, NULL);
1634: if(s < 0) {
1635: syslog(LOG_ERR, "accept(s_unix): %m");
1636: } else {
1637: syslog(LOG_INFO, "(s=%d) new request connection", s);
1638: if(!set_non_blocking(s))
1639: syslog(LOG_WARNING, "Failed to set new socket non blocking : %m");
1640: tmp = malloc(sizeof(struct reqelem));
1641: if(!tmp) {
1642: syslog(LOG_ERR, "cannot allocate memory for request");
1643: close(s);
1644: } else {
1645: memset(tmp, 0, sizeof(struct reqelem));
1646: tmp->socket = s;
1647: LIST_INSERT_HEAD(&reqlisthead, tmp, entries);
1648: }
1649: }
1650: }
1651: /* processing route/network interface config changes */
1652: if((s_ifacewatch >= 0) && FD_ISSET(s_ifacewatch, &readfds)) {
1653: ProcessInterfaceWatch(s_ifacewatch, s_ssdp, s_ssdp6);
1654: }
1655: }
1656: syslog(LOG_DEBUG, "quitting...");
1657: finalize_sendto();
1658:
1659: /* closing and cleaning everything */
1660: quit:
1661: if(s_ssdp >= 0) {
1662: close(s_ssdp);
1663: s_ssdp = -1;
1664: }
1665: #ifdef ENABLE_IPV6
1666: if(s_ssdp6 >= 0) {
1667: close(s_ssdp6);
1668: s_ssdp6 = -1;
1669: }
1670: #endif /* ENABLE_IPV6 */
1671: if(s_unix >= 0) {
1672: close(s_unix);
1673: s_unix = -1;
1674: if(unlink(sockpath) < 0)
1675: syslog(LOG_ERR, "unlink(%s): %m", sockpath);
1676: }
1677: if(s_ifacewatch >= 0) {
1678: close(s_ifacewatch);
1679: s_ifacewatch = -1;
1680: }
1681: /* empty LAN interface/address list */
1682: while(lan_addrs.lh_first != NULL) {
1683: lan_addr = lan_addrs.lh_first;
1684: LIST_REMOVE(lan_addrs.lh_first, list);
1685: free(lan_addr);
1686: }
1687: /* empty device list */
1688: while(devlist != NULL) {
1689: struct device * next = devlist->next;
1690: free(devlist);
1691: devlist = next;
1692: }
1693: /* empty service list */
1694: while(servicelisthead.lh_first != NULL) {
1695: struct service * serv = servicelisthead.lh_first;
1696: LIST_REMOVE(servicelisthead.lh_first, entries);
1697: free(serv->st);
1698: free(serv->usn);
1699: free(serv->server);
1700: free(serv->location);
1701: free(serv);
1702: }
1703: #ifndef NO_BACKGROUND_NO_PIDFILE
1704: if(unlink(pidfilename) < 0)
1705: syslog(LOG_ERR, "unlink(%s): %m", pidfilename);
1706: #endif
1707: closelog();
1708: return ret;
1709: }
1710:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>