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	dest_str
27		.parse()
28		.map(FedDest::Literal)
29		.or_else(|_| {
30			dest_str
31				.parse()
32				.map(|ip_addr: IpAddr| FedDest::Literal(SocketAddr::new(ip_addr, 8448)))
33		})
34		.ok()
35}
36
37pub(crate) fn add_port_to_hostname(dest: &str) -> FedDest {
38	let (host, port) = match dest.find(':') {
39		| None => (dest, DEFAULT_PORT),
40		| Some(pos) => dest.split_at(pos),
41	};
42
43	FedDest::Named(
44		host.into(),
45		PortString::from(port).unwrap_or_else(|_| FedDest::default_port()),
46	)
47}
48
49impl FedDest {
50	pub(crate) fn https_string(&self) -> DestString {
51		match self {
52			| Self::Literal(addr) => format!("https://{addr}").into(),
53			| Self::Named(host, port) => format!("https://{host}{port}").into(),
54		}
55	}
56
57	pub(crate) fn uri_string(&self) -> DestString {
58		match self {
59			| Self::Literal(addr) => addr.to_string().into(),
60			| Self::Named(host, port) => [host.as_str(), port.as_str()].concat().into(),
61		}
62	}
63
64	#[inline]
65	pub(crate) fn hostname(&self) -> HostString {
66		match &self {
67			| Self::Literal(addr) => addr.ip().to_string().into(),
68			| Self::Named(host, _) => host.clone(),
69		}
70	}
71
72	#[inline]
73	#[expect(clippy::string_slice)]
74	pub(crate) fn port(&self) -> Option<u16> {
75		match &self {
76			| Self::Literal(addr) => Some(addr.port()),
77			| Self::Named(_, port) => port[1..].parse().ok(),
78		}
79	}
80
81	#[inline]
82	#[must_use]
83	pub fn default_port() -> PortString {
84		PortString::from(DEFAULT_PORT).expect("default port string")
85	}
86
87	#[inline]
88	#[must_use]
89	pub fn size(&self) -> usize {
90		match self {
91			| Self::Literal(saddr) => size_of_val(saddr),
92			| Self::Named(host, port) => host.len().expected_add(port.capacity()),
93		}
94	}
95}
96
97impl fmt::Display for FedDest {
98	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99		f.write_str(self.uri_string().as_str())
100	}
101}