1
/* $Id: minissdpd.c,v 1.18 2008/10/07 12:24:54 nanard Exp $ */
1
/* $Id: minissdpd.c,v 1.22 2011/07/29 08:44:04 nanard Exp $ */
3
* (c) 2007-2008 Thomas Bernard
3
* (c) 2007-2011 Thomas Bernard
4
4
* website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
5
5
* This software is subject to the conditions detailed
6
6
* in the LICENCE file provided within the distribution */
10
12
#include <string.h>
59
64
/* discovered device list kept in memory */
60
65
struct device * devlist = 0;
67
/* bootid and configid */
68
unsigned int upnp_bootid = 1;
69
unsigned int upnp_configid = 1337;
62
71
/* updateDevice() :
63
72
* adds or updates the device to the list.
65
75
* 0 : the device was updated
66
76
* 1 : the device was new */
67
77
int updateDevice(const struct header * headers, time_t t)
148
162
/* SendSSDPMSEARCHResponse() :
149
163
* build and send response to M-SEARCH SSDP packets. */
151
SendSSDPMSEARCHResponse(int s, const struct sockaddr_in * sockname,
165
SendSSDPMSEARCHResponse(int s, const struct sockaddr * sockname,
152
166
const char * st, const char * usn,
153
167
const char * server, const char * location)
158
* follow guideline from document "UPnP Device Architecture 1.0"
159
* uppercase is recommended.
160
* DATE: is recommended
161
* SERVER: OS/ver UPnP/1.0 miniupnpd/1.0
162
* - check what to put in the 'Cache-Control' header
164
l = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\n"
165
"CACHE-CONTROL: max-age=120\r\n"
175
n = sendto(s, buf, l, 0,
176
(struct sockaddr *)sockname, sizeof(struct sockaddr_in) );
178
syslog(LOG_ERR, "sendto(udp): %m");
171
socklen_t sockname_len;
173
* follow guideline from document "UPnP Device Architecture 1.0"
174
* uppercase is recommended.
175
* DATE: is recommended
176
* SERVER: OS/ver UPnP/1.0 miniupnpd/1.0
177
* - check what to put in the 'Cache-Control' header
179
* have a look at the document "UPnP Device Architecture v1.1 */
180
l = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\n"
181
"CACHE-CONTROL: max-age=120\r\n"
188
"OPT: \"http://schemas.upnp.org/upnp/1/0/\";\r\n" /* UDA v1.1 */
189
"01-NLS: %u\r\n" /* same as BOOTID. UDA v1.1 */
190
"BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
191
"CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
195
upnp_bootid, upnp_bootid, upnp_configid);
197
sockname_len = (sockname->sa_family == PF_INET6)
198
? sizeof(struct sockaddr_in6)
199
: sizeof(struct sockaddr_in);
201
sockname_len = sizeof(struct sockaddr_in);
203
n = sendto(s, buf, l, 0,
204
sockname, sockname_len );
206
syslog(LOG_ERR, "sendto(udp): %m");
182
210
/* Services stored for answering to M-SEARCH */
192
220
/* Process M-SEARCH requests */
193
221
void processMSEARCH(int s, const char * st, int st_len,
194
const struct sockaddr_in * addr)
222
const struct sockaddr * addr)
196
224
struct service * serv;
197
229
if(!st || st_len==0)
232
sockaddr_to_string(addr, buf, sizeof(buf));
233
syslog(LOG_INFO, "SSDP M-SEARCH from %s ST:%.*s",
199
236
syslog(LOG_INFO, "SSDP M-SEARCH from %s:%d ST: %.*s",
200
inet_ntoa(addr->sin_addr),
201
ntohs(addr->sin_port),
237
inet_ntoa(((const struct sockaddr_in *)addr)->sin_addr),
238
ntohs(((const struct sockaddr_in *)addr)->sin_port),
203
241
if(st_len==8 && (0==memcmp(st, "ssdp:all", 8))) {
204
242
/* send a response for all services */
205
243
for(serv = servicelisthead.lh_first;
247
285
* 0 : no device removed nor added
248
286
* 1 : a device was added. */
249
287
int ParseSSDPPacket(int s, const char * p, ssize_t n,
250
const struct sockaddr_in * addr)
288
const struct sockaddr * addr)
252
290
const char * linestart;
253
291
const char * lineend;
273
311
while(linestart < p + n - 2) {
274
312
/* start parsing the line : detect line end */
275
313
lineend = linestart;
276
while(*lineend != '\n' && *lineend != '\r' && lineend < p + n)
314
while(lineend < p + n && *lineend != '\n' && *lineend != '\r')
278
316
//printf("line: '%.*s'\n", lineend - linestart, linestart);
279
317
/* detect name end : ':' character */
280
318
nameend = linestart;
281
while(*nameend != ':' && nameend < lineend)
319
while(nameend < lineend && *nameend != ':')
283
321
/* detect value */
284
valuestart = nameend + 1;
285
while(isspace(*valuestart) && valuestart < lineend)
322
if(nameend < lineend)
323
valuestart = nameend + 1;
325
valuestart = nameend;
327
while(valuestart < lineend && isspace(*valuestart))
287
329
/* suppress leading " if needed */
288
if(*valuestart=='\"')
330
if(valuestart < lineend && *valuestart=='\"')
290
332
if(nameend > linestart && valuestart < lineend) {
291
int l = nameend - linestart;
292
int m = lineend - valuestart;
333
int l = nameend - linestart; /* header name length */
334
int m = lineend - valuestart; /* header value length */
293
335
/* suppress tailing spaces */
294
336
while(m>0 && isspace(valuestart[m-1]))
296
338
/* suppress tailing ' if needed */
297
if(valuestart[m-1] == '\"')
339
if(m>0 && valuestart[m-1] == '\"')
300
342
/*printf("--%.*s: (%d)%.*s--\n", l, linestart,
312
354
else if(l==8 && 0==strncasecmp(linestart, "location", 8))
313
355
i = HEADER_LOCATION;
314
356
else if(l==13 && 0==strncasecmp(linestart, "cache-control", 13)) {
315
const char * name = valuestart;
357
/* parse "name1=value1, name_alone, name2=value2" string */
358
const char * name = valuestart; /* name */
359
const char * val; /* value */
360
int rem = m; /* remaining bytes to process */
320
while(*val != '=' && *val != ',')
363
while(val < name + rem && *val != '=' && *val != ',')
365
if(val >= name + rem)
322
367
if(*val == '=') {
323
while(*val == '=' || isspace(*val))
368
while(val < name + rem && (*val == '=' || isspace(*val)))
370
if(val >= name + rem)
325
372
if(0==strncasecmp(name, "max-age", 7))
326
373
lifetime = (unsigned int)strtoul(val, 0, 0);
374
/* move to the next name=value pair */
327
375
while(rem > 0 && *name != ',') {
331
380
while(rem > 0 && (*name == ',' || isspace(*name))) {
435
484
unsigned char * rp = rbuf+1;
436
485
unsigned char nrep = 0;
438
struct service * newserv;
487
struct service * newserv = NULL;
439
488
struct service * serv;
441
490
n = read(req->socket, buf, sizeof(buf));
443
492
syslog(LOG_ERR, "(s=%d) processRequest(): read(): %m", req->socket);
449
496
syslog(LOG_INFO, "(s=%d) request connection closed", req->socket);
457
502
DECODELENGTH(l, p);
504
syslog(LOG_WARNING, "bad request (length encoding)");
458
507
syslog(LOG_INFO, "(s=%d) request type=%d str='%.*s'",
459
508
req->socket, type, l, p);
497
546
for(serv = servicelisthead.lh_first;
498
547
serv && (nrep < 255);
499
548
serv = serv->entries.le_next) {
549
/* test if we can put more responses in the buffer */
500
550
if(strlen(serv->location) + strlen(serv->st)
501
551
+ strlen(serv->usn) + 6 + (rp - rbuf) >= sizeof(rbuf))
526
if(write(req->socket, rbuf, rp - rbuf) < 0)
576
if(write(req->socket, rbuf, rp - rbuf) < 0) {
527
577
syslog(LOG_ERR, "(s=%d) write: %m", req->socket);
530
582
newserv = malloc(sizeof(struct service));
532
584
syslog(LOG_ERR, "cannot allocate memory");
535
587
newserv->st = malloc(l + 1);
536
588
if(!newserv->st) {
537
589
syslog(LOG_ERR, "cannot allocate memory");
540
592
memcpy(newserv->st, p, l);
541
593
newserv->st[l] = '\0';
596
syslog(LOG_WARNING, "bad request (missing usn)");
543
599
DECODELENGTH(l, p);
601
syslog(LOG_WARNING, "bad request (length encoding)");
544
604
syslog(LOG_INFO, "usn='%.*s'", l, p);
545
605
newserv->usn = malloc(l + 1);
546
606
if(!newserv->usn) {
547
607
syslog(LOG_ERR, "cannot allocate memory");
550
610
memcpy(newserv->usn, p, l);
551
611
newserv->usn[l] = '\0';
555
615
newserv->server = malloc(l + 1);
556
616
if(!newserv->server) {
557
617
syslog(LOG_ERR, "cannot allocate memory");
560
620
memcpy(newserv->server, p, l);
561
621
newserv->server[l] = '\0';
565
625
newserv->location = malloc(l + 1);
566
626
if(!newserv->location) {
567
627
syslog(LOG_ERR, "cannot allocate memory");
570
630
memcpy(newserv->location, p, l);
571
631
newserv->location[l] = '\0';
583
643
free(serv->location);
584
644
serv->location = newserv->location;
589
650
/* Inserting new service */
590
651
LIST_INSERT_HEAD(&servicelisthead, newserv, entries);
591
653
/*rbuf[0] = '\0';
592
654
if(write(req->socket, rbuf, 1) < 0)
593
655
syslog(LOG_ERR, "(s=%d) write: %m", req->socket);
597
659
syslog(LOG_WARNING, "Unknown request type %d", type);
599
if(write(req->socket, rbuf, 1) < 0)
661
if(write(req->socket, rbuf, 1) < 0) {
600
662
syslog(LOG_ERR, "(s=%d) write: %m", req->socket);
671
free(newserv->server);
672
free(newserv->location);
604
681
static volatile int quitting = 0;
614
691
/* main(): program entry point */
615
692
int main(int argc, char * * argv)
618
696
struct sigaction sa;
621
int s_ssdp; /* udp socket receiving ssdp packets */
622
int s_unix; /* unix socket communicating with clients */
699
int s_ssdp = -1; /* udp socket receiving ssdp packets */
701
int s_ssdp6 = -1; /* udp socket receiving ssdp packets IPv6*/
703
int s_unix = -1; /* unix socket communicating with clients */
624
705
LIST_HEAD(reqstructhead, reqelem) reqlisthead;
625
706
struct reqelem * req;
631
712
const char * sockpath = "/var/run/minissdpd.sock";
632
713
const char * pidfilename = "/var/run/minissdpd.pid";
633
714
int debug_flag = 0;
634
716
int deltadev = 0;
635
717
struct sockaddr_in sendername;
637
len_r = sizeof(struct sockaddr_in);
718
socklen_t sendername_len;
720
struct sockaddr_in6 sendername6;
721
socklen_t sendername6_len;
639
724
LIST_INIT(&reqlisthead);
640
725
LIST_INIT(&servicelisthead);
653
738
sockpath = argv[++i];
654
739
else if(0==strcmp(argv[i], "-p"))
655
740
pidfilename = argv[++i];
742
else if(0==strcmp(argv[i], "-6"))
657
746
if(n_if_addr < 1)
660
"Usage: %s [-d] [-s socket] [-p pidfile] "
661
"-i <interface_address> [-i <interface_address2>] ...\n",
749
"Usage: %s [-d] [-6] [-s socket] [-p pidfile] "
750
"-i <interface> [-i <interface2>] ...\n",
664
"By default, socket will be open as %s "
753
"\n <interface> is either an IPv4 address such as 192.168.1.42, or an\ninterface name such as eth0.\n");
755
"\n By default, socket will be open as %s\n"
665
756
"and pid written to file %s\n",
666
757
sockpath, pidfilename);
700
791
if(sigaction(SIGTERM, &sa, NULL))
702
793
syslog(LOG_ERR, "Failed to set SIGTERM handler. EXITING");
705
797
if(sigaction(SIGINT, &sa, NULL))
707
799
syslog(LOG_ERR, "Failed to set SIGINT handler. EXITING");
710
s_ssdp = OpenAndConfSSDPReceiveSocket(n_if_addr, if_addr);
803
/* open UDP socket(s) for receiving SSDP packets */
804
s_ssdp = OpenAndConfSSDPReceiveSocket(n_if_addr, if_addr, 0);
713
807
syslog(LOG_ERR, "Cannot open socket for receiving SSDP messages, exiting");
813
s_ssdp6 = OpenAndConfSSDPReceiveSocket(n_if_addr, if_addr, 1);
816
syslog(LOG_ERR, "Cannot open socket for receiving SSDP messages (IPv6), exiting");
822
/* Open Unix socket to communicate with other programs on
823
* the same machine */
716
824
s_unix = OpenUnixSocket(sockpath);
719
827
syslog(LOG_ERR, "Cannot open unix socket for communicating with clients. Exiting");
832
/* drop privileges */
834
/* if we drop privileges, how to unlink(/var/run/minissdpd.sock) ? */
836
struct passwd * user;
837
struct group * group;
838
user = getpwnam("nobody");
840
syslog(LOG_ERR, "getpwnam(\"%s\") : %m", "nobody");
844
group = getgrnam("nogroup");
846
syslog(LOG_ERR, "getgrnam(\"%s\") : %m", "nogroup");
850
if(setgid(group->gr_gid) < 0) {
851
syslog(LOG_ERR, "setgit(%d) : %m", group->gr_gid);
855
if(setuid(user->pw_uid) < 0) {
856
syslog(LOG_ERR, "setuid(%d) : %m", user->pw_uid);
726
866
/* fill readfds fd_set */
727
867
FD_ZERO(&readfds);
728
868
FD_SET(s_ssdp, &readfds);
871
FD_SET(s_ssdp6, &readfds);
729
874
FD_SET(s_unix, &readfds);
730
875
for(req = reqlisthead.lh_first; req; req = req->entries.le_next)
739
884
syslog(LOG_ERR, "select: %m");
888
if((s_ssdp6 >= 0) && FD_ISSET(s_ssdp6, &readfds))
890
sendername6_len = sizeof(struct sockaddr_in6);
891
n = recvfrom(s_ssdp6, buf, sizeof(buf), 0,
892
(struct sockaddr *)&sendername6, &sendername6_len);
895
syslog(LOG_ERR, "recvfrom: %m");
899
/* Parse and process the packet received */
900
/*printf("%.*s", n, buf);*/
901
i = ParseSSDPPacket(s_ssdp6, buf, n,
902
(struct sockaddr *)&sendername6);
903
syslog(LOG_DEBUG, "** i=%d deltadev=%d **", i, deltadev);
904
if(i==0 || (i*deltadev < 0))
907
syslog(LOG_NOTICE, "%d new devices added", deltadev);
908
else if(deltadev < 0)
909
syslog(LOG_NOTICE, "%d devices removed (good-bye!)", -deltadev);
912
else if((i*deltadev) >= 0)
742
919
if(FD_ISSET(s_ssdp, &readfds))
921
sendername_len = sizeof(struct sockaddr_in);
744
922
n = recvfrom(s_ssdp, buf, sizeof(buf), 0,
745
(struct sockaddr *)&sendername, &len_r);
923
(struct sockaddr *)&sendername, &sendername_len);
748
926
syslog(LOG_ERR, "recvfrom: %m");
752
930
/* Parse and process the packet received */
753
931
/*printf("%.*s", n, buf);*/
754
i = ParseSSDPPacket(s_ssdp, buf, n, &sendername);
932
i = ParseSSDPPacket(s_ssdp, buf, n,
933
(struct sockaddr *)&sendername);
755
934
syslog(LOG_DEBUG, "** i=%d deltadev=%d **", i, deltadev);
756
935
if(i==0 || (i*deltadev < 0))
794
975
syslog(LOG_INFO, "(s=%d) new request connection", s);
795
976
tmp = malloc(sizeof(struct reqelem));
797
LIST_INSERT_HEAD(&reqlisthead, tmp, entries);
979
syslog(LOG_ERR, "cannot allocate memory for request");
985
LIST_INSERT_HEAD(&reqlisthead, tmp, entries);
801
991
/* closing and cleaning everything */
804
1007
if(unlink(sockpath) < 0)
805
1008
syslog(LOG_ERR, "unlink(%s): %m", sockpath);
806
1009
if(unlink(pidfilename) < 0)
807
1010
syslog(LOG_ERR, "unlink(%s): %m", pidfilename);