Protocol: act as a DNS proxy.

Source: Internet
Author: User

DNS stands for the domain name system. DNS supports TCP and UDP protocols and port 53. It may be because of Chinese textbooks. Many people do not know that DNS can be transmitted over TCP. Of course, most DNS servers in China do not support the TCP protocol. This article describes how to create a DNS proxy.

You may ask me: Why should I write a DNS proxy when DNS servers are used all over the world?

The answer is simple:

1. Some networks need a lightweight solution to save costs without setting up DNS servers;

2. the upgraded DNS proxy can implement the management functions required by enterprises;

3. Our national firewall does not understand TCP-encapsulated DNS data;

4. Learn network protocols.

If you need an enhanced version of DNS proxy, such as modifying DNS requests and responses, you can refer to the open-source class library arsoft. Tools. net.

It is up to you to decide what the DNS proxy can bring to you.

 

Objective: to create a stable, TCP-and UDP-supported DNS proxy that can force TCP requests and support IPv6.

 

Now that TCP protocol is supported, let's briefly mention the Difference Between TCP protocol encapsulation and UDP protocol encapsulation: UDP directly transmits messages; a tcp-encapsulated message adds the length data of the followed message before the message. It occupies two bytes and uses the large-end order as its byte order.

 

What we need to do is:

1. Listen to TCP and UDP ports 53 at the same time;

2. Once the data is received, it will be forwarded to the external DNS server;

3. Return the data returned by the DNS server to the requester without any change;

4. Continue listening.

 

Implementation Code:

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;        }    }}

Call code:

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

After many tests, the current version is relatively stable.

If the following results are returned, the local DNS proxy service is correctly executed.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.