#include "alloc-util.h"
#include "fd-util.h"
#include "io-util.h"
+#include "macro.h"
#include "missing_network.h"
#include "resolved-dns-stream.h"
#include "resolved-manager.h"
}
static int dns_stream_update_io(DnsStream *s) {
- int f = 0;
+ uint32_t f = 0;
assert(s);
set_size(s->queries) < DNS_QUERIES_PER_STREAM)
f |= EPOLLIN;
+ s->requested_events = f;
+
#if ENABLE_DNS_OVER_TLS
/* For handshake and clean closing purposes, TLS can override requested events */
if (s->dnstls_events != 0)
assert(iov);
#if ENABLE_DNS_OVER_TLS
- if (s->encrypted && !(flags & DNS_STREAM_WRITE_TLS_DATA)) {
- ssize_t ss;
- size_t i;
-
- m = 0;
- for (i = 0; i < iovcnt; i++) {
- ss = dnstls_stream_write(s, iov[i].iov_base, iov[i].iov_len);
- if (ss < 0)
- return ss;
-
- m += ss;
- if (ss != (ssize_t) iov[i].iov_len)
- continue;
- }
- } else
+ if (s->encrypted && !(flags & DNS_STREAM_WRITE_TLS_DATA))
+ return dnstls_stream_writev(s, iov, iovcnt);
#endif
+
if (s->tfo_salen > 0) {
struct msghdr hdr = {
.msg_iov = (struct iovec*) iov,
}
static int on_stream_timeout(sd_event_source *es, usec_t usec, void *userdata) {
- DnsStream *s = userdata;
+ DnsStream *s = ASSERT_PTR(userdata);
+ return dns_stream_complete(s, ETIMEDOUT);
+}
+
+static DnsPacket *dns_stream_take_read_packet(DnsStream *s) {
assert(s);
- return dns_stream_complete(s, ETIMEDOUT);
+ /* Note, dns_stream_update() should be called after this is called. When this is called, the
+ * stream may be already full and the EPOLLIN flag is dropped from the stream IO event source.
+ * Even this makes a room to read in the stream, this does not call dns_stream_update(), hence
+ * EPOLLIN flag is not set automatically. So, to read further packets from the stream,
+ * dns_stream_update() must be called explicitly. Currently, this is only called from
+ * on_stream_io(), and there dns_stream_update() is called. */
+
+ if (!s->read_packet)
+ return NULL;
+
+ if (s->n_read < sizeof(s->read_size))
+ return NULL;
+
+ if (s->n_read < sizeof(s->read_size) + be16toh(s->read_size))
+ return NULL;
+
+ s->n_read = 0;
+ return TAKE_PTR(s->read_packet);
}
static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
}
}
- if ((revents & (EPOLLIN|EPOLLHUP|EPOLLRDHUP)) &&
- (!s->read_packet ||
- s->n_read < sizeof(s->read_size) + s->read_packet->size)) {
+ while ((revents & (EPOLLIN|EPOLLHUP|EPOLLRDHUP)) &&
+ (!s->read_packet ||
+ s->n_read < sizeof(s->read_size) + s->read_packet->size)) {
if (s->n_read < sizeof(s->read_size)) {
ssize_t ss;
if (ss < 0) {
if (!ERRNO_IS_TRANSIENT(ss))
return dns_stream_complete(s, -ss);
+ break;
} else if (ss == 0)
return dns_stream_complete(s, ECONNRESET);
else {
s->read_packet->family = s->peer.sa.sa_family;
s->read_packet->ttl = s->ttl;
s->read_packet->ifindex = s->ifindex;
- s->read_packet->timestamp = now(clock_boottime_or_monotonic());
+ s->read_packet->timestamp = now(CLOCK_BOOTTIME);
if (s->read_packet->family == AF_INET) {
s->read_packet->sender.in = s->peer.in.sin_addr;
if (ss < 0) {
if (!ERRNO_IS_TRANSIENT(ss))
return dns_stream_complete(s, -ss);
+ break;
} else if (ss == 0)
return dns_stream_complete(s, ECONNRESET);
else
s->n_read += ss;
}
- /* Are we done? If so, disable the event source for EPOLLIN */
- if (s->n_read >= sizeof(s->read_size) + be16toh(s->read_size)) {
- /* If there's a packet handler
- * installed, call that. Note that
- * this is optional... */
- if (s->on_packet) {
- r = s->on_packet(s);
- if (r < 0)
- return r;
- }
+ /* Are we done? If so, call the packet handler and re-enable EPOLLIN for the
+ * event source if necessary. */
+ _cleanup_(dns_packet_unrefp) DnsPacket *p = dns_stream_take_read_packet(s);
+ if (p) {
+ assert(s->on_packet);
+ r = s->on_packet(s, p);
+ if (r < 0)
+ return r;
r = dns_stream_update_io(s);
if (r < 0)
return dns_stream_complete(s, -r);
+
+ s->packet_received = true;
+
+ /* If we just disabled the read event, stop reading */
+ if (!FLAGS_SET(s->requested_events, EPOLLIN))
+ break;
}
}
}
- /* 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))
+ /* Complete the stream if finished reading and writing one packet, and there's nothing
+ * else left to write. */
+ if (s->type == DNS_STREAM_LLMNR_SEND && s->packet_received &&
+ !FLAGS_SET(s->requested_events, EPOLLOUT))
return dns_stream_complete(s, 0);
/* If we did something, let's restart the timeout event source */
DnsProtocol protocol,
int fd,
const union sockaddr_union *tfo_address,
+ int (on_packet)(DnsStream*, DnsPacket*),
+ int (complete)(DnsStream*, int), /* optional */
usec_t connect_timeout_usec) {
_cleanup_(dns_stream_unrefp) DnsStream *s = NULL;
assert(protocol >= 0);
assert(protocol < _DNS_PROTOCOL_MAX);
assert(fd >= 0);
+ assert(on_packet);
if (m->n_dns_streams[type] > DNS_STREAMS_MAX)
return -EBUSY;
*s = (DnsStream) {
.n_ref = 1,
- .fd = -1,
+ .fd = -EBADF,
.protocol = protocol,
.type = type,
};
r = sd_event_add_time_relative(
m->event,
&s->timeout_event_source,
- clock_boottime_or_monotonic(),
+ CLOCK_BOOTTIME,
connect_timeout_usec, 0,
on_stream_timeout, s);
if (r < 0)
s->manager = m;
s->fd = fd;
+ s->on_packet = on_packet;
+ s->complete = complete;
if (tfo_address) {
s->tfo_address = *tfo_address;
return dns_stream_update_io(s);
}
-DnsPacket *dns_stream_take_read_packet(DnsStream *s) {
- assert(s);
-
- if (!s->read_packet)
- return NULL;
-
- if (s->n_read < sizeof(s->read_size))
- return NULL;
-
- if (s->n_read < sizeof(s->read_size) + be16toh(s->read_size))
- return NULL;
-
- s->n_read = 0;
- return TAKE_PTR(s->read_packet);
-}
-
void dns_stream_detach(DnsStream *s) {
assert(s);