fix: refactor dns to handle tcp connections: (#3083)

* fix: refactor dns to handle tcp connections:
- do not use long-lived tcp connections to upstream dns servers
- when incoming request is over tcp, force a tcp lookup instead of udp

this solves cases where large dns records were not being resolved due to udp->tcp switch-over.

* use forwarding resolver for fallback

---------

Co-authored-by: Aiden McClelland <me@drbonez.dev>
This commit is contained in:
Remco Ros
2025-12-20 07:26:29 +01:00
committed by GitHub
parent 5446c89bc0
commit 7c12b58bb5
5 changed files with 258 additions and 309 deletions

165
core/Cargo.lock generated
View File

@@ -2262,7 +2262,7 @@ checksum = "6e39034cee21a2f5bbb66ba0e3689819c4bb5d00382a282006e802a7ffa6c41d"
dependencies = [
"cfg-if",
"libc",
"socket2",
"socket2 0.6.1",
"windows-sys 0.60.2",
]
@@ -2496,12 +2496,6 @@ version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099"
[[package]]
name = "endian-type"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
[[package]]
name = "enum-as-inner"
version = "0.6.1"
@@ -3240,6 +3234,16 @@ dependencies = [
"allocator-api2",
]
[[package]]
name = "hashing-serializer"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66c9b1a5e47c3bf40ae0f5705e84daa4cd6d8a74b2bdba43c06eb01dbc236f6e"
dependencies = [
"digest 0.10.7",
"serde",
]
[[package]]
name = "hashlink"
version = "0.10.0"
@@ -3300,25 +3304,6 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hickory-client"
version = "0.25.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c466cd63a4217d5b2b8e32f23f58312741ce96e3c84bf7438677d2baff0fc555"
dependencies = [
"cfg-if",
"data-encoding",
"futures-channel",
"futures-util",
"hickory-proto",
"once_cell",
"radix_trie",
"rand 0.9.2",
"thiserror 2.0.17",
"tokio",
"tracing",
]
[[package]]
name = "hickory-proto"
version = "0.25.2"
@@ -3345,6 +3330,28 @@ dependencies = [
"url",
]
[[package]]
name = "hickory-resolver"
version = "0.25.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc62a9a99b0bfb44d2ab95a7208ac952d31060efc16241c87eaf36406fecf87a"
dependencies = [
"cfg-if",
"futures-util",
"hickory-proto",
"ipconfig",
"moka",
"once_cell",
"parking_lot 0.12.5",
"rand 0.9.2",
"resolv-conf",
"serde",
"smallvec",
"thiserror 2.0.17",
"tokio",
"tracing",
]
[[package]]
name = "hickory-server"
version = "0.25.2"
@@ -3358,6 +3365,7 @@ dependencies = [
"enum-as-inner",
"futures-util",
"hickory-proto",
"hickory-resolver",
"ipnet",
"prefix-trie",
"serde",
@@ -3576,7 +3584,7 @@ dependencies = [
"libc",
"percent-encoding",
"pin-project-lite",
"socket2",
"socket2 0.6.1",
"system-configuration",
"tokio",
"tower-service",
@@ -3919,6 +3927,18 @@ dependencies = [
"rustversion",
]
[[package]]
name = "ipconfig"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f"
dependencies = [
"socket2 0.5.10",
"widestring",
"windows-sys 0.48.0",
"winreg 0.50.0",
]
[[package]]
name = "ipnet"
version = "2.11.0"
@@ -4297,7 +4317,7 @@ dependencies = [
"quoted_printable",
"rustls 0.23.35",
"rustls-platform-verifier",
"socket2",
"socket2 0.6.1",
"tokio",
"tokio-rustls 0.26.4",
"url",
@@ -4628,6 +4648,24 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "moka"
version = "0.12.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8261cd88c312e0004c1d51baad2980c66528dfdb2bee62003e643a4d8f86b077"
dependencies = [
"crossbeam-channel",
"crossbeam-epoch",
"crossbeam-utils",
"equivalent",
"parking_lot 0.12.5",
"portable-atomic",
"rustc_version",
"smallvec",
"tagptr",
"uuid",
]
[[package]]
name = "moxcms"
version = "0.7.11"
@@ -4671,15 +4709,6 @@ dependencies = [
"unicase",
]
[[package]]
name = "nibble_vec"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43"
dependencies = [
"smallvec",
]
[[package]]
name = "nix"
version = "0.23.2"
@@ -5644,7 +5673,7 @@ dependencies = [
"shared_library",
"shell-words",
"winapi",
"winreg",
"winreg 0.10.1",
]
[[package]]
@@ -5965,7 +5994,7 @@ dependencies = [
"quinn-udp",
"rustc-hash",
"rustls 0.23.35",
"socket2",
"socket2 0.6.1",
"thiserror 2.0.17",
"tokio",
"tracing",
@@ -6002,7 +6031,7 @@ dependencies = [
"cfg_aliases 0.2.1",
"libc",
"once_cell",
"socket2",
"socket2 0.6.1",
"tracing",
"windows-sys 0.60.2",
]
@@ -6089,16 +6118,6 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
[[package]]
name = "radix_trie"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd"
dependencies = [
"endian-type",
"nibble_vec",
]
[[package]]
name = "rand"
version = "0.7.3"
@@ -6434,6 +6453,12 @@ dependencies = [
"url",
]
[[package]]
name = "resolv-conf"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7"
[[package]]
name = "retry-error"
version = "0.6.5"
@@ -7380,6 +7405,16 @@ dependencies = [
"wayland-client",
]
[[package]]
name = "socket2"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678"
dependencies = [
"libc",
"windows-sys 0.52.0",
]
[[package]]
name = "socket2"
version = "0.6.1"
@@ -7667,8 +7702,8 @@ dependencies = [
"form_urlencoded",
"futures",
"gpt",
"hashing-serializer",
"hex",
"hickory-client",
"hickory-server",
"hmac 0.12.1",
"http",
@@ -7736,7 +7771,7 @@ dependencies = [
"sha-crypt",
"sha2 0.10.9",
"signal-hook",
"socket2",
"socket2 0.6.1",
"socks5-impl",
"sqlx",
"sscanf",
@@ -7979,6 +8014,12 @@ dependencies = [
"libc",
]
[[package]]
name = "tagptr"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417"
[[package]]
name = "tap"
version = "1.0.1"
@@ -8197,7 +8238,7 @@ dependencies = [
"parking_lot 0.12.5",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"socket2 0.6.1",
"tokio-macros",
"tracing",
"windows-sys 0.61.2",
@@ -8431,7 +8472,7 @@ dependencies = [
"hyper-util",
"percent-encoding",
"pin-project",
"socket2",
"socket2 0.6.1",
"sync_wrapper",
"tokio",
"tokio-stream",
@@ -10229,6 +10270,12 @@ dependencies = [
"wasite",
]
[[package]]
name = "widestring"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471"
[[package]]
name = "winapi"
version = "0.3.9"
@@ -10743,6 +10790,16 @@ dependencies = [
"winapi",
]
[[package]]
name = "winreg"
version = "0.50.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
dependencies = [
"cfg-if",
"windows-sys 0.48.0",
]
[[package]]
name = "wit-bindgen"
version = "0.46.0"

View File

@@ -122,8 +122,7 @@ form_urlencoded = "1.2.1"
futures = "0.3.28"
gpt = "4.1.0"
hex = "0.4.3"
hickory-client = "0.25.2"
hickory-server = "0.25.2"
hickory-server = { version = "0.25.2", features = ["resolver"] }
hmac = "0.12.1"
http = "1.0.0"
http-body-util = "0.1"
@@ -271,6 +270,7 @@ uuid = { version = "1.4.1", features = ["v4"] }
visit-rs = "0.1.1"
x25519-dalek = { version = "2.0.1", features = ["static_secrets"] }
zbus = "5.1.1"
hashing-serializer = "0.1.1"
[target.'cfg(target_os = "linux")'.dependencies]
procfs = "0.18.0"

View File

@@ -1,5 +1,5 @@
use std::borrow::Borrow;
use std::collections::{BTreeMap, VecDeque};
use std::collections::BTreeMap;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use std::str::FromStr;
use std::sync::{Arc, Weak};
@@ -7,27 +7,23 @@ use std::time::Duration;
use clap::Parser;
use color_eyre::eyre::eyre;
use futures::future::BoxFuture;
use futures::{FutureExt, StreamExt};
use hickory_client::client::Client;
use hickory_client::proto::DnsHandle;
use hickory_client::proto::runtime::TokioRuntimeProvider;
use hickory_client::proto::tcp::TcpClientStream;
use hickory_client::proto::udp::UdpClientStream;
use hickory_client::proto::xfer::DnsRequestOptions;
use hickory_server::ServerFuture;
use hickory_server::authority::MessageResponseBuilder;
use futures::{FutureExt, StreamExt, TryStreamExt};
use hickory_server::authority::{AuthorityObject, Catalog, MessageResponseBuilder};
use hickory_server::proto::op::{Header, ResponseCode};
use hickory_server::proto::rr::{Name, Record, RecordType};
use hickory_server::proto::rr::{LowerName, Name, Record, RecordType};
use hickory_server::resolver::config::{ResolverConfig, ResolverOpts};
use hickory_server::server::{Request, RequestHandler, ResponseHandler, ResponseInfo};
use hickory_server::store::forwarder::{ForwardAuthority, ForwardConfig};
use hickory_server::{ServerFuture, resolver as hickory_resolver};
use imbl::OrdMap;
use imbl_value::InternedString;
use patch_db::json_ptr::JsonPointer;
use itertools::Itertools;
use rpc_toolkit::{
Context, HandlerArgs, HandlerExt, ParentHandler, from_fn_async, from_fn_blocking,
};
use serde::{Deserialize, Serialize};
use tokio::net::{TcpListener, UdpSocket};
use tokio::sync::RwLock;
use tracing::instrument;
use crate::context::{CliContext, RpcContext};
@@ -35,7 +31,6 @@ use crate::db::model::Database;
use crate::db::model::public::NetworkInterfaceInfo;
use crate::net::gateway::NetworkInterfaceWatcher;
use crate::prelude::*;
use crate::util::actor::background::BackgroundJobQueue;
use crate::util::future::NonDetachingJoinHandle;
use crate::util::io::file_string_stream;
use crate::util::serde::{HandlerExtSerde, display_serializable};
@@ -214,173 +209,6 @@ pub struct DnsController {
dns_server: NonDetachingJoinHandle<()>,
}
struct DnsClient {
client: Arc<SyncRwLock<Vec<(SocketAddr, hickory_client::client::Client)>>>,
_thread: NonDetachingJoinHandle<()>,
}
impl DnsClient {
pub fn new(db: TypedPatchDb<Database>) -> Self {
let client = Arc::new(SyncRwLock::new(Vec::new()));
Self {
client: client.clone(),
_thread: tokio::spawn(async move {
let (bg, mut runner) = BackgroundJobQueue::new();
runner
.run_while(async move {
let dhcp_ns_db = db.clone();
bg.add_job(async move {
loop {
if let Err(e) = async {
let mut stream =
file_string_stream("/run/systemd/resolve/resolv.conf")
.filter_map(|a| futures::future::ready(a.transpose()))
.boxed();
while let Some(conf) = stream.next().await {
let conf: String = conf?;
let mut nameservers = conf
.lines()
.map(|l| l.trim())
.filter_map(|l| l.strip_prefix("nameserver "))
.map(|n| {
n.parse::<SocketAddr>().or_else(|_| {
n.parse::<IpAddr>().map(|a| (a, 53).into())
})
})
.collect::<Result<VecDeque<_>, _>>()?;
if nameservers
.front()
.map_or(false, |addr| addr.ip().is_loopback())
{
nameservers.pop_front();
}
if nameservers.front().map_or(false, |addr| {
addr.ip() == IpAddr::from([1, 1, 1, 1])
}) {
nameservers.pop_front();
}
dhcp_ns_db
.mutate(|db| {
let dns = db
.as_public_mut()
.as_server_info_mut()
.as_network_mut()
.as_dns_mut();
dns.as_dhcp_servers_mut().ser(&nameservers)
})
.await
.result?
}
Ok::<_, Error>(())
}
.await
{
tracing::error!("{e}");
tracing::debug!("{e:?}");
tokio::time::sleep(Duration::from_secs(1)).await;
}
}
});
loop {
if let Err::<(), Error>(e) = async {
let mut dns_changed = db
.subscribe(
"/public/serverInfo/network/dns"
.parse::<JsonPointer>()
.with_kind(ErrorKind::Database)?,
)
.await;
let mut prev_nameservers = VecDeque::new();
let mut bg = BTreeMap::<SocketAddr, BoxFuture<_>>::new();
loop {
let dns = db
.peek()
.await
.into_public()
.into_server_info()
.into_network()
.into_dns();
let nameservers = dns
.as_static_servers()
.transpose_ref()
.unwrap_or_else(|| dns.as_dhcp_servers())
.de()?;
if nameservers != prev_nameservers {
let mut existing: BTreeMap<_, _> =
client.peek(|c| c.iter().cloned().collect());
let mut new = Vec::with_capacity(nameservers.len());
for addr in &nameservers {
if let Some(existing) = existing.remove(addr) {
new.push((*addr, existing));
} else {
let client = if let Ok((client, bg_thread)) =
Client::connect(
UdpClientStream::builder(
*addr,
TokioRuntimeProvider::new(),
)
.build(),
)
.await
{
bg.insert(*addr, bg_thread.boxed());
client
} else {
let (stream, sender) = TcpClientStream::new(
*addr,
None,
Some(Duration::from_secs(30)),
TokioRuntimeProvider::new(),
);
let (client, bg_thread) =
Client::new(stream, sender, None)
.await
.with_kind(ErrorKind::Network)?;
bg.insert(*addr, bg_thread.fuse().boxed());
client
};
new.push((*addr, client));
}
}
bg.retain(|n, _| nameservers.iter().any(|a| a == n));
prev_nameservers = nameservers;
client.replace(new);
}
futures::future::select(
dns_changed.recv().boxed(),
futures::future::join(
futures::future::join_all(bg.values_mut()),
futures::future::pending::<()>(),
),
)
.await;
}
}
.await
{
tracing::error!("{e}");
tracing::debug!("{e:?}");
}
tokio::time::sleep(Duration::from_secs(1)).await;
}
})
.await;
})
.into(),
}
}
fn lookup(
&self,
query: hickory_client::proto::op::Query,
options: DnsRequestOptions,
) -> Vec<hickory_client::proto::xfer::DnsExchangeSend> {
self.client.peek(|c| {
c.iter()
.map(|(_, c)| c.lookup(query.clone(), options.clone()))
.collect()
})
}
}
lazy_static::lazy_static! {
static ref LOCALHOST: Name = Name::from_ascii("localhost").unwrap();
static ref STARTOS: Name = Name::from_ascii("startos").unwrap();
@@ -388,11 +216,106 @@ lazy_static::lazy_static! {
}
struct Resolver {
client: DnsClient,
net_iface: Watch<OrdMap<GatewayId, NetworkInterfaceInfo>>,
catalog: Arc<RwLock<Catalog>>,
resolve: Arc<SyncRwLock<ResolveMap>>,
net_iface: Watch<OrdMap<GatewayId, NetworkInterfaceInfo>>,
_thread: NonDetachingJoinHandle<()>,
}
impl Resolver {
fn new(
db: TypedPatchDb<Database>,
net_iface: Watch<OrdMap<GatewayId, NetworkInterfaceInfo>>,
) -> Self {
let catalog = Arc::new(RwLock::new(Catalog::new()));
Self {
catalog: catalog.clone(),
resolve: Arc::new(SyncRwLock::new(ResolveMap::default())),
net_iface,
_thread: tokio::spawn(async move {
let mut prev = crate::util::serde::hash_serializable::<sha2::Sha256, _>(&(
ResolverConfig::new(),
ResolverOpts::default(),
))
.unwrap_or_default();
loop {
if let Err(e) = async {
let mut stream = file_string_stream("/run/systemd/resolve/resolv.conf")
.filter_map(|a| futures::future::ready(a.transpose()))
.boxed();
while let Some(conf) = stream.try_next().await? {
let (config, mut opts) =
hickory_resolver::system_conf::parse_resolv_conf(conf)
.with_kind(ErrorKind::ParseSysInfo)?;
opts.timeout = Duration::from_secs(30);
let hash = crate::util::serde::hash_serializable::<sha2::Sha256, _>(
&(&config, &opts),
)?;
if hash != prev {
db.mutate(|db| {
db.as_public_mut()
.as_server_info_mut()
.as_network_mut()
.as_dns_mut()
.as_dhcp_servers_mut()
.ser(
&config
.name_servers()
.into_iter()
.map(|n| n.socket_addr)
.dedup()
.skip(2)
.collect(),
)
})
.await
.result?;
let auth: Vec<Arc<dyn AuthorityObject>> = vec![Arc::new(
ForwardAuthority::builder_tokio(ForwardConfig {
name_servers: from_value(Value::Array(
config
.name_servers()
.into_iter()
.skip(4)
.map(to_value)
.collect::<Result<_, Error>>()?,
))?,
options: Some(opts),
})
.build()
.map_err(|e| Error::new(eyre!("{e}"), ErrorKind::Network))?,
)];
{
let mut guard = tokio::time::timeout(
Duration::from_secs(10),
catalog.write(),
)
.await
.map_err(|_| {
Error::new(
eyre!("timed out waiting to update dns catalog"),
ErrorKind::Timeout,
)
})?;
guard.upsert(Name::root().into(), auth);
drop(guard);
}
}
prev = hash;
}
Ok::<_, Error>(())
}
.await
{
tracing::error!("{e}");
tracing::debug!("{e:?}");
tokio::time::sleep(Duration::from_secs(1)).await;
}
}
})
.into(),
}
}
fn resolve(&self, name: &Name, mut src: IpAddr) -> Option<Vec<IpAddr>> {
if name.zone_of(&*LOCALHOST) {
return Some(vec![Ipv4Addr::LOCALHOST.into(), Ipv6Addr::LOCALHOST.into()]);
@@ -495,6 +418,7 @@ impl RequestHandler for Resolver {
),
)
.await
.map(Some)
}
RecordType::AAAA => {
let mut header = Header::response_from_request(request.header());
@@ -521,6 +445,7 @@ impl RequestHandler for Resolver {
),
)
.await
.map(Some)
}
_ => {
let mut header = Header::response_from_request(request.header());
@@ -536,70 +461,27 @@ impl RequestHandler for Resolver {
),
)
.await
.map(Some)
}
}
} else {
let query = query.original().clone();
let mut opt = DnsRequestOptions::default();
opt.recursion_desired = request.recursion_desired();
let mut streams = self.client.lookup(query, opt);
let mut err = None;
for stream in streams.iter_mut() {
match tokio::time::timeout(Duration::from_secs(5), stream.next()).await {
Ok(Some(Err(e))) => err = Some(e),
Ok(Some(Ok(msg))) => {
let mut header = msg.header().clone();
header.set_id(request.id());
header.set_checking_disabled(request.checking_disabled());
header.set_recursion_available(true);
return response_handle
.send_response(
MessageResponseBuilder::from_message_request(&*request).build(
header,
msg.answers(),
msg.name_servers(),
&msg.soa().map(|s| s.to_owned().into_record_of_rdata()),
msg.additionals(),
),
)
.await;
}
_ => (),
}
}
if let Some(e) = err {
tracing::error!("{e}");
tracing::debug!("{e:?}");
}
let mut header = Header::response_from_request(request.header());
header.set_recursion_available(true);
header.set_response_code(ResponseCode::ServFail);
response_handle
.send_response(
MessageResponseBuilder::from_message_request(&*request).build(
header,
[],
[],
[],
[],
),
)
.await
Ok(None)
}
}
.await
{
Ok(a) => a,
Ok(Some(a)) => return a,
Ok(None) => (),
Err(e) => {
tracing::error!("{}", e);
tracing::debug!("{:?}", e);
tracing::error!("Error resolving internal DNS: {e}");
tracing::debug!("{e:?}");
let mut header = Header::response_from_request(request.header());
header.set_recursion_available(true);
header.set_response_code(ResponseCode::ServFail);
response_handle
return response_handle
.send_response(
MessageResponseBuilder::from_message_request(&*request).build(
header,
header.into(),
[],
[],
[],
@@ -607,9 +489,14 @@ impl RequestHandler for Resolver {
),
)
.await
.unwrap_or(header.into())
.unwrap_or_else(|_| header.into());
}
}
self.catalog
.read()
.await
.handle_request(request, response_handle)
.await
}
}
@@ -619,13 +506,9 @@ impl DnsController {
db: TypedPatchDb<Database>,
watcher: &NetworkInterfaceWatcher,
) -> Result<Self, Error> {
let resolve = Arc::new(SyncRwLock::new(ResolveMap::default()));
let mut server = ServerFuture::new(Resolver {
client: DnsClient::new(db),
net_iface: watcher.subscribe(),
resolve: resolve.clone(),
});
let resolver = Resolver::new(db, watcher.subscribe());
let resolve = Arc::downgrade(&resolver.resolve);
let mut server = ServerFuture::new(resolver);
let dns_server = tokio::spawn(
async move {
@@ -653,7 +536,7 @@ impl DnsController {
.into();
Ok(Self {
resolve: Arc::downgrade(&resolve),
resolve,
dns_server,
})
}

View File

@@ -13,6 +13,7 @@ use crate::PackageId;
use crate::context::CliContext;
use crate::prelude::*;
use crate::progress::{FullProgressTracker, ProgressTrackerWriter, ProgressUnits};
use crate::registry::asset::BufferedHttpSource;
use crate::registry::context::RegistryContext;
use crate::registry::package::index::PackageVersionInfo;
use crate::s9pk::S9pk;
@@ -131,18 +132,10 @@ pub async fn cli_add_package(
sign_phase.complete();
verify_phase.start();
let source = HttpSource::new(ctx.client.clone(), url.clone()).await?;
let len = source.size().await;
let source = BufferedHttpSource::new(ctx.client.clone(), url.clone(), verify_phase).await?;
let mut src = S9pk::deserialize(&Arc::new(source), Some(&commitment)).await?;
if let Some(len) = len {
verify_phase.set_total(len);
}
verify_phase.set_units(Some(ProgressUnits::Bytes));
let mut verify_writer = ProgressTrackerWriter::new(tokio::io::sink(), verify_phase);
src.serialize(&mut TrackingIO::new(0, &mut verify_writer), true)
src.serialize(&mut TrackingIO::new(0, &mut tokio::io::sink()), true)
.await?;
let (_, mut verify_phase) = verify_writer.into_inner();
verify_phase.complete();
index_phase.start();
ctx.call_remote::<RegistryContext>(

View File

@@ -7,6 +7,9 @@ use base64::Engine;
use clap::builder::ValueParserFactory;
use clap::{ArgMatches, CommandFactory, FromArgMatches};
use color_eyre::eyre::eyre;
use digest::Update;
use digest::generic_array::GenericArray;
use hashing_serializer::HashingSerializer;
use imbl_value::imbl::OrdMap;
use openssl::pkey::{PKey, Private};
use openssl::x509::X509;
@@ -16,6 +19,7 @@ use rpc_toolkit::{
use serde::de::DeserializeOwned;
use serde::ser::{SerializeMap, SerializeSeq};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use sha2::Digest;
use ts_rs::TS;
use super::IntoDoubleEndedIterator;
@@ -1444,3 +1448,15 @@ pub fn is_partial_of(partial: &Value, full: &Value) -> bool {
(_, _) => partial == full,
}
}
pub fn hash_serializable<D: Digest + Update, T: Serialize>(
value: &T,
) -> Result<GenericArray<u8, D::OutputSize>, Error> {
let mut digest = D::new();
value
.serialize(HashingSerializer {
digest: &mut digest,
})
.with_kind(ErrorKind::Serialization)?;
Ok(digest.finalize())
}