简体   繁体   English

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

[英]Specify which DNS servers to use to resolve hostnames in .NET

I'd like to know if there's any way to force the System.Net.Dns class to resolve hostnames using a set of custom DNS servers instead of using the ones that are associated with the main network connection. 我想知道是否有办法强制System.Net.Dns类使用一组自定义DNS服务器来解析主机名,而不是使用与主网络连接相关联的服务器。

I suspect that this is only possible using a custom library like DnDns ( http://dndns.codeplex.com ), but I was hoping this could be done from within .NET. 我怀疑这只能使用像DnDns( http://dndns.codeplex.com )这样的自定义库,但我希望这可以在.NET中完成。

No this cannot be done with the .Net Framework. 不能用.Net Framework做到这一点。 The Dns.Resolve method relies on the internal Win32 APIs which in turn go through the DNS servers associated with the network connection. Dns.Resolve方法依赖于内部Win32 API,后者又通过与网络连接关联的DNS服务器。

In order to get this to work, you'd have to change the DNS servers associated with the network adapter's address. 为了使其工作,您必须更改与网络适配器地址关联的DNS服务器。

Tested in .net standard: 测试.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)
                            {
                                break;
                            }
                            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;

    }

You can do this with "JH Software's DNS Client for .NET" - without changing the DNS local servers. 您可以使用“JH Software的.NET Client for .NET”执行此操作 - 而无需更改DNS本地服务器。 See the second code sample at http://www.simpledns.com/dns-client-lib.aspx 请参阅http://www.simpledns.com/dns-client-lib.aspx上的第二个代码示例

If you're able to run your application inside a docker container you can provide the dns server ip address on start up. 如果您能够在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/ https://docs.docker.com/v17.09/engine/userguide/networking/configure-dns/

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM