nx_smtp_server.nx
buildroot/runtime/nx_smtp_server.nx
about
nx_smtp_server.nx -- EMAIL RUNG R2: SMTP server receive grammar (RFC 5321 server side).
module: nishi-core.email.smtp_server
depends: (none -- pure RFC 5321 command parser + reply builders + un-dot-stuff + sequence machine)
capability: CORE_EMAIL
The MIRROR of nx_smtp_client (R1): the conversation grammar a MAIL SERVER
speaks to a connecting client, ABOVE the nx_socket TCP rung. Pure (no
syscalls -- live socket I/O composes in a later nx_smtp_server_io.nx,
mirroring the nx_smtp_client / nx_dns split) so the whole receive protocol
is deterministically gateable offline against a scripted CLIENT transcript:
1. COMMAND PARSER -- classify a client command line (EHLO/HELO/MAIL/RCPT/
DATA/QUIT/RSET/NOOP/VRFY) case-insensitively, requiring a delimiter
after the 4-char verb (so "MAILBOX" never matches "MAIL"), and extract
the <addr> span for MAIL/RCPT and the domain span for EHLO/HELO.
2. REPLY BUILDERS -- 3-digit reply + text, CRLF framed (220 greeting,
250 ok, 354 start-data, 221 bye, 500 syntax, 501 bad-args, 503 bad-
sequence, 550 no-such-user). Exact bytes for protocol conformance.
3. UN-DOT-STUFF + DATA TERMINATOR (RFC 5321 4.5.2) -- the receive crux /
SECURITY: reverse the client's leading-dot stuffing AND detect the lone
"<CRLF>.<CRLF>" end-of-DATA, such that a STUFFED ".." line can never be
mistaken for the terminator (premature-termination / truncation /
SMTP-smuggling defense -- the exact inverse of nx_smtp_dot_stuff).
4. SEQUENCE STATE MACHINE -- enforce RFC 5321 command ordering: MAIL only
from the greeted/idle state; RCPT only after MAIL; DATA only after >=1
RCPT. Any out-of-sequence verb -> 503 with the state LEFT UNCHANGED
(no envelope is ever accepted without sender+recipient). A completed
DATA body resets to the greeted state (ready for the next transaction).
license_tier: INDEPENDENT_REDERIVE
genealogy_id: international-research-sources/ietf/rfc_5321
lineage_id: nishi_email_smtp_server_r2
nx_safety_envelope:
intended_use: "SMTP server command parse + reply build + un-dot-
stuff/terminator + receive-sequence state machine.
dependencies 0 imports · 1 importers
imports: none
imported by: nx_smtp_server_gate.nx
structs
| none |
consts
| 55 | const NX_SMTPS_BAD_LINE: i64 = 2 // command line empty / no verb |
| 56 | const NX_SMTPS_BAD_ADDR: i64 = 3 // MAIL/RCPT missing <...> address |
| 57 | const NX_SMTPS_BUF_FULL: i64 = 4 // builder ran out of caller buffer |
| 58 | const NX_SMTPS_N: i64 = 5 |
| 61 | const SV_EHLO: i64 = 0 |
| 62 | const SV_HELO: i64 = 1 |
| 63 | const SV_MAIL: i64 = 2 |
| 64 | const SV_RCPT: i64 = 3 |
| 65 | const SV_DATA: i64 = 4 |
| 66 | const SV_QUIT: i64 = 5 |
| 67 | const SV_RSET: i64 = 6 |
| 68 | const SV_NOOP: i64 = 7 |
| 69 | const SV_VRFY: i64 = 8 |
| 70 | const SV_UNKNOWN: i64 = 9 |
| 73 | const SS_INIT: i64 = 0 // 220 greeting sent; awaiting EHLO/HELO |
| 74 | const SS_GREETED: i64 = 1 // EHLO ok; transaction idle; awaiting MAIL FROM |
| 75 | const SS_MAIL: i64 = 2 // MAIL FROM accepted; awaiting RCPT TO |
| 76 | const SS_RCPT: i64 = 3 // >=1 RCPT accepted; awaiting more RCPT or DATA |
| 77 | const SS_DATA: i64 = 4 // DATA accepted (354 sent); reading the body |
| 78 | const SS_QUIT: i64 = 5 // QUIT seen; 221 sent; conversation closed |
functions
| 80 | func nx_smtp_srv_verb_name(v: i64) -> *u8 |
| 94 | func ss_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } |
| 95 | func ss_cat(out: *u8, oi: i64, s: *u8) -> i64 { var k: i64 = 0; while s[k] != (0 as u8) { out[oi] = s[k]; oi = oi + 1; k = k + 1 } return oi } called by 1: nx_smtp_srv_reply |
| 96 | func ss_upper(c: i64) -> i64 { if c >= 97 { if c <= 122 { return c - 32 } } return c } called by 1: ss_kw_ci |
| 97 | func ss_is_delim(c: i64) -> i64 { if c == 32 { return 1 } if c == 13 { return 1 } if c == 10 { return 1 } return 0 } called by 1: ss_verb_is |
| 100 | func ss_kw_ci(buf: *u8, off: i64, len: i64, lit: *u8, litlen: i64) -> i64 |
| 112 | func ss_verb_is(buf: *u8, len: i64, lit: *u8) -> i64 |
| 121 | func ss_span_after_sp(buf: *u8, start: i64, len: i64, out_off: *i64, out_len: *i64) -> i64 called by 1: nx_smtp_srv_parse |
| 139 | func ss_addr_angle(buf: *u8, start: i64, len: i64, out_off: *i64, out_len: *i64) -> i64 called by 1: nx_smtp_srv_parse |
| 164 | func nx_smtp_srv_parse(buf: *u8, len: i64, out_aoff: *i64, out_alen: *i64) -> i64 |
| 181 | func nx_smtp_srv_reply(out: *u8, cap: i64, code: i64, text: *u8) -> i64 |
| 207 | func nx_smtp_srv_undot(recv: *u8, n: i64, out: *u8, cap: i64, out_complete: *i64) -> i64 called by 1: main |
| 247 | func nx_smtp_srv_advance(state: i64, verb: i64, out_code: *i64) -> i64 called by 1: main |
| 277 | func nx_smtp_srv_data_complete(out_code: *i64) -> i64 { *out_code = 250; return SS_GREETED } called by 1: main |