Changed response message signing to match accepted message integrity of request.

This commit is contained in:
ghost 2025-12-29 03:04:22 +01:00
parent a79d0f2a95
commit a434a6ad8a
4 changed files with 295 additions and 87 deletions

View File

@ -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<u8> },
Granted {
username: String,
key: Vec<u8>,
mi_mode: MessageIntegrityMode,
},
Challenge { nonce: String },
StaleNonce { nonce: String },
Reject { code: u16, reason: &'static str },
@ -238,7 +243,11 @@ impl<S: CredentialStore + Clone> AuthManager<S> {
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<S: CredentialStore + Clone> AuthManager<S> {
"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<S: CredentialStore + Clone> AuthManager<S> {
return AuthStatus::Granted {
username,
key: short_key.to_vec(),
mi_mode: MessageIntegrityMode::Rfc5389,
};
}
@ -274,6 +288,7 @@ impl<S: CredentialStore + Clone> AuthManager<S> {
return AuthStatus::Granted {
username,
key: short_key.to_vec(),
mi_mode: MessageIntegrityMode::Rfc5389,
};
}
@ -281,7 +296,11 @@ impl<S: CredentialStore + Clone> AuthManager<S> {
// 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<S: CredentialStore + Clone> AuthManager<S> {
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,
};
}
}
}

View File

@ -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::<AllocationError>(),
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;

View File

@ -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<u8> {
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<u8> {
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<u8> {
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<u8> {
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<u8> {
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<u8> {
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<u8> {
/// Build a simple success (200) response including MESSAGE-INTEGRITY and FINGERPRINT.
pub fn build_success_response_with_integrity(req: &StunHeader, key: &[u8]) -> Vec<u8> {
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<u8> {
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()
}

View File

@ -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<Vec<u8>> = None;
let mut auth_mi_mode: Option<MessageIntegrityMode> = 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::<AllocationError>(),
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",