use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use crate::bencode::{self, Bencode, dict_get, dict_get_bytes, dict_get_int};
use crate::error::{Error, ErrorKind};
use crate::peer::PeerId;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum AnnounceEvent {
Started,
Stopped,
Completed,
None,
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct AnnounceRequest {
pub info_hash: [u8; 20],
pub peer_id: PeerId,
pub port: u16,
pub uploaded: u64,
pub downloaded: u64,
pub left: u64,
pub event: AnnounceEvent,
pub compact: bool,
pub numwant: Option<u32>,
pub key: Option<u32>,
pub trackerid: Option<String>,
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct AnnounceResponse {
pub interval: u32,
pub complete: u32,
pub incomplete: u32,
pub peers: Vec<SocketAddr>,
pub warning_message: Option<String>,
pub tracker_id: Option<String>,
pub min_interval: Option<u32>,
}
impl AnnounceRequest {
pub fn new(info_hash: [u8; 20], peer_id: PeerId, port: u16) -> Self {
AnnounceRequest {
info_hash,
peer_id,
port,
uploaded: 0,
downloaded: 0,
left: 0,
event: AnnounceEvent::None,
compact: true,
numwant: Some(50),
key: None,
trackerid: None,
}
}
}
impl AnnounceResponse {
pub fn from_bencode(data: &[u8]) -> Result<Self, Error> {
tracing::debug!("parsing tracker response");
let (val, _rest) = bencode::decode(data)?;
let interval_i64 =
dict_get_int(&val, b"interval").ok_or(Error::new(ErrorKind::TrackerInvalidResponse))?;
let interval = u32::try_from(interval_i64)
.map_err(|_| Error::new(ErrorKind::TrackerInvalidResponse))?;
let complete = dict_get_int(&val, b"complete")
.and_then(|v| u32::try_from(v).ok())
.unwrap_or(0);
let incomplete = dict_get_int(&val, b"incomplete")
.and_then(|v| u32::try_from(v).ok())
.unwrap_or(0);
let warning_message = dict_get(&val, b"warning message").and_then(|v| match v {
Bencode::Bytes(b) => String::from_utf8(b.to_vec()).ok(),
_ => None,
});
let tracker_id = dict_get(&val, b"tracker id").and_then(|v| match v {
Bencode::Bytes(b) => String::from_utf8(b.to_vec()).ok(),
_ => None,
});
let min_interval = dict_get_int(&val, b"min interval").and_then(|v| u32::try_from(v).ok());
let peers = parse_peers(&val)?;
Ok(AnnounceResponse {
interval,
complete,
incomplete,
peers,
warning_message,
tracker_id,
min_interval,
})
}
pub fn from_udp_fields(
interval: u32, complete: u32, incomplete: u32, peers: Vec<SocketAddr>,
) -> Self {
AnnounceResponse {
interval,
complete,
incomplete,
peers,
warning_message: None,
tracker_id: None,
min_interval: None,
}
}
}
fn parse_peers(val: &Bencode) -> Result<Vec<SocketAddr>, Error> {
if let Some(bytes) = dict_get_bytes(val, b"peers")
&& !bytes.is_empty()
{
return parse_compact_peers_ipv4(bytes);
}
if let Some(bytes) = dict_get_bytes(val, b"peers6")
&& !bytes.is_empty()
{
let mut peers = Vec::with_capacity(bytes.len() / 18);
for chunk in bytes.chunks_exact(18) {
let mut ip_bytes = [0u8; 16];
ip_bytes.copy_from_slice(&chunk[..16]);
let ip = Ipv6Addr::from(ip_bytes);
let port = u16::from_be_bytes([chunk[16], chunk[17]]);
peers.push(SocketAddr::new(IpAddr::V6(ip), port));
}
if !bytes.chunks_exact(18).remainder().is_empty() {
return Err(Error::new(ErrorKind::TrackerInvalidResponse));
}
return Ok(peers);
}
if let Some(Bencode::List(peer_list)) = dict_get(val, b"peers") {
let mut peers = Vec::with_capacity(peer_list.len());
for peer in peer_list {
let ip_str = dict_get(peer, b"ip")
.and_then(|v| match v {
Bencode::Bytes(b) => String::from_utf8(b.to_vec()).ok(),
_ => None,
})
.unwrap_or_default();
let port = dict_get_int(peer, b"port").unwrap_or(0) as u16;
if let Ok(ip) = ip_str.parse::<IpAddr>() {
peers.push(SocketAddr::new(ip, port));
} else if let Some(Bencode::Bytes(b)) = dict_get(peer, b"ip")
&& b.len() == 4
{
let ip = Ipv4Addr::new(b[0], b[1], b[2], b[3]);
peers.push(SocketAddr::new(IpAddr::V4(ip), port));
}
}
return Ok(peers);
}
Ok(Vec::new())
}
pub fn parse_compact_peers_ipv4(data: &[u8]) -> Result<Vec<SocketAddr>, Error> {
if !data.len().is_multiple_of(6) {
return Err(Error::new(ErrorKind::TrackerInvalidResponse));
}
data.chunks_exact(6)
.map(|chunk| {
let ip = Ipv4Addr::new(chunk[0], chunk[1], chunk[2], chunk[3]);
let port = u16::from_be_bytes([chunk[4], chunk[5]]);
Ok(SocketAddr::new(IpAddr::V4(ip), port))
})
.collect()
}
pub fn encode_compact_peers_ipv4(addrs: &[SocketAddr]) -> Vec<u8> {
let mut buf = Vec::with_capacity(addrs.len() * 6);
for addr in addrs {
if let IpAddr::V4(ip) = addr.ip() {
buf.extend_from_slice(&ip.octets());
buf.extend_from_slice(&addr.port().to_be_bytes());
}
}
buf
}
pub fn parse_compact_peers_ipv6(data: &[u8]) -> Result<Vec<SocketAddr>, Error> {
if !data.len().is_multiple_of(18) {
return Err(Error::new(ErrorKind::TrackerInvalidResponse));
}
data.chunks_exact(18)
.map(|chunk| {
let mut ip_bytes = [0u8; 16];
ip_bytes.copy_from_slice(&chunk[..16]);
let ip = Ipv6Addr::from(ip_bytes);
let port = u16::from_be_bytes([chunk[16], chunk[17]]);
Ok(SocketAddr::new(IpAddr::V6(ip), port))
})
.collect()
}
pub fn encode_compact_peers_ipv6(addrs: &[SocketAddr]) -> Vec<u8> {
let mut buf = Vec::with_capacity(addrs.len() * 18);
for addr in addrs {
if let IpAddr::V6(ip) = addr.ip() {
buf.extend_from_slice(&ip.octets());
buf.extend_from_slice(&addr.port().to_be_bytes());
}
}
buf
}
#[cfg(test)]
mod compact_tests {
use super::*;
use std::net::Ipv4Addr;
#[test]
fn encode_compact_peers_ipv4_roundtrip() {
let addrs: Vec<SocketAddr> = vec![
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 6881),
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)), 6889),
];
let encoded = encode_compact_peers_ipv4(&addrs);
let decoded = parse_compact_peers_ipv4(&encoded).unwrap();
assert_eq!(addrs, decoded);
}
#[test]
fn encode_compact_peers_ipv4_empty() {
let encoded = encode_compact_peers_ipv4(&[]);
assert!(encoded.is_empty());
}
#[test]
fn encode_compact_peers_ipv4_skips_ipv6() {
use std::net::Ipv6Addr;
let ipv4 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)), 6881);
let ipv6 = SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 6881);
let encoded = encode_compact_peers_ipv4(&[ipv4, ipv6]);
assert_eq!(encoded.len(), 6);
}
#[test]
fn encode_compact_peers_ipv6_roundtrip() {
use std::net::Ipv6Addr;
let addrs: Vec<SocketAddr> = vec![
SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 6881),
SocketAddr::new(
IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1)),
6889,
),
];
let encoded = encode_compact_peers_ipv6(&addrs);
let decoded = parse_compact_peers_ipv6(&encoded).unwrap();
assert_eq!(addrs, decoded);
}
#[test]
fn encode_compact_peers_ipv6_empty() {
let encoded = encode_compact_peers_ipv6(&[]);
assert!(encoded.is_empty());
}
#[test]
fn encode_compact_peers_ipv6_skips_ipv4() {
let ipv4 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)), 6881);
let ipv6 = SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 6881);
let encoded = encode_compact_peers_ipv6(&[ipv4, ipv6]);
assert_eq!(encoded.len(), 18);
}
}