resolved-dns-transaction.c revision f535705a457f9bee976a45baf20272b7228d0c65
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek This file is part of systemd.
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek Copyright 2014 Lennart Poettering
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek systemd is free software; you can redistribute it and/or modify it
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek under the terms of the GNU Lesser General Public License as published by
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek the Free Software Foundation; either version 2.1 of the License, or
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek (at your option) any later version.
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek systemd is distributed in the hope that it will be useful, but
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek WITHOUT ANY WARRANTY; without even the implied warranty of
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek Lesser General Public License for more details.
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek You should have received a copy of the GNU Lesser General Public License
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek along with systemd; If not, see <http://www.gnu.org/licenses/>.
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek#include "resolved-dns-transaction.h"
cc3773810855956bad92337cee8fa193584ab62eLennart Poetteringstatic void dns_transaction_reset_answer(DnsTransaction *t) {
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering t->received = dns_packet_unref(t->received);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek t->answer = dns_answer_unref(t->answer);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek t->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek t->answer_source = _DNS_TRANSACTION_SOURCE_INVALID;
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek t->answer_authenticated = false;
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmekstatic void dns_transaction_close_connection(DnsTransaction *t) {
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek t->stream = dns_stream_free(t->stream);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek t->dns_udp_event_source = sd_event_source_unref(t->dns_udp_event_source);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek t->dns_udp_fd = safe_close(t->dns_udp_fd);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmekstatic void dns_transaction_stop_timeout(DnsTransaction *t) {
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek t->timeout_event_source = sd_event_source_unref(t->timeout_event_source);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-SzmekDnsTransaction* dns_transaction_free(DnsTransaction *t) {
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek dns_transaction_close_connection(t);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek dns_transaction_stop_timeout(t);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek dns_transaction_reset_answer(t);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek hashmap_remove_value(t->scope->transactions_by_key, t->key, t);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek LIST_REMOVE(transactions_by_scope, t->scope->transactions, t);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek hashmap_remove(t->scope->manager->dns_transactions, UINT_TO_PTR(t->id));
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek while ((c = set_steal_first(t->notify_query_candidates)))
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek set_free(t->notify_query_candidates);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek while ((i = set_steal_first(t->notify_zone_items)))
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek while ((z = set_steal_first(t->notify_transactions)))
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek set_remove(z->dnssec_transactions, t);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek set_free(t->notify_transactions);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek while ((z = set_steal_first(t->dnssec_transactions))) {
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek set_remove(z->notify_transactions, t);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek set_free(t->dnssec_transactions);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek dns_answer_unref(t->validated_keys);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-SzmekDEFINE_TRIVIAL_CLEANUP_FUNC(DnsTransaction*, dns_transaction_free);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmekvoid dns_transaction_gc(DnsTransaction *t) {
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek if (set_isempty(t->notify_query_candidates) &&
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek set_isempty(t->notify_zone_items) &&
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek set_isempty(t->notify_transactions))
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmekint dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key) {
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek _cleanup_(dns_transaction_freep) DnsTransaction *t = NULL;
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek /* Don't allow looking up invalid or pseudo RRs */
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek if (!dns_type_is_valid_query(key->type))
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek /* We only support the IN class */
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek if (key->class != DNS_CLASS_IN && key->class != DNS_CLASS_ANY)
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek r = hashmap_ensure_allocated(&s->manager->dns_transactions, NULL);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek r = hashmap_ensure_allocated(&s->transactions_by_key, &dns_resource_key_hash_ops);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek t->answer_source = _DNS_TRANSACTION_SOURCE_INVALID;
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek t->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek t->key = dns_resource_key_ref(key);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek /* Find a fresh, unused transaction id */
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek random_bytes(&t->id, sizeof(t->id));
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek while (t->id == 0 ||
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek hashmap_get(s->manager->dns_transactions, UINT_TO_PTR(t->id)));
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek r = hashmap_put(s->manager->dns_transactions, UINT_TO_PTR(t->id), t);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek r = hashmap_replace(s->transactions_by_key, t->key, t);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek hashmap_remove(s->manager->dns_transactions, UINT_TO_PTR(t->id));
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek LIST_PREPEND(transactions_by_scope, s->transactions, t);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek s->manager->n_transactions_total ++;
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmekstatic void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) {
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek _cleanup_free_ char *pretty = NULL;
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek if (manager_our_packet(t->scope->manager, p) != 0)
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek in_addr_to_string(p->family, &p->sender, &pretty);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek log_debug("Transaction %" PRIu16 " for <%s> on scope %s on %s/%s got tentative packet from %s.",
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek dns_protocol_to_string(t->scope->protocol),
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek t->scope->link ? t->scope->link->name : "*",
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family),
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek /* RFC 4795, Section 4.1 says that the peer with the
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek * lexicographically smaller IP address loses */
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek if (memcmp(&p->sender, &p->destination, FAMILY_ADDRESS_SIZE(p->family)) >= 0) {
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek log_debug("Peer has lexicographically larger IP address and thus lost in the conflict.");
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek log_debug("We have the lexicographically larger IP address and thus lost in the conflict.");
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek while ((z = set_first(t->notify_zone_items))) {
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek /* First, make sure the zone item drops the reference
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek /* Secondly, report this as conflict, so that we might
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek * look for a different hostname */
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmekvoid dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek assert(!DNS_TRANSACTION_IS_LIVE(state));
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek if (state == DNS_TRANSACTION_DNSSEC_FAILED)
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek LOG_MESSAGE("DNSSEC validation failed for question %s: %s", dns_transaction_key_string(t), dnssec_result_to_string(t->answer_dnssec_result)),
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek "DNS_TRANSACTION=%" PRIu16, t->id,
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek "DNS_QUESTION=%s", dns_transaction_key_string(t),
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek "DNSSEC_RESULT=%s", dnssec_result_to_string(t->answer_dnssec_result),
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek /* Note that this call might invalidate the query. Callers
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering * should hence not attempt to access the query or transaction
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek * after calling this function. */
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek log_debug("Transaction %" PRIu16 " for <%s> on scope %s on %s/%s now complete with <%s> from %s (%s).",
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek dns_protocol_to_string(t->scope->protocol),
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek t->scope->link ? t->scope->link->name : "*",
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family),
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek dns_transaction_state_to_string(state),
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek t->answer_source < 0 ? "none" : dns_transaction_source_to_string(t->answer_source),
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek t->answer_authenticated ? "authenticated" : "unsigned");
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek dns_transaction_close_connection(t);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek dns_transaction_stop_timeout(t);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek /* Notify all queries that are interested, but make sure the
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek * transaction isn't freed while we are still looking at it */
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek SET_FOREACH(c, t->notify_query_candidates, i)
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek SET_FOREACH(z, t->notify_zone_items, i)
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek if (!set_isempty(t->notify_transactions)) {
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek unsigned j, n = 0;
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek /* We need to be careful when notifying other
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek * transactions, as that might destroy other
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek * transactions in our list. Hence, in order to be
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek * able to safely iterate through the list of
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek * transactions, take a GC lock on all of them
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek * first. Then, in a second loop, notify them, but
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek * first unlock that specific transaction. */
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek nt = newa(DnsTransaction*, set_size(t->notify_transactions));
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek SET_FOREACH(d, t->notify_transactions, i) {
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek assert(n == set_size(t->notify_transactions));
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek for (j = 0; j < n; j++) {
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek if (set_contains(t->notify_transactions, nt[j]))
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek dns_transaction_notify(nt[j], t);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmekstatic int dns_transaction_pick_server(DnsTransaction *t) {
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek assert(t->scope->protocol == DNS_PROTOCOL_DNS);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek server = dns_scope_get_dns_server(t->scope);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek t->current_features = dns_server_possible_feature_level(server);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek t->server = dns_server_ref(server);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmekstatic int on_stream_complete(DnsStream *s, int error) {
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek /* Copy the data we care about out of the stream before we
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek * destroy it. */
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek p = dns_packet_ref(s->read_packet);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek t->stream = dns_stream_free(t->stream);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek if (IN_SET(error, ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE)) {
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek dns_transaction_complete(t, DNS_TRANSACTION_CONNECTION_FAILURE);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek if (dns_packet_validate_reply(p) <= 0) {
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek log_debug("Invalid TCP reply packet.");
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek dns_scope_check_conflicts(t->scope, p);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek dns_transaction_process_reply(t, p);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek /* If the response wasn't useful, then complete the transition
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek * now. After all, we are the worst feature set now with TCP
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek * sockets, and there's really no point in retrying. */
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek if (t->state == DNS_TRANSACTION_PENDING)
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmekstatic int dns_transaction_open_tcp(DnsTransaction *t) {
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek dns_transaction_close_connection(t);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek r = dns_transaction_pick_server(t);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek r = dns_server_adjust_opt(t->server, t->sent, t->current_features);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek fd = dns_scope_socket_tcp(t->scope, AF_UNSPEC, NULL, t->server, 53);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek /* When we already received a reply to this (but it was truncated), send to its sender address */
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek fd = dns_scope_socket_tcp(t->scope, t->received->family, &t->received->sender, NULL, t->received->sender_port);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek /* Otherwise, try to talk to the owner of a
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek * the IP address, in case this is a reverse
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek r = dns_name_address(DNS_RESOURCE_KEY_NAME(t->key), &family, &address);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek fd = dns_scope_socket_tcp(t->scope, family, &address, NULL, LLMNR_PORT);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek r = dns_stream_new(t->scope->manager, &t->stream, t->scope->protocol, fd);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek r = dns_stream_write_packet(t->stream, t->sent);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek t->stream = dns_stream_free(t->stream);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek t->stream->complete = on_stream_complete;
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek /* The interface index is difficult to determine if we are
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek * connecting to the local host, hence fill this in right away
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek * instead of determining it from the socket */
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek t->stream->ifindex = t->scope->link->ifindex;
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek dns_transaction_reset_answer(t);
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmekstatic void dns_transaction_cache_answer(DnsTransaction *t) {
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek /* For mDNS we cache whenever we get the packet, rather than
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek * in each transaction. */
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek if (!IN_SET(t->scope->protocol, DNS_PROTOCOL_DNS, DNS_PROTOCOL_LLMNR))
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek /* We never cache if this packet is from the local host, under
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek * the assumption that a locally running DNS server would
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering * cache this anyway, and probably knows better when to flush
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering * the cache then we could. */
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek if (!DNS_PACKET_SHALL_CACHE(t->received))
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmekstatic bool dns_transaction_dnssec_is_live(DnsTransaction *t) {
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek SET_FOREACH(dt, t->dnssec_transactions, i)
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek if (DNS_TRANSACTION_IS_LIVE(dt->state))
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmekstatic void dns_transaction_process_dnssec(DnsTransaction *t) {
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek /* Are there ongoing DNSSEC transactions? If so, let's wait for them. */
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek /* All our auxiliary DNSSEC transactions are complete now. Try
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek * to validate our RRset now. */
2b3ab29de466ae6bd7c3243a5a48c7291cc2af0aZbigniew Jędrzejewski-Szmek r = dns_transaction_validate_dnssec(t);
DNSSEC_INCOMPATIBLE_SERVER)) { /* Server does not do DNSSEC (Yay, we are downgrade attack vulnerable!) */
assert(t);
assert(p);
case DNS_PROTOCOL_LLMNR:
if (DNS_PACKET_LLMNR_T(p)) {
dns_transaction_tentative(t, p);
case DNS_PROTOCOL_MDNS:
case DNS_PROTOCOL_DNS:
if (t->received != p) {
if (DNS_PACKET_TC(p)) {
case DNS_PROTOCOL_DNS:
r = dns_transaction_go(t);
case DNS_PROTOCOL_LLMNR:
case DNS_PROTOCOL_MDNS:
if (DNS_PACKET_TC(p)) {
r = dns_transaction_open_tcp(t);
if (r == -ESRCH) {
r = dns_transaction_go(t);
r = dns_packet_extract(p);
t->answer_authenticated = false;
assert(t);
if (dns_packet_validate_reply(p) > 0 &&
assert(t);
r = dns_transaction_pick_server(t);
return -EAGAIN;
int fd;
if (fd < 0)
return fd;
r = sd_event_add_io(t->scope->manager->event, &t->dns_udp_event_source, fd, EPOLLIN, on_dns_packet, t);
assert(s);
assert(t);
case DNS_PROTOCOL_DNS:
case DNS_PROTOCOL_LLMNR:
case DNS_PROTOCOL_MDNS:
if (t->initial_jitter_scheduled)
t->initial_jitter_elapsed = true;
r = dns_transaction_go(t);
assert(t);
case DNS_PROTOCOL_DNS:
case DNS_PROTOCOL_MDNS:
case DNS_PROTOCOL_LLMNR:
assert(t);
t->n_attempts++;
t->answer_authenticated = true;
t->answer_authenticated = true;
r = dns_cache_lookup(&t->scope->cache, t->key, &t->answer_rcode, &t->answer, &t->answer_authenticated);
bool add_known_answers = false;
unsigned qdcount;
assert(t);
add_known_answers = true;
* query packet on the wire as possible. To achieve that, we iterate through all pending transactions
if (other == t)
if (r == -EMSGSIZE)
r = sd_event_add_time(
ts, 0,
qdcount ++;
add_known_answers = true;
if (add_known_answers) {
t->sent = p;
p = NULL;
assert(t);
return dns_transaction_make_packet_mdns(t);
if (t->sent)
return -EDOM;
t->sent = p;
p = NULL;
assert(t);
t->id,
if (!t->initial_jitter_scheduled &&
t->initial_jitter_scheduled = true;
case DNS_PROTOCOL_LLMNR:
case DNS_PROTOCOL_MDNS:
r = sd_event_add_time(
&t->timeout_event_source,
t->n_attempts = 0;
log_debug("Delaying %s transaction for " USEC_FMT "us.", dns_protocol_to_string(t->scope->protocol), jitter);
r = dns_transaction_make_packet(t);
if (r == -EDOM) {
r = dns_transaction_open_tcp(t);
r = dns_transaction_emit_udp(t);
r = dns_transaction_open_tcp(t);
if (r == -ESRCH) {
return dns_transaction_go(t);
r = sd_event_add_time(
&t->timeout_event_source,
ts, 0,
static int dns_transaction_add_dnssec_transaction(DnsTransaction *t, DnsResourceKey *key, DnsTransaction **ret) {
assert(t);
if (!aux) {
goto gc;
goto gc;
goto gc;
goto gc;
gc:
assert(t);
assert(t);
assert(t);
assert(t);
r = dns_name_parent(&p);
assert(t);
return 0; /* Server handles DNSSEC requests, but isn't augmenting responses with RRSIGs. No point in trying DNSSEC then. */
case DNS_TYPE_RRSIG: {
if (!dnskey)
return -ENOMEM;
log_debug("Requesting DNSKEY to validate transaction %" PRIu16" (%s, RRSIG with key tag: %" PRIu16 ").", t->id, DNS_RESOURCE_KEY_NAME(rr->key), rr->rrsig.key_tag);
case DNS_TYPE_DNSKEY: {
if (!ds)
return -ENOMEM;
log_debug("Requesting DS to validate transaction %" PRIu16" (%s, DNSKEY with key tag: %" PRIu16 ").", t->id, DNS_RESOURCE_KEY_NAME(rr->key), dnssec_keytag(rr));
case DNS_TYPE_SOA:
case DNS_TYPE_NS: {
if (!ds)
return -ENOMEM;
log_debug("Requesting DS to validate transaction %" PRIu16 " (%s, unsigned SOA/NS RRset).", t->id, DNS_RESOURCE_KEY_NAME(rr->key));
case DNS_TYPE_DS:
case DNS_TYPE_CNAME:
case DNS_TYPE_DNAME: {
const char *name;
if (!soa)
return -ENOMEM;
log_debug("Requesting parent SOA to validate transaction %" PRIu16 " (%s, unsigned CNAME/DNAME/DS RRset).", t->id, DNS_RESOURCE_KEY_NAME(rr->key));
if (!soa)
return -ENOMEM;
log_debug("Requesting SOA to validate transaction %" PRIu16 " (%s, unsigned non-SOA/NS RRset <%s>).", t->id, DNS_RESOURCE_KEY_NAME(rr->key), dns_resource_record_to_string(rr));
const char *name;
log_debug("Requesting parent SOA to validate transaction %" PRIu16 " (%s, unsigned empty SOA/NS/DS response).", t->id, DNS_RESOURCE_KEY_NAME(t->key));
log_debug("Requesting SOA to validate transaction %" PRIu16 " (%s, unsigned empty non-SOA/NS/DS response).", t->id, DNS_RESOURCE_KEY_NAME(t->key));
if (name) {
if (!soa)
return -ENOMEM;
return dns_transaction_dnssec_is_live(t);
assert(t);
log_debug("Auxiliary DNSSEC RR query failed validation: %s", dnssec_result_to_string(source->answer_dnssec_result));
goto fail;
case DNS_TRANSACTION_SUCCESS:
goto fail;
log_debug("Auxiliary DNSSEC RR query failed with %s", dns_transaction_state_to_string(source->state));
goto fail;
fail:
int ifindex, r;
assert(t);
assert(t);
return -EINVAL;
case DNS_TYPE_RRSIG:
case DNS_TYPE_SOA:
case DNS_TYPE_NS: {
Iterator i;
case DNS_TYPE_DS:
case DNS_TYPE_CNAME:
case DNS_TYPE_DNAME: {
Iterator i;
if (!parent) {
return -EBADMSG;
return t->answer_authenticated;
Iterator i;
/* Any other kind of RR (including DNSKEY/NSEC/NSEC3). Let's see if our SOA lookup was authenticated */
return t->answer_authenticated;
const char *name;
Iterator i;
assert(t);
return -EINVAL;
bool found = false;
Iterator i;
found = true;
assert(t);
bool dnskeys_finalized = false;
assert(t);
t->answer_authenticated = true;
log_debug("Validating response from transaction %" PRIu16 " (%s).", t->id, dns_transaction_key_string(t));
bool changed = false;
log_debug("Looking at %s: %s", strna(dns_resource_record_to_string(rr)), dnssec_result_to_string(result));
r = dns_answer_move_by_key(&validated, &t->answer, rr->key, DNS_ANSWER_AUTHENTICATED|DNS_ANSWER_CACHEABLE);
changed = true;
} else if (dnskeys_finalized) {
changed = true;
changed = true;
if (r < 0 && r != -ENXIO)
changed = true;
changed = true;
if (changed)
if (!dnskeys_finalized) {
dnskeys_finalized = true;
t->answer_authenticated = true;
t->answer_authenticated = false;
bool authenticated = false;
switch (nr) {
case DNSSEC_NSEC_NXDOMAIN:
log_debug("Proved NXDOMAIN via NSEC/NSEC3 for transaction %u (%s)", t->id, dns_transaction_key_string(t));
case DNSSEC_NSEC_NODATA:
log_debug("Proved NODATA via NSEC/NSEC3 for transaction %u (%s)", t->id, dns_transaction_key_string(t));
case DNSSEC_NSEC_OPTOUT:
log_debug("Data is NSEC3 opt-out via NSEC/NSEC3 for transaction %u (%s)", t->id, dns_transaction_key_string(t));
t->answer_authenticated = false;
case DNSSEC_NSEC_NO_RR:
r = dns_transaction_requires_nsec(t);
t->answer_authenticated = false;
case DNSSEC_NSEC_FOUND:
assert(t);
if (!t->key_string) {