| File: System\Net\DnsMessageReader.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 { // A parsed question entry from the question section. internal readonly ref struct DnsQuestion { public DnsEncodedName Name { get; } public DnsRecordType Type { get; } public DnsRecordClass Class { get; } internal DnsQuestion(DnsEncodedName name, DnsRecordType type, DnsRecordClass @class) { Name = name; Type = type; Class = @class; } } // A parsed resource record from any section (answer, authority, additional). internal readonly ref struct DnsRecord { public DnsEncodedName Name { get; } public DnsRecordType Type { get; } public DnsRecordClass Class { get; } public uint TimeToLive { get; } // Raw RDATA bytes. public ReadOnlySpan<byte> Data { get; } // The full DNS message buffer, for resolving compression pointers in RDATA. public ReadOnlySpan<byte> Message { get; } // Offset of Data within Message. public int DataOffset { get; } internal DnsRecord(DnsEncodedName name, DnsRecordType type, DnsRecordClass @class, uint ttl, ReadOnlySpan<byte> data, ReadOnlySpan<byte> message, int dataOffset) { Name = name; Type = type; Class = @class; TimeToLive = ttl; Data = data; Message = message; DataOffset = dataOffset; } } // Reads DNS messages from a buffer. Parses sequentially: header, questions, resource records. internal ref struct DnsMessageReader { private readonly ReadOnlySpan<byte> _message; private int _pos; public DnsMessageHeader Header { get; } private DnsMessageReader(ReadOnlySpan<byte> message, DnsMessageHeader header) { _message = message; _pos = DnsMessageHeader.Size; Header = header; } // Attempts to create a reader over a DNS message. Parses the header eagerly. // Returns false if the buffer is too small for a valid header. public static bool TryCreate(ReadOnlySpan<byte> message, out DnsMessageReader reader) { reader = default; if (!DnsMessageHeader.TryRead(message, out DnsMessageHeader header)) { return false; } reader = new DnsMessageReader(message, header); return true; } // Reads the next question from the message. public bool TryReadQuestion(out DnsQuestion question) { question = default; if (_pos >= _message.Length) { return false; } if (!DnsEncodedName.TryParse(_message, _pos, out DnsEncodedName name, out int nameWireLen)) { return false; } _pos += nameWireLen; // QTYPE (2) + QCLASS (2) = 4 bytes if (_pos + 4 > _message.Length) { return false; } DnsRecordType type = (DnsRecordType)BinaryPrimitives.ReadUInt16BigEndian(_message[_pos..]); _pos += 2; DnsRecordClass @class = (DnsRecordClass)BinaryPrimitives.ReadUInt16BigEndian(_message[_pos..]); _pos += 2; question = new DnsQuestion(name, type, @class); return true; } // Reads the next resource record from the message. public bool TryReadRecord(out DnsRecord record) { record = default; if (_pos >= _message.Length) { return false; } if (!DnsEncodedName.TryParse(_message, _pos, out DnsEncodedName name, out int nameWireLen)) { return false; } _pos += nameWireLen; // TYPE(2) + CLASS(2) + TTL(4) + RDLENGTH(2) = 10 bytes if (_pos + 10 > _message.Length) { return false; } DnsRecordType type = (DnsRecordType)BinaryPrimitives.ReadUInt16BigEndian(_message[_pos..]); _pos += 2; DnsRecordClass @class = (DnsRecordClass)BinaryPrimitives.ReadUInt16BigEndian(_message[_pos..]); _pos += 2; uint ttl = BinaryPrimitives.ReadUInt32BigEndian(_message[_pos..]); _pos += 4; ushort rdLength = BinaryPrimitives.ReadUInt16BigEndian(_message[_pos..]); _pos += 2; int dataOffset = _pos; if (dataOffset + rdLength > _message.Length) { return false; } ReadOnlySpan<byte> data = _message.Slice(dataOffset, rdLength); _pos += rdLength; record = new DnsRecord(name, type, @class, ttl, data, _message, dataOffset); return true; } } }