Skip to main content

datumesh_core/
fetch.rs

1//! Key retrieval over HTTPS (9.3), only with the `fetch` feature. [`crate::verify`] never
2//! calls this: retrieval is a separate, explicit operation (9.4).
3//!
4//! Deprecated since 0.3.1 and removed at 1.0: an HTTP client is the application's choice
5//! (its TLS roots, proxies, timeouts, async runtime), so the crate keeps only the part that is
6//! protocol, [`keys_from_response`], which turns any client's response into keys.
7
8use std::time::Duration;
9
10use crate::error::{err, ErrorCode, Result};
11use crate::keys::{keys_from_response, KeyDoc};
12use crate::lexical;
13
14/// The largest key document read: 64 keys fit in far less.
15const MAX_BODY: u64 = 256 * 1024;
16
17/// Fetches `https://<host>/.well-known/datumesh.json` and checks it (9.2, 9.3).
18///
19/// Certificates are validated (Web PKI roots), no redirect is followed, and the request
20/// times out after 10 seconds. A 200 with a valid document gives the keys; a 200 with an
21/// invalid document is `E_KEY_DOC`; every other status (redirects included) and every
22/// network, DNS, TLS or timeout failure is `E_KEY_UNAVAILABLE`. A host that is not a DNS
23/// name is never fetched: it has no keys (`E_KEY_UNAVAILABLE`, 9.3).
24///
25/// Deprecated since 0.3.1; the `fetch` feature and this function are removed at 1.0. Fetch
26/// `https://<host>/.well-known/datumesh.json` with your own HTTP client (no redirects,
27/// validated certificates, a bounded body) and pass the status and body to
28/// [`keys_from_response`], or record a network failure with
29/// [`Keys::insert_unavailable`](crate::Keys::insert_unavailable).
30#[deprecated(
31    since = "0.3.1",
32    note = "removed at 1.0: fetch the key document with your own HTTP client and pass the response to `keys_from_response`"
33)]
34pub fn fetch_keys(host: &str) -> Result<KeyDoc> {
35    if lexical::check_dns_name(host, "host").is_err() {
36        return err(ErrorCode::KeyUnavailable, "$", format!("{host:?} is not a DNS name: no keys are resolved for it"));
37    }
38    let agent: ureq::Agent = ureq::Agent::config_builder()
39        .max_redirects(0)
40        .http_status_as_error(false)
41        .https_only(true)
42        .timeout_global(Some(Duration::from_secs(10)))
43        .build()
44        .into();
45    let url = format!("https://{host}/.well-known/datumesh.json");
46    let mut resp = match agent.get(&url).call() {
47        Ok(r) => r,
48        Err(e) => return err(ErrorCode::KeyUnavailable, "$", format!("{url}: {e}")),
49    };
50    let status = resp.status().as_u16();
51    if status != 200 {
52        return keys_from_response(host, status, None);
53    }
54    match resp.body_mut().with_config().limit(MAX_BODY).read_to_vec() {
55        Ok(body) => keys_from_response(host, status, Some(&body)),
56        Err(e) => err(ErrorCode::KeyUnavailable, "$", format!("{url}: {e}")),
57    }
58}