117 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			117 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| '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
 | |
|   }
 | |
| }
 |