aquatic_http: check info_hash and peer_id len when deserializing

This commit is contained in:
Joakim Frostegård 2020-07-02 15:29:01 +02:00
parent 5e7f8bea20
commit c8de9857f8
3 changed files with 33 additions and 61 deletions

View file

@ -184,6 +184,13 @@ pub fn run_handshake_and_read_requests<'a>(
debug!("read request, sending to handler"); debug!("read request, sending to handler");
if let Request::Announce(ref request) = request {
for (i, c) in request.info_hash.0.chars().enumerate() {
debug!("{}: {}", i, c.escape_unicode());
}
debug!("request info hash char count: {}", request.info_hash.0.chars().count());
}
if let Err(err) = request_channel_sender if let Err(err) = request_channel_sender
.send((meta, request)) .send((meta, request))
{ {

View file

@ -4,9 +4,9 @@ use serde::{Serialize, Deserialize, Serializer};
use crate::common::Peer; use crate::common::Peer;
// mod serde_helpers; mod serde_helpers;
// use serde_helpers::*; use serde_helpers::*;
pub fn serialize_response_peers_compact<S>( pub fn serialize_response_peers_compact<S>(
@ -36,10 +36,9 @@ pub fn serialize_response_peers_compact<S>(
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)] #[serde(transparent)]
pub struct PeerId( pub struct PeerId(
// #[serde( #[serde(
// deserialize_with = "deserialize_20_bytes", deserialize_with = "deserialize_20_char_string",
// serialize_with = "serialize_20_bytes" )]
// )]
pub String pub String
); );
@ -47,10 +46,9 @@ pub struct PeerId(
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)] #[serde(transparent)]
pub struct InfoHash( pub struct InfoHash(
// #[serde( #[serde(
// deserialize_with = "deserialize_20_bytes", deserialize_with = "deserialize_20_char_string",
// serialize_with = "serialize_20_bytes" )]
// )]
pub String pub String
); );
@ -129,7 +127,10 @@ pub struct AnnounceResponseFailure {
#[derive(Debug, Clone, Deserialize)] #[derive(Debug, Clone, Deserialize)]
pub struct ScrapeRequest { pub struct ScrapeRequest {
#[serde(rename = "info_hash")] #[serde(
rename = "info_hash",
deserialize_with = "deserialize_info_hashes" // FIXME: does this work?
)]
pub info_hashes: Vec<InfoHash>, pub info_hashes: Vec<InfoHash>,
} }

View file

@ -3,71 +3,35 @@ use serde::{Serializer, Deserializer, de::{Visitor, SeqAccess}};
use super::InfoHash; use super::InfoHash;
pub fn serialize_20_bytes<S>( struct TwentyCharStringVisitor;
data: &[u8; 20],
serializer: S
) -> Result<S::Ok, S::Error> where S: Serializer {
let text: String = data.iter().map(|byte| *byte as char).collect();
serializer.serialize_str(&text) impl<'de> Visitor<'de> for TwentyCharStringVisitor {
} type Value = String;
struct TwentyByteVisitor;
impl<'de> Visitor<'de> for TwentyByteVisitor {
type Value = [u8; 20];
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("string consisting of 20 bytes") formatter.write_str("string consisting of 20 chars")
} }
#[inline] #[inline]
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where E: ::serde::de::Error, where E: ::serde::de::Error,
{ {
// Value is encoded in nodejs reference client something as follows: if value.chars().count() == 20 {
// ``` Ok(value.to_string())
// var infoHash = 'abcd..'; // 40 hexadecimals
// Buffer.from(infoHash, 'hex').toString('binary');
// ```
// As I understand it:
// - the code above produces a UTF16 string of 20 chars, each having
// only the "low byte" set (e.g., numeric value ranges from 0-255)
// - serde_json decodes this to string of 20 chars (tested), each in
// the aforementioned range (tested), so the bytes can be extracted
// by casting each char to u8.
let mut arr = [0u8; 20];
let mut char_iter = value.chars();
for a in arr.iter_mut(){
if let Some(c) = char_iter.next(){
if c as u32 > 255 {
return Err(E::custom(format!(
"character not in single byte range: {:#?}",
c
)));
}
*a = c as u8;
} else { } else {
return Err(E::custom(format!("not 20 bytes: {:#?}", value))); Err(E::custom(format!("not 20 chars: {:#?}", value)))
} }
} }
Ok(arr)
}
} }
#[inline] #[inline]
pub fn deserialize_20_bytes<'de, D>( pub fn deserialize_20_char_string<'de, D>(
deserializer: D deserializer: D
) -> Result<[u8; 20], D::Error> ) -> Result<String, D::Error>
where D: Deserializer<'de> where D: Deserializer<'de>
{ {
deserializer.deserialize_any(TwentyByteVisitor) deserializer.deserialize_any(TwentyCharStringVisitor)
} }
@ -85,7 +49,7 @@ impl<'de> Visitor<'de> for InfoHashVecVisitor {
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where E: ::serde::de::Error, where E: ::serde::de::Error,
{ {
match TwentyByteVisitor::visit_str::<E>(TwentyByteVisitor, value){ match TwentyCharStringVisitor::visit_str::<E>(TwentyCharStringVisitor, value){
Ok(arr) => Ok(vec![InfoHash(arr)]), Ok(arr) => Ok(vec![InfoHash(arr)]),
Err(err) => Err(E::custom(format!("got string, but {}", err))) Err(err) => Err(E::custom(format!("got string, but {}", err)))
} }
@ -98,8 +62,8 @@ impl<'de> Visitor<'de> for InfoHashVecVisitor {
let mut info_hashes: Self::Value = Vec::new(); let mut info_hashes: Self::Value = Vec::new();
while let Ok(Some(value)) = seq.next_element::<&str>(){ while let Ok(Some(value)) = seq.next_element::<&str>(){
let arr = TwentyByteVisitor::visit_str( let arr = TwentyCharStringVisitor::visit_str(
TwentyByteVisitor, value TwentyCharStringVisitor, value
)?; )?;
info_hashes.push(InfoHash(arr)); info_hashes.push(InfoHash(arr));