Introduction
I'm working on a bookmarks manager, and I wanted to be able to make whois queries for any domain in my bookmarks. So I searched for any class or tool in .NET that can do that. However, I did not meet with success and decided to build my own. Let me explain how I've done that.
Background
My first (and only) idea was to look at the source code of the GNU/Whois client (thanks to Open Source), and adapt it to my needs. Here is the result.
Architecture
I store a list of Top-Level Domains (TLD), with the Whois server to query in a *.ini file, as shown below (I provide my own list on top of this page):
[Servers]
.br.com=whois.centralnic.net
.cn.com=whois.centralnic.net
.eu.org=whois.eu.org
.com=whois.crsnic.net
.net=whois.crsnic.net
.org=whois.publicinterestregistry.net
.edu=whois.educause.net
.gov=whois.nic.gov
[...]
I made this list using the original list found in the source package of the GNU client, and my own searches.
If you prefer using another way to store TLD and Whois servers, you can use XML or AppSettings, so you don't need to have a class for handling INI files. I personally use *.ini files because it's more flexible for my project.
Using the Code
The client is contained in just two files: one for the EventArgs
, and one for the query. This is the content of the WhoisEventArgs
class:
using System;
namespace WhoisClient
{
public class WhoisEventArgs : EventArgs
{
private string _url, _message, _tld, _targetServer;
public string Url { get { return _url; } }
public string Message { get { return _message; } }
public string TLD { get { return _tld; } }
public string TargetServer { get { return _targetServer; } }
public WhoisEventArgs
(string url, string tld, string targetServer, string message)
{
_url = url;
_tld = tld;
_targetServer = targetServer;
_message = message;
}
}
}
Now, the code of the main class:
using System;
using System.IO;
using System.Text;
using System.Net.Sockets;
using System.ComponentModel;
using System.Text.RegularExpressions;
namespace WhoisClient
{
public class WhoisRequest
{
private string _url,
_domain,
_tld,
_targetServer,
_defaultTargetServer = "whois.arin.net"
public string Url { get { return _url; } }
public string DefaultTargetServer
{ get { return _defaultTargetServer; }
set { _defaultTargetServer = value; } }
public event EventHandler<WhoisEventArgs> ResponseReceived;
public event EventHandler ConnectionClosed;
public WhoisRequest(string url)
{
_url = url;
Uri u = null;
try
{
u = new Uri(url);
}
catch(Exception ex)
{
throw new Exception(ex.Message);
}
if (u != null)
{
Initialize(u);
}
}
public WhoisRequest(Uri url)
{
Initialize(url);
}
void Initialize(Uri url)
{
if (url != null)
{
_url = url.ToString();
_domain = url.DnsSafeHost;
if (_domain.IndexOf(".") < _domain.Length - 4)
{
_domain = _domain.Substring(_domain.IndexOf(".") + 1);
}
_tld = _domain.Substring(_domain.LastIndexOf("."));
_targetServer = Global.Global.WhoisServersConf.GetValue
("Servers", _tld, typeof(string)).ToString();
if (_targetServer == "" || _targetServer == null)
{
_targetServer = _defaultTargetServer;
}
}
}
public void GetResponse()
{
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerAsync();
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
string response = DoRequest(_targetServer);
StringReader sr = new StringReader(response);
if (response.Contains("Whois Server:"))
{
string newTargetServer = "";
while (newTargetServer == "")
{
string line = sr.ReadLine().Trim();
if (line.StartsWith("Whois Server:"))
{
newTargetServer = line.Substring(14);
DoRequest(newTargetServer);
}
}
}
OnConnectionClosed();
}
string DoRequest(string server)
{
WhoisEventArgs wea = new WhoisEventArgs(
_url,
_tld,
server,
"Trying to connect to " + server + " (query : " + _domain + ") ...\r\n"
);
OnResponseReceived(wea);
string response = "";
TcpClient client = null;
try
{
client = new TcpClient(server, 43);
}
catch (Exception ex)
{
wea = new WhoisEventArgs(
_url,
_targetServer,
_targetServer,
"Connection to server " + server + " failed : \r\n" +
ex.Message + "\r\n\r\n"
);
OnResponseReceived(wea);
}
if (client != null)
{
string formatedDomain = _domain + "\r\n";
byte[] byteUrl = Encoding.ASCII.GetBytes(formatedDomain);
Stream s = client.GetStream();
bool error = true;
try
{
wea = new WhoisEventArgs(
_url,
_targetServer,
_targetServer,
"Connection to server " + _targetServer +
" established. Executing query ...\r\n\r\n"
);
OnResponseReceived(wea);
s.Write(byteUrl, 0, formatedDomain.Length);
error = false;
}
catch (Exception ex)
{
wea = new WhoisEventArgs(
_url,
_targetServer,
_targetServer,
"Unable to perform query : \r\n" + ex.Message + "\r\n\r\n"
);
OnResponseReceived(wea);
}
if (!error)
{
StreamReader sr = new StreamReader(s, Encoding.ASCII);
response = sr.ReadToEnd() + "\r\n";
sr.Close();
sr.Dispose();
sr = null;
wea = new WhoisEventArgs(
_url,
_targetServer,
_targetServer,
response
);
OnResponseReceived(wea);
}
s.Close();
s.Dispose();
s = null;
client.Close();
client = null;
}
return response;
}
void OnResponseReceived(WhoisEventArgs e)
{
if (ResponseReceived != null)
{
ResponseReceived(this, e);
}
}
void OnConnectionClosed()
{
if (ConnectionClosed != null)
{
ConnectionClosed(this, null);
}
}
}
}
Explanation
The WhoisEventArgs
class is used to pass some arguments to the main project (or the console or MessageBox
, maybe).
When we want to perform a request, we use this code:
WhoisRequest req = new WhoisRequest("http://www.codeproject.com/");
req.ResponseReceived += new EventHandler<WhoisEventArgs>(req_ResponseReceived);
req.ConnectionClosed += new EventHandler(req_ConnectionClosed);
req.GetResponse();
So what's going on when we call this code ?
- If we pass a URI in the ctor, directly call the
Initialize (Uri uri)
method. If we pass a string, try to convert it to a valid URI and then call the Initialize (Uri uri)
method. - When we have our so called URI, we can extract from the *.ini file (or whatever you want, where you have saved your list) the whois server corresponding to the TLD of that URI.
Note: In my example, I use my own INI parser which I'll probably post to The Code Project in the near future (the line "Global.Global.WhoisServersConf.GetValue([...])
").
Then we wait for the call of the GetResponse()
method.
- When this method is called, the hard work starts.
Note: I use a BackgroundWorker
there because, once again, it's more flexible for my own project. The worker is optional, you can just adapt to suit your needs.
- We make a first query.
- If that query returns a result containing the words "Whois Server:", then the server we queried is not the actual server handling the domain of the URL we're checking. So we make a second query against the server indicated in the response.
Final Words
I hope this will be helpful. This is my first contribution to The Code Project, and I know that it won't be the last.
I think many things in that little class can be improved, so I wait for your comments.
Points of Interest
If we want to slightly customize the way to do queries, we can, like the GNU client, use some options, as removing advertisement message or server description, just show the final response when we haven't queried the right server for the first time, etc.
History
- 13/02/2008 - 1.0: First publication
- 06/09/2010 - Updated source code
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.