resolved-dns-transaction.c revision ae6a4bbf318e197813227e50c245a00de03784a2
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering This file is part of systemd.
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering Copyright 2014 Lennart 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 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 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/>.
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart PoetteringDnsTransaction* dns_transaction_free(DnsTransaction *t) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering sd_event_source_unref(t->timeout_event_source);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering sd_event_source_unref(t->dns_udp_event_source);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering hashmap_remove(t->scope->transactions, t->key);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering hashmap_remove(t->scope->manager->dns_transactions, UINT_TO_PTR(t->id));
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering while ((c = set_steal_first(t->query_candidates)))
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering while ((i = set_steal_first(t->zone_items)))
cc3773810855956bad92337cee8fa193584ab62eLennart PoetteringDEFINE_TRIVIAL_CLEANUP_FUNC(DnsTransaction*, dns_transaction_free);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringvoid dns_transaction_gc(DnsTransaction *t) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (set_isempty(t->query_candidates) && set_isempty(t->zone_items))
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringint dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering _cleanup_(dns_transaction_freep) DnsTransaction *t = NULL;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering r = hashmap_ensure_allocated(&s->manager->dns_transactions, NULL);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering r = hashmap_ensure_allocated(&s->transactions, &dns_resource_key_hash_ops);
90b2de37b80603168f4e9c9c81cff7eea4efa21aZbigniew Jędrzejewski-Szmek /* Find a fresh, unused transaction id */
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering while (t->id == 0 ||
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering hashmap_get(s->manager->dns_transactions, UINT_TO_PTR(t->id)));
88231eb62cafc8bb51406919c8cf6019dc1ac916Thomas Hindoe Paaboel Andersen r = hashmap_put(s->manager->dns_transactions, UINT_TO_PTR(t->id), t);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering r = hashmap_put(s->transactions, t->key, t);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering hashmap_remove(s->manager->dns_transactions, UINT_TO_PTR(t->id));
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic void dns_transaction_stop(DnsTransaction *t) {
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poettering t->timeout_event_source = sd_event_source_unref(t->timeout_event_source);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering /* Note that we do not drop the UDP socket here, as we want to
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poettering * reuse it to repeat the interaction. */
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poetteringstatic void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (manager_our_packet(t->scope->manager, p) != 0)
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering in_addr_to_string(p->family, &p->sender, &pretty);
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),
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.");
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering log_debug("We have the lexicographically larger IP address and thus lost in the conflict.");
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering /* First, make sure the zone item drops the reference
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering /* Secondly, report this as conflict, so that we might
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering * look for a different hostname */
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringvoid dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering assert(!IN_SET(state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
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 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),
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 */
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic int on_stream_complete(DnsStream *s, int error) {
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering /* Copy the data we care about out of the stream before we
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering * destroy it. */
a34faf579d2be139b0b9e8cd0c73ad4d918ef736Lukas Nykryn dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
a34faf579d2be139b0b9e8cd0c73ad4d918ef736Lukas Nykryn dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering /* If the response wasn't useful, then complete the transition now */
527b7a421ff3927d4f3f170b1b143452e88ae1dcLennart Poettering dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic int dns_transaction_open_tcp(DnsTransaction *t) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering fd = dns_scope_tcp_socket(t->scope, AF_UNSPEC, NULL, 53, &server);
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering /* When we already received a reply to this (but it was truncated), send to its sender address */
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering fd = dns_scope_tcp_socket(t->scope, t->received->family, &t->received->sender, t->received->sender_port, NULL);
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 r = dns_name_address(DNS_RESOURCE_KEY_NAME(t->key), &family, &address);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering fd = dns_scope_tcp_socket(t->scope, family, &address, LLMNR_PORT, NULL);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering r = dns_stream_new(t->scope->manager, &t->stream, t->scope->protocol, fd);
151b9b9662a90455262ce575a8a8ae74bf4ff336Lennart Poettering r = dns_stream_write_packet(t->stream, t->sent);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering t->received = dns_packet_unref(t->received);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering t->stream->complete = on_stream_complete;
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 t->stream->ifindex = t->scope->link->ifindex;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic void dns_transaction_next_dns_server(DnsTransaction *t) {
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);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringvoid dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering assert(t->state == DNS_TRANSACTION_PENDING);
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. */
2c4f86c1298f402220965682ab0e7729e150a562Lennart Poettering /* For LLMNR we will not accept any packets from other
2c4f86c1298f402220965682ab0e7729e150a562Lennart Poettering * interfaces */
2c4f86c1298f402220965682ab0e7729e150a562Lennart Poettering if (p->ifindex != t->scope->link->ifindex)
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering /* Tentative packets are not full responses but still
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering * useful for identifying uniqueness conflicts during
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering assert_not_reached("Invalid DNS protocol.");
409133be63387fc04d927e8aecd2f6ba03d2f143Lennart Poettering /* Truncated via TCP? Somebody must be fucking with us */
409133be63387fc04d927e8aecd2f6ba03d2f143Lennart Poettering dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering /* Not the reply to our query? Somebody must be fucking with us */
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
85a428c69465b047731b6abb5005f01824f1444eLennart Poettering assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &ts) >= 0);
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering dns_server_packet_received(t->server, ts - t->start_usec);
ec202eae8e84a4c99f054f771cb832046cb8769fShawn Landden dns_scope_packet_received(t->scope, ts - t->start_usec);
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering /* Response was truncated, let's try again with good old TCP */
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering /* No servers found? Damn! */
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS);
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 /* On DNS, couldn't send? Try immediately again, with a new server */
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
a2a5291b3f5ab6ed4c92f51d0fd10a03047380d8Zbigniew Jędrzejewski-Szmek /* Parse and update the cache */
4943c1c94ba751c98763f4232b4350481b22c90aLennart Poettering dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering /* Install the answer as answer to the transaction */
a2a5291b3f5ab6ed4c92f51d0fd10a03047380d8Zbigniew Jędrzejewski-Szmek t->answer_rcode = DNS_PACKET_RCODE(p);
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 /* 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 if (DNS_PACKET_RCODE(p) == DNS_RCODE_SUCCESS)
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering dns_transaction_complete(t, DNS_TRANSACTION_FAILURE);
assert(t);
if (dns_packet_validate_reply(p) > 0 &&
assert(t);
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);
if (t->server)
r = dns_transaction_go(t);
assert(t);
if (t->sent)
return -EDOM;
t->sent = p;
p = NULL;
assert(t);
case DNS_PROTOCOL_DNS:
case DNS_PROTOCOL_LLMNR:
case DNS_PROTOCOL_MDNS:
bool had_stream;
assert(t);
t->n_attempts++;
t->answer_rcode = 0;
t->initial_jitter = true;
r = sd_event_add_time(
&t->timeout_event_source,
t->n_attempts = 0;
r = dns_transaction_make_packet(t);
if (r == -EDOM) {
r = dns_transaction_open_tcp(t);
r = dns_transaction_emit(t);
if (r == -EMSGSIZE)
r = dns_transaction_open_tcp(t);
if (r == -ESRCH) {
return dns_transaction_go(t);
r = sd_event_add_time(
&t->timeout_event_source,