In questo tutorial vedremo come effettuare la scansione delle porte di un host con PHP.

Si tratta di creare un range di numeri di porte validi (non inferiori a 1 e non superiori a 65535) e di usare la funzione core fsockcopen() cercando di stabilire una connessione con l'host remoto.

Se la funzione restituisce false, vuol dire che non è stato possibile stabilire una connessione. Viceversa, chiudiamo la connessione appena stabilita e aggiungiamo la porta all'array delle porte aperte.

I valori da validare sono il nome dell'host, che può essere un nome a dominio o un indirizzo IP e la porta di partenza e di destinazione.

Possiamo racchiudere questa logica in una classe.

class Scanner {

    const MAX_PORTS = 65535;

    private $host;
    private $start;
    private $end;
    private $results;

    public function __construct($host, $start, $end)
    {
        $this->host = $host;
        $this->start = $start;
        $this->end = $end;

        $this->results = [
            'open' => [],
            'closed' => []
        ];
    }

    public function getResults()
    {
        return $this->results;
    }

    public function scan()
    {
        if(!$this->validate()) {
            return false;
        }
        $ports = range($this->start, $this->end);
        for($i = 0; $i < count($ports); $i++) {
            $port = $ports[$i];
            $ping = $this->ping($port);
            if($ping) {
                $this->results['open'][] = $port;
            } else {
                $this->results['closed'][] = $port;
            }
        }
    }

    private function ping($port)
    {
        $socket = fsockopen($this->host, $port, $errno, $errstr, 1);
        if(!$socket) {
            return false;
        } else {
            fclose($socket);
            return true;
        }
    }

    private function validate()
    {
        $valid = true;
        if(!filter_var($this->host, FILTER_VALIDATE_DOMAIN) && !filter_var($this->host, FILTER_VALIDATE_IP)) {
            $valid = false;
        }
        if(!is_int($this->start) || !is_int($this->end)) {
            $valid = false;
        }

        $startPort = (int) $this->start;
        $endPort = (int) $this->end;

        if($startPort < 1 || $startPort >= self::MAX_PORTS || $startPort > $endPort) {
            $valid = false;
        }
        if($endPort < $startPort || $endPort > self::MAX_PORTS) {
            $valid = false;
        }

        return $valid;
    }
}

Esempio d'uso:

$scanner = new Scanner('phptutorial.it', 80, 443);
$scanner->scan();
print_r($scanner->getResults());