net: configure maximum message name length to prevent possible dos vector

This commit is contained in:
oars
2025-03-30 17:54:33 +03:00
parent bafd7c4089
commit cfeb874fda
4 changed files with 21 additions and 7 deletions

View File

@@ -29,6 +29,7 @@ use std::{
use darkfi_serial::{
async_trait, AsyncDecodable, AsyncEncodable, SerialDecodable, SerialEncodable, VarInt,
};
use log::{debug, error, info, trace, warn};
use rand::{rngs::OsRng, Rng};
use smol::{
@@ -42,7 +43,7 @@ use super::{
dnet::{self, dnetev, DnetEvent},
hosts::{HostColor, HostsPtr},
message,
message::{SerializedMessage, VersionMessage},
message::{SerializedMessage, VersionMessage, MAX_COMMAND_LENGTH},
message_publisher::{MessageSubscription, MessageSubsystem},
metering::{MeteringConfiguration, MeteringQueue},
p2p::P2pPtr,
@@ -207,7 +208,7 @@ impl Channel {
/// Sends the encoded payload of provided `SerializedMessage` across the channel.
///
/// We first check if we should apply some throttling, based on the provided
/// `Message` configuration. We always sleep 2x times more that the exepted one,
/// `Message` configuration. We always sleep 2x times more than the expected one,
/// so we don't flood the peer.
/// Then, calls `send_message` that creates a new payload and sends it over the
/// network transport as a packet.
@@ -334,6 +335,11 @@ impl Channel {
// First extract the length from the stream
let cmd_len = VarInt::decode_async(stream).await?.0;
if cmd_len > (MAX_COMMAND_LENGTH as u64) {
error!(target: "net::channel::read_command",
"Error: Command length ({cmd_len}) exceeds configured limit ({MAX_COMMAND_LENGTH}). Dropping...");
return Err(Error::MessageInvalid);
}
// Then extract precisely `cmd_len` items from the stream.
let mut take = stream.take(cmd_len);
@@ -401,6 +407,11 @@ impl Channel {
"[P2P] Channel {} disconnected",
self.address()
);
} else if let Error::MessageInvalid = err {
// The command name length has exceeded the limit, this is possibly a malicious attack so ban it
if let BanPolicy::Strict = self.p2p().settings().read().await.ban_policy {
self.ban().await;
}
} else if self.session.upgrade().unwrap().type_id() &
(SESSION_ALL & !SESSION_REFINE) !=
0
@@ -451,7 +462,7 @@ impl Channel {
if self.session.upgrade().unwrap().type_id() != SESSION_REFINE {
warn!(
target: "net::channel::main_receive_loop()",
"MissingDispatcher|MessageInvalid|MeteringLimitExcheeded for command={command}, channel={self:?}"
"MissingDispatcher|MessageInvalid|MeteringLimitExceeded for command={command}, channel={self:?}"
);
if let BanPolicy::Strict = self.p2p().settings().read().await.ban_policy {

View File

@@ -63,6 +63,9 @@ macro_rules! impl_p2p_message {
};
}
/// Maximum command (message name) length in bytes
pub const MAX_COMMAND_LENGTH: u8 = 255;
/// Outbound keepalive message.
#[derive(Debug, Copy, Clone, SerialEncodable, SerialDecodable)]
pub struct PingMessage {
@@ -77,12 +80,12 @@ pub struct PongMessage {
}
impl_p2p_message!(PongMessage, "pong", 0, 0, DEFAULT_METERING_CONFIGURATION);
/// Requests address of outbound connecction.
/// Requests address of outbound connection.
#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
pub struct GetAddrsMessage {
/// Maximum number of addresses with preferred
/// transports to receive. Response vector will
/// also containg addresses without the preferred
/// also contain addresses without the preferred
/// transports, so its size will be 2 * max.
pub max: u32,
/// Preferred addresses transports

View File

@@ -241,7 +241,7 @@ impl<M: Message> MessageDispatcherInterface for MessageDispatcher<M> {
/// from an inbound stream.
///
/// We extract the message length from the stream and use `take()`
/// to allocate an appropiately sized buffer as a basic DDOS protection.
/// to allocate an appropriately sized buffer as a basic DDOS protection.
async fn trigger(
&self,
stream: &mut smol::io::ReadHalf<Box<dyn PtStream + 'static>>,

View File

@@ -114,7 +114,7 @@ impl MeteringQueue {
/// Add new metering value to the queue, after
/// prunning expired metering information.
/// If no thresshold has been set, the insert is
/// If no threshold has been set, the insert is
/// ignored.
pub fn push(&mut self, value: &u64) {
// Check if threshold has been set