Example #1
0
        /// <summary>
        /// Parse an EdnsOption based on the given type and array offset
        /// </summary>
        /// <param name="optionType"></param>
        /// <param name="optionLength"></param>
        /// <param name="array"></param>
        /// <param name="offset"></param>
        /// <returns></returns>
        public static EdnsOption ParseOption(byte[] array, int offset, ref int bytesProcessed)
        {
            // 2 bytes ~ option code
            uint optionCode = DnsEncoder.ParseUint16(array, offset);

            offset += 2;

            // 2 bytes ~ option length
            uint optionLength = DnsEncoder.ParseUint16(array, offset);

            offset += 2;

            bytesProcessed += 4;

            EdnsOption option = null;

            // Load based on the option code
            switch ((EdnsOptionCode)optionCode)
            {
            case EdnsOptionCode.ClientSubnet:
                option = EdnsClientSubnetOption.Parse(array, offset, ref bytesProcessed);
                break;
            }

            return(option);
        }
Example #2
0
        /// <summary>
        /// Write the record data into the given byte array and return the number of bytes written
        /// </summary>
        /// <param name="array">Array.</param>
        /// <param name="offset">Offset.</param>
        public override int WriteRecord(byte[] array, int offset)
        {
            offset--;
            int startingOffset = offset;

            // 2 bytes - TYPE
            offset += DnsEncoder.WriteUint16(array, (uint)this.Type, offset);
            // 2 bytes - CLASS We just use the default one here
            offset += DnsEncoder.WriteUint16(array, (uint)4096, offset);
            // 2 bytes - Extended TTL: EXTENDED-RCODE
            offset += DnsEncoder.WriteUint16(array, (uint)0, offset);
            // 2 bytes - Extended TTL: VERSION
            offset += DnsEncoder.WriteUint16(array, (uint)0, offset);
            // 2 bytes - RDLENGTH the length of the response data for this response
            // This is actually written later, after we write the data with a negative offset

            // X bytes - Response data
            var dataLength = this.WriteResponseData(array, offset + 2);

            // Write the RDLENGTH
            offset += DnsEncoder.WriteUint16(array, (uint)dataLength, offset);
            offset += dataLength;

            //offset += this.ResponseData.Length;
            return(offset - startingOffset);
        }
Example #3
0
        /// <summary>
        /// Write the response data
        /// </summary>
        /// <param name="array"></param>
        /// <param name="offset"></param>
        /// <returns></returns>
        protected override int WriteResponseData(byte[] array, int offset)
        {
            int bytesWritten = DnsEncoder.WriteUint16(array, this.Priority, offset);

            bytesWritten += DnsUtil.WriteHostnameLabel(array, offset + bytesWritten, this.Value);

            return(bytesWritten);
        }
Example #4
0
        public static async Task ReturnContext(HttpContext context, bool returnMsg, DnsMessage dnsMsg,
                                               bool cache = true, bool transIdEnable = false, ushort id = 0)
        {
            try
            {
                var queryDictionary = context.Request.Query;
                if (dnsMsg == null)
                {
                    await context.WriteResponseAsync("Remote DNS server timeout",
                                                     StatusCodes.Status500InternalServerError);

                    return;
                }

                if (returnMsg)
                {
                    if (GetClientType(queryDictionary, "json"))
                    {
                        await context.WriteResponseAsync(DnsJsonEncoder.Encode(dnsMsg).ToString(Formatting.None),
                                                         type : "application/json", headers : Startup.HeaderDict);
                    }
                    else
                    {
                        await context.WriteResponseAsync(
                            DnsEncoder.Encode(dnsMsg, transIdEnable, id),
                            type : "application/dns-message");
                    }
                }
                else
                {
                    if (GetClientType(queryDictionary, "message"))
                    {
                        await context.WriteResponseAsync(
                            DnsEncoder.Encode(dnsMsg, transIdEnable, id),
                            type : "application/dns-message");
                    }
                    else
                    {
                        await context.WriteResponseAsync(DnsJsonEncoder.Encode(dnsMsg).ToString(Formatting.None),
                                                         type : "application/json", headers : Startup.HeaderDict);
                    }
                }

                if (cache)
                {
                    WriteLogCache(dnsMsg, context);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
        }
Example #5
0
        /// <summary>
        /// Write the response data
        /// </summary>
        /// <param name="array"></param>
        /// <param name="offset"></param>
        /// <returns></returns>
        protected override int WriteResponseData(byte[] array, int offset)
        {
            int bytesWritten = DnsUtil.WriteHostnameLabel(array, offset, this.MasterHostname);

            bytesWritten += DnsUtil.WriteHostnameLabel(array, offset + bytesWritten, this.ResponsibleMailboxHostname);

            bytesWritten += DnsEncoder.WriteUint32(array, this.SerialNumber, offset + bytesWritten);
            bytesWritten += DnsEncoder.WriteInt32(array, this.RefreshInterval, offset + bytesWritten);
            bytesWritten += DnsEncoder.WriteInt32(array, this.RetryInterval, offset + bytesWritten);
            bytesWritten += DnsEncoder.WriteInt32(array, this.ExpirationLimit, offset + bytesWritten);
            bytesWritten += DnsEncoder.WriteInt32(array, this.MinimumTTL, offset + bytesWritten);

            return(bytesWritten);
        }
Example #6
0
        /// <summary>
        /// Write the header data into the given array
        /// </summary>
        /// <returns>The header.</returns>
        /// <param name="array">Array.</param>
        /// <param name="offset">Offset.</param>
        public int WriteHeader(byte[] array, int offset)
        {
            // HEADER PART 1
            // 2 bytes - Message ID
            array [offset++] = this.MessageId[0];
            array [offset++] = this.MessageId[1];

            // 1 byte - QR, OPCODE, AA, TC, RD, RA
            byte header1 = 0;

            // 1 bit
            header1 += (byte)((this.IsResponse ? 1 : 0) << 7);             // Add QR - Query response bit, since this is a response, it's a 1
            // 4 bits - Request type
            DnsEncoder.Save4BitNumeric(ref header1, (byte)this.RequestType, 1);
            // 1 bit, this is an athorative answer
            header1 += (byte)((this.IsAuthorativeMessage ? 1 : 0) << 2);
            // 1 bit, this message is not truncated
            header1 += (byte)((this.IsTruncated ? 1 : 0) << 1);
            // 1 bit, recursion not desired
            header1        += (byte)((this.RecursionDesired ? 1 : 0) << 0);
            array[offset++] = header1;

            // Write into the second byte
            // 1 bit, recursion not supported
            array[offset] = (byte)((this.RecursionSupported ? 1 : 0) << 0);

            // HEADER PART 2
            // 3 bits - res1, res2, res3
            // Skipped ~

            // 4 bits RCODE - Response status code
            DnsEncoder.Save4BitNumeric(ref array[offset++], (byte)this.StatusCode, 0);

            // HEADER COUNTS
            // 2 bytes - question count
            DnsEncoder.WriteUint16(array, (uint)this.QuestionCount, offset);
            // 2 bytes - answer count
            DnsEncoder.WriteUint16(array, this.NormalAnswerCount, offset + 2);
            // 2 bytes - NS type answer count
            DnsEncoder.WriteUint16(array, this.NameServerAnswerCount, offset + 4);
            // 2 bytes - AR type answer count
            DnsEncoder.WriteUint16(array, this.AdditionalAnswerCount, offset + 6);

            // A header is always the same size
            return(DnsMessageHeader.SizeInBytes);
        }
Example #7
0
        /// <summary>
        /// Parse a DnsMessageHeader object from the given array
        /// </summary>
        /// <param name="array">Array.</param>
        public static DnsMessageHeader Parse(byte[] array, int offset)
        {
            // TODO: check length
            // HEADER: A total of 12 bytes
            // 2 bytes - Message ID
            byte[] messageId = new byte[2];
            messageId[0] = array[offset++];
            messageId[1] = array[offset++];

            var dnsMessageHeader = new DnsMessageHeader(messageId);

            // 1 bit - QR query response, should be set to 0
            var queryHeaderByte1 = array[offset++];
            var queryHeaderByte2 = array[offset++];
            var isQuestion       = ((queryHeaderByte1 >> 7) & 1) == 0;

            if (!isQuestion)
            {
                // INVALID DNS query
                return(null);
            }
            // 4 bits - Request type
            dnsMessageHeader.RequestType = (DnsQueryRequestType)DnsEncoder.Parse4BitNumeric(queryHeaderByte1, 1);
            // 1 bit - authorative answer
            dnsMessageHeader.IsAuthorativeMessage = 1 == ((queryHeaderByte1 >> 2) & 1);
            dnsMessageHeader.IsTruncated          = 1 == ((queryHeaderByte1 >> 1) & 1);
            dnsMessageHeader.RecursionDesired     = 1 == ((queryHeaderByte1 >> 0) & 1);
            dnsMessageHeader.RecursionSupported   = 1 == ((queryHeaderByte2 >> 7) & 1);

            // 2 bytes - QDCOUNT
            dnsMessageHeader.QuestionCount = DnsEncoder.ParseUint16(array, offset);
            // 4 bytes, skip
            offset += 6;
            // 2 bytes - Additional questions
            dnsMessageHeader.AdditionalQuestionCount = DnsEncoder.ParseUint16(array, offset);


            // TODO: what to do if number of questions is 0?
            // TODO: what to do if number of questions does not match

            return(dnsMessageHeader);
        }
Example #8
0
        public static int Parse(out DnsQuestion question, byte[] array, int offset)
        {
            question             = new DnsQuestion();
            question.DataPointer = offset;
            int startingOffset = offset;

            // Parse the domain name
            byte partLength      = 0;
            var  hostnameBuilder = new StringBuilder();

            while ((partLength = array[offset++]) > 0)
            {
                var domainPart = Encoding.UTF8.GetString(array, offset, partLength);
                // Add separating dots
                if (hostnameBuilder.Length > 0)
                {
                    hostnameBuilder.Append('.');
                }
                hostnameBuilder.Append(domainPart);
                offset += partLength;
                // TODO: handle bytes larger than 255 chracters (LIMIT!)
            }

            // Parse the zone request type
            // 2 bytes
            var zoneType = (DnsZoneType)DnsEncoder.ParseUint16(array, offset);

            offset += 2;

            // Skip 2 bytes of QCLASS, we don't need them
            var questionClass = (DnsRecordClass)DnsEncoder.ParseUint16(array, offset);

            offset += 2;

            // Wire up the data
            question.Hostname = hostnameBuilder.ToString();
            question.ZoneType = zoneType;
            question.Class    = questionClass;

            return(offset - startingOffset);
        }
Example #9
0
        public static async void ReturnContext(HttpContext context, bool returnMsg, DnsMessage dnsMsg)
        {
            var queryDictionary = context.Request.Query;

            if (dnsMsg == null)
            {
                context.Response.StatusCode = StatusCodes.Status500InternalServerError;
                await context.Response.WriteAsync("Remote DNS server timeout");

                return;
            }

            if (returnMsg)
            {
                if (queryDictionary.ContainsKey("ct") && queryDictionary["ct"].ToString().Contains("json"))
                {
                    context.Response.ContentType = "application/json";
                    await context.Response.WriteAsync(DohJsonEncoder.Encode(dnsMsg).ToString(Formatting.None));
                }
                else
                {
                    context.Response.ContentType = "application/dns-message";
                    await context.Response.Body.WriteAsync(DnsEncoder.Encode(dnsMsg));
                }
            }
            else
            {
                if (queryDictionary.ContainsKey("ct") && queryDictionary["ct"].ToString().Contains("message"))
                {
                    context.Response.ContentType = "application/dns-message";
                    await context.Response.Body.WriteAsync(DnsEncoder.Encode(dnsMsg));
                }
                else
                {
                    context.Response.ContentType = "application/json";
                    await context.Response.WriteAsync(DohJsonEncoder.Encode(dnsMsg).ToString(Formatting.None));
                }
            }

            WriteLogCache(dnsMsg, context);
        }
Example #10
0
        /// <summary>
        /// Write the record data into the given byte array and return the number of bytes written
        /// </summary>
        /// <param name="array">Array.</param>
        /// <param name="offset">Offset.</param>
        public virtual int WriteRecord(byte[] array, int offset)
        {
            int startingOffset = offset;

            // 2 bytes - TYPE
            offset += DnsEncoder.WriteUint16(array, (uint)this.Type, offset);
            // 2 bytes - CLASS We just use the default one here
            offset += DnsEncoder.WriteUint16(array, (uint)1, offset);
            // 4 bytes - TTL
            offset += DnsEncoder.WriteInt32(array, this.TTL, offset);
            // 2 bytes - RDLENGTH the length of the response data for this response
            // This is actually written later, after we write the data with a negative offset

            // X bytes - Response data
            var dataLength = this.WriteResponseData(array, offset + 2);

            // Write the RDLENGTH
            offset += DnsEncoder.WriteUint16(array, (uint)dataLength, offset);
            offset += dataLength;

            return(offset - startingOffset);
        }
        /// <summary>
        /// Parse a EdnsClientSubnetOption object from the given data
        /// </summary>
        /// <param name="array"></param>
        /// <param name="offset"></param>
        /// <param name="bytesProcessed"></param>
        /// <returns></returns>
        public static EdnsClientSubnetOption Parse(byte[] array, int offset, ref int bytesProcessed)
        {
            // 2 bytes - Check if an IPv4 or IPv6 ip will be given
            bool isIPv4 = DnsEncoder.ParseUint16(array, offset) == 1;

            offset += 2;
            // 1 byte - source netmask
            byte sourceNetmask = array[offset++];
            // 1 byte - scope netmask
            byte scopeNetmask = array[offset++];

            bytesProcessed += 4;

            // Create an array to hold the IP address data
            byte[] addressBytes = new byte[isIPv4 ? 4 : 16];

            // Validate array size
            int addressLength = (int)Math.Ceiling(sourceNetmask / 8d);

            if (addressLength > addressBytes.Length)
            {
                return(null);
            }

            Buffer.BlockCopy(array, offset, addressBytes, 0, addressLength);
            bytesProcessed += addressLength;
            offset         += addressLength;

            // Copy the data into a new EdnsClientSubnetOption object
            return(new EdnsClientSubnetOption()
            {
                IPAddress = new IPAddress(addressBytes),
                ScopeNetmask = scopeNetmask,
                SourceNetmask = sourceNetmask
            });
        }
Example #12
0
 /// <summary>
 /// Write the response data
 /// </summary>
 /// <param name="array"></param>
 /// <param name="offset"></param>
 /// <returns></returns>
 protected override int WriteResponseData(byte[] array, int offset)
 {
     return(DnsEncoder.WriteTextBlock(array, offset, this.Value));
 }
Example #13
0
 public void ConfigureServices(IServiceCollection services)
 {
     DnsEncoder.Init();
 }
Example #14
0
        /// <summary>
        /// Parse the DnsEdnsRequestMessage from the given byte array
        /// </summary>
        /// <param name="array"></param>
        /// <param name="offset"></param>
        /// <returns></returns>
        public static EdnsQuestion Parse(byte[] array, int offset, ref int bytesProcessed)
        {
            // Check the length
            if (offset + 10 >= array.Length)
            {
                return(null);
            }

            // domain
            var domain = array[offset++];

            // 2 bytes - Question type
            var type = (DnsZoneType)DnsEncoder.ParseUint16(array, offset);

            offset += 2;

            // 2 bytes - requestor UDP size
            // TODO: save this, should be useful
            var requestorUdpSize = DnsEncoder.ParseUint16(array, offset);

            if (requestorUdpSize < 512)
            {
                requestorUdpSize = 512; // Don't allow smaller than 512 bytes
            }
            else if (requestorUdpSize > 4096)
            {
                requestorUdpSize = 4096; // Don't allow smaller than 4096 bytes
            }
            offset += 2;

            // extended RCODE and flags ~ skip
            offset += 4;

            // Domain must be 0, type must be OPT
            if (domain != 0 || type != DnsZoneType.OPT)
            {
                return(null);
            }

            // Read the RDATA length
            var rdataLength = DnsEncoder.ParseUint16(array, offset);

            offset += 2;

            // Validate length
            if (offset + rdataLength > array.Length)
            {
                return(null);
            }

            // Create a new question
            var ednsQuestion = new EdnsQuestion();

            // Read the options
            int dataBytesProcessed = 0;

            while (dataBytesProcessed < rdataLength)
            {
                // Load the option based on type
                var option = EdnsOption.ParseOption(array, offset + dataBytesProcessed, ref dataBytesProcessed);
                if (option != null)
                {
                    ednsQuestion.Options.Add(option);
                }
            }

            // Save the bytes processed
            bytesProcessed = 10 + dataBytesProcessed;

            return(ednsQuestion);
        }