code wiki / (root) / nx_socket.nx

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}