NLnetLabs / domain

A DNS library for Rust.

Home Page:https://nlnetlabs.nl/projects/domain/about/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

serde_json round-trip of TXT record fails with spaces

WesleyAC opened this issue · comments

Trying to round-trip a Txt record containing spaces with serde_json fails:

use domain::{base::Dname, rdata::ZoneRecordData};

type Data = ZoneRecordData<Vec<u8>, Dname<Vec<u8>>>;

fn main() {
    let a: Data = serde_json::from_str(r#"{"Txt":"test txt record"}"#).unwrap();
    println!("{:?}", a);
    let b = serde_json::to_string(&a).unwrap();
    println!("{}", b);
    let _: Data = serde_json::from_str(&b).unwrap();
}

Output:

ZoneRecordData::Txt(Txt(test\32txt\32record))
{"Txt":["test\\32txt\\32record"]}
thread 'main' panicked at src/main.rs:10:44:
called `Result::unwrap()` on an `Err` value: Error("invalid type: string \"test\\\\32txt\\\\32record\", expected a borrowed string", line: 1, column: 31)

My impression is that the TXT record wire format isn't supposed to do any sort of escaping, so it's strange to see this here.

Thank you for the report!

There’s actually two issues here.

The error you get is from the sequence deserializer expecting a borrowed string for each item – which serde_json doesn’t provide.

The gratuitous escaping stems from serializing the content of the character strings in unquoted representation format which does require white space to be escaped. That is probably unnecessary and we can instead use a simplified serialization that only escapes non-printable characters and the backslash.

Since I am currently cleaning up TXT anyway, I’ll fix both of the issues as part of the 0.10.0 release.

So this is all a wee bit annoying.

For human readable serialization formats, the TXT record data should canonically serialize as a sequence of character strings – because that is what it is. However, for deserializing, it should also accept a single string, because that is very likely what people will provide (like in the example above). Unfortunately, serde_json doesn’t follow the serde documentation and treats the type hints as absolute truth, meaning that it will reject the string if I hint at the type being a sequence. You can use deserialize_any, but that means the deserializing will not work from formats that don’t have embedded type information.

I think the right thing to do is still to use the hint at a sequence and also implement deserializing from a string, even if that means JSON deserialization from a string won’t actually work.