DNS 协议

一、DNS 基础原理

1. 为什么需要 DNS?

互联网中设备通信依赖 IP 地址(如 183.232.231.174),但 IP 地址一串数字难记,人类更习惯用域名(如www.baidu.com)访问服务。DNS 的核心作用就是域名与 IP 地址的双向映射,同时提供负载均衡、容错等扩展能力,让用户无需记忆 IP,就能顺畅访问互联网服务。

2. DNS 核心工作流程

以 "访问www.baidu.com" 为例,DNS 解析流程分 6 步,结合递归查询(用户侧)和迭代查询(服务器侧):

  1. 本地缓存查询:用户浏览器先查本地 DNS 缓存(系统缓存、浏览器缓存),若有该域名的 IP 映射,直接返回结果,无需后续步骤;
  2. 本地 DNS 服务器查询:若本地缓存无结果,请求发送到本地 DNS 服务器(通常由运营商分配,如 114.114.114.114),本地 DNS 先查自身缓存,有结果则返回;
  3. 根服务器迭代查询:本地 DNS 无缓存时,向根 DNS 服务器(全球共 13 组)发起请求,根服务器不存储具体域名 IP,仅返回对应顶级域(如.com)的权威服务器地址;
  4. 顶级域服务器迭代查询:本地 DNS 向顶级域服务器(.com 服务器)请求,顶级域服务器返回目标域名(baidu.com)的权威服务器地址;
  5. 权威服务器查询:本地 DNS 向baidu.com的权威服务器请求,权威服务器存储该域名的 IP 映射,返回具体 IP;
  6. 结果缓存与返回:本地 DNS 将 IP 映射存入自身缓存(设置超时时间),同时返回给浏览器,浏览器缓存后发起 HTTP 请求,完成访问。
3. DNS 核心概念与记录类型

核心概念

  • 递归查询:客户端发起请求后,服务器全程处理,最终返回结果(用户→本地 DNS);
  • 迭代查询:服务器仅返回下一级服务器地址,由请求方自行向下查询(本地 DNS→根→顶级域→权威服务器);
  • TTL(生存时间):DNS 记录的缓存有效期(单位秒),超时后缓存失效,需重新查询,避免 IP 变更导致解析异常。

常用 DNS 记录类型

  • A 记录: IPv4 地址映射(最常用,如www.baidu.com→183.232.231.174);
  • AAAA 记录:IPv6 地址映射,对应 IPv6 场景;
  • CNAME 记录:别名记录,将域名指向另一个域名(如blog.baidu.comwww.baidu.com),无直接 IP 映射;
  • MX 记录:邮件交换记录,指定接收该域名邮件的服务器地址(如 @baidu.com的邮件服务器);
  • NS 记录:名称服务器记录,指定该域名的权威服务器地址。

二、DNS 高频考点

1. DNS 使用什么端口和协议?

默认用 UDP 53 端口(普通解析,报文小、效率高);当报文超过 512 字节(如返回多个 IP)或进行区域传输(服务器间同步 DNS 记录)时,用 TCP 53 端口。

2. 本地 DNS 服务器的作用是什么?

核心:缓存解析结果,减少重复查询,提升解析效率;代理客户端发起迭代查询,降低用户侧复杂度。

3. DNS 解析的顺序是什么?

浏览器缓存→系统缓存→本地 DNS 服务器缓存→根服务器→顶级域服务器→权威服务器。

4. DNS 劫持(域名劫持)的原理及防御?

原理:攻击者篡改 DNS 解析结果,将域名指向恶意 IP(如钓鱼网站),常见方式有本地 HOSTS 文件篡改、路由器 DNS 劫持、本地 DNS 服务器污染;

防御:① 改用公共 DNS(如 8.8.8.8、1.1.1.1);② 开启 DNS 加密(DNS over HTTPS/DoH);③ 定期检查 HOSTS 文件和路由器设置。

5. DNS 负载均衡的实现方式?

核心:权威服务器返回多个目标 IP,本地 DNS 按一定规则(轮询、权重)选择 IP,实现请求分发;补充:CDN 的核心就是结合 DNS 负载均衡,将用户解析到最近的节点。

6. 什么是 DNS 污染(DNS 缓存投毒)?

原理:攻击者向本地 DNS 服务器注入虚假 DNS 记录,污染缓存,导致后续解析返回错误 IP;与 DNS 劫持的区别:污染针对服务器缓存,劫持针对客户端 / 传输链路。

7. DoH 和 DoT 是什么?

均为 DNS 加密协议,解决传统 DNS 明文传输易被监听、篡改的问题:DoH(DNS over HTTPS)通过 HTTPS 传输 DNS 请求,端口 443;DoT(DNS over TLS)通过 TLS 协议传输,端口 853。

8. DNS 是应用层协议?

是应用层协议,依赖 UDP/TCP 传输,属于 TCP/IP 协议栈的应用层;

9. 根服务器存储所有域名 IP?

根服务器仅负责指向顶级域服务器,不存储具体域名映射;

10. CNAME 记录可以和 A 记录共存?

同一域名不能同时设置 A 记录和 CNAME 记录,需二选一。

三、DNS 域名解析实现

1. 环境与依赖
  • 环境:Windows(MinGW/VS)、Linux(Ubuntu/CentOS)通用;
  • 依赖:无需额外第三方库,使用系统自带的getaddrinfo函数(跨平台、易上手,适合小白),该函数封装了 DNS 解析逻辑,无需手动处理协议细节;
  • 核心:getaddrinfo支持 IPv4/IPv6 解析,自动处理 DNS 缓存,简化开发流程。
2. 基础域名解析

实现功能:输入域名(如www.baidu.com),解析并输出对应的 IPv4 地址,代码加详细注释,小白可直接复制运行。

cpp 复制代码
#include <string<sys/socket.h>
#include <netdb.h>
#include<arpa/inet.h<cstring>

// 域名解析函数:输入域名,输出IPv4地址
std::string dns_resolve(const std::string& domain) {
    struct addrinfo hints;       // 解析参数设置
    struct addrinfo* result;     // 解析结果
    struct addrinfo* ptr;        // 遍历结果的指针
    char ip_str[INET_ADDRSTRLEN];// 存储IPv4地址字符串

    // 初始化hints结构体,指定解析类型为IPv4、TCP
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET;    // AF_INET=IPv4,AF_INET6=IPv6
    hints.ai_socktype = SOCK_STREAM; // TCP类型(不影响DNS解析,仅指定协议族)

    // 调用getaddrinfo执行DNS解析,0表示成功
    int ret = getaddrinfo(domain.c_str(), nullptr, &hints, &result);
    if (ret != 0) {
        std::< "DNS解析失败< gai_strerror< std::endl;
        return "";
    }

    // 遍历解析结果,提取第一个IPv4地址
    std::string ip;
    for (ptr = result; ptr != nullptr; ptr = ptr->ai_next) {
        // 将二进制IP转换为字符串格式
        inet_ntop(ptr->ai_family, 
                  &((struct sockaddr_in*)ptr->ai_addr)->sin_addr,
                  ip_str, sizeof(ip_str));
        ip = ip_str;
        break; // 取第一个IP,可扩展为返回所有IP
    }

    freeaddrinfo(result); // 释放结果内存,避免内存泄漏
    return ip;
}

int main() {
    std::string domain;
    std< "请输入要解析的域名:";
    std::cin >> domain;

    std::string ip = dns_resolve(domain);
    if (!ip.empty()) {
        std::cout << " 的IPv4地址:" << ip << std::endl;
    }

    return 0;
}

编译与运行

  • Linux:g++ dns_demo.cpp -o dns_demo,运行./dns_demo,输入域名即可得到 IP;
  • Windows(VS):直接创建控制台项目,粘贴代码编译运行,无需额外配置。
3. 支持多 IP、IPv6 解析

基于基础版本扩展,实现同时返回多个 IPv4/IPv6 地址,适配更多场景,代码如下:

cpp 复制代码
<string>
#include <vector>
#include <sys/socket.h>
<netdb.h><arpa/inet.h>
#include <cstring>

// 解析域名,返回所有IPv4/IPv6地址
<std::string> dns_resolve_all(const std::string& domain, bool is_ipv6 = false) {
    struct addrinfo hints;
    struct addrinfo* result;
    struct addrinfo* ptr;
    std<std::string> ips;

    memset(&hints, 0, sizeof(hints));
    // 选择IPv4或IPv6
    hints.ai_family = is_ipv6 ? AF_INET6 : AF_INET;
    hints.ai_socktype = SOCK_STREAM;

    int ret = getaddrinfo(domain.c_str(), nullptr, &hints, &result);
    if (ret != 0) {
        std< "DNS解析< gai_str< std::endl;
        return ips;
    }

    // 遍历所有解析结果,收集IP
    char ip_str[INET6_ADDRSTRLEN]; // 适配IPv6最长长度
    for (ptr = result; ptr != nullptr; ptr = ptr->ai_next) {
        if (is_ipv6) {
            inet_ntop(ptr->ai_family, 
                      &((struct sockaddr_in6*)ptr->ai_addr)->sin6_addr,
                      ip_str, sizeof(ip_str));
        } else {
            inet_ntop(ptr->ai_family, 
                      &((struct sockaddr_in*)ptr->ai_addr)->sin_addr,
                      ip_str, sizeof(ip_str));
        }
        ips.push_back(ip_str);
    }

    freeaddrinfo(result);
    return ips;
}

int main() {
    std::string domain;
   < "请输入要解析的域名:";
    std::cin >> domain;

    // 解析IPv4地址
<std::string> ipv4_ips = dns_resolve_all(domain, false);
    if (!ipv4_ips.empty()) {
        std::cout << domain << " 的IPv4地址:< std::endl;
        for (const auto& ip : ipv4_ips) {
            std::cout << "- " << std::endl;
        }
    }

    // 解析IPv6地址
    std::<std::string> ipv6_ips = dns_resolve_all(domain, true);
    if (!ipv6_ips.empty()) {
        std::cout< " 的IPv< std::endl;
        for (const auto& ip : ipv6_ips) {
            std::cout << std::endl;
        }
    }

    return 0;
}
4. 扩展
  • 结合缓存:添加本地缓存逻辑(用哈希表存储域名 - IP 映射,设置 TTL),减少重复解析,模拟本地 DNS 缓存功能;
  • 批量解析:读取文件中的多个域名,批量解析并输出结果,生成解析报告;
  • 自定义 DNS 服务器:指定解析使用的 DNS 服务器(如 8.8.8.8),而非系统默认,需结合socket手动发送 DNS UDP 请求,深入底层协议;
  • 异常处理强化:添加超时机制、重试逻辑,处理网络异常、域名不存在等场景。
相关推荐
小小代码狗2 小时前
【无标题】
网络·sql·php
qq_336313932 小时前
java基础-网络编程-UDP
网络
乾元2 小时前
范式转移:从基于规则的“特征码”到基于统计的“特征向量”
运维·网络·人工智能·网络协议·安全
txinyu的博客2 小时前
手写 C++ 高性能 Reactor 网络服务器
服务器·网络·c++
华普微HOPERF2 小时前
BLE6.0规范,如何助力智能门锁突破性能极限?
网络·智能家居·解决方案·智能门锁·芯片模组·蓝牙6.0
程序猿编码2 小时前
无状态TCP技术:DNS代理的轻量级实现逻辑与核心原理(C/C++代码实现)
c语言·网络·c++·tcp/ip·dns
小二·2 小时前
Python Web 开发进阶实战:可验证网络 —— 在 Flask + Vue 中实现去中心化身份(DID)与零知识证明(ZKP)认证
前端·网络·python
饿了么骑手贪大心3 小时前
简单易用的网络测试工具——Clumsy使用总结
网络·测试工具
Jia ming3 小时前
游戏卡顿?SMB传输惹的祸!
网络