Compare commits
5 Commits
v20241114.
...
v20241205.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d554c444cc | ||
|
|
7b9fb28619 | ||
|
|
74e6d0aa47 | ||
|
|
509f516d1d | ||
|
|
4ae3541f20 |
1
.github/workflows/auto-generate-pac.yml
vendored
1
.github/workflows/auto-generate-pac.yml
vendored
@@ -11,6 +11,7 @@ on:
|
|||||||
- 'direct-domains.txt'
|
- 'direct-domains.txt'
|
||||||
- 'proxy-domains.txt'
|
- 'proxy-domains.txt'
|
||||||
- 'cidrs-cn.txt'
|
- 'cidrs-cn.txt'
|
||||||
|
- '!gfw.pac'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -43,3 +43,5 @@ $RECYCLE.BIN/
|
|||||||
|
|
||||||
# Windows shortcuts
|
# Windows shortcuts
|
||||||
*.lnk
|
*.lnk
|
||||||
|
|
||||||
|
test.js
|
||||||
777
cidrs-cn.txt
777
cidrs-cn.txt
File diff suppressed because it is too large
Load Diff
32
gfw-pac.py
32
gfw-pac.py
@@ -27,16 +27,18 @@ def parse_args():
|
|||||||
def convert_cidr(cidr):
|
def convert_cidr(cidr):
|
||||||
if '/' in cidr:
|
if '/' in cidr:
|
||||||
network = ipaddress.ip_network(cidr.strip(), strict=False)
|
network = ipaddress.ip_network(cidr.strip(), strict=False)
|
||||||
network_address = network.network_address
|
network_address = int(network.network_address) >> (network.max_prefixlen - network.prefixlen)
|
||||||
prefixlen = network.prefixlen
|
|
||||||
else:
|
else:
|
||||||
network = ipaddress.ip_address(cidr.strip())
|
network = ipaddress.ip_address(cidr.strip())
|
||||||
network_address = network
|
network_address = network
|
||||||
prefixlen = network.max_prefixlen
|
return hex(int(network_address))[2:]
|
||||||
if network.version == 4:
|
|
||||||
return hex(int(network_address))[2:] + '/' + str(prefixlen)
|
def longest_common_prefix(str1, str2):
|
||||||
else:
|
min_length = min(len(str1), len(str2))
|
||||||
return network.compressed
|
for i in range(min_length):
|
||||||
|
if str1[i] != str2[i]:
|
||||||
|
return str1[:i]
|
||||||
|
return str1[:min_length]
|
||||||
|
|
||||||
def generate_cnip_cidrs():
|
def generate_cnip_cidrs():
|
||||||
""" 从文件中读取CIDR地址 """
|
""" 从文件中读取CIDR地址 """
|
||||||
@@ -47,6 +49,22 @@ def generate_cnip_cidrs():
|
|||||||
for cidr in cidrs:
|
for cidr in cidrs:
|
||||||
converted_cidrs.append(convert_cidr(cidr))
|
converted_cidrs.append(convert_cidr(cidr))
|
||||||
|
|
||||||
|
converted_cidrs.sort(key=lambda x: (len(x), x), reverse=False)
|
||||||
|
converted_cidrs_clone = converted_cidrs[:]
|
||||||
|
|
||||||
|
lastFullCidr = ''
|
||||||
|
for i in range(len(converted_cidrs)):
|
||||||
|
prevCidr = converted_cidrs_clone[i-1] if i > 0 else ''
|
||||||
|
currentCidr = converted_cidrs[i]
|
||||||
|
if len(prevCidr) != len(currentCidr):
|
||||||
|
lastFullCidr = currentCidr
|
||||||
|
continue
|
||||||
|
prefix = longest_common_prefix(lastFullCidr, currentCidr)
|
||||||
|
if len(prefix) < len(lastFullCidr)//1.2:
|
||||||
|
lastFullCidr = currentCidr
|
||||||
|
continue
|
||||||
|
converted_cidrs[i] = '~' + currentCidr[len(prefix):]
|
||||||
|
|
||||||
cidr_list = ','.join(converted_cidrs)
|
cidr_list = ','.join(converted_cidrs)
|
||||||
return f"'{cidr_list}'.split(',')"
|
return f"'{cidr_list}'.split(',')"
|
||||||
|
|
||||||
|
|||||||
70
pac-template
70
pac-template
@@ -10,35 +10,45 @@ var localTlds = __LOCAL_TLDS__;
|
|||||||
|
|
||||||
var cidrs = __CIDRS__;
|
var cidrs = __CIDRS__;
|
||||||
|
|
||||||
var hasOwnProperty = Object.hasOwnProperty;
|
|
||||||
|
|
||||||
function isIpAddress(ip) {
|
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);
|
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() {
|
function RadixTree() {
|
||||||
this.root = {};
|
this.root = new Map();
|
||||||
}
|
}
|
||||||
|
|
||||||
RadixTree.prototype.insert = function(string) {
|
RadixTree.prototype.insert = function(string) {
|
||||||
var node = this.root;
|
var node = this.root;
|
||||||
for (var i = 0; i < string.length; i++) {
|
for (var i = 0; i < string.length; i++) {
|
||||||
var char = string[i];
|
var char = string[i];
|
||||||
if (!node[char]) {
|
if (!node.has(char)) {
|
||||||
node[char] = {};
|
node.set(char, new Map());
|
||||||
}
|
}
|
||||||
node = node[char];
|
node = node.get(char);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
RadixTree.prototype.to_list = function() {
|
RadixTree.prototype.search = function(string) {
|
||||||
return this.root;
|
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) {
|
function ipToBinary(ip) {
|
||||||
|
var bin = ''
|
||||||
// Check if it's IPv4
|
// Check if it's IPv4
|
||||||
if (/^\d{1,3}(\.\d{1,3}){3}$/.test(ip)) {
|
if (/^\d{1,3}(\.\d{1,3}){3}$/.test(ip)) {
|
||||||
return ip.split('.').map(function(num) {
|
bin = ip.split('.').map(function(num) {
|
||||||
return ("00000000" + parseInt(num, 10).toString(2)).slice(-8);
|
return ("00000000" + parseInt(num, 10).toString(2)).slice(-8);
|
||||||
}).join('');
|
}).join('');
|
||||||
} else if (/^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}$/.test(ip)) {
|
} else if (/^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}$/.test(ip)) {
|
||||||
@@ -54,25 +64,11 @@ function ipToBinary(ip) {
|
|||||||
var fullAddress = left.concat(Array(zeroGroups + 1).join('0').split('')).concat(right);
|
var fullAddress = left.concat(Array(zeroGroups + 1).join('0').split('')).concat(right);
|
||||||
|
|
||||||
// Convert each group to binary and pad to 16 bits
|
// Convert each group to binary and pad to 16 bits
|
||||||
return fullAddress.map(function(group) {
|
bin = fullAddress.map(function(group) {
|
||||||
return ("0000000000000000" + parseInt(group || '0', 16).toString(2)).slice(-16);
|
return ("0000000000000000" + parseInt(group || '0', 16).toString(2)).slice(-16);
|
||||||
}).join('');
|
}).join('');
|
||||||
}
|
}
|
||||||
}
|
return bin.replace(/^0+/, '');
|
||||||
|
|
||||||
function searchRadixTree(bits) {
|
|
||||||
var currentNode = radixTree;
|
|
||||||
var isLastNode = false;
|
|
||||||
for (var i=0; i<bits.length; i++) {
|
|
||||||
var char = bits[i];
|
|
||||||
if (currentNode[char]) {
|
|
||||||
currentNode = currentNode[char];
|
|
||||||
isLastNode = Object.keys(currentNode).length === 0;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return isLastNode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function isInDirectDomain(host) {
|
function isInDirectDomain(host) {
|
||||||
@@ -142,7 +138,7 @@ function FindProxyForURL(url, host) {
|
|||||||
} else if (isPrivateIp(ip)) {
|
} else if (isPrivateIp(ip)) {
|
||||||
debug('域名解析后命中私有 IP 地址', host, ip);
|
debug('域名解析后命中私有 IP 地址', host, ip);
|
||||||
return direct;
|
return direct;
|
||||||
} else if (searchRadixTree(ipToBinary(ip))) {
|
} else if (radixTree.search(ipToBinary(ip))) {
|
||||||
debug('匹配到直连IP', host, ip);
|
debug('匹配到直连IP', host, ip);
|
||||||
return direct;
|
return direct;
|
||||||
}
|
}
|
||||||
@@ -152,7 +148,7 @@ function FindProxyForURL(url, host) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var allowAlert = true
|
var allowAlert = true
|
||||||
function debug(msg, host, ip) {
|
function debug(msg, host='', ip='') {
|
||||||
if (!allowAlert) {
|
if (!allowAlert) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -166,19 +162,17 @@ function debug(msg, host, ip) {
|
|||||||
var radixTree = new RadixTree();
|
var radixTree = new RadixTree();
|
||||||
|
|
||||||
(function () {
|
(function () {
|
||||||
var startTime = new Date().getMilliseconds();
|
debug('开始生成 Radix Tree', 'PAC文件载入开始');
|
||||||
debug('开始生成 Radix Tree', 'PAC文件载入开始', startTime.toString());
|
lastFullPrefix = ''
|
||||||
for (let i=0; i<cidrs.length; i++) {
|
for (let i=0; i<cidrs.length; i++) {
|
||||||
var cidr = cidrs[i];
|
var prefix = cidrs[i];
|
||||||
var [ip, prefixLen] = cidr.split('/');
|
if (prefix.substring(0, 1) !== '~') {
|
||||||
if (!cidr.includes(':')) {
|
lastFullPrefix = prefix
|
||||||
var ip = ip.match(/.{1,2}/g).map(function(byte) {
|
} else {
|
||||||
return parseInt(byte, 16);
|
prefix = lastFullPrefix.substring(0, lastFullPrefix.length-prefix.length+1) + prefix.substring(1)
|
||||||
}).join('.');
|
|
||||||
}
|
}
|
||||||
var bits = ipToBinary(ip).slice(0, prefixLen);
|
var bits = (parseInt(prefix, 16)).toString(2);
|
||||||
radixTree.insert(bits);
|
radixTree.insert(bits);
|
||||||
}
|
}
|
||||||
radixTree = radixTree.to_list();
|
|
||||||
debug('Radix Tree 已生成', 'PAC文件载入完毕', cidrs.length.toString()+'个CIDR条目');
|
debug('Radix Tree 已生成', 'PAC文件载入完毕', cidrs.length.toString()+'个CIDR条目');
|
||||||
})();
|
})();
|
||||||
Reference in New Issue
Block a user