kumo_address/
host_or_socket.rs

1use crate::host::{AddressParseError, HostAddress};
2use crate::socket::SocketAddress;
3use serde::{Deserialize, Serialize};
4use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
5use std::os::unix::net::SocketAddr as UnixSocketAddr;
6use std::str::FromStr;
7
8/// Specify either a unix or IP address. The IP address can optionally
9/// include a port number
10#[derive(Clone, Serialize, Deserialize)]
11#[serde(try_from = "String", into = "String")]
12pub enum HostOrSocketAddress {
13    UnixDomain(Box<UnixSocketAddr>),
14    V4Socket(Box<SocketAddrV4>),
15    V6Socket(Box<SocketAddrV6>),
16    V4Host(std::net::Ipv4Addr),
17    V6Host(std::net::Ipv6Addr),
18}
19
20impl std::fmt::Debug for HostOrSocketAddress {
21    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
22        <Self as std::fmt::Display>::fmt(self, fmt)
23    }
24}
25
26impl std::fmt::Display for HostOrSocketAddress {
27    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
28        match self {
29            Self::UnixDomain(unix) => match unix.as_pathname() {
30                Some(path) => path.display().fmt(fmt),
31                None => write!(fmt, "<unbound unix domain>"),
32            },
33            Self::V4Socket(a) => a.fmt(fmt),
34            Self::V6Socket(a) => a.fmt(fmt),
35            Self::V4Host(a) => a.fmt(fmt),
36            Self::V6Host(a) => a.fmt(fmt),
37        }
38    }
39}
40
41impl From<SocketAddress> for HostOrSocketAddress {
42    fn from(a: SocketAddress) -> HostOrSocketAddress {
43        match a {
44            SocketAddress::UnixDomain(unix) => HostOrSocketAddress::UnixDomain(unix),
45            SocketAddress::V4(v4) => HostOrSocketAddress::V4Socket(v4.into()),
46            SocketAddress::V6(v6) => HostOrSocketAddress::V6Socket(v6.into()),
47        }
48    }
49}
50
51impl From<HostAddress> for HostOrSocketAddress {
52    fn from(a: HostAddress) -> HostOrSocketAddress {
53        match a {
54            HostAddress::UnixDomain(unix) => HostOrSocketAddress::UnixDomain(unix),
55            HostAddress::V4(v4) => HostOrSocketAddress::V4Host(v4),
56            HostAddress::V6(v6) => HostOrSocketAddress::V6Host(v6),
57        }
58    }
59}
60
61impl From<HostOrSocketAddress> for String {
62    fn from(a: HostOrSocketAddress) -> String {
63        format!("{a}")
64    }
65}
66
67impl TryFrom<String> for HostOrSocketAddress {
68    type Error = AddressParseError;
69    fn try_from(s: String) -> Result<HostOrSocketAddress, Self::Error> {
70        HostOrSocketAddress::from_str(&s)
71    }
72}
73
74impl HostOrSocketAddress {
75    /// Returns the "host" portion of the address
76    pub fn host(&self) -> HostAddress {
77        match self {
78            Self::UnixDomain(p) => HostAddress::UnixDomain(p.clone()),
79            Self::V4Host(a) => HostAddress::V4(*a),
80            Self::V4Socket(a) => HostAddress::V4(*a.ip()),
81            Self::V6Host(a) => HostAddress::V6(*a),
82            Self::V6Socket(a) => HostAddress::V6(*a.ip()),
83        }
84    }
85
86    /// Returns the unix domain socket representation of the address
87    pub fn unix(&self) -> Option<UnixSocketAddr> {
88        match self {
89            Self::UnixDomain(unix) => Some((**unix).clone()),
90            Self::V4Host(_) | Self::V6Host(_) | Self::V4Socket(_) | Self::V6Socket(_) => None,
91        }
92    }
93
94    /// Returns the ip representation of the address
95    pub fn ip(&self) -> Option<IpAddr> {
96        match self {
97            Self::UnixDomain(_) => None,
98            Self::V4Host(a) => Some((*a).into()),
99            Self::V4Socket(a) => Some((*a.ip()).into()),
100            Self::V6Host(a) => Some((*a).into()),
101            Self::V6Socket(a) => Some((*a.ip()).into()),
102        }
103    }
104
105    /// Returns the ip SocketAddr (including port) representation of the address
106    pub fn ip_and_port(&self) -> Option<SocketAddr> {
107        match self {
108            Self::UnixDomain(_) => None,
109            Self::V4Host(_) | Self::V6Host(_) => None,
110            Self::V4Socket(a) => Some((*a.clone()).into()),
111            Self::V6Socket(a) => Some((*a.clone()).into()),
112        }
113    }
114
115    /// Returns the port number, if specified
116    pub fn port(&self) -> Option<u16> {
117        self.ip_and_port().map(|sa| sa.port())
118    }
119
120    /// Assign a new port number
121    pub fn set_port(&mut self, port: u16) {
122        match self {
123            HostOrSocketAddress::UnixDomain(_) => {}
124            HostOrSocketAddress::V6Socket(s) => {
125                s.set_port(port);
126            }
127            HostOrSocketAddress::V4Socket(s) => {
128                s.set_port(port);
129            }
130            HostOrSocketAddress::V4Host(v4) => {
131                *self = HostOrSocketAddress::V4Socket(Box::new(SocketAddrV4::new(*v4, port)));
132            }
133            HostOrSocketAddress::V6Host(v6) => {
134                *self = HostOrSocketAddress::V6Socket(Box::new(SocketAddrV6::new(*v6, port, 0, 0)));
135            }
136        }
137    }
138
139    /// Assign a port number if no port is already set
140    pub fn set_port_if_not_set(&mut self, port: u16) {
141        match self {
142            HostOrSocketAddress::UnixDomain(_) => {}
143            HostOrSocketAddress::V6Socket(_) | HostOrSocketAddress::V4Socket(_) => {
144                // Already has a port: don't override
145            }
146            HostOrSocketAddress::V4Host(v4) => {
147                *self = HostOrSocketAddress::V4Socket(Box::new(SocketAddrV4::new(*v4, port)));
148            }
149            HostOrSocketAddress::V6Host(v6) => {
150                *self = HostOrSocketAddress::V6Socket(Box::new(SocketAddrV6::new(*v6, port, 0, 0)));
151            }
152        }
153    }
154}
155
156impl FromStr for HostOrSocketAddress {
157    type Err = AddressParseError;
158    fn from_str(s: &str) -> Result<HostOrSocketAddress, Self::Err> {
159        match SocketAddress::from_str(s) {
160            Ok(sa) => Ok(sa.into()),
161            Err(sa_err) => match HostAddress::from_str(s) {
162                Ok(ha) => Ok(ha.into()),
163                Err(_ha_err) => Err(sa_err),
164            },
165        }
166    }
167}
168
169impl PartialEq for HostOrSocketAddress {
170    fn eq(&self, other: &Self) -> bool {
171        match (self, other) {
172            (Self::UnixDomain(a), Self::UnixDomain(b)) => {
173                match (a.as_pathname(), b.as_pathname()) {
174                    (Some(a), Some(b)) => a.eq(b),
175                    (None, None) => true,
176                    _ => false,
177                }
178            }
179            (Self::V4Socket(a), Self::V4Socket(b)) => a.eq(b),
180            (Self::V6Socket(a), Self::V6Socket(b)) => a.eq(b),
181            (Self::V4Host(a), Self::V4Host(b)) => a.eq(b),
182            (Self::V6Host(a), Self::V6Host(b)) => a.eq(b),
183            _ => false,
184        }
185    }
186}
187
188impl Eq for HostOrSocketAddress {}
189
190impl From<UnixSocketAddr> for HostOrSocketAddress {
191    fn from(unix: UnixSocketAddr) -> HostOrSocketAddress {
192        HostOrSocketAddress::UnixDomain(unix.into())
193    }
194}
195
196impl From<IpAddr> for HostOrSocketAddress {
197    fn from(ip: IpAddr) -> HostOrSocketAddress {
198        match ip {
199            IpAddr::V4(a) => HostOrSocketAddress::V4Host(a),
200            IpAddr::V6(a) => HostOrSocketAddress::V6Host(a),
201        }
202    }
203}
204
205impl From<Ipv4Addr> for HostOrSocketAddress {
206    fn from(ip: Ipv4Addr) -> HostOrSocketAddress {
207        HostOrSocketAddress::V4Host(ip)
208    }
209}
210
211impl From<Ipv6Addr> for HostOrSocketAddress {
212    fn from(ip: Ipv6Addr) -> HostOrSocketAddress {
213        HostOrSocketAddress::V6Host(ip)
214    }
215}
216
217impl From<SocketAddr> for HostOrSocketAddress {
218    fn from(ip: SocketAddr) -> HostOrSocketAddress {
219        match ip {
220            SocketAddr::V4(a) => HostOrSocketAddress::V4Socket(a.into()),
221            SocketAddr::V6(a) => HostOrSocketAddress::V6Socket(a.into()),
222        }
223    }
224}
225
226impl From<tokio::net::unix::SocketAddr> for HostOrSocketAddress {
227    fn from(unix: tokio::net::unix::SocketAddr) -> HostOrSocketAddress {
228        let unix: UnixSocketAddr = unix.into();
229        unix.into()
230    }
231}
232
233#[cfg(test)]
234mod test {
235    use super::*;
236    use std::net::{Ipv4Addr, Ipv6Addr};
237
238    #[test]
239    fn parse() {
240        assert_eq!(
241            "10.0.0.1:25".parse::<HostOrSocketAddress>(),
242            Ok(HostOrSocketAddress::V4Socket(
243                SocketAddrV4::new(Ipv4Addr::new(10, 0, 0, 1), 25).into()
244            ))
245        );
246        assert_eq!(
247            "[10.0.0.1]:25".parse::<HostOrSocketAddress>(),
248            Ok(HostOrSocketAddress::V4Socket(
249                SocketAddrV4::new(Ipv4Addr::new(10, 0, 0, 1), 25).into()
250            ))
251        );
252        assert_eq!(
253            "[::1]:100".parse::<HostOrSocketAddress>(),
254            Ok(HostOrSocketAddress::V6Socket(
255                SocketAddrV6::new(Ipv6Addr::LOCALHOST, 100, 0, 0).into()
256            ))
257        );
258        assert_eq!(
259            "/some/path".parse::<HostOrSocketAddress>(),
260            Ok(HostOrSocketAddress::UnixDomain(
261                UnixSocketAddr::from_pathname("/some/path").unwrap().into()
262            ))
263        );
264        assert_eq!(
265            "[10.0.0.1]".parse::<HostOrSocketAddress>(),
266            Ok(HostOrSocketAddress::V4Host(Ipv4Addr::new(10, 0, 0, 1),))
267        );
268        assert_eq!(
269            "[::1]".parse::<HostOrSocketAddress>(),
270            Ok(HostOrSocketAddress::V6Host(Ipv6Addr::LOCALHOST))
271        );
272        assert_eq!(
273            format!(
274                "{:#}",
275                "hello there".parse::<HostOrSocketAddress>().unwrap_err()
276            ),
277            "Failed to parse 'hello there' as an address. \
278            Got 'invalid socket address syntax' when considering it as \
279            an IP address and 'unix domain path must be absolute' \
280            when considering it as a unix domain socket path."
281        );
282        assert_eq!(
283            format!(
284                "{:#}",
285                "[10.0.0.1]:bogus"
286                    .parse::<HostOrSocketAddress>()
287                    .unwrap_err()
288            ),
289            "Failed to parse '[10.0.0.1]:bogus' as an address. \
290            Got 'invalid socket address syntax' when considering it as \
291            an IP address and 'unix domain path must be absolute' \
292            when considering it as a unix domain socket path."
293        );
294    }
295}