pub mod datachannel;
pub mod dtls;
pub mod ice;
pub mod rtp;
pub mod sctp;
#[cfg(feature = "std")]
pub mod udptl;
#[cfg(not(feature = "std"))]
use crate::prelude::*;
#[cfg_attr(feature = "std", allow(unused_imports))]
use async_trait::async_trait;
#[cfg_attr(feature = "std", allow(unused_imports))]
use bytes::Bytes;
use core::net::{IpAddr, SocketAddr};
use core::time::Duration;
#[cfg(feature = "std")]
use parking_lot::Mutex;
#[cfg(feature = "std")]
use std::sync::OnceLock;
#[cfg(feature = "std")]
use std::time::Instant;
#[cfg(feature = "std")]
const DEFAULT_LOCAL_IP_CACHE_TTL: Duration = Duration::from_secs(5);
#[cfg(feature = "std")]
const LOCAL_IP_CACHE_TTL_ENV: &str = "RUSTRTC_LOCAL_IP_CACHE_TTL_SECS";
#[cfg(feature = "std")]
#[derive(Clone, Copy)]
struct LocalIpCacheEntry {
ip: IpAddr,
expires_at: Instant,
}
#[cfg(feature = "std")]
static LOCAL_IP_CACHE: OnceLock<Mutex<Option<LocalIpCacheEntry>>> = OnceLock::new();
#[async_trait]
pub trait PacketReceiver: Send + Sync {
async fn receive(&self, packet: Bytes, addr: SocketAddr, marshal_buf: &mut Vec<u8>);
}
#[cfg(not(feature = "std"))]
static LOCAL_IP_FN: crate::platform::sync::Mutex<Option<fn() -> Option<IpAddr>>> =
crate::platform::sync::Mutex::new(None);
#[cfg(not(feature = "std"))]
pub fn set_local_ip_fn(f: fn() -> Option<IpAddr>) {
*LOCAL_IP_FN.lock() = Some(f);
}
#[cfg(not(feature = "std"))]
pub fn get_local_ip() -> Result<IpAddr, crate::errors::RtcError> {
let f = *LOCAL_IP_FN.lock();
match f {
Some(f) => f().ok_or_else(|| {
crate::errors::RtcError::Internal(alloc::string::String::from(
"get_local_ip: provider has no address yet (network down?)",
))
}),
None => Err(crate::errors::RtcError::Internal(
alloc::string::String::from("get_local_ip: no provider wired (set_local_ip_fn)"),
)),
}
}
#[cfg(feature = "std")]
pub fn get_local_ip() -> Result<IpAddr, anyhow::Error> {
let ttl = local_ip_cache_ttl();
if ttl.is_zero() {
return resolve_local_ip_uncached();
}
let cache = LOCAL_IP_CACHE.get_or_init(|| Mutex::new(None));
{
let cache_guard = cache.lock();
if let Some(entry) = *cache_guard
&& Instant::now() < entry.expires_at
{
return Ok(entry.ip);
}
}
let ip = resolve_local_ip_uncached()?;
{
let mut cache_guard = cache.lock();
*cache_guard = Some(LocalIpCacheEntry {
ip,
expires_at: Instant::now() + ttl,
});
}
Ok(ip)
}
#[cfg(feature = "std")]
fn local_ip_cache_ttl() -> Duration {
static TTL: OnceLock<Duration> = OnceLock::new();
*TTL.get_or_init(|| match std::env::var(LOCAL_IP_CACHE_TTL_ENV) {
Ok(value) => match value.parse::<u64>() {
Ok(secs) => Duration::from_secs(secs),
Err(_) => {
tracing::warn!(
"Invalid {} value '{}', fallback to {}s",
LOCAL_IP_CACHE_TTL_ENV,
value,
DEFAULT_LOCAL_IP_CACHE_TTL.as_secs()
);
DEFAULT_LOCAL_IP_CACHE_TTL
}
},
Err(_) => DEFAULT_LOCAL_IP_CACHE_TTL,
})
}
#[cfg(feature = "std")]
fn resolve_local_ip_uncached() -> Result<IpAddr, anyhow::Error> {
use local_ip_address::list_afinet_netifas;
if let Ok(interfaces) = list_afinet_netifas() {
let mut candidates: Vec<(std::net::Ipv4Addr, i32, String)> = Vec::new();
for (name, addr) in &interfaces {
if let IpAddr::V4(ip) = addr {
let ip = *ip;
let score = interface_priority(name, &ip);
if score > -1000 {
candidates.push((ip, score, name.clone()));
}
}
}
candidates.sort_by_key(|c| std::cmp::Reverse(c.1));
if let Some((ip, score, name)) = candidates.first() {
tracing::trace!(
"Selected network interface: {} (IP: {}, score: {})",
name,
ip,
score
);
return Ok(IpAddr::V4(*ip));
}
}
Err(anyhow::anyhow!("No suitable network interface found"))
}
#[cfg(feature = "std")]
fn interface_priority(name: &str, ip: &std::net::Ipv4Addr) -> i32 {
let mut score = 0;
if name == "lo0" || ip.is_loopback() {
return -1000; }
if name.starts_with("en") || name.starts_with("wlan") || name.starts_with("eth") {
score += 100;
} else if name.starts_with("utun")
|| name.starts_with("bridge")
|| name.starts_with("gif")
|| name.starts_with("stf")
|| name.starts_with("awdl")
|| name.starts_with("llw")
{
score -= 50;
}
let octets = ip.octets();
if octets[0] == 192 && octets[1] == 168 {
score += 50;
} else if octets[0] == 10 {
score += 40;
} else if octets[0] == 172 && (octets[1] >= 16 && octets[1] <= 31) {
score += 40;
} else if !ip.is_private() {
score -= 30;
}
if octets[0] == 169 && octets[1] == 254 {
score -= 100;
}
score
}