quick_file_transfer/ssh/
remote_info.rs1use anyhow::bail;
2
3use crate::config::ssh::{SendSshArgs, TargetComponents};
4
5use std::{
6 net::{IpAddr, ToSocketAddrs},
7 path::Path,
8};
9
10#[derive(Debug, Clone, Copy)]
11pub enum Remote<'a> {
12 Ip(&'a str),
13 DnsHostname(IpAddr),
14 #[cfg(feature = "mdns")]
15 MdnsHostname(&'a str),
16}
17
18impl<'a> Remote<'a> {
19 pub fn new(host: &'a str) -> anyhow::Result<Self> {
20 tracing::trace!("Resolving remote: '{host}'");
21 if host.parse::<std::net::IpAddr>().is_ok() {
22 return Ok(Self::Ip(host));
23 }
24 #[cfg(feature = "mdns")]
25 if crate::ssh::mdns_util::is_mdns_hostname(host) {
26 return Ok(Self::MdnsHostname(host));
27 }
28
29 let addrs_iter = match (host, 0).to_socket_addrs() {
30 Ok(addrs_iter) => addrs_iter,
31 Err(e) => {
32 #[cfg(feature = "mdns")]
33 bail!("'{host}' was not recognized as an IP or a mDNS/DNS-SD hostname, attempt at resolving as a regular DNS hostname failed: {e}");
34 #[cfg(not(feature = "mdns"))]
35 bail!("'{host}' was not recognized as an IP, (install with mdns feature to resolve mDNS), attempt at resolving as a regular DNS hostname failed: {e}");
36 }
37 };
38
39 let mut ipv6_fallback: Option<IpAddr> = None;
40 for addr in addrs_iter {
41 if addr.ip().is_ipv4() {
42 return Ok(Self::DnsHostname(addr.ip()));
43 } else if ipv6_fallback.is_none() {
44 ipv6_fallback = Some(addr.ip());
45 }
46 }
47 if let Some(ipv6) = ipv6_fallback {
48 return Ok(Self::DnsHostname(ipv6));
49 }
50 unreachable!("Should be")
51 }
52
53 pub fn to_resolved_ip(
54 self,
55 #[cfg(feature = "mdns")] timeout_ms: u64,
56 ) -> anyhow::Result<IpAddr> {
57 match self {
58 Remote::Ip(ip) => Ok(ip.parse()?),
59 Remote::DnsHostname(hn) => Ok(hn),
60 #[cfg(feature = "mdns")]
61 Remote::MdnsHostname(hn) => {
62 let ip = super::mdns_util::get_remote_ip_from_mdns_hostname(
63 hn,
64 timeout_ms,
65 crate::config::misc::IpVersion::V4,
66 )?;
67 Ok(ip)
68 }
69 }
70 }
71}
72
73pub struct RemoteInfo<'a> {
74 pub user: &'a str,
75 pub ssh_port: u16,
76 pub resolved_ip: IpAddr,
77 pub destination: &'a Path,
78}
79
80impl<'a> RemoteInfo<'a> {
81 pub fn new(user: &'a str, ssh_port: u16, resolved_ip: IpAddr, destination: &'a Path) -> Self {
82 Self {
83 user,
84 ssh_port,
85 resolved_ip,
86 destination,
87 }
88 }
89
90 pub fn from_args(ssh_args: &'a SendSshArgs, components: &'a TargetComponents) -> Self {
91 let TargetComponents {
92 ref user,
93 ref host,
94 ref destination,
95 } = components;
96
97 let resolved_ip: IpAddr = Remote::new(host.as_str())
98 .unwrap()
99 .to_resolved_ip(
100 #[cfg(feature = "mdns")]
101 ssh_args.mdns_resolve_timeout_ms,
102 )
103 .unwrap();
104
105 Self::new(user, ssh_args.ssh_port, resolved_ip, destination)
106 }
107
108 pub fn ip(&self) -> IpAddr {
109 self.resolved_ip
110 }
111 pub fn user(&self) -> &str {
112 self.user
113 }
114 pub fn dest(&self) -> &Path {
115 self.destination
116 }
117 pub fn ssh_port(&self) -> u16 {
118 self.ssh_port
119 }
120}