twitst4tz

twitter statistics web application
Log | Files | Refs | README | LICENSE

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 }