From bdb63f9b848b7a497a4a8e26d187d1955db224ae Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?V=C3=ADt=C4=9Bzslav=20K=C5=99=C3=AD=C5=BE?=
 <vitezslav.kriz@nic.cz>
Date: Wed, 17 Feb 2016 16:25:04 +0100
Subject: [PATCH] kzonecheck: standalone tool for semantic checks

---
 .gitignore                     |   1 +
 src/Makefile.am                |   7 +-
 src/knot/zone/semantic-check.c |   4 +-
 src/knot/zone/semantic-check.h |   4 +-
 src/utils/kzonecheck/main.c    | 168 +++++++++++++++++++++++++++++++++
 5 files changed, 180 insertions(+), 4 deletions(-)
 create mode 100644 src/utils/kzonecheck/main.c

diff --git a/.gitignore b/.gitignore
index e86e39068b..ee9ff161d6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -82,6 +82,7 @@
 /src/knsupdate
 /src/knsec3hash
 /src/rosedb_tool
+/src/kzonecheck
 
 # Plugin binaries
 /src/rosedb_tool
diff --git a/src/Makefile.am b/src/Makefile.am
index b018c16f18..6fff85e007 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -224,7 +224,7 @@ libknotus_la_LIBADD = libcontrib.la libknot.la
 
 if HAVE_DAEMON
 
-sbin_PROGRAMS = knotc knotd
+sbin_PROGRAMS = knotc knotd kzonecheck
 libexec_PROGRAMS = knot1to2
 noinst_LTLIBRARIES += libknotd.la libknotus.la
 
@@ -406,6 +406,11 @@ knotc_CPPFLAGS = $(AM_CPPFLAGS) $(libedit_CFLAGS)
 knotc_LDADD = libknotd.la libknotus.la $(libedit_LIBS)
 knot1to2_LDADD = libcontrib.la
 
+kzonecheck_SOURCES = \
+	utils/kzonecheck/main.c
+
+kzonecheck_LDADD = libknotd.la
+
 ####################################
 # Optional Knot DNS Daemon modules #
 ####################################
diff --git a/src/knot/zone/semantic-check.c b/src/knot/zone/semantic-check.c
index fc166bbea6..16b7baabab 100644
--- a/src/knot/zone/semantic-check.c
+++ b/src/knot/zone/semantic-check.c
@@ -31,7 +31,7 @@
 #include "contrib/mempattern.h"
 #include "contrib/wire.h"
 
-static char *error_messages[(-ZC_ERR_UNKNOWN) + 1] = {
+const char *zonechecks_error_messages[(-ZC_ERR_UNKNOWN) + 1] = {
 	[-ZC_ERR_MISSING_SOA] =
 	"SOA record missing in zone",
 	[-ZC_ERR_MISSING_NS_DEL_POINT] =
@@ -167,7 +167,7 @@ void err_handler_log_errors(err_handler_t *handler)
 			return;
 		}
 
-		const char *errmsg = error_messages[-n->error];
+		const char *errmsg = zonechecks_error_messages[-n->error];
 		log_zone_str_warning(n->zone_name,
 		                     "semantic check, node '%s' (%s%s%s)",
 		                     n->name ? n->name : "",
diff --git a/src/knot/zone/semantic-check.h b/src/knot/zone/semantic-check.h
index 8ea675cf8a..1ea6c106d0 100644
--- a/src/knot/zone/semantic-check.h
+++ b/src/knot/zone/semantic-check.h
@@ -97,9 +97,11 @@ enum zonechecks_errors {
 	ZC_ERR_GLUE_RECORD,
 
 	ZC_ERR_GLUE_GENERAL_ERROR, /* GLUE error delimiter. */
-	/// \TODO ADD LAST DELIMITER
+	ZC_ERR_LAST = ZC_ERR_GLUE_GENERAL_ERROR,
 };
 
+extern const char *zonechecks_error_messages[];
+
 /*!
  * \brief Structure for handling semantic errors.
  */
diff --git a/src/utils/kzonecheck/main.c b/src/utils/kzonecheck/main.c
new file mode 100644
index 0000000000..98236e0c78
--- /dev/null
+++ b/src/utils/kzonecheck/main.c
@@ -0,0 +1,168 @@
+/*  Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <unistd.h>
+#include <getopt.h>
+#include <stdio.h>
+
+#include "libknot/libknot.h"
+#include "knot/zone/contents.h"
+#include "knot/zone/zonefile.h"
+#include "contrib/ucw/lists.h"
+#include "utils/common/params.h"
+#include "knot/common/log.h"
+
+
+#define PROGRAM_NAME "kzonecheck"
+
+static void print_help(void)
+{
+	printf("Usage: %s [parameters] <action> [action_args]\n"
+	       "\n"
+	       "Parameters:\n"
+	       " -o, --origin <zone origin>                  blabla\n"
+	       "                                       (default blabla)\n"
+	       " -v, --verbose                        Enable debug output.\n"
+	       " -h, --help                           Print the program help.\n"
+	       " -V, --version                        Print the program version.\n"
+	       "\n"
+	       "Actions:\n",
+	       PROGRAM_NAME);
+}
+
+void print_errors(err_handler_t *handler)
+{
+	err_node_t *n;
+	WALK_LIST(n, handler->error_list) {
+		if (n->error > (int)ZC_ERR_GLUE_RECORD) {
+			fprintf(stderr, "zone: [%s], semantic check, unknown error\n",
+			        n->zone_name ? n->zone_name : "?");
+			return;
+		}
+
+		const char *errmsg = zonechecks_error_messages[-n->error];
+
+		fprintf(stderr ,"node: '%s' (%s%s%s)\n",
+		        n->name ? n->name : "?",
+		        errmsg ? errmsg : "unknown error",
+		        n->data ? " " : "",
+		        n->data ? n->data : "");
+	}
+}
+
+void  print_statistics(err_handler_t *handler)
+{
+	fprintf(stderr, "\nERRORS SUMMARY:\n\tCount\tError\n");
+	for(int i = ZC_ERR_UNKNOWN; i < ZC_ERR_LAST; ++i) {
+		if (handler->errors[-i] > 0) {
+			fprintf(stderr, "\t%u\t%s\n", handler->errors[-i], zonechecks_error_messages[-i]);
+		}
+	}
+}
+
+int zone_check(const char *zone_file, const knot_dname_t *zone_name)
+{
+	zloader_t zl;
+	int ret = zonefile_open(&zl, zone_file, zone_name, true);
+	if (ret != KNOT_EOK) {
+		return ret;
+	}
+
+	zl.creator->master = true;
+
+	zone_contents_t *contents;
+	contents = zonefile_load(&zl);
+
+	print_errors(&zl.err_handler);
+	print_statistics(&zl.err_handler);
+
+	zonefile_close(&zl);
+	if (contents == NULL) {
+		return KNOT_ERROR;
+	}
+
+	zone_contents_deep_free(&contents);
+
+	return KNOT_EOK;
+}
+
+
+int main(int argc, char *argv[])
+{
+	char *filename;
+	char *zonename = "";
+	bool verbose = false;
+
+	/* Long options. */
+	struct option opts[] = {
+		{ "origin",  required_argument, NULL, 'o' },
+		{ "verbose", no_argument,       NULL, 'v' },
+		{ "help",    no_argument,       NULL, 'h' },
+		{ "version", no_argument,       NULL, 'V' },
+		{ NULL }
+	};
+
+	/* Parse command line arguments */
+	int opt = 0, li = 0;
+	while ((opt = getopt_long(argc, argv, "o:vVh", opts, &li)) != -1) {
+		switch (opt) {
+		case 'o':
+			zonename = optarg;
+			break;
+		case 'v':
+			verbose = true;
+			break;
+		case 'h':
+			print_help();
+			return EXIT_SUCCESS;
+		case 'V':
+			print_version(PROGRAM_NAME);
+			return EXIT_SUCCESS;
+		default:
+			print_help();
+			return EXIT_FAILURE;
+		}
+	}
+
+	/* Check if there's at least one remaining non-option. */
+	if (optind >= argc) {
+		fprintf(stderr, "Expected argument after options\n");
+		print_help();
+		return EXIT_FAILURE;
+	}
+	filename = argv[optind];
+
+
+	/* Set up simplified logging just to stdout/stderr. */
+	log_init();
+	log_levels_set(LOGT_STDOUT, LOG_ANY, LOG_MASK(LOG_INFO) | LOG_MASK(LOG_NOTICE));
+	log_levels_set(LOGT_STDERR, LOG_ANY, LOG_UPTO(LOG_WARNING));
+	log_levels_set(LOGT_SYSLOG, LOG_ANY, 0);
+	log_flag_set(LOG_FNO_TIMESTAMP | LOG_FNO_INFO);
+	if (verbose) {
+		log_levels_add(LOGT_STDOUT, LOG_ANY, LOG_MASK(LOG_DEBUG));
+	}
+
+	knot_dname_t *dname = knot_dname_from_str_alloc(zonename);
+
+	int ret = zone_check(filename, dname);
+
+	printf("%d", ret);
+
+	free(dname);
+
+	return ret;
+}
-- 
GitLab