解析主机名时防止异常

Posted

技术标签:

【中文标题】解析主机名时防止异常【英文标题】:Preventing Exception when resolving Hostname 【发布时间】:2017-06-20 14:05:04 【问题描述】:

我正在尝试制作一个扫描网络以查找 ARP 请求并列出所有现有网络设备的应用。目前我使用SharpPcapPacketDoNet

当根据给定的 IP 解析主机名时,我在解析“未知”主机时得到一个 SocketException。所以我把它放在了try/catch中。由于我认为这是一种不好的风格(忽略异常),我正在寻找不同的解决方案。

这是一些代码:

// Button for scanning the network
private void btnStartScanningForClients_Click(object sender, RoutedEventArgs e)

    // Check for correct interface
    // [...]

    // Start scanning process
    if (!this.netWorkItOut.Startet)
    
        // Dis-/Enable visual controls
        // [...]

        // Start scanning
        var index = this.cbNetworkInterface.SelectedIndex
        this.netWorkItOut.StartDevice(index);
        this.netWorkItOut.Scanner.StartScanningNetwork(resolveHostnames);
    

这是控制对象,它持有扫描器、处理事件、接受数据包并将它们放入队列中

public void StartDevice(int deviceIndex)

    this.Startet = true;
    // [...]
    this.Device = WinPcapDeviceList.Instance[deviceIndex];

    // Activate Scanner
    this.Scanner = new Scanner(this.DeviceInfo);

    // Subscribe Events
    // [...]

    this.Device.Open(DeviceMode.Promiscuous, 1);
    this.Device.Filter = "(arp || ip || ip6)";

    this.Device.OnPacketArrival += device_OnPacketArrival;
    this.Device.StartCapture();


private void device_OnPacketArrival(object sender, CaptureEventArgs e)

    //PacketDoNet
    Packet packet;

    try
     packet = Packet.ParsePacket(LinkLayers.Ethernet, e.Packet.Data); 
    catch (Exception)
     return;  

    if (packet is EthernetPacket)
    
        var arp = ARPPacket.GetEncapsulated(packet);

        if (arp != null)
        
            if (this.Scanner.Started)
            
                lock (this.Scanner.PacketQueueARP)
                
                    this.Scanner.PacketQueueARP.Add(arp);
                
            
        
    

这是控制对象和扫描器类。扫描器类处理 ARP 请求并解析主机名

public void StartScanningNetwork(bool resolveHostnames)

    // [...]
    this.ResolveHostnames = resolveHostnames;

    // start worker to listen for ARP packets
    this.workerARP = new Thread(WorkerARP);
    this.workerARP.Name = "Scanner thread (ARP)";
    this.workerARP.Start();

    this.Started = true;


private void WorkerARP()

    List<IPAddress> processedIps = new List<IPAddress>();

    // copy packets from storage queue to thread queue for processing
    while (Started)
    
        // [...]

        if (this.threadQueueARP.Count > 0)
        
            foreach (var packet in this.threadQueueARP)
            
                // [...]

                if (!processedIps.Contains(ip))
                
                    // [...]

                    if (this.ResolveHostnames)
                    
                        var resolveHostnamesTask = Task.Factory.StartNew(ResolveHostnamesWorker, ip);
                    
                
                // [...]
            

            // [...]
        
        // [...]
    



private void ResolveHostnamesWorker(object data)

    if (data is IPAddress)
    
        var ip = (IPAddress)data;
        var hostname = "";

        try
        
            hostname = Dns.GetHostEntry(ip).HostName;
        
        catch  

        // Raise Event for hostname resolved
    

这一切都与hostname = Dns.GetHostEntry(ip).HostName这一行有关

那么:通过 Dns.GetHostEntry() 解析 HostEntry 时,如何避免使用 try/catch?如果没有已知主机,是否有一个函数只返回null

提前致谢!

【问题讨论】:

【参考方案1】:

由于Dns 类的代码在https://github.com/Microsoft/referencesource/blob/master/System/net/System/Net/DNS.cs 下可用,您只能从那里使用相关部分。 (当然你要检查你的项目是否可以使用MIT许可)

实现是在抛出异常的方法InternalGetHostByAddress 中。如果查询成功,您可以返回一个值(bool、enum ...)来提供信息。

【讨论】:

【参考方案2】:

据我所知,没有像 TryGetHostName() 这样不会引发异常的方法。

但在我看来,捕获异常是很容易理解的。因此,您应该将捕获异常限制在您期望的范围内:

private void ResolveHostnamesWorker(object data)

    if (data is IPAddress)
    
        var ip = (IPAddress)data;
        var hostname = "";

        try
        
            hostname = Dns.GetHostEntry(ip).HostName;
        
        catch(SocketException socketException)
        
            // maybe limit handling based on data in socketException and
            // call throw; to rethrow exception if not the expected one
        

    // Raise Event for hostname resolved

【讨论】:

因为捕捉异常是一个耗时的过程,这应该是我最后的选择。但如果似乎没有其他方法,我必须这样做。谢谢你的回答!

以上是关于解析主机名时防止异常的主要内容,如果未能解决你的问题,请参考以下文章

DNS域名解析的过程

使用 postgres_fdw 创建外部表时翻译主机名时出错

DNS客户端测试工具详解

SQL Server 查询:主机名

Linux DNS 解析与配置 nslookup使用 与 /etc/resolv.conf文件的配置

防止 Dns.GetHostAddresses 抛出异常?