proxy-addr
Advanced tools
Comparing version 0.0.1 to 1.0.0
@@ -0,1 +1,14 @@ | ||
1.0.0 / 2014-05-08 | ||
================== | ||
* Add `trust` argument to determine proxy trust on | ||
* Accepts custom function | ||
* Accepts IPv4/IPv6 address(es) | ||
* Accepts subnets | ||
* Accepts pre-defined names | ||
* Add optional `trust` argument to `proxyaddr.all` to | ||
stop at first untrusted | ||
* Add `proxyaddr.compile` to pre-compile `trust` function | ||
to make subsequent calls faster | ||
0.0.1 / 2014-05-04 | ||
@@ -2,0 +15,0 @@ ================== |
337
index.js
@@ -12,38 +12,343 @@ /*! | ||
module.exports = proxyaddr; | ||
module.exports.all = proxyaddrs; | ||
module.exports.all = alladdrs; | ||
module.exports.compile = compile; | ||
/** | ||
* Determine IP of proxied request. | ||
* | ||
* @param {Object} request | ||
* @api public | ||
* Module dependencies. | ||
*/ | ||
function proxyaddr(req) { | ||
var addrs = proxyaddrs(req); | ||
var ipaddr = require('ipaddr.js'); | ||
return addrs[addrs.length - 1]; | ||
} | ||
/** | ||
* Variables. | ||
*/ | ||
var digitre = /^[0-9]+$/; | ||
var isip = ipaddr.isValid; | ||
var parseip = ipaddr.parse; | ||
/** | ||
* Get all addresses in the request. | ||
* Pre-defined IP ranges. | ||
*/ | ||
var ipranges = { | ||
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] | ||
* @api public | ||
*/ | ||
function proxyaddrs(req) { | ||
if (!req) throw new TypeError('req argument is required'); | ||
function alladdrs(req, trust) { | ||
if (!req) { | ||
throw new TypeError('req argument is required'); | ||
} | ||
var proxyAddrs = (req.headers['x-forwarded-for'] || '') | ||
.split(/ *, */) | ||
.filter(isTruthy) | ||
.filter(Boolean) | ||
.reverse(); | ||
var socketAddr = req.connection.remoteAddress; | ||
var addrs = [socketAddr].concat(proxyAddrs); | ||
return [socketAddr].concat(proxyAddrs); | ||
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; | ||
} | ||
function isTruthy(val) { | ||
return Boolean(val); | ||
/** | ||
* Compile argument into trust function. | ||
* | ||
* @param {Array|String} val | ||
* @api private | ||
*/ | ||
function compile(val) { | ||
if (!val) { | ||
throw new TypeError('argument is required'); | ||
} | ||
var trust = typeof val === 'string' | ||
? [val] | ||
: val; | ||
if (!Array.isArray(trust)) { | ||
throw new TypeError('unsupported trust argument'); | ||
} | ||
for (var i = 0; i < trust.length; i++) { | ||
val = trust[i]; | ||
if (!ipranges.hasOwnProperty(val)) { | ||
continue; | ||
} | ||
// Splice in pre-defined range | ||
val = ipranges[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 | ||
* @api 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 | ||
* @api 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 | ||
* @api private | ||
*/ | ||
function parseipNotation(note) { | ||
var ip; | ||
var kind; | ||
var max; | ||
var pos = note.lastIndexOf('/'); | ||
var range; | ||
ip = pos !== -1 | ||
? note.substring(0, pos) | ||
: note; | ||
if (!isip(ip)) { | ||
throw new TypeError('invalid IP address: ' + ip); | ||
} | ||
ip = parseip(ip); | ||
kind = ip.kind(); | ||
max = kind === 'ipv4' ? 32 | ||
: kind === 'ipv6' ? 128 | ||
: 0; | ||
range = pos !== -1 | ||
? note.substring(pos + 1, note.length) | ||
: max; | ||
if (typeof range !== 'number') { | ||
range = digitre.test(range) | ||
? parseInt(range, 10) | ||
: isip(range) | ||
? parseNetmask(range) | ||
: 0; | ||
} | ||
if (ip.kind() === 'ipv6' && ip.isIPv4MappedAddress()) { | ||
// Store as IPv4 | ||
ip = ip.toIPv4Address(); | ||
range = range <= max | ||
? range - 96 | ||
: range; | ||
} | ||
if (range <= 0 || range > max) { | ||
throw new TypeError('invalid range on address: ' + note); | ||
} | ||
return [ip, range]; | ||
} | ||
/** | ||
* Parse netmask string into CIDR range. | ||
* | ||
* @param {String} note | ||
* @api private | ||
*/ | ||
function parseNetmask(netmask) { | ||
var ip = parseip(netmask); | ||
var parts; | ||
var size; | ||
switch (ip.kind()) { | ||
case 'ipv4': | ||
parts = ip.octets; | ||
size = 8; | ||
break; | ||
case 'ipv6': | ||
parts = ip.parts; | ||
size = 16; | ||
break; | ||
default: | ||
throw new TypeError('unknown netmask'); | ||
} | ||
var max = Math.pow(2, size) - 1; | ||
var part; | ||
var range = 0; | ||
for (var i = 0; i < parts.length; i++) { | ||
part = parts[i] & max; | ||
if (part === max) { | ||
range += size; | ||
continue; | ||
} | ||
while (part) { | ||
part = (part << 1) & max; | ||
range += 1; | ||
} | ||
break; | ||
} | ||
return range; | ||
} | ||
/** | ||
* Determine address of proxied request. | ||
* | ||
* @param {Object} request | ||
* @param {Function|Array|String} trust | ||
* @api 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. | ||
* | ||
* @api private | ||
*/ | ||
function trustNone() { | ||
return false; | ||
} | ||
/** | ||
* Compile trust function for multiple subnets. | ||
* | ||
* @param {Array} subnets | ||
* @api private | ||
*/ | ||
function trustMulti(subnets) { | ||
return function trust(addr) { | ||
if (!isip(addr)) return false; | ||
var ip = parseip(addr); | ||
var ipv4; | ||
var kind = ip.kind(); | ||
var subnet; | ||
var subnetip; | ||
var subnetkind; | ||
var trusted; | ||
for (var i = 0; i < subnets.length; i++) { | ||
subnet = subnets[i]; | ||
subnetip = subnet[0]; | ||
subnetkind = subnetip.kind(); | ||
subnetrange = subnet[1]; | ||
trusted = ip; | ||
if (kind !== subnetkind) { | ||
if (kind !== 'ipv6' || subnetkind !== 'ipv4' || !ip.isIPv4MappedAddress()) { | ||
continue; | ||
} | ||
// Store addr as IPv4 | ||
ipv4 = ipv4 || ip.toIPv4Address(); | ||
trusted = ipv4; | ||
} | ||
if (trusted.match(subnetip, subnetrange)) return true; | ||
} | ||
return false; | ||
}; | ||
} | ||
/** | ||
* Compile trust function for single subnet. | ||
* | ||
* @param {Object} subnet | ||
* @api 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(); | ||
return kind === subnetkind | ||
? ip.match(subnetip, subnetrange) | ||
: subnetisipv4 && kind === 'ipv6' && ip.isIPv4MappedAddress() | ||
? ip.toIPv4Address().match(subnetip, subnetrange) | ||
: false; | ||
}; | ||
} |
{ | ||
"name": "proxy-addr", | ||
"description": "Determine address of proxied request", | ||
"version": "0.0.1", | ||
"version": "1.0.0", | ||
"author": "Douglas Christopher Wilson <doug@somethingdoug.com>", | ||
@@ -19,3 +19,5 @@ "license": "MIT", | ||
}, | ||
"dependencies": {}, | ||
"dependencies": { | ||
"ipaddr.js": "0.1.2" | ||
}, | ||
"devDependencies": { | ||
@@ -22,0 +24,0 @@ "mocha": "~1.18.2", |
@@ -13,14 +13,79 @@ # proxy-addr [![Build Status](https://travis-ci.org/expressjs/proxy-addr.svg?branch=master)](https://travis-ci.org/expressjs/proxy-addr) [![NPM version](https://badge.fury.io/js/proxy-addr.svg)](http://badge.fury.io/js/proxy-addr) | ||
### proxyaddr(req) | ||
### proxyaddr(req, trust) | ||
Return the address of the request. This returns the furthest-away | ||
address. | ||
Return the address of the request, using the given `trust` parameter. | ||
### proxyaddr.all(req) | ||
The `trust` argument is a function that returns `true` if you trust | ||
the address, `false` if you don't. The closest untrusted address is | ||
returned. | ||
Return all the addresses of the request. This array is ordered from | ||
closest to furthest (i.e. `arr[0] === req.connection.remoteAddress`). | ||
proxyaddr(req, function(addr){ return addr === '127.0.0.1' }) | ||
proxyaddr(req, function(addr, i){ return i < 1 }) | ||
The `trust` arugment may also be a single IP address string or an | ||
array of trusted addresses, as plain IP addresses, CIDR-formatted | ||
strings, or IP/netmask strings. | ||
proxyaddr(req, '127.0.0.1') | ||
proxyaddr(req, ['127.0.0.0/8', '10.0.0.0/8']) | ||
proxyaddr(req, ['127.0.0.0/255.0.0.0', '192.168.0.0/255.255.0.0']) | ||
This module also supports IPv6. Your IPv6 addresses will be normalized | ||
automatically (i.e. `fe80::00ed:1` equals `fe80:0:0:0:0:0:ed:1`). | ||
proxyaddr(req, '::1') | ||
proxyaddr(req, ['::1/128', 'fe80::/10']) | ||
proxyaddr(req, ['fe80::/ffc0::']) | ||
This module will automatically work with IPv4-mapped IPv6 addresses | ||
as well to support node.js in IPv6-only mode. This means that you do | ||
not have to specify both `::ffff:a00:1` and `10.0.0.1`. | ||
As a convenience, this module also takes certain pre-defined names | ||
in addition to IP addresses, which expand into IP addresses: | ||
proxyaddr(req, 'loopback') | ||
proxyaddr(req, ['loopback', 'fc00:ac:1ab5:fff::1/64']) | ||
* `loopback`: IPv4 and IPv6 loopback addresses (like `::1` and | ||
`127.0.0.1`). | ||
* `linklocal`: IPv4 and IPv6 link-local addresses (like | ||
`fe80::1:1:1:1` and `169.254.0.1`). | ||
* `uniquelocal`: IPv4 private addresses and IPv6 unique-local | ||
addresses (like `fc00:ac:1ab5:fff::1` and `192.168.0.1`). | ||
When `trust` is specified as a function, it will be called for each | ||
address to determine if it is a trusted address. The function is | ||
given two arguments: `addr` and `i`, where `addr` is a string of | ||
the address to check and `i` is a number that represents the distance | ||
from the socket address. | ||
### proxyaddr.all(req, [trust]) | ||
Return all the addresses of the request, optionally stopping at the | ||
first untrusted. This array is ordered from closest to furthest | ||
(i.e. `arr[0] === req.connection.remoteAddress`). | ||
proxyaddr.all(req) | ||
The optional `trust` argument takes the same arguments as `trust` | ||
does in `proxyaddr(req, trust)`. | ||
proxyaddr.all(req, 'loopback') | ||
### proxyaddr.compile(val) | ||
Compiles argument `val` into a `trust` function. This function takes | ||
the same arguments as `trust` does in `proxyaddr(req, trust)` and | ||
returns a function suitable for `proxyaddr(req, trust)`. | ||
var trust = proxyaddr.compile('localhost') | ||
var addr = proxyaddr(req, trust) | ||
This function is meant to be optimized for use against every request. | ||
It is recommend to compile a trust function up-front for the trusted | ||
configuration and pass that to `proxyaddr(req, trust)` for each request. | ||
## License | ||
[MIT](LICENSE) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
11934
283
1
91
1
1
+ Addedipaddr.js@0.1.2
+ Addedipaddr.js@0.1.2(transitive)