3 * Copyright(c) 2014-2016 Douglas Christopher Wilson
14 module.exports = proxyaddr
15 module.exports.all = alladdrs
16 module.exports.compile = compile
19 * Module dependencies.
23 var forwarded = require('forwarded')
24 var ipaddr = require('ipaddr.js')
31 var DIGIT_REGEXP = /^[0-9]+$/
32 var isip = ipaddr.isValid
33 var parseip = ipaddr.parse
36 * Pre-defined 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']
47 * Get all addresses in the request, optionally stopping
48 * at the first untrusted.
50 * @param {Object} request
51 * @param {Function|Array|String} [trust]
55 function alladdrs (req, trust) {
57 var addrs = forwarded(req)
60 // Return all addresses
64 if (typeof trust !== 'function') {
65 trust = compile(trust)
68 for (var i = 0; i < addrs.length - 1; i++) {
69 if (trust(addrs[i], i)) continue
78 * Compile argument into trust function.
80 * @param {Array|String} val
84 function compile (val) {
86 throw new TypeError('argument is required')
91 if (typeof val === 'string') {
93 } else if (Array.isArray(val)) {
96 throw new TypeError('unsupported trust argument')
99 for (var i = 0; i < trust.length; i++) {
102 if (!IP_RANGES.hasOwnProperty(val)) {
106 // Splice in pre-defined range
108 trust.splice.apply(trust, [i, 1].concat(val))
112 return compileTrust(compileRangeSubnets(trust))
116 * Compile `arr` elements into range subnets.
122 function compileRangeSubnets (arr) {
123 var rangeSubnets = new Array(arr.length)
125 for (var i = 0; i < arr.length; i++) {
126 rangeSubnets[i] = parseipNotation(arr[i])
133 * Compile range subnet array into trust function.
135 * @param {Array} rangeSubnets
139 function compileTrust (rangeSubnets) {
140 // Return optimized function based on length
141 var len = rangeSubnets.length
145 ? trustSingle(rangeSubnets[0])
146 : trustMulti(rangeSubnets)
150 * Parse IP notation string into range subnet.
152 * @param {String} note
156 function parseipNotation (note) {
157 var pos = note.lastIndexOf('/')
159 ? note.substring(0, pos)
163 throw new TypeError('invalid IP address: ' + str)
166 var ip = parseip(str)
168 if (pos === -1 && ip.kind() === 'ipv6' && ip.isIPv4MappedAddress()) {
170 ip = ip.toIPv4Address()
173 var max = ip.kind() === 'ipv6'
177 var range = pos !== -1
178 ? note.substring(pos + 1, note.length)
181 if (range === null) {
183 } else if (DIGIT_REGEXP.test(range)) {
184 range = parseInt(range, 10)
185 } else if (ip.kind() === 'ipv4' && isip(range)) {
186 range = parseNetmask(range)
191 if (range <= 0 || range > max) {
192 throw new TypeError('invalid range on address: ' + note)
199 * Parse netmask string into CIDR range.
201 * @param {String} netmask
205 function parseNetmask (netmask) {
206 var ip = parseip(netmask)
209 return kind === 'ipv4'
210 ? ip.prefixLengthFromSubnetMask()
215 * Determine address of proxied request.
217 * @param {Object} request
218 * @param {Function|Array|String} trust
222 function proxyaddr (req, trust) {
224 throw new TypeError('req argument is required')
228 throw new TypeError('trust argument is required')
231 var addrs = alladdrs(req, trust)
232 var addr = addrs[addrs.length - 1]
238 * Static trust function to trust nothing.
243 function trustNone () {
248 * Compile trust function for multiple subnets.
250 * @param {Array} subnets
254 function trustMulti (subnets) {
255 return function trust (addr) {
256 if (!isip(addr)) return false
258 var ip = parseip(addr)
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]
269 if (kind !== subnetkind) {
270 if (subnetkind === 'ipv4' && !ip.isIPv4MappedAddress()) {
271 // Incompatible IP addresses
276 // Convert IP to match subnet IP kind
277 ipconv = subnetkind === 'ipv4'
279 : ip.toIPv4MappedAddress()
285 if (trusted.match(subnetip, subnetrange)) {
295 * Compile trust function for single subnet.
297 * @param {Object} subnet
301 function trustSingle (subnet) {
302 var subnetip = subnet[0]
303 var subnetkind = subnetip.kind()
304 var subnetisipv4 = subnetkind === 'ipv4'
305 var subnetrange = subnet[1]
307 return function trust (addr) {
308 if (!isip(addr)) return false
310 var ip = parseip(addr)
313 if (kind !== subnetkind) {
314 if (subnetisipv4 && !ip.isIPv4MappedAddress()) {
315 // Incompatible IP addresses
319 // Convert IP to match subnet IP kind
322 : ip.toIPv4MappedAddress()
325 return ip.match(subnetip, subnetrange)