Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20230704160700.GC4163@brightrain.aerifal.cx>
Date: Tue, 4 Jul 2023 12:07:00 -0400
From: Rich Felker <dalias@...c.org>
To: Hamish Forbes <hamish.forbes@...il.com>
Cc: musl@...ts.openwall.com
Subject: Re: DNS answer buffer is too small

On Tue, Jul 04, 2023 at 04:19:16PM +1200, Hamish Forbes wrote:
> On Tue, 4 Jul 2023 at 15:29, Rich Felker <dalias@...c.org> wrote:
> > Your report here is missing the motivation for why you might care to
> > have more than 768 bytes of response, which, as I understand it, is
> > because of CNAME chains.
> 
> Yes, sorry, a response can contain a chain of CNAMEs like
> 
> a.example.com 10 IN CNAME b.example.com.
> b.example.com 10 IN CNAME c.example.com.
> ....
> n.example.com 10 IN A 127.0.0.1.
> 
> With a max length per CNAME of 255 this could quickly blow out the 768
> byte limit.
> 
> > Otherwise, the buffer size is chosen to hold
> > the number of answer records the stub resolver is willing to accept,
> > and there is no problem.
> 
> Are you referring to the 48 MAXADDRS limit?
> That's also possibly a problem in edge cases though, isn't it?
> AIUI that was chosen as the maximum number of IP responses that can
> fit in a 512 byte UDP response.
> With TCP DNS implemented now, is that still a safe assumption?

It's as safe as it was before, and it's always been the intended
behavior. Aside from the CNAME chain issue which wasn't even realized
at the time, use of TCP for getaddrinfo is not about getting more
answers than fit in the UDP response size. It's about handling the
case where the recursive server returns a truncated response with zero
answer records instead of the max number that fit. It turned out this
could also occur with a single CNAME where both the queried name and
the CNAME target take up nearly the full 255 length.

As for why not to care about more results, getaddrinfo does not
provide a precise view of DNS space. It takes a hostname and gives you
a set of addresses you can use to attempt to connect to that host (or
bind if that name is your own, etc.). There's very little utility in
timing out more than 47 times then continuing to try more addresses
rather than just failing. "Our name resolves to 100 addresses and you
have to try all of them to find the one that works" is not a viable
configuration. (A lot of software does not even iterate and try
fallbacks at all, but only attempts to use the first one, typically
round-robin rotated by the nameserver.)

Anyway, if there are objections to this behavior, it's a completely
separate issue from handling long CNAME chains.

> It seems nuts to be returning more than 48 addresses but the DNS spec
> doesn't prevent it,
> so I bet it's being done somewhere!
> 
> This post[0] I came across while investigating all this stuff is
> somewhat related
> 
> > Long CNAME chains are rather hostile and are not guaranteed to be well
> > supported -- AIUI recursive nameservers already impose their own
> > limits on the number of redirections in a chain, though I cannot find
> > any specification in the RFCs for this behavior or suggesting a value
> > for that limit, so if you can dig up what they actually do, this would
> > be useful to know. But it seems there are chains longer than what we
> > currently support out in the wild, which most software does support.
> > So the next step is nailing down exactly what the "requirement" here
> > is, so we can figure out what's the most reasonable and least costly
> > way to do it.
> 
> Bind defaults[1] to 7, although that appears[2] to only be when it has
> to switch to another NS.
> So if the CNAME chain is all in the same zone... that's only  a depth of 1
> 
> Looks like Unbound defaults[3] to 11.
> Although it also has a slightly confusing 'target-fetch-policy' which
> looks to limit depth in a similar way to Bind, maybe...

>From my reading of your links, and

https://groups.google.com/g/comp.protocols.dns.bind/c/rXici9NvIqI

I don't think max-recursion-depth is related to CNAMEs. It's the depth
of delegation recursion. The max CNAME chain length is separate, and
in unbound terminology is the number of "restarts". Unbound's limit as
you've found is 11. BIND's is supposedly hard-coded at 16.

Assuming the recursive server uses pointers properly, max size of a
length-N CNAME chain is (N+1)*(255+epsilon). This comes out to a
little over 4k for the BIND limit, and that's assuming max-length
names with no further redundancy. I would expect the real-world need
is considerably lower than this, and that the Unbound default limit on
chain length also suffices in practice (or it wouldn't be the default
for a widely used recursive server). So, for example, using a 4k
buffer (adding a little over 3k to what we have now, which already had
enough for one CNAME) should solve the problem entirely.

Does this sound like an okay fix to you?

Rich

Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.