Commit 74a03367 authored by Brickner_cp's avatar Brickner_cp

DNS

parent cd46a194
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
</MODIFIERS_ORDER> </MODIFIERS_ORDER>
<SPACE_AFTER_TYPECAST_PARENTHESES>False</SPACE_AFTER_TYPECAST_PARENTHESES> <SPACE_AFTER_TYPECAST_PARENTHESES>False</SPACE_AFTER_TYPECAST_PARENTHESES>
<SPACE_AROUND_MULTIPLICATIVE_OP>True</SPACE_AROUND_MULTIPLICATIVE_OP> <SPACE_AROUND_MULTIPLICATIVE_OP>True</SPACE_AROUND_MULTIPLICATIVE_OP>
<SPACE_BEFORE_SIZEOF_PARENTHESES>False</SPACE_BEFORE_SIZEOF_PARENTHESES>
<WRAP_LIMIT>160</WRAP_LIMIT> <WRAP_LIMIT>160</WRAP_LIMIT>
</FormatSettings> </FormatSettings>
<UsingsSettings /> <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 @@ ...@@ -71,6 +71,7 @@
<Compile Include="ByteArrayExtensionsTests.cs" /> <Compile Include="ByteArrayExtensionsTests.cs" />
<Compile Include="DatagramTests.cs" /> <Compile Include="DatagramTests.cs" />
<Compile Include="DataLinkTests.cs" /> <Compile Include="DataLinkTests.cs" />
<Compile Include="DnsTests.cs" />
<Compile Include="EndianitiyTests.cs" /> <Compile Include="EndianitiyTests.cs" />
<Compile Include="EthernetTests.cs" /> <Compile Include="EthernetTests.cs" />
<Compile Include="GreTests.cs" /> <Compile Include="GreTests.cs" />
......
...@@ -4,6 +4,7 @@ using System.Linq; ...@@ -4,6 +4,7 @@ using System.Linq;
using System.Text; using System.Text;
using PcapDotNet.Base; using PcapDotNet.Base;
using PcapDotNet.Packets.Arp; using PcapDotNet.Packets.Arp;
using PcapDotNet.Packets.Dns;
using PcapDotNet.Packets.Ethernet; using PcapDotNet.Packets.Ethernet;
using PcapDotNet.Packets.Gre; using PcapDotNet.Packets.Gre;
using PcapDotNet.Packets.Http; using PcapDotNet.Packets.Http;
...@@ -956,6 +957,13 @@ namespace PcapDotNet.Packets.TestUtils ...@@ -956,6 +957,13 @@ namespace PcapDotNet.Packets.TestUtils
yield return new IcmpRouterAdvertisementEntry(random.NextIpV4Address(), random.Next()); yield return new IcmpRouterAdvertisementEntry(random.NextIpV4Address(), random.Next());
} }
// DNS
public static DnsLayer NextDnsLayer(this Random random)
{
DnsLayer dnsLayer = new DnsLayer();
return dnsLayer;
}
// HTTP // HTTP
public static HttpLayer NextHttpLayer(this Random random) public static HttpLayer NextHttpLayer(this Random random)
{ {
......
...@@ -157,6 +157,11 @@ namespace PcapDotNet.Packets ...@@ -157,6 +157,11 @@ namespace PcapDotNet.Packets
return Buffer.ReadBytes(StartOffset + offset, length); 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) internal bool ReadBool(int offset, byte mask)
{ {
return (this[offset] & mask) == mask; return (this[offset] & mask) == mask;
...@@ -181,7 +186,7 @@ namespace PcapDotNet.Packets ...@@ -181,7 +186,7 @@ namespace PcapDotNet.Packets
/// <param name="endianity">The endianity to use to translate the bytes to the value.</param> /// <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> /// <returns>The value converted from the read bytes according to the endianity.</returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "int")] [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); return Buffer.ReadInt(StartOffset + offset, endianity);
} }
......
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq;
namespace PcapDotNet.Packets.Dns namespace PcapDotNet.Packets.Dns
{ {
...@@ -84,99 +87,167 @@ namespace PcapDotNet.Packets.Dns ...@@ -84,99 +87,167 @@ namespace PcapDotNet.Packets.Dns
/// </summary> /// </summary>
public const int HeaderLength = 10; public const int HeaderLength = 10;
/// <summary>
/// A 16 bit identifier assigned by the program that generates any kind of query.
/// This identifier is copied the corresponding reply and can be used by the requester to match up replies to outstanding queries.
/// </summary>
public ushort Id public ushort Id
{ {
get { return ReadUShort(Offset.Id, Endianity.Big); } get { return ReadUShort(Offset.Id, Endianity.Big); }
} }
/// <summary>
/// A one bit field that specifies whether this message is a query (0), or a response (1).
/// </summary>
public bool IsResponse public bool IsResponse
{ {
get { return ReadBool(Offset.IsResponse, Mask.IsResponse); } get { return ReadBool(Offset.IsResponse, Mask.IsResponse); }
} }
/// <summary>
/// Specifies whether this message is a query or a response.
/// </summary>
public bool IsQuery public bool IsQuery
{ {
get { return !IsResponse; } get { return !IsResponse; }
} }
public byte Opcode /// <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 DnsOpcode Opcode
{ {
get { return (byte)((this[Offset.Opcode] & Mask.Opcode) >> Shift.Opcode); } get { return (DnsOpcode)((this[Offset.Opcode] & Mask.Opcode) >> Shift.Opcode); }
} }
/// <summary>
/// This bit is valid in responses, and specifies that the responding name server is an authority for the domain name in question section.
/// Note that the contents of the answer section may have multiple owner names because of aliases.
/// The AA bit corresponds to the name which matches the query name, or the first owner name in the answer section.
/// </summary>
public bool IsAuthoritiveAnswer public bool IsAuthoritiveAnswer
{ {
get { return ReadBool(Offset.IsAuthoritiveAnswer, Mask.IsAuthoritiveAnswer); } get { return ReadBool(Offset.IsAuthoritiveAnswer, Mask.IsAuthoritiveAnswer); }
} }
/// <summary>
/// Specifies that this message was truncated due to length greater than that permitted on the transmission channel.
/// </summary>
public bool IsTruncated public bool IsTruncated
{ {
get { return ReadBool(Offset.IsTruncated, Mask.IsTruncated); } get { return ReadBool(Offset.IsTruncated, Mask.IsTruncated); }
} }
/// <summary>
/// This bit may be set in a query and is copied into the response.
/// If RD is set, it directs the name server to pursue the query recursively.
/// Recursive query support is optional.
/// </summary>
public bool IsRecusionDesired public bool IsRecusionDesired
{ {
get { return ReadBool(Offset.IsRecusionDesired, Mask.IsRecusionDesired); } get { return ReadBool(Offset.IsRecusionDesired, Mask.IsRecusionDesired); }
} }
/// <summary>
/// This be is set or cleared in a response, and denotes whether recursive query support is available in the name server.
/// </summary>
public bool IsRecusionAvailable public bool IsRecusionAvailable
{ {
get { return ReadBool(Offset.IsRecusionAvailable, Mask.IsRecusionAvailable); } get { return ReadBool(Offset.IsRecusionAvailable, Mask.IsRecusionAvailable); }
} }
/// <summary>
/// Reserved for future use.
/// Must be zero in all queries and responses.
/// </summary>
public byte FutureUse public byte FutureUse
{ {
get { return (byte)((this[Offset.FutureUse] & Mask.FutureUse) >> Shift.FutureUse); } get { return (byte)((this[Offset.FutureUse] & Mask.FutureUse) >> Shift.FutureUse); }
} }
public byte ResponseCode public DnsResponseCode ResponseCode
{ {
get { return (byte)(this[Offset.ResponseCode] & Mask.ResponseCode); } get { return (DnsResponseCode)(this[Offset.ResponseCode] & Mask.ResponseCode); }
} }
/// <summary>
/// An unsigned 16 bit integer specifying the number of entries in the question section.
/// </summary>
public ushort QueryCount public ushort QueryCount
{ {
get { return ReadUShort(Offset.QueryCount, Endianity.Big); } get { return ReadUShort(Offset.QueryCount, Endianity.Big); }
} }
/// <summary>
/// An unsigned 16 bit integer specifying the number of resource records in the answer section.
/// </summary>
public ushort AnswerCount public ushort AnswerCount
{ {
get { return ReadUShort(Offset.AnswerCount, Endianity.Big); } get { return ReadUShort(Offset.AnswerCount, Endianity.Big); }
} }
/// <summary>
/// An unsigned 16 bit integer specifying the number of name server resource records in the authority records section.
/// </summary>
public ushort AuthorityCount public ushort AuthorityCount
{ {
get { return ReadUShort(Offset.AuthorityCount, Endianity.Big); } get { return ReadUShort(Offset.AuthorityCount, Endianity.Big); }
} }
/// <summary>
/// An unsigned 16 bit integer specifying the number of resource records in the additional records section.
/// </summary>
public ushort AdditionalCount public ushort AdditionalCount
{ {
get { return ReadUShort(Offset.AdditionalCount, Endianity.Big); } get { return ReadUShort(Offset.AdditionalCount, Endianity.Big); }
} }
/*
public ReadOnlyCollection<DnsResourceRecord> Query public ReadOnlyCollection<DnsQueryResouceRecord> Queries
{
get
{ {
ParseQueries();
return _queries;
}
}
public ReadOnlyCollection<DnsDataResourceRecord> Answers
{
get
{
ParseAnswers();
return _answers;
}
}
public ReadOnlyCollection<DnsDataResourceRecord> Authorities
{
get
{
ParseAuthorities();
return _authorities;
}
}
public ReadOnlyCollection<DnsDataResourceRecord> Additionals
{
get
{
ParseAdditionals();
return _additionals;
}
} }
*/
/// <summary> /// <summary>
/// Creates a Layer that represents the datagram to be used with PacketBuilder. /// Creates a Layer that represents the datagram to be used with PacketBuilder.
/// </summary> /// </summary>
/*
public override ILayer ExtractLayer() public override ILayer ExtractLayer()
{ {
return new ArpLayer return new DnsLayer
{ {
SenderHardwareAddress = SenderHardwareAddress,
SenderProtocolAddress = SenderProtocolAddress,
TargetHardwareAddress = TargetHardwareAddress,
TargetProtocolAddress = TargetProtocolAddress,
ProtocolType = ProtocolType,
Operation = Operation,
}; };
} }
*/
/* /*
protected override bool CalculateIsValid() protected override bool CalculateIsValid()
{ {
...@@ -187,5 +258,103 @@ namespace PcapDotNet.Packets.Dns ...@@ -187,5 +258,103 @@ namespace PcapDotNet.Packets.Dns
: base(buffer, offset, length) : base(buffer, offset, length)
{ {
} }
internal static int GetLength(IEnumerable<DnsResourceRecord> resourceRecords, DnsDomainNameCompressionMode domainNameCompressionMode)
{
int length = HeaderLength;
DnsDomainNameCompressionData compressionData = new DnsDomainNameCompressionData(domainNameCompressionMode);
foreach (DnsResourceRecord record in resourceRecords)
length += record.GetLength(compressionData, length);
return length;
}
private int QueriesOffset
{
get { return HeaderLength; }
}
private int AnswersOffset
{
get
{
ParseQueries();
return _answersOffset;
}
}
private int AuthoritiesOffset
{
get
{
ParseAnswers();
return _authoritiesOffset;
}
}
private int AdditionalsOffset
{
get
{
ParseAuthorities();
return _additionalsOffset;
}
}
private void ParseQueries()
{
ParseRecords(QueriesOffset, () => QueryCount, DnsQueryResouceRecord.Parse, ref _queries, ref _answersOffset);
}
private void ParseAnswers()
{
ParseRecords(AnswersOffset, () => AnswerCount, DnsDataResourceRecord.Parse, ref _answers, ref _authoritiesOffset);
}
private void ParseAuthorities()
{
ParseRecords(AuthoritiesOffset, () => AuthorityCount, DnsDataResourceRecord.Parse, ref _authorities, ref _additionalsOffset);
}
private void ParseAdditionals()
{
int nextOffset = 0;
ParseRecords(AdditionalsOffset, () => AdditionalCount, DnsDataResourceRecord.Parse, ref _additionals, ref nextOffset);
}
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,
ref ReadOnlyCollection<TRecord> parsedRecords, ref int nextOffset)
{
if (parsedRecords == null && Length >= offset)
{
ushort count = countDelegate();
List<TRecord> records = new List<TRecord>(count);
for (int i = 0; i != count; ++i)
{
int numBytesRead;
TRecord record = parseRecord(this, offset, out numBytesRead);
if (record == null)
{
offset = 0;
break;
}
records.Add(record);
offset += numBytesRead;
}
parsedRecords = new ReadOnlyCollection<TRecord>(records.ToArray());
nextOffset = offset;
}
}
private ReadOnlyCollection<DnsQueryResouceRecord> _queries;
private ReadOnlyCollection<DnsDataResourceRecord> _answers;
private ReadOnlyCollection<DnsDataResourceRecord> _authorities;
private ReadOnlyCollection<DnsDataResourceRecord> _additionals;
private int _answersOffset;
private int _authoritiesOffset;
private int _additionalsOffset;
} }
} }
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
using System; using System;
using System.Collections.Generic;
namespace PcapDotNet.Packets.Dns namespace PcapDotNet.Packets.Dns
{ {
...@@ -6,34 +7,40 @@ namespace PcapDotNet.Packets.Dns ...@@ -6,34 +7,40 @@ namespace PcapDotNet.Packets.Dns
/// RFC 1035. /// RFC 1035.
/// All RRs have the same top level format shown below: /// All RRs have the same top level format shown below:
/// <pre> /// <pre>
/// +------+------------------------+ /// +------+-------------------------------------------------+
/// | byte | 0-1 | /// | byte | 0-1 |
/// +------+------------------------+ /// +------+-------------------------------------------------+
/// | 0 | Name | /// | 0 | Name |
/// | ... | | /// | ... | |
/// +------+------------------------+ /// +------+-------------------------------------------------+
/// | | Type | /// | | Type |
/// +------+------------------------+ /// +------+-------------------------------------------------+
/// | | Class | /// | | Class |
/// +------+------------------------+ /// +------+-------------------------------------------------+
/// | | TTL | /// | | TTL (not available in queries) |
/// | | | /// | | |
/// +------+------------------------+ /// +------+-------------------------------------------------+
/// | | Resource Data Length | /// | | Resource Data Length (not available in queries) |
/// +------+------------------------+ /// +------+-------------------------------------------------+
/// | | Resource Data | /// | | Resource Data (not available in queries) |
/// | ... | | /// | ... | |
/// +------+------------------------+ /// +------+-------------------------------------------------+
/// </pre> /// </pre>
public class DnsResourceRecord public abstract class DnsResourceRecord
{ {
public DnsResourceRecord(DnsDomainName domainName, DnsType type, DnsClass dnsClass, int ttl, DataSegment data) private static class OffsetAfterDomainName
{
public const int Type = 0;
public const int DnsClass = 2;
}
private const int MinimumLengthAfterDomainName = 4;
public DnsResourceRecord(DnsDomainName domainName, DnsType type, DnsClass dnsClass)
{ {
DomainName = domainName; DomainName = domainName;
Type = type; Type = type;
DnsClass = dnsClass; DnsClass = dnsClass;
Ttl = ttl;
Data = data;
} }
/// <summary> /// <summary>
...@@ -53,15 +60,165 @@ namespace PcapDotNet.Packets.Dns ...@@ -53,15 +60,165 @@ namespace PcapDotNet.Packets.Dns
/// Zero values are interpreted to mean that the RR can only be used for the transaction in progress, and should not be cached. /// Zero values are interpreted to mean that the RR can only be used for the transaction in progress, and should not be cached.
/// For example, SOA records are always distributed with a zero TTL to prohibit caching. /// For example, SOA records are always distributed with a zero TTL to prohibit caching.
/// Zero values can also be used for extremely volatile data. /// Zero values can also be used for extremely volatile data.
/// Not available in queries.
/// </summary> /// </summary>
public int Ttl { get; private set; } public abstract int Ttl { get; protected set; }
public ushort DataLength { get { return (ushort)Data.Length; } } /// <summary>
/// A variable length string of octets that describes the resource.
/// The format of this information varies according to the TYPE and CLASS of the resource record.
/// For example, the if the TYPE is A and the CLASS is IN, the RDATA field is a 4 octet ARPA Internet address.
/// Not available in queries.
/// </summary>
public abstract DnsResourceData Data { get; protected set; }
internal static bool TryParseBase(DnsDatagram dns, int offsetInDns,
out DnsDomainName domainName, out DnsType type, out DnsClass dnsClass, out int numBytesRead)
{
type = DnsType.All;
dnsClass = DnsClass.Any;
domainName = DnsDomainName.Parse(dns, offsetInDns, out numBytesRead);
if (domainName == null)
return false;
if (offsetInDns + numBytesRead + MinimumLengthAfterDomainName >= dns.Length)
return false;
type = (DnsType)dns.ReadUShort(OffsetAfterDomainName.Type, Endianity.Big);
dnsClass = (DnsClass)dns.ReadUShort(OffsetAfterDomainName.DnsClass, Endianity.Big);
numBytesRead += MinimumLengthAfterDomainName;
return true;
}
internal int GetLength(DnsDomainNameCompressionData compressionData, int offsetInDns)
{
return DomainName.GetLength(compressionData, offsetInDns) + MinimumLengthAfterDomainName + GetLengthAfterBase(compressionData, offsetInDns);
}
internal abstract int GetLengthAfterBase(DnsDomainNameCompressionData compressionData, int offsetInDns);
}
public class DnsQueryResouceRecord : DnsResourceRecord
{
public DnsQueryResouceRecord(DnsDomainName domainName, DnsType type, DnsClass dnsClass)
: base(domainName, type, dnsClass)
{
}
public override int Ttl
{
get { throw new InvalidOperationException("No TTL in queries"); }
protected set { throw new InvalidOperationException("No TTL in queries"); }
}
public override DnsResourceData Data
{
get { 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)
{
DnsDomainName domainName;
DnsType type;
DnsClass dnsClass;
if (!TryParseBase(dns, offsetInDns, out domainName, out type, out dnsClass, out numBytesRead))
return null;
return new DnsQueryResouceRecord(domainName, type, dnsClass);
}
internal override int GetLengthAfterBase(DnsDomainNameCompressionData compressionData, int offsetInDns)
{
return 0;
}
}
public class DnsDataResourceRecord : DnsResourceRecord
{
private static class OffsetAfterBase
{
public const int Ttl = 0;
public const int DataLength = 4;
public const int Data = 6;
}
private const int MinimumLengthAfterBase = 6;
public DnsDataResourceRecord(DnsDomainName domainName, DnsType type, DnsClass dnsClass, int ttl, DnsResourceData data)
: base(domainName, type, dnsClass)
{
Ttl = ttl;
Data = data;
}
/// <summary>
/// A 32 bit signed integer that specifies the time interval that the resource record may be cached before the source of the information should again be consulted.
/// Zero values are interpreted to mean that the RR can only be used for the transaction in progress, and should not be cached.
/// For example, SOA records are always distributed with a zero TTL to prohibit caching.
/// Zero values can also be used for extremely volatile data.
/// </summary>
public override int Ttl { get; protected set; }
/// <summary> /// <summary>
/// A variable length string of octets that describes the resource. /// A variable length string of octets that describes the resource.
/// The format of this information varies according to the TYPE and CLASS of the resource record. /// The format of this information varies according to the TYPE and CLASS of the resource record.
/// For example, the if the TYPE is A and the CLASS is IN, the RDATA field is a 4 octet ARPA Internet address.
/// </summary> /// </summary>
public override DnsResourceData Data { get; protected set; }
internal static DnsDataResourceRecord Parse(DnsDatagram dns, int offsetInDns, out int numBytesRead)
{
DnsDomainName domainName;
DnsType type;
DnsClass dnsClass;
if (!TryParseBase(dns, offsetInDns, out domainName, out type, out dnsClass, out numBytesRead))
return null;
offsetInDns += numBytesRead;
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;
DnsResourceData data = DnsResourceData.Read(dns, offsetInDns + numBytesRead, dataLength);
if (data == null)
return null;
numBytesRead += dataLength;
return new DnsDataResourceRecord(domainName, type, dnsClass, ttl, data);
}
internal override int GetLengthAfterBase(DnsDomainNameCompressionData compressionData, int offsetInDns)
{
return MinimumLengthAfterBase + Data.GetLength(compressionData, offsetInDns);
}
}
public abstract class DnsResourceData
{
internal static DnsResourceData Read(DnsDatagram dns, int offsetInDns, int dataLength)
{
return new DnsResourceDataUnknown(dns.SubSegment(offsetInDns, dataLength));
}
internal abstract int GetLength(DnsDomainNameCompressionData compressionData, int offsetInDns);
}
public class DnsResourceDataUnknown : DnsResourceData
{
public DnsResourceDataUnknown(DataSegment data)
{
Data = data;
}
public DataSegment Data { get; private set; } public DataSegment Data { get; private set; }
internal override int GetLength(DnsDomainNameCompressionData compressionData, int offsetInDns)
{
return Data.Length;
}
} }
} }
\ 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 @@ ...@@ -94,9 +94,14 @@
<Compile Include="DataLinkKind.cs" /> <Compile Include="DataLinkKind.cs" />
<Compile Include="DataSegment.cs" /> <Compile Include="DataSegment.cs" />
<Compile Include="Dns\DnsClass.cs" /> <Compile Include="Dns\DnsClass.cs" />
<Compile Include="Dns\DnsDomain.cs" /> <Compile Include="Dns\DnsDomainName.cs" />
<Compile Include="Dns\DnsDatagram.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\DnsResourceRecord.cs" />
<Compile Include="Dns\DnsResponseCode.cs" />
<Compile Include="Dns\DnsType.cs" /> <Compile Include="Dns\DnsType.cs" />
<Compile Include="Endianity.cs" /> <Compile Include="Endianity.cs" />
<Compile Include="Ethernet\EthernetLayer.cs" /> <Compile Include="Ethernet\EthernetLayer.cs" />
......
using PcapDotNet.Packets.Dns;
namespace PcapDotNet.Packets.Transport namespace PcapDotNet.Packets.Transport
{ {
/// <summary> /// <summary>
...@@ -93,6 +95,20 @@ namespace PcapDotNet.Packets.Transport ...@@ -93,6 +95,20 @@ namespace PcapDotNet.Packets.Transport
get { return new Datagram(Buffer, StartOffset + HeaderLength, Length - HeaderLength); } 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 internal override int ChecksumOffset
{ {
get { return Offset.Checksum; } get { return Offset.Checksum; }
...@@ -116,5 +132,7 @@ namespace PcapDotNet.Packets.Transport ...@@ -116,5 +132,7 @@ namespace PcapDotNet.Packets.Transport
{ {
return Length >= HeaderLength; 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