Merge pull request #2225 from poettering/dnssec7
Seventh DNSSEC patchset
This commit is contained in:
commit
f791b677a8
|
@ -140,7 +140,8 @@ bool bitmap_isset(Bitmap *b, unsigned n) {
|
|||
bool bitmap_isclear(Bitmap *b) {
|
||||
unsigned i;
|
||||
|
||||
assert(b);
|
||||
if (!b)
|
||||
return true;
|
||||
|
||||
for (i = 0; i < b->n_bitmaps; i++)
|
||||
if (b->bitmaps[i] != 0)
|
||||
|
@ -150,7 +151,9 @@ bool bitmap_isclear(Bitmap *b) {
|
|||
}
|
||||
|
||||
void bitmap_clear(Bitmap *b) {
|
||||
assert(b);
|
||||
|
||||
if (!b)
|
||||
return;
|
||||
|
||||
b->bitmaps = mfree(b->bitmaps);
|
||||
b->n_bitmaps = 0;
|
||||
|
@ -197,7 +200,10 @@ bool bitmap_equal(Bitmap *a, Bitmap *b) {
|
|||
Bitmap *c;
|
||||
unsigned i;
|
||||
|
||||
if (!a ^ !b)
|
||||
if (a == b)
|
||||
return true;
|
||||
|
||||
if (!a != !b)
|
||||
return false;
|
||||
|
||||
if (!a)
|
||||
|
|
|
@ -72,6 +72,7 @@
|
|||
#define BUS_ERROR_NO_RESOURCES "org.freedesktop.resolve1.NoResources"
|
||||
#define BUS_ERROR_CNAME_LOOP "org.freedesktop.resolve1.CNameLoop"
|
||||
#define BUS_ERROR_ABORTED "org.freedesktop.resolve1.Aborted"
|
||||
#define BUS_ERROR_CONNECTION_FAILURE "org.freedesktop.resolve1.ConnectionFailure"
|
||||
#define _BUS_ERROR_DNS "org.freedesktop.resolve1.DnsError."
|
||||
|
||||
#define BUS_ERROR_NO_SUCH_TRANSFER "org.freedesktop.import1.NoSuchTransfer"
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "parse-util.h"
|
||||
#include "resolved-def.h"
|
||||
#include "resolved-dns-packet.h"
|
||||
#include "terminal-util.h"
|
||||
|
||||
#define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
|
||||
|
||||
|
@ -42,7 +43,14 @@ static uint16_t arg_type = 0;
|
|||
static uint16_t arg_class = 0;
|
||||
static bool arg_legend = true;
|
||||
static uint64_t arg_flags = 0;
|
||||
static bool arg_resolve_service = false;
|
||||
|
||||
static enum {
|
||||
MODE_RESOLVE_HOST,
|
||||
MODE_RESOLVE_RECORD,
|
||||
MODE_RESOLVE_SERVICE,
|
||||
MODE_STATISTICS,
|
||||
MODE_RESET_STATISTICS,
|
||||
} arg_mode = MODE_RESOLVE_HOST;
|
||||
|
||||
static void print_source(uint64_t flags, usec_t rtt) {
|
||||
char rtt_str[FORMAT_TIMESTAMP_MAX];
|
||||
|
@ -368,7 +376,7 @@ static int resolve_record(sd_bus *bus, const char *name) {
|
|||
while ((r = sd_bus_message_enter_container(reply, 'r', "iqqay")) > 0) {
|
||||
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
|
||||
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
|
||||
_cleanup_free_ char *s = NULL;
|
||||
const char *s;
|
||||
uint16_t c, t;
|
||||
int ifindex;
|
||||
const void *d;
|
||||
|
@ -399,15 +407,13 @@ static int resolve_record(sd_bus *bus, const char *name) {
|
|||
return log_oom();
|
||||
|
||||
r = dns_packet_read_rr(p, &rr, NULL, NULL);
|
||||
if (r < 0) {
|
||||
log_error("Failed to parse RR.");
|
||||
return r;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse RR.");
|
||||
|
||||
r = dns_resource_record_to_string(rr, &s);
|
||||
if (r < 0) {
|
||||
s = dns_resource_record_to_string(rr);
|
||||
if (!s) {
|
||||
log_error("Failed to format RR.");
|
||||
return r;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ifname[0] = 0;
|
||||
|
@ -639,6 +645,121 @@ static int resolve_service(sd_bus *bus, const char *name, const char *type, cons
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int show_statistics(sd_bus *bus) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||
uint64_t n_current_transactions, n_total_transactions,
|
||||
cache_size, n_cache_hit, n_cache_miss,
|
||||
n_dnssec_secure, n_dnssec_insecure, n_dnssec_bogus, n_dnssec_indeterminate;
|
||||
int r;
|
||||
|
||||
assert(bus);
|
||||
|
||||
r = sd_bus_get_property(bus,
|
||||
"org.freedesktop.resolve1",
|
||||
"/org/freedesktop/resolve1",
|
||||
"org.freedesktop.resolve1.Manager",
|
||||
"TransactionStatistics",
|
||||
&error,
|
||||
&reply,
|
||||
"(tt)");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to get transaction statistics: %s", bus_error_message(&error, r));
|
||||
|
||||
r = sd_bus_message_read(reply, "(tt)",
|
||||
&n_current_transactions,
|
||||
&n_total_transactions);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
printf("%sTransactions%s\n"
|
||||
"Current Transactions: %" PRIu64 "\n"
|
||||
" Total Transactions: %" PRIu64 "\n",
|
||||
ansi_highlight(),
|
||||
ansi_normal(),
|
||||
n_current_transactions,
|
||||
n_total_transactions);
|
||||
|
||||
reply = sd_bus_message_unref(reply);
|
||||
|
||||
r = sd_bus_get_property(bus,
|
||||
"org.freedesktop.resolve1",
|
||||
"/org/freedesktop/resolve1",
|
||||
"org.freedesktop.resolve1.Manager",
|
||||
"CacheStatistics",
|
||||
&error,
|
||||
&reply,
|
||||
"(ttt)");
|
||||
|
||||
r = sd_bus_message_read(reply, "(ttt)",
|
||||
&cache_size,
|
||||
&n_cache_hit,
|
||||
&n_cache_miss);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
printf("\n%sCache%s\n"
|
||||
" Current Cache Size: %" PRIu64 "\n"
|
||||
" Cache Hits: %" PRIu64 "\n"
|
||||
" Cache Misses: %" PRIu64 "\n",
|
||||
ansi_highlight(),
|
||||
ansi_normal(),
|
||||
cache_size,
|
||||
n_cache_hit,
|
||||
n_cache_miss);
|
||||
|
||||
reply = sd_bus_message_unref(reply);
|
||||
|
||||
r = sd_bus_get_property(bus,
|
||||
"org.freedesktop.resolve1",
|
||||
"/org/freedesktop/resolve1",
|
||||
"org.freedesktop.resolve1.Manager",
|
||||
"DNSSECStatistics",
|
||||
&error,
|
||||
&reply,
|
||||
"(tttt)");
|
||||
|
||||
r = sd_bus_message_read(reply, "(tttt)",
|
||||
&n_dnssec_secure,
|
||||
&n_dnssec_insecure,
|
||||
&n_dnssec_bogus,
|
||||
&n_dnssec_indeterminate);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
printf("\n%sDNSSEC%s\n"
|
||||
" Secure RRsets: %" PRIu64 "\n"
|
||||
" Insecure RRsets: %" PRIu64 "\n"
|
||||
" Bogus RRsets: %" PRIu64 "\n"
|
||||
"Indeterminate RRsets: %" PRIu64 "\n",
|
||||
ansi_highlight(),
|
||||
ansi_normal(),
|
||||
n_dnssec_secure,
|
||||
n_dnssec_insecure,
|
||||
n_dnssec_bogus,
|
||||
n_dnssec_indeterminate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int reset_statistics(sd_bus *bus) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
int r;
|
||||
|
||||
r = sd_bus_call_method(bus,
|
||||
"org.freedesktop.resolve1",
|
||||
"/org/freedesktop/resolve1",
|
||||
"org.freedesktop.resolve1.Manager",
|
||||
"ResetStatistics",
|
||||
&error,
|
||||
NULL,
|
||||
NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to reset statistics: %s", bus_error_message(&error, r));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void help_dns_types(void) {
|
||||
int i;
|
||||
const char *t;
|
||||
|
@ -683,6 +804,8 @@ static void help(void) {
|
|||
" --cname=BOOL Do [not] follow CNAME redirects\n"
|
||||
" --search=BOOL Do [not] use search domains\n"
|
||||
" --legend=BOOL Do [not] print column headers\n"
|
||||
" --statistics Show resolver statistics\n"
|
||||
" --reset-statistics Reset resolver statistics\n"
|
||||
, program_invocation_short_name, program_invocation_short_name);
|
||||
}
|
||||
|
||||
|
@ -695,20 +818,24 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
ARG_SERVICE_ADDRESS,
|
||||
ARG_SERVICE_TXT,
|
||||
ARG_SEARCH,
|
||||
ARG_STATISTICS,
|
||||
ARG_RESET_STATISTICS,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ "type", required_argument, NULL, 't' },
|
||||
{ "class", required_argument, NULL, 'c' },
|
||||
{ "legend", required_argument, NULL, ARG_LEGEND },
|
||||
{ "protocol", required_argument, NULL, 'p' },
|
||||
{ "cname", required_argument, NULL, ARG_CNAME },
|
||||
{ "service", no_argument, NULL, ARG_SERVICE },
|
||||
{ "service-address", required_argument, NULL, ARG_SERVICE_ADDRESS },
|
||||
{ "service-txt", required_argument, NULL, ARG_SERVICE_TXT },
|
||||
{ "search", required_argument, NULL, ARG_SEARCH },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ "type", required_argument, NULL, 't' },
|
||||
{ "class", required_argument, NULL, 'c' },
|
||||
{ "legend", required_argument, NULL, ARG_LEGEND },
|
||||
{ "protocol", required_argument, NULL, 'p' },
|
||||
{ "cname", required_argument, NULL, ARG_CNAME },
|
||||
{ "service", no_argument, NULL, ARG_SERVICE },
|
||||
{ "service-address", required_argument, NULL, ARG_SERVICE_ADDRESS },
|
||||
{ "service-txt", required_argument, NULL, ARG_SERVICE_TXT },
|
||||
{ "search", required_argument, NULL, ARG_SEARCH },
|
||||
{ "statistics", no_argument, NULL, ARG_STATISTICS, },
|
||||
{ "reset-statistics", no_argument, NULL, ARG_RESET_STATISTICS },
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -765,6 +892,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
arg_type = (uint16_t) r;
|
||||
assert((int) arg_type == r);
|
||||
|
||||
arg_mode = MODE_RESOLVE_RECORD;
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
|
@ -808,7 +936,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
break;
|
||||
|
||||
case ARG_SERVICE:
|
||||
arg_resolve_service = true;
|
||||
arg_mode = MODE_RESOLVE_SERVICE;
|
||||
break;
|
||||
|
||||
case ARG_CNAME:
|
||||
|
@ -851,6 +979,14 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
arg_flags &= ~SD_RESOLVED_NO_SEARCH;
|
||||
break;
|
||||
|
||||
case ARG_STATISTICS:
|
||||
arg_mode = MODE_STATISTICS;
|
||||
break;
|
||||
|
||||
case ARG_RESET_STATISTICS:
|
||||
arg_mode = MODE_RESET_STATISTICS;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -863,7 +999,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (arg_type != 0 && arg_resolve_service) {
|
||||
if (arg_type != 0 && arg_mode != MODE_RESOLVE_RECORD) {
|
||||
log_error("--service and --type= may not be combined.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -885,20 +1021,57 @@ int main(int argc, char **argv) {
|
|||
if (r <= 0)
|
||||
goto finish;
|
||||
|
||||
if (optind >= argc) {
|
||||
log_error("No arguments passed");
|
||||
r = -EINVAL;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = sd_bus_open_system(&bus);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "sd_bus_open_system: %m");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (arg_resolve_service) {
|
||||
switch (arg_mode) {
|
||||
|
||||
case MODE_RESOLVE_HOST:
|
||||
if (optind >= argc) {
|
||||
log_error("No arguments passed");
|
||||
r = -EINVAL;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
while (argv[optind]) {
|
||||
int family, ifindex, k;
|
||||
union in_addr_union a;
|
||||
|
||||
k = parse_address(argv[optind], &family, &a, &ifindex);
|
||||
if (k >= 0)
|
||||
k = resolve_address(bus, family, &a, ifindex);
|
||||
else
|
||||
k = resolve_host(bus, argv[optind]);
|
||||
|
||||
if (r == 0)
|
||||
r = k;
|
||||
|
||||
optind++;
|
||||
}
|
||||
break;
|
||||
|
||||
case MODE_RESOLVE_RECORD:
|
||||
if (optind >= argc) {
|
||||
log_error("No arguments passed");
|
||||
r = -EINVAL;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
while (argv[optind]) {
|
||||
int k;
|
||||
|
||||
k = resolve_record(bus, argv[optind]);
|
||||
if (r == 0)
|
||||
r = k;
|
||||
|
||||
optind++;
|
||||
}
|
||||
break;
|
||||
|
||||
case MODE_RESOLVE_SERVICE:
|
||||
if (argc < optind + 1) {
|
||||
log_error("Domain specification required.");
|
||||
r = -EINVAL;
|
||||
|
@ -916,27 +1089,27 @@ int main(int argc, char **argv) {
|
|||
goto finish;
|
||||
}
|
||||
|
||||
goto finish;
|
||||
}
|
||||
break;
|
||||
|
||||
while (argv[optind]) {
|
||||
int family, ifindex, k;
|
||||
union in_addr_union a;
|
||||
|
||||
if (arg_type != 0)
|
||||
k = resolve_record(bus, argv[optind]);
|
||||
else {
|
||||
k = parse_address(argv[optind], &family, &a, &ifindex);
|
||||
if (k >= 0)
|
||||
k = resolve_address(bus, family, &a, ifindex);
|
||||
else
|
||||
k = resolve_host(bus, argv[optind]);
|
||||
case MODE_STATISTICS:
|
||||
if (argc > optind) {
|
||||
log_error("Too many arguments.");
|
||||
r = -EINVAL;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (r == 0)
|
||||
r = k;
|
||||
r = show_statistics(bus);
|
||||
break;
|
||||
|
||||
optind++;
|
||||
case MODE_RESET_STATISTICS:
|
||||
if (argc > optind) {
|
||||
log_error("Too many arguments.");
|
||||
r = -EINVAL;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = reset_statistics(bus);
|
||||
break;
|
||||
}
|
||||
|
||||
finish:
|
||||
|
|
|
@ -95,6 +95,25 @@ bool dns_class_is_valid_rr(uint16_t class) {
|
|||
return class != DNS_CLASS_ANY;
|
||||
}
|
||||
|
||||
bool dns_type_may_redirect(uint16_t type) {
|
||||
/* The following record types should never be redirected using
|
||||
* CNAME/DNAME RRs. See
|
||||
* <https://tools.ietf.org/html/rfc4035#section-2.5>. */
|
||||
|
||||
if (dns_type_is_pseudo(type))
|
||||
return false;
|
||||
|
||||
return !IN_SET(type,
|
||||
DNS_TYPE_CNAME,
|
||||
DNS_TYPE_DNAME,
|
||||
DNS_TYPE_NSEC3,
|
||||
DNS_TYPE_NSEC,
|
||||
DNS_TYPE_RRSIG,
|
||||
DNS_TYPE_NXT,
|
||||
DNS_TYPE_SIG,
|
||||
DNS_TYPE_KEY);
|
||||
}
|
||||
|
||||
const char *dns_class_to_string(uint16_t class) {
|
||||
|
||||
switch (class) {
|
||||
|
|
|
@ -128,6 +128,7 @@ enum {
|
|||
bool dns_type_is_pseudo(uint16_t type);
|
||||
bool dns_type_is_valid_query(uint16_t type);
|
||||
bool dns_type_is_valid_rr(uint16_t type);
|
||||
bool dns_type_may_redirect(uint16_t type);
|
||||
|
||||
bool dns_class_is_pseudo(uint16_t class);
|
||||
bool dns_class_is_valid_rr(uint16_t class);
|
||||
|
|
|
@ -57,6 +57,9 @@ static int reply_query_state(DnsQuery *q) {
|
|||
case DNS_TRANSACTION_RESOURCES:
|
||||
return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_RESOURCES, "Not enough resources");
|
||||
|
||||
case DNS_TRANSACTION_CONNECTION_FAILURE:
|
||||
return sd_bus_reply_method_errorf(q->request, BUS_ERROR_CONNECTION_FAILURE, "DNS server connection failure");
|
||||
|
||||
case DNS_TRANSACTION_ABORTED:
|
||||
return sd_bus_reply_method_errorf(q->request, BUS_ERROR_ABORTED, "Query aborted");
|
||||
|
||||
|
@ -1214,16 +1217,101 @@ static int bus_property_get_search_domains(
|
|||
return sd_bus_message_close_container(reply);
|
||||
}
|
||||
|
||||
static int bus_property_get_transaction_statistics(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
|
||||
Manager *m = userdata;
|
||||
|
||||
assert(reply);
|
||||
assert(m);
|
||||
|
||||
return sd_bus_message_append(reply, "(tt)",
|
||||
(uint64_t) hashmap_size(m->dns_transactions),
|
||||
(uint64_t) m->n_transactions_total);
|
||||
}
|
||||
|
||||
static int bus_property_get_cache_statistics(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
|
||||
uint64_t size = 0, hit = 0, miss = 0;
|
||||
Manager *m = userdata;
|
||||
DnsScope *s;
|
||||
|
||||
assert(reply);
|
||||
assert(m);
|
||||
|
||||
LIST_FOREACH(scopes, s, m->dns_scopes) {
|
||||
size += dns_cache_size(&s->cache);
|
||||
hit += s->cache.n_hit;
|
||||
miss += s->cache.n_miss;
|
||||
}
|
||||
|
||||
return sd_bus_message_append(reply, "(ttt)", size, hit, miss);
|
||||
}
|
||||
|
||||
static int bus_property_get_dnssec_statistics(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
|
||||
Manager *m = userdata;
|
||||
|
||||
assert(reply);
|
||||
assert(m);
|
||||
|
||||
return sd_bus_message_append(reply, "(tttt)",
|
||||
(uint64_t) m->n_dnssec_secure,
|
||||
(uint64_t) m->n_dnssec_insecure,
|
||||
(uint64_t) m->n_dnssec_bogus,
|
||||
(uint64_t) m->n_dnssec_indeterminate);
|
||||
}
|
||||
|
||||
static int bus_method_reset_statistics(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
Manager *m = userdata;
|
||||
DnsScope *s;
|
||||
|
||||
assert(message);
|
||||
assert(m);
|
||||
|
||||
LIST_FOREACH(scopes, s, m->dns_scopes)
|
||||
s->cache.n_hit = s->cache.n_miss = 0;
|
||||
|
||||
m->n_transactions_total = 0;
|
||||
m->n_dnssec_secure = m->n_dnssec_insecure = m->n_dnssec_bogus = m->n_dnssec_indeterminate = 0;
|
||||
|
||||
return sd_bus_reply_method_return(message, NULL);
|
||||
}
|
||||
|
||||
static const sd_bus_vtable resolve_vtable[] = {
|
||||
SD_BUS_VTABLE_START(0),
|
||||
SD_BUS_PROPERTY("LLMNRHostname", "s", NULL, offsetof(Manager, llmnr_hostname), 0),
|
||||
SD_BUS_PROPERTY("DNSServers", "a(iiay)", bus_property_get_dns_servers, 0, 0),
|
||||
SD_BUS_PROPERTY("SearchDomains", "a(is)", bus_property_get_search_domains, 0, 0),
|
||||
SD_BUS_PROPERTY("TransactionStatistics", "(tt)", bus_property_get_transaction_statistics, 0, 0),
|
||||
SD_BUS_PROPERTY("CacheStatistics", "(ttt)", bus_property_get_cache_statistics, 0, 0),
|
||||
SD_BUS_PROPERTY("DNSSECStatistics", "(tttt)", bus_property_get_dnssec_statistics, 0, 0),
|
||||
|
||||
SD_BUS_METHOD("ResolveHostname", "isit", "a(iiay)st", bus_method_resolve_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("ResolveAddress", "iiayt", "a(is)t", bus_method_resolve_address, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("ResolveRecord", "isqqt", "a(iqqay)t", bus_method_resolve_record, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("ResolveService", "isssit", "a(qqqsa(iiay)s)aayssst", bus_method_resolve_service, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("ResetStatistics", NULL, NULL, bus_method_reset_statistics, 0),
|
||||
SD_BUS_VTABLE_END,
|
||||
};
|
||||
|
||||
|
|
|
@ -303,8 +303,8 @@ int dns_answer_contains_nsec_or_nsec3(DnsAnswer *a) {
|
|||
}
|
||||
|
||||
int dns_answer_find_soa(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) {
|
||||
DnsResourceRecord *rr;
|
||||
DnsAnswerFlags rr_flags;
|
||||
DnsResourceRecord *rr, *soa = NULL;
|
||||
DnsAnswerFlags rr_flags, soa_flags = 0;
|
||||
int r;
|
||||
|
||||
assert(key);
|
||||
|
@ -318,15 +318,29 @@ int dns_answer_find_soa(DnsAnswer *a, const DnsResourceKey *key, DnsResourceReco
|
|||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0) {
|
||||
if (ret)
|
||||
*ret = rr;
|
||||
if (flags)
|
||||
*flags = rr_flags;
|
||||
return 1;
|
||||
|
||||
if (soa) {
|
||||
r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(soa->key));
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
soa = rr;
|
||||
soa_flags = rr_flags;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
if (!soa)
|
||||
return 0;
|
||||
|
||||
if (ret)
|
||||
*ret = soa;
|
||||
if (flags)
|
||||
*flags = soa_flags;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int dns_answer_find_cname_or_dname(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) {
|
||||
|
@ -337,7 +351,7 @@ int dns_answer_find_cname_or_dname(DnsAnswer *a, const DnsResourceKey *key, DnsR
|
|||
assert(key);
|
||||
|
||||
/* For a {C,D}NAME record we can never find a matching {C,D}NAME record */
|
||||
if (key->type == DNS_TYPE_CNAME || key->type == DNS_TYPE_DNAME)
|
||||
if (!dns_type_may_redirect(key->type))
|
||||
return 0;
|
||||
|
||||
DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
|
||||
|
@ -643,18 +657,18 @@ int dns_answer_reserve_or_clone(DnsAnswer **a, unsigned n_free) {
|
|||
void dns_answer_dump(DnsAnswer *answer, FILE *f) {
|
||||
DnsResourceRecord *rr;
|
||||
DnsAnswerFlags flags;
|
||||
int ifindex, r;
|
||||
int ifindex;
|
||||
|
||||
if (!f)
|
||||
f = stdout;
|
||||
|
||||
DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, answer) {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
const char *t;
|
||||
|
||||
fputc('\t', f);
|
||||
|
||||
r = dns_resource_record_to_string(rr, &t);
|
||||
if (r < 0) {
|
||||
t = dns_resource_record_to_string(rr);
|
||||
if (!t) {
|
||||
log_oom();
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -470,6 +470,14 @@ static int dns_cache_put_negative(
|
|||
i->key = dns_resource_key_new(key->class, DNS_TYPE_ANY, DNS_RESOURCE_KEY_NAME(key));
|
||||
if (!i->key)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Make sure to remove any previous entry for this
|
||||
* specific ANY key. (For non-ANY keys the cache data
|
||||
* is already cleared by the caller.) Note that we
|
||||
* don't bother removing positive or NODATA cache
|
||||
* items in this case, because it would either be slow
|
||||
* or require explicit indexing by name */
|
||||
dns_cache_remove_by_key(c, key);
|
||||
} else
|
||||
i->key = dns_resource_key_ref(key);
|
||||
|
||||
|
@ -607,7 +615,6 @@ int dns_cache_put(
|
|||
/* See https://tools.ietf.org/html/rfc2308, which say that a
|
||||
* matching SOA record in the packet is used to to enable
|
||||
* negative caching. */
|
||||
|
||||
r = dns_answer_find_soa(answer, key, &soa, &flags);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
@ -672,11 +679,7 @@ static DnsCacheItem *dns_cache_get_by_key_follow_cname_dname_nsec(DnsCache *c, D
|
|||
if (i && i->type == DNS_CACHE_NXDOMAIN)
|
||||
return i;
|
||||
|
||||
/* The following record types should never be redirected. See
|
||||
* <https://tools.ietf.org/html/rfc4035#section-2.5>. */
|
||||
if (!IN_SET(k->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME,
|
||||
DNS_TYPE_NSEC3, DNS_TYPE_NSEC, DNS_TYPE_RRSIG,
|
||||
DNS_TYPE_NXT, DNS_TYPE_SIG, DNS_TYPE_KEY)) {
|
||||
if (dns_type_may_redirect(k->type)) {
|
||||
/* Check if we have a CNAME record instead */
|
||||
i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_CNAME, n));
|
||||
if (i)
|
||||
|
@ -737,6 +740,8 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r
|
|||
log_debug("Ignoring cache for ANY lookup: %s", key_str);
|
||||
}
|
||||
|
||||
c->n_miss++;
|
||||
|
||||
*ret = NULL;
|
||||
*rcode = DNS_RCODE_SUCCESS;
|
||||
return 0;
|
||||
|
@ -754,6 +759,8 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r
|
|||
log_debug("Cache miss for %s", key_str);
|
||||
}
|
||||
|
||||
c->n_miss++;
|
||||
|
||||
*ret = NULL;
|
||||
*rcode = DNS_RCODE_SUCCESS;
|
||||
return 0;
|
||||
|
@ -791,9 +798,15 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r
|
|||
*rcode = DNS_RCODE_SUCCESS;
|
||||
*authenticated = nsec->authenticated;
|
||||
|
||||
return !bitmap_isset(nsec->rr->nsec.types, key->type) &&
|
||||
!bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_CNAME) &&
|
||||
!bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_DNAME);
|
||||
if (!bitmap_isset(nsec->rr->nsec.types, key->type) &&
|
||||
!bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_CNAME) &&
|
||||
!bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_DNAME)) {
|
||||
c->n_hit++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
c->n_miss++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (log_get_max_level() >= LOG_DEBUG) {
|
||||
|
@ -808,6 +821,8 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r
|
|||
}
|
||||
|
||||
if (n <= 0) {
|
||||
c->n_hit++;
|
||||
|
||||
*ret = NULL;
|
||||
*rcode = nxdomain ? DNS_RCODE_NXDOMAIN : DNS_RCODE_SUCCESS;
|
||||
*authenticated = have_authenticated && !have_non_authenticated;
|
||||
|
@ -827,6 +842,8 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r
|
|||
return r;
|
||||
}
|
||||
|
||||
c->n_hit++;
|
||||
|
||||
*ret = answer;
|
||||
*rcode = DNS_RCODE_SUCCESS;
|
||||
*authenticated = have_authenticated && !have_non_authenticated;
|
||||
|
@ -935,13 +952,13 @@ void dns_cache_dump(DnsCache *cache, FILE *f) {
|
|||
DnsCacheItem *j;
|
||||
|
||||
LIST_FOREACH(by_key, j, i) {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
|
||||
fputc('\t', f);
|
||||
|
||||
if (j->rr) {
|
||||
r = dns_resource_record_to_string(j->rr, &t);
|
||||
if (r < 0) {
|
||||
const char *t;
|
||||
t = dns_resource_record_to_string(j->rr);
|
||||
if (!t) {
|
||||
log_oom();
|
||||
continue;
|
||||
}
|
||||
|
@ -949,13 +966,14 @@ void dns_cache_dump(DnsCache *cache, FILE *f) {
|
|||
fputs(t, f);
|
||||
fputc('\n', f);
|
||||
} else {
|
||||
r = dns_resource_key_to_string(j->key, &t);
|
||||
_cleanup_free_ char *z = NULL;
|
||||
r = dns_resource_key_to_string(j->key, &z);
|
||||
if (r < 0) {
|
||||
log_oom();
|
||||
continue;
|
||||
}
|
||||
|
||||
fputs(t, f);
|
||||
fputs(z, f);
|
||||
fputs(" -- ", f);
|
||||
fputs(j->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN", f);
|
||||
fputc('\n', f);
|
||||
|
@ -970,3 +988,10 @@ bool dns_cache_is_empty(DnsCache *cache) {
|
|||
|
||||
return hashmap_isempty(cache->by_key);
|
||||
}
|
||||
|
||||
unsigned dns_cache_size(DnsCache *cache) {
|
||||
if (!cache)
|
||||
return 0;
|
||||
|
||||
return hashmap_size(cache->by_key);
|
||||
}
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
typedef struct DnsCache {
|
||||
Hashmap *by_key;
|
||||
Prioq *by_expiry;
|
||||
unsigned n_hit;
|
||||
unsigned n_miss;
|
||||
} DnsCache;
|
||||
|
||||
#include "resolved-dns-answer.h"
|
||||
|
@ -47,4 +49,6 @@ int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_
|
|||
void dns_cache_dump(DnsCache *cache, FILE *f);
|
||||
bool dns_cache_is_empty(DnsCache *cache);
|
||||
|
||||
unsigned dns_cache_size(DnsCache *cache);
|
||||
|
||||
int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p);
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
* TODO:
|
||||
*
|
||||
* - Make trust anchor store read additional DS+DNSKEY data from disk
|
||||
* - wildcard zones compatibility
|
||||
* - wildcard zones compatibility (NSEC/NSEC3 wildcard check is missing)
|
||||
* - multi-label zone compatibility
|
||||
* - cname/dname compatibility
|
||||
* - per-interface DNSSEC setting
|
||||
|
@ -384,10 +384,17 @@ int dnssec_verify_rrset(
|
|||
gcry_md_write(md, wire_format_name, r);
|
||||
|
||||
for (k = 0; k < n; k++) {
|
||||
const char *suffix;
|
||||
size_t l;
|
||||
rr = list[k];
|
||||
|
||||
r = dns_name_to_wire_format(DNS_RESOURCE_KEY_NAME(rr->key), wire_format_name, sizeof(wire_format_name), true);
|
||||
r = dns_name_suffix(DNS_RESOURCE_KEY_NAME(rr->key), rrsig->rrsig.labels, &suffix);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
if (r > 0) /* This is a wildcard! */
|
||||
gcry_md_write(md, (uint8_t[]) { 1, '*'}, 2);
|
||||
|
||||
r = dns_name_to_wire_format(suffix, wire_format_name, sizeof(wire_format_name), true);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
gcry_md_write(md, wire_format_name, r);
|
||||
|
@ -497,6 +504,8 @@ int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnske
|
|||
}
|
||||
|
||||
int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig) {
|
||||
int r;
|
||||
|
||||
assert(key);
|
||||
assert(rrsig);
|
||||
|
||||
|
@ -509,6 +518,18 @@ int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig)
|
|||
if (rrsig->rrsig.type_covered != key->type)
|
||||
return 0;
|
||||
|
||||
/* Make sure signer is a parent of the RRset */
|
||||
r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(rrsig->key), rrsig->rrsig.signer);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
/* Make sure the owner name has at least as many labels as the "label" fields indicates. */
|
||||
r = dns_name_count_labels(DNS_RESOURCE_KEY_NAME(rrsig->key));
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r < rrsig->rrsig.labels)
|
||||
return 0;
|
||||
|
||||
return dns_name_equal(DNS_RESOURCE_KEY_NAME(rrsig->key), DNS_RESOURCE_KEY_NAME(key));
|
||||
}
|
||||
|
||||
|
@ -810,6 +831,15 @@ int dnssec_verify_dnskey_search(DnsResourceRecord *dnskey, DnsAnswer *validated_
|
|||
if (ds->key->type != DNS_TYPE_DS)
|
||||
continue;
|
||||
|
||||
if (ds->key->class != dnskey->key->class)
|
||||
continue;
|
||||
|
||||
r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), DNS_RESOURCE_KEY_NAME(ds->key));
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
continue;
|
||||
|
||||
r = dnssec_verify_dnskey(dnskey, ds);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
@ -888,62 +918,138 @@ finish:
|
|||
return r;
|
||||
}
|
||||
|
||||
static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result) {
|
||||
static int nsec3_is_good(DnsResourceRecord *rr, DnsAnswerFlags flags, DnsResourceRecord *nsec3) {
|
||||
const char *a, *b;
|
||||
int r;
|
||||
|
||||
assert(rr);
|
||||
|
||||
if (rr->key->type != DNS_TYPE_NSEC3)
|
||||
return 0;
|
||||
|
||||
/* RFC 5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */
|
||||
if (!IN_SET(rr->nsec3.flags, 0, 1))
|
||||
return 0;
|
||||
|
||||
if (!nsec3)
|
||||
return 1;
|
||||
|
||||
/* If a second NSEC3 RR is specified, also check if they are from the same zone. */
|
||||
|
||||
if (nsec3 == rr) /* Shortcut */
|
||||
return 1;
|
||||
|
||||
if (rr->key->class != nsec3->key->class)
|
||||
return 0;
|
||||
if (rr->nsec3.algorithm != nsec3->nsec3.algorithm)
|
||||
return 0;
|
||||
if (rr->nsec3.iterations != nsec3->nsec3.iterations)
|
||||
return 0;
|
||||
if (rr->nsec3.salt_size != nsec3->nsec3.salt_size)
|
||||
return 0;
|
||||
if (memcmp(rr->nsec3.salt, nsec3->nsec3.salt, rr->nsec3.salt_size) != 0)
|
||||
return 0;
|
||||
|
||||
a = DNS_RESOURCE_KEY_NAME(rr->key);
|
||||
r = dns_name_parent(&a); /* strip off hash */
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return 0;
|
||||
|
||||
b = DNS_RESOURCE_KEY_NAME(nsec3->key);
|
||||
r = dns_name_parent(&b); /* strip off hash */
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return 0;
|
||||
|
||||
return dns_name_equal(a, b);
|
||||
}
|
||||
|
||||
static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated) {
|
||||
_cleanup_free_ char *next_closer_domain = NULL, *l = NULL;
|
||||
uint8_t hashed[DNSSEC_HASH_SIZE_MAX];
|
||||
const char *p, *pp = NULL;
|
||||
DnsResourceRecord *rr;
|
||||
const char *suffix, *p, *pp = NULL;
|
||||
DnsResourceRecord *rr, *suffix_rr;
|
||||
DnsAnswerFlags flags;
|
||||
int hashed_size, r;
|
||||
bool a;
|
||||
|
||||
assert(key);
|
||||
assert(result);
|
||||
assert(authenticated);
|
||||
|
||||
/* First step, look for the closest encloser NSEC3 RR in 'answer' that matches 'key' */
|
||||
p = DNS_RESOURCE_KEY_NAME(key);
|
||||
/* First step, look for the longest common suffix we find with any NSEC3 RR in the response. */
|
||||
suffix = DNS_RESOURCE_KEY_NAME(key);
|
||||
for (;;) {
|
||||
DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
|
||||
DNS_ANSWER_FOREACH_FLAGS(suffix_rr, flags, answer) {
|
||||
_cleanup_free_ char *hashed_domain = NULL, *label = NULL;
|
||||
|
||||
if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
|
||||
continue;
|
||||
|
||||
if (rr->key->type != DNS_TYPE_NSEC3)
|
||||
continue;
|
||||
|
||||
/* RFC 5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */
|
||||
if (!IN_SET(rr->nsec3.flags, 0, 1))
|
||||
continue;
|
||||
|
||||
r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(rr->key), p);
|
||||
r = nsec3_is_good(suffix_rr, flags, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
continue;
|
||||
|
||||
r = dns_name_equal_skip(DNS_RESOURCE_KEY_NAME(suffix_rr->key), 1, suffix);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
goto found_suffix;
|
||||
}
|
||||
|
||||
/* Strip one label from the front */
|
||||
r = dns_name_parent(&suffix);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
*result = DNSSEC_NSEC_NO_RR;
|
||||
return 0;
|
||||
|
||||
found_suffix:
|
||||
/* Second step, find the closest encloser NSEC3 RR in 'answer' that matches 'key' */
|
||||
p = DNS_RESOURCE_KEY_NAME(key);
|
||||
for (;;) {
|
||||
_cleanup_free_ char *hashed_domain = NULL, *label = NULL;
|
||||
|
||||
hashed_size = dnssec_nsec3_hash(suffix_rr, p, hashed);
|
||||
if (hashed_size == -EOPNOTSUPP) {
|
||||
*result = DNSSEC_NSEC_UNSUPPORTED_ALGORITHM;
|
||||
return 0;
|
||||
}
|
||||
if (hashed_size < 0)
|
||||
return hashed_size;
|
||||
|
||||
label = base32hexmem(hashed, hashed_size, false);
|
||||
if (!label)
|
||||
return -ENOMEM;
|
||||
|
||||
hashed_domain = strjoin(label, ".", suffix, NULL);
|
||||
if (!hashed_domain)
|
||||
return -ENOMEM;
|
||||
|
||||
DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
|
||||
|
||||
r = nsec3_is_good(rr, flags, suffix_rr);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
continue;
|
||||
|
||||
hashed_size = dnssec_nsec3_hash(rr, p, hashed);
|
||||
if (hashed_size == -EOPNOTSUPP) {
|
||||
*result = DNSSEC_NSEC_UNSUPPORTED_ALGORITHM;
|
||||
return 0;
|
||||
}
|
||||
if (hashed_size < 0)
|
||||
return hashed_size;
|
||||
if (rr->nsec3.next_hashed_name_size != (size_t) hashed_size)
|
||||
return -EBADMSG;
|
||||
|
||||
label = base32hexmem(hashed, hashed_size, false);
|
||||
if (!label)
|
||||
return -ENOMEM;
|
||||
|
||||
hashed_domain = strjoin(label, ".", p, NULL);
|
||||
if (!hashed_domain)
|
||||
return -ENOMEM;
|
||||
continue;
|
||||
|
||||
r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), hashed_domain);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
goto found;
|
||||
if (r > 0) {
|
||||
a = flags & DNS_ANSWER_AUTHENTICATED;
|
||||
goto found_closest_encloser;
|
||||
}
|
||||
}
|
||||
|
||||
/* We didn't find the closest encloser with this name,
|
||||
|
@ -963,7 +1069,7 @@ static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecR
|
|||
*result = DNSSEC_NSEC_NO_RR;
|
||||
return 0;
|
||||
|
||||
found:
|
||||
found_closest_encloser:
|
||||
/* We found a closest encloser in 'p'; next closer is 'pp' */
|
||||
|
||||
/* Ensure this is not a DNAME domain, see RFC5155, section 8.3. */
|
||||
|
@ -981,6 +1087,7 @@ found:
|
|||
if (!pp) {
|
||||
/* No next closer NSEC3 RR. That means there's a direct NSEC3 RR for our key. */
|
||||
*result = bitmap_isset(rr->nsec3.types, key->type) ? DNSSEC_NSEC_FOUND : DNSSEC_NSEC_NODATA;
|
||||
*authenticated = a;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1000,26 +1107,8 @@ found:
|
|||
|
||||
DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
|
||||
_cleanup_free_ char *label = NULL, *next_hashed_domain = NULL;
|
||||
const char *nsec3_parent;
|
||||
|
||||
if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
|
||||
continue;
|
||||
|
||||
if (rr->key->type != DNS_TYPE_NSEC3)
|
||||
continue;
|
||||
|
||||
/* RFC 5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */
|
||||
if (!IN_SET(rr->nsec3.flags, 0, 1))
|
||||
continue;
|
||||
|
||||
nsec3_parent = DNS_RESOURCE_KEY_NAME(rr->key);
|
||||
r = dns_name_parent(&nsec3_parent);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
continue;
|
||||
|
||||
r = dns_name_equal(p, nsec3_parent);
|
||||
r = nsec3_is_good(rr, flags, suffix_rr);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
|
@ -1042,6 +1131,7 @@ found:
|
|||
else
|
||||
*result = DNSSEC_NSEC_NXDOMAIN;
|
||||
|
||||
*authenticated = a && (flags & DNS_ANSWER_AUTHENTICATED);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@ -1050,7 +1140,7 @@ found:
|
|||
return 0;
|
||||
}
|
||||
|
||||
int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result) {
|
||||
int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated) {
|
||||
DnsResourceRecord *rr;
|
||||
bool have_nsec3 = false;
|
||||
DnsAnswerFlags flags;
|
||||
|
@ -1058,6 +1148,7 @@ int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r
|
|||
|
||||
assert(key);
|
||||
assert(result);
|
||||
assert(authenticated);
|
||||
|
||||
/* Look for any NSEC/NSEC3 RRs that say something about the specified key. */
|
||||
|
||||
|
@ -1066,9 +1157,6 @@ int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r
|
|||
if (rr->key->class != key->class)
|
||||
continue;
|
||||
|
||||
if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
|
||||
continue;
|
||||
|
||||
switch (rr->key->type) {
|
||||
|
||||
case DNS_TYPE_NSEC:
|
||||
|
@ -1078,6 +1166,7 @@ int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r
|
|||
return r;
|
||||
if (r > 0) {
|
||||
*result = bitmap_isset(rr->nsec.types, key->type) ? DNSSEC_NSEC_FOUND : DNSSEC_NSEC_NODATA;
|
||||
*authenticated = flags & DNS_ANSWER_AUTHENTICATED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1086,6 +1175,7 @@ int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r
|
|||
return r;
|
||||
if (r > 0) {
|
||||
*result = DNSSEC_NSEC_NXDOMAIN;
|
||||
*authenticated = flags & DNS_ANSWER_AUTHENTICATED;
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
@ -1098,7 +1188,7 @@ int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r
|
|||
|
||||
/* OK, this was not sufficient. Let's see if NSEC3 can help. */
|
||||
if (have_nsec3)
|
||||
return dnssec_test_nsec3(answer, key, result);
|
||||
return dnssec_test_nsec3(answer, key, result, authenticated);
|
||||
|
||||
/* No approproate NSEC RR found, report this. */
|
||||
*result = DNSSEC_NSEC_NO_RR;
|
||||
|
@ -1107,6 +1197,7 @@ int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r
|
|||
|
||||
static const char* const dnssec_mode_table[_DNSSEC_MODE_MAX] = {
|
||||
[DNSSEC_NO] = "no",
|
||||
[DNSSEC_DOWNGRADE_OK] = "downgrade-ok",
|
||||
[DNSSEC_YES] = "yes",
|
||||
};
|
||||
DEFINE_STRING_TABLE_LOOKUP(dnssec_mode, DnssecMode);
|
||||
|
@ -1121,5 +1212,6 @@ static const char* const dnssec_result_table[_DNSSEC_RESULT_MAX] = {
|
|||
[DNSSEC_UNSIGNED] = "unsigned",
|
||||
[DNSSEC_FAILED_AUXILIARY] = "failed-auxiliary",
|
||||
[DNSSEC_NSEC_MISMATCH] = "nsec-mismatch",
|
||||
[DNSSEC_INCOMPATIBLE_SERVER] = "incompatible-server",
|
||||
};
|
||||
DEFINE_STRING_TABLE_LOOKUP(dnssec_result, DnssecResult);
|
||||
|
|
|
@ -32,7 +32,14 @@ enum DnssecMode {
|
|||
/* No DNSSEC validation is done */
|
||||
DNSSEC_NO,
|
||||
|
||||
/* Validate locally, if the server knows DO, but if not, don't. Don't trust the AD bit */
|
||||
/* Validate locally, if the server knows DO, but if not,
|
||||
* don't. Don't trust the AD bit. If the server doesn't do
|
||||
* DNSSEC properly, downgrade to non-DNSSEC operation. Of
|
||||
* course, we then are vulnerable to a downgrade attack, but
|
||||
* that's life and what is configured. */
|
||||
DNSSEC_DOWNGRADE_OK,
|
||||
|
||||
/* Insist on DNSSEC server support, and rather fail than downgrading. */
|
||||
DNSSEC_YES,
|
||||
|
||||
_DNSSEC_MODE_MAX,
|
||||
|
@ -54,6 +61,8 @@ enum DnssecResult {
|
|||
DNSSEC_UNSIGNED,
|
||||
DNSSEC_FAILED_AUXILIARY,
|
||||
DNSSEC_NSEC_MISMATCH,
|
||||
DNSSEC_INCOMPATIBLE_SERVER,
|
||||
|
||||
_DNSSEC_RESULT_MAX,
|
||||
_DNSSEC_RESULT_INVALID = -1
|
||||
};
|
||||
|
@ -89,7 +98,7 @@ typedef enum DnssecNsecResult {
|
|||
DNSSEC_NSEC_OPTOUT,
|
||||
} DnssecNsecResult;
|
||||
|
||||
int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result);
|
||||
int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated);
|
||||
|
||||
const char* dnssec_mode_to_string(DnssecMode m) _const_;
|
||||
DnssecMode dnssec_mode_from_string(const char *s) _pure_;
|
||||
|
|
|
@ -58,6 +58,7 @@ int dns_packet_new(DnsPacket **ret, DnsProtocol protocol, size_t mtu) {
|
|||
p->size = p->rindex = DNS_PACKET_HEADER_SIZE;
|
||||
p->allocated = a;
|
||||
p->protocol = protocol;
|
||||
p->opt_start = p->opt_size = (size_t) -1;
|
||||
p->n_ref = 1;
|
||||
|
||||
*ret = p;
|
||||
|
@ -499,7 +500,7 @@ int dns_packet_append_name(
|
|||
saved_size = p->size;
|
||||
|
||||
while (*name) {
|
||||
_cleanup_free_ char *s = NULL;
|
||||
const char *z = name;
|
||||
char label[DNS_LABEL_MAX];
|
||||
size_t n = 0;
|
||||
int k;
|
||||
|
@ -518,12 +519,6 @@ int dns_packet_append_name(
|
|||
}
|
||||
}
|
||||
|
||||
s = strdup(name);
|
||||
if (!s) {
|
||||
r = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = dns_label_unescape(&name, label, sizeof(label));
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
@ -544,6 +539,14 @@ int dns_packet_append_name(
|
|||
goto fail;
|
||||
|
||||
if (allow_compression) {
|
||||
_cleanup_free_ char *s = NULL;
|
||||
|
||||
s = strdup(z);
|
||||
if (!s) {
|
||||
r = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = hashmap_ensure_allocated(&p->names, &dns_name_hash_ops);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
@ -643,7 +646,6 @@ static int dns_packet_append_types(DnsPacket *p, Bitmap *types, size_t *start) {
|
|||
int r;
|
||||
|
||||
assert(p);
|
||||
assert(types);
|
||||
|
||||
saved_size = p->size;
|
||||
|
||||
|
@ -680,7 +682,7 @@ fail:
|
|||
}
|
||||
|
||||
/* Append the OPT pseudo-RR described in RFC6891 */
|
||||
int dns_packet_append_opt_rr(DnsPacket *p, uint16_t max_udp_size, bool edns0_do, size_t *start) {
|
||||
int dns_packet_append_opt(DnsPacket *p, uint16_t max_udp_size, bool edns0_do, size_t *start) {
|
||||
size_t saved_size;
|
||||
int r;
|
||||
|
||||
|
@ -688,6 +690,11 @@ int dns_packet_append_opt_rr(DnsPacket *p, uint16_t max_udp_size, bool edns0_do,
|
|||
/* we must never advertise supported packet size smaller than the legacy max */
|
||||
assert(max_udp_size >= DNS_PACKET_UNICAST_SIZE_MAX);
|
||||
|
||||
if (p->opt_start != (size_t) -1)
|
||||
return -EBUSY;
|
||||
|
||||
assert(p->opt_size == (size_t) -1);
|
||||
|
||||
saved_size = p->size;
|
||||
|
||||
/* empty name */
|
||||
|
@ -720,6 +727,11 @@ int dns_packet_append_opt_rr(DnsPacket *p, uint16_t max_udp_size, bool edns0_do,
|
|||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
DNS_PACKET_HEADER(p)->arcount = htobe16(DNS_PACKET_ARCOUNT(p) + 1);
|
||||
|
||||
p->opt_start = saved_size;
|
||||
p->opt_size = p->size - saved_size;
|
||||
|
||||
if (start)
|
||||
*start = saved_size;
|
||||
|
||||
|
@ -730,6 +742,27 @@ fail:
|
|||
return r;
|
||||
}
|
||||
|
||||
int dns_packet_truncate_opt(DnsPacket *p) {
|
||||
assert(p);
|
||||
|
||||
if (p->opt_start == (size_t) -1) {
|
||||
assert(p->opt_size == (size_t) -1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert(p->opt_size != (size_t) -1);
|
||||
assert(DNS_PACKET_ARCOUNT(p) > 0);
|
||||
|
||||
if (p->opt_start + p->opt_size != p->size)
|
||||
return -EBUSY;
|
||||
|
||||
dns_packet_truncate(p, p->opt_start);
|
||||
DNS_PACKET_HEADER(p)->arcount = htobe16(DNS_PACKET_ARCOUNT(p) - 1);
|
||||
p->opt_start = p->opt_size = (size_t) -1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *start, size_t *rdata_start) {
|
||||
size_t saved_size, rdlength_offset, end, rdlength, rds;
|
||||
int r;
|
||||
|
@ -1045,7 +1078,6 @@ fail:
|
|||
return r;
|
||||
}
|
||||
|
||||
|
||||
int dns_packet_read(DnsPacket *p, size_t sz, const void **ret, size_t *start) {
|
||||
assert(p);
|
||||
|
||||
|
|
|
@ -76,6 +76,7 @@ struct DnsPacket {
|
|||
size_t size, allocated, rindex;
|
||||
void *_data; /* don't access directly, use DNS_PACKET_DATA()! */
|
||||
Hashmap *names; /* For name compression */
|
||||
size_t opt_start, opt_size;
|
||||
|
||||
/* Parsed data */
|
||||
DnsQuestion *question;
|
||||
|
@ -173,9 +174,10 @@ int dns_packet_append_label(DnsPacket *p, const char *s, size_t l, bool canonica
|
|||
int dns_packet_append_name(DnsPacket *p, const char *name, bool allow_compression, bool canonical_candidate, 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, size_t *rdata_start);
|
||||
int dns_packet_append_opt_rr(DnsPacket *p, uint16_t max_udp_size, bool edns0_do, size_t *start);
|
||||
int dns_packet_append_opt(DnsPacket *p, uint16_t max_udp_size, bool edns0_do, size_t *start);
|
||||
|
||||
void dns_packet_truncate(DnsPacket *p, size_t sz);
|
||||
int dns_packet_truncate_opt(DnsPacket *p);
|
||||
|
||||
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);
|
||||
|
|
|
@ -1039,8 +1039,7 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
|
|||
if (state == DNS_TRANSACTION_SUCCESS)
|
||||
continue;
|
||||
|
||||
dns_answer_unref(q->answer);
|
||||
q->answer = dns_answer_ref(t->answer);
|
||||
q->answer = dns_answer_unref(q->answer);
|
||||
q->answer_rcode = t->answer_rcode;
|
||||
q->answer_dnssec_result = t->answer_dnssec_result;
|
||||
|
||||
|
|
|
@ -261,16 +261,13 @@ int dns_resource_key_match_soa(const DnsResourceKey *key, const DnsResourceKey *
|
|||
|
||||
/* Checks whether 'soa' is a SOA record for the specified key. */
|
||||
|
||||
if (soa->class != DNS_CLASS_IN)
|
||||
if (soa->class != key->class)
|
||||
return 0;
|
||||
|
||||
if (soa->type != DNS_TYPE_SOA)
|
||||
return 0;
|
||||
|
||||
if (!dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(soa)))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
return dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(soa));
|
||||
}
|
||||
|
||||
static void dns_resource_key_hash_func(const void *i, struct siphash *state) {
|
||||
|
@ -451,6 +448,7 @@ DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr) {
|
|||
dns_resource_key_unref(rr->key);
|
||||
}
|
||||
|
||||
free(rr->to_string);
|
||||
free(rr);
|
||||
|
||||
return NULL;
|
||||
|
@ -766,16 +764,19 @@ static char *format_txt(DnsTxtItem *first) {
|
|||
return s;
|
||||
}
|
||||
|
||||
int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
|
||||
const char *dns_resource_record_to_string(DnsResourceRecord *rr) {
|
||||
_cleanup_free_ char *k = NULL, *t = NULL;
|
||||
char *s;
|
||||
int r;
|
||||
|
||||
assert(rr);
|
||||
|
||||
if (rr->to_string)
|
||||
return rr->to_string;
|
||||
|
||||
r = dns_resource_key_to_string(rr->key, &k);
|
||||
if (r < 0)
|
||||
return r;
|
||||
return NULL;
|
||||
|
||||
switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
|
||||
|
||||
|
@ -787,7 +788,7 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
|
|||
rr->srv.port,
|
||||
strna(rr->srv.name));
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
return NULL;
|
||||
break;
|
||||
|
||||
case DNS_TYPE_PTR:
|
||||
|
@ -796,25 +797,25 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
|
|||
case DNS_TYPE_DNAME:
|
||||
s = strjoin(k, " ", rr->ptr.name, NULL);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
return NULL;
|
||||
|
||||
break;
|
||||
|
||||
case DNS_TYPE_HINFO:
|
||||
s = strjoin(k, " ", rr->hinfo.cpu, " ", rr->hinfo.os, NULL);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
return NULL;
|
||||
break;
|
||||
|
||||
case DNS_TYPE_SPF: /* exactly the same as TXT */
|
||||
case DNS_TYPE_TXT:
|
||||
t = format_txt(rr->txt.items);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
return NULL;
|
||||
|
||||
s = strjoin(k, " ", t, NULL);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
return NULL;
|
||||
break;
|
||||
|
||||
case DNS_TYPE_A: {
|
||||
|
@ -822,22 +823,22 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
|
|||
|
||||
r = in_addr_to_string(AF_INET, (const union in_addr_union*) &rr->a.in_addr, &x);
|
||||
if (r < 0)
|
||||
return r;
|
||||
return NULL;
|
||||
|
||||
s = strjoin(k, " ", x, NULL);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
return NULL;
|
||||
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;
|
||||
return NULL;
|
||||
|
||||
s = strjoin(k, " ", t, NULL);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
return NULL;
|
||||
break;
|
||||
|
||||
case DNS_TYPE_SOA:
|
||||
|
@ -851,7 +852,7 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
|
|||
rr->soa.expire,
|
||||
rr->soa.minimum);
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
return NULL;
|
||||
break;
|
||||
|
||||
case DNS_TYPE_MX:
|
||||
|
@ -860,7 +861,7 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
|
|||
rr->mx.priority,
|
||||
rr->mx.exchange);
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
return NULL;
|
||||
break;
|
||||
|
||||
case DNS_TYPE_LOC:
|
||||
|
@ -873,17 +874,17 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
|
|||
rr->loc.horiz_pre,
|
||||
rr->loc.vert_pre);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
return NULL;
|
||||
|
||||
s = strjoin(k, " ", t, NULL);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
return NULL;
|
||||
break;
|
||||
|
||||
case DNS_TYPE_DS:
|
||||
t = hexmem(rr->ds.digest, rr->ds.digest_size);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
return NULL;
|
||||
|
||||
r = asprintf(&s, "%s %u %u %u %s",
|
||||
k,
|
||||
|
@ -892,13 +893,13 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
|
|||
rr->ds.digest_type,
|
||||
t);
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
return NULL;
|
||||
break;
|
||||
|
||||
case DNS_TYPE_SSHFP:
|
||||
t = hexmem(rr->sshfp.fingerprint, rr->sshfp.fingerprint_size);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
return NULL;
|
||||
|
||||
r = asprintf(&s, "%s %u %u %s",
|
||||
k,
|
||||
|
@ -906,7 +907,7 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
|
|||
rr->sshfp.fptype,
|
||||
t);
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
return NULL;
|
||||
break;
|
||||
|
||||
case DNS_TYPE_DNSKEY: {
|
||||
|
@ -916,7 +917,7 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
|
|||
|
||||
t = base64mem(rr->dnskey.key, rr->dnskey.key_size);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
return NULL;
|
||||
|
||||
r = asprintf(&s, "%s %u %u %.*s%.*u %s",
|
||||
k,
|
||||
|
@ -926,7 +927,7 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
|
|||
alg ? 0 : 1, alg ? 0u : (unsigned) rr->dnskey.algorithm,
|
||||
t);
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -939,15 +940,15 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
|
|||
|
||||
t = base64mem(rr->rrsig.signature, rr->rrsig.signature_size);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
return NULL;
|
||||
|
||||
r = format_timestamp_dns(expiration, sizeof(expiration), rr->rrsig.expiration);
|
||||
if (r < 0)
|
||||
return r;
|
||||
return NULL;
|
||||
|
||||
r = format_timestamp_dns(inception, sizeof(inception), rr->rrsig.inception);
|
||||
if (r < 0)
|
||||
return r;
|
||||
return NULL;
|
||||
|
||||
/* TYPE?? follows
|
||||
* http://tools.ietf.org/html/rfc3597#section-5 */
|
||||
|
@ -966,21 +967,21 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
|
|||
rr->rrsig.signer,
|
||||
t);
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
case DNS_TYPE_NSEC:
|
||||
t = format_types(rr->nsec.types);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
return NULL;
|
||||
|
||||
r = asprintf(&s, "%s %s %s",
|
||||
k,
|
||||
rr->nsec.next_domain_name,
|
||||
t);
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
return NULL;
|
||||
break;
|
||||
|
||||
case DNS_TYPE_NSEC3: {
|
||||
|
@ -989,16 +990,16 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
|
|||
if (rr->nsec3.salt_size > 0) {
|
||||
salt = hexmem(rr->nsec3.salt, rr->nsec3.salt_size);
|
||||
if (!salt)
|
||||
return -ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hash = base32hexmem(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, false);
|
||||
if (!hash)
|
||||
return -ENOMEM;
|
||||
return NULL;
|
||||
|
||||
t = format_types(rr->nsec3.types);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
return NULL;
|
||||
|
||||
r = asprintf(&s, "%s %"PRIu8" %"PRIu8" %"PRIu16" %s %s %s",
|
||||
k,
|
||||
|
@ -1009,7 +1010,7 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
|
|||
hash,
|
||||
t);
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
return NULL;
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -1017,16 +1018,16 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) {
|
|||
default:
|
||||
t = hexmem(rr->generic.data, rr->generic.size);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
return NULL;
|
||||
|
||||
r = asprintf(&s, "%s \\# %zu %s", k, rr->generic.size, t);
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
*ret = s;
|
||||
return 0;
|
||||
rr->to_string = s;
|
||||
return s;
|
||||
}
|
||||
|
||||
int dns_resource_record_to_wire_format(DnsResourceRecord *rr, bool canonical) {
|
||||
|
|
|
@ -95,6 +95,7 @@ struct DnsTxtItem {
|
|||
struct DnsResourceRecord {
|
||||
unsigned n_ref;
|
||||
DnsResourceKey *key;
|
||||
char *to_string;
|
||||
uint32_t ttl;
|
||||
bool unparseable:1;
|
||||
bool wire_format_canonical:1;
|
||||
|
@ -253,7 +254,7 @@ 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_new_address(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);
|
||||
const char* dns_resource_record_to_string(DnsResourceRecord *rr);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceRecord*, dns_resource_record_unref);
|
||||
|
||||
int dns_resource_record_to_wire_format(DnsResourceRecord *rr, bool canonical);
|
||||
|
|
|
@ -162,17 +162,15 @@ void dns_scope_packet_lost(DnsScope *s, usec_t usec) {
|
|||
s->resend_timeout = MIN(s->resend_timeout * 2, MULTICAST_RESEND_TIMEOUT_MAX_USEC);
|
||||
}
|
||||
|
||||
static int dns_scope_emit_one(DnsScope *s, int fd, DnsServer *server, DnsPacket *p) {
|
||||
static int dns_scope_emit_one(DnsScope *s, int fd, DnsPacket *p) {
|
||||
union in_addr_union addr;
|
||||
int ifindex = 0, r;
|
||||
int family;
|
||||
uint32_t mtu;
|
||||
size_t saved_size = 0;
|
||||
|
||||
assert(s);
|
||||
assert(p);
|
||||
assert(p->protocol == s->protocol);
|
||||
assert((s->protocol == DNS_PROTOCOL_DNS) != (fd < 0));
|
||||
|
||||
if (s->link) {
|
||||
mtu = s->link->mtu;
|
||||
|
@ -181,30 +179,13 @@ static int dns_scope_emit_one(DnsScope *s, int fd, DnsServer *server, DnsPacket
|
|||
mtu = manager_find_mtu(s->manager);
|
||||
|
||||
switch (s->protocol) {
|
||||
|
||||
case DNS_PROTOCOL_DNS:
|
||||
assert(server);
|
||||
assert(fd >= 0);
|
||||
|
||||
if (DNS_PACKET_QDCOUNT(p) > 1)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (server->possible_features >= DNS_SERVER_FEATURE_LEVEL_EDNS0) {
|
||||
bool edns_do;
|
||||
size_t packet_size;
|
||||
|
||||
edns_do = server->possible_features >= DNS_SERVER_FEATURE_LEVEL_DO;
|
||||
|
||||
if (server->possible_features >= DNS_SERVER_FEATURE_LEVEL_LARGE)
|
||||
packet_size = DNS_PACKET_UNICAST_SIZE_LARGE_MAX;
|
||||
else
|
||||
packet_size = server->received_udp_packet_max;
|
||||
|
||||
r = dns_packet_append_opt_rr(p, packet_size, edns_do, &saved_size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
DNS_PACKET_HEADER(p)->arcount = htobe16(be16toh(DNS_PACKET_HEADER(p)->arcount) + 1);
|
||||
}
|
||||
|
||||
if (p->size > DNS_PACKET_UNICAST_SIZE_MAX)
|
||||
return -EMSGSIZE;
|
||||
|
||||
|
@ -215,15 +196,11 @@ static int dns_scope_emit_one(DnsScope *s, int fd, DnsServer *server, DnsPacket
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (saved_size > 0) {
|
||||
dns_packet_truncate(p, saved_size);
|
||||
|
||||
DNS_PACKET_HEADER(p)->arcount = htobe16(be16toh(DNS_PACKET_HEADER(p)->arcount) - 1);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case DNS_PROTOCOL_LLMNR:
|
||||
assert(fd < 0);
|
||||
|
||||
if (DNS_PACKET_QDCOUNT(p) > 1)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
|
@ -250,6 +227,8 @@ static int dns_scope_emit_one(DnsScope *s, int fd, DnsServer *server, DnsPacket
|
|||
break;
|
||||
|
||||
case DNS_PROTOCOL_MDNS:
|
||||
assert(fd < 0);
|
||||
|
||||
if (!ratelimit_test(&s->ratelimit))
|
||||
return -EBUSY;
|
||||
|
||||
|
@ -279,13 +258,13 @@ static int dns_scope_emit_one(DnsScope *s, int fd, DnsServer *server, DnsPacket
|
|||
return 1;
|
||||
}
|
||||
|
||||
int dns_scope_emit(DnsScope *s, int fd, DnsServer *server, DnsPacket *p) {
|
||||
int dns_scope_emit_udp(DnsScope *s, int fd, DnsPacket *p) {
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
assert(p);
|
||||
assert(p->protocol == s->protocol);
|
||||
assert((s->protocol == DNS_PROTOCOL_DNS) != (fd < 0));
|
||||
assert((s->protocol == DNS_PROTOCOL_DNS) == (fd >= 0));
|
||||
|
||||
do {
|
||||
/* If there are multiple linked packets, set the TC bit in all but the last of them */
|
||||
|
@ -294,18 +273,24 @@ int dns_scope_emit(DnsScope *s, int fd, DnsServer *server, DnsPacket *p) {
|
|||
dns_packet_set_flags(p, true, true);
|
||||
}
|
||||
|
||||
r = dns_scope_emit_one(s, fd, server, p);
|
||||
r = dns_scope_emit_one(s, fd, p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
p = p->more;
|
||||
} while(p);
|
||||
} while (p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dns_scope_socket(DnsScope *s, int type, int family, const union in_addr_union *address, uint16_t port, DnsServer **server) {
|
||||
DnsServer *srv = NULL;
|
||||
static int dns_scope_socket(
|
||||
DnsScope *s,
|
||||
int type,
|
||||
int family,
|
||||
const union in_addr_union *address,
|
||||
DnsServer *server,
|
||||
uint16_t port) {
|
||||
|
||||
_cleanup_close_ int fd = -1;
|
||||
union sockaddr_union sa = {};
|
||||
socklen_t salen;
|
||||
|
@ -313,31 +298,27 @@ static int dns_scope_socket(DnsScope *s, int type, int family, const union in_ad
|
|||
int ret, r;
|
||||
|
||||
assert(s);
|
||||
assert((family == AF_UNSPEC) == !address);
|
||||
|
||||
if (family == AF_UNSPEC) {
|
||||
srv = dns_scope_get_dns_server(s);
|
||||
if (!srv)
|
||||
return -ESRCH;
|
||||
if (server) {
|
||||
assert(family == AF_UNSPEC);
|
||||
assert(!address);
|
||||
|
||||
srv->possible_features = dns_server_possible_features(srv);
|
||||
|
||||
if (type == SOCK_DGRAM && srv->possible_features < DNS_SERVER_FEATURE_LEVEL_UDP)
|
||||
return -EAGAIN;
|
||||
|
||||
sa.sa.sa_family = srv->family;
|
||||
if (srv->family == AF_INET) {
|
||||
sa.sa.sa_family = server->family;
|
||||
if (server->family == AF_INET) {
|
||||
sa.in.sin_port = htobe16(port);
|
||||
sa.in.sin_addr = srv->address.in;
|
||||
sa.in.sin_addr = server->address.in;
|
||||
salen = sizeof(sa.in);
|
||||
} else if (srv->family == AF_INET6) {
|
||||
} else if (server->family == AF_INET6) {
|
||||
sa.in6.sin6_port = htobe16(port);
|
||||
sa.in6.sin6_addr = srv->address.in6;
|
||||
sa.in6.sin6_addr = server->address.in6;
|
||||
sa.in6.sin6_scope_id = s->link ? s->link->ifindex : 0;
|
||||
salen = sizeof(sa.in6);
|
||||
} else
|
||||
return -EAFNOSUPPORT;
|
||||
} else {
|
||||
assert(family != AF_UNSPEC);
|
||||
assert(address);
|
||||
|
||||
sa.sa.sa_family = family;
|
||||
|
||||
if (family == AF_INET) {
|
||||
|
@ -395,21 +376,18 @@ static int dns_scope_socket(DnsScope *s, int type, int family, const union in_ad
|
|||
if (r < 0 && errno != EINPROGRESS)
|
||||
return -errno;
|
||||
|
||||
if (server)
|
||||
*server = srv;
|
||||
|
||||
ret = fd;
|
||||
fd = -1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int dns_scope_udp_dns_socket(DnsScope *s, DnsServer **server) {
|
||||
return dns_scope_socket(s, SOCK_DGRAM, AF_UNSPEC, NULL, 53, server);
|
||||
int dns_scope_socket_udp(DnsScope *s, DnsServer *server, uint16_t port) {
|
||||
return dns_scope_socket(s, SOCK_DGRAM, AF_UNSPEC, NULL, server, port);
|
||||
}
|
||||
|
||||
int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port, DnsServer **server) {
|
||||
return dns_scope_socket(s, SOCK_STREAM, family, address, port, server);
|
||||
int dns_scope_socket_tcp(DnsScope *s, int family, const union in_addr_union *address, DnsServer *server, uint16_t port) {
|
||||
return dns_scope_socket(s, SOCK_STREAM, family, address, server, port);
|
||||
}
|
||||
|
||||
DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain) {
|
||||
|
@ -868,7 +846,7 @@ static int on_conflict_dispatch(sd_event_source *es, usec_t usec, void *userdata
|
|||
return 0;
|
||||
}
|
||||
|
||||
r = dns_scope_emit(scope, -1, NULL, p);
|
||||
r = dns_scope_emit_udp(scope, -1, p);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to send conflict packet: %m");
|
||||
}
|
||||
|
|
|
@ -82,9 +82,9 @@ DnsScope* dns_scope_free(DnsScope *s);
|
|||
void dns_scope_packet_received(DnsScope *s, usec_t rtt);
|
||||
void dns_scope_packet_lost(DnsScope *s, usec_t usec);
|
||||
|
||||
int dns_scope_emit(DnsScope *s, int fd, DnsServer *server, DnsPacket *p);
|
||||
int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port, DnsServer **server);
|
||||
int dns_scope_udp_dns_socket(DnsScope *s, DnsServer **server);
|
||||
int dns_scope_emit_udp(DnsScope *s, int fd, DnsPacket *p);
|
||||
int dns_scope_socket_tcp(DnsScope *s, int family, const union in_addr_union *address, DnsServer *server, uint16_t port);
|
||||
int dns_scope_socket_udp(DnsScope *s, DnsServer *server, 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);
|
||||
|
|
|
@ -68,8 +68,8 @@ int dns_server_new(
|
|||
|
||||
s->n_ref = 1;
|
||||
s->manager = m;
|
||||
s->verified_features = _DNS_SERVER_FEATURE_LEVEL_INVALID;
|
||||
s->possible_features = DNS_SERVER_FEATURE_LEVEL_BEST;
|
||||
s->verified_feature_level = _DNS_SERVER_FEATURE_LEVEL_INVALID;
|
||||
s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_BEST;
|
||||
s->features_grace_period_usec = DNS_SERVER_FEATURE_GRACE_PERIOD_MIN_USEC;
|
||||
s->received_udp_packet_max = DNS_PACKET_UNICAST_SIZE_MAX;
|
||||
s->type = type;
|
||||
|
@ -224,23 +224,25 @@ void dns_server_move_back_and_unmark(DnsServer *s) {
|
|||
}
|
||||
}
|
||||
|
||||
void dns_server_packet_received(DnsServer *s, DnsServerFeatureLevel features, usec_t rtt, size_t size) {
|
||||
void dns_server_packet_received(DnsServer *s, DnsServerFeatureLevel level, usec_t rtt, size_t size) {
|
||||
assert(s);
|
||||
|
||||
if (features == DNS_SERVER_FEATURE_LEVEL_LARGE) {
|
||||
/* even if we successfully receive a reply to a request announcing
|
||||
support for large packets, that does not mean we can necessarily
|
||||
receive large packets. */
|
||||
if (s->verified_features < DNS_SERVER_FEATURE_LEVEL_LARGE - 1) {
|
||||
s->verified_features = DNS_SERVER_FEATURE_LEVEL_LARGE - 1;
|
||||
if (level == DNS_SERVER_FEATURE_LEVEL_LARGE) {
|
||||
/* Even if we successfully receive a reply to a
|
||||
request announcing support for large packets, that
|
||||
does not mean we can necessarily receive large
|
||||
packets. */
|
||||
|
||||
if (s->verified_feature_level < DNS_SERVER_FEATURE_LEVEL_LARGE - 1) {
|
||||
s->verified_feature_level = DNS_SERVER_FEATURE_LEVEL_LARGE - 1;
|
||||
assert_se(sd_event_now(s->manager->event, clock_boottime_or_monotonic(), &s->verified_usec) >= 0);
|
||||
}
|
||||
} else if (s->verified_features < features) {
|
||||
s->verified_features = features;
|
||||
} else if (s->verified_feature_level < level) {
|
||||
s->verified_feature_level = level;
|
||||
assert_se(sd_event_now(s->manager->event, clock_boottime_or_monotonic(), &s->verified_usec) >= 0);
|
||||
}
|
||||
|
||||
if (s->possible_features == features)
|
||||
if (s->possible_feature_level == level)
|
||||
s->n_failed_attempts = 0;
|
||||
|
||||
/* Remember the size of the largest UDP packet we received from a server,
|
||||
|
@ -255,11 +257,11 @@ void dns_server_packet_received(DnsServer *s, DnsServerFeatureLevel features, us
|
|||
}
|
||||
}
|
||||
|
||||
void dns_server_packet_lost(DnsServer *s, DnsServerFeatureLevel features, usec_t usec) {
|
||||
void dns_server_packet_lost(DnsServer *s, DnsServerFeatureLevel level, usec_t usec) {
|
||||
assert(s);
|
||||
assert(s->manager);
|
||||
|
||||
if (s->possible_features == features)
|
||||
if (s->possible_feature_level == level)
|
||||
s->n_failed_attempts ++;
|
||||
|
||||
if (s->resend_timeout > usec)
|
||||
|
@ -268,16 +270,27 @@ void dns_server_packet_lost(DnsServer *s, DnsServerFeatureLevel features, usec_t
|
|||
s->resend_timeout = MIN(s->resend_timeout * 2, DNS_TIMEOUT_MAX_USEC);
|
||||
}
|
||||
|
||||
void dns_server_packet_failed(DnsServer *s, DnsServerFeatureLevel features) {
|
||||
void dns_server_packet_failed(DnsServer *s, DnsServerFeatureLevel level) {
|
||||
assert(s);
|
||||
assert(s->manager);
|
||||
|
||||
if (s->possible_features != features)
|
||||
if (s->possible_feature_level != level)
|
||||
return;
|
||||
|
||||
s->n_failed_attempts = (unsigned) -1;
|
||||
}
|
||||
|
||||
void dns_server_packet_rrsig_missing(DnsServer *s) {
|
||||
_cleanup_free_ char *ip = NULL;
|
||||
assert(s);
|
||||
assert(s->manager);
|
||||
|
||||
in_addr_to_string(s->family, &s->address, &ip);
|
||||
log_warning("DNS server %s does not augment replies with RRSIG records, DNSSEC not available.", strna(ip));
|
||||
|
||||
s->rrsig_missing = true;
|
||||
}
|
||||
|
||||
static bool dns_server_grace_period_expired(DnsServer *s) {
|
||||
usec_t ts;
|
||||
|
||||
|
@ -297,35 +310,64 @@ static bool dns_server_grace_period_expired(DnsServer *s) {
|
|||
return true;
|
||||
}
|
||||
|
||||
DnsServerFeatureLevel dns_server_possible_features(DnsServer *s) {
|
||||
DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) {
|
||||
assert(s);
|
||||
|
||||
if (s->possible_features != DNS_SERVER_FEATURE_LEVEL_BEST &&
|
||||
if (s->possible_feature_level != DNS_SERVER_FEATURE_LEVEL_BEST &&
|
||||
dns_server_grace_period_expired(s)) {
|
||||
_cleanup_free_ char *ip = NULL;
|
||||
|
||||
s->possible_features = DNS_SERVER_FEATURE_LEVEL_BEST;
|
||||
s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_BEST;
|
||||
s->n_failed_attempts = 0;
|
||||
s->verified_usec = 0;
|
||||
s->rrsig_missing = false;
|
||||
|
||||
in_addr_to_string(s->family, &s->address, &ip);
|
||||
log_info("Grace period over, resuming full feature set for DNS server %s", strna(ip));
|
||||
} else if (s->possible_features <= s->verified_features)
|
||||
s->possible_features = s->verified_features;
|
||||
} else if (s->possible_feature_level <= s->verified_feature_level)
|
||||
s->possible_feature_level = s->verified_feature_level;
|
||||
else if (s->n_failed_attempts >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS &&
|
||||
s->possible_features > DNS_SERVER_FEATURE_LEVEL_WORST) {
|
||||
s->possible_feature_level > DNS_SERVER_FEATURE_LEVEL_WORST) {
|
||||
_cleanup_free_ char *ip = NULL;
|
||||
|
||||
s->possible_features --;
|
||||
s->possible_feature_level --;
|
||||
s->n_failed_attempts = 0;
|
||||
s->verified_usec = 0;
|
||||
|
||||
in_addr_to_string(s->family, &s->address, &ip);
|
||||
log_warning("Using degraded feature set (%s) for DNS server %s",
|
||||
dns_server_feature_level_to_string(s->possible_features), strna(ip));
|
||||
dns_server_feature_level_to_string(s->possible_feature_level), strna(ip));
|
||||
}
|
||||
|
||||
return s->possible_features;
|
||||
return s->possible_feature_level;
|
||||
}
|
||||
|
||||
int dns_server_adjust_opt(DnsServer *server, DnsPacket *packet, DnsServerFeatureLevel level) {
|
||||
size_t packet_size;
|
||||
bool edns_do;
|
||||
int r;
|
||||
|
||||
assert(server);
|
||||
assert(packet);
|
||||
assert(packet->protocol == DNS_PROTOCOL_DNS);
|
||||
|
||||
/* Fix the OPT field in the packet to match our current feature level. */
|
||||
|
||||
r = dns_packet_truncate_opt(packet);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (level < DNS_SERVER_FEATURE_LEVEL_EDNS0)
|
||||
return 0;
|
||||
|
||||
edns_do = level >= DNS_SERVER_FEATURE_LEVEL_DO;
|
||||
|
||||
if (level >= DNS_SERVER_FEATURE_LEVEL_LARGE)
|
||||
packet_size = DNS_PACKET_UNICAST_SIZE_LARGE_MAX;
|
||||
else
|
||||
packet_size = server->received_udp_packet_max;
|
||||
|
||||
return dns_packet_append_opt(packet, packet_size, edns_do, NULL);
|
||||
}
|
||||
|
||||
static void dns_server_hash_func(const void *p, struct siphash *state) {
|
||||
|
|
|
@ -61,18 +61,25 @@ struct DnsServer {
|
|||
int family;
|
||||
union in_addr_union address;
|
||||
|
||||
bool marked:1;
|
||||
|
||||
usec_t resend_timeout;
|
||||
usec_t max_rtt;
|
||||
|
||||
DnsServerFeatureLevel verified_features;
|
||||
DnsServerFeatureLevel possible_features;
|
||||
DnsServerFeatureLevel verified_feature_level;
|
||||
DnsServerFeatureLevel possible_feature_level;
|
||||
size_t received_udp_packet_max;
|
||||
unsigned n_failed_attempts;
|
||||
usec_t verified_usec;
|
||||
usec_t features_grace_period_usec;
|
||||
|
||||
/* Indicates whether responses are augmented with RRSIG by
|
||||
* server or not. Note that this is orthogonal to the feature
|
||||
* level stuff, as it's only information describing responses,
|
||||
* and has no effect on how the questions are asked. */
|
||||
bool rrsig_missing:1;
|
||||
|
||||
/* Used when GC'ing old DNS servers when configuration changes. */
|
||||
bool marked:1;
|
||||
|
||||
/* If linked is set, then this server appears in the servers linked list */
|
||||
bool linked:1;
|
||||
LIST_FIELDS(DnsServer, servers);
|
||||
|
@ -92,9 +99,14 @@ DnsServer* dns_server_unref(DnsServer *s);
|
|||
void dns_server_unlink(DnsServer *s);
|
||||
void dns_server_move_back_and_unmark(DnsServer *s);
|
||||
|
||||
void dns_server_packet_received(DnsServer *s, DnsServerFeatureLevel features, usec_t rtt, size_t size);
|
||||
void dns_server_packet_lost(DnsServer *s, DnsServerFeatureLevel features, usec_t usec);
|
||||
void dns_server_packet_failed(DnsServer *s, DnsServerFeatureLevel features);
|
||||
void dns_server_packet_received(DnsServer *s, DnsServerFeatureLevel level, usec_t rtt, size_t size);
|
||||
void dns_server_packet_lost(DnsServer *s, DnsServerFeatureLevel level, usec_t usec);
|
||||
void dns_server_packet_failed(DnsServer *s, DnsServerFeatureLevel level);
|
||||
void dns_server_packet_rrsig_missing(DnsServer *s);
|
||||
|
||||
DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s);
|
||||
|
||||
int dns_server_adjust_opt(DnsServer *server, DnsPacket *packet, DnsServerFeatureLevel level);
|
||||
|
||||
DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr);
|
||||
|
||||
|
@ -110,6 +122,4 @@ void manager_next_dns_server(Manager *m);
|
|||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsServer*, dns_server_unref);
|
||||
|
||||
DnsServerFeatureLevel dns_server_possible_features(DnsServer *s);
|
||||
|
||||
extern const struct hash_ops dns_server_hash_ops;
|
||||
|
|
|
@ -347,7 +347,6 @@ DnsStream *dns_stream_free(DnsStream *s) {
|
|||
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;
|
||||
|
||||
|
@ -364,10 +363,6 @@ int dns_stream_new(Manager *m, DnsStream **ret, DnsProtocol protocol, int fd) {
|
|||
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;
|
||||
|
|
|
@ -29,6 +29,35 @@
|
|||
#include "resolved-llmnr.h"
|
||||
#include "string-table.h"
|
||||
|
||||
static void dns_transaction_reset_answer(DnsTransaction *t) {
|
||||
assert(t);
|
||||
|
||||
t->received = dns_packet_unref(t->received);
|
||||
t->answer = dns_answer_unref(t->answer);
|
||||
t->answer_rcode = 0;
|
||||
t->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
|
||||
t->answer_source = _DNS_TRANSACTION_SOURCE_INVALID;
|
||||
t->answer_authenticated = false;
|
||||
}
|
||||
|
||||
static void dns_transaction_close_connection(DnsTransaction *t) {
|
||||
assert(t);
|
||||
|
||||
t->stream = dns_stream_free(t->stream);
|
||||
t->dns_udp_event_source = sd_event_source_unref(t->dns_udp_event_source);
|
||||
t->dns_udp_fd = safe_close(t->dns_udp_fd);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
/* Note that we do not drop the UDP socket here, as we want to
|
||||
* reuse it to repeat the interaction. */
|
||||
}
|
||||
|
||||
DnsTransaction* dns_transaction_free(DnsTransaction *t) {
|
||||
DnsQueryCandidate *c;
|
||||
DnsZoneItem *i;
|
||||
|
@ -37,18 +66,13 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) {
|
|||
if (!t)
|
||||
return NULL;
|
||||
|
||||
sd_event_source_unref(t->timeout_event_source);
|
||||
dns_transaction_close_connection(t);
|
||||
dns_transaction_stop(t);
|
||||
|
||||
dns_packet_unref(t->sent);
|
||||
dns_packet_unref(t->received);
|
||||
|
||||
dns_answer_unref(t->answer);
|
||||
|
||||
sd_event_source_unref(t->dns_udp_event_source);
|
||||
safe_close(t->dns_udp_fd);
|
||||
dns_transaction_reset_answer(t);
|
||||
|
||||
dns_server_unref(t->server);
|
||||
dns_stream_free(t->stream);
|
||||
|
||||
if (t->scope) {
|
||||
hashmap_remove_value(t->scope->transactions_by_key, t->key, t);
|
||||
|
@ -58,8 +82,6 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) {
|
|||
hashmap_remove(t->scope->manager->dns_transactions, UINT_TO_PTR(t->id));
|
||||
}
|
||||
|
||||
dns_resource_key_unref(t->key);
|
||||
|
||||
while ((c = set_steal_first(t->notify_query_candidates)))
|
||||
set_remove(c->transactions, t);
|
||||
set_free(t->notify_query_candidates);
|
||||
|
@ -79,8 +101,9 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) {
|
|||
set_free(t->dnssec_transactions);
|
||||
|
||||
dns_answer_unref(t->validated_keys);
|
||||
|
||||
dns_resource_key_unref(t->key);
|
||||
free(t->key_string);
|
||||
|
||||
free(t);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -153,6 +176,8 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key)
|
|||
LIST_PREPEND(transactions_by_scope, s->transactions, t);
|
||||
t->scope = s;
|
||||
|
||||
s->manager->n_transactions_total ++;
|
||||
|
||||
if (ret)
|
||||
*ret = t;
|
||||
|
||||
|
@ -161,16 +186,6 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key)
|
|||
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);
|
||||
|
||||
/* Note that we do not drop the UDP socket here, as we want to
|
||||
* reuse it to repeat the interaction. */
|
||||
}
|
||||
|
||||
static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) {
|
||||
_cleanup_free_ char *pretty = NULL;
|
||||
DnsZoneItem *z;
|
||||
|
@ -224,6 +239,14 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
|
|||
assert(t);
|
||||
assert(!DNS_TRANSACTION_IS_LIVE(state));
|
||||
|
||||
if (state == DNS_TRANSACTION_DNSSEC_FAILED)
|
||||
log_struct(LOG_NOTICE,
|
||||
LOG_MESSAGE("DNSSEC validation failed for question %s: %s", dns_transaction_key_string(t), dnssec_result_to_string(t->answer_dnssec_result)),
|
||||
"DNS_TRANSACTION=%" PRIu16, t->id,
|
||||
"DNS_QUESTION=%s", dns_transaction_key_string(t),
|
||||
"DNSSEC_RESULT=%s", dnssec_result_to_string(t->answer_dnssec_result),
|
||||
NULL);
|
||||
|
||||
/* Note that this call might invalidate the query. Callers
|
||||
* should hence not attempt to access the query or transaction
|
||||
* after calling this function. */
|
||||
|
@ -240,6 +263,7 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
|
|||
|
||||
t->state = state;
|
||||
|
||||
dns_transaction_close_connection(t);
|
||||
dns_transaction_stop(t);
|
||||
|
||||
/* Notify all queries that are interested, but make sure the
|
||||
|
@ -284,6 +308,27 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
|
|||
dns_transaction_gc(t);
|
||||
}
|
||||
|
||||
static int dns_transaction_pick_server(DnsTransaction *t) {
|
||||
DnsServer *server;
|
||||
|
||||
assert(t);
|
||||
assert(t->scope->protocol == DNS_PROTOCOL_DNS);
|
||||
|
||||
server = dns_scope_get_dns_server(t->scope);
|
||||
if (!server)
|
||||
return -ESRCH;
|
||||
|
||||
t->current_features = dns_server_possible_feature_level(server);
|
||||
|
||||
if (server == t->server)
|
||||
return 0;
|
||||
|
||||
dns_server_unref(t->server);
|
||||
t->server = dns_server_ref(server);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int on_stream_complete(DnsStream *s, int error) {
|
||||
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
|
||||
DnsTransaction *t;
|
||||
|
@ -298,6 +343,11 @@ static int on_stream_complete(DnsStream *s, int error) {
|
|||
|
||||
t->stream = dns_stream_free(t->stream);
|
||||
|
||||
if (IN_SET(error, ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE)) {
|
||||
dns_transaction_complete(t, DNS_TRANSACTION_CONNECTION_FAILURE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (error != 0) {
|
||||
dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
|
||||
return 0;
|
||||
|
@ -315,32 +365,43 @@ static int on_stream_complete(DnsStream *s, int error) {
|
|||
dns_transaction_process_reply(t, p);
|
||||
t->block_gc--;
|
||||
|
||||
/* If the response wasn't useful, then complete the transition now */
|
||||
/* If the response wasn't useful, then complete the transition
|
||||
* now. After all, we are the worst feature set now with TCP
|
||||
* sockets, and there's really no point in retrying. */
|
||||
if (t->state == DNS_TRANSACTION_PENDING)
|
||||
dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
|
||||
else
|
||||
dns_transaction_gc(t);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dns_transaction_open_tcp(DnsTransaction *t) {
|
||||
DnsServer *server = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
int r;
|
||||
|
||||
assert(t);
|
||||
|
||||
if (t->stream)
|
||||
return 0;
|
||||
dns_transaction_close_connection(t);
|
||||
|
||||
switch (t->scope->protocol) {
|
||||
|
||||
case DNS_PROTOCOL_DNS:
|
||||
fd = dns_scope_tcp_socket(t->scope, AF_UNSPEC, NULL, 53, &server);
|
||||
r = dns_transaction_pick_server(t);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dns_server_adjust_opt(t->server, t->sent, t->current_features);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
fd = dns_scope_socket_tcp(t->scope, AF_UNSPEC, NULL, t->server, 53);
|
||||
break;
|
||||
|
||||
case DNS_PROTOCOL_LLMNR:
|
||||
/* When we already received a reply 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, NULL);
|
||||
fd = dns_scope_socket_tcp(t->scope, t->received->family, &t->received->sender, NULL, t->received->sender_port);
|
||||
else {
|
||||
union in_addr_union address;
|
||||
int family = AF_UNSPEC;
|
||||
|
@ -357,7 +418,7 @@ static int dns_transaction_open_tcp(DnsTransaction *t) {
|
|||
if (family != t->scope->family)
|
||||
return -ESRCH;
|
||||
|
||||
fd = dns_scope_tcp_socket(t->scope, family, &address, LLMNR_PORT, NULL);
|
||||
fd = dns_scope_socket_tcp(t->scope, family, &address, NULL, LLMNR_PORT);
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -372,7 +433,6 @@ static int dns_transaction_open_tcp(DnsTransaction *t) {
|
|||
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);
|
||||
|
@ -381,11 +441,6 @@ static int dns_transaction_open_tcp(DnsTransaction *t) {
|
|||
return r;
|
||||
}
|
||||
|
||||
dns_server_unref(t->server);
|
||||
t->server = dns_server_ref(server);
|
||||
t->received = dns_packet_unref(t->received);
|
||||
t->answer = dns_answer_unref(t->answer);
|
||||
t->answer_rcode = 0;
|
||||
t->stream->complete = on_stream_complete;
|
||||
t->stream->transaction = t;
|
||||
|
||||
|
@ -395,19 +450,13 @@ static int dns_transaction_open_tcp(DnsTransaction *t) {
|
|||
if (t->scope->link)
|
||||
t->stream->ifindex = t->scope->link->ifindex;
|
||||
|
||||
dns_transaction_reset_answer(t);
|
||||
|
||||
t->tried_stream = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dns_transaction_next_dns_server(DnsTransaction *t) {
|
||||
assert(t);
|
||||
|
||||
t->server = dns_server_unref(t->server);
|
||||
t->dns_udp_event_source = sd_event_source_unref(t->dns_udp_event_source);
|
||||
t->dns_udp_fd = safe_close(t->dns_udp_fd);
|
||||
|
||||
dns_scope_next_dns_server(t->scope);
|
||||
}
|
||||
|
||||
static void dns_transaction_cache_answer(DnsTransaction *t) {
|
||||
assert(t);
|
||||
|
||||
|
@ -463,10 +512,19 @@ static void dns_transaction_process_dnssec(DnsTransaction *t) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (t->answer_dnssec_result == DNSSEC_INCOMPATIBLE_SERVER &&
|
||||
t->scope->dnssec_mode == DNSSEC_YES) {
|
||||
/* We are not in automatic downgrade mode, and the
|
||||
* server is bad, refuse operation. */
|
||||
dns_transaction_complete(t, DNS_TRANSACTION_DNSSEC_FAILED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IN_SET(t->answer_dnssec_result,
|
||||
_DNSSEC_RESULT_INVALID, /* No DNSSEC validation enabled */
|
||||
DNSSEC_VALIDATED, /* Answer is signed and validated successfully */
|
||||
DNSSEC_UNSIGNED)) { /* Answer is right-fully unsigned */
|
||||
_DNSSEC_RESULT_INVALID, /* No DNSSEC validation enabled */
|
||||
DNSSEC_VALIDATED, /* Answer is signed and validated successfully */
|
||||
DNSSEC_UNSIGNED, /* Answer is right-fully unsigned */
|
||||
DNSSEC_INCOMPATIBLE_SERVER)) { /* Server does not do DNSSEC (Yay, we are downgrade attack vulnerable!) */
|
||||
dns_transaction_complete(t, DNS_TRANSACTION_DNSSEC_FAILED);
|
||||
return;
|
||||
}
|
||||
|
@ -485,10 +543,12 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
|
|||
|
||||
assert(t);
|
||||
assert(p);
|
||||
assert(t->state == DNS_TRANSACTION_PENDING);
|
||||
assert(t->scope);
|
||||
assert(t->scope->manager);
|
||||
|
||||
if (t->state != 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. */
|
||||
|
@ -618,16 +678,16 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
|
|||
}
|
||||
|
||||
/* On DNS, couldn't send? Try immediately again, with a new server */
|
||||
dns_transaction_next_dns_server(t);
|
||||
dns_scope_next_dns_server(t->scope);
|
||||
|
||||
r = dns_transaction_go(t);
|
||||
if (r < 0) {
|
||||
dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Parse message, if it isn't parsed yet. */
|
||||
|
@ -654,6 +714,7 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
|
|||
dns_answer_unref(t->answer);
|
||||
t->answer = dns_answer_ref(p->answer);
|
||||
t->answer_rcode = DNS_PACKET_RCODE(p);
|
||||
t->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
|
||||
t->answer_authenticated = false;
|
||||
|
||||
r = dns_transaction_request_dnssec_keys(t);
|
||||
|
@ -688,39 +749,54 @@ static int on_dns_packet(sd_event_source *s, int fd, uint32_t revents, void *use
|
|||
DNS_PACKET_ID(p) == t->id)
|
||||
dns_transaction_process_reply(t, p);
|
||||
else
|
||||
log_debug("Invalid DNS packet, ignoring.");
|
||||
log_debug("Invalid DNS UDP packet, ignoring.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dns_transaction_emit(DnsTransaction *t) {
|
||||
static int dns_transaction_emit_udp(DnsTransaction *t) {
|
||||
int r;
|
||||
|
||||
assert(t);
|
||||
|
||||
if (t->scope->protocol == DNS_PROTOCOL_DNS && !t->server) {
|
||||
DnsServer *server = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
if (t->scope->protocol == DNS_PROTOCOL_DNS) {
|
||||
|
||||
fd = dns_scope_udp_dns_socket(t->scope, &server);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
r = sd_event_add_io(t->scope->manager->event, &t->dns_udp_event_source, fd, EPOLLIN, on_dns_packet, t);
|
||||
r = dns_transaction_pick_server(t);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
t->dns_udp_fd = fd;
|
||||
fd = -1;
|
||||
t->server = dns_server_ref(server);
|
||||
}
|
||||
if (t->current_features < DNS_SERVER_FEATURE_LEVEL_UDP)
|
||||
return -EAGAIN;
|
||||
|
||||
r = dns_scope_emit(t->scope, t->dns_udp_fd, t->server, t->sent);
|
||||
if (r > 0 || t->dns_udp_fd < 0) { /* Server changed, or no connection yet. */
|
||||
int fd;
|
||||
|
||||
dns_transaction_close_connection(t);
|
||||
|
||||
fd = dns_scope_socket_udp(t->scope, t->server, 53);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
r = sd_event_add_io(t->scope->manager->event, &t->dns_udp_event_source, fd, EPOLLIN, on_dns_packet, t);
|
||||
if (r < 0) {
|
||||
safe_close(fd);
|
||||
return r;
|
||||
}
|
||||
|
||||
t->dns_udp_fd = fd;
|
||||
}
|
||||
|
||||
r = dns_server_adjust_opt(t->server, t->sent, t->current_features);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else
|
||||
dns_transaction_close_connection(t);
|
||||
|
||||
r = dns_scope_emit_udp(t->scope, t->dns_udp_fd, t->sent);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (t->server)
|
||||
t->current_features = t->server->possible_features;
|
||||
dns_transaction_reset_answer(t);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -735,17 +811,17 @@ static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdat
|
|||
if (!t->initial_jitter_scheduled || t->initial_jitter_elapsed) {
|
||||
/* Timeout reached? Increase the timeout for the server used */
|
||||
switch (t->scope->protocol) {
|
||||
|
||||
case DNS_PROTOCOL_DNS:
|
||||
assert(t->server);
|
||||
|
||||
dns_server_packet_lost(t->server, t->current_features, usec - t->start_usec);
|
||||
|
||||
break;
|
||||
|
||||
case DNS_PROTOCOL_LLMNR:
|
||||
case DNS_PROTOCOL_MDNS:
|
||||
dns_scope_packet_lost(t->scope, usec - t->start_usec);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached("Invalid DNS protocol.");
|
||||
}
|
||||
|
@ -757,7 +833,7 @@ static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdat
|
|||
log_debug("Timeout reached on transaction %" PRIu16 ".", t->id);
|
||||
|
||||
/* ...and try again with a new server */
|
||||
dns_transaction_next_dns_server(t);
|
||||
dns_scope_next_dns_server(t->scope);
|
||||
|
||||
r = dns_transaction_go(t);
|
||||
if (r < 0)
|
||||
|
@ -771,28 +847,28 @@ static usec_t transaction_get_resend_timeout(DnsTransaction *t) {
|
|||
assert(t->scope);
|
||||
|
||||
switch (t->scope->protocol) {
|
||||
|
||||
case DNS_PROTOCOL_DNS:
|
||||
assert(t->server);
|
||||
|
||||
return t->server->resend_timeout;
|
||||
|
||||
case DNS_PROTOCOL_MDNS:
|
||||
assert(t->n_attempts > 0);
|
||||
return (1 << (t->n_attempts - 1)) * USEC_PER_SEC;
|
||||
|
||||
case DNS_PROTOCOL_LLMNR:
|
||||
return t->scope->resend_timeout;
|
||||
|
||||
default:
|
||||
assert_not_reached("Invalid DNS protocol.");
|
||||
}
|
||||
}
|
||||
|
||||
static int dns_transaction_prepare(DnsTransaction *t, usec_t ts) {
|
||||
bool had_stream;
|
||||
int r;
|
||||
|
||||
assert(t);
|
||||
|
||||
had_stream = !!t->stream;
|
||||
|
||||
dns_transaction_stop(t);
|
||||
|
||||
if (t->n_attempts >= TRANSACTION_ATTEMPTS_MAX(t->scope->protocol)) {
|
||||
|
@ -800,7 +876,7 @@ static int dns_transaction_prepare(DnsTransaction *t, usec_t ts) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (t->scope->protocol == DNS_PROTOCOL_LLMNR && had_stream) {
|
||||
if (t->scope->protocol == DNS_PROTOCOL_LLMNR && t->tried_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);
|
||||
|
@ -809,10 +885,8 @@ static int dns_transaction_prepare(DnsTransaction *t, usec_t ts) {
|
|||
|
||||
t->n_attempts++;
|
||||
t->start_usec = ts;
|
||||
t->received = dns_packet_unref(t->received);
|
||||
t->answer = dns_answer_unref(t->answer);
|
||||
t->answer_rcode = 0;
|
||||
t->answer_source = _DNS_TRANSACTION_SOURCE_INVALID;
|
||||
|
||||
dns_transaction_reset_answer(t);
|
||||
|
||||
/* Check the trust anchor. Do so only on classic DNS, since DNSSEC does not apply otherwise. */
|
||||
if (t->scope->protocol == DNS_PROTOCOL_DNS) {
|
||||
|
@ -986,7 +1060,7 @@ static int dns_transaction_make_packet(DnsTransaction *t) {
|
|||
if (t->sent)
|
||||
return 0;
|
||||
|
||||
r = dns_packet_new_query(&p, t->scope->protocol, 0, t->scope->dnssec_mode == DNSSEC_YES);
|
||||
r = dns_packet_new_query(&p, t->scope->protocol, 0, t->scope->dnssec_mode != DNSSEC_NO);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -1041,10 +1115,12 @@ int dns_transaction_go(DnsTransaction *t) {
|
|||
random_bytes(&jitter, sizeof(jitter));
|
||||
|
||||
switch (t->scope->protocol) {
|
||||
|
||||
case DNS_PROTOCOL_LLMNR:
|
||||
jitter %= LLMNR_JITTER_INTERVAL_USEC;
|
||||
accuracy = LLMNR_JITTER_INTERVAL_USEC;
|
||||
break;
|
||||
|
||||
case DNS_PROTOCOL_MDNS:
|
||||
jitter %= MDNS_JITTER_RANGE_USEC;
|
||||
jitter += MDNS_JITTER_MIN_USEC;
|
||||
|
@ -1093,7 +1169,7 @@ int dns_transaction_go(DnsTransaction *t) {
|
|||
} else {
|
||||
/* Try via UDP, and if that fails due to large size or lack of
|
||||
* support try via TCP */
|
||||
r = dns_transaction_emit(t);
|
||||
r = dns_transaction_emit_udp(t);
|
||||
if (r == -EMSGSIZE || r == -EAGAIN)
|
||||
r = dns_transaction_open_tcp(t);
|
||||
}
|
||||
|
@ -1109,7 +1185,7 @@ int dns_transaction_go(DnsTransaction *t) {
|
|||
}
|
||||
|
||||
/* Couldn't send? Try immediately again, with a new server */
|
||||
dns_transaction_next_dns_server(t);
|
||||
dns_scope_next_dns_server(t->scope);
|
||||
|
||||
return dns_transaction_go(t);
|
||||
}
|
||||
|
@ -1315,15 +1391,20 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
|
|||
* - For RRSIG we get the matching DNSKEY
|
||||
* - For DNSKEY we get the matching DS
|
||||
* - For unsigned SOA/NS we get the matching DS
|
||||
* - For unsigned CNAME/DNAME we get the parent SOA RR
|
||||
* - For unsigned CNAME/DNAME/DS we get the parent SOA RR
|
||||
* - For other unsigned RRs we get the matching SOA RR
|
||||
* - For SOA/NS/DS queries with no matching response RRs, and no NSEC/NSEC3, the parent's SOA RR
|
||||
* - For other queries with no matching response RRs, and no NSEC/NSEC3, the SOA RR
|
||||
*/
|
||||
|
||||
if (t->scope->dnssec_mode != DNSSEC_YES)
|
||||
if (t->scope->dnssec_mode == DNSSEC_NO)
|
||||
return 0;
|
||||
|
||||
if (t->current_features < DNS_SERVER_FEATURE_LEVEL_DO)
|
||||
return 0; /* Server doesn't do DNSSEC, there's no point in requesting any RRs then. */
|
||||
if (t->server && t->server->rrsig_missing)
|
||||
return 0; /* Server handles DNSSEC requests, but isn't augmenting responses with RRSIGs. No point in trying DNSSEC then. */
|
||||
|
||||
DNS_ANSWER_FOREACH(rr, t->answer) {
|
||||
|
||||
if (dns_type_is_pseudo(rr->key->type))
|
||||
|
@ -1404,15 +1485,6 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
|
|||
break;
|
||||
}
|
||||
|
||||
case DNS_TYPE_DS:
|
||||
case DNS_TYPE_NSEC:
|
||||
case DNS_TYPE_NSEC3:
|
||||
/* Don't acquire anything for
|
||||
* DS/NSEC/NSEC3. We require they come with an
|
||||
* RRSIG without us asking for anything, and
|
||||
* that's sufficient. */
|
||||
break;
|
||||
|
||||
case DNS_TYPE_SOA:
|
||||
case DNS_TYPE_NS: {
|
||||
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *ds = NULL;
|
||||
|
@ -1448,6 +1520,7 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
|
|||
break;
|
||||
}
|
||||
|
||||
case DNS_TYPE_DS:
|
||||
case DNS_TYPE_CNAME:
|
||||
case DNS_TYPE_DNAME: {
|
||||
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *soa = NULL;
|
||||
|
@ -1458,7 +1531,10 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
|
|||
* unsigned CNAME/DNAME RRs, maybe that's the
|
||||
* apex. But do all that only if this is
|
||||
* actually a response to our original
|
||||
* question. */
|
||||
* question.
|
||||
*
|
||||
* Similar for DS RRs, which are signed when
|
||||
* the parent SOA is signed. */
|
||||
|
||||
r = dns_transaction_is_primary_response(t, rr);
|
||||
if (r < 0)
|
||||
|
@ -1483,7 +1559,7 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
|
|||
if (!soa)
|
||||
return -ENOMEM;
|
||||
|
||||
log_debug("Requesting parent SOA to validate transaction %" PRIu16 " (%s, unsigned CNAME/DNAME RRset).", t->id, DNS_RESOURCE_KEY_NAME(rr->key));
|
||||
log_debug("Requesting parent SOA to validate transaction %" PRIu16 " (%s, unsigned CNAME/DNAME/DS RRset).", t->id, DNS_RESOURCE_KEY_NAME(rr->key));
|
||||
r = dns_transaction_request_dnssec_rr(t, soa);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
@ -1494,10 +1570,11 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
|
|||
default: {
|
||||
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *soa = NULL;
|
||||
|
||||
/* For other unsigned RRsets, look for proof
|
||||
* the zone is unsigned, by requesting the SOA
|
||||
* RR of the zone. However, do so only if they
|
||||
* are directly relevant to our original
|
||||
/* For other unsigned RRsets (including
|
||||
* NSEC/NSEC3!), look for proof the zone is
|
||||
* unsigned, by requesting the SOA RR of the
|
||||
* zone. However, do so only if they are
|
||||
* directly relevant to our original
|
||||
* question. */
|
||||
|
||||
r = dns_transaction_is_primary_response(t, rr);
|
||||
|
@ -1516,7 +1593,7 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
|
|||
if (!soa)
|
||||
return -ENOMEM;
|
||||
|
||||
log_debug("Requesting SOA to validate transaction %" PRIu16 " (%s, unsigned non-SOA/NS RRset).", t->id, DNS_RESOURCE_KEY_NAME(rr->key));
|
||||
log_debug("Requesting SOA to validate transaction %" PRIu16 " (%s, unsigned non-SOA/NS RRset <%s>).", t->id, DNS_RESOURCE_KEY_NAME(rr->key), dns_resource_record_to_string(rr));
|
||||
r = dns_transaction_request_dnssec_rr(t, soa);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
@ -1671,7 +1748,7 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord *
|
|||
/* Checks if the RR we are looking for must be signed with an
|
||||
* RRSIG. This is used for positive responses. */
|
||||
|
||||
if (t->scope->dnssec_mode != DNSSEC_YES)
|
||||
if (t->scope->dnssec_mode == DNSSEC_NO)
|
||||
return false;
|
||||
|
||||
if (dns_type_is_pseudo(rr->key->type))
|
||||
|
@ -1679,13 +1756,6 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord *
|
|||
|
||||
switch (rr->key->type) {
|
||||
|
||||
case DNS_TYPE_DNSKEY:
|
||||
case DNS_TYPE_DS:
|
||||
case DNS_TYPE_NSEC:
|
||||
case DNS_TYPE_NSEC3:
|
||||
/* We never consider DNSKEY, DS, NSEC, NSEC3 RRs if they aren't signed. */
|
||||
return true;
|
||||
|
||||
case DNS_TYPE_RRSIG:
|
||||
/* RRSIGs are the signatures themselves, they need no signing. */
|
||||
return false;
|
||||
|
@ -1695,7 +1765,7 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord *
|
|||
DnsTransaction *dt;
|
||||
Iterator i;
|
||||
|
||||
/* For SOA or NS RRs we look for a matching DS transaction, or a SOA transaction of the parent */
|
||||
/* For SOA or NS RRs we look for a matching DS transaction */
|
||||
|
||||
SET_FOREACH(dt, t->dnssec_transactions, i) {
|
||||
|
||||
|
@ -1726,13 +1796,18 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord *
|
|||
return true;
|
||||
}
|
||||
|
||||
case DNS_TYPE_DS:
|
||||
case DNS_TYPE_CNAME:
|
||||
case DNS_TYPE_DNAME: {
|
||||
const char *parent = NULL;
|
||||
DnsTransaction *dt;
|
||||
Iterator i;
|
||||
|
||||
/* CNAME/DNAME RRs cannot be located at a zone apex, hence look directly for the parent SOA. */
|
||||
/*
|
||||
* CNAME/DNAME RRs cannot be located at a zone apex, hence look directly for the parent SOA.
|
||||
*
|
||||
* DS RRs are signed if the parent is signed, hence also look at the parent SOA
|
||||
*/
|
||||
|
||||
SET_FOREACH(dt, t->dnssec_transactions, i) {
|
||||
|
||||
|
@ -1747,6 +1822,9 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord *
|
|||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
if (rr->key->type == DNS_TYPE_DS)
|
||||
return true;
|
||||
|
||||
/* A CNAME/DNAME without a parent? That's sooo weird. */
|
||||
log_debug("Transaction %" PRIu16 " claims CNAME/DNAME at root. Refusing.", t->id);
|
||||
return -EBADMSG;
|
||||
|
@ -1769,7 +1847,7 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord *
|
|||
DnsTransaction *dt;
|
||||
Iterator i;
|
||||
|
||||
/* Any other kind of RR. Let's see if our SOA lookup was authenticated */
|
||||
/* Any other kind of RR (including DNSKEY/NSEC/NSEC3). Let's see if our SOA lookup was authenticated */
|
||||
|
||||
SET_FOREACH(dt, t->dnssec_transactions, i) {
|
||||
|
||||
|
@ -1807,7 +1885,7 @@ static int dns_transaction_requires_nsec(DnsTransaction *t) {
|
|||
/* Checks if we need to insist on NSEC/NSEC3 RRs for proving
|
||||
* this negative reply */
|
||||
|
||||
if (t->scope->dnssec_mode != DNSSEC_YES)
|
||||
if (t->scope->dnssec_mode == DNSSEC_NO)
|
||||
return false;
|
||||
|
||||
if (dns_type_is_pseudo(t->key->type))
|
||||
|
@ -1855,6 +1933,82 @@ static int dns_transaction_requires_nsec(DnsTransaction *t) {
|
|||
return true;
|
||||
}
|
||||
|
||||
static int dns_transaction_dnskey_authenticated(DnsTransaction *t, DnsResourceRecord *rr) {
|
||||
DnsResourceRecord *rrsig;
|
||||
bool found = false;
|
||||
int r;
|
||||
|
||||
/* Checks whether any of the DNSKEYs used for the RRSIGs for
|
||||
* the specified RRset is authenticated (i.e. has a matching
|
||||
* DS RR). */
|
||||
|
||||
DNS_ANSWER_FOREACH(rrsig, t->answer) {
|
||||
DnsTransaction *dt;
|
||||
Iterator i;
|
||||
|
||||
r = dnssec_key_match_rrsig(rr->key, rrsig);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
continue;
|
||||
|
||||
SET_FOREACH(dt, t->dnssec_transactions, i) {
|
||||
|
||||
if (dt->key->class != rr->key->class)
|
||||
continue;
|
||||
|
||||
if (dt->key->type == DNS_TYPE_DNSKEY) {
|
||||
|
||||
r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dt->key), rrsig->rrsig.signer);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
continue;
|
||||
|
||||
/* OK, we found an auxiliary DNSKEY
|
||||
* lookup. If that lookup is
|
||||
* authenticated, report this. */
|
||||
|
||||
if (dt->answer_authenticated)
|
||||
return true;
|
||||
|
||||
found = true;
|
||||
|
||||
} else if (dt->key->type == DNS_TYPE_DS) {
|
||||
|
||||
r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dt->key), rrsig->rrsig.signer);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
continue;
|
||||
|
||||
/* OK, we found an auxiliary DS
|
||||
* lookup. If that lookup is
|
||||
* authenticated and non-zero, we
|
||||
* won! */
|
||||
|
||||
if (!dt->answer_authenticated)
|
||||
return false;
|
||||
|
||||
return dns_answer_match_key(dt->answer, dt->key, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found ? false : -ENXIO;
|
||||
}
|
||||
|
||||
static int dns_transaction_known_signed(DnsTransaction *t, DnsResourceRecord *rr) {
|
||||
assert(t);
|
||||
assert(rr);
|
||||
|
||||
/* We know that the root domain is signed, hence if it appears
|
||||
* not to be signed, there's a problem with the DNS server */
|
||||
|
||||
return rr->key->class == DNS_CLASS_IN &&
|
||||
dns_name_is_root(DNS_RESOURCE_KEY_NAME(rr->key));
|
||||
}
|
||||
|
||||
int dns_transaction_validate_dnssec(DnsTransaction *t) {
|
||||
_cleanup_(dns_answer_unrefp) DnsAnswer *validated = NULL;
|
||||
bool dnskeys_finalized = false;
|
||||
|
@ -1868,7 +2022,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
|
|||
* t->validated_keys, let's see which RRs we can now
|
||||
* authenticate with that. */
|
||||
|
||||
if (t->scope->dnssec_mode != DNSSEC_YES)
|
||||
if (t->scope->dnssec_mode == DNSSEC_NO)
|
||||
return 0;
|
||||
|
||||
/* Already validated */
|
||||
|
@ -1886,6 +2040,13 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
|
|||
if (t->answer_source != DNS_TRANSACTION_NETWORK)
|
||||
return 0;
|
||||
|
||||
if (t->current_features < DNS_SERVER_FEATURE_LEVEL_DO ||
|
||||
(t->server && t->server->rrsig_missing)) {
|
||||
/* The server does not support DNSSEC, or doesn't augment responses with RRSIGs. */
|
||||
t->answer_dnssec_result = DNSSEC_INCOMPATIBLE_SERVER;
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_debug("Validating response from transaction %" PRIu16 " (%s).", t->id, dns_transaction_key_string(t));
|
||||
|
||||
/* First see if there are DNSKEYs we already known a validated DS for. */
|
||||
|
@ -1906,12 +2067,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (log_get_max_level() >= LOG_DEBUG) {
|
||||
_cleanup_free_ char *rrs = NULL;
|
||||
|
||||
(void) dns_resource_record_to_string(rr, &rrs);
|
||||
log_debug("Looking at %s: %s", rrs ? strstrip(rrs) : "???", dnssec_result_to_string(result));
|
||||
}
|
||||
log_debug("Looking at %s: %s", strna(dns_resource_record_to_string(rr)), dnssec_result_to_string(result));
|
||||
|
||||
if (result == DNSSEC_VALIDATED) {
|
||||
|
||||
|
@ -1936,11 +2092,14 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
t->scope->manager->n_dnssec_secure++;
|
||||
|
||||
/* Exit the loop, we dropped something from the answer, start from the beginning */
|
||||
changed = true;
|
||||
break;
|
||||
|
||||
} else if (dnskeys_finalized) {
|
||||
|
||||
/* If we haven't read all DNSKEYs yet
|
||||
* a negative result of the validation
|
||||
* is irrelevant, as there might be
|
||||
|
@ -1957,11 +2116,72 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
t->scope->manager->n_dnssec_insecure++;
|
||||
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
r = dns_transaction_known_signed(t, rr);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0) {
|
||||
/* This is an RR we know has to be signed. If it isn't this means
|
||||
* the server is not attaching RRSIGs, hence complain. */
|
||||
|
||||
dns_server_packet_rrsig_missing(t->server);
|
||||
|
||||
if (t->scope->dnssec_mode == DNSSEC_DOWNGRADE_OK) {
|
||||
|
||||
/* Downgrading is OK? If so, just consider the information unsigned */
|
||||
|
||||
r = dns_answer_move_by_key(&validated, &t->answer, rr->key, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
t->scope->manager->n_dnssec_insecure++;
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Otherwise, fail */
|
||||
t->answer_dnssec_result = DNSSEC_INCOMPATIBLE_SERVER;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (IN_SET(result,
|
||||
DNSSEC_MISSING_KEY,
|
||||
DNSSEC_SIGNATURE_EXPIRED,
|
||||
DNSSEC_UNSUPPORTED_ALGORITHM)) {
|
||||
|
||||
r = dns_transaction_dnskey_authenticated(t, rr);
|
||||
if (r < 0 && r != -ENXIO)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
/* The DNSKEY transaction was not authenticated, this means there's
|
||||
* no DS for this, which means it's OK if no keys are found for this signature. */
|
||||
|
||||
r = dns_answer_move_by_key(&validated, &t->answer, rr->key, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
t->scope->manager->n_dnssec_insecure++;
|
||||
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (IN_SET(result,
|
||||
DNSSEC_INVALID,
|
||||
DNSSEC_SIGNATURE_EXPIRED,
|
||||
DNSSEC_NO_SIGNATURE,
|
||||
DNSSEC_UNSUPPORTED_ALGORITHM))
|
||||
t->scope->manager->n_dnssec_bogus++;
|
||||
else
|
||||
t->scope->manager->n_dnssec_indeterminate++;
|
||||
|
||||
r = dns_transaction_is_primary_response(t, rr);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
@ -2030,9 +2250,10 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
|
|||
|
||||
} else if (r == 0) {
|
||||
DnssecNsecResult nr;
|
||||
bool authenticated = false;
|
||||
|
||||
/* Bummer! Let's check NSEC/NSEC3 */
|
||||
r = dnssec_test_nsec(t->answer, t->key, &nr);
|
||||
r = dnssec_test_nsec(t->answer, t->key, &nr, &authenticated);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -2043,7 +2264,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
|
|||
log_debug("Proved NXDOMAIN via NSEC/NSEC3 for transaction %u (%s)", t->id, dns_transaction_key_string(t));
|
||||
t->answer_dnssec_result = DNSSEC_VALIDATED;
|
||||
t->answer_rcode = DNS_RCODE_NXDOMAIN;
|
||||
t->answer_authenticated = true;
|
||||
t->answer_authenticated = authenticated;
|
||||
break;
|
||||
|
||||
case DNSSEC_NSEC_NODATA:
|
||||
|
@ -2051,7 +2272,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
|
|||
log_debug("Proved NODATA via NSEC/NSEC3 for transaction %u (%s)", t->id, dns_transaction_key_string(t));
|
||||
t->answer_dnssec_result = DNSSEC_VALIDATED;
|
||||
t->answer_rcode = DNS_RCODE_SUCCESS;
|
||||
t->answer_authenticated = true;
|
||||
t->answer_authenticated = authenticated;
|
||||
break;
|
||||
|
||||
case DNSSEC_NSEC_OPTOUT:
|
||||
|
@ -2116,6 +2337,7 @@ static const char* const dns_transaction_state_table[_DNS_TRANSACTION_STATE_MAX]
|
|||
[DNS_TRANSACTION_ATTEMPTS_MAX_REACHED] = "attempts-max-reached",
|
||||
[DNS_TRANSACTION_INVALID_REPLY] = "invalid-reply",
|
||||
[DNS_TRANSACTION_RESOURCES] = "resources",
|
||||
[DNS_TRANSACTION_CONNECTION_FAILURE] = "connection-failure",
|
||||
[DNS_TRANSACTION_ABORTED] = "aborted",
|
||||
[DNS_TRANSACTION_DNSSEC_FAILED] = "dnssec-failed",
|
||||
};
|
||||
|
|
|
@ -36,6 +36,7 @@ enum DnsTransactionState {
|
|||
DNS_TRANSACTION_ATTEMPTS_MAX_REACHED,
|
||||
DNS_TRANSACTION_INVALID_REPLY,
|
||||
DNS_TRANSACTION_RESOURCES,
|
||||
DNS_TRANSACTION_CONNECTION_FAILURE,
|
||||
DNS_TRANSACTION_ABORTED,
|
||||
DNS_TRANSACTION_DNSSEC_FAILED,
|
||||
_DNS_TRANSACTION_STATE_MAX,
|
||||
|
@ -68,6 +69,8 @@ struct DnsTransaction {
|
|||
|
||||
uint16_t id;
|
||||
|
||||
bool tried_stream:1;
|
||||
|
||||
bool initial_jitter_scheduled:1;
|
||||
bool initial_jitter_elapsed:1;
|
||||
|
||||
|
@ -97,18 +100,19 @@ struct DnsTransaction {
|
|||
sd_event_source *timeout_event_source;
|
||||
unsigned n_attempts;
|
||||
|
||||
/* UDP connection logic, if we need it */
|
||||
int dns_udp_fd;
|
||||
sd_event_source *dns_udp_event_source;
|
||||
|
||||
/* TCP connection logic, if we need it */
|
||||
DnsStream *stream;
|
||||
|
||||
/* The active server */
|
||||
DnsServer *server;
|
||||
|
||||
/* The features of the DNS server at time of transaction start */
|
||||
DnsServerFeatureLevel current_features;
|
||||
|
||||
/* TCP connection logic, if we need it */
|
||||
DnsStream *stream;
|
||||
|
||||
/* Query candidates this transaction is referenced by and that
|
||||
* shall be notified about this specific transaction
|
||||
* completing. */
|
||||
|
|
|
@ -471,15 +471,12 @@ return_empty:
|
|||
}
|
||||
|
||||
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));
|
||||
log_info("Detected conflict on %s", strna(dns_resource_record_to_string(i->rr)));
|
||||
|
||||
dns_zone_item_probe_stop(i);
|
||||
|
||||
|
@ -492,8 +489,6 @@ void dns_zone_item_conflict(DnsZoneItem *i) {
|
|||
}
|
||||
|
||||
void dns_zone_item_notify(DnsZoneItem *i) {
|
||||
_cleanup_free_ char *pretty = NULL;
|
||||
|
||||
assert(i);
|
||||
assert(i->probe_transaction);
|
||||
|
||||
|
@ -530,15 +525,13 @@ void dns_zone_item_notify(DnsZoneItem *i) {
|
|||
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));
|
||||
log_debug("Record %s successfully probed.", strna(dns_resource_record_to_string(i->rr)));
|
||||
|
||||
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);
|
||||
|
@ -546,8 +539,7 @@ static int dns_zone_item_verify(DnsZoneItem *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));
|
||||
log_debug("Verifying RR %s", strna(dns_resource_record_to_string(i->rr)));
|
||||
|
||||
i->state = DNS_ZONE_ITEM_VERIFYING;
|
||||
r = dns_zone_item_probe_start(i);
|
||||
|
@ -632,7 +624,6 @@ void dns_zone_verify_all(DnsZone *zone) {
|
|||
void dns_zone_dump(DnsZone *zone, FILE *f) {
|
||||
Iterator iterator;
|
||||
DnsZoneItem *i;
|
||||
int r;
|
||||
|
||||
if (!zone)
|
||||
return;
|
||||
|
@ -644,10 +635,10 @@ void dns_zone_dump(DnsZone *zone, FILE *f) {
|
|||
DnsZoneItem *j;
|
||||
|
||||
LIST_FOREACH(by_key, j, i) {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
const char *t;
|
||||
|
||||
r = dns_resource_record_to_string(j->rr, &t);
|
||||
if (r < 0) {
|
||||
t = dns_resource_record_to_string(j->rr);
|
||||
if (!t) {
|
||||
log_oom();
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -117,7 +117,7 @@ static int on_llmnr_packet(sd_event_source *s, int fd, uint32_t revents, void *u
|
|||
|
||||
dns_scope_process_query(scope, NULL, p);
|
||||
} else
|
||||
log_debug("Invalid LLMNR UDP packet.");
|
||||
log_debug("Invalid LLMNR UDP packet, ignoring.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -128,6 +128,9 @@ struct Manager {
|
|||
sd_bus_slot *prepare_for_sleep_slot;
|
||||
|
||||
sd_event_source *sigusr1_event_source;
|
||||
|
||||
unsigned n_transactions_total;
|
||||
unsigned n_dnssec_secure, n_dnssec_insecure, n_dnssec_bogus, n_dnssec_indeterminate;
|
||||
};
|
||||
|
||||
/* Manager */
|
||||
|
|
|
@ -56,7 +56,6 @@ static void test_dnssec_verify_rrset2(void) {
|
|||
|
||||
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *nsec = NULL, *rrsig = NULL, *dnskey = NULL;
|
||||
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
|
||||
_cleanup_free_ char *x = NULL, *y = NULL, *z = NULL;
|
||||
DnssecResult result;
|
||||
|
||||
nsec = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_NSEC, "nasa.gov");
|
||||
|
@ -77,8 +76,7 @@ static void test_dnssec_verify_rrset2(void) {
|
|||
assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_DNSKEY) >= 0);
|
||||
assert_se(bitmap_set(nsec->nsec.types, 65534) >= 0);
|
||||
|
||||
assert_se(dns_resource_record_to_string(nsec, &x) >= 0);
|
||||
log_info("NSEC: %s", x);
|
||||
log_info("NSEC: %s", strna(dns_resource_record_to_string(nsec)));
|
||||
|
||||
rrsig = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_RRSIG, "NaSa.GOV.");
|
||||
assert_se(rrsig);
|
||||
|
@ -96,8 +94,7 @@ static void test_dnssec_verify_rrset2(void) {
|
|||
rrsig->rrsig.signature = memdup(signature_blob, rrsig->rrsig.signature_size);
|
||||
assert_se(rrsig->rrsig.signature);
|
||||
|
||||
assert_se(dns_resource_record_to_string(rrsig, &y) >= 0);
|
||||
log_info("RRSIG: %s", y);
|
||||
log_info("RRSIG: %s", strna(dns_resource_record_to_string(rrsig)));
|
||||
|
||||
dnskey = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, "nASA.gOV");
|
||||
assert_se(dnskey);
|
||||
|
@ -109,8 +106,7 @@ static void test_dnssec_verify_rrset2(void) {
|
|||
dnskey->dnskey.key = memdup(dnskey_blob, sizeof(dnskey_blob));
|
||||
assert_se(dnskey->dnskey.key);
|
||||
|
||||
assert_se(dns_resource_record_to_string(dnskey, &z) >= 0);
|
||||
log_info("DNSKEY: %s", z);
|
||||
log_info("DNSKEY: %s", strna(dns_resource_record_to_string(dnskey)));
|
||||
log_info("DNSKEY keytag: %u", dnssec_keytag(dnskey));
|
||||
|
||||
assert_se(dnssec_key_match_rrsig(nsec->key, rrsig) > 0);
|
||||
|
@ -152,7 +148,6 @@ static void test_dnssec_verify_rrset(void) {
|
|||
|
||||
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *a = NULL, *rrsig = NULL, *dnskey = NULL;
|
||||
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
|
||||
_cleanup_free_ char *x = NULL, *y = NULL, *z = NULL;
|
||||
DnssecResult result;
|
||||
|
||||
a = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, "nAsA.gov");
|
||||
|
@ -160,8 +155,7 @@ static void test_dnssec_verify_rrset(void) {
|
|||
|
||||
a->a.in_addr.s_addr = inet_addr("52.0.14.116");
|
||||
|
||||
assert_se(dns_resource_record_to_string(a, &x) >= 0);
|
||||
log_info("A: %s", x);
|
||||
log_info("A: %s", strna(dns_resource_record_to_string(a)));
|
||||
|
||||
rrsig = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_RRSIG, "NaSa.GOV.");
|
||||
assert_se(rrsig);
|
||||
|
@ -179,8 +173,7 @@ static void test_dnssec_verify_rrset(void) {
|
|||
rrsig->rrsig.signature = memdup(signature_blob, rrsig->rrsig.signature_size);
|
||||
assert_se(rrsig->rrsig.signature);
|
||||
|
||||
assert_se(dns_resource_record_to_string(rrsig, &y) >= 0);
|
||||
log_info("RRSIG: %s", y);
|
||||
log_info("RRSIG: %s", strna(dns_resource_record_to_string(rrsig)));
|
||||
|
||||
dnskey = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, "nASA.gOV");
|
||||
assert_se(dnskey);
|
||||
|
@ -192,8 +185,7 @@ static void test_dnssec_verify_rrset(void) {
|
|||
dnskey->dnskey.key = memdup(dnskey_blob, sizeof(dnskey_blob));
|
||||
assert_se(dnskey->dnskey.key);
|
||||
|
||||
assert_se(dns_resource_record_to_string(dnskey, &z) >= 0);
|
||||
log_info("DNSKEY: %s", z);
|
||||
log_info("DNSKEY: %s", strna(dns_resource_record_to_string(dnskey)));
|
||||
log_info("DNSKEY keytag: %u", dnssec_keytag(dnskey));
|
||||
|
||||
assert_se(dnssec_key_match_rrsig(a->key, rrsig) > 0);
|
||||
|
@ -239,7 +231,6 @@ static void test_dnssec_verify_dns_key(void) {
|
|||
};
|
||||
|
||||
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *dnskey = NULL, *ds1 = NULL, *ds2 = NULL;
|
||||
_cleanup_free_ char *a = NULL, *b = NULL, *c = NULL;
|
||||
|
||||
/* The two DS RRs in effect for nasa.gov on 2015-12-01. */
|
||||
ds1 = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, "nasa.gov");
|
||||
|
@ -252,8 +243,7 @@ static void test_dnssec_verify_dns_key(void) {
|
|||
ds1->ds.digest = memdup(ds1_fprint, ds1->ds.digest_size);
|
||||
assert_se(ds1->ds.digest);
|
||||
|
||||
assert_se(dns_resource_record_to_string(ds1, &a) >= 0);
|
||||
log_info("DS1: %s", a);
|
||||
log_info("DS1: %s", strna(dns_resource_record_to_string(ds1)));
|
||||
|
||||
ds2 = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, "NASA.GOV");
|
||||
assert_se(ds2);
|
||||
|
@ -265,8 +255,7 @@ static void test_dnssec_verify_dns_key(void) {
|
|||
ds2->ds.digest = memdup(ds2_fprint, ds2->ds.digest_size);
|
||||
assert_se(ds2->ds.digest);
|
||||
|
||||
assert_se(dns_resource_record_to_string(ds2, &b) >= 0);
|
||||
log_info("DS2: %s", b);
|
||||
log_info("DS2: %s", strna(dns_resource_record_to_string(ds2)));
|
||||
|
||||
dnskey = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, "nasa.GOV");
|
||||
assert_se(dnskey);
|
||||
|
@ -278,8 +267,7 @@ static void test_dnssec_verify_dns_key(void) {
|
|||
dnskey->dnskey.key = memdup(dnskey_blob, sizeof(dnskey_blob));
|
||||
assert_se(dnskey->dnskey.key);
|
||||
|
||||
assert_se(dns_resource_record_to_string(dnskey, &c) >= 0);
|
||||
log_info("DNSKEY: %s", c);
|
||||
log_info("DNSKEY: %s", strna(dns_resource_record_to_string(dnskey)));
|
||||
log_info("DNSKEY keytag: %u", dnssec_keytag(dnskey));
|
||||
|
||||
assert_se(dnssec_verify_dnskey(dnskey, ds1) > 0);
|
||||
|
@ -310,8 +298,8 @@ static void test_dnssec_nsec3_hash(void) {
|
|||
static const uint8_t salt[] = { 0xB0, 0x1D, 0xFA, 0xCE };
|
||||
static const uint8_t next_hashed_name[] = { 0x84, 0x10, 0x26, 0x53, 0xc9, 0xfa, 0x4d, 0x85, 0x6c, 0x97, 0x82, 0xe2, 0x8f, 0xdf, 0x2d, 0x5e, 0x87, 0x69, 0xc4, 0x52 };
|
||||
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
|
||||
_cleanup_free_ char *a = NULL, *b = NULL;
|
||||
uint8_t h[DNSSEC_HASH_SIZE_MAX];
|
||||
_cleanup_free_ char *b = NULL;
|
||||
int k;
|
||||
|
||||
/* The NSEC3 RR for eurid.eu on 2015-12-14. */
|
||||
|
@ -328,8 +316,7 @@ static void test_dnssec_nsec3_hash(void) {
|
|||
assert_se(rr->nsec3.next_hashed_name);
|
||||
rr->nsec3.next_hashed_name_size = sizeof(next_hashed_name);
|
||||
|
||||
assert_se(dns_resource_record_to_string(rr, &a) >= 0);
|
||||
log_info("NSEC3: %s", a);
|
||||
log_info("NSEC3: %s", strna(dns_resource_record_to_string(rr)));
|
||||
|
||||
k = dnssec_nsec3_hash(rr, "eurid.eu", &h);
|
||||
assert_se(k >= 0);
|
||||
|
|
|
@ -154,20 +154,24 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha
|
|||
return 0;
|
||||
}
|
||||
|
||||
assert(**label_terminal == '.' || **label_terminal == 0);
|
||||
terminal = *label_terminal;
|
||||
assert(*terminal == '.' || *terminal == 0);
|
||||
|
||||
/* skip current terminal character */
|
||||
terminal = *label_terminal - 1;
|
||||
/* Skip current terminal character (and accept domain names ending it ".") */
|
||||
if (*terminal == 0)
|
||||
terminal--;
|
||||
if (terminal >= name && *terminal == '.')
|
||||
terminal--;
|
||||
|
||||
/* point name to the last label, and terminal to the preceding terminal symbol (or make it a NULL pointer) */
|
||||
/* Point name to the last label, and terminal to the preceding terminal symbol (or make it a NULL pointer) */
|
||||
for (;;) {
|
||||
if (terminal < name) {
|
||||
/* reached the first label, so indicate that there are no more */
|
||||
/* Reached the first label, so indicate that there are no more */
|
||||
terminal = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
/* find the start of the last label */
|
||||
/* Find the start of the last label */
|
||||
if (*terminal == '.') {
|
||||
const char *y;
|
||||
unsigned slashes = 0;
|
||||
|
@ -176,7 +180,7 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha
|
|||
slashes ++;
|
||||
|
||||
if (slashes % 2 == 0) {
|
||||
/* the '.' was not escaped */
|
||||
/* The '.' was not escaped */
|
||||
name = terminal + 1;
|
||||
break;
|
||||
} else {
|
||||
|
@ -533,7 +537,7 @@ int dns_name_compare_func(const void *a, const void *b) {
|
|||
if (k > 0)
|
||||
r = k;
|
||||
if (w > 0)
|
||||
r = w;
|
||||
q = w;
|
||||
|
||||
la[r] = lb[q] = 0;
|
||||
r = strcasecmp(la, lb);
|
||||
|
@ -1159,3 +1163,77 @@ finish:
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dns_name_suffix(const char *name, unsigned n_labels, const char **ret) {
|
||||
const char* labels[DNS_N_LABELS_MAX+1];
|
||||
unsigned n = 0;
|
||||
const char *p;
|
||||
int r;
|
||||
|
||||
assert(name);
|
||||
assert(ret);
|
||||
|
||||
p = name;
|
||||
for (;;) {
|
||||
if (n > DNS_N_LABELS_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
labels[n] = p;
|
||||
|
||||
r = dns_name_parent(&p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
n++;
|
||||
}
|
||||
|
||||
if (n < n_labels)
|
||||
return -EINVAL;
|
||||
|
||||
*ret = labels[n - n_labels];
|
||||
return (int) (n - n_labels);
|
||||
}
|
||||
|
||||
int dns_name_count_labels(const char *name) {
|
||||
unsigned n = 0;
|
||||
const char *p;
|
||||
int r;
|
||||
|
||||
assert(name);
|
||||
|
||||
p = name;
|
||||
for (;;) {
|
||||
r = dns_name_parent(&p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
if (n >= DNS_N_LABELS_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
n++;
|
||||
}
|
||||
|
||||
return (int) n;
|
||||
}
|
||||
|
||||
int dns_name_equal_skip(const char *a, unsigned n_labels, const char *b) {
|
||||
int r;
|
||||
|
||||
assert(a);
|
||||
assert(b);
|
||||
|
||||
while (n_labels > 0) {
|
||||
|
||||
r = dns_name_parent(&a);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
n_labels --;
|
||||
}
|
||||
|
||||
return dns_name_equal(a, b);
|
||||
}
|
||||
|
|
|
@ -42,6 +42,9 @@
|
|||
/* Maximum length of a full hostname, on the wire, including the final NUL byte */
|
||||
#define DNS_WIRE_FOMAT_HOSTNAME_MAX 255
|
||||
|
||||
/* Maximum number of labels per valid hostname */
|
||||
#define DNS_N_LABELS_MAX 127
|
||||
|
||||
int dns_label_unescape(const char **name, char *dest, size_t sz);
|
||||
int dns_label_unescape_suffix(const char *name, const char **label_end, char *dest, size_t sz);
|
||||
int dns_label_escape(const char *p, size_t l, char *dest, size_t sz);
|
||||
|
@ -96,3 +99,8 @@ bool dns_service_name_is_valid(const char *name);
|
|||
|
||||
int dns_service_join(const char *name, const char *type, const char *domain, char **ret);
|
||||
int dns_service_split(const char *joined, char **name, char **type, char **domain);
|
||||
|
||||
int dns_name_suffix(const char *name, unsigned n_labels, const char **ret);
|
||||
int dns_name_count_labels(const char *name);
|
||||
|
||||
int dns_name_equal_skip(const char *a, unsigned n_labels, const char *b);
|
||||
|
|
|
@ -86,6 +86,8 @@ _SD_BEGIN_DECLARATIONS;
|
|||
|
||||
#define SD_MESSAGE_BOOTCHART SD_ID128_MAKE(9f,26,aa,56,2c,f4,40,c2,b1,6c,77,3d,04,79,b5,18)
|
||||
|
||||
#define SD_MESSAGE_DNSSEC_FAILURE SD_ID128_MAKE(16,75,d7,f1,72,17,40,98,b1,10,8b,f8,c7,dc,8f,5d)
|
||||
|
||||
_SD_END_DECLARATIONS;
|
||||
|
||||
#endif
|
||||
|
|
|
@ -140,9 +140,9 @@ static void test_dns_label_unescape_suffix(void) {
|
|||
test_dns_label_unescape_suffix_one("hallo\\", "hallo", "hallo", 20, -EINVAL, -EINVAL);
|
||||
test_dns_label_unescape_suffix_one("hallo\\032 ", "hallo ", "", 20, 7, 0);
|
||||
test_dns_label_unescape_suffix_one(".", "", "", 20, 0, 0);
|
||||
test_dns_label_unescape_suffix_one("..", "", "", 20, 0, 0);
|
||||
test_dns_label_unescape_suffix_one("..", "", "", 20, 0, -EINVAL);
|
||||
test_dns_label_unescape_suffix_one(".foobar", "foobar", "", 20, 6, -EINVAL);
|
||||
test_dns_label_unescape_suffix_one("foobar.", "", "foobar", 20, 0, 6);
|
||||
test_dns_label_unescape_suffix_one("foobar.", "foobar", "", 20, 6, 0);
|
||||
test_dns_label_unescape_suffix_one("foo\\\\bar", "foo\\bar", "", 20, 7, 0);
|
||||
test_dns_label_unescape_suffix_one("foo.bar", "bar", "foo", 20, 3, 3);
|
||||
test_dns_label_unescape_suffix_one("foo..bar", "bar", "", 20, 3, -EINVAL);
|
||||
|
@ -475,6 +475,90 @@ static void test_dns_name_change_suffix(void) {
|
|||
test_dns_name_change_suffix_one("a", "b", "c", 0, NULL);
|
||||
}
|
||||
|
||||
static void test_dns_name_suffix_one(const char *name, unsigned n_labels, const char *result, int ret) {
|
||||
const char *p = NULL;
|
||||
|
||||
assert_se(ret == dns_name_suffix(name, n_labels, &p));
|
||||
assert_se(streq_ptr(p, result));
|
||||
}
|
||||
|
||||
static void test_dns_name_suffix(void) {
|
||||
test_dns_name_suffix_one("foo.bar", 2, "foo.bar", 0);
|
||||
test_dns_name_suffix_one("foo.bar", 1, "bar", 1);
|
||||
test_dns_name_suffix_one("foo.bar", 0, "", 2);
|
||||
test_dns_name_suffix_one("foo.bar", 3, NULL, -EINVAL);
|
||||
test_dns_name_suffix_one("foo.bar", 4, NULL, -EINVAL);
|
||||
|
||||
test_dns_name_suffix_one("bar", 1, "bar", 0);
|
||||
test_dns_name_suffix_one("bar", 0, "", 1);
|
||||
test_dns_name_suffix_one("bar", 2, NULL, -EINVAL);
|
||||
test_dns_name_suffix_one("bar", 3, NULL, -EINVAL);
|
||||
|
||||
test_dns_name_suffix_one("", 0, "", 0);
|
||||
test_dns_name_suffix_one("", 1, NULL, -EINVAL);
|
||||
test_dns_name_suffix_one("", 2, NULL, -EINVAL);
|
||||
}
|
||||
|
||||
static void test_dns_name_count_labels_one(const char *name, int n) {
|
||||
assert_se(dns_name_count_labels(name) == n);
|
||||
}
|
||||
|
||||
static void test_dns_name_count_labels(void) {
|
||||
test_dns_name_count_labels_one("foo.bar.quux.", 3);
|
||||
test_dns_name_count_labels_one("foo.bar.quux", 3);
|
||||
test_dns_name_count_labels_one("foo.bar.", 2);
|
||||
test_dns_name_count_labels_one("foo.bar", 2);
|
||||
test_dns_name_count_labels_one("foo.", 1);
|
||||
test_dns_name_count_labels_one("foo", 1);
|
||||
test_dns_name_count_labels_one("", 0);
|
||||
test_dns_name_count_labels_one(".", 0);
|
||||
test_dns_name_count_labels_one("..", -EINVAL);
|
||||
}
|
||||
|
||||
static void test_dns_name_equal_skip_one(const char *a, unsigned n_labels, const char *b, int ret) {
|
||||
assert_se(dns_name_equal_skip(a, n_labels, b) == ret);
|
||||
}
|
||||
|
||||
static void test_dns_name_equal_skip(void) {
|
||||
test_dns_name_equal_skip_one("foo", 0, "bar", 0);
|
||||
test_dns_name_equal_skip_one("foo", 0, "foo", 1);
|
||||
test_dns_name_equal_skip_one("foo", 1, "foo", 0);
|
||||
test_dns_name_equal_skip_one("foo", 2, "foo", 0);
|
||||
|
||||
test_dns_name_equal_skip_one("foo.bar", 0, "foo.bar", 1);
|
||||
test_dns_name_equal_skip_one("foo.bar", 1, "foo.bar", 0);
|
||||
test_dns_name_equal_skip_one("foo.bar", 2, "foo.bar", 0);
|
||||
test_dns_name_equal_skip_one("foo.bar", 3, "foo.bar", 0);
|
||||
|
||||
test_dns_name_equal_skip_one("foo.bar", 0, "bar", 0);
|
||||
test_dns_name_equal_skip_one("foo.bar", 1, "bar", 1);
|
||||
test_dns_name_equal_skip_one("foo.bar", 2, "bar", 0);
|
||||
test_dns_name_equal_skip_one("foo.bar", 3, "bar", 0);
|
||||
|
||||
test_dns_name_equal_skip_one("foo.bar", 0, "", 0);
|
||||
test_dns_name_equal_skip_one("foo.bar", 1, "", 0);
|
||||
test_dns_name_equal_skip_one("foo.bar", 2, "", 1);
|
||||
test_dns_name_equal_skip_one("foo.bar", 3, "", 0);
|
||||
|
||||
test_dns_name_equal_skip_one("", 0, "", 1);
|
||||
test_dns_name_equal_skip_one("", 1, "", 0);
|
||||
test_dns_name_equal_skip_one("", 1, "foo", 0);
|
||||
test_dns_name_equal_skip_one("", 2, "foo", 0);
|
||||
}
|
||||
|
||||
static void test_dns_name_compare_func(void) {
|
||||
assert_se(dns_name_compare_func("", "") == 0);
|
||||
assert_se(dns_name_compare_func("", ".") == 0);
|
||||
assert_se(dns_name_compare_func(".", "") == 0);
|
||||
assert_se(dns_name_compare_func("foo", "foo.") == 0);
|
||||
assert_se(dns_name_compare_func("foo.", "foo") == 0);
|
||||
assert_se(dns_name_compare_func("foo", "foo") == 0);
|
||||
assert_se(dns_name_compare_func("foo.", "foo.") == 0);
|
||||
assert_se(dns_name_compare_func("heise.de", "HEISE.DE.") == 0);
|
||||
|
||||
assert_se(dns_name_compare_func("de.", "heise.de") != 0);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
test_dns_label_unescape();
|
||||
|
@ -495,6 +579,10 @@ int main(int argc, char *argv[]) {
|
|||
test_dns_service_join();
|
||||
test_dns_service_split();
|
||||
test_dns_name_change_suffix();
|
||||
test_dns_name_suffix();
|
||||
test_dns_name_count_labels();
|
||||
test_dns_name_equal_skip();
|
||||
test_dns_name_compare_func();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue