Click here to Skip to main content
15,886,519 members
Articles / Programming Languages / C#

C# .NET DNS query component

Rate me:
Please Sign up or sign in to vote.
4.89/5 (83 votes)
25 Oct 2005CPOL9 min read 736.2K   12.3K   195  
A reusable component for performing DNS queries.
#region
//
// Bdev.Net.Dns by Rob Philpott, Big Developments Ltd. Please send all bugs/enhancements to
// rob@bigdevelopments.co.uk  This file and the code contained within is freeware and may be
// distributed and edited without restriction.
// 

#endregion

using System;
using System.Text;

namespace Bdev.Net.Dns
{
	/// <summary>
	/// Logical representation of a pointer, but in fact a byte array reference and a position in it. This
	/// is used to read logical units (bytes, shorts, integers, domain names etc.) from a byte array, keeping
	/// the pointer updated and pointing to the next record. This type of Pointer can be considered the logical
	/// equivalent of an (unsigned char*) in C++
	/// </summary>
	internal class Pointer
	{
		// a pointer is a reference to the message and an index
		private byte[]		_message;
		private int			_position;

		// pointers can only be created by passing on an existing message
		public Pointer(byte[] message, int position)
		{
			_message = message;
			_position = position;
		}

		/// <summary>
		/// Shallow copy function
		/// </summary>
		/// <returns></returns>
		public Pointer Copy()
		{
			return new Pointer(_message, _position);
		}

		/// <summary>
		/// Adjust the pointers position within the message
		/// </summary>
		/// <param name="position">new position in the message</param>
		public void SetPosition(int position)
		{
			_position = position;
		}

		/// <summary>
		/// Overloads the + operator to allow advancing the pointer by so many bytes
		/// </summary>
		/// <param name="pointer">the initial pointer</param>
		/// <param name="offset">the offset to add to the pointer in bytes</param>
		/// <returns>a reference to a new pointer moved forward by offset bytes</returns>
		public static Pointer operator+(Pointer pointer, int offset)
		{
			return new Pointer(pointer._message, pointer._position + offset);
		}

		/// <summary>
		/// Reads a single byte at the current pointer, does not advance pointer
		/// </summary>
		/// <returns>the byte at the pointer</returns>
		public byte Peek()
		{
			return _message[_position];
		}

		/// <summary>
		/// Reads a single byte at the current pointer, advancing pointer
		/// </summary>
		/// <returns>the byte at the pointer</returns>
		public byte ReadByte()
		{
			return _message[_position++];
		}

		/// <summary>
		/// Reads two bytes to form a short at the current pointer, advancing pointer
		/// </summary>
		/// <returns>the byte at the pointer</returns>
		public short ReadShort()
		{
			return (short)(ReadByte()<<8 | ReadByte());
		}

		/// <summary>
		/// Reads four bytes to form a int at the current pointer, advancing pointer
		/// </summary>
		/// <returns>the byte at the pointer</returns>
		public int ReadInt()
		{
			return (ushort)ReadShort()<<16 | (ushort)ReadShort();
		}

		/// <summary>
		/// Reads a single byte as a char at the current pointer, advancing pointer
		/// </summary>
		/// <returns>the byte at the pointer</returns>
		public char ReadChar()
		{
			return (char)ReadByte();
		}

		/// <summary>
		/// Reads a domain name from the byte array. The method by which this works is described
		/// in RFC1035 - 4.1.4. Essentially to minimise the size of the message, if part of a domain
		/// name already been seen in the message, rather than repeating it, a pointer to the existing
		/// definition is used. Each word in a domain name is a label, and is preceded by its length
		/// 
		/// eg. bigdevelopments.co.uk
		/// 
		/// is [15] (size of bigdevelopments) + "bigdevelopments"
		///    [2]  "co"
		///    [2]  "uk"
		///    [1]  0 (NULL)
		/// </summary>
		/// <returns>the byte at the pointer</returns>
		public string ReadDomain()
		{
			StringBuilder domain = new StringBuilder();
			int length = 0;
		
			// get  the length of the first label
			while ((length = ReadByte()) != 0)
			{
				// top 2 bits set denotes domain name compression and to reference elsewhere
				if ((length & 0xc0) == 0xc0)
				{
					// work out the existing domain name, copy this pointer
					Pointer newPointer = Copy();
						
					// and move it to where specified here
					newPointer.SetPosition((length & 0x3f)<<8 | ReadByte());

					// repeat call recursively
					domain.Append(newPointer.ReadDomain());
					return domain.ToString();
				}

				// if not using compression, copy a char at a time to the domain name
				while (length > 0)
				{
					domain.Append(ReadChar());
					length--;
				}

				// if size of next label isn't null (end of domain name) add a period ready for next label
				if (Peek() != 0) domain.Append('.');
			}

			// and return
			return domain.ToString();
		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect
United Kingdom United Kingdom
I am a .NET architect/developer based in London working mostly on financial trading systems. My love of computers started at an early age with BASIC on a 3KB VIC20 and progressed onto a 32KB BBC Micro using BASIC and 6502 assembly language. From there I moved on to the blisteringly fast Acorn Archimedes using BASIC and ARM assembly.

I started developing with C++ since 1990, where it was introduced to me in my first year studying for a Computer Science degree at the University of Nottingham. I started professionally with Visual C++ version 1.51 in 1993.

I moved over to C# and .NET in early 2004 after a long period of denial that anything could improve upon C++.

Recently I did a bit of work in my old language of C++ and I now realise that frankly, it's a total pain in the arse.

Comments and Discussions