use std::{
error::Error,
fmt::{self, Display, Formatter},
net::SocketAddr,
};
use crate::AgentRole;
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum CandidateKind {
Host,
ServerReflexive,
PeerReflexive,
Relayed,
}
#[derive(Copy, Clone)]
pub struct LocalCandidate {
channel: usize,
component: u8,
kind: CandidateKind,
base: SocketAddr,
addr: SocketAddr,
foundation: u32,
priority: u32,
}
impl LocalCandidate {
#[inline]
pub const fn calculate_priority(component: u8, kind: CandidateKind, base: SocketAddr) -> u32 {
let type_preference = match kind {
CandidateKind::Host => 126,
CandidateKind::PeerReflexive => 110,
CandidateKind::ServerReflexive => 100,
CandidateKind::Relayed => 0,
};
let local_preference = if matches!(base, SocketAddr::V6(_)) {
65_535
} else {
65_534
};
type_preference << 24 | local_preference << 8 | (255 - component as u32)
}
#[inline]
pub const fn host(channel: usize, component: u8, addr: SocketAddr) -> Self {
Self {
channel,
component,
kind: CandidateKind::Host,
base: addr,
addr,
foundation: 0,
priority: Self::calculate_priority(component, CandidateKind::Host, addr),
}
}
#[inline]
pub const fn server_reflexive(
channel: usize,
component: u8,
base: SocketAddr,
addr: SocketAddr,
) -> Self {
Self {
channel,
component,
kind: CandidateKind::ServerReflexive,
base,
addr,
foundation: 0,
priority: Self::calculate_priority(component, CandidateKind::ServerReflexive, base),
}
}
#[inline]
pub const fn peer_reflexive(
channel: usize,
component: u8,
base: SocketAddr,
addr: SocketAddr,
) -> Self {
Self {
channel,
component,
kind: CandidateKind::PeerReflexive,
base,
addr,
foundation: 0,
priority: Self::calculate_priority(component, CandidateKind::PeerReflexive, base),
}
}
#[inline]
pub const fn relayed(channel: usize, component: u8, addr: SocketAddr) -> Self {
Self {
channel,
component,
kind: CandidateKind::Relayed,
base: addr,
addr,
foundation: 0,
priority: Self::calculate_priority(component, CandidateKind::Relayed, addr),
}
}
#[inline]
pub fn with_foundation(mut self, foundation: u32) -> Self {
self.foundation = foundation;
self
}
#[inline]
pub fn channel(&self) -> usize {
self.channel
}
#[inline]
pub fn component(&self) -> u8 {
self.component
}
#[inline]
pub fn kind(&self) -> CandidateKind {
self.kind
}
#[inline]
pub fn base(&self) -> SocketAddr {
self.base
}
#[inline]
pub fn addr(&self) -> SocketAddr {
self.addr
}
#[inline]
pub fn priority(&self) -> u32 {
self.priority
}
#[inline]
pub fn foundation(&self) -> u32 {
self.foundation
}
}
#[derive(Clone)]
pub struct RemoteCandidate {
channel: usize,
component: u8,
kind: CandidateKind,
addr: SocketAddr,
foundation: String,
priority: u32,
}
impl RemoteCandidate {
#[inline]
pub fn new<T>(
channel: usize,
component: u8,
kind: CandidateKind,
addr: SocketAddr,
foundation: T,
priority: u32,
) -> Self
where
T: ToString,
{
Self {
channel,
component,
kind,
addr,
foundation: foundation.to_string(),
priority,
}
}
#[inline]
pub fn peer_reflexive(channel: usize, component: u8, addr: SocketAddr, priority: u32) -> Self {
Self {
channel,
component,
kind: CandidateKind::PeerReflexive,
addr,
foundation: addr.to_string(),
priority,
}
}
#[inline]
pub fn channel(&self) -> usize {
self.channel
}
#[inline]
pub fn component(&self) -> u8 {
self.component
}
#[inline]
pub fn kind(&self) -> CandidateKind {
self.kind
}
#[inline]
pub fn addr(&self) -> SocketAddr {
self.addr
}
#[inline]
pub fn priority(&self) -> u32 {
self.priority
}
#[inline]
pub fn foundation(&self) -> &str {
&self.foundation
}
}
#[derive(Clone)]
pub struct CandidatePair {
local: LocalCandidate,
remote: RemoteCandidate,
foundation: String,
}
impl CandidatePair {
pub fn new(
local: LocalCandidate,
remote: RemoteCandidate,
) -> Result<Self, InvalidCandidatePair> {
if local.channel != remote.channel || local.component != remote.component {
return Err(InvalidCandidatePair);
}
let local_addr = local.addr();
match remote.addr() {
SocketAddr::V4(_) if local_addr.is_ipv4() => (),
SocketAddr::V6(_) if local_addr.is_ipv6() => (),
_ => return Err(InvalidCandidatePair),
}
let foundation = format!("{}:{}", local.foundation(), remote.foundation());
let res = Self {
local,
remote,
foundation,
};
Ok(res)
}
pub fn local(&self) -> &LocalCandidate {
&self.local
}
pub fn remote(&self) -> &RemoteCandidate {
&self.remote
}
pub fn priority(&self, local_role: AgentRole) -> u64 {
let (g, d) = match local_role {
AgentRole::Controlling => (self.local.priority(), self.remote.priority()),
AgentRole::Controlled => (self.remote.priority(), self.local.priority()),
};
let min = g.min(d) as u64;
let max = g.max(d) as u64;
(min << 32) + (max << 1) + u64::from(g > d)
}
pub fn component(&self) -> u8 {
self.remote.component()
}
pub fn foundation(&self) -> &str {
&self.foundation
}
}
#[derive(Debug, Copy, Clone)]
pub struct InvalidCandidatePair;
impl Display for InvalidCandidatePair {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_str("the given local and remote candidates do not form a pair")
}
}
impl Error for InvalidCandidatePair {}