/// <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); }
/// <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); }
/// <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); }
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); } }
/// <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); }
/// <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); }
/// <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); }
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); }
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); }
/// <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 }); }
/// <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)); }
public void ConfigureServices(IServiceCollection services) { DnsEncoder.Init(); }
/// <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); }