347 lines
15 KiB
C#
347 lines
15 KiB
C#
/**********************************************************************
|
|
* Copyright (c) 2010, j. montgomery *
|
|
* All rights reserved. *
|
|
* *
|
|
* Redistribution and use in source and binary forms, with or without *
|
|
* modification, are permitted provided that the following conditions *
|
|
* are met: *
|
|
* *
|
|
* + Redistributions of source code must retain the above copyright *
|
|
* notice, this list of conditions and the following disclaimer. *
|
|
* *
|
|
* + Redistributions in binary form must reproduce the above copyright*
|
|
* notice, this list of conditions and the following disclaimer *
|
|
* in the documentation and/or other materials provided with the *
|
|
* distribution. *
|
|
* *
|
|
* + Neither the name of j. montgomery's employer nor the names of *
|
|
* its contributors may be used to endorse or promote products *
|
|
* derived from this software without specific prior written *
|
|
* permission. *
|
|
* *
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
|
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
|
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
|
|
* OF THE POSSIBILITY OF SUCH DAMAGE. *
|
|
**********************************************************************/
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using System.IO;
|
|
using System.Net;
|
|
using System.Diagnostics;
|
|
|
|
namespace DnDns.Records
|
|
{
|
|
/// <summary>
|
|
/// Handles a basic Dns record
|
|
///
|
|
/// From RFC 1035:
|
|
///
|
|
/// 3.2.1. Format
|
|
///
|
|
/// All RRs have the same top level format shown below:
|
|
///
|
|
/// 1 1 1 1 1 1
|
|
/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
|
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
|
/// | |
|
|
/// / /
|
|
/// / NAME /
|
|
/// | |
|
|
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
|
/// | TYPE |
|
|
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
|
/// | CLASS |
|
|
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
|
/// | TTL |
|
|
/// | |
|
|
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
|
/// | RDLENGTH |
|
|
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
|
|
/// / RDATA /
|
|
/// / /
|
|
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
|
///
|
|
/// where:
|
|
///
|
|
/// NAME an owner name, i.e., the name of the node to which this
|
|
/// resource record pertains.
|
|
///
|
|
/// TYPE two octets containing one of the RR TYPE codes.
|
|
///
|
|
/// CLASS two octets containing one of the RR CLASS codes.
|
|
///
|
|
/// TTL 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.
|
|
///
|
|
/// RDLENGTH an unsigned 16 bit integer that specifies the length in
|
|
/// octets of the RDATA field.
|
|
///
|
|
/// RDATA 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.
|
|
/// </summary>
|
|
public abstract class DnsRecordBase : IDnsRecord
|
|
{
|
|
#region Fields
|
|
// NAME an owner name, i.e., the name of the node to which this
|
|
// resource record pertains.
|
|
//private string _name;
|
|
// TYPE two octets containing one of the RR TYPE codes.
|
|
//protected NsType _nsType;
|
|
// CLASS - two octets containing one of the RR CLASS codes.
|
|
//private NsClass _nsClass;
|
|
// TTL - 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.
|
|
//private int _timeToLive;
|
|
// RDLENGTH - an unsigned 16 bit integer that specifies the length in
|
|
// octets of the RDATA field.
|
|
//protected short _dataLength;
|
|
protected RecordHeader _dnsHeader;
|
|
protected string _answer;
|
|
protected string _errorMsg;
|
|
#endregion
|
|
|
|
#region Properties
|
|
/// <summary>
|
|
/// NAME - an owner name, i.e., the name of the node to which this
|
|
/// resource record pertains.
|
|
/// </summary>
|
|
//public string Name
|
|
//{
|
|
// get { return _name; }
|
|
//}
|
|
|
|
public RecordHeader DnsHeader
|
|
{
|
|
get { return _dnsHeader; }
|
|
protected set { _dnsHeader = value; }
|
|
}
|
|
|
|
public string Answer
|
|
{
|
|
get { return _answer; }
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// TYPE two octets containing one of the RR TYPE codes.
|
|
/// </summary>
|
|
//public NsType NsType
|
|
//{
|
|
// get { return _nsType; }
|
|
//}
|
|
|
|
/// <summary>
|
|
/// CLASS - two octets containing one of the RR CLASS codes.
|
|
/// </summary>
|
|
//public NsClass NsClass
|
|
//{
|
|
// get { return _nsClass; }
|
|
//}
|
|
|
|
/// <summary>
|
|
/// TTL - 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 int TimeToLive
|
|
//{
|
|
// get { return _timeToLive; }
|
|
//}
|
|
|
|
/// <summary>
|
|
/// RDLENGTH - an unsigned 16 bit integer that specifies the length in
|
|
/// octets of the RDATA field.
|
|
/// </summary>
|
|
//public short DataLength
|
|
//{
|
|
// get { return _dataLength; }
|
|
//}
|
|
|
|
public string ErrorMsg
|
|
{
|
|
get { return _errorMsg; }
|
|
}
|
|
#endregion
|
|
|
|
internal DnsRecordBase()
|
|
{
|
|
}
|
|
|
|
public virtual void ParseRecord(ref MemoryStream ms)
|
|
{
|
|
// Default implementation - the most common.
|
|
_answer = DnsRecordBase.ParseName(ref ms);
|
|
}
|
|
|
|
internal DnsRecordBase(RecordHeader dnsHeader)
|
|
{
|
|
_dnsHeader = dnsHeader;
|
|
}
|
|
|
|
// RFC
|
|
// 4.1.4. Message compression
|
|
//
|
|
// In order to reduce the size of messages, the domain system utilizes a
|
|
// compression scheme which eliminates the repetition of domain names in a
|
|
// message. In this scheme, an entire domain name or a list of labels at
|
|
// the end of a domain name is replaced with a pointer to a prior occurance
|
|
// of the same name.
|
|
//
|
|
// The pointer takes the form of a two octet sequence:
|
|
//
|
|
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
|
// | 1 1| OFFSET |
|
|
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
|
//
|
|
// The first two bits are ones. This allows a pointer to be distinguished
|
|
// from a label, since the label must begin with two zero bits because
|
|
// labels are restricted to 63 octets or less. (The 10 and 01 combinations
|
|
// are reserved for future use.) The OFFSET field specifies an offset from
|
|
// the start of the message (i.e., the first octet of the ID field in the
|
|
// domain header). A zero offset specifies the first byte of the ID field,
|
|
// etc.
|
|
//
|
|
// The compression scheme allows a domain name in a message to be
|
|
// represented as either:
|
|
//
|
|
// - a sequence of labels ending in a zero octet
|
|
// - a pointer
|
|
// - a sequence of labels ending with a pointer
|
|
//
|
|
|
|
internal static string ParseName(ref MemoryStream ms)
|
|
{
|
|
Trace.WriteLine("Reading Name...");
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
uint next = (uint)ms.ReadByte();
|
|
Trace.WriteLine("Next is 0x" + next.ToString("x2"));
|
|
int bPointer;
|
|
|
|
while ((next != 0x00))
|
|
{
|
|
// Isolate 2 most significat bits -> e.g. 11xx xxxx
|
|
// if it's 0xc0 (11000000b} then pointer
|
|
switch (0xc0 & next)
|
|
{
|
|
// 0xc0 -> Name is a pointer.
|
|
case 0xc0:
|
|
{
|
|
// Isolate Offset
|
|
int offsetMASK = ~0xc0;
|
|
|
|
// Example on how to calculate the offset
|
|
// e.g.
|
|
//
|
|
// So if given the following 2 bytes - 0xc1 0x1c (11000001 00011100)
|
|
//
|
|
// The pointer takes the form of a two octet sequence:
|
|
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
|
// | 1 1| OFFSET |
|
|
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
|
// | 1 1| 0 0 0 0 0 1 0 0 0 1 1 1 0 0|
|
|
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
|
//
|
|
// A pointer is indicated by the a 1 in the two most significant bits
|
|
// The Offset is the remaining bits.
|
|
//
|
|
// The Pointer = 0xc0 (11000000 00000000)
|
|
// The offset = 0x11c (00000001 00011100)
|
|
|
|
// Move offset into the proper position
|
|
int offset = (int)(offsetMASK & next) << 8;
|
|
|
|
// extract the pointer to the data in the stream
|
|
bPointer = ms.ReadByte() + offset;
|
|
// store the position so we can resume later
|
|
long oldPtr = ms.Position;
|
|
// Move to the specified position in the stream and
|
|
// parse the name (recursive call)
|
|
ms.Position = bPointer;
|
|
sb.Append(DnsRecordBase.ParseName(ref ms));
|
|
Trace.WriteLine(sb.ToString());
|
|
// Move back to original position, and continue
|
|
ms.Position = oldPtr;
|
|
next = 0x00;
|
|
break;
|
|
}
|
|
case 0x00:
|
|
{
|
|
Debug.Assert(next < 0xc0, "Offset cannot be greater then 0xc0.");
|
|
byte[] buffer = new byte[next];
|
|
ms.Read(buffer, 0, (int)next);
|
|
sb.Append(Encoding.ASCII.GetString(buffer) + ".");
|
|
next = (uint)ms.ReadByte();
|
|
Trace.WriteLine("0x" + next.ToString("x2"));
|
|
break;
|
|
}
|
|
default:
|
|
throw new InvalidOperationException("There was a problem decompressing the DNS Message.");
|
|
}
|
|
}
|
|
return sb.ToString();
|
|
}
|
|
|
|
internal string ParseText(ref MemoryStream ms)
|
|
{
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
int len = ms.ReadByte();
|
|
byte[] buffer = new byte[len];
|
|
ms.Read(buffer, 0, len);
|
|
sb.Append(Encoding.ASCII.GetString(buffer));
|
|
return sb.ToString();
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return _answer;
|
|
}
|
|
|
|
#region IDnsRecord Members
|
|
|
|
public virtual byte[] GetMessageBytes()
|
|
{
|
|
return new byte[]{};
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
internal class Trace
|
|
{
|
|
public static void WriteLine(string message)
|
|
{
|
|
Logging.AddLogMessage(Logging.LoggingType.INFO, message);
|
|
}
|
|
}
|
|
}
|