144 lines
5.2 KiB
Python
Executable File
144 lines
5.2 KiB
Python
Executable File
#!/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 = network.network_address
|
|
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
|
|
|
|
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))
|
|
|
|
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()
|