Gaurav Pal
Gaurav Pal

Reputation: 77

NTP query takes a very long time when passing invalid hostname

I am using the code below for querying an NTP server. This code is working fine until I pass an invalid hostname for the NTP server: then this code takes a very long time.

const string ntpServer = "pool.ntp.org";

var ntpData = new byte[48];           
ntpData[0] = 0x1B; //LeapIndicator = 0 (no warning), VersionNum = 3 (IPv4 only), Mode = 3 (Client Mode)       

var addresses = Dns.GetHostEntry(ntpServer).AddressList;            
var ipEndPoint = new IPEndPoint( addresses[0] , 123);
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram,  ProtocolType.Udp);    

socket.Connect(ipEndPoint);
socket.Send(ntpData);
socket.Receive(ntpData);
socket.Close();

ulong intPart = (ulong)ntpData[40] << 24 | (ulong)ntpData[41] << 16 | (ulong)ntpData[42] << 8 | (ulong)ntpData[43];
ulong fractPart = (ulong)ntpData[44] << 24 | (ulong)ntpData[45] << 16 | (ulong)ntpData[46] << 8 | (ulong)ntpData[47];

var milliseconds = (intPart * 1000) + ((fractPart * 1000) / 0x100000000L);
var networkDateTime = (new DateTime(1900, 1, 1)).AddMilliseconds((long)milliseconds);
Label1.Text = networkDateTime.ToString();

I'm getting the following error

show following error msgServer Error in '/' Application. No such host is known Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. Exception Details: System.Net.Sockets.SocketException: No such host is known Source Error: Line 26: ntpData[0] = 0x1B; //LeapIndicator = 0 (no warning),

Upvotes: 3

Views: 640

Answers (1)

phuzi
phuzi

Reputation: 13059

UPDATE - New information

Ah, the wait you're getting is probably due to DNS timing out try to resolve the host address.

The error you're getting is probably because the domain name cannot be resolved.

It's a good idea to get in to the practice of writing defensive code. Wherever there's the possibility of something being null, throwing an exception or you make an assumption.

Original answer

UDP is a connectionless protocol that doesn't have any built in error handling.

Since your code uses UDP it has no idea about whether it successfully sent an request to the NTP server or not and will, by default, wait forever for a response. I'm guessing you're killing the process yourself.

The only thing you can do is set a ReceiveTimeout and catch the exception when it does.

const string ntpServer = "pool.ntp.org";
const int timeout = 2000;

var ntpData = new byte[48];           
ntpData[0] = 0x1B; //LeapIndicator = 0 (no warning), VersionNum = 3 (IPv4 only), Mode = 3 (Client Mode)       

IPHostEntry dnsLookup = null;
try {
    dnsLookup = Dns.GetHostEntry(ntpServer);
}
catch(Exception e){ //Better to catch specific types of exceptions
    Label1.Text = string.Format("Unable to resolve hostname: {0}, ntpServer)";
}

if (dnsLookup == null || dnsLookup.AddressList.Length == 0){
    return;
}

var ipEndPoint = new IPEndPoint(dnsLookup.AddressList[0], 123);
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram,  ProtocolType.Udp);    

// wait two seconds before timing out
socket.ReceiveTimeout = timeout; 

try{
    socket.Connect(ipEndPoint);
    socket.Send(ntpData);

    socket.Receive(ntpData);
    ulong intPart = (ulong)ntpData[40] << 24 | (ulong)ntpData[41] << 16 | (ulong)ntpData[42] << 8 | (ulong)ntpData[43];
    ulong fractPart = (ulong)ntpData[44] << 24 | (ulong)ntpData[45] << 16 | (ulong)ntpData[46] << 8 | (ulong)ntpData[47];

    var milliseconds = (intPart * 1000) + ((fractPart * 1000) / 0x100000000L);
    var networkDateTime = (new DateTime(1900, 1, 1)).AddMilliseconds((long)milliseconds);
    Label1.Text = networkDateTime.ToString();
}
catch(Exception e){ //Better to catch specific types of exceptions
    Label1.Text = string.Format("Unable to get time from NTP server: {0}, ntpServer)";
}
finally{
    socket.Close();
}

Upvotes: 3

Related Questions