diff --git a/src/auth.rs b/src/auth.rs index aaf4c12..4fd139d 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -14,6 +14,7 @@ use crate::stun::{ compute_message_integrity_len_preserved_nozero, compute_message_integrity_through_mi_header, find_message_integrity, + MessageIntegrityMode, validate_message_integrity, validate_message_integrity_len_preserved_nozero, validate_message_integrity_nozero, @@ -89,7 +90,11 @@ impl AuthSettings { /// Result of validating authentication attributes on an incoming STUN/TURN request. #[derive(Debug, Clone)] pub enum AuthStatus { - Granted { username: String, key: Vec }, + Granted { + username: String, + key: Vec, + mi_mode: MessageIntegrityMode, + }, Challenge { nonce: String }, StaleNonce { nonce: String }, Reject { code: u16, reason: &'static str }, @@ -238,7 +243,11 @@ impl AuthManager { let key = self.derive_long_term_key(&username, &password); // Primary: long-term (MD5(username:realm:password)) if validate_message_integrity(msg, &key) { - return AuthStatus::Granted { username, key }; + return AuthStatus::Granted { + username, + key, + mi_mode: MessageIntegrityMode::Rfc5389, + }; } // Interop: some clients appear to compute MESSAGE-INTEGRITY without zeroing the MI bytes. @@ -249,7 +258,11 @@ impl AuthManager { "auth accept via MI nozero username={} realm={} peer={} (interop)", username, realm, peer ); - return AuthStatus::Granted { username, key }; + return AuthStatus::Granted { + username, + key, + mi_mode: MessageIntegrityMode::Rfc5389, + }; } // Workaround: also accept short-term style (raw password as key) for test clients like turnutils_uclient. @@ -261,6 +274,7 @@ impl AuthManager { return AuthStatus::Granted { username, key: short_key.to_vec(), + mi_mode: MessageIntegrityMode::Rfc5389, }; } @@ -274,6 +288,7 @@ impl AuthManager { return AuthStatus::Granted { username, key: short_key.to_vec(), + mi_mode: MessageIntegrityMode::Rfc5389, }; } @@ -281,7 +296,11 @@ impl AuthManager { // try validation without adjusting the header length. if validate_message_integrity_len_preserved(msg, &key) { warn!("auth accept via len-preserved MI username={} realm={} peer={} (interop fallback)", username, realm, peer); - return AuthStatus::Granted { username, key }; + return AuthStatus::Granted { + username, + key, + mi_mode: MessageIntegrityMode::Rfc5389, + }; } // No acceptance without MI validation. Emit detailed diagnostics. @@ -411,8 +430,25 @@ impl AuthManager { for (label, cand) in variants.iter() { if let Some(c) = cand { if c.len() >= 20 && &c[..20] == mi_bytes.as_slice() { + let mi_mode = if *label == "long_before_mi_len_to_mi_end" + || *label == "short_before_mi_len_to_mi_end" + { + MessageIntegrityMode::BeforeMiLenToMiEnd + } else { + MessageIntegrityMode::Rfc5389 + }; + + let chosen_key = if label.starts_with("short_") { + short_key.to_vec() + } else { + key.clone() + }; warn!("auth accept via MI variant={} username={} realm={} peer={} (interop)", label, username, realm, peer); - return AuthStatus::Granted { username, key }; + return AuthStatus::Granted { + username, + key: chosen_key, + mi_mode, + }; } } } diff --git a/src/server.rs b/src/server.rs index fcba8ee..7090e23 100644 --- a/src/server.rs +++ b/src/server.rs @@ -9,11 +9,11 @@ use crate::auth::{AuthManager, AuthStatus, InMemoryStore}; use crate::constants::*; use crate::rate_limit::RateLimiters; use crate::stun::{ - build_401_response, build_allocate_success_with_integrity, build_error_response, - build_error_response_with_integrity, build_lifetime_success_with_integrity, - build_success_response_with_integrity, decode_xor_peer_address, extract_lifetime_seconds, - parse_channel_data, extract_requested_transport_protocol, parse_message, - validate_fingerprint_if_present, + build_401_response, build_allocate_success_with_integrity_mode, build_error_response, + build_error_response_with_integrity_mode, build_lifetime_success_with_integrity_mode, + build_success_response_with_integrity_mode, decode_xor_peer_address, + extract_lifetime_seconds, extract_requested_transport_protocol, parse_channel_data, + parse_message, validate_fingerprint_if_present, }; use std::time::Duration; @@ -105,15 +105,19 @@ pub async fn udp_reader_loop_with_limits( ); if requires_auth { - let key = match auth.authenticate(&msg, &peer).await { - AuthStatus::Granted { username, key } => { + let (key, mi_mode) = match auth.authenticate(&msg, &peer).await { + AuthStatus::Granted { + username, + key, + mi_mode, + } => { tracing::debug!( "TURN auth ok for {} as {} (0x{:04x})", peer, username, msg.header.msg_type ); - key + (key, mi_mode) } AuthStatus::Challenge { nonce } => { crate::metrics::inc_auth_challenge(); @@ -164,22 +168,24 @@ pub async fn udp_reader_loop_with_limits( Some(IPPROTO_UDP) => {} Some(_) => { crate::metrics::inc_allocate_fail(); - let resp = build_error_response_with_integrity( + let resp = build_error_response_with_integrity_mode( &msg.header, 442, "Unsupported Transport", &key, + mi_mode, ); let _ = udp.send_to(&resp, &peer).await; continue; } None => { crate::metrics::inc_allocate_fail(); - let resp = build_error_response_with_integrity( + let resp = build_error_response_with_integrity_mode( &msg.header, 400, "Missing REQUESTED-TRANSPORT", &key, + mi_mode, ); let _ = udp.send_to(&resp, &peer).await; continue; @@ -207,11 +213,12 @@ pub async fn udp_reader_loop_with_limits( peer, e ); - let resp = build_error_response_with_integrity( + let resp = build_error_response_with_integrity_mode( &msg.header, 500, "Allocate Failed", &key, + mi_mode, ); let _ = udp.send_to(&resp, &peer).await; continue; @@ -220,11 +227,12 @@ pub async fn udp_reader_loop_with_limits( let lifetime_secs = applied.as_secs().min(u32::MAX as u64) as u32; let advertised = allocs.relay_addr_for_response(relay_addr); - let resp = build_allocate_success_with_integrity( + let resp = build_allocate_success_with_integrity_mode( &msg.header, &advertised, lifetime_secs, &key, + mi_mode, ); tracing::info!( "allocated relay {} for {} lifetime={}s", @@ -244,7 +252,13 @@ pub async fn udp_reader_loop_with_limits( _ => (500, "Allocate Failed"), }; crate::metrics::inc_allocate_fail(); - let resp = build_error_response_with_integrity(&msg.header, code, reason, &key); + let resp = build_error_response_with_integrity_mode( + &msg.header, + code, + reason, + &key, + mi_mode, + ); let _ = udp.send_to(&resp, &peer).await; } } @@ -254,7 +268,13 @@ pub async fn udp_reader_loop_with_limits( if allocs.get_allocation(&peer).is_none() { warn!("create-permission without allocation from {}", peer); let resp = - build_error_response_with_integrity(&msg.header, 437, "Allocation Mismatch", &key); + build_error_response_with_integrity_mode( + &msg.header, + 437, + "Allocation Mismatch", + &key, + mi_mode, + ); let _ = udp.send_to(&resp, &peer).await; continue; } @@ -289,11 +309,12 @@ pub async fn udp_reader_loop_with_limits( e.downcast_ref::(), Some(AllocationError::PermissionQuotaExceeded) ) { - let resp = build_error_response_with_integrity( + let resp = build_error_response_with_integrity_mode( &msg.header, 508, "Insufficient Capacity", &key, + mi_mode, ); let _ = udp.send_to(&resp, &peer).await; continue; @@ -306,11 +327,17 @@ pub async fn udp_reader_loop_with_limits( } if added == 0 { - let resp = - build_error_response_with_integrity(&msg.header, 400, "No valid XOR-PEER-ADDRESS", &key); + let resp = build_error_response_with_integrity_mode( + &msg.header, + 400, + "No valid XOR-PEER-ADDRESS", + &key, + mi_mode, + ); let _ = udp.send_to(&resp, &peer).await; } else { - let resp = build_success_response_with_integrity(&msg.header, &key); + let resp = + build_success_response_with_integrity_mode(&msg.header, &key, mi_mode); let _ = udp.send_to(&resp, &peer).await; } continue; @@ -320,8 +347,13 @@ pub async fn udp_reader_loop_with_limits( Some(a) => a, None => { warn!("channel-bind without allocation from {}", peer); - let resp = - build_error_response_with_integrity(&msg.header, 437, "Allocation Mismatch", &key); + let resp = build_error_response_with_integrity_mode( + &msg.header, + 437, + "Allocation Mismatch", + &key, + mi_mode, + ); let _ = udp.send_to(&resp, &peer).await; continue; } @@ -336,11 +368,12 @@ pub async fn udp_reader_loop_with_limits( let (channel_attr, peer_attr) = match (channel_attr, peer_attr) { (Some(c), Some(p)) => (c, p), _ => { - let resp = build_error_response_with_integrity( + let resp = build_error_response_with_integrity_mode( &msg.header, 400, "Missing CHANNEL-NUMBER or XOR-PEER-ADDRESS", &key, + mi_mode, ); let _ = udp.send_to(&resp, &peer).await; continue; @@ -355,11 +388,12 @@ pub async fn udp_reader_loop_with_limits( ) { Some(addr) => addr, None => { - let resp = build_error_response_with_integrity( + let resp = build_error_response_with_integrity_mode( &msg.header, 400, "Invalid XOR-PEER-ADDRESS", &key, + mi_mode, ); let _ = udp.send_to(&resp, &peer).await; continue; @@ -367,11 +401,12 @@ pub async fn udp_reader_loop_with_limits( }; if !allocation.is_peer_allowed(&peer_addr) { - let resp = build_error_response_with_integrity( + let resp = build_error_response_with_integrity_mode( &msg.header, 403, "Peer Not Permitted", &key, + mi_mode, ); let _ = udp.send_to(&resp, &peer).await; continue; @@ -391,13 +426,19 @@ pub async fn udp_reader_loop_with_limits( } _ => (500, "Channel Bind Failed"), }; - let resp = build_error_response_with_integrity(&msg.header, code, reason, &key); + let resp = build_error_response_with_integrity_mode( + &msg.header, + code, + reason, + &key, + mi_mode, + ); let _ = udp.send_to(&resp, &peer).await; continue; } crate::metrics::inc_channel_binding_added(); - let resp = build_success_response_with_integrity(&msg.header, &key); + let resp = build_success_response_with_integrity_mode(&msg.header, &key, mi_mode); let _ = udp.send_to(&resp, &peer).await; continue; } @@ -406,8 +447,13 @@ pub async fn udp_reader_loop_with_limits( Some(a) => a, None => { warn!("send indication without allocation from {}", peer); - let resp = - build_error_response_with_integrity(&msg.header, 437, "Allocation Mismatch", &key); + let resp = build_error_response_with_integrity_mode( + &msg.header, + 437, + "Allocation Mismatch", + &key, + mi_mode, + ); let _ = udp.send_to(&resp, &peer).await; continue; } @@ -421,11 +467,12 @@ pub async fn udp_reader_loop_with_limits( let (peer_attr, data_attr) = match (peer_attr, data_attr) { (Some(p), Some(d)) => (p, d), _ => { - let resp = build_error_response_with_integrity( + let resp = build_error_response_with_integrity_mode( &msg.header, 400, "Missing DATA or XOR-PEER-ADDRESS", &key, + mi_mode, ); let _ = udp.send_to(&resp, &peer).await; continue; @@ -438,11 +485,12 @@ pub async fn udp_reader_loop_with_limits( ) { Some(addr) => addr, None => { - let resp = build_error_response_with_integrity( + let resp = build_error_response_with_integrity_mode( &msg.header, 400, "Invalid XOR-PEER-ADDRESS", &key, + mi_mode, ); let _ = udp.send_to(&resp, &peer).await; continue; @@ -450,11 +498,12 @@ pub async fn udp_reader_loop_with_limits( }; if !allocation.is_peer_allowed(&peer_addr) { - let resp = build_error_response_with_integrity( + let resp = build_error_response_with_integrity_mode( &msg.header, 403, "Peer Not Permitted", &key, + mi_mode, ); let _ = udp.send_to(&resp, &peer).await; continue; @@ -468,7 +517,8 @@ pub async fn udp_reader_loop_with_limits( peer, peer_addr ); - let resp = build_success_response_with_integrity(&msg.header, &key); + let resp = + build_success_response_with_integrity_mode(&msg.header, &key, mi_mode); let _ = udp.send_to(&resp, &peer).await; } Err(e) => { @@ -478,11 +528,12 @@ pub async fn udp_reader_loop_with_limits( peer_addr, e ); - let resp = build_error_response_with_integrity( + let resp = build_error_response_with_integrity_mode( &msg.header, 500, "Peer Send Failed", &key, + mi_mode, ); let _ = udp.send_to(&resp, &peer).await; } @@ -504,19 +555,21 @@ pub async fn udp_reader_loop_with_limits( applied.as_secs() ); } - let resp = build_lifetime_success_with_integrity( + let resp = build_lifetime_success_with_integrity_mode( &msg.header, applied.as_secs().min(u32::MAX as u64) as u32, &key, + mi_mode, ); let _ = udp.send_to(&resp, &peer).await; } Err(_) => { - let resp = build_error_response_with_integrity( + let resp = build_error_response_with_integrity_mode( &msg.header, 437, "Allocation Mismatch", &key, + mi_mode, ); let _ = udp.send_to(&resp, &peer).await; } @@ -524,11 +577,12 @@ pub async fn udp_reader_loop_with_limits( continue; } _ => { - let resp = build_error_response_with_integrity( + let resp = build_error_response_with_integrity_mode( &msg.header, 420, "Unknown TURN Method", &key, + mi_mode, ); let _ = udp.send_to(&resp, &peer).await; continue; diff --git a/src/stun.rs b/src/stun.rs index 3ae25f9..b51306c 100644 --- a/src/stun.rs +++ b/src/stun.rs @@ -5,6 +5,15 @@ use crate::models::stun::{StunAttribute, StunHeader, StunMessage}; use std::convert::TryInto; use uuid::Uuid; +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum MessageIntegrityMode { + /// RFC 5389-compliant MESSAGE-INTEGRITY calculation. + Rfc5389, + /// Interop mode observed with `turnutils_uclient`: HMAC is computed over bytes *before* + /// the MESSAGE-INTEGRITY attribute, with the STUN header length field set to end-of-MI. + BeforeMiLenToMiEnd, +} + #[derive(thiserror::Error, Debug)] pub enum ParseError { #[error("too short")] @@ -158,6 +167,17 @@ pub fn build_error_response_with_integrity( code: u16, reason: &str, key: &[u8], +) -> Vec { + build_error_response_with_integrity_mode(req, code, reason, key, MessageIntegrityMode::Rfc5389) +} + +/// Build a generic STUN/TURN error response including MESSAGE-INTEGRITY and FINGERPRINT. +pub fn build_error_response_with_integrity_mode( + req: &StunHeader, + code: u16, + reason: &str, + key: &[u8], + mi_mode: MessageIntegrityMode, ) -> Vec { use bytes::BytesMut; let mut buf = BytesMut::new(); @@ -182,7 +202,7 @@ pub fn build_error_response_with_integrity( buf.extend_from_slice(&[0]); } - append_message_integrity(&mut buf, key); + append_message_integrity_with_mode(&mut buf, key, mi_mode); append_fingerprint(&mut buf); buf.to_vec() } @@ -227,6 +247,23 @@ pub fn build_allocate_success_with_integrity( relay: &std::net::SocketAddr, lifetime_secs: u32, key: &[u8], +) -> Vec { + build_allocate_success_with_integrity_mode( + req, + relay, + lifetime_secs, + key, + MessageIntegrityMode::Rfc5389, + ) +} + +/// Build an Allocate success response including MESSAGE-INTEGRITY and FINGERPRINT. +pub fn build_allocate_success_with_integrity_mode( + req: &StunHeader, + relay: &std::net::SocketAddr, + lifetime_secs: u32, + key: &[u8], + mi_mode: MessageIntegrityMode, ) -> Vec { use bytes::BytesMut; let mut buf = BytesMut::new(); @@ -252,7 +289,7 @@ pub fn build_allocate_success_with_integrity( buf.extend_from_slice(&[0]); } - append_message_integrity(&mut buf, key); + append_message_integrity_with_mode(&mut buf, key, mi_mode); append_fingerprint(&mut buf); buf.to_vec() } @@ -284,6 +321,16 @@ pub fn build_lifetime_success_with_integrity( req: &StunHeader, lifetime_secs: u32, key: &[u8], +) -> Vec { + build_lifetime_success_with_integrity_mode(req, lifetime_secs, key, MessageIntegrityMode::Rfc5389) +} + +/// Build a Refresh success response including MESSAGE-INTEGRITY and FINGERPRINT. +pub fn build_lifetime_success_with_integrity_mode( + req: &StunHeader, + lifetime_secs: u32, + key: &[u8], + mi_mode: MessageIntegrityMode, ) -> Vec { use bytes::BytesMut; let mut buf = BytesMut::new(); @@ -301,7 +348,7 @@ pub fn build_lifetime_success_with_integrity( buf.extend_from_slice(&[0]); } - append_message_integrity(&mut buf, key); + append_message_integrity_with_mode(&mut buf, key, mi_mode); append_fingerprint(&mut buf); buf.to_vec() } @@ -578,9 +625,14 @@ pub fn validate_message_integrity_len_preserved(msg: &StunMessage, key: &[u8]) - false } -fn append_message_integrity(buf: &mut bytes::BytesMut, key: &[u8]) { +fn append_message_integrity_with_mode( + buf: &mut bytes::BytesMut, + key: &[u8], + mi_mode: MessageIntegrityMode, +) { // Append attribute header and placeholder; set length to end-of-MI, then compute // HMAC over the message slice up to end-of-MI (with the MI placeholder still zero). + let mi_attr_offset = buf.len(); buf.extend_from_slice(&ATTR_MESSAGE_INTEGRITY.to_be_bytes()); buf.extend_from_slice(&((HMAC_SHA1_LEN as u16).to_be_bytes())); let mi_val_pos = buf.len(); @@ -592,12 +644,22 @@ fn append_message_integrity(buf: &mut bytes::BytesMut, key: &[u8]) { let mi_end = buf.len(); // Set length to end-of-MI (excluding any later attributes like FINGERPRINT) - let len = (mi_end - 20) as u16; - let len_bytes = len.to_be_bytes(); - buf[2] = len_bytes[0]; - buf[3] = len_bytes[1]; + let len_to_mi_end = (mi_end - 20) as u16; + + let hmac = match mi_mode { + MessageIntegrityMode::Rfc5389 => { + buf[2..4].copy_from_slice(&len_to_mi_end.to_be_bytes()); + compute_message_integrity(key, &buf[..mi_end]) + } + MessageIntegrityMode::BeforeMiLenToMiEnd => { + // HMAC input is only the prefix before the MI attribute, but the header length + // is forced to end-of-MI. + let mut signed = buf[..mi_attr_offset].to_vec(); + signed[2..4].copy_from_slice(&len_to_mi_end.to_be_bytes()); + compute_message_integrity(key, &signed) + } + }; - let hmac = compute_message_integrity(key, &buf[..mi_end]); buf[mi_val_pos..mi_val_pos + HMAC_SHA1_LEN].copy_from_slice(&hmac[..HMAC_SHA1_LEN]); } @@ -616,6 +678,15 @@ pub fn build_success_response(req: &StunHeader) -> Vec { /// Build a simple success (200) response including MESSAGE-INTEGRITY and FINGERPRINT. pub fn build_success_response_with_integrity(req: &StunHeader, key: &[u8]) -> Vec { + build_success_response_with_integrity_mode(req, key, MessageIntegrityMode::Rfc5389) +} + +/// Build a simple success (200) response including MESSAGE-INTEGRITY and FINGERPRINT. +pub fn build_success_response_with_integrity_mode( + req: &StunHeader, + key: &[u8], + mi_mode: MessageIntegrityMode, +) -> Vec { use bytes::BytesMut; let mut buf = BytesMut::new(); let msg_type: u16 = req.msg_type | CLASS_SUCCESS; @@ -623,7 +694,7 @@ pub fn build_success_response_with_integrity(req: &StunHeader, key: &[u8]) -> Ve buf.extend_from_slice(&0u16.to_be_bytes()); buf.extend_from_slice(&MAGIC_COOKIE_BYTES); buf.extend_from_slice(&req.transaction_id); - append_message_integrity(&mut buf, key); + append_message_integrity_with_mode(&mut buf, key, mi_mode); append_fingerprint(&mut buf); buf.to_vec() } diff --git a/src/turn_stream.rs b/src/turn_stream.rs index bded45b..5a5060c 100644 --- a/src/turn_stream.rs +++ b/src/turn_stream.rs @@ -13,10 +13,11 @@ use crate::alloc::AllocationError; use crate::auth::{AuthManager, AuthStatus, InMemoryStore}; use crate::constants::*; use crate::stun::{ - build_401_response, build_allocate_success_with_integrity, build_error_response, - build_error_response_with_integrity, build_lifetime_success_with_integrity, - build_success_response_with_integrity, decode_xor_peer_address, extract_lifetime_seconds, - parse_message, validate_fingerprint_if_present, extract_requested_transport_protocol, + build_401_response, build_allocate_success_with_integrity_mode, build_error_response, + build_error_response_with_integrity_mode, build_lifetime_success_with_integrity_mode, + build_success_response_with_integrity_mode, decode_xor_peer_address, + extract_lifetime_seconds, extract_requested_transport_protocol, parse_message, + validate_fingerprint_if_present, MessageIntegrityMode, }; use crate::rate_limit::RateLimiters; @@ -232,10 +233,16 @@ where ); let mut auth_key: Option> = None; + let mut auth_mi_mode: Option = None; if requires_auth { match auth.authenticate(&msg, &peer).await { - AuthStatus::Granted { username, key } => { + AuthStatus::Granted { + username, + key, + mi_mode, + } => { auth_key = Some(key); + auth_mi_mode = Some(mi_mode); tracing::debug!( "TURN stream auth ok for {} as {} (0x{:04x})", peer, @@ -291,28 +298,32 @@ where let key = auth_key .as_deref() .expect("auth key must be set after AuthStatus::Granted"); + let mi_mode = auth_mi_mode + .expect("auth mi mode must be set after AuthStatus::Granted"); // TURN Allocate MUST include REQUESTED-TRANSPORT; WebRTC expects UDP (17). match extract_requested_transport_protocol(&msg) { Some(IPPROTO_UDP) => {} Some(_) => { crate::metrics::inc_allocate_fail(); - let resp = build_error_response_with_integrity( + let resp = build_error_response_with_integrity_mode( &msg.header, 442, "Unsupported Transport", key, + mi_mode, ); let _ = tx.send(resp).await; continue; } None => { crate::metrics::inc_allocate_fail(); - let resp = build_error_response_with_integrity( + let resp = build_error_response_with_integrity_mode( &msg.header, 400, "Missing REQUESTED-TRANSPORT", key, + mi_mode, ); let _ = tx.send(resp).await; continue; @@ -343,11 +354,12 @@ where peer, e ); - let resp = build_error_response_with_integrity( + let resp = build_error_response_with_integrity_mode( &msg.header, 500, "Allocate Failed", key, + mi_mode, ); let _ = tx.send(resp).await; continue; @@ -358,11 +370,12 @@ where applied.as_secs().min(u32::MAX as u64) as u32; let advertised = allocs.relay_addr_for_response(relay_addr); - let resp = build_allocate_success_with_integrity( + let resp = build_allocate_success_with_integrity_mode( &msg.header, &advertised, lifetime_secs, key, + mi_mode, ); crate::metrics::inc_allocate_success(); let _ = tx.send(resp).await; @@ -376,11 +389,12 @@ where _ => (500, "Allocate Failed"), }; crate::metrics::inc_allocate_fail(); - let resp = build_error_response_with_integrity( + let resp = build_error_response_with_integrity_mode( &msg.header, code, reason, key, + mi_mode, ); let _ = tx.send(resp).await; } @@ -390,12 +404,15 @@ where let key = auth_key .as_deref() .expect("auth key must be set after AuthStatus::Granted"); + let mi_mode = auth_mi_mode + .expect("auth mi mode must be set after AuthStatus::Granted"); if allocs.get_allocation(&peer).is_none() { - let resp = build_error_response_with_integrity( + let resp = build_error_response_with_integrity_mode( &msg.header, 437, "Allocation Mismatch", key, + mi_mode, ); let _ = tx.send(resp).await; continue; @@ -418,11 +435,12 @@ where e.downcast_ref::(), Some(AllocationError::PermissionQuotaExceeded) ) { - let resp = build_error_response_with_integrity( + let resp = build_error_response_with_integrity_mode( &msg.header, 508, "Insufficient Capacity", key, + mi_mode, ); let _ = tx.send(resp).await; continue; @@ -433,15 +451,17 @@ where } if added == 0 { - let resp = build_error_response_with_integrity( + let resp = build_error_response_with_integrity_mode( &msg.header, 400, "No valid XOR-PEER-ADDRESS", key, + mi_mode, ); let _ = tx.send(resp).await; } else { - let resp = build_success_response_with_integrity(&msg.header, key); + let resp = + build_success_response_with_integrity_mode(&msg.header, key, mi_mode); let _ = tx.send(resp).await; } } @@ -449,14 +469,17 @@ where let key = auth_key .as_deref() .expect("auth key must be set after AuthStatus::Granted"); + let mi_mode = auth_mi_mode + .expect("auth mi mode must be set after AuthStatus::Granted"); let allocation = match allocs.get_allocation(&peer) { Some(a) => a, None => { - let resp = build_error_response_with_integrity( + let resp = build_error_response_with_integrity_mode( &msg.header, 437, "Allocation Mismatch", key, + mi_mode, ); let _ = tx.send(resp).await; continue; @@ -475,11 +498,12 @@ where { (Some(c), Some(p)) => (c, p), _ => { - let resp = build_error_response_with_integrity( + let resp = build_error_response_with_integrity_mode( &msg.header, 400, "Missing CHANNEL-NUMBER or XOR-PEER-ADDRESS", key, + mi_mode, ); let _ = tx.send(resp).await; continue; @@ -487,11 +511,12 @@ where }; if channel_attr.value.len() < 2 { - let resp = build_error_response_with_integrity( + let resp = build_error_response_with_integrity_mode( &msg.header, 400, "Invalid CHANNEL-NUMBER", key, + mi_mode, ); let _ = tx.send(resp).await; continue; @@ -507,11 +532,12 @@ where ) { Some(addr) => addr, None => { - let resp = build_error_response_with_integrity( + let resp = build_error_response_with_integrity_mode( &msg.header, 400, "Invalid XOR-PEER-ADDRESS", key, + mi_mode, ); let _ = tx.send(resp).await; continue; @@ -519,11 +545,12 @@ where }; if !allocation.is_peer_allowed(&peer_addr) { - let resp = build_error_response_with_integrity( + let resp = build_error_response_with_integrity_mode( &msg.header, 403, "Peer Not Permitted", key, + mi_mode, ); let _ = tx.send(resp).await; continue; @@ -545,11 +572,12 @@ where } _ => (500, "Channel Bind Failed"), }; - let resp = build_error_response_with_integrity( + let resp = build_error_response_with_integrity_mode( &msg.header, code, reason, key, + mi_mode, ); let _ = tx.send(resp).await; continue; @@ -557,21 +585,25 @@ where crate::metrics::inc_channel_binding_added(); - let resp = build_success_response_with_integrity(&msg.header, key); + let resp = + build_success_response_with_integrity_mode(&msg.header, key, mi_mode); let _ = tx.send(resp).await; } METHOD_SEND => { let key = auth_key .as_deref() .expect("auth key must be set after AuthStatus::Granted"); + let mi_mode = auth_mi_mode + .expect("auth mi mode must be set after AuthStatus::Granted"); let allocation = match allocs.get_allocation(&peer) { Some(a) => a, None => { - let resp = build_error_response_with_integrity( + let resp = build_error_response_with_integrity_mode( &msg.header, 437, "Allocation Mismatch", key, + mi_mode, ); let _ = tx.send(resp).await; continue; @@ -586,11 +618,12 @@ where let (peer_attr, data_attr) = match (peer_attr, data_attr) { (Some(p), Some(d)) => (p, d), _ => { - let resp = build_error_response_with_integrity( + let resp = build_error_response_with_integrity_mode( &msg.header, 400, "Missing DATA or XOR-PEER-ADDRESS", key, + mi_mode, ); let _ = tx.send(resp).await; continue; @@ -603,11 +636,12 @@ where ) { Some(addr) => addr, None => { - let resp = build_error_response_with_integrity( + let resp = build_error_response_with_integrity_mode( &msg.header, 400, "Invalid XOR-PEER-ADDRESS", key, + mi_mode, ); let _ = tx.send(resp).await; continue; @@ -615,11 +649,12 @@ where }; if !allocation.is_peer_allowed(&peer_addr) { - let resp = build_error_response_with_integrity( + let resp = build_error_response_with_integrity_mode( &msg.header, 403, "Peer Not Permitted", key, + mi_mode, ); let _ = tx.send(resp).await; continue; @@ -627,7 +662,11 @@ where match allocation.send_to_peer(peer_addr, &data_attr.value).await { Ok(_) => { - let resp = build_success_response_with_integrity(&msg.header, key); + let resp = build_success_response_with_integrity_mode( + &msg.header, + key, + mi_mode, + ); let _ = tx.send(resp).await; } Err(e) => { @@ -637,11 +676,12 @@ where peer_addr, e ); - let resp = build_error_response_with_integrity( + let resp = build_error_response_with_integrity_mode( &msg.header, 500, "Peer Send Failed", key, + mi_mode, ); let _ = tx.send(resp).await; } @@ -651,24 +691,28 @@ where let key = auth_key .as_deref() .expect("auth key must be set after AuthStatus::Granted"); + let mi_mode = auth_mi_mode + .expect("auth mi mode must be set after AuthStatus::Granted"); let requested = extract_lifetime_seconds(&msg) .map(|secs| Duration::from_secs(secs as u64)); match allocs.refresh_allocation(peer, requested) { Ok(applied) => { - let resp = build_lifetime_success_with_integrity( + let resp = build_lifetime_success_with_integrity_mode( &msg.header, applied.as_secs().min(u32::MAX as u64) as u32, key, + mi_mode, ); let _ = tx.send(resp).await; } Err(_) => { - let resp = build_error_response_with_integrity( + let resp = build_error_response_with_integrity_mode( &msg.header, 437, "Allocation Mismatch", key, + mi_mode, ); let _ = tx.send(resp).await; } @@ -683,14 +727,17 @@ where } } _ => { - let resp = match auth_key.as_deref() { - Some(key) => build_error_response_with_integrity( - &msg.header, - 420, - "Unknown TURN Method", - key, - ), - None => build_error_response( + let resp = match (auth_key.as_deref(), auth_mi_mode) { + (Some(key), Some(mi_mode)) => { + build_error_response_with_integrity_mode( + &msg.header, + 420, + "Unknown TURN Method", + key, + mi_mode, + ) + } + _ => build_error_response( &msg.header, 420, "Unknown TURN Method",