# -*- coding: utf-8 -*- r'''在设备上测 Cloudflare 隧道边缘 IP 的 TCP 握手延迟,挑优选 IP。 为什么要这么干: cloudflared 连 region1.v2.argotunnel.com,这个域名由 GeoDNS 决定 —— 国内 4G 常被解析到美西(实测 lax13),单程 200ms+, 往返加丢包重传能到 4 秒(用户实测 4141ms)。 Cloudflare 是 anycast,同一批 IP 从不同网络过去落点不同。 做法:测各候选 IP 的 7844(隧道端口)握手延迟, 把最快的写进 /etc/hosts 覆盖 GeoDNS。 ⚠ 关键教训: 7844 **只对 Cloudflare 的隧道专用段开放**,不是所有 anycast IP 都行。 专用段是 198.41.192.0/24 和 198.41.200.0/24(实测 198.41.192.107 通)。 我第一次拿 104.16.x.x 测,56 个全不通,就是选错段了。 ''' from __future__ import annotations import socket import sys import time PORT = 7844 # cloudflared 的 HTTP/2 隧道端口 # 已知能用的几个放前面:先确认能通,再放大范围扫 KNOWN = [ "198.41.192.107", "198.41.192.7", "198.41.192.167", "198.41.200.13", "198.41.200.33", "198.41.200.53", "198.41.200.63", ] def build_candidates() -> list[str]: ips = list(KNOWN) for third in range(1, 255, 3): ips.append("198.41.192." + str(third)) for third in range(1, 255, 3): ips.append("198.41.200." + str(third)) seen = set() out = [] for ip in ips: if ip not in seen: seen.add(ip) out.append(ip) return out def probe(ip: str, timeout: float = 2.5): """返回 TCP 握手耗时(毫秒),不通返回 None。""" sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(timeout) start = time.perf_counter() try: sock.connect((ip, PORT)) return (time.perf_counter() - start) * 1000 except OSError: return None finally: sock.close() def main() -> int: candidates = build_candidates() print(" 候选 %d 个(Cloudflare 隧道专用段)" % len(candidates)) print(" 测 %d 端口,每个 2 次取最快" % PORT) print() results = [] reachable = 0 for index, ip in enumerate(candidates, 1): best = None for _ in range(2): ms = probe(ip) if ms is not None and (best is None or ms < best): best = ms if best is not None: reachable += 1 results.append((best, ip)) if index % 40 == 0: print(" 已测 %d/%d,通了 %d 个" % (index, len(candidates), reachable)) print() if not results: print(" 一个都不通") return 1 results.sort() print(" 通了 %d 个,最快的 15 个:" % reachable) for ms, ip in results[:15]: print(" %7.1f ms %s" % (ms, ip)) print() print(" 默认 GeoDNS 解析:") for host in ("region1.v2.argotunnel.com", "region2.v2.argotunnel.com"): try: infos = socket.getaddrinfo(host, PORT) v4 = sorted({i[4][0] for i in infos if i[0] == socket.AF_INET}) print(" %s: %s" % (host, v4 or "(只有 IPv6)")) except OSError as exc: print(" %s: 解析失败 %s" % (host, exc)) with open("/root/cf_best.txt", "w") as fh: for ms, ip in results[:5]: fh.write("%s %.0f\n" % (ip, ms)) print() print(" 最快 5 个 -> /root/cf_best.txt") return 0 if __name__ == "__main__": sys.exit(main())