Index: frontend/node_modules/multicast-dns/index.js
===================================================================
--- frontend/node_modules/multicast-dns/index.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/multicast-dns/index.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,205 @@
+var packet = require('dns-packet')
+var dgram = require('dgram')
+var thunky = require('thunky')
+var events = require('events')
+var os = require('os')
+
+var noop = function () {}
+
+module.exports = function (opts) {
+  if (!opts) opts = {}
+
+  var that = new events.EventEmitter()
+  var port = typeof opts.port === 'number' ? opts.port : 5353
+  var type = opts.type || 'udp4'
+  var ip = opts.ip || opts.host || (type === 'udp4' ? '224.0.0.251' : null)
+  var me = {address: ip, port: port}
+  var memberships = {}
+  var destroyed = false
+  var interval = null
+
+  if (type === 'udp6' && (!ip || !opts.interface)) {
+    throw new Error('For IPv6 multicast you must specify `ip` and `interface`')
+  }
+
+  var socket = opts.socket || dgram.createSocket({
+    type: type,
+    reuseAddr: opts.reuseAddr !== false,
+    toString: function () {
+      return type
+    }
+  })
+
+  socket.on('error', function (err) {
+    if (err.code === 'EACCES' || err.code === 'EADDRINUSE') that.emit('error', err)
+    else that.emit('warning', err)
+  })
+
+  socket.on('message', function (message, rinfo) {
+    try {
+      message = packet.decode(message)
+    } catch (err) {
+      that.emit('warning', err)
+      return
+    }
+
+    that.emit('packet', message, rinfo)
+
+    if (message.type === 'query') that.emit('query', message, rinfo)
+    if (message.type === 'response') that.emit('response', message, rinfo)
+  })
+
+  socket.on('listening', function () {
+    if (!port) port = me.port = socket.address().port
+    if (opts.multicast !== false) {
+      that.update()
+      interval = setInterval(that.update, 5000)
+      socket.setMulticastTTL(opts.ttl || 255)
+      socket.setMulticastLoopback(opts.loopback !== false)
+    }
+  })
+
+  var bind = thunky(function (cb) {
+    if (!port || opts.bind === false) return cb(null)
+    socket.once('error', cb)
+    socket.bind(port, opts.bind || opts.interface, function () {
+      socket.removeListener('error', cb)
+      cb(null)
+    })
+  })
+
+  bind(function (err) {
+    if (err) return that.emit('error', err)
+    that.emit('ready')
+  })
+
+  that.send = function (value, rinfo, cb) {
+    if (typeof rinfo === 'function') return that.send(value, null, rinfo)
+    if (!cb) cb = noop
+    if (!rinfo) rinfo = me
+    else if (!rinfo.host && !rinfo.address) rinfo.address = me.address
+
+    bind(onbind)
+
+    function onbind (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)
+    }
+  }
+
+  that.response =
+  that.respond = function (res, rinfo, cb) {
+    if (Array.isArray(res)) res = {answers: res}
+
+    res.type = 'response'
+    res.flags = (res.flags || 0) | packet.AUTHORITATIVE_ANSWER
+    that.send(res, rinfo, cb)
+  }
+
+  that.query = function (q, type, rinfo, cb) {
+    if (typeof type === 'function') return that.query(q, null, null, type)
+    if (typeof type === 'object' && type && type.port) return that.query(q, null, type, rinfo)
+    if (typeof rinfo === 'function') return that.query(q, type, null, rinfo)
+    if (!cb) cb = noop
+
+    if (typeof q === 'string') q = [{name: q, type: type || 'ANY'}]
+    if (Array.isArray(q)) q = {type: 'query', questions: q}
+
+    q.type = 'query'
+    that.send(q, rinfo, cb)
+  }
+
+  that.destroy = function (cb) {
+    if (!cb) cb = noop
+    if (destroyed) return process.nextTick(cb)
+    destroyed = true
+    clearInterval(interval)
+
+    // Need to drop memberships by hand and ignore errors.
+    // socket.close() does not cope with errors.
+    for (var iface in memberships) {
+      try {
+        socket.dropMembership(ip, iface)
+      } catch (e) {
+        // eat it
+      }
+    }
+    memberships = {}
+    socket.close(cb)
+  }
+
+  that.update = function () {
+    var ifaces = opts.interface ? [].concat(opts.interface) : allInterfaces()
+    var updated = false
+
+    for (var i = 0; i < ifaces.length; i++) {
+      var addr = ifaces[i]
+      if (memberships[addr]) continue
+
+      try {
+        socket.addMembership(ip, addr)
+        memberships[addr] = true
+        updated = true
+      } catch (err) {
+        that.emit('warning', err)
+      }
+    }
+
+    if (updated) {
+      if (socket.setMulticastInterface) {
+        try {
+          socket.setMulticastInterface(opts.interface || defaultInterface())
+        } catch (err) {
+          that.emit('warning', err)
+        }
+      }
+      that.emit('networkInterface')
+    }
+  }
+
+  return that
+}
+
+function defaultInterface () {
+  var networks = os.networkInterfaces()
+  var names = Object.keys(networks)
+
+  for (var i = 0; i < names.length; i++) {
+    var net = networks[names[i]]
+    for (var j = 0; j < net.length; j++) {
+      var iface = net[j]
+      if (isIPv4(iface.family) && !iface.internal) {
+        if (os.platform() === 'darwin' && names[i] === 'en0') return iface.address
+        return '0.0.0.0'
+      }
+    }
+  }
+
+  return '127.0.0.1'
+}
+
+function allInterfaces () {
+  var networks = os.networkInterfaces()
+  var names = Object.keys(networks)
+  var res = []
+
+  for (var i = 0; i < names.length; i++) {
+    var net = networks[names[i]]
+    for (var j = 0; j < net.length; j++) {
+      var iface = net[j]
+      if (isIPv4(iface.family)) {
+        res.push(iface.address)
+        // could only addMembership once per interface (https://nodejs.org/api/dgram.html#dgram_socket_addmembership_multicastaddress_multicastinterface)
+        break
+      }
+    }
+  }
+
+  return res
+}
+
+function isIPv4 (family) { // for backwards compat
+  return family === 4 || family === 'IPv4'
+}
