Le pays d'une adresse IP
Obtenir le pays correspondant à une adresse IP
Vous vous souvenez de GeoIp, produit de la société MaxMind ? Ils ont un exécutable à installer sur un serveur pour obtenir le pays correspondant à une adresse IP.
J'avais deux soucis quand j'utilisais ce produit :
- d'une part j'avais affaire à un binaire dont je n'avais pas les sources, et faire tourner quelque chose d'opaque sur un serveur est une invitation au hack pour des escrocs russes et
- d'autre part j'étais tributaire de leurs mises à jour des données également opaques.
Tout peut se résoudre en utilisant le site ip-to-country. Leur méthode ? Faire que le pays d'une adresse IP soit obtenu par une simple requête SQL, ce qui donne :
- qu'il n'y a plus de binaire douteux à installer sur mon précieux serveur de prod
- et que je bénéficie de toutes les facilités du language SQL et de ses implémentations de mon choix (MySQL et PosgreSQL).
Pour fixer les idées la requête en question est presque aussi simple que celle-ci :
select countryName from ip2Country where \$MonIP<=highLimitIP and \$MonIP>=lowLimitIP;
Malheureusement les opérateurs <= et >= ne fonctionnent pas nativement sur les bases de données entre adresse IPs classiques. On va donc devoir les convertir en adresses IP numériques, c'est-à-dire en simples nombres entiers. Ainsi l'adresse 192.168.1.1 deviendra 3 136 235 777 = 192*2^24 + 168*2^16 + 1*2^8 + 1*2^0.
La source des données
C'est un simple fichier CSV téléchargeable depuis le site principal et régulièrement mis à jour. Voici un extrait pour examiner le format d'une ligne type :
... "202385408","202385919","PR","PRI","PUERTO RICO" "202385920","202706431","US","USA","UNITED STATES" "202706432","202706943","PR","PRI","PUERTO RICO" "202706944","203197063","US","USA","UNITED STATES" ...
Les deux premiers champs définissent un intervalle d'adresses IP au format numérique (par opposition au format séparé par des points) tel qu'une IP comprise dans cet intervalle est réputée représenter une machine installée dans le pays indiqué par les champs qui suivent. Les trois champs en question sont : le code ISO 3166-1 alpha 2 à deux lettres du pays, son code ISO 3166-1 alpha 3 à trois lettres et son nom complet.
Avant d'insérer les données dans le moteur de base de données, il faut nettoyer le fichier d'entrée pour que MySQL puisse le traiter sans complications :
cat ip-to-country.csv | sed 's/\"//g;s/\r//g' > ip2Country.csv
Ensuite, à partir d'un prompt MySQL et avec le fichier csv nettoyé et dans le path, on peut créer la table qui va héberger nos données :
create table ip2Country
(
lowIP int unsigned,
highIP int unsigned,
code2 varchar(2),
code3 varchar(3),
name varchar(60),
INDEX(lowIP)
) ENGINE=INNODB;On peut terminer cette phase d'installation en important les données du fichier transformé dans la table MySQL que l'on vient de créer avec la commande suivante :
load data local infile './ip2Country.csv' into table ip2Country
fields terminated by ","
lines terminated by "\n"
(lowIP, highIP, code2, code3, name);Le résultat
Il reste donc l'essentiel et le plus simple à faire : interroger la base de données avec une IP quelconque pour en déduire le pays dans lequel la machine correspondante est installée.
Ainsi, si on utilise PHP on peut donc avoir la fonction suivante et triviale qui renvoie le pays correspondant à une adresse IP passée en paramètre :
function IPToCountry($IP)
{
$NumIP = ip2long($IP); //PHP possède une fonction de conversion pour les adresses IP
$Result = mysql_query("select * from ip2Country where lowIP<=$NumIP and highIP>=$NumIP;");
$Result = mysql_fetch_array($Result);
return $Result[0];
}Tant qu'on y est, puisque j'aime le Bash, voici un oneliner qui fait la même chose depuis la ligne de commande :
mysql --silent --execute
"SELECT name FROM ip2Country WHERE lowIP<=inet_aton('$IP') AND highIP>=inet_aton('$IP')"L'intérêt de cette solution réside donc à la fois dans la simplicité de sa mise en oeuvre et dans son ouverture totale. On a vu qu'en quelques minutes on dispose d'une solution fonctionnelle, duplicable, d'un niveau demandant de faibles compétences techniques et à moindre coût.
