#include "dns-type.h"
#include "tests.h"
+static void test_to_json_from_json(DnsResourceRecord *rr) {
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *j = NULL;
+ ASSERT_OK(dns_resource_record_to_json(rr, &j));
+
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr2 = NULL;
+ ASSERT_OK(dns_resource_record_from_json(j, &rr2));
+
+ ASSERT_TRUE(dns_resource_record_equal(rr, rr2));
+}
+
/* ================================================================
* DNS_RESOURCE_RECORD_RDATA()
* ================================================================ */
ASSERT_EQ(rr->key->type, DNS_TYPE_A);
ASSERT_STREQ(dns_resource_key_name(rr->key), "www.example.com");
ASSERT_EQ(rr->a.in_addr.s_addr, addr.in.s_addr);
+
+ test_to_json_from_json(rr);
}
TEST(dns_resource_record_new_address_ipv6) {
ASSERT_EQ(rr->key->type, DNS_TYPE_AAAA);
ASSERT_STREQ(dns_resource_key_name(rr->key), "www.example.com");
ASSERT_EQ(memcmp(&rr->aaaa.in6_addr, &addr.in6, sizeof(struct in6_addr)), 0);
+
+ test_to_json_from_json(rr);
}
/* ================================================================
a = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_CNAME, "www.example.com");
ASSERT_NOT_NULL(a);
- a->cname.name = strdup("example.com");
+ a->cname.name = ASSERT_PTR(strdup("example.com"));
b = dns_resource_record_copy(a);
ASSERT_NOT_NULL(b);
ASSERT_TRUE(dns_resource_record_equal(a, b));
+
+ test_to_json_from_json(a);
}
TEST(dns_resource_record_equal_cname_fail) {
a = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_PTR, "127.1.168.192.in-addr-arpa");
ASSERT_NOT_NULL(a);
- a->ptr.name = strdup("example.com");
+ a->ptr.name = ASSERT_PTR(strdup("example.com"));
b = dns_resource_record_copy(a);
ASSERT_NOT_NULL(b);
ASSERT_TRUE(dns_resource_record_equal(a, b));
+
+ test_to_json_from_json(a);
}
TEST(dns_resource_record_equal_ptr_fail) {
ASSERT_EQ(orig->ttl, 3600u);
}
+static void test_from_json(const char *text, int expected) {
+ log_notice("Trying to parse as JSON RR: %s", text);
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *j = NULL;
+ ASSERT_OK(sd_json_parse(text, /* flags= */ 0, &j, /* reterr_line= */ NULL, /* reterr_column= */ NULL));
+ ASSERT_EQ(dns_resource_record_from_json(j, NULL), expected);
+}
+
+TEST(from_bad_json) {
+ test_from_json("{}", -EBADMSG);
+ test_from_json("{\"key\":{}}", -ENXIO);
+ test_from_json("{\"key\":{\"name\":\"foobar\",\"type\":9}}", -EOPNOTSUPP);
+ test_from_json("{\"key\":{\"name\":\"foobar\"}}", -ENXIO);
+ test_from_json("{\"key\":{\"type\":9}}", -ENXIO);
+ test_from_json("{\"key\":{\"name\":\"foobar\",\"type\":1}}", -ENXIO);
+ test_from_json("{\"key\":{\"name\":\"foobar\",\"type\":1},\"address\":[1,2,3,4]}", 0);
+ test_from_json("{\"key\":{\"name\":\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"type\":1},\"address\":[1,2,3,4]}", 0);
+ test_from_json("{\"key\":{\"name\":\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"type\":1},\"address\":[1,2,3,4]}", -EBADMSG);
+ test_from_json("{\"key\":{\"name\":\"a.a\",\"type\":1},\"address\":[1,2,3,4]}", 0);
+ test_from_json("{\"key\":{\"name\":\"a..a\",\"type\":1},\"address\":[1,2,3,4]}", -EBADMSG);
+}
+
DEFINE_TEST_MAIN(LOG_DEBUG);
int r;
assert(rr);
- assert(ret);
r = dns_resource_key_to_json(rr->key, &k);
if (r < 0)
default:
/* Can't provide broken-down format */
- *ret = NULL;
+ if (ret)
+ *ret = NULL;
return 0;
}
}
+int dns_resource_record_from_json(sd_json_variant *v, DnsResourceRecord **ret) {
+ int r;
+
+ assert(v);
+
+ sd_json_variant *k = sd_json_variant_by_key(v, "key");
+ if (!k)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Resource record entry lacks key field, refusing.");
+
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
+ r = dns_resource_key_from_json(k, &key);
+ if (r < 0)
+ return r;
+
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+ rr = dns_resource_record_new(key);
+ if (!rr)
+ return log_oom_debug();
+
+ /* Note, for now we only support the most common subset of RRs for decoding here. Please send patches for more. */
+ switch (key->type) {
+
+ case DNS_TYPE_PTR:
+ case DNS_TYPE_NS:
+ case DNS_TYPE_CNAME:
+ case DNS_TYPE_DNAME: {
+ _cleanup_free_ char *name = NULL;
+
+ static const struct sd_json_dispatch_field table[] = {
+ { "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, 0, SD_JSON_MANDATORY },
+ { "key", SD_JSON_VARIANT_OBJECT, NULL, 0, SD_JSON_MANDATORY },
+ {}
+ };
+
+ r = sd_json_dispatch(v, table, /* flags= */ 0, &name);
+ if (r < 0)
+ return r;
+
+ r = dns_name_is_valid(name);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EBADMSG;
+
+ rr->ptr.name = TAKE_PTR(name);
+ break;
+ }
+
+ case DNS_TYPE_A: {
+ struct in_addr addr = {};
+
+ static const struct sd_json_dispatch_field table[] = {
+ { "address", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_in_addr, 0, SD_JSON_MANDATORY },
+ { "key", SD_JSON_VARIANT_OBJECT, NULL, 0, SD_JSON_MANDATORY },
+ {}
+ };
+
+ r = sd_json_dispatch(v, table, /* flags= */ 0, &addr);
+ if (r < 0)
+ return r;
+
+ rr->a.in_addr = addr;
+ break;
+ }
+
+ case DNS_TYPE_AAAA: {
+ struct in6_addr addr = {};
+
+ static const struct sd_json_dispatch_field table[] = {
+ { "address", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_in6_addr, 0, SD_JSON_MANDATORY },
+ { "key", SD_JSON_VARIANT_OBJECT, NULL, 0, SD_JSON_MANDATORY },
+ {}
+ };
+
+ r = sd_json_dispatch(v, table, /* flags= */ 0, &addr);
+ if (r < 0)
+ return r;
+
+ rr->aaaa.in6_addr = addr;
+ break;
+ }
+
+ default:
+ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Decoding DNS type %s is currently not supported.", dns_type_to_string(key->type));
+ }
+
+ if (ret)
+ *ret = TAKE_PTR(rr);
+ return 0;
+}
+
static const char* const dnssec_algorithm_table[_DNSSEC_ALGORITHM_MAX_DEFINED] = {
/* Mnemonics as listed on https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml */
[DNSSEC_ALGORITHM_RSAMD5] = "RSAMD5",