Skip to content

Commit 0bfa870

Browse files
authored
chore(dns): update hickory-resolver from 0.25 to 0.26 (#4544)
see: * https://rustsec.org/advisories/RUSTSEC-2026-0120.html * https://rustsec.org/advisories/RUSTSEC-2026-0119.html * GHSA-3v94-mw7p-v465 * GHSA-q2qq-hmj6-3wpp * https://www.cve.org/CVERecord?id=CVE-2024-8508 this branch provides changes, updating `hickory-resolver` in particular, to address RUSTSEC-2026-0120 (_aka GHSA-3v94-mw7p-v465_) and RUSTSEC-2026-0119 (_aka GHSA-q2qq-hmj6-3wpp, CVE-2024-8508_). we update `hickory-resolver` from 0.25.2 to 0.26.1. this is a significant release that includes many changes to the hickory-resolver public interface. see the release notes in 0.26.0 for a detailed summary of changes to that library since 0.25.2. see also: * #4552 * linkerd/linkerd2-proxy-api#561 * linkerd/linkerd2-proxy-api#562 * linkerd/linkerd2-proxy-api#563 * open-telemetry/opentelemetry-rust#3536 this branch updates the `quickcheck` library to 1.1.0, now that the linkerd2-proxy-api dependency has been updated to v0.20.0. this removes a dependency on rand@0.8. rand 0.9 is still in our dependency tree, and is ignored in our `cargo deny` at the time of writing. open-telemetry/opentelemetry-rust#3536 updates our remaining dependency on rand 0.9, but otherwise we now depend entirely on rand 0.10. Signed-off-by: katelyn martin <kate@buoyant.io>
1 parent fe48279 commit 0bfa870

11 files changed

Lines changed: 512 additions & 108 deletions

File tree

Cargo.lock

Lines changed: 458 additions & 69 deletions
Large diffs are not rendered by default.

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,6 @@ features = ["tokio", "tracing"]
137137
version = "0.20.0"
138138

139139
[workspace.dependencies.rand]
140-
version = "0.9"
140+
version = "0.10"
141141
default-features = false
142-
features = ["std", "std_rng", "os_rng"]
142+
features = ["std", "std_rng", "sys_rng"]

deny.toml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,15 @@ deny = [
4545
# aws-lc-rs should be used instead.
4646
{ name = "ring" }
4747
]
48-
skip = []
48+
skip = [
49+
{ name = "getrandom", version = "0.2.17" },
50+
{ name = "cpufeatures", version = "0.2.17" }
51+
]
4952
skip-tree = [
50-
# rand v0.9 is still propagating through the ecosystem
51-
{ name = "rand", version = "0.8" },
53+
# rand v0.10 is still propagating through the ecosystem
54+
#
55+
# see https://github.com/open-telemetry/opentelemetry-rust/pull/3536.
56+
{ name = "rand", version = "0.9" },
5257
# `pprof` uses a number of old dependencies. for now, we skip its subtree.
5358
{ name = "pprof" },
5459
# zerocopy v0.8 is still propagating through the ecosystem

linkerd/distribute/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ publish = { workspace = true }
1010
ahash = "0.8"
1111
linkerd-stack = { path = "../stack" }
1212
parking_lot = "0.12"
13-
rand = { workspace = true, features = ["small_rng", "thread_rng"] }
13+
rand = { workspace = true, features = ["thread_rng"] }
1414
tracing = { workspace = true }
1515

1616
[dev-dependencies]

linkerd/dns/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ edition = { workspace = true }
77
publish = { workspace = true }
88

99
[dependencies]
10-
hickory-resolver = "0.25.2"
10+
hickory-resolver = "0.26"
1111
linkerd-dns-name = { path = "./name" }
1212
prometheus-client = { workspace = true }
1313
thiserror = "2"

linkerd/dns/src/lib.rs

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,14 @@ struct InvalidSrv(rdata::SRV);
4141

4242
#[derive(Debug, Error)]
4343
#[error("failed to resolve A record: {0}")]
44-
struct ARecordError(#[from] hickory_resolver::ResolveError);
44+
struct ARecordError(#[from] hickory_resolver::net::NetError);
4545

4646
#[derive(Debug, Error)]
4747
enum SrvRecordError {
4848
#[error("{0}")]
4949
Invalid(#[from] InvalidSrv),
5050
#[error("failed to resolve SRV record: {0}")]
51-
Resolve(#[from] hickory_resolver::ResolveError),
51+
Resolve(#[from] hickory_resolver::net::NetError),
5252
}
5353

5454
#[derive(Debug, Error)]
@@ -67,32 +67,30 @@ impl Resolver {
6767
///
6868
/// Either a new `Resolver` or an error if the system configuration
6969
/// could not be parsed.
70-
///
71-
/// TODO: This should be infallible like it is in the `domain` crate.
72-
pub fn from_system_config_with<C: ConfigureResolver>(c: &C) -> std::io::Result<Self> {
70+
pub fn from_system_config_with<C: ConfigureResolver>(
71+
c: &C,
72+
) -> Result<Self, hickory_resolver::net::NetError> {
7373
let (config, mut opts) = system_conf::read_system_conf()?;
7474
c.configure_resolver(&mut opts);
7575
trace!("DNS config: {:?}", &config);
7676
trace!("DNS opts: {:?}", &opts);
77-
Ok(Self::new(config, opts))
77+
Self::new(config, opts)
7878
}
7979

80-
pub fn new(config: ResolverConfig, mut opts: ResolverOpts) -> Self {
80+
pub fn new(
81+
config: ResolverConfig,
82+
mut opts: ResolverOpts,
83+
) -> Result<Self, hickory_resolver::net::NetError> {
8184
// Disable Hickory-resolver's caching.
8285
opts.cache_size = 0;
8386
// This function is synchronous, but needs to be called within the Tokio
8487
// 0.2 runtime context, since it gets a handle.
85-
let provider = hickory_resolver::name_server::TokioConnectionProvider::default();
86-
let mut builder = hickory_resolver::Resolver::builder_with_config(config, provider);
87-
*builder.options_mut() = opts;
88-
let dns = builder.build();
89-
/* TODO(kate): this can be used if/when hickory-dns/hickory-dns#2877 is released.
88+
let provider = hickory_resolver::net::runtime::TokioRuntimeProvider::default();
9089
let dns = hickory_resolver::Resolver::builder_with_config(config, provider)
9190
.with_options(opts)
92-
.build();
93-
*/
91+
.build()?;
9492

95-
Resolver { dns, metrics: None }
93+
Ok(Resolver { dns, metrics: None })
9694
}
9795

9896
/// Installs a counter tracking the number of A/AAAA records resolved.
@@ -157,11 +155,18 @@ impl Resolver {
157155
debug!(%name, "Resolving a SRV record");
158156
let srv = self.dns.srv_lookup(name.as_str()).await?;
159157

160-
let valid_until = Instant::from_std(srv.as_lookup().valid_until());
158+
let valid_until = Instant::from_std(srv.valid_until());
161159
let addrs = srv
162-
.into_iter()
160+
.answers()
161+
.iter()
162+
.map(|record| &record.data)
163+
.filter_map(|rdata| match rdata {
164+
hickory_resolver::proto::rr::RData::SRV(srv) => Some(srv),
165+
_ => None,
166+
})
163167
.map(Self::srv_to_socket_addr)
164168
.collect::<Result<_, InvalidSrv>>()?;
169+
165170
debug!(ttl = ?valid_until - Instant::now(), ?addrs);
166171

167172
Ok((addrs, valid_until))
@@ -174,19 +179,19 @@ impl Resolver {
174179
// instead of dots/colons. We can alternatively do another lookup
175180
// on the pod's DNS but it seems unnecessary since the pod's
176181
// ip is in the target of the SRV record.
177-
fn srv_to_socket_addr(srv: rdata::SRV) -> Result<net::SocketAddr, InvalidSrv> {
178-
if let Some(first_label) = srv.target().iter().next() {
182+
fn srv_to_socket_addr(srv: &rdata::SRV) -> Result<net::SocketAddr, InvalidSrv> {
183+
if let Some(first_label) = srv.target.iter().next() {
179184
if let Ok(utf8) = std::str::from_utf8(first_label) {
180185
let mut res = utf8.replace('-', ".").parse::<std::net::IpAddr>();
181186
if res.is_err() {
182187
res = utf8.replace('-', ":").parse::<std::net::IpAddr>();
183188
}
184189
if let Ok(ip) = res {
185-
return Ok(net::SocketAddr::new(ip, srv.port()));
190+
return Ok(net::SocketAddr::new(ip, srv.port));
186191
}
187192
}
188193
}
189-
Err(InvalidSrv(srv))
194+
Err(InvalidSrv(srv.clone()))
190195
}
191196
}
192197

@@ -220,16 +225,16 @@ impl ResolveError {
220225
}
221226
}
222227

223-
/// Returns the negative TTL [`time::Duration`] of a [`hickory_resolver::ResolveError`].
228+
/// Returns the negative TTL [`time::Duration`] of a [`hickory_resolver::net::NetError`].
224229
///
225230
/// This function will defensively enforce a minimum negative TTL.
226-
fn negative_ttl_of(error: &hickory_resolver::ResolveError) -> Option<time::Duration> {
227-
use hickory_resolver::proto::{ProtoError, ProtoErrorKind};
231+
fn negative_ttl_of(error: &hickory_resolver::net::NetError) -> Option<time::Duration> {
232+
use hickory_resolver::net::{DnsError, NetError, NoRecords};
228233

229-
let Some(ProtoErrorKind::NoRecordsFound {
234+
let NetError::Dns(DnsError::NoRecordsFound(NoRecords {
230235
negative_ttl: Some(ttl_secs),
231236
..
232-
}) = error.proto().map(ProtoError::kind)
237+
})) = error
233238
else {
234239
return None;
235240
};
@@ -373,7 +378,7 @@ mod tests {
373378
let name = "foobar.linkerd-dst-headless.linkerd.svc.cluster.local.";
374379
let target = domain::Name::from_str(name).unwrap();
375380
let srv = rdata::SRV::new(1, 1, 8086, target);
376-
assert!(Resolver::srv_to_socket_addr(srv).is_err());
381+
assert!(Resolver::srv_to_socket_addr(&srv).is_err());
377382
}
378383

379384
#[test]
@@ -399,15 +404,15 @@ mod tests {
399404
] {
400405
let target = domain::Name::from_str(case.input).unwrap();
401406
let srv = rdata::SRV::new(1, 1, 8086, target);
402-
let socket = Resolver::srv_to_socket_addr(srv).unwrap();
407+
let socket = Resolver::srv_to_socket_addr(&srv).unwrap();
403408
assert_eq!(socket.ip(), net::IpAddr::from_str(case.output).unwrap());
404409
}
405410
}
406411

407412
#[test]
408413
fn srv_record_reports_cause_correctly() {
409414
let srv = "foobar.linkerd-dst-headless.linkerd.svc.cluster.local."
410-
.parse::<hickory_resolver::Name>()
415+
.parse::<domain::Name>()
411416
.map(|name| rdata::SRV::new(1, 1, 8086, name))
412417
.expect("a valid domain name");
413418

linkerd/exp-backoff/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ publish = { workspace = true }
88

99
[dependencies]
1010
futures = { version = "0.3", default-features = false }
11-
rand = { workspace = true, features = ["small_rng", "thread_rng"] }
11+
rand = { workspace = true, features = ["thread_rng"] }
1212
thiserror = "2"
1313
tokio = { version = "1", features = ["time"] }
1414
pin-project = "1"

linkerd/exp-backoff/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ impl ExponentialBackoff {
106106
if self.jitter == 0.0 {
107107
time::Duration::default()
108108
} else {
109+
use rand::RngExt;
109110
let jitter_factor = rng.random::<f64>();
110111
debug_assert!(
111112
jitter_factor > 0.0,

linkerd/pool/p2c/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ publish = { workspace = true }
1010
ahash = "0.8"
1111
futures = { version = "0.3", default-features = false }
1212
prometheus-client = { workspace = true }
13-
rand = { workspace = true, features = ["small_rng", "thread_rng"] }
13+
rand = { workspace = true, features = ["thread_rng"] }
1414
tracing = { workspace = true }
1515

1616
linkerd-error = { path = "../../error" }

linkerd/pool/p2c/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use linkerd_error::Error;
1111
use linkerd_metrics::prom;
1212
use linkerd_pool::Pool;
1313
use linkerd_stack::{NewService, Service};
14-
use rand::{rngs::SmallRng, Rng, SeedableRng};
14+
use rand::{rngs::SmallRng, SeedableRng};
1515
use std::{
1616
collections::hash_map::Entry,
1717
net::SocketAddr,
@@ -117,6 +117,8 @@ where
117117
}
118118

119119
fn gen_pair(rng: &mut SmallRng, len: usize) -> (usize, usize) {
120+
use rand::RngExt;
121+
120122
debug_assert!(len >= 2, "must have at least two endpoints");
121123
// Get two distinct random indexes (in a random order) and
122124
// compare the loads of the service at each index.

0 commit comments

Comments
 (0)