-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fixed len computation when size just goes beyond 14 bits #668
Fixed len computation when size just goes beyond 14 bits #668
Conversation
Fun times trying to make 2 diff. algorithms do the same thing: This now fails in travis:
You PR looks sane btw. |
Ok, I undestand fully the problem and have a fix: Basically, the function compressionLenHelper should take as parameter the current len of message to guess whether compression is possible or not: When you arrive around 16384, lets say 16380, if you plan to add those entries: let's say you already added those labels:
I made a patch, but I am a bit lost regarding the overhead of DNS structure to compute really the overhead of labels... You probably know the answer better than I do and how to fix my offsets :-) I you can have a look Regards |
@miekg I added a new test that does padding at begin of DNS query to ensure we properly handle all cases, it should fail for now |
Ah, I see, because I wonder if all that extra complexity is worth it though, right now |
Of course in the range 16K <-> 64K this adds up. hmm |
@miekg yes... :( |
Ok I similar fix may or may not be needed in |
On 11 Apr 2018, at 4:45 pm, Miek Gieben ***@***.***> wrote:
I wonder if all that extra complexity is worth it though, right now Len() will over estimate, so this boils down to X missing RRs (compared to Pack())
This use case would probably be better handled by an API that allowed incrementally building a wire format message. Maybe something to consider for v2?
|
[ Quoting <notifications@github.com> in "Re: [miekg/dns] Fixed len computati..." ]
> On 11 Apr 2018, at 4:45 pm, Miek Gieben ***@***.***> wrote:
>
> I wonder if all that extra complexity is worth it though, right now Len() will over estimate, so this boils down to X missing RRs (compared to Pack())
This use case would probably be better handled by an API that allowed incrementally building a wire format message. Maybe something to consider for v2?
It would, but one of the reason I like the current API is that you can just do
`ns.Extra = append(ns.Extra, xx)`. Having a lib that surfaces the wireformat is
entirely different (and probably harder to use).
Now because DNS is silly protocol it is hard to not seep through some of those
underlaying details.
|
Codecov Report
@@ Coverage Diff @@
## master #668 +/- ##
==========================================
+ Coverage 58.05% 58.07% +0.01%
==========================================
Files 37 37
Lines 9999 10072 +73
==========================================
+ Hits 5805 5849 +44
- Misses 3144 3174 +30
+ Partials 1050 1049 -1
Continue to review full report at Codecov.
|
msg.go
Outdated
func compressionLenHelper(c map[string]int, s string) { | ||
// Put the parts of the name in the compression map, return the size added in payload | ||
func compressionLenHelper(c map[string]int, s string, currentLen int) int { | ||
addedSize := 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we can optimize this more; if we have seen s
here, there shouldn't be a need a spit it at all. WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, probably, split might be reused... but since the review is already big (I mean for the bug we consider), I did prefer to limit as much as possible the changes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in next patch version
msg.go
Outdated
func compressionLenHelper(c map[string]int, s string) { | ||
// Put the parts of the name in the compression map, return the size added in payload | ||
func compressionLenHelper(c map[string]int, s string, currentLen int) int { | ||
addedSize := 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
addedLen ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, my comment is probably not clear: compressionLenHelper return the size added to global DNS response (thus the addedSize name).
Considering adding "host1.example.com" with a compression map having already:
"host0.example.com" (and thus "example.com" as well as "com")
it is supposed to return 2+ len("host0") + 2 (pointer to "example.com" label
Do you have a suggestion to have clearer name?
msg.go
Outdated
c[pref] = len(pref) | ||
lenAdded := len(pref) | ||
numLabelsBefore := len(lbs) - j - 1 | ||
offsetOfLabel := currentLen + len(s) - len(pref) + numLabelsBefore*2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't fully understand this yet... why the *2 ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
numLabelsBefore*2 => size of label pointers (since their size is 2 bytes => 2 times)
[ Quoting <notifications@github.com> in "Re: [miekg/dns] Fixed len computati..." ]
pierresouchay commented on this pull request.
> }
-// Put the parts of the name in the compression map.
-func compressionLenHelper(c map[string]int, s string) {
+// Put the parts of the name in the compression map, return the size added in payload
+func compressionLenHelper(c map[string]int, s string, currentLen int) int {
+ addedSize := 0
Well, my comment is probably not clear: compressionLenHelper return the size added to global DNS response (thus the addedSize name).
Considering adding "host1.example.com" with a compression map having already:
"host0.example.com" (and thus "example.com" as well as "com")
it is supposed to return 2+ len("host0") + 2 (pointer to "example.com" label
Ok, I see where you're heading with this, you're not adding when we shouldn't
but then the callers does now how much saving there where.
But `addedSizeBytes += 2 + lenAdded` in a loop can't be right (I think).
I need to take a step back here, because I find the logic hard to follow. There
is also bunch of constants that we should probably name (2 and 6 in the current
PR).
Let's see: the current code works, unless, precisely on the border of the 14
bits we have name that can be compressed, but the end of the name overflows the
14 bits; i.e. we should use any labels before that to compress as well (if we
haven't seen them).
I.e if "len < 2<<13" and "len+x > 2<<13", we could just check for that case and
split the name our selves.
Or did you find more issues with the current code?
/Miek
…--
Miek Gieben
|
@miekg Ok for the constants... About the change, I tried several approaches (including adding another function within compressionLenSlice that did check whether currentLen + r.len() did overflow 14bits, but the code was even more unreadable. The idea I took is that when we add compression, we also add some data in the global payload, so, all function adding compression (compressionLenHelper / compressionLenSearch should return how much data size they added themselves. The algorithm is weird, because there are actually 2 parts:
I first tried to decompose it, but it was not easy, hence the addedSize stuff => but I agree having both ways is crap. (But method for length computation were not present - hence the +6 from nowhere) Of course, you are the boss, so decide what you really want, but for now, among all my tries, this sounds like the easier to understand |
[ Quoting <notifications@github.com> in "Re: [miekg/dns] Fixed len computati..." ]
@miekg Ok for the constants...
About the change, I tried several approaches (including adding another function within compressionLenSlice that did check whether currentLen + r.len() did overflow 14bits, but the code was even more unreadable.
The idea I took is that when we add compression, we also add some data in the global payload, so, all function adding compression (compressionLenHelper / compressionLenSearch should return how much data size they added themselves.
The algorithm is weird, because there are actually 2 parts:
* one that does decrease the size of x (with the += (1 - k) ) Note that the decrease is weird as well (why += 1 - k ? => took me a while to understand why)
* the other that increase it (sound like a more common approach since the size is in bytes)
I first tried to decompose it, but it was not easy, hence the addedSize stuff => but I agree having both ways is crap. (But method for length computation were not present - hence the +6 from nowhere)
Of course, you are the boss, so decide what you really want, but for now, among all my tries, this sounds like the easier to understand
Thanks for your mail. yeah, while thinking about a simple change is occurred
that this must also be expanded to the compressionLenTypeHelper.
Let me whip up some (internal) branches to understand this code better, so I can
give a better code review.
|
I don't think I can come up with something better - this seems to work :) I give a quick glance over one final time and then we should be good to go. |
@miekg Great, I'll update Consul dependency with it as soon as a release is done to re-enable compression in it! Thank you |
msg.go
Outdated
for _, r := range rs { | ||
if r == nil { | ||
continue | ||
} | ||
// track this length, and the global length in len, while taking compression into account for both. | ||
// TmpLen is to track len of record at 14bits boudaries, 6 bytes is the raw overhead | ||
tmpLen := lenp + 6 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still have trouble understanding where this +6 comes from; what's it this? What bytes of the message are we skipping over here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't have access to the code easily from my phone, but I would say the signaling of a new record? (I am not at all an expert in DNS, I just read RFC part related to compression for this patch)
@miekg I had not the time to look further, but I think I know why we have to add 6 and not 10 or something similar. Actually 6 is a constant regarding the start of message, erased at each iteration since we take x (we discard 6 and all added len at each iteration) What it could mean is just that we already added some len of the DNS message (lets say 4 bytes if you expected the constant to be 10) before entering this section of the code and those bytes are actually supposed to be AFTER this computation. I'll try to have a look tomorrow |
@miekg I finally go this right I think I changed the compression map used by Len() to have the same semantics (aka the offset of label) as the one used in Pack() to be able to add unit tests properly. With this one, I change a few things, but everything is well tested now and "Fake" (aka used by Len()) has the same values as Pack() -> so I am confident compression will work the same way around 14bits |
[ Quoting <notifications@github.com> in "Re: [miekg/dns] Fixed len computati..." ]
@miekg I finally go this right I think
I changed the compression map used by Len() to have the same semantics (aka the offset of label) as the one used in Pack() to be able to add unit tests properly.
With this one, I change a few things, but everything is well tested now and "Fake" (aka used by Len()) has the same values as Pack() -> so I am confident compression will work the same way around 14bits
Ok, thanks. I will take a look after kubecon, so next weekend probably.
|
@miekg have a nice kubecon! |
@miekg does the patch is OK for you ? Since now compression map contains the same exact information as the one used for real packing, I am quite confident the result will always be the same since I test many permutations with it in test TestCompareCompressionMapsForSRV(), we now are for sure that any change in computation is equivalent in both Pack() and Len() since offset are all equal. In order to test this, I had to create a new packBufferWithCompressionMap() function that takes into parameter the compression map so we can compare it easily both maps easily. The patch now does not take any magic offset anymore and is tested with a very large combination of lengths and offset, so I am pretty confident it works well. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks very sane. Actual code changes (apart from tests) are not too massive. Some nits that i'll brush up.
Thanks for pushing this through!
@miekg Thank you, I'll be able to fully re-enable compression in Consul then once a new release is created :) |
[ Quoting <notifications@github.com> in "Re: [miekg/dns] Fixed len computati..." ]
@miekg Thank you, I'll be able to fully re-enable compression in Consul then once a new release is created :)
Pushed 1.0.7
|
Fixes #663
Since offset of compression has to start within the first 14 bits, we don't want to take it into account before trying to compress but once the length computation has been performed