Subdivide ResourceRecord types

This commit is contained in:
femsci 2023-09-30 08:49:12 +02:00
parent efdf871b4e
commit 359c370b49
Signed by: femsci
GPG key ID: 08F7911F0E650C67
5 changed files with 91 additions and 18 deletions

View file

@ -22,7 +22,7 @@ public struct DnsMessage : IBinarySerializable<DnsMessage>
public DnsMessageType MessageType { get; set; } public DnsMessageType MessageType { get; set; }
public DnsMessageOpcode Opcode { get; set; } public DnsMessageOpcode Opcode { get; set; }
public DnsResponseCode ResponseCode { get; set; } public DnsResponseCode ResponseCode { get; set; }
public ICollection<ResourceRecord> Questions { get; set; } public ICollection<QueryResourceRecord> Questions { get; set; }
public static DnsMessage Deserialize(ReadOnlyMemory<byte> data) public static DnsMessage Deserialize(ReadOnlyMemory<byte> data)
{ {

View file

@ -51,17 +51,44 @@ public enum ResourceRecordClass : short
HS = 4, HS = 4,
} }
public struct ResourceRecord : IBinarySerializable<ResourceRecord> public record QueryResourceRecord : IBinarySerializable<QueryResourceRecord>
{ {
public string Name { get; set; } public bool IsQuery => true;
public string Name { get; set; } = default!;
public int Labels => Name.Split('.').Length;
//UTF8
internal int NameOffset => Name.Split('.').Select(x => x.Length).Sum() + Labels + 1;
public ResourceRecordType Type { get; set; } public ResourceRecordType Type { get; set; }
public ResourceRecordClass Class { get; set; } public ResourceRecordClass Class { get; set; }
public TimeSpan Ttl { get; set; }
public static ResourceRecord Deserialize(ReadOnlyMemory<byte> data) public static string DeserializeName(BitEnumerator b)
{ {
throw new NotImplementedException(); StringBuilder sb = new();
byte len;
while ((len = b.TakeByte()) != 0)
{
sb.Append(Encoding.UTF8.GetString(b.TakeBytes(len))).Append('.');
}
return sb.ToString().Trim('.');
}
public static QueryResourceRecord Deserialize(ReadOnlyMemory<byte> data)
{
BitEnumerator b = new(data);
string name = DeserializeName(b);
ResourceRecordType type = (ResourceRecordType)b.TakeInt16();
ResourceRecordClass clazz = (ResourceRecordClass)b.TakeInt16();
return new QueryResourceRecord()
{
Name = name,
Class = clazz,
Type = type,
};
} }
public ReadOnlySpan<byte> SerializeName() public ReadOnlySpan<byte> SerializeName()
@ -73,7 +100,7 @@ public struct ResourceRecord : IBinarySerializable<ResourceRecord>
throw new ArgumentOutOfRangeException("Name must be less than 255"); throw new ArgumentOutOfRangeException("Name must be less than 255");
} }
ArrayBufferWriter<byte> buffer = new(name.Length + 1 + 4); ArrayBufferWriter<byte> buffer = new(NameOffset);
foreach (string s in name.Split('.')) foreach (string s in name.Split('.'))
{ {
@ -90,7 +117,7 @@ public struct ResourceRecord : IBinarySerializable<ResourceRecord>
return buffer.WrittenSpan; return buffer.WrittenSpan;
} }
public ReadOnlyMemory<byte> Serialize() public virtual ReadOnlyMemory<byte> Serialize()
{ {
var buffer = new ArrayBufferWriter<byte>(); var buffer = new ArrayBufferWriter<byte>();
@ -99,11 +126,53 @@ public struct ResourceRecord : IBinarySerializable<ResourceRecord>
buffer.Advance(2); buffer.Advance(2);
BinaryPrimitives.WriteInt16BigEndian(buffer.GetSpan(), (short)Class); BinaryPrimitives.WriteInt16BigEndian(buffer.GetSpan(), (short)Class);
buffer.Advance(2); buffer.Advance(2);
BinaryPrimitives.WriteInt32BigEndian(buffer.GetSpan(), (int)Ttl.TotalSeconds);
buffer.Advance(4); return buffer.WrittenMemory;
//TODO: Implement RDATA }
BinaryPrimitives.WriteInt16BigEndian(buffer.GetSpan(), 0); }
buffer.Advance(2);
public record ResourceRecord : QueryResourceRecord, IBinarySerializable<ResourceRecord>
{
public TimeSpan Ttl { get; set; }
public byte[] Data { get; set; } = Array.Empty<byte>();
static ResourceRecord IBinarySerializable<ResourceRecord>.Deserialize(ReadOnlyMemory<byte> data)
{
QueryResourceRecord qr = Deserialize(data);
BitEnumerator b = new(data);
b.Skip(qr.NameOffset);
TimeSpan ttl = TimeSpan.FromSeconds(b.TakeInt32());
short dataLen = b.TakeInt16();
byte[] rdata = b.TakeBytes(dataLen);
return new ResourceRecord()
{
Name = qr.Name,
Class = qr.Class,
Type = qr.Type,
Ttl = ttl,
Data = rdata
};
}
public override ReadOnlyMemory<byte> Serialize()
{
var buffer = new ArrayBufferWriter<byte>();
buffer.Write(base.Serialize().Span);
BinaryPrimitives.WriteInt32BigEndian(buffer.GetSpan(), (int)Ttl.TotalSeconds);
buffer.Advance(4);
//TODO: Implement RDATA
BinaryPrimitives.WriteInt16BigEndian(buffer.GetSpan(), (short)Data.Length);
buffer.Advance(2);
if (Data.Length > 0)
{
buffer.Write(Data);
}
return buffer.WrittenMemory; return buffer.WrittenMemory;
} }
} }

View file

@ -26,6 +26,11 @@ public class BitEnumerator
return data.ToArray(); return data.ToArray();
} }
public void Skip(int len)
{
_position += len;
}
public short TakeInt16() public short TakeInt16()
{ {
short num = BinaryPrimitives.ReadInt16BigEndian(_data.Span[_position..(_position + 2)]); short num = BinaryPrimitives.ReadInt16BigEndian(_data.Span[_position..(_position + 2)]);

View file

@ -20,12 +20,11 @@ public class DnsTest
MessageId = 0xbabe, MessageId = 0xbabe,
MessageType = DnsMessageType.Query, MessageType = DnsMessageType.Query,
Opcode = DnsMessageOpcode.Query, Opcode = DnsMessageOpcode.Query,
Questions = new List<ResourceRecord>() Questions = new List<QueryResourceRecord>()
{ {
new ResourceRecord() new QueryResourceRecord()
{ {
Class = ResourceRecordClass.IN, Class = ResourceRecordClass.IN,
Ttl = TimeSpan.Zero,
Name = "meow.lgbt", Name = "meow.lgbt",
Type = ResourceRecordType.A Type = ResourceRecordType.A
}, },
@ -37,7 +36,7 @@ public class DnsTest
Console.WriteLine("sendt"); Console.WriteLine("sendt");
msg.IsRecursionDesired = false; msg.IsRecursionDesired = false;
msg.IsAuthoritative = true; msg.IsAuthoritative = false;
msg.MessageType = DnsMessageType.Reply; msg.MessageType = DnsMessageType.Reply;
msg.Opcode = DnsMessageOpcode.Status; msg.Opcode = DnsMessageOpcode.Status;
msg.ResponseCode = DnsResponseCode.NxDomain; msg.ResponseCode = DnsResponseCode.NxDomain;