second
[josuexyz/.git] / node_modules / proxy-addr / index.js
1 /*!
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 }