[GH-ISSUE #2435] RecursorDnsHandle misclasifies DS no-ds.extended-dns-errors.com. as ErrorKind::Forward #990

Open
opened 2026-03-16 01:12:06 +03:00 by kerem · 1 comment
Owner

Originally created by @japaric on GitHub (Sep 6, 2024).
Original GitHub issue: https://github.com/hickory-dns/hickory-dns/issues/2435

Describe the bug
What the title says

To Reproduce
Run the following code:

// # Cargo.toml
// hickory-recursor.features = ["dnssec-ring"]
// tokio-test = "0.4.4"
// tracing-subscriber = "0.3.18"

use std::{
    env,
    error::Error,
    net::{Ipv4Addr, SocketAddrV4},
    sync::Arc,
    time::Instant,
};

use hickory_recursor::{
    proto::{
        op::Query,
        rr::{dnssec::TrustAnchor, RecordType},
    },
    resolver::{
        config::{NameServerConfigGroup, Protocol},
        Name,
    },
    DnssecPolicy, NameServerConfig, Recursor,
};
use tracing_subscriber::{filter, prelude::*};

fn main() -> Result<(), Box<dyn Error>> {
    let mut args = env::args().skip(1);
    let [Some(record_type), Some(fqdn), None] = [args.next(), args.next(), args.next()] else {
        return Err("expected exactly two arguments".into());
    };
    let record_type: RecordType = record_type.parse()?;
    let fqdn = Name::from_ascii(fqdn)?;

    let stdout_log = tracing_subscriber::fmt::layer().pretty();
    tracing_subscriber::registry()
        .with(stdout_log.with_filter(filter::LevelFilter::TRACE))
        .init();

    let mut config = NameServerConfigGroup::default();
    // add `a.root-servers.net`
    config.push(NameServerConfig::new(
        SocketAddrV4::new(Ipv4Addr::new(198, 41, 0, 4), 53).into(),
        Protocol::Udp,
    ));

    let recursor = Recursor::builder()
        .dnssec_policy(DnssecPolicy::ValidateWithStaticKey {
            trust_anchor: Some(Arc::new(TrustAnchor::default())),
        })
        .build(config)?;

    let lookup = tokio_test::block_on(recursor.resolve(
        Query::query(fqdn, record_type),
        Instant::now(),
        false,
    ))?;

    println!("{lookup:#?}");

    Ok(())
}

Relevant part of the logs / output:

; edns version: 0 dnssec_ok: true z_flags: 0x0000 max_payload: 1232 opts: 0
; query
;; no-ds.extended-dns-errors.com. IN DS
; answers 0
; nameservers 4
extended-dns-errors.com. 600 IN SOA ns1.extended-dns-errors.com. hostmaster.extended-dns-errors.com. 20240619 21600 3600 604800 86400
extended-dns-errors.com. 600 IN RRSIG SOA RSASHA256 2 600 1733802864 1718799264 58644 extended-dns-errors.com. R3hYnbC4zj0a+v3u24ALmRnC9muVTv9lIyCsfyj87SvaLP9B8iSxdk7/CyEo6VEdCnYhxufRKjfU+9sAQv6i8CZaMXYaNv7ErsjnxJzTc6Y0nXvBd1V92sQU8zOcJgRSPA0juj9QPjW12kO3vcVSaSoCaXqhthiRVad0IUq7xZuudNy6U6FiUkTG3gFudNCNvWb52Cz3JRhk+TJHX2nmQ/dl20jXDpKBrTeWtHes40ZATTFK/WcUd6UaJdIzsod2XMEwDLqcSk7UhOWvobB030ivqSrUtxxHMkTpHy8kUZd5M06nRrwkCvMaqYGfFHy6c46j5t2Y0faTBv8KpBp5bw==
FQHQGB3QHEGOI7GA9OENEP0LOPTBJ128.extended-dns-errors.com. 600 IN NSEC3 1 0 0 54D79E5FEA7C4677 QB7FHUG63CBIJ3ZAFEE4N3PBWMNN2QBM NS
FQHQGB3QHEGOI7GA9OENEP0LOPTBJ128.extended-dns-errors.com. 600 IN RRSIG NSEC3 RSASHA256 3 600 1733802864 1718799264 58644 extended-dns-errors.com. Io5FuKW2EiLeDTmthyv+/DKgtGwC6KYdLpNqUMzu7j96YctlQDq446F7WlmLCiK9vhxWr6Qs3XOUIiLfl5MLgaM7gOrDfXHlwmr/OVJZRqSj3GaK6rP9ZchepkWYZeSRnnTf/1yjCAmFlLLcufihgsuv18oOhrg25WAIxGfhUSUODIPgLzmRb1sY8DZms4ekZnDw6Q3pB6W0qwJ5TPNDj4uO+ZdvOmONvp0cPqNJN+YdYUW5X596XJPYtISrgf5UyaFTo7TTF6D64wwosN6lRNRywWtWvbFddwr/kBRtbAEBNFZPs5STdmg1qOGs7nWNXz7eFMGgzReQM3WVk0eDFQ==
; additionals 1

Error: Error { kind: Proto(ProtoError { kind: Msg("forward response: extended-dns-errors.com.") }) }

Expected behavior
Although it is true that extended-dns-errors.com. returns a SOA record, its response also contains NSEC3 records that indicate that the DS record does not exist. The NSEC* logic should have precedence here and ProtoErrorKind::Nsec should be returned instead. Without that information, other parts of the code won't be able to correctly identify this as an Insecure scenario (see #2395)

System:

  • OS: Ubuntu
  • Architecture: x86_64
  • Version 22.04
  • rustc version: 1.81

Version:
Crate: hickory-recursor
Version: a792b8288b

Additional context
This was tested without #2313 but the bug appears to be in the recursor crate not in proto so, as of now, that PR won't fix this bug.

no-ds.extended-dns-errors.com. uses NSEC3 to deny existence but this issue is also present when NSEC is used. I have tested /confirmed that locally and will submit a conformance test that hits that code path shortly.

cc @pvdrz @listochkin

Originally created by @japaric on GitHub (Sep 6, 2024). Original GitHub issue: https://github.com/hickory-dns/hickory-dns/issues/2435 **Describe the bug** What the title says **To Reproduce** Run the following code: ``` rust // # Cargo.toml // hickory-recursor.features = ["dnssec-ring"] // tokio-test = "0.4.4" // tracing-subscriber = "0.3.18" use std::{ env, error::Error, net::{Ipv4Addr, SocketAddrV4}, sync::Arc, time::Instant, }; use hickory_recursor::{ proto::{ op::Query, rr::{dnssec::TrustAnchor, RecordType}, }, resolver::{ config::{NameServerConfigGroup, Protocol}, Name, }, DnssecPolicy, NameServerConfig, Recursor, }; use tracing_subscriber::{filter, prelude::*}; fn main() -> Result<(), Box<dyn Error>> { let mut args = env::args().skip(1); let [Some(record_type), Some(fqdn), None] = [args.next(), args.next(), args.next()] else { return Err("expected exactly two arguments".into()); }; let record_type: RecordType = record_type.parse()?; let fqdn = Name::from_ascii(fqdn)?; let stdout_log = tracing_subscriber::fmt::layer().pretty(); tracing_subscriber::registry() .with(stdout_log.with_filter(filter::LevelFilter::TRACE)) .init(); let mut config = NameServerConfigGroup::default(); // add `a.root-servers.net` config.push(NameServerConfig::new( SocketAddrV4::new(Ipv4Addr::new(198, 41, 0, 4), 53).into(), Protocol::Udp, )); let recursor = Recursor::builder() .dnssec_policy(DnssecPolicy::ValidateWithStaticKey { trust_anchor: Some(Arc::new(TrustAnchor::default())), }) .build(config)?; let lookup = tokio_test::block_on(recursor.resolve( Query::query(fqdn, record_type), Instant::now(), false, ))?; println!("{lookup:#?}"); Ok(()) } ``` Relevant part of the logs / output: ``` text ; edns version: 0 dnssec_ok: true z_flags: 0x0000 max_payload: 1232 opts: 0 ; query ;; no-ds.extended-dns-errors.com. IN DS ; answers 0 ; nameservers 4 extended-dns-errors.com. 600 IN SOA ns1.extended-dns-errors.com. hostmaster.extended-dns-errors.com. 20240619 21600 3600 604800 86400 extended-dns-errors.com. 600 IN RRSIG SOA RSASHA256 2 600 1733802864 1718799264 58644 extended-dns-errors.com. R3hYnbC4zj0a+v3u24ALmRnC9muVTv9lIyCsfyj87SvaLP9B8iSxdk7/CyEo6VEdCnYhxufRKjfU+9sAQv6i8CZaMXYaNv7ErsjnxJzTc6Y0nXvBd1V92sQU8zOcJgRSPA0juj9QPjW12kO3vcVSaSoCaXqhthiRVad0IUq7xZuudNy6U6FiUkTG3gFudNCNvWb52Cz3JRhk+TJHX2nmQ/dl20jXDpKBrTeWtHes40ZATTFK/WcUd6UaJdIzsod2XMEwDLqcSk7UhOWvobB030ivqSrUtxxHMkTpHy8kUZd5M06nRrwkCvMaqYGfFHy6c46j5t2Y0faTBv8KpBp5bw== FQHQGB3QHEGOI7GA9OENEP0LOPTBJ128.extended-dns-errors.com. 600 IN NSEC3 1 0 0 54D79E5FEA7C4677 QB7FHUG63CBIJ3ZAFEE4N3PBWMNN2QBM NS FQHQGB3QHEGOI7GA9OENEP0LOPTBJ128.extended-dns-errors.com. 600 IN RRSIG NSEC3 RSASHA256 3 600 1733802864 1718799264 58644 extended-dns-errors.com. Io5FuKW2EiLeDTmthyv+/DKgtGwC6KYdLpNqUMzu7j96YctlQDq446F7WlmLCiK9vhxWr6Qs3XOUIiLfl5MLgaM7gOrDfXHlwmr/OVJZRqSj3GaK6rP9ZchepkWYZeSRnnTf/1yjCAmFlLLcufihgsuv18oOhrg25WAIxGfhUSUODIPgLzmRb1sY8DZms4ekZnDw6Q3pB6W0qwJ5TPNDj4uO+ZdvOmONvp0cPqNJN+YdYUW5X596XJPYtISrgf5UyaFTo7TTF6D64wwosN6lRNRywWtWvbFddwr/kBRtbAEBNFZPs5STdmg1qOGs7nWNXz7eFMGgzReQM3WVk0eDFQ== ; additionals 1 Error: Error { kind: Proto(ProtoError { kind: Msg("forward response: extended-dns-errors.com.") }) } ``` **Expected behavior** Although it is true that `extended-dns-errors.com.` returns a SOA record, its response also contains NSEC3 records that indicate that the DS record does not exist. The NSEC* logic should have precedence here and `ProtoErrorKind::Nsec` should be returned instead. Without that information, [other parts of the code](https://github.com/hickory-dns/hickory-dns/blob/9ad0cf2590ee6fd5a2ac0237fa417b9b30555bea/crates/proto/src/xfer/dnssec_dns_handle.rs#L560-L572) won't be able to correctly identify this as an Insecure scenario (see #2395) **System:** - OS: Ubuntu - Architecture: x86_64 - Version 22.04 - rustc version: 1.81 **Version:** Crate: `hickory-recursor` Version: a792b8288b3cc5ed10c194d4980961ee4d4c0781 **Additional context** This was tested without #2313 but the bug appears to be in the `recursor` crate not in `proto` so, as of now, that PR won't fix this bug. `no-ds.extended-dns-errors.com.` uses NSEC3 to deny existence but this issue is also present when NSEC is used. I have tested /confirmed that locally and will submit a conformance test that hits that code path shortly. cc @pvdrz @listochkin
Author
Owner

@japaric commented on GitHub (Sep 6, 2024):

I have tested /confirmed that locally and will submit a conformance test that hits that code path shortly.

#2436 hits the ErrorKind::Forward path / issue using NSEC instead of NSEC3. that can be observed in the resolver logs.

<!-- gh-comment-id:2333942729 --> @japaric commented on GitHub (Sep 6, 2024): > I have tested /confirmed that locally and will submit a conformance test that hits that code path shortly. #2436 hits the `ErrorKind::Forward` path / issue using NSEC instead of NSEC3. that can be observed in the resolver logs.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/hickory-dns#990
No description provided.