Diese Dokumentation beschreibt die Architektur von twmailer-pro, den Ablauf zwischen Client und Server sowie die wichtigsten Module, Datenstrukturen und Protokollkommandos.
TWMailer Pro ist ein einfaches textbasiertes Mail-System im Client-Server-Modell:
-
twmailer-client
Terminal-Programm, das Kommandos eingibt und Serverantworten ausgibt. -
twmailer-server
Multi-Thread-TCP-Server mit:- LDAP-Login
- Mail-Speicherung im Dateisystem
- IP-Blacklist nach Fehlversuchen
- Session-basierter Verarbeitung
Die Kommunikation erfolgt über ein zeilenbasiertes Textprotokoll über TCP.
Jede Nachricht endet mit einem \n.
Der Client:
- baut eine TCP-Verbindung zum Server auf
- zeigt ein interaktives Menü
- interpretiert Nutzereingaben
- konvertiert diese in Protokollkommandos
- sendet diese an den Server
- empfängt Antworten und zeigt sie an
- Erwartet Argumente:
./twmailer-client <IP> <PORT> - Erstellt einen TCP-Socket:
socket(AF_INET, SOCK_STREAM, 0) - Baut
sockaddr_in:sin_family = AF_INETsin_port = htons(port)inet_pton(AF_INET, ip, &addr.sin_addr)
- Verbindung via
connect() - Menü-Loop:
- Eingabe lesen
- passende doXYZ-Funktion aufrufen
- Sendet einen String vollständig über TCP.
- Nutzt eine Schleife, um Teil-Sends auszugleichen.
- Gibt
trueoderfalsezurück.
- Liest Byte für Byte, bis
\nerreicht wird. - Entfernt optional
\ram Ende. - Rückgabe:
trueoderfalse.
Ablauf:
-
Benutzer gibt Username + Passwort ein.
-
Client sendet:
LOGIN
<username>
<password> -
Server antwortet:
OK→ Login erfolgreichERR→ Login fehlgeschlagen
Body wird zeilenweise eingegeben, beendet durch eine einzelne Zeile mit ..
Request:
SEND
<receiver>
<subject>
<body-Zeile 1>
<body-Zeile 2>
...
.
Response:
OK
oder
ERR
Request:
LIST
Response:
<N>
<subject1>
<subject2>
...
Request:
READ
<number>
Antwort bei Erfolg:
OK
<sender>
<receiver>
<subject>
<body-Zeilen ...>
.
Bei Fehler:
ERR
Request:
DEL
<number>
Response:
OK
oder
ERR
Der Server besteht aus fünf zentralen Modulen:
-
Server
- verwaltet den TCP-Socket
- akzeptiert Verbindungen
- erzeugt Threads pro Client
-
ClientSession
- verarbeitet Kommandos einer TCP-Verbindung
- hält Authentifizierungsstatus
- ruft MailStore, LDAP und Blacklist auf
-
MailStore
- speichert Nachrichten als Dateien
- listet Nachrichten
- liest Nachrichten
- löscht Nachrichten
-
BlacklistManager
- sperrt IPs temporär nach zu vielen Fehl-Logins
- speichert Sperren persistent in Datei
-
LdapAuthenticator
- führt Echt-Login gegen Technikum-LDAP durch
- verwendet OpenLDAP-C-API
Der Server wird gestartet als:
./twmailer-server <PORT> <spoolDir>
Beispiel:
./twmailer-server 2025 /var/spool/twmailer
- Erstellt TCP-Socket (socket, bind, listen)
- Erzeugt:
MailStore storeBlacklistManager blacklistLdapAuthenticator authenticator
- Endlosschleife:
accept()auf eingehende Verbindungen- IP des Clients auslesen
- Blacklist prüfen
- neuen Thread mit
ClientSessionstarten
authenticated_(bool)username_(string)clientIp_(string)- Referenzen auf:
MailStoreBlacklistManagerLdapAuthenticator
- Zeile mit dem Kommando einlesen (
LOGIN,SEND,LIST,READ,DEL,QUIT). - Je nach Kommando entsprechende Handler-Funktion aufrufen.
- Bei
QUIToder Verbindungsfehler: Socket schließen und Thread beenden.
- Liest Username- und Passwort-Zeile.
- Prüft, ob IP bereits gesperrt ist (
BlacklistManager::isBlacklisted). - Ruft
LdapAuthenticator::authenticate(username, password)auf. - Falls erfolgreich:
- setzt
authenticated_ = true - speichert
username_ - ruft
BlacklistManager::recordSuccess - sendet
OK
- setzt
- Falls fehlgeschlagen:
- ruft
BlacklistManager::recordFailure - bei Überschreiten der Maximalversuche wird IP gesperrt
- sendet
ERR
- ruft
- Nur erlaubt, wenn
authenticated_ == true, sonstERR. - Liest:
- Empfänger
- Betreff
- Body-Zeilen bis zu einer Zeile mit
.
- Ruft
MailStore::storeMessage(sender, receiver, subject, body)auf. - Sendet:
OKbei ErfolgERRbei Fehler
-
Nur erlaubt bei authentifiziertem Benutzer.
-
Ruft
MailStore::listMessages(username_, subjects)auf. -
Sendet:
<N> <subject1> <subject2> ...wobei
Ndie Anzahl der Nachrichten ist.
-
Nur erlaubt bei authentifiziertem Benutzer.
-
Liest eine Nachrichten-Nummer.
-
Ruft
MailStore::readMessage(username_, num, sender, receiver, subject, body)auf. -
Bei Erfolg sendet:
OK <sender> <receiver> <subject> <body> . -
Bei Fehlschlag sendet:
ERR
-
Nur erlaubt bei authentifiziertem Benutzer.
-
Liest eine Nachrichten-Nummer.
-
Ruft
MailStore::deleteMessage(username_, num)auf. -
Antwort:
OKoder
ERR
Der MailStore verwaltet die persistente Ablage der Nachrichten auf dem Dateisystem.
Im Spool-Verzeichnis (spoolDir) wird für jeden Benutzer ein Unterordner angelegt:
<spoolDir>/
alice/
1.msg
2.msg
bob/
1.msg
Die .msg-Datei hat folgendes Format:
- Zeile: Sender
- Zeile: Empfänger
- Zeile: Betreff
- und folgende Zeilen: Body (Text der Nachricht)
-
storeMessage(sender, receiver, subject, body)- validiert Benutzernamen
- erzeugt Verzeichnis für den Empfänger (falls nötig)
- ermittelt nächste freie Nachrichtennummer
- schreibt eine neue
.msg-Datei
-
listMessages(username, subjects)- durchsucht das Benutzerverzeichnis
- liest zu jeder Nachricht den Betreff
- füllt einen Vektor mit allen Betreffzeilen
-
readMessage(username, num, sender, receiver, subject, body)- öffnet die Datei
<num>.msg - liest Sender, Empfänger, Betreff und Body
- gibt die Daten über Referenzen zurück
- öffnet die Datei
-
deleteMessage(username, num)- löscht die Datei
<num>.msg
- löscht die Datei
Alle schreibenden Zugriffe werden typischerweise durch einen Mutex geschützt, um Thread-Sicherheit zu gewährleisten.
Der BlacklistManager schützt den Server vor Brute-Force-Logins.
- Jeder Fehlversuch wird pro Kombination aus IP und Benutzer gezählt.
- Nach einer definierten Anzahl von Fehlversuchen (z.B. 3) wird die IP für eine gewisse Zeit (z.B. 60 Sekunden) gesperrt.
- Sperren werden in einer Datei gespeichert, sodass sie Server-Neustarts überleben.
-
isBlacklisted(ip)- prüft, ob die IP aktuell gesperrt ist.
-
recordFailure(ip, username)- erhöht den Zähler für
(ip, username). - sperrt die IP, wenn das Limit erreicht ist.
- erhöht den Zähler für
-
recordSuccess(ip, username)- setzt den Fehlversuchs-Zähler für
(ip, username)zurück.
- setzt den Fehlversuchs-Zähler für
-
load()/persist()- Laden/Speichern der Sperr-Liste aus/in einer Datei.
Der LdapAuthenticator kapselt die Authentifizierung gegen den LDAP-Server.
Typische Parameter:
- LDAP-Host: z.B.
ldap.technikum-wien.at - Port:
389 - Base-DN: z.B.
dc=technikum-wien,dc=at
- Initialisiert eine LDAP-Verbindung.
- Sucht den DN des Benutzers über einen Filter wie
(uid=<username>). - Führt einen einfachen Bind mit diesem DN und dem Passwort durch.
- Rückgabe:
truebei Erfolgfalsebei Fehler oder ungültigen Credentials
Intern werden Funktionen der OpenLDAP-C-API verwendet (z.B. ldap_initialize, ldap_search_ext_s, ldap_sasl_bind_s).
- Client verbindet sich mit dem Server.
- Benutzer meldet sich per
LOGINan. - Server prüft Blacklist und LDAP.
- Nach erfolgreichem Login kann der Benutzer:
- Nachrichten mit
SENDverschicken, - mit
LISTBetreffzeilen anzeigen, - mit
READNachrichten lesen, - mit
DELNachrichten löschen.
- Nachrichten mit
- Alle Nachrichten werden im Dateisystem im Spool-Verzeichnis abgelegt.
- Zu viele falsche Logins führen zu einer temporären Sperre der IP.
- Mit
QUITbeendet der Client die Session.
TWMailer Pro implementiert ein einfaches, aber sauberes Messaging-System mit:
- klarer Trennung von Client und Server
- zeilenbasiertem Textprotokoll
- LDAP-Authentifizierung
- dateibasierter Mail-Persistenz
- Thread-basiertem Session-Handling
- Sicherheitsmechanismus durch IP-Blacklist
Dank der modularen Struktur (Server, ClientSession, MailStore, BlacklistManager, LdapAuthenticator) ist das System gut erweiterbar und verständlich.