2024-12-03 23:57:04 +00:00
|
|
|
use rsasl::config::SASLConfig;
|
2024-12-04 18:18:37 +00:00
|
|
|
use stanza::{
|
|
|
|
sasl::Mechanisms,
|
|
|
|
stream::{Feature, Features},
|
|
|
|
};
|
2024-12-03 23:57:04 +00:00
|
|
|
|
|
|
|
use crate::{
|
|
|
|
connection::{Tls, Unencrypted},
|
2025-02-25 19:50:15 +00:00
|
|
|
jabber_stream::bound_stream::BoundJabberStream,
|
2024-12-03 23:57:04 +00:00
|
|
|
Connection, Error, JabberStream, Result, JID,
|
|
|
|
};
|
|
|
|
|
2025-01-12 21:19:07 +00:00
|
|
|
pub async fn connect_and_login(
|
2025-02-25 19:50:15 +00:00
|
|
|
jid: &mut JID,
|
2025-01-12 23:10:03 +00:00
|
|
|
password: impl AsRef<str>,
|
2025-01-12 21:19:07 +00:00
|
|
|
server: &mut String,
|
|
|
|
) -> Result<BoundJabberStream<Tls>> {
|
2025-01-12 23:10:03 +00:00
|
|
|
let auth = SASLConfig::with_credentials(
|
|
|
|
None,
|
|
|
|
jid.localpart.clone().ok_or(Error::NoLocalpart)?,
|
|
|
|
password.as_ref().to_string(),
|
|
|
|
)?;
|
2025-01-12 21:19:07 +00:00
|
|
|
let mut conn_state = Connecting::start(&server).await?;
|
|
|
|
loop {
|
|
|
|
match conn_state {
|
|
|
|
Connecting::InsecureConnectionEstablised(tcp_stream) => {
|
|
|
|
conn_state = Connecting::InsecureStreamStarted(
|
|
|
|
JabberStream::start_stream(tcp_stream, server).await?,
|
|
|
|
)
|
2024-12-22 18:58:28 +00:00
|
|
|
}
|
2025-01-12 21:19:07 +00:00
|
|
|
Connecting::InsecureStreamStarted(jabber_stream) => {
|
|
|
|
conn_state = Connecting::InsecureGotFeatures(jabber_stream.get_features().await?)
|
2024-12-22 18:58:28 +00:00
|
|
|
}
|
2025-01-12 21:19:07 +00:00
|
|
|
Connecting::InsecureGotFeatures((features, jabber_stream)) => {
|
|
|
|
match features.negotiate().ok_or(Error::Negotiation)? {
|
|
|
|
Feature::StartTls(_start_tls) => {
|
|
|
|
conn_state = Connecting::StartTls(jabber_stream)
|
|
|
|
}
|
|
|
|
// TODO: better error
|
|
|
|
_ => return Err(Error::TlsRequired),
|
|
|
|
}
|
2024-12-22 18:58:28 +00:00
|
|
|
}
|
2025-01-12 21:19:07 +00:00
|
|
|
Connecting::StartTls(jabber_stream) => {
|
|
|
|
conn_state =
|
|
|
|
Connecting::ConnectionEstablished(jabber_stream.starttls(&server).await?)
|
2024-12-22 18:58:28 +00:00
|
|
|
}
|
2025-01-12 21:19:07 +00:00
|
|
|
Connecting::ConnectionEstablished(tls_stream) => {
|
|
|
|
conn_state =
|
|
|
|
Connecting::StreamStarted(JabberStream::start_stream(tls_stream, server).await?)
|
2024-12-22 18:58:28 +00:00
|
|
|
}
|
2025-01-12 21:19:07 +00:00
|
|
|
Connecting::StreamStarted(jabber_stream) => {
|
|
|
|
conn_state = Connecting::GotFeatures(jabber_stream.get_features().await?)
|
|
|
|
}
|
|
|
|
Connecting::GotFeatures((features, jabber_stream)) => {
|
|
|
|
match features.negotiate().ok_or(Error::Negotiation)? {
|
|
|
|
Feature::StartTls(_start_tls) => return Err(Error::AlreadyTls),
|
|
|
|
Feature::Sasl(mechanisms) => {
|
|
|
|
conn_state = Connecting::Sasl(mechanisms, jabber_stream)
|
2024-12-04 02:09:07 +00:00
|
|
|
}
|
2025-01-12 21:19:07 +00:00
|
|
|
Feature::Bind => conn_state = Connecting::Bind(jabber_stream),
|
|
|
|
Feature::Unknown => return Err(Error::Unsupported),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Connecting::Sasl(mechanisms, jabber_stream) => {
|
|
|
|
conn_state = Connecting::ConnectionEstablished(
|
|
|
|
jabber_stream.sasl(mechanisms, auth.clone()).await?,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
Connecting::Bind(jabber_stream) => {
|
|
|
|
return Ok(jabber_stream.bind(jid).await?.to_bound_jabber());
|
2024-12-04 02:09:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub enum Connecting {
|
2024-12-03 23:57:04 +00:00
|
|
|
InsecureConnectionEstablised(Unencrypted),
|
|
|
|
InsecureStreamStarted(JabberStream<Unencrypted>),
|
|
|
|
InsecureGotFeatures((Features, JabberStream<Unencrypted>)),
|
|
|
|
StartTls(JabberStream<Unencrypted>),
|
|
|
|
ConnectionEstablished(Tls),
|
|
|
|
StreamStarted(JabberStream<Tls>),
|
|
|
|
GotFeatures((Features, JabberStream<Tls>)),
|
|
|
|
Sasl(Mechanisms, JabberStream<Tls>),
|
|
|
|
Bind(JabberStream<Tls>),
|
|
|
|
}
|
|
|
|
|
2024-12-04 02:09:07 +00:00
|
|
|
impl Connecting {
|
|
|
|
pub async fn start(server: &str) -> Result<Self> {
|
|
|
|
match Connection::connect(server).await? {
|
|
|
|
Connection::Encrypted(tls_stream) => Ok(Connecting::ConnectionEstablished(tls_stream)),
|
|
|
|
Connection::Unencrypted(tcp_stream) => {
|
|
|
|
Ok(Connecting::InsecureConnectionEstablised(tcp_stream))
|
2024-12-03 23:57:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-04 02:09:07 +00:00
|
|
|
pub enum InsecureConnecting {
|
2024-12-03 23:57:04 +00:00
|
|
|
Disconnected,
|
|
|
|
ConnectionEstablished(Connection),
|
|
|
|
PreStarttls(JabberStream<Unencrypted>),
|
|
|
|
PreAuthenticated(JabberStream<Tls>),
|
|
|
|
Authenticated(Tls),
|
|
|
|
PreBound(JabberStream<Tls>),
|
|
|
|
Bound(JabberStream<Tls>),
|
|
|
|
}
|
|
|
|
|
2024-12-04 02:09:07 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2024-12-22 18:58:28 +00:00
|
|
|
use std::{sync::Arc, time::Duration};
|
2024-12-04 02:09:07 +00:00
|
|
|
|
2024-12-22 18:58:28 +00:00
|
|
|
use futures::{SinkExt, StreamExt};
|
2025-01-12 23:10:03 +00:00
|
|
|
use jid::JID;
|
2024-12-22 18:58:28 +00:00
|
|
|
use stanza::{
|
|
|
|
client::{
|
|
|
|
iq::{Iq, IqType, Query},
|
|
|
|
Stanza,
|
|
|
|
},
|
|
|
|
xep_0199::Ping,
|
|
|
|
};
|
2024-12-04 02:09:07 +00:00
|
|
|
use test_log::test;
|
2024-12-22 18:58:28 +00:00
|
|
|
use tokio::{sync::Mutex, time::sleep};
|
|
|
|
use tracing::info;
|
2024-12-04 02:09:07 +00:00
|
|
|
|
2025-01-12 23:10:03 +00:00
|
|
|
use super::connect_and_login;
|
|
|
|
|
2024-12-04 02:09:07 +00:00
|
|
|
#[test(tokio::test)]
|
|
|
|
async fn login() {
|
2025-01-12 23:10:03 +00:00
|
|
|
let mut jid: JID = "test@blos.sm".try_into().unwrap();
|
|
|
|
let client = connect_and_login(&mut jid, "slayed", &mut "blos.sm".to_string())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2024-12-04 02:09:07 +00:00
|
|
|
sleep(Duration::from_secs(5)).await
|
|
|
|
}
|
2024-12-22 18:58:28 +00:00
|
|
|
|
|
|
|
#[test(tokio::test)]
|
|
|
|
async fn ping_parallel() {
|
2025-01-12 23:10:03 +00:00
|
|
|
let mut jid: JID = "test@blos.sm".try_into().unwrap();
|
|
|
|
let mut server = "blos.sm".to_string();
|
|
|
|
let client = connect_and_login(&mut jid, "slayed", &mut server)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
let (mut read, mut write) = client.split();
|
2024-12-22 18:58:28 +00:00
|
|
|
|
|
|
|
tokio::join!(
|
|
|
|
async {
|
2024-12-22 19:55:48 +00:00
|
|
|
write
|
2025-01-12 21:19:07 +00:00
|
|
|
.write(&Stanza::Iq(Iq {
|
2024-12-22 18:58:28 +00:00
|
|
|
from: Some(jid.clone()),
|
|
|
|
id: "c2s1".to_string(),
|
|
|
|
to: Some(server.clone().try_into().unwrap()),
|
|
|
|
r#type: IqType::Get,
|
|
|
|
lang: None,
|
|
|
|
query: Some(Query::Ping(Ping)),
|
|
|
|
errors: Vec::new(),
|
|
|
|
}))
|
2025-01-12 21:19:07 +00:00
|
|
|
.await
|
|
|
|
.unwrap();
|
2024-12-22 19:55:48 +00:00
|
|
|
write
|
2025-01-12 21:19:07 +00:00
|
|
|
.write(&Stanza::Iq(Iq {
|
2024-12-22 18:58:28 +00:00
|
|
|
from: Some(jid.clone()),
|
|
|
|
id: "c2s2".to_string(),
|
|
|
|
to: Some(server.clone().try_into().unwrap()),
|
|
|
|
r#type: IqType::Get,
|
|
|
|
lang: None,
|
|
|
|
query: Some(Query::Ping(Ping)),
|
|
|
|
errors: Vec::new(),
|
|
|
|
}))
|
2025-01-12 21:19:07 +00:00
|
|
|
.await
|
|
|
|
.unwrap();
|
2024-12-22 18:58:28 +00:00
|
|
|
},
|
|
|
|
async {
|
2025-01-12 21:19:07 +00:00
|
|
|
for _ in 0..2 {
|
|
|
|
let stanza = read.read::<Stanza>().await.unwrap();
|
|
|
|
info!("ping reply: {:#?}", stanza);
|
2024-12-22 18:58:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
2024-12-04 02:09:07 +00:00
|
|
|
}
|