code wiki / _hdl_build / nx_tls13_app_send_fd.nx

nx_tls13_app_send_fd.nx source

↩ module page · 83 lines · 3729 B

1// nx_tls13_app_send_fd.nx -- chunked TLS 1.3 application-data send to an fd. 2// 3// ROOT CAUSE this fixes: nx_tls13_server_session_app_send encrypts the WHOLE 4// payload as ONE record, but RFC 8446 ยง5.1 caps record plaintext at 2^14 5// (16384) bytes -- compliant clients MUST abort with record_overflow on 6// anything bigger. The hosting path therefore could not serve any asset 7// over ~16.3KB (the /video page is 19KB, its JS ~35KB). This module splits 8// the payload into <=NX_TLS13_SENDFD_CHUNK-byte records and writes each to 9// the fd, handling partial writes. One reusable record buffer, supplied by 10// the caller (constant-memory daemons hoist it once). 11// 12// Composes nx_tls13_server_session_app_send (KAT'd record build) -- the 13// per-record path is UNCHANGED, so existing small-response behaviour is 14// byte-identical (API contract stability: additive, nothing renamed). 15// 16// Gate: nx_tls13_app_send_fd_gate.nx (fabricated CONNECTED session with 17// fixed keys -> 40KB payload -> records to a file -> decrypt each record 18// with nx_tls13_record_decrypt_v2 -> reassembled plaintext byte-compares). 19// license_tier: ORIGINAL 20 21import "nx_syscalls.nx" 22import "nx_tls13.nx" 23import "nx_tls13_record.nx" 24import "nx_tls13_server_session.nx" 25import "nx_tls13_server_session_app_data.nx" 26 27// Per-record plaintext chunk. Below the 16384 RFC cap with margin so 28// header(5) + inner-type(1) + tag(16) stays well inside a 16384+384 29// record buffer. 30const NX_TLS13_SENDFD_CHUNK: i64 = 16000 31// Minimum record buffer the caller must supply: chunk + header + type + tag. 32const NX_TLS13_SENDFD_REC_MIN: i64 = 16384 33 34// write_all: loop sys_write until n bytes are on the fd (partial writes 35// happen on sockets under pressure). Returns n, or negative on error. 36func nx_tls13_sendfd_write_all(fd: i64, buf: *u8, n: i64) -> i64 { 37 var off: i64 = 0 38 while off < n { 39 let w: i64 = sys_write(fd, (buf as i64 + off) as *u8, n - off) 40 if w <= 0 { return 0 - 1 } 41 off = off + w 42 } 43 return n 44} 45 46// Encrypt payload as a sequence of <=CHUNK-byte application-data records, 47// writing each record to fd as it is built. rec_buf/rec_cap is scratch for 48// ONE wire record (rec_cap >= NX_TLS13_SENDFD_REC_MIN). Returns payload_len 49// on success, negative on any session/encrypt/write error. 50func nx_tls13_app_send_fd( 51 session: *Tls13ServerSession, 52 payload: *u8, payload_len: i64, 53 fd: i64, 54 rec_buf: *u8, rec_cap: i64 55) -> i64 { 56 if (session as i64) == 0 { return 0 - NX_TLS13_SSESSION_BAD_STATE } 57 if (payload as i64) == 0 { return 0 - NX_TLS13_SSESSION_BAD_STATE } 58 if payload_len < 0 { return 0 - NX_TLS13_SSESSION_BAD_STATE } 59 if rec_cap < NX_TLS13_SENDFD_REC_MIN { return 0 - NX_TLS13_SSESSION_BUF_OVERFLOW } 60 61 // Zero-length payload: still emit one empty record (callers that send 62 // "" expect a record on the wire, matching app_send's behaviour). 63 if payload_len == 0 { 64 let w0: i64 = nx_tls13_server_session_app_send(session, payload, 0, rec_buf, rec_cap) 65 if w0 <= 0 { return w0 } 66 let wr0: i64 = nx_tls13_sendfd_write_all(fd, rec_buf, w0) 67 if wr0 < 0 { return 0 - NX_TLS13_SSESSION_INTERNAL } 68 return 0 69 } 70 71 var off: i64 = 0 72 while off < payload_len { 73 var chunk: i64 = payload_len - off 74 if chunk > NX_TLS13_SENDFD_CHUNK { chunk = NX_TLS13_SENDFD_CHUNK } 75 let w: i64 = nx_tls13_server_session_app_send( 76 session, (payload as i64 + off) as *u8, chunk, rec_buf, rec_cap) 77 if w <= 0 { return w } 78 let wr: i64 = nx_tls13_sendfd_write_all(fd, rec_buf, w) 79 if wr < 0 { return 0 - NX_TLS13_SSESSION_INTERNAL } 80 off = off + chunk 81 } 82 return payload_len 83}