softroute/scripts/gfw_rules_generator.py
2025-11-30 12:24:03 +08:00

114 lines
4.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import argparse
import base64
import requests
import os
import sys
# GFWList 官方(或常见)的地址
GFWLIST_URL = "https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt"
def log_info(message):
print(f"[INFO] {message}")
# 注意foreign_dns_list 和 domestic_dns_list 现在是列表
def generate_rules(proxy_server, foreign_dns_list, domestic_dns_list, ipset_name="gfwlist"):
"""
生成 Dnsmasq 和 IPSet 规则文件。
"""
if not foreign_dns_list or not domestic_dns_list:
log_info("错误:必须提供至少一个国内 DNS 和一个国外 DNS。")
sys.exit(1)
try:
log_info(f"正在从 {GFWLIST_URL} 下载 GFWList...")
# 尝试通过代理下载 GFWList (这里假设环境已配置或可以直接访问 GitHub)
response = requests.get(GFWLIST_URL, timeout=30)
response.raise_for_status()
# 1. 解码 GFWList
try:
content = base64.b64decode(response.text).decode('utf-8')
except:
# 如果内容不是 base64 编码,则直接使用
content = response.text
except requests.exceptions.RequestException as e:
log_info(f"下载 GFWList 失败,请检查网络连接: {e}")
# 如果下载失败,中止脚本以避免生成空文件
sys.exit(1)
domains = set()
for line in content.splitlines():
line = line.strip()
if not line or line.startswith(('#', '!')):
continue
# 简单提取域名
if line.startswith('||'):
domain = line[2:]
elif line.startswith('.'):
domain = line[1:]
elif '/' in line:
continue
else:
domain = line
# 过滤掉 IP 地址,只保留域名
if domain and '.' in domain and not domain.startswith('*.'):
domains.add(domain)
# 2. 生成 DNSMasq 配置文件
dnsmasq_conf_path = "/etc/dnsmasq.d/gfwlist_router.conf"
# 代理的上游 DNS (只使用提供的第一个国外 DNS)
primary_foreign_dns = foreign_dns_list[0]
proxy_port = proxy_server.split(':')[1]
proxy_dns_server = f"{primary_foreign_dns}#{proxy_port}"
try:
with open(dnsmasq_conf_path, 'w') as f:
f.write(f"# Dnsmasq rules generated by gfw_rules_generator.py on {os.path.basename(__file__)}\n")
# 写入所有国内 DNS 服务器
for dns in domestic_dns_list:
f.write(f"server={dns}\n")
# GFWList 域名设置 (通过代理的上游 DNS 解析,并加入 IPSet)
for domain in sorted(list(domains)):
# 1. 将域名加入 IPSet 集合 (由 dnsmasq 动态处理)
f.write(f"ipset=/{domain}/{ipset_name}\n")
# 2. 通过代理的 DNS 服务器解析 (流量会走 redsocks/ssh)
f.write(f"server=/{domain}/{proxy_dns_server}\n")
log_info(f"已生成 Dnsmasq 规则到 {dnsmasq_conf_path},包含 {len(domains)} 个域名。")
except IOError as e:
log_info(f"写入 Dnsmasq 文件失败: {e}")
sys.exit(1)
# 3. 确保 IPSet 集合存在 (必须在 Dnsmasq 启动前存在)
try:
# 使用 hash:ip 集合,如果集合已存在则清除其内容 (flush)
os.system(f"sudo ipset create {ipset_name} hash:ip family inet hashsize 1024 maxelem 65536 &>/dev/null || sudo ipset flush {ipset_name}")
log_info(f"已确保 IPSet 集合 '{ipset_name}' 存在。")
except Exception as e:
log_info(f"创建 IPSet 失败: {e}")
sys.exit(1)
return True
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="GFWList Rules Generator for Dnsmasq/IPSet")
# 允许接受多个参数 (nargs='+')
parser.add_argument("-p", "--proxy", required=True, help="SOCKS5 Proxy server (e.g., 127.0.0.1:1086)")
parser.add_argument("-f", "--foreign-dns", nargs='+', required=True, help="Foreign DNS IP(s) (e.g., 8.8.8.8 9.9.9.9)")
parser.add_argument("-d", "--domestic-dns", nargs='+', required=True, help="Domestic DNS IP(s) (e.g., 223.5.5.5 114.114.114.114)")
parser.add_argument("-n", "--name", default="gfwlist", help="IPSet Name")
args = parser.parse_args()
log_info("开始生成 GFWList 规则...")
generate_rules(args.proxy, args.foreign_dns, args.domestic_dns, args.name)
log_info("规则生成完成。")