use std::net::{IpAddr, Ipv6Addr, SocketAddr, SocketAddrV6};
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ServerCertHash {
pub hash: [u8; 32],
}
impl TryFrom<Vec<u8>> for ServerCertHash {
type Error = ();
fn try_from(vec: Vec<u8>) -> Result<Self, ()> {
if vec.len() != 32 {
return Err(());
}
let mut hash = [0; 32];
hash.copy_from_slice(&vec[0..32]);
Ok(Self { hash })
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum WebServerDestination {
Addr(SocketAddr),
Url(url::Url),
}
impl From<SocketAddr> for WebServerDestination {
fn from(addr: SocketAddr) -> Self {
Self::Addr(addr)
}
}
impl From<url::Url> for WebServerDestination {
fn from(url: url::Url) -> Self {
Self::Url(url)
}
}
impl From<WebServerDestination> for SocketAddr {
fn from(dest: WebServerDestination) -> Self {
match dest {
WebServerDestination::Addr(addr) => addr,
WebServerDestination::Url(url) => hash_url_to_socket_addr(url),
}
}
}
impl TryFrom<WebServerDestination> for url::Url {
type Error = ();
fn try_from(dest: WebServerDestination) -> Result<Self, Self::Error> {
match dest {
WebServerDestination::Addr(addr) => wt_server_addr_to_url(addr),
WebServerDestination::Url(url) => Ok(url),
}
}
}
#[allow(unused)]
pub(crate) const HTTP_CONNECT_REQ: &str = "creq";
fn hash_url_to_socket_addr(url: url::Url) -> SocketAddr {
let hash = hmac_sha256::Hash::hash(url.as_str().as_bytes());
SocketAddrV6::new(
Ipv6Addr::from(TryInto::<[u8; 16]>::try_into(&hash[0..16]).unwrap()),
u16::from_le_bytes(hash[16..18].try_into().unwrap()),
u32::from_le_bytes(hash[18..22].try_into().unwrap()),
u32::from_le_bytes(hash[22..26].try_into().unwrap()),
)
.into()
}
fn wt_server_addr_to_url(addr: SocketAddr) -> Result<url::Url, ()> {
let mut url = url::Url::parse("https://example.net").map_err(|_| ())?;
url.set_ip_host(addr.ip())?;
url.set_port(Some(addr.port()))?;
Ok(url)
}
#[allow(unused)]
pub(crate) fn client_idx_to_addr(idx: u64) -> SocketAddr {
SocketAddr::new(
IpAddr::V6(Ipv6Addr::new(
idx as u16,
(idx >> 16) as u16,
(idx >> 32) as u16,
(idx >> 48) as u16,
0,
0,
0,
0,
)),
0,
)
}
#[allow(unused)]
pub(crate) fn client_idx_from_addr(addr: SocketAddr) -> u64 {
let SocketAddr::V6(addr_v6) = addr else {
panic!("V6 addresses are expected to represent client idxs")
};
let octets = addr_v6.ip().octets();
let mut idx = 0u64;
for i in (0..4).rev() {
idx <<= 16;
idx += ((octets[2 * i] as u64) << 8) + (octets[2 * i + 1] as u64);
}
idx
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn client_addr_conversion() {
let addr0 = client_idx_to_addr(0);
let addr1 = client_idx_to_addr(1);
let addr16 = client_idx_to_addr(16);
let addr257 = client_idx_to_addr(257);
assert_eq!(client_idx_from_addr(addr0), 0);
assert_eq!(client_idx_from_addr(addr1), 1);
assert_eq!(client_idx_from_addr(addr16), 16);
assert_eq!(client_idx_from_addr(addr257), 257);
}
}