code wiki / (root) / nx_peerwire.nx

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}