Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

mdns-js

Package Overview
Dependencies
Maintainers
1
Versions
31
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

mdns-js - npm Package Compare versions

Comparing version 0.1.3 to 0.1.4

test/service_type_test.js

266

lib/advertisement.js
var debug = require('debug')('mdns:advertisement');
var dgram = require('dgram');
var os = require('os');
var DNSPacket = require('./dnspacket');
var DNSRecord = require('./dnsrecord');
var ServiceType = require('./service_type').ServiceType;
var internal = {};
internal.buildDNSPacket = function (ttl) {
internal.buildQDPacket = function () {
var packet = new DNSPacket();
var name = this.options.name + this.nameSuffix;
var domain = this.options.domain || 'local';
var serviceType = this.serviceType.toString() + '.' + domain;
this.alias = name + '.' + serviceType;
packet.push('qd', new DNSRecord(this.alias, DNSRecord.Type.ANY, 1));
return packet;
};
internal.buildANPacket = function (ttl) {
var packet =
new DNSPacket(DNSPacket.Flag.RESPONSE | DNSPacket.Flag.AUTHORATIVE);
var name = this.options.name;
var address = name + '.local';
if ('host' in this.options) {
address = this.options.host;
}
var serviceType = this.serviceType.toString() + '.local';
var alias = name + '.' + serviceType;
var name = this.options.name + this.nameSuffix;
var domain = this.options.domain || 'local';
var target = (this.options.host || name) + '.' + domain;
var serviceType = this.serviceType.toString() + '.' + domain;
var cl = DNSRecord.Class.IN | DNSRecord.Class.FLUSH;
debug('serviceType:', serviceType);
debug('alias:', this.alias);
packet.push('an', new DNSRecord(
serviceType, DNSRecord.Type.PTR, 1, ttl,
DNSRecord.toName(alias)));
serviceType, DNSRecord.Type.PTR, cl, ttl, DNSRecord.toName(this.alias)));
packet.push('an', new DNSRecord(
alias, DNSRecord.Type.SRV, 1, ttl,
DNSRecord.toSrv(0, 0, this.port, address)));
this.alias, DNSRecord.Type.SRV, cl, ttl,
DNSRecord.toSrv(0, 0, this.port, target)));
// TODO: https://github.com/agnat/node_mdns/blob/master/lib/advertisement.js
// has 'txtRecord'
if ('txt' in this.options) {
packet.push('an', new DNSRecord(
alias, DNSRecord.Type.TXT, 1, ttl,
this.alias, DNSRecord.Type.TXT, cl, ttl,
DNSRecord.toTxt(this.options.txt)));
}
var interfaces = os.networkInterfaces();
var ifaceFilter = this.options.networkInterface;
for (var key in interfaces) {
if (typeof ifaceFilter === 'undefined' || key === ifaceFilter) {
debug('add A record for interface:' , key);
for (var i = 0; i < interfaces[key].length; i++) {
var address = interfaces[key][i].address;
if (address.indexOf(':') === -1) {
packet.push('an', new DNSRecord(
target, DNSRecord.Type.A, cl, ttl, DNSRecord.toA(address)));
} else {
// TODO: also publish the ip6_address in an AAAA record
}
}
}
}
return packet;
};
internal.sendDNSPacket = function (packet) {
internal.sendDNSPacket = function (packet, cb) {
var buf = packet.toBuffer();

@@ -49,5 +78,7 @@

}
sock.setMulticastTTL(255);
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();
});

@@ -57,2 +88,184 @@ });

// Array of published services.
internal.services = [];
// Array of pending probes.
internal.probes = [];
// Array of open sockets
internal.connections = [];
internal.haveResponder = function () {
return (internal.services.length !== 0 || internal.probes.length !== 0);
};
internal.startResponder = 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 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++, key, '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', address);
sock.bind(port, address, function (err) {
cb(err, interfaceIndex, networkInterface, sock);
});
}
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 it is a query where we are the authority for
packet.each('qd', handleQuery.bind(this));
packet.each('an', handleAnswer.bind(this));
}.bind(this));
sock.on('error', function (err) {
debug('socket error', err);
});
}
function handleQuery(rec) {
if (rec.type !== DNSRecord.Type.PTR &&
rec.type !== DNSRecord.Type.ANY) {
debug('skipping query: type not PTR/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.cl &= ~DNSRecord.Class.IS_OM;
if (rec.cl !== DNSRecord.Class.IN && rec.type !== DNSRecord.Class.ANY) {
debug('skipping query: class not IN/ANY');
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(
internal.buildANPacket.apply(service, [DNSRecord.TTL]));
} else {
debug('skipping query; type %s not * or %s', type,
service.serviceType);
}
});
} 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;
}
}
});
} 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 () {
switch (this.status) {
case 0:
case 1:
case 2:
debug('probing service %d', this.status + 1);
internal.sendDNSPacket(internal.buildQDPacket.apply(this, []));
break;
case 3:
debug('publishing service, suffix=%s', this.nameSuffix);
internal.sendDNSPacket(
internal.buildANPacket.apply(this, [DNSRecord.TTL]));
// Repost announcement after 1sec (see rfc6762: 8.3)
setTimeout(function onTimeout() {
internal.sendDNSPacket(
internal.buildANPacket.apply(this, [DNSRecord.TTL]));
}.bind(this), 1000);
// Service has been registered, repond to matching queries
internal.services.push(this);
internal.probes =
internal.probes.filter(function (service) { return service === this; });
break;
case 4:
// we had a conflict
if (this.nameSuffix === '') {
this.nameSuffix = '1';
} else {
this.nameSuffix = (parseInt(this.nameSuffix) + 1) + '';
}
this.status = -1;
break;
}
if (this.status < 3) {
this.status++;
setTimeout(internal.probeAndAdvertise.bind(this), 250);
}
};
/**

@@ -77,2 +290,5 @@ * mDNS Advertisement class

this.options = options;
this.nameSuffix = '';
this.alias = '';
this.status = 0; // inactive
debug('created new service');

@@ -82,7 +298,7 @@ }; //--Advertisement constructor

Advertisement.prototype.start = function () {
debug('publishing service');
internal.sendDNSPacket(internal.buildDNSPacket.apply(this, [DNSRecord.TTL]));
setTimeout(function onTimeout() {
this.start();
}.bind(this), DNSRecord.TTL * 950); // TTL is seconds, need ms
if (!internal.haveResponder()) {
internal.startResponder.apply(this, []);
}
internal.probes.push(this);
internal.probeAndAdvertise.apply(this, []);
};

@@ -92,3 +308,11 @@

debug('unpublishing service');
internal.sendDNSPacket(internal.buildDNSPacket.apply(this, [0]));
internal.services =
internal.services.filter(function (service) { return service === this; });
if (!internal.haveResponder()) {
internal.stopResponder.apply(this, []);
}
internal.sendDNSPacket(internal.buildANPacket.apply(this, [0]));
this.nameSuffix = '';
this.alias = '';
this.status = 0; // inactive
};

@@ -114,2 +114,6 @@

var info = sock.address();
if (info.address === '224.0.0.251') {
sock.addMembership('224.0.0.251');
}
var connection = {

@@ -116,0 +120,0 @@ socket:sock,

@@ -53,3 +53,3 @@ var debug = require('debug')('mdns:lib:bufferconsumer');

var len;
this.ref = undefined;
var savedOffset;
for (;;) {

@@ -63,9 +63,28 @@ try {

break;
} else if (len === 0xc0) {
// TODO: This indicates a pointer to another valid name inside the
// DNSPacket, and is always a suffix: we're at the end of the name.
// We should probably hold onto this value instead of discarding it.
this.ref = this.byte();
} else if ((len & 0xc0) === 0xc0) {
debug('compressed label: bits=%d', ((len & 0xc0) >> 6));
// This indicates a pointer to another valid name inside the DNSPacket,
// and is always a suffix: we're at the end of the name.
// Store the current parse position and read the name from the offset.
var offset = this.byte();
offset |= ((len & ~0xc0) << 8);
if (offset > this._view.length) {
debug('!! offset=%d beyond buffer-size=%d', offset, this._view.length);
break;
}
if (typeof savedOffset === 'undefined') {
savedOffset = this._offset;
debug('saved offset=%d', savedOffset);
}
this._offset = offset;
debug('continue at %d', offset);
continue;
}
if ((this._offset + len) > this._view.length) {
debug('!! offset + len=%d+%d=%d beyond buffer-size=%d', this._offset,
len, (this._offset + len), this._view.length);
break;
}
debug('reading string with len=%d at %d, remaining size=%d', len,
this._offset, (this._view.length - this._offset));

@@ -77,4 +96,11 @@ // Otherwise, consume a string!

}
debug('single \'%s\', remaining size=%d', v,
(this._view.length - this._offset));
parts.push(v);
}
if (typeof savedOffset !== 'undefined') {
this._offset = savedOffset;
savedOffset = undefined;
}
debug('all labels \'%s\'', parts.join('.'));
if (join) {

@@ -81,0 +107,0 @@ return parts.join('.');

@@ -39,6 +39,19 @@ var debug = require('debug')('mdns:lib:dnsrecord');

TXT: 0x10, // 16
AAAA: 28, // 0x16
SRV: 0x21 // 33
AAAA: 0x16, // 28
SRV: 0x21, // 33
ANY: 0xff // 255
};
/**
* Enum for record class values
* @readonly
* @enum {number}
*/
DNSRecord.Class = {
IN: 0x01,
ANY: 0xff,
FLUSH: 0x8000,
IS_QM: 0x8000
};
DNSRecord.TTL = 60 * 60; // one hour default TTL

@@ -55,2 +68,3 @@

DNSRecord.prototype.asName = function () {
debug('parse PTR');
return new DataConsumer(this.data_).name();

@@ -68,2 +82,3 @@ };

DNSRecord.prototype.asSrv = function () {
debug('parse SRV');
var consumer = new DataConsumer(this.data_);

@@ -89,2 +104,3 @@ return {

DNSRecord.prototype.asTxt = function () {
debug('parse TXT');
var consumer = new DataConsumer(this.data_);

@@ -101,3 +117,14 @@ var data = {};

DNSRecord.toA = function (ip) {
var out = new BufferWriter();
var parts = ip.split('.');
for (var i = 0; i < 4; i++) {
out.byte(parts[i]);
}
return out.buf.slice(0, out.offset);
};
DNSRecord.prototype.asA = function () {
debug('parse A');
var consumer = new DataConsumer(this.data_);

@@ -118,2 +145,3 @@ var data = '';

DNSRecord.prototype.asAAAA = function () {
debug('parse AAAA');
var consumer = new DataConsumer(this.data_);

@@ -120,0 +148,0 @@ var data = '';

@@ -65,2 +65,3 @@ var debug = require('debug')('mdns:lib:ServiceType');

debug('fromString', text);
text = text.replace(/.local$/, '');
var isWildcard = text === ServiceType.wildcard;

@@ -73,5 +74,12 @@ var subtypes = text.split(',');

debug('primary: %s, servicetype: %s, serviceTokens: %s, subtypes: %s',
primaryString, serviceType, serviceTokens.join('.'), subtypes.join(','));
if (isWildcard) {
serviceType += '.' + serviceTokens.shift();
}
if (primaryString[0] !== '_' || primaryString[0] === '_services') {
serviceType = serviceTokens.shift();
}
protocol = serviceTokens.shift();

@@ -98,2 +106,5 @@ //make tcp default if not already defined

this.subtypes = subtypes.map(function (t) { return t.substr(1); });
debug('name: %s, protocol: %s, subtypes: %s', this.name, this.protocol,
this.subtypes.join(','));
};

@@ -100,0 +111,0 @@

2

package.json
{
"name": "mdns-js",
"version": "0.1.3",
"version": "0.1.4",
"repository": {

@@ -5,0 +5,0 @@ "type": "git",

@@ -44,2 +44,3 @@

var ptrCount = 0;
var aCount = 0;
var aaaaCount = 0;

@@ -57,5 +58,9 @@ var srvCount = 0;

packet.each('an', DNSRecord.Type.A, function (rec) {
rec.asA();
aCount++;
});
packet.each('an', DNSRecord.Type.AAAA, function (rec) {
var aaaa = rec.asAAAA();
aaaa.should.equal('fe80:0:0:0:2ac6:8eff:fe34:b8c3');
rec.asAAAA();
aaaaCount++;

@@ -65,8 +70,3 @@ });

packet.each('an', DNSRecord.Type.SRV, function (rec) {
var value = rec.asSrv();
value.should.be.type('object');
value.should.have.property('priority', 0),
value.should.have.property('weight', 0),
value.should.have.property('port', 9),
value.should.have.property('target', 'vestri');
rec.asSrv();
srvCount++;

@@ -76,6 +76,3 @@ });

packet.each('an', DNSRecord.Type.TXT, function (rec) {
var value = rec.asTxt();
value.should.be.type('object');
value.should.be.empty;
rec.asTxt();
txtCount++;

@@ -85,5 +82,6 @@ });

ptrCount.should.equal(4);
aCount.should.equal(0, 'bad A count');
aaaaCount.should.equal(0, 'bad AAAA count');
srvCount.should.equal(0, 'bad SRV count');
txtCount.should.equal(0, 'bad TXT count');
srvCount.should.equal(0, 'bad SRV count');
done();

@@ -97,2 +95,3 @@ });

var ptrCount = 0;
var aCount = 0;
var aaaaCount = 0;

@@ -109,5 +108,10 @@ var srvCount = 0;

packet.each('an', DNSRecord.Type.A, function (rec) {
var a = rec.asA();
a.should.equal('10.100.0.99');
aCount++;
});
packet.each('an', DNSRecord.Type.AAAA, function (rec) {
var aaaa = rec.asAAAA();
aaaa.should.equal('fe80:0:0:0:2ac6:8eff:fe34:b8c3');
rec.asAAAA();
aaaaCount++;

@@ -133,5 +137,8 @@ });

if (ptrCount === 1 && aaaaCount === 1 && txtCount === 1 && srvCount === 1) {
done();
}
ptrCount.should.equal(1);
aCount.should.equal(1, 'bad A count');
aaaaCount.should.equal(0, 'bad AAAA count');
srvCount.should.equal(1, 'bad SRV count');
txtCount.should.equal(1, 'bad TXT count');
done();
});

@@ -138,0 +145,0 @@

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc