3 * Copyright(c) 2014 Douglas Christopher Wilson
11 module.exports = proxyaddr;
12 module.exports.all = alladdrs;
13 module.exports.compile = compile;
16 * Module dependencies.
19 var forwarded = require('forwarded');
20 var ipaddr = require('ipaddr.js');
26 var digitre = /^[0-9]+$/;
27 var isip = ipaddr.isValid;
28 var parseip = ipaddr.parse;
31 * Pre-defined IP ranges.
35 linklocal: ['169.254.0.0/16', 'fe80::/10'],
36 loopback: ['127.0.0.1/8', '::1/128'],
37 uniquelocal: ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16', 'fc00::/7']
41 * Get all addresses in the request, optionally stopping
42 * at the first untrusted.
44 * @param {Object} request
45 * @param {Function|Array|String} [trust]
49 function alladdrs(req, trust) {
51 var addrs = forwarded(req);
54 // Return all addresses
58 if (typeof trust !== 'function') {
59 trust = compile(trust);
62 for (var i = 0; i < addrs.length - 1; i++) {
63 if (trust(addrs[i], i)) continue;
72 * Compile argument into trust function.
74 * @param {Array|String} val
78 function compile(val) {
80 throw new TypeError('argument is required');
83 var trust = typeof val === 'string'
87 if (!Array.isArray(trust)) {
88 throw new TypeError('unsupported trust argument');
91 for (var i = 0; i < trust.length; i++) {
94 if (!ipranges.hasOwnProperty(val)) {
98 // Splice in pre-defined range
100 trust.splice.apply(trust, [i, 1].concat(val));
104 return compileTrust(compileRangeSubnets(trust));
108 * Compile `arr` elements into range subnets.
114 function compileRangeSubnets(arr) {
115 var rangeSubnets = new Array(arr.length);
117 for (var i = 0; i < arr.length; i++) {
118 rangeSubnets[i] = parseipNotation(arr[i]);
125 * Compile range subnet array into trust function.
127 * @param {Array} rangeSubnets
131 function compileTrust(rangeSubnets) {
132 // Return optimized function based on length
133 var len = rangeSubnets.length;
137 ? trustSingle(rangeSubnets[0])
138 : trustMulti(rangeSubnets);
142 * Parse IP notation string into range subnet.
144 * @param {String} note
148 function parseipNotation(note) {
152 var pos = note.lastIndexOf('/');
156 ? note.substring(0, pos)
160 throw new TypeError('invalid IP address: ' + ip);
166 max = kind === 'ipv6'
171 ? note.substring(pos + 1, note.length)
174 if (typeof range !== 'number') {
175 range = digitre.test(range)
176 ? parseInt(range, 10)
178 ? parseNetmask(range)
182 if (ip.kind() === 'ipv6' && ip.isIPv4MappedAddress()) {
184 ip = ip.toIPv4Address();
190 if (range <= 0 || range > max) {
191 throw new TypeError('invalid range on address: ' + note);
198 * Parse netmask string into CIDR range.
200 * @param {String} note
204 function parseNetmask(netmask) {
205 var ip = parseip(netmask);
220 var max = Math.pow(2, size) - 1;
224 for (var i = 0; i < parts.length; i++) {
225 part = parts[i] & max;
233 part = (part << 1) & max;
244 * Determine address of proxied request.
246 * @param {Object} request
247 * @param {Function|Array|String} trust
251 function proxyaddr(req, trust) {
253 throw new TypeError('req argument is required');
257 throw new TypeError('trust argument is required');
260 var addrs = alladdrs(req, trust);
261 var addr = addrs[addrs.length - 1];
267 * Static trust function to trust nothing.
272 function trustNone() {
277 * Compile trust function for multiple subnets.
279 * @param {Array} subnets
283 function trustMulti(subnets) {
284 return function trust(addr) {
285 if (!isip(addr)) return false;
287 var ip = parseip(addr);
289 var kind = ip.kind();
296 for (var i = 0; i < subnets.length; i++) {
298 subnetip = subnet[0];
299 subnetkind = subnetip.kind();
300 subnetrange = subnet[1];
303 if (kind !== subnetkind) {
304 if (kind !== 'ipv6' || subnetkind !== 'ipv4' || !ip.isIPv4MappedAddress()) {
308 // Store addr as IPv4
309 ipv4 = ipv4 || ip.toIPv4Address();
313 if (trusted.match(subnetip, subnetrange)) return true;
321 * Compile trust function for single subnet.
323 * @param {Object} subnet
327 function trustSingle(subnet) {
328 var subnetip = subnet[0];
329 var subnetkind = subnetip.kind();
330 var subnetisipv4 = subnetkind === 'ipv4';
331 var subnetrange = subnet[1];
333 return function trust(addr) {
334 if (!isip(addr)) return false;
336 var ip = parseip(addr);
337 var kind = ip.kind();
339 return kind === subnetkind
340 ? ip.match(subnetip, subnetrange)
341 : subnetisipv4 && kind === 'ipv6' && ip.isIPv4MappedAddress()
342 ? ip.toIPv4Address().match(subnetip, subnetrange)