This commit is contained in:
wits-fe 2023-05-24 13:54:28 +08:00 committed by GitHub
commit aae97a9947
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 268 additions and 22 deletions

View File

@ -14,7 +14,7 @@ PAC scripts for proxies
## 使用
获取方式:[本仓库的 Releases](https://github.com/iBug/pac/releases/latest)
获取方式:[本仓库的 Releases](https://github.com/wits-fe/pac/releases/latest)
- `pac-<name>.txt` 包含从数据源 `<name>` 获取的 IP 地址列表(白名单)
- `pac-gfwlist-<name>.txt` 在 IP 白名单的基础上添加了 GFWList 的匹配

146
build.py
View File

@ -8,11 +8,16 @@ from requests.exceptions import RequestException, HTTPError
import gfwlist
SOURCES = {
SOURCES_4 = {
'ipdeny.com': 'http://www.ipdeny.com/ipblocks/data/aggregated/cn-aggregated.zone',
'17mon': 'https://raw.githubusercontent.com/17mon/china_ip_list/master/china_ip_list.txt',
}
SOURCES_6 = {
'gaoyifan': 'https://gaoyifan.github.io/china-operator-ip/china6.txt',
}
SOURCES_46 = {
'maxmind': 'https://github.com/v2fly/geoip/raw/release/text/cn.txt',
}
OUT_DIR = "dist"
# Stub content to disable GFWList check
@ -25,6 +30,8 @@ def fetch_and_convert(src):
template = "var CHINA = [\n{}\n];\n"
lines = []
for iprange in response.text.strip().split("\n"):
if iprange.find(":") != -1:
break
ipnet = ipaddress.IPv4Network(iprange)
netaddr = int(ipnet.network_address)
netmask = int(ipnet.netmask)
@ -34,6 +41,101 @@ def fetch_and_convert(src):
return template.format("\n".join(lines))
def fetch_and_convert_ip6(src):
response = requests.get(src)
response.raise_for_status()
text = response.text
template = "var CHINA6_F = [\n{}\n];\n\n"
template2 = "var CHINA6_S = [\n{}\n];\n"
lines = []
lines2 = []
lastnum = 0
count = 0
begins = False
ends = False
lower = 0
upper = 0
networkstr_b = ""
iprangestr_b = ""
fixlen = len(f" [0xFFFFFFFF, -1, 0xFFFFFFFF],")
for iprange in text.strip().split("\n"):
if iprange.find(":") == -1:
continue
ipnet = ipaddress.IPv6Network(iprange)
prefixlen = ipnet.prefixlen
fulladdr = str(ipnet.exploded).replace(':', '')
num1 = int(fulladdr[0:8], 16)
num2 = int(fulladdr[8:16], 16)
fullmask = f"{ipnet.netmask:X}"
mask1 = fullmask[0:8]
mask2 = fullmask[8:16]
if lastnum != num1 and begins:
ends = True
if ends:
begins = False
ends = False
upper = count - 1
s2 = networkstr_b
if upper == lower:
s2 = iprangestr_b
s = f" [0x{lastnum:08X}, {lower}, {upper}],"
len1 = len(s)
if len1 < fixlen:
s = s + " " * (fixlen - len1)
s = f"{s} // {s2}"
lines.append(s)
if prefixlen <= 32:
if begins:
raise NameError(f"Invalid list order: \n{iprange}")
s = f" [0x{num1:08X}, -1, 0x{mask1}], // {iprange}"
lines.append(s)
else:
if not begins:
begins = True
lower = count
networkstr_b = str(ipnet.exploded)[0:10]
iprangestr_b = iprange
s = f" [0x{num2:08X}, 0x{mask2}], // {count}, {iprange}"
lines2.append(s)
count = count + 1
lastnum = num1
if begins:
begins = False
ends = False
upper = count - 1
s2 = networkstr_b
if upper == lower:
s2 = iprangestr_b
s = f" [0x{lastnum:08X}, {lower}, {upper}],"
len1 = len(s)
if len1 < fixlen:
s = s + " " * (fixlen - len1)
s = f"{s} // {s2}"
lines.append(s)
lines.append(" [0xFFFFFFFF, -1, 0xFFFFFFFF] // ffff:ffff::/32 placeholder")
lines2.append(f" [0xFFFFFFFF, 0xFFFFFFFF] // {count}, placeholder, not in use")
return template.format("\n".join(lines)) + template2.format("\n".join(lines2))
def write_pac(filename, code, data, data_6, tail):
with open(os.path.join(OUT_DIR, filename), "w") as f:
f.write(code)
f.write(data)
f.write("\n")
f.write(data_6)
f.write("\n")
f.write(tail)
def main():
now = datetime.utcnow()
date = now.strftime("%Y%m%d")
@ -45,27 +147,37 @@ def main():
gfwlist_stub = GFWLIST_STUB
os.makedirs(OUT_DIR, mode=0o755, exist_ok=True)
for key in SOURCES:
print(f"Generating PAC script from source {key}")
for key in SOURCES_4:
key_6 = list(SOURCES_6)[0]
print(f"Generating PAC script from source {key}(IPv4) & {key_6}(IPv6)")
try:
data = fetch_and_convert(SOURCES[key])
data = fetch_and_convert(SOURCES_4[key])
data_6 = fetch_and_convert_ip6(SOURCES_6[key_6])
except RequestException:
continue
except HTTPError:
continue
filename = f"pac-{key}.txt"
filename_gfwlist = f"pac-gfwlist-{key}.txt"
with open(os.path.join(OUT_DIR, filename), "w") as f:
f.write(code)
f.write(data)
f.write("\n")
f.write(gfwlist_stub)
with open(os.path.join(OUT_DIR, filename_gfwlist), "w") as f:
f.write(code)
f.write(data)
f.write("\n")
f.write(gfwlist_part)
filename = f"pac-IPv4_{key}--IPv6_{key_6}.txt"
filename_gfwlist = f"pac-gfwlist-IPv4_{key}--IPv6_{key_6}.txt"
write_pac(filename, code, data, data_6, gfwlist_stub)
write_pac(filename_gfwlist, code, data, data_6, gfwlist_part)
for key in SOURCES_46:
print(f"Generating PAC script from source {key}(IPv4v6)")
try:
data = fetch_and_convert(SOURCES_46[key])
data_6 = fetch_and_convert_ip6(SOURCES_46[key])
except RequestException:
continue
except HTTPError:
continue
filename = f"pac-IPv4v6_{key}.txt"
filename_gfwlist = f"pac-gfwlist-IPv4v6_{key}.txt"
write_pac(filename, code, data, data_6, gfwlist_stub)
write_pac(filename_gfwlist, code, data, data_6, gfwlist_part)
if __name__ == '__main__':

142
code.js
View File

@ -5,8 +5,100 @@
var proxy = __PROXY__;
var direct = "DIRECT";
// lower: lower_index
// upper: (upper_index + 1) / (array_length if upper_index = last)
function binarySearch(list, num, lower, upper) {
var x = lower, y = upper, middle;
while (y - x > 1) {
middle = Math.floor((x + y) / 2);
if (list[middle][0] > num)
y = middle;
else
x = middle;
}
return x;
}
function convertToUInt6(high, low) {
var num1 = parseInt(high, 16) & 0xFFFF;
var num2 = parseInt(low, 16) & 0xFFFF;
return (((num1 << 16) | num2) >>> 0);
}
function isInNet6(parts, list, list2) {
var num = convertToUInt6(parts[0], parts[1]);
var x = binarySearch(list, num, 0, list.length);
if (list[x][1] == -1)
return (((num & list[x][2]) ^ list[x][0]) === 0);
// not in net (/33 - /64)
if (num !== list[x][0])
return false;
var num2 = convertToUInt6(parts[2], parts[3]);
var x2 = binarySearch(list2, num2, list[x][1], list[x][2] + 1);
return (((num2 & list2[x2][1]) ^ list2[x2][0]) === 0);
}
function isLanOrChina6(host) {
var addr = host;
if (addr.indexOf("[") !== -1) {
addr = addr.substring(1, addr.length - 1);
}
if (addr.indexOf("::") === -1) {
var groups = addr.split(":");
if (groups.length != 8)
return false; // invalid ipv6 format
return isInNet6(groups, CHINA6_F, CHINA6_S) || isInNet6(groups, LAN6_F, LAN6_S);
}
var halfs = addr.split("::");
var left = halfs[0];
var right = halfs[1];
if (left.length < 1) left = "0000";
if (right.length < 1) right = "0000";
var groups1 = left.split(":");
if (groups1.length > 3)
return isInNet6(groups1, CHINA6_F, CHINA6_S) || isInNet6(groups1, LAN6_F, LAN6_S);
var groups2 = right.split(":");
var zeros = 8 - (groups1.length + groups2.length);
if (zeros < 2)
return false; // invalid ipv6 format
var parts = ["0", "0", "0", "0"];
parts[0] = groups1[0];
var i = 1;
for (var j = 1; j < groups1.length; j++) {
parts[i] = groups1[j];
i = i + 1;
if (i == 4) break;
}
if (i < 4) {
for (var k = 0; k < zeros; k++) {
parts[i] = "0000";
i = i + 1;
if (i == 4) break;
}
}
if (i == 3) parts[3] = groups2[0];
return isInNet6(parts, CHINA6_F, CHINA6_S) || isInNet6(parts, LAN6_F, LAN6_S);
}
function convertToUInt(host) {
var bytes = host.split(".");
var result = ((bytes[0] & 0xFF) << 24) | ((bytes[1] & 0xFF) << 16) | ((bytes[2] & 0xFF) << 8) | (bytes[3] & 0xFF);
return (result >>> 0);
}
function belongsToSubnet(host, list) {
var ip = convert_addr(host) >>> 0;
var ip = convertToUInt(host);
if (list.length === 0 || ip < list[0][0])
return false;
@ -83,11 +175,35 @@ function FindProxyForURL(url, host) {
}
// Fallback to IP whitelist
var remote = dnsResolve(host);
if (!remote || remote.indexOf(":") !== -1) {
// resolution failed or is IPv6 addr
// if host is IPv6
if (host.indexOf(":") !== -1) {
if (isLanOrChina6(host)) {
return direct;
}
return proxy;
}
var remote;
if(typeof dnsResolveEx == 'function') {
remote = dnsResolveEx(host);
} else {
remote = dnsResolve(host);
}
if (!remote) {
return proxy;
} else {
remote = remote.split(";")[0];
}
if (remote.indexOf(":") !== -1) {
if (isLanOrChina6(remote)) {
return direct;
}
return proxy;
}
if (isLan(remote) || isChina(remote)) {
return direct;
}
@ -104,3 +220,21 @@ var LAN = [
[0xC0A80000, 0xFFFF0000] // 192.168.0.0/16
];
// not support /65 - /128
var LAN6_F = [
[0x00000000, 0, 0], // ::/64
[0x0064FF9B, 1, 2], // 64:ff9b:
[0x01000000, 3, 3], // 100::/64
[0x20010000, -1, 0xFFFFFFFF], // 2001::/32 - teredo, may remove
[0xFC000000, -1, 0xFE000000], // fc00::/7
[0xFE800000, -1, 0xFFC00000], // fe80::/10
[0xFF000000, -1, 0xFF000000] // ff00::/8
];
var LAN6_S = [
[0x00000000, 0xFFFFFFFF], // 0, ::/64 - catch {::, ::1, ::ffff:0:0/96, ::ffff:0:0:0/96}, may remove
[0x00000000, 0xFFFFFFFF], // 1, 64:ff9b::/64 - catch {64:ff9b::/96, NAT64}, may remove
[0x00010000, 0xFFFF0000], // 2, 64:ff9b:1::/48 - NAT64, may remove
[0x00000000, 0xFFFFFFFF] // 3, 100::/64
];