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

前端 未结 4 1217
野性不改
野性不改 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 GetHostEntryAsync(string host, string dns = null)
        {
            if (string.IsNullOrEmpty(host))
            {
                return null;
            }
    
            //Check if any cached result exists
            Tuple key = new Tuple(host, dns);
            if (NetHelper._dnsCache.TryGetValue(key, out Tuple 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 addresses = new List();
                        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(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;
    
        }
    

提交回复
热议问题