448

WHOIS queries the .NET way

Share on TwitterShare on TumblrSubmit to StumbleUponSave on DeliciousDigg This

Querying a dot-com domain

To demonstrate the principle of how a WHOIS query works, we use the American registry Network Solutions (compare screen shot):

http://www.networksolutions.com/cgi-bin/whois/whois

All one has to do is enter the domain name, click Search – and the registry information is displayed. But what database is running in the background allowing such queries?

The database is the so called WHOIS database and it has one distinct property: it provides us with a query interface via TCP port 43! And as the .NET framework provides us with the TcpClient class, we can use this interface to directly obtain our data without the extra effort of scraping the web pages at Network Solutions.

The following example is a minimal implementatin of a WHOIS lookup. There is only minimum error handling and “selection” of the domain is not yet possible (queryinternic.aspx):

<% @Page Language="C#" %>
<% @Import Namespace="System.Net.Sockets" %>
<% @Import Namespace="System.Text" %>
<% @Import Namespace="System.IO" %>
<%
TcpClient tcpc = new TcpClient();
try
{
  tcpc.Connect("whois.networksolutions.com", 43);
}
catch(SocketException ex)
{
  Response.Write(ex.ToString());
  Response.End();
}

String strDomain = "gotdotnet.com\r\n";
Byte[] arrDomain = Encoding.ASCII.GetBytes(strDomain.ToCharArray());

Stream s = tcpc.GetStream();
s.Write(arrDomain, 0, strDomain.Length);

StreamReader sr = new StreamReader(tcpc.GetStream(), Encoding.ASCII);
string strLine = null;

while (null != (strLine = sr.ReadLine()))
{
   Response.Write(strLine + "<br>");
}

tcpc.Close();
%>

To be able to work with the TcpClient class, we need the System.Net.Sockets namespace. Furtheron, we need old acquaintances like System.Text or System.IO.

Most of the work in this example is being done by the TcpClient class: using it, I can connect to a server on any port (the Connect method does that) and send data to and fro (via Stream).

Sending the query warrants a little explanation: for sending I use the binary stream without any wrappers. Therefore, I have to turn the query string (CR/LF terminated) into a byte array (with ASCII encoding) and can then send the desired number of bytes to the server.

Fetching the data happens via the StreamReader, which allows comfortable and direct work with the character data. And to keep the example simple to the end, the received data are immediately passed through to the client – the result can be seen in the following screen shot.

Querying several registries

Now we know how to query the WHOIS server of Network Solutions. Have we achieved something with that? Certainly – (almost) every registry has such a server and the procedure is always the same. You just need a list of registries and their WHOIS servers.

Precisely that is what I implemented in the following example: it contains a simple form for entering the domain name. ASP.NET on the server side checks the validity of the domain name and then decides according to the domain suffix (.com, .de etc.) which WHOIS server of which registry is to be queried for information about this domain.

The code already has more than 100 lines, so that I split the explanation. If you want to keep the “big picture” before you, open the code in the file queryregistries.aspx in a second window.

The form

There is not much to say about the form being used. There is an input field, a button and a label control into which the result is output.

<html>
<head>
<title></title>
</head>
<body>

<form runat="server">
Domain name: 
 <asp:TextBox id="txtDomain" value="aspheute.com" runat="server" />
&nbsp; <asp:Button id="btnQuery" OnClick="doQuery" 
 text="Query!" runat="server" />
<BR><HR width="100%"><BR>
<asp:label id="txtResult" runat="server" />
</form>

</body>
</html>

The button event doQuery

This event is host to most of the code. Basically it does the following:

  • The submitted domain is checked for plausibility
  • The appropriate WHOIS server is selected
  • The function DoWhoisLookup is called and results are output

Now to the source code:

void doQuery(Object sender, EventArgs e)
{
 String strDomain = txtDomain.Text;
 char[] chSplit = {'.'};
 string[] arrDomain = strDomain.Split(chSplit);
 // There may only be exactly one domain name and one suffix
 if (arrDomain.Length != 2)
 {
 return;
 }

 // The suffix may only be 2 or 3 characters long
 int nLength = arrDomain[1].Length;
 if (nLength != 2 && nLength != 3) 
 {
 return;
 }

 Hashtable table = new Hashtable();
 table.Add("de", "whois.denic.de");
 table.Add("be", "whois.dns.be");
 table.Add("gov", "whois.nic.gov");
 table.Add("mil", "whois.nic.mil");

 String strServer = "whois.networksolutions.com";
 if (table.ContainsKey(arrDomain[1]))
 {
 strServer = table[arrDomain[1]].ToString();
 }
 else if (nLength == 2)
 {
 // 2-letter TLD's always default to RIPE in Europe
 strServer = "whois.ripe.net";
 }

 String strResponse;
 bool bSuccess = DoWhoisLookup(strDomain, strServer, out strResponse);
 if (bSuccess)
 {
 txtResult.Text = strResponse;
 }
 else
 {
 txtResult.Text = "Lookup failed";
 }
}

The uppermost block concerns itself only with checking the validity of the input (perhaps you should print out the error messages after all). Then I set up a hash table linking the TLDs with the respective WHOIS servers. I only picked the most important TLDs, but this is enough to query all US and European domains.

After the creation of the Hashtable the code decides which server to use. By default it is the one of Network Solutions, in other cases a special one or the RIPE server in case of 2 letter TLDs. In any case the hash table should be completed if querying of South American and Asian TLDs is wanted.

As we now know what we want to look up, and especially where to query, we can let the function DoWhoisLookup do the work – and then simply output its results.

The query – DoWhoisLookup

Now we are looking at the real work horse of the application. This function performs the query and the Good News is that we indeed could reuse all the code of the Network Solutions query – I just added some exception handling to make the application really robust.

bool DoWhoisLookup(String strDomain, String strServer, 
                   out String strResponse)
{
  strResponse = "none";
  bool bSuccess = false;

  TcpClient tcpc = new TcpClient();
  try
  {
    tcpc.Connect(strServer, 43);
  }
  catch(SocketException ex)
  {
    strResponse = "Could not connect to Whois server";
    return false;
  }

  strDomain += "\r\n";
  Byte[] arrDomain = Encoding.ASCII.GetBytes(strDomain.ToCharArray());
  try
  {
	Stream s = tcpc.GetStream();
	s.Write(arrDomain, 0, strDomain.Length);

	StreamReader sr = new StreamReader(tcpc.GetStream(), Encoding.ASCII);
	StringBuilder strBuilder = new StringBuilder();
	string strLine = null;

	while (null != (strLine = sr.ReadLine()))
	{
		strBuilder.Append(strLine+"<br>");
	}
	tcpc.Close();

	bSuccess = true;
	strResponse = strBuilder.ToString();
  }
  catch(Exception e)
  {
	strResponse = e.ToString();
  }

  return bSuccess;
}

As already announced, exception handling was added. Furthermore, I now use the StringBuilder class as I have to pass the response of the WHOIS server on to the caller instead of directly to the client. And that already was the whole magic!

Conclusion

Thanks to the .NET Framework classes we were able to build a comfortable cross-registry WHOIS query form. The only blemish is easy to remove: the hash table ought to have the remaining international WHOIS servers added, but from the European’s point of view the most important registries already can be queried!

Downloading the code

Click here to start the download.

prajapat