1use crate::host::{AddressParseError, HostAddress};
2use serde::{Deserialize, Serialize};
3use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6};
4use std::os::unix::net::SocketAddr as UnixSocketAddr;
5use std::path::Path;
6use std::str::FromStr;
7
8#[derive(Clone, Serialize, Deserialize)]
9#[serde(try_from = "String", into = "String")]
10pub enum SocketAddress {
11 UnixDomain(Box<UnixSocketAddr>),
12 V4(std::net::SocketAddrV4),
13 V6(std::net::SocketAddrV6),
14}
15
16impl std::fmt::Debug for SocketAddress {
17 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
18 <Self as std::fmt::Display>::fmt(self, fmt)
19 }
20}
21
22impl std::fmt::Display for SocketAddress {
23 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
24 match self {
25 Self::UnixDomain(unix) => match unix.as_pathname() {
26 Some(path) => path.display().fmt(fmt),
27 None => write!(fmt, "<unbound unix domain>"),
28 },
29 Self::V4(a) => a.fmt(fmt),
30 Self::V6(a) => a.fmt(fmt),
31 }
32 }
33}
34
35impl From<SocketAddress> for String {
36 fn from(a: SocketAddress) -> String {
37 format!("{a}")
38 }
39}
40
41impl TryFrom<String> for SocketAddress {
42 type Error = AddressParseError;
43 fn try_from(s: String) -> Result<SocketAddress, Self::Error> {
44 SocketAddress::from_str(&s)
45 }
46}
47
48impl SocketAddress {
49 pub fn host(&self) -> HostAddress {
51 match self {
52 Self::UnixDomain(p) => HostAddress::UnixDomain(p.clone()),
53 Self::V4(a) => HostAddress::V4(*a.ip()),
54 Self::V6(a) => HostAddress::V6(*a.ip()),
55 }
56 }
57
58 pub fn unix(&self) -> Option<UnixSocketAddr> {
60 match self {
61 Self::V4(_) | Self::V6(_) => None,
62 Self::UnixDomain(unix) => Some((**unix).clone()),
63 }
64 }
65
66 pub fn ip(&self) -> Option<SocketAddr> {
68 match self {
69 Self::V4(a) => Some((*a).into()),
70 Self::V6(a) => Some((*a).into()),
71 Self::UnixDomain(_) => None,
72 }
73 }
74}
75
76impl FromStr for SocketAddress {
77 type Err = AddressParseError;
78 fn from_str(s: &str) -> Result<SocketAddress, Self::Err> {
79 if s.starts_with('[') {
88 if let Some(host_end) = s.find(']') {
89 let (host, remainder) = s.split_at(host_end);
90 let host = &host[1..];
91
92 if let Some(port) = remainder.strip_prefix("]:") {
93 if let Ok(port) = port.parse::<u16>() {
94 match HostAddress::from_str(host) {
95 Ok(HostAddress::V4(a)) => {
96 return Ok(SocketAddress::V4(SocketAddrV4::new(a, port)))
97 }
98 Ok(HostAddress::V6(a)) => {
99 return Ok(SocketAddress::V6(SocketAddrV6::new(a, port, 0, 0)))
100 }
101
102 _ => {}
103 }
104 }
105 }
106 }
107 }
108
109 match SocketAddr::from_str(s) {
110 Ok(a) => Ok(a.into()),
111 Err(net_err) => {
112 let path: &Path = s.as_ref();
113 if path.is_relative() {
114 Err(AddressParseError {
115 candidate: s.to_string(),
116 net_err,
117 unix_err: std::io::Error::other("unix domain path must be absolute"),
118 })
119 } else {
120 match UnixSocketAddr::from_pathname(path) {
121 Ok(unix) => Ok(SocketAddress::UnixDomain(unix.into())),
122 Err(unix_err) => Err(AddressParseError {
123 candidate: s.to_string(),
124 net_err,
125 unix_err,
126 }),
127 }
128 }
129 }
130 }
131 }
132}
133
134impl PartialEq for SocketAddress {
135 fn eq(&self, other: &Self) -> bool {
136 match (self, other) {
137 (Self::UnixDomain(a), Self::UnixDomain(b)) => {
138 match (a.as_pathname(), b.as_pathname()) {
139 (Some(a), Some(b)) => a.eq(b),
140 (None, None) => true,
141 _ => false,
142 }
143 }
144 (Self::V4(a), Self::V4(b)) => a.eq(b),
145 (Self::V6(a), Self::V6(b)) => a.eq(b),
146 _ => false,
147 }
148 }
149}
150
151impl Eq for SocketAddress {}
152
153impl From<UnixSocketAddr> for SocketAddress {
154 fn from(unix: UnixSocketAddr) -> SocketAddress {
155 SocketAddress::UnixDomain(unix.into())
156 }
157}
158
159impl From<SocketAddr> for SocketAddress {
160 fn from(ip: SocketAddr) -> SocketAddress {
161 match ip {
162 SocketAddr::V4(a) => SocketAddress::V4(a),
163 SocketAddr::V6(a) => SocketAddress::V6(a),
164 }
165 }
166}
167
168impl From<tokio::net::unix::SocketAddr> for SocketAddress {
169 fn from(unix: tokio::net::unix::SocketAddr) -> SocketAddress {
170 let unix: UnixSocketAddr = unix.into();
171 unix.into()
172 }
173}
174
175#[cfg(test)]
176mod test {
177 use super::*;
178 use std::net::{Ipv4Addr, Ipv6Addr};
179
180 #[test]
181 fn parse() {
182 assert_eq!(
183 "10.0.0.1:25".parse::<SocketAddress>(),
184 Ok(SocketAddress::V4(SocketAddrV4::new(
185 Ipv4Addr::new(10, 0, 0, 1),
186 25
187 )))
188 );
189 assert_eq!(
190 "[10.0.0.1]:25".parse::<SocketAddress>(),
191 Ok(SocketAddress::V4(SocketAddrV4::new(
192 Ipv4Addr::new(10, 0, 0, 1),
193 25
194 )))
195 );
196 assert_eq!(
197 "[::1]:100".parse::<SocketAddress>(),
198 Ok(SocketAddress::V6(SocketAddrV6::new(
199 Ipv6Addr::LOCALHOST,
200 100,
201 0,
202 0
203 )))
204 );
205 assert_eq!(
206 "/some/path".parse::<SocketAddress>(),
207 Ok(SocketAddress::UnixDomain(
208 UnixSocketAddr::from_pathname("/some/path").unwrap().into()
209 ))
210 );
211 assert_eq!(
212 format!("{:#}", "hello there".parse::<SocketAddress>().unwrap_err()),
213 "Failed to parse 'hello there' as an address. \
214 Got 'invalid socket address syntax' when considering it as \
215 an IP address and 'unix domain path must be absolute' \
216 when considering it as a unix domain socket path."
217 );
218 assert_eq!(
219 format!("{:#}", "[10.0.0.1]".parse::<SocketAddress>().unwrap_err()),
220 "Failed to parse '[10.0.0.1]' as an address. \
221 Got 'invalid socket address syntax' when considering it as \
222 an IP address and 'unix domain path must be absolute' \
223 when considering it as a unix domain socket path."
224 );
225 }
226}