Ban for 60 seconds after 3 errors, adjust ban times

This commit is contained in:
Mike Dilger 2024-02-19 20:20:54 +13:00
parent b85cd9297c
commit dbf7313d2b
3 changed files with 48 additions and 20 deletions

View File

@ -109,6 +109,9 @@ pub enum ChorusError {
// Filter is underspecified // Filter is underspecified
Scraper, Scraper,
// Too many errors
TooManyErrors,
// URL Parse // URL Parse
UrlParse(url::ParseError), UrlParse(url::ParseError),
@ -166,6 +169,7 @@ impl std::fmt::Display for ChorusError {
ChorusError::Rustls(e) => write!(f, "{e}"), ChorusError::Rustls(e) => write!(f, "{e}"),
ChorusError::Tungstenite(e) => write!(f, "{e}"), ChorusError::Tungstenite(e) => write!(f, "{e}"),
ChorusError::Scraper => write!(f, "Filter is underspecified. Scrapers are not allowed"), ChorusError::Scraper => write!(f, "Filter is underspecified. Scrapers are not allowed"),
ChorusError::TooManyErrors => write!(f, "Too many errors"),
ChorusError::UrlParse(e) => write!(f, "{e}"), ChorusError::UrlParse(e) => write!(f, "{e}"),
ChorusError::Utf8(e) => write!(f, "{e}"), ChorusError::Utf8(e) => write!(f, "{e}"),
ChorusError::Utf8Error => write!(f, "UTF-8 error"), ChorusError::Utf8Error => write!(f, "UTF-8 error"),

View File

@ -49,3 +49,14 @@ lazy_static! {
} }
}; };
} }
impl Globals {
pub async fn ban(ipaddr: std::net::IpAddr, seconds: u64) {
let mut until = Time::now();
until.0 += seconds;
if let Some(current_ban) = GLOBALS.banlist.read().await.get(&ipaddr) {
until.0 = current_ban.0.max(until.0);
}
GLOBALS.banlist.write().await.insert(ipaddr, until);
}
}

View File

@ -12,7 +12,7 @@ pub mod web;
use crate::config::{Config, FriendlyConfig}; use crate::config::{Config, FriendlyConfig};
use crate::error::{ChorusError, Error}; use crate::error::{ChorusError, Error};
use crate::globals::GLOBALS; use crate::globals::{Globals, GLOBALS};
use crate::reply::NostrReply; use crate::reply::NostrReply;
use crate::store::Store; use crate::store::Store;
use crate::tls::MaybeTlsStream; use crate::tls::MaybeTlsStream;
@ -251,6 +251,7 @@ async fn handle_http_request(
websocket, websocket,
challenge: TextNonce::new().into_string(), challenge: TextNonce::new().into_string(),
user: None, user: None,
errcount: 0,
}; };
// Increment count of active websockets // Increment count of active websockets
@ -262,17 +263,26 @@ async fn handle_http_request(
old_num_websockets + 1 old_num_websockets + 1
); );
// Everybody gets a 4-second ban on disconnect to prevent
// rapid reconnection
let mut ban_seconds: u64 = 4;
// Handle the websocket // Handle the websocket
if let Err(e) = ws_service.handle_websocket_stream().await { if let Err(e) = ws_service.handle_websocket_stream().await {
if matches!( match e.inner {
e.inner,
ChorusError::Tungstenite(tungstenite::error::Error::Protocol( ChorusError::Tungstenite(tungstenite::error::Error::Protocol(
tungstenite::error::ProtocolError::ResetWithoutClosingHandshake tungstenite::error::ProtocolError::ResetWithoutClosingHandshake,
)) )) => {
) { // So they disconnected ungracefully.
// Swallow the boring error // No big deal, no extra ban for that.
} else { }
log::error!("{}: {}", peer, e); ChorusError::TooManyErrors => {
ban_seconds = 60;
}
_ => {
log::error!("{}: {}", peer, e);
ban_seconds = 15;
}
} }
} }
@ -285,14 +295,8 @@ async fn handle_http_request(
old_num_websockets - 1 old_num_websockets - 1
); );
// Everybody gets a 4 second ban on disconnect, to prevent rapid reconnection // Ban for the appropriate duration
let ipaddr = peer.ip(); Globals::ban(peer.ip(), ban_seconds).await;
let mut until = Time::now();
until.0 += 4;
if let Some(current_ban) = GLOBALS.banlist.read().await.get(&ipaddr) {
until.0 = current_ban.0.max(until.0);
}
GLOBALS.banlist.write().await.insert(ipaddr, until);
} }
Err(e) => { Err(e) => {
log::error!("{}", e); log::error!("{}", e);
@ -321,6 +325,7 @@ struct WebSocketService {
pub websocket: WebSocketStream<Upgraded>, pub websocket: WebSocketStream<Upgraded>,
pub challenge: String, pub challenge: String,
pub user: Option<Pubkey>, pub user: Option<Pubkey>,
pub errcount: usize,
} }
impl WebSocketService { impl WebSocketService {
@ -392,13 +397,21 @@ impl WebSocketService {
async fn handle_websocket_message(&mut self, message: Message) -> Result<(), Error> { async fn handle_websocket_message(&mut self, message: Message) -> Result<(), Error> {
match message { match message {
Message::Text(msg) => { Message::Text(msg) => {
log::debug!("{}: <= {}", self.peer, msg); log::trace!("{}: <= {}", self.peer, msg);
// This is defined in nostr.rs // This is defined in nostr.rs
if let Err(e) = self.handle_nostr_message(&msg).await { if let Err(e) = self.handle_nostr_message(&msg).await {
log::error!("{e}"); self.errcount += 1;
log::error!("msg was {}", msg); log::error!("{}: {e}", self.peer);
log::error!("{}: msg was {}", self.peer, msg);
let reply = NostrReply::Notice(format!("error: {}", e)); let reply = NostrReply::Notice(format!("error: {}", e));
self.websocket.send(Message::text(reply.as_json())).await?; self.websocket.send(Message::text(reply.as_json())).await?;
if self.errcount > 3 {
let reply = NostrReply::Notice(
"Too many errors (3). Banned for 60 seconds.".into(),
);
self.websocket.send(Message::text(reply.as_json())).await?;
return Err(ChorusError::TooManyErrors.into());
}
} }
} }
Message::Binary(msg) => { Message::Binary(msg) => {