|
|
- 'use strict'
-
- var multicastdns = require('multicast-dns')
- var dnsEqual = require('dns-equal')
- var flatten = require('array-flatten')
- var deepEqual = require('deep-equal')
-
- module.exports = Server
-
- function Server (opts) {
- this.mdns = multicastdns(opts)
- this.mdns.setMaxListeners(0)
- this.registry = {}
- this.mdns.on('query', this._respondToQuery.bind(this))
- }
-
- Server.prototype.register = function (records) {
- var self = this
-
- if (Array.isArray(records)) records.forEach(register)
- else register(records)
-
- function register (record) {
- var subRegistry = self.registry[record.type]
- if (!subRegistry) subRegistry = self.registry[record.type] = []
- else if (subRegistry.some(isDuplicateRecord(record))) return
- subRegistry.push(record)
- }
- }
-
- Server.prototype.unregister = function (records) {
- var self = this
-
- if (Array.isArray(records)) records.forEach(unregister)
- else unregister(records)
-
- function unregister (record) {
- var type = record.type
- if (!(type in self.registry)) return
- self.registry[type] = self.registry[type].filter(function (r) {
- return r.name !== record.name
- })
- }
- }
-
- Server.prototype._respondToQuery = function (query) {
- var self = this
- query.questions.forEach(function (question) {
- var type = question.type
- var name = question.name
-
- // generate the answers section
- var answers = type === 'ANY'
- ? flatten.depth(Object.keys(self.registry).map(self._recordsFor.bind(self, name)), 1)
- : self._recordsFor(name, type)
-
- if (answers.length === 0) return
-
- // generate the additionals section
- var additionals = []
- if (type !== 'ANY') {
- answers.forEach(function (answer) {
- if (answer.type !== 'PTR') return
- additionals = additionals
- .concat(self._recordsFor(answer.data, 'SRV'))
- .concat(self._recordsFor(answer.data, 'TXT'))
- })
-
- // to populate the A and AAAA records, we need to get a set of unique
- // targets from the SRV record
- additionals
- .filter(function (record) {
- return record.type === 'SRV'
- })
- .map(function (record) {
- return record.data.target
- })
- .filter(unique())
- .forEach(function (target) {
- additionals = additionals
- .concat(self._recordsFor(target, 'A'))
- .concat(self._recordsFor(target, 'AAAA'))
- })
- }
-
- self.mdns.respond({ answers: answers, additionals: additionals }, function (err) {
- if (err) throw err // TODO: Handle this (if no callback is given, the error will be ignored)
- })
- })
- }
-
- Server.prototype._recordsFor = function (name, type) {
- if (!(type in this.registry)) return []
-
- return this.registry[type].filter(function (record) {
- var _name = ~name.indexOf('.') ? record.name : record.name.split('.')[0]
- return dnsEqual(_name, name)
- })
- }
-
- function isDuplicateRecord (a) {
- return function (b) {
- return a.type === b.type &&
- a.name === b.name &&
- deepEqual(a.data, b.data)
- }
- }
-
- function unique () {
- var set = []
- return function (obj) {
- if (~set.indexOf(obj)) return false
- set.push(obj)
- return true
- }
- }
|