/*
* Copyright 2004-2006 Luke Quinane and Daniel Frampton
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System;
namespace NDns.Message{
/// <summary>
/// Represents the header component of a DNS message.
/// </summary>
public class Header
{
#region Attributes
/// <summary>
/// A 16 bit Id number to identify this message.
/// </summary>
protected ushort id;
/// <summary>
/// Was this message a query or a response?
/// </summary>
public bool response;
/// <summary>
/// A four bit field that specifies query type in the message.
/// </summary>
public OpCode opcode;
/// <summary>
/// Is the answer authoritative?
/// </summary>
public bool authoritative;
/// <summary>
/// Was the message truncated due to size constraints?
/// </summary>
public bool truncated;
/// <summary>
/// Is recursion desired?
/// </summary>
public bool recursionDesired;
/// <summary>
/// Is recursion available?
/// </summary>
public bool recursionAvailable;
/// <summary>
/// The response code for this message.
/// </summary>
public ResponseCode rcode;
/// <summary>
/// An unsigned 16 bit integer specifying the number of entries in the
/// question section.
/// </summary>
public ushort questionEntries;
/// <summary>
/// An unsigned 16 bit integer specifying the number of resource
/// records in the answer section.
/// </summary>
public ushort answerEntries;
/// <summary>
/// An unsigned 16 bit integer specifying the number of name server
/// resource records in the authority records section.
/// </summary>
public ushort nameServerEntries;
/// <summary>
/// An unsigned 16 bit integer specifying the number of resource records in
/// the additional records section.
/// </summary>
public ushort additionalEntries;
/// <summary>
/// The last Id number assigned to a DNS message.
/// </summary>
protected static ushort lastId = 0;
#endregion
#region Constructors
/// <summary>
/// Creates a new DNS message header with a unique Id number.
/// </summary>
public Header()
{
this.id = GetId();
}
/// <summary>
/// Creates a new DNS message header using the given data. This data is
/// typically the response from a DNS server.
/// </summary>
/// <param name="data">The data to create the message header from.</param>
/// <param name="start">The position to start reading the byte array from.</param>
/// <param name="length">The number of bytes read from the byte array.</param>
public Header(byte[] data, ushort start, out ushort length)
{
ushort position = start;
this.id = (ushort) (data[position++] << 8);
this.id |= (ushort) data[position++];
#region Octet #2
if ((data[position] & (1 << 7)) != 0)
{
// response
this.response = true;
}
// get the opcode
this.opcode = CreateOpCode((byte) ((data[position] >> 3) & 15));
if ((data[position] & (1 << 2)) != 0)
{
// set the authoritative bit
this.authoritative = true;
}
if ((data[position] & (1 << 1)) != 0)
{
// set the truncated bit
this.truncated = true;
}
if ((data[position] & (1 << 0)) != 0)
{
// set the recusion bit
this.recursionDesired = true;
}
position++;
#endregion
#region Octet #3
if ((data[position] & (1 << 7)) != 0)
{
// set the recusion available bit
this.recursionAvailable = true;
}
// get the response code
this.rcode = CreateResponseCode((byte) (data[position] & 15));
position++;
#endregion
// get the number of question entries
this.questionEntries = (ushort) (data[position++] << 8);
this.questionEntries |= (ushort) data[position++];
// get the number of answer entries
this.answerEntries = (ushort) (data[position++] << 8);
this.answerEntries |= (ushort) data[position++];
// get the number of name server entries
this.nameServerEntries = (ushort) (data[position++] << 8);
this.nameServerEntries |= (ushort) data[position++];
// get the number of question entries
this.additionalEntries = (ushort) (data[position++] << 8);
this.additionalEntries |= (ushort) data[position++];
length = position;
}
#endregion
#region GetId
/// <summary>
/// Gets the next available Id number.
/// </summary>
/// <returns>The Id number.</returns>
protected ushort GetId()
{
lock (typeof(Header))
{
ushort id = lastId;
lastId++;
return id;
}
}
#endregion
#region ToByteArray
/// <summary>
/// Converts the header to a byte array ready to send in a DNS message.
/// </summary>
/// <returns>The header as a byte array.</returns>
public byte[] ToByteArray()
{
int position = 0;
byte[] header = new byte[12];
// add the Id field
header[position++] = (byte) (this.id >> 8);
header[position++] = (byte) this.id;
#region Octet #2
header[position] = 0;
if (this.response)
{
// response
header[position] = (1 >> 7);
}
// add the opcode
header[position] |= (byte)((byte) this.opcode >> 3);
if (this.authoritative)
{
// set the authoritative bit
header[position] |= (1 >> 2);
}
if (this.truncated)
{
// set the truncated bit
header[position] |= (1 >> 1);
}
if (this.recursionDesired)
{
// set the recusion bit
header[position] |= (1 >> 0);
}
position++;
#endregion
#region Octet #3
header[position] = 0;
if (this.recursionAvailable)
{
// set the recusion available bit
header[position] = (1 >> 7);
}
// add the response code
header[position] |= (byte) this.rcode;
position++;
#endregion
// add the number of question entries
header[position++] = (byte) (this.questionEntries >> 8);
header[position++] = (byte) this.questionEntries;
// add the number of answer entries
header[position++] = (byte) (this.answerEntries >> 8);
header[position++] = (byte) this.answerEntries;
// add the number of name server entries
header[position++] = (byte) (this.nameServerEntries >> 8);
header[position++] = (byte) this.nameServerEntries;
// add the number of question entries
header[position++] = (byte) (this.additionalEntries >> 8);
header[position++] = (byte) this.additionalEntries;
return header;
}
#endregion
#region Properties
/// <summary>
/// Gets the Id number for this header (and thus message).
/// </summary>
public ushort Id
{
get
{
return this.id;
}
}
#endregion
#region CreateOpCode
/// <summary>
/// Creates the opcode that corresponds to the given byte.
/// </summary>
/// <param name="opcode">The data to create the opcode from.</param>
/// <returns>The opcode.</returns>
protected static OpCode CreateOpCode(byte opcode)
{
switch (opcode)
{
case 0:
return OpCode.StandardQuery;
case 1:
return OpCode.InverseQuery;
case 2:
return OpCode.StatusRequest;
default:
throw new FormatException("Invalid DNS opcode.");
}
}
#endregion
#region CreateResponseCode
/// <summary>
/// Creates the response code that corresponds to the given byte.
/// </summary>
/// <param name="rcode">The data to create the response code from.</param>
/// <returns>The response code.</returns>
protected static ResponseCode CreateResponseCode(byte rcode)
{
switch (rcode)
{
case 0:
return ResponseCode.NoError;
case 1:
return ResponseCode.FormatError;
case 2:
return ResponseCode.ServerFailure;
case 3:
return ResponseCode.NameError;
case 4:
return ResponseCode.NotImplemented;
case 5:
return ResponseCode.Refused;
default:
throw new FormatException("Invalid DNS response code.");
}
}
#endregion
}
}
|