/* * Copyright (C) Internet Systems Consortium, Inc. ("ISC") * * 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 http://mozilla.org/MPL/2.0/. * * See the COPYRIGHT file distributed with this work for additional * information regarding copyright ownership. */ /* ! \file */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dnstest.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef MAP_FILE #define MAP_FILE 0 #endif typedef struct data_holder { int len; const char *data; } data_holder_t; typedef struct rbt_testdata { const char *name; size_t name_len; data_holder_t data; } rbt_testdata_t; #define DATA_ITEM(name) { (name), sizeof(name) - 1, { sizeof(name), (name) } } rbt_testdata_t testdata[] = { DATA_ITEM("first.com."), DATA_ITEM("one.net."), DATA_ITEM("two.com."), DATA_ITEM("three.org."), DATA_ITEM("asdf.com."), DATA_ITEM("ghjkl.com."), DATA_ITEM("1.edu."), DATA_ITEM("2.edu."), DATA_ITEM("3.edu."), DATA_ITEM("123.edu."), DATA_ITEM("1236.com."), DATA_ITEM("and_so_forth.com."), DATA_ITEM("thisisalongname.com."), DATA_ITEM("a.b."), DATA_ITEM("test.net."), DATA_ITEM("whoknows.org."), DATA_ITEM("blargh.com."), DATA_ITEM("www.joe.com."), DATA_ITEM("test.com."), DATA_ITEM("isc.org."), DATA_ITEM("uiop.mil."), DATA_ITEM("last.fm."), { NULL, 0, { 0, NULL } } }; static void delete_data(void *data, void *arg) { UNUSED(arg); UNUSED(data); } static isc_result_t write_data(FILE *file, unsigned char *datap, void *arg, uint64_t *crc) { isc_result_t result; size_t ret = 0; data_holder_t *data = (data_holder_t *)datap; data_holder_t temp; off_t where; UNUSED(arg); REQUIRE(file != NULL); REQUIRE(crc != NULL); REQUIRE(data != NULL); REQUIRE((data->len == 0 && data->data == NULL) || (data->len != 0 && data->data != NULL)); result = isc_stdio_tell(file, &where); if (result != ISC_R_SUCCESS) return (result); temp = *data; temp.data = (data->len == 0 ? NULL : (char *)((uintptr_t)where + sizeof(data_holder_t))); isc_crc64_update(crc, (void *)&temp, sizeof(temp)); ret = fwrite(&temp, sizeof(data_holder_t), 1, file); if (ret != 1) return (ISC_R_FAILURE); if (data->len > 0) { isc_crc64_update(crc, (const void *)data->data, data->len); ret = fwrite(data->data, data->len, 1, file); if (ret != 1) return (ISC_R_FAILURE); } return (ISC_R_SUCCESS); } static isc_result_t fix_data(dns_rbtnode_t *p, void *base, size_t max, void *arg, uint64_t *crc) { data_holder_t *data = p->data; size_t size; UNUSED(base); UNUSED(max); UNUSED(arg); REQUIRE(crc != NULL); REQUIRE(p != NULL); if (data == NULL) printf("fixing data: data NULL\n"); else printf("fixing data: len %d, data %p\n", data->len, data->data); if (data == NULL || (data->len == 0 && data->data != NULL) || (data->len != 0 && data->data == NULL)) return (ISC_R_INVALIDFILE); size = max - ((char *)p - (char *)base); if (data->len > (int) size || data->data > (const char *) max) { printf("data invalid\n"); return (ISC_R_INVALIDFILE); } isc_crc64_update(crc, (void *)data, sizeof(*data)); data->data = (data->len == 0) ? NULL : (char *)data + sizeof(data_holder_t); if (data->len > 0) isc_crc64_update(crc, (const void *)data->data, data->len); return (ISC_R_SUCCESS); } /* * Load test data into the RBT. */ static void add_test_data(isc_mem_t *mymctx, dns_rbt_t *rbt) { char buffer[1024]; isc_buffer_t b; isc_result_t result; dns_fixedname_t fname; dns_name_t *name; dns_compress_t cctx; rbt_testdata_t *testdatap = testdata; dns_compress_init(&cctx, -1, mymctx); while (testdatap->name != NULL && testdatap->data.data != NULL) { memmove(buffer, testdatap->name, testdatap->name_len); isc_buffer_init(&b, buffer, testdatap->name_len); isc_buffer_add(&b, testdatap->name_len); name = dns_fixedname_initname(&fname); result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL); if (result != ISC_R_SUCCESS) { testdatap++; continue; } if (name != NULL) { result = dns_rbt_addname(rbt, name, &testdatap->data); ATF_CHECK_STREQ(dns_result_totext(result), "success"); } testdatap++; } dns_compress_invalidate(&cctx); } /* * Walk the tree and ensure that all the test nodes are present. */ static void check_test_data(dns_rbt_t *rbt) { char buffer[1024]; char *arg; dns_fixedname_t fname; dns_fixedname_t fixed; dns_name_t *name; isc_buffer_t b; data_holder_t *data; isc_result_t result; dns_name_t *foundname; rbt_testdata_t *testdatap = testdata; foundname = dns_fixedname_initname(&fixed); while (testdatap->name != NULL && testdatap->data.data != NULL) { memmove(buffer, testdatap->name, testdatap->name_len + 1); arg = buffer; isc_buffer_init(&b, arg, testdatap->name_len); isc_buffer_add(&b, testdatap->name_len); name = dns_fixedname_initname(&fname); result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL); if (result != ISC_R_SUCCESS) { testdatap++; continue; } data = NULL; result = dns_rbt_findname(rbt, name, 0, foundname, (void *) &data); ATF_CHECK_STREQ(dns_result_totext(result), "success"); testdatap++; } } static void data_printer(FILE *out, void *datap) { data_holder_t *data = (data_holder_t *)datap; fprintf(out, "%d bytes, %s", data->len, data->data); } ATF_TC(serialize); ATF_TC_HEAD(serialize, tc) { atf_tc_set_md_var(tc, "descr", "Test writing an rbt to file"); } ATF_TC_BODY(serialize, tc) { dns_rbt_t *rbt = NULL; isc_result_t result; FILE *rbtfile = NULL; dns_rbt_t *rbt_deserialized = NULL; off_t offset; int fd; off_t filesize = 0; char *base; UNUSED(tc); isc_mem_debugging = ISC_MEM_DEBUGRECORD; result = dns_test_begin(NULL, true); ATF_CHECK_STREQ(dns_result_totext(result), "success"); result = dns_rbt_create(mctx, delete_data, NULL, &rbt); ATF_CHECK_STREQ(dns_result_totext(result), "success"); add_test_data(mctx, rbt); dns_rbt_printtext(rbt, data_printer, stdout); /* * Serialize the tree. */ printf("serialization begins.\n"); rbtfile = fopen("./zone.bin", "w+b"); ATF_REQUIRE(rbtfile != NULL); result = dns_rbt_serialize_tree(rbtfile, rbt, write_data, NULL, &offset); ATF_REQUIRE(result == ISC_R_SUCCESS); dns_rbt_destroy(&rbt); /* * Deserialize the tree */ printf("deserialization begins.\n"); /* * Map in the whole file in one go */ fd = open("zone.bin", O_RDWR); isc_file_getsizefd(fd, &filesize); base = mmap(NULL, filesize, PROT_READ|PROT_WRITE, MAP_FILE|MAP_PRIVATE, fd, 0); ATF_REQUIRE(base != NULL && base != MAP_FAILED); close(fd); result = dns_rbt_deserialize_tree(base, filesize, 0, mctx, delete_data, NULL, fix_data, NULL, NULL, &rbt_deserialized); /* Test to make sure we have a valid tree */ ATF_REQUIRE(result == ISC_R_SUCCESS); if (rbt_deserialized == NULL) atf_tc_fail("deserialized rbt is null!"); /* Abort execution. */ check_test_data(rbt_deserialized); dns_rbt_printtext(rbt_deserialized, data_printer, stdout); dns_rbt_destroy(&rbt_deserialized); munmap(base, filesize); unlink("zone.bin"); dns_test_end(); } ATF_TC(deserialize_corrupt); ATF_TC_HEAD(deserialize_corrupt, tc) { atf_tc_set_md_var(tc, "descr", "Test reading a corrupt map file"); } ATF_TC_BODY(deserialize_corrupt, tc) { dns_rbt_t *rbt = NULL; isc_result_t result; FILE *rbtfile = NULL; off_t offset; int fd; off_t filesize = 0; char *base, *p, *q; uint32_t r; int i; UNUSED(tc); isc_mem_debugging = ISC_MEM_DEBUGRECORD; result = dns_test_begin(NULL, true); ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); /* Set up map file */ result = dns_rbt_create(mctx, delete_data, NULL, &rbt); ATF_CHECK_EQ(result, ISC_R_SUCCESS); add_test_data(mctx, rbt); rbtfile = fopen("./zone.bin", "w+b"); ATF_REQUIRE(rbtfile != NULL); result = dns_rbt_serialize_tree(rbtfile, rbt, write_data, NULL, &offset); ATF_REQUIRE(result == ISC_R_SUCCESS); dns_rbt_destroy(&rbt); /* Read back with random fuzzing */ for (i = 0; i < 256; i++) { dns_rbt_t *rbt_deserialized = NULL; fd = open("zone.bin", O_RDWR); isc_file_getsizefd(fd, &filesize); base = mmap(NULL, filesize, PROT_READ|PROT_WRITE, MAP_FILE|MAP_PRIVATE, fd, 0); ATF_REQUIRE(base != NULL && base != MAP_FAILED); close(fd); /* Randomly fuzz a portion of the memory */ isc_random_get(&r); p = base + (r % filesize); q = base + filesize; isc_random_get(&r); q -= (r % (q - p)); while (p++ < q) { isc_random_get(&r); *p = r & 0xff; } result = dns_rbt_deserialize_tree(base, filesize, 0, mctx, delete_data, NULL, fix_data, NULL, NULL, &rbt_deserialized); printf("%d: %s\n", i, isc_result_totext(result)); /* Test to make sure we have a valid tree */ ATF_REQUIRE(result == ISC_R_SUCCESS || result == ISC_R_INVALIDFILE); if (result != ISC_R_SUCCESS) ATF_REQUIRE(rbt_deserialized == NULL); if (rbt_deserialized != NULL) dns_rbt_destroy(&rbt_deserialized); munmap(base, filesize); } unlink("zone.bin"); dns_test_end(); } ATF_TC(serialize_align); ATF_TC_HEAD(serialize_align, tc) { atf_tc_set_md_var(tc, "descr", "Test the dns_rbt_serialize_align() function."); } ATF_TC_BODY(serialize_align, tc) { UNUSED(tc); ATF_CHECK(dns_rbt_serialize_align(0) == 0); ATF_CHECK(dns_rbt_serialize_align(1) == 8); ATF_CHECK(dns_rbt_serialize_align(2) == 8); ATF_CHECK(dns_rbt_serialize_align(3) == 8); ATF_CHECK(dns_rbt_serialize_align(4) == 8); ATF_CHECK(dns_rbt_serialize_align(5) == 8); ATF_CHECK(dns_rbt_serialize_align(6) == 8); ATF_CHECK(dns_rbt_serialize_align(7) == 8); ATF_CHECK(dns_rbt_serialize_align(8) == 8); ATF_CHECK(dns_rbt_serialize_align(9) == 16); ATF_CHECK(dns_rbt_serialize_align(0xff) == 0x100); ATF_CHECK(dns_rbt_serialize_align(0x301) == 0x308); } /* * Main */ ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, serialize); ATF_TP_ADD_TC(tp, deserialize_corrupt); ATF_TP_ADD_TC(tp, serialize_align); return (atf_no_error()); }