code wiki / _hdl_build / nx_email_mta_gate.nx
nx_email_mta_gate.nx source
↩ module page · 148 lines · 6977 B
1// nx_email_mta_gate.nx -- GATE for EMAIL R7 (receiving MTA, nx_email_mta).
2//
3// The INTEGRATION CAPSTONE: a full inbound SMTP session composed across
4// rungs -- R7 command parse + policy + FSM, R4 (nx_pop3_dedot) to unstuff
5// the DATA stream, R3 (nx_email_mailbox over nx_seg_store) to DELIVER,
6// then fetch the stored message back BYTE-IDENTICAL = receive->store
7// proven end to end.
8//
9// COMPLETENESS : EHLO/MAIL/RCPT/DATA/QUIT classified + correct reply
10// codes (250/250/250/354/221); DATA dot-unstuffed;
11// delivered + fetched byte-exact.
12// POLICY/SEC : RCPT for our domain accepted; RCPT to a foreign domain
13// REFUSED with 550 (open-relay defense).
14// NEG-CONTROL : a command out of sequence (RCPT before MAIL) -> 503.
15//
16// Evidence -> knowledge/status/email_mta.log (MTAGATE ... verdict=GREEN)
17// license_tier: ORIGINAL
18import "nx_email_mta.nx"
19import "nx_email_retrieve.nx"
20import "nx_email_mailbox.nx"
21import "nx_syscalls.nx"
22
23const MTA_LOG: *u8 = "knowledge/status/email_mta.log"
24const MTA_PREFIX: *u8 = "knowledge/status/mta_test/"
25const OUR_DOMAIN: *u8 = "nishi.test"
26
27func ew(fd: i64, s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(fd, s, n); return 0 }
28func ewn(fd: i64, v: i64) -> i64 {
29 let b: *u8 = sys_mmap(28); var m: i64 = v
30 if m < 0 { m = 0 - m; sys_write(fd, "-" as *u8, 1) }
31 let t: *u8 = sys_mmap(28); var k: i64 = 0
32 if m == 0 { t[0] = 48; k = 1 }
33 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
34 var i: i64 = 0; while i < k { b[i] = t[k - 1 - i]; i = i + 1 }
35 sys_write(fd, b, k); return 0
36}
37func epf(fd: i64, label: *u8, pass: i64) -> i64 { ew(fd, label); if pass == 1 { ew(fd, "PASS" as *u8) } else { ew(fd, "FAIL" as *u8) } return 0 }
38func slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
39func memeq(a: *u8, b: *u8, len: i64) -> i64 { var i: i64 = 0; while i < len { if a[i] != b[i] { return 0 } i = i + 1 } return 1 }
40
41func main() -> i64 {
42 var ok: i64 = 1
43 // reset store dir
44 sys_mkdir("knowledge/status/mta_test" as *u8, 511)
45 let rfd: i64 = sys_openat_wr("knowledge/status/mta_test/manifest.txt" as *u8, 420)
46 if rfd >= 0 { sys_close(rfd) }
47
48 let nxt: *i64 = sys_mmap(8) as *i64
49 let addr: *u8 = sys_mmap(256)
50 let dedot: *u8 = sys_mmap(4096)
51 let pp: *i64 = sys_mmap(8) as *i64
52 let ll: *i64 = sys_mmap(8) as *i64
53 var st: i64 = MTA_S_GREET
54
55 // ---- command classification ----
56 var cmd_parse: i64 = 1
57 if nx_mta_parse_cmd("EHLO relay.remote\r\n" as *u8, slen("EHLO relay.remote\r\n" as *u8)) != MTA_C_EHLO { cmd_parse = 0 }
58 if nx_mta_parse_cmd("mail from:<a@b>\r\n" as *u8, slen("mail from:<a@b>\r\n" as *u8)) != MTA_C_MAIL { cmd_parse = 0 }
59 if nx_mta_parse_cmd("RCPT TO:<x@y>\r\n" as *u8, slen("RCPT TO:<x@y>\r\n" as *u8)) != MTA_C_RCPT { cmd_parse = 0 }
60 if nx_mta_parse_cmd("DATA\r\n" as *u8, slen("DATA\r\n" as *u8)) != MTA_C_DATA { cmd_parse = 0 }
61 if nx_mta_parse_cmd("QUIT\r\n" as *u8, slen("QUIT\r\n" as *u8)) != MTA_C_QUIT { cmd_parse = 0 }
62 if cmd_parse != 1 { ok = 0 }
63
64 // ---- EHLO ----
65 let r_ehlo: i64 = nx_mta_reply_for(st, MTA_C_EHLO, 0, nxt); st = *nxt
66 // ---- MAIL FROM ----
67 let r_mail: i64 = nx_mta_reply_for(st, MTA_C_MAIL, 0, nxt); st = *nxt
68
69 // ---- RCPT (our domain, accept) ----
70 let rcpt1: *u8 = "RCPT TO:<bob@nishi.test>\r\n" as *u8
71 nx_mta_extract_addr(rcpt1, slen(rcpt1), addr, 256)
72 let acc1: i64 = nx_mta_rcpt_accept(addr, slen(addr), OUR_DOMAIN, slen(OUR_DOMAIN))
73 let r_rcpt: i64 = nx_mta_reply_for(st, MTA_C_RCPT, acc1, nxt); st = *nxt
74
75 // ---- RCPT (foreign domain, refuse relay) ----
76 let rcpt2: *u8 = "RCPT TO:<eve@evil.test>\r\n" as *u8
77 nx_mta_extract_addr(rcpt2, slen(rcpt2), addr, 256)
78 let acc2: i64 = nx_mta_rcpt_accept(addr, slen(addr), OUR_DOMAIN, slen(OUR_DOMAIN))
79 let r_relay: i64 = nx_mta_reply_for(st, MTA_C_RCPT, acc2, nxt); st = *nxt
80
81 // ---- DATA ----
82 let r_data: i64 = nx_mta_reply_for(st, MTA_C_DATA, 0, nxt); st = *nxt
83
84 // ---- DATA body: dot-unstuff (R4) then deliver (R3) ----
85 let raw: *u8 = "Subject: Hi Bob\r\nFrom: alice@remote.test\r\n\r\nHello Bob, this is a test.\r\n.\r\n" as *u8
86 let exp: *u8 = "Subject: Hi Bob\r\nFrom: alice@remote.test\r\n\r\nHello Bob, this is a test.\r\n" as *u8
87 let dlen: i64 = nx_pop3_dedot(raw, slen(raw), dedot, 4096)
88 var data_dedot: i64 = 0
89 if dlen == slen(exp) && memeq(dedot, exp, dlen) == 1 { data_dedot = 1 } else { ok = 0 }
90 let stored: i64 = nx_mbox_deliver(MTA_PREFIX, "bob@nishi.test" as *u8, dedot, dlen, 1)
91 var stored_ok: i64 = 0
92 if stored == 0 { stored_ok = 1 }
93 let r_delivered: i64 = nx_mta_data_delivered(stored_ok, nxt); st = *nxt
94
95 // ---- QUIT ----
96 let r_quit: i64 = nx_mta_reply_for(st, MTA_C_QUIT, 0, nxt); st = *nxt
97
98 // ---- end-to-end: fetch the stored message back byte-exact ----
99 var e2e: i64 = 0
100 if nx_mbox_fetch(MTA_PREFIX, "bob@nishi.test" as *u8, pp, ll) == 1 {
101 if ll[0] == dlen && memeq(pp[0] as *u8, exp, dlen) == 1 { e2e = 1 }
102 }
103 if e2e != 1 { ok = 0 }
104
105 // ---- NEG-CONTROL: out-of-sequence RCPT before MAIL -> 503 ----
106 let bs: i64 = nx_mta_reply_for(MTA_S_READY, MTA_C_RCPT, 1, nxt)
107 var badseq: i64 = 0
108 if bs == 503 { badseq = 1 } else { ok = 0 }
109
110 // ---- reply-code assertions ----
111 if r_ehlo != 250 { ok = 0 }
112 if r_mail != 250 { ok = 0 }
113 if r_rcpt != 250 { ok = 0 }
114 if r_relay != 550 { ok = 0 }
115 if r_data != 354 { ok = 0 }
116 if r_delivered != 250 { ok = 0 }
117 if r_quit != 221 { ok = 0 }
118 var rcpt_accept_own: i64 = 0
119 if acc1 == 1 { rcpt_accept_own = 1 }
120 var rcpt_reject_relay: i64 = 0
121 if acc2 == 0 { rcpt_reject_relay = 1 }
122
123 var fd: i64 = 1
124 while fd >= 1 {
125 ew(fd, "MTAGATE authored=organ rfc=5321-server compose=R3+R4 " as *u8)
126 epf(fd, "cmd_parse=" as *u8, cmd_parse)
127 epf(fd, " rcpt_accept_own=" as *u8, rcpt_accept_own)
128 epf(fd, " rcpt_reject_relay=" as *u8, rcpt_reject_relay)
129 ew(fd, " reply_ehlo=" as *u8); ewn(fd, r_ehlo)
130 ew(fd, " reply_mail=" as *u8); ewn(fd, r_mail)
131 ew(fd, " reply_rcpt=" as *u8); ewn(fd, r_rcpt)
132 ew(fd, " reply_relay=" as *u8); ewn(fd, r_relay)
133 ew(fd, " reply_data=" as *u8); ewn(fd, r_data)
134 ew(fd, " reply_delivered=" as *u8); ewn(fd, r_delivered)
135 ew(fd, " reply_quit=" as *u8); ewn(fd, r_quit)
136 epf(fd, " badseq_503=" as *u8, badseq)
137 epf(fd, " data_dedot=" as *u8, data_dedot)
138 epf(fd, " e2e_fetch_byteexact=" as *u8, e2e)
139 if ok == 1 { ew(fd, " verdict=GREEN\n" as *u8) } else { ew(fd, " verdict=RED\n" as *u8) }
140 if fd == 1 {
141 let lf: i64 = sys_openat_append(MTA_LOG, 420)
142 if lf >= 1 { fd = lf } else { fd = 0 }
143 } else { sys_close(fd); fd = 0 }
144 }
145
146 if ok == 1 { return 0 }
147 return 1
148}