/// <summary> /// Get the response data /// </summary> /// <returns>The response.</returns> public int GetData(byte[] responseBuffer) { // Create and write the response header var dnsMessageHeader = new DnsMessageHeader (this.Query.Header.MessageId) { StatusCode = DnsQueryStatusCode.OK, AdditionalAnswerCount = this.AdditionalAnswerCount, NameServerAnswerCount = this.NameServerAnswerCount, NormalAnswerCount = this.NormalAnswerCount, QuestionCount = (uint)this.Query.Questions.Count, IsAuthorativeMessage = true, IsResponse = true, IsTruncated = false, RecursionDesired = false, RecursionSupported = false }; dnsMessageHeader.WriteHeader (responseBuffer, 0); // After the header, copy the original request Buffer.BlockCopy ( src: this.Query.OriginalQuestionData, srcOffset: DnsMessageHeader.SizeInBytes, dst: responseBuffer, dstOffset: DnsMessageHeader.SizeInBytes, count: this.Query.OriginalQuestionData.Length - DnsMessageHeader.SizeInBytes); // Now write the response var byteIndex = this.Query.OriginalQuestionData.Length; foreach (var answer in this.Answers) { byteIndex += answer.WriteAnswer(responseBuffer, byteIndex); } // Return the number of bytes written return byteIndex; }
/// <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); }
/// <summary> /// Get the response data /// </summary> /// <returns>The response.</returns> public int GetData(byte[] responseBuffer) { // Create and write the response header var dnsMessageHeader = new DnsMessageHeader(this.Query.Header.MessageId) { StatusCode = DnsQueryStatusCode.OK, AdditionalAnswerCount = this.AdditionalAnswerCount, NameServerAnswerCount = this.NameServerAnswerCount, NormalAnswerCount = this.NormalAnswerCount, QuestionCount = (uint)this.Query.Questions.Count, IsAuthorativeMessage = true, IsResponse = true, IsTruncated = false, RecursionDesired = false, RecursionSupported = false }; dnsMessageHeader.WriteHeader(responseBuffer, 0); // After the header, copy the original request Buffer.BlockCopy( src: this.Query.OriginalQuestionData, srcOffset: DnsMessageHeader.SizeInBytes, dst: responseBuffer, dstOffset: DnsMessageHeader.SizeInBytes, count: this.Query.OriginalQuestionData.Length - DnsMessageHeader.SizeInBytes); // Now write the response var byteIndex = this.Query.OriginalQuestionData.Length; foreach (var answer in this.Answers) { byteIndex += answer.WriteAnswer(responseBuffer, byteIndex); } // Return the number of bytes written return(byteIndex); }
/// <summary> /// Parse the DnsQuery from the given UdpReceiveResult /// </summary> /// <param name="data"></param> /// <returns></returns> public static DnsQuery Parse(byte[] buffer, IPAddress remoteEndpoint) { // Create the query var query = new DnsQuery(); query.Status = DnsQueryStatus.OK; query.ClientIPAddress = remoteEndpoint; // Parse the dns request header query.Header = DnsMessageHeader.Parse(buffer, 0); // DNS QUESTIONS: int offset = 12; for (int i = 0; i < query.Header.QuestionCount; i++) { try { // Save the question DnsQuestion question; offset += DnsQuestion.Parse(out question, buffer, offset); if (question != null) { query.Questions.Add(question); } } catch (Exception e) { Console.WriteLine(e.Message); Console.WriteLine(e.StackTrace); query.Status = DnsQueryStatus.ParsingError; break; } } // Save the original question data to include it in the answer query.OriginalQuestionData = new byte[offset]; Buffer.BlockCopy(buffer, 0, query.OriginalQuestionData, 0, offset); // Parse the additional EDNS question // For now, we only support a single additional question if (query.Header.AdditionalQuestionCount > 0) { //Console.WriteLine("Reading OPT"); try { int bytesProcessed = 0; query.EdnsQuestion = EdnsQuestion.Parse(buffer, offset, ref bytesProcessed); offset += bytesProcessed; // If the question was validely parsed, try using the subnet to assign the IP based on the user's location instead of the // dns proxy server if (query.EdnsQuestion != null) { var clientSubnetOption = (EdnsClientSubnetOption)query.EdnsQuestion.GetOption(EdnsOptionCode.ClientSubnet); if (clientSubnetOption != null) { query.ClientIPAddress = clientSubnetOption.IPAddress; } } } catch (Exception e) { Console.WriteLine(e.Message); Console.WriteLine(e.StackTrace); // If this fails, we still try to proceed normally // TODO: log } } return(query); }
/// <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; }