use std::net::{
SocketAddr,
SocketAddrV4,
SocketAddrV6
};
use ipnetwork::{
IpNetwork,
Ipv4Network,
Ipv6Network
};
use serde::{Serialize, Deserialize};
use crate::peer::Address;
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum InterfaceAddress {
V4UdpChaCha20 {
if_name: Option<String>,
addr: SocketAddrV4,
routes: Vec<Ipv4Network>
},
V6UdpChaCha20 {
if_name: Option<String>,
addr: SocketAddrV6,
routes: Vec<Ipv6Network>
},
Dummy(usize)
}
#[derive(Copy, Clone, Debug, Serialize, PartialEq, Eq)]
pub enum InterfaceRoute<'a> {
V4UdpChaCha20(&'a [Ipv4Network]),
V6UdpChaCha20(&'a [Ipv6Network]),
Dummy
}
fn normalize_network(network: IpNetwork) -> IpNetwork {
IpNetwork::new(network.network(), network.prefix()).unwrap()
}
impl InterfaceAddress {
pub fn get_address(&self) -> Address {
match self {
Self::V4UdpChaCha20{addr, ..} => Address::V4UdpChaCha20(*addr),
Self::V6UdpChaCha20{addr, ..} => Address::V6UdpChaCha20(*addr),
Self::Dummy(addr) => Address::Dummy(*addr)
}
}
pub fn get_routes(&self) -> InterfaceRoute {
use InterfaceRoute::*;
match self {
Self::V4UdpChaCha20{routes, ..} => V4UdpChaCha20(routes),
Self::V6UdpChaCha20{routes, ..} => V6UdpChaCha20(routes),
Self::Dummy(_) => Dummy
}
}
pub fn can_send(&self, address: Address) -> bool {
match (&self, address) {
(Self::V4UdpChaCha20{routes, ..}, Address::V4UdpChaCha20(addr)) => {
routes
.iter()
.any(|r| r.contains(*addr.ip()))
},
(Self::V6UdpChaCha20{routes, ..}, Address::V6UdpChaCha20(addr)) => {
routes
.iter()
.any(|r| r.contains(*addr.ip()))
},
(Self::Dummy(_), Address::Dummy(_)) => true,
_ => false
}
}
pub fn get_socket_address(&self) -> Option<SocketAddr> {
match self {
Self::V4UdpChaCha20{addr, ..} => Some(SocketAddr::V4(*addr)),
Self::V6UdpChaCha20{addr, ..} => Some(SocketAddr::V6(*addr)),
_ => None
}
}
pub fn get_name(&self) -> Option<&String> {
match self {
Self::V4UdpChaCha20{if_name, ..} |
Self::V6UdpChaCha20{if_name, ..} => if_name.as_ref(),
_ => None
}
}
}
impl std::fmt::Display for InterfaceAddress {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self {
Self::V4UdpChaCha20{addr, routes, ..} => {
write!(f, "IPv4-UDP-ChaCha20/{addr}/[")?;
if routes.is_empty() {
write!(f, "no routes")?;
}
else {
write!(f, "{}", routes[0])?;
for r in routes[1..].iter() {
write!(f, ", {r}")?
}
}
write!(f, "]")
},
Self::V6UdpChaCha20{addr, routes, ..} => {
write!(f, "IPv6-UDP-ChaCha20/{addr}/[")?;
if routes.is_empty() {
write!(f, "no routes")?;
}
else {
write!(f, "{}", routes[0])?;
for r in routes[1..].iter() {
write!(f, ", {r}")?
}
}
write!(f, "]")
},
Self::Dummy(addr) => write!(f, "dummy_if/{addr}")
}
}
}
impl std::str::FromStr for InterfaceAddress {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut split = s.split_whitespace();
let socket_addr: SocketAddr = split.next()
.ok_or_else(|| "invalid empty interface address".to_string())?
.parse()
.map_err(|e| format!("failed to parse socket address: {e}"))?;
let parse_route_spec = |route_spec: &str| -> Result<IpNetwork, String> {
match route_spec.parse::<IpNetwork>() {
Ok(n) if n.is_ipv4() && socket_addr.is_ipv4() => Ok(n),
Ok(n) if n.is_ipv6() && socket_addr.is_ipv6() => Ok(n),
Err(e) => Err(format!("invalid route spec: {e}")),
_ => Err("invalid route spec: IP version mismatch".to_string())
}
};
let parse_first_route_spec = |route_spec: &str| {
if route_spec.starts_with('/') {
let prefix: u8 = route_spec
.split_at(1)
.1
.parse()
.map_err(|e| format!("invalid route spec: {e}"))?;
IpNetwork::new(socket_addr.ip(), prefix)
.map_err(|e| format!("invalid route spec: {e}"))
}
else {
parse_route_spec(route_spec)
}
};
let routes = match split.next() {
Some(route_spec) => {
let mut networks = vec![
parse_first_route_spec(route_spec)?,
];
for route_spec in split {
networks.push(parse_route_spec(route_spec)?);
}
networks
.into_iter()
.map(normalize_network)
.collect()
},
None => Vec::new()
};
let if_name = None;
match socket_addr {
SocketAddr::V4(addr) => {
let routes = routes.into_iter().map(|n| match n {
IpNetwork::V4(r) => Ok(r),
IpNetwork::V6(_) => Err(
"IP version mismatch in parsed routes".to_string()
)
}).collect::<Result<_, _>>()?;
Ok(Self::V4UdpChaCha20{addr, routes, if_name})
},
SocketAddr::V6(addr) => {
let routes = routes.into_iter().map(|n| match n {
IpNetwork::V4(_) => Err(
"IP version mismatch in parsed routes".to_string()
),
IpNetwork::V6(r) => Ok(r)
}).collect::<Result<_, _>>()?;
Ok(Self::V6UdpChaCha20{addr, routes, if_name})
}
}
}
}