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 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}