2020-11-09 05:23:58 +01:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
2014-07-16 00:26:02 +02:00
|
|
|
#pragma once
|
|
|
|
|
2014-07-18 13:59:49 +02:00
|
|
|
#include "in-addr-util.h"
|
2020-05-06 19:10:59 +02:00
|
|
|
#include "list.h"
|
|
|
|
#include "resolve-util.h"
|
|
|
|
#include "time-util.h"
|
2014-07-18 13:59:49 +02:00
|
|
|
|
2020-05-06 19:10:59 +02:00
|
|
|
typedef struct DnsScope DnsScope;
|
2014-07-16 00:26:02 +02:00
|
|
|
typedef struct DnsServer DnsServer;
|
2020-05-06 19:10:59 +02:00
|
|
|
typedef struct DnsStream DnsStream;
|
|
|
|
typedef struct DnsPacket DnsPacket;
|
|
|
|
typedef struct Link Link;
|
|
|
|
typedef struct Manager Manager;
|
|
|
|
|
|
|
|
#include "resolved-dnstls.h"
|
2014-07-16 00:26:02 +02:00
|
|
|
|
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-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 */
|
2020-07-13 01:58:02 +02:00
|
|
|
uint16_t port;
|
|
|
|
char *server_name;
|
2014-07-16 00:26:02 +02:00
|
|
|
|
2016-01-08 20:59:03 +01:00
|
|
|
char *server_string;
|
2020-07-13 02:29:14 +02:00
|
|
|
char *server_string_full;
|
2018-12-04 20:52:43 +01:00
|
|
|
|
|
|
|
/* The long-lived stream towards this server. */
|
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,
|
2020-07-13 01:58:02 +02:00
|
|
|
uint16_t port,
|
2019-11-30 03:51:40 +01:00
|
|
|
int ifindex,
|
|
|
|
const char *server_string);
|
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);
|
2020-07-13 02:29:14 +02:00
|
|
|
const char *dns_server_string_full(DnsServer *server);
|
2016-06-03 21:29:14 +02:00
|
|
|
int dns_server_ifindex(const DnsServer *s);
|
2020-07-13 01:58:02 +02:00
|
|
|
uint16_t dns_server_port(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);
|
|
|
|
|
2020-07-13 02:05:15 +02:00
|
|
|
DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr, uint16_t port, int ifindex, const char *name);
|
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);
|
|
|
|
|
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);
|
2018-12-04 22:09:08 +01:00
|
|
|
|
|
|
|
void dns_server_unref_stream(DnsServer *s);
|
2018-12-04 12:08:18 +01:00
|
|
|
|
|
|
|
DnsScope *dns_server_scope(DnsServer *s);
|