-
Notifications
You must be signed in to change notification settings - Fork 280
/
gfw-pac.py
executable file
·161 lines (140 loc) · 5.91 KB
/
gfw-pac.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import json
import urllib.request, urllib.error, urllib.parse
from argparse import ArgumentParser
import ipaddress
def parse_args():
parser = ArgumentParser()
parser.add_argument('-f', '--file', dest='output', required=True,
help='输出的PAC文件名', metavar='PAC')
parser.add_argument('-p', '--proxy', dest='proxy', required=True,
help='代理服务器, '
'例如, "PROXY 127.0.0.1:3128;"',
metavar='PROXY')
parser.add_argument('--proxy-domains', dest='user_rule',
help='直接通过代理域名的文件,每行一个')
parser.add_argument('--direct-domains', dest='direct_rule',
help='直连的域名文件,每行一个')
parser.add_argument('--localtld-domains', dest='localtld_rule',
help='本地 TLD 规则文件, 不走代理, 每行一个,以 . 开头')
parser.add_argument('--ip-file', dest='ip_file', required=True,
help='中国IP地址段文件')
return parser.parse_args()
def convert_cidr(cidr):
if '/' in cidr:
network = ipaddress.ip_network(cidr.strip(), strict=False)
network_address = int(network.network_address) >> (network.max_prefixlen - network.prefixlen)
else:
network = ipaddress.ip_address(cidr.strip())
network_address = network
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地址 """
args = parse_args()
with open(args.ip_file, 'r') as file:
cidrs = file.read().splitlines()
converted_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(',')"
def generate_pac_fast(domains, proxy, direct_domains, cidrs, local_tlds):
# render the pac file
with open('./pac-template', 'r') as f:
proxy_content = f.read()
domains_list = []
for domain in domains:
domains_list.append(domain)
proxy_content = proxy_content.replace('__PROXY__', json.dumps(str(proxy)))
proxy_content = proxy_content.replace(
'__DOMAINS__',
json.dumps(domains_list, sort_keys=True, separators=(',', ':'))
)
direct_domains_list = []
for domain in direct_domains:
direct_domains_list.append(domain)
proxy_content = proxy_content.replace(
'__DIRECT_DOMAINS__',
json.dumps(direct_domains_list, sort_keys=True, separators=(',', ':'))
)
proxy_content = proxy_content.replace(
'__CIDRS__', cidrs
)
tlds_list = []
for domain in local_tlds:
tlds_list.append(domain)
proxy_content = proxy_content.replace(
'__LOCAL_TLDS__',
json.dumps(tlds_list, sort_keys=True, separators=(',', ':'))
)
return proxy_content
def main():
args = parse_args()
user_rule = None
direct_rule = None
localtld_rule = None
if args.user_rule:
userrule_parts = urllib.parse.urlsplit(args.user_rule)
if not userrule_parts.scheme or not userrule_parts.netloc:
# It's not an URL, deal it as local file
with open(args.user_rule, 'r') as f:
user_rule = f.read()
else:
# Yeah, it's an URL, try to download it
print('Downloading user rules file from %s' % args.user_rule)
user_rule = urllib.request.urlopen(args.user_rule, timeout=10).read().decode('utf-8')
user_rule = user_rule.splitlines(False)
if args.direct_rule:
directrule_parts = urllib.parse.urlsplit(args.direct_rule)
if not directrule_parts.scheme or not directrule_parts.netloc:
# It's not an URL, deal it as local file
with open(args.direct_rule, 'r') as f:
direct_rule = f.read()
else:
# Yeah, it's an URL, try to download it
print('Downloading user rules file from %s' % args.user_rule)
direct_rule = urllib.request.urlopen(args.direct_rule, timeout=10).read().decode('utf-8')
direct_rule = direct_rule.splitlines(False)
else:
direct_rule = []
if args.localtld_rule:
tldrule_parts = urllib.parse.urlsplit(args.localtld_rule)
if not tldrule_parts.scheme or not tldrule_parts.netloc:
# It's not an URL, deal it as local file
with open(args.localtld_rule, 'r') as f:
localtld_rule = f.read()
else:
# Yeah, it's an URL, try to download it
print('Downloading local tlds rules file from %s' % args.user_rule)
localtld_rule = urllib.request.urlopen(args.localtld_rule, timeout=10).read().decode('utf-8')
localtld_rule = localtld_rule.splitlines(False)
else:
localtld_rule = []
cidrs = generate_cnip_cidrs()
# domains = reduce_domains(domains)
pac_content = generate_pac_fast(user_rule, args.proxy, direct_rule, cidrs, localtld_rule)
with open(args.output, 'w') as f:
f.write(pac_content)
if __name__ == '__main__':
main()