Merge pull request #11055 from poettering/resolved-close-fix

a number of resolved fixes
This commit is contained in:
Lennart Poettering 2018-12-08 00:23:43 +01:00 committed by GitHub
commit 99b5b0d0ef
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 174 additions and 100 deletions

View file

@ -43,16 +43,18 @@ int dns_server_new(
return -E2BIG;
}
s = new0(DnsServer, 1);
s = new(DnsServer, 1);
if (!s)
return -ENOMEM;
s->n_ref = 1;
s->manager = m;
s->type = type;
s->family = family;
s->address = *in_addr;
s->ifindex = ifindex;
*s = (DnsServer) {
.n_ref = 1,
.manager = m,
.type = type,
.family = family,
.address = *in_addr,
.ifindex = ifindex,
};
dns_server_reset_features(s);
@ -101,7 +103,7 @@ int dns_server_new(
static DnsServer* dns_server_free(DnsServer *s) {
assert(s);
dns_stream_unref(s->stream);
dns_server_unref_stream(s);
#if ENABLE_DNS_OVER_TLS
dnstls_server_free(s);
@ -156,6 +158,9 @@ void dns_server_unlink(DnsServer *s) {
if (s->manager->current_dns_server == s)
manager_set_dns_server(s->manager, NULL);
/* No need to keep a default stream around anymore */
dns_server_unref_stream(s);
dns_server_unref(s);
}
@ -824,6 +829,9 @@ void dns_server_reset_features(DnsServer *s) {
s->warned_downgrade = false;
dns_server_reset_counters(s);
/* Let's close the default stream, so that we reprobe with the new features */
dns_server_unref_stream(s);
}
void dns_server_reset_features_all(DnsServer *s) {
@ -884,6 +892,20 @@ void dns_server_dump(DnsServer *s, FILE *f) {
yes_no(s->packet_rrsig_missing));
}
void dns_server_unref_stream(DnsServer *s) {
DnsStream *ref;
assert(s);
/* Detaches the default stream of this server. Some special care needs to be taken here, as that stream and
* this server reference each other. First, take the stream out of the server. It's destructor will check if it
* is registered with us, hence let's invalidate this separatly, so that it is already unregistered. */
ref = TAKE_PTR(s->stream);
/* And then, unref it */
dns_stream_unref(ref);
}
static const char* const dns_server_type_table[_DNS_SERVER_TYPE_MAX] = {
[DNS_SERVER_SYSTEM] = "system",
[DNS_SERVER_FALLBACK] = "fallback",

View file

@ -54,6 +54,8 @@ struct DnsServer {
int ifindex; /* for IPv6 link-local DNS servers */
char *server_string;
/* The long-lived stream towards this server. */
DnsStream *stream;
#if ENABLE_DNS_OVER_TLS
@ -149,3 +151,5 @@ void dns_server_reset_features(DnsServer *s);
void dns_server_reset_features_all(DnsServer *s);
void dns_server_dump(DnsServer *s, FILE *f);
void dns_server_unref_stream(DnsServer *s);

View file

@ -17,6 +17,9 @@ static void dns_stream_stop(DnsStream *s) {
s->io_event_source = sd_event_source_unref(s->io_event_source);
s->timeout_event_source = sd_event_source_unref(s->timeout_event_source);
s->fd = safe_close(s->fd);
/* Disconnect us from the server object if we are now not usable anymore */
dns_stream_detach(s);
}
static int dns_stream_update_io(DnsStream *s) {
@ -46,6 +49,8 @@ static int dns_stream_update_io(DnsStream *s) {
}
static int dns_stream_complete(DnsStream *s, int error) {
_cleanup_(dns_stream_unrefp) _unused_ DnsStream *ref = dns_stream_ref(s); /* Protect stream while we process it */
assert(s);
#if ENABLE_DNS_OVER_TLS
@ -59,6 +64,8 @@ static int dns_stream_complete(DnsStream *s, int error) {
#endif
dns_stream_stop(s);
dns_stream_detach(s);
if (s->complete)
s->complete(s, error);
else /* the default action if no completion function is set is to close the stream */
@ -193,7 +200,7 @@ static int dns_stream_identify(DnsStream *s) {
}
ssize_t dns_stream_writev(DnsStream *s, const struct iovec *iov, size_t iovcnt, int flags) {
ssize_t r;
ssize_t m;
assert(s);
assert(iov);
@ -203,13 +210,13 @@ ssize_t dns_stream_writev(DnsStream *s, const struct iovec *iov, size_t iovcnt,
ssize_t ss;
size_t i;
r = 0;
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;
r += ss;
m += ss;
if (ss != (ssize_t) iov[i].iov_len)
continue;
}
@ -223,28 +230,28 @@ ssize_t dns_stream_writev(DnsStream *s, const struct iovec *iov, size_t iovcnt,
.msg_namelen = s->tfo_salen
};
r = sendmsg(s->fd, &hdr, MSG_FASTOPEN);
if (r < 0) {
m = sendmsg(s->fd, &hdr, MSG_FASTOPEN);
if (m < 0) {
if (errno == EOPNOTSUPP) {
s->tfo_salen = 0;
r = connect(s->fd, &s->tfo_address.sa, s->tfo_salen);
if (r < 0)
if (connect(s->fd, &s->tfo_address.sa, s->tfo_salen) < 0)
return -errno;
r = -EAGAIN;
} else if (errno == EINPROGRESS)
r = -EAGAIN;
else
r = -errno;
return -EAGAIN;
}
if (errno == EINPROGRESS)
return -EAGAIN;
return -errno;
} else
s->tfo_salen = 0; /* connection is made */
} else {
r = writev(s->fd, iov, iovcnt);
if (r < 0)
r = -errno;
m = writev(s->fd, iov, iovcnt);
if (m < 0)
return -errno;
}
return r;
return m;
}
static ssize_t dns_stream_read(DnsStream *s, void *buf, size_t count) {
@ -258,7 +265,7 @@ static ssize_t dns_stream_read(DnsStream *s, void *buf, size_t count) {
{
ss = read(s->fd, buf, count);
if (ss < 0)
ss = -errno;
return -errno;
}
return ss;
@ -273,7 +280,7 @@ static int on_stream_timeout(sd_event_source *es, usec_t usec, void *userdata) {
}
static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
DnsStream *s = userdata;
_cleanup_(dns_stream_unrefp) DnsStream *s = dns_stream_ref(userdata); /* Protect stream while we process it */
int r;
assert(s);
@ -281,18 +288,16 @@ static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *use
#if ENABLE_DNS_OVER_TLS
if (s->encrypted) {
r = dnstls_stream_on_io(s, revents);
if (r == DNSTLS_STREAM_CLOSED)
return 0;
else if (r == -EAGAIN)
if (r == -EAGAIN)
return dns_stream_update_io(s);
else if (r < 0) {
if (r < 0)
return dns_stream_complete(s, -r);
} else {
r = dns_stream_update_io(s);
if (r < 0)
return r;
}
r = dns_stream_update_io(s);
if (r < 0)
return r;
}
#endif
@ -430,9 +435,6 @@ static DnsStream *dns_stream_free(DnsStream *s) {
dns_stream_stop(s);
if (s->server && s->server->stream == s)
s->server->stream = NULL;
if (s->manager) {
LIST_REMOVE(streams, s->manager->dns_streams, s);
s->manager->n_dns_streams--;
@ -457,28 +459,37 @@ static DnsStream *dns_stream_free(DnsStream *s) {
DEFINE_TRIVIAL_REF_UNREF_FUNC(DnsStream, dns_stream, dns_stream_free);
int dns_stream_new(Manager *m, DnsStream **ret, DnsProtocol protocol, int fd, const union sockaddr_union *tfo_address) {
int dns_stream_new(
Manager *m,
DnsStream **ret,
DnsProtocol protocol,
int fd,
const union sockaddr_union *tfo_address) {
_cleanup_(dns_stream_unrefp) DnsStream *s = NULL;
int r;
assert(m);
assert(ret);
assert(fd >= 0);
if (m->n_dns_streams > DNS_STREAMS_MAX)
return -EBUSY;
s = new0(DnsStream, 1);
s = new(DnsStream, 1);
if (!s)
return -ENOMEM;
*s = (DnsStream) {
.n_ref = 1,
.fd = -1,
.protocol = protocol,
};
r = ordered_set_ensure_allocated(&s->write_queue, &dns_packet_hash_ops);
if (r < 0)
return r;
s->n_ref = 1;
s->fd = -1;
s->protocol = protocol;
r = sd_event_add_io(m->event, &s->io_event_source, fd, EPOLLIN, on_stream_io, s);
if (r < 0)
return r;
@ -497,15 +508,16 @@ int dns_stream_new(Manager *m, DnsStream **ret, DnsProtocol protocol, int fd, co
(void) sd_event_source_set_description(s->timeout_event_source, "dns-stream-timeout");
LIST_PREPEND(streams, m->dns_streams, s);
m->n_dns_streams++;
s->manager = m;
s->fd = fd;
if (tfo_address) {
s->tfo_address = *tfo_address;
s->tfo_salen = tfo_address->sa.sa_family == AF_INET6 ? sizeof(tfo_address->in6) : sizeof(tfo_address->in);
}
m->n_dns_streams++;
*ret = TAKE_PTR(s);
return 0;
@ -515,6 +527,7 @@ int dns_stream_write_packet(DnsStream *s, DnsPacket *p) {
int r;
assert(s);
assert(p);
r = ordered_set_put(s->write_queue, p);
if (r < 0)
@ -524,3 +537,31 @@ int dns_stream_write_packet(DnsStream *s, DnsPacket *p) {
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);
if (!s->server)
return;
if (s->server->stream != s)
return;
dns_server_unref_stream(s->server);
}

View file

@ -53,13 +53,12 @@ struct DnsStream {
size_t n_written, n_read;
OrderedSet *write_queue;
int (*on_connection)(DnsStream *s);
int (*on_packet)(DnsStream *s);
int (*complete)(DnsStream *s, int error);
LIST_HEAD(DnsTransaction, transactions); /* when used by the transaction logic */
DnsServer *server; /* when used by the transaction logic */
DnsQuery *query; /* when used by the DNS stub logic */
DnsQuery *query; /* when used by the DNS stub logic */
/* used when DNS-over-TLS is enabled */
bool encrypted:1;
@ -87,3 +86,7 @@ static inline bool DNS_STREAM_QUEUED(DnsStream *s) {
return !!s->write_packet;
}
DnsPacket *dns_stream_take_read_packet(DnsStream *s);
void dns_stream_detach(DnsStream *s);

View file

@ -437,13 +437,17 @@ static int manager_dns_stub_udp_fd(Manager *m) {
}
static int on_dns_stub_stream_packet(DnsStream *s) {
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
assert(s);
assert(s->read_packet);
if (dns_packet_validate_query(s->read_packet) > 0) {
log_debug("Got DNS stub TCP query packet for id %u", DNS_PACKET_ID(s->read_packet));
p = dns_stream_take_read_packet(s);
assert(p);
dns_stub_process_query(s->manager, s, s->read_packet);
if (dns_packet_validate_query(p) > 0) {
log_debug("Got DNS stub TCP query packet for id %u", DNS_PACKET_ID(p));
dns_stub_process_query(s->manager, s, p);
} else
log_debug("Invalid DNS stub TCP packet, ignoring.");

View file

@ -503,59 +503,54 @@ static int dns_transaction_on_stream_packet(DnsTransaction *t, DnsPacket *p) {
}
static int on_stream_complete(DnsStream *s, int error) {
_cleanup_(dns_stream_unrefp) DnsStream *p = NULL;
DnsTransaction *t, *n;
int r = 0;
/* Do not let new transactions use this stream */
if (s->server && s->server->stream == s)
p = TAKE_PTR(s->server->stream);
assert(s);
if (ERRNO_IS_DISCONNECT(error) && s->protocol != DNS_PROTOCOL_LLMNR) {
usec_t usec;
log_debug_errno(error, "Connection failure for DNS TCP stream: %m");
if (s->transactions) {
DnsTransaction *t;
t = s->transactions;
assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &usec) >= 0);
dns_server_packet_lost(t->server, IPPROTO_TCP, t->current_feature_level);
}
}
LIST_FOREACH_SAFE(transactions_by_stream, t, n, s->transactions)
if (error != 0)
if (error != 0) {
DnsTransaction *t, *n;
LIST_FOREACH_SAFE(transactions_by_stream, t, n, s->transactions)
on_transaction_stream_error(t, error);
else if (DNS_PACKET_ID(s->read_packet) == t->id)
/* As each transaction have a unique id the return code is only set once */
r = dns_transaction_on_stream_packet(t, s->read_packet);
return r;
}
static int dns_stream_on_packet(DnsStream *s) {
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
int r = 0;
DnsTransaction *t;
/* Take ownership of packet to be able to receive new packets */
p = TAKE_PTR(s->read_packet);
s->n_read = 0;
t = hashmap_get(s->manager->dns_transactions, UINT_TO_PTR(DNS_PACKET_ID(p)));
/* Ignore incorrect transaction id as transaction can have been canceled */
if (t)
r = dns_transaction_on_stream_packet(t, p);
else {
if (dns_packet_validate_reply(p) <= 0) {
log_debug("Invalid TCP reply packet.");
on_stream_complete(s, 0);
}
return 0;
}
return r;
return 0;
}
static int on_stream_packet(DnsStream *s) {
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
DnsTransaction *t;
assert(s);
/* Take ownership of packet to be able to receive new packets */
p = dns_stream_take_read_packet(s);
assert(p);
t = hashmap_get(s->manager->dns_transactions, UINT_TO_PTR(DNS_PACKET_ID(p)));
if (t)
return dns_transaction_on_stream_packet(t, p);
/* Ignore incorrect transaction id as transaction can have been canceled */
if (dns_packet_validate_reply(p) <= 0) {
log_debug("Invalid TCP reply packet.");
on_stream_complete(s, 0);
}
return 0;
}
static uint16_t dns_port_for_feature_level(DnsServerFeatureLevel level) {
return DNS_SERVER_FEATURE_LEVEL_IS_TLS(level) ? 853 : 53;
}
static int dns_transaction_emit_tcp(DnsTransaction *t) {
@ -585,7 +580,7 @@ static int dns_transaction_emit_tcp(DnsTransaction *t) {
if (t->server->stream && (DNS_SERVER_FEATURE_LEVEL_IS_TLS(t->current_feature_level) == t->server->stream->encrypted))
s = dns_stream_ref(t->server->stream);
else
fd = dns_scope_socket_tcp(t->scope, AF_UNSPEC, NULL, t->server, DNS_SERVER_FEATURE_LEVEL_IS_TLS(t->current_feature_level) ? 853 : 53, &sa);
fd = dns_scope_socket_tcp(t->scope, AF_UNSPEC, NULL, t->server, dns_port_for_feature_level(t->current_feature_level), &sa);
break;
@ -629,7 +624,9 @@ static int dns_transaction_emit_tcp(DnsTransaction *t) {
fd = -1;
#if ENABLE_DNS_OVER_TLS
if (DNS_SERVER_FEATURE_LEVEL_IS_TLS(t->current_feature_level)) {
if (t->scope->protocol == DNS_PROTOCOL_DNS &&
DNS_SERVER_FEATURE_LEVEL_IS_TLS(t->current_feature_level)) {
assert(t->server);
r = dnstls_stream_connect_tls(s, t->server);
if (r < 0)
@ -638,13 +635,13 @@ static int dns_transaction_emit_tcp(DnsTransaction *t) {
#endif
if (t->server) {
dns_stream_unref(t->server->stream);
dns_server_unref_stream(t->server);
t->server->stream = dns_stream_ref(s);
s->server = dns_server_ref(t->server);
}
s->complete = on_stream_complete;
s->on_packet = dns_stream_on_packet;
s->on_packet = on_stream_packet;
/* The interface index is difficult to determine if we are
* connecting to the local host, hence fill this in right away

View file

@ -260,18 +260,21 @@ int manager_llmnr_ipv6_udp_fd(Manager *m) {
}
static int on_llmnr_stream_packet(DnsStream *s) {
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
DnsScope *scope;
assert(s);
assert(s->read_packet);
scope = manager_find_scope(s->manager, s->read_packet);
p = dns_stream_take_read_packet(s);
assert(p);
scope = manager_find_scope(s->manager, p);
if (!scope)
log_debug("Got LLMNR TCP packet on unknown scope. Ignoring.");
else if (dns_packet_validate_query(s->read_packet) > 0) {
log_debug("Got LLMNR TCP query packet for id %u", DNS_PACKET_ID(s->read_packet));
else if (dns_packet_validate_query(p) > 0) {
log_debug("Got LLMNR TCP query packet for id %u", DNS_PACKET_ID(p));
dns_scope_process_query(scope, s, s->read_packet);
dns_scope_process_query(scope, s, p);
} else
log_debug("Invalid LLMNR TCP packet, ignoring.");