Skip to main content

tuwunel_service/resolver/
fed.rs

1use std::{
2	fmt,
3	net::{IpAddr, SocketAddr},
4};
5
6use serde::{Deserialize, Serialize};
7use tuwunel_core::{arrayvec::ArrayString, smallstr::SmallString, utils::math::Expected};
8
9use super::DestString;
10
11#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
12pub enum FedDest {
13	Literal(SocketAddr),
14	Named(HostString, PortString),
15}
16
17/// FedDest::Named host domain
18pub(super) type HostString = SmallString<[u8; 32]>;
19
20/// FedDest::Named numeric or service-name
21pub(super) type PortString = ArrayString<16>;
22
23const DEFAULT_PORT: &str = ":8448";
24
25pub(crate) fn get_ip_with_port(dest_str: &str) -> Option<FedDest> {
26	if let Ok(dest) = dest_str.parse::<SocketAddr>() {
27		Some(FedDest::Literal(dest))
28	} else if let Ok(ip_addr) = dest_str.parse::<IpAddr>() {
29		Some(FedDest::Literal(SocketAddr::new(ip_addr, 8448)))
30	} else {
31		None
32	}
33}
34
35pub(crate) fn add_port_to_hostname(dest: &str) -> FedDest {
36	let (host, port) = match dest.find(':') {
37		| None => (dest, DEFAULT_PORT),
38		| Some(pos) => dest.split_at(pos),
39	};
40
41	FedDest::Named(
42		host.into(),
43		PortString::from(port).unwrap_or_else(|_| FedDest::default_port()),
44	)
45}
46
47impl FedDest {
48	pub(crate) fn https_string(&self) -> DestString {
49		match self {
50			| Self::Literal(addr) => format!("https://{addr}").into(),
51			| Self::Named(host, port) => format!("https://{host}{port}").into(),
52		}
53	}
54
55	pub(crate) fn uri_string(&self) -> DestString {
56		match self {
57			| Self::Literal(addr) => addr.to_string().into(),
58			| Self::Named(host, port) => [host.as_str(), port.as_str()].concat().into(),
59		}
60	}
61
62	#[inline]
63	pub(crate) fn hostname(&self) -> HostString {
64		match &self {
65			| Self::Literal(addr) => addr.ip().to_string().into(),
66			| Self::Named(host, _) => host.clone(),
67		}
68	}
69
70	#[inline]
71	#[expect(clippy::string_slice)]
72	pub(crate) fn port(&self) -> Option<u16> {
73		match &self {
74			| Self::Literal(addr) => Some(addr.port()),
75			| Self::Named(_, port) => port[1..].parse().ok(),
76		}
77	}
78
79	#[inline]
80	#[must_use]
81	pub fn default_port() -> PortString {
82		PortString::from(DEFAULT_PORT).expect("default port string")
83	}
84
85	#[inline]
86	#[must_use]
87	pub fn size(&self) -> usize {
88		match self {
89			| Self::Literal(saddr) => size_of_val(saddr),
90			| Self::Named(host, port) => host.len().expected_add(port.capacity()),
91		}
92	}
93}
94
95impl fmt::Display for FedDest {
96	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97		f.write_str(self.uri_string().as_str())
98	}
99}