c#编写一个dns服务

c#编写一个dns服务

c#编写一个dns服务

一、什么是DNS

域名系统(DNS)是因特网的电话簿。人类通过域名在线访问信息,如baidu.com或bfw.wiki。Web浏览器通过Internet协议(IP)地址进行交互。DNS将域名转换为IP地址,以便浏览器可以加载Internet资源。

连接到Internet的每个设备都有一个唯一的IP地址,其他计算机可以使用该IP地址来查找设备。DNS服务器无需人类记忆IP地址,如192.168.1.1(在IPv4中),或更复杂的新字母数字IP地址,如2400:cb00:2048:1 :: c629:d7a2(在IPv6中)。

也就是说dns服务器充当的是翻译的工作,将人类易理解的域名转换成机器能理解的ip地址,方便进行资源的加载,如下图

c#编写一个dns服务

二、DNS报文的构成

DNS占用53号端口,同时使用TCP和UDP协议。DNS在区域传输的时候使用TCP协议,其他时候使用UDP协议。

DNS区域传输的时候使用TCP协议:

1.辅域名服务器会定时(一般3小时)向主域名服务器进行查询以便了解数据是否有变动。如有变动,会执行一次区域传送,进行数据同步。区域传送使用TCP而不是UDP,因为数据同步传送的数据量比一个请求应答的数据量要多得多。

2.TCP是一种可靠连接,保证了数据的准确性。

域名解析时使用UDP协议:

客户端向DNS服务器查询域名,一般返回的内容都不超过512字节,用UDP传输即可。不用经过三次握手,这样DNS服务器负载更低,响应更快。理论上说,客户端也可以指定向DNS服务器查询时用TCP,但事实上,很多DNS服务器进行配置的时候,仅支持UDP查询包。

c#编写一个dns服务

三、c#代码实现

今天使用的是ARSoft.Tools.Net这个动态库,他已经实现了dns的报文解析及发送,我们将他引入项目中

c#编写一个dns服务

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using ARSoft.Tools.Net.Dns;
using System.Net;

namespace Bo
{
    class Program
    {
        static void Main(string[] args)
        {
            using (DnsServer server = new DnsServer(IPAddress.Any, 10, 10, ProcessQuery))
            {
                server.Start();
                Console.WriteLine("Press any key to stop server");
                Console.ReadLine();
            }
        }
        public static IPAddress ClientQuery(string domain)
        {
            IPAddress Ip = null;
            DnsMessage dnsMessage = DnsClient.Default.Resolve(domain, RecordType.A);
            if ((dnsMessage == null) || ((dnsMessage.ReturnCode != ReturnCode.NoError) && (dnsMessage.ReturnCode != ReturnCode.NxDomain)))
            {
                Console.WriteLine("DNS request failed");
            }
            else
            {
                foreach (DnsRecordBase dnsRecord in dnsMessage.AnswerRecords)
                {
                    ARecord aRecord = dnsRecord as ARecord;
                    if (aRecord != null)
                    {
                        Ip = aRecord.Address;
                        Console.WriteLine("DNS request successfully : {0}", aRecord.Address.ToString());
                    }
                }
            }
            return Ip;
        }

        static DnsMessageBase ProcessQuery(DnsMessageBase message, IPAddress clientAddress, System.Net.Sockets.ProtocolType protocol)
        {
            message.IsQuery = false;
            DnsMessage query = message as DnsMessage;

            // 官方的样例文档中没有体现DNS请求发起者的IP,我在这里增加了。
            Console.WriteLine("Client Address:{0}", clientAddress.ToString());
            Console.WriteLine("query.Questions.Count:{0}", query.Questions.Count);
            Console.WriteLine("query.Questions.ToString():{0}", query.Questions.ToString());
            Console.WriteLine("query.Questions[0].ToString():{0}", query.Questions[0].ToString());
            Console.WriteLine("query.Questions[0].GetType():{0}", query.Questions[0].GetType().ToString());
            Console.WriteLine("query.Questions[0].Name:{0}", query.Questions[0].Name.ToString());
            Console.WriteLine("query.Questions[0].RecordType:{0}", query.Questions[0].RecordType.ToString());
            Console.WriteLine("query.Questions[0].RecordClass {0}", query.Questions[0].RecordClass.ToString());

            if (query.Questions[0].Name.Contains("www.jd.com"))
            {

                Console.WriteLine("Contains jd.com");
                query.AnswerRecords.Add(new ARecord("www.jd.com", 3600, IPAddress.Parse("112.82.246.51")));
                message.ReturnCode = ReturnCode.NoError;
                return message;

            }
            else
            {
                Console.WriteLine("local not ,go to server found");
                IPAddress Ip = ClientQuery(query.Questions[0].Name);
                if (Ip != null)
                {
                    query.AnswerRecords.Add(new ARecord(query.Questions[0].Name, 3600, Ip));
                    message.ReturnCode = ReturnCode.NoError;
                }
                else
                {
                    message.ReturnCode = ReturnCode.ServerFailure;

                }
                //message.ReturnCode = ReturnCode.ServerFailure;
               // Console.WriteLine(message.ReturnCode.ToString());

                return message;
            }
        }
    }
}

这里我们增加了一条记录,将jd.coim的域名指向了一台服务器ip,当客户进行请求的时候我们可以直接返回地址,其他的域名请求我们直接去查询上级域名服务器,将返回的结果再返回给客户端,相当于dns劫持的效果

下面我们模拟一下客户端的域名解析请求

using System;
using System.Collections.Generic;
using System.Text;
using ARSoft.Tools.Net.Dns;

namespace BoDnsClient
{
    class Program
    {
        static void Main(string[] args)
        {
            ClientQuery("jd.com");
            Console.ReadKey();
        }
        public static void ClientQuery(string domain)
        {
            DnsMessage dnsMessage = DnsClient.Default.Resolve(domain, RecordType.A);
            if ((dnsMessage == null) || ((dnsMessage.ReturnCode != ReturnCode.NoError) && (dnsMessage.ReturnCode != ReturnCode.NxDomain)))
            {
                Console.WriteLine("DNS request failed");
            }
            else
            {
                foreach (DnsRecordBase dnsRecord in dnsMessage.AnswerRecords)
                {
                    ARecord aRecord = dnsRecord as ARecord;
                    if (aRecord != null)
                    {
                        Console.WriteLine("DNS request successfully : {0}", aRecord.Address.ToString());
                    }
                }
            }
        }
    }
}

ok,这样就实现了,这个项目的源码大家可以下载,直接运行

BoDnsServer.zip

{{collectdata}}

网友评论0