Comparing version 0.2.8 to 0.2.9
@@ -5,5 +5,6 @@ | ||
var st = require('./lib/service_type'); | ||
var Networking = require('./lib/networking'); | ||
var networking = new Networking(); | ||
/** @member {string} */ | ||
@@ -26,3 +27,3 @@ module.exports.version = config.version; | ||
} | ||
return new module.exports.Browser(serviceType); | ||
return new module.exports.Browser(networking, serviceType); | ||
}; | ||
@@ -45,3 +46,4 @@ | ||
function advertisementCreated(serviceType, port, options) { | ||
return new module.exports.Advertisement(serviceType, port, options); | ||
return new module.exports.Advertisement( | ||
networking, serviceType, port, options); | ||
}; | ||
@@ -48,0 +50,0 @@ |
var debug = require('debug')('mdns:advertisement'); | ||
var dgram = require('dgram'); | ||
var os = require('os'); | ||
var dns = require('mdns-js-packet'); | ||
var DNSPacket = dns.DNSPacket; | ||
var DNSRecord = dns.DNSRecord; | ||
@@ -14,26 +11,2 @@ var ServiceType = require('./service_type').ServiceType; | ||
internal.sendDNSPacket = function (packet, cb) { | ||
debug('sending %d question, %d answer', | ||
packet.question.length, packet.answer.length); | ||
debug('packet', packet); | ||
var buf = DNSPacket.toBuffer(packet); | ||
// send packet | ||
var sock = dgram.createSocket('udp4'); | ||
sock.bind(5353, function (err) { | ||
if (err) { | ||
debug('there was an error binding %s', err); | ||
return; | ||
} | ||
sock.addMembership('224.0.0.251'); | ||
sock.setMulticastTTL(255); | ||
sock.setMulticastLoopback(true); | ||
sock.send(buf, 0, buf.length, 5353, '224.0.0.251', function (err, bytes) { | ||
debug('sent %d bytes with err:%s', bytes, err); | ||
sock.close(); | ||
typeof cb === 'function' && cb(); | ||
}); | ||
}); | ||
}; | ||
// Array of published services. | ||
@@ -46,151 +19,74 @@ internal.services = []; | ||
internal.haveResponder = function () { | ||
return (internal.services.length !== 0 || internal.probes.length !== 0); | ||
}; | ||
internal.startResponder = function () { | ||
var interfaces = os.networkInterfaces(); | ||
var ifaceFilter = this.options.networkInterface; | ||
var index = 0; | ||
for (var key in interfaces) { | ||
if (typeof ifaceFilter === 'undefined' || key === ifaceFilter) { | ||
if (interfaces.hasOwnProperty(key)) { | ||
for (var i = 0; i < interfaces[key].length; i++) { | ||
var address = interfaces[key][i].address; | ||
debug('interface', key, interfaces[key]); | ||
//no IPv6 addresses | ||
if (address.indexOf(':') !== -1) { | ||
continue; | ||
} | ||
// these are for unicast queries ? | ||
createSocket(index++, key, address, 0, bindToAddress.bind(this)); | ||
} | ||
} | ||
} | ||
} | ||
// this is for multicast queries ? | ||
createSocket(index++, '(multicast)', '224.0.0.251', 5353, | ||
bindToAddress.bind(this)); | ||
function createSocket(interfaceIndex, networkInterface, address, port, cb) { | ||
var sock = dgram.createSocket('udp4'); | ||
debug('creating socket for interface %s: %s:%d', | ||
networkInterface, address, port); | ||
sock.bind(port, address, function (err) { | ||
if (port === 5353) { | ||
sock.addMembership(address); | ||
sock.setMulticastTTL(255); | ||
sock.setMulticastLoopback(true); | ||
} | ||
cb(err, interfaceIndex, networkInterface, sock); | ||
}); | ||
internal.handleQuery = function (rec) { | ||
if (rec.type !== DNSRecord.Type.PTR && | ||
rec.type !== DNSRecord.Type.SRV && | ||
rec.type !== DNSRecord.Type.ANY) { | ||
debug('skipping query: type not PTR/SRV/ANY'); | ||
return; | ||
} | ||
function bindToAddress (err, interfaceIndex, networkInterface, sock) { | ||
if (err) { | ||
debug('there was an error binding %s', err); | ||
return; | ||
} | ||
debug('bindToAddress'); | ||
internal.connections.push(sock); | ||
sock.on('message', function (message, remote) { | ||
debug('got packet from remote', remote); | ||
var packet; | ||
try { | ||
packet = DNSPacket.parse(message); | ||
} catch (err) { | ||
debug('got packet truncated package, ignoring'); | ||
return; | ||
// check if we should reply via multi or unicast | ||
// TODO: handle the is_qu === true case and reply directly to remote | ||
// var is_qu = (rec.cl & DNSRecord.Class.IS_QM) === DNSRecord.Class.IS_QM; | ||
rec.class &= ~DNSRecord.Class.IS_OM; | ||
if (rec.class !== DNSRecord.Class.IN && rec.type !== DNSRecord.Class.ANY) { | ||
debug('skipping query: class not IN/ANY: %d', rec.class); | ||
return; | ||
} | ||
try { | ||
var type = new ServiceType(rec.name); | ||
internal.services.forEach(function (service) { | ||
if (type.isWildcard() || type.matches(service.serviceType)) { | ||
debug('answering query'); | ||
// TODO: should we only send PTR records if the query was for PTR | ||
// records? | ||
internal.sendDNSPacket( | ||
pf.buildANPacket.apply(service, [DNSRecord.TTL])); | ||
} else { | ||
debug('skipping query; type %s not * or %s', type, | ||
service.serviceType); | ||
} | ||
// check if it is a query where we are the authority for | ||
packet.question.forEach(handleQuery.bind(this)); | ||
packet.answer.forEach(handleAnswer.bind(this)); | ||
}.bind(this)); | ||
sock.on('error', function (err) { | ||
debug('socket error', err); | ||
}); | ||
} catch (err) { | ||
// invalid service type | ||
} | ||
}; | ||
function handleQuery(rec) { | ||
if (rec.type !== DNSRecord.Type.PTR && | ||
rec.type !== DNSRecord.Type.SRV && | ||
rec.type !== DNSRecord.Type.ANY) { | ||
debug('skipping query: type not PTR/SRV/ANY'); | ||
return; | ||
} | ||
// check if we should reply via multi or unicast | ||
// TODO: handle the is_qu === true case and reply directly to remote | ||
// var is_qu = (rec.cl & DNSRecord.Class.IS_QM) === DNSRecord.Class.IS_QM; | ||
rec.class &= ~DNSRecord.Class.IS_OM; | ||
if (rec.class !== DNSRecord.Class.IN && rec.type !== DNSRecord.Class.ANY) { | ||
debug('skipping query: class not IN/ANY: %d', rec.class); | ||
return; | ||
} | ||
try { | ||
var type = new ServiceType(rec.name); | ||
internal.services.forEach(function (service) { | ||
if (type.isWildcard() || type.matches(service.serviceType)) { | ||
debug('answering query'); | ||
// TODO: should we only send PTR records if the query was for PTR | ||
// records? | ||
internal.sendDNSPacket( | ||
pf.buildANPacket.apply(service, [DNSRecord.TTL])); | ||
} else { | ||
debug('skipping query; type %s not * or %s', type, | ||
service.serviceType); | ||
internal.handleAnswer = function (rec) { | ||
try { | ||
internal.probes.forEach(function (service) { | ||
if (service.status < 3) { | ||
var conflict = false; | ||
// parse answers and check if they match a probe | ||
debug('check names: %s and %s', rec.name, service.alias); | ||
switch (rec.type) { | ||
case DNSRecord.Type.PTR: | ||
if (rec.asName() === service.alias) { | ||
conflict = true; | ||
debug('name conflict in PTR'); | ||
} | ||
break; | ||
case DNSRecord.Type.SRV: | ||
case DNSRecord.Type.TXT: | ||
if (rec.name === service.alias) { | ||
conflict = true; | ||
debug('name conflict in SRV/TXT'); | ||
} | ||
break; | ||
} | ||
}); | ||
} catch (err) { | ||
// invalid service type | ||
} | ||
} | ||
function handleAnswer(rec) { | ||
try { | ||
internal.probes.forEach(function (service) { | ||
if (service.status < 3) { | ||
var conflict = false; | ||
// parse answers and check if they match a probe | ||
debug('check names: %s and %s', rec.name, service.alias); | ||
switch (rec.type) { | ||
case DNSRecord.Type.PTR: | ||
if (rec.asName() === service.alias) { | ||
conflict = true; | ||
debug('name conflict in PTR'); | ||
} | ||
break; | ||
case DNSRecord.Type.SRV: | ||
case DNSRecord.Type.TXT: | ||
if (rec.name === service.alias) { | ||
conflict = true; | ||
debug('name conflict in SRV/TXT'); | ||
} | ||
break; | ||
} | ||
if (conflict) { | ||
// no more probes | ||
service.status = 4; | ||
} | ||
if (conflict) { | ||
// no more probes | ||
service.status = 4; | ||
} | ||
}); | ||
} catch (err) { | ||
// invalid service type | ||
} | ||
} | ||
}); | ||
} catch (err) { | ||
// invalid service type | ||
} | ||
}; | ||
internal.stopResponder = function () { | ||
debug('stopping %d sockets', internal.connections.length); | ||
for (var i = 0; i < internal.connections.length; i++) { | ||
var sock = internal.connections[i]; | ||
sock.close(); | ||
sock.unref(); | ||
} | ||
internal.connections = []; | ||
}; | ||
internal.probeAndAdvertise = function () { | ||
debug('probeAndAdvertise(%s)', this.status); | ||
switch (this.status) { | ||
@@ -201,12 +97,11 @@ case 0: | ||
debug('probing service %d', this.status + 1); | ||
internal.sendDNSPacket(pf.buildQDPacket.apply(this, [])); | ||
this.networking.send(pf.buildQDPacket.apply(this, [])); | ||
break; | ||
case 3: | ||
debug('publishing service, suffix=%s', this.nameSuffix); | ||
internal.sendDNSPacket( | ||
pf.buildANPacket.apply(this, [DNSRecord.TTL])); | ||
var packet = pf.buildANPacket.apply(this, [DNSRecord.TTL]); | ||
this.networking.send(packet); | ||
// Repost announcement after 1sec (see rfc6762: 8.3) | ||
setTimeout(function onTimeout() { | ||
internal.sendDNSPacket( | ||
pf.buildANPacket.apply(this, [DNSRecord.TTL])); | ||
this.networking.send(packet); | ||
}.bind(this), 1000); | ||
@@ -241,3 +136,4 @@ // Service has been registered, respond to matching queries | ||
*/ | ||
var Advertisement = module.exports = function (serviceType, port, options) { | ||
var Advertisement = module.exports = function ( | ||
networking, serviceType, port, options) { | ||
if (!(this instanceof Advertisement)) { | ||
@@ -247,2 +143,3 @@ return new Advertisement(serviceType, port, options); | ||
// TODO: check more parameters | ||
@@ -252,2 +149,3 @@ if (!('name' in options)) { | ||
} | ||
var self = this; | ||
this.serviceType = serviceType; | ||
@@ -259,24 +157,31 @@ this.port = port; | ||
this.status = 0; // inactive | ||
this.networking = networking; | ||
networking.on('packets', function (packets /*, remote, connection*/) { | ||
packets.forEach(function (packet) { | ||
packet.question.forEach(internal.handleQuery.bind(self)); | ||
packet.answer.forEach(internal.handleAnswer.bind(self)); | ||
}); | ||
}); | ||
this.start = function () { | ||
networking.addUsage(self, function () { | ||
internal.probes.push(self); | ||
internal.probeAndAdvertise.apply(self, []); | ||
}); | ||
}; | ||
this.stop = function () { | ||
debug('unpublishing service'); | ||
internal.services = | ||
internal.services.filter(function (service) { return service === self; }); | ||
networking.send(pf.buildANPacket.apply(self, [0])); | ||
this.nameSuffix = ''; | ||
this.alias = ''; | ||
this.status = 0; // inactive | ||
}; | ||
debug('created new service'); | ||
}; //--Advertisement constructor | ||
Advertisement.prototype.start = function () { | ||
if (!internal.haveResponder()) { | ||
internal.startResponder.apply(this, []); | ||
} | ||
internal.probes.push(this); | ||
internal.probeAndAdvertise.apply(this, []); | ||
}; | ||
Advertisement.prototype.stop = function () { | ||
debug('unpublishing service'); | ||
internal.services = | ||
internal.services.filter(function (service) { return service === this; }); | ||
if (!internal.haveResponder()) { | ||
internal.stopResponder.apply(this, []); | ||
} | ||
internal.sendDNSPacket(pf.buildANPacket.apply(this, [0])); | ||
this.nameSuffix = ''; | ||
this.alias = ''; | ||
this.status = 0; // inactive | ||
}; |
var debug = require('debug')('mdns:browser'); | ||
var debugpacket = require('debug')('mdns:browser:packet'); | ||
var util = require('util'); | ||
var EventEmitter = require('events').EventEmitter; | ||
var dgram = require('dgram'); | ||
var os = require('os'); | ||
//var helper = require('../test/helper'); | ||
var dns = require('mdns-js-packet'); | ||
@@ -18,19 +16,2 @@ var DNSPacket = dns.DNSPacket; | ||
var MDNS_MULTICAST = '224.0.0.251'; | ||
internal.broadcast = function (sock, serviceType) { | ||
debug('broadcasting to', sock.address()); | ||
var packet = new DNSPacket(); | ||
packet.question.push(new DNSRecord( | ||
serviceType.toString() + '.local', | ||
DNSRecord.Type.PTR, 1) | ||
); | ||
var buf = DNSPacket.toBuffer(packet); | ||
debug('created buffer with length', buf.length); | ||
sock.send(buf, 0, buf.length, 5353, '224.0.0.251', function (err, bytes) { | ||
debug('%s sent %d bytes with err:%s', sock.address().address, bytes, err); | ||
}); | ||
}; | ||
/** | ||
@@ -40,6 +21,6 @@ * Handles incoming UDP traffic. | ||
*/ | ||
internal.onMessage = function (message, remote, connection) { | ||
debug('got packet from remote', remote); | ||
debugpacket('incomming packet', message.toString('hex')); | ||
var data = decoder.decodeMessage(message); | ||
internal.onMessage = function (packets, remote, connection) { | ||
debug('got packets from remote', remote); | ||
var data = decoder.decodePackets(packets); | ||
var isNew = false; | ||
@@ -138,3 +119,3 @@ | ||
*/ | ||
var Browser = module.exports = function (serviceType) { | ||
var Browser = module.exports = function (networking, serviceType) { | ||
if (!(this instanceof Browser)) { return new Browser(serviceType); } | ||
@@ -152,129 +133,24 @@ | ||
var self = this; | ||
this._all = new EventEmitter(); | ||
var services = {}; | ||
var addresses = {}; | ||
var connections = []; | ||
var created = 0; | ||
process.nextTick(function () { | ||
var interfaces = os.networkInterfaces(); | ||
var index = 0; | ||
for (var key in interfaces) { | ||
if (interfaces.hasOwnProperty(key)) { | ||
for (var i = 0; i < interfaces[key].length; i++) { | ||
var iface = interfaces[key][i]; | ||
//no localhost | ||
if (iface.internal) { | ||
continue; | ||
} | ||
//no IPv6 addresses | ||
if (iface.address.indexOf(':') !== -1) { | ||
continue; | ||
} | ||
debug('interface', key, iface.address); | ||
createSocket(index++, key, | ||
iface.address, 0, bindToAddress.bind(self)); | ||
} | ||
} | ||
} | ||
// var services = {}; | ||
// var addresses = {}; | ||
createSocket(index++, 'pseudo multicast', | ||
'0.0.0.0', 5353, bindToAddress.bind(self)); | ||
}.bind(this)); | ||
networking.addUsage(this, function () { | ||
self.emit('ready'); | ||
}); | ||
function createSocket(interfaceIndex, networkInterface, address, port, cb) { | ||
var sock = dgram.createSocket('udp4'); | ||
debug('creating socket for interface %s', address); | ||
created++; | ||
sock.bind(port, address, function (err) { | ||
if (port === 5353 && address === '0.0.0.0') { | ||
sock.addMembership(MDNS_MULTICAST); | ||
} | ||
cb(err, interfaceIndex, networkInterface, sock); | ||
}); | ||
} | ||
function bindToAddress (err, interfaceIndex, networkInterface, sock) { | ||
if (err) { | ||
debug('there was an error binding %s', err); | ||
return; | ||
} | ||
debug('bindToAddress'); | ||
var info = sock.address(); | ||
var connection = { | ||
socket:sock, | ||
hasTraffic: false, | ||
interfaceIndex: interfaceIndex, | ||
networkInterface: networkInterface, | ||
services: services, | ||
addresses: addresses | ||
}; | ||
connections.push(connection); | ||
sock.on('message', function () { | ||
connection.hasTraffic = true; | ||
[].push.call(arguments, connection); | ||
internal.onMessage.apply(this, arguments); | ||
}.bind(this)); | ||
sock.on('error', _onError); | ||
sock.on('close', function () { | ||
debug('socket closed', info); | ||
}); | ||
self._all.on('broadcast', function () { | ||
internal.broadcast(sock, serviceType); | ||
}.bind(this)); | ||
if (created === connections.length) { | ||
this.emit('ready', connections.length); | ||
} | ||
}//--bindToAddress | ||
function _onError (err) { | ||
debug('socket error', err); | ||
self.emit('error', err); | ||
} | ||
this.stop = function () { | ||
debug('stopping'); | ||
debug('connection.services', services); | ||
debug('connection.addresses', addresses); | ||
for (var i = 0; i < connections.length; i++) { | ||
var socket = connections[i].socket; | ||
socket.close(); | ||
socket.unref(); | ||
} | ||
connections = []; | ||
networking.removeUsage(this); | ||
};//--start | ||
networking.on('packets', internal.onMessage.bind(this)); | ||
this.discover = function () { | ||
var packet = new DNSPacket(); | ||
packet.question.push(new DNSRecord( | ||
serviceType.toString() + '.local', | ||
DNSRecord.Type.PTR, 1) | ||
); | ||
networking.send(packet); | ||
}; | ||
/** | ||
* Close interfaces where no traffic have occured | ||
*/ | ||
this.closeUnused = function () { | ||
var i; | ||
debug('closing sockets without traffic'); | ||
var closed = []; | ||
for (i = 0; i < connections.length; i++) { | ||
var connection = connections[i]; | ||
if (!connection.hasTraffic) { | ||
connection.socket.close(); | ||
connection.socket.unref(); | ||
closed.push(connection); | ||
} | ||
} | ||
for (i = 0; i < closed.length; i++) { | ||
var index = connections.indexOf(closed[i]); | ||
connections.splice(index, 1); | ||
} | ||
closed = []; | ||
};//--closeUnused | ||
};//--Browser constructor | ||
@@ -284,18 +160,1 @@ | ||
// /** | ||
// * Handles socket listen event | ||
// * @private | ||
// */ | ||
// Browser.prototype._onListening = function () { | ||
// var address = this.sock.address(); | ||
// debug('Browser listening on %s:%s', address.address, address.port); | ||
// }; | ||
Browser.prototype.discover = function () { | ||
process.nextTick(function () { | ||
debug('emitting broadcast request'); | ||
this._all.emit('broadcast'); | ||
}.bind(this)); | ||
}; |
@@ -75,2 +75,11 @@ var debug = require('debug')('mdns:lib:decoder'); | ||
module.exports.decodeMessage = function (message) { | ||
var packets = dns.DNSPacket.parse(message); | ||
if (!(packets instanceof Array)) { | ||
packets = [packets]; | ||
} | ||
return decodePackets(packets); | ||
}; | ||
var decodePackets = module.exports.decodePackets = function (packets) { | ||
var queryOnly = false; | ||
@@ -80,6 +89,2 @@ var data = { | ||
}; | ||
var packets = dns.DNSPacket.parse(message); | ||
if (!(packets instanceof Array)) { | ||
packets = [packets]; | ||
} | ||
var query = []; | ||
@@ -86,0 +91,0 @@ data.query = query; |
{ | ||
"name": "mdns-js", | ||
"version": "0.2.8", | ||
"version": "0.2.9", | ||
"repository": { | ||
@@ -16,3 +16,3 @@ "type": "git", | ||
"dependencies": { | ||
"debug": "~0.8.1", | ||
"debug": "^2.1.0", | ||
"mdns-js-packet": "0.1.x" | ||
@@ -22,6 +22,6 @@ }, | ||
"code": "^1.2.1", | ||
"joi": "^4.8.1", | ||
"joi": "^5.0.1", | ||
"jscs": "^1.6.2", | ||
"jshint": "*", | ||
"lab": "^5.0.2" | ||
"jshint": "^2.5.0", | ||
"lab": "^5.1.0" | ||
}, | ||
@@ -28,0 +28,0 @@ "scripts": { |
@@ -20,4 +20,3 @@ var Lab = require('lab'); | ||
browser.on('ready', function onReady(socketcount) { | ||
expect(socketcount).to.be.above(0); | ||
browser.on('ready', function onReady() { | ||
done(); | ||
@@ -44,7 +43,2 @@ }); | ||
it('should close unused', function (done) { | ||
browser.closeUnused(); | ||
setTimeout(done, 500); | ||
}); | ||
}); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
64
21
77859
1766
- Removeddebug@0.8.1(transitive)
Updateddebug@^2.1.0