Click here to Skip to main content
15,896,269 members
Articles / Programming Languages / C#

SMTP and POP3 Mail Server

Rate me:
Please Sign up or sign in to vote.
4.88/5 (96 votes)
29 Sep 20031 min read 1M   18.9K   315  
An SMTP and POP3 mail server written using the .NET Framework and C#.
using System;
using System.Text;
using System.Collections;

namespace LumiSoft.Net.Dns
{
	/// <summary>
	/// This class holds Dns answers returned by server.
	/// </summary>
	internal class Dns_Answers
	{
		private Dns_Answer[] m_Answers = null;

		/// <summary>
		/// Default constructor.
		/// </summary>
		public Dns_Answers()
		{			
		}


		#region function ParseAnswers

		/// <summary>
		/// Parses answer.
		/// </summary>
		/// <param name="reply"></param>
		/// <param name="queryID"></param>
		/// <returns>Returns true if answer parsed successfully.</returns>
		internal bool ParseAnswers(byte[] reply,int queryID)
		{			
			try
			{
				/*
			     							   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                     /
				/                                               /
				+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
				*/

				//------- Parse result -----------------------------------//

				Dns_Header replyHeader = new Dns_Header();
				if(!replyHeader.ParseHeader(reply)){
					return false;
				}

				// Check that it's query what we want
				if(queryID != replyHeader.ID){
					return false;
				}
				
				int pos = 12;

				//----- Parse question part ------------//
				for(int q=0;q<replyHeader.QDCOUNT;q++){
					string dummy = "";
					GetQName(reply,ref pos,ref dummy);
					//qtype + qclass
					pos += 4;
				}
				//--------------------------------------//
				
				ArrayList answers = new ArrayList();

				//---- Start parsing aswers ------------------------------------------------------------------//
				for(int i=0;i<replyHeader.ANCOUNT;i++){
					string name = "";
					if(!GetQName(reply,ref pos,ref name)){
						return false;
					}

					int type     = reply[pos++] << 8 | reply[pos++];
					int rdClass  = reply[pos++] << 8 | reply[pos++];
					int ttl      = reply[pos++] << 24 | reply[pos++] << 16 | reply[pos++] << 8  | reply[pos++];
					int rdLength = reply[pos++] << 8 | reply[pos++];

					object answerObj = null;
					switch((QTYPE)type)
					{
						case QTYPE.MX:
							answerObj = ParseMxRecord(reply,ref pos);
							break;

						default:
							answerObj = "sgas"; // Dummy place holder for now
							pos += rdLength;
							break;
					}
			
					// Add answer to answer list
					if(answerObj != null){
						answers.Add(new Dns_Answer(name,(QTYPE)type,rdClass,ttl,rdLength,answerObj));
					}
					else{
						return false; // Parse failed
					}
				}
				//-------------------------------------------------------------------------------------------//

				if(answers.Count > 0){
					m_Answers = new Dns_Answer[answers.Count];
					answers.CopyTo(m_Answers);
				}

				return true;
			}
			catch{
				return false;
			}
		}

		#endregion
		
		#region function GetQName

		private bool GetQName(byte[] reply,ref int offset,ref string name)
		{	
			try
			{
				// Do while not terminator
				while(reply[offset] != 0){
					
					// Check if it's pointer(In pointer first two bits always 1)
					bool isPointer = ((reply[offset] & 0xC0) == 0xC0);
					
					// If pointer
					if(isPointer){
						int pStart = ((reply[offset] & 0x3F) << 8) | (reply[++offset]);
						offset++;						
						return GetQName(reply,ref pStart,ref name);
					}
					else{
						// label lenght (length = 8Bit and first 2 bits always 0)
						int labelLenght = (reply[offset] & 0x3F);
						offset++;
						
						// Copy label into name 
						name += Encoding.ASCII.GetString(reply,offset,labelLenght);
						offset += labelLenght;
					}
									
					// If the next char isn't terminator,
					// label continues - add dot between two labels
					if (reply[offset] != 0){
						name += ".";
					}
				}

				// Move offset by terminator lenght
				offset++;

				return true;
			}
			catch//(Exception x)
			{
		//		System.Windows.Forms.MessageBox.Show(x.Message);
				return false;
			}
		}

		#endregion


		#region function ParseMxRecord

		/// <summary>
		/// Parses MX record.
		/// </summary>
		/// <param name="reply"></param>
		/// <param name="offset"></param>
		/// <returns>Returns null, if failed.</returns>
		private object ParseMxRecord(byte[] reply,ref int offset)
		{
			/* RFC 1035	3.3.9. MX RDATA format

			+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
			|                  PREFERENCE                   |
			+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
			/                   EXCHANGE                    /
			/                                               /
			+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

			where:

			PREFERENCE      
				A 16 bit integer which specifies the preference given to
				this RR among others at the same owner.  Lower values
                are preferred.

			EXCHANGE 
			    A <domain-name> which specifies a host willing to act as
                a mail exchange for the owner name. 
			*/

			try
			{
				int pref = reply[offset++] << 8 | reply[offset++];
		
				string name = "";				
				if(GetQName(reply,ref offset,ref name)){
					return new MX_Record(pref,name);
				}
			}
			catch{
			}

			return null;
		}

		#endregion

		#region function GetMxRecordsFromAnswers

		/// <summary>
		/// Gets MX records from answer collection and ORDERS them by preference.
		/// NOTE: Duplicate preference records are appended to end. 
		/// </summary>
		/// <returns></returns>
		internal MX_Record[] GetMxRecordsFromAnswers()
		{
			MX_Record[] retVal = null;

			try
			{		
				SortedList mx            = new SortedList();
				ArrayList  duplicateList = new ArrayList();
				foreach(Dns_Answer answer in m_Answers){
					if(answer.QTYPE == QTYPE.MX){
						MX_Record mxRec = (MX_Record)answer.RecordObj;

						if(!mx.Contains(mxRec.Preference)){
							mx.Add(mxRec.Preference,mxRec);
						}
						else{
							duplicateList.Add(mxRec);
						}
					}
				}

				MX_Record[] mxBuff = new MX_Record[mx.Count + duplicateList.Count];
				mx.Values.CopyTo(mxBuff,0);
				duplicateList.CopyTo(mxBuff,mx.Count);
				retVal = mxBuff;
			}
			catch{				
			}

			return retVal;
		}

		#endregion


		#region Properties Implementation

		/// <summary>
		/// Gets answers.
		/// </summary>
		public Dns_Answer[] Answers
		{
			get{ return m_Answers; }
		}

		#endregion

	}
}

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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Estonia Estonia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions