2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-24 19:18:50 +00:00
bind/bin/named/builtin.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1290 lines
32 KiB
C
Raw Normal View History

/*
* 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.
*/
/*! \file
* \brief
* The built-in "version", "hostname", "id", "authors" and "empty" databases.
*/
#include <stdio.h>
#include <string.h>
#include <isc/lex.h>
#include <isc/mem.h>
#include <isc/result.h>
#include <isc/util.h>
#include <dns/callbacks.h>
#include <dns/dbiterator.h>
#include <dns/rdatalist.h>
#include <dns/rdatasetiter.h>
#include <dns/types.h>
#include <named/builtin.h>
#include <named/globals.h>
#include <named/os.h>
#include <named/server.h>
#define BDBNODE_MAGIC ISC_MAGIC('B', 'D', 'B', 'N')
#define VALID_BDBNODE(bdbl) ISC_MAGIC_VALID(bdbl, BDBNODE_MAGIC)
/*%
* Note that "impmagic" is not the first four bytes of the struct, so
* ISC_MAGIC_VALID cannot be used here.
*/
#define BDB_MAGIC ISC_MAGIC('B', 'D', 'B', '-')
#define VALID_BDB(bdb) ((bdb) != NULL && (bdb)->common.impmagic == BDB_MAGIC)
#define BDB_DNS64 0x00000001U
typedef struct bdbimplementation {
unsigned int flags;
dns_dbimplementation_t *dbimp;
} bdbimplementation_t;
typedef struct bdbnode bdbnode_t;
typedef struct bdb {
dns_db_t common;
bdbimplementation_t *implementation;
isc_result_t (*lookup)(bdbnode_t *node);
char *server;
char *contact;
} bdb_t;
struct bdbnode {
unsigned int magic;
isc_refcount_t references;
bdb_t *bdb;
ISC_LIST(dns_rdatalist_t) lists;
ISC_LIST(isc_buffer_t) buffers;
dns_name_t *name;
ISC_LINK(bdbnode_t) link;
dns_rdatacallbacks_t callbacks;
};
typedef struct bdb_rdatasetiter {
dns_rdatasetiter_t common;
dns_rdatalist_t *current;
} bdb_rdatasetiter_t;
static isc_result_t
findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
dns_rdatatype_t type, dns_rdatatype_t covers, isc_stdtime_t now,
dns_rdataset_t *rdataset,
dns_rdataset_t *sigrdataset DNS__DB_FLARG);
static void
attachnode(dns_db_t *db, dns_dbnode_t *source,
dns_dbnode_t **targetp DNS__DB_FLARG);
static void
detachnode(dns_db_t *db, dns_dbnode_t **nodep DNS__DB_FLARG);
/*
* Helper functions to convert text to wire forma.
*/
static isc_result_t
putrdata(bdbnode_t *node, dns_rdatatype_t typeval, dns_ttl_t ttl,
const unsigned char *rdatap, unsigned int rdlen) {
dns_rdatalist_t *rdatalist = NULL;
dns_rdata_t *rdata = NULL;
isc_buffer_t *rdatabuf = NULL;
isc_mem_t *mctx = NULL;
isc_region_t region;
mctx = node->bdb->common.mctx;
rdatalist = ISC_LIST_HEAD(node->lists);
while (rdatalist != NULL) {
if (rdatalist->type == typeval) {
break;
}
rdatalist = ISC_LIST_NEXT(rdatalist, link);
}
if (rdatalist == NULL) {
rdatalist = isc_mem_get(mctx, sizeof(dns_rdatalist_t));
dns_rdatalist_init(rdatalist);
rdatalist->rdclass = node->bdb->common.rdclass;
rdatalist->type = typeval;
rdatalist->ttl = ttl;
ISC_LIST_APPEND(node->lists, rdatalist, link);
} else if (rdatalist->ttl != ttl) {
return (DNS_R_BADTTL);
}
rdata = isc_mem_get(mctx, sizeof(dns_rdata_t));
isc_buffer_allocate(mctx, &rdatabuf, rdlen);
region.base = UNCONST(rdatap);
region.length = rdlen;
isc_buffer_copyregion(rdatabuf, &region);
isc_buffer_usedregion(rdatabuf, &region);
dns_rdata_init(rdata);
dns_rdata_fromregion(rdata, rdatalist->rdclass, rdatalist->type,
&region);
ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
ISC_LIST_APPEND(node->buffers, rdatabuf, link);
return (ISC_R_SUCCESS);
}
static isc_result_t
putrr(bdbnode_t *node, const char *type, dns_ttl_t ttl, const char *data) {
isc_result_t result;
dns_rdatatype_t typeval;
isc_lex_t *lex = NULL;
isc_mem_t *mctx = NULL;
const dns_name_t *origin = NULL;
isc_buffer_t *rb = NULL;
isc_buffer_t b;
REQUIRE(VALID_BDBNODE(node));
REQUIRE(type != NULL);
REQUIRE(data != NULL);
mctx = node->bdb->common.mctx;
origin = &node->bdb->common.origin;
isc_constregion_t r = { .base = type, .length = strlen(type) };
result = dns_rdatatype_fromtext(&typeval, (isc_textregion_t *)&r);
if (result != ISC_R_SUCCESS) {
return (result);
}
isc_lex_create(mctx, 64, &lex);
size_t datalen = strlen(data);
isc_buffer_constinit(&b, data, datalen);
isc_buffer_add(&b, datalen);
result = isc_lex_openbuffer(lex, &b);
if (result != ISC_R_SUCCESS) {
return (result);
}
isc_buffer_allocate(mctx, &rb, DNS_RDATA_MAXLENGTH);
result = dns_rdata_fromtext(NULL, node->bdb->common.rdclass, typeval,
lex, origin, 0, mctx, rb, &node->callbacks);
isc_lex_destroy(&lex);
if (result == ISC_R_SUCCESS) {
result = putrdata(node, typeval, ttl, isc_buffer_base(rb),
isc_buffer_usedlength(rb));
}
isc_buffer_free(&rb);
return (result);
}
/* Reasonable default SOA values */
#define DEFAULT_REFRESH 28800U /* 8 hours */
#define DEFAULT_RETRY 7200U /* 2 hours */
#define DEFAULT_EXPIRE 604800U /* 7 days */
#define DEFAULT_MINIMUM 86400U /* 1 day */
#define DEFAULT_TTL (60 * 60 * 24)
static isc_result_t
putsoa(bdbnode_t *node, const char *mname, const char *rname, uint32_t serial) {
char str[2 * DNS_NAME_MAXTEXT + 5 * (sizeof("2147483647")) + 7];
int n;
REQUIRE(mname != NULL);
REQUIRE(rname != NULL);
n = snprintf(str, sizeof(str), "%s %s %u %u %u %u %u", mname, rname,
serial, DEFAULT_REFRESH, DEFAULT_RETRY, DEFAULT_EXPIRE,
DEFAULT_MINIMUM);
if (n >= (int)sizeof(str) || n < 0) {
return (ISC_R_NOSPACE);
}
return (putrr(node, "SOA", DEFAULT_TTL, str));
}
static isc_result_t
puttxt(bdbnode_t *node, const char *text) {
unsigned char buf[256];
unsigned int len = strlen(text);
if (len > 255) {
len = 255; /* Silently truncate */
}
buf[0] = len;
memmove(&buf[1], text, len);
return (putrdata(node, dns_rdatatype_txt, 0, buf, len + 1));
}
/*
* Builtin database implementation functions.
*/
/* Precomputed HEX * 16 or 1 table. */
static const unsigned char hex16[256] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*00*/
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*10*/
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*20*/
0, 16, 32, 48, 64, 80, 96, 112, 128, 144, 1, 1, 1, 1, 1, 1, /*30*/
1, 160, 176, 192, 208, 224, 240, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*40*/
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*50*/
1, 160, 176, 192, 208, 224, 240, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*60*/
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*70*/
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*80*/
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*90*/
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*A0*/
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*B0*/
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*C0*/
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*D0*/
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*E0*/
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /*F0*/
};
static const unsigned char decimal[] = "0123456789";
static const unsigned char ipv4only[] = "\010ipv4only\004arpa";
static size_t
dns64_rdata(unsigned char *v, size_t start, unsigned char *rdata) {
size_t i, j = 0;
2012-08-24 07:32:50 +10:00
for (i = 0; i < 4U; i++) {
unsigned char c = v[start++];
2012-08-24 07:32:50 +10:00
if (start == 7U) {
start++;
}
if (c > 99) {
rdata[j++] = 3;
rdata[j++] = decimal[c / 100];
c = c % 100;
rdata[j++] = decimal[c / 10];
c = c % 10;
rdata[j++] = decimal[c];
} else if (c > 9) {
rdata[j++] = 2;
rdata[j++] = decimal[c / 10];
c = c % 10;
rdata[j++] = decimal[c];
} else {
rdata[j++] = 1;
rdata[j++] = decimal[c];
}
}
memmove(&rdata[j], "\07in-addr\04arpa", 14);
return (j + 14);
}
static isc_result_t
dns64_cname(const dns_name_t *zone, const dns_name_t *name, bdbnode_t *node) {
size_t zlen, nlen, j, len;
unsigned char v[16], n;
unsigned int i;
unsigned char rdata[sizeof("123.123.123.123.in-addr.arpa.")];
unsigned char *ndata;
/*
* The combined length of the zone and name is 74.
*
* The minimum zone length is 10 ((3)ip6(4)arpa(0)).
*
* The length of name should always be even as we are expecting
* a series of nibbles.
*/
zlen = zone->length;
nlen = name->length;
if ((zlen + nlen) > 74U || zlen < 10U || (nlen % 2) != 0U) {
return (ISC_R_NOTFOUND);
}
/*
* Check that name is a series of nibbles.
* Compute the byte values that correspond to the nibbles as we go.
*
* Shift the final result 4 bits, by setting 'i' to 1, if we if we
* have a odd number of nibbles so that "must be zero" tests below
* are byte aligned and we correctly return ISC_R_NOTFOUND or
* ISC_R_SUCCESS. We will not generate a CNAME in this case.
*/
ndata = name->ndata;
i = (nlen % 4) == 2U ? 1 : 0;
j = nlen;
memset(v, 0, sizeof(v));
2012-08-24 07:32:50 +10:00
while (j != 0U) {
INSIST((i / 2) < sizeof(v));
if (ndata[0] != 1) {
return (ISC_R_NOTFOUND);
}
n = hex16[ndata[1] & 0xff];
if (n == 1) {
return (ISC_R_NOTFOUND);
}
v[i / 2] = n | (v[i / 2] >> 4);
j -= 2;
ndata += 2;
i++;
}
/*
2011-01-07 23:47:07 +00:00
* If we get here then we know name only consisted of nibbles.
* Now we need to determine if the name exists or not and whether
* it corresponds to a empty node in the zone or there should be
* a CNAME.
*/
#define ZLEN(x) (10 + (x) / 2)
switch (zlen) {
case ZLEN(32): /* prefix len 32 */
/*
* The nibbles that map to this byte must be zero for 'name'
* to exist in the zone.
*/
if (nlen > 16U && v[(nlen - 1) / 4 - 4] != 0) {
return (ISC_R_NOTFOUND);
}
/*
* If the total length is not 74 then this is a empty node
* so return success.
*/
if (nlen + zlen != 74U) {
return (ISC_R_SUCCESS);
}
len = dns64_rdata(v, 8, rdata);
break;
case ZLEN(40): /* prefix len 40 */
/*
* The nibbles that map to this byte must be zero for 'name'
* to exist in the zone.
*/
if (nlen > 12U && v[(nlen - 1) / 4 - 3] != 0) {
return (ISC_R_NOTFOUND);
}
/*
* If the total length is not 74 then this is a empty node
* so return success.
*/
if (nlen + zlen != 74U) {
return (ISC_R_SUCCESS);
}
len = dns64_rdata(v, 6, rdata);
break;
case ZLEN(48): /* prefix len 48 */
/*
* The nibbles that map to this byte must be zero for 'name'
* to exist in the zone.
*/
if (nlen > 8U && v[(nlen - 1) / 4 - 2] != 0) {
return (ISC_R_NOTFOUND);
}
/*
* If the total length is not 74 then this is a empty node
* so return success.
*/
if (nlen + zlen != 74U) {
return (ISC_R_SUCCESS);
}
len = dns64_rdata(v, 5, rdata);
break;
case ZLEN(56): /* prefix len 56 */
/*
* The nibbles that map to this byte must be zero for 'name'
* to exist in the zone.
*/
if (nlen > 4U && v[(nlen - 1) / 4 - 1] != 0) {
return (ISC_R_NOTFOUND);
}
/*
* If the total length is not 74 then this is a empty node
* so return success.
*/
if (nlen + zlen != 74U) {
return (ISC_R_SUCCESS);
}
len = dns64_rdata(v, 4, rdata);
break;
case ZLEN(64): /* prefix len 64 */
/*
* The nibbles that map to this byte must be zero for 'name'
* to exist in the zone.
*/
if (v[(nlen - 1) / 4] != 0) {
return (ISC_R_NOTFOUND);
}
/*
* If the total length is not 74 then this is a empty node
* so return success.
*/
if (nlen + zlen != 74U) {
return (ISC_R_SUCCESS);
}
len = dns64_rdata(v, 3, rdata);
break;
case ZLEN(96): /* prefix len 96 */
/*
* If the total length is not 74 then this is a empty node
* so return success.
*/
if (nlen + zlen != 74U) {
return (ISC_R_SUCCESS);
}
len = dns64_rdata(v, 0, rdata);
break;
default:
/*
* This should never be reached unless someone adds a
* zone declaration with this internal type to named.conf.
*/
return (ISC_R_NOTFOUND);
}
/*
* Reverse of 192.0.0.170 or 192.0.0.171 maps to ipv4only.arpa.
*/
if ((v[0] == 170 || v[0] == 171) && v[1] == 0 && v[2] == 0 &&
2022-11-02 19:33:14 +01:00
v[3] == 192)
{
return (putrdata(node, dns_rdatatype_ptr, 3600, ipv4only,
sizeof(ipv4only)));
}
return (putrdata(node, dns_rdatatype_cname, 600, rdata,
(unsigned int)len));
}
static isc_result_t
builtin_lookup(bdb_t *bdb, const dns_name_t *name, bdbnode_t *node) {
if (name->labels == 0 && name->length == 0) {
return (bdb->lookup(node));
} else if ((node->bdb->implementation->flags & BDB_DNS64) != 0) {
return (dns64_cname(&bdb->common.origin, name, node));
} else {
return (ISC_R_NOTFOUND);
}
}
static isc_result_t
builtin_authority(bdb_t *bdb, bdbnode_t *node) {
isc_result_t result;
const char *contact = "hostmaster";
const char *server = "@";
if (bdb->server != NULL) {
server = bdb->server;
}
if (bdb->contact != NULL) {
contact = bdb->contact;
}
result = putsoa(node, server, contact, 0);
if (result != ISC_R_SUCCESS) {
return (ISC_R_FAILURE);
}
result = putrr(node, "NS", 0, server);
if (result != ISC_R_SUCCESS) {
return (ISC_R_FAILURE);
}
return (ISC_R_SUCCESS);
}
static isc_result_t
version_lookup(bdbnode_t *node) {
if (named_g_server->version_set) {
if (named_g_server->version == NULL) {
return (ISC_R_SUCCESS);
} else {
return (puttxt(node, named_g_server->version));
}
} else {
return (puttxt(node, PACKAGE_VERSION));
}
}
static isc_result_t
hostname_lookup(bdbnode_t *node) {
if (named_g_server->hostname_set) {
if (named_g_server->hostname == NULL) {
return (ISC_R_SUCCESS);
} else {
return (puttxt(node, named_g_server->hostname));
}
} else {
2002-01-25 03:12:07 +00:00
char buf[256];
if (gethostname(buf, sizeof(buf)) != 0) {
return (ISC_R_FAILURE);
}
return (puttxt(node, buf));
}
}
static isc_result_t
authors_lookup(bdbnode_t *node) {
isc_result_t result;
const char **p = NULL;
static const char *authors[] = {
2012-01-11 18:27:45 +00:00
"Mark Andrews", "Curtis Blackburn", "James Brister",
2012-01-21 19:44:18 +00:00
"Ben Cottrell", "John H. DuBois III", "Francis Dupont",
"Michael Graff", "Andreas Gustafsson", "Bob Halley",
"Evan Hunt", "JINMEI Tatuya", "Witold Krecicki",
"David Lawrence", "Scott Mann", "Danny Mayer",
"Damien Neil", "Matt Nelson", "Jeremy C. Reed",
"Michael Sawyer", "Brian Wellington", NULL
};
/*
* If a version string is specified, disable the authors.bind zone.
*/
if (named_g_server->version_set) {
return (ISC_R_SUCCESS);
}
for (p = authors; *p != NULL; p++) {
result = puttxt(node, *p);
if (result != ISC_R_SUCCESS) {
return (result);
}
}
return (ISC_R_SUCCESS);
}
static isc_result_t
id_lookup(bdbnode_t *node) {
if (named_g_server->sctx->usehostname) {
char buf[256];
if (gethostname(buf, sizeof(buf)) != 0) {
return (ISC_R_FAILURE);
}
return (puttxt(node, buf));
} else if (named_g_server->sctx->server_id != NULL) {
return (puttxt(node, named_g_server->sctx->server_id));
} else {
return (ISC_R_SUCCESS);
}
}
static isc_result_t
empty_lookup(bdbnode_t *node) {
UNUSED(node);
return (ISC_R_SUCCESS);
}
static isc_result_t
ipv4only_lookup(bdbnode_t *node) {
isc_result_t result;
unsigned char data[2][4] = { { 192, 0, 0, 170 }, { 192, 0, 0, 171 } };
for (int i = 0; i < 2; i++) {
result = putrdata(node, dns_rdatatype_a, 3600, data[i], 4);
if (result != ISC_R_SUCCESS) {
return (result);
}
}
return (ISC_R_SUCCESS);
}
static isc_result_t
ipv4reverse_lookup(bdbnode_t *node) {
isc_result_t result;
result = putrdata(node, dns_rdatatype_ptr, 3600, ipv4only,
sizeof(ipv4only));
return (result);
}
/*
* Rdataset implementation methods. An rdataset in the builtin databases is
* implemented as an rdatalist which holds a reference to the dbnode,
* to prevent the node being freed while the rdataset is still in use, so
* we need local implementations of clone and disassociate but the rest of
* the implementation can be the same as dns_rdatalist..
*/
static void
disassociate(dns_rdataset_t *rdataset DNS__DB_FLARG) {
Give the rdataset->privateN fields more helpful names BIND's rdataset structure is a view of some DNS records. It is polymorphic, so the details of how the records are stored can vary. For instance, the records can be held in an rdatalist, or in an rdataslab in the rbtdb. The dns_rdataset structure previously had a number of fields called `private1` up to `private7`, which were used by the various rdataset implementations. It was not at all clear what these fields were for, without reading the code and working it out from context. This change makes the rdataset inheritance hierarchy more clear. The polymorphic part of a `struct dns_rdataset` is now a union of structs, each of which is named for the class of implementation using it. The fields of these structs replace the old `privateN` fields. (Note: the term "inheritance hierarchy" refers to the fact that the builtin and SDLZ implementations are based on and inherit from the rdatalist implementation, which in turn inherits from the generic rdataset. Most of this change is mechanical, but there are a few extras. In keynode.c there were a number of REQUIRE()ments that were not necessary: they had already been checked by the rdataset method dispatch code. On the other hand, In ncache.c there was a public function which needed to REQUIRE() that an rdataset was valid. I have removed lots of "reset iterator state" comments, because it should now be clear from `target->iter = NULL` where before `target->private5 = NULL` could have been doing anything. Initialization is a bit neater in a few places, using C structure literals where appropriate. The pointer arithmetic for translating between an rdataslab header and its raw contents is now fractionally safer.
2023-04-28 01:12:39 +01:00
dns_dbnode_t *node = rdataset->rdlist.node;
bdbnode_t *bdbnode = (bdbnode_t *)node;
dns_db_t *db = (dns_db_t *)bdbnode->bdb;
detachnode(db, &node DNS__DB_FLARG_PASS);
dns_rdatalist_disassociate(rdataset DNS__DB_FLARG_PASS);
}
static void
rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target DNS__DB_FLARG) {
Give the rdataset->privateN fields more helpful names BIND's rdataset structure is a view of some DNS records. It is polymorphic, so the details of how the records are stored can vary. For instance, the records can be held in an rdatalist, or in an rdataslab in the rbtdb. The dns_rdataset structure previously had a number of fields called `private1` up to `private7`, which were used by the various rdataset implementations. It was not at all clear what these fields were for, without reading the code and working it out from context. This change makes the rdataset inheritance hierarchy more clear. The polymorphic part of a `struct dns_rdataset` is now a union of structs, each of which is named for the class of implementation using it. The fields of these structs replace the old `privateN` fields. (Note: the term "inheritance hierarchy" refers to the fact that the builtin and SDLZ implementations are based on and inherit from the rdatalist implementation, which in turn inherits from the generic rdataset. Most of this change is mechanical, but there are a few extras. In keynode.c there were a number of REQUIRE()ments that were not necessary: they had already been checked by the rdataset method dispatch code. On the other hand, In ncache.c there was a public function which needed to REQUIRE() that an rdataset was valid. I have removed lots of "reset iterator state" comments, because it should now be clear from `target->iter = NULL` where before `target->private5 = NULL` could have been doing anything. Initialization is a bit neater in a few places, using C structure literals where appropriate. The pointer arithmetic for translating between an rdataslab header and its raw contents is now fractionally safer.
2023-04-28 01:12:39 +01:00
dns_dbnode_t *node = source->rdlist.node;
bdbnode_t *bdbnode = (bdbnode_t *)node;
dns_db_t *db = (dns_db_t *)bdbnode->bdb;
dns_rdatalist_clone(source, target DNS__DB_FLARG_PASS);
Give the rdataset->privateN fields more helpful names BIND's rdataset structure is a view of some DNS records. It is polymorphic, so the details of how the records are stored can vary. For instance, the records can be held in an rdatalist, or in an rdataslab in the rbtdb. The dns_rdataset structure previously had a number of fields called `private1` up to `private7`, which were used by the various rdataset implementations. It was not at all clear what these fields were for, without reading the code and working it out from context. This change makes the rdataset inheritance hierarchy more clear. The polymorphic part of a `struct dns_rdataset` is now a union of structs, each of which is named for the class of implementation using it. The fields of these structs replace the old `privateN` fields. (Note: the term "inheritance hierarchy" refers to the fact that the builtin and SDLZ implementations are based on and inherit from the rdatalist implementation, which in turn inherits from the generic rdataset. Most of this change is mechanical, but there are a few extras. In keynode.c there were a number of REQUIRE()ments that were not necessary: they had already been checked by the rdataset method dispatch code. On the other hand, In ncache.c there was a public function which needed to REQUIRE() that an rdataset was valid. I have removed lots of "reset iterator state" comments, because it should now be clear from `target->iter = NULL` where before `target->private5 = NULL` could have been doing anything. Initialization is a bit neater in a few places, using C structure literals where appropriate. The pointer arithmetic for translating between an rdataslab header and its raw contents is now fractionally safer.
2023-04-28 01:12:39 +01:00
attachnode(db, node, &target->rdlist.node DNS__DB_FLARG_PASS);
}
static dns_rdatasetmethods_t bdb_rdataset_methods = {
.disassociate = disassociate,
.first = dns_rdatalist_first,
.next = dns_rdatalist_next,
.current = dns_rdatalist_current,
.clone = rdataset_clone,
.count = dns_rdatalist_count,
.addnoqname = dns_rdatalist_addnoqname,
.getnoqname = dns_rdatalist_getnoqname,
};
static void
new_rdataset(dns_rdatalist_t *rdatalist, dns_db_t *db, dns_dbnode_t *node,
dns_rdataset_t *rdataset) {
dns_rdatalist_tordataset(rdatalist, rdataset);
rdataset->methods = &bdb_rdataset_methods;
Give the rdataset->privateN fields more helpful names BIND's rdataset structure is a view of some DNS records. It is polymorphic, so the details of how the records are stored can vary. For instance, the records can be held in an rdatalist, or in an rdataslab in the rbtdb. The dns_rdataset structure previously had a number of fields called `private1` up to `private7`, which were used by the various rdataset implementations. It was not at all clear what these fields were for, without reading the code and working it out from context. This change makes the rdataset inheritance hierarchy more clear. The polymorphic part of a `struct dns_rdataset` is now a union of structs, each of which is named for the class of implementation using it. The fields of these structs replace the old `privateN` fields. (Note: the term "inheritance hierarchy" refers to the fact that the builtin and SDLZ implementations are based on and inherit from the rdatalist implementation, which in turn inherits from the generic rdataset. Most of this change is mechanical, but there are a few extras. In keynode.c there were a number of REQUIRE()ments that were not necessary: they had already been checked by the rdataset method dispatch code. On the other hand, In ncache.c there was a public function which needed to REQUIRE() that an rdataset was valid. I have removed lots of "reset iterator state" comments, because it should now be clear from `target->iter = NULL` where before `target->private5 = NULL` could have been doing anything. Initialization is a bit neater in a few places, using C structure literals where appropriate. The pointer arithmetic for translating between an rdataslab header and its raw contents is now fractionally safer.
2023-04-28 01:12:39 +01:00
dns_db_attachnode(db, node, &rdataset->rdlist.node);
}
/*
* Rdataset iterator methods
*/
static void
rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp DNS__DB_FLARG) {
bdb_rdatasetiter_t *bdbiterator = (bdb_rdatasetiter_t *)(*iteratorp);
detachnode(bdbiterator->common.db,
&bdbiterator->common.node DNS__DB_FLARG_PASS);
isc_mem_put(bdbiterator->common.db->mctx, bdbiterator,
sizeof(bdb_rdatasetiter_t));
*iteratorp = NULL;
}
static isc_result_t
rdatasetiter_first(dns_rdatasetiter_t *iterator DNS__DB_FLARG) {
bdb_rdatasetiter_t *bdbiterator = (bdb_rdatasetiter_t *)iterator;
bdbnode_t *bdbnode = (bdbnode_t *)iterator->node;
if (ISC_LIST_EMPTY(bdbnode->lists)) {
return (ISC_R_NOMORE);
}
bdbiterator->current = ISC_LIST_HEAD(bdbnode->lists);
return (ISC_R_SUCCESS);
}
static isc_result_t
rdatasetiter_next(dns_rdatasetiter_t *iterator DNS__DB_FLARG) {
bdb_rdatasetiter_t *bdbiterator = (bdb_rdatasetiter_t *)iterator;
bdbiterator->current = ISC_LIST_NEXT(bdbiterator->current, link);
if (bdbiterator->current == NULL) {
return (ISC_R_NOMORE);
} else {
return (ISC_R_SUCCESS);
}
}
static void
rdatasetiter_current(dns_rdatasetiter_t *iterator,
dns_rdataset_t *rdataset DNS__DB_FLARG) {
bdb_rdatasetiter_t *bdbiterator = (bdb_rdatasetiter_t *)iterator;
new_rdataset(bdbiterator->current, iterator->db, iterator->node,
rdataset);
}
static dns_rdatasetitermethods_t rdatasetiter_methods = {
rdatasetiter_destroy, rdatasetiter_first, rdatasetiter_next,
rdatasetiter_current
};
/*
* Database implementation methods
*/
static void
destroy(dns_db_t *db) {
bdb_t *bdb = (bdb_t *)db;
isc_refcount_destroy(&bdb->common.references);
if (bdb->server != NULL) {
isc_mem_free(named_g_mctx, bdb->server);
}
if (bdb->contact != NULL) {
isc_mem_free(named_g_mctx, bdb->contact);
}
bdb->common.magic = 0;
bdb->common.impmagic = 0;
dns_name_free(&bdb->common.origin, bdb->common.mctx);
isc_mem_putanddetach(&bdb->common.mctx, bdb, sizeof(bdb_t));
}
/*
* A dummy 'version' value is used so that dns_db_createversion()
* can return a non-NULL version to the caller, but there can only be
* one version of these databases, so the version value is never used.
*/
static int dummy;
static void
currentversion(dns_db_t *db, dns_dbversion_t **versionp) {
bdb_t *bdb = (bdb_t *)db;
REQUIRE(VALID_BDB(bdb));
*versionp = (void *)&dummy;
return;
}
static void
attachversion(dns_db_t *db, dns_dbversion_t *source,
dns_dbversion_t **targetp) {
bdb_t *bdb = (bdb_t *)db;
REQUIRE(VALID_BDB(bdb));
REQUIRE(source != NULL && source == (void *)&dummy);
REQUIRE(targetp != NULL && *targetp == NULL);
*targetp = source;
return;
}
static void
closeversion(dns_db_t *db, dns_dbversion_t **versionp,
bool commit DNS__DB_FLARG) {
bdb_t *bdb = (bdb_t *)db;
REQUIRE(VALID_BDB(bdb));
REQUIRE(versionp != NULL && *versionp == (void *)&dummy);
REQUIRE(!commit);
*versionp = NULL;
}
static isc_result_t
createnode(bdb_t *bdb, bdbnode_t **nodep) {
bdbnode_t *node = NULL;
REQUIRE(VALID_BDB(bdb));
node = isc_mem_get(bdb->common.mctx, sizeof(bdbnode_t));
*node = (bdbnode_t){
.lists = ISC_LIST_INITIALIZER,
.buffers = ISC_LIST_INITIALIZER,
.link = ISC_LINK_INITIALIZER,
};
dns_db_attach((dns_db_t *)bdb, (dns_db_t **)&node->bdb);
dns_rdatacallbacks_init(&node->callbacks);
isc_refcount_init(&node->references, 1);
node->magic = BDBNODE_MAGIC;
*nodep = node;
return (ISC_R_SUCCESS);
}
static void
destroynode(bdbnode_t *node) {
dns_rdatalist_t *list = NULL;
dns_rdata_t *rdata = NULL;
isc_buffer_t *b = NULL;
bdb_t *bdb = NULL;
isc_mem_t *mctx = NULL;
bdb = node->bdb;
mctx = bdb->common.mctx;
while (!ISC_LIST_EMPTY(node->lists)) {
list = ISC_LIST_HEAD(node->lists);
while (!ISC_LIST_EMPTY(list->rdata)) {
rdata = ISC_LIST_HEAD(list->rdata);
ISC_LIST_UNLINK(list->rdata, rdata, link);
isc_mem_put(mctx, rdata, sizeof(dns_rdata_t));
}
ISC_LIST_UNLINK(node->lists, list, link);
isc_mem_put(mctx, list, sizeof(dns_rdatalist_t));
}
while (!ISC_LIST_EMPTY(node->buffers)) {
b = ISC_LIST_HEAD(node->buffers);
ISC_LIST_UNLINK(node->buffers, b, link);
isc_buffer_free(&b);
}
if (node->name != NULL) {
dns_name_free(node->name, mctx);
isc_mem_put(mctx, node->name, sizeof(dns_name_t));
}
node->magic = 0;
isc_mem_put(mctx, node, sizeof(bdbnode_t));
dns_db_detach((dns_db_t **)(void *)&bdb);
}
static isc_result_t
getoriginnode(dns_db_t *db, dns_dbnode_t **nodep DNS__DB_FLARG) {
bdb_t *bdb = (bdb_t *)db;
bdbnode_t *node = NULL;
isc_result_t result;
dns_name_t relname;
dns_name_t *name = NULL;
REQUIRE(VALID_BDB(bdb));
REQUIRE(nodep != NULL && *nodep == NULL);
dns_name_init(&relname, NULL);
name = &relname;
result = createnode(bdb, &node);
if (result != ISC_R_SUCCESS) {
return (result);
}
result = builtin_lookup(bdb, name, node);
if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) {
destroynode(node);
return (result);
}
result = builtin_authority(bdb, node);
if (result != ISC_R_SUCCESS) {
destroynode(node);
return (result);
}
*nodep = node;
return (ISC_R_SUCCESS);
}
static isc_result_t
findnode(dns_db_t *db, const dns_name_t *name, bool create,
dns_dbnode_t **nodep DNS__DB_FLARG) {
bdb_t *bdb = (bdb_t *)db;
bdbnode_t *node = NULL;
isc_result_t result;
bool isorigin;
dns_name_t relname;
unsigned int labels;
REQUIRE(VALID_BDB(bdb));
REQUIRE(nodep != NULL && *nodep == NULL);
UNUSED(create);
isorigin = dns_name_equal(name, &bdb->common.origin);
labels = dns_name_countlabels(name) - dns_name_countlabels(&db->origin);
dns_name_init(&relname, NULL);
dns_name_getlabelsequence(name, 0, labels, &relname);
name = &relname;
result = createnode(bdb, &node);
if (result != ISC_R_SUCCESS) {
return (result);
}
result = builtin_lookup(bdb, name, node);
if (result != ISC_R_SUCCESS && (!isorigin || result != ISC_R_NOTFOUND))
{
destroynode(node);
return (result);
}
if (isorigin) {
result = builtin_authority(bdb, node);
if (result != ISC_R_SUCCESS) {
destroynode(node);
return (result);
}
}
*nodep = node;
return (ISC_R_SUCCESS);
}
static isc_result_t
find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version,
dns_rdatatype_t type, unsigned int options, isc_stdtime_t now,
dns_dbnode_t **nodep, dns_name_t *foundname, dns_rdataset_t *rdataset,
dns_rdataset_t *sigrdataset DNS__DB_FLARG) {
bdb_t *bdb = (bdb_t *)db;
isc_result_t result;
dns_dbnode_t *node = NULL;
dns_fixedname_t fname;
dns_rdataset_t xrdataset;
dns_name_t *xname = NULL;
unsigned int nlabels, olabels, i;
bool dns64;
REQUIRE(VALID_BDB(bdb));
REQUIRE(nodep == NULL || *nodep == NULL);
REQUIRE(version == NULL || version == (void *)&dummy);
if (!dns_name_issubdomain(name, &db->origin)) {
return (DNS_R_NXDOMAIN);
}
olabels = dns_name_countlabels(&db->origin);
nlabels = dns_name_countlabels(name);
xname = dns_fixedname_initname(&fname);
if (rdataset == NULL) {
dns_rdataset_init(&xrdataset);
rdataset = &xrdataset;
}
result = DNS_R_NXDOMAIN;
dns64 = ((bdb->implementation->flags & BDB_DNS64) != 0);
for (i = (dns64 ? nlabels : olabels); i <= nlabels; i++) {
/*
* Look up the next label.
*/
dns_name_getlabelsequence(name, nlabels - i, i, xname);
result = findnode(db, xname, false, &node DNS__DB_FLARG_PASS);
if (result == ISC_R_NOTFOUND) {
/*
* No data at zone apex?
*/
if (i == olabels) {
return (DNS_R_BADDB);
}
result = DNS_R_NXDOMAIN;
continue;
}
if (result != ISC_R_SUCCESS) {
return (result);
}
/*
* DNS64 zones don't have DNAME or NS records.
*/
if (dns64) {
goto skip;
}
/*
* Look for a DNAME at the current label, unless this is
* the qname.
*/
if (i < nlabels) {
result = findrdataset(
db, node, version, dns_rdatatype_dname, 0, now,
rdataset, sigrdataset DNS__DB_FLARG_PASS);
if (result == ISC_R_SUCCESS) {
result = DNS_R_DNAME;
break;
}
}
/*
* Look for an NS at the current label, unless this is the
* origin or glue is ok.
*/
if (i != olabels && (options & DNS_DBFIND_GLUEOK) == 0) {
result = findrdataset(
db, node, version, dns_rdatatype_ns, 0, now,
rdataset, sigrdataset DNS__DB_FLARG_PASS);
if (result == ISC_R_SUCCESS) {
if (i == nlabels && type == dns_rdatatype_any) {
result = DNS_R_ZONECUT;
dns_rdataset_disassociate(rdataset);
if (sigrdataset != NULL &&
dns_rdataset_isassociated(
sigrdataset))
{
dns_rdataset_disassociate(
sigrdataset);
}
} else {
result = DNS_R_DELEGATION;
}
break;
}
}
/*
* If the current name is not the qname, add another label
* and try again.
*/
if (i < nlabels) {
destroynode(node);
node = NULL;
continue;
}
skip:
/*
* If we're looking for ANY, we're done.
*/
if (type == dns_rdatatype_any) {
result = ISC_R_SUCCESS;
break;
}
/*
* Look for the qtype.
*/
result = findrdataset(db, node, version, type, 0, now, rdataset,
sigrdataset DNS__DB_FLARG_PASS);
if (result == ISC_R_SUCCESS) {
break;
}
/*
* Look for a CNAME.
*/
if (type != dns_rdatatype_cname) {
result = findrdataset(
db, node, version, dns_rdatatype_cname, 0, now,
rdataset, sigrdataset DNS__DB_FLARG_PASS);
if (result == ISC_R_SUCCESS) {
result = DNS_R_CNAME;
break;
}
}
result = DNS_R_NXRRSET;
break;
}
if (rdataset == &xrdataset && dns_rdataset_isassociated(rdataset)) {
dns_rdataset_disassociate(rdataset);
}
if (foundname != NULL) {
dns_name_copy(xname, foundname);
}
if (nodep != NULL) {
*nodep = node;
} else if (node != NULL) {
detachnode(db, &node DNS__DB_FLARG_PASS);
}
return (result);
}
static void
attachnode(dns_db_t *db, dns_dbnode_t *source,
dns_dbnode_t **targetp DNS__DB_FLARG) {
bdb_t *bdb = (bdb_t *)db;
bdbnode_t *node = (bdbnode_t *)source;
REQUIRE(VALID_BDB(bdb));
isc_refcount_increment(&node->references);
*targetp = source;
}
static void
detachnode(dns_db_t *db, dns_dbnode_t **nodep DNS__DB_FLARG) {
bdb_t *bdb = (bdb_t *)db;
bdbnode_t *node = NULL;
REQUIRE(VALID_BDB(bdb));
REQUIRE(nodep != NULL && *nodep != NULL);
node = (bdbnode_t *)(*nodep);
*nodep = NULL;
if (isc_refcount_decrement(&node->references) == 1) {
destroynode(node);
}
}
static isc_result_t
findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
dns_rdatatype_t type, dns_rdatatype_t covers, isc_stdtime_t now,
dns_rdataset_t *rdataset,
dns_rdataset_t *sigrdataset DNS__DB_FLARG) {
bdbnode_t *bdbnode = (bdbnode_t *)node;
dns_rdatalist_t *list = NULL;
REQUIRE(VALID_BDBNODE(bdbnode));
UNUSED(version);
UNUSED(covers);
UNUSED(now);
UNUSED(sigrdataset);
if (type == dns_rdatatype_rrsig) {
return (ISC_R_NOTIMPLEMENTED);
}
list = ISC_LIST_HEAD(bdbnode->lists);
while (list != NULL) {
if (list->type == type) {
break;
}
list = ISC_LIST_NEXT(list, link);
}
if (list == NULL) {
return (ISC_R_NOTFOUND);
}
new_rdataset(list, db, node, rdataset);
return (ISC_R_SUCCESS);
}
static isc_result_t
allrdatasets(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
unsigned int options, isc_stdtime_t now,
dns_rdatasetiter_t **iteratorp DNS__DB_FLARG) {
bdb_rdatasetiter_t *iterator = NULL;
REQUIRE(version == NULL || version == &dummy);
iterator = isc_mem_get(db->mctx, sizeof(bdb_rdatasetiter_t));
*iterator = (bdb_rdatasetiter_t){
.common.methods = &rdatasetiter_methods,
.common.db = db,
.common.version = version,
.common.options = options,
.common.now = now,
.common.magic = DNS_RDATASETITER_MAGIC,
};
attachnode(db, node, &iterator->common.node DNS__DB_FLARG_PASS);
*iteratorp = (dns_rdatasetiter_t *)iterator;
return (ISC_R_SUCCESS);
}
static dns_dbmethods_t bdb_methods = {
.destroy = destroy,
.currentversion = currentversion,
.attachversion = attachversion,
.closeversion = closeversion,
.attachnode = attachnode,
.detachnode = detachnode,
.findrdataset = findrdataset,
.allrdatasets = allrdatasets,
.getoriginnode = getoriginnode,
.findnode = findnode,
.find = find,
};
static isc_result_t
create(isc_mem_t *mctx, const dns_name_t *origin, dns_dbtype_t type,
dns_rdataclass_t rdclass, unsigned int argc, char *argv[],
void *implementation, dns_db_t **dbp) {
isc_result_t result;
bool needargs = false;
bdb_t *bdb = NULL;
REQUIRE(implementation != NULL);
if (type != dns_dbtype_zone) {
return (ISC_R_NOTIMPLEMENTED);
}
bdb = isc_mem_get(mctx, sizeof(*bdb));
*bdb = (bdb_t){
.common = { .methods = &bdb_methods, .rdclass = rdclass },
.implementation = implementation,
};
isc_refcount_init(&bdb->common.references, 1);
isc_mem_attach(mctx, &bdb->common.mctx);
dns_name_init(&bdb->common.origin, NULL);
dns_name_dupwithoffsets(origin, mctx, &bdb->common.origin);
INSIST(argc >= 1);
if (strcmp(argv[0], "authors") == 0) {
bdb->lookup = authors_lookup;
} else if (strcmp(argv[0], "hostname") == 0) {
bdb->lookup = hostname_lookup;
} else if (strcmp(argv[0], "id") == 0) {
bdb->lookup = id_lookup;
} else if (strcmp(argv[0], "version") == 0) {
bdb->lookup = version_lookup;
} else if (strcmp(argv[0], "dns64") == 0) {
needargs = true;
bdb->lookup = empty_lookup;
} else if (strcmp(argv[0], "empty") == 0) {
needargs = true;
bdb->lookup = empty_lookup;
} else if (strcmp(argv[0], "ipv4only") == 0) {
needargs = true;
bdb->lookup = ipv4only_lookup;
} else {
needargs = true;
bdb->lookup = ipv4reverse_lookup;
}
if (needargs) {
if (argc != 3) {
result = DNS_R_SYNTAX;
goto cleanup;
}
bdb->server = isc_mem_strdup(named_g_mctx, argv[1]);
bdb->contact = isc_mem_strdup(named_g_mctx, argv[2]);
} else if (argc != 1) {
result = DNS_R_SYNTAX;
goto cleanup;
}
bdb->common.magic = DNS_DB_MAGIC;
bdb->common.impmagic = BDB_MAGIC;
*dbp = (dns_db_t *)bdb;
return (ISC_R_SUCCESS);
cleanup:
dns_name_free(&bdb->common.origin, mctx);
if (bdb->server != NULL) {
isc_mem_free(named_g_mctx, bdb->server);
}
if (bdb->contact != NULL) {
isc_mem_free(named_g_mctx, bdb->contact);
}
isc_mem_putanddetach(&bdb->common.mctx, bdb, sizeof(bdb_t));
return (result);
}
/*
* Builtin database registration functions
*/
static bdbimplementation_t builtin = { .flags = 0 };
static bdbimplementation_t dns64 = { .flags = BDB_DNS64 };
isc_result_t
named_builtin_init(void) {
isc_result_t result;
result = dns_db_register("_builtin", create, &builtin, named_g_mctx,
&builtin.dbimp);
if (result != ISC_R_SUCCESS) {
return (result);
}
result = dns_db_register("_dns64", create, &dns64, named_g_mctx,
&dns64.dbimp);
if (result != ISC_R_SUCCESS) {
dns_db_unregister(&builtin.dbimp);
return (result);
}
return (ISC_R_SUCCESS);
}
void
named_builtin_deinit(void) {
if (builtin.dbimp != NULL) {
dns_db_unregister(&builtin.dbimp);
}
if (dns64.dbimp != NULL) {
dns_db_unregister(&dns64.dbimp);
}
}