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.8 KiB

4 years ago
  1. /*!
  2. * proxy-addr
  3. * Copyright(c) 2014-2016 Douglas Christopher Wilson
  4. * MIT Licensed
  5. */
  6. 'use strict'
  7. /**
  8. * Module exports.
  9. * @public
  10. */
  11. module.exports = proxyaddr
  12. module.exports.all = alladdrs
  13. module.exports.compile = compile
  14. /**
  15. * Module dependencies.
  16. * @private
  17. */
  18. var forwarded = require('forwarded')
  19. var ipaddr = require('ipaddr.js')
  20. /**
  21. * Variables.
  22. * @private
  23. */
  24. var DIGIT_REGEXP = /^[0-9]+$/
  25. var isip = ipaddr.isValid
  26. var parseip = ipaddr.parse
  27. /**
  28. * Pre-defined IP ranges.
  29. * @private
  30. */
  31. var IP_RANGES = {
  32. linklocal: ['169.254.0.0/16', 'fe80::/10'],
  33. loopback: ['127.0.0.1/8', '::1/128'],
  34. uniquelocal: ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16', 'fc00::/7']
  35. }
  36. /**
  37. * Get all addresses in the request, optionally stopping
  38. * at the first untrusted.
  39. *
  40. * @param {Object} request
  41. * @param {Function|Array|String} [trust]
  42. * @public
  43. */
  44. function alladdrs (req, trust) {
  45. // get addresses
  46. var addrs = forwarded(req)
  47. if (!trust) {
  48. // Return all addresses
  49. return addrs
  50. }
  51. if (typeof trust !== 'function') {
  52. trust = compile(trust)
  53. }
  54. for (var i = 0; i < addrs.length - 1; i++) {
  55. if (trust(addrs[i], i)) continue
  56. addrs.length = i + 1
  57. }
  58. return addrs
  59. }
  60. /**
  61. * Compile argument into trust function.
  62. *
  63. * @param {Array|String} val
  64. * @private
  65. */
  66. function compile (val) {
  67. if (!val) {
  68. throw new TypeError('argument is required')
  69. }
  70. var trust
  71. if (typeof val === 'string') {
  72. trust = [val]
  73. } else if (Array.isArray(val)) {
  74. trust = val.slice()
  75. } else {
  76. throw new TypeError('unsupported trust argument')
  77. }
  78. for (var i = 0; i < trust.length; i++) {
  79. val = trust[i]
  80. if (!IP_RANGES.hasOwnProperty(val)) {
  81. continue
  82. }
  83. // Splice in pre-defined range
  84. val = IP_RANGES[val]
  85. trust.splice.apply(trust, [i, 1].concat(val))
  86. i += val.length - 1
  87. }
  88. return compileTrust(compileRangeSubnets(trust))
  89. }
  90. /**
  91. * Compile `arr` elements into range subnets.
  92. *
  93. * @param {Array} arr
  94. * @private
  95. */
  96. function compileRangeSubnets (arr) {
  97. var rangeSubnets = new Array(arr.length)
  98. for (var i = 0; i < arr.length; i++) {
  99. rangeSubnets[i] = parseipNotation(arr[i])
  100. }
  101. return rangeSubnets
  102. }
  103. /**
  104. * Compile range subnet array into trust function.
  105. *
  106. * @param {Array} rangeSubnets
  107. * @private
  108. */
  109. function compileTrust (rangeSubnets) {
  110. // Return optimized function based on length
  111. var len = rangeSubnets.length
  112. return len === 0
  113. ? trustNone
  114. : len === 1
  115. ? trustSingle(rangeSubnets[0])
  116. : trustMulti(rangeSubnets)
  117. }
  118. /**
  119. * Parse IP notation string into range subnet.
  120. *
  121. * @param {String} note
  122. * @private
  123. */
  124. function parseipNotation (note) {
  125. var pos = note.lastIndexOf('/')
  126. var str = pos !== -1
  127. ? note.substring(0, pos)
  128. : note
  129. if (!isip(str)) {
  130. throw new TypeError('invalid IP address: ' + str)
  131. }
  132. var ip = parseip(str)
  133. if (pos === -1 && ip.kind() === 'ipv6' && ip.isIPv4MappedAddress()) {
  134. // Store as IPv4
  135. ip = ip.toIPv4Address()
  136. }
  137. var max = ip.kind() === 'ipv6'
  138. ? 128
  139. : 32
  140. var range = pos !== -1
  141. ? note.substring(pos + 1, note.length)
  142. : null
  143. if (range === null) {
  144. range = max
  145. } else if (DIGIT_REGEXP.test(range)) {
  146. range = parseInt(range, 10)
  147. } else if (ip.kind() === 'ipv4' && isip(range)) {
  148. range = parseNetmask(range)
  149. } else {
  150. range = null
  151. }
  152. if (range <= 0 || range > max) {
  153. throw new TypeError('invalid range on address: ' + note)
  154. }
  155. return [ip, range]
  156. }
  157. /**
  158. * Parse netmask string into CIDR range.
  159. *
  160. * @param {String} netmask
  161. * @private
  162. */
  163. function parseNetmask (netmask) {
  164. var ip = parseip(netmask)
  165. var kind = ip.kind()
  166. return kind === 'ipv4'
  167. ? ip.prefixLengthFromSubnetMask()
  168. : null
  169. }
  170. /**
  171. * Determine address of proxied request.
  172. *
  173. * @param {Object} request
  174. * @param {Function|Array|String} trust
  175. * @public
  176. */
  177. function proxyaddr (req, trust) {
  178. if (!req) {
  179. throw new TypeError('req argument is required')
  180. }
  181. if (!trust) {
  182. throw new TypeError('trust argument is required')
  183. }
  184. var addrs = alladdrs(req, trust)
  185. var addr = addrs[addrs.length - 1]
  186. return addr
  187. }
  188. /**
  189. * Static trust function to trust nothing.
  190. *
  191. * @private
  192. */
  193. function trustNone () {
  194. return false
  195. }
  196. /**
  197. * Compile trust function for multiple subnets.
  198. *
  199. * @param {Array} subnets
  200. * @private
  201. */
  202. function trustMulti (subnets) {
  203. return function trust (addr) {
  204. if (!isip(addr)) return false
  205. var ip = parseip(addr)
  206. var ipconv
  207. var kind = ip.kind()
  208. for (var i = 0; i < subnets.length; i++) {
  209. var subnet = subnets[i]
  210. var subnetip = subnet[0]
  211. var subnetkind = subnetip.kind()
  212. var subnetrange = subnet[1]
  213. var trusted = ip
  214. if (kind !== subnetkind) {
  215. if (subnetkind === 'ipv4' && !ip.isIPv4MappedAddress()) {
  216. // Incompatible IP addresses
  217. continue
  218. }
  219. if (!ipconv) {
  220. // Convert IP to match subnet IP kind
  221. ipconv = subnetkind === 'ipv4'
  222. ? ip.toIPv4Address()
  223. : ip.toIPv4MappedAddress()
  224. }
  225. trusted = ipconv
  226. }
  227. if (trusted.match(subnetip, subnetrange)) {
  228. return true
  229. }
  230. }
  231. return false
  232. }
  233. }
  234. /**
  235. * Compile trust function for single subnet.
  236. *
  237. * @param {Object} subnet
  238. * @private
  239. */
  240. function trustSingle (subnet) {
  241. var subnetip = subnet[0]
  242. var subnetkind = subnetip.kind()
  243. var subnetisipv4 = subnetkind === 'ipv4'
  244. var subnetrange = subnet[1]
  245. return function trust (addr) {
  246. if (!isip(addr)) return false
  247. var ip = parseip(addr)
  248. var kind = ip.kind()
  249. if (kind !== subnetkind) {
  250. if (subnetisipv4 && !ip.isIPv4MappedAddress()) {
  251. // Incompatible IP addresses
  252. return false
  253. }
  254. // Convert IP to match subnet IP kind
  255. ip = subnetisipv4
  256. ? ip.toIPv4Address()
  257. : ip.toIPv4MappedAddress()
  258. }
  259. return ip.match(subnetip, subnetrange)
  260. }
  261. }