tuwunel_service/resolver/
fed.rs1use 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
17pub(super) type HostString = SmallString<[u8; 32]>;
19
20pub(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}