nx_smtp_client.nx
buildroot/runtime/nx_smtp_client.nx
about
nx_smtp_client.nx -- EMAIL RUNG R1: SMTP client wire protocol (RFC 5321).
module: nishi-core.email.smtp_client
depends: (none -- pure RFC 5321 line codec + state machine)
capability: CORE_EMAIL
The conversation grammar a client speaks to a mail server, ABOVE the
MX host R0 discovered and the nx_socket TCP rung. Three pure parts,
no syscalls (live socket I/O composes in a later nx_smtp_client_io.nx,
mirroring the nx_dns / nx_dns_io split) so the whole protocol is
deterministically gateable offline against a scripted transcript:
1. REPLY PARSER -- 3-digit reply codes with multiline continuation
(RFC 5321 §4.2.1: "250-" continues, "250 " is final). Detects a
complete vs still-streaming reply, enforces the code matches
across all continuation lines (a mismatch is a malformed/forged
reply), rejects non-digit codes and illegal separators.
2. COMMAND BUILDERS -- EHLO / MAIL FROM / RCPT TO / DATA / QUIT etc.
with correct CRLF framing and <addr> bracketing.
3. DOT-STUFFING (RFC 5321 §4.5.2) -- the correctness/SECURITY crux:
a body line beginning with "." gets an extra "." prepended so a
lone "." in the message can never be mistaken for the
"<CRLF>.<CRLF>" end-of-DATA terminator (premature-termination /
SMTP-injection defense).
4. CLIENT STATE MACHINE -- the deterministic send sequence
INIT(220) -> EHLO(2xx) -> MAIL(2xx) -> RCPT(2xx) -> DATA(354) ->
BODY(2xx) -> QUIT(221) -> DONE, with any unexpected code routing
to ABORT/FAIL (the conversation then closes with QUIT).
license_tier: INDEPENDENT_REDERIVE
genealogy_id: international-research-sources/ietf/rfc_5321
lineage_id: nishi_email_smtp_client_r1
nx_safety_envelope:
intended_use: "SMTP client reply parse + command build +
dot-stuffing + send-sequence state machine.
RFC 5321 substrate."
dependencies 0 imports · 2 importers
imports: none
imported by: nx_email_live_gate.nxnx_smtp_client_gate.nx
structs
| none |
consts
| 53 | const NX_SMTP_BAD_CODE: i64 = 2 // reply code not 3 digits |
| 54 | const NX_SMTP_CODE_MISMATCH: i64 = 3 // continuation line code != first line |
| 55 | const NX_SMTP_BAD_LINE: i64 = 4 // illegal separator after code |
| 56 | const NX_SMTP_BUF_FULL: i64 = 5 // builder ran out of caller buffer |
| 57 | const NX_SMTP_N: i64 = 6 |
| 60 | const SMTP_S_INIT: i64 = 0 // awaiting 220 greeting |
| 61 | const SMTP_S_EHLO: i64 = 1 // EHLO sent, awaiting 250 |
| 62 | const SMTP_S_MAIL: i64 = 2 // MAIL FROM sent |
| 63 | const SMTP_S_RCPT: i64 = 3 // RCPT TO sent |
| 64 | const SMTP_S_DATA: i64 = 4 // DATA sent, awaiting 354 |
| 65 | const SMTP_S_BODY: i64 = 5 // message body sent, awaiting 250 |
| 66 | const SMTP_S_QUIT: i64 = 6 // QUIT sent, awaiting 221 |
| 67 | const SMTP_S_DONE: i64 = 7 // conversation complete |
| 68 | const SMTP_S_FAIL: i64 = 8 // aborted |
| 71 | const SMTP_A_NONE: i64 = 0 |
| 72 | const SMTP_A_SEND_EHLO: i64 = 1 |
| 73 | const SMTP_A_SEND_MAIL: i64 = 2 |
| 74 | const SMTP_A_SEND_RCPT: i64 = 3 |
| 75 | const SMTP_A_SEND_DATA: i64 = 4 |
| 76 | const SMTP_A_SEND_BODY: i64 = 5 |
| 77 | const SMTP_A_SEND_QUIT: i64 = 6 |
| 78 | const SMTP_A_DONE: i64 = 7 |
| 79 | const SMTP_A_ABORT: i64 = 8 |
| 81 | const SMTP_MAX_REPLY_LINES: i64 = 64 |
functions
| 83 | func nx_smtp_action_name(a: i64) -> *u8 called by 1: main |
| 96 | func smtp_is_digit(c: i64) -> i64 called by 1: nx_smtp_parse_reply |
| 101 | func smtp_is_2xx(code: i64) -> i64 called by 1: nx_smtp_advance |
| 114 | func nx_smtp_parse_reply(buf: *u8, n: i64, out_complete: *i64, out_consumed: *i64) -> i64 |
| 166 | func first_code_or_badline(first_code: i64) -> i64 called by 1: nx_smtp_parse_reply |
| 173 | func sm_cat(out: *u8, oi: i64, s: *u8) -> i64 |
| 178 | func sm_catn(out: *u8, oi: i64, s: *u8, len: i64) -> i64 |
| 184 | func nx_smtp_build_ehlo(out: *u8, cap: i64, domain: *u8, dlen: i64) -> i64 |
| 193 | func nx_smtp_build_mail_from(out: *u8, cap: i64, addr: *u8, alen: i64) -> i64 |
| 202 | func nx_smtp_build_rcpt_to(out: *u8, cap: i64, addr: *u8, alen: i64) -> i64 |
| 211 | func nx_smtp_build_simple(out: *u8, cap: i64, verb: *u8) -> i64 |
| 224 | func nx_smtp_dot_stuff(msg: *u8, mlen: i64, out: *u8, cap: i64) -> i64 |
| 254 | func nx_smtp_advance(state: i64, code: i64, out_action: *i64) -> i64 |