UNPKG

3.18 kBJavaScriptView Raw
1'use strict'
2
3var multicastdns = require('multicast-dns')
4var dnsEqual = require('dns-equal')
5var flatten = require('array-flatten')
6var deepEqual = require('deep-equal')
7
8module.exports = Server
9
10function Server (opts) {
11 this.mdns = multicastdns(opts)
12 this.mdns.setMaxListeners(0)
13 this.registry = {}
14 this.mdns.on('query', this._respondToQuery.bind(this))
15}
16
17Server.prototype.register = function (records) {
18 var self = this
19
20 if (Array.isArray(records)) records.forEach(register)
21 else register(records)
22
23 function register (record) {
24 var subRegistry = self.registry[record.type]
25 if (!subRegistry) subRegistry = self.registry[record.type] = []
26 else if (subRegistry.some(isDuplicateRecord(record))) return
27 subRegistry.push(record)
28 }
29}
30
31Server.prototype.unregister = function (records) {
32 var self = this
33
34 if (Array.isArray(records)) records.forEach(unregister)
35 else unregister(records)
36
37 function unregister (record) {
38 var type = record.type
39 if (!(type in self.registry)) return
40 self.registry[type] = self.registry[type].filter(function (r) {
41 return r.name !== record.name
42 })
43 }
44}
45
46Server.prototype._respondToQuery = function (query) {
47 var self = this
48 query.questions.forEach(function (question) {
49 var type = question.type
50 var name = question.name
51
52 // generate the answers section
53 var answers = type === 'ANY'
54 ? flatten.depth(Object.keys(self.registry).map(self._recordsFor.bind(self, name)), 1)
55 : self._recordsFor(name, type)
56
57 if (answers.length === 0) return
58
59 // generate the additionals section
60 var additionals = []
61 if (type !== 'ANY') {
62 answers.forEach(function (answer) {
63 if (answer.type !== 'PTR') return
64 additionals = additionals
65 .concat(self._recordsFor(answer.data, 'SRV'))
66 .concat(self._recordsFor(answer.data, 'TXT'))
67 })
68
69 // to populate the A and AAAA records, we need to get a set of unique
70 // targets from the SRV record
71 additionals
72 .filter(function (record) {
73 return record.type === 'SRV'
74 })
75 .map(function (record) {
76 return record.data.target
77 })
78 .filter(unique())
79 .forEach(function (target) {
80 additionals = additionals
81 .concat(self._recordsFor(target, 'A'))
82 .concat(self._recordsFor(target, 'AAAA'))
83 })
84 }
85
86 self.mdns.respond({ answers: answers, additionals: additionals }, function (err) {
87 if (err) throw err // TODO: Handle this (if no callback is given, the error will be ignored)
88 })
89 })
90}
91
92Server.prototype._recordsFor = function (name, type) {
93 if (!(type in this.registry)) return []
94
95 return this.registry[type].filter(function (record) {
96 var _name = ~name.indexOf('.') ? record.name : record.name.split('.')[0]
97 return dnsEqual(_name, name)
98 })
99}
100
101function isDuplicateRecord (a) {
102 return function (b) {
103 return a.type === b.type &&
104 a.name === b.name &&
105 deepEqual(a.data, b.data)
106 }
107}
108
109function unique () {
110 var set = []
111 return function (obj) {
112 if (~set.indexOf(obj)) return false
113 set.push(obj)
114 return true
115 }
116}