From: Yu Watanabe Date: Fri, 28 Jan 2022 00:29:59 +0000 (+0900) Subject: resolve: llmnr: fix never hit condition X-Git-Tag: v251-rc1~409^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=refs%2Fpull%2F22274%2Fhead;p=thirdparty%2Fsystemd.git resolve: llmnr: fix never hit condition Previously, the condition in on_stream_io_impl() never hit, as the read packet is always taken from the stream in the few lines above. Instead of the dns_stream_complete() under the condition, the stream is unref()ed in the on_packet callback for LLMNR stream, unlike the other on_packet callbacks. That's quite tricky. Also, potentially, the stream may still have queued packets to write. This fix the condition, and drops the unref() in the on_packet callback. C.f. https://github.com/systemd/systemd/pull/22274#issuecomment-1023708449. Closes #22266. --- diff --git a/src/resolve/resolved-dns-stream.c b/src/resolve/resolved-dns-stream.c index d16ea95d434..cf9d1a9d5e5 100644 --- a/src/resolve/resolved-dns-stream.c +++ b/src/resolve/resolved-dns-stream.c @@ -446,17 +446,25 @@ static int on_stream_io_impl(DnsStream *s, uint32_t revents) { r = dns_stream_update_io(s); if (r < 0) return dns_stream_complete(s, -r); + + s->packet_received = true; } } } - /* Call "complete" callback if finished reading and writing one packet, and there's nothing else left - * to write. */ - if (s->type == DNS_STREAM_LLMNR_SEND && - (s->write_packet && s->n_written >= sizeof(s->write_size) + s->write_packet->size) && - ordered_set_isempty(s->write_queue) && - (s->read_packet && s->n_read >= sizeof(s->read_size) + s->read_packet->size)) - return dns_stream_complete(s, 0); + if (s->type == DNS_STREAM_LLMNR_SEND && s->packet_received) { + uint32_t events; + + /* Complete the stream if finished reading and writing one packet, and there's nothing + * else left to write. */ + + r = sd_event_source_get_io_events(s->io_event_source, &events); + if (r < 0) + return r; + + if (!FLAGS_SET(events, EPOLLOUT)) + return dns_stream_complete(s, 0); + } /* If we did something, let's restart the timeout event source */ if (progressed && s->timeout_event_source) { diff --git a/src/resolve/resolved-dns-stream.h b/src/resolve/resolved-dns-stream.h index fedbab2da2c..1c606365cdc 100644 --- a/src/resolve/resolved-dns-stream.h +++ b/src/resolve/resolved-dns-stream.h @@ -60,6 +60,7 @@ struct DnsStream { int ifindex; uint32_t ttl; bool identified; + bool packet_received; /* At least one packet is received. Used by LLMNR. */ /* only when using TCP fast open */ union sockaddr_union tfo_address; diff --git a/src/resolve/resolved-llmnr.c b/src/resolve/resolved-llmnr.c index b4e551c219d..76e42940f45 100644 --- a/src/resolve/resolved-llmnr.c +++ b/src/resolve/resolved-llmnr.c @@ -294,7 +294,6 @@ static int on_llmnr_stream_packet(DnsStream *s, DnsPacket *p) { } else log_debug("Invalid LLMNR TCP packet, ignoring."); - dns_stream_unref(s); return 0; } @@ -311,8 +310,7 @@ static int on_llmnr_stream(sd_event_source *s, int fd, uint32_t revents, void *u return -errno; } - /* We don't configure a "complete" handler here, we rely on the default handler than simply drops the - * reference to the stream, thus freeing it */ + /* We don't configure a "complete" handler here, we rely on the default handler, thus freeing it */ r = dns_stream_new(m, &stream, DNS_STREAM_LLMNR_RECV, DNS_PROTOCOL_LLMNR, cfd, NULL, on_llmnr_stream_packet, NULL, DNS_STREAM_DEFAULT_TIMEOUT_USEC); if (r < 0) {