Add disabled-by-default insecure-tcp feature to tokio-xmpp for use by component connections

moparisthebest created

Change summary

tokio-xmpp/Cargo.toml                 |  3 +
tokio-xmpp/examples/echo_component.rs | 15 ++++----
tokio-xmpp/src/component/mod.rs       |  6 ++
tokio-xmpp/src/lib.rs                 |  2 +
tokio-xmpp/src/starttls/error.rs      |  4 +
tokio-xmpp/src/starttls/mod.rs        |  9 ++++-
tokio-xmpp/src/tcp/component.rs       | 10 +++++
tokio-xmpp/src/tcp/error.rs           | 26 +++++++++++++++
tokio-xmpp/src/tcp/mod.rs             | 49 +++++++++++++++++++++++++++++
9 files changed, 112 insertions(+), 12 deletions(-)

Detailed changes

tokio-xmpp/Cargo.toml 🔗

@@ -36,6 +36,8 @@ tokio-rustls = { version = "0.24", optional = true }
 
 [dev-dependencies]
 env_logger = { version = "0.10", default-features = false, features = ["auto-color", "humantime"] }
+# this is needed for echo-component example
+tokio-xmpp = { path = ".", features = ["insecure-tcp"]}
 
 [features]
 default = ["starttls-rust"]
@@ -44,4 +46,5 @@ tls-rust = ["tokio-rustls", "webpki-roots"]
 tls-native = ["tokio-native-tls", "native-tls"]
 starttls-native = ["starttls", "tls-native"]
 starttls-rust = ["starttls", "tls-rust"]
+insecure-tcp = []
 syntax-highlighting = ["syntect"]

tokio-xmpp/examples/echo_component.rs 🔗

@@ -2,7 +2,7 @@ use futures::stream::StreamExt;
 use std::env::args;
 use std::process::exit;
 use std::str::FromStr;
-use tokio_xmpp::Component;
+use tokio_xmpp::tcp::TcpComponent as Component;
 use xmpp_parsers::message::{Body, Message, MessageType};
 use xmpp_parsers::presence::{Presence, Show as PresenceShow, Type as PresenceType};
 use xmpp_parsers::{Element, Jid};
@@ -12,22 +12,21 @@ async fn main() {
     env_logger::init();
 
     let args: Vec<String> = args().collect();
-    if args.len() < 3 || args.len() > 5 {
-        println!("Usage: {} <jid> <password> [server] [port]", args[0]);
+    if args.len() < 3 || args.len() > 4 {
+        println!("Usage: {} <jid> <password> [server:port]", args[0]);
         exit(1);
     }
     let jid = &args[1];
     let password = &args[2];
-    let server = &args
+    let server = args
         .get(3)
         .unwrap()
         .parse()
-        .unwrap_or("127.0.0.1".to_owned());
-    let port: u16 = args.get(4).unwrap().parse().unwrap_or(5347u16);
+        .unwrap_or("127.0.0.1:5347".to_owned());
 
     // Component instance
-    println!("{} {} {} {}", jid, password, server, port);
-    let mut component = Component::new(jid, password, server, port).await.unwrap();
+    println!("{} {} {}", jid, password, server);
+    let mut component = Component::new(jid, password, server).await.unwrap();
 
     // Make the two interfaces for sending and receiving independent
     // of each other so we can move one into a closure.

tokio-xmpp/src/component/mod.rs 🔗

@@ -31,7 +31,11 @@ pub struct Component<C: ServerConnector> {
 
 impl<C: ServerConnector> Component<C> {
     /// Start a new XMPP component
-    pub async fn new(jid: &str, password: &str, connector: C) -> Result<Self, Error> {
+    pub async fn new_with_connector(
+        jid: &str,
+        password: &str,
+        connector: C,
+    ) -> Result<Self, Error> {
         let jid = Jid::from_str(jid)?;
         let password = password.to_owned();
         let stream = component_login(connector, jid.clone(), password).await?;

tokio-xmpp/src/lib.rs 🔗

@@ -17,6 +17,8 @@ compile_error!(
 #[cfg(feature = "starttls")]
 pub mod starttls;
 mod stream_start;
+#[cfg(feature = "insecure-tcp")]
+pub mod tcp;
 mod xmpp_codec;
 pub use crate::xmpp_codec::Packet;
 mod event;

tokio-xmpp/src/starttls/error.rs 🔗

@@ -1,3 +1,5 @@
+//! StartTLS ServerConnector Error
+
 use hickory_resolver::{error::ResolveError, proto::error::ProtoError};
 #[cfg(feature = "tls-native")]
 use native_tls::Error as TlsError;
@@ -9,7 +11,7 @@ use tokio_rustls::rustls::client::InvalidDnsNameError;
 #[cfg(all(feature = "tls-rust", not(feature = "tls-native")))]
 use tokio_rustls::rustls::Error as TlsError;
 
-/// Top-level error type
+/// StartTLS ServerConnector Error
 #[derive(Debug)]
 pub enum Error {
     /// Error resolving DNS and establishing a connection

tokio-xmpp/src/starttls/mod.rs 🔗

@@ -26,16 +26,21 @@ use tokio::{
 };
 use xmpp_parsers::{ns, Element, Jid};
 
-use crate::{connect::ServerConnector, xmpp_codec::Packet};
+use crate::{connect::ServerConnector, xmpp_codec::Packet, AsyncClient, SimpleClient};
 use crate::{connect::ServerConnectorError, xmpp_stream::XMPPStream};
 
 use self::error::Error;
 use self::happy_eyeballs::{connect_to_host, connect_with_srv};
 
 mod client;
-mod error;
+pub mod error;
 mod happy_eyeballs;
 
+/// AsyncClient that connects over StartTls
+pub type StartTlsAsyncClient = AsyncClient<ServerConfig>;
+/// SimpleClient that connects over StartTls
+pub type StartTlsSimpleClient = SimpleClient<ServerConfig>;
+
 /// StartTLS XMPP server connection configuration
 #[derive(Clone, Debug)]
 pub enum ServerConfig {

tokio-xmpp/src/tcp/component.rs 🔗

@@ -0,0 +1,10 @@
+use crate::{Component, Error};
+
+use super::TcpServerConnector;
+
+impl Component<TcpServerConnector> {
+    /// Start a new XMPP component
+    pub async fn new(jid: &str, password: &str, server: String) -> Result<Self, Error> {
+        Self::new_with_connector(jid, password, TcpServerConnector::new(server)).await
+    }
+}

tokio-xmpp/src/tcp/error.rs 🔗

@@ -0,0 +1,26 @@
+//! TCP ServerConnector Error
+
+use core::fmt;
+
+/// TCP ServerConnector Error
+#[derive(Debug)]
+pub enum Error {
+    /// tokio-xmpp error
+    TokioXMPP(crate::error::Error),
+}
+
+impl std::error::Error for Error {}
+
+impl fmt::Display for Error {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            Error::TokioXMPP(e) => write!(fmt, "TokioXMPP error: {}", e),
+        }
+    }
+}
+
+impl From<crate::error::Error> for Error {
+    fn from(e: crate::error::Error) -> Self {
+        Error::TokioXMPP(e)
+    }
+}

tokio-xmpp/src/tcp/mod.rs 🔗

@@ -0,0 +1,49 @@
+//! `starttls::ServerConfig` provides a `ServerConnector` for starttls connections
+
+use std::sync::Arc;
+
+use tokio::net::TcpStream;
+
+use crate::{
+    connect::{ServerConnector, ServerConnectorError},
+    xmpp_stream::XMPPStream,
+    Component,
+};
+
+use self::error::Error;
+
+mod component;
+pub mod error;
+
+/// Component that connects over TCP
+pub type TcpComponent = Component<TcpServerConnector>;
+
+/// Connect via insecure plaintext TCP to an XMPP server
+/// This should only be used over localhost or otherwise when you know what you are doing
+/// Probably mostly useful for Components
+#[derive(Debug, Clone)]
+pub struct TcpServerConnector(Arc<String>);
+
+impl TcpServerConnector {
+    /// Create a new connector with the given address
+    pub fn new(addr: String) -> Self {
+        Self(addr.into())
+    }
+}
+
+impl ServerConnectorError for Error {}
+
+impl ServerConnector for TcpServerConnector {
+    type Stream = TcpStream;
+    type Error = Error;
+    async fn connect(
+        &self,
+        jid: &xmpp_parsers::Jid,
+        ns: &str,
+    ) -> Result<XMPPStream<Self::Stream>, Self::Error> {
+        let stream = TcpStream::connect(&*self.0)
+            .await
+            .map_err(|e| crate::Error::Io(e))?;
+        Ok(XMPPStream::start(stream, jid.clone(), ns.to_owned()).await?)
+    }
+}