Using SQL CLR Stored Procedure To Track IP Address

24. April 2010 00:24

C# SQL Server T-SQL  1 Comment

Recently I was asked by one of my friend how to call a web service directly from a stored procedure. He needs to track IP addresses using a stored procedures so, I suggested him two ways to call a web service using the SQL Procedure.

  • Using SP_OACREATE with MSXML
  • Create an assembly using SQL CLR using C#.

I personally tried using the first way to call a web service but was not successful. Then I learnt about SQL CLR programming with Visual Studio and C# and get it done in a first go. As a good friend I create the whole project for my friend. And if your thinking that this is going to be a difficult, this is not so.

To start creating a project, open Visual Studio and create a new project.

Before the project gets loaded completely, you will be asked to add database reference. Here select the database you want to build the assembly for. If you don’t want to add a database reference then just simply cancel. But if you choose the reference to add at this point then this would really be of help as this will allow you to deploy your assembly directly to the SQL Server database but if you have not then you need to write some SQL scripts.

NOTE: If you have added a database reference then it doesn’t mean that your assembly is build specifically for that database you can still deploy your assembly in other database also.

As I am creating a stored procedure, I then have to add a New Item to my project. Right-click project choose Add>Stored Procedure.  Here give a name of your choice to stored procedure. In my case it is IPInfo.

Change the static method here to accept a parameter. Our parameter will be an IP address, which I have set as of string type.

public static void GetIPInfo(string IP)
{  
//Do something here
}

Now the question here is from where we can the information of the IP Addresses? I do some search and found a website http://www.geobytes.com. It offers some rich information of the IP Address you want to track, so out of five sources I choose to go with geobytes.

Lets take a quick look at the code. This is not a web service as you can see from the code below, I am just making a http request and then get the response in an XML form so I can easily traverse the nodes and read/save the information I received.

string XMLResponse;
IPAddress = IP;
byte[] XMLResults;
string str = "http://www.geobytes.com/IpLocator.htm?GetLocation&template=xml.txt"
+ "&IpAddress=" + IP;
XMLResults = new WebClient().DownloadData(str.ToString());
XMLResponse = Encoding.UTF8.GetString(XMLResults);

Note the fourth line where I have built the URL. The parameter Template should always have a value to xml.txt. If you change the name, it won’t throw any error but the data received cannot be read and saved in a database table. Convert the response to string format and then read the nodes one by one using the below code.

try
        {
            XmlDocument document = new XmlDocument();
            document.LoadXml(XMLResponse);
            XmlNode documentElement = document.DocumentElement;
            if (documentElement.HasChildNodes)
            {
                for (int i = 0; i < documentElement.ChildNodes.Count; i++)
                {
                    if (documentElement.ChildNodes[i].NodeType == XmlNodeType.Element)
                    {
                        switch (documentElement.ChildNodes[i].Name)
                        {
                            case "locationcode":
                                LocationCode = documentElement.ChildNodes[i].InnerText;
                                break;

                            case "fips":
                                FIPS104 = documentElement.ChildNodes[i].InnerText;
                                break;

                            case "iso2":
                                ISO2 = documentElement.ChildNodes[i].InnerText;
                                break;

                            case "iso3":
                                ISO3 = documentElement.ChildNodes[i].InnerText;
                                break;

                            case "ison":
                                ISON = documentElement.ChildNodes[i].InnerText;
                                break;

                            case "internet":
                                Internet = documentElement.ChildNodes[i].InnerText;
                                break;

                            case "countryid":
                                CountryID = documentElement.ChildNodes[i].InnerText;
                                break;

                            case "country":
                                Country = documentElement.ChildNodes[i].InnerText;
                                break;

                            case "regionid":
                                RegionID = documentElement.ChildNodes[i].InnerText;
                                break;

                            case "region":
                                Region = documentElement.ChildNodes[i].InnerText;
                                break;

                            case "code":
                                RegionCode = documentElement.ChildNodes[i].InnerText;
                                break;

                            case "adm":
                                Admn1Code = documentElement.ChildNodes[i].InnerText;
                                break;

                            case "cityid":
                                CityID = documentElement.ChildNodes[i].InnerText;
                                break;

                            case "city":
                                City = documentElement.ChildNodes[i].InnerText;
                                break;

                            case "latitude":
                                Latitude = documentElement.ChildNodes[i].InnerText;
                                break;

                            case "longitude":
                                Longitude = documentElement.ChildNodes[i].InnerText;
                                break;

                            case "timezone":
                                TimeZone = documentElement.ChildNodes[i].InnerText;
                                break;

                            case "certainty":
                                Certainty = documentElement.ChildNodes[i].InnerText;
                                break;
                        }
                    }
                }
            }
        }
        catch (Exception) { }

I traversed through each node and save the response data in a variable I have already declared in my program using a switch case. Once all the response has been assigned to the variable save the response to the database.

try
        {
            using (SqlConnection con = new SqlConnection("context connection=true"))
            {
                string strSQL = "INSERT INTO IPTrack values ('" + IPAddress + "','" + LocationCode + "','" + FIPS104 + "','" + ISO2 + "','" + ISO3 + "','" + ISON + "','" + Internet + "','" + CountryID + "','" + Country +
                    "','" + RegionID + "','" + Region + "','" + RegionCode + "','" + Admn1Code + "','" + CityID + "','" + City + "','" + Latitude + "','" + Longitude + "','" + TimeZone + "','" + Certainty + "')";
                SqlCommand cmd = new SqlCommand(strSQL, con);
                //SqlContext.Pipe.ExecuteAndSend(cmd);
                con.Open();
                SqlContext.Pipe.ExecuteAndSend(cmd);
                //cmd.ExecuteNonQuery();
            }
        }
        catch (Exception) { }
    }

Build you project and you will see a DLL file in your debug/release folder. But this will not work. You will still have to set some permission level to assembly and to SQL Server database.

Set Assembly Permissions

Before you build the final version of the assembly go the project properties and set the permissions as shown below.

Under Build select option for Generate Serialization Assembly to On.

Under Database select Permission Level to Unsafe

SQL Server Permissions

If you find problems while deploying your assembly, then make sure you have CLR enabled and set the Trustworthy property for the database to ON.

You can find all the scripts in the attached solution below.

Deploying your assembly

To deploy your assembly use the below SQL command:

CREATE ASSEMBLY IPTracker from 'C:\IPInfo.dll' WITH PERMISSION_SET = EXTERNAL_ACCESS
GO

To create a stored procedure whcih in trun calls the SQL method from the deployed assembly:

CREATE PROC TrackIP(@IP as nvarchar(50))AS 
-- [Assembly Name].[Class Name].[CLR function Name]
EXTERNAL NAME IPTracker.StoredProcedures.GetIPInfo 
GO

And execute the procedure to get the IP information

Download: IPInfo.zip (19.46 kb)

Currently rated 5.0 by 1 person

Comments (1)

GopalChettri GopalChettri
6/7/2010 11:27:26 AM #

Hey dude.nice work.i was about to put my head on this.but came across your work.so my 50% work is done. thanx...

Add Comment

Visit blogadda.com to discover Indian blogs Computers Blogs