code wiki / _hdl_build / nx_ws_client_session.nx
nx_ws_client_session.nx source
↩ module page · 139 lines · 7556 B
1// nx_ws_client_session.nx -- shared sovereign WSS-over-TLS1.3 CLIENT SESSION helpers. Extracted from
2// nx_video_2party_probe so the mobile-native room client and the probe share ONE implementation instead of
3// copy-pasting the TLS-record + RFC-6455 framing glue (rule #15 DRY / the operator's SSOT direction).
4// No main -- import this and call wscs_connect / wscs_send / wscs_ws_recv. These are the transport a
5// ZERO-JS, ZERO-browser NishiLang client (mobile-native) uses to join a live room.
6// license_tier: ORIGINAL
7import "nx_syscalls.nx"
8import "nx_x509_trust_store.nx"
9import "nx_trust_store_load_from_certdata.nx"
10import "nx_tls13_client_validate_certificate.nx"
11import "nx_tls13_client_session_run.nx"
12import "nx_https_url_for_fetch.nx"
13import "nx_https_url_connect.nx"
14import "nx_https_get_complete.nx" // record constants + session struct + record encrypt/decrypt (transitive)
15import "nx_websocket_client_upgrade.nx" // nx_ws_client_build_request / nx_ws_client_validate_response
16import "nx_websocket_frame.nx" // ws_build_header / ws_apply_mask / ws_parse_frame / WS_OP_BINARY / WsFrame
17const WSCS_MAGIC_16645: i64 = 16645
18const WSCS_MAGIC_131072: i64 = 131072
19const WSCS_MAGIC_4096: i64 = 4096
20const WSCS_MAGIC_65536: i64 = 65536
21
22const WSCS_TMO_S: i64 = 12
23
24// TLS-encrypt `bytes` as ONE application_data record (client-app keys) and write it. 0 ok / -1 fail.
25func wscs_send(s: *Tls13ClientSession, fd: i64, bytes: *u8, blen: i64) -> i64 {
26 let rec_buf: *u8 = sys_mmap(blen + 64)
27 let header_out: *u8 = rec_buf
28 let ct_out: *u8 = ((rec_buf as i64) + NX_TLS13_RECORD_HEADER_LEN) as *u8
29 let tag_out: *u8 = ((rec_buf as i64) + NX_TLS13_RECORD_HEADER_LEN + blen + 1) as *u8
30 let ev: i64 = nx_tls13_record_encrypt_v2(s.cipher_suite, s.client_app_traffic_key, s.client_app_iv, s.client_app_seq, bytes, blen, NX_TLS13_CT_APPLICATION_DATA, 0, header_out, ct_out, tag_out)
31 s.client_app_seq = s.client_app_seq + 1
32 if ev != NX_TLS13_REC_VERDICT_OK { return 0 - 1 }
33 let total: i64 = NX_TLS13_RECORD_HEADER_LEN + blen + 1 + NX_TLS13_RECORD_TAG_LEN
34 var wo: i64 = 0
35 while wo < total { let w: i64 = sys_write(fd, ((rec_buf as i64) + wo) as *u8, total - wo); if w <= 0 { return 0 - 1 } wo = wo + w }
36 return 0
37}
38
39// read+decrypt records into out until CRLFCRLF (the 101 headers) present. returns bytes accumulated, or -1.
40func wscs_recv_headers(s: *Tls13ClientSession, fd: i64, out: *u8, cap: i64) -> i64 {
41 var acc: i64 = 0
42 let rec_in: *u8 = sys_mmap(WSCS_MAGIC_16645)
43 let pt: *u8 = sys_mmap(WSCS_MAGIC_16645)
44 let ctp: *i64 = sys_mmap(16) as *i64
45 let lenp: *i64 = sys_mmap(16) as *i64
46 var go: i64 = 1
47 while go == 1 {
48 let rt: i64 = nx_tls13_read_record_from_fd(fd, rec_in, WSCS_MAGIC_16645)
49 if rt < 0 { return 0 - 1 }
50 let ct_len: i64 = rt - NX_TLS13_RECORD_HEADER_LEN - NX_TLS13_RECORD_TAG_LEN
51 let tag: *u8 = ((rec_in as i64) + rt - NX_TLS13_RECORD_TAG_LEN) as *u8
52 let dv: i64 = nx_tls13_record_decrypt_v2(s.cipher_suite, s.server_app_traffic_key, s.server_app_iv, s.server_app_seq, rec_in, ((rec_in as i64) + NX_TLS13_RECORD_HEADER_LEN) as *u8, ct_len, tag, pt, ctp, lenp)
53 s.server_app_seq = s.server_app_seq + 1
54 if dv != NX_TLS13_REC_VERDICT_OK { return 0 - 1 }
55 if ctp[0] == NX_TLS13_CT_APPLICATION_DATA {
56 var i: i64 = 0
57 while i < lenp[0] { if acc < cap { out[i + acc] = pt[i] } i = i + 1 }
58 acc = acc + lenp[0]
59 if _wscu_find_header_end(out, acc) >= 0 { go = 0 }
60 }
61 if ctp[0] == NX_TLS13_CT_ALERT { return 0 - 1 }
62 }
63 return acc
64}
65
66// read+decrypt records, parse ONE WS data frame, copy its payload into out. returns payload_len, or -1.
67func wscs_ws_recv(s: *Tls13ClientSession, fd: i64, out: *u8, cap: i64) -> i64 {
68 let plain: *u8 = sys_mmap(WSCS_MAGIC_131072)
69 var acc: i64 = 0
70 let rec_in: *u8 = sys_mmap(WSCS_MAGIC_16645)
71 let pt: *u8 = sys_mmap(WSCS_MAGIC_16645)
72 let ctp: *i64 = sys_mmap(16) as *i64
73 let lenp: *i64 = sys_mmap(16) as *i64
74 let f_raw: *u8 = sys_mmap(128)
75 let f: *WsFrame = f_raw as *WsFrame
76 var go: i64 = 1
77 while go == 1 {
78 let rt: i64 = nx_tls13_read_record_from_fd(fd, rec_in, WSCS_MAGIC_16645)
79 if rt < 0 { return 0 - 1 }
80 let ct_len: i64 = rt - NX_TLS13_RECORD_HEADER_LEN - NX_TLS13_RECORD_TAG_LEN
81 let tag: *u8 = ((rec_in as i64) + rt - NX_TLS13_RECORD_TAG_LEN) as *u8
82 let dv: i64 = nx_tls13_record_decrypt_v2(s.cipher_suite, s.server_app_traffic_key, s.server_app_iv, s.server_app_seq, rec_in, ((rec_in as i64) + NX_TLS13_RECORD_HEADER_LEN) as *u8, ct_len, tag, pt, ctp, lenp)
83 s.server_app_seq = s.server_app_seq + 1
84 if dv != NX_TLS13_REC_VERDICT_OK { return 0 - 1 }
85 if ctp[0] == NX_TLS13_CT_ALERT { return 0 - 1 }
86 if ctp[0] == NX_TLS13_CT_APPLICATION_DATA {
87 var i: i64 = 0
88 while i < lenp[0] { if acc < WSCS_MAGIC_131072 { plain[acc] = pt[i]; acc = acc + 1 } i = i + 1 }
89 if ws_parse_frame(plain, acc, 0, f) == 0 {
90 if f.opcode == WS_OP_BINARY {
91 var pl: i64 = f.payload_len
92 if pl > cap { pl = cap }
93 var c: i64 = 0
94 while c < pl { out[c] = plain[f.payload_off + c]; c = c + 1 }
95 if f.masked == 1 { ws_apply_mask(out, 0, pl, f.mask_key) }
96 return pl
97 }
98 }
99 }
100 }
101 return 0 - 1
102}
103
104// connect + TLS-1.3 + WS upgrade to https://<host>/signal/<room>. sess_out[0]=session ptr, fd_out[0]=fd. 1 ok.
105func wscs_connect(store: *TrustStore, url: *u8, kseed: i64, sess_out: *i64, fd_out: *i64) -> i64 {
106 let cr: *u8 = sys_mmap(32); var i: i64=0; while i<32{cr[i]=((kseed+i)&0xff) as u8;i=i+1}
107 let priv: *u8 = sys_mmap(32); i=0; while i<32{priv[i]=((kseed+64+i)&0xff) as u8;i=i+1}
108 let url_p: *NxUrl = nx_url_new()
109 let target_raw: *u8 = sys_mmap(32)
110 let target: *NxHttpsTarget = target_raw as *NxHttpsTarget
111 target.url = url_p; target.port = 0
112 if nx_https_url_for_fetch(url, target) != NX_HTTPS_URL_OK { return 0 }
113 let fd_p: *i64 = sys_mmap(16) as *i64
114 if nx_https_url_connect(target, url, sys_now_realtime_sec(), fd_p) != NX_HTTPS_CONNECT_OK { return 0 }
115 let fd: i64 = *fd_p
116 sys_set_socket_timeout(fd, WSCS_TMO_S)
117 let host: *u8 = url + target.url.host_off
118 let hlen: i64 = target.url.host_len
119 let path: *u8 = url + target.url.path_off
120 let plen: i64 = target.url.path_len
121 let vc_raw: *u8 = sys_mmap(128)
122 let vc: *TlsValidationContext = vc_raw as *TlsValidationContext
123 vc.store = store; vc.sni_host = host; vc.sni_host_len = hlen; vc.now_epoch = sys_now_realtime_sec()
124 let sr: i64 = nx_tls13_client_session_run(fd, host, hlen, cr, priv, vc)
125 if sr <= 0 { sys_close(fd); return 0 }
126 let s: *Tls13ClientSession = sr as *Tls13ClientSession
127 let req: *u8 = sys_mmap(WSCS_MAGIC_4096)
128 let key24: *u8 = sys_mmap(32)
129 let rn: i64 = nx_ws_client_build_request(req, WSCS_MAGIC_4096, host, hlen, path, plen, 443, 0, key24)
130 if rn < 0 { sys_close(fd); return 0 }
131 if wscs_send(s, fd, req, rn) != 0 { sys_close(fd); return 0 }
132 let resp: *u8 = sys_mmap(WSCS_MAGIC_65536)
133 let hn: i64 = wscs_recv_headers(s, fd, resp, WSCS_MAGIC_65536)
134 if hn < 0 { sys_close(fd); return 0 }
135 if nx_ws_client_validate_response(resp, hn, key24) != NX_WSCU_OK { sys_close(fd); return 0 }
136 sess_out[0] = sr
137 fd_out[0] = fd
138 return 1
139}