dial spec 2.0

This commit is contained in:
jcli
2015-10-20 16:33:55 -07:00
parent 3454a59e99
commit 2477615a40
15 changed files with 656 additions and 362 deletions

View File

@@ -1,3 +1,9 @@
---------------------------------------------------------------------
Version 1.0.6 (72ab437)
---------------------------------------------------------------------
Server changes:
- Update to DIAL specificiation 2.0
--------------------------------------------------------------------- ---------------------------------------------------------------------
Version 1.0.5 (06edbce) Version 1.0.5 (06edbce)
--------------------------------------------------------------------- ---------------------------------------------------------------------

View File

@@ -40,6 +40,7 @@
#include <vector> #include <vector>
#include "DialDiscovery.h" #include "DialDiscovery.h"
#include <assert.h> #include <assert.h>
#include <ctime>
#define SSDP_TIMEOUT 30 #define SSDP_TIMEOUT 30
#define SSDP_RESPONSE_TIMEOUT 3 #define SSDP_RESPONSE_TIMEOUT 3
@@ -97,6 +98,45 @@ static string getLocation(char *pResponse)
return retUrl; return retUrl;
} }
static string getMac(char *pResponse)
{
string response(pResponse);
string mac("");
size_t index = response.find("WAKEUP:");
if (index==string::npos){
return mac;
}else{
size_t start = response.find("MAC=", index);
if (start==string::npos) return mac;
start+=4;
size_t end = response.find(";", start);
if (end==string::npos) return mac;
mac = response.substr(start, end-start);
return mac;
}
}
static int getTimeout(char *pResponse)
{
string response(pResponse);
int timeOut=0;
string timeOutStr;
size_t index = response.find("WAKEUP:");
if (index==string::npos){
return timeOut;
}else{
size_t start = response.find("Timeout=", index);
if (start==string::npos) return timeOut;
start+=8;
size_t end = response.find("\r\n", start);
if (end==string::npos) return timeOut;
timeOutStr = response.substr(start, end-start);
timeOut = (int)(strtol(timeOutStr.c_str(), NULL, 10));
return timeOut;
}
}
static size_t header_cb(void* ptr, size_t size, size_t nmemb, void* userdata) static size_t header_cb(void* ptr, size_t size, size_t nmemb, void* userdata)
{ {
if ((size * nmemb) != 0) { if ((size * nmemb) != 0) {
@@ -132,7 +172,6 @@ static void getServerInfo( const string &server, string& appsUrl, string& ddxml
{ {
CURL *curl; CURL *curl;
CURLcode res = CURLE_OK; CURLcode res = CURLE_OK;
if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
fprintf(stderr, "curl_global_init() failed\n"); fprintf(stderr, "curl_global_init() failed\n");
return; return;
@@ -146,6 +185,7 @@ static void getServerInfo( const string &server, string& appsUrl, string& ddxml
ATRACE("Sending ##%s##\n", server.c_str()); ATRACE("Sending ##%s##\n", server.c_str());
curl_easy_setopt(curl, CURLOPT_URL, server.c_str()); curl_easy_setopt(curl, CURLOPT_URL, server.c_str());
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 1);
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_cb); curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_cb);
curl_easy_setopt(curl, CURLOPT_HEADERDATA, &appsUrl); curl_easy_setopt(curl, CURLOPT_HEADERDATA, &appsUrl);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, receiveData); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, receiveData);
@@ -157,7 +197,7 @@ static void getServerInfo( const string &server, string& appsUrl, string& ddxml
//curl_global_cleanup(); //curl_global_cleanup();
} }
void DialDiscovery::updateServerList(string& server) void DialDiscovery::updateServerList(string& server, string mac, int timeOut)
{ {
ServerMap::const_iterator it; ServerMap::const_iterator it;
it = mServerMap.find(server); it = mServerMap.find(server);
@@ -165,7 +205,7 @@ void DialDiscovery::updateServerList(string& server)
// not found, add it // not found, add it
string appsUrl, ddxml; string appsUrl, ddxml;
getServerInfo(server, appsUrl, ddxml); getServerInfo(server, appsUrl, ddxml);
mServerMap[server] = new DialServer(server, appsUrl, ddxml); mServerMap[server] = new DialServer(server, appsUrl, ddxml, mac, timeOut);
} else { } else {
// just mark that we found it // just mark that we found it
(*it).second->setFound(true); (*it).second->setFound(true);
@@ -181,9 +221,12 @@ void DialDiscovery::processServer(char *pResponse)
// parse for LOCATION header // parse for LOCATION header
server = getLocation(pResponse); server = getLocation(pResponse);
ATRACE("FOUND server: %s\n", server.c_str()); ATRACE("FOUND server: %s\n", server.c_str());
string mac=getMac(pResponse);
int timeOut=getTimeout(pResponse);
// save the appURL in the server // save the appURL in the server
updateServerList(server); updateServerList(server, mac, timeOut);
#ifndef DEBUG #ifndef DEBUG
} }
#else #else
@@ -196,17 +239,39 @@ void DialDiscovery::processServer(char *pResponse)
void *DialDiscovery::receiveResponses(void *p) void *DialDiscovery::receiveResponses(void *p)
{ {
search_conn *pConn = (search_conn*)p; search_conn *pConn = (search_conn*)p;
fd_set readfds;
struct timeval tv;
int selectRt;
// clear the set ahead of time
FD_ZERO(&readfds);
// add our descriptors to the set
FD_SET(pConn->sock, &readfds);
// wait until either socket has data ready to be recv()d (timeout 1.5 secs)
tv.tv_sec = 1;
tv.tv_usec = 500000;
int bytes; int bytes;
char buf[4096] = {0,}; char buf[4096] = {0,};
while (1) { time_t start=std::time(NULL);
if (-1 == (bytes = recvfrom(pConn->sock, buf, sizeof(buf) - 1, 0, while (std::time(NULL)-start<RESPONSE_TIMEOUT) {
(struct sockaddr *)&pConn->saddr, &pConn->addrlen))) { // printf("beat %ld %ld %ld\n", std::time(NULL), start, std::time(NULL)-start);
perror("recvfrom"); selectRt=select(pConn->sock+1, &readfds, NULL, NULL, &tv);
if (selectRt==-1){
perror("select"); // error occurred in select()
break; break;
}else if (selectRt == 0){
// printf("Timeout occurred! No data after 1.5 seconds.\n");
}else{
if (-1 == (bytes = recvfrom(pConn->sock, buf, sizeof(buf) - 1, 0,
(struct sockaddr *)&pConn->saddr, &pConn->addrlen))) {
perror("recvfrom");
break;
}
buf[bytes] = 0;
ATRACE("Received [%s:%d] %s\n", __FUNCTION__, __LINE__, buf);
DialDiscovery::instance()->processServer(buf);
} }
buf[bytes] = 0;
ATRACE("Received [%s:%d] %s\n", __FUNCTION__, __LINE__, buf);
DialDiscovery::instance()->processServer(buf);
} }
return 0; return 0;
} }
@@ -244,17 +309,17 @@ void DialDiscovery::resetDiscovery(void)
} }
} }
void * DialDiscovery::send_mcast(void *p) void *DialDiscovery::send_mcast()
{ {
int one = 1, my_sock; int one = 1, my_sock;
socklen_t addrlen; socklen_t addrlen;
//struct ip_mreq mreq; //struct ip_mreq mreq;
char send_buf[strlen((char*)ssdp_msearch) + INET_ADDRSTRLEN + 256] = {0,}; char send_buf[strlen((char*)ssdp_msearch) + INET_ADDRSTRLEN + 256] = {0,};
int send_size; int send_size;
pthread_attr_t attr;
search_conn connection; search_conn connection;
// send_size = snprintf(send_buf, sizeof(send_buf), ssdp_msearch, ip_addr, my_port); printf("Sending mcast for discovery. Please wait for %d seconds for the response.\n", RESPONSE_TIMEOUT);
send_size = snprintf(send_buf, sizeof(send_buf), ssdp_msearch); send_size = snprintf(send_buf, sizeof(send_buf), ssdp_msearch);
ATRACE("[%s:%d] %s\n", __FUNCTION__, __LINE__, send_buf); ATRACE("[%s:%d] %s\n", __FUNCTION__, __LINE__, send_buf);
@@ -270,45 +335,38 @@ void * DialDiscovery::send_mcast(void *p)
saddr.sin_addr.s_addr = inet_addr("239.255.255.250"); saddr.sin_addr.s_addr = inet_addr("239.255.255.250");
saddr.sin_port = htons(1900); saddr.sin_port = htons(1900);
while (1) { addrlen = sizeof(saddr);
addrlen = sizeof(saddr); ATRACE("Sending SSDP M-SEARCH to %s:%d\n",
ATRACE("Sending SSDP M-SEARCH to %s:%d\n", inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); if (-1 == sendto(my_sock, send_buf, send_size, 0, (struct sockaddr *)&saddr, addrlen)) {
if (-1 == sendto(my_sock, send_buf, send_size, 0, (struct sockaddr *)&saddr, addrlen)) { perror("sendto");
perror("sendto"); return 0;
continue;
}
// set all servers to not found
DialDiscovery::instance()->resetDiscovery();
// spawn a response thread
connection.saddr = saddr;
connection.sock = my_sock;
connection.addrlen = addrlen;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_create(&DialDiscovery::instance()->_responseThread, &attr, DialDiscovery::receiveResponses, &connection);
// sleep SSDP_RESPONSE_TIMEOUT seconds to allow clients to response
sleep(SSDP_RESPONSE_TIMEOUT);
DialDiscovery::instance()->cleanServerList();
sleep(SSDP_TIMEOUT-SSDP_RESPONSE_TIMEOUT);
pthread_cancel(DialDiscovery::instance()->_responseThread);
} }
// set all servers to not found
DialDiscovery::instance()->resetDiscovery();
connection.saddr = saddr;
connection.sock = my_sock;
connection.addrlen = addrlen;
receiveResponses(&connection);
// // sleep SSDP_RESPONSE_TIMEOUT seconds to allow clients to response
// sleep(SSDP_RESPONSE_TIMEOUT);
DialDiscovery::instance()->cleanServerList();
return 0;
} }
void DialDiscovery::init() void DialDiscovery::init()
{ {
pthread_attr_t attr; send_mcast();
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_create(&_mcastThread, &attr, DialDiscovery::send_mcast, (void*)ssdp_msearch );
} }
void DialDiscovery::getServerList( vector<DialServer*>& list ) void DialDiscovery::getServerList( vector<DialServer*>& list )
{ {
list.clear();
for( ServerMap::iterator it = mServerMap.begin(); it != mServerMap.end(); ++it ) { for( ServerMap::iterator it = mServerMap.begin(); it != mServerMap.end(); ++it ) {
list.push_back( it->second ); list.push_back( it->second );
} }

View File

@@ -73,11 +73,11 @@ public:
const string& friendlyName, const string& friendlyName,
DialServer &server ); DialServer &server );
void *send_mcast();
private: private:
DialDiscovery(); DialDiscovery();
void updateServerList(string& server); void updateServerList(string& server, string mac, int timeOut);
static void *receiveResponses(void *p); static void *receiveResponses(void *p);
static void *send_mcast(void *p);
void processServer(char *pResponse); void processServer(char *pResponse);
void cleanServerList(); void cleanServerList();
void resetDiscovery(); void resetDiscovery();
@@ -89,6 +89,7 @@ private:
ServerMap mServerMap; ServerMap mServerMap;
static DialDiscovery* sDiscovery; static DialDiscovery* sDiscovery;
static const int RESPONSE_TIMEOUT = 5;
}; };
#endif // DIALDISCOVERY_H #endif // DIALDISCOVERY_H

View File

@@ -185,6 +185,16 @@ bool DialServer::getUuid( string& uuid )
return retval; return retval;
} }
string DialServer::getMacAddress()
{
return m_macAddr;
}
int DialServer::getWakeOnLanTimeout()
{
return m_wakeUpTimeout;
}
int DialServer::launchApplication( int DialServer::launchApplication(
string &application, string &application,
string &payload, string &payload,
@@ -193,8 +203,8 @@ int DialServer::launchApplication(
{ {
ATRACE("%s: Launch %s\n", __FUNCTION__, application.c_str()); ATRACE("%s: Launch %s\n", __FUNCTION__, application.c_str());
string appUrl = m_appsUrl; string appUrl = m_appsUrl;
sendCommand( appUrl.append(application), COMMAND_LAUNCH, payload, responseHeaders, responseBody); int status = sendCommand( appUrl.append(application), COMMAND_LAUNCH, payload, responseHeaders, responseBody);
return 0; return status;
} }
int DialServer::getStatus( int DialServer::getStatus(
@@ -205,8 +215,9 @@ int DialServer::getStatus(
ATRACE("%s: GetStatus %s\n", __FUNCTION__, application.c_str()); ATRACE("%s: GetStatus %s\n", __FUNCTION__, application.c_str());
string emptyPayload; string emptyPayload;
string appUrl = m_appsUrl; string appUrl = m_appsUrl;
sendCommand( appUrl.append(application), COMMAND_STATUS, emptyPayload, responseHeaders, responseBody ); int status = sendCommand( appUrl.append(application), COMMAND_STATUS, emptyPayload, responseHeaders, responseBody );
if (!status) return 0;
ATRACE("Body: %s\n", responseBody.c_str()); ATRACE("Body: %s\n", responseBody.c_str());
unsigned found = responseBody.find("href="); unsigned found = responseBody.find("href=");
if( found != string::npos ) if( found != string::npos )
@@ -234,10 +245,10 @@ int DialServer::stopApplication(
// just call status to update the run endpoint // just call status to update the run endpoint
getStatus( application, responseHeaders, responseBody ); getStatus( application, responseHeaders, responseBody );
sendCommand( int status = sendCommand(
(appUrl.append(application)).append("/"+m_stopEndPoint), (appUrl.append(application)).append("/"+m_stopEndPoint),
COMMAND_KILL, emptyPayload, responseHeaders, responseBody ); COMMAND_KILL, emptyPayload, responseHeaders, responseBody );
return 0; return status;
} }
int DialServer::getHttpResponseHeader( int DialServer::getHttpResponseHeader(

View File

@@ -50,11 +50,13 @@ public:
* empty string to find any server * empty string to find any server
* *
*/ */
DialServer( string location, string appsUrl, string dd_xml ) : DialServer( string location, string appsUrl, string dd_xml, string macAddr, int wakeUpTimeout) :
m_location(location), m_location(location),
m_appsUrl(appsUrl), m_appsUrl(appsUrl),
found(true), found(true),
m_ddxml(dd_xml) m_ddxml(dd_xml),
m_macAddr(macAddr),
m_wakeUpTimeout(wakeUpTimeout)
{} {}
~DialServer() { ATRACE("%s\n", __FUNCTION__); } ~DialServer() { ATRACE("%s\n", __FUNCTION__); }
@@ -94,6 +96,20 @@ public:
*/ */
bool getUuid( string& uuid ); bool getUuid( string& uuid );
/**
* Get the dial server mac address
*
* @return mac address if avaliable, '' if not.
*/
string getMacAddress();
/**
* Get the wake on lan timeout in seconds
*
* @return timeout value if avaliable, zero if not.
*/
int getWakeOnLanTimeout();
/** /**
* Launch a DIAL application * Launch a DIAL application
* *
@@ -176,6 +192,9 @@ private:
bool found; bool found;
string m_ddxml; // information about the device string m_ddxml; // information about the device
string m_stopEndPoint; string m_stopEndPoint;
string m_macAddr;
int m_wakeUpTimeout;
}; };
#endif // DIALSERVER_H #endif // DIALSERVER_H

View File

@@ -31,6 +31,11 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
using namespace std; using namespace std;
@@ -51,12 +56,15 @@ static void printServerList( vector<DialServer*> list )
vector<DialServer*>::iterator it; vector<DialServer*>::iterator it;
for( i = 0, it = list.begin(); it < list.end(); it++, i++ ) for( i = 0, it = list.begin(); it < list.end(); it++, i++ )
{ {
string uuid, name; string uuid, name, macAddr;
int wolTimeOut;
(*it)->getFriendlyName( name ); (*it)->getFriendlyName( name );
(*it)->getUuid( uuid ); (*it)->getUuid( uuid );
printf("%Zu: Server IP[%s] UUID[%s] FriendlyName[%s] \n", macAddr =(*it)->getMacAddress();
i+1, (*it)->getIpAddress().c_str(), wolTimeOut =(*it)->getWakeOnLanTimeout();
uuid.c_str(), name.c_str() ); printf("%Zu: Server IP[%s] UUID[%s] FriendlyName[%s] MacAddress[%s] WakeOnLanTimeout[%d]\n",
i+1, (*it)->getIpAddress().c_str(),
uuid.c_str(), name.c_str(), macAddr.c_str(), wolTimeOut);
} }
} }
@@ -64,22 +72,19 @@ static DialServer* getServerFromUser( vector<DialServer*> list )
{ {
DialServer* pServer; DialServer* pServer;
// show a list to the user // show a list to the user
if( list.size() > 1 ) char buf[80] = {0,};
{ vector<DialServer*>::iterator it;
char buf[80] = {0,};
vector<DialServer*>::iterator it;
printf("Found Multiple servers\n"); printf("Found Multiple servers\n");
printServerList(list); printf("0: Rescan and list DIAL servers\n");
printf("Enter server: "); printServerList(list);
scanf("%s", buf); printf("Enter server: ");
unsigned int server = atoi(buf); scanf("%s", buf);
assert( server > 0 && server <= list.size() ); unsigned int server = atoi(buf);
if( server > 0 && server <= list.size()){
pServer = list[server-1]; pServer = list[server-1];
} }else{
else pServer = NULL;
{
pServer = list.front();
} }
return pServer; return pServer;
} }
@@ -141,85 +146,140 @@ static void runConformance()
} }
} }
static void sendMagic(string macAddr)
{
unsigned char tosend[102];
unsigned char mac[6];
/** first 6 bytes of 255 **/
for(int i = 0; i < 6; i++) {
tosend[i] = 0xFF;
}
/** store mac address **/
printf("sending magic packet to: ");
for (int i=0; i<6; i++){
mac[i]=(unsigned char)(strtoul(macAddr.substr(i*3, 2).c_str(), NULL, 16));
printf("%02x:", mac[i]);
}
printf("\n");
/** append it 16 times to packet **/
for(int i = 1; i <= 16; i++) {
memcpy(&tosend[i * 6], &mac, 6 * sizeof(unsigned char));
}
int udpSocket;
struct sockaddr_in udpClient, udpServer;
int broadcast = 1;
udpSocket = socket(AF_INET, SOCK_DGRAM, 0);
/** you need to set this so you can broadcast **/
if (setsockopt(udpSocket, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof broadcast) == -1) {
perror("setsockopt (SO_BROADCAST)");
exit(1);
}
udpClient.sin_family = AF_INET;
udpClient.sin_addr.s_addr = INADDR_ANY;
udpClient.sin_port = 0;
bind(udpSocket, (struct sockaddr*)&udpClient, sizeof(udpClient));
/** set server end point (the broadcast addres)**/
udpServer.sin_family = AF_INET;
udpServer.sin_addr.s_addr = inet_addr("255.255.255.255");
udpServer.sin_port = htons(9);
/** send the packet **/
sendto(udpSocket, &tosend, sizeof(unsigned char) * 102, 0, (struct sockaddr*)&udpServer, sizeof(udpServer));
}
int handleUser(DialDiscovery *pDial) { int handleUser(DialDiscovery *pDial) {
int processInput = 1; int processInputOuter = 1;
char buf[80]; char buf[80];
vector<DialServer*> list; vector<DialServer*> list;
pDial->getServerList(list); while (processInputOuter){
if( list.size() == 0 ) pDial->getServerList(list);
{ if( list.size() == 0 ){
printf("No servers available\n"); printf("No servers available\n");
return 1; return 1;
}
DialServer* pServer = getServerFromUser( list );
while(processInput)
{
string responseHeaders, responseBody, payload;
string netflix = "Netflix";
string youtube = "YouTube";
memset(buf, 0, 80);
printf("0. List DIAL servers\n");
printf("1. Launch Netflix\n");
printf("2. Kill Netflix\n");
printf("3. Netflix status\n");
printf("4. Launch YouTube\n");
printf("5. Kill YouTube\n");
printf("6. YouTube status\n");
printf("7. Run conformance tests\n");
printf("8. QUIT\n");
printf("Command (0:1:2:3:4:5:6:7:8): ");
scanf("%s", buf);
switch( atoi(buf) )
{
case 0:
{
printf("\n\n******** %Zu servers found ********\n\n", list.size());
for( unsigned int i = 0; i < list.size(); i++ )
{
string name;
list[i]->getFriendlyName(name);
printf("Server %Zu: %s\n", i+1, name.c_str());
}
printf("\n*********************************\n\n");
}break;
case 1:
printf("Launch Netflix\n");
pServer->launchApplication( netflix, payload, responseHeaders, responseBody );
break;
case 2:
printf("Kill Netflix\n");
pServer->stopApplication( netflix, responseHeaders );
break;
case 3:
printf("Netflix Status: \n");
pServer->getStatus( netflix, responseHeaders, responseBody );
printf("RESPONSE: \n%s\n", responseBody.c_str());
break;
case 4:
printf("Launch YouTube\n");
pServer->launchApplication( youtube, payload, responseHeaders, responseBody );
break;
case 5:
printf("Kill YouTube\n");
pServer->stopApplication( youtube, responseHeaders );
break;
case 6:
printf("YouTube Status: \n");
pServer->getStatus( youtube, responseHeaders, responseBody );
break;
case 7:
runConformance();
break;
case 8:
processInput = 0;
break;
default:
printf("Invalid, try again\n");
} }
} DialServer* pServer = getServerFromUser( list );
if (pServer==NULL){
pDial->send_mcast();
continue;
}
int processInput = 1;
while(processInput){
string responseHeaders, responseBody, payload;
string netflix = "Netflix";
string youtube = "YouTube";
memset(buf, 0, 80);
printf("0. Rescan and list DIAL servers\n");
printf("1. Launch Netflix\n");
printf("2. Kill Netflix\n");
printf("3. Netflix status\n");
printf("4. Launch YouTube\n");
printf("5. Kill YouTube\n");
printf("6. YouTube status\n");
printf("7. Run conformance tests\n");
printf("8. Wake up on lan/wlan\n");
printf("9. QUIT\n");
printf("Command (0:1:2:3:4:5:6:7:8:9): ");
scanf("%s", buf);
switch( atoi(buf) )
{
case 0:
{
pDial->send_mcast();
processInput=0;
}break;
case 1:
printf("Launch Netflix\n");
pServer->launchApplication( netflix, payload, responseHeaders, responseBody );
break;
case 2:
printf("Kill Netflix\n");
pServer->stopApplication( netflix, responseHeaders );
break;
case 3:
printf("Netflix Status: \n");
pServer->getStatus( netflix, responseHeaders, responseBody );
printf("RESPONSE: \n%s\n", responseBody.c_str());
break;
case 4:
printf("Launch YouTube\n");
pServer->launchApplication( youtube, payload, responseHeaders, responseBody );
break;
case 5:
printf("Kill YouTube\n");
pServer->stopApplication( youtube, responseHeaders );
break;
case 6:
printf("YouTube Status: \n");
pServer->getStatus( youtube, responseHeaders, responseBody );
break;
case 7:
runConformance();
break;
case 8:
printf("Sending the magic packet\n");
sendMagic(pServer->getMacAddress());
break;
case 9:
processInput = 0;
processInputOuter = 0;
break;
default:
printf("Invalid, try again\n");
}
}
}
return 0; return 0;
} }

79
server/LinuxInterfaces.c Normal file
View File

@@ -0,0 +1,79 @@
#include "LinuxInterfaces.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
/* Fetch the MACAddress of a network interface */
int getMACAddress(char* ifname, char* macBuf, unsigned int macBufSize)
{
struct ifreq ifr;
int sock;
unsigned char chMAC[6];
sock=socket(AF_INET,SOCK_DGRAM,0);
strcpy( ifr.ifr_name, ifname );
ifr.ifr_addr.sa_family = AF_INET;
if (ioctl( sock, SIOCGIFHWADDR, &ifr ) < 0) {
close(sock);
return -1;
}
memcpy(chMAC, ifr.ifr_hwaddr.sa_data, 6);
if (macBufSize<32){
close(sock);
return -1;
}else{
sprintf(macBuf,"%02X:%02X:%02X:%02X:%02X:%02X",chMAC[0],chMAC[1],chMAC[2],chMAC[3],chMAC[4],chMAC[5]);
}
close(sock);
return 0;
}
bool hasRoute(const char *nf, int flag)
{
bool result = false;
const char* fileName = "/proc/net/route";
int r;
FILE* fp = fopen(fileName, "r");
if (fp) {
char line[1024];
int nfBegin = 0, nfEnd = 0, nfFlag = 0;
const int nfLen = strlen(nf);
while (!feof(fp)) {
if (!fgets(line, sizeof(line), fp))
break;
r = sscanf(line, "%n%*s%n %*s %*s %d %*s %*s %*s %*s %*s %*s %*s", &nfBegin, &nfEnd, &nfFlag);
if (r == 1 && nfBegin == 0 && nfEnd == nfLen && !strncmp(nf, line, nfLen) && (flag == -1 || nfFlag == flag)) {
result = true;
break;
}
}
fclose(fp);
}
return result;
}
bool isDefault(char *nf)
{
return hasRoute(nf, 0x03);
}
NetInterface getDefaultNetworkInterfaces()
{
DIR *d;
struct dirent *dir;
d = opendir("/sys/class/net/");
NetInterface defaultNet;
if (d){
while ((dir = readdir(d)) != NULL){
if(strcmp(dir->d_name,".") && strcmp(dir->d_name,"..")){
if (hasRoute(dir->d_name, -1) && isDefault(dir->d_name)){
getMACAddress(dir->d_name, defaultNet.macAddress, MACBUFSIZE);
break;
}
}
}
closedir(d);
}
return defaultNet;
}

26
server/LinuxInterfaces.h Normal file
View File

@@ -0,0 +1,26 @@
#ifndef LINUXINTERFACES_H_
#define LINUXINTERFACES_H_
#include <arpa/inet.h>
#include <dirent.h>
#include <linux/limits.h>
#include <linux/wireless.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
/* Fetch the MACAddress of a network interface */
#define MACBUFSIZE 32
#define WAKEUPTIMEOUT 15
typedef struct
{
char macAddress[MACBUFSIZE];
}NetInterface;
NetInterface getDefaultNetworkInterfaces();
#endif

View File

@@ -1,2 +1,2 @@
export TARGET=/usr/local/i686-netflix-linux-gnu-4.2/bin/i686-netflix-linux-gnu- export TARGET=/usr/local/i686-netflix-linux-gnu-4.3/bin/i686-netflix-linux-gnu-
make make

View File

@@ -46,6 +46,10 @@
#define UUID_OPTION_LONG "--uuid-name" #define UUID_OPTION_LONG "--uuid-name"
#define UUID_DESCRIPTION "UUID of the device" #define UUID_DESCRIPTION "UUID of the device"
#define WAKE_OPTION "-W"
#define WAKE_OPTION_LONG "--wake-on-wifi-len"
#define WAKE_DESCRIPTION "Enable wake on wifi/len. Value: on/off. Default (on)"
struct dial_options struct dial_options
{ {
const char * pOption; const char * pOption;
@@ -79,6 +83,11 @@ struct dial_options gDialOptions[] =
UUID_OPTION, UUID_OPTION,
UUID_OPTION_LONG, UUID_OPTION_LONG,
UUID_DESCRIPTION UUID_DESCRIPTION
},
{
WAKE_OPTION,
WAKE_OPTION_LONG,
WAKE_DESCRIPTION
} }
}; };

View File

@@ -37,6 +37,7 @@
#include "mongoose.h" #include "mongoose.h"
#include "url_lib.h" #include "url_lib.h"
#include "LinuxInterfaces.h"
// TODO: Partners should define this port // TODO: Partners should define this port
#define DIAL_PORT (56789) #define DIAL_PORT (56789)
@@ -213,6 +214,7 @@ static void handle_app_status(struct mg_connection *conn,
free(encoded_key); free(encoded_key);
free(encoded_value); free(encoded_value);
} }
app->state = app->callbacks.status_cb(ds, app_name, app->run_id, &canStop, app->state = app->callbacks.status_cb(ds, app_name, app->run_id, &canStop,
app->callback_data); app->callback_data);
mg_printf( mg_printf(
@@ -231,7 +233,7 @@ static void handle_app_status(struct mg_connection *conn,
"%s" "%s"
"\n </additionalData>\n" "\n </additionalData>\n"
"</service>\r\n", "</service>\r\n",
origin_header, origin_header,
DIAL_VERSION, DIAL_VERSION,
app->name, app->name,
canStop ? "true" : "false", canStop ? "true" : "false",
@@ -510,7 +512,6 @@ static void *request_handler(enum mg_event event, struct mg_connection *conn,
free(app_name); free(app_name);
return ret; return ret;
} }
int use_payload = int use_payload =
strcmp(request_info->request_method, "POST") ? 0 : 1; strcmp(request_info->request_method, "POST") ? 0 : 1;
handle_dial_data(conn, request_info, app_name, origin_header, handle_dial_data(conn, request_info, app_name, origin_header,

View File

@@ -39,7 +39,7 @@ typedef enum {
/* /*
* DIAL version that is reported via in the status response. * DIAL version that is reported via in the status response.
*/ */
#define DIAL_VERSION ("\"1.7\"") #define DIAL_VERSION ("\"2.0\"")
/* /*
* The maximum DIAL payload accepted per the DIAL 1.6.1 specification. * The maximum DIAL payload accepted per the DIAL 1.6.1 specification.

View File

@@ -38,6 +38,7 @@
#include "dial_server.h" #include "dial_server.h"
#include "dial_options.h" #include "dial_options.h"
#include <signal.h> #include <signal.h>
#include <stdbool.h>
#define BUFSIZE 256 #define BUFSIZE 256
@@ -54,6 +55,7 @@ static char spNetflix[BUFSIZE];
static char spFriendlyName[BUFSIZE]; static char spFriendlyName[BUFSIZE];
static char spModelName[BUFSIZE]; static char spModelName[BUFSIZE];
static char spUuid[BUFSIZE]; static char spUuid[BUFSIZE];
extern bool wakeOnWifiLan;
static int gDialPort; static int gDialPort;
static char *spAppYouTube = "chrome"; static char *spAppYouTube = "chrome";
@@ -393,26 +395,37 @@ static void processOption( int index, char * pOption )
{ {
switch(index) switch(index)
{ {
case 0: // Data path case 0: // Data path
memset( spDataDir, 0, sizeof(spDataDir) ); memset( spDataDir, 0, sizeof(spDataDir) );
setDataDir( pOption ); setDataDir( pOption );
break; break;
case 1: // Netflix path case 1: // Netflix path
setValue( pOption, spNetflix ); setValue( pOption, spNetflix );
break; break;
case 2: // Friendly name case 2: // Friendly name
setValue( pOption, spFriendlyName ); setValue( pOption, spFriendlyName );
break; break;
case 3: // Model Name case 3: // Model Name
setValue( pOption, spModelName ); setValue( pOption, spModelName );
break; break;
case 4: // UUID case 4: // UUID
setValue( pOption, spUuid ); setValue( pOption, spUuid );
break; break;
default: case 5:
// Should not get here if (strcmp(pOption, "on")==0){
fprintf( stderr, "Option %d not valid\n", index); wakeOnWifiLan=true;
}else if (strcmp(pOption, "off")==0){
wakeOnWifiLan=false;
}else{
fprintf(stderr, "Option %s is not valid for %s",
pOption, WAKE_OPTION_LONG);
exit(1); exit(1);
}
break;
default:
// Should not get here
fprintf( stderr, "Option %d not valid\n", index);
exit(1);
} }
} }

View File

@@ -3,7 +3,7 @@ CC=$(TARGET)gcc
.PHONY: clean .PHONY: clean
.DEFAULT_GOAL=all .DEFAULT_GOAL=all
OBJS := main.o dial_server.o mongoose.o quick_ssdp.o url_lib.o dial_data.o OBJS := main.o dial_server.o mongoose.o quick_ssdp.o url_lib.o dial_data.o LinuxInterfaces.o
HEADERS := $(wildcard *.h) HEADERS := $(wildcard *.h)
%.c: $(HEADERS) %.c: $(HEADERS)

View File

@@ -1,27 +1,27 @@
/* /*
* Copyright (c) 2014 Netflix, Inc. * Copyright (c) 2014 Netflix, Inc.
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* Redistributions of source code must retain the above copyright notice, this * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* THIS SOFTWARE IS PROVIDED BY NETFLIX, INC. AND CONTRIBUTORS "AS IS" AND ANY * THIS SOFTWARE IS PROVIDED BY NETFLIX, INC. AND CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NETFLIX OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL NETFLIX OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include <arpa/inet.h> #include <arpa/inet.h>
#include <net/if.h> #include <net/if.h>
@@ -32,54 +32,54 @@
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <unistd.h> #include <unistd.h>
#include "mongoose.h" #include "mongoose.h"
#include <stdbool.h>
// TODO: Partners should define this port // TODO: Partners should define this port
#define SSDP_PORT (56790) #define SSDP_PORT (56790)
static int exit_ssdp = 0;
void stop_ssdp()
{
exit_ssdp = 1;
};
static char gBuf[4096]; static char gBuf[4096];
// TODO: Partners should get the friendlyName from the system and insert here. // TODO: Partners should get the friendlyName from the system and insert here.
// TODO: Partners should get the UUID from the system and insert here. // TODO: Partners should get the UUID from the system and insert here.
static const char ddxml[] = "" static const char ddxml[] = ""
"<?xml version=\"1.0\"?>" "<?xml version=\"1.0\"?>"
"<root" "<root"
" xmlns=\"urn:schemas-upnp-org:device-1-0\"" " xmlns=\"urn:schemas-upnp-org:device-1-0\""
" xmlns:r=\"urn:restful-tv-org:schemas:upnp-dd\">" " xmlns:r=\"urn:restful-tv-org:schemas:upnp-dd\">"
" <specVersion>" " <specVersion>"
" <major>1</major>" " <major>1</major>"
" <minor>0</minor>" " <minor>0</minor>"
" </specVersion>" " </specVersion>"
" <device>" " <device>"
" <deviceType>urn:schemas-upnp-org:device:tvdevice:1</deviceType>" " <deviceType>urn:schemas-upnp-org:device:tvdevice:1</deviceType>"
" <friendlyName>%s</friendlyName>" " <friendlyName>%s</friendlyName>"
" <manufacturer> </manufacturer>" " <manufacturer> </manufacturer>"
" <modelName>%s</modelName>" " <modelName>%s</modelName>"
" <UDN>uuid:%s</UDN>" " <UDN>uuid:%s</UDN>"
" </device>" " </device>"
"</root>"; "</root>";
// TODO: Partners should use appropriate timeout (in seconds) if hardware supports WoL.
static const short wakeup_timeout = 10;
// TODO: Partners should get the UUID from the system and insert here. // TODO: Partners should get the UUID from the system and insert here.
static const char ssdp_reply[] = "HTTP/1.1 200 OK\r\n" static const char ssdp_reply[] = "HTTP/1.1 200 OK\r\n"
"LOCATION: http://%s:%d/dd.xml\r\n" "LOCATION: http://%s:%d/dd.xml\r\n"
"CACHE-CONTROL: max-age=1800\r\n" "CACHE-CONTROL: max-age=1800\r\n"
"EXT:\r\n" "EXT:\r\n"
"BOOTID.UPNP.ORG: 1\r\n" "BOOTID.UPNP.ORG: 1\r\n"
"SERVER: Linux/2.6 UPnP/1.0 quick_ssdp/1.0\r\n" "SERVER: Linux/2.6 UPnP/1.0 quick_ssdp/1.0\r\n"
"ST: urn:dial-multiscreen-org:service:dial:1\r\n" "ST: urn:dial-multiscreen-org:service:dial:1\r\n"
"USN: uuid:%s::" "USN: uuid:%s::"
"urn:dial-multiscreen-org:service:dial:1\r\n\r\n"; "urn:dial-multiscreen-org:service:dial:1\r\n"
"%s"
"\r\n";
static const char wakeup_header[] = "WAKEUP: MAC=%s;Timeout=%d\r\n";
#define STR_TIMEOUTLEN 6 /* Longest is 32767 */
#define HW_ADDRSTRLEN 18
static char ip_addr[INET_ADDRSTRLEN] = "127.0.0.1"; static char ip_addr[INET_ADDRSTRLEN] = "127.0.0.1";
static char hw_addr[HW_ADDRSTRLEN] = "00:00:00:00:00:00";
static int dial_port = 0; static int dial_port = 0;
static int my_port = 0; static int my_port = 0;
static char friendly_name[256]; static char friendly_name[256];
@@ -87,156 +87,167 @@ static char uuid[256];
static char model_name[256]; static char model_name[256];
static struct mg_context *ctx; static struct mg_context *ctx;
bool wakeOnWifiLan=true;
static void *request_handler(enum mg_event event, static void *request_handler(enum mg_event event,
struct mg_connection *conn, struct mg_connection *conn,
const struct mg_request_info *request_info) { const struct mg_request_info *request_info) {
if (event == MG_NEW_REQUEST) { if (event == MG_NEW_REQUEST) {
if (!strcmp(request_info->uri, "/dd.xml") && if (!strcmp(request_info->uri, "/dd.xml") &&
!strcmp(request_info->request_method, "GET")) { !strcmp(request_info->request_method, "GET")) {
mg_printf(conn, "HTTP/1.1 200 OK\r\n" mg_printf(conn, "HTTP/1.1 200 OK\r\n"
"Content-Type: application/xml\r\n" "Content-Type: application/xml\r\n"
"Application-URL: http://%s:%d/apps/\r\n" "Application-URL: http://%s:%d/apps/\r\n"
"\r\n", ip_addr, dial_port); "\r\n", ip_addr, dial_port);
mg_printf(conn, ddxml, friendly_name, model_name, uuid); mg_printf(conn, ddxml, friendly_name, model_name, uuid);
} else { } else {
mg_send_http_error(conn, 404, "Not Found", "Not Found"); mg_send_http_error(conn, 404, "Not Found", "Not Found");
}
return "done";
} }
return "done"; return NULL;
}
return NULL;
} }
static void get_local_address() { static void get_local_address() {
struct ifconf ifc; struct ifconf ifc;
char buf[4096]; char buf[4096];
int s, i; int s, i;
if (-1 == (s = socket(AF_INET, SOCK_DGRAM, 0))) { if (-1 == (s = socket(AF_INET, SOCK_DGRAM, 0))) {
perror("socket"); perror("socket");
exit(1); exit(1);
}
ifc.ifc_len = sizeof(buf);
ifc.ifc_buf = buf;
if (0 > ioctl(s, SIOCGIFCONF, &ifc)) {
perror("SIOCGIFCONF");
exit(1);
}
if (ifc.ifc_len == sizeof(buf)) {
fprintf(stderr, "SIOCGIFCONF output too long");
exit(1);
}
close(s);
for (i = 0; i < ifc.ifc_len/sizeof(ifc.ifc_req[0]); i++) {
strcpy(ip_addr,
inet_ntoa(((struct sockaddr_in *)(&ifc.ifc_req[i].ifr_addr))->sin_addr));
// exit if we found a non-loopback address
if (strcmp("127.0.0.1", ip_addr)) {
break;
} }
} ifc.ifc_len = sizeof(buf);
ifc.ifc_buf = buf;
if (0 > ioctl(s, SIOCGIFCONF, &ifc)) {
perror("SIOCGIFCONF");
exit(1);
}
if (ifc.ifc_len == sizeof(buf)) {
fprintf(stderr, "SIOCGIFCONF output too long");
exit(1);
}
for (i = 0; i < ifc.ifc_len/sizeof(ifc.ifc_req[0]); i++) {
strcpy(ip_addr,
inet_ntoa(((struct sockaddr_in *)(&ifc.ifc_req[i].ifr_addr))->sin_addr));
if (0 > ioctl(s, SIOCGIFFLAGS, &ifc.ifc_req[i])) {
perror("SIOCGIFFLAGS");
exit(1);
}
if (ifc.ifc_req[i].ifr_flags & IFF_LOOPBACK) {
// don't use loopback interfaces
continue;
}
if (0 > ioctl(s, SIOCGIFHWADDR, &ifc.ifc_req[i])) {
perror("SIOCGIFHWADDR");
exit(1);
}
sprintf(hw_addr, "%02x:%02x:%02x:%02x:%02x:%02x",
(unsigned char)ifc.ifc_req[i].ifr_hwaddr.sa_data[0],
(unsigned char)ifc.ifc_req[i].ifr_hwaddr.sa_data[1],
(unsigned char)ifc.ifc_req[i].ifr_hwaddr.sa_data[2],
(unsigned char)ifc.ifc_req[i].ifr_hwaddr.sa_data[3],
(unsigned char)ifc.ifc_req[i].ifr_hwaddr.sa_data[4],
(unsigned char)ifc.ifc_req[i].ifr_hwaddr.sa_data[5]);
break;
}
close(s);
} }
static void handle_mcast() { static void handle_mcast() {
int s, one = 1, bytes; int s, one = 1, bytes;
socklen_t addrlen; socklen_t addrlen;
struct sockaddr_in saddr; struct sockaddr_in saddr;
struct ip_mreq mreq; struct ip_mreq mreq;
char send_buf[sizeof(ssdp_reply) + INET_ADDRSTRLEN + 256 + 256] = {0,}; char wakeup_buf[sizeof(wakeup_header) + HW_ADDRSTRLEN + STR_TIMEOUTLEN] = {0, };
int send_size; char send_buf[sizeof(ssdp_reply) + INET_ADDRSTRLEN + 256 + 256 + sizeof(wakeup_buf)] = {0,};
int send_size;
send_size = snprintf(send_buf, sizeof(send_buf), ssdp_reply, ip_addr, my_port, uuid); if (-1 < wakeup_timeout && wakeOnWifiLan) {
snprintf(wakeup_buf, sizeof(wakeup_buf), wakeup_header, hw_addr, wakeup_timeout);
if (-1 == (s = socket(AF_INET, SOCK_DGRAM, 0))) {
perror("socket");
exit(1);
}
if (-1 == setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) {
perror("reuseaddr");
exit(1);
}
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = inet_addr("239.255.255.250");
saddr.sin_port = htons(1900);
if (-1 == bind(s, (struct sockaddr *)&saddr, sizeof(saddr))) {
perror("bind");
exit(1);
}
mreq.imr_multiaddr.s_addr = inet_addr("239.255.255.250");
mreq.imr_interface.s_addr = inet_addr(ip_addr);
if (-1 == setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,
&mreq, sizeof(mreq))) {
perror("add_membership");
exit(1);
}
//printf("Starting Multicast handling on 239.255.255.250\n");
while (!exit_ssdp) {
addrlen = sizeof(saddr);
if (-1 == (bytes = recvfrom(s, gBuf, sizeof(gBuf) - 1, 0,
(struct sockaddr *)&saddr, &addrlen))) {
perror("recvfrom");
continue;
} }
gBuf[bytes] = 0; send_size = snprintf(send_buf, sizeof(send_buf), ssdp_reply, ip_addr, my_port, uuid, wakeup_buf);
if (-1 == (s = socket(AF_INET, SOCK_DGRAM, 0))) {
// sophisticated SSDP parsing algorithm perror("socket");
if (!strstr(gBuf, "urn:dial-multiscreen-org:service:dial:1")) exit(1);
{ }
#if 0 // use for debugging if (-1 == setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) {
printf("Dropping: \n"); perror("reuseaddr");
{ exit(1);
int i; }
for (i = 0; i < bytes; i++) saddr.sin_family = AF_INET;
{ saddr.sin_addr.s_addr = inet_addr("239.255.255.250");
putchar(gBuf[i]); saddr.sin_port = htons(1900);
} if (-1 == bind(s, (struct sockaddr *)&saddr, sizeof(saddr))) {
} perror("bind");
printf("\n##### End of DROP #######\n"); exit(1);
}
mreq.imr_multiaddr.s_addr = inet_addr("239.255.255.250");
mreq.imr_interface.s_addr = inet_addr(ip_addr);
if (-1 == setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,
&mreq, sizeof(mreq))) {
perror("add_membership");
exit(1);
}
//printf("Starting Multicast handling on 239.255.255.250\n");
while (1) {
addrlen = sizeof(saddr);
if (-1 == (bytes = recvfrom(s, gBuf, sizeof(gBuf) - 1, 0,
(struct sockaddr *)&saddr, &addrlen))) {
perror("recvfrom");
continue;
}
gBuf[bytes] = 0;
// sophisticated SSDP parsing algorithm
if (!strstr(gBuf, "urn:dial-multiscreen-org:service:dial:1"))
{
#if 0 // use for debugging
printf("Dropping: \n");
{
int i;
for (i = 0; i < bytes; i++)
{
putchar(gBuf[i]);
}
}
printf("\n##### End of DROP #######\n");
#endif #endif
continue; continue;
}
printf("Sending SSDP reply to %s:%d\n",
inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
if (-1 == sendto(s, send_buf, send_size, 0, (struct sockaddr *)&saddr, addrlen)) {
perror("sendto");
continue;
}
} }
printf("Sending SSDP reply to %s:%d\n",
inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
if (-1 == sendto(s, send_buf, send_size, 0, (struct sockaddr *)&saddr, addrlen)) {
perror("sendto");
continue;
}
}
close(s);
exit_ssdp = 0;
} }
void run_ssdp(int port, const char *pFriendlyName, const char * pModelName, const char *pUuid) { void run_ssdp(int port, const char *pFriendlyName, const char * pModelName, const char *pUuid) {
struct sockaddr sa; struct sockaddr sa;
socklen_t len = sizeof(sa); socklen_t len = sizeof(sa);
if(pFriendlyName) {
if(pFriendlyName) { strncpy(friendly_name, pFriendlyName, sizeof(friendly_name));
strncpy(friendly_name, pFriendlyName, sizeof(friendly_name)); friendly_name[255] = '\0';
friendly_name[255] = '\0'; } else {
} else { strcpy(friendly_name, "DIAL server sample");
strcpy(friendly_name, "DIAL server sample"); }
} if(pModelName) {
if(pModelName) { strncpy(model_name, pModelName, sizeof(model_name));
strncpy(model_name, pModelName, sizeof(model_name)); uuid[255] = '\0';
uuid[255] = '\0'; } else {
} else { strcpy(model_name, "deadbeef-dead-beef-dead-beefdeadbeef");
strcpy(model_name, "deadbeef-dead-beef-dead-beefdeadbeef"); }
} if(pUuid) {
if(pUuid) { strncpy(uuid, pUuid, sizeof(uuid));
strncpy(uuid, pUuid, sizeof(uuid)); uuid[255] = '\0';
uuid[255] = '\0'; } else {
} else { strcpy(uuid, "deadbeef-dead-beef-dead-beefdeadbeef");
strcpy(uuid, "deadbeef-dead-beef-dead-beefdeadbeef"); }
} dial_port = port;
get_local_address();
dial_port = port; ctx = mg_start(&request_handler, NULL, SSDP_PORT);
get_local_address(); if (mg_get_listen_addr(ctx, &sa, &len)) {
ctx = mg_start(&request_handler, NULL, SSDP_PORT); my_port = ntohs(((struct sockaddr_in *)&sa)->sin_port);
}
if (mg_get_listen_addr(ctx, &sa, &len)) { printf("SSDP listening on %s:%d\n", ip_addr, my_port);
my_port = ntohs(((struct sockaddr_in *)&sa)->sin_port); handle_mcast();
}
printf("SSDP listening on %s:%d\n", ip_addr, my_port);
handle_mcast();
mg_stop(ctx);
} }