diff --git a/Changelog b/Changelog index 0906af8..c112427 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,9 @@ +--------------------------------------------------------------------- +Version 1.0.6 (72ab437) +--------------------------------------------------------------------- +Server changes: + - Update to DIAL specificiation 2.0 + --------------------------------------------------------------------- Version 1.0.5 (06edbce) --------------------------------------------------------------------- diff --git a/client/DialDiscovery.cpp b/client/DialDiscovery.cpp index efb37af..3d04c39 100644 --- a/client/DialDiscovery.cpp +++ b/client/DialDiscovery.cpp @@ -40,6 +40,7 @@ #include #include "DialDiscovery.h" #include +#include #define SSDP_TIMEOUT 30 #define SSDP_RESPONSE_TIMEOUT 3 @@ -97,6 +98,45 @@ static string getLocation(char *pResponse) 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) { if ((size * nmemb) != 0) { @@ -132,7 +172,6 @@ static void getServerInfo( const string &server, string& appsUrl, string& ddxml { CURL *curl; CURLcode res = CURLE_OK; - if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { fprintf(stderr, "curl_global_init() failed\n"); return; @@ -146,6 +185,7 @@ static void getServerInfo( const string &server, string& appsUrl, string& ddxml ATRACE("Sending ##%s##\n", 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_HEADERDATA, &appsUrl); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, receiveData); @@ -157,7 +197,7 @@ static void getServerInfo( const string &server, string& appsUrl, string& ddxml //curl_global_cleanup(); } -void DialDiscovery::updateServerList(string& server) +void DialDiscovery::updateServerList(string& server, string mac, int timeOut) { ServerMap::const_iterator it; it = mServerMap.find(server); @@ -165,7 +205,7 @@ void DialDiscovery::updateServerList(string& server) // not found, add it string appsUrl, ddxml; getServerInfo(server, appsUrl, ddxml); - mServerMap[server] = new DialServer(server, appsUrl, ddxml); + mServerMap[server] = new DialServer(server, appsUrl, ddxml, mac, timeOut); } else { // just mark that we found it (*it).second->setFound(true); @@ -181,9 +221,12 @@ void DialDiscovery::processServer(char *pResponse) // parse for LOCATION header server = getLocation(pResponse); ATRACE("FOUND server: %s\n", server.c_str()); - + + string mac=getMac(pResponse); + int timeOut=getTimeout(pResponse); + // save the appURL in the server - updateServerList(server); + updateServerList(server, mac, timeOut); #ifndef DEBUG } #else @@ -196,17 +239,39 @@ void DialDiscovery::processServer(char *pResponse) void *DialDiscovery::receiveResponses(void *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; char buf[4096] = {0,}; - while (1) { - if (-1 == (bytes = recvfrom(pConn->sock, buf, sizeof(buf) - 1, 0, - (struct sockaddr *)&pConn->saddr, &pConn->addrlen))) { - perror("recvfrom"); + time_t start=std::time(NULL); + while (std::time(NULL)-startsock+1, &readfds, NULL, NULL, &tv); + if (selectRt==-1){ + perror("select"); // error occurred in select() 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; } @@ -244,17 +309,17 @@ void DialDiscovery::resetDiscovery(void) } } -void * DialDiscovery::send_mcast(void *p) +void *DialDiscovery::send_mcast() { int one = 1, my_sock; socklen_t addrlen; //struct ip_mreq mreq; char send_buf[strlen((char*)ssdp_msearch) + INET_ADDRSTRLEN + 256] = {0,}; int send_size; - pthread_attr_t attr; 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); 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_port = htons(1900); - while (1) { - addrlen = sizeof(saddr); - ATRACE("Sending SSDP M-SEARCH to %s:%d\n", - inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); - if (-1 == sendto(my_sock, send_buf, send_size, 0, (struct sockaddr *)&saddr, addrlen)) { - perror("sendto"); - 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); + addrlen = sizeof(saddr); + ATRACE("Sending SSDP M-SEARCH to %s:%d\n", + inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); + if (-1 == sendto(my_sock, send_buf, send_size, 0, (struct sockaddr *)&saddr, addrlen)) { + perror("sendto"); + return 0; } + + // 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() { - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); - pthread_create(&_mcastThread, &attr, DialDiscovery::send_mcast, (void*)ssdp_msearch ); + send_mcast(); } void DialDiscovery::getServerList( vector& list ) { + list.clear(); for( ServerMap::iterator it = mServerMap.begin(); it != mServerMap.end(); ++it ) { list.push_back( it->second ); } diff --git a/client/DialDiscovery.h b/client/DialDiscovery.h index c6ba01f..f33ec75 100644 --- a/client/DialDiscovery.h +++ b/client/DialDiscovery.h @@ -73,11 +73,11 @@ public: const string& friendlyName, DialServer &server ); + void *send_mcast(); private: DialDiscovery(); - void updateServerList(string& server); + void updateServerList(string& server, string mac, int timeOut); static void *receiveResponses(void *p); - static void *send_mcast(void *p); void processServer(char *pResponse); void cleanServerList(); void resetDiscovery(); @@ -89,6 +89,7 @@ private: ServerMap mServerMap; static DialDiscovery* sDiscovery; + static const int RESPONSE_TIMEOUT = 5; }; #endif // DIALDISCOVERY_H diff --git a/client/DialServer.cpp b/client/DialServer.cpp index 951727d..d1def42 100644 --- a/client/DialServer.cpp +++ b/client/DialServer.cpp @@ -185,6 +185,16 @@ bool DialServer::getUuid( string& uuid ) return retval; } +string DialServer::getMacAddress() +{ + return m_macAddr; +} + +int DialServer::getWakeOnLanTimeout() +{ + return m_wakeUpTimeout; +} + int DialServer::launchApplication( string &application, string &payload, @@ -193,8 +203,8 @@ int DialServer::launchApplication( { ATRACE("%s: Launch %s\n", __FUNCTION__, application.c_str()); string appUrl = m_appsUrl; - sendCommand( appUrl.append(application), COMMAND_LAUNCH, payload, responseHeaders, responseBody); - return 0; + int status = sendCommand( appUrl.append(application), COMMAND_LAUNCH, payload, responseHeaders, responseBody); + return status; } int DialServer::getStatus( @@ -205,8 +215,9 @@ int DialServer::getStatus( ATRACE("%s: GetStatus %s\n", __FUNCTION__, application.c_str()); string emptyPayload; 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()); unsigned found = responseBody.find("href="); if( found != string::npos ) @@ -234,10 +245,10 @@ int DialServer::stopApplication( // just call status to update the run endpoint getStatus( application, responseHeaders, responseBody ); - sendCommand( - (appUrl.append(application)).append("/"+m_stopEndPoint), - COMMAND_KILL, emptyPayload, responseHeaders, responseBody ); - return 0; + int status = sendCommand( + (appUrl.append(application)).append("/"+m_stopEndPoint), + COMMAND_KILL, emptyPayload, responseHeaders, responseBody ); + return status; } int DialServer::getHttpResponseHeader( diff --git a/client/DialServer.h b/client/DialServer.h index 59b5aa7..ccd37e4 100644 --- a/client/DialServer.h +++ b/client/DialServer.h @@ -50,11 +50,13 @@ public: * 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_appsUrl(appsUrl), found(true), - m_ddxml(dd_xml) + m_ddxml(dd_xml), + m_macAddr(macAddr), + m_wakeUpTimeout(wakeUpTimeout) {} ~DialServer() { ATRACE("%s\n", __FUNCTION__); } @@ -94,6 +96,20 @@ public: */ 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 * @@ -176,6 +192,9 @@ private: bool found; string m_ddxml; // information about the device string m_stopEndPoint; + string m_macAddr; + int m_wakeUpTimeout; + }; #endif // DIALSERVER_H diff --git a/client/main.cpp b/client/main.cpp index c882207..a1a85f0 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -31,6 +31,11 @@ #include #include #include +#include +#include +#include +#include +#include using namespace std; @@ -51,12 +56,15 @@ static void printServerList( vector list ) vector::iterator it; for( i = 0, it = list.begin(); it < list.end(); it++, i++ ) { - string uuid, name; + string uuid, name, macAddr; + int wolTimeOut; (*it)->getFriendlyName( name ); (*it)->getUuid( uuid ); - printf("%Zu: Server IP[%s] UUID[%s] FriendlyName[%s] \n", - i+1, (*it)->getIpAddress().c_str(), - uuid.c_str(), name.c_str() ); + macAddr =(*it)->getMacAddress(); + wolTimeOut =(*it)->getWakeOnLanTimeout(); + 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 list ) { DialServer* pServer; // show a list to the user - if( list.size() > 1 ) - { - char buf[80] = {0,}; - vector::iterator it; + char buf[80] = {0,}; + vector::iterator it; - printf("Found Multiple servers\n"); - printServerList(list); - printf("Enter server: "); - scanf("%s", buf); - unsigned int server = atoi(buf); - assert( server > 0 && server <= list.size() ); + printf("Found Multiple servers\n"); + printf("0: Rescan and list DIAL servers\n"); + printServerList(list); + printf("Enter server: "); + scanf("%s", buf); + unsigned int server = atoi(buf); + if( server > 0 && server <= list.size()){ pServer = list[server-1]; - } - else - { - pServer = list.front(); + }else{ + pServer = NULL; } 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 processInput = 1; + int processInputOuter = 1; char buf[80]; vector list; - - pDial->getServerList(list); - if( list.size() == 0 ) - { - printf("No servers available\n"); - 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"); + + while (processInputOuter){ + pDial->getServerList(list); + if( list.size() == 0 ){ + printf("No servers available\n"); + return 1; } - } + 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; } diff --git a/server/LinuxInterfaces.c b/server/LinuxInterfaces.c new file mode 100644 index 0000000..ba9153a --- /dev/null +++ b/server/LinuxInterfaces.c @@ -0,0 +1,79 @@ +#include "LinuxInterfaces.h" +#include +#include +#include + +/* 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; +} diff --git a/server/LinuxInterfaces.h b/server/LinuxInterfaces.h new file mode 100644 index 0000000..1ec41ee --- /dev/null +++ b/server/LinuxInterfaces.h @@ -0,0 +1,26 @@ +#ifndef LINUXINTERFACES_H_ +#define LINUXINTERFACES_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Fetch the MACAddress of a network interface */ +#define MACBUFSIZE 32 +#define WAKEUPTIMEOUT 15 + +typedef struct +{ + char macAddress[MACBUFSIZE]; +}NetInterface; + +NetInterface getDefaultNetworkInterfaces(); + +#endif diff --git a/server/build.sh b/server/build.sh index 6490e9b..8fc4d16 100755 --- a/server/build.sh +++ b/server/build.sh @@ -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 diff --git a/server/dial_options.h b/server/dial_options.h index 52bd052..6f6b970 100644 --- a/server/dial_options.h +++ b/server/dial_options.h @@ -46,6 +46,10 @@ #define UUID_OPTION_LONG "--uuid-name" #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 { const char * pOption; @@ -79,6 +83,11 @@ struct dial_options gDialOptions[] = UUID_OPTION, UUID_OPTION_LONG, UUID_DESCRIPTION + }, + { + WAKE_OPTION, + WAKE_OPTION_LONG, + WAKE_DESCRIPTION } }; diff --git a/server/dial_server.c b/server/dial_server.c index 7c28a69..a357907 100644 --- a/server/dial_server.c +++ b/server/dial_server.c @@ -37,6 +37,7 @@ #include "mongoose.h" #include "url_lib.h" +#include "LinuxInterfaces.h" // TODO: Partners should define this port #define DIAL_PORT (56789) @@ -213,6 +214,7 @@ static void handle_app_status(struct mg_connection *conn, free(encoded_key); free(encoded_value); } + app->state = app->callbacks.status_cb(ds, app_name, app->run_id, &canStop, app->callback_data); mg_printf( @@ -231,7 +233,7 @@ static void handle_app_status(struct mg_connection *conn, "%s" "\n \n" "\r\n", - origin_header, + origin_header, DIAL_VERSION, app->name, canStop ? "true" : "false", @@ -510,7 +512,6 @@ static void *request_handler(enum mg_event event, struct mg_connection *conn, free(app_name); return ret; } - int use_payload = strcmp(request_info->request_method, "POST") ? 0 : 1; handle_dial_data(conn, request_info, app_name, origin_header, diff --git a/server/dial_server.h b/server/dial_server.h index 41310f7..09f68b3 100644 --- a/server/dial_server.h +++ b/server/dial_server.h @@ -39,7 +39,7 @@ typedef enum { /* * 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. diff --git a/server/main.c b/server/main.c index 311561c..0a7af31 100644 --- a/server/main.c +++ b/server/main.c @@ -38,6 +38,7 @@ #include "dial_server.h" #include "dial_options.h" #include +#include #define BUFSIZE 256 @@ -54,6 +55,7 @@ static char spNetflix[BUFSIZE]; static char spFriendlyName[BUFSIZE]; static char spModelName[BUFSIZE]; static char spUuid[BUFSIZE]; +extern bool wakeOnWifiLan; static int gDialPort; static char *spAppYouTube = "chrome"; @@ -393,26 +395,37 @@ static void processOption( int index, char * pOption ) { switch(index) { - case 0: // Data path - memset( spDataDir, 0, sizeof(spDataDir) ); - setDataDir( pOption ); - break; - case 1: // Netflix path - setValue( pOption, spNetflix ); - break; - case 2: // Friendly name - setValue( pOption, spFriendlyName ); - break; - case 3: // Model Name - setValue( pOption, spModelName ); - break; - case 4: // UUID - setValue( pOption, spUuid ); - break; - default: - // Should not get here - fprintf( stderr, "Option %d not valid\n", index); + case 0: // Data path + memset( spDataDir, 0, sizeof(spDataDir) ); + setDataDir( pOption ); + break; + case 1: // Netflix path + setValue( pOption, spNetflix ); + break; + case 2: // Friendly name + setValue( pOption, spFriendlyName ); + break; + case 3: // Model Name + setValue( pOption, spModelName ); + break; + case 4: // UUID + setValue( pOption, spUuid ); + break; + case 5: + if (strcmp(pOption, "on")==0){ + 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); + } + break; + default: + // Should not get here + fprintf( stderr, "Option %d not valid\n", index); + exit(1); } } diff --git a/server/makefile b/server/makefile index 8b4d663..ec9cd95 100644 --- a/server/makefile +++ b/server/makefile @@ -3,7 +3,7 @@ CC=$(TARGET)gcc .PHONY: clean .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) %.c: $(HEADERS) diff --git a/server/quick_ssdp.c b/server/quick_ssdp.c index ddaa3e3..689419e 100644 --- a/server/quick_ssdp.c +++ b/server/quick_ssdp.c @@ -1,27 +1,27 @@ /* - * Copyright (c) 2014 Netflix, Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY NETFLIX, INC. AND CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL NETFLIX OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * 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 - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ +* Copyright (c) 2014 Netflix, Inc. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY NETFLIX, INC. AND CONTRIBUTORS "AS IS" AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL NETFLIX OR CONTRIBUTORS BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* 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 +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ #include #include @@ -32,54 +32,54 @@ #include #include #include - #include "mongoose.h" - +#include // TODO: Partners should define this port #define SSDP_PORT (56790) - -static int exit_ssdp = 0; -void stop_ssdp() -{ - exit_ssdp = 1; -}; - static char gBuf[4096]; // TODO: Partners should get the friendlyName from the system and insert here. // TODO: Partners should get the UUID from the system and insert here. static const char ddxml[] = "" -"" -"" -" " -" 1" -" 0" -" " -" " -" urn:schemas-upnp-org:device:tvdevice:1" -" %s" -" " -" %s" -" uuid:%s" -" " -""; + "" + "" + " " + " 1" + " 0" + " " + " " + " urn:schemas-upnp-org:device:tvdevice:1" + " %s" + " " + " %s" + " uuid:%s" + " " + ""; + +// 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. static const char ssdp_reply[] = "HTTP/1.1 200 OK\r\n" - "LOCATION: http://%s:%d/dd.xml\r\n" - "CACHE-CONTROL: max-age=1800\r\n" - "EXT:\r\n" - "BOOTID.UPNP.ORG: 1\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" - "USN: uuid:%s::" - "urn:dial-multiscreen-org:service:dial:1\r\n\r\n"; - + "LOCATION: http://%s:%d/dd.xml\r\n" + "CACHE-CONTROL: max-age=1800\r\n" + "EXT:\r\n" + "BOOTID.UPNP.ORG: 1\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" + "USN: uuid:%s::" + "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 hw_addr[HW_ADDRSTRLEN] = "00:00:00:00:00:00"; static int dial_port = 0; static int my_port = 0; static char friendly_name[256]; @@ -87,156 +87,167 @@ static char uuid[256]; static char model_name[256]; static struct mg_context *ctx; +bool wakeOnWifiLan=true; + static void *request_handler(enum mg_event event, struct mg_connection *conn, const struct mg_request_info *request_info) { - if (event == MG_NEW_REQUEST) { - if (!strcmp(request_info->uri, "/dd.xml") && - !strcmp(request_info->request_method, "GET")) { - mg_printf(conn, "HTTP/1.1 200 OK\r\n" + if (event == MG_NEW_REQUEST) { + if (!strcmp(request_info->uri, "/dd.xml") && + !strcmp(request_info->request_method, "GET")) { + mg_printf(conn, "HTTP/1.1 200 OK\r\n" "Content-Type: application/xml\r\n" "Application-URL: http://%s:%d/apps/\r\n" "\r\n", ip_addr, dial_port); - mg_printf(conn, ddxml, friendly_name, model_name, uuid); - } else { - mg_send_http_error(conn, 404, "Not Found", "Not Found"); + mg_printf(conn, ddxml, friendly_name, model_name, uuid); + } else { + mg_send_http_error(conn, 404, "Not Found", "Not Found"); + } + return "done"; } - return "done"; - } - return NULL; + return NULL; } static void get_local_address() { - struct ifconf ifc; - char buf[4096]; - int s, i; - if (-1 == (s = socket(AF_INET, SOCK_DGRAM, 0))) { - perror("socket"); - 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; + struct ifconf ifc; + char buf[4096]; + int s, i; + if (-1 == (s = socket(AF_INET, SOCK_DGRAM, 0))) { + perror("socket"); + 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); + } + 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() { - int s, one = 1, bytes; - socklen_t addrlen; - struct sockaddr_in saddr; - struct ip_mreq mreq; - char send_buf[sizeof(ssdp_reply) + INET_ADDRSTRLEN + 256 + 256] = {0,}; - int send_size; - - send_size = snprintf(send_buf, sizeof(send_buf), ssdp_reply, ip_addr, my_port, uuid); - - 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; + int s, one = 1, bytes; + socklen_t addrlen; + struct sockaddr_in saddr; + struct ip_mreq mreq; + char wakeup_buf[sizeof(wakeup_header) + HW_ADDRSTRLEN + STR_TIMEOUTLEN] = {0, }; + char send_buf[sizeof(ssdp_reply) + INET_ADDRSTRLEN + 256 + 256 + sizeof(wakeup_buf)] = {0,}; + int send_size; + if (-1 < wakeup_timeout && wakeOnWifiLan) { + snprintf(wakeup_buf, sizeof(wakeup_buf), wakeup_header, hw_addr, wakeup_timeout); } - 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"); + 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))) { + 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 (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 - 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) { - struct sockaddr sa; - socklen_t len = sizeof(sa); - - if(pFriendlyName) { - strncpy(friendly_name, pFriendlyName, sizeof(friendly_name)); - friendly_name[255] = '\0'; - } else { - strcpy(friendly_name, "DIAL server sample"); - } - if(pModelName) { - strncpy(model_name, pModelName, sizeof(model_name)); - uuid[255] = '\0'; - } else { - strcpy(model_name, "deadbeef-dead-beef-dead-beefdeadbeef"); - } - if(pUuid) { - strncpy(uuid, pUuid, sizeof(uuid)); - uuid[255] = '\0'; - } else { - strcpy(uuid, "deadbeef-dead-beef-dead-beefdeadbeef"); - } - - dial_port = port; - get_local_address(); - ctx = mg_start(&request_handler, NULL, SSDP_PORT); - - if (mg_get_listen_addr(ctx, &sa, &len)) { - my_port = ntohs(((struct sockaddr_in *)&sa)->sin_port); - } - - printf("SSDP listening on %s:%d\n", ip_addr, my_port); - handle_mcast(); - mg_stop(ctx); + struct sockaddr sa; + socklen_t len = sizeof(sa); + if(pFriendlyName) { + strncpy(friendly_name, pFriendlyName, sizeof(friendly_name)); + friendly_name[255] = '\0'; + } else { + strcpy(friendly_name, "DIAL server sample"); + } + if(pModelName) { + strncpy(model_name, pModelName, sizeof(model_name)); + uuid[255] = '\0'; + } else { + strcpy(model_name, "deadbeef-dead-beef-dead-beefdeadbeef"); + } + if(pUuid) { + strncpy(uuid, pUuid, sizeof(uuid)); + uuid[255] = '\0'; + } else { + strcpy(uuid, "deadbeef-dead-beef-dead-beefdeadbeef"); + } + dial_port = port; + get_local_address(); + ctx = mg_start(&request_handler, NULL, SSDP_PORT); + if (mg_get_listen_addr(ctx, &sa, &len)) { + my_port = ntohs(((struct sockaddr_in *)&sa)->sin_port); + } + printf("SSDP listening on %s:%d\n", ip_addr, my_port); + handle_mcast(); }