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");
}
}