dd-trace
Version:
Datadog APM tracing client for JavaScript
137 lines (105 loc) • 3 kB
JavaScript
'use strict'
const { BlockList } = require('net')
const net = require('net')
const FORWARED_HEADER_NAME = 'forwarded'
const ipHeaderList = [
'x-forwarded-for',
'x-real-ip',
'true-client-ip',
'x-client-ip',
FORWARED_HEADER_NAME,
'forwarded-for',
'x-cluster-client-ip',
'fastly-client-ip',
'cf-connecting-ip',
'cf-connecting-ipv6'
]
const privateCIDRs = [
'127.0.0.0/8',
'10.0.0.0/8',
'172.16.0.0/12',
'192.168.0.0/16',
'169.254.0.0/16',
'100.65.0.0/10',
'::1/128',
'fec0::/10',
'fe80::/10',
'fc00::/7',
'fd00::/8'
]
const privateIPMatcher = new BlockList()
for (const cidr of privateCIDRs) {
const [address, prefix] = cidr.split('/')
privateIPMatcher.addSubnet(address, Number.parseInt(prefix), net.isIPv6(address) ? 'ipv6' : 'ipv4')
}
function extractIp (config, req) {
const headers = req.headers
if (config.clientIpHeader) {
if (!headers) return
const ip = findFirstIp(headers[config.clientIpHeader])
return ip.public || ip.private
}
let firstPrivateIp
if (headers) {
for (const ipHeaderName of ipHeaderList) {
const header = headers[ipHeaderName]
const firstIp = ipHeaderName === FORWARED_HEADER_NAME ? findFirstIpForwardedFormat(header) : findFirstIp(header)
if (firstIp.public) {
return firstIp.public
} else if (!firstPrivateIp && firstIp.private) {
firstPrivateIp = firstIp.private
}
}
}
return firstPrivateIp || req.socket?.remoteAddress
}
function isPublicIp (ip, type) {
return !privateIPMatcher.check(ip, type === 6 ? 'ipv6' : 'ipv4')
}
function findFirstIp (str) {
const result = {}
if (!str) return result
const splitted = str.split(',')
for (const part of splitted) {
const chunk = part.trim()
// TODO: strip port and interface data ?
const type = net.isIP(chunk)
if (!type) continue
if (isPublicIp(chunk, type)) {
// it's public, return it immediately
result.public = chunk
return result
}
// it's private, only save the first one found
if (!result.private) result.private = chunk
}
return result
}
const forwardedForRegexp = /for="?\[?(([0-9]+\.)+[0-9]+|[0-9a-f:]*:[0-9a-f]*)/i
const forwardedByRegexp = /by="?\[?(([0-9]+\.)+[0-9]+|[0-9a-f:]*:[0-9a-f]*)/i
const forwardedRegexps = [forwardedForRegexp, forwardedByRegexp]
function findFirstIpForwardedFormat (str) {
const result = {}
if (!str) return result
const splitted = str.split(',')
for (const part of splitted) {
const chunk = part.trim()
for (const regex of forwardedRegexps) {
const ip = regex.exec(chunk)?.[1]
const type = net.isIP(ip)
if (!type) continue
if (isPublicIp(ip, type)) {
// it's public, return it immediately
result.public = ip
return result
}
// it's private, only save the first one found
if (!result.private) result.private = ip
}
}
return result
}
module.exports = {
extractIp,
ipHeaderList
}