code wiki / (root) / nx_stun_server.nx

nx_stun_server.nx source

↩ module page · 79 lines · 4053 B

1// nx_stun_server.nx -- SOVEREIGN STUN server (reflexive-address responder), NishiLang, composing nx_stun. 2// 3// Rung 3 of the Nishi P2P interop stack (rung1 STUN codec, rung2 ICE). A Nishi endpoint behind a NAT sends 4// a STUN Binding Request to this server; the server replies with a Binding Response whose XOR-MAPPED-ADDRESS 5// is the sender's PUBLIC reflexive IP:port exactly as the server observed it on the wire. That is how an 6// endpoint learns its server-reflexive (srflx) candidate -- the address a peer must use to reach it through 7// the NAT -- WITHOUT any third-party STUN server (no stun.l.google.com). Every byte is ours. 8// 9// ss_handle is PURE (given a datagram + its source sockaddr -> a response), so it is gate-testable without 10// sockets; main() is the UDP daemon that binds and services requests forever. No third-party code. 11// license_tier: INDEPENDENT_REDERIVE genealogy_id: international-research-sources/ietf/rfc_5389 12import "nx_syscalls.nx" 13import "nx_stun.nx" 14const SS_MAGIC_3478: i64 = 3478 15const SS_MAGIC_2048: i64 = 2048 16 17const SS_PORT: i64 = 3478 // the IANA-assigned STUN port 18 19// extract the reflexive port + IPv4 address from a 16-byte sockaddr_in (both in network byte order). 20func ss_src_port(sa: *u8) -> i64 { return ((((sa[2] as i64)&0xff)<<8) | ((sa[3] as i64)&0xff)) } 21func ss_src_addr(sa: *u8) -> i64 { 22 return ((((sa[4] as i64)&0xff)<<24) | (((sa[5] as i64)&0xff)<<16) | (((sa[6] as i64)&0xff)<<8) | ((sa[7] as i64)&0xff)) 23} 24 25// build a minimal RFC-compliant Binding Response: header + XOR-MAPPED-ADDRESS + FINGERPRINT. 26func ss_build_response(buf: *u8, txid: *u8, port: i64, addr: i64) -> i64 { 27 var o: i64 = st_write_header(buf, STUN_BINDING_RESPONSE, 0, txid) 28 st_put16(buf, o, STUN_ATTR_XOR_MAPPED_ADDRESS); st_put16(buf, o+2, 8) 29 let xp: *u8 = (buf as i64 + o + 4) as *u8 30 st_xma_encode(xp, port, addr) 31 o = o + 12 32 st_put16(buf, 2, (o-20)+8) // length field for the FINGERPRINT computation 33 let fp: i64 = st_fingerprint(buf, o) 34 st_put16(buf, o, STUN_ATTR_FINGERPRINT); st_put16(buf, o+2, 4); st_put32(buf, o+4, fp) 35 o = o + 8 36 return o 37} 38 39// PURE handler: received datagram + its source sockaddr -> response bytes into out. Returns the response 40// length, or 0 if the packet is not a valid STUN Binding Request (unknown packets are ignored silently). 41func ss_handle(req: *u8, reqlen: i64, src: *u8, out: *u8) -> i64 { 42 if reqlen < 20 { return 0 } 43 if st_type(req) != STUN_BINDING_REQUEST { return 0 } 44 if st_magic(req) != STUN_MAGIC { return 0 } 45 let txid: *u8 = (req as i64 + 8) as *u8 46 return ss_build_response(out, txid, ss_src_port(src), ss_src_addr(src)) 47} 48 49func ssp(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 50func ss_sockaddr(sa: *u8, port: i64, i0: i64, i1: i64, i2: i64, i3: i64) -> i64 { 51 sa[0]=2 as u8; sa[1]=0 as u8 52 sa[2]=((port>>8)&0xff) as u8; sa[3]=(port&0xff) as u8 53 sa[4]=i0 as u8; sa[5]=i1 as u8; sa[6]=i2 as u8; sa[7]=i3 as u8 54 var k: i64=8; while k<16 { sa[k]=0 as u8; k=k+1 } 55 return 0 56} 57 58func main() -> i64 { 59 let sa: *u8 = sys_mmap(16) 60 ss_sockaddr(sa, SS_PORT, 0, 0, 0, 0) // bind 0.0.0.0:SS_MAGIC_3478 (all interfaces) 61 let fd: i64 = sys_socket(AF_INET, SOCK_DGRAM, 0) 62 if fd < 0 { ssp("NX-STUN-SERVER socket FAILED\n" as *u8); return 1 } 63 if sys_bind(fd, sa, 16) < 0 { ssp("NX-STUN-SERVER bind 0.0.0.0:3478 FAILED (port busy?)\n" as *u8); return 1 } 64 ssp("NX-STUN-SERVER reflexive responder on 0.0.0.0:3478 (sovereign; no third-party STUN)\n" as *u8) 65 let req: *u8 = sys_mmap(SS_MAGIC_2048) 66 let out: *u8 = sys_mmap(SS_MAGIC_2048) 67 let src: *u8 = sys_mmap(16) 68 let slen: *i64 = sys_mmap(16) as *i64 69 var go: i64 = 1 70 while go == 1 { 71 slen[0] = 16 72 let n: i64 = sys_recvfrom(fd, req, SS_MAGIC_2048, 0, src, slen) 73 if n > 0 { 74 let rn: i64 = ss_handle(req, n, src, out) 75 if rn > 0 { sys_sendto(fd, out, rn, 0, src, slen[0]) } 76 } 77 } 78 return 0 79}