Uno dei servizi che capita più comunemente di incontrare nei siti WEB delle aziende fornitrici hosting riguarda la possibilità di verificare se un nome dominio di secondo livello sia disponibile oppure no.
Le informazioni sono affidate ad appositi database consultabili pubblicamente, i cosiddetti "WHOIS DATABASE", che si spartiscono la gestione dei dati sui domini in base al TLD (top level domain) di appartenenza: ad esempio per i domini di secondo livello con TLD ".it" è necessario fare riferimento a whois.nic.it.
In questo articolo sopriremo come realizzare script Php in grado di connettersi ai WHOIS, effettuare un "whois lookup" (interrogazione) e verificare lo status di un nome dominio.
Php e socket
Nell'articolo Php, Socket e HTTP (articolo completo) abbiamo illustrato l'interazione diretta con un webserver attraverso le funzioni per i socket e il protocollo HTTP.
Anche un WHOIS è un servizio basato su TCP/IP ma normalmente sta in ascolto sulla porta 43 (non sulla 80, tipica dei webserver) e il protocollo utilizzato è molto più semplice rispetto a HTTP, come risulta evidente dall'esempio che segue (puramente dimostrativo).
Ci siamo serviti anche questa volta della funzione client generica fsockopen()
<?php
/*
Il dominio di secondo livello sui cui desideriamo ricavare informazioni,
notare l'assenza di "WWW"
*/
$dominio ='html.it' ;
/*
l'host del servizio
*/
$whois = 'whois.nic.it' ;
$info = '' ;
/*
La connessione
*/
$sk=fsockopen($whois, 43, $errno, $errstr, 30) or die('Connessione impossibile') ;
/*
Inviamo la request
*/
fputs ($sk, $dominio."rn") or die('Request impossibile') ;
/*
Leggiamo il response
*/
while (!feof($sk)) {
$info.= fgets ($sk, 2048);
}
/*
Output con le informazioni ricavate
*/
echo('<pre>'.$info.'</pre>') ;
?>
Lo script è del tutto analogo a quelli illustrati nell'articolo citato poco fa, ma balza agli occhi l'esiguità dei dati da inviare nella request: il nome dominio coprensivo di TLD e un "a-capo".
È importante notare che non è possibile effettuare query per più di un dominio alla volta, infatti il servizio chiude la connessione immediatamente dopo aver inviato la risposta.
Il risultato di una request
Da un "whois lookup" possiamo attenderci soltanto due tipi di risultato:
- se il nome dominio è già stato assegnato ci verranno forniti tutti i dati che lo riguardano ;
- oppure il servizio ci informerà che non ha trovato alcun riscontro nel database: questo potrebbe voler dire che il dominio in questione è disponibile, o che ci siamo rivolti al WHOIS sbagliato.
Sostanzialmente non ci viene detto "Il dominio non è disponibile" ma "Io non ne so nulla":
quindi è fondamentale conoscere il corretto WHOIS da interrogare, cioè quale sia quello che gestisce le informazioni per il TLD del dominio di secondo livello che ci interessa.
Un'altra cosa da tenere presente è che i vari servizi non comunicano tutti esattamente allo stesso modo: ad esempio internic.net indica il mancato riscontro per il domain name richiesto con l'espressione "NO MATCH FOR..." mentre nic.it utilizza "No entries for...".
Verificare la presenza di una di queste espressioni nella stringa che contiene la risposta è l'unico modo per qualificarla esattamente attraverso uno script, ed è quindi indispensabile sapere quale sia l'espressione esatta utilizzata da un dato WHOIS.
Fortunatamente in rete possiamo reperire delle liste di WHOIS database molto complete, le quali forniscono queste e altre informazioni senza che vi sia bisogno di reperirle una per una.
Una delle migliori si trova a questo indirizzo http://www.mattsscripts.co.uk/servers.lst, mi è stata gentilmente indicata da Enrico Altavilla aka Low (www.motoricerca.info).
whois.internic.net
E utile spendere appositamente due parole su questo servizio: poichè la gestione delle informazioni per i TLD .net e .com è affidata ad un numero indefinito di società private (ognuna con un proprio WHOIS), si rende necessario un "servizio informazioni" centralizzato.
Internic.net è in grado di dire se un dominio sia disponibile oppure no, tuttavia se questo risulta già assegnato anziche fornire direttamente i dettagli ci indica quale sia il database specifico da interrogare.
Questo WHOIS può rispondere anche per i TLD aero, .arpa, .biz, .com, .coop (e altri ancora), ma per ulteriori informazioni consiglio di visitare www.internic.net.
Una classe per i WHOIS
Nelle pagine che seguiranno realizzeremo una classe in grado di interrogare qualsiasi WHOIS e interpretarne correttamente la risposta. Il package completo dei file è a disposizione degli utenti.
Lo script si compone di 3 classi, una principale (WHOISdb) e due accessorie (ErrorHandler e WHOISresult).
Classe ErrorHandler
È un sistema molto semplice per la gestione degli errori: la classe principale eredita da questa la capacità di intercettare alcuni eventi indesiderati significativi.
<?php
/**
* Classe per la gestione degli errori
*/
class ErrorHandler {
var $errors ;
var $TCP_CANNOT_CONNECT ;
var $TCP_CANNOT_SEND ;
var $TCP_CANNOT_READ ;
var $TCP_CANNOT_DISCONNECT ;
var $DOMAIN_NOT_VALID ;
function ErrorHandler()
{
/**
* Un messaggio per ogni tipo di errore che intendiamo intercettare
*/
$this->TCP_CANNOT_CONNECT = 'Impossibile connettersi a ' ;
$this->TCP_CANNOT_SEND = 'Impossibile inviare la seguente request: ' ;
$this->TCP_CANNOT_READ = 'Impossibile lettura da ' ;
$this->TCP_CANNOT_DISCONNECT = 'Impossibile chiudere la connessione a ' ;
$this->DOMAIN_NOT_VALID = 'Nome dominio non valido: ' ;
$this->errors = array() ;
}
/**
* Aggiunge l'errore alla lista
*/
function addError($msg)
{
$this->errors[] = $msg ;
}
/**
* Recupera la lista degli errori
*/
function getErrors()
{
$errorList = '' ;
foreach($this->errors as $v) {
$errorList .= $v . "n" ;
}
return($errorList) ;
}
} //END class ErrorHandler
?>
Classe WHOISdb
Per ogni WHOIS che desideriamo poter interrogare dobbiamo creare un oggetto istanza di questa classe.
Il costruttore richiede che si forniscano obligatoriamente come argomenti l'host del servizio e la stringa la cui presenza, all'interno della risposta, indica la disponibilità del dominio.
<?php
/**
* La classe principale:
*connesssione, query e lettura dei risultati da un WHOIS
*/
class WHOISdb extends ErrorHandler {
var $target ;
var $port ;
var $link ;
var $timeout ;
var $crlf ;
var $notFound ;
var $info ;
/**
* Costruttore
*/
function WHOISdb($target, $notFoundMsg, $port = 43, $timeout = 30, $br = "rn")
{
$this->ErrorHandler() ;
$this->target = $target ;
$this->port = $port ;
$this->timeout = $timeout ;
$this->crlf = $br ;
$this->notFound = $notFoundMsg ;
$this->info = '' ;
} //END contructor
/**
* Metodo privato per la connessione
*/
function _connect()
{
if (!$this->link = fsockopen($this->target, $this->port, $errno, $errstr, $this->timeout)) {
$this->addError($this->TCP_CANNOT_CONNECT . $this->target . ':' . $this->port) ;
return(false) ;
}
return(true) ;
} //END _connect
/**
* Metodo privato per invio di una request
*/
function _send($domainStr)
{
if (!fputs($this->link, $domainStr . $this->crlf)) {
$this->addError($this->TCP_CANNOT_SEND . $domain . '.' . $tld) ;
return(false) ;
}
return(true) ;
} //END _send
/**
* Metodo privato per la cattura del response
*/
function _get()
{
$dati = '' ;
/**
* Stabilisce il tempo di attesa max prima
* dell'inizio della lettura dei dati
*/
if (function_exists('stream_set_timeout')) {
stream_set_timeout($this->link, $this->timeout, 0);
} while (!feof($this->link) && $line = fgets ($this->link, 4096)) {
if ($line === false || $line === 0) {
$this->addError($this->TCP_CANNOT_READ . $this->target) ;
return(false) ;
}
$dati .= $line ;
}
return($dati) ;
} //END _get
/**
* Metodo principale (pubblico), è il metodo che
* controlla la disponibilità del dominio,
* verificando la presenza della stringa $this->notFound.
* Crea l'oggetto WHOISresult
*/
function checkDomain($domain, $tld)
{
$pattern = '<^[0-9A-Za-z]([0-9A-Za-z]|-)+[0-9A-Za-z].[A-Za-z]{2,4}(.[A-Za-z]{2,4})?$>' ;
$domainStr = $domain . '.' . $tld ;
/**
* Verifica la validità dei caratteri del dominio
*/
if (!preg_match($pattern, $domainStr)) {
$this->addError($this->DOMAIN_NOT_VALID . $domainStr) ;
return(false) ;
} elseif (!$this->_connect()) {
return(false) ;
} elseif (!$this->_send($domainStr)) {
return(false) ;
} elseif (!$this->info = $this->_get()) {
return(false) ;
}
$disponibile = (bool)stristr($this->info, $this->notFound) ;
return(new WHOISresult($domainStr, $disponibile, $this->info)) ;
}
} //END class WHOISdb
?>
checkDomain() è il metodo principale: cattura la risposta e la restituisce trasformata in un oggetto WHOISresult (vedi pagina successiva).
Classe WHOISresult
Ogni istanza di questa classe rappresenta il risultato restituito dall'interrogazione al WHOIS.
Sono disponibili tre metodi:
- isAvailable(): restituisce un valore booleano e indica se il dominio risulta già assegnato oppure no.
- getInfo(): restituisce la stringa completa con la risposta del servizio
- getDomain(): fornisce il nome del dominio verificato
<?php
/**
* Classe da cui si istanziano gli oggetti
* risultato delle query
*/
class WHOISresult {
/**
* Costruttore
*/
function WHOISresult($domain, $disponibile, &$info)
{
$this->domain = $domain ;
$this->disponibile = $disponibile ;
$this->info = &$info ;
}
/**
* Dice se il domain è disponibile oppure no
*/
function isAvailable()
{
return($this->disponibile) ;
}
/**
* Restituisce il nome del dominio richiesto
*/
function getDomain()
{
return($this->domain) ;
}
/**
* Restituisce la risposta completa del servizio WHOIS
*/
function getInfo()
{
return($this->info) ;
}
} //END class WHOISresult
?>
Un semplice esempio di utilizzo.....
<?php
/******************
file esempio1.php
*******************/
/*
Includiamo le classi
*/
include_once('./whoisClasses.php') ;
/*
Desideriamo ricevere informazioni su un dominio .it
*/
$itnicObj = new WHOISdb('whois.nic.it', 'no entries found ') ;
$resultObj = $itnicObj->checkDomain( 'html', 'it' ) or die( $itnicObj->getErrors() ) ; ;
echo( '<pre>'.$resultObj->getInfo().'</pre>' ) ;
?>
...ed uno più completo
<?php
/******************
file esempio2.php
*******************/
/*
Includiamo le classi
*/
include_once('./whoisClasses.php') ;
/*
Definiamo alcuni oggetti WHOISdb precisando per ognuno il server e la stringa che indica la disponibilità del dominio.
Aggiungi gli altri che ti interessano.
*/
$itnicObj = new WHOISdb('whois.nic.it', 'no entries found') ;
$internicObj = new WHOISdb('whois.internic.net', 'no match for') ;
$orgObj = new WHOISdb('whois.publicinterestregistry.net', 'not found') ;
$bizObj = new WHOISdb('whois.nic.biz', 'not found') ;
/*
Array che associa i TLD al corretto WHOIS database
*/
$tldList = array(
'it' => $itnicObj,
'com' => $internicObj,
'net' => $internicObj,
'org' => $orgObj,
'biz' => $bizObj
) ;
/*
I dati provenienti da un ipotetico form
con 2 campi: uno per selezionare il TLD e l'altro per il dominio di secondo livello
*/
$domain = 'html' ;
$tld = 'it' ;
if(!$resultObj = $tldList[ $tld ]->checkDomain($domain, $tld)){
die('Si sono verificati dei problemi, ci scusiamo per il disservizio') ;
/*
debug
*/
//$tldList[ $tld ]->getErrors() ;
}
elseif( $resultObj->isAvailable() ){
echo('<b>'.$resultObj->getDomain().' </b>è disponibile') ;
}
else{
echo('<b>'.$resultObj->getDomain().'</b> non è disponibile<br><br>') ;
echo('<b>Ecco i dettagli </b><br><br><pre>'.$resultObj->getInfo().'</pre>') ;
}
?>
Conclusioni
Abbiamo introdotto i servizi WHOIS e descritto come sia possibile interfacciarsi ad essi attraverso fsockopen() e le funzioni php per i socket.
Ci siamo serviti dell'approccio object oriented ponendo le basi per la realizzazioni di uno script completo da inserire nelle nostre pagine WEB, argomento di cui ci occuperemo prossimamente sempre su freephp.html.it.