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}