nx_ipp_codec_test.nx source
↩ module page · 123 lines · 6445 B
1// nx_ipp_codec_test.nx -- gate for the sovereign IPP/1.1 wire codec (R0).
2//
3// Proves, with unique exit codes per invariant (no_silent_failure: you know exactly which broke):
4// 1x encode a Get-Printer-Attributes request byte-exactly per RFC 8010 (lengths re-derived
5// independently in this test, header/group/end bytes spot-checked, total length asserted)
6// 2x round-trip: parse every attribute back out by name, value bytes match
7// 3x parse a STOPPED-printer response and surface printer-state(=5) + printer-state-reasons
8// ('media-empty') -- the anti-silent-failure proof (Brother hides these; we surface them)
9// 4x defensive liar-kill: truncated header and a value-length overrun both yield NX_IPP_MALFORMED
10// 5x a missing attribute yields NX_IPP_NOT_FOUND (not a false hit, not MALFORMED)
11//
12// All inputs are constructed from the RFC byte-layout rules, independent of the encoder's own code, so
13// a bug in the encoder cannot mask a bug in the parser or vice-versa.
14//
15// expect_exit: 0
16// license_tier: ORIGINAL
17
18import "nx_syscalls.nx"
19import "nx_ipp_codec.nx"
20
21func t_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
22
23func main() -> i64 {
24 let buf: *u8 = sys_mmap(2048)
25
26 // scratch out-cells for nx_ipp_find: [0]=tag [1]=voff [2]=vlen
27 let sc: *i64 = sys_mmap(64) as *i64
28 let p_tag: *i64 = sc
29 let p_voff: *i64 = ((sc as i64) + 8) as *i64
30 let p_vlen: *i64 = ((sc as i64) + 16) as *i64
31 let outv: *i64 = sys_mmap(16) as *i64
32
33 // ============ KAT 1: encode Get-Printer-Attributes request ============
34 var o: i64 = nx_ipp_begin(buf, 1, 1, NX_IPP_OP_GET_PRINTER_ATTRIBUTES, 1)
35 if o != 8 { return 1 }
36 if (buf[0] as i64) != 1 { return 2 } // version major
37 if (buf[1] as i64) != 1 { return 3 } // version minor
38 if nx_ipp_op_id(buf) != 0x000b { return 4 } // operation-id
39 if nx_ipp_request_id(buf) != 1 { return 5 } // request-id
40
41 o = nx_ipp_group(buf, o, NX_IPP_GRP_OPERATION)
42 if (buf[8] as i64) != 0x01 { return 6 } // operation-attributes-tag
43
44 let n_chs: *u8 = "attributes-charset"
45 let n_lng: *u8 = "attributes-natural-language"
46 let n_uri: *u8 = "printer-uri"
47 let v_uri: *u8 = "ipp://h/ipp/print"
48 let n_ra: *u8 = "requested-attributes"
49 let v_ps: *u8 = "printer-state"
50
51 o = nx_ipp_attr(buf, o, NX_IPP_VT_CHARSET, n_chs, nx_ipp_strlen(n_chs), "utf-8", 5)
52 o = nx_ipp_attr(buf, o, NX_IPP_VT_NATLANG, n_lng, nx_ipp_strlen(n_lng), "en", 2)
53 o = nx_ipp_attr(buf, o, NX_IPP_VT_URI, n_uri, nx_ipp_strlen(n_uri), v_uri, nx_ipp_strlen(v_uri))
54 o = nx_ipp_attr(buf, o, NX_IPP_VT_KEYWORD, n_ra, nx_ipp_strlen(n_ra), v_ps, nx_ipp_strlen(v_ps))
55 o = nx_ipp_end(buf, o)
56
57 // independent length re-derivation (each attr = 1 + 2 + nlen + 2 + vlen):
58 // header 8 + group 1
59 // charset : 1+2+18+2+5 = 28
60 // natlang : 1+2+27+2+2 = 34
61 // uri : 1+2+11+2+17 = 33
62 // reqattr : 1+2+20+2+13 = 38
63 // end 1 => total 143
64 if o != 143 { return 10 }
65 if (buf[o - 1] as i64) != 0x03 { return 11 } // end-of-attributes-tag
66
67 // ============ KAT 2: round-trip parse the request ============
68 if nx_ipp_find(buf, o, n_uri, p_tag, p_voff, p_vlen) != NX_IPP_OK { return 20 }
69 if p_tag[0] != NX_IPP_VT_URI { return 21 }
70 if p_vlen[0] != nx_ipp_strlen(v_uri) { return 22 }
71 if nx_ipp_name_eq(buf, p_voff[0], v_uri, p_vlen[0]) != 1 { return 23 }
72
73 if nx_ipp_find(buf, o, n_ra, p_tag, p_voff, p_vlen) != NX_IPP_OK { return 24 }
74 if p_tag[0] != NX_IPP_VT_KEYWORD { return 25 }
75 if nx_ipp_name_eq(buf, p_voff[0], v_ps, p_vlen[0]) != 1 { return 26 }
76
77 if nx_ipp_find(buf, o, n_chs, p_tag, p_voff, p_vlen) != NX_IPP_OK { return 27 }
78 if nx_ipp_name_eq(buf, p_voff[0], "utf-8", 5) != 1 { return 28 }
79
80 // ============ KAT 3: parse a STOPPED-printer response ============
81 // Build it from the wire rules: a Get-Printer-Attributes RESPONSE with status successful-ok,
82 // printer-state(enum)=stopped(5), printer-state-reasons(1setOf keyword)={media-empty, marker-supply-low}.
83 let r: *u8 = sys_mmap(2048)
84 let n_pst: *u8 = "printer-state"
85 let n_psr: *u8 = "printer-state-reasons"
86 var p: i64 = nx_ipp_begin(r, 1, 1, NX_IPP_STATUS_OK, 1)
87 p = nx_ipp_group(r, p, NX_IPP_GRP_PRINTER)
88 p = nx_ipp_attr_int(r, p, NX_IPP_VT_ENUM, n_pst, nx_ipp_strlen(n_pst), NX_IPP_PSTATE_STOPPED)
89 p = nx_ipp_attr(r, p, NX_IPP_VT_KEYWORD, n_psr, nx_ipp_strlen(n_psr), "media-empty", 11)
90 p = nx_ipp_attr_add(r, p, NX_IPP_VT_KEYWORD, "marker-supply-low", 17)
91 p = nx_ipp_end(r, p)
92
93 if nx_ipp_status(r) != NX_IPP_STATUS_OK { return 30 }
94 // surface printer-state -> must read enum 5 (stopped)
95 if nx_ipp_get_enum(r, p, n_pst, sc, outv) != NX_IPP_OK { return 31 }
96 if outv[0] != NX_IPP_PSTATE_STOPPED { return 32 }
97 // surface the first printer-state-reasons value -> 'media-empty' (the reason Brother would hide)
98 if nx_ipp_find(r, p, n_psr, p_tag, p_voff, p_vlen) != NX_IPP_OK { return 33 }
99 if p_tag[0] != NX_IPP_VT_KEYWORD { return 34 }
100 if nx_ipp_name_eq(r, p_voff[0], "media-empty", 11) != 1 { return 35 }
101
102 // ============ KAT 4: defensive liar-kill ============
103 // (a) buffer too short to even hold the 8-byte header
104 let tiny: *u8 = sys_mmap(64)
105 if nx_ipp_find(tiny, 5, "x", p_tag, p_voff, p_vlen) != NX_IPP_MALFORMED { return 40 }
106
107 // (b) value-length overrun: an attribute claims a 0x7fff-byte value that runs past the buffer
108 let bad: *u8 = sys_mmap(2048)
109 var b: i64 = nx_ipp_begin(bad, 1, 1, NX_IPP_STATUS_OK, 1)
110 b = nx_ipp_group(bad, b, NX_IPP_GRP_PRINTER)
111 bad[b] = NX_IPP_VT_ENUM as u8
112 b = b + 1
113 b = nx_ipp_put_u16(bad, b, 13)
114 b = nx_ipp_memcpy(bad, b, "printer-state", 13)
115 b = nx_ipp_put_u16(bad, b, 0x7fff) // lying length, no value bytes follow
116 if nx_ipp_find(bad, b, "printer-state", p_tag, p_voff, p_vlen) != NX_IPP_MALFORMED { return 41 }
117
118 // ============ KAT 5: missing attribute -> NOT_FOUND ============
119 if nx_ipp_find(r, p, "no-such-attr", p_tag, p_voff, p_vlen) != NX_IPP_NOT_FOUND { return 50 }
120
121 t_puts("nx_ipp_codec: 5/5 KAT groups PASS (encode byte-exact + round-trip + state-reasons surfaced + liar-kill + not-found)\n")
122 return 0
123}