Projekt

Obecné

Profil

Stáhnout (4.73 KB) Statistiky
| Větev: | Revize:
1
var packet = require('dns-packet')
2
var dgram = require('dgram')
3
var thunky = require('thunky')
4
var events = require('events')
5
var os = require('os')
6

    
7
var noop = function () {}
8

    
9
module.exports = function (opts) {
10
  if (!opts) opts = {}
11

    
12
  var that = new events.EventEmitter()
13
  var port = typeof opts.port === 'number' ? opts.port : 5353
14
  var type = opts.type || 'udp4'
15
  var ip = opts.ip || opts.host || (type === 'udp4' ? '224.0.0.251' : null)
16
  var me = {address: ip, port: port}
17
  var memberships = {}
18
  var destroyed = false
19
  var interval = null
20

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

    
25
  var socket = opts.socket || dgram.createSocket({
26
    type: type,
27
    reuseAddr: opts.reuseAddr !== false,
28
    toString: function () {
29
      return type
30
    }
31
  })
32

    
33
  socket.on('error', function (err) {
34
    if (err.code === 'EACCES' || err.code === 'EADDRINUSE') that.emit('error', err)
35
    else that.emit('warning', err)
36
  })
37

    
38
  socket.on('message', function (message, rinfo) {
39
    try {
40
      message = packet.decode(message)
41
    } catch (err) {
42
      that.emit('warning', err)
43
      return
44
    }
45

    
46
    that.emit('packet', message, rinfo)
47

    
48
    if (message.type === 'query') that.emit('query', message, rinfo)
49
    if (message.type === 'response') that.emit('response', message, rinfo)
50
  })
51

    
52
  socket.on('listening', function () {
53
    if (!port) port = me.port = socket.address().port
54
    if (opts.multicast !== false) {
55
      that.update()
56
      interval = setInterval(that.update, 5000)
57
      socket.setMulticastTTL(opts.ttl || 255)
58
      socket.setMulticastLoopback(opts.loopback !== false)
59
    }
60
  })
61

    
62
  var bind = thunky(function (cb) {
63
    if (!port) return cb(null)
64
    socket.once('error', cb)
65
    socket.bind(port, opts.interface, function () {
66
      socket.removeListener('error', cb)
67
      cb(null)
68
    })
69
  })
70

    
71
  bind(function (err) {
72
    if (err) return that.emit('error', err)
73
    that.emit('ready')
74
  })
75

    
76
  that.send = function (value, rinfo, cb) {
77
    if (typeof rinfo === 'function') return that.send(value, null, rinfo)
78
    if (!cb) cb = noop
79
    if (!rinfo) rinfo = me
80

    
81
    bind(onbind)
82

    
83
    function onbind (err) {
84
      if (destroyed) return cb()
85
      if (err) return cb(err)
86
      var message = packet.encode(value)
87
      socket.send(message, 0, message.length, rinfo.port, rinfo.address || rinfo.host, cb)
88
    }
89
  }
90

    
91
  that.response =
92
  that.respond = function (res, rinfo, cb) {
93
    if (Array.isArray(res)) res = {answers: res}
94

    
95
    res.type = 'response'
96
    res.flags = (res.flags || 0) | packet.AUTHORITATIVE_ANSWER
97
    that.send(res, rinfo, cb)
98
  }
99

    
100
  that.query = function (q, type, rinfo, cb) {
101
    if (typeof type === 'function') return that.query(q, null, null, type)
102
    if (typeof type === 'object' && type && type.port) return that.query(q, null, type, rinfo)
103
    if (typeof rinfo === 'function') return that.query(q, type, null, rinfo)
104
    if (!cb) cb = noop
105

    
106
    if (typeof q === 'string') q = [{name: q, type: type || 'ANY'}]
107
    if (Array.isArray(q)) q = {type: 'query', questions: q}
108

    
109
    q.type = 'query'
110
    that.send(q, rinfo, cb)
111
  }
112

    
113
  that.destroy = function (cb) {
114
    if (!cb) cb = noop
115
    if (destroyed) return process.nextTick(cb)
116
    destroyed = true
117
    clearInterval(interval)
118
    socket.once('close', cb)
119
    socket.close()
120
  }
121

    
122
  that.update = function () {
123
    var ifaces = opts.interface ? [].concat(opts.interface) : allInterfaces()
124
    var updated = false
125

    
126
    for (var i = 0; i < ifaces.length; i++) {
127
      var addr = ifaces[i]
128

    
129
      if (memberships[addr]) continue
130
      memberships[addr] = true
131
      updated = true
132

    
133
      try {
134
        socket.addMembership(ip, addr)
135
      } catch (err) {
136
        that.emit('warning', err)
137
      }
138
    }
139

    
140
    if (!updated || !socket.setMulticastInterface) return
141
    socket.setMulticastInterface(opts.interface || defaultInterface())
142
  }
143

    
144
  return that
145
}
146

    
147
function defaultInterface () {
148
  var networks = os.networkInterfaces()
149
  var names = Object.keys(networks)
150

    
151
  for (var i = 0; i < names.length; i++) {
152
    var net = networks[names[i]]
153
    for (var j = 0; j < net.length; j++) {
154
      var iface = net[j]
155
      if (iface.family === 'IPv4' && !iface.internal) return iface.address
156
    }
157
  }
158

    
159
  return '127.0.0.1'
160
}
161

    
162
function allInterfaces () {
163
  var networks = os.networkInterfaces()
164
  var names = Object.keys(networks)
165
  var res = []
166

    
167
  for (var i = 0; i < names.length; i++) {
168
    var net = networks[names[i]]
169
    for (var j = 0; j < net.length; j++) {
170
      var iface = net[j]
171
      if (iface.family === 'IPv4') {
172
        res.push(iface.address)
173
        // could only addMembership once per interface (https://nodejs.org/api/dgram.html#dgram_socket_addmembership_multicastaddress_multicastinterface)
174
        break
175
      }
176
    }
177
  }
178

    
179
  return res
180
}
(6-6/8)