1use std::{fmt::Debug, net::IpAddr};
2
3use futures::{FutureExt, TryFutureExt};
4use hickory_resolver::{
5 net::{DnsError, NetError},
6 proto::rr::{RData, rdata::SRV},
7};
8use ipaddress::IPAddress;
9use ruma::ServerName;
10use tuwunel_core::{
11 Err, Result, debug, debug_info, debug_warn, err, error, format_array_string, implement,
12 trace, utils::string::to_small_string,
13};
14
15use super::{
16 DestString, FedDest,
17 cache::{CachedDest, CachedOverride, MAX_IPS},
18 fed::{HostString, PortString, add_port_to_hostname, get_ip_with_port},
19};
20
21#[derive(Clone, Debug)]
22pub(crate) struct ActualDest {
23 pub(crate) dest: FedDest,
24 pub(crate) host: DestString,
25}
26
27impl ActualDest {
28 #[inline]
29 pub(crate) fn to_string(&self) -> DestString { self.dest.https_string() }
30}
31
32#[implement(super::Service)]
33#[tracing::instrument(skip_all, level = "debug", name = "resolve")]
34pub(crate) async fn get_actual_dest(&self, server_name: &ServerName) -> Result<ActualDest> {
35 let (CachedDest { dest, host, .. }, _cached) = self.lookup_actual_dest(server_name).await?;
36
37 Ok(ActualDest { dest, host })
38}
39
40#[implement(super::Service)]
41pub(crate) async fn lookup_actual_dest(
42 &self,
43 server_name: &ServerName,
44) -> Result<(CachedDest, bool)> {
45 if let Ok(result) = self.cache.get_destination(server_name).await {
46 return Ok((result, true));
47 }
48
49 let _dedup = self.resolving.lock(server_name).await;
50 if let Ok(result) = self.cache.get_destination(server_name).await {
51 return Ok((result, true));
52 }
53
54 self.resolve_actual_dest(server_name, true)
55 .inspect_ok(|result| self.cache.set_destination(server_name, result))
56 .map_ok(|result| (result, false))
57 .boxed()
58 .await
59}
60
61#[implement(super::Service)]
66#[tracing::instrument(name = "actual", level = "debug", skip(self, cache))]
67pub async fn resolve_actual_dest(&self, dest: &ServerName, cache: bool) -> Result<CachedDest> {
68 self.validate_dest(dest)?;
69 let mut host: DestString = dest.as_str().into();
70 let actual_dest = self.actual_dest(dest, cache, &mut host).await?;
71 let actual_host = Self::dest_host(&host);
72
73 debug!("Actual destination: {actual_dest:?} hostname: {actual_host:?}");
74 Ok(CachedDest {
75 dest: actual_dest,
76 host: actual_host.uri_string(),
77 expire: CachedDest::default_expire(),
78 })
79}
80
81#[implement(super::Service)]
82fn dest_host(host: &DestString) -> FedDest {
83 host.parse()
85 .map(FedDest::Literal)
86 .or_else(|_| {
87 host.parse().map(|addr: IpAddr| {
88 FedDest::Named(addr.to_string().into(), FedDest::default_port())
89 })
90 })
91 .unwrap_or_else(|_| {
92 host.find(':').map_or_else(
93 || FedDest::Named(host.as_str().into(), FedDest::default_port()),
94 |pos| {
95 let (host, port) = host.split_at(pos);
96
97 FedDest::Named(
98 host.into(),
99 port.try_into()
100 .unwrap_or_else(|_| FedDest::default_port()),
101 )
102 },
103 )
104 })
105}
106
107#[implement(super::Service)]
108async fn actual_dest(
109 &self,
110 dest: &ServerName,
111 cache: bool,
112 host: &mut DestString,
113) -> Result<FedDest> {
114 match get_ip_with_port(dest.as_str()) {
115 | Some(host_port) => Self::actual_dest_1(host_port),
116 | None if let Some(pos) = dest.as_str().find(':') =>
117 self.actual_dest_2(dest, cache, pos).await,
118 | None => {
119 self.maybe_query_and_cache(dest.as_str(), 8448, true)
120 .await?;
121 self.services.server.check_running()?;
122 match self.request_well_known(dest.as_str()).await? {
123 | Some(delegated) => self.actual_dest_3(host, cache, &delegated).await,
124 | _ => match self.query_srv_record(dest.as_str()).await? {
125 | Some(overrider) => self.actual_dest_4(host, cache, overrider).await,
126 | _ => self.actual_dest_5(dest, cache).await,
127 },
128 }
129 },
130 }
131}
132
133#[implement(super::Service)]
134fn actual_dest_1(host_port: FedDest) -> Result<FedDest> {
135 debug!("1: IP literal with provided or default port");
136 Ok(host_port)
137}
138
139#[implement(super::Service)]
140async fn actual_dest_2(&self, dest: &ServerName, cache: bool, pos: usize) -> Result<FedDest> {
141 debug!("2: Hostname with included port");
142 let (host, port) = dest.as_str().split_at(pos);
143 let port_num = port
144 .trim_start_matches(':')
145 .parse::<u16>()
146 .unwrap_or(8448);
147
148 self.maybe_query_and_cache(host, port_num, cache)
149 .await?;
150
151 let port = port
152 .try_into()
153 .unwrap_or_else(|_| FedDest::default_port());
154
155 Ok(FedDest::Named(host.into(), port))
156}
157
158#[implement(super::Service)]
159async fn actual_dest_3(
160 &self,
161 host: &mut DestString,
162 cache: bool,
163 delegated: &str,
164) -> Result<FedDest> {
165 debug!("3: A .well-known file is available");
166 *host = add_port_to_hostname(delegated).uri_string();
167 match get_ip_with_port(delegated) {
168 | Some(host_and_port) => Self::actual_dest_3_1(host_and_port),
169 | None =>
170 if let Some(pos) = delegated.find(':') {
171 self.actual_dest_3_2(cache, delegated, pos).await
172 } else {
173 trace!("Delegated hostname has no port in this branch");
174 match self.query_srv_record(delegated).await? {
175 | Some(overrider) =>
176 self.actual_dest_3_3(cache, delegated, overrider)
177 .await,
178 | _ => self.actual_dest_3_4(cache, delegated).await,
179 }
180 },
181 }
182}
183
184#[implement(super::Service)]
185fn actual_dest_3_1(host_and_port: FedDest) -> Result<FedDest> {
186 debug!("3.1: IP literal in .well-known file");
187 Ok(host_and_port)
188}
189
190#[implement(super::Service)]
191async fn actual_dest_3_2(&self, cache: bool, delegated: &str, pos: usize) -> Result<FedDest> {
192 debug!("3.2: Hostname with port in .well-known file");
193 let (host, port) = delegated.split_at(pos);
194 let port_num = port
195 .trim_start_matches(':')
196 .parse::<u16>()
197 .unwrap_or(8448);
198
199 self.maybe_query_and_cache(host, port_num, cache)
200 .await?;
201
202 let port = port
203 .try_into()
204 .unwrap_or_else(|_| FedDest::default_port());
205
206 Ok(FedDest::Named(host.into(), port))
207}
208
209#[implement(super::Service)]
210async fn actual_dest_3_3(
211 &self,
212 cache: bool,
213 delegated: &str,
214 overrider: FedDest,
215) -> Result<FedDest> {
216 debug!("3.3: SRV lookup successful");
217 let force_port = overrider.port();
218 self.maybe_query_and_cache_override(
219 delegated,
220 &overrider.hostname(),
221 force_port.unwrap_or(8448),
222 cache,
223 )
224 .await?;
225
226 if let Some(port) = force_port {
227 let port: PortString = format_array_string!(":{port}");
228
229 return Ok(FedDest::Named(delegated.into(), port));
230 }
231
232 Ok(add_port_to_hostname(delegated))
233}
234
235#[implement(super::Service)]
236async fn actual_dest_3_4(&self, cache: bool, delegated: &str) -> Result<FedDest> {
237 debug!("3.4: No SRV records, just use the hostname from .well-known");
238 self.maybe_query_and_cache(delegated, 8448, cache)
239 .await?;
240
241 Ok(add_port_to_hostname(delegated))
242}
243
244#[implement(super::Service)]
245async fn actual_dest_4(&self, host: &str, cache: bool, overrider: FedDest) -> Result<FedDest> {
246 debug!("4: No .well-known; SRV record found");
247 let force_port = overrider.port();
248 self.maybe_query_and_cache_override(
249 host,
250 &overrider.hostname(),
251 force_port.unwrap_or(8448),
252 cache,
253 )
254 .await?;
255
256 if let Some(port) = force_port {
257 let port: PortString = format_array_string!(":{port}");
258
259 return Ok(FedDest::Named(host.into(), port));
260 }
261
262 Ok(add_port_to_hostname(host))
263}
264
265#[implement(super::Service)]
266async fn actual_dest_5(&self, dest: &ServerName, cache: bool) -> Result<FedDest> {
267 debug!("5: No SRV record found");
268 self.maybe_query_and_cache(dest.as_str(), 8448, cache)
269 .await?;
270
271 Ok(add_port_to_hostname(dest.as_str()))
272}
273
274#[implement(super::Service)]
275#[inline]
276async fn maybe_query_and_cache(&self, hostname: &str, port: u16, cache: bool) -> Result {
277 self.maybe_query_and_cache_override(hostname, hostname, port, cache)
278 .await
279}
280
281#[implement(super::Service)]
282#[inline]
283async fn maybe_query_and_cache_override(
284 &self,
285 untername: &str,
286 hostname: &str,
287 port: u16,
288 cache: bool,
289) -> Result {
290 if !cache {
291 return Ok(());
292 }
293
294 if self.cache.has_override(untername).await {
295 return Ok(());
296 }
297
298 self.query_and_cache_override(untername, hostname, port)
299 .await
300}
301
302#[implement(super::Service)]
303#[tracing::instrument(name = "ip", level = "debug", skip(self))]
304async fn query_and_cache_override(
305 &self,
306 untername: &'_ str,
307 hostname: &'_ str,
308 port: u16,
309) -> Result {
310 self.services.server.check_running()?;
311
312 debug!("querying IP for {untername:?} ({hostname:?}:{port})");
313 match self
314 .resolver
315 .resolver
316 .lookup_ip(hostname.to_owned())
317 .await
318 {
319 | Err(e) => Self::handle_resolve_error(&e, hostname),
320 | Ok(override_ip) => {
321 self.cache
322 .set_override(untername, &CachedOverride {
323 ips: override_ip.iter().take(MAX_IPS).collect(),
324 port,
325 expire: CachedOverride::default_expire(),
326 overriding: (hostname != untername)
327 .then_some(hostname.into())
328 .inspect(|_| debug_info!("{untername:?} overridden by {hostname:?}")),
329 });
330
331 Ok(())
332 },
333 }
334}
335
336#[implement(super::Service)]
337#[tracing::instrument(name = "srv", level = "debug", skip(self))]
338async fn query_srv_record(&self, hostname: &'_ str) -> Result<Option<FedDest>> {
339 let hostnames =
340 [format!("_matrix-fed._tcp.{hostname}."), format!("_matrix._tcp.{hostname}.")];
341
342 for hostname in hostnames {
343 self.services.server.check_running()?;
344
345 debug!("querying SRV for {hostname:?}");
346 let hostname = hostname.trim_end_matches('.');
347 match self.resolver.resolver.srv_lookup(hostname).await {
348 | Err(e) => Self::handle_resolve_error(&e, hostname)?,
349 | Ok(result) => {
350 let srv = result
351 .answers()
352 .iter()
353 .find_map(|r| match &r.data {
354 | RData::SRV(srv) => Some(srv),
355 | _ => None,
356 });
357
358 return Ok(srv.map(Self::srv_dest));
359 },
360 }
361 }
362
363 Ok(None)
364}
365
366#[implement(super::Service)]
367fn srv_dest(srv: &SRV) -> FedDest {
368 let host: HostString = to_small_string(&srv.target);
369 let port: PortString = format_array_string!(":{}", srv.port);
370
371 FedDest::Named(host.trim_end_matches('.').into(), port)
372}
373
374#[implement(super::Service)]
375fn handle_resolve_error(e: &NetError, host: &'_ str) -> Result {
376 match e {
381 | NetError::Dns(DnsError::NoRecordsFound(_)) => {
382 debug!(%host, "No DNS records found: {e}");
384 Ok(())
385 },
386 | NetError::Dns(_) => {
387 debug_warn!(%host, "DNS response error: {e}");
388 Ok(())
389 },
390 | NetError::Timeout => Err!(warn!(%host, "DNS {e}")),
391 | NetError::NoConnections => {
392 error!(
393 "Your DNS server is overloaded and has ran out of connections. It is strongly \
394 recommended you remediate this issue to ensure proper federation connectivity."
395 );
396
397 Err!(error!(%host, "DNS error: {e}"))
398 },
399 | _ => Err!(error!(%host, "DNS error: {e}")),
400 }
401}
402
403#[implement(super::Service)]
404fn validate_dest(&self, dest: &ServerName) -> Result {
405 if dest == self.services.server.name && !self.services.server.config.federation_loopback {
406 return Err!("Won't send federation request to ourselves");
407 }
408
409 if dest.is_ip_literal() || IPAddress::is_valid(dest.host()) {
410 self.validate_dest_ip_literal(dest)?;
411 }
412
413 Ok(())
414}
415
416#[implement(super::Service)]
417fn validate_dest_ip_literal(&self, dest: &ServerName) -> Result {
418 trace!("Destination is an IP literal, checking against IP range denylist.",);
419 debug_assert!(
420 dest.is_ip_literal() || !IPAddress::is_valid(dest.host()),
421 "Destination is not an IP literal."
422 );
423 let ip = IPAddress::parse(dest.host()).map_err(|e| {
424 err!(BadServerResponse(debug_error!("Failed to parse IP literal from string: {e}")))
425 })?;
426
427 self.validate_ip(&ip)?;
428
429 Ok(())
430}
431
432#[implement(super::Service)]
433pub(crate) fn validate_ip(&self, ip: &IPAddress) -> Result {
434 if !self.services.client.valid_cidr_range(ip) {
435 return Err!(BadServerResponse("Not allowed to send requests to this IP"));
436 }
437
438 Ok(())
439}