1use crate::{CfgPath, CfgPathError};
4use serde::{Deserialize, Serialize};
5use std::{io, net, path::PathBuf, str::FromStr, sync::Arc};
6use tor_general_addr::{general, unix};
7
8#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
20#[serde(into = "CfgAddrSerde", try_from = "CfgAddrSerde")]
21pub struct CfgAddr(AddrInner);
22
23#[derive(Clone, Debug, Eq, PartialEq)]
27enum AddrInner {
28 Inet(net::SocketAddr),
30 Unix(CfgPath),
32}
33
34impl CfgAddr {
35 pub fn new_unix(path: CfgPath) -> Self {
41 CfgAddr(AddrInner::Unix(path))
42 }
43
44 #[cfg_attr(not(unix), expect(unused_variables))]
46 pub fn address(
47 &self,
48 path_resolver: &crate::CfgPathResolver,
49 ) -> Result<general::SocketAddr, CfgAddrError> {
50 match &self.0 {
51 AddrInner::Inet(socket_addr) => {
52 Ok((*socket_addr).into())
54 }
55 AddrInner::Unix(cfg_path) => {
56 #[cfg(not(unix))]
57 {
58 Err(unix::NoAfUnixSocketSupport::default().into())
60 }
61 #[cfg(unix)]
62 {
63 let addr = unix::SocketAddr::from_pathname(cfg_path.path(path_resolver)?)
64 .map_err(|e| CfgAddrError::ConstructAfUnixAddress(Arc::new(e)))?;
65 Ok(addr.into())
66 }
67 }
68 }
69 }
70
71 pub fn substitutions_will_apply(&self) -> bool {
75 match &self.0 {
76 AddrInner::Inet(_) => false,
77 AddrInner::Unix(_) => true,
78 }
79 }
80
81 fn try_to_string(&self) -> Result<String, &PathBuf> {
89 use crate::PathInner as PI;
90 use AddrInner as AI;
91 match &self.0 {
92 AI::Inet(socket_addr) => Ok(format!("inet:{}", socket_addr)),
93 AI::Unix(cfg_path) => match &cfg_path.0 {
94 PI::Shell(s) => Ok(format!("unix:{}", s)),
95 PI::Literal(path) => match path.literal.to_str() {
96 Some(literal_as_str) => Ok(format!("unix-literal:{}", literal_as_str)),
97 None => Err(&path.literal),
98 },
99 },
100 }
101 }
102}
103
104#[derive(Clone, Debug, thiserror::Error)]
106#[non_exhaustive]
107pub enum CfgAddrError {
108 #[error("No support for AF_UNIX addresses on this platform")]
110 NoAfUnixSocketSupport(#[from] unix::NoAfUnixSocketSupport),
111 #[error("Could not expand path")]
113 Path(#[from] CfgPathError),
114 #[error("Could not construct AF_UNIX address")]
118 ConstructAfUnixAddress(#[source] Arc<io::Error>),
119}
120
121impl FromStr for CfgAddr {
122 type Err = general::AddrParseError;
123
124 fn from_str(s: &str) -> Result<Self, Self::Err> {
125 if s.starts_with(|c: char| c.is_ascii_digit() || c == '[') {
128 Ok(s.parse::<net::SocketAddr>()?.into())
130 } else if let Some((schema, remainder)) = s.split_once(':') {
131 match schema {
132 "unix" => {
133 let path = CfgPath::new(remainder.to_string());
134 Ok(CfgAddr::new_unix(path))
135 }
136 "unix-literal" => {
137 let path = CfgPath::new_literal(remainder.to_string());
138 Ok(CfgAddr::new_unix(path))
139 }
140 "inet" => Ok(remainder.parse::<net::SocketAddr>()?.into()),
141 _ => Err(general::AddrParseError::UnrecognizedSchema(
142 schema.to_string(),
143 )),
144 }
145 } else {
146 Err(general::AddrParseError::NoSchema)
147 }
148 }
149}
150
151impl From<net::SocketAddr> for CfgAddr {
152 fn from(value: net::SocketAddr) -> Self {
153 CfgAddr(AddrInner::Inet(value))
154 }
155}
156impl TryFrom<unix::SocketAddr> for CfgAddr {
157 type Error = UnixAddrNotAPath;
158
159 fn try_from(value: unix::SocketAddr) -> Result<Self, Self::Error> {
160 Ok(Self::new_unix(CfgPath::new_literal(
163 value.as_pathname().ok_or(UnixAddrNotAPath)?,
164 )))
165 }
166}
167#[derive(Clone, Debug, Default, thiserror::Error)]
173#[non_exhaustive]
174#[error("Unix domain socket address was not a path.")]
175pub struct UnixAddrNotAPath;
176
177#[derive(Serialize, Deserialize)]
179#[serde(untagged)]
180enum CfgAddrSerde {
181 Str(String),
183 UnixLiteral {
186 unix_literal: PathBuf,
188 },
189}
190
191impl TryFrom<CfgAddrSerde> for CfgAddr {
192 type Error = general::AddrParseError;
193
194 fn try_from(value: CfgAddrSerde) -> Result<Self, Self::Error> {
195 use CfgAddrSerde as S;
196 match value {
197 S::Str(s) => s.parse(),
198 S::UnixLiteral { unix_literal } => {
199 Ok(CfgAddr::new_unix(CfgPath::new_literal(unix_literal)))
200 }
201 }
202 }
203}
204impl From<CfgAddr> for CfgAddrSerde {
205 fn from(value: CfgAddr) -> Self {
206 match value.try_to_string() {
207 Ok(s) => CfgAddrSerde::Str(s),
208 Err(unix_literal) => CfgAddrSerde::UnixLiteral {
209 unix_literal: unix_literal.clone(),
210 },
211 }
212 }
213}
214
215#[cfg(test)]
216mod test {
217 #![allow(clippy::bool_assert_comparison)]
219 #![allow(clippy::clone_on_copy)]
220 #![allow(clippy::dbg_macro)]
221 #![allow(clippy::mixed_attributes_style)]
222 #![allow(clippy::print_stderr)]
223 #![allow(clippy::print_stdout)]
224 #![allow(clippy::single_char_pattern)]
225 #![allow(clippy::unwrap_used)]
226 #![allow(clippy::unchecked_time_subtraction)]
227 #![allow(clippy::useless_vec)]
228 #![allow(clippy::needless_pass_by_value)]
229 #![allow(clippy::string_slice)] use super::*;
233 use assert_matches::assert_matches;
234 use std::path::PathBuf;
235
236 use crate::{CfgPathResolver, home};
237
238 #[test]
239 fn parse_inet_ok() {
240 fn check(s: &str) {
241 let resolv = CfgPathResolver::from_pairs([("FOO", "foo")]);
242 let a: general::SocketAddr = CfgAddr::from_str(s).unwrap().address(&resolv).unwrap();
243 assert_eq!(a, general::SocketAddr::from_str(s).unwrap());
244 }
245
246 check("127.0.0.1:9999");
247 check("inet:127.0.0.1:9999");
248 check("[2001:db8::413]:443");
249 check("inet:[2001:db8::413]:443");
250 }
251
252 #[test]
253 fn parse_inet_bad() {
254 assert_matches!(
255 CfgAddr::from_str("612"),
256 Err(general::AddrParseError::InvalidInetAddress(_))
257 );
258 assert_matches!(
259 CfgAddr::from_str("612unix:/home"),
260 Err(general::AddrParseError::InvalidInetAddress(_))
261 );
262 assert_matches!(
263 CfgAddr::from_str("127.0.0.1.1:99"),
264 Err(general::AddrParseError::InvalidInetAddress(_))
265 );
266 assert_matches!(
267 CfgAddr::from_str("inet:6"),
268 Err(general::AddrParseError::InvalidInetAddress(_))
269 );
270 assert_matches!(
271 CfgAddr::from_str("[[[[[]]]]]"),
272 Err(general::AddrParseError::InvalidInetAddress(_))
273 );
274 }
275
276 #[test]
277 fn parse_bad_schemas() {
278 assert_matches!(
279 CfgAddr::from_str("uranian:umbra"),
280 Err(general::AddrParseError::UnrecognizedSchema(_))
281 );
282 }
283
284 #[test]
285 #[cfg_attr(not(unix), expect(unused_variables))]
286 fn unix_literal() {
287 let resolv = CfgPathResolver::from_pairs([("USER_HOME", home().unwrap())]);
288 let pb = PathBuf::from("${USER_HOME}/.local/socket");
289 let a1 = CfgAddr::new_unix(CfgPath::new_literal(&pb));
290 let a2 = CfgAddr::from_str("unix-literal:${USER_HOME}/.local/socket").unwrap();
291 #[cfg(unix)]
292 {
293 assert_eq!(a1.address(&resolv).unwrap(), a2.address(&resolv).unwrap(),);
294 match a1.address(&resolv).unwrap() {
295 general::SocketAddr::Unix(socket_addr) => {
296 assert!(socket_addr.as_pathname() == Some(pb.as_ref()));
298 }
299 _ => panic!("Expected a unix domain socket address"),
300 }
301 }
302 #[cfg(not(unix))]
303 assert_matches!(
304 a1.address(&resolv),
305 Err(CfgAddrError::NoAfUnixSocketSupport(_))
306 );
307 }
308
309 #[cfg_attr(not(unix), expect(unused_variables))]
310 fn try_unix(addr: &str, want: &str, path_resolver: &CfgPathResolver) {
311 let p = CfgPath::new(want.to_string());
312 let expansion = p.path(path_resolver).unwrap();
313 let cfg_addr = CfgAddr::from_str(addr).unwrap();
314 assert_matches!(&cfg_addr.0, AddrInner::Unix(_));
315 #[cfg(unix)]
316 {
317 let gen_addr = cfg_addr.address(path_resolver).unwrap();
318 let expected_addr = unix::SocketAddr::from_pathname(expansion).unwrap();
319 assert_eq!(gen_addr, expected_addr.into());
320 }
321 #[cfg(not(unix))]
322 {
323 assert_matches!(
324 cfg_addr.address(path_resolver),
325 Err(CfgAddrError::NoAfUnixSocketSupport(_))
326 );
327 }
328 }
329
330 #[test]
331 fn unix_no_substitution() {
332 let resolver = CfgPathResolver::from_pairs([("FOO", "foo")]);
333 try_unix("unix:/home/mayor/.socket", "/home/mayor/.socket", &resolver);
334 }
335
336 #[test]
337 #[cfg(feature = "expand-paths")]
338 fn unix_substitution() {
339 let resolver = CfgPathResolver::from_pairs([("FOO", "foo")]);
340 try_unix("unix:${FOO}/socket", "${FOO}/socket", &resolver);
341 }
342
343 #[test]
344 fn serde() {
345 fn testcase_with_provided_addr(json: &str, addr: &CfgAddr) {
346 let a1: CfgAddr = serde_json::from_str(json).unwrap();
347 assert_eq!(&a1, addr);
348 let encoded = serde_json::to_string(&a1).unwrap();
349 let a2: CfgAddr = serde_json::from_str(&encoded).unwrap();
350 assert_eq!(&a2, addr);
351 }
352 fn testcase(json: &str, addr: &str) {
353 let addr = CfgAddr::from_str(addr).unwrap();
354 testcase_with_provided_addr(json, &addr);
355 }
356
357 testcase(r#" "inet:127.0.0.1:443" "#, "inet:127.0.0.1:443");
358 testcase(r#" "unix:${HOME}/socket" "#, "unix:${HOME}/socket");
359 testcase(
360 r#" "unix-literal:${HOME}/socket" "#,
361 "unix-literal:${HOME}/socket",
362 );
363 }
364}