mirror of
https://github.com/Netflix/dial-reference.git
synced 2026-06-08 10:59:59 +00:00
dial spec 2.0
This commit is contained in:
79
server/LinuxInterfaces.c
Normal file
79
server/LinuxInterfaces.c
Normal 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
26
server/LinuxInterfaces.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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 </additionalData>\n"
|
||||
"</service>\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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "dial_server.h"
|
||||
#include "dial_options.h"
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 <arpa/inet.h>
|
||||
#include <net/if.h>
|
||||
@@ -32,54 +32,54 @@
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "mongoose.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
// 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[] = ""
|
||||
"<?xml version=\"1.0\"?>"
|
||||
"<root"
|
||||
" xmlns=\"urn:schemas-upnp-org:device-1-0\""
|
||||
" xmlns:r=\"urn:restful-tv-org:schemas:upnp-dd\">"
|
||||
" <specVersion>"
|
||||
" <major>1</major>"
|
||||
" <minor>0</minor>"
|
||||
" </specVersion>"
|
||||
" <device>"
|
||||
" <deviceType>urn:schemas-upnp-org:device:tvdevice:1</deviceType>"
|
||||
" <friendlyName>%s</friendlyName>"
|
||||
" <manufacturer> </manufacturer>"
|
||||
" <modelName>%s</modelName>"
|
||||
" <UDN>uuid:%s</UDN>"
|
||||
" </device>"
|
||||
"</root>";
|
||||
"<?xml version=\"1.0\"?>"
|
||||
"<root"
|
||||
" xmlns=\"urn:schemas-upnp-org:device-1-0\""
|
||||
" xmlns:r=\"urn:restful-tv-org:schemas:upnp-dd\">"
|
||||
" <specVersion>"
|
||||
" <major>1</major>"
|
||||
" <minor>0</minor>"
|
||||
" </specVersion>"
|
||||
" <device>"
|
||||
" <deviceType>urn:schemas-upnp-org:device:tvdevice:1</deviceType>"
|
||||
" <friendlyName>%s</friendlyName>"
|
||||
" <manufacturer> </manufacturer>"
|
||||
" <modelName>%s</modelName>"
|
||||
" <UDN>uuid:%s</UDN>"
|
||||
" </device>"
|
||||
"</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.
|
||||
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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user