Commit 74a03367 authored by Brickner_cp's avatar Brickner_cp

DNS

parent cd46a194
......@@ -26,6 +26,7 @@
</MODIFIERS_ORDER>
<SPACE_AFTER_TYPECAST_PARENTHESES>False</SPACE_AFTER_TYPECAST_PARENTHESES>
<SPACE_AROUND_MULTIPLICATIVE_OP>True</SPACE_AROUND_MULTIPLICATIVE_OP>
<SPACE_BEFORE_SIZEOF_PARENTHESES>False</SPACE_BEFORE_SIZEOF_PARENTHESES>
<WRAP_LIMIT>160</WRAP_LIMIT>
</FormatSettings>
<UsingsSettings />
......
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using PcapDotNet.Base;
using PcapDotNet.Packets.Arp;
using PcapDotNet.Packets.Dns;
using PcapDotNet.Packets.Ethernet;
using PcapDotNet.Packets.IpV4;
using PcapDotNet.Packets.TestUtils;
using PcapDotNet.Packets.Transport;
namespace PcapDotNet.Packets.Test
{
/// <summary>
/// Summary description for DnsTests
/// </summary>
[TestClass]
public class DnsTests
{
/// <summary>
/// Gets or sets the test context which provides
/// information about and functionality for the current test run.
/// </summary>
public TestContext TestContext { get; set; }
#region Additional test attributes
//
// You can use the following additional attributes as you write your tests:
//
// Use ClassInitialize to run code before running the first test in the class
// [ClassInitialize()]
// public static void MyClassInitialize(TestContext testContext) { }
//
// Use ClassCleanup to run code after all tests in a class have run
// [ClassCleanup()]
// public static void MyClassCleanup() { }
//
// Use TestInitialize to run code before running each test
// [TestInitialize()]
// public void MyTestInitialize() { }
//
// Use TestCleanup to run code after each test has run
// [TestCleanup()]
// public void MyTestCleanup() { }
//
#endregion
[TestMethod]
public void RandomDnsTest()
{
EthernetLayer ethernetLayer = new EthernetLayer
{
Source = new MacAddress("00:01:02:03:04:05"),
Destination = new MacAddress("A0:A1:A2:A3:A4:A5")
};
Random random = new Random();
IpV4Layer ipV4Layer = random.NextIpV4Layer(null);
ipV4Layer.HeaderChecksum = null;
UdpLayer udpLayer = random.NextUdpLayer();
udpLayer.Checksum = null;
for (int i = 0; i != 1000; ++i)
{
DnsLayer dnsLayer = random.NextDnsLayer();
Packet packet = PacketBuilder.Build(DateTime.Now, ethernetLayer, ipV4Layer, 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
......@@ -71,6 +71,7 @@
<Compile Include="ByteArrayExtensionsTests.cs" />
<Compile Include="DatagramTests.cs" />
<Compile Include="DataLinkTests.cs" />
<Compile Include="DnsTests.cs" />
<Compile Include="EndianitiyTests.cs" />
<Compile Include="EthernetTests.cs" />
<Compile Include="GreTests.cs" />
......
......@@ -4,6 +4,7 @@ using System.Linq;
using System.Text;
using PcapDotNet.Base;
using PcapDotNet.Packets.Arp;
using PcapDotNet.Packets.Dns;
using PcapDotNet.Packets.Ethernet;
using PcapDotNet.Packets.Gre;
using PcapDotNet.Packets.Http;
......@@ -956,6 +957,13 @@ namespace PcapDotNet.Packets.TestUtils
yield return new IcmpRouterAdvertisementEntry(random.NextIpV4Address(), random.Next());
}
// DNS
public static DnsLayer NextDnsLayer(this Random random)
{
DnsLayer dnsLayer = new DnsLayer();
return dnsLayer;
}
// HTTP
public static HttpLayer NextHttpLayer(this Random random)
{
......
......@@ -157,6 +157,11 @@ namespace PcapDotNet.Packets
return Buffer.ReadBytes(StartOffset + offset, length);
}
internal DataSegment SubSegment(int offset, int length)
{
return new DataSegment(Buffer, StartOffset + offset, length);
}
internal bool ReadBool(int offset, byte mask)
{
return (this[offset] & mask) == mask;
......@@ -181,7 +186,7 @@ namespace PcapDotNet.Packets
/// <param name="endianity">The endianity to use to translate the bytes to the value.</param>
/// <returns>The value converted from the read bytes according to the endianity.</returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "int")]
protected int ReadInt(int offset, Endianity endianity)
internal int ReadInt(int offset, Endianity endianity)
{
return Buffer.ReadInt(StartOffset + offset, endianity);
}
......
namespace PcapDotNet.Packets.Dns
{
/// <summary>
/// A domain name represented as a series of labels, and terminated by a label with zero length.
/// </summary>
public class DnsDomainName
{
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PcapDotNet.Base;
namespace PcapDotNet.Packets.Dns
{
/// <summary>
/// A domain name represented as a series of labels, and terminated by a label with zero length.
/// </summary>
public class DnsDomainName
{
private const byte MaxLabelLength = 63;
private const ushort OffsetMask = 0xC000;
public int NumLabels
{
get
{
return _labels.Count;
}
}
public override string ToString()
{
if (_ascii == null)
_ascii = _labels.Select(label => label.ToString(Encoding.ASCII)).Concat(string.Empty).SequenceToString('.');
return _ascii;
}
internal int GetLength(DnsDomainNameCompressionData compressionData, int offsetInDns)
{
int length = 0;
for (int i = 0; i != NumLabels; ++i)
{
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);
}
return length + sizeof(byte);
}
internal static DnsDomainName Parse(DnsDatagram dns, int offsetInDns, out int numBytesRead)
{
List<DataSegment> labels = new List<DataSegment>();
ReadLabels(dns, offsetInDns, out numBytesRead, labels);
return new DnsDomainName(labels);
}
private DnsDomainName(List<DataSegment> labels)
{
_labels = labels;
}
private static void ReadLabels(DnsDatagram dns, int offsetInDns, out int numBytesRead, List<DataSegment> labels)
{
numBytesRead = 0;
byte labelLength;
do
{
if (offsetInDns >= dns.Length)
return;
labelLength = dns[offsetInDns];
++numBytesRead;
if (labelLength > MaxLabelLength)
{
// Compression.
if (offsetInDns + 1 >= dns.Length)
return;
offsetInDns = dns.ReadUShort(offsetInDns, Endianity.Big) & OffsetMask;
++numBytesRead;
int internalBytesRead;
ReadLabels(dns, offsetInDns, out internalBytesRead, labels);
return;
}
if (labelLength != 0)
{
++offsetInDns;
if (offsetInDns + labelLength >= dns.Length)
return;
labels.Add(dns.SubSegment(offsetInDns, labelLength));
numBytesRead += labelLength;
}
} while (labelLength != 0);
}
private readonly List<DataSegment> _labels;
private string _ascii;
}
}
\ No newline at end of file
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace PcapDotNet.Packets.Dns
{
public class ListSegment<T> : IList<T>
{
public ListSegment(IList<T> data, int startIndex, int count)
{
_data = data;
_startIndex = startIndex;
Count = count;
}
public ListSegment(IList<T> data, int startIndex)
: this(data, startIndex, data.Count - startIndex)
{
}
public IEnumerator<T> GetEnumerator()
{
for (int i = 0; i != Count; ++i)
yield return this[i];
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Add(T item)
{
throw new NotSupportedException("ListSegment<T> is read-only");
}
public void Clear()
{
throw new NotSupportedException("ListSegment<T> is read-only");
}
public bool Contains(T item)
{
return Enumerable.Contains(this, item);
}
public void CopyTo(T[] array, int arrayIndex)
{
foreach (T value in this)
array[arrayIndex++] = value;
}
public bool Remove(T item)
{
throw new NotSupportedException("ListSegment<T> is read-only");
}
public int Count { get; private set; }
public bool IsReadOnly
{
get { return true; }
}
public int IndexOf(T item)
{
if (ReferenceEquals(item, null))
{
for (int i = 0; i != Count; ++i)
{
if (ReferenceEquals(this[i], null))
return i;
}
return -1;
}
for (int i = 0; i != Count; ++i)
{
if (item.Equals(this[i]))
return i;
}
return -1;
}
public void Insert(int index, T item)
{
throw new NotSupportedException("ListSegment<T> is read-only");
}
public void RemoveAt(int index)
{
throw new NotSupportedException("ListSegment<T> is read-only");
}
public T this[int index]
{
get { return _data[_startIndex + index]; }
set { throw new NotSupportedException("ListSegment<T> is read-only"); }
}
public ListSegment<T> SubSegment(int startIndex, int count)
{
return new ListSegment<T>(_data, _startIndex + startIndex, count);
}
public ListSegment<T> SubSegment(int startIndex)
{
return SubSegment(startIndex, Count - startIndex);
}
private readonly IList<T> _data;
private readonly int _startIndex;
}
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>();
}
public DnsDomainNameCompressionData(DnsDomainNameCompressionMode domainNameCompressionMode)
{
DomainNameCompressionMode = domainNameCompressionMode;
}
public DnsDomainNameCompressionMode DomainNameCompressionMode { get; private set; }
public bool IsAvailable(ListSegment<DataSegment> labels)
{
switch (DomainNameCompressionMode)
{
case DnsDomainNameCompressionMode.All:
return _root.IsAvailable(labels);
case DnsDomainNameCompressionMode.Nothing:
return false;
default:
throw new InvalidOperationException(string.Format("Invalid DomainNameCompressionMode {0}", DomainNameCompressionMode));
}
}
public void AddCompressionData(ListSegment<DataSegment> labels, int dnsOffset)
{
switch (DomainNameCompressionMode)
{
case DnsDomainNameCompressionMode.All:
_root.AddLabels(labels, dnsOffset);
return;
case DnsDomainNameCompressionMode.Nothing:
return;
default:
throw new InvalidOperationException(string.Format("Invalid DomainNameCompressionMode {0}", DomainNameCompressionMode));
}
}
private readonly LabelNode _root = new LabelNode();
}
}
\ No newline at end of file
namespace PcapDotNet.Packets.Dns
{
public enum DnsDomainNameCompressionMode
{
All,
Nothing
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Linq;
using PcapDotNet.Base;
using PcapDotNet.Packets.Ethernet;
namespace PcapDotNet.Packets.Dns
{
/// <summary>
/// Represents a DNS layer.
/// <seealso cref="DnsDatagram"/>
/// </summary>
public class DnsLayer : Layer, IEquatable<DnsLayer>
{
public ushort Id { get; set; }
public bool IsResponse { get; set; }
public bool IsQuery
{
get { return !IsResponse; }
set { IsResponse = !value; }
}
public DnsOpcode Opcode{ get; set; }
public bool IsAuthoritiveAnswer { get; set; }
public bool IsTruncated { get; set; }
public bool IsRecusionDesired { get; set; }
public bool IsRecusionAvailable { get; set;}
public byte FutureUse { get; set; }
public DnsResponseCode ResponseCode { get; set; }
public List<DnsQueryResouceRecord> Queries { get; set; }
public List<DnsDataResourceRecord> Answers { get; set; }
public List<DnsDataResourceRecord> Authorities { get; set; }
public List<DnsDataResourceRecord> Additionals { get; set; }
public IEnumerable<DnsResourceRecord> Records
{
get { return Queries.Concat<DnsResourceRecord>(Answers).Concat(Authorities).Concat(Additionals); }
}
public DnsDomainNameCompressionMode DomainNameCompressionMode { get; set; }
/// <summary>
/// The number of bytes this layer will take.
/// </summary>
public override int Length
{
get
{
return DnsDatagram.GetLength(Records, DomainNameCompressionMode);
}
}
/// <summary>
/// Writes the layer to the buffer.
/// </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)
{
}
/// <summary>
/// Finalizes the layer data in the buffer.
/// Used for fields that must be calculated according to the layer's payload (like checksum).
/// </summary>
/// <param name="buffer">The buffer to finalize the layer in.</param>
/// <param name="offset">The offset in the buffer the layer starts.</param>
/// <param name="payloadLength">The length of the layer's payload (the number of bytes after the layer in the packet).</param>
/// <param name="nextLayer">The layer that comes after this layer. null if this is the last layer.</param>
public override void Finalize(byte[] buffer, int offset, int payloadLength, ILayer nextLayer)
{
}
/// <summary>
/// True iff the two objects are equal Layers.
/// </summary>
public bool Equals(DnsLayer other)
{
return other != null;
}
/// <summary>
/// True iff the two objects are equal Layers.
/// </summary>
public override bool Equals(Layer other)
{
return Equals(other as DnsLayer);
}
}
}
\ No newline at end of file
namespace PcapDotNet.Packets.Dns
{
/// <summary>
/// Specifies kind of query in this message.
/// This value is set by the originator of a query and copied into the response.
/// </summary>
public enum DnsOpcode : byte
{
/// <summary>
/// A standard query (QUERY).
/// </summary>
Query = 0,
/// <summary>
/// An inverse query (IQUERY).
/// </summary>
IQuery = 1,
/// <summary>
/// A server status request (STATUS).
/// </summary>
Status = 2,
}
}
\ No newline at end of file
namespace PcapDotNet.Packets.Dns
{
public enum DnsResponseCode : byte
{
/// <summary>
/// No error condition
/// </summary>
NoError = 0,
/// <summary>
/// Format error - The name server was unable to interpret the query.
/// </summary>
FormatError = 1,
/// <summary>
/// Server failure - The name server was unable to process this query due to a problem with the name server.
/// </summary>
ServerFailure = 2,
/// <summary>
/// Name Error - Meaningful only for responses from an authoritative name server,
/// this code signifies that the domain name referenced in the query does not exist.
/// </summary>
NameError = 3,
/// <summary>
/// Not Implemented - The name server does not support the requested kind of query.
/// </summary>
NotImplemented = 4,
/// <summary>
/// Refused - The name server refuses to perform the specified operation for policy reasons.
/// For example, a name server may not wish to provide the information to the particular requester,
/// or a name server may not wish to perform a particular operation (e.g., zone transfer) for particular data.
/// </summary>
Refused = 5,
}
}
\ No newline at end of file
......@@ -94,9 +94,14 @@
<Compile Include="DataLinkKind.cs" />
<Compile Include="DataSegment.cs" />
<Compile Include="Dns\DnsClass.cs" />
<Compile Include="Dns\DnsDomain.cs" />
<Compile Include="Dns\DnsDomainName.cs" />
<Compile Include="Dns\DnsDatagram.cs" />
<Compile Include="Dns\DnsDomainNameCompressionData.cs" />
<Compile Include="Dns\DnsDomainNameCompressionMode.cs" />
<Compile Include="Dns\DnsLayer.cs" />
<Compile Include="Dns\DnsOpcode.cs" />
<Compile Include="Dns\DnsResourceRecord.cs" />
<Compile Include="Dns\DnsResponseCode.cs" />
<Compile Include="Dns\DnsType.cs" />
<Compile Include="Endianity.cs" />
<Compile Include="Ethernet\EthernetLayer.cs" />
......
using PcapDotNet.Packets.Dns;
namespace PcapDotNet.Packets.Transport
{
/// <summary>
......@@ -93,6 +95,20 @@ namespace PcapDotNet.Packets.Transport
get { return new Datagram(Buffer, StartOffset + HeaderLength, Length - HeaderLength); }
}
/// <summary>
/// The payload of the datagram as a DNS datagram.
/// </summary>
public DnsDatagram Dns
{
get
{
if (_dns == null && Length >= HeaderLength)
_dns = new DnsDatagram(Buffer, StartOffset + HeaderLength, Length - HeaderLength);
return _dns;
}
}
internal override int ChecksumOffset
{
get { return Offset.Checksum; }
......@@ -116,5 +132,7 @@ namespace PcapDotNet.Packets.Transport
{
return Length >= HeaderLength;
}
private DnsDatagram _dns;
}
}
\ No newline at end of file
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