minissdpd-1.1.20120121/Changelog.txt010064400017500000024000000026511170030540000160400ustar00nanardstaff$Id: Changelog.txt,v 1.22 2012/01/02 10:41:52 nanard Exp $ 2012/01/02: Install manpage. Fix installation under Mac OS X. 2011/10/07: unlink unix socket before binding. set SO_REUSEADDR on SSDP socket. daemonize after init VERSION 1.1: 2011/07/30: fixes. More overflow checks 2011/07/29: added a lot of buffer overflow checks. Check malloc() failure, etc. Better cleanup in case of crash at start. network interface watch to add/drop multicast membership when the interface get live. 2011/06/18: Starting to add support for UPnP Device Architecture v1.1 2011/05/23: Added IPv6 support. -i option now understands interface names as well as addresses. VERSION 1.0: 2008/10/07: added codelength.h Fixing response to M-SEARCH Doc update 2008/10/06: UPnP server support (answering M-SEARCH) 2008/10/04: listening on several interfaces. 2008/10/01: use of daemon() instead of home made daemonize. 2007/12/19: added uuid in responses 3 types of requests supported. preventing buffer overflow 2007/12/18: It is now possible to change the location of both pid file and unix socket. 2007/10/08: Added a man page 2007/09/27: Support for install in different location $ PREFIX=... make install 2007/09/23: added a script for use in /etc/init.d improved Makefile creating /var/run/minissdpd.pid adding synthetic messages for new devices/removed devices 2007/09/19: Take SSDP announce packets lifetime into account. minissdpd-1.1.20120121/daemonize.c010064400017500000024000000041621156767010600155510ustar00nanardstaff/* $Id: daemonize.c,v 1.12 2011/05/27 09:35:02 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #include #include #include #include #include #include #include #include #include "daemonize.h" #include "config.h" #ifndef USE_DAEMON int daemonize(void) { int pid, i; switch(fork()) { /* fork error */ case -1: perror("fork()"); exit(1); /* child process */ case 0: /* obtain a new process group */ if( (pid = setsid()) < 0) { perror("setsid()"); exit(1); } /* close all descriptors */ for (i=getdtablesize();i>=0;--i) close(i); i = open("/dev/null", O_RDWR); /* open stdin */ dup(i); /* stdout */ dup(i); /* stderr */ umask(027); chdir("/"); /* chdir to /tmp ? */ return pid; /* parent process */ default: exit(0); } } #endif int writepidfile(const char * fname, int pid) { char pidstring[16]; int pidstringlen; int pidfile; if(!fname || (strlen(fname) == 0)) return -1; if( (pidfile = open(fname, O_WRONLY|O_CREAT, 0644)) < 0) { syslog(LOG_ERR, "Unable to open pidfile for writing %s: %m", fname); return -1; } pidstringlen = snprintf(pidstring, sizeof(pidstring), "%d\n", pid); if(pidstringlen <= 0) { syslog(LOG_ERR, "Unable to write to pidfile %s: snprintf(): FAILED", fname); close(pidfile); return -1; } else { if(write(pidfile, pidstring, pidstringlen) < 0) syslog(LOG_ERR, "Unable to write to pidfile %s: %m", fname); } close(pidfile); return 0; } int checkforrunning(const char * fname) { char buffer[64]; int pidfile; pid_t pid; if(!fname || (strlen(fname) == 0)) return -1; if( (pidfile = open(fname, O_RDONLY)) < 0) return 0; memset(buffer, 0, 64); if(read(pidfile, buffer, 63)) { if( (pid = atol(buffer)) > 0) { if(!kill(pid, 0)) { close(pidfile); return -2; } } } close(pidfile); return 0; } minissdpd-1.1.20120121/listifaces.c010064400017500000024000000036231067555150400157240ustar00nanardstaff/* $Id: listifaces.c,v 1.4 2007/09/23 16:59:02 nanard Exp $ */ #include #include #include #include #include #include #include #include #include void printhex(const unsigned char * p, int n) { int i; while(n>0) { for(i=0; i<16; i++) printf("%02x ", p[i]); printf("| "); for(i=0; i<16; i++) { putchar((p[i]>=32 && p[i]<127)?p[i]:'.'); } printf("\n"); p+=16; n -= 16; } } void listifaces() { struct ifconf ifc; char * buf = 0; int buflen = sizeof(struct ifreq)*20; /*[sizeof(struct ifreq)*8];*/ int s, i; int j; char saddr[256/*INET_ADDRSTRLEN*/]; /*s = socket(PF_INET, SOCK_DGRAM, 0);*/ s = socket(AF_INET, SOCK_DGRAM, 0); do { buflen += buflen; buf = realloc(buf, buflen); ifc.ifc_len = buflen; ifc.ifc_buf = (caddr_t)buf; if(ioctl(s, SIOCGIFCONF, &ifc) < 0) { perror("ioctl"); close(s); free(buf); return; } printf("%d - %d - %d\n", buflen, ifc.ifc_len, (int)sizeof(struct ifreq)); printf(" %d\n", IFNAMSIZ); printf(" %d %d\n", (int)sizeof(struct sockaddr), (int)sizeof(struct sockaddr_in) ); } while(buflen == ifc.ifc_len); printhex((const unsigned char *)ifc.ifc_buf, ifc.ifc_len); j = 0; for(i=0; iifr_name) + 16;//ifrp->ifr_addr.sa_len; /*inet_ntop(AF_INET, &(((struct sockaddr_in *)&(ifrp->ifr_addr))->sin_addr), saddr, sizeof(saddr));*/ saddr[0] = '\0'; inet_ntop(ifrp->ifr_addr.sa_family, &(ifrp->ifr_addr.sa_data[2]), saddr, sizeof(saddr)); printf("%2d %d %d %s %s\n", j, ifrp->ifr_addr.sa_family, -1/*ifrp->ifr_addr.sa_len*/, ifrp->ifr_name, saddr); j++; } free(buf); close(s); } int main(int argc, char * * argv) { listifaces(); return 0; } minissdpd-1.1.20120121/minissdpd.c010064400017500000024000000673521164354516500156030ustar00nanardstaff/* $Id: minissdpd.c,v 1.26 2011/10/07 09:45:19 nanard Exp $ */ /* MiniUPnP project * (c) 2007-2011 Thomas Bernard * website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for chmod : */ #include /* unix sockets */ #include /* for getpwnam() and getgrnam() */ #include #include #include "upnputils.h" #include "openssdpsocket.h" #include "daemonize.h" #include "codelength.h" #include "ifacewatch.h" /* current request management stucture */ struct reqelem { int socket; LIST_ENTRY(reqelem) entries; }; /* divice data structures */ struct header { const char * p; /* string pointer */ int l; /* string length */ }; #define HEADER_NT 0 #define HEADER_USN 1 #define HEADER_LOCATION 2 struct device { struct device * next; time_t t; /* validity time */ struct header headers[3]; /* NT, USN and LOCATION headers */ char data[]; }; #define NTS_SSDP_ALIVE 1 #define NTS_SSDP_BYEBYE 2 /* discovered device list kept in memory */ struct device * devlist = 0; /* bootid and configid */ unsigned int upnp_bootid = 1; unsigned int upnp_configid = 1337; /* updateDevice() : * adds or updates the device to the list. * return value : * -1 : error * 0 : the device was updated * 1 : the device was new */ static int updateDevice(const struct header * headers, time_t t) { struct device ** pp = &devlist; struct device * p = *pp; /* = devlist; */ while(p) { if( p->headers[HEADER_NT].l == headers[HEADER_NT].l && (0==memcmp(p->headers[HEADER_NT].p, headers[HEADER_NT].p, headers[HEADER_NT].l)) && p->headers[HEADER_USN].l == headers[HEADER_USN].l && (0==memcmp(p->headers[HEADER_USN].p, headers[HEADER_USN].p, headers[HEADER_USN].l)) ) { //printf("found! %d\n", (int)(t - p->t)); syslog(LOG_DEBUG, "device updated : %.*s", headers[HEADER_USN].l, headers[HEADER_USN].p); p->t = t; /* update Location ! */ if(headers[HEADER_LOCATION].l > p->headers[HEADER_LOCATION].l) { p = realloc(p, sizeof(struct device) + headers[0].l+headers[1].l+headers[2].l ); if(!p) /* allocation error */ return 0; *pp = p; } memcpy(p->data + p->headers[0].l + p->headers[1].l, headers[2].p, headers[2].l); return 0; } pp = &p->next; p = *pp; /* p = p->next; */ } syslog(LOG_INFO, "new device discovered : %.*s", headers[HEADER_USN].l, headers[HEADER_USN].p); /* add */ { char * pc; int i; p = malloc( sizeof(struct device) + headers[0].l+headers[1].l+headers[2].l ); if(!p) { syslog(LOG_ERR, "updateDevice(): cannot allocate memory"); return -1; } p->next = devlist; p->t = t; pc = p->data; for(i = 0; i < 3; i++) { p->headers[i].p = pc; p->headers[i].l = headers[i].l; memcpy(pc, headers[i].p, headers[i].l); pc += headers[i].l; } devlist = p; } return 1; } /* removeDevice() : * remove a device from the list * return value : * 0 : no device removed * -1 : device removed */ static int removeDevice(const struct header * headers) { struct device ** pp = &devlist; struct device * p = *pp; /* = devlist */ while(p) { if( p->headers[HEADER_NT].l == headers[HEADER_NT].l && (0==memcmp(p->headers[HEADER_NT].p, headers[HEADER_NT].p, headers[HEADER_NT].l)) && p->headers[HEADER_USN].l == headers[HEADER_USN].l && (0==memcmp(p->headers[HEADER_USN].p, headers[HEADER_USN].p, headers[HEADER_USN].l)) ) { syslog(LOG_INFO, "remove device : %.*s", headers[HEADER_USN].l, headers[HEADER_USN].p); *pp = p->next; free(p); return -1; } pp = &p->next; p = *pp; /* p = p->next; */ } syslog(LOG_WARNING, "device not found for removing : %.*s", headers[HEADER_USN].l, headers[HEADER_USN].p); return 0; } /* SendSSDPMSEARCHResponse() : * build and send response to M-SEARCH SSDP packets. */ static void SendSSDPMSEARCHResponse(int s, const struct sockaddr * sockname, const char * st, const char * usn, const char * server, const char * location) { int l, n; char buf[512]; socklen_t sockname_len; /* * follow guideline from document "UPnP Device Architecture 1.0" * uppercase is recommended. * DATE: is recommended * SERVER: OS/ver UPnP/1.0 miniupnpd/1.0 * - check what to put in the 'Cache-Control' header * * have a look at the document "UPnP Device Architecture v1.1 */ l = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\n" "CACHE-CONTROL: max-age=120\r\n" /*"DATE: ...\r\n"*/ "ST: %s\r\n" "USN: %s\r\n" "EXT:\r\n" "SERVER: %s\r\n" "LOCATION: %s\r\n" "OPT: \"http://schemas.upnp.org/upnp/1/0/\";\r\n" /* UDA v1.1 */ "01-NLS: %u\r\n" /* same as BOOTID. UDA v1.1 */ "BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */ "CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */ "\r\n", st, usn, server, location, upnp_bootid, upnp_bootid, upnp_configid); #ifdef ENABLE_IPV6 sockname_len = (sockname->sa_family == PF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); #else sockname_len = sizeof(struct sockaddr_in); #endif n = sendto(s, buf, l, 0, sockname, sockname_len ); if(n < 0) { syslog(LOG_ERR, "sendto(udp): %m"); } } /* Services stored for answering to M-SEARCH */ struct service { char * st; /* Service type */ char * usn; /* Unique identifier */ char * server; /* Server string */ char * location; /* URL */ LIST_ENTRY(service) entries; }; LIST_HEAD(servicehead, service) servicelisthead; /* Process M-SEARCH requests */ static void processMSEARCH(int s, const char * st, int st_len, const struct sockaddr * addr) { struct service * serv; #ifdef ENABLE_IPV6 char buf[64]; #endif if(!st || st_len==0) return; #ifdef ENABLE_IPV6 sockaddr_to_string(addr, buf, sizeof(buf)); syslog(LOG_INFO, "SSDP M-SEARCH from %s ST:%.*s", buf, st_len, st); #else syslog(LOG_INFO, "SSDP M-SEARCH from %s:%d ST: %.*s", inet_ntoa(((const struct sockaddr_in *)addr)->sin_addr), ntohs(((const struct sockaddr_in *)addr)->sin_port), st_len, st); #endif if(st_len==8 && (0==memcmp(st, "ssdp:all", 8))) { /* send a response for all services */ for(serv = servicelisthead.lh_first; serv; serv = serv->entries.le_next) { SendSSDPMSEARCHResponse(s, addr, serv->st, serv->usn, serv->server, serv->location); } } else if(st_len > 5 && (0==memcmp(st, "uuid:", 5))) { /* find a matching UUID value */ for(serv = servicelisthead.lh_first; serv; serv = serv->entries.le_next) { if(0 == strncmp(serv->usn, st, st_len)) { SendSSDPMSEARCHResponse(s, addr, serv->st, serv->usn, serv->server, serv->location); } } } else { /* find matching services */ if(st[st_len-2]==':' && isdigit(st[st_len-1])) st_len -= 2; for(serv = servicelisthead.lh_first; serv; serv = serv->entries.le_next) { if(0 == strncmp(serv->st, st, st_len)) { SendSSDPMSEARCHResponse(s, addr, serv->st, serv->usn, serv->server, serv->location); } } } } /** * helper function. * reject any non ASCII or non printable character. */ static int containsForbiddenChars(const unsigned char * p, int len) { while(len > 0) { if(*p < ' ' || *p >= '\x7f') return 1; p++; len--; } return 0; } #define METHOD_MSEARCH 1 #define METHOD_NOTIFY 2 /* ParseSSDPPacket() : * parse a received SSDP Packet and call * updateDevice() or removeDevice() as needed * return value : * -1 : a device was removed * 0 : no device removed nor added * 1 : a device was added. */ static int ParseSSDPPacket(int s, const char * p, ssize_t n, const struct sockaddr * addr) { const char * linestart; const char * lineend; const char * nameend; const char * valuestart; struct header headers[3]; int i, r = 0; int methodlen; int nts = -1; int method = -1; unsigned int lifetime = 180; /* 3 minutes by default */ const char * st = NULL; int st_len = 0; memset(headers, 0, sizeof(headers)); for(methodlen = 0; methodlen < n && (isalpha(p[methodlen]) || p[methodlen]=='-'); methodlen++); if(methodlen==8 && 0==memcmp(p, "M-SEARCH", 8)) method = METHOD_MSEARCH; else if(methodlen==6 && 0==memcmp(p, "NOTIFY", 6)) method = METHOD_NOTIFY; linestart = p; while(linestart < p + n - 2) { /* start parsing the line : detect line end */ lineend = linestart; while(lineend < p + n && *lineend != '\n' && *lineend != '\r') lineend++; //printf("line: '%.*s'\n", lineend - linestart, linestart); /* detect name end : ':' character */ nameend = linestart; while(nameend < lineend && *nameend != ':') nameend++; /* detect value */ if(nameend < lineend) valuestart = nameend + 1; else valuestart = nameend; /* trim spaces */ while(valuestart < lineend && isspace(*valuestart)) valuestart++; /* suppress leading " if needed */ if(valuestart < lineend && *valuestart=='\"') valuestart++; if(nameend > linestart && valuestart < lineend) { int l = nameend - linestart; /* header name length */ int m = lineend - valuestart; /* header value length */ /* suppress tailing spaces */ while(m>0 && isspace(valuestart[m-1])) m--; /* suppress tailing ' if needed */ if(m>0 && valuestart[m-1] == '\"') m--; i = -1; /*printf("--%.*s: (%d)%.*s--\n", l, linestart, m, m, valuestart);*/ if(l==2 && 0==strncasecmp(linestart, "nt", 2)) i = HEADER_NT; else if(l==3 && 0==strncasecmp(linestart, "usn", 3)) i = HEADER_USN; else if(l==3 && 0==strncasecmp(linestart, "nts", 3)) { if(m==10 && 0==strncasecmp(valuestart, "ssdp:alive", 10)) nts = NTS_SSDP_ALIVE; else if(m==11 && 0==strncasecmp(valuestart, "ssdp:byebye", 11)) nts = NTS_SSDP_BYEBYE; } else if(l==8 && 0==strncasecmp(linestart, "location", 8)) i = HEADER_LOCATION; else if(l==13 && 0==strncasecmp(linestart, "cache-control", 13)) { /* parse "name1=value1, name_alone, name2=value2" string */ const char * name = valuestart; /* name */ const char * val; /* value */ int rem = m; /* remaining bytes to process */ while(rem > 0) { val = name; while(val < name + rem && *val != '=' && *val != ',') val++; if(val >= name + rem) break; if(*val == '=') { while(val < name + rem && (*val == '=' || isspace(*val))) val++; if(val >= name + rem) break; if(0==strncasecmp(name, "max-age", 7)) lifetime = (unsigned int)strtoul(val, 0, 0); /* move to the next name=value pair */ while(rem > 0 && *name != ',') { rem--; name++; } /* skip spaces */ while(rem > 0 && (*name == ',' || isspace(*name))) { rem--; name++; } } else { rem -= (val - name); name = val; while(rem > 0 && (*name == ',' || isspace(*name))) { rem--; name++; } } } /*syslog(LOG_DEBUG, "**%.*s**%u", m, valuestart, lifetime);*/ } else if(l==2 && 0==strncasecmp(linestart, "st", 2)) { st = valuestart; st_len = m; } if(i>=0) { headers[i].p = valuestart; headers[i].l = m; } } linestart = lineend; while((*linestart == '\n' || *linestart == '\r') && linestart < p + n) linestart++; } #if 0 printf("NTS=%d\n", nts); for(i=0; i<3; i++) { if(headers[i].p) printf("%d-'%.*s'\n", i, headers[i].l, headers[i].p); } #endif syslog(LOG_DEBUG,"SSDP request: '%.*s' (%d) st=%.*s", methodlen, p, method, st_len, st); switch(method) { case METHOD_NOTIFY: if(headers[0].p && headers[1].p && headers[2].p) { if(nts==NTS_SSDP_ALIVE) { r = updateDevice(headers, time(NULL) + lifetime); } else if(nts==NTS_SSDP_BYEBYE) { r = removeDevice(headers); } } break; case METHOD_MSEARCH: processMSEARCH(s, st, st_len, addr); break; default: syslog(LOG_WARNING, "method %.*s, don't know what to do", methodlen, p); } return r; } /* OpenUnixSocket() * open the unix socket and call bind() and listen() * return -1 in case of error */ static int OpenUnixSocket(const char * path) { struct sockaddr_un addr; int s; int rv; s = socket(AF_UNIX, SOCK_STREAM, 0); if(s < 0) { syslog(LOG_ERR, "socket(AF_UNIX): %m"); return -1; } /* unlink the socket pseudo file before binding */ rv = unlink(path); if(rv < 0 && errno != ENOENT) { syslog(LOG_ERR, "unlink(unixsocket, \"%s\"): %m", path); close(s); return -1; } addr.sun_family = AF_UNIX; strncpy(addr.sun_path, path, sizeof(addr.sun_path)); if(bind(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) { syslog(LOG_ERR, "bind(unixsocket, \"%s\"): %m", path); close(s); return -1; } else if(listen(s, 5) < 0) { syslog(LOG_ERR, "listen(unixsocket): %m"); close(s); return -1; } /* Change rights so everyone can communicate with us */ if(chmod(path, 0666) < 0) { syslog(LOG_WARNING, "chmod(\"%s\"): %m", path); } return s; } /* processRequest() : * process the request coming from a unix socket */ void processRequest(struct reqelem * req) { ssize_t n; unsigned int l, m; unsigned char buf[2048]; const unsigned char * p; int type; struct device * d = devlist; unsigned char rbuf[4096]; unsigned char * rp = rbuf+1; unsigned char nrep = 0; time_t t; struct service * newserv = NULL; struct service * serv; n = read(req->socket, buf, sizeof(buf)); if(n<0) { syslog(LOG_ERR, "(s=%d) processRequest(): read(): %m", req->socket); goto error; } if(n==0) { syslog(LOG_INFO, "(s=%d) request connection closed", req->socket); goto error; } t = time(NULL); type = buf[0]; p = buf + 1; DECODELENGTH_CHECKLIMIT(l, p, buf + n); if(p+l > buf+n) { syslog(LOG_WARNING, "bad request (length encoding)"); goto error; } if(l == 0) { syslog(LOG_WARNING, "bad request (length=0)"); goto error; } syslog(LOG_INFO, "(s=%d) request type=%d str='%.*s'", req->socket, type, l, p); switch(type) { case 1: case 2: case 3: while(d && (nrep < 255)) { if(d->t < t) { syslog(LOG_INFO, "outdated device"); } else { /* test if we can put more responses in the buffer */ if(d->headers[HEADER_LOCATION].l + d->headers[HEADER_NT].l + d->headers[HEADER_USN].l + 6 + (rp - rbuf) >= sizeof(rbuf)) break; if( (type==1 && 0==memcmp(d->headers[HEADER_NT].p, p, l)) ||(type==2 && 0==memcmp(d->headers[HEADER_USN].p, p, l)) ||(type==3) ) { /* response : * 1 - Location * 2 - NT (device/service type) * 3 - usn */ m = d->headers[HEADER_LOCATION].l; CODELENGTH(m, rp); memcpy(rp, d->headers[HEADER_LOCATION].p, d->headers[HEADER_LOCATION].l); rp += d->headers[HEADER_LOCATION].l; m = d->headers[HEADER_NT].l; CODELENGTH(m, rp); memcpy(rp, d->headers[HEADER_NT].p, d->headers[HEADER_NT].l); rp += d->headers[HEADER_NT].l; m = d->headers[HEADER_USN].l; CODELENGTH(m, rp); memcpy(rp, d->headers[HEADER_USN].p, d->headers[HEADER_USN].l); rp += d->headers[HEADER_USN].l; nrep++; } } d = d->next; } /* Also look in service list */ for(serv = servicelisthead.lh_first; serv && (nrep < 255); serv = serv->entries.le_next) { /* test if we can put more responses in the buffer */ if(strlen(serv->location) + strlen(serv->st) + strlen(serv->usn) + 6 + (rp - rbuf) >= sizeof(rbuf)) break; if( (type==1 && 0==strncmp(serv->st, (const char *)p, l)) ||(type==2 && 0==strncmp(serv->usn, (const char *)p, l)) ||(type==3) ) { /* response : * 1 - Location * 2 - NT (device/service type) * 3 - usn */ m = strlen(serv->location); CODELENGTH(m, rp); memcpy(rp, serv->location, m); rp += m; m = strlen(serv->st); CODELENGTH(m, rp); memcpy(rp, serv->st, m); rp += m; m = strlen(serv->usn); CODELENGTH(m, rp); memcpy(rp, serv->usn, m); rp += m; nrep++; } } rbuf[0] = nrep; if(write(req->socket, rbuf, rp - rbuf) < 0) { syslog(LOG_ERR, "(s=%d) write: %m", req->socket); goto error; } break; case 4: newserv = malloc(sizeof(struct service)); if(!newserv) { syslog(LOG_ERR, "cannot allocate memory"); goto error; } if(containsForbiddenChars(p, l)) { syslog(LOG_ERR, "bad request (st contains forbidden chars)"); goto error; } newserv->st = malloc(l + 1); if(!newserv->st) { syslog(LOG_ERR, "cannot allocate memory"); goto error; } memcpy(newserv->st, p, l); newserv->st[l] = '\0'; p += l; if(p >= buf + n) { syslog(LOG_WARNING, "bad request (missing usn)"); goto error; } DECODELENGTH_CHECKLIMIT(l, p, buf + n); if(p+l > buf+n) { syslog(LOG_WARNING, "bad request (length encoding)"); goto error; } if(containsForbiddenChars(p, l)) { syslog(LOG_ERR, "bad request (usn contains forbidden chars)"); goto error; } syslog(LOG_INFO, "usn='%.*s'", l, p); newserv->usn = malloc(l + 1); if(!newserv->usn) { syslog(LOG_ERR, "cannot allocate memory"); goto error; } memcpy(newserv->usn, p, l); newserv->usn[l] = '\0'; p += l; DECODELENGTH_CHECKLIMIT(l, p, buf + n); if(p+l > buf+n) { syslog(LOG_WARNING, "bad request (length encoding)"); goto error; } if(containsForbiddenChars(p, l)) { syslog(LOG_ERR, "bad request (server contains forbidden chars)"); goto error; } syslog(LOG_INFO, "server='%.*s'", l, p); newserv->server = malloc(l + 1); if(!newserv->server) { syslog(LOG_ERR, "cannot allocate memory"); goto error; } memcpy(newserv->server, p, l); newserv->server[l] = '\0'; p += l; DECODELENGTH_CHECKLIMIT(l, p, buf + n); if(p+l > buf+n) { syslog(LOG_WARNING, "bad request (length encoding)"); goto error; } if(containsForbiddenChars(p, l)) { syslog(LOG_ERR, "bad request (location contains forbidden chars)"); goto error; } syslog(LOG_INFO, "location='%.*s'", l, p); newserv->location = malloc(l + 1); if(!newserv->location) { syslog(LOG_ERR, "cannot allocate memory"); goto error; } memcpy(newserv->location, p, l); newserv->location[l] = '\0'; /* look in service list for duplicate */ for(serv = servicelisthead.lh_first; serv; serv = serv->entries.le_next) { if(0 == strcmp(newserv->usn, serv->usn) && 0 == strcmp(newserv->st, serv->st)) { syslog(LOG_INFO, "Service allready in the list. Updating..."); free(newserv->st); free(newserv->usn); free(serv->server); serv->server = newserv->server; free(serv->location); serv->location = newserv->location; free(newserv); newserv = NULL; return; } } /* Inserting new service */ LIST_INSERT_HEAD(&servicelisthead, newserv, entries); newserv = NULL; /*rbuf[0] = '\0'; if(write(req->socket, rbuf, 1) < 0) syslog(LOG_ERR, "(s=%d) write: %m", req->socket); */ break; default: syslog(LOG_WARNING, "Unknown request type %d", type); rbuf[0] = '\0'; if(write(req->socket, rbuf, 1) < 0) { syslog(LOG_ERR, "(s=%d) write: %m", req->socket); goto error; } } return; error: if(newserv) { free(newserv->st); free(newserv->usn); free(newserv->server); free(newserv->location); free(newserv); newserv = NULL; } close(req->socket); req->socket = -1; return; } static volatile int quitting = 0; /* SIGTERM signal handler */ static void sigterm(int sig) { signal(sig, SIG_IGN); syslog(LOG_NOTICE, "received signal %d, good-bye", sig); quitting = 1; } /* main(): program entry point */ int main(int argc, char * * argv) { int ret = 0; int pid; struct sigaction sa; char buf[1500]; ssize_t n; int s_ssdp = -1; /* udp socket receiving ssdp packets */ #ifdef ENABLE_IPV6 int s_ssdp6 = -1; /* udp socket receiving ssdp packets IPv6*/ #else #define s_ssdp6 (-1) #endif int s_unix = -1; /* unix socket communicating with clients */ int s_ifacewatch = -1; /* socket to receive Route / network interface config changes */ int s; LIST_HEAD(reqstructhead, reqelem) reqlisthead; struct reqelem * req; struct reqelem * reqnext; fd_set readfds; const char * if_addr[MAX_IF_ADDR]; int n_if_addr = 0; int i; const char * sockpath = "/var/run/minissdpd.sock"; const char * pidfilename = "/var/run/minissdpd.pid"; int debug_flag = 0; int ipv6 = 0; int deltadev = 0; struct sockaddr_in sendername; socklen_t sendername_len; #ifdef ENABLE_IPV6 struct sockaddr_in6 sendername6; socklen_t sendername6_len; #endif LIST_INIT(&reqlisthead); LIST_INIT(&servicelisthead); /* process command line */ for(i=1; i [-i ] ...\n", argv[0]); fprintf(stderr, "\n is either an IPv4 address such as 192.168.1.42, or an\ninterface name such as eth0.\n"); fprintf(stderr, "\n By default, socket will be open as %s\n" "and pid written to file %s\n", sockpath, pidfilename); return 1; } /* open log */ openlog("minissdpd", LOG_CONS|LOG_PID|(debug_flag?LOG_PERROR:0), LOG_MINISSDPD); if(!debug_flag) /* speed things up and ignore LOG_INFO and LOG_DEBUG */ setlogmask(LOG_UPTO(LOG_NOTICE)); if(checkforrunning(pidfilename) < 0) { syslog(LOG_ERR, "MiniSSDPd is already running. EXITING"); return 1; } /* set signal handlers */ memset(&sa, 0, sizeof(struct sigaction)); sa.sa_handler = sigterm; if(sigaction(SIGTERM, &sa, NULL)) { syslog(LOG_ERR, "Failed to set SIGTERM handler. EXITING"); ret = 1; goto quit; } if(sigaction(SIGINT, &sa, NULL)) { syslog(LOG_ERR, "Failed to set SIGINT handler. EXITING"); ret = 1; goto quit; } /* open route/interface config changes socket */ s_ifacewatch = OpenAndConfInterfaceWatchSocket(); /* open UDP socket(s) for receiving SSDP packets */ s_ssdp = OpenAndConfSSDPReceiveSocket(n_if_addr, if_addr, 0); if(s_ssdp < 0) { syslog(LOG_ERR, "Cannot open socket for receiving SSDP messages, exiting"); ret = 1; goto quit; } #ifdef ENABLE_IPV6 if(ipv6) { s_ssdp6 = OpenAndConfSSDPReceiveSocket(n_if_addr, if_addr, 1); if(s_ssdp6 < 0) { syslog(LOG_ERR, "Cannot open socket for receiving SSDP messages (IPv6), exiting"); ret = 1; goto quit; } } #endif /* Open Unix socket to communicate with other programs on * the same machine */ s_unix = OpenUnixSocket(sockpath); if(s_unix < 0) { syslog(LOG_ERR, "Cannot open unix socket for communicating with clients. Exiting"); ret = 1; goto quit; } /* drop privileges */ #if 0 /* if we drop privileges, how to unlink(/var/run/minissdpd.sock) ? */ if(getuid() == 0) { struct passwd * user; struct group * group; user = getpwnam("nobody"); if(!user) { syslog(LOG_ERR, "getpwnam(\"%s\") : %m", "nobody"); ret = 1; goto quit; } group = getgrnam("nogroup"); if(!group) { syslog(LOG_ERR, "getgrnam(\"%s\") : %m", "nogroup"); ret = 1; goto quit; } if(setgid(group->gr_gid) < 0) { syslog(LOG_ERR, "setgit(%d) : %m", group->gr_gid); ret = 1; goto quit; } if(setuid(user->pw_uid) < 0) { syslog(LOG_ERR, "setuid(%d) : %m", user->pw_uid); ret = 1; goto quit; } } #endif /* daemonize or in any case get pid ! */ if(debug_flag) pid = getpid(); else { #ifdef USE_DAEMON if(daemon(0, 0) < 0) perror("daemon()"); pid = getpid(); #else pid = daemonize(); #endif } writepidfile(pidfilename, pid); /* Main loop */ while(!quitting) { /* fill readfds fd_set */ FD_ZERO(&readfds); if(s_ssdp >= 0) { FD_SET(s_ssdp, &readfds); } #ifdef ENABLE_IPV6 if(s_ssdp6 >= 0) { FD_SET(s_ssdp6, &readfds); } #endif if(s_ifacewatch >= 0) { FD_SET(s_ifacewatch, &readfds); } FD_SET(s_unix, &readfds); for(req = reqlisthead.lh_first; req; req = req->entries.le_next) { if(req->socket >= 0) FD_SET(req->socket, &readfds); } /* select call */ if(select(FD_SETSIZE, &readfds, 0, 0, 0) < 0) { if(errno != EINTR) syslog(LOG_ERR, "select: %m"); break; } #ifdef ENABLE_IPV6 if((s_ssdp6 >= 0) && FD_ISSET(s_ssdp6, &readfds)) { sendername6_len = sizeof(struct sockaddr_in6); n = recvfrom(s_ssdp6, buf, sizeof(buf), 0, (struct sockaddr *)&sendername6, &sendername6_len); if(n<0) { syslog(LOG_ERR, "recvfrom: %m"); } else { /* Parse and process the packet received */ /*printf("%.*s", n, buf);*/ i = ParseSSDPPacket(s_ssdp6, buf, n, (struct sockaddr *)&sendername6); syslog(LOG_DEBUG, "** i=%d deltadev=%d **", i, deltadev); if(i==0 || (i*deltadev < 0)) { if(deltadev > 0) syslog(LOG_NOTICE, "%d new devices added", deltadev); else if(deltadev < 0) syslog(LOG_NOTICE, "%d devices removed (good-bye!)", -deltadev); deltadev = i; } else if((i*deltadev) >= 0) { deltadev += i; } } } #endif if(FD_ISSET(s_ssdp, &readfds)) { sendername_len = sizeof(struct sockaddr_in); n = recvfrom(s_ssdp, buf, sizeof(buf), 0, (struct sockaddr *)&sendername, &sendername_len); if(n<0) { syslog(LOG_ERR, "recvfrom: %m"); } else { /* Parse and process the packet received */ /*printf("%.*s", n, buf);*/ i = ParseSSDPPacket(s_ssdp, buf, n, (struct sockaddr *)&sendername); syslog(LOG_DEBUG, "** i=%d deltadev=%d **", i, deltadev); if(i==0 || (i*deltadev < 0)) { if(deltadev > 0) syslog(LOG_NOTICE, "%d new devices added", deltadev); else if(deltadev < 0) syslog(LOG_NOTICE, "%d devices removed (good-bye!)", -deltadev); deltadev = i; } else if((i*deltadev) >= 0) { deltadev += i; } } } /* processing unix socket requests */ for(req = reqlisthead.lh_first; req;) { reqnext = req->entries.le_next; if((req->socket >= 0) && FD_ISSET(req->socket, &readfds)) { processRequest(req); } if(req->socket < 0) { LIST_REMOVE(req, entries); free(req); } req = reqnext; } /* processing new requests */ if(FD_ISSET(s_unix, &readfds)) { struct reqelem * tmp; s = accept(s_unix, NULL, NULL); if(s<0) { syslog(LOG_ERR, "accept(s_unix): %m"); } else { syslog(LOG_INFO, "(s=%d) new request connection", s); tmp = malloc(sizeof(struct reqelem)); if(!tmp) { syslog(LOG_ERR, "cannot allocate memory for request"); close(s); } else { tmp->socket = s; LIST_INSERT_HEAD(&reqlisthead, tmp, entries); } } } /* processing route/network interface config changes */ if((s_ifacewatch >= 0) && FD_ISSET(s_ifacewatch, &readfds)) { ProcessInterfaceWatch(s_ifacewatch, s_ssdp, s_ssdp6, n_if_addr, if_addr); } } /* closing and cleaning everything */ quit: if(s_ssdp >= 0) { close(s_ssdp); s_ssdp = -1; } #ifdef ENABLE_IPV6 if(s_ssdp6 >= 0) { close(s_ssdp6); s_ssdp6 = -1; } #endif if(s_unix >= 0) { close(s_unix); s_unix = -1; if(unlink(sockpath) < 0) syslog(LOG_ERR, "unlink(%s): %m", sockpath); } if(s_ifacewatch >= 0) { close(s_ifacewatch); s_ifacewatch = -1; } if(unlink(pidfilename) < 0) syslog(LOG_ERR, "unlink(%s): %m", pidfilename); closelog(); return ret; } minissdpd-1.1.20120121/openssdpsocket.c010064400017500000024000000134261164354516500166460ustar00nanardstaff/* $Id: openssdpsocket.c,v 1.8 2011/10/07 09:21:03 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2011 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include "config.h" #include #include #include #include #include #include #include #include #include "openssdpsocket.h" /* SSDP ip/port */ #define SSDP_PORT (1900) #define SSDP_MCAST_ADDR ("239.255.255.250") /* Link Local and Site Local SSDP IPv6 multicast addresses */ #define LL_SSDP_MCAST_ADDR ("FF02::C") #define SL_SSDP_MCAST_ADDR ("FF05::C") /** * Get the IPv4 address from a string * representing the address or the interface name */ static in_addr_t GetIfAddrIPv4(const char * ifaddr) { in_addr_t addr; int s; struct ifreq ifr; int ifrlen; /* let's suppose ifaddr is a IPv4 address * such as 192.168.1.1 */ addr = inet_addr(ifaddr); if(addr != INADDR_NONE) { return addr; } /* let's suppose the ifaddr was in fact an interface name * such as eth0 */ s = socket(PF_INET, SOCK_DGRAM, 0); if(s < 0) { syslog(LOG_ERR, "socket(PF_INET, SOCK_DGRAM): %m"); return INADDR_NONE; } memset(&ifr, 0, sizeof(struct ifreq)); strncpy(ifr.ifr_name, ifaddr, IFNAMSIZ); if(ioctl(s, SIOCGIFADDR, &ifr, &ifrlen) < 0) { syslog(LOG_ERR, "ioctl(s, SIOCGIFADDR, ...): %m"); close(s); return INADDR_NONE; } syslog(LOG_DEBUG, "GetIfAddrIPv4(%s) = %s", ifaddr, inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr)); addr = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr; close(s); return addr; } /** * Add the multicast membership for SSDP on the interface * @param s the socket * @param ifaddr the IPv4 address or interface name * @param ipv6 IPv6 or IPv4 * return -1 on error, 0 on success */ int AddDropMulticastMembership(int s, const char * ifaddr, int ipv6, int drop) { struct ip_mreq imr; /* Ip multicast membership */ #ifdef ENABLE_IPV6 struct ipv6_mreq mr; unsigned int ifindex; #endif #ifdef ENABLE_IPV6 if(ipv6) { ifindex = if_nametoindex(ifaddr); memset(&mr, 0, sizeof(mr)); inet_pton(AF_INET6, LL_SSDP_MCAST_ADDR, &mr.ipv6mr_multiaddr); mr.ipv6mr_interface = ifindex; if(setsockopt(s, IPPROTO_IPV6, drop ? IPV6_LEAVE_GROUP : IPV6_JOIN_GROUP, &mr, sizeof(struct ipv6_mreq)) < 0) { syslog(LOG_ERR, "setsockopt(udp, %s)(%s): %m", drop ? "IPV6_LEAVE_GROUP" : "IPV6_JOIN_GROUP", ifaddr); return -1; } inet_pton(AF_INET6, SL_SSDP_MCAST_ADDR, &mr.ipv6mr_multiaddr); if(setsockopt(s, IPPROTO_IPV6, drop ? IPV6_LEAVE_GROUP : IPV6_JOIN_GROUP, &mr, sizeof(struct ipv6_mreq)) < 0) { syslog(LOG_ERR, "setsockopt(udp, %s)(%s): %m", drop ? "IPV6_LEAVE_GROUP" : "IPV6_JOIN_GROUP", ifaddr); return -1; } } else { #endif /* setting up imr structure */ imr.imr_multiaddr.s_addr = inet_addr(SSDP_MCAST_ADDR); /*imr.imr_interface.s_addr = htonl(INADDR_ANY);*/ /*imr.imr_interface.s_addr = inet_addr(ifaddr);*/ imr.imr_interface.s_addr = GetIfAddrIPv4(ifaddr); if(imr.imr_interface.s_addr == INADDR_NONE) { syslog(LOG_ERR, "no IPv4 address for interface %s", ifaddr); return -1; } if (setsockopt(s, IPPROTO_IP, drop ? IP_DROP_MEMBERSHIP : IP_ADD_MEMBERSHIP, (void *)&imr, sizeof(struct ip_mreq)) < 0) { syslog(LOG_ERR, "setsockopt(udp, %s)(%s): %m", drop ? "IP_DROP_MEMBERSHIP" : "IP_ADD_MEMBERSHIP", ifaddr); return -1; } #ifdef ENABLE_IPV6 } #endif return 0; } int OpenAndConfSSDPReceiveSocket(int n_listen_addr, const char * * listen_addr, int ipv6) { int s; int opt = 1; #ifdef ENABLE_IPV6 struct sockaddr_storage sockname; #else struct sockaddr_in sockname; #endif socklen_t sockname_len; #ifdef ENABLE_IPV6 if( (s = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, 0)) < 0) #else if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) #endif { syslog(LOG_ERR, "socket(udp): %m"); return -1; } #ifdef ENABLE_IPV6 memset(&sockname, 0, sizeof(struct sockaddr_storage)); if(ipv6) { #ifdef IPV6_V6ONLY if(setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&opt, sizeof(opt)) < 0) { syslog(LOG_WARNING, "setsockopt(IPV6_V6ONLY): %m"); } #endif struct sockaddr_in6 * sa = (struct sockaddr_in6 *)&sockname; sa->sin6_family = AF_INET6; sa->sin6_port = htons(SSDP_PORT); sa->sin6_addr = in6addr_any; sockname_len = sizeof(struct sockaddr_in6); } else { struct sockaddr_in * sa = (struct sockaddr_in *)&sockname; sa->sin_family = AF_INET; sa->sin_port = htons(SSDP_PORT); sa->sin_addr.s_addr = htonl(INADDR_ANY); sockname_len = sizeof(struct sockaddr_in); } #else memset(&sockname, 0, sizeof(struct sockaddr_in)); sockname.sin_family = AF_INET; sockname.sin_port = htons(SSDP_PORT); /* NOTE : it seems it doesnt work when binding on the specific address */ /*sockname.sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);*/ sockname.sin_addr.s_addr = htonl(INADDR_ANY); /*sockname.sin_addr.s_addr = inet_addr(ifaddr);*/ sockname_len = sizeof(struct sockaddr_in); #endif if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { syslog(LOG_WARNING, "setsockopt(SO_REUSEADDR): %m"); } if(bind(s, (struct sockaddr *)&sockname, sockname_len) < 0) { syslog(LOG_ERR, "bind(udp%s): %m", ipv6 ? "6" : ""); close(s); return -1; } while(n_listen_addr>0) { n_listen_addr--; if(AddDropMulticastMembership(s, listen_addr[n_listen_addr], ipv6, 0) < 0) { syslog(LOG_WARNING, "Failed to add IPv%d multicast membership for interface %s.", ipv6 ? 6 : 4, listen_addr[n_listen_addr] ); } } return s; } minissdpd-1.1.20120121/testminissdpd.c010064400017500000024000000050251073222772300164630ustar00nanardstaff/* $Id: testminissdpd.c,v 1.6 2007/12/19 14:49:30 nanard Exp $ */ /* Project : miniupnp * website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * Author : Thomas BERNARD * copyright (c) 2005-2007 Thomas Bernard * This software is subjet to the conditions detailed in the * provided LICENCE file. */ #include #include #include #include #include #include #define DECODELENGTH(n, p) n = 0; \ do { n = (n << 7) | (*p & 0x7f); } \ while(*(p++)&0x80); void printresponse(const unsigned char * resp, int n) { int i, l; unsigned int nresp; const unsigned char * p; for(i=0; i/dev/null # DO NOT DELETE minissdpd.o: config.h upnputils.h openssdpsocket.h daemonize.h codelength.h minissdpd.o: ifacewatch.h openssdpsocket.o: config.h openssdpsocket.h daemonize.o: daemonize.h config.h upnputils.o: config.h upnputils.h ifacewatch.o: config.h openssdpsocket.h testcodelength.o: codelength.h minissdpd-1.1.20120121/minissdpd.init.d.script010064400017500000024000000023641067552322100200320ustar00nanardstaff#!/bin/sh # $Id: minissdpd.init.d.script,v 1.2 2007/09/23 17:46:57 nanard Exp $ # MiniUPnP project # author: Thomas Bernard # website: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ MINISSDPD=/usr/sbin/minissdpd PIDFILE=/var/run/minissdpd.pid # get default interface IF=`route | grep default |awk -- '{ print $8 }'` ARGS="-i `ifconfig $IF|grep 'inet adr'|sed 's/.\+inet adr:\([0-9.]\+\).\+/\1/'`" test -f $MINISSDPD || exit 0 . /lib/lsb/init-functions case "$1" in start) log_daemon_msg "Starting minissdpd" "minissdpd" start-stop-daemon --start --quiet --pidfile $PIDFILE \ --exec $MINISSDPD -- $ARGS $LSBNAMES log_end_msg $? ;; stop) log_daemon_msg "Stopping minissdpd" "minissdpd" start-stop-daemon --stop --quiet --pidfile $PIDFILE log_end_msg $? ;; restart|reload|force-reload) log_daemon_msg "Restarting minissdpd" "minissdpd" start-stop-daemon --stop --retry 5 --quiet --pidfile $PIDFILE start-stop-daemon --start --quiet --pidfile $PIDFILE \ --exec $MINISSDPD -- $ARGS $LSBNAMES log_end_msg $? ;; *) log_action_msg "Usage: /etc/init.d/minissdpd {start|stop|restart|reload|force-reload}" exit 2 ;; esac exit 0 minissdpd-1.1.20120121/LICENSE010064400017500000024000000026771130743304300144350ustar00nanardstaffCopyright (c) 2007-2009, Thomas BERNARD All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. minissdpd-1.1.20120121/minissdpd.1010064400017500000024000000020031164434541100154700ustar00nanardstaff.TH "minissdpd" 1 .SH NAME minissdpd \- daemon keeping track of UPnP devices up .SH SYNOPSIS .B minissdpd [\-d] [\-s socket] [\-p pidfile] \-i address [\-i address] ... .SH DESCRIPTION minissdpd listen for SSDP traffic and keeps track of what are the UPnP devices up on the network. The list of the UPnP devices is accessed by programs looking for devices, skipping the UPnP discovery process. .SH OPTIONS .TP .B \-d debug : do not go to background, output messages to console and do not filter out low priority messages. .TP .B \-6 IPv6 : Enable IPv6 in addition to IPv4. .TP .B \-s socket path of the unix socket open for communicating with other processes. By default /var/run/minissdpd.sock is used. .TP .B \-p pidfile path of the file where pid is written at startup. By default /var/run/minissdpd.pid is used. .TP .B \-i interface name or IP address of the interface used to listen to SSDP packets comming on port 1900, multicast address 239.255.255.250. .SH "SEE ALSO" miniupnpd(1) miniupnpc(3) .SH BUGS No known bugs. minissdpd-1.1.20120121/config.h010064400017500000024000000011471156645077200150540ustar00nanardstaff/* $Id: config.h,v 1.4 2011/05/23 12:22:29 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2011 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #ifndef __CONFIG_H__ #define __CONFIG_H__ /* use BSD daemon() ? */ #define USE_DAEMON /* set the syslog facility to use. See man syslog(3) and syslog.conf(5). */ #define LOG_MINISSDPD LOG_DAEMON /* enable IPv6 */ #define ENABLE_IPV6 /* Maximum number of network interface we can listen on */ #define MAX_IF_ADDR 8 #endif minissdpd-1.1.20120121/README010064400017500000024000000015451107237113100142770ustar00nanardstaffprotocole : connection à la socket unix. envoie d'une requete, retour d'une reponse. fermeture de la connexion. format de requete : 1 octet : type de la requete 1 - type 2 - USN (id unique) 3 - tout n octets longueur de la chaine : 1 octet si < 128 sinon le bit haut indique s'il existe un octet suplementaire, etc... n octets = chaine format reponse : 1 octet : nombre de reponses pour chaque rep : URL : n octets longueur de la chaine n octets = chaine Location ST: n octets longueur de la chaine n octets = chaine type USN: n octets longueur de la chaine n octets = chaine identifiant Type de requete 4 = submit service 1 octet = 4 (k,n) octets : longueur et chaine "ST" (service type) (k,n) octets : longueur et chaine "USN" (k,n) octets : longueur et chaine "Server" (k,n) octets : longueur et chaine "Location" Pas de reponse minissdpd-1.1.20120121/testcodelength.c010064400017500000024000000012511157675713100166120ustar00nanardstaff/* $Id: testcodelength.c,v 1.2 2011/06/17 23:05:00 nanard Exp $ */ /* Project : miniupnp * Author : Thomas BERNARD * copyright (c) 2005-2008 Thomas Bernard * This software is subjet to the conditions detailed in the * provided LICENCE file. */ #include #include "codelength.h" int main(int argc, char * * argv) { unsigned char buf[256]; unsigned char * p; long i, j; for(i = 1; i < 1000000000; i *= 2) { /* encode i, decode to j */ printf("%ld ", i); p = buf; CODELENGTH(i, p); p = buf; DECODELENGTH(j, p); if(i != j) { fprintf(stderr, "Error ! encoded %ld, decoded %ld.\n", i, j); return 1; } } printf("Test succesful\n"); return 0; } minissdpd-1.1.20120121/codelength.h010064400017500000024000000021131161500175300156770ustar00nanardstaff/* $Id: codelength.h,v 1.3 2011/07/30 13:10:05 nanard Exp $ */ /* Project : miniupnp * Author : Thomas BERNARD * copyright (c) 2005-2011 Thomas Bernard * This software is subjet to the conditions detailed in the * provided LICENCE file. */ #ifndef __CODELENGTH_H__ #define __CODELENGTH_H__ /* Encode length by using 7bit per Byte : * Most significant bit of each byte specifies that the * following byte is part of the code */ #define DECODELENGTH(n, p) n = 0; \ do { n = (n << 7) | (*p & 0x7f); } \ while((*(p++)&0x80) && (n<(1<<25))); #define DECODELENGTH_CHECKLIMIT(n, p, p_limit) \ n = 0; \ do { \ if((p) >= (p_limit)) break; \ n = (n << 7) | (*(p) & 0x7f); \ } while((*((p)++)&0x80) && (n<(1<<25))); #define CODELENGTH(n, p) if(n>=268435456) *(p++) = (n >> 28) | 0x80; \ if(n>=2097152) *(p++) = (n >> 21) | 0x80; \ if(n>=16384) *(p++) = (n >> 14) | 0x80; \ if(n>=128) *(p++) = (n >> 7) | 0x80; \ *(p++) = n & 0x7f; #endif minissdpd-1.1.20120121/upnputils.c010064400017500000024000000035121156543357700156460ustar00nanardstaff/* $Id: upnputils.c,v 1.3 2011/05/20 09:42:23 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2006-2011 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include "config.h" #include #include #include #include #include #ifdef AF_LINK #include #endif #include "upnputils.h" int sockaddr_to_string(const struct sockaddr * addr, char * str, size_t size) { char buffer[64]; unsigned short port = 0; int n = -1; switch(addr->sa_family) { case AF_INET6: inet_ntop(addr->sa_family, &((struct sockaddr_in6 *)addr)->sin6_addr, buffer, sizeof(buffer)); port = ntohs(((struct sockaddr_in6 *)addr)->sin6_port); n = snprintf(str, size, "[%s]:%hu", buffer, port); break; case AF_INET: inet_ntop(addr->sa_family, &((struct sockaddr_in *)addr)->sin_addr, buffer, sizeof(buffer)); port = ntohs(((struct sockaddr_in *)addr)->sin_port); n = snprintf(str, size, "%s:%hu", buffer, port); break; #ifdef AF_LINK case AF_LINK: { struct sockaddr_dl * sdl = (struct sockaddr_dl *)addr; n = snprintf(str, size, "index=%hu type=%d %s", sdl->sdl_index, sdl->sdl_type, link_ntoa(sdl)); } break; #endif default: n = snprintf(str, size, "unknown address family %d", addr->sa_family); #if 0 n = snprintf(str, size, "unknown address family %d " "%02x %02x %02x %02x %02x %02x %02x %02x", addr->sa_family, addr->sa_data[0], addr->sa_data[1], (unsigned)addr->sa_data[2], addr->sa_data[3], addr->sa_data[4], addr->sa_data[5], (unsigned)addr->sa_data[6], addr->sa_data[7]); #endif } return n; } minissdpd-1.1.20120121/upnputils.h010064400017500000024000000010721156371270100156350ustar00nanardstaff/* $Id: upnputils.h,v 1.1 2011/05/15 08:58:41 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2011 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #ifndef __UPNPUTILS_H__ #define __UPNPUTILS_H__ /** * convert a struct sockaddr to a human readable string. * [ipv6]:port or ipv4:port * return the number of characters used (as snprintf) */ int sockaddr_to_string(const struct sockaddr * addr, char * str, size_t size); #endif minissdpd-1.1.20120121/VERSION010064400017500000024000000000041161500205100144470ustar00nanardstaff1.1 minissdpd-1.1.20120121/ifacewatch.h010064400017500000024000000007771161454775100157130ustar00nanardstaff/* $Id: ifacewatch.h,v 1.1 2011/07/29 15:21:13 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * (c) 2011 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #ifndef __IFACEWATCH_H__ #define __IFACEWATCH_H__ int OpenAndConfInterfaceWatchSocket(void); int ProcessInterfaceWatch(int s, int s_ssdp, int s_ssdp6, int n_if_addr, const char * * if_addr); #endif minissdpd-1.1.20120121/ifacewatch.c010064400017500000024000000100621170654255300156670ustar00nanardstaff/* $Id: ifacewatch.c,v 1.3 2012/01/20 22:08:07 nanard Exp $ */ /* MiniUPnP project * (c) 2011 Thomas Bernard * website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include "config.h" #include #include #include #include #include #include #include #include #ifdef __linux__ #include #include #else /* __linux__ */ #include #endif #include #include "openssdpsocket.h" int OpenAndConfInterfaceWatchSocket(void) { int s; #ifdef __linux__ struct sockaddr_nl addr; s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); #else /* __linux__*/ /*s = socket(PF_ROUTE, SOCK_RAW, AF_INET);*/ s = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC); /* The family parameter may be AF_UNSPEC which will provide routing informa- * tion for all address families, or can be restricted to a specific address * family by specifying which one is desired. There can be more than one * routing socket open per system. */ #endif if(s < 0) { syslog(LOG_ERR, "OpenAndConfInterfaceWatchSocket socket: %m"); return -1; } #ifdef __linux__ memset(&addr, 0, sizeof(addr)); addr.nl_family = AF_NETLINK; addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR; if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { syslog(LOG_ERR, "bind(netlink): %m"); close(s); return -1; } #endif return s; } /** * Process the message and add/drop multicast membership if needed */ int ProcessInterfaceWatch(int s, int s_ssdp, int s_ssdp6, int n_if_addr, const char * * if_addr) { ssize_t len; int i; char buffer[4096]; #ifdef __linux__ struct iovec iov; struct msghdr hdr; struct nlmsghdr *nlhdr; struct ifaddrmsg *ifa; iov.iov_base = buffer; iov.iov_len = sizeof(buffer); memset(&hdr, 0, sizeof(hdr)); hdr.msg_iov = &iov; hdr.msg_iovlen = 1; len = recvmsg(s, &hdr, 0); if(len < 0) { syslog(LOG_ERR, "recvmsg(s, &hdr, 0): %m"); return -1; } for(nlhdr = (struct nlmsghdr *)buffer; NLMSG_OK(nlhdr, len); nlhdr = NLMSG_NEXT(nlhdr, len)) { syslog(LOG_DEBUG, "nlmsg_type=%d", nlhdr->nlmsg_type); if(nlhdr->nlmsg_type == NLMSG_DONE) break; if(nlhdr->nlmsg_type == RTM_NEWADDR) { ifa = (struct ifaddrmsg *)NLMSG_DATA(nlhdr); syslog(LOG_DEBUG, "ProcessInterfaceWatchNotify RTM_NEWADDR index=%d", ifa->ifa_index); for(i = 0; i < n_if_addr; i++) { if(ifa->ifa_index == if_nametoindex(if_addr[i])) { AddDropMulticastMembership(s_ssdp, if_addr[i], 0, 0); break; } } } else if(nlhdr->nlmsg_type == RTM_DELADDR) { ifa = (struct ifaddrmsg *)NLMSG_DATA(nlhdr); syslog(LOG_DEBUG, "ProcessInterfaceWatchNotify RTM_DELADDR index=%d", ifa->ifa_index); for(i = 0; i < n_if_addr; i++) { if(ifa->ifa_index == if_nametoindex(if_addr[i])) { AddDropMulticastMembership(s_ssdp, if_addr[i], 0, 1); break; } } } } #else /* __linux__ */ struct rt_msghdr * rtm; struct ifa_msghdr * ifam; len = recv(s, buffer, sizeof(buffer), 0); if(len < 0) { syslog(LOG_ERR, "ProcessInterfaceWatchNotify recv: %m"); return -1; } rtm = (struct rt_msghdr *)buffer; switch(rtm->rtm_type) { case RTM_NEWADDR: ifam = (struct ifa_msghdr *)buffer; syslog(LOG_DEBUG, "ProcessInterfaceWatchNotify RTM_NEWADDR index=%d", ifam->ifam_index); for(i = 0; i < n_if_addr; i++) { if(ifam->ifam_index == if_nametoindex(if_addr[i])) { AddDropMulticastMembership(s_ssdp, if_addr[i], 0, 0); break; } } break; case RTM_DELADDR: ifam = (struct ifa_msghdr *)buffer; syslog(LOG_DEBUG, "ProcessInterfaceWatchNotify RTM_DELADDR index=%d", ifam->ifam_index); for(i = 0; i < n_if_addr; i++) { if(ifam->ifam_index == if_nametoindex(if_addr[i])) { /* I dont think it is useful */ /*AddDropMulticastMembership(s_ssdp, if_addr[i], 0, 1);*/ break; } } break; default: syslog(LOG_DEBUG, "rtm->rtm_type=%d", rtm->rtm_type); } #endif return 0; }