Compare commits

..

7 Commits

Author SHA1 Message Date
JustTestingV 809ddaac25 Merge remote-tracking branch 'origin/xmpp-sanitizing' into xmpp-sanitizing
# Conflicts:
#	crates/projection-xmpp/src/lib.rs
#	crates/projection-xmpp/tests/lib.rs
2024-02-18 14:26:15 +03:00
JustTestingV 07e26ee77a termination usage for socket graceful shutdown 2024-02-18 14:25:56 +03:00
JustTestingV d76e786ceb deleted lavina-core dependency in proto-xmpp 2024-02-18 14:25:53 +03:00
JustTestingV 6a473f3ebb [xmpp] no panic! 2024-02-18 14:25:52 +03:00
Nikita Vilunov 7613055dde update dependencies 2024-02-08 21:15:22 +01:00
G1ng3r 7ff9ffdcf7 irc: send ERR_SASLFAIL reply for auth fails (#30)
Reviewed-on: lavina/lavina#30
Co-authored-by: G1ng3r <saynulanude@gmail.com>
Co-committed-by: G1ng3r <saynulanude@gmail.com>
2024-02-06 23:08:14 +00:00
JustTestingV 614be92be3 xmpp: no panic! (#29)
Reviewed-on: lavina/lavina#29
Co-authored-by: JustTestingV <JustTestingV@gmail.com>
Co-committed-by: JustTestingV <JustTestingV@gmail.com>
2024-01-22 15:13:19 +00:00
8 changed files with 364 additions and 317 deletions

441
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -54,7 +54,7 @@ async fn handle_socket(
socket_addr: &SocketAddr, socket_addr: &SocketAddr,
players: PlayerRegistry, players: PlayerRegistry,
rooms: RoomRegistry, rooms: RoomRegistry,
termination: Deferred<()>, // TODO use it to stop the connection gracefully termination: Deferred<()>,
mut storage: Storage, mut storage: Storage,
) -> Result<()> { ) -> Result<()> {
log::info!("Received an IRC connection from {socket_addr}"); log::info!("Received an IRC connection from {socket_addr}");
@ -62,19 +62,24 @@ async fn handle_socket(
let mut reader: BufReader<ReadHalf> = BufReader::new(reader); let mut reader: BufReader<ReadHalf> = BufReader::new(reader);
let mut writer = BufWriter::new(writer); let mut writer = BufWriter::new(writer);
let registered_user: Result<RegisteredUser> = pin!(termination);
handle_registration(&mut reader, &mut writer, &mut storage, &config).await; select! {
biased;
match registered_user { _ = &mut termination =>{
Ok(user) => { log::info!("Socket handling was terminated");
log::debug!("User registered"); return Ok(())
handle_registered_socket(config, players, rooms, &mut reader, &mut writer, user).await?; },
} registered_user = handle_registration(&mut reader, &mut writer, &mut storage, &config) =>
Err(err) => { match registered_user {
log::debug!("Registration failed: {err}"); Ok(user) => {
} log::debug!("User registered");
handle_registered_socket(config, players, rooms, &mut reader, &mut writer, user).await?;
}
Err(err) => {
log::debug!("Registration failed: {err}");
}
}
} }
stream.shutdown().await?; stream.shutdown().await?;
Ok(()) Ok(())
} }
@ -180,14 +185,16 @@ async fn handle_registration<'a>(
writer.flush().await?; writer.flush().await?;
} }
CapabilitySubcommand::End => { CapabilitySubcommand::End => {
let Some((username, realname)) = future_username else { let Some((ref username, ref realname)) = future_username else {
todo!() todo!();
}; };
let Some(nickname) = future_nickname.clone() else { let Some(nickname) = future_nickname.clone() else {
todo!() todo!();
}; };
let username = username.clone();
let realname = realname.clone();
let candidate_user = RegisteredUser { let candidate_user = RegisteredUser {
nickname, nickname: nickname.clone(),
username, username,
realname, realname,
}; };
@ -197,7 +204,15 @@ async fn handle_registration<'a>(
break Ok(candidate_user); break Ok(candidate_user);
} else { } else {
let Some(candidate_password) = pass else { let Some(candidate_password) = pass else {
todo!(); sasl_fail_message(
config.server_name.clone(),
nickname.clone(),
"User credentials was not provided".into(),
)
.write_async(writer)
.await?;
writer.flush().await?;
continue;
}; };
auth_user(storage, &*candidate_user.nickname, &*candidate_password).await?; auth_user(storage, &*candidate_user.nickname, &*candidate_password).await?;
break Ok(candidate_user); break Ok(candidate_user);
@ -209,12 +224,20 @@ async fn handle_registration<'a>(
future_nickname = Some(nickname); future_nickname = Some(nickname);
} else if let Some((username, realname)) = future_username.clone() { } else if let Some((username, realname)) = future_username.clone() {
let candidate_user = RegisteredUser { let candidate_user = RegisteredUser {
nickname, nickname: nickname.clone(),
username, username,
realname, realname,
}; };
let Some(candidate_password) = pass else { let Some(candidate_password) = pass else {
todo!(); sasl_fail_message(
config.server_name.clone(),
nickname.clone(),
"User credentials was not provided".into(),
)
.write_async(writer)
.await?;
writer.flush().await?;
continue;
}; };
auth_user(storage, &*candidate_user.nickname, &*candidate_password).await?; auth_user(storage, &*candidate_user.nickname, &*candidate_password).await?;
break Ok(candidate_user); break Ok(candidate_user);
@ -227,12 +250,20 @@ async fn handle_registration<'a>(
future_username = Some((username, realname)); future_username = Some((username, realname));
} else if let Some(nickname) = future_nickname.clone() { } else if let Some(nickname) = future_nickname.clone() {
let candidate_user = RegisteredUser { let candidate_user = RegisteredUser {
nickname, nickname: nickname.clone(),
username, username,
realname, realname,
}; };
let Some(candidate_password) = pass else { let Some(candidate_password) = pass else {
todo!(); sasl_fail_message(
config.server_name.clone(),
nickname.clone(),
"User credentials was not provided".into(),
)
.write_async(writer)
.await?;
writer.flush().await?;
continue;
}; };
auth_user(storage, &*candidate_user.nickname, &*candidate_password).await?; auth_user(storage, &*candidate_user.nickname, &*candidate_password).await?;
break Ok(candidate_user); break Ok(candidate_user);
@ -255,38 +286,59 @@ async fn handle_registration<'a>(
.await?; .await?;
writer.flush().await?; writer.flush().await?;
} else { } else {
// TODO respond with 904 if let Some(nickname) = future_nickname.clone() {
todo!(); sasl_fail_message(
config.server_name.clone(),
nickname.clone(),
"Unsupported mechanism".into(),
)
.write_async(writer)
.await?;
writer.flush().await?;
} else {
break Err(anyhow::Error::msg("Wrong authentication sequence"));
}
} }
} else { } else {
let body = AuthBody::from_str(body.as_bytes())?; let body = AuthBody::from_str(body.as_bytes())?;
auth_user(storage, &body.login, &body.password).await?; if let Err(e) = auth_user(storage, &body.login, &body.password).await {
let login: Str = body.login.into(); tracing::warn!("Authentication failed: {:?}", e);
validated_user = Some(login.clone()); if let Some(nickname) = future_nickname.clone() {
ServerMessage { sasl_fail_message(config.server_name.clone(), nickname.clone(), "Bad credentials".into())
tags: vec![], .write_async(writer)
sender: Some(config.server_name.clone().into()), .await?;
body: ServerMessageBody::N900LoggedIn { writer.flush().await?;
nick: login.clone(), } else {
address: login.clone(), }
account: login.clone(), } else {
message: format!("You are now logged in as {}", login).into(), let login: Str = body.login.into();
}, validated_user = Some(login.clone());
ServerMessage {
tags: vec![],
sender: Some(config.server_name.clone().into()),
body: ServerMessageBody::N900LoggedIn {
nick: login.clone(),
address: login.clone(),
account: login.clone(),
message: format!("You are now logged in as {}", login).into(),
},
}
.write_async(writer)
.await?;
ServerMessage {
tags: vec![],
sender: Some(config.server_name.clone().into()),
body: ServerMessageBody::N903SaslSuccess {
nick: login.clone(),
message: "SASL authentication successful".into(),
},
}
.write_async(writer)
.await?;
writer.flush().await?;
} }
.write_async(writer)
.await?;
ServerMessage {
tags: vec![],
sender: Some(config.server_name.clone().into()),
body: ServerMessageBody::N903SaslSuccess {
nick: login.clone(),
message: "SASL authentication successful".into(),
},
}
.write_async(writer)
.await?;
writer.flush().await?;
} }
// TODO handle abortion of authentication // TODO handle abortion of authentication
} }
_ => {} _ => {}
@ -297,6 +349,14 @@ async fn handle_registration<'a>(
Ok(user) Ok(user)
} }
fn sasl_fail_message(sender: Str, nick: Str, text: Str) -> ServerMessage {
ServerMessage {
tags: vec![],
sender: Some(sender),
body: ServerMessageBody::N904SaslFail { nick, text },
}
}
async fn auth_user(storage: &mut Storage, login: &str, plain_password: &str) -> Result<()> { async fn auth_user(storage: &mut Storage, login: &str, plain_password: &str) -> Result<()> {
let stored_user = storage.retrieve_user_by_name(login).await?; let stored_user = storage.retrieve_user_by_name(login).await?;

View File

@ -1,3 +1,4 @@
use std::net::SocketAddr;
use std::time::Duration; use std::time::Duration;
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
@ -218,3 +219,30 @@ async fn scenario_cap_short_negotiation() -> Result<()> {
server.server.terminate().await?; server.server.terminate().await?;
Ok(()) Ok(())
} }
#[tokio::test]
async fn terminate_socket_scenario() -> Result<()> {
let mut server = TestServer::start().await?;
let address: SocketAddr = ("127.0.0.1:0".parse().unwrap());
// test scenario
server.storage.create_user("tester").await?;
server.storage.set_password("tester", "password").await?;
let mut stream = TcpStream::connect(server.server.addr).await?;
let mut s = TestScope::new(&mut stream);
s.send("NICK tester").await?;
s.send("CAP REQ :sasl").await?;
s.send("USER UserName 0 * :Real Name").await?;
s.expect(":testserver CAP tester ACK :sasl").await?;
s.send("AUTHENTICATE PLAIN").await?;
s.expect(":testserver AUTHENTICATE +").await?;
stream.shutdown().await?;
server.server.terminate().await?;
assert!(TcpStream::connect(&address).await.is_err());
Ok(())
}

View File

@ -162,7 +162,7 @@ async fn handle_socket(
mut players: PlayerRegistry, mut players: PlayerRegistry,
rooms: RoomRegistry, rooms: RoomRegistry,
mut storage: Storage, mut storage: Storage,
termination: Deferred<()>, // TODO use it to stop the connection gracefully termination: Deferred<()>,
) -> Result<()> { ) -> Result<()> {
log::info!("Received an XMPP connection from {socket_addr}"); log::info!("Received an XMPP connection from {socket_addr}");
let mut reader_buf = vec![]; let mut reader_buf = vec![];
@ -190,11 +190,13 @@ async fn handle_socket(
pin!(termination); pin!(termination);
select! { select! {
biased; biased;
_ = &mut termination => return Ok(()), _ = &mut termination =>{
log::info!("Socket handling was terminated");
return Ok(())
},
authenticated = socket_auth(&mut xml_reader, &mut xml_writer, &mut reader_buf, &mut storage) => { authenticated = socket_auth(&mut xml_reader, &mut xml_writer, &mut reader_buf, &mut storage) => {
match authenticated { match authenticated {
Ok(authenticated) => { Ok(authenticated) => {
log::debug!("User authenticated");
let mut connection = players.connect_to_player(authenticated.player_id.clone()).await; let mut connection = players.connect_to_player(authenticated.player_id.clone()).await;
socket_final( socket_final(
&mut xml_reader, &mut xml_reader,

View File

@ -1,3 +1,4 @@
use std::net::SocketAddr;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
@ -202,7 +203,7 @@ async fn terminate_socket() -> Result<()> {
let rooms = RoomRegistry::new(&mut metrics, storage.clone()).unwrap(); let rooms = RoomRegistry::new(&mut metrics, storage.clone()).unwrap();
let players = PlayerRegistry::empty(rooms.clone(), &mut metrics).unwrap(); let players = PlayerRegistry::empty(rooms.clone(), &mut metrics).unwrap();
let server = launch(config, players, rooms, metrics, storage.clone()).await.unwrap(); let server = launch(config, players, rooms, metrics, storage.clone()).await.unwrap();
let address: SocketAddr = ("127.0.0.1:0".parse().unwrap());
// test scenario // test scenario
storage.create_user("tester").await?; storage.create_user("tester").await?;
@ -213,6 +214,7 @@ async fn terminate_socket() -> Result<()> {
tracing::info!("TCP connection established"); tracing::info!("TCP connection established");
s.send(r#"<?xml version="1.0"?>"#).await?; s.send(r#"<?xml version="1.0"?>"#).await?;
s.send(r#"<stream:stream xmlns:stream="http://etherx.jabber.org/streams" to="127.0.0.1" xml:lang="en" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns="jabber:client" version="1.0">"#).await?; s.send(r#"<stream:stream xmlns:stream="http://etherx.jabber.org/streams" to="127.0.0.1" xml:lang="en" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns="jabber:client" version="1.0">"#).await?;
assert_matches!(s.next_xml_event().await?, Event::Decl(_) => {}); assert_matches!(s.next_xml_event().await?, Event::Decl(_) => {});
assert_matches!(s.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"stream")); assert_matches!(s.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"stream"));
@ -224,7 +226,6 @@ async fn terminate_socket() -> Result<()> {
s.send(r#"<starttls/>"#).await?; s.send(r#"<starttls/>"#).await?;
assert_matches!(s.next_xml_event().await?, Event::Empty(b) => assert_eq!(b.local_name().into_inner(), b"proceed")); assert_matches!(s.next_xml_event().await?, Event::Empty(b) => assert_eq!(b.local_name().into_inner(), b"proceed"));
let buffer = s.buffer; let buffer = s.buffer;
tracing::info!("TLS feature negotiation complete");
let connector = TlsConnector::from(Arc::new( let connector = TlsConnector::from(Arc::new(
ClientConfig::builder() ClientConfig::builder()
@ -236,23 +237,17 @@ async fn terminate_socket() -> Result<()> {
let mut stream = connector.connect(ServerName::IpAddress(server.addr.ip()), stream).await?; let mut stream = connector.connect(ServerName::IpAddress(server.addr.ip()), stream).await?;
tracing::info!("TLS connection established"); tracing::info!("TLS connection established");
server.terminate().await?;
let mut s = TestScopeTls::new(&mut stream, buffer); let mut s = TestScopeTls::new(&mut stream, buffer);
println!("--------------------------------"); s.send(r#"<?xml version="1.0"?>"#).await?;
let res = s.send(r#"<?xml version="1.0"?>"#).await; s.send(r#"<stream:stream xmlns:stream="http://etherx.jabber.org/streams" to="127.0.0.1" xml:lang="en" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns="jabber:client" version="1.0">"#).await?;
println!(); assert_matches!(s.next_xml_event().await?, Event::Decl(_) => {});
assert!(res.is_ok()); assert_matches!(s.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"stream"));
// s.send(r#"<stream:stream xmlns:stream="http://etherx.jabber.org/streams" to="127.0.0.1" xml:lang="en" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns="jabber:client" version="1.0">"#).await?; stream.shutdown().await?;
// assert_matches!(s.next_xml_event().await?, Event::Decl(_) => {}); server.terminate().await?;
// assert_matches!(s.next_xml_event().await?, Event::Start(b) => assert_eq!(b.local_name().into_inner(), b"stream"));
// stream.shutdown().await?; assert!(TcpStream::connect(&address).await.is_err());
// wrap up
// server.terminate().await?;
Ok(()) Ok(())
} }

View File

@ -30,6 +30,10 @@ fn token(input: &str) -> IResult<&str, &str> {
take_while(|i| i != '\n' && i != '\r')(input) take_while(|i| i != '\n' && i != '\r')(input)
} }
fn params(input: &str) -> IResult<&str, &str> {
take_while(|i| i != '\n' && i != '\r' && i != ':')(input)
}
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub enum Chan { pub enum Chan {
/// #<name> — network-global channel, available from any server in the network. /// #<name> — network-global channel, available from any server in the network.

View File

@ -1,3 +1,5 @@
use std::sync::Arc;
use nonempty::NonEmpty; use nonempty::NonEmpty;
use tokio::io::AsyncWrite; use tokio::io::AsyncWrite;
use tokio::io::AsyncWriteExt; use tokio::io::AsyncWriteExt;
@ -152,6 +154,10 @@ pub enum ServerMessageBody {
N903SaslSuccess { N903SaslSuccess {
nick: Str, nick: Str,
message: Str, message: Str,
},
N904SaslFail {
nick: Str,
text: Str,
} }
} }
@ -369,6 +375,13 @@ impl ServerMessageBody {
writer.write_all(b" :").await?; writer.write_all(b" :").await?;
writer.write_all(message.as_bytes()).await?; writer.write_all(message.as_bytes()).await?;
} }
ServerMessageBody::N904SaslFail { nick, text } => {
writer.write_all(b"904").await?;
writer.write_all(b" ").await?;
writer.write_all(nick.as_bytes()).await?;
writer.write_all(b" :").await?;
writer.write_all(text.as_bytes()).await?;
}
} }
Ok(()) Ok(())
} }
@ -391,7 +404,7 @@ fn server_message_body(input: &str) -> IResult<&str, ServerMessageBody> {
server_message_body_notice, server_message_body_notice,
server_message_body_ping, server_message_body_ping,
server_message_body_pong, server_message_body_pong,
server_message_body_cap, server_message_body_cap
))(input) ))(input)
} }

View File

@ -1 +1 @@
nightly-2023-12-07 nightly-2024-02-08