use bytes::BytesMut; use std::net::SocketAddr; use tokio::net::UdpSocket; use niom_turn::constants::*; #[tokio::main] async fn main() -> anyhow::Result<()> { tracing_subscriber::fmt::init(); let server: SocketAddr = "127.0.0.1:3478".parse()?; let local = UdpSocket::bind("0.0.0.0:0").await?; // Build a minimal STUN Binding Request with USERNAME and placeholder MESSAGE-INTEGRITY let username = "testuser"; let password = "secretpassword"; // matches server's in-memory creds let mut buf = BytesMut::new(); buf.extend_from_slice(&METHOD_BINDING.to_be_bytes()); // Binding Request buf.extend_from_slice(&0u16.to_be_bytes()); // length placeholder buf.extend_from_slice(&MAGIC_COOKIE_U32.to_be_bytes()); let trans = [7u8; 12]; buf.extend_from_slice(&trans); // USERNAME let uname = username.as_bytes(); buf.extend_from_slice(&ATTR_USERNAME.to_be_bytes()); buf.extend_from_slice(&(uname.len() as u16).to_be_bytes()); buf.extend_from_slice(uname); while (buf.len() % 4) != 0 { buf.extend_from_slice(&[0u8]); } // MESSAGE-INTEGRITY placeholder let mi_attr_offset = buf.len(); buf.extend_from_slice(&ATTR_MESSAGE_INTEGRITY.to_be_bytes()); buf.extend_from_slice(&(20u16).to_be_bytes()); let mi_val_pos = buf.len(); buf.extend_from_slice(&[0u8;20]); while (buf.len() % 4) != 0 { buf.extend_from_slice(&[0u8]); } // fix length let total_len = (buf.len() - 20) as u16; let len_bytes = total_len.to_be_bytes(); buf[2] = len_bytes[0]; buf[3] = len_bytes[1]; // compute HMAC over bytes up to MI attribute header { use hmac::{Hmac, Mac}; use sha1::Sha1; type HmacSha1 = Hmac; let mut mac = HmacSha1::new_from_slice(password.as_bytes()).expect("HMAC key"); mac.update(&buf[..mi_attr_offset]); let res = mac.finalize().into_bytes(); for i in 0..20 { buf[mi_val_pos + i] = res[i]; } } // send local.send_to(&buf, server).await?; let mut r = vec![0u8; 1500]; let (len, addr) = local.recv_from(&mut r).await?; println!("got {} bytes from {}", len, addr); // dump hex println!("{:02x?}", &r[..len]); Ok(()) }