如何写校验DNS解析结果脚本

wen 实用脚本 31

如何写校验DNS解析结果脚本:从入门到精通的实战指南

📖 目录导读

  1. 为什么需要校验DNS解析脚本? —— 常见场景与痛点
  2. 核心原理:DNS解析结果的结构与校验逻辑
  3. 脚本编写基础工具(Linux/Windows通用)
  4. 实战案例:多场景校验脚本实现
    • 1 检查A记录是否与预期IP匹配
    • 2 批量检查多个域名的MX记录
    • 3 校验CNAME链是否正确
  5. 常见问题与调试技巧(QA环节)
  6. 性能优化与异常处理
  7. 编写可维护的DNS校验脚本

为什么需要校验DNS解析结果脚本?

在企业运维、网站迁移、CDN配置变更或故障排查时,DNS解析的正确性直接影响服务可用性,手动逐个查询不仅低效,还容易遗漏。

如何写校验DNS解析结果脚本

  • 迁移服务器后,需确认所有域名的A记录是否指向新IP。
  • 配置了CNAME到CDN后,需验证解析链是否完整。
  • 监控系统需要定时检查DNS解析结果是否异常。

核心痛点:人工操作无法应对数百个域名的批量校验,且无法自动比对预期值。

核心原理:DNS解析结果的结构与校验逻辑

DNS解析返回的结果包含多种记录类型:

  • A记录:域名→IPv4地址
  • AAAA记录:域名→IPv6地址
  • CNAME记录:域名→另一个域名(别名)
  • MX记录:域名→邮件服务器地址
  • TXT记录:验证信息(如SPF、DKIM)

校验逻辑

输入:域名 + 预期记录类型 + 预期值
输出:解析结果是否与预期一致

脚本需处理:解析失败(超时/无记录)、结果不一致、多条记录比较。

脚本编写基础工具(Linux/Windows通用)

Linux/macOS:dignslookup

# 安装
sudo apt install dnsutils  # Debian/Ubuntu
brew install bind          # macOS
# 基础用法
dig example.com A +short        # 获取A记录
dig example.com MX +short       # 获取MX记录
nslookup -type=CNAME example.com # 获取CNAME

Windows:nslookupResolve-DnsName (PowerShell)

Resolve-DnsName -Name example.com -Type A

通用脚本语言:Python 的 dns.resolver

import dns.resolver
# 跨平台解析

实战案例:多场景校验脚本实现

1 检查A记录是否与预期IP匹配

场景:网站迁移后,需确认domain.com指向新的IP地址(如2.3.4)。

Python脚本

import dns.resolver
import sys
def check_a_record(domain, expected_ip):
    try:
        answers = dns.resolver.resolve(domain, 'A')
        actual_ips = [str(r) for r in answers]
        return expected_ip in actual_ips, actual_ips
    except Exception as e:
        return False, str(e)
# 使用
domain = "example.com"
expected = "1.2.3.4"
is_ok, detail = check_a_record(domain, expected)
print(f"校验结果:{'通过' if is_ok else '失败'},当前记录:{detail}")

2 批量检查多个域名的MX记录

场景:邮件服务器变更后,验证所有相关域名的MX指向新服务器。

Shell脚本(Linux)

#!/bin/bash
DOMAINS=("example.com" "test.com" "mail.example.com")
EXPECTED_MX="mx1.newexample.com"
for domain in "${DOMAINS[@]}"; do
    mx_result=$(dig $domain MX +short | awk '{print $2}' | sort)
    if [[ "$mx_result" == "$EXPECTED_MX" ]]; then
        echo "[PASS] $domain 的MX记录正确"
    else
        echo "[FAIL] $domain 的MX记录为 $mx_result,预期为 $EXPECTED_MX"
    fi
done

3 校验CNAME链是否正确

场景:配置了cdn.example.commycdn.cloudfront.net的CNAME,需确认解析链无断裂。

逻辑要点

  1. 获取CNAME记录的下一跳域名。
  2. 对新域名进行二次解析(可能还是CNAME或最终A记录)。
  3. 对比最终IP是否符合预期。
def resolve_cname_chain(domain, max_depth=5):
    chain = []
    current = domain
    for _ in range(max_depth):
        try:
            answers = dns.resolver.resolve(current, 'CNAME')
            target = str(answers[0].target)
            chain.append((current, target))
            current = target
        except dns.resolver.NoAnswer:
            # 没有CNAME,尝试A记录
            a_answers = dns.resolver.resolve(current, 'A')
            chain.append((current, [str(r) for r in a_answers]))
            break
        except Exception as e:
            chain.append((current, f"解析错误: {e}"))
            break
    return chain
# 示例:校验域名
result = resolve_cname_chain("cdn.example.com")
for hop in result:
    print(f"{hop[0]} -> {hop[1]}")

常见问题与调试技巧(QA环节)

Q1:为什么脚本执行时提示“域名解析超时”?

A:常见原因包括:DNS服务器不可用、网络防火墙拦截、域名真实不存在,建议:

  • 检查本机DNS设置:cat /etc/resolv.conf(Linux)或 ipconfig /all(Windows)。
  • 使用公共DNS(如8.8.8.8)作为备用:dig @8.8.8.8 example.com
  • 脚本中增加超时处理:dns.resolver.resolve(domain, 'A', lifetime=5)

Q2:如何忽略大小写敏感问题?

A:域名本身不区分大小写,但部分解析结果(如TXT记录)建议统一转换为小写比较:

actual_lower = actual.lower()
expected_lower = expected.lower()

Q3:多条A记录场景如何精确匹配?

A:如果预期应包含多个IP(如负载均衡),需判断子集关系而非完全相等:

expected_ips = ["1.2.3.4", "5.6.7.8"]
actual_ips = [str(r) for r in answers]
if set(expected_ips).issubset(set(actual_ips)):
    print("包含预期IP集合")

Q4:脚本在Windows下无法运行怎么办?

A:Windows推荐使用PowerShell,内置Resolve-DnsName

$result = Resolve-DnsName -Name "example.com" -Type A -ErrorAction SilentlyContinue
if ($result.IPAddress -eq "1.2.3.4") { Write-Host "通过" }

性能优化与异常处理

批量校验时避免超时

  • 使用连接池(Python的dns.resolver通过复用Resolver对象自动实现)。
  • 增加异步并发:对于1000+域名,异步库asyncio配合aiodns可提升10倍速度。

示例:异步批量校验100个域名

import asyncio
import aiodns
async def check_one(domain, expected):
    resolver = aiodns.DNSResolver()
    try:
        result = await resolver.gethostbyname(domain, socket.AF_INET)
        return domain == expected
    except:
        return False
async def main():
    domains = ["example.com", "test.com"]
    tasks = [check_one(domain, "1.2.3.4") for domain in domains]
    results = await asyncio.gather(*tasks)
    print(results)
asyncio.run(main())

异常处理策略

  • dns.exception.Timeout → 记录超时并重试最多3次,间隔2秒。
  • dns.resolver.NXDOMAIN → 明确告知域名不存在。
  • dns.resolver.NoAnswer → 表示域名存在但无指定类型记录。

编写可维护的DNS校验脚本

  1. 模块化设计:将单域名校验、批量校验、结果输出分离成函数。
  2. 参数化配置:使用配置文件或环境变量存储预期值,避免硬编码。
  3. 日志与报告:生成CSV/JSON格式的校验报告,包含通过/失败/异常。
  4. 跨平台兼容:优先使用Python,其次利用dig(需注意Windows的WSL环境)。
  5. 测试用例:包括正常域名、不存在的域名、多记录场景。

终极建议:在正式使用前,先用5-10个已知结果域名测试脚本,确保逻辑无误,如果涉及生产环境,建议先用--dry-run模式模拟输出而不执行真正校验。

完整脚本示例已包含在本文各小节中,请根据自身环境调整DNS服务器和超时参数,如果出现任何域名占位符,请替换为你的实际域名。

抱歉,评论功能暂时关闭!