137 lines
4.0 KiB
JavaScript
137 lines
4.0 KiB
JavaScript
import { forwarded } from "@tinyhttp/forwarded";
|
|
import ipaddr from "ipaddr.js";
|
|
const DIGIT_REGEXP = /^[0-9]+$/;
|
|
const isip = ipaddr.isValid;
|
|
const parseip = ipaddr.parse;
|
|
const IP_RANGES = {
|
|
linklocal: ["169.254.0.0/16", "fe80::/10"],
|
|
loopback: ["127.0.0.1/8", "::1/128"],
|
|
uniquelocal: ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "fc00::/7"]
|
|
};
|
|
const trustNone = () => false;
|
|
function alladdrs(req, trust) {
|
|
const addrs = forwarded(req);
|
|
if (!trust)
|
|
return addrs;
|
|
if (typeof trust !== "function")
|
|
trust = compile(trust);
|
|
for (let i = 0; i < addrs.length - 1; i++) {
|
|
if (trust(addrs[i], i))
|
|
continue;
|
|
addrs.length = i + 1;
|
|
}
|
|
return addrs;
|
|
}
|
|
function compile(val) {
|
|
let trust;
|
|
if (typeof val === "string")
|
|
trust = [val];
|
|
else if (Array.isArray(val))
|
|
trust = val.slice();
|
|
else
|
|
throw new TypeError("unsupported trust argument");
|
|
for (let i = 0; i < trust.length; i++) {
|
|
val = trust[i];
|
|
if (!Object.prototype.hasOwnProperty.call(IP_RANGES, val))
|
|
continue;
|
|
val = IP_RANGES[val];
|
|
trust.splice.apply(trust, [i, 1].concat(val));
|
|
i += val.length - 1;
|
|
}
|
|
return compileTrust(compileRangeSubnets(trust));
|
|
}
|
|
function compileRangeSubnets(arr) {
|
|
const rangeSubnets = new Array(arr.length);
|
|
for (let i = 0; i < arr.length; i++)
|
|
rangeSubnets[i] = parseIPNotation(arr[i]);
|
|
return rangeSubnets;
|
|
}
|
|
function compileTrust(rangeSubnets) {
|
|
const len = rangeSubnets.length;
|
|
return len === 0 ? trustNone : len === 1 ? trustSingle(rangeSubnets[0]) : trustMulti(rangeSubnets);
|
|
}
|
|
function parseIPNotation(note) {
|
|
const pos = note.lastIndexOf("/");
|
|
const str = pos !== -1 ? note.substring(0, pos) : note;
|
|
if (!isip(str))
|
|
throw new TypeError("invalid IP address: " + str);
|
|
let ip = parseip(str);
|
|
if (pos === -1 && ip.kind() === "ipv6") {
|
|
ip = ip;
|
|
if (ip.isIPv4MappedAddress())
|
|
ip = ip.toIPv4Address();
|
|
}
|
|
const max = ip.kind() === "ipv6" ? 128 : 32;
|
|
let range = pos !== -1 ? note.substring(pos + 1, note.length) : null;
|
|
if (range === null)
|
|
range = max;
|
|
else if (DIGIT_REGEXP.test(range))
|
|
range = parseInt(range, 10);
|
|
else if (ip.kind() === "ipv4" && isip(range))
|
|
range = parseNetmask(range);
|
|
else
|
|
range = null;
|
|
if (typeof range === "number" && (range <= 0 || range > max))
|
|
throw new TypeError("invalid range on address: " + note);
|
|
return [ip, range];
|
|
}
|
|
function parseNetmask(netmask) {
|
|
const ip = parseip(netmask);
|
|
return ip.kind() === "ipv4" ? ip.prefixLengthFromSubnetMask() : null;
|
|
}
|
|
function proxyaddr(req, trust) {
|
|
const addrs = alladdrs(req, trust);
|
|
return addrs[addrs.length - 1];
|
|
}
|
|
function trustMulti(subnets) {
|
|
return function trust(addr) {
|
|
if (!isip(addr))
|
|
return false;
|
|
const ip = parseip(addr);
|
|
let ipconv;
|
|
const kind = ip.kind();
|
|
for (let i = 0; i < subnets.length; i++) {
|
|
const subnet = subnets[i];
|
|
const subnetip = subnet[0];
|
|
const subnetkind = subnetip.kind();
|
|
const subnetrange = subnet[1];
|
|
let trusted = ip;
|
|
if (kind !== subnetkind) {
|
|
if (subnetkind === "ipv4" && !ip.isIPv4MappedAddress())
|
|
continue;
|
|
if (!ipconv)
|
|
ipconv = subnetkind === "ipv4" ? ip.toIPv4Address() : ip.toIPv4MappedAddress();
|
|
trusted = ipconv;
|
|
}
|
|
if (trusted.match(subnetip, subnetrange))
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
}
|
|
function trustSingle(subnet) {
|
|
const subnetip = subnet[0];
|
|
const subnetkind = subnetip.kind();
|
|
const subnetisipv4 = subnetkind === "ipv4";
|
|
const subnetrange = subnet[1];
|
|
return function trust(addr) {
|
|
if (!isip(addr))
|
|
return false;
|
|
let ip = parseip(addr);
|
|
const kind = ip.kind();
|
|
if (kind !== subnetkind) {
|
|
if (subnetisipv4 && !ip.isIPv4MappedAddress())
|
|
return false;
|
|
ip = subnetisipv4 ? ip.toIPv4Address() : ip.toIPv4MappedAddress();
|
|
}
|
|
return ip.match(subnetip, subnetrange);
|
|
};
|
|
}
|
|
export {
|
|
alladdrs as all,
|
|
compile,
|
|
parseIPNotation,
|
|
proxyaddr
|
|
};
|
|
//# sourceMappingURL=index.js.map
|