]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
resolve: llmnr: fix never hit condition 22274/head
authorYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 28 Jan 2022 00:29:59 +0000 (09:29 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 28 Jan 2022 00:37:50 +0000 (09:37 +0900)
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.

src/resolve/resolved-dns-stream.c
src/resolve/resolved-dns-stream.h
src/resolve/resolved-llmnr.c

index d16ea95d434534b0b023a53c322d9f9637843024..cf9d1a9d5e5317902fea00d73b9ead39fd588e8f 100644 (file)
@@ -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) {
index fedbab2da2cadea6fad0fe315d3d3a68898cc039..1c606365cdcebb4b9183190efdc0d26033a77ee7 100644 (file)
@@ -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;
index b4e551c219ddec303401ac84fad5af2e840e7268..76e42940f45e13a9f95f30de2d0d33d955aeae08 100644 (file)
@@ -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) {