FTP Passiv Mode erklärt mit Perl

FTP (File Transfer Protocol) leicht gemacht

Ein paar Worte über die Funktionsweise des File Transfer Protocol. Mit diesem Verständnis ist es gar nicht so schwer, einen eigenen FTP-Client für spezielle Belange zu schreiben.

Aufbau der Verbindung

Betrachte untenstehenden Code:

use strict; use warnings; use IO::Socket; use constant CRLF => "\r\n"; my $cmd = IO::Socket::INET->new("FTP_HOST:FTP_PORT") or die $!;

Wobei der FTP_HOST ein IP-Adresse sein kann oder ein im DNS eingetragener Hostname. FTP_PORT ist in der Regel der Port 21. Sobald die Verbindung steht, wird eine Zeile aus dem Socket gelesen:

$cmd->getline(); und der Server antwortet bspw. so: 220 ProFTPD Server (ProFTPD) [***.146.238.137]

FTP-Kommandos und FTP-Response

Die Verbindung auf dem FTP_PORT ist das sogenannte Kommando-Socket. Nach jedem Kommando (erklärt in RFC 959) was in das Socket geschrieben wird, schreibt der FTP-Server die Antwort in dasselbe Socket und diese Antwort wird mit $cmd->getline(); ausgelesen. Auf diese Art und Weise wird nun der Login-Prozess abgewickelt, wobei jedes Kommando mit einem CRLF (Carriage Return, Line Feed) terminiert wird:

$cmd->print("USER $user".CRLF); print $cmd->getline; # 331 Password required for *** $cmd->print("PASS $pass".CRLF); print $cmd->getline; # 230 User *** logged in

Passiv Mode einschalten

Mit dem Einschalten des Passiv-Mode öffnet der FT-Server ein weiteres Socket und teilt dem Client mit, unter welcher Adresse das zu finden ist:

$cmd->print("PASV".CRLF); my $line = $cmd->getline; print $cmd->line; # 227 Entering Passive Mode (***,146,238,137,221,252). # wir parsen diese 6 Zahlenangaben $line =~ /(\d+),(\d+),(\d+),(\d+),(\d+),(\d+)/; # erhalten die IP sowie den Port für das Datensocket # und öffnen das Datensocket my $data = IO::Socket::INET->new( PeerAddr => unpack("N", pack "CCCC", $1,$2,$3,$4), PeerPort => unpack("n", pack "CC", $5,$6) ) or die $!;

Anmerkung zum Datensocket: Je nachdem ob Daten gesendet oder gelesen werden, jeder dieser Vorgänge ist mit einem $data->close; abzuschließen und danach mit $cmd->getline; die Antwort vom Server auszulesen. Wenn der Datentransfer erfolgreich war lautet die Antwort: 226 Transfer complete. Wenn also in einer FTP-Sitzung mehrere Datenübertragungen gemacht werden sollen, ist dazu jedesmal der Passiv-Mode wiedereinzuschalten womit der Server ein neues Socket bereitstellt.

Daten empfangen

Als Beispiel wählen wir hierzu das LIST-Kommando:

$cmd->print("LIST".CRLF); print $cmd->getline; # 150 Opening BINARY mode data connection for file list # hier wird das Datensocket gelesen while( my $line = $data->getline ){ print $line; } $data->close; # Datensocket schließen print $cmd->getline;

Antwort vom Server

150 Opening BINARY mode data connection for file list drwxr-xr-x 2 root root 47 Aug 9 2023 dev drwxr-xr-x 2 root root 4096 Aug 9 2023 error_docs drwxr-xr-x 2 root root 25 Aug 9 2023 etc drwxr-xr-x 2 root root 97 Aug 9 2023 sql drwxr-xr-x 5 root root 45 Aug 9 2023 usr 226 Transfer complete

Zum Empfangen von Dateien wäre mit IO::File ein lokales Dateihandle zu erstellen. Das FTP-Datenhandle $data wird dann ausgelesen und die Daten in das Dateihandle geschrieben.

Daten senden

# Neues Datensocket $cmd->print("PASV".CRLF); $line = $cmd->getline; $line =~ /(\d+),(\d+),(\d+),(\d+),(\d+),(\d+)/; # erhalten die IP sowie den Port für das Datensocket # und öffnen das Datensocket $data = IO::Socket::INET->new( PeerAddr => unpack("N", pack "CCCC", $1,$2,$3,$4), PeerPort => unpack("n", pack "CC", $5,$6) ) or die $!; # Verzeichnis wechseln $cmd->print("CWD /files/tmp".CRLF); print $cmd->getline; # Aktuelles Verzeichnis ausgeben $cmd->print("PWD".CRLF); print $cmd->getline; # Datei anlegen $cmd->print("STOR test.txt".CRLF); print $cmd->getline; # Daten in die Datei schreiben # sprich in das Datensocket $data->print("asdf\n"); # hier könnten auch Daten aus einer anderen Datei # gelesen und in das Datensocket geschrieben werden $data->close; print $cmd->getline;

Antwort vom Server

250 CWD command successful 257 "/files/tmp" is the current directory 150 Opening BINARY mode data connection for test.txt 226 Transfer complete

Verbindung quittieren

# Abmelden $cmd->print("QUIT".CRLF); print $cmd->getline; # Antwort vom Server 221 Goodbye.

Datenschutzerklärung: Diese Seite dient rein privaten Zwecken. Auf den für diese Domäne installierten Seiten werden grundsätzlich keine personenbezogenen Daten erhoben. Das Loggen der Zugriffe mit Ihrer Remote Adresse erfolgt beim Provider soweit das technisch erforderlich ist. s​os­@rolf­rost.de. Entity: d615bed813477e103c14cf91abfae3f0