nvpn 4.1.13

CLI and daemon for Nostr VPN private mesh networks
#[cfg(any(target_os = "linux", target_os = "macos"))]
fn temporary_tun_read_error(error: &io::Error) -> bool {
    matches!(
        error.kind(),
        std::io::ErrorKind::WouldBlock | std::io::ErrorKind::Interrupted
    )
}

#[cfg(any(target_os = "linux", target_os = "macos"))]
fn fips_unix_packet_debug_enabled() -> bool {
    static ENABLED: OnceLock<bool> = OnceLock::new();
    *ENABLED.get_or_init(|| {
        std::env::var("NVPN_FIPS_PACKET_DEBUG")
            .ok()
            .is_some_and(|value| fips_packet_debug_value_enabled(&value))
    })
}

fn fips_packet_debug_value_enabled(value: &str) -> bool {
    let value = value.trim();
    !(value.is_empty()
        || value == "0"
        || value.eq_ignore_ascii_case("false")
        || value.eq_ignore_ascii_case("no")
        || value.eq_ignore_ascii_case("off"))
}

#[cfg(any(target_os = "linux", target_os = "macos"))]
fn describe_ip_packet(packet: &[u8]) -> String {
    match packet.first().map(|byte| byte >> 4) {
        Some(4) if packet.len() >= 20 => {
            let src = std::net::Ipv4Addr::new(packet[12], packet[13], packet[14], packet[15]);
            let dst = std::net::Ipv4Addr::new(packet[16], packet[17], packet[18], packet[19]);
            format!("IPv4 proto={} {src}->{dst}", packet[9])
        }
        Some(6) if packet.len() >= 40 => {
            let src = std::net::Ipv6Addr::from([
                packet[8], packet[9], packet[10], packet[11], packet[12], packet[13], packet[14],
                packet[15], packet[16], packet[17], packet[18], packet[19], packet[20],
                packet[21], packet[22], packet[23],
            ]);
            let dst = std::net::Ipv6Addr::from([
                packet[24], packet[25], packet[26], packet[27], packet[28], packet[29],
                packet[30], packet[31], packet[32], packet[33], packet[34], packet[35],
                packet[36], packet[37], packet[38], packet[39],
            ]);
            format!("IPv6 next_header={} {src}->{dst}", packet[6])
        }
        Some(version) => format!("IP version {version} short packet"),
        None => "empty packet".to_string(),
    }
}