Clean shutdowns on negotiation failures
Diff
src/connection.rs | 24 ++++++++++++++++++++++++
src/main.rs | 28 ++++++++++++++++++++++++++--
2 files changed, 47 insertions(+), 5 deletions(-)
@@ -9,7 +9,9 @@
use actix::{io::FramedWrite, Actor, Addr};
use const_format::concatcp;
use futures::{SinkExt, TryStreamExt};
use irc_proto::{error::ProtocolError, CapSubCommand, Command, IrcCodec, Message, Prefix};
use irc_proto::{
error::ProtocolError, CapSubCommand, Command, IrcCodec, Message, Prefix, Response,
};
use tokio::{
io::{ReadHalf, WriteHalf},
net::TcpStream,
@@ -198,6 +200,10 @@
.map_err(|e| ProtocolError::Io(Error::new(ErrorKind::InvalidData, e)))?;
if !reserved_nick {
write
.send(NickNotOwnedByUser(initiated.nick).into_message())
.await?;
return Err(ProtocolError::Io(Error::new(
ErrorKind::InvalidData,
"nick is already in use by another user",
@@ -205,6 +211,22 @@
}
Ok(Some(initiated))
}
pub struct NickNotOwnedByUser(String);
impl NickNotOwnedByUser {
#[must_use]
pub fn into_message(self) -> Message {
Message {
tags: None,
prefix: None,
command: Command::Response(
Response::ERR_NICKLOCKED,
vec![self.0, "You must use a nick assigned to you".to_string()],
),
}
}
}
@@ -11,7 +11,8 @@
use actix_rt::{Arbiter, System};
use bytes::BytesMut;
use clap::Parser;
use irc_proto::IrcCodec;
use futures::SinkExt;
use irc_proto::{Command, IrcCodec, Message};
use rand::seq::SliceRandom;
use sqlx::migrate::Migrator;
use tokio::{
@@ -147,9 +148,28 @@
let Some(connection) = connection::negotiate_client_connection(&mut read, &mut write, addr, &persistence, database).await.unwrap() else {
error!("Failed to fully handshake with client, dropping connection");
return;
let connection = match connection::negotiate_client_connection(&mut read, &mut write, addr, &persistence, database).await {
Ok(Some(v)) => v,
Ok(None) => {
error!("Failed to fully handshake with client, dropping connection");
let command = Command::ERROR("You must use SASL to connect to this server".to_string());
if let Err(error) = write.send(Message { tags: None, prefix: None, command, }).await {
error!(%error, "Failed to send error message to client, forcefully closing connection.");
}
return;
}
Err(error) => {
error!(%error, "An error occurred whilst handshaking with client");
let command = Command::ERROR(error.to_string());
if let Err(error) = write.send(Message { tags: None, prefix: None, command, }).await {
error!(%error, "Failed to send error message to client, forcefully closing connection.");
}
return;
}
};