172 lines
5.0 KiB
Plaintext
172 lines
5.0 KiB
Plaintext
var proxy = __PROXY__;
|
|
|
|
var direct = 'DIRECT';
|
|
|
|
var directDomains = __DIRECT_DOMAINS__;
|
|
|
|
var domainsUsingProxy = __DOMAINS__;
|
|
|
|
var localTlds = __LOCAL_TLDS__;
|
|
|
|
var cidrs = __CIDRS__;
|
|
|
|
function isIpAddress(ip) {
|
|
return /^\d{1,3}(\.\d{1,3}){3}$/.test(ip) || /^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}$/.test(ip);
|
|
}
|
|
|
|
function RadixTree() {
|
|
this.root = new Map();
|
|
}
|
|
|
|
RadixTree.prototype.insert = function(string) {
|
|
var node = this.root;
|
|
for (var i = 0; i < string.length; i++) {
|
|
var char = string[i];
|
|
if (!node.has(char)) {
|
|
node.set(char, new Map());
|
|
}
|
|
node = node.get(char);
|
|
}
|
|
};
|
|
|
|
RadixTree.prototype.search = function(string) {
|
|
var currentNode = this.root;
|
|
var isLastNode = false;
|
|
for (var i=0; i < string.length; i++) {
|
|
var char = string[i];
|
|
if (currentNode.has(char)) {
|
|
currentNode = currentNode.get(char);
|
|
isLastNode = currentNode.size === 0;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
return isLastNode;
|
|
}
|
|
|
|
function ipToBinary(ip) {
|
|
var bin = ''
|
|
// Check if it's IPv4
|
|
if (/^\d{1,3}(\.\d{1,3}){3}$/.test(ip)) {
|
|
bin = ip.split('.').map(function(num) {
|
|
return ("00000000" + parseInt(num, 10).toString(2)).slice(-8);
|
|
}).join('');
|
|
} else if (/^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}$/.test(ip)) {
|
|
// Expand the IPv6 address if it contains '::'
|
|
var parts = ip.split('::');
|
|
var left = parts[0] ? parts[0].split(':') : [];
|
|
var right = parts[1] ? parts[1].split(':') : [];
|
|
|
|
// Calculate the number of zero groups to insert
|
|
var zeroGroups = 8 - (left.length + right.length);
|
|
|
|
// Create the full address by inserting zero groups
|
|
var fullAddress = left.concat(Array(zeroGroups + 1).join('0').split('')).concat(right);
|
|
|
|
// Convert each group to binary and pad to 16 bits
|
|
bin = fullAddress.map(function(group) {
|
|
return ("0000000000000000" + parseInt(group || '0', 16).toString(2)).slice(-16);
|
|
}).join('');
|
|
}
|
|
return bin.replace(/^0+/, '');
|
|
}
|
|
|
|
function isInDirectDomain(host) {
|
|
for (var i = 0; i < directDomains.length; i++) {
|
|
var domain = directDomains[i];
|
|
if (host === domain || host.endsWith('.' + domain)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function isInProxyDomain(host) {
|
|
for (var i = 0; i < domainsUsingProxy.length; i++) {
|
|
var domain = domainsUsingProxy[i];
|
|
if (host === domain || host.endsWith('.' + domain)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function isLocalTestDomain(domain) {
|
|
// Chrome uses .test as testing gTLD.
|
|
var tld = domain.substring(domain.lastIndexOf('.'));
|
|
if (tld === domain) {
|
|
return false;
|
|
}
|
|
return localTlds.some(function(localTld) {
|
|
return tld === localTld;
|
|
});
|
|
}
|
|
|
|
/* https://github.com/frenchbread/private-ip */
|
|
function isPrivateIp(ip) {
|
|
return /^(::f{4}:)?10\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/i.test(ip) ||
|
|
/^(::f{4}:)?192\.168\.([0-9]{1,3})\.([0-9]{1,3})$/i.test(ip) ||
|
|
/^(::f{4}:)?172\.(1[6-9]|2\d|30|31)\.([0-9]{1,3})\.([0-9]{1,3})$/i.test(ip) ||
|
|
/^(::f{4}:)?127\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/i.test(ip) ||
|
|
/^(::f{4}:)?169\.254\.([0-9]{1,3})\.([0-9]{1,3})$/i.test(ip) ||
|
|
/^f[cd][0-9a-f]{2}:/i.test(ip) ||
|
|
/^fe80:/i.test(ip) ||
|
|
/^::1$/.test(ip) ||
|
|
/^::$/.test(ip);
|
|
}
|
|
|
|
function FindProxyForURL(url, host) {
|
|
if (isInDirectDomain(host)) {
|
|
debug('命中直连域名', host, 'N/A');
|
|
return direct;
|
|
} else if (isInProxyDomain(host)) {
|
|
debug('命中代理域名', host, 'N/A');
|
|
return proxy;
|
|
} else if (isPlainHostName(host) || host === 'localhost' || isLocalTestDomain(host)) {
|
|
debug('命中本地主机名或本地tld', host, 'N/A');
|
|
return direct;
|
|
} else if (isPrivateIp(host)) {
|
|
debug('命中私有 IP 地址', host, 'N/A');
|
|
return direct;
|
|
}
|
|
|
|
ip = isIpAddress(host) ? host : dnsResolve(host);
|
|
|
|
if (!ip) {
|
|
debug('无法解析 IP 地址', host, 'N/A');
|
|
return proxy;
|
|
} else if (isPrivateIp(ip)) {
|
|
debug('域名解析后命中私有 IP 地址', host, ip);
|
|
return direct;
|
|
} else if (radixTree.search(ipToBinary(ip))) {
|
|
debug('匹配到直连IP', host, ip);
|
|
return direct;
|
|
}
|
|
|
|
debug('未命中任何规则', host, ip);
|
|
return proxy;
|
|
}
|
|
|
|
var allowAlert = true
|
|
function debug(msg, host='', ip='') {
|
|
if (!allowAlert) {
|
|
return
|
|
}
|
|
try {
|
|
alert('[' + host + ' -> ' + ip + '] ' + msg);
|
|
} catch (e) {
|
|
allowAlert = false
|
|
}
|
|
}
|
|
|
|
var radixTree = new RadixTree();
|
|
|
|
(function () {
|
|
debug('开始生成 Radix Tree', 'PAC文件载入开始');
|
|
for (let i=0; i<cidrs.length; i++) {
|
|
var prefix = cidrs[i];
|
|
var bits = (parseInt(prefix, 16)).toString(2);
|
|
radixTree.insert(bits);
|
|
}
|
|
debug('Radix Tree 已生成', 'PAC文件载入完毕', cidrs.length.toString()+'个CIDR条目');
|
|
})(); |