Compare commits
48 Commits
v20241004.
...
v20250501.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b08e659514 | ||
|
|
1a511c81ef | ||
|
|
e084116209 | ||
|
|
64f75179f5 | ||
|
|
948a002d73 | ||
|
|
046401ec20 | ||
|
|
9cf413c43c | ||
|
|
601b549b31 | ||
|
|
6b65baa275 | ||
|
|
8c52b77a03 | ||
|
|
02f24366f5 | ||
|
|
419485dde2 | ||
|
|
2a1c178d3a | ||
|
|
ab03a97789 | ||
|
|
ac4fb7b19b | ||
|
|
f810a66b0c | ||
|
|
e6129a6dd4 | ||
|
|
f1df8acaba | ||
|
|
7add95746e | ||
|
|
9622bd4456 | ||
|
|
3949a407b0 | ||
|
|
8b51175a34 | ||
|
|
bdc53dd5ca | ||
|
|
f3d1085c2d | ||
|
|
932d427c01 | ||
|
|
d554c444cc | ||
|
|
7b9fb28619 | ||
|
|
74e6d0aa47 | ||
|
|
509f516d1d | ||
|
|
4ae3541f20 | ||
|
|
58d793c13c | ||
|
|
340754f759 | ||
|
|
d36a5097f5 | ||
|
|
03998c2db0 | ||
|
|
bc33e69e7d | ||
|
|
fcd1ce19aa | ||
|
|
d923c54720 | ||
|
|
16f2713aa1 | ||
|
|
07a9e9773b | ||
|
|
f827e221e7 | ||
|
|
357bbd09bd | ||
|
|
a283940216 | ||
|
|
4a64f36f5c | ||
|
|
34b4cd9097 | ||
|
|
812b8e7d4c | ||
|
|
0ab915b467 | ||
|
|
87d75dba5f | ||
|
|
3027e9e6a7 |
30
.github/workflows/auto-generate-pac.yml
vendored
30
.github/workflows/auto-generate-pac.yml
vendored
@@ -4,12 +4,13 @@ on:
|
||||
schedule:
|
||||
- cron: '10 0 * * 4'
|
||||
push:
|
||||
paths-ignore:
|
||||
- 'README.md'
|
||||
- 'LICENSE'
|
||||
- 'gfw.pac'
|
||||
- '.gitignore'
|
||||
- '.github/**'
|
||||
paths:
|
||||
- 'gfw-pac.py'
|
||||
- 'pac-template'
|
||||
- 'local-tlds.txt'
|
||||
- 'direct-domains.txt'
|
||||
- 'proxy-domains.txt'
|
||||
- 'cidrs-cn.txt'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
@@ -19,21 +20,36 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Check if gfw.pac is in the commit
|
||||
id: check-file
|
||||
run: |
|
||||
if git diff --name-only HEAD^ HEAD | grep -q 'gfw\.pac'; then
|
||||
echo "file-exists=true" >> $GITHUB_ENV
|
||||
else
|
||||
echo "file-exists=false" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Download the latest cn.txt
|
||||
if: env.file-exists == 'false'
|
||||
run: curl -L https://raw.githubusercontent.com/Loyalsoldier/geoip/release/text/cn.txt -o cidrs-cn.txt
|
||||
|
||||
- name: Run gfw-pac.py script
|
||||
if: env.file-exists == 'false'
|
||||
run: ./gfw-pac.py -f gfw.pac -p "PROXY 127.0.0.1:3128" --proxy-domains=proxy-domains.txt --direct-domains=direct-domains.txt --localtld-domains=local-tlds.txt --ip-file=cidrs-cn.txt
|
||||
|
||||
- name: Commit and push changes
|
||||
if: env.file-exists == 'false'
|
||||
run: |
|
||||
last_commit_message=$(git log -1 --pretty=%B)
|
||||
commit_message=$([[ "${{ github.event_name }}" == "schedule" ]] && echo "定期更新数据并生成 gfw.pac" || echo "自动生成 gfw.pac: $last_commit_message")
|
||||
git config --local user.email "action@github.com"
|
||||
git config --local user.name "GitHub Action"
|
||||
git add -A
|
||||
git commit -a -m "自动生成 gfw.pac"
|
||||
git commit -a -m "${commit_message}"
|
||||
git push
|
||||
|
||||
- name: Trigger Auto-Release
|
||||
if: env.file-exists == 'false'
|
||||
uses: benc-uk/workflow-dispatch@v1
|
||||
with:
|
||||
workflow: Auto Generate Release
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -43,3 +43,5 @@ $RECYCLE.BIN/
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
test.js
|
||||
6924
cidrs-cn.txt
6924
cidrs-cn.txt
File diff suppressed because it is too large
Load Diff
@@ -69,8 +69,18 @@ douyinstatic.com
|
||||
douyinvod.com
|
||||
supercachenode.com
|
||||
bytedance.com
|
||||
bytedanceapi.com
|
||||
bytescm.com
|
||||
bytecdn.cn
|
||||
byteoc.com
|
||||
bytednsdoc.com
|
||||
bytetcc.com
|
||||
feishu.cn
|
||||
feishucdn.com
|
||||
toutiao.com
|
||||
toutiaoimg.com
|
||||
toutiaostatic.com
|
||||
yhgfb-cn-static.com
|
||||
cmbchina.com
|
||||
mi.com
|
||||
xiaomi.com
|
||||
@@ -83,6 +93,8 @@ dianping.com
|
||||
quark.cn
|
||||
wps.cn
|
||||
wpscdn.cn
|
||||
xiaohongshu.com
|
||||
xhscdn.com
|
||||
push.apple.com
|
||||
setup.icloud.com
|
||||
appldnld.apple.com
|
||||
@@ -118,3 +130,10 @@ ocsp.globalsign.com
|
||||
ocsp2.globalsign.com
|
||||
ocsp.digicert.cn
|
||||
ocsp.dcocsp.cn
|
||||
api.onedrive.com
|
||||
storage.live.com
|
||||
gr.global.aa-rt.sharepoint.com
|
||||
skyapi.live.net
|
||||
roaming.officeapps.live.com
|
||||
blob.core.windows.net
|
||||
default.exp-tas.com
|
||||
32
gfw-pac.py
32
gfw-pac.py
@@ -27,16 +27,18 @@ def parse_args():
|
||||
def convert_cidr(cidr):
|
||||
if '/' in cidr:
|
||||
network = ipaddress.ip_network(cidr.strip(), strict=False)
|
||||
network_address = network.network_address
|
||||
prefixlen = network.prefixlen
|
||||
network_address = int(network.network_address) >> (network.max_prefixlen - network.prefixlen)
|
||||
else:
|
||||
network = ipaddress.ip_address(cidr.strip())
|
||||
network_address = network
|
||||
prefixlen = network.max_prefixlen
|
||||
if network.version == 4:
|
||||
return hex(int(network_address))[2:] + '/' + str(prefixlen)
|
||||
else:
|
||||
return network.compressed
|
||||
return hex(int(network_address))[2:]
|
||||
|
||||
def longest_common_prefix(str1, str2):
|
||||
min_length = min(len(str1), len(str2))
|
||||
for i in range(min_length):
|
||||
if str1[i] != str2[i]:
|
||||
return str1[:i]
|
||||
return str1[:min_length]
|
||||
|
||||
def generate_cnip_cidrs():
|
||||
""" 从文件中读取CIDR地址 """
|
||||
@@ -47,6 +49,22 @@ def generate_cnip_cidrs():
|
||||
for cidr in cidrs:
|
||||
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)
|
||||
return f"'{cidr_list}'.split(',')"
|
||||
|
||||
|
||||
70
pac-template
70
pac-template
@@ -10,35 +10,45 @@ var localTlds = __LOCAL_TLDS__;
|
||||
|
||||
var cidrs = __CIDRS__;
|
||||
|
||||
var hasOwnProperty = Object.hasOwnProperty;
|
||||
|
||||
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 = {};
|
||||
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[char]) {
|
||||
node[char] = {};
|
||||
if (!node.has(char)) {
|
||||
node.set(char, new Map());
|
||||
}
|
||||
node = node[char];
|
||||
node = node.get(char);
|
||||
}
|
||||
};
|
||||
|
||||
RadixTree.prototype.to_list = function() {
|
||||
return this.root;
|
||||
};
|
||||
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)) {
|
||||
return ip.split('.').map(function(num) {
|
||||
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)) {
|
||||
@@ -54,25 +64,11 @@ function ipToBinary(ip) {
|
||||
var fullAddress = left.concat(Array(zeroGroups + 1).join('0').split('')).concat(right);
|
||||
|
||||
// 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);
|
||||
}).join('');
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
return bin.replace(/^0+/, '');
|
||||
}
|
||||
|
||||
function isInDirectDomain(host) {
|
||||
@@ -142,7 +138,7 @@ function FindProxyForURL(url, host) {
|
||||
} else if (isPrivateIp(ip)) {
|
||||
debug('域名解析后命中私有 IP 地址', host, ip);
|
||||
return direct;
|
||||
} else if (searchRadixTree(ipToBinary(ip))) {
|
||||
} else if (radixTree.search(ipToBinary(ip))) {
|
||||
debug('匹配到直连IP', host, ip);
|
||||
return direct;
|
||||
}
|
||||
@@ -152,7 +148,7 @@ function FindProxyForURL(url, host) {
|
||||
}
|
||||
|
||||
var allowAlert = true
|
||||
function debug(msg, host, ip) {
|
||||
function debug(msg, host='', ip='') {
|
||||
if (!allowAlert) {
|
||||
return
|
||||
}
|
||||
@@ -166,19 +162,17 @@ function debug(msg, host, ip) {
|
||||
var radixTree = new RadixTree();
|
||||
|
||||
(function () {
|
||||
var startTime = new Date().getMilliseconds();
|
||||
debug('开始生成 Radix Tree', 'PAC文件载入开始', startTime.toString());
|
||||
debug('开始生成 Radix Tree', 'PAC文件载入开始');
|
||||
lastFullPrefix = ''
|
||||
for (let i=0; i<cidrs.length; i++) {
|
||||
var cidr = cidrs[i];
|
||||
var [ip, prefixLen] = cidr.split('/');
|
||||
if (!cidr.includes(':')) {
|
||||
var ip = ip.match(/.{1,2}/g).map(function(byte) {
|
||||
return parseInt(byte, 16);
|
||||
}).join('.');
|
||||
var prefix = cidrs[i];
|
||||
if (prefix.substring(0, 1) !== '~') {
|
||||
lastFullPrefix = prefix
|
||||
} else {
|
||||
prefix = lastFullPrefix.substring(0, lastFullPrefix.length-prefix.length+1) + prefix.substring(1)
|
||||
}
|
||||
var bits = ipToBinary(ip).slice(0, prefixLen);
|
||||
var bits = (parseInt(prefix, 16)).toString(2);
|
||||
radixTree.insert(bits);
|
||||
}
|
||||
radixTree = radixTree.to_list();
|
||||
debug('Radix Tree 已生成', 'PAC文件载入完毕', cidrs.length.toString()+'个CIDR条目');
|
||||
})();
|
||||
@@ -10,6 +10,7 @@ githubassets.com
|
||||
bing.com
|
||||
bing.cn
|
||||
bing.net
|
||||
bingapis.com
|
||||
live.com
|
||||
stackoverflow.com
|
||||
wikipedia.org
|
||||
@@ -26,3 +27,9 @@ segment.io
|
||||
unpkg.com
|
||||
jsdelivr.com
|
||||
tv.apple.com
|
||||
instagram.com
|
||||
cdninstagram.com
|
||||
reddit.com
|
||||
redd.it
|
||||
whatsapp.com
|
||||
whatsapp.net
|
||||
Reference in New Issue
Block a user