首页
/ curl ECH 支持实战指南:基于 DoH 与 --ech 选项实现 Encrypted Client Hello

curl ECH 支持实战指南:基于 DoH 与 --ech 选项实现 Encrypted Client Hello

2026-09-05 14:59:36作者:何将鹤

本文以 curl 仓库中的 docs/ECH.md 为主体,完整讲解如何在 curl 中构建并启用 ECH(Encrypted Client Hello)支持:涵盖 OpenSSL/BoringSSL/wolfSSL 三种 TLS 后端的编译方法、--ech 命令行选项的六种取值、ECHConfigList 的获取与注入方式、默认配置策略,以及结合 lib/vtls/openssl.clib/vdns/doh.clib/setopt.c 等源码的底层实现链路。读完本文,你可以复现 curl 的 ECH 实验性构建,理解其 “DoH 拉取 HTTPS RR → DNS 缓存 → TLS 握手喂入 OpenSSL” 的完整数据通路。

需要特别强调文档中的原始声明:此功能为 EXPERIMENTAL(实验性),切勿用于生产环境。它的定位是一个“足以引发 ECH 在 curl 中长期方案讨论的概念验证(proof-of-concept)”,可配合 AWS-LC、BoringSSL、OpenSSL、Rustls 或 wolfSSL 作为 TLS 提供方工作。

ECH 在 curl 中的两条配置路径

ECH 要加密 TLS ClientHello,前提是先拿到服务器的 ECHConfigList。curl 目前提供两条获取路径:

  1. 通过 DoH 自动获取:curl 本就支持用 DoH 做 A/AAAA 解析,因此顺带扩展了在同一 DoH 请求中拉取 HTTPS RR(SVCB/HTTPS 类型记录)中 ech= 标签的能力。只要配置了 DoH URL,--ech true 即可工作;
  2. 命令行直接提供:用 --ech ecl:<b64value> 把 base64 编码的 ECHConfigList 直接贴在命令行上,绕开 DNS。这条路径的价值在于:它可用于在 ECHConfigList 尚未发布到 DNS 之前的测试与部署验证阶段。

两条路径共同依赖一个约束:ECH 只在 TLS 1.3 上工作,且要求构建时开启 ECH 开关(USE_ECH)。

构建:autotools 与 CMake 两条路线

OpenSSL 后端 + autotools

文档给出的标准流程是:先构建 ECH-enabled 的 OpenSSL(4.0.0+),再基于它构建 curl:

# 1. 构建 ECH-enabled OpenSSL(浅克隆 openssl 源码后)
cd $HOME/code
git clone --depth 1 --branch openssl-4.0.0 <openssl 源码仓库>
cd openssl
./config --libdir=lib --prefix=$HOME/code/openssl-local-inst
make -j8
make install_sw
# 2. 构建启用 ECH 的 curl
cd $HOME/code
git clone --depth 1 <curl 源码仓库>
cd curl
autoreconf -fi
LDFLAGS="-Wl,-rpath,$HOME/code/openssl-local-inst/lib/" ./configure --with-ssl=$HOME/code/openssl-local-inst --enable-ech
make

验证要点:configure 输出结尾必须出现如下警告,否则说明 ECH 没有真正启用,需要回查步骤:

WARNING: ECH is enabled but marked EXPERIMENTAL...

如果需要调试 curl 本体,可在 configure 中追加 --enable-debug

文档还记录了一个真实的构建坑:某次 2024-05-20 的构建中,configure 找不到 ECH-enabled 的 SSL 库,排查发现与 $HOME/code/openssl-local-inst/lib/pkgconfig 目录的存在有关,删除该目录后问题消失——这提示在自定义 --prefix 安装 OpenSSL 时,pkgconfig 残留可能干扰 curl 的能力探测。

对应到仓库源码,autotools 路径的开关逻辑在 configure.ac--enable-ech 打开后,用 AC_CHECK_FUNCS(SSL_set1_ech_config_list, ...) 探测 TLS 库是否具备 ECH API,探测成功才定义 USE_ECH,否则报错 --enable-ech ignored: No ECH support found

CMake 构建

CMake 路线更简洁,同样假设 ECH-enabled OpenSSL 已就绪:

cd $HOME/code
git clone --depth 1 <curl 源码仓库>
cd curl
mkdir build
cd build
cmake -DOPENSSL_ROOT_DIR=$HOME/code/openssl -DUSE_ECH=1 ..
make
# [100%] Built target curl

一个实用细节:CMake 构建出的二进制不需要任何 ECH 相关的 LD_LIBRARY_PATH 设置(autotools 路线则往往需要,见下文“默认设置”一节的陷阱)。

CMake 侧的实现在 CMakeLists.txtUSE_ECH 选项默认 OFF,开启后依次验证 TLS 后端(OpenSSL/wolfSSL/Rustls)、用 curl_openssl_check_exists("SSL_set1_ech_config_list" ...) 确认 API 存在,随后强制连带打开 USE_HTTPSRR,并打印 ECH enabled / HTTPSRR enabled 状态。

BoringSSL 后端

BoringSSL 同样支持 ECH,构建方式(省略外部仓库地址):

cd $HOME/code
git clone --depth 1 <boringssl 源码仓库>
cd boringssl
cmake -DCMAKE_INSTALL_PREFIX:PATH=$HOME/code/boringssl/inst -DBUILD_SHARED_LIBS=1
make
make install

然后构建 curl:

cd $HOME/code
git clone --depth 1 <curl 源码仓库>
cd curl
autoreconf -fi
LDFLAGS="-Wl,-rpath,$HOME/code/boringssl/inst/lib" ./configure --with-ssl=$HOME/code/boringssl/inst --enable-ech
make

成功时同样应看到实验性警告(BoringSSL 下措辞略不同):

WARNING: ECH HTTPSRR enabled but marked EXPERIMENTAL. Use with caution.

由于 AWS-LC/BoringSSL 的 ECH API 与 ECH-enabled OpenSSL fork 非常接近,代码改动同样落在 lib/vtls/openssl.c 中,通过 #ifdef OPENSSL_IS_BORINGSSL 保护的分支实现,大多是 API 的等价替换。注意限制:AWS-LC/BoringSSL 目前不支持 --ech pn: 命令行变体

wolfSSL 后端

cd $HOME/code
git clone --depth 1 <wolfssl 源码仓库>
cd wolfssl
./autogen.sh
./configure --prefix=$HOME/code/wolfssl/inst --enable-ech --enable-debug --enable-opensslextra
make
make install

其中安装前缀(inst)是必须的,否则 curl 的 configure 探测不顺利;而 --enable-opensslextra 经过反复尝试后确认是必需的,缺失会导致 curl 构建出问题。

接着构建 curl:

cd $HOME/code
git clone --depth 1 <curl 源码仓库>
cd curl
autoreconf -fi
./configure --with-wolfssl=$HOME/code/wolfssl/inst --enable-ech
make

wolfSSL 后端的 ECH 实现有已知问题(原文档列出的上游问题,此处不复述外部链接):

  • HelloRetryRequest(HRR)处理不正确:导致客户端无法访问强制 HRR 的 ECH 测试站点(如 tls-ech.dev);
  • 中间盒兼容模式(middlebox compatibility mode)相关缺陷同样存在。

wolfSSL 与 OpenSSL 路线还有一些“怪异的差异”,来自实际踩坑记录:

  • .curlrc 中的 DoH URL 在 OpenSSL 下可以直接写 1.1.1.1,wolfSSL 下则必须写 one.one.one.one(后者对两者都可用,所以统一用它);
  • CA 数据库差异:wolfSSL 版本不喜欢 defo.ie 的证书,可用 --insecure/-k 绕过(真实部署则必须解决)。

wolfSSL 侧的源码改动包括:configure.ac 中新增 wolfSSL 是否具备 ECH 的探测;在 lib/vtls/wolfssl.c 中新增与 OpenSSL 版本镜像的 ECH 代码。功能差异上,wolfSSL 不支持 --ech false,也不支持 --ech pn:。不支持 --ech false 的根因在于 wolfSSL 的设计决策:只要以 ECH 方式构建,就总是至少发送 GREASE 扩展——即对 wolfSSL 来说 GREASE 是编译期选择,而对 OpenSSL / AWS-LC / BoringSSL 来说是运行期选择(两种设计都合理)。

--ech <config> 选项全解

--ech 选项自 curl 8.8.0 引入,属于 TLS 分类,仅对 HTTPS 协议生效(见 docs/cmdline-opts/ech.md)。完整取值如下:

取值 行为 失败语义
false 不尝试 ECH(默认值)
grease 发送 GREASE 占位 ECH 扩展 永不失败
true 尽量尝试 ECH 尝试未果不失败;但一旦真正发起 ECH 而解密失败则连接失败
hard 必须成功发起 ECH 无法尝试(如 DNS 中找不到 ECHConfig)即硬失败
ecl:<b64value> 使用给定的 base64 ECHConfigList 而非来自 DNS 配合 ECH 尝试逻辑
pn:<name> 覆盖 ECHConfigList 中的 public_name 字段(仅 OpenSSL 后端可用)

一个容易混淆的点(原文档专门强调):所谓“attempt ECH”指客户端发出带“真实” ECH 扩展的 TLS ClientHello,并不意味着服务器一定能成功解密——解密还可能因其它原因失败。

源码视角:选项如何落到内部状态

选项解析实现在 lib/setopt.csetopt_ech()(受 #ifdef USE_ECH 保护,未启用 ECH 构建时整个函数退化为 CURLE_NOT_BUILT_IN):

static CURLcode setopt_ech(struct Curl_easy *data, const char *ptr)
{
  ...
  if(!ptr || !strcmp(ptr, "false"))
    s->tls_ech = CURLECH_DISABLE;
  else {
    ...
    if(!strcmp(ptr, "grease"))
      s->tls_ech = CURLECH_GREASE;
    else if(!strcmp(ptr, "true"))
      s->tls_ech = CURLECH_ENABLE;
    else if(!strcmp(ptr, "hard"))
      s->tls_ech = CURLECH_HARD;
    else if(plen > 4 && !strncmp(ptr, "ecl:", 4)) {
      if(!s->tls_ech)
        s->tls_ech = CURLECH_HARD;   /* 给出 ecl: 但未显式指定模式时,默认 hard */
      result = Curl_setstropt(data, STRING_ECH_CONFIG, ptr + 4);
    }
    else if(plen > 3 && !strncmp(ptr, "pn:", 3)) {
      if(!s->tls_ech)
        s->tls_ech = CURLECH_HARD;
      result = Curl_setstropt(data, STRING_ECH_PUBLIC, ptr + 3);
    }
    else
      result = CURLE_BAD_FUNCTION_ARGUMENT;
  }
  ...
}

从源码结构看有三个值得注意的细节:

  • 字符串被映射为 CURLECH_DISABLE / CURLECH_GREASE / CURLECH_ENABLE / CURLECH_HARD 四态枚举,存储在 data->set.tls_ech
  • 单独使用 ecl:pn: 而未显式指定 true/hard 时,内部默认按 CURLECH_HARD 处理,即“给了配置列表就应当严格尝试”;
  • ecl: 后的 base64 串存入 STRING_ECH_CONFIGpn: 后的名字存入 STRING_ECH_PUBLIC,二者在 TLS 握手阶段才被消费。

错误语义方面,绝大多数 ECH 相关错误会映射为 CURLE_ECH_REQUIRED(错误码 101),这在后文的错误示例中可以看到。

ECH + DoH:完整工作流

curl 支持用 DoH 做 A/AAAA 查找,因此在其上拉取 HTTPS RR 相当自然。典型用法:

cd $HOME/code/curl
LD_LIBRARY_PATH=$HOME/code/openssl ./src/curl --ech true \
  --doh-url https://one.one.one.one/dns-query \
  https://defo.ie/ech-check.php

一切正常时,响应 HTML 中会包含(这是 defo.ie 测试页回显的握手状态):

SSL_ECH_STATUS: success <img src="greentick-small.png" alt="good" /> <br/>

原文档验证过以下测试站点(覆盖 4 种不同服务器技术、3 方实现,其中含一个强制 HRR 的 8414 端口场景):

https://defo.ie/ech-check.php
https://crypto.cloudflare.com/cdn-cgi/trace
https://tls-ech.dev/

非 443 端口的场景也受支持:按 SVCB/HTTPS 规范,目标端口不是 443 时,DoH 查询的 qname 会被相应改写,保证取到对应端口的 HTTPS RR。

命令行直接提供 ECHConfigList

不依赖 DoH 时,可以 dig 拿到 HTTPS RR 中的 ech= 值并粘贴回命令行:

dig +short https defo.ie
1 . ipv4hint=213.108.108.101 ech=AED+DQA8PAAgACD8WhlS7VwEt5bf3lekhHvXrQBGDrZh03n/LsNtAodbUAAEAAEAAQANY292ZXIuZGVmby5pZQAA
  ipv6hint=2a00:c6c0:0:116:5::10

然后:

LD_LIBRARY_PATH=$HOME/code/openssl ./src/curl --ech ecl:AED+DQA8PAAgACD8WhlS7VwEt5bf3lekhHvXrQBGDrZh03n/LsNtAodbUAAEAAEAAQANY292ZXIuZGVmby5pZQAA \
  https://defo.ie/ech-check.php
# ...
# SSL_ECH_STATUS: success <img src="greentick-small.png" alt="good" /> <br/>

配置列表过期与 retry_configs

注意 ECHConfigList 可能频繁轮换(defo.ie 的每小时变化)。粘贴了过期值时,-vvv 下会看到硬失败错误:

LD_LIBRARY_PATH=$HOME/code/openssl ./src/curl -vvv --ech ecl:AED+DQA8yAAgACDRMQo+qYNsNRNj+vfuQfFIkrrUFmM4vogucxKj/4nzYgAEAAEAAQANY292ZXIuZGVmby5pZQAA \
  https://defo.ie/ech-check.php
...
* OpenSSL/3.3.0: error:0A00054B:SSL routines::ech required
...

好消息是:贴错 ECHConfigList 时,服务器可以通过 TLS 层的 retry_configs 机制回发正确值,curl 会把它打印在 verbose 输出里:

...
* ECH: retry_configs AQD+DQA8DAAgACBvYqJy+Hgk33wh/ZLBzKSPgwxeop7gvojQzfASq7zeZQAEAAEAAQANY292ZXIuZGVmby5pZQAA/g0APEMAIAAgXkT5r4cYs8z19q5rdittyIX
  8gfQ3ENW4wj1fVoiJZBoABAABAAEADWNvdmVyLmRlZm8uaWUAAP4NADw2ACAAINXSE9EdXzEQIJZA7vpwCIQsWqsFohZARXChgPsnfI1kAAQAAQABAA1jb3Zlci5kZWZvLmllAAD+DQA8c
  QAgACASeiD5F+UoSnVoHvA2l1EifUVMFtbVZ76xwDqmMPraHQAEAAEAAQANY292ZXIuZGVmby5pZQAA
* ECH: retry_configs for defo.ie from cover.defo.ie, 319
...

此时可以复制该 base64 值重试。当前限制:retry_configs 的读取只对 OpenSSL 与 AWS-LC/BoringSSL 构建可用(wolfSSL 缺少对应 API)。并且目前 curl 还没有实现 retry_configs 的自动化二次握手——对命令行工具,可行的替代手段是:用 dig/kdig 重新取 HTTPS RR 后手动传 ecl:,或从上一轮的 verbose 输出里提取 retry_configs 值再发起一次请求。

retry_configs 的源码位置在 lib/vtls/openssl.cossl_trace_ech_retry_configs():先尝试新 API SSL_ech_get1_retry_config(),再回退到旧 API SSL_get0_ech_retry_configs(),并同步读取 ECH 状态与内外层 SNI 名用于日志。

默认设置:.curlrc 与环境变量的配合

curl 支持用 ~/.curlrc 固化默认参数,因此可以把 DoH 与 ECH 一起设为长期默认:

cat ~/.curlrc
doh-url=https://one.one.one.one/dns-query
silent
ech=true

配套注意事项(均来自原文档的踩坑记录):

  • 如果系统自带的 curl 读到了含 ech.curlrc,会警告 ech 是未知选项。加入 silent 行可压掉这类噪音,但需注意:另有脚本可能依赖非静默行为,取舍需自行判断。文档还提到历史上用 silent=TRUE,但最新构建中该写法反而会“毒化”后续配置行(使后续行被忽略),因此改用裸 silent
  • 若想长期指向自编译的 OpenSSL,可 export LD_LIBRARY_PATH=$HOME/code/openssl。副作用是部分程序会检测到 OpenSSL 版本不匹配,例如 git pushOpenSSL version mismatch. Built against 30000080, you have 30200000——执行 git push 前先 unset LD_LIBRARY_PATH 或换用另一个 shell。

配置就绪后命令行即可简化到最简形式:

./src/curl https://defo.ie/ech-check.php
# ...
# SSL_ECH_STATUS: success <img src="greentick-small.png" alt="good" /> <br/>

为什么默认建议 --ech true 而不是 hardtrue 是机会主义的——尝试 ECH,但若客户端找不到任何 ECHConfig 值则不失败;hard 在 DNS 中没有 ECHConfig 时直接硬失败,因此现阶段不适合作为默认值。但要注意两者的共同底线:一旦客户端真正发起了 ECH,而服务端解密失败,连接就会失败

源码实现走读:从 DNS 到 TLS 握手

编译开关的双宏设计

ECH 相关代码由两个宏分别保护(docs/ECH.md 原文如此说明):

  • USE_HTTPSRR:HTTPS RR 的拉取与存储代码。这部分具有通用性——将来即使没有 ECH,HTTPS RR 中的 ALPN 值(alpn=)或 IP 地址提示(ipv4hint=/ipv6hint=)也有独立用途;
  • USE_ECH:ECH 专有逻辑。

二者通过 configure.ac / CMakeLists.txt 可分别启停,但规则是:启用 USE_ECH 会强制启用 USE_HTTPSRR,且两者都要求 CURL_DISABLE_DOH 不得开启(原文档调侃道:“对于执着的人,仍可能存在一些能自相矛盾的构建组合 :-)”)。

DoH 侧:复用 dohprobe 拉取 HTTPS RR

文档指出主要新增代码在 lib/doh.c(现仓库中位于 lib/vdns/doh.c):

  • 复用现有的 dohprobe() 机制,在同一次 DoH 查询里请求目标域名的 HTTPS RR;
  • 取到值后由新增的 doh_store_https() 函数存入 dohentry 结构的新字段——该函数现位于 lib/vdns/doh.c
  • DoH 流程走通后,Curl_doh_take_result() 会顺带把 HTTPS RR 数据通过 Curl_dns_entry 结构返回,供后续 TLS 会话建立阶段取用。

HTTPS RR 的解码实现独立在 lib/vdns/httpsrr.c(配套头文件 lib/vdns/httpsrr.h),解析出的 ECHConfigList 与长度存于 DNS 条目中(echconfiglist / echconfiglist_len),供 TLS 层消费。

TLS 侧:ossl_init_ech 的三路输入

核心功能改动在 lib/vtls/openssl.cossl_init_ech(),它按优先级处理三个来源:

  1. GREASE 模式tls_ech == CURLECH_GREASE 时调用 SSL_set_enable_ech_grease(ssl, 1)
  2. 命令行 ecl::从 STRING_ECH_CONFIG 取 base64 串,curlx_base64_decode() 解码后 SSL_set1_ech_config_list() 注入;失败且模式为 CURLECH_HARD 时返回错误;
  3. DoH/DNS 缓存:从 DNS 结果 rinfo->echconfiglist 直接取二进制配置注入。

三条路任一成功进入 ECH 尝试(trying_ech_now)后,还会调用 SSL_ech_set1_server_names() 处理外层/内层 SNI(对应 pn: 覆盖 public_name 的场景)。失败处理集中在连接失败回调处:ECH 状态非 SSL_ECH_STATUS_SUCCESS 且模式为 CURLECH_HARD 时,verbose 输出 ECH: ech-hard failed 并终止连接(见 lib/vtls/openssl.c)。

握手后,SSL_ech_get1_status() 会给出内/外层 SNI 与实际状态,最终体现为测试页回显的 SSL_ECH_STATUS: success

wolfSSL 与 BoringSSL 的镜像实现

  • lib/vtls/wolfssl.c 中新增了与 OpenSSL 版本镜像的 ECH 代码(受 USE_ECH 保护);
  • AWS-LC/BoringSSL 走 lib/vtls/openssl.c#ifdef OPENSSL_IS_BORINGSSL 分支,API 差异大多是等价替换;
  • lib/vtls/rustls.c 也参与 USE_ECH 分支(Rustls 后端在 CMake 侧被直接视为具备 ECH 能力)。

已知限制

原文档的 Limitations 一节值得完整继承,分为“可暂时无视”与“当前真实限制”两层。

可暂缓的改进

  • HTTPS RR 中若出现 alpn= 标签,本可以把它作为“内层 ALPN”传给 OpenSSL,但目前尚未实现。

当前真实限制

  • 只处理第一个 HTTPS RR:多个 RR 同时发布时如何挑“对”的那条并不容易——用 ipv4hint/ipv6hint 与实际解析到的 A/AAAA 值做匹配可能是个好基础。文档作者检查时,支持 ECH 的浏览器对多条 HTTPS RR 的处理也不理想(该结论可能已过时,需复查);
  • IP 地址提示(hints)尚未利用:HTTPS RR 可以携带 ipv4hint=/ipv6hint=,curl 目前没有据此做连接选择。作者推测结合“multi-CDN”部署场景的思考可能给出答案,但现阶段无定论;
  • SVCB/HTTPS 的 aliasMode(CNAME at apex)完全未处理:理论上可以模拟“跟随 CNAME”,但是否值得做不明。文档记录当时 Chrome 不支持 aliasMode,Firefox 未再检查;
  • libcurl 应用侧未调查:以上所有讨论都基于命令行工具形态,libcurl API 用户需要哪些配套改动尚未研究;
  • 测试体系缺口:常规 curl 测试框架(runtests)要求一个 ECH-enabled 服务器与 DoH 服务器,目前未实现,只能靠脚本覆盖(见下节)。

测试现状:脚本与两个基础用例

仓库中的 tests/ech_tests.sh 是一个 1000+ 行的手工脚本,对已知支持 ECH 的服务器(Cloudflare、defo.ie 等)、有 HTTPS RR 但不做 ECH 的服务器、以及两者皆无的服务器,遍历各种 --ech 选项组合进行验证。脚本头部有一个关键前置检查:如果 ~/.curlrc 中存在激活的 ech 配置,脚本直接退出,因为默认值会污染正/负测试的判定(“把一个 fail 变成 success 或反过来”):

# Exit with an error if there is an active ech stanza in ~/.curlrc
: "${CURL_CFG_FILE=$HOME/.curlrc}"
active_ech=$(grep ech "$CURL_CFG_FILE" | grep -v "#.*ech")
if [[ "$active_ech" != "" ]]; then
  echo "You seem to have an active ECH setting in $CURL_CFG_FILE"
  ...
  exit 1
fi

脚本自身的 TODO 注释也承认:它需要“翻译成近似正式的 curl 测试”,而翻译成本包括自建 ECH-enabled 服务器和 DoH 服务器。

另一方面,常规测试框架下已有两个基础用例,目标是验证客户端在要求时确实发送 GREASE 或真实 ECH 扩展、以及在 ECH 失败时正确报错(stunnel 无 ECH 能力,被用作“不会解密 ECH”的对照):

  • data/test4000:GREASE ECH,预期结果——连接成功;
  • data/test4001:真实 ECH,预期结果——以错误 101(ECH required)失败。

这两个用例与其它同类测试一样依赖 stunnel 工具(Ubuntu 上 sudo apt install stunnel4 即可安装)。

补充:无 DoH 场景与本地测试

无 DoH 时的 ECH 支持

上述全部能力都以“curl 自身使用 DoH”为前提。文档讨论了一个尚未解决的用例:当系统 stub 解析器本身支持 DoT/DoH 时(例如作者本机用 stubby+unbound 监听 localhost:53),从网络威胁模型看 curl 也应该支持 ECH,但不必自己走 DoH。目前的探索方向都未落地:

  • 扩展 c-ares 支持 HTTPS RR——但 HTTPS/SVCB 的 tag=value 扩展性与 c-ares“为每种支持的 RRtype 定义专用解码结构”的风格是否匹配存疑,且不确定下游 curl 部署中有多少实际使用 c-ares;
  • 引入其它支持 HTTPS RR 的通用 DNS 库——不确定它能否被几乎所有 curl 构建与下游发行版采用。

当前结论:先把“用 DoH”的方案跑一段时间积累经验,此用例暂搁置。

本地 localhost 测试

针对本地调试,可以用 OpenSSL s_server 搭一个 ECH 服务器(作者团队在另一个仓库发布了详细步骤)。基本姿势是:

# 在 ech-dev-utils 仓库中启动 ECH 服务器(脚本支持大量 ECH 相关选项,echsvr.sh -h 查看)
./scripts/echsvr.sh -d

另开窗口用 curl 打向它,用 --connect-to 把域名映射到本地端口:

cd $HOME/code/curl/
./src/curl -vvv --insecure --connect-to foo.example.com:8443:localhost:8443 \
  --ech ecl:AD7+DQA6uwAgACBix2B78sX+EQhEbxMspDOc8Z3xVS5aQpYP0Cxpc2AWPAAEAAEAAQALZXhhbXBsZS5jb20AAA==

注意 --connect-tofoo.example.com:8443 重定向到 localhost:8443,而 ECH 的内层 SNI 仍是 foo.example.com,这与 ECHConfigList 中编码的 public_name 对应。

小结

curl 的 ECH 支持是一个边界清晰的实验性实现:autotools 与 CMake 均提供 --enable-ech / -DUSE_ECH=1 开关并强制联动 HTTPS RR 能力;运行时以 --ech 的六种取值控制 GREASE、机会式尝试、强制尝试与显式配置注入;数据通路为 “DoH 复用 dohprobe() 拉取 HTTPS RR → doh_store_https() 落缓存 → Curl_doh_take_result() 交付 → ossl_init_ech()SSL_set1_ech_config_list() 喂入 TLS 库”。多后端(OpenSSL fork、BoringSSL、wolfSSL、Rustls)通过各自受宏保护的分支镜像实现,能力差异(如 pn:retry_configs、GREASE 语义)在文档与源码中均有明确标注。由于 retry_configs 自动化、多条 HTTPS RR 选择、aliasMode 与 libcurl 应用侧支持均未完成,在生产环境启用前应以本文“已知限制”一节为准绳评估风险。

登录后查看全文
热门项目推荐
相关项目推荐