| File: System\Net\DnsMessageHeader.cs | Web Access |
| Project: src\runtime\src\libraries\System.Net.NameResolution\src\System.Net.NameResolution.csproj (System.Net.NameResolution) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers.Binary; namespace System.Net { // The fixed 12-byte DNS message header (RFC 1035 §4.1.1). internal struct DnsMessageHeader { public ushort Id { get; set; } public bool IsResponse { get; set; } public DnsOpCode OpCode { get; set; } public DnsHeaderFlags Flags { get; set; } public DnsResponseCode ResponseCode { get; set; } public ushort QuestionCount { get; set; } public ushort AnswerCount { get; set; } public ushort AuthorityCount { get; set; } public ushort AdditionalCount { get; set; } internal const int Size = 12; internal bool TryWrite(Span<byte> destination) { if (destination.Length < Size) { return false; } BinaryPrimitives.WriteUInt16BigEndian(destination, Id); BinaryPrimitives.WriteUInt16BigEndian(destination[2..], EncodeFlagsWord()); BinaryPrimitives.WriteUInt16BigEndian(destination[4..], QuestionCount); BinaryPrimitives.WriteUInt16BigEndian(destination[6..], AnswerCount); BinaryPrimitives.WriteUInt16BigEndian(destination[8..], AuthorityCount); BinaryPrimitives.WriteUInt16BigEndian(destination[10..], AdditionalCount); return true; } internal static bool TryRead(ReadOnlySpan<byte> source, out DnsMessageHeader header) { header = default; if (source.Length < Size) { return false; } ushort id = BinaryPrimitives.ReadUInt16BigEndian(source); ushort flagsWord = BinaryPrimitives.ReadUInt16BigEndian(source[2..]); ushort qdCount = BinaryPrimitives.ReadUInt16BigEndian(source[4..]); ushort anCount = BinaryPrimitives.ReadUInt16BigEndian(source[6..]); ushort nsCount = BinaryPrimitives.ReadUInt16BigEndian(source[8..]); ushort arCount = BinaryPrimitives.ReadUInt16BigEndian(source[10..]); DecodeFlagsWord(flagsWord, out bool isResponse, out DnsOpCode opCode, out DnsHeaderFlags flags, out DnsResponseCode responseCode); header = new DnsMessageHeader { Id = id, IsResponse = isResponse, OpCode = opCode, Flags = flags, ResponseCode = responseCode, QuestionCount = qdCount, AnswerCount = anCount, AuthorityCount = nsCount, AdditionalCount = arCount, }; return true; } // RFC 1035 §4.1.1 wire format of the flags word (bytes 2-3): // // Bit: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 // QR | OpCode | AA TC RD RA Z AD CD | RCODE | // // DnsHeaderFlags enum values are the wire bit positions shifted right by 4, // so the enum fits in a byte. Encoding shifts left by 4 to restore wire positions, // decoding shifts right by 4. The Z bit (wire bit 6) gap is preserved by the shift. // Wire flag bits: AA(10) TC(9) RD(8) RA(7) AD(5) CD(4) // Enum bits: AA(6) TC(5) RD(4) RA(3) AD(1) CD(0) private const int FlagsShift = 4; private const ushort WireFlagsMask = 0x07F0; // wire bits 10-7 and 5-4 private readonly ushort EncodeFlagsWord() { ushort word = 0; if (IsResponse) { word |= 1 << 15; } word |= (ushort)(((int)OpCode & 0xF) << 11); word |= (ushort)((int)Flags << FlagsShift); word |= (ushort)((int)ResponseCode & 0xF); return word; } private static void DecodeFlagsWord(ushort word, out bool isResponse, out DnsOpCode opCode, out DnsHeaderFlags flags, out DnsResponseCode responseCode) { isResponse = (word & (1 << 15)) != 0; opCode = (DnsOpCode)((word >> 11) & 0xF); responseCode = (DnsResponseCode)(word & 0xF); flags = (DnsHeaderFlags)((word & WireFlagsMask) >> FlagsShift); } } }