C# 版 防止 DNS 污染,获取域名真实 IP 地址

using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
 
namespace TestDnsResolver
{
    class Program
    {
        static void Main(string[] args)
        {
            GFWDnsResolver dnsResolver = GFWDnsResolver.Instance();
            string domain = "www.google.com";
            string ip = dnsResolver.GFWResolve(domain);
            Console.WriteLine(ip + "    " + domain);
            Console.ReadKey();
        }
    }
 
    public class GFWDnsResolver
    {
        private static GFWDnsResolver resolver = null;
         
        private static string DNS_SERVER = "8.8.8.8";
 
        private Encoding coding = Encoding.UTF8;
 
        private bool debug = false;
        private bool cache = false;
 
        private int maxTryTimes = 2;
        private int waitTimes = 3;
 
        private Dictionary<string, string> dnsCache = new Dictionary<string, string>();
 
        string[] blackList = {
            "74.125.127.102", "74.125.155.102", "74.125.39.102", "74.125.39.113",
             "209.85.229.138",
             "128.121.126.139", "159.106.121.75", "169.132.13.103", "192.67.198.6",
             "202.106.1.2", "202.181.7.85", "203.161.230.171", "203.98.7.65",
             "207.12.88.98", "208.56.31.43", "209.145.54.50", "209.220.30.174",
             "209.36.73.33", "211.94.66.147", "213.169.251.35", "216.221.188.182", 
             "216.234.179.13", "243.185.187.39", "37.61.54.158", "4.36.66.178",
             "46.82.174.68", "59.24.3.173", "64.33.88.161", "64.33.99.47",
             "64.66.163.251", "65.104.202.252", "65.160.219.113", "66.45.252.237",                                                                                                                           
             "72.14.205.104", "72.14.205.99", "78.16.49.15", "8.7.198.45", "93.46.8.89"};
 
        public static GFWDnsResolver Instance()
        {
            if (resolver == null)
            {
                resolver = new GFWDnsResolver();
            }
            return resolver;
        }
 
        private GFWDnsResolver() { }
 
        private bool IsBadReply(string ip)
        {
            for (int i = 0; i < blackList.Length; i++)
            {
                if (blackList[i].Equals(ip))
                {
                    return true;
                }
            }
            return false;
        }
 
        public string GFWResolve(string domain)
        {
            IPAddress[] address = Dns.GetHostAddresses(domain);
            string ip = address[0].ToString();
            if (!IsBadReply(ip))
            {
                return ip;
            }
            else if (cache && dnsCache.ContainsKey(domain))
            {
                return dnsCache[domain];
            }
 
            for (int i = 0; i < maxTryTimes; i++)
            {
                ip = Resolve(domain);
                if (IsBadReply(ip) || ip == null)
                {
                    continue;
                }
                else
                {
                    if (cache)
                    {
                        dnsCache.Add(domain, ip);
                    }
                    return ip;
                }
            }
            return string.Empty;
        }
 
        private void HexDump(byte[] bytes)
        {
            Console.WriteLine(BytesToHex(bytes));
        }
 
        private string BytesToHex(byte[] bytes)
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < bytes.Length; i++)
            {
                sb.AppendFormat("{0:X2}", bytes[i]);
            }
 
            string hex = sb.ToString();
            return hex;
        }
 
        private string Resolve(string domain)
        {
            byte[] recvData = new byte[512];
            byte[] data = BuildRequestData(domain);
            string result = null;
            if (debug)
            {
                Console.WriteLine(" =============== dns query request package dump: ================");
                HexDump(data);
            }
 
            IPEndPoint iep = new IPEndPoint(IPAddress.Parse(DNS_SERVER), 53);
            Socket dataSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            dataSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 6 * 1000);
            dataSocket.SendTo(data, iep);
 
            byte[] respData = new byte[512];
            for (int i = 0; i < waitTimes; i++)
            {
                try
                {
                    int intReceived = dataSocket.Receive(respData);
                    byte[] dataReceive = new byte[intReceived];
                    Array.Copy(respData, dataReceive, intReceived);
 
                    if (debug)
                    {
                        Console.WriteLine("============ dns query answer package dump");
                        HexDump(dataReceive);
                    }
 
                    string ip = DecodeDnsResponse(dataReceive, domain);
                    if (IsBadReply(ip))
                    {
                        continue;
                    }
                    else
                    {
                        result = ip;
                        break;
                    }
                }
                catch (SocketException ex)
                {
                    throw ex;
                }
            }
 
            dataSocket.Close();
            return result;
        }
 
        private byte[] BuildRequestData(string host)
        {
            // head + (host length +1) + eof sign + qtype + qclass
            int size = 12 + host.Length + 1 + 1 + 4;
            using (MemoryStream buff = new MemoryStream(size))
            {
                byte[] tmp = null;
 
                Random random = new Random();
                byte[] seq = new byte[2];
                random.NextBytes(seq);
                buff.Write(seq, 0, seq.Length);
 
                byte[] header = new byte[] { 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
                buff.Write(header, 0, header.Length);
 
                string[] parts = host.Split('.');
                for (int i = 0; i < parts.Length; i++)
                {
                    buff.WriteByte((byte)parts[i].Length);
 
                    byte[] partsByte = coding.GetBytes(parts[i]);
                    buff.Write(partsByte, 0, partsByte.Length);
                }
 
                tmp = new byte[] { 0x00 };
                buff.Write(tmp, 0, tmp.Length);
 
                tmp = new byte[] { 0x00, 0x01, 0x00, 0x01 };
                buff.Write(tmp, 0, tmp.Length);
 
                return buff.ToArray();
            }
        }
 
        private string DecodeDnsResponse(byte[] resp, string host)
        {
            using (MemoryStream stream = new MemoryStream(resp))
            {
                using (BinaryReader buffer = new BinaryReader(stream))
                {
                    //parse the query answer count.
                    int pos = 7;
                    stream.Position = pos;
                    ushort qncount = buffer.ReadUInt16();
 
                    //skip query answer field
                    pos = 12 + 1 + host.Length + 1 + 4;
                    stream.Position = pos;
                    for (int i = 0; i < qncount; i++)
                    {
                        stream.Position = pos;
                        byte pointFlg = buffer.ReadByte();
                        if ((pointFlg & 0xc0) == 0xc0)
                        {
                            pos += 3;
                        }
                        else
                        {
                            pos += 2 + host.Length + 1;
                        }
 
                        stream.Position = pos;
                        ushort queryType = buffer.ReadUInt16();
 
                        if (debug)
                        {
                            Console.WriteLine("qncount:" + qncount + "pos:" + pos + "queryType:" + queryType);
                        }
 
                        pos += 8;
                        stream.Position = pos;
                        int dataLen = buffer.ReadByte();
                        pos += 1; 
 
                        //A record
                        if (queryType == 0x0001)
                        {
                            if (debug)
                            {
                                Console.WriteLine("parse A record");
                            }
 
                            string ip = string.Empty;
                            for (int j = 0; j < dataLen; j++)
                            {
                                stream.Position = pos;
                                int v = buffer.ReadByte();
                                v = v > 0 ? v : 0x0ff & v;
                                ip += v + (j == dataLen - 1 ? "" : ".");
                                pos += 1;
                            }
                            return ip;
                        }
                        else
                        {
                            pos += dataLen;
                        }
                    }
                    return string.Empty;
                }            
            }
        }
    }
}