Files
gfw-pac/gfw-pac.py

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()