zoukankan      html  css  js  c++  java
  • 【协议】自己做个DNS代理

        DNS域名系统的缩写。DNS支持使用TCPUDP协议,使用53号端口。可能是国产教科书的缘故,许多人都不知道DNS还可以在TCP协议上传输。当然,国内的绝大多数DNS服务器都不支持TCP协议。本文讲述如何动手制作一个DNS代理。

        你可能要问我:满世界都是DNS服务器,为什么要写一个DNS代理?

        答案很简单:

        1、某些网络为了节约成本没有架设DNS服务器,需要一个轻量级的解决方案;

        2、升级的DNS代理可以实现一些企业需要的管理功能;

        3、我们国家防火墙看不懂TCP封装的DNS数据;

        4、学习网络协议。

        如果你需要一个增强版的DNS代理,比如可以修改DNS请求和回答,可以参考使用此开源类库:ARSoft.Tools.Net

        至于这个DNS代理能给你带来什么,还要由你自己决定。

        本文目标:制作一个稳定的、支持TCP和UDP协议的、可强制使用TCP协议请求的、支持IPv6的DNS代理。

        既然提到了要支持TCP协议,就简单提一下TCP协议封装和UDP协议封装的差异:UDP是直接传送消息;TCP封装的消息在消息前增加了所跟随消息的长度数据,其占用两个字节,使用大端序作为其字节序

        那么我们需要做的事情是:

        1、同时监听TCP和UDP的53号端口;

        2、接收到数据就原封不动的转发给外部的DNS服务器;

        3、把DNS服务器返回的数据原封不动的返回给请求者;

        4、继续监听。

        实现代码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Net.Sockets;
    
    namespace SimpleDnsProxy
    {
        public class DnsProxy : IDisposable
        {
            private const int DNS_PORT = 53;
            private const int BUFFER_SIZE = 4096;
            private const int RETRY_TIMES = 3;
    
            private readonly IPAddress[] _dnsServers;
            private readonly AddressFamily _addressFamily;
            private readonly int _listenerCount;
            private readonly bool _forceTcp;
            private readonly object _aysncRoot = new object();
    
            private Socket _udpSocket = null;
            private Socket _tcpSocket = null;
            private Socket _udpQuerySocket = null;
            private Socket _tcpQuerySocket = null;
            private int _serverIndex = 0;
    
            static DnsProxy()
            {
                DefaultV4 = new DnsProxy(new[] {
                        IPAddress.Parse("8.8.4.4"),         //google
                        IPAddress.Parse("208.67.220.220"),  //opendns
                        IPAddress.Parse("8.8.8.8"),         //google
                        IPAddress.Parse("208.67.222.222"),  //opendns
                    }, AddressFamily.InterNetwork, 10, true);
                DefaultV6 = new DnsProxy(new[] {
                        IPAddress.Parse("2001:4860:4860::8844"),//google
                        IPAddress.Parse("2620:0:ccd::2"),       //opendns
                        IPAddress.Parse("2001:4860:4860::8888"),//google
                        IPAddress.Parse("2620:0:ccc::2"),       //opendns
                    }, AddressFamily.InterNetworkV6, 10, true);
            }
            public static DnsProxy DefaultV4 { get; private set; }
            public static DnsProxy DefaultV6 { get; private set; }
    
            public DnsProxy(IPAddress[] dnsServers, AddressFamily addressFamily, int listenerCount, bool forceTcp = false)
            {
                if (dnsServers == null)
                    throw new ArgumentNullException("dnsServers");
                if (dnsServers.Length == 0)
                    throw new ArgumentException("at least need one server address");
                if (dnsServers.Any(s => s.AddressFamily != addressFamily))
                    throw new ArgumentException("some dns servers address not belong to specified address family");
    
                _dnsServers = dnsServers;
                _addressFamily = addressFamily;
                _listenerCount = listenerCount;
                _forceTcp = forceTcp;
    
                if (!Socket.OSSupportsIPv4 && addressFamily == AddressFamily.InterNetwork)
                    throw new NotSupportedException("OS not supports IPv4 address family");
                if (!Socket.OSSupportsIPv6 && addressFamily == AddressFamily.InterNetworkV6)
                    throw new NotSupportedException("OS not supports IPv6 address family");
    
                _udpSocket = new Socket(addressFamily, SocketType.Dgram, ProtocolType.Udp);
                _tcpSocket = new Socket(addressFamily, SocketType.Stream, ProtocolType.Tcp);
                _udpQuerySocket = new Socket(addressFamily, SocketType.Dgram, ProtocolType.Udp);
                _tcpQuerySocket = new Socket(addressFamily, SocketType.Stream, ProtocolType.Tcp);
            }
    
            public void Start()
            {
                EndPoint ep = new IPEndPoint(_addressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any, DNS_PORT);
                _udpSocket.Bind(ep);
                for (int i = 0; i < _listenerCount; i++)
                {
                    AsyncState state = new AsyncState
                    {
                        Buffer = new byte[BUFFER_SIZE],
                        EndPoint = new IPEndPoint(_addressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any, DNS_PORT),
                    };
                    StartUdpListen(state);
                }
                _tcpSocket.Bind(ep);
                _tcpSocket.Listen(_listenerCount);
                for (int i = 0; i < _listenerCount; i++)
                {
                    StartTcpListen();
                }
            }
    
            private void TcpAccept_Completed(object sender, SocketAsyncEventArgs e)
            {
                try
                {
                    byte[] buf = new byte[BUFFER_SIZE];
                    int size = e.AcceptSocket.Receive(buf);
    
                    buf = TcpQuery(buf.Take(size).ToArray());
    
                    e.AcceptSocket.Send(buf);
                    e.AcceptSocket.Disconnect(false);
                    e.AcceptSocket.Dispose();
                }
                catch { }
                StartTcpListen();
            }
    
            private void UdpAsyncCallback(IAsyncResult ar)
            {
                var state = ar.AsyncState as AsyncState;
                try
                {
                    int size = _udpSocket.EndReceiveFrom(ar, ref state.EndPoint);
                    byte[] buf = state.Buffer;
    
                    IEnumerable<byte> data = BitConverter.GetBytes((short)size);
                    if (BitConverter.IsLittleEndian)
                        data = data.Reverse();
    
                    buf = _forceTcp
                        ? TcpQuery(data.Concat(buf.Take(size)).ToArray()).Skip(2).ToArray()
                        : UdpQuery(buf.Take(size).ToArray());
    
                    _udpSocket.SendTo(buf, state.EndPoint);
                    state.EndPoint = new IPEndPoint(_addressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any, DNS_PORT);
                }
                catch { }
                StartUdpListen(state);
            }
    
            private byte[] UdpQuery(byte[] message)
            {
                EndPoint ep = CreateServerEndPoint();
                byte[] buf = new byte[BUFFER_SIZE];
                int size = -1;
                int retry = 0;
                try
                {
                    lock (_aysncRoot)
                        do
                        {
                            _udpQuerySocket.SendTo(message, ep);
                            size = _udpQuerySocket.ReceiveFrom(buf, ref ep);
                        } while (size == 0 && retry++ < RETRY_TIMES);
                }
                catch
                {
                    _serverIndex = (_serverIndex + 1) % _dnsServers.Length;
                }
                return buf.Take(size).ToArray();
            }
    
            private byte[] TcpQuery(byte[] message)
            {
                EndPoint ep = CreateServerEndPoint();
                byte[] buf = new byte[BUFFER_SIZE];
                int size = -1;
                int retry = 0;
                try
                {
                    lock (_aysncRoot)
                        do
                        {
                            if (size == 0 || !_tcpQuerySocket.Connected && _tcpQuerySocket.IsBound)
                            {
                                _tcpQuerySocket.Dispose();
                                _tcpQuerySocket = new Socket(_addressFamily, SocketType.Stream, ProtocolType.Tcp);
                            }
                            if (!_tcpQuerySocket.Connected)
                                _tcpQuerySocket.Connect(ep);
                            _tcpQuerySocket.Send(message);
                            size = _tcpQuerySocket.Receive(buf);
                        } while (size == 0 && retry++ < RETRY_TIMES);
                }
                catch
                {
                    _serverIndex = (_serverIndex + 1) % _dnsServers.Length;
                }
                return buf.Take(size).ToArray();
            }
    
            private EndPoint CreateServerEndPoint()
            {
                return new IPEndPoint(_dnsServers[_serverIndex], DNS_PORT);
            }
    
            private SocketAsyncEventArgs CreateSocketAsyncEventArgs()
            {
                var args = new SocketAsyncEventArgs();
                args.Completed += new EventHandler<SocketAsyncEventArgs>(TcpAccept_Completed);
                return args;
            }
    
            private void StartUdpListen(AsyncState state)
            {
                try
                {
                    _udpSocket.BeginReceiveFrom(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, ref state.EndPoint, UdpAsyncCallback, state);
                }
                catch (ObjectDisposedException)
                {
                    return;
                }
                catch
                {
                    StartUdpListen(state);
                }
            }
    
            private void StartTcpListen()
            {
                try
                {
                    _tcpSocket.AcceptAsync(CreateSocketAsyncEventArgs());
                }
                catch (ObjectDisposedException)
                {
                    return;
                }
                catch
                {
                    StartTcpListen();
                }
            }
    
            public void Stop()
            {
                _udpSocket.Shutdown(SocketShutdown.Both);
                _tcpSocket.Shutdown(SocketShutdown.Both);
            }
    
            #region IDisposable.Dispose
            void IDisposable.Dispose()
            {
                _udpSocket.Dispose();
                _tcpSocket.Dispose();
                _udpQuerySocket.Dispose();
                _tcpQuerySocket.Dispose();
            }
            #endregion
    
            private class AsyncState
            {
                public byte[] Buffer;
                public EndPoint EndPoint;
            }
        }
    }
    

        调用代码:

    using System.Threading;
    
    namespace SimpleDnsProxy
    {
        class Program
        {
            static void Main(string[] args)
            {
                DnsProxy.DefaultV4.Start();
                DnsProxy.DefaultV6.Start();
                new ManualResetEvent(false).WaitOne();
            }
        }
    }
    

        经过了许多测试,目前此版本比较稳定。

        如果能得到类似如下的结果,说明本机的DNS代理服务执行正确。

    image

  • 相关阅读:
    hdu1546+spfa
    hdu1245+dij,堆优化
    hdu1669+二分多重匹配+二分
    hdu2389+二分匹配(Hopcroft-Karp算法)
    hdu3360+二分匹配(匈牙利算法)
    hdu4253 二分+MST (经典模型)
    本次项目开发的体会
    test

    结构图
  • 原文地址:https://www.cnblogs.com/Aimeast/p/2298672.html
Copyright © 2011-2022 走看看