nvpn 4.1.13

CLI and daemon for Nostr VPN private mesh networks
#[cfg(target_os = "windows")]
fn start_windows_fips_wintun(
    config: &FipsPrivateTunnelConfig,
) -> Result<(Arc<Session>, String, u32)> {
    let wintun = load_wintun()?;
    let adapter = Adapter::open(&wintun, &config.iface)
        .or_else(|_| Adapter::create(&wintun, &config.iface, "NostrVPN", None))
        .with_context(|| format!("failed to open or create wintun adapter {}", config.iface))?;
    adapter
        .set_mtu(config.mesh_mtu.tunnel as usize)
        .with_context(|| format!("failed to set MTU on wintun adapter {}", config.iface))?;
    let parsed_address = crate::windows_tunnel::windows_interface_address(&config.local_address)?;
    adapter
        .set_network_addresses_tuple(
            parsed_address.address.into(),
            parsed_address.mask.into(),
            None,
        )
        .with_context(|| format!("failed to set address on wintun adapter {}", config.iface))?;
    let interface_index = adapter
        .get_adapter_index()
        .with_context(|| format!("failed to resolve interface index for {}", config.iface))?;
    let session = Arc::new(
        adapter
            .start_session(MAX_RING_CAPACITY)
            .with_context(|| format!("failed to start wintun session for {}", config.iface))?,
    );
    Ok((session, config.iface.clone(), interface_index))
}

#[cfg(target_os = "windows")]
fn spawn_windows_fips_tun_read_thread(
    stop: Arc<AtomicBool>,
    session: Arc<Session>,
    mesh: Arc<FipsPrivateMeshRuntime>,
) -> ThreadJoinHandle<()> {
    thread::spawn(move || {
        let pipeline_profile_enabled = crate::pipeline_profile::enabled();
        let packet_debug = windows_fips_packet_debug_enabled();
        let mut send_runs = Vec::new();
        while !stop.load(Ordering::Relaxed) {
            let packet = match session.receive_blocking() {
                Ok(packet) => packet,
                Err(error) => {
                    if !stop.load(Ordering::Relaxed) {
                        eprintln!("fips: Windows Wintun receive failed: {error}");
                    }
                    break;
                }
            };
            let mut batch = Vec::with_capacity(WINDOWS_FIPS_TUN_READ_BURST);
            let mut batch_bytes = 0usize;
            {
                let _t =
                    crate::pipeline_profile::Timer::start(crate::pipeline_profile::Stage::TunRead);
                let payload = packet.bytes().to_vec();
                drop(packet);
                batch_bytes = batch_bytes.saturating_add(payload.len());
                if packet_debug {
                    eprintln!(
                        "fips: Windows Wintun read {} bytes {}",
                        payload.len(),
                        describe_ip_packet(&payload)
                    );
                }
                batch.push(payload);
                while batch.len() < WINDOWS_FIPS_TUN_READ_BURST {
                    match session.try_receive() {
                        Ok(Some(packet)) => {
                            let payload = packet.bytes().to_vec();
                            drop(packet);
                            batch_bytes = batch_bytes.saturating_add(payload.len());
                            if packet_debug {
                                eprintln!(
                                    "fips: Windows Wintun read {} bytes {}",
                                    payload.len(),
                                    describe_ip_packet(&payload)
                                );
                            }
                            batch.push(payload);
                        }
                        Ok(None) => break,
                        Err(error) => {
                            if !stop.load(Ordering::Relaxed) {
                                eprintln!("fips: Windows Wintun receive failed: {error}");
                            }
                            return;
                        }
                    }
                }
            }
            if pipeline_profile_enabled {
                crate::pipeline_profile::record_tun_read_batch(
                    batch.len(),
                    batch_bytes,
                    WINDOWS_FIPS_TUN_READ_BURST,
                );
            }
            let packet_count = batch.len();
            crate::pipeline_profile::record_mesh_send_bulk_turn(0, packet_count);
            let send_result = {
                let _t = crate::pipeline_profile::Timer::start(
                    crate::pipeline_profile::Stage::MeshSend,
                );
                mesh.blocking_send_tunnel_packet_batch_owned_with_capacity(
                    batch,
                    WINDOWS_FIPS_TUN_READ_BURST,
                    &mut send_runs,
                )
            };
            match send_result {
                Ok(sent) if sent == packet_count => {}
                Ok(_sent) if packet_debug => {
                    eprintln!("fips: Windows mesh route miss");
                }
                Ok(_sent) => {}
                Err(error) => {
                    eprintln!("fips: failed to send Windows tunnel packet: {error}");
                    thread::sleep(Duration::from_millis(100));
                }
            }
        }
    })
}

#[cfg(target_os = "windows")]
fn spawn_windows_fips_mesh_recv_task(
    stop: Arc<AtomicBool>,
    mesh: Arc<FipsPrivateMeshRuntime>,
    session: Arc<Session>,
    event_tx: mpsc::Sender<FipsPrivateMeshEvent>,
) -> JoinHandle<()> {
    tokio::task::spawn_blocking(move || {
        let recv_burst = FIPS_MESH_RECV_BURST;
        let mut packet_batch = DirectTunWriteBatch::with_capacity(recv_burst);
        let pipeline_profile_enabled = crate::pipeline_profile::enabled();
        while !stop.load(Ordering::Acquire) {
            packet_batch.clear();
            let received = mesh.recv_direct_endpoint_tun_batch_blocking(
                &stop,
                &mut packet_batch,
                &event_tx,
            );
            match received {
                Ok(Some(drained)) => {
                    if let Err(error) =
                        mesh.finalize_direct_endpoint_tun_batch_blocking(&packet_batch)
                    {
                        packet_batch.clear();
                        eprintln!("fips: failed to finalize Windows tunnel packet batch: {error}");
                        std::thread::sleep(Duration::from_millis(100));
                        continue;
                    }
                    if pipeline_profile_enabled {
                        let packet_count = packet_batch.len();
                        let packet_bytes = packet_batch.bytes();
                        crate::pipeline_profile::record_mesh_recv_batch(
                            drained,
                            packet_count,
                            packet_bytes,
                            recv_burst,
                        );
                    }
                    write_windows_direct_endpoint_packet_batch(&session, &mut packet_batch);
                    if drained >= recv_burst {
                        std::thread::yield_now();
                    }
                }
                Ok(None) => {
                    if let Err(error) =
                        mesh.finalize_direct_endpoint_tun_batch_blocking(&packet_batch)
                    {
                        packet_batch.clear();
                        eprintln!("fips: failed to finalize Windows tunnel packet batch: {error}");
                    }
                    write_windows_direct_endpoint_packet_batch(&session, &mut packet_batch);
                    break;
                }
                Err(error) => {
                    eprintln!("fips: failed to receive Windows tunnel packet: {error}");
                    std::thread::sleep(Duration::from_millis(100));
                }
            }
        }
    })
}

#[cfg(target_os = "windows")]
fn write_windows_direct_endpoint_packet_batch(
    session: &Arc<Session>,
    packet_batch: &mut DirectTunWriteBatch,
) {
    if packet_batch.is_empty() {
        return;
    }
    let _batch_timer =
        crate::pipeline_profile::Timer::start(crate::pipeline_profile::Stage::TunWriteBatch);
    let packet_debug = windows_fips_packet_debug_enabled();
    if packet_debug {
        for packet in packet_batch.run_slices() {
            eprintln!(
                "fips: Windows mesh -> Wintun {} bytes {}",
                packet.len(),
                describe_ip_packet(packet)
            );
        }
    }
    if crate::pipeline_profile::enabled() {
        crate::pipeline_profile::record_tun_write_packets(
            packet_batch.len(),
            packet_batch.bytes(),
        );
        for packet in packet_batch.run_slices() {
            crate::pipeline_profile::record_tun_write_frame(packet.len());
        }
    }
    let write_result = {
        let _t = crate::pipeline_profile::Timer::start(crate::pipeline_profile::Stage::TunWrite);
        crate::windows_tunnel::write_tunnel_packet_slices(session, packet_batch.run_slices())
    };
    if let Err(error) = write_result {
        eprintln!("fips: failed to write Windows tunnel packet: {error}");
    }
    packet_batch.clear();
}

#[cfg(target_os = "windows")]
fn windows_fips_packet_debug_enabled() -> bool {
    static ENABLED: OnceLock<bool> = OnceLock::new();
    *ENABLED.get_or_init(|| std::env::var_os("NVPN_FIPS_PACKET_DEBUG").is_some())
}

#[cfg(target_os = "windows")]
fn describe_ip_packet(packet: &[u8]) -> String {
    match packet.first().map(|byte| byte >> 4) {
        Some(4) if packet.len() >= 20 => format!(
            "{} -> {}",
            std::net::Ipv4Addr::new(packet[12], packet[13], packet[14], packet[15]),
            std::net::Ipv4Addr::new(packet[16], packet[17], packet[18], packet[19])
        ),
        Some(6) if packet.len() >= 40 => "IPv6".to_string(),
        Some(version) => format!("IPv{version} malformed"),
        None => "empty packet".to_string(),
    }
}