1use std::net::SocketAddr;
2use tokio::net::lookup_host;
3use tracing::trace;
4
5#[derive(Clone, Debug)]
6pub enum ConnectionArgs {
7 Quic {
9 hostname: String,
10 prefer_ipv6: bool,
11 validate_tls: bool,
12 },
13 Tcp {
15 hostname: String,
16 prefer_ipv6: bool,
17 },
18 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
36pub(crate) async fn resolve(
40 address: &str,
41 prefer_ipv6: bool,
42) -> Result<Vec<SocketAddr>, std::io::Error> {
43 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 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), }
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 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}