diff --git a/src/IPMeow.Dhcp/AddressTable.cs b/src/IPMeow.Dhcp/AddressTable.cs index 04bc8a6..a311d72 100644 --- a/src/IPMeow.Dhcp/AddressTable.cs +++ b/src/IPMeow.Dhcp/AddressTable.cs @@ -11,6 +11,6 @@ public class AddressTable public IPAddress Next() { - throw new NotImplementedException(); + throw new NotImplementedException(); } } diff --git a/src/IPMeow.Dns/DnsMessage.cs b/src/IPMeow.Dns/DnsMessage.cs index f4cfa8f..ba599af 100644 --- a/src/IPMeow.Dns/DnsMessage.cs +++ b/src/IPMeow.Dns/DnsMessage.cs @@ -22,7 +22,7 @@ public struct DnsMessage : IBinarySerializable public DnsMessageType MessageType { get; set; } public DnsMessageOpcode Opcode { get; set; } public DnsResponseCode ResponseCode { get; set; } - public ICollection Questions { get; set; } + public ICollection Questions { get; set; } public static DnsMessage Deserialize(ReadOnlyMemory data) { diff --git a/src/IPMeow.Dns/ResourceRecord.cs b/src/IPMeow.Dns/ResourceRecord.cs index afbe698..6daa4c4 100644 --- a/src/IPMeow.Dns/ResourceRecord.cs +++ b/src/IPMeow.Dns/ResourceRecord.cs @@ -51,17 +51,44 @@ public enum ResourceRecordClass : short HS = 4, } -public struct ResourceRecord : IBinarySerializable +public record QueryResourceRecord : IBinarySerializable { - 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 ResourceRecordClass Class { get; set; } - public TimeSpan Ttl { get; set; } - public static ResourceRecord Deserialize(ReadOnlyMemory 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 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 SerializeName() @@ -73,7 +100,7 @@ public struct ResourceRecord : IBinarySerializable throw new ArgumentOutOfRangeException("Name must be less than 255"); } - ArrayBufferWriter buffer = new(name.Length + 1 + 4); + ArrayBufferWriter buffer = new(NameOffset); foreach (string s in name.Split('.')) { @@ -90,7 +117,7 @@ public struct ResourceRecord : IBinarySerializable return buffer.WrittenSpan; } - public ReadOnlyMemory Serialize() + public virtual ReadOnlyMemory Serialize() { var buffer = new ArrayBufferWriter(); @@ -99,11 +126,53 @@ public struct ResourceRecord : IBinarySerializable buffer.Advance(2); BinaryPrimitives.WriteInt16BigEndian(buffer.GetSpan(), (short)Class); buffer.Advance(2); - BinaryPrimitives.WriteInt32BigEndian(buffer.GetSpan(), (int)Ttl.TotalSeconds); - buffer.Advance(4); - //TODO: Implement RDATA - BinaryPrimitives.WriteInt16BigEndian(buffer.GetSpan(), 0); - buffer.Advance(2); + + return buffer.WrittenMemory; + } +} + +public record ResourceRecord : QueryResourceRecord, IBinarySerializable +{ + public TimeSpan Ttl { get; set; } + public byte[] Data { get; set; } = Array.Empty(); + + static ResourceRecord IBinarySerializable.Deserialize(ReadOnlyMemory 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 Serialize() + { + var buffer = new ArrayBufferWriter(); + 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; } } diff --git a/src/IPMeow.Lib/BitEnumerator.cs b/src/IPMeow.Lib/BitEnumerator.cs index 7839a67..fe84817 100644 --- a/src/IPMeow.Lib/BitEnumerator.cs +++ b/src/IPMeow.Lib/BitEnumerator.cs @@ -26,6 +26,11 @@ public class BitEnumerator return data.ToArray(); } + public void Skip(int len) + { + _position += len; + } + public short TakeInt16() { short num = BinaryPrimitives.ReadInt16BigEndian(_data.Span[_position..(_position + 2)]); diff --git a/test/IPMeow.Test/DnsTest.cs b/test/IPMeow.Test/DnsTest.cs index ff494f8..5ba84d4 100644 --- a/test/IPMeow.Test/DnsTest.cs +++ b/test/IPMeow.Test/DnsTest.cs @@ -20,12 +20,11 @@ public class DnsTest MessageId = 0xbabe, MessageType = DnsMessageType.Query, Opcode = DnsMessageOpcode.Query, - Questions = new List() + Questions = new List() { - new ResourceRecord() + new QueryResourceRecord() { Class = ResourceRecordClass.IN, - Ttl = TimeSpan.Zero, Name = "meow.lgbt", Type = ResourceRecordType.A }, @@ -37,7 +36,7 @@ public class DnsTest Console.WriteLine("sendt"); msg.IsRecursionDesired = false; - msg.IsAuthoritative = true; + msg.IsAuthoritative = false; msg.MessageType = DnsMessageType.Reply; msg.Opcode = DnsMessageOpcode.Status; msg.ResponseCode = DnsResponseCode.NxDomain;