use crate::common::*;
#[derive(Debug, PartialEq)]
pub(crate) struct Request {
pub(crate) connection_id: u64, pub(crate) action: u32, pub(crate) transaction_id: u32, pub(crate) infohash: [u8; 20], pub(crate) peer_id: [u8; 20], pub(crate) downloaded: u64, pub(crate) left: u64, pub(crate) uploaded: u64, pub(crate) event: u64, pub(crate) ip_address: u32, pub(crate) num_want: u32, pub(crate) port: u16, }
impl Request {
pub(crate) const LENGTH: usize = 98;
pub(crate) fn new(connection_id: u64, btinh: Infohash, peer_id: [u8; 20], port: u16) -> Self {
let mut rng = rand::rng();
Self {
connection_id,
action: tracker::Action::Announce.into(),
transaction_id: rng.random(),
infohash: btinh.into(),
peer_id,
downloaded: 0x0000,
left: u64::MAX,
uploaded: 0x0000,
event: 0x0000,
ip_address: 0x0000,
num_want: u32::MAX,
port,
}
}
}
#[derive(Debug, PartialEq)]
pub(crate) struct Response {
pub(crate) action: u32, pub(crate) transaction_id: u32, pub(crate) interval: u32, pub(crate) leechers: u32, pub(crate) seeders: u32, }
impl Response {
pub(crate) const LENGTH: usize = 20;
}
impl super::Request for Request {
type Response = Response;
fn serialize(&self) -> Vec<u8> {
let mut msg = Vec::new();
msg.extend_from_slice(&self.connection_id.to_be_bytes());
msg.extend_from_slice(&self.action.to_be_bytes());
msg.extend_from_slice(&self.transaction_id.to_be_bytes());
msg.extend_from_slice(&self.infohash);
msg.extend_from_slice(&self.peer_id);
msg.extend_from_slice(&self.downloaded.to_be_bytes());
msg.extend_from_slice(&self.left.to_be_bytes());
msg.extend_from_slice(&self.uploaded.to_be_bytes());
msg.extend_from_slice(&self.event.to_be_bytes());
msg.extend_from_slice(&self.ip_address.to_be_bytes());
msg.extend_from_slice(&self.num_want.to_be_bytes());
msg.extend_from_slice(&self.port.to_be_bytes());
msg
}
fn transaction_id(&self) -> u32 {
self.transaction_id
}
fn action(&self) -> u32 {
self.action
}
}
impl super::Response for Request {
fn deserialize(buf: &[u8]) -> Result<(Self, &[u8])> {
if buf.len() != Request::LENGTH {
return Err(Error::TrackerResponseLength {
got: buf.len(),
want: Request::LENGTH,
});
}
Ok((
Request {
connection_id: u64::from_be_bytes(
buf[0..8]
.try_into()
.invariant_unwrap("buf size is at least Request::LENGTH"),
),
action: u32::from_be_bytes(
buf[8..12]
.try_into()
.invariant_unwrap("buf size is at least Request::LENGTH"),
),
transaction_id: u32::from_be_bytes(
buf[12..16]
.try_into()
.invariant_unwrap("buf size is at least Request::LENGTH"),
),
infohash: buf[16..36]
.try_into()
.invariant_unwrap("buf size is at least Request::LENGTH"),
peer_id: buf[36..56]
.try_into()
.invariant_unwrap("buf size is at least Request::LENGTH"),
downloaded: u64::from_be_bytes(
buf[56..64]
.try_into()
.invariant_unwrap("buf size is at least Request::LENGTH"),
),
left: u64::from_be_bytes(
buf[64..72]
.try_into()
.invariant_unwrap("buf size is at least Request::LENGTH"),
),
uploaded: u64::from_be_bytes(
buf[72..80]
.try_into()
.invariant_unwrap("buf size is at least Request::LENGTH"),
),
event: u64::from_be_bytes(
buf[80..88]
.try_into()
.invariant_unwrap("buf size is at least Request::LENGTH"),
),
ip_address: u32::from_be_bytes(
buf[88..92]
.try_into()
.invariant_unwrap("buf size is at least Request::LENGTH"),
),
num_want: u32::from_be_bytes(
buf[92..96]
.try_into()
.invariant_unwrap("buf size is at least Request::LENGTH"),
),
port: u16::from_be_bytes(
buf[96..98]
.try_into()
.invariant_unwrap("buf size is at least Request::LENGTH"),
),
},
&buf[Self::LENGTH..],
))
}
fn transaction_id(&self) -> u32 {
self.transaction_id
}
fn action(&self) -> u32 {
self.action
}
}
impl super::Response for Response {
fn deserialize(buf: &[u8]) -> Result<(Self, &[u8])> {
if buf.len() < Response::LENGTH {
return Err(Error::TrackerResponseLength {
want: Response::LENGTH,
got: buf.len(),
});
}
Ok((
Response {
action: u32::from_be_bytes(
buf[0..4]
.try_into()
.invariant_unwrap("buf size is at least Response::LENGTH"),
),
transaction_id: u32::from_be_bytes(
buf[4..8]
.try_into()
.invariant_unwrap("buf size is at least Response::LENGTH"),
),
interval: u32::from_be_bytes(
buf[8..12]
.try_into()
.invariant_unwrap("buf size is at least Response::LENGTH"),
),
leechers: u32::from_be_bytes(
buf[12..16]
.try_into()
.invariant_unwrap("buf size is at least Response::LENGTH"),
),
seeders: u32::from_be_bytes(
buf[16..20]
.try_into()
.invariant_unwrap("buf size is at least Response::LENGTH"),
),
},
&buf[Self::LENGTH..],
))
}
fn transaction_id(&self) -> u32 {
self.transaction_id
}
fn action(&self) -> u32 {
self.action
}
}
impl super::Request for Response {
type Response = Request;
#[allow(dead_code)]
fn serialize(&self) -> Vec<u8> {
let mut msg = Vec::new();
msg.extend_from_slice(&self.action.to_be_bytes());
msg.extend_from_slice(&self.transaction_id.to_be_bytes());
msg.extend_from_slice(&self.interval.to_be_bytes());
msg.extend_from_slice(&self.leechers.to_be_bytes());
msg.extend_from_slice(&self.seeders.to_be_bytes());
msg
}
fn transaction_id(&self) -> u32 {
self.transaction_id
}
fn action(&self) -> u32 {
self.action
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tracker::{announce, request::Request, response::Response};
#[test]
pub(crate) fn announce_request_roundtrip() {
let req = announce::Request {
connection_id: 0x01,
action: 0x02,
transaction_id: 0x03,
infohash: [0x04; 20],
peer_id: [0x05; 20],
downloaded: 0x06,
left: 0x07,
uploaded: 0x08,
event: 0x09,
ip_address: 0x0a,
num_want: 0x0b,
port: 0x0c,
};
let buf = req.serialize();
let (req2, _) = announce::Request::deserialize(&buf).unwrap();
assert_eq!(req, req2);
}
#[test]
pub(crate) fn announce_response_roundtrip() {
let resp = announce::Response {
action: 0x01,
transaction_id: 0x02,
interval: 0x03,
leechers: 0x04,
seeders: 0x05,
};
let buf = resp.serialize();
let (resp2, _) = announce::Response::deserialize(&buf).unwrap();
assert_eq!(resp, resp2);
}
#[test]
pub(crate) fn announce_request_bad_deserialize() {
let buf = [0x01, 0x02, 0x03, 0x04, 0x05];
let err = announce::Request::deserialize(&buf);
assert_matches!(err, Err(Error::TrackerResponseLength { .. }));
}
#[test]
pub(crate) fn announce_response_bad_deserialize() {
let buf = [0x01, 0x02, 0x03, 0x04, 0x05];
let err = announce::Response::deserialize(&buf);
assert_matches!(err, Err(Error::TrackerResponseLength { .. }));
}
}