nx_socket.nx source
↩ module page · 226 lines · 7109 B
1// nx_socket.nx -- IPv4 TCP/UDP socket primitives.
2//
3// Used by:
4// - Network services (HTTP, gRPC servers)
5// - RPC client + server scaffolding
6// - The future Nishi netcode test harness
7//
8// The host net.nx wraps a few socket calls but doesn't expose
9// the full primitive set or the address-family/type/protocol
10// constants. This module is the canonical surface.
11//
12// Linux RV64 syscall numbers:
13// socket 198
14// bind 200
15// listen 201
16// accept 202
17// accept4 242
18// connect 203
19// sendto 206
20// recvfrom 207
21// shutdown 210
22// setsockopt 208
23// getsockopt 209
24//
25// Address family / socket type / protocol constants are reused
26// across the whole net stack.
27
28// nx_safety_envelope:
29// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
30// sil_target: SIL1
31// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
32// verdict: NOT_YET_EVALUATED
33
34import "syscalls.nx"
35const NX_MAGIC_8080: i64 = 8080
36
37// AF_*
38const NX_AF_UNIX: i64 = 1
39const NX_AF_INET: i64 = 2
40const NX_AF_INET6: i64 = 10
41
42// SOCK_*
43const NX_SOCK_STREAM: i64 = 1
44const NX_SOCK_DGRAM: i64 = 2
45const NX_SOCK_RAW: i64 = 3
46const NX_SOCK_NONBLOCK: i64 = 0x800
47const NX_SOCK_CLOEXEC: i64 = 0x80000
48
49// IPPROTO_*
50const NX_IPPROTO_IP: i64 = 0
51const NX_IPPROTO_TCP: i64 = 6
52const NX_IPPROTO_UDP: i64 = 17
53
54// SOL_*
55const NX_SOL_SOCKET: i64 = 1
56
57// SO_*
58const NX_SO_REUSEADDR: i64 = 2
59const NX_SO_REUSEPORT: i64 = 15
60const NX_SO_KEEPALIVE: i64 = 9
61const NX_SO_SNDBUF: i64 = 7
62const NX_SO_RCVBUF: i64 = 8
63
64// SHUT_*
65const NX_SHUT_RD: i64 = 0
66const NX_SHUT_WR: i64 = 1
67const NX_SHUT_RDWR: i64 = 2
68
69// Linux RV64 syscall numbers.
70const NX_SYS_SOCKET: i64 = 198
71const NX_SYS_BIND: i64 = 200
72const NX_SYS_LISTEN: i64 = 201
73const NX_SYS_ACCEPT: i64 = 202
74const NX_SYS_CONNECT: i64 = 203
75const NX_SYS_SENDTO: i64 = 206
76const NX_SYS_RECVFROM: i64 = 207
77const NX_SYS_SETSOCKOPT: i64 = 208
78const NX_SYS_GETSOCKOPT: i64 = 209
79const NX_SYS_SHUTDOWN: i64 = 210
80const NX_SYS_ACCEPT4: i64 = 242
81
82// struct sockaddr_in (16 bytes):
83// u16 sin_family (= AF_INET, LE)
84// u16 sin_port (BE network byte order)
85// u32 sin_addr (BE)
86// u8 sin_zero[8]
87//
88// We expose helpers for big-endian byte-flipping since RV64 is LE.
89
90func nx_sock_htons(v: i64) -> i64 {
91 return ((v & 0xFF) << 8) | ((v >> 8) & 0xFF)
92}
93
94func nx_sock_htonl(v: i64) -> i64 {
95 return ((v & 0xFF) << 24)
96 | (((v >> 8) & 0xFF) << 16)
97 | (((v >> 16) & 0xFF) << 8)
98 | ((v >> 24) & 0xFF)
99}
100
101// Build a sockaddr_in into the caller's 16-byte buffer.
102func nx_sock_sin_init(buf: *u8, ip_be: i64, port_host: i64) -> i64 {
103 let port_be: i64 = nx_sock_htons(port_host)
104 buf[0] = NX_AF_INET & 0xFF
105 buf[1] = (NX_AF_INET >> 8) & 0xFF
106 buf[2] = port_be & 0xFF
107 buf[3] = (port_be >> 8) & 0xFF
108 buf[4] = ip_be & 0xFF
109 buf[5] = (ip_be >> 8) & 0xFF
110 buf[6] = (ip_be >> 16) & 0xFF
111 buf[7] = (ip_be >> 24) & 0xFF
112 var i: i64 = 8
113 while i < 16 { buf[i] = 0; i = i + 1 }
114 return 0
115}
116
117// Convert dotted-quad ASCII IPv4 ("192.168.1.10") to packed BE u32.
118// Returns the packed value or -1 on parse error.
119func nx_sock_inet_aton(s: *u8) -> i64 {
120 var parts: i64 = 0
121 var cur: i64 = 0
122 var packed: i64 = 0
123 var i: i64 = 0
124 var done: i64 = 0
125 while done == 0 {
126 let c: i64 = s[i]
127 if c == 0 {
128 if parts != 3 { return 0 - 1 }
129 packed = packed | (cur << 24)
130 done = 1
131 } else {
132 if c == 0x2E {
133 if parts >= 3 { return 0 - 1 }
134 packed = packed | (cur << (parts * 8))
135 cur = 0
136 parts = parts + 1
137 i = i + 1
138 } else {
139 if c < 0x30 { return 0 - 1 }
140 if c > 0x39 { return 0 - 1 }
141 cur = cur * 10 + (c - 0x30)
142 if cur > 255 { return 0 - 1 }
143 i = i + 1
144 }
145 }
146 }
147 return packed
148}
149
150// Wrappers.
151func nx_sock_socket(family: i64, sock_type: i64, proto: i64) -> i64 {
152 return __syscall(NX_SYS_SOCKET, family, sock_type, proto, 0, 0, 0)
153}
154
155func nx_sock_bind(fd: i64, addr: *u8, addrlen: i64) -> i64 {
156 return __syscall(NX_SYS_BIND, fd, addr as i64, addrlen, 0, 0, 0)
157}
158
159func nx_sock_listen(fd: i64, backlog: i64) -> i64 {
160 return __syscall(NX_SYS_LISTEN, fd, backlog, 0, 0, 0, 0)
161}
162
163func nx_sock_accept(fd: i64, addr: *u8, addrlen: *i64) -> i64 {
164 return __syscall(NX_SYS_ACCEPT, fd, addr as i64, addrlen as i64, 0, 0, 0)
165}
166
167func nx_sock_connect(fd: i64, addr: *u8, addrlen: i64) -> i64 {
168 return __syscall(NX_SYS_CONNECT, fd, addr as i64, addrlen, 0, 0, 0)
169}
170
171func nx_sock_setsockopt(fd: i64, level: i64, opt: i64, val: *u8, len: i64) -> i64 {
172 return __syscall(NX_SYS_SETSOCKOPT, fd, level, opt, val as i64, len, 0)
173}
174
175func nx_sock_shutdown(fd: i64, how: i64) -> i64 {
176 return __syscall(NX_SYS_SHUTDOWN, fd, how, 0, 0, 0, 0)
177}
178
179// Convenience: SO_REUSEADDR=1.
180func nx_sock_reuseaddr(fd: i64) -> i64 {
181 let one_raw: *u8 = sys_mmap(8)
182 let one: *i32 = one_raw as *i32
183 one[0] = 1
184 return nx_sock_setsockopt(fd, NX_SOL_SOCKET, NX_SO_REUSEADDR, one_raw, 4)
185}
186
187// ---- self-test ---------------------------------------------------
188
189func main() -> i64 {
190 // htons / htonl byte-swap.
191 if nx_sock_htons(0x1234) != 0x3412 { return __syscall(93, 1, 0, 0, 0, 0, 0) }
192 if nx_sock_htonl(0x12345678) != 0x78563412 { return __syscall(93, 2, 0, 0, 0, 0, 0) }
193
194 // sin_init writes correct family + port + addr bytes.
195 let buf: *u8 = sys_mmap(16)
196 nx_sock_sin_init(buf, 0x0100007F, NX_MAGIC_8080) // 127.0.0.1:NX_MAGIC_8080
197 if buf[0] != 2 { return __syscall(93, 3, 0, 0, 0, 0, 0) } // AF_INET LE
198 if buf[2] != 0x1F { return __syscall(93, 4, 0, 0, 0, 0, 0) } // NX_MAGIC_8080 = 0x1F90 BE
199 if buf[3] != 0x90 { return __syscall(93, 5, 0, 0, 0, 0, 0) }
200 if buf[4] != 0x7F { return __syscall(93, 6, 0, 0, 0, 0, 0) } // 127
201 if buf[7] != 0x01 { return __syscall(93, 7, 0, 0, 0, 0, 0) } // 1
202
203 // inet_aton on dotted-quad.
204 let ip: *u8 = sys_mmap(16)
205 ip[0] = 0x31; ip[1] = 0x32; ip[2] = 0x37; ip[3] = 0x2E // "127."
206 ip[4] = 0x30; ip[5] = 0x2E // "0."
207 ip[6] = 0x30; ip[7] = 0x2E // "0."
208 ip[8] = 0x31; ip[9] = 0 // "1"
209 let v: i64 = nx_sock_inet_aton(ip)
210 if v != 0x0100007F { return __syscall(93, 8, 0, 0, 0, 0, 0) }
211
212 // inet_aton on bad input.
213 let bad: *u8 = sys_mmap(8)
214 bad[0] = 0x31; bad[1] = 0x32; bad[2] = 0x33; bad[3] = 0
215 let bv: i64 = nx_sock_inet_aton(bad)
216 if bv != (0 - 1) { return __syscall(93, 9, 0, 0, 0, 0, 0) }
217
218 // socket() should succeed (or return errno; just check it
219 // doesn't crash).
220 let s: i64 = nx_sock_socket(NX_AF_INET, NX_SOCK_STREAM, 0)
221 if s >= 0 { sys_close(s) }
222 // We don't enforce success because the test environment might
223 // restrict socket creation.
224
225 return 0
226}