#[cfg(feature = "qlog")]
use std::path::PathBuf;
use std::{io, net::SocketAddr, num::ParseIntError, str::FromStr, sync::Arc, time::Duration};

use anyhow::{Context, Result};
use clap::{Parser, ValueEnum};
use quinn::{
    AckFrequencyConfig, TransportConfig, VarInt,
    congestion::{self, ControllerFactory},
    udp::UdpSocketState,
};
use rustls::crypto::ring::cipher_suite;
use socket2::{Domain, Protocol, Socket, Type};
use tracing::warn;

#[cfg_attr(not(feature = "json-output"), allow(dead_code))]
pub mod stats;

pub mod noprotection;

pub mod client;
pub mod server;

// Common options between client and server binary
#[derive(Parser)]
pub struct CommonOpt {
    /// Send buffer size in bytes
    ///
    /// This can use SI suffixes for sizes. For example, 1M will request
    /// 1MiB, 10G will request 10GiB.
    #[clap(long, default_value = "2M", value_parser = parse_byte_size)]
    pub send_buffer_size: u64,
    /// Receive buffer size in bytes
    ///
    /// This can use SI suffixes for sizes. For example, 1M will request
    /// 1MiB, 10G will request 10GiB.
    #[clap(long, default_value = "2M", value_parser = parse_byte_size)]
    pub recv_buffer_size: u64,
    /// Whether to print connection statistics
    #[clap(long)]
    pub conn_stats: bool,
    /// Perform NSS-compatible TLS key logging to the file specified in `SSLKEYLOGFILE`.
    #[clap(long = "keylog")]
    pub keylog: bool,
    /// UDP payload size that the network must be capable of carrying
    #[clap(long, default_value = "1200")]
    pub initial_mtu: u16,
    /// Disable packet encryption/decryption (for debugging purpose)
    #[clap(long = "no-protection")]
    pub no_protection: bool,
    /// The initial round-trip-time (in msecs)
    #[clap(long, group = "common")]
    pub initial_rtt: Option<u64>,
    /// Ack Frequency mode
    #[clap(long = "ack-frequency")]
    pub ack_frequency: bool,
    /// Congestion algorithm to use
    #[clap(long = "congestion")]
    pub cong_alg: Option<CongestionAlgorithm>,
    /// Maximum number of bytes the peer may transmit without acknowledgement on any one stream
    /// before becoming blocked.
    ///
    /// This can use SI suffixes for sizes. For example, 1M will limit to
    /// 1MiB, 10G will limit to 10GiB.
    #[clap(long, value_parser = parse_byte_size)]
    pub stream_receive_window: Option<u64>,
    /// Maximum number of bytes the peer may transmit across all streams of a connection before
    /// becoming blocked.
    ///
    /// This can use SI suffixes for sizes. For example, 1M will limit to
    /// 1MiB, 10G will limit to 10GiB.
    #[clap(long, value_parser = parse_byte_size)]
    pub receive_window: Option<u64>,
    /// Maximum number of bytes to transmit to a peer without acknowledgment
    ///
    /// This can use SI suffixes for sizes. For example, 1M will limit to
    /// 1MiB, 10G will limit to 10GiB.
    #[clap(long, value_parser = parse_byte_size)]
    pub send_window: Option<u64>,
    /// Max UDP payload size in bytes
    #[clap(long, default_value = "1472")]
    pub max_udp_payload_size: u16,
    /// qlog output directory
    ///
    /// Alternatively you can set the `QLOGDIR` environment variable.
    #[cfg(feature = "qlog")]
    #[clap(long = "qlog")]
    pub qlog_dir: Option<PathBuf>,
}

impl CommonOpt {
    pub fn build_transport_config(
        &self,
        #[cfg(feature = "qlog")] name: &str,
    ) -> io::Result<TransportConfig> {
        let mut transport = TransportConfig::default();
        transport.initial_mtu(self.initial_mtu);

        if let Some(initial_rtt) = self.initial_rtt {
            transport.initial_rtt(Duration::from_millis(initial_rtt));
        }

        if self.ack_frequency {
            transport.ack_frequency_config(Some(AckFrequencyConfig::default()));
        }

        if let Some(cong_alg) = self.cong_alg {
            transport.congestion_controller_factory(cong_alg.build());
        }

        if let Some(stream_receive_window) = self.stream_receive_window {
            transport.stream_receive_window(
                VarInt::from_u64(stream_receive_window).unwrap_or(VarInt::MAX),
            );
        }

        if let Some(receive_window) = self.receive_window {
            transport.receive_window(VarInt::from_u64(receive_window).unwrap_or(VarInt::MAX));
        }

        if let Some(send_window) = self.send_window {
            transport.send_window(send_window);
        }

        #[cfg(feature = "qlog")]
        if let Some(qlog_dir) = &self.qlog_dir {
            transport.qlog_from_path(qlog_dir, name);
        } else {
            transport.qlog_from_env(name);
        }

        Ok(transport)
    }

    pub fn bind_socket(&self, addr: SocketAddr) -> Result<std::net::UdpSocket> {
        let socket = Socket::new(Domain::for_address(addr), Type::DGRAM, Some(Protocol::UDP))
            .context("create socket")?;

        if addr.is_ipv6() {
            socket.set_only_v6(false).context("set_only_v6")?;
        }

        socket
            .bind(&socket2::SockAddr::from(addr))
            .context("binding endpoint")?;

        let socket_state = UdpSocketState::new((&socket).into())?;
        socket_state
            .set_send_buffer_size((&socket).into(), self.send_buffer_size as usize)
            .context("send buffer size")?;
        socket_state
            .set_recv_buffer_size((&socket).into(), self.recv_buffer_size as usize)
            .context("recv buffer size")?;

        let buf_size = socket_state
            .send_buffer_size((&socket).into())
            .context("send buffer size")?;
        if buf_size < self.send_buffer_size as usize {
            warn!(
                "Unable to set desired send buffer size. Desired: {}, Actual: {}",
                self.send_buffer_size, buf_size
            );
        }

        let buf_size = socket_state
            .recv_buffer_size((&socket).into())
            .context("recv buffer size")?;
        if buf_size < self.recv_buffer_size as usize {
            warn!(
                "Unable to set desired recv buffer size. Desired: {}, Actual: {}",
                self.recv_buffer_size, buf_size
            );
        }

        Ok(socket.into())
    }
}

pub fn parse_byte_size(s: &str) -> Result<u64, ParseIntError> {
    let s = s.trim();

    let multiplier = match s.chars().last() {
        Some('T') => 1024 * 1024 * 1024 * 1024,
        Some('G') => 1024 * 1024 * 1024,
        Some('M') => 1024 * 1024,
        Some('k') => 1024,
        _ => 1,
    };

    let s = match multiplier {
        1 => s,
        _ => &s[..s.len() - 1],
    };

    Ok(u64::from_str(s)? * multiplier)
}

#[derive(Clone, Copy, ValueEnum)]
pub enum CongestionAlgorithm {
    Cubic,
    Bbr,
    NewReno,
}

impl CongestionAlgorithm {
    pub fn build(self) -> Arc<dyn ControllerFactory + Send + Sync + 'static> {
        match self {
            Self::Cubic => Arc::new(congestion::CubicConfig::default()),
            Self::Bbr => Arc::new(congestion::BbrConfig::default()),
            Self::NewReno => Arc::new(congestion::NewRenoConfig::default()),
        }
    }
}

pub static PERF_CIPHER_SUITES: &[rustls::SupportedCipherSuite] = &[
    cipher_suite::TLS13_AES_128_GCM_SHA256,
    cipher_suite::TLS13_AES_256_GCM_SHA384,
    cipher_suite::TLS13_CHACHA20_POLY1305_SHA256,
];

Homonyms

soft3/mir/src/lib.rs
soft3/strata/src/lib.rs
soft3/lens/src/lib.rs
soft3/crate/src/lib.rs
cyb/prysm/rs/lib.rs
cyb/honeycrisp/src/lib.rs
soft3/mudra/src/lib.rs
soft3/cybergraph/src/lib.rs
warriors/trisha/wgpu/lib.rs
soft3/glia/run/lib.rs
soft3/nox/rs/lib.rs
warriors/trisha/rs/lib.rs
soft3/tru/rs/lib.rs
cyb/optica/src/lib.rs
warriors/trisha/honeycrisp/lib.rs
soft3/foculus/src/lib.rs
cyb/core/src/lib.rs
soft3/glia/import/lib.rs
cyb/shell/src/lib.rs
neural/trident/src/lib.rs
neural/rune/rs/ast/lib.rs
cyb/crates/cyb/src/lib.rs
neural/rs/dialect/src/lib.rs
neural/rune/rs/compile/lib.rs
neural/rs/link/src/lib.rs
neural/rune/rs/lex/lib.rs
soft3/hemera/rs/src/lib.rs
soft3/radio/iroh/src/lib.rs
soft3/lens/core/src/lib.rs
cyb/honeycrisp/unimem/src/lib.rs
soft3/radio/iroh-ffi/src/lib.rs
soft3/lens/brakedown/src/lib.rs
soft3/lens/binius/src/lib.rs
neural/rune/rs/interp/lib.rs
soft3/radio/iroh-blobs/src/lib.rs
neural/rune/rs/parse/lib.rs
neural/rune/rs/mold/lib.rs
neural/rune/rs/subject/lib.rs
soft3/strata/proof/src/lib.rs
soft3/lens/ikat/src/lib.rs
soft3/radio/iroh-docs/src/lib.rs
soft3/radio/cyber-bao/src/lib.rs
cyb/honeycrisp/acpu/src/lib.rs
neural/eidos/rs/src/lib.rs
soft3/hemera/wgsl/src/lib.rs
soft3/lens/porphyry/src/lib.rs
neural/rs/macros/src/lib.rs
soft3/bbg/rs/src/lib.rs
cyb/honeycrisp/rane/src/lib.rs
soft3/radio/iroh-base/src/lib.rs
neural/rune/rs/parse-pure/lib.rs
neural/rs/mir-format/src/lib.rs
soft3/zheng/rs/src/lib.rs
soft3/lens/assayer/src/lib.rs
neural/rune/rs/prysm/lib.rs
soft3/strata/compute/src/lib.rs
soft3/tok/rs/src/lib.rs
soft3/strata/nebu/rs/lib.rs
soft3/radio/iroh-dns-server/src/lib.rs
soft3/conformance/rs/src/lib.rs
cyb/honeycrisp/aruminium/src/lib.rs
soft3/strata/kuro/rs/lib.rs
soft3/radio/iroh-car/src/lib.rs
soft3/radio/iroh-gossip/src/lib.rs
soft3/radio/iroh-willow/src/lib.rs
soft3/strata/ext/src/lib.rs
neural/rs/sigil/src/lib.rs
neural/rs/core/src/lib.rs
cyb/crates/cyb-reserve/src/lib.rs
soft3/radio/iroh-relay/src/lib.rs
neural/rs/darwin-sys/src/lib.rs
soft3/strata/core/src/lib.rs
neural/rs/codegen/src/lib.rs
neural/rune/rs/lower/lib.rs
cyb/wysm/crates/wasmi/src/lib.rs
soft3/radio/quinn/quinn/src/lib.rs
soft3/radio/quinn/quinn-proto/src/lib.rs
cyb/evy/forks/bevy_mesh/src/lib.rs
cyb/evy/crates/evy_diagnostic/src/lib.rs
cyb/evy/forks/bevy_anti_alias/src/lib.rs
neural/inf/rs/parse/src/lib.rs
neural/inf/rs/ast/src/lib.rs
cyb/evy/forks/bevy_post_process/src/lib.rs
soft3/radio/nettools/netwatch/src/lib.rs
neural/inf/rs/plan/src/lib.rs
cyb/evy/crates/evy_ecs_storage/src/lib.rs
soft3/strata/jali/rs/src/lib.rs
cyb/evy/crates/evy_engine_core/src/lib.rs
cyb/wysm/crates/collections/src/lib.rs
neural/inf/rs/oracle/src/lib.rs
cyb/evy/forks/bevy_sprite_render/src/lib.rs
cyb/wysm/crates/wast/src/lib.rs
cyb/evy/crates/evy_prysm_core/src/lib.rs
soft3/radio/quinn/quinn-udp/src/lib.rs
soft3/strata/kuro/wgsl/src/lib.rs
cyb/evy/forks/bevy_sprite/src/lib.rs
soft3/lytics/rs/core/src/lib.rs
cyb/wysm/crates/c_api/macro/lib.rs
cyb/evy/forks/bevy_gizmos/src/lib.rs
soft3/strata/nebu/wgsl/src/lib.rs
soft3/radio/nettools/portmapper/src/lib.rs
cyb/evy/forks/bevy_gizmos_render/src/lib.rs
cyb/evy/forks/bevy_transform/src/lib.rs
cyb/wysm/crates/wasi/src/lib.rs
soft3/radio/tests/integration/src/lib.rs
neural/trident/editor/zed/src/lib.rs
cyb/evy/forks/bevy_core_pipeline/src/lib.rs
cyb/evy/forks/bevy_pbr/src/lib.rs
cyb/evy/crates/evy_engine_dispatch/src/lib.rs
soft3/lytics/rs/event/src/lib.rs
cyb/evy/forks/bevy_ecs/src/lib.rs
cyb/evy/forks/bevy_diagnostic/src/lib.rs
neural/inf/rs/lex/src/lib.rs
neural/inf/rs/eval/src/lib.rs
cyb/evy/forks/naga/src/lib.rs
cyb/evy/crates/evy_platform_caps/src/lib.rs
soft3/strata/trop/wgsl/src/lib.rs
cyb/wysm/crates/core/src/lib.rs
cyb/wysm/crates/c_api/artifact/lib.rs
cyb/wysm/crates/fuzz/src/lib.rs
cyb/evy/forks/bevy_animation/src/lib.rs
soft3/strata/genies/wgsl/src/lib.rs
soft3/radio/quinn/bench/src/lib.rs
cyb/evy/crates/evy_dialect/src/lib.rs
soft3/tape/impl/rust/src/lib.rs
soft3/strata/trop/rs/src/lib.rs
cyb/evy/crates/evy_engine_tasks/src/lib.rs
neural/rs/tests/macro-integration/src/lib.rs
soft3/strata/genies/rs/src/lib.rs
cyb/evy/crates/evy_radio/src/lib.rs
soft3/radio/iroh/bench/src/lib.rs
cyb/wysm/crates/c_api/src/lib.rs
cyb/evy/forks/bevy_tasks/src/lib.rs
cyb/evy/forks/bevy_render/src/lib.rs
cyb/wysm/crates/ir/src/lib.rs
neural/inf/rs/source/src/lib.rs
soft3/radio/iroh-ffi/iroh-js/src/lib.rs
soft3/strata/jali/wgsl/src/lib.rs
neural/inf/rs/value/src/lib.rs
cyb/evy/forks/bevy_image/src/lib.rs
neural/inf/rs/lower/src/lib.rs
bootloader/go-cyber/cw/contracts/graph-filter/src/lib.rs
bootloader/go-cyber/cw/packages/cyber-std-test/src/lib.rs
bootloader/go-cyber/cw/contracts/std-test/src/lib.rs
bootloader/go-cyber/cw/packages/cyber-std/src/lib.rs

Graph