【golang】网络数据包捕获库 gopacket

打印 上一主题 下一主题

主题 1650|帖子 1650|积分 4950

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
详解 github.com/google/gopacket/pcap 包

github.com/google/gopacket/pcap 是 Go 语言中一个强大的网络数据包捕获库,它是 gopacket 项目标一部分,提供了对 libpcap(Linux/Unix)和 WinPcap(Windows)的 Go 语言绑定,用于及时网络数据包捕获和分析。
核心功能


  • 及时网络数据包捕获
  • 过滤网络流量(BPF过滤器)
  • 读取息争析pcap文件
  • 统计网络接口信息
基本使用

1. 安装

  1. go get github.com/google/gopacket
  2. go get github.com/google/gopacket/pcap
复制代码
2. 获取网络接口列表

  1. devices, err := pcap.FindAllDevs()
  2. if err != nil {
  3.     log.Fatal(err)
  4. }
  5. for _, device := range devices {
  6.     fmt.Printf("Device: %s\n", device.Name)
  7.     fmt.Printf("Description: %s\n", device.Description)
  8.     fmt.Printf("Flags: %d\n", device.Flags)
  9.     for _, address := range device.Addresses {
  10.         fmt.Printf("\tIP: %s\n", address.IP)
  11.         fmt.Printf("\tNetmask: %s\n", address.Netmask)
  12.     }
  13. }
复制代码
3. 打开网络接口进行捕获

  1. handle, err := pcap.OpenLive(
  2.     "eth0",    // 接口名
  3.     65536,     // 最大包长度
  4.     true,      // 是否启用混杂模式
  5.     pcap.BlockForever, // 超时时间
  6. )
  7. if err != nil {
  8.     log.Fatal(err)
  9. }
  10. defer handle.Close()
复制代码
4. 设置BPF过滤器

  1. err = handle.SetBPFFilter("tcp and port 80")
  2. if err != nil {
  3.     log.Fatal(err)
  4. }
复制代码
5. 读取数据包

  1. packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
  2. for packet := range packetSource.Packets() {
  3.     // 处理每个数据包
  4.     fmt.Println(packet)
  5. }
复制代码
高级功能

1. 剖析数据包

  1. // 解析以太网层
  2. ethernetLayer := packet.Layer(layers.LayerTypeEthernet)
  3. if ethernetLayer != nil {
  4.     ethernetPacket, _ := ethernetLayer.(*layers.Ethernet)
  5.     fmt.Println("Source MAC: ", ethernetPacket.SrcMAC)
  6.     fmt.Println("Destination MAC: ", ethernetPacket.DstMAC)
  7. }
  8. // 解析IP层
  9. ipLayer := packet.Layer(layers.LayerTypeIPv4)
  10. if ipLayer != nil {
  11.     ip, _ := ipLayer.(*layers.IPv4)
  12.     fmt.Println("Source IP: ", ip.SrcIP)
  13.     fmt.Println("Destination IP: ", ip.DstIP)
  14. }
  15. // 解析TCP层
  16. tcpLayer := packet.Layer(layers.LayerTypeTCP)
  17. if tcpLayer != nil {
  18.     tcp, _ := tcpLayer.(*layers.TCP)
  19.     fmt.Println("Source Port: ", tcp.SrcPort)
  20.     fmt.Println("Destination Port: ", tcp.DstPort)
  21. }
复制代码
2. 写入pcap文件

  1. handle, err := pcap.OpenDead(layers.LinkTypeEthernet, 65536)
  2. if err != nil {
  3.     log.Fatal(err)
  4. }
  5. defer handle.Close()
  6. writer, err := pcap.NewWriter(handle, "output.pcap")
  7. if err != nil {
  8.     log.Fatal(err)
  9. }
  10. defer writer.Close()
  11. // 创建并写入数据包
  12. // ... (创建数据包代码)
  13. err = writer.WritePacket(packet.Metadata().CaptureInfo, packet.Data())
  14. if err != nil {
  15.     log.Fatal(err)
  16. }
复制代码
3. 读取pcap文件

  1. handle, err := pcap.OpenOffline("input.pcap")
  2. if err != nil {
  3.     log.Fatal(err)
  4. }
  5. defer handle.Close()
  6. packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
  7. for packet := range packetSource.Packets() {
  8.     fmt.Println(packet)
  9. }
复制代码
性能优化


  • 重用缓冲区:淘汰内存分配
    1. var buf [4096]byte
    2. for {
    3.     data, ci, err := handle.ReadPacketData()
    4.     if err != nil {
    5.         continue
    6.     }
    7.     copy(buf[:], data)
    8.     // 处理数据
    9. }
    复制代码
  • 零拷贝处理:直接操作原始数据
    1. packet := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.Default)
    复制代码
  • 并发处理:使用多个goroutine处理数据包
    1. packets := make(chan gopacket.Packet, 1000)
    2. go func() {
    3.     for packet := range packetSource.Packets() {
    4.         packets <- packet
    5.     }
    6.     close(packets)
    7. }()
    8. for i := 0; i < runtime.NumCPU(); i++ {
    9.     go func() {
    10.         for packet := range packets {
    11.             // 处理数据包
    12.         }
    13.     }()
    14. }
    复制代码
常见题目


  • 权限题目:需要root或管理员权限才气捕获网络数据包
  • 接口不可用:确保接口名称精确且处于运动状态
  • 过滤器语法错误:BPF过滤器需要精确语法
  • 内存走漏:确保及时关闭handle和释放资源
实际应用示例

简单的HTTP哀求捕获

  1. handle, err := pcap.OpenLive("eth0", 1600, true, pcap.BlockForever)if err != nil {    log.Fatal(err)}defer handle.Close()err = handle.SetBPFFilter("tcp and port 80")
  2. if err != nil {
  3.     log.Fatal(err)
  4. }
  5. packetSource := gopacket.NewPacketSource(handle, handle.LinkType())for packet := range packetSource.Packets() {    appLayer := packet.ApplicationLayer()    if appLayer != nil {        payload := appLayer.Payload()        if bytes.Contains(payload, []byte("HTTP")) {            fmt.Printf("%s\n", payload)        }    }}
复制代码
pcap 包是 Go 中网络监控和安全工具开发的基础,结合 gopacket 的其他组件可以构建强大的网络分析工具。


使用 github.com/google/gopacket/pcap 剖析 DNS 哀求和响应

github.com/google/gopacket/pcap 可以捕获息争析 DNS 哀求和响应,但需要结合 gopacket/layers 包中的 DNS 层剖析功能。
1. 基本 DNS 剖析设置

首先需要导入必要的包:
  1. import (
  2.     "github.com/google/gopacket"
  3.     "github.com/google/gopacket/layers" // 包含DNS层定义
  4.     "github.com/google/gopacket/pcap"
  5. )
复制代码
2. 捕获 DNS 流量的配置

方法一:捕获全部DNS流量(端口53)

  1. handle, err := pcap.OpenLive("eth0", 1600, true, pcap.BlockForever)
  2. if err != nil {
  3.     panic(err)
  4. }
  5. defer handle.Close()
  6. // 设置BPF过滤器捕获DNS流量(UDP和TCP端口53)
  7. err = handle.SetBPFFilter("udp port 53 or tcp port 53")
  8. if err != nil {
  9.     panic(err)
  10. }
复制代码
方法二:区分哀求和响应

  1. // 捕获发往DNS服务器的请求
  2. err = handle.SetBPFFilter("dst port 53")
  3. // 或者捕获来自DNS服务器的响应
  4. err = handle.SetBPFFilter("src port 53")
复制代码
3. 剖析DNS数据包

  1. packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
  2. for packet := range packetSource.Packets() {
  3.     // 检查是否包含DNS层
  4.     dnsLayer := packet.Layer(layers.LayerTypeDNS)
  5.     if dnsLayer != nil {
  6.         dns, _ := dnsLayer.(*layers.DNS)
  7.         analyzeDNS(dns) // 自定义解析函数
  8.     }
  9. }
复制代码
4. 详细DNS剖析函数

  1. func analyzeDNS(dns *layers.DNS) {
  2.     // 判断是请求还是响应
  3.     if dns.QR {
  4.         fmt.Println("[DNS Response]")
  5.     } else {
  6.         fmt.Println("[DNS Query]")
  7.     }
  8.   
  9.     // 打印基本信息
  10.     fmt.Printf("ID: %d, OpCode: %s, RecursionDesired: %t\n",
  11.         dns.ID, dns.OpCode, dns.RD)
  12.   
  13.     // 解析问题部分(查询的问题)
  14.     for _, question := range dns.Questions {
  15.         fmt.Printf("Query: %s (Type: %s, Class: %s)\n",
  16.             string(question.Name),
  17.             question.Type,
  18.             question.Class)
  19.     }
  20.   
  21.     // 解析回答部分(响应的资源记录)
  22.     for _, answer := range dns.Answers {
  23.         fmt.Printf("Answer: %s -> %s (Type: %s, TTL: %d)\n",
  24.             string(answer.Name),
  25.             getAnswerValue(answer), // 自定义函数获取值
  26.             answer.Type,
  27.             answer.TTL)
  28.     }
  29.   
  30.     // 解析权威名称服务器部分
  31.     for _, ns := range dns.Authorities {
  32.         fmt.Printf("Authority: %s -> %s\n",
  33.             string(ns.Name),
  34.             getAnswerValue(ns))
  35.     }
  36.   
  37.     // 解析附加记录部分
  38.     for _, extra := range dns.Additionals {
  39.         fmt.Printf("Additional: %s -> %s\n",
  40.             string(extra.Name),
  41.             getAnswerValue(extra))
  42.     }
  43. }
  44. // 辅助函数:根据类型获取DNS记录的值
  45. func getAnswerValue(answer layers.DNSResourceRecord) string {
  46.     switch answer.Type {
  47.     case layers.DNSTypeA:
  48.         return answer.IP.String()
  49.     case layers.DNSTypeAAAA:
  50.         return answer.IP.String()
  51.     case layers.DNSTypeCNAME:
  52.         return string(answer.CNAME)
  53.     case layers.DNSTypeMX:
  54.         return fmt.Sprintf("%s (pref %d)", string(answer.MX), answer.Preference)
  55.     case layers.DNSTypeNS:
  56.         return string(answer.NS)
  57.     case layers.DNSTypeTXT:
  58.         return string(answer.TXT)
  59.     case layers.DNSTypeSOA:
  60.         return fmt.Sprintf("MName: %s, RName: %s", string(answer.SOA.MName), string(answer.SOA.RName))
  61.     default:
  62.         return fmt.Sprintf("[Unhandled Type %d]", answer.Type)
  63.     }
  64. }
复制代码
5. 处理TCP DNS流量

DNS通常使用UDP,但大响应大概使用TCP:
  1. // 在分析函数中添加TCP处理
  2. tcpLayer := packet.Layer(layers.LayerTypeTCP)
  3. if tcpLayer != nil {
  4.     tcp, _ := tcpLayer.(*layers.TCP)
  5.     if tcp.SYN {
  6.         // TCP握手开始
  7.     } else if len(tcp.Payload) > 0 {
  8.         // 尝试解析TCP负载中的DNS
  9.         dns := &layers.DNS{}
  10.         err := dns.DecodeFromBytes(tcp.Payload, gopacket.NilDecodeFeedback)
  11.         if err == nil {
  12.             analyzeDNS(dns)
  13.         }
  14.     }
  15. }
复制代码
6. 完整示例:DNS监控工具

  1. package main
  2. import (
  3.     "fmt"
  4.     "log"
  5.     "time"
  6.   
  7.     "github.com/google/gopacket"
  8.     "github.com/google/gopacket/layers"
  9.     "github.com/google/gopacket/pcap"
  10. )
  11. func main() {
  12.     // 1. 获取网络设备
  13.     devices, err := pcap.FindAllDevs()
  14.     if err != nil {
  15.         log.Fatal(err)
  16.     }
  17.   
  18.     // 打印可用设备
  19.     fmt.Println("Available devices:")
  20.     for _, dev := range devices {
  21.         fmt.Printf("- %s", dev.Name)
  22.         if dev.Description != "" {
  23.             fmt.Printf(" (%s)", dev.Description)
  24.         }
  25.         fmt.Println()
  26.     }
  27.   
  28.     // 2. 打开设备
  29.     var deviceName = "eth0" // 根据实际情况修改
  30.     var snapshotLen int32 = 1024
  31.     var promiscuous = true
  32.     var timeout = 30 * time.Second
  33.   
  34.     handle, err := pcap.OpenLive(deviceName, snapshotLen, promiscuous, timeout)
  35.     if err != nil {
  36.         log.Fatal(err)
  37.     }
  38.     defer handle.Close()
  39.   
  40.     // 3. 设置过滤器
  41.     var filter = "udp port 53 or tcp port 53"
  42.     err = handle.SetBPFFilter(filter)
  43.     if err != nil {
  44.         log.Fatal(err)
  45.     }
  46.   
  47.     // 4. 处理数据包
  48.     packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
  49.     for packet := range packetSource.Packets() {
  50.         processPacket(packet)
  51.     }
  52. }
  53. func processPacket(packet gopacket.Packet) {
  54.     // 检查DNS层
  55.     dnsLayer := packet.Layer(layers.LayerTypeDNS)
  56.     if dnsLayer != nil {
  57.         dns, _ := dnsLayer.(*layers.DNS)
  58.         printDNSInfo(dns)
  59.     }
  60.   
  61.     // 检查TCP DNS
  62.     tcpLayer := packet.Layer(layers.LayerTypeTCP)
  63.     if tcpLayer != nil {
  64.         tcp, _ := tcpLayer.(*layers.TCP)
  65.         if len(tcp.Payload) > 0 && (tcp.SrcPort == 53 || tcp.DstPort == 53) {
  66.             dns := &layers.DNS{}
  67.             err := dns.DecodeFromBytes(tcp.Payload, gopacket.NilDecodeFeedback)
  68.             if err == nil {
  69.                 printDNSInfo(dns)
  70.             }
  71.         }
  72.     }
  73. }
  74. func printDNSInfo(dns *layers.DNS) {
  75.     // 打印基本信息
  76.     direction := "Query"
  77.     if dns.QR {
  78.         direction = "Response"
  79.     }
  80.   
  81.     fmt.Printf("\n=== %s ===\n", direction)
  82.     fmt.Printf("Transaction ID: 0x%x\n", dns.ID)
  83.     fmt.Printf("Flags: %s (QR: %t, OpCode: %s, AA: %t, TC: %t, RD: %t, RA: %t, Z: %d, RCode: %s)\n",
  84.         dns.Flags, dns.QR, dns.OpCode, dns.AA, dns.TC, dns.RD, dns.RA, dns.Z, dns.ResponseCode)
  85.   
  86.     // 打印问题部分
  87.     if len(dns.Questions) > 0 {
  88.         fmt.Println("\nQuestions:")
  89.         for _, q := range dns.Questions {
  90.             fmt.Printf("- %s (Type: %s, Class: %s)\n",
  91.                 string(q.Name), q.Type, q.Class)
  92.         }
  93.     }
  94.   
  95.     // 打印回答部分
  96.     if len(dns.Answers) > 0 {
  97.         fmt.Println("\nAnswers:")
  98.         for _, a := range dns.Answers {
  99.             fmt.Printf("- %s: %s (Type: %s, TTL: %d)\n",
  100.                 string(a.Name), getDNSRecordValue(a), a.Type, a.TTL)
  101.         }
  102.     }
  103.   
  104.     // 打印权威部分
  105.     if len(dns.Authorities) > 0 {
  106.         fmt.Println("\nAuthorities:")
  107.         for _, a := range dns.Authorities {
  108.             fmt.Printf("- %s: %s (Type: %s, TTL: %d)\n",
  109.                 string(a.Name), getDNSRecordValue(a), a.Type, a.TTL)
  110.         }
  111.     }
  112.   
  113.     // 打印附加部分
  114.     if len(dns.Additionals) > 0 {
  115.         fmt.Println("\nAdditionals:")
  116.         for _, a := range dns.Additionals {
  117.             fmt.Printf("- %s: %s (Type: %s, TTL: %d)\n",
  118.                 string(a.Name), getDNSRecordValue(a), a.Type, a.TTL)
  119.         }
  120.     }
  121. }
  122. func getDNSRecordValue(r layers.DNSResourceRecord) string {
  123.     switch r.Type {
  124.     case layers.DNSTypeA:
  125.         return r.IP.String()
  126.     case layers.DNSTypeAAAA:
  127.         return r.IP.String()
  128.     case layers.DNSTypeCNAME:
  129.         return string(r.CNAME)
  130.     case layers.DNSTypeMX:
  131.         return fmt.Sprintf("%s (pref %d)", string(r.MX), r.Preference)
  132.     case layers.DNSTypeNS:
  133.         return string(r.NS)
  134.     case layers.DNSTypePTR:
  135.         return string(r.PTR)
  136.     case layers.DNSTypeSOA:
  137.         return fmt.Sprintf("MName: %s, RName: %s", string(r.SOA.MName), string(r.SOA.RName))
  138.     case layers.DNSTypeTXT:
  139.         return string(r.TXT)
  140.     case layers.DNSTypeSRV:
  141.         return fmt.Sprintf("Target: %s, Port: %d, Priority: %d, Weight: %d",
  142.             string(r.SRV.Name), r.SRV.Port, r.SRV.Priority, r.SRV.Weight)
  143.     default:
  144.         return fmt.Sprintf("[Type %d Data]", r.Type)
  145.     }
  146. }
复制代码
注意事项


  • 权限要求:需要root或管理员权限才气捕获网络数据包
  • 性能考虑:高流量环境下大概需要优化处理逻辑
  • DNS over HTTPS/TLS:这种方法无法剖析DoH/DoT加密的DNS流量
  • EDNS扩展:假如需要剖析EDNS扩展信息,需要额外处理OPT记录
通过这种组合使用 pcap 和 layers 包,你可以构建功能强大的DNS监控和分析工具,可以或许详细剖析DNS协议的各种细节。





从 layers.DNS 中获取域名和对应 IP 的方法

要从 layers.DNS 数据包中提取域名和对应的 IP 地点,你需要检查 DNS 响应中的回答部分 (Answers)。以下是详细方法和示例代码:
基本方法


  • 检查 DNS 响应:确保是 DNS 响应 (QR == true)
  • 遍历回答记录:检查 Answers 切片
  • 过滤 A 和 AAAA 记录:获取 IPv4 和 IPv6 地点
  • 剖析记录数据:将二进制数据转换为可读格式
示例代码

  1. package main
  2. import (
  3.     "fmt"
  4.     "github.com/google/gopacket"
  5.     "github.com/google/gopacket/layers"
  6.     "github.com/google/gopacket/pcap"
  7.     "net"
  8. )
  9. func main() {
  10.     // 打开网络接口
  11.     handle, err := pcap.OpenLive("eth0", 1600, true, pcap.BlockForever)
  12.     if err != nil {
  13.         panic(err)
  14.     }
  15.     defer handle.Close()
  16.   
  17.     // 设置过滤器只捕获 DNS 响应
  18.     err = handle.SetBPFFilter("udp and port 53")
  19.     if err != nil {
  20.         panic(err)
  21.     }
  22.   
  23.     // 开始捕获数据包
  24.     packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
  25.     for packet := range packetSource.Packets() {
  26.         // 检查是否包含 DNS 层
  27.         dnsLayer := packet.Layer(layers.LayerTypeDNS)
  28.         if dnsLayer == nil {
  29.             continue
  30.         }
  31.       
  32.         dns, _ := dnsLayer.(*layers.DNS)
  33.       
  34.         // 只处理响应包
  35.         if !dns.QR {
  36.             continue
  37.         }
  38.       
  39.         // 遍历所有回答记录
  40.         for _, answer := range dns.Answers {
  41.             var ip string
  42.          
  43.             // 根据记录类型处理
  44.             switch answer.Type {
  45.             case layers.DNSTypeA:
  46.                 // IPv4 地址
  47.                 if len(answer.Data) == net.IPv4len {
  48.                     ip = net.IP(answer.Data).String()
  49.                 }
  50.             case layers.DNSTypeAAAA:
  51.                 // IPv6 地址
  52.                 if len(answer.Data) == net.IPv6len {
  53.                     ip = net.IP(answer.Data).String()
  54.                 }
  55.             default:
  56.                 continue // 跳过非IP记录
  57.             }
  58.          
  59.             if ip != "" {
  60.                 fmt.Printf("域名: %s, IP: %s, TTL: %d\n",
  61.                     string(answer.Name), ip, answer.TTL)
  62.             }
  63.         }
  64.     }
  65. }
复制代码
更完整的处理函数

下面是一个更完整的函数,可以处理各种情况:
  1. func extractDomainsAndIPs(dns *layers.DNS) map[string][]string {
  2.     result := make(map[string][]string)
  3.   
  4.     // 首先收集所有查询的域名
  5.     var domains []string
  6.     for _, q := range dns.Questions {
  7.         domains = append(domains, string(q.Name))
  8.     }
  9.   
  10.     // 处理回答记录
  11.     for _, answer := range dns.Answers {
  12.         domain := string(answer.Name)
  13.         var ip string
  14.       
  15.         switch answer.Type {
  16.         case layers.DNSTypeA:
  17.             if len(answer.Data) >= net.IPv4len {
  18.                 ip = net.IP(answer.Data).String()
  19.             }
  20.         case layers.DNSTypeAAAA:
  21.             if len(answer.Data) >= net.IPv6len {
  22.                 ip = net.IP(answer.Data).String()
  23.             }
  24.         case layers.DNSTypeCNAME:
  25.             // 处理CNAME记录
  26.             cname := string(answer.Data)
  27.             result[domain] = append(result[domain], "CNAME: "+cname)
  28.             continue
  29.         default:
  30.             continue
  31.         }
  32.       
  33.         if ip != "" {
  34.             result[domain] = append(result[domain], ip)
  35.         }
  36.     }
  37.   
  38.     return result
  39. }
复制代码
处理特别情况


  • CNAME 记录:假如遇到 CNAME 记录,你大概需要继续查找对应的 A/AAAA 记录
  • 多IP情况:一个域名大概对应多个IP地点
  • 压缩域名:layers.DNS 已经自动处理了DNS名称压缩
性能优化发起


  • 预分配切片:假如你知道大致数量,可以预分配切片大小
  • 重用缓冲区:在处理大量数据包时重用缓冲区
  • 并行处理:使用 goroutine 池处理数据包
完整示例(带CNAME处理)

  1. func processDNSResponse(dns *layers.DNS) {
  2.     if !dns.QR {
  3.         return // 不是响应包
  4.     }
  5.   
  6.     // 创建域名到IP的映射
  7.     domainToIPs := make(map[string][]string)
  8.     cnameMap := make(map[string]string)
  9.   
  10.     // 首先处理CNAME记录
  11.     for _, answer := range dns.Answers {
  12.         if answer.Type == layers.DNSTypeCNAME {
  13.             domain := string(answer.Name)
  14.             cname := string(answer.Data)
  15.             cnameMap[domain] = cname
  16.         }
  17.     }
  18.   
  19.     // 然后处理A和AAAA记录
  20.     for _, answer := range dns.Answers {
  21.         var ip string
  22.         domain := string(answer.Name)
  23.       
  24.         switch answer.Type {
  25.         case layers.DNSTypeA:
  26.             if len(answer.Data) >= net.IPv4len {
  27.                 ip = net.IP(answer.Data).String()
  28.             }
  29.         case layers.DNSTypeAAAA:
  30.             if len(answer.Data) >= net.IPv6len {
  31.                 ip = net.IP(answer.Data).String()
  32.             }
  33.         default:
  34.             continue
  35.         }
  36.       
  37.         if ip != "" {
  38.             // 检查是否有CNAME链
  39.             finalDomain := domain
  40.             for {
  41.                 if cname, exists := cnameMap[finalDomain]; exists {
  42.                     finalDomain = cname
  43.                 } else {
  44.                     break
  45.                 }
  46.             }
  47.             domainToIPs[finalDomain] = append(domainToIPs[finalDomain], ip)
  48.         }
  49.     }
  50.   
  51.     // 打印结果
  52.     for domain, ips := range domainToIPs {
  53.         fmt.Printf("域名: %s\n", domain)
  54.         for _, ip := range ips {
  55.             fmt.Printf("  IP: %s\n", ip)
  56.         }
  57.     }
  58. }
复制代码
通过这些方法,你可以有用地从 layers.DNS 数据包中提取域名和对应的IP地点,并处理各种DNS记录类型和特别情况。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

慢吞云雾缓吐愁

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表