Utilizzo di FTP RAW con PHP – Esempio di funzionamento

scs ingegneria dei sistemi ftp raw

 

Esempio di utilizzo dei comandi di FTP RAW in php per l’invio di un file ad un server remoto.

 

Ho dovuto realizzare una procedura sofisticata di trasferimento file tra due server per un cliente, con una serie di vincoli imposti dal server di partenza, che, essendo condiviso, non mi permetteva di utilizzare strumenti avanzati tipo rsync, ssh ecc.

Per fare questo ho dovuto implementare un client FTP, e ho deciso di utilizzare, per praticità, il PHP, dato che dovevo utilizzare un server web per la gestione della procedura.

Inizialmente ho utilizzato, come da manuale, la connessione in modalità attiva, ma ricevevo una serie random di errori, tra cui il famigerato “Warning: ftp_get(): Opening BINARY mode data connection”, per cui ho deciso di utilizzare la modalità passiva per il trasferimento FTP.

Per chi non lo sapesse, la modalità attiva prevede che il server attivamente utilizzi la porta 20 come porta sorgente per mettere e prelevare i file su una porta alta ( > 1024), che viene aperta e gli viene indicata dal client. Per il cui il client apre una porta alta, e il server, dalla porta 20 ci scrive sopra. I firewall sono contenti, perche’ hanno una porta di provenienza fissa.

La modalità passiva prevede che il server comunica al client una porta alta ( > 1024 ) su cui e’ in ascolto per il trasferimento, e il client apre la connessione su quella porta, e vi trasmette il file. I firewall del client dovrebbero essere contenti perche’ non devono aprire nulla, mentre il problema e’ demandato ai server e relativi firewall.

La modalità passiva prevede quindi che il client apra una socket su una porta temporanea che il server gli comunica, e che gli invii un file.

Questa operazione non è molto agevole, e ho deciso, dopo parecchi tentativi, di scrivere queste note per spiegare come fare e lasciare un esempio funzionante.

I due punti significativi della procedura sono la creazione ricorsiva (anche se non ho utilizzato la ricorsione, ma una semplice lista J ) delle directory, e l’utilizzo in modo misto della modalità RAW dell’FTP, e la modalità normale.

Il protocollo FTP prevede la modalità RAW come sistema di scambio dati, ma tale modo è piuttosto complesso da usare, specialmente a linea comando, per cui i client utilizzano tutti una modalità avanzata, più semplice da usare. Il RAW non viene praticamente usato dalle persone, ma solo dai programmi di basso livello. Per avere il controllo dei pacchetti ho però dovuto usarlo, per cui vi mostro come ho fatto.

scs ingegneria dei sistemi ftp raw 1

Il codice e’ fatto da tre procedure, una di controllo, in cui si invocano le altre due. Nella prima parte si invoca la procedura di creazione del path, mentre nell’altra si invia il file. La prima procedura non e’ niente di speciale, ho preso il suggerimento in rete e vi ho apportato piccole modifiche.

La seconda procedura invece e’ particolare. Si apre la connessione con le tecniche classiche, ad alto livello, mentre il file lo si invia con i comandi RAW. Da notare in particolare come si riesce ad utilizzare in modo misto sia i comandi di alto livello, che quelli RAW di basso livello.

Il codice presentato sotto e’ per solo scopo didattico, e, in particolare, NON SONO PRESENTI I CONTROLLI DI USCITA DEI COMANDI, e i relativi log, che devono essere presenti in un ambiente di produzione.

Il programma da cui e’ stato estrapolato il codice era molto più vasto, per cui questo codice risulta essere abbastanza disomogeneo, ma abbiate pietà di un povero informatico… J

Buona lettura

 

TAG: php, ftp raw, ftp passive, ftp


function manda_fileRAW( $ftp_server, $ftp_username, $ftp_password, $ftp_remote_dir, $flocal, $fremote  )
{
       
        $ftp_connessione = ftp_connect($ftp_server);
        ftp_login($ftp_connessione, $ftp_username, $ftp_password);
        
		   // setto un timeout piu' alto per tranquillita'
           $timeout = ftp_get_option($ftp_connessione, FTP_TIMEOUT_SEC);  
           $r=ftp_set_option($ftp_connessione, FTP_TIMEOUT_SEC, 300);
           $timeout = ftp_get_option($ftp_connessione, FTP_TIMEOUT_SEC);
           echo "TIMEOUT = $timeout \n";
           
           
        if ( $ftp_remote_dir != "" &&  !ftp_chdir($ftp_connessione, $ftp_remote_dir))  
		{ftp_quit($ftp_connessione); return false; }
        
     
        ftp_raw($ftp_connessione, 'TYPE I');  // comandi raw come da manuale
        ftp_raw($ftp_connessione, 'STRU F');
        ftp_raw($ftp_connessione, 'MODE S');        
        
        $ret=ftp_raw($ftp_connessione, 'PASV');  // il server invia IP e porta in cui e' in ascolto 


        if (preg_match( '#^227.*\(([0-9]+,[0-9]+,[0-9]+,[0-9]+),([0-9]+),([0-9]+)\).*$#', 
		                $ret[0], 
					    $matches )) 
        {
            $controlIp = str_replace(',', '.', $matches[1]);
			// gli ultimi 2 numeri sono la porta .. ($n-1)*256 + $n
            $controlPort = intval($matches[2]) * 256 + intval($matches[3]);    
        }
        $file=fopen($flocal, 'r');  // apro il file locale in lettura 

        //apro la socket subito dopo il comando di passive
        $socket = fsockopen($controlIp, $controlPort);// e la socket remota via TCP

        // il server remoto crea il file con dimensione 0 e aspetta
        ftp_raw($ftp_connessione, 'STOR '.$fremote);  
                
        $s = '';
        while (!feof($file))            // il for di invio pacchetti da 4096 ... 
        {
            $s=fread($file, 4096);           
			// qua controllo di corretta scrittura .. 
			// se non ho errori iil TCP mi assicura che il pacchetto e' arrivato
            $n=fwrite($socket, $s, 4096);    
        }
               
        fclose($socket);  // effettuare controlli di ritorno valore
        fclose($file);
        
        ftp_quit($ftp_connessione);
        return true;            
}
   
   
   
function ftp_mksubdirs($ftpcon, $ftpbasedir, $f)  // ritorna false o il nome del file da inviare
{
        $parts = explode('/',$f);   // path al file  
        $file=array_pop($parts);          // elimino l'ultimo elemento dell'array che e' il file

        $descr=$ftpbasedir;
        ftp_chdir($ftpcon, $ftpbasedir);
        foreach($parts as $part)
        {
            if( $part == "." ) { continue; }
			// tenta un chdir sulla directory per vedere se esiste gia'....
            if(  !@ftp_chdir($ftpcon, $part) ) 
            {
                ftp_mkdir($ftpcon, $part);
                $descr=$descr."/$part";
                
                ftp_chdir($ftpcon, $part);
            }
        }
        return $file;
}
        
    
// fremote e' un path ad un file. Il path remoto completo al file e' 
// dato in questo esempio da $ftp_remote_dir.$fremote
function manda_file( $fremote ) 
{
        $ftp_server="xxx.yyy.zzz.hhh"; // indirizzo ip del sever remoto
        $ftp_username="**********";
        $ftp_password="*******";
        $ftp_remote_dir="/aaa/bbb/";
        $file_TMP="pippo";  // file da inviare

        // crea directory
        $ftp_connessione = ftp_connect($ftp_server);                                  
        $login = ftp_login($ftp_connessione, $ftp_username, $ftp_password);   
                              
           ftp_chdir($ftp_connessione, $ftp_remote_dir); // chdir remota iniziale        
           ftp_mksubdirs($ftp_connessione, $ftp_remote_dir, $fremote ); 
           
		// chiude la connessione dopo la creazione della dir.. 
		// la riapre dopo per l'invio del file
        ftp_quit($ftp_connessione);  
 
 
        // invia file 
        $path_parts = pathinfo($fremote);
        $dirc=$ftp_remote_dir.$path_parts['dirname'];
        $fremote=$path_parts['basename'];
 
 
        manda_fileRAW( $ftp_server, $ftp_username, $ftp_password, 
		               $dirc, $file_TMP, $fremote  );
        return true;
            
}

 

© 2018 SCS di Alberto Montanari & C. s.a.s. | Pricacy Policy - Cookie Policy