azalea_protocol/
resolve.rs1use std::{
4 net::{IpAddr, SocketAddr},
5 sync::LazyLock,
6};
7
8pub use hickory_resolver::net::NetError as ResolveError;
9use hickory_resolver::{
10 Resolver, TokioResolver,
11 config::{GOOGLE, ResolverConfig},
12 net::runtime::TokioRuntimeProvider,
13 proto::rr::{Name, RData},
14};
15use tracing::warn;
16
17use crate::address::ServerAddr;
18
19#[doc(hidden)]
20#[deprecated(note = "Renamed to ResolveError")]
21pub type ResolverError = ResolveError;
22
23static RESOLVER: LazyLock<TokioResolver> = LazyLock::new(|| {
24 Resolver::builder_tokio()
25 .unwrap_or_else(|_| {
26 warn!("System DNS resolver unavailable; falling back to Google DNS.");
27
28 Resolver::builder_with_config(
29 ResolverConfig::udp_and_tcp(&GOOGLE),
30 TokioRuntimeProvider::new(),
31 )
32 })
33 .build()
34 .unwrap()
35});
36
37pub async fn resolve_address(mut address: &ServerAddr) -> Result<SocketAddr, ResolveError> {
41 let redirect = resolve_srv_redirect(address).await;
42 if let Ok(redirect_target) = &redirect {
43 address = redirect_target;
44 }
45
46 resolve_ip_without_redirects(address).await
47}
48
49async fn resolve_ip_without_redirects(address: &ServerAddr) -> Result<SocketAddr, ResolveError> {
50 if let Ok(ip) = address.host.parse::<IpAddr>() {
51 return Ok(SocketAddr::new(ip, address.port));
53 }
54
55 let name = Name::from_ascii(&address.host)?;
56 let lookup_ip = RESOLVER.lookup_ip(name).await?;
57
58 let ip = lookup_ip
59 .iter()
60 .next()
61 .ok_or(ResolveError::from("No A/AAAA record found"))?;
62
63 Ok(SocketAddr::new(ip, address.port))
64}
65
66async fn resolve_srv_redirect(address: &ServerAddr) -> Result<ServerAddr, ResolveError> {
67 if address.port != 25565 {
68 return Err(ResolveError::from("Port must be 25565 to do a SRV lookup"));
69 }
70
71 let query = format!("_minecraft._tcp.{}", address.host);
72 let res = RESOLVER.srv_lookup(query).await?;
73
74 let srv = res
75 .answers()
76 .first()
77 .ok_or(ResolveError::from("No SRV record found"))?;
78 let RData::SRV(srv) = &srv.data else {
79 return Err(ResolveError::from(
80 "Record returned from SRV lookup wasn't SRV",
81 ));
82 };
83
84 Ok(ServerAddr {
85 host: srv.target.to_ascii(),
86 port: srv.port,
87 })
88}