linux下读取/etc/resolv.conf是最直接的方式,windows上必须用getnetworkparams;跨平台需区分_win32和__linux__,避免用__unix__;容器中该文件可能被覆盖为127.0.0.11。

Linux下读取/etc/resolv.conf是最直接的方式
绝大多数Linux发行版(包括WSL)的DNS配置都落在/etc/resolv.conf里,C++程序只需按文本文件读取即可。它格式简单:nameserver行后跟IP地址,可能有多行,也可能被符号链接到动态生成路径(比如/run/systemd/resolve/stub-resolv.conf)。
实操建议:
- 用
std::ifstream打开文件,逐行std::getline,用std::string::find检查是否以"nameserver "开头(注意末尾空格) - 跳过注释行(以
#开头)和空行 - 提取IP部分时,用
std::string::find_first_of(" \t")找第一个空白符位置,避免误截IPv6地址中的冒号 - 别硬编码路径——先检查
/etc/resolv.conf是否存在且可读;若不可读,再尝试readlink("/etc/resolv.conf", ...)获取真实路径
Windows上必须用GetNetworkParams,不能靠解析文件
Windows没有统一的resolv.conf等价物,DNS服务器列表由系统网络栈动态管理。GetNetworkParams是唯一可靠方式,它返回FIXED_INFO结构,其中pDnsServerList是链表头。
常见错误现象:
立即学习“C++免费学习笔记(深入)”;
- 忘记调用
#include <iphlpapi.h></iphlpapi.h>和链接-liphlpapi(Linux交叉编译时容易漏) - 传入
NULL缓冲区直接调用,导致ERROR_BUFFER_OVERFLOW但没处理——必须先调用一次获取所需缓冲区大小 - 把
pDnsServerList当成单个IP字符串处理,其实它是IP_ADDR_STRING链表,需用Next字段遍历 - 忽略
AdapterName字段,误以为所有适配器共用同一组DNS——实际每个适配器可独立配置
getaddrinfo不暴露DNS服务器,别试图绕过系统API
有人想通过抓getaddrinfo或res_init的内部行为反推DNS服务器,这条路走不通。glibc的resolv.conf解析逻辑是私有实现,musl甚至不读该文件;Windows的getaddrinfo完全走Winsock栈,底层DNS服务器对应用透明。
为什么这样做:
-
getaddrinfo只负责“把域名转成IP”,不关心“谁答的”,也不暴露查询路径 - 强行hook libc函数风险极高:不同版本glibc/musl行为差异大,静态链接时hook失效,ASLR下地址难固定
- 即使成功,拿到的也只是当前查询使用的DNS(可能来自DHCP、组策略或手动覆盖),不是完整列表
跨平台代码要区分_WIN32和__linux__,别用ifdef __unix__
__unix__在macOS上也定义,但macOS既不用/etc/resolv.conf(默认走mDNSResponder),也不支持GetNetworkParams。真要支持macOS,得用SCDynamicStoreCopyDNSConfiguration,那是另一套API。
实操建议:
- 只在明确目标平台时启用对应逻辑:Linux用
/etc/resolv.conf,Windows用GetNetworkParams,其他平台显式报错或返回空列表 - 避免用
__unix__做条件编译——加个static_assert(false, "Unsupported platform")比静默失败更安全 - 如果项目必须支持macOS,单独封装
get_dns_servers_macos(),用CoreFoundation和SystemConfiguration框架,别混进Linux/Windows分支
真正麻烦的是容器环境:/etc/resolv.conf可能被挂载为只读,或者内容是127.0.0.11(Docker内置DNS)。这时候读出来的不是宿主机DNS,而是容器运行时注入的——这点很容易被忽略。










