1use std::{
2 borrow::Cow,
3 hash::{Hash, Hasher},
4 net::{IpAddr, Ipv4Addr, SocketAddr},
5 path::Path,
6 sync::Arc,
7};
8
9use derive_more::Debug;
10use rustls_pki_types::ServerName;
11
12use crate::TlsParameters;
13
14#[derive(Clone)]
15pub struct TargetName {
17 inner: MaybeResolvedTarget,
18}
19
20impl std::fmt::Debug for TargetName {
21 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22 write!(f, "{:?}", self.inner)
23 }
24}
25
26impl TargetName {
27 pub fn new_unix_path(path: impl AsRef<Path>) -> Result<Self, std::io::Error> {
29 #[cfg(unix)]
30 {
31 let path = ResolvedTarget::from(std::os::unix::net::SocketAddr::from_pathname(path)?);
32 Ok(Self {
33 inner: MaybeResolvedTarget::Resolved(path),
34 })
35 }
36 #[cfg(not(unix))]
37 {
38 Err(std::io::Error::new(
39 std::io::ErrorKind::Unsupported,
40 "Unix sockets are not supported on this platform",
41 ))
42 }
43 }
44
45 pub fn new_unix_domain(domain: impl AsRef<[u8]>) -> Result<Self, std::io::Error> {
47 #[cfg(any(target_os = "linux", target_os = "android"))]
48 {
49 use std::os::linux::net::SocketAddrExt;
50 let domain =
51 ResolvedTarget::from(std::os::unix::net::SocketAddr::from_abstract_name(domain)?);
52 Ok(Self {
53 inner: MaybeResolvedTarget::Resolved(domain),
54 })
55 }
56 #[cfg(not(any(target_os = "linux", target_os = "android")))]
57 {
58 Err(std::io::Error::new(
59 std::io::ErrorKind::Unsupported,
60 "Unix domain sockets are not supported on this platform",
61 ))
62 }
63 }
64
65 #[allow(private_bounds)]
67 pub fn new_tcp(host: impl TcpResolve) -> Self {
68 Self { inner: host.into() }
69 }
70
71 pub fn to_addrs_sync(&self) -> Result<Vec<ResolvedTarget>, std::io::Error> {
73 use std::net::ToSocketAddrs;
74 let mut result = Vec::new();
75 match &self.inner {
76 MaybeResolvedTarget::Resolved(addr) => {
77 return Ok(vec![addr.clone()]);
78 }
79 MaybeResolvedTarget::Unresolved(host, port, _interface) => {
80 let addrs = format!("{host}:{port}").to_socket_addrs()?;
81 result.extend(addrs.map(ResolvedTarget::SocketAddr));
82 }
83 }
84 Ok(result)
85 }
86
87 pub(crate) fn maybe_resolved(&self) -> &MaybeResolvedTarget {
88 &self.inner
89 }
90
91 pub(crate) fn maybe_resolved_mut(&mut self) -> &mut MaybeResolvedTarget {
92 &mut self.inner
93 }
94
95 pub fn is_tcp(&self) -> bool {
97 self.maybe_resolved().port().is_some()
98 }
99
100 pub fn port(&self) -> Option<u16> {
103 self.maybe_resolved().port()
104 }
105
106 pub fn try_set_port(&mut self, port: u16) -> Option<u16> {
109 self.maybe_resolved_mut().set_port(port)
110 }
111
112 pub fn path(&self) -> Option<&Path> {
115 self.maybe_resolved().path()
116 }
117
118 pub fn host(&self) -> Option<Cow<str>> {
123 self.maybe_resolved().host()
124 }
125
126 pub fn name(&self) -> Option<ServerName> {
130 self.maybe_resolved().name()
131 }
132
133 pub fn tcp(&self) -> Option<(Cow<str>, u16)> {
136 self.maybe_resolved().tcp()
137 }
138}
139
140#[derive(Clone)]
143pub struct Target {
144 inner: TargetInner,
145}
146
147impl std::fmt::Debug for Target {
148 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149 match &self.inner {
150 TargetInner::NoTls(target) => write!(f, "{target:?}"),
151 TargetInner::Tls(target, _) => write!(f, "{target:?} (TLS)"),
152 TargetInner::StartTls(target, _) => write!(f, "{target:?} (STARTTLS)"),
153 }
154 }
155}
156
157#[allow(private_bounds)]
158impl Target {
159 pub fn new(name: TargetName) -> Self {
160 Self {
161 inner: TargetInner::NoTls(name.inner),
162 }
163 }
164
165 pub fn new_tls(name: TargetName, params: TlsParameters) -> Self {
166 Self {
167 inner: TargetInner::Tls(name.inner, params.into()),
168 }
169 }
170
171 pub fn new_starttls(name: TargetName, params: TlsParameters) -> Self {
172 Self {
173 inner: TargetInner::StartTls(name.inner, params.into()),
174 }
175 }
176
177 pub fn new_resolved(target: ResolvedTarget) -> Self {
178 Self {
179 inner: TargetInner::NoTls(target.into()),
180 }
181 }
182
183 pub fn new_resolved_tls(target: ResolvedTarget, params: TlsParameters) -> Self {
184 Self {
185 inner: TargetInner::Tls(target.into(), params.into()),
186 }
187 }
188
189 pub fn new_resolved_starttls(target: ResolvedTarget, params: TlsParameters) -> Self {
190 Self {
191 inner: TargetInner::StartTls(target.into(), params.into()),
192 }
193 }
194
195 pub fn new_unix_path(path: impl AsRef<Path>) -> Result<Self, std::io::Error> {
197 #[cfg(unix)]
198 {
199 let path = ResolvedTarget::from(std::os::unix::net::SocketAddr::from_pathname(path)?);
200 Ok(Self {
201 inner: TargetInner::NoTls(path.into()),
202 })
203 }
204 #[cfg(not(unix))]
205 {
206 Err(std::io::Error::new(
207 std::io::ErrorKind::Unsupported,
208 "Unix sockets are not supported on this platform",
209 ))
210 }
211 }
212
213 pub fn new_unix_domain(domain: impl AsRef<[u8]>) -> Result<Self, std::io::Error> {
215 #[cfg(any(target_os = "linux", target_os = "android"))]
216 {
217 use std::os::linux::net::SocketAddrExt;
218 let domain =
219 ResolvedTarget::from(std::os::unix::net::SocketAddr::from_abstract_name(domain)?);
220 Ok(Self {
221 inner: TargetInner::NoTls(domain.into()),
222 })
223 }
224 #[cfg(not(any(target_os = "linux", target_os = "android")))]
225 {
226 Err(std::io::Error::new(
227 std::io::ErrorKind::Unsupported,
228 "Unix domain sockets are not supported on this platform",
229 ))
230 }
231 }
232
233 pub fn new_tcp(host: impl TcpResolve) -> Self {
235 Self {
236 inner: TargetInner::NoTls(host.into()),
237 }
238 }
239
240 pub fn new_tcp_tls(host: impl TcpResolve, params: TlsParameters) -> Self {
242 Self {
243 inner: TargetInner::Tls(host.into(), params.into()),
244 }
245 }
246
247 pub fn new_tcp_starttls(host: impl TcpResolve, params: TlsParameters) -> Self {
249 Self {
250 inner: TargetInner::StartTls(host.into(), params.into()),
251 }
252 }
253
254 pub fn try_set_tls(&mut self, params: TlsParameters) -> Option<Option<Arc<TlsParameters>>> {
255 if self.maybe_resolved().path().is_some() {
257 return None;
258 }
259
260 let params = params.into();
261
262 let no_target = TargetInner::NoTls(MaybeResolvedTarget::Resolved(
264 ResolvedTarget::SocketAddr(SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0)),
265 ));
266
267 match std::mem::replace(&mut self.inner, no_target) {
268 TargetInner::NoTls(target) => {
269 self.inner = TargetInner::Tls(target, params);
270 Some(None)
271 }
272 TargetInner::Tls(target, old_params) => {
273 self.inner = TargetInner::Tls(target, params);
274 Some(Some(old_params))
275 }
276 TargetInner::StartTls(target, old_params) => {
277 self.inner = TargetInner::StartTls(target, params);
278 Some(Some(old_params))
279 }
280 }
281 }
282
283 pub fn try_remove_tls(&mut self) -> Option<Arc<TlsParameters>> {
284 let no_target = TargetInner::NoTls(MaybeResolvedTarget::Resolved(
286 ResolvedTarget::SocketAddr(SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0)),
287 ));
288
289 match std::mem::replace(&mut self.inner, no_target) {
290 TargetInner::NoTls(target) => {
291 self.inner = TargetInner::NoTls(target);
292 None
293 }
294 TargetInner::Tls(target, old_params) => {
295 self.inner = TargetInner::NoTls(target);
296 Some(old_params)
297 }
298 TargetInner::StartTls(target, old_params) => {
299 self.inner = TargetInner::NoTls(target);
300 Some(old_params)
301 }
302 }
303 }
304
305 pub fn is_tcp(&self) -> bool {
307 self.maybe_resolved().port().is_some()
308 }
309
310 pub fn port(&self) -> Option<u16> {
313 self.maybe_resolved().port()
314 }
315
316 pub fn try_set_port(&mut self, port: u16) -> Option<u16> {
319 self.maybe_resolved_mut().set_port(port)
320 }
321
322 pub fn path(&self) -> Option<&Path> {
325 self.maybe_resolved().path()
326 }
327
328 pub fn host(&self) -> Option<Cow<str>> {
333 self.maybe_resolved().host()
334 }
335
336 pub fn name(&self) -> Option<ServerName> {
340 self.maybe_resolved().name()
341 }
342
343 pub fn tcp(&self) -> Option<(Cow<str>, u16)> {
346 self.maybe_resolved().tcp()
347 }
348
349 pub(crate) fn maybe_resolved(&self) -> &MaybeResolvedTarget {
350 match &self.inner {
351 TargetInner::NoTls(target) => target,
352 TargetInner::Tls(target, _) => target,
353 TargetInner::StartTls(target, _) => target,
354 }
355 }
356
357 pub(crate) fn maybe_resolved_mut(&mut self) -> &mut MaybeResolvedTarget {
358 match &mut self.inner {
359 TargetInner::NoTls(target) => target,
360 TargetInner::Tls(target, _) => target,
361 TargetInner::StartTls(target, _) => target,
362 }
363 }
364
365 pub(crate) fn is_starttls(&self) -> bool {
366 matches!(self.inner, TargetInner::StartTls(_, _))
367 }
368
369 pub(crate) fn maybe_ssl(&self) -> Option<&TlsParameters> {
370 match &self.inner {
371 TargetInner::NoTls(_) => None,
372 TargetInner::Tls(_, params) => Some(params),
373 TargetInner::StartTls(_, params) => Some(params),
374 }
375 }
376}
377
378#[derive(Clone, derive_more::From)]
379pub(crate) enum MaybeResolvedTarget {
380 Resolved(ResolvedTarget),
381 Unresolved(Cow<'static, str>, u16, Option<Cow<'static, str>>),
382}
383
384impl std::fmt::Debug for MaybeResolvedTarget {
385 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
386 match self {
387 MaybeResolvedTarget::Resolved(ResolvedTarget::SocketAddr(addr)) => {
388 if let SocketAddr::V6(addr) = addr {
389 if addr.scope_id() != 0 {
390 write!(f, "[{}%{}]:{}", addr.ip(), addr.scope_id(), addr.port())
391 } else {
392 write!(f, "[{}]:{}", addr.ip(), addr.port())
393 }
394 } else {
395 write!(f, "{}:{}", addr.ip(), addr.port())
396 }
397 }
398 #[cfg(unix)]
399 MaybeResolvedTarget::Resolved(ResolvedTarget::UnixSocketAddr(addr)) => {
400 if let Some(path) = addr.as_pathname() {
401 return write!(f, "{}", path.to_string_lossy());
402 } else {
403 #[cfg(any(target_os = "linux", target_os = "android"))]
404 {
405 use std::os::linux::net::SocketAddrExt;
406 if let Some(name) = addr.as_abstract_name() {
407 return write!(f, "@{}", String::from_utf8_lossy(name));
408 }
409 }
410 }
411 Ok(())
412 }
413 MaybeResolvedTarget::Unresolved(host, port, interface) => {
414 write!(f, "{host}:{port}")?;
415 if let Some(interface) = interface {
416 write!(f, "%{interface}")?;
417 }
418 Ok(())
419 }
420 }
421 }
422}
423
424impl MaybeResolvedTarget {
425 fn name(&self) -> Option<ServerName> {
426 match self {
427 MaybeResolvedTarget::Resolved(ResolvedTarget::SocketAddr(addr)) => {
428 Some(ServerName::IpAddress(addr.ip().into()))
429 }
430 MaybeResolvedTarget::Unresolved(host, _, _) => {
431 Some(ServerName::DnsName(host.to_string().try_into().ok()?))
432 }
433 #[cfg(unix)]
434 _ => None,
435 }
436 }
437
438 fn tcp(&self) -> Option<(Cow<str>, u16)> {
439 match self {
440 MaybeResolvedTarget::Resolved(ResolvedTarget::SocketAddr(addr)) => {
441 Some((Cow::Owned(addr.ip().to_string()), addr.port()))
442 }
443 MaybeResolvedTarget::Unresolved(host, port, _) => Some((Cow::Borrowed(host), *port)),
444 #[cfg(unix)]
445 _ => None,
446 }
447 }
448
449 fn path(&self) -> Option<&Path> {
450 match self {
451 #[cfg(unix)]
452 MaybeResolvedTarget::Resolved(ResolvedTarget::UnixSocketAddr(addr)) => {
453 addr.as_pathname()
454 }
455 _ => None,
456 }
457 }
458
459 fn host(&self) -> Option<Cow<str>> {
460 match self {
461 MaybeResolvedTarget::Resolved(ResolvedTarget::SocketAddr(addr)) => {
462 Some(Cow::Owned(addr.ip().to_string()))
463 }
464 MaybeResolvedTarget::Unresolved(host, _, _) => Some(Cow::Borrowed(host)),
465 #[cfg(unix)]
466 _ => None,
467 }
468 }
469
470 fn port(&self) -> Option<u16> {
471 match self {
472 MaybeResolvedTarget::Resolved(ResolvedTarget::SocketAddr(addr)) => Some(addr.port()),
473 MaybeResolvedTarget::Unresolved(_, port, _) => Some(*port),
474 #[cfg(unix)]
475 _ => None,
476 }
477 }
478
479 fn set_port(&mut self, new_port: u16) -> Option<u16> {
480 match self {
481 MaybeResolvedTarget::Resolved(ResolvedTarget::SocketAddr(addr)) => {
482 let old_port = addr.port();
483 addr.set_port(new_port);
484 Some(old_port)
485 }
486 MaybeResolvedTarget::Unresolved(_, port, _) => {
487 let old_port = *port;
488 *port = new_port;
489 Some(old_port)
490 }
491 #[cfg(unix)]
492 _ => None,
493 }
494 }
495}
496
497#[derive(Clone, Debug)]
499enum TargetInner {
500 NoTls(MaybeResolvedTarget),
501 Tls(MaybeResolvedTarget, Arc<TlsParameters>),
502 StartTls(MaybeResolvedTarget, Arc<TlsParameters>),
503}
504
505#[derive(Clone, Debug, derive_more::From, derive_more::TryFrom)]
506#[from(forward)]
508pub enum ResolvedTarget {
509 SocketAddr(std::net::SocketAddr),
510 #[cfg(unix)]
511 UnixSocketAddr(std::os::unix::net::SocketAddr),
512}
513
514#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
518enum ResolvedTargetInner<'a> {
519 SocketAddr(std::net::SocketAddr),
520 #[cfg(unix)]
521 UnixSocketPath(&'a std::path::Path),
522 #[cfg(any(target_os = "linux", target_os = "android"))]
523 UnixSocketAbstract(&'a [u8]),
524 #[allow(dead_code)]
527 Phantom(std::marker::PhantomData<&'a ()>),
528}
529
530#[cfg(unix)]
531impl TryFrom<std::path::PathBuf> for ResolvedTarget {
532 type Error = std::io::Error;
533
534 fn try_from(value: std::path::PathBuf) -> Result<Self, Self::Error> {
535 Ok(ResolvedTarget::UnixSocketAddr(
536 std::os::unix::net::SocketAddr::from_pathname(value)?,
537 ))
538 }
539}
540
541impl Eq for ResolvedTarget {}
542
543impl PartialEq for ResolvedTarget {
544 fn eq(&self, other: &Self) -> bool {
545 self.inner() == other.inner()
546 }
547}
548
549impl Hash for ResolvedTarget {
550 fn hash<H: Hasher>(&self, state: &mut H) {
551 self.inner().hash(state);
552 }
553}
554
555impl PartialOrd for ResolvedTarget {
556 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
557 self.inner().partial_cmp(&other.inner())
558 }
559}
560
561impl ResolvedTarget {
562 pub fn tcp(&self) -> Option<SocketAddr> {
563 match self {
564 ResolvedTarget::SocketAddr(addr) => Some(*addr),
565 _ => None,
566 }
567 }
568
569 pub fn is_tcp(&self) -> bool {
570 self.tcp().is_some()
571 }
572
573 pub fn transport(&self) -> Transport {
574 match self {
575 ResolvedTarget::SocketAddr(_) => Transport::Tcp,
576 #[cfg(unix)]
577 ResolvedTarget::UnixSocketAddr(_) => Transport::Unix,
578 }
579 }
580
581 #[allow(unreachable_code)]
583 fn inner(&self) -> ResolvedTargetInner {
584 match self {
585 ResolvedTarget::SocketAddr(addr) => ResolvedTargetInner::SocketAddr(*addr),
586 #[cfg(unix)]
587 ResolvedTarget::UnixSocketAddr(addr) => {
588 if let Some(path) = addr.as_pathname() {
589 return ResolvedTargetInner::UnixSocketPath(path);
590 } else {
591 #[cfg(any(target_os = "linux", target_os = "android"))]
592 {
593 use std::os::linux::net::SocketAddrExt;
594 return ResolvedTargetInner::UnixSocketAbstract(
595 addr.as_abstract_name().expect("abstract socket address"),
596 );
597 }
598 }
599 unreachable!()
600 }
601 }
602 }
603}
604
605pub trait LocalAddress {
607 fn local_address(&self) -> std::io::Result<ResolvedTarget>;
608}
609
610pub trait RemoteAddress {
612 fn remote_address(&self) -> std::io::Result<ResolvedTarget>;
613}
614
615pub trait PeerCred {
616 #[cfg(all(unix, feature = "tokio"))]
617 fn peer_cred(&self) -> std::io::Result<tokio::net::unix::UCred>;
618}
619
620#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
622pub enum Transport {
623 Tcp,
624 Unix,
625}
626
627pub trait StreamMetadata: LocalAddress + RemoteAddress + PeerCred + Send {
629 fn transport(&self) -> Transport;
630}
631
632pub(crate) trait TcpResolve {
633 fn into(self) -> MaybeResolvedTarget;
634}
635
636impl<S: AsRef<str>> TcpResolve for (S, u16) {
637 fn into(self) -> MaybeResolvedTarget {
638 if let Ok(addr) = self.0.as_ref().parse::<IpAddr>() {
639 MaybeResolvedTarget::Resolved(ResolvedTarget::SocketAddr(SocketAddr::new(addr, self.1)))
640 } else {
641 MaybeResolvedTarget::Unresolved(Cow::Owned(self.0.as_ref().to_owned()), self.1, None)
642 }
643 }
644}
645
646impl TcpResolve for SocketAddr {
647 fn into(self) -> MaybeResolvedTarget {
648 MaybeResolvedTarget::Resolved(ResolvedTarget::SocketAddr(self))
649 }
650}
651
652#[cfg(test)]
653mod tests {
654 use std::net::SocketAddrV6;
655
656 use super::*;
657
658 #[test]
659 fn test_target() {
660 let target = Target::new_tcp(("localhost", 5432));
661 assert_eq!(
662 target.name(),
663 Some(ServerName::DnsName("localhost".try_into().unwrap()))
664 );
665 }
666
667 #[test]
668 fn test_target_name() {
669 let target = TargetName::new_tcp(("localhost", 5432));
670 assert_eq!(format!("{target:?}"), "localhost:5432");
671
672 let target = TargetName::new_tcp(("127.0.0.1", 5432));
673 assert_eq!(format!("{target:?}"), "127.0.0.1:5432");
674
675 let target = TargetName::new_tcp(("::1", 5432));
676 assert_eq!(format!("{target:?}"), "[::1]:5432");
677
678 let target = TargetName::new_tcp(SocketAddr::V6(SocketAddrV6::new(
679 "fe80::1ff:fe23:4567:890a".parse().unwrap(),
680 5432,
681 0,
682 2,
683 )));
684 assert_eq!(format!("{target:?}"), "[fe80::1ff:fe23:4567:890a%2]:5432");
685
686 #[cfg(unix)]
687 {
688 let target = TargetName::new_unix_path("/tmp/test.sock").unwrap();
689 assert_eq!(format!("{target:?}"), "/tmp/test.sock");
690 }
691
692 #[cfg(any(target_os = "linux", target_os = "android"))]
693 {
694 let target = TargetName::new_unix_domain("test").unwrap();
695 assert_eq!(format!("{target:?}"), "@test");
696 }
697 }
698
699 #[test]
700 fn test_target_debug() {
701 let target = Target::new_tcp(("localhost", 5432));
702 assert_eq!(format!("{target:?}"), "localhost:5432");
703
704 let target = Target::new_tcp_tls(("localhost", 5432), TlsParameters::default());
705 assert_eq!(format!("{target:?}"), "localhost:5432 (TLS)");
706
707 let target = Target::new_tcp_starttls(("localhost", 5432), TlsParameters::default());
708 assert_eq!(format!("{target:?}"), "localhost:5432 (STARTTLS)");
709
710 let target = Target::new_tcp(("127.0.0.1", 5432));
711 assert_eq!(format!("{target:?}"), "127.0.0.1:5432");
712
713 let target = Target::new_tcp(("::1", 5432));
714 assert_eq!(format!("{target:?}"), "[::1]:5432");
715
716 #[cfg(unix)]
717 {
718 let target = Target::new_unix_path("/tmp/test.sock").unwrap();
719 assert_eq!(format!("{target:?}"), "/tmp/test.sock");
720 }
721
722 #[cfg(any(target_os = "linux", target_os = "android"))]
723 {
724 let target = Target::new_unix_domain("test").unwrap();
725 assert_eq!(format!("{target:?}"), "@test");
726 }
727 }
728}