Commit ce1f95a8 authored by Brickner_cp's avatar Brickner_cp

DNS.

Improve PacketBuilder (calculate each Layer's length once).
parent a31049c4
......@@ -13,6 +13,11 @@ namespace PcapDotNet.Base
public static class IEnumerableExtensions
// ReSharper restore InconsistentNaming
{
public static bool IsNullOrEmpty<T>(this IEnumerable<T> sequence)
{
return sequence == null || !sequence.Any();
}
/// <summary>
/// Concatenates a sequence with more values.
/// </summary>
......
using System;
using System.Collections.Generic;
namespace PcapDotNet.Base
{
public class InlineEqualityComparer<T> : IEqualityComparer<T>
{
public InlineEqualityComparer(Func<T, T, bool> equals, Func<T,int> getHashCode)
{
Equals = equals;
GetHashCode = getHashCode;
}
bool IEqualityComparer<T>.Equals(T x, T y)
{
return Equals(x, y);
}
int IEqualityComparer<T>.GetHashCode(T obj)
{
return GetHashCode(obj);
}
private Func<T, T, bool> Equals { get; set; }
private Func<T, int> GetHashCode { get; set; }
}
}
\ No newline at end of file
......@@ -91,6 +91,7 @@
<Compile Include="IDictionaryExtensions.cs" />
<Compile Include="IEnumerableExtensions.cs" />
<Compile Include="IListExtensions.cs" />
<Compile Include="InlineEqualityComparer.cs" />
<Compile Include="MatchExtensions.cs" />
<Compile Include="PropertyInfoExtensions.cs" />
<Compile Include="TimeSpanExtensions.cs" />
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using PcapDotNet.Base;
using PcapDotNet.Packets.Arp;
......@@ -73,5 +76,54 @@ namespace PcapDotNet.Packets.Test
Assert.AreEqual(dnsLayer, packet.Ethernet.IpV4.Udp.Dns.ExtractLayer(), "DNS Layer");
}
}
[TestMethod]
public void SimpleDnsTest()
{
DnsLayer dnsLayer = new DnsLayer
{
//Queries = new[] {new DnsQueryResourceRecord(new DnsDomainName("abc.def."), DnsType.A, DnsClass.In)}.ToList(),
Answers =
new[]
{
// new DnsDataResourceRecord(new DnsDomainName("abc.def."), DnsType.A, DnsClass.In, 100,
// new DnsResourceDataUnknown(new DataSegment(Encoding.ASCII.GetBytes("abcd")))),
// new DnsDataResourceRecord(new DnsDomainName("abc.def."), DnsType.A, DnsClass.In, 100,
// new DnsResourceDataUnknown(new DataSegment(Encoding.ASCII.GetBytes("abce")))),
new DnsDataResourceRecord(new DnsDomainName(""), DnsType.A, DnsClass.Any, 1, new DnsResourceDataUnknown(new DataSegment(new byte[0])))
}.ToList(),
// Authorities =
// new[]
// {
// new DnsDataResourceRecord(new DnsDomainName("def"), DnsType.Ns, DnsClass.In, 2222,
// new DnsResourceDataUnknown(new DataSegment(Encoding.ASCII.GetBytes("123"))))
// }.ToList(),
// Additionals =
// new[]
// {
// new DnsDataResourceRecord(new DnsDomainName(""), DnsType.A, DnsClass.In, 2222,
// new DnsResourceDataUnknown(new DataSegment(Encoding.ASCII.GetBytes("444")))),
// new DnsDataResourceRecord(new DnsDomainName("123"), DnsType.A, DnsClass.In, 2222,
// new DnsResourceDataUnknown(new DataSegment(Encoding.ASCII.GetBytes("444")))),
// }.ToList(),
// DomainNameCompressionMode = DnsDomainNameCompressionMode.All,
// FutureUse = 6,
// Id = 16365,
// IsAuthoritiveAnswer = false,
// IsQuery = false,
// IsRecusionAvailable = true,
// IsRecusionDesired = true,
// Opcode = DnsOpcode.Query,
// ResponseCode = DnsResponseCode.ServerFailure,
};
Packet packet = PacketBuilder.Build(DateTime.Now,
new EthernetLayer(), new IpV4Layer(), new UdpLayer(),
dnsLayer);
Assert.IsTrue(packet.IsValid, "IsValid");
// DNS
Assert.AreEqual(dnsLayer, packet.Ethernet.IpV4.Udp.Dns.ExtractLayer(), "DNS Layer");
}
}
}
\ No newline at end of file
......@@ -961,9 +961,72 @@ namespace PcapDotNet.Packets.TestUtils
public static DnsLayer NextDnsLayer(this Random random)
{
DnsLayer dnsLayer = new DnsLayer();
dnsLayer.Id = random.NextUShort();
dnsLayer.IsQuery = random.NextBool();
dnsLayer.Opcode = random.NextEnum<DnsOpcode>();
dnsLayer.IsAuthoritiveAnswer = random.NextBool();
dnsLayer.IsTruncated = random.NextBool();
dnsLayer.IsRecusionDesired = random.NextBool();
dnsLayer.IsRecusionAvailable = random.NextBool();
dnsLayer.FutureUse = random.NextByte(DnsDatagram.MaxFutureUse + 1);
dnsLayer.ResponseCode = random.NextEnum<DnsResponseCode>();
dnsLayer.DomainNameCompressionMode = random.NextEnum<DnsDomainNameCompressionMode>();
int numQueries = random.Next(10);
List<DnsQueryResourceRecord> queries = new List<DnsQueryResourceRecord>();
for (int i = 0; i != numQueries; ++i)
queries.Add(random.NextDnsQueryResourceRecord());
dnsLayer.Queries = queries;
int numAnswers = random.Next(10);
List<DnsDataResourceRecord> answers = new List<DnsDataResourceRecord>();
for (int i = 0; i != numAnswers; ++i)
answers.Add(random.NextDnsDataResourceRecord());
dnsLayer.Answers = answers;
int numAuthorities = random.Next(10);
List<DnsDataResourceRecord> authorities = new List<DnsDataResourceRecord>();
for (int i = 0; i != numAuthorities; ++i)
authorities.Add(random.NextDnsDataResourceRecord());
dnsLayer.Authorities = authorities;
int numAdditionals = random.Next(10);
List<DnsDataResourceRecord> additionals = new List<DnsDataResourceRecord>();
for (int i = 0; i != numAdditionals; ++i)
additionals.Add(random.NextDnsDataResourceRecord());
dnsLayer.Additionals = additionals;
return dnsLayer;
}
public static DnsQueryResourceRecord NextDnsQueryResourceRecord(this Random random)
{
DnsQueryResourceRecord record = new DnsQueryResourceRecord(random.NextDnsDomainName(), random.NextEnum<DnsType>(), random.NextEnum<DnsClass>());
return record;
}
public static DnsDataResourceRecord NextDnsDataResourceRecord(this Random random)
{
DnsDataResourceRecord record = new DnsDataResourceRecord(random.NextDnsDomainName(), random.NextEnum<DnsType>(), random.NextEnum<DnsClass>(), random.Next(), random.NextDnsResourceData());
return record;
}
public static DnsDomainName NextDnsDomainName(this Random random)
{
List<string> labels = new List<string>();
int numLabels = random.Next(10);
for (int i = 0; i != numLabels; ++i)
{
int labelLength = random.Next(10);
StringBuilder label = new StringBuilder();
for (int j = 0; j != labelLength; ++j)
label.Append(random.NextChar('a', 'z'));
labels.Add(label.ToString());
}
return new DnsDomainName(string.Join(".", labels));
}
public static DnsResourceData NextDnsResourceData(this Random random)
{
DnsResourceData resourceData = new DnsResourceDataUnknown(new DataSegment(random.NextBytes(random.Next(100))));
return resourceData;
}
// HTTP
public static HttpLayer NextHttpLayer(this Random random)
{
......
......@@ -85,7 +85,9 @@ namespace PcapDotNet.Packets.Dns
/// <summary>
/// The number of bytes the DNS header takes.
/// </summary>
public const int HeaderLength = 10;
public const int HeaderLength = 12;
public const byte MaxFutureUse = 7;
/// <summary>
/// A 16 bit identifier assigned by the program that generates any kind of query.
......@@ -156,7 +158,7 @@ namespace PcapDotNet.Packets.Dns
{
get { return ReadBool(Offset.IsRecusionAvailable, Mask.IsRecusionAvailable); }
}
/// <summary>
/// Reserved for future use.
/// Must be zero in all queries and responses.
......@@ -202,8 +204,8 @@ namespace PcapDotNet.Packets.Dns
{
get { return ReadUShort(Offset.AdditionalCount, Endianity.Big); }
}
public ReadOnlyCollection<DnsQueryResouceRecord> Queries
public ReadOnlyCollection<DnsQueryResourceRecord> Queries
{
get
{
......@@ -246,8 +248,22 @@ namespace PcapDotNet.Packets.Dns
{
return new DnsLayer
{
Id = Id,
IsQuery = IsQuery,
Opcode = Opcode,
IsAuthoritiveAnswer = IsAuthoritiveAnswer,
IsTruncated = IsTruncated,
IsRecusionDesired = IsRecusionDesired,
IsRecusionAvailable = IsRecusionAvailable,
FutureUse = FutureUse,
ResponseCode = ResponseCode,
Queries = Queries.ToList(),
Answers = Answers.ToList(),
Authorities = Authorities.ToList(),
Additionals = Additionals.ToList(),
};
}
/*
protected override bool CalculateIsValid()
{
......@@ -263,12 +279,68 @@ namespace PcapDotNet.Packets.Dns
{
int length = HeaderLength;
DnsDomainNameCompressionData compressionData = new DnsDomainNameCompressionData(domainNameCompressionMode);
foreach (DnsResourceRecord record in resourceRecords)
length += record.GetLength(compressionData, length);
if (resourceRecords != null)
{
foreach (DnsResourceRecord record in resourceRecords)
length += record.GetLength(compressionData, length);
}
return length;
}
internal static void Write(byte[] buffer, int offset,
ushort id, bool isResponse, DnsOpcode opcode, bool isAuthoritiveAnswer, bool isTruncated,
bool isRecursionDesired, bool isRecursionAvailable, byte futureUse, DnsResponseCode responseCode,
List<DnsQueryResourceRecord> queries, List<DnsDataResourceRecord> answers,
List<DnsDataResourceRecord> authorities, List<DnsDataResourceRecord> additionals,
DnsDomainNameCompressionMode domainNameCompressionMode)
{
buffer.Write(offset + Offset.Id, id, Endianity.Big);
byte flags0 = 0;
if (isResponse)
flags0 |= Mask.IsResponse;
flags0 |= (byte)((((byte)opcode) << Shift.Opcode) & Mask.Opcode);
if (isAuthoritiveAnswer)
flags0 |= Mask.IsAuthoritiveAnswer;
if (isTruncated)
flags0 |= Mask.IsTruncated;
if (isRecursionDesired)
flags0 |= Mask.IsRecusionDesired;
buffer.Write(offset + Offset.IsResponse, flags0);
byte flags1 = 0;
if (isRecursionAvailable)
flags1 |= Mask.IsRecusionAvailable;
flags1 |= (byte)((futureUse << Shift.FutureUse) & Mask.FutureUse);
flags1 |= (byte)((byte)responseCode & Mask.ResponseCode);
buffer.Write(offset + Offset.IsRecusionAvailable, flags1);
DnsDomainNameCompressionData compressionData = new DnsDomainNameCompressionData(domainNameCompressionMode);
int recordOffset = HeaderLength;
if (queries != null)
{
buffer.Write(offset + Offset.QueryCount, (ushort)queries.Count, Endianity.Big);
foreach (DnsQueryResourceRecord record in queries)
recordOffset += record.Write(buffer, offset, compressionData, recordOffset);
}
if (answers != null)
{
buffer.Write(offset + Offset.AnswerCount, (ushort)answers.Count, Endianity.Big);
foreach (DnsDataResourceRecord record in answers)
recordOffset += record.Write(buffer, offset, compressionData, recordOffset);
}
if (authorities != null)
{
buffer.Write(offset + Offset.AuthorityCount, (ushort)authorities.Count, Endianity.Big);
foreach (DnsDataResourceRecord record in authorities)
recordOffset += record.Write(buffer, offset, compressionData, recordOffset);
}
if (additionals != null)
{
buffer.Write(offset + Offset.AdditionalCount, (ushort)additionals.Count, Endianity.Big);
foreach (DnsDataResourceRecord record in additionals)
recordOffset += record.Write(buffer, offset, compressionData, recordOffset);
}
}
private int QueriesOffset
{
get { return HeaderLength; }
......@@ -303,7 +375,7 @@ namespace PcapDotNet.Packets.Dns
private void ParseQueries()
{
ParseRecords(QueriesOffset, () => QueryCount, DnsQueryResouceRecord.Parse, ref _queries, ref _answersOffset);
ParseRecords(QueriesOffset, () => QueryCount, DnsQueryResourceRecord.Parse, ref _queries, ref _answersOffset);
}
private void ParseAnswers()
......@@ -324,7 +396,7 @@ namespace PcapDotNet.Packets.Dns
private delegate TRecord ParseRecord<out TRecord>(DnsDatagram dns, int offset, out int numBytesRead);
private void ParseRecords<TRecord>(int offset, Func<ushort> countDelegate, ParseRecord<TRecord> parseRecord,
private void ParseRecords<TRecord>(int offset, Func<ushort> countDelegate, ParseRecord<TRecord> parseRecord,
ref ReadOnlyCollection<TRecord> parsedRecords, ref int nextOffset)
{
if (parsedRecords == null && Length >= offset)
......@@ -348,7 +420,7 @@ namespace PcapDotNet.Packets.Dns
}
}
private ReadOnlyCollection<DnsQueryResouceRecord> _queries;
private ReadOnlyCollection<DnsQueryResourceRecord> _queries;
private ReadOnlyCollection<DnsDataResourceRecord> _answers;
private ReadOnlyCollection<DnsDataResourceRecord> _authorities;
private ReadOnlyCollection<DnsDataResourceRecord> _additionals;
......
......@@ -12,7 +12,15 @@ namespace PcapDotNet.Packets.Dns
public class DnsDomainName
{
private const byte MaxLabelLength = 63;
private const ushort OffsetMask = 0xC000;
private const ushort CompressionMarker = 0xC000;
private const ushort OffsetMask = 0x3FFF;
private static readonly char[] Colon = new[] {'.'};
public DnsDomainName(string domainName)
{
string[] labels = domainName.ToLowerInvariant().Split(Colon, StringSplitOptions.RemoveEmptyEntries);
_labels = labels.Select(label => new DataSegment(Encoding.UTF8.GetBytes(label))).ToList();
}
public int NumLabels
{
......@@ -25,7 +33,7 @@ namespace PcapDotNet.Packets.Dns
public override string ToString()
{
if (_ascii == null)
_ascii = _labels.Select(label => label.ToString(Encoding.ASCII)).Concat(string.Empty).SequenceToString('.');
_ascii = _labels.Select(label => label.ToString(Encoding.UTF8)).SequenceToString('.') + ".";
return _ascii;
}
......@@ -37,8 +45,8 @@ namespace PcapDotNet.Packets.Dns
ListSegment<DataSegment> labels = new ListSegment<DataSegment>(_labels, i);
if (compressionData.IsAvailable(labels))
return length + sizeof(ushort);
length += sizeof(byte) + _labels[i].Length;
compressionData.AddCompressionData(labels, offsetInDns + length);
length += sizeof(byte) + labels[0].Length;
}
return length + sizeof(byte);
}
......@@ -50,6 +58,28 @@ namespace PcapDotNet.Packets.Dns
return new DnsDomainName(labels);
}
internal int Write(byte[] buffer, int dnsOffset, DnsDomainNameCompressionData compressionData, int offsetInDns)
{
int length = 0;
for (int i = 0; i != NumLabels; ++i)
{
ListSegment<DataSegment> labels = new ListSegment<DataSegment>(_labels, i);
int pointerOffset;
if (compressionData.TryGetOffset(labels, out pointerOffset))
{
buffer.Write(dnsOffset + offsetInDns + length, (ushort)(CompressionMarker | (ushort)pointerOffset), Endianity.Big);
return length + sizeof(ushort);
}
DataSegment currentLabel = labels[0];
compressionData.AddCompressionData(labels, offsetInDns + length);
buffer.Write(dnsOffset + offsetInDns + length, (byte)currentLabel.Length);
length += sizeof(byte);
currentLabel.Write(buffer, dnsOffset + offsetInDns + length);
length += currentLabel.Length;
}
return length + sizeof(byte);
}
private DnsDomainName(List<DataSegment> labels)
{
_labels = labels;
......@@ -84,6 +114,7 @@ namespace PcapDotNet.Packets.Dns
return;
labels.Add(dns.SubSegment(offsetInDns, labelLength));
numBytesRead += labelLength;
offsetInDns += labelLength;
}
} while (labelLength != 0);
}
......
......@@ -2,6 +2,7 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using PcapDotNet.Base;
namespace PcapDotNet.Packets.Dns
{
......@@ -115,32 +116,47 @@ namespace PcapDotNet.Packets.Dns
internal class DnsDomainNameCompressionData
{
private class LabelNode
{
public int? DnsOffset { get; private set; }
public bool IsAvailable(ListSegment<DataSegment> labels)
{
if (labels.Count == 0)
return DnsOffset != null;
LabelNode nextNode;
if (!_nextLabels.TryGetValue(labels[0], out nextNode))
return false;
return nextNode.IsAvailable(labels.SubSegment(1));
}
public void AddLabels(ListSegment<DataSegment> labels, int dnsOffset)
{
if (labels.Count == 0)
{
DnsOffset = dnsOffset;
return;
}
_nextLabels[labels[0]].AddLabels(labels.SubSegment(1), dnsOffset);
}
private readonly Dictionary<DataSegment, LabelNode> _nextLabels = new Dictionary<DataSegment, LabelNode>();
}
// private class LabelNode
// {
// public int? DnsOffset { get; private set; }
//
// public bool TryGetOffset(ListSegment<DataSegment> labels, out int offsetInDns)
// {
// if (labels.Count == 0)
// {
// offsetInDns = DnsOffset.GetValueOrDefault();
// return DnsOffset.HasValue;
// }
// LabelNode nextNode;
// if (!_nextLabels.TryGetValue(labels[0], out nextNode))
// {
// offsetInDns = 0;
// return false;
// }
// offsetInDns = nextNode.DnsOffset.Value;
// int nextNodeOffset;
// return nextNode.TryGetOffset(labels.SubSegment(1), out nextNodeOffset);
// }
//
// public void AddLabels(ListSegment<DataSegment> labels, int dnsOffset)
// {
// if (labels.Count == 0)
// {
// DnsOffset = dnsOffset;
// return;
// }
// DataSegment firstLabel = labels[0];
// LabelNode node;
// if (!_nextLabels.TryGetValue(firstLabel, out node))
// {
// node = new LabelNode();
// _nextLabels.Add(firstLabel, node);
// }
// node.AddLabels(labels.SubSegment(1), dnsOffset);
// }
//
// private readonly Dictionary<DataSegment, LabelNode> _nextLabels = new Dictionary<DataSegment, LabelNode>();
// }
public DnsDomainNameCompressionData(DnsDomainNameCompressionMode domainNameCompressionMode)
{
......@@ -150,12 +166,19 @@ namespace PcapDotNet.Packets.Dns
public DnsDomainNameCompressionMode DomainNameCompressionMode { get; private set; }
public bool IsAvailable(ListSegment<DataSegment> labels)
{
int offsetInDns;
return TryGetOffset(labels, out offsetInDns);
}
public bool TryGetOffset(ListSegment<DataSegment> labels, out int offsetInDns)
{
switch (DomainNameCompressionMode)
{
case DnsDomainNameCompressionMode.All:
return _root.IsAvailable(labels);
return _data.TryGetValue(labels, out offsetInDns);
case DnsDomainNameCompressionMode.Nothing:
offsetInDns = 0;
return false;
default:
throw new InvalidOperationException(string.Format("Invalid DomainNameCompressionMode {0}", DomainNameCompressionMode));
......@@ -167,7 +190,8 @@ namespace PcapDotNet.Packets.Dns
switch (DomainNameCompressionMode)
{
case DnsDomainNameCompressionMode.All:
_root.AddLabels(labels, dnsOffset);
if (!_data.ContainsKey(labels))
_data.Add(labels, dnsOffset);
return;
case DnsDomainNameCompressionMode.Nothing:
return;
......@@ -176,6 +200,9 @@ namespace PcapDotNet.Packets.Dns
}
}
private readonly LabelNode _root = new LabelNode();
private static readonly InlineEqualityComparer<ListSegment<DataSegment>> _labelsComparer =
new InlineEqualityComparer<ListSegment<DataSegment>>((x, y) => x.SequenceEqual(y), obj => obj.SequenceGetHashCode());
private readonly Dictionary<ListSegment<DataSegment>, int> _data = new Dictionary<ListSegment<DataSegment>, int>(_labelsComparer);
}
}
\ No newline at end of file
......@@ -10,9 +10,8 @@ namespace PcapDotNet.Packets.Dns
/// Represents a DNS layer.
/// <seealso cref="DnsDatagram"/>
/// </summary>
public class DnsLayer : Layer, IEquatable<DnsLayer>
public class DnsLayer : SimpleLayer, IEquatable<DnsLayer>
{
public ushort Id { get; set; }
public bool IsResponse { get; set; }
public bool IsQuery
......@@ -35,7 +34,7 @@ namespace PcapDotNet.Packets.Dns
public DnsResponseCode ResponseCode { get; set; }
public List<DnsQueryResouceRecord> Queries { get; set; }
public List<DnsQueryResourceRecord> Queries { get; set; }
public List<DnsDataResourceRecord> Answers { get; set; }
......@@ -45,7 +44,16 @@ namespace PcapDotNet.Packets.Dns
public IEnumerable<DnsResourceRecord> Records
{
get { return Queries.Concat<DnsResourceRecord>(Answers).Concat(Authorities).Concat(Additionals); }
get
{
IEnumerable<DnsResourceRecord> allRecords = null;
foreach (IEnumerable<DnsResourceRecord> records in new IEnumerable<DnsResourceRecord>[] {Queries,Answers,Authorities,Additionals})
{
if (records != null)
allRecords = allRecords == null ? records : allRecords.Concat(records);
}
return allRecords;
}
}
public DnsDomainNameCompressionMode DomainNameCompressionMode { get; set; }
......@@ -66,11 +74,11 @@ namespace PcapDotNet.Packets.Dns
/// </summary>
/// <param name="buffer">The buffer to write the layer to.</param>
/// <param name="offset">The offset in the buffer to start writing the layer at.</param>
/// <param name="payloadLength">The length of the layer's payload (the number of bytes after the layer in the packet).</param>
/// <param name="previousLayer">The layer that comes before this layer. null if this is the first layer.</param>
/// <param name="nextLayer">The layer that comes after this layer. null if this is the last layer.</param>
public override void Write(byte[] buffer, int offset, int payloadLength, ILayer previousLayer, ILayer nextLayer)
protected override void Write(byte[] buffer, int offset)
{
DnsDatagram.Write(buffer, offset,
Id, IsResponse, Opcode, IsAuthoritiveAnswer, IsTruncated, IsRecusionDesired, IsRecusionAvailable, FutureUse, ResponseCode,
Queries, Answers, Authorities, Additionals, DomainNameCompressionMode);
}
/// <summary>
......@@ -90,7 +98,20 @@ namespace PcapDotNet.Packets.Dns
/// </summary>
public bool Equals(DnsLayer other)
{
return other != null;
return other != null &&
Id == other.Id &&
IsQuery == other.IsQuery &&
Opcode == other.Opcode &&
IsAuthoritiveAnswer == other.IsAuthoritiveAnswer &&
IsTruncated == other.IsTruncated &&
IsRecusionDesired == other.IsRecusionDesired &&
IsRecusionAvailable == other.IsRecusionAvailable &&
FutureUse == other.FutureUse &&
ResponseCode == other.ResponseCode &&
(Queries.IsNullOrEmpty() == other.Queries.IsNullOrEmpty() || Queries.SequenceEqual(other.Queries)) &&
(Answers.IsNullOrEmpty() == other.Answers.IsNullOrEmpty() || Answers.SequenceEqual(other.Answers)) &&
(Authorities.IsNullOrEmpty() == other.Authorities.IsNullOrEmpty() || Authorities.SequenceEqual(other.Authorities)) &&
(Additionals.IsNullOrEmpty() == other.Additionals.IsNullOrEmpty() || Additionals.SequenceEqual(other.Additionals));
}
/// <summary>
......
......@@ -3,6 +3,14 @@ using System.Collections.Generic;
namespace PcapDotNet.Packets.Dns
{
// public enum DnsSection
// {
// Query,
// Answer,
// Authority,
// Additional,
// }
/// <summary>
/// RFC 1035.
/// All RRs have the same top level format shown below:
......@@ -43,6 +51,8 @@ namespace PcapDotNet.Packets.Dns
DnsClass = dnsClass;
}
// public abstract DnsSection Section { get; }
/// <summary>
/// An owner name, i.e., the name of the node to which this resource record pertains.
/// </summary>
......@@ -72,6 +82,11 @@ namespace PcapDotNet.Packets.Dns
/// </summary>
public abstract DnsResourceData Data { get; protected set; }
public override string ToString()
{
return DomainName + " " + Type + " " + DnsClass;
}
internal static bool TryParseBase(DnsDatagram dns, int offsetInDns,
out DnsDomainName domainName, out DnsType type, out DnsClass dnsClass, out int numBytesRead)
{
......@@ -81,11 +96,11 @@ namespace PcapDotNet.Packets.Dns
if (domainName == null)
return false;
if (offsetInDns + numBytesRead + MinimumLengthAfterDomainName >= dns.Length)
if (offsetInDns + numBytesRead + MinimumLengthAfterDomainName > dns.Length)
return false;
type = (DnsType)dns.ReadUShort(OffsetAfterDomainName.Type, Endianity.Big);
dnsClass = (DnsClass)dns.ReadUShort(OffsetAfterDomainName.DnsClass, Endianity.Big);
type = (DnsType)dns.ReadUShort(offsetInDns + numBytesRead + OffsetAfterDomainName.Type, Endianity.Big);
dnsClass = (DnsClass)dns.ReadUShort(offsetInDns + numBytesRead + OffsetAfterDomainName.DnsClass, Endianity.Big);
numBytesRead += MinimumLengthAfterDomainName;
return true;
}
......@@ -96,11 +111,22 @@ namespace PcapDotNet.Packets.Dns
}
internal abstract int GetLengthAfterBase(DnsDomainNameCompressionData compressionData, int offsetInDns);
internal virtual int Write(byte[] buffer, int dnsOffset, DnsDomainNameCompressionData compressionData, int offsetInDns)
{
int length = 0;
length += DomainName.Write(buffer, dnsOffset, compressionData, offsetInDns + length);
buffer.Write(dnsOffset + offsetInDns + length, (ushort)Type, Endianity.Big);
length += sizeof(ushort);
buffer.Write(dnsOffset + offsetInDns + length, (ushort)DnsClass, Endianity.Big);
length += sizeof(ushort);
return length;
}
}
public class DnsQueryResouceRecord : DnsResourceRecord
public class DnsQueryResourceRecord : DnsResourceRecord
{
public DnsQueryResouceRecord(DnsDomainName domainName, DnsType type, DnsClass dnsClass)
public DnsQueryResourceRecord(DnsDomainName domainName, DnsType type, DnsClass dnsClass)
: base(domainName, type, dnsClass)
{
}
......@@ -117,7 +143,7 @@ namespace PcapDotNet.Packets.Dns
protected set { throw new InvalidOperationException("No Resource Data in queries"); }
}
internal static DnsQueryResouceRecord Parse(DnsDatagram dns, int offsetInDns, out int numBytesRead)
internal static DnsQueryResourceRecord Parse(DnsDatagram dns, int offsetInDns, out int numBytesRead)
{
DnsDomainName domainName;
DnsType type;
......@@ -125,7 +151,7 @@ namespace PcapDotNet.Packets.Dns
if (!TryParseBase(dns, offsetInDns, out domainName, out type, out dnsClass, out numBytesRead))
return null;
return new DnsQueryResouceRecord(domainName, type, dnsClass);
return new DnsQueryResourceRecord(domainName, type, dnsClass);
}
internal override int GetLengthAfterBase(DnsDomainNameCompressionData compressionData, int offsetInDns)
......@@ -167,6 +193,11 @@ namespace PcapDotNet.Packets.Dns
/// </summary>
public override DnsResourceData Data { get; protected set; }
public override string ToString()
{
return base.ToString() + " " + Ttl + " " + Data;
}
internal static DnsDataResourceRecord Parse(DnsDatagram dns, int offsetInDns, out int numBytesRead)
{
DnsDomainName domainName;
......@@ -175,14 +206,14 @@ namespace PcapDotNet.Packets.Dns
if (!TryParseBase(dns, offsetInDns, out domainName, out type, out dnsClass, out numBytesRead))
return null;
offsetInDns += numBytesRead;
if (offsetInDns + numBytesRead + MinimumLengthAfterBase >= dns.Length)
if (offsetInDns + numBytesRead + MinimumLengthAfterBase > dns.Length)
return null;
int ttl = dns.ReadInt(offsetInDns + numBytesRead + OffsetAfterBase.Ttl, Endianity.Big);
ushort dataLength = dns.ReadUShort(offsetInDns + numBytesRead + OffsetAfterBase.DataLength, Endianity.Big);
numBytesRead += MinimumLengthAfterBase;
if (offsetInDns + numBytesRead + dataLength > dns.Length)
return null;
DnsResourceData data = DnsResourceData.Read(dns, offsetInDns + numBytesRead, dataLength);
if (data == null)
return null;
......@@ -195,6 +226,15 @@ namespace PcapDotNet.Packets.Dns
{
return MinimumLengthAfterBase + Data.GetLength(compressionData, offsetInDns);
}
internal override int Write(byte[] buffer, int dnsOffset, DnsDomainNameCompressionData compressionData, int offsetInDns)
{
int length = base.Write(buffer, dnsOffset, compressionData, offsetInDns);
buffer.Write(dnsOffset + offsetInDns + length, Ttl, Endianity.Big);
length += sizeof(uint);
length += Data.Write(buffer, dnsOffset, offsetInDns + length, compressionData);
return length;
}
}
public abstract class DnsResourceData
......@@ -205,6 +245,16 @@ namespace PcapDotNet.Packets.Dns
}
internal abstract int GetLength(DnsDomainNameCompressionData compressionData, int offsetInDns);
internal int Write(byte[] buffer, int dnsOffset, int offsetInDns, DnsDomainNameCompressionData compressionData)
{
int length = WriteData(buffer, dnsOffset, offsetInDns + sizeof(ushort), compressionData);
buffer.Write(dnsOffset + offsetInDns, (ushort)length, Endianity.Big);
length += sizeof(ushort);
return length;
}
internal abstract int WriteData(byte[] buffer, int dnsOffset, int offsetInDns, DnsDomainNameCompressionData compressionData);
}
public class DnsResourceDataUnknown : DnsResourceData
......@@ -220,5 +270,11 @@ namespace PcapDotNet.Packets.Dns
{
return Data.Length;
}
internal override int WriteData(byte[] buffer, int dnsOffset, int offsetInDns, DnsDomainNameCompressionData compressionData)
{
Data.Write(buffer, dnsOffset + offsetInDns);
return Data.Length;
}
}
}
\ No newline at end of file
......@@ -105,25 +105,27 @@ namespace PcapDotNet.Packets
/// <returns>A packet built from the builder's layers with the given timestamp.</returns>
public Packet Build(DateTime timestamp)
{
int length = _layers.Select(layer => layer.Length).Sum();
int[] layersLength = _layers.Select(layer => layer.Length).ToArray();
int length = layersLength.Sum();
byte[] buffer = new byte[length];
WriteLayers(buffer, length);
WriteLayers(layersLength, buffer, length);
FinalizeLayers(buffer, length);
return new Packet(buffer, timestamp, _dataLink);
}
private void WriteLayers(byte[] buffer, int length)
private void WriteLayers(int[] layersLength, byte[] buffer, int length)
{
int offset = 0;
for (int i = 0; i != _layers.Length; ++i)
{
ILayer layer = _layers[i];
int layerLength = layersLength[i];
ILayer previousLayer = i == 0 ? null : _layers[i - 1];
ILayer nextLayer = i == _layers.Length - 1 ? null : _layers[i + 1];
layer.Write(buffer, offset, length - offset - layer.Length, previousLayer, nextLayer);
offset += layer.Length;
layer.Write(buffer, offset, length - offset - layerLength, previousLayer, nextLayer);
offset += layerLength;
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment