mirror of
https://github.com/owntone/owntone-server.git
synced 2025-01-24 13:13:17 -05:00
More UPnP work. Almost finished with discovery.
This commit is contained in:
parent
0115887492
commit
0eba96d92a
14
src/main.c
14
src/main.c
@ -423,10 +423,6 @@ int main(int argc, char *argv[]) {
|
|||||||
VERSION,err_getlevel());
|
VERSION,err_getlevel());
|
||||||
|
|
||||||
|
|
||||||
#ifdef UPNP
|
|
||||||
upnp_init();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* load plugins before we drop privs? Maybe... let the
|
/* load plugins before we drop privs? Maybe... let the
|
||||||
* plugins do stuff they might need to */
|
* plugins do stuff they might need to */
|
||||||
plugin_init();
|
plugin_init();
|
||||||
@ -476,6 +472,10 @@ int main(int argc, char *argv[]) {
|
|||||||
|
|
||||||
free(runas);
|
free(runas);
|
||||||
|
|
||||||
|
#ifdef UPNP
|
||||||
|
upnp_init();
|
||||||
|
#endif
|
||||||
|
|
||||||
/* this will require that the db be readable by the runas user */
|
/* this will require that the db be readable by the runas user */
|
||||||
db_type = conf_alloc_string("general","db_type","sqlite");
|
db_type = conf_alloc_string("general","db_type","sqlite");
|
||||||
db_parms = conf_alloc_string("general","db_parms","/var/cache/mt-daapd");
|
db_parms = conf_alloc_string("general","db_parms","/var/cache/mt-daapd");
|
||||||
@ -497,7 +497,7 @@ int main(int argc, char *argv[]) {
|
|||||||
|
|
||||||
/* Initialize the database before starting */
|
/* Initialize the database before starting */
|
||||||
DPRINTF(E_LOG,L_MAIN|L_DB,"Initializing database\n");
|
DPRINTF(E_LOG,L_MAIN|L_DB,"Initializing database\n");
|
||||||
if(db_init(&reload)) {
|
if(db_init(reload)) {
|
||||||
DPRINTF(E_FATAL,L_MAIN|L_DB,"Error in db_init: %s\n",strerror(errno));
|
DPRINTF(E_FATAL,L_MAIN|L_DB,"Error in db_init: %s\n",strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -641,6 +641,10 @@ int main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef UPNP
|
||||||
|
upnp_deinit();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/* Got to find a cleaner way to stop the web server.
|
/* Got to find a cleaner way to stop the web server.
|
||||||
* Closing the fd of the socking accepting doesn't necessarily
|
* Closing the fd of the socking accepting doesn't necessarily
|
||||||
|
349
src/upnp.c
349
src/upnp.c
@ -1,4 +1,4 @@
|
|||||||
/*
|
/**
|
||||||
* $Id: $
|
* $Id: $
|
||||||
*
|
*
|
||||||
* Implementation of functions for UPnP discovery
|
* Implementation of functions for UPnP discovery
|
||||||
@ -20,10 +20,12 @@
|
|||||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
#include <pthread.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
@ -41,47 +43,80 @@
|
|||||||
#define UPNP_ADDR "239.255.255.250"
|
#define UPNP_ADDR "239.255.255.250"
|
||||||
#define UPNP_PORT 1900
|
#define UPNP_PORT 1900
|
||||||
|
|
||||||
|
#define UPNP_TYPE_ALIVE 1
|
||||||
|
#define UPNP_TYPE_BYEBYE 2
|
||||||
|
#define UPNP_TYPE_RESPONSE 3
|
||||||
|
|
||||||
|
#define UPNP_SELECT_TIMEOUT 1 /* update loop for discover replies */
|
||||||
|
|
||||||
|
typedef struct upnp_adinfo_t {
|
||||||
|
int type; /** one of the UPNP_AD_ values */
|
||||||
|
int version; /** numeric version. 0 if unversioned */
|
||||||
|
char *location; /** url path to descriptor. e.g. /upnp-device.xml */
|
||||||
|
char *namespace; /** usually "upnp" or "urn:schemas-upnp-org" */
|
||||||
|
char *name; /** MediaServer, ConnectionManager, rootdevice, etc */
|
||||||
|
char *body; /** body of a alive packet (postinfo) */
|
||||||
|
} UPNP_ADINFO;
|
||||||
|
|
||||||
typedef struct upnp_packetinfo_t {
|
typedef struct upnp_packetinfo_t {
|
||||||
char *group_id;
|
char *group_id;
|
||||||
char *base_packet;
|
UPNP_ADINFO *padinfo;
|
||||||
char *location;
|
|
||||||
char *usn;
|
|
||||||
char *nt;
|
|
||||||
char *body;
|
|
||||||
struct upnp_packetinfo_t *next;
|
struct upnp_packetinfo_t *next;
|
||||||
} UPNP_PACKETINFO;
|
} UPNP_PACKETINFO;
|
||||||
|
|
||||||
|
typedef struct upnp_disco_t {
|
||||||
|
int seconds_remaining;
|
||||||
|
char *query;
|
||||||
|
struct sockaddr to;
|
||||||
|
struct upnp_disco_t *next;
|
||||||
|
} UPNP_DISCO;
|
||||||
|
|
||||||
/* Globals */
|
/* Globals */
|
||||||
UPNP_PACKETINFO upnp_packetlist;
|
UPNP_PACKETINFO upnp_packetlist;
|
||||||
OS_SOCKETTYPE upnp_socket;
|
OS_SOCKETTYPE upnp_socket;
|
||||||
|
pthread_t upnp_listener_tid;
|
||||||
|
int upnp_quitflag = 0;
|
||||||
|
int upnp_thread_started = 0;
|
||||||
|
|
||||||
/* Forwards */
|
/* Forwards */
|
||||||
int upnp_strcat(char *what, char *where, int bytes_left);
|
int upnp_strcat(char *what, char *where, int bytes_left);
|
||||||
void upnp_build_packet(char *packet, int len, UPNP_PACKETINFO *pi);
|
void upnp_build_packet(char *packet, int len, int type, UPNP_PACKETINFO *pi,
|
||||||
void upnp_broadcast(void);
|
UPNP_DISCO *pdisco);
|
||||||
|
void upnp_broadcast(int type, UPNP_DISCO *pdisco);
|
||||||
|
void *upnp_listener(void *arg);
|
||||||
|
void upnp_process_packet(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* add a upnp packet too the root list.
|
* add a upnp packet too the root list.
|
||||||
*/
|
*/
|
||||||
void upnp_add_packet(char *group_id, char *location,
|
void upnp_add_packet(char *group_id, int type, char *location,
|
||||||
char *usn, char *nt, char *body) {
|
char *namespace, char *name, int version,
|
||||||
|
char *body) {
|
||||||
UPNP_PACKETINFO *pnew;
|
UPNP_PACKETINFO *pnew;
|
||||||
|
UPNP_ADINFO *pnewinfo;
|
||||||
|
|
||||||
pnew = (UPNP_PACKETINFO *)malloc(sizeof(UPNP_PACKETINFO));
|
pnew = (UPNP_PACKETINFO *)malloc(sizeof(UPNP_PACKETINFO));
|
||||||
if(!pnew)
|
if(!pnew)
|
||||||
DPRINTF(E_FATAL,L_MISC,"Malloc error\n");
|
DPRINTF(E_FATAL,L_MISC,"Malloc error\n");
|
||||||
|
|
||||||
|
pnewinfo = (UPNP_ADINFO *)malloc(sizeof(UPNP_ADINFO));
|
||||||
|
if(!pnewinfo)
|
||||||
|
DPRINTF(E_FATAL,L_MISC,"Malloc error\n");
|
||||||
|
|
||||||
memset(pnew,0,sizeof(UPNP_PACKETINFO));
|
memset(pnew,0,sizeof(UPNP_PACKETINFO));
|
||||||
|
memset(pnewinfo,0,sizeof(UPNP_ADINFO));
|
||||||
|
|
||||||
if(group_id)
|
if(group_id)
|
||||||
pnew->group_id = strdup(group_id);
|
pnew->group_id = strdup(group_id);
|
||||||
if(location)
|
|
||||||
pnew->location = strdup(location);
|
pnewinfo->type = type;
|
||||||
if(usn)
|
pnewinfo->version = version;
|
||||||
pnew->usn = strdup(usn);
|
if(location) pnewinfo->location = strdup(location);
|
||||||
if(nt)
|
if(namespace) pnewinfo->namespace = strdup(namespace);
|
||||||
pnew->nt = strdup(nt);
|
if(name) pnewinfo->name = strdup(name);
|
||||||
if(body)
|
if(body) pnewinfo->body = strdup(body);
|
||||||
pnew->body = strdup(body);
|
|
||||||
|
pnew->padinfo = pnewinfo;
|
||||||
|
|
||||||
util_mutex_lock(l_upnp);
|
util_mutex_lock(l_upnp);
|
||||||
pnew->next = upnp_packetlist.next;
|
pnew->next = upnp_packetlist.next;
|
||||||
@ -102,7 +137,8 @@ int upnp_strcat(char *what, char *where, int bytes_left) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void upnp_build_packet(char *packet, int len, UPNP_PACKETINFO *pi) {
|
void upnp_build_packet(char *packet, int len, int type,
|
||||||
|
UPNP_PACKETINFO *pi, UPNP_DISCO *pdisco) {
|
||||||
char buffer[256];
|
char buffer[256];
|
||||||
char hostname[256];
|
char hostname[256];
|
||||||
*packet = '\0';
|
*packet = '\0';
|
||||||
@ -110,41 +146,81 @@ void upnp_build_packet(char *packet, int len, UPNP_PACKETINFO *pi) {
|
|||||||
|
|
||||||
port = conf_get_int("general","port",0);
|
port = conf_get_int("general","port",0);
|
||||||
|
|
||||||
|
if(type == UPNP_TYPE_RESPONSE) {
|
||||||
|
len = upnp_strcat("HTTP/1.1 200 OK\r\n",packet,len);
|
||||||
|
} else {
|
||||||
len = upnp_strcat("NOTIFY * HTTP/1.1\r\n",packet,len);
|
len = upnp_strcat("NOTIFY * HTTP/1.1\r\n",packet,len);
|
||||||
if(pi->location) {
|
}
|
||||||
|
|
||||||
|
if(pi->padinfo->location) {
|
||||||
gethostname(hostname,sizeof(hostname));
|
gethostname(hostname,sizeof(hostname));
|
||||||
snprintf(buffer,sizeof(buffer),"LOCATION: http://%s:%d%s\r\n",
|
snprintf(buffer,sizeof(buffer),"LOCATION: http://%s:%d%s\r\n",
|
||||||
hostname,port,pi->location);
|
hostname,port,pi->padinfo->location);
|
||||||
|
|
||||||
len=upnp_strcat(buffer,packet,len);
|
len=upnp_strcat(buffer,packet,len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(type != UPNP_TYPE_RESPONSE)
|
||||||
len=upnp_strcat("HOST: 239.255.255.250:1900\r\n",packet,len);
|
len=upnp_strcat("HOST: 239.255.255.250:1900\r\n",packet,len);
|
||||||
|
|
||||||
len=upnp_strcat("SERVER: POSIX, UPnP/1.0, Firefly/" VERSION "\r\n",
|
len=upnp_strcat("SERVER: POSIX, UPnP/1.0, Firefly/" VERSION "\r\n",
|
||||||
packet,len);
|
packet,len);
|
||||||
len=upnp_strcat("NTS: ssdp:alive\r\n",packet,len);
|
|
||||||
|
|
||||||
/* USN */
|
if(type == UPNP_TYPE_ALIVE) {
|
||||||
len=upnp_strcat("USN:uuid:",packet,len);
|
len=upnp_strcat("NTS: ssdp:alive\r\n",packet,len);
|
||||||
len=upnp_strcat(UPNP_UUID,packet,len);
|
} else if(type == UPNP_TYPE_BYEBYE) {
|
||||||
if(pi->usn) {
|
len=upnp_strcat("NTS: ssdp:byebye\r\n",packet,len);
|
||||||
snprintf(buffer,sizeof(buffer),"::%s",pi->usn);
|
} else if ((type == UPNP_TYPE_RESPONSE) && (pdisco)) {
|
||||||
|
/* we need the original ST */
|
||||||
|
snprintf(buffer,sizeof(buffer),"ST: %s\r\n",pdisco->query);
|
||||||
|
len = upnp_strcat(buffer,packet,len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* USN & NT */
|
||||||
|
switch(pi->padinfo->type) {
|
||||||
|
case UPNP_AD_BARE:
|
||||||
|
snprintf(buffer,sizeof(buffer),"USN: uuid:%s\r\n",UPNP_UUID);
|
||||||
|
len=upnp_strcat(buffer,packet,len);
|
||||||
|
if(type != UPNP_TYPE_RESPONSE) { /* no NT for responses */
|
||||||
|
snprintf(buffer,sizeof(buffer),"NT: uuid:%s\r\n",UPNP_UUID);
|
||||||
|
len=upnp_strcat(buffer,packet,len);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UPNP_AD_DEVICE:
|
||||||
|
case UPNP_AD_SERVICE:
|
||||||
|
case UPNP_AD_ROOT:
|
||||||
|
snprintf(buffer,sizeof(buffer),"USN: uuid:%s::%s:%s",UPNP_UUID,
|
||||||
|
pi->padinfo->namespace, pi->padinfo->name);
|
||||||
|
len=upnp_strcat(buffer,packet,len);
|
||||||
|
if(pi->padinfo->version) {
|
||||||
|
snprintf(buffer,sizeof(buffer),"%d",pi->padinfo->version);
|
||||||
len=upnp_strcat(buffer,packet,len);
|
len=upnp_strcat(buffer,packet,len);
|
||||||
}
|
}
|
||||||
len=upnp_strcat("\r\n",packet,len);
|
len=upnp_strcat("\r\n",packet,len);
|
||||||
|
|
||||||
len=upnp_strcat("CACHE-CONTROL: max-age=1800\r\n",packet,len);
|
if(type != UPNP_TYPE_RESPONSE) { /* no NT for responses */
|
||||||
if(pi->nt) {
|
snprintf(buffer,sizeof(buffer),"NT: %s:%s",
|
||||||
snprintf(buffer,sizeof(buffer),"NT:%s\r\n",pi->nt);
|
pi->padinfo->namespace, pi->padinfo->name);
|
||||||
} else {
|
len=upnp_strcat(buffer,packet,len);
|
||||||
snprintf(buffer,sizeof(buffer),"NT:uuid:%s\r\n", UPNP_UUID);
|
if(pi->padinfo->version) {
|
||||||
|
snprintf(buffer,sizeof(buffer),"%d",pi->padinfo->version);
|
||||||
|
len=upnp_strcat(buffer,packet,len);
|
||||||
|
}
|
||||||
|
len=upnp_strcat("\r\n",packet,len);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
len=upnp_strcat(buffer,packet,len);
|
|
||||||
|
|
||||||
if(pi->body) {
|
len=upnp_strcat("CACHE-CONTROL: max-age=1800\r\n",packet,len);
|
||||||
|
|
||||||
|
if((pi->padinfo->body) && (type != UPNP_TYPE_RESPONSE)) {
|
||||||
snprintf(buffer,sizeof(buffer),"Content-Length: %d\r\n\r\n",
|
snprintf(buffer,sizeof(buffer),"Content-Length: %d\r\n\r\n",
|
||||||
(int)strlen(pi->body));
|
(int)strlen(pi->padinfo->body));
|
||||||
len=upnp_strcat(buffer,packet,len);
|
len=upnp_strcat(buffer,packet,len);
|
||||||
len=upnp_strcat(pi->body,packet,len);
|
len=upnp_strcat(pi->padinfo->body,packet,len);
|
||||||
} else {
|
} else {
|
||||||
len=upnp_strcat("Content-Length: 0\r\n\r\n",packet,len);
|
len=upnp_strcat("Content-Length: 0\r\n\r\n",packet,len);
|
||||||
}
|
}
|
||||||
@ -153,7 +229,7 @@ void upnp_build_packet(char *packet, int len, UPNP_PACKETINFO *pi) {
|
|||||||
/**
|
/**
|
||||||
* broadcast out the upnp packets
|
* broadcast out the upnp packets
|
||||||
*/
|
*/
|
||||||
void upnp_broadcast(void) {
|
void upnp_broadcast(int type, UPNP_DISCO *pdisco) {
|
||||||
UPNP_PACKETINFO *pi;
|
UPNP_PACKETINFO *pi;
|
||||||
struct sockaddr_in sin;
|
struct sockaddr_in sin;
|
||||||
char packet[UPNP_MAX_PACKET];
|
char packet[UPNP_MAX_PACKET];
|
||||||
@ -166,10 +242,12 @@ void upnp_broadcast(void) {
|
|||||||
|
|
||||||
util_mutex_lock(l_upnp);
|
util_mutex_lock(l_upnp);
|
||||||
|
|
||||||
|
DPRINTF(E_DBG,L_MISC,"Sending upnp broadcast of type: %d\n",type);
|
||||||
|
|
||||||
for(pass=0; pass < 2; pass++) {
|
for(pass=0; pass < 2; pass++) {
|
||||||
pi=upnp_packetlist.next;
|
pi=upnp_packetlist.next;
|
||||||
while(pi) {
|
while(pi) {
|
||||||
upnp_build_packet(packet, UPNP_MAX_PACKET, pi);
|
upnp_build_packet(packet, UPNP_MAX_PACKET, type, pi, pdisco);
|
||||||
sendto(upnp_socket,packet,strlen(packet),0,
|
sendto(upnp_socket,packet,strlen(packet),0,
|
||||||
(struct sockaddr *)&sin, sizeof(sin));
|
(struct sockaddr *)&sin, sizeof(sin));
|
||||||
pi = pi->next;
|
pi = pi->next;
|
||||||
@ -184,7 +262,7 @@ int upnp_tick(void) {
|
|||||||
static time_t last_broadcast = 0;
|
static time_t last_broadcast = 0;
|
||||||
|
|
||||||
if((time(NULL) - last_broadcast) > 60) {
|
if((time(NULL) - last_broadcast) > 60) {
|
||||||
upnp_broadcast();
|
upnp_broadcast(UPNP_TYPE_ALIVE,NULL);
|
||||||
last_broadcast = time(NULL);
|
last_broadcast = time(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,24 +278,23 @@ int upnp_init(void) {
|
|||||||
int ttl = 3;
|
int ttl = 3;
|
||||||
int reuse=1;
|
int reuse=1;
|
||||||
int result;
|
int result;
|
||||||
|
int err;
|
||||||
struct sockaddr_in addr;
|
struct sockaddr_in addr;
|
||||||
struct ip_mreq mreq;
|
struct ip_mreq mreq;
|
||||||
|
|
||||||
|
srand((unsigned)time(NULL));
|
||||||
|
|
||||||
memset(&upnp_packetlist,0,sizeof(upnp_packetlist));
|
memset(&upnp_packetlist,0,sizeof(upnp_packetlist));
|
||||||
upnp_add_packet("base","/upnp-basic.xml",NULL,NULL,NULL);
|
upnp_add_packet("base,", UPNP_AD_BARE, "/upnp-basic.xml",
|
||||||
upnp_add_packet("base","/upnp-basic.xml",
|
NULL, NULL, 0, NULL);
|
||||||
"urn:schemas-upnp-org:device:MediaServer:1",
|
upnp_add_packet("base,", UPNP_AD_DEVICE, "/upnp-basic.xml",
|
||||||
"urn:schemas-upnp-org:device:MediaServer:1",NULL);
|
"urn:schemas-upnp-org","MediaServer", 1, NULL);
|
||||||
upnp_add_packet("base","/upnp-basic.xml",
|
upnp_add_packet("base,", UPNP_AD_SERVICE, "/upnp-basic.xml",
|
||||||
"urn:schemas-upnp-org:service:AVTransport:1",
|
"urn:schemas-upnp-org","ContentDirectory", 1, NULL);
|
||||||
"urn:schemas-upnp-org:service:AVTransport:1",NULL);
|
upnp_add_packet("base,", UPNP_AD_SERVICE, "/upnp-basic.xml",
|
||||||
upnp_add_packet("base","/upnp-basic.xml",
|
"urn:schemas-upnp-org","ConnectionManager", 1, NULL);
|
||||||
"urn:schemas-upnp-org:service:ContentDirectory:1",
|
upnp_add_packet("base,", UPNP_AD_ROOT, "/upnp-basic.xml",
|
||||||
"urn:schemas-upnp-org:service:ContentDirectory:1",NULL);
|
"upnp","rootdevice",0,NULL);
|
||||||
upnp_add_packet("basic","/upnp-basic.xml",
|
|
||||||
"urn:schemas-upnp-org:service:ConnectionManager:1",
|
|
||||||
"urn:schemas-upnp-org:service:ConnectionManager:1",NULL);
|
|
||||||
upnp_add_packet("base","/upnp-basic.xml","upnp:rootdevice","upnp:rootdevice",NULL);
|
|
||||||
|
|
||||||
upnp_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
upnp_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||||
result = setsockopt(upnp_socket, IPPROTO_IP, IP_MULTICAST_TTL,
|
result = setsockopt(upnp_socket, IPPROTO_IP, IP_MULTICAST_TTL,
|
||||||
@ -256,15 +333,181 @@ int upnp_init(void) {
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* got the socket, let's start up the listener thread */
|
||||||
|
DPRINTF(E_DBG,L_MISC,"Starting UPnP listener thread\n");
|
||||||
|
if((err=pthread_create(&upnp_listener_tid,NULL,upnp_listener,NULL))) {
|
||||||
|
DPRINTF(E_LOG,L_MISC,"Warning: could not start upnp listener: %s\n",
|
||||||
|
strerror(err));
|
||||||
|
upnp_thread_started=0;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* listener thread for upnp query requests
|
||||||
|
*/
|
||||||
|
void *upnp_listener(void *arg) {
|
||||||
|
int result;
|
||||||
|
fd_set readset;
|
||||||
|
struct timeval timeout;
|
||||||
|
|
||||||
|
upnp_thread_started = 1;
|
||||||
|
DPRINTF(E_DBG,L_MISC,"upnp listener thread started\n");
|
||||||
|
|
||||||
|
/* sit in a tight select loop until upnp_deinit sets the
|
||||||
|
* quitflag */
|
||||||
|
while(!upnp_quitflag) {
|
||||||
|
FD_ZERO(&readset);
|
||||||
|
FD_SET(upnp_socket,&readset); /* win32? */
|
||||||
|
|
||||||
|
/* this is kind of lame, but will defend from an OS
|
||||||
|
* that doesn't return a select error when an underlying
|
||||||
|
* file handle is closed */
|
||||||
|
timeout.tv_sec=UPNP_SELECT_TIMEOUT;
|
||||||
|
timeout.tv_usec=0;
|
||||||
|
|
||||||
|
while((result=select(upnp_socket+1,&readset,NULL,NULL,&timeout)==-1) &&
|
||||||
|
(errno = EINTR)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
if(result == -1) {
|
||||||
|
/* this is a real error */
|
||||||
|
DPRINTF(E_LOG,L_MISC,"Error in select in upnp listener: %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
upnp_thread_started=0;
|
||||||
|
pthread_exit(NULL);
|
||||||
|
}
|
||||||
|
upnp_process_packet();
|
||||||
|
}
|
||||||
|
|
||||||
|
upnp_thread_started=0;
|
||||||
|
DPRINTF(E_DBG,L_MISC,"upnp listener exiting\n");
|
||||||
|
pthread_exit(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* process a upnp packet. This is either a discovery or an advertisement
|
||||||
|
* from another device on the local netowrk.
|
||||||
|
*/
|
||||||
|
void upnp_process_packet(void) {
|
||||||
|
char upnp_packet[UPNP_MAX_PACKET];
|
||||||
|
ssize_t bytes_read;
|
||||||
|
char *line_start, *line_end;
|
||||||
|
int discovery = 0;
|
||||||
|
int mx = 0;
|
||||||
|
char *query=NULL;
|
||||||
|
struct sockaddr_in from;
|
||||||
|
socklen_t from_len;
|
||||||
|
UPNP_DISCO *pdisco;
|
||||||
|
|
||||||
|
from_len = sizeof(from);
|
||||||
|
bytes_read = recvfrom(upnp_socket,&upnp_packet,sizeof(upnp_packet),0,
|
||||||
|
(struct sockaddr *)&from, &from_len);
|
||||||
|
|
||||||
|
/* now, parse the packet. If it's a upnp query, it's a
|
||||||
|
* packet with a ssdp of "discover".
|
||||||
|
*/
|
||||||
|
|
||||||
|
line_start = line_end = upnp_packet;
|
||||||
|
while((line_end - upnp_packet) < bytes_read) {
|
||||||
|
while(((*line_start=='\n')||(*line_start=='\r')) &&
|
||||||
|
(line_end - upnp_packet < bytes_read)) {
|
||||||
|
/* absorb empty cr/lf pairs */
|
||||||
|
line_start++;
|
||||||
|
line_end++;
|
||||||
|
}
|
||||||
|
|
||||||
|
while((*line_end != '\n') && (*line_end != '\r') &&
|
||||||
|
(line_end - upnp_packet < bytes_read))
|
||||||
|
line_end++;
|
||||||
|
|
||||||
|
if((*line_end == '\n') || (*line_end == '\r')) {
|
||||||
|
/* we have a full line */
|
||||||
|
*line_end = '\0';
|
||||||
|
line_end++;
|
||||||
|
|
||||||
|
/* okay... see what it is */
|
||||||
|
if(strncasecmp(line_start,"M-SEARCH",8) == 0) {
|
||||||
|
DPRINTF(E_DBG,L_MISC,"upnp_packet: M-SEARCH from %s\n",
|
||||||
|
inet_ntoa(from.sin_addr));
|
||||||
|
|
||||||
|
discovery=1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(strncasecmp(line_start,"ST:",3)==0) {
|
||||||
|
/* There is a search term specified */
|
||||||
|
if(query)
|
||||||
|
free(query);
|
||||||
|
|
||||||
|
line_start += 3;
|
||||||
|
while(*line_start && *line_start == ' ')
|
||||||
|
line_start++;
|
||||||
|
|
||||||
|
query = strdup(line_start);
|
||||||
|
DPRINTF(E_DBG,L_MISC,"upnp_packet: Query: %s\n",query);
|
||||||
|
}
|
||||||
|
if(strncasecmp(line_start,"MX:",3)==0) {
|
||||||
|
mx = atoi(line_start + 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
line_start=line_end;
|
||||||
|
/* we could process the packet, and advertise, if necessary */
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if(discovery) {
|
||||||
|
DPRINTF(E_DBG,L_MISC,"Responding to query request...\n");
|
||||||
|
|
||||||
|
/* do we care? Let's check the query and see if it is interesting */
|
||||||
|
discovery = 0;
|
||||||
|
|
||||||
|
|
||||||
|
pdisco = (UPNP_DISCO *)malloc(sizeof(UPNP_DISCO));
|
||||||
|
if(!pdisco)
|
||||||
|
DPRINTF(E_FATAL,L_MISC,"malloc error");
|
||||||
|
|
||||||
|
memset(pdisco,0,sizeof(UPNP_DISCO));
|
||||||
|
if(mx) {
|
||||||
|
pdisco->seconds_remaining = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
pdisco->query = query;
|
||||||
|
query = NULL;
|
||||||
|
memcpy((void*)&pdisco->to, (void*)&from, sizeof(struct sockaddr));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(query) {
|
||||||
|
free(query);
|
||||||
|
query = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* turn off any upnp services. Should really de-register
|
* turn off any upnp services. Should really de-register
|
||||||
* all registered devices, etc.
|
* all registered devices, etc.
|
||||||
*/
|
*/
|
||||||
int upnp_deinit(void) {
|
int upnp_deinit(void) {
|
||||||
|
void *result_ptr;
|
||||||
|
|
||||||
|
upnp_quitflag=1;
|
||||||
|
upnp_broadcast(UPNP_TYPE_BYEBYE,NULL);
|
||||||
|
|
||||||
|
if(upnp_socket)
|
||||||
close(upnp_socket);
|
close(upnp_socket);
|
||||||
|
|
||||||
|
/* socket is dead, quitflag is set, listener should terminate
|
||||||
|
* reasonably quickly */
|
||||||
|
|
||||||
|
if(upnp_thread_started) {
|
||||||
|
upnp_thread_started=0;
|
||||||
|
pthread_join(upnp_listener_tid, &result_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
10
src/upnp.h
10
src/upnp.h
@ -33,10 +33,16 @@
|
|||||||
|
|
||||||
#define UPNP_UUID "12345678-1234-1234-1234-123456789013"
|
#define UPNP_UUID "12345678-1234-1234-1234-123456789013"
|
||||||
|
|
||||||
|
#define UPNP_AD_DEVICE 1
|
||||||
|
#define UPNP_AD_SERVICE 2
|
||||||
|
#define UPNP_AD_ROOT 3
|
||||||
|
#define UPNP_AD_BARE 4
|
||||||
|
|
||||||
extern int upnp_init(void);
|
extern int upnp_init(void);
|
||||||
extern int upnp_tick(void);
|
extern int upnp_tick(void);
|
||||||
extern int upnp_deinit(void);
|
extern int upnp_deinit(void);
|
||||||
extern void upnp_add_packet(char *group_id, char *location,
|
extern void upnp_add_packet(char *group_id, int type, char *location,
|
||||||
char *usn, char *nt, char *body);
|
char *namespace, char *name, int version,
|
||||||
|
char *body);
|
||||||
|
|
||||||
#endif /* _UPNP_H_ */
|
#endif /* _UPNP_H_ */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user