resolved: Fix DoT timeout on multiple answer records
authorJoan Bruguera <joanbrugueram@gmail.com>
Sat, 15 Jan 2022 16:33:25 +0000 (17:33 +0100)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 17 Mar 2022 17:36:10 +0000 (02:36 +0900)
commit88b4e8f74ed981000ded8e23ead930a6f68eebc8
tree6df87283b1437104b7f72c64853ea23d500b16ad
parentd5b871bdfe0585f44f87beb0ea661b46bd9eb122
resolved: Fix DoT timeout on multiple answer records

When sending multiple DNS questions to a DNS-over-TLS server (e.g. a question
for A and AAAA records, as is typical) on the same session, the server may
answer to each question in a separate TLS record, but it may also aggregate
multiple answers in a single TLS record.
(Some servers do this very often (e.g. Cloudflare 1.0.0.1), some do it sometimes
(e.g. Google 8.8.8.8) and some seem to never do it (e.g. Quad9 9.9.9.10)).

Both cases should be handled equivalently, as the byte stream is the same, but
when multiple answers came in a single TLS record, usually the first answer was
processed, but the second answer was entirely ignored, which caused a 10s delay
until the resolution timed out and the missing question was retried.
This can be reproduced by configuring one of the offending server and running
`resolvectl query google.com --cache=no` a few times.

To be notified of incoming data, systemd-resolved listens to `EPOLLIN` events
on the underlying socket. However, when DNS-over-TLS is used, the TLS library
(OpenSSL or GnuTLS) may read and buffer the entire TLS record when reading the
first answer, so usually no further `EPOLLIN` events will be generated, and the
second answer will never be processed.

To avoid this, if there's buffered TLS data, generate a "fake" EPOLLIN event.
This is hacky, but it makes this case transparent to the rest of the IO code.

(cherry picked from commit 2aaf6bb6e99b0f2bd73e0c49bef9e11a2844bf1a)
src/resolve/resolved-dns-stream.c
src/resolve/resolved-dnstls-gnutls.c
src/resolve/resolved-dnstls-openssl.c
src/resolve/resolved-dnstls.h