veloren_client/
addr.rs

1use std::net::SocketAddr;
2use tokio::net::lookup_host;
3use tracing::trace;
4
5#[derive(Clone, Debug)]
6pub enum ConnectionArgs {
7    ///hostname: (hostname|ip):[<port>]
8    Quic {
9        hostname: String,
10        prefer_ipv6: bool,
11        validate_tls: bool,
12    },
13    ///hostname: (hostname|ip):[<port>]
14    Tcp {
15        hostname: String,
16        prefer_ipv6: bool,
17    },
18    /// SRV lookup
19    ///
20    /// SRV lookups can not contain a port, but will be able to connect to
21    /// configured port automatically. If a connection with a port is given,
22    /// this will gracefully fall back to TCP.
23    Srv {
24        hostname: String,
25        prefer_ipv6: bool,
26        validate_tls: bool,
27        use_quic: bool,
28    },
29    Mpsc(u64),
30}
31
32impl ConnectionArgs {
33    const DEFAULT_PORT: u16 = 14004;
34}
35
36/// Parse ip address or resolves hostname.
37/// Note: If you use an ipv6 address, the number after the last
38/// colon will be used as the port unless you use [] around the address.
39pub(crate) async fn resolve(
40    address: &str,
41    prefer_ipv6: bool,
42) -> Result<Vec<SocketAddr>, std::io::Error> {
43    // `lookup_host` will internally try to parse it as a SocketAddr
44    // 1. Assume it's a hostname + port
45    match lookup_host(address).await {
46        Ok(s) => {
47            trace!("Host lookup succeeded");
48            Ok(sort_ipv6(s, prefer_ipv6))
49        },
50        Err(e) => {
51            // 2. Assume its a hostname without port
52            match lookup_host((address, ConnectionArgs::DEFAULT_PORT)).await {
53                Ok(s) => {
54                    trace!("Host lookup without ports succeeded");
55                    Ok(sort_ipv6(s, prefer_ipv6))
56                },
57                Err(_) => Err(e), // Todo: evaluate returning both errors
58            }
59        },
60    }
61}
62
63pub(crate) async fn try_connect<F>(
64    network: &network::Network,
65    address: &str,
66    override_port: Option<u16>,
67    prefer_ipv6: bool,
68    f: F,
69) -> Result<network::Participant, crate::error::Error>
70where
71    F: Fn(SocketAddr) -> network::ConnectAddr,
72{
73    use crate::error::Error;
74    let mut participant = None;
75    for mut addr in resolve(address, prefer_ipv6)
76        .await
77        .map_err(Error::HostnameLookupFailed)?
78    {
79        // Override the port if one was passed. Used for SRV lookups which get port info
80        // out-of-band
81        if let Some(port) = override_port {
82            addr.set_port(port);
83        }
84        match network.connect(f(addr)).await {
85            Ok(p) => {
86                participant = Some(Ok(p));
87                break;
88            },
89            Err(e) => participant = Some(Err(Error::NetworkErr(e))),
90        }
91    }
92    participant.unwrap_or_else(|| Err(Error::Other("No Ip Addr provided".to_string())))
93}
94
95fn sort_ipv6(s: impl Iterator<Item = SocketAddr>, prefer_ipv6: bool) -> Vec<SocketAddr> {
96    let (mut first_addrs, mut second_addrs) =
97        s.partition::<Vec<_>, _>(|a| a.is_ipv6() == prefer_ipv6);
98    Iterator::chain(first_addrs.drain(..), second_addrs.drain(..)).collect::<Vec<_>>()
99}
100
101#[cfg(test)]
102mod tests {
103    use super::*;
104    use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
105
106    #[tokio::test]
107    async fn resolve_localhost() {
108        let args = resolve("localhost", false).await.expect("resolve failed");
109        assert!(args.len() == 1 || args.len() == 2);
110        assert_eq!(args[0].ip(), IpAddr::V4(Ipv4Addr::LOCALHOST));
111        assert_eq!(args[0].port(), 14004);
112
113        let args = resolve("localhost:666", false)
114            .await
115            .expect("resolve failed");
116        assert!(args.len() == 1 || args.len() == 2);
117        assert_eq!(args[0].port(), 666);
118    }
119
120    #[tokio::test]
121    async fn resolve_ipv6() {
122        let args = resolve("localhost", true).await.expect("resolve failed");
123        assert!(args.len() == 1 || args.len() == 2);
124        assert_eq!(args[0].ip(), Ipv6Addr::LOCALHOST);
125        assert_eq!(args[0].port(), 14004);
126    }
127
128    #[tokio::test]
129    async fn tresolve() {
130        let args = resolve("google.com", false).await.expect("resolve failed");
131        assert!(!args.is_empty());
132        assert_eq!(args[0].port(), 14004);
133
134        let args = resolve("127.0.0.1", false).await.expect("resolve failed");
135        assert_eq!(args.len(), 1);
136        assert_eq!(args[0].port(), 14004);
137        assert_eq!(args[0].ip(), IpAddr::V4(Ipv4Addr::LOCALHOST));
138
139        let args = resolve("55.66.77.88", false).await.expect("resolve failed");
140        assert_eq!(args.len(), 1);
141        assert_eq!(args[0].port(), 14004);
142        assert_eq!(args[0].ip(), IpAddr::V4(Ipv4Addr::new(55, 66, 77, 88)));
143
144        let args = resolve("127.0.0.1:776", false)
145            .await
146            .expect("resolve failed");
147        assert_eq!(args.len(), 1);
148        assert_eq!(args[0].port(), 776);
149        assert_eq!(args[0].ip(), IpAddr::V4(Ipv4Addr::LOCALHOST));
150    }
151}