LOGIC Library

This site is the Logic involvement in sharing expertise and skills acquired in daily work. The goal is to create a solid knowledge base and share best practices in software development and systems management.

More info about us can be found on logicsistemi.it.

ASP.NET IP address in-depth analysis using the RFC3912 - WHOIS protocol

Sometimes it can rise the need of having to thoroughly analyze the IP addresses of visitors to our site in order to change the behavior of the same depending on the information that reveals us the address.

Purposes

In our case due to continuous and suspicious visits all coming from a particular country we had the need to temporarily block access from that country. Obviously not being able to prevent anyone to type the URL of our site we drastically decided to redirect all visits coming from this country to another site. Anyway other uses - less drastic - of this mechanism could, for instance, showing a country specific default page bypassing the settings of visitor's browser and OS.

First we have to make some assumptions

In the cloud of the Internet there are many services that - given an IP - provide key information such as the ISP, country of origin or, even, the geolocation. Some of these web services (WS) are free (but often with many limitations) others not, however querying a WS (either REST or SOAP) if a side is an extremely versatile solution, on the other hand has the drawback of requiring a certain system overhead, particularly significant in the process of creating web pages. So we propose an alternative solution, less sophisticated, but extremely fast, that uses the standard WHOIS procotol on TCP/IP port 43. It's obvious that the identification of the IP should be done only once, at the user's first visit also - if possible - it would certainly be more efficient to build a database of addresses, this to avoid burdening our site with continuous calls to an external service. It should also be provided that the WHOIS service responds in ASCII plain text, and this implies the necessity of having to do some simple string operations of extraction.

Solution proposed

The issue of one-off control is easily solved using the Global.asax class and in particular the event Session_Start that is automatically invoked only once at startup, in fact, a new session by a visitor. The IPs repository can be implemented on a database table or by a local file. We prefer the data based solution since doing CRUD operations on files (even if XML) can be heavy, even if this solution free the need to have a mandatory data base engine (with additional costs) connected to the web application. It's clear that the IP addresses and related information are not immutable then if you want you could add a check that - after a certain period - asks for an upgrade of a address already exists in our local repository. Let's see in detail the implementation, the code comments explain the various steps, as you can see it requires very few lines of code


void Session_Start(object sender, EventArgs e) 
{
	try{
		//Get the visitor's IP address
		string ip = Request.UserHostAddress.ToString();
		//SQL connection string for IPs repository from web.config (for DB repository solution, not mandatory)
		string ConStr = (String) System.Configuration.ConfigurationManager.AppSettings[“SqlConnection”];
		//Open connection
		System.Data.Odbc.OdbcConnection odbcConn = new System.Data.Odbc.OdbcConnection(ConStr);
		if (odbcConn.State == System.Data.ConnectionState.Closed)
			odbcConn.Open();
		//Query the IPs repository table, the check is made on whole IP address, you can better and faster define it by checking range of address instead
		string Sql = String.Format("SELECT * FROM ips where ip ='{0}'", ip);
		System.Data.Odbc.OdbcCommand odbcCom = new System.Data.Odbc.OdbcCommand(Sql, odbcConn);
 	    System.Data.Odbc.OdbcDataReader rd = odbcCom.ExecuteReader();
	    string whois_answer = "";
	    if (!rd.Read())
	    {
			//Who are you? Ok let's check from a whois service, for instance whois.ripe.net, you can put this URL in web.config
			//Open a tcp/ip sockets
			System.Net.Sockets.TcpClient objTCPC = new System.Net.Sockets.TcpClient("whois.ripe.net", 43);
			//Set time out in milliseconds 
			objTCPC.ReceiveTimeout = 5000;
			//whois parameter (we need to envelop it on a ASCII stream, much simplier and easier than a XML WS call)
			string strDomain = ip + "\r\n";
			byte[] arrDomain = Encoding.ASCII.GetBytes(strDomain);
			System.IO.Stream objStream = objTCPC.GetStream();
			//"CALL" the whois service
			objStream.Write(arrDomain, 0, strDomain.Length);
			//Get the answer in ASCII plain text
			System.IO.StreamReader objSR = new System.IO.StreamReader(objTCPC.GetStream(), Encoding.ASCII);
			whois_answer = objSR.ReadToEnd()
			//Close the socket
			objTCPC.Close();
			//Put here your ip’s table new insert
			//Insert into ips….
		}
		else
		{
			//Welcome back my friend we remember you, in whois_answer is stored the previous whois service answer, you can store only country in order to save space and time
			whois_answer = (string)rd["whois_answer"];
			//Now we can decide if the visit is older than a certain time limit to re-check the IP
		}
		//Let's isolate only "COUNTRY: xxxxxx\r\n" key info, we know this is a weird solution, you can use REGEX class instead
		if (whois_answer.Length > 0)
			whois_answer = whois_answer.Substring(whois_answer.ToLower().IndexOf("country"), whois_answer.ToLower().IndexOf("\r\n", whois_answer.ToLower().IndexOf("country")) - whois_answer.ToLower().IndexOf("country"));
		//List of banned countries from web.config (";" separated)
		if (System.Configuration.ConfigurationManager.AppSettings["Countries "]!=null && System.Configuration.ConfigurationManager.AppSettings["DefaultRedirect "]!=null)
		{
			string[] countries = System.Configuration.ConfigurationManager.AppSettings["Countries"].ToString().ToLower().Split(';');
			foreach (string s in countries)
			{
				//Check only the xx country code corrispondence
				if (whois_answer.ToLower().Replace("country:", "").Trim() == s)
				{
					//Sorry but you can't see my site, take a look on this other beautiful site from web.config, bye bye
					Response.Redirect(System.Configuration.ConfigurationManager.AppSettings["DefaultRedirect"]);
				}
			} 
		}
	}
	catch (SystemException ex)
	{
	  //Something went wrong… let’s do something
	}
}