curl 命令行--dns-servers选项完全指南:用 c-ares 自定义 DNS 服务器与端口
【免费下载链接】curlA command line tool and library for transferring data with URL syntax, supporting DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, MQTTS, POP3, POP3S, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS and WSS. libcurl offers a myriad of powerful features项目地址: https://gitcode.com/GitHub_Trending/cu/curl
--dns-servers是 curl 提供的覆盖系统默认 DNS 解析目标的高级选项,它允许用户在命令行直接指定一个或多个 DNS 服务器(含自定义端口),常用于调试、绕过错误的上游 DNS、或对接内网解析环境。本文以 curl 仓库官方手册文档 docs/cmdline-opts/dns-servers.md 为核心,结合命令解析、libcurl 选项设置与 c-ares 解析器三层源码,讲透该选项的语法、适用范围、底层实现与排错思路。
一、选项概览:它能做什么
从官方选项定义文档来看,--dns-servers的核心作用非常聚焦:
设置要使用的 DNS 服务器列表,以取代系统默认配置。IP 地址列表应以逗号分隔;也支持可选端口号,以冒号附加在 IP 地址后。
即它控制的是 curl 内部做主机名解析(将主机名转换为 IP 地址)这一环节使用的上游 DNS 服务器,而非连接目标本身。
该选项定义在 docs/cmdline-opts/dns-servers.md,其 YAML 头元数据完整说明了关键属性:
| 字段 | 取值 | 含义 |
|---|---|---|
Long | dns-servers | 长选项名称 |
Arg | <addresses> | 需要一个"服务器地址列表"参数 |
Help | DNS server addrs to use | 帮助信息中的一句话说明 |
Protocols | DNS | 作用于 DNS 解析过程 |
Requires | c-ares | 仅在启用 c-ares 解析后端时可用 |
Added | 7.33.0 | 从该版本开始提供 |
Category | dns | 属于 DNS 相关选项类别 |
Multi | single | 非可累加选项(重复指定时后值覆盖前值,而非累积成列表) |
二、前提条件:必须在编译期启用 c-ares
这是理解--dns-servers最重要的一步——它不是所有 curl 版本/发行版都携带的选项。curl 支持多种主机名解析后端(阻塞式、线程式、c-ares),而只有 c-ares 后端才能让应用自定义 DNS 服务器。选项元数据中的Requires: c-ares已明确这一点。
从仓库构建配置可以印证:
- CMake 构建中默认关闭,需显式开启。见 CMakeLists.txt 第 184 行的
option(ENABLE_ARES "Enable c-ares support" OFF);开启后它会通过 CMake/FindCares.cmake 定位系统 c-ares,并要求链接CURL::cares。 - autotools 构建中则通过
--enable-ares启用,见 configure.ac 第 4380-4381 行,启用成功后会AC_DEFINE(USE_RESOLV_ARES, 1, ...),此时解析后端报告为c-ares。 - c-ares 版本有硬性下限。源码 lib/vdns/asyn-ares.c 第 63-65 行强制要求
ARES_VERSION >= 0x011000(即 c-ares 1.16.0 及以上),否则编译直接报错。
验证当前构建是否支持该选项,可在交互使用前执行curl --version检查输出中是否包含c-ares,或直接运行:
curl --help all | grep dns-servers三、命令语法与参数格式详解
完整语法:
curl --dns-servers <addresses> [其它选项] URL参数<addresses>是一个由逗号分隔的 DNS 服务器地址列表,官方文档给出了两个示例:
# 使用两个 IPv4 DNS 服务器(默认 53 端口) curl --dns-servers 192.168.0.1,192.168.0.2 $URL # 使用一个带自定义端口的 DNS 服务器 curl --dns-servers 10.0.0.1:53 $URL格式要点可归纳为:
- 多服务器用英文逗号分隔:如
--dns-servers 8.8.8.8,1.1.1.1。curl 会按文档语义将其理解为有序的服务器列表;同时从 c-ares 的ares_set_servers_ports_csv调用看,该函数本身也接受一个 CSV 格式字符串(见下文源码解析),两者格式完全对应。 - 可选端口用英文冒号追加:
<ip>:<port>形式,如10.0.0.1:5353。这与"默认 53 端口可省略"的常见 DNS 配置习惯一致。 - 支持 IPv6 地址场景:地址格式由 c-ares 解析器按 CSV 规则处理,结合同目录的 dns-ipv6-addr.md 等选项可知,curl 的 DNS 定制一族对 v4/v6 环境均有覆盖。涉及 IPv6 时需按 c-ares 的 CSV 规则书写,端口分隔同样是冒号。
与配置文件 / 变量写法
该选项也可以写入 curl 配置文件(--config指定的文件或~/.curlrc),行内写法与命令行一致:
# ~/.curlrc 示例 dns-servers = "192.168.0.1,192.168.0.2"配置文件的通用解析规则参见 docs/cmdline-opts/config.md。
四、一条命令的完整调用链(源码级解析)
--dns-servers从命令行到真正生效,跨越了三个层次,仓库源码完整保留了这条链路。
第 1 层:命令行参数解析
在命令行工具源码 src/tool_getparam.c 第 120 行注册了选项名{"dns-servers", ARG_STRG, ' ', C_DNS_SERVERS},表明它是一个字符串参数;第 2536-2542 行则做了关键守卫:
case C_DNS_SERVERS: /* --dns-servers */ if(!curlinfo->ares_num) /* c-ares is needed for this */ err = PARAM_LIBCURL_DOESNT_SUPPORT; else /* IP addrs of DNS servers */ err = getstr(&config->dns_servers, nextarg, DENY_BLANK);可见:工具会先检查运行时探测到的 c-ares 支持情况(curlinfo->ares_num)。若当前链接的 libcurl 未启用 c-ares,则直接返回PARAM_LIBCURL_DOESNT_SUPPORT,命令行会拒绝接受该选项并给出相应错误提示,而不是默默忽略。DENY_BLANK表示不允许空字符串参数。
第 2 层:映射为 libcurl 传输选项
解析得到字符串后,src/config2setopts.c 第 1062 行在构建 easy 句柄时把它转换为 libcurl 选项:
MY_SETOPT_STR(curl, CURLOPT_DNS_SERVERS, config->dns_servers);程序化使用 libcurl 的开发者也可直接设置CURLOPT_DNS_SERVERS(字符串类型选项,见 lib/easyoptions.c 第 83 行的类型登记)。
第 3 层:libcurl 存储与 c-ares 落地
在 lib/setopt.c 第 2248-2250 行,CURLOPT_DNS_SERVERS的分支被#ifdef USE_RESOLV_ARES包裹:
#ifdef USE_RESOLV_ARES case CURLOPT_DNS_SERVERS: return Curl_setstropt(data, STRING_DNS_SERVERS, ptr);这从库层面再次印证:编译时未定义USE_RESOLV_ARES(即未启用 c-ares)的 libcurl 根本不接受该选项,此时无论命令行还是curl_easy_setopt都无法使用自定义 DNS 服务器。
真正把服务器列表下发给 c-ares 的逻辑在 lib/vdns/asyn-ares.c 的async_ares_set_dns_servers()(第 658-693 行)。该函数在创建 c-ares channel 时被调用(第 165 行),核心是:
const char *servers = CURL_EASY_STR(data, STRING_DNS_SERVERS); ... if(ares && ares->channel) ares_result = ares_set_servers_ports_csv(ares->channel, servers);即最终调用 c-ares 的ares_set_servers_ports_csv(),把逗号分隔的地址列表(含可选端口)解析并注入 ares channel。代码同时把 c-ares 返回码映射为 curl 错误码:
| c-ares 返回码 | curl 结果 |
|---|---|
ARES_SUCCESS | CURLE_OK(成功) |
ARES_ENOMEM | CURLE_OUT_OF_MEMORY |
ARES_ENOTINITIALIZED/ARES_ENODATA/ARES_EBADSTR等 | CURLE_BAD_FUNCTION_ARGUMENT,并打印bad servers set调试信息 |
这意味着非法格式的服务器列表会在选项生效检查阶段被拒绝(返回参数错误),而非在解析时静默失败。
调试彩蛋:CURL_DNS_SERVER
在同文件第 666-669 行,还存在一个仅供调试构建(DEBUGBUILD)使用的环境变量覆盖机制:
#ifdef DEBUGBUILD if(getenv("CURL_DNS_SERVER")) servers = getenv("CURL_DNS_SERVER"); #endif也就是说在开启 debug 的 curl 构建中,可通过设置CURL_DNS_SERVER环境变量临时覆盖命令行传入的服务器列表,方便测试人员在不改动参数的情况下切换上游 DNS。
五、与相近 DNS 定制选项的关系
--dns-servers属于 curl 的 DNS 定制选项族,使用时常与以下选项区分或搭配,它们定义于同目录:
- --dns-interface:指定发送 DNS 查询所使用的本地网络接口(同样要求 c-ares),对应
CURLOPT_DNS_INTERFACE。 - --dns-ipv4-addr / --dns-ipv6-addr:指定绑定到 DNS 套接字上的本地 IPv4/IPv6 地址(源地址),对应
CURLOPT_DNS_LOCAL_IP4/CURLOPT_DNS_LOCAL_IP6。 - --doh-url:使用 DNS-over-HTTPS 方式解析,走 HTTPS 通道而非传统 DNS 服务器,不要求 c-ares。
- --resolve:为指定主机名静态固定解析结果(可含端口),优先级高于任何 DNS 服务器;适合"就是要连到某个特定 IP"的场景。
四者的关系可以这样理解:--resolve是绕过解析、直接钉死结果;--dns-servers是替换查询目标;--dns-interface/--dns-ipv4-addr/--dns-ipv6-addr是控制查询报文从哪个本地出口发出。底层上,四个 c-ares 相关选项在 lib/vdns/asyn-ares.c 的 channel 初始化流程中按顺序依次应用(第 165-179 行),互不冲突。
在 lib/vdns/asyn-ares.c 第 652-657 行的注释中还揭示了两种特殊场景:应用通过CURLOPT_DNS_SERVERS传入NULL表示清除先前设定并重建 ares channel;而在 channel 惰性初始化且值为NULL(无偏好)时不重置既有 channel。这解释了为何在同一个 easy 句柄生命周期内反复设置/清除该选项是安全的。
六、常见问题与排查思路
Q1:提示选项不支持 /curl --help中看不到该选项?大概率是链接的 curl 未启用 c-ares。按第二节方法重新编译(CMake 开启ENABLE_ARES、autotools 加--enable-ares),或换用带 c-ares 的发行包。可先curl --version确认。
Q2:传入的服务器列表报参数错误?确认列表格式:纯逗号分隔、端口用冒号、无多余空白、每个地址本身合法。底层ares_set_servers_ports_csv解析失败会映射为CURLE_BAD_FUNCTION_ARGUMENT(见第四节错误映射表)。
Q3:指定后仍走了系统 DNS?先确认命中 c-ares 后端而不是线程式/阻塞式解析器;其次确认没有--resolve或 DoH(--doh-url)等更高优先级机制在起作用。
Q4:多个 DNS 服务器如何选择?列表以逗号分隔作为整体传入 c-ares,由 c-ares 按自身策略使用这些服务器;curl 侧不额外做负载均衡,因此"顺序即策略"的期望应由 c-ares 行为决定,不同 c-ares 版本对服务器选择/失败切换的策略可能存在差异。
七、速查与延伸
- 选项手册原文:docs/cmdline-opts/dns-servers.md
- 命令行帮助清单:src/tool_listhelp.c 第 158 行
- 参数解析与 c-ares 守卫:src/tool_getparam.c 第 120、2536-2542 行
- 选项到 libcurl 的映射:src/config2setopts.c 第 1062 行
- libcurl 选项存储:lib/setopt.c 第 2248-2250 行、lib/easyoptions.c 第 83 行
- c-ares 落地实现:lib/vdns/asyn-ares.c 第 658-693 行
- 构建开关:CMakeLists.txt 第 184 行、configure.ac 第 4380-4381 行、CMake/FindCares.cmake
一句话总结:--dns-servers的威力在于把"用哪台 DNS 服务器解析"的决定权从操作系统交还到 curl 使用者手中,但其可用性的根基是 c-ares 编译支持——先确认后端,再关注列表格式,最后借助错误映射快速定位问题。
【免费下载链接】curlA command line tool and library for transferring data with URL syntax, supporting DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, MQTTS, POP3, POP3S, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS and WSS. libcurl offers a myriad of powerful features项目地址: https://gitcode.com/GitHub_Trending/cu/curl
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考