diff --git a/package.json b/package.json index f60a681..a739ef4 100644 --- a/package.json +++ b/package.json @@ -35,11 +35,11 @@ "homepage": "https://github.com/libp2p/js-libp2p-mdns", "devDependencies": { "aegir": "^18.0.2", - "async": "^2.6.1", "chai": "^4.2.0", "dirty-chai": "^2.0.1" }, "dependencies": { + "async": "^2.6.2", "debug": "^4.1.1", "libp2p-tcp": "~0.13.0", "multiaddr": "^6.0.2", diff --git a/src/compat/constants.js b/src/compat/constants.js new file mode 100644 index 0000000..f512d7b --- /dev/null +++ b/src/compat/constants.js @@ -0,0 +1,4 @@ +'use strict' + +exports.SERVICE_TAG = '_ipfs-discovery._udp' +exports.SERVICE_TAG_LOCAL = `${exports.SERVICE_TAG}.local` diff --git a/src/compat/index.js b/src/compat/index.js new file mode 100644 index 0000000..3d70e4c --- /dev/null +++ b/src/compat/index.js @@ -0,0 +1,94 @@ +'use strict' + +const OS = require('os') +const assert = require('assert') +const MDNS = require('multicast-dns') +const TCP = require('libp2p-tcp') +const nextTick = require('async/nextTick') +const { SERVICE_TAG_LOCAL } = require('./constants') + +const tcp = new TCP() + +// Simply advertise ourselves every 10 seconds with a go-libp2p-mdns compatible +// MDNS response. We can't discover go-libp2p nodes but they'll discover and +// connect to us. +class GoMulticastDNS { + constructor (peerInfo, options) { + assert(peerInfo, 'missing peerInfo parameter') + this._peerInfo = peerInfo + this._peerIdStr = peerInfo.id.toB58String() + this._options = options || {} + this._onTick = this._onTick.bind(this) + } + + start (callback) { + this._mdns = MDNS() + this._intervalId = setInterval(this._onTick, this._options.interval || 10000) + nextTick(() => callback()) + } + + _onTick () { + const multiaddrs = tcp.filter(this._peerInfo.multiaddrs.toArray()) + // Only announce TCP for now + if (!multiaddrs.length) return + + const answers = [] + const peerServiceTagLocal = `${this._peerIdStr}.${SERVICE_TAG_LOCAL}` + + answers.push({ + name: SERVICE_TAG_LOCAL, + type: 'PTR', + class: 'IN', + ttl: 120, + data: peerServiceTagLocal + }) + + // Only announce TCP multiaddrs for now + const port = multiaddrs[0].toString().split('/')[4] + + answers.push({ + name: peerServiceTagLocal, + type: 'SRV', + class: 'IN', + ttl: 120, + data: { + priority: 10, + weight: 1, + port, + target: OS.hostname() + } + }) + + answers.push({ + name: peerServiceTagLocal, + type: 'TXT', + class: 'IN', + ttl: 120, + data: [Buffer.from(this._peerIdStr)] + }) + + multiaddrs.forEach((ma) => { + const proto = ma.protoNames()[0] + if (proto === 'ip4' || proto === 'ip6') { + answers.push({ + name: OS.hostname(), + type: proto === 'ip4' ? 'A' : 'AAAA', + class: 'IN', + ttl: 120, + data: ma.toString().split('/')[2] + }) + } + }) + + this._mdns.respond(answers) + } + + stop (callback) { + clearInterval(this._intervalId) + this._mdns.removeListener('query', this._onQuery) + this._mdns.destroy(callback) + } +} + +module.exports = GoMulticastDNS +module.exports.tag = 'mdns' diff --git a/src/index.js b/src/index.js index d2c3ef3..b0d0cff 100644 --- a/src/index.js +++ b/src/index.js @@ -3,9 +3,12 @@ const multicastDNS = require('multicast-dns') const EventEmitter = require('events').EventEmitter const assert = require('assert') +const setImmediate = require('async/setImmediate') +const parallel = require('async/parallel') const debug = require('debug') const log = debug('libp2p:mdns') const query = require('./query') +const GoMulticastDNS = require('./compat') class MulticastDNS extends EventEmitter { constructor (options) { @@ -18,6 +21,10 @@ class MulticastDNS extends EventEmitter { this.port = options.port || 5353 this.peerInfo = options.peerInfo this._queryInterval = null + + if (options.compat !== false) { + this._goMdns = new GoMulticastDNS(options.peerInfo) + } } start (callback) { @@ -42,15 +49,27 @@ class MulticastDNS extends EventEmitter { query.gotQuery(event, this.mdns, this.peerInfo, this.serviceTag, this.broadcast) }) - setImmediate(() => callback()) + if (this._goMdns) { + this._goMdns.start(callback) + } else { + setImmediate(() => callback()) + } } stop (callback) { if (!this.mdns) { - callback(new Error('MulticastDNS service had not started yet')) + return callback(new Error('MulticastDNS service had not started yet')) + } + + clearInterval(this._queryInterval) + this._queryInterval = null + + if (this._goMdns) { + parallel([ + cb => this._goMdns.stop(cb), + cb => this.mdns.destroy(cb) + ], callback) } else { - clearInterval(this._queryInterval) - this._queryInterval = null this.mdns.destroy(callback) this.mdns = undefined }