summaryrefslogtreecommitdiff
path: root/src/resolve
diff options
context:
space:
mode:
Diffstat (limited to 'src/resolve')
-rw-r--r--src/resolve/dns-type.h3
-rw-r--r--src/resolve/resolve-tool.c69
-rw-r--r--src/resolve/resolved-dns-dnssec.c20
-rw-r--r--src/resolve/resolved-dns-packet.c452
-rw-r--r--src/resolve/resolved-dns-rr.c67
-rw-r--r--src/resolve/resolved-dns-rr.h10
-rw-r--r--src/resolve/test-data/_443._tcp.fedoraproject.org.pktsbin0 -> 169 bytes
-rw-r--r--src/resolve/test-data/_openpgpkey.fedoraproject.org.pktsbin0 -> 986 bytes
-rw-r--r--src/resolve/test-data/fake-caa.pktsbin0 -> 196 bytes
-rw-r--r--src/resolve/test-data/fedoraproject.org.pktsbin0 -> 1483 bytes
-rw-r--r--src/resolve/test-data/gandi.net.pktsbin0 -> 1010 bytes
-rw-r--r--src/resolve/test-data/google.com.pktsbin0 -> 747 bytes
-rw-r--r--src/resolve/test-data/kyhwana.org.pktsbin0 -> 1803 bytes
-rw-r--r--src/resolve/test-data/root.pktsbin0 -> 1061 bytes
-rw-r--r--src/resolve/test-data/sw1a1aa-sw1a2aa-sw1a2ab-sw1a2ac.find.me.uk.pktsbin0 -> 330 bytes
-rw-r--r--src/resolve/test-data/teamits.com.pktsbin0 -> 1021 bytes
-rw-r--r--src/resolve/test-data/zbyszek@fedoraproject.org.pktsbin0 -> 2533 bytes
-rw-r--r--src/resolve/test-dns-packet.c115
18 files changed, 451 insertions, 285 deletions
diff --git a/src/resolve/dns-type.h b/src/resolve/dns-type.h
index a6c1630021..f18ac6eef3 100644
--- a/src/resolve/dns-type.h
+++ b/src/resolve/dns-type.h
@@ -152,3 +152,6 @@ const char *tlsa_selector_to_string(uint8_t selector);
/* https://tools.ietf.org/html/draft-ietf-dane-protocol-23#section-7.4 */
const char *tlsa_matching_type_to_string(uint8_t selector);
+
+/* https://tools.ietf.org/html/rfc6844#section-5.1 */
+#define CAA_FLAG_CRITICAL (1u << 7)
diff --git a/src/resolve/resolve-tool.c b/src/resolve/resolve-tool.c
index 824cb267b5..9aade8e490 100644
--- a/src/resolve/resolve-tool.c
+++ b/src/resolve/resolve-tool.c
@@ -17,6 +17,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <gcrypt.h>
#include <getopt.h>
#include <net/if.h>
@@ -28,6 +29,7 @@
#include "bus-util.h"
#include "escape.h"
#include "in-addr-util.h"
+#include "gcrypt-util.h"
#include "parse-util.h"
#include "resolved-def.h"
#include "resolved-dns-packet.h"
@@ -46,6 +48,7 @@ static enum {
MODE_RESOLVE_HOST,
MODE_RESOLVE_RECORD,
MODE_RESOLVE_SERVICE,
+ MODE_RESOLVE_OPENPGP,
MODE_STATISTICS,
MODE_RESET_STATISTICS,
} arg_mode = MODE_RESOLVE_HOST;
@@ -545,15 +548,10 @@ static int resolve_rfc4501(sd_bus *bus, const char *name) {
} else
n = p;
- if (type == 0)
- type = arg_type;
- if (type == 0)
- type = DNS_TYPE_A;
-
if (class == 0)
- class = arg_class;
- if (class == 0)
- class = DNS_CLASS_IN;
+ class = arg_class ?: DNS_CLASS_IN;
+ if (type == 0)
+ type = arg_type ?: DNS_TYPE_A;
return resolve_record(bus, n, class, type);
@@ -763,6 +761,36 @@ static int resolve_service(sd_bus *bus, const char *name, const char *type, cons
return 0;
}
+static int resolve_openpgp(sd_bus *bus, const char *address) {
+ const char *domain, *full;
+ int r;
+ _cleanup_free_ char *hashed = NULL;
+
+ assert(bus);
+ assert(address);
+
+ domain = strrchr(address, '@');
+ if (!domain) {
+ log_error("Address does not contain '@': \"%s\"", address);
+ return -EINVAL;
+ } else if (domain == address || domain[1] == '\0') {
+ log_error("Address starts or ends with '@': \"%s\"", address);
+ return -EINVAL;
+ }
+ domain++;
+
+ r = string_hashsum(address, domain - 1 - address, GCRY_MD_SHA224, &hashed);
+ if (r < 0)
+ return log_error_errno(r, "Hashing failed: %m");
+
+ full = strjoina(hashed, "._openpgpkey.", domain);
+ log_debug("Looking up \"%s\".", full);
+
+ return resolve_record(bus, full,
+ arg_class ?: DNS_CLASS_IN,
+ arg_type ?: DNS_TYPE_OPENPGPKEY);
+}
+
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;
@@ -945,6 +973,7 @@ static void help(void) {
" --service Resolve service (SRV)\n"
" --service-address=BOOL Do [not] resolve address for services\n"
" --service-txt=BOOL Do [not] resolve TXT records for services\n"
+ " --openpgp Query OpenPGP public key\n"
" --cname=BOOL Do [not] follow CNAME redirects\n"
" --search=BOOL Do [not] use search domains\n"
" --legend=BOOL Do [not] print column headers and meta information\n"
@@ -961,6 +990,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_CNAME,
ARG_SERVICE_ADDRESS,
ARG_SERVICE_TXT,
+ ARG_OPENPGP,
ARG_SEARCH,
ARG_STATISTICS,
ARG_RESET_STATISTICS,
@@ -978,6 +1008,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "service", no_argument, NULL, ARG_SERVICE },
{ "service-address", required_argument, NULL, ARG_SERVICE_ADDRESS },
{ "service-txt", required_argument, NULL, ARG_SERVICE_TXT },
+ { "openpgp", no_argument, NULL, ARG_OPENPGP },
{ "search", required_argument, NULL, ARG_SEARCH },
{ "statistics", no_argument, NULL, ARG_STATISTICS, },
{ "reset-statistics", no_argument, NULL, ARG_RESET_STATISTICS },
@@ -1087,6 +1118,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_mode = MODE_RESOLVE_SERVICE;
break;
+ case ARG_OPENPGP:
+ arg_mode = MODE_RESOLVE_OPENPGP;
+ break;
+
case ARG_CNAME:
r = parse_boolean(optarg);
if (r < 0)
@@ -1246,6 +1281,24 @@ int main(int argc, char **argv) {
break;
+ case MODE_RESOLVE_OPENPGP:
+ if (argc < optind + 1) {
+ log_error("E-mail address required.");
+ r = -EINVAL;
+ goto finish;
+
+ }
+
+ r = 0;
+ while (optind < argc) {
+ int k;
+
+ k = resolve_openpgp(bus, argv[optind++]);
+ if (k < 0)
+ r = k;
+ }
+ break;
+
case MODE_STATISTICS:
if (argc > optind) {
log_error("Too many arguments.");
diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c
index 7aea9cb653..7098265929 100644
--- a/src/resolve/resolved-dns-dnssec.c
+++ b/src/resolve/resolved-dns-dnssec.c
@@ -23,6 +23,7 @@
#include "alloc-util.h"
#include "dns-domain.h"
+#include "gcrypt-util.h"
#include "hexdecoct.h"
#include "resolved-dns-dnssec.h"
#include "resolved-dns-packet.h"
@@ -126,19 +127,6 @@ int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) {
#ifdef HAVE_GCRYPT
-static void initialize_libgcrypt(void) {
- const char *p;
-
- if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
- return;
-
- p = gcry_check_version("1.4.5");
- assert(p);
-
- gcry_control(GCRYCTL_DISABLE_SECMEM);
- gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
-}
-
static int rr_compare(const void *a, const void *b) {
DnsResourceRecord **x = (DnsResourceRecord**) a, **y = (DnsResourceRecord**) b;
size_t m;
@@ -737,7 +725,7 @@ int dnssec_verify_rrset(
qsort_safe(list, n, sizeof(DnsResourceRecord*), rr_compare);
/* OK, the RRs are now in canonical order. Let's calculate the digest */
- initialize_libgcrypt();
+ initialize_libgcrypt(false);
hash_size = gcry_md_get_algo_dlen(md_algorithm);
assert(hash_size > 0);
@@ -1070,7 +1058,7 @@ int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds,
if (dnssec_keytag(dnskey, mask_revoke) != ds->ds.key_tag)
return 0;
- initialize_libgcrypt();
+ initialize_libgcrypt(false);
md_algorithm = digest_to_gcrypt_md(ds->ds.digest_type);
if (md_algorithm < 0)
@@ -1189,7 +1177,7 @@ int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) {
if (algorithm < 0)
return algorithm;
- initialize_libgcrypt();
+ initialize_libgcrypt(false);
hash_size = gcry_md_get_algo_dlen(algorithm);
assert(hash_size > 0);
diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c
index c940dd8929..c2fc1d8b05 100644
--- a/src/resolve/resolved-dns-packet.c
+++ b/src/resolve/resolved-dns-packet.c
@@ -28,6 +28,19 @@
#define EDNS0_OPT_DO (1<<15)
+typedef struct DnsPacketRewinder {
+ DnsPacket *packet;
+ size_t saved_rindex;
+} DnsPacketRewinder;
+
+static void rewind_dns_packet(DnsPacketRewinder *rewinder) {
+ if (rewinder->packet)
+ dns_packet_rewind(rewinder->packet, rewinder->saved_rindex);
+}
+
+#define INIT_REWINDER(rewinder, p) do { rewinder.packet = p; rewinder.saved_rindex = p->rindex; } while(0)
+#define CANCEL_REWINDER(rewinder) do { rewinder.packet = NULL; } while(0)
+
int dns_packet_new(DnsPacket **ret, DnsProtocol protocol, size_t mtu) {
DnsPacket *p;
size_t a;
@@ -431,8 +444,7 @@ int dns_packet_append_raw_string(DnsPacket *p, const void *s, size_t size, size_
((uint8_t*) d)[0] = (uint8_t) size;
- if (size > 0)
- memcpy(((uint8_t*) d) + 1, s, size);
+ memcpy_safe(((uint8_t*) d) + 1, s, size);
return 0;
}
@@ -1072,6 +1084,18 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
r = dns_packet_append_blob(p, rr->tlsa.data, rr->tlsa.data_size, NULL);
break;
+ case DNS_TYPE_CAA:
+ r = dns_packet_append_uint8(p, rr->caa.flags, NULL);
+ if (r < 0)
+ goto fail;
+
+ r = dns_packet_append_string(p, rr->caa.tag, NULL);
+ if (r < 0)
+ goto fail;
+
+ r = dns_packet_append_blob(p, rr->caa.value, rr->caa.value_size, NULL);
+ break;
+
case DNS_TYPE_OPT:
case DNS_TYPE_OPENPGPKEY:
case _DNS_TYPE_INVALID: /* unparseable */
@@ -1230,80 +1254,67 @@ int dns_packet_read_uint32(DnsPacket *p, uint32_t *ret, size_t *start) {
}
int dns_packet_read_string(DnsPacket *p, char **ret, size_t *start) {
- size_t saved_rindex;
+ _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
const void *d;
char *t;
uint8_t c;
int r;
assert(p);
-
- saved_rindex = p->rindex;
+ INIT_REWINDER(rewinder, p);
r = dns_packet_read_uint8(p, &c, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read(p, c, &d, NULL);
if (r < 0)
- goto fail;
+ return r;
- if (memchr(d, 0, c)) {
- r = -EBADMSG;
- goto fail;
- }
+ if (memchr(d, 0, c))
+ return -EBADMSG;
t = strndup(d, c);
- if (!t) {
- r = -ENOMEM;
- goto fail;
- }
+ if (!t)
+ return -ENOMEM;
if (!utf8_is_valid(t)) {
free(t);
- r = -EBADMSG;
- goto fail;
+ return -EBADMSG;
}
*ret = t;
if (start)
- *start = saved_rindex;
+ *start = rewinder.saved_rindex;
+ CANCEL_REWINDER(rewinder);
return 0;
-
-fail:
- dns_packet_rewind(p, saved_rindex);
- return r;
}
int dns_packet_read_raw_string(DnsPacket *p, const void **ret, size_t *size, size_t *start) {
- size_t saved_rindex;
+ _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
uint8_t c;
int r;
assert(p);
-
- saved_rindex = p->rindex;
+ INIT_REWINDER(rewinder, p);
r = dns_packet_read_uint8(p, &c, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read(p, c, ret, NULL);
if (r < 0)
- goto fail;
+ return r;
if (size)
*size = c;
if (start)
- *start = saved_rindex;
+ *start = rewinder.saved_rindex;
+ CANCEL_REWINDER(rewinder);
return 0;
-
-fail:
- dns_packet_rewind(p, saved_rindex);
- return r;
}
int dns_packet_read_name(
@@ -1312,7 +1323,8 @@ int dns_packet_read_name(
bool allow_compression,
size_t *start) {
- size_t saved_rindex, after_rindex = 0, jump_barrier;
+ _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
+ size_t after_rindex = 0, jump_barrier;
_cleanup_free_ char *ret = NULL;
size_t n = 0, allocated = 0;
bool first = true;
@@ -1320,19 +1332,18 @@ int dns_packet_read_name(
assert(p);
assert(_ret);
+ INIT_REWINDER(rewinder, p);
+ jump_barrier = p->rindex;
if (p->refuse_compression)
allow_compression = false;
- saved_rindex = p->rindex;
- jump_barrier = p->rindex;
-
for (;;) {
uint8_t c, d;
r = dns_packet_read_uint8(p, &c, NULL);
if (r < 0)
- goto fail;
+ return r;
if (c == 0)
/* End of name */
@@ -1343,12 +1354,10 @@ int dns_packet_read_name(
/* Literal label */
r = dns_packet_read(p, c, (const void**) &label, NULL);
if (r < 0)
- goto fail;
+ return r;
- if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) {
- r = -ENOMEM;
- goto fail;
- }
+ if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
+ return -ENOMEM;
if (first)
first = false;
@@ -1357,7 +1366,7 @@ int dns_packet_read_name(
r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX);
if (r < 0)
- goto fail;
+ return r;
n += r;
continue;
@@ -1367,13 +1376,11 @@ int dns_packet_read_name(
/* Pointer */
r = dns_packet_read_uint8(p, &d, NULL);
if (r < 0)
- goto fail;
+ return r;
ptr = (uint16_t) (c & ~0xc0) << 8 | (uint16_t) d;
- if (ptr < DNS_PACKET_HEADER_SIZE || ptr >= jump_barrier) {
- r = -EBADMSG;
- goto fail;
- }
+ if (ptr < DNS_PACKET_HEADER_SIZE || ptr >= jump_barrier)
+ return -EBADMSG;
if (after_rindex == 0)
after_rindex = p->rindex;
@@ -1381,16 +1388,12 @@ int dns_packet_read_name(
/* Jumps are limited to a "prior occurrence" (RFC-1035 4.1.4) */
jump_barrier = ptr;
p->rindex = ptr;
- } else {
- r = -EBADMSG;
- goto fail;
- }
+ } else
+ return -EBADMSG;
}
- if (!GREEDY_REALLOC(ret, allocated, n + 1)) {
- r = -ENOMEM;
- goto fail;
- }
+ if (!GREEDY_REALLOC(ret, allocated, n + 1))
+ return -ENOMEM;
ret[n] = 0;
@@ -1401,13 +1404,10 @@ int dns_packet_read_name(
ret = NULL;
if (start)
- *start = saved_rindex;
+ *start = rewinder.saved_rindex;
+ CANCEL_REWINDER(rewinder);
return 0;
-
-fail:
- dns_packet_rewind(p, saved_rindex);
- return r;
}
static int dns_packet_read_type_window(DnsPacket *p, Bitmap **types, size_t *start) {
@@ -1417,32 +1417,31 @@ static int dns_packet_read_type_window(DnsPacket *p, Bitmap **types, size_t *sta
uint8_t bit = 0;
unsigned i;
bool found = false;
- size_t saved_rindex;
+ _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
int r;
assert(p);
assert(types);
-
- saved_rindex = p->rindex;
+ INIT_REWINDER(rewinder, p);
r = bitmap_ensure_allocated(types);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_uint8(p, &window, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_uint8(p, &length, NULL);
if (r < 0)
- goto fail;
+ return r;
if (length == 0 || length > 32)
return -EBADMSG;
r = dns_packet_read(p, length, (const void **)&bitmap, NULL);
if (r < 0)
- goto fail;
+ return r;
for (i = 0; i < length; i++) {
uint8_t bitmask = 1 << 7;
@@ -1467,7 +1466,7 @@ static int dns_packet_read_type_window(DnsPacket *p, Bitmap **types, size_t *sta
r = bitmap_set(*types, n);
if (r < 0)
- goto fail;
+ return r;
}
bit ++;
@@ -1479,70 +1478,61 @@ static int dns_packet_read_type_window(DnsPacket *p, Bitmap **types, size_t *sta
return -EBADMSG;
if (start)
- *start = saved_rindex;
+ *start = rewinder.saved_rindex;
+ CANCEL_REWINDER(rewinder);
return 0;
-fail:
- dns_packet_rewind(p, saved_rindex);
- return r;
}
static int dns_packet_read_type_windows(DnsPacket *p, Bitmap **types, size_t size, size_t *start) {
- size_t saved_rindex;
+ _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
int r;
- saved_rindex = p->rindex;
+ INIT_REWINDER(rewinder, p);
- while (p->rindex < saved_rindex + size) {
+ while (p->rindex < rewinder.saved_rindex + size) {
r = dns_packet_read_type_window(p, types, NULL);
if (r < 0)
- goto fail;
+ return r;
/* don't read past end of current RR */
- if (p->rindex > saved_rindex + size) {
- r = -EBADMSG;
- goto fail;
- }
+ if (p->rindex > rewinder.saved_rindex + size)
+ return -EBADMSG;
}
- if (p->rindex != saved_rindex + size) {
- r = -EBADMSG;
- goto fail;
- }
+ if (p->rindex != rewinder.saved_rindex + size)
+ return -EBADMSG;
if (start)
- *start = saved_rindex;
+ *start = rewinder.saved_rindex;
+ CANCEL_REWINDER(rewinder);
return 0;
-fail:
- dns_packet_rewind(p, saved_rindex);
- return r;
}
int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, bool *ret_cache_flush, size_t *start) {
+ _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
_cleanup_free_ char *name = NULL;
bool cache_flush = false;
uint16_t class, type;
DnsResourceKey *key;
- size_t saved_rindex;
int r;
assert(p);
assert(ret);
-
- saved_rindex = p->rindex;
+ INIT_REWINDER(rewinder, p);
r = dns_packet_read_name(p, &name, true, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_uint16(p, &type, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_uint16(p, &class, NULL);
if (r < 0)
- goto fail;
+ return r;
if (p->protocol == DNS_PROTOCOL_MDNS) {
/* See RFC6762, Section 10.2 */
@@ -1554,10 +1544,8 @@ int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, bool *ret_cache_flus
}
key = dns_resource_key_new_consume(class, type, name);
- if (!key) {
- r = -ENOMEM;
- goto fail;
- }
+ if (!key)
+ return -ENOMEM;
name = NULL;
*ret = key;
@@ -1565,12 +1553,10 @@ int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, bool *ret_cache_flus
if (ret_cache_flush)
*ret_cache_flush = cache_flush;
if (start)
- *start = saved_rindex;
+ *start = rewinder.saved_rindex;
+ CANCEL_REWINDER(rewinder);
return 0;
-fail:
- dns_packet_rewind(p, saved_rindex);
- return r;
}
static bool loc_size_ok(uint8_t size) {
@@ -1582,7 +1568,8 @@ static bool loc_size_ok(uint8_t size) {
int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_flush, size_t *start) {
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
- size_t saved_rindex, offset;
+ _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder;
+ size_t offset;
uint16_t rdlength;
bool cache_flush;
int r;
@@ -1590,27 +1577,22 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
assert(p);
assert(ret);
- saved_rindex = p->rindex;
+ INIT_REWINDER(rewinder, p);
r = dns_packet_read_key(p, &key, &cache_flush, NULL);
if (r < 0)
- goto fail;
+ return r;
- if (!dns_class_is_valid_rr(key->class)||
- !dns_type_is_valid_rr(key->type)) {
- r = -EBADMSG;
- goto fail;
- }
+ if (!dns_class_is_valid_rr(key->class) || !dns_type_is_valid_rr(key->type))
+ return -EBADMSG;
rr = dns_resource_record_new(key);
- if (!rr) {
- r = -ENOMEM;
- goto fail;
- }
+ if (!rr)
+ return -ENOMEM;
r = dns_packet_read_uint32(p, &rr->ttl, NULL);
if (r < 0)
- goto fail;
+ return r;
/* RFC 2181, Section 8, suggests to
* treat a TTL with the MSB set as a zero TTL. */
@@ -1619,12 +1601,10 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
r = dns_packet_read_uint16(p, &rdlength, NULL);
if (r < 0)
- goto fail;
+ return r;
- if (p->rindex + rdlength > p->size) {
- r = -EBADMSG;
- goto fail;
- }
+ if (p->rindex + rdlength > p->size)
+ return -EBADMSG;
offset = p->rindex;
@@ -1633,13 +1613,13 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
case DNS_TYPE_SRV:
r = dns_packet_read_uint16(p, &rr->srv.priority, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_uint16(p, &rr->srv.weight, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_uint16(p, &rr->srv.port, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_name(p, &rr->srv.name, true, NULL);
break;
@@ -1653,7 +1633,7 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
case DNS_TYPE_HINFO:
r = dns_packet_read_string(p, &rr->hinfo.cpu, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_string(p, &rr->hinfo.os, NULL);
break;
@@ -1709,27 +1689,27 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
case DNS_TYPE_SOA:
r = dns_packet_read_name(p, &rr->soa.mname, true, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_name(p, &rr->soa.rname, true, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_uint32(p, &rr->soa.serial, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_uint32(p, &rr->soa.refresh, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_uint32(p, &rr->soa.retry, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_uint32(p, &rr->soa.expire, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_uint32(p, &rr->soa.minimum, NULL);
break;
@@ -1737,7 +1717,7 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
case DNS_TYPE_MX:
r = dns_packet_read_uint16(p, &rr->mx.priority, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_name(p, &rr->mx.exchange, true, NULL);
break;
@@ -1748,49 +1728,43 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
r = dns_packet_read_uint8(p, &t, &pos);
if (r < 0)
- goto fail;
+ return r;
if (t == 0) {
rr->loc.version = t;
r = dns_packet_read_uint8(p, &rr->loc.size, NULL);
if (r < 0)
- goto fail;
+ return r;
- if (!loc_size_ok(rr->loc.size)) {
- r = -EBADMSG;
- goto fail;
- }
+ if (!loc_size_ok(rr->loc.size))
+ return -EBADMSG;
r = dns_packet_read_uint8(p, &rr->loc.horiz_pre, NULL);
if (r < 0)
- goto fail;
+ return r;
- if (!loc_size_ok(rr->loc.horiz_pre)) {
- r = -EBADMSG;
- goto fail;
- }
+ if (!loc_size_ok(rr->loc.horiz_pre))
+ return -EBADMSG;
r = dns_packet_read_uint8(p, &rr->loc.vert_pre, NULL);
if (r < 0)
- goto fail;
+ return r;
- if (!loc_size_ok(rr->loc.vert_pre)) {
- r = -EBADMSG;
- goto fail;
- }
+ if (!loc_size_ok(rr->loc.vert_pre))
+ return -EBADMSG;
r = dns_packet_read_uint32(p, &rr->loc.latitude, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_uint32(p, &rr->loc.longitude, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_uint32(p, &rr->loc.altitude, NULL);
if (r < 0)
- goto fail;
+ return r;
break;
} else {
@@ -1803,122 +1777,114 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
case DNS_TYPE_DS:
r = dns_packet_read_uint16(p, &rr->ds.key_tag, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_uint8(p, &rr->ds.algorithm, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_uint8(p, &rr->ds.digest_type, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_memdup(p, rdlength - 4,
&rr->ds.digest, &rr->ds.digest_size,
NULL);
if (r < 0)
- goto fail;
+ return r;
- if (rr->ds.digest_size <= 0) {
+ if (rr->ds.digest_size <= 0)
/* the accepted size depends on the algorithm, but for now
just ensure that the value is greater than zero */
- r = -EBADMSG;
- goto fail;
- }
+ return -EBADMSG;
break;
case DNS_TYPE_SSHFP:
r = dns_packet_read_uint8(p, &rr->sshfp.algorithm, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_uint8(p, &rr->sshfp.fptype, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_memdup(p, rdlength - 2,
&rr->sshfp.fingerprint, &rr->sshfp.fingerprint_size,
NULL);
- if (rr->sshfp.fingerprint_size <= 0) {
+ if (rr->sshfp.fingerprint_size <= 0)
/* the accepted size depends on the algorithm, but for now
just ensure that the value is greater than zero */
- r = -EBADMSG;
- goto fail;
- }
+ return -EBADMSG;
break;
case DNS_TYPE_DNSKEY:
r = dns_packet_read_uint16(p, &rr->dnskey.flags, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_uint8(p, &rr->dnskey.protocol, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_uint8(p, &rr->dnskey.algorithm, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_memdup(p, rdlength - 4,
&rr->dnskey.key, &rr->dnskey.key_size,
NULL);
- if (rr->dnskey.key_size <= 0) {
+ if (rr->dnskey.key_size <= 0)
/* the accepted size depends on the algorithm, but for now
just ensure that the value is greater than zero */
- r = -EBADMSG;
- goto fail;
- }
+ return -EBADMSG;
break;
case DNS_TYPE_RRSIG:
r = dns_packet_read_uint16(p, &rr->rrsig.type_covered, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_uint8(p, &rr->rrsig.algorithm, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_uint8(p, &rr->rrsig.labels, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_uint32(p, &rr->rrsig.original_ttl, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_uint32(p, &rr->rrsig.expiration, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_uint32(p, &rr->rrsig.inception, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_uint16(p, &rr->rrsig.key_tag, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_name(p, &rr->rrsig.signer, false, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_memdup(p, offset + rdlength - p->rindex,
&rr->rrsig.signature, &rr->rrsig.signature_size,
NULL);
- if (rr->rrsig.signature_size <= 0) {
+ if (rr->rrsig.signature_size <= 0)
/* the accepted size depends on the algorithm, but for now
just ensure that the value is greater than zero */
- r = -EBADMSG;
- goto fail;
- }
+ return -EBADMSG;
break;
@@ -1933,11 +1899,9 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
r = dns_packet_read_name(p, &rr->nsec.next_domain_name, allow_compressed, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_type_windows(p, &rr->nsec.types, offset + rdlength - p->rindex, NULL);
- if (r < 0)
- goto fail;
/* We accept empty NSEC bitmaps. The bit indicating the presence of the NSEC record itself
* is redundant and in e.g., RFC4956 this fact is used to define a use for NSEC records
@@ -1950,41 +1914,39 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
r = dns_packet_read_uint8(p, &rr->nsec3.algorithm, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_uint8(p, &rr->nsec3.flags, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_uint16(p, &rr->nsec3.iterations, NULL);
if (r < 0)
- goto fail;
+ return r;
/* this may be zero */
r = dns_packet_read_uint8(p, &size, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_memdup(p, size, &rr->nsec3.salt, &rr->nsec3.salt_size, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_uint8(p, &size, NULL);
if (r < 0)
- goto fail;
+ return r;
- if (size <= 0) {
- r = -EBADMSG;
- goto fail;
- }
+ if (size <= 0)
+ return -EBADMSG;
- r = dns_packet_read_memdup(p, size, &rr->nsec3.next_hashed_name, &rr->nsec3.next_hashed_name_size, NULL);
+ r = dns_packet_read_memdup(p, size,
+ &rr->nsec3.next_hashed_name, &rr->nsec3.next_hashed_name_size,
+ NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_type_windows(p, &rr->nsec3.types, offset + rdlength - p->rindex, NULL);
- if (r < 0)
- goto fail;
/* empty non-terminals can have NSEC3 records, so empty bitmaps are allowed */
@@ -1994,25 +1956,39 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
case DNS_TYPE_TLSA:
r = dns_packet_read_uint8(p, &rr->tlsa.cert_usage, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_uint8(p, &rr->tlsa.selector, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_uint8(p, &rr->tlsa.matching_type, NULL);
if (r < 0)
- goto fail;
+ return r;
r = dns_packet_read_memdup(p, rdlength - 3,
&rr->tlsa.data, &rr->tlsa.data_size,
NULL);
- if (rr->tlsa.data_size <= 0) {
+
+ if (rr->tlsa.data_size <= 0)
/* the accepted size depends on the algorithm, but for now
just ensure that the value is greater than zero */
- r = -EBADMSG;
- goto fail;
- }
+ return -EBADMSG;
+
+ break;
+
+ case DNS_TYPE_CAA:
+ r = dns_packet_read_uint8(p, &rr->caa.flags, NULL);
+ if (r < 0)
+ return r;
+
+ r = dns_packet_read_string(p, &rr->caa.tag, NULL);
+ if (r < 0)
+ return r;
+
+ r = dns_packet_read_memdup(p,
+ rdlength + offset - p->rindex,
+ &rr->caa.value, &rr->caa.value_size, NULL);
break;
@@ -2021,16 +1997,13 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
default:
unparseable:
r = dns_packet_read_memdup(p, rdlength, &rr->generic.data, &rr->generic.data_size, NULL);
- if (r < 0)
- goto fail;
+
break;
}
if (r < 0)
- goto fail;
- if (p->rindex != offset + rdlength) {
- r = -EBADMSG;
- goto fail;
- }
+ return r;
+ if (p->rindex != offset + rdlength)
+ return -EBADMSG;
*ret = rr;
rr = NULL;
@@ -2038,12 +2011,10 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
if (ret_cache_flush)
*ret_cache_flush = cache_flush;
if (start)
- *start = saved_rindex;
+ *start = rewinder.saved_rindex;
+ CANCEL_REWINDER(rewinder);
return 0;
-fail:
- dns_packet_rewind(p, saved_rindex);
- return r;
}
static bool opt_is_good(DnsResourceRecord *rr, bool *rfc6975) {
@@ -2091,23 +2062,21 @@ static bool opt_is_good(DnsResourceRecord *rr, bool *rfc6975) {
int dns_packet_extract(DnsPacket *p) {
_cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
- size_t saved_rindex;
+ _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder = {};
unsigned n, i;
int r;
if (p->extracted)
return 0;
- saved_rindex = p->rindex;
+ INIT_REWINDER(rewinder, p);
dns_packet_rewind(p, DNS_PACKET_HEADER_SIZE);
n = DNS_PACKET_QDCOUNT(p);
if (n > 0) {
question = dns_question_new(n);
- if (!question) {
- r = -ENOMEM;
- goto finish;
- }
+ if (!question)
+ return -ENOMEM;
for (i = 0; i < n; i++) {
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
@@ -2115,21 +2084,17 @@ int dns_packet_extract(DnsPacket *p) {
r = dns_packet_read_key(p, &key, &cache_flush, NULL);
if (r < 0)
- goto finish;
+ return r;
- if (cache_flush) {
- r = -EBADMSG;
- goto finish;
- }
+ if (cache_flush)
+ return -EBADMSG;
- if (!dns_type_is_valid_query(key->type)) {
- r = -EBADMSG;
- goto finish;
- }
+ if (!dns_type_is_valid_query(key->type))
+ return -EBADMSG;
r = dns_question_add(question, key);
if (r < 0)
- goto finish;
+ return r;
}
}
@@ -2139,10 +2104,8 @@ int dns_packet_extract(DnsPacket *p) {
bool bad_opt = false;
answer = dns_answer_new(n);
- if (!answer) {
- r = -ENOMEM;
- goto finish;
- }
+ if (!answer)
+ return -ENOMEM;
for (i = 0; i < n; i++) {
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
@@ -2150,7 +2113,7 @@ int dns_packet_extract(DnsPacket *p) {
r = dns_packet_read_rr(p, &rr, &cache_flush, NULL);
if (r < 0)
- goto finish;
+ return r;
/* Try to reduce memory usage a bit */
if (previous)
@@ -2213,7 +2176,7 @@ int dns_packet_extract(DnsPacket *p) {
(i < DNS_PACKET_ANCOUNT(p) ? DNS_ANSWER_CACHEABLE : 0) |
(p->protocol == DNS_PROTOCOL_MDNS && !cache_flush ? DNS_ANSWER_SHARED_OWNER : 0));
if (r < 0)
- goto finish;
+ return r;
}
/* Remember this RR, so that we potentically can merge it's ->key object with the next RR. Note
@@ -2234,11 +2197,8 @@ int dns_packet_extract(DnsPacket *p) {
p->extracted = true;
- r = 0;
-
-finish:
- p->rindex = saved_rindex;
- return r;
+ /* no CANCEL, always rewind */
+ return 0;
}
int dns_packet_is_reply_for(DnsPacket *p, const DnsResourceKey *key) {
diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c
index 40f8e28dfd..6397005a68 100644
--- a/src/resolve/resolved-dns-rr.c
+++ b/src/resolve/resolved-dns-rr.c
@@ -22,6 +22,7 @@
#include "alloc-util.h"
#include "dns-domain.h"
#include "dns-type.h"
+#include "escape.h"
#include "hexdecoct.h"
#include "resolved-dns-dnssec.h"
#include "resolved-dns-packet.h"
@@ -490,6 +491,11 @@ DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr) {
free(rr->tlsa.data);
break;
+ case DNS_TYPE_CAA:
+ free(rr->caa.tag);
+ free(rr->caa.value);
+ break;
+
case DNS_TYPE_OPENPGPKEY:
default:
free(rr->generic.data);
@@ -697,6 +703,12 @@ int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecor
a->tlsa.matching_type == b->tlsa.matching_type &&
FIELD_EQUAL(a->tlsa, b->tlsa, data);
+ 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:
default:
return FIELD_EQUAL(a->generic, b->generic, data);
}
@@ -966,7 +978,7 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) {
case DNS_TYPE_DNSKEY: {
_cleanup_free_ char *alg = NULL;
char *ss;
- int n, n1;
+ int n;
uint16_t key_tag;
key_tag = dnssec_keytag(rr, true);
@@ -975,9 +987,8 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) {
if (r < 0)
return NULL;
- r = asprintf(&s, "%s %n%u %u %s %n",
+ r = asprintf(&s, "%s %u %u %s %n",
k,
- &n1,
rr->dnskey.flags,
rr->dnskey.protocol,
alg,
@@ -992,14 +1003,12 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) {
return NULL;
r = asprintf(&ss, "%s\n"
- "%*s-- Flags:%s%s%s\n"
- "%*s-- Key tag: %u",
+ " -- Flags:%s%s%s\n"
+ " -- Key tag: %u",
s,
- n1, "",
rr->dnskey.flags & DNSKEY_FLAG_SEP ? " SEP" : "",
rr->dnskey.flags & DNSKEY_FLAG_REVOKE ? " REVOKE" : "",
rr->dnskey.flags & DNSKEY_FLAG_ZONE_KEY ? " ZONE_KEY" : "",
- n1, "",
key_tag);
if (r < 0)
return NULL;
@@ -1125,13 +1134,13 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) {
return NULL;
r = asprintf(&ss, "%s\n"
- "%*s-- Cert. usage: %s\n"
- "%*s-- Selector: %s\n"
- "%*s-- Matching type: %s",
+ " -- Cert. usage: %s\n"
+ " -- Selector: %s\n"
+ " -- Matching type: %s",
s,
- n - 6, "", cert_usage,
- n - 6, "", selector,
- n - 6, "", matching_type);
+ cert_usage,
+ selector,
+ matching_type);
if (r < 0)
return NULL;
free(s);
@@ -1140,6 +1149,28 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) {
break;
}
+ case DNS_TYPE_CAA: {
+ _cleanup_free_ char *value;
+
+ value = octescape(rr->caa.value, rr->caa.value_size);
+ if (!value)
+ return NULL;
+
+ r = asprintf(&s, "%s %u %s \"%s\"%s%s%s%.0u",
+ k,
+ rr->caa.flags,
+ rr->caa.tag,
+ 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);
+ if (r < 0)
+ return NULL;
+
+ break;
+ }
+
case DNS_TYPE_OPENPGPKEY: {
int n;
@@ -1300,7 +1331,7 @@ int dns_resource_record_is_synthetic(DnsResourceRecord *rr) {
return !r;
}
-static void dns_resource_record_hash_func(const void *i, struct siphash *state) {
+void dns_resource_record_hash_func(const void *i, struct siphash *state) {
const DnsResourceRecord *rr = i;
assert(rr);
@@ -1427,7 +1458,13 @@ static void dns_resource_record_hash_func(const void *i, struct siphash *state)
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);
- siphash24_compress(&rr->tlsa.data, rr->tlsa.data_size, state);
+ siphash24_compress(rr->tlsa.data, rr->tlsa.data_size, state);
+ break;
+
+ case DNS_TYPE_CAA:
+ siphash24_compress(&rr->caa.flags, sizeof(rr->caa.flags), state);
+ string_hash_func(rr->caa.tag, state);
+ siphash24_compress(rr->caa.value, rr->caa.value_size, state);
break;
case DNS_TYPE_OPENPGPKEY:
diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h
index 2e0dfbaba3..23749790b4 100644
--- a/src/resolve/resolved-dns-rr.h
+++ b/src/resolve/resolved-dns-rr.h
@@ -249,6 +249,14 @@ struct DnsResourceRecord {
void *data;
size_t data_size;
} tlsa;
+
+ /* https://tools.ietf.org/html/rfc6844 */
+ struct {
+ uint8_t flags;
+ char *tag;
+ void *value;
+ size_t value_size;
+ } caa;
};
};
@@ -323,6 +331,8 @@ int dns_resource_record_is_synthetic(DnsResourceRecord *rr);
DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *i);
bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b);
+void dns_resource_record_hash_func(const void *i, struct siphash *state);
+
extern const struct hash_ops dns_resource_key_hash_ops;
extern const struct hash_ops dns_resource_record_hash_ops;
diff --git a/src/resolve/test-data/_443._tcp.fedoraproject.org.pkts b/src/resolve/test-data/_443._tcp.fedoraproject.org.pkts
new file mode 100644
index 0000000000..a383c6286d
--- /dev/null
+++ b/src/resolve/test-data/_443._tcp.fedoraproject.org.pkts
Binary files differ
diff --git a/src/resolve/test-data/_openpgpkey.fedoraproject.org.pkts b/src/resolve/test-data/_openpgpkey.fedoraproject.org.pkts
new file mode 100644
index 0000000000..15de02e997
--- /dev/null
+++ b/src/resolve/test-data/_openpgpkey.fedoraproject.org.pkts
Binary files differ
diff --git a/src/resolve/test-data/fake-caa.pkts b/src/resolve/test-data/fake-caa.pkts
new file mode 100644
index 0000000000..1c3ecc5491
--- /dev/null
+++ b/src/resolve/test-data/fake-caa.pkts
Binary files differ
diff --git a/src/resolve/test-data/fedoraproject.org.pkts b/src/resolve/test-data/fedoraproject.org.pkts
new file mode 100644
index 0000000000..17874844d9
--- /dev/null
+++ b/src/resolve/test-data/fedoraproject.org.pkts
Binary files differ
diff --git a/src/resolve/test-data/gandi.net.pkts b/src/resolve/test-data/gandi.net.pkts
new file mode 100644
index 0000000000..5ef51e0c8e
--- /dev/null
+++ b/src/resolve/test-data/gandi.net.pkts
Binary files differ
diff --git a/src/resolve/test-data/google.com.pkts b/src/resolve/test-data/google.com.pkts
new file mode 100644
index 0000000000..f98c4cd855
--- /dev/null
+++ b/src/resolve/test-data/google.com.pkts
Binary files differ
diff --git a/src/resolve/test-data/kyhwana.org.pkts b/src/resolve/test-data/kyhwana.org.pkts
new file mode 100644
index 0000000000..e28a725c9a
--- /dev/null
+++ b/src/resolve/test-data/kyhwana.org.pkts
Binary files differ
diff --git a/src/resolve/test-data/root.pkts b/src/resolve/test-data/root.pkts
new file mode 100644
index 0000000000..54ba668c75
--- /dev/null
+++ b/src/resolve/test-data/root.pkts
Binary files differ
diff --git a/src/resolve/test-data/sw1a1aa-sw1a2aa-sw1a2ab-sw1a2ac.find.me.uk.pkts b/src/resolve/test-data/sw1a1aa-sw1a2aa-sw1a2ab-sw1a2ac.find.me.uk.pkts
new file mode 100644
index 0000000000..a854249532
--- /dev/null
+++ b/src/resolve/test-data/sw1a1aa-sw1a2aa-sw1a2ab-sw1a2ac.find.me.uk.pkts
Binary files differ
diff --git a/src/resolve/test-data/teamits.com.pkts b/src/resolve/test-data/teamits.com.pkts
new file mode 100644
index 0000000000..11deb39677
--- /dev/null
+++ b/src/resolve/test-data/teamits.com.pkts
Binary files differ
diff --git a/src/resolve/test-data/zbyszek@fedoraproject.org.pkts b/src/resolve/test-data/zbyszek@fedoraproject.org.pkts
new file mode 100644
index 0000000000..f0a6f982df
--- /dev/null
+++ b/src/resolve/test-data/zbyszek@fedoraproject.org.pkts
Binary files differ
diff --git a/src/resolve/test-dns-packet.c b/src/resolve/test-dns-packet.c
new file mode 100644
index 0000000000..1abbd3fa2e
--- /dev/null
+++ b/src/resolve/test-dns-packet.c
@@ -0,0 +1,115 @@
+/***
+ This file is part of systemd
+
+ Copyright 2016 Zbigniew Jędrzejewski-Szmek
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <net/if.h>
+#include <glob.h>
+
+#include "alloc-util.h"
+#include "fileio.h"
+#include "glob-util.h"
+#include "log.h"
+#include "macro.h"
+#include "resolved-dns-packet.h"
+#include "resolved-dns-rr.h"
+#include "string-util.h"
+#include "strv.h"
+
+#define HASH_KEY SD_ID128_MAKE(d3,1e,48,90,4b,fa,4c,fe,af,9d,d5,a1,d7,2e,8a,b1)
+
+static uint64_t hash(DnsResourceRecord *rr) {
+ struct siphash state;
+
+ siphash24_init(&state, HASH_KEY.bytes);
+ dns_resource_record_hash_func(rr, &state);
+ return siphash24_finalize(&state);
+}
+
+static void test_packet_from_file(const char* filename, bool canonical) {
+ _cleanup_free_ char *data = NULL;
+ size_t data_size, packet_size, offset;
+
+ assert_se(read_full_file(filename, &data, &data_size) >= 0);
+ assert_se(data);
+ assert_se(data_size > 8);
+
+ log_info("============== %s %s==============", filename, canonical ? "canonical " : "");
+
+ for (offset = 0; offset < data_size; offset += 8 + packet_size) {
+ _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL, *p2 = NULL;
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL, *rr2 = NULL;
+ const char *s, *s2;
+ uint64_t hash1, hash2;
+
+ packet_size = le64toh( *(uint64_t*)(data + offset) );
+ assert_se(packet_size > 0);
+ assert_se(offset + 8 + packet_size <= data_size);
+
+ assert_se(dns_packet_new(&p, DNS_PROTOCOL_DNS, 0) >= 0);
+
+ assert_se(dns_packet_append_blob(p, data + offset + 8, packet_size, NULL) >= 0);
+ assert_se(dns_packet_read_rr(p, &rr, NULL, NULL) >= 0);
+
+ s = dns_resource_record_to_string(rr);
+ assert_se(s);
+ puts(s);
+
+ hash1 = hash(rr);
+
+ assert_se(dns_resource_record_to_wire_format(rr, canonical) >= 0);
+
+ assert_se(dns_packet_new(&p2, DNS_PROTOCOL_DNS, 0) >= 0);
+ assert_se(dns_packet_append_blob(p2, rr->wire_format, rr->wire_format_size, NULL) >= 0);
+ assert_se(dns_packet_read_rr(p2, &rr2, NULL, NULL) >= 0);
+
+ s2 = dns_resource_record_to_string(rr);
+ assert_se(s2);
+ assert_se(streq(s, s2));
+
+ hash2 = hash(rr);
+ assert_se(hash1 == hash2);
+ }
+}
+
+int main(int argc, char **argv) {
+ int i, N;
+ _cleanup_globfree_ glob_t g = {};
+ _cleanup_strv_free_ char **globs = NULL;
+ char **fnames;
+
+ log_parse_environment();
+
+ if (argc >= 2) {
+ N = argc - 1;
+ fnames = argv + 1;
+ } else {
+ assert_se(glob(RESOLVE_TEST_DIR "/*.pkts", GLOB_NOSORT, NULL, &g) == 0);
+ N = g.gl_pathc;
+ fnames = g.gl_pathv;
+ }
+
+ for (i = 0; i < N; i++) {
+ test_packet_from_file(fnames[i], false);
+ puts("");
+ test_packet_from_file(fnames[i], true);
+ if (i + 1 < N)
+ puts("");
+ }
+
+ return EXIT_SUCCESS;
+}