azalea_protocol/
resolver.rs1use std::net::{IpAddr, SocketAddr};
4
5use async_recursion::async_recursion;
6use hickory_resolver::{
7 config::{ResolverConfig, ResolverOpts},
8 Name, TokioAsyncResolver,
9};
10use thiserror::Error;
11
12use crate::ServerAddress;
13
14#[derive(Error, Debug)]
15pub enum ResolverError {
16 #[error("No SRV record found")]
17 NoSrvRecord,
18 #[error("No IP found")]
19 NoIp,
20}
21
22#[must_use]
25#[async_recursion]
26pub async fn resolve_address(address: &ServerAddress) -> Result<SocketAddr, ResolverError> {
27 if let Ok(ip) = address.host.parse::<IpAddr>() {
29 return Ok(SocketAddr::new(ip, address.port));
30 }
31
32 let resolver = TokioAsyncResolver::tokio(ResolverConfig::cloudflare(), ResolverOpts::default());
36
37 let srv_redirect_result = resolver
39 .srv_lookup(format!("_minecraft._tcp.{}", address.host).as_str())
40 .await;
41
42 if let Ok(redirect_result) = srv_redirect_result {
45 let redirect_srv = redirect_result
46 .iter()
47 .next()
48 .ok_or(ResolverError::NoSrvRecord)?;
49 let redirect_address = ServerAddress {
50 host: redirect_srv.target().to_ascii(),
51 port: redirect_srv.port(),
52 };
53
54 if redirect_address.host == address.host {
55 let lookup_ip_result = resolver.lookup_ip(redirect_address.host).await;
56 let lookup_ip = lookup_ip_result.map_err(|_| ResolverError::NoIp)?;
57 return Ok(SocketAddr::new(
58 lookup_ip.iter().next().unwrap(),
59 redirect_address.port,
60 ));
61 }
62
63 return resolve_address(&redirect_address).await;
64 }
65
66 let name = Name::from_ascii(&address.host).map_err(|_| ResolverError::NoIp)?;
68 let lookup_ip_result = resolver.lookup_ip(name).await;
69 let lookup_ip = lookup_ip_result.map_err(|_| ResolverError::NoIp)?;
70
71 Ok(SocketAddr::new(
72 lookup_ip.iter().next().unwrap(),
73 address.port,
74 ))
75}