diff --git a/doc/man_kzonecheck.rst b/doc/man_kzonecheck.rst index 58bf8ec97..225317eff 100644 --- a/doc/man_kzonecheck.rst +++ b/doc/man_kzonecheck.rst @@ -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 diff --git a/src/knot/zone/zonefile.c b/src/knot/zone/zonefile.c index 5d809dc50..0bad6ad37 100644 --- a/src/knot/zone/zonefile.c +++ b/src/knot/zone/zonefile.c @@ -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); diff --git a/src/libzscanner/error.c b/src/libzscanner/error.c index 72fb62661..34e034f3c 100644 --- a/src/libzscanner/error.c +++ b/src/libzscanner/error.c @@ -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 }; diff --git a/src/libzscanner/error.h b/src/libzscanner/error.h index b8de7794d..56c3f9742 100644 --- a/src/libzscanner/error.h +++ b/src/libzscanner/error.h @@ -86,7 +86,6 @@ enum err_codes { ZS_EMPTY_LIST_ITEM, ZS_FILE_ACCESS, ZS_BAD_ALPN_BACKSLASH, - ZS_NO_SOA, }; /*! diff --git a/src/libzscanner/scanner.c.g2 b/src/libzscanner/scanner.c.g2 index 0d3afc61f..079b23d03 100644 --- a/src/libzscanner/scanner.c.g2 +++ b/src/libzscanner/scanner.c.g2 @@ -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; diff --git a/src/libzscanner/scanner.c.t0 b/src/libzscanner/scanner.c.t0 index d8d2d2e86..d00c4e348 100644 --- a/src/libzscanner/scanner.c.t0 +++ b/src/libzscanner/scanner.c.t0 @@ -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; diff --git a/src/libzscanner/scanner.h b/src/libzscanner/scanner.h index c4c4caa71..b85641b2b 100644 --- a/src/libzscanner/scanner.h +++ b/src/libzscanner/scanner.h @@ -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. * diff --git a/src/libzscanner/scanner.rl b/src/libzscanner/scanner.rl index 0341affd3..2435c8f0d 100644 --- a/src/libzscanner/scanner.rl +++ b/src/libzscanner/scanner.rl @@ -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; diff --git a/src/utils/kzonecheck/main.c b/src/utils/kzonecheck/main.c index 84f59d5db..ccb88365c 100644 --- a/src/utils/kzonecheck/main.c +++ b/src/utils/kzonecheck/main.c @@ -30,7 +30,7 @@ static void print_help(void) "\n" "Options:\n" " -o, --origin Zone name.\n" - " (default filename without .zone)\n" + " (default SOA owner)\n" " -d, --dnssec Enforce check of DNSSEC records.\n" " (default autodetection)\n" " -j, --jobs Number of threads.\n" diff --git a/src/utils/kzonecheck/zone_check.c b/src/utils/kzonecheck/zone_check.c index e12744eb9..68d06827d 100644 --- a/src/utils/kzonecheck/zone_check.c +++ b/src/utils/kzonecheck/zone_check.c @@ -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); } } diff --git a/tests/knot/semantic_check_data/soa.relative b/tests/knot/semantic_check_data/soa.relative new file mode 100644 index 000000000..fdf0d2d54 --- /dev/null +++ b/tests/knot/semantic_check_data/soa.relative @@ -0,0 +1,2 @@ +soa.relative. NS example.net. +@ SOA dns1 hostmaster 1 1000 360 10000 7200 diff --git a/tests/knot/test_semantic_check.in b/tests/knot/test_semantic_check.in index 2fa9a8540..e1dafeab9 100644 --- a/tests/knot/test_semantic_check.in +++ b/tests/knot/test_semantic_check.in @@ -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"