/*
* Copyright (C) 2016-2018 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/.
*/
/*! \file */
#include <config.h>
#include <isc/parseint.h>
#include <dns/dbiterator.h>
#include <dns/rdatasetiter.h>
/*%
* Single member zone in a catalog
*/
struct dns_catz_entry {
};
/*%
* Catalog zone
*/
struct dns_catz_zone {
/* key in entries is 'mhash', not domain name! */
/*
* defoptions are taken from named.conf
* zoneoptions are global options from zone
*/
};
static isc_result_t
dns_label_t *mhash);
static isc_result_t
/*%
* Collection of catalog zones for a view
*/
struct dns_catz_zones {
};
void
}
void
}
}
{
}
return (ISC_R_SUCCESS);
}
{
/* This option is always taken from config, so it's always 'default' */
return (ISC_R_SUCCESS);
}
{
return (ISC_R_NOMEMORY);
if (result != ISC_R_SUCCESS)
goto cleanup;
}
return (ISC_R_SUCCESS);
return (result);
}
}
{
if (result != ISC_R_SUCCESS)
return (result);
if (result != ISC_R_SUCCESS)
return (result);
}
void
}
void
unsigned int refs;
if (refs == 0) {
}
}
return (ISC_TRUE);
}
return (ISC_TRUE);
return (ISC_FALSE);
return (ISC_FALSE);
/* If one is NULL and the other isn't, the entries don't match */
return (ISC_FALSE);
/* If one is non-NULL, then they both are */
return (ISC_FALSE);
}
/* Repeat the above checks with allow_transfer */
return (ISC_FALSE);
return (ISC_FALSE);
}
return (ISC_TRUE);
}
}
return (&zone->defoptions);
}
void
}
/* TODO verify the new zone first! */
/* Copy zoneoptions from newzone into target. */
&target->zoneoptions);
&target->zoneoptions);
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_SUCCESS)
goto cleanup;
/*
* We can create those iterators now, even though toadd and tomod are
* empty
*/
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_SUCCESS)
goto cleanup;
/*
* First - walk the new zone and find all nodes that are not in the
* old zone, or are in both zones and are modified.
*/
result == ISC_R_SUCCESS;
{
unsigned char * key;
/*
* Spurious record that came from suboption without main
* record, removed.
* xxxwpk: make it a separate verification phase?
*/
continue;
}
"catz: iterating over '%s' from catalog '%s'",
if (result != ISC_R_SUCCESS) {
nentry);
if (result != ISC_R_SUCCESS)
"catz: error adding zone '%s' "
"from catalog '%s' - %s",
continue;
}
nentry);
if (result != ISC_R_SUCCESS)
"catz: error modifying zone '%s' "
"from catalog '%s' - %s",
}
}
/*
* Then - walk the old zone; only deleted entries should remain.
*/
result == ISC_R_SUCCESS;
{
"catz: deleting zone '%s' from catalog '%s' - %s",
}
/* At this moment target->entries has to be be empty. */
result == ISC_R_SUCCESS;
{
"catz: adding zone '%s' from catalog "
"'%s' - %s",
}
result == ISC_R_SUCCESS;
{
"catz: modifying zone '%s' from catalog "
"'%s' - %s",
}
return (result);
}
{
return (ISC_R_NOMEMORY);
if (result != ISC_R_SUCCESS)
goto cleanup_newzones;
if (result != ISC_R_SUCCESS)
goto cleanup_mutex;
if (result != ISC_R_SUCCESS)
goto cleanup_refcount;
if (result != ISC_R_SUCCESS)
goto cleanup_ht;
return (ISC_R_SUCCESS);
return (result);
}
void
/* Either it's a new one or it's being reconfigured. */
}
const dns_name_t *name)
{
return (ISC_R_NOMEMORY);
if (result != ISC_R_SUCCESS)
goto cleanup_newzone;
if (result != ISC_R_SUCCESS)
goto cleanup_name;
if (result != ISC_R_SUCCESS)
goto cleanup_ht;
return (ISC_R_SUCCESS);
return (result);
}
{
"catz: dns_catz_add_zone %s", zname);
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_SUCCESS) {
if (result != ISC_R_EXISTS)
goto cleanup;
}
if (result == ISC_R_EXISTS) {
}
return (result);
}
(void **) &found);
if (result != ISC_R_SUCCESS)
return (NULL);
return (found);
}
void
}
void
}
void
unsigned int refs;
if (refs == 0) {
result == ISC_R_SUCCESS;
{
}
/* The hashtable has to be empty now. */
}
}
}
}
void
unsigned int refs;
if (refs == 0) {
result == ISC_R_SUCCESS;)
{
}
}
}
}
typedef enum {
} catz_opt_t;
static isc_boolean_t
return (ISC_TRUE);
else
return (ISC_FALSE);
}
static catz_opt_t
return (CATZ_OPT_ZONES);
return (CATZ_OPT_MASTERS);
return (CATZ_OPT_ALLOW_QUERY);
return (CATZ_OPT_ALLOW_TRANSFER);
return (CATZ_OPT_VERSION);
else
return (CATZ_OPT_NONE);
}
static isc_result_t
{
return (ISC_R_FAILURE);
return (ISC_R_FAILURE);
else {
}
}
static isc_result_t
{
/*
* We only take -first- value, as mhash must be
* different.
*/
return (ISC_R_FAILURE);
if (result != ISC_R_SUCCESS)
return (ISC_R_FAILURE);
if (result == ISC_R_SUCCESS) {
/* We have a duplicate. */
return (ISC_R_FAILURE);
} else {
if (result != ISC_R_SUCCESS) {
return (result);
}
}
} else {
&entry);
if (result != ISC_R_SUCCESS) {
return (result);
}
if (result != ISC_R_SUCCESS) {
return (result);
}
}
return (ISC_R_SUCCESS);
}
static isc_result_t
char t[16];
return (ISC_R_FAILURE);
if (result != ISC_R_SUCCESS)
return (result);
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_NOMORE) {
goto cleanup;
}
goto cleanup;
}
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
return (result);
}
static isc_result_t
{
unsigned int rcount;
unsigned int i;
/*
* We have three possibilities here:
* - label and IN TXT - TSIG key name
*/
return (ISC_R_FAILURE);
/*
* We're pre-preparing the data once, we'll put it into
* the right spot in the masters array once we find it.
*/
case dns_rdatatype_a:
break;
case dns_rdatatype_aaaa:
break;
case dns_rdatatype_txt:
if (result != ISC_R_SUCCESS)
return (result);
if (result != ISC_R_SUCCESS)
return (result);
if (result != ISC_R_NOMORE)
return (ISC_R_FAILURE);
/* rdatastr.length < DNS_NAME_MAXTEXT */
return (ISC_R_NOMEMORY);
dns_name_init(keyname, 0);
if (result != ISC_R_SUCCESS) {
return (result);
}
break;
default:
return (ISC_R_FAILURE);
}
/*
* We have to find the appropriate labeled record in masters
* if it exists.
* In common case we'll have no more than 3-4 records here so
* no optimization.
*/
break;
}
sizeof(isc_sockaddr_t));
} else {
i+1);
if (result != ISC_R_SUCCESS) {
return (result);
}
sizeof(dns_name_t));
}
return (ISC_R_NOMEMORY);
}
if (result != ISC_R_SUCCESS) {
sizeof(dns_name_t));
}
return (result);
}
sizeof(isc_sockaddr_t));
}
return (ISC_R_SUCCESS);
}
/* else - 'simple' case - without labels */
return (ISC_R_FAILURE);
if (result != ISC_R_SUCCESS) {
return (result);
}
result == ISC_R_SUCCESS;
{
/*
* port 0 == take the default
*/
} else {
&rdata_aaaa.in6_addr, 0);
}
}
return (ISC_R_SUCCESS);
}
static isc_result_t
{
return (ISC_R_FAILURE);
"catz: more than one APL entry for member zone, "
"result is undefined");
}
if (result != ISC_R_SUCCESS)
return (result);
if (result != ISC_R_SUCCESS)
goto cleanup;
result == ISC_R_SUCCESS;
else
continue; /* xxxwpk log it or simply ignore? */
}
}
if (result == ISC_R_NOMORE)
else
goto cleanup;
return (result);
}
static isc_result_t
{
return (ISC_R_FAILURE);
/*
* We're adding this entry now, in case the option is invalid we'll get
* rid of it in verification phase.
*/
(void **) &entry);
if (result != ISC_R_SUCCESS) {
if (result != ISC_R_SUCCESS)
return (result);
entry);
if (result != ISC_R_SUCCESS) {
return (result);
}
}
switch (opt) {
case CATZ_OPT_MASTERS:
&prefix));
case CATZ_OPT_ALLOW_QUERY:
return (ISC_R_FAILURE);
value));
case CATZ_OPT_ALLOW_TRANSFER:
return (ISC_R_FAILURE);
value));
default:
return (ISC_R_FAILURE);
}
return (ISC_R_FAILURE);
}
static isc_result_t
{
switch (opt) {
case CATZ_OPT_ZONES:
case CATZ_OPT_MASTERS:
case CATZ_OPT_ALLOW_QUERY:
return (ISC_R_FAILURE);
rdataset));
case CATZ_OPT_ALLOW_TRANSFER:
return (ISC_R_FAILURE);
return (catz_process_apl(zone,
rdataset));
case CATZ_OPT_VERSION:
return (ISC_R_FAILURE);
default:
return (ISC_R_FAILURE);
}
}
{
int order;
unsigned int nlabels;
if (nrres == dns_namereln_equal) {
if (result != ISC_R_SUCCESS)
return (result);
/*
* xxxwpk TODO do we want to save something from SOA?
*/
return (result);
return (ISC_R_SUCCESS);
} else {
return (ISC_R_UNEXPECTED);
}
} else if (nrres != dns_namereln_subdomain) {
return (ISC_R_UNEXPECTED);
}
return (result);
}
{
isc_region_t r;
if (result != ISC_R_SUCCESS)
return (result);
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_SUCCESS)
goto cleanup;
/* __catz__<digest>.db */
/* optionally prepend with <zonedir>/ */
if (result != ISC_R_SUCCESS)
goto cleanup;
}
isc_buffer_usedregion(tbuf, &r);
/* we can do that because digest string < 2 * DNS_NAME */
} else {
isc_buffer_copyregion(*buffer, &r);
}
return (result);
}
isc_buffer_t **buf)
{
/*
* We have to generate a text buffer with regular zone config:
* zone foo.bar {
* type slave;
* masters [ dscp X ] { ip1 port port1; ip2 port port2; };
* }
*/
isc_uint32_t i;
/*
* The buffer will be reallocated if something won't fit,
* ISC_BUFFER_INCR seems like a good start.
*/
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
/*
* DSCP value has no default, but when it is specified, it is identical
* for all masters and cannot be overriden for a specific master IP, so
* use the DSCP value set for the first master
*/
}
/*
* Every master must have an IP address assigned.
*/
case AF_INET:
case AF_INET6:
break;
default:
"catz: zone '%s' uses an invalid master "
"(no IP address assigned)",
zname);
goto cleanup;
}
if (result != ISC_R_SUCCESS)
goto cleanup;
}
}
if (result != ISC_R_SUCCESS)
goto cleanup;
}
}
}
return (ISC_R_SUCCESS);
return (result);
}
void
(void) task;
}
isc_region_t r;
if (result != ISC_R_SUCCESS)
goto cleanup;
/* New zone came as AXFR */
/*
* We're not registering db update callback, it will be
* registered at the end of update_from_db
*/
}
isc_time_now(&now);
"catz: new zone version came too soon, "
"deferring update");
(unsigned int)tdiff, 0);
if (result != ISC_R_SUCCESS)
goto cleanup;
} else {
}
} else {
"catz: update already queued");
}
return (result);
}
void
isc_region_t r;
/*
* Create a new catz in the same context as current catz.
*/
if (result != ISC_R_SUCCESS) {
/* This can happen if we remove the zone in the meantime. */
"catz: zone '%s' not in config",
bname);
return;
}
if (result != ISC_R_SUCCESS) {
/* A zone without SOA record?!? */
"catz: zone '%s' has no SOA record (%s)",
return;
}
"catz: updating catalog zone '%s' with serial %d",
if (result != ISC_R_SUCCESS) {
"catz: failed to create new zone - %s",
return;
}
if (result != ISC_R_SUCCESS) {
"catz: failed to create DB iterator - %s",
return;
}
/*
* Iterate over database to fill the new zone.
*/
if (result != ISC_R_SUCCESS) {
"catz: failed to get db iterator - %s",
}
while (result == ISC_R_SUCCESS) {
if (result != ISC_R_SUCCESS) {
"catz: failed to get db iterator - %s",
break;
}
&rdsiter);
if (result != ISC_R_SUCCESS) {
"catz: failed to fetch rrdatasets - %s",
break;
}
while (result == ISC_R_SUCCESS) {
&rdataset);
if (result != ISC_R_SUCCESS) {
sizeof(classbuf));
sizeof(typebuf));
"catz: unknown record in catalog "
"zone - %s %s %s(%s) - ignoring",
}
if (result != ISC_R_SUCCESS) {
break;
}
}
}
"catz: update_from_db: iteration finished");
/*
* Finally merge new zone into old zone.
*/
if (result != ISC_R_SUCCESS) {
"catz: failed merging zones: %s",
return;
}
"catz: update_from_db: new zone merged");
/*
* When we're doing reconfig and setting a new catalog zone
* from an existing zone we won't have a chance to set up
* update callback in zone_startload or axfr_makedb, but we will
* call onupdate() artificially so we can register the callback here.
*/
if (result == ISC_R_SUCCESS)
}
}
void
result == ISC_R_SUCCESS;
{
}
}
void
result == ISC_R_SUCCESS;)
{
"catz: removing catalog zone %s", cname);
/*
* Merge the old zone with an empty one to remove
* all members.
*/
/* Make sure that we have an empty catalog zone. */
} else {
}
}
}
}