简体   繁体   中英

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.

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.

No this cannot be done with the .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.

In order to get this to work, you'd have to change the DNS servers associated with the network adapter's address.

Tested in .net standard:

    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. See the second code sample at 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 run --dns=1.2.3.4 repo/myimage command.exe

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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