{"id":13511491,"url":"https://github.com/zfl9/chinadns-ng","last_synced_at":"2025-05-16T01:06:17.235Z","repository":{"id":36544597,"uuid":"198151653","full_name":"zfl9/chinadns-ng","owner":"zfl9","description":"chinadns 重构增强版，支持域名分流、ipset/nftset、UDP/TCP/DoT","archived":false,"fork":false,"pushed_at":"2025-03-27T12:01:44.000Z","size":2545,"stargazers_count":1209,"open_issues_count":3,"forks_count":193,"subscribers_count":39,"default_branch":"master","last_synced_at":"2025-04-13T16:53:29.473Z","etag":null,"topics":["chinadns","chinadns-ng","dns","dns-over-tls","dnsmasq","dot","gfwlist","ipset","iptables","netlink","nftables","ss-tproxy"],"latest_commit_sha":null,"homepage":"","language":"Zig","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zfl9.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2019-07-22T05:13:50.000Z","updated_at":"2025-04-13T11:08:52.000Z","dependencies_parsed_at":"2022-08-01T00:08:09.985Z","dependency_job_id":"c289ccef-40fe-4094-a8ec-3c6be85957b5","html_url":"https://github.com/zfl9/chinadns-ng","commit_stats":{"total_commits":803,"total_committers":3,"mean_commits":267.6666666666667,"dds":0.007471980074719831,"last_synced_commit":"39d4881f83fa139b52cff9d8e306c4313bf758ad"},"previous_names":[],"tags_count":31,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zfl9%2Fchinadns-ng","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zfl9%2Fchinadns-ng/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zfl9%2Fchinadns-ng/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zfl9%2Fchinadns-ng/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zfl9","download_url":"https://codeload.github.com/zfl9/chinadns-ng/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254448579,"owners_count":22072764,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["chinadns","chinadns-ng","dns","dns-over-tls","dnsmasq","dot","gfwlist","ipset","iptables","netlink","nftables","ss-tproxy"],"created_at":"2024-08-01T03:00:51.802Z","updated_at":"2025-05-16T01:06:12.226Z","avatar_url":"https://github.com/zfl9.png","language":"Zig","funding_links":[],"categories":["Zig"],"sub_categories":[],"readme":"## 简介\n\n[ChinaDNS](https://github.com/shadowsocks/ChinaDNS) 的个人重构版本，功能简述：\n\n- 基于 epoll、netlink(ipset/nftset) 实现，性能更强。\n- 完整支持 IPv4 和 IPv6 协议，兼容 EDNS 请求和响应。\n- 手动指定国内 DNS 和可信 DNS，而非自动识别，更加可控。\n- 修复原版对保留地址的处理问题，去除过时特性，只留核心功能。\n- 修复原版对可信 DNS 先于国内 DNS 返回而导致判断失效的问题。\n- 支持 `chnlist/gfwlist` 域名列表，并深度优化了性能以及内存占用。\n- 支持纯域名分流：要么走china上游，要么走trust上游，不进行IP测试。\n- 可动态收集域名的解析结果IP至`ipset/nftset`，辅助各种代理/分流场景。\n- 支持`nftables set`，并针对 IP add 操作进行了性能优化，避免操作延迟。\n- 更加细致的 no-ipv6(AAAA) 控制，可根据域名类型，IP测试结果进行过滤。\n- DNS 缓存、stale 缓存模式、缓存预刷新、缓存忽略名单（不缓存的域名）。\n- 支持 tag:none 域名的判定结果缓存，避免重复请求和判定，减少DNS泄露。\n- 除默认的 china、trust 组外，还有 6 个自定义组（上游DNS、ipset/nftset）。\n\n---\n\n对于常规的使用模式，大致原理和流程可总结为：\n\n- 两组DNS上游：china组(大陆DNS)、trust组(国外DNS)。\n\n- 两个域名列表：chnlist.txt(大陆域名)、gfwlist.txt(受污染域名)。\n\n- chnlist.txt域名(tag:chn域名)，转发给china组，保证大陆域名不会被解析到国外，对大陆域名cdn友好。\n\n- gfwlist.txt域名(tag:gfw域名)，转发给trust组，trust需返回未受污染的结果，比如走代理，具体方式不限。\n\n- 其他域名(tag:none域名)，同时转发给china组和trust组，如果china组解析结果(A/AAAA)是大陆ip，则采纳china组，否则采纳trust组。是否为大陆ip的核心依据，就是测试ip是否在`ipset-name4/6`指定的地址集合中。\n\n- 若启用了tag:none域名的判决缓存，则同一域名的后续请求只会转发给特定的上游组（china组或trust组，具体取决于之前的ip测试结果），建议启用此功能，避免重复请求、判定，减少DNS泄露。\n\n- 如果使用纯域名分流模式，则不存在tag:none域名，因此要么走china组，要么走trust组，可避免dns泄露问题。\n\n- 若启用`--add-tagchn-ip`，则tag:chn域名的解析结果IP会被动态添加到指定的ipset/nftset，配合chnroute透明代理分流时，可用于实现大陆域名必走直连，使dns分流与ip分流一致；类似 dnsmasq 的 ipset/nftset 功能。\n\n- 若启用`--add-taggfw-ip`，则tag:gfw域名的解析结果IP会被动态添加到指定的ipset/nftset，可用来实现gfwlist透明代理分流；也可配合chnroute透明代理分流，用来收集黑名单域名的IP，用于iptables/nftables操作，比如确保黑名单域名必走代理，即使某些黑名单域名的IP是大陆IP。\n\n\u003e chinadns-ng 根据域名 tag 来执行不同逻辑，包括 ipset/nftset 的逻辑（test、add），见 [原理](#tagchntaggfwtagnone-%E6%98%AF%E6%8C%87%E4%BB%80%E4%B9%88)。\n\n## 编译\n\n\u003e 请前往 [releases](https://github.com/zfl9/chinadns-ng/releases) 页面下载可执行文件，添加可执行权限，放到 PATH 路径下（如 `/usr/local/bin/`）。\n\n\u003cdetails\u003e\u003csummary\u003e\u003cb\u003e点我展开编译说明\u003c/b\u003e\u003c/summary\u003e\u003cp\u003e\n\n---\n\n**zig 工具链**\n\n- 从 2024.03.07 版本起，程序使用 Zig + C 语言编写，`zig` 是唯一需要的工具链。\n- 从 [ziglang.org](https://ziglang.org/download/) 下载 zig 0.10.1，请根据当前（编译）主机的架构来选择合适的版本。\n- 将解压后的目录加入 PATH 环境变量，执行 `zig version`，检查是否有输出 `0.10.1`。\n- 注意，目前必须使用 zig 0.10.1 版本，因为 0.11、master 版本暂时不支持 async 特性。\n\n---\n\n如果要构建 DoT 支持，请带上 `-Dwolfssl` 参数，构建过程需要以下依赖：\n- `wget` 或 `curl` 用于下载 wolfssl 源码包；`tar` 用于解压缩\n- `autoconf`、`automake`、`libtool`、`make` 用于构建 wolfssl\n\n针对 x86_64(v3/v4)、aarch64 的 wolfssl 构建已默认启用硬件指令加速，若目标硬件(CPU)不支持相关指令（部分树莓派阉割了 aes 相关指令），请指定 `-Dwolfssl-noasm` 选项，避免运行 chinadns-ng 时出现 SIGILL 非法指令异常。\n\n---\n\n如果遇到编译错误，请先执行 `zig build clean-all`，然后重新执行相关构建命令。\n\n可执行文件在 `./zig-out/bin` 目录，将文件安装（复制）到目标主机 PATH 路径下即可。\n\n---\n\n```bash\ngit clone https://github.com/zfl9/chinadns-ng\ncd chinadns-ng\n\n# 本机 (若构建失败，请手动指定\"-Dtarget\"和\"-Dcpu\")\nzig build # 链接到glibc\nzig build -Dtarget=native-native-musl # 静态链接到musl\n\n# x86\nzig build -Dtarget=i386-linux-musl -Dcpu=i686\nzig build -Dtarget=i386-linux-musl -Dcpu=pentium4\n\n# x86_64\nzig build -Dtarget=x86_64-linux-musl -Dcpu=x86_64 # v1\nzig build -Dtarget=x86_64-linux-musl -Dcpu=x86_64_v2\nzig build -Dtarget=x86_64-linux-musl -Dcpu=x86_64_v3\nzig build -Dtarget=x86_64-linux-musl -Dcpu=x86_64_v4\n\n# arm\nzig build -Dtarget=arm-linux-musleabi -Dcpu=generic+v5t+soft_float\nzig build -Dtarget=arm-linux-musleabi -Dcpu=generic+v5te+soft_float\nzig build -Dtarget=arm-linux-musleabi -Dcpu=generic+v6+soft_float\nzig build -Dtarget=arm-linux-musleabi -Dcpu=generic+v6t2+soft_float\nzig build -Dtarget=arm-linux-musleabi -Dcpu=generic+v7a # soft_float\nzig build -Dtarget=arm-linux-musleabihf -Dcpu=generic+v7a # hard_float\n\n# aarch64\nzig build -Dtarget=aarch64-linux-musl -Dcpu=generic+v8a\nzig build -Dtarget=aarch64-linux-musl -Dcpu=generic+v9a\n\n# mips + soft_float\n# 请先阅读 https://www.zfl9.com/zig-mips.html\nARCH=mips32 \u0026\u0026 MIPS_M_ARCH=$ARCH MIPS_SOFT_FP=1 zig build -Dtarget=mips-linux-musl -Dcpu=$ARCH+soft_float\nARCH=mips32r2 \u0026\u0026 MIPS_M_ARCH=$ARCH MIPS_SOFT_FP=1 zig build -Dtarget=mips-linux-musl -Dcpu=$ARCH+soft_float\nARCH=mips32r3 \u0026\u0026 MIPS_M_ARCH=$ARCH MIPS_SOFT_FP=1 zig build -Dtarget=mips-linux-musl -Dcpu=$ARCH+soft_float\nARCH=mips32r5 \u0026\u0026 MIPS_M_ARCH=$ARCH MIPS_SOFT_FP=1 zig build -Dtarget=mips-linux-musl -Dcpu=$ARCH+soft_float\n\n# mipsel + soft_float\n# 请先阅读 https://www.zfl9.com/zig-mips.html\nARCH=mips32 \u0026\u0026 MIPS_M_ARCH=$ARCH MIPS_SOFT_FP=1 zig build -Dtarget=mipsel-linux-musl -Dcpu=$ARCH+soft_float\nARCH=mips32r2 \u0026\u0026 MIPS_M_ARCH=$ARCH MIPS_SOFT_FP=1 zig build -Dtarget=mipsel-linux-musl -Dcpu=$ARCH+soft_float\nARCH=mips32r3 \u0026\u0026 MIPS_M_ARCH=$ARCH MIPS_SOFT_FP=1 zig build -Dtarget=mipsel-linux-musl -Dcpu=$ARCH+soft_float\nARCH=mips32r5 \u0026\u0026 MIPS_M_ARCH=$ARCH MIPS_SOFT_FP=1 zig build -Dtarget=mipsel-linux-musl -Dcpu=$ARCH+soft_float\n\n# mips + hard_float\n# 请先阅读 https://www.zfl9.com/zig-mips.html\nARCH=mips32 \u0026\u0026 MIPS_M_ARCH=$ARCH zig build -Dtarget=mips-linux-musl -Dcpu=$ARCH\nARCH=mips32r2 \u0026\u0026 MIPS_M_ARCH=$ARCH zig build -Dtarget=mips-linux-musl -Dcpu=$ARCH\nARCH=mips32r3 \u0026\u0026 MIPS_M_ARCH=$ARCH zig build -Dtarget=mips-linux-musl -Dcpu=$ARCH\nARCH=mips32r5 \u0026\u0026 MIPS_M_ARCH=$ARCH zig build -Dtarget=mips-linux-musl -Dcpu=$ARCH\n\n# mipsel + hard_float\n# 请先阅读 https://www.zfl9.com/zig-mips.html\nARCH=mips32 \u0026\u0026 MIPS_M_ARCH=$ARCH zig build -Dtarget=mipsel-linux-musl -Dcpu=$ARCH\nARCH=mips32r2 \u0026\u0026 MIPS_M_ARCH=$ARCH zig build -Dtarget=mipsel-linux-musl -Dcpu=$ARCH\nARCH=mips32r3 \u0026\u0026 MIPS_M_ARCH=$ARCH zig build -Dtarget=mipsel-linux-musl -Dcpu=$ARCH\nARCH=mips32r5 \u0026\u0026 MIPS_M_ARCH=$ARCH zig build -Dtarget=mipsel-linux-musl -Dcpu=$ARCH\n\n# mips64/mips64el 请前往 releases 页面下载预编译的可执行文件\n# 如果想自己编译，请先前往 zig 根目录，按顺序 apply 这两个补丁\n# - https://github.com/ziglang/zig/pull/14541.patch\n# - https://github.com/ziglang/zig/pull/14556.patch\n\n# riscv64\nzig build -Dtarget=riscv64-linux-musl\n```\n\n\u003c/p\u003e\u003c/details\u003e\n\n## Docker\n\n因为要访问内核的 ipset/nftset，docker run 时请带上 `--privileged` 参数。\n\n请前往 [releases](https://github.com/zfl9/chinadns-ng/releases) 页面下载可执行文件（无依赖），cp 至目标容器，运行即可。\n\n## OpenWrt\n\n- 某些科学上网插件附带了 chinadns-ng，请查看对应插件的文档。\n- https://github.com/pexcn/openwrt-chinadns-ng (未适配 2.0 的新功能)。\n\n## 配置示例\n\n- chinadns-ng 通常与 iptables/nftables 透明代理一起使用。\n- chinadns-ng 也可作为单纯的 DNS 转发器（如 UDP-\u003eTCP）使用。\n- 下面列举的 3 种分流模式配置其实都是 [zfl9/ss-tproxy](https://github.com/zfl9/ss-tproxy) 中的相关用例。\n- 程序不会自动创建 ipset/nftset 集合；如需 ip test/add，请先导入/创建 set。\n- 所有带 ipset 字眼的配置都支持 nftset，如需使用 nftset，请阅读 [nftset 说明](#ipsetnftset-相关说明)。\n\n---\n\n### chnroute 分流\n\n- chnlist.txt (tag:chn) 走国内上游，将 IP 收集至 `chnip,chnip6` ipset（可选）\n- gfwlist.txt (tag:gfw) 走可信上游，将 IP 收集至 `gfwip,gfwip6` ipset（可选）\n- 其他域名 (tag:none) 同时走国内和可信上游，根据 IP 测试结果决定最终响应\n\n\u003cdetails\u003e\u003csummary\u003e\u003cb\u003e点我展开\u003c/b\u003e\u003c/summary\u003e\u003cp\u003e\n\n```shell\n# 监听地址和端口\nbind-addr 0.0.0.0\nbind-port 53\n\n# 国内上游、可信上游\nchina-dns 223.5.5.5\ntrust-dns tcp://8.8.8.8\n\n# 域名列表，用于分流\nchnlist-file /etc/chinadns/chnlist.txt\ngfwlist-file /etc/chinadns/gfwlist.txt\n# chnlist-first\n\n# 收集 tag:chn、tag:gfw 域名的 IP (可选)\nadd-tagchn-ip chnip,chnip6\nadd-taggfw-ip gfwip,gfwip6\n\n# 测试 tag:none 域名的 IP (针对国内上游)\nipset-name4 chnroute\nipset-name6 chnroute6\n\n# dns 缓存\ncache 4096\ncache-stale 86400\ncache-refresh 20\n\n# verdict 缓存 (用于 tag:none 域名)\nverdict-cache 4096\n\n# 详细日志\n# verbose\n```\n\n\u003c/p\u003e\u003c/details\u003e\n\n---\n\n### gfwlist 分流\n\n- gfwlist.txt (tag:gfw) 走可信上游，将 IP 收集至 `gfwip,gfwip6` ipset（可选）\n- 其他域名 (tag:chn) 走国内上游，不需要收集 IP（未指定 add-tagchn-ip）\n\n\u003cdetails\u003e\u003csummary\u003e\u003cb\u003e点我展开\u003c/b\u003e\u003c/summary\u003e\u003cp\u003e\n\n```shell\n# 监听地址和端口\nbind-addr 0.0.0.0\nbind-port 53\n\n# 国内上游、可信上游\nchina-dns 223.5.5.5\ntrust-dns tcp://8.8.8.8\n\n# 域名列表，用于分流\n# 未被 gfwlist.txt 匹配的归为 tag:chn\ngfwlist-file /etc/chinadns/gfwlist.txt\ndefault-tag chn\n\n# 收集 tag:gfw 域名的 IP (可选)\nadd-taggfw-ip gfwip,gfwip6\n\n# dns 缓存\ncache 4096\ncache-stale 86400\ncache-refresh 20\n\n# 详细日志\n# verbose\n```\n\n\u003c/p\u003e\u003c/details\u003e\n\n---\n\n### chnlist 分流\n\n- chnlist.txt (tag:chn) 走国内上游，将 IP 收集至 `chnip,chnip6` ipset（可选）\n- 其他域名 (tag:gfw) 走可信上游，不需要收集 IP（未指定 add-taggfw-ip）\n\n\u003cdetails\u003e\u003csummary\u003e\u003cb\u003e点我展开\u003c/b\u003e\u003c/summary\u003e\u003cp\u003e\n\n```shell\n# 监听地址和端口\nbind-addr 0.0.0.0\nbind-port 53\n\n# 国内上游、可信上游\nchina-dns 223.5.5.5\ntrust-dns tcp://8.8.8.8\n\n# 域名列表，用于分流\n# 未被 chnlist.txt 匹配的归为 tag:gfw\nchnlist-file /etc/chinadns/chnlist.txt\ndefault-tag gfw\n\n# 收集 tag:chn 域名的 IP (可选)\nadd-tagchn-ip chnip,chnip6\n\n# dns 缓存\ncache 4096\ncache-stale 86400\ncache-refresh 20\n\n# 详细日志\n# verbose\n```\n\n\u003c/p\u003e\u003c/details\u003e\n\n---\n\n### DNS 转发器\n\n- 转发器不执行分流操作，但其他功能仍可正常使用，如 DNS 缓存、ip add、ipv6 过滤等。\n- 核心在于 `-d chn`，由于未指定域名列表，因此所有查询都是 tag:chn，从而实现单纯转发。\n\n```bash\n# 127.0.0.1:53(udp/tcp) =\u003e 1.1.1.1(udp/tcp/tls)\n# 允许指定任意多个 upstream，多次给出 -c 选项即可\n# 使用 -d gfw 也是一样的，只不过将 -c 改为 -t 选项\nchinadns-ng -b 127.0.0.1 -l 53 -d chn -c 1.1.1.1\nchinadns-ng -b 127.0.0.1 -l 53 -d chn -c udp://1.1.1.1\nchinadns-ng -b 127.0.0.1 -l 53 -d chn -c tcp://1.1.1.1\nchinadns-ng -b 127.0.0.1 -l 53 -d chn -c tls://1.1.1.1\n```\n\n## 命令选项\n\n- `-f` 短选项、`--foobar` 长选项\n- `--foo \u003cvalue\u003e`：选项值是 required 的\n- `--bar [value]`：选项值是 optional 的\n- `--flag`：没有选项值，即 bool/flag 选项\n- 配置文件中使用长选项格式（没有`--`），如 `verbose`\n\n\u003cdetails\u003e\u003csummary\u003e\u003cb\u003e点我展开所有选项\u003c/b\u003e\u003c/summary\u003e\u003cp\u003e\n\n```console\n$ chinadns-ng --help\nusage: chinadns-ng \u003coptions...\u003e. the existing options are as follows:\n -C, --config \u003cpath\u003e                  format similar to the long option\n -b, --bind-addr \u003cip\u003e                 listen address, default: 127.0.0.1\n -l, --bind-port \u003cport[@proto]\u003e       listen port number, default: 65353\n -c, --china-dns \u003cupstreams\u003e          china dns server, default: \u003c114 DNS\u003e\n -t, --trust-dns \u003cupstreams\u003e          trust dns server, default: \u003cGoogle DNS\u003e\n -m, --chnlist-file \u003cpaths\u003e           path(s) of chnlist, '-' indicate stdin\n -g, --gfwlist-file \u003cpaths\u003e           path(s) of gfwlist, '-' indicate stdin\n -M, --chnlist-first                  match chnlist first, default gfwlist first\n -d, --default-tag \u003ctag\u003e              chn or gfw or \u003cuser-tag\u003e or none(default)\n -a, --add-tagchn-ip [set4,set6]      add the ip of name-tag:chn to ipset/nftset\n                                      use '--ipset-name4/6' setname if no value\n -A, --add-taggfw-ip \u003cset4,set6\u003e      add the ip of name-tag:gfw to ipset/nftset\n -4, --ipset-name4 \u003cset4\u003e             ip test for tag:none, default: chnroute\n -6, --ipset-name6 \u003cset6\u003e             ip test for tag:none, default: chnroute6\n                                      if setname contains @, then use nftset\n                                      format: family_name@table_name@set_name\n --group \u003cname\u003e                       define rule group: {dnl, upstream, ipset}\n --group-dnl \u003cpaths\u003e                  domain name list for the current group\n --group-upstream \u003cupstreams\u003e         upstream dns server for the current group\n --group-ipset \u003cset4,set6\u003e            add the ip of the current group to ipset\n -N, --no-ipv6 [rules]                tag:\u003cname\u003e[@ip:*], ip:china, ip:non_china\n                                      if no rules, then filter all AAAA queries\n --filter-qtype \u003cqtypes\u003e              filter queries with the given qtype (u16)\n --cache \u003csize\u003e                       enable dns caching, size 0 means disabled\n --cache-stale \u003cN\u003e                    use stale cache: expired time \u003c= N(second)\n --cache-refresh \u003cN\u003e                  pre-refresh the cached data if TTL \u003c= N(%)\n --cache-nodata-ttl \u003cttl\u003e             TTL of the NODATA response, default is 60\n --cache-ignore \u003cdomain\u003e              ignore the dns cache for this domain(suffix)\n --cache-db \u003cpath\u003e                    dns cache persistence (from/to db file)\n --verdict-cache \u003csize\u003e               enable verdict caching for tag:none domains\n --verdict-cache-db \u003cpath\u003e            verdict cache persistence (from/to db file)\n --hosts [path]                       load hosts file, default path is /etc/hosts\n --dns-rr-ip \u003cnames\u003e=\u003cips\u003e            define local resource records of type A/AAAA\n --cert-verify                        enable SSL certificate validation, default: no\n --ca-certs \u003cpath\u003e                    CA certs path for SSL certificate validation\n --no-ipset-blacklist                 add-ip: don't enable built-in ip blacklist\n                                      blacklist: 127.0.0.0/8, 0.0.0.0/8, ::1, ::\n -o, --timeout-sec \u003csec\u003e              response timeout of upstream, default: 5\n -p, --repeat-times \u003cnum\u003e             num of packets to trustdns, default:1, max:5\n -n, --noip-as-chnip                  allow no-ip reply from chinadns (tag:none)\n -f, --fair-mode                      enable fair mode (nop, only fair mode now)\n -r, --reuse-port                     enable SO_REUSEPORT, default: \u003cdisabled\u003e\n -v, --verbose                        print the verbose log, default: \u003cdisabled\u003e\n -V, --version                        print `chinadns-ng` version number and exit\n -h, --help                           print `chinadns-ng` help information and exit\nbug report: https://github.com/zfl9/chinadns-ng. email: zfl9.com@gmail.com (Otokaze)\n```\n\n\u003c/p\u003e\u003c/details\u003e\n\n---\n\n### config\n\n- `-C/--config \u003cpath\u003e` 指定配置文件路径，支持多个配置文件。\n  - 配置文件是一个 UTF-8 纯文本文件，没有特定的文件扩展名。\n  - 格式 `optname [value]`，`optname` 是不带 `--` 的长命令行选项名。\n  - 例如 `bind-addr 127.0.0.1`、`bind-port 65353`、`noip-as-chnip`。\n  - 空白行、`#`开头的行 被忽略；不支持行尾注释（如`verbose #foo`）。\n  - 文件中可多次使用 `config \u003cpath\u003e` 配置行来实现配置文件包含的效果。\n- `-C/--config` 只是从给定文本文件读取“命令行选项”并处理，无其他特别之处。\n- `-C/--config` 和其他命令行选项可随意混用，不可重复的选项以最后一个为准。\n\n### bind-addr、bind-port\n\n- `bind-addr` 用于指定监听地址，默认为 127.0.0.1。\n  - 2023.10.28 版本起，若监听地址为 `::`，则允许来自 IPv4/IPv6 的 DNS 查询。\n  - 2024.03.07 版本起，`bind-addr` 允许指定多次，以便监听多个不同的 ip 地址。\n- `bind-port` 用于指定监听端口，默认为 65353。\n  - 2024.03.07 版本起，将同时监听 UDP 和 TCP 端口，之前只监听了 UDP。\n  - 2024.03.25 版本起，可以给 `bind-port` 指定要监听的协议（UDP、TCP）：\n    - `--bind-port 65353`：监听 UDP 和 TCP（默认）。\n    - `--bind-port 65353@udp`：只监听 UDP。\n    - `--bind-port 65353@tcp`：只监听 TCP。\n  - 2024.07.16 版本起，`bind-port` 允许指定多次，以便监听多个不同的 port。\n\n### china-dns、trust-dns\n\n- `china-dns` 选项指定国内上游 DNS 服务器，多个用逗号隔开。\n- `trust-dns` 选项指定可信上游 DNS 服务器，多个用逗号隔开。\n  - 国内上游默认为 `114.114.114.114`，可信上游默认为 `8.8.8.8`。\n  - 组内的多个上游服务器是并发查询的模式，采纳最先返回的那个结果。\n  - 2024.03.07 版本起，允许多次指定 `china-dns`、`trust-dns` 选项/配置。\n  - 2024.03.07 版本起，每组上游的服务器数量不受限制（之前最多两个）。\n\n### 上游服务器的地址格式\n\n- 完整格式：`proto:// host@ ip #port ?count=N ?life=N`\n- 注：加空格只是为了方便阅读和说明，实际格式中并没有空格。\n- `proto://`：可省略，查询协议，默认为`无`。\n  - `无`：UDP/TCP 上游，根据查询方的传入协议来决定使用 UDP 查询还是 TCP 查询。\n  - `udp://`：UDP 上游。\n  - `tcp://`：TCP 上游。\n  - `tls://`：DoT 上游（需使用 wolfssl 版本）。\n- `host@`：可省略，用于 DoT 上游。\n  - 提供 SSL/TLS 握手时的 SNI（服务器名称指示）信息。\n  - 启用 SSL/TLS 证书验证时，将检查证书中的域名是否与之匹配。\n- `ip`：不可省略，支持 IPv4 和 IPv6 地址（不需要用 `[]` 括起来）。\n- `#port`：可省略，默认为所选定协议的标准端口（UDP/TCP 是 53，DoT 是 853）。\n- `?count=N`：可省略，默认为 10，表示单个会话最多处理多少查询，见 [#189](https://github.com/zfl9/chinadns-ng/issues/189)。\n  - 0 表示不限制，只要上游不主动断开连接，对应 TCP/TLS 会话就一直存在。\n- `?life=N`：可省略，默认为 10，表示单个会话最多存活多少秒，见 [#189](https://github.com/zfl9/chinadns-ng/issues/189)。\n  - 0 表示不限制，只要上游不主动断开连接，对应 TCP/TLS 会话就一直存在。\n\n### chnlist-file、gfwlist-file、chnlist-first\n\n- `chnlist-file` 白名单 [域名列表文件](#域名列表)，命中的域名只走国内 DNS。\n- `gfwlist-file` 黑名单 [域名列表文件](#域名列表)，命中的域名只走可信 DNS。\n  - 2023.04.01 版本起，可指定多个路径，逗号隔开，如 `-g a.txt,b.txt`。\n  - 2024.03.07 版本起，可多次指定 `chnlist-file`、`gfwlist-file` 选项。\n- `chnlist-first` 选项表示优先加载 chnlist，默认是优先加载 gfwlist。\n  - 只有 chnlist 和 gfwlist 文件都提供时，`*-first` 才有实际意义。\n\n### default-tag\n\n- `default-tag` 可用于实现\"纯域名分流\"，也可用于实现 [gfwlist分流](#chinadns-ng-也可用于-gfwlist-透明代理分流)。\n- 其核心逻辑是设置 **未匹配任何列表的域名** 的`tag`，并无其他特别之处。\n- 通常与`-g`或`-m`选项一起使用，比如下述例子，实现了\"纯域名分流\"模式：\n  - `-g gfwlist.txt -d chn`：gfw列表的域名走可信上游，其他走国内上游。\n  - `-m chnlist.txt -d gfw`：chn列表的域名走国内上游，其他走可信上游。\n- 如果想了解更多细节，建议看一下 [chinadns-ng 的核心处理流程](#tagchntaggfwtagnone-是指什么)。\n\n### add-tagchn-ip、add-taggfw-ip\n\n- `add-tagchn-ip` 用于动态添加 tag:chn 域名的解析结果 ip 至 ipset/nftset 集合。\n- `add-taggfw-ip` 用于动态添加 tag:gfw 域名的解析结果 ip 至 ipset/nftset 集合。\n  - 参数为`ipv4集合名[,ipv6集合名]`，nftset 格式和注意事项见 [nftset 相关说明](#ipsetnftset-相关说明)。\n  - 对于`add-tagchn-ip`，若未给出集合名，则使用`ipset-name4/6`的那个集合。\n  - 2024.04.13 版本起，可使用特殊集合名 `null` 表示对应集合不会被使用：\n    - `--add-tagchn-ip null,chnip6`：表示不需要收集 ipv4 地址\n    - `--add-tagchn-ip chnip,null`：表示不需要收集 ipv6 地址\n    - `--add-tagchn-ip chnip`：表示不需要收集 ipv6 地址\n    - 注意：仅当 ipv6 集合为 `null` 时，才可被省略\n\n### ipset-name4、ipset-name6\n\n- `ipset-name4` 大陆 IPv4 地址的 ipset/nftset 集合名，默认为 `chnroute` (ipset)。\n- `ipset-name6` 大陆 IPv6 地址的 ipset/nftset 集合名，默认为 `chnroute6` (ipset)。\n- 这两个集合用于 tag:none 域名，用于判定 china 上游的解析结果是否为大陆 IP。\n- 2024.04.13 版本起，也用于 `--no-ipv6` 的 `ip:china`、`ip:non_china` 规则。\n- 2024.04.13 版本起，可使用特殊集合名 `null` 表示对应集合不会被使用。\n\n### ipset/nftset 相关说明\n\n相关配置/命令行选项：\n\n- **ip test**：`ipset-name4`、`ipset-name6`（默认为 chnroute、chnroute6）\n- **ip add**：`add-tagchn-ip`、`add-taggfw-ip`、`group-ipset`（无默认行为）\n- ip add 选项的参数为`ipv4集合名[,ipv6集合名]`，`null`可作为空集合的占位符\n- 注意：所有相关配置要么使用 ipset 后端，要么使用 nftset 后端，不允许混用\n- 程序不会自动创建 ipset/nftset 集合，如果需要，请先手动导入/创建相关集合\n\nipset 相关说明：\n\n- 集合名正常给出即可，如 chnroute\n- res/chnroute.ipset 文件的集合名：`chnroute`\n- res/chnroute6.ipset 文件的集合名：`chnroute6`\n\nnftset 相关说明：\n\n- 集合名的完整格式：`family名@table名@set名`\n- 创建 nftset 集合时，必须带上 `flags interval` 标志\n- 支持的 family：`ip`、`ip6`、`inet`、`arp`、`bridge`、`netdev`\n- res/chnroute.nftset 文件的集合名：`inet@global@chnroute`\n- res/chnroute6.nftset 文件的集合名：`inet@global@chnroute6`\n\n### group、group-*\n\n- `group` 声明一个自定义组（tag），参数值是组（tag）的名字。\n  - 支持最多 6 个自定义组，每个组都有 3 个信息可配置，其中 ipset 可选。\n  - 加载域名列表文件时，优先加载自定义组，然后加载内置组（chn、gfw）。\n  - 内置组的加载顺序没有改变，依旧默认 gfw 优先，使用 `-M` 切换为 chn 优先。\n  - 对于多个自定义组，按照命令行参数/配置顺序，后声明的组具有更高优先级。\n  - 用例 1：将 DDNS域名 划分出来，单独一个组，用域名提供商的 DNS 去解析。\n  - 用例 2：将 公司域名 划分出来，单独一个组，用公司内网专用的 DNS 去解析。\n  - 2024.04.27 版本起，使用 `null` 作为 group 名时，表示过滤该组的域名查询。\n    - null 组只有 `group-dnl` 信息，查询相关域名时，将返回 NODATA 响应消息。\n- `group-dnl` 当前组的[域名列表文件](#域名列表)，多个用逗号隔开，可多次指定。\n- `group-upstream` 当前组的上游 DNS，多个用逗号隔开，可多次指定。\n- `group-ipset` 当前组的 ipset/nftset (可选)，用于收集解析出的结果 IP。\n\n以配置文件举例：\n\n```shell\n# 声明自定义组 \"foo\"\ngroup foo\ngroup-dnl foo.txt\ngroup-upstream 1.1.1.1,8.8.8.8\ngroup-ipset fooip,fooip6\n\n# 声明自定义组 \"bar\"\ngroup bar\ngroup-dnl bar.txt\ngroup-upstream 192.168.1.1\n# 没有 group-ipset，表示不需要 add ip\n```\n\n### no-ipv6\n\n- `no-ipv6` 过滤 AAAA 查询（查询域名的 IPv6 地址），默认不启用。\n  - 此选项可多次指定，选项参数为`过滤规则`。\n    - 如果没有选项参数，则表示过滤所有 AAAA 查询。\n    - 选项参数中可以有多条`过滤规则`，中间用逗号隔开。\n    - 被`过滤规则`匹配的查询将以 NODATA 形式进行响应。\n  - `过滤规则`的完整形式为`tag:域名组@ip:测试结果`。\n    - 每个域名组都有一个 AAAA 过滤器，不同域名组的过滤规则互相独立。\n    - `tag:域名组`是域名组选择器，表示`过滤条件`将要添加到哪个域名组中。\n    - `ip:测试结果`是`过滤条件`，若域名组中的查询符合其条件，则被“过滤”。\n    - 若未指定`tag:域名组@`部分，则该`过滤条件`将添加到每个域名组中。\n    - 若未指定`@ip:测试结果`部分，则该域名组的所有查询都将被“过滤”。\n  - `ip:测试结果`只有以下两种（二选一）：\n    - `ip:china`：若域名解析结果为 **大陆 IPv6 地址**，则“过滤”。\n    - `ip:non_china`：若域名解析结果为 **非大陆 IPv6 地址**，则“过滤”。\n    - IPv6 数据库由 `--ipset-name6` 选项提供，默认为 `chnroute6` (ipset)。\n  - 列举一些 `过滤规则`，以及对应的 AAAA 过滤效果：\n    - `tag:none`：过滤 none 域名组中 所有 AAAA 查询。\n    - `ip:non_china`：过滤 所有 域名组中 解析结果为 '非大陆 IPv6' 的 AAAA 查询。\n    - `tag:none@ip:non_china`：过滤 none 域名组中 解析结果为 '非大陆 IPv6' 的 AAAA 查询。\n\n### filter-qtype\n\n- `filter-qtype` 过滤给定 qtype 的查询，多个用逗号隔开，可多次指定。\n  - `--filter-qtype 64,65`：过滤 SVCB(64)、HTTPS(65) 查询\n\n### cache、cache-*\n\n- `cache` 启用 DNS 缓存，参数是缓存容量（最多缓存多少个请求的响应消息）。\n- `cache-stale` 允许使用 TTL 已过期的（陈旧）缓存，参数是最大过期时长（秒）。\n  - 向查询方返回“陈旧”缓存的同时，自动在后台刷新缓存，以便稍后能使用新数据。\n  - 2024.04.13 版本起，数据类型从 `u16` 改为 `u32`，以允许设置更大的过期时长。\n- `cache-refresh` 若当前查询的缓存的 TTL 不足初始值的百分之 N，则提前在后台刷新。\n- `cache-nodata-ttl` 给 NODATA 响应提供默认的缓存时长，默认 60 秒，0 表示不缓存。\n- `cache-ignore` 不要缓存给定的域名（后缀，最高支持 8 级），此选项可多次指定。\n- `cache-db` 启用缓存持久化，参数是 db 文件路径（可以不预先创建）。\n  - 进程启动时，自动从 db 恢复缓存；进程退出时，自动将缓存写回至 db。\n  - “进程退出”是指进程收到`SIGTERM/SIGINT`信号，即`kill \u003cPID\u003e`或`CTRL+C`。\n  - “缓存写回”可通过`SIGUSR1`信号强制触发（未启用持久化则写至`/tmp/chinadns@cache.db`）。\n  - 为了降低性能开销，恢复缓存时不进行校验，请勿修改 db 文件，请勿跨平台共享 db 文件。\n  - 有时可能需要手动清空 db 文件来丢弃旧缓存（关进程，清空文件，重新启动），例如：\n    - 更改了`cache-ignore`、域名列表（内容更改、优先级更改等）。\n    - ~~需要重新触发 add ip 操作（有缓存的情况下不会触发 add ip）~~。\n    - 2024.07.21 版本起，从 db 恢复的缓存被首次查询时将触发 add-ip。\n  - tool/dns_cache_mgr 可用于操纵 db 文件，进入 tool 目录，`./make.sh` 即可。\n    - `./dns_cache_mgr`：列出 db 中的所有缓存条目（域名、qtype、TTL、size 等）。\n    - `./dns_cache_mgr -r 域名后缀`：删除给定域名的缓存条目，-r 选项可以多次指定。\n    - 默认 db 文件路径是当前目录下的 `dns-cache.db`，可通过 `-f 文件路径` 选项修改。\n\n### verdict-cache\n\n- `verdict-cache` 启用 tag:none 域名的判决结果缓存，参数是缓存容量。\n  - tag:none 域名的查询会同时转发给 china、trust 上游，根据 china 上游的 ip test 结果，决定最终响应。\n  - 这里说的 **判决结果** 就是指这个 ip test 结果，即：给定的 tag:none 域名是 **大陆域名** 还是 **非大陆域名**。\n  - 如果记下此信息，则后续查询同一域名时（未命中 DNS 缓存时），只转发给特定上游组，不同时转发。\n  - 缓存容量上限是 65535，此缓存没有 TTL 限制；缓存满时会随机删除一个旧缓存数据。\n  - 建议启用此缓存，可帮助减少 tag:none 域名的重复请求和判定，还能减少 DNS 泄露。\n  - 注意，判决结果缓存与 DNS 缓存是互相独立的、互补的；这两个缓存系统可同时启用。\n- `verdict-cache-db` 启用缓存持久化，参数是 db 文件路径（可以不预先创建）。\n  - 进程启动时，自动从 db 恢复缓存；进程退出时，自动将缓存写回至 db。\n  - “进程退出”是指进程收到`SIGTERM/SIGINT`信号，即`kill \u003cPID\u003e`或`CTRL+C`。\n  - “缓存写回”可通过`SIGUSR1`信号强制触发（未启用持久化则写至`/tmp/chinadns@verdict-cache.db`）。\n  - db 是“纯文本”文件，可手动编辑和共享，第一个字段为“是否大陆域名(1是0否)”，第二个字段为“域名”。\n\n### hosts、dns-rr-ip\n\n- `hosts` 加载 hosts 文件，参数默认值为 `/etc/hosts`，此选项可多次指定。\n- `dns-rr-ip` 定义本地的 A/AAAA 记录（与 hosts 类似），此选项可多次指定。\n  - 格式：`\u003cnames\u003e=\u003cips\u003e`，多个 name 使用逗号隔开，多个 ip 使用逗号隔开。\n\n### cert-verify、ca-certs\n\n- `cert-verify` 验证 DoT 上游的 SSL 证书（有效性，是否受信任，域名是否匹配）。\n  - wolfssl 在某些平台（arm32、mips）可能无法正确验证 SSL 证书，见 [#169](https://github.com/zfl9/chinadns-ng/issues/169)。\n- `ca-certs` CA 根证书路径，用于验证 DoT 上游的 SSL 证书。默认自动检测。\n\n### no-ipset-blacklist\n\n- `no-ipset-blacklist` 若指定此选项，则 add-ip 时不进行内置的 IP 过滤。\n  - 默认情况下，以下 IP 不会被添加到 ipset/nftset 集合，见 [#162](https://github.com/zfl9/chinadns-ng/issues/162)\n  - `127.0.0.0/8`、`0.0.0.0/8`、`::1`、`::` (loopback地址、全0地址)\n\n### 其他杂项配置\n\n- `timeout-sec` 用于指定上游的响应超时时长，单位秒，默认 5 秒。\n- `repeat-times` 针对可信 DNS (UDP) [重复发包](#trust上游存在一定的丢包怎么缓解)，默认为 1，最大为 5。\n- `noip-as-chnip` 接受来自 china 上游的没有 IP 地址的响应，[详细说明](#--noip-as-chnip-选项的作用)。\n- `fair-mode` 从`2023.03.06`版本开始，只有公平模式，指不指定都一样。\n- `reuse-port` 用于多进程负载均衡（实践证明没必要，单进程已经够用）。\n- `verbose` 选项表示记录详细的运行日志，除非调试，否则不建议启用。\n\n## 域名列表\n\n**文件格式**\n\n域名列表是一个纯文本文件（不支持注释），每一行都是一个 **域名后缀**，如`baidu.com`、`www.google.com`。域名后缀不能以`.`开头或`.`结尾。出于性能考虑，最多允许 8 级域名（2025.03.27 版本之前是 4 级），超出部分将截断。\n\n---\n\n**加载顺序**\n\n所有组的域名列表都被 **加载** 到同一个数据结构，一个 **域名后缀** 一旦被加载，其内部属性就不会被修改。因此，当一个 **域名后缀** 存在于多个组的域名列表时，优先加载的那个组将“获胜”。举个例子：假设 `foo.com` 同时存在于 tag:chn、tag:gfw 组的域名列表内，且优先加载 tag:gfw 组，则 `foo.com` 属于 tag:gfw 组。\n\n先加载“自定义组”的域名列表，然后再加载“内置组”的域名列表（chn 和 gfw 谁先，取决于`--chnlist-first`）。\n\n---\n\n**匹配顺序**\n\n收到 dns query 时，会对 qname 进行 **最长后缀匹配**。举个例子，若 qname 为 `x.y.z`，则匹配顺序为：\n\n- `x.y.z`，检查数据结构中是否存在此域名后缀。\n- `y.z`，检查数据结构中是否存在此域名后缀。\n- `z`，检查数据结构中是否存在此域名后缀。\n\n一旦其中某个 **域名后缀** 匹配成功，匹配就结束，并获取该 **域名后缀** 所属的 tag(group)，并将 tag 信息记录到该 dns query 的相关数据结构，后续所有逻辑（分流、ipset/nftset）都基于这个 tag 信息，与 qname 无关。\n\n如果都匹配失败，则该 dns query 的 tag 被设为 `default-tag` 选项的值，默认情况下，`default-tag` 是 none。global 分流、gfwlist 分流都基于此机制实现。你可以将 `default-tag` 设为不同的 tag，来实现各种目的。\n\n---\n\n**性能、内存开销**\n\nchinadns-ng 在编码时特意考虑了性能和内存占用，并进行了深度优化，因此不必担心查询效率和内存开销。域名条目数量只会影响一点儿内存占用，对查询速度没影响，也不必担心内存占用，这是在`Linux x86-64`的实测数据：\n\n- 没有加载域名列表时，内存为 `140` KB\n- 加载 5700+ 条 `gfwlist.txt` 时，内存为 `304` KB\n- 加载 5700+ 条 `gfwlist.txt` 以及 73300+ 条 `chnlist.txt` 时，内存为 `2424` KB\n- 如果确实内存吃紧，可以使用更加精简的 chnlist 源（不建议只使用 gfwlist.txt 列表）\n- 2023.04.11 版本针对域名列表的内存占用做了进一步优化，因此占用会更少，测试数据就不贴了\n\n## 简单测试\n\n导入 chnroute/chnroute6 大陆 IP 数据库（用于 tag:none 域名的 IP 测试）：\n\n```bash\n# ipset (chnroute, chnroute6)\nipset -R \u003cres/chnroute.ipset\nipset -R \u003cres/chnroute6.ipset\n\n# nftset (inet@global@chnroute, inet@global@chnroute6)\nnft -f res/chnroute.nftset\nnft -f res/chnroute6.nftset\n\n# 只需导入一次，除非对应集合已从内核移除（比如重启了）\n```\n\n运行 chinadns-ng，我自己配了全局透明代理，所以访问 `8.8.8.8` 会走代理。\n\n```bash\n# ipset\nchinadns-ng -m res/chnlist.txt -g res/gfwlist.txt -v\n\n# nftset\nchinadns-ng -m res/chnlist.txt -g res/gfwlist.txt -v -4 inet@global@chnroute -6 inet@global@chnroute6\n```\n\nchinadns-ng 默认监听 `127.0.0.1:65353`，可以给 chinadns-ng 加上 -v 参数，使用 dig 测试，观察其日志。\n\n## 常见问题\n\n### tag:chn、tag:gfw、tag:none 是指什么\n\n这是 chinadns-ng 对域名的一个简单分类：\n\n- 被 chnlist.txt 匹配的域名归为 `tag:chn`\n- 被 gfwlist.txt 匹配的域名归为 `tag:gfw`\n- 被 `group-dnl` 匹配的域名归为 `自定义组`\n- 其它域名默认归为 `tag:none`，可通过 -d 修改\n\n**域名分流** 和 **ipset/nftset** 的核心流程，可以用这几句话来描述：\n\n- `tag:chn`：只走 china 上游（单纯转发），如果启用 --add-tagchn-ip，则添加解析结果至 ipset/nftset\n- `tag:gfw`：只走 trust 上游（单纯转发），如果启用 --add-taggfw-ip，则添加解析结果至 ipset/nftset\n- `自定义组`：只走 所属组的 上游（单纯转发），如果启用 --group-ipset，则添加解析结果至 ipset/nftset\n- `tag:none`：同时走 china 和 trust，如果 china 上游返回国内 IP，则接受其结果，否则采纳 trust 结果\n\n\u003e `tag:chn`和`tag:gfw`和`自定义组`不存在任何判定/过滤；`tag:none`的判定/过滤也仅限于 china 上游的响应结果\n\n---\n\n### 如何以守护进程形式在后台运行 chinadns-ng\n\n```bash\n# 纯 shell 语法：\n(chinadns-ng 参数... \u003c/dev/null \u0026\u003e\u003e/var/log/chinadns-ng.log \u0026)\n\n# 也可借助 systemd 的 service 来实现，此处不展开叙述\n```\n\n---\n\n### 如何更新 chnroute.ipset、chnroute6.ipset\n\n```bash\ncd res\n./update-chnroute.sh\n./update-chnroute6.sh\nipset -F chnroute\nipset -F chnroute6\nipset -R -exist \u003cchnroute.ipset\nipset -R -exist \u003cchnroute6.ipset\n# 支持运行时重载 ipset/nftset，无需操作 chinadns-ng 进程\n```\n\n---\n\n### 如何更新 chnroute.nftset、chnroute6.nftset\n\n```bash\ncd res\n./update-chnroute-nft.sh\n./update-chnroute6-nft.sh\nnft flush set inet global chnroute\nnft flush set inet global chnroute6\nnft -f chnroute.nftset\nnft -f chnroute6.nftset\n# 支持运行时重载 ipset/nftset，无需操作 chinadns-ng 进程\n```\n\n---\n\n### 如何更新 chnlist.txt、gfwlist.txt\n\n```bash\ncd res\n./update-chnlist.sh\n./update-gfwlist.sh\n# pkill chinadns-ng # 关闭旧的 chinadns-ng 进程\nchinadns-ng -m chnlist.txt -g gfwlist.txt 其他参数... # 重新运行 chinadns-ng\n```\n\n---\n\n### 如何使用 TCP 协议与 DNS 上游进行通信\n\n从 2024.03.07 版本开始，有以下更改：\n\n- 若上游地址为 `1.1.1.1`，则根据 **查询方的传入协议** 来选择与上游的通信协议：\n  - 若查询方的传入协议为 UDP，则 chinadns-ng 与该上游的通信协议为 UDP。\n  - 若查询方的传入协议为 TCP，则 chinadns-ng 与该上游的通信协议为 TCP。\n- 若上游地址为 `udp://1.1.1.1`，则 chinadns-ng 与该上游的通信方式为 UDP。\n- 若上游地址为 `tcp://1.1.1.1`，则 chinadns-ng 与该上游的通信方式为 TCP。\n- 若上游地址为 `tls://1.1.1.1`，则 chinadns-ng 与该上游的通信方式为 TLS(DoT)。\n\n---\n\n### 为什么不内置 ~~TCP~~、~~DoT~~、DoH 等协议的支持\n\n- 2024.03.07 版本起，已内置完整的 TCP 支持（传入、传出）。\n- 2024.04.27 版本起，支持 DoT 协议的上游，DoH 不打算实现。\n\n我想让代码保持简单，只做真正必要的事，其他事让专业工具去做。\n\n换句话说，让程序保持简单和愚蠢，只做一件事，并认真做好这件事。\n\n---\n\n### chinadns-ng 并不读取 chnroute.ipset、chnroute6.ipset\n\n启动时也不会检查这些 ipset/nftset 集合是否已存在，程序只在收到来自国内上游的 DNS 响应时（仅针对 tag:none 域名），通过 netlink 查询给定 ipset/nftset 集合，对应 IP 是否存在。这种机制使得我们可以在 chinadns-ng 运行时直接更新 chnroute、chnroute6 列表，它立即生效，不需要重启 chinadns-ng。\n\n\u003e 只有 tag:none 域名存在 ipset/nftset 判断\u0026\u0026过滤。\n\n---\n\n### 接受 china 上游返回的 IP为保留地址 的解析记录\n\n将对应的地址(段)加入到 `chnroute`、`chnroute6` 集合即可。chinadns-ng 判断是否为\"大陆 IP\"的核心依据就是查询 chnroute、chnroute6 集合，程序内部并没有其他隐含的判断规则。\n\n注意：**只有 tag:none 域名需要这么做**；对于 tag:chn 域名，chinadns-ng 只是转发，不涉及 ipset/nftset 判定；所以你也可以将相关域名加入到 chnlist.txt 列表（支持从多个文件加载域名列表）。\n\n为什么没有默认将保留地址加入 `chnroute*.ipset/nftset`？因为我担心 GFW/ISP 会给受污染域名返回保留地址，所以没放到 chnroute 去。不过现在受污染域名都走 gfwlist.txt 机制了，只会走 trust 上游，加进去应该是没问题的。\n\n---\n\n### received an error code from kernel: (-2) No such file or directory\n\n- 如果是 ip test 报错，说明未导入 chnroute、chnroute6，请导入相关 ipset/nftset 集合。\n- 如果是 ip add 报错，说明未提前创建给定的 ipset/nftset，请创建配置中给出的相关集合。\n\n---\n\n### trust上游存在一定的丢包，怎么缓解\n\n- 方法1：**重复发包**，也即 `--repeat-times N` 选项，这里的 `N` 默认为 1，可以改为 3，表示在给一个 trust 上游（UDP）转发查询消息时，同时发送 3 个相同的查询消息。\n- 方法2：**TCP查询**，对于新版本（\u003e= 2024.03.07），在 trust 上游的地址前加上 `tcp://`；对于老版本，可以加一层 [dns2tcp](https://github.com/zfl9/dns2tcp)，来将 chinadns-ng 发出的 UDP 查询转为 TCP 查询。\n\n推荐方法2，因为 QoS 等因素，TCP 流量的优先级通常比 UDP 高，且 TCP 本身就提供丢包重传等机制，比重复发包策略更可靠。另外，很多代理程序的 UDP 实现效率较低，很有可能出现 TCP 查询总体耗时低于 UDP 查询的情况。\n\n---\n\n### 为何选择 ipset/nftset 来处理 chnroute 查询\n\n因为使用 ipset/nftset 可以与 iptables/nftables 规则共用一份 chnroute；达到联动的效果。\n\n---\n\n### 是否打算支持 geoip.dat 等格式的 chnroute\n\n目前没有这个计划，因为 chinadns-ng 通常与 iptables/nftables 一起使用（配合透明代理），若使用非 ipset/nftset 实现，会导致两份重复的 chnroute，且无法与 iptables/nftables 规则实现联动。\n\n---\n\n### 是否打算支持 geosite.dat 等格式的 gfwlist/chnlist\n\n目前没有这个计划，这些二进制格式需要引入 protobuf 等库，我不想引入依赖，而且 geosite.dat 本身也很大。\n\n---\n\n### chinadns-ng 也可用于 gfwlist 透明代理分流\n\n```bash\n# 创建 ipset，用于存储 tag:gfw 域名的 IP (nftset 同理)\nipset create gfwlist hash:net family inet # ipv4\nipset create gfwlist6 hash:net family inet6 # ipv6\n\n# 指定 gfwlist.txt，default-tag，add-taggfw-ip 选项\nchinadns-ng -g gfwlist.txt -d chn -A gfwlist,gfwlist6\n```\n\n传统上，这是通过 dnsmasq 来实现的，但 dnsmasq 的 server/ipset/nftset 功能不擅长处理大量域名，影响性能，只是 gfwlist.txt 域名数量比 chnlist.txt 少，所以影响较小。如果你在意性能，如低端路由器，可使用 chinadns-ng 来实现。\n\n---\n\n### 使用 chinadns-ng 替代 dnsmasq 的注意事项\n\nchinadns-ng 2.0 已经足以替代经典用例下的 dnsmasq：\n\n- 域名分流效率比 dnsmasq 高得多，即使存在大量域名规则，也不影响性能，内存占用也很低\n- nftset 操作效率比 dnsmasq 高，即使在短时间内写入大量 IP 也没问题（dnsmasq 可能会报错）\n- dnsmasq 的 TCP DNS 实现方式非常低效，每个 TCP 连接都可能在后台 fork 一个 dnsmasq 进程\n- chinadns-ng 可强制使用 TCP 上游，且原生支持 DoT，wolfssl 的体积/性能/内存开销都非常优秀\n\n对于路由器等场景，你可能仍然需要 dnsmasq 的 DHCP 等功能，这种情况下，建议关闭 dnsmasq 的 DNS：\n\n- 修改 dnsmasq 配置，将`port`改为0，关闭 dnsmasq 的 DNS 功能，其他功能不受影响（如 DHCP）\n- 此时请务必配置 `dhcp-option=option:dns-server,0.0.0.0`，确保会下发 dns-server 给 DHCP 客户端\n- 因为关闭 DNS 功能后，在未显式配置相关 dhcp-option 的情况下，dnsmasq 不会自动下发 dns-server\n- 0.0.0.0 是一个特殊 IP，dnsmasq 在内部会替换为“dnsmasq 所在主机的 IP”，避免写死 IP 地址，更灵活\n\n---\n\n### --noip-as-chnip 选项的作用\n\n\u003e 此选项只作用于 `tag:none 域名` \u0026\u0026 `qtype=A/AAAA` \u0026\u0026 `china 上游`，trust 上游不存在过滤。\n\nchinadns-ng 对 tag:none 域名的 A/AAAA 查询有特殊处理逻辑：对 china 上游返回的 reply 进行 ip test (chnroute)，如果测试结果是 china IP，则采纳 china 上游的结果，否则采纳 trust 上游的结果（为了减少重复判定，可启用 verdict-cache 来缓存该测试结果）。\n\n要进行 ip test，显然要求 reply 中有 IP 地址；如果没有 IP（如 NODATA 响应），就没办法 test 了。\n\n- 默认情况下，chinadns-ng 将 no-ip 视为 **非 china IP**，也即：采纳 trust 上游结果。\n- 若指定了 `--noip-as-chnip`，则将 no-ip 视为 **china IP**，也即：采纳 china 上游结果。\n\n默认拒绝 china 上游的 no-ip 结果是为了减少 GFW/ISP 污染，防止其故意对某些域名返回空 answer (no-ip)。\n\n---\n\n### 如何以普通用户身份运行 chinadns-ng\n\n向内核查询 ipset/nftset 需要 `CAP_NET_ADMIN` 权限，使用非 root 用户身份运行 chinadns-ng 时将产生 `Operation not permitted` 错误。解决方法有很多，这里介绍其中一种：\n\n```shell\n# 授予 CAP_NET_ADMIN 权限\n# 用于执行 ipset/nftset 操作\nsudo setcap cap_net_admin+ep /usr/local/bin/chinadns-ng\n\n# 授予 CAP_NET_ADMIN + CAP_NET_BIND_SERVICE 权限\n# 用于执行 ipset/nftset 操作、监听小于 1024 的端口\nsudo setcap cap_net_bind_service,cap_net_admin+ep /usr/local/bin/chinadns-ng\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzfl9%2Fchinadns-ng","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzfl9%2Fchinadns-ng","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzfl9%2Fchinadns-ng/lists"}