From a0a3b8ad3e91db5181023fceea6732eb6c6f0759 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Sun, 20 Jun 2021 22:57:54 +0100 Subject: [PATCH] Fix bug in 6860cf932baeaf1c2f09c2a58e38be189ae394de The optimisation based on the assumption that "try now points to the last domain that sorts before the query" fails in the specific edge case that the query sorts before _any_ of the server domains. Handle this case. Thanks to Xingcong Li for finding a test case for this bug. --- src/domain-match.c | 48 +++++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/src/domain-match.c b/src/domain-match.c index f7bb506..644c10f 100644 --- a/src/domain-match.c +++ b/src/domain-match.c @@ -133,7 +133,32 @@ int lookup_domain(char *qdomain, int flags, int *lowout, int *highout) else { if (low == try) - break; + { + /* try now points to the last domain that sorts before the query, so + we know that a substring of the query shorter than it is required to match, so + find the largest domain that's shorter than try. Note that just going to + try+1 is not optimal, consider searching bbb in (aaa,ccc,bb). try will point + to aaa, since ccc sorts after bbb, but the first domain that has a chance to + match is bb. So find the length of the first domain later than try which is + is shorter than it. + There's a nasty edge case when qdomain sorts before _any_ of the + server domains, where try _doesn't point_ to the last domain that sorts + before the query, since no such domain exists. In that case, the loop + exits via the rc < 0 && high == try path above and this code is + not executed. */ + ssize_t len, old = daemon->serverarray[try]->domain_len; + while (++try != daemon->serverarraysz) + { + if (old != (len = daemon->serverarray[try]->domain_len)) + { + /* crop_query must be at least one always. */ + if (qlen != len) + crop_query = qlen - len; + break; + } + } + break; + } low = try; } }; @@ -153,27 +178,6 @@ int lookup_domain(char *qdomain, int flags, int *lowout, int *highout) break; } } - else - { - /* try now points to the last domain that sorts before the query, so - we know that a substring of the query shorter than it is required to match, so - find the largest domain that's shorter than try. Note that just going to - try+1 is not optimal, consider searching bbb in (aaa,ccc,bb). try will point - to aaa, since ccc sorts after bbb, but the first domain that has a chance to - match is dd. So find the length of the first domain later than try which is - is shorter than it. */ - ssize_t len, old = daemon->serverarray[try]->domain_len; - while (++try != daemon->serverarraysz) - { - if (old != (len = daemon->serverarray[try]->domain_len)) - { - /* crop_query must be at least one always. */ - if (qlen != len) - crop_query = qlen - len; - break; - } - } - } qlen -= crop_query; if (leading_dot)