stochastic-routing-extended 1.0.2

SRX (Stochastic Routing eXtended) — a next-generation VPN protocol with stochastic routing, DPI evasion, post-quantum cryptography, and multi-transport channel splitting
Documentation
//! Dynamic port schedule derived from the shared seed.
//!
//! Generates time-windowed port assignments so that client and server
//! agree on which ports to use at any given moment, without a static
//! listening port.

use super::generator::SeedRng;

/// A port schedule entry: which port to listen/connect on during a time window.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PortWindow {
    /// Start of the time window (seconds since session start).
    pub start_secs: u64,
    /// Duration of the window in seconds.
    pub duration_secs: u64,
    /// Port to use during this window.
    pub port: u16,
}

/// Generates the deterministic port schedule from a seed.
pub struct PortSchedule {
    rng: SeedRng,
    port_range: (u16, u16),
}

impl PortSchedule {
    /// Create a new port schedule generator.
    pub fn new(seed: [u8; 32], port_range: (u16, u16)) -> Self {
        Self {
            rng: SeedRng::new(seed),
            port_range,
        }
    }

    /// Generate the next `count` port windows (deterministic given the same RNG draw order).
    pub fn generate(&mut self, count: usize) -> Vec<PortWindow> {
        let (lo, hi) = self.port_range;
        if count == 0 || hi <= lo {
            return Vec::new();
        }

        let span = (hi - lo) as u64;
        let mut out = Vec::with_capacity(count);
        let mut t = 0u64;

        for _ in 0..count {
            let duration_secs = self.rng.range(30, 3600).max(1);
            let port = lo + (self.rng.range(0, span) as u16);
            out.push(PortWindow {
                start_secs: t,
                duration_secs,
                port,
            });
            t = t.saturating_add(duration_secs);
        }

        out
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn windows_stay_in_range() {
        let mut ps = PortSchedule::new([0x5Eu8; 32], (40_000, 40_100));
        let w = ps.generate(5);
        assert_eq!(w.len(), 5);
        for win in &w {
            assert!((40_000..40_100).contains(&win.port));
            assert!(win.duration_secs >= 1);
        }
    }

    #[test]
    fn empty_on_bad_range() {
        let mut ps = PortSchedule::new([1u8; 32], (100, 100));
        assert!(ps.generate(3).is_empty());
    }
}