use std::{collections::BTreeMap, fmt};
use crate::protocol::bencode::{self, Limits, Node, Value};
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Error {
Bencode(bencode::Error),
Dictionary,
ConflictingId(u8),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "extension handshake {self:?}")
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Bencode(error) => Some(error),
_ => None,
}
}
}
#[derive(Clone, Debug)]
pub struct Handshake<'a> {
document: Node<'a>,
}
impl<'a> Handshake<'a> {
pub fn parse(input: &'a [u8], limits: Limits) -> Result<Self, Error> {
Self::from_node(bencode::decode(input, limits).map_err(Error::Bencode)?)
}
pub fn from_node(document: Node<'a>) -> Result<Self, Error> {
if !matches!(document.value(), Value::Dictionary(_)) {
return Err(Error::Dictionary);
}
Ok(Self { document })
}
pub fn document(&self) -> &Node<'a> {
&self.document
}
pub fn encode(&self, limits: Limits) -> Result<Vec<u8>, Error> {
bencode::encode(&self.document, limits).map_err(Error::Bencode)
}
pub fn extensions(&self) -> impl Iterator<Item = (&'a [u8], u8)> + '_ {
self.document
.get(b"m")
.and_then(|node| match node.value() {
Value::Dictionary(entries) => Some(entries),
_ => None,
})
.into_iter()
.flat_map(|entries| entries.iter())
.filter_map(|(&name, node)| Some((name, u8::try_from(integer(node)?).ok()?)))
}
pub fn extension_id(&self, name: &[u8]) -> Option<u8> {
u8::try_from(integer(self.document.get(b"m")?.get(name)?)?).ok()
}
pub fn port(&self) -> Option<u16> {
u16::try_from(integer(self.document.get(b"p")?)?).ok()
}
pub fn request_queue(&self) -> Option<u32> {
u32::try_from(integer(self.document.get(b"reqq")?)?).ok()
}
pub fn metadata_size(&self) -> Option<u32> {
u32::try_from(integer(self.document.get(b"metadata_size")?)?).ok()
}
pub fn client(&self) -> Option<&'a [u8]> {
match self.document.get(b"v")?.value() {
Value::Bytes(bytes) => Some(bytes),
_ => None,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ExtensionMap {
entries: BTreeMap<Vec<u8>, u8>,
}
impl ExtensionMap {
pub fn new(entries: &[(&[u8], u8)]) -> Result<Self, Error> {
let entries: BTreeMap<_, _> = entries
.iter()
.map(|(name, id)| (name.to_vec(), *id))
.collect();
unique_ids(entries.values().copied())?;
Ok(Self { entries })
}
pub fn apply(&mut self, handshake: &Handshake<'_>) -> Result<(), Error> {
let next: Vec<_> = self
.entries
.iter()
.map(|(name, id)| handshake.extension_id(name).unwrap_or(*id))
.collect();
unique_ids(next.iter().copied())?;
for (id, next) in self.entries.values_mut().zip(next) {
*id = next;
}
Ok(())
}
pub fn id_for(&self, name: &[u8]) -> Option<u8> {
self.entries.get(name).copied().filter(|&id| id != 0)
}
pub fn name_for(&self, id: u8) -> Option<&[u8]> {
if id == 0 {
return None;
}
self.entries
.iter()
.find_map(|(name, &value)| (value == id).then_some(name.as_slice()))
}
pub fn handshake(&self) -> Handshake<'_> {
let entries = self
.entries
.iter()
.map(|(name, &id)| (name.as_slice(), Node::new(Value::Integer(i64::from(id)))))
.collect();
Handshake {
document: Node::new(Value::Dictionary(BTreeMap::from([(
b"m".as_slice(),
Node::new(Value::Dictionary(entries)),
)]))),
}
}
}
fn integer(node: &Node<'_>) -> Option<i64> {
match node.value() {
Value::Integer(value) => Some(*value),
_ => None,
}
}
fn unique_ids(ids: impl Iterator<Item = u8>) -> Result<(), Error> {
let mut used = [false; 256];
for id in ids.filter(|&id| id != 0) {
if used[usize::from(id)] {
return Err(Error::ConflictingId(id));
}
used[usize::from(id)] = true;
}
Ok(())
}