Finish (mostly) upnp discovery

This commit is contained in:
Ron Pedde 2007-05-04 21:31:05 +00:00
parent 0eba96d92a
commit 648297e253
11 changed files with 472 additions and 49 deletions

View File

@ -15,18 +15,27 @@
<modelURL>http://www.fireflymediaserver.org/</modelURL> <modelURL>http://www.fireflymediaserver.org/</modelURL>
<serialNumber>SERIAL-001</serialNumber> <serialNumber>SERIAL-001</serialNumber>
<UDN>uuid:@upnp@</UDN> <UDN>uuid:@upnp@</UDN>
<iconList>
<icon>
<mimetype>image/png</mimetype>
<width>32</width>
<height>32</height>
<depth>32</depth>
<url>/upnp/ff_logo_32.png</url>
</icon>
</iconList>
<serviceList> <serviceList>
<service> <service>
<serviceType>urn:schemas-upnp-org:service:ConnectionManager:1</serviceType> <serviceType>urn:schemas-upnp-org:service:ConnectionManager:1</serviceType>
<serviceId>urn:upnp-org:serviceId:ConnectionManager</serviceId> <serviceId>urn:upnp-org:serviceId:ConnectionManager</serviceId>
<SCPDURL>/upnp/cm/scpdurl</SCPDURL> <SCPDURL>/upnp-cm.xml</SCPDURL>
<controlURL>/upnp/cm/control</controlURL> <controlURL>/upnp/cm/control</controlURL>
<eventSubURL>/upnp/cm/event</eventSubURL> <eventSubURL>/upnp/cm/event</eventSubURL>
</service> </service>
<service> <service>
<serviceType>urn:schemas-upnp-org:service:ContentDirectory:1</serviceType> <serviceType>urn:schemas-upnp-org:service:ContentDirectory:1</serviceType>
<serviceId>urn:upnp-org:serviceId:ContentDirectory</serviceId> <serviceId>urn:upnp-org:serviceId:ContentDirectory</serviceId>
<SCPDURL>/upnp/cd/scpdurl</SCPDURL> <SCPDURL>/upnp-cd.xml</SCPDURL>
<controlURL>/upnp/cd/control</controlURL> <controlURL>/upnp/cd/control</controlURL>
<eventSubURL>/upnp/cd/event</eventSubURL> <eventSubURL>/upnp/cd/event</eventSubURL>
</service> </service>

178
admin-root/upnp-cd.xml Normal file
View File

@ -0,0 +1,178 @@
<?xml version="1.0" encoding="utf-8"?>
<scpd xmlns="urn:schemas-upnp-org:service-1-0">
<specVersion>
<major>1</major>
<minor>0</minor>
</specVersion>
<actionList>
<action>
<name>Browse</name>
<argumentList>
<argument>
<name>ObjectID</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_ObjectID</relatedStateVariable>
</argument>
<argument>
<name>BrowseFlag</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_BrowseFlag</relatedStateVariable>
</argument>
<argument>
<name>Filter</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_Filter</relatedStateVariable>
</argument>
<argument>
<name>StartingIndex</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_Index</relatedStateVariable>
</argument>
<argument>
<name>RequestedCount</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_Count</relatedStateVariable>
</argument>
<argument>
<name>SortCriteria</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_SortCriteria</relatedStateVariable>
</argument>
<argument>
<name>Result</name>
<direction>out</direction>
<relatedStateVariable>A_ARG_TYPE_Result</relatedStateVariable>
</argument>
<argument>
<name>NumberReturned</name>
<direction>out</direction>
<relatedStateVariable>A_ARG_TYPE_Count</relatedStateVariable>
</argument>
<argument>
<name>TotalMatches</name>
<direction>out</direction>
<relatedStateVariable>A_ARG_TYPE_Count</relatedStateVariable>
</argument>
<argument>
<name>UpdateID</name>
<direction>out</direction>
<relatedStateVariable>A_ARG_TYPE_UpdateID</relatedStateVariable>
</argument>
</argumentList>
</action>
<action>
<name>DestroyObject</name>
<argumentList>
<argument>
<name>ObjectID</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_ObjectID</relatedStateVariable>
</argument>
</argumentList>
</action>
<action>
<name>GetSystemUpdateID</name>
<argumentList>
<argument>
<name>Id</name>
<direction>out</direction>
<relatedStateVariable>SystemUpdateID</relatedStateVariable>
</argument>
</argumentList>
</action>
<action>
<name>GetSearchCapabilities</name>
<argumentList>
<argument>
<name>SearchCaps</name>
<direction>out</direction>
<relatedStateVariable>SearchCapabilities</relatedStateVariable>
</argument>
</argumentList>
</action>
<action>
<name>GetSortCapabilities</name>
<argumentList>
<argument>
<name>SortCaps</name>
<direction>out</direction>
<relatedStateVariable>SortCapabilities</relatedStateVariable>
</argument>
</argumentList>
</action>
<action>
<name>UpdateObject</name>
<argumentList>
<argument>
<name>ObjectID</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_ObjectID</relatedStateVariable>
</argument>
<argument>
<name>CurrentTagValue</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_TagValueList</relatedStateVariable>
</argument>
<argument>
<name>NewTagValue</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_TagValueList</relatedStateVariable>
</argument>
</argumentList>
</action>
</actionList>
<serviceStateTable>
<stateVariable sendEvents="no">
<name>A_ARG_TYPE_BrowseFlag</name>
<dataType>string</dataType>
<allowedValueList>
<allowedValue>BrowseMetadata</allowedValue>
<allowedValue>BrowseDirectChildren</allowedValue>
</allowedValueList>
</stateVariable>
<stateVariable sendEvents="yes">
<name>SystemUpdateID</name>
<dataType>ui4</dataType>
</stateVariable>
<stateVariable sendEvents="no">
<name>A_ARG_TYPE_Count</name>
<dataType>ui4</dataType>
</stateVariable>
<stateVariable sendEvents="no">
<name>A_ARG_TYPE_SortCriteria</name>
<dataType>string</dataType>
</stateVariable>
<stateVariable sendEvents="no">
<name>SortCapabilities</name>
<dataType>string</dataType>
</stateVariable>
<stateVariable sendEvents="no">
<name>A_ARG_TYPE_Index</name>
<dataType>ui4</dataType>
</stateVariable>
<stateVariable sendEvents="no">
<name>A_ARG_TYPE_ObjectID</name>
<dataType>string</dataType>
</stateVariable>
<stateVariable sendEvents="no">
<name>A_ARG_TYPE_UpdateID</name>
<dataType>ui4</dataType>
</stateVariable>
<stateVariable sendEvents="no">
<name>A_ARG_TYPE_TagValueList</name>
<dataType>string</dataType>
</stateVariable>
<stateVariable sendEvents="no">
<name>A_ARG_TYPE_Result</name>
<dataType>string</dataType>
</stateVariable>
<stateVariable sendEvents="no">
<name>SearchCapabilities</name>
<dataType>string</dataType>
</stateVariable>
<stateVariable sendEvents="no">
<name>A_ARG_TYPE_Filter</name>
<dataType>string</dataType>
</stateVariable>
</serviceStateTable>
</scpd>

132
admin-root/upnp-cm.xml Normal file
View File

@ -0,0 +1,132 @@
<?xml version="1.0" encoding="utf-8"?>
<scpd xmlns="urn:schemas-upnp-org:service-1-0">
<specVersion>
<major>1</major>
<minor>0</minor>
</specVersion>
<actionList>
<action>
<name>GetCurrentConnectionInfo</name>
<argumentList>
<argument>
<name>ConnectionID</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_ConnectionID</relatedStateVariable>
</argument>
<argument>
<name>RcsID</name>
<direction>out</direction>
<relatedStateVariable>A_ARG_TYPE_RcsID</relatedStateVariable>
</argument>
<argument>
<name>AVTransportID</name>
<direction>out</direction>
<relatedStateVariable>A_ARG_TYPE_AVTransportID</relatedStateVariable>
</argument>
<argument>
<name>ProtocolInfo</name>
<direction>out</direction>
<relatedStateVariable>A_ARG_TYPE_ProtocolInfo</relatedStateVariable>
</argument>
<argument>
<name>PeerConnectionManager</name>
<direction>out</direction>
<relatedStateVariable>A_ARG_TYPE_ConnectionManager</relatedStateVariable>
</argument>
<argument>
<name>PeerConnectionID</name>
<direction>out</direction>
<relatedStateVariable>A_ARG_TYPE_ConnectionID</relatedStateVariable>
</argument>
<argument>
<name>Direction</name>
<direction>out</direction>
<relatedStateVariable>A_ARG_TYPE_Direction</relatedStateVariable>
</argument>
<argument>
<name>Status</name>
<direction>out</direction>
<relatedStateVariable>A_ARG_TYPE_ConnectionStatus</relatedStateVariable>
</argument>
</argumentList>
</action>
<action>
<name>GetProtocolInfo</name>
<argumentList>
<argument>
<name>Source</name>
<direction>out</direction>
<relatedStateVariable>SourceProtocolInfo</relatedStateVariable>
</argument>
<argument>
<name>Sink</name>
<direction>out</direction>
<relatedStateVariable>SinkProtocolInfo</relatedStateVariable>
</argument>
</argumentList>
</action>
<action>
<name>GetCurrentConnectionIDs</name>
<argumentList>
<argument>
<name>ConnectionIDs</name>
<direction>out</direction>
<relatedStateVariable>CurrentConnectionIDs</relatedStateVariable>
</argument>
</argumentList>
</action>
</actionList>
<serviceStateTable>
<stateVariable sendEvents="no">
<name>A_ARG_TYPE_ProtocolInfo</name>
<dataType>string</dataType>
</stateVariable>
<stateVariable sendEvents="no">
<name>A_ARG_TYPE_ConnectionStatus</name>
<dataType>string</dataType>
<allowedValueList>
<allowedValue>OK</allowedValue>
<allowedValue>ContentFormatMismatch</allowedValue>
<allowedValue>InsufficientBandwidth</allowedValue>
<allowedValue>UnreliableChannel</allowedValue>
<allowedValue>Unknown</allowedValue>
</allowedValueList>
</stateVariable>
<stateVariable sendEvents="no">
<name>A_ARG_TYPE_AVTransportID</name>
<dataType>i4</dataType>
</stateVariable>
<stateVariable sendEvents="no">
<name>A_ARG_TYPE_RcsID</name>
<dataType>i4</dataType>
</stateVariable>
<stateVariable sendEvents="no">
<name>A_ARG_TYPE_ConnectionID</name>
<dataType>i4</dataType>
</stateVariable>
<stateVariable sendEvents="no">
<name>A_ARG_TYPE_ConnectionManager</name>
<dataType>string</dataType>
</stateVariable>
<stateVariable sendEvents="yes">
<name>SourceProtocolInfo</name>
<dataType>string</dataType>
</stateVariable>
<stateVariable sendEvents="yes">
<name>SinkProtocolInfo</name>
<dataType>string</dataType>
</stateVariable>
<stateVariable sendEvents="no">
<name>A_ARG_TYPE_Direction</name>
<dataType>string</dataType>
<allowedValueList>
<allowedValue>Input</allowedValue>
<allowedValue>Output</allowedValue>
</allowedValueList>
</stateVariable>
<stateVariable sendEvents="yes">
<name>CurrentConnectionIDs</name>
<dataType>string</dataType>
</stateVariable>
</serviceStateTable>
</scpd>

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 606 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 838 B

View File

@ -1226,6 +1226,7 @@ int conf_write(void) {
FILE *fp; FILE *fp;
if(!conf_main_file) { if(!conf_main_file) {
DPRINTF(E_DBG,L_CONF,"Config file apparently not loaded\n");
return CONF_E_NOCONF; return CONF_E_NOCONF;
} }
@ -1233,10 +1234,13 @@ int conf_write(void) {
if((fp = fopen(conf_main_file,"w+")) != NULL) { if((fp = fopen(conf_main_file,"w+")) != NULL) {
retval = _conf_write(fp,conf_main,0,NULL); retval = _conf_write(fp,conf_main,0,NULL);
fclose(fp); fclose(fp);
} else {
DPRINTF(E_LOG,L_CONF,"Error opening config file for write: %s\n",
strerror(errno));
} }
util_mutex_unlock(l_conf); util_mutex_unlock(l_conf);
return retval ? CONF_E_SUCCESS : CONF_E_NOTWRITABLE; return retval;
} }
/** /**
@ -1299,8 +1303,10 @@ int _conf_write(FILE *fp, LL *pll, int sublevel, char *parent) {
} }
fprintf(fp,"\n"); fprintf(fp,"\n");
if(!_conf_write(fp, pli->value.as_ll, 1, pli->key)) if(!_conf_write(fp, pli->value.as_ll, 1, pli->key)) {
return FALSE; DPRINTF(E_DBG,L_CONF,"Error writing key %s:%s\n",pli->key);
return FALSE;
}
} }
break; break;

View File

@ -531,7 +531,7 @@ int config_auth(WS_CONNINFO *pwsc, char *user, char *password) {
} }
void config_emit_upnp(WS_CONNINFO *pwsc, void *value, char *arg) { void config_emit_upnp(WS_CONNINFO *pwsc, void *value, char *arg) {
ws_writefd(pwsc,"%s",UPNP_UUID); ws_writefd(pwsc,"%s",upnp_uuid());
} }

View File

@ -589,10 +589,6 @@ int main(int argc, char *argv[]) {
config.reload = 1; /* force a reload on start */ config.reload = 1; /* force a reload on start */
while(!config.stop) { while(!config.stop) {
#ifdef UPNP
upnp_tick(); /* run the upnp loop */
#endif
if((conf_get_int("general","rescan_interval",0) && if((conf_get_int("general","rescan_interval",0) &&
(rescan_counter > conf_get_int("general","rescan_interval",0)))) { (rescan_counter > conf_get_int("general","rescan_interval",0)))) {
if((conf_get_int("general","always_scan",0)) || if((conf_get_int("general","always_scan",0)) ||

View File

@ -40,6 +40,7 @@
#include "upnp.h" #include "upnp.h"
#define UPNP_MAX_PACKET 1500 #define UPNP_MAX_PACKET 1500
#define UPNP_CACHE_DURATION 1800
#define UPNP_ADDR "239.255.255.250" #define UPNP_ADDR "239.255.255.250"
#define UPNP_PORT 1900 #define UPNP_PORT 1900
@ -47,7 +48,9 @@
#define UPNP_TYPE_BYEBYE 2 #define UPNP_TYPE_BYEBYE 2
#define UPNP_TYPE_RESPONSE 3 #define UPNP_TYPE_RESPONSE 3
#define UPNP_SELECT_TIMEOUT 1 /* update loop for discover replies */ #define UPNP_UUID "12345678-1234-1234-1234-123456789013"
#define UPNP_SELECT_TIMEOUT 1
typedef struct upnp_adinfo_t { typedef struct upnp_adinfo_t {
int type; /** one of the UPNP_AD_ values */ int type; /** one of the UPNP_AD_ values */
@ -67,16 +70,24 @@ typedef struct upnp_packetinfo_t {
typedef struct upnp_disco_t { typedef struct upnp_disco_t {
int seconds_remaining; int seconds_remaining;
char *query; char *query;
struct sockaddr to; struct sockaddr_in to;
struct upnp_disco_t *next; struct upnp_disco_t *next;
} UPNP_DISCO; } UPNP_DISCO;
/* Globals */ /* Globals */
UPNP_PACKETINFO upnp_packetlist; UPNP_PACKETINFO upnp_packetlist;
OS_SOCKETTYPE upnp_socket; OS_SOCKETTYPE upnp_socket;
UPNP_DISCO upnp_disco;
pthread_t upnp_listener_tid; pthread_t upnp_listener_tid;
int upnp_quitflag = 0; int upnp_quitflag = 0;
int upnp_thread_started = 0; int upnp_thread_started = 0;
char *upnp_broadcast_types[] = {
"none",
"SSDP Alive",
"SSDP Byebye",
"SSDP Discovery Response"
};
/* Forwards */ /* Forwards */
int upnp_strcat(char *what, char *where, int bytes_left); int upnp_strcat(char *what, char *where, int bytes_left);
@ -85,6 +96,15 @@ void upnp_build_packet(char *packet, int len, int type, UPNP_PACKETINFO *pi,
void upnp_broadcast(int type, UPNP_DISCO *pdisco); void upnp_broadcast(int type, UPNP_DISCO *pdisco);
void *upnp_listener(void *arg); void *upnp_listener(void *arg);
void upnp_process_packet(void); void upnp_process_packet(void);
void upnp_process_queries(void);
/**
* return the current upnp uuid
*/
char *upnp_uuid(void) {
return UPNP_UUID;
}
/** /**
* add a upnp packet too the root list. * add a upnp packet too the root list.
@ -178,11 +198,11 @@ void upnp_build_packet(char *packet, int len, int type,
/* USN & NT */ /* USN & NT */
switch(pi->padinfo->type) { switch(pi->padinfo->type) {
case UPNP_AD_BARE: case UPNP_AD_UUID:
snprintf(buffer,sizeof(buffer),"USN: uuid:%s\r\n",UPNP_UUID); snprintf(buffer,sizeof(buffer),"USN: uuid:%s\r\n",upnp_uuid());
len=upnp_strcat(buffer,packet,len); len=upnp_strcat(buffer,packet,len);
if(type != UPNP_TYPE_RESPONSE) { /* no NT for responses */ if(type != UPNP_TYPE_RESPONSE) { /* no NT for responses */
snprintf(buffer,sizeof(buffer),"NT: uuid:%s\r\n",UPNP_UUID); snprintf(buffer,sizeof(buffer),"NT: uuid:%s\r\n",upnp_uuid());
len=upnp_strcat(buffer,packet,len); len=upnp_strcat(buffer,packet,len);
} }
break; break;
@ -190,7 +210,7 @@ void upnp_build_packet(char *packet, int len, int type,
case UPNP_AD_DEVICE: case UPNP_AD_DEVICE:
case UPNP_AD_SERVICE: case UPNP_AD_SERVICE:
case UPNP_AD_ROOT: case UPNP_AD_ROOT:
snprintf(buffer,sizeof(buffer),"USN: uuid:%s::%s:%s",UPNP_UUID, snprintf(buffer,sizeof(buffer),"USN: uuid:%s::%s:%s",upnp_uuid(),
pi->padinfo->namespace, pi->padinfo->name); pi->padinfo->namespace, pi->padinfo->name);
len=upnp_strcat(buffer,packet,len); len=upnp_strcat(buffer,packet,len);
if(pi->padinfo->version) { if(pi->padinfo->version) {
@ -214,7 +234,9 @@ void upnp_build_packet(char *packet, int len, int type,
break; break;
} }
len=upnp_strcat("CACHE-CONTROL: max-age=1800\r\n",packet,len); snprintf(buffer,sizeof(buffer),"CACHE-CONTROL: max-age=%d\r\n",
UPNP_CACHE_DURATION);
len=upnp_strcat(buffer,packet,len);
if((pi->padinfo->body) && (type != UPNP_TYPE_RESPONSE)) { 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",
@ -234,41 +256,79 @@ void upnp_broadcast(int type, UPNP_DISCO *pdisco) {
struct sockaddr_in sin; struct sockaddr_in sin;
char packet[UPNP_MAX_PACKET]; char packet[UPNP_MAX_PACKET];
int pass; int pass;
char passes;
int send_packet;
int elements=0;
int recognized=0;
char **argv = NULL;
memset(&sin, 0, sizeof(sin)); memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET; if(type == UPNP_TYPE_RESPONSE) {
sin.sin_port = htons(UPNP_PORT); memcpy((void*)&sin,(void*)&pdisco->to,sizeof(struct sockaddr_in));
sin.sin_addr.s_addr = inet_addr(UPNP_ADDR); } else {
sin.sin_addr.s_addr = inet_addr(UPNP_ADDR);
sin.sin_family = AF_INET;
sin.sin_port = htons(UPNP_PORT);
}
util_mutex_lock(l_upnp); util_mutex_lock(l_upnp);
DPRINTF(E_DBG,L_MISC,"Sending upnp broadcast of type: %d\n",type); DPRINTF(E_DBG,L_MISC,"Sending upnp broadcast of type: %d (%s)\n",type,
upnp_broadcast_types[type]);
for(pass=0; pass < 2; pass++) { passes = 2;
if(type == UPNP_TYPE_RESPONSE) {
passes = 1;
if(strcasecmp(pdisco->query,"ssdp:all")==0)
passes = 3;
elements = util_split(pdisco->query,":",&argv);
}
for(pass=0; pass < passes; pass++) {
pi=upnp_packetlist.next; pi=upnp_packetlist.next;
while(pi) { while(pi) {
upnp_build_packet(packet, UPNP_MAX_PACKET, type, pi, pdisco); send_packet = 1;
sendto(upnp_socket,packet,strlen(packet),0, if(type == UPNP_TYPE_RESPONSE) {
(struct sockaddr *)&sin, sizeof(sin)); send_packet = 0;
/* if it's a request for rootdevice, only respond
with root device. */
if(strcasecmp(pdisco->query,"ssdp:all")==0) {
send_packet = 1;
} else if((strcasecmp(pdisco->query,"upnp:rootdevice")==0) &&
(pi->padinfo->type == UPNP_AD_ROOT)) {
send_packet = 1;
}
else if((strncasecmp(pdisco->query,"uuid:",5) == 0) &&
(!strcasecmp((char*)&pdisco->query[5],upnp_uuid())) &&
(pi->padinfo->type == UPNP_AD_UUID)) {
send_packet = 1;
}
}
if(send_packet) {
usleep(100);
recognized = 1;
upnp_build_packet(packet, UPNP_MAX_PACKET, type, pi, pdisco);
sendto(upnp_socket,packet,strlen(packet),0,
(struct sockaddr *)&sin, sizeof(sin));
}
pi = pi->next; pi = pi->next;
} }
} }
util_mutex_unlock(l_upnp); if(type == UPNP_TYPE_RESPONSE) {
} printf("%s respond to query %s\n", recognized ? "Did" : "Did not",
pdisco->query);
int upnp_tick(void) {
static time_t last_broadcast = 0;
if((time(NULL) - last_broadcast) > 60) {
upnp_broadcast(UPNP_TYPE_ALIVE,NULL);
last_broadcast = time(NULL);
} }
return TRUE; if(argv) {
} util_dispose_split(argv);
}
util_mutex_unlock(l_upnp);
}
/** /**
* start UPnP broadcaster. We'll want to send at least a rootdevice * start UPnP broadcaster. We'll want to send at least a rootdevice
@ -285,7 +345,9 @@ int upnp_init(void) {
srand((unsigned)time(NULL)); srand((unsigned)time(NULL));
memset(&upnp_packetlist,0,sizeof(upnp_packetlist)); memset(&upnp_packetlist,0,sizeof(upnp_packetlist));
upnp_add_packet("base,", UPNP_AD_BARE, "/upnp-basic.xml", memset(&upnp_disco,0,sizeof(upnp_disco));
upnp_add_packet("base,", UPNP_AD_UUID, "/upnp-basic.xml",
NULL, NULL, 0, NULL); NULL, NULL, 0, NULL);
upnp_add_packet("base,", UPNP_AD_DEVICE, "/upnp-basic.xml", upnp_add_packet("base,", UPNP_AD_DEVICE, "/upnp-basic.xml",
"urn:schemas-upnp-org","MediaServer", 1, NULL); "urn:schemas-upnp-org","MediaServer", 1, NULL);
@ -345,11 +407,42 @@ int upnp_init(void) {
return TRUE; return TRUE;
} }
/**
* walk the query chain and see if there are any queries we need
* to respond to.
*/
void upnp_process_queries(void) {
UPNP_DISCO *plast, *pcurrent;
plast=&upnp_disco;
pcurrent = upnp_disco.next;
/* don't need locks here since this ist he same thread as the listening
* thread */
while(pcurrent) {
if(pcurrent->seconds_remaining - UPNP_SELECT_TIMEOUT < 1) {
/* do the broadcast, and cull it */
upnp_broadcast(UPNP_TYPE_RESPONSE,pcurrent);
pcurrent = pcurrent->next;
free(plast->next->query);
free(plast->next);
plast->next = pcurrent;
} else {
pcurrent->seconds_remaining -= UPNP_SELECT_TIMEOUT;
plast = pcurrent;
pcurrent = pcurrent->next;
}
}
}
/** /**
* listener thread for upnp query requests * listener thread for upnp query requests
*/ */
void *upnp_listener(void *arg) { void *upnp_listener(void *arg) {
int result; int result;
static time_t last_broadcast=0;
fd_set readset; fd_set readset;
struct timeval timeout; struct timeval timeout;
@ -379,7 +472,18 @@ void *upnp_listener(void *arg) {
upnp_thread_started=0; upnp_thread_started=0;
pthread_exit(NULL); pthread_exit(NULL);
} }
upnp_process_packet();
if(FD_ISSET(upnp_socket,&readset)) {
upnp_process_packet();
}
if((time(NULL) - last_broadcast) > (UPNP_CACHE_DURATION/3)) {
DPRINTF(E_DBG,L_MISC,"Time for alive message!\n");
upnp_broadcast(UPNP_TYPE_ALIVE,NULL);
last_broadcast = time(NULL);
}
upnp_process_queries();
} }
upnp_thread_started=0; upnp_thread_started=0;
@ -464,19 +568,22 @@ void upnp_process_packet(void) {
/* do we care? Let's check the query and see if it is interesting */ /* do we care? Let's check the query and see if it is interesting */
discovery = 0; discovery = 0;
pdisco = (UPNP_DISCO *)malloc(sizeof(UPNP_DISCO)); pdisco = (UPNP_DISCO *)malloc(sizeof(UPNP_DISCO));
if(!pdisco) if(!pdisco)
DPRINTF(E_FATAL,L_MISC,"malloc error"); DPRINTF(E_FATAL,L_MISC,"malloc error");
memset(pdisco,0,sizeof(UPNP_DISCO)); memset(pdisco,0,sizeof(UPNP_DISCO));
if(mx) { if(mx) {
pdisco->seconds_remaining = 2; pdisco->seconds_remaining = (rand() / (RAND_MAX/mx)) + 1;
DPRINTF(E_DBG,L_MISC,"Responding in %d (of %d) seconds\n",
pdisco->seconds_remaining, mx);
} }
pdisco->query = query; pdisco->query = query;
query = NULL; query = NULL;
memcpy((void*)&pdisco->to, (void*)&from, sizeof(struct sockaddr)); memcpy((void*)&pdisco->to, (void*)&from,sizeof(struct sockaddr_in));
pdisco->next = upnp_disco.next;
upnp_disco.next = pdisco;
} }
if(query) { if(query) {
@ -510,6 +617,3 @@ int upnp_deinit(void) {
return TRUE; return TRUE;
} }

View File

@ -31,18 +31,16 @@
#ifndef _UPNP_H_ #ifndef _UPNP_H_
#define _UPNP_H_ #define _UPNP_H_
#define UPNP_UUID "12345678-1234-1234-1234-123456789013"
#define UPNP_AD_DEVICE 1 #define UPNP_AD_DEVICE 1
#define UPNP_AD_SERVICE 2 #define UPNP_AD_SERVICE 2
#define UPNP_AD_ROOT 3 #define UPNP_AD_ROOT 3
#define UPNP_AD_BARE 4 #define UPNP_AD_UUID 4
extern int upnp_init(void); extern int upnp_init(void);
extern int upnp_tick(void);
extern int upnp_deinit(void); extern int upnp_deinit(void);
extern void upnp_add_packet(char *group_id, int type, char *location, extern void upnp_add_packet(char *group_id, int type, char *location,
char *namespace, char *name, int version, char *namespace, char *name, int version,
char *body); char *body);
extern char *upnp_uuid();
#endif /* _UPNP_H_ */ #endif /* _UPNP_H_ */