nx_peerwire.nx source
↩ module page · 100 lines · 3658 B
1// nx_peerwire.nx -- BitTorrent peer wire protocol (BEP-3), bits-up.
2//
3// module: nishi-core.torrent.peerwire
4// depends: nx_syscalls.nx
5// capability: CORE_COMPUTE
6// wired_status: FULLY_WIRED
7//
8// The block-exchange core: the 68-byte handshake (<19>"BitTorrent protocol"
9// <8 reserved><info_hash 20><peer_id 20>) and length-prefixed messages
10// (<4 BE len><id><payload>): choke/unchoke/interested/not_interested, have,
11// bitfield, request, piece, cancel. Pure build/parse here (KAT'd, deterministic
12// -- part of the determinism exceed thesis); the socket round-trip reuses the
13// net stack, and piece verification reuses sha1/sha256. Big-endian on the wire.
14
15import "nx_syscalls.nx"
16
17const NX_PW_CHOKE: i64 = 0
18const NX_PW_UNCHOKE: i64 = 1
19const NX_PW_INTERESTED: i64 = 2
20const NX_PW_NOT_INTERESTED: i64 = 3
21const NX_PW_HAVE: i64 = 4
22const NX_PW_BITFIELD: i64 = 5
23const NX_PW_REQUEST: i64 = 6
24const NX_PW_PIECE: i64 = 7
25const NX_PW_CANCEL: i64 = 8
26
27func _pw_put_u32(out: *u8, off: i64, v: i64) -> i64 {
28 out[off] = (v >> 24) & 0xff
29 out[off + 1] = (v >> 16) & 0xff
30 out[off + 2] = (v >> 8) & 0xff
31 out[off + 3] = v & 0xff
32 return 0
33}
34func _pw_get_u32(buf: *u8, off: i64) -> i64 {
35 return ((buf[off] as i64) << 24) | ((buf[off + 1] as i64) << 16) | ((buf[off + 2] as i64) << 8) | (buf[off + 3] as i64)
36}
37
38// Build the 68-byte handshake. Returns 68.
39func nx_pw_build_handshake(info_hash: *u8, peer_id: *u8, out: *u8) -> i64 {
40 out[0] = 19
41 let pstr: *u8 = "BitTorrent protocol"
42 var i: i64 = 0
43 while i < 19 { out[1 + i] = pstr[i]; i = i + 1 }
44 i = 0
45 while i < 8 { out[20 + i] = 0; i = i + 1 }
46 i = 0
47 while i < 20 { out[28 + i] = info_hash[i]; i = i + 1 }
48 i = 0
49 while i < 20 { out[48 + i] = peer_id[i]; i = i + 1 }
50 return 68
51}
52
53// Validate a handshake + extract info_hash + peer_id. Returns 1 if valid.
54func nx_pw_parse_handshake(buf: *u8, n: i64, out_ih: *u8, out_pid: *u8) -> i64 {
55 if n < 68 { return 0 }
56 if (buf[0] as i64) != 19 { return 0 }
57 let pstr: *u8 = "BitTorrent protocol"
58 var i: i64 = 0
59 while i < 19 { if (buf[1 + i] as i64) != (pstr[i] as i64) { return 0 } i = i + 1 }
60 i = 0
61 while i < 20 { out_ih[i] = buf[28 + i]; i = i + 1 }
62 i = 0
63 while i < 20 { out_pid[i] = buf[48 + i]; i = i + 1 }
64 return 1
65}
66
67// Build a generic message: <4 BE len=1+plen><id><payload>. Returns total bytes.
68func nx_pw_build_msg(id: i64, payload: *u8, plen: i64, out: *u8) -> i64 {
69 _pw_put_u32(out, 0, 1 + plen)
70 out[4] = id
71 var i: i64 = 0
72 while i < plen { out[5 + i] = payload[i]; i = i + 1 }
73 return 5 + plen
74}
75
76func nx_pw_msg_len(buf: *u8) -> i64 { return _pw_get_u32(buf, 0) } // payload+id len; 0 = keep-alive
77func nx_pw_msg_id(buf: *u8) -> i64 { return buf[4] as i64 } // valid when msg_len > 0
78
79func nx_pw_build_have(piece_index: i64, out: *u8) -> i64 {
80 let p: *u8 = sys_mmap(4)
81 _pw_put_u32(p, 0, piece_index)
82 return nx_pw_build_msg(NX_PW_HAVE, p, 4, out)
83}
84
85func nx_pw_build_request(index: i64, begin: i64, length: i64, out: *u8) -> i64 {
86 let p: *u8 = sys_mmap(12)
87 _pw_put_u32(p, 0, index)
88 _pw_put_u32(p, 4, begin)
89 _pw_put_u32(p, 8, length)
90 return nx_pw_build_msg(NX_PW_REQUEST, p, 12, out)
91}
92
93// Does a peer's bitfield advertise piece_index? MSB-first per BEP-3.
94func nx_pw_bitfield_has(bitfield: *u8, byte_len: i64, piece_index: i64) -> i64 {
95 let byte: i64 = piece_index / 8
96 if byte >= byte_len { return 0 }
97 let bit: i64 = 7 - (piece_index - byte * 8)
98 if (((bitfield[byte] as i64) >> bit) & 1) == 1 { return 1 }
99 return 0
100}