Par exemple, le serveur DNS retourne crawl-66-249-65-102.googlebot.com sur l'adresse IP 66.249.65.102.
En PHP, le nom d'hote est obtenu avec la fonction gethostbyaddr(). Dans l'application de localisation d'ip, l'opération est répétée sans cesse pour en apprendre d'avantage sur une adresse ip. La rapidité de la fonction est importante, chaque seconde de perdu réduit les performances des scripts de geolocation.
Function php gethostbyaddr()
Prenons un benchmark pour mesurer les différences de performance de la fonction gethostbyaddr(). Le sigle @ sert à désactiver les warning sur cette fonction.<?php
//Mesure de perf sur une adresse ip de Google
$t0=microtime(true);
$hostname=@gethostbyaddr('66.249.65.102');
$st=microtime(true)-$t0;
echo "IP 66.249.65.102 <br>nom d'hote :".$hostname."<br>duree :".substr($st,0,4).' sec';
//Mesure de perf sur une adresse ip située en Afrique
$t0=microtime(true);
$hostname=@gethostbyaddr('41.223.184.3 ');
$st=microtime(true)-$t0;
echo "<br>IP 41.223.184.3 <br>nom d'hote :".$hostname."<br>duree :".substr($st,0,4).' sec';
?>
Résultats
IP 66.249.65.102
nom d'hote :crawl-66-249-65-102.googlebot.com
duree :0.02 sec
IP 41.223.184.3
nom d'hote :
duree :9.50 sec
nom d'hote :crawl-66-249-65-102.googlebot.com
duree :0.02 sec
IP 41.223.184.3
nom d'hote :
duree :9.50 sec
L'adresse 41.223.184.3 ne retourne aucun nom d'hote. Le temps de réponse de 9 secondes correspond au timeout de la fonction gethostbyaddr(). Dans le cas des scripts de geoloc, c'est bien trop long !
Comment réduire la durée du Timeout ???
N'ayant pas trouvé de solution, j'ai préféré utiliser d'autres fonctions pour déterminer le nom d'hote d'une adresse ip.
Résolution avec le protocole UDP
Au fil des recherches, il apparait que la resolution du nom d'hote est une fonction de base sur un réseau. Cette fonction traduite à partir d'un code source en C semble parfait. Le timeout peut etre défini et le protocole UDP est en général rapide.<?php
/**
* Function gethostbyaddr fast resolve Hostname and with Timeout !
*
* @see http://php.net/manual/en/function.gethostbyaddr.php#46869
* @param string $ip IP Address to resolve
* @param string $dns DNS Server IP
* @param int $timeout
* @return string Hostname
*/
function gethostbyaddr_timeout($ip, $dns, $timeout=1000) {
// random transaction number (for routers etc to get the reply back)
$data = rand(0, 99);
// trim it to 2 bytes
$data = substr($data, 0, 2);
// request header
$data .= "1 1 ";
// split IP up
$bits = explode(".", $ip);
// error checking
if (count($bits) != 4) return "ERROR";
// there is probably a better way to do this bit...
// loop through each segment
for ($x=3; $x>=0; $x--) {
// needs a byte to indicate the length of each segment of the request
switch (strlen($bits[$x]))
{
case 1: // 1 byte long segment
$data .= "1"; break;
case 2: // 2 byte long segment
$data .= "2"; break;
case 3: // 3 byte long segment
$data .= "3"; break;
default: // segment is too big, invalid IP
return "INVALID";break;
}
// and the segment itself
$data .= $bits[$x];
}
// and the final bit of the request
$data .= "7in-addr4arpa??x0C?1";
// create UDP socket
$handle = fsockopen("udp://$dns", 53);
if (!$handle) {
return NULL;
}
// send our request (and store request size so we can cheat later)
$requestsize=fwrite($handle, $data);
socket_set_timeout($handle, $timeout - $timeout00, $timeout00);
// hope we get a reply
$response = fread($handle, 1000);
fclose($handle);
if ($response == "") {
return $ip;
}
// find the response type
$type = @unpack("s", substr($response, $requestsize 2));
if ($type[1] == 0x0C00) // answer
{
// set up our variables
$host="";
$len = 0;
// set our pointer at the beginning of the hostname
// uses the request size from earlier rather than work it out
$position=$requestsize 12;
// reconstruct hostname
do {
// get segment size
$len = unpack("c", substr($response, $position));
// null terminated string, so length 0 = finished
if ($len[1] == 0)
// return the hostname, without the trailing .
return substr($host, 0, strlen($host) -1);
// add segment to our host
$host .= substr($response, $position 1, $len[1]) . ".";
// move pointer on to the next segment
$position = $len[1] 1;
} while ($len != 0);
// error - return the hostname we constructed (without the . on the end)
return $ip;
}
return $ip;
}
?>
Résolution avec la fonction Host de Linux
Les serveurs de Geoloc tournent sur des VM Linux, les scripts PHP ont donc accès aux fonctions de base de l'OS. La fonction suivante est un wrapper de la fonction host avec un timeout fixé à 2 secondes.<?php
function gethostbyaddr_exec($ip) {
$output = shell_exec('host -W 2 '.$ip);;
if (ereg('.*pointer ([A-Za-z0-9.-] )..*',$output,$regs)) {
return $regs[1];
}
return $ip;
}
?>