Simple Mail Transfer Protocol einfach erklärt

Das Senden einer Mail per SMTP leicht verständlich gemacht mit Beispielen in Perl

Der Ablauf beim Senden einer Mail

Der erste Schritt besteht darin, eine Verbindung zum Host herzustellen. Dieser Host ist derjenige, welcher eine Mail entgegennimmt und lauscht dafür gewöhnlich auf dem Port 25. Diesen ersten Schritt und alle Weiteren gehen wir nun gemeinsam durch, dazu öffnen wir eine Konsole und geben ein: telnet example.com 25, also Hostname und Port mit Leerzeichen getrennt. Darauf antwortet der Mailserver z.B. so:

220 example.com ESMTP Postfix

Das nächste Kommando lautet wie folgt und wird wie alle weiteren Kommandos mit dem Drücken der Eingabetaste beendet:

HELO example.com

Worauf der Server antwortet:

250 example.com

Es folgt nun der Dialog zur Authentifizierung am Server, eingegeben wird hierzu:

AUTH LOGIN

Und der Server antwortet:

334 VXNlcm5hbWU6

Was soviel heißt wie Username: und dieser soll Base64 kodiert sein, also geben wir ein:

dXNlcg==

Daraufhin möchte der Server in Base64 ein Password:

UGFzc3dvcmQ6

Und auch dieses geben wir ein:

cGFzcw==

Sofern das erfolgreich war, zeigt sich der Server mit:

235 2.7.0 Authentication successful

Ansonsten bekommen wir eine Fehlermeldung. Weiter geht es, wir geben ein:

Mail From: <from@example.com>

Und mit einem schlichten:

250 2.1.0 Ok

bedankt sich der Server dafür. Weiter gehts:

Rcpt To: <to@example.com>

Als Antwort erhalten wir:

250 2.1.5 Ok

Der Server ist nun bereit zum Senden der eigentlichen Daten. Zum kenntlich machen, daß Daten gesendet werden sollen, genügt das Kommando:

Data

Was der Server beantwortet mit:

354 End data with <CR><LF>.<CR><LF>

Das heißt, daß der Server nun bereit ist, eine komplette Mail entgegenzunehmen, die im einfachsten Fall so aussieht:

From: from@@example.com
To: to@example.com
Subject: Hey!

text text text
.

Beachte also den Punkt ganz unten, er signalisiert dem Mailserver, daß keine weiteren Daten folgen. Mit einem:

250 2.0.0 Ok: queued as 44445215690C

quittiert dies der Server womit er auch die ID bekanngibt mit welcher die Mail in die Warteschleife kommt. Diese ID wird bis zum Empfänger durchgereicht. Verabschieden wir uns nun vom Server:

Quit

Und zum Dank für diese Höflichkeit antwortet der Server schlußendlich:

221 2.0.0 Bye

Sämtliche zum Protokoll gehörigen Kommandos implementiert das Perlmodul Net::SMTP, es gehört zur libnet und ist auch auf CPAN verfügbar. Wer etwas tiefer eindringen möchte, folge den weiteren Ausführungen dieses Artikels.

Eine Mail mit Perl via SMTP senden

Grundsätzlich gibt es zwei Möglichkeiten zum Senden einer Mail per SMTP mit Perl. Basis ist ein Socket, was per IO:Socket::INET recht einfach zu erstellen ist, dazu später mehr.

Die Mail wird samt Kommandos in das Socket geschoben

helo %host%
auth login
%user_base64%
%pass_base64%
mail from: <%from%>
rcpt to: <%to%>
data
from: %from_plain% <%from%>
to: %to_plain% <%to%>
subject: %subject%
%mesg%
.
Quit

Von daher kommt auch alles auf einmal vom Server als Antwort:

220 example.com ESMTP Postfix
250 example.com
334 VXNlcm5hbWU6
334 UGFzc3dvcmQ6
235 2.7.0 Authentication successful
250 2.1.0 Ok
250 2.1.5 Ok
354 End data with <CR><LF>.<CR><LF>
250 2.0.0 Ok: queued as 44445215690C
221 2.0.0 Bye

Hnweis: Jedes Zeilenende ist mit einem CRLF (Carriage Return + Line Feed) abzuschließen. Dieselben Zeilenenden finden sich auch in der Serverantwort. Ausgenommen von dieser protokollspezifischen Festlegung sind die eigenen Maildaten die zwischen DATA und <CR><LF>.<CR><LF> eingebaut sind.

Dieser Allesaufeinmal-Lösung haftet jedoch der Nachteil an, daß die Kommandos nicht im Einzelnen geprüft werden können ob sie erfolgreich waren. Beispiel einer Fehlermeldung wie sie erscheinen würde, wenn ein falscher Benutzername gesendet wurde:

535 5.7.8 Error: authentication failed: authentication failure

Die erste Ziffer kodiert die Fehlerklasse, diese sind:

# aus der libnet, Net::Cmd
sub CMD_INFO    {1}
sub CMD_OK      {2}
sub CMD_MORE    {3}
sub CMD_REJECT  {4}
sub CMD_ERROR   {5}
sub CMD_PENDING {0}

Ein weiterer Nachteil ergibt sich daraus, daß mit dem SMTP-Kommando Quit die Sitzung beendet und keine weitere Mail gesendet werden kann.

Die Kommandos werden einzeln abgesetzt

Betrachte untenstehenden Code:

use strict;
use warnings;
use IO::Socket;
my $io = IO::Socket::INET->new('example.com:25') or die $@

Infolgedessen liegt in $io das gewünschte Socket bzw. Handle zur Verbindung. Dieses Handle verhält sich wie ein PIPE in welches Daten hineingeschoben und wieder ausgelesen werden können. So steht bereits die erste Antwort vom Mailserver in der PIPE die wie folgt da ausgelesen wird:

$io->getline;
# Antwort
# 220 example.com ESMTP Postfix

Anmerkungen: Die Klasse IO::Socket::INET ist als Package in der Datei IO/Socket.pm definiert die mit use IO::Socket; einzubinden ist. Sämtliche IO-Klassen erben von IO::Handle, somit also auch die Methoden print() und getline(). Letztere liest aus dem Handle (Socket) solange bis die einem CRLF entsprechenden Bytes erscheinen. Untenstehende eigene Methode veranschaulicht die Funktionsweise:

sub _getline{
    my $self = shift;
    my $line = '';
    while( read($self, my $chr, 1) ){
        $line .= $chr;
        return $line if $line =~ /\r\n$/;
    }
}

Für etwas mehr Komfort, betrachte das SubModul SendMail::SMTP in der Source (ganz unten). Das prüft jedes an den Mailserver abgesetztes Kommando einzeln und verwendet hierzu Perl's bewährtes Exceptionmodell: Wenn es einen Fehler gibt, wird dessen gesamte Statusline als Exception geworfen. Als besonderes Feature liefert sendmail() die ID der gesendeten Mail zurück.

Während der Konstruktor eine SMTP Sitzung zum Mailserver aufgebaut hat, wird diese nach dem Senden einer Mail per sendmail() nicht beendet, so daß weitere Mails mit der einmal erstellten Instanz gesendet werden können. Sofern die Sitzung nicht explizit per bye() beendet wird, erfolgt dies im Destruktor der hier vorgestellten Klasse.

Modul SendMail::SMTP und weitere

Verschlüsselte Übertragung: Mit SendMail::SMTP können Mails auch per SSL versendet werden. Hierzu wäre einfach die Option ssl => 1 zu setzen bei der Objekterstellung. Es sei jedoch angemerkt, daß eine verschlüsselte Übertragung nicht immer sinnvoll ist, insbesondere wenn mehrere Mailserver im Spiel sind. Ebensowenig ist sichergestellt, daß ein Mailempfänger zum Abholen seiner Mails mit POP3 oder IMAP eine sichere Verbindung verwendet.

Eine Ende-zu-Ende-Sicherheit wird auch schon deswegen nicht erreicht, da alle Mailserver und Mailrelays die E-Mail im Klartext verarbeiten. Insofern ist es auch unsinnig, Mailformulare per HTTPS auszuliefern und abzusenden, weil HTTPS ja keine Garantie dafür ist, daß die Mail sicher übertragen wird.













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.