加上网络库的版本日志输出 DNSServer严格判断父级DNS
nnhy authored at 2012-07-16 11:41:05
11.22 KiB
X
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using NewLife.Collections;
using NewLife.Log;
using NewLife.Net.Common;
using NewLife.Net.Sockets;
#if NET4
using System.Linq;
#else
using NewLife.Linq;
#endif

namespace NewLife.Net.DNS
{
    /// <summary>DNS服务器</summary>
    public class DNSServer : NetServer
    {
        #region 属性
        private String _DomainName;
        /// <summary>域名</summary>
        public String DomainName { get { return _DomainName ?? "dns.nnhy.org"; } set { _DomainName = value; } }

        private List<NetUri> _Parents;
        /// <summary>上级DNS地址</summary>
        public List<NetUri> Parents
        {
            get
            {
                if (_Parents == null)
                {
                    var list = new List<NetUri>();
                    foreach (var item in NetHelper.GetDns())
                    {
                        if (item.IsAny())
                        {
                            WriteLog("取得的本地DNS[{0}]有误,任意地址不能作为父级DNS地址。", item);
                            continue;
                        }
                        var uri = new NetUri(ProtocolType.Udp, item, 53);
                        WriteLog("使用本地地址作为父级DNS:{0}", uri);
                        list.Add(uri);
                    }
                    list.Add(new NetUri("tcp://8.8.8.8:53"));
                    list.Add(new NetUri("udp://4.4.4.4:53"));

                    _Parents = list;
                }
                return _Parents;
            }
            set { _Parents = value; }
        }

        /// <summary>上级DNS地址,多个地址以逗号隔开</summary>
        public String Parent
        {
            get
            {
                var ps = Parents;
                if (ps == null || ps.Count < 1) return null;

                var sb = new StringBuilder();
                foreach (var item in ps)
                {
                    if (sb.Length > 0) sb.Append(",");
                    sb.Append(item);
                }
                return sb.ToString();
            }
            set
            {
                var ps = Parents;
                var list = new HashSet<String>(ps.Select(p => p.ToString()), StringComparer.OrdinalIgnoreCase);
                ps.Clear();

                if (value.IsNullOrWhiteSpace()) return;

                var ss = value.Split(",");
                if (ss == null || ss.Length < 1) return;

                foreach (var item in ss)
                {
                    var uri = new NetUri(item);
                    if (uri.Port <= 0) uri.Port = 53;
                    if (!list.Contains(uri.ToString()))
                    {
                        if (uri.Address.IsAny())
                        {
                            WriteLog("配置的父级DNS[{0}]有误,任意地址不能作为父级DNS地址。", uri);
                            continue;
                        }
                        ps.Add(uri);
                        list.Add(uri.ToString());
                    }
                }
            }
        }
        #endregion

        #region 构造
        /// <summary>实例化一个DNS服务器</summary>
        public DNSServer() { Port = 53; }
        #endregion

        #region 方法
        DictionaryCache<String, DNSEntity> cache = new DictionaryCache<string, DNSEntity>() { Expriod = 600, Asynchronous = true };

        /// <summary>接收处理</summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected override void OnReceived(object sender, NetEventArgs e)
        {
            var session = e.Session;
            Boolean isTcp = session.ProtocolType == ProtocolType.Tcp;

            // 解析
            var request = DNSEntity.Read(e.GetStream(), isTcp);

            var response = Request(session, request);
            if (response != null)
            {
                response.Header.ID = request.Header.ID;
                Response(session, request, response);
            }

            session.Dispose();
        }

        /// <summary>处理请求</summary>
        /// <param name="session"></param>
        /// <param name="request"></param>
        /// <returns></returns>
        protected virtual DNSEntity Request(ISocketSession session, DNSEntity request)
        {
            Boolean isTcp = session.ProtocolType == ProtocolType.Tcp;

            // 处理,修改
            WriteDNSLog("{0}://{1} 请求 {2}", session.ProtocolType, session.RemoteEndPoint, request);

            // 请求事件,如果第二参数有值,则直接返回
            if (OnRequest != null)
            {
                var e = new DNSEventArgs();
                e.Request = request;
                OnRequest(this, e);
                if (e.Response != null) return e.Response;
            }

            // 如果是PTR请求
            if (request.Type == DNSQueryType.PTR)
            {
                var ptr = RequestPTR(request);
                if (ptr != null) return ptr;
            }

            // 读取缓存
            var rs = cache.GetItem<DNSEntity>(request.ToString(), request, GetDNS, false);

            // 返回给客户端
            if (rs != null)
            {
                //String file = String.Format("dns_{0:MMddHHmmss}.bin", DateTime.Now);
                //File.WriteAllBytes(file, entity2.GetStream().ReadBytes());

                // 如果是PTR请求
                if (request.Type == DNSQueryType.PTR && rs.Type == DNSQueryType.PTR)
                {
                    var ptr = request.Questions[0] as DNS_PTR;
                    var ptr2 = rs.GetAnswer() as DNS_PTR;
                    if (ptr2 != null)
                    {
                        ptr2.Name = ptr.Name;
                        ptr2.DomainName = DomainName;
                    }
                    if (rs.Answers != null && rs.Answers.Length > 0)
                    {
                        foreach (var item in rs.Answers)
                        {
                            if (item.Type == DNSQueryType.PTR) item.Name = ptr.Name;
                        }
                    }
                }
            }

            return rs;
        }

        /// <summary>处理PTR请求</summary>
        /// <param name="request"></param>
        /// <returns></returns>
        protected virtual DNSEntity RequestPTR(DNSEntity request)
        {
            var ptr = request.Questions[0] as DNS_PTR;
            // 对本地的请求马上返回
            var addr = ptr.Address;
            if (addr != null && (IPAddress.IsLoopback(addr) || NetHelper.GetIPs().Any(ip => ip + "" == addr + "")))
            {
                var ptr2 = new DNS_PTR();
                ptr2.Name = ptr.Name;
                ptr2.DomainName = DomainName;

                var rs = new DNSEntity();
                rs.Questions = request.Questions;
                rs.Answers = new DNSRecord[] { ptr2 };

                rs.Header.ID = request.Header.ID;
                return rs;
            }
            return null;
        }

        /// <summary>处理响应</summary>
        /// <param name="session"></param>
        /// <param name="request"></param>
        /// <param name="response"></param>
        protected virtual void Response(ISocketSession session, DNSEntity request, DNSEntity response)
        {
            Boolean isTcp = session.ProtocolType == ProtocolType.Tcp;

            if (OnResponse != null)
            {
                var e = new DNSEventArgs { Request = request, Response = response, Session = session };
                OnResponse(this, e);
            }

            session.Send(response.GetStream(isTcp));
        }

        DNSEntity GetDNS(String key, DNSEntity request)
        {
            // 请求父级代理
            NetUri parent = null;
            Byte[] data = null;
            ISocketSession session = null;
            foreach (var item in Parents)
            {
                session = NetService.CreateSession(item);
                parent = item;
                // 如果是PTR请求
                if (request.Type == DNSQueryType.PTR)
                {
                    // 复制一份,防止修改外部
                    request = new DNSEntity().CloneFrom(request);

                    var ptr = request.GetAnswer(true) as DNS_PTR;
                    if (ptr != null) ptr.Address = parent.Address;
                }

                try
                {
                    session.Send(request.GetStream(item.ProtocolType == ProtocolType.Tcp));
                    data = session.Receive();

                    if (data != null && data.Length > 0) break;
                }
                catch { }
            }
            if (data == null || data.Length < 1) return null;

            DNSEntity response = null;
            try
            {
                // 解析父级代理返回的数据
                response = DNSEntity.Read(data, parent.ProtocolType == ProtocolType.Tcp);

                // 处理,修改
                WriteDNSLog("{0} 返回 {1}", parent, response);
            }
            catch (Exception ex)
            {
                String file = String.Format("dns_{0:MMddHHmmss}.bin", DateTime.Now);
                XTrace.WriteLine("解析父级代理返回数据出错!数据保存于" + file + "。" + ex.Message);
                File.WriteAllBytes(file, data);
            }

            if (OnNew != null)
            {
                var e = new DNSEventArgs { Request = request, Response = response, Session = session };
                OnNew(this, e);
            }

            return response;
        }
        #endregion

        #region 事件
        /// <summary>请求时触发。</summary>
        public event EventHandler<DNSEventArgs> OnRequest;

        /// <summary>响应时触发。</summary>
        public event EventHandler<DNSEventArgs> OnResponse;

        /// <summary>取得新DNS时触发。</summary>
        public event EventHandler<DNSEventArgs> OnNew;
        #endregion

        #region 写日志
        static TextFileLog log = TextFileLog.Create("DNSLog");
        [Conditional("DEBUG")]
        void WriteDNSLog(String format, params Object[] args)
        {
            log.WriteLine(format, args);
        }
        #endregion
    }

    /// <summary>DNS事件参数</summary>
    public class DNSEventArgs : EventArgs
    {
        private DNSEntity _Request;
        /// <summary>请求</summary>
        public DNSEntity Request { get { return _Request; } set { _Request = value; } }

        private DNSEntity _Response;
        /// <summary>响应</summary>
        public DNSEntity Response { get { return _Response; } set { _Response = value; } }

        private ISocketSession _Session;
        /// <summary>网络会话</summary>
        public ISocketSession Session { get { return _Session; } set { _Session = value; } }
    }
}