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