Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi-interface support (with conflict resolved). #42

Closed
wants to merge 10 commits into from
Closed
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ Creates a new `mdns` instance. Options can contain the following
``` js
{
multicast: true // use udp multicasting
interface: '192.168.0.2' // explicitly specify a network interface. defaults to all
interface: '192.168.0.2' // explicitly specify a network interface or array of interfaces**
port: 5353, // set the udp port
ip: '224.0.0.251', // set the udp ip
ttl: 255, // set the multicast ttl
Expand All @@ -123,6 +123,10 @@ Creates a new `mdns` instance. Options can contain the following
}
```

<sup>**</sup> The interface option should be specified on host systems with multiple network
interface options. When not specified a single interface will be automatically chosen for
broadcasting.

#### `mdns.on('query', (packet, rinfo))`

Emitted when a query packet is received.
Expand Down
58 changes: 45 additions & 13 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,39 @@ module.exports = function (opts) {
var ip = opts.ip || opts.host || (type === 'udp4' ? '224.0.0.251' : null)
var me = {address: ip, port: port}
var destroyed = false
var interfaces = opts.interface ? (Array.isArray(opts.interface) ? opts.interface : [opts.interface]) : []
var sendSockets = []

if (type === 'udp6' && (!ip || !opts.interface)) {
if (type === 'udp6' && (!ip || !interfaces.length)) {
throw new Error('For IPv6 multicast you must specify `ip` and `interface`')
}

// create sockets to send messages
for (var si = 0; si <= interfaces.length; si++) {
sendSockets[si] = dgram.createSocket({
type: type,
reuseAddr: opts.reuseAddr !== false,
toString: function () {
return type
}
})

sendSockets[si].on('listening', function () {
if (opts.multicast !== false) {
this.setMulticastTTL(opts.ttl || 255)
this.setMulticastLoopback(opts.loopback !== false)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not enough to send packets on this interface. For example, Linux will select default interface.

To achieve sending via proper interface the call this.setMulticastInterface(iterfaceAddress) is required. Because this is a callback, I suggest convert entire loop body to immediately invoked function with interface address as a parameter.

}
})

sendSockets[si].on('error', function (err) {
if (err.code === 'EACCES' || err.code === 'EADDRINUSE') that.emit('error', err)
else that.emit('warning', err)
})

sendSockets[si].bind(port, interfaces[si] || null)
}

// create socket to listen for messages
var socket = opts.socket || dgram.createSocket({
type: type,
reuseAddr: opts.reuseAddr !== false,
Expand Down Expand Up @@ -48,12 +76,14 @@ module.exports = function (opts) {

socket.on('listening', function () {
if (!port) port = me.port = socket.address().port
if (opts.multicast !== false) {
try {
socket.addMembership(ip, opts.interface)
} catch (err) {
that.emit('error', err)
}
if (opts.multicast !== false && interfaces.length) {
interfaces.forEach(function (iface) {
try {
socket.addMembership(ip, iface)
} catch (err) {
that.emit('warning', err)
}
})
socket.setMulticastTTL(opts.ttl || 255)
socket.setMulticastLoopback(opts.loopback !== false)
}
Expand All @@ -62,7 +92,7 @@ module.exports = function (opts) {
var bind = thunky(function (cb) {
if (!port) return cb(null)
socket.once('error', cb)
socket.bind(port, opts.interface, function () {
socket.bind(port, function () {
socket.removeListener('error', cb)
cb(null)
})
Expand All @@ -77,11 +107,10 @@ module.exports = function (opts) {
if (typeof rinfo === 'function') return that.send(value, null, rinfo)
if (!cb) cb = noop
if (!rinfo) rinfo = me
bind(function (err) {
if (destroyed) return cb()
if (err) return cb(err)
var message = packet.encode(value)
socket.send(message, 0, message.length, rinfo.port, rinfo.address || rinfo.host, cb)
if (destroyed) return cb()
var message = packet.encode(value)
sendSockets.forEach(function (sendSocket) {
sendSocket.send(message, 0, message.length, rinfo.port, rinfo.address || rinfo.host, cb)
})
}

Expand Down Expand Up @@ -112,6 +141,9 @@ module.exports = function (opts) {
destroyed = true
socket.once('close', cb)
socket.close()
sendSockets.forEach(function (sendSocket) {
sendSocket.close()
})
}

return that
Expand Down