knot-dns/tests/libzscanner/zscanner-tool.c
2025-03-24 09:53:50 +01:00

246 lines
5.3 KiB
C

/* Copyright (C) CZ.NIC, z.s.p.o. and contributors
* SPDX-License-Identifier: GPL-2.0-or-later
* For more information, see <https://www.knot-dns.cz/>
*/
#include <getopt.h>
#include <inttypes.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include "libzscanner/processing.h"
#include "libzscanner/scanner.h"
#define DEFAULT_MODE 1
#define DEFAULT_CLASS 1
#define DEFAULT_TTL 0
static void *timestamp_worker(void *data)
{
int *ret = (int *)data;
*ret = test_date_to_timestamp();
return NULL;
}
static void help(void)
{
printf("\nZone scanner testing tool.\n"
"Usage: zscanner-tool [parameters] origin zonefile\n"
"\n"
"Parameters:\n"
" -m [0,1,2] Processing mode.\n"
" 0 Empty output.\n"
" 1 Debug output (DEFAULT).\n"
" 2 Test output.\n"
" -b <num> Divide hex string to blocks of length <num>.\n"
" -s State parsing mode.\n"
" -t Launch unit tests.\n"
" -h Print this help.\n");
}
static int time_test(void)
{
pthread_t t1, t2, t3;
int ret1, ret2, ret3;
pthread_create(&t1, NULL, timestamp_worker, &ret1);
pthread_create(&t2, NULL, timestamp_worker, &ret2);
pthread_create(&t3, NULL, timestamp_worker, &ret3);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_join(t3, NULL);
if (ret1 != 0 || ret2 != 0 || ret3 != 0) {
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
static int include(zs_scanner_t *s);
static int state_parsing(zs_scanner_t *s)
{
while (zs_parse_record(s) == 0) {
switch (s->state) {
case ZS_STATE_DATA:
if (s->process.record != NULL) {
s->process.record(s);
}
break;
case ZS_STATE_ERROR:
if (s->process.error != NULL) {
s->process.error(s);
}
if (s->error.fatal) {
return -1;
}
break;
case ZS_STATE_INCLUDE:
if (include(s) != 0) {
return -1;
}
break;
default:
return (s->error.counter == 0) ? 0 : -1;
}
}
return -1;
}
static int include(zs_scanner_t *s)
{
zs_scanner_t *ss;
int ret = 0;
if ((ss = malloc(sizeof(zs_scanner_t))) == NULL ||
zs_init(ss, (char *)s->buffer, s->default_class, s->default_ttl) != 0 ||
zs_set_input_file(ss, (char *)(s->include_filename)) != 0 ||
zs_set_processing(ss, s->process.record, s->process.error, s->process.data) != 0 ||
state_parsing(ss) != 0) {
if (ss == NULL) {
s->error.code = ZS_ENOMEM;
} else if (ss->error.counter > 0) {
s->error.counter += ss->error.counter;
s->error.code = ZS_UNPROCESSED_INCLUDE;
} else {
s->error.code = ss->error.code;
}
if (s->process.error != NULL) {
s->buffer[0] = '\0'; // Clear unrelated content.
s->buffer_length = 0;
s->error.counter++;
s->error.fatal = true;
s->process.error(s);
}
ret = -1;
}
zs_deinit(ss);
free(ss);
return ret;
}
int main(int argc, char *argv[])
{
int mode = DEFAULT_MODE, block = 0, state = 0, test = 0;
// Command line long options.
struct option opts[] = {
{ "mode", required_argument, NULL, 'm' },
{ "block", required_argument, NULL, 'b' },
{ "state", no_argument, NULL, 's' },
{ "test", no_argument, NULL, 't' },
{ "help", no_argument, NULL, 'h' },
{ NULL }
};
// Parsed command line arguments.
int opt = 0, li = 0;
while ((opt = getopt_long(argc, argv, "m:b:sth", opts, &li)) != -1) {
switch (opt) {
case 'm':
mode = atoi(optarg);
break;
case 'b':
block = atoi(optarg);
break;
case 's':
state = 1;
break;
case 't':
test = 1;
break;
case 'h':
help();
return EXIT_SUCCESS;
default:
help();
return EXIT_FAILURE;
}
}
if (test == 1) {
return time_test();
}
// Check if there are 2 remaining non-options.
if (argc - optind != 2) {
help();
return EXIT_FAILURE;
}
const char *origin = argv[optind];
const char *zone_file = argv[optind + 1];
// Create a zone scanner.
zs_scanner_t *s = malloc(sizeof(zs_scanner_t));
if (s == NULL) {
printf("Scanner create error!\n");
return EXIT_FAILURE;
}
if (zs_init(s, origin, DEFAULT_CLASS, DEFAULT_TTL) != 0) {
printf("Scanner init error!\n");
free(s);
return EXIT_FAILURE;
}
if (zs_set_input_file(s, zone_file) != 0) {
printf("Scanner file error!\n");
zs_deinit(s);
free(s);
return EXIT_FAILURE;
}
// Set the processing mode.
int ret;
switch (mode) {
case 0:
ret = 0;
break;
case 1:
ret = zs_set_processing(s, debug_process_record, debug_process_error, &block);
ret += zs_set_processing_comment(s, debug_process_comment);
break;
case 2:
ret = zs_set_processing(s, test_process_record, test_process_error, NULL);
break;
default:
printf("Bad mode number!\n");
help();
return EXIT_FAILURE;
}
if (ret != 0) {
printf("Processing setup error!\n");
return EXIT_FAILURE;
}
// Parse the file.
ret = state ? state_parsing(s) : zs_parse_all(s);
if (ret == 0) {
if (mode == DEFAULT_MODE) {
printf("Zone file has been processed successfully\n");
}
zs_deinit(s);
free(s);
return EXIT_SUCCESS;
} else {
if (s->error.counter > 0 && mode == DEFAULT_MODE) {
printf("Zone processing has stopped with "
"%"PRIu64" warnings/errors!\n",
s->error.counter);
} else if (mode == DEFAULT_MODE) {
printf("%s\n", zs_strerror(s->error.code));
}
zs_deinit(s);
free(s);
return EXIT_FAILURE;
}
}