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");
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
.send((meta, request))
{

View file

@ -4,9 +4,9 @@ use serde::{Serialize, Deserialize, Serializer};
use crate::common::Peer;
// mod serde_helpers;
mod serde_helpers;
// use serde_helpers::*;
use serde_helpers::*;
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)]
#[serde(transparent)]
pub struct PeerId(
// #[serde(
// deserialize_with = "deserialize_20_bytes",
// serialize_with = "serialize_20_bytes"
// )]
#[serde(
deserialize_with = "deserialize_20_char_string",
)]
pub String
);
@ -47,10 +46,9 @@ pub struct PeerId(
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct InfoHash(
// #[serde(
// deserialize_with = "deserialize_20_bytes",
// serialize_with = "serialize_20_bytes"
// )]
#[serde(
deserialize_with = "deserialize_20_char_string",
)]
pub String
);
@ -129,7 +127,10 @@ pub struct AnnounceResponseFailure {
#[derive(Debug, Clone, Deserialize)]
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>,
}

View file

@ -3,71 +3,35 @@ use serde::{Serializer, Deserializer, de::{Visitor, SeqAccess}};
use super::InfoHash;
pub fn serialize_20_bytes<S>(
data: &[u8; 20],
serializer: S
) -> Result<S::Ok, S::Error> where S: Serializer {
let text: String = data.iter().map(|byte| *byte as char).collect();
struct TwentyCharStringVisitor;
serializer.serialize_str(&text)
}
struct TwentyByteVisitor;
impl<'de> Visitor<'de> for TwentyByteVisitor {
type Value = [u8; 20];
impl<'de> Visitor<'de> for TwentyCharStringVisitor {
type Value = String;
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]
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where E: ::serde::de::Error,
{
// Value is encoded in nodejs reference client something as follows:
// ```
// 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 {
return Err(E::custom(format!("not 20 bytes: {:#?}", value)));
}
if value.chars().count() == 20 {
Ok(value.to_string())
} else {
Err(E::custom(format!("not 20 chars: {:#?}", value)))
}
Ok(arr)
}
}
#[inline]
pub fn deserialize_20_bytes<'de, D>(
pub fn deserialize_20_char_string<'de, D>(
deserializer: D
) -> Result<[u8; 20], D::Error>
) -> Result<String, D::Error>
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>
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)]),
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();
while let Ok(Some(value)) = seq.next_element::<&str>(){
let arr = TwentyByteVisitor::visit_str(
TwentyByteVisitor, value
let arr = TwentyCharStringVisitor::visit_str(
TwentyCharStringVisitor, value
)?;
info_hashes.push(InfoHash(arr));