Specify which DNS servers to use to resolve hostnames in .NET

前端 未结 4 1192
野性不改
野性不改 2020-12-09 10:46

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

相关标签:
4条回答
  • 2020-12-09 11:13

    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)
                                {
                                    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;
    
        }
    
    0 讨论(0)
  • 2020-12-09 11:30

    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/

    0 讨论(0)
  • 2020-12-09 11:35

    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.

    0 讨论(0)
  • 2020-12-09 11:38

    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

    0 讨论(0)
提交回复
热议问题