Created
January 5, 2014 04:40
-
-
Save CloCkWeRX/8264408 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var http = require('http') | |
, os = require('os') | |
, url = require('url') | |
// , SSDP = require('../steward/node_modules/node-ssdp') | |
, netmask = require('../steward/node_modules/netmask') | |
, xml2json = require('../steward/node_modules/xml2json') | |
; | |
'use strict'; | |
var dgram = require('dgram') | |
, EE = require('events').EventEmitter | |
, util = require('util') | |
; | |
var SSDP_SIG = 'node.js/0.0.8 UPnP/1.1 node-ssdp/0.0.1' | |
, SSDP_IP = '239.255.255.250' | |
, SSDP_PORT = 1900 | |
, SSDP_IPPORT = SSDP_IP + ':' + SSDP_PORT | |
, TTL = 1800 | |
, SSDP_LOGGER = { error : function(msg, props) { console.log(msg); if (!!props) console.trace(props.exception); } | |
, warning : function(msg, props) { console.log(msg); if (!!props) console.log(props); } | |
, notice : function(msg, props) { console.log(msg); if (!!props) console.log(props); } | |
, info : function(msg, props) { console.log(msg); if (!!props) console.log(props); } | |
, debug : function(msg, props) { console.log(msg); if (!!props) console.log(props); } | |
} | |
; | |
function SSDP() { | |
var self = this; | |
if (!(this instanceof SSDP)) return new SSDP(); | |
EE.call(self); | |
self.logger = SSDP_LOGGER; | |
self.description = 'upnp/desc.php'; | |
self.usns = {}; | |
self.udn = 'uuid:e3f28962-f694-471f-8f74-c6abd507594b'; | |
// Configure socket for either client or server. | |
self.listening = false; | |
self.responses = {}; | |
self.sock = dgram.createSocket('udp4'); | |
self.sock.on('error', function (err) { | |
(self.logger.fatal || self.logger.error)('ssdp', { event: 'socket', diagnostic: 'error', exception: err }); | |
}); | |
self.sock.on('message', function onMessage(msg, rinfo) { | |
self.parseMessage(msg, rinfo); | |
}); | |
self.sock.on('listening', function onListening() { | |
var addr = self.sock.address(); | |
self.listening = 'http://' + addr.address + ':' + addr.port; | |
self.logger.info('SSDP listening on ' + self.listening); | |
// self.sock.addMembership(SSDP_IP); | |
self.sock.setMulticastLoopback(false); | |
// self.sock.setMulticastTTL(2); | |
}); | |
process.on('exit', function () { | |
self.close(); | |
}); | |
} | |
util.inherits(SSDP, EE); | |
SSDP.prototype.inMSearch = function (st, rinfo) { | |
var self = this | |
, peer = rinfo['address'] | |
, port = rinfo['port']; | |
if (st[0] == '"') st = st.slice(1, -1); | |
Object.keys(self.usns).forEach(function (usn) { | |
var udn = self.usns[usn]; | |
if (st == 'ssdp:all' || usn == st) { | |
var pkt = self.getSSDPHeader('200 OK', { | |
ST: usn, | |
USN: udn, | |
LOCATION: self.httphost + '/' + self.description, | |
'CACHE-CONTROL': 'max-age=' + TTL, | |
DATE: new Date().toUTCString(), | |
SERVER: SSDP_SIG, | |
EXT: '' | |
}, true); | |
self.logger.debug('Sending a 200 OK for an M-SEARCH to ' + peer + ':' + port); | |
pkt = new Buffer(pkt); | |
self.sock.send(pkt, 0, pkt.length, port, peer); | |
} | |
}); | |
}; | |
SSDP.prototype.addUSN = function (device) { | |
this.usns[device] = this.udn + '::' + device; | |
}; | |
SSDP.prototype.parseMessage = function (msg, rinfo) { | |
var type = msg.toString().split('\r\n').shift(); | |
// HTTP/#.# ### Response | |
if (true || type.match(/HTTP\/(\d{1})\.(\d{1}) (\d+) (.*)/)) { | |
this.parseResponse(msg, rinfo); | |
} else { | |
this.parseCommand(msg, rinfo); | |
} | |
}; | |
SSDP.prototype.parseCommand = function parseCommand(msg, rinfo) { | |
var lines = msg.toString().split("\r\n") | |
, type = lines.shift().split(' ') | |
, method = type[0] | |
, self = this; | |
var heads = {}; | |
lines.forEach(function (line) { | |
if (line.length) { | |
var vv = line.match(/^([^:]+):\s*(.*)$/); | |
heads[vv[1].toUpperCase()] = vv[2]; | |
} | |
}); | |
switch (method) { | |
case 'NOTIFY': | |
// Device coming to life. | |
if (heads['NTS'] == 'ssdp:alive') { | |
self.emit('advertise-alive', heads); | |
} | |
// Device shutting down. | |
else if (heads['NTS'] == 'ssdp:byebye') { | |
self.emit('advertise-bye', heads); | |
} else { | |
self.logger.warning('NOTIFY unhandled', { msg: msg, rinfo: rinfo }); | |
} | |
break; | |
case 'M-SEARCH': | |
self.logger.debug('SSDP M-SEARCH: for (' + heads['ST'] + ') from (' + rinfo['address'] + ':' + rinfo['port'] + ')'); | |
if (!heads['MAN']) return; | |
if (!heads['MX']) return; | |
if (!heads['ST']) return; | |
self.inMSearch(heads['ST'], rinfo); | |
break; | |
default: | |
self.logger.warning('NOTIFY unhandled', { msg: msg, rinfo: rinfo }); | |
} | |
}; | |
SSDP.prototype.parseResponse = function parseResponse(msg, rinfo) { | |
if (!this.responses[rinfo.address]) { | |
this.responses[rinfo.address] = true; | |
this.logger.debug('SSDP response', { rinfo: rinfo }); | |
} | |
this.emit('response', msg, rinfo); | |
/*console.log('Parsing a response!'); | |
console.log(msg.toString());*/ | |
}; | |
SSDP.prototype.search = function search(st, ipaddr) { | |
var self = this; | |
require('dns').lookup(require('os').hostname(), function (err, add) {/* jshint unused: false */ | |
//self.sock.bind(0, add); | |
var pkt = self.getSSDPHeader('M-SEARCH', { | |
'HOST': SSDP_IPPORT, | |
'ST': st, | |
'MAN': '"ssdp:discover"', | |
'MX': 3 | |
}); | |
pkt = new Buffer(pkt); | |
// console.log('@138', pkt.toString()); | |
self.sock.send(pkt, 0, pkt.length, SSDP_PORT, (!!ipaddr) ? ipaddr : SSDP_IP); | |
}); | |
}; | |
SSDP.prototype.server = function (ip, portH, portS) { | |
var self = this; | |
if (!portH) portH = 10293; | |
if (!portS) portS = 1900; | |
this.httphost = 'http://'+ip+':'+portH; | |
this.usns[this.udn] = this.udn; | |
if (!this.listening) this.sock.bind(portS, ip); | |
}; | |
SSDP.prototype.close = function () { | |
this.advertise(false); | |
this.advertise(false); | |
this.sock.close(); | |
}; | |
SSDP.prototype.advertise = function (alive) { | |
var self = this; | |
if (!this.sock) return; | |
if (alive === undefined) alive = true; | |
Object.keys(self.usns).forEach(function (usn) { | |
var udn = self.usns[usn] | |
, out = 'NOTIFY * HTTP/1.1\r\n'; | |
var heads = { | |
HOST: SSDP_IPPORT, | |
NT: usn, | |
NTS: (alive ? 'ssdp:alive' : 'ssdp:byebye'), | |
USN: udn | |
}; | |
if (alive) { | |
heads['LOCATION'] = self.httphost + '/' + self.description; | |
heads['CACHE-CONTROL'] = 'max-age=1800'; | |
heads['SERVER'] = SSDP_SIG; | |
} | |
out = new Buffer(self.getSSDPHeader('NOTIFY', heads)); | |
self.sock.send(out, 0, out.length, SSDP_PORT, SSDP_IP); | |
}); | |
}; | |
SSDP.prototype.getSSDPHeader = function (head, vars, res) { | |
var ret = ''; | |
if (res === null) res = false; | |
if (res) { | |
ret = "HTTP/1.1 " + head + "\r\n"; | |
} else { | |
ret = head + " * HTTP/1.1\r\n"; | |
} | |
Object.keys(vars).forEach(function (n) { | |
ret += n + ": " + vars[n] + "\r\n"; | |
}); | |
return ret + "\r\n"; | |
}; | |
SSDP.prototype.notify = function(ipaddr, portno) {/* jshint unused: false */ | |
var out; | |
var self = this; | |
if (!this.sock) return; | |
Object.keys(self.usns).forEach(function (usn) { | |
out = 'NOTIFY * HTTP/1.1\rnHOST: 239.255.255.250:10293\r\nCACHE-CONTROL: max-age=20\r\nSERVER: AIR CONDITIONER\r\n\r\nSPEC_VER: MSpec-1.00\r\nSERVICE_NAME: ControlServer-MLib\r\nMESSAGE_TYPE: CONTROLLER_START\r\n'; | |
bcast = '192.168.1.15'; // | |
console.log('multicasting to ' + bcast + ':1900 from ' + ipaddr + ':' + portno); | |
out = new Buffer(out); | |
self.sock.send(out, 0, out.length, 1900, bcast); | |
}); | |
}; | |
SSDP.prototype.server = function (ip, portH, portS) { | |
var self = this; | |
portH = 1900; | |
portS = 1900; | |
this.httphost = 'http://'+ip+':'+portH; | |
this.usns[this.udn] = this.udn; | |
this.sock.bind(portS, ip); | |
}; | |
var listen = function(ipaddr, portno) { | |
var ssdp; | |
ssdp = new SSDP().on('response', function(msg, rinfo) { | |
var f, i, info, j, lines; | |
lines = msg.toString().split("\r\n"); | |
info = {}; | |
for (i = 1; i < lines.length; i++) { | |
j = lines[i].indexOf(':'); | |
if (j <= 0) break; | |
info[lines[i].substring(0, j)] = lines[i].substring(j + 1).trim(); | |
} | |
f = function() { | |
console.log(''); | |
console.log(rinfo); | |
console.log(info); | |
}; | |
http.request(url.parse(info.LOCATION || info.Location), function(response) { | |
var data = ''; | |
response.on('data', function(chunk) { | |
data += chunk.toString(); | |
}).on('end', function() { | |
f(); | |
console.log(xml2json.toJson(data)); | |
}).on('close', function() { | |
f(); | |
console.log('socket premature eof'); | |
}).setEncoding('utf8'); | |
}).on('error', function(err) { | |
f(); | |
console.log('socket error: ' + err.message); | |
}).end(); | |
}); | |
ssdp.server('0.0.0.0', null, portno); | |
setTimeout(function() { | |
ssdp.notify('192.168.1.3', portno); | |
}, 1000); | |
}; | |
var ifa, ifaces, ifaddrs, ifname; | |
ifaces = os.networkInterfaces(); | |
for (ifname in ifaces) { | |
if ((!ifaces.hasOwnProperty(ifname)) | |
|| (ifname.indexOf('vmnet') === 0) | |
|| (ifname.indexOf('vnic') === 0) | |
|| (ifname.indexOf('tun') !== -1)) continue; | |
ifaddrs = ifaces[ifname]; | |
if (ifaddrs.length === 0) continue; | |
for (ifa = 0; ifa < ifaddrs.length; ifa++) { | |
if ((ifaddrs[ifa].internal) || (ifaddrs[ifa].family !== 'IPv4')) continue; | |
console.log('listening ' + ifname + ' on ' + ifaddrs[ifa].address + ' udp port'); | |
listen(ifaddrs[ifa].address, 1900); | |
} | |
} |
do you resolve it? In my mac it's not work! Cannot discover
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
hi. two things:
i have a new version of list-notify.js i will be uploading momentarily...