#include <stdbool.h>
+#include <isc/hex.h>
#include <isc/mem.h>
#include <isc/netaddr.h>
+#include <isc/parseint.h>
#include <isc/result.h>
#include <isc/string.h>
#include <isc/util.h>
isc_buffer_add(&buffer, len);
return dns_name_fromtext(name, &buffer, dns_rootname, 0);
}
+
+static isc_result_t
+parseptrnamev4(const dns_name_t *name, isc_netaddr_t *addr) {
+ isc_buffer_t b;
+ static unsigned char inaddrarpa_data[] = "\007IN-ADDR\004ARPA";
+ static dns_name_t const inaddrarpa =
+ DNS_NAME_INITABSOLUTE(inaddrarpa_data);
+
+ if (!dns_name_issubdomain(name, &inaddrarpa)) {
+ return ISC_R_FAILURE;
+ }
+
+ *addr = (isc_netaddr_t){ .family = AF_INET };
+ isc_buffer_init(&b, &addr->type.in, sizeof(addr->type.in));
+
+ /*
+ * Parse the IP address by extracting z y x w labels in reverse
+ * order to put the IP blocks in the right order.
+ */
+ for (int i = 3; i >= 0; i--) {
+ dns_label_t label;
+ char labelstr[4];
+ uint8_t block;
+
+ dns_name_getlabel(name, i, &label);
+ if (label.length > 4) {
+ return ISC_R_FAILURE;
+ }
+
+ /*
+ * Skip the first byte of the label as it encodes the length
+ * of the label (name wire format).
+ */
+ strncpy(labelstr, (char *)label.base + 1, label.length);
+ labelstr[label.length - 1] = 0;
+ if (isc_parse_uint8(&block, labelstr, 10) != ISC_R_SUCCESS) {
+ return ISC_R_FAILURE;
+ }
+
+ isc_buffer_putuint8(&b, block);
+ }
+
+ INSIST(isc_buffer_availablelength(&b) == 0);
+ return ISC_R_SUCCESS;
+}
+
+static isc_result_t
+parseptrnamev6(const dns_name_t *name, isc_netaddr_t *addr) {
+ isc_buffer_t b;
+ isc_hex_decodectx_t ctx;
+ static unsigned char ip6arpa_data[] = "\003IP6\004ARPA";
+ static dns_name_t const ip6arpa = DNS_NAME_INITABSOLUTE(ip6arpa_data);
+
+ if (!dns_name_issubdomain(name, &ip6arpa)) {
+ return ISC_R_FAILURE;
+ }
+
+ *addr = (isc_netaddr_t){ .family = AF_INET6 };
+ isc_buffer_init(&b, &addr->type.in6, sizeof(addr->type.in6));
+ isc_hex_decodeinit(&ctx, isc_buffer_length(&b), &b);
+
+ /*
+ * Parse the IP address by extracting labels in reverse order to
+ * put the IP blocks in the right order.
+ */
+ for (int i = 31; i >= 0; i--) {
+ dns_label_t label;
+
+ dns_name_getlabel(name, i, &label);
+ if (label.length != 2) {
+ return ISC_R_FAILURE;
+ }
+
+ /*
+ * First byte is the label length
+ */
+ if (isc_hex_decodechar(&ctx, label.base[1]) != ISC_R_SUCCESS) {
+ return ISC_R_FAILURE;
+ }
+ }
+
+ if (isc_hex_decodefinish(&ctx) != ISC_R_SUCCESS) {
+ return ISC_R_FAILURE;
+ }
+
+ INSIST(isc_buffer_availablelength(&b) == 0);
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t
+dns_byaddr_parseptrname(const dns_name_t *name, isc_netaddr_t *addr) {
+ int result;
+
+ REQUIRE(DNS_NAME_VALID(name));
+ REQUIRE(addr != NULL);
+ REQUIRE(dns_name_isabsolute(name));
+
+ switch (dns_name_countlabels(name)) {
+ case 7:
+ /* z.y.x.w.in-addr.arpa. has 7 labels */
+ result = parseptrnamev4(name, addr);
+ break;
+ case 35:
+ /*
+ * 0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0
+ * .0.0.0.0.0.0.0.0.0.ip6.arpa. has 35 labels
+ */
+ result = parseptrnamev6(name, addr);
+ break;
+ default:
+ result = ISC_R_FAILURE;
+ }
+
+ return result;
+}
--- /dev/null
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <sched.h> /* IWYU pragma: keep */
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define UNIT_TESTING
+#include <cmocka.h>
+
+#include <isc/lib.h>
+#include <isc/netaddr.h>
+
+#include <dns/byaddr.h>
+#include <dns/name.h>
+
+#include <tests/isc.h>
+
+ISC_RUN_TEST_IMPL(byaddr_parseptrname) {
+ struct {
+ const char *ptrname;
+ const char *address;
+ } tests[] = {
+ { "1.0.168.192.in-addr.arpa.", "192.168.0.1" },
+ { "ab.0.168.192.in-addr.arpa.", NULL },
+ { "abcd.0.168.192.in-addr.arpa.", NULL },
+ { "1111.0.168.192.in-addr.arpa.", NULL },
+ { "1.0.168.192.in-addr.arp.", NULL },
+ { "4.1.999.4.in-addr.arpa.", NULL },
+ { "e.f.a.c.3.2.1.0.e.f.a.c.7.6.5.4.1.1.1.1.0.0.0.0.0.0.0."
+ "0.1.2.e.f.ip6.arpa.",
+ "fe21::1111:4567:cafe:123:cafe" },
+ { "e.f.a.c.3.g.1.0.e.f.a.c.7.6.5.4.1.1.1.1.0.0.0.0.0.0.0."
+ "0.1.2.e.f.ip6.arpa.",
+ NULL },
+ { "e.f.a.c.3.ee.1.0.e.f.a.c.7.6.5.4.1.1.1.1.0.0.0.0.0.0.0."
+ "0.1.2.e.f.ip6.arpa.",
+ NULL },
+ { "e.f.a.c.3.2.1.0.e.f.a.c.7.6.5.4.1.1.1.1.0.0.0.0.0.0.0."
+ "0.1.2.e.f.ip6.arp.",
+ NULL },
+ { "a::z.ip6.arpa.", NULL },
+ { "ed.f.a.c.3.2.1.0.e.f.a.c.7.6.5.4.1.1.1.1.0.0.0.0.0.0.0."
+ "0.1.2.e.f.ip6.arpa.",
+ NULL },
+ { "1.0. . "
+ ".0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0."
+ "ip6.arpa.",
+ NULL },
+ };
+
+ for (size_t i = 0; i < ARRAY_SIZE(tests); i++) {
+ int result;
+ char bdata[128];
+ isc_buffer_t b;
+ isc_netaddr_t addr;
+ dns_name_t name;
+
+ isc_buffer_init(&b, bdata, sizeof(bdata));
+ dns_name_init(&name);
+ dns_name_setbuffer(&name, &b);
+ dns_name_fromstring(&name, tests[i].ptrname, NULL, 0, NULL);
+
+ result = dns_byaddr_parseptrname(&name, &addr);
+
+ if (tests[i].address) {
+ assert_int_equal(result, ISC_R_SUCCESS);
+ } else {
+ assert_int_not_equal(result, ISC_R_SUCCESS);
+ }
+
+ dns_name_invalidate(&name);
+ isc_buffer_clear(&b);
+ isc_netaddr_totext(&addr, &b);
+ isc_buffer_putuint8(&b, 0);
+
+ if (tests[i].address) {
+ result = strcmp(tests[i].address, b.base);
+ assert_int_equal(result, 0);
+ }
+ }
+}
+
+ISC_TEST_LIST_START
+ISC_TEST_ENTRY(byaddr_parseptrname)
+ISC_TEST_LIST_END
+ISC_TEST_MAIN