diff --git a/minidlnatypes.h b/minidlnatypes.h index 7ded991..8bb6783 100644 --- a/minidlnatypes.h +++ b/minidlnatypes.h @@ -68,6 +68,7 @@ enum client_types { EMediaRoom, ESonyBDP, ESonyBravia, + ERokuSoundBridge, EStandardDLNA150 = 100 }; diff --git a/minissdp.c b/minissdp.c index 72cc201..23fb62e 100644 --- a/minissdp.c +++ b/minissdp.c @@ -41,6 +41,8 @@ #include "minidlnapath.h" #include "upnphttp.h" #include "upnpglobalvars.h" +#include "upnpreplyparse.h" +#include "getifaddr.h" #include "minissdp.h" #include "log.h" @@ -332,6 +334,137 @@ SendSSDPNotifies2(int * sockets, } } +void +ParseUPnPClient(char *location) +{ + char buf[8192]; + struct sockaddr_in dest; + int s, n, do_headers = 0, nread = 0; + struct timeval tv; + char *addr, *path, *port_str; + long port = 80; + char *off = NULL, *p; + int content_len = sizeof(buf); + struct NameValueParserData xml; + int client; + enum client_types type = -1; + uint32_t flags = 0; + char *model; + + if (strncmp(location, "http://", 7) != 0) + return; + path = location + 7; + port_str = strsep(&path, "/"); + if (!path) + return; + addr = strsep(&port_str, ":"); + if (port_str) + { + port = strtol(port_str, NULL, 10); + if (!port) + port = 80; + } + + memset(&dest, '\0', sizeof(dest)); + if (!inet_aton(addr, &dest.sin_addr)) + return; + /* Check if the client is already in cache */ + dest.sin_family = AF_INET; + dest.sin_port = htons(port); + + s = socket(PF_INET, SOCK_STREAM, 0); + if( s < 0 ) + return; + + tv.tv_sec = 0; + tv.tv_usec = 500000; + setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); + + if( connect(s, (struct sockaddr*)&dest, sizeof(struct sockaddr_in)) < 0 ) + goto close; + + n = snprintf(buf, sizeof(buf), "GET /%s HTTP/1.0\r\n" + "HOST: %s:%ld\r\n\r\n", + path, addr, port); + if( write(s, buf, n) < 1 ) + goto close; + + while( (n = read(s, buf+nread, sizeof(buf)-nread-1)) > 0 ) + { + nread += n; + buf[nread] = '\0'; + n = nread; + p = buf; + + while( !off && n-- > 0 ) + { + if(p[0]=='\r' && p[1]=='\n' && p[2]=='\r' && p[3]=='\n') + { + off = p + 4; + do_headers = 1; + } + p++; + } + if( !off ) + continue; + + if( do_headers ) + { + p = buf; + if( strncmp(p, "HTTP/", 5) != 0 ) + goto close; + while(*p != ' ' && *p != '\t') p++; + /* If we don't get a 200 status, ignore it */ + if( strtol(p, NULL, 10) != 200 ) + goto close; + if( (p = strcasestr(p, "Content-Length:")) ) + content_len = strtol(p+15, NULL, 10); + do_headers = 0; + } + if( buf + nread - off >= content_len ) + break; + } +close: + close(s); + if( !off ) + return; + nread -= off - buf; + ParseNameValue(off, nread, &xml); + model = GetValueFromNameValueList(&xml, "modelName"); + if( model ) + { + if (strstr(model, "Roku SoundBridge")) + { + type = ERokuSoundBridge; + flags |= FLAG_AUDIO_ONLY; + } + } + + if( type < 0 ) + return; + client = SearchClientCache(dest.sin_addr, 1); + /* Add this client to the cache if it's not there already. */ + if( client < 0 ) + { + for( client=0; client= 0 && clients[i].type < EStandardDLNA150 ) + { + clients[i].age = time(NULL); + return; + } + ParseUPnPClient(loc); + } + return; + } + else if(memcmp(bufr, "M-SEARCH", 8) == 0) + { + //DPRINTF(E_DEBUG, L_SSDP, "Received SSDP request:\n%.*s", n, bufr); + for(i=0; i < n; i++) + { + if( bufr[i] == '*' ) + break; + } + if( !strcasestr(bufr+i, "HTTP/1.1") ) + { + return; + } + while(i < n) + { + while((i < n - 2) && (bufr[i] != '\r' || bufr[i+1] != '\n')) + i++; + i += 2; + if(strncasecmp(bufr+i, "ST:", 3) == 0) { st = bufr+i+3; st_len = 0; diff --git a/upnphttp.c b/upnphttp.c index 10c70fb..e437507 100644 --- a/upnphttp.c +++ b/upnphttp.c @@ -126,7 +126,7 @@ Delete_upnphttp(struct upnphttp * h) } int -SearchClientCache(struct in_addr addr) +SearchClientCache(struct in_addr addr, int quiet) { int i; for( i=0; iclientaddr); + n = SearchClientCache(h->clientaddr, 0); if( h->req_client ) { /* Add this client to the cache if it's not there already. */ @@ -456,7 +458,7 @@ next_header: break; } } - else if( (n < EStandardDLNA150) && (h->req_client == EStandardDLNA150) ) + else if( (clients[n].type < EStandardDLNA150) && (h->req_client == EStandardDLNA150) ) { /* If we know the client and our new detection is generic, use our cached info */ h->reqflags |= clients[n].flags; diff --git a/upnphttp.h b/upnphttp.h index 75436cc..91df730 100644 --- a/upnphttp.h +++ b/upnphttp.h @@ -110,6 +110,7 @@ struct upnphttp { #define FLAG_MIME_FLAC_FLAC 0x00800000 #define FLAG_NO_RESIZE 0x01000000 #define FLAG_MS_PFS 0x02000000 // Microsoft PlaysForSure client +#define FLAG_AUDIO_ONLY 0x04000000 /* New_upnphttp() */ struct upnphttp * @@ -156,6 +157,9 @@ Send501(struct upnphttp *); void SendResp_upnphttp(struct upnphttp *); +int +SearchClientCache(struct in_addr addr, int quiet); + void SendResp_icon(struct upnphttp *, char * url); void