Network Time Protocol request in C#

I’m writing an application in C# that needs to know if the system time is not fake (only if the network is on). For this reason, I make a class that retrieves the time from a NTP server. The class is written to prevent that the same servers are called too often using a simple circular array. For the data returned by the servers, I invite you to see the RFC-2030.

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Net;
using System.Net.Sockets;


public class NoServerFoundException : System.Exception {
	public NoServerFoundException() : base() { }
	public NoServerFoundException(string message) : base(message) { }
	public NoServerFoundException(string message,
			System.Exception inner) : base(message, inner) { }
	protected NoServerFoundException(SerializationInfo info,
		System.Runtime.Serialization.StreamingContext context) { }
}

class NetworkTime {
	/* For more info, see:
	 *	NTP (RFC-2030)
	 *	http://tools.ietf.org/html/rfc2030
	 */

	private const int requestTimeout = 3000;
	private const int timesForEachServer = 5;
	private const byte offTime = 40; //Transmit Time (see RFC-2030)
	private uint lastSrv;

	//NIST Servers
	public static string[] srvs = {
		"time.nist.gov",
		"pool.ntp.org",
		"europe.pool.ntp.org",
		"asia.pool.ntp.org",
		"oceania.pool.ntp.org",
		"north-america.pool.ntp.org",
		"south-america.pool.ntp.org",
		"africa.pool.ntp.org",
		"ntp1.inrim.it",
		"ntp2.inrim.it"
	};

	public NetworkTime() {
		Random rnd = new Random(DateTime.Now.Millisecond);
		lastSrv = (uint)rnd.Next(0, srvs.Length);
	}

	private IPAddress getServer() {
		lastSrv = (uint)((lastSrv + 1) % srvs.Length);
		IPAddress[] address = Dns.GetHostEntry(srvs[lastSrv]).AddressList;
		if (address == null || address.Length == 0)
			throw new NoServerFoundException("no ip found");
		return address[0];
	}

	public DateTime GetDateTime() { return GetDateTime(false); }
	public DateTime GetDateTime(bool utc) {
		//Examine all servers until we find a server that responds
		for (int st = 0; st < srvs.Length * timesForEachServer; st++) {
			try {
				IPAddress ip = getServer();
				IPEndPoint ipEndP = new IPEndPoint(ip, 123);

				Socket sk = new Socket(AddressFamily.InterNetwork,
									  SocketType.Dgram,
									  ProtocolType.Udp);
				sk.ReceiveTimeout = requestTimeout;

				sk.Connect(ipEndP);

				/* Request
				 * VN: 4 = NTP/SNTP version 4
				 * Mode: 3 = client
				 */
				byte[] data = new byte[48];
				data[0] = 0x23;
				for (int i = 1; i < 48; i++) data[i] = 0;
				sk.Send(data);

				/* Response
				 * we read the integer part and fraction part
				 * of transmit time (see RFC-2030)
				 */
				sk.Receive(data);
				byte[] integerPart = new byte[4];
				integerPart[0] = data[offTime + 3];
				integerPart[1] = data[offTime + 2];
				integerPart[2] = data[offTime + 1];
				integerPart[3] = data[offTime + 0];
				byte[] fractPart = new byte[4];
				fractPart[0] = data[offTime + 7];
				fractPart[1] = data[offTime + 6];
				fractPart[2] = data[offTime + 5];
				fractPart[3] = data[offTime + 4];
				long ms = (long)(
							(ulong)BitConverter.ToUInt32(integerPart, 0) * 1000
						 + ((ulong)BitConverter.ToUInt32(fractPart, 0) * 1000)
							/ 0x100000000L);
				sk.Close();

				/* DateTime*/
				DateTime date = new DateTime(1900, 1, 1);
				date += TimeSpan.FromTicks(ms * TimeSpan.TicksPerMillisecond);

				return utc ? date : date.ToLocalTime();

			} catch (Exception ex) { }
		}

		throw new NoServerFoundException("no working server has been found");
	}
}

Download: NetworkTime.cs

3 responses to “Network Time Protocol request in C#

  1. Mike

    Nice post, thank you!

  2. DiscoNinja

    Great solution! Thank you!

  3. HaGever

    Excellent! Thanks!

Leave a Reply

Your email address will not be published. Required fields are marked *