index.js (6000B)
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 }