code wiki / _hdl_build / nx_email_mime_gate.nx
nx_email_mime_gate.nx source
↩ module page · 191 lines · 9190 B
1// nx_email_mime_gate.nx -- GATE for EMAIL R2 (RFC 5322 + MIME, nx_email_mime).
2//
3// COMPLETENESS : header/body split; case-insensitive header find;
4// folded-header unfolding; header append; QP encode/
5// decode round-trip over arbitrary bytes incl 0x00 and
6// 0xFF; base64 76-col wrap round-trip (composing the
7// canonical nx_base64); multipart assemble + extract.
8// NEG-CONTROL : absent header -> NOT_FOUND; malformed QP "=ZZ" ->
9// BAD_QP; invalid base64 char -> -1.
10// TAMPER : header find is PREFIX-EXACT -- "Subject" returns the
11// real "Subject:" value, never the earlier "X-Subject:"
12// trap (header-confusion / injection defense); a "=CRLF"
13// soft line break decodes away (no spurious bytes).
14//
15// Evidence -> knowledge/status/email_mime.log
16// (MIMEGATE authored=organ ... verdict=GREEN)
17// license_tier: ORIGINAL
18import "nx_email_mime.nx"
19import "nx_syscalls.nx"
20import "nx_gate_verdict.nx"
21
22const MIME_LOG: *u8 = "knowledge/status/email_mime.log"
23
24func 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 }
25func ewn(fd: i64, v: i64) -> i64 {
26 let bb: *u8 = sys_mmap(28); var m: i64 = v
27 if m < 0 { m = 0 - m; sys_write(fd, "-" as *u8, 1) }
28 let t: *u8 = sys_mmap(28); var k: i64 = 0
29 if m == 0 { t[0] = 48; k = 1 }
30 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
31 var i: i64 = 0; while i < k { bb[i] = t[k - 1 - i]; i = i + 1 }
32 sys_write(fd, bb, k); return 0
33}
34func epf(fd: i64, label: *u8, pass: i64) -> i64 {
35 ew(fd, label); if pass == 1 { ew(fd, "PASS" as *u8) } else { ew(fd, "FAIL" as *u8) } return 0
36}
37func slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
38func 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 }
39func max_line(buf: *u8, n: i64) -> i64 {
40 var mx: i64 = 0; var cur: i64 = 0; var i: i64 = 0
41 while i < n {
42 if (buf[i] & 0xff) == 10 { cur = 0 } else { if (buf[i] & 0xff) != 13 { cur = cur + 1; if cur > mx { mx = cur } } }
43 i = i + 1
44 }
45 return mx
46}
47func has_crlf(buf: *u8, n: i64) -> i64 {
48 var i: i64 = 0
49 while i + 1 < n { if (buf[i] & 0xff) == 13 && (buf[i + 1] & 0xff) == 10 { return 1 } i = i + 1 }
50 return 0
51}
52
53func main() -> i64 {
54 var ok: i64 = 1
55 let bo: *i64 = sys_mmap(8) as *i64
56 let val: *u8 = sys_mmap(512)
57 let out: *u8 = sys_mmap(2048)
58 let out2: *u8 = sys_mmap(2048)
59 let scratch: *u8 = sys_mmap(2048)
60
61 // ---- headers ----
62 let msg: *u8 = "From: a@x.test\r\nSubject: Hello There\r\nTo: b@y.test\r\n\r\nThis is the body.\r\n" as *u8
63 let mlen: i64 = slen(msg)
64 let hlen: i64 = nx_mime_split_body(msg, mlen, bo)
65 var split_ok: i64 = 0
66 if hlen > 0 {
67 let bodyp: *u8 = msg + *bo
68 let blen: i64 = mlen - *bo
69 if blen == slen("This is the body.\r\n" as *u8) && memeq(bodyp, "This is the body.\r\n" as *u8, blen) == 1 { split_ok = 1 }
70 }
71 if split_ok != 1 { ok = 0 }
72
73 let vs1: i64 = nx_mime_header_find(msg, hlen, "subject" as *u8, 7, val, 512)
74 var hdr_subject: i64 = 0
75 if vs1 >= 0 && memeq(val, "Hello There" as *u8, slen("Hello There" as *u8)) == 1 && vs1 == slen("Hello There" as *u8) { hdr_subject = 1 } else { ok = 0 }
76
77 let vf1: i64 = nx_mime_header_find(msg, hlen, "From" as *u8, 4, val, 512)
78 var hdr_from: i64 = 0
79 if vf1 >= 0 && memeq(val, "a@x.test" as *u8, slen("a@x.test" as *u8)) == 1 { hdr_from = 1 } else { ok = 0 }
80
81 let vabs: i64 = nx_mime_header_find(msg, hlen, "Bcc" as *u8, 3, val, 512)
82 var hdr_absent: i64 = 0
83 if vabs == (0 - NX_MIME_NOT_FOUND) { hdr_absent = 1 } else { ok = 0 }
84
85 // folded
86 let msgf: *u8 = "Subject: part one\r\n\tpart two\r\n\r\nbody" as *u8
87 let hlenf: i64 = nx_mime_split_body(msgf, slen(msgf), bo)
88 let vff: i64 = nx_mime_header_find(msgf, hlenf, "Subject" as *u8, 7, val, 512)
89 var hdr_folded: i64 = 0
90 if vff >= 0 && memeq(val, "part one part two" as *u8, slen("part one part two" as *u8)) == 1 && vff == slen("part one part two" as *u8) { hdr_folded = 1 } else { ok = 0 }
91
92 // prefix-exact tamper
93 let msgt: *u8 = "X-Subject: TRAP\r\nSubject: REAL\r\n\r\nb" as *u8
94 let hlent: i64 = nx_mime_split_body(msgt, slen(msgt), bo)
95 let vtt: i64 = nx_mime_header_find(msgt, hlent, "Subject" as *u8, 7, val, 512)
96 var hdr_prefix: i64 = 0
97 if vtt >= 0 && memeq(val, "REAL" as *u8, 4) == 1 && vtt == 4 { hdr_prefix = 1 } else { ok = 0 }
98
99 // header append
100 let ai: i64 = nx_mime_header_append(out, 0, "X-Mailer" as *u8, "nishi" as *u8)
101 var hdr_append: i64 = 0
102 if ai == slen("X-Mailer: nishi\r\n" as *u8) && memeq(out, "X-Mailer: nishi\r\n" as *u8, ai) == 1 { hdr_append = 1 } else { ok = 0 }
103
104 // ---- quoted-printable ----
105 let qin: *u8 = sys_mmap(16)
106 qin[0] = 72 as u8; qin[1] = 105 as u8; qin[2] = 61 as u8; qin[3] = 0 as u8
107 qin[4] = 255 as u8; qin[5] = 32 as u8; qin[6] = 9 as u8; qin[7] = 87 as u8 // H i = \0 \xFF SP TAB W
108 let qenc: i64 = nx_mime_qp_encode(qin, 8, out, 2048)
109 let qdec: i64 = nx_mime_qp_decode(out, qenc, out2, 2048)
110 var qp_rt: i64 = 0
111 if qenc > 8 && qdec == 8 && memeq(out2, qin, 8) == 1 { qp_rt = 1 } else { ok = 0 }
112
113 let qkd: i64 = nx_mime_qp_decode("Hello=20World=21" as *u8, slen("Hello=20World=21" as *u8), out, 2048)
114 var qp_known: i64 = 0
115 if qkd == 12 && memeq(out, "Hello World!" as *u8, 12) == 1 { qp_known = 1 } else { ok = 0 }
116
117 let qsb: i64 = nx_mime_qp_decode("AB=\r\nCD" as *u8, slen("AB=\r\nCD" as *u8), out, 2048)
118 var qp_soft: i64 = 0
119 if qsb == 4 && memeq(out, "ABCD" as *u8, 4) == 1 { qp_soft = 1 } else { ok = 0 }
120
121 let qbad: i64 = nx_mime_qp_decode("=ZZ" as *u8, 3, out, 2048)
122 var qp_bad: i64 = 0
123 if qbad == (0 - NX_MIME_BAD_QP) { qp_bad = 1 } else { ok = 0 }
124
125 // ---- base64 wrap round-trip ----
126 let bin: *u8 = sys_mmap(128)
127 var z: i64 = 0
128 while z < 60 { bin[z] = ((z * 7) & 0xff) as u8; z = z + 1 }
129 let benc: i64 = nx_mime_b64_encode_wrap(bin, 60, out, 2048)
130 let bdec: i64 = nx_mime_b64_decode_ws(out, benc, scratch, out2)
131 var b64_rt: i64 = 0
132 if bdec == 60 && memeq(out2, bin, 60) == 1 { b64_rt = 1 } else { ok = 0 }
133 let b64_wrapped: i64 = has_crlf(out, benc)
134 let b64_maxline: i64 = max_line(out, benc)
135 if b64_wrapped != 1 { ok = 0 }
136 if b64_maxline > 76 { ok = 0 }
137
138 let bbad: i64 = nx_mime_b64_decode_ws("@@@@" as *u8, 4, scratch, out2)
139 var b64_bad: i64 = 0
140 if bbad < 0 { b64_bad = 1 } else { ok = 0 }
141
142 // ---- multipart assemble + extract ----
143 let mp: i64 = nx_mime_multipart_assemble(
144 "NXBOUND" as *u8, 7,
145 "text/plain" as *u8, 10, "PART-ONE" as *u8, 8,
146 "application/octet-stream" as *u8, 24, "PART-TWO" as *u8, 8,
147 out, 2048)
148 let pl0: i64 = nx_mime_multipart_part(out, mp, "NXBOUND" as *u8, 7, 0, out2, 2048)
149 var mp_p0: i64 = 0
150 if pl0 == 8 && memeq(out2, "PART-ONE" as *u8, 8) == 1 { mp_p0 = 1 } else { ok = 0 }
151 let pl1: i64 = nx_mime_multipart_part(out, mp, "NXBOUND" as *u8, 7, 1, out2, 2048)
152 var mp_p1: i64 = 0
153 if pl1 == 8 && memeq(out2, "PART-TWO" as *u8, 8) == 1 { mp_p1 = 1 } else { ok = 0 }
154
155 var fd: i64 = 1
156 while fd >= 1 {
157 ew(fd, "MIMEGATE authored=organ rfc=5322+2045+2046 " as *u8)
158 epf(fd, "split_body=" as *u8, split_ok)
159 epf(fd, " hdr_subject=" as *u8, hdr_subject)
160 epf(fd, " hdr_from=" as *u8, hdr_from)
161 epf(fd, " hdr_folded=" as *u8, hdr_folded)
162 epf(fd, " hdr_prefix_exact=" as *u8, hdr_prefix)
163 epf(fd, " hdr_absent_negctrl=" as *u8, hdr_absent)
164 epf(fd, " hdr_append=" as *u8, hdr_append)
165 epf(fd, " qp_roundtrip=" as *u8, qp_rt)
166 epf(fd, " qp_known=" as *u8, qp_known)
167 epf(fd, " qp_softbreak=" as *u8, qp_soft)
168 epf(fd, " qp_bad_negctrl=" as *u8, qp_bad)
169 epf(fd, " b64_roundtrip=" as *u8, b64_rt)
170 ew(fd, " b64_wrapped=" as *u8); ewn(fd, b64_wrapped)
171 ew(fd, " b64_maxline=" as *u8); ewn(fd, b64_maxline)
172 epf(fd, " b64_bad_negctrl=" as *u8, b64_bad)
173 epf(fd, " multipart_p0=" as *u8, mp_p0)
174 epf(fd, " multipart_p1=" as *u8, mp_p1)
175 if ok == 1 { ew(fd, " verdict=GREEN\n" as *u8) } else { ew(fd, " verdict=RED\n" as *u8) }
176 if fd == 1 {
177 let lf: i64 = sys_openat_append(MIME_LOG, 420)
178 if lf >= 1 { fd = lf } else { fd = 0 }
179 } else { sys_close(fd); fd = 0 }
180 }
181
182 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check
183 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled
184 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify.
185 let ctr__dry: *i64 = gv_ctr()
186 ctr__dry[0] = ok
187 ctr__dry[1] = 1
188 let rc__dry: i64 = gv_verdict("EMAIL-MIME-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8)
189 sys_exit(rc__dry)
190 return rc__dry
191}