diff options
author | Andy Wingo <wingo@pobox.com> | 2015-04-19 13:28:46 +0200 |
---|---|---|
committer | Andy Wingo <wingo@pobox.com> | 2015-04-19 13:32:26 +0200 |
commit | 2dd57213e1114c1e034e52b7ff8eaeaee752d9f3 (patch) | |
tree | 524803384d8065fd99a70fdbc1dff6ede6dce3f4 | |
parent | 1dcca8e5fe679b21a7203d6e35d58aa7aa83bbe1 (diff) |
Remove src/resolve
43 files changed, 0 insertions, 12213 deletions
diff --git a/src/resolve/.gitignore b/src/resolve/.gitignore deleted file mode 100644 index f0835923b7..0000000000 --- a/src/resolve/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -/resolved-gperf.c -/resolved.conf -/dns_type-from-name.gperf -/dns_type-from-name.h -/dns_type-list.txt -/dns_type-to-name.h diff --git a/src/resolve/Makefile b/src/resolve/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/resolve/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile
\ No newline at end of file diff --git a/src/resolve/dns-type.c b/src/resolve/dns-type.c deleted file mode 100644 index a3e740896f..0000000000 --- a/src/resolve/dns-type.c +++ /dev/null @@ -1,45 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2014 Zbigniew Jędrzejewski-Szmek - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include "dns-type.h" - -typedef const struct { - uint16_t type; - const char *name; -} dns_type; - -static const struct dns_type_name * -lookup_dns_type (register const char *str, register unsigned int len); - -#include "dns_type-from-name.h" -#include "dns_type-to-name.h" - -int dns_type_from_string(const char *s) { - const struct dns_type_name *sc; - - assert(s); - - sc = lookup_dns_type(s, strlen(s)); - if (!sc) - return _DNS_TYPE_INVALID; - - return sc->id; -} diff --git a/src/resolve/dns-type.h b/src/resolve/dns-type.h deleted file mode 100644 index 86951d233a..0000000000 --- a/src/resolve/dns-type.h +++ /dev/null @@ -1,120 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2014 Zbigniew Jędrzejewski-Szmek - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#pragma once - -#include "macro.h" - -const char *dns_type_to_string(int type); -int dns_type_from_string(const char *s); - -/* DNS record types, taken from - * http://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml. - */ -enum { - /* Normal records */ - DNS_TYPE_A = 0x01, - DNS_TYPE_NS, - DNS_TYPE_MD, - DNS_TYPE_MF, - DNS_TYPE_CNAME, - DNS_TYPE_SOA, - DNS_TYPE_MB, - DNS_TYPE_MG, - DNS_TYPE_MR, - DNS_TYPE_NULL, - DNS_TYPE_WKS, - DNS_TYPE_PTR, - DNS_TYPE_HINFO, - DNS_TYPE_MINFO, - DNS_TYPE_MX, - DNS_TYPE_TXT, - DNS_TYPE_RP, - DNS_TYPE_AFSDB, - DNS_TYPE_X25, - DNS_TYPE_ISDN, - DNS_TYPE_RT, - DNS_TYPE_NSAP, - DNS_TYPE_NSAP_PTR, - DNS_TYPE_SIG, - DNS_TYPE_KEY, - DNS_TYPE_PX, - DNS_TYPE_GPOS, - DNS_TYPE_AAAA, - DNS_TYPE_LOC, - DNS_TYPE_NXT, - DNS_TYPE_EID, - DNS_TYPE_NIMLOC, - DNS_TYPE_SRV, - DNS_TYPE_ATMA, - DNS_TYPE_NAPTR, - DNS_TYPE_KX, - DNS_TYPE_CERT, - DNS_TYPE_A6, - DNS_TYPE_DNAME, - DNS_TYPE_SINK, - DNS_TYPE_OPT, /* EDNS0 option */ - DNS_TYPE_APL, - DNS_TYPE_DS, - DNS_TYPE_SSHFP, - DNS_TYPE_IPSECKEY, - DNS_TYPE_RRSIG, - DNS_TYPE_NSEC, - DNS_TYPE_DNSKEY, - DNS_TYPE_DHCID, - DNS_TYPE_NSEC3, - DNS_TYPE_NSEC3PARAM, - DNS_TYPE_TLSA, - - DNS_TYPE_HIP = 0x37, - DNS_TYPE_NINFO, - DNS_TYPE_RKEY, - DNS_TYPE_TALINK, - DNS_TYPE_CDS, - DNS_TYPE_CDNSKEY, - - DNS_TYPE_SPF = 0x63, - DNS_TYPE_NID, - DNS_TYPE_L32, - DNS_TYPE_L64, - DNS_TYPE_LP, - DNS_TYPE_EUI48, - DNS_TYPE_EUI64, - - DNS_TYPE_TKEY = 0xF9, - DNS_TYPE_TSIG, - DNS_TYPE_IXFR, - DNS_TYPE_AXFR, - DNS_TYPE_MAILB, - DNS_TYPE_MAILA, - DNS_TYPE_ANY, - DNS_TYPE_URI, - DNS_TYPE_CAA, - DNS_TYPE_TA = 0x8000, - DNS_TYPE_DLV, - - _DNS_TYPE_MAX, - _DNS_TYPE_INVALID = -1 -}; - -assert_cc(DNS_TYPE_SSHFP == 44); -assert_cc(DNS_TYPE_TLSA == 52); -assert_cc(DNS_TYPE_ANY == 255); diff --git a/src/resolve/org.freedesktop.resolve1.conf b/src/resolve/org.freedesktop.resolve1.conf deleted file mode 100644 index 25b09774e5..0000000000 --- a/src/resolve/org.freedesktop.resolve1.conf +++ /dev/null @@ -1,27 +0,0 @@ -<?xml version="1.0"?> <!--*-nxml-*--> -<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" - "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> - -<!-- - This file is part of systemd. - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. ---> - -<busconfig> - - <policy user="systemd-resolve"> - <allow own="org.freedesktop.resolve1"/> - <allow send_destination="org.freedesktop.resolve1"/> - <allow receive_sender="org.freedesktop.resolve1"/> - </policy> - - <policy context="default"> - <allow send_destination="org.freedesktop.resolve1"/> - <allow receive_sender="org.freedesktop.resolve1"/> - </policy> - -</busconfig> diff --git a/src/resolve/org.freedesktop.resolve1.service b/src/resolve/org.freedesktop.resolve1.service deleted file mode 100644 index 7ac5c323f0..0000000000 --- a/src/resolve/org.freedesktop.resolve1.service +++ /dev/null @@ -1,12 +0,0 @@ -# This file is part of systemd. -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. - -[D-BUS Service] -Name=org.freedesktop.resolve1 -Exec=/bin/false -User=root -SystemdService=dbus-org.freedesktop.resolve1.service diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c deleted file mode 100644 index fba2afcc8d..0000000000 --- a/src/resolve/resolved-bus.c +++ /dev/null @@ -1,755 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include "bus-common-errors.h" -#include "bus-util.h" - -#include "resolved-dns-domain.h" -#include "resolved-bus.h" -#include "resolved-def.h" - -static int reply_query_state(DnsQuery *q) { - _cleanup_free_ char *ip = NULL; - const char *name; - int r; - - if (q->request_hostname) - name = q->request_hostname; - else { - r = in_addr_to_string(q->request_family, &q->request_address, &ip); - if (r < 0) - return r; - - name = ip; - } - - switch (q->state) { - - case DNS_TRANSACTION_NO_SERVERS: - return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found"); - - case DNS_TRANSACTION_TIMEOUT: - return sd_bus_reply_method_errorf(q->request, SD_BUS_ERROR_TIMEOUT, "Query timed out"); - - case DNS_TRANSACTION_ATTEMPTS_MAX_REACHED: - return sd_bus_reply_method_errorf(q->request, SD_BUS_ERROR_TIMEOUT, "All attempts to contact name servers or networks failed"); - - case DNS_TRANSACTION_INVALID_REPLY: - return sd_bus_reply_method_errorf(q->request, BUS_ERROR_INVALID_REPLY, "Received invalid reply"); - - case DNS_TRANSACTION_RESOURCES: - return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_RESOURCES, "Not enough resources"); - - case DNS_TRANSACTION_ABORTED: - return sd_bus_reply_method_errorf(q->request, BUS_ERROR_ABORTED, "Query aborted"); - - case DNS_TRANSACTION_FAILURE: { - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; - - if (q->answer_rcode == DNS_RCODE_NXDOMAIN) - sd_bus_error_setf(&error, _BUS_ERROR_DNS "NXDOMAIN", "'%s' not found", name); - else { - const char *rc, *n; - char p[3]; /* the rcode is 4 bits long */ - - rc = dns_rcode_to_string(q->answer_rcode); - if (!rc) { - sprintf(p, "%i", q->answer_rcode); - rc = p; - } - - n = strjoina(_BUS_ERROR_DNS, rc); - sd_bus_error_setf(&error, n, "Could not resolve '%s', server or network returned error %s", name, rc); - } - - return sd_bus_reply_method_error(q->request, &error); - } - - case DNS_TRANSACTION_NULL: - case DNS_TRANSACTION_PENDING: - case DNS_TRANSACTION_SUCCESS: - default: - assert_not_reached("Impossible state"); - } -} - -static int append_address(sd_bus_message *reply, DnsResourceRecord *rr) { - int r; - - assert(reply); - assert(rr); - - r = sd_bus_message_open_container(reply, 'r', "iay"); - if (r < 0) - return r; - - if (rr->key->type == DNS_TYPE_A) { - r = sd_bus_message_append(reply, "i", AF_INET); - if (r < 0) - return r; - - r = sd_bus_message_append_array(reply, 'y', &rr->a.in_addr, sizeof(struct in_addr)); - - } else if (rr->key->type == DNS_TYPE_AAAA) { - r = sd_bus_message_append(reply, "i", AF_INET6); - if (r < 0) - return r; - - r = sd_bus_message_append_array(reply, 'y', &rr->aaaa.in6_addr, sizeof(struct in6_addr)); - } else - return -EAFNOSUPPORT; - - if (r < 0) - return r; - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - - return 0; -} - -static void bus_method_resolve_hostname_complete(DnsQuery *q) { - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *cname = NULL, *canonical = NULL; - _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; - unsigned added = 0, i; - int r; - - assert(q); - - if (q->state != DNS_TRANSACTION_SUCCESS) { - r = reply_query_state(q); - goto finish; - } - - r = sd_bus_message_new_method_return(q->request, &reply); - if (r < 0) - goto finish; - - r = sd_bus_message_append(reply, "i", q->answer_ifindex); - if (r < 0) - goto finish; - - r = sd_bus_message_open_container(reply, 'a', "(iay)"); - if (r < 0) - goto finish; - - if (q->answer) { - answer = dns_answer_ref(q->answer); - - for (i = 0; i < answer->n_rrs; i++) { - r = dns_question_matches_rr(q->question, answer->rrs[i]); - if (r < 0) - goto finish; - if (r == 0) { - /* Hmm, if this is not an address record, - maybe it's a cname? If so, remember this */ - r = dns_question_matches_cname(q->question, answer->rrs[i]); - if (r < 0) - goto finish; - if (r > 0) - cname = dns_resource_record_ref(answer->rrs[i]); - - continue; - } - - r = append_address(reply, answer->rrs[i]); - if (r < 0) - goto finish; - - if (!canonical) - canonical = dns_resource_record_ref(answer->rrs[i]); - - added ++; - } - } - - if (added == 0) { - if (!cname) { - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of requested type", q->request_hostname); - goto finish; - } - - /* This has a cname? Then update the query with the - * new cname. */ - r = dns_query_cname_redirect(q, cname->cname.name); - if (r < 0) { - if (r == -ELOOP) - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop on '%s'", q->request_hostname); - else - r = sd_bus_reply_method_errno(q->request, -r, NULL); - - goto finish; - } - - /* Before we restart the query, let's see if any of - * the RRs we already got already answers our query */ - for (i = 0; i < answer->n_rrs; i++) { - r = dns_question_matches_rr(q->question, answer->rrs[i]); - if (r < 0) - goto finish; - if (r == 0) - continue; - - r = append_address(reply, answer->rrs[i]); - if (r < 0) - goto finish; - - if (!canonical) - canonical = dns_resource_record_ref(answer->rrs[i]); - - added++; - } - - // what about the cache? - - /* If we didn't find anything, then let's restart the - * query, this time with the cname */ - if (added <= 0) { - r = dns_query_go(q); - if (r == -ESRCH) { - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found"); - goto finish; - } - if (r < 0) { - r = sd_bus_reply_method_errno(q->request, -r, NULL); - goto finish; - } - - return; - } - } - - r = sd_bus_message_close_container(reply); - if (r < 0) - goto finish; - - /* Return the precise spelling and uppercasing reported by the server */ - assert(canonical); - r = sd_bus_message_append(reply, "st", DNS_RESOURCE_KEY_NAME(canonical->key), SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family)); - if (r < 0) - goto finish; - - r = sd_bus_send(q->manager->bus, reply, NULL); - -finish: - if (r < 0) { - log_error_errno(r, "Failed to send hostname reply: %m"); - sd_bus_reply_method_errno(q->request, -r, NULL); - } - - dns_query_free(q); -} - -static int check_ifindex_flags(int ifindex, uint64_t *flags, sd_bus_error *error) { - assert(flags); - - if (ifindex < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index"); - - if (*flags & ~SD_RESOLVED_FLAGS_ALL) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid flags parameter"); - - if (*flags == 0) - *flags = SD_RESOLVED_FLAGS_DEFAULT; - - return 0; -} - -static int bus_method_resolve_hostname(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; - Manager *m = userdata; - const char *hostname; - int family, ifindex; - uint64_t flags; - DnsQuery *q; - int r; - - assert(bus); - assert(message); - assert(m); - - r = sd_bus_message_read(message, "isit", &ifindex, &hostname, &family, &flags); - if (r < 0) - return r; - - if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family); - - r = dns_name_normalize(hostname, NULL); - if (r < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", hostname); - - r = check_ifindex_flags(ifindex, &flags, error); - if (r < 0) - return r; - - question = dns_question_new(family == AF_UNSPEC ? 2 : 1); - if (!question) - return -ENOMEM; - - if (family != AF_INET6) { - _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; - - key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, hostname); - if (!key) - return -ENOMEM; - - r = dns_question_add(question, key); - if (r < 0) - return r; - } - - if (family != AF_INET) { - _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; - - key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, hostname); - if (!key) - return -ENOMEM; - - r = dns_question_add(question, key); - if (r < 0) - return r; - } - - r = dns_query_new(m, &q, question, ifindex, flags); - if (r < 0) - return r; - - q->request = sd_bus_message_ref(message); - q->request_family = family; - q->request_hostname = hostname; - q->complete = bus_method_resolve_hostname_complete; - - r = dns_query_bus_track(q, bus, message); - if (r < 0) - return r; - - r = dns_query_go(q); - if (r < 0) { - dns_query_free(q); - - if (r == -ESRCH) - sd_bus_error_setf(error, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found"); - - return r; - } - - return 1; -} - -static void bus_method_resolve_address_complete(DnsQuery *q) { - _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; - unsigned added = 0, i; - int r; - - assert(q); - - if (q->state != DNS_TRANSACTION_SUCCESS) { - r = reply_query_state(q); - goto finish; - } - - r = sd_bus_message_new_method_return(q->request, &reply); - if (r < 0) - goto finish; - - r = sd_bus_message_append(reply, "i", q->answer_ifindex); - if (r < 0) - goto finish; - - r = sd_bus_message_open_container(reply, 'a', "s"); - if (r < 0) - goto finish; - - if (q->answer) { - answer = dns_answer_ref(q->answer); - - for (i = 0; i < answer->n_rrs; i++) { - r = dns_question_matches_rr(q->question, answer->rrs[i]); - if (r < 0) - goto finish; - if (r == 0) - continue; - - r = sd_bus_message_append(reply, "s", answer->rrs[i]->ptr.name); - if (r < 0) - goto finish; - - added ++; - } - } - - if (added == 0) { - _cleanup_free_ char *ip = NULL; - - in_addr_to_string(q->request_family, &q->request_address, &ip); - - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Address '%s' does not have any RR of requested type", ip); - goto finish; - } - - r = sd_bus_message_close_container(reply); - if (r < 0) - goto finish; - - r = sd_bus_message_append(reply, "t", SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family)); - if (r < 0) - goto finish; - - r = sd_bus_send(q->manager->bus, reply, NULL); - -finish: - if (r < 0) { - log_error_errno(r, "Failed to send address reply: %m"); - sd_bus_reply_method_errno(q->request, -r, NULL); - } - - dns_query_free(q); -} - -static int bus_method_resolve_address(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; - _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; - _cleanup_free_ char *reverse = NULL; - Manager *m = userdata; - int family, ifindex; - uint64_t flags; - const void *d; - DnsQuery *q; - size_t sz; - int r; - - assert(bus); - assert(message); - assert(m); - - r = sd_bus_message_read(message, "ii", &ifindex, &family); - if (r < 0) - return r; - - if (!IN_SET(family, AF_INET, AF_INET6)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family); - - r = sd_bus_message_read_array(message, 'y', &d, &sz); - if (r < 0) - return r; - - if (sz != FAMILY_ADDRESS_SIZE(family)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size"); - - r = sd_bus_message_read(message, "t", &flags); - if (r < 0) - return r; - - r = check_ifindex_flags(ifindex, &flags, error); - if (r < 0) - return r; - - r = dns_name_reverse(family, d, &reverse); - if (r < 0) - return r; - - question = dns_question_new(1); - if (!question) - return -ENOMEM; - - key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, reverse); - if (!key) - return -ENOMEM; - - reverse = NULL; - - r = dns_question_add(question, key); - if (r < 0) - return r; - - r = dns_query_new(m, &q, question, ifindex, flags); - if (r < 0) - return r; - - q->request = sd_bus_message_ref(message); - q->request_family = family; - memcpy(&q->request_address, d, sz); - q->complete = bus_method_resolve_address_complete; - - r = dns_query_bus_track(q, bus, message); - if (r < 0) - return r; - - r = dns_query_go(q); - if (r < 0) { - dns_query_free(q); - - if (r == -ESRCH) - sd_bus_error_setf(error, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found"); - - return r; - } - - return 1; -} - -static void bus_method_resolve_record_complete(DnsQuery *q) { - _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; - unsigned added = 0, i; - int r; - - assert(q); - - if (q->state != DNS_TRANSACTION_SUCCESS) { - r = reply_query_state(q); - goto finish; - } - - r = sd_bus_message_new_method_return(q->request, &reply); - if (r < 0) - goto finish; - - r = sd_bus_message_append(reply, "i", q->answer_ifindex); - if (r < 0) - goto finish; - - r = sd_bus_message_open_container(reply, 'a', "(qqay)"); - if (r < 0) - goto finish; - - if (q->answer) { - answer = dns_answer_ref(q->answer); - - for (i = 0; i < answer->n_rrs; i++) { - _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; - size_t start; - - r = dns_question_matches_rr(q->question, answer->rrs[i]); - if (r < 0) - goto finish; - if (r == 0) - continue; - - r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0); - if (r < 0) - goto finish; - - r = dns_packet_append_rr(p, answer->rrs[i], &start); - if (r < 0) - goto finish; - - r = sd_bus_message_open_container(reply, 'r', "qqay"); - if (r < 0) - goto finish; - - r = sd_bus_message_append(reply, "qq", answer->rrs[i]->key->class, answer->rrs[i]->key->type); - if (r < 0) - goto finish; - - r = sd_bus_message_append_array(reply, 'y', DNS_PACKET_DATA(p) + start, p->size - start); - if (r < 0) - goto finish; - - r = sd_bus_message_close_container(reply); - if (r < 0) - goto finish; - - added ++; - } - } - - if (added <= 0) { - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Name '%s' does not have any RR of the requested type", q->request_hostname); - goto finish; - } - - r = sd_bus_message_close_container(reply); - if (r < 0) - goto finish; - - r = sd_bus_message_append(reply, "t", SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family)); - if (r < 0) - goto finish; - - r = sd_bus_send(q->manager->bus, reply, NULL); - -finish: - if (r < 0) { - log_error_errno(r, "Failed to send record reply: %m"); - sd_bus_reply_method_errno(q->request, -r, NULL); - } - - dns_query_free(q); -} - -static int bus_method_resolve_record(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; - _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; - Manager *m = userdata; - uint16_t class, type; - const char *name; - int r, ifindex; - uint64_t flags; - DnsQuery *q; - - assert(bus); - assert(message); - assert(m); - - r = sd_bus_message_read(message, "isqqt", &ifindex, &name, &class, &type, &flags); - if (r < 0) - return r; - - r = dns_name_normalize(name, NULL); - if (r < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid name '%s'", name); - - r = check_ifindex_flags(ifindex, &flags, error); - if (r < 0) - return r; - - question = dns_question_new(1); - if (!question) - return -ENOMEM; - - key = dns_resource_key_new(class, type, name); - if (!key) - return -ENOMEM; - - r = dns_question_add(question, key); - if (r < 0) - return r; - - r = dns_query_new(m, &q, question, ifindex, flags); - if (r < 0) - return r; - - q->request = sd_bus_message_ref(message); - q->request_hostname = name; - q->complete = bus_method_resolve_record_complete; - - r = dns_query_bus_track(q, bus, message); - if (r < 0) - return r; - - r = dns_query_go(q); - if (r < 0) { - dns_query_free(q); - - if (r == -ESRCH) - sd_bus_error_setf(error, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found"); - - return r; - } - - return 1; -} - -static const sd_bus_vtable resolve_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_METHOD("ResolveHostname", "isit", "ia(iay)st", bus_method_resolve_hostname, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ResolveAddress", "iiayt", "iast", bus_method_resolve_address, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ResolveRecord", "isqqt", "ia(qqay)t", bus_method_resolve_record, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_VTABLE_END, -}; - -static int on_bus_retry(sd_event_source *s, usec_t usec, void *userdata) { - Manager *m = userdata; - - assert(s); - assert(m); - - m->bus_retry_event_source = sd_event_source_unref(m->bus_retry_event_source); - - manager_connect_bus(m); - return 0; -} - -static int match_prepare_for_sleep(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *ret_error) { - Manager *m = userdata; - int b, r; - - assert(bus); - assert(bus); - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) { - log_debug_errno(r, "Failed to parse PrepareForSleep signal: %m"); - return 0; - } - - if (b) - return 0; - - log_debug("Coming back from suspend, verifying all RRs..."); - - manager_verify_all(m); - return 0; -} - -int manager_connect_bus(Manager *m) { - int r; - - assert(m); - - if (m->bus) - return 0; - - r = sd_bus_default_system(&m->bus); - if (r < 0) { - /* We failed to connect? Yuck, we must be in early - * boot. Let's try in 5s again. As soon as we have - * kdbus we can stop doing this... */ - - log_debug_errno(r, "Failed to connect to bus, trying again in 5s: %m"); - - r = sd_event_add_time(m->event, &m->bus_retry_event_source, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + 5*USEC_PER_SEC, 0, on_bus_retry, m); - if (r < 0) - return log_error_errno(r, "Failed to install bus reconnect time event: %m"); - - return 0; - } - - r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/resolve1", "org.freedesktop.resolve1.Manager", resolve_vtable, m); - if (r < 0) - return log_error_errno(r, "Failed to register object: %m"); - - r = sd_bus_request_name(m->bus, "org.freedesktop.resolve1", 0); - if (r < 0) - return log_error_errno(r, "Failed to register name: %m"); - - r = sd_bus_attach_event(m->bus, m->event, 0); - if (r < 0) - return log_error_errno(r, "Failed to attach bus to event loop: %m"); - - r = sd_bus_add_match(m->bus, &m->prepare_for_sleep_slot, - "type='signal'," - "sender='org.freedesktop.login1'," - "interface='org.freedesktop.login1.Manager'," - "member='PrepareForSleep'," - "path='/org/freedesktop/login1'", - match_prepare_for_sleep, - m); - if (r < 0) - log_error_errno(r, "Failed to add match for PrepareForSleep: %m"); - - return 0; -} diff --git a/src/resolve/resolved-bus.h b/src/resolve/resolved-bus.h deleted file mode 100644 index 1e72891178..0000000000 --- a/src/resolve/resolved-bus.h +++ /dev/null @@ -1,26 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include "resolved-manager.h" - -int manager_connect_bus(Manager *m); diff --git a/src/resolve/resolved-conf.c b/src/resolve/resolved-conf.c deleted file mode 100644 index 7af63b0a82..0000000000 --- a/src/resolve/resolved-conf.c +++ /dev/null @@ -1,153 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2014 Tom Gundersen <teg@jklm.no> - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. - ***/ - -#include "conf-parser.h" - -#include "resolved-conf.h" - -int manager_parse_dns_server(Manager *m, DnsServerType type, const char *string) { - const char *word, *state; - size_t length; - DnsServer *first; - int r; - - assert(m); - assert(string); - - first = type == DNS_SERVER_FALLBACK ? m->fallback_dns_servers : m->dns_servers; - - FOREACH_WORD_QUOTED(word, length, string, state) { - char buffer[length+1]; - int family; - union in_addr_union addr; - bool found = false; - DnsServer *s; - - memcpy(buffer, word, length); - buffer[length] = 0; - - r = in_addr_from_string_auto(buffer, &family, &addr); - if (r < 0) { - log_warning("Ignoring invalid DNS address '%s'", buffer); - continue; - } - - /* Filter out duplicates */ - LIST_FOREACH(servers, s, first) - if (s->family == family && in_addr_equal(family, &s->address, &addr)) { - found = true; - break; - } - - if (found) - continue; - - r = dns_server_new(m, NULL, type, NULL, family, &addr); - if (r < 0) - return r; - } - - return 0; -} - -int config_parse_dnsv( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Manager *m = userdata; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(m); - - if (isempty(rvalue)) - /* Empty assignment means clear the list */ - manager_flush_dns_servers(m, ltype); - else { - /* Otherwise add to the list */ - r = manager_parse_dns_server(m, ltype, rvalue); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to parse DNS server string '%s'. Ignoring.", rvalue); - return 0; - } - } - - /* If we have a manual setting, then we stop reading - * /etc/resolv.conf */ - if (ltype == DNS_SERVER_SYSTEM) - m->read_resolv_conf = false; - - return 0; -} - -int config_parse_support( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Support support, *v = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - support = support_from_string(rvalue); - if (support < 0) { - r = parse_boolean(rvalue); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to parse support level '%s'. Ignoring.", rvalue); - return 0; - } - - support = r ? SUPPORT_YES : SUPPORT_NO; - } - - *v = support; - return 0; -} - -int manager_parse_config_file(Manager *m) { - assert(m); - - return config_parse_many("/etc/systemd/resolved.conf", - CONF_DIRS_NULSTR("systemd/resolved.conf"), - "Resolve\0", - config_item_perf_lookup, resolved_gperf_lookup, - false, m); -} diff --git a/src/resolve/resolved-conf.h b/src/resolve/resolved-conf.h deleted file mode 100644 index b3dbea7b6b..0000000000 --- a/src/resolve/resolved-conf.h +++ /dev/null @@ -1,32 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Tom Gundersen <teg@jklm.no> - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include "resolved-manager.h" - -int manager_parse_dns_server(Manager *m, DnsServerType type, const char *string); -int manager_parse_config_file(Manager *m); - -const struct ConfigPerfItem* resolved_gperf_lookup(const char *key, unsigned length); - -int config_parse_dnsv(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_support(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/resolve/resolved-def.h b/src/resolve/resolved-def.h deleted file mode 100644 index 086d111205..0000000000 --- a/src/resolve/resolved-def.h +++ /dev/null @@ -1,30 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#define SD_RESOLVED_DNS ((uint64_t) 1) -#define SD_RESOLVED_LLMNR_IPV4 ((uint64_t) 2) -#define SD_RESOLVED_LLMNR_IPV6 ((uint64_t) 4) -#define SD_RESOLVED_LLMNR (SD_RESOLVED_LLMNR_IPV4|SD_RESOLVED_LLMNR_IPV6) - -#define SD_RESOLVED_FLAGS_ALL (SD_RESOLVED_DNS|SD_RESOLVED_LLMNR_IPV4|SD_RESOLVED_LLMNR_IPV6) -#define SD_RESOLVED_FLAGS_DEFAULT SD_RESOLVED_FLAGS_ALL diff --git a/src/resolve/resolved-dns-answer.c b/src/resolve/resolved-dns-answer.c deleted file mode 100644 index 7c4ab18b58..0000000000 --- a/src/resolve/resolved-dns-answer.c +++ /dev/null @@ -1,238 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include "resolved-dns-answer.h" -#include "resolved-dns-domain.h" - -DnsAnswer *dns_answer_new(unsigned n) { - DnsAnswer *a; - - assert(n > 0); - - a = malloc0(offsetof(DnsAnswer, rrs) + sizeof(DnsResourceRecord*) * n); - if (!a) - return NULL; - - a->n_ref = 1; - a->n_allocated = n; - - return a; -} - -DnsAnswer *dns_answer_ref(DnsAnswer *a) { - if (!a) - return NULL; - - assert(a->n_ref > 0); - a->n_ref++; - return a; -} - -DnsAnswer *dns_answer_unref(DnsAnswer *a) { - if (!a) - return NULL; - - assert(a->n_ref > 0); - - if (a->n_ref == 1) { - unsigned i; - - for (i = 0; i < a->n_rrs; i++) - dns_resource_record_unref(a->rrs[i]); - - free(a); - } else - a->n_ref--; - - return NULL; -} - -int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr) { - unsigned i; - int r; - - assert(a); - assert(rr); - - for (i = 0; i < a->n_rrs; i++) { - r = dns_resource_record_equal(a->rrs[i], rr); - if (r < 0) - return r; - if (r > 0) { - /* Entry already exists, keep the entry with - * the higher RR, or the one with TTL 0 */ - - if (rr->ttl == 0 || (rr->ttl > a->rrs[i]->ttl && a->rrs[i]->ttl != 0)) { - dns_resource_record_ref(rr); - dns_resource_record_unref(a->rrs[i]); - a->rrs[i] = rr; - } - - return 0; - } - } - - if (a->n_rrs >= a->n_allocated) - return -ENOSPC; - - a->rrs[a->n_rrs++] = dns_resource_record_ref(rr); - return 1; -} - -int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl) { - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *soa = NULL; - - soa = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_SOA, name); - if (!soa) - return -ENOMEM; - - soa->ttl = ttl; - - soa->soa.mname = strdup(name); - if (!soa->soa.mname) - return -ENOMEM; - - soa->soa.rname = strappend("root.", name); - if (!soa->soa.rname) - return -ENOMEM; - - soa->soa.serial = 1; - soa->soa.refresh = 1; - soa->soa.retry = 1; - soa->soa.expire = 1; - soa->soa.minimum = ttl; - - return dns_answer_add(a, soa); -} - -int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key) { - unsigned i; - int r; - - assert(a); - assert(key); - - for (i = 0; i < a->n_rrs; i++) { - r = dns_resource_key_match_rr(key, a->rrs[i]); - if (r < 0) - return r; - if (r > 0) - return 1; - } - - return 0; -} - -int dns_answer_find_soa(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord **ret) { - unsigned i; - - assert(a); - assert(key); - assert(ret); - - /* For a SOA record we can never find a matching SOA record */ - if (key->type == DNS_TYPE_SOA) - return 0; - - for (i = 0; i < a->n_rrs; i++) { - - if (a->rrs[i]->key->class != DNS_CLASS_IN) - continue; - - if (a->rrs[i]->key->type != DNS_TYPE_SOA) - continue; - - if (dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(a->rrs[i]->key))) { - *ret = a->rrs[i]; - return 1; - } - } - - return 0; -} - -DnsAnswer *dns_answer_merge(DnsAnswer *a, DnsAnswer *b) { - _cleanup_(dns_answer_unrefp) DnsAnswer *ret = NULL; - DnsAnswer *k; - unsigned i; - int r; - - if (a && (!b || b->n_rrs <= 0)) - return dns_answer_ref(a); - if ((!a || a->n_rrs <= 0) && b) - return dns_answer_ref(b); - - ret = dns_answer_new((a ? a->n_rrs : 0) + (b ? b->n_rrs : 0)); - if (!ret) - return NULL; - - if (a) { - for (i = 0; i < a->n_rrs; i++) { - r = dns_answer_add(ret, a->rrs[i]); - if (r < 0) - return NULL; - } - } - - if (b) { - for (i = 0; i < b->n_rrs; i++) { - r = dns_answer_add(ret, b->rrs[i]); - if (r < 0) - return NULL; - } - } - - k = ret; - ret = NULL; - - return k; -} - -void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) { - DnsResourceRecord **rrs; - unsigned i, start, end; - assert(a); - - if (a->n_rrs <= 1) - return; - - start = 0; - end = a->n_rrs-1; - - /* RFC 4795, Section 2.6 suggests we should order entries - * depending on whether the sender is a link-local address. */ - - rrs = newa(DnsResourceRecord*, a->n_rrs); - for (i = 0; i < a->n_rrs; i++) { - - if (a->rrs[i]->key->class == DNS_CLASS_IN && - ((a->rrs[i]->key->type == DNS_TYPE_A && in_addr_is_link_local(AF_INET, (union in_addr_union*) &a->rrs[i]->a.in_addr) != prefer_link_local) || - (a->rrs[i]->key->type == DNS_TYPE_AAAA && in_addr_is_link_local(AF_INET6, (union in_addr_union*) &a->rrs[i]->aaaa.in6_addr) != prefer_link_local))) - /* Order address records that are are not preferred to the end of the array */ - rrs[end--] = a->rrs[i]; - else - /* Order all other records to the beginning of the array */ - rrs[start++] = a->rrs[i]; - } - - assert(start == end+1); - memcpy(a->rrs, rrs, sizeof(DnsResourceRecord*) * a->n_rrs); -} diff --git a/src/resolve/resolved-dns-answer.h b/src/resolve/resolved-dns-answer.h deleted file mode 100644 index af3e462ed5..0000000000 --- a/src/resolve/resolved-dns-answer.h +++ /dev/null @@ -1,48 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -typedef struct DnsAnswer DnsAnswer; - -#include "resolved-dns-rr.h" - -/* A simple array of resource records */ - -struct DnsAnswer { - unsigned n_ref; - unsigned n_rrs, n_allocated; - DnsResourceRecord* rrs[0]; -}; - -DnsAnswer *dns_answer_new(unsigned n); -DnsAnswer *dns_answer_ref(DnsAnswer *a); -DnsAnswer *dns_answer_unref(DnsAnswer *a); - -int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr); -int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl); -int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key); -int dns_answer_find_soa(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord **ret); - -DnsAnswer *dns_answer_merge(DnsAnswer *a, DnsAnswer *b); -void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local); - -DEFINE_TRIVIAL_CLEANUP_FUNC(DnsAnswer*, dns_answer_unref); diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c deleted file mode 100644 index 33ca4d1a45..0000000000 --- a/src/resolve/resolved-dns-cache.c +++ /dev/null @@ -1,564 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include "resolved-dns-cache.h" -#include "resolved-dns-packet.h" - -/* Never cache more than 1K entries */ -#define CACHE_MAX 1024 - -/* We never keep any item longer than 10min in our cache */ -#define CACHE_TTL_MAX_USEC (10 * USEC_PER_MINUTE) - -typedef enum DnsCacheItemType DnsCacheItemType; -typedef struct DnsCacheItem DnsCacheItem; - -enum DnsCacheItemType { - DNS_CACHE_POSITIVE, - DNS_CACHE_NODATA, - DNS_CACHE_NXDOMAIN, -}; - -struct DnsCacheItem { - DnsResourceKey *key; - DnsResourceRecord *rr; - usec_t until; - DnsCacheItemType type; - unsigned prioq_idx; - int owner_family; - union in_addr_union owner_address; - LIST_FIELDS(DnsCacheItem, by_key); -}; - -static void dns_cache_item_free(DnsCacheItem *i) { - if (!i) - return; - - dns_resource_record_unref(i->rr); - dns_resource_key_unref(i->key); - free(i); -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(DnsCacheItem*, dns_cache_item_free); - -static void dns_cache_item_remove_and_free(DnsCache *c, DnsCacheItem *i) { - DnsCacheItem *first; - - assert(c); - - if (!i) - return; - - first = hashmap_get(c->by_key, i->key); - LIST_REMOVE(by_key, first, i); - - if (first) - assert_se(hashmap_replace(c->by_key, first->key, first) >= 0); - else - hashmap_remove(c->by_key, i->key); - - prioq_remove(c->by_expiry, i, &i->prioq_idx); - - dns_cache_item_free(i); -} - -void dns_cache_flush(DnsCache *c) { - DnsCacheItem *i; - - assert(c); - - while ((i = hashmap_first(c->by_key))) - dns_cache_item_remove_and_free(c, i); - - assert(hashmap_size(c->by_key) == 0); - assert(prioq_size(c->by_expiry) == 0); - - hashmap_free(c->by_key); - c->by_key = NULL; - - prioq_free(c->by_expiry); - c->by_expiry = NULL; -} - -static void dns_cache_remove(DnsCache *c, DnsResourceKey *key) { - DnsCacheItem *i; - - assert(c); - assert(key); - - while ((i = hashmap_get(c->by_key, key))) - dns_cache_item_remove_and_free(c, i); -} - -static void dns_cache_make_space(DnsCache *c, unsigned add) { - assert(c); - - if (add <= 0) - return; - - /* Makes space for n new entries. Note that we actually allow - * the cache to grow beyond CACHE_MAX, but only when we shall - * add more RRs to the cache than CACHE_MAX at once. In that - * case the cache will be emptied completely otherwise. */ - - for (;;) { - _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; - DnsCacheItem *i; - - if (prioq_size(c->by_expiry) <= 0) - break; - - if (prioq_size(c->by_expiry) + add < CACHE_MAX) - break; - - i = prioq_peek(c->by_expiry); - assert(i); - - /* Take an extra reference to the key so that it - * doesn't go away in the middle of the remove call */ - key = dns_resource_key_ref(i->key); - dns_cache_remove(c, key); - } -} - -void dns_cache_prune(DnsCache *c) { - usec_t t = 0; - - assert(c); - - /* Remove all entries that are past their TTL */ - - for (;;) { - _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; - DnsCacheItem *i; - - i = prioq_peek(c->by_expiry); - if (!i) - break; - - if (t <= 0) - t = now(CLOCK_BOOTTIME); - - if (i->until > t) - break; - - /* Take an extra reference to the key so that it - * doesn't go away in the middle of the remove call */ - key = dns_resource_key_ref(i->key); - dns_cache_remove(c, key); - } -} - -static int dns_cache_item_prioq_compare_func(const void *a, const void *b) { - const DnsCacheItem *x = a, *y = b; - - if (x->until < y->until) - return -1; - if (x->until > y->until) - return 1; - return 0; -} - -static int dns_cache_init(DnsCache *c) { - int r; - - assert(c); - - r = prioq_ensure_allocated(&c->by_expiry, dns_cache_item_prioq_compare_func); - if (r < 0) - return r; - - r = hashmap_ensure_allocated(&c->by_key, &dns_resource_key_hash_ops); - if (r < 0) - return r; - - return r; -} - -static int dns_cache_link_item(DnsCache *c, DnsCacheItem *i) { - DnsCacheItem *first; - int r; - - assert(c); - assert(i); - - r = prioq_put(c->by_expiry, i, &i->prioq_idx); - if (r < 0) - return r; - - first = hashmap_get(c->by_key, i->key); - if (first) { - LIST_PREPEND(by_key, first, i); - assert_se(hashmap_replace(c->by_key, first->key, first) >= 0); - } else { - r = hashmap_put(c->by_key, i->key, i); - if (r < 0) { - prioq_remove(c->by_expiry, i, &i->prioq_idx); - return r; - } - } - - return 0; -} - -static DnsCacheItem* dns_cache_get(DnsCache *c, DnsResourceRecord *rr) { - DnsCacheItem *i; - - assert(c); - assert(rr); - - LIST_FOREACH(by_key, i, hashmap_get(c->by_key, rr->key)) - if (i->rr && dns_resource_record_equal(i->rr, rr) > 0) - return i; - - return NULL; -} - -static void dns_cache_item_update_positive(DnsCache *c, DnsCacheItem *i, DnsResourceRecord *rr, usec_t timestamp) { - assert(c); - assert(i); - assert(rr); - - i->type = DNS_CACHE_POSITIVE; - - if (!i->by_key_prev) { - /* We are the first item in the list, we need to - * update the key used in the hashmap */ - - assert_se(hashmap_replace(c->by_key, rr->key, i) >= 0); - } - - dns_resource_record_ref(rr); - dns_resource_record_unref(i->rr); - i->rr = rr; - - dns_resource_key_unref(i->key); - i->key = dns_resource_key_ref(rr->key); - - i->until = timestamp + MIN(rr->ttl * USEC_PER_SEC, CACHE_TTL_MAX_USEC); - - prioq_reshuffle(c->by_expiry, i, &i->prioq_idx); -} - -static int dns_cache_put_positive( - DnsCache *c, - DnsResourceRecord *rr, - usec_t timestamp, - int owner_family, - const union in_addr_union *owner_address) { - - _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL; - DnsCacheItem *existing; - int r; - - assert(c); - assert(rr); - assert(owner_address); - - /* New TTL is 0? Delete the entry... */ - if (rr->ttl <= 0) { - dns_cache_remove(c, rr->key); - return 0; - } - - if (rr->key->class == DNS_CLASS_ANY) - return 0; - if (rr->key->type == DNS_TYPE_ANY) - return 0; - - /* Entry exists already? Update TTL and timestamp */ - existing = dns_cache_get(c, rr); - if (existing) { - dns_cache_item_update_positive(c, existing, rr, timestamp); - return 0; - } - - /* Otherwise, add the new RR */ - r = dns_cache_init(c); - if (r < 0) - return r; - - dns_cache_make_space(c, 1); - - i = new0(DnsCacheItem, 1); - if (!i) - return -ENOMEM; - - i->type = DNS_CACHE_POSITIVE; - i->key = dns_resource_key_ref(rr->key); - i->rr = dns_resource_record_ref(rr); - i->until = timestamp + MIN(i->rr->ttl * USEC_PER_SEC, CACHE_TTL_MAX_USEC); - i->prioq_idx = PRIOQ_IDX_NULL; - i->owner_family = owner_family; - i->owner_address = *owner_address; - - r = dns_cache_link_item(c, i); - if (r < 0) - return r; - - i = NULL; - return 0; -} - -static int dns_cache_put_negative( - DnsCache *c, - DnsResourceKey *key, - int rcode, - usec_t timestamp, - uint32_t soa_ttl, - int owner_family, - const union in_addr_union *owner_address) { - - _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL; - int r; - - assert(c); - assert(key); - assert(owner_address); - - dns_cache_remove(c, key); - - if (key->class == DNS_CLASS_ANY) - return 0; - if (key->type == DNS_TYPE_ANY) - return 0; - if (soa_ttl <= 0) - return 0; - - if (!IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN)) - return 0; - - r = dns_cache_init(c); - if (r < 0) - return r; - - dns_cache_make_space(c, 1); - - i = new0(DnsCacheItem, 1); - if (!i) - return -ENOMEM; - - i->type = rcode == DNS_RCODE_SUCCESS ? DNS_CACHE_NODATA : DNS_CACHE_NXDOMAIN; - i->key = dns_resource_key_ref(key); - i->until = timestamp + MIN(soa_ttl * USEC_PER_SEC, CACHE_TTL_MAX_USEC); - i->prioq_idx = PRIOQ_IDX_NULL; - i->owner_family = owner_family; - i->owner_address = *owner_address; - - r = dns_cache_link_item(c, i); - if (r < 0) - return r; - - i = NULL; - return 0; -} - -int dns_cache_put( - DnsCache *c, - DnsQuestion *q, - int rcode, - DnsAnswer *answer, - unsigned max_rrs, - usec_t timestamp, - int owner_family, - const union in_addr_union *owner_address) { - - unsigned i; - int r; - - assert(c); - assert(q); - - /* First, delete all matching old RRs, so that we only keep - * complete by_key in place. */ - for (i = 0; i < q->n_keys; i++) - dns_cache_remove(c, q->keys[i]); - - if (!answer) - return 0; - - for (i = 0; i < answer->n_rrs; i++) - dns_cache_remove(c, answer->rrs[i]->key); - - /* We only care for positive replies and NXDOMAINs, on all - * other replies we will simply flush the respective entries, - * and that's it */ - - if (!IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN)) - return 0; - - /* Make some space for our new entries */ - dns_cache_make_space(c, answer->n_rrs + q->n_keys); - - if (timestamp <= 0) - timestamp = now(CLOCK_BOOTTIME); - - /* Second, add in positive entries for all contained RRs */ - for (i = 0; i < MIN(max_rrs, answer->n_rrs); i++) { - r = dns_cache_put_positive(c, answer->rrs[i], timestamp, owner_family, owner_address); - if (r < 0) - goto fail; - } - - /* Third, add in negative entries for all keys with no RR */ - for (i = 0; i < q->n_keys; i++) { - DnsResourceRecord *soa = NULL; - - r = dns_answer_contains(answer, q->keys[i]); - if (r < 0) - goto fail; - if (r > 0) - continue; - - r = dns_answer_find_soa(answer, q->keys[i], &soa); - if (r < 0) - goto fail; - if (r == 0) - continue; - - r = dns_cache_put_negative(c, q->keys[i], rcode, timestamp, MIN(soa->soa.minimum, soa->ttl), owner_family, owner_address); - if (r < 0) - goto fail; - } - - return 0; - -fail: - /* Adding all RRs failed. Let's clean up what we already - * added, just in case */ - - for (i = 0; i < q->n_keys; i++) - dns_cache_remove(c, q->keys[i]); - for (i = 0; i < answer->n_rrs; i++) - dns_cache_remove(c, answer->rrs[i]->key); - - return r; -} - -int dns_cache_lookup(DnsCache *c, DnsQuestion *q, int *rcode, DnsAnswer **ret) { - _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; - unsigned i, n = 0; - int r; - bool nxdomain = false; - - assert(c); - assert(q); - assert(rcode); - assert(ret); - - if (q->n_keys <= 0) { - *ret = NULL; - *rcode = 0; - return 0; - } - - for (i = 0; i < q->n_keys; i++) { - DnsCacheItem *j; - - if (q->keys[i]->type == DNS_TYPE_ANY || - q->keys[i]->class == DNS_CLASS_ANY) { - /* If we have ANY lookups we simply refresh */ - *ret = NULL; - *rcode = 0; - return 0; - } - - j = hashmap_get(c->by_key, q->keys[i]); - if (!j) { - /* If one question cannot be answered we need to refresh */ - *ret = NULL; - *rcode = 0; - return 0; - } - - LIST_FOREACH(by_key, j, j) { - if (j->rr) - n++; - else if (j->type == DNS_CACHE_NXDOMAIN) - nxdomain = true; - } - } - - if (n <= 0) { - *ret = NULL; - *rcode = nxdomain ? DNS_RCODE_NXDOMAIN : DNS_RCODE_SUCCESS; - return 1; - } - - answer = dns_answer_new(n); - if (!answer) - return -ENOMEM; - - for (i = 0; i < q->n_keys; i++) { - DnsCacheItem *j; - - j = hashmap_get(c->by_key, q->keys[i]); - LIST_FOREACH(by_key, j, j) { - if (j->rr) { - r = dns_answer_add(answer, j->rr); - if (r < 0) - return r; - } - } - } - - *ret = answer; - *rcode = DNS_RCODE_SUCCESS; - answer = NULL; - - return n; -} - -int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address) { - DnsCacheItem *i, *first; - bool same_owner = true; - - assert(cache); - assert(rr); - - dns_cache_prune(cache); - - /* See if there's a cache entry for the same key. If there - * isn't there's no conflict */ - first = hashmap_get(cache->by_key, rr->key); - if (!first) - return 0; - - /* See if the RR key is owned by the same owner, if so, there - * isn't a conflict either */ - LIST_FOREACH(by_key, i, first) { - if (i->owner_family != owner_family || - !in_addr_equal(owner_family, &i->owner_address, owner_address)) { - same_owner = false; - break; - } - } - if (same_owner) - return 0; - - /* See if there's the exact same RR in the cache. If yes, then - * there's no conflict. */ - if (dns_cache_get(cache, rr)) - return 0; - - /* There's a conflict */ - return 1; -} diff --git a/src/resolve/resolved-dns-cache.h b/src/resolve/resolved-dns-cache.h deleted file mode 100644 index 8a9b3d459d..0000000000 --- a/src/resolve/resolved-dns-cache.h +++ /dev/null @@ -1,45 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - - -#include "hashmap.h" -#include "prioq.h" -#include "time-util.h" -#include "list.h" - -typedef struct DnsCache { - Hashmap *by_key; - Prioq *by_expiry; -} DnsCache; - -#include "resolved-dns-rr.h" -#include "resolved-dns-question.h" -#include "resolved-dns-answer.h" - -void dns_cache_flush(DnsCache *c); -void dns_cache_prune(DnsCache *c); - -int dns_cache_put(DnsCache *c, DnsQuestion *q, int rcode, DnsAnswer *answer, unsigned max_rrs, usec_t timestamp, int owner_family, const union in_addr_union *owner_address); -int dns_cache_lookup(DnsCache *c, DnsQuestion *q, int *rcode, DnsAnswer **answer); - -int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address); diff --git a/src/resolve/resolved-dns-domain.c b/src/resolve/resolved-dns-domain.c deleted file mode 100644 index e1eb3ddfe5..0000000000 --- a/src/resolve/resolved-dns-domain.c +++ /dev/null @@ -1,613 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. - ***/ - -#ifdef HAVE_LIBIDN -#include <idna.h> -#include <stringprep.h> -#endif - -#include "resolved-dns-domain.h" - -int dns_label_unescape(const char **name, char *dest, size_t sz) { - const char *n; - char *d; - int r = 0; - - assert(name); - assert(*name); - assert(dest); - - n = *name; - d = dest; - - for (;;) { - if (*n == '.') { - n++; - break; - } - - if (*n == 0) - break; - - if (sz <= 0) - return -ENOSPC; - - if (r >= DNS_LABEL_MAX) - return -EINVAL; - - if (*n == '\\') { - /* Escaped character */ - - n++; - - if (*n == 0) - /* Ending NUL */ - return -EINVAL; - - else if (*n == '\\' || *n == '.') { - /* Escaped backslash or dot */ - *(d++) = *(n++); - sz--; - r++; - - } else if (n[0] >= '0' && n[0] <= '9') { - unsigned k; - - /* Escaped literal ASCII character */ - - if (!(n[1] >= '0' && n[1] <= '9') || - !(n[2] >= '0' && n[2] <= '9')) - return -EINVAL; - - k = ((unsigned) (n[0] - '0') * 100) + - ((unsigned) (n[1] - '0') * 10) + - ((unsigned) (n[2] - '0')); - - /* Don't allow CC characters or anything that doesn't fit in 8bit */ - if (k < ' ' || k > 255 || k == 127) - return -EINVAL; - - *(d++) = (char) k; - sz--; - r++; - - n += 3; - } else - return -EINVAL; - - } else if ((uint8_t) *n >= (uint8_t) ' ' && *n != 127) { - - /* Normal character */ - *(d++) = *(n++); - sz--; - r++; - } else - return -EINVAL; - } - - /* Empty label that is not at the end? */ - if (r == 0 && *n) - return -EINVAL; - - if (sz >= 1) - *d = 0; - - *name = n; - return r; -} - -int dns_label_escape(const char *p, size_t l, char **ret) { - _cleanup_free_ char *s = NULL; - char *q; - int r; - - assert(p); - assert(ret); - - if (l > DNS_LABEL_MAX) - return -EINVAL; - - s = malloc(l * 4 + 1); - if (!s) - return -ENOMEM; - - q = s; - while (l > 0) { - - if (*p == '.' || *p == '\\') { - - /* Dot or backslash */ - *(q++) = '\\'; - *(q++) = *p; - - } else if (*p == '_' || - *p == '-' || - (*p >= '0' && *p <= '9') || - (*p >= 'a' && *p <= 'z') || - (*p >= 'A' && *p <= 'Z')) { - - /* Proper character */ - *(q++) = *p; - } else if ((uint8_t) *p >= (uint8_t) ' ' && *p != 127) { - - /* Everything else */ - *(q++) = '\\'; - *(q++) = '0' + (char) ((uint8_t) *p / 100); - *(q++) = '0' + (char) (((uint8_t) *p / 10) % 10); - *(q++) = '0' + (char) ((uint8_t) *p % 10); - - } else - return -EINVAL; - - p++; - l--; - } - - *q = 0; - *ret = s; - r = q - s; - s = NULL; - - return r; -} - -int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) { -#ifdef HAVE_LIBIDN - _cleanup_free_ uint32_t *input = NULL; - size_t input_size; - const char *p; - bool contains_8bit = false; - - assert(encoded); - assert(decoded); - assert(decoded_max >= DNS_LABEL_MAX); - - if (encoded_size <= 0) - return 0; - - for (p = encoded; p < encoded + encoded_size; p++) - if ((uint8_t) *p > 127) - contains_8bit = true; - - if (!contains_8bit) - return 0; - - input = stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size); - if (!input) - return -ENOMEM; - - if (idna_to_ascii_4i(input, input_size, decoded, 0) != 0) - return -EINVAL; - - return strlen(decoded); -#else - return 0; -#endif -} - -int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) { -#ifdef HAVE_LIBIDN - size_t input_size, output_size; - _cleanup_free_ uint32_t *input = NULL; - _cleanup_free_ char *result = NULL; - uint32_t *output = NULL; - size_t w; - - /* To be invoked after unescaping */ - - assert(encoded); - assert(decoded); - - if (encoded_size < sizeof(IDNA_ACE_PREFIX)-1) - return 0; - - if (memcmp(encoded, IDNA_ACE_PREFIX, sizeof(IDNA_ACE_PREFIX) -1) != 0) - return 0; - - input = stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size); - if (!input) - return -ENOMEM; - - output_size = input_size; - output = newa(uint32_t, output_size); - - idna_to_unicode_44i(input, input_size, output, &output_size, 0); - - result = stringprep_ucs4_to_utf8(output, output_size, NULL, &w); - if (!result) - return -ENOMEM; - if (w <= 0) - return 0; - if (w+1 > decoded_max) - return -EINVAL; - - memcpy(decoded, result, w+1); - return w; -#else - return 0; -#endif -} - -int dns_name_normalize(const char *s, char **_ret) { - _cleanup_free_ char *ret = NULL; - size_t n = 0, allocated = 0; - const char *p = s; - bool first = true; - int r; - - assert(s); - - for (;;) { - _cleanup_free_ char *t = NULL; - char label[DNS_LABEL_MAX]; - int k; - - r = dns_label_unescape(&p, label, sizeof(label)); - if (r < 0) - return r; - if (r == 0) { - if (*p != 0) - return -EINVAL; - break; - } - - k = dns_label_undo_idna(label, r, label, sizeof(label)); - if (k < 0) - return k; - if (k > 0) - r = k; - - r = dns_label_escape(label, r, &t); - if (r < 0) - return r; - - if (!GREEDY_REALLOC(ret, allocated, n + !first + strlen(t) + 1)) - return -ENOMEM; - - if (!first) - ret[n++] = '.'; - else - first = false; - - memcpy(ret + n, t, r); - n += r; - } - - if (n > DNS_NAME_MAX) - return -EINVAL; - - if (!GREEDY_REALLOC(ret, allocated, n + 1)) - return -ENOMEM; - - ret[n] = 0; - - if (_ret) { - *_ret = ret; - ret = NULL; - } - - return 0; -} - -unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_SIZE]) { - const char *p = s; - unsigned long ul = hash_key[0]; - int r; - - assert(p); - - while (*p) { - char label[DNS_LABEL_MAX+1]; - int k; - - r = dns_label_unescape(&p, label, sizeof(label)); - if (r < 0) - break; - - k = dns_label_undo_idna(label, r, label, sizeof(label)); - if (k < 0) - break; - if (k > 0) - r = k; - - label[r] = 0; - ascii_strlower(label); - - ul = ul * hash_key[1] + ul + string_hash_func(label, hash_key); - } - - return ul; -} - -int dns_name_compare_func(const void *a, const void *b) { - const char *x = a, *y = b; - int r, q, k, w; - - assert(a); - assert(b); - - for (;;) { - char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1]; - - if (*x == 0 && *y == 0) - return 0; - - r = dns_label_unescape(&x, la, sizeof(la)); - q = dns_label_unescape(&y, lb, sizeof(lb)); - if (r < 0 || q < 0) - return r - q; - - k = dns_label_undo_idna(la, r, la, sizeof(la)); - w = dns_label_undo_idna(lb, q, lb, sizeof(lb)); - if (k < 0 || w < 0) - return k - w; - if (k > 0) - r = k; - if (w > 0) - r = w; - - la[r] = lb[q] = 0; - r = strcasecmp(la, lb); - if (r != 0) - return r; - } -} - -const struct hash_ops dns_name_hash_ops = { - .hash = dns_name_hash_func, - .compare = dns_name_compare_func -}; - -int dns_name_equal(const char *x, const char *y) { - int r, q, k, w; - - assert(x); - assert(y); - - for (;;) { - char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1]; - - if (*x == 0 && *y == 0) - return true; - - r = dns_label_unescape(&x, la, sizeof(la)); - if (r < 0) - return r; - - k = dns_label_undo_idna(la, r, la, sizeof(la)); - if (k < 0) - return k; - if (k > 0) - r = k; - - q = dns_label_unescape(&y, lb, sizeof(lb)); - if (q < 0) - return q; - w = dns_label_undo_idna(lb, q, lb, sizeof(lb)); - if (w < 0) - return w; - if (w > 0) - q = w; - - la[r] = lb[q] = 0; - if (strcasecmp(la, lb)) - return false; - } -} - -int dns_name_endswith(const char *name, const char *suffix) { - const char *n, *s, *saved_n = NULL; - int r, q, k, w; - - assert(name); - assert(suffix); - - n = name; - s = suffix; - - for (;;) { - char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1]; - - r = dns_label_unescape(&n, ln, sizeof(ln)); - if (r < 0) - return r; - k = dns_label_undo_idna(ln, r, ln, sizeof(ln)); - if (k < 0) - return k; - if (k > 0) - r = k; - - if (!saved_n) - saved_n = n; - - q = dns_label_unescape(&s, ls, sizeof(ls)); - if (q < 0) - return q; - w = dns_label_undo_idna(ls, q, ls, sizeof(ls)); - if (w < 0) - return w; - if (w > 0) - q = w; - - if (r == 0 && q == 0) - return true; - if (r == 0 && saved_n == n) - return false; - - ln[r] = ls[q] = 0; - - if (r != q || strcasecmp(ln, ls)) { - - /* Not the same, let's jump back, and try with the next label again */ - s = suffix; - n = saved_n; - saved_n = NULL; - } - } -} - -int dns_name_reverse(int family, const union in_addr_union *a, char **ret) { - const uint8_t *p; - int r; - - assert(a); - assert(ret); - - p = (const uint8_t*) a; - - if (family == AF_INET) - r = asprintf(ret, "%u.%u.%u.%u.in-addr.arpa", p[3], p[2], p[1], p[0]); - else if (family == AF_INET6) - r = asprintf(ret, "%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.ip6.arpa", - hexchar(p[15] & 0xF), hexchar(p[15] >> 4), hexchar(p[14] & 0xF), hexchar(p[14] >> 4), - hexchar(p[13] & 0xF), hexchar(p[13] >> 4), hexchar(p[12] & 0xF), hexchar(p[12] >> 4), - hexchar(p[11] & 0xF), hexchar(p[11] >> 4), hexchar(p[10] & 0xF), hexchar(p[10] >> 4), - hexchar(p[ 9] & 0xF), hexchar(p[ 9] >> 4), hexchar(p[ 8] & 0xF), hexchar(p[ 8] >> 4), - hexchar(p[ 7] & 0xF), hexchar(p[ 7] >> 4), hexchar(p[ 6] & 0xF), hexchar(p[ 6] >> 4), - hexchar(p[ 5] & 0xF), hexchar(p[ 5] >> 4), hexchar(p[ 4] & 0xF), hexchar(p[ 4] >> 4), - hexchar(p[ 3] & 0xF), hexchar(p[ 3] >> 4), hexchar(p[ 2] & 0xF), hexchar(p[ 2] >> 4), - hexchar(p[ 1] & 0xF), hexchar(p[ 1] >> 4), hexchar(p[ 0] & 0xF), hexchar(p[ 0] >> 4)); - else - return -EAFNOSUPPORT; - if (r < 0) - return -ENOMEM; - - return 0; -} - -int dns_name_address(const char *p, int *family, union in_addr_union *address) { - int r; - - assert(p); - assert(family); - assert(address); - - r = dns_name_endswith(p, "in-addr.arpa"); - if (r < 0) - return r; - if (r > 0) { - uint8_t a[4]; - unsigned i; - - for (i = 0; i < ELEMENTSOF(a); i++) { - char label[DNS_LABEL_MAX+1]; - - r = dns_label_unescape(&p, label, sizeof(label)); - if (r < 0) - return r; - if (r == 0) - return -EINVAL; - if (r > 3) - return -EINVAL; - - r = safe_atou8(label, &a[i]); - if (r < 0) - return r; - } - - r = dns_name_equal(p, "in-addr.arpa"); - if (r <= 0) - return r; - - *family = AF_INET; - address->in.s_addr = htobe32(((uint32_t) a[3] << 24) | - ((uint32_t) a[2] << 16) | - ((uint32_t) a[1] << 8) | - (uint32_t) a[0]); - - return 1; - } - - r = dns_name_endswith(p, "ip6.arpa"); - if (r < 0) - return r; - if (r > 0) { - struct in6_addr a; - unsigned i; - - for (i = 0; i < ELEMENTSOF(a.s6_addr); i++) { - char label[DNS_LABEL_MAX+1]; - int x, y; - - r = dns_label_unescape(&p, label, sizeof(label)); - if (r <= 0) - return r; - if (r != 1) - return -EINVAL; - x = unhexchar(label[0]); - if (x < 0) - return -EINVAL; - - r = dns_label_unescape(&p, label, sizeof(label)); - if (r <= 0) - return r; - if (r != 1) - return -EINVAL; - y = unhexchar(label[0]); - if (y < 0) - return -EINVAL; - - a.s6_addr[ELEMENTSOF(a.s6_addr) - i - 1] = (uint8_t) y << 4 | (uint8_t) x; - } - - r = dns_name_equal(p, "ip6.arpa"); - if (r <= 0) - return r; - - *family = AF_INET6; - address->in6 = a; - return 1; - } - - return 0; -} - -int dns_name_root(const char *name) { - char label[DNS_LABEL_MAX+1]; - int r; - - assert(name); - - r = dns_label_unescape(&name, label, sizeof(label)); - if (r < 0) - return r; - - return r == 0 && *name == 0; -} - -int dns_name_single_label(const char *name) { - char label[DNS_LABEL_MAX+1]; - int r; - - assert(name); - - r = dns_label_unescape(&name, label, sizeof(label)); - if (r < 0) - return r; - if (r == 0) - return 0; - - r = dns_label_unescape(&name, label, sizeof(label)); - if (r < 0) - return r; - - return r == 0 && *name == 0; -} diff --git a/src/resolve/resolved-dns-domain.h b/src/resolve/resolved-dns-domain.h deleted file mode 100644 index 516d244f7a..0000000000 --- a/src/resolve/resolved-dns-domain.h +++ /dev/null @@ -1,50 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. - ***/ - -#pragma once - - -#include "hashmap.h" -#include "in-addr-util.h" - -#define DNS_LABEL_MAX 63 -#define DNS_NAME_MAX 255 - -int dns_label_unescape(const char **name, char *dest, size_t sz); -int dns_label_escape(const char *p, size_t l, char **ret); - -int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max); -int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max); - -int dns_name_normalize(const char *s, char **_ret); - -unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_SIZE]); -int dns_name_compare_func(const void *a, const void *b); -extern const struct hash_ops dns_name_hash_ops; - -int dns_name_equal(const char *x, const char *y); -int dns_name_endswith(const char *name, const char *suffix); - -int dns_name_reverse(int family, const union in_addr_union *a, char **ret); -int dns_name_address(const char *p, int *family, union in_addr_union *a); - -int dns_name_root(const char *name); -int dns_name_single_label(const char *name); diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c deleted file mode 100644 index 21756f566f..0000000000 --- a/src/resolve/resolved-dns-packet.c +++ /dev/null @@ -1,1478 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. - ***/ - -#include "utf8.h" -#include "util.h" -#include "strv.h" -#include "unaligned.h" -#include "resolved-dns-domain.h" -#include "resolved-dns-packet.h" - -int dns_packet_new(DnsPacket **ret, DnsProtocol protocol, size_t mtu) { - DnsPacket *p; - size_t a; - - assert(ret); - - if (mtu <= 0) - a = DNS_PACKET_SIZE_START; - else - a = mtu; - - if (a < DNS_PACKET_HEADER_SIZE) - a = DNS_PACKET_HEADER_SIZE; - - /* round up to next page size */ - a = PAGE_ALIGN(ALIGN(sizeof(DnsPacket)) + a) - ALIGN(sizeof(DnsPacket)); - - /* make sure we never allocate more than useful */ - if (a > DNS_PACKET_SIZE_MAX) - a = DNS_PACKET_SIZE_MAX; - - p = malloc0(ALIGN(sizeof(DnsPacket)) + a); - if (!p) - return -ENOMEM; - - p->size = p->rindex = DNS_PACKET_HEADER_SIZE; - p->allocated = a; - p->protocol = protocol; - p->n_ref = 1; - - *ret = p; - - return 0; -} - -int dns_packet_new_query(DnsPacket **ret, DnsProtocol protocol, size_t mtu) { - DnsPacket *p; - DnsPacketHeader *h; - int r; - - assert(ret); - - r = dns_packet_new(&p, protocol, mtu); - if (r < 0) - return r; - - h = DNS_PACKET_HEADER(p); - - if (protocol == DNS_PROTOCOL_LLMNR) - h->flags = htobe16(DNS_PACKET_MAKE_FLAGS(0 /* qr */, - 0 /* opcode */, - 0 /* c */, - 0 /* tc */, - 0 /* t */, - 0 /* ra */, - 0 /* ad */, - 0 /* cd */, - 0 /* rcode */)); - else - h->flags = htobe16(DNS_PACKET_MAKE_FLAGS(0 /* qr */, - 0 /* opcode */, - 0 /* aa */, - 0 /* tc */, - 1 /* rd (ask for recursion) */, - 0 /* ra */, - 0 /* ad */, - 0 /* cd */, - 0 /* rcode */)); - - *ret = p; - return 0; -} - -DnsPacket *dns_packet_ref(DnsPacket *p) { - - if (!p) - return NULL; - - assert(p->n_ref > 0); - p->n_ref++; - return p; -} - -static void dns_packet_free(DnsPacket *p) { - char *s; - - assert(p); - - dns_question_unref(p->question); - dns_answer_unref(p->answer); - - while ((s = hashmap_steal_first_key(p->names))) - free(s); - hashmap_free(p->names); - - free(p->_data); - free(p); -} - -DnsPacket *dns_packet_unref(DnsPacket *p) { - if (!p) - return NULL; - - assert(p->n_ref > 0); - - if (p->n_ref == 1) - dns_packet_free(p); - else - p->n_ref--; - - return NULL; -} - -int dns_packet_validate(DnsPacket *p) { - assert(p); - - if (p->size < DNS_PACKET_HEADER_SIZE) - return -EBADMSG; - - if (p->size > DNS_PACKET_SIZE_MAX) - return -EBADMSG; - - return 1; -} - -int dns_packet_validate_reply(DnsPacket *p) { - int r; - - assert(p); - - r = dns_packet_validate(p); - if (r < 0) - return r; - - if (DNS_PACKET_QR(p) != 1) - return 0; - - if (DNS_PACKET_OPCODE(p) != 0) - return -EBADMSG; - - /* RFC 4795, Section 2.1.1. says to discard all replies with QDCOUNT != 1 */ - if (p->protocol == DNS_PROTOCOL_LLMNR && - DNS_PACKET_QDCOUNT(p) != 1) - return -EBADMSG; - - return 1; -} - -int dns_packet_validate_query(DnsPacket *p) { - int r; - - assert(p); - - r = dns_packet_validate(p); - if (r < 0) - return r; - - if (DNS_PACKET_QR(p) != 0) - return 0; - - if (DNS_PACKET_OPCODE(p) != 0) - return -EBADMSG; - - if (DNS_PACKET_TC(p)) - return -EBADMSG; - - /* RFC 4795, Section 2.1.1. says to discard all queries with QDCOUNT != 1 */ - if (p->protocol == DNS_PROTOCOL_LLMNR && - DNS_PACKET_QDCOUNT(p) != 1) - return -EBADMSG; - - /* RFC 4795, Section 2.1.1. says to discard all queries with ANCOUNT != 0 */ - if (DNS_PACKET_ANCOUNT(p) > 0) - return -EBADMSG; - - /* RFC 4795, Section 2.1.1. says to discard all queries with NSCOUNT != 0 */ - if (DNS_PACKET_NSCOUNT(p) > 0) - return -EBADMSG; - - return 1; -} - -static int dns_packet_extend(DnsPacket *p, size_t add, void **ret, size_t *start) { - assert(p); - - if (p->size + add > p->allocated) { - size_t a; - - a = PAGE_ALIGN((p->size + add) * 2); - if (a > DNS_PACKET_SIZE_MAX) - a = DNS_PACKET_SIZE_MAX; - - if (p->size + add > a) - return -EMSGSIZE; - - if (p->_data) { - void *d; - - d = realloc(p->_data, a); - if (!d) - return -ENOMEM; - - p->_data = d; - } else { - p->_data = malloc(a); - if (!p->_data) - return -ENOMEM; - - memcpy(p->_data, (uint8_t*) p + ALIGN(sizeof(DnsPacket)), p->size); - memzero((uint8_t*) p->_data + p->size, a - p->size); - } - - p->allocated = a; - } - - if (start) - *start = p->size; - - if (ret) - *ret = (uint8_t*) DNS_PACKET_DATA(p) + p->size; - - p->size += add; - return 0; -} - -static void dns_packet_truncate(DnsPacket *p, size_t sz) { - Iterator i; - char *s; - void *n; - - assert(p); - - if (p->size <= sz) - return; - - HASHMAP_FOREACH_KEY(s, n, p->names, i) { - - if (PTR_TO_SIZE(n) < sz) - continue; - - hashmap_remove(p->names, s); - free(s); - } - - p->size = sz; -} - -int dns_packet_append_blob(DnsPacket *p, const void *d, size_t l, size_t *start) { - void *q; - int r; - - assert(p); - - r = dns_packet_extend(p, l, &q, start); - if (r < 0) - return r; - - memcpy(q, d, l); - return 0; -} - -int dns_packet_append_uint8(DnsPacket *p, uint8_t v, size_t *start) { - void *d; - int r; - - assert(p); - - r = dns_packet_extend(p, sizeof(uint8_t), &d, start); - if (r < 0) - return r; - - ((uint8_t*) d)[0] = v; - - return 0; -} - -int dns_packet_append_uint16(DnsPacket *p, uint16_t v, size_t *start) { - void *d; - int r; - - assert(p); - - r = dns_packet_extend(p, sizeof(uint16_t), &d, start); - if (r < 0) - return r; - - unaligned_write_be16(d, v); - - return 0; -} - -int dns_packet_append_uint32(DnsPacket *p, uint32_t v, size_t *start) { - void *d; - int r; - - assert(p); - - r = dns_packet_extend(p, sizeof(uint32_t), &d, start); - if (r < 0) - return r; - - unaligned_write_be32(d, v); - - return 0; -} - -int dns_packet_append_string(DnsPacket *p, const char *s, size_t *start) { - void *d; - size_t l; - int r; - - assert(p); - assert(s); - - l = strlen(s); - if (l > 255) - return -E2BIG; - - r = dns_packet_extend(p, 1 + l, &d, start); - if (r < 0) - return r; - - ((uint8_t*) d)[0] = (uint8_t) l; - memcpy(((uint8_t*) d) + 1, s, l); - - return 0; -} - -int dns_packet_append_label(DnsPacket *p, const char *d, size_t l, size_t *start) { - void *w; - int r; - - assert(p); - assert(d); - - if (l > DNS_LABEL_MAX) - return -E2BIG; - - r = dns_packet_extend(p, 1 + l, &w, start); - if (r < 0) - return r; - - ((uint8_t*) w)[0] = (uint8_t) l; - memcpy(((uint8_t*) w) + 1, d, l); - - return 0; -} - -int dns_packet_append_name(DnsPacket *p, const char *name, - bool allow_compression, size_t *start) { - size_t saved_size; - int r; - - assert(p); - assert(name); - - saved_size = p->size; - - while (*name) { - _cleanup_free_ char *s = NULL; - char label[DNS_LABEL_MAX]; - size_t n = 0; - int k; - - if (allow_compression) - n = PTR_TO_SIZE(hashmap_get(p->names, name)); - if (n > 0) { - assert(n < p->size); - - if (n < 0x4000) { - r = dns_packet_append_uint16(p, 0xC000 | n, NULL); - if (r < 0) - goto fail; - - goto done; - } - } - - s = strdup(name); - if (!s) { - r = -ENOMEM; - goto fail; - } - - r = dns_label_unescape(&name, label, sizeof(label)); - if (r < 0) - goto fail; - - if (p->protocol == DNS_PROTOCOL_DNS) - k = dns_label_apply_idna(label, r, label, sizeof(label)); - else - k = dns_label_undo_idna(label, r, label, sizeof(label)); - if (k < 0) { - r = k; - goto fail; - } - if (k > 0) - r = k; - - r = dns_packet_append_label(p, label, r, &n); - if (r < 0) - goto fail; - - if (allow_compression) { - r = hashmap_ensure_allocated(&p->names, &dns_name_hash_ops); - if (r < 0) - goto fail; - - r = hashmap_put(p->names, s, SIZE_TO_PTR(n)); - if (r < 0) - goto fail; - - s = NULL; - } - } - - r = dns_packet_append_uint8(p, 0, NULL); - if (r < 0) - return r; - -done: - if (start) - *start = saved_size; - - return 0; - -fail: - dns_packet_truncate(p, saved_size); - return r; -} - -int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *k, size_t *start) { - size_t saved_size; - int r; - - assert(p); - assert(k); - - saved_size = p->size; - - r = dns_packet_append_name(p, DNS_RESOURCE_KEY_NAME(k), true, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint16(p, k->type, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint16(p, k->class, NULL); - if (r < 0) - goto fail; - - if (start) - *start = saved_size; - - return 0; - -fail: - dns_packet_truncate(p, saved_size); - return r; -} - -int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *start) { - size_t saved_size, rdlength_offset, end, rdlength; - int r; - - assert(p); - assert(rr); - - saved_size = p->size; - - r = dns_packet_append_key(p, rr->key, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint32(p, rr->ttl, NULL); - if (r < 0) - goto fail; - - /* Initially we write 0 here */ - r = dns_packet_append_uint16(p, 0, &rdlength_offset); - if (r < 0) - goto fail; - - switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) { - - case DNS_TYPE_SRV: - r = dns_packet_append_uint16(p, rr->srv.priority, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint16(p, rr->srv.weight, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint16(p, rr->srv.port, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_name(p, rr->srv.name, true, NULL); - break; - - case DNS_TYPE_PTR: - case DNS_TYPE_NS: - case DNS_TYPE_CNAME: - case DNS_TYPE_DNAME: - r = dns_packet_append_name(p, rr->ptr.name, true, NULL); - break; - - case DNS_TYPE_HINFO: - r = dns_packet_append_string(p, rr->hinfo.cpu, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_string(p, rr->hinfo.os, NULL); - break; - - case DNS_TYPE_SPF: /* exactly the same as TXT */ - case DNS_TYPE_TXT: { - char **s; - - if (strv_isempty(rr->txt.strings)) { - /* RFC 6763, section 6.1 suggests to generate - * single empty string for an empty array. */ - - r = dns_packet_append_string(p, "", NULL); - if (r < 0) - goto fail; - } else { - STRV_FOREACH(s, rr->txt.strings) { - r = dns_packet_append_string(p, *s, NULL); - if (r < 0) - goto fail; - } - } - - r = 0; - break; - } - - case DNS_TYPE_A: - r = dns_packet_append_blob(p, &rr->a.in_addr, sizeof(struct in_addr), NULL); - break; - - case DNS_TYPE_AAAA: - r = dns_packet_append_blob(p, &rr->aaaa.in6_addr, sizeof(struct in6_addr), NULL); - break; - - case DNS_TYPE_SOA: - r = dns_packet_append_name(p, rr->soa.mname, true, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_name(p, rr->soa.rname, true, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint32(p, rr->soa.serial, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint32(p, rr->soa.refresh, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint32(p, rr->soa.retry, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint32(p, rr->soa.expire, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint32(p, rr->soa.minimum, NULL); - break; - - case DNS_TYPE_MX: - r = dns_packet_append_uint16(p, rr->mx.priority, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_name(p, rr->mx.exchange, true, NULL); - break; - - case DNS_TYPE_LOC: - r = dns_packet_append_uint8(p, rr->loc.version, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint8(p, rr->loc.size, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint8(p, rr->loc.horiz_pre, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint8(p, rr->loc.vert_pre, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint32(p, rr->loc.latitude, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint32(p, rr->loc.longitude, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint32(p, rr->loc.altitude, NULL); - break; - - case DNS_TYPE_SSHFP: - r = dns_packet_append_uint8(p, rr->sshfp.algorithm, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint8(p, rr->sshfp.fptype, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_blob(p, rr->sshfp.key, rr->sshfp.key_size, NULL); - break; - - case DNS_TYPE_DNSKEY: - r = dns_packet_append_uint16(p, dnskey_to_flags(rr), NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint8(p, 3u, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint8(p, rr->dnskey.algorithm, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_blob(p, rr->dnskey.key, rr->dnskey.key_size, NULL); - break; - - case DNS_TYPE_RRSIG: - r = dns_packet_append_uint16(p, rr->rrsig.type_covered, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint8(p, rr->rrsig.algorithm, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint8(p, rr->rrsig.labels, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint32(p, rr->rrsig.original_ttl, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint32(p, rr->rrsig.expiration, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint32(p, rr->rrsig.inception, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint8(p, rr->rrsig.key_tag, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_name(p, rr->rrsig.signer, false, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_blob(p, rr->rrsig.signature, rr->rrsig.signature_size, NULL); - break; - - case _DNS_TYPE_INVALID: /* unparseable */ - default: - - r = dns_packet_append_blob(p, rr->generic.data, rr->generic.size, NULL); - break; - } - if (r < 0) - goto fail; - - /* Let's calculate the actual data size and update the field */ - rdlength = p->size - rdlength_offset - sizeof(uint16_t); - if (rdlength > 0xFFFF) { - r = ENOSPC; - goto fail; - } - - end = p->size; - p->size = rdlength_offset; - r = dns_packet_append_uint16(p, rdlength, NULL); - if (r < 0) - goto fail; - p->size = end; - - if (start) - *start = saved_size; - - return 0; - -fail: - dns_packet_truncate(p, saved_size); - return r; -} - - -int dns_packet_read(DnsPacket *p, size_t sz, const void **ret, size_t *start) { - assert(p); - - if (p->rindex + sz > p->size) - return -EMSGSIZE; - - if (ret) - *ret = (uint8_t*) DNS_PACKET_DATA(p) + p->rindex; - - if (start) - *start = p->rindex; - - p->rindex += sz; - return 0; -} - -void dns_packet_rewind(DnsPacket *p, size_t idx) { - assert(p); - assert(idx <= p->size); - assert(idx >= DNS_PACKET_HEADER_SIZE); - - p->rindex = idx; -} - -int dns_packet_read_blob(DnsPacket *p, void *d, size_t sz, size_t *start) { - const void *q; - int r; - - assert(p); - assert(d); - - r = dns_packet_read(p, sz, &q, start); - if (r < 0) - return r; - - memcpy(d, q, sz); - return 0; -} - -int dns_packet_read_uint8(DnsPacket *p, uint8_t *ret, size_t *start) { - const void *d; - int r; - - assert(p); - - r = dns_packet_read(p, sizeof(uint8_t), &d, start); - if (r < 0) - return r; - - *ret = ((uint8_t*) d)[0]; - return 0; -} - -int dns_packet_read_uint16(DnsPacket *p, uint16_t *ret, size_t *start) { - const void *d; - int r; - - assert(p); - - r = dns_packet_read(p, sizeof(uint16_t), &d, start); - if (r < 0) - return r; - - *ret = unaligned_read_be16(d); - - return 0; -} - -int dns_packet_read_uint32(DnsPacket *p, uint32_t *ret, size_t *start) { - const void *d; - int r; - - assert(p); - - r = dns_packet_read(p, sizeof(uint32_t), &d, start); - if (r < 0) - return r; - - *ret = unaligned_read_be32(d); - - return 0; -} - -int dns_packet_read_string(DnsPacket *p, char **ret, size_t *start) { - size_t saved_rindex; - const void *d; - char *t; - uint8_t c; - int r; - - assert(p); - - saved_rindex = p->rindex; - - r = dns_packet_read_uint8(p, &c, NULL); - if (r < 0) - goto fail; - - r = dns_packet_read(p, c, &d, NULL); - if (r < 0) - goto fail; - - if (memchr(d, 0, c)) { - r = -EBADMSG; - goto fail; - } - - t = strndup(d, c); - if (!t) { - r = -ENOMEM; - goto fail; - } - - if (!utf8_is_valid(t)) { - free(t); - r = -EBADMSG; - goto fail; - } - - *ret = t; - - if (start) - *start = saved_rindex; - - return 0; - -fail: - dns_packet_rewind(p, saved_rindex); - return r; -} - -int dns_packet_read_name(DnsPacket *p, char **_ret, - bool allow_compression, size_t *start) { - size_t saved_rindex, after_rindex = 0, jump_barrier; - _cleanup_free_ char *ret = NULL; - size_t n = 0, allocated = 0; - bool first = true; - int r; - - assert(p); - assert(_ret); - - saved_rindex = p->rindex; - jump_barrier = p->rindex; - - for (;;) { - uint8_t c, d; - - r = dns_packet_read_uint8(p, &c, NULL); - if (r < 0) - goto fail; - - if (c == 0) - /* End of name */ - break; - else if (c <= 63) { - _cleanup_free_ char *t = NULL; - const char *label; - - /* Literal label */ - r = dns_packet_read(p, c, (const void**) &label, NULL); - if (r < 0) - goto fail; - - r = dns_label_escape(label, c, &t); - if (r < 0) - goto fail; - - if (!GREEDY_REALLOC(ret, allocated, n + !first + strlen(t) + 1)) { - r = -ENOMEM; - goto fail; - } - - if (!first) - ret[n++] = '.'; - else - first = false; - - memcpy(ret + n, t, r); - n += r; - continue; - } else if (allow_compression && (c & 0xc0) == 0xc0) { - uint16_t ptr; - - /* Pointer */ - r = dns_packet_read_uint8(p, &d, NULL); - if (r < 0) - goto fail; - - ptr = (uint16_t) (c & ~0xc0) << 8 | (uint16_t) d; - if (ptr < DNS_PACKET_HEADER_SIZE || ptr >= jump_barrier) { - r = -EBADMSG; - goto fail; - } - - if (after_rindex == 0) - after_rindex = p->rindex; - - /* Jumps are limited to a "prior occurrence" (RFC-1035 4.1.4) */ - jump_barrier = ptr; - p->rindex = ptr; - } else { - r = -EBADMSG; - goto fail; - } - } - - if (!GREEDY_REALLOC(ret, allocated, n + 1)) { - r = -ENOMEM; - goto fail; - } - - ret[n] = 0; - - if (after_rindex != 0) - p->rindex= after_rindex; - - *_ret = ret; - ret = NULL; - - if (start) - *start = saved_rindex; - - return 0; - -fail: - dns_packet_rewind(p, saved_rindex); - return r; -} - -int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, size_t *start) { - _cleanup_free_ char *name = NULL; - uint16_t class, type; - DnsResourceKey *key; - size_t saved_rindex; - int r; - - assert(p); - assert(ret); - - saved_rindex = p->rindex; - - r = dns_packet_read_name(p, &name, true, NULL); - if (r < 0) - goto fail; - - r = dns_packet_read_uint16(p, &type, NULL); - if (r < 0) - goto fail; - - r = dns_packet_read_uint16(p, &class, NULL); - if (r < 0) - goto fail; - - key = dns_resource_key_new_consume(class, type, name); - if (!key) { - r = -ENOMEM; - goto fail; - } - - name = NULL; - *ret = key; - - if (start) - *start = saved_rindex; - - return 0; -fail: - dns_packet_rewind(p, saved_rindex); - return r; -} - -static int dns_packet_read_public_key(DnsPacket *p, size_t length, - void **dp, size_t *lengthp, - size_t *start) { - int r; - const void *d; - void *d2; - - r = dns_packet_read(p, length, &d, NULL); - if (r < 0) - return r; - - d2 = memdup(d, length); - if (!d2) - return -ENOMEM; - - *dp = d2; - *lengthp = length; - return 0; -} - -static bool loc_size_ok(uint8_t size) { - uint8_t m = size >> 4, e = size & 0xF; - - return m <= 9 && e <= 9 && (m > 0 || e == 0); -} - -static int dnskey_parse_flags(DnsResourceRecord *rr, uint16_t flags) { - assert(rr); - - if (flags & ~(DNSKEY_FLAG_SEP | DNSKEY_FLAG_ZONE_KEY)) - return -EBADMSG; - - rr->dnskey.zone_key_flag = flags & DNSKEY_FLAG_ZONE_KEY; - rr->dnskey.sep_flag = flags & DNSKEY_FLAG_SEP; - return 0; -} - -int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) { - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; - _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; - size_t saved_rindex, offset; - uint16_t rdlength; - const void *d; - int r; - - assert(p); - assert(ret); - - saved_rindex = p->rindex; - - r = dns_packet_read_key(p, &key, NULL); - if (r < 0) - goto fail; - - if (key->class == DNS_CLASS_ANY || - key->type == DNS_TYPE_ANY) { - r = -EBADMSG; - goto fail; - } - - rr = dns_resource_record_new(key); - if (!rr) { - r = -ENOMEM; - goto fail; - } - - r = dns_packet_read_uint32(p, &rr->ttl, NULL); - if (r < 0) - goto fail; - - r = dns_packet_read_uint16(p, &rdlength, NULL); - if (r < 0) - goto fail; - - if (p->rindex + rdlength > p->size) { - r = -EBADMSG; - goto fail; - } - - offset = p->rindex; - - switch (rr->key->type) { - - case DNS_TYPE_SRV: - r = dns_packet_read_uint16(p, &rr->srv.priority, NULL); - if (r < 0) - goto fail; - r = dns_packet_read_uint16(p, &rr->srv.weight, NULL); - if (r < 0) - goto fail; - r = dns_packet_read_uint16(p, &rr->srv.port, NULL); - if (r < 0) - goto fail; - r = dns_packet_read_name(p, &rr->srv.name, true, NULL); - break; - - case DNS_TYPE_PTR: - case DNS_TYPE_NS: - case DNS_TYPE_CNAME: - case DNS_TYPE_DNAME: - r = dns_packet_read_name(p, &rr->ptr.name, true, NULL); - break; - - case DNS_TYPE_HINFO: - r = dns_packet_read_string(p, &rr->hinfo.cpu, NULL); - if (r < 0) - goto fail; - - r = dns_packet_read_string(p, &rr->hinfo.os, NULL); - break; - - case DNS_TYPE_SPF: /* exactly the same as TXT */ - case DNS_TYPE_TXT: - if (rdlength <= 0) { - /* RFC 6763, section 6.1 suggests to treat - * empty TXT RRs as equivalent to a TXT record - * with a single empty string. */ - - r = strv_extend(&rr->txt.strings, ""); - if (r < 0) - goto fail; - } else { - while (p->rindex < offset + rdlength) { - char *s; - - r = dns_packet_read_string(p, &s, NULL); - if (r < 0) - goto fail; - - r = strv_consume(&rr->txt.strings, s); - if (r < 0) - goto fail; - } - } - - r = 0; - break; - - case DNS_TYPE_A: - r = dns_packet_read_blob(p, &rr->a.in_addr, sizeof(struct in_addr), NULL); - break; - - case DNS_TYPE_AAAA: - r = dns_packet_read_blob(p, &rr->aaaa.in6_addr, sizeof(struct in6_addr), NULL); - break; - - case DNS_TYPE_SOA: - r = dns_packet_read_name(p, &rr->soa.mname, true, NULL); - if (r < 0) - goto fail; - - r = dns_packet_read_name(p, &rr->soa.rname, true, NULL); - if (r < 0) - goto fail; - - r = dns_packet_read_uint32(p, &rr->soa.serial, NULL); - if (r < 0) - goto fail; - - r = dns_packet_read_uint32(p, &rr->soa.refresh, NULL); - if (r < 0) - goto fail; - - r = dns_packet_read_uint32(p, &rr->soa.retry, NULL); - if (r < 0) - goto fail; - - r = dns_packet_read_uint32(p, &rr->soa.expire, NULL); - if (r < 0) - goto fail; - - r = dns_packet_read_uint32(p, &rr->soa.minimum, NULL); - break; - - case DNS_TYPE_MX: - r = dns_packet_read_uint16(p, &rr->mx.priority, NULL); - if (r < 0) - goto fail; - - r = dns_packet_read_name(p, &rr->mx.exchange, true, NULL); - break; - - case DNS_TYPE_LOC: { - uint8_t t; - size_t pos; - - r = dns_packet_read_uint8(p, &t, &pos); - if (r < 0) - goto fail; - - if (t == 0) { - rr->loc.version = t; - - r = dns_packet_read_uint8(p, &rr->loc.size, NULL); - if (r < 0) - goto fail; - - if (!loc_size_ok(rr->loc.size)) { - r = -EBADMSG; - goto fail; - } - - r = dns_packet_read_uint8(p, &rr->loc.horiz_pre, NULL); - if (r < 0) - goto fail; - - if (!loc_size_ok(rr->loc.horiz_pre)) { - r = -EBADMSG; - goto fail; - } - - r = dns_packet_read_uint8(p, &rr->loc.vert_pre, NULL); - if (r < 0) - goto fail; - - if (!loc_size_ok(rr->loc.vert_pre)) { - r = -EBADMSG; - goto fail; - } - - r = dns_packet_read_uint32(p, &rr->loc.latitude, NULL); - if (r < 0) - goto fail; - - r = dns_packet_read_uint32(p, &rr->loc.longitude, NULL); - if (r < 0) - goto fail; - - r = dns_packet_read_uint32(p, &rr->loc.altitude, NULL); - if (r < 0) - goto fail; - - break; - } else { - dns_packet_rewind(p, pos); - rr->unparseable = true; - goto unparseable; - } - } - - case DNS_TYPE_SSHFP: - r = dns_packet_read_uint8(p, &rr->sshfp.algorithm, NULL); - if (r < 0) - goto fail; - - r = dns_packet_read_uint8(p, &rr->sshfp.fptype, NULL); - if (r < 0) - goto fail; - - r = dns_packet_read_public_key(p, rdlength - 2, - &rr->sshfp.key, &rr->sshfp.key_size, - NULL); - break; - - case DNS_TYPE_DNSKEY: { - uint16_t flags; - uint8_t proto; - - r = dns_packet_read_uint16(p, &flags, NULL); - if (r < 0) - goto fail; - - r = dnskey_parse_flags(rr, flags); - if (r < 0) - goto fail; - - r = dns_packet_read_uint8(p, &proto, NULL); - if (r < 0) - goto fail; - - /* protocol is required to be always 3 */ - if (proto != 3) { - r = -EBADMSG; - goto fail; - } - - r = dns_packet_read_uint8(p, &rr->dnskey.algorithm, NULL); - if (r < 0) - goto fail; - - r = dns_packet_read_public_key(p, rdlength - 4, - &rr->dnskey.key, &rr->dnskey.key_size, - NULL); - break; - } - - case DNS_TYPE_RRSIG: - r = dns_packet_read_uint16(p, &rr->rrsig.type_covered, NULL); - if (r < 0) - goto fail; - - r = dns_packet_read_uint8(p, &rr->rrsig.algorithm, NULL); - if (r < 0) - goto fail; - - r = dns_packet_read_uint8(p, &rr->rrsig.labels, NULL); - if (r < 0) - goto fail; - - r = dns_packet_read_uint32(p, &rr->rrsig.original_ttl, NULL); - if (r < 0) - goto fail; - - r = dns_packet_read_uint32(p, &rr->rrsig.expiration, NULL); - if (r < 0) - goto fail; - - r = dns_packet_read_uint32(p, &rr->rrsig.inception, NULL); - if (r < 0) - goto fail; - - r = dns_packet_read_uint16(p, &rr->rrsig.key_tag, NULL); - if (r < 0) - goto fail; - - r = dns_packet_read_name(p, &rr->rrsig.signer, false, NULL); - if (r < 0) - goto fail; - - r = dns_packet_read_public_key(p, offset + rdlength - p->rindex, - &rr->rrsig.signature, &rr->rrsig.signature_size, - NULL); - break; - - default: - unparseable: - r = dns_packet_read(p, rdlength, &d, NULL); - if (r < 0) - goto fail; - - rr->generic.data = memdup(d, rdlength); - if (!rr->generic.data) { - r = -ENOMEM; - goto fail; - } - - rr->generic.size = rdlength; - break; - } - if (r < 0) - goto fail; - if (p->rindex != offset + rdlength) { - r = -EBADMSG; - goto fail; - } - - *ret = rr; - rr = NULL; - - if (start) - *start = saved_rindex; - - return 0; -fail: - dns_packet_rewind(p, saved_rindex); - return r; -} - -int dns_packet_extract(DnsPacket *p) { - _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; - _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; - size_t saved_rindex; - unsigned n, i; - int r; - - if (p->extracted) - return 0; - - saved_rindex = p->rindex; - dns_packet_rewind(p, DNS_PACKET_HEADER_SIZE); - - n = DNS_PACKET_QDCOUNT(p); - if (n > 0) { - question = dns_question_new(n); - if (!question) { - r = -ENOMEM; - goto finish; - } - - for (i = 0; i < n; i++) { - _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; - - r = dns_packet_read_key(p, &key, NULL); - if (r < 0) - goto finish; - - r = dns_question_add(question, key); - if (r < 0) - goto finish; - } - } - - n = DNS_PACKET_RRCOUNT(p); - if (n > 0) { - answer = dns_answer_new(n); - if (!answer) { - r = -ENOMEM; - goto finish; - } - - for (i = 0; i < n; i++) { - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; - - r = dns_packet_read_rr(p, &rr, NULL); - if (r < 0) - goto finish; - - r = dns_answer_add(answer, rr); - if (r < 0) - goto finish; - } - } - - p->question = question; - question = NULL; - - p->answer = answer; - answer = NULL; - - p->extracted = true; - - r = 0; - -finish: - p->rindex = saved_rindex; - return r; -} - -static const char* const dns_rcode_table[_DNS_RCODE_MAX_DEFINED] = { - [DNS_RCODE_SUCCESS] = "SUCCESS", - [DNS_RCODE_FORMERR] = "FORMERR", - [DNS_RCODE_SERVFAIL] = "SERVFAIL", - [DNS_RCODE_NXDOMAIN] = "NXDOMAIN", - [DNS_RCODE_NOTIMP] = "NOTIMP", - [DNS_RCODE_REFUSED] = "REFUSED", - [DNS_RCODE_YXDOMAIN] = "YXDOMAIN", - [DNS_RCODE_YXRRSET] = "YRRSET", - [DNS_RCODE_NXRRSET] = "NXRRSET", - [DNS_RCODE_NOTAUTH] = "NOTAUTH", - [DNS_RCODE_NOTZONE] = "NOTZONE", - [DNS_RCODE_BADVERS] = "BADVERS", - [DNS_RCODE_BADKEY] = "BADKEY", - [DNS_RCODE_BADTIME] = "BADTIME", - [DNS_RCODE_BADMODE] = "BADMODE", - [DNS_RCODE_BADNAME] = "BADNAME", - [DNS_RCODE_BADALG] = "BADALG", - [DNS_RCODE_BADTRUNC] = "BADTRUNC", -}; -DEFINE_STRING_TABLE_LOOKUP(dns_rcode, int); - -static const char* const dns_protocol_table[_DNS_PROTOCOL_MAX] = { - [DNS_PROTOCOL_DNS] = "dns", - [DNS_PROTOCOL_MDNS] = "mdns", - [DNS_PROTOCOL_LLMNR] = "llmnr", -}; -DEFINE_STRING_TABLE_LOOKUP(dns_protocol, DnsProtocol); - -static const char* const dnssec_algorithm_table[_DNSSEC_ALGORITHM_MAX_DEFINED] = { - [DNSSEC_ALGORITHM_RSAMD5] = "RSAMD5", - [DNSSEC_ALGORITHM_DH] = "DH", - [DNSSEC_ALGORITHM_DSA] = "DSA", - [DNSSEC_ALGORITHM_ECC] = "ECC", - [DNSSEC_ALGORITHM_RSASHA1] = "RSASHA1", - [DNSSEC_ALGORITHM_INDIRECT] = "INDIRECT", - [DNSSEC_ALGORITHM_PRIVATEDNS] = "PRIVATEDNS", - [DNSSEC_ALGORITHM_PRIVATEOID] = "PRIVATEOID", -}; -DEFINE_STRING_TABLE_LOOKUP(dnssec_algorithm, int); diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h deleted file mode 100644 index c5867386c6..0000000000 --- a/src/resolve/resolved-dns-packet.h +++ /dev/null @@ -1,235 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. - ***/ - - -#include "macro.h" -#include "sparse-endian.h" -#include "hashmap.h" -#include "in-addr-util.h" - -typedef struct DnsPacketHeader DnsPacketHeader; -typedef struct DnsPacket DnsPacket; - -#include "resolved-dns-rr.h" -#include "resolved-dns-question.h" -#include "resolved-dns-answer.h" -#include "resolved-def.h" - -typedef enum DnsProtocol { - DNS_PROTOCOL_DNS, - DNS_PROTOCOL_MDNS, - DNS_PROTOCOL_LLMNR, - _DNS_PROTOCOL_MAX, - _DNS_PROTOCOL_INVALID = -1 -} DnsProtocol; - -struct DnsPacketHeader { - uint16_t id; - be16_t flags; - be16_t qdcount; - be16_t ancount; - be16_t nscount; - be16_t arcount; -}; - -#define DNS_PACKET_HEADER_SIZE sizeof(DnsPacketHeader) - -/* The various DNS protocols deviate in how large a packet can grow, - but the TCP transport has a 16bit size field, hence that appears to - be the absolute maximum. */ -#define DNS_PACKET_SIZE_MAX 0xFFFF - -/* RFC 1035 say 512 is the maximum, for classic unicast DNS */ -#define DNS_PACKET_UNICAST_SIZE_MAX 512 - -#define DNS_PACKET_SIZE_START 512 - -struct DnsPacket { - int n_ref; - DnsProtocol protocol; - size_t size, allocated, rindex; - void *_data; /* don't access directly, use DNS_PACKET_DATA()! */ - Hashmap *names; /* For name compression */ - - /* Parsed data */ - DnsQuestion *question; - DnsAnswer *answer; - - /* Packet reception meta data */ - int ifindex; - int family, ipproto; - union in_addr_union sender, destination; - uint16_t sender_port, destination_port; - uint32_t ttl; - - bool extracted; -}; - -static inline uint8_t* DNS_PACKET_DATA(DnsPacket *p) { - if (_unlikely_(!p)) - return NULL; - - if (p->_data) - return p->_data; - - return ((uint8_t*) p) + ALIGN(sizeof(DnsPacket)); -} - -#define DNS_PACKET_HEADER(p) ((DnsPacketHeader*) DNS_PACKET_DATA(p)) -#define DNS_PACKET_ID(p) DNS_PACKET_HEADER(p)->id -#define DNS_PACKET_QR(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 15) & 1) -#define DNS_PACKET_OPCODE(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 11) & 15) -#define DNS_PACKET_RCODE(p) (be16toh(DNS_PACKET_HEADER(p)->flags) & 15) -#define DNS_PACKET_TC(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 9) & 1) -#define DNS_PACKET_C(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 10) & 1) -#define DNS_PACKET_T(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 8) & 1) -#define DNS_PACKET_QDCOUNT(p) be16toh(DNS_PACKET_HEADER(p)->qdcount) -#define DNS_PACKET_ANCOUNT(p) be16toh(DNS_PACKET_HEADER(p)->ancount) -#define DNS_PACKET_NSCOUNT(p) be16toh(DNS_PACKET_HEADER(p)->nscount) -#define DNS_PACKET_ARCOUNT(p) be16toh(DNS_PACKET_HEADER(p)->arcount) - -#define DNS_PACKET_MAKE_FLAGS(qr, opcode, aa, tc, rd, ra, ad, cd, rcode) \ - (((uint16_t) !!qr << 15) | \ - ((uint16_t) (opcode & 15) << 11) | \ - ((uint16_t) !!aa << 10) | \ - ((uint16_t) !!tc << 9) | \ - ((uint16_t) !!rd << 8) | \ - ((uint16_t) !!ra << 7) | \ - ((uint16_t) !!ad << 5) | \ - ((uint16_t) !!cd << 4) | \ - ((uint16_t) (rcode & 15))) - -static inline unsigned DNS_PACKET_RRCOUNT(DnsPacket *p) { - return - (unsigned) DNS_PACKET_ANCOUNT(p) + - (unsigned) DNS_PACKET_NSCOUNT(p) + - (unsigned) DNS_PACKET_ARCOUNT(p); -} - -int dns_packet_new(DnsPacket **p, DnsProtocol protocol, size_t mtu); -int dns_packet_new_query(DnsPacket **p, DnsProtocol protocol, size_t mtu); - -DnsPacket *dns_packet_ref(DnsPacket *p); -DnsPacket *dns_packet_unref(DnsPacket *p); - -DEFINE_TRIVIAL_CLEANUP_FUNC(DnsPacket*, dns_packet_unref); - -int dns_packet_validate(DnsPacket *p); -int dns_packet_validate_reply(DnsPacket *p); -int dns_packet_validate_query(DnsPacket *p); - -int dns_packet_append_blob(DnsPacket *p, const void *d, size_t sz, size_t *start); -int dns_packet_append_uint8(DnsPacket *p, uint8_t v, size_t *start); -int dns_packet_append_uint16(DnsPacket *p, uint16_t v, size_t *start); -int dns_packet_append_uint32(DnsPacket *p, uint32_t v, size_t *start); -int dns_packet_append_string(DnsPacket *p, const char *s, size_t *start); -int dns_packet_append_label(DnsPacket *p, const char *s, size_t l, size_t *start); -int dns_packet_append_name(DnsPacket *p, const char *name, - bool allow_compression, size_t *start); -int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *key, size_t *start); -int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *start); - -int dns_packet_read(DnsPacket *p, size_t sz, const void **ret, size_t *start); -int dns_packet_read_blob(DnsPacket *p, void *d, size_t sz, size_t *start); -int dns_packet_read_uint8(DnsPacket *p, uint8_t *ret, size_t *start); -int dns_packet_read_uint16(DnsPacket *p, uint16_t *ret, size_t *start); -int dns_packet_read_uint32(DnsPacket *p, uint32_t *ret, size_t *start); -int dns_packet_read_string(DnsPacket *p, char **ret, size_t *start); -int dns_packet_read_name(DnsPacket *p, char **ret, - bool allow_compression, size_t *start); -int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, size_t *start); -int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start); - -void dns_packet_rewind(DnsPacket *p, size_t idx); - -int dns_packet_skip_question(DnsPacket *p); -int dns_packet_extract(DnsPacket *p); - -enum { - DNS_RCODE_SUCCESS = 0, - DNS_RCODE_FORMERR = 1, - DNS_RCODE_SERVFAIL = 2, - DNS_RCODE_NXDOMAIN = 3, - DNS_RCODE_NOTIMP = 4, - DNS_RCODE_REFUSED = 5, - DNS_RCODE_YXDOMAIN = 6, - DNS_RCODE_YXRRSET = 7, - DNS_RCODE_NXRRSET = 8, - DNS_RCODE_NOTAUTH = 9, - DNS_RCODE_NOTZONE = 10, - DNS_RCODE_BADVERS = 16, - DNS_RCODE_BADSIG = 16, /* duplicate value! */ - DNS_RCODE_BADKEY = 17, - DNS_RCODE_BADTIME = 18, - DNS_RCODE_BADMODE = 19, - DNS_RCODE_BADNAME = 20, - DNS_RCODE_BADALG = 21, - DNS_RCODE_BADTRUNC = 22, - _DNS_RCODE_MAX_DEFINED -}; - -const char* dns_rcode_to_string(int i) _const_; -int dns_rcode_from_string(const char *s) _pure_; - -const char* dns_protocol_to_string(DnsProtocol p) _const_; -DnsProtocol dns_protocol_from_string(const char *s) _pure_; - -#define LLMNR_MULTICAST_IPV4_ADDRESS ((struct in_addr) { .s_addr = htobe32(224U << 24 | 252U) }) -#define LLMNR_MULTICAST_IPV6_ADDRESS ((struct in6_addr) { .s6_addr = { 0xFF, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03 } }) - -#define DNSKEY_FLAG_ZONE_KEY (1u << 8) -#define DNSKEY_FLAG_SEP (1u << 0) - -static inline uint16_t dnskey_to_flags(const DnsResourceRecord *rr) { - return (rr->dnskey.zone_key_flag * DNSKEY_FLAG_ZONE_KEY | - rr->dnskey.sep_flag * DNSKEY_FLAG_SEP); -} - -/* http://tools.ietf.org/html/rfc4034#appendix-A.1 */ -enum { - DNSSEC_ALGORITHM_RSAMD5 = 1, - DNSSEC_ALGORITHM_DH, - DNSSEC_ALGORITHM_DSA, - DNSSEC_ALGORITHM_ECC, - DNSSEC_ALGORITHM_RSASHA1, - DNSSEC_ALGORITHM_INDIRECT = 252, - DNSSEC_ALGORITHM_PRIVATEDNS, - DNSSEC_ALGORITHM_PRIVATEOID, - _DNSSEC_ALGORITHM_MAX_DEFINED -}; - -const char* dnssec_algorithm_to_string(int i) _const_; -int dnssec_algorithm_from_string(const char *s) _pure_; - -static inline uint64_t SD_RESOLVED_FLAGS_MAKE(DnsProtocol protocol, int family) { - - /* Converts a protocol + family into a flags field as used in queries */ - - if (protocol == DNS_PROTOCOL_DNS) - return SD_RESOLVED_DNS; - - if (protocol == DNS_PROTOCOL_LLMNR) - return family == AF_INET6 ? SD_RESOLVED_LLMNR_IPV6 : SD_RESOLVED_LLMNR_IPV4; - - return 0; -} diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c deleted file mode 100644 index d619fae725..0000000000 --- a/src/resolve/resolved-dns-query.c +++ /dev/null @@ -1,487 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - - -#include "resolved-dns-query.h" - -/* How long to wait for the query in total */ -#define QUERY_TIMEOUT_USEC (30 * USEC_PER_SEC) - -#define CNAME_MAX 8 -#define QUERIES_MAX 2048 - -static void dns_query_stop(DnsQuery *q) { - DnsTransaction *t; - - assert(q); - - q->timeout_event_source = sd_event_source_unref(q->timeout_event_source); - - while ((t = set_steal_first(q->transactions))) { - set_remove(t->queries, q); - dns_transaction_gc(t); - } -} - -DnsQuery *dns_query_free(DnsQuery *q) { - if (!q) - return NULL; - - dns_query_stop(q); - set_free(q->transactions); - - dns_question_unref(q->question); - dns_answer_unref(q->answer); - - sd_bus_message_unref(q->request); - sd_bus_track_unref(q->bus_track); - - if (q->manager) { - LIST_REMOVE(queries, q->manager->dns_queries, q); - q->manager->n_dns_queries--; - } - - free(q); - - return NULL; -} - -int dns_query_new(Manager *m, DnsQuery **ret, DnsQuestion *question, int ifindex, uint64_t flags) { - _cleanup_(dns_query_freep) DnsQuery *q = NULL; - unsigned i; - int r; - - assert(m); - assert(question); - - r = dns_question_is_valid(question); - if (r < 0) - return r; - - if (m->n_dns_queries >= QUERIES_MAX) - return -EBUSY; - - q = new0(DnsQuery, 1); - if (!q) - return -ENOMEM; - - q->question = dns_question_ref(question); - q->ifindex = ifindex; - q->flags = flags; - - for (i = 0; i < question->n_keys; i++) { - _cleanup_free_ char *p; - - r = dns_resource_key_to_string(question->keys[i], &p); - if (r < 0) - return r; - - log_debug("Looking up RR for %s", p); - } - - LIST_PREPEND(queries, m->dns_queries, q); - m->n_dns_queries++; - q->manager = m; - - if (ret) - *ret = q; - q = NULL; - - return 0; -} - -static void dns_query_complete(DnsQuery *q, DnsTransactionState state) { - assert(q); - assert(!IN_SET(state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING)); - assert(IN_SET(q->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING)); - - /* Note that this call might invalidate the query. Callers - * should hence not attempt to access the query or transaction - * after calling this function. */ - - q->state = state; - - dns_query_stop(q); - if (q->complete) - q->complete(q); -} - -static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) { - DnsQuery *q = userdata; - - assert(s); - assert(q); - - dns_query_complete(q, DNS_TRANSACTION_TIMEOUT); - return 0; -} - -static int dns_query_add_transaction(DnsQuery *q, DnsScope *s, DnsResourceKey *key) { - _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; - DnsTransaction *t; - int r; - - assert(q); - assert(s); - - r = set_ensure_allocated(&q->transactions, NULL); - if (r < 0) - return r; - - if (key) { - question = dns_question_new(1); - if (!question) - return -ENOMEM; - - r = dns_question_add(question, key); - if (r < 0) - return r; - } else - question = dns_question_ref(q->question); - - t = dns_scope_find_transaction(s, question, true); - if (!t) { - r = dns_transaction_new(&t, s, question); - if (r < 0) - return r; - } - - r = set_ensure_allocated(&t->queries, NULL); - if (r < 0) - goto gc; - - r = set_put(t->queries, q); - if (r < 0) - goto gc; - - r = set_put(q->transactions, t); - if (r < 0) { - set_remove(t->queries, q); - goto gc; - } - - return 0; - -gc: - dns_transaction_gc(t); - return r; -} - -static int dns_query_add_transaction_split(DnsQuery *q, DnsScope *s) { - int r; - - assert(q); - assert(s); - - if (s->protocol == DNS_PROTOCOL_MDNS) { - r = dns_query_add_transaction(q, s, NULL); - if (r < 0) - return r; - } else { - unsigned i; - - /* On DNS and LLMNR we can only send a single - * question per datagram, hence issue multiple - * transactions. */ - - for (i = 0; i < q->question->n_keys; i++) { - r = dns_query_add_transaction(q, s, q->question->keys[i]); - if (r < 0) - return r; - } - } - - return 0; -} - -int dns_query_go(DnsQuery *q) { - DnsScopeMatch found = DNS_SCOPE_NO; - DnsScope *s, *first = NULL; - DnsTransaction *t; - const char *name; - Iterator i; - int r; - - assert(q); - - if (q->state != DNS_TRANSACTION_NULL) - return 0; - - assert(q->question); - assert(q->question->n_keys > 0); - - name = DNS_RESOURCE_KEY_NAME(q->question->keys[0]); - - LIST_FOREACH(scopes, s, q->manager->dns_scopes) { - DnsScopeMatch match; - - match = dns_scope_good_domain(s, q->ifindex, q->flags, name); - if (match < 0) - return match; - - if (match == DNS_SCOPE_NO) - continue; - - found = match; - - if (match == DNS_SCOPE_YES) { - first = s; - break; - } else { - assert(match == DNS_SCOPE_MAYBE); - - if (!first) - first = s; - } - } - - if (found == DNS_SCOPE_NO) - return -ESRCH; - - r = dns_query_add_transaction_split(q, first); - if (r < 0) - goto fail; - - LIST_FOREACH(scopes, s, first->scopes_next) { - DnsScopeMatch match; - - match = dns_scope_good_domain(s, q->ifindex, q->flags, name); - if (match < 0) - goto fail; - - if (match != found) - continue; - - r = dns_query_add_transaction_split(q, s); - if (r < 0) - goto fail; - } - - q->answer = dns_answer_unref(q->answer); - q->answer_ifindex = 0; - q->answer_rcode = 0; - q->answer_family = AF_UNSPEC; - q->answer_protocol = _DNS_PROTOCOL_INVALID; - - r = sd_event_add_time( - q->manager->event, - &q->timeout_event_source, - clock_boottime_or_monotonic(), - now(clock_boottime_or_monotonic()) + QUERY_TIMEOUT_USEC, 0, - on_query_timeout, q); - if (r < 0) - goto fail; - - q->state = DNS_TRANSACTION_PENDING; - q->block_ready++; - - /* Start the transactions that are not started yet */ - SET_FOREACH(t, q->transactions, i) { - if (t->state != DNS_TRANSACTION_NULL) - continue; - - r = dns_transaction_go(t); - if (r < 0) - goto fail; - } - - q->block_ready--; - dns_query_ready(q); - - return 1; - -fail: - dns_query_stop(q); - return r; -} - -void dns_query_ready(DnsQuery *q) { - DnsTransaction *t; - DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS; - _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; - int rcode = 0; - DnsScope *scope = NULL; - bool pending = false; - Iterator i; - - assert(q); - assert(IN_SET(q->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING)); - - /* Note that this call might invalidate the query. Callers - * should hence not attempt to access the query or transaction - * after calling this function, unless the block_ready - * counter was explicitly bumped before doing so. */ - - if (q->block_ready > 0) - return; - - SET_FOREACH(t, q->transactions, i) { - - /* If we found a successful answer, ignore all answers from other scopes */ - if (state == DNS_TRANSACTION_SUCCESS && t->scope != scope) - continue; - - /* One of the transactions is still going on, let's maybe wait for it */ - if (IN_SET(t->state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_NULL)) { - pending = true; - continue; - } - - /* One of the transactions is successful, let's use - * it, and copy its data out */ - if (t->state == DNS_TRANSACTION_SUCCESS) { - DnsAnswer *a; - - if (t->received) { - rcode = DNS_PACKET_RCODE(t->received); - a = t->received->answer; - } else { - rcode = t->cached_rcode; - a = t->cached; - } - - if (state == DNS_TRANSACTION_SUCCESS) { - DnsAnswer *merged; - - merged = dns_answer_merge(answer, a); - if (!merged) { - dns_query_complete(q, DNS_TRANSACTION_RESOURCES); - return; - } - - dns_answer_unref(answer); - answer = merged; - } else { - dns_answer_unref(answer); - answer = dns_answer_ref(a); - } - - scope = t->scope; - state = DNS_TRANSACTION_SUCCESS; - continue; - } - - /* One of the transactions has failed, let's see - * whether we find anything better, but if not, return - * its response data */ - if (state != DNS_TRANSACTION_SUCCESS && t->state == DNS_TRANSACTION_FAILURE) { - DnsAnswer *a; - - if (t->received) { - rcode = DNS_PACKET_RCODE(t->received); - a = t->received->answer; - } else { - rcode = t->cached_rcode; - a = t->cached; - } - - dns_answer_unref(answer); - answer = dns_answer_ref(a); - - scope = t->scope; - state = DNS_TRANSACTION_FAILURE; - continue; - } - - if (state == DNS_TRANSACTION_NO_SERVERS && t->state != DNS_TRANSACTION_NO_SERVERS) - state = t->state; - } - - if (pending) { - - /* If so far we weren't successful, and there's - * something still pending, then wait for it */ - if (state != DNS_TRANSACTION_SUCCESS) - return; - - /* If we already were successful, then only wait for - * other transactions on the same scope to finish. */ - SET_FOREACH(t, q->transactions, i) { - if (t->scope == scope && IN_SET(t->state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_NULL)) - return; - } - } - - if (IN_SET(state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_FAILURE)) { - q->answer = dns_answer_ref(answer); - q->answer_rcode = rcode; - q->answer_ifindex = (scope && scope->link) ? scope->link->ifindex : 0; - q->answer_protocol = scope ? scope->protocol : _DNS_PROTOCOL_INVALID; - q->answer_family = scope ? scope->family : AF_UNSPEC; - } - - dns_query_complete(q, state); -} - -int dns_query_cname_redirect(DnsQuery *q, const char *name) { - _cleanup_(dns_question_unrefp) DnsQuestion *nq = NULL; - int r; - - assert(q); - - if (q->n_cname_redirects > CNAME_MAX) - return -ELOOP; - - r = dns_question_cname_redirect(q->question, name, &nq); - if (r < 0) - return r; - - dns_question_unref(q->question); - q->question = nq; - nq = NULL; - - q->n_cname_redirects++; - - dns_query_stop(q); - q->state = DNS_TRANSACTION_NULL; - - return 0; -} - -static int on_bus_track(sd_bus_track *t, void *userdata) { - DnsQuery *q = userdata; - - assert(t); - assert(q); - - log_debug("Client of active query vanished, aborting query."); - dns_query_complete(q, DNS_TRANSACTION_ABORTED); - return 0; -} - -int dns_query_bus_track(DnsQuery *q, sd_bus *bus, sd_bus_message *m) { - int r; - - assert(q); - assert(m); - - if (!q->bus_track) { - r = sd_bus_track_new(bus, &q->bus_track, on_bus_track, q); - if (r < 0) - return r; - } - - r = sd_bus_track_add_sender(q->bus_track, m); - if (r < 0) - return r; - - return 0; -} diff --git a/src/resolve/resolved-dns-query.h b/src/resolve/resolved-dns-query.h deleted file mode 100644 index 788992e679..0000000000 --- a/src/resolve/resolved-dns-query.h +++ /dev/null @@ -1,80 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - - -#include "sd-bus.h" -#include "set.h" - -typedef struct DnsQuery DnsQuery; - -#include "resolved-dns-question.h" -#include "resolved-dns-answer.h" -#include "resolved-dns-stream.h" - -struct DnsQuery { - Manager *manager; - DnsQuestion *question; - - uint64_t flags; - int ifindex; - - DnsTransactionState state; - unsigned n_cname_redirects; - - sd_event_source *timeout_event_source; - - /* Discovered data */ - DnsAnswer *answer; - int answer_ifindex; - int answer_family; - DnsProtocol answer_protocol; - int answer_rcode; - - /* Bus client information */ - sd_bus_message *request; - int request_family; - const char *request_hostname; - union in_addr_union request_address; - - /* Completion callback */ - void (*complete)(DnsQuery* q); - unsigned block_ready; - - Set *transactions; - - sd_bus_track *bus_track; - - LIST_FIELDS(DnsQuery, queries); -}; - -int dns_query_new(Manager *m, DnsQuery **q, DnsQuestion *question, int family, uint64_t flags); -DnsQuery *dns_query_free(DnsQuery *q); - -int dns_query_go(DnsQuery *q); -void dns_query_ready(DnsQuery *q); - -int dns_query_cname_redirect(DnsQuery *q, const char *name); - -int dns_query_bus_track(DnsQuery *q, sd_bus *bus, sd_bus_message *m); - -DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQuery*, dns_query_free); diff --git a/src/resolve/resolved-dns-question.c b/src/resolve/resolved-dns-question.c deleted file mode 100644 index 45bcbbf23a..0000000000 --- a/src/resolve/resolved-dns-question.c +++ /dev/null @@ -1,274 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include "resolved-dns-question.h" -#include "resolved-dns-domain.h" - -DnsQuestion *dns_question_new(unsigned n) { - DnsQuestion *q; - - assert(n > 0); - - q = malloc0(offsetof(DnsQuestion, keys) + sizeof(DnsResourceKey*) * n); - if (!q) - return NULL; - - q->n_ref = 1; - q->n_allocated = n; - - return q; -} - -DnsQuestion *dns_question_ref(DnsQuestion *q) { - if (!q) - return NULL; - - assert(q->n_ref > 0); - q->n_ref++; - return q; -} - -DnsQuestion *dns_question_unref(DnsQuestion *q) { - if (!q) - return NULL; - - assert(q->n_ref > 0); - - if (q->n_ref == 1) { - unsigned i; - - for (i = 0; i < q->n_keys; i++) - dns_resource_key_unref(q->keys[i]); - free(q); - } else - q->n_ref--; - - return NULL; -} - -int dns_question_add(DnsQuestion *q, DnsResourceKey *key) { - unsigned i; - int r; - - assert(q); - assert(key); - - for (i = 0; i < q->n_keys; i++) { - r = dns_resource_key_equal(q->keys[i], key); - if (r < 0) - return r; - if (r > 0) - return 0; - } - - if (q->n_keys >= q->n_allocated) - return -ENOSPC; - - q->keys[q->n_keys++] = dns_resource_key_ref(key); - return 0; -} - -int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr) { - unsigned i; - int r; - - assert(q); - assert(rr); - - for (i = 0; i < q->n_keys; i++) { - r = dns_resource_key_match_rr(q->keys[i], rr); - if (r != 0) - return r; - } - - return 0; -} - -int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr) { - unsigned i; - int r; - - assert(q); - assert(rr); - - for (i = 0; i < q->n_keys; i++) { - r = dns_resource_key_match_cname(q->keys[i], rr); - if (r != 0) - return r; - } - - return 0; -} - -int dns_question_is_valid(DnsQuestion *q) { - const char *name; - unsigned i; - int r; - - assert(q); - - if (q->n_keys <= 0) - return 0; - - if (q->n_keys > 65535) - return 0; - - name = DNS_RESOURCE_KEY_NAME(q->keys[0]); - if (!name) - return 0; - - /* Check that all keys in this question bear the same name */ - for (i = 1; i < q->n_keys; i++) { - assert(q->keys[i]); - - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(q->keys[i]), name); - if (r <= 0) - return r; - } - - return 1; -} - -int dns_question_is_superset(DnsQuestion *q, DnsQuestion *other) { - unsigned j; - int r; - - assert(q); - assert(other); - - /* Checks if all keys in "other" are also contained in "q" */ - - for (j = 0; j < other->n_keys; j++) { - DnsResourceKey *b = other->keys[j]; - bool found = false; - unsigned i; - - for (i = 0; i < q->n_keys; i++) { - DnsResourceKey *a = q->keys[i]; - - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(a), DNS_RESOURCE_KEY_NAME(b)); - if (r < 0) - return r; - - if (r == 0) - continue; - - if (a->class != b->class && a->class != DNS_CLASS_ANY) - continue; - - if (a->type != b->type && a->type != DNS_TYPE_ANY) - continue; - - found = true; - break; - } - - if (!found) - return 0; - } - - return 1; -} - -int dns_question_cname_redirect(DnsQuestion *q, const char *name, DnsQuestion **ret) { - _cleanup_(dns_question_unrefp) DnsQuestion *n = NULL; - bool same = true; - unsigned i; - int r; - - assert(q); - assert(name); - assert(ret); - - for (i = 0; i < q->n_keys; i++) { - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(q->keys[i]), name); - if (r < 0) - return r; - - if (r == 0) { - same = false; - break; - } - } - - if (same) { - /* Shortcut, the names are already right */ - *ret = dns_question_ref(q); - return 0; - } - - n = dns_question_new(q->n_keys); - if (!n) - return -ENOMEM; - - /* Create a new question, and patch in the new name */ - for (i = 0; i < q->n_keys; i++) { - _cleanup_(dns_resource_key_unrefp) DnsResourceKey *k = NULL; - - k = dns_resource_key_new(q->keys[i]->class, q->keys[i]->type, name); - if (!k) - return -ENOMEM; - - r = dns_question_add(n, k); - if (r < 0) - return r; - } - - *ret = n; - n = NULL; - - return 1; -} - -int dns_question_endswith(DnsQuestion *q, const char *suffix) { - unsigned i; - - assert(q); - assert(suffix); - - for (i = 0; i < q->n_keys; i++) { - int k; - - k = dns_name_endswith(DNS_RESOURCE_KEY_NAME(q->keys[i]), suffix); - if (k <= 0) - return k; - } - - return 1; -} - -int dns_question_extract_reverse_address(DnsQuestion *q, int *family, union in_addr_union *address) { - unsigned i; - - assert(q); - assert(family); - assert(address); - - for (i = 0; i < q->n_keys; i++) { - int k; - - k = dns_name_address(DNS_RESOURCE_KEY_NAME(q->keys[i]), family, address); - if (k != 0) - return k; - } - - return 0; -} diff --git a/src/resolve/resolved-dns-question.h b/src/resolve/resolved-dns-question.h deleted file mode 100644 index 4ba2fe9f0e..0000000000 --- a/src/resolve/resolved-dns-question.h +++ /dev/null @@ -1,52 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -typedef struct DnsQuestion DnsQuestion; - -#include "resolved-dns-rr.h" - -/* A simple array of resources keys */ - -struct DnsQuestion { - unsigned n_ref; - unsigned n_keys, n_allocated; - DnsResourceKey* keys[0]; -}; - -DnsQuestion *dns_question_new(unsigned n); -DnsQuestion *dns_question_ref(DnsQuestion *q); -DnsQuestion *dns_question_unref(DnsQuestion *q); - -int dns_question_add(DnsQuestion *q, DnsResourceKey *key); - -int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr); -int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr); -int dns_question_is_valid(DnsQuestion *q); -int dns_question_is_superset(DnsQuestion *q, DnsQuestion *other); - -int dns_question_cname_redirect(DnsQuestion *q, const char *name, DnsQuestion **ret); - -int dns_question_endswith(DnsQuestion *q, const char *suffix); -int dns_question_extract_reverse_address(DnsQuestion *q, int *family, union in_addr_union *address); - -DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQuestion*, dns_question_unref); diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c deleted file mode 100644 index 78d9e4a412..0000000000 --- a/src/resolve/resolved-dns-rr.c +++ /dev/null @@ -1,698 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <math.h> - -#include "strv.h" - -#include "resolved-dns-domain.h" -#include "resolved-dns-rr.h" -#include "resolved-dns-packet.h" -#include "dns-type.h" - -DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *name) { - DnsResourceKey *k; - size_t l; - - assert(name); - - l = strlen(name); - k = malloc0(sizeof(DnsResourceKey) + l + 1); - if (!k) - return NULL; - - k->n_ref = 1; - k->class = class; - k->type = type; - - strcpy((char*) k + sizeof(DnsResourceKey), name); - - return k; -} - -DnsResourceKey* dns_resource_key_new_consume(uint16_t class, uint16_t type, char *name) { - DnsResourceKey *k; - - assert(name); - - k = new0(DnsResourceKey, 1); - if (!k) - return NULL; - - k->n_ref = 1; - k->class = class; - k->type = type; - k->_name = name; - - return k; -} - -DnsResourceKey* dns_resource_key_ref(DnsResourceKey *k) { - - if (!k) - return NULL; - - assert(k->n_ref > 0); - k->n_ref++; - - return k; -} - -DnsResourceKey* dns_resource_key_unref(DnsResourceKey *k) { - if (!k) - return NULL; - - assert(k->n_ref > 0); - - if (k->n_ref == 1) { - free(k->_name); - free(k); - } else - k->n_ref--; - - return NULL; -} - -int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b) { - int r; - - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(a), DNS_RESOURCE_KEY_NAME(b)); - if (r <= 0) - return r; - - if (a->class != b->class) - return 0; - - if (a->type != b->type) - return 0; - - return 1; -} - -int dns_resource_key_match_rr(const DnsResourceKey *key, const DnsResourceRecord *rr) { - assert(key); - assert(rr); - - if (rr->key->class != key->class && key->class != DNS_CLASS_ANY) - return 0; - - if (rr->key->type != key->type && key->type != DNS_TYPE_ANY) - return 0; - - return dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key)); -} - -int dns_resource_key_match_cname(const DnsResourceKey *key, const DnsResourceRecord *rr) { - assert(key); - assert(rr); - - if (rr->key->class != key->class && key->class != DNS_CLASS_ANY) - return 0; - - if (rr->key->type != DNS_TYPE_CNAME) - return 0; - - return dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key)); -} - -static unsigned long dns_resource_key_hash_func(const void *i, const uint8_t hash_key[HASH_KEY_SIZE]) { - const DnsResourceKey *k = i; - unsigned long ul; - - ul = dns_name_hash_func(DNS_RESOURCE_KEY_NAME(k), hash_key); - ul = ul * hash_key[0] + ul + k->class; - ul = ul * hash_key[1] + ul + k->type; - - return ul; -} - -static int dns_resource_key_compare_func(const void *a, const void *b) { - const DnsResourceKey *x = a, *y = b; - int ret; - - ret = dns_name_compare_func(DNS_RESOURCE_KEY_NAME(x), DNS_RESOURCE_KEY_NAME(y)); - if (ret != 0) - return ret; - - if (x->type < y->type) - return -1; - if (x->type > y->type) - return 1; - - if (x->class < y->class) - return -1; - if (x->class > y->class) - return 1; - - return 0; -} - -const struct hash_ops dns_resource_key_hash_ops = { - .hash = dns_resource_key_hash_func, - .compare = dns_resource_key_compare_func -}; - -int dns_resource_key_to_string(const DnsResourceKey *key, char **ret) { - char cbuf[DECIMAL_STR_MAX(uint16_t)], tbuf[DECIMAL_STR_MAX(uint16_t)]; - const char *c, *t; - char *s; - - c = dns_class_to_string(key->class); - if (!c) { - sprintf(cbuf, "%i", key->class); - c = cbuf; - } - - t = dns_type_to_string(key->type); - if (!t){ - sprintf(tbuf, "%i", key->type); - t = tbuf; - } - - if (asprintf(&s, "%s %s %-5s", DNS_RESOURCE_KEY_NAME(key), c, t) < 0) - return -ENOMEM; - - *ret = s; - return 0; -} - -DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key) { - DnsResourceRecord *rr; - - rr = new0(DnsResourceRecord, 1); - if (!rr) - return NULL; - - rr->n_ref = 1; - rr->key = dns_resource_key_ref(key); - - return rr; -} - -DnsResourceRecord* dns_resource_record_new_full(uint16_t class, uint16_t type, const char *name) { - _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; - - key = dns_resource_key_new(class, type, name); - if (!key) - return NULL; - - return dns_resource_record_new(key); -} - -DnsResourceRecord* dns_resource_record_ref(DnsResourceRecord *rr) { - if (!rr) - return NULL; - - assert(rr->n_ref > 0); - rr->n_ref++; - - return rr; -} - -DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr) { - if (!rr) - return NULL; - - assert(rr->n_ref > 0); - - if (rr->n_ref > 1) { - rr->n_ref--; - return NULL; - } - - if (rr->key) { - switch(rr->key->type) { - - case DNS_TYPE_SRV: - free(rr->srv.name); - break; - - case DNS_TYPE_PTR: - case DNS_TYPE_NS: - case DNS_TYPE_CNAME: - case DNS_TYPE_DNAME: - free(rr->ptr.name); - break; - - case DNS_TYPE_HINFO: - free(rr->hinfo.cpu); - free(rr->hinfo.os); - break; - - case DNS_TYPE_TXT: - case DNS_TYPE_SPF: - strv_free(rr->txt.strings); - break; - - case DNS_TYPE_SOA: - free(rr->soa.mname); - free(rr->soa.rname); - break; - - case DNS_TYPE_MX: - free(rr->mx.exchange); - break; - - case DNS_TYPE_SSHFP: - free(rr->sshfp.key); - break; - - case DNS_TYPE_DNSKEY: - free(rr->dnskey.key); - break; - - case DNS_TYPE_RRSIG: - free(rr->rrsig.signer); - free(rr->rrsig.signature); - break; - - case DNS_TYPE_LOC: - case DNS_TYPE_A: - case DNS_TYPE_AAAA: - break; - - default: - free(rr->generic.data); - } - - dns_resource_key_unref(rr->key); - } - - free(rr); - - return NULL; -} - -int dns_resource_record_new_reverse(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *hostname) { - _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; - _cleanup_free_ char *ptr = NULL; - int r; - - assert(ret); - assert(address); - assert(hostname); - - r = dns_name_reverse(family, address, &ptr); - if (r < 0) - return r; - - key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, ptr); - if (!key) - return -ENOMEM; - - ptr = NULL; - - rr = dns_resource_record_new(key); - if (!rr) - return -ENOMEM; - - rr->ptr.name = strdup(hostname); - if (!rr->ptr.name) - return -ENOMEM; - - *ret = rr; - rr = NULL; - - return 0; -} - -int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b) { - int r; - - assert(a); - assert(b); - - r = dns_resource_key_equal(a->key, b->key); - if (r <= 0) - return r; - - if (a->unparseable != b->unparseable) - return 0; - - switch (a->unparseable ? _DNS_TYPE_INVALID : a->key->type) { - - case DNS_TYPE_SRV: - r = dns_name_equal(a->srv.name, b->srv.name); - if (r <= 0) - return r; - - return a->srv.priority == b->srv.priority && - a->srv.weight == b->srv.weight && - a->srv.port == b->srv.port; - - case DNS_TYPE_PTR: - case DNS_TYPE_NS: - case DNS_TYPE_CNAME: - case DNS_TYPE_DNAME: - return dns_name_equal(a->ptr.name, b->ptr.name); - - case DNS_TYPE_HINFO: - return strcaseeq(a->hinfo.cpu, b->hinfo.cpu) && - strcaseeq(a->hinfo.os, b->hinfo.os); - - case DNS_TYPE_SPF: /* exactly the same as TXT */ - case DNS_TYPE_TXT: - return strv_equal(a->txt.strings, b->txt.strings); - - case DNS_TYPE_A: - return memcmp(&a->a.in_addr, &b->a.in_addr, sizeof(struct in_addr)) == 0; - - case DNS_TYPE_AAAA: - return memcmp(&a->aaaa.in6_addr, &b->aaaa.in6_addr, sizeof(struct in6_addr)) == 0; - - case DNS_TYPE_SOA: - r = dns_name_equal(a->soa.mname, b->soa.mname); - if (r <= 0) - return r; - r = dns_name_equal(a->soa.rname, b->soa.rname); - if (r <= 0) - return r; - - return a->soa.serial == b->soa.serial && - a->soa.refresh == b->soa.refresh && - a->soa.retry == b->soa.retry && - a->soa.expire == b->soa.expire && - a->soa.minimum == b->soa.minimum; - - case DNS_TYPE_MX: - if (a->mx.priority != b->mx.priority) - return 0; - - return dns_name_equal(a->mx.exchange, b->mx.exchange); - - case DNS_TYPE_LOC: - assert(a->loc.version == b->loc.version); - - return a->loc.size == b->loc.size && - a->loc.horiz_pre == b->loc.horiz_pre && - a->loc.vert_pre == b->loc.vert_pre && - a->loc.latitude == b->loc.latitude && - a->loc.longitude == b->loc.longitude && - a->loc.altitude == b->loc.altitude; - - case DNS_TYPE_SSHFP: - return a->sshfp.algorithm == b->sshfp.algorithm && - a->sshfp.fptype == b->sshfp.fptype && - a->sshfp.key_size == b->sshfp.key_size && - memcmp(a->sshfp.key, b->sshfp.key, a->sshfp.key_size) == 0; - - case DNS_TYPE_DNSKEY: - return a->dnskey.zone_key_flag == b->dnskey.zone_key_flag && - a->dnskey.sep_flag == b->dnskey.sep_flag && - a->dnskey.algorithm == b->dnskey.algorithm && - a->dnskey.key_size == b->dnskey.key_size && - memcmp(a->dnskey.key, b->dnskey.key, a->dnskey.key_size) == 0; - - case DNS_TYPE_RRSIG: - /* do the fast comparisons first */ - if (a->rrsig.type_covered != b->rrsig.type_covered || - a->rrsig.algorithm != b->rrsig.algorithm || - a->rrsig.labels != b->rrsig.labels || - a->rrsig.original_ttl != b->rrsig.original_ttl || - a->rrsig.expiration != b->rrsig.expiration || - a->rrsig.inception != b->rrsig.inception || - a->rrsig.key_tag != b->rrsig.key_tag || - a->rrsig.signature_size != b->rrsig.signature_size || - memcmp(a->rrsig.signature, b->rrsig.signature, a->rrsig.signature_size) != 0) - return false; - - return dns_name_equal(a->rrsig.signer, b->rrsig.signer); - - default: - return a->generic.size == b->generic.size && - memcmp(a->generic.data, b->generic.data, a->generic.size) == 0; - } -} - -static char* format_location(uint32_t latitude, uint32_t longitude, uint32_t altitude, - uint8_t size, uint8_t horiz_pre, uint8_t vert_pre) { - char *s; - char NS = latitude >= 1U<<31 ? 'N' : 'S'; - char EW = longitude >= 1U<<31 ? 'E' : 'W'; - - int lat = latitude >= 1U<<31 ? (int) (latitude - (1U<<31)) : (int) ((1U<<31) - latitude); - int lon = longitude >= 1U<<31 ? (int) (longitude - (1U<<31)) : (int) ((1U<<31) - longitude); - double alt = altitude >= 10000000u ? altitude - 10000000u : -(double)(10000000u - altitude); - double siz = (size >> 4) * exp10((double) (size & 0xF)); - double hor = (horiz_pre >> 4) * exp10((double) (horiz_pre & 0xF)); - double ver = (vert_pre >> 4) * exp10((double) (vert_pre & 0xF)); - - if (asprintf(&s, "%d %d %.3f %c %d %d %.3f %c %.2fm %.2fm %.2fm %.2fm", - (lat / 60000 / 60), - (lat / 60000) % 60, - (lat % 60000) / 1000., - NS, - (lon / 60000 / 60), - (lon / 60000) % 60, - (lon % 60000) / 1000., - EW, - alt / 100., - siz / 100., - hor / 100., - ver / 100.) < 0) - return NULL; - - return s; -} - -int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) { - _cleanup_free_ char *k = NULL, *t = NULL; - char *s; - int r; - - assert(rr); - - r = dns_resource_key_to_string(rr->key, &k); - if (r < 0) - return r; - - switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) { - - case DNS_TYPE_SRV: - r = asprintf(&s, "%s %u %u %u %s", - k, - rr->srv.priority, - rr->srv.weight, - rr->srv.port, - strna(rr->srv.name)); - if (r < 0) - return -ENOMEM; - break; - - case DNS_TYPE_PTR: - case DNS_TYPE_NS: - case DNS_TYPE_CNAME: - case DNS_TYPE_DNAME: - s = strjoin(k, " ", rr->ptr.name, NULL); - if (!s) - return -ENOMEM; - - break; - - case DNS_TYPE_HINFO: - s = strjoin(k, " ", rr->hinfo.cpu, " ", rr->hinfo.os, NULL); - if (!s) - return -ENOMEM; - break; - - case DNS_TYPE_SPF: /* exactly the same as TXT */ - case DNS_TYPE_TXT: - t = strv_join_quoted(rr->txt.strings); - if (!t) - return -ENOMEM; - - s = strjoin(k, " ", t, NULL); - if (!s) - return -ENOMEM; - - break; - - case DNS_TYPE_A: { - _cleanup_free_ char *x = NULL; - - r = in_addr_to_string(AF_INET, (const union in_addr_union*) &rr->a.in_addr, &x); - if (r < 0) - return r; - - s = strjoin(k, " ", x, NULL); - if (!s) - return -ENOMEM; - break; - } - - case DNS_TYPE_AAAA: - r = in_addr_to_string(AF_INET6, (const union in_addr_union*) &rr->aaaa.in6_addr, &t); - if (r < 0) - return r; - - s = strjoin(k, " ", t, NULL); - if (!s) - return -ENOMEM; - break; - - case DNS_TYPE_SOA: - r = asprintf(&s, "%s %s %s %u %u %u %u %u", - k, - strna(rr->soa.mname), - strna(rr->soa.rname), - rr->soa.serial, - rr->soa.refresh, - rr->soa.retry, - rr->soa.expire, - rr->soa.minimum); - if (r < 0) - return -ENOMEM; - break; - - case DNS_TYPE_MX: - r = asprintf(&s, "%s %u %s", - k, - rr->mx.priority, - rr->mx.exchange); - if (r < 0) - return -ENOMEM; - break; - - case DNS_TYPE_LOC: - assert(rr->loc.version == 0); - - t = format_location(rr->loc.latitude, - rr->loc.longitude, - rr->loc.altitude, - rr->loc.size, - rr->loc.horiz_pre, - rr->loc.vert_pre); - if (!t) - return -ENOMEM; - - s = strjoin(k, " ", t, NULL); - if (!s) - return -ENOMEM; - break; - - case DNS_TYPE_SSHFP: - t = hexmem(rr->sshfp.key, rr->sshfp.key_size); - if (!t) - return -ENOMEM; - - r = asprintf(&s, "%s %u %u %s", - k, - rr->sshfp.algorithm, - rr->sshfp.fptype, - t); - if (r < 0) - return -ENOMEM; - break; - - case DNS_TYPE_DNSKEY: { - const char *alg; - - alg = dnssec_algorithm_to_string(rr->dnskey.algorithm); - - t = hexmem(rr->dnskey.key, rr->dnskey.key_size); - if (!t) - return -ENOMEM; - - r = asprintf(&s, "%s %u 3 %.*s%.*u %s", - k, - dnskey_to_flags(rr), - alg ? -1 : 0, alg, - alg ? 0 : 1, alg ? 0u : (unsigned) rr->dnskey.algorithm, - t); - if (r < 0) - return -ENOMEM; - break; - } - - case DNS_TYPE_RRSIG: { - const char *type, *alg; - - type = dns_type_to_string(rr->rrsig.type_covered); - alg = dnssec_algorithm_to_string(rr->rrsig.algorithm); - - t = hexmem(rr->rrsig.signature, rr->rrsig.signature_size); - if (!t) - return -ENOMEM; - - /* TYPE?? follows - * http://tools.ietf.org/html/rfc3597#section-5 */ - - r = asprintf(&s, "%s %s%.*u %.*s%.*u %u %u %u %u %u %s %s", - k, - type ?: "TYPE", - type ? 0 : 1, type ? 0u : (unsigned) rr->rrsig.type_covered, - alg ? -1 : 0, alg, - alg ? 0 : 1, alg ? 0u : (unsigned) rr->rrsig.algorithm, - rr->rrsig.labels, - rr->rrsig.original_ttl, - rr->rrsig.expiration, - rr->rrsig.inception, - rr->rrsig.key_tag, - rr->rrsig.signer, - t); - if (r < 0) - return -ENOMEM; - break; - } - - default: - t = hexmem(rr->generic.data, rr->generic.size); - if (!t) - return -ENOMEM; - - s = strjoin(k, " ", t, NULL); - if (!s) - return -ENOMEM; - break; - } - - *ret = s; - return 0; -} - -const char *dns_class_to_string(uint16_t class) { - - switch (class) { - - case DNS_CLASS_IN: - return "IN"; - - case DNS_CLASS_ANY: - return "ANY"; - } - - return NULL; -} - -int dns_class_from_string(const char *s, uint16_t *class) { - assert(s); - assert(class); - - if (strcaseeq(s, "IN")) - *class = DNS_CLASS_IN; - else if (strcaseeq(s, "ANY")) - *class = DNS_TYPE_ANY; - else - return -EINVAL; - - return 0; -} diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h deleted file mode 100644 index 26796c842b..0000000000 --- a/src/resolve/resolved-dns-rr.h +++ /dev/null @@ -1,175 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. - ***/ - -#include <netinet/in.h> - -#include "hashmap.h" -#include "in-addr-util.h" -#include "dns-type.h" - -typedef struct DnsResourceKey DnsResourceKey; -typedef struct DnsResourceRecord DnsResourceRecord; - -/* DNS record classes, see RFC 1035 */ -enum { - DNS_CLASS_IN = 0x01, - DNS_CLASS_ANY = 0xFF, - _DNS_CLASS_MAX, - _DNS_CLASS_INVALID = -1 -}; - -struct DnsResourceKey { - unsigned n_ref; - uint16_t class, type; - char *_name; /* don't access directy, use DNS_RESOURCE_KEY_NAME()! */ -}; - -struct DnsResourceRecord { - unsigned n_ref; - DnsResourceKey *key; - uint32_t ttl; - bool unparseable; - union { - struct { - void *data; - uint16_t size; - } generic; - - struct { - uint16_t priority; - uint16_t weight; - uint16_t port; - char *name; - } srv; - - struct { - char *name; - } ptr, ns, cname, dname; - - struct { - char *cpu; - char *os; - } hinfo; - - struct { - char **strings; - } txt, spf; - - struct { - struct in_addr in_addr; - } a; - - struct { - struct in6_addr in6_addr; - } aaaa; - - struct { - char *mname; - char *rname; - uint32_t serial; - uint32_t refresh; - uint32_t retry; - uint32_t expire; - uint32_t minimum; - } soa; - - struct { - uint16_t priority; - char *exchange; - } mx; - - struct { - uint8_t version; - uint8_t size; - uint8_t horiz_pre; - uint8_t vert_pre; - uint32_t latitude; - uint32_t longitude; - uint32_t altitude; - } loc; - - struct { - uint8_t algorithm; - uint8_t fptype; - void *key; - size_t key_size; - } sshfp; - - /* http://tools.ietf.org/html/rfc4034#section-2.1 */ - struct { - bool zone_key_flag:1; - bool sep_flag:1; - uint8_t algorithm; - void* key; - size_t key_size; - } dnskey; - - /* http://tools.ietf.org/html/rfc4034#section-3.1 */ - struct { - uint16_t type_covered; - uint8_t algorithm; - uint8_t labels; - uint32_t original_ttl; - uint32_t expiration; - uint32_t inception; - uint16_t key_tag; - char *signer; - void *signature; - size_t signature_size; - } rrsig; - }; -}; - -static inline const char* DNS_RESOURCE_KEY_NAME(const DnsResourceKey *key) { - if (_unlikely_(!key)) - return NULL; - - if (key->_name) - return key->_name; - - return (char*) key + sizeof(DnsResourceKey); -} - -DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *name); -DnsResourceKey* dns_resource_key_new_consume(uint16_t class, uint16_t type, char *name); -DnsResourceKey* dns_resource_key_ref(DnsResourceKey *key); -DnsResourceKey* dns_resource_key_unref(DnsResourceKey *key); -int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b); -int dns_resource_key_match_rr(const DnsResourceKey *key, const DnsResourceRecord *rr); -int dns_resource_key_match_cname(const DnsResourceKey *key, const DnsResourceRecord *rr); -int dns_resource_key_to_string(const DnsResourceKey *key, char **ret); -DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceKey*, dns_resource_key_unref); - -DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key); -DnsResourceRecord* dns_resource_record_new_full(uint16_t class, uint16_t type, const char *name); -DnsResourceRecord* dns_resource_record_ref(DnsResourceRecord *rr); -DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr); -int dns_resource_record_new_reverse(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *name); -int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b); -int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret); -DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceRecord*, dns_resource_record_unref); - -const char *dns_class_to_string(uint16_t type); -int dns_class_from_string(const char *name, uint16_t *class); - -extern const struct hash_ops dns_resource_key_hash_ops; diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c deleted file mode 100644 index ba116de6f5..0000000000 --- a/src/resolve/resolved-dns-scope.c +++ /dev/null @@ -1,794 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <netinet/tcp.h> - -#include "missing.h" -#include "strv.h" -#include "socket-util.h" -#include "af-list.h" -#include "resolved-dns-domain.h" -#include "resolved-dns-scope.h" - -#define MULTICAST_RATELIMIT_INTERVAL_USEC (1*USEC_PER_SEC) -#define MULTICAST_RATELIMIT_BURST 1000 - -int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int family) { - DnsScope *s; - - assert(m); - assert(ret); - - s = new0(DnsScope, 1); - if (!s) - return -ENOMEM; - - s->manager = m; - s->link = l; - s->protocol = protocol; - s->family = family; - - LIST_PREPEND(scopes, m->dns_scopes, s); - - dns_scope_llmnr_membership(s, true); - - log_debug("New scope on link %s, protocol %s, family %s", l ? l->name : "*", dns_protocol_to_string(protocol), family == AF_UNSPEC ? "*" : af_to_name(family)); - - /* Enforce ratelimiting for the multicast protocols */ - RATELIMIT_INIT(s->ratelimit, MULTICAST_RATELIMIT_INTERVAL_USEC, MULTICAST_RATELIMIT_BURST); - - *ret = s; - return 0; -} - -DnsScope* dns_scope_free(DnsScope *s) { - DnsTransaction *t; - DnsResourceRecord *rr; - - if (!s) - return NULL; - - log_debug("Removing scope on link %s, protocol %s, family %s", s->link ? s->link->name : "*", dns_protocol_to_string(s->protocol), s->family == AF_UNSPEC ? "*" : af_to_name(s->family)); - - dns_scope_llmnr_membership(s, false); - - while ((t = s->transactions)) { - - /* Abort the transaction, but make sure it is not - * freed while we still look at it */ - - t->block_gc++; - dns_transaction_complete(t, DNS_TRANSACTION_ABORTED); - t->block_gc--; - - dns_transaction_free(t); - } - - while ((rr = ordered_hashmap_steal_first(s->conflict_queue))) - dns_resource_record_unref(rr); - - ordered_hashmap_free(s->conflict_queue); - sd_event_source_unref(s->conflict_event_source); - - dns_cache_flush(&s->cache); - dns_zone_flush(&s->zone); - - LIST_REMOVE(scopes, s->manager->dns_scopes, s); - strv_free(s->domains); - free(s); - - return NULL; -} - -DnsServer *dns_scope_get_dns_server(DnsScope *s) { - assert(s); - - if (s->protocol != DNS_PROTOCOL_DNS) - return NULL; - - if (s->link) - return link_get_dns_server(s->link); - else - return manager_get_dns_server(s->manager); -} - -void dns_scope_next_dns_server(DnsScope *s) { - assert(s); - - if (s->protocol != DNS_PROTOCOL_DNS) - return; - - if (s->link) - link_next_dns_server(s->link); - else - manager_next_dns_server(s->manager); -} - -int dns_scope_emit(DnsScope *s, DnsPacket *p) { - union in_addr_union addr; - int ifindex = 0, r; - int family; - uint16_t port; - uint32_t mtu; - int fd; - - assert(s); - assert(p); - assert(p->protocol == s->protocol); - - if (s->link) { - mtu = s->link->mtu; - ifindex = s->link->ifindex; - } else - mtu = manager_find_mtu(s->manager); - - if (s->protocol == DNS_PROTOCOL_DNS) { - DnsServer *srv; - - if (DNS_PACKET_QDCOUNT(p) > 1) - return -EOPNOTSUPP; - - srv = dns_scope_get_dns_server(s); - if (!srv) - return -ESRCH; - - family = srv->family; - addr = srv->address; - port = 53; - - if (p->size > DNS_PACKET_UNICAST_SIZE_MAX) - return -EMSGSIZE; - - if (p->size > mtu) - return -EMSGSIZE; - - if (family == AF_INET) - fd = manager_dns_ipv4_fd(s->manager); - else if (family == AF_INET6) - fd = manager_dns_ipv6_fd(s->manager); - else - return -EAFNOSUPPORT; - if (fd < 0) - return fd; - - } else if (s->protocol == DNS_PROTOCOL_LLMNR) { - - if (DNS_PACKET_QDCOUNT(p) > 1) - return -EOPNOTSUPP; - - if (!ratelimit_test(&s->ratelimit)) - return -EBUSY; - - family = s->family; - port = 5355; - - if (family == AF_INET) { - addr.in = LLMNR_MULTICAST_IPV4_ADDRESS; - fd = manager_llmnr_ipv4_udp_fd(s->manager); - } else if (family == AF_INET6) { - addr.in6 = LLMNR_MULTICAST_IPV6_ADDRESS; - fd = manager_llmnr_ipv6_udp_fd(s->manager); - } else - return -EAFNOSUPPORT; - if (fd < 0) - return fd; - } else - return -EAFNOSUPPORT; - - r = manager_send(s->manager, fd, ifindex, family, &addr, port, p); - if (r < 0) - return r; - - return 1; -} - -int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port) { - _cleanup_close_ int fd = -1; - union sockaddr_union sa = {}; - socklen_t salen; - static const int one = 1; - int ret, r; - - assert(s); - assert((family == AF_UNSPEC) == !address); - - if (family == AF_UNSPEC) { - DnsServer *srv; - - srv = dns_scope_get_dns_server(s); - if (!srv) - return -ESRCH; - - sa.sa.sa_family = srv->family; - if (srv->family == AF_INET) { - sa.in.sin_port = htobe16(port); - sa.in.sin_addr = srv->address.in; - salen = sizeof(sa.in); - } else if (srv->family == AF_INET6) { - sa.in6.sin6_port = htobe16(port); - sa.in6.sin6_addr = srv->address.in6; - sa.in6.sin6_scope_id = s->link ? s->link->ifindex : 0; - salen = sizeof(sa.in6); - } else - return -EAFNOSUPPORT; - } else { - sa.sa.sa_family = family; - - if (family == AF_INET) { - sa.in.sin_port = htobe16(port); - sa.in.sin_addr = address->in; - salen = sizeof(sa.in); - } else if (family == AF_INET6) { - sa.in6.sin6_port = htobe16(port); - sa.in6.sin6_addr = address->in6; - sa.in6.sin6_scope_id = s->link ? s->link->ifindex : 0; - salen = sizeof(sa.in6); - } else - return -EAFNOSUPPORT; - } - - fd = socket(sa.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (fd < 0) - return -errno; - - r = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)); - if (r < 0) - return -errno; - - if (s->link) { - uint32_t ifindex = htobe32(s->link->ifindex); - - if (sa.sa.sa_family == AF_INET) { - r = setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex, sizeof(ifindex)); - if (r < 0) - return -errno; - } else if (sa.sa.sa_family == AF_INET6) { - r = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex, sizeof(ifindex)); - if (r < 0) - return -errno; - } - } - - if (s->protocol == DNS_PROTOCOL_LLMNR) { - /* RFC 4795, section 2.5 requires the TTL to be set to 1 */ - - if (sa.sa.sa_family == AF_INET) { - r = setsockopt(fd, IPPROTO_IP, IP_TTL, &one, sizeof(one)); - if (r < 0) - return -errno; - } else if (sa.sa.sa_family == AF_INET6) { - r = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &one, sizeof(one)); - if (r < 0) - return -errno; - } - } - - r = connect(fd, &sa.sa, salen); - if (r < 0 && errno != EINPROGRESS) - return -errno; - - ret = fd; - fd = -1; - - return ret; -} - -DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain) { - char **i; - - assert(s); - assert(domain); - - if (ifindex != 0 && (!s->link || s->link->ifindex != ifindex)) - return DNS_SCOPE_NO; - - if ((SD_RESOLVED_FLAGS_MAKE(s->protocol, s->family) & flags) == 0) - return DNS_SCOPE_NO; - - STRV_FOREACH(i, s->domains) - if (dns_name_endswith(domain, *i) > 0) - return DNS_SCOPE_YES; - - if (dns_name_root(domain) != 0) - return DNS_SCOPE_NO; - - if (is_localhost(domain)) - return DNS_SCOPE_NO; - - if (s->protocol == DNS_PROTOCOL_DNS) { - if (dns_name_endswith(domain, "254.169.in-addr.arpa") == 0 && - dns_name_endswith(domain, "0.8.e.f.ip6.arpa") == 0 && - dns_name_single_label(domain) == 0) - return DNS_SCOPE_MAYBE; - - return DNS_SCOPE_NO; - } - - if (s->protocol == DNS_PROTOCOL_MDNS) { - if (dns_name_endswith(domain, "254.169.in-addr.arpa") > 0 || - dns_name_endswith(domain, "0.8.e.f.ip6.arpa") > 0 || - (dns_name_endswith(domain, "local") > 0 && dns_name_equal(domain, "local") == 0)) - return DNS_SCOPE_MAYBE; - - return DNS_SCOPE_NO; - } - - if (s->protocol == DNS_PROTOCOL_LLMNR) { - if (dns_name_endswith(domain, "in-addr.arpa") > 0 || - dns_name_endswith(domain, "ip6.arpa") > 0 || - (dns_name_single_label(domain) > 0 && - dns_name_equal(domain, "gateway") <= 0)) /* don't resolve "gateway" with LLMNR, let nss-myhostname handle this */ - return DNS_SCOPE_MAYBE; - - return DNS_SCOPE_NO; - } - - assert_not_reached("Unknown scope protocol"); -} - -int dns_scope_good_key(DnsScope *s, DnsResourceKey *key) { - assert(s); - assert(key); - - if (s->protocol == DNS_PROTOCOL_DNS) - return true; - - /* On mDNS and LLMNR, send A and AAAA queries only on the - * respective scopes */ - - if (s->family == AF_INET && key->class == DNS_CLASS_IN && key->type == DNS_TYPE_AAAA) - return false; - - if (s->family == AF_INET6 && key->class == DNS_CLASS_IN && key->type == DNS_TYPE_A) - return false; - - return true; -} - -int dns_scope_llmnr_membership(DnsScope *s, bool b) { - int fd; - - assert(s); - - if (s->protocol != DNS_PROTOCOL_LLMNR) - return 0; - - assert(s->link); - - if (s->family == AF_INET) { - struct ip_mreqn mreqn = { - .imr_multiaddr = LLMNR_MULTICAST_IPV4_ADDRESS, - .imr_ifindex = s->link->ifindex, - }; - - fd = manager_llmnr_ipv4_udp_fd(s->manager); - if (fd < 0) - return fd; - - /* Always first try to drop membership before we add - * one. This is necessary on some devices, such as - * veth. */ - if (b) - (void) setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn)); - - if (setsockopt(fd, IPPROTO_IP, b ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn)) < 0) - return -errno; - - } else if (s->family == AF_INET6) { - struct ipv6_mreq mreq = { - .ipv6mr_multiaddr = LLMNR_MULTICAST_IPV6_ADDRESS, - .ipv6mr_interface = s->link->ifindex, - }; - - fd = manager_llmnr_ipv6_udp_fd(s->manager); - if (fd < 0) - return fd; - - if (b) - (void) setsockopt(fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq)); - - if (setsockopt(fd, IPPROTO_IPV6, b ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) - return -errno; - } else - return -EAFNOSUPPORT; - - return 0; -} - -int dns_scope_good_dns_server(DnsScope *s, int family, const union in_addr_union *address) { - assert(s); - assert(address); - - if (s->protocol != DNS_PROTOCOL_DNS) - return 1; - - if (s->link) - return !!link_find_dns_server(s->link, family, address); - else - return !!manager_find_dns_server(s->manager, family, address); -} - -static int dns_scope_make_reply_packet( - DnsScope *s, - uint16_t id, - int rcode, - DnsQuestion *q, - DnsAnswer *answer, - DnsAnswer *soa, - bool tentative, - DnsPacket **ret) { - - _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; - unsigned i; - int r; - - assert(s); - assert(ret); - - if ((!q || q->n_keys <= 0) - && (!answer || answer->n_rrs <= 0) - && (!soa || soa->n_rrs <= 0)) - return -EINVAL; - - r = dns_packet_new(&p, s->protocol, 0); - if (r < 0) - return r; - - DNS_PACKET_HEADER(p)->id = id; - DNS_PACKET_HEADER(p)->flags = htobe16(DNS_PACKET_MAKE_FLAGS( - 1 /* qr */, - 0 /* opcode */, - 0 /* c */, - 0 /* tc */, - tentative, - 0 /* (ra) */, - 0 /* (ad) */, - 0 /* (cd) */, - rcode)); - - if (q) { - for (i = 0; i < q->n_keys; i++) { - r = dns_packet_append_key(p, q->keys[i], NULL); - if (r < 0) - return r; - } - - DNS_PACKET_HEADER(p)->qdcount = htobe16(q->n_keys); - } - - if (answer) { - for (i = 0; i < answer->n_rrs; i++) { - r = dns_packet_append_rr(p, answer->rrs[i], NULL); - if (r < 0) - return r; - } - - DNS_PACKET_HEADER(p)->ancount = htobe16(answer->n_rrs); - } - - if (soa) { - for (i = 0; i < soa->n_rrs; i++) { - r = dns_packet_append_rr(p, soa->rrs[i], NULL); - if (r < 0) - return r; - } - - DNS_PACKET_HEADER(p)->arcount = htobe16(soa->n_rrs); - } - - *ret = p; - p = NULL; - - return 0; -} - -static void dns_scope_verify_conflicts(DnsScope *s, DnsPacket *p) { - unsigned n; - - assert(s); - assert(p); - - if (p->question) - for (n = 0; n < p->question->n_keys; n++) - dns_zone_verify_conflicts(&s->zone, p->question->keys[n]); - if (p->answer) - for (n = 0; n < p->answer->n_rrs; n++) - dns_zone_verify_conflicts(&s->zone, p->answer->rrs[n]->key); -} - -void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) { - _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL; - _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL; - bool tentative = false; - int r, fd; - - assert(s); - assert(p); - - if (p->protocol != DNS_PROTOCOL_LLMNR) - return; - - if (p->ipproto == IPPROTO_UDP) { - /* Don't accept UDP queries directed to anything but - * the LLMNR multicast addresses. See RFC 4795, - * section 2.5. */ - - if (p->family == AF_INET && !in_addr_equal(AF_INET, &p->destination, (union in_addr_union*) &LLMNR_MULTICAST_IPV4_ADDRESS)) - return; - - if (p->family == AF_INET6 && !in_addr_equal(AF_INET6, &p->destination, (union in_addr_union*) &LLMNR_MULTICAST_IPV6_ADDRESS)) - return; - } - - r = dns_packet_extract(p); - if (r < 0) { - log_debug_errno(r, "Failed to extract resources from incoming packet: %m"); - return; - } - - if (DNS_PACKET_C(p)) { - /* Somebody notified us about a possible conflict */ - dns_scope_verify_conflicts(s, p); - return; - } - - r = dns_zone_lookup(&s->zone, p->question, &answer, &soa, &tentative); - if (r < 0) { - log_debug_errno(r, "Failed to lookup key: %m"); - return; - } - if (r == 0) - return; - - if (answer) - dns_answer_order_by_scope(answer, in_addr_is_link_local(p->family, &p->sender) > 0); - - r = dns_scope_make_reply_packet(s, DNS_PACKET_ID(p), DNS_RCODE_SUCCESS, p->question, answer, soa, tentative, &reply); - if (r < 0) { - log_debug_errno(r, "Failed to build reply packet: %m"); - return; - } - - if (stream) - r = dns_stream_write_packet(stream, reply); - else { - if (!ratelimit_test(&s->ratelimit)) - return; - - if (p->family == AF_INET) - fd = manager_llmnr_ipv4_udp_fd(s->manager); - else if (p->family == AF_INET6) - fd = manager_llmnr_ipv6_udp_fd(s->manager); - else { - log_debug("Unknown protocol"); - return; - } - if (fd < 0) { - log_debug_errno(fd, "Failed to get reply socket: %m"); - return; - } - - /* Note that we always immediately reply to all LLMNR - * requests, and do not wait any time, since we - * verified uniqueness for all records. Also see RFC - * 4795, Section 2.7 */ - - r = manager_send(s->manager, fd, p->ifindex, p->family, &p->sender, p->sender_port, reply); - } - - if (r < 0) { - log_debug_errno(r, "Failed to send reply packet: %m"); - return; - } -} - -DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsQuestion *question, bool cache_ok) { - DnsTransaction *t; - - assert(scope); - assert(question); - - /* Try to find an ongoing transaction that is a equal or a - * superset of the specified question */ - - LIST_FOREACH(transactions_by_scope, t, scope->transactions) { - - /* Refuse reusing transactions that completed based on - * cached data instead of a real packet, if that's - * requested. */ - if (!cache_ok && - IN_SET(t->state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_FAILURE) && - !t->received) - continue; - - if (dns_question_is_superset(t->question, question) > 0) - return t; - } - - return NULL; -} - -static int dns_scope_make_conflict_packet( - DnsScope *s, - DnsResourceRecord *rr, - DnsPacket **ret) { - - _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; - int r; - - assert(s); - assert(rr); - assert(ret); - - r = dns_packet_new(&p, s->protocol, 0); - if (r < 0) - return r; - - DNS_PACKET_HEADER(p)->flags = htobe16(DNS_PACKET_MAKE_FLAGS( - 0 /* qr */, - 0 /* opcode */, - 1 /* conflict */, - 0 /* tc */, - 0 /* t */, - 0 /* (ra) */, - 0 /* (ad) */, - 0 /* (cd) */, - 0)); - random_bytes(&DNS_PACKET_HEADER(p)->id, sizeof(uint16_t)); - DNS_PACKET_HEADER(p)->qdcount = htobe16(1); - DNS_PACKET_HEADER(p)->arcount = htobe16(1); - - r = dns_packet_append_key(p, rr->key, NULL); - if (r < 0) - return r; - - r = dns_packet_append_rr(p, rr, NULL); - if (r < 0) - return r; - - *ret = p; - p = NULL; - - return 0; -} - -static int on_conflict_dispatch(sd_event_source *es, usec_t usec, void *userdata) { - DnsScope *scope = userdata; - int r; - - assert(es); - assert(scope); - - scope->conflict_event_source = sd_event_source_unref(scope->conflict_event_source); - - for (;;) { - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; - _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; - - rr = ordered_hashmap_steal_first(scope->conflict_queue); - if (!rr) - break; - - r = dns_scope_make_conflict_packet(scope, rr, &p); - if (r < 0) { - log_error_errno(r, "Failed to make conflict packet: %m"); - return 0; - } - - r = dns_scope_emit(scope, p); - if (r < 0) - log_debug_errno(r, "Failed to send conflict packet: %m"); - } - - return 0; -} - -int dns_scope_notify_conflict(DnsScope *scope, DnsResourceRecord *rr) { - usec_t jitter; - int r; - - assert(scope); - assert(rr); - - /* We don't send these queries immediately. Instead, we queue - * them, and send them after some jitter delay. */ - r = ordered_hashmap_ensure_allocated(&scope->conflict_queue, &dns_resource_key_hash_ops); - if (r < 0) { - log_oom(); - return r; - } - - /* We only place one RR per key in the conflict - * messages, not all of them. That should be enough to - * indicate where there might be a conflict */ - r = ordered_hashmap_put(scope->conflict_queue, rr->key, rr); - if (r == -EEXIST || r == 0) - return 0; - if (r < 0) - return log_debug_errno(r, "Failed to queue conflicting RR: %m"); - - dns_resource_record_ref(rr); - - if (scope->conflict_event_source) - return 0; - - random_bytes(&jitter, sizeof(jitter)); - jitter %= LLMNR_JITTER_INTERVAL_USEC; - - r = sd_event_add_time(scope->manager->event, - &scope->conflict_event_source, - clock_boottime_or_monotonic(), - now(clock_boottime_or_monotonic()) + jitter, - LLMNR_JITTER_INTERVAL_USEC, - on_conflict_dispatch, scope); - if (r < 0) - return log_debug_errno(r, "Failed to add conflict dispatch event: %m"); - - return 0; -} - -void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p) { - unsigned i; - int r; - - assert(scope); - assert(p); - - if (p->protocol != DNS_PROTOCOL_LLMNR) - return; - - if (DNS_PACKET_RRCOUNT(p) <= 0) - return; - - if (DNS_PACKET_C(p) != 0) - return; - - if (DNS_PACKET_T(p) != 0) - return; - - if (manager_our_packet(scope->manager, p)) - return; - - r = dns_packet_extract(p); - if (r < 0) { - log_debug_errno(r, "Failed to extract packet: %m"); - return; - } - - log_debug("Checking for conflicts..."); - - for (i = 0; i < p->answer->n_rrs; i++) { - - /* Check for conflicts against the local zone. If we - * found one, we won't check any further */ - r = dns_zone_check_conflicts(&scope->zone, p->answer->rrs[i]); - if (r != 0) - continue; - - /* Check for conflicts against the local cache. If so, - * send out an advisory query, to inform everybody */ - r = dns_cache_check_conflicts(&scope->cache, p->answer->rrs[i], p->family, &p->sender); - if (r <= 0) - continue; - - dns_scope_notify_conflict(scope, p->answer->rrs[i]); - } -} diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h deleted file mode 100644 index cfbde1343f..0000000000 --- a/src/resolve/resolved-dns-scope.h +++ /dev/null @@ -1,85 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include "list.h" - -typedef struct DnsScope DnsScope; - -#include "resolved-dns-server.h" -#include "resolved-dns-packet.h" -#include "resolved-dns-cache.h" -#include "resolved-dns-zone.h" -#include "resolved-link.h" - -typedef enum DnsScopeMatch { - DNS_SCOPE_NO, - DNS_SCOPE_MAYBE, - DNS_SCOPE_YES, - _DNS_SCOPE_MATCH_MAX, - _DNS_SCOPE_INVALID = -1 -} DnsScopeMatch; - -struct DnsScope { - Manager *manager; - - DnsProtocol protocol; - int family; - - Link *link; - - char **domains; - - DnsCache cache; - DnsZone zone; - - OrderedHashmap *conflict_queue; - sd_event_source *conflict_event_source; - - RateLimit ratelimit; - - LIST_HEAD(DnsTransaction, transactions); - - LIST_FIELDS(DnsScope, scopes); -}; - -int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol p, int family); -DnsScope* dns_scope_free(DnsScope *s); - -int dns_scope_emit(DnsScope *s, DnsPacket *p); -int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port); - -DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain); -int dns_scope_good_key(DnsScope *s, DnsResourceKey *key); -int dns_scope_good_dns_server(DnsScope *s, int family, const union in_addr_union *address); - -DnsServer *dns_scope_get_dns_server(DnsScope *s); -void dns_scope_next_dns_server(DnsScope *s); - -int dns_scope_llmnr_membership(DnsScope *s, bool b); - -void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p); - -DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsQuestion *question, bool cache_ok); - -int dns_scope_notify_conflict(DnsScope *scope, DnsResourceRecord *rr); -void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p); diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c deleted file mode 100644 index caf06fe450..0000000000 --- a/src/resolve/resolved-dns-server.c +++ /dev/null @@ -1,127 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include "siphash24.h" - -#include "resolved-dns-server.h" - -int dns_server_new( - Manager *m, - DnsServer **ret, - DnsServerType type, - Link *l, - int family, - const union in_addr_union *in_addr) { - - DnsServer *s, *tail; - - assert(m); - assert((type == DNS_SERVER_LINK) == !!l); - assert(in_addr); - - s = new0(DnsServer, 1); - if (!s) - return -ENOMEM; - - s->type = type; - s->family = family; - s->address = *in_addr; - - if (type == DNS_SERVER_LINK) { - LIST_FIND_TAIL(servers, l->dns_servers, tail); - LIST_INSERT_AFTER(servers, l->dns_servers, tail, s); - s->link = l; - } else if (type == DNS_SERVER_SYSTEM) { - LIST_FIND_TAIL(servers, m->dns_servers, tail); - LIST_INSERT_AFTER(servers, m->dns_servers, tail, s); - } else if (type == DNS_SERVER_FALLBACK) { - LIST_FIND_TAIL(servers, m->fallback_dns_servers, tail); - LIST_INSERT_AFTER(servers, m->fallback_dns_servers, tail, s); - } else - assert_not_reached("Unknown server type"); - - s->manager = m; - - /* A new DNS server that isn't fallback is added and the one - * we used so far was a fallback one? Then let's try to pick - * the new one */ - if (type != DNS_SERVER_FALLBACK && - m->current_dns_server && - m->current_dns_server->type == DNS_SERVER_FALLBACK) - manager_set_dns_server(m, NULL); - - if (ret) - *ret = s; - - return 0; -} - -DnsServer* dns_server_free(DnsServer *s) { - if (!s) - return NULL; - - if (s->manager) { - if (s->type == DNS_SERVER_LINK) - LIST_REMOVE(servers, s->link->dns_servers, s); - else if (s->type == DNS_SERVER_SYSTEM) - LIST_REMOVE(servers, s->manager->dns_servers, s); - else if (s->type == DNS_SERVER_FALLBACK) - LIST_REMOVE(servers, s->manager->fallback_dns_servers, s); - else - assert_not_reached("Unknown server type"); - - if (s->manager->current_dns_server == s) - manager_set_dns_server(s->manager, NULL); - } - - if (s->link && s->link->current_dns_server == s) - link_set_dns_server(s->link, NULL); - - free(s); - - return NULL; -} - -static unsigned long dns_server_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) { - const DnsServer *s = p; - uint64_t u; - - siphash24((uint8_t*) &u, &s->address, FAMILY_ADDRESS_SIZE(s->family), hash_key); - u = u * hash_key[0] + u + s->family; - - return u; -} - -static int dns_server_compare_func(const void *a, const void *b) { - const DnsServer *x = a, *y = b; - - if (x->family < y->family) - return -1; - if (x->family > y->family) - return 1; - - return memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family)); -} - -const struct hash_ops dns_server_hash_ops = { - .hash = dns_server_hash_func, - .compare = dns_server_compare_func -}; diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h deleted file mode 100644 index 70ff35b08f..0000000000 --- a/src/resolve/resolved-dns-server.h +++ /dev/null @@ -1,62 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include "in-addr-util.h" - -typedef struct DnsServer DnsServer; -typedef enum DnsServerSource DnsServerSource; - -typedef enum DnsServerType { - DNS_SERVER_SYSTEM, - DNS_SERVER_FALLBACK, - DNS_SERVER_LINK, -} DnsServerType; - -#include "resolved-link.h" - -struct DnsServer { - Manager *manager; - - DnsServerType type; - - Link *link; - - int family; - union in_addr_union address; - - bool marked:1; - - LIST_FIELDS(DnsServer, servers); -}; - -int dns_server_new( - Manager *m, - DnsServer **s, - DnsServerType type, - Link *l, - int family, - const union in_addr_union *address); - -DnsServer* dns_server_free(DnsServer *s); - -extern const struct hash_ops dns_server_hash_ops; diff --git a/src/resolve/resolved-dns-stream.c b/src/resolve/resolved-dns-stream.c deleted file mode 100644 index 4c0b557bad..0000000000 --- a/src/resolve/resolved-dns-stream.c +++ /dev/null @@ -1,402 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <netinet/tcp.h> - -#include "missing.h" -#include "resolved-dns-stream.h" - -#define DNS_STREAM_TIMEOUT_USEC (10 * USEC_PER_SEC) -#define DNS_STREAMS_MAX 128 - -static void dns_stream_stop(DnsStream *s) { - assert(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); -} - -static int dns_stream_update_io(DnsStream *s) { - int f = 0; - - assert(s); - - if (s->write_packet && s->n_written < sizeof(s->write_size) + s->write_packet->size) - f |= EPOLLOUT; - if (!s->read_packet || s->n_read < sizeof(s->read_size) + s->read_packet->size) - f |= EPOLLIN; - - return sd_event_source_set_io_events(s->io_event_source, f); -} - -static int dns_stream_complete(DnsStream *s, int error) { - assert(s); - - dns_stream_stop(s); - - if (s->complete) - s->complete(s, error); - else - dns_stream_free(s); - - return 0; -} - -static int dns_stream_identify(DnsStream *s) { - union { - struct cmsghdr header; /* For alignment */ - uint8_t buffer[CMSG_SPACE(MAXSIZE(struct in_pktinfo, struct in6_pktinfo)) - + EXTRA_CMSG_SPACE /* kernel appears to require extra space */]; - } control; - struct msghdr mh = {}; - struct cmsghdr *cmsg; - socklen_t sl; - int r; - - assert(s); - - if (s->identified) - return 0; - - /* Query the local side */ - s->local_salen = sizeof(s->local); - r = getsockname(s->fd, &s->local.sa, &s->local_salen); - if (r < 0) - return -errno; - if (s->local.sa.sa_family == AF_INET6 && s->ifindex <= 0) - s->ifindex = s->local.in6.sin6_scope_id; - - /* Query the remote side */ - s->peer_salen = sizeof(s->peer); - r = getpeername(s->fd, &s->peer.sa, &s->peer_salen); - if (r < 0) - return -errno; - if (s->peer.sa.sa_family == AF_INET6 && s->ifindex <= 0) - s->ifindex = s->peer.in6.sin6_scope_id; - - /* Check consistency */ - assert(s->peer.sa.sa_family == s->local.sa.sa_family); - assert(IN_SET(s->peer.sa.sa_family, AF_INET, AF_INET6)); - - /* Query connection meta information */ - sl = sizeof(control); - if (s->peer.sa.sa_family == AF_INET) { - r = getsockopt(s->fd, IPPROTO_IP, IP_PKTOPTIONS, &control, &sl); - if (r < 0) - return -errno; - } else if (s->peer.sa.sa_family == AF_INET6) { - - r = getsockopt(s->fd, IPPROTO_IPV6, IPV6_2292PKTOPTIONS, &control, &sl); - if (r < 0) - return -errno; - } else - return -EAFNOSUPPORT; - - mh.msg_control = &control; - mh.msg_controllen = sl; - for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg)) { - - if (cmsg->cmsg_level == IPPROTO_IPV6) { - assert(s->peer.sa.sa_family == AF_INET6); - - switch (cmsg->cmsg_type) { - - case IPV6_PKTINFO: { - struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg); - - if (s->ifindex <= 0) - s->ifindex = i->ipi6_ifindex; - break; - } - - case IPV6_HOPLIMIT: - s->ttl = *(int *) CMSG_DATA(cmsg); - break; - } - - } else if (cmsg->cmsg_level == IPPROTO_IP) { - assert(s->peer.sa.sa_family == AF_INET); - - switch (cmsg->cmsg_type) { - - case IP_PKTINFO: { - struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg); - - if (s->ifindex <= 0) - s->ifindex = i->ipi_ifindex; - break; - } - - case IP_TTL: - s->ttl = *(int *) CMSG_DATA(cmsg); - break; - } - } - } - - /* The Linux kernel sets the interface index to the loopback - * device if the connection came from the local host since it - * avoids the routing table in such a case. Let's unset the - * interface index in such a case. */ - if (s->ifindex == LOOPBACK_IFINDEX) - s->ifindex = 0; - - /* If we don't know the interface index still, we look for the - * first local interface with a matching address. Yuck! */ - if (s->ifindex <= 0) - s->ifindex = manager_find_ifindex(s->manager, s->local.sa.sa_family, s->local.sa.sa_family == AF_INET ? (union in_addr_union*) &s->local.in.sin_addr : (union in_addr_union*) &s->local.in6.sin6_addr); - - if (s->protocol == DNS_PROTOCOL_LLMNR && s->ifindex > 0) { - uint32_t ifindex = htobe32(s->ifindex); - - /* Make sure all packets for this connection are sent on the same interface */ - if (s->local.sa.sa_family == AF_INET) { - r = setsockopt(s->fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex, sizeof(ifindex)); - if (r < 0) - log_debug_errno(errno, "Failed to invoke IP_UNICAST_IF: %m"); - } else if (s->local.sa.sa_family == AF_INET6) { - r = setsockopt(s->fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex, sizeof(ifindex)); - if (r < 0) - log_debug_errno(errno, "Failed to invoke IPV6_UNICAST_IF: %m"); - } - } - - s->identified = true; - - return 0; -} - -static int on_stream_timeout(sd_event_source *es, usec_t usec, void *userdata) { - DnsStream *s = userdata; - - assert(s); - - return dns_stream_complete(s, ETIMEDOUT); -} - -static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *userdata) { - DnsStream *s = userdata; - int r; - - assert(s); - - r = dns_stream_identify(s); - if (r < 0) - return dns_stream_complete(s, -r); - - if ((revents & EPOLLOUT) && - s->write_packet && - s->n_written < sizeof(s->write_size) + s->write_packet->size) { - - struct iovec iov[2]; - ssize_t ss; - - iov[0].iov_base = &s->write_size; - iov[0].iov_len = sizeof(s->write_size); - iov[1].iov_base = DNS_PACKET_DATA(s->write_packet); - iov[1].iov_len = s->write_packet->size; - - IOVEC_INCREMENT(iov, 2, s->n_written); - - ss = writev(fd, iov, 2); - if (ss < 0) { - if (errno != EINTR && errno != EAGAIN) - return dns_stream_complete(s, errno); - } else - s->n_written += ss; - - /* Are we done? If so, disable the event source for EPOLLOUT */ - if (s->n_written >= sizeof(s->write_size) + s->write_packet->size) { - r = dns_stream_update_io(s); - if (r < 0) - return dns_stream_complete(s, -r); - } - } - - if ((revents & (EPOLLIN|EPOLLHUP|EPOLLRDHUP)) && - (!s->read_packet || - s->n_read < sizeof(s->read_size) + s->read_packet->size)) { - - if (s->n_read < sizeof(s->read_size)) { - ssize_t ss; - - ss = read(fd, (uint8_t*) &s->read_size + s->n_read, sizeof(s->read_size) - s->n_read); - if (ss < 0) { - if (errno != EINTR && errno != EAGAIN) - return dns_stream_complete(s, errno); - } else if (ss == 0) - return dns_stream_complete(s, ECONNRESET); - else - s->n_read += ss; - } - - if (s->n_read >= sizeof(s->read_size)) { - - if (be16toh(s->read_size) < DNS_PACKET_HEADER_SIZE) - return dns_stream_complete(s, EBADMSG); - - if (s->n_read < sizeof(s->read_size) + be16toh(s->read_size)) { - ssize_t ss; - - if (!s->read_packet) { - r = dns_packet_new(&s->read_packet, s->protocol, be16toh(s->read_size)); - if (r < 0) - return dns_stream_complete(s, -r); - - s->read_packet->size = be16toh(s->read_size); - s->read_packet->ipproto = IPPROTO_TCP; - s->read_packet->family = s->peer.sa.sa_family; - s->read_packet->ttl = s->ttl; - s->read_packet->ifindex = s->ifindex; - - if (s->read_packet->family == AF_INET) { - s->read_packet->sender.in = s->peer.in.sin_addr; - s->read_packet->sender_port = be16toh(s->peer.in.sin_port); - s->read_packet->destination.in = s->local.in.sin_addr; - s->read_packet->destination_port = be16toh(s->local.in.sin_port); - } else { - assert(s->read_packet->family == AF_INET6); - s->read_packet->sender.in6 = s->peer.in6.sin6_addr; - s->read_packet->sender_port = be16toh(s->peer.in6.sin6_port); - s->read_packet->destination.in6 = s->local.in6.sin6_addr; - s->read_packet->destination_port = be16toh(s->local.in6.sin6_port); - - if (s->read_packet->ifindex == 0) - s->read_packet->ifindex = s->peer.in6.sin6_scope_id; - if (s->read_packet->ifindex == 0) - s->read_packet->ifindex = s->local.in6.sin6_scope_id; - } - } - - ss = read(fd, - (uint8_t*) DNS_PACKET_DATA(s->read_packet) + s->n_read - sizeof(s->read_size), - sizeof(s->read_size) + be16toh(s->read_size) - s->n_read); - if (ss < 0) { - if (errno != EINTR && errno != EAGAIN) - return dns_stream_complete(s, errno); - } else if (ss == 0) - return dns_stream_complete(s, ECONNRESET); - else - s->n_read += ss; - } - - /* Are we done? If so, disable the event source for EPOLLIN */ - if (s->n_read >= sizeof(s->read_size) + be16toh(s->read_size)) { - r = dns_stream_update_io(s); - if (r < 0) - return dns_stream_complete(s, -r); - - /* If there's a packet handler - * installed, call that. Note that - * this is optional... */ - if (s->on_packet) - return s->on_packet(s); - } - } - } - - if ((s->write_packet && s->n_written >= sizeof(s->write_size) + s->write_packet->size) && - (s->read_packet && s->n_read >= sizeof(s->read_size) + s->read_packet->size)) - return dns_stream_complete(s, 0); - - return 0; -} - -DnsStream *dns_stream_free(DnsStream *s) { - if (!s) - return NULL; - - dns_stream_stop(s); - - if (s->manager) { - LIST_REMOVE(streams, s->manager->dns_streams, s); - s->manager->n_dns_streams--; - } - - dns_packet_unref(s->write_packet); - dns_packet_unref(s->read_packet); - - free(s); - - return 0; -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(DnsStream*, dns_stream_free); - -int dns_stream_new(Manager *m, DnsStream **ret, DnsProtocol protocol, int fd) { - static const int one = 1; - _cleanup_(dns_stream_freep) DnsStream *s = NULL; - int r; - - assert(m); - assert(fd >= 0); - - if (m->n_dns_streams > DNS_STREAMS_MAX) - return -EBUSY; - - s = new0(DnsStream, 1); - if (!s) - return -ENOMEM; - - s->fd = -1; - s->protocol = protocol; - - r = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)); - if (r < 0) - return -errno; - - r = sd_event_add_io(m->event, &s->io_event_source, fd, EPOLLIN, on_stream_io, s); - if (r < 0) - return r; - - r = sd_event_add_time( - m->event, - &s->timeout_event_source, - clock_boottime_or_monotonic(), - now(clock_boottime_or_monotonic()) + DNS_STREAM_TIMEOUT_USEC, 0, - on_stream_timeout, s); - if (r < 0) - return r; - - LIST_PREPEND(streams, m->dns_streams, s); - s->manager = m; - s->fd = fd; - m->n_dns_streams++; - - *ret = s; - s = NULL; - - return 0; -} - -int dns_stream_write_packet(DnsStream *s, DnsPacket *p) { - assert(s); - - if (s->write_packet) - return -EBUSY; - - s->write_packet = dns_packet_ref(p); - s->write_size = htobe16(p->size); - s->n_written = 0; - - return dns_stream_update_io(s); -} diff --git a/src/resolve/resolved-dns-stream.h b/src/resolve/resolved-dns-stream.h deleted file mode 100644 index fb81e9f1ac..0000000000 --- a/src/resolve/resolved-dns-stream.h +++ /dev/null @@ -1,63 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include "socket-util.h" - -typedef struct DnsStream DnsStream; - -#include "resolved-dns-packet.h" -#include "resolved-dns-transaction.h" - -struct DnsStream { - Manager *manager; - - DnsProtocol protocol; - - int fd; - union sockaddr_union peer; - socklen_t peer_salen; - union sockaddr_union local; - socklen_t local_salen; - int ifindex; - uint32_t ttl; - bool identified; - - sd_event_source *io_event_source; - sd_event_source *timeout_event_source; - - be16_t write_size, read_size; - DnsPacket *write_packet, *read_packet; - size_t n_written, n_read; - - int (*on_packet)(DnsStream *s); - int (*complete)(DnsStream *s, int error); - - DnsTransaction *transaction; - - LIST_FIELDS(DnsStream, streams); -}; - -int dns_stream_new(Manager *m, DnsStream **s, DnsProtocol protocol, int fd); -DnsStream *dns_stream_free(DnsStream *s); - -int dns_stream_write_packet(DnsStream *s, DnsPacket *p); diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c deleted file mode 100644 index bc1a90db1b..0000000000 --- a/src/resolve/resolved-dns-transaction.c +++ /dev/null @@ -1,619 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include "af-list.h" - -#include "resolved-dns-transaction.h" - -DnsTransaction* dns_transaction_free(DnsTransaction *t) { - DnsQuery *q; - DnsZoneItem *i; - - if (!t) - return NULL; - - sd_event_source_unref(t->timeout_event_source); - - dns_question_unref(t->question); - dns_packet_unref(t->sent); - dns_packet_unref(t->received); - dns_answer_unref(t->cached); - - dns_stream_free(t->stream); - - if (t->scope) { - LIST_REMOVE(transactions_by_scope, t->scope->transactions, t); - - if (t->id != 0) - hashmap_remove(t->scope->manager->dns_transactions, UINT_TO_PTR(t->id)); - } - - while ((q = set_steal_first(t->queries))) - set_remove(q->transactions, t); - set_free(t->queries); - - while ((i = set_steal_first(t->zone_items))) - i->probe_transaction = NULL; - set_free(t->zone_items); - - free(t); - return NULL; -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(DnsTransaction*, dns_transaction_free); - -void dns_transaction_gc(DnsTransaction *t) { - assert(t); - - if (t->block_gc > 0) - return; - - if (set_isempty(t->queries) && set_isempty(t->zone_items)) - dns_transaction_free(t); -} - -int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsQuestion *q) { - _cleanup_(dns_transaction_freep) DnsTransaction *t = NULL; - int r; - - assert(ret); - assert(s); - assert(q); - - r = hashmap_ensure_allocated(&s->manager->dns_transactions, NULL); - if (r < 0) - return r; - - t = new0(DnsTransaction, 1); - if (!t) - return -ENOMEM; - - t->question = dns_question_ref(q); - - do - random_bytes(&t->id, sizeof(t->id)); - while (t->id == 0 || - hashmap_get(s->manager->dns_transactions, UINT_TO_PTR(t->id))); - - r = hashmap_put(s->manager->dns_transactions, UINT_TO_PTR(t->id), t); - if (r < 0) { - t->id = 0; - return r; - } - - LIST_PREPEND(transactions_by_scope, s->transactions, t); - t->scope = s; - - if (ret) - *ret = t; - - t = NULL; - - return 0; -} - -static void dns_transaction_stop(DnsTransaction *t) { - assert(t); - - t->timeout_event_source = sd_event_source_unref(t->timeout_event_source); - t->stream = dns_stream_free(t->stream); -} - -static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) { - _cleanup_free_ char *pretty = NULL; - DnsZoneItem *z; - - assert(t); - assert(p); - - if (manager_our_packet(t->scope->manager, p) != 0) - return; - - in_addr_to_string(p->family, &p->sender, &pretty); - - log_debug("Transaction on scope %s on %s/%s got tentative packet from %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), - pretty); - - /* RFC 4795, Section 4.1 says that the peer with the - * lexicographically smaller IP address loses */ - if (memcmp(&p->sender, &p->destination, FAMILY_ADDRESS_SIZE(p->family)) >= 0) { - log_debug("Peer has lexicographically larger IP address and thus lost in the conflict."); - return; - } - - log_debug("We have the lexicographically larger IP address and thus lost in the conflict."); - - t->block_gc++; - while ((z = set_first(t->zone_items))) { - /* First, make sure the zone item drops the reference - * to us */ - dns_zone_item_probe_stop(z); - - /* Secondly, report this as conflict, so that we might - * look for a different hostname */ - dns_zone_item_conflict(z); - } - t->block_gc--; - - dns_transaction_gc(t); -} - -void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) { - DnsQuery *q; - DnsZoneItem *z; - Iterator i; - - assert(t); - assert(!IN_SET(state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING)); - - if (!IN_SET(t->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING)) - return; - - /* Note that this call might invalidate the query. Callers - * should hence not attempt to access the query or transaction - * after calling this function. */ - - log_debug("Transaction on scope %s on %s/%s now complete with <%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), - dns_transaction_state_to_string(state)); - - t->state = state; - - dns_transaction_stop(t); - - /* Notify all queries that are interested, but make sure the - * transaction isn't freed while we are still looking at it */ - t->block_gc++; - SET_FOREACH(q, t->queries, i) - dns_query_ready(q); - SET_FOREACH(z, t->zone_items, i) - dns_zone_item_ready(z); - t->block_gc--; - - dns_transaction_gc(t); -} - -static int on_stream_complete(DnsStream *s, int error) { - _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; - DnsTransaction *t; - - assert(s); - assert(s->transaction); - - /* Copy the data we care about out of the stream before we - * destroy it. */ - t = s->transaction; - p = dns_packet_ref(s->read_packet); - - t->stream = dns_stream_free(t->stream); - - if (error != 0) { - dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES); - return 0; - } - - if (dns_packet_validate_reply(p) <= 0) { - log_debug("Invalid LLMNR TCP packet."); - dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY); - return 0; - } - - dns_scope_check_conflicts(t->scope, p); - - t->block_gc++; - dns_transaction_process_reply(t, p); - t->block_gc--; - - /* If the response wasn't useful, then complete the transition now */ - if (t->state == DNS_TRANSACTION_PENDING) - dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY); - - return 0; -} - -static int dns_transaction_open_tcp(DnsTransaction *t) { - _cleanup_close_ int fd = -1; - int r; - - assert(t); - - if (t->stream) - return 0; - - if (t->scope->protocol == DNS_PROTOCOL_DNS) - fd = dns_scope_tcp_socket(t->scope, AF_UNSPEC, NULL, 53); - else if (t->scope->protocol == DNS_PROTOCOL_LLMNR) { - - /* When we already received a query to this (but it was truncated), send to its sender address */ - if (t->received) - fd = dns_scope_tcp_socket(t->scope, t->received->family, &t->received->sender, t->received->sender_port); - else { - union in_addr_union address; - int family = AF_UNSPEC; - - /* Otherwise, try to talk to the owner of a - * the IP address, in case this is a reverse - * PTR lookup */ - r = dns_question_extract_reverse_address(t->question, &family, &address); - if (r < 0) - return r; - if (r == 0) - return -EINVAL; - - fd = dns_scope_tcp_socket(t->scope, family, &address, 5355); - } - } else - return -EAFNOSUPPORT; - - if (fd < 0) - return fd; - - r = dns_stream_new(t->scope->manager, &t->stream, t->scope->protocol, fd); - if (r < 0) - return r; - - fd = -1; - - r = dns_stream_write_packet(t->stream, t->sent); - if (r < 0) { - t->stream = dns_stream_free(t->stream); - return r; - } - - t->received = dns_packet_unref(t->received); - t->stream->complete = on_stream_complete; - t->stream->transaction = t; - - /* The interface index is difficult to determine if we are - * connecting to the local host, hence fill this in right away - * instead of determining it from the socket */ - if (t->scope->link) - t->stream->ifindex = t->scope->link->ifindex; - - return 0; -} - -void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { - int r; - - assert(t); - assert(p); - assert(t->state == DNS_TRANSACTION_PENDING); - - /* Note that this call might invalidate the query. Callers - * should hence not attempt to access the query or transaction - * after calling this function. */ - - if (t->scope->protocol == DNS_PROTOCOL_LLMNR) { - assert(t->scope->link); - - /* For LLMNR we will not accept any packets from other - * interfaces */ - - if (p->ifindex != t->scope->link->ifindex) - return; - - if (p->family != t->scope->family) - return; - - /* Tentative packets are not full responses but still - * useful for identifying uniqueness conflicts during - * probing. */ - if (DNS_PACKET_T(p)) { - dns_transaction_tentative(t, p); - return; - } - } - - if (t->scope->protocol == DNS_PROTOCOL_DNS) { - - /* For DNS we are fine with accepting packets on any - * interface, but the source IP address must be one of - * a valid DNS server */ - - if (!dns_scope_good_dns_server(t->scope, p->family, &p->sender)) - return; - - if (p->sender_port != 53) - return; - } - - if (t->received != p) { - dns_packet_unref(t->received); - t->received = dns_packet_ref(p); - } - - if (p->ipproto == IPPROTO_TCP) { - if (DNS_PACKET_TC(p)) { - /* Truncated via TCP? Somebody must be fucking with us */ - dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY); - return; - } - - if (DNS_PACKET_ID(p) != t->id) { - /* Not the reply to our query? Somebody must be fucking with us */ - dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY); - return; - } - } - - if (DNS_PACKET_TC(p)) { - /* Response was truncated, let's try again with good old TCP */ - r = dns_transaction_open_tcp(t); - if (r == -ESRCH) { - /* No servers found? Damn! */ - dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS); - return; - } - if (r < 0) { - /* On LLMNR, if we cannot connect to the host, - * we immediately give up */ - if (t->scope->protocol == DNS_PROTOCOL_LLMNR) { - dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES); - return; - } - - /* On DNS, couldn't send? Try immediately again, with a new server */ - dns_scope_next_dns_server(t->scope); - - r = dns_transaction_go(t); - if (r < 0) { - dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES); - return; - } - - return; - } - } - - /* Parse and update the cache */ - r = dns_packet_extract(p); - if (r < 0) { - dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY); - return; - } - - /* According to RFC 4795, section 2.9. only the RRs from the answer section shall be cached */ - dns_cache_put(&t->scope->cache, p->question, DNS_PACKET_RCODE(p), p->answer, DNS_PACKET_ANCOUNT(p), 0, p->family, &p->sender); - - if (DNS_PACKET_RCODE(p) == DNS_RCODE_SUCCESS) - dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS); - else - dns_transaction_complete(t, DNS_TRANSACTION_FAILURE); -} - -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_scope_next_dns_server(t->scope); - - 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; - unsigned n, added = 0; - int r; - - assert(t); - - if (t->sent) - return 0; - - r = dns_packet_new_query(&p, t->scope->protocol, 0); - if (r < 0) - return r; - - for (n = 0; n < t->question->n_keys; n++) { - r = dns_scope_good_key(t->scope, t->question->keys[n]); - if (r < 0) - return r; - if (r == 0) - continue; - - r = dns_packet_append_key(p, t->question->keys[n], NULL); - if (r < 0) - return r; - - added++; - } - - if (added <= 0) - return -EDOM; - - DNS_PACKET_HEADER(p)->qdcount = htobe16(added); - DNS_PACKET_HEADER(p)->id = t->id; - - t->sent = p; - p = NULL; - - return 0; -} - -int dns_transaction_go(DnsTransaction *t) { - bool had_stream; - 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; - } - - t->n_attempts++; - t->received = dns_packet_unref(t->received); - t->cached = dns_answer_unref(t->cached); - t->cached_rcode = 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->question, &t->cached_rcode, &t->cached); - if (r < 0) - return r; - if (r > 0) { - log_debug("Cache hit!"); - if (t->cached_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(), - now(clock_boottime_or_monotonic()) + 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; - } - - log_debug("Cache miss!"); - - /* 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_question_endswith(t->question, "in-addr.arpa") > 0 || - dns_question_endswith(t->question, "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_scope_emit(t->scope, t->sent); - 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; - } - 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_scope_next_dns_server(t->scope); - - return dns_transaction_go(t); - } - - r = sd_event_add_time( - t->scope->manager->event, - &t->timeout_event_source, - clock_boottime_or_monotonic(), - now(clock_boottime_or_monotonic()) + TRANSACTION_TIMEOUT_USEC(t->scope->protocol), 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); diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h deleted file mode 100644 index f6d539d315..0000000000 --- a/src/resolve/resolved-dns-transaction.h +++ /dev/null @@ -1,108 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -typedef struct DnsTransaction DnsTransaction; -typedef enum DnsTransactionState DnsTransactionState; - -enum DnsTransactionState { - DNS_TRANSACTION_NULL, - DNS_TRANSACTION_PENDING, - DNS_TRANSACTION_FAILURE, - DNS_TRANSACTION_SUCCESS, - DNS_TRANSACTION_NO_SERVERS, - DNS_TRANSACTION_TIMEOUT, - DNS_TRANSACTION_ATTEMPTS_MAX_REACHED, - DNS_TRANSACTION_INVALID_REPLY, - DNS_TRANSACTION_RESOURCES, - DNS_TRANSACTION_ABORTED, - _DNS_TRANSACTION_STATE_MAX, - _DNS_TRANSACTION_STATE_INVALID = -1 -}; - -#include "resolved-dns-scope.h" -#include "resolved-dns-packet.h" -#include "resolved-dns-question.h" -#include "resolved-dns-answer.h" - -struct DnsTransaction { - DnsScope *scope; - - DnsQuestion *question; - - DnsTransactionState state; - uint16_t id; - - bool initial_jitter; - - DnsPacket *sent, *received; - DnsAnswer *cached; - int cached_rcode; - - sd_event_source *timeout_event_source; - unsigned n_attempts; - - /* TCP connection logic, if we need it */ - DnsStream *stream; - - /* Queries this transaction is referenced by and that shall be - * notified about this specific transaction completing. */ - Set *queries; - - /* Zone items this transaction is referenced by and that shall - * be notified about completion. */ - Set *zone_items; - - unsigned block_gc; - - LIST_FIELDS(DnsTransaction, transactions_by_scope); -}; - -int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsQuestion *q); -DnsTransaction* dns_transaction_free(DnsTransaction *t); - -void dns_transaction_gc(DnsTransaction *t); -int dns_transaction_go(DnsTransaction *t); - -void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p); -void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state); - -const char* dns_transaction_state_to_string(DnsTransactionState p) _const_; -DnsTransactionState dns_transaction_state_from_string(const char *s) _pure_; - -/* After how much time to repeat classic DNS requests */ -#define DNS_TRANSACTION_TIMEOUT_USEC (5 * USEC_PER_SEC) - -/* After how much time to repeat LLMNR requests, see RFC 4795 Section 7 */ -#define LLMNR_TRANSACTION_TIMEOUT_USEC (1 * USEC_PER_SEC) - -/* LLMNR Jitter interval, see RFC 4795 Section 7 */ -#define LLMNR_JITTER_INTERVAL_USEC (100 * USEC_PER_MSEC) - -/* Maximum attempts to send DNS requests, across all DNS servers */ -#define DNS_TRANSACTION_ATTEMPTS_MAX 8 - -/* Maximum attempts to send LLMNR requests, see RFC 4795 Section 2.7 */ -#define LLMNR_TRANSACTION_ATTEMPTS_MAX 3 - -#define TRANSACTION_TIMEOUT_USEC(p) (p == DNS_PROTOCOL_LLMNR ? LLMNR_TRANSACTION_TIMEOUT_USEC : DNS_TRANSACTION_TIMEOUT_USEC) -#define TRANSACTION_ATTEMPTS_MAX(p) (p == DNS_PROTOCOL_LLMNR ? LLMNR_TRANSACTION_ATTEMPTS_MAX : DNS_TRANSACTION_ATTEMPTS_MAX) diff --git a/src/resolve/resolved-dns-zone.c b/src/resolve/resolved-dns-zone.c deleted file mode 100644 index a4c9b7d7af..0000000000 --- a/src/resolve/resolved-dns-zone.c +++ /dev/null @@ -1,648 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include "list.h" - -#include "resolved-dns-zone.h" -#include "resolved-dns-domain.h" -#include "resolved-dns-packet.h" - -/* Never allow more than 1K entries */ -#define ZONE_MAX 1024 - -void dns_zone_item_probe_stop(DnsZoneItem *i) { - DnsTransaction *t; - assert(i); - - if (!i->probe_transaction) - return; - - t = i->probe_transaction; - i->probe_transaction = NULL; - - set_remove(t->zone_items, i); - dns_transaction_gc(t); -} - -static void dns_zone_item_free(DnsZoneItem *i) { - if (!i) - return; - - dns_zone_item_probe_stop(i); - dns_resource_record_unref(i->rr); - - free(i); -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(DnsZoneItem*, dns_zone_item_free); - -static void dns_zone_item_remove_and_free(DnsZone *z, DnsZoneItem *i) { - DnsZoneItem *first; - - assert(z); - - if (!i) - return; - - first = hashmap_get(z->by_key, i->rr->key); - LIST_REMOVE(by_key, first, i); - if (first) - assert_se(hashmap_replace(z->by_key, first->rr->key, first) >= 0); - else - hashmap_remove(z->by_key, i->rr->key); - - first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(i->rr->key)); - LIST_REMOVE(by_name, first, i); - if (first) - assert_se(hashmap_replace(z->by_name, DNS_RESOURCE_KEY_NAME(first->rr->key), first) >= 0); - else - hashmap_remove(z->by_name, DNS_RESOURCE_KEY_NAME(i->rr->key)); - - dns_zone_item_free(i); -} - -void dns_zone_flush(DnsZone *z) { - DnsZoneItem *i; - - assert(z); - - while ((i = hashmap_first(z->by_key))) - dns_zone_item_remove_and_free(z, i); - - assert(hashmap_size(z->by_key) == 0); - assert(hashmap_size(z->by_name) == 0); - - hashmap_free(z->by_key); - z->by_key = NULL; - - hashmap_free(z->by_name); - z->by_name = NULL; -} - -static DnsZoneItem* dns_zone_get(DnsZone *z, DnsResourceRecord *rr) { - DnsZoneItem *i; - - assert(z); - assert(rr); - - LIST_FOREACH(by_key, i, hashmap_get(z->by_key, rr->key)) - if (dns_resource_record_equal(i->rr, rr) > 0) - return i; - - return NULL; -} - -void dns_zone_remove_rr(DnsZone *z, DnsResourceRecord *rr) { - DnsZoneItem *i; - - assert(z); - assert(rr); - - i = dns_zone_get(z, rr); - if (i) - dns_zone_item_remove_and_free(z, i); -} - -static int dns_zone_init(DnsZone *z) { - int r; - - assert(z); - - r = hashmap_ensure_allocated(&z->by_key, &dns_resource_key_hash_ops); - if (r < 0) - return r; - - r = hashmap_ensure_allocated(&z->by_name, &dns_name_hash_ops); - if (r < 0) - return r; - - return 0; -} - -static int dns_zone_link_item(DnsZone *z, DnsZoneItem *i) { - DnsZoneItem *first; - int r; - - first = hashmap_get(z->by_key, i->rr->key); - if (first) { - LIST_PREPEND(by_key, first, i); - assert_se(hashmap_replace(z->by_key, first->rr->key, first) >= 0); - } else { - r = hashmap_put(z->by_key, i->rr->key, i); - if (r < 0) - return r; - } - - first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(i->rr->key)); - if (first) { - LIST_PREPEND(by_name, first, i); - assert_se(hashmap_replace(z->by_name, DNS_RESOURCE_KEY_NAME(first->rr->key), first) >= 0); - } else { - r = hashmap_put(z->by_name, DNS_RESOURCE_KEY_NAME(i->rr->key), i); - if (r < 0) - return r; - } - - return 0; -} - -static int dns_zone_item_probe_start(DnsZoneItem *i) { - _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; - _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; - DnsTransaction *t; - int r; - - assert(i); - - if (i->probe_transaction) - return 0; - - key = dns_resource_key_new(i->rr->key->class, DNS_TYPE_ANY, DNS_RESOURCE_KEY_NAME(i->rr->key)); - if (!key) - return -ENOMEM; - - question = dns_question_new(1); - if (!question) - return -ENOMEM; - - r = dns_question_add(question, key); - if (r < 0) - return r; - - t = dns_scope_find_transaction(i->scope, question, false); - if (!t) { - r = dns_transaction_new(&t, i->scope, question); - if (r < 0) - return r; - } - - r = set_ensure_allocated(&t->zone_items, NULL); - if (r < 0) - goto gc; - - r = set_put(t->zone_items, i); - if (r < 0) - goto gc; - - i->probe_transaction = t; - - if (t->state == DNS_TRANSACTION_NULL) { - - i->block_ready++; - r = dns_transaction_go(t); - i->block_ready--; - - if (r < 0) { - dns_zone_item_probe_stop(i); - return r; - } - } - - dns_zone_item_ready(i); - - return 0; - -gc: - dns_transaction_gc(t); - return r; -} - -int dns_zone_put(DnsZone *z, DnsScope *s, DnsResourceRecord *rr, bool probe) { - _cleanup_(dns_zone_item_freep) DnsZoneItem *i = NULL; - DnsZoneItem *existing; - int r; - - assert(z); - assert(s); - assert(rr); - - if (rr->key->class == DNS_CLASS_ANY) - return -EINVAL; - if (rr->key->type == DNS_TYPE_ANY) - return -EINVAL; - - existing = dns_zone_get(z, rr); - if (existing) - return 0; - - r = dns_zone_init(z); - if (r < 0) - return r; - - i = new0(DnsZoneItem, 1); - if (!i) - return -ENOMEM; - - i->scope = s; - i->rr = dns_resource_record_ref(rr); - i->probing_enabled = probe; - - r = dns_zone_link_item(z, i); - if (r < 0) - return r; - - if (probe) { - DnsZoneItem *first, *j; - bool established = false; - - /* Check if there's already an RR with the same name - * established. If so, it has been probed already, and - * we don't ned to probe again. */ - - LIST_FIND_HEAD(by_name, i, first); - LIST_FOREACH(by_name, j, first) { - if (i == j) - continue; - - if (j->state == DNS_ZONE_ITEM_ESTABLISHED) - established = true; - } - - if (established) - i->state = DNS_ZONE_ITEM_ESTABLISHED; - else { - i->state = DNS_ZONE_ITEM_PROBING; - - r = dns_zone_item_probe_start(i); - if (r < 0) { - dns_zone_item_remove_and_free(z, i); - i = NULL; - return r; - } - } - } else - i->state = DNS_ZONE_ITEM_ESTABLISHED; - - i = NULL; - return 0; -} - -int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **ret_answer, DnsAnswer **ret_soa, bool *ret_tentative) { - _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL; - unsigned i, n_answer = 0, n_soa = 0; - bool tentative = true; - int r; - - assert(z); - assert(q); - assert(ret_answer); - assert(ret_soa); - - if (q->n_keys <= 0) { - *ret_answer = NULL; - *ret_soa = NULL; - - if (ret_tentative) - *ret_tentative = false; - - return 0; - } - - /* First iteration, count what we have */ - for (i = 0; i < q->n_keys; i++) { - DnsZoneItem *j, *first; - - if (q->keys[i]->type == DNS_TYPE_ANY || - q->keys[i]->class == DNS_CLASS_ANY) { - bool found = false, added = false; - int k; - - /* If this is a generic match, then we have to - * go through the list by the name and look - * for everything manually */ - - first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i])); - LIST_FOREACH(by_name, j, first) { - if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) - continue; - - found = true; - - k = dns_resource_key_match_rr(q->keys[i], j->rr); - if (k < 0) - return k; - if (k > 0) { - n_answer++; - added = true; - } - - } - - if (found && !added) - n_soa++; - - } else { - bool found = false; - - /* If this is a specific match, then look for - * the right key immediately */ - - first = hashmap_get(z->by_key, q->keys[i]); - LIST_FOREACH(by_key, j, first) { - if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) - continue; - - found = true; - n_answer++; - } - - if (!found) { - first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i])); - LIST_FOREACH(by_name, j, first) { - if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) - continue; - - n_soa++; - break; - } - } - } - } - - if (n_answer <= 0 && n_soa <= 0) { - *ret_answer = NULL; - *ret_soa = NULL; - - if (ret_tentative) - *ret_tentative = false; - - return 0; - } - - if (n_answer > 0) { - answer = dns_answer_new(n_answer); - if (!answer) - return -ENOMEM; - } - - if (n_soa > 0) { - soa = dns_answer_new(n_soa); - if (!soa) - return -ENOMEM; - } - - /* Second iteration, actually add the RRs to the answers */ - for (i = 0; i < q->n_keys; i++) { - DnsZoneItem *j, *first; - - if (q->keys[i]->type == DNS_TYPE_ANY || - q->keys[i]->class == DNS_CLASS_ANY) { - bool found = false, added = false; - int k; - - first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i])); - LIST_FOREACH(by_name, j, first) { - if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) - continue; - - found = true; - - if (j->state != DNS_ZONE_ITEM_PROBING) - tentative = false; - - k = dns_resource_key_match_rr(q->keys[i], j->rr); - if (k < 0) - return k; - if (k > 0) { - r = dns_answer_add(answer, j->rr); - if (r < 0) - return r; - - added = true; - } - } - - if (found && !added) { - r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(q->keys[i]), LLMNR_DEFAULT_TTL); - if (r < 0) - return r; - } - } else { - bool found = false; - - first = hashmap_get(z->by_key, q->keys[i]); - LIST_FOREACH(by_key, j, first) { - if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) - continue; - - found = true; - - if (j->state != DNS_ZONE_ITEM_PROBING) - tentative = false; - - r = dns_answer_add(answer, j->rr); - if (r < 0) - return r; - } - - if (!found) { - bool add_soa = false; - - first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i])); - LIST_FOREACH(by_name, j, first) { - if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) - continue; - - if (j->state != DNS_ZONE_ITEM_PROBING) - tentative = false; - - add_soa = true; - } - - if (add_soa) { - r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(q->keys[i]), LLMNR_DEFAULT_TTL); - if (r < 0) - return r; - } - } - } - } - - *ret_answer = answer; - answer = NULL; - - *ret_soa = soa; - soa = NULL; - - if (ret_tentative) - *ret_tentative = tentative; - - return 1; -} - -void dns_zone_item_conflict(DnsZoneItem *i) { - _cleanup_free_ char *pretty = NULL; - - assert(i); - - if (!IN_SET(i->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_VERIFYING, DNS_ZONE_ITEM_ESTABLISHED)) - return; - - dns_resource_record_to_string(i->rr, &pretty); - log_info("Detected conflict on %s", strna(pretty)); - - dns_zone_item_probe_stop(i); - - /* Withdraw the conflict item */ - i->state = DNS_ZONE_ITEM_WITHDRAWN; - - /* Maybe change the hostname */ - if (dns_name_equal(i->scope->manager->hostname, DNS_RESOURCE_KEY_NAME(i->rr->key)) > 0) - manager_next_hostname(i->scope->manager); -} - -void dns_zone_item_ready(DnsZoneItem *i) { - _cleanup_free_ char *pretty = NULL; - - assert(i); - assert(i->probe_transaction); - - if (i->block_ready > 0) - return; - - if (IN_SET(i->probe_transaction->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING)) - return; - - if (i->probe_transaction->state == DNS_TRANSACTION_SUCCESS) { - bool we_lost = false; - - /* The probe got a successful reply. If we so far - * weren't established we just give up. If we already - * were established, and the peer has the - * lexicographically larger IP address we continue - * and defend it. */ - - if (!IN_SET(i->state, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) { - log_debug("Got a successful probe for not yet established RR, we lost."); - we_lost = true; - } else { - assert(i->probe_transaction->received); - we_lost = memcmp(&i->probe_transaction->received->sender, &i->probe_transaction->received->destination, FAMILY_ADDRESS_SIZE(i->probe_transaction->received->family)) < 0; - if (we_lost) - log_debug("Got a successful probe reply for an established RR, and we have a lexicographically larger IP address and thus lost."); - } - - if (we_lost) { - dns_zone_item_conflict(i); - return; - } - - log_debug("Got a successful probe reply, but peer has lexicographically lower IP address and thus lost."); - } - - dns_resource_record_to_string(i->rr, &pretty); - log_debug("Record %s successfully probed.", strna(pretty)); - - dns_zone_item_probe_stop(i); - i->state = DNS_ZONE_ITEM_ESTABLISHED; -} - -static int dns_zone_item_verify(DnsZoneItem *i) { - _cleanup_free_ char *pretty = NULL; - int r; - - assert(i); - - if (i->state != DNS_ZONE_ITEM_ESTABLISHED) - return 0; - - dns_resource_record_to_string(i->rr, &pretty); - log_debug("Verifying RR %s", strna(pretty)); - - i->state = DNS_ZONE_ITEM_VERIFYING; - r = dns_zone_item_probe_start(i); - if (r < 0) { - log_error_errno(r, "Failed to start probing for verifying RR: %m"); - i->state = DNS_ZONE_ITEM_ESTABLISHED; - return r; - } - - return 0; -} - -int dns_zone_check_conflicts(DnsZone *zone, DnsResourceRecord *rr) { - DnsZoneItem *i, *first; - int c = 0; - - assert(zone); - assert(rr); - - /* This checks whether a response RR we received from somebody - * else is one that we actually thought was uniquely ours. If - * so, we'll verify our RRs. */ - - /* No conflict if we don't have the name at all. */ - first = hashmap_get(zone->by_name, DNS_RESOURCE_KEY_NAME(rr->key)); - if (!first) - return 0; - - /* No conflict if we have the exact same RR */ - if (dns_zone_get(zone, rr)) - return 0; - - /* OK, somebody else has RRs for the same name. Yuck! Let's - * start probing again */ - - LIST_FOREACH(by_name, i, first) { - if (dns_resource_record_equal(i->rr, rr)) - continue; - - dns_zone_item_verify(i); - c++; - } - - return c; -} - -int dns_zone_verify_conflicts(DnsZone *zone, DnsResourceKey *key) { - DnsZoneItem *i, *first; - int c = 0; - - assert(zone); - - /* Somebody else notified us about a possible conflict. Let's - * verify if that's true. */ - - first = hashmap_get(zone->by_name, DNS_RESOURCE_KEY_NAME(key)); - if (!first) - return 0; - - LIST_FOREACH(by_name, i, first) { - dns_zone_item_verify(i); - c++; - } - - return c; -} - -void dns_zone_verify_all(DnsZone *zone) { - DnsZoneItem *i; - Iterator iterator; - - assert(zone); - - HASHMAP_FOREACH(i, zone->by_key, iterator) { - DnsZoneItem *j; - - LIST_FOREACH(by_key, j, i) - dns_zone_item_verify(j); - } -} diff --git a/src/resolve/resolved-dns-zone.h b/src/resolve/resolved-dns-zone.h deleted file mode 100644 index 71851265c6..0000000000 --- a/src/resolve/resolved-dns-zone.h +++ /dev/null @@ -1,80 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include "hashmap.h" - -typedef struct DnsZone { - Hashmap *by_key; - Hashmap *by_name; -} DnsZone; - -typedef struct DnsZoneItem DnsZoneItem; -typedef enum DnsZoneItemState DnsZoneItemState; - -#include "resolved-dns-rr.h" -#include "resolved-dns-question.h" -#include "resolved-dns-answer.h" -#include "resolved-dns-transaction.h" - -/* RFC 4795 Section 2.8. suggests a TTL of 30s by default */ -#define LLMNR_DEFAULT_TTL (30) - -enum DnsZoneItemState { - DNS_ZONE_ITEM_PROBING, - DNS_ZONE_ITEM_ESTABLISHED, - DNS_ZONE_ITEM_VERIFYING, - DNS_ZONE_ITEM_WITHDRAWN, -}; - -struct DnsZoneItem { - DnsScope *scope; - DnsResourceRecord *rr; - - DnsZoneItemState state; - - unsigned block_ready; - - bool probing_enabled; - - LIST_FIELDS(DnsZoneItem, by_key); - LIST_FIELDS(DnsZoneItem, by_name); - - DnsTransaction *probe_transaction; -}; - -void dns_zone_flush(DnsZone *z); - -int dns_zone_put(DnsZone *z, DnsScope *s, DnsResourceRecord *rr, bool probe); -void dns_zone_remove_rr(DnsZone *z, DnsResourceRecord *rr); - -int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **answer, DnsAnswer **soa, bool *tentative); - -void dns_zone_item_conflict(DnsZoneItem *i); -void dns_zone_item_ready(DnsZoneItem *i); - -int dns_zone_check_conflicts(DnsZone *zone, DnsResourceRecord *rr); -int dns_zone_verify_conflicts(DnsZone *zone, DnsResourceKey *key); - -void dns_zone_verify_all(DnsZone *zone); - -void dns_zone_item_probe_stop(DnsZoneItem *i); diff --git a/src/resolve/resolved-gperf.gperf b/src/resolve/resolved-gperf.gperf deleted file mode 100644 index 8e78fbf06a..0000000000 --- a/src/resolve/resolved-gperf.gperf +++ /dev/null @@ -1,19 +0,0 @@ -%{ -#include <stddef.h> -#include "conf-parser.h" -#include "resolved-conf.h" -%} -struct ConfigPerfItem; -%null_strings -%language=ANSI-C -%define slot-name section_and_lvalue -%define hash-function-name resolved_gperf_hash -%define lookup-function-name resolved_gperf_lookup -%readonly-tables -%omit-struct-type -%struct-type -%includes -%% -Resolve.DNS, config_parse_dnsv, DNS_SERVER_SYSTEM, 0 -Resolve.FallbackDNS, config_parse_dnsv, DNS_SERVER_FALLBACK, 0 -Resolve.LLMNR, config_parse_support, 0, offsetof(Manager, llmnr_support) diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c deleted file mode 100644 index f94e4bb6f0..0000000000 --- a/src/resolve/resolved-link.c +++ /dev/null @@ -1,552 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <net/if.h> - -#include "sd-network.h" -#include "strv.h" -#include "missing.h" -#include "resolved-link.h" - -int link_new(Manager *m, Link **ret, int ifindex) { - _cleanup_(link_freep) Link *l = NULL; - int r; - - assert(m); - assert(ifindex > 0); - - r = hashmap_ensure_allocated(&m->links, NULL); - if (r < 0) - return r; - - l = new0(Link, 1); - if (!l) - return -ENOMEM; - - l->ifindex = ifindex; - l->llmnr_support = SUPPORT_YES; - - r = hashmap_put(m->links, INT_TO_PTR(ifindex), l); - if (r < 0) - return r; - - l->manager = m; - - if (ret) - *ret = l; - l = NULL; - - return 0; -} - -Link *link_free(Link *l) { - - if (!l) - return NULL; - - while (l->addresses) - link_address_free(l->addresses); - - if (l->manager) - hashmap_remove(l->manager->links, INT_TO_PTR(l->ifindex)); - - dns_scope_free(l->unicast_scope); - dns_scope_free(l->llmnr_ipv4_scope); - dns_scope_free(l->llmnr_ipv6_scope); - - while (l->dns_servers) - dns_server_free(l->dns_servers); - - free(l); - return NULL; -} - -static void link_allocate_scopes(Link *l) { - int r; - - assert(l); - - if (l->dns_servers) { - if (!l->unicast_scope) { - r = dns_scope_new(l->manager, &l->unicast_scope, l, DNS_PROTOCOL_DNS, AF_UNSPEC); - if (r < 0) - log_warning_errno(r, "Failed to allocate DNS scope: %m"); - } - } else - l->unicast_scope = dns_scope_free(l->unicast_scope); - - if (link_relevant(l, AF_INET) && - l->llmnr_support != SUPPORT_NO && - l->manager->llmnr_support != SUPPORT_NO) { - if (!l->llmnr_ipv4_scope) { - r = dns_scope_new(l->manager, &l->llmnr_ipv4_scope, l, DNS_PROTOCOL_LLMNR, AF_INET); - if (r < 0) - log_warning_errno(r, "Failed to allocate LLMNR IPv4 scope: %m"); - } - } else - l->llmnr_ipv4_scope = dns_scope_free(l->llmnr_ipv4_scope); - - if (link_relevant(l, AF_INET6) && - l->llmnr_support != SUPPORT_NO && - l->manager->llmnr_support != SUPPORT_NO && - socket_ipv6_is_supported()) { - if (!l->llmnr_ipv6_scope) { - r = dns_scope_new(l->manager, &l->llmnr_ipv6_scope, l, DNS_PROTOCOL_LLMNR, AF_INET6); - if (r < 0) - log_warning_errno(r, "Failed to allocate LLMNR IPv6 scope: %m"); - } - } else - l->llmnr_ipv6_scope = dns_scope_free(l->llmnr_ipv6_scope); -} - -void link_add_rrs(Link *l, bool force_remove) { - LinkAddress *a; - - LIST_FOREACH(addresses, a, l->addresses) - link_address_add_rrs(a, force_remove); -} - -int link_update_rtnl(Link *l, sd_rtnl_message *m) { - const char *n = NULL; - int r; - - assert(l); - assert(m); - - r = sd_rtnl_message_link_get_flags(m, &l->flags); - if (r < 0) - return r; - - sd_rtnl_message_read_u32(m, IFLA_MTU, &l->mtu); - - if (sd_rtnl_message_read_string(m, IFLA_IFNAME, &n) >= 0) { - strncpy(l->name, n, sizeof(l->name)-1); - char_array_0(l->name); - } - - link_allocate_scopes(l); - link_add_rrs(l, false); - - return 0; -} - -static int link_update_dns_servers(Link *l) { - _cleanup_strv_free_ char **nameservers = NULL; - char **nameserver; - DnsServer *s, *nx; - int r; - - assert(l); - - r = sd_network_link_get_dns(l->ifindex, &nameservers); - if (r < 0) - goto clear; - - LIST_FOREACH(servers, s, l->dns_servers) - s->marked = true; - - STRV_FOREACH(nameserver, nameservers) { - union in_addr_union a; - int family; - - r = in_addr_from_string_auto(*nameserver, &family, &a); - if (r < 0) - goto clear; - - s = link_find_dns_server(l, family, &a); - if (s) - s->marked = false; - else { - r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, family, &a); - if (r < 0) - goto clear; - } - } - - LIST_FOREACH_SAFE(servers, s, nx, l->dns_servers) - if (s->marked) - dns_server_free(s); - - return 0; - -clear: - while (l->dns_servers) - dns_server_free(l->dns_servers); - - return r; -} - -static int link_update_llmnr_support(Link *l) { - _cleanup_free_ char *b = NULL; - int r; - - assert(l); - - r = sd_network_link_get_llmnr(l->ifindex, &b); - if (r < 0) - goto clear; - - r = parse_boolean(b); - if (r < 0) { - if (streq(b, "resolve")) - l->llmnr_support = SUPPORT_RESOLVE; - else - goto clear; - - } else if (r > 0) - l->llmnr_support = SUPPORT_YES; - else - l->llmnr_support = SUPPORT_NO; - - return 0; - -clear: - l->llmnr_support = SUPPORT_YES; - return r; -} - -static int link_update_domains(Link *l) { - int r; - - if (!l->unicast_scope) - return 0; - - strv_free(l->unicast_scope->domains); - l->unicast_scope->domains = NULL; - - r = sd_network_link_get_domains(l->ifindex, - &l->unicast_scope->domains); - if (r < 0) - return r; - - return 0; -} - -int link_update_monitor(Link *l) { - assert(l); - - link_update_dns_servers(l); - link_update_llmnr_support(l); - link_allocate_scopes(l); - link_update_domains(l); - link_add_rrs(l, false); - - return 0; -} - -bool link_relevant(Link *l, int family) { - _cleanup_free_ char *state = NULL; - LinkAddress *a; - - assert(l); - - /* A link is relevant if it isn't a loopback or pointopoint - * device, has a link beat, can do multicast and has at least - * one relevant IP address */ - - if (l->flags & (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_DORMANT)) - return false; - - if ((l->flags & (IFF_UP|IFF_LOWER_UP|IFF_MULTICAST)) != (IFF_UP|IFF_LOWER_UP|IFF_MULTICAST)) - return false; - - sd_network_link_get_operational_state(l->ifindex, &state); - if (state && !STR_IN_SET(state, "unknown", "degraded", "routable")) - return false; - - LIST_FOREACH(addresses, a, l->addresses) - if (a->family == family && link_address_relevant(a)) - return true; - - return false; -} - -LinkAddress *link_find_address(Link *l, int family, const union in_addr_union *in_addr) { - LinkAddress *a; - - assert(l); - - LIST_FOREACH(addresses, a, l->addresses) - if (a->family == family && in_addr_equal(family, &a->in_addr, in_addr)) - return a; - - return NULL; -} - -DnsServer* link_find_dns_server(Link *l, int family, const union in_addr_union *in_addr) { - DnsServer *s; - - assert(l); - - LIST_FOREACH(servers, s, l->dns_servers) - if (s->family == family && in_addr_equal(family, &s->address, in_addr)) - return s; - return NULL; -} - -DnsServer* link_set_dns_server(Link *l, DnsServer *s) { - assert(l); - - if (l->current_dns_server == s) - return s; - - if (s) { - _cleanup_free_ char *ip = NULL; - - in_addr_to_string(s->family, &s->address, &ip); - log_info("Switching to DNS server %s for interface %s.", strna(ip), l->name); - } - - l->current_dns_server = s; - - if (l->unicast_scope) - dns_cache_flush(&l->unicast_scope->cache); - - return s; -} - -DnsServer *link_get_dns_server(Link *l) { - assert(l); - - if (!l->current_dns_server) - link_set_dns_server(l, l->dns_servers); - - return l->current_dns_server; -} - -void link_next_dns_server(Link *l) { - assert(l); - - if (!l->current_dns_server) - return; - - if (l->current_dns_server->servers_next) { - link_set_dns_server(l, l->current_dns_server->servers_next); - return; - } - - link_set_dns_server(l, l->dns_servers); -} - -int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr) { - LinkAddress *a; - - assert(l); - assert(in_addr); - - a = new0(LinkAddress, 1); - if (!a) - return -ENOMEM; - - a->family = family; - a->in_addr = *in_addr; - - a->link = l; - LIST_PREPEND(addresses, l->addresses, a); - - if (ret) - *ret = a; - - return 0; -} - -LinkAddress *link_address_free(LinkAddress *a) { - if (!a) - return NULL; - - if (a->link) { - LIST_REMOVE(addresses, a->link->addresses, a); - - if (a->llmnr_address_rr) { - if (a->family == AF_INET && a->link->llmnr_ipv4_scope) - dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr); - else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope) - dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr); - } - - if (a->llmnr_ptr_rr) { - if (a->family == AF_INET && a->link->llmnr_ipv4_scope) - dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr); - else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope) - dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr); - } - } - - dns_resource_record_unref(a->llmnr_address_rr); - dns_resource_record_unref(a->llmnr_ptr_rr); - - free(a); - return NULL; -} - -void link_address_add_rrs(LinkAddress *a, bool force_remove) { - int r; - - assert(a); - - if (a->family == AF_INET) { - - if (!force_remove && - link_address_relevant(a) && - a->link->llmnr_ipv4_scope && - a->link->llmnr_support == SUPPORT_YES && - a->link->manager->llmnr_support == SUPPORT_YES) { - - if (!a->link->manager->host_ipv4_key) { - a->link->manager->host_ipv4_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, a->link->manager->hostname); - if (!a->link->manager->host_ipv4_key) { - r = -ENOMEM; - goto fail; - } - } - - if (!a->llmnr_address_rr) { - a->llmnr_address_rr = dns_resource_record_new(a->link->manager->host_ipv4_key); - if (!a->llmnr_address_rr) { - r = -ENOMEM; - goto fail; - } - - a->llmnr_address_rr->a.in_addr = a->in_addr.in; - a->llmnr_address_rr->ttl = LLMNR_DEFAULT_TTL; - } - - if (!a->llmnr_ptr_rr) { - r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->hostname); - if (r < 0) - goto fail; - - a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL; - } - - r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_address_rr, true); - if (r < 0) - log_warning_errno(r, "Failed to add A record to LLMNR zone: %m"); - - r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_ptr_rr, false); - if (r < 0) - log_warning_errno(r, "Failed to add IPv6 PTR record to LLMNR zone: %m"); - } else { - if (a->llmnr_address_rr) { - if (a->link->llmnr_ipv4_scope) - dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr); - a->llmnr_address_rr = dns_resource_record_unref(a->llmnr_address_rr); - } - - if (a->llmnr_ptr_rr) { - if (a->link->llmnr_ipv4_scope) - dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr); - a->llmnr_ptr_rr = dns_resource_record_unref(a->llmnr_ptr_rr); - } - } - } - - if (a->family == AF_INET6) { - - if (!force_remove && - link_address_relevant(a) && - a->link->llmnr_ipv6_scope && - a->link->llmnr_support == SUPPORT_YES && - a->link->manager->llmnr_support == SUPPORT_YES) { - - if (!a->link->manager->host_ipv6_key) { - a->link->manager->host_ipv6_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, a->link->manager->hostname); - if (!a->link->manager->host_ipv6_key) { - r = -ENOMEM; - goto fail; - } - } - - if (!a->llmnr_address_rr) { - a->llmnr_address_rr = dns_resource_record_new(a->link->manager->host_ipv6_key); - if (!a->llmnr_address_rr) { - r = -ENOMEM; - goto fail; - } - - a->llmnr_address_rr->aaaa.in6_addr = a->in_addr.in6; - a->llmnr_address_rr->ttl = LLMNR_DEFAULT_TTL; - } - - if (!a->llmnr_ptr_rr) { - r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->hostname); - if (r < 0) - goto fail; - - a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL; - } - - r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_address_rr, true); - if (r < 0) - log_warning_errno(r, "Failed to add AAAA record to LLMNR zone: %m"); - - r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_ptr_rr, false); - if (r < 0) - log_warning_errno(r, "Failed to add IPv6 PTR record to LLMNR zone: %m"); - } else { - if (a->llmnr_address_rr) { - if (a->link->llmnr_ipv6_scope) - dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr); - a->llmnr_address_rr = dns_resource_record_unref(a->llmnr_address_rr); - } - - if (a->llmnr_ptr_rr) { - if (a->link->llmnr_ipv6_scope) - dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr); - a->llmnr_ptr_rr = dns_resource_record_unref(a->llmnr_ptr_rr); - } - } - } - - return; - -fail: - log_debug_errno(r, "Failed to update address RRs: %m"); -} - -int link_address_update_rtnl(LinkAddress *a, sd_rtnl_message *m) { - int r; - assert(a); - assert(m); - - r = sd_rtnl_message_addr_get_flags(m, &a->flags); - if (r < 0) - return r; - - sd_rtnl_message_addr_get_scope(m, &a->scope); - - link_allocate_scopes(a->link); - link_add_rrs(a->link, false); - - return 0; -} - -bool link_address_relevant(LinkAddress *a) { - assert(a); - - if (a->flags & (IFA_F_DEPRECATED|IFA_F_TENTATIVE)) - return false; - - if (IN_SET(a->scope, RT_SCOPE_HOST, RT_SCOPE_NOWHERE)) - return false; - - return true; -} diff --git a/src/resolve/resolved-link.h b/src/resolve/resolved-link.h deleted file mode 100644 index eee1846108..0000000000 --- a/src/resolve/resolved-link.h +++ /dev/null @@ -1,89 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <net/if.h> - -#include "in-addr-util.h" -#include "ratelimit.h" - -typedef struct Link Link; -typedef struct LinkAddress LinkAddress; - -#include "resolved-dns-rr.h" -#include "resolved-manager.h" - -struct LinkAddress { - Link *link; - - int family; - union in_addr_union in_addr; - - unsigned char flags, scope; - - DnsResourceRecord *llmnr_address_rr; - DnsResourceRecord *llmnr_ptr_rr; - - LIST_FIELDS(LinkAddress, addresses); -}; - -struct Link { - Manager *manager; - - int ifindex; - unsigned flags; - - LIST_HEAD(LinkAddress, addresses); - - LIST_HEAD(DnsServer, dns_servers); - DnsServer *current_dns_server; - - Support llmnr_support; - - DnsScope *unicast_scope; - DnsScope *llmnr_ipv4_scope; - DnsScope *llmnr_ipv6_scope; - - char name[IF_NAMESIZE]; - uint32_t mtu; -}; - -int link_new(Manager *m, Link **ret, int ifindex); -Link *link_free(Link *l); -int link_update_rtnl(Link *l, sd_rtnl_message *m); -int link_update_monitor(Link *l); -bool link_relevant(Link *l, int family); -LinkAddress* link_find_address(Link *l, int family, const union in_addr_union *in_addr); -void link_add_rrs(Link *l, bool force_remove); - -DnsServer* link_set_dns_server(Link *l, DnsServer *s); -DnsServer* link_find_dns_server(Link *l, int family, const union in_addr_union *in_addr); -DnsServer* link_get_dns_server(Link *l); -void link_next_dns_server(Link *l); - -int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr); -LinkAddress *link_address_free(LinkAddress *a); -int link_address_update_rtnl(LinkAddress *a, sd_rtnl_message *m); -bool link_address_relevant(LinkAddress *l); -void link_address_add_rrs(LinkAddress *a, bool force_remove); - -DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_free); diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c deleted file mode 100644 index 167bea39b3..0000000000 --- a/src/resolve/resolved-manager.c +++ /dev/null @@ -1,1841 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2014 Tom Gundersen <teg@jklm.no> - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. - ***/ - -#include <resolv.h> -#include <sys/ioctl.h> -#include <poll.h> -#include <netinet/in.h> - -#include "rtnl-util.h" -#include "network-internal.h" -#include "socket-util.h" -#include "af-list.h" -#include "utf8.h" -#include "fileio-label.h" - -#include "resolved-dns-domain.h" -#include "resolved-conf.h" -#include "resolved-bus.h" -#include "resolved-manager.h" - -#define SEND_TIMEOUT_USEC (200 * USEC_PER_MSEC) - -static int manager_process_link(sd_rtnl *rtnl, sd_rtnl_message *mm, void *userdata) { - Manager *m = userdata; - uint16_t type; - Link *l; - int ifindex, r; - - assert(rtnl); - assert(m); - assert(mm); - - r = sd_rtnl_message_get_type(mm, &type); - if (r < 0) - goto fail; - - r = sd_rtnl_message_link_get_ifindex(mm, &ifindex); - if (r < 0) - goto fail; - - l = hashmap_get(m->links, INT_TO_PTR(ifindex)); - - switch (type) { - - case RTM_NEWLINK:{ - bool is_new = !l; - - if (!l) { - r = link_new(m, &l, ifindex); - if (r < 0) - goto fail; - } - - r = link_update_rtnl(l, mm); - if (r < 0) - goto fail; - - r = link_update_monitor(l); - if (r < 0) - goto fail; - - if (is_new) - log_debug("Found new link %i/%s", ifindex, l->name); - - break; - } - - case RTM_DELLINK: - if (l) { - log_debug("Removing link %i/%s", l->ifindex, l->name); - link_free(l); - } - - break; - } - - return 0; - -fail: - log_warning_errno(r, "Failed to process RTNL link message: %m"); - return 0; -} - -static int manager_process_address(sd_rtnl *rtnl, sd_rtnl_message *mm, void *userdata) { - Manager *m = userdata; - union in_addr_union address; - uint16_t type; - int r, ifindex, family; - LinkAddress *a; - Link *l; - - assert(rtnl); - assert(mm); - assert(m); - - r = sd_rtnl_message_get_type(mm, &type); - if (r < 0) - goto fail; - - r = sd_rtnl_message_addr_get_ifindex(mm, &ifindex); - if (r < 0) - goto fail; - - l = hashmap_get(m->links, INT_TO_PTR(ifindex)); - if (!l) - return 0; - - r = sd_rtnl_message_addr_get_family(mm, &family); - if (r < 0) - goto fail; - - switch (family) { - - case AF_INET: - r = sd_rtnl_message_read_in_addr(mm, IFA_LOCAL, &address.in); - if (r < 0) { - r = sd_rtnl_message_read_in_addr(mm, IFA_ADDRESS, &address.in); - if (r < 0) - goto fail; - } - - break; - - case AF_INET6: - r = sd_rtnl_message_read_in6_addr(mm, IFA_LOCAL, &address.in6); - if (r < 0) { - r = sd_rtnl_message_read_in6_addr(mm, IFA_ADDRESS, &address.in6); - if (r < 0) - goto fail; - } - - break; - - default: - return 0; - } - - a = link_find_address(l, family, &address); - - switch (type) { - - case RTM_NEWADDR: - - if (!a) { - r = link_address_new(l, &a, family, &address); - if (r < 0) - return r; - } - - r = link_address_update_rtnl(a, mm); - if (r < 0) - return r; - - break; - - case RTM_DELADDR: - if (a) - link_address_free(a); - break; - } - - return 0; - -fail: - log_warning_errno(r, "Failed to process RTNL address message: %m"); - return 0; -} - -static int manager_rtnl_listen(Manager *m) { - _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL; - sd_rtnl_message *i; - int r; - - assert(m); - - /* First, subscribe to interfaces coming and going */ - r = sd_rtnl_open(&m->rtnl, 3, RTNLGRP_LINK, RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR); - if (r < 0) - return r; - - r = sd_rtnl_attach_event(m->rtnl, m->event, 0); - if (r < 0) - return r; - - r = sd_rtnl_add_match(m->rtnl, RTM_NEWLINK, manager_process_link, m); - if (r < 0) - return r; - - r = sd_rtnl_add_match(m->rtnl, RTM_DELLINK, manager_process_link, m); - if (r < 0) - return r; - - r = sd_rtnl_add_match(m->rtnl, RTM_NEWADDR, manager_process_address, m); - if (r < 0) - return r; - - r = sd_rtnl_add_match(m->rtnl, RTM_DELADDR, manager_process_address, m); - if (r < 0) - return r; - - /* Then, enumerate all links */ - r = sd_rtnl_message_new_link(m->rtnl, &req, RTM_GETLINK, 0); - if (r < 0) - return r; - - r = sd_rtnl_message_request_dump(req, true); - if (r < 0) - return r; - - r = sd_rtnl_call(m->rtnl, req, 0, &reply); - if (r < 0) - return r; - - for (i = reply; i; i = sd_rtnl_message_next(i)) { - r = manager_process_link(m->rtnl, i, m); - if (r < 0) - return r; - } - - req = sd_rtnl_message_unref(req); - reply = sd_rtnl_message_unref(reply); - - /* Finally, enumerate all addresses, too */ - r = sd_rtnl_message_new_addr(m->rtnl, &req, RTM_GETADDR, 0, AF_UNSPEC); - if (r < 0) - return r; - - r = sd_rtnl_message_request_dump(req, true); - if (r < 0) - return r; - - r = sd_rtnl_call(m->rtnl, req, 0, &reply); - if (r < 0) - return r; - - for (i = reply; i; i = sd_rtnl_message_next(i)) { - r = manager_process_address(m->rtnl, i, m); - if (r < 0) - return r; - } - - return r; -} - -static int on_network_event(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - Manager *m = userdata; - Iterator i; - Link *l; - int r; - - assert(m); - - sd_network_monitor_flush(m->network_monitor); - - HASHMAP_FOREACH(l, m->links, i) { - r = link_update_monitor(l); - if (r < 0) - log_warning_errno(r, "Failed to update monitor information for %i: %m", l->ifindex); - } - - r = manager_write_resolv_conf(m); - if (r < 0) - log_warning_errno(r, "Could not update resolv.conf: %m"); - - return 0; -} - -static int manager_network_monitor_listen(Manager *m) { - int r, fd, events; - - assert(m); - - r = sd_network_monitor_new(&m->network_monitor, NULL); - if (r < 0) - return r; - - fd = sd_network_monitor_get_fd(m->network_monitor); - if (fd < 0) - return fd; - - events = sd_network_monitor_get_events(m->network_monitor); - if (events < 0) - return events; - - r = sd_event_add_io(m->event, &m->network_event_source, fd, events, &on_network_event, m); - if (r < 0) - return r; - - return 0; -} - -static int determine_hostname(char **ret) { - _cleanup_free_ char *h = NULL, *n = NULL; - int r; - - assert(ret); - - h = gethostname_malloc(); - if (!h) - return log_oom(); - - if (!utf8_is_valid(h)) { - log_error("System hostname is not UTF-8 clean."); - return -EINVAL; - } - - r = dns_name_normalize(h, &n); - if (r < 0) { - log_error("System hostname '%s' cannot be normalized.", h); - return r; - } - - *ret = n; - n = NULL; - - return 0; -} - -static int on_hostname_change(sd_event_source *es, int fd, uint32_t revents, void *userdata) { - _cleanup_free_ char *h = NULL; - Manager *m = userdata; - int r; - - assert(m); - - r = determine_hostname(&h); - if (r < 0) - return 0; /* ignore invalid hostnames */ - - if (streq(h, m->hostname)) - return 0; - - log_info("System hostname changed to '%s'.", h); - free(m->hostname); - m->hostname = h; - h = NULL; - - manager_refresh_rrs(m); - - return 0; -} - -static int manager_watch_hostname(Manager *m) { - int r; - - assert(m); - - m->hostname_fd = open("/proc/sys/kernel/hostname", O_RDONLY|O_CLOEXEC|O_NDELAY|O_NOCTTY); - if (m->hostname_fd < 0) { - log_warning_errno(errno, "Failed to watch hostname: %m"); - return 0; - } - - r = sd_event_add_io(m->event, &m->hostname_event_source, m->hostname_fd, 0, on_hostname_change, m); - if (r < 0) { - if (r == -EPERM) - /* kernels prior to 3.2 don't support polling this file. Ignore the failure. */ - m->hostname_fd = safe_close(m->hostname_fd); - else - return log_error_errno(r, "Failed to add hostname event source: %m"); - } - - r = determine_hostname(&m->hostname); - if (r < 0) { - log_info("Defaulting to hostname 'linux'."); - m->hostname = strdup("linux"); - if (!m->hostname) - return log_oom(); - } else - log_info("Using system hostname '%s'.", m->hostname); - - return 0; -} - -static void manager_llmnr_stop(Manager *m) { - assert(m); - - m->llmnr_ipv4_udp_event_source = sd_event_source_unref(m->llmnr_ipv4_udp_event_source); - m->llmnr_ipv4_udp_fd = safe_close(m->llmnr_ipv4_udp_fd); - - m->llmnr_ipv6_udp_event_source = sd_event_source_unref(m->llmnr_ipv6_udp_event_source); - m->llmnr_ipv6_udp_fd = safe_close(m->llmnr_ipv6_udp_fd); - - m->llmnr_ipv4_tcp_event_source = sd_event_source_unref(m->llmnr_ipv4_tcp_event_source); - m->llmnr_ipv4_tcp_fd = safe_close(m->llmnr_ipv4_tcp_fd); - - m->llmnr_ipv6_tcp_event_source = sd_event_source_unref(m->llmnr_ipv6_tcp_event_source); - m->llmnr_ipv6_tcp_fd = safe_close(m->llmnr_ipv6_tcp_fd); -} - -static int manager_llmnr_start(Manager *m) { - int r; - - assert(m); - - if (m->llmnr_support == SUPPORT_NO) - return 0; - - r = manager_llmnr_ipv4_udp_fd(m); - if (r == -EADDRINUSE) - goto eaddrinuse; - if (r < 0) - return r; - - r = manager_llmnr_ipv4_tcp_fd(m); - if (r == -EADDRINUSE) - goto eaddrinuse; - if (r < 0) - return r; - - if (socket_ipv6_is_supported()) { - r = manager_llmnr_ipv6_udp_fd(m); - if (r == -EADDRINUSE) - goto eaddrinuse; - if (r < 0) - return r; - - r = manager_llmnr_ipv6_tcp_fd(m); - if (r == -EADDRINUSE) - goto eaddrinuse; - if (r < 0) - return r; - } - - return 0; - -eaddrinuse: - log_warning("There appears to be another LLMNR responder running. Turning off LLMNR support."); - m->llmnr_support = SUPPORT_NO; - manager_llmnr_stop(m); - - return 0; -} - -int manager_new(Manager **ret) { - _cleanup_(manager_freep) Manager *m = NULL; - int r; - - assert(ret); - - m = new0(Manager, 1); - if (!m) - return -ENOMEM; - - m->dns_ipv4_fd = m->dns_ipv6_fd = -1; - m->llmnr_ipv4_udp_fd = m->llmnr_ipv6_udp_fd = -1; - m->llmnr_ipv4_tcp_fd = m->llmnr_ipv6_tcp_fd = -1; - m->hostname_fd = -1; - - m->llmnr_support = SUPPORT_YES; - m->read_resolv_conf = true; - - r = manager_parse_dns_server(m, DNS_SERVER_FALLBACK, DNS_SERVERS); - if (r < 0) - return r; - - r = sd_event_default(&m->event); - if (r < 0) - return r; - - sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL); - sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL); - - sd_event_set_watchdog(m->event, true); - - r = manager_watch_hostname(m); - if (r < 0) - return r; - - r = dns_scope_new(m, &m->unicast_scope, NULL, DNS_PROTOCOL_DNS, AF_UNSPEC); - if (r < 0) - return r; - - r = manager_network_monitor_listen(m); - if (r < 0) - return r; - - r = manager_rtnl_listen(m); - if (r < 0) - return r; - - r = manager_connect_bus(m); - if (r < 0) - return r; - - *ret = m; - m = NULL; - - return 0; -} - -int manager_start(Manager *m) { - int r; - - assert(m); - - r = manager_llmnr_start(m); - if (r < 0) - return r; - - return 0; -} - -Manager *manager_free(Manager *m) { - Link *l; - - if (!m) - return NULL; - - while ((l = hashmap_first(m->links))) - link_free(l); - - while (m->dns_queries) - dns_query_free(m->dns_queries); - - dns_scope_free(m->unicast_scope); - - manager_flush_dns_servers(m, DNS_SERVER_SYSTEM); - manager_flush_dns_servers(m, DNS_SERVER_FALLBACK); - - hashmap_free(m->links); - hashmap_free(m->dns_transactions); - - sd_event_source_unref(m->network_event_source); - sd_network_monitor_unref(m->network_monitor); - - sd_event_source_unref(m->dns_ipv4_event_source); - sd_event_source_unref(m->dns_ipv6_event_source); - safe_close(m->dns_ipv4_fd); - safe_close(m->dns_ipv6_fd); - - manager_llmnr_stop(m); - - sd_bus_slot_unref(m->prepare_for_sleep_slot); - sd_event_source_unref(m->bus_retry_event_source); - sd_bus_unref(m->bus); - - sd_event_unref(m->event); - - dns_resource_key_unref(m->host_ipv4_key); - dns_resource_key_unref(m->host_ipv6_key); - - safe_close(m->hostname_fd); - sd_event_source_unref(m->hostname_event_source); - free(m->hostname); - - free(m); - - return NULL; -} - -int manager_read_resolv_conf(Manager *m) { - _cleanup_fclose_ FILE *f = NULL; - struct stat st, own; - char line[LINE_MAX]; - DnsServer *s, *nx; - usec_t t; - int r; - - assert(m); - - /* Reads the system /etc/resolv.conf, if it exists and is not - * symlinked to our own resolv.conf instance */ - - if (!m->read_resolv_conf) - return 0; - - r = stat("/etc/resolv.conf", &st); - if (r < 0) { - if (errno != ENOENT) - log_warning_errno(errno, "Failed to open /etc/resolv.conf: %m"); - r = -errno; - goto clear; - } - - /* Have we already seen the file? */ - t = timespec_load(&st.st_mtim); - if (t == m->resolv_conf_mtime) - return 0; - - m->resolv_conf_mtime = t; - - /* Is it symlinked to our own file? */ - if (stat("/run/systemd/resolve/resolv.conf", &own) >= 0 && - st.st_dev == own.st_dev && - st.st_ino == own.st_ino) { - r = 0; - goto clear; - } - - f = fopen("/etc/resolv.conf", "re"); - if (!f) { - if (errno != ENOENT) - log_warning_errno(errno, "Failed to open /etc/resolv.conf: %m"); - r = -errno; - goto clear; - } - - if (fstat(fileno(f), &st) < 0) { - log_error_errno(errno, "Failed to stat open file: %m"); - r = -errno; - goto clear; - } - - LIST_FOREACH(servers, s, m->dns_servers) - s->marked = true; - - FOREACH_LINE(line, f, r = -errno; goto clear) { - union in_addr_union address; - int family; - char *l; - const char *a; - - truncate_nl(line); - - l = strstrip(line); - if (*l == '#' || *l == ';') - continue; - - a = first_word(l, "nameserver"); - if (!a) - continue; - - r = in_addr_from_string_auto(a, &family, &address); - if (r < 0) { - log_warning("Failed to parse name server %s.", a); - continue; - } - - LIST_FOREACH(servers, s, m->dns_servers) - if (s->family == family && in_addr_equal(family, &s->address, &address) > 0) - break; - - if (s) - s->marked = false; - else { - r = dns_server_new(m, NULL, DNS_SERVER_SYSTEM, NULL, family, &address); - if (r < 0) - goto clear; - } - } - - LIST_FOREACH_SAFE(servers, s, nx, m->dns_servers) - if (s->marked) - dns_server_free(s); - - /* Whenever /etc/resolv.conf changes, start using the first - * DNS server of it. This is useful to deal with broken - * network managing implementations (like NetworkManager), - * that when connecting to a VPN place both the VPN DNS - * servers and the local ones in /etc/resolv.conf. Without - * resetting the DNS server to use back to the first entry we - * will continue to use the local one thus being unable to - * resolve VPN domains. */ - manager_set_dns_server(m, m->dns_servers); - - return 0; - -clear: - while (m->dns_servers) - dns_server_free(m->dns_servers); - - return r; -} - -static void write_resolv_conf_server(DnsServer *s, FILE *f, unsigned *count) { - _cleanup_free_ char *t = NULL; - int r; - - assert(s); - assert(f); - assert(count); - - r = in_addr_to_string(s->family, &s->address, &t); - if (r < 0) { - log_warning_errno(r, "Invalid DNS address. Ignoring: %m"); - return; - } - - if (*count == MAXNS) - fputs("# Too many DNS servers configured, the following entries may be ignored.\n", f); - - fprintf(f, "nameserver %s\n", t); - (*count) ++; -} - -static void write_resolv_conf_search(const char *domain, FILE *f, - unsigned *count, unsigned *length) { - assert(domain); - assert(f); - assert(length); - - if (*count >= MAXDNSRCH || - *length + strlen(domain) > 256) { - if (*count == MAXDNSRCH) - fputs(" # Too many search domains configured, remaining ones ignored.", f); - if (*length <= 256) - fputs(" # Total length of all search domains is too long, remaining ones ignored.", f); - - return; - } - - fprintf(f, " %s", domain); - - (*length) += strlen(domain); - (*count) ++; -} - -static int write_resolv_conf_contents(FILE *f, Set *dns, Set *domains) { - Iterator i; - - fputs("# This file is managed by systemd-resolved(8). Do not edit.\n#\n" - "# Third party programs must not access this file directly, but\n" - "# only through the symlink at /etc/resolv.conf. To manage\n" - "# resolv.conf(5) in a different way, replace the symlink by a\n" - "# static file or a different symlink.\n\n", f); - - if (set_isempty(dns)) - fputs("# No DNS servers known.\n", f); - else { - DnsServer *s; - unsigned count = 0; - - SET_FOREACH(s, dns, i) - write_resolv_conf_server(s, f, &count); - } - - if (!set_isempty(domains)) { - unsigned length = 0, count = 0; - char *domain; - - fputs("search", f); - SET_FOREACH(domain, domains, i) - write_resolv_conf_search(domain, f, &count, &length); - fputs("\n", f); - } - - return fflush_and_check(f); -} - - -int manager_write_resolv_conf(Manager *m) { - static const char path[] = "/run/systemd/resolve/resolv.conf"; - _cleanup_free_ char *temp_path = NULL; - _cleanup_fclose_ FILE *f = NULL; - _cleanup_set_free_ Set *dns = NULL, *domains = NULL; - DnsServer *s; - Iterator i; - Link *l; - int r; - - assert(m); - - /* Read the system /etc/resolv.conf first */ - manager_read_resolv_conf(m); - - /* Add the full list to a set, to filter out duplicates */ - dns = set_new(&dns_server_hash_ops); - if (!dns) - return -ENOMEM; - - domains = set_new(&dns_name_hash_ops); - if (!domains) - return -ENOMEM; - - /* First add the system-wide servers */ - LIST_FOREACH(servers, s, m->dns_servers) { - r = set_put(dns, s); - if (r == -EEXIST) - continue; - if (r < 0) - return r; - } - - /* Then, add the per-link servers and domains */ - HASHMAP_FOREACH(l, m->links, i) { - char **domain; - - LIST_FOREACH(servers, s, l->dns_servers) { - r = set_put(dns, s); - if (r == -EEXIST) - continue; - if (r < 0) - return r; - } - - if (!l->unicast_scope) - continue; - - STRV_FOREACH(domain, l->unicast_scope->domains) { - r = set_put(domains, *domain); - if (r == -EEXIST) - continue; - if (r < 0) - return r; - } - } - - /* If we found nothing, add the fallback servers */ - if (set_isempty(dns)) { - LIST_FOREACH(servers, s, m->fallback_dns_servers) { - r = set_put(dns, s); - if (r == -EEXIST) - continue; - if (r < 0) - return r; - } - } - - r = fopen_temporary_label(path, path, &f, &temp_path); - if (r < 0) - return r; - - fchmod(fileno(f), 0644); - - r = write_resolv_conf_contents(f, dns, domains); - if (r < 0) - goto fail; - - if (rename(temp_path, path) < 0) { - r = -errno; - goto fail; - } - - return 0; - -fail: - unlink(path); - unlink(temp_path); - return r; -} - -int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) { - _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; - union { - struct cmsghdr header; /* For alignment */ - uint8_t buffer[CMSG_SPACE(MAXSIZE(struct in_pktinfo, struct in6_pktinfo)) - + CMSG_SPACE(int) /* ttl/hoplimit */ - + EXTRA_CMSG_SPACE /* kernel appears to require extra buffer space */]; - } control; - union sockaddr_union sa; - struct msghdr mh = {}; - struct cmsghdr *cmsg; - struct iovec iov; - int ms = 0, r; - ssize_t l; - - assert(m); - assert(fd >= 0); - assert(ret); - - r = ioctl(fd, FIONREAD, &ms); - if (r < 0) - return -errno; - if (ms < 0) - return -EIO; - - r = dns_packet_new(&p, protocol, ms); - if (r < 0) - return r; - - iov.iov_base = DNS_PACKET_DATA(p); - iov.iov_len = p->allocated; - - mh.msg_name = &sa.sa; - mh.msg_namelen = sizeof(sa); - mh.msg_iov = &iov; - mh.msg_iovlen = 1; - mh.msg_control = &control; - mh.msg_controllen = sizeof(control); - - l = recvmsg(fd, &mh, 0); - if (l < 0) { - if (errno == EAGAIN || errno == EINTR) - return 0; - - return -errno; - } - - if (l <= 0) - return -EIO; - - assert(!(mh.msg_flags & MSG_CTRUNC)); - assert(!(mh.msg_flags & MSG_TRUNC)); - - p->size = (size_t) l; - - p->family = sa.sa.sa_family; - p->ipproto = IPPROTO_UDP; - if (p->family == AF_INET) { - p->sender.in = sa.in.sin_addr; - p->sender_port = be16toh(sa.in.sin_port); - } else if (p->family == AF_INET6) { - p->sender.in6 = sa.in6.sin6_addr; - p->sender_port = be16toh(sa.in6.sin6_port); - p->ifindex = sa.in6.sin6_scope_id; - } else - return -EAFNOSUPPORT; - - for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg)) { - - if (cmsg->cmsg_level == IPPROTO_IPV6) { - assert(p->family == AF_INET6); - - switch (cmsg->cmsg_type) { - - case IPV6_PKTINFO: { - struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg); - - if (p->ifindex <= 0) - p->ifindex = i->ipi6_ifindex; - - p->destination.in6 = i->ipi6_addr; - break; - } - - case IPV6_HOPLIMIT: - p->ttl = *(int *) CMSG_DATA(cmsg); - break; - - } - } else if (cmsg->cmsg_level == IPPROTO_IP) { - assert(p->family == AF_INET); - - switch (cmsg->cmsg_type) { - - case IP_PKTINFO: { - struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg); - - if (p->ifindex <= 0) - p->ifindex = i->ipi_ifindex; - - p->destination.in = i->ipi_addr; - break; - } - - case IP_TTL: - p->ttl = *(int *) CMSG_DATA(cmsg); - break; - } - } - } - - /* The Linux kernel sets the interface index to the loopback - * device if the packet came from the local host since it - * avoids the routing table in such a case. Let's unset the - * interface index in such a case. */ - if (p->ifindex == LOOPBACK_IFINDEX) - p->ifindex = 0; - - /* If we don't know the interface index still, we look for the - * first local interface with a matching address. Yuck! */ - if (p->ifindex <= 0) - p->ifindex = manager_find_ifindex(m, p->family, &p->destination); - - *ret = p; - p = NULL; - - return 1; -} - -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 = NULL; - Manager *m = userdata; - int r; - - r = manager_recv(m, fd, DNS_PROTOCOL_DNS, &p); - if (r <= 0) - return r; - - if (dns_packet_validate_reply(p) > 0) { - t = hashmap_get(m->dns_transactions, UINT_TO_PTR(DNS_PACKET_ID(p))); - if (!t) - return 0; - - dns_transaction_process_reply(t, p); - - } else - log_debug("Invalid DNS packet."); - - return 0; -} - -int manager_dns_ipv4_fd(Manager *m) { - const int one = 1; - int r; - - assert(m); - - if (m->dns_ipv4_fd >= 0) - return m->dns_ipv4_fd; - - m->dns_ipv4_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (m->dns_ipv4_fd < 0) - return -errno; - - r = setsockopt(m->dns_ipv4_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = sd_event_add_io(m->event, &m->dns_ipv4_event_source, m->dns_ipv4_fd, EPOLLIN, on_dns_packet, m); - if (r < 0) - goto fail; - - return m->dns_ipv4_fd; - -fail: - m->dns_ipv4_fd = safe_close(m->dns_ipv4_fd); - return r; -} - -int manager_dns_ipv6_fd(Manager *m) { - const int one = 1; - int r; - - assert(m); - - if (m->dns_ipv6_fd >= 0) - return m->dns_ipv6_fd; - - m->dns_ipv6_fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (m->dns_ipv6_fd < 0) - return -errno; - - r = setsockopt(m->dns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = sd_event_add_io(m->event, &m->dns_ipv6_event_source, m->dns_ipv6_fd, EPOLLIN, on_dns_packet, m); - if (r < 0) - goto fail; - - return m->dns_ipv6_fd; - -fail: - m->dns_ipv6_fd = safe_close(m->dns_ipv6_fd); - return r; -} - -static int sendmsg_loop(int fd, struct msghdr *mh, int flags) { - int r; - - assert(fd >= 0); - assert(mh); - - for (;;) { - if (sendmsg(fd, mh, flags) >= 0) - return 0; - - if (errno == EINTR) - continue; - - if (errno != EAGAIN) - return -errno; - - r = fd_wait_for_event(fd, POLLOUT, SEND_TIMEOUT_USEC); - if (r < 0) - return r; - if (r == 0) - return -ETIMEDOUT; - } -} - -static int manager_ipv4_send(Manager *m, int fd, int ifindex, const struct in_addr *addr, uint16_t port, DnsPacket *p) { - union sockaddr_union sa = { - .in.sin_family = AF_INET, - }; - union { - struct cmsghdr header; /* For alignment */ - uint8_t buffer[CMSG_SPACE(sizeof(struct in_pktinfo))]; - } control; - struct msghdr mh = {}; - struct iovec iov; - - assert(m); - assert(fd >= 0); - assert(addr); - assert(port > 0); - assert(p); - - iov.iov_base = DNS_PACKET_DATA(p); - iov.iov_len = p->size; - - sa.in.sin_addr = *addr; - sa.in.sin_port = htobe16(port), - - mh.msg_iov = &iov; - mh.msg_iovlen = 1; - mh.msg_name = &sa.sa; - mh.msg_namelen = sizeof(sa.in); - - if (ifindex > 0) { - struct cmsghdr *cmsg; - struct in_pktinfo *pi; - - zero(control); - - mh.msg_control = &control; - mh.msg_controllen = CMSG_LEN(sizeof(struct in_pktinfo)); - - cmsg = CMSG_FIRSTHDR(&mh); - cmsg->cmsg_len = mh.msg_controllen; - cmsg->cmsg_level = IPPROTO_IP; - cmsg->cmsg_type = IP_PKTINFO; - - pi = (struct in_pktinfo*) CMSG_DATA(cmsg); - pi->ipi_ifindex = ifindex; - } - - return sendmsg_loop(fd, &mh, 0); -} - -static int manager_ipv6_send(Manager *m, int fd, int ifindex, const struct in6_addr *addr, uint16_t port, DnsPacket *p) { - union sockaddr_union sa = { - .in6.sin6_family = AF_INET6, - }; - union { - struct cmsghdr header; /* For alignment */ - uint8_t buffer[CMSG_SPACE(sizeof(struct in6_pktinfo))]; - } control; - struct msghdr mh = {}; - struct iovec iov; - - assert(m); - assert(fd >= 0); - assert(addr); - assert(port > 0); - assert(p); - - iov.iov_base = DNS_PACKET_DATA(p); - iov.iov_len = p->size; - - sa.in6.sin6_addr = *addr; - sa.in6.sin6_port = htobe16(port), - sa.in6.sin6_scope_id = ifindex; - - mh.msg_iov = &iov; - mh.msg_iovlen = 1; - mh.msg_name = &sa.sa; - mh.msg_namelen = sizeof(sa.in6); - - if (ifindex > 0) { - struct cmsghdr *cmsg; - struct in6_pktinfo *pi; - - zero(control); - - mh.msg_control = &control; - mh.msg_controllen = CMSG_LEN(sizeof(struct in6_pktinfo)); - - cmsg = CMSG_FIRSTHDR(&mh); - cmsg->cmsg_len = mh.msg_controllen; - cmsg->cmsg_level = IPPROTO_IPV6; - cmsg->cmsg_type = IPV6_PKTINFO; - - pi = (struct in6_pktinfo*) CMSG_DATA(cmsg); - pi->ipi6_ifindex = ifindex; - } - - return sendmsg_loop(fd, &mh, 0); -} - -int manager_send(Manager *m, int fd, int ifindex, int family, const union in_addr_union *addr, uint16_t port, DnsPacket *p) { - assert(m); - assert(fd >= 0); - assert(addr); - assert(port > 0); - assert(p); - - log_debug("Sending %s packet with id %u on interface %i/%s", DNS_PACKET_QR(p) ? "response" : "query", DNS_PACKET_ID(p), ifindex, af_to_name(family)); - - if (family == AF_INET) - return manager_ipv4_send(m, fd, ifindex, &addr->in, port, p); - else if (family == AF_INET6) - return manager_ipv6_send(m, fd, ifindex, &addr->in6, port, p); - - return -EAFNOSUPPORT; -} - -DnsServer* manager_find_dns_server(Manager *m, int family, const union in_addr_union *in_addr) { - DnsServer *s; - - assert(m); - assert(in_addr); - - LIST_FOREACH(servers, s, m->dns_servers) - if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0) - return s; - - LIST_FOREACH(servers, s, m->fallback_dns_servers) - if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0) - return s; - - return NULL; -} - -DnsServer *manager_set_dns_server(Manager *m, DnsServer *s) { - assert(m); - - if (m->current_dns_server == s) - return s; - - if (s) { - _cleanup_free_ char *ip = NULL; - - in_addr_to_string(s->family, &s->address, &ip); - log_info("Switching to system DNS server %s.", strna(ip)); - } - - m->current_dns_server = s; - - if (m->unicast_scope) - dns_cache_flush(&m->unicast_scope->cache); - - return s; -} - -DnsServer *manager_get_dns_server(Manager *m) { - Link *l; - assert(m); - - /* Try to read updates resolv.conf */ - manager_read_resolv_conf(m); - - if (!m->current_dns_server) - manager_set_dns_server(m, m->dns_servers); - - if (!m->current_dns_server) { - bool found = false; - Iterator i; - - /* No DNS servers configured, let's see if there are - * any on any links. If not, we use the fallback - * servers */ - - HASHMAP_FOREACH(l, m->links, i) - if (l->dns_servers) { - found = true; - break; - } - - if (!found) - manager_set_dns_server(m, m->fallback_dns_servers); - } - - return m->current_dns_server; -} - -void manager_next_dns_server(Manager *m) { - assert(m); - - /* If there's currently no DNS server set, then the next - * manager_get_dns_server() will find one */ - if (!m->current_dns_server) - return; - - /* Change to the next one */ - if (m->current_dns_server->servers_next) { - manager_set_dns_server(m, m->current_dns_server->servers_next); - return; - } - - /* If there was no next one, then start from the beginning of - * the list */ - if (m->current_dns_server->type == DNS_SERVER_FALLBACK) - manager_set_dns_server(m, m->fallback_dns_servers); - else - manager_set_dns_server(m, m->dns_servers); -} - -uint32_t manager_find_mtu(Manager *m) { - uint32_t mtu = 0; - Link *l; - Iterator i; - - /* If we don't know on which link a DNS packet would be - * delivered, let's find the largest MTU that works on all - * interfaces we know of */ - - HASHMAP_FOREACH(l, m->links, i) { - if (l->mtu <= 0) - continue; - - if (mtu <= 0 || l->mtu < mtu) - mtu = l->mtu; - } - - return mtu; -} - -static int on_llmnr_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; - DnsTransaction *t = NULL; - Manager *m = userdata; - DnsScope *scope; - int r; - - r = manager_recv(m, fd, DNS_PROTOCOL_LLMNR, &p); - if (r <= 0) - return r; - - scope = manager_find_scope(m, p); - if (!scope) { - log_warning("Got LLMNR UDP packet on unknown scope. Ignoring."); - return 0; - } - - if (dns_packet_validate_reply(p) > 0) { - log_debug("Got reply packet for id %u", DNS_PACKET_ID(p)); - - dns_scope_check_conflicts(scope, p); - - t = hashmap_get(m->dns_transactions, UINT_TO_PTR(DNS_PACKET_ID(p))); - if (t) - dns_transaction_process_reply(t, p); - - } else if (dns_packet_validate_query(p) > 0) { - log_debug("Got query packet for id %u", DNS_PACKET_ID(p)); - - dns_scope_process_query(scope, NULL, p); - } else - log_debug("Invalid LLMNR UDP packet."); - - return 0; -} - -int manager_llmnr_ipv4_udp_fd(Manager *m) { - union sockaddr_union sa = { - .in.sin_family = AF_INET, - .in.sin_port = htobe16(5355), - }; - static const int one = 1, pmtu = IP_PMTUDISC_DONT, ttl = 255; - int r; - - assert(m); - - if (m->llmnr_ipv4_udp_fd >= 0) - return m->llmnr_ipv4_udp_fd; - - m->llmnr_ipv4_udp_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (m->llmnr_ipv4_udp_fd < 0) - return -errno; - - /* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */ - r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv4_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - /* Disable Don't-Fragment bit in the IP header */ - r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = bind(m->llmnr_ipv4_udp_fd, &sa.sa, sizeof(sa.in)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = sd_event_add_io(m->event, &m->llmnr_ipv4_udp_event_source, m->llmnr_ipv4_udp_fd, EPOLLIN, on_llmnr_packet, m); - if (r < 0) - goto fail; - - return m->llmnr_ipv4_udp_fd; - -fail: - m->llmnr_ipv4_udp_fd = safe_close(m->llmnr_ipv4_udp_fd); - return r; -} - -int manager_llmnr_ipv6_udp_fd(Manager *m) { - union sockaddr_union sa = { - .in6.sin6_family = AF_INET6, - .in6.sin6_port = htobe16(5355), - }; - static const int one = 1, ttl = 255; - int r; - - assert(m); - - if (m->llmnr_ipv6_udp_fd >= 0) - return m->llmnr_ipv6_udp_fd; - - m->llmnr_ipv6_udp_fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (m->llmnr_ipv6_udp_fd < 0) - return -errno; - - r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)); - if (r < 0) { - r = -errno; - goto fail; - } - - /* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */ - r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv6_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = bind(m->llmnr_ipv6_udp_fd, &sa.sa, sizeof(sa.in6)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = sd_event_add_io(m->event, &m->llmnr_ipv6_udp_event_source, m->llmnr_ipv6_udp_fd, EPOLLIN, on_llmnr_packet, m); - if (r < 0) { - r = -errno; - goto fail; - } - - return m->llmnr_ipv6_udp_fd; - -fail: - m->llmnr_ipv6_udp_fd = safe_close(m->llmnr_ipv6_udp_fd); - return r; -} - -static int on_llmnr_stream_packet(DnsStream *s) { - DnsScope *scope; - - assert(s); - - scope = manager_find_scope(s->manager, s->read_packet); - if (!scope) { - log_warning("Got LLMNR TCP packet on unknown scope. Ignroing."); - return 0; - } - - if (dns_packet_validate_query(s->read_packet) > 0) { - log_debug("Got query packet for id %u", DNS_PACKET_ID(s->read_packet)); - - dns_scope_process_query(scope, s, s->read_packet); - - /* If no reply packet was set, we free the stream */ - if (s->write_packet) - return 0; - } else - log_debug("Invalid LLMNR TCP packet."); - - dns_stream_free(s); - return 0; -} - -static int on_llmnr_stream(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - DnsStream *stream; - Manager *m = userdata; - int cfd, r; - - cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); - if (cfd < 0) { - if (errno == EAGAIN || errno == EINTR) - return 0; - - return -errno; - } - - r = dns_stream_new(m, &stream, DNS_PROTOCOL_LLMNR, cfd); - if (r < 0) { - safe_close(cfd); - return r; - } - - stream->on_packet = on_llmnr_stream_packet; - return 0; -} - -int manager_llmnr_ipv4_tcp_fd(Manager *m) { - union sockaddr_union sa = { - .in.sin_family = AF_INET, - .in.sin_port = htobe16(5355), - }; - static const int one = 1, pmtu = IP_PMTUDISC_DONT; - int r; - - assert(m); - - if (m->llmnr_ipv4_tcp_fd >= 0) - return m->llmnr_ipv4_tcp_fd; - - m->llmnr_ipv4_tcp_fd = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (m->llmnr_ipv4_tcp_fd < 0) - return -errno; - - /* RFC 4795, section 2.5. requires setting the TTL of TCP streams to 1 */ - r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_TTL, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv4_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - /* Disable Don't-Fragment bit in the IP header */ - r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = bind(m->llmnr_ipv4_tcp_fd, &sa.sa, sizeof(sa.in)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = listen(m->llmnr_ipv4_tcp_fd, SOMAXCONN); - if (r < 0) { - r = -errno; - goto fail; - } - - r = sd_event_add_io(m->event, &m->llmnr_ipv4_tcp_event_source, m->llmnr_ipv4_tcp_fd, EPOLLIN, on_llmnr_stream, m); - if (r < 0) - goto fail; - - return m->llmnr_ipv4_tcp_fd; - -fail: - m->llmnr_ipv4_tcp_fd = safe_close(m->llmnr_ipv4_tcp_fd); - return r; -} - -int manager_llmnr_ipv6_tcp_fd(Manager *m) { - union sockaddr_union sa = { - .in6.sin6_family = AF_INET6, - .in6.sin6_port = htobe16(5355), - }; - static const int one = 1; - int r; - - assert(m); - - if (m->llmnr_ipv6_tcp_fd >= 0) - return m->llmnr_ipv6_tcp_fd; - - m->llmnr_ipv6_tcp_fd = socket(AF_INET6, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (m->llmnr_ipv6_tcp_fd < 0) - return -errno; - - /* RFC 4795, section 2.5. requires setting the TTL of TCP streams to 1 */ - r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv6_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = bind(m->llmnr_ipv6_tcp_fd, &sa.sa, sizeof(sa.in6)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = listen(m->llmnr_ipv6_tcp_fd, SOMAXCONN); - if (r < 0) { - r = -errno; - goto fail; - } - - r = sd_event_add_io(m->event, &m->llmnr_ipv6_tcp_event_source, m->llmnr_ipv6_tcp_fd, EPOLLIN, on_llmnr_stream, m); - if (r < 0) { - r = -errno; - goto fail; - } - - return m->llmnr_ipv6_tcp_fd; - -fail: - m->llmnr_ipv6_tcp_fd = safe_close(m->llmnr_ipv6_tcp_fd); - return r; -} - -int manager_find_ifindex(Manager *m, int family, const union in_addr_union *in_addr) { - LinkAddress *a; - - assert(m); - - a = manager_find_link_address(m, family, in_addr); - if (a) - return a->link->ifindex; - - return 0; -} - -void manager_refresh_rrs(Manager *m) { - Iterator i; - Link *l; - - assert(m); - - m->host_ipv4_key = dns_resource_key_unref(m->host_ipv4_key); - m->host_ipv6_key = dns_resource_key_unref(m->host_ipv6_key); - - HASHMAP_FOREACH(l, m->links, i) { - link_add_rrs(l, true); - link_add_rrs(l, false); - } -} - -int manager_next_hostname(Manager *m) { - const char *p; - uint64_t u, a; - char *h; - - assert(m); - - p = strchr(m->hostname, 0); - assert(p); - - while (p > m->hostname) { - if (!strchr("0123456789", p[-1])) - break; - - p--; - } - - if (*p == 0 || safe_atou64(p, &u) < 0 || u <= 0) - u = 1; - - /* Add a random number to the old value. This way we can avoid - * that two hosts pick the same hostname, win on IPv4 and lose - * on IPv6 (or vice versa), and pick the same hostname - * replacement hostname, ad infinitum. We still want the - * numbers to go up monotonically, hence we just add a random - * value 1..10 */ - - random_bytes(&a, sizeof(a)); - u += 1 + a % 10; - - if (asprintf(&h, "%.*s%" PRIu64, (int) (p - m->hostname), m->hostname, u) < 0) - return -ENOMEM; - - log_info("Hostname conflict, changing published hostname from '%s' to '%s'.", m->hostname, h); - - free(m->hostname); - m->hostname = h; - - manager_refresh_rrs(m); - - return 0; -} - -LinkAddress* manager_find_link_address(Manager *m, int family, const union in_addr_union *in_addr) { - Iterator i; - Link *l; - - assert(m); - - HASHMAP_FOREACH(l, m->links, i) { - LinkAddress *a; - - a = link_find_address(l, family, in_addr); - if (a) - return a; - } - - return NULL; -} - -bool manager_our_packet(Manager *m, DnsPacket *p) { - assert(m); - assert(p); - - return !!manager_find_link_address(m, p->family, &p->sender); -} - -DnsScope* manager_find_scope(Manager *m, DnsPacket *p) { - Link *l; - - assert(m); - assert(p); - - l = hashmap_get(m->links, INT_TO_PTR(p->ifindex)); - if (!l) - return NULL; - - if (p->protocol == DNS_PROTOCOL_LLMNR) { - if (p->family == AF_INET) - return l->llmnr_ipv4_scope; - else if (p->family == AF_INET6) - return l->llmnr_ipv6_scope; - } - - return NULL; -} - -void manager_verify_all(Manager *m) { - DnsScope *s; - - assert(m); - - LIST_FOREACH(scopes, s, m->dns_scopes) - dns_zone_verify_all(&s->zone); -} - -void manager_flush_dns_servers(Manager *m, DnsServerType t) { - assert(m); - - if (t == DNS_SERVER_SYSTEM) - while (m->dns_servers) - dns_server_free(m->dns_servers); - - if (t == DNS_SERVER_FALLBACK) - while (m->fallback_dns_servers) - dns_server_free(m->fallback_dns_servers); -} - -static const char* const support_table[_SUPPORT_MAX] = { - [SUPPORT_NO] = "no", - [SUPPORT_YES] = "yes", - [SUPPORT_RESOLVE] = "resolve", -}; -DEFINE_STRING_TABLE_LOOKUP(support, Support); diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h deleted file mode 100644 index 5a581cc13a..0000000000 --- a/src/resolve/resolved-manager.h +++ /dev/null @@ -1,156 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Tom Gundersen <teg@jklm.no> - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include "sd-event.h" -#include "sd-network.h" -#include "sd-rtnl.h" -#include "list.h" -#include "hashmap.h" - -typedef struct Manager Manager; -typedef enum Support Support; - -enum Support { - SUPPORT_NO, - SUPPORT_YES, - SUPPORT_RESOLVE, - _SUPPORT_MAX, - _SUPPORT_INVALID = -1 -}; - -#include "resolved-dns-query.h" -#include "resolved-dns-stream.h" -#include "resolved-link.h" - -struct Manager { - sd_event *event; - - Support llmnr_support; - - /* Network */ - Hashmap *links; - - sd_rtnl *rtnl; - sd_event_source *rtnl_event_source; - - sd_network_monitor *network_monitor; - sd_event_source *network_event_source; - - /* DNS query management */ - Hashmap *dns_transactions; - LIST_HEAD(DnsQuery, dns_queries); - unsigned n_dns_queries; - - LIST_HEAD(DnsStream, dns_streams); - unsigned n_dns_streams; - - /* Unicast dns */ - int dns_ipv4_fd; - int dns_ipv6_fd; - - sd_event_source *dns_ipv4_event_source; - sd_event_source *dns_ipv6_event_source; - - LIST_HEAD(DnsServer, dns_servers); - LIST_HEAD(DnsServer, fallback_dns_servers); - DnsServer *current_dns_server; - - bool read_resolv_conf; - usec_t resolv_conf_mtime; - - LIST_HEAD(DnsScope, dns_scopes); - DnsScope *unicast_scope; - - /* LLMNR */ - int llmnr_ipv4_udp_fd; - int llmnr_ipv6_udp_fd; - int llmnr_ipv4_tcp_fd; - int llmnr_ipv6_tcp_fd; - - sd_event_source *llmnr_ipv4_udp_event_source; - sd_event_source *llmnr_ipv6_udp_event_source; - sd_event_source *llmnr_ipv4_tcp_event_source; - sd_event_source *llmnr_ipv6_tcp_event_source; - - /* dbus */ - sd_bus *bus; - sd_event_source *bus_retry_event_source; - - /* The hostname we publish on LLMNR and mDNS */ - char *hostname; - DnsResourceKey *host_ipv4_key; - DnsResourceKey *host_ipv6_key; - - /* Watch the system hostname */ - int hostname_fd; - sd_event_source *hostname_event_source; - - /* Watch for system suspends */ - sd_bus_slot *prepare_for_sleep_slot; -}; - -/* Manager */ - -int manager_new(Manager **ret); -Manager* manager_free(Manager *m); - -int manager_start(Manager *m); -int manager_read_resolv_conf(Manager *m); -int manager_write_resolv_conf(Manager *m); - -DnsServer *manager_set_dns_server(Manager *m, DnsServer *s); -DnsServer *manager_find_dns_server(Manager *m, int family, const union in_addr_union *in_addr); -DnsServer *manager_get_dns_server(Manager *m); -void manager_next_dns_server(Manager *m); - -uint32_t manager_find_mtu(Manager *m); - -int manager_send(Manager *m, int fd, int ifindex, int family, const union in_addr_union *addr, uint16_t port, DnsPacket *p); -int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret); - -int manager_dns_ipv4_fd(Manager *m); -int manager_dns_ipv6_fd(Manager *m); -int manager_llmnr_ipv4_udp_fd(Manager *m); -int manager_llmnr_ipv6_udp_fd(Manager *m); -int manager_llmnr_ipv4_tcp_fd(Manager *m); -int manager_llmnr_ipv6_tcp_fd(Manager *m); - -int manager_find_ifindex(Manager *m, int family, const union in_addr_union *in_addr); -LinkAddress* manager_find_link_address(Manager *m, int family, const union in_addr_union *in_addr); - -void manager_refresh_rrs(Manager *m); -int manager_next_hostname(Manager *m); - -bool manager_our_packet(Manager *m, DnsPacket *p); -DnsScope* manager_find_scope(Manager *m, DnsPacket *p); - -void manager_verify_all(Manager *m); - -void manager_flush_dns_servers(Manager *m, DnsServerType t); - -DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); - -#define EXTRA_CMSG_SPACE 1024 - -const char* support_to_string(Support p) _const_; -int support_from_string(const char *s) _pure_; diff --git a/src/resolve/resolved.c b/src/resolve/resolved.c deleted file mode 100644 index 271247ca3f..0000000000 --- a/src/resolve/resolved.c +++ /dev/null @@ -1,115 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2014 Tom Gundersen <teg@jklm.no> - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include "sd-event.h" -#include "sd-daemon.h" -#include "mkdir.h" -#include "capability.h" -#include "selinux-util.h" - -#include "resolved-manager.h" -#include "resolved-conf.h" - -int main(int argc, char *argv[]) { - _cleanup_(manager_freep) Manager *m = NULL; - const char *user = "systemd-resolve"; - uid_t uid; - gid_t gid; - int r; - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - if (argc != 1) { - log_error("This program takes no arguments."); - r = -EINVAL; - goto finish; - } - - umask(0022); - - r = mac_selinux_init(NULL); - if (r < 0) { - log_error_errno(r, "SELinux setup failed: %m"); - goto finish; - } - - r = get_user_creds(&user, &uid, &gid, NULL, NULL); - if (r < 0) { - log_error_errno(r, "Cannot resolve user name %s: %m", user); - goto finish; - } - - /* Always create the directory where resolv.conf will live */ - r = mkdir_safe_label("/run/systemd/resolve", 0755, uid, gid); - if (r < 0) { - log_error_errno(r, "Could not create runtime directory: %m"); - goto finish; - } - - r = drop_privileges(uid, gid, 0); - if (r < 0) - goto finish; - - assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0); - - r = manager_new(&m); - if (r < 0) { - log_error_errno(r, "Could not create manager: %m"); - goto finish; - } - - r = manager_parse_config_file(m); - if (r < 0) - log_warning_errno(r, "Failed to parse configuration file: %m"); - - r = manager_start(m); - if (r < 0) { - log_error_errno(r, "Failed to start manager: %m"); - goto finish; - } - - /* Write finish default resolv.conf to avoid a dangling - * symlink */ - r = manager_write_resolv_conf(m); - if (r < 0) - log_warning_errno(r, "Could not create resolv.conf: %m"); - - sd_notify(false, - "READY=1\n" - "STATUS=Processing requests..."); - - r = sd_event_loop(m->event); - if (r < 0) { - log_error_errno(r, "Event loop failed: %m"); - goto finish; - } - - sd_event_get_exit_code(m->event, &r); - -finish: - sd_notify(false, - "STOPPING=1\n" - "STATUS=Shutting down..."); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/resolve/resolved.conf.in b/src/resolve/resolved.conf.in deleted file mode 100644 index 3eb19e42b7..0000000000 --- a/src/resolve/resolved.conf.in +++ /dev/null @@ -1,17 +0,0 @@ -# This file is part of systemd. -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. -# -# Entries in this file show the compile time defaults. -# You can change settings by editing this file. -# Defaults can be restored by simply deleting this file. -# -# See resolved.conf(5) for details - -[Resolve] -#DNS= -#FallbackDNS=@DNS_SERVERS@ -#LLMNR=yes diff --git a/src/resolve/test-dns-domain.c b/src/resolve/test-dns-domain.c deleted file mode 100644 index c3208abc78..0000000000 --- a/src/resolve/test-dns-domain.c +++ /dev/null @@ -1,192 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. - ***/ - -#include "macro.h" -#include "resolved-dns-domain.h" - -static void test_dns_label_unescape_one(const char *what, const char *expect, size_t buffer_sz, int ret) { - char buffer[buffer_sz]; - int r; - - r = dns_label_unescape(&what, buffer, buffer_sz); - assert_se(r == ret); - - if (r < 0) - return; - - assert_se(streq(buffer, expect)); -} - -static void test_dns_label_unescape(void) { - test_dns_label_unescape_one("hallo", "hallo", 6, 5); - test_dns_label_unescape_one("hallo", "hallo", 4, -ENOSPC); - test_dns_label_unescape_one("", "", 10, 0); - test_dns_label_unescape_one("hallo\\.foobar", "hallo.foobar", 20, 12); - test_dns_label_unescape_one("hallo.foobar", "hallo", 10, 5); - test_dns_label_unescape_one("hallo\n.foobar", "hallo", 20, -EINVAL); - test_dns_label_unescape_one("hallo\\", "hallo", 20, -EINVAL); - test_dns_label_unescape_one("hallo\\032 ", "hallo ", 20, 7); - test_dns_label_unescape_one(".", "", 20, 0); - test_dns_label_unescape_one("..", "", 20, -EINVAL); - test_dns_label_unescape_one(".foobar", "", 20, -EINVAL); - test_dns_label_unescape_one("foobar.", "foobar", 20, 6); -} - -static void test_dns_label_escape_one(const char *what, size_t l, const char *expect, int ret) { - _cleanup_free_ char *t = NULL; - int r; - - r = dns_label_escape(what, l, &t); - assert_se(r == ret); - - if (r < 0) - return; - - assert_se(streq_ptr(expect, t)); -} - -static void test_dns_label_escape(void) { - test_dns_label_escape_one("", 0, "", 0); - test_dns_label_escape_one("hallo", 5, "hallo", 5); - test_dns_label_escape_one("hallo", 6, NULL, -EINVAL); - test_dns_label_escape_one("hallo hallo.foobar,waldi", 24, "hallo\\032hallo\\.foobar\\044waldi", 31); -} - -static void test_dns_name_normalize_one(const char *what, const char *expect, int ret) { - _cleanup_free_ char *t = NULL; - int r; - - r = dns_name_normalize(what, &t); - assert_se(r == ret); - - if (r < 0) - return; - - assert_se(streq_ptr(expect, t)); -} - -static void test_dns_name_normalize(void) { - test_dns_name_normalize_one("", "", 0); - test_dns_name_normalize_one("f", "f", 0); - test_dns_name_normalize_one("f.waldi", "f.waldi", 0); - test_dns_name_normalize_one("f \\032.waldi", "f\\032\\032.waldi", 0); - test_dns_name_normalize_one("\\000", NULL, -EINVAL); - test_dns_name_normalize_one("..", NULL, -EINVAL); - test_dns_name_normalize_one(".foobar", NULL, -EINVAL); - test_dns_name_normalize_one("foobar.", "foobar", 0); - test_dns_name_normalize_one(".", "", 0); -} - -static void test_dns_name_equal_one(const char *a, const char *b, int ret) { - int r; - - r = dns_name_equal(a, b); - assert_se(r == ret); - - r = dns_name_equal(b, a); - assert_se(r == ret); -} - -static void test_dns_name_equal(void) { - test_dns_name_equal_one("", "", true); - test_dns_name_equal_one("x", "x", true); - test_dns_name_equal_one("x", "x.", true); - test_dns_name_equal_one("abc.def", "abc.def", true); - test_dns_name_equal_one("abc.def", "ABC.def", true); - test_dns_name_equal_one("abc.def", "CBA.def", false); - test_dns_name_equal_one("", "xxx", false); - test_dns_name_equal_one("ab", "a", false); - test_dns_name_equal_one("\\000", "xxxx", -EINVAL); - test_dns_name_equal_one(".", "", true); - test_dns_name_equal_one(".", ".", true); - test_dns_name_equal_one("..", "..", -EINVAL); -} - -static void test_dns_name_endswith_one(const char *a, const char *b, int ret) { - assert_se(dns_name_endswith(a, b) == ret); -} - -static void test_dns_name_endswith(void) { - test_dns_name_endswith_one("", "", true); - test_dns_name_endswith_one("", "xxx", false); - test_dns_name_endswith_one("xxx", "", true); - test_dns_name_endswith_one("x", "x", true); - test_dns_name_endswith_one("x", "y", false); - test_dns_name_endswith_one("x.y", "y", true); - test_dns_name_endswith_one("x.y", "Y", true); - test_dns_name_endswith_one("x.y", "x", false); - test_dns_name_endswith_one("x.y.z", "Z", true); - test_dns_name_endswith_one("x.y.z", "y.Z", true); - test_dns_name_endswith_one("x.y.z", "x.y.Z", true); - test_dns_name_endswith_one("x.y.z", "waldo", false); - test_dns_name_endswith_one("x.y.z.u.v.w", "y.z", false); - test_dns_name_endswith_one("x.y.z.u.v.w", "u.v.w", true); - test_dns_name_endswith_one("x.y\001.z", "waldo", -EINVAL); -} - -static void test_dns_name_root(void) { - assert_se(dns_name_root("") == true); - assert_se(dns_name_root(".") == true); - assert_se(dns_name_root("xxx") == false); - assert_se(dns_name_root("xxx.") == false); - assert_se(dns_name_root("..") == -EINVAL); -} - -static void test_dns_name_single_label(void) { - assert_se(dns_name_single_label("") == false); - assert_se(dns_name_single_label(".") == false); - assert_se(dns_name_single_label("..") == -EINVAL); - assert_se(dns_name_single_label("x") == true); - assert_se(dns_name_single_label("x.") == true); - assert_se(dns_name_single_label("xx.yy") == false); -} - -static void test_dns_name_reverse_one(const char *address, const char *name) { - _cleanup_free_ char *p = NULL; - union in_addr_union a, b = {}; - int familya, familyb; - - assert_se(in_addr_from_string_auto(address, &familya, &a) >= 0); - assert_se(dns_name_reverse(familya, &a, &p) >= 0); - assert_se(streq(p, name)); - assert_se(dns_name_address(p, &familyb, &b) > 0); - assert_se(familya == familyb); - assert_se(in_addr_equal(familya, &a, &b)); -} - -static void test_dns_name_reverse(void) { - test_dns_name_reverse_one("47.11.8.15", "15.8.11.47.in-addr.arpa"); - test_dns_name_reverse_one("fe80::47", "7.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.e.f.ip6.arpa"); -} - -int main(int argc, char *argv[]) { - - test_dns_label_unescape(); - test_dns_label_escape(); - test_dns_name_normalize(); - test_dns_name_equal(); - test_dns_name_endswith(); - test_dns_name_root(); - test_dns_name_single_label(); - test_dns_name_reverse(); - - return 0; -} |