azalea_protocol/
resolve.rs1use std::{
4 net::{IpAddr, SocketAddr},
5 sync::LazyLock,
6};
7
8pub use hickory_resolver::ResolveError;
9use hickory_resolver::{Name, TokioResolver, name_server::TokioConnectionProvider};
10
11use crate::ServerAddress;
12
13#[deprecated(note = "Renamed to ResolveError")]
14pub type ResolverError = ResolveError;
15
16static RESOLVER: LazyLock<TokioResolver> = LazyLock::new(|| {
17 TokioResolver::builder(TokioConnectionProvider::default())
18 .unwrap()
19 .build()
20});
21
22pub async fn resolve_address(mut address: &ServerAddress) -> Result<SocketAddr, ResolveError> {
26 let redirect = resolve_srv_redirect(address).await;
27 if let Ok(redirect_target) = &redirect {
28 address = redirect_target;
29 }
30
31 resolve_ip_without_redirects(address).await
32}
33
34async fn resolve_ip_without_redirects(address: &ServerAddress) -> Result<SocketAddr, ResolveError> {
35 if let Ok(ip) = address.host.parse::<IpAddr>() {
36 return Ok(SocketAddr::new(ip, address.port));
38 }
39
40 let name = Name::from_ascii(&address.host)?;
41 let lookup_ip = RESOLVER.lookup_ip(name).await?;
42
43 let ip = lookup_ip
44 .iter()
45 .next()
46 .ok_or(hickory_resolver::ResolveError::from(
47 "No A/AAAA record found",
48 ))?;
49
50 Ok(SocketAddr::new(ip, address.port))
51}
52
53async fn resolve_srv_redirect(address: &ServerAddress) -> Result<ServerAddress, ResolveError> {
54 if address.port != 25565 {
55 return Err(ResolveError::from("Port must be 25565 to do a SRV lookup"));
56 }
57
58 let query = format!("_minecraft._tcp.{}", address.host);
59 let res = RESOLVER.srv_lookup(query).await?;
60
61 let srv = res
62 .iter()
63 .next()
64 .ok_or(ResolveError::from("No SRV record found"))?;
65 Ok(ServerAddress {
66 host: srv.target().to_ascii(),
67 port: srv.port(),
68 })
69}