From c10be75b8ea4e4e8436ea7cb03641ccb55584cd5 Mon Sep 17 00:00:00 2001 From: link2xt Date: Tue, 7 Oct 2025 16:19:22 +0000 Subject: [PATCH] feat: add Unix socket to request credentials --- Cargo.lock | 1 + Cargo.toml | 1 + src/main.rs | 45 +++++++++++++++++++++++++++++++++++++++------ 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7e88cf0..14aa537 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -184,6 +184,7 @@ checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" name = "chatmail-turn" version = "0.1.0" dependencies = [ + "anyhow", "clap", "env_logger", "netdev", diff --git a/Cargo.toml b/Cargo.toml index 5fa8902..3fc9d01 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] +anyhow = "1" clap = "3" env_logger = "0.11.8" netdev = { version = "0.37.3", default-features = false } diff --git a/src/main.rs b/src/main.rs index d7feeac..513941d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,15 @@ use std::collections::BTreeSet; use std::net::IpAddr; +use std::path::Path; use std::sync::Arc; +use std::time::Duration; +use anyhow::{Context as _, Result}; use clap::{App, AppSettings, Arg}; -use tokio::net::UdpSocket; -use tokio::signal; -use tokio::time::Duration; +use tokio::io::AsyncWriteExt; +use tokio::net::{UdpSocket, UnixListener}; use turn::Error; +use turn::auth::generate_long_term_credentials; use turn::auth::*; use turn::relay::relay_static::RelayAddressGeneratorStatic; use turn::server::Server; @@ -22,6 +25,25 @@ fn public_ips() -> BTreeSet { ip_set } +/// Listens on the Unix socket, +/// returning valid credentials to any connecting client. +async fn socket_loop(path: &Path, shared_secret: &str) -> Result<()> { + let listener = UnixListener::bind(path).context("Failed to bind Unix socket")?; + loop { + match listener.accept().await { + Ok((mut stream, _addr)) => { + let duration = Duration::from_secs(5 * 24 * 3600); + let (username, password) = generate_long_term_credentials(shared_secret, duration)?; + let res = format!("{username}:{password}"); + stream.write_all(res.as_bytes()).await?; + } + Err(err) => { + eprintln!("Unix connection failed: {err}."); + } + } + } +} + #[tokio::main] async fn main() -> Result<(), Error> { env_logger::init(); @@ -41,6 +63,13 @@ async fn main() -> Result<(), Error> { .takes_value(true) .long("realm") .help("Realm (defaults to \"webrtc.rs\")"), + ) + .arg( + Arg::with_name("socket") + .required(true) + .takes_value(true) + .long("socket") + .help("Unix socket path"), ); let matches = app.clone().get_matches(); @@ -52,6 +81,7 @@ async fn main() -> Result<(), Error> { let port = 3478; let realm = matches.value_of("realm").unwrap(); + let socket_path = Path::new(matches.value_of("socket").unwrap()); let mut conn_configs = Vec::new(); for public_ip in public_ips() { @@ -68,7 +98,8 @@ async fn main() -> Result<(), Error> { conn_configs.push(conn_config); } - let auth_handler = LongTermAuthHandler::new("north".to_string()); + let shared_secret = "north"; + let auth_handler = LongTermAuthHandler::new(shared_secret.to_string()); let server = Server::new(ServerConfig { conn_configs, @@ -79,8 +110,10 @@ async fn main() -> Result<(), Error> { }) .await?; - println!("Waiting for Ctrl-C..."); - signal::ctrl_c().await.expect("failed to listen for event"); + socket_loop(Path::new(socket_path), shared_secret) + .await + .unwrap(); + server.close().await?; Ok(())