nx_udp_tracker.nx source
↩ module page · 105 lines · 4468 B
1// nx_udp_tracker.nx -- BitTorrent UDP tracker protocol (BEP-15), bits-up.
2//
3// module: nishi-core.torrent.udp_tracker
4// depends: nx_syscalls.nx
5// capability: CORE_COMPUTE
6// wired_status: FULLY_WIRED
7//
8// The dominant tracker transport (BEP-15, Olaf van der Spek 2008): ~618 bytes /
9// 4 packets for 50 peers vs HTTP's ~1206 / 10 -- half the bytes, no TCP setup.
10// Two-step: connect (get a connection_id) then announce (get peers). Pure
11// build/parse here (KAT'd, deterministic); the UDP socket round-trip reuses the
12// net stack + nx_net_governor pacing. Big-endian wire; magic protocol_id
13// 0x41727101980.
14
15import "nx_syscalls.nx"
16
17const NX_UDP_PROTO_ID: i64 = 0x41727101980 // BEP-15 connect magic
18const NX_UDP_ACT_CONNECT: i64 = 0
19const NX_UDP_ACT_ANNOUNCE: i64 = 1
20const NX_UDP_ACT_SCRAPE: i64 = 2
21const NX_UDP_ACT_ERROR: i64 = 3
22
23func _ut_put_u64(out: *u8, off: i64, v: i64) -> i64 {
24 out[off] = (v >> 56) & 0xff; out[off+1] = (v >> 48) & 0xff
25 out[off+2] = (v >> 40) & 0xff; out[off+3] = (v >> 32) & 0xff
26 out[off+4] = (v >> 24) & 0xff; out[off+5] = (v >> 16) & 0xff
27 out[off+6] = (v >> 8) & 0xff; out[off+7] = v & 0xff
28 return 0
29}
30func _ut_get_u64(buf: *u8, off: i64) -> i64 {
31 return ((buf[off] as i64)<<56)|((buf[off+1] as i64)<<48)|((buf[off+2] as i64)<<40)|((buf[off+3] as i64)<<32)|((buf[off+4] as i64)<<24)|((buf[off+5] as i64)<<16)|((buf[off+6] as i64)<<8)|(buf[off+7] as i64)
32}
33func _ut_put_u32(out: *u8, off: i64, v: i64) -> i64 {
34 out[off]=(v>>24)&0xff; out[off+1]=(v>>16)&0xff; out[off+2]=(v>>8)&0xff; out[off+3]=v&0xff
35 return 0
36}
37func _ut_get_u32(buf: *u8, off: i64) -> i64 {
38 return ((buf[off] as i64)<<24)|((buf[off+1] as i64)<<16)|((buf[off+2] as i64)<<8)|(buf[off+3] as i64)
39}
40func _ut_put_u16(out: *u8, off: i64, v: i64) -> i64 { out[off]=(v>>8)&0xff; out[off+1]=v&0xff; return 0 }
41
42// connect request: <magic u64><action=0 u32><transaction_id u32> = 16 bytes.
43func nx_udp_build_connect(txid: i64, out: *u8) -> i64 {
44 _ut_put_u64(out, 0, NX_UDP_PROTO_ID)
45 _ut_put_u32(out, 8, NX_UDP_ACT_CONNECT)
46 _ut_put_u32(out, 12, txid)
47 return 16
48}
49// connect response: <action=0 u32><txid u32><connection_id u64>. Validates +
50// writes connection_id to out_conn[0]. Returns 1 if valid, 0 otherwise.
51func nx_udp_parse_connect_resp(buf: *u8, n: i64, txid: i64, out_conn: *i64) -> i64 {
52 if n < 16 { return 0 }
53 if _ut_get_u32(buf, 0) != NX_UDP_ACT_CONNECT { return 0 }
54 if _ut_get_u32(buf, 4) != txid { return 0 }
55 out_conn[0] = _ut_get_u64(buf, 8)
56 return 1
57}
58
59// announce request (98 bytes, BEP-15).
60func nx_udp_build_announce(conn_id: i64, txid: i64, info_hash: *u8, peer_id: *u8,
61 downloaded: i64, left: i64, uploaded: i64, event: i64,
62 num_want: i64, port: i64, out: *u8) -> i64 {
63 _ut_put_u64(out, 0, conn_id)
64 _ut_put_u32(out, 8, NX_UDP_ACT_ANNOUNCE)
65 _ut_put_u32(out, 12, txid)
66 var i: i64 = 0
67 while i < 20 { out[16 + i] = info_hash[i]; i = i + 1 }
68 i = 0
69 while i < 20 { out[36 + i] = peer_id[i]; i = i + 1 }
70 _ut_put_u64(out, 56, downloaded)
71 _ut_put_u64(out, 64, left)
72 _ut_put_u64(out, 72, uploaded)
73 _ut_put_u32(out, 80, event)
74 _ut_put_u32(out, 84, 0) // IP = default
75 _ut_put_u32(out, 88, 0) // key
76 _ut_put_u32(out, 92, num_want)
77 _ut_put_u16(out, 96, port)
78 return 98
79}
80
81// announce response: <action=1><txid><interval><leechers><seeders><6N peers>.
82// Writes interval/seeders/leechers + compact peers. Returns peer count or -1.
83func nx_udp_parse_announce_resp(buf: *u8, n: i64, txid: i64,
84 out_interval: *i64, out_seeders: *i64, out_leechers: *i64,
85 out_ips: *i64, out_ports: *i64, max: i64) -> i64 {
86 if n < 20 { return 0 - 1 }
87 if _ut_get_u32(buf, 0) != NX_UDP_ACT_ANNOUNCE { return 0 - 1 }
88 if _ut_get_u32(buf, 4) != txid { return 0 - 1 }
89 out_interval[0] = _ut_get_u32(buf, 8)
90 out_leechers[0] = _ut_get_u32(buf, 12)
91 out_seeders[0] = _ut_get_u32(buf, 16)
92 let total: i64 = (n - 20) / 6
93 var c: i64 = 0
94 var i: i64 = 0
95 while i < total {
96 if c < max {
97 let b: i64 = 20 + i * 6
98 out_ips[c] = ((buf[b] as i64)<<24)|((buf[b+1] as i64)<<16)|((buf[b+2] as i64)<<8)|(buf[b+3] as i64)
99 out_ports[c] = ((buf[b+4] as i64)<<8)|(buf[b+5] as i64)
100 c = c + 1
101 }
102 i = i + 1
103 }
104 return c
105}