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
39 lines
1.2 KiB
Go
39 lines
1.2 KiB
Go
package imap
|
|
|
|
import (
|
|
"bufio"
|
|
"strings"
|
|
)
|
|
|
|
// sanitizeResponseText entfernt eingebettete CR/LF aus text, BEVOR er in
|
|
// eine Antwortzeile eingebettet wird (Bekannter Fehler vermeiden:
|
|
// archivmail erlaubte Header-/Zeilen-Injection durch Stringkonkatenation
|
|
// ohne CRLF-Prüfung — jede Antwortzeile dieses Pakets läuft durch diese
|
|
// Funktion, niemals direkte Interpolation von Nutzereingaben in eine
|
|
// Rohantwort).
|
|
func sanitizeResponseText(text string) string {
|
|
text = strings.ReplaceAll(text, "\r", "")
|
|
text = strings.ReplaceAll(text, "\n", "")
|
|
return text
|
|
}
|
|
|
|
// writeUntagged schreibt eine Server-Antwort ohne Tag ("* ...").
|
|
func writeUntagged(w *bufio.Writer, text string) error {
|
|
_, err := w.WriteString("* " + sanitizeResponseText(text) + "\r\n")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return w.Flush()
|
|
}
|
|
|
|
// writeTagged schreibt eine getaggte Server-Antwort ("<tag> OK/NO/BAD ...").
|
|
// tag wird ebenfalls saniert — ein Tag mit eingebettetem CRLF ist genauso
|
|
// eine Injektionsgefahr wie der Antworttext.
|
|
func writeTagged(w *bufio.Writer, tag, status, text string) error {
|
|
_, err := w.WriteString(sanitizeResponseText(tag) + " " + status + " " + sanitizeResponseText(text) + "\r\n")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return w.Flush()
|
|
}
|