kzonecheck: consider zone file name with origin auto-detection

Partially revert 38f65806dd
This commit is contained in:
Daniel Salzman 2025-05-29 10:53:57 +02:00
parent 1cec5a5fb0
commit 1464b18288
12 changed files with 133 additions and 218 deletions

View file

@ -29,8 +29,9 @@ Options
.......
**-o**, **--origin** *origin*
Zone origin. If not specified, the origin is determined from the SOA record
in the zone file.
Zone origin. If not specified, the SOA record owner in the zone file is used
and the zone file name (without possible **.zone** suffix) is considered as
the initial zone origin in case the owner isn't FQDN.
**-d**, **--dnssec** **on**\|\ **off**
Also check DNSSEC-related records. The default is to decide based on the

View file

@ -120,49 +120,89 @@ static void process_data(zs_scanner_t *scanner)
knot_rrset_clear(&rr, NULL);
}
static void check_origin(zs_scanner_t *s)
{
if (s->r_type == KNOT_RRTYPE_SOA) {
uint8_t *origin_buf = s->process.data;
assert(s->r_owner_length <= KNOT_DNAME_MAXLEN);
origin_buf[0] = s->r_owner_length;
memcpy(origin_buf + 1, s->r_owner, s->r_owner_length);
s->state = ZS_STATE_STOP;
}
}
static void error_origin(zs_scanner_t *s)
{
log_error("failed to detect zone origin, file '%s', line %"PRIu64" (%s)",
s->file.name, s->line_counter, zs_strerror(s->error.code));
}
int zonefile_open(zloader_t *loader, const char *source, const knot_dname_t *origin,
uint32_t dflt_ttl, semcheck_optional_t semantic_checks, time_t time)
{
if (!loader) {
if (loader == NULL || source == NULL) {
return KNOT_EINVAL;
}
memset(loader, 0, sizeof(zloader_t));
/* Check zone file. */
if (access(source, F_OK | R_OK) != 0) {
return knot_map_errno();
}
/* Create context. */
zcreator_t *zc = malloc(sizeof(zcreator_t));
if (zc == NULL) {
return KNOT_ENOMEM;
}
memset(zc, 0, sizeof(zcreator_t));
/* Prepare textual owner for zone scanner (NULL for autodetection). */
char *origin_str = NULL;
if (origin != NULL) {
origin_str = knot_dname_to_str_alloc(origin);
if (origin_str == NULL) {
free(zc);
return KNOT_ENOMEM;
uint8_t origin_buf[1 + KNOT_DNAME_MAXLEN];
if (origin == NULL) { // Origin autodetection based on SOA owner and source.
const char *ext = ".zone";
char *origin_str = basename(source);
if (strcmp(origin_str + strlen(origin_str) - strlen(ext), ext) == 0) {
origin_str = strndup(origin_str, strlen(origin_str) - strlen(ext));
} else {
origin_str = strdup(origin_str);
}
origin_buf[0] = 0;
zs_scanner_t s;
if (zs_init(&s, origin_str, KNOT_CLASS_IN, 0) != 0 ||
zs_set_input_file(&s, source) != 0 ||
zs_set_processing(&s, check_origin, error_origin, &origin_buf) != 0) {
free(origin_str);
zs_deinit(&s);
return KNOT_EFILE;
}
free(origin_str);
if (zs_parse_all(&s) != 0 && s.error.fatal) {
zs_deinit(&s);
return KNOT_EPARSEFAIL;
}
zs_deinit(&s);
if (origin_buf[0] == 0) {
return KNOT_ESOAINVAL;
}
origin = origin_buf + 1;
}
knot_dname_txt_storage_t origin_str;
if (knot_dname_to_str(origin_str, origin, sizeof(origin_str)) == NULL) {
return KNOT_EINVAL;
}
if (zs_init(&loader->scanner, origin_str, KNOT_CLASS_IN, dflt_ttl) != 0 ||
zs_set_input_file(&loader->scanner, source) != 0 ||
zs_set_processing(&loader->scanner, process_data, process_error, zc) != 0) {
bool missing_origin = loader->scanner.error.code == ZS_NO_SOA;
zs_deinit(&loader->scanner);
free(origin_str);
free(zc);
return missing_origin ? KNOT_ESOAINVAL : KNOT_EFILE;
return KNOT_EFILE;
}
free(origin_str);
zc->z = zone_contents_new(loader->scanner.zone_origin, true);
zc->z = zone_contents_new(origin, true);
if (zc->z == NULL) {
zs_deinit(&loader->scanner);
free(zc);

View file

@ -159,8 +159,6 @@ static const err_table_t err_msgs[] = {
"permission denied" ),
ERR_ITEM( ZS_BAD_ALPN_BACKSLASH,
"unscaped backslash character" ),
ERR_ITEM( ZS_NO_SOA,
"missing SOA record" ),
ERR_ITEM( 0, NULL ) // Terminator
};

View file

@ -86,7 +86,6 @@ enum err_codes {
ZS_EMPTY_LIST_ITEM,
ZS_FILE_ACCESS,
ZS_BAD_ALPN_BACKSLASH,
ZS_NO_SOA,
};
/*!

View file

@ -104,32 +104,31 @@ int zs_init(
// Reset the file descriptor.
s->file.descriptor = -1;
// Use non-empty origin if specified.
if (origin != NULL && strlen(origin) > 0) {
size_t origin_len = strlen(origin);
// Use the root zone as origin if not specified.
if (origin == NULL || strlen(origin) == 0) {
origin = ".";
}
size_t origin_len = strlen(origin);
// Prepare a zone settings header.
const char *format;
if (origin[origin_len - 1] != '.') {
format = "$ORIGIN %s.\n";
} else {
format = "$ORIGIN %s\n";
}
char settings[1024];
int ret = snprintf(settings, sizeof(settings), format, origin);
if (ret <= 0 || ret >= sizeof(settings)) {
ERR(ZS_ENOMEM);
return -1;
}
// Parse the settings to set up the scanner origin.
if (zs_set_input_string(s, settings, ret) != 0 ||
zs_parse_all(s) != 0) {
return -1;
}
// Prepare a zone settings header.
const char *format;
if (origin[origin_len - 1] != '.') {
format = "$ORIGIN %s.\n";
} else {
s->zone_origin_length = ZS_NO_ORIGIN_LEN;
format = "$ORIGIN %s\n";
}
char settings[1024];
int ret = snprintf(settings, sizeof(settings), format, origin);
if (ret <= 0 || ret >= sizeof(settings)) {
ERR(ZS_ENOMEM);
return -1;
}
// Parse the settings to set up the scanner origin.
if (zs_set_input_string(s, settings, ret) != 0 ||
zs_parse_all(s) != 0) {
return -1;
}
// Set scanner defaults.
@ -251,46 +250,12 @@ static char *read_file_to_buf(
return buf;
}
static void check_origin(zs_scanner_t *ss)
{
if (ss->r_type == KNOT_RRTYPE_SOA) {
zs_scanner_t *s = ss->process.data;
memcpy(s->zone_origin, ss->r_owner, ss->r_owner_length);
s->zone_origin_length = ss->r_owner_length;
ss->state = ZS_STATE_STOP;
}
}
static int parse_origin(zs_scanner_t *s)
{
if (s->zone_origin_length != ZS_NO_ORIGIN_LEN) {
return 0;
}
zs_scanner_t ss;
if (zs_init(&ss, ".", KNOT_CLASS_IN, 0) != 0 ||
zs_set_input_string(&ss, s->input.start, s->input.end - s->input.start) != 0 ||
zs_set_processing(&ss, check_origin, NULL, s) != 0 ||
zs_parse_all(&ss) != 0) {
zs_deinit(&ss);
return -1;
}
zs_deinit(&ss);
return s->zone_origin_length != ZS_NO_ORIGIN_LEN ? 0 : -1;
}
_public_
int zs_set_input_string(
zs_scanner_t *s,
const char *input,
size_t size)
{
if (parse_origin(s) != 0) {
ERR(ZS_NO_SOA);
return -1;
}
s->state = ZS_STATE_NONE;
return set_input_string(s, input, size, false);
@ -390,12 +355,6 @@ int zs_set_input_file(
return -1;
}
if (parse_origin(s) != 0) {
ERR(ZS_NO_SOA);
input_deinit(s, false);
return -1;
}
s->state = ZS_STATE_NONE;
return 0;

View file

@ -6499,32 +6499,31 @@ int zs_init(
// Reset the file descriptor.
s->file.descriptor = -1;
// Use non-empty origin if specified.
if (origin != NULL && strlen(origin) > 0) {
size_t origin_len = strlen(origin);
// Use the root zone as origin if not specified.
if (origin == NULL || strlen(origin) == 0) {
origin = ".";
}
size_t origin_len = strlen(origin);
// Prepare a zone settings header.
const char *format;
if (origin[origin_len - 1] != '.') {
format = "$ORIGIN %s.\n";
} else {
format = "$ORIGIN %s\n";
}
char settings[1024];
int ret = snprintf(settings, sizeof(settings), format, origin);
if (ret <= 0 || ret >= sizeof(settings)) {
ERR(ZS_ENOMEM);
return -1;
}
// Parse the settings to set up the scanner origin.
if (zs_set_input_string(s, settings, ret) != 0 ||
zs_parse_all(s) != 0) {
return -1;
}
// Prepare a zone settings header.
const char *format;
if (origin[origin_len - 1] != '.') {
format = "$ORIGIN %s.\n";
} else {
s->zone_origin_length = ZS_NO_ORIGIN_LEN;
format = "$ORIGIN %s\n";
}
char settings[1024];
int ret = snprintf(settings, sizeof(settings), format, origin);
if (ret <= 0 || ret >= sizeof(settings)) {
ERR(ZS_ENOMEM);
return -1;
}
// Parse the settings to set up the scanner origin.
if (zs_set_input_string(s, settings, ret) != 0 ||
zs_parse_all(s) != 0) {
return -1;
}
// Set scanner defaults.
@ -6646,46 +6645,12 @@ static char *read_file_to_buf(
return buf;
}
static void check_origin(zs_scanner_t *ss)
{
if (ss->r_type == KNOT_RRTYPE_SOA) {
zs_scanner_t *s = ss->process.data;
memcpy(s->zone_origin, ss->r_owner, ss->r_owner_length);
s->zone_origin_length = ss->r_owner_length;
ss->state = ZS_STATE_STOP;
}
}
static int parse_origin(zs_scanner_t *s)
{
if (s->zone_origin_length != ZS_NO_ORIGIN_LEN) {
return 0;
}
zs_scanner_t ss;
if (zs_init(&ss, ".", KNOT_CLASS_IN, 0) != 0 ||
zs_set_input_string(&ss, s->input.start, s->input.end - s->input.start) != 0 ||
zs_set_processing(&ss, check_origin, NULL, s) != 0 ||
zs_parse_all(&ss) != 0) {
zs_deinit(&ss);
return -1;
}
zs_deinit(&ss);
return s->zone_origin_length != ZS_NO_ORIGIN_LEN ? 0 : -1;
}
_public_
int zs_set_input_string(
zs_scanner_t *s,
const char *input,
size_t size)
{
if (parse_origin(s) != 0) {
ERR(ZS_NO_SOA);
return -1;
}
s->state = ZS_STATE_NONE;
return set_input_string(s, input, size, false);
@ -6785,12 +6750,6 @@ int zs_set_input_file(
return -1;
}
if (parse_origin(s) != 0) {
ERR(ZS_NO_SOA);
input_deinit(s, false);
return -1;
}
s->state = ZS_STATE_NONE;
return 0;

View file

@ -38,9 +38,6 @@
/*! \brief Ragel call stack size (see Ragel internals). */
#define ZS_RAGEL_STACK_SIZE 16
/*! \brief Indication of automatic origin detection. */
#define ZS_NO_ORIGIN_LEN UINT32_MAX
/*! \brief Auxiliary structure for storing bitmap window items (see RFC4034). */
typedef struct {
uint8_t bitmap[32];
@ -268,7 +265,7 @@ struct zs_scanner {
* \note Error code is stored in the scanner context.
*
* \param scanner Scanner context.
* \param origin Initial zone origin. Autodetected if empty.
* \param origin Initial zone origin ("." is used if empty).
* \param rclass Zone class value.
* \param ttl Initial ttl value.
*

View file

@ -105,32 +105,31 @@ int zs_init(
// Reset the file descriptor.
s->file.descriptor = -1;
// Use non-empty origin if specified.
if (origin != NULL && strlen(origin) > 0) {
size_t origin_len = strlen(origin);
// Use the root zone as origin if not specified.
if (origin == NULL || strlen(origin) == 0) {
origin = ".";
}
size_t origin_len = strlen(origin);
// Prepare a zone settings header.
const char *format;
if (origin[origin_len - 1] != '.') {
format = "$ORIGIN %s.\n";
} else {
format = "$ORIGIN %s\n";
}
char settings[1024];
int ret = snprintf(settings, sizeof(settings), format, origin);
if (ret <= 0 || ret >= sizeof(settings)) {
ERR(ZS_ENOMEM);
return -1;
}
// Parse the settings to set up the scanner origin.
if (zs_set_input_string(s, settings, ret) != 0 ||
zs_parse_all(s) != 0) {
return -1;
}
// Prepare a zone settings header.
const char *format;
if (origin[origin_len - 1] != '.') {
format = "$ORIGIN %s.\n";
} else {
s->zone_origin_length = ZS_NO_ORIGIN_LEN;
format = "$ORIGIN %s\n";
}
char settings[1024];
int ret = snprintf(settings, sizeof(settings), format, origin);
if (ret <= 0 || ret >= sizeof(settings)) {
ERR(ZS_ENOMEM);
return -1;
}
// Parse the settings to set up the scanner origin.
if (zs_set_input_string(s, settings, ret) != 0 ||
zs_parse_all(s) != 0) {
return -1;
}
// Set scanner defaults.
@ -252,46 +251,12 @@ static char *read_file_to_buf(
return buf;
}
static void check_origin(zs_scanner_t *ss)
{
if (ss->r_type == KNOT_RRTYPE_SOA) {
zs_scanner_t *s = ss->process.data;
memcpy(s->zone_origin, ss->r_owner, ss->r_owner_length);
s->zone_origin_length = ss->r_owner_length;
ss->state = ZS_STATE_STOP;
}
}
static int parse_origin(zs_scanner_t *s)
{
if (s->zone_origin_length != ZS_NO_ORIGIN_LEN) {
return 0;
}
zs_scanner_t ss;
if (zs_init(&ss, ".", KNOT_CLASS_IN, 0) != 0 ||
zs_set_input_string(&ss, s->input.start, s->input.end - s->input.start) != 0 ||
zs_set_processing(&ss, check_origin, NULL, s) != 0 ||
zs_parse_all(&ss) != 0) {
zs_deinit(&ss);
return -1;
}
zs_deinit(&ss);
return s->zone_origin_length != ZS_NO_ORIGIN_LEN ? 0 : -1;
}
_public_
int zs_set_input_string(
zs_scanner_t *s,
const char *input,
size_t size)
{
if (parse_origin(s) != 0) {
ERR(ZS_NO_SOA);
return -1;
}
s->state = ZS_STATE_NONE;
return set_input_string(s, input, size, false);
@ -391,12 +356,6 @@ int zs_set_input_file(
return -1;
}
if (parse_origin(s) != 0) {
ERR(ZS_NO_SOA);
input_deinit(s, false);
return -1;
}
s->state = ZS_STATE_NONE;
return 0;

View file

@ -30,7 +30,7 @@ static void print_help(void)
"\n"
"Options:\n"
" -o, --origin <zone_origin> Zone name.\n"
" (default filename without .zone)\n"
" (default SOA owner)\n"
" -d, --dnssec <on|off> Enforce check of DNSSEC records.\n"
" (default autodetection)\n"
" -j, --jobs <num> Number of threads.\n"

View file

@ -81,7 +81,7 @@ int zone_check(const char *zone_file, const knot_dname_t *zone_name, bool zonemd
if (zone_name == NULL) {
knot_dname_txt_storage_t origin;
if (knot_dname_to_str(origin,zl.scanner.zone_origin , sizeof(origin)) != NULL) {
if (knot_dname_to_str(origin, zl.scanner.zone_origin, sizeof(origin)) != NULL) {
log_debug("detected zone origin %s", origin);
}
}

View file

@ -0,0 +1,2 @@
soa.relative. NS example.net.
@ SOA dns1 hostmaster 1 1000 360 10000 7200

View file

@ -103,6 +103,7 @@ plan_lazy
expect_error "soa.missing" 0 0 "" "AUTO"
test_correct "soa.duplicate" "AUTO"
test_correct "soa.nonfirst" "AUTO"
test_correct "soa.relative" "AUTO"
expect_error "soa.missing" 1 1 "$SOA_MISSING"
expect_error "soa.multiple" 1 1 "$SOA_MULTIPLE"