2020-11-09 05:23:58 +01:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
2014-07-16 00:26:02 +02:00
|
|
|
|
2014-07-31 10:19:43 +02:00
|
|
|
#include <math.h>
|
|
|
|
|
2015-10-27 03:01:06 +01:00
|
|
|
#include "alloc-util.h"
|
2015-06-02 20:49:43 +02:00
|
|
|
#include "dns-domain.h"
|
2014-08-02 01:37:16 +02:00
|
|
|
#include "dns-type.h"
|
2016-01-31 22:21:00 +01:00
|
|
|
#include "escape.h"
|
2015-10-26 16:41:43 +01:00
|
|
|
#include "hexdecoct.h"
|
2019-06-15 23:12:24 +02:00
|
|
|
#include "memory-util.h"
|
2015-02-04 23:06:33 +01:00
|
|
|
#include "resolved-dns-dnssec.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "resolved-dns-packet.h"
|
2015-10-26 16:41:43 +01:00
|
|
|
#include "resolved-dns-rr.h"
|
2015-12-02 22:56:04 +01:00
|
|
|
#include "string-table.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "string-util.h"
|
|
|
|
#include "strv.h"
|
2014-08-05 00:59:31 +02:00
|
|
|
#include "terminal-util.h"
|
2014-07-16 00:26:02 +02:00
|
|
|
|
2014-07-22 21:48:41 +02:00
|
|
|
DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *name) {
|
|
|
|
DnsResourceKey *k;
|
|
|
|
size_t l;
|
2014-07-16 00:26:02 +02:00
|
|
|
|
2014-07-22 21:48:41 +02:00
|
|
|
assert(name);
|
|
|
|
|
|
|
|
l = strlen(name);
|
|
|
|
k = malloc0(sizeof(DnsResourceKey) + l + 1);
|
|
|
|
if (!k)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
k->n_ref = 1;
|
|
|
|
k->class = class;
|
|
|
|
k->type = type;
|
|
|
|
|
|
|
|
strcpy((char*) k + sizeof(DnsResourceKey), name);
|
|
|
|
|
|
|
|
return k;
|
|
|
|
}
|
|
|
|
|
2015-09-04 01:56:23 +02:00
|
|
|
DnsResourceKey* dns_resource_key_new_redirect(const DnsResourceKey *key, const DnsResourceRecord *cname) {
|
2015-11-24 00:18:49 +01:00
|
|
|
int r;
|
|
|
|
|
2015-09-04 01:56:23 +02:00
|
|
|
assert(key);
|
|
|
|
assert(cname);
|
|
|
|
|
2015-11-24 00:18:49 +01:00
|
|
|
assert(IN_SET(cname->key->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME));
|
|
|
|
|
|
|
|
if (cname->key->type == DNS_TYPE_CNAME)
|
|
|
|
return dns_resource_key_new(key->class, key->type, cname->cname.name);
|
|
|
|
else {
|
|
|
|
DnsResourceKey *k;
|
|
|
|
char *destination = NULL;
|
|
|
|
|
2016-02-13 20:54:15 +01:00
|
|
|
r = dns_name_change_suffix(dns_resource_key_name(key), dns_resource_key_name(cname->key), cname->dname.name, &destination);
|
2015-11-24 00:18:49 +01:00
|
|
|
if (r < 0)
|
|
|
|
return NULL;
|
|
|
|
if (r == 0)
|
|
|
|
return dns_resource_key_ref((DnsResourceKey*) key);
|
|
|
|
|
|
|
|
k = dns_resource_key_new_consume(key->class, key->type, destination);
|
2016-10-17 00:28:30 +02:00
|
|
|
if (!k)
|
|
|
|
return mfree(destination);
|
2015-11-24 00:18:49 +01:00
|
|
|
|
|
|
|
return k;
|
|
|
|
}
|
2015-09-04 01:56:23 +02:00
|
|
|
}
|
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
int dns_resource_key_new_append_suffix(DnsResourceKey **ret, DnsResourceKey *key, char *name) {
|
|
|
|
DnsResourceKey *new_key;
|
|
|
|
char *joined;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(ret);
|
|
|
|
assert(key);
|
|
|
|
assert(name);
|
|
|
|
|
2015-11-25 21:07:17 +01:00
|
|
|
if (dns_name_is_root(name)) {
|
2015-11-25 20:47:27 +01:00
|
|
|
*ret = dns_resource_key_ref(key);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
resolve: reject host names with leading or trailing dashes in /etc/hosts
https://tools.ietf.org/html/rfc1035#section-2.3.1 says (approximately)
that only letters, numbers, and non-leading non-trailing dashes are allowed
(for entries with A/AAAA records). We set no restrictions.
hosts(5) says:
> Host names may contain only alphanumeric characters, minus signs ("-"), and
> periods ("."). They must begin with an alphabetic character and end with an
> alphanumeric character.
nss-files follows those rules, and will ignore names in /etc/hosts that do not
follow this rule.
Let's follow the documented rules for /etc/hosts. In particular, this makes us
consitent with nss-files, reducing surprises for the user.
I'm pretty sure we should apply stricter filtering to names received over DNS
and LLMNR and MDNS, but it's a bigger project, because the rules differ
depepending on which level the label appears (rules for top-level names are
stricter), and this patch takes the minimalistic approach and only changes
behaviour for /etc/hosts.
Escape syntax is also disallowed in /etc/hosts, even if the resulting character
would be allowed. Other tools that parse /etc/hosts do not support this, and
there is no need to use it because no allowed characters benefit from escaping.
2018-11-21 22:58:13 +01:00
|
|
|
r = dns_name_concat(dns_resource_key_name(key), name, 0, &joined);
|
2015-11-25 20:47:27 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
new_key = dns_resource_key_new_consume(key->class, key->type, joined);
|
|
|
|
if (!new_key) {
|
|
|
|
free(joined);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
*ret = new_key;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-07-22 21:48:41 +02:00
|
|
|
DnsResourceKey* dns_resource_key_new_consume(uint16_t class, uint16_t type, char *name) {
|
|
|
|
DnsResourceKey *k;
|
|
|
|
|
|
|
|
assert(name);
|
|
|
|
|
2020-10-27 14:28:25 +01:00
|
|
|
k = new(DnsResourceKey, 1);
|
2014-07-22 21:48:41 +02:00
|
|
|
if (!k)
|
|
|
|
return NULL;
|
|
|
|
|
2020-10-27 14:28:25 +01:00
|
|
|
*k = (DnsResourceKey) {
|
|
|
|
.n_ref = 1,
|
|
|
|
.class = class,
|
|
|
|
.type = type,
|
|
|
|
._name = name,
|
|
|
|
};
|
2014-07-22 21:48:41 +02:00
|
|
|
|
|
|
|
return k;
|
|
|
|
}
|
|
|
|
|
|
|
|
DnsResourceKey* dns_resource_key_ref(DnsResourceKey *k) {
|
|
|
|
|
|
|
|
if (!k)
|
|
|
|
return NULL;
|
|
|
|
|
2015-12-03 17:27:13 +01:00
|
|
|
/* Static/const keys created with DNS_RESOURCE_KEY_CONST will
|
|
|
|
* set this to -1, they should not be reffed/unreffed */
|
|
|
|
assert(k->n_ref != (unsigned) -1);
|
|
|
|
|
2014-07-22 21:48:41 +02:00
|
|
|
assert(k->n_ref > 0);
|
|
|
|
k->n_ref++;
|
|
|
|
|
|
|
|
return k;
|
|
|
|
}
|
|
|
|
|
|
|
|
DnsResourceKey* dns_resource_key_unref(DnsResourceKey *k) {
|
|
|
|
if (!k)
|
|
|
|
return NULL;
|
|
|
|
|
2015-12-03 17:27:13 +01:00
|
|
|
assert(k->n_ref != (unsigned) -1);
|
2014-07-22 21:48:41 +02:00
|
|
|
assert(k->n_ref > 0);
|
|
|
|
|
|
|
|
if (k->n_ref == 1) {
|
|
|
|
free(k->_name);
|
|
|
|
free(k);
|
|
|
|
} else
|
|
|
|
k->n_ref--;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-02-13 20:54:15 +01:00
|
|
|
const char* dns_resource_key_name(const DnsResourceKey *key) {
|
|
|
|
const char *name;
|
|
|
|
|
|
|
|
if (!key)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (key->_name)
|
|
|
|
name = key->_name;
|
|
|
|
else
|
|
|
|
name = (char*) key + sizeof(DnsResourceKey);
|
|
|
|
|
|
|
|
if (dns_name_is_root(name))
|
|
|
|
return ".";
|
|
|
|
else
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
2015-12-03 18:26:12 +01:00
|
|
|
bool dns_resource_key_is_address(const DnsResourceKey *key) {
|
|
|
|
assert(key);
|
|
|
|
|
|
|
|
/* Check if this is an A or AAAA resource key */
|
|
|
|
|
|
|
|
return key->class == DNS_CLASS_IN && IN_SET(key->type, DNS_TYPE_A, DNS_TYPE_AAAA);
|
|
|
|
}
|
|
|
|
|
2017-10-04 09:07:44 +02:00
|
|
|
bool dns_resource_key_is_dnssd_ptr(const DnsResourceKey *key) {
|
|
|
|
assert(key);
|
|
|
|
|
|
|
|
/* Check if this is a PTR resource key used in
|
|
|
|
Service Instance Enumeration as described in RFC6763 p4.1. */
|
|
|
|
|
|
|
|
if (key->type != DNS_TYPE_PTR)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return dns_name_endswith(dns_resource_key_name(key), "_tcp.local") ||
|
|
|
|
dns_name_endswith(dns_resource_key_name(key), "_udp.local");
|
|
|
|
}
|
|
|
|
|
2014-07-22 21:48:41 +02:00
|
|
|
int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b) {
|
|
|
|
int r;
|
|
|
|
|
2015-12-09 17:28:50 +01:00
|
|
|
if (a == b)
|
|
|
|
return 1;
|
|
|
|
|
2016-02-13 20:54:15 +01:00
|
|
|
r = dns_name_equal(dns_resource_key_name(a), dns_resource_key_name(b));
|
2014-07-22 21:48:41 +02:00
|
|
|
if (r <= 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (a->class != b->class)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (a->type != b->type)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2015-12-18 14:37:06 +01:00
|
|
|
int dns_resource_key_match_rr(const DnsResourceKey *key, DnsResourceRecord *rr, const char *search_domain) {
|
2015-11-25 20:47:27 +01:00
|
|
|
int r;
|
|
|
|
|
2014-07-22 21:48:41 +02:00
|
|
|
assert(key);
|
|
|
|
assert(rr);
|
|
|
|
|
2015-12-09 17:28:50 +01:00
|
|
|
if (key == rr->key)
|
|
|
|
return 1;
|
|
|
|
|
2015-11-25 20:47:27 +01:00
|
|
|
/* Checks if an rr matches the specified key. If a search
|
|
|
|
* domain is specified, it will also be checked if the key
|
|
|
|
* with the search domain suffixed might match the RR. */
|
|
|
|
|
2014-07-22 21:48:41 +02:00
|
|
|
if (rr->key->class != key->class && key->class != DNS_CLASS_ANY)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (rr->key->type != key->type && key->type != DNS_TYPE_ANY)
|
|
|
|
return 0;
|
|
|
|
|
2016-02-13 20:54:15 +01:00
|
|
|
r = dns_name_equal(dns_resource_key_name(rr->key), dns_resource_key_name(key));
|
2015-11-25 20:47:27 +01:00
|
|
|
if (r != 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (search_domain) {
|
|
|
|
_cleanup_free_ char *joined = NULL;
|
|
|
|
|
resolve: reject host names with leading or trailing dashes in /etc/hosts
https://tools.ietf.org/html/rfc1035#section-2.3.1 says (approximately)
that only letters, numbers, and non-leading non-trailing dashes are allowed
(for entries with A/AAAA records). We set no restrictions.
hosts(5) says:
> Host names may contain only alphanumeric characters, minus signs ("-"), and
> periods ("."). They must begin with an alphabetic character and end with an
> alphanumeric character.
nss-files follows those rules, and will ignore names in /etc/hosts that do not
follow this rule.
Let's follow the documented rules for /etc/hosts. In particular, this makes us
consitent with nss-files, reducing surprises for the user.
I'm pretty sure we should apply stricter filtering to names received over DNS
and LLMNR and MDNS, but it's a bigger project, because the rules differ
depepending on which level the label appears (rules for top-level names are
stricter), and this patch takes the minimalistic approach and only changes
behaviour for /etc/hosts.
Escape syntax is also disallowed in /etc/hosts, even if the resulting character
would be allowed. Other tools that parse /etc/hosts do not support this, and
there is no need to use it because no allowed characters benefit from escaping.
2018-11-21 22:58:13 +01:00
|
|
|
r = dns_name_concat(dns_resource_key_name(key), search_domain, 0, &joined);
|
2015-11-25 20:47:27 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2016-02-13 20:54:15 +01:00
|
|
|
return dns_name_equal(dns_resource_key_name(rr->key), joined);
|
2015-11-25 20:47:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2014-07-22 21:48:41 +02:00
|
|
|
}
|
|
|
|
|
2015-12-02 18:46:32 +01:00
|
|
|
int dns_resource_key_match_cname_or_dname(const DnsResourceKey *key, const DnsResourceKey *cname, const char *search_domain) {
|
2015-11-25 20:47:27 +01:00
|
|
|
int r;
|
|
|
|
|
2014-07-22 21:48:41 +02:00
|
|
|
assert(key);
|
2015-12-02 18:46:32 +01:00
|
|
|
assert(cname);
|
2014-07-22 21:48:41 +02:00
|
|
|
|
2015-12-02 18:46:32 +01:00
|
|
|
if (cname->class != key->class && key->class != DNS_CLASS_ANY)
|
2014-07-22 21:48:41 +02:00
|
|
|
return 0;
|
|
|
|
|
2015-12-02 18:46:32 +01:00
|
|
|
if (cname->type == DNS_TYPE_CNAME)
|
2016-02-13 20:54:15 +01:00
|
|
|
r = dns_name_equal(dns_resource_key_name(key), dns_resource_key_name(cname));
|
2015-12-02 18:46:32 +01:00
|
|
|
else if (cname->type == DNS_TYPE_DNAME)
|
2016-02-13 20:54:15 +01:00
|
|
|
r = dns_name_endswith(dns_resource_key_name(key), dns_resource_key_name(cname));
|
2015-11-24 00:18:49 +01:00
|
|
|
else
|
2014-07-22 21:48:41 +02:00
|
|
|
return 0;
|
2015-11-25 20:47:27 +01:00
|
|
|
|
|
|
|
if (r != 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (search_domain) {
|
|
|
|
_cleanup_free_ char *joined = NULL;
|
|
|
|
|
resolve: reject host names with leading or trailing dashes in /etc/hosts
https://tools.ietf.org/html/rfc1035#section-2.3.1 says (approximately)
that only letters, numbers, and non-leading non-trailing dashes are allowed
(for entries with A/AAAA records). We set no restrictions.
hosts(5) says:
> Host names may contain only alphanumeric characters, minus signs ("-"), and
> periods ("."). They must begin with an alphabetic character and end with an
> alphanumeric character.
nss-files follows those rules, and will ignore names in /etc/hosts that do not
follow this rule.
Let's follow the documented rules for /etc/hosts. In particular, this makes us
consitent with nss-files, reducing surprises for the user.
I'm pretty sure we should apply stricter filtering to names received over DNS
and LLMNR and MDNS, but it's a bigger project, because the rules differ
depepending on which level the label appears (rules for top-level names are
stricter), and this patch takes the minimalistic approach and only changes
behaviour for /etc/hosts.
Escape syntax is also disallowed in /etc/hosts, even if the resulting character
would be allowed. Other tools that parse /etc/hosts do not support this, and
there is no need to use it because no allowed characters benefit from escaping.
2018-11-21 22:58:13 +01:00
|
|
|
r = dns_name_concat(dns_resource_key_name(key), search_domain, 0, &joined);
|
2015-11-25 20:47:27 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2015-12-02 18:46:32 +01:00
|
|
|
if (cname->type == DNS_TYPE_CNAME)
|
2016-02-13 20:54:15 +01:00
|
|
|
return dns_name_equal(joined, dns_resource_key_name(cname));
|
2015-12-02 18:46:32 +01:00
|
|
|
else if (cname->type == DNS_TYPE_DNAME)
|
2016-02-13 20:54:15 +01:00
|
|
|
return dns_name_endswith(joined, dns_resource_key_name(cname));
|
2015-11-25 20:47:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
resolved: chase DNSKEY/DS RRs when doing look-ups with DNSSEC enabled
This adds initial support for validating RRSIG/DNSKEY/DS chains when
doing lookups. Proof-of-non-existance, or proof-of-unsigned-zones is not
implemented yet.
With this change DnsTransaction objects will generate additional
DnsTransaction objects when looking for DNSKEY or DS RRs to validate an
RRSIG on a response. DnsTransaction objects are thus created for three
reasons now:
1) Because a user asked for something to be resolved, i.e. requested by
a DnsQuery/DnsQueryCandidate object.
2) As result of LLMNR RR probing, requested by a DnsZoneItem.
3) Because another DnsTransaction requires the requested RRs for
validation of its own response.
DnsTransactions are shared between all these users, and are GC
automatically as soon as all of these users don't need a specific
transaction anymore.
To unify the handling of these three reasons for existance for a
DnsTransaction, a new common naming is introduced: each DnsTransaction
now tracks its "owners" via a Set* object named "notify_xyz", containing
all owners to notify on completion.
A new DnsTransaction state is introduced called "VALIDATING" that is
entered after a response has been receieved which needs to be validated,
as long as we are still waiting for the DNSKEY/DS RRs from other
DnsTransactions.
This patch will request the DNSKEY/DS RRs bottom-up, and then validate
them top-down.
Caching of RRs is now only done after verification, so that the cache is
not poisoned with known invalid data.
The "DnsAnswer" object gained a substantial number of new calls, since
we need to add/remove RRs to it dynamically now.
2015-12-09 18:13:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int dns_resource_key_match_soa(const DnsResourceKey *key, const DnsResourceKey *soa) {
|
|
|
|
assert(soa);
|
|
|
|
assert(key);
|
|
|
|
|
|
|
|
/* Checks whether 'soa' is a SOA record for the specified key. */
|
|
|
|
|
2015-12-21 16:28:35 +01:00
|
|
|
if (soa->class != key->class)
|
resolved: chase DNSKEY/DS RRs when doing look-ups with DNSSEC enabled
This adds initial support for validating RRSIG/DNSKEY/DS chains when
doing lookups. Proof-of-non-existance, or proof-of-unsigned-zones is not
implemented yet.
With this change DnsTransaction objects will generate additional
DnsTransaction objects when looking for DNSKEY or DS RRs to validate an
RRSIG on a response. DnsTransaction objects are thus created for three
reasons now:
1) Because a user asked for something to be resolved, i.e. requested by
a DnsQuery/DnsQueryCandidate object.
2) As result of LLMNR RR probing, requested by a DnsZoneItem.
3) Because another DnsTransaction requires the requested RRs for
validation of its own response.
DnsTransactions are shared between all these users, and are GC
automatically as soon as all of these users don't need a specific
transaction anymore.
To unify the handling of these three reasons for existance for a
DnsTransaction, a new common naming is introduced: each DnsTransaction
now tracks its "owners" via a Set* object named "notify_xyz", containing
all owners to notify on completion.
A new DnsTransaction state is introduced called "VALIDATING" that is
entered after a response has been receieved which needs to be validated,
as long as we are still waiting for the DNSKEY/DS RRs from other
DnsTransactions.
This patch will request the DNSKEY/DS RRs bottom-up, and then validate
them top-down.
Caching of RRs is now only done after verification, so that the cache is
not poisoned with known invalid data.
The "DnsAnswer" object gained a substantial number of new calls, since
we need to add/remove RRs to it dynamically now.
2015-12-09 18:13:16 +01:00
|
|
|
return 0;
|
2015-11-25 20:47:27 +01:00
|
|
|
|
resolved: chase DNSKEY/DS RRs when doing look-ups with DNSSEC enabled
This adds initial support for validating RRSIG/DNSKEY/DS chains when
doing lookups. Proof-of-non-existance, or proof-of-unsigned-zones is not
implemented yet.
With this change DnsTransaction objects will generate additional
DnsTransaction objects when looking for DNSKEY or DS RRs to validate an
RRSIG on a response. DnsTransaction objects are thus created for three
reasons now:
1) Because a user asked for something to be resolved, i.e. requested by
a DnsQuery/DnsQueryCandidate object.
2) As result of LLMNR RR probing, requested by a DnsZoneItem.
3) Because another DnsTransaction requires the requested RRs for
validation of its own response.
DnsTransactions are shared between all these users, and are GC
automatically as soon as all of these users don't need a specific
transaction anymore.
To unify the handling of these three reasons for existance for a
DnsTransaction, a new common naming is introduced: each DnsTransaction
now tracks its "owners" via a Set* object named "notify_xyz", containing
all owners to notify on completion.
A new DnsTransaction state is introduced called "VALIDATING" that is
entered after a response has been receieved which needs to be validated,
as long as we are still waiting for the DNSKEY/DS RRs from other
DnsTransactions.
This patch will request the DNSKEY/DS RRs bottom-up, and then validate
them top-down.
Caching of RRs is now only done after verification, so that the cache is
not poisoned with known invalid data.
The "DnsAnswer" object gained a substantial number of new calls, since
we need to add/remove RRs to it dynamically now.
2015-12-09 18:13:16 +01:00
|
|
|
if (soa->type != DNS_TYPE_SOA)
|
|
|
|
return 0;
|
|
|
|
|
2016-02-13 20:54:15 +01:00
|
|
|
return dns_name_endswith(dns_resource_key_name(key), dns_resource_key_name(soa));
|
2014-07-16 00:26:02 +02:00
|
|
|
}
|
|
|
|
|
2018-11-27 14:25:20 +01:00
|
|
|
static void dns_resource_key_hash_func(const DnsResourceKey *k, struct siphash *state) {
|
2015-10-04 00:22:41 +02:00
|
|
|
assert(k);
|
2014-07-17 19:38:37 +02:00
|
|
|
|
2016-02-13 20:54:15 +01:00
|
|
|
dns_name_hash_func(dns_resource_key_name(k), state);
|
2015-10-04 00:22:41 +02:00
|
|
|
siphash24_compress(&k->class, sizeof(k->class), state);
|
|
|
|
siphash24_compress(&k->type, sizeof(k->type), state);
|
2014-07-17 19:38:37 +02:00
|
|
|
}
|
|
|
|
|
2018-11-27 14:25:20 +01:00
|
|
|
static int dns_resource_key_compare_func(const DnsResourceKey *x, const DnsResourceKey *y) {
|
2020-12-29 14:29:21 +01:00
|
|
|
int r;
|
2014-07-17 19:38:37 +02:00
|
|
|
|
2020-12-29 14:29:21 +01:00
|
|
|
r = dns_name_compare_func(dns_resource_key_name(x), dns_resource_key_name(y));
|
|
|
|
if (r != 0)
|
|
|
|
return r;
|
2014-07-17 19:38:37 +02:00
|
|
|
|
2020-12-29 14:29:21 +01:00
|
|
|
r = CMP(x->type, y->type);
|
|
|
|
if (r != 0)
|
|
|
|
return r;
|
2014-07-17 19:38:37 +02:00
|
|
|
|
2020-12-29 14:29:21 +01:00
|
|
|
return CMP(x->class, y->class);
|
2014-07-17 19:38:37 +02:00
|
|
|
}
|
|
|
|
|
2018-11-27 14:25:20 +01:00
|
|
|
DEFINE_HASH_OPS(dns_resource_key_hash_ops, DnsResourceKey, dns_resource_key_hash_func, dns_resource_key_compare_func);
|
2014-08-13 01:00:18 +02:00
|
|
|
|
2016-02-15 00:51:55 +01:00
|
|
|
char* dns_resource_key_to_string(const DnsResourceKey *key, char *buf, size_t buf_size) {
|
2016-02-13 20:54:15 +01:00
|
|
|
const char *c, *t;
|
2016-02-15 00:51:55 +01:00
|
|
|
char *ans = buf;
|
2014-07-30 19:23:27 +02:00
|
|
|
|
2015-12-29 19:27:09 +01:00
|
|
|
/* If we cannot convert the CLASS/TYPE into a known string,
|
|
|
|
use the format recommended by RFC 3597, Section 5. */
|
|
|
|
|
2014-07-30 19:23:27 +02:00
|
|
|
c = dns_class_to_string(key->class);
|
|
|
|
t = dns_type_to_string(key->type);
|
|
|
|
|
2016-02-15 00:51:55 +01:00
|
|
|
snprintf(buf, buf_size, "%s %s%s%.0u %s%s%.0u",
|
|
|
|
dns_resource_key_name(key),
|
2017-11-24 10:31:08 +01:00
|
|
|
strempty(c), c ? "" : "CLASS", c ? 0 : key->class,
|
2018-06-19 14:01:57 +02:00
|
|
|
strempty(t), t ? "" : "TYPE", t ? 0 : key->type);
|
2014-07-30 19:23:27 +02:00
|
|
|
|
2016-02-15 00:51:55 +01:00
|
|
|
return ans;
|
2014-07-30 19:23:27 +02:00
|
|
|
}
|
|
|
|
|
2016-01-15 21:38:27 +01:00
|
|
|
bool dns_resource_key_reduce(DnsResourceKey **a, DnsResourceKey **b) {
|
|
|
|
assert(a);
|
|
|
|
assert(b);
|
|
|
|
|
|
|
|
/* Try to replace one RR key by another if they are identical, thus saving a bit of memory. Note that we do
|
|
|
|
* this only for RR keys, not for RRs themselves, as they carry a lot of additional metadata (where they come
|
|
|
|
* from, validity data, and suchlike), and cannot be replaced so easily by other RRs that have the same
|
|
|
|
* superficial data. */
|
|
|
|
|
|
|
|
if (!*a)
|
|
|
|
return false;
|
|
|
|
if (!*b)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* We refuse merging const keys */
|
|
|
|
if ((*a)->n_ref == (unsigned) -1)
|
|
|
|
return false;
|
|
|
|
if ((*b)->n_ref == (unsigned) -1)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* Already the same? */
|
|
|
|
if (*a == *b)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
/* Are they really identical? */
|
|
|
|
if (dns_resource_key_equal(*a, *b) <= 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* Keep the one which already has more references. */
|
|
|
|
if ((*a)->n_ref > (*b)->n_ref) {
|
|
|
|
dns_resource_key_unref(*b);
|
|
|
|
*b = dns_resource_key_ref(*a);
|
|
|
|
} else {
|
|
|
|
dns_resource_key_unref(*a);
|
|
|
|
*a = dns_resource_key_ref(*b);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-07-22 21:48:41 +02:00
|
|
|
DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key) {
|
2014-07-16 00:26:02 +02:00
|
|
|
DnsResourceRecord *rr;
|
|
|
|
|
2020-10-27 14:28:25 +01:00
|
|
|
rr = new(DnsResourceRecord, 1);
|
2014-07-16 00:26:02 +02:00
|
|
|
if (!rr)
|
|
|
|
return NULL;
|
|
|
|
|
2020-10-27 14:28:25 +01:00
|
|
|
*rr = (DnsResourceRecord) {
|
|
|
|
.n_ref = 1,
|
|
|
|
.key = dns_resource_key_ref(key),
|
|
|
|
.expiry = USEC_INFINITY,
|
|
|
|
.n_skip_labels_signer = (unsigned) -1,
|
|
|
|
.n_skip_labels_source = (unsigned) -1,
|
|
|
|
};
|
2014-07-22 21:48:41 +02:00
|
|
|
|
2014-07-16 00:26:02 +02:00
|
|
|
return rr;
|
|
|
|
}
|
|
|
|
|
2014-07-30 16:30:25 +02:00
|
|
|
DnsResourceRecord* dns_resource_record_new_full(uint16_t class, uint16_t type, const char *name) {
|
|
|
|
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
|
|
|
|
|
|
|
|
key = dns_resource_key_new(class, type, name);
|
|
|
|
if (!key)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return dns_resource_record_new(key);
|
|
|
|
}
|
|
|
|
|
2018-08-27 07:01:46 +02:00
|
|
|
static DnsResourceRecord* dns_resource_record_free(DnsResourceRecord *rr) {
|
|
|
|
assert(rr);
|
2014-07-16 00:26:02 +02:00
|
|
|
|
2014-07-22 21:48:41 +02:00
|
|
|
if (rr->key) {
|
2014-08-01 03:47:51 +02:00
|
|
|
switch(rr->key->type) {
|
2014-07-31 18:23:00 +02:00
|
|
|
|
|
|
|
case DNS_TYPE_SRV:
|
|
|
|
free(rr->srv.name);
|
|
|
|
break;
|
|
|
|
|
2014-08-01 03:47:51 +02:00
|
|
|
case DNS_TYPE_PTR:
|
|
|
|
case DNS_TYPE_NS:
|
|
|
|
case DNS_TYPE_CNAME:
|
2014-07-31 18:02:24 +02:00
|
|
|
case DNS_TYPE_DNAME:
|
2014-07-22 21:48:41 +02:00
|
|
|
free(rr->ptr.name);
|
2014-08-01 03:47:51 +02:00
|
|
|
break;
|
2014-07-31 18:23:00 +02:00
|
|
|
|
2014-08-01 03:47:51 +02:00
|
|
|
case DNS_TYPE_HINFO:
|
2014-07-22 21:48:41 +02:00
|
|
|
free(rr->hinfo.cpu);
|
|
|
|
free(rr->hinfo.os);
|
2014-08-01 03:47:51 +02:00
|
|
|
break;
|
2014-07-31 18:23:00 +02:00
|
|
|
|
2014-08-01 03:47:51 +02:00
|
|
|
case DNS_TYPE_TXT:
|
2014-07-31 18:23:00 +02:00
|
|
|
case DNS_TYPE_SPF:
|
2015-11-20 19:01:43 +01:00
|
|
|
dns_txt_item_free_all(rr->txt.items);
|
2014-08-01 03:47:51 +02:00
|
|
|
break;
|
2014-07-31 18:23:00 +02:00
|
|
|
|
2014-08-01 03:47:51 +02:00
|
|
|
case DNS_TYPE_SOA:
|
2014-07-23 00:57:25 +02:00
|
|
|
free(rr->soa.mname);
|
|
|
|
free(rr->soa.rname);
|
2014-08-01 03:47:51 +02:00
|
|
|
break;
|
2014-07-31 18:23:00 +02:00
|
|
|
|
2014-08-01 03:47:51 +02:00
|
|
|
case DNS_TYPE_MX:
|
2014-08-01 03:06:00 +02:00
|
|
|
free(rr->mx.exchange);
|
2014-08-01 03:47:51 +02:00
|
|
|
break;
|
2014-07-31 18:23:00 +02:00
|
|
|
|
2015-07-13 01:51:03 +02:00
|
|
|
case DNS_TYPE_DS:
|
|
|
|
free(rr->ds.digest);
|
|
|
|
break;
|
|
|
|
|
2014-07-31 18:41:41 +02:00
|
|
|
case DNS_TYPE_SSHFP:
|
2015-07-23 13:09:35 +02:00
|
|
|
free(rr->sshfp.fingerprint);
|
2014-07-31 18:41:41 +02:00
|
|
|
break;
|
|
|
|
|
2014-08-03 22:05:41 +02:00
|
|
|
case DNS_TYPE_DNSKEY:
|
|
|
|
free(rr->dnskey.key);
|
|
|
|
break;
|
|
|
|
|
2014-08-04 00:17:22 +02:00
|
|
|
case DNS_TYPE_RRSIG:
|
|
|
|
free(rr->rrsig.signer);
|
|
|
|
free(rr->rrsig.signature);
|
|
|
|
break;
|
|
|
|
|
2015-07-13 01:51:03 +02:00
|
|
|
case DNS_TYPE_NSEC:
|
|
|
|
free(rr->nsec.next_domain_name);
|
|
|
|
bitmap_free(rr->nsec.types);
|
|
|
|
break;
|
|
|
|
|
2015-07-13 01:51:03 +02:00
|
|
|
case DNS_TYPE_NSEC3:
|
|
|
|
free(rr->nsec3.next_hashed_name);
|
|
|
|
free(rr->nsec3.salt);
|
|
|
|
bitmap_free(rr->nsec3.types);
|
|
|
|
break;
|
|
|
|
|
2014-07-31 10:19:43 +02:00
|
|
|
case DNS_TYPE_LOC:
|
2014-08-01 03:47:51 +02:00
|
|
|
case DNS_TYPE_A:
|
|
|
|
case DNS_TYPE_AAAA:
|
|
|
|
break;
|
2014-07-31 18:23:00 +02:00
|
|
|
|
2015-02-02 01:17:24 +01:00
|
|
|
case DNS_TYPE_TLSA:
|
|
|
|
free(rr->tlsa.data);
|
|
|
|
break;
|
|
|
|
|
2016-01-31 22:21:00 +01:00
|
|
|
case DNS_TYPE_CAA:
|
|
|
|
free(rr->caa.tag);
|
|
|
|
free(rr->caa.value);
|
|
|
|
break;
|
|
|
|
|
2015-02-02 02:54:15 +01:00
|
|
|
case DNS_TYPE_OPENPGPKEY:
|
2014-08-01 03:47:51 +02:00
|
|
|
default:
|
2020-07-02 09:58:23 +02:00
|
|
|
if (!rr->unparsable)
|
2018-01-18 10:19:48 +01:00
|
|
|
free(rr->generic.data);
|
2014-08-01 03:47:51 +02:00
|
|
|
}
|
2014-07-17 19:38:37 +02:00
|
|
|
|
2020-07-02 09:58:23 +02:00
|
|
|
if (rr->unparsable)
|
2018-01-18 10:19:48 +01:00
|
|
|
free(rr->generic.data);
|
|
|
|
|
2015-12-02 20:58:51 +01:00
|
|
|
free(rr->wire_format);
|
2014-07-22 21:48:41 +02:00
|
|
|
dns_resource_key_unref(rr->key);
|
|
|
|
}
|
2014-07-17 19:38:37 +02:00
|
|
|
|
2015-12-21 16:31:29 +01:00
|
|
|
free(rr->to_string);
|
2016-10-17 00:28:30 +02:00
|
|
|
return mfree(rr);
|
2014-07-17 19:38:37 +02:00
|
|
|
}
|
|
|
|
|
2018-08-27 07:01:46 +02:00
|
|
|
DEFINE_TRIVIAL_REF_UNREF_FUNC(DnsResourceRecord, dns_resource_record, dns_resource_record_free);
|
|
|
|
|
2014-07-29 14:24:02 +02:00
|
|
|
int dns_resource_record_new_reverse(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *hostname) {
|
|
|
|
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
|
|
|
|
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
|
|
|
|
_cleanup_free_ char *ptr = NULL;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(ret);
|
|
|
|
assert(address);
|
|
|
|
assert(hostname);
|
|
|
|
|
|
|
|
r = dns_name_reverse(family, address, &ptr);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, ptr);
|
|
|
|
if (!key)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
ptr = NULL;
|
|
|
|
|
|
|
|
rr = dns_resource_record_new(key);
|
|
|
|
if (!rr)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
rr->ptr.name = strdup(hostname);
|
|
|
|
if (!rr->ptr.name)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2018-04-05 07:26:26 +02:00
|
|
|
*ret = TAKE_PTR(rr);
|
2014-07-29 14:24:02 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-08-17 23:54:08 +02:00
|
|
|
int dns_resource_record_new_address(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *name) {
|
|
|
|
DnsResourceRecord *rr;
|
|
|
|
|
|
|
|
assert(ret);
|
|
|
|
assert(address);
|
|
|
|
assert(family);
|
|
|
|
|
|
|
|
if (family == AF_INET) {
|
|
|
|
|
|
|
|
rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, name);
|
|
|
|
if (!rr)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
rr->a.in_addr = address->in;
|
|
|
|
|
|
|
|
} else if (family == AF_INET6) {
|
|
|
|
|
|
|
|
rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_AAAA, name);
|
|
|
|
if (!rr)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
rr->aaaa.in6_addr = address->in6;
|
|
|
|
} else
|
|
|
|
return -EAFNOSUPPORT;
|
|
|
|
|
|
|
|
*ret = rr;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-01-29 00:23:59 +01:00
|
|
|
#define FIELD_EQUAL(a, b, field) \
|
|
|
|
((a).field ## _size == (b).field ## _size && \
|
2019-06-15 23:12:24 +02:00
|
|
|
memcmp_safe((a).field, (b).field, (a).field ## _size) == 0)
|
2016-01-29 00:23:59 +01:00
|
|
|
|
2019-02-27 10:27:35 +01:00
|
|
|
int dns_resource_record_payload_equal(const DnsResourceRecord *a, const DnsResourceRecord *b) {
|
2014-07-17 19:38:37 +02:00
|
|
|
int r;
|
|
|
|
|
2019-02-27 10:27:35 +01:00
|
|
|
/* Check if a and b are the same, but don't look at their keys */
|
2014-07-17 19:38:37 +02:00
|
|
|
|
2020-07-02 09:58:23 +02:00
|
|
|
if (a->unparsable != b->unparsable)
|
2014-07-31 18:41:54 +02:00
|
|
|
return 0;
|
|
|
|
|
2020-07-02 09:58:23 +02:00
|
|
|
switch (a->unparsable ? _DNS_TYPE_INVALID : a->key->type) {
|
2014-07-30 19:23:27 +02:00
|
|
|
|
2014-07-31 18:23:00 +02:00
|
|
|
case DNS_TYPE_SRV:
|
|
|
|
r = dns_name_equal(a->srv.name, b->srv.name);
|
|
|
|
if (r <= 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
return a->srv.priority == b->srv.priority &&
|
|
|
|
a->srv.weight == b->srv.weight &&
|
|
|
|
a->srv.port == b->srv.port;
|
|
|
|
|
2014-07-30 19:23:27 +02:00
|
|
|
case DNS_TYPE_PTR:
|
|
|
|
case DNS_TYPE_NS:
|
|
|
|
case DNS_TYPE_CNAME:
|
2014-07-31 18:02:24 +02:00
|
|
|
case DNS_TYPE_DNAME:
|
2014-07-17 19:38:37 +02:00
|
|
|
return dns_name_equal(a->ptr.name, b->ptr.name);
|
2014-07-30 19:23:27 +02:00
|
|
|
|
|
|
|
case DNS_TYPE_HINFO:
|
|
|
|
return strcaseeq(a->hinfo.cpu, b->hinfo.cpu) &&
|
|
|
|
strcaseeq(a->hinfo.os, b->hinfo.os);
|
|
|
|
|
2014-08-01 03:47:51 +02:00
|
|
|
case DNS_TYPE_SPF: /* exactly the same as TXT */
|
2014-11-27 16:08:46 +01:00
|
|
|
case DNS_TYPE_TXT:
|
2015-11-20 19:01:43 +01:00
|
|
|
return dns_txt_item_equal(a->txt.items, b->txt.items);
|
2014-08-01 03:36:58 +02:00
|
|
|
|
2014-07-30 19:23:27 +02:00
|
|
|
case DNS_TYPE_A:
|
2014-07-17 19:38:37 +02:00
|
|
|
return memcmp(&a->a.in_addr, &b->a.in_addr, sizeof(struct in_addr)) == 0;
|
2014-07-30 19:23:27 +02:00
|
|
|
|
|
|
|
case DNS_TYPE_AAAA:
|
2014-07-17 19:38:37 +02:00
|
|
|
return memcmp(&a->aaaa.in6_addr, &b->aaaa.in6_addr, sizeof(struct in6_addr)) == 0;
|
2014-07-30 19:23:27 +02:00
|
|
|
|
|
|
|
case DNS_TYPE_SOA:
|
2014-07-23 00:57:25 +02:00
|
|
|
r = dns_name_equal(a->soa.mname, b->soa.mname);
|
|
|
|
if (r <= 0)
|
|
|
|
return r;
|
|
|
|
r = dns_name_equal(a->soa.rname, b->soa.rname);
|
|
|
|
if (r <= 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
return a->soa.serial == b->soa.serial &&
|
|
|
|
a->soa.refresh == b->soa.refresh &&
|
|
|
|
a->soa.retry == b->soa.retry &&
|
|
|
|
a->soa.expire == b->soa.expire &&
|
|
|
|
a->soa.minimum == b->soa.minimum;
|
2014-07-31 18:23:00 +02:00
|
|
|
|
2014-08-01 03:06:00 +02:00
|
|
|
case DNS_TYPE_MX:
|
|
|
|
if (a->mx.priority != b->mx.priority)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return dns_name_equal(a->mx.exchange, b->mx.exchange);
|
|
|
|
|
2014-07-31 10:19:43 +02:00
|
|
|
case DNS_TYPE_LOC:
|
|
|
|
assert(a->loc.version == b->loc.version);
|
|
|
|
|
|
|
|
return a->loc.size == b->loc.size &&
|
|
|
|
a->loc.horiz_pre == b->loc.horiz_pre &&
|
|
|
|
a->loc.vert_pre == b->loc.vert_pre &&
|
|
|
|
a->loc.latitude == b->loc.latitude &&
|
|
|
|
a->loc.longitude == b->loc.longitude &&
|
|
|
|
a->loc.altitude == b->loc.altitude;
|
|
|
|
|
2015-07-13 01:51:03 +02:00
|
|
|
case DNS_TYPE_DS:
|
|
|
|
return a->ds.key_tag == b->ds.key_tag &&
|
|
|
|
a->ds.algorithm == b->ds.algorithm &&
|
|
|
|
a->ds.digest_type == b->ds.digest_type &&
|
2016-01-29 00:23:59 +01:00
|
|
|
FIELD_EQUAL(a->ds, b->ds, digest);
|
2015-07-13 01:51:03 +02:00
|
|
|
|
2014-07-31 18:41:41 +02:00
|
|
|
case DNS_TYPE_SSHFP:
|
|
|
|
return a->sshfp.algorithm == b->sshfp.algorithm &&
|
|
|
|
a->sshfp.fptype == b->sshfp.fptype &&
|
2016-01-29 00:23:59 +01:00
|
|
|
FIELD_EQUAL(a->sshfp, b->sshfp, fingerprint);
|
2014-07-31 18:41:41 +02:00
|
|
|
|
2014-08-03 22:05:41 +02:00
|
|
|
case DNS_TYPE_DNSKEY:
|
2015-12-02 20:53:10 +01:00
|
|
|
return a->dnskey.flags == b->dnskey.flags &&
|
|
|
|
a->dnskey.protocol == b->dnskey.protocol &&
|
2014-08-03 22:05:41 +02:00
|
|
|
a->dnskey.algorithm == b->dnskey.algorithm &&
|
2016-01-29 00:23:59 +01:00
|
|
|
FIELD_EQUAL(a->dnskey, b->dnskey, key);
|
2014-08-03 22:05:41 +02:00
|
|
|
|
2014-08-04 00:17:22 +02:00
|
|
|
case DNS_TYPE_RRSIG:
|
|
|
|
/* do the fast comparisons first */
|
2016-01-29 00:23:59 +01:00
|
|
|
return a->rrsig.type_covered == b->rrsig.type_covered &&
|
|
|
|
a->rrsig.algorithm == b->rrsig.algorithm &&
|
|
|
|
a->rrsig.labels == b->rrsig.labels &&
|
|
|
|
a->rrsig.original_ttl == b->rrsig.original_ttl &&
|
|
|
|
a->rrsig.expiration == b->rrsig.expiration &&
|
|
|
|
a->rrsig.inception == b->rrsig.inception &&
|
|
|
|
a->rrsig.key_tag == b->rrsig.key_tag &&
|
|
|
|
FIELD_EQUAL(a->rrsig, b->rrsig, signature) &&
|
|
|
|
dns_name_equal(a->rrsig.signer, b->rrsig.signer);
|
2014-08-04 00:17:22 +02:00
|
|
|
|
2015-07-13 01:51:03 +02:00
|
|
|
case DNS_TYPE_NSEC:
|
|
|
|
return dns_name_equal(a->nsec.next_domain_name, b->nsec.next_domain_name) &&
|
|
|
|
bitmap_equal(a->nsec.types, b->nsec.types);
|
|
|
|
|
2015-07-13 01:51:03 +02:00
|
|
|
case DNS_TYPE_NSEC3:
|
|
|
|
return a->nsec3.algorithm == b->nsec3.algorithm &&
|
2016-01-29 00:23:59 +01:00
|
|
|
a->nsec3.flags == b->nsec3.flags &&
|
|
|
|
a->nsec3.iterations == b->nsec3.iterations &&
|
|
|
|
FIELD_EQUAL(a->nsec3, b->nsec3, salt) &&
|
|
|
|
FIELD_EQUAL(a->nsec3, b->nsec3, next_hashed_name) &&
|
|
|
|
bitmap_equal(a->nsec3.types, b->nsec3.types);
|
2015-07-13 01:51:03 +02:00
|
|
|
|
2015-02-02 01:17:24 +01:00
|
|
|
case DNS_TYPE_TLSA:
|
|
|
|
return a->tlsa.cert_usage == b->tlsa.cert_usage &&
|
|
|
|
a->tlsa.selector == b->tlsa.selector &&
|
|
|
|
a->tlsa.matching_type == b->tlsa.matching_type &&
|
2016-01-29 00:23:59 +01:00
|
|
|
FIELD_EQUAL(a->tlsa, b->tlsa, data);
|
2015-02-02 01:17:24 +01:00
|
|
|
|
2016-01-31 22:21:00 +01:00
|
|
|
case DNS_TYPE_CAA:
|
|
|
|
return a->caa.flags == b->caa.flags &&
|
|
|
|
streq(a->caa.tag, b->caa.tag) &&
|
|
|
|
FIELD_EQUAL(a->caa, b->caa, value);
|
|
|
|
|
|
|
|
case DNS_TYPE_OPENPGPKEY:
|
2014-07-30 19:23:27 +02:00
|
|
|
default:
|
2016-01-29 00:23:59 +01:00
|
|
|
return FIELD_EQUAL(a->generic, b->generic, data);
|
2014-07-30 19:23:27 +02:00
|
|
|
}
|
2014-07-17 19:38:37 +02:00
|
|
|
}
|
|
|
|
|
2019-02-27 10:27:35 +01:00
|
|
|
int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b) {
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(a);
|
|
|
|
assert(b);
|
|
|
|
|
|
|
|
if (a == b)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
r = dns_resource_key_equal(a->key, b->key);
|
|
|
|
if (r <= 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
return dns_resource_record_payload_equal(a, b);
|
|
|
|
}
|
|
|
|
|
2014-07-31 10:19:43 +02:00
|
|
|
static char* format_location(uint32_t latitude, uint32_t longitude, uint32_t altitude,
|
|
|
|
uint8_t size, uint8_t horiz_pre, uint8_t vert_pre) {
|
|
|
|
char *s;
|
|
|
|
char NS = latitude >= 1U<<31 ? 'N' : 'S';
|
|
|
|
char EW = longitude >= 1U<<31 ? 'E' : 'W';
|
|
|
|
|
|
|
|
int lat = latitude >= 1U<<31 ? (int) (latitude - (1U<<31)) : (int) ((1U<<31) - latitude);
|
|
|
|
int lon = longitude >= 1U<<31 ? (int) (longitude - (1U<<31)) : (int) ((1U<<31) - longitude);
|
|
|
|
double alt = altitude >= 10000000u ? altitude - 10000000u : -(double)(10000000u - altitude);
|
|
|
|
double siz = (size >> 4) * exp10((double) (size & 0xF));
|
|
|
|
double hor = (horiz_pre >> 4) * exp10((double) (horiz_pre & 0xF));
|
|
|
|
double ver = (vert_pre >> 4) * exp10((double) (vert_pre & 0xF));
|
|
|
|
|
|
|
|
if (asprintf(&s, "%d %d %.3f %c %d %d %.3f %c %.2fm %.2fm %.2fm %.2fm",
|
|
|
|
(lat / 60000 / 60),
|
|
|
|
(lat / 60000) % 60,
|
|
|
|
(lat % 60000) / 1000.,
|
|
|
|
NS,
|
|
|
|
(lon / 60000 / 60),
|
|
|
|
(lon / 60000) % 60,
|
|
|
|
(lon % 60000) / 1000.,
|
|
|
|
EW,
|
|
|
|
alt / 100.,
|
|
|
|
siz / 100.,
|
|
|
|
hor / 100.,
|
|
|
|
ver / 100.) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2015-07-13 00:58:00 +02:00
|
|
|
static int format_timestamp_dns(char *buf, size_t l, time_t sec) {
|
|
|
|
struct tm tm;
|
|
|
|
|
|
|
|
assert(buf);
|
2017-12-14 19:02:29 +01:00
|
|
|
assert(l > STRLEN("YYYYMMDDHHmmSS"));
|
2015-07-13 00:58:00 +02:00
|
|
|
|
|
|
|
if (!gmtime_r(&sec, &tm))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (strftime(buf, l, "%Y%m%d%H%M%S", &tm) <= 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-07-13 01:51:03 +02:00
|
|
|
static char *format_types(Bitmap *types) {
|
|
|
|
_cleanup_strv_free_ char **strv = NULL;
|
|
|
|
_cleanup_free_ char *str = NULL;
|
|
|
|
unsigned type;
|
|
|
|
int r;
|
|
|
|
|
2020-09-08 11:58:29 +02:00
|
|
|
BITMAP_FOREACH(type, types) {
|
2015-07-13 01:51:03 +02:00
|
|
|
if (dns_type_to_string(type)) {
|
2015-07-23 03:24:08 +02:00
|
|
|
r = strv_extend(&strv, dns_type_to_string(type));
|
2015-07-13 01:51:03 +02:00
|
|
|
if (r < 0)
|
|
|
|
return NULL;
|
|
|
|
} else {
|
|
|
|
char *t;
|
|
|
|
|
|
|
|
r = asprintf(&t, "TYPE%u", type);
|
|
|
|
if (r < 0)
|
|
|
|
return NULL;
|
|
|
|
|
2015-07-23 03:24:08 +02:00
|
|
|
r = strv_consume(&strv, t);
|
2015-07-13 01:51:03 +02:00
|
|
|
if (r < 0)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
str = strv_join(strv, " ");
|
|
|
|
if (!str)
|
|
|
|
return NULL;
|
|
|
|
|
2016-10-23 17:43:27 +02:00
|
|
|
return strjoin("( ", str, " )");
|
2015-07-13 01:51:03 +02:00
|
|
|
}
|
|
|
|
|
2015-11-20 19:01:43 +01:00
|
|
|
static char *format_txt(DnsTxtItem *first) {
|
|
|
|
DnsTxtItem *i;
|
|
|
|
size_t c = 1;
|
|
|
|
char *p, *s;
|
|
|
|
|
|
|
|
LIST_FOREACH(items, i, first)
|
|
|
|
c += i->length * 4 + 3;
|
|
|
|
|
|
|
|
p = s = new(char, c);
|
|
|
|
if (!s)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
LIST_FOREACH(items, i, first) {
|
|
|
|
size_t j;
|
|
|
|
|
|
|
|
if (i != first)
|
|
|
|
*(p++) = ' ';
|
|
|
|
|
|
|
|
*(p++) = '"';
|
|
|
|
|
|
|
|
for (j = 0; j < i->length; j++) {
|
|
|
|
if (i->data[j] < ' ' || i->data[j] == '"' || i->data[j] >= 127) {
|
|
|
|
*(p++) = '\\';
|
|
|
|
*(p++) = '0' + (i->data[j] / 100);
|
|
|
|
*(p++) = '0' + ((i->data[j] / 10) % 10);
|
|
|
|
*(p++) = '0' + (i->data[j] % 10);
|
|
|
|
} else
|
|
|
|
*(p++) = i->data[j];
|
|
|
|
}
|
|
|
|
|
|
|
|
*(p++) = '"';
|
|
|
|
}
|
|
|
|
|
|
|
|
*p = 0;
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2015-12-21 16:31:29 +01:00
|
|
|
const char *dns_resource_record_to_string(DnsResourceRecord *rr) {
|
2016-02-15 00:51:55 +01:00
|
|
|
_cleanup_free_ char *t = NULL;
|
|
|
|
char *s, k[DNS_RESOURCE_KEY_STRING_MAX];
|
2014-07-30 19:23:27 +02:00
|
|
|
int r;
|
2014-07-17 19:38:37 +02:00
|
|
|
|
2014-07-30 19:23:27 +02:00
|
|
|
assert(rr);
|
2014-07-17 19:38:37 +02:00
|
|
|
|
2015-12-21 16:31:29 +01:00
|
|
|
if (rr->to_string)
|
|
|
|
return rr->to_string;
|
|
|
|
|
2016-02-15 00:51:55 +01:00
|
|
|
dns_resource_key_to_string(rr->key, k, sizeof(k));
|
2014-07-17 19:38:37 +02:00
|
|
|
|
2020-07-02 09:58:23 +02:00
|
|
|
switch (rr->unparsable ? _DNS_TYPE_INVALID : rr->key->type) {
|
2014-07-17 19:38:37 +02:00
|
|
|
|
2014-07-31 18:23:00 +02:00
|
|
|
case DNS_TYPE_SRV:
|
|
|
|
r = asprintf(&s, "%s %u %u %u %s",
|
|
|
|
k,
|
|
|
|
rr->srv.priority,
|
|
|
|
rr->srv.weight,
|
|
|
|
rr->srv.port,
|
|
|
|
strna(rr->srv.name));
|
|
|
|
if (r < 0)
|
2015-12-21 16:31:29 +01:00
|
|
|
return NULL;
|
2014-07-31 18:23:00 +02:00
|
|
|
break;
|
|
|
|
|
2014-07-30 19:23:27 +02:00
|
|
|
case DNS_TYPE_PTR:
|
|
|
|
case DNS_TYPE_NS:
|
|
|
|
case DNS_TYPE_CNAME:
|
2014-07-31 18:02:24 +02:00
|
|
|
case DNS_TYPE_DNAME:
|
2016-10-23 17:43:27 +02:00
|
|
|
s = strjoin(k, " ", rr->ptr.name);
|
2014-07-30 19:23:27 +02:00
|
|
|
if (!s)
|
2015-12-21 16:31:29 +01:00
|
|
|
return NULL;
|
2014-07-17 19:38:37 +02:00
|
|
|
|
2014-07-30 19:23:27 +02:00
|
|
|
break;
|
2014-07-17 19:38:37 +02:00
|
|
|
|
2014-07-30 19:23:27 +02:00
|
|
|
case DNS_TYPE_HINFO:
|
2016-10-23 17:43:27 +02:00
|
|
|
s = strjoin(k, " ", rr->hinfo.cpu, " ", rr->hinfo.os);
|
2014-07-30 19:23:27 +02:00
|
|
|
if (!s)
|
2015-12-21 16:31:29 +01:00
|
|
|
return NULL;
|
2014-07-30 19:23:27 +02:00
|
|
|
break;
|
2014-07-17 19:38:37 +02:00
|
|
|
|
2014-08-01 03:47:51 +02:00
|
|
|
case DNS_TYPE_SPF: /* exactly the same as TXT */
|
2014-08-03 22:05:41 +02:00
|
|
|
case DNS_TYPE_TXT:
|
2015-11-20 19:01:43 +01:00
|
|
|
t = format_txt(rr->txt.items);
|
2014-08-01 03:36:58 +02:00
|
|
|
if (!t)
|
2015-12-21 16:31:29 +01:00
|
|
|
return NULL;
|
2014-08-01 03:36:58 +02:00
|
|
|
|
2016-10-23 17:43:27 +02:00
|
|
|
s = strjoin(k, " ", t);
|
2014-08-01 03:36:58 +02:00
|
|
|
if (!s)
|
2015-12-21 16:31:29 +01:00
|
|
|
return NULL;
|
2014-08-01 03:36:58 +02:00
|
|
|
break;
|
|
|
|
|
2014-07-30 19:23:27 +02:00
|
|
|
case DNS_TYPE_A: {
|
|
|
|
_cleanup_free_ char *x = NULL;
|
2014-07-17 19:38:37 +02:00
|
|
|
|
2014-07-30 19:23:27 +02:00
|
|
|
r = in_addr_to_string(AF_INET, (const union in_addr_union*) &rr->a.in_addr, &x);
|
|
|
|
if (r < 0)
|
2015-12-21 16:31:29 +01:00
|
|
|
return NULL;
|
2014-07-17 19:38:37 +02:00
|
|
|
|
2016-10-23 17:43:27 +02:00
|
|
|
s = strjoin(k, " ", x);
|
2014-07-30 19:23:27 +02:00
|
|
|
if (!s)
|
2015-12-21 16:31:29 +01:00
|
|
|
return NULL;
|
2014-07-30 19:23:27 +02:00
|
|
|
break;
|
|
|
|
}
|
2014-07-17 19:38:37 +02:00
|
|
|
|
2014-08-03 22:05:41 +02:00
|
|
|
case DNS_TYPE_AAAA:
|
|
|
|
r = in_addr_to_string(AF_INET6, (const union in_addr_union*) &rr->aaaa.in6_addr, &t);
|
2014-07-30 19:23:27 +02:00
|
|
|
if (r < 0)
|
2015-12-21 16:31:29 +01:00
|
|
|
return NULL;
|
2014-07-17 19:38:37 +02:00
|
|
|
|
2016-10-23 17:43:27 +02:00
|
|
|
s = strjoin(k, " ", t);
|
2014-07-30 19:23:27 +02:00
|
|
|
if (!s)
|
2015-12-21 16:31:29 +01:00
|
|
|
return NULL;
|
2014-07-30 19:23:27 +02:00
|
|
|
break;
|
2014-07-17 19:38:37 +02:00
|
|
|
|
2014-07-30 19:23:27 +02:00
|
|
|
case DNS_TYPE_SOA:
|
|
|
|
r = asprintf(&s, "%s %s %s %u %u %u %u %u",
|
|
|
|
k,
|
|
|
|
strna(rr->soa.mname),
|
|
|
|
strna(rr->soa.rname),
|
|
|
|
rr->soa.serial,
|
|
|
|
rr->soa.refresh,
|
|
|
|
rr->soa.retry,
|
|
|
|
rr->soa.expire,
|
|
|
|
rr->soa.minimum);
|
|
|
|
if (r < 0)
|
2015-12-21 16:31:29 +01:00
|
|
|
return NULL;
|
2014-07-30 19:23:27 +02:00
|
|
|
break;
|
|
|
|
|
2014-08-01 03:06:00 +02:00
|
|
|
case DNS_TYPE_MX:
|
|
|
|
r = asprintf(&s, "%s %u %s",
|
|
|
|
k,
|
|
|
|
rr->mx.priority,
|
|
|
|
rr->mx.exchange);
|
|
|
|
if (r < 0)
|
2015-12-21 16:31:29 +01:00
|
|
|
return NULL;
|
2014-08-01 03:06:00 +02:00
|
|
|
break;
|
|
|
|
|
2014-08-03 22:05:41 +02:00
|
|
|
case DNS_TYPE_LOC:
|
2014-07-31 10:19:43 +02:00
|
|
|
assert(rr->loc.version == 0);
|
|
|
|
|
2014-08-03 22:05:41 +02:00
|
|
|
t = format_location(rr->loc.latitude,
|
|
|
|
rr->loc.longitude,
|
|
|
|
rr->loc.altitude,
|
|
|
|
rr->loc.size,
|
|
|
|
rr->loc.horiz_pre,
|
|
|
|
rr->loc.vert_pre);
|
|
|
|
if (!t)
|
2015-12-21 16:31:29 +01:00
|
|
|
return NULL;
|
2014-07-31 10:19:43 +02:00
|
|
|
|
2016-10-23 17:43:27 +02:00
|
|
|
s = strjoin(k, " ", t);
|
2014-07-31 10:19:43 +02:00
|
|
|
if (!s)
|
2015-12-21 16:31:29 +01:00
|
|
|
return NULL;
|
2014-07-31 10:19:43 +02:00
|
|
|
break;
|
|
|
|
|
2015-07-13 01:51:03 +02:00
|
|
|
case DNS_TYPE_DS:
|
|
|
|
t = hexmem(rr->ds.digest, rr->ds.digest_size);
|
|
|
|
if (!t)
|
2015-12-21 16:31:29 +01:00
|
|
|
return NULL;
|
2015-07-13 01:51:03 +02:00
|
|
|
|
|
|
|
r = asprintf(&s, "%s %u %u %u %s",
|
|
|
|
k,
|
|
|
|
rr->ds.key_tag,
|
|
|
|
rr->ds.algorithm,
|
|
|
|
rr->ds.digest_type,
|
|
|
|
t);
|
|
|
|
if (r < 0)
|
2015-12-21 16:31:29 +01:00
|
|
|
return NULL;
|
2015-07-13 01:51:03 +02:00
|
|
|
break;
|
|
|
|
|
2014-08-03 22:05:41 +02:00
|
|
|
case DNS_TYPE_SSHFP:
|
2015-07-23 13:09:35 +02:00
|
|
|
t = hexmem(rr->sshfp.fingerprint, rr->sshfp.fingerprint_size);
|
2014-08-03 22:05:41 +02:00
|
|
|
if (!t)
|
2015-12-21 16:31:29 +01:00
|
|
|
return NULL;
|
2014-07-31 18:41:41 +02:00
|
|
|
|
|
|
|
r = asprintf(&s, "%s %u %u %s",
|
|
|
|
k,
|
|
|
|
rr->sshfp.algorithm,
|
|
|
|
rr->sshfp.fptype,
|
2014-08-03 22:05:41 +02:00
|
|
|
t);
|
2014-07-31 18:41:41 +02:00
|
|
|
if (r < 0)
|
2015-12-21 16:31:29 +01:00
|
|
|
return NULL;
|
2014-07-31 18:41:41 +02:00
|
|
|
break;
|
|
|
|
|
2014-08-03 22:44:49 +02:00
|
|
|
case DNS_TYPE_DNSKEY: {
|
2016-01-02 22:12:13 +01:00
|
|
|
_cleanup_free_ char *alg = NULL;
|
2015-02-03 05:49:49 +01:00
|
|
|
char *ss;
|
2015-02-04 23:06:33 +01:00
|
|
|
uint16_t key_tag;
|
|
|
|
|
|
|
|
key_tag = dnssec_keytag(rr, true);
|
2014-08-03 22:44:49 +02:00
|
|
|
|
2016-01-02 22:12:13 +01:00
|
|
|
r = dnssec_algorithm_to_string_alloc(rr->dnskey.algorithm, &alg);
|
|
|
|
if (r < 0)
|
|
|
|
return NULL;
|
2014-08-03 22:44:49 +02:00
|
|
|
|
2019-02-16 17:04:49 +01:00
|
|
|
r = asprintf(&s, "%s %u %u %s",
|
2014-08-03 22:05:41 +02:00
|
|
|
k,
|
2015-12-02 20:53:10 +01:00
|
|
|
rr->dnskey.flags,
|
|
|
|
rr->dnskey.protocol,
|
2019-02-16 17:04:49 +01:00
|
|
|
alg);
|
2014-08-03 22:05:41 +02:00
|
|
|
if (r < 0)
|
2015-12-21 16:31:29 +01:00
|
|
|
return NULL;
|
2014-08-05 00:59:31 +02:00
|
|
|
|
2019-02-16 17:04:49 +01:00
|
|
|
r = base64_append(&s, r,
|
2014-08-05 00:59:31 +02:00
|
|
|
rr->dnskey.key, rr->dnskey.key_size,
|
|
|
|
8, columns());
|
|
|
|
if (r < 0)
|
|
|
|
return NULL;
|
|
|
|
|
2015-02-03 05:49:49 +01:00
|
|
|
r = asprintf(&ss, "%s\n"
|
2016-02-02 03:35:44 +01:00
|
|
|
" -- Flags:%s%s%s\n"
|
|
|
|
" -- Key tag: %u",
|
2015-02-03 05:49:49 +01:00
|
|
|
s,
|
|
|
|
rr->dnskey.flags & DNSKEY_FLAG_SEP ? " SEP" : "",
|
|
|
|
rr->dnskey.flags & DNSKEY_FLAG_REVOKE ? " REVOKE" : "",
|
2015-02-04 23:06:33 +01:00
|
|
|
rr->dnskey.flags & DNSKEY_FLAG_ZONE_KEY ? " ZONE_KEY" : "",
|
|
|
|
key_tag);
|
2015-02-03 05:49:49 +01:00
|
|
|
if (r < 0)
|
|
|
|
return NULL;
|
|
|
|
free(s);
|
|
|
|
s = ss;
|
|
|
|
|
2014-08-03 22:05:41 +02:00
|
|
|
break;
|
2014-08-03 22:44:49 +02:00
|
|
|
}
|
2014-07-30 19:23:27 +02:00
|
|
|
|
2014-08-04 00:17:22 +02:00
|
|
|
case DNS_TYPE_RRSIG: {
|
2016-01-02 22:12:13 +01:00
|
|
|
_cleanup_free_ char *alg = NULL;
|
2017-12-14 19:02:29 +01:00
|
|
|
char expiration[STRLEN("YYYYMMDDHHmmSS") + 1], inception[STRLEN("YYYYMMDDHHmmSS") + 1];
|
2016-01-02 22:12:13 +01:00
|
|
|
const char *type;
|
2014-08-04 00:17:22 +02:00
|
|
|
|
|
|
|
type = dns_type_to_string(rr->rrsig.type_covered);
|
2016-01-02 22:12:13 +01:00
|
|
|
|
|
|
|
r = dnssec_algorithm_to_string_alloc(rr->rrsig.algorithm, &alg);
|
|
|
|
if (r < 0)
|
|
|
|
return NULL;
|
2014-08-04 00:17:22 +02:00
|
|
|
|
2015-07-13 00:58:00 +02:00
|
|
|
r = format_timestamp_dns(expiration, sizeof(expiration), rr->rrsig.expiration);
|
|
|
|
if (r < 0)
|
2015-12-21 16:31:29 +01:00
|
|
|
return NULL;
|
2015-07-13 00:58:00 +02:00
|
|
|
|
|
|
|
r = format_timestamp_dns(inception, sizeof(inception), rr->rrsig.inception);
|
|
|
|
if (r < 0)
|
2015-12-21 16:31:29 +01:00
|
|
|
return NULL;
|
2015-07-13 00:58:00 +02:00
|
|
|
|
2014-08-04 00:17:22 +02:00
|
|
|
/* TYPE?? follows
|
|
|
|
* http://tools.ietf.org/html/rfc3597#section-5 */
|
|
|
|
|
2019-02-16 17:04:49 +01:00
|
|
|
r = asprintf(&s, "%s %s%.*u %s %u %u %s %s %u %s",
|
2014-08-04 00:17:22 +02:00
|
|
|
k,
|
|
|
|
type ?: "TYPE",
|
|
|
|
type ? 0 : 1, type ? 0u : (unsigned) rr->rrsig.type_covered,
|
2016-01-02 22:12:13 +01:00
|
|
|
alg,
|
2014-08-04 00:17:22 +02:00
|
|
|
rr->rrsig.labels,
|
|
|
|
rr->rrsig.original_ttl,
|
2015-07-13 00:58:00 +02:00
|
|
|
expiration,
|
|
|
|
inception,
|
2014-08-04 00:17:22 +02:00
|
|
|
rr->rrsig.key_tag,
|
2019-02-16 17:04:49 +01:00
|
|
|
rr->rrsig.signer);
|
2014-08-04 00:17:22 +02:00
|
|
|
if (r < 0)
|
2015-12-21 16:31:29 +01:00
|
|
|
return NULL;
|
2014-08-05 00:59:31 +02:00
|
|
|
|
2019-02-16 17:04:49 +01:00
|
|
|
r = base64_append(&s, r,
|
2014-08-05 00:59:31 +02:00
|
|
|
rr->rrsig.signature, rr->rrsig.signature_size,
|
|
|
|
8, columns());
|
|
|
|
if (r < 0)
|
|
|
|
return NULL;
|
|
|
|
|
2014-08-04 00:17:22 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-07-13 01:51:03 +02:00
|
|
|
case DNS_TYPE_NSEC:
|
|
|
|
t = format_types(rr->nsec.types);
|
|
|
|
if (!t)
|
2015-12-21 16:31:29 +01:00
|
|
|
return NULL;
|
2015-07-13 01:51:03 +02:00
|
|
|
|
|
|
|
r = asprintf(&s, "%s %s %s",
|
|
|
|
k,
|
|
|
|
rr->nsec.next_domain_name,
|
|
|
|
t);
|
|
|
|
if (r < 0)
|
2015-12-21 16:31:29 +01:00
|
|
|
return NULL;
|
2015-07-13 01:51:03 +02:00
|
|
|
break;
|
|
|
|
|
2015-07-13 01:51:03 +02:00
|
|
|
case DNS_TYPE_NSEC3: {
|
|
|
|
_cleanup_free_ char *salt = NULL, *hash = NULL;
|
|
|
|
|
2015-07-23 04:04:19 +02:00
|
|
|
if (rr->nsec3.salt_size > 0) {
|
2015-07-13 01:51:03 +02:00
|
|
|
salt = hexmem(rr->nsec3.salt, rr->nsec3.salt_size);
|
|
|
|
if (!salt)
|
2015-12-21 16:31:29 +01:00
|
|
|
return NULL;
|
2015-07-13 01:51:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
hash = base32hexmem(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, false);
|
|
|
|
if (!hash)
|
2015-12-21 16:31:29 +01:00
|
|
|
return NULL;
|
2015-07-13 01:51:03 +02:00
|
|
|
|
|
|
|
t = format_types(rr->nsec3.types);
|
|
|
|
if (!t)
|
2015-12-21 16:31:29 +01:00
|
|
|
return NULL;
|
2015-07-13 01:51:03 +02:00
|
|
|
|
|
|
|
r = asprintf(&s, "%s %"PRIu8" %"PRIu8" %"PRIu16" %s %s %s",
|
|
|
|
k,
|
|
|
|
rr->nsec3.algorithm,
|
|
|
|
rr->nsec3.flags,
|
|
|
|
rr->nsec3.iterations,
|
2015-07-23 04:04:19 +02:00
|
|
|
rr->nsec3.salt_size > 0 ? salt : "-",
|
2015-07-13 01:51:03 +02:00
|
|
|
hash,
|
|
|
|
t);
|
|
|
|
if (r < 0)
|
2015-12-21 16:31:29 +01:00
|
|
|
return NULL;
|
2015-07-13 01:51:03 +02:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-02-02 01:17:24 +01:00
|
|
|
case DNS_TYPE_TLSA: {
|
2015-02-02 05:50:50 +01:00
|
|
|
const char *cert_usage, *selector, *matching_type;
|
2015-02-02 01:17:24 +01:00
|
|
|
|
2015-02-02 05:50:50 +01:00
|
|
|
cert_usage = tlsa_cert_usage_to_string(rr->tlsa.cert_usage);
|
|
|
|
selector = tlsa_selector_to_string(rr->tlsa.selector);
|
|
|
|
matching_type = tlsa_matching_type_to_string(rr->tlsa.matching_type);
|
|
|
|
|
2016-02-17 02:36:10 +01:00
|
|
|
t = hexmem(rr->sshfp.fingerprint, rr->sshfp.fingerprint_size);
|
|
|
|
if (!t)
|
2015-02-02 01:17:24 +01:00
|
|
|
return NULL;
|
2015-02-02 05:50:50 +01:00
|
|
|
|
2016-02-17 02:36:10 +01:00
|
|
|
r = asprintf(&s,
|
|
|
|
"%s %u %u %u %s\n"
|
2016-02-02 03:35:44 +01:00
|
|
|
" -- Cert. usage: %s\n"
|
|
|
|
" -- Selector: %s\n"
|
|
|
|
" -- Matching type: %s",
|
2016-02-17 02:36:10 +01:00
|
|
|
k,
|
|
|
|
rr->tlsa.cert_usage,
|
|
|
|
rr->tlsa.selector,
|
|
|
|
rr->tlsa.matching_type,
|
|
|
|
t,
|
2016-02-02 03:35:44 +01:00
|
|
|
cert_usage,
|
|
|
|
selector,
|
|
|
|
matching_type);
|
2015-02-02 05:50:50 +01:00
|
|
|
if (r < 0)
|
|
|
|
return NULL;
|
|
|
|
|
2015-02-02 01:17:24 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-01-31 22:21:00 +01:00
|
|
|
case DNS_TYPE_CAA: {
|
|
|
|
_cleanup_free_ char *value;
|
|
|
|
|
|
|
|
value = octescape(rr->caa.value, rr->caa.value_size);
|
|
|
|
if (!value)
|
|
|
|
return NULL;
|
|
|
|
|
2016-02-02 03:35:44 +01:00
|
|
|
r = asprintf(&s, "%s %u %s \"%s\"%s%s%s%.0u",
|
2016-01-31 22:21:00 +01:00
|
|
|
k,
|
|
|
|
rr->caa.flags,
|
|
|
|
rr->caa.tag,
|
2016-02-02 03:35:44 +01:00
|
|
|
value,
|
|
|
|
rr->caa.flags ? "\n -- Flags:" : "",
|
|
|
|
rr->caa.flags & CAA_FLAG_CRITICAL ? " critical" : "",
|
|
|
|
rr->caa.flags & ~CAA_FLAG_CRITICAL ? " " : "",
|
|
|
|
rr->caa.flags & ~CAA_FLAG_CRITICAL);
|
2016-01-31 22:21:00 +01:00
|
|
|
if (r < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-02-02 02:54:15 +01:00
|
|
|
case DNS_TYPE_OPENPGPKEY: {
|
2019-02-16 17:04:49 +01:00
|
|
|
r = asprintf(&s, "%s", k);
|
2015-02-02 02:54:15 +01:00
|
|
|
if (r < 0)
|
|
|
|
return NULL;
|
|
|
|
|
2019-02-16 17:04:49 +01:00
|
|
|
r = base64_append(&s, r,
|
2016-01-29 00:23:59 +01:00
|
|
|
rr->generic.data, rr->generic.data_size,
|
2015-02-02 02:54:15 +01:00
|
|
|
8, columns());
|
|
|
|
if (r < 0)
|
|
|
|
return NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-08-03 22:05:41 +02:00
|
|
|
default:
|
2016-01-29 00:23:59 +01:00
|
|
|
t = hexmem(rr->generic.data, rr->generic.data_size);
|
2014-08-03 22:05:41 +02:00
|
|
|
if (!t)
|
2015-12-21 16:31:29 +01:00
|
|
|
return NULL;
|
2014-08-03 22:05:41 +02:00
|
|
|
|
2015-12-29 19:27:09 +01:00
|
|
|
/* Format as documented in RFC 3597, Section 5 */
|
2016-01-29 00:23:59 +01:00
|
|
|
r = asprintf(&s, "%s \\# %zu %s", k, rr->generic.data_size, t);
|
2015-07-14 04:32:29 +02:00
|
|
|
if (r < 0)
|
2015-12-21 16:31:29 +01:00
|
|
|
return NULL;
|
2014-07-30 19:23:27 +02:00
|
|
|
break;
|
2014-08-03 22:05:41 +02:00
|
|
|
}
|
2014-07-30 19:23:27 +02:00
|
|
|
|
2015-12-21 16:31:29 +01:00
|
|
|
rr->to_string = s;
|
|
|
|
return s;
|
2014-07-30 19:23:27 +02:00
|
|
|
}
|
2014-07-17 19:38:37 +02:00
|
|
|
|
2016-01-29 00:24:28 +01:00
|
|
|
ssize_t dns_resource_record_payload(DnsResourceRecord *rr, void **out) {
|
|
|
|
assert(rr);
|
|
|
|
assert(out);
|
|
|
|
|
2020-07-02 09:58:23 +02:00
|
|
|
switch(rr->unparsable ? _DNS_TYPE_INVALID : rr->key->type) {
|
2016-01-29 00:24:28 +01:00
|
|
|
case DNS_TYPE_SRV:
|
|
|
|
case DNS_TYPE_PTR:
|
|
|
|
case DNS_TYPE_NS:
|
|
|
|
case DNS_TYPE_CNAME:
|
|
|
|
case DNS_TYPE_DNAME:
|
|
|
|
case DNS_TYPE_HINFO:
|
|
|
|
case DNS_TYPE_SPF:
|
|
|
|
case DNS_TYPE_TXT:
|
|
|
|
case DNS_TYPE_A:
|
|
|
|
case DNS_TYPE_AAAA:
|
|
|
|
case DNS_TYPE_SOA:
|
|
|
|
case DNS_TYPE_MX:
|
|
|
|
case DNS_TYPE_LOC:
|
|
|
|
case DNS_TYPE_DS:
|
|
|
|
case DNS_TYPE_DNSKEY:
|
|
|
|
case DNS_TYPE_RRSIG:
|
|
|
|
case DNS_TYPE_NSEC:
|
|
|
|
case DNS_TYPE_NSEC3:
|
|
|
|
return -EINVAL;
|
|
|
|
|
2016-02-17 02:55:23 +01:00
|
|
|
case DNS_TYPE_SSHFP:
|
|
|
|
*out = rr->sshfp.fingerprint;
|
|
|
|
return rr->sshfp.fingerprint_size;
|
|
|
|
|
2016-01-29 00:24:28 +01:00
|
|
|
case DNS_TYPE_TLSA:
|
|
|
|
*out = rr->tlsa.data;
|
|
|
|
return rr->tlsa.data_size;
|
|
|
|
|
|
|
|
case DNS_TYPE_OPENPGPKEY:
|
|
|
|
default:
|
|
|
|
*out = rr->generic.data;
|
|
|
|
return rr->generic.data_size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-02 20:58:51 +01:00
|
|
|
int dns_resource_record_to_wire_format(DnsResourceRecord *rr, bool canonical) {
|
|
|
|
|
|
|
|
DnsPacket packet = {
|
|
|
|
.n_ref = 1,
|
|
|
|
.protocol = DNS_PROTOCOL_DNS,
|
|
|
|
.on_stack = true,
|
|
|
|
.refuse_compression = true,
|
|
|
|
.canonical_form = canonical,
|
|
|
|
};
|
|
|
|
|
|
|
|
size_t start, rds;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(rr);
|
|
|
|
|
|
|
|
/* Generates the RR in wire-format, optionally in the
|
|
|
|
* canonical form as discussed in the DNSSEC RFC 4034, Section
|
|
|
|
* 6.2. We allocate a throw-away DnsPacket object on the stack
|
|
|
|
* here, because we need some book-keeping for memory
|
|
|
|
* management, and can reuse the DnsPacket serializer, that
|
|
|
|
* can generate the canonical form, too, but also knows label
|
|
|
|
* compression and suchlike. */
|
|
|
|
|
|
|
|
if (rr->wire_format && rr->wire_format_canonical == canonical)
|
|
|
|
return 0;
|
|
|
|
|
2016-12-02 14:28:14 +01:00
|
|
|
r = dns_packet_append_rr(&packet, rr, 0, &start, &rds);
|
2015-12-02 20:58:51 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
assert(start == 0);
|
|
|
|
assert(packet._data);
|
|
|
|
|
|
|
|
free(rr->wire_format);
|
|
|
|
rr->wire_format = packet._data;
|
|
|
|
rr->wire_format_size = packet.size;
|
|
|
|
rr->wire_format_rdata_offset = rds;
|
|
|
|
rr->wire_format_canonical = canonical;
|
|
|
|
|
|
|
|
packet._data = NULL;
|
|
|
|
dns_packet_unref(&packet);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-01-14 18:03:03 +01:00
|
|
|
int dns_resource_record_signer(DnsResourceRecord *rr, const char **ret) {
|
|
|
|
const char *n;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(rr);
|
|
|
|
assert(ret);
|
|
|
|
|
|
|
|
/* Returns the RRset's signer, if it is known. */
|
|
|
|
|
|
|
|
if (rr->n_skip_labels_signer == (unsigned) -1)
|
|
|
|
return -ENODATA;
|
|
|
|
|
2016-02-13 20:54:15 +01:00
|
|
|
n = dns_resource_key_name(rr->key);
|
2016-01-14 18:03:03 +01:00
|
|
|
r = dns_name_skip(n, rr->n_skip_labels_signer, &n);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (r == 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
*ret = n;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int dns_resource_record_source(DnsResourceRecord *rr, const char **ret) {
|
|
|
|
const char *n;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(rr);
|
|
|
|
assert(ret);
|
|
|
|
|
|
|
|
/* Returns the RRset's synthesizing source, if it is known. */
|
|
|
|
|
|
|
|
if (rr->n_skip_labels_source == (unsigned) -1)
|
|
|
|
return -ENODATA;
|
|
|
|
|
2016-02-13 20:54:15 +01:00
|
|
|
n = dns_resource_key_name(rr->key);
|
2016-01-14 18:03:03 +01:00
|
|
|
r = dns_name_skip(n, rr->n_skip_labels_source, &n);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (r == 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
*ret = n;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int dns_resource_record_is_signer(DnsResourceRecord *rr, const char *zone) {
|
|
|
|
const char *signer;
|
|
|
|
int r;
|
|
|
|
|
2016-01-15 02:21:22 +01:00
|
|
|
assert(rr);
|
|
|
|
|
2016-01-14 18:03:03 +01:00
|
|
|
r = dns_resource_record_signer(rr, &signer);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
return dns_name_equal(zone, signer);
|
|
|
|
}
|
|
|
|
|
2016-01-15 02:21:22 +01:00
|
|
|
int dns_resource_record_is_synthetic(DnsResourceRecord *rr) {
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(rr);
|
|
|
|
|
|
|
|
/* Returns > 0 if the RR is generated from a wildcard, and is not the asterisk name itself */
|
|
|
|
|
|
|
|
if (rr->n_skip_labels_source == (unsigned) -1)
|
|
|
|
return -ENODATA;
|
|
|
|
|
|
|
|
if (rr->n_skip_labels_source == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (rr->n_skip_labels_source > 1)
|
|
|
|
return 1;
|
|
|
|
|
2016-02-13 20:54:15 +01:00
|
|
|
r = dns_name_startswith(dns_resource_key_name(rr->key), "*");
|
2016-01-15 02:21:22 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
return !r;
|
|
|
|
}
|
|
|
|
|
2018-11-27 14:25:20 +01:00
|
|
|
void dns_resource_record_hash_func(const DnsResourceRecord *rr, struct siphash *state) {
|
2016-01-07 20:33:31 +01:00
|
|
|
assert(rr);
|
|
|
|
|
|
|
|
dns_resource_key_hash_func(rr->key, state);
|
|
|
|
|
2020-07-02 09:58:23 +02:00
|
|
|
switch (rr->unparsable ? _DNS_TYPE_INVALID : rr->key->type) {
|
2016-01-07 20:33:31 +01:00
|
|
|
|
|
|
|
case DNS_TYPE_SRV:
|
|
|
|
siphash24_compress(&rr->srv.priority, sizeof(rr->srv.priority), state);
|
|
|
|
siphash24_compress(&rr->srv.weight, sizeof(rr->srv.weight), state);
|
|
|
|
siphash24_compress(&rr->srv.port, sizeof(rr->srv.port), state);
|
|
|
|
dns_name_hash_func(rr->srv.name, state);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DNS_TYPE_PTR:
|
|
|
|
case DNS_TYPE_NS:
|
|
|
|
case DNS_TYPE_CNAME:
|
|
|
|
case DNS_TYPE_DNAME:
|
|
|
|
dns_name_hash_func(rr->ptr.name, state);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DNS_TYPE_HINFO:
|
|
|
|
string_hash_func(rr->hinfo.cpu, state);
|
|
|
|
string_hash_func(rr->hinfo.os, state);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DNS_TYPE_TXT:
|
|
|
|
case DNS_TYPE_SPF: {
|
|
|
|
DnsTxtItem *j;
|
|
|
|
|
|
|
|
LIST_FOREACH(items, j, rr->txt.items) {
|
2020-12-29 17:48:35 +01:00
|
|
|
siphash24_compress_safe(j->data, j->length, state);
|
2016-01-07 20:33:31 +01:00
|
|
|
|
2016-01-08 01:11:55 +01:00
|
|
|
/* Add an extra NUL byte, so that "a" followed by "b" doesn't result in the same hash as "ab"
|
|
|
|
* followed by "". */
|
|
|
|
siphash24_compress_byte(0, state);
|
2016-01-07 20:33:31 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DNS_TYPE_A:
|
|
|
|
siphash24_compress(&rr->a.in_addr, sizeof(rr->a.in_addr), state);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DNS_TYPE_AAAA:
|
|
|
|
siphash24_compress(&rr->aaaa.in6_addr, sizeof(rr->aaaa.in6_addr), state);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DNS_TYPE_SOA:
|
|
|
|
dns_name_hash_func(rr->soa.mname, state);
|
|
|
|
dns_name_hash_func(rr->soa.rname, state);
|
|
|
|
siphash24_compress(&rr->soa.serial, sizeof(rr->soa.serial), state);
|
|
|
|
siphash24_compress(&rr->soa.refresh, sizeof(rr->soa.refresh), state);
|
|
|
|
siphash24_compress(&rr->soa.retry, sizeof(rr->soa.retry), state);
|
|
|
|
siphash24_compress(&rr->soa.expire, sizeof(rr->soa.expire), state);
|
|
|
|
siphash24_compress(&rr->soa.minimum, sizeof(rr->soa.minimum), state);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DNS_TYPE_MX:
|
|
|
|
siphash24_compress(&rr->mx.priority, sizeof(rr->mx.priority), state);
|
|
|
|
dns_name_hash_func(rr->mx.exchange, state);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DNS_TYPE_LOC:
|
|
|
|
siphash24_compress(&rr->loc.version, sizeof(rr->loc.version), state);
|
|
|
|
siphash24_compress(&rr->loc.size, sizeof(rr->loc.size), state);
|
|
|
|
siphash24_compress(&rr->loc.horiz_pre, sizeof(rr->loc.horiz_pre), state);
|
|
|
|
siphash24_compress(&rr->loc.vert_pre, sizeof(rr->loc.vert_pre), state);
|
|
|
|
siphash24_compress(&rr->loc.latitude, sizeof(rr->loc.latitude), state);
|
|
|
|
siphash24_compress(&rr->loc.longitude, sizeof(rr->loc.longitude), state);
|
|
|
|
siphash24_compress(&rr->loc.altitude, sizeof(rr->loc.altitude), state);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DNS_TYPE_SSHFP:
|
|
|
|
siphash24_compress(&rr->sshfp.algorithm, sizeof(rr->sshfp.algorithm), state);
|
|
|
|
siphash24_compress(&rr->sshfp.fptype, sizeof(rr->sshfp.fptype), state);
|
2020-12-29 17:48:35 +01:00
|
|
|
siphash24_compress_safe(rr->sshfp.fingerprint, rr->sshfp.fingerprint_size, state);
|
2016-01-07 20:33:31 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case DNS_TYPE_DNSKEY:
|
|
|
|
siphash24_compress(&rr->dnskey.flags, sizeof(rr->dnskey.flags), state);
|
|
|
|
siphash24_compress(&rr->dnskey.protocol, sizeof(rr->dnskey.protocol), state);
|
|
|
|
siphash24_compress(&rr->dnskey.algorithm, sizeof(rr->dnskey.algorithm), state);
|
2020-12-29 17:48:35 +01:00
|
|
|
siphash24_compress_safe(rr->dnskey.key, rr->dnskey.key_size, state);
|
2016-01-07 20:33:31 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case DNS_TYPE_RRSIG:
|
|
|
|
siphash24_compress(&rr->rrsig.type_covered, sizeof(rr->rrsig.type_covered), state);
|
|
|
|
siphash24_compress(&rr->rrsig.algorithm, sizeof(rr->rrsig.algorithm), state);
|
|
|
|
siphash24_compress(&rr->rrsig.labels, sizeof(rr->rrsig.labels), state);
|
|
|
|
siphash24_compress(&rr->rrsig.original_ttl, sizeof(rr->rrsig.original_ttl), state);
|
|
|
|
siphash24_compress(&rr->rrsig.expiration, sizeof(rr->rrsig.expiration), state);
|
|
|
|
siphash24_compress(&rr->rrsig.inception, sizeof(rr->rrsig.inception), state);
|
|
|
|
siphash24_compress(&rr->rrsig.key_tag, sizeof(rr->rrsig.key_tag), state);
|
|
|
|
dns_name_hash_func(rr->rrsig.signer, state);
|
2020-12-29 17:48:35 +01:00
|
|
|
siphash24_compress_safe(rr->rrsig.signature, rr->rrsig.signature_size, state);
|
2016-01-07 20:33:31 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case DNS_TYPE_NSEC:
|
|
|
|
dns_name_hash_func(rr->nsec.next_domain_name, state);
|
|
|
|
/* FIXME: we leave out the type bitmap here. Hash
|
|
|
|
* would be better if we'd take it into account
|
|
|
|
* too. */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DNS_TYPE_DS:
|
|
|
|
siphash24_compress(&rr->ds.key_tag, sizeof(rr->ds.key_tag), state);
|
|
|
|
siphash24_compress(&rr->ds.algorithm, sizeof(rr->ds.algorithm), state);
|
|
|
|
siphash24_compress(&rr->ds.digest_type, sizeof(rr->ds.digest_type), state);
|
2020-12-29 17:48:35 +01:00
|
|
|
siphash24_compress_safe(rr->ds.digest, rr->ds.digest_size, state);
|
2016-01-07 20:33:31 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case DNS_TYPE_NSEC3:
|
|
|
|
siphash24_compress(&rr->nsec3.algorithm, sizeof(rr->nsec3.algorithm), state);
|
|
|
|
siphash24_compress(&rr->nsec3.flags, sizeof(rr->nsec3.flags), state);
|
|
|
|
siphash24_compress(&rr->nsec3.iterations, sizeof(rr->nsec3.iterations), state);
|
2020-12-29 17:48:35 +01:00
|
|
|
siphash24_compress_safe(rr->nsec3.salt, rr->nsec3.salt_size, state);
|
|
|
|
siphash24_compress_safe(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, state);
|
2016-01-07 20:33:31 +01:00
|
|
|
/* FIXME: We leave the bitmaps out */
|
|
|
|
break;
|
2015-02-02 01:17:24 +01:00
|
|
|
|
|
|
|
case DNS_TYPE_TLSA:
|
|
|
|
siphash24_compress(&rr->tlsa.cert_usage, sizeof(rr->tlsa.cert_usage), state);
|
|
|
|
siphash24_compress(&rr->tlsa.selector, sizeof(rr->tlsa.selector), state);
|
|
|
|
siphash24_compress(&rr->tlsa.matching_type, sizeof(rr->tlsa.matching_type), state);
|
2020-12-29 17:48:35 +01:00
|
|
|
siphash24_compress_safe(rr->tlsa.data, rr->tlsa.data_size, state);
|
2015-02-02 01:17:24 +01:00
|
|
|
break;
|
2016-01-07 20:33:31 +01:00
|
|
|
|
2016-01-31 22:21:00 +01:00
|
|
|
case DNS_TYPE_CAA:
|
|
|
|
siphash24_compress(&rr->caa.flags, sizeof(rr->caa.flags), state);
|
|
|
|
string_hash_func(rr->caa.tag, state);
|
2020-12-29 17:48:35 +01:00
|
|
|
siphash24_compress_safe(rr->caa.value, rr->caa.value_size, state);
|
2015-02-02 01:17:24 +01:00
|
|
|
break;
|
2016-01-07 20:33:31 +01:00
|
|
|
|
2015-02-02 02:54:15 +01:00
|
|
|
case DNS_TYPE_OPENPGPKEY:
|
2016-01-07 20:33:31 +01:00
|
|
|
default:
|
2020-12-29 17:48:35 +01:00
|
|
|
siphash24_compress_safe(rr->generic.data, rr->generic.data_size, state);
|
2016-01-07 20:33:31 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-29 15:50:54 +01:00
|
|
|
int dns_resource_record_compare_func(const DnsResourceRecord *x, const DnsResourceRecord *y) {
|
2018-11-27 14:25:20 +01:00
|
|
|
int r;
|
2016-01-07 20:33:31 +01:00
|
|
|
|
2018-11-27 14:25:20 +01:00
|
|
|
r = dns_resource_key_compare_func(x->key, y->key);
|
|
|
|
if (r != 0)
|
|
|
|
return r;
|
2016-01-07 20:33:31 +01:00
|
|
|
|
2020-12-29 15:44:53 +01:00
|
|
|
if (dns_resource_record_payload_equal(x, y) > 0)
|
2016-01-07 20:33:31 +01:00
|
|
|
return 0;
|
|
|
|
|
2018-08-07 04:21:38 +02:00
|
|
|
/* We still use CMP() here, even though don't implement proper
|
|
|
|
* ordering, since the hashtable doesn't need ordering anyway. */
|
|
|
|
return CMP(x, y);
|
2016-01-07 20:33:31 +01:00
|
|
|
}
|
|
|
|
|
2018-11-27 14:25:20 +01:00
|
|
|
DEFINE_HASH_OPS(dns_resource_record_hash_ops, DnsResourceRecord, dns_resource_record_hash_func, dns_resource_record_compare_func);
|
2016-01-07 20:33:31 +01:00
|
|
|
|
2016-06-20 21:24:46 +02:00
|
|
|
DnsResourceRecord *dns_resource_record_copy(DnsResourceRecord *rr) {
|
|
|
|
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *copy = NULL;
|
|
|
|
DnsResourceRecord *t;
|
|
|
|
|
|
|
|
assert(rr);
|
|
|
|
|
|
|
|
copy = dns_resource_record_new(rr->key);
|
|
|
|
if (!copy)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
copy->ttl = rr->ttl;
|
|
|
|
copy->expiry = rr->expiry;
|
|
|
|
copy->n_skip_labels_signer = rr->n_skip_labels_signer;
|
|
|
|
copy->n_skip_labels_source = rr->n_skip_labels_source;
|
2020-07-02 09:58:23 +02:00
|
|
|
copy->unparsable = rr->unparsable;
|
2016-06-20 21:24:46 +02:00
|
|
|
|
2020-07-02 09:58:23 +02:00
|
|
|
switch (rr->unparsable ? _DNS_TYPE_INVALID : rr->key->type) {
|
2016-06-20 21:24:46 +02:00
|
|
|
|
|
|
|
case DNS_TYPE_SRV:
|
|
|
|
copy->srv.priority = rr->srv.priority;
|
|
|
|
copy->srv.weight = rr->srv.weight;
|
|
|
|
copy->srv.port = rr->srv.port;
|
|
|
|
copy->srv.name = strdup(rr->srv.name);
|
|
|
|
if (!copy->srv.name)
|
|
|
|
return NULL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DNS_TYPE_PTR:
|
|
|
|
case DNS_TYPE_NS:
|
|
|
|
case DNS_TYPE_CNAME:
|
|
|
|
case DNS_TYPE_DNAME:
|
|
|
|
copy->ptr.name = strdup(rr->ptr.name);
|
|
|
|
if (!copy->ptr.name)
|
|
|
|
return NULL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DNS_TYPE_HINFO:
|
|
|
|
copy->hinfo.cpu = strdup(rr->hinfo.cpu);
|
|
|
|
if (!copy->hinfo.cpu)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
copy->hinfo.os = strdup(rr->hinfo.os);
|
2017-12-02 01:49:52 +01:00
|
|
|
if (!copy->hinfo.os)
|
2016-06-20 21:24:46 +02:00
|
|
|
return NULL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DNS_TYPE_TXT:
|
|
|
|
case DNS_TYPE_SPF:
|
|
|
|
copy->txt.items = dns_txt_item_copy(rr->txt.items);
|
|
|
|
if (!copy->txt.items)
|
|
|
|
return NULL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DNS_TYPE_A:
|
|
|
|
copy->a = rr->a;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DNS_TYPE_AAAA:
|
|
|
|
copy->aaaa = rr->aaaa;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DNS_TYPE_SOA:
|
|
|
|
copy->soa.mname = strdup(rr->soa.mname);
|
|
|
|
if (!copy->soa.mname)
|
|
|
|
return NULL;
|
|
|
|
copy->soa.rname = strdup(rr->soa.rname);
|
|
|
|
if (!copy->soa.rname)
|
|
|
|
return NULL;
|
|
|
|
copy->soa.serial = rr->soa.serial;
|
|
|
|
copy->soa.refresh = rr->soa.refresh;
|
|
|
|
copy->soa.retry = rr->soa.retry;
|
|
|
|
copy->soa.expire = rr->soa.expire;
|
|
|
|
copy->soa.minimum = rr->soa.minimum;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DNS_TYPE_MX:
|
|
|
|
copy->mx.priority = rr->mx.priority;
|
|
|
|
copy->mx.exchange = strdup(rr->mx.exchange);
|
|
|
|
if (!copy->mx.exchange)
|
|
|
|
return NULL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DNS_TYPE_LOC:
|
|
|
|
copy->loc = rr->loc;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DNS_TYPE_SSHFP:
|
|
|
|
copy->sshfp.algorithm = rr->sshfp.algorithm;
|
|
|
|
copy->sshfp.fptype = rr->sshfp.fptype;
|
|
|
|
copy->sshfp.fingerprint = memdup(rr->sshfp.fingerprint, rr->sshfp.fingerprint_size);
|
|
|
|
if (!copy->sshfp.fingerprint)
|
|
|
|
return NULL;
|
|
|
|
copy->sshfp.fingerprint_size = rr->sshfp.fingerprint_size;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DNS_TYPE_DNSKEY:
|
|
|
|
copy->dnskey.flags = rr->dnskey.flags;
|
|
|
|
copy->dnskey.protocol = rr->dnskey.protocol;
|
|
|
|
copy->dnskey.algorithm = rr->dnskey.algorithm;
|
|
|
|
copy->dnskey.key = memdup(rr->dnskey.key, rr->dnskey.key_size);
|
|
|
|
if (!copy->dnskey.key)
|
|
|
|
return NULL;
|
|
|
|
copy->dnskey.key_size = rr->dnskey.key_size;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DNS_TYPE_RRSIG:
|
|
|
|
copy->rrsig.type_covered = rr->rrsig.type_covered;
|
|
|
|
copy->rrsig.algorithm = rr->rrsig.algorithm;
|
|
|
|
copy->rrsig.labels = rr->rrsig.labels;
|
|
|
|
copy->rrsig.original_ttl = rr->rrsig.original_ttl;
|
|
|
|
copy->rrsig.expiration = rr->rrsig.expiration;
|
|
|
|
copy->rrsig.inception = rr->rrsig.inception;
|
|
|
|
copy->rrsig.key_tag = rr->rrsig.key_tag;
|
|
|
|
copy->rrsig.signer = strdup(rr->rrsig.signer);
|
|
|
|
if (!copy->rrsig.signer)
|
|
|
|
return NULL;
|
|
|
|
copy->rrsig.signature = memdup(rr->rrsig.signature, rr->rrsig.signature_size);
|
|
|
|
if (!copy->rrsig.signature)
|
|
|
|
return NULL;
|
|
|
|
copy->rrsig.signature_size = rr->rrsig.signature_size;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DNS_TYPE_NSEC:
|
|
|
|
copy->nsec.next_domain_name = strdup(rr->nsec.next_domain_name);
|
|
|
|
if (!copy->nsec.next_domain_name)
|
|
|
|
return NULL;
|
|
|
|
copy->nsec.types = bitmap_copy(rr->nsec.types);
|
|
|
|
if (!copy->nsec.types)
|
|
|
|
return NULL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DNS_TYPE_DS:
|
|
|
|
copy->ds.key_tag = rr->ds.key_tag;
|
|
|
|
copy->ds.algorithm = rr->ds.algorithm;
|
|
|
|
copy->ds.digest_type = rr->ds.digest_type;
|
|
|
|
copy->ds.digest = memdup(rr->ds.digest, rr->ds.digest_size);
|
|
|
|
if (!copy->ds.digest)
|
|
|
|
return NULL;
|
|
|
|
copy->ds.digest_size = rr->ds.digest_size;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DNS_TYPE_NSEC3:
|
|
|
|
copy->nsec3.algorithm = rr->nsec3.algorithm;
|
|
|
|
copy->nsec3.flags = rr->nsec3.flags;
|
|
|
|
copy->nsec3.iterations = rr->nsec3.iterations;
|
|
|
|
copy->nsec3.salt = memdup(rr->nsec3.salt, rr->nsec3.salt_size);
|
|
|
|
if (!copy->nsec3.salt)
|
|
|
|
return NULL;
|
|
|
|
copy->nsec3.salt_size = rr->nsec3.salt_size;
|
|
|
|
copy->nsec3.next_hashed_name = memdup(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size);
|
2020-11-03 18:06:47 +01:00
|
|
|
if (!copy->nsec3.next_hashed_name)
|
2016-06-20 21:24:46 +02:00
|
|
|
return NULL;
|
|
|
|
copy->nsec3.next_hashed_name_size = rr->nsec3.next_hashed_name_size;
|
|
|
|
copy->nsec3.types = bitmap_copy(rr->nsec3.types);
|
|
|
|
if (!copy->nsec3.types)
|
|
|
|
return NULL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DNS_TYPE_TLSA:
|
|
|
|
copy->tlsa.cert_usage = rr->tlsa.cert_usage;
|
|
|
|
copy->tlsa.selector = rr->tlsa.selector;
|
|
|
|
copy->tlsa.matching_type = rr->tlsa.matching_type;
|
|
|
|
copy->tlsa.data = memdup(rr->tlsa.data, rr->tlsa.data_size);
|
|
|
|
if (!copy->tlsa.data)
|
|
|
|
return NULL;
|
|
|
|
copy->tlsa.data_size = rr->tlsa.data_size;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DNS_TYPE_CAA:
|
|
|
|
copy->caa.flags = rr->caa.flags;
|
|
|
|
copy->caa.tag = strdup(rr->caa.tag);
|
|
|
|
if (!copy->caa.tag)
|
|
|
|
return NULL;
|
|
|
|
copy->caa.value = memdup(rr->caa.value, rr->caa.value_size);
|
|
|
|
if (!copy->caa.value)
|
|
|
|
return NULL;
|
|
|
|
copy->caa.value_size = rr->caa.value_size;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DNS_TYPE_OPT:
|
|
|
|
default:
|
|
|
|
copy->generic.data = memdup(rr->generic.data, rr->generic.data_size);
|
|
|
|
if (!copy->generic.data)
|
|
|
|
return NULL;
|
|
|
|
copy->generic.data_size = rr->generic.data_size;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2018-04-05 07:26:26 +02:00
|
|
|
t = TAKE_PTR(copy);
|
2016-06-20 21:24:46 +02:00
|
|
|
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
|
|
|
|
int dns_resource_record_clamp_ttl(DnsResourceRecord **rr, uint32_t max_ttl) {
|
|
|
|
DnsResourceRecord *old_rr, *new_rr;
|
|
|
|
uint32_t new_ttl;
|
|
|
|
|
|
|
|
assert(rr);
|
|
|
|
old_rr = *rr;
|
|
|
|
|
|
|
|
if (old_rr->key->type == DNS_TYPE_OPT)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
new_ttl = MIN(old_rr->ttl, max_ttl);
|
|
|
|
if (new_ttl == old_rr->ttl)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (old_rr->n_ref == 1) {
|
|
|
|
/* Patch in place */
|
|
|
|
old_rr->ttl = new_ttl;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
new_rr = dns_resource_record_copy(old_rr);
|
|
|
|
if (!new_rr)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
new_rr->ttl = new_ttl;
|
|
|
|
|
|
|
|
dns_resource_record_unref(*rr);
|
|
|
|
*rr = new_rr;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2015-11-20 19:01:43 +01:00
|
|
|
DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *i) {
|
|
|
|
DnsTxtItem *n;
|
|
|
|
|
|
|
|
if (!i)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
n = i->items_next;
|
|
|
|
|
|
|
|
free(i);
|
|
|
|
return dns_txt_item_free_all(n);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b) {
|
|
|
|
|
2015-12-09 17:28:50 +01:00
|
|
|
if (a == b)
|
|
|
|
return true;
|
|
|
|
|
2015-11-20 19:01:43 +01:00
|
|
|
if (!a != !b)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!a)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (a->length != b->length)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (memcmp(a->data, b->data, a->length) != 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return dns_txt_item_equal(a->items_next, b->items_next);
|
|
|
|
}
|
2015-12-02 22:56:04 +01:00
|
|
|
|
2016-06-20 21:24:46 +02:00
|
|
|
DnsTxtItem *dns_txt_item_copy(DnsTxtItem *first) {
|
|
|
|
DnsTxtItem *i, *copy = NULL, *end = NULL;
|
|
|
|
|
|
|
|
LIST_FOREACH(items, i, first) {
|
|
|
|
DnsTxtItem *j;
|
|
|
|
|
|
|
|
j = memdup(i, offsetof(DnsTxtItem, data) + i->length + 1);
|
|
|
|
if (!j) {
|
|
|
|
dns_txt_item_free_all(copy);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
LIST_INSERT_AFTER(items, copy, end, j);
|
|
|
|
end = j;
|
|
|
|
}
|
|
|
|
|
|
|
|
return copy;
|
|
|
|
}
|
|
|
|
|
2017-11-24 14:24:57 +01:00
|
|
|
int dns_txt_item_new_empty(DnsTxtItem **ret) {
|
|
|
|
DnsTxtItem *i;
|
|
|
|
|
|
|
|
/* RFC 6763, section 6.1 suggests to treat
|
|
|
|
* empty TXT RRs as equivalent to a TXT record
|
|
|
|
* with a single empty string. */
|
|
|
|
|
|
|
|
i = malloc0(offsetof(DnsTxtItem, data) + 1); /* for safety reasons we add an extra NUL byte */
|
|
|
|
if (!i)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
*ret = i;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-12-02 22:56:04 +01:00
|
|
|
static const char* const dnssec_algorithm_table[_DNSSEC_ALGORITHM_MAX_DEFINED] = {
|
2015-12-29 19:09:14 +01:00
|
|
|
/* Mnemonics as listed on https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml */
|
2015-12-02 22:56:04 +01:00
|
|
|
[DNSSEC_ALGORITHM_RSAMD5] = "RSAMD5",
|
|
|
|
[DNSSEC_ALGORITHM_DH] = "DH",
|
|
|
|
[DNSSEC_ALGORITHM_DSA] = "DSA",
|
|
|
|
[DNSSEC_ALGORITHM_ECC] = "ECC",
|
|
|
|
[DNSSEC_ALGORITHM_RSASHA1] = "RSASHA1",
|
|
|
|
[DNSSEC_ALGORITHM_DSA_NSEC3_SHA1] = "DSA-NSEC3-SHA1",
|
|
|
|
[DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1] = "RSASHA1-NSEC3-SHA1",
|
|
|
|
[DNSSEC_ALGORITHM_RSASHA256] = "RSASHA256",
|
|
|
|
[DNSSEC_ALGORITHM_RSASHA512] = "RSASHA512",
|
2015-12-29 19:09:14 +01:00
|
|
|
[DNSSEC_ALGORITHM_ECC_GOST] = "ECC-GOST",
|
|
|
|
[DNSSEC_ALGORITHM_ECDSAP256SHA256] = "ECDSAP256SHA256",
|
|
|
|
[DNSSEC_ALGORITHM_ECDSAP384SHA384] = "ECDSAP384SHA384",
|
2017-12-12 16:30:12 +01:00
|
|
|
[DNSSEC_ALGORITHM_ED25519] = "ED25519",
|
|
|
|
[DNSSEC_ALGORITHM_ED448] = "ED448",
|
2015-12-02 22:56:04 +01:00
|
|
|
[DNSSEC_ALGORITHM_INDIRECT] = "INDIRECT",
|
|
|
|
[DNSSEC_ALGORITHM_PRIVATEDNS] = "PRIVATEDNS",
|
|
|
|
[DNSSEC_ALGORITHM_PRIVATEOID] = "PRIVATEOID",
|
|
|
|
};
|
2016-01-02 22:12:13 +01:00
|
|
|
DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(dnssec_algorithm, int, 255);
|
2015-12-02 22:56:04 +01:00
|
|
|
|
|
|
|
static const char* const dnssec_digest_table[_DNSSEC_DIGEST_MAX_DEFINED] = {
|
2015-12-29 19:09:14 +01:00
|
|
|
/* Names as listed on https://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml */
|
|
|
|
[DNSSEC_DIGEST_SHA1] = "SHA-1",
|
|
|
|
[DNSSEC_DIGEST_SHA256] = "SHA-256",
|
|
|
|
[DNSSEC_DIGEST_GOST_R_34_11_94] = "GOST_R_34.11-94",
|
|
|
|
[DNSSEC_DIGEST_SHA384] = "SHA-384",
|
2015-12-02 22:56:04 +01:00
|
|
|
};
|
2016-01-02 22:12:13 +01:00
|
|
|
DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(dnssec_digest, int, 255);
|