Twitter Facebook RSS Feed

domingo, 24 de mayo de 2009 a las 15:24hs por Gustavo Cantero (The Wolf)

Muchos habrán notado que al ingresar en algunos sitios web éstos modifican su contenido, incluyendo idioma, publicidad, textos, etc., dependiendo del país desde el que estamos ingresando, por ejemplo, al ingresar en www.linksys.com desde Argentina éste nos redirecciona a www-ar.linksys.com, o al ingresar a www.google.com nos reenvía a www.google.com.ar. Para poder hacer esto la aplicación web busca la IP con la que estamos ingresando en una base de datos donde previamente se cargaron rangos de IP con su respectivo país.

Hasta acá todo parece muy obvio, el problema surge cuando nos preguntamos: ¿Dónde conseguimos estas bases de datos? La respuesta es sencilla: en internet.

En internet hay varios sitios que ofrecen, gratuitamente o como servicio pago, archivos con rangos de IP con su respectivo país. Muchos de estos sitios ofrecen además web services para poder consultar contra su base de datos la ubicación correspondiente a una dirección IP sin necesidad de tener los datos alojados en nuestro sitio. En este artículo voy a mostrar cómo obtener la ubicación consultando los datos en una base de datos local.

Uno de los sitios desde donde se pueden bajar estos datos es WebHosting.Info (www.webhosting.info), un sitio dedicado a dar servicios y estadísticas sobre ISP, WHOIS, Reverse IP Lookup, etc. Los datos que estos sitios ofrecen casi siempre están en archivos CSV, donde el país está codificado en ISO 3166 y la dirección IP está como un entero sin signo de 4 bytes llamado “número IP”.

Para obtener la lista de códigos de países según la ISO 3166 pueden ver en el sitio mismo de ISO, http://www.iso.org/iso/country_codes, o en Wikipedia, http://es.wikipedia.org/wiki/ISO_3166-1.
Para convertir la dirección IP en un número IP (un entero de 4 bytes) es una simple fórmula matemática donde lo que tenemos que hacer es un “shift left” de cada número para “posicionarlo” en el octeto correspondiente o, en español, moverlo hasta el byte correspondiente. Como cada byte representa 256 números (de 0 a 255), lo que debemos hacer es multiplicar cada octeto de la dirección por 256 elevado a la posición de ese byte. Supongamos que nuestra dirección a convertir es A.B.C.D, entonces lo que debemos hacer es:

(A * 256^3) + (B * 256^2) + (C * 256^1) + (D * 256^0)

Si reemplazamos las constantes por su resultado nos quedaría lo siguiente:

(A * 16777216) + (B * 65536) + (C * 256) + D

Ahora supongamos que la IP de mi cliente es la 190.2.8.88, entonces deberíamos hacer:

(190 * 16777216) + (2 * 65536) + (8 * 256) + 88 =
   3187671040    +   131072    +    2048   + 88 = 3187804248

Cabe mencionar que para obtener la dirección IP del usuario que se está conectando a nuestro sitio se debería leer el encabezado de la petición http. Para los desarrolladores de .NET esto se puede hacer simplemente utilizando la propiedad UserHostAddress del objeto Request del Page, por ejemplo, en C# sería:

string strIP = Page.Request.UserHostAddress;

o, para los usuarios de Visual Basic.NET:

Dim strIP as string = Page.Request.UserHostAddress

Obviamente la IP está en un string, con lo cual debería dividir cada octeto de la dirección, convertirlo a un tipo de dato numérico y hacer la fórmula. A continuación les dejo un ejemplo en C#:

//Convierto la IP a un entero de 32 bits
string[] strBytes = Page.Request.UserHostAddress.Split('.');
uint intNumeroIP=
    (uint.Parse(strBytes[0]) * 16777216) +
    (uint.Parse(strBytes[1]) * 65536) +
    (uint.Parse(strBytes[2]) * 256) +
    uint.Parse(strBytes[3]);

y, para los usuarios de Visual Basic.NET:

'Convierto la IP a un entero de 32 bits
Dim strBytes As String() = Page.Request.UserHostAddress.Split('.')
Dim intNumeroIP As UInteger = _
    (UInteger.Parse(strBytes(0)) * 16777216) + _
    (UInteger.Parse(strBytes(1)) * 65536) + _
    (UInteger.Parse(strBytes(2)) * 256) + _
    UInteger.Parse(strBytes(3))

Nótese que en el ejemplo utilicé un entero sin signo, esto es porque el número IP no lleva signo. También cabe notar que, aunque cada octeto se puede representar con un byte, yo lo convierto a un entero sin signo para evitar la conversión del tipo de dato al almacenarlo en la variable final.

Ahora con este número en mente lo podemos buscar en el archivo con la información de la localización de las IPs.  Obviamente lo mejor es guardar previamente la información de este archivo en una base de datos. Como mínimo deberíamos guardar la dirección IP y el país. Muchos de estos archivos de IP nos traen una “dirección desde” y una “dirección hasta” por cada línea del archivos, pero con almacenar la “dirección desde” ya nos alcanza.
Para los usuarios de motores de bases de datos como Oracle o MySql no tendrán problema con guardar los números IP en campos enteros de 4 bytes sin signo, pero para los usuarios de SQL Server se nos complica, ya que este motor no permite utilizar este tipo de datos. Para solucionar este inconveniente podemos hacer dos cosas: guardar el número en un campo del tipo entero de 8 bytes o a cada número IP restarle 2147483648.

Ahora sólo nos quedaría buscar la IP del usuario en la base, con un script sencillo como el siguiente para SQL Server:

SELECT TOP 1 Pais
FROM ListaIPs
WHERE IP >= 3187804248
ORDER BY IP DESC

Si en SQL Server elegimos utilizar un entero de 4 bytes (int) y restarle la mitad para no utilizar un entero de 8 bytes (bigint) habría que utilizar el valor 1040320600 en lugar de 3187804248.

Si quisiéramos hacer esta misma consulta pero utilizando MySql como motor de base de datos deberíamos utilizar un script como el siguiente:

SELECT Pais
FROM ListaIPs
WHERE IP >= 3187804248
ORDER BY IP DESC
LIMIT 1

Bien, hasta acá ya sabemos cómo obtener el país correspondiente a una dirección IP pero, como el título bien dice, también podemos obtener la ciudad correspondiente a esta dirección. Para esto la solución vuelve a ser sencilla, solamente tenemos que buscar en internet donde se puede conseguir una lista de IPs correspondiente a cada ciudad.  Un lugar donde se puede obtener esta lista es en IP Location Tools (http://iplocationtools.com), donde se puede conseguir la lista de país por IP y ciudad por IP, en formato CSV y en SQL. La única diferencia con el proceso anterior es que en la base de datos, además o en lugar de guardar el país, se debería guardar la ciudad.

Espero que este artículo les haya sido de utilidad y, como siempre, los animo a escribir sus consultas, sugerencias y comentarios.

5 comentarios »

  1. lottus s.a. dice:

    felicitaciones excelente tu pagina

  2. cce dice:

    Muy bien explicado, pero de donde saco la base de datos con el pais, ciudad y demas informacion para cada ip..Entre a la pagina de ISO pero tenes q pagar…saludos.-

  3. que bardo, se agradece igualmente…

Deja un comentario

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.