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}