mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
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:
165
core/Cargo.lock
generated
165
core/Cargo.lock
generated
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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>(
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user