224 lines
7.1 KiB
Rust
224 lines
7.1 KiB
Rust
use std::collections::BTreeMap;
|
|
|
|
use super::{
|
|
bencode::{
|
|
BencodeValue, dict_bytes, dict_int, encode_bencode_root, parse_bencode_root_exact,
|
|
parse_bencode_root_prefix,
|
|
},
|
|
utils::{bytes_to_string, i64_to_u64},
|
|
};
|
|
|
|
/// BEP 10 extension-protocol handshake and metadata helpers.
|
|
mod extension;
|
|
/// Peer-wire frame parsing and serialization helpers.
|
|
mod framing;
|
|
/// `BitTorrent` handshake parsing and serialization helpers.
|
|
mod handshake;
|
|
|
|
/// Generic torrent message wrapper reused by higher-level peer-wire helpers.
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
pub struct TorrentMessageModel {
|
|
/// Canonical internal message type name.
|
|
pub message_type: String,
|
|
/// Raw payload bytes excluding transport framing.
|
|
pub payload: Vec<u8>,
|
|
}
|
|
|
|
/// `BitTorrent` peer-wire handshake header.
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
pub struct PeerWireHandshakeModel {
|
|
/// Reserved extension bits.
|
|
pub reserved: [u8; 8],
|
|
/// 20-byte torrent info-hash.
|
|
pub info_hash: [u8; 20],
|
|
/// 20-byte local peer id.
|
|
pub peer_id: [u8; 20],
|
|
}
|
|
|
|
/// One parsed peer-wire message, optionally associated with a peer id.
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
pub struct PeerWireMessageModel {
|
|
/// Optional peer id attached by higher-level wrappers.
|
|
pub peer_id: Option<[u8; 20]>,
|
|
/// Parsed message payload.
|
|
pub message: TorrentMessageModel,
|
|
}
|
|
|
|
/// Lightweight inspection result for a framed peer-wire message.
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
pub struct PeerWireFrameHeaderModel {
|
|
/// Optional peer-wire message id. `None` represents keepalive.
|
|
pub message_id: Option<u8>,
|
|
/// Payload length excluding the length prefix and optional message id byte.
|
|
pub payload_len: usize,
|
|
}
|
|
|
|
/// Packed peer-wire bitfield bytes.
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
pub struct PeerWireBitfieldModel {
|
|
/// Raw bitfield bytes in network order.
|
|
pub bytes: Vec<u8>,
|
|
}
|
|
|
|
/// Piece request or cancel coordinates for the peer-wire protocol.
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
pub struct PeerWireBlockRequestModel {
|
|
/// Zero-based piece index.
|
|
pub piece_index: u32,
|
|
/// Byte offset within the piece.
|
|
pub block_offset: u32,
|
|
/// Requested block length in bytes.
|
|
pub block_length: u32,
|
|
}
|
|
|
|
/// Piece payload delivered through the peer-wire protocol.
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
pub struct PeerWirePieceBlockModel {
|
|
/// Zero-based piece index.
|
|
pub piece_index: u32,
|
|
/// Byte offset within the piece.
|
|
pub block_offset: u32,
|
|
/// Raw block bytes.
|
|
pub block: Vec<u8>,
|
|
}
|
|
|
|
/// Extension-protocol message payload.
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
pub struct PeerWireExtensionMessageModel {
|
|
/// Extension message id.
|
|
pub extension_message_id: u8,
|
|
/// Extension payload bytes after the extension id.
|
|
pub payload: Vec<u8>,
|
|
}
|
|
|
|
/// Parsed extended-handshake payload from BEP 10.
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
pub struct PeerWireExtensionHandshakeModel {
|
|
/// Named extension ids announced under the `m` dictionary.
|
|
pub extensions: BTreeMap<String, u8>,
|
|
/// Optional peer/client version string from `v`.
|
|
pub client_name: Option<String>,
|
|
/// Optional BEP 9 metadata byte length.
|
|
pub metadata_size: Option<u32>,
|
|
/// Optional request queue depth from `reqq`.
|
|
pub request_queue: Option<u32>,
|
|
}
|
|
|
|
/// BEP 9 `ut_metadata` message type.
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
pub enum PeerWireMetadataMessageType {
|
|
/// Requests one metadata piece.
|
|
Request,
|
|
/// Carries one metadata piece payload.
|
|
Data,
|
|
/// Rejects one metadata piece request.
|
|
Reject,
|
|
}
|
|
|
|
impl PeerWireMetadataMessageType {
|
|
/// Returns the BEP 9 wire value for the metadata message type.
|
|
#[must_use]
|
|
pub const fn wire_value(self) -> u8 {
|
|
match self {
|
|
Self::Request => 0,
|
|
Self::Data => 1,
|
|
Self::Reject => 2,
|
|
}
|
|
}
|
|
|
|
/// Decodes one BEP 9 wire value into the typed metadata message kind.
|
|
fn from_wire_value(value: i64) -> Result<Self, String> {
|
|
match value {
|
|
0 => Ok(Self::Request),
|
|
1 => Ok(Self::Data),
|
|
2 => Ok(Self::Reject),
|
|
_ => Err(format!("unsupported ut_metadata msg_type: {value}")),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Default BEP 9 metadata piece size in bytes.
|
|
pub const PEER_WIRE_METADATA_PIECE_SIZE: u32 = 16 * 1024;
|
|
|
|
/// Parsed BEP 9 `ut_metadata` message with header and optional payload bytes.
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
pub struct PeerWireMetadataMessageModel {
|
|
/// Metadata message subtype.
|
|
pub message_type: PeerWireMetadataMessageType,
|
|
/// Metadata piece index addressed by the message.
|
|
pub piece: u32,
|
|
/// Total metadata byte length when included in `data` messages.
|
|
pub total_size: Option<u32>,
|
|
/// Metadata payload bytes for `data` messages.
|
|
pub payload: Vec<u8>,
|
|
}
|
|
|
|
/// Unknown peer-wire message payload retained losslessly.
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
pub struct PeerWireUnknownMessageModel {
|
|
/// Raw peer-wire message id.
|
|
pub message_id: u8,
|
|
/// Unparsed message payload bytes.
|
|
pub payload: Vec<u8>,
|
|
}
|
|
|
|
/// Supported peer-wire message variants.
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
pub enum PeerWireMessageKind {
|
|
/// Zero-length keepalive frame.
|
|
KeepAlive,
|
|
/// Choke control message.
|
|
Choke,
|
|
/// Unchoke control message.
|
|
Unchoke,
|
|
/// Interested control message.
|
|
Interested,
|
|
/// Not-interested control message.
|
|
NotInterested,
|
|
/// `have` message naming one completed piece.
|
|
Have(u32),
|
|
/// `bitfield` message.
|
|
Bitfield(PeerWireBitfieldModel),
|
|
/// `request` message.
|
|
Request(PeerWireBlockRequestModel),
|
|
/// `piece` message.
|
|
Piece(PeerWirePieceBlockModel),
|
|
/// `cancel` message.
|
|
Cancel(PeerWireBlockRequestModel),
|
|
/// `port` DHT advertisement message.
|
|
Port(u16),
|
|
/// Extension-protocol message.
|
|
Extension(PeerWireExtensionMessageModel),
|
|
/// Unknown message preserved losslessly.
|
|
Unknown(PeerWireUnknownMessageModel),
|
|
}
|
|
|
|
/// Canonical protocol string embedded in peer-wire handshakes.
|
|
const PEER_WIRE_PROTOCOL_NAME: &str = "BitTorrent protocol";
|
|
/// Byte length of [`PEER_WIRE_PROTOCOL_NAME`].
|
|
const PEER_WIRE_PROTOCOL_LEN: u8 = 19;
|
|
/// Handshake bytes following the protocol-length octet and protocol string.
|
|
const PEER_WIRE_HANDSHAKE_PREFIX_LEN: usize = 49;
|
|
/// Peer-wire message id for `choke`.
|
|
const PEER_WIRE_CHOKE_ID: u8 = 0;
|
|
/// Peer-wire message id for `unchoke`.
|
|
const PEER_WIRE_UNCHOKE_ID: u8 = 1;
|
|
/// Peer-wire message id for `interested`.
|
|
const PEER_WIRE_INTERESTED_ID: u8 = 2;
|
|
/// Peer-wire message id for `not interested`.
|
|
const PEER_WIRE_NOT_INTERESTED_ID: u8 = 3;
|
|
/// Peer-wire message id for `have`.
|
|
const PEER_WIRE_HAVE_ID: u8 = 4;
|
|
/// Peer-wire message id for `bitfield`.
|
|
const PEER_WIRE_BITFIELD_ID: u8 = 5;
|
|
/// Peer-wire message id for `request`.
|
|
const PEER_WIRE_REQUEST_ID: u8 = 6;
|
|
/// Peer-wire message id for `piece`.
|
|
const PEER_WIRE_PIECE_ID: u8 = 7;
|
|
/// Peer-wire message id for `cancel`.
|
|
const PEER_WIRE_CANCEL_ID: u8 = 8;
|
|
/// Peer-wire message id for `port`.
|
|
const PEER_WIRE_PORT_ID: u8 = 9;
|
|
/// Peer-wire message id for extension-protocol payloads.
|
|
const PEER_WIRE_EXTENSION_ID: u8 = 20;
|