2017-11-18 17:09:20 +01:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
2014-07-16 00:26:02 +02:00
|
|
|
#pragma once
|
|
|
|
|
2014-07-18 13:59:49 +02:00
|
|
|
#include "in-addr-util.h"
|
|
|
|
|
2014-07-16 00:26:02 +02:00
|
|
|
typedef struct DnsServer DnsServer;
|
|
|
|
|
2014-08-01 16:04:12 +02:00
|
|
|
typedef enum DnsServerType {
|
|
|
|
DNS_SERVER_SYSTEM,
|
|
|
|
DNS_SERVER_FALLBACK,
|
|
|
|
DNS_SERVER_LINK,
|
2018-07-23 18:23:41 +02:00
|
|
|
_DNS_SERVER_TYPE_MAX,
|
|
|
|
_DNS_SERVER_TYPE_INVALID = -1
|
2014-08-01 16:04:12 +02:00
|
|
|
} DnsServerType;
|
2016-01-29 00:24:27 +01:00
|
|
|
|
|
|
|
const char* dns_server_type_to_string(DnsServerType i) _const_;
|
|
|
|
DnsServerType dns_server_type_from_string(const char *s) _pure_;
|
2014-08-01 16:04:12 +02:00
|
|
|
|
resolved: fallback to TCP if UDP fails
This is inspired by the logic in BIND [0], follow-up patches
will implement the reset of that scheme.
If we get a server error back, or if after several attempts we don't
get a reply at all, we switch from UDP to TCP for the given
server for the current and all subsequent requests. However, if
we ever successfully received a reply over UDP, we never fall
back to TCP, and once a grace-period has passed, we try to upgrade
again to using UDP. The grace-period starts off at five minutes
after the current feature level was verified and then grows
exponentially to six hours. This is to mitigate problems due
to temporary lack of network connectivity, but at the same time
avoid flooding the network with retries when the feature attempted
feature level genuinely does not work.
Note that UDP is likely much more commonly supported than TCP,
but depending on the path between the client and the server, we
may have more luck with TCP in case something is wrong. We really
do prefer UDP though, as that is much more lightweight, that is
why TCP is only the last resort.
[0]: <https://kb.isc.org/article/AA-01219/0/Refinements-to-EDNS-fallback-behavior-can-cause-different-outcomes-in-Recursive-Servers.html>
2015-07-06 08:15:25 +02:00
|
|
|
typedef enum DnsServerFeatureLevel {
|
|
|
|
DNS_SERVER_FEATURE_LEVEL_TCP,
|
|
|
|
DNS_SERVER_FEATURE_LEVEL_UDP,
|
2015-06-23 23:06:09 +02:00
|
|
|
DNS_SERVER_FEATURE_LEVEL_EDNS0,
|
2018-04-27 17:50:38 +02:00
|
|
|
DNS_SERVER_FEATURE_LEVEL_TLS_PLAIN,
|
2015-06-24 15:08:40 +02:00
|
|
|
DNS_SERVER_FEATURE_LEVEL_DO,
|
resolved: announce support for large UDP packets
This is often needed for proper DNSSEC support, and even to handle AAAA records
without falling back to TCP.
If the path between the client and server is fully compliant, this should always
work, however, that is not the case, and overlarge packets will get mysteriously
lost in some cases.
For that reason, we use a similar fallback mechanism as we do for palin EDNS0,
EDNS0+DO, etc.:
The large UDP size feature is different from the other supported feature, as we
cannot simply verify that it works based on receiving a reply (as the server
will usually send us much smaller packets than what we claim to support, so
simply receiving a reply does not mean much).
For that reason, we keep track of the largest UDP packet we ever received, as this
is the smallest known good size (defaulting to the standard 512 bytes). If
announcing the default large size of 4096 fails (in the same way as the other
features), we fall back to the known good size. The same logic of retrying after a
grace-period applies.
2015-07-06 16:48:24 +02:00
|
|
|
DNS_SERVER_FEATURE_LEVEL_LARGE,
|
2018-04-27 17:50:38 +02:00
|
|
|
DNS_SERVER_FEATURE_LEVEL_TLS_DO,
|
resolved: fallback to TCP if UDP fails
This is inspired by the logic in BIND [0], follow-up patches
will implement the reset of that scheme.
If we get a server error back, or if after several attempts we don't
get a reply at all, we switch from UDP to TCP for the given
server for the current and all subsequent requests. However, if
we ever successfully received a reply over UDP, we never fall
back to TCP, and once a grace-period has passed, we try to upgrade
again to using UDP. The grace-period starts off at five minutes
after the current feature level was verified and then grows
exponentially to six hours. This is to mitigate problems due
to temporary lack of network connectivity, but at the same time
avoid flooding the network with retries when the feature attempted
feature level genuinely does not work.
Note that UDP is likely much more commonly supported than TCP,
but depending on the path between the client and the server, we
may have more luck with TCP in case something is wrong. We really
do prefer UDP though, as that is much more lightweight, that is
why TCP is only the last resort.
[0]: <https://kb.isc.org/article/AA-01219/0/Refinements-to-EDNS-fallback-behavior-can-cause-different-outcomes-in-Recursive-Servers.html>
2015-07-06 08:15:25 +02:00
|
|
|
_DNS_SERVER_FEATURE_LEVEL_MAX,
|
|
|
|
_DNS_SERVER_FEATURE_LEVEL_INVALID = -1
|
|
|
|
} DnsServerFeatureLevel;
|
|
|
|
|
|
|
|
#define DNS_SERVER_FEATURE_LEVEL_WORST 0
|
|
|
|
#define DNS_SERVER_FEATURE_LEVEL_BEST (_DNS_SERVER_FEATURE_LEVEL_MAX - 1)
|
2018-04-27 17:50:38 +02:00
|
|
|
#define DNS_SERVER_FEATURE_LEVEL_IS_TLS(x) IN_SET(x, DNS_SERVER_FEATURE_LEVEL_TLS_PLAIN, DNS_SERVER_FEATURE_LEVEL_TLS_DO)
|
resolved: fallback to TCP if UDP fails
This is inspired by the logic in BIND [0], follow-up patches
will implement the reset of that scheme.
If we get a server error back, or if after several attempts we don't
get a reply at all, we switch from UDP to TCP for the given
server for the current and all subsequent requests. However, if
we ever successfully received a reply over UDP, we never fall
back to TCP, and once a grace-period has passed, we try to upgrade
again to using UDP. The grace-period starts off at five minutes
after the current feature level was verified and then grows
exponentially to six hours. This is to mitigate problems due
to temporary lack of network connectivity, but at the same time
avoid flooding the network with retries when the feature attempted
feature level genuinely does not work.
Note that UDP is likely much more commonly supported than TCP,
but depending on the path between the client and the server, we
may have more luck with TCP in case something is wrong. We really
do prefer UDP though, as that is much more lightweight, that is
why TCP is only the last resort.
[0]: <https://kb.isc.org/article/AA-01219/0/Refinements-to-EDNS-fallback-behavior-can-cause-different-outcomes-in-Recursive-Servers.html>
2015-07-06 08:15:25 +02:00
|
|
|
|
|
|
|
const char* dns_server_feature_level_to_string(int i) _const_;
|
|
|
|
int dns_server_feature_level_from_string(const char *s) _pure_;
|
|
|
|
|
2014-08-12 19:32:55 +02:00
|
|
|
#include "resolved-link.h"
|
resolved: fallback to TCP if UDP fails
This is inspired by the logic in BIND [0], follow-up patches
will implement the reset of that scheme.
If we get a server error back, or if after several attempts we don't
get a reply at all, we switch from UDP to TCP for the given
server for the current and all subsequent requests. However, if
we ever successfully received a reply over UDP, we never fall
back to TCP, and once a grace-period has passed, we try to upgrade
again to using UDP. The grace-period starts off at five minutes
after the current feature level was verified and then grows
exponentially to six hours. This is to mitigate problems due
to temporary lack of network connectivity, but at the same time
avoid flooding the network with retries when the feature attempted
feature level genuinely does not work.
Note that UDP is likely much more commonly supported than TCP,
but depending on the path between the client and the server, we
may have more luck with TCP in case something is wrong. We really
do prefer UDP though, as that is much more lightweight, that is
why TCP is only the last resort.
[0]: <https://kb.isc.org/article/AA-01219/0/Refinements-to-EDNS-fallback-behavior-can-cause-different-outcomes-in-Recursive-Servers.html>
2015-07-06 08:15:25 +02:00
|
|
|
#include "resolved-manager.h"
|
2018-07-17 08:34:29 +02:00
|
|
|
#if ENABLE_DNS_OVER_TLS
|
|
|
|
#include "resolved-dnstls.h"
|
|
|
|
#endif
|
2014-08-12 19:32:55 +02:00
|
|
|
|
2014-07-16 00:26:02 +02:00
|
|
|
struct DnsServer {
|
|
|
|
Manager *manager;
|
|
|
|
|
2015-06-24 18:41:46 +02:00
|
|
|
unsigned n_ref;
|
|
|
|
|
2014-08-01 16:04:12 +02:00
|
|
|
DnsServerType type;
|
2014-07-18 13:59:49 +02:00
|
|
|
Link *link;
|
|
|
|
|
2014-07-18 16:09:30 +02:00
|
|
|
int family;
|
2014-07-16 00:26:02 +02:00
|
|
|
union in_addr_union address;
|
2016-06-03 21:29:14 +02:00
|
|
|
int ifindex; /* for IPv6 link-local DNS servers */
|
2014-07-16 00:26:02 +02:00
|
|
|
|
2016-01-08 20:59:03 +01:00
|
|
|
char *server_string;
|
2018-04-22 15:23:45 +02:00
|
|
|
DnsStream *stream;
|
2016-01-08 20:59:03 +01:00
|
|
|
|
2018-06-20 18:29:49 +02:00
|
|
|
#if ENABLE_DNS_OVER_TLS
|
2018-07-17 08:34:29 +02:00
|
|
|
DnsTlsServerData dnstls_data;
|
2018-04-27 17:50:38 +02:00
|
|
|
#endif
|
|
|
|
|
2015-12-27 01:35:00 +01:00
|
|
|
DnsServerFeatureLevel verified_feature_level;
|
|
|
|
DnsServerFeatureLevel possible_feature_level;
|
2016-01-15 19:23:51 +01:00
|
|
|
|
resolved: announce support for large UDP packets
This is often needed for proper DNSSEC support, and even to handle AAAA records
without falling back to TCP.
If the path between the client and server is fully compliant, this should always
work, however, that is not the case, and overlarge packets will get mysteriously
lost in some cases.
For that reason, we use a similar fallback mechanism as we do for palin EDNS0,
EDNS0+DO, etc.:
The large UDP size feature is different from the other supported feature, as we
cannot simply verify that it works based on receiving a reply (as the server
will usually send us much smaller packets than what we claim to support, so
simply receiving a reply does not mean much).
For that reason, we keep track of the largest UDP packet we ever received, as this
is the smallest known good size (defaulting to the standard 512 bytes). If
announcing the default large size of 4096 fails (in the same way as the other
features), we fall back to the known good size. The same logic of retrying after a
grace-period applies.
2015-07-06 16:48:24 +02:00
|
|
|
size_t received_udp_packet_max;
|
2016-01-15 19:23:51 +01:00
|
|
|
|
2016-01-08 18:50:41 +01:00
|
|
|
unsigned n_failed_udp;
|
|
|
|
unsigned n_failed_tcp;
|
2018-04-27 17:50:38 +02:00
|
|
|
unsigned n_failed_tls;
|
2016-01-15 19:23:51 +01:00
|
|
|
|
2016-01-08 18:50:41 +01:00
|
|
|
bool packet_truncated:1;
|
2016-01-15 19:23:51 +01:00
|
|
|
bool packet_bad_opt:1;
|
|
|
|
bool packet_rrsig_missing:1;
|
|
|
|
|
resolved: fallback to TCP if UDP fails
This is inspired by the logic in BIND [0], follow-up patches
will implement the reset of that scheme.
If we get a server error back, or if after several attempts we don't
get a reply at all, we switch from UDP to TCP for the given
server for the current and all subsequent requests. However, if
we ever successfully received a reply over UDP, we never fall
back to TCP, and once a grace-period has passed, we try to upgrade
again to using UDP. The grace-period starts off at five minutes
after the current feature level was verified and then grows
exponentially to six hours. This is to mitigate problems due
to temporary lack of network connectivity, but at the same time
avoid flooding the network with retries when the feature attempted
feature level genuinely does not work.
Note that UDP is likely much more commonly supported than TCP,
but depending on the path between the client and the server, we
may have more luck with TCP in case something is wrong. We really
do prefer UDP though, as that is much more lightweight, that is
why TCP is only the last resort.
[0]: <https://kb.isc.org/article/AA-01219/0/Refinements-to-EDNS-fallback-behavior-can-cause-different-outcomes-in-Recursive-Servers.html>
2015-07-06 08:15:25 +02:00
|
|
|
usec_t verified_usec;
|
|
|
|
usec_t features_grace_period_usec;
|
2014-07-16 00:26:02 +02:00
|
|
|
|
2016-01-22 13:39:31 +01:00
|
|
|
/* Whether we already warned about downgrading to non-DNSSEC mode for this server */
|
|
|
|
bool warned_downgrade:1;
|
|
|
|
|
2015-12-25 15:05:46 +01:00
|
|
|
/* Used when GC'ing old DNS servers when configuration changes. */
|
|
|
|
bool marked:1;
|
|
|
|
|
2015-11-24 17:59:40 +01:00
|
|
|
/* If linked is set, then this server appears in the servers linked list */
|
|
|
|
bool linked:1;
|
2014-07-16 00:26:02 +02:00
|
|
|
LIST_FIELDS(DnsServer, servers);
|
|
|
|
};
|
|
|
|
|
|
|
|
int dns_server_new(
|
|
|
|
Manager *m,
|
2015-11-24 20:50:37 +01:00
|
|
|
DnsServer **ret,
|
2014-08-01 16:04:12 +02:00
|
|
|
DnsServerType type,
|
2015-11-24 20:50:37 +01:00
|
|
|
Link *link,
|
2014-07-18 16:09:30 +02:00
|
|
|
int family,
|
2016-06-03 21:29:14 +02:00
|
|
|
const union in_addr_union *address,
|
|
|
|
int ifindex);
|
2014-07-16 00:26:02 +02:00
|
|
|
|
2015-06-24 18:41:46 +02:00
|
|
|
DnsServer* dns_server_ref(DnsServer *s);
|
|
|
|
DnsServer* dns_server_unref(DnsServer *s);
|
2014-08-12 12:21:10 +02:00
|
|
|
|
2015-11-24 17:59:40 +01:00
|
|
|
void dns_server_unlink(DnsServer *s);
|
2015-11-24 20:50:37 +01:00
|
|
|
void dns_server_move_back_and_unmark(DnsServer *s);
|
2015-11-24 17:59:40 +01:00
|
|
|
|
resolve: do not derive query timeout from RTT
DNS queries need timeout values to detect whether a DNS server is
unresponsive or, if the query is sent over UDP, whether a DNS message
was lost and has to be resent. The total time that it takes to answer a
query to arrive is t + RTT, where t is the maximum time that the DNS
server that is being queried needs to answer the query.
An authoritative server stores a copy of the zone that it serves in main
memory or secondary storage, so t is very small and therefore the time
that it takes to answer a query is almost entirely determined by the
RTT. Modern authoritative server software keeps its zones in main memory
and, for example, Knot DNS and NSD are able to answer in less than
100 µs [1]. So iterative resolvers continuously measure the RTT to
optimize their query timeouts and to resend queries more quickly if they
are lost.
systemd-resolved is a stub resolver: it forwards DNS queries to an
upstream resolver and waits for an answer. So the time that it takes for
systemd-resolved to answer a query is determined by the RTT and the time
that it takes the upstream resolver to answer the query.
It seems common for iterative resolver software to set a total timeout
for the query. Such total timeout subsumes the timeout of all queries
that the iterative has to make to answer a query. For example, BIND
seems to use a default timeout of 10 s.
At the moment systemd-resolved derives its query timeout entirely from
the RTT and does not consider the query timeout of the upstream
resolver. Therefore it often mistakenly degrades the feature set of its
upstream resolvers if it takes them longer than usual to answer a query.
It has been reported to be a considerable problem in practice, in
particular if DNSSEC=yes. So the query timeout systemd-resolved should
be derived from the timeout of the upstream resolved and the RTT to the
upstream resolver.
At the moment systemd-resolved measures the RTT as the time that it
takes the upstream resolver to answer a query. This clearly leads to
incorrect measurements. In order to correctly measure the RTT
systemd-resolved would have to measure RTT separately and continuously,
for example with a query with an empty question section or a query for
the SOA RR of the root zone so that the upstream resolver would be able
to answer to query without querying another server. However, this
requires significant changes to systemd-resolved. So it seems best to
postpone them until other issues have been addressed and to set the
resend timeout to a fixed value for now.
As mentioned, BIND seems to use a timeout of 10 s, so perhaps 12 s is a
reasonable value that also accounts for common RTT values. If we assume
that the we are going to retry, it could be less. So it should be enough
to set the resend timeout to DNS_TIMEOUT_MAX_USEC as
DNS_SERVER_FEATURE_RETRY_ATTEMPTS * DNS_TIMEOUT_MAX_USEC = 15 s.
However, this will not solve the incorrect feature set degradation and
should be seen as a temporary change until systemd-resolved does
probe the feature set of an upstream resolver independently from the
actual queries.
[1] https://www.knot-dns.cz/benchmark/
2018-06-11 20:07:36 +02:00
|
|
|
void dns_server_packet_received(DnsServer *s, int protocol, DnsServerFeatureLevel level, size_t size);
|
2018-06-13 06:20:23 +02:00
|
|
|
void dns_server_packet_lost(DnsServer *s, int protocol, DnsServerFeatureLevel level);
|
2016-01-08 18:50:41 +01:00
|
|
|
void dns_server_packet_truncated(DnsServer *s, DnsServerFeatureLevel level);
|
2016-01-15 19:23:51 +01:00
|
|
|
void dns_server_packet_rrsig_missing(DnsServer *s, DnsServerFeatureLevel level);
|
|
|
|
void dns_server_packet_bad_opt(DnsServer *s, DnsServerFeatureLevel level);
|
2016-06-23 23:24:38 +02:00
|
|
|
void dns_server_packet_rcode_downgrade(DnsServer *s, DnsServerFeatureLevel level);
|
2015-07-28 02:32:24 +02:00
|
|
|
|
2015-12-27 01:35:00 +01:00
|
|
|
DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s);
|
|
|
|
|
2015-12-26 18:49:32 +01:00
|
|
|
int dns_server_adjust_opt(DnsServer *server, DnsPacket *packet, DnsServerFeatureLevel level);
|
|
|
|
|
2016-01-08 20:59:03 +01:00
|
|
|
const char *dns_server_string(DnsServer *server);
|
2016-06-03 21:29:14 +02:00
|
|
|
int dns_server_ifindex(const DnsServer *s);
|
2016-01-08 20:59:03 +01:00
|
|
|
|
2016-01-10 22:58:58 +01:00
|
|
|
bool dns_server_dnssec_supported(DnsServer *server);
|
|
|
|
|
2016-01-22 13:39:31 +01:00
|
|
|
void dns_server_warn_downgrade(DnsServer *server);
|
|
|
|
|
2016-09-30 09:30:08 +02:00
|
|
|
bool dns_server_limited_domains(DnsServer *server);
|
|
|
|
|
2016-06-03 21:29:14 +02:00
|
|
|
DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr, int ifindex);
|
2015-11-24 21:27:29 +01:00
|
|
|
|
|
|
|
void dns_server_unlink_all(DnsServer *first);
|
|
|
|
void dns_server_unlink_marked(DnsServer *first);
|
|
|
|
void dns_server_mark_all(DnsServer *first);
|
2015-11-24 17:03:12 +01:00
|
|
|
|
2015-11-24 21:27:29 +01:00
|
|
|
DnsServer *manager_get_first_dns_server(Manager *m, DnsServerType t);
|
2015-11-24 16:48:13 +01:00
|
|
|
|
2015-11-24 17:59:40 +01:00
|
|
|
DnsServer *manager_set_dns_server(Manager *m, DnsServer *s);
|
|
|
|
DnsServer *manager_get_dns_server(Manager *m);
|
|
|
|
void manager_next_dns_server(Manager *m);
|
|
|
|
|
resolved: respond to local resolver requests on 127.0.0.53:53
In order to improve compatibility with local clients that speak DNS directly
(and do not use NSS or our bus API) listen locally on 127.0.0.53:53 and process
any queries made that way.
Note that resolved does not implement a full DNS server on this port, but
simply enough to allow normal, local clients to resolve RRs through resolved.
Specifically it does not implement queries without the RD bit set (these are
requests where recursive lookups are explicitly disabled), and neither queries
with DNSSEC DO set in combination with DNSSEC CD (i.e. DNSSEC lookups with
validation turned off). It also refuses zone transfers and obsolete RR types.
All lookups done this way will be rejected with a clean error code, so that the
client side can repeat the query with a reduced feature set.
The code will set the DNSSEC AD flag however, depending on whether the data
resolved has been validated (or comes from a local, trusted source).
Lookups made via this mechanisms are propagated to LLMNR and mDNS as necessary,
but this is only partially useful as DNS packets cannot carry IP scope data
(i.e. the ifindex), and hence link-local addresses returned cannot be used
properly (and given that LLMNR/mDNS are mostly about link-local communication
this is quite a limitation). Also, given that DNS tends to use IDNA for
non-ASCII names, while LLMNR/mDNS uses UTF-8 lookups cannot be mapped 1:1.
In general this should improve compatibility with clients bypassing NSS but
it is highly recommended for clients to instead use NSS or our native bus API.
This patch also beefs up the DnsStream logic, as it reuses the code for local
TCP listening. DnsStream now provides proper reference counting for its
objects.
In order to avoid feedback loops resolved will no silently ignore 127.0.0.53
specified as DNS server when reading configuration.
resolved listens on 127.0.0.53:53 instead of 127.0.0.1:53 in order to leave
the latter free for local, external DNS servers or forwarders.
This also changes the "etc.conf" tmpfiles snippet to create a symlink from
/etc/resolv.conf to /usr/lib/systemd/resolv.conf by default, thus making this
stub the default mode of operation if /etc is not populated.
2016-06-21 00:58:47 +02:00
|
|
|
bool dns_server_address_valid(int family, const union in_addr_union *sa);
|
|
|
|
|
2017-02-08 20:35:32 +01:00
|
|
|
DnssecMode dns_server_get_dnssec_mode(DnsServer *s);
|
2018-06-13 20:26:24 +02:00
|
|
|
DnsOverTlsMode dns_server_get_dns_over_tls_mode(DnsServer *s);
|
2017-02-08 20:35:32 +01:00
|
|
|
|
2015-06-24 18:54:12 +02:00
|
|
|
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsServer*, dns_server_unref);
|
|
|
|
|
2014-08-13 01:00:18 +02:00
|
|
|
extern const struct hash_ops dns_server_hash_ops;
|
2017-02-14 18:20:34 +01:00
|
|
|
|
|
|
|
void dns_server_flush_cache(DnsServer *s);
|
2017-09-29 21:18:29 +02:00
|
|
|
|
|
|
|
void dns_server_reset_features(DnsServer *s);
|
|
|
|
void dns_server_reset_features_all(DnsServer *s);
|
2017-10-05 16:53:32 +02:00
|
|
|
|
|
|
|
void dns_server_dump(DnsServer *s, FILE *f);
|