按运营商线路优选 Cloudflare 节点 + 智能 DNS 更新

cf-ddns

移动、联通、电信用户访问同一个域名,各自解析到自己线路最快的节点。常见做法是「定期测速换 IP」,但一条 DNS 记录只能有一个值;cf-ddns 为每条运营商线路分别优选节点,再借助 DNSPod 的线路解析能力写入同一域名的不同线路。

Go 1.26+ MIT License Linux · Windows · macOS 零依赖 单文件二进制
bash — cf-ddns / 快速上手
$ go build -o cf-ddns .
$ ./cf-ddns -config config.json -dry-run
# 仅测速并输出优选结果,不写 DNS
$ ./cf-ddns -config config.json
# 常驻运行,每 interval 循环一轮
01

简介与原理

Introduction & Architecture

cf-ddns 是「按运营商线路优选 Cloudflare 节点 + 智能 DNS 更新」工具:纯 Go 标准库实现,零依赖,编译后是单文件二进制,开箱即用。

背景:Cloudflare anycast IP 在国内不同运营商下的质量差异很大——对移动最快的 IP,对电信可能很慢。常见做法是定期测速、更换 IP,但一条 DNS 记录只能有一个值,无法同时满足三家运营商。

做法:cf-ddns 为每条运营商线路(默认 / 移动 / 联通 / 电信)分别优选节点,再借助 DNSPod 的线路解析能力,把同一个域名写成不同线路的解析记录。移动 / 联通 / 电信用户访问同一个域名,各自解析到自己线路最快的节点。

cf.example.com 同一个域名 · 一条记录名 DNSPod 同一域名 · 按运营商解析 移动用户 联通用户 电信用户 其他用户 移动线路记录 优选 IP 联通线路记录 优选 IP 电信线路记录 优选 IP 默认线路记录 优选 IP
DNSPod 为同一域名维护四条线路记录;各运营商用户解析到各自的优选 IP。写哪几条、记录类型是 A / AAAA / CNAME,由候选列表内容决定(见第 02 节)。
前提

Cloudflare DNS 没有运营商线路维度,因此承载优选记录的域名需要托管到 DNSPod(免费套餐即可)。具体迁移 / 委派方式见「快速开始」第 1 步。

02

核心工作流

Pipeline · 每轮 6 步

每轮循环对所有线路执行以下 6 步;所有线路处理完毕后休眠 interval(默认 10 分钟)再开始下一轮。单条线路失败不影响其它线路。

1 拉取候选 从 BestCF 获取该运营商的优选列表 IP#备注 · 每行一个 · 可带端口 · IPv6 用方括号 2 类型识别 IPv4 → A 记录 · IPv6 → AAAA 记录(需 ipv6) 域名 → CNAME 记录 去重,忽略无法识别的条目 3 并发测速 task(默认 50 协程)并发:TCP:80 建连 + HTTP GET 确认存活,并从 CF-RAY 响应头提取数据中心(如 HKG) 同一轮内多条线路共享测速缓存 · 重复地址只测一次 建连超时 1s · 探测上限 2s 4 分槽位选优 主槽位:IPv4 / 域名候选竞争一条 A 或 CNAME 记录(互斥) v6 槽位:IPv6 候选竞争一条 AAAA 记录 可先按 colo 数据中心过滤候选 5 收敛记录 与线上现有记录比对:无则创建 · 一致则跳过(幂等)· 不同则更新 CNAME 与 AAAA 互斥:主记录为 CNAME 时跳过 AAAA 维护 线路上残留冲突 AAAA 记录则提示手动清理,不做破坏性删除 6 循环 所有线路处理完毕后休眠 interval(默认 10m) 重复上述流程 · 单条线路失败不影响其它线路 interval 10m 后循环
第 5 步是整条流水线的幂等保证:无论重复运行多少轮,线上记录始终收敛到当前最优结果,且不会破坏性地删除任何记录。
说明

优选「主槽位」在 A 记录与 CNAME 记录之间互斥;当主记录为 CNAME 时,目标域名自带双栈解析,因此跳过 AAAA 记录的维护。

03

选优策略

pick 策略对比

每种策略下,本机测速都只是「存活过滤」;区别在于从存活的候选中按什么顺序取第一名。

策略选择方式适用场景
order默认 本机测速仅用于存活过滤,在存活候选中取 BestCF 列表排名最靠前的 跨运营商线路:程序跑在电信机器上时,实测的移动线路延迟没有参考价值,应信任 BestCF 的全国多点测速排名
latency 取本机实测建连延迟最低的候选 程序部署在与该线路相同运营商的网络中,本机测速结果可信
注意

机器所在运营商与线路不匹配时,latency 的实测结果会失真(例如在电信网络里测出的「最优」移动 IP)。默认的 order 正是为此设计。

04

配置参考

字段表 + JSON 示例

配置文件为 JSON,必须使用 UTF-8 编码(线路名是中文)。下面按「通用字段 / lines 元素 / 服务商专属」三组说明。

通用字段

字段类型默认说明
providerstringcloudflarednspod:分线路模式;cloudflare:单记录兼容模式
recordstring完整记录名,如 cf.example.com
ttlintdnspod 600 / cloudflare 60DNS TTL
intervalstring"10m"Go duration 格式,一轮循环的休眠时长
taskint50并发测速协程数
colostring全局数据中心过滤,逗号分隔(如 HKG,NRT
ipv6boolfalse是否优选 IPv6 并维护 AAAA 记录;需本机具备 IPv6 连通
dry_runboolfalse仅测速并输出结果,不写 DNS

lines 元素

字段类型默认说明
linestring必填线路名:默认 / 移动 / 联通 / 电信,须与 DNSPod 的线路名一致
ips_urlstring必填候选列表地址(BestCF 下载)
pickstringorderorder(信任 BestCF 排名)或 latency(本机实测最低)
colostring继承全局线路级数据中心过滤,缺省继承全局 colo

服务商专属

provider字段类型说明
dnspodsecret_idstring腾讯云 API SecretId
secret_keystring腾讯云 API SecretKey
domainstring域名,如 example.com
cloudflarecf_tokenstringAPI Token,需 Zone→DNS→Edit 权限
zone_idstringZone ID,在域名 Overview 页右下角
proxiedbool是否开启代理(小黄云);直连优选场景应为 false

JSON 示例(dnspod 分线路模式)

jsonconfig.json
{
  "provider": "dnspod",
  "record": "cf.example.com",
  "ttl": 600,
  "interval": "10m",
  "task": 50,
  "colo": "HKG,NRT",
  "ipv6": true,
  "dry_run": false,
  "secret_id": "AKID…",
  "secret_key": "…",
  "domain": "example.com",
  "lines": [
    { "line": "默认", "ips_url": "https://github.com/DustinWin/BestCF/releases/download/bestcf/bestcf-ip.txt", "pick": "order" },
    { "line": "移动", "ips_url": "https://github.com/DustinWin/BestCF/releases/download/bestcf/cmcc-ip.txt", "pick": "order", "colo": "HKG,NRT" },
    { "line": "联通", "ips_url": "https://github.com/DustinWin/BestCF/releases/download/bestcf/cucc-ip.txt", "pick": "latency" },
    { "line": "电信", "ips_url": "https://github.com/DustinWin/BestCF/releases/download/bestcf/ctcc-ip.txt", "pick": "order" }
  ]
}

候选列表地址表

线路文件名完整地址
默认 / 通用bestcf-ip.txthttps://github.com/DustinWin/BestCF/releases/download/bestcf/bestcf-ip.txt
移动cmcc-ip.txthttps://github.com/DustinWin/BestCF/releases/download/bestcf/cmcc-ip.txt
联通cucc-ip.txthttps://github.com/DustinWin/BestCF/releases/download/bestcf/cucc-ip.txt
电信ctcc-ip.txthttps://github.com/DustinWin/BestCF/releases/download/bestcf/ctcc-ip.txt
优选域名(CNAME)bestcf-domain.txthttps://github.com/DustinWin/BestCF/releases/download/bestcf/bestcf-domain.txt
05

快速开始

Quick Start · 5 步

  1. 1

    准备域名:托管到 DNSPod

    Cloudflare DNS 没有运营商线路维度,承载优选记录的域名需托管到 DNSPod(免费套餐即可)。两种方案任选其一:

    • 方案 A · 整域托管:把域名的 NS 记录改为 DNSPod 提供的 f1g1ns1.dnspod.net,整域迁入 DNSPod。
    • 方案 B · 子域名委派:域名保留在 Cloudflare,为承载优选记录的子域名(如 cf.example.com)添加 NS 记录,指向 DNSPod。

    验证:nslookup -type=NS 你的域名,确认返回 DNSPod 的 NS 记录。

  2. 2

    获取腾讯云密钥

    腾讯云控制台 → 访问管理 → API 密钥管理,创建密钥。建议使用子账号并仅授权 QcloudDNSPodFullAccess,避免主账号密钥泄露造成过大影响。

  3. 3

    写配置

    复制示例配置并按需修改。配置文件必须为 UTF-8 编码——线路名是中文,切勿用 GBK / ANSI 保存。

    bash初始化配置
    cp config.example.json config.json
    # 编辑 config.json,填入 record / secret_id / secret_key / domain / lines
  4. 4

    构建并试运行

    先编译,再用 -dry-run 观察输出;确认无误后正式运行。

    bash构建与试运行
    go build -o cf-ddns .
    ./cf-ddns -config config.json -dry-run   # 仅测速,不写 DNS
    ./cf-ddns -config config.json            # 正式运行
  5. 5

    命令行参数

    参数说明
    -config配置文件路径
    -cf-tokenCloudflare API Token
    -zone-idCloudflare Zone ID
    -record完整记录名
    -proxied是否开启代理(小黄云)
    -ttlTTL
    -interval循环间隔(Go duration,如 "10m"
    -ips-url候选列表地址
    -colo数据中心过滤(逗号分隔)
    -task并发测速协程数
    -ipv6启用 IPv6 优选
    -dry-run仅测速,不写 DNS

    其中 -dry-run-ipv6 在显式传入时会覆盖配置文件中的对应值。

06

部署(systemd)

Linux 常驻部署

  1. 1

    交叉编译

    在本机交叉编译出 Linux amd64 二进制(也可直接换成目标机器的架构)。

    bash交叉编译
    GOOS=linux GOARCH=amd64 go build -o cf-ddns .
  2. 2

    传输与安装

    配置里包含密钥,务必通过 scp / sftp 等加密通道传输。二进制放 /usr/local/bin,配置放 /etc/cf-ddns/config.json 并设为 600。

    bash传输与安装
    scp cf-ddns config.json user@server:/tmp/
    sudo install -m 0755 /tmp/cf-ddns /usr/local/bin/cf-ddns
    sudo mkdir -p /etc/cf-ddns
    sudo install -m 600 /tmp/config.json /etc/cf-ddns/config.json
  3. 3

    创建 systemd unit

    ini/etc/systemd/system/cf-ddns.service
    [Unit]
    Description=cf-ddns Cloudflare 优选 IP 动态 DNS
    After=network-online.target
    Wants=network-online.target
    
    [Service]
    Type=simple
    ExecStart=/usr/local/bin/cf-ddns -config /etc/cf-ddns/config.json
    Restart=always
    RestartSec=10
    ProtectSystem=strict
    ProtectHome=read-only
    NoNewPrivileges=true
    PrivateTmp=true
    
    [Install]
    WantedBy=multi-user.target
  4. 4

    注册并启动

    bash启用与查看日志
    sudo cp cf-ddns.service /etc/systemd/system/
    sudo systemctl daemon-reload
    sudo systemctl enable --now cf-ddns
    journalctl -u cf-ddns -f
注意

配置文件含密钥:scp 请走加密通道;落地后权限保持 600,勿用明文传输或随意改权限。

07

验证方法

Verification

  1. 1

    在线多点测试

    itdog.cn/dns 输入域名做多点 DNS 解析,三网(移动 / 联通 / 电信)的差异一目了然。

  2. 2

    本机解析

    在服务器或本机执行 nslookup cf.example.com,应返回当前宽带运营商线路的解析结果。

注意

不要指定其它运营商的公共 DNS 来「验证」——由于 EDNS 与出口 IP 拦截问题,指定其它运营商公共 DNS 的结果不可靠,不能代表目标线路的真实解析。

08

常见问题

FAQ

Q1提示「无存活 IPv6 候选」,怎么办?

原因是本机没有 IPv6 出口。这不影响 IPv4 线路,工具会自动跳过 AAAA 记录的维护。可用 curl -6 https://ip.sb 验证本机是否具备 IPv6 连通性。

Q2提示 TTL 低于下限,怎么处理?

DNSPod 免费套餐的最小 TTL 为 600 秒,把 ttl 调到 600 及以上即可。

Q3写入失败提示需要「默认」线路,为什么?

DNSPod 要求记录名下先存在「默认」线路的记录。工具已自动把「默认」线路排到处理最前,正常流程下无需手动干预。

Q4从配置里删掉了某条线路,线上记录还在?

工具只收敛自身维护的记录,不会自动删除已存在的线上记录。删除线路后需到 DNSPod 控制台手动清理残留的线路记录。

Q5cloudflare 模式提示不支持线路,怎么办?

Cloudflare DNS 不支持按运营商线路解析,无法承载多线路记录。请改用 dnspod 模式,并把域名托管到 DNSPod(见第 05 节)。

09

兼容模式

Cloudflare 单记录模式

provider=cloudflare 是「单记录模式」:不需要分线路时的旧版行为,旧配置无需任何修改即可继续使用。要点:

  • 仅允许 默认 线路,不维护运营商多线路。
  • TTL 默认 60
  • Token 需 Zone→DNS→Edit 权限;Zone ID 在域名 Overview 页右下角。
  • 直连优选场景下 proxied 应为 false
jsonconfig.json · cloudflare
{
  "provider": "cloudflare",
  "record": "cf.example.com",
  "ttl": 60,
  "cf_token": "…",
  "zone_id": "…",
  "proxied": false
}