指定用于解析 .NET 中的主机名的 DNS 服务器

Posted

技术标签:

【中文标题】指定用于解析 .NET 中的主机名的 DNS 服务器【英文标题】:Specify which DNS servers to use to resolve hostnames in .NET 【发布时间】:2010-11-21 21:26:14 【问题描述】:

我想知道是否有任何方法可以强制 System.Net.Dns 类使用一组自定义 DNS 服务器解析主机名,而不是使用与主网络连接关联的那些。

我怀疑这只能使用像 DnDns (http://dndns.codeplex.com) 这样的自定义库,但我希望这可以在 .NET 中完成。

【问题讨论】:

使用dnsclient.michaco.net,它是开源的,也适用于.net core 【参考方案1】:

不,这不能通过 .Net 框架完成。 Dns.Resolve 方法依赖于内部 Win32 API,后者依次通过与网络连接关联的 DNS 服务器。

为了使其正常工作,您必须更改与网络适配器地址关联的 DNS 服务器。

【讨论】:

【参考方案2】:

在 .net 标准中测试:

    public static async Task<IPHostEntry> GetHostEntryAsync(string host, string dns = null)
    
        if (string.IsNullOrEmpty(host))
        
            return null;
        

        //Check if any cached result exists
        Tuple<string, string> key = new Tuple<string, string>(host, dns);
        if (NetHelper._dnsCache.TryGetValue(key, out Tuple<IPHostEntry, DateTime> record) && record.Item2 > DateTime.Now)
        
            return record.Item1;
        

        //Check dns server's address or port
        IPHostEntry result = null;
        int dnsPort;
        if (dns != null)
        
            string[] blocks = dns.Split(':');
            if (blocks.Length == 2 && int.TryParse(blocks[1], out dnsPort))//dns is ip v4
            
                dns = blocks[0];
            
            else if (blocks.Length == 9 && int.TryParse(blocks[8], out dnsPort))//dns is ip v6
            
                blocks[0] = blocks[0].TrimStart('[');
                blocks[7] = blocks[7].TrimStart(']');
                dns = string.Format("0:1:2:3:4:5:6:7", blocks);
            
            else
            
                dnsPort = 53;
            
        
        else
        
            dnsPort = 53;
        

        //Check if host is ip address
        if (host[0] == '[' && host[host.Length - 1] == ']')//IPV6 address
        
            host = host.Substring(1, host.Length - 2);
        
        if (IPAddress.TryParse(host, out IPAddress address))
        
            result = new IPHostEntry  AddressList = new IPAddress[]  address  ;
        
        else if (string.IsNullOrEmpty(dns))
        
            result = await Dns.GetHostEntryAsync(host);
        
        else
        
            #region Resolve with customized dns server
            IPAddress dnsAddr;
            if (!IPAddress.TryParse(dns, out dnsAddr))
            
                throw new ArgumentException("The dns host must be ip address.", nameof(dns));
            
            using (MemoryStream ms = new MemoryStream())
            
                Random rnd = new Random();
                //About the dns message:http://www.ietf.org/rfc/rfc1035.txt

                //Write message header.
                ms.Write(new byte[] 
                    (byte)rnd.Next(0, 0xFF),(byte)rnd.Next(0, 0xFF),
                    0x01,
                    0x00,
                    0x00,0x01,
                    0x00,0x00,
                    0x00,0x00,
                    0x00,0x00
                , 0, 12);

                //Write the host to query.
                foreach (string block in host.Split('.'))
                
                    byte[] data = Encoding.UTF8.GetBytes(block);
                    ms.WriteByte((byte)data.Length);
                    ms.Write(data, 0, data.Length);
                
                ms.WriteByte(0);//The end of query, muest 0(null string)

                //Query type:A
                ms.WriteByte(0x00);
                ms.WriteByte(0x01);

                //Query class:IN
                ms.WriteByte(0x00);
                ms.WriteByte(0x01);

                Socket socket = new Socket(SocketType.Dgram, ProtocolType.Udp);
                try
                
                    //send to dns server
                    byte[] buffer = ms.ToArray();
                    while (socket.SendTo(buffer, 0, buffer.Length, SocketFlags.None, new IPEndPoint(dnsAddr, dnsPort)) < buffer.Length) ;
                    buffer = new byte[0x100];
                    EndPoint ep = socket.LocalEndPoint;
                    int num = socket.ReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref ep);

                    //The response message has the same header and question structure, so we move index to the answer part directly.
                    int index = (int)ms.Length;
                    //Parse response records.
                    void SkipName()
                    
                        while (index < num)
                        
                            int length = buffer[index++];
                            if (length == 0)
                            
                                return;
                            
                            else if (length > 191)
                            
                                return;
                            
                            index += length;
                        
                    

                    List<IPAddress> addresses = new List<IPAddress>();
                    while (index < num)
                    
                        SkipName();//Seems the name of record is useless in this scense, so we just needs to get the next index after name.
                        byte type = buffer[index += 2];
                        index += 7;//Skip class and ttl

                        int length = buffer[index++] << 8 | buffer[index++];//Get record data's length

                        if (type == 0x01)//A record
                        
                            if (length == 4)//Parse record data to ip v4, this is what we need.
                            
                                addresses.Add(new IPAddress(new byte[]  buffer[index], buffer[index + 1], buffer[index + 2], buffer[index + 3] ));
                            
                        
                        index += length;
                    
                    result = new IPHostEntry  AddressList = addresses.ToArray() ;
                
                finally
                
                    socket.Dispose();
                
            
            #endregion
        

        //Cache and return result
        NetHelper._dnsCache[key] = new Tuple<IPHostEntry, DateTime>(result, DateTime.Now.AddMinutes(15));

        #pragma warning disable CS4014
        Task.Run(async () =>
        
            await Task.Delay((int)TimeSpan.FromMinutes(15).TotalMilliseconds);
            NetHelper._dnsCache.Remove(key);
        );
        #pragma warning restore CS4014
        return result;

    

【讨论】:

@dexiang 能否解释一下 SkipName() 方法中的“else if (length > 191)”行的作用? @nikola-miljkovic 抱歉,我忘记了。 buffer[index++] 【参考方案3】:

如果您能够在 docker 容器中运行您的应用程序,您可以在启动时提供 dns 服务器 IP 地址。

docker run --dns=1.2.3.4 repo/myimage command.exe

https://docs.docker.com/v17.09/engine/userguide/networking/configure-dns/

【讨论】:

【参考方案4】:

您可以使用“JH Software 的 .NET 的 DNS 客户端”来执行此操作 - 无需更改 DNS 本地服务器。 请参阅http://www.simpledns.com/dns-client-lib.aspx的第二个代码示例

【讨论】:

以上是关于指定用于解析 .NET 中的主机名的 DNS 服务器的主要内容,如果未能解决你的问题,请参考以下文章

DNS

Linux-dns基础知识和BIND的简单配置-1

网络--DNS

Linux学习70 DNS服务解析过程精讲

DNS域名解析服务

DNS-VIEW的原理与配置