aquatic_udp: improve request parse errors, send less error responses

This commit is contained in:
Joakim Frostegård 2021-10-18 02:10:39 +02:00
parent bfab6bb48f
commit f0a15e9b6f
4 changed files with 78 additions and 67 deletions

1
Cargo.lock generated
View file

@ -216,6 +216,7 @@ name = "aquatic_udp_protocol"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"byteorder", "byteorder",
"either",
"quickcheck", "quickcheck",
"quickcheck_macros", "quickcheck_macros",
] ]

View file

@ -216,21 +216,18 @@ fn read_requests(
} }
} }
Err(err) => { Err(err) => {
::log::debug!("request_from_bytes error: {:?}", err); ::log::debug!("Request::from_bytes error: {:?}", err);
if let Some(transaction_id) = err.transaction_id { if let RequestParseError::Sendable {
let opt_message = if err.error.is_some() { connection_id,
Some("Parse error".into()) transaction_id,
} else if let Some(message) = err.message { err,
Some(message.into()) } = err
} else { {
None if connections.contains_key(&ConnectionKey::new(connection_id, src)) {
};
if let Some(message) = opt_message {
let response = ErrorResponse { let response = ErrorResponse {
transaction_id, transaction_id,
message, message: err.right_or("Parse error").into(),
}; };
local_responses.push((response.into(), src)); local_responses.push((response.into(), src));

View file

@ -9,6 +9,7 @@ repository = "https://github.com/greatest-ape/aquatic"
[dependencies] [dependencies]
byteorder = "1" byteorder = "1"
either = "1"
[dev-dependencies] [dev-dependencies]
quickcheck = "1.0" quickcheck = "1.0"

View file

@ -3,6 +3,7 @@ use std::io::{self, Cursor, Read, Write};
use std::net::Ipv4Addr; use std::net::Ipv4Addr;
use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt}; use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt};
use either::Either;
use super::common::*; use super::common::*;
@ -67,32 +68,40 @@ pub struct ScrapeRequest {
} }
#[derive(Debug)] #[derive(Debug)]
pub struct RequestParseError { pub enum RequestParseError {
pub transaction_id: Option<TransactionId>, Sendable {
pub message: Option<String>, connection_id: ConnectionId,
pub error: Option<io::Error>, transaction_id: TransactionId,
err: Either<io::Error, &'static str>,
},
Unsendable {
err: Either<io::Error, &'static str>,
},
} }
impl RequestParseError { impl RequestParseError {
pub fn new(err: io::Error, transaction_id: i32) -> Self { pub fn sendable_io(err: io::Error, connection_id: i64, transaction_id: i32) -> Self {
Self { Self::Sendable {
transaction_id: Some(TransactionId(transaction_id)), connection_id: ConnectionId(connection_id),
message: None, transaction_id: TransactionId(transaction_id),
error: Some(err), err: Either::Left(err),
} }
} }
pub fn io(err: io::Error) -> Self { pub fn sendable_text(text: &'static str, connection_id: i64, transaction_id: i32) -> Self {
Self { Self::Sendable {
transaction_id: None, connection_id: ConnectionId(connection_id),
message: None, transaction_id: TransactionId(transaction_id),
error: Some(err), err: Either::Right(text),
} }
} }
pub fn text(transaction_id: i32, message: &str) -> Self { pub fn unsendable_io(err: io::Error) -> Self {
Self { Self::Unsendable {
transaction_id: Some(TransactionId(transaction_id)), err: Either::Left(err),
message: Some(message.to_string()), }
error: None, }
pub fn unsendable_text(text: &'static str) -> Self {
Self::Unsendable {
err: Either::Right(text),
} }
} }
} }
@ -171,13 +180,13 @@ impl Request {
let connection_id = cursor let connection_id = cursor
.read_i64::<NetworkEndian>() .read_i64::<NetworkEndian>()
.map_err(RequestParseError::io)?; .map_err(RequestParseError::unsendable_io)?;
let action = cursor let action = cursor
.read_i32::<NetworkEndian>() .read_i32::<NetworkEndian>()
.map_err(RequestParseError::io)?; .map_err(RequestParseError::unsendable_io)?;
let transaction_id = cursor let transaction_id = cursor
.read_i32::<NetworkEndian>() .read_i32::<NetworkEndian>()
.map_err(RequestParseError::io)?; .map_err(RequestParseError::unsendable_io)?;
match action { match action {
// Connect // Connect
@ -188,8 +197,7 @@ impl Request {
}) })
.into()) .into())
} else { } else {
Err(RequestParseError::text( Err(RequestParseError::unsendable_text(
transaction_id,
"Protocol identifier missing", "Protocol identifier missing",
)) ))
} }
@ -201,39 +209,39 @@ impl Request {
let mut peer_id = [0; 20]; let mut peer_id = [0; 20];
let mut ip = [0; 4]; let mut ip = [0; 4];
cursor cursor.read_exact(&mut info_hash).map_err(|err| {
.read_exact(&mut info_hash) RequestParseError::sendable_io(err, connection_id, transaction_id)
.map_err(|err| RequestParseError::new(err, transaction_id))?; })?;
cursor cursor.read_exact(&mut peer_id).map_err(|err| {
.read_exact(&mut peer_id) RequestParseError::sendable_io(err, connection_id, transaction_id)
.map_err(|err| RequestParseError::new(err, transaction_id))?; })?;
let bytes_downloaded = cursor let bytes_downloaded = cursor.read_i64::<NetworkEndian>().map_err(|err| {
.read_i64::<NetworkEndian>() RequestParseError::sendable_io(err, connection_id, transaction_id)
.map_err(|err| RequestParseError::new(err, transaction_id))?; })?;
let bytes_left = cursor let bytes_left = cursor.read_i64::<NetworkEndian>().map_err(|err| {
.read_i64::<NetworkEndian>() RequestParseError::sendable_io(err, connection_id, transaction_id)
.map_err(|err| RequestParseError::new(err, transaction_id))?; })?;
let bytes_uploaded = cursor let bytes_uploaded = cursor.read_i64::<NetworkEndian>().map_err(|err| {
.read_i64::<NetworkEndian>() RequestParseError::sendable_io(err, connection_id, transaction_id)
.map_err(|err| RequestParseError::new(err, transaction_id))?; })?;
let event = cursor let event = cursor.read_i32::<NetworkEndian>().map_err(|err| {
.read_i32::<NetworkEndian>() RequestParseError::sendable_io(err, connection_id, transaction_id)
.map_err(|err| RequestParseError::new(err, transaction_id))?; })?;
cursor cursor.read_exact(&mut ip).map_err(|err| {
.read_exact(&mut ip) RequestParseError::sendable_io(err, connection_id, transaction_id)
.map_err(|err| RequestParseError::new(err, transaction_id))?; })?;
let key = cursor let key = cursor.read_u32::<NetworkEndian>().map_err(|err| {
.read_u32::<NetworkEndian>() RequestParseError::sendable_io(err, connection_id, transaction_id)
.map_err(|err| RequestParseError::new(err, transaction_id))?; })?;
let peers_wanted = cursor let peers_wanted = cursor.read_i32::<NetworkEndian>().map_err(|err| {
.read_i32::<NetworkEndian>() RequestParseError::sendable_io(err, connection_id, transaction_id)
.map_err(|err| RequestParseError::new(err, transaction_id))?; })?;
let port = cursor let port = cursor.read_u16::<NetworkEndian>().map_err(|err| {
.read_u16::<NetworkEndian>() RequestParseError::sendable_io(err, connection_id, transaction_id)
.map_err(|err| RequestParseError::new(err, transaction_id))?; })?;
let opt_ip = if ip == [0; 4] { let opt_ip = if ip == [0; 4] {
None None
@ -277,7 +285,11 @@ impl Request {
.into()) .into())
} }
_ => Err(RequestParseError::text(transaction_id, "Invalid action")), _ => Err(RequestParseError::sendable_text(
"Invalid action",
connection_id,
transaction_id,
)),
} }
} }
} }