Replace STUN/TURN hex literals with RFC/project constants; refactor main to use library exports
This commit is contained in:
parent
5bbeb8fc55
commit
8363217c96
@ -10,6 +10,10 @@ pub const METHOD_ALLOCATE: u16 = 0x0003;
|
||||
// Common response/error types
|
||||
pub const RESP_BINDING_SUCCESS: u16 = 0x0101;
|
||||
|
||||
// STUN/TURN class bits per RFC5389/RFC5766
|
||||
pub const CLASS_SUCCESS: u16 = 0x0100;
|
||||
pub const CLASS_ERROR: u16 = 0x0110;
|
||||
|
||||
// Common attribute types
|
||||
pub const ATTR_USERNAME: u16 = 0x0006;
|
||||
pub const ATTR_MESSAGE_INTEGRITY: u16 = 0x0008;
|
||||
@ -22,3 +26,9 @@ pub const ATTR_XOR_RELAYED_ADDRESS: u16 = 0x0016;
|
||||
// Some helper values
|
||||
pub const FAMILY_IPV4: u8 = 0x01;
|
||||
|
||||
// Fingerprint XOR magic (XOR with CRC32 for FINGERPRINT attribute)
|
||||
pub const FINGERPRINT_XOR: u32 = 0x5354554e;
|
||||
|
||||
// Length of HMAC-SHA1 (MESSAGE-INTEGRITY)
|
||||
pub const HMAC_SHA1_LEN: usize = 20;
|
||||
|
||||
|
||||
28
src/main.rs
28
src/main.rs
@ -3,18 +3,12 @@ use std::sync::Arc;
|
||||
use tokio::net::UdpSocket;
|
||||
use tracing::{info, error};
|
||||
|
||||
mod stun;
|
||||
mod auth;
|
||||
mod traits;
|
||||
mod models;
|
||||
mod alloc;
|
||||
mod constants;
|
||||
use crate::constants::*;
|
||||
use crate::auth::InMemoryStore;
|
||||
use crate::stun::{parse_message, build_401_response};
|
||||
use crate::traits::CredentialStore;
|
||||
// use crate::models::stun::StunHeader; // currently unused
|
||||
use crate::alloc::AllocationManager;
|
||||
// Use the library crate's public modules instead of local `mod` declarations.
|
||||
use niom_turn::constants::*;
|
||||
use niom_turn::auth::InMemoryStore;
|
||||
use niom_turn::stun::{parse_message, build_401_response, find_message_integrity, validate_message_integrity, build_success_response, encode_xor_relayed_address};
|
||||
use niom_turn::traits::CredentialStore;
|
||||
use niom_turn::alloc::AllocationManager;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
@ -62,7 +56,7 @@ async fn udp_reader_loop(udp: Arc<UdpSocket>, creds: InMemoryStore, allocs: Allo
|
||||
if let Ok(msg) = parse_message(&buf[..len]) {
|
||||
tracing::info!("STUN/TURN message from {} type=0x{:04x} len={}", peer, msg.header.msg_type, len);
|
||||
// If MESSAGE-INTEGRITY present, attempt validation using credential store
|
||||
if let Some(_mi_attr) = crate::stun::find_message_integrity(&msg) {
|
||||
if let Some(_mi_attr) = find_message_integrity(&msg) {
|
||||
// For MVP we expect username attribute (USERNAME) to be present
|
||||
let username_attr = msg.attributes.iter().find(|a| a.typ == ATTR_USERNAME);
|
||||
if let Some(u) = username_attr {
|
||||
@ -71,7 +65,7 @@ async fn udp_reader_loop(udp: Arc<UdpSocket>, creds: InMemoryStore, allocs: Allo
|
||||
let store = creds.clone();
|
||||
let pw = store.get_password(username).await;
|
||||
if let Some(password) = pw {
|
||||
let valid = crate::stun::validate_message_integrity(&msg, &password);
|
||||
let valid = validate_message_integrity(&msg, &password);
|
||||
if valid {
|
||||
tracing::info!("MI valid for user {}", username);
|
||||
// If this is an Allocate request, perform allocation
|
||||
@ -85,7 +79,7 @@ async fn udp_reader_loop(udp: Arc<UdpSocket>, creds: InMemoryStore, allocs: Allo
|
||||
out.extend_from_slice(&MAGIC_COOKIE_U32.to_be_bytes());
|
||||
out.extend_from_slice(&msg.header.transaction_id);
|
||||
// RFC: XOR-RELAYED-ADDRESS (0x0016)
|
||||
let attr_val = crate::stun::encode_xor_relayed_address(&relay_addr, &msg.header.transaction_id);
|
||||
let attr_val = encode_xor_relayed_address(&relay_addr, &msg.header.transaction_id);
|
||||
out.extend_from_slice(&ATTR_XOR_RELAYED_ADDRESS.to_be_bytes());
|
||||
out.extend_from_slice(&((attr_val.len() as u16).to_be_bytes()));
|
||||
out.extend_from_slice(&attr_val);
|
||||
@ -102,7 +96,7 @@ async fn udp_reader_loop(udp: Arc<UdpSocket>, creds: InMemoryStore, allocs: Allo
|
||||
}
|
||||
}
|
||||
// default success response
|
||||
let resp = crate::stun::build_success_response(&msg.header);
|
||||
let resp = build_success_response(&msg.header);
|
||||
let _ = udp.send_to(&resp, &peer).await;
|
||||
continue;
|
||||
} else {
|
||||
@ -126,7 +120,7 @@ async fn udp_reader_loop(udp: Arc<UdpSocket>, creds: InMemoryStore, allocs: Allo
|
||||
out.extend_from_slice(&0u16.to_be_bytes());
|
||||
out.extend_from_slice(&MAGIC_COOKIE_U32.to_be_bytes());
|
||||
out.extend_from_slice(&msg.header.transaction_id);
|
||||
let attr_val = crate::stun::encode_xor_relayed_address(&relay_addr, &msg.header.transaction_id);
|
||||
let attr_val = encode_xor_relayed_address(&relay_addr, &msg.header.transaction_id);
|
||||
out.extend_from_slice(&ATTR_XOR_RELAYED_ADDRESS.to_be_bytes());
|
||||
out.extend_from_slice(&((attr_val.len() as u16).to_be_bytes()));
|
||||
out.extend_from_slice(&attr_val);
|
||||
|
||||
22
src/stun.rs
22
src/stun.rs
@ -48,23 +48,23 @@ pub fn parse_message(buf: &[u8]) -> Result<StunMessage, ParseError> {
|
||||
pub fn build_401_response(req: &StunHeader, realm: &str, nonce: &str, _err_code: u16) -> Vec<u8> {
|
||||
use bytes::BytesMut;
|
||||
let mut buf = BytesMut::new();
|
||||
// Error response type for TURN often uses same method with error bit set; here we reuse 0x0111 placeholder
|
||||
let msg_type: u16 = 0x0111;
|
||||
// Error response type for TURN: reuse the request method with error class bits set
|
||||
let msg_type: u16 = req.msg_type | CLASS_ERROR;
|
||||
buf.extend_from_slice(&msg_type.to_be_bytes());
|
||||
buf.extend_from_slice(&0u16.to_be_bytes()); // length
|
||||
buf.extend_from_slice(&MAGIC_COOKIE_BYTES);
|
||||
buf.extend_from_slice(&req.transaction_id);
|
||||
|
||||
// REALM (0x0014)
|
||||
// REALM (RFC attr)
|
||||
let realm_bytes = realm.as_bytes();
|
||||
buf.extend_from_slice(&0x0014u16.to_be_bytes());
|
||||
buf.extend_from_slice(&ATTR_REALM.to_be_bytes());
|
||||
buf.extend_from_slice(&(realm_bytes.len() as u16).to_be_bytes());
|
||||
buf.extend_from_slice(realm_bytes);
|
||||
while (buf.len() % 4) != 0 { buf.extend_from_slice(&[0]); }
|
||||
|
||||
// NONCE (0x0015)
|
||||
// NONCE (RFC attr)
|
||||
let nonce_bytes = nonce.as_bytes();
|
||||
buf.extend_from_slice(&0x0015u16.to_be_bytes());
|
||||
buf.extend_from_slice(&ATTR_NONCE.to_be_bytes());
|
||||
buf.extend_from_slice(&(nonce_bytes.len() as u16).to_be_bytes());
|
||||
buf.extend_from_slice(nonce_bytes);
|
||||
while (buf.len() % 4) != 0 { buf.extend_from_slice(&[0]); }
|
||||
@ -122,7 +122,7 @@ pub fn compute_fingerprint(msg: &[u8]) -> u32 {
|
||||
let mut hasher = Hasher::new();
|
||||
hasher.update(msg);
|
||||
let crc = hasher.finalize();
|
||||
crc ^ 0x5354554e
|
||||
crc ^ FINGERPRINT_XOR
|
||||
}
|
||||
|
||||
/// Compute MESSAGE-INTEGRITY (HMAC-SHA1) over the message
|
||||
@ -147,7 +147,7 @@ pub fn encode_xor_relayed_address(addr: &std::net::SocketAddr, _trans_id: &[u8;1
|
||||
match addr.ip() {
|
||||
IpAddr::V4(v4) => {
|
||||
out.push(0); // first 8 bits zero per spec
|
||||
out.push(0x01); // family: 0x01 for IPv4
|
||||
out.push(FAMILY_IPV4); // family: IPv4
|
||||
// xport = port ^ (magic_cookie >> 16)
|
||||
let port = addr.port();
|
||||
let xport = (port ^ ((MAGIC_COOKIE_U32 >> 16) as u16)) as u16;
|
||||
@ -168,7 +168,7 @@ pub fn encode_xor_relayed_address(addr: &std::net::SocketAddr, _trans_id: &[u8;1
|
||||
/// Decode XOR-RELAYED-ADDRESS attribute value into SocketAddr (IPv4 only)
|
||||
pub fn decode_xor_relayed_address(value: &[u8], _trans_id: &[u8;12]) -> Option<std::net::SocketAddr> {
|
||||
if value.len() < 8 { return None; }
|
||||
if value[1] != 0x01 { return None; } // not IPv4
|
||||
if value[1] != FAMILY_IPV4 { return None; } // not IPv4
|
||||
let xport = u16::from_be_bytes([value[2], value[3]]);
|
||||
let port = xport ^ ((MAGIC_COOKIE_U32 >> 16) as u16);
|
||||
let cookie_bytes = MAGIC_COOKIE_BYTES;
|
||||
@ -218,7 +218,7 @@ mod tests {
|
||||
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(&0x2112A442u32.to_be_bytes());
|
||||
buf.extend_from_slice(&MAGIC_COOKIE_BYTES);
|
||||
let trans = [9u8; 12];
|
||||
buf.extend_from_slice(&trans);
|
||||
|
||||
@ -232,7 +232,7 @@ mod tests {
|
||||
// MESSAGE-INTEGRITY placeholder (0x0008) length 20
|
||||
let mi_attr_offset = buf.len();
|
||||
buf.extend_from_slice(&ATTR_MESSAGE_INTEGRITY.to_be_bytes());
|
||||
buf.extend_from_slice(&(20u16).to_be_bytes());
|
||||
buf.extend_from_slice(&((HMAC_SHA1_LEN as u16).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]); }
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user