Skip to content

Commit

Permalink
Adding record types etc.
Browse files Browse the repository at this point in the history
Closes #4
Some work towards #5
  • Loading branch information
kmpm committed Jul 31, 2014
1 parent 8a520d2 commit 8f7deb6
Show file tree
Hide file tree
Showing 13 changed files with 427 additions and 253 deletions.
4 changes: 3 additions & 1 deletion .jshintrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@
"unused": true,
"quotmark": true,
"undef": true,
"laxcomma": true
"laxcomma": true,
"curly": true,
"eqeqeq": true
}
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,4 @@ References
* http://en.wikipedia.org/wiki/Zero_configuration_networking#Service_discovery
* RFC 6762 - mDNS - http://tools.ietf.org/html/rfc6762
* RFC 6763 - DNS Based Service Discovery - http://tools.ietf.org/html/rfc6763
* http://www.tcpipguide.com/free/t_DNSMessageHeaderandQuestionSectionFormat.htm
45 changes: 24 additions & 21 deletions examples/simple.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
var mdns = require('../');


// var browser = new mdns.Mdns(mdns.tcp("googlecast"));
// console.log(mdns.ServiceType.wildcard);
var browser = new mdns.Mdns(mdns.ServiceType.wildcard);

browser.on('ready', function () {
console.log('browser is ready')
browser.discover();
});


browser.on('update', function (data) {
var device = {
address: data.addresses[0],
name: data.name,
servicename: data.type
}
console.log('device:', data);
});
var mdns = require('../');




var browser = new mdns.createBrowser(); //defaults to mdns.ServiceType.wildcard
//var browser = new mdns.Mdns(mdns.tcp("googlecast"));
//var browser = mdns.createBrowser(mdns.tcp("workstation"));

browser.on('ready', function () {
console.log('browser is ready');
browser.discover();
});


browser.on('update', function (data) {

console.log('data:', data);
});

//stop after 60 seconds
setTimeout(function () {
browser.stop();
}, 6000);
17 changes: 16 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
var config = require('./package.json');
var st = require('./lib/service_type');

var Browser = module.exports.Browser = require('./lib/browser'); //just for convenience

module.exports.Mdns = require('./lib');
module.exports.version = config.version;
module.exports.name = config.name;

/**
* Create a browser instance
* @param {string} [serviceType] - The Service type to browse for. Defaults to ServiceType.wildcard
* @return {Browser}
*/
module.exports.createBrowser = function (serviceType) {
if (typeof serviceType === 'undefined') {
serviceType = st.ServiceType.wildcard
}
return new Browser(serviceType);
}




module.exports.ServiceType = st.ServiceType;
module.exports.makeServiceType = st.makeServiceType;
module.exports.tcp = st.protocolHelper('tcp');
Expand Down
234 changes: 234 additions & 0 deletions lib/browser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
var debug = require('debug')('mdns:browser');
var util = require('util');
var EventEmitter = require('events').EventEmitter;
var dgram = require('dgram');
var os = require('os');

var DNSPacket = require('./dnspacket');
var DNSRecord = require('./dnsrecord');
var sorter = require('./sorter');

var ServiceType = require('./service_type').ServiceType;

var internal = {};


internal.broadcast = function(sock, serviceType) {
debug('broadcasting to', sock.address());
var packet = new DNSPacket();
packet.push('qd', new DNSRecord(serviceType.toString() + '.local', DNSRecord.Type.PTR, 1));
var buf = packet.toBuffer();
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);
});
};

/**
* Handles incoming UDP traffic.
* @private
*/
internal.onMessage = function (message, remote, connection) {
debug('got packet from remote', remote);
var packet = DNSPacket.parse(message);

var data = {
interfaceIndex: connection.interfaceIndex,
networkInterface: connection.networkInterface,
addresses: [remote.address]
};

packet.each('qd', DNSRecord.Type.PTR, function (rec) {
data.query = rec.name;
}.bind(this));

['an', 'ns', 'ar'].forEach(function(section) {
packet.each(section, DNSRecord.Type.PTR, function (rec) {
var name = rec.asName();
if(!data.hasOwnProperty('type')) {
data.type = [];
}
try {
var type = new ServiceType(name);
data.type.push({
name: type.name,
protocol: type.protocol,
subtypes: type.subtypes
});
} catch (e) {
debug('error', e);
data.type.push({name: name});
}
});

packet.each(section, DNSRecord.Type.SRV, function(rec) {
var srv = rec.asSrv();
data.port = srv.port;
data.host = srv.target + '.local';
});

packet.each(section, DNSRecord.Type.TXT, function(rec) {
data.txt = rec.asTxt();
});

packet.each(section, DNSRecord.Type.A, function(rec) {
var value = rec.asA();
if(data.addresses.indexOf(value) < 0) {
data.addresses.push(value);
}
});
sorter.sortIps(data.addresses);

packet.each(section, DNSRecord.Type.AAAA, function(rec) {
var value = rec.asAAAA();
if(data.addresses.indexOf(value) < 0) {
data.addresses.push(value);
}
});

});//-forEach section

this.emit('update', data);
};

/**
* mDNS Browser class
* @class
* @param {string|ServiceType} serviceType - The service type to browse for.
*/
var Browser = module.exports = function (serviceType) {
if(!(this instanceof Browser)) { return new Browser(serviceType); }
if (typeof serviceType !== 'string' && !(serviceType instanceof ServiceType)) {
debug('serviceType type:', typeof serviceType);
debug('serviceType is ServiceType:', serviceType instanceof ServiceType);
debug('serviceType=', serviceType);
throw new Error('argument must be instance of ServiceType or valid string');
}
this.serviceType = serviceType;
var self = this;
this._all = new EventEmitter();

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 address = interfaces[key][i].address;
debug('interface', key, interfaces[key]);
//no IPv6 addresses
if (address.indexOf(':') !== -1) {
continue;
}
createSocket(index++, key, address, bindToAddress.bind(self));
}
}
}
}.bind(this));


function createSocket (interfaceIndex, networkInterface, address, callback) {
var sock = dgram.createSocket('udp4');
debug('creating socket for interface %s', address);
created++;
sock.bind(0, address, function (err) {
callback(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};

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');
for(var i=0; i < connections.length; i++) {
var socket = connections[i].socket;
socket.close();
socket.unref();
}
connections = [];
};//--start



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

util.inherits(Browser, EventEmitter);

// /**
// * 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));
};


6 changes: 3 additions & 3 deletions lib/bufferconsumer.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,19 @@ BufferConsumer.prototype.long = function() {
* reference (i.e., 0xc0 <ref>).
*/
BufferConsumer.prototype.name = function(join) {
if (typeof join === 'undefined') join = true;
if (typeof join === 'undefined') { join = true; }
var parts = [];
var len;
this.ref = undefined;
for (;;) {
try {
len = this.byte();
} catch (e) {
if (e instanceof RangeError) break;
if (e instanceof RangeError) { break; }
}
if (!len) {
break;
} else if (len == 0xc0) {
} 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.
Expand Down
Loading

0 comments on commit 8f7deb6

Please sign in to comment.