2015-08-05 22:22:06 +02:00
|
|
|
/*
|
|
|
|
(C) 2012-14 Digital Devices GmbH.
|
|
|
|
|
|
|
|
This file is part of the octoserve SAT>IP server.
|
|
|
|
|
|
|
|
Octoserve is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
Octoserve is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with octoserve. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "octoserve.h"
|
|
|
|
|
|
|
|
extern struct rtsp_error rtsp_errors;
|
|
|
|
|
|
|
|
extern uint32_t debug;
|
|
|
|
|
|
|
|
static void ssdp_reset_setup(struct os_ssdp *ss)
|
|
|
|
{
|
|
|
|
ss->setup = 0;
|
2015-08-31 21:29:53 +02:00
|
|
|
mtime(&ss->setupt);
|
2015-08-05 22:22:06 +02:00
|
|
|
ss->setupt += 5;
|
2015-08-31 21:29:53 +02:00
|
|
|
mtime(&ss->annt);
|
2015-08-05 22:22:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int read_id(char *type, uint32_t *id)
|
|
|
|
{
|
|
|
|
int fd, len;
|
|
|
|
char name[80];
|
|
|
|
|
|
|
|
len = snprintf(name, 80, "/config/%s.id", type);
|
|
|
|
if (len < 0 || len == 80)
|
|
|
|
return -1;
|
|
|
|
fd = open(name, O_RDONLY);
|
|
|
|
if (fd < 0)
|
|
|
|
return -1;
|
|
|
|
printf("read id from %s\n", name);
|
|
|
|
len = read(fd, name, 80);
|
|
|
|
name[len] = 0;
|
|
|
|
*id = strtol(name, NULL, 10);
|
|
|
|
printf("%s.id = %d\n", type, *id);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int write_id(char *type, uint32_t id)
|
|
|
|
{
|
|
|
|
int fd, len;
|
|
|
|
char name[80];
|
|
|
|
|
|
|
|
len = snprintf(name, 80, "/config/%s.id", type);
|
|
|
|
if (len < 0 || len == 80)
|
|
|
|
return -1;
|
|
|
|
fd = open(name, O_WRONLY|O_CREAT|O_TRUNC, 00644);
|
|
|
|
if (fd < 0)
|
|
|
|
return -1;
|
|
|
|
printf("write id = %d to %s\n", id, name);
|
|
|
|
len = snprintf(name, 80, "%d", id);
|
|
|
|
write(fd, name, len);
|
|
|
|
close(fd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ssdp_msg(struct os_ssdp *ss, char *buf, int n, int notify, int nr, int v6, int send_id)
|
|
|
|
{
|
|
|
|
int len;
|
|
|
|
uint8_t *u = ss->uuid;
|
|
|
|
char uuid[37];
|
|
|
|
char *header = notify ?
|
|
|
|
"NOTIFY * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\n" :
|
|
|
|
"HTTP/1.1 200 OK\r\n";
|
|
|
|
char id[32];
|
|
|
|
|
|
|
|
if (send_id) {
|
|
|
|
len = snprintf(id, sizeof(id),
|
|
|
|
"DEVICEID.SES.COM: %d",
|
|
|
|
ss->devid);
|
|
|
|
if (len < 0 || len >= sizeof(id))
|
|
|
|
return -1;
|
|
|
|
} else
|
|
|
|
id[0] = 0;
|
|
|
|
|
|
|
|
if (ss->alive) {
|
|
|
|
len = snprintf(buf, n,
|
|
|
|
"%s"
|
|
|
|
"CACHE-CONTROL: max-age=1800\r\n"
|
|
|
|
"LOCATION: http://%s%s\r\n"
|
|
|
|
"NT: %s\r\n"
|
|
|
|
"NTS: ssdp:%s\r\n"
|
|
|
|
"SERVER: %s\r\n"
|
|
|
|
"USN: %s%s%s\r\n"
|
|
|
|
"BOOTID.UPNP.ORG: %d\r\n"
|
|
|
|
"CONFIGID.UPNP.ORG: %d\r\n"
|
|
|
|
"%s\r\n"
|
|
|
|
"\r\n",
|
|
|
|
header,
|
|
|
|
v6 ? ss->ip6 : ss->ip,
|
|
|
|
ss->dev[nr].loc,
|
|
|
|
ss->dev[nr].nt[0] ? ss->dev[nr].nt : ss->dev[nr].uuid,
|
|
|
|
"alive",
|
|
|
|
ss->dev[nr].server,
|
|
|
|
ss->dev[nr].uuid, ss->dev[nr].nt[0] ? "::" : "", ss->dev[nr].nt,
|
|
|
|
ss->bootid, ss->configid,
|
|
|
|
id
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
dbgprintf(DEBUG_SSDP, "BYEBYE\n");
|
|
|
|
|
|
|
|
len = snprintf(buf, n,
|
|
|
|
"%s"
|
|
|
|
"NT: %s\r\n"
|
|
|
|
"NTS: ssdp:byebye\r\n"
|
|
|
|
"USN: %s%s%s\r\n"
|
|
|
|
"BOOTID.UPNP.ORG: %d\r\n"
|
|
|
|
"CONFIGID.UPNP.ORG: %d\r\n"
|
|
|
|
"\r\n",
|
|
|
|
header,
|
|
|
|
ss->dev[nr].nt[0] ? ss->dev[nr].nt : ss->dev[nr].uuid,
|
|
|
|
ss->dev[nr].uuid, ss->dev[nr].nt[0] ? "::" : "", ss->dev[nr].nt,
|
|
|
|
ss->bootid, ss->configid
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sendto_port(int sock, const void *buf, size_t len,
|
2015-12-13 21:40:43 +01:00
|
|
|
struct sockaddr *cadr, uint16_t port)
|
2015-08-05 22:22:06 +02:00
|
|
|
{
|
2015-12-13 21:40:43 +01:00
|
|
|
struct sockaddr_in adr;
|
|
|
|
int r;
|
2015-08-05 22:22:06 +02:00
|
|
|
|
2015-12-13 21:40:43 +01:00
|
|
|
/*adr = *((struct sockaddr_in *) cadr); */
|
2015-12-13 22:25:20 +01:00
|
|
|
/* struct assignment seem to be broken in arm gcc 4.5.4?!? */
|
2015-12-13 21:40:43 +01:00
|
|
|
memcpy(&adr, cadr, sizeof(adr));
|
2015-08-05 22:22:06 +02:00
|
|
|
adr.sin_port = htons(port);
|
2015-12-13 21:40:43 +01:00
|
|
|
r = sendto(sock, buf, len, 0, (struct sockaddr *) &adr, sizeof(adr));
|
|
|
|
//dbgprintf(DEBUG_SSDP, "sent to port %u, family %u, ret=%d errno=%d\n", port, adr.sin_family, r, errno);
|
|
|
|
return r;
|
2015-08-05 22:22:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int send_ssdp_msg(struct os_ssdp *ss, int nr, int notify)
|
|
|
|
{
|
|
|
|
uint8_t buf[2048];
|
|
|
|
int len;
|
|
|
|
|
|
|
|
len = ssdp_msg(ss, buf, sizeof(buf), notify, nr, 0, nr < 3);
|
|
|
|
|
|
|
|
if (len < 0 || len >= sizeof(buf))
|
|
|
|
return -1;
|
|
|
|
if (notify) {
|
|
|
|
struct sockaddr_in insadr;
|
|
|
|
|
|
|
|
memset(&insadr, 0, sizeof(insadr));
|
|
|
|
insadr.sin_family = AF_INET;
|
|
|
|
insadr.sin_port = htons(1900);
|
|
|
|
insadr.sin_addr.s_addr = inet_addr("239.255.255.250");
|
|
|
|
return sendto(ss->sock, buf, len, 0, (struct sockaddr *) &insadr, sizeof(insadr));
|
|
|
|
} else
|
|
|
|
return sendto(ss->sock, buf, len, 0, &ss->cadr, sizeof(struct sockaddr));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ssdp_announce(struct octoserve *os, int alive, int notify)
|
|
|
|
{
|
|
|
|
struct os_ssdp *ss = &os->ssdp;
|
|
|
|
uint8_t buf[2048];
|
|
|
|
int len, nr, res;
|
|
|
|
int s = os->ssdp.sock;
|
|
|
|
uint8_t *u = ss->uuid;
|
|
|
|
time_t t;
|
|
|
|
|
|
|
|
if (notify) {
|
2015-08-31 21:29:53 +02:00
|
|
|
mtime(&t);
|
2015-08-05 22:22:06 +02:00
|
|
|
if (!ss->setup)
|
|
|
|
if (t >= ss->setupt) {
|
|
|
|
ss->setup = 1;
|
|
|
|
dbgprintf(DEBUG_SSDP, "END SSDP SETUP\n");
|
|
|
|
}
|
|
|
|
if (ss->alive == alive && t < ss->annt)
|
|
|
|
return 0;
|
|
|
|
ss->alive = alive;
|
|
|
|
ss->annt = t + 900; //FIXME: +/- random
|
|
|
|
}
|
|
|
|
for (nr = 0; nr < ss->dev_num; nr++) {
|
|
|
|
send_ssdp_msg(ss, nr, notify);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ssdp_defend(struct os_ssdp *ss)
|
|
|
|
{
|
|
|
|
char buf[2048], host[64];
|
|
|
|
int len;
|
|
|
|
int s = ss->v6 ? ss->sock6 : ss->sock;
|
|
|
|
|
|
|
|
sockname(&ss->cadr, host);
|
2015-08-31 21:29:53 +02:00
|
|
|
dbgprintf(DEBUG_SSDP, "defend against HOST:%s\n", host);
|
2015-08-05 22:22:06 +02:00
|
|
|
|
|
|
|
len = snprintf(buf, sizeof(buf),
|
|
|
|
"M-SEARCH * HTTP/1.1\r\n"
|
|
|
|
"HOST: %s:1900\r\n"
|
|
|
|
"MAN: \"ssdp:discover\"\r\n"
|
|
|
|
"ST: urn:ses-com:device:SatIPServer:1\r\n"
|
|
|
|
"USER-AGENT: %s\r\n"
|
|
|
|
"DEVICEID.SES.COM: %d\r\n"
|
|
|
|
"\r\n",
|
|
|
|
ss->v6 ? ss->ip6 : ss->ip,
|
|
|
|
ss->server,
|
|
|
|
ss->devid);
|
|
|
|
if (len < 0 || len >= sizeof(buf))
|
|
|
|
return -1;
|
2015-11-24 23:08:27 +01:00
|
|
|
//return sendto(s, buf, len, 0, &ss->cadr, sizeof(ss->cadr));
|
|
|
|
return sendto_port(ss->sock, buf, len, &ss->cadr, ss->csport);
|
2015-08-05 22:22:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int send_reply_msearch(struct os_ssdp *ss, int mc, int send_id, int nr)
|
|
|
|
{
|
|
|
|
char buf[2048], id[32];
|
|
|
|
int len, res;
|
|
|
|
int v6 = ss->v6;
|
|
|
|
int s = v6 ? ss->sock6 : ss->sock;
|
|
|
|
|
|
|
|
dbgprintf(DEBUG_SSDP, "send msearch reply nr=%d\n", nr);
|
|
|
|
if (send_id) {
|
|
|
|
len = snprintf(id, sizeof(id),
|
|
|
|
"DEVICEID.SES.COM: %d\r\n",
|
|
|
|
ss->devid);
|
|
|
|
if (len < 0 || len >= sizeof(id))
|
|
|
|
return -1;
|
|
|
|
} else
|
|
|
|
id[0] = 0;
|
|
|
|
len = snprintf(buf, sizeof(buf),
|
|
|
|
"HTTP/1.1 200 OK\r\n"
|
|
|
|
"CACHE-CONTROL: max-age=1800\r\n"
|
|
|
|
"EXT:\r\n"
|
|
|
|
"LOCATION: http://%s%s\r\n"
|
|
|
|
"SERVER: %s\r\n"
|
|
|
|
"ST: %s\r\n"
|
|
|
|
"USN: %s%s%s\r\n"
|
|
|
|
"BOOTID.UPNP.ORG: %d\r\n"
|
|
|
|
"CONFIGID.UPNP.ORG: %d\r\n"
|
|
|
|
"%s"
|
|
|
|
"\r\n",
|
|
|
|
ss->v6 ? ss->ip6 : ss->ip,
|
|
|
|
ss->dev[nr].loc,
|
|
|
|
ss->dev[nr].server,
|
|
|
|
ss->dev[nr].nt[0] ? ss->dev[nr].nt : ss->dev[nr].uuid,
|
|
|
|
ss->dev[nr].uuid, ss->dev[nr].nt[0] ? "::" : "", ss->dev[nr].nt,
|
|
|
|
ss->bootid, ss->configid, id
|
|
|
|
);
|
|
|
|
if (len < 0 || len >= sizeof(buf))
|
|
|
|
return -1;
|
|
|
|
dbgprintf(DEBUG_SSDP, "sending:\n%s", buf);
|
|
|
|
if (mc) {
|
|
|
|
struct sockaddr_in insadr;
|
|
|
|
|
|
|
|
memset(&insadr, 0, sizeof(insadr));
|
|
|
|
insadr.sin_family = AF_INET;
|
|
|
|
insadr.sin_port = htons(1900);
|
|
|
|
insadr.sin_addr.s_addr = inet_addr("239.255.255.250");
|
|
|
|
res = sendto(s, buf, len, 0, (struct sockaddr *) &insadr, sizeof(insadr));
|
|
|
|
} else
|
2016-01-04 21:16:39 +01:00
|
|
|
res = sendto(s, buf, len, 0, &ss->cadr, sizeof(struct sockaddr));
|
|
|
|
//res = sendto_port(s, buf, len, &ss->cadr, ss->csport);
|
2015-08-05 22:22:06 +02:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ssdp_msearch(struct os_ssdp *ss, int mc, char *st)
|
|
|
|
{
|
|
|
|
int nr, len = strlen(st), type = 0;
|
|
|
|
uint8_t *u = ss->uuid;
|
|
|
|
|
|
|
|
if (!len)
|
|
|
|
return 0;
|
|
|
|
if (!strncasecmp(st, "ssdp:all", 8))
|
|
|
|
type = 1;
|
|
|
|
else if (!strncasecmp(st, "uuid:", 5))
|
|
|
|
type = 2;
|
|
|
|
dbgprintf(DEBUG_SSDP, "msearch mc=%u, st=%s (type=%u)\n", mc, st, type);
|
|
|
|
|
|
|
|
for (nr = 0; nr < ss->dev_num; nr++) {
|
|
|
|
if ((type == 1) ||
|
|
|
|
((type == 2) && !ss->dev[nr].nt[0] &&
|
|
|
|
!strncasecmp(ss->dev[nr].uuid, st, len)) ||
|
|
|
|
((type == 0) && !strncasecmp(ss->dev[nr].nt, st, len)))
|
|
|
|
send_reply_msearch(ss, mc, nr < 3, nr);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void handle_ssdp(struct octoserve *os, char *m, int ml)
|
|
|
|
{
|
|
|
|
struct os_ssdp *ss = &os->ssdp;
|
|
|
|
int ln, ll, tl, as, al;
|
|
|
|
char *l, *le, *me = m + ml, *st = NULL;
|
|
|
|
int type = 0;
|
|
|
|
int htype = 0;
|
|
|
|
int devid = -1;
|
|
|
|
int mx = -1;
|
|
|
|
uint8_t uu[16];
|
|
|
|
char host[64];
|
|
|
|
char str[INET_ADDRSTRLEN];
|
|
|
|
int sport = 1900, hport = 1900;
|
|
|
|
|
|
|
|
dbgprintf(DEBUG_SSDP,
|
|
|
|
"\n********************************************************************************\n");
|
|
|
|
inet_ntop(AF_INET, &((struct sockaddr_in *) &ss->cadr)->sin_addr, str, INET_ADDRSTRLEN);
|
|
|
|
dbgprintf(DEBUG_SSDP, "\nSSDP: %s\n", str);
|
|
|
|
|
|
|
|
memset(uu, 0, 16);
|
|
|
|
|
|
|
|
for (l = m, ln = -1; l < me; l += ll + 1) {
|
|
|
|
for (le = l; *le != '\r' && *le != '\n' && le < me; le++);
|
|
|
|
ll = le - l;
|
|
|
|
if (!ll)
|
|
|
|
continue;
|
|
|
|
*le = 0;
|
|
|
|
ln++;
|
|
|
|
dbgprintf(DEBUG_SSDP, "%02d: %s\n", ln, l);
|
|
|
|
if (ln == 0) {
|
|
|
|
if (!strncasecmp(l, "M-SEARCH * HTTP/1.1", ll)) {
|
|
|
|
type = 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strncasecmp(l, "NOTIFY * HTTP/1.1", ll)) {
|
|
|
|
type = 2;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strncasecmp(l, "HTTP/1.1 200 OK", ll)) {
|
|
|
|
type = 3;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
dbgprintf(DEBUG_SSDP, "unknown header\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
for (tl = 0; tl < ll && l[tl] != ':'; tl++);
|
|
|
|
if (tl == ll)
|
2015-08-31 21:29:53 +02:00
|
|
|
continue;
|
2015-08-05 22:22:06 +02:00
|
|
|
for (as = tl + 1; as < ll && isspace(l[as]); as++);
|
|
|
|
al = ll - as;
|
|
|
|
|
|
|
|
if (tl == 4 && !strncasecmp(l, "HOST", 4)) {
|
|
|
|
char *h, *p;
|
|
|
|
|
|
|
|
if (!strncmp(l + as, "239.255.255.250:1900", al))
|
|
|
|
htype = 1;
|
2015-08-31 21:29:53 +02:00
|
|
|
else {
|
2015-08-05 22:22:06 +02:00
|
|
|
htype = 2;
|
2015-08-31 21:29:53 +02:00
|
|
|
if (mx < 0)
|
|
|
|
mx = 1;
|
|
|
|
}
|
2015-08-05 22:22:06 +02:00
|
|
|
h = host;
|
|
|
|
p = l + as;
|
|
|
|
while (*p && *p != ':')
|
|
|
|
*(h++) = *(p++);
|
|
|
|
*h = 0;
|
|
|
|
p++;
|
|
|
|
hport = atoi(p);
|
|
|
|
}
|
|
|
|
if (tl == 2 && !strncasecmp(l, "MX", tl)) {
|
|
|
|
mx = atoi(l + as);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (tl == 2 && !strncasecmp(l, "ST", tl)) {
|
|
|
|
st = l + as;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (tl == 16 && !memcmp(l, "DEVICEID.SES.COM", tl)) {
|
|
|
|
devid = atoi(l + as);
|
|
|
|
//dbgprintf(DEBUG_SSDP, "sat>ip device id %d\n", devid);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (tl = 19 && !memcmp(l, "SEARCHPORT.UPNP.ORG", tl)) {
|
|
|
|
sport = atoi(l + as);
|
|
|
|
dbgprintf(DEBUG_SSDP, "searchport %d\n", sport);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (tl == 3 && !memcmp(l, "USN", tl) && !memcmp(l+5, "uuid:", 5)) {
|
|
|
|
int n;
|
|
|
|
|
|
|
|
//printf("USN: %s\n", l+5);
|
|
|
|
sscanf(l+10, "%2hhx%2hhx%2hhx%2hhx-%2hhx%2hhx-"
|
|
|
|
"%2hhx%2hhx-%2hhx%2hhx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%n",
|
|
|
|
&uu[0], &uu[1], &uu[2], &uu[3],
|
|
|
|
&uu[4], &uu[5], &uu[6], &uu[7],
|
|
|
|
&uu[8], &uu[9], &uu[10], &uu[11],
|
|
|
|
&uu[12], &uu[13], &uu[14], &uu[15], &n);
|
|
|
|
/* ignore message from myself */
|
|
|
|
if (!memcmp(uu, ss->uuid, 16))
|
|
|
|
dbgprintf(DEBUG_SSDP, "that's me!\n");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ss->csport = sport;
|
2015-08-31 21:29:53 +02:00
|
|
|
dbgprintf(DEBUG_SSDP, "host=%s, hport=%u, type=%d, htype=%d mx=%d\n",
|
|
|
|
host, hport, type, htype, mx);
|
2015-08-05 22:22:06 +02:00
|
|
|
/* M-SEARCH */
|
|
|
|
if (ss->setup) {
|
2015-12-13 21:40:43 +01:00
|
|
|
if (type == 1 && mx > 0 && st != NULL) {
|
2015-08-31 21:29:53 +02:00
|
|
|
//ssdp_msearch(ss, htype == 2 ? 0 : 1, st);
|
2015-08-05 22:22:06 +02:00
|
|
|
ssdp_msearch(ss, 0, st);
|
|
|
|
}
|
|
|
|
/* NOTIFY */
|
2015-08-31 21:29:53 +02:00
|
|
|
if (type == 2 && devid == ss->devid && memcmp(uu, ss->uuid, 16))
|
2015-08-05 22:22:06 +02:00
|
|
|
ssdp_defend(ss);
|
|
|
|
} else {
|
|
|
|
if (type == 1 && htype == 2 && ss->devid == devid) {
|
|
|
|
send_reply_msearch(ss, 0, 1, 2);
|
|
|
|
ss->devid++;
|
|
|
|
write_id("device", ss->devid);
|
|
|
|
dbgprintf(DEBUG_SSDP, "new device id %d \n", ss->devid);
|
|
|
|
ssdp_reset_setup(ss);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#define SSDP_MCAST "239.255.255.250"
|
|
|
|
#define SSDP_MCAST_LL "FF02::C"
|
|
|
|
#define SSDP_MCAST_SL "FF05::C"
|
|
|
|
|
|
|
|
static int ssdp_mc_add(int s, const char *ifname, int add, int v6)
|
|
|
|
{
|
|
|
|
if (v6) {
|
|
|
|
struct ipv6_mreq imr;
|
|
|
|
unsigned int ifindex = if_nametoindex(ifname);
|
|
|
|
|
|
|
|
memset(&imr, 0, sizeof(imr));
|
|
|
|
inet_pton(AF_INET6, SSDP_MCAST_LL, &imr.ipv6mr_multiaddr);
|
|
|
|
imr.ipv6mr_interface = ifindex;
|
|
|
|
if (setsockopt(s, IPPROTO_IPV6, add ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP,
|
|
|
|
&imr, sizeof(imr)) < 0)
|
|
|
|
return -1;
|
|
|
|
inet_pton(AF_INET6, SSDP_MCAST_SL, &imr.ipv6mr_multiaddr);
|
|
|
|
if (setsockopt(s, IPPROTO_IPV6, add ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP,
|
|
|
|
&imr, sizeof(imr)) < 0)
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
struct ip_mreq imr;
|
|
|
|
struct sockaddr_in sadr;
|
|
|
|
unsigned char ttl = 2, one = 1, zero = 0;
|
|
|
|
|
|
|
|
get_ifa(ifname, AF_INET, (struct sockaddr *) &sadr);
|
|
|
|
imr.imr_multiaddr.s_addr = inet_addr(SSDP_MCAST);
|
|
|
|
imr.imr_interface.s_addr = sadr.sin_addr.s_addr;
|
|
|
|
|
|
|
|
if (setsockopt(s, IPPROTO_IP, add ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP,
|
|
|
|
&imr, sizeof(imr)) < 0)
|
|
|
|
return -1;
|
|
|
|
setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
|
|
|
|
//setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, &zero, sizeof(zero));
|
|
|
|
}
|
|
|
|
printf("mc_add OK\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ssdpsock(const char *ifnam, int family, struct sockaddr *sadr)
|
|
|
|
{
|
|
|
|
int one=1, sock;
|
|
|
|
struct addrinfo *ais, *ai, hints = {
|
|
|
|
.ai_flags = AI_PASSIVE,
|
|
|
|
.ai_family = family,
|
|
|
|
.ai_socktype = SOCK_DGRAM,
|
|
|
|
.ai_protocol = 0,
|
|
|
|
.ai_addrlen = 0,
|
|
|
|
.ai_addr = sadr,
|
|
|
|
.ai_canonname = NULL,
|
|
|
|
.ai_next = NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (getaddrinfo(NULL, "1900", &hints, &ais) < 0)
|
|
|
|
return -1;
|
|
|
|
for (ai = ais; ai; ai = ai->ai_next) {
|
|
|
|
sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
|
|
|
|
if (sock == -1)
|
|
|
|
continue;
|
|
|
|
//dump(ai->ai_addr, sizeof(*ai->ai_addr));
|
|
|
|
if (!setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) &&
|
|
|
|
!bind(sock, ai->ai_addr, ai->ai_addrlen))
|
|
|
|
break;
|
|
|
|
close(sock);
|
|
|
|
}
|
|
|
|
freeaddrinfo(ais);
|
|
|
|
ssdp_mc_add(sock, ifnam, 1, family == AF_INET6 ? 1 : 0);
|
|
|
|
return sock;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void add_fd(int fd, int *mfd, fd_set *fds)
|
|
|
|
{
|
|
|
|
FD_SET(fd, fds);
|
|
|
|
if (fd > *mfd)
|
|
|
|
*mfd = fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ssdp_loop(struct octoserve *os)
|
|
|
|
{
|
|
|
|
struct os_ssdp *ss = &os->ssdp;
|
|
|
|
int num;
|
|
|
|
int mfd;
|
|
|
|
fd_set fds;
|
|
|
|
struct timeval timeout;
|
|
|
|
char buf[1501];
|
|
|
|
|
|
|
|
ss->http_sock = streamsock("8888", AF_INET, &ss->http_sadr);
|
|
|
|
if (listen(ss->http_sock, 10) < 0) {
|
|
|
|
printf("listen error");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ssdp_reset_setup(ss);
|
|
|
|
|
|
|
|
while (!os->exit) {
|
|
|
|
ssdp_announce(os, 1, 1);
|
|
|
|
|
|
|
|
timeout.tv_sec = 1;
|
|
|
|
timeout.tv_usec = 0;
|
|
|
|
|
|
|
|
FD_ZERO(&fds);
|
|
|
|
mfd = 0;
|
|
|
|
add_fd(ss->sock, &mfd, &fds);
|
|
|
|
//add_fd(ss->sock6, &mfd, &fds);
|
|
|
|
add_fd(ss->http_sock, &mfd, &fds);
|
|
|
|
|
|
|
|
num = select(mfd + 1, &fds, NULL, NULL, &timeout);
|
|
|
|
if (num < 0)
|
|
|
|
break;
|
|
|
|
if (FD_ISSET(ss->sock, &fds)) {
|
|
|
|
socklen_t len;
|
|
|
|
int n;
|
|
|
|
|
|
|
|
len = sizeof(ss->cadr);
|
|
|
|
n = recvfrom(ss->sock, buf, sizeof(buf) - 1, 0, &ss->cadr, &len);
|
|
|
|
ss->v6 = 0;
|
|
|
|
if (n > 0)
|
|
|
|
handle_ssdp(os, buf, n);
|
|
|
|
}
|
|
|
|
if (FD_ISSET(ss->http_sock, &fds)) {
|
|
|
|
socklen_t len;
|
|
|
|
pthread_t pt;
|
|
|
|
|
|
|
|
//ss->csock = accept4(ss->http_sock, &ss->cadr, &len, SOCK_NONBLOCK);
|
|
|
|
ss->csock = accept(ss->http_sock, &ss->cadr, &len);
|
|
|
|
handle_http(ss);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ssdp_announce(os, 0, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ssdp_search(int s)
|
|
|
|
{
|
|
|
|
struct sockaddr_in insadr;
|
|
|
|
char ms[] = "M-SEARCH * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\n"
|
|
|
|
"MAN: \"ssdp:discover\"\r\nMX: 5\r\nST: ssdp:all\r\n\r\n";
|
|
|
|
|
|
|
|
memset(&insadr, 0, sizeof(insadr));
|
|
|
|
insadr.sin_family = AF_INET;
|
|
|
|
insadr.sin_port = htons(1900);
|
|
|
|
insadr.sin_addr.s_addr = inet_addr("239.255.255.250");
|
|
|
|
sendto(s, &ms[0], sizeof(ms) - 1, 0,
|
|
|
|
(struct sockaddr *) &insadr, sizeof(insadr));
|
|
|
|
}
|
|
|
|
|
|
|
|
void ssdp_thread(struct octoserve *os)
|
|
|
|
{
|
|
|
|
//ssdp_search(s);
|
|
|
|
ssdp_loop(os);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint64_t get_us(void)
|
|
|
|
{
|
|
|
|
struct timeval tv;
|
|
|
|
gettimeofday(&tv, 0);
|
|
|
|
return ((uint64_t) tv.tv_sec * 1000000ULL + (uint64_t) tv.tv_usec);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void create_uuid(uint8_t *u, uint8_t *mac)
|
|
|
|
{
|
|
|
|
uint8_t uu[10] = { 0xdd, 0x84, 0x8d, 0x00, 0x26,
|
|
|
|
0xec, 0x11, 0xeb, 0x80, 0x00 };
|
|
|
|
|
|
|
|
memcpy(u, uu, 10);
|
|
|
|
memcpy(u + 10, mac, 6);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void create_uuid_old(uint8_t *u, uint8_t *mac)
|
|
|
|
{
|
|
|
|
uint64_t us, ns;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
memcpy(u + 10, mac, 6);
|
|
|
|
|
|
|
|
us = get_us();
|
|
|
|
ns = us * 1000ULL + 122192928000000000ULL;
|
|
|
|
|
|
|
|
u[0] = ns >> 24;
|
|
|
|
u[1] = ns >> 16;
|
|
|
|
u[2] = ns >> 8;
|
|
|
|
u[3] = ns;
|
|
|
|
u[4] = ns >> 40;
|
|
|
|
u[5] = ns >> 32;
|
|
|
|
u[6] = ns >> 56;
|
|
|
|
u[7] = ns >> 48;
|
|
|
|
|
|
|
|
u[8] = 0;
|
|
|
|
u[9] = 0;
|
|
|
|
|
|
|
|
u[6] = (u[6] & 0x0f) | 0x10;
|
|
|
|
u[8] = (u[8] & 0x3f) | 0x80;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int add_dev(struct os_ssdp *ss, char *nt, char *loc, char *uuid, char *server)
|
|
|
|
{
|
|
|
|
int n = ss->dev_num;
|
|
|
|
|
|
|
|
if (n == UPNP_DEV_MAX)
|
|
|
|
return -1;
|
|
|
|
ss->dev[n].nt = nt;
|
|
|
|
ss->dev[n].loc = loc;
|
|
|
|
ss->dev[n].uuid = uuid;
|
|
|
|
ss->dev[n].server = server;
|
|
|
|
ss->dev_num++;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int init_ssdp(struct octoserve *os, struct os_ssdp *ss, uint32_t d, int nossdp, int nodms)
|
|
|
|
{
|
|
|
|
time_t t;
|
|
|
|
uint8_t *u, mac[6];
|
|
|
|
struct ifreq ifr;
|
|
|
|
uint32_t id;
|
|
|
|
int stat;
|
|
|
|
char *dms = "Linux/3.17.7 DLNADOC/1.50 UPnP/1.0 OctopusNet-DMS/1.0";
|
|
|
|
|
|
|
|
debug = d;
|
|
|
|
ss->os = os;
|
|
|
|
ss->server = "Linux/3.17.7 UPnP/1.1 OctopusNet/" OCTOSERVE_VERSION;
|
|
|
|
stat = read_id("device", &id);
|
|
|
|
if (stat < 0) {
|
|
|
|
ss->devid = 5;
|
|
|
|
write_id("device", ss->devid);
|
|
|
|
} else
|
|
|
|
ss->devid = id;
|
|
|
|
stat = read_id("boot", &id);
|
|
|
|
if (stat < 0)
|
|
|
|
ss->bootid = 0;
|
|
|
|
else
|
|
|
|
ss->bootid = id + 1;
|
|
|
|
write_id("boot", ss->bootid);
|
|
|
|
|
2015-12-13 21:40:43 +01:00
|
|
|
dbgprintf(DEBUG_SSDP, "DEVID = %u\n", ss->devid);
|
|
|
|
|
|
|
|
|
2015-08-05 22:22:06 +02:00
|
|
|
ss->configid = 1;
|
|
|
|
|
2015-12-13 21:40:43 +01:00
|
|
|
get_ifa(os->ifname, AF_INET, (struct sockaddr *) &os->ssdp.sadr);
|
|
|
|
get_ifa(os->ifname, AF_INET6, (struct sockaddr *) &os->ssdp.sadr6);
|
|
|
|
sockname((struct sockaddr *) &os->ssdp.sadr, os->ssdp.ip);
|
|
|
|
sockname((struct sockaddr *) &os->ssdp.sadr6, os->ssdp.ip6);
|
2015-08-05 22:22:06 +02:00
|
|
|
|
|
|
|
if (!nossdp) {
|
2015-12-13 21:40:43 +01:00
|
|
|
ss->sock = ssdpsock(os->ifname, AF_INET, (struct sockaddr *) &os->ssdp.sadr);
|
2015-08-05 22:22:06 +02:00
|
|
|
if (ss->sock < 0)
|
|
|
|
return -1;
|
|
|
|
|
2015-12-13 21:40:43 +01:00
|
|
|
ss->sock6 = ssdpsock(os->ifname, AF_INET6, (struct sockaddr *) &os->ssdp.sadr);
|
2015-08-05 22:22:06 +02:00
|
|
|
if (ss->sock6 < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
strcpy(ifr.ifr_name, os->ifname);
|
|
|
|
ioctl(ss->sock, SIOCGIFHWADDR, &ifr);
|
|
|
|
memcpy(mac, ifr.ifr_hwaddr.sa_data, 6);
|
|
|
|
|
|
|
|
create_uuid(ss->uuid, mac);
|
|
|
|
u = ss->uuid;
|
|
|
|
|
|
|
|
snprintf(ss->uuid_str, 42,
|
|
|
|
"uuid:%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
|
|
|
|
u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7],
|
|
|
|
u[8], u[9], u[10], u[11], u[12], u[13], u[14], u[15]);
|
|
|
|
|
|
|
|
snprintf(ss->uuid_str2, 42,
|
|
|
|
"uuid:f0287290-e1e1-11e2-9a21-%02x%02x%02x%02x%02x%02x",
|
|
|
|
u[10], u[11], u[12], u[13], u[14], u[15]);
|
|
|
|
|
|
|
|
add_dev(ss, "upnp:rootdevice", "/octoserve/octonet.xml", ss->uuid_str, ss->server);
|
|
|
|
add_dev(ss, "", "/octoserve/octonet.xml", ss->uuid_str, ss->server);
|
|
|
|
add_dev(ss, "urn:ses-com:device:SatIPServer:1", "/octoserve/octonet.xml",
|
|
|
|
ss->uuid_str, ss->server);
|
|
|
|
|
|
|
|
if (!nodms) {
|
|
|
|
add_dev(ss, "upnp:rootdevice", ":8080/dms.xml", ss->uuid_str2, dms);
|
|
|
|
add_dev(ss, "", ":8080/dms.xml", ss->uuid_str2, dms);
|
|
|
|
add_dev(ss, "urn:schemas-upnp-org:device:MediaServer:1", ":8080/dms.xml",
|
|
|
|
ss->uuid_str2, dms);
|
|
|
|
add_dev(ss, "urn:schemas-upnp-org:service:ConnectionManager:1",
|
|
|
|
":8080/dms.xml", ss->uuid_str2, dms);
|
|
|
|
add_dev(ss, "urn:schemas-upnp-org:service:ContentDirectory:1",
|
|
|
|
":8080/dms.xml", ss->uuid_str2, dms);
|
|
|
|
add_dev(ss, "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1",
|
|
|
|
":8080/dms.xml", ss->uuid_str2, dms);
|
|
|
|
}
|
|
|
|
pthread_create(&os->ssdp.pt, NULL, (void *) ssdp_thread, os);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|