You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							327 lines
						
					
					
						
							5.9 KiB
						
					
					
				
			
		
		
	
	
							327 lines
						
					
					
						
							5.9 KiB
						
					
					
				/*! | 
						|
 * proxy-addr | 
						|
 * Copyright(c) 2014-2016 Douglas Christopher Wilson | 
						|
 * MIT Licensed | 
						|
 */ | 
						|
 | 
						|
'use strict' | 
						|
 | 
						|
/** | 
						|
 * Module exports. | 
						|
 * @public | 
						|
 */ | 
						|
 | 
						|
module.exports = proxyaddr | 
						|
module.exports.all = alladdrs | 
						|
module.exports.compile = compile | 
						|
 | 
						|
/** | 
						|
 * Module dependencies. | 
						|
 * @private | 
						|
 */ | 
						|
 | 
						|
var forwarded = require('forwarded') | 
						|
var ipaddr = require('ipaddr.js') | 
						|
 | 
						|
/** | 
						|
 * Variables. | 
						|
 * @private | 
						|
 */ | 
						|
 | 
						|
var DIGIT_REGEXP = /^[0-9]+$/ | 
						|
var isip = ipaddr.isValid | 
						|
var parseip = ipaddr.parse | 
						|
 | 
						|
/** | 
						|
 * Pre-defined IP ranges. | 
						|
 * @private | 
						|
 */ | 
						|
 | 
						|
var IP_RANGES = { | 
						|
  linklocal: ['169.254.0.0/16', 'fe80::/10'], | 
						|
  loopback: ['127.0.0.1/8', '::1/128'], | 
						|
  uniquelocal: ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16', 'fc00::/7'] | 
						|
} | 
						|
 | 
						|
/** | 
						|
 * Get all addresses in the request, optionally stopping | 
						|
 * at the first untrusted. | 
						|
 * | 
						|
 * @param {Object} request | 
						|
 * @param {Function|Array|String} [trust] | 
						|
 * @public | 
						|
 */ | 
						|
 | 
						|
function alladdrs (req, trust) { | 
						|
  // get addresses | 
						|
  var addrs = forwarded(req) | 
						|
 | 
						|
  if (!trust) { | 
						|
    // Return all addresses | 
						|
    return addrs | 
						|
  } | 
						|
 | 
						|
  if (typeof trust !== 'function') { | 
						|
    trust = compile(trust) | 
						|
  } | 
						|
 | 
						|
  for (var i = 0; i < addrs.length - 1; i++) { | 
						|
    if (trust(addrs[i], i)) continue | 
						|
 | 
						|
    addrs.length = i + 1 | 
						|
  } | 
						|
 | 
						|
  return addrs | 
						|
} | 
						|
 | 
						|
/** | 
						|
 * Compile argument into trust function. | 
						|
 * | 
						|
 * @param {Array|String} val | 
						|
 * @private | 
						|
 */ | 
						|
 | 
						|
function compile (val) { | 
						|
  if (!val) { | 
						|
    throw new TypeError('argument is required') | 
						|
  } | 
						|
 | 
						|
  var trust | 
						|
 | 
						|
  if (typeof val === 'string') { | 
						|
    trust = [val] | 
						|
  } else if (Array.isArray(val)) { | 
						|
    trust = val.slice() | 
						|
  } else { | 
						|
    throw new TypeError('unsupported trust argument') | 
						|
  } | 
						|
 | 
						|
  for (var i = 0; i < trust.length; i++) { | 
						|
    val = trust[i] | 
						|
 | 
						|
    if (!Object.prototype.hasOwnProperty.call(IP_RANGES, val)) { | 
						|
      continue | 
						|
    } | 
						|
 | 
						|
    // Splice in pre-defined range | 
						|
    val = IP_RANGES[val] | 
						|
    trust.splice.apply(trust, [i, 1].concat(val)) | 
						|
    i += val.length - 1 | 
						|
  } | 
						|
 | 
						|
  return compileTrust(compileRangeSubnets(trust)) | 
						|
} | 
						|
 | 
						|
/** | 
						|
 * Compile `arr` elements into range subnets. | 
						|
 * | 
						|
 * @param {Array} arr | 
						|
 * @private | 
						|
 */ | 
						|
 | 
						|
function compileRangeSubnets (arr) { | 
						|
  var rangeSubnets = new Array(arr.length) | 
						|
 | 
						|
  for (var i = 0; i < arr.length; i++) { | 
						|
    rangeSubnets[i] = parseipNotation(arr[i]) | 
						|
  } | 
						|
 | 
						|
  return rangeSubnets | 
						|
} | 
						|
 | 
						|
/** | 
						|
 * Compile range subnet array into trust function. | 
						|
 * | 
						|
 * @param {Array} rangeSubnets | 
						|
 * @private | 
						|
 */ | 
						|
 | 
						|
function compileTrust (rangeSubnets) { | 
						|
  // Return optimized function based on length | 
						|
  var len = rangeSubnets.length | 
						|
  return len === 0 | 
						|
    ? trustNone | 
						|
    : len === 1 | 
						|
      ? trustSingle(rangeSubnets[0]) | 
						|
      : trustMulti(rangeSubnets) | 
						|
} | 
						|
 | 
						|
/** | 
						|
 * Parse IP notation string into range subnet. | 
						|
 * | 
						|
 * @param {String} note | 
						|
 * @private | 
						|
 */ | 
						|
 | 
						|
function parseipNotation (note) { | 
						|
  var pos = note.lastIndexOf('/') | 
						|
  var str = pos !== -1 | 
						|
    ? note.substring(0, pos) | 
						|
    : note | 
						|
 | 
						|
  if (!isip(str)) { | 
						|
    throw new TypeError('invalid IP address: ' + str) | 
						|
  } | 
						|
 | 
						|
  var ip = parseip(str) | 
						|
 | 
						|
  if (pos === -1 && ip.kind() === 'ipv6' && ip.isIPv4MappedAddress()) { | 
						|
    // Store as IPv4 | 
						|
    ip = ip.toIPv4Address() | 
						|
  } | 
						|
 | 
						|
  var max = ip.kind() === 'ipv6' | 
						|
    ? 128 | 
						|
    : 32 | 
						|
 | 
						|
  var range = pos !== -1 | 
						|
    ? note.substring(pos + 1, note.length) | 
						|
    : null | 
						|
 | 
						|
  if (range === null) { | 
						|
    range = max | 
						|
  } else if (DIGIT_REGEXP.test(range)) { | 
						|
    range = parseInt(range, 10) | 
						|
  } else if (ip.kind() === 'ipv4' && isip(range)) { | 
						|
    range = parseNetmask(range) | 
						|
  } else { | 
						|
    range = null | 
						|
  } | 
						|
 | 
						|
  if (range <= 0 || range > max) { | 
						|
    throw new TypeError('invalid range on address: ' + note) | 
						|
  } | 
						|
 | 
						|
  return [ip, range] | 
						|
} | 
						|
 | 
						|
/** | 
						|
 * Parse netmask string into CIDR range. | 
						|
 * | 
						|
 * @param {String} netmask | 
						|
 * @private | 
						|
 */ | 
						|
 | 
						|
function parseNetmask (netmask) { | 
						|
  var ip = parseip(netmask) | 
						|
  var kind = ip.kind() | 
						|
 | 
						|
  return kind === 'ipv4' | 
						|
    ? ip.prefixLengthFromSubnetMask() | 
						|
    : null | 
						|
} | 
						|
 | 
						|
/** | 
						|
 * Determine address of proxied request. | 
						|
 * | 
						|
 * @param {Object} request | 
						|
 * @param {Function|Array|String} trust | 
						|
 * @public | 
						|
 */ | 
						|
 | 
						|
function proxyaddr (req, trust) { | 
						|
  if (!req) { | 
						|
    throw new TypeError('req argument is required') | 
						|
  } | 
						|
 | 
						|
  if (!trust) { | 
						|
    throw new TypeError('trust argument is required') | 
						|
  } | 
						|
 | 
						|
  var addrs = alladdrs(req, trust) | 
						|
  var addr = addrs[addrs.length - 1] | 
						|
 | 
						|
  return addr | 
						|
} | 
						|
 | 
						|
/** | 
						|
 * Static trust function to trust nothing. | 
						|
 * | 
						|
 * @private | 
						|
 */ | 
						|
 | 
						|
function trustNone () { | 
						|
  return false | 
						|
} | 
						|
 | 
						|
/** | 
						|
 * Compile trust function for multiple subnets. | 
						|
 * | 
						|
 * @param {Array} subnets | 
						|
 * @private | 
						|
 */ | 
						|
 | 
						|
function trustMulti (subnets) { | 
						|
  return function trust (addr) { | 
						|
    if (!isip(addr)) return false | 
						|
 | 
						|
    var ip = parseip(addr) | 
						|
    var ipconv | 
						|
    var kind = ip.kind() | 
						|
 | 
						|
    for (var i = 0; i < subnets.length; i++) { | 
						|
      var subnet = subnets[i] | 
						|
      var subnetip = subnet[0] | 
						|
      var subnetkind = subnetip.kind() | 
						|
      var subnetrange = subnet[1] | 
						|
      var trusted = ip | 
						|
 | 
						|
      if (kind !== subnetkind) { | 
						|
        if (subnetkind === 'ipv4' && !ip.isIPv4MappedAddress()) { | 
						|
          // Incompatible IP addresses | 
						|
          continue | 
						|
        } | 
						|
 | 
						|
        if (!ipconv) { | 
						|
          // Convert IP to match subnet IP kind | 
						|
          ipconv = subnetkind === 'ipv4' | 
						|
            ? ip.toIPv4Address() | 
						|
            : ip.toIPv4MappedAddress() | 
						|
        } | 
						|
 | 
						|
        trusted = ipconv | 
						|
      } | 
						|
 | 
						|
      if (trusted.match(subnetip, subnetrange)) { | 
						|
        return true | 
						|
      } | 
						|
    } | 
						|
 | 
						|
    return false | 
						|
  } | 
						|
} | 
						|
 | 
						|
/** | 
						|
 * Compile trust function for single subnet. | 
						|
 * | 
						|
 * @param {Object} subnet | 
						|
 * @private | 
						|
 */ | 
						|
 | 
						|
function trustSingle (subnet) { | 
						|
  var subnetip = subnet[0] | 
						|
  var subnetkind = subnetip.kind() | 
						|
  var subnetisipv4 = subnetkind === 'ipv4' | 
						|
  var subnetrange = subnet[1] | 
						|
 | 
						|
  return function trust (addr) { | 
						|
    if (!isip(addr)) return false | 
						|
 | 
						|
    var ip = parseip(addr) | 
						|
    var kind = ip.kind() | 
						|
 | 
						|
    if (kind !== subnetkind) { | 
						|
      if (subnetisipv4 && !ip.isIPv4MappedAddress()) { | 
						|
        // Incompatible IP addresses | 
						|
        return false | 
						|
      } | 
						|
 | 
						|
      // Convert IP to match subnet IP kind | 
						|
      ip = subnetisipv4 | 
						|
        ? ip.toIPv4Address() | 
						|
        : ip.toIPv4MappedAddress() | 
						|
    } | 
						|
 | 
						|
    return ip.match(subnetip, subnetrange) | 
						|
  } | 
						|
}
 | 
						|
 |