diff --git a/config.toml b/config.toml index 6104dce..4765aa0 100644 --- a/config.toml +++ b/config.toml @@ -9,6 +9,7 @@ server_name = "irc.localhost" listen_on = "127.0.0.1:5222" cert = "./certs/xmpp.pem" key = "./certs/xmpp.key" +hostname = "localhost" [storage] db_path = "db.sqlite" diff --git a/crates/projection-xmpp/src/iq.rs b/crates/projection-xmpp/src/iq.rs index 01135b1..6766e19 100644 --- a/crates/projection-xmpp/src/iq.rs +++ b/crates/projection-xmpp/src/iq.rs @@ -25,7 +25,7 @@ impl<'a> XmppConnection<'a> { r#type: IqType::Result, body: BindResponse(Jid { name: Some(self.user.xmpp_name.clone()), - server: Server("localhost".into()), + server: Server(self.hostname.clone()), resource: Some(self.user.xmpp_resource.clone()), }), }; @@ -52,7 +52,7 @@ impl<'a> XmppConnection<'a> { req.serialize(output); } IqClientBody::DiscoInfo(info) => { - let response = disco_info(iq.to.as_deref(), &info); + let response = self.disco_info(iq.to.as_deref(), &info); let req = Iq { from: iq.to, id: iq.id, @@ -63,7 +63,7 @@ impl<'a> XmppConnection<'a> { req.serialize(output); } IqClientBody::DiscoItem(item) => { - let response = disco_items(iq.to.as_deref(), &item, self.rooms).await; + let response = self.disco_items(iq.to.as_deref(), &item, self.rooms).await; let req = Iq { from: iq.to, id: iq.id, @@ -87,78 +87,79 @@ impl<'a> XmppConnection<'a> { } } } -} -fn disco_info(to: Option<&str>, req: &InfoQuery) -> InfoQuery { - let identity; - let feature; - match to { - Some("localhost") => { - identity = vec![Identity { - category: "server".into(), - name: None, - r#type: "im".into(), - }]; - feature = vec![ - Feature::new("http://jabber.org/protocol/disco#info"), - Feature::new("http://jabber.org/protocol/disco#items"), - Feature::new("iq"), - Feature::new("presence"), - ] - } - Some("rooms.localhost") => { - identity = vec![Identity { - category: "conference".into(), - name: Some("Chat rooms".into()), - r#type: "text".into(), - }]; - feature = vec![ - Feature::new("http://jabber.org/protocol/disco#info"), - Feature::new("http://jabber.org/protocol/disco#items"), - Feature::new("http://jabber.org/protocol/muc"), - ] - } - _ => { - identity = vec![]; - feature = vec![]; - } - }; - InfoQuery { - node: None, - identity, - feature, - } -} + fn disco_info(&self, to: Option<&str>, req: &InfoQuery) -> InfoQuery { + let identity; + let feature; -async fn disco_items(to: Option<&str>, req: &ItemQuery, rooms: &RoomRegistry) -> ItemQuery { - let item = match to { - Some("localhost") => { - vec![Item { - jid: Jid { + match to { + Some(r) if r == &*self.hostname => { + identity = vec![Identity { + category: "server".into(), name: None, - server: Server("rooms.localhost".into()), - resource: None, - }, - name: None, - node: None, - }] + r#type: "im".into(), + }]; + feature = vec![ + Feature::new("http://jabber.org/protocol/disco#info"), + Feature::new("http://jabber.org/protocol/disco#items"), + Feature::new("iq"), + Feature::new("presence"), + ] + } + Some(r) if r == &*self.hostname_rooms => { + identity = vec![Identity { + category: "conference".into(), + name: Some("Chat rooms".into()), + r#type: "text".into(), + }]; + feature = vec![ + Feature::new("http://jabber.org/protocol/disco#info"), + Feature::new("http://jabber.org/protocol/disco#items"), + Feature::new("http://jabber.org/protocol/muc"), + ] + } + _ => { + identity = vec![]; + feature = vec![]; + } + }; + InfoQuery { + node: None, + identity, + feature, } - Some("rooms.localhost") => { - let room_list = rooms.get_all_rooms().await; - room_list - .into_iter() - .map(|room_info| Item { + } + + async fn disco_items(&self, to: Option<&str>, req: &ItemQuery, rooms: &RoomRegistry) -> ItemQuery { + let item = match to { + Some(r) if r == &*self.hostname => { + vec![Item { jid: Jid { - name: Some(Name(room_info.id.into_inner())), - server: Server("rooms.localhost".into()), + name: None, + server: Server(self.hostname_rooms.clone()), resource: None, }, name: None, node: None, - }) - .collect() - } - _ => vec![], - }; - ItemQuery { item } + }] + } + Some(r) if r == &*self.hostname_rooms => { + let room_list = rooms.get_all_rooms().await; + room_list + .into_iter() + .map(|room_info| Item { + jid: Jid { + name: Some(Name(room_info.id.into_inner())), + server: Server(self.hostname_rooms.clone()), + resource: None, + }, + name: None, + node: None, + }) + .collect() + } + _ => vec![], + }; + ItemQuery { item } + } } diff --git a/crates/projection-xmpp/src/lib.rs b/crates/projection-xmpp/src/lib.rs index c659e5b..0da3019 100644 --- a/crates/projection-xmpp/src/lib.rs +++ b/crates/projection-xmpp/src/lib.rs @@ -9,7 +9,6 @@ use std::net::SocketAddr; use std::path::PathBuf; use std::sync::Arc; -use anyhow::anyhow; use futures_util::future::join_all; use prometheus::Registry as MetricsRegistry; use quick_xml::events::{BytesDecl, Event}; @@ -44,6 +43,7 @@ pub struct ServerConfig { pub listen_on: SocketAddr, pub cert: PathBuf, pub key: PathBuf, + pub hostname: Str, } struct LoadedConfig { @@ -125,11 +125,12 @@ pub async fn launch( let players = players.clone(); let rooms = rooms.clone(); let storage = storage.clone(); + let hostname = config.hostname.clone(); let terminator = Terminator::spawn(|termination| { let stopped_tx = stopped_tx.clone(); let loaded_config = loaded_config.clone(); async move { - match handle_socket(loaded_config, stream, &socket_addr, players, rooms, storage, termination).await { + match handle_socket(loaded_config, stream, &socket_addr, players, rooms, storage, hostname, termination).await { Ok(_) => log::info!("Connection terminated"), Err(err) => log::warn!("Connection failed: {err}"), } @@ -164,12 +165,13 @@ pub async fn launch( } async fn handle_socket( - config: Arc, + cert_config: Arc, mut stream: TcpStream, socket_addr: &SocketAddr, mut players: PlayerRegistry, rooms: RoomRegistry, mut storage: Storage, + hostname: Str, termination: Deferred<()>, // TODO use it to stop the connection gracefully ) -> Result<()> { log::info!("Received an XMPP connection from {socket_addr}"); @@ -178,12 +180,12 @@ async fn handle_socket( let mut buf_reader = BufReader::new(reader); let mut buf_writer = BufWriter::new(writer); - socket_force_tls(&mut buf_reader, &mut buf_writer, &mut reader_buf).await?; + socket_force_tls(&mut buf_reader, &mut buf_writer, &mut reader_buf, &hostname).await?; let mut config = tokio_rustls::rustls::ServerConfig::builder() .with_safe_defaults() .with_no_client_auth() - .with_single_cert(vec![config.cert.clone()], config.key.clone())?; + .with_single_cert(vec![cert_config.cert.clone()], cert_config.key.clone())?; config.key_log = Arc::new(tokio_rustls::rustls::KeyLogFile::new()); log::debug!("Accepting TLS connection..."); @@ -202,7 +204,7 @@ async fn handle_socket( 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, &hostname) => { match authenticated { Ok(authenticated) => { let mut connection = players.connect_to_player(authenticated.player_id.clone()).await; @@ -213,6 +215,7 @@ async fn handle_socket( &authenticated, &mut connection, &rooms, + &hostname, ) .await?; }, @@ -233,16 +236,18 @@ async fn socket_force_tls( reader: &mut (impl AsyncBufRead + Unpin), writer: &mut (impl AsyncWrite + Unpin), reader_buf: &mut Vec, + hostname: &Str, ) -> Result<()> { use proto_xmpp::tls::*; let xml_reader = &mut NsReader::from_reader(reader); let xml_writer = &mut Writer::new(writer); + // TODO validate the server hostname received in the stream start let _ = ClientStreamStart::parse(xml_reader, reader_buf).await?; let event = Event::Decl(BytesDecl::new("1.0", None, None)); xml_writer.write_event_async(event).await?; let msg = ServerStreamStart { - from: "localhost".into(), + from: hostname.to_string(), lang: "en".into(), id: uuid::Uuid::new_v4().to_string(), version: "1.0".into(), @@ -267,12 +272,14 @@ async fn socket_auth( xml_writer: &mut Writer<(impl AsyncWrite + Unpin)>, reader_buf: &mut Vec, storage: &mut Storage, + hostname: &Str, ) -> Result { + // TODO validate the server hostname received in the stream start let _ = ClientStreamStart::parse(xml_reader, reader_buf).await?; xml_writer.write_event_async(Event::Decl(BytesDecl::new("1.0", None, None))).await?; ServerStreamStart { - from: "localhost".into(), + from: hostname.to_string(), lang: "en".into(), id: uuid::Uuid::new_v4().to_string(), version: "1.0".into(), @@ -335,12 +342,14 @@ async fn socket_final( authenticated: &Authenticated, user_handle: &mut PlayerConnection, rooms: &RoomRegistry, + hostname: &Str, ) -> Result<()> { + // TODO validate the server hostname received in the stream start let _ = ClientStreamStart::parse(xml_reader, reader_buf).await?; xml_writer.write_event_async(Event::Decl(BytesDecl::new("1.0", None, None))).await?; ServerStreamStart { - from: "localhost".into(), + from: hostname.to_string(), lang: "en".into(), id: uuid::Uuid::new_v4().to_string(), version: "1.0".into(), @@ -366,6 +375,8 @@ async fn socket_final( user: authenticated, user_handle, rooms, + hostname: hostname.clone(), + hostname_rooms: format!("rooms.{}", hostname).into(), }; let should_recreate_xml_future = select! { biased; @@ -422,6 +433,8 @@ struct XmppConnection<'a> { user: &'a Authenticated, user_handle: &'a mut PlayerConnection, rooms: &'a RoomRegistry, + hostname: Str, + hostname_rooms: Str, } impl<'a> XmppConnection<'a> { diff --git a/crates/projection-xmpp/src/message.rs b/crates/projection-xmpp/src/message.rs index 44aab05..a737b2b 100644 --- a/crates/projection-xmpp/src/message.rs +++ b/crates/projection-xmpp/src/message.rs @@ -18,17 +18,17 @@ impl<'a> XmppConnection<'a> { resource: _, }) = m.to { - if server.0.as_ref() == "rooms.localhost" && m.r#type == MessageType::Groupchat { + if server.0.as_ref() == &*self.hostname_rooms && m.r#type == MessageType::Groupchat { self.user_handle.send_message(RoomId::from(name.0.clone())?, m.body.clone().into()).await?; Message::<()> { to: Some(Jid { name: Some(self.user.xmpp_name.clone()), - server: Server("localhost".into()), + server: Server(self.hostname.clone()), resource: Some(self.user.xmpp_resource.clone()), }), from: Some(Jid { name: Some(name), - server: Server("rooms.localhost".into()), + server: Server(self.hostname_rooms.clone()), resource: Some(self.user.xmpp_muc_name.clone()), }), id: m.id, diff --git a/crates/projection-xmpp/src/presence.rs b/crates/projection-xmpp/src/presence.rs index eabf0fd..6f9540e 100644 --- a/crates/projection-xmpp/src/presence.rs +++ b/crates/projection-xmpp/src/presence.rs @@ -16,12 +16,12 @@ impl<'a> XmppConnection<'a> { Presence::<()> { to: Some(Jid { name: Some(self.user.xmpp_name.clone()), - server: Server("localhost".into()), + server: Server(self.hostname.clone()), resource: Some(self.user.xmpp_resource.clone()), }), from: Some(Jid { name: Some(self.user.xmpp_name.clone()), - server: Server("localhost".into()), + server: Server(self.hostname.clone()), resource: Some(self.user.xmpp_resource.clone()), }), ..Default::default() @@ -36,12 +36,12 @@ impl<'a> XmppConnection<'a> { Presence::<()> { to: Some(Jid { name: Some(self.user.xmpp_name.clone()), - server: Server("localhost".into()), + server: Server(self.hostname.clone()), resource: Some(self.user.xmpp_resource.clone()), }), from: Some(Jid { name: Some(name.clone()), - server: Server("rooms.localhost".into()), + server: Server(self.hostname_rooms.clone()), resource: Some(self.user.xmpp_muc_name.clone()), }), ..Default::default() diff --git a/crates/projection-xmpp/src/updates.rs b/crates/projection-xmpp/src/updates.rs index c211be8..0161b3f 100644 --- a/crates/projection-xmpp/src/updates.rs +++ b/crates/projection-xmpp/src/updates.rs @@ -21,12 +21,12 @@ impl<'a> XmppConnection<'a> { Message::<()> { to: Some(Jid { name: Some(self.user.xmpp_name.clone()), - server: Server("localhost".into()), + server: Server(self.hostname.clone()), resource: Some(self.user.xmpp_resource.clone()), }), from: Some(Jid { name: Some(Name(room_id.into_inner().into())), - server: Server("rooms.localhost".into()), + server: Server(self.hostname_rooms.clone()), resource: Some(Resource(author_id.into_inner().into())), }), id: None, diff --git a/crates/projection-xmpp/tests/lib.rs b/crates/projection-xmpp/tests/lib.rs index 7ce583c..cc7e645 100644 --- a/crates/projection-xmpp/tests/lib.rs +++ b/crates/projection-xmpp/tests/lib.rs @@ -135,6 +135,7 @@ impl TestServer { listen_on: "127.0.0.1:0".parse().unwrap(), cert: "tests/certs/xmpp.pem".parse().unwrap(), key: "tests/certs/xmpp.key".parse().unwrap(), + hostname: "localhost".into(), }; let mut metrics = MetricsRegistry::new(); let mut storage = Storage::open(StorageConfig { diff --git a/docs/running.md b/docs/running.md index 61f3067..ad422a1 100644 --- a/docs/running.md +++ b/docs/running.md @@ -19,6 +19,7 @@ server_name = "irc.localhost" listen_on = "127.0.0.1:5222" cert = "./certs/xmpp.pem" key = "./certs/xmpp.key" +hostname = "localhost" [storage] db_path = "db.sqlite"