From ee947670bd61f58c734e0a8787a0e133309fdc24 Mon Sep 17 00:00:00 2001 From: Mauro D Date: Sun, 4 Dec 2022 18:15:15 +0000 Subject: [PATCH] Response parsing --- src/lib.rs | 70 ++- src/request/mod.rs | 971 ++++++++++++++++++++-------------------- src/request/parser.rs | 96 ++-- src/request/receiver.rs | 193 ++++++++ src/response/mod.rs | 191 ++++++++ src/response/parser.rs | 589 ++++++++++++++++++++++++ 6 files changed, 1592 insertions(+), 518 deletions(-) create mode 100644 src/request/receiver.rs create mode 100644 src/response/parser.rs diff --git a/src/lib.rs b/src/lib.rs index 0db3ec9..4932ae3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -120,7 +120,7 @@ pub const NOTIFY_SUCCESS: u8 = 0x01; pub const NOTIFY_FAILURE: u8 = 0x02; pub const NOTIFY_DELAY: u8 = 0x04; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Mechanism { _9798MDsaSha1, _9798MEcdsaSha1, @@ -168,15 +168,79 @@ pub enum Mechanism { Unknown, } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum Capability { + EightBitMime, + Atrn, + Auth { + mechanisms: Vec, + }, + BinaryMime, + Burl, + Checkpoint, + Chunking, + Conneg, + Conperm, + DeliverBy { + min: u64, + }, + Dsn, + EnhancedStatusCodes, + Etrn, + Expn, + FutureRelease { + max_interval: u64, + max_datetime: u64, + }, + Help, + MtPriority { + priority: MtPriority, + }, + Mtrk, + NoSoliciting { + keywords: Option, + }, + Onex, + Pipelining, + RequireTls, + Rrvs, + Size { + size: usize, + }, + SmtpUtf8, + StartTls, + Verb, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum MtPriority { + Mixer, + Stanag4406, + Nsep, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct EhloResponse { + pub hostname: String, + pub capabilities: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Response { + pub code: u16, + pub esc: [u8; 3], + pub message: String, +} + #[derive(Debug, Clone, PartialEq, Eq)] pub enum Error { - NeedsMoreData, + NeedsMoreData { bytes_left: usize }, UnknownCommand, InvalidAddress, SyntaxError { syntax: &'static str }, InvalidParameter { param: &'static str }, UnsupportedParameter { param: String }, - UnexpectedChar { char: u8 }, + InvalidResponse { response: Response }, } pub(crate) const LF: u8 = b'\n'; diff --git a/src/request/mod.rs b/src/request/mod.rs index d568f5f..aaedca1 100644 --- a/src/request/mod.rs +++ b/src/request/mod.rs @@ -1,212 +1,213 @@ pub mod parser; +pub mod receiver; // SMTP commands pub(crate) const EHLO: u64 = - (b'e' as u64) | (b'h' as u64) << 8 | (b'l' as u64) << 16 | (b'o' as u64) << 24; + (b'E' as u64) | (b'H' as u64) << 8 | (b'L' as u64) << 16 | (b'O' as u64) << 24; pub(crate) const HELO: u64 = - (b'h' as u64) | (b'e' as u64) << 8 | (b'l' as u64) << 16 | (b'o' as u64) << 24; + (b'H' as u64) | (b'E' as u64) << 8 | (b'L' as u64) << 16 | (b'O' as u64) << 24; pub(crate) const LHLO: u64 = - (b'l' as u64) | (b'h' as u64) << 8 | (b'l' as u64) << 16 | (b'o' as u64) << 24; + (b'L' as u64) | (b'H' as u64) << 8 | (b'L' as u64) << 16 | (b'O' as u64) << 24; pub(crate) const MAIL: u64 = - (b'm' as u64) | (b'a' as u64) << 8 | (b'i' as u64) << 16 | (b'l' as u64) << 24; + (b'M' as u64) | (b'A' as u64) << 8 | (b'I' as u64) << 16 | (b'L' as u64) << 24; pub(crate) const RCPT: u64 = - (b'r' as u64) | (b'c' as u64) << 8 | (b'p' as u64) << 16 | (b't' as u64) << 24; + (b'R' as u64) | (b'C' as u64) << 8 | (b'P' as u64) << 16 | (b'T' as u64) << 24; pub(crate) const DATA: u64 = - (b'd' as u64) | (b'a' as u64) << 8 | (b't' as u64) << 16 | (b'a' as u64) << 24; + (b'D' as u64) | (b'A' as u64) << 8 | (b'T' as u64) << 16 | (b'A' as u64) << 24; pub(crate) const BDAT: u64 = - (b'b' as u64) | (b'd' as u64) << 8 | (b'a' as u64) << 16 | (b't' as u64) << 24; + (b'B' as u64) | (b'D' as u64) << 8 | (b'A' as u64) << 16 | (b'T' as u64) << 24; pub(crate) const RSET: u64 = - (b'r' as u64) | (b's' as u64) << 8 | (b'e' as u64) << 16 | (b't' as u64) << 24; + (b'R' as u64) | (b'S' as u64) << 8 | (b'E' as u64) << 16 | (b'T' as u64) << 24; pub(crate) const VRFY: u64 = - (b'v' as u64) | (b'r' as u64) << 8 | (b'f' as u64) << 16 | (b'y' as u64) << 24; + (b'V' as u64) | (b'R' as u64) << 8 | (b'F' as u64) << 16 | (b'Y' as u64) << 24; pub(crate) const EXPN: u64 = - (b'e' as u64) | (b'x' as u64) << 8 | (b'p' as u64) << 16 | (b'n' as u64) << 24; + (b'E' as u64) | (b'X' as u64) << 8 | (b'P' as u64) << 16 | (b'N' as u64) << 24; pub(crate) const HELP: u64 = - (b'h' as u64) | (b'e' as u64) << 8 | (b'l' as u64) << 16 | (b'p' as u64) << 24; + (b'H' as u64) | (b'E' as u64) << 8 | (b'L' as u64) << 16 | (b'P' as u64) << 24; pub(crate) const NOOP: u64 = - (b'n' as u64) | (b'o' as u64) << 8 | (b'o' as u64) << 16 | (b'p' as u64) << 24; + (b'N' as u64) | (b'O' as u64) << 8 | (b'O' as u64) << 16 | (b'P' as u64) << 24; pub(crate) const QUIT: u64 = - (b'q' as u64) | (b'u' as u64) << 8 | (b'i' as u64) << 16 | (b't' as u64) << 24; + (b'Q' as u64) | (b'U' as u64) << 8 | (b'I' as u64) << 16 | (b'T' as u64) << 24; pub(crate) const ETRN: u64 = - (b'e' as u64) | (b't' as u64) << 8 | (b'r' as u64) << 16 | (b'n' as u64) << 24; + (b'E' as u64) | (b'T' as u64) << 8 | (b'R' as u64) << 16 | (b'N' as u64) << 24; pub(crate) const ATRN: u64 = - (b'a' as u64) | (b't' as u64) << 8 | (b'r' as u64) << 16 | (b'n' as u64) << 24; + (b'A' as u64) | (b'T' as u64) << 8 | (b'R' as u64) << 16 | (b'N' as u64) << 24; pub(crate) const AUTH: u64 = - (b'a' as u64) | (b'u' as u64) << 8 | (b't' as u64) << 16 | (b'h' as u64) << 24; + (b'A' as u64) | (b'U' as u64) << 8 | (b'T' as u64) << 16 | (b'H' as u64) << 24; pub(crate) const BURL: u64 = - (b'b' as u64) | (b'u' as u64) << 8 | (b'r' as u64) << 16 | (b'l' as u64) << 24; -pub(crate) const STARTTLS: u64 = (b's' as u64) - | (b't' as u64) << 8 - | (b'a' as u64) << 16 - | (b'r' as u64) << 24 - | (b't' as u64) << 32 - | (b't' as u64) << 40 - | (b'l' as u64) << 48 - | (b's' as u64) << 56; + (b'B' as u64) | (b'U' as u64) << 8 | (b'R' as u64) << 16 | (b'L' as u64) << 24; +pub(crate) const STARTTLS: u64 = (b'S' as u64) + | (b'T' as u64) << 8 + | (b'A' as u64) << 16 + | (b'R' as u64) << 24 + | (b'T' as u64) << 32 + | (b'T' as u64) << 40 + | (b'L' as u64) << 48 + | (b'S' as u64) << 56; // Arguments pub(crate) const FROM: u64 = - (b'f' as u64) | (b'r' as u64) << 8 | (b'o' as u64) << 16 | (b'm' as u64) << 24; -pub(crate) const TO: u64 = (b't' as u64) | (b'o' as u64) << 8; + (b'F' as u64) | (b'R' as u64) << 8 | (b'O' as u64) << 16 | (b'M' as u64) << 24; +pub(crate) const TO: u64 = (b'T' as u64) | (b'O' as u64) << 8; pub(crate) const LAST: u64 = - (b'l' as u64) | (b'a' as u64) << 8 | (b's' as u64) << 16 | (b't' as u64) << 24; + (b'L' as u64) | (b'A' as u64) << 8 | (b'S' as u64) << 16 | (b'T' as u64) << 24; // Parameters pub(crate) const BODY: u128 = - (b'b' as u128) | (b'o' as u128) << 8 | (b'd' as u128) << 16 | (b'y' as u128) << 24; + (b'B' as u128) | (b'O' as u128) << 8 | (b'D' as u128) << 16 | (b'Y' as u128) << 24; pub(crate) const SEVENBIT: u128 = - (b'7' as u128) | (b'b' as u128) << 8 | (b'i' as u128) << 16 | (b't' as u128) << 24; + (b'7' as u128) | (b'B' as u128) << 8 | (b'I' as u128) << 16 | (b'T' as u128) << 24; pub(crate) const EIGHBITMIME: u128 = (b'8' as u128) - | (b'b' as u128) << 8 - | (b'i' as u128) << 16 - | (b't' as u128) << 24 - | (b'm' as u128) << 32 - | (b'i' as u128) << 40 - | (b'm' as u128) << 48 - | (b'e' as u128) << 56; -pub(crate) const BINARYMIME: u128 = (b'b' as u128) - | (b'i' as u128) << 8 - | (b'n' as u128) << 16 - | (b'a' as u128) << 24 - | (b'r' as u128) << 32 - | (b'y' as u128) << 40 - | (b'm' as u128) << 48 - | (b'i' as u128) << 56 - | (b'm' as u128) << 64 - | (b'e' as u128) << 72; + | (b'B' as u128) << 8 + | (b'I' as u128) << 16 + | (b'T' as u128) << 24 + | (b'M' as u128) << 32 + | (b'I' as u128) << 40 + | (b'M' as u128) << 48 + | (b'E' as u128) << 56; +pub(crate) const BINARYMIME: u128 = (b'B' as u128) + | (b'I' as u128) << 8 + | (b'N' as u128) << 16 + | (b'A' as u128) << 24 + | (b'R' as u128) << 32 + | (b'Y' as u128) << 40 + | (b'M' as u128) << 48 + | (b'I' as u128) << 56 + | (b'M' as u128) << 64 + | (b'E' as u128) << 72; pub(crate) const SIZE: u128 = - (b's' as u128) | (b'i' as u128) << 8 | (b'z' as u128) << 16 | (b'e' as u128) << 24; -pub(crate) const TRANSID: u128 = (b't' as u128) - | (b'r' as u128) << 8 - | (b'a' as u128) << 16 - | (b'n' as u128) << 24 - | (b's' as u128) << 32 - | (b'i' as u128) << 40 - | (b'd' as u128) << 48; -pub(crate) const BY: u128 = (b'b' as u128) | (b'y' as u128) << 8; + (b'S' as u128) | (b'I' as u128) << 8 | (b'Z' as u128) << 16 | (b'E' as u128) << 24; +pub(crate) const TRANSID: u128 = (b'T' as u128) + | (b'R' as u128) << 8 + | (b'A' as u128) << 16 + | (b'N' as u128) << 24 + | (b'S' as u128) << 32 + | (b'I' as u128) << 40 + | (b'D' as u128) << 48; +pub(crate) const BY: u128 = (b'B' as u128) | (b'Y' as u128) << 8; -pub(crate) const N: u64 = b'n' as u64; -pub(crate) const NT: u64 = (b'n' as u64) | (b't' as u64) << 8; -pub(crate) const C: u64 = b'c' as u64; -pub(crate) const R: u64 = b'r' as u64; -pub(crate) const RT: u64 = (b'r' as u64) | (b't' as u64) << 8; +pub(crate) const N: u64 = b'N' as u64; +pub(crate) const NT: u64 = (b'N' as u64) | (b'T' as u64) << 8; +pub(crate) const C: u64 = b'C' as u64; +pub(crate) const R: u64 = b'R' as u64; +pub(crate) const RT: u64 = (b'R' as u64) | (b'T' as u64) << 8; -pub(crate) const NOTIFY: u128 = (b'n' as u128) - | (b'o' as u128) << 8 - | (b't' as u128) << 16 - | (b'i' as u128) << 24 - | (b'f' as u128) << 32 - | (b'y' as u128) << 40; -pub(crate) const ORCPT: u128 = (b'o' as u128) - | (b'r' as u128) << 8 - | (b'c' as u128) << 16 - | (b'p' as u128) << 24 - | (b't' as u128) << 32; -pub(crate) const RET: u128 = (b'r' as u128) | (b'e' as u128) << 8 | (b't' as u128) << 16; -pub(crate) const ENVID: u128 = (b'e' as u128) - | (b'n' as u128) << 8 - | (b'v' as u128) << 16 - | (b'i' as u128) << 24 - | (b'd' as u128) << 32; -pub(crate) const NEVER: u128 = (b'n' as u128) - | (b'e' as u128) << 8 - | (b'v' as u128) << 16 - | (b'e' as u128) << 24 - | (b'r' as u128) << 32; -pub(crate) const SUCCESS: u128 = (b's' as u128) - | (b'u' as u128) << 8 - | (b'c' as u128) << 16 - | (b'c' as u128) << 24 - | (b'e' as u128) << 32 - | (b's' as u128) << 40 - | (b's' as u128) << 48; -pub(crate) const FAILURE: u128 = (b'f' as u128) - | (b'a' as u128) << 8 - | (b'i' as u128) << 16 - | (b'l' as u128) << 24 - | (b'u' as u128) << 32 - | (b'r' as u128) << 40 - | (b'e' as u128) << 48; -pub(crate) const DELAY: u128 = (b'd' as u128) - | (b'e' as u128) << 8 - | (b'l' as u128) << 16 - | (b'a' as u128) << 24 - | (b'y' as u128) << 32; +pub(crate) const NOTIFY: u128 = (b'N' as u128) + | (b'O' as u128) << 8 + | (b'T' as u128) << 16 + | (b'I' as u128) << 24 + | (b'F' as u128) << 32 + | (b'Y' as u128) << 40; +pub(crate) const ORCPT: u128 = (b'O' as u128) + | (b'R' as u128) << 8 + | (b'C' as u128) << 16 + | (b'P' as u128) << 24 + | (b'T' as u128) << 32; +pub(crate) const RET: u128 = (b'R' as u128) | (b'E' as u128) << 8 | (b'T' as u128) << 16; +pub(crate) const ENVID: u128 = (b'E' as u128) + | (b'N' as u128) << 8 + | (b'V' as u128) << 16 + | (b'I' as u128) << 24 + | (b'D' as u128) << 32; +pub(crate) const NEVER: u128 = (b'N' as u128) + | (b'E' as u128) << 8 + | (b'V' as u128) << 16 + | (b'E' as u128) << 24 + | (b'R' as u128) << 32; +pub(crate) const SUCCESS: u128 = (b'S' as u128) + | (b'U' as u128) << 8 + | (b'C' as u128) << 16 + | (b'C' as u128) << 24 + | (b'E' as u128) << 32 + | (b'S' as u128) << 40 + | (b'S' as u128) << 48; +pub(crate) const FAILURE: u128 = (b'F' as u128) + | (b'A' as u128) << 8 + | (b'I' as u128) << 16 + | (b'L' as u128) << 24 + | (b'U' as u128) << 32 + | (b'R' as u128) << 40 + | (b'E' as u128) << 48; +pub(crate) const DELAY: u128 = (b'D' as u128) + | (b'E' as u128) << 8 + | (b'L' as u128) << 16 + | (b'A' as u128) << 24 + | (b'Y' as u128) << 32; pub(crate) const FULL: u64 = - (b'f' as u64) | (b'u' as u64) << 8 | (b'l' as u64) << 16 | (b'l' as u64) << 24; + (b'F' as u64) | (b'U' as u64) << 8 | (b'L' as u64) << 16 | (b'L' as u64) << 24; pub(crate) const HDRS: u64 = - (b'h' as u64) | (b'd' as u64) << 8 | (b'r' as u64) << 16 | (b's' as u64) << 24; -pub(crate) const SOLICIT: u128 = (b's' as u128) - | (b'o' as u128) << 8 - | (b'l' as u128) << 16 - | (b'i' as u128) << 24 - | (b'c' as u128) << 32 - | (b'i' as u128) << 40 - | (b't' as u128) << 48; + (b'H' as u64) | (b'D' as u64) << 8 | (b'R' as u64) << 16 | (b'S' as u64) << 24; +pub(crate) const SOLICIT: u128 = (b'S' as u128) + | (b'O' as u128) << 8 + | (b'L' as u128) << 16 + | (b'I' as u128) << 24 + | (b'C' as u128) << 32 + | (b'I' as u128) << 40 + | (b'T' as u128) << 48; pub(crate) const MTRK: u128 = - (b'm' as u128) | (b't' as u128) << 8 | (b'r' as u128) << 16 | (b'k' as u128) << 24; + (b'M' as u128) | (b'T' as u128) << 8 | (b'R' as u128) << 16 | (b'K' as u128) << 24; pub(crate) const AUTH_: u128 = - (b'a' as u128) | (b'u' as u128) << 8 | (b't' as u128) << 16 | (b'h' as u128) << 24; -pub(crate) const HOLDFOR: u128 = (b'h' as u128) - | (b'o' as u128) << 8 - | (b'l' as u128) << 16 - | (b'd' as u128) << 24 - | (b'f' as u128) << 32 - | (b'o' as u128) << 40 - | (b'r' as u128) << 48; -pub(crate) const HOLDUNTIL: u128 = (b'h' as u128) - | (b'o' as u128) << 8 - | (b'l' as u128) << 16 - | (b'd' as u128) << 24 - | (b'u' as u128) << 32 - | (b'n' as u128) << 40 - | (b't' as u128) << 48 - | (b'i' as u128) << 56 - | (b'l' as u128) << 64; -pub(crate) const SMTPUTF8: u128 = (b's' as u128) - | (b'm' as u128) << 8 - | (b't' as u128) << 16 - | (b'p' as u128) << 24 - | (b'u' as u128) << 32 - | (b't' as u128) << 40 - | (b'f' as u128) << 48 + (b'A' as u128) | (b'U' as u128) << 8 | (b'T' as u128) << 16 | (b'H' as u128) << 24; +pub(crate) const HOLDFOR: u128 = (b'H' as u128) + | (b'O' as u128) << 8 + | (b'L' as u128) << 16 + | (b'D' as u128) << 24 + | (b'F' as u128) << 32 + | (b'O' as u128) << 40 + | (b'R' as u128) << 48; +pub(crate) const HOLDUNTIL: u128 = (b'H' as u128) + | (b'O' as u128) << 8 + | (b'L' as u128) << 16 + | (b'D' as u128) << 24 + | (b'U' as u128) << 32 + | (b'N' as u128) << 40 + | (b'T' as u128) << 48 + | (b'I' as u128) << 56 + | (b'L' as u128) << 64; +pub(crate) const SMTPUTF8: u128 = (b'S' as u128) + | (b'M' as u128) << 8 + | (b'T' as u128) << 16 + | (b'P' as u128) << 24 + | (b'U' as u128) << 32 + | (b'T' as u128) << 40 + | (b'F' as u128) << 48 | (b'8' as u128) << 56; -pub(crate) const CONPERM: u128 = (b'c' as u128) - | (b'o' as u128) << 8 - | (b'n' as u128) << 16 - | (b'p' as u128) << 24 - | (b'e' as u128) << 32 - | (b'r' as u128) << 40 - | (b'm' as u128) << 48; -pub(crate) const CONNEG: u128 = (b'c' as u128) - | (b'o' as u128) << 8 - | (b'n' as u128) << 16 - | (b'n' as u128) << 24 - | (b'e' as u128) << 32 - | (b'g' as u128) << 40; -pub(crate) const MT_PRIORITY: u128 = (b'm' as u128) - | (b't' as u128) << 8 +pub(crate) const CONPERM: u128 = (b'C' as u128) + | (b'O' as u128) << 8 + | (b'N' as u128) << 16 + | (b'P' as u128) << 24 + | (b'E' as u128) << 32 + | (b'R' as u128) << 40 + | (b'M' as u128) << 48; +pub(crate) const CONNEG: u128 = (b'C' as u128) + | (b'O' as u128) << 8 + | (b'N' as u128) << 16 + | (b'N' as u128) << 24 + | (b'E' as u128) << 32 + | (b'G' as u128) << 40; +pub(crate) const MT_PRIORITY: u128 = (b'M' as u128) + | (b'T' as u128) << 8 | (b'-' as u128) << 16 - | (b'p' as u128) << 24 - | (b'r' as u128) << 32 - | (b'i' as u128) << 40 - | (b'o' as u128) << 48 - | (b'r' as u128) << 56 - | (b'i' as u128) << 64 - | (b't' as u128) << 72 - | (b'y' as u128) << 80; + | (b'P' as u128) << 24 + | (b'R' as u128) << 32 + | (b'I' as u128) << 40 + | (b'O' as u128) << 48 + | (b'R' as u128) << 56 + | (b'I' as u128) << 64 + | (b'T' as u128) << 72 + | (b'Y' as u128) << 80; pub(crate) const RRVS: u128 = - (b'r' as u128) | (b'r' as u128) << 8 | (b'v' as u128) << 16 | (b's' as u128) << 24; -pub(crate) const REQUIRETLS: u128 = (b'r' as u128) - | (b'e' as u128) << 8 - | (b'q' as u128) << 16 - | (b'u' as u128) << 24 - | (b'i' as u128) << 32 - | (b'r' as u128) << 40 - | (b'e' as u128) << 48 - | (b't' as u128) << 56 - | (b'l' as u128) << 64 - | (b's' as u128) << 72; + (b'R' as u128) | (b'R' as u128) << 8 | (b'V' as u128) << 16 | (b'S' as u128) << 24; +pub(crate) const REQUIRETLS: u128 = (b'R' as u128) + | (b'E' as u128) << 8 + | (b'Q' as u128) << 16 + | (b'U' as u128) << 24 + | (b'I' as u128) << 32 + | (b'R' as u128) << 40 + | (b'E' as u128) << 48 + | (b'T' as u128) << 56 + | (b'L' as u128) << 64 + | (b'S' as u128) << 72; // SASL Mechanisms pub(crate) const _9798_M_DSA_SHA1: u128 = (b'9' as u128) @@ -214,46 +215,46 @@ pub(crate) const _9798_M_DSA_SHA1: u128 = (b'9' as u128) | (b'9' as u128) << 16 | (b'8' as u128) << 24 | (b'-' as u128) << 32 - | (b'm' as u128) << 40 + | (b'M' as u128) << 40 | (b'-' as u128) << 48 - | (b'd' as u128) << 56 - | (b's' as u128) << 64 - | (b'a' as u128) << 72 + | (b'D' as u128) << 56 + | (b'S' as u128) << 64 + | (b'A' as u128) << 72 | (b'-' as u128) << 80 - | (b's' as u128) << 88 - | (b'h' as u128) << 96 - | (b'a' as u128) << 104 + | (b'S' as u128) << 88 + | (b'H' as u128) << 96 + | (b'A' as u128) << 104 | (b'1' as u128) << 112; pub(crate) const _9798_M_ECDSA_SHA: u128 = (b'9' as u128) | (b'7' as u128) << 8 | (b'9' as u128) << 16 | (b'8' as u128) << 24 | (b'-' as u128) << 32 - | (b'm' as u128) << 40 + | (b'M' as u128) << 40 | (b'-' as u128) << 48 - | (b'e' as u128) << 56 - | (b'c' as u128) << 64 - | (b'd' as u128) << 72 - | (b's' as u128) << 80 - | (b'a' as u128) << 88 + | (b'E' as u128) << 56 + | (b'C' as u128) << 64 + | (b'D' as u128) << 72 + | (b'S' as u128) << 80 + | (b'A' as u128) << 88 | (b'-' as u128) << 96 - | (b's' as u128) << 104 - | (b'h' as u128) << 112 - | (b'a' as u128) << 120; + | (b'S' as u128) << 104 + | (b'H' as u128) << 112 + | (b'A' as u128) << 120; pub(crate) const _9798_M_RSA_SHA1_: u128 = (b'9' as u128) | (b'7' as u128) << 8 | (b'9' as u128) << 16 | (b'8' as u128) << 24 | (b'-' as u128) << 32 - | (b'm' as u128) << 40 + | (b'M' as u128) << 40 | (b'-' as u128) << 48 - | (b'r' as u128) << 56 - | (b's' as u128) << 64 - | (b'a' as u128) << 72 + | (b'R' as u128) << 56 + | (b'S' as u128) << 64 + | (b'A' as u128) << 72 | (b'-' as u128) << 80 - | (b's' as u128) << 88 - | (b'h' as u128) << 96 - | (b'a' as u128) << 104 + | (b'S' as u128) << 88 + | (b'H' as u128) << 96 + | (b'A' as u128) << 104 | (b'1' as u128) << 112 | (b'-' as u128) << 120; pub(crate) const _9798_U_DSA_SHA1: u128 = (b'9' as u128) @@ -261,386 +262,386 @@ pub(crate) const _9798_U_DSA_SHA1: u128 = (b'9' as u128) | (b'9' as u128) << 16 | (b'8' as u128) << 24 | (b'-' as u128) << 32 - | (b'u' as u128) << 40 + | (b'U' as u128) << 40 | (b'-' as u128) << 48 - | (b'd' as u128) << 56 - | (b's' as u128) << 64 - | (b'a' as u128) << 72 + | (b'D' as u128) << 56 + | (b'S' as u128) << 64 + | (b'A' as u128) << 72 | (b'-' as u128) << 80 - | (b's' as u128) << 88 - | (b'h' as u128) << 96 - | (b'a' as u128) << 104 + | (b'S' as u128) << 88 + | (b'H' as u128) << 96 + | (b'A' as u128) << 104 | (b'1' as u128) << 112; pub(crate) const _9798_U_ECDSA_SHA: u128 = (b'9' as u128) | (b'7' as u128) << 8 | (b'9' as u128) << 16 | (b'8' as u128) << 24 | (b'-' as u128) << 32 - | (b'u' as u128) << 40 + | (b'U' as u128) << 40 | (b'-' as u128) << 48 - | (b'e' as u128) << 56 - | (b'c' as u128) << 64 - | (b'd' as u128) << 72 - | (b's' as u128) << 80 - | (b'a' as u128) << 88 + | (b'E' as u128) << 56 + | (b'C' as u128) << 64 + | (b'D' as u128) << 72 + | (b'S' as u128) << 80 + | (b'A' as u128) << 88 | (b'-' as u128) << 96 - | (b's' as u128) << 104 - | (b'h' as u128) << 112 - | (b'a' as u128) << 120; + | (b'S' as u128) << 104 + | (b'H' as u128) << 112 + | (b'A' as u128) << 120; pub(crate) const _9798_U_RSA_SHA1_: u128 = (b'9' as u128) | (b'7' as u128) << 8 | (b'9' as u128) << 16 | (b'8' as u128) << 24 | (b'-' as u128) << 32 - | (b'u' as u128) << 40 + | (b'U' as u128) << 40 | (b'-' as u128) << 48 - | (b'r' as u128) << 56 - | (b's' as u128) << 64 - | (b'a' as u128) << 72 + | (b'R' as u128) << 56 + | (b'S' as u128) << 64 + | (b'A' as u128) << 72 | (b'-' as u128) << 80 - | (b's' as u128) << 88 - | (b'h' as u128) << 96 - | (b'a' as u128) << 104 + | (b'S' as u128) << 88 + | (b'H' as u128) << 96 + | (b'A' as u128) << 104 | (b'1' as u128) << 112 | (b'-' as u128) << 120; -pub(crate) const ANONYMOUS: u128 = (b'a' as u128) - | (b'n' as u128) << 8 - | (b'o' as u128) << 16 - | (b'n' as u128) << 24 - | (b'y' as u128) << 32 - | (b'm' as u128) << 40 - | (b'o' as u128) << 48 - | (b'u' as u128) << 56 - | (b's' as u128) << 64; -pub(crate) const CRAM_MD5: u128 = (b'c' as u128) - | (b'r' as u128) << 8 - | (b'a' as u128) << 16 - | (b'm' as u128) << 24 +pub(crate) const ANONYMOUS: u128 = (b'A' as u128) + | (b'N' as u128) << 8 + | (b'O' as u128) << 16 + | (b'N' as u128) << 24 + | (b'Y' as u128) << 32 + | (b'M' as u128) << 40 + | (b'O' as u128) << 48 + | (b'U' as u128) << 56 + | (b'S' as u128) << 64; +pub(crate) const CRAM_MD5: u128 = (b'C' as u128) + | (b'R' as u128) << 8 + | (b'A' as u128) << 16 + | (b'M' as u128) << 24 | (b'-' as u128) << 32 - | (b'm' as u128) << 40 - | (b'd' as u128) << 48 + | (b'M' as u128) << 40 + | (b'D' as u128) << 48 | (b'5' as u128) << 56; -pub(crate) const DIGEST_MD5: u128 = (b'd' as u128) - | (b'i' as u128) << 8 - | (b'g' as u128) << 16 - | (b'e' as u128) << 24 - | (b's' as u128) << 32 - | (b't' as u128) << 40 +pub(crate) const DIGEST_MD5: u128 = (b'D' as u128) + | (b'I' as u128) << 8 + | (b'G' as u128) << 16 + | (b'E' as u128) << 24 + | (b'S' as u128) << 32 + | (b'T' as u128) << 40 | (b'-' as u128) << 48 - | (b'm' as u128) << 56 - | (b'd' as u128) << 64 + | (b'M' as u128) << 56 + | (b'D' as u128) << 64 | (b'5' as u128) << 72; -pub(crate) const EAP_AES128: u128 = (b'e' as u128) - | (b'a' as u128) << 8 - | (b'p' as u128) << 16 +pub(crate) const EAP_AES128: u128 = (b'E' as u128) + | (b'A' as u128) << 8 + | (b'P' as u128) << 16 | (b'-' as u128) << 24 - | (b'a' as u128) << 32 - | (b'e' as u128) << 40 - | (b's' as u128) << 48 + | (b'A' as u128) << 32 + | (b'E' as u128) << 40 + | (b'S' as u128) << 48 | (b'1' as u128) << 56 | (b'2' as u128) << 64 | (b'8' as u128) << 72; -pub(crate) const EAP_AES128_PLUS: u128 = (b'e' as u128) - | (b'a' as u128) << 8 - | (b'p' as u128) << 16 +pub(crate) const EAP_AES128_PLUS: u128 = (b'E' as u128) + | (b'A' as u128) << 8 + | (b'P' as u128) << 16 | (b'-' as u128) << 24 - | (b'a' as u128) << 32 - | (b'e' as u128) << 40 - | (b's' as u128) << 48 + | (b'A' as u128) << 32 + | (b'E' as u128) << 40 + | (b'S' as u128) << 48 | (b'1' as u128) << 56 | (b'2' as u128) << 64 | (b'8' as u128) << 72 | (b'-' as u128) << 80 - | (b'p' as u128) << 88 - | (b'l' as u128) << 96 - | (b'u' as u128) << 104 - | (b's' as u128) << 112; -pub(crate) const ECDH_X25519_CHAL: u128 = (b'e' as u128) - | (b'c' as u128) << 8 - | (b'd' as u128) << 16 - | (b'h' as u128) << 24 + | (b'P' as u128) << 88 + | (b'L' as u128) << 96 + | (b'U' as u128) << 104 + | (b'S' as u128) << 112; +pub(crate) const ECDH_X25519_CHAL: u128 = (b'E' as u128) + | (b'C' as u128) << 8 + | (b'D' as u128) << 16 + | (b'H' as u128) << 24 | (b'-' as u128) << 32 - | (b'x' as u128) << 40 + | (b'X' as u128) << 40 | (b'2' as u128) << 48 | (b'5' as u128) << 56 | (b'5' as u128) << 64 | (b'1' as u128) << 72 | (b'9' as u128) << 80 | (b'-' as u128) << 88 - | (b'c' as u128) << 96 - | (b'h' as u128) << 104 - | (b'a' as u128) << 112 - | (b'l' as u128) << 120; -pub(crate) const ECDSA_NIST256P_C: u128 = (b'e' as u128) - | (b'c' as u128) << 8 - | (b'd' as u128) << 16 - | (b's' as u128) << 24 - | (b'a' as u128) << 32 + | (b'C' as u128) << 96 + | (b'H' as u128) << 104 + | (b'A' as u128) << 112 + | (b'L' as u128) << 120; +pub(crate) const ECDSA_NIST256P_C: u128 = (b'E' as u128) + | (b'C' as u128) << 8 + | (b'D' as u128) << 16 + | (b'S' as u128) << 24 + | (b'A' as u128) << 32 | (b'-' as u128) << 40 - | (b'n' as u128) << 48 - | (b'i' as u128) << 56 - | (b's' as u128) << 64 - | (b't' as u128) << 72 + | (b'N' as u128) << 48 + | (b'I' as u128) << 56 + | (b'S' as u128) << 64 + | (b'T' as u128) << 72 | (b'2' as u128) << 80 | (b'5' as u128) << 88 | (b'6' as u128) << 96 - | (b'p' as u128) << 104 + | (b'P' as u128) << 104 | (b'-' as u128) << 112 - | (b'c' as u128) << 120; -pub(crate) const EXTERNAL: u128 = (b'e' as u128) - | (b'x' as u128) << 8 - | (b't' as u128) << 16 - | (b'e' as u128) << 24 - | (b'r' as u128) << 32 - | (b'n' as u128) << 40 - | (b'a' as u128) << 48 - | (b'l' as u128) << 56; -pub(crate) const GS2_KRB5: u128 = (b'g' as u128) - | (b's' as u128) << 8 + | (b'C' as u128) << 120; +pub(crate) const EXTERNAL: u128 = (b'E' as u128) + | (b'X' as u128) << 8 + | (b'T' as u128) << 16 + | (b'E' as u128) << 24 + | (b'R' as u128) << 32 + | (b'N' as u128) << 40 + | (b'A' as u128) << 48 + | (b'L' as u128) << 56; +pub(crate) const GS2_KRB5: u128 = (b'G' as u128) + | (b'S' as u128) << 8 | (b'2' as u128) << 16 | (b'-' as u128) << 24 - | (b'k' as u128) << 32 - | (b'r' as u128) << 40 - | (b'b' as u128) << 48 + | (b'K' as u128) << 32 + | (b'R' as u128) << 40 + | (b'B' as u128) << 48 | (b'5' as u128) << 56; -pub(crate) const GS2_KRB5_PLUS: u128 = (b'g' as u128) - | (b's' as u128) << 8 +pub(crate) const GS2_KRB5_PLUS: u128 = (b'G' as u128) + | (b'S' as u128) << 8 | (b'2' as u128) << 16 | (b'-' as u128) << 24 - | (b'k' as u128) << 32 - | (b'r' as u128) << 40 - | (b'b' as u128) << 48 + | (b'K' as u128) << 32 + | (b'R' as u128) << 40 + | (b'B' as u128) << 48 | (b'5' as u128) << 56 | (b'-' as u128) << 64 - | (b'p' as u128) << 72 - | (b'l' as u128) << 80 - | (b'u' as u128) << 88 - | (b's' as u128) << 96; -pub(crate) const GSS_SPNEGO: u128 = (b'g' as u128) - | (b's' as u128) << 8 - | (b's' as u128) << 16 + | (b'P' as u128) << 72 + | (b'L' as u128) << 80 + | (b'U' as u128) << 88 + | (b'S' as u128) << 96; +pub(crate) const GSS_SPNEGO: u128 = (b'G' as u128) + | (b'S' as u128) << 8 + | (b'S' as u128) << 16 | (b'-' as u128) << 24 - | (b's' as u128) << 32 - | (b'p' as u128) << 40 - | (b'n' as u128) << 48 - | (b'e' as u128) << 56 - | (b'g' as u128) << 64 - | (b'o' as u128) << 72; -pub(crate) const GSSAPI: u128 = (b'g' as u128) - | (b's' as u128) << 8 - | (b's' as u128) << 16 - | (b'a' as u128) << 24 - | (b'p' as u128) << 32 - | (b'i' as u128) << 40; -pub(crate) const KERBEROS_V4: u128 = (b'k' as u128) - | (b'e' as u128) << 8 - | (b'r' as u128) << 16 - | (b'b' as u128) << 24 - | (b'e' as u128) << 32 - | (b'r' as u128) << 40 - | (b'o' as u128) << 48 - | (b's' as u128) << 56 + | (b'S' as u128) << 32 + | (b'P' as u128) << 40 + | (b'N' as u128) << 48 + | (b'E' as u128) << 56 + | (b'G' as u128) << 64 + | (b'O' as u128) << 72; +pub(crate) const GSSAPI: u128 = (b'G' as u128) + | (b'S' as u128) << 8 + | (b'S' as u128) << 16 + | (b'A' as u128) << 24 + | (b'P' as u128) << 32 + | (b'I' as u128) << 40; +pub(crate) const KERBEROS_V4: u128 = (b'K' as u128) + | (b'E' as u128) << 8 + | (b'R' as u128) << 16 + | (b'B' as u128) << 24 + | (b'E' as u128) << 32 + | (b'R' as u128) << 40 + | (b'O' as u128) << 48 + | (b'S' as u128) << 56 | (b'_' as u128) << 64 - | (b'v' as u128) << 72 + | (b'V' as u128) << 72 | (b'4' as u128) << 80; -pub(crate) const KERBEROS_V5: u128 = (b'k' as u128) - | (b'e' as u128) << 8 - | (b'r' as u128) << 16 - | (b'b' as u128) << 24 - | (b'e' as u128) << 32 - | (b'r' as u128) << 40 - | (b'o' as u128) << 48 - | (b's' as u128) << 56 +pub(crate) const KERBEROS_V5: u128 = (b'K' as u128) + | (b'E' as u128) << 8 + | (b'R' as u128) << 16 + | (b'B' as u128) << 24 + | (b'E' as u128) << 32 + | (b'R' as u128) << 40 + | (b'O' as u128) << 48 + | (b'S' as u128) << 56 | (b'_' as u128) << 64 - | (b'v' as u128) << 72 + | (b'V' as u128) << 72 | (b'5' as u128) << 80; -pub(crate) const LOGIN: u128 = (b'l' as u128) - | (b'o' as u128) << 8 - | (b'g' as u128) << 16 - | (b'i' as u128) << 24 - | (b'n' as u128) << 32; -pub(crate) const NMAS_SAMBA_AUTH: u128 = (b'n' as u128) - | (b'm' as u128) << 8 - | (b'a' as u128) << 16 - | (b's' as u128) << 24 +pub(crate) const LOGIN: u128 = (b'L' as u128) + | (b'O' as u128) << 8 + | (b'G' as u128) << 16 + | (b'I' as u128) << 24 + | (b'N' as u128) << 32; +pub(crate) const NMAS_SAMBA_AUTH: u128 = (b'N' as u128) + | (b'M' as u128) << 8 + | (b'A' as u128) << 16 + | (b'S' as u128) << 24 | (b'-' as u128) << 32 - | (b's' as u128) << 40 - | (b'a' as u128) << 48 - | (b'm' as u128) << 56 - | (b'b' as u128) << 64 - | (b'a' as u128) << 72 + | (b'S' as u128) << 40 + | (b'A' as u128) << 48 + | (b'M' as u128) << 56 + | (b'B' as u128) << 64 + | (b'A' as u128) << 72 | (b'-' as u128) << 80 - | (b'a' as u128) << 88 - | (b'u' as u128) << 96 - | (b't' as u128) << 104 - | (b'h' as u128) << 112; -pub(crate) const NMAS_AUTHEN: u128 = (b'n' as u128) - | (b'm' as u128) << 8 - | (b'a' as u128) << 16 - | (b's' as u128) << 24 + | (b'A' as u128) << 88 + | (b'U' as u128) << 96 + | (b'T' as u128) << 104 + | (b'H' as u128) << 112; +pub(crate) const NMAS_AUTHEN: u128 = (b'N' as u128) + | (b'M' as u128) << 8 + | (b'A' as u128) << 16 + | (b'S' as u128) << 24 | (b'_' as u128) << 32 - | (b'a' as u128) << 40 - | (b'u' as u128) << 48 - | (b't' as u128) << 56 - | (b'h' as u128) << 64 - | (b'e' as u128) << 72 - | (b'n' as u128) << 80; -pub(crate) const NMAS_LOGIN: u128 = (b'n' as u128) - | (b'm' as u128) << 8 - | (b'a' as u128) << 16 - | (b's' as u128) << 24 + | (b'A' as u128) << 40 + | (b'U' as u128) << 48 + | (b'T' as u128) << 56 + | (b'H' as u128) << 64 + | (b'E' as u128) << 72 + | (b'N' as u128) << 80; +pub(crate) const NMAS_LOGIN: u128 = (b'N' as u128) + | (b'M' as u128) << 8 + | (b'A' as u128) << 16 + | (b'S' as u128) << 24 | (b'_' as u128) << 32 - | (b'l' as u128) << 40 - | (b'o' as u128) << 48 - | (b'g' as u128) << 56 - | (b'i' as u128) << 64 - | (b'n' as u128) << 72; + | (b'L' as u128) << 40 + | (b'O' as u128) << 48 + | (b'G' as u128) << 56 + | (b'I' as u128) << 64 + | (b'N' as u128) << 72; pub(crate) const NTLM: u128 = - (b'n' as u128) | (b't' as u128) << 8 | (b'l' as u128) << 16 | (b'm' as u128) << 24; -pub(crate) const OAUTH10A: u128 = (b'o' as u128) - | (b'a' as u128) << 8 - | (b'u' as u128) << 16 - | (b't' as u128) << 24 - | (b'h' as u128) << 32 + (b'N' as u128) | (b'T' as u128) << 8 | (b'L' as u128) << 16 | (b'M' as u128) << 24; +pub(crate) const OAUTH10A: u128 = (b'O' as u128) + | (b'A' as u128) << 8 + | (b'U' as u128) << 16 + | (b'T' as u128) << 24 + | (b'H' as u128) << 32 | (b'1' as u128) << 40 | (b'0' as u128) << 48 - | (b'a' as u128) << 56; -pub(crate) const OAUTHBEARER: u128 = (b'o' as u128) - | (b'a' as u128) << 8 - | (b'u' as u128) << 16 - | (b't' as u128) << 24 - | (b'h' as u128) << 32 - | (b'b' as u128) << 40 - | (b'e' as u128) << 48 - | (b'a' as u128) << 56 - | (b'r' as u128) << 64 - | (b'e' as u128) << 72 - | (b'r' as u128) << 80; -pub(crate) const OPENID20: u128 = (b'o' as u128) - | (b'p' as u128) << 8 - | (b'e' as u128) << 16 - | (b'n' as u128) << 24 - | (b'i' as u128) << 32 - | (b'd' as u128) << 40 + | (b'A' as u128) << 56; +pub(crate) const OAUTHBEARER: u128 = (b'O' as u128) + | (b'A' as u128) << 8 + | (b'U' as u128) << 16 + | (b'T' as u128) << 24 + | (b'H' as u128) << 32 + | (b'B' as u128) << 40 + | (b'E' as u128) << 48 + | (b'A' as u128) << 56 + | (b'R' as u128) << 64 + | (b'E' as u128) << 72 + | (b'R' as u128) << 80; +pub(crate) const OPENID20: u128 = (b'O' as u128) + | (b'P' as u128) << 8 + | (b'E' as u128) << 16 + | (b'N' as u128) << 24 + | (b'I' as u128) << 32 + | (b'D' as u128) << 40 | (b'2' as u128) << 48 | (b'0' as u128) << 56; -pub(crate) const OTP: u128 = (b'o' as u128) | (b't' as u128) << 8 | (b'p' as u128) << 16; -pub(crate) const PLAIN: u128 = (b'p' as u128) - | (b'l' as u128) << 8 - | (b'a' as u128) << 16 - | (b'i' as u128) << 24 - | (b'n' as u128) << 32; -pub(crate) const SAML20: u128 = (b's' as u128) - | (b'a' as u128) << 8 - | (b'm' as u128) << 16 - | (b'l' as u128) << 24 +pub(crate) const OTP: u128 = (b'O' as u128) | (b'T' as u128) << 8 | (b'P' as u128) << 16; +pub(crate) const PLAIN: u128 = (b'P' as u128) + | (b'L' as u128) << 8 + | (b'A' as u128) << 16 + | (b'I' as u128) << 24 + | (b'N' as u128) << 32; +pub(crate) const SAML20: u128 = (b'S' as u128) + | (b'A' as u128) << 8 + | (b'M' as u128) << 16 + | (b'L' as u128) << 24 | (b'2' as u128) << 32 | (b'0' as u128) << 40; -pub(crate) const SCRAM_SHA_1: u128 = (b's' as u128) - | (b'c' as u128) << 8 - | (b'r' as u128) << 16 - | (b'a' as u128) << 24 - | (b'm' as u128) << 32 +pub(crate) const SCRAM_SHA_1: u128 = (b'S' as u128) + | (b'C' as u128) << 8 + | (b'R' as u128) << 16 + | (b'A' as u128) << 24 + | (b'M' as u128) << 32 | (b'-' as u128) << 40 - | (b's' as u128) << 48 - | (b'h' as u128) << 56 - | (b'a' as u128) << 64 + | (b'S' as u128) << 48 + | (b'H' as u128) << 56 + | (b'A' as u128) << 64 | (b'-' as u128) << 72 | (b'1' as u128) << 80; -pub(crate) const SCRAM_SHA_1_PLUS: u128 = (b's' as u128) - | (b'c' as u128) << 8 - | (b'r' as u128) << 16 - | (b'a' as u128) << 24 - | (b'm' as u128) << 32 +pub(crate) const SCRAM_SHA_1_PLUS: u128 = (b'S' as u128) + | (b'C' as u128) << 8 + | (b'R' as u128) << 16 + | (b'A' as u128) << 24 + | (b'M' as u128) << 32 | (b'-' as u128) << 40 - | (b's' as u128) << 48 - | (b'h' as u128) << 56 - | (b'a' as u128) << 64 + | (b'S' as u128) << 48 + | (b'H' as u128) << 56 + | (b'A' as u128) << 64 | (b'-' as u128) << 72 | (b'1' as u128) << 80 | (b'-' as u128) << 88 - | (b'p' as u128) << 96 - | (b'l' as u128) << 104 - | (b'u' as u128) << 112 - | (b's' as u128) << 120; -pub(crate) const SCRAM_SHA_256: u128 = (b's' as u128) - | (b'c' as u128) << 8 - | (b'r' as u128) << 16 - | (b'a' as u128) << 24 - | (b'm' as u128) << 32 + | (b'P' as u128) << 96 + | (b'L' as u128) << 104 + | (b'U' as u128) << 112 + | (b'S' as u128) << 120; +pub(crate) const SCRAM_SHA_256: u128 = (b'S' as u128) + | (b'C' as u128) << 8 + | (b'R' as u128) << 16 + | (b'A' as u128) << 24 + | (b'M' as u128) << 32 | (b'-' as u128) << 40 - | (b's' as u128) << 48 - | (b'h' as u128) << 56 - | (b'a' as u128) << 64 + | (b'S' as u128) << 48 + | (b'H' as u128) << 56 + | (b'A' as u128) << 64 | (b'-' as u128) << 72 | (b'2' as u128) << 80 | (b'5' as u128) << 88 | (b'6' as u128) << 96; -pub(crate) const SCRAM_SHA_256_PL: u128 = (b's' as u128) - | (b'c' as u128) << 8 - | (b'r' as u128) << 16 - | (b'a' as u128) << 24 - | (b'm' as u128) << 32 +pub(crate) const SCRAM_SHA_256_PL: u128 = (b'S' as u128) + | (b'C' as u128) << 8 + | (b'R' as u128) << 16 + | (b'A' as u128) << 24 + | (b'M' as u128) << 32 | (b'-' as u128) << 40 - | (b's' as u128) << 48 - | (b'h' as u128) << 56 - | (b'a' as u128) << 64 + | (b'S' as u128) << 48 + | (b'H' as u128) << 56 + | (b'A' as u128) << 64 | (b'-' as u128) << 72 | (b'2' as u128) << 80 | (b'5' as u128) << 88 | (b'6' as u128) << 96 | (b'-' as u128) << 104 - | (b'p' as u128) << 112 - | (b'l' as u128) << 120; -pub(crate) const SECURID: u128 = (b's' as u128) - | (b'e' as u128) << 8 - | (b'c' as u128) << 16 - | (b'u' as u128) << 24 - | (b'r' as u128) << 32 - | (b'i' as u128) << 40 - | (b'd' as u128) << 48; + | (b'P' as u128) << 112 + | (b'L' as u128) << 120; +pub(crate) const SECURID: u128 = (b'S' as u128) + | (b'E' as u128) << 8 + | (b'C' as u128) << 16 + | (b'U' as u128) << 24 + | (b'R' as u128) << 32 + | (b'I' as u128) << 40 + | (b'D' as u128) << 48; pub(crate) const SKEY: u128 = - (b's' as u128) | (b'k' as u128) << 8 | (b'e' as u128) << 16 | (b'y' as u128) << 24; -pub(crate) const SPNEGO: u128 = (b's' as u128) - | (b'p' as u128) << 8 - | (b'n' as u128) << 16 - | (b'e' as u128) << 24 - | (b'g' as u128) << 32 - | (b'o' as u128) << 40; -pub(crate) const SPNEGO_PLUS: u128 = (b's' as u128) - | (b'p' as u128) << 8 - | (b'n' as u128) << 16 - | (b'e' as u128) << 24 - | (b'g' as u128) << 32 - | (b'o' as u128) << 40 + (b'S' as u128) | (b'K' as u128) << 8 | (b'E' as u128) << 16 | (b'Y' as u128) << 24; +pub(crate) const SPNEGO: u128 = (b'S' as u128) + | (b'P' as u128) << 8 + | (b'N' as u128) << 16 + | (b'E' as u128) << 24 + | (b'G' as u128) << 32 + | (b'O' as u128) << 40; +pub(crate) const SPNEGO_PLUS: u128 = (b'S' as u128) + | (b'P' as u128) << 8 + | (b'N' as u128) << 16 + | (b'E' as u128) << 24 + | (b'G' as u128) << 32 + | (b'O' as u128) << 40 | (b'-' as u128) << 48 - | (b'p' as u128) << 56 - | (b'l' as u128) << 64 - | (b'u' as u128) << 72 - | (b's' as u128) << 80; -pub(crate) const SXOVER_PLUS: u128 = (b's' as u128) - | (b'x' as u128) << 8 - | (b'o' as u128) << 16 - | (b'v' as u128) << 24 - | (b'e' as u128) << 32 - | (b'r' as u128) << 40 + | (b'P' as u128) << 56 + | (b'L' as u128) << 64 + | (b'U' as u128) << 72 + | (b'S' as u128) << 80; +pub(crate) const SXOVER_PLUS: u128 = (b'S' as u128) + | (b'X' as u128) << 8 + | (b'O' as u128) << 16 + | (b'V' as u128) << 24 + | (b'E' as u128) << 32 + | (b'R' as u128) << 40 | (b'-' as u128) << 48 - | (b'p' as u128) << 56 - | (b'l' as u128) << 64 - | (b'u' as u128) << 72 - | (b's' as u128) << 80; -pub(crate) const XOAUTH: u128 = (b'x' as u128) - | (b'o' as u128) << 8 - | (b'a' as u128) << 16 - | (b'u' as u128) << 24 - | (b't' as u128) << 32 - | (b'h' as u128) << 40; -pub(crate) const XOAUTH2: u128 = (b'x' as u128) - | (b'o' as u128) << 8 - | (b'a' as u128) << 16 - | (b'u' as u128) << 24 - | (b't' as u128) << 32 - | (b'h' as u128) << 40 + | (b'P' as u128) << 56 + | (b'L' as u128) << 64 + | (b'U' as u128) << 72 + | (b'S' as u128) << 80; +pub(crate) const XOAUTH: u128 = (b'X' as u128) + | (b'O' as u128) << 8 + | (b'A' as u128) << 16 + | (b'U' as u128) << 24 + | (b'T' as u128) << 32 + | (b'H' as u128) << 40; +pub(crate) const XOAUTH2: u128 = (b'X' as u128) + | (b'O' as u128) << 8 + | (b'A' as u128) << 16 + | (b'U' as u128) << 24 + | (b'T' as u128) << 32 + | (b'H' as u128) << 40 | (b'2' as u128) << 48; /* diff --git a/src/request/parser.rs b/src/request/parser.rs index 0d9ac69..6e157ce 100644 --- a/src/request/parser.rs +++ b/src/request/parser.rs @@ -8,8 +8,8 @@ use crate::{ use super::*; impl Request { - pub fn parse<'x>(bytes: &'x mut Iter<'x, u8>) -> Result, Error> { - let mut parser = RequestParser::new(bytes); + pub fn parse(bytes: &mut Iter<'_, u8>) -> Result, Error> { + let mut parser = Rfc5321Parser::new(bytes); let command = parser.hashed_value()?; if !parser.stop_char.is_ascii_whitespace() { return Err(Error::UnknownCommand); @@ -289,15 +289,18 @@ impl Request { } } -struct RequestParser<'x> { - bytes: &'x mut Iter<'x, u8>, - stop_char: u8, +pub(crate) struct Rfc5321Parser<'x, 'y> { + bytes: &'x mut Iter<'y, u8>, + pub(crate) stop_char: u8, + bytes_left: usize, } -impl<'x> RequestParser<'x> { - pub fn new(bytes: &'x mut Iter<'x, u8>) -> Self { - RequestParser { +impl<'x, 'y> Rfc5321Parser<'x, 'y> { + pub fn new(bytes: &'x mut Iter<'y, u8>) -> Self { + let (bytes_left, _) = bytes.size_hint(); + Rfc5321Parser { bytes, + bytes_left, stop_char: 0, } } @@ -309,12 +312,12 @@ impl<'x> RequestParser<'x> { while let Some(&ch) = self.bytes.next() { match ch { - b'A'..=b'Z' if shift < 64 => { - value |= ((ch - b'A' + b'a') as u64) << shift; + b'A'..=b'Z' | b'0'..=b'9' | b'-' if shift < 64 => { + value |= (ch as u64) << shift; shift += 8; } - b'a'..=b'z' | b'0'..=b'9' | b'-' if shift < 64 => { - value |= (ch as u64) << shift; + b'a'..=b'z' if shift < 64 => { + value |= ((ch - b'a' + b'A') as u64) << shift; shift += 8; } b'\r' => (), @@ -331,7 +334,9 @@ impl<'x> RequestParser<'x> { } } - Err(Error::NeedsMoreData) + Err(Error::NeedsMoreData { + bytes_left: self.bytes_left, + }) } #[allow(clippy::while_let_on_iterator)] @@ -341,12 +346,12 @@ impl<'x> RequestParser<'x> { while let Some(&ch) = self.bytes.next() { match ch { - b'A'..=b'Z' if shift < 128 => { - value |= ((ch - b'A' + b'a') as u128) << shift; + b'A'..=b'Z' | b'0'..=b'9' | b'-' if shift < 128 => { + value |= (ch as u128) << shift; shift += 8; } - b'a'..=b'z' | b'0'..=b'9' | b'-' if shift < 128 => { - value |= (ch as u128) << shift; + b'a'..=b'z' if shift < 128 => { + value |= ((ch - b'a' + b'A') as u128) << shift; shift += 8; } b' ' => { @@ -363,7 +368,9 @@ impl<'x> RequestParser<'x> { } } - Err(Error::NeedsMoreData) + Err(Error::NeedsMoreData { + bytes_left: self.bytes_left, + }) } pub fn address(&mut self) -> Result, Error> { @@ -470,7 +477,9 @@ impl<'x> RequestParser<'x> { last_ch = ch; } - Err(Error::NeedsMoreData) + Err(Error::NeedsMoreData { + bytes_left: self.bytes_left, + }) } pub fn string(&mut self) -> Result { @@ -503,7 +512,9 @@ impl<'x> RequestParser<'x> { last_ch = ch; } - Err(Error::NeedsMoreData) + Err(Error::NeedsMoreData { + bytes_left: self.bytes_left, + }) } #[allow(clippy::while_let_on_iterator)] @@ -526,7 +537,9 @@ impl<'x> RequestParser<'x> { } } - Err(Error::NeedsMoreData) + Err(Error::NeedsMoreData { + bytes_left: self.bytes_left, + }) } #[allow(clippy::while_let_on_iterator)] @@ -572,7 +585,9 @@ impl<'x> RequestParser<'x> { } } - Err(Error::NeedsMoreData) + Err(Error::NeedsMoreData { + bytes_left: self.bytes_left, + }) } #[allow(clippy::while_let_on_iterator)] @@ -602,7 +617,9 @@ impl<'x> RequestParser<'x> { } } - Err(Error::NeedsMoreData) + Err(Error::NeedsMoreData { + bytes_left: self.bytes_left, + }) } #[inline(always)] @@ -613,7 +630,9 @@ impl<'x> RequestParser<'x> { return Ok(()); } } - Err(Error::NeedsMoreData) + Err(Error::NeedsMoreData { + bytes_left: self.bytes_left, + }) } else { Ok(()) } @@ -630,7 +649,16 @@ impl<'x> RequestParser<'x> { } } } - Err(Error::NeedsMoreData) + Err(Error::NeedsMoreData { + bytes_left: self.bytes_left, + }) + } + + #[inline(always)] + pub fn read_char(&mut self) -> Result { + self.bytes.next().copied().ok_or(Error::NeedsMoreData { + bytes_left: self.bytes_left, + }) } pub fn size(&mut self) -> Result { @@ -659,7 +687,9 @@ impl<'x> RequestParser<'x> { } } } - Err(Error::NeedsMoreData) + Err(Error::NeedsMoreData { + bytes_left: self.bytes_left, + }) } pub fn integer(&mut self) -> Result { @@ -696,7 +726,9 @@ impl<'x> RequestParser<'x> { } } } - Err(Error::NeedsMoreData) + Err(Error::NeedsMoreData { + bytes_left: self.bytes_left, + }) } pub fn timestamp(&mut self) -> Result { @@ -752,7 +784,9 @@ impl<'x> RequestParser<'x> { } } - Err(Error::NeedsMoreData) + Err(Error::NeedsMoreData { + bytes_left: self.bytes_left, + }) } pub fn parameters(&mut self) -> Result>, Error> { @@ -1012,7 +1046,7 @@ impl<'x> RequestParser<'x> { Ok(params) } - fn mechanism(&mut self) -> Result, Error> { + pub(crate) fn mechanism(&mut self) -> Result, Error> { let mut trailing_chars = [0u8; 8]; let mut pos = 0; let mechanism = self.hashed_value_long()?; @@ -1031,7 +1065,9 @@ impl<'x> RequestParser<'x> { } } if !self.stop_char.is_ascii_whitespace() { - return Err(Error::NeedsMoreData); + return Err(Error::NeedsMoreData { + bytes_left: self.bytes_left, + }); } else if pos > 8 { return Ok(Mechanism::Unknown.into()); } diff --git a/src/request/receiver.rs b/src/request/receiver.rs new file mode 100644 index 0000000..7a92087 --- /dev/null +++ b/src/request/receiver.rs @@ -0,0 +1,193 @@ +use std::slice::Iter; + +use crate::{Error, Request}; + +pub struct RequestReceiver { + pub buf: Vec, +} + +pub struct DataReceiver { + pub buf: Vec, + crlf_dot: bool, + last_ch: u8, + prev_last_ch: u8, +} + +pub struct BdatReceiver { + pub buf: Vec, + bytes_left: usize, +} + +impl RequestReceiver { + #[allow(clippy::new_without_default)] + pub fn new() -> Self { + Self { + buf: Vec::with_capacity(0), + } + } + + pub fn ingest( + &mut self, + bytes: &mut Iter<'_, u8>, + buf: &[u8], + ) -> Result, Error> { + if self.buf.is_empty() { + match Request::parse(bytes) { + Err(Error::NeedsMoreData { bytes_left }) if bytes_left > 0 => { + self.buf = buf[buf.len() - bytes_left..].to_vec(); + } + result => return result, + } + } else { + for &ch in bytes { + self.buf.push(ch); + if ch == b'\n' { + let result = Request::parse(&mut self.buf.iter()); + self.buf.clear(); + return result; + } + } + } + + Err(Error::NeedsMoreData { bytes_left: 0 }) + } +} + +impl DataReceiver { + #[allow(clippy::new_without_default)] + pub fn new() -> Self { + Self { + buf: Vec::with_capacity(1024), + crlf_dot: false, + last_ch: 0, + prev_last_ch: 0, + } + } + + pub fn ingest(&mut self, bytes: &mut Iter<'_, u8>) -> bool { + for &ch in bytes { + match ch { + b'.' if self.last_ch == b'\n' && self.prev_last_ch == b'\r' => { + self.crlf_dot = true; + } + b'\n' if self.crlf_dot && self.last_ch == b'\r' => { + self.buf.truncate(self.buf.len() - 3); + return true; + } + b'\r' => { + self.buf.push(ch); + } + _ => { + self.buf.push(ch); + self.crlf_dot = false; + } + } + self.prev_last_ch = self.last_ch; + self.last_ch = ch; + } + + false + } +} + +impl BdatReceiver { + pub fn new(bytes_left: usize) -> Self { + Self { + buf: Vec::with_capacity(bytes_left), + bytes_left, + } + } + + pub fn ingest(&mut self, bytes: &mut Iter<'_, u8>) -> bool { + for &ch in bytes { + self.buf.push(ch); + if self.buf.len() == self.bytes_left { + return true; + } + } + + false + } +} + +#[cfg(test)] +mod tests { + use crate::{request::receiver::RequestReceiver, Error, Request}; + + use super::DataReceiver; + + #[test] + fn data_receiver() { + 'outer: for (data, message) in [ + ( + vec!["hi\r\n", "..\r\n", ".a\r\n", "\r\n.\r\n"], + "hi\r\n.\r\na\r\n", + ), + ( + vec!["\r\na\rb\nc\r\n.d\r\n..\r\n", "\r\n.\r\n"], + "\r\na\rb\nc\r\nd\r\n.\r\n", + ), + ] { + let mut r = DataReceiver::new(); + for data in &data { + if r.ingest(&mut data.as_bytes().iter()) { + assert_eq!(message, String::from_utf8(r.buf).unwrap()); + continue 'outer; + } + } + panic!("Failed for {:?}", data); + } + } + + #[test] + fn request_receiver() { + for (data, expected_requests) in [ + ( + vec![ + "data\n", + "start", + "tls\n", + "quit\nnoop", + " hello\nehlo test\nvrfy name\n", + ], + vec![ + Request::Data, + Request::StartTls, + Request::Quit, + Request::Noop { + value: "hello".to_string(), + }, + Request::Ehlo { + host: "test".to_string(), + }, + Request::Vrfy { + value: "name".to_string(), + }, + ], + ), + ( + vec!["d", "a", "t", "a", "\n", "quit", "\n"], + vec![Request::Data, Request::Quit], + ), + ] { + let mut requests = Vec::new(); + let mut r = RequestReceiver::new(); + for data in &data { + let mut bytes = data.as_bytes().iter(); + loop { + match r.ingest(&mut bytes, data.as_bytes()) { + Ok(request) => { + requests.push(request); + continue; + } + Err(Error::NeedsMoreData { .. }) => { + break; + } + err => panic!("Unexpected error {:?}", err), + } + } + } + assert_eq!(expected_requests, requests); + } + } +} diff --git a/src/response/mod.rs b/src/response/mod.rs index e69de29..5d06867 100644 --- a/src/response/mod.rs +++ b/src/response/mod.rs @@ -0,0 +1,191 @@ +pub mod parser; + +pub(crate) const _8BITMIME: u128 = (b'8' as u128) + | (b'B' as u128) << 8 + | (b'I' as u128) << 16 + | (b'T' as u128) << 24 + | (b'M' as u128) << 32 + | (b'I' as u128) << 40 + | (b'M' as u128) << 48 + | (b'E' as u128) << 56; +pub(crate) const ATRN: u128 = + (b'A' as u128) | (b'T' as u128) << 8 | (b'R' as u128) << 16 | (b'N' as u128) << 24; +pub(crate) const AUTH: u128 = + (b'A' as u128) | (b'U' as u128) << 8 | (b'T' as u128) << 16 | (b'H' as u128) << 24; +pub(crate) const BINARYMIME: u128 = (b'B' as u128) + | (b'I' as u128) << 8 + | (b'N' as u128) << 16 + | (b'A' as u128) << 24 + | (b'R' as u128) << 32 + | (b'Y' as u128) << 40 + | (b'M' as u128) << 48 + | (b'I' as u128) << 56 + | (b'M' as u128) << 64 + | (b'E' as u128) << 72; +pub(crate) const BURL: u128 = + (b'B' as u128) | (b'U' as u128) << 8 | (b'R' as u128) << 16 | (b'L' as u128) << 24; +pub(crate) const CHECKPOINT: u128 = (b'C' as u128) + | (b'H' as u128) << 8 + | (b'E' as u128) << 16 + | (b'C' as u128) << 24 + | (b'K' as u128) << 32 + | (b'P' as u128) << 40 + | (b'O' as u128) << 48 + | (b'I' as u128) << 56 + | (b'N' as u128) << 64 + | (b'T' as u128) << 72; +pub(crate) const CHUNKING: u128 = (b'C' as u128) + | (b'H' as u128) << 8 + | (b'U' as u128) << 16 + | (b'N' as u128) << 24 + | (b'K' as u128) << 32 + | (b'I' as u128) << 40 + | (b'N' as u128) << 48 + | (b'G' as u128) << 56; +pub(crate) const CONNEG: u128 = (b'C' as u128) + | (b'O' as u128) << 8 + | (b'N' as u128) << 16 + | (b'N' as u128) << 24 + | (b'E' as u128) << 32 + | (b'G' as u128) << 40; +pub(crate) const CONPERM: u128 = (b'C' as u128) + | (b'O' as u128) << 8 + | (b'N' as u128) << 16 + | (b'P' as u128) << 24 + | (b'E' as u128) << 32 + | (b'R' as u128) << 40 + | (b'M' as u128) << 48; +pub(crate) const DELIVERBY: u128 = (b'D' as u128) + | (b'E' as u128) << 8 + | (b'L' as u128) << 16 + | (b'I' as u128) << 24 + | (b'V' as u128) << 32 + | (b'E' as u128) << 40 + | (b'R' as u128) << 48 + | (b'B' as u128) << 56 + | (b'Y' as u128) << 64; +pub(crate) const DSN: u128 = (b'D' as u128) | (b'S' as u128) << 8 | (b'N' as u128) << 16; +pub(crate) const ENHANCEDSTATUSCO: u128 = (b'E' as u128) + | (b'N' as u128) << 8 + | (b'H' as u128) << 16 + | (b'A' as u128) << 24 + | (b'N' as u128) << 32 + | (b'C' as u128) << 40 + | (b'E' as u128) << 48 + | (b'D' as u128) << 56 + | (b'S' as u128) << 64 + | (b'T' as u128) << 72 + | (b'A' as u128) << 80 + | (b'T' as u128) << 88 + | (b'U' as u128) << 96 + | (b'S' as u128) << 104 + | (b'C' as u128) << 112 + | (b'O' as u128) << 120; +pub(crate) const ETRN: u128 = + (b'E' as u128) | (b'T' as u128) << 8 | (b'R' as u128) << 16 | (b'N' as u128) << 24; +pub(crate) const EXPN: u128 = + (b'E' as u128) | (b'X' as u128) << 8 | (b'P' as u128) << 16 | (b'N' as u128) << 24; +pub(crate) const FUTURERELEASE: u128 = (b'F' as u128) + | (b'U' as u128) << 8 + | (b'T' as u128) << 16 + | (b'U' as u128) << 24 + | (b'R' as u128) << 32 + | (b'E' as u128) << 40 + | (b'R' as u128) << 48 + | (b'E' as u128) << 56 + | (b'L' as u128) << 64 + | (b'E' as u128) << 72 + | (b'A' as u128) << 80 + | (b'S' as u128) << 88 + | (b'E' as u128) << 96; +pub(crate) const HELP: u128 = + (b'H' as u128) | (b'E' as u128) << 8 | (b'L' as u128) << 16 | (b'P' as u128) << 24; +pub(crate) const MT_PRIORITY: u128 = (b'M' as u128) + | (b'T' as u128) << 8 + | (b'-' as u128) << 16 + | (b'P' as u128) << 24 + | (b'R' as u128) << 32 + | (b'I' as u128) << 40 + | (b'O' as u128) << 48 + | (b'R' as u128) << 56 + | (b'I' as u128) << 64 + | (b'T' as u128) << 72 + | (b'Y' as u128) << 80; +pub(crate) const MTRK: u128 = + (b'M' as u128) | (b'T' as u128) << 8 | (b'R' as u128) << 16 | (b'K' as u128) << 24; +pub(crate) const NO_SOLICITING: u128 = (b'N' as u128) + | (b'O' as u128) << 8 + | (b'-' as u128) << 16 + | (b'S' as u128) << 24 + | (b'O' as u128) << 32 + | (b'L' as u128) << 40 + | (b'I' as u128) << 48 + | (b'C' as u128) << 56 + | (b'I' as u128) << 64 + | (b'T' as u128) << 72 + | (b'I' as u128) << 80 + | (b'N' as u128) << 88 + | (b'G' as u128) << 96; +pub(crate) const ONEX: u128 = + (b'O' as u128) | (b'N' as u128) << 8 | (b'E' as u128) << 16 | (b'X' as u128) << 24; +pub(crate) const PIPELINING: u128 = (b'P' as u128) + | (b'I' as u128) << 8 + | (b'P' as u128) << 16 + | (b'E' as u128) << 24 + | (b'L' as u128) << 32 + | (b'I' as u128) << 40 + | (b'N' as u128) << 48 + | (b'I' as u128) << 56 + | (b'N' as u128) << 64 + | (b'G' as u128) << 72; +pub(crate) const REQUIRETLS: u128 = (b'R' as u128) + | (b'E' as u128) << 8 + | (b'Q' as u128) << 16 + | (b'U' as u128) << 24 + | (b'I' as u128) << 32 + | (b'R' as u128) << 40 + | (b'E' as u128) << 48 + | (b'T' as u128) << 56 + | (b'L' as u128) << 64 + | (b'S' as u128) << 72; +pub(crate) const RRVS: u128 = + (b'R' as u128) | (b'R' as u128) << 8 | (b'V' as u128) << 16 | (b'S' as u128) << 24; +pub(crate) const SIZE: u128 = + (b'S' as u128) | (b'I' as u128) << 8 | (b'Z' as u128) << 16 | (b'E' as u128) << 24; +pub(crate) const SMTPUTF8: u128 = (b'S' as u128) + | (b'M' as u128) << 8 + | (b'T' as u128) << 16 + | (b'P' as u128) << 24 + | (b'U' as u128) << 32 + | (b'T' as u128) << 40 + | (b'F' as u128) << 48 + | (b'8' as u128) << 56; +pub(crate) const STARTTLS: u128 = (b'S' as u128) + | (b'T' as u128) << 8 + | (b'A' as u128) << 16 + | (b'R' as u128) << 24 + | (b'T' as u128) << 32 + | (b'T' as u128) << 40 + | (b'L' as u128) << 48 + | (b'S' as u128) << 56; +pub(crate) const VERB: u128 = + (b'V' as u128) | (b'E' as u128) << 8 | (b'R' as u128) << 16 | (b'B' as u128) << 24; + +// Priorities +pub(crate) const MIXER: u128 = (b'M' as u128) + | (b'I' as u128) << 8 + | (b'X' as u128) << 16 + | (b'E' as u128) << 24 + | (b'R' as u128) << 32; +pub(crate) const STANAG4406: u128 = (b'S' as u128) + | (b'T' as u128) << 8 + | (b'A' as u128) << 16 + | (b'N' as u128) << 24 + | (b'A' as u128) << 32 + | (b'G' as u128) << 40 + | (b'4' as u128) << 48 + | (b'4' as u128) << 56 + | (b'0' as u128) << 64 + | (b'6' as u128) << 72; +pub(crate) const NSEP: u128 = + (b'N' as u128) | (b'S' as u128) << 8 | (b'E' as u128) << 16 | (b'P' as u128) << 24; diff --git a/src/response/parser.rs b/src/response/parser.rs new file mode 100644 index 0000000..f0480e1 --- /dev/null +++ b/src/response/parser.rs @@ -0,0 +1,589 @@ +use std::slice::Iter; + +use crate::{ + request::parser::Rfc5321Parser, Capability, EhloResponse, Error, IntoString, MtPriority, + Response, LF, +}; + +use super::*; + +impl EhloResponse { + pub fn parse(bytes: &mut Iter<'_, u8>) -> Result { + let mut parser = Rfc5321Parser::new(bytes); + let mut response = EhloResponse { + hostname: String::new(), + capabilities: Vec::new(), + }; + let mut eol = false; + let mut buf = Vec::with_capacity(32); + let mut code = u16::MAX; + let mut is_first_line = true; + + while !eol { + code = parser.size()? as u16; + match parser.stop_char { + b' ' => { + eol = true; + } + b'-' => (), + b'\n' if code < 600 => { + break; + } + _ => { + return Err(Error::SyntaxError { + syntax: "unexpected token", + }); + } + } + + if !is_first_line && code == 250 { + response + .capabilities + .push(match parser.hashed_value_long()? { + _8BITMIME => Capability::EightBitMime, + ATRN => Capability::Atrn, + AUTH => { + let mut mechanisms = Vec::new(); + while parser.stop_char != LF { + if let Some(mechanism) = parser.mechanism()? { + mechanisms.push(mechanism); + } + } + + Capability::Auth { mechanisms } + } + BINARYMIME => Capability::BinaryMime, + BURL => Capability::Burl, + CHECKPOINT => Capability::Checkpoint, + CHUNKING => Capability::Chunking, + CONNEG => Capability::Conneg, + CONPERM => Capability::Conperm, + DELIVERBY => Capability::DeliverBy { + min: if parser.stop_char != LF { + let db = parser.size()?; + if db != usize::MAX { + db as u64 + } else { + 0 + } + } else { + 0 + }, + }, + DSN => Capability::Dsn, + ENHANCEDSTATUSCO + if parser.stop_char.to_ascii_uppercase() == b'D' + && parser.read_char()?.to_ascii_uppercase() == b'E' + && parser.read_char()?.to_ascii_uppercase() == b'S' => + { + Capability::EnhancedStatusCodes + } + ETRN => Capability::Etrn, + EXPN => Capability::Expn, + FUTURERELEASE => { + let max_interval = if parser.stop_char != LF { + parser.size()? + } else { + 0 + }; + let max_datetime = if parser.stop_char != LF { + parser.size()? + } else { + 0 + }; + + Capability::FutureRelease { + max_interval: if max_interval != usize::MAX { + max_interval as u64 + } else { + 0 + }, + max_datetime: if max_datetime != usize::MAX { + max_datetime as u64 + } else { + 0 + }, + } + } + HELP => Capability::Help, + MT_PRIORITY => Capability::MtPriority { + priority: if parser.stop_char != LF { + match parser.hashed_value_long()? { + MIXER => MtPriority::Mixer, + STANAG4406 => MtPriority::Stanag4406, + NSEP => MtPriority::Nsep, + _ => MtPriority::Mixer, + } + } else { + MtPriority::Mixer + }, + }, + MTRK => Capability::Mtrk, + NO_SOLICITING => Capability::NoSoliciting { + keywords: if parser.stop_char != LF { + let text = parser.text()?; + if !text.is_empty() { + text.into() + } else { + None + } + } else { + None + }, + }, + ONEX => Capability::Onex, + PIPELINING => Capability::Pipelining, + REQUIRETLS => Capability::RequireTls, + RRVS => Capability::Rrvs, + SIZE => Capability::Size { + size: if parser.stop_char != LF { + let size = parser.size()?; + if size != usize::MAX { + size + } else { + 0 + } + } else { + 0 + }, + }, + SMTPUTF8 => Capability::SmtpUtf8, + STARTTLS => Capability::StartTls, + VERB => Capability::Verb, + _ => { + parser.seek_lf()?; + continue; + } + }); + parser.seek_lf()?; + } else { + let is_hostname = code == 250; + if is_first_line { + is_first_line = false; + } else if !buf.is_empty() && !matches!(buf.last(), Some(b' ')) { + buf.push(b' '); + } + + loop { + match parser.read_char()? { + b'\n' => break, + b'\r' => (), + b' ' if is_hostname => { + parser.seek_lf()?; + break; + } + ch => { + buf.push(ch); + } + } + } + + if is_hostname { + response.hostname = buf.into_string(); + buf = Vec::new(); + } + } + } + + if code == 250 { + Ok(response) + } else { + Err(Error::InvalidResponse { + response: Response { + code, + esc: [0, 0, 0], + message: buf.into_string(), + }, + }) + } + } +} + +impl Response { + pub fn parse(bytes: &mut Iter<'_, u8>, has_esc: bool) -> Result { + let mut parser = Rfc5321Parser::new(bytes); + let mut code = 0; + let mut message = Vec::with_capacity(32); + let mut esc = [0u8; 3]; + let mut eol = false; + + 'outer: while !eol { + code = match parser.size()? { + val @ 100..=999 => val as u16, + _ => 0, + }; + match parser.stop_char { + b' ' => { + eol = true; + } + b'-' => (), + b'\n' if code < 600 => { + break; + } + _ => { + return Err(Error::SyntaxError { + syntax: "unexpected token", + }); + } + } + + let mut esc_parse_error = 0; + if has_esc { + if esc[0] == 0 { + for (pos, esc) in esc.iter_mut().enumerate() { + let val = parser.size()?; + *esc = if val < 100 { val as u8 } else { 0 }; + if pos < 2 && parser.stop_char != b'.' { + esc_parse_error = parser.stop_char; + break; + } + } + if parser.stop_char == LF { + continue; + } + } else { + loop { + match parser.read_char()? { + b'0'..=b'9' | b'.' => (), + b'\n' => continue 'outer, + _ => break, + } + } + } + } + + if !message.is_empty() && !matches!(message.last(), Some(b' ')) { + message.push(b' '); + } + if esc_parse_error != 0 { + message.push(esc_parse_error); + } + + loop { + match parser.read_char()? { + b'\n' => break, + b'\r' => (), + ch => { + message.push(ch); + } + } + } + } + + Ok(Response { + code, + esc, + message: message.into_string(), + }) + } +} + +impl Capability { + pub fn parse(value: &[u8]) -> Option { + if value.eq_ignore_ascii_case(b"8BITMIME") { + Capability::EightBitMime.into() + } else if value.eq_ignore_ascii_case(b"ATRN") { + Capability::Atrn.into() + } else if value.eq_ignore_ascii_case(b"AUTH") { + Capability::Auth { + mechanisms: Vec::new(), + } + .into() + } else if value.eq_ignore_ascii_case(b"BINARYMIME") { + Capability::BinaryMime.into() + } else if value.eq_ignore_ascii_case(b"BURL") { + Capability::Burl.into() + } else if value.eq_ignore_ascii_case(b"CHECKPOINT") { + Capability::Checkpoint.into() + } else if value.eq_ignore_ascii_case(b"CHUNKING") { + Capability::Chunking.into() + } else if value.eq_ignore_ascii_case(b"CONNEG") { + Capability::Conneg.into() + } else if value.eq_ignore_ascii_case(b"CONPERM") { + Capability::Conperm.into() + } else if value.eq_ignore_ascii_case(b"DELIVERBY") { + Capability::DeliverBy { min: 0 }.into() + } else if value.eq_ignore_ascii_case(b"DSN") { + Capability::Dsn.into() + } else if value.eq_ignore_ascii_case(b"ENHANCEDSTATUSCODES") { + Capability::EnhancedStatusCodes.into() + } else if value.eq_ignore_ascii_case(b"ETRN") { + Capability::Etrn.into() + } else if value.eq_ignore_ascii_case(b"EXPN") { + Capability::Expn.into() + } else if value.eq_ignore_ascii_case(b"FUTURERELEASE") { + Capability::FutureRelease { + max_interval: 0, + max_datetime: 0, + } + .into() + } else if value.eq_ignore_ascii_case(b"HELP") { + Capability::Help.into() + } else if value.eq_ignore_ascii_case(b"MT-PRIORITY") { + Capability::MtPriority { + priority: MtPriority::Mixer, + } + .into() + } else if value.eq_ignore_ascii_case(b"MTRK") { + Capability::Mtrk.into() + } else if value.eq_ignore_ascii_case(b"NO-SOLICITING") { + Capability::NoSoliciting { keywords: None }.into() + } else if value.eq_ignore_ascii_case(b"ONEX") { + Capability::Onex.into() + } else if value.eq_ignore_ascii_case(b"PIPELINING") { + Capability::Pipelining.into() + } else if value.eq_ignore_ascii_case(b"REQUIRETLS") { + Capability::RequireTls.into() + } else if value.eq_ignore_ascii_case(b"RRVS") { + Capability::Rrvs.into() + } else if value.eq_ignore_ascii_case(b"SIZE") { + Capability::Size { size: 0 }.into() + } else if value.eq_ignore_ascii_case(b"SMTPUTF8") { + Capability::SmtpUtf8.into() + } else if value.eq_ignore_ascii_case(b"STARTTLS") { + Capability::StartTls.into() + } else if value.eq_ignore_ascii_case(b"VERB") { + Capability::Verb.into() + } else { + None + } + } +} + +#[cfg(test)] +mod tests { + use crate::{Capability, EhloResponse, Error, Mechanism, MtPriority, Response}; + + #[test] + fn parse_ehlo() { + for item in [ + ( + concat!( + "250-dbc.mtview.ca.us says hello\n", + "250-8BITMIME\n", + "250-ATRN\n", + "250-AUTH GSSAPI DIGEST-MD5 PLAIN\n", + "250-BINARYMIME\n", + "250-BURL imap\n", + "250-CHECKPOINT\n", + "250-CHUNKING\n", + "250-CONNEG\n", + "250-CONPERM\n", + "250-DELIVERBY\n", + "250-DELIVERBY 240\n", + "250-DSN\n", + "250-ENHANCEDSTATUSCODES\n", + "250-ETRN\n", + "250-EXPN\n", + "250-FUTURERELEASE 1234 5678\n", + "250-FUTURERELEASE 123\n", + "250-FUTURERELEASE\n", + "250-HELP\n", + "250-MT-PRIORITY\n", + "250-MT-PRIORITY MIXER\n", + "250-MT-PRIORITY STANAG4406\n", + "250-MTRK\n", + "250-NO-SOLICITING net.example:ADV\n", + "250-NO-SOLICITING\n", + "250-PIPELINING\n", + "250-REQUIRETLS\n", + "250-RRVS\n", + "250-SIZE 1000000\n", + "250-SIZE\n", + "250-SMTPUTF8 ignore\n", + "250-SMTPUTF8\n", + "250 STARTTLS\n", + ), + Ok(EhloResponse { + hostname: "dbc.mtview.ca.us".to_string(), + capabilities: vec![ + Capability::EightBitMime, + Capability::Atrn, + Capability::Auth { + mechanisms: vec![ + Mechanism::Gssapi, + Mechanism::DigestMd5, + Mechanism::Plain, + ], + }, + Capability::BinaryMime, + Capability::Burl, + Capability::Checkpoint, + Capability::Chunking, + Capability::Conneg, + Capability::Conperm, + Capability::DeliverBy { min: 0 }, + Capability::DeliverBy { min: 240 }, + Capability::Dsn, + Capability::EnhancedStatusCodes, + Capability::Etrn, + Capability::Expn, + Capability::FutureRelease { + max_interval: 1234, + max_datetime: 5678, + }, + Capability::FutureRelease { + max_interval: 123, + max_datetime: 0, + }, + Capability::FutureRelease { + max_interval: 0, + max_datetime: 0, + }, + Capability::Help, + Capability::MtPriority { + priority: MtPriority::Mixer, + }, + Capability::MtPriority { + priority: MtPriority::Mixer, + }, + Capability::MtPriority { + priority: MtPriority::Stanag4406, + }, + Capability::Mtrk, + Capability::NoSoliciting { + keywords: Some("net.example:ADV".to_string()), + }, + Capability::NoSoliciting { keywords: None }, + Capability::Pipelining, + Capability::RequireTls, + Capability::Rrvs, + Capability::Size { size: 1000000 }, + Capability::Size { size: 0 }, + Capability::SmtpUtf8, + Capability::SmtpUtf8, + Capability::StartTls, + ], + }), + ), + ( + concat!("523-Massive\n", "523-Error\n", "523 Message\n"), + Err(Error::InvalidResponse { + response: Response { + code: 523, + esc: [0, 0, 0], + message: "Massive Error Message".to_string(), + }, + }), + ), + ] { + let (response, parsed_response): (&str, Result) = item; + + for replacement in ["", "\r\n", " \n", " \r\n"] { + let response = if !replacement.is_empty() { + response.replace('\n', replacement) + } else { + response.to_string() + }; + assert_eq!( + parsed_response, + EhloResponse::parse(&mut response.as_bytes().iter()).map_err(|err| match err { + Error::InvalidResponse { response } => Error::InvalidResponse { + response: Response { + code: response.code, + esc: response.esc, + message: response.message.trim_end().to_string() + } + }, + err => err, + }), + "failed for {:?}", + response + ); + } + } + } + + #[test] + fn parse_response() { + for (response, parsed_response, has_esc) in [ + ( + "250 2.1.1 Originator ok\n", + Response { + code: 250, + esc: [2, 1, 1], + message: "Originator ok".to_string(), + }, + true, + ), + ( + concat!( + "551-5.7.1 Forwarding to remote hosts disabled\n", + "551 5.7.1 Select another host to act as your forwarder\n" + ), + Response { + code: 551, + esc: [5, 7, 1], + message: concat!( + "Forwarding to remote hosts disabled ", + "Select another host to act as your forwarder" + ) + .to_string(), + }, + true, + ), + ( + concat!( + "550-mailbox unavailable\n", + "550 user has moved with no forwarding address\n" + ), + Response { + code: 550, + esc: [0, 0, 0], + message: "mailbox unavailable user has moved with no forwarding address" + .to_string(), + }, + false, + ), + ( + concat!( + "550-mailbox unavailable\n", + "550 user has moved with no forwarding address\n" + ), + Response { + code: 550, + esc: [0, 0, 0], + message: "mailbox unavailable user has moved with no forwarding address" + .to_string(), + }, + true, + ), + ( + concat!( + "432-6.8.9\n", + "432-6.8.9 Hello\n", + "432-6.8.9 \n", + "432-6.8.9 ,\n", + "432-\n", + "432-6\n", + "432-6.\n", + "432-6.8\n", + "432-6.8.9\n", + "432 6.8.9 World!\n" + ), + Response { + code: 432, + esc: [6, 8, 9], + message: "Hello , World!".to_string(), + }, + true, + ), + ( + concat!("250-Missing space\n", "250\n", "250 Ignore this"), + Response { + code: 250, + esc: [0, 0, 0], + message: "Missing space".to_string(), + }, + true, + ), + ] { + assert_eq!( + parsed_response, + Response::parse(&mut response.as_bytes().iter(), has_esc).unwrap(), + "failed for {:?}", + response + ); + } + } +}