punchline-stund 0.1.0

STUN server for punchline P2P messenger
Documentation
use std::net::UdpSocket;

use anyhow::Context;
use clap::Parser;
use punchline_proto::stun;
use punchline_proto::transport::Transport;
use punchline_proto::udp::UdpTransport;
use punchline_stund::cli::Args;
use tracing::{debug, error, info, warn};

fn main() -> anyhow::Result<()> {
    let args = Args::parse();

    let log_level = if args.quiet {
        None
    } else {
        match args.verbose {
            0 => Some(tracing::Level::INFO),
            1 => Some(tracing::Level::DEBUG),
            _ => Some(tracing::Level::TRACE),
        }
    };

    if let Some(level) = log_level {
        tracing_subscriber::fmt().with_max_level(level).init();
    }

    let sock = UdpTransport::new(
        UdpSocket::bind(format!("{}:{}", args.address, args.port))
            .context("Failed to bind STUN server socket")?,
    );
    let mut buf = [0u8; 1024];

    info!("STUN server listening on {}:{}", args.address, args.port);

    loop {
        let (len, src_addr) = sock.recv_from(&mut buf)?;
        let request = &buf[..len];

        let header = match stun::parse_header(request) {
            Ok(h) => h,
            Err(e) => {
                warn!(%src_addr, %e, "Invalid STUN packet");
                continue;
            }
        };

        if !stun::is_binding_request(&header) {
            warn!(%src_addr, msg_type = format_args!("0x{:04x}", header.msg_type), "Unexpected message type");
            continue;
        }

        debug!(%src_addr, "Binding Request");

        let response = match stun::build_binding_response(&header.transaction_id, src_addr) {
            Ok(r) => r,
            Err(e) => {
                warn!(%src_addr, %e, "Failed to build response");
                continue;
            }
        };

        if let Err(e) = sock.send_to(&response, src_addr) {
            error!(%src_addr, %e, "Failed to send response");
        }
    }
}