Spec couche 2 — air-mail (SMTP + POP3 + IMAP4, patron sans-IO)
Spécification technique — Version 0.1 (proposition). Couche 2 « Frameworks système ».
Cadrage.
air-mail= client de messagerie : SMTP (soumission, RFC 5321/ 6409), POP3 (RFC 1939), IMAP4rev2 (RFC 9051) — trois modules internes d’un crate unique (commeair-httpfédère h1/h2/h3 : codec-ligne, SASL et TLS partagés). Maison, pur Rust, zéro-C, zérounsafe. Serveur mail hors périmètre (client only). Composeair-tls+air-network.
Position et méthode
Trois protocoles filaires texte, ligne par ligne (CRLF) qui partagent : framing de
ligne (+ littéraux IMAP {n}), STARTTLS / TLS implicite, SASL AUTH. Un crate,
trois modules, un codec-ligne + SASL + TLS communs.
Décision — crate unique, modules internes. SMTP (envoi) et POP3/IMAP (réception) diffèrent par le rôle mais partagent framing/TLS/SASL → factorisés une fois (anti-duplication, règle 80 %). Surface publique par protocole ; cœur partagé.
Périmètre de sécurité — moderne, TLS d’abord
| Protocole | Retenu | Ports |
|---|---|---|
| SMTP | soumission RFC 6409, SMTPUTF8, DSN, PIPELINING ; TLS implicite (465) préféré, STARTTLS (587) sinon | 465/587 |
| POP3 | RFC 1939 + STARTTLS ; TLS implicite (995) préféré | 995/110 |
| IMAP4rev2 | RFC 9051 + STARTTLS ; TLS implicite (993) préféré ; IDLE, extensions | 993/143 |
| SASL | SCRAM-SHA-256(-PLUS), PLAIN/LOGIN uniquement sous TLS, OAUTHBEARER/XOAUTH2 | — |
- TLS obligatoire pour l’AUTH : jamais d’AUTH en clair ; anti STARTTLS-stripping
(préférer le port TLS implicite ; rejeter le downgrade). Certif. vérifié (
air-tls). - Omis : AUTH en clair sans TLS ; POP3
APOP(MD5) ; IMAP legacyLOGINsans TLS ; SMTP relay ouvert (client only).
Anatomie : les 9 composants (par module, cœur partagé)
| # | Composant | SMTP | POP3 | IMAP |
|---|---|---|---|---|
| 1 | Framer | LineFramer (CRLF) — partagé ; IMAP ajoute littéraux {n} | ||
| 2 | Codec | SmtpCodec | Pop3Codec | ImapCodec (commandes taguées, réponses) |
| 3 | StateMachine | greeting→EHLO→STARTTLS→AUTH→MAIL/RCPT/DATA | greeting→STLS→AUTH→TRANS→UPDATE | greeting→STARTTLS→AUTH→SELECT→… |
| 4 | Handshaker | STARTTLS/TLS via air-tls + SASL (partagé) | ||
| 5 | Flow Controller | // NO | // NO | // NO |
| 6 | Multiplexer | // NO | // NO | tags de commande (léger : réponses corrélées) |
| 7 | Timer Manager | timeouts | timeouts | IDLE (push), timeouts |
| 8 | Session Context | capabilities (EHLO) | état TRANS | capabilities + état de mailbox (SELECT) |
| 9 | Extension hooks | SMTPUTF8/DSN/SIZE | — | extensions IMAP (CONDSTORE, MOVE…) |
Inventaire — ~20 objets
Surface (~12) : SmtpClient, Pop3Client, ImapClient, MailAddress (validé),
OutgoingMessage/Envelope, Mailbox/MessageSummary (IMAP), SaslCredential
(SCRAM/PLAIN/OAuth), Capabilities, FetchItem (IMAP), MailError, MailAuth,
ConnectionSecurity (implicit-tls/starttls/plain-refused).
Cœur partagé (~5) : LineFramer, SaslCodec (SCRAM/PLAIN/OAUTHBEARER), HeaderCodec
(RFC 5322 en-têtes), ImecCodec (encodages MIME/UTF-8), TlsUpgrade (STARTTLS).
Cœurs par module (~3) : SmtpStateMachine, Pop3StateMachine, ImapStateMachine.
#![allow(unused)]
fn main() {
impl ImapClient {
pub async fn connect(endpoint: AirEndpoint, security: ConnectionSecurity,
tls: Arc<TlsClientConfig>) -> Result<Self, MailError>;
pub async fn authenticate(&mut self, cred: SaslCredential) -> Result<(), MailError>;
pub async fn select(&mut self, mailbox: &str) -> Result<Mailbox, MailError>;
pub async fn fetch(&mut self, set: SequenceSet, items: &[FetchItem]) -> Result<Vec<MessageSummary>, MailError>;
pub async fn idle(&mut self) -> Result<IdleHandle, MailError>; // push (RFC 2177)
}
}
Dépendances
air-network (L2, connexion), air-tls (L2, STARTTLS/implicite), air-crypto (L1 —
HMAC/SHA-256 pour SCRAM ; PBKDF2 = additif ci-dessous), air-runtime/air-base-core
(L1). Zéro-C, zéro tierce.
Additif
air-crypto— INSTRUIT (ADR-082).SCRAM-SHA-256dérive via PBKDF2-HMAC-SHA256 → additifpbkdf2(RustCrypto), pur Rust zéro-C, KAT RFC 6070. Requis si SCRAM ; sinonOAUTHBEARER/XOAUTH2(token, sans PBKDF2) couvre les fournisseurs modernes.
Sécurité & tests
- TLS avant AUTH non négociable ; anti STARTTLS-stripping ; SASL moderne ; adresses/ en-têtes RFC 5322 parsés bornés (anti-injection d’en-têtes / CRLF injection).
- Tests :
LineFramer/codecs fuzzés (littéraux IMAP, réponses hostiles) ; StateMachine property (phases + refus AUTH-avant-TLS) ; interop Postfix/Dovecot en loopback TLS.
Travail à reprendre
- Additif
air-cryptoPBKDF2 — instruit (ADR-082) (si SCRAM) ; MIME complet (parsing/multipart) ; IDLE/push ; envoi (composition MIME) ; ABI C différée.
Licence : MPL 2.0. Statut : spec air-mail (L2) v0.1 — SMTP/POP3/IMAP4 client, crate
unique modules internes, sans-IO ; TLS d’abord, SASL moderne.