tubes 0.6.4

Host/Client protocol based on pipenet
Documentation
use std::{net::IpAddr, time::Duration};

use pipenet::Versions;

/// Configure the connection.
/// See extra features `compression` or `encryption` to enable extra fields.
///
/// Config can be interpreted directly from a string, see examples in
/// [Session](crate::prelude::Session).
#[derive(Clone, Debug)]
pub struct Config {
    /// address the destination where to connect to when connecting,
    /// or the address to bind to when hosting.
    pub address: Option<IpAddr>,
    /// port to connect to when client or to bind to when hosting.
    pub port: u16,
    /// Versioning for the protocol.
    pub versions: Versions,
    /// How long to wait for a new accepted client that did not confirm its
    /// session with the protocol. Defaults to 5 secs. While waiting for this
    /// to time out, new connections are delayed from being accepted.
    pub accept_timeout: Duration,
    /// Enable compression (LZ4) implemented in pipenet.
    #[cfg(feature = "compression")]
    pub compress: bool,
    /// If ``Some()`` this key is used to enable encryption in pipenet.
    /// Algo used is ``ChaCha20Poly1305``.
    /// The key must be at least 32 bytes long.
    /// When this is None, no encryption is enabled.
    #[cfg(feature = "encryption")]
    pub key: Option<Vec<u8>>,
}

impl From<&str> for Config {
    fn from(value: &str) -> Self {
        let mut config = Config::default();
        let mut colons = value.split(':');
        config.address = colons.next().and_then(|s| s.parse().ok());
        let after = colons.next().expect("no port provided");
        let mut with_options = after.split('+');
        config.port = with_options
            .next()
            .and_then(|s| s.parse().ok())
            .expect("invalid port");
        #[cfg(feature = "compression")]
        if let Some(o) = with_options.next()
            && o == "C"
        {
            config.compress = true;
        }
        #[cfg(feature = "encryption")]
        if let Some(o) = with_options.next() {
            config.key = Some(o.bytes().collect::<Vec<_>>());
        }
        config
    }
}

impl Default for Config {
    fn default() -> Self {
        Self {
            address: Option::default(),
            port: u16::MAX,
            versions: Versions::default(),
            accept_timeout: Duration::from_secs(5),
            #[cfg(feature = "compression")]
            compress: Default::default(),
            #[cfg(feature = "encryption")]
            key: Option::default(),
        }
    }
}

// Secrets need proper zeroizing when dropped.
#[cfg(feature = "encryption")]
impl Drop for Config {
    fn drop(&mut self) {
        let Some(k) = self.key.as_mut() else {
            return;
        };
        for b in k.iter_mut() {
            *b = 0;
        }
    }
}