homebridge-gsh-kazari
Version:
110 lines (88 loc) • 3.06 kB
JavaScript
'use strict'
const multicastdns = require('multicast-dns')
const dnsEqual = require('./utils/dnsEqual')
const flatten = require('array-flatten')
const helpers = require('./helpers.js')
const Server = function (opts) {
this.mdns = multicastdns(opts)
this.mdns.setMaxListeners(0)
this.registry = {}
this.mdns.on('query', this._respondToQuery.bind(this))
}
Server.prototype = {
_respondToQuery: function (query) {
for (let i = 0; i < query.questions.length; i++) {
const question = query.questions[i]
const type = question.type
const name = question.name
// generate the answers section
const answers = type === 'ANY'
? flatten.depth(Object.keys(this.registry).map(this._recordsFor.bind(this, name)), 1)
: this._recordsFor(name, type)
if (answers.length === 0) return
// generate the additionals section
let additionals = []
if (type !== 'ANY') {
answers.forEach(answer => {
if (answer.type !== 'PTR') return
additionals = additionals
.concat(this._recordsFor(answer.data, 'SRV'))
.concat(this._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(record => {
return record.type === 'SRV'
})
.map(record => {
return record.data.target
})
.filter(helpers.unique())
.forEach(target => {
additionals = additionals
.concat(this._recordsFor(target, 'A'))
.concat(this._recordsFor(target, 'AAAA'))
})
}
this.mdns.respond({
answers: answers,
additionals: additionals
}, err => {
if (err) throw err // TODO: Handle this (if no callback is given, the error will be ignored)
})
}
},
register: function (records) {
if (!Array.isArray(records)) { records = [records] }
for (let i = 0; i < records.length; i++) {
const record = records[i]
let subRegistry = this.registry[record.type]
if (!subRegistry) {
subRegistry = this.registry[record.type] = []
} else if (subRegistry.some(helpers.isDuplicateRecord(record))) {
continue
}
subRegistry.push(record)
}
},
unregister: function (records) {
if (!Array.isArray(records)) { records = [records] }
for (let i = 0; i < records.length; i++) {
const record = records[i]
const type = record.type
if (!(type in this.registry)) { continue }
this.registry[type] = this.registry[type].filter(r => {
return r.name !== record.name
})
}
},
_recordsFor: function (name, type) {
if (!(type in this.registry)) { return [] }
return this.registry[type].filter(record => {
const recordName = ~name.indexOf('.') ? record.name : record.name.split('.')[0]
return dnsEqual(recordName, name)
})
}
}
module.exports = Server