resolved-dns-transaction.c revision ae6a4bbf318e197813227e50c245a00de03784a2
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering/***
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering This file is part of systemd.
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering Copyright 2014 Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering systemd is free software; you can redistribute it and/or modify it
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering under the terms of the GNU Lesser General Public License as published by
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering (at your option) any later version.
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering systemd is distributed in the hope that it will be useful, but
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering Lesser General Public License for more details.
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering You should have received a copy of the GNU Lesser General Public License
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering***/
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "af-list.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "alloc-util.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "dns-domain.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "fd-util.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "random-util.h"
4f5dd3943bef8a04be7e3b838b822bb9a7ad6cb3Lennart Poettering#include "resolved-dns-transaction.h"
a5c32cff1f56afe6f0c6c70d91a88a7a8238b2d7Harald Hoyer#include "resolved-llmnr.h"
6482f6269c87d2249e52e889a63adbdd50f2d691Ronny Chevalier#include "string-table.h"
4f5dd3943bef8a04be7e3b838b822bb9a7ad6cb3Lennart Poettering
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart PoetteringDnsTransaction* dns_transaction_free(DnsTransaction *t) {
4f5dd3943bef8a04be7e3b838b822bb9a7ad6cb3Lennart Poettering DnsQueryCandidate *c;
4f5dd3943bef8a04be7e3b838b822bb9a7ad6cb3Lennart Poettering DnsZoneItem *i;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (!t)
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering return NULL;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering sd_event_source_unref(t->timeout_event_source);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering dns_packet_unref(t->sent);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering dns_packet_unref(t->received);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering dns_answer_unref(t->answer);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering sd_event_source_unref(t->dns_udp_event_source);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering safe_close(t->dns_udp_fd);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering dns_server_unref(t->server);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering dns_stream_free(t->stream);
2b6bf07dd23bb467099d213c97b3875c5e453491Zbigniew Jędrzejewski-Szmek
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (t->scope) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering hashmap_remove(t->scope->transactions, t->key);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (t->id != 0)
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering hashmap_remove(t->scope->manager->dns_transactions, UINT_TO_PTR(t->id));
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering }
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering dns_resource_key_unref(t->key);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering while ((c = set_steal_first(t->query_candidates)))
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering set_remove(c->transactions, t);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering set_free(t->query_candidates);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering while ((i = set_steal_first(t->zone_items)))
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering i->probe_transaction = NULL;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering set_free(t->zone_items);
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering free(t);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering return NULL;
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering}
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering
cc3773810855956bad92337cee8fa193584ab62eLennart PoetteringDEFINE_TRIVIAL_CLEANUP_FUNC(DnsTransaction*, dns_transaction_free);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringvoid dns_transaction_gc(DnsTransaction *t) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering assert(t);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (t->block_gc > 0)
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering return;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (set_isempty(t->query_candidates) && set_isempty(t->zone_items))
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering dns_transaction_free(t);
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering}
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringint dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering _cleanup_(dns_transaction_freep) DnsTransaction *t = NULL;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering int r;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
d2e54fae5ca7a0f71b5ac8b356a589ff0a09ea0aKay Sievers assert(ret);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering assert(s);
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poettering assert(key);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering r = hashmap_ensure_allocated(&s->manager->dns_transactions, NULL);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (r < 0)
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poettering return r;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering r = hashmap_ensure_allocated(&s->transactions, &dns_resource_key_hash_ops);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (r < 0)
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering return r;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering t = new0(DnsTransaction, 1);
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering if (!t)
90b2de37b80603168f4e9c9c81cff7eea4efa21aZbigniew Jędrzejewski-Szmek return -ENOMEM;
90b2de37b80603168f4e9c9c81cff7eea4efa21aZbigniew Jędrzejewski-Szmek
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering t->dns_udp_fd = -1;
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering t->key = dns_resource_key_ref(key);
90b2de37b80603168f4e9c9c81cff7eea4efa21aZbigniew Jędrzejewski-Szmek
90b2de37b80603168f4e9c9c81cff7eea4efa21aZbigniew Jędrzejewski-Szmek /* Find a fresh, unused transaction id */
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering do
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering random_bytes(&t->id, sizeof(t->id));
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering while (t->id == 0 ||
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering hashmap_get(s->manager->dns_transactions, UINT_TO_PTR(t->id)));
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
88231eb62cafc8bb51406919c8cf6019dc1ac916Thomas Hindoe Paaboel Andersen r = hashmap_put(s->manager->dns_transactions, UINT_TO_PTR(t->id), t);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (r < 0) {
88231eb62cafc8bb51406919c8cf6019dc1ac916Thomas Hindoe Paaboel Andersen t->id = 0;
88231eb62cafc8bb51406919c8cf6019dc1ac916Thomas Hindoe Paaboel Andersen return r;
88231eb62cafc8bb51406919c8cf6019dc1ac916Thomas Hindoe Paaboel Andersen }
88231eb62cafc8bb51406919c8cf6019dc1ac916Thomas Hindoe Paaboel Andersen
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering r = hashmap_put(s->transactions, t->key, t);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (r < 0) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering hashmap_remove(s->manager->dns_transactions, UINT_TO_PTR(t->id));
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering return r;
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering }
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
88231eb62cafc8bb51406919c8cf6019dc1ac916Thomas Hindoe Paaboel Andersen t->scope = s;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
88231eb62cafc8bb51406919c8cf6019dc1ac916Thomas Hindoe Paaboel Andersen if (ret)
88231eb62cafc8bb51406919c8cf6019dc1ac916Thomas Hindoe Paaboel Andersen *ret = t;
88231eb62cafc8bb51406919c8cf6019dc1ac916Thomas Hindoe Paaboel Andersen
88231eb62cafc8bb51406919c8cf6019dc1ac916Thomas Hindoe Paaboel Andersen t = NULL;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering return 0;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering}
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic void dns_transaction_stop(DnsTransaction *t) {
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poettering assert(t);
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poettering
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poettering t->timeout_event_source = sd_event_source_unref(t->timeout_event_source);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering t->stream = dns_stream_free(t->stream);
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering /* Note that we do not drop the UDP socket here, as we want to
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poettering * reuse it to repeat the interaction. */
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering}
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poetteringstatic void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) {
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poettering _cleanup_free_ char *pretty = NULL;
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poettering DnsZoneItem *z;
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poettering
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poettering assert(t);
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poettering assert(p);
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (manager_our_packet(t->scope->manager, p) != 0)
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poettering return;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering in_addr_to_string(p->family, &p->sender, &pretty);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering log_debug("Transaction on scope %s on %s/%s got tentative packet from %s",
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering dns_protocol_to_string(t->scope->protocol),
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering t->scope->link ? t->scope->link->name : "*",
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family),
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering pretty);
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering /* RFC 4795, Section 4.1 says that the peer with the
de0671ee7fe465e108f62dcbbbe9366f81dd9e9aZbigniew Jędrzejewski-Szmek * lexicographically smaller IP address loses */
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (memcmp(&p->sender, &p->destination, FAMILY_ADDRESS_SIZE(p->family)) >= 0) {
de0671ee7fe465e108f62dcbbbe9366f81dd9e9aZbigniew Jędrzejewski-Szmek log_debug("Peer has lexicographically larger IP address and thus lost in the conflict.");
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering return;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering }
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering log_debug("We have the lexicographically larger IP address and thus lost in the conflict.");
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering t->block_gc++;
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering while ((z = set_first(t->zone_items))) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering /* First, make sure the zone item drops the reference
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering * to us */
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering dns_zone_item_probe_stop(z);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering /* Secondly, report this as conflict, so that we might
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering * look for a different hostname */
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering dns_zone_item_conflict(z);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering }
de0671ee7fe465e108f62dcbbbe9366f81dd9e9aZbigniew Jędrzejewski-Szmek t->block_gc--;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
de0671ee7fe465e108f62dcbbbe9366f81dd9e9aZbigniew Jędrzejewski-Szmek dns_transaction_gc(t);
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering}
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringvoid dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering DnsQueryCandidate *c;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering DnsZoneItem *z;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering Iterator i;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering assert(t);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering assert(!IN_SET(state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering /* Note that this call might invalidate the query. Callers
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering * should hence not attempt to access the query or transaction
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering * after calling this function. */
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering log_debug("Transaction on scope %s on %s/%s now complete with <%s>",
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering dns_protocol_to_string(t->scope->protocol),
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering t->scope->link ? t->scope->link->name : "*",
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family),
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering dns_transaction_state_to_string(state));
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering t->state = state;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering dns_transaction_stop(t);
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering /* Notify all queries that are interested, but make sure the
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering * transaction isn't freed while we are still looking at it */
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering t->block_gc++;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering SET_FOREACH(c, t->query_candidates, i)
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering dns_query_candidate_ready(c);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering SET_FOREACH(z, t->zone_items, i)
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering dns_zone_item_ready(z);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering t->block_gc--;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering dns_transaction_gc(t);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering}
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic int on_stream_complete(DnsStream *s, int error) {
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering DnsTransaction *t;
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering assert(s);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering assert(s->transaction);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering /* Copy the data we care about out of the stream before we
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering * destroy it. */
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering t = s->transaction;
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering p = dns_packet_ref(s->read_packet);
a34faf579d2be139b0b9e8cd0c73ad4d918ef736Lukas Nykryn
a34faf579d2be139b0b9e8cd0c73ad4d918ef736Lukas Nykryn t->stream = dns_stream_free(t->stream);
a34faf579d2be139b0b9e8cd0c73ad4d918ef736Lukas Nykryn
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering if (error != 0) {
a34faf579d2be139b0b9e8cd0c73ad4d918ef736Lukas Nykryn dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering return 0;
a34faf579d2be139b0b9e8cd0c73ad4d918ef736Lukas Nykryn }
a34faf579d2be139b0b9e8cd0c73ad4d918ef736Lukas Nykryn
a34faf579d2be139b0b9e8cd0c73ad4d918ef736Lukas Nykryn if (dns_packet_validate_reply(p) <= 0) {
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering log_debug("Invalid LLMNR TCP packet.");
a34faf579d2be139b0b9e8cd0c73ad4d918ef736Lukas Nykryn dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering return 0;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering }
527b7a421ff3927d4f3f170b1b143452e88ae1dcLennart Poettering
527b7a421ff3927d4f3f170b1b143452e88ae1dcLennart Poettering dns_scope_check_conflicts(t->scope, p);
527b7a421ff3927d4f3f170b1b143452e88ae1dcLennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering t->block_gc++;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering dns_transaction_process_reply(t, p);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering t->block_gc--;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering /* If the response wasn't useful, then complete the transition now */
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (t->state == DNS_TRANSACTION_PENDING)
527b7a421ff3927d4f3f170b1b143452e88ae1dcLennart Poettering dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
527b7a421ff3927d4f3f170b1b143452e88ae1dcLennart Poettering
527b7a421ff3927d4f3f170b1b143452e88ae1dcLennart Poettering return 0;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering}
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic int dns_transaction_open_tcp(DnsTransaction *t) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering DnsServer *server = NULL;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering _cleanup_close_ int fd = -1;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering int r;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering assert(t);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
03e334a1c7dc8c20c38902aa039440763acc9b17Lennart Poettering if (t->stream)
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering return 0;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering switch (t->scope->protocol) {
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering case DNS_PROTOCOL_DNS:
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering fd = dns_scope_tcp_socket(t->scope, AF_UNSPEC, NULL, 53, &server);
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering break;
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering case DNS_PROTOCOL_LLMNR:
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering /* When we already received a reply to this (but it was truncated), send to its sender address */
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering if (t->received)
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering fd = dns_scope_tcp_socket(t->scope, t->received->family, &t->received->sender, t->received->sender_port, NULL);
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering else {
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering union in_addr_union address;
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering int family = AF_UNSPEC;
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering /* Otherwise, try to talk to the owner of a
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering * the IP address, in case this is a reverse
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering * PTR lookup */
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering r = dns_name_address(DNS_RESOURCE_KEY_NAME(t->key), &family, &address);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (r < 0)
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering return r;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (r == 0)
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering return -EINVAL;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (family != t->scope->family)
d2e54fae5ca7a0f71b5ac8b356a589ff0a09ea0aKay Sievers return -ESRCH;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering fd = dns_scope_tcp_socket(t->scope, family, &address, LLMNR_PORT, NULL);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering }
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering break;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering default:
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering return -EAFNOSUPPORT;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering }
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (fd < 0)
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering return fd;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering r = dns_stream_new(t->scope->manager, &t->stream, t->scope->protocol, fd);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (r < 0)
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering return r;
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering fd = -1;
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering
151b9b9662a90455262ce575a8a8ae74bf4ff336Lennart Poettering r = dns_stream_write_packet(t->stream, t->sent);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (r < 0) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering t->stream = dns_stream_free(t->stream);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering return r;
718db96199eb307751264e4163555662c9a389faLennart Poettering }
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering dns_server_unref(t->server);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering t->server = dns_server_ref(server);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering t->received = dns_packet_unref(t->received);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering t->answer = dns_answer_unref(t->answer);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering t->answer_rcode = 0;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering t->stream->complete = on_stream_complete;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering t->stream->transaction = t;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering /* The interface index is difficult to determine if we are
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering * connecting to the local host, hence fill this in right away
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering * instead of determining it from the socket */
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (t->scope->link)
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering t->stream->ifindex = t->scope->link->ifindex;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
03e334a1c7dc8c20c38902aa039440763acc9b17Lennart Poettering return 0;
03e334a1c7dc8c20c38902aa039440763acc9b17Lennart Poettering}
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic void dns_transaction_next_dns_server(DnsTransaction *t) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering assert(t);
a1e58e8ee1c84b633d6d6d651d5328d4dd4eba5bLennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering t->server = dns_server_unref(t->server);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering t->dns_udp_event_source = sd_event_source_unref(t->dns_udp_event_source);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering t->dns_udp_fd = safe_close(t->dns_udp_fd);
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering dns_scope_next_dns_server(t->scope);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering}
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringvoid dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering usec_t ts;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering int r;
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering assert(t);
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering assert(p);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering assert(t->state == DNS_TRANSACTION_PENDING);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering assert(t->scope);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering assert(t->scope->manager);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering /* Note that this call might invalidate the query. Callers
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering * should hence not attempt to access the query or transaction
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering * after calling this function. */
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering switch (t->scope->protocol) {
2c4f86c1298f402220965682ab0e7729e150a562Lennart Poettering case DNS_PROTOCOL_LLMNR:
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering assert(t->scope->link);
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering
2c4f86c1298f402220965682ab0e7729e150a562Lennart Poettering /* For LLMNR we will not accept any packets from other
2c4f86c1298f402220965682ab0e7729e150a562Lennart Poettering * interfaces */
2c4f86c1298f402220965682ab0e7729e150a562Lennart Poettering
2c4f86c1298f402220965682ab0e7729e150a562Lennart Poettering if (p->ifindex != t->scope->link->ifindex)
2c4f86c1298f402220965682ab0e7729e150a562Lennart Poettering return;
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering if (p->family != t->scope->family)
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering return;
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering /* Tentative packets are not full responses but still
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering * useful for identifying uniqueness conflicts during
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering * probing. */
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering if (DNS_PACKET_LLMNR_T(p)) {
409133be63387fc04d927e8aecd2f6ba03d2f143Lennart Poettering dns_transaction_tentative(t, p);
409133be63387fc04d927e8aecd2f6ba03d2f143Lennart Poettering return;
85a428c69465b047731b6abb5005f01824f1444eLennart Poettering }
85a428c69465b047731b6abb5005f01824f1444eLennart Poettering
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering break;
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering case DNS_PROTOCOL_DNS:
5cb14b3742038b28551b161635a0cba3559404b2Lennart Poettering break;
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering default:
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering assert_not_reached("Invalid DNS protocol.");
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering }
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering if (t->received != p) {
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering dns_packet_unref(t->received);
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering t->received = dns_packet_ref(p);
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering }
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering if (p->ipproto == IPPROTO_TCP) {
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering if (DNS_PACKET_TC(p)) {
409133be63387fc04d927e8aecd2f6ba03d2f143Lennart Poettering /* Truncated via TCP? Somebody must be fucking with us */
409133be63387fc04d927e8aecd2f6ba03d2f143Lennart Poettering dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
409133be63387fc04d927e8aecd2f6ba03d2f143Lennart Poettering return;
409133be63387fc04d927e8aecd2f6ba03d2f143Lennart Poettering }
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering if (DNS_PACKET_ID(p) != t->id) {
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering /* Not the reply to our query? Somebody must be fucking with us */
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering return;
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering }
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering }
85a428c69465b047731b6abb5005f01824f1444eLennart Poettering
85a428c69465b047731b6abb5005f01824f1444eLennart Poettering assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &ts) >= 0);
85a428c69465b047731b6abb5005f01824f1444eLennart Poettering
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering switch (t->scope->protocol) {
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering case DNS_PROTOCOL_DNS:
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering assert(t->server);
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering dns_server_packet_received(t->server, ts - t->start_usec);
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering break;
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering case DNS_PROTOCOL_LLMNR:
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering case DNS_PROTOCOL_MDNS:
ec202eae8e84a4c99f054f771cb832046cb8769fShawn Landden dns_scope_packet_received(t->scope, ts - t->start_usec);
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering break;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering default:
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering break;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering }
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering if (DNS_PACKET_TC(p)) {
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering /* Response was truncated, let's try again with good old TCP */
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering r = dns_transaction_open_tcp(t);
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering if (r == -ESRCH) {
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering /* No servers found? Damn! */
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS);
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering return;
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering }
8e7fd6ade44ce5dde0867ba748c7978ed1206865Lennart Poettering if (r < 0) {
8e7fd6ade44ce5dde0867ba748c7978ed1206865Lennart Poettering /* On LLMNR, if we cannot connect to the host,
8e7fd6ade44ce5dde0867ba748c7978ed1206865Lennart Poettering * we immediately give up */
8e7fd6ade44ce5dde0867ba748c7978ed1206865Lennart Poettering if (t->scope->protocol == DNS_PROTOCOL_LLMNR) {
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering return;
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering }
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering /* On DNS, couldn't send? Try immediately again, with a new server */
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering dns_transaction_next_dns_server(t);
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering r = dns_transaction_go(t);
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering if (r < 0) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering return;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering }
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
a2a5291b3f5ab6ed4c92f51d0fd10a03047380d8Zbigniew Jędrzejewski-Szmek return;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering }
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering }
a2a5291b3f5ab6ed4c92f51d0fd10a03047380d8Zbigniew Jędrzejewski-Szmek
a2a5291b3f5ab6ed4c92f51d0fd10a03047380d8Zbigniew Jędrzejewski-Szmek /* Parse and update the cache */
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering r = dns_packet_extract(p);
a2a5291b3f5ab6ed4c92f51d0fd10a03047380d8Zbigniew Jędrzejewski-Szmek if (r < 0) {
4943c1c94ba751c98763f4232b4350481b22c90aLennart Poettering dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
a2a5291b3f5ab6ed4c92f51d0fd10a03047380d8Zbigniew Jędrzejewski-Szmek return;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering }
a2a5291b3f5ab6ed4c92f51d0fd10a03047380d8Zbigniew Jędrzejewski-Szmek
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering /* Install the answer as answer to the transaction */
a2a5291b3f5ab6ed4c92f51d0fd10a03047380d8Zbigniew Jędrzejewski-Szmek dns_answer_unref(t->answer);
8e7fd6ade44ce5dde0867ba748c7978ed1206865Lennart Poettering t->answer = dns_answer_ref(p->answer);
a2a5291b3f5ab6ed4c92f51d0fd10a03047380d8Zbigniew Jędrzejewski-Szmek t->answer_rcode = DNS_PACKET_RCODE(p);
8e7fd6ade44ce5dde0867ba748c7978ed1206865Lennart Poettering
a2a5291b3f5ab6ed4c92f51d0fd10a03047380d8Zbigniew Jędrzejewski-Szmek /* Only consider responses with equivalent query section to the request */
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering if (p->question->n_keys != 1 || dns_resource_key_equal(p->question->keys[0], t->key) <= 0) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering return;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering }
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering /* According to RFC 4795, section 2.9. only the RRs from the answer section shall be cached */
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering dns_cache_put(&t->scope->cache, t->key, DNS_PACKET_RCODE(p), p->answer, DNS_PACKET_ANCOUNT(p), 0, p->family, &p->sender);
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering if (DNS_PACKET_RCODE(p) == DNS_RCODE_SUCCESS)
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering else
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering dns_transaction_complete(t, DNS_TRANSACTION_FAILURE);
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering}
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering
static int on_dns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
DnsTransaction *t = userdata;
int r;
assert(t);
assert(t->scope);
r = manager_recv(t->scope->manager, fd, DNS_PROTOCOL_DNS, &p);
if (r <= 0)
return r;
if (dns_packet_validate_reply(p) > 0 &&
DNS_PACKET_ID(p) == t->id)
dns_transaction_process_reply(t, p);
else
log_debug("Invalid DNS packet.");
return 0;
}
static int dns_transaction_emit(DnsTransaction *t) {
int r;
assert(t);
if (t->scope->protocol == DNS_PROTOCOL_DNS && !t->server) {
DnsServer *server = NULL;
_cleanup_close_ int fd = -1;
fd = dns_scope_udp_dns_socket(t->scope, &server);
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);
if (r < 0)
return r;
t->dns_udp_fd = fd;
fd = -1;
t->server = dns_server_ref(server);
}
r = dns_scope_emit(t->scope, t->dns_udp_fd, t->sent);
if (r < 0)
return r;
return 0;
}
static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdata) {
DnsTransaction *t = userdata;
int r;
assert(s);
assert(t);
/* Timeout reached? Try again, with a new server */
dns_transaction_next_dns_server(t);
/* ... and possibly increased timeout */
if (t->server)
dns_server_packet_lost(t->server, usec - t->start_usec);
else
dns_scope_packet_lost(t->scope, usec - t->start_usec);
r = dns_transaction_go(t);
if (r < 0)
dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
return 0;
}
static int dns_transaction_make_packet(DnsTransaction *t) {
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
int r;
assert(t);
if (t->sent)
return 0;
r = dns_packet_new_query(&p, t->scope->protocol, 0);
if (r < 0)
return r;
r = dns_scope_good_key(t->scope, t->key);
if (r < 0)
return r;
if (r == 0)
return -EDOM;
r = dns_packet_append_key(p, t->key, NULL);
if (r < 0)
return r;
DNS_PACKET_HEADER(p)->qdcount = htobe16(1);
DNS_PACKET_HEADER(p)->id = t->id;
t->sent = p;
p = NULL;
return 0;
}
static usec_t transaction_get_resend_timeout(DnsTransaction *t) {
assert(t);
assert(t->scope);
switch (t->scope->protocol) {
case DNS_PROTOCOL_DNS:
assert(t->server);
return t->server->resend_timeout;
case DNS_PROTOCOL_LLMNR:
case DNS_PROTOCOL_MDNS:
return t->scope->resend_timeout;
default:
assert_not_reached("Invalid DNS protocol.");
}
}
int dns_transaction_go(DnsTransaction *t) {
bool had_stream;
usec_t ts;
int r;
assert(t);
had_stream = !!t->stream;
dns_transaction_stop(t);
log_debug("Excercising transaction on scope %s on %s/%s",
dns_protocol_to_string(t->scope->protocol),
t->scope->link ? t->scope->link->name : "*",
t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family));
if (t->n_attempts >= TRANSACTION_ATTEMPTS_MAX(t->scope->protocol)) {
dns_transaction_complete(t, DNS_TRANSACTION_ATTEMPTS_MAX_REACHED);
return 0;
}
if (t->scope->protocol == DNS_PROTOCOL_LLMNR && had_stream) {
/* If we already tried via a stream, then we don't
* retry on LLMNR. See RFC 4795, Section 2.7. */
dns_transaction_complete(t, DNS_TRANSACTION_ATTEMPTS_MAX_REACHED);
return 0;
}
assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &ts) >= 0);
t->n_attempts++;
t->start_usec = ts;
t->received = dns_packet_unref(t->received);
t->answer = dns_answer_unref(t->answer);
t->answer_rcode = 0;
/* Check the zone, but obly if this transaction is not used
* for probing or verifying a zone item. */
if (set_isempty(t->zone_items)) {
r = dns_zone_lookup(&t->scope->zone, t->key, &t->answer, NULL, NULL);
if (r < 0)
return r;
if (r > 0) {
t->answer_rcode = DNS_RCODE_SUCCESS;
dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
return 0;
}
}
/* Check the cache, but only if this transaction is not used
* for probing or verifying a zone item. */
if (set_isempty(t->zone_items)) {
/* Before trying the cache, let's make sure we figured out a
* server to use. Should this cause a change of server this
* might flush the cache. */
dns_scope_get_dns_server(t->scope);
/* Let's then prune all outdated entries */
dns_cache_prune(&t->scope->cache);
r = dns_cache_lookup(&t->scope->cache, t->key, &t->answer_rcode, &t->answer);
if (r < 0)
return r;
if (r > 0) {
if (t->answer_rcode == DNS_RCODE_SUCCESS)
dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
else
dns_transaction_complete(t, DNS_TRANSACTION_FAILURE);
return 0;
}
}
if (t->scope->protocol == DNS_PROTOCOL_LLMNR && !t->initial_jitter) {
usec_t jitter;
/* RFC 4795 Section 2.7 suggests all queries should be
* delayed by a random time from 0 to JITTER_INTERVAL. */
t->initial_jitter = true;
random_bytes(&jitter, sizeof(jitter));
jitter %= LLMNR_JITTER_INTERVAL_USEC;
r = sd_event_add_time(
t->scope->manager->event,
&t->timeout_event_source,
clock_boottime_or_monotonic(),
ts + jitter,
LLMNR_JITTER_INTERVAL_USEC,
on_transaction_timeout, t);
if (r < 0)
return r;
t->n_attempts = 0;
t->state = DNS_TRANSACTION_PENDING;
log_debug("Delaying LLMNR transaction for " USEC_FMT "us.", jitter);
return 0;
}
/* Otherwise, we need to ask the network */
r = dns_transaction_make_packet(t);
if (r == -EDOM) {
/* Not the right request to make on this network?
* (i.e. an A request made on IPv6 or an AAAA request
* made on IPv4, on LLMNR or mDNS.) */
dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS);
return 0;
}
if (r < 0)
return r;
if (t->scope->protocol == DNS_PROTOCOL_LLMNR &&
(dns_name_endswith(DNS_RESOURCE_KEY_NAME(t->key), "in-addr.arpa") > 0 ||
dns_name_endswith(DNS_RESOURCE_KEY_NAME(t->key), "ip6.arpa") > 0)) {
/* RFC 4795, Section 2.4. says reverse lookups shall
* always be made via TCP on LLMNR */
r = dns_transaction_open_tcp(t);
} else {
/* Try via UDP, and if that fails due to large size try via TCP */
r = dns_transaction_emit(t);
if (r == -EMSGSIZE)
r = dns_transaction_open_tcp(t);
}
if (r == -ESRCH) {
/* No servers to send this to? */
dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS);
return 0;
} else if (r < 0) {
if (t->scope->protocol != DNS_PROTOCOL_DNS) {
dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
return 0;
}
/* Couldn't send? Try immediately again, with a new server */
dns_transaction_next_dns_server(t);
return dns_transaction_go(t);
}
r = sd_event_add_time(
t->scope->manager->event,
&t->timeout_event_source,
clock_boottime_or_monotonic(),
ts + transaction_get_resend_timeout(t), 0,
on_transaction_timeout, t);
if (r < 0)
return r;
t->state = DNS_TRANSACTION_PENDING;
return 1;
}
static const char* const dns_transaction_state_table[_DNS_TRANSACTION_STATE_MAX] = {
[DNS_TRANSACTION_NULL] = "null",
[DNS_TRANSACTION_PENDING] = "pending",
[DNS_TRANSACTION_FAILURE] = "failure",
[DNS_TRANSACTION_SUCCESS] = "success",
[DNS_TRANSACTION_NO_SERVERS] = "no-servers",
[DNS_TRANSACTION_TIMEOUT] = "timeout",
[DNS_TRANSACTION_ATTEMPTS_MAX_REACHED] = "attempts-max-reached",
[DNS_TRANSACTION_INVALID_REPLY] = "invalid-reply",
[DNS_TRANSACTION_RESOURCES] = "resources",
[DNS_TRANSACTION_ABORTED] = "aborted",
};
DEFINE_STRING_TABLE_LOOKUP(dns_transaction_state, DnsTransactionState);