Skip to content

Double dot (..) when using Display impl to print out record with . as its owner Name or a rdata value with Name (e.g. NS) #591

@mozzieongit

Description

@mozzieongit
  1. The domain::base::record::Record Display impl always prints the owner with a trailing dot. However, if the owner is the root, the Name/Chain will print . itself, which turns the owner name output into ...
  2. The Display impl from the domain::rdata::macros::name_type_base macro always adds a trailing dot to the Name it prints. However, if the owner is the root (like above) it will result in ...

This is only an issue when using the Display impl to print the records. When using ZoneFileFmt everything is fine.

Relevant lines in code:

  1. impl<Name, Data> fmt::Display for Record<Name, Data>: https://github.com/NLnetLabs/domain/blob/main/src/base/record.rs#L402-L410
  2. impl<N: fmt::Display> fmt::Display for $target<N> in macros: https://github.com/NLnetLabs/domain/blob/main/src/rdata/macros.rs#L1338-L1340

For example:

$ cat zone.test
$ORIGIN .
$TTL 3600   ; 1 hour
@       IN SOA  ns1 mail 1 8000 4000 20000000 4000
@       NS  example.com.
@       NS  .
@       A   127.0.0.2
$ cargo -q run --example=read-zone --all-features -- zone.test # this is a modified read-zone example that just println the record
Processing zone.test: Data loaded (0.000s).
.. 3600 IN SOA ns1. mail. 1 8000 4000 20000000 4000
.. 3600 IN NS example.com.
.. 3600 IN NS ..
.. 3600 IN A 127.0.0.2
Complete with 4 records (0.000s)

Minimal reproducible

root_name: .
root_chain: .
ScannedRecord: .. 60 IN NS ..
use bytes::Bytes;
use domain::base::iana::class::Class;
use domain::base::name::Chain;
use domain::base::name::Name;
use domain::base::name::RelativeName;
use domain::base::record::Record;
use domain::base::record::Ttl;
use domain::rdata::ZoneRecordData;

// From inplace zonefile parser
// pub type ScannedDname = Name<Bytes>; // using Name doesn't fix it
pub type ScannedDname = Chain<RelativeName<Bytes>, Name<Bytes>>;
pub type ScannedRecordData = ZoneRecordData<Bytes, ScannedDname>;
pub type ScannedRecord = Record<ScannedDname, ScannedRecordData>;

fn main() {
    // OK: This is printing correctly: `root_name: .`
    let root_name =
        Name::<Bytes>::from_octets(Bytes::from_static(b"\0")).unwrap();
    println!("root_name: {}", &root_name);

    // OK: This is printing correctly: `root_chain: .`
    let root_chain = RelativeName::<Bytes>::empty()
        .chain(Name::<Bytes>::from_octets(Bytes::from_static(b"\0")).unwrap())
        .unwrap();
    println!("root_chain: {}", &root_chain);

    // Broken example
    let rr = ScannedRecord::new(
        root_chain.clone(),
        Class::IN,
        Ttl::from_secs(60),
        ScannedRecordData::from(domain::rdata::Ns::<ScannedDname>::new(
            root_chain.clone(),
        )),
    );

    // dbg!(&rr);

    // ERROR: This prints the owner as `..` and the NS name field as `..`
    println!("ScannedRecord: {}", &rr);
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions