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 ...@@ -13,6 +13,11 @@ namespace PcapDotNet.Base
public static class IEnumerableExtensions public static class IEnumerableExtensions
// ReSharper restore InconsistentNaming // ReSharper restore InconsistentNaming
{ {
public static bool IsNullOrEmpty<T>(this IEnumerable<T> sequence)
{
return sequence == null || !sequence.Any();
}
/// <summary> /// <summary>
/// Concatenates a sequence with more values. /// Concatenates a sequence with more values.
/// </summary> /// </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 @@ ...@@ -91,6 +91,7 @@
<Compile Include="IDictionaryExtensions.cs" /> <Compile Include="IDictionaryExtensions.cs" />
<Compile Include="IEnumerableExtensions.cs" /> <Compile Include="IEnumerableExtensions.cs" />
<Compile Include="IListExtensions.cs" /> <Compile Include="IListExtensions.cs" />
<Compile Include="InlineEqualityComparer.cs" />
<Compile Include="MatchExtensions.cs" /> <Compile Include="MatchExtensions.cs" />
<Compile Include="PropertyInfoExtensions.cs" /> <Compile Include="PropertyInfoExtensions.cs" />
<Compile Include="TimeSpanExtensions.cs" /> <Compile Include="TimeSpanExtensions.cs" />
......
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using PcapDotNet.Base; using PcapDotNet.Base;
using PcapDotNet.Packets.Arp; using PcapDotNet.Packets.Arp;
...@@ -73,5 +76,54 @@ namespace PcapDotNet.Packets.Test ...@@ -73,5 +76,54 @@ namespace PcapDotNet.Packets.Test
Assert.AreEqual(dnsLayer, packet.Ethernet.IpV4.Udp.Dns.ExtractLayer(), "DNS Layer"); 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 ...@@ -961,9 +961,72 @@ namespace PcapDotNet.Packets.TestUtils
public static DnsLayer NextDnsLayer(this Random random) public static DnsLayer NextDnsLayer(this Random random)
{ {
DnsLayer dnsLayer = new DnsLayer(); 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; 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 // HTTP
public static HttpLayer NextHttpLayer(this Random random) public static HttpLayer NextHttpLayer(this Random random)
{ {
......
...@@ -85,7 +85,9 @@ namespace PcapDotNet.Packets.Dns ...@@ -85,7 +85,9 @@ namespace PcapDotNet.Packets.Dns
/// <summary> /// <summary>
/// The number of bytes the DNS header takes. /// The number of bytes the DNS header takes.
/// </summary> /// </summary>
public const int HeaderLength = 10; public const int HeaderLength = 12;
public const byte MaxFutureUse = 7;
/// <summary> /// <summary>
/// A 16 bit identifier assigned by the program that generates any kind of query. /// A 16 bit identifier assigned by the program that generates any kind of query.
...@@ -203,7 +205,7 @@ namespace PcapDotNet.Packets.Dns ...@@ -203,7 +205,7 @@ namespace PcapDotNet.Packets.Dns
get { return ReadUShort(Offset.AdditionalCount, Endianity.Big); } get { return ReadUShort(Offset.AdditionalCount, Endianity.Big); }
} }
public ReadOnlyCollection<DnsQueryResouceRecord> Queries public ReadOnlyCollection<DnsQueryResourceRecord> Queries
{ {
get get
{ {
...@@ -246,8 +248,22 @@ namespace PcapDotNet.Packets.Dns ...@@ -246,8 +248,22 @@ namespace PcapDotNet.Packets.Dns
{ {
return new DnsLayer 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() protected override bool CalculateIsValid()
{ {
...@@ -263,12 +279,68 @@ namespace PcapDotNet.Packets.Dns ...@@ -263,12 +279,68 @@ namespace PcapDotNet.Packets.Dns
{ {
int length = HeaderLength; int length = HeaderLength;
DnsDomainNameCompressionData compressionData = new DnsDomainNameCompressionData(domainNameCompressionMode); DnsDomainNameCompressionData compressionData = new DnsDomainNameCompressionData(domainNameCompressionMode);
if (resourceRecords != null)
{
foreach (DnsResourceRecord record in resourceRecords) foreach (DnsResourceRecord record in resourceRecords)
length += record.GetLength(compressionData, length); length += record.GetLength(compressionData, length);
}
return 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 private int QueriesOffset
{ {
get { return HeaderLength; } get { return HeaderLength; }
...@@ -303,7 +375,7 @@ namespace PcapDotNet.Packets.Dns ...@@ -303,7 +375,7 @@ namespace PcapDotNet.Packets.Dns
private void ParseQueries() private void ParseQueries()
{ {
ParseRecords(QueriesOffset, () => QueryCount, DnsQueryResouceRecord.Parse, ref _queries, ref _answersOffset); ParseRecords(QueriesOffset, () => QueryCount, DnsQueryResourceRecord.Parse, ref _queries, ref _answersOffset);
} }
private void ParseAnswers() private void ParseAnswers()
...@@ -348,7 +420,7 @@ namespace PcapDotNet.Packets.Dns ...@@ -348,7 +420,7 @@ namespace PcapDotNet.Packets.Dns
} }
} }
private ReadOnlyCollection<DnsQueryResouceRecord> _queries; private ReadOnlyCollection<DnsQueryResourceRecord> _queries;
private ReadOnlyCollection<DnsDataResourceRecord> _answers; private ReadOnlyCollection<DnsDataResourceRecord> _answers;
private ReadOnlyCollection<DnsDataResourceRecord> _authorities; private ReadOnlyCollection<DnsDataResourceRecord> _authorities;
private ReadOnlyCollection<DnsDataResourceRecord> _additionals; private ReadOnlyCollection<DnsDataResourceRecord> _additionals;
......
...@@ -12,7 +12,15 @@ namespace PcapDotNet.Packets.Dns ...@@ -12,7 +12,15 @@ namespace PcapDotNet.Packets.Dns
public class DnsDomainName public class DnsDomainName
{ {
private const byte MaxLabelLength = 63; 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 public int NumLabels
{ {
...@@ -25,7 +33,7 @@ namespace PcapDotNet.Packets.Dns ...@@ -25,7 +33,7 @@ namespace PcapDotNet.Packets.Dns
public override string ToString() public override string ToString()
{ {
if (_ascii == null) 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; return _ascii;
} }
...@@ -37,8 +45,8 @@ namespace PcapDotNet.Packets.Dns ...@@ -37,8 +45,8 @@ namespace PcapDotNet.Packets.Dns
ListSegment<DataSegment> labels = new ListSegment<DataSegment>(_labels, i); ListSegment<DataSegment> labels = new ListSegment<DataSegment>(_labels, i);
if (compressionData.IsAvailable(labels)) if (compressionData.IsAvailable(labels))
return length + sizeof(ushort); return length + sizeof(ushort);
length += sizeof(byte) + _labels[i].Length;
compressionData.AddCompressionData(labels, offsetInDns + length); compressionData.AddCompressionData(labels, offsetInDns + length);
length += sizeof(byte) + labels[0].Length;
} }
return length + sizeof(byte); return length + sizeof(byte);
} }
...@@ -50,6 +58,28 @@ namespace PcapDotNet.Packets.Dns ...@@ -50,6 +58,28 @@ namespace PcapDotNet.Packets.Dns
return new DnsDomainName(labels); 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) private DnsDomainName(List<DataSegment> labels)
{ {
_labels = labels; _labels = labels;
...@@ -84,6 +114,7 @@ namespace PcapDotNet.Packets.Dns ...@@ -84,6 +114,7 @@ namespace PcapDotNet.Packets.Dns
return; return;
labels.Add(dns.SubSegment(offsetInDns, labelLength)); labels.Add(dns.SubSegment(offsetInDns, labelLength));
numBytesRead += labelLength; numBytesRead += labelLength;
offsetInDns += labelLength;
} }
} while (labelLength != 0); } while (labelLength != 0);
} }
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using PcapDotNet.Base;
namespace PcapDotNet.Packets.Dns namespace PcapDotNet.Packets.Dns
{ {
...@@ -115,32 +116,47 @@ namespace PcapDotNet.Packets.Dns ...@@ -115,32 +116,47 @@ namespace PcapDotNet.Packets.Dns
internal class DnsDomainNameCompressionData internal class DnsDomainNameCompressionData
{ {
private class LabelNode // private class LabelNode
{ // {
public int? DnsOffset { get; private set; } // public int? DnsOffset { get; private set; }
//
public bool IsAvailable(ListSegment<DataSegment> labels) // public bool TryGetOffset(ListSegment<DataSegment> labels, out int offsetInDns)
{ // {
if (labels.Count == 0) // if (labels.Count == 0)
return DnsOffset != null; // {
LabelNode nextNode; // offsetInDns = DnsOffset.GetValueOrDefault();
if (!_nextLabels.TryGetValue(labels[0], out nextNode)) // return DnsOffset.HasValue;
return false; // }
return nextNode.IsAvailable(labels.SubSegment(1)); // LabelNode nextNode;
} // if (!_nextLabels.TryGetValue(labels[0], out nextNode))
// {
public void AddLabels(ListSegment<DataSegment> labels, int dnsOffset) // offsetInDns = 0;
{ // return false;
if (labels.Count == 0) // }
{ // offsetInDns = nextNode.DnsOffset.Value;
DnsOffset = dnsOffset; // int nextNodeOffset;
return; // return nextNode.TryGetOffset(labels.SubSegment(1), out nextNodeOffset);
} // }
_nextLabels[labels[0]].AddLabels(labels.SubSegment(1), dnsOffset); //
} // public void AddLabels(ListSegment<DataSegment> labels, int dnsOffset)
// {
private readonly Dictionary<DataSegment, LabelNode> _nextLabels = new Dictionary<DataSegment, LabelNode>(); // 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) public DnsDomainNameCompressionData(DnsDomainNameCompressionMode domainNameCompressionMode)
{ {
...@@ -150,12 +166,19 @@ namespace PcapDotNet.Packets.Dns ...@@ -150,12 +166,19 @@ namespace PcapDotNet.Packets.Dns
public DnsDomainNameCompressionMode DomainNameCompressionMode { get; private set; } public DnsDomainNameCompressionMode DomainNameCompressionMode { get; private set; }
public bool IsAvailable(ListSegment<DataSegment> labels) public bool IsAvailable(ListSegment<DataSegment> labels)
{
int offsetInDns;
return TryGetOffset(labels, out offsetInDns);
}
public bool TryGetOffset(ListSegment<DataSegment> labels, out int offsetInDns)
{ {
switch (DomainNameCompressionMode) switch (DomainNameCompressionMode)
{ {
case DnsDomainNameCompressionMode.All: case DnsDomainNameCompressionMode.All:
return _root.IsAvailable(labels); return _data.TryGetValue(labels, out offsetInDns);
case DnsDomainNameCompressionMode.Nothing: case DnsDomainNameCompressionMode.Nothing:
offsetInDns = 0;
return false; return false;
default: default:
throw new InvalidOperationException(string.Format("Invalid DomainNameCompressionMode {0}", DomainNameCompressionMode)); throw new InvalidOperationException(string.Format("Invalid DomainNameCompressionMode {0}", DomainNameCompressionMode));
...@@ -167,7 +190,8 @@ namespace PcapDotNet.Packets.Dns ...@@ -167,7 +190,8 @@ namespace PcapDotNet.Packets.Dns
switch (DomainNameCompressionMode) switch (DomainNameCompressionMode)
{ {
case DnsDomainNameCompressionMode.All: case DnsDomainNameCompressionMode.All:
_root.AddLabels(labels, dnsOffset); if (!_data.ContainsKey(labels))
_data.Add(labels, dnsOffset);
return; return;
case DnsDomainNameCompressionMode.Nothing: case DnsDomainNameCompressionMode.Nothing:
return; return;
...@@ -176,6 +200,9 @@ namespace PcapDotNet.Packets.Dns ...@@ -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 ...@@ -10,9 +10,8 @@ namespace PcapDotNet.Packets.Dns
/// Represents a DNS layer. /// Represents a DNS layer.
/// <seealso cref="DnsDatagram"/> /// <seealso cref="DnsDatagram"/>
/// </summary> /// </summary>
public class DnsLayer : Layer, IEquatable<DnsLayer> public class DnsLayer : SimpleLayer, IEquatable<DnsLayer>
{ {
public ushort Id { get; set; } public ushort Id { get; set; }
public bool IsResponse { get; set; } public bool IsResponse { get; set; }
public bool IsQuery public bool IsQuery
...@@ -35,7 +34,7 @@ namespace PcapDotNet.Packets.Dns ...@@ -35,7 +34,7 @@ namespace PcapDotNet.Packets.Dns
public DnsResponseCode ResponseCode { get; set; } public DnsResponseCode ResponseCode { get; set; }
public List<DnsQueryResouceRecord> Queries { get; set; } public List<DnsQueryResourceRecord> Queries { get; set; }
public List<DnsDataResourceRecord> Answers { get; set; } public List<DnsDataResourceRecord> Answers { get; set; }
...@@ -45,7 +44,16 @@ namespace PcapDotNet.Packets.Dns ...@@ -45,7 +44,16 @@ namespace PcapDotNet.Packets.Dns
public IEnumerable<DnsResourceRecord> Records 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; } public DnsDomainNameCompressionMode DomainNameCompressionMode { get; set; }
...@@ -66,11 +74,11 @@ namespace PcapDotNet.Packets.Dns ...@@ -66,11 +74,11 @@ namespace PcapDotNet.Packets.Dns
/// </summary> /// </summary>
/// <param name="buffer">The buffer to write the layer to.</param> /// <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="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> protected override void Write(byte[] buffer, int offset)
/// <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)
{ {
DnsDatagram.Write(buffer, offset,
Id, IsResponse, Opcode, IsAuthoritiveAnswer, IsTruncated, IsRecusionDesired, IsRecusionAvailable, FutureUse, ResponseCode,
Queries, Answers, Authorities, Additionals, DomainNameCompressionMode);
} }
/// <summary> /// <summary>
...@@ -90,7 +98,20 @@ namespace PcapDotNet.Packets.Dns ...@@ -90,7 +98,20 @@ namespace PcapDotNet.Packets.Dns
/// </summary> /// </summary>
public bool Equals(DnsLayer other) 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> /// <summary>
......
...@@ -3,6 +3,14 @@ using System.Collections.Generic; ...@@ -3,6 +3,14 @@ using System.Collections.Generic;
namespace PcapDotNet.Packets.Dns namespace PcapDotNet.Packets.Dns
{ {
// public enum DnsSection
// {
// Query,
// Answer,
// Authority,
// Additional,
// }
/// <summary> /// <summary>
/// RFC 1035. /// RFC 1035.
/// All RRs have the same top level format shown below: /// All RRs have the same top level format shown below:
...@@ -43,6 +51,8 @@ namespace PcapDotNet.Packets.Dns ...@@ -43,6 +51,8 @@ namespace PcapDotNet.Packets.Dns
DnsClass = dnsClass; DnsClass = dnsClass;
} }
// public abstract DnsSection Section { get; }
/// <summary> /// <summary>
/// An owner name, i.e., the name of the node to which this resource record pertains. /// An owner name, i.e., the name of the node to which this resource record pertains.
/// </summary> /// </summary>
...@@ -72,6 +82,11 @@ namespace PcapDotNet.Packets.Dns ...@@ -72,6 +82,11 @@ namespace PcapDotNet.Packets.Dns
/// </summary> /// </summary>
public abstract DnsResourceData Data { get; protected set; } public abstract DnsResourceData Data { get; protected set; }
public override string ToString()
{
return DomainName + " " + Type + " " + DnsClass;
}
internal static bool TryParseBase(DnsDatagram dns, int offsetInDns, internal static bool TryParseBase(DnsDatagram dns, int offsetInDns,
out DnsDomainName domainName, out DnsType type, out DnsClass dnsClass, out int numBytesRead) out DnsDomainName domainName, out DnsType type, out DnsClass dnsClass, out int numBytesRead)
{ {
...@@ -81,11 +96,11 @@ namespace PcapDotNet.Packets.Dns ...@@ -81,11 +96,11 @@ namespace PcapDotNet.Packets.Dns
if (domainName == null) if (domainName == null)
return false; return false;
if (offsetInDns + numBytesRead + MinimumLengthAfterDomainName >= dns.Length) if (offsetInDns + numBytesRead + MinimumLengthAfterDomainName > dns.Length)
return false; return false;
type = (DnsType)dns.ReadUShort(OffsetAfterDomainName.Type, Endianity.Big); type = (DnsType)dns.ReadUShort(offsetInDns + numBytesRead + OffsetAfterDomainName.Type, Endianity.Big);
dnsClass = (DnsClass)dns.ReadUShort(OffsetAfterDomainName.DnsClass, Endianity.Big); dnsClass = (DnsClass)dns.ReadUShort(offsetInDns + numBytesRead + OffsetAfterDomainName.DnsClass, Endianity.Big);
numBytesRead += MinimumLengthAfterDomainName; numBytesRead += MinimumLengthAfterDomainName;
return true; return true;
} }
...@@ -96,11 +111,22 @@ namespace PcapDotNet.Packets.Dns ...@@ -96,11 +111,22 @@ namespace PcapDotNet.Packets.Dns
} }
internal abstract int GetLengthAfterBase(DnsDomainNameCompressionData compressionData, int offsetInDns); 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) : base(domainName, type, dnsClass)
{ {
} }
...@@ -117,7 +143,7 @@ namespace PcapDotNet.Packets.Dns ...@@ -117,7 +143,7 @@ namespace PcapDotNet.Packets.Dns
protected set { throw new InvalidOperationException("No Resource Data in queries"); } 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; DnsDomainName domainName;
DnsType type; DnsType type;
...@@ -125,7 +151,7 @@ namespace PcapDotNet.Packets.Dns ...@@ -125,7 +151,7 @@ namespace PcapDotNet.Packets.Dns
if (!TryParseBase(dns, offsetInDns, out domainName, out type, out dnsClass, out numBytesRead)) if (!TryParseBase(dns, offsetInDns, out domainName, out type, out dnsClass, out numBytesRead))
return null; return null;
return new DnsQueryResouceRecord(domainName, type, dnsClass); return new DnsQueryResourceRecord(domainName, type, dnsClass);
} }
internal override int GetLengthAfterBase(DnsDomainNameCompressionData compressionData, int offsetInDns) internal override int GetLengthAfterBase(DnsDomainNameCompressionData compressionData, int offsetInDns)
...@@ -167,6 +193,11 @@ namespace PcapDotNet.Packets.Dns ...@@ -167,6 +193,11 @@ namespace PcapDotNet.Packets.Dns
/// </summary> /// </summary>
public override DnsResourceData Data { get; protected set; } 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) internal static DnsDataResourceRecord Parse(DnsDatagram dns, int offsetInDns, out int numBytesRead)
{ {
DnsDomainName domainName; DnsDomainName domainName;
...@@ -175,14 +206,14 @@ namespace PcapDotNet.Packets.Dns ...@@ -175,14 +206,14 @@ namespace PcapDotNet.Packets.Dns
if (!TryParseBase(dns, offsetInDns, out domainName, out type, out dnsClass, out numBytesRead)) if (!TryParseBase(dns, offsetInDns, out domainName, out type, out dnsClass, out numBytesRead))
return null; return null;
offsetInDns += numBytesRead; if (offsetInDns + numBytesRead + MinimumLengthAfterBase > dns.Length)
if (offsetInDns + numBytesRead + MinimumLengthAfterBase >= dns.Length)
return null; return null;
int ttl = dns.ReadInt(offsetInDns + numBytesRead + OffsetAfterBase.Ttl, Endianity.Big); int ttl = dns.ReadInt(offsetInDns + numBytesRead + OffsetAfterBase.Ttl, Endianity.Big);
ushort dataLength = dns.ReadUShort(offsetInDns + numBytesRead + OffsetAfterBase.DataLength, Endianity.Big); ushort dataLength = dns.ReadUShort(offsetInDns + numBytesRead + OffsetAfterBase.DataLength, Endianity.Big);
numBytesRead += MinimumLengthAfterBase; numBytesRead += MinimumLengthAfterBase;
if (offsetInDns + numBytesRead + dataLength > dns.Length)
return null;
DnsResourceData data = DnsResourceData.Read(dns, offsetInDns + numBytesRead, dataLength); DnsResourceData data = DnsResourceData.Read(dns, offsetInDns + numBytesRead, dataLength);
if (data == null) if (data == null)
return null; return null;
...@@ -195,6 +226,15 @@ namespace PcapDotNet.Packets.Dns ...@@ -195,6 +226,15 @@ namespace PcapDotNet.Packets.Dns
{ {
return MinimumLengthAfterBase + Data.GetLength(compressionData, offsetInDns); 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 public abstract class DnsResourceData
...@@ -205,6 +245,16 @@ namespace PcapDotNet.Packets.Dns ...@@ -205,6 +245,16 @@ namespace PcapDotNet.Packets.Dns
} }
internal abstract int GetLength(DnsDomainNameCompressionData compressionData, int offsetInDns); 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 public class DnsResourceDataUnknown : DnsResourceData
...@@ -220,5 +270,11 @@ namespace PcapDotNet.Packets.Dns ...@@ -220,5 +270,11 @@ namespace PcapDotNet.Packets.Dns
{ {
return Data.Length; 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 ...@@ -105,25 +105,27 @@ namespace PcapDotNet.Packets
/// <returns>A packet built from the builder's layers with the given timestamp.</returns> /// <returns>A packet built from the builder's layers with the given timestamp.</returns>
public Packet Build(DateTime timestamp) 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]; byte[] buffer = new byte[length];
WriteLayers(buffer, length); WriteLayers(layersLength, buffer, length);
FinalizeLayers(buffer, length); FinalizeLayers(buffer, length);
return new Packet(buffer, timestamp, _dataLink); 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; int offset = 0;
for (int i = 0; i != _layers.Length; ++i) for (int i = 0; i != _layers.Length; ++i)
{ {
ILayer layer = _layers[i]; ILayer layer = _layers[i];
int layerLength = layersLength[i];
ILayer previousLayer = i == 0 ? null : _layers[i - 1]; ILayer previousLayer = i == 0 ? null : _layers[i - 1];
ILayer nextLayer = i == _layers.Length - 1 ? null : _layers[i + 1]; ILayer nextLayer = i == _layers.Length - 1 ? null : _layers[i + 1];
layer.Write(buffer, offset, length - offset - layer.Length, previousLayer, nextLayer); layer.Write(buffer, offset, length - offset - layerLength, previousLayer, nextLayer);
offset += layer.Length; 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