IMAP-Server-Grundgerüst: TCP-Listener, Command-Parser, Session- Zustandsmaschine (Not Authenticated/Authenticated/Selected), Grundbefehle CAPABILITY/LOGIN/SELECT/FETCH/LOGOUT. - parser.go: Tag+Kommando+Argumente (Atome, zitierte Zeichenketten), keine IMAP-Literalsyntax (kleinste Lösung). - response.go: sanitizeResponseText entfernt eingebettete CR/LF vor jeder Antwortzeile — bekannten archivmail-Fehler (Header-/Zeilen-Injection durch Stringkonkatenation ohne CRLF-Prüfung) strukturell vermieden. - session.go/commands.go: strikte Zustandsprüfung je Kommando, verbotene Übergänge und fehlerhafte Zeilen liefern BAD/NO statt Verbindungsabbruch. maxCommandLineBytes begrenzt Pufferwachstum defensiv. - server.go: TCP-Accept-Schleife, eine Goroutine je Verbindung. - Authenticator/MailboxStore als schmale Schnittstellen — echte Benutzerverwaltungs-/Postfach-Anbindung ist Sache von IMP-01 u. a. Prüfungen (alle real durchgeführt, siehe mail/docs/ING-01-PRUEFPROTOKOLL.md): 1. Manuelle Session mit Pythons imaplib gegen den echten laufenden Server: alle Grundbefehle real beantwortet, ungültiges SELECT liefert real NO ohne Verbindungsabbruch. 2. TestSession_StateTransitionsAndForbiddenTransitions: alle drei Zustandsübergänge und deren verbotene Übergänge real über TCP geprüft. 3. TestServer_50ParallelSessionsNoLeak: 50 reale parallele Sessions, 0 Fehler. Kein Umbau: alle bestehenden Pakete unverändert. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HhgFcLS8tYMhDJpP74C6AQ
79 lines
4.2 KiB
Markdown
79 lines
4.2 KiB
Markdown
# ING-01 – Prüfprotokoll: IMAP-Server-Grundgerüst
|
||
|
||
Keine Vorbedingungen im Mail-Board (sofort startbar).
|
||
|
||
## Umsetzung
|
||
|
||
- `mail/internal/imap/state.go` — `State` (`NotAuthenticated`,
|
||
`Authenticated`, `Selected`, RFC 3501 §3).
|
||
- `mail/internal/imap/parser.go` — `parseCommandLine`/`tokenize`: Tag +
|
||
Kommando + Argumente (Atome und doppelt zitierte Zeichenketten), keine
|
||
IMAP-Literalsyntax (`{n}CRLF...` — bewusst nicht Bestandteil der
|
||
kleinsten Lösung, LOGIN/SELECT/FETCH kommen ohne Literale aus).
|
||
- `mail/internal/imap/response.go` — `sanitizeResponseText`: Bekannten
|
||
Fehler vermieden (archivmail: Header-/Zeilen-Injection durch
|
||
Stringkonkatenation ohne CRLF-Prüfung) — jede Antwortzeile entfernt
|
||
eingebettete CR/LF, bevor sie geschrieben wird, keine direkte
|
||
Interpolation von Nutzereingaben in eine Rohantwort.
|
||
- `mail/internal/imap/session.go`/`commands.go` — Session-
|
||
Zustandsmaschine mit `CAPABILITY`/`LOGIN`/`SELECT`/`FETCH`/`LOGOUT`,
|
||
strikte Zustandsprüfung je Kommando (Akzeptanzkriterium 1), fehlerhafte
|
||
Zeilen/unbekannte Kommandos/verbotene Zustandsübergänge liefern eine
|
||
`BAD`/`NO`-Antwort statt eines Verbindungsabbruchs (Akzeptanzkriterium
|
||
3). `maxCommandLineBytes` begrenzt die Puffergröße defensiv (Vorbild
|
||
Dovecot: defensive Fehlerbehandlung statt optimistischem Parsing).
|
||
- `mail/internal/imap/server.go` — `Server.Serve`: TCP-Accept-Schleife,
|
||
eine Goroutine je Verbindung.
|
||
- `Authenticator`/`MailboxStore` sind schmale Schnittstellen — echte
|
||
Benutzerverwaltungs-/Postfach-Anbindung ist Sache von IMP-01 u. a.
|
||
(„Nicht Bestandteil dieser Kachel"), dieses Paket kennt weder Core-IAM
|
||
noch `mail/internal/storage`.
|
||
- Kein Umbau: alle bestehenden Pakete unverändert — ING-01 fügt
|
||
ausschließlich das neue `mail/internal/imap`-Paket hinzu.
|
||
|
||
## Prüfungen
|
||
|
||
| # | Prüfung | Ergebnis |
|
||
|---|---|---|
|
||
| 1 | Manuelle Session mit Standard-IMAP-Client durchgespielt und protokolliert | **bestanden** – echte Session mit Pythons Standardbibliothek `imaplib` gegen den real laufenden Server auf 192.168.1.131 (Port 14300): CAPABILITY→OK, LOGIN→OK, SELECT INBOX→OK (`2` Nachrichten), FETCH 1:2 (FLAGS)→OK mit realen Flags, SELECT eines nicht existierenden Postfachs→NO OHNE Verbindungsabbruch, danach CAPABILITY erneut→OK, LOGOUT→BYE. Vollständiges Protokoll siehe unten |
|
||
| 2 | Automatisierter Test deckt alle drei Zustandsübergänge und deren verbotene Übergänge ab | **bestanden** – `TestSession_StateTransitionsAndForbiddenTransitions`: SELECT/FETCH in NotAuthenticated→BAD, LOGIN→Authenticated, erneutes LOGIN/FETCH in Authenticated→BAD, SELECT→Selected, FETCH in Selected→OK — alle real über echte TCP-Verbindung gegen den echten Server geprüft |
|
||
| 3 | Lasttest mit 50 parallelen Sessions ohne Ressourcenleck | **bestanden** – `TestServer_50ParallelSessionsNoLeak`: 50 reale, gleichzeitige TCP-Verbindungen, je vollständiger LOGIN→SELECT→FETCH→LOGOUT-Durchlauf, 0 Fehler |
|
||
|
||
### Manuelles Sitzungsprotokoll (Pflichtprüfung 1, real erzeugt)
|
||
|
||
```
|
||
CAPABILITY -> OK [b'IMAP4rev1']
|
||
LOGIN -> OK [b'LOGIN completed']
|
||
SELECT INBOX -> OK [b'2']
|
||
FETCH 1:2 (FLAGS) -> OK [b'1 (FLAGS (\\Seen))', b'2 (FLAGS ())']
|
||
SELECT NICHT_VORHANDEN (erwartet NO) -> NO [b'SELECT failed: no such mailbox']
|
||
CAPABILITY nach Fehler (Verbindung noch offen) -> OK [b'IMAP4rev1']
|
||
LOGOUT -> BYE [b'IMAP4rev1 Server logging out']
|
||
```
|
||
|
||
Testserver und Testskript wurden nach der Prüfung wieder entfernt
|
||
(Wegwerf-`cmd/imap-manual-test`, nicht Teil des Produktcodes).
|
||
|
||
Zusätzlich (AC2/AC3, ergänzend real geprüft):
|
||
`TestCommands_AllBaseCommandsAnswered` (alle fünf Grundbefehle real
|
||
beantwortet) und `TestSession_MalformedLineDoesNotDisconnect`
|
||
(syntaktisch fehlerhafte Zeile → `* BAD`, Verbindung bleibt real
|
||
funktionsfähig).
|
||
|
||
## Build/Test-Ergebnis (192.168.1.131)
|
||
|
||
```
|
||
go build ./... -> clean
|
||
go vet ./... -> clean
|
||
golangci-lint run ./... -> 0 issues
|
||
go test ./internal/imap/... -v -timeout 60s -> 5/5 bestanden
|
||
TEST_TENANT_DSN=... TEST_MANTICORE_URL=... go test ./... -p 1
|
||
-> alle 12 Pakete bestanden, keine Regression
|
||
```
|
||
|
||
## Gesamtergebnis
|
||
|
||
**Bestanden.** Alle drei Akzeptanzkriterien und alle drei Pflichtprüfungen
|
||
real erfüllt. Entsperrt IMP-01, ING-02, ING-05, ING-06, ING-07, ING-08,
|
||
ING-10, QA-07.
|