Click here to Skip to main content
15,880,796 members
Articles / Programming Languages / C#

STUN Client

Rate me:
Please Sign up or sign in to vote.
4.83/5 (36 votes)
20 Apr 2007CPOL 322.2K   14.9K   85  
STUN client C# implementation with sample application
using System;
using System.Net;
using System.Net.Sockets;
using System.Collections.Generic;

namespace LumiSoft.Net.ICMP
{
	/// <summary>
	/// ICMP type.
	/// </summary>
	public enum ICMP_Type
	{
		/// <summary>
		/// Echo rely.
		/// </summary>
		EchoReply = 0,

		/// <summary>
		/// Time to live exceeded reply.
		/// </summary>
		TimeExceeded = 11,

		/// <summary>
		/// Echo.
		/// </summary>
		Echo = 8,
    }

    #region class EchoMessage

    /// <summary>
	/// Echo reply message.
	/// </summary>
	public class EchoMessage
	{
		private IPAddress m_pIP  = null;
		private int       m_TTL  = 0;
		private int       m_Time = 0;
		
		/// <summary>
		/// Default constructor.
		/// </summary>
		/// <param name="ip">IP address what sent echo message.</param>
		/// <param name="ttl">Time to live in milli seconds.</param>
		/// <param name="time">Time what elapsed before getting echo response.</param>
		internal EchoMessage(IPAddress ip,int ttl,int time)
		{
			m_pIP  = ip;
			m_TTL  = ttl;
			m_Time = time;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
        [Obsolete("Will be removed !")]
		public string ToStringEx()
		{
			return "TTL=" + m_TTL + "\tTime=" + m_Time + "ms" + "\tIP=" + m_pIP;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="messages"></param>
		/// <returns></returns>
        [Obsolete("Will be removed !")]
		public static string ToStringEx(EchoMessage[] messages)
		{
			string retVal = "";

			foreach(EchoMessage m in messages){
				retVal += m.ToStringEx() + "\r\n";
			}

			return retVal;
        }

        #region Properties Implementation

        /// <summary>
        /// Gets IP address what sent echo message.
        /// </summary>
        public IPAddress IPAddress
        {
            get{ return m_pIP; }
        }
        /*
        /// <summary>
        /// Gets time to live in milli seconds.
        /// </summary>
        public int TTL
        {
            get{ return m_TTL; }
        }*/

        /// <summary>
        /// Gets time in milliseconds what toke to get reply.
        /// </summary>
        public int ReplyTime
        {
            get{ return m_Time; }
        }

        #endregion
    }

    #endregion

    /// <summary>
	/// Icmp utils.
	/// </summary>
	public class Icmp
	{
		#region methoc Trace

        /// <summary>
		/// Traces specified ip.
		/// </summary>
		/// <param name="destIP">Destination IP address.</param>
		/// <returns></returns>
		public static EchoMessage[] Trace(string destIP)
		{
            return Trace(System.Net.IPAddress.Parse(destIP),2000);
        }

		/// <summary>
		/// Traces specified ip.
		/// </summary>
		/// <param name="ip">IP address to tracce.</param>
		/// <param name="timeout">Send recieve timeout in milli seconds.</param>
		/// <returns></returns>
		public static EchoMessage[] Trace(IPAddress ip,int timeout)
		{
			List<EchoMessage> retVal = new List<EchoMessage>();

			//Create Raw ICMP Socket 
			Socket s = new Socket(AddressFamily.InterNetwork,SocketType.Raw,ProtocolType.Icmp);
			
			IPEndPoint ipdest = new IPEndPoint(ip,80);			
			EndPoint endpoint = (EndPoint)(new IPEndPoint(System.Net.Dns.GetHostEntry(System.Net.Dns.GetHostName()).AddressList[0],80));
													
			ushort id         = (ushort)DateTime.Now.Millisecond;
			byte[] sendPacket = CreatePacket(id);

			int continuesNoReply = 0;
			//send requests with increasing number of TTL
			for(int ittl=1;ittl<=30; ittl++){
				byte[] buffer = new byte[1024];
				
				try{
					//Socket options to set TTL and Timeouts 
					s.SetSocketOption(SocketOptionLevel.IP,SocketOptionName.IpTimeToLive      ,ittl);
					s.SetSocketOption(SocketOptionLevel.Socket,SocketOptionName.SendTimeout   ,timeout); 
					s.SetSocketOption(SocketOptionLevel.Socket,SocketOptionName.ReceiveTimeout,timeout); 

					//Get current time
					DateTime startTime = DateTime.Now;

					//Send Request
					s.SendTo(sendPacket,sendPacket.Length,SocketFlags.None,ipdest);
				
					//Receive				
					s.ReceiveFrom(buffer,buffer.Length,SocketFlags.None,ref endpoint);

					//Calculate time required
					TimeSpan ts = DateTime.Now - startTime;
					retVal.Add(new EchoMessage(((IPEndPoint)endpoint).Address,ittl,ts.Milliseconds));

					// Endpoint reached
					if(buffer[20] == (byte)ICMP_Type.EchoReply){
						break;
					}
					
					// Un wanted reply
					if(buffer[20] != (byte)ICMP_Type.TimeExceeded){
						throw new Exception("UnKnown error !");
					}

					continuesNoReply = 0;
				}
				catch{
					//ToDo: Handle recive/send timeouts
					continuesNoReply++;
				}

				// If there is 3 continues no reply, consider that destination host won't accept ping.
				if(continuesNoReply >= 3){
					break;
				}
			}

			return retVal.ToArray();
		}

		#endregion

        #region method Ping

        /// <summary>
        /// Pings specified destination host.
        /// </summary>
        /// <param name="ip">IP address to ping.</param>
        /// <param name="timeout">Send recieve timeout in milli seconds.</param>
        /// <returns></returns>
		public static EchoMessage Ping(IPAddress ip,int timeout)
		{
            //Create Raw ICMP Socket 
			Socket s = new Socket(AddressFamily.InterNetwork,SocketType.Raw,ProtocolType.Icmp);
			
			IPEndPoint ipdest = new IPEndPoint(ip,80);			
			EndPoint endpoint = (EndPoint)(new IPEndPoint(System.Net.Dns.GetHostEntry(System.Net.Dns.GetHostName()).AddressList[0],80));
													
			ushort id         = (ushort)DateTime.Now.Millisecond;
			byte[] sendPacket = CreatePacket(id);

			//Socket options to set TTL and Timeouts 
			s.SetSocketOption(SocketOptionLevel.IP,SocketOptionName.IpTimeToLive      ,30);
			s.SetSocketOption(SocketOptionLevel.Socket,SocketOptionName.SendTimeout   ,timeout); 
			s.SetSocketOption(SocketOptionLevel.Socket,SocketOptionName.ReceiveTimeout,timeout); 

			//Get current time
			DateTime startTime = DateTime.Now;

			//Send Request
			s.SendTo(sendPacket,sendPacket.Length,SocketFlags.None,ipdest);
				
			//Receive
			byte[] buffer = new byte[1024];
			s.ReceiveFrom(buffer,buffer.Length,SocketFlags.None,ref endpoint);
            			
			// Endpoint reached
			if(buffer[20] == (byte)ICMP_Type.EchoReply){				
			}					
			// Un wanted reply
			else if(buffer[20] != (byte)ICMP_Type.TimeExceeded){
				throw new Exception("UnKnown error !");
			}

            //Calculate time elapsed
			TimeSpan ts = DateTime.Now - startTime;
			return new EchoMessage(((IPEndPoint)endpoint).Address,0,ts.Milliseconds);
        }

        #endregion


        #region method CreatePacket

        private static byte[] CreatePacket(ushort id)
		{
			/*Rfc 792  Echo or Echo Reply Message
			  0               8              16              24
			 +---------------+---------------+---------------+---------------+
			 |     Type      |     Code      |           Checksum            |
			 +---------------+---------------+---------------+---------------+
			 |           ID Number           |            Sequence Number    |
			 +---------------+---------------+---------------+---------------+
			 |     Data...        
			 +---------------+---------------+---------------+---------------+
			*/

			byte[] packet = new byte[8 + 2];
			packet[0] = (byte)ICMP_Type.Echo; // Type
			packet[1] = 0;  // Code
			packet[2] = 0;  // Checksum
			packet[3] = 0;  // Checksum
			packet[4] = 0;  // ID
			packet[5] = 0;  // ID
			packet[6] = 0;  // Sequence
			packet[7] = 0;  // Sequence

			// Set id
			Array.Copy(BitConverter.GetBytes(id), 0, packet, 4, 2);

			// Fill data 2 byte data
			for(int i=0;i<2;i++){
				packet[i + 8] = (byte)'x'; // Data
			}
			
			// calculate checksum
			int checkSum = 0;
			for(int i= 0;i<packet.Length;i+= 2){ 
				checkSum += Convert.ToInt32(BitConverter.ToUInt16(packet,i));
			}

			// The checksum is the 16-bit ones's complement of the one's
			// complement sum of the ICMP message starting with the ICMP Type.
			checkSum  = (checkSum & 0xffff);
			Array.Copy(BitConverter.GetBytes((ushort)~checkSum),0,packet,2,2);

			return packet;
		}

		#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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


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