diff --git a/README.md b/README.md index 177ed29d2df954ca06485f5af2263d827a2174a1..e4ef4df7014faa0791d557d3f80472f3f5529afa 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ The bridge (non-SSL) running on your server should ideally be only a backup - th Usage examples: (configure in your forever script or run from the command line) # Standard bridge on port 1338 - /usr/local/bin/node /.../ws2bridge-ssl.js -config /.../ws2bridge.config + /usr/local/bin/node /.../ws2bridge.js -config /.../ws2bridge.config # SSL bridge on port 1339 /usr/local/bin/node /.../ws2bridge-ssl.js -ssl -config /.../ws2bridge-html5-ssl.config diff --git a/ws2bridge_ssdp.js b/ws2bridge_ssdp.js deleted file mode 100644 index 27852479c45eb973664f8dfb24e82ea82ca8126e..0000000000000000000000000000000000000000 --- a/ws2bridge_ssdp.js +++ /dev/null @@ -1,1328 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////// -// -// WS2Bridge Server -// -// First cut at WS bridge for Remote and Personal STB prototype. -// This is CableLabs Confidential information and should not be disclosed outside of CableLabs. -// -// 3/15/13 - (SJ) Added NSDIRECTORY code to directly query WebSocketServer for device and application lists, and be notified of changes. -// -///////////////////////////////////////////////////////////////////////////////// - -// Common Modules -var fs = require('fs'); -// 'hack' to fix fs.exists in different versions of Node's FS module -fs.exists = fs.exists || require('path').exists; -fs.existsSync = fs.existsSync || require('path').existsSync; -var os=require('os'); - -// TODO: We need a better way to specify the network interface. Could be wired or wireless. For now, just -// pick the first one that is not local (lo). Use config file? -var ipaddr = ''; -var ifaces=os.networkInterfaces(); -for (var dev in ifaces) { - var alias=0; - ifaces[dev].forEach(function(details){ - if (details.family=='IPv4') { - console.log(dev+(alias?':'+alias:''),details.address); - ++alias; - dev = dev.substring(0,2); - if (dev != 'lo') { - if (ipaddr == '') { - ipaddr = details.address; - } - } - } - }); -} - -// Defaults -var configPath = './ws2bridge.config'; -var scheme = 'ws'; - -// Command line args -for (var index = 2; index < process.argv.length; index++) { - var val = process.argv[index]; - if ( val == "-config") { - index++; - if (process.argv[index] != undefined) { - configPath = process.argv[index]; - } - else { - console.log("missing config file path"); - } - } - else if (val == "-scheme") { - index++; - if (process.argv[index] != undefined) { - if (process.argv[index] == 'ws' || process.argv[index] == 'wss') { - scheme = process.argv[index]; - } - else { - console.log("Invalid scheme: "+ process.argv[index]); - } - - } - else { - console.log("missing scheme name"); - } - } -} - -var WebSocketServer = require('ws').Server, - http = require('https'), - fs = require('fs'), - url = require('url'), - mime = require('mime'), - querystring = require('querystring'), - //sys = require('sys'), - //spawn = require('child_process').spawn, - logfile = '/var/log/ws2bridge.log', - keepaliveLimit = 8, // Seconds until death if no keepalive. - serviceType = 'urn:cablelabs:html5:service:wsbridge:1'; - -// Config file -var config = {}; -(function(){ - fs.exists(configPath, function(exists) { - if (exists) { - fs.readFile( configPath, function(err, data) { - if(err) { - return; - } - config = JSON.parse(data); - - // TODO: Validate config parameters, esp required. - - if (config.scheme != undefined) { - scheme = config.scheme; - } - - // Start the servers - startup(); - }); - } - else { - console.log("File not found: "+configPath); - exit(1); - } - }); -})(); - -// Proper exit -process.on('SIGTERM', function(){ - console.log('process received SIGTERM: terminating'); - process.exit(1); -}); - -var server = http.createServer(function(request, response) { - var path = url.parse(request.url).pathname; - var fileRoot = __dirname + '/wsroot'; - var relativePath = fileRoot + path; - - switch (path) { - - case '/dd.xml': - // Device description - sendDeviceDescription(response); - break; - - case '/getshareddevices': - var query = url.parse(request.url).query, appType = querystring.parse(query).type; - response.writeHead(200, {"Content-Type": "text/plain"}); - - if ( query == null ) { - response.end(JSON.stringify(advertisedApps.getDevNames(request.connection.remoteAddress, appType))); - } else { - response.end(JSON.stringify(advertisedApps.getDevices(request.connection.remoteAddress))); - } - - break; - - case '/restart': - serveFile( fileRoot + "/monitorRestart.html", response); - setTimeout(function(){ - process.kill(process.pid, 'SIGTERM'); - },500); - - break; - - case '/ready': - response.writeHead(200, { 'Content-Type': 'text/html' }); - response.end('Ready', "utf-8"); - break; - - // TODO: make this a dynamically updating page - case '/status': - response.writeHead(200, { 'Content-Type': 'text/html' }); - response.end('Bridge Status', "utf-8"); - break; - - case '/xxxxxxlogtail': - response.writeHead(200, {'Content-Type': "text/plain;charset=UTF-8"}); - tail.stdout.on("data", function (data) { - if (typeof data != 'string') { - data = data.toString(); - } - response.end(data, "utf-8"); - }); - - break; - - default: - - // First check to see if this is a file - - fs.exists(relativePath, function(exists) { - - if (exists) { - - fs.readFile( relativePath, function(err, file) { - if(err) { - console.log("Error on readfile for " + relativePath); - return; - } - - response.writeHead(200, { 'Content-Type': mime.lookup(relativePath) }); - - response.end(file, "utf-8"); - }); - } - else { - console.log("Error 404: unknown http request for " + path); - response.writeHead(404, { 'Content-Type': 'text/html' }); - response.end('File not found', "utf-8"); - } - }); - } -}); - -// Moved to startup() -//server.listen(webSocketServerPort, function() { -// console.log('Server is running on port ' + webSocketServerPort); -//}); - -/* - * AdervistedApps contains apps that advertised by opening a WS connection - * Var apps contains the information and is structured as - * { WAN_IP1 :{ app1 :['DeviceName1',...],app2 :...}, - * WAN_IP2 : - * ... - * } - * add() adds DeviceName for app @ WAN_IP - * remove() deletes DeviceName for app @ WAN_IP - * getDevNames() returns the array of DeviceNames for app @ WAN_IP - */ - -function AdvertisedApps() { - - var apps = {}; - var devs = {}; - - this.add = function(remoteAddress, appName, devName) { - - if(apps[remoteAddress] == undefined) { - apps[remoteAddress] = {}; - } - - if(apps[remoteAddress][appName] == undefined) { - apps[remoteAddress][appName] = []; - } - - if(devs[remoteAddress] == undefined) { - devs[remoteAddress] = {}; - } - - if(devs[remoteAddress][devName] == undefined) { - devs[remoteAddress][devName] = []; - } - - apps[remoteAddress][appName].push(devName); - devs[remoteAddress][devName].push(appName); - - return true; - }; - - this.remove = function(remoteAddress, name) { - var devName = name.split(':')[0], - appName = name.split(':')[1]; - - if(typeof(devName) != 'string' || typeof(appName) != 'string' - || devName == '' || appName == '') { - return false; - } - - var appDevices = apps[remoteAddress][appName]; -// console.log('remove: ' + devName + 'from ' + appDevices); - appDevices.splice(appDevices.indexOf(devName), 1); - - if (appDevices.length == 0) { - delete apps[remoteAddress][appName]; - if (Object.keys(apps[remoteAddress]).length == 0 ) { - delete apps[remoteAddress]; - } - } - - var deviceApps = devs[remoteAddress][devName]; -// console.log('remove: ' + appName + 'from ' + deviceApps); - deviceApps.splice(deviceApps.indexOf(appName), 1); - - if (deviceApps.length == 0) { - delete devs[remoteAddress][devName]; - if (Object.keys(devs[remoteAddress]).length == 0 ) { - delete devs[remoteAddress]; - } - } - - return true; - }; - - this.remoteAddresses = function() { - - var remoteAddresses = []; - for (var ipAddr in apps) { - remoteAddresses.push(ipAddr); - } - return remoteAddresses; - } - - this.getDevNames = function(ipAddr, appName) { - if(apps[ipAddr] != undefined) { - if(apps[ipAddr][appName] != undefined) { - return apps[ipAddr][appName]; - } - } - return []; - }; - - // Return a device or list of devices and the apps they are sharing - // We only return advertised applications that do not have a remote connection - // This needs to be revisited for one to many bridges. - this.getDevices = function(ipAddr, device) { - - var devices = {}; - - if(devs[ipAddr] != undefined) { - - for (var dev in devs[ipAddr]) { - devices[dev] = []; - var sharedApps = devs[ipAddr][dev]; - if (sharedApps != undefined) { - for (var i=0; i < sharedApps.length; i++) { - var sharedApp = sharedApps[i]; - if ( isBridgeConnected(ipAddr, dev, sharedApp) == false) { - devices[dev].push(sharedApp); - } - } - } - } - } - return devices; - } - - // Return an app or list of apps and devices sharing the app - // We only return advertised applications that do not have a remote connection - // This needs to be revisited for one to many bridges. - this.getApplications = function(ipAddr) { - - var applications = {}; - - if(apps[ipAddr] != undefined) { - - for (var app in apps[ipAddr]) { - - var sharedDevices = apps[ipAddr][app]; - if (sharedDevices != undefined) { - for (var i=0; i < sharedDevices.length; i++) { - var sharedDevice = sharedDevices[i]; - if ( isBridgeConnected(ipAddr, sharedDevice, app) == false) { - if (applications[app] == undefined) { - applications[app] = []; - } - applications[app].push(sharedDevice); - } - } - } - } - } - return applications; - } -} - -function isBridgeConnected(ipaddr, devName, appName) { - var bridge = devName + ':' + appName; - if (bridgeClients[ipaddr] == undefined) { - return false; - } - else if (bridgeClients[ipaddr][bridge] == undefined) { - return false; - } - else if (bridgeClients[ipaddr][bridge].c2 == undefined) { - return false; - } - - //console.log('bridge connected: '+bridge+' to device: '+bridgeClients[ipaddr][bridge].c2.deviceName); - return true; -} - -var wsServer = new WebSocketServer({server: server}), - bridgeClients = {}, // Currently connected bridge clients - advertisedApps = new AdvertisedApps, - directoryClients = {}, - monitorClients = [], - messageCounter = 0, - updateCounter = 0; - - - -// This callback function is called every time someone tries to connect to wsServer -// UPDATE: this is actually never called! -//wsServer.on('request', function(request) { -// //console.log('Request received'); -// request.accept(null, request.origin); -//}); - -var KEndPoint_Shared = 0; -var KEndPoint_Remote = 1; -var KEndPoint_Directory = 2; -var KEndPoint_Monitor = 3; - -var KConnState_Open = 0; // Socket is open -var KConnState_Connected = 1; // Socket is connected to Bridge - -wsServer.on('connection', function(connection) { - - // TODO: Need new BridgeObject, referenced by both connection and bridgeObjects array so we have full context after endpoint closes. - var firstMsg = false; - var bridgeName = ''; - var otherConnection = ''; - var remoteAddress = resolveRemoteAddress(connection._socket.remoteAddress); - - // Extend connection to maintain state and guard sends - connection.otherEndpoint = null; - connection.endpointType = -1; - connection.remoteAddress = resolveRemoteAddress(connection._socket.remoteAddress); - connection.realAddress = connection._socket.remoteAddress; - connection.bridgeName = ''; - connection.connState = KConnState_Open; - connection.deviceName = ''; - connection.send2 = connection.send; - connection.send = function(msg) { - if ( connection.readyState == 1) { - try { - connection.send2(msg); - } - catch(e) { - console.log('Send error: '+e+' Message: '+msg); - connection.close(); - } - } - else { - connection.close(); - } - } - - // user sent some message - connection.on('message', function(message) { - - if (firstMsg == false) { // first message is bridge name - - firstMsg = true; - bridgeName = message; - - connection.connState = KConnState_Connected; - connection.bridgeName = bridgeName; - connection.connectTime = new Date(); - - if ( bridgeName == 'NSDIRECTORY') { // directory client - connection.endpointType = KEndPoint_Directory; - console.log("DIRECTORY connection from: " + remoteAddress + " ("+connection.realAddress+")" ); - if(directoryClients[remoteAddress] == undefined) { - directoryClients[remoteAddress] = new Array(); - } - directoryClients[remoteAddress].push(connection); - connection.send(JSON.stringify({message: 'wsDirectoryChanged'})); - notifyMonitorListeners(); - } - else if ( bridgeName == 'NSMONITOR') { // directory client - connection.endpointType = KEndPoint_Monitor; - //console.log("MONITOR connection from " + remoteAddress ); - if(monitorClients == undefined) { - monitorClients = new Array(); - } - monitorClients.push(connection); - generateConnectionMap(connection); - generateLogData(connection); - } - else { // bridge client - - console.log("BRIDGE connect request ("+bridgeName+") from: " + remoteAddress + " ("+connection.realAddress+")" ); - - // Parse the bridge name - var device1 = bridgeName.split(':')[0], // shared device - appName = bridgeName.split(':')[1], // application - device2 = bridgeName.split(':')[2]; // remote device (if this is a c2 connection) - - if (appName == undefined) { - console.log(" Invalid bridgeName: " + bridgeName); - connection.send('wsInvalidBridgeName'); - connection.close(); - return; - } - - if (device2 != undefined) { - - // This is a remote device attempting to connect to an existing bridge. - bridgeName = device1 + ':' + appName; - connection.bridgeName = bridgeName; - connection.endpointType = KEndPoint_Remote; - - if (bridgeClients[remoteAddress] != undefined && bridgeClients[remoteAddress][bridgeName] != undefined) { - if (bridgeClients[remoteAddress][bridgeName].c2 == undefined) { // 1st remote connection - - bridgeClients[remoteAddress][bridgeName].c2 = connection; - connection.deviceName = device2; - otherConnection = 'c1'; - console.log(' First remote connection from device: '+device2); - - // Tell both bridgeClients that bridge is connected - bridgeClients[remoteAddress][bridgeName].c1.send('wsBridgeConnected'); - bridgeClients[remoteAddress][bridgeName].c2.send('wsBridgeConnected'); - bridgeClients[remoteAddress][bridgeName].activity = new Date(); - connection.keepalive = keepaliveLimit; - - notifyDirectoryListeners(remoteAddress); - - } else { //2nd remote connection - close it - // TODO: Revisit about support for one to many connections. - console.log(' Second remote connection from device: '+device2+ " (refusing connection, closing)"); - connection.send('wsBridgeAlreadyConnected'); - connection.close(); - return; - } - } - else { - connection.send('wsBridgeDoesNotExist'); - connection.close(); - return; - } - } - else { - - if (bridgeClients[remoteAddress] == undefined) { - bridgeClients[remoteAddress] = {}; - } - - // This is a shared device - if(bridgeClients[remoteAddress][bridgeName] == undefined) { // Create empty bridge - bridgeClients[remoteAddress][bridgeName] = {}; - } - - connection.endpointType = KEndPoint_Shared; - - if(bridgeClients[remoteAddress][bridgeName].c1 != undefined) { // This shared device/application is already connected - connection.send('wsBridgeAlreadyExists'); - connection.close(); - } - else { - - // Establish new bridge connection from shared device - bridgeClients[remoteAddress][bridgeName].c1 = connection; - bridgeClients[remoteAddress][bridgeName].application = appName; - bridgeClients[remoteAddress][bridgeName].sent = 0; - bridgeClients[remoteAddress][bridgeName].activity = new Date(); - bridgeClients[remoteAddress][bridgeName].remoteAddress = remoteAddress; - connection.deviceName = device1; - connection.keepalive = keepaliveLimit; - - connection.send('wsSharedConnected'); - - otherConnection = 'c2'; - console.log(' First shared connection from: '+device1); - advertisedApps.add(remoteAddress, appName, device1, connection); - - notifyDirectoryListeners(remoteAddress); - } - } - - notifyMonitorListeners(); - } - } - else { // process incoming message - - if ( bridgeName == 'NSDIRECTORY') { // directory request for devices/applications - - var msgObj = eval("(" + message + ')'); - - //console.log( "Received directory request: "+JSON.stringify(msgObj)); - - if ( msgObj.message == 'getApplications') { - //console.log(" - getApplications"); - - var appList = advertisedApps.getApplications(remoteAddress); - connection.send(JSON.stringify({message: 'wsApplicationList', applications: appList})); - } - else if ( msgObj.message == 'getDevices') { - - - var deviceList = advertisedApps.getDevices(remoteAddress); - //console.log(" - getDevices " + deviceList); - connection.send(JSON.stringify({message: 'wsDeviceList', devices: deviceList})); - } - else if ( msgObj.message == 'resetSharedDevice') { - var sharedConnection = getConnectionForSharedDevice(remoteAddress, msgObj.device); - if (sharedConnection != null) { - sharedConnection.send("wsReset"); - } - } - } - else if ( bridgeName == 'NSMONITOR') { // monitor request - - if ( message == 'refresh') { - - // TODO: build json structure with topology and full log file. - //var appList = advertisedApps.getApplications(remoteAddress); - generateConnectionMap(connection); - generateLogData(connection); - } - else if (message == 'restart') { - setTimeout(function(){ - process.kill(process.pid, 'SIGTERM'); - },500); - } - } - else { // relay the message to the other WebSocket connection - - if (message == 'wsKeepalive') { - connection.keepalive = keepaliveLimit; - } - else { - var dest = bridgeClients[remoteAddress][bridgeName][otherConnection]; - - if(dest != undefined) { - dest.send(message); - bridgeClients[remoteAddress][bridgeName].sent++; - bridgeClients[remoteAddress][bridgeName].activity = new Date(); - messageCounter++; - } - } - } - } - }); - - // user disconnected - connection.on('close', function(connection) { - - //console.log('\nConnection closed for socket at remote address: ' + remoteAddress + ' on '+(new Date())); - - var index = -1; - - if (bridgeName == 'NSDIRECTORY') { - if (directoryClients[remoteAddress] != undefined) { - index = directoryClients[remoteAddress].indexOf(this); - } - - if (index!=-1) { // directory connection - console.log('Closing NSDIRECTORY client from: ' + remoteAddress); - directoryClients[remoteAddress].splice(index, 1); - if (directoryClients[remoteAddress].length == 0) { - delete directoryClients[remoteAddress]; - } - } - } - else if (bridgeName == 'NSMONITOR') { - index = monitorClients.indexOf(this); - - if (index!=-1) { // directory connection - //console.log('Closing NSMONITOR client from: ' + remoteAddress); - monitorClients.splice(index, 1); - } - } - else { // bridge connection - - switch(connection.endpointType) { - - case KEndPoint_Remote: - // 2nd connection closing - // TODO: Need new BridgeObject that is referenced both by the bridgeClients array and the connection. - closeC2(remoteAddress, bridgeName); - break; - - case KEndPoint_Shared: - // 1st client closing. Close other side and delete bridge - closeC1(remoteAddress, bridgeName); - - break; - case '': - console.log('Closing connection of unknown type: '+otherConnection); - break; - } - - notifyMonitorListeners(); - } - }); - - connection.on('error', function(err) { - - console.log('Connection error detected for socket at remote address: ' + remoteAddress + ' (removing connection)'); - - var index = -1; - - if (bridgeName == 'NSDIRECTORY') { - if (directoryClients[remoteAddress] != undefined) { - index = directoryClients[remoteAddress].indexOf(this); - } - - if (index!=-1) { // directory connection - console.log('Closing NSDIRECTORY client from: ' + remoteAddress); - directoryClients[remoteAddress].splice(index, 1); - if (directoryClients[remoteAddress].length == 0) { - delete directoryClients[remoteAddress]; - } - } - } - else if (bridgeName == 'NSMONITOR') { - index = monitorClients.indexOf(this); - - if (index!=-1) { // directory connection - //console.log('Closing NSMONITOR client from: ' + remoteAddress); - monitorClients.splice(index, 1); - } - } - else { // bridge connection - - switch(connection.endpointType) { - - case KEndPoint_Remote: - // 2nd connection closing - // TODO: Need new BridgeObject that is referenced both by the bridgeClients array and the connection. - closeC2(remoteAddress, bridgeName); - break; - - case KEndPoint_Shared: - // 1st client closing. Close other side and delete bridge - closeC1(remoteAddress, bridgeName); - - break; - case '': - console.log('Closing connection of unknown type: '+otherConnection); - break; - } - - notifyMonitorListeners(); - } - }); -}); - -function networkAddr(ipAddr) { - - var splitAddr = ipAddr.split('.'); - - if ((splitAddr[0] & 7) == 1) {return splitAddr[0] + '.00.00.00';} //class A - if ((splitAddr[0] & 7) == 2) {return splitAddr[0] + '.' + splitAddr[1] + '.00.00';} //class B - if ((splitAddr[0] & 7) == 6) {return splitAddr[0] + '.' + splitAddr[1] + '.' + splitAddr[2] + '.00'} //class C - - return ipAddr; -} - -function resolveRemoteAddress(remoteAddress) { - - //console.log("resolveRemoteAddress(" + remoteAddress + ")"); - var resolvedAddress = remoteAddress; - - if (config.filterByAddress != undefined && config.filterByAddress == false) { - //return "globalAddress"; - resolvedAddress = "globalAddress"; - } - else { - - // DM: 2013-06-11: updated remote address using Bob's code: - - // mask the subnet - resolvedAddress = networkAddr(remoteAddress); - } - - // did the address change? - if (resolvedAddress != remoteAddress) { - //console.log("Remote address ["+remoteAddress+"] resolved to ["+resolvedAddress+"]"); - } - - return (resolvedAddress); -} - -function getBridgeConnection(remoteAddress, bridgeName, endpoint) { - - try { - if (endpoint == KEndPoint_Shared) - return bridgeClients[remoteAddress][bridgeName].c1; - else if (endpoint == KEndPoint_Remote) - return bridgeClients[remoteAddress][bridgeName].c2; - else { - return undefined; - } - } - catch(err) { - return undefined; - } -} - -function getConnectionForSharedDevice(remoteAddress, device) { - - for (var ipAddr in bridgeClients) { - - if (ipAddr == remoteAddress) { - var bridges = bridgeClients[ipAddr]; - for (var bridgeName in bridges) { - - var bridge = bridges[bridgeName]; - if (bridge.c1.deviceName == device) { - return bridge.c1; - } - } - } - } - return null; -} - -function closeC2(remoteAddress, bridgeName) { - - var c1 = getBridgeConnection(remoteAddress, bridgeName, KEndPoint_Shared); - var c2 = getBridgeConnection(remoteAddress, bridgeName, KEndPoint_Remote); - - console.log('Closing remote connection to bridge: [' + remoteAddress + ' ] '+ bridgeName + ' from device: '+c2.deviceName); - - if(c2 != undefined) { - - delete bridgeClients[remoteAddress][bridgeName].c2; - - // Notify first connection when second connection disconnects - if (c1 != undefined) { - c1.send('wsBridgeDisconnected'); - bridgeClients[remoteAddress][bridgeName].activity = new Date(); - } - - notifyDirectoryListeners(remoteAddress); - } -} - -function closeC1(remoteAddress, bridgeName) { - - var c1 = getBridgeConnection(remoteAddress, bridgeName, KEndPoint_Shared); - var c2 = getBridgeConnection(remoteAddress, bridgeName, KEndPoint_Remote); - - if (c1 != undefined) { - c1.close(); - console.log('Closing shared connection to bridge: [' + remoteAddress + '] '+ bridgeName + ' from device: '+c1.deviceName); - advertisedApps.remove(remoteAddress, bridgeName); - delete bridgeClients[remoteAddress][bridgeName].c1; - - if(c2 != undefined){ - c2.close(); - delete bridgeClients[remoteAddress][bridgeName].c2; - } - - delete bridgeClients[remoteAddress][bridgeName]; - - notifyDirectoryListeners(remoteAddress) ; - } -} - -function serveFile(relativePath, response) { - - //var theResponse = response; - - fs.exists(relativePath, function(exists) { - - if (exists) { - - // First check if this is a directory with index.html - fs.stat(relativePath, function(err,stats){ - - if (stats.isDirectory()) { - - console.log("Error: url is a directory: " + path); - response.writeHead(404, { 'Content-Type': 'text/html' }); - response.end('File is a directory', "utf-8"); - } - else { - fs.readFile(relativePath, function(err, file) { - if (err) { - console.log("Error: " + err + " from readfile for " + relativePath); - response.writeHead(500, { 'Content-Type': 'text/plain'}); - response.write(err + '\n'); - response.end(); - } - else { - response.writeHead(200, {'Content-Type': mime.lookup(relativePath)}); - response.end(file, "utf-8"); - } - }); - } - }); - } - else { - console.log("Error: file not found: " + path); - response.writeHead(404, { 'Content-Type': 'text/html' }); - response.end('File not found', "utf-8"); - } - }); -} - -function generateConnectionMap(connection) { - - var map = {}; - map['advertisedApps'] = {}; - map['connectionMap'] = {}; - map['directoryMap'] = {}; - - var connectionCount = 0; - var applicationCount = 0; - var directoryCount = 0; - - advertisedApps.remoteAddresses().forEach(function(ipAddr){ - map['advertisedApps'][ipAddr] = advertisedApps.getApplications(ipAddr); - applicationCount += Object.keys(map['advertisedApps'][ipAddr]).length; - }); - - for (var ipAddr in bridgeClients) { - - map['connectionMap'][ipAddr] = []; - - var bridges = bridgeClients[ipAddr]; - - for (var bridgeName in bridges) { - - var bridge = bridges[bridgeName]; - var mapObject = {}; - - mapObject.bridge = bridgeName; - mapObject.remoteAddress = bridge.remoteAddress; - mapObject.realAddress = bridge.c1.realAddress; - mapObject.sharedDevice = bridge.c1.deviceName; - if (bridge.c2 != undefined) { - mapObject.remoteDevice = bridge.c2.deviceName; - mapObject.remoteDeviceAddress = bridge.c2.realAddress; - } - mapObject.application = bridge.application; - mapObject.activity = bridge.activity; - mapObject.sent = bridge.sent; - - map['connectionMap'][ipAddr].push(mapObject); - connectionCount++; - } - } - - for (var ipAddr in directoryClients) { - - map['directoryMap'][ipAddr] = []; - - var directoryConnections = directoryClients[ipAddr]; - - for (var dc in directoryConnections) { - - var directoryConnection = directoryConnections[dc]; - - var mapObject = {}; - - mapObject.remoteAddress = directoryConnection.remoteAddress; - mapObject.realAddress = directoryConnection.realAddress; - mapObject.connectTime = directoryConnection.connectTime; - - map['directoryMap'][ipAddr].push(mapObject); - directoryCount++; - } - } - - map['connectionCount'] = connectionCount; - map['applicationCount'] = applicationCount; - map['directoryCount'] = directoryCount; - - connection.send(JSON.stringify(map)); -} - -function notifyMonitorListeners(updateLog) { - - // Notify monitor listeners - for ( var i=0; i < monitorClients.length; i++ ) { - var mclient = monitorClients[i]; - //console.log( "sending directory changed message to "+ dclient._socket.remoteAddress); - generateConnectionMap(mclient); - - if (updateLog != undefined && updateLog) { - // DM: 2013-05-31: added log updating when updating monitor listeners - generateLogData(mclient); - } - } -} - -function notifyDirectoryListeners(remoteAddress) { - - if (directoryClients[remoteAddress] != undefined) { - for ( var i=0; i < directoryClients[remoteAddress].length; i++ ) { - var dclient = directoryClients[remoteAddress][i]; - //console.log( "Sending directory changed message to "+ dclient._socket.remoteAddress); - dclient.send(JSON.stringify({message: 'wsDirectoryChanged'})); - } - } -} - -// Update listeners with message activity at most once per second. -(function monitorUpdates() { - - setTimeout(function(){ - fs.watchFile(logfile, function(curr, prev) { - //if(prev.size > curr.size) - // return {clear:true}; - var stream = fs.createReadStream(logfile, { start: prev.size, end: curr.size}); - stream.addListener("data", function(lines) { - //socket.broadcast({ tail : lines.toString('utf-8').split("\n") }); - // Notify monitor listeners - - lines = lines.toString('utf-8'); - //var ndx = lines.indexOf("\n"); - //lines = lines.slice(ndx+1); - lines = lines.split("\n"); - var goodLines = []; - for (var i=0; i 0) { - msg.logTail = goodLines; - var msgStr = JSON.stringify(msg); - - for ( var i=0; i < monitorClients.length; i++ ) { - var mclient = monitorClients[i]; - mclient.send(msgStr); - } - } - }); - }); - },500); - - var timerCount = 0; - setInterval(function(){ - if (messageCounter > updateCounter) { - updateCounter = messageCounter; - notifyMonitorListeners(); - } - - // Shared connections should send a keepalive once every 2 seconds - // If we do not hear from them within a 5 second interval, close the connection. - if (config.enableKeepalive) { - checkSharedConnections(); - } - - },1000); - -})() - -function checkSharedConnections() { - for (var ipAddr in bridgeClients) { - - var bridges = bridgeClients[ipAddr]; - - for (var bridgeName in bridges) { - - var bridge = bridges[bridgeName]; - - var c1 = bridge.c1; - c1.keepalive--; - - if (c1.keepalive <=0) { - console.log('shared connection timed out: '+ipAddr+' '+c1.bridgeName+' (deleting)'); - closeC1(ipAddr, c1.bridgeName); - - // DM: 2013-05-31: added monitor update when a connection times out - notifyMonitorListeners(true); - } - - //console.log('checking remote connection for bridge: '+bridgeName); - - if (bridge.c2 != undefined) { - //console.log(' + c2: '+bridge.c2.deviceName+' keepalive: '+bridge.c2.keepalive); - var c2 = bridge.c2; - c2.keepalive--; - - if (c2.keepalive <=0) { - console.log('remote connection timed out: '+ipAddr+' '+c2.bridgeName+' device: '+c2.deviceName+' (deleting)'); - closeC2(ipAddr, c2.bridgeName); - - // DM: 2013-05-31: added monitor update when a connection times out - notifyMonitorListeners(true); - } - } - else { - //console.log(' + c2: undefined'); - } - } - } -} - -function generateLogData(connection) { - - //var backlogSize = 500; - - fs.stat(logfile,function(err,stats){ - if (err) throw err; - //var start = (stats.size > backlogSize)?(stats.size - backlogSize):0; - var start = 0; - var stream = fs.createReadStream(logfile,{start:start, end:stats.size}); - stream.addListener("data", function(lines){ - lines = lines.toString('utf-8'); - lines = lines.slice(lines.indexOf("\n")+1).split("\n"); - var msg = {}; - msg.logFile = lines; - var msgStr = JSON.stringify(msg); - //console.log(msgStr); - connection.send(msgStr); - }); - }); -} - -function zeroPrefixed(n) { - n=n+''; - if (n.length == 1) - return '0'+n; - return n; -} - -console.log2 = console.log; -console.log = function(text) { - - var date = new Date(); - var Y = date.getFullYear(); - var M = zeroPrefixed(date.getMonth()+1); - var D = zeroPrefixed(date.getDate()); - var hh = zeroPrefixed(date.getHours()); - var mm = zeroPrefixed(date.getMinutes()); - var ss = zeroPrefixed(date.getSeconds()); - var ts = ''+Y+'-'+M+'-'+D+' '+hh+':'+mm+':'+ss+' - '; - - - var msg = ts+text; - console.log2(msg); -} - -/******************************************************************************************************/ -/* SSDP Server to implement discovery -/******************************************************************************************************/ - -var dgram = require('dgram'); - -function SSDP(serviceType) { - - var SSDP_SIG = 'CableLabs DIAL Server/1.0 UPnP/1.1'; - var SSDP_IP = '239.255.255.250'; - var SSDP_PORT = 1900; - var SSDP_IPPORT = SSDP_IP+':'+SSDP_PORT; - var TTL = 1800; - - // Configure socket for either client or server. - this.sock = dgram.createSocket('udp4'); - this.sock.on('error', function () { - console.log('############### Got an error!'); - }); - - this.sock.on('message', function onMessage(msg, rinfo) { - //console.log("Received message: "+msg); - this.parseMessage(msg, rinfo); - }.bind(this)); - - this.sock.on('listening', function onListening() { - var addr = this.sock.address(); - //this.sock.addMembership(SSDP_IP); - this.sock.addMembership(SSDP_IP, ipaddr); - this.sock.setMulticastTTL(128); - //console.log('SSDP Listening on '+addr.address+':'+addr.port); - }.bind(this)); - - //this.sock.bind(SSDP_PORT, ipaddr); - this.sock.bind(SSDP_PORT); - - this.inMSearch = function (st, rinfo) { - - console.log("Received M-SEARCH: "+st); - var peer = rinfo['address']; - var port = rinfo['port']; - - if (st[0] == '"') st = st.slice(1, -1); - - if (st == 'ssdp:all' || serviceType == st) { - var pkt = this.getSSDPHeader('200 OK', { - ST: serviceType, - USN: 'uuid:'+config.uuid+'::'+serviceType, - LOCATION: ipaddr+':'+config.httpPort+'/dd.xml', // Location in config file?? - 'CACHE-CONTROL': 'max-age='+TTL, - DATE: new Date().toUTCString(), - SERVER: SSDP_SIG, - EXT: '' - }, true); - console.log('Sending a 200 OK for an m-search to '+peer+':'+port); - pkt = new Buffer(pkt); - this.sock.send(pkt, 0, pkt.length, port, peer); - } - } - - process.on('exit', function () { this.close; } ); - - this.parseMessage = function (msg, rinfo) { - var type = msg.toString().split('\r\n').shift(); - - // HTTP/#.# ### Response - if (type.match(/HTTP\/(\d{1})\.(\d{1}) (\d+) (.*)/)) - this.parseResponse(msg, rinfo); - else - this.parseCommand(msg, rinfo); - } - - this.parseCommand = function parseCommand(msg, rinfo) { - var lines = msg.toString().split("\r\n"); - var type = lines.shift().split(' '); - var method = type[0]; - var uri = type[1]; - - var heads = {}; - for (ix in lines) { - var line = lines[ix]; - if (line.length < 1) continue; - var vv = line.match(/^([^:]+):\s*(.*)$/); - heads[vv[1].toUpperCase()] = vv[2]; - } - - switch (method) { - // This is if we were discovering other devices, but we are not. - case 'NOTIFY': - /* - // Device coming to life. - if (heads['NTS'] == 'ssdp:alive') { - this.emit('advertise-alive', heads); - } - // Device shutting down. - else if (heads['NTS'] == 'ssdp:byebye') { - this.emit('advertise-bye', heads); - } - else console.log('############### Notify unhandled!'); - */ - break; - - case 'M-SEARCH': - console.log('SSDP M-SEARCH: for ('+heads['ST']+') from ('+rinfo['address']+':'+rinfo['port']+')'); - if (!heads['MAN']) return; - if (!heads['MX']) return; - if (!heads['ST']) return; - this.inMSearch(heads['ST'], rinfo); - break; - default: - console.log("Unknown message: \r\n"+msg); - } - } - - this.parseResponse = function parseResponse(msg, rinfo) { - //this.emit('response', msg, rinfo); - console.log('Parsing a response!'); - console.log(msg.toString()); - } - - this.server = function (ip) { - - console.log("this.server called: "+ip); - - this.httphost = 'http://'+ip+':'+config.httpPort; - - this.usns[this.udn] = this.udn; - - this.sock.bind(SSDP_PORT, ip); - - - // Shut down. - this.advertise(false); - setTimeout(this.advertise.bind(this), 1000, false); - - // Wake up. - setTimeout(this.advertise.bind(this), 2000); - setTimeout(this.advertise.bind(this), 3000); - - // Ad loop. - setInterval(this.advertise.bind(this), 10000); - } - - this.close = function () { - console.log(" - socket closed"); - this.advertise(false); - this.advertise(false); - this.sock.close(); - } - - this.advertise = function (alive) { - if (!this.sock) return; - if (alive == undefined) alive = true; - - for (usn in this.usns) { - udn = this.usns[usn]; - - var out = 'NOTIFY * HTTP/1.1\r\n'; - heads = { - HOST: SSDP_IPPORT, - NT: usn, - NTS: (alive ? 'ssdp:alive' : 'ssdp:byebye'), - USN: udn - } - if (alive) { - heads['LOCATION'] = this.httphost+'/upnp/desc.php'; - heads['CACHE-CONTROL'] = 'max-age=1800'; - heads['SERVER'] = SSDP_SIG; - } - out = new Buffer(this.getSSDPHeader('NOTIFY', heads)); - this.sock.send(out, 0, out.length, SSDP_PORT, SSDP_IP); - } - } - - this.getSSDPHeader = function (head, vars, res) { - if (res == undefined) res = false; - if (res) ret = "HTTP/1.1 "+head+"\r\n"; - else ret = head+" * HTTP/1.1\r\n"; - for (n in vars) ret += n+": "+vars[n]+"\r\n"; - return ret+"\r\n"; - } -} - -function sendDeviceDescription(response) { - // Initialize Device Description. GET http://host:port/dd.xml - var ddxml = - "\n" + - "\n" + - " \n" + - " 1\n" + - " 0\n" + - " \n" + - " \n" + - " urn:cablelabs:device:bridge:1\n" + - " CableLabs WebSockets Bridge Server\n" + - " CableLabs\n" + - " WS2Bridge 2.0\n" + - " uuid:"+config.uuid+"\n" + - " \n" + - ""; - - var path = 'http://' + ipaddr + ':' + config.httpPort; - response.writeHead(200, {"Content-Type": "text/xml", "WSBridge-URL": path}); - response.end(ddxml); -} - - -// Here after config file has been processed. -function startup() { - - // Start the HTTP server - server.listen(config.httpPort, function() { - console.log('Server is running on port ' + config.httpPort); - }); - - // Start the SSDP service - //var ssdpServer = new SSDP(serviceType); -}