Nameservers and storing offsetted values
Closed this issue · 5 comments
Hello,
First, thanks for creating this!
I have two questions, I was hoping you could answer.
First, I was wondering if there was a reason you hadn't implemented nameservers
yet. It looks like it's trivially easy since the parsing is the same as parsing Answer
s, at least it seems that way to me. I did that and it worked. Is there some edge case I'm missing?
My other more important question is do you think there is a good way in the Name
struct to store the display version of the name (ie a vec of www.google.com instead of the label
slice which would be the offset value to www.google.com)?
I was able to get it work by adding a translated
field to Name
which is a Vec<u8>
but to do so, I had to remove the Copy
trait in the derive
for Name
, and I'm not sure you're ok with that. And I update the translated
variable in the scan
function where you validate the offset.
I could just convert the label into a string since you have it implemented, but I don't like the idea of having to parse the data again to convert it when we've already done the offset parsing during the validation.
Do you have thoughts on this?
I can also push up my branch, if you'd like to take a look.
Thanks!
First, I was wondering if there was a reason you hadn't implemented nameservers yet. It looks like it's trivially easy since the parsing is the same as parsing Answers, at least it seems that way to me. I did that and it worked. Is there some edge case I'm missing?
If you've done that, please provide a pull request.
I don't think there is an edge case, I just never needed it myself.
My other more important question is do you think there is a good way in the Name struct to store the display version of the name (ie a vec of www.google.com instead of the label slice which would be the offset value to www.google.com)?
I wanted to avoid memory allocations at this level.
I could just convert the label into a string since you have it implemented, but I don't like the idea of having to parse the data again to convert it when we've already done the offset parsing during the validation.
You need another level of abstraction anyway. For example, here is a wrapper structure of rotor-dns. If we decode names in dns-parser we would need to clone strings twice (or alternatively decompose structure in some non-convenient way).
What's your use case? Wouldn't you benefit from the rotor-dns instead of using dns-parser directly?
So I made a pull request here
#4
I could also add another loop to do additional records parsing, but I didn't want to deal with making a test for that.
I was also wondering if you'd be ok with me adding a separate function that essentially does the offset translation you do when implementing display
for name
but instead returns a vec of bytes.
Then in your display
impl, you could just call that and convert it into a str before writing it. The function would be something like this
pub fn read_from_offset(&self, mut offsetted_value: Vec<u8>) -> Result<Vec<u8>, Error> {
let data = self.labels;
let original = self.original;
let mut pos = 0;
loop {
let byte = data[pos];
if byte == 0 {
return Ok(offsetted_value);
} else if byte & 0b1100_0000 == 0b1100_0000 {
let off = (BigEndian::read_u16(&data[pos..pos+2])
& !0b1100_0000_0000_0000) as usize;
if pos != 0 {
offsetted_value.push(46u8) // '.' ascii value
}
return Name::scan(&original[off..], original).unwrap()
.read_from_offset(offsetted_value);
} else if byte & 0b1100_0000 == 0 {
if pos != 0 {
offsetted_value.push(46u8) // '.' ascii value
}
let end = pos + byte as usize + 1;
offsetted_value.extend_from_slice(&data[pos+1..end]);
pos = end;
continue;
} else {
unreachable!();
}
}
}
If you don't want to add that, I can just add that to my repo directly instead.
My use case is that I want to parse individual dns packets and output the answer and nameserver records to file in a specific format.
Rotor-dns seems like too much abstraction for me.
Thanks for taking the time to read this!
Hi,
Doing what you specified will incur a separate scan of the Vec<u8>
when doing String::from_utf8()
(to ensure that all characters are valid utf-8). Also for Display
trait we don't allocate anything. I.e. we write bytes dirrectly into the underlying buffer.
For your use case you can do the following (which I believe should be as efficient as the code above):
write!(&mut vec, "{}", name).unwrap()
Other hints to the code above:
- you can use
b'.'
instead of46u8
, this makes code clear without a comment - it's usually an anti-pattern to pass the vector by the argument, mutate and return it back, either pass a mutable reference to vector and return
Ok(())
or return a completely new vector.
Your suggestion seems like it should work. Thank you for the other tips as well.
When do you think the namesever parsing addtion will get released? I would like to not have to carry a local copy of your crate in my repo if possible.
Besides that, feel free to close my issue.
Just released v0.3.2. Thanks for your help!