1use std::net::{IpAddr, SocketAddr};
4
5use hickory_resolver::{TokioResolver, net::NetError, proto::rr::RData};
6use reqwest_middleware::ClientWithMiddleware;
7use serde::{Deserialize, Serialize};
8use tracing::{debug, info, instrument};
9
10use crate::cache;
11
12pub mod error;
13
14#[derive(Debug, Clone, Deserialize, Serialize)]
21pub struct ServerWellKnown {
22 #[serde(rename = "m.server")]
25 pub server: String,
26}
27
28#[derive(Debug, Clone)]
30pub struct Resolver {
31 http: ClientWithMiddleware,
33 resolver: TokioResolver,
35}
36
37#[derive(Debug, Clone, PartialEq, Eq)]
39pub enum Server {
40 Ip(IpAddr),
42 Socket(SocketAddr),
44 Host(String),
46 HostPort(String),
48 Srv(String, String),
50}
51
52impl Server {
53 #[must_use]
55 pub fn host_header(&self) -> String {
56 match self {
57 Server::Ip(addr) => addr.to_string(),
58 Server::Socket(addr) => addr.to_string(),
59 Server::Host(host) => host.clone(),
60 Server::HostPort(host) => host.clone(),
61 Server::Srv(_, host) => host.to_string(),
62 }
63 }
64
65 #[must_use]
67 pub fn address(&self) -> String {
68 match self {
69 Server::Ip(addr) => format!("{}:8448", addr),
70 Server::Socket(addr) => addr.to_string(),
71 Server::Host(host) => format!("{}:8448", host),
72 Server::HostPort(host) => host.clone(),
73 Server::Srv(host, _) => host.clone(),
74 }
75 }
76}
77
78impl Resolver {
79 pub fn new() -> Result<Self, NetError> {
81 Ok(Self {
82 http: reqwest_middleware::ClientBuilder::new(reqwest::Client::new())
83 .with(cache())
84 .build(),
85 resolver: TokioResolver::builder_tokio()?.build()?,
86 })
87 }
88
89 #[must_use]
92 pub fn with(http: reqwest::Client, resolver: TokioResolver) -> Self {
93 Self { http: reqwest_middleware::ClientBuilder::new(http).with(cache()).build(), resolver }
94 }
95
96 #[instrument(skip(self, port), err)]
98 pub async fn resolve(
99 &self,
100 name: &str,
101 #[cfg(test)] port: Option<u16>,
102 ) -> error::Result<Server> {
103 debug!("Parsing socket literal");
105 if let Ok(addr) = name.parse::<SocketAddr>() {
106 info!("The server name is a socket literal");
107 return Ok(Server::Socket(addr));
108 }
109 debug!("Parsing IP literal");
110 if let Ok(addr) = name.parse::<IpAddr>() {
111 info!("The server name is an IP literal");
112 return Ok(Server::Ip(addr));
113 }
114 debug!("Parsing host with port");
116 if split_port(name).is_some() {
117 info!("The servername is a host with port");
118 return Ok(Server::HostPort(name.to_owned()));
119 }
120 debug!("Querying well known");
122 if let Some(well_known) = self
123 .well_known(
124 name,
125 #[cfg(test)]
126 port,
127 )
128 .await?
129 {
130 debug!("Well-known received: {:?}", &well_known);
131 debug!("Parsing delegated socket literal");
133 if let Ok(addr) = well_known.server.parse::<SocketAddr>() {
134 info!("The server name is a delegated IP literal");
135 return Ok(Server::Socket(addr));
136 }
137 debug!("Parsing delegated IP literal");
138 if let Ok(addr) = well_known.server.parse::<IpAddr>() {
139 info!("The server name is a delegated socket literal");
140 return Ok(Server::Ip(addr));
141 }
142 debug!("Parsing delegated hostname with port");
144 if split_port(&well_known.server).is_some() {
145 info!("The server name is a delegated hostname with port");
146 return Ok(Server::HostPort(well_known.server));
147 }
148 debug!("Looking up SRV record for delegated hostname");
150 if let Some(name) = self.srv_lookup_with_fallback(&well_known.server).await {
151 info!("The server name is a delegated SRV record");
152 return Ok(Server::Srv(name, well_known.server));
153 }
154 debug!("Using delegated hostname directly");
156 return Ok(Server::Host(well_known.server));
157 }
158 debug!("Looking up SRV record for hostname");
160 if let Some(srv) = self.srv_lookup_with_fallback(name).await {
161 info!("The server name is an SRV record");
162 return Ok(Server::Srv(srv, name.to_owned()));
163 }
164 debug!("Using provided hostname directly");
166 Ok(Server::Host(name.to_owned()))
167 }
168
169 #[cfg_attr(test, allow(unused_variables))]
171 #[instrument(skip(self, name, port), err)]
172 async fn well_known(
173 &self,
174 name: &str,
175 #[cfg(test)] port: Option<u16>,
176 ) -> error::Result<Option<ServerWellKnown>> {
177 #[cfg(not(test))]
178 let response = self.http.get(format!("https://{}/.well-known/matrix/server", name)).send().await;
179
180 #[cfg(test)]
181 #[allow(clippy::expect_used)]
182 let response = self
183 .http
184 .get(format!(
185 "http://{name}:{port}/.well-known/matrix/server",
186 port = port.expect("port needed for test env")
187 ))
188 .send()
189 .await;
190
191 let response = match response {
193 Ok(response) => response,
194 Err(reqwest_middleware::Error::Reqwest(e)) if e.is_connect() => return Err(e.into()),
195 Err(_) => return Ok(None),
196 };
197 let well_known = response.json::<ServerWellKnown>().await.ok();
198 Ok(well_known)
199 }
200
201 #[instrument(skip(self))]
204 async fn srv_lookup(&self, query: &str) -> Option<String> {
205 let srv = self.resolver.srv_lookup(query).await.ok()?;
206 match srv
208 .answers()
209 .iter()
210 .filter_map(|record| match &record.data {
211 RData::SRV(srv) => Some(srv),
212 _ => None,
213 })
214 .min_by_key(|srv| srv.priority)
215 {
216 Some(srv) => {
217 let target = srv.target.to_ascii();
218 let host = target.trim_end_matches('.');
219 Some(format!("{}:{}", host, srv.port))
220 }
221 None => None,
222 }
223 }
224
225 #[instrument(skip(self))]
228 async fn srv_lookup_with_fallback(&self, name: &str) -> Option<String> {
229 if let Some(found) = self.srv_lookup(&fed_srv_name(name)).await {
230 debug!("Found _matrix-fed._tcp SRV record");
231 return Some(found);
232 }
233 debug!("No _matrix-fed._tcp record; trying deprecated _matrix._tcp");
234 self.srv_lookup(&deprecated_srv_name(name)).await
235 }
236
237 pub async fn socket(&self, server: &Server) -> Result<SocketAddr, NetError> {
239 let (host, port) = match *server {
240 Server::Ip(ip) => return Ok(SocketAddr::new(ip, 8448)),
241 Server::Socket(socket) => return Ok(socket),
242 Server::Host(ref host) => (host.as_str(), 8448),
243 #[allow(clippy::expect_used)]
244 Server::HostPort(ref host) => split_port(host).expect("HostPort was constructed with port"),
245 #[allow(clippy::expect_used)]
246 Server::Srv(ref addr, _) => split_port(addr).expect("The SRV record includes the port"),
247 };
248 let record = self.resolver.lookup_ip(host).await?;
249 let socket =
251 SocketAddr::new(record.iter().next().ok_or(NetError::Message("No records"))?, port);
252 Ok(socket)
253 }
254}
255
256fn fed_srv_name(name: &str) -> String {
258 format!("_matrix-fed._tcp.{name}")
259}
260
261fn deprecated_srv_name(name: &str) -> String {
263 format!("_matrix._tcp.{name}")
264}
265
266fn split_port(host: &str) -> Option<(&str, u16)> {
268 match &host.split(':').collect::<Vec<_>>()[..] {
269 [host, port] => match port.parse() {
270 Ok(port) => Some((host, port)),
271 Err(_) => None,
272 },
273 _ => None,
274 }
275}
276
277#[cfg(test)]
278mod tests {
279 use std::net::{IpAddr, SocketAddr};
280
281 use hickory_resolver::TokioResolver;
282 use proptest::prelude::*;
283 use wiremock::{
284 Mock, MockServer, ResponseTemplate,
285 matchers::{method, path},
286 };
287
288 use super::{Resolver, Server};
289
290 #[test]
293 fn srv_query_names() {
294 assert_eq!(super::fed_srv_name("example.test"), "_matrix-fed._tcp.example.test");
295 assert_eq!(super::deprecated_srv_name("example.test"), "_matrix._tcp.example.test");
296 }
297
298 #[tokio::test]
300 async fn literals() -> Result<(), Box<dyn std::error::Error>> {
301 let resolver = Resolver::new()?;
302 assert_eq!(
303 resolver.resolve("127.0.0.1", None).await?,
304 Server::Ip(IpAddr::from([127, 0, 0, 1])),
305 "1. IP literal"
306 );
307 assert_eq!(
308 resolver.resolve("127.0.0.1:4884", None).await?,
309 Server::Socket(SocketAddr::new(IpAddr::from([127, 0, 0, 1]), 4884)),
310 "1. Socket literal"
311 );
312 assert_eq!(
313 resolver.resolve("example.test:1234", None).await?,
314 Server::HostPort(String::from("example.test:1234")),
315 "2. Host with port"
316 );
317 Ok(())
318 }
319
320 #[tokio::test]
322 async fn http() -> Result<(), Box<dyn std::error::Error>> {
323 let mock_server = MockServer::start().await;
324
325 let client = reqwest::Client::builder()
326 .resolve("example.test", *mock_server.address())
327 .resolve("destination.test", *mock_server.address())
328 .build()?;
329 let resolver = Resolver::with(client, TokioResolver::builder_tokio()?.build()?);
330
331 let addr = mock_server.address();
332
333 Mock::given(method("GET"))
334 .and(path("/.well-known/matrix/server"))
335 .respond_with(
336 ResponseTemplate::new(200).set_body_raw(
337 format!(r#"{{"m.server": "{}"}}"#, addr.ip()),
338 "application/json",
339 ),
340 )
341 .up_to_n_times(1)
342 .expect(1)
343 .mount(&mock_server)
344 .await;
345
346 assert_eq!(
347 resolver.resolve("example.test", Some(addr.port())).await?,
348 Server::Ip(addr.ip()),
349 "3.1 delegated_hostname is an IP literal"
350 );
351
352 Mock::given(method("GET"))
353 .and(path("/.well-known/matrix/server"))
354 .respond_with(
355 ResponseTemplate::new(200)
356 .set_body_raw(format!(r#"{{"m.server": "{}"}}"#, addr), "application/json"),
357 )
358 .up_to_n_times(1)
359 .expect(1)
360 .mount(&mock_server)
361 .await;
362
363 assert_eq!(
364 resolver.resolve("example.test", Some(addr.port())).await?,
365 Server::Socket(*mock_server.address()),
366 "3.1 delegated_hostname is a socket literal"
367 );
368
369 Mock::given(method("GET"))
370 .and(path("/.well-known/matrix/server"))
371 .respond_with(ResponseTemplate::new(200).set_body_raw(
372 format!(r#"{{"m.server": "destination.test:{}"}}"#, addr.port()),
373 "application/json",
374 ))
375 .expect(1)
376 .up_to_n_times(1)
377 .mount(&mock_server)
378 .await;
379
380 assert_eq!(
381 resolver.resolve("example.test", Some(addr.port())).await?,
382 Server::HostPort(format!("destination.test:{}", addr.port())),
383 "3.2 delegated_hostname includes a port"
384 );
385 Ok(())
386 }
387
388 proptest! {
389 #[test]
392 fn split_port_roundtrip(host in "[a-z][a-z0-9-]{0,20}", port in any::<u16>()) {
393 let input = format!("{host}:{port}");
394 prop_assert_eq!(super::split_port(&input), Some((host.as_str(), port)));
395 }
396
397 #[test]
399 fn split_port_rejects_bare_host(host in "[a-z][a-z0-9-]{0,20}") {
400 prop_assert_eq!(super::split_port(&host), None);
401 }
402
403 #[test]
405 fn srv_names_prefixed(name in "[a-z][a-z0-9.-]{0,40}") {
406 prop_assert!(super::fed_srv_name(&name).starts_with("_matrix-fed._tcp."));
407 prop_assert!(super::deprecated_srv_name(&name).starts_with("_matrix._tcp."));
408 prop_assert!(super::fed_srv_name(&name).ends_with(&name));
409 }
410
411 #[test]
414 fn host_derivations(host in "[a-z][a-z0-9.-]{0,40}") {
415 let server = Server::Host(host.clone());
416 prop_assert_eq!(server.address(), format!("{host}:8448"));
417 prop_assert_eq!(server.host_header(), host);
418 }
419 }
420}