
本文介绍如何使用 Go 标准库 net 包的 LookupSRV 函数,高效、安全地批量查询多个服务名(如 sipfederationtls、autodiscover、VLMCS)对应的 SRV DNS 记录,并正确处理成功与失败场景。
本文介绍如何使用 go 标准库 `net` 包的 `lookupsrv` 函数,高效、安全地批量查询多个服务名(如 `sipfederationtls`、`autodiscover`、`vlmcs`)对应的 srv dns 记录,并正确处理成功与失败场景。
Go 的 net.LookupSRV 函数不支持一次性查询多个服务名——它严格遵循 DNS SRV 查询语义:每次调用仅对应一个 <service>.<proto>.<name></name></proto></service> 三元组(例如 _sipfederationtls._tcp.example.com)。因此,要检查多个服务前缀,必须显式循环调用该函数。
以下是一个结构清晰、生产就绪的实现方案:
func srvRecords(domain string) {
fmt.Printf("\n[+] SRV Records\n")
services := []string{"sipfederationtls", "autodiscover", "VLMCS"}
protocol := "tcp"
for _, service := range services {
cname, addrs, err := net.LookupSRV(service, protocol, domain)
if err != nil {
fmt.Printf("[!] SRV lookup for _%s._%s.%s failed: %v\n", service, protocol, domain, err)
continue // 跳过失败项,继续下一个服务
}
fmt.Printf("✓ Service: _%s._%s.%s → Canonical name: %s\n", service, protocol, domain, cname)
if len(addrs) == 0 {
fmt.Printf(" → No records returned.\n")
continue
}
for i, r := range addrs {
// 推荐使用 range 遍历切片,避免手动索引和越界风险
fmt.Printf(" [%d] Target: %s:%d | Priority: %d | Weight: %d\n",
i+1, r.Target, r.Port, r.Priority, r.Weight)
}
}
}
关键要点说明:
- ✅ 使用切片
[]string存储待查服务名,便于扩展和维护; - ✅ 每次
LookupSRV调用独立封装,错误仅影响当前服务,不影响后续查询; - ✅ 使用
range addrs替代传统for i := 0; i ,更符合 Go 惯例,且自动处理空切片; - ✅ 输出中明确标注服务全称(
_%s._%s.%s),便于用户理解 DNS 查询路径; - ⚠️ 注意:
LookupSRV返回的cname是规范域名(canonical name),通常为查询域名本身或其 CNAME 目标,实际解析仍以addresses为准; - ⚠️ 若目标域名不支持某服务(如无
_autodiscover._tcp.google.com记录),会返回err != nil,此时addrs为nil,务必判空再遍历。
最后提醒:DNS 查询受网络、本地 resolver 配置及目标域策略影响,建议在生产环境中添加超时控制(可通过 net.Resolver 自定义并设置 Timeout 字段),但标准 net.LookupSRV 使用系统默认 resolver,如需精细控制,请升级至自定义 net.Resolver 实例。










