Projekt

Obecné

Profil

Stáhnout (5.86 KB) Statistiky
| Větev: | Revize:
1 3a515b92 cagy
/*!
2
 * proxy-addr
3
 * Copyright(c) 2014-2016 Douglas Christopher Wilson
4
 * MIT Licensed
5
 */
6
7
'use strict'
8
9
/**
10
 * Module exports.
11
 * @public
12
 */
13
14
module.exports = proxyaddr
15
module.exports.all = alladdrs
16
module.exports.compile = compile
17
18
/**
19
 * Module dependencies.
20
 * @private
21
 */
22
23
var forwarded = require('forwarded')
24
var ipaddr = require('ipaddr.js')
25
26
/**
27
 * Variables.
28
 * @private
29
 */
30
31
var DIGIT_REGEXP = /^[0-9]+$/
32
var isip = ipaddr.isValid
33
var parseip = ipaddr.parse
34
35
/**
36
 * Pre-defined IP ranges.
37
 * @private
38
 */
39
40
var IP_RANGES = {
41
  linklocal: ['169.254.0.0/16', 'fe80::/10'],
42
  loopback: ['127.0.0.1/8', '::1/128'],
43
  uniquelocal: ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16', 'fc00::/7']
44
}
45
46
/**
47
 * Get all addresses in the request, optionally stopping
48
 * at the first untrusted.
49
 *
50
 * @param {Object} request
51
 * @param {Function|Array|String} [trust]
52
 * @public
53
 */
54
55
function alladdrs (req, trust) {
56
  // get addresses
57
  var addrs = forwarded(req)
58
59
  if (!trust) {
60
    // Return all addresses
61
    return addrs
62
  }
63
64
  if (typeof trust !== 'function') {
65
    trust = compile(trust)
66
  }
67
68
  for (var i = 0; i < addrs.length - 1; i++) {
69
    if (trust(addrs[i], i)) continue
70
71
    addrs.length = i + 1
72
  }
73
74
  return addrs
75
}
76
77
/**
78
 * Compile argument into trust function.
79
 *
80
 * @param {Array|String} val
81
 * @private
82
 */
83
84
function compile (val) {
85
  if (!val) {
86
    throw new TypeError('argument is required')
87
  }
88
89
  var trust
90
91
  if (typeof val === 'string') {
92
    trust = [val]
93
  } else if (Array.isArray(val)) {
94
    trust = val.slice()
95
  } else {
96
    throw new TypeError('unsupported trust argument')
97
  }
98
99
  for (var i = 0; i < trust.length; i++) {
100
    val = trust[i]
101
102
    if (!Object.prototype.hasOwnProperty.call(IP_RANGES, val)) {
103
      continue
104
    }
105
106
    // Splice in pre-defined range
107
    val = IP_RANGES[val]
108
    trust.splice.apply(trust, [i, 1].concat(val))
109
    i += val.length - 1
110
  }
111
112
  return compileTrust(compileRangeSubnets(trust))
113
}
114
115
/**
116
 * Compile `arr` elements into range subnets.
117
 *
118
 * @param {Array} arr
119
 * @private
120
 */
121
122
function compileRangeSubnets (arr) {
123
  var rangeSubnets = new Array(arr.length)
124
125
  for (var i = 0; i < arr.length; i++) {
126
    rangeSubnets[i] = parseipNotation(arr[i])
127
  }
128
129
  return rangeSubnets
130
}
131
132
/**
133
 * Compile range subnet array into trust function.
134
 *
135
 * @param {Array} rangeSubnets
136
 * @private
137
 */
138
139
function compileTrust (rangeSubnets) {
140
  // Return optimized function based on length
141
  var len = rangeSubnets.length
142
  return len === 0
143
    ? trustNone
144
    : len === 1
145
      ? trustSingle(rangeSubnets[0])
146
      : trustMulti(rangeSubnets)
147
}
148
149
/**
150
 * Parse IP notation string into range subnet.
151
 *
152
 * @param {String} note
153
 * @private
154
 */
155
156
function parseipNotation (note) {
157
  var pos = note.lastIndexOf('/')
158
  var str = pos !== -1
159
    ? note.substring(0, pos)
160
    : note
161
162
  if (!isip(str)) {
163
    throw new TypeError('invalid IP address: ' + str)
164
  }
165
166
  var ip = parseip(str)
167
168
  if (pos === -1 && ip.kind() === 'ipv6' && ip.isIPv4MappedAddress()) {
169
    // Store as IPv4
170
    ip = ip.toIPv4Address()
171
  }
172
173
  var max = ip.kind() === 'ipv6'
174
    ? 128
175
    : 32
176
177
  var range = pos !== -1
178
    ? note.substring(pos + 1, note.length)
179
    : null
180
181
  if (range === null) {
182
    range = max
183
  } else if (DIGIT_REGEXP.test(range)) {
184
    range = parseInt(range, 10)
185
  } else if (ip.kind() === 'ipv4' && isip(range)) {
186
    range = parseNetmask(range)
187
  } else {
188
    range = null
189
  }
190
191
  if (range <= 0 || range > max) {
192
    throw new TypeError('invalid range on address: ' + note)
193
  }
194
195
  return [ip, range]
196
}
197
198
/**
199
 * Parse netmask string into CIDR range.
200
 *
201
 * @param {String} netmask
202
 * @private
203
 */
204
205
function parseNetmask (netmask) {
206
  var ip = parseip(netmask)
207
  var kind = ip.kind()
208
209
  return kind === 'ipv4'
210
    ? ip.prefixLengthFromSubnetMask()
211
    : null
212
}
213
214
/**
215
 * Determine address of proxied request.
216
 *
217
 * @param {Object} request
218
 * @param {Function|Array|String} trust
219
 * @public
220
 */
221
222
function proxyaddr (req, trust) {
223
  if (!req) {
224
    throw new TypeError('req argument is required')
225
  }
226
227
  if (!trust) {
228
    throw new TypeError('trust argument is required')
229
  }
230
231
  var addrs = alladdrs(req, trust)
232
  var addr = addrs[addrs.length - 1]
233
234
  return addr
235
}
236
237
/**
238
 * Static trust function to trust nothing.
239
 *
240
 * @private
241
 */
242
243
function trustNone () {
244
  return false
245
}
246
247
/**
248
 * Compile trust function for multiple subnets.
249
 *
250
 * @param {Array} subnets
251
 * @private
252
 */
253
254
function trustMulti (subnets) {
255
  return function trust (addr) {
256
    if (!isip(addr)) return false
257
258
    var ip = parseip(addr)
259
    var ipconv
260
    var kind = ip.kind()
261
262
    for (var i = 0; i < subnets.length; i++) {
263
      var subnet = subnets[i]
264
      var subnetip = subnet[0]
265
      var subnetkind = subnetip.kind()
266
      var subnetrange = subnet[1]
267
      var trusted = ip
268
269
      if (kind !== subnetkind) {
270
        if (subnetkind === 'ipv4' && !ip.isIPv4MappedAddress()) {
271
          // Incompatible IP addresses
272
          continue
273
        }
274
275
        if (!ipconv) {
276
          // Convert IP to match subnet IP kind
277
          ipconv = subnetkind === 'ipv4'
278
            ? ip.toIPv4Address()
279
            : ip.toIPv4MappedAddress()
280
        }
281
282
        trusted = ipconv
283
      }
284
285
      if (trusted.match(subnetip, subnetrange)) {
286
        return true
287
      }
288
    }
289
290
    return false
291
  }
292
}
293
294
/**
295
 * Compile trust function for single subnet.
296
 *
297
 * @param {Object} subnet
298
 * @private
299
 */
300
301
function trustSingle (subnet) {
302
  var subnetip = subnet[0]
303
  var subnetkind = subnetip.kind()
304
  var subnetisipv4 = subnetkind === 'ipv4'
305
  var subnetrange = subnet[1]
306
307
  return function trust (addr) {
308
    if (!isip(addr)) return false
309
310
    var ip = parseip(addr)
311
    var kind = ip.kind()
312
313
    if (kind !== subnetkind) {
314
      if (subnetisipv4 && !ip.isIPv4MappedAddress()) {
315
        // Incompatible IP addresses
316
        return false
317
      }
318
319
      // Convert IP to match subnet IP kind
320
      ip = subnetisipv4
321
        ? ip.toIPv4Address()
322
        : ip.toIPv4MappedAddress()
323
    }
324
325
    return ip.match(subnetip, subnetrange)
326
  }
327
}