Changed response message signing to match accepted message integrity of request.
This commit is contained in:
parent
a79d0f2a95
commit
a434a6ad8a
46
src/auth.rs
46
src/auth.rs
@ -14,6 +14,7 @@ use crate::stun::{
|
|||||||
compute_message_integrity_len_preserved_nozero,
|
compute_message_integrity_len_preserved_nozero,
|
||||||
compute_message_integrity_through_mi_header,
|
compute_message_integrity_through_mi_header,
|
||||||
find_message_integrity,
|
find_message_integrity,
|
||||||
|
MessageIntegrityMode,
|
||||||
validate_message_integrity,
|
validate_message_integrity,
|
||||||
validate_message_integrity_len_preserved_nozero,
|
validate_message_integrity_len_preserved_nozero,
|
||||||
validate_message_integrity_nozero,
|
validate_message_integrity_nozero,
|
||||||
@ -89,7 +90,11 @@ impl AuthSettings {
|
|||||||
/// Result of validating authentication attributes on an incoming STUN/TURN request.
|
/// Result of validating authentication attributes on an incoming STUN/TURN request.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum AuthStatus {
|
pub enum AuthStatus {
|
||||||
Granted { username: String, key: Vec<u8> },
|
Granted {
|
||||||
|
username: String,
|
||||||
|
key: Vec<u8>,
|
||||||
|
mi_mode: MessageIntegrityMode,
|
||||||
|
},
|
||||||
Challenge { nonce: String },
|
Challenge { nonce: String },
|
||||||
StaleNonce { nonce: String },
|
StaleNonce { nonce: String },
|
||||||
Reject { code: u16, reason: &'static str },
|
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);
|
let key = self.derive_long_term_key(&username, &password);
|
||||||
// Primary: long-term (MD5(username:realm:password))
|
// Primary: long-term (MD5(username:realm:password))
|
||||||
if validate_message_integrity(msg, &key) {
|
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.
|
// 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)",
|
"auth accept via MI nozero username={} realm={} peer={} (interop)",
|
||||||
username, realm, peer
|
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.
|
// 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 {
|
return AuthStatus::Granted {
|
||||||
username,
|
username,
|
||||||
key: short_key.to_vec(),
|
key: short_key.to_vec(),
|
||||||
|
mi_mode: MessageIntegrityMode::Rfc5389,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,6 +288,7 @@ impl<S: CredentialStore + Clone> AuthManager<S> {
|
|||||||
return AuthStatus::Granted {
|
return AuthStatus::Granted {
|
||||||
username,
|
username,
|
||||||
key: short_key.to_vec(),
|
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.
|
// try validation without adjusting the header length.
|
||||||
if validate_message_integrity_len_preserved(msg, &key) {
|
if validate_message_integrity_len_preserved(msg, &key) {
|
||||||
warn!("auth accept via len-preserved MI username={} realm={} peer={} (interop fallback)", username, realm, peer);
|
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.
|
// No acceptance without MI validation. Emit detailed diagnostics.
|
||||||
@ -411,8 +430,25 @@ impl<S: CredentialStore + Clone> AuthManager<S> {
|
|||||||
for (label, cand) in variants.iter() {
|
for (label, cand) in variants.iter() {
|
||||||
if let Some(c) = cand {
|
if let Some(c) = cand {
|
||||||
if c.len() >= 20 && &c[..20] == mi_bytes.as_slice() {
|
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);
|
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,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
124
src/server.rs
124
src/server.rs
@ -9,11 +9,11 @@ use crate::auth::{AuthManager, AuthStatus, InMemoryStore};
|
|||||||
use crate::constants::*;
|
use crate::constants::*;
|
||||||
use crate::rate_limit::RateLimiters;
|
use crate::rate_limit::RateLimiters;
|
||||||
use crate::stun::{
|
use crate::stun::{
|
||||||
build_401_response, build_allocate_success_with_integrity, build_error_response,
|
build_401_response, build_allocate_success_with_integrity_mode, build_error_response,
|
||||||
build_error_response_with_integrity, build_lifetime_success_with_integrity,
|
build_error_response_with_integrity_mode, build_lifetime_success_with_integrity_mode,
|
||||||
build_success_response_with_integrity, decode_xor_peer_address, extract_lifetime_seconds,
|
build_success_response_with_integrity_mode, decode_xor_peer_address,
|
||||||
parse_channel_data, extract_requested_transport_protocol, parse_message,
|
extract_lifetime_seconds, extract_requested_transport_protocol, parse_channel_data,
|
||||||
validate_fingerprint_if_present,
|
parse_message, validate_fingerprint_if_present,
|
||||||
};
|
};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
@ -105,15 +105,19 @@ pub async fn udp_reader_loop_with_limits(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if requires_auth {
|
if requires_auth {
|
||||||
let key = match auth.authenticate(&msg, &peer).await {
|
let (key, mi_mode) = match auth.authenticate(&msg, &peer).await {
|
||||||
AuthStatus::Granted { username, key } => {
|
AuthStatus::Granted {
|
||||||
|
username,
|
||||||
|
key,
|
||||||
|
mi_mode,
|
||||||
|
} => {
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
"TURN auth ok for {} as {} (0x{:04x})",
|
"TURN auth ok for {} as {} (0x{:04x})",
|
||||||
peer,
|
peer,
|
||||||
username,
|
username,
|
||||||
msg.header.msg_type
|
msg.header.msg_type
|
||||||
);
|
);
|
||||||
key
|
(key, mi_mode)
|
||||||
}
|
}
|
||||||
AuthStatus::Challenge { nonce } => {
|
AuthStatus::Challenge { nonce } => {
|
||||||
crate::metrics::inc_auth_challenge();
|
crate::metrics::inc_auth_challenge();
|
||||||
@ -164,22 +168,24 @@ pub async fn udp_reader_loop_with_limits(
|
|||||||
Some(IPPROTO_UDP) => {}
|
Some(IPPROTO_UDP) => {}
|
||||||
Some(_) => {
|
Some(_) => {
|
||||||
crate::metrics::inc_allocate_fail();
|
crate::metrics::inc_allocate_fail();
|
||||||
let resp = build_error_response_with_integrity(
|
let resp = build_error_response_with_integrity_mode(
|
||||||
&msg.header,
|
&msg.header,
|
||||||
442,
|
442,
|
||||||
"Unsupported Transport",
|
"Unsupported Transport",
|
||||||
&key,
|
&key,
|
||||||
|
mi_mode,
|
||||||
);
|
);
|
||||||
let _ = udp.send_to(&resp, &peer).await;
|
let _ = udp.send_to(&resp, &peer).await;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
crate::metrics::inc_allocate_fail();
|
crate::metrics::inc_allocate_fail();
|
||||||
let resp = build_error_response_with_integrity(
|
let resp = build_error_response_with_integrity_mode(
|
||||||
&msg.header,
|
&msg.header,
|
||||||
400,
|
400,
|
||||||
"Missing REQUESTED-TRANSPORT",
|
"Missing REQUESTED-TRANSPORT",
|
||||||
&key,
|
&key,
|
||||||
|
mi_mode,
|
||||||
);
|
);
|
||||||
let _ = udp.send_to(&resp, &peer).await;
|
let _ = udp.send_to(&resp, &peer).await;
|
||||||
continue;
|
continue;
|
||||||
@ -207,11 +213,12 @@ pub async fn udp_reader_loop_with_limits(
|
|||||||
peer,
|
peer,
|
||||||
e
|
e
|
||||||
);
|
);
|
||||||
let resp = build_error_response_with_integrity(
|
let resp = build_error_response_with_integrity_mode(
|
||||||
&msg.header,
|
&msg.header,
|
||||||
500,
|
500,
|
||||||
"Allocate Failed",
|
"Allocate Failed",
|
||||||
&key,
|
&key,
|
||||||
|
mi_mode,
|
||||||
);
|
);
|
||||||
let _ = udp.send_to(&resp, &peer).await;
|
let _ = udp.send_to(&resp, &peer).await;
|
||||||
continue;
|
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 lifetime_secs = applied.as_secs().min(u32::MAX as u64) as u32;
|
||||||
let advertised = allocs.relay_addr_for_response(relay_addr);
|
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,
|
&msg.header,
|
||||||
&advertised,
|
&advertised,
|
||||||
lifetime_secs,
|
lifetime_secs,
|
||||||
&key,
|
&key,
|
||||||
|
mi_mode,
|
||||||
);
|
);
|
||||||
tracing::info!(
|
tracing::info!(
|
||||||
"allocated relay {} for {} lifetime={}s",
|
"allocated relay {} for {} lifetime={}s",
|
||||||
@ -244,7 +252,13 @@ pub async fn udp_reader_loop_with_limits(
|
|||||||
_ => (500, "Allocate Failed"),
|
_ => (500, "Allocate Failed"),
|
||||||
};
|
};
|
||||||
crate::metrics::inc_allocate_fail();
|
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;
|
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() {
|
if allocs.get_allocation(&peer).is_none() {
|
||||||
warn!("create-permission without allocation from {}", peer);
|
warn!("create-permission without allocation from {}", peer);
|
||||||
let resp =
|
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;
|
let _ = udp.send_to(&resp, &peer).await;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -289,11 +309,12 @@ pub async fn udp_reader_loop_with_limits(
|
|||||||
e.downcast_ref::<AllocationError>(),
|
e.downcast_ref::<AllocationError>(),
|
||||||
Some(AllocationError::PermissionQuotaExceeded)
|
Some(AllocationError::PermissionQuotaExceeded)
|
||||||
) {
|
) {
|
||||||
let resp = build_error_response_with_integrity(
|
let resp = build_error_response_with_integrity_mode(
|
||||||
&msg.header,
|
&msg.header,
|
||||||
508,
|
508,
|
||||||
"Insufficient Capacity",
|
"Insufficient Capacity",
|
||||||
&key,
|
&key,
|
||||||
|
mi_mode,
|
||||||
);
|
);
|
||||||
let _ = udp.send_to(&resp, &peer).await;
|
let _ = udp.send_to(&resp, &peer).await;
|
||||||
continue;
|
continue;
|
||||||
@ -306,11 +327,17 @@ pub async fn udp_reader_loop_with_limits(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if added == 0 {
|
if added == 0 {
|
||||||
let resp =
|
let resp = build_error_response_with_integrity_mode(
|
||||||
build_error_response_with_integrity(&msg.header, 400, "No valid XOR-PEER-ADDRESS", &key);
|
&msg.header,
|
||||||
|
400,
|
||||||
|
"No valid XOR-PEER-ADDRESS",
|
||||||
|
&key,
|
||||||
|
mi_mode,
|
||||||
|
);
|
||||||
let _ = udp.send_to(&resp, &peer).await;
|
let _ = udp.send_to(&resp, &peer).await;
|
||||||
} else {
|
} 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;
|
let _ = udp.send_to(&resp, &peer).await;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
@ -320,8 +347,13 @@ pub async fn udp_reader_loop_with_limits(
|
|||||||
Some(a) => a,
|
Some(a) => a,
|
||||||
None => {
|
None => {
|
||||||
warn!("channel-bind without allocation from {}", peer);
|
warn!("channel-bind without allocation from {}", peer);
|
||||||
let resp =
|
let resp = build_error_response_with_integrity_mode(
|
||||||
build_error_response_with_integrity(&msg.header, 437, "Allocation Mismatch", &key);
|
&msg.header,
|
||||||
|
437,
|
||||||
|
"Allocation Mismatch",
|
||||||
|
&key,
|
||||||
|
mi_mode,
|
||||||
|
);
|
||||||
let _ = udp.send_to(&resp, &peer).await;
|
let _ = udp.send_to(&resp, &peer).await;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -336,11 +368,12 @@ pub async fn udp_reader_loop_with_limits(
|
|||||||
let (channel_attr, peer_attr) = match (channel_attr, peer_attr) {
|
let (channel_attr, peer_attr) = match (channel_attr, peer_attr) {
|
||||||
(Some(c), Some(p)) => (c, p),
|
(Some(c), Some(p)) => (c, p),
|
||||||
_ => {
|
_ => {
|
||||||
let resp = build_error_response_with_integrity(
|
let resp = build_error_response_with_integrity_mode(
|
||||||
&msg.header,
|
&msg.header,
|
||||||
400,
|
400,
|
||||||
"Missing CHANNEL-NUMBER or XOR-PEER-ADDRESS",
|
"Missing CHANNEL-NUMBER or XOR-PEER-ADDRESS",
|
||||||
&key,
|
&key,
|
||||||
|
mi_mode,
|
||||||
);
|
);
|
||||||
let _ = udp.send_to(&resp, &peer).await;
|
let _ = udp.send_to(&resp, &peer).await;
|
||||||
continue;
|
continue;
|
||||||
@ -355,11 +388,12 @@ pub async fn udp_reader_loop_with_limits(
|
|||||||
) {
|
) {
|
||||||
Some(addr) => addr,
|
Some(addr) => addr,
|
||||||
None => {
|
None => {
|
||||||
let resp = build_error_response_with_integrity(
|
let resp = build_error_response_with_integrity_mode(
|
||||||
&msg.header,
|
&msg.header,
|
||||||
400,
|
400,
|
||||||
"Invalid XOR-PEER-ADDRESS",
|
"Invalid XOR-PEER-ADDRESS",
|
||||||
&key,
|
&key,
|
||||||
|
mi_mode,
|
||||||
);
|
);
|
||||||
let _ = udp.send_to(&resp, &peer).await;
|
let _ = udp.send_to(&resp, &peer).await;
|
||||||
continue;
|
continue;
|
||||||
@ -367,11 +401,12 @@ pub async fn udp_reader_loop_with_limits(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if !allocation.is_peer_allowed(&peer_addr) {
|
if !allocation.is_peer_allowed(&peer_addr) {
|
||||||
let resp = build_error_response_with_integrity(
|
let resp = build_error_response_with_integrity_mode(
|
||||||
&msg.header,
|
&msg.header,
|
||||||
403,
|
403,
|
||||||
"Peer Not Permitted",
|
"Peer Not Permitted",
|
||||||
&key,
|
&key,
|
||||||
|
mi_mode,
|
||||||
);
|
);
|
||||||
let _ = udp.send_to(&resp, &peer).await;
|
let _ = udp.send_to(&resp, &peer).await;
|
||||||
continue;
|
continue;
|
||||||
@ -391,13 +426,19 @@ pub async fn udp_reader_loop_with_limits(
|
|||||||
}
|
}
|
||||||
_ => (500, "Channel Bind Failed"),
|
_ => (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;
|
let _ = udp.send_to(&resp, &peer).await;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
crate::metrics::inc_channel_binding_added();
|
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;
|
let _ = udp.send_to(&resp, &peer).await;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -406,8 +447,13 @@ pub async fn udp_reader_loop_with_limits(
|
|||||||
Some(a) => a,
|
Some(a) => a,
|
||||||
None => {
|
None => {
|
||||||
warn!("send indication without allocation from {}", peer);
|
warn!("send indication without allocation from {}", peer);
|
||||||
let resp =
|
let resp = build_error_response_with_integrity_mode(
|
||||||
build_error_response_with_integrity(&msg.header, 437, "Allocation Mismatch", &key);
|
&msg.header,
|
||||||
|
437,
|
||||||
|
"Allocation Mismatch",
|
||||||
|
&key,
|
||||||
|
mi_mode,
|
||||||
|
);
|
||||||
let _ = udp.send_to(&resp, &peer).await;
|
let _ = udp.send_to(&resp, &peer).await;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -421,11 +467,12 @@ pub async fn udp_reader_loop_with_limits(
|
|||||||
let (peer_attr, data_attr) = match (peer_attr, data_attr) {
|
let (peer_attr, data_attr) = match (peer_attr, data_attr) {
|
||||||
(Some(p), Some(d)) => (p, d),
|
(Some(p), Some(d)) => (p, d),
|
||||||
_ => {
|
_ => {
|
||||||
let resp = build_error_response_with_integrity(
|
let resp = build_error_response_with_integrity_mode(
|
||||||
&msg.header,
|
&msg.header,
|
||||||
400,
|
400,
|
||||||
"Missing DATA or XOR-PEER-ADDRESS",
|
"Missing DATA or XOR-PEER-ADDRESS",
|
||||||
&key,
|
&key,
|
||||||
|
mi_mode,
|
||||||
);
|
);
|
||||||
let _ = udp.send_to(&resp, &peer).await;
|
let _ = udp.send_to(&resp, &peer).await;
|
||||||
continue;
|
continue;
|
||||||
@ -438,11 +485,12 @@ pub async fn udp_reader_loop_with_limits(
|
|||||||
) {
|
) {
|
||||||
Some(addr) => addr,
|
Some(addr) => addr,
|
||||||
None => {
|
None => {
|
||||||
let resp = build_error_response_with_integrity(
|
let resp = build_error_response_with_integrity_mode(
|
||||||
&msg.header,
|
&msg.header,
|
||||||
400,
|
400,
|
||||||
"Invalid XOR-PEER-ADDRESS",
|
"Invalid XOR-PEER-ADDRESS",
|
||||||
&key,
|
&key,
|
||||||
|
mi_mode,
|
||||||
);
|
);
|
||||||
let _ = udp.send_to(&resp, &peer).await;
|
let _ = udp.send_to(&resp, &peer).await;
|
||||||
continue;
|
continue;
|
||||||
@ -450,11 +498,12 @@ pub async fn udp_reader_loop_with_limits(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if !allocation.is_peer_allowed(&peer_addr) {
|
if !allocation.is_peer_allowed(&peer_addr) {
|
||||||
let resp = build_error_response_with_integrity(
|
let resp = build_error_response_with_integrity_mode(
|
||||||
&msg.header,
|
&msg.header,
|
||||||
403,
|
403,
|
||||||
"Peer Not Permitted",
|
"Peer Not Permitted",
|
||||||
&key,
|
&key,
|
||||||
|
mi_mode,
|
||||||
);
|
);
|
||||||
let _ = udp.send_to(&resp, &peer).await;
|
let _ = udp.send_to(&resp, &peer).await;
|
||||||
continue;
|
continue;
|
||||||
@ -468,7 +517,8 @@ pub async fn udp_reader_loop_with_limits(
|
|||||||
peer,
|
peer,
|
||||||
peer_addr
|
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;
|
let _ = udp.send_to(&resp, &peer).await;
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -478,11 +528,12 @@ pub async fn udp_reader_loop_with_limits(
|
|||||||
peer_addr,
|
peer_addr,
|
||||||
e
|
e
|
||||||
);
|
);
|
||||||
let resp = build_error_response_with_integrity(
|
let resp = build_error_response_with_integrity_mode(
|
||||||
&msg.header,
|
&msg.header,
|
||||||
500,
|
500,
|
||||||
"Peer Send Failed",
|
"Peer Send Failed",
|
||||||
&key,
|
&key,
|
||||||
|
mi_mode,
|
||||||
);
|
);
|
||||||
let _ = udp.send_to(&resp, &peer).await;
|
let _ = udp.send_to(&resp, &peer).await;
|
||||||
}
|
}
|
||||||
@ -504,19 +555,21 @@ pub async fn udp_reader_loop_with_limits(
|
|||||||
applied.as_secs()
|
applied.as_secs()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let resp = build_lifetime_success_with_integrity(
|
let resp = build_lifetime_success_with_integrity_mode(
|
||||||
&msg.header,
|
&msg.header,
|
||||||
applied.as_secs().min(u32::MAX as u64) as u32,
|
applied.as_secs().min(u32::MAX as u64) as u32,
|
||||||
&key,
|
&key,
|
||||||
|
mi_mode,
|
||||||
);
|
);
|
||||||
let _ = udp.send_to(&resp, &peer).await;
|
let _ = udp.send_to(&resp, &peer).await;
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
let resp = build_error_response_with_integrity(
|
let resp = build_error_response_with_integrity_mode(
|
||||||
&msg.header,
|
&msg.header,
|
||||||
437,
|
437,
|
||||||
"Allocation Mismatch",
|
"Allocation Mismatch",
|
||||||
&key,
|
&key,
|
||||||
|
mi_mode,
|
||||||
);
|
);
|
||||||
let _ = udp.send_to(&resp, &peer).await;
|
let _ = udp.send_to(&resp, &peer).await;
|
||||||
}
|
}
|
||||||
@ -524,11 +577,12 @@ pub async fn udp_reader_loop_with_limits(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let resp = build_error_response_with_integrity(
|
let resp = build_error_response_with_integrity_mode(
|
||||||
&msg.header,
|
&msg.header,
|
||||||
420,
|
420,
|
||||||
"Unknown TURN Method",
|
"Unknown TURN Method",
|
||||||
&key,
|
&key,
|
||||||
|
mi_mode,
|
||||||
);
|
);
|
||||||
let _ = udp.send_to(&resp, &peer).await;
|
let _ = udp.send_to(&resp, &peer).await;
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
91
src/stun.rs
91
src/stun.rs
@ -5,6 +5,15 @@ use crate::models::stun::{StunAttribute, StunHeader, StunMessage};
|
|||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use uuid::Uuid;
|
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)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
pub enum ParseError {
|
pub enum ParseError {
|
||||||
#[error("too short")]
|
#[error("too short")]
|
||||||
@ -158,6 +167,17 @@ pub fn build_error_response_with_integrity(
|
|||||||
code: u16,
|
code: u16,
|
||||||
reason: &str,
|
reason: &str,
|
||||||
key: &[u8],
|
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> {
|
) -> Vec<u8> {
|
||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
let mut buf = BytesMut::new();
|
let mut buf = BytesMut::new();
|
||||||
@ -182,7 +202,7 @@ pub fn build_error_response_with_integrity(
|
|||||||
buf.extend_from_slice(&[0]);
|
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);
|
append_fingerprint(&mut buf);
|
||||||
buf.to_vec()
|
buf.to_vec()
|
||||||
}
|
}
|
||||||
@ -227,6 +247,23 @@ pub fn build_allocate_success_with_integrity(
|
|||||||
relay: &std::net::SocketAddr,
|
relay: &std::net::SocketAddr,
|
||||||
lifetime_secs: u32,
|
lifetime_secs: u32,
|
||||||
key: &[u8],
|
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> {
|
) -> Vec<u8> {
|
||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
let mut buf = BytesMut::new();
|
let mut buf = BytesMut::new();
|
||||||
@ -252,7 +289,7 @@ pub fn build_allocate_success_with_integrity(
|
|||||||
buf.extend_from_slice(&[0]);
|
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);
|
append_fingerprint(&mut buf);
|
||||||
buf.to_vec()
|
buf.to_vec()
|
||||||
}
|
}
|
||||||
@ -284,6 +321,16 @@ pub fn build_lifetime_success_with_integrity(
|
|||||||
req: &StunHeader,
|
req: &StunHeader,
|
||||||
lifetime_secs: u32,
|
lifetime_secs: u32,
|
||||||
key: &[u8],
|
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> {
|
) -> Vec<u8> {
|
||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
let mut buf = BytesMut::new();
|
let mut buf = BytesMut::new();
|
||||||
@ -301,7 +348,7 @@ pub fn build_lifetime_success_with_integrity(
|
|||||||
buf.extend_from_slice(&[0]);
|
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);
|
append_fingerprint(&mut buf);
|
||||||
buf.to_vec()
|
buf.to_vec()
|
||||||
}
|
}
|
||||||
@ -578,9 +625,14 @@ pub fn validate_message_integrity_len_preserved(msg: &StunMessage, key: &[u8]) -
|
|||||||
false
|
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
|
// 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).
|
// 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(&ATTR_MESSAGE_INTEGRITY.to_be_bytes());
|
||||||
buf.extend_from_slice(&((HMAC_SHA1_LEN as u16).to_be_bytes()));
|
buf.extend_from_slice(&((HMAC_SHA1_LEN as u16).to_be_bytes()));
|
||||||
let mi_val_pos = buf.len();
|
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();
|
let mi_end = buf.len();
|
||||||
|
|
||||||
// Set length to end-of-MI (excluding any later attributes like FINGERPRINT)
|
// Set length to end-of-MI (excluding any later attributes like FINGERPRINT)
|
||||||
let len = (mi_end - 20) as u16;
|
let len_to_mi_end = (mi_end - 20) as u16;
|
||||||
let len_bytes = len.to_be_bytes();
|
|
||||||
buf[2] = len_bytes[0];
|
let hmac = match mi_mode {
|
||||||
buf[3] = len_bytes[1];
|
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]);
|
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.
|
/// Build a simple success (200) response including MESSAGE-INTEGRITY and FINGERPRINT.
|
||||||
pub fn build_success_response_with_integrity(req: &StunHeader, key: &[u8]) -> Vec<u8> {
|
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;
|
use bytes::BytesMut;
|
||||||
let mut buf = BytesMut::new();
|
let mut buf = BytesMut::new();
|
||||||
let msg_type: u16 = req.msg_type | CLASS_SUCCESS;
|
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(&0u16.to_be_bytes());
|
||||||
buf.extend_from_slice(&MAGIC_COOKIE_BYTES);
|
buf.extend_from_slice(&MAGIC_COOKIE_BYTES);
|
||||||
buf.extend_from_slice(&req.transaction_id);
|
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);
|
append_fingerprint(&mut buf);
|
||||||
buf.to_vec()
|
buf.to_vec()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,10 +13,11 @@ use crate::alloc::AllocationError;
|
|||||||
use crate::auth::{AuthManager, AuthStatus, InMemoryStore};
|
use crate::auth::{AuthManager, AuthStatus, InMemoryStore};
|
||||||
use crate::constants::*;
|
use crate::constants::*;
|
||||||
use crate::stun::{
|
use crate::stun::{
|
||||||
build_401_response, build_allocate_success_with_integrity, build_error_response,
|
build_401_response, build_allocate_success_with_integrity_mode, build_error_response,
|
||||||
build_error_response_with_integrity, build_lifetime_success_with_integrity,
|
build_error_response_with_integrity_mode, build_lifetime_success_with_integrity_mode,
|
||||||
build_success_response_with_integrity, decode_xor_peer_address, extract_lifetime_seconds,
|
build_success_response_with_integrity_mode, decode_xor_peer_address,
|
||||||
parse_message, validate_fingerprint_if_present, extract_requested_transport_protocol,
|
extract_lifetime_seconds, extract_requested_transport_protocol, parse_message,
|
||||||
|
validate_fingerprint_if_present, MessageIntegrityMode,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::rate_limit::RateLimiters;
|
use crate::rate_limit::RateLimiters;
|
||||||
@ -232,10 +233,16 @@ where
|
|||||||
);
|
);
|
||||||
|
|
||||||
let mut auth_key: Option<Vec<u8>> = None;
|
let mut auth_key: Option<Vec<u8>> = None;
|
||||||
|
let mut auth_mi_mode: Option<MessageIntegrityMode> = None;
|
||||||
if requires_auth {
|
if requires_auth {
|
||||||
match auth.authenticate(&msg, &peer).await {
|
match auth.authenticate(&msg, &peer).await {
|
||||||
AuthStatus::Granted { username, key } => {
|
AuthStatus::Granted {
|
||||||
|
username,
|
||||||
|
key,
|
||||||
|
mi_mode,
|
||||||
|
} => {
|
||||||
auth_key = Some(key);
|
auth_key = Some(key);
|
||||||
|
auth_mi_mode = Some(mi_mode);
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
"TURN stream auth ok for {} as {} (0x{:04x})",
|
"TURN stream auth ok for {} as {} (0x{:04x})",
|
||||||
peer,
|
peer,
|
||||||
@ -291,28 +298,32 @@ where
|
|||||||
let key = auth_key
|
let key = auth_key
|
||||||
.as_deref()
|
.as_deref()
|
||||||
.expect("auth key must be set after AuthStatus::Granted");
|
.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).
|
// TURN Allocate MUST include REQUESTED-TRANSPORT; WebRTC expects UDP (17).
|
||||||
match extract_requested_transport_protocol(&msg) {
|
match extract_requested_transport_protocol(&msg) {
|
||||||
Some(IPPROTO_UDP) => {}
|
Some(IPPROTO_UDP) => {}
|
||||||
Some(_) => {
|
Some(_) => {
|
||||||
crate::metrics::inc_allocate_fail();
|
crate::metrics::inc_allocate_fail();
|
||||||
let resp = build_error_response_with_integrity(
|
let resp = build_error_response_with_integrity_mode(
|
||||||
&msg.header,
|
&msg.header,
|
||||||
442,
|
442,
|
||||||
"Unsupported Transport",
|
"Unsupported Transport",
|
||||||
key,
|
key,
|
||||||
|
mi_mode,
|
||||||
);
|
);
|
||||||
let _ = tx.send(resp).await;
|
let _ = tx.send(resp).await;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
crate::metrics::inc_allocate_fail();
|
crate::metrics::inc_allocate_fail();
|
||||||
let resp = build_error_response_with_integrity(
|
let resp = build_error_response_with_integrity_mode(
|
||||||
&msg.header,
|
&msg.header,
|
||||||
400,
|
400,
|
||||||
"Missing REQUESTED-TRANSPORT",
|
"Missing REQUESTED-TRANSPORT",
|
||||||
key,
|
key,
|
||||||
|
mi_mode,
|
||||||
);
|
);
|
||||||
let _ = tx.send(resp).await;
|
let _ = tx.send(resp).await;
|
||||||
continue;
|
continue;
|
||||||
@ -343,11 +354,12 @@ where
|
|||||||
peer,
|
peer,
|
||||||
e
|
e
|
||||||
);
|
);
|
||||||
let resp = build_error_response_with_integrity(
|
let resp = build_error_response_with_integrity_mode(
|
||||||
&msg.header,
|
&msg.header,
|
||||||
500,
|
500,
|
||||||
"Allocate Failed",
|
"Allocate Failed",
|
||||||
key,
|
key,
|
||||||
|
mi_mode,
|
||||||
);
|
);
|
||||||
let _ = tx.send(resp).await;
|
let _ = tx.send(resp).await;
|
||||||
continue;
|
continue;
|
||||||
@ -358,11 +370,12 @@ where
|
|||||||
applied.as_secs().min(u32::MAX as u64) as u32;
|
applied.as_secs().min(u32::MAX as u64) as u32;
|
||||||
let advertised =
|
let advertised =
|
||||||
allocs.relay_addr_for_response(relay_addr);
|
allocs.relay_addr_for_response(relay_addr);
|
||||||
let resp = build_allocate_success_with_integrity(
|
let resp = build_allocate_success_with_integrity_mode(
|
||||||
&msg.header,
|
&msg.header,
|
||||||
&advertised,
|
&advertised,
|
||||||
lifetime_secs,
|
lifetime_secs,
|
||||||
key,
|
key,
|
||||||
|
mi_mode,
|
||||||
);
|
);
|
||||||
crate::metrics::inc_allocate_success();
|
crate::metrics::inc_allocate_success();
|
||||||
let _ = tx.send(resp).await;
|
let _ = tx.send(resp).await;
|
||||||
@ -376,11 +389,12 @@ where
|
|||||||
_ => (500, "Allocate Failed"),
|
_ => (500, "Allocate Failed"),
|
||||||
};
|
};
|
||||||
crate::metrics::inc_allocate_fail();
|
crate::metrics::inc_allocate_fail();
|
||||||
let resp = build_error_response_with_integrity(
|
let resp = build_error_response_with_integrity_mode(
|
||||||
&msg.header,
|
&msg.header,
|
||||||
code,
|
code,
|
||||||
reason,
|
reason,
|
||||||
key,
|
key,
|
||||||
|
mi_mode,
|
||||||
);
|
);
|
||||||
let _ = tx.send(resp).await;
|
let _ = tx.send(resp).await;
|
||||||
}
|
}
|
||||||
@ -390,12 +404,15 @@ where
|
|||||||
let key = auth_key
|
let key = auth_key
|
||||||
.as_deref()
|
.as_deref()
|
||||||
.expect("auth key must be set after AuthStatus::Granted");
|
.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() {
|
if allocs.get_allocation(&peer).is_none() {
|
||||||
let resp = build_error_response_with_integrity(
|
let resp = build_error_response_with_integrity_mode(
|
||||||
&msg.header,
|
&msg.header,
|
||||||
437,
|
437,
|
||||||
"Allocation Mismatch",
|
"Allocation Mismatch",
|
||||||
key,
|
key,
|
||||||
|
mi_mode,
|
||||||
);
|
);
|
||||||
let _ = tx.send(resp).await;
|
let _ = tx.send(resp).await;
|
||||||
continue;
|
continue;
|
||||||
@ -418,11 +435,12 @@ where
|
|||||||
e.downcast_ref::<AllocationError>(),
|
e.downcast_ref::<AllocationError>(),
|
||||||
Some(AllocationError::PermissionQuotaExceeded)
|
Some(AllocationError::PermissionQuotaExceeded)
|
||||||
) {
|
) {
|
||||||
let resp = build_error_response_with_integrity(
|
let resp = build_error_response_with_integrity_mode(
|
||||||
&msg.header,
|
&msg.header,
|
||||||
508,
|
508,
|
||||||
"Insufficient Capacity",
|
"Insufficient Capacity",
|
||||||
key,
|
key,
|
||||||
|
mi_mode,
|
||||||
);
|
);
|
||||||
let _ = tx.send(resp).await;
|
let _ = tx.send(resp).await;
|
||||||
continue;
|
continue;
|
||||||
@ -433,15 +451,17 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
if added == 0 {
|
if added == 0 {
|
||||||
let resp = build_error_response_with_integrity(
|
let resp = build_error_response_with_integrity_mode(
|
||||||
&msg.header,
|
&msg.header,
|
||||||
400,
|
400,
|
||||||
"No valid XOR-PEER-ADDRESS",
|
"No valid XOR-PEER-ADDRESS",
|
||||||
key,
|
key,
|
||||||
|
mi_mode,
|
||||||
);
|
);
|
||||||
let _ = tx.send(resp).await;
|
let _ = tx.send(resp).await;
|
||||||
} else {
|
} 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;
|
let _ = tx.send(resp).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -449,14 +469,17 @@ where
|
|||||||
let key = auth_key
|
let key = auth_key
|
||||||
.as_deref()
|
.as_deref()
|
||||||
.expect("auth key must be set after AuthStatus::Granted");
|
.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) {
|
let allocation = match allocs.get_allocation(&peer) {
|
||||||
Some(a) => a,
|
Some(a) => a,
|
||||||
None => {
|
None => {
|
||||||
let resp = build_error_response_with_integrity(
|
let resp = build_error_response_with_integrity_mode(
|
||||||
&msg.header,
|
&msg.header,
|
||||||
437,
|
437,
|
||||||
"Allocation Mismatch",
|
"Allocation Mismatch",
|
||||||
key,
|
key,
|
||||||
|
mi_mode,
|
||||||
);
|
);
|
||||||
let _ = tx.send(resp).await;
|
let _ = tx.send(resp).await;
|
||||||
continue;
|
continue;
|
||||||
@ -475,11 +498,12 @@ where
|
|||||||
{
|
{
|
||||||
(Some(c), Some(p)) => (c, p),
|
(Some(c), Some(p)) => (c, p),
|
||||||
_ => {
|
_ => {
|
||||||
let resp = build_error_response_with_integrity(
|
let resp = build_error_response_with_integrity_mode(
|
||||||
&msg.header,
|
&msg.header,
|
||||||
400,
|
400,
|
||||||
"Missing CHANNEL-NUMBER or XOR-PEER-ADDRESS",
|
"Missing CHANNEL-NUMBER or XOR-PEER-ADDRESS",
|
||||||
key,
|
key,
|
||||||
|
mi_mode,
|
||||||
);
|
);
|
||||||
let _ = tx.send(resp).await;
|
let _ = tx.send(resp).await;
|
||||||
continue;
|
continue;
|
||||||
@ -487,11 +511,12 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
if channel_attr.value.len() < 2 {
|
if channel_attr.value.len() < 2 {
|
||||||
let resp = build_error_response_with_integrity(
|
let resp = build_error_response_with_integrity_mode(
|
||||||
&msg.header,
|
&msg.header,
|
||||||
400,
|
400,
|
||||||
"Invalid CHANNEL-NUMBER",
|
"Invalid CHANNEL-NUMBER",
|
||||||
key,
|
key,
|
||||||
|
mi_mode,
|
||||||
);
|
);
|
||||||
let _ = tx.send(resp).await;
|
let _ = tx.send(resp).await;
|
||||||
continue;
|
continue;
|
||||||
@ -507,11 +532,12 @@ where
|
|||||||
) {
|
) {
|
||||||
Some(addr) => addr,
|
Some(addr) => addr,
|
||||||
None => {
|
None => {
|
||||||
let resp = build_error_response_with_integrity(
|
let resp = build_error_response_with_integrity_mode(
|
||||||
&msg.header,
|
&msg.header,
|
||||||
400,
|
400,
|
||||||
"Invalid XOR-PEER-ADDRESS",
|
"Invalid XOR-PEER-ADDRESS",
|
||||||
key,
|
key,
|
||||||
|
mi_mode,
|
||||||
);
|
);
|
||||||
let _ = tx.send(resp).await;
|
let _ = tx.send(resp).await;
|
||||||
continue;
|
continue;
|
||||||
@ -519,11 +545,12 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
if !allocation.is_peer_allowed(&peer_addr) {
|
if !allocation.is_peer_allowed(&peer_addr) {
|
||||||
let resp = build_error_response_with_integrity(
|
let resp = build_error_response_with_integrity_mode(
|
||||||
&msg.header,
|
&msg.header,
|
||||||
403,
|
403,
|
||||||
"Peer Not Permitted",
|
"Peer Not Permitted",
|
||||||
key,
|
key,
|
||||||
|
mi_mode,
|
||||||
);
|
);
|
||||||
let _ = tx.send(resp).await;
|
let _ = tx.send(resp).await;
|
||||||
continue;
|
continue;
|
||||||
@ -545,11 +572,12 @@ where
|
|||||||
}
|
}
|
||||||
_ => (500, "Channel Bind Failed"),
|
_ => (500, "Channel Bind Failed"),
|
||||||
};
|
};
|
||||||
let resp = build_error_response_with_integrity(
|
let resp = build_error_response_with_integrity_mode(
|
||||||
&msg.header,
|
&msg.header,
|
||||||
code,
|
code,
|
||||||
reason,
|
reason,
|
||||||
key,
|
key,
|
||||||
|
mi_mode,
|
||||||
);
|
);
|
||||||
let _ = tx.send(resp).await;
|
let _ = tx.send(resp).await;
|
||||||
continue;
|
continue;
|
||||||
@ -557,21 +585,25 @@ where
|
|||||||
|
|
||||||
crate::metrics::inc_channel_binding_added();
|
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;
|
let _ = tx.send(resp).await;
|
||||||
}
|
}
|
||||||
METHOD_SEND => {
|
METHOD_SEND => {
|
||||||
let key = auth_key
|
let key = auth_key
|
||||||
.as_deref()
|
.as_deref()
|
||||||
.expect("auth key must be set after AuthStatus::Granted");
|
.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) {
|
let allocation = match allocs.get_allocation(&peer) {
|
||||||
Some(a) => a,
|
Some(a) => a,
|
||||||
None => {
|
None => {
|
||||||
let resp = build_error_response_with_integrity(
|
let resp = build_error_response_with_integrity_mode(
|
||||||
&msg.header,
|
&msg.header,
|
||||||
437,
|
437,
|
||||||
"Allocation Mismatch",
|
"Allocation Mismatch",
|
||||||
key,
|
key,
|
||||||
|
mi_mode,
|
||||||
);
|
);
|
||||||
let _ = tx.send(resp).await;
|
let _ = tx.send(resp).await;
|
||||||
continue;
|
continue;
|
||||||
@ -586,11 +618,12 @@ where
|
|||||||
let (peer_attr, data_attr) = match (peer_attr, data_attr) {
|
let (peer_attr, data_attr) = match (peer_attr, data_attr) {
|
||||||
(Some(p), Some(d)) => (p, d),
|
(Some(p), Some(d)) => (p, d),
|
||||||
_ => {
|
_ => {
|
||||||
let resp = build_error_response_with_integrity(
|
let resp = build_error_response_with_integrity_mode(
|
||||||
&msg.header,
|
&msg.header,
|
||||||
400,
|
400,
|
||||||
"Missing DATA or XOR-PEER-ADDRESS",
|
"Missing DATA or XOR-PEER-ADDRESS",
|
||||||
key,
|
key,
|
||||||
|
mi_mode,
|
||||||
);
|
);
|
||||||
let _ = tx.send(resp).await;
|
let _ = tx.send(resp).await;
|
||||||
continue;
|
continue;
|
||||||
@ -603,11 +636,12 @@ where
|
|||||||
) {
|
) {
|
||||||
Some(addr) => addr,
|
Some(addr) => addr,
|
||||||
None => {
|
None => {
|
||||||
let resp = build_error_response_with_integrity(
|
let resp = build_error_response_with_integrity_mode(
|
||||||
&msg.header,
|
&msg.header,
|
||||||
400,
|
400,
|
||||||
"Invalid XOR-PEER-ADDRESS",
|
"Invalid XOR-PEER-ADDRESS",
|
||||||
key,
|
key,
|
||||||
|
mi_mode,
|
||||||
);
|
);
|
||||||
let _ = tx.send(resp).await;
|
let _ = tx.send(resp).await;
|
||||||
continue;
|
continue;
|
||||||
@ -615,11 +649,12 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
if !allocation.is_peer_allowed(&peer_addr) {
|
if !allocation.is_peer_allowed(&peer_addr) {
|
||||||
let resp = build_error_response_with_integrity(
|
let resp = build_error_response_with_integrity_mode(
|
||||||
&msg.header,
|
&msg.header,
|
||||||
403,
|
403,
|
||||||
"Peer Not Permitted",
|
"Peer Not Permitted",
|
||||||
key,
|
key,
|
||||||
|
mi_mode,
|
||||||
);
|
);
|
||||||
let _ = tx.send(resp).await;
|
let _ = tx.send(resp).await;
|
||||||
continue;
|
continue;
|
||||||
@ -627,7 +662,11 @@ where
|
|||||||
|
|
||||||
match allocation.send_to_peer(peer_addr, &data_attr.value).await {
|
match allocation.send_to_peer(peer_addr, &data_attr.value).await {
|
||||||
Ok(_) => {
|
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;
|
let _ = tx.send(resp).await;
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -637,11 +676,12 @@ where
|
|||||||
peer_addr,
|
peer_addr,
|
||||||
e
|
e
|
||||||
);
|
);
|
||||||
let resp = build_error_response_with_integrity(
|
let resp = build_error_response_with_integrity_mode(
|
||||||
&msg.header,
|
&msg.header,
|
||||||
500,
|
500,
|
||||||
"Peer Send Failed",
|
"Peer Send Failed",
|
||||||
key,
|
key,
|
||||||
|
mi_mode,
|
||||||
);
|
);
|
||||||
let _ = tx.send(resp).await;
|
let _ = tx.send(resp).await;
|
||||||
}
|
}
|
||||||
@ -651,24 +691,28 @@ where
|
|||||||
let key = auth_key
|
let key = auth_key
|
||||||
.as_deref()
|
.as_deref()
|
||||||
.expect("auth key must be set after AuthStatus::Granted");
|
.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)
|
let requested = extract_lifetime_seconds(&msg)
|
||||||
.map(|secs| Duration::from_secs(secs as u64));
|
.map(|secs| Duration::from_secs(secs as u64));
|
||||||
|
|
||||||
match allocs.refresh_allocation(peer, requested) {
|
match allocs.refresh_allocation(peer, requested) {
|
||||||
Ok(applied) => {
|
Ok(applied) => {
|
||||||
let resp = build_lifetime_success_with_integrity(
|
let resp = build_lifetime_success_with_integrity_mode(
|
||||||
&msg.header,
|
&msg.header,
|
||||||
applied.as_secs().min(u32::MAX as u64) as u32,
|
applied.as_secs().min(u32::MAX as u64) as u32,
|
||||||
key,
|
key,
|
||||||
|
mi_mode,
|
||||||
);
|
);
|
||||||
let _ = tx.send(resp).await;
|
let _ = tx.send(resp).await;
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
let resp = build_error_response_with_integrity(
|
let resp = build_error_response_with_integrity_mode(
|
||||||
&msg.header,
|
&msg.header,
|
||||||
437,
|
437,
|
||||||
"Allocation Mismatch",
|
"Allocation Mismatch",
|
||||||
key,
|
key,
|
||||||
|
mi_mode,
|
||||||
);
|
);
|
||||||
let _ = tx.send(resp).await;
|
let _ = tx.send(resp).await;
|
||||||
}
|
}
|
||||||
@ -683,14 +727,17 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let resp = match auth_key.as_deref() {
|
let resp = match (auth_key.as_deref(), auth_mi_mode) {
|
||||||
Some(key) => build_error_response_with_integrity(
|
(Some(key), Some(mi_mode)) => {
|
||||||
&msg.header,
|
build_error_response_with_integrity_mode(
|
||||||
420,
|
&msg.header,
|
||||||
"Unknown TURN Method",
|
420,
|
||||||
key,
|
"Unknown TURN Method",
|
||||||
),
|
key,
|
||||||
None => build_error_response(
|
mi_mode,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => build_error_response(
|
||||||
&msg.header,
|
&msg.header,
|
||||||
420,
|
420,
|
||||||
"Unknown TURN Method",
|
"Unknown TURN Method",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user