summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--KNOWN_ISSUES1
-rw-r--r--Knot.files10
-rw-r--r--RELNOTES17
-rw-r--r--src/Makefile.am19
-rw-r--r--src/Makefile.in87
-rw-r--r--src/common/WELL1024a.c25
-rw-r--r--src/common/acl.c55
-rw-r--r--src/common/acl.h27
-rw-r--r--src/common/errors.c1
-rw-r--r--src/common/evsched.c14
-rw-r--r--src/common/fdset_kqueue.c2
-rw-r--r--src/common/libtap/tap.c60
-rw-r--r--src/common/libtap/tap.h21
-rw-r--r--src/knot/conf/cf-parse.y60
-rw-r--r--src/knot/conf/conf.c87
-rw-r--r--src/knot/ctl/knotc_main.c1
-rw-r--r--src/knot/main.c6
-rw-r--r--src/knot/other/debug.h2
-rw-r--r--src/knot/other/log.c5
-rw-r--r--src/knot/server/dthreads.c13
-rw-r--r--src/knot/server/journal.c57
-rw-r--r--src/knot/server/notify.c22
-rw-r--r--src/knot/server/server.c37
-rw-r--r--src/knot/server/socket.c13
-rw-r--r--src/knot/server/tcp-handler.c58
-rw-r--r--src/knot/server/udp-handler.c159
-rw-r--r--src/knot/server/xfr-handler.c467
-rw-r--r--src/knot/server/zones.c616
-rw-r--r--src/knot/server/zones.h17
-rw-r--r--src/knot/zone/semantic-check.c1244
-rw-r--r--src/knot/zone/semantic-check.h276
-rw-r--r--src/knot/zone/zone-dump.c1462
-rw-r--r--src/libknot/nameserver/name-server.c376
-rw-r--r--src/libknot/nameserver/name-server.h30
-rw-r--r--src/libknot/packet/packet.c16
-rw-r--r--src/libknot/packet/packet.h7
-rw-r--r--src/libknot/rdata.c16
-rw-r--r--src/libknot/rdata.h1
-rw-r--r--src/libknot/rrset.c39
-rw-r--r--src/libknot/rrset.h2
-rw-r--r--src/libknot/tsig-op.c644
-rw-r--r--src/libknot/tsig-op.h15
-rw-r--r--src/libknot/tsig.c45
-rw-r--r--src/libknot/tsig.h4
-rw-r--r--src/libknot/updates/xfr-in.c82
-rw-r--r--src/libknot/updates/xfr-in.h2
-rw-r--r--src/libknot/util/conv.c325
-rw-r--r--src/libknot/util/conv.h44
-rw-r--r--src/libknot/util/debug.h43
-rw-r--r--src/libknot/util/error.h3
-rw-r--r--src/libknot/util/libknot_error.c1
-rw-r--r--src/libknot/zone/zone-contents.c4
-rw-r--r--src/tests/common/acl_tests.c22
-rw-r--r--src/tests/libknot/libknot/tsig_tests.c685
-rw-r--r--src/tests/libknot/libknot/tsig_tests.h9
-rw-r--r--src/tests/libknot/realdata/libknot_tests_loader_realdata.c3
-rw-r--r--src/tests/libknot/unittests_libknot.c2
-rw-r--r--src/tests/xfr_tests.c369
-rw-r--r--src/tests/xfr_tests.h10
59 files changed, 5121 insertions, 2619 deletions
diff --git a/KNOWN_ISSUES b/KNOWN_ISSUES
index b2da935..1a42844 100644
--- a/KNOWN_ISSUES
+++ b/KNOWN_ISSUES
@@ -4,7 +4,6 @@ Features not supported
Here is a list of the most notable features that are not supported in the
current version of Knot.
-* TSIG
* NSID (RFC5001)
* RRSet rotation
* Root zone support
diff --git a/Knot.files b/Knot.files
index 001186d..e60b931 100644
--- a/Knot.files
+++ b/Knot.files
@@ -50,7 +50,7 @@ src/libknot/util/utils.h
src/libknot/util/utils.c
src/libknot/util/descriptor.h
src/libknot/util/descriptor.c
-src/libknot/util/error.c
+src/libknot/util/libknot_error.c
src/libknot/util/error.h
src/libknot/zone/zonedb.h
src/libknot/zone/zonedb.c
@@ -251,3 +251,11 @@ src/libknot/tsig.h
src/libknot/tsig.c
src/libknot/tsig-op.c
src/libknot/tsig-op.h
+src/libknot/util/conv.c
+src/libknot/util/conv.h
+src/tests/libknot/libknot/tsig_tests.h
+src/tests/libknot/libknot/tsig_tests.c
+src/knot/zone/semantic-check.c
+src/knot/zone/semantic-check.h
+src/tests/xfr_tests.h
+src/tests/xfr_tests.c
diff --git a/RELNOTES b/RELNOTES
index a948f15..147f9c2 100644
--- a/RELNOTES
+++ b/RELNOTES
@@ -1,3 +1,20 @@
+v0.9 - Jan 13, 2012
+-------------------
+
+New features:
+ * TSIG support in both client and server.
+ * Use of sendmmsg() on Linux 3.0+ (improves performance).
+
+Bugfixes:
+ * Knot was not accepting AXFR-style IXFR with first SOA in a separate
+ packet (i.e. from Power DNS).
+ * Wrong SOA TTL in negative answers.
+ * Wrong max packet size for outgoing transfers (was causing the
+ packets to be malformed).
+ * Wrong handling of WKS record in zone compiler.
+ * Problems with zone bootstrapping.
+
+
v0.8.1 - Dec 1, 2011
--------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index 7c86e3f..f8d81e5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,5 +1,5 @@
ACLOCAL_AMFLAGS = -I ../m4
-libexec_PROGRAMS = knot-zcompile unittests unittests-zcompile unittests-libknot-realdata unittests-libknot
+libexec_PROGRAMS = knot-zcompile unittests unittests-zcompile unittests-libknot-realdata unittests-libknot unittests-xfr
sbin_PROGRAMS = knotc knotd
MANPAGES = knotc.8 knotd.8
man8_MANS = knotc.8 knotd.8
@@ -66,8 +66,8 @@ unittests_SOURCES = \
tests/common/skiplist_tests.h \
tests/common/slab_tests.c \
tests/common/slab_tests.h \
- tests/common/fdset_tests.c \
- tests/common/fdset_tests.h \
+ tests/common/fdset_tests.c \
+ tests/common/fdset_tests.h \
tests/knot/conf_tests.c \
tests/knot/conf_tests.h \
tests/knot/dthreads_tests.c \
@@ -116,9 +116,11 @@ unittests_libknot_SOURCES = \
tests/libknot/libknot/rdata_tests.h \
tests/libknot/libknot/rrset_tests.c \
tests/libknot/libknot/rrset_tests.h \
+ tests/libknot/libknot/tsig_tests.c \
+ tests/libknot/libknot/tsig_tests.h \
tests/libknot/libknot/zone_tests.c \
tests/libknot/libknot/zone_tests.h \
- tests/libknot/libknot/zone_tree_tests.h\
+ tests/libknot/libknot/zone_tree_tests.h \
tests/libknot/libknot/zone_tree_tests.c \
tests/libknot/libknot/zonedb_tests.c \
tests/libknot/libknot/zonedb_tests.h \
@@ -135,6 +137,10 @@ unittests_zcompile_SOURCES = \
zcompile/parser-descriptor.c \
zcompile/tests/unittests_zp_main.c
+unittests_xfr_SOURCES = \
+ tests/xfr_tests.c \
+ tests/xfr_tests.h
+
nodist_unittests_SOURCES = \
tests/libknot/parsed_data.rc \
tests/libknot/raw_data_queries.rc \
@@ -153,6 +159,8 @@ libknot_la_SOURCES = \
libknot/util/debug.c \
libknot/util/debug.h \
libknot/util/utils.h \
+ libknot/util/conv.h \
+ libknot/util/conv.c \
libknot/util/descriptor.c \
libknot/util/tolower.h \
libknot/util/tolower.c \
@@ -294,6 +302,8 @@ libknotd_la_SOURCES = \
knot/server/zones.h \
knot/zone/zone-load.c \
knot/zone/zone-load.h \
+ knot/zone/semantic-check.c \
+ knot/zone/semantic-check.h \
knot/zone/zone-dump.c \
knot/zone/zone-dump-text.c \
knot/zone/zone-dump-text.h \
@@ -309,6 +319,7 @@ unittests_LDADD = libknotd.la libknots.la @LIBOBJS@
unittests_zcompile_LDADD = libknot.la libknots.la libknotd.la @LIBOBJS@
unittests_libknot_LDADD = libknot.la libknots.la @LIBOBJS@
unittests_libknot_realdata_LDADD = libknot.la libknots.la @LIBOBJS@
+unittests_xfr_LDADD = libknotd.la libknot.la libknots.la @LIBOBJS@
# automake complains on % rules:
# `%'-style pattern rules are a GNU make extension
diff --git a/src/Makefile.in b/src/Makefile.in
index efbd2d9..d144a8d 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -37,7 +37,8 @@ build_triplet = @build@
host_triplet = @host@
libexec_PROGRAMS = knot-zcompile$(EXEEXT) unittests$(EXEEXT) \
unittests-zcompile$(EXEEXT) \
- unittests-libknot-realdata$(EXEEXT) unittests-libknot$(EXEEXT)
+ unittests-libknot-realdata$(EXEEXT) unittests-libknot$(EXEEXT) \
+ unittests-xfr$(EXEEXT)
sbin_PROGRAMS = knotc$(EXEEXT) knotd$(EXEEXT)
subdir = src
DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
@@ -59,7 +60,7 @@ CONFIG_CLEAN_FILES =
CONFIG_CLEAN_VPATH_FILES =
LTLIBRARIES = $(noinst_LTLIBRARIES)
libknot_la_LIBADD =
-am_libknot_la_OBJECTS = libknot_error.lo utils.lo debug.lo \
+am_libknot_la_OBJECTS = libknot_error.lo utils.lo debug.lo conv.lo \
descriptor.lo tolower.lo query.lo response.lo packet.lo \
zone.lo zone-contents.lo zone-tree.lo zonedb.lo node.lo \
dname-table.lo hash-functions.lo cuckoo-hash-table.lo \
@@ -72,7 +73,8 @@ am_libknotd_la_OBJECTS = gatherer.lo stat.lo log.lo error.lo \
libknotd_la-cf-parse.lo libknotd_la-cf-lex.lo conf.lo \
logconf.lo process.lo dthreads.lo journal.lo socket.lo \
server.lo udp-handler.lo tcp-handler.lo xfr-handler.lo \
- zones.lo notify.lo zone-load.lo zone-dump.lo zone-dump-text.lo
+ zones.lo notify.lo zone-load.lo semantic-check.lo zone-dump.lo \
+ zone-dump-text.lo
libknotd_la_OBJECTS = $(am_libknotd_la_OBJECTS)
libknots_la_DEPENDENCIES = @LIBOBJS@
am_libknots_la_OBJECTS = slab.lo malloc.lo tap.lo lists.lo base32.lo \
@@ -113,8 +115,9 @@ am_unittests_libknot_OBJECTS = cuckoo_tests.$(OBJEXT) \
packet_tests.$(OBJEXT) query_tests.$(OBJEXT) \
edns_tests.$(OBJEXT) node_tests.$(OBJEXT) \
rdata_tests.$(OBJEXT) rrset_tests.$(OBJEXT) \
- zone_tests.$(OBJEXT) zone_tree_tests.$(OBJEXT) \
- zonedb_tests.$(OBJEXT) unittests_libknot.$(OBJEXT)
+ tsig_tests.$(OBJEXT) zone_tests.$(OBJEXT) \
+ zone_tree_tests.$(OBJEXT) zonedb_tests.$(OBJEXT) \
+ unittests_libknot.$(OBJEXT)
unittests_libknot_OBJECTS = $(am_unittests_libknot_OBJECTS)
unittests_libknot_DEPENDENCIES = libknot.la libknots.la @LIBOBJS@
am_unittests_libknot_realdata_OBJECTS = \
@@ -130,6 +133,10 @@ unittests_libknot_realdata_OBJECTS = \
$(am_unittests_libknot_realdata_OBJECTS)
unittests_libknot_realdata_DEPENDENCIES = libknot.la libknots.la \
@LIBOBJS@ $(am__empty)
+am_unittests_xfr_OBJECTS = xfr_tests.$(OBJEXT)
+unittests_xfr_OBJECTS = $(am_unittests_xfr_OBJECTS)
+unittests_xfr_DEPENDENCIES = libknotd.la libknot.la libknots.la \
+ @LIBOBJS@ $(am__empty)
am_unittests_zcompile_OBJECTS = zcompile-error.$(OBJEXT) \
zparser.$(OBJEXT) zlexer.$(OBJEXT) zcompile.$(OBJEXT) \
parser-util.$(OBJEXT) parser-descriptor.$(OBJEXT) \
@@ -163,13 +170,13 @@ SOURCES = $(libknot_la_SOURCES) $(libknotd_la_SOURCES) \
$(libknots_la_SOURCES) $(knot_zcompile_SOURCES) \
$(knotc_SOURCES) $(knotd_SOURCES) $(unittests_SOURCES) \
$(nodist_unittests_SOURCES) $(unittests_libknot_SOURCES) \
- $(unittests_libknot_realdata_SOURCES) \
+ $(unittests_libknot_realdata_SOURCES) $(unittests_xfr_SOURCES) \
$(unittests_zcompile_SOURCES)
DIST_SOURCES = $(libknot_la_SOURCES) $(libknotd_la_SOURCES) \
$(libknots_la_SOURCES) $(knot_zcompile_SOURCES) \
$(knotc_SOURCES) $(knotd_SOURCES) $(unittests_SOURCES) \
$(unittests_libknot_SOURCES) \
- $(unittests_libknot_realdata_SOURCES) \
+ $(unittests_libknot_realdata_SOURCES) $(unittests_xfr_SOURCES) \
$(unittests_zcompile_SOURCES)
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
am__vpath_adj = case $$p in \
@@ -383,8 +390,8 @@ unittests_SOURCES = \
tests/common/skiplist_tests.h \
tests/common/slab_tests.c \
tests/common/slab_tests.h \
- tests/common/fdset_tests.c \
- tests/common/fdset_tests.h \
+ tests/common/fdset_tests.c \
+ tests/common/fdset_tests.h \
tests/knot/conf_tests.c \
tests/knot/conf_tests.h \
tests/knot/dthreads_tests.c \
@@ -433,9 +440,11 @@ unittests_libknot_SOURCES = \
tests/libknot/libknot/rdata_tests.h \
tests/libknot/libknot/rrset_tests.c \
tests/libknot/libknot/rrset_tests.h \
+ tests/libknot/libknot/tsig_tests.c \
+ tests/libknot/libknot/tsig_tests.h \
tests/libknot/libknot/zone_tests.c \
tests/libknot/libknot/zone_tests.h \
- tests/libknot/libknot/zone_tree_tests.h\
+ tests/libknot/libknot/zone_tree_tests.h \
tests/libknot/libknot/zone_tree_tests.c \
tests/libknot/libknot/zonedb_tests.c \
tests/libknot/libknot/zonedb_tests.h \
@@ -452,6 +461,10 @@ unittests_zcompile_SOURCES = \
zcompile/parser-descriptor.c \
zcompile/tests/unittests_zp_main.c
+unittests_xfr_SOURCES = \
+ tests/xfr_tests.c \
+ tests/xfr_tests.h
+
nodist_unittests_SOURCES = \
tests/libknot/parsed_data.rc \
tests/libknot/raw_data_queries.rc \
@@ -469,6 +482,8 @@ libknot_la_SOURCES = \
libknot/util/debug.c \
libknot/util/debug.h \
libknot/util/utils.h \
+ libknot/util/conv.h \
+ libknot/util/conv.c \
libknot/util/descriptor.c \
libknot/util/tolower.h \
libknot/util/tolower.c \
@@ -610,6 +625,8 @@ libknotd_la_SOURCES = \
knot/server/zones.h \
knot/zone/zone-load.c \
knot/zone/zone-load.h \
+ knot/zone/semantic-check.c \
+ knot/zone/semantic-check.h \
knot/zone/zone-dump.c \
knot/zone/zone-dump-text.c \
knot/zone/zone-dump-text.h \
@@ -625,6 +642,7 @@ unittests_LDADD = libknotd.la libknots.la @LIBOBJS@
unittests_zcompile_LDADD = libknot.la libknots.la libknotd.la @LIBOBJS@
unittests_libknot_LDADD = libknot.la libknots.la @LIBOBJS@
unittests_libknot_realdata_LDADD = libknot.la libknots.la @LIBOBJS@
+unittests_xfr_LDADD = libknotd.la libknot.la libknots.la @LIBOBJS@
all: $(BUILT_SOURCES) config.h
$(MAKE) $(AM_MAKEFLAGS) all-am
@@ -806,6 +824,9 @@ unittests-libknot$(EXEEXT): $(unittests_libknot_OBJECTS) $(unittests_libknot_DEP
unittests-libknot-realdata$(EXEEXT): $(unittests_libknot_realdata_OBJECTS) $(unittests_libknot_realdata_DEPENDENCIES)
@rm -f unittests-libknot-realdata$(EXEEXT)
$(LINK) $(unittests_libknot_realdata_OBJECTS) $(unittests_libknot_realdata_LDADD) $(LIBS)
+unittests-xfr$(EXEEXT): $(unittests_xfr_OBJECTS) $(unittests_xfr_DEPENDENCIES)
+ @rm -f unittests-xfr$(EXEEXT)
+ $(LINK) $(unittests_xfr_OBJECTS) $(unittests_xfr_LDADD) $(LIBS)
unittests-zcompile$(EXEEXT): $(unittests_zcompile_OBJECTS) $(unittests_zcompile_DEPENDENCIES)
@rm -f unittests-zcompile$(EXEEXT)
$(LINK) $(unittests_zcompile_OBJECTS) $(unittests_zcompile_LDADD) $(LIBS)
@@ -824,6 +845,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/changesets.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conf.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conf_tests.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conv.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crc.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cuckoo-hash-table.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cuckoo_tests.Po@am__quote@
@@ -893,6 +915,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rrset.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rrset_tests.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rrset_tests_realdata.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/semantic-check.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/server.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/server_tests.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/skip-list.Plo@am__quote@
@@ -907,6 +930,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tolower.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsig-op.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsig.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsig_tests.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/udp-handler.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unittests_libknot.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unittests_libknot_realdata.Po@am__quote@
@@ -916,6 +940,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utils.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xfr-handler.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xfr-in.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xfr_tests.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zcompile-error.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zcompile.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zcompile_main.Po@am__quote@
@@ -977,6 +1002,13 @@ debug.lo: libknot/util/debug.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o debug.lo `test -f 'libknot/util/debug.c' || echo '$(srcdir)/'`libknot/util/debug.c
+conv.lo: libknot/util/conv.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT conv.lo -MD -MP -MF $(DEPDIR)/conv.Tpo -c -o conv.lo `test -f 'libknot/util/conv.c' || echo '$(srcdir)/'`libknot/util/conv.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/conv.Tpo $(DEPDIR)/conv.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libknot/util/conv.c' object='conv.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o conv.lo `test -f 'libknot/util/conv.c' || echo '$(srcdir)/'`libknot/util/conv.c
+
descriptor.lo: libknot/util/descriptor.c
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT descriptor.lo -MD -MP -MF $(DEPDIR)/descriptor.Tpo -c -o descriptor.lo `test -f 'libknot/util/descriptor.c' || echo '$(srcdir)/'`libknot/util/descriptor.c
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/descriptor.Tpo $(DEPDIR)/descriptor.Plo
@@ -1271,6 +1303,13 @@ zone-load.lo: knot/zone/zone-load.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o zone-load.lo `test -f 'knot/zone/zone-load.c' || echo '$(srcdir)/'`knot/zone/zone-load.c
+semantic-check.lo: knot/zone/semantic-check.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT semantic-check.lo -MD -MP -MF $(DEPDIR)/semantic-check.Tpo -c -o semantic-check.lo `test -f 'knot/zone/semantic-check.c' || echo '$(srcdir)/'`knot/zone/semantic-check.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/semantic-check.Tpo $(DEPDIR)/semantic-check.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='knot/zone/semantic-check.c' object='semantic-check.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o semantic-check.lo `test -f 'knot/zone/semantic-check.c' || echo '$(srcdir)/'`knot/zone/semantic-check.c
+
zone-dump.lo: knot/zone/zone-dump.c
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT zone-dump.lo -MD -MP -MF $(DEPDIR)/zone-dump.Tpo -c -o zone-dump.lo `test -f 'knot/zone/zone-dump.c' || echo '$(srcdir)/'`knot/zone/zone-dump.c
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/zone-dump.Tpo $(DEPDIR)/zone-dump.Plo
@@ -1845,6 +1884,20 @@ rrset_tests.obj: tests/libknot/libknot/rrset_tests.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rrset_tests.obj `if test -f 'tests/libknot/libknot/rrset_tests.c'; then $(CYGPATH_W) 'tests/libknot/libknot/rrset_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/libknot/rrset_tests.c'; fi`
+tsig_tests.o: tests/libknot/libknot/tsig_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT tsig_tests.o -MD -MP -MF $(DEPDIR)/tsig_tests.Tpo -c -o tsig_tests.o `test -f 'tests/libknot/libknot/tsig_tests.c' || echo '$(srcdir)/'`tests/libknot/libknot/tsig_tests.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/tsig_tests.Tpo $(DEPDIR)/tsig_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/libknot/tsig_tests.c' object='tsig_tests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o tsig_tests.o `test -f 'tests/libknot/libknot/tsig_tests.c' || echo '$(srcdir)/'`tests/libknot/libknot/tsig_tests.c
+
+tsig_tests.obj: tests/libknot/libknot/tsig_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT tsig_tests.obj -MD -MP -MF $(DEPDIR)/tsig_tests.Tpo -c -o tsig_tests.obj `if test -f 'tests/libknot/libknot/tsig_tests.c'; then $(CYGPATH_W) 'tests/libknot/libknot/tsig_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/libknot/tsig_tests.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/tsig_tests.Tpo $(DEPDIR)/tsig_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/libknot/libknot/tsig_tests.c' object='tsig_tests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o tsig_tests.obj `if test -f 'tests/libknot/libknot/tsig_tests.c'; then $(CYGPATH_W) 'tests/libknot/libknot/tsig_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/libknot/tsig_tests.c'; fi`
+
zone_tests.o: tests/libknot/libknot/zone_tests.c
@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT zone_tests.o -MD -MP -MF $(DEPDIR)/zone_tests.Tpo -c -o zone_tests.o `test -f 'tests/libknot/libknot/zone_tests.c' || echo '$(srcdir)/'`tests/libknot/libknot/zone_tests.c
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/zone_tests.Tpo $(DEPDIR)/zone_tests.Po
@@ -2055,6 +2108,20 @@ unittests_libknot_realdata.obj: tests/libknot/realdata/unittests_libknot_realdat
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o unittests_libknot_realdata.obj `if test -f 'tests/libknot/realdata/unittests_libknot_realdata.c'; then $(CYGPATH_W) 'tests/libknot/realdata/unittests_libknot_realdata.c'; else $(CYGPATH_W) '$(srcdir)/tests/libknot/realdata/unittests_libknot_realdata.c'; fi`
+xfr_tests.o: tests/xfr_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT xfr_tests.o -MD -MP -MF $(DEPDIR)/xfr_tests.Tpo -c -o xfr_tests.o `test -f 'tests/xfr_tests.c' || echo '$(srcdir)/'`tests/xfr_tests.c
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/xfr_tests.Tpo $(DEPDIR)/xfr_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/xfr_tests.c' object='xfr_tests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xfr_tests.o `test -f 'tests/xfr_tests.c' || echo '$(srcdir)/'`tests/xfr_tests.c
+
+xfr_tests.obj: tests/xfr_tests.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT xfr_tests.obj -MD -MP -MF $(DEPDIR)/xfr_tests.Tpo -c -o xfr_tests.obj `if test -f 'tests/xfr_tests.c'; then $(CYGPATH_W) 'tests/xfr_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/xfr_tests.c'; fi`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/xfr_tests.Tpo $(DEPDIR)/xfr_tests.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/xfr_tests.c' object='xfr_tests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o xfr_tests.obj `if test -f 'tests/xfr_tests.c'; then $(CYGPATH_W) 'tests/xfr_tests.c'; else $(CYGPATH_W) '$(srcdir)/tests/xfr_tests.c'; fi`
+
unittests_zp_main.o: zcompile/tests/unittests_zp_main.c
@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT unittests_zp_main.o -MD -MP -MF $(DEPDIR)/unittests_zp_main.Tpo -c -o unittests_zp_main.o `test -f 'zcompile/tests/unittests_zp_main.c' || echo '$(srcdir)/'`zcompile/tests/unittests_zp_main.c
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/unittests_zp_main.Tpo $(DEPDIR)/unittests_zp_main.Po
diff --git a/src/common/WELL1024a.c b/src/common/WELL1024a.c
index dddf75e..0d1526b 100644
--- a/src/common/WELL1024a.c
+++ b/src/common/WELL1024a.c
@@ -10,6 +10,7 @@
#include <string.h>
#include <stdlib.h>
#include <time.h>
+#include <sys/time.h>
#include <stdio.h>
#include "WELL1024a.h"
@@ -87,11 +88,27 @@ double tls_rand()
/* Initialize seed from system PRNG generator. */
unsigned init[WELL1024_WIDTH];
FILE *fp = fopen("/dev/urandom", "r");
- for (unsigned i = 0; i < WELL1024_WIDTH; ++i) {
- int rc = fread(&init[i], sizeof(unsigned), 1, fp);
- rc = rc;
+ if (fp == NULL) {
+ fp = fopen("/dev/random", "r");
+ }
+ if (fp == NULL) {
+ fprintf(stderr, "error: PRNG: cannot seed from "
+ "/dev/urandom, seeding from local time\n");
+ struct timeval tv;
+ if (gettimeofday(&tv, NULL) == 0) {
+ memcpy(init, &tv, sizeof(struct timeval));
+ } else {
+ /* Last resort. */
+ time_t tm = time(NULL);
+ memcpy(init, &tm, sizeof(time_t));
+ }
+ } else {
+ for (unsigned i = 0; i < WELL1024_WIDTH; ++i) {
+ int rc = fread(&init[i], sizeof(unsigned), 1, fp);
+ rc = rc;
+ }
+ fclose(fp);
}
- fclose(fp);
/* Initialize PRNG state. */
s = InitWELLRNG1024a(init);
diff --git a/src/common/acl.c b/src/common/acl.c
index e73c4dd..a1c3a6e 100644
--- a/src/common/acl.c
+++ b/src/common/acl.c
@@ -62,18 +62,14 @@ static int acl_compare(void *k1, void *k2)
if (a1->len == sizeof(struct sockaddr_in6)) {
/* Compare address. */
- /*! \todo Maybe use memcmp()? */
- ldiff = 0;
- const unsigned int *a6 = (const unsigned int *)&a1->addr6.sin6_addr;
- const unsigned int *b6 = (const unsigned int *)&a2->addr6.sin6_addr;
- for (int i = 0; i < (sizeof(struct in6_addr)/ sizeof(int)) ; ++i) {
- ldiff = a6[i] - b6[i];
- if (ldiff < 0) {
- return -1;
- }
- if (ldiff > 0) {
- return 1;
- }
+ ldiff = memcmp(&a1->addr6.sin6_addr,
+ &a2->addr6.sin6_addr,
+ sizeof(struct in6_addr));
+ if (ldiff < 0) {
+ return -1;
+ }
+ if (ldiff > 0) {
+ return 1;
}
/* Port = 0 means any port match. */
@@ -138,38 +134,43 @@ void acl_delete(acl_t **acl)
*acl = 0;
}
-int acl_create(acl_t *acl, const sockaddr_t* addr, acl_rule_t rule)
+int acl_create(acl_t *acl, const sockaddr_t* addr, acl_rule_t rule, void *val)
{
- if (!acl || !addr || rule < 0) {
+ if (!acl || !addr) {
return ACL_ERROR;
}
/* Insert into skip list. */
- sockaddr_t *key = malloc(sizeof(sockaddr_t));
- memcpy(key, addr, sizeof(sockaddr_t));
+ acl_key_t *key = malloc(sizeof(acl_key_t));
+ memcpy(&key->addr, addr, sizeof(sockaddr_t));
+ sockaddr_update(&key->addr);
+ key->rule = rule;
+ key->val = val;
- skip_insert(acl->rules, key, (void*)((ssize_t)rule + 1), 0);
+ skip_insert(acl->rules, &key->addr, key, 0);
return ACL_ACCEPT;
}
-int acl_match(acl_t *acl, sockaddr_t* addr)
+int acl_match(acl_t *acl, const sockaddr_t* addr, acl_key_t **key)
{
if (!acl || !addr) {
return ACL_ERROR;
}
- /* Return default rule if not found.
- * Conversion to the same length integer is made,
- * but we can be sure, that the value range is within <-1,1>.
- */
- ssize_t val = ((ssize_t)skip_find(acl->rules, addr)) - 1;
- if (val < 0) {
+ acl_key_t *found = skip_find(acl->rules, (void*)addr);
+
+ /* Set stored value if exists. */
+ if (key != 0) {
+ *key = found;
+ }
+
+ /* Return appropriate rule. */
+ if (!found) {
return acl->default_rule;
}
- /* Return stored rule if found. */
- return (int)val;
+ return found->rule;
}
int acl_truncate(acl_t *acl)
@@ -179,7 +180,7 @@ int acl_truncate(acl_t *acl)
}
/* Destroy all rules. */
- skip_destroy_list(&acl->rules, free, 0);
+ skip_destroy_list(&acl->rules, 0, free);
return ACL_ACCEPT;
}
diff --git a/src/common/acl.h b/src/common/acl.h
index c79db7f..1b7e97f 100644
--- a/src/common/acl.h
+++ b/src/common/acl.h
@@ -42,11 +42,18 @@ typedef enum acl_rule_t {
/*! \brief ACL structure. */
typedef struct acl_t {
- acl_rule_t default_rule;
- skip_list_t *rules;
- const char name[];
+ acl_rule_t default_rule; /*!< \brief Default rule. */
+ skip_list_t *rules; /*!< \brief Data container. */
+ const char name[]; /*!< \brief ACL semantic name. */
} acl_t;
+/*! \brief Single ACL value. */
+typedef struct acl_key_t {
+ sockaddr_t addr; /*!< \brief Address for comparison. */
+ acl_rule_t rule; /*!< \brief Rule for address. */
+ void *val; /*!< \brief Associated value (or NULL). */
+} acl_key_t;
+
/*!
* \brief Create a new ACL.
*
@@ -71,25 +78,27 @@ void acl_delete(acl_t **acl);
* \todo Support address subnets.
*
* \param acl Pointer to ACL instance.
- * \param addr IP address (will be duplicated).
- * \param rule Rule.
+ * \param addr IP address.
+ * \param rule Rule for given address.
+ * \param val Value to be stored for given address (or NULL).
*
* \retval ACL_ACCEPT if successful.
* \retval ACP_ERROR on error.
*/
-int acl_create(acl_t *acl, const sockaddr_t* addr, acl_rule_t rule);
+int acl_create(acl_t *acl, const sockaddr_t* addr, acl_rule_t rule, void *val);
/*!
* \brief Match address against ACL.
*
* \param acl Pointer to ACL instance.
* \param addr IP address.
+ * \param key Set to related key or NULL if not found.
*
- * \retval ACL_ACCEPT if the address is accepted.
- * \retval ACL_DENY if the address is not accepted.
+ * \retval Address rule if the address is accepted.
+ * \retval Default rule if the address is not accepted.
* \retval ACP_ERROR on error.
*/
-int acl_match(acl_t *acl, sockaddr_t* addr);
+int acl_match(acl_t *acl, const sockaddr_t* addr, acl_key_t **key);
/*!
* \brief Truncate ACL.
diff --git a/src/common/errors.c b/src/common/errors.c
index f1e650d..3b770e9 100644
--- a/src/common/errors.c
+++ b/src/common/errors.c
@@ -64,6 +64,7 @@ int _map_errno(int fallback_value, int arg0, ...)
/* Error code matches with mapped. */
if (c == errno) {
/* Return negative value of the code. */
+ va_end(ap);
return -abs(c);
}
}
diff --git a/src/common/evsched.c b/src/common/evsched.c
index 4e56028..c4a6786 100644
--- a/src/common/evsched.c
+++ b/src/common/evsched.c
@@ -187,7 +187,7 @@ int evsched_event_finished(evsched_t *s)
int evsched_schedule(evsched_t *s, event_t *ev, uint32_t dt)
{
- if (!s || !ev || dt < 0) {
+ if (!s || !ev) {
return -1;
}
@@ -276,12 +276,12 @@ int evsched_cancel(evsched_t *s, event_t *ev)
return -1;
}
- /* Lock calendar. */
- pthread_mutex_lock(&s->mx);
-
/* Make sure not running. */
pthread_mutex_lock(&s->rl);
+ /* Lock calendar. */
+ pthread_mutex_lock(&s->mx);
+
/* Find in list. */
event_t *n = 0;
int found = 0;
@@ -297,13 +297,13 @@ int evsched_cancel(evsched_t *s, event_t *ev)
rem_node(&ev->n);
}
- /* Enable running events. */
- pthread_mutex_unlock(&s->rl);
-
/* Unlock calendar. */
pthread_cond_signal(&s->notify);
pthread_mutex_unlock(&s->mx);
+ /* Enable running events. */
+ pthread_mutex_unlock(&s->rl);
+
return 0;
}
diff --git a/src/common/fdset_kqueue.c b/src/common/fdset_kqueue.c
index c7199ae..b0daa33 100644
--- a/src/common/fdset_kqueue.c
+++ b/src/common/fdset_kqueue.c
@@ -14,8 +14,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <config.h>
-
#ifdef HAVE_KQUEUE
#include <stdint.h>
diff --git a/src/common/libtap/tap.c b/src/common/libtap/tap.c
index 61e0528..8b660fe 100644
--- a/src/common/libtap/tap.c
+++ b/src/common/libtap/tap.c
@@ -51,38 +51,41 @@ vstrdupf (const char *fmt, va_list args) {
}
int
-vok_at_loc (const char *file, int line, int test, const char *fmt,
+vok_at_loc (const char *file, int line, int test, int verbose, const char *fmt,
va_list args)
{
- char *name = vstrdupf(fmt, args);
- printf("%sok %d", test ? "" : "not ", ++current_test);
- if (*name)
- printf(" - %s", name);
- if (todo_mesg) {
- printf(" # TODO");
- if (*todo_mesg)
- printf(" %s", todo_mesg);
+ if (verbose) {
+ char *name = vstrdupf(fmt, args);
+ printf("%sok %d", test ? "" : "not ", ++current_test);
+ if (*name)
+ printf(" - %s", name);
+ if (todo_mesg) {
+ printf(" # TODO");
+ if (*todo_mesg)
+ printf(" %s", todo_mesg);
+ }
+ printf("\n");
+ if (!test) {
+ if (*name)
+ diag(" Failed%s test '%s'\n at %s line %d.",
+ todo_mesg ? " (TODO)" : "", name, file, line);
+ else
+ diag(" Failed%s test at %s line %d.",
+ todo_mesg ? " (TODO)" : "", file, line);
+ if (!todo_mesg)
+ failed_tests++;
+
+ free(name);
+ }
}
- printf("\n");
- if (!test) {
- if (*name)
- diag(" Failed%s test '%s'\n at %s line %d.",
- todo_mesg ? " (TODO)" : "", name, file, line);
- else
- diag(" Failed%s test at %s line %d.",
- todo_mesg ? " (TODO)" : "", file, line);
- if (!todo_mesg)
- failed_tests++;
- }
- free(name);
return test;
}
int
-ok_at_loc (const char *file, int line, int test, const char *fmt, ...) {
+ok_at_loc (const char *file, int line, int verbose, int test, const char *fmt, ...) {
va_list args;
va_start(args, fmt);
- vok_at_loc(file, line, test, fmt, args);
+ vok_at_loc(file, line, test, verbose, fmt, args);
va_end(args);
return test;
}
@@ -102,7 +105,7 @@ is_at_loc (const char *file, int line, const char *got, const char *expected,
int test = eq(got, expected);
va_list args;
va_start(args, fmt);
- vok_at_loc(file, line, test, fmt, args);
+ vok_at_loc(file, line, test, 1, fmt, args);
va_end(args);
if (!test) {
diag(" got: '%s'", got);
@@ -113,12 +116,13 @@ is_at_loc (const char *file, int line, const char *got, const char *expected,
int
isnt_at_loc (const char *file, int line, const char *got, const char *expected,
+ int verbose,
const char *fmt, ...)
{
int test = ne(got, expected);
va_list args;
va_start(args, fmt);
- vok_at_loc(file, line, test, fmt, args);
+ vok_at_loc(file, line, test, verbose, fmt, args);
va_end(args);
if (!test) {
diag(" got: '%s'", got);
@@ -152,7 +156,7 @@ cmp_ok_at_loc (const char *file, int line, int a, const char *op, int b,
: diag("unrecognized operator '%s'", op);
va_list args;
va_start(args, fmt);
- vok_at_loc(file, line, test, fmt, args);
+ vok_at_loc(file, line, test, 1, fmt, args);
va_end(args);
if (!test) {
diag(" %d", a);
@@ -278,7 +282,7 @@ tap_test_died (int status) {
int
like_at_loc (int for_match, const char *file, int line, const char *got,
- const char *expected, const char *fmt, ...)
+ const char *expected, int verbose, const char *fmt, ...)
{
int test;
regex_t re;
@@ -295,7 +299,7 @@ like_at_loc (int for_match, const char *file, int line, const char *got,
test = for_match ? !err : err;
va_list args;
va_start(args, fmt);
- vok_at_loc(file, line, test, fmt, args);
+ vok_at_loc(file, line, test, verbose, fmt, args);
va_end(args);
if (!test) {
if (for_match) {
diff --git a/src/common/libtap/tap.h b/src/common/libtap/tap.h
index 2e89b90..4522fbb 100644
--- a/src/common/libtap/tap.h
+++ b/src/common/libtap/tap.h
@@ -22,17 +22,17 @@
#include <stdarg.h>
#define NO_PLAN -1
-#define ok(...) ok_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL)
+#define ok(...) ok_at_loc(__FILE__, __LINE__, 1, __VA_ARGS__, NULL)
#define pass(...) ok(1, ## __VA_ARGS__)
#define fail(...) ok(0, ## __VA_ARGS__)
#define is(...) is_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL)
#define isnt(...) isnt_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL)
#define cmp_ok(...) cmp_ok_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL)
-int vok_at_loc (const char *file, int line, int test, const char *fmt,
+int vok_at_loc (const char *file, int line, int test, int verbose, const char *fmt,
va_list args);
void plan (int tests);
-int ok_at_loc (const char *file, int line, int test, const char *fmt,
+int ok_at_loc (const char *file, int line, int test, int verbose, const char *fmt,
...);
int diag (const char *fmt, ...);
int note (const char *fmt, ...);
@@ -41,9 +41,10 @@ void skippy (int n, const char *fmt, ...);
void ctodo (int ignore, const char *fmt, ...);
void cendtodo (void);
int is_at_loc (const char *file, int line, const char *got,
- const char *expected, const char *fmt, ...);
+ const char *expected,
+ const char *fmt, ...);
int isnt_at_loc (const char *file, int line, const char *got,
- const char *expected, const char *fmt, ...);
+ const char *expected, int verbose, const char *fmt, ...);
int cmp_ok_at_loc (const char *file, int line, int a, const char *op,
int b, const char *fmt, ...);
@@ -55,6 +56,7 @@ int cmp_ok_at_loc (const char *file, int line, int a, const char *op,
#define unlike(...) like_at_loc(0, __FILE__, __LINE__, __VA_ARGS__, NULL)
int like_at_loc (int for_match, const char *file, int line,
const char *got, const char *expected,
+ int verbose,
const char *fmt, ...);
#endif
@@ -64,8 +66,9 @@ int like_at_loc (int for_match, const char *file, int line,
#define todo(...) ctodo(0, ## __VA_ARGS__, NULL)
#define endtodo cendtodo()
-#define dies_ok(code, ...) dies_ok_common(code, 1, ## __VA_ARGS__)
-#define lives_ok(code, ...) dies_ok_common(code, 0, ## __VA_ARGS__)
+#define dies_ok(code, ...) dies_ok_common(code, 1, 1, ## __VA_ARGS__)
+#define lives_ok(code, ...) dies_ok_common(code, 0, 1, ## __VA_ARGS__)
+#define lives_ok_silent(code, ...) dies_ok_common(code, 0, 0, ## __VA_ARGS__)
#ifdef _WIN32
#define dies_ok_common(...) \
@@ -75,7 +78,7 @@ int like_at_loc (int for_match, const char *file, int line,
#include <sys/types.h>
#include <sys/wait.h>
int tap_test_died (int status);
-#define dies_ok_common(code, for_death, ...) \
+#define dies_ok_common(code, for_death, verbose, ...) \
do { \
tap_test_died(1); \
int cpid = fork(); \
@@ -95,7 +98,7 @@ int tap_test_died (int status);
} \
int it_died = tap_test_died(0); \
if (!it_died) {code} \
- ok(for_death ? it_died : !it_died, ## __VA_ARGS__); \
+ ok_at_loc(__FILE__, __LINE__, verbose, for_death ? it_died : !it_died, ## __VA_ARGS__); \
} while (0)
#endif
#endif
diff --git a/src/knot/conf/cf-parse.y b/src/knot/conf/cf-parse.y
index d793865..9939540 100644
--- a/src/knot/conf/cf-parse.y
+++ b/src/knot/conf/cf-parse.y
@@ -282,30 +282,39 @@ system:
keys:
KEYS '{'
| keys TEXT TSIG_ALGO_NAME TEXT ';' {
+ /* Check algorithm length. */
+ if (tsig_alg_digest_length($3.alg) == 0) {
+ cf_error(scanner, "unsupported digest algorithm");
+ }
+
/* Normalize to FQDN */
char *fqdn = $2.t;
- if (fqdn[strlen(fqdn) - 1] != '.') {
- char* tmp = malloc(strlen(fqdn) + 1 + 1); /* '.', '\0' */
- if (!tmp) {
+ size_t fqdnl = strlen(fqdn);
+ if (fqdn[fqdnl - 1] != '.') {
+ /*! \todo Oddly, it requires memory aligned to 4B */
+ fqdnl = ((fqdnl + 2)/4+1)*4; /* '.', '\0' */
+ char* tmpdn = malloc(fqdnl);
+ if (!tmpdn) {
cf_error(scanner, "out of memory when allocating string");
free(fqdn);
- fqdn = 0;
+ fqdn = NULL;
+ fqdnl = 0;
} else {
- strcpy(tmp, fqdn);
- strcat(tmp, ".");
+ strncpy(tmpdn, fqdn, fqdnl);
+ strncat(tmpdn, ".", 1);
free(fqdn);
- fqdn = tmp;
+ fqdn = tmpdn;
+ fqdnl = strlen(fqdn);
}
}
- if (!conf_key_exists(scanner, fqdn)) {
- knot_dname_t *dname = knot_dname_new_from_str(fqdn, strlen(fqdn), 0);
+ if (fqdn != NULL && !conf_key_exists(scanner, fqdn)) {
+ knot_dname_t *dname = knot_dname_new_from_str(fqdn, fqdnl, 0);
if (!dname) {
char buf[512];
snprintf(buf, sizeof(buf), "key name '%s' not in valid domain "
"name format", fqdn);
cf_error(scanner, buf);
- free(fqdn);
free($4.t);
} else {
conf_key_t *k = malloc(sizeof(conf_key_t));
@@ -315,12 +324,12 @@ keys:
k->k.secret = $4.t;
add_tail(&new_config->keys, &k->n);
++new_config->key_count;
- free(fqdn);
}
} else {
- free(fqdn);
free($4.t);
}
+
+ free(fqdn);
}
remote_start:
@@ -387,6 +396,7 @@ remote:
} else {
conf_key_add(scanner, &this_remote->key, $3.t);
}
+ free($3.t);
}
;
@@ -469,20 +479,28 @@ zone_start: TEXT {
this_zone->notify_retries = 0; // Default policy applies
this_zone->ixfr_fslimit = -1; // Default policy applies
this_zone->dbsync_timeout = -1; // Default policy applies
- this_zone->name = $1.t;
// Append mising dot to ensure FQDN
- size_t nlen = strlen(this_zone->name);
- if (this_zone->name[nlen - 1] != '.') {
- this_zone->name = realloc(this_zone->name, nlen + 1 + 1);
- strcat(this_zone->name, ".");
+ char *name = $1.t;
+ size_t nlen = strlen(name);
+ if (name[nlen - 1] != '.') {
+ this_zone->name = malloc(nlen + 2);
+ if (this_zone->name != NULL) {
+ memcpy(this_zone->name, name, nlen);
+ this_zone->name[nlen] = '.';
+ this_zone->name[nlen + 1] = '\0';
+ }
+ free(name);
+ } else {
+ this_zone->name = name; /* Already FQDN */
}
/* Check domain name. */
- knot_dname_t *dn = knot_dname_new_from_str(this_zone->name,
- nlen + 1,
- 0);
- if (dn == 0) {
+ knot_dname_t *dn = NULL;
+ if (this_zone->name != NULL) {
+ dn = knot_dname_new_from_str(this_zone->name, nlen + 1, 0);
+ }
+ if (dn == NULL) {
free(this_zone->name);
free(this_zone);
cf_error(scanner, "invalid zone origin");
diff --git a/src/knot/conf/conf.c b/src/knot/conf/conf.c
index 4e2d665..636901b 100644
--- a/src/knot/conf/conf.c
+++ b/src/knot/conf/conf.c
@@ -207,6 +207,7 @@ static int conf_process(conf_t *conf)
conf->pidfile = strcdup(conf->storage, "/" PID_FILE);
// Postprocess zones
+ int ret = KNOTD_EOK;
node *n = 0;
WALK_LIST (n, conf->zones) {
conf_zone_t *zone = (conf_zone_t*)n;
@@ -240,33 +241,48 @@ static int conf_process(conf_t *conf)
zone->file = strcpath(zone->file);
// Create zone db filename
+ size_t zname_len = strlen(zone->name);
size_t stor_len = strlen(conf->storage);
- size_t size = stor_len + strlen(zone->name) + 4; // db/,\0
+ size_t size = stor_len + zname_len + 4; // db/,\0
char *dest = malloc(size);
- strcpy(dest, conf->storage);
+ if (dest == NULL) {
+ zone->db = NULL; /* Not enough memory. */
+ ret = KNOTD_ENOMEM; /* Error report. */
+ continue;
+ }
+
+ /* Since we have already allocd dest to accomodate
+ * storage/zname length strcpy is safe. */
+ strncpy(dest, conf->storage, stor_len + 1);
if (conf->storage[stor_len - 1] != '/') {
- strcat(dest, "/");
+ strncat(dest, "/", 1);
}
- strcat(dest, zone->name);
- strcat(dest, "db");
+ strncat(dest, zone->name, zname_len);
+ strncat(dest, "db", 2);
zone->db = dest;
// Create IXFR db filename
stor_len = strlen(conf->storage);
- size = stor_len + strlen(zone->name) + 9; // diff.db/,\0
+ size = stor_len + zname_len + 9; // diff.db/,\0
dest = malloc(size);
- strcpy(dest, conf->storage);
+ if (dest == NULL) {
+ zone->ixfr_db = NULL; /* Not enough memory. */
+ ret = KNOTD_ENOMEM; /* Error report. */
+ continue;
+ }
+ strncpy(dest, conf->storage, stor_len + 1);
if (conf->storage[stor_len - 1] != '/') {
- strcat(dest, "/");
+ strncat(dest, "/", 1);
}
- strcat(dest, zone->name);
- strcat(dest, "diff.db");
+ const char *dbext = "diff.db";
+ strncat(dest, zone->name, zname_len);
+ strncat(dest, dbext, strlen(dbext));
zone->ixfr_db = dest;
}
- return 0;
+ return ret;
}
/*
@@ -384,10 +400,6 @@ static int conf_strparser(conf_t *conf, const char *src)
// {
// Hook new configuration
new_config = conf;
- if (src == 0) {
- pthread_mutex_unlock(&_parser_lock);
- return KNOTD_ENOENT;
- }
// Parse config
_parser_res = KNOTD_EOK;
@@ -662,18 +674,19 @@ int conf_open(const char* path)
char* strcdup(const char *s1, const char *s2)
{
if (!s1 || !s2) {
- return 0;
+ return NULL;
}
size_t slen = strlen(s1);
- size_t nlen = slen + strlen(s2) + 1;
+ size_t s2len = strlen(s2);
+ size_t nlen = slen + s2len + 1;
char* dst = malloc(nlen);
- if (!dst) {
- return 0;
+ if (dst == NULL) {
+ return NULL;
}
memcpy(dst, s1, slen);
- strcpy(dst + slen, s2); // With trailing '\0'
+ strncpy(dst + slen, s2, s2len + 1); // With trailing '\0'
return dst;
}
@@ -681,7 +694,7 @@ char* strcpath(char *path)
{
// NULL path
if (!path) {
- return 0;
+ return NULL;
}
// Remote trailing slash
@@ -691,23 +704,41 @@ char* strcpath(char *path)
}
// Expand '~'
- char* tild_p = strchr(path,'~');
- if (tild_p != 0) {
+ char* remainder = strchr(path,'~');
+ if (remainder != NULL) {
// Get full path
- char *tild_exp = getenv("HOME");
- size_t tild_len = strlen(tild_exp);
+ char *tild_exp_unsafe = getenv("HOME");
+ if (tild_exp_unsafe == NULL) {
+ return NULL;
+ }
+ // Sanitize
+ size_t tild_len = strlen(tild_exp_unsafe);
+ char *tild_exp = malloc(tild_len);
+ if (tild_exp == NULL) {
+ return NULL;
+ }
+ strncpy(tild_exp, tild_exp_unsafe, tild_len + 1);
if (tild_exp[tild_len - 1] == '/') {
tild_exp[--tild_len] = '\0';
}
// Expand
char *npath = malloc(plen + tild_len + 1);
+ if (npath == NULL) {
+ free(tild_exp);
+ return NULL;
+ }
npath[0] = '\0';
- strncpy(npath, path, (size_t)(tild_p - path));
- strcat(npath, tild_exp);
- strcat(npath, tild_p + 1);
+ strncpy(npath, path, (size_t)(remainder - path));
+ strncat(npath, tild_exp, tild_len);
+
+ // Append remainder
+ ++remainder;
+ size_t remainder_len = strlen(remainder);
+ strncat(npath, remainder, remainder_len);
free(path);
path = npath;
+ free(tild_exp);
}
return path;
diff --git a/src/knot/ctl/knotc_main.c b/src/knot/ctl/knotc_main.c
index 16c3863..f472303 100644
--- a/src/knot/ctl/knotc_main.c
+++ b/src/knot/ctl/knotc_main.c
@@ -29,7 +29,6 @@
#include "knot/other/error.h"
#include "knot/ctl/process.h"
#include "knot/conf/conf.h"
-#include "knot/conf/logconf.h"
#include "knot/zone/zone-load.h"
/*! \brief Controller constants. */
diff --git a/src/knot/main.c b/src/knot/main.c
index 4091055..d4e10b9 100644
--- a/src/knot/main.c
+++ b/src/knot/main.c
@@ -24,7 +24,6 @@
#include "knot/common.h"
#include "knot/other/error.h"
#include "knot/server/server.h"
-#include "zcompile/zcompile.h"
#include "knot/ctl/process.h"
#include "knot/conf/conf.h"
#include "knot/conf/logconf.h"
@@ -123,6 +122,7 @@ int main(int argc, char **argv)
// Register service and signal handler
struct sigaction emptyset;
+ memset(&emptyset, 0, sizeof(struct sigaction));
emptyset.sa_handler = interrupt_handle;
sigemptyset(&emptyset.sa_mask);
emptyset.sa_flags = 0;
@@ -172,7 +172,7 @@ int main(int argc, char **argv)
// Append ending slash
if (cwbuf[cwbuflen - 1] != '/') {
- cwbuf = strcat(cwbuf, "/");
+ cwbuf = strncat(cwbuf, "/", 1);
}
// Assemble path to config file
@@ -263,7 +263,7 @@ int main(int argc, char **argv)
if (sig_req_reload) {
log_server_info("Reloading configuration...\n");
sig_req_reload = 0;
- int cf_ret = cf_ret = conf_open(config_fn);
+ int cf_ret = conf_open(config_fn);
switch (cf_ret) {
case KNOTD_EOK:
log_server_info("Configuration "
diff --git a/src/knot/other/debug.h b/src/knot/other/debug.h
index aa80373..8b01c85 100644
--- a/src/knot/other/debug.h
+++ b/src/knot/other/debug.h
@@ -37,7 +37,7 @@
//#define KNOTD_THREADS_DEBUG
//#define KNOTD_JOURNAL_DEBUG
//#define KNOTD_NET_DEBUG
-//#define KNOTD_ZONES_DEBUG
+#define KNOTD_ZONES_DEBUG
//#define KNOTD_XFR_DEBUG
//#define KNOTD_NOTIFY_DEBUG
//#define KNOTD_ZDUMP_DEBUG
diff --git a/src/knot/other/log.c b/src/knot/other/log.c
index 9318d5f..00bcea3 100644
--- a/src/knot/other/log.c
+++ b/src/knot/other/log.c
@@ -260,8 +260,11 @@ int log_msg(logsrc_t src, int level, const char *msg, ...)
/* Prepend prefix. */
int plen = strlen(prefix);
+ if (plen > buflen) {
+ return KNOTD_ENOMEM;
+ }
if (plen > 0) {
- strcpy(buf, prefix);
+ strncpy(buf, prefix, plen + 1);
buf += plen;
buflen -= plen;
}
diff --git a/src/knot/server/dthreads.c b/src/knot/server/dthreads.c
index 9707e57..9d1e69d 100644
--- a/src/knot/server/dthreads.c
+++ b/src/knot/server/dthreads.c
@@ -571,7 +571,10 @@ int dt_resize(dt_unit_t *unit, int size)
}
pthread_join(thread->_thr, 0);
+ /* Thread is already joined and flagged, but anyway... */
+ lock_thread_rw(thread);
thread->state = ThreadJoined;
+ unlock_thread_rw(thread);
// Delete thread
dt_delete_thread(&thread);
@@ -711,7 +714,9 @@ int dt_join(dt_unit_t *unit)
pthread_join(thread->_thr, 0);
dbg_dt("dthreads: [%p] %s: reclaimed\n",
thread, __func__);
+ lock_thread_rw(thread);
thread->state = ThreadJoined;
+ unlock_thread_rw(thread);
} else {
unlock_thread_rw(thread);
}
@@ -812,19 +817,19 @@ int dt_setprio(dthread_t *thread, int prio)
int ret = pthread_attr_setschedpolicy(&thread->_attr, policy);
// Update priority
- if (ret >= 0) {
+ if (ret == 0) {
struct sched_param sp;
sp.sched_priority = prio;
ret = pthread_attr_setschedparam(&thread->_attr, &sp);
}
/* Map error codes. */
- if (ret < 0) {
+ if (ret != 0) {
dbg_dt("dthreads: [%p] %s(%d): failed",
thread, __func__, prio);
/* Map "not supported". */
- if (ret == ENOTSUP) {
+ if (errno == ENOTSUP) {
return KNOTD_ENOTSUP;
}
@@ -927,7 +932,9 @@ int dt_compact(dt_unit_t *unit)
pthread_join(thread->_thr, 0);
dbg_dt("dthreads: [%p] %s: thread reclaimed\n",
thread, __func__);
+ lock_thread_rw(thread);
thread->state = ThreadJoined;
+ unlock_thread_rw(thread);
} else {
unlock_thread_rw(thread);
}
diff --git a/src/knot/server/journal.c b/src/knot/server/journal.c
index 651f0f3..438332a 100644
--- a/src/knot/server/journal.c
+++ b/src/knot/server/journal.c
@@ -232,17 +232,11 @@ journal_t* journal_open(const char *fn, size_t fslimit, uint16_t bflags)
fl.l_len = 0;
fl.l_pid = getpid();
- /* Check file. */
- struct stat st;
- if (stat(fn, &st) < 0) {
- return 0;
- }
-
- /* Open journal file for r/w. */
+ /* Open journal file for r/w (returns error if not exists). */
int fd = open(fn, O_RDWR);
if (fd < 0) {
dbg_journal("journal: failed to open file '%s'\n", fn);
- return 0;
+ return NULL;
}
/* Attempt to lock. */
@@ -266,18 +260,28 @@ journal_t* journal_open(const char *fn, size_t fslimit, uint16_t bflags)
/* Read maximum number of entries. */
uint16_t max_nodes = 512;
if (!sfread(&max_nodes, sizeof(uint16_t), fd)) {
+ dbg_journal_detail("journal: cannot read max_nodes\n");
fcntl(fd, F_SETLK, &fl);
close(fd);
- return 0;
+ return NULL;
+ }
+
+ /* Check max_nodes, but this is riddiculous. */
+ if (max_nodes == 0) {
+ dbg_journal_detail("journal: max_nodes is invalid\n");
+ fcntl(fd, F_SETLK, &fl);
+ close(fd);
+ return NULL;
}
/* Allocate journal structure. */
const size_t node_len = sizeof(journal_node_t);
journal_t *j = malloc(sizeof(journal_t) + max_nodes * node_len);
if (!j) {
+ dbg_journal_detail("journal: cannot allocate journal\n");
fcntl(fd, F_SETLK, &fl);
close(fd);
- return 0;
+ return NULL;
}
j->qhead = j->qtail = 0;
j->fd = fd;
@@ -286,34 +290,57 @@ journal_t* journal_open(const char *fn, size_t fslimit, uint16_t bflags)
/* Load node queue state. */
if (!sfread(&j->qhead, sizeof(uint16_t), fd)) {
+ dbg_journal_detail("journal: cannot read qhead\n");
fcntl(fd, F_SETLK, &fl);
close(fd);
free(j);
- return 0;
+ return NULL;
}
/* Load queue tail. */
if (!sfread(&j->qtail, sizeof(uint16_t), fd)) {
+ dbg_journal_detail("journal: cannot read qtail\n");
fcntl(fd, F_SETLK, &fl);
close(fd);
free(j);
- return 0;
+ return NULL;
+ }
+
+ /* Check head + tail */
+ if (j->qtail > max_nodes || j->qhead > max_nodes) {
+ dbg_journal_detail("journal: queue pointers corrupted\n");
+ fcntl(fd, F_SETLK, &fl);
+ close(fd);
+ free(j);
+ return NULL;
}
/* Load empty segment descriptor. */
if (!sfread(&j->free, node_len, fd)) {
+ dbg_journal_detail("journal: cannot read free segment ptr\n");
fcntl(fd, F_SETLK, &fl);
close(fd);
free(j);
- return 0;
+ return NULL;
}
/* Read journal descriptors table. */
if (!sfread(&j->nodes, max_nodes * node_len, fd)) {
+ dbg_journal_detail("journal: cannot read node table\n");
fcntl(fd, F_SETLK, &fl);
close(fd);
free(j);
- return 0;
+ return NULL;
+ }
+
+ /* Get journal file size. */
+ struct stat st;
+ if (stat(fn, &st) < 0) {
+ dbg_journal_detail("journal: cannot get journal fsize\n");
+ fcntl(fd, F_SETLK, &fl);
+ close(fd);
+ free(j);
+ return NULL;
}
/* Set file size. */
@@ -346,7 +373,7 @@ journal_t* journal_open(const char *fn, size_t fslimit, uint16_t bflags)
fcntl(fd, F_SETLK, &fl);
close(fd);
free(j);
- return 0;
+ return NULL;
}
}
diff --git a/src/knot/server/notify.c b/src/knot/server/notify.c
index 3966b26..8736372 100644
--- a/src/knot/server/notify.c
+++ b/src/knot/server/notify.c
@@ -123,13 +123,23 @@ int notify_create_response(knot_packet_t *request, uint8_t *buffer,
CHECK_ALLOC_LOG(response, KNOTD_ENOMEM);
/* Set maximum packet size. */
- knot_packet_set_max_size(response, *size);
- knot_response_init_from_query(response, request);
+ int rc = knot_packet_set_max_size(response, *size);
+ if (rc == KNOT_EOK) {
+ rc = knot_response_init_from_query(response, request);
+ }
+
+ /* Aggregated result check. */
+ if (rc != KNOT_EOK) {
+ dbg_notify("%s: failed to init response packet: %s",
+ "notify_create_response", knot_strerror(rc));
+ knot_packet_free(&response);
+ return KNOTD_EINVAL;
+ }
// TODO: copy the SOA in Answer section
uint8_t *wire = NULL;
size_t wire_size = 0;
- int rc = knot_packet_to_wire(response, &wire, &wire_size);
+ rc = knot_packet_to_wire(response, &wire, &wire_size);
if (rc != KNOT_EOK) {
knot_packet_free(&response);
return rc;
@@ -178,7 +188,7 @@ static int notify_check_and_schedule(knot_nameserver_t *nameserver,
/* Check ACL for notify-in. */
zonedata_t *zd = (zonedata_t *)knot_zone_data(zone);
if (from) {
- if (acl_match(zd->notify_in, from) == ACL_DENY) {
+ if (acl_match(zd->notify_in, from, 0) == ACL_DENY) {
/* rfc1996: Ignore request and report incident. */
char straddr[SOCKADDR_STRLEN];
sockaddr_tostr(from, straddr, sizeof(straddr));
@@ -230,8 +240,7 @@ int notify_process_request(knot_nameserver_t *ns,
dbg_notify("notify: parsing rest of the packet\n");
if (notify->parsed < notify->size) {
- ret = knot_packet_parse_rest(notify);
- if (ret != KNOT_EOK) {
+ if (knot_packet_parse_rest(notify) != KNOT_EOK) {
dbg_notify("notify: failed to parse NOTIFY query\n");
knot_ns_error_response(ns, knot_packet_id(notify),
KNOT_RCODE_FORMERR, buffer,
@@ -310,6 +319,7 @@ int notify_process_response(knot_nameserver_t *nameserver,
if (!match) {
log_server_notice("No pending NOTIFY query found for ID=%u\n",
pkt_id);
+ pthread_mutex_unlock(&zd->lock);
return KNOTD_ERROR;
}
diff --git a/src/knot/server/server.c b/src/knot/server/server.c
index 80db35d..6b61d61 100644
--- a/src/knot/server/server.c
+++ b/src/knot/server/server.c
@@ -32,10 +32,8 @@
#include "libknot/nameserver/name-server.h"
#include "knot/stat/stat.h"
#include "libknot/zone/zonedb.h"
-#include "knot/zone/zone-load.h"
#include "libknot/dname.h"
#include "knot/conf/conf.h"
-#include "knot/server/zones.h"
/*! \brief Event scheduler loop. */
static int evsched_run(dthread_t *thread)
@@ -50,11 +48,6 @@ static int evsched_run(dthread_t *thread)
event_t *ev = 0;
while((ev = evsched_next(s))) {
- /* Error. */
- if (!ev) {
- return KNOTD_ERROR;
- }
-
/* Process termination event. */
if (ev->type == EVSCHED_TERM) {
evsched_event_finished(s);
@@ -128,26 +121,31 @@ static void server_remove_iface(iface_t *iface)
static int server_init_iface(iface_t *new_if, conf_iface_t *cfg_if)
{
/* Initialize interface. */
+ int ret = 0;
+ int sock = 0;
char errbuf[128];
int opt = 1024 * 1024;
int snd_opt = 1024 * 1024;
memset(new_if, 0, sizeof(iface_t));
/* Create UDP socket. */
- int sock = socket_create(cfg_if->family, SOCK_DGRAM);
- if (sock <= 0) {
+ ret = socket_create(cfg_if->family, SOCK_DGRAM);
+ if (ret < 0) {
strerror_r(errno, errbuf, sizeof(errbuf));
log_server_error("Could not create UDP socket: %s.\n",
errbuf);
- return sock;
+ return ret;
+ } else {
+ sock = ret;
}
- if (socket_bind(sock, cfg_if->family,
- cfg_if->address, cfg_if->port) < 0) {
+
+ ret = socket_bind(sock, cfg_if->family, cfg_if->address, cfg_if->port);
+ if (ret < 0) {
socket_close(sock);
log_server_error("Could not bind to "
"UDP interface %s port %d.\n",
cfg_if->address, cfg_if->port);
- return knot_map_errno(EACCES, EINVAL, ENOMEM);
+ return ret;
}
new_if->fd[UDP_ID] = sock;
@@ -163,14 +161,15 @@ static int server_init_iface(iface_t *new_if, conf_iface_t *cfg_if)
}
/* Create TCP socket. */
- int ret = 0;
- sock = socket_create(cfg_if->family, SOCK_STREAM);
- if (sock <= 0) {
+ ret = socket_create(cfg_if->family, SOCK_STREAM);
+ if (ret < 0) {
socket_close(new_if->fd[UDP_ID]);
strerror_r(errno, errbuf, sizeof(errbuf));
log_server_error("Could not create TCP socket: %s.\n",
errbuf);
- return sock;
+ return ret;
+ } else {
+ sock = ret;
}
ret = socket_bind(sock, cfg_if->family, cfg_if->address, cfg_if->port);
@@ -357,7 +356,7 @@ static int server_bind_handlers(server_t *server)
h->iface = iface;
/* Save pointer. */
- rcu_set_pointer(&iface->handler[UDP_ID], h);
+ iface->handler[UDP_ID] = h; /* No need for cmpxchg */
dbg_server("server: creating UDP socket handlers for '%s:%d'\n",
iface->addr, iface->port);
@@ -372,7 +371,7 @@ static int server_bind_handlers(server_t *server)
h->iface = iface;
/* Save pointer. */
- rcu_set_pointer(&iface->handler[TCP_ID], h);
+ iface->handler[TCP_ID] = h; /* No need for cmpxchg */
dbg_server("server: creating TCP socket handlers for '%s:%d'\n",
iface->addr, iface->port);
}
diff --git a/src/knot/server/socket.c b/src/knot/server/socket.c
index d3dd664..67f4a5f 100644
--- a/src/knot/server/socket.c
+++ b/src/knot/server/socket.c
@@ -78,17 +78,14 @@ int socket_connect(int fd, const char *addr, unsigned short port)
/* Connect. */
ret = -1;
- if (addr) {
- ret = connect(fd, saddr, addrlen);
- if (ret < 0) {
- ret = knot_map_errno(EACCES, EADDRINUSE, EAGAIN,
- ECONNREFUSED, EISCONN);
- }
- } else {
- ret = KNOTD_EINVAL;
+ ret = connect(fd, saddr, addrlen);
+ if (ret < 0) {
+ ret = knot_map_errno(EACCES, EADDRINUSE, EAGAIN,
+ ECONNREFUSED, EISCONN);
}
+
/* Free addresses. */
freeaddrinfo(res);
diff --git a/src/knot/server/tcp-handler.c b/src/knot/server/tcp-handler.c
index db58fef..2fdc354 100644
--- a/src/knot/server/tcp-handler.c
+++ b/src/knot/server/tcp-handler.c
@@ -28,14 +28,17 @@
#include "common/sockaddr.h"
#include "common/skip-list.h"
#include "common/fdset.h"
+#include "common/WELL1024a.h"
#include "knot/common.h"
#include "knot/server/tcp-handler.h"
#include "knot/server/xfr-handler.h"
+#include "knot/server/zones.h"
#include "libknot/nameserver/name-server.h"
#include "knot/other/error.h"
-#include "knot/stat/stat.h"
#include "libknot/util/wire.h"
-#include "knot/server/zones.h"
+
+/* Defines */
+#define TCP_BUFFER_SIZE 65535 /*! Do not change, as it is used for maximum DNS/TCP packet size. */
/*! \brief TCP worker data. */
typedef struct tcp_worker_t {
@@ -47,6 +50,14 @@ typedef struct tcp_worker_t {
/*
* Forward decls.
*/
+#define TCP_THROTTLE_LO 10 /*!< Minimum recovery time on errors. */
+#define TCP_THROTTLE_HI 50 /*!< Maximum recovery time on errors. */
+
+/*! \brief Calculate TCP throttle time (random). */
+static inline int tcp_throttle() {
+ //(TCP_THROTTLE_LO + (int)(tls_rand() * TCP_THROTTLE_HI));
+ return (rand() % TCP_THROTTLE_HI) + TCP_THROTTLE_LO;
+}
/*! \brief Wrapper for TCP send. */
static int xfr_send_cb(int session, sockaddr_t *addr, uint8_t *msg, size_t msglen)
@@ -63,7 +74,7 @@ static int xfr_send_cb(int session, sockaddr_t *addr, uint8_t *msg, size_t msgle
* \param w Associated I/O event.
* \param revents Returned events.
*/
-static void tcp_handle(tcp_worker_t *w, int fd)
+static void tcp_handle(tcp_worker_t *w, int fd, uint8_t *qbuf, size_t qbuf_maxlen)
{
if (fd < 0 || !w || !w->ioh) {
dbg_net("tcp: tcp_handle(%p, %d) - invalid parameters\n", w, fd);
@@ -86,8 +97,6 @@ static void tcp_handle(tcp_worker_t *w, int fd)
}
/* Receive data. */
- uint8_t qbuf[65535]; /*! \todo This may be problematic. */
- size_t qbuf_maxlen = sizeof(qbuf);
int n = tcp_recv(fd, qbuf, qbuf_maxlen, &addr);
if (n <= 0) {
dbg_net("tcp: client on fd=%d disconnected\n", fd);
@@ -134,7 +143,8 @@ static void tcp_handle(tcp_worker_t *w, int fd)
/* Query types. */
case KNOT_QUERY_NORMAL:
- res = knot_ns_answer_normal(ns, packet, qbuf, &resp_len);
+ //res = knot_ns_answer_normal(ns, packet, qbuf, &resp_len);
+ res = zones_normal_query_answer(ns, packet, &addr, qbuf, &resp_len);
break;
case KNOT_QUERY_IXFR:
res = xfr_request_init(&xfr, XFR_TYPE_IOUT, XFR_FLAG_TCP, packet);
@@ -228,9 +238,21 @@ static int tcp_accept(int fd)
/* Evaluate connection. */
if (incoming < 0) {
- if (errno != EINTR) {
+ int en = errno;
+ if (en != EINTR) {
log_server_error("Cannot accept connection "
"(%d).\n", errno);
+ if (en == EMFILE || en == ENFILE ||
+ en == ENOBUFS || en == ENOMEM) {
+ int throttle = tcp_throttle();
+ log_server_error("Throttling TCP connection pool"
+ " for %d seconds because of "
+ "too many open descriptors "
+ "or lack of memory.\n",
+ throttle);
+ sleep(throttle);
+ }
+
}
} else {
dbg_net("tcp: accepted connection fd=%d\n", incoming);
@@ -242,16 +264,16 @@ static int tcp_accept(int fd)
tcp_worker_t* tcp_worker_create()
{
tcp_worker_t *w = malloc(sizeof(tcp_worker_t));
- if (!w) {
+ if (w == NULL) {
dbg_net("tcp: out of memory when creating worker\n");
- return 0;
+ return NULL;
}
/* Create signal pipes. */
memset(w, 0, sizeof(tcp_worker_t));
if (pipe(w->pipe) < 0) {
free(w);
- return 0;
+ return NULL;
}
/* Create fdset. */
@@ -260,6 +282,7 @@ tcp_worker_t* tcp_worker_create()
close(w->pipe[0]);
close(w->pipe[1]);
free(w);
+ return NULL;
}
fdset_add(w->fdset, w->pipe[0], OS_EV_READ);
@@ -361,13 +384,14 @@ int tcp_loop_master(dthread_t *thread)
{
iohandler_t *handler = (iohandler_t *)thread->data;
dt_unit_t *unit = thread->unit;
- tcp_worker_t **workers = handler->data;
/* Check socket. */
- if (!handler || handler->fd < 0 || !workers) {
+ if (!handler || handler->fd < 0 || handler->data == NULL) {
dbg_net("tcp: failed to initialize master thread\n");
return KNOTD_EINVAL;
}
+
+ tcp_worker_t **workers = handler->data;
/* Accept connections. */
int id = 0;
@@ -407,6 +431,13 @@ int tcp_loop_worker(dthread_t *thread)
if (!w) {
return KNOTD_EINVAL;
}
+
+ /* Allocate buffer for requests. */
+ uint8_t *qbuf = malloc(TCP_BUFFER_SIZE);
+ if (qbuf == NULL) {
+ dbg_net("tcp: failed to allocate buffers for TCP worker\n");
+ return KNOTD_EINVAL;
+ }
/* Accept clients. */
dbg_net_verb("tcp: worker %p started\n", w);
@@ -443,7 +474,7 @@ int tcp_loop_worker(dthread_t *thread)
fdset_add(w->fdset, client, OS_EV_READ);
} else {
/* Handle other events. */
- tcp_handle(w, it.fd);
+ tcp_handle(w, it.fd, qbuf, TCP_BUFFER_SIZE);
}
/* Check if next exists. */
@@ -455,6 +486,7 @@ int tcp_loop_worker(dthread_t *thread)
}
/* Stop whole unit. */
+ free(qbuf);
dbg_net_verb("tcp: worker %p finished\n", w);
tcp_worker_free(w);
return KNOTD_EOK;
diff --git a/src/knot/server/udp-handler.c b/src/knot/server/udp-handler.c
index f7ae550..b202669 100644
--- a/src/knot/server/udp-handler.c
+++ b/src/knot/server/udp-handler.c
@@ -14,6 +14,11 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE /* Required for RTLD_DEFAULT. */
+#endif
+
+#include <dlfcn.h>
#include <config.h>
#include <time.h>
#include <unistd.h>
@@ -21,6 +26,7 @@
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/poll.h>
+#include <sys/syscall.h>
#include <netinet/in.h>
#include <string.h>
#include <assert.h>
@@ -39,6 +45,14 @@
#include "knot/server/zones.h"
#include "knot/server/notify.h"
+/* Check for sendmmsg syscall. */
+#ifdef SYS_sendmmsg
+#define ENABLE_SENDMMSG 1
+#endif
+
+/*! \brief Pointer to selected UDP master implementation. */
+static int (*_udp_master)(dthread_t *, stat_t *) = 0;
+
///*! \brief Wrapper for UDP send. */
//static int xfr_send_udp(int session, sockaddr_t *addr, uint8_t *msg, size_t msglen)
//{
@@ -96,8 +110,10 @@ int udp_handle(int fd, uint8_t *qbuf, size_t qbuflen, size_t *resp_len,
/* Query types. */
case KNOT_QUERY_NORMAL:
- res = knot_ns_answer_normal(ns, packet, qbuf,
- resp_len);
+ res = zones_normal_query_answer(ns, packet, addr, qbuf,
+ resp_len);
+// res = knot_ns_answer_normal(ns, packet, qbuf,
+// resp_len);
break;
case KNOT_QUERY_AXFR:
/* RFC1034, p.28 requires reliable transfer protocol.
@@ -133,8 +149,10 @@ int udp_handle(int fd, uint8_t *qbuf, size_t qbuflen, size_t *resp_len,
* IXFR/UDP.
*/
knot_packet_set_qtype(packet, KNOT_RRTYPE_SOA);
- res = knot_ns_answer_normal(ns, packet, qbuf,
- resp_len);
+ res = zones_normal_query_answer(ns, packet, addr,
+ qbuf, resp_len);
+// res = knot_ns_answer_normal(ns, packet, qbuf,
+// resp_len);
break;
// /* Process IXFR over UDP. */
// res = xfr_request_init(&xfr, XFR_TYPE_IOUT, XFR_FLAG_UDP, packet);
@@ -192,8 +210,12 @@ int udp_handle(int fd, uint8_t *qbuf, size_t qbuflen, size_t *resp_len,
static inline int udp_master_recvfrom(dthread_t *thread, stat_t *thread_stat)
{
iohandler_t *h = (iohandler_t *)thread->data;
+ if (h == NULL || h->server == NULL || h->server->nameserver == NULL) {
+ dbg_net("udp: invalid parameters for udp_master_recvfrom\n");
+ return KNOTD_EINVAL;
+ }
+
knot_nameserver_t *ns = h->server->nameserver;
- int sock = dup(h->fd);
sockaddr_t addr;
if (sockaddr_init(&addr, h->type) != KNOTD_EOK) {
@@ -203,6 +225,7 @@ static inline int udp_master_recvfrom(dthread_t *thread, stat_t *thread_stat)
return KNOTD_ENOTSUP;
}
+ int sock = dup(h->fd);
uint8_t qbuf[SOCKET_MTU_SZ];
struct msghdr msg;
memset(&msg, 0, sizeof(struct msghdr));
@@ -271,6 +294,66 @@ static inline int udp_master_recvfrom(dthread_t *thread, stat_t *thread_stat)
#ifdef ENABLE_RECVMMSG
#ifdef MSG_WAITFORONE
+
+/*! \brief Pointer to selected UDP send implementation. */
+static int (*_send_mmsg)(int, sockaddr_t *, struct mmsghdr *, size_t) = 0;
+
+/*!
+ * \brief Send multiple packets.
+ *
+ * Basic, sendto() based implementation.
+ */
+int udp_sendto(int sock, sockaddr_t * addrs, struct mmsghdr *msgs, size_t count)
+{
+ for (unsigned i = 0; i < count; ++i) {
+
+ const size_t resp_len = msgs[i].msg_len;
+ if (resp_len > 0) {
+ dbg_net("udp: on fd=%d, sending answer size=%zd.\n",
+ sock, resp_len);
+
+ // Send datagram
+ sockaddr_t *addr = addrs + i;
+ struct iovec *cvec = msgs[i].msg_hdr.msg_iov;
+ int res = sendto(sock, cvec->iov_base, resp_len,
+ 0, addr->ptr, addr->len);
+
+ // Check result
+ if (res != (int)resp_len) {
+ dbg_net("udp: sendto(): failed: %d - %d.\n",
+ res, errno);
+ }
+ }
+ }
+
+ return KNOTD_EOK;
+}
+
+#ifdef ENABLE_SENDMMSG
+/*! \brief sendmmsg() syscall interface. */
+static inline int sendmmsg(int fd, struct mmsghdr *mmsg, unsigned vlen,
+ unsigned flags)
+{
+ return syscall(SYS_sendmmsg, fd, mmsg, vlen, flags, NULL);
+}
+
+/*!
+ * \brief Send multiple packets.
+ *
+ * sendmmsg() implementation.
+ */
+int udp_sendmmsg(int sock, sockaddr_t *_, struct mmsghdr *msgs, size_t count)
+{
+ UNUSED(_);
+ dbg_net("udp: sending multiple responses\n");
+ if (sendmmsg(sock, msgs, count, 0) < 0) {
+ return KNOTD_ERROR;
+ }
+
+ return KNOTD_EOK;
+}
+#endif
+
static inline int udp_master_recvmmsg(dthread_t *thread, stat_t *thread_stat)
{
iohandler_t *h = (iohandler_t *)thread->data;
@@ -330,32 +413,20 @@ static inline int udp_master_recvmmsg(dthread_t *thread, stat_t *thread_stat)
addrs + i, ns);
if (ret == KNOTD_EOK) {
msgs[i].msg_len = resp_len;
+ iov[i].iov_len = resp_len;
} else {
msgs[i].msg_len = 0;
+ iov[i].iov_len = 0;
}
}
/* Gather results. */
- /*! \todo Implement with sendmmsg() when it's ready. */
+ _send_mmsg(sock, addrs, msgs, n);
+
+ /* Reset iov buffer size. */
for (unsigned i = 0; i < n; ++i) {
- const size_t resp_len = msgs[i].msg_len;
- if (resp_len > 0) {
- dbg_net("udp: on fd=%d, sending answer size=%zd.\n",
- sock, resp_len);
-
- // Send datagram
- sockaddr_t *addr = addrs + i;
- struct iovec *cvec = msgs[i].msg_hdr.msg_iov;
- int res = sendto(sock, cvec->iov_base, resp_len,
- 0, addr->ptr, addr->len);
-
- // Check result
- if (res != (int)resp_len) {
- dbg_net("udp: sendto(): failed: %d - %d.\n",
- res, errno);
- }
- }
+ iov[i].iov_len = SOCKET_MTU_SZ;
}
}
@@ -370,6 +441,33 @@ static inline int udp_master_recvmmsg(dthread_t *thread, stat_t *thread_stat)
#endif
#endif
+/*! \brief Initialize UDP master routine on run-time. */
+void __attribute__ ((constructor)) udp_master_init()
+{
+ /* Initialize defaults. */
+ _udp_master = udp_master_recvfrom;
+
+ /* Optimized functions. */
+#ifdef ENABLE_RECVMMSG
+#ifdef MSG_WAITFORONE
+ /* Check for recvmmsg() support. */
+ if (dlsym(RTLD_DEFAULT, "recvmmsg") != 0) {
+ _udp_master = udp_master_recvmmsg;
+ }
+
+ /* Check for sendmmsg() support. */
+#ifdef ENABLE_SENDMMSG
+ _send_mmsg = udp_sendto;
+ sendmmsg(0, 0, 0, 0); /* Just check if syscall exists */
+ if (errno != ENOSYS) {
+ _send_mmsg = udp_sendmmsg;
+ }
+#endif /* ENABLE_SENDMMSG */
+#endif /* MSG_WAITFORONE */
+#endif /* ENABLE_RECVMMSG */
+}
+
+
int udp_master(dthread_t *thread)
{
iohandler_t *handler = (iohandler_t *)thread->data;
@@ -384,7 +482,6 @@ int udp_master(dthread_t *thread)
/* Set socket options. */
int flag = 1;
#ifndef DISABLE_IPV6
-#
if (handler->type == AF_INET6) {
/* Disable dual-stack for performance reasons. */
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag));
@@ -416,20 +513,8 @@ int udp_master(dthread_t *thread)
/* Execute proper handler. */
dbg_net_verb("udp: thread started (worker %p).\n", thread);
int ret = KNOTD_EOK;
-
-#ifdef ENABLE_RECVMMSG
-/* Check if MSG_WAITFORONE is supported. */
-#ifdef MSG_WAITFORONE
- ret = udp_master_recvmmsg(thread, thread_stat);
-#else /* Don't have MSG_WAITFORONE */
- ret = udp_master_recvfrom(thread, thread_stat);
-#endif
-
-#else /* Don't have ENABLE_RECVMMSG */
- ret = udp_master_recvfrom(thread, thread_stat);
-#endif
-
+ ret = _udp_master(thread, thread_stat);
stat_free(thread_stat);
dbg_net_verb("udp: worker %p finished.\n", thread);
diff --git a/src/knot/server/xfr-handler.c b/src/knot/server/xfr-handler.c
index 45817cb..cec3f53 100644
--- a/src/knot/server/xfr-handler.c
+++ b/src/knot/server/xfr-handler.c
@@ -17,7 +17,6 @@
#include <config.h>
#include <unistd.h>
#include <fcntl.h>
-#include <errno.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <sys/socket.h>
@@ -40,6 +39,10 @@
#include "libknot/util/error.h"
#include "libknot/tsig-op.h"
#include "common/evsched.h"
+#include "common/WELL1024a.h"
+
+/* Constants */
+#define XFR_BUFFER_SIZE 65535 /*! Do not change this - maximum value for UDP packet length. */
void xfr_interrupt(xfrhandler_t *h)
{
@@ -220,8 +223,12 @@ static int xfr_xfrin_cleanup(xfrworker_t *w, knot_ns_xfr_t *data)
switch(data->type) {
case XFR_TYPE_AIN:
if (data->data) {
+ xfrin_constructed_zone_t *constr_zone =
+ (xfrin_constructed_zone_t *)data->data;
knot_zone_contents_deep_free(
- (knot_zone_contents_t **)&data->data, 0);
+ &(constr_zone->contents), 0);
+ xfrin_free_orphan_rrsigs(&(constr_zone->rrsigs));
+ free(data->data);
data->data = 0;
}
break;
@@ -261,6 +268,7 @@ static int xfr_xfrin_finalize(xfrworker_t *w, knot_ns_xfr_t *data)
// }
int ret = KNOTD_EOK;
+ knot_changesets_t *chs = NULL;
switch(data->type) {
case XFR_TYPE_AIN:
@@ -307,7 +315,7 @@ static int xfr_xfrin_finalize(xfrworker_t *w, knot_ns_xfr_t *data)
}
}
/* Free changesets, but not the data. */
- knot_changesets_t *chs = (knot_changesets_t *)data->data;
+ chs = (knot_changesets_t *)data->data;
knot_free_changesets(&chs);
/* CLEANUP */
// free(chs->sets);
@@ -336,14 +344,14 @@ static int xfr_xfrin_finalize(xfrworker_t *w, knot_ns_xfr_t *data)
static int xfr_prepare_tsig(knot_ns_xfr_t *xfr, knot_key_t *key)
{
int ret = KNOT_EOK;
- xfr->tsig_key = 0; /*key;*/ /*!< \todo [TSIG] DISABLED */
-// xfr->tsig_size = tsig_wire_maxsize(key);
-// xfr->digest_max_size = tsig_alg_digest_length(
-// key->algorithm);
-// xfr->digest = malloc(xfr->digest_max_size);
-// memset(xfr->digest, 0 , xfr->digest_max_size);
-// dbg_xfr("xfr: found TSIG key (MAC len=%zu), adding to transfer\n",
-// xfr->digest_max_size);
+ xfr->tsig_key = key;
+ xfr->tsig_size = tsig_wire_maxsize(key);
+ xfr->digest_max_size = tsig_alg_digest_length(
+ key->algorithm);
+ xfr->digest = malloc(xfr->digest_max_size);
+ memset(xfr->digest, 0 , xfr->digest_max_size);
+ dbg_xfr("xfr: found TSIG key (MAC len=%zu), adding to transfer\n",
+ xfr->digest_max_size);
return ret;
}
@@ -353,108 +361,149 @@ static int xfr_prepare_tsig(knot_ns_xfr_t *xfr, knot_key_t *key)
*/
static int xfr_check_tsig(knot_ns_xfr_t *xfr, knot_rcode_t *rcode)
{
- /*!< \todo [TSIG] DISABLED */
- return knot_packet_parse_rest(xfr->query);
-
-// /* Parse rest of the packet. */
-// int ret = KNOT_EOK;
-// knot_packet_t *qry = xfr->query;
-// knot_key_t *key = 0;
-// const knot_rrset_t *tsig_rr = 0;
-// ret = knot_packet_parse_rest(qry);
-// if (ret == KNOT_EOK) {
+ /* Parse rest of the packet. */
+ int ret = KNOT_EOK;
+ knot_packet_t *qry = xfr->query;
+ knot_key_t *key = 0;
+ const knot_rrset_t *tsig_rr = 0;
+ ret = knot_packet_parse_rest(qry);
+ if (ret == KNOT_EOK) {
-// /* Find TSIG key name from query. */
-// const knot_dname_t* kname = 0;
-// int tsig_pos = knot_packet_additional_rrset_count(qry) - 1;
-// if (tsig_pos >= 0) {
-// tsig_rr = knot_packet_additional_rrset(qry, tsig_pos);
-// if (knot_rrset_type(tsig_rr) == KNOT_RRTYPE_TSIG) {
-// dbg_xfr("xfr: found TSIG in AR\n");
-// kname = knot_rrset_owner(tsig_rr);
-// ret = KNOT_TSIG_EBADKEY;
-// }
-// }
-// if (!kname) {
-// dbg_xfr("xfr: TSIG not found in AR\n");
-// }
-
-// /* Find configured key for claimed key name. */
-// conf_key_t *ck = 0;
-// WALK_LIST(ck, conf()->keys) {
-// if (!kname) {
-// break;
-// }
-// /* Compare stored keys to claimed. */
-// if (knot_dname_compare(ck->k.name,
-// kname) == 0) {
-// dbg_xfr("xfr: found claimed "
-// "TSIG key for "
-// "comparison\n");
-// key = &ck->k;
-// break;
-// }
-// }
-
-// /* Validate with TSIG. */
-// if (key) {
-// /* Prepare variables for TSIG */
-// xfr_prepare_tsig(xfr, key);
+ /* Find TSIG key name from query. */
+ const knot_dname_t* kname = 0;
+ int tsig_pos = knot_packet_additional_rrset_count(qry) - 1;
+ if (tsig_pos >= 0) {
+ tsig_rr = knot_packet_additional_rrset(qry, tsig_pos);
+ if (knot_rrset_type(tsig_rr) == KNOT_RRTYPE_TSIG) {
+ dbg_xfr("xfr: found TSIG in AR\n");
+ kname = knot_rrset_owner(tsig_rr);
+ } else {
+ tsig_rr = 0;
+ }
+ }
+ if (!kname) {
+ dbg_xfr("xfr: TSIG not found in AR\n");
+ char *name = knot_dname_to_str(
+ knot_zone_name(xfr->zone));
+ log_answer_warning("Unauthorized request for XFR '%s/"
+ "OUT'. (TSIG)\n", name);
+ free(name);
+
+ // return REFUSED
+ xfr->tsig_key = 0;
+ *rcode = KNOT_RCODE_REFUSED;
+ return KNOT_EXFRREFUSED;
+ }
+ if (tsig_rr) {
+ tsig_algorithm_t alg = tsig_rdata_alg(tsig_rr);
+ if (tsig_alg_digest_length(alg) == 0) {
+ log_server_info("Unsupported digest algorithm "
+ "requested, treating as "
+ "bad key.\n");
+ /*! \todo [TSIG] It is unclear from RFC if I
+ * should treat is as a bad key
+ * or some other error.
+ */
+ *rcode = KNOT_RCODE_NOTAUTH;
+ xfr->tsig_key = NULL;
+ xfr->tsig_rcode = KNOT_TSIG_RCODE_BADKEY;
+ xfr->tsig_prev_time_signed =
+ tsig_rdata_time_signed(tsig_rr);
+ return KNOT_TSIG_EBADKEY;
+ }
+ }
+
+ /* Evaluate configured key for claimed key name.*/
+ key = xfr->tsig_key; /* Expects already set key (check_zone) */
+ xfr->tsig_key = 0;
+ if (key && kname && knot_dname_compare(key->name, kname) == 0) {
+ dbg_xfr("xfr: found claimed TSIG key for comparison\n");
+ } else {
+ /*! \todo These ifs are redundant. */
+ *rcode = KNOT_RCODE_NOTAUTH;
+ /* TSIG is mandatory if configured for interface. */
+ if (key && !kname) {
+ dbg_xfr("xfr: TSIG key is mandatory for "
+ "this interface\n");
+ ret = KNOT_TSIG_EBADKEY;
+ xfr->tsig_rcode = KNOT_TSIG_RCODE_BADKEY;
+ }
-// /* Copy MAC from query. */
-// dbg_xfr("xfr: validating TSIG from query\n");
-// const uint8_t* mac = tsig_rdata_mac(tsig_rr);
-// size_t mac_len = tsig_rdata_mac_length(tsig_rr);
-// if (mac_len > xfr->digest_max_size) {
-// ret = KNOT_EMALF;
-// dbg_xfr("xfr: MAC length %zu exceeds digest "
-// "maximum size %zu\n",
-// mac_len, xfr->digest_max_size);
-// } else {
-// memcpy(xfr->digest, mac, mac_len);
-// xfr->digest_size = mac_len;
-
-// /* Check query TSIG. */
-// ret = knot_tsig_server_check(
-// tsig_rr,
-// knot_packet_wireformat(qry),
-// knot_packet_size(qry),
-// key);
-// dbg_xfr("knot_tsig_server_check() returned %s\n",
-// knot_strerror(ret));
-// }
+ /* Configured, but doesn't match. */
+ if (kname) {
+ dbg_xfr("xfr: no claimed key configured, "
+ "treating as bad key\n");
+ ret = KNOT_TSIG_EBADKEY;
+ xfr->tsig_rcode = KNOT_TSIG_RCODE_BADKEY;
+ }
+
+ key = 0; /* Invalidate, ret already set to BADKEY */
+ }
+
+ /* Validate with TSIG. */
+ if (key) {
+ /* Prepare variables for TSIG */
+ xfr_prepare_tsig(xfr, key);
-// /* Evaluate TSIG check results. */
-// switch(ret) {
-// case KNOT_EOK:
-// *rcode = KNOT_RCODE_NOERROR;
-// break;
-// case KNOT_TSIG_EBADKEY:
-// case KNOT_TSIG_EBADSIG:
-// // delete the TSIG key so that the error
-// // response is not signed
-// xfr->tsig_key = NULL;
-// case KNOT_TSIG_EBADTIME:
-// /*! \note [TSIG] Set TSIG rcode in TSIG RR. */
+ /* Copy MAC from query. */
+ dbg_xfr("xfr: validating TSIG from query\n");
+ const uint8_t* mac = tsig_rdata_mac(tsig_rr);
+ size_t mac_len = tsig_rdata_mac_length(tsig_rr);
+ if (mac_len > xfr->digest_max_size) {
+ ret = KNOT_EMALF;
+ dbg_xfr("xfr: MAC length %zu exceeds digest "
+ "maximum size %zu\n",
+ mac_len, xfr->digest_max_size);
+ } else {
+ memcpy(xfr->digest, mac, mac_len);
+ xfr->digest_size = mac_len;
-// *rcode = KNOT_RCODE_NOTAUTH;
-// break;
-// case KNOT_EMALF:
-// *rcode = KNOT_RCODE_FORMERR;
-// break;
-// default:
-// *rcode = KNOT_RCODE_SERVFAIL;
-// }
-// } else {
-// dbg_xfr("xfr: no claimed key configured, "
-// "treating as bad key\n");
-// }
-// } else {
-// dbg_xfr("xfr: failed to parse rest of the packet\n");
-// *rcode = KNOT_RCODE_FORMERR;
-// }
+ /* Check query TSIG. */
+ ret = knot_tsig_server_check(
+ tsig_rr,
+ knot_packet_wireformat(qry),
+ knot_packet_size(qry),
+ key);
+ dbg_xfr("knot_tsig_server_check() returned %s\n",
+ knot_strerror(ret));
+ }
+
+ /* Evaluate TSIG check results. */
+ switch(ret) {
+ case KNOT_EOK:
+ *rcode = KNOT_RCODE_NOERROR;
+ break;
+ case KNOT_TSIG_EBADKEY:
+ xfr->tsig_rcode = KNOT_TSIG_RCODE_BADKEY;
+ xfr->tsig_key = NULL;
+ *rcode = KNOT_RCODE_NOTAUTH;
+ break;
+ case KNOT_TSIG_EBADSIG:
+ xfr->tsig_rcode = KNOT_TSIG_RCODE_BADSIG;
+ xfr->tsig_key = NULL;
+ *rcode = KNOT_RCODE_NOTAUTH;
+ break;
+ case KNOT_TSIG_EBADTIME:
+ xfr->tsig_rcode = KNOT_TSIG_RCODE_BADTIME;
+ // store the time signed from the query
+ assert(tsig_rr != NULL);
+ xfr->tsig_prev_time_signed =
+ tsig_rdata_time_signed(tsig_rr);
+ *rcode = KNOT_RCODE_NOTAUTH;
+ break;
+ case KNOT_EMALF:
+ *rcode = KNOT_RCODE_FORMERR;
+ break;
+ default:
+ *rcode = KNOT_RCODE_SERVFAIL;
+ }
+ }
+ } else {
+ dbg_xfr("xfr: failed to parse rest of the packet\n");
+ *rcode = KNOT_RCODE_FORMERR;
+ }
-// return ret;
+ return ret;
}
/*!
@@ -466,14 +515,11 @@ static int xfr_check_tsig(knot_ns_xfr_t *xfr, knot_rcode_t *rcode)
* \param fd Associated file descriptor.
* \param data Transfer data.
*/
-int xfr_process_event(xfrworker_t *w, int fd, knot_ns_xfr_t *data)
+int xfr_process_event(xfrworker_t *w, int fd, knot_ns_xfr_t *data, uint8_t *buf, size_t buflen)
{
- /* Buffer for answering. */
- uint8_t buf[65535];
-
/* Update xfer state. */
data->wire = buf;
- data->wire_size = sizeof(buf);
+ data->wire_size = buflen;
/* Handle SOA/NOTIFY responses. */
if (data->type == XFR_TYPE_NOTIFY || data->type == XFR_TYPE_SOA) {
@@ -482,18 +528,13 @@ int xfr_process_event(xfrworker_t *w, int fd, knot_ns_xfr_t *data)
/* Read DNS/TCP packet. */
int ret = 0;
- int rcvd = tcp_recv(fd, buf, sizeof(buf), 0);
+ int rcvd = tcp_recv(fd, buf, buflen, 0);
data->wire_size = rcvd;
if (rcvd <= 0) {
data->wire_size = 0;
ret = KNOT_ECONN;
} else {
- /*!
- * \todo [TSIG] Somewhere before this fetch the query digest and the TSIG
- * associated with this transfer and save them to 'data'.
- */
-
/* Process incoming packet. */
switch(data->type) {
case XFR_TYPE_AIN:
@@ -510,7 +551,7 @@ int xfr_process_event(xfrworker_t *w, int fd, knot_ns_xfr_t *data)
/* AXFR-style IXFR. */
if (ret == KNOT_ENOIXFR) {
- dbg_xfr("xfr: Fallback to AXFR/IN.\n");
+ log_server_notice("IXFR/IN - Fallback to AXFR/IN.\n");
assert(data->type == XFR_TYPE_IIN);
data->type = XFR_TYPE_AIN;
ret = knot_ns_process_axfrin(w->ns, data);
@@ -529,15 +570,15 @@ int xfr_process_event(xfrworker_t *w, int fd, knot_ns_xfr_t *data)
knot_zone_t *zone = (knot_zone_t *)data->zone;
if (zone && data->type == XFR_TYPE_IIN && ret == KNOT_EXFRREFUSED) {
log_server_notice("IXFR/IN failed, attempting to use "
- "AXFR/IN instead.\n");
- size_t bufsize = sizeof(buf);
- data->wire_size = sizeof(buf); /* Reset maximum bufsize */
+ "AXFR/IN instead.\n");
+ size_t bufsize = buflen;
+ data->wire_size = buflen; /* Reset maximum bufsize */
ret = xfrin_create_axfr_query(zone->name, data,
- &bufsize, 1);
+ &bufsize, 1);
/* Send AXFR/IN query. */
- if (ret == KNOTD_EOK) {
+ if (ret == KNOT_EOK) {
ret = data->send(data->session, &data->addr,
- data->wire, bufsize);
+ data->wire, bufsize);
/* Switch to AIN type XFR and return now. */
if (ret == bufsize) {
data->type = XFR_TYPE_AIN;
@@ -547,13 +588,19 @@ int xfr_process_event(xfrworker_t *w, int fd, knot_ns_xfr_t *data)
}
/* Handle errors. */
- if (ret < 0 && ret != KNOT_ENOXFR) {
+ if (ret == KNOT_ENOXFR) {
+ log_server_warning("%cXFR/IN request finished - %s\n",
+ data->type == XFR_TYPE_AIN ? 'A' : 'I',
+ knot_strerror(ret));
+ } else if (ret < 0) {
log_server_error("%cXFR/IN request failed - %s\n",
data->type == XFR_TYPE_AIN ? 'A' : 'I',
knot_strerror(ret));
}
+
/* Check finished zone. */
+ int result = KNOTD_EOK;
if (xfer_finished) {
knot_zone_t *zone = (knot_zone_t *)data->zone;
@@ -566,17 +613,17 @@ int xfr_process_event(xfrworker_t *w, int fd, knot_ns_xfr_t *data)
/* AXFR bootstrap timeout. */
rcu_read_lock();
- if (!knot_zone_contents(zone) && data->type == XFR_TYPE_AIN) {
+ if (ret != KNOTD_EOK && data->type == XFR_TYPE_AIN) {
/* Schedule request (60 - 90s random delay). */
int tmr_s = AXFR_BOOTSTRAP_RETRY;
- tmr_s += (30.0 * 1000) * (rand() / (RAND_MAX + 1.0));
+ tmr_s += (30.0 * 1000) * (tls_rand());
zd->xfr_in.bootstrap_retry = tmr_s;
log_zone_info("Another attempt to AXFR bootstrap "
"zone '%s' in %d seconds.\n",
zorigin, tmr_s/1000);
}
rcu_read_unlock();
-
+
/* Update timers. */
server_t *server = (server_t *)knot_ns_get_data(w->ns);
zones_timers_update(zone, zd->conf, server->sched);
@@ -599,12 +646,10 @@ int xfr_process_event(xfrworker_t *w, int fd, knot_ns_xfr_t *data)
}
/* Disconnect. */
- ret = KNOTD_ECONNREFUSED; /* Make it disconnect. */
- } else {
- ret = KNOTD_EOK;
+ result = KNOTD_ECONNREFUSED; /* Make it disconnect. */
}
- return ret;
+ return result;
}
/*! \todo Document me.
@@ -649,6 +694,7 @@ static int xfr_client_start(xfrworker_t *w, knot_ns_xfr_t *data)
if (data->session <= 0) {
int fd = socket_create(data->addr.family, SOCK_STREAM);
if (fd < 0) {
+ pthread_mutex_unlock(&zd->xfr_in.lock);
log_server_warning("Failed to create socket "
"(type=%s, family=%s).\n",
"SOCK_STREAM",
@@ -658,6 +704,7 @@ static int xfr_client_start(xfrworker_t *w, knot_ns_xfr_t *data)
}
ret = connect(fd, data->addr.ptr, data->addr.len);
if (ret < 0) {
+ pthread_mutex_unlock(&zd->xfr_in.lock);
log_server_warning("Failed to connect to %cXFR master "
"at %s:%d.\n",
data->type == XFR_TYPE_AIN ? 'A' : 'I',
@@ -665,8 +712,7 @@ static int xfr_client_start(xfrworker_t *w, knot_ns_xfr_t *data)
if (!knot_zone_contents(zone)) {
/* Reschedule request (120 - 240s random delay). */
int tmr_s = AXFR_BOOTSTRAP_RETRY * 2; /* Malus x2 */
- tmr_s += (int)((120.0 * 1000) *
- (rand() / (RAND_MAX + 1.0)));
+ tmr_s += (int)((120.0 * 1000) * tls_rand());
event_t *ev = zd->xfr_in.timer;
if (ev) {
evsched_cancel(ev->parent, ev);
@@ -690,16 +736,14 @@ static int xfr_client_start(xfrworker_t *w, knot_ns_xfr_t *data)
rcu_read_lock();
const knot_zone_contents_t *contents = knot_zone_contents(zone);
if (!contents && data->type == XFR_TYPE_IIN) {
+ pthread_mutex_unlock(&zd->xfr_in.lock);
rcu_read_unlock();
log_server_warning("Failed start IXFR on zone with no "
"contents\n");
return KNOTD_ERROR;
}
-
- /*! \todo [TSIG] Somewhere before this determine if the server should
- * use TSIG for this transfer and set appropriate fields
- * in 'data'.
- */
+
+ /* Prepare TSIG key if set. */
int add_tsig = 0;
if (data->tsig_key) {
if (xfr_prepare_tsig(data, data->tsig_key) == KNOT_EOK) {
@@ -708,7 +752,7 @@ static int xfr_client_start(xfrworker_t *w, knot_ns_xfr_t *data)
if (data->tsig_data) {
dbg_xfr("xfr: using TSIG for XFR/IN\n");
add_tsig = 1;
- data->tsig_data_size = data_bufsize;
+ data->tsig_data_size = 0;
} else {
dbg_xfr("xfr: failed to allocate TSIG data "
"buffer (%zu kB)\n",
@@ -727,15 +771,16 @@ static int xfr_client_start(xfrworker_t *w, knot_ns_xfr_t *data)
ret = xfrin_create_ixfr_query(contents, data, &bufsize, add_tsig);
break;
default:
- ret = KNOTD_EINVAL;
+ ret = KNOT_EBADARG;
break;
}
/* Handle errors. */
if (ret != KNOT_EOK) {
+ pthread_mutex_unlock(&zd->xfr_in.lock);
dbg_xfr("xfr: failed to create XFR query type %d: %s\n",
data->type, knot_strerror(ret));
- return ret;
+ return KNOTD_ERROR;
}
/* Unlock zone contents. */
@@ -822,8 +867,8 @@ xfrhandler_t *xfr_create(size_t thrcount, knot_nameserver_t *ns)
{
/* Create XFR handler data. */
xfrhandler_t *data = malloc(sizeof(xfrhandler_t));
- if (!data) {
- return 0;
+ if (data == NULL) {
+ return NULL;
}
memset(data, 0, sizeof(xfrhandler_t));
@@ -836,18 +881,19 @@ xfrhandler_t *xfr_create(size_t thrcount, knot_nameserver_t *ns)
/* Initialize threads. */
data->workers = malloc(thrcount * sizeof(xfrhandler_t*));
- if(data->workers == 0) {
+ if(data->workers == NULL) {
pthread_mutex_destroy(&data->rr_mx);
free(data);
+ return NULL;
}
/* Create threading unit. */
dt_unit_t *unit = dt_create(thrcount);
- if (!unit) {
+ if (unit == NULL) {
pthread_mutex_destroy(&data->rr_mx);
free(data->workers);
free(data);
- return 0;
+ return NULL;
}
data->unit = unit;
@@ -870,7 +916,7 @@ xfrhandler_t *xfr_create(size_t thrcount, knot_nameserver_t *ns)
free(data->workers);
free(data->unit);
free(data);
- return 0;
+ return NULL;
}
/* Assign worker threads. */
@@ -994,6 +1040,8 @@ static int xfr_process_request(xfrworker_t *w, uint8_t *buf, size_t buflen)
const char *req_type = "";
knot_rcode_t rcode = 0;
char *zname = "(unknown)";
+ uint32_t serial_from = 0;
+ uint32_t serial_to = 0;
/* XFR request state tracking. */
int init_failed = 0;
@@ -1025,7 +1073,7 @@ static int xfr_process_request(xfrworker_t *w, uint8_t *buf, size_t buflen)
}
/* Check TSIG. */
- if (!init_failed) {
+ if (!init_failed && xfr.tsig_key != NULL) {
ret = xfr_check_tsig(&xfr, &rcode);
init_failed = (ret != KNOT_EOK);
errstr = knot_strerror(ret);
@@ -1041,6 +1089,18 @@ static int xfr_process_request(xfrworker_t *w, uint8_t *buf, size_t buflen)
r_addr, r_port,
errstr);
} else {
+ /* Prepare place for TSIG data */
+ xfr.tsig_data = malloc(KNOT_NS_TSIG_DATA_MAX_SIZE);
+ if (xfr.tsig_data) {
+ dbg_xfr("xfr: TSIG data allocated: %zu.\n",
+ KNOT_NS_TSIG_DATA_MAX_SIZE);
+ xfr.tsig_data_size = 0;
+ } else {
+ dbg_xfr("xfr: failed to allocate TSIG data "
+ "buffer (%zu kB)\n",
+ KNOT_NS_TSIG_DATA_MAX_SIZE / 1024);
+ }
+
ret = knot_ns_answer_axfr(w->ns, &xfr);
dbg_xfr("xfr: ns_answer_axfr() = %d.\n", ret);
if (ret != KNOTD_EOK) {
@@ -1051,6 +1111,10 @@ static int xfr_process_request(xfrworker_t *w, uint8_t *buf, size_t buflen)
zname,
r_addr, r_port);
}
+
+ /* Free allocated data. */
+ free(xfr.tsig_data);
+ xfr.tsig_data = NULL;
}
if (xfr.digest) {
@@ -1060,7 +1124,10 @@ static int xfr_process_request(xfrworker_t *w, uint8_t *buf, size_t buflen)
}
free(xfr.query->wireformat);
xfr.query->wireformat = 0;
- knot_packet_free(&xfr.query); /* Free query. */
+ knot_packet_free(&xfr.query); /* Free query. */
+ xfr.query = NULL;
+ knot_packet_free(&xfr.response); /* Free response. */
+ xfr.response = NULL;
if (qname != NULL) {
free(zname);
@@ -1086,15 +1153,12 @@ static int xfr_process_request(xfrworker_t *w, uint8_t *buf, size_t buflen)
}
/* Check TSIG. */
- if (!init_failed) {
+ if (!init_failed && xfr.tsig_key != NULL) {
ret = xfr_check_tsig(&xfr, &rcode);
init_failed = (ret != KNOT_EOK);
errstr = knot_strerror(ret);
}
- uint32_t serial_from = 0;
- uint32_t serial_to = 0;
-
// Check serial differeces
if (!init_failed) {
dbg_xfr_verb("Loading serials for IXFR.\n");
@@ -1131,51 +1195,87 @@ static int xfr_process_request(xfrworker_t *w, uint8_t *buf, size_t buflen)
errstr = knotd_strerror(ret);
}
}
-
+
/* Evaluate progress and answer if passed. */
if (init_failed) {
knot_ns_xfr_send_error(w->ns, &xfr, rcode);
log_server_notice("IXFR transfer of zone '%s/OUT' "
- "%s:%d failed: %s\n",
- zname,
- r_addr, r_port,
- errstr);
+ "%s:%d failed: %s\n",
+ zname,
+ r_addr, r_port,
+ errstr);
ret = KNOTD_ERROR;
} else {
+ /* Prepare place for TSIG data */
+ xfr.tsig_data = malloc(KNOT_NS_TSIG_DATA_MAX_SIZE);
+ if (xfr.tsig_data) {
+ dbg_xfr("xfr: TSIG data allocated: %zu.\n",
+ KNOT_NS_TSIG_DATA_MAX_SIZE);
+ xfr.tsig_data_size = 0;
+ } else {
+ dbg_xfr("xfr: failed to allocate TSIG data "
+ "buffer (%zu kB)\n",
+ KNOT_NS_TSIG_DATA_MAX_SIZE / 1024);
+ }
+
ret = knot_ns_answer_ixfr(w->ns, &xfr);
dbg_xfr("xfr: ns_answer_ixfr() = %d.\n", ret);
- if (ret != KNOTD_EOK) {
+ if (ret != KNOT_EOK) {
+ errstr = knot_strerror(ret);
+ log_server_notice("IXFR transfer of zone '%s/OUT' "
+ "%s:%d failed: %s\n",
+ zname,
+ r_addr, r_port,
+ errstr);
socket_close(xfr.session);
} else {
- log_server_info("IXFR transfer of zone '%s/OUT'"
- " - not enough data in journal,"
- " fallback to AXFR.\n",
- zname);
- xfr.type = XFR_TYPE_AOUT;
- xfr_request(w->master, &xfr);
- return KNOTD_EOK;
+ log_server_info("IXFR transfer of zone '%s/OUT' "
+ "to %s:%d successful.\n",
+ zname,
+ r_addr, r_port);
}
+
+ /* Free allocated data. */
+ free(xfr.tsig_data);
+ xfr.tsig_data = NULL;
}
+
+ /* Cleanup. */
if (xfr.digest) {
free(xfr.digest);
- xfr.digest = 0;
+ xfr.digest = NULL;
xfr.digest_max_size = 0;
}
- free(xfr.query->wireformat);
- knot_packet_free(&xfr.query); /* Free query. */
+ free(xfr.query->wireformat); /* Free wireformat. */
+ xfr.query->wireformat = NULL;
+ knot_packet_free(&xfr.query); /* Free query. */
+ xfr.query = NULL;
+ knot_packet_free(&xfr.response); /* Free response. */
+ xfr.response = NULL;
+
+ if (xfr.data != NULL) { /* Free changesets. */
+ knot_free_changesets((knot_changesets_t **)
+ (&xfr.data));
+ }
- if (qname) {
+ if (zname) {
free(zname);
}
break;
case XFR_TYPE_AIN:
req_type = "AXFR/IN";
- case XFR_TYPE_IIN:
- if (xfr.type == XFR_TYPE_IIN) {
- req_type = "IXFR/IN";
- }
+ ret = xfr_client_start(w, &xfr);
+ /* Report. */
+ if (ret != KNOTD_EOK && ret != KNOTD_EACCES) {
+ log_server_error("%s request from %s:%d failed: %s\n",
+ req_type, r_addr, r_port,
+ knotd_strerror(ret));
+ }
+ break;
+ case XFR_TYPE_IIN:
+ req_type = "IXFR/IN";
ret = xfr_client_start(w, &xfr);
/* Report. */
@@ -1228,7 +1328,13 @@ int xfr_worker(dthread_t *thread)
}
/* Buffer for answering. */
- uint8_t buf[65535];
+ size_t buflen = XFR_BUFFER_SIZE;
+ uint8_t* buf = malloc(buflen);
+ if (buf == NULL) {
+ dbg_xfr("xfr: failed to allocate buffer for XFR worker\n");
+ return KNOTD_ENOMEM;
+ }
+
/* Accept requests. */
int ret = 0;
@@ -1263,7 +1369,7 @@ int xfr_worker(dthread_t *thread)
if (it.fd == rfd) {
dbg_xfr_verb("xfr: worker=%p processing request\n",
w);
- ret = xfr_process_request(w, buf, sizeof(buf));
+ ret = xfr_process_request(w, buf, buflen);
if (ret == KNOTD_ENOTRUNNING) {
break;
}
@@ -1275,7 +1381,7 @@ int xfr_worker(dthread_t *thread)
dbg_xfr_verb("xfr: worker=%p processing event on "
"fd=%d data=%p.\n",
w, it.fd, data);
- ret = xfr_process_event(w, it.fd, data);
+ ret = xfr_process_event(w, it.fd, data, buf, buflen);
if (ret != KNOTD_EOK) {
xfr_free_task(data);
}
@@ -1290,6 +1396,7 @@ int xfr_worker(dthread_t *thread)
/* Stop whole unit. */
+ free(buf);
dbg_xfr_verb("xfr: worker=%p finished.\n", w);
thread->data = 0;
return KNOTD_EOK;
diff --git a/src/knot/server/zones.c b/src/knot/server/zones.c
index 4456dac..bd23c7d 100644
--- a/src/knot/server/zones.c
+++ b/src/knot/server/zones.c
@@ -17,6 +17,7 @@
#include <sys/stat.h>
#include "common/lists.h"
+#include "common/WELL1024a.h"
#include "libknot/dname.h"
#include "libknot/util/wire.h"
#include "knot/zone/zone-dump-text.h"
@@ -35,6 +36,8 @@
#include "knot/zone/zone-dump.h"
#include "libknot/nameserver/name-server.h"
#include "libknot/updates/changesets.h"
+#include "libknot/tsig-op.h"
+#include "libknot/packet/response.h"
static const size_t XFRIN_CHANGESET_BINARY_SIZE = 100;
static const size_t XFRIN_CHANGESET_BINARY_STEP = 100;
@@ -367,11 +370,9 @@ static int zones_refresh_ev(event_t *e)
xfr_req.zone = zone;
/* Select TSIG key. */
- /*!< \todo [TSIG] DISABLED */
- xfr_req.tsig_key = 0;
-// if (zd->xfr_in.tsig_key.name) {
-// xfr_req.tsig_key = &zd->xfr_in.tsig_key;
-// }
+ if (zd->xfr_in.tsig_key.name) {
+ xfr_req.tsig_key = &zd->xfr_in.tsig_key;
+ }
/* Unlock zone contents. */
rcu_read_unlock();
@@ -382,11 +383,11 @@ static int zones_refresh_ev(event_t *e)
dbg_zones("zones: already bootstrapping '%s'\n",
zd->conf->name);
return KNOTD_EOK;
- } else {
- log_zone_info("Attempting to bootstrap zone %s from master\n",
- zd->conf->name);
- pthread_mutex_unlock(&zd->xfr_in.lock);
}
+
+ log_zone_info("Attempting to bootstrap zone %s from master\n",
+ zd->conf->name);
+ pthread_mutex_unlock(&zd->xfr_in.lock);
return xfr_request(zd->server->xfr_h, &xfr_req);
}
@@ -427,29 +428,37 @@ static int zones_refresh_ev(event_t *e)
int sock = socket_create(master->family, SOCK_DGRAM);
/* Send query. */
- ret = -1;
+ ret = KNOTD_ERROR;
if (sock > -1) {
- ret = sendto(sock, qbuf, buflen, 0,
- master->ptr, master->len);
+ int sent = sendto(sock, qbuf, buflen, 0,
+ master->ptr, master->len);
+
+ /* Store ID of the awaited response. */
+ if (sent == buflen) {
+ ret = KNOTD_EOK;
+ } else {
+ socket_close(sock);
+ sock = -1;
+ }
}
-
- /* Store ID of the awaited response. */
- if (ret == buflen) {
+
+ /* Check result. */
+ if (ret == KNOTD_EOK) {
zd->xfr_in.next_id = knot_wire_get_id(qbuf);
dbg_zones("zones: expecting SOA response "
"ID=%d for '%s'\n",
zd->xfr_in.next_id, zd->conf->name);
+
+ /* Watch socket. */
+ knot_ns_xfr_t req;
+ memset(&req, 0, sizeof(req));
+ req.session = sock;
+ req.type = XFR_TYPE_SOA;
+ req.zone = zone;
+ memcpy(&req.addr, master, sizeof(sockaddr_t));
+ sockaddr_update(&req.addr);
+ xfr_request(zd->server->xfr_h, &req);
}
-
- /* Watch socket. */
- knot_ns_xfr_t req;
- memset(&req, 0, sizeof(req));
- req.session = sock;
- req.type = XFR_TYPE_SOA;
- req.zone = zone;
- memcpy(&req.addr, master, sizeof(sockaddr_t));
- sockaddr_update(&req.addr);
- xfr_request(zd->server->xfr_h, &req);
} else {
ret = KNOTD_ERROR;
}
@@ -612,7 +621,7 @@ static int zones_zonefile_sync_ev(event_t *e)
if (ret == KNOTD_EOK) {
log_zone_info("Applied differences of '%s' to zonefile.\n",
zd->conf->name);
- } else {
+ } else if (ret != KNOTD_ERANGE) {
log_zone_warning("Failed to apply differences of '%s' "
"to zonefile.\n",
zd->conf->name);
@@ -666,7 +675,7 @@ static int zones_set_acl(acl_t **acl, list* acl_list)
/* Load rule. */
if (ret > 0) {
- acl_create(*acl, &addr, ACL_ACCEPT);
+ acl_create(*acl, &addr, ACL_ACCEPT, cfg_if);
}
}
@@ -752,12 +761,17 @@ static int zones_load_zone(knot_zonedb_t *zonedb, const char *zone_name,
if (zone) {
/* save the timestamp from the zone db file */
struct stat s;
- stat(filename, &s);
- knot_zone_set_version(zone, s.st_mtime);
-
- if (knot_zonedb_add_zone(zonedb, zone) != 0){
+ if (stat(filename, &s) < 0) {
+ dbg_zones("zones: failed to stat() zone db, "
+ "something is seriously wrong\n");
knot_zone_deep_free(&zone, 0);
zone = 0;
+ } else {
+ knot_zone_set_version(zone, s.st_mtime);
+ if (knot_zonedb_add_zone(zonedb, zone) != 0){
+ knot_zone_deep_free(&zone, 0);
+ zone = 0;
+ }
}
}
@@ -1031,10 +1045,10 @@ static int zones_load_changesets(const knot_zone_t *zone,
dbg_xfr_detail("xfr: Journal entries read.\n");
/* Unpack binary data. */
- ret = zones_changesets_from_binary(dst);
- if (ret != KNOT_EOK) {
+ int unpack_ret = zones_changesets_from_binary(dst);
+ if (unpack_ret != KNOT_EOK) {
dbg_xfr("xfr: failed to unpack changesets "
- "from binary, %s\n", knot_strerror(ret));
+ "from binary, %s\n", knot_strerror(unpack_ret));
return KNOTD_ERROR;
}
@@ -1099,12 +1113,12 @@ static int zones_journal_apply(knot_zone_t *zone)
log_server_info("Applying '%zu' changesets from journal "
"to zone '%s'.\n",
chsets->count, zd->conf->name);
- ret = xfrin_apply_changesets_to_zone(zone, chsets);
- if (ret != KNOT_EOK) {
+ int apply_ret = xfrin_apply_changesets_to_zone(zone, chsets);
+ if (apply_ret != KNOT_EOK) {
log_server_error("Failed to apply changesets to "
"'%s' - %s\n",
zd->conf->name,
- knot_strerror(ret));
+ knot_strerror(apply_ret));
ret = KNOTD_ERROR;
}
}
@@ -1263,7 +1277,6 @@ static int zones_insert_zones(knot_nameserver_t *ns,
log_server_error("Error adding known zone '%s' to"
" the new database - %s\n",
z->name, knot_strerror(ret));
- ret = KNOTD_ERROR;
} else {
++inserted;
}
@@ -1296,12 +1309,12 @@ static int zones_insert_zones(knot_nameserver_t *ns,
cfg_if->family,
cfg_if->address,
cfg_if->port);
- /*!< \todo [TSIG] DISABLED */
-// if (cfg_if->key) {
-// memcpy(&zd->xfr_in.tsig_key,
-// cfg_if->key,
-// sizeof(knot_key_t));
-// }
+
+ if (cfg_if->key) {
+ memcpy(&zd->xfr_in.tsig_key,
+ cfg_if->key,
+ sizeof(knot_key_t));
+ }
dbg_zones("zones: using %s:%d as XFR master "
"for '%s'\n",
@@ -1374,6 +1387,180 @@ dbg_zones_exec(
}
/*----------------------------------------------------------------------------*/
+
+static int zones_verify_tsig_query(const knot_packet_t *query,
+ const knot_rrset_t *tsig_rr,
+ const knot_key_t *key,
+ knot_rcode_t *rcode, uint16_t *tsig_rcode,
+ uint64_t *tsig_prev_time_signed)
+{
+ assert(tsig_rr != NULL);
+ assert(key != NULL);
+ assert(rcode != NULL);
+ assert(tsig_rcode != NULL);
+
+ /*
+ * 1) Check if we support the requested algorithm.
+ */
+ tsig_algorithm_t alg = tsig_rdata_alg(tsig_rr);
+ if (tsig_alg_digest_length(alg) == 0) {
+ log_answer_info("Unsupported digest algorithm "
+ "requested, treating as bad key\n");
+ /*! \todo [TSIG] It is unclear from RFC if I
+ * should treat is as a bad key
+ * or some other error.
+ */
+ *rcode = KNOT_RCODE_NOTAUTH;
+ *tsig_rcode = KNOT_TSIG_RCODE_BADKEY;
+ return KNOT_TSIG_EBADKEY;
+ }
+
+ const knot_dname_t *kname = knot_rrset_owner(tsig_rr);
+ assert(kname != NULL);
+
+ /*
+ * 2) Find the particular key used by the TSIG.
+ */
+ if (key && kname && knot_dname_compare(key->name, kname) == 0) {
+ dbg_zones_verb("Found claimed TSIG key for comparison\n");
+ } else {
+ *rcode = KNOT_RCODE_NOTAUTH;
+ *tsig_rcode = KNOT_TSIG_RCODE_BADKEY;
+ return KNOT_TSIG_EBADKEY;
+ }
+
+ /*
+ * 3) Validate the query with TSIG.
+ */
+ /* Prepare variables for TSIG */
+ /*! \todo These need to be saved to the response somehow. */
+ //size_t tsig_size = tsig_wire_maxsize(key);
+ size_t digest_max_size = tsig_alg_digest_length(key->algorithm);
+ //size_t digest_size = 0;
+ //uint64_t tsig_prev_time_signed = 0;
+ //uint8_t *digest = (uint8_t *)malloc(digest_max_size);
+ //memset(digest, 0 , digest_max_size);
+
+ /* Copy MAC from query. */
+ dbg_zones_verb("Validating TSIG from query\n");
+
+ //const uint8_t* mac = tsig_rdata_mac(tsig_rr);
+ size_t mac_len = tsig_rdata_mac_length(tsig_rr);
+
+ int ret = KNOT_EOK;
+
+ if (mac_len > digest_max_size) {
+ *rcode = KNOT_RCODE_FORMERR;
+ dbg_zones("MAC length %zu exceeds digest "
+ "maximum size %zu\n", mac_len, digest_max_size);
+ return KNOT_EMALF;
+ } else {
+ //memcpy(digest, mac, mac_len);
+ //digest_size = mac_len;
+
+ /* Check query TSIG. */
+ ret = knot_tsig_server_check(tsig_rr,
+ knot_packet_wireformat(query),
+ knot_packet_size(query), key);
+ dbg_zones_verb("knot_tsig_server_check() returned %s\n",
+ knot_strerror(ret));
+
+ /* Evaluate TSIG check results. */
+ switch(ret) {
+ case KNOT_EOK:
+ *rcode = KNOT_RCODE_NOERROR;
+ break;
+ case KNOT_TSIG_EBADKEY:
+ *tsig_rcode = KNOT_TSIG_RCODE_BADKEY;
+ *rcode = KNOT_RCODE_NOTAUTH;
+ break;
+ case KNOT_TSIG_EBADSIG:
+ *tsig_rcode = KNOT_TSIG_RCODE_BADSIG;
+ *rcode = KNOT_RCODE_NOTAUTH;
+ break;
+ case KNOT_TSIG_EBADTIME:
+ *tsig_rcode = KNOT_TSIG_RCODE_BADTIME;
+ // store the time signed from the query
+ *tsig_prev_time_signed = tsig_rdata_time_signed(tsig_rr);
+ *rcode = KNOT_RCODE_NOTAUTH;
+ break;
+ case KNOT_EMALF:
+ *rcode = KNOT_RCODE_FORMERR;
+ break;
+ default:
+ *rcode = KNOT_RCODE_SERVFAIL;
+ }
+ }
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int zones_check_tsig_query(const knot_zone_t *zone,
+ knot_packet_t *query,
+ const sockaddr_t *addr,
+ knot_rcode_t *rcode,
+ uint16_t *tsig_rcode,
+ knot_key_t **tsig_key_zone,
+ uint64_t *tsig_prev_time_signed)
+{
+ assert(zone != NULL);
+ assert(query != NULL);
+ assert(rcode != NULL);
+ assert(tsig_key_zone != NULL);
+
+ knot_rrset_t *tsig = NULL;
+
+ if (knot_packet_additional_rrset_count(query) > 0) {
+ /*! \todo warning */
+ tsig = knot_packet_additional_rrset(query,
+ knot_packet_additional_rrset_count(query) - 1);
+ if (knot_rrset_type(tsig) == KNOT_RRTYPE_TSIG) {
+ dbg_zones_verb("found TSIG in normal query\n");
+ } else {
+ tsig = NULL; /* Invalidate if not TSIG RRTYPE. */
+ }
+ }
+
+ if (tsig == NULL) {
+ // no TSIG, this is completely valid
+ *tsig_rcode = 0;
+ return KNOT_EOK;
+ }
+
+ // if there is some TSIG in the query, find the TSIG associated with
+ // the zone
+ //knot_key_t *tsig_key_zone = NULL;
+
+ dbg_zones_verb("Checking zone and ACL.\n");
+ int ret = zones_query_check_zone(zone, addr, tsig_key_zone, rcode);
+
+ /*! \todo What if there is TSIG, but no key is configured? */
+
+ if (ret == KNOTD_EOK) {
+ if (*tsig_key_zone != NULL) {
+ // everything OK, so check TSIG
+ dbg_zones_verb("Verifying TSIG.\n");
+ ret = zones_verify_tsig_query(query, tsig, *tsig_key_zone,
+ rcode, tsig_rcode,
+ tsig_prev_time_signed);
+ } else {
+ dbg_zones_verb("No key configured for zone.\n");
+ // no key configured for zone, return BADKEY
+ *tsig_rcode = KNOT_TSIG_RCODE_BADKEY;
+ *rcode = KNOT_RCODE_NOTAUTH;
+ ret = KNOT_TSIG_EBADKEY;
+ }
+ }
+
+ // save TSIG RR to query structure
+ knot_packet_set_tsig(query, tsig);
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
/* API functions */
/*----------------------------------------------------------------------------*/
@@ -1414,8 +1601,8 @@ int zones_update_db_from_config(const conf_t *conf, knot_nameserver_t *ns,
log_server_warning("Not all the zones were loaded.\n");
}
- dbg_zones_detail("zones: old db in nameserver: %p, old db stored: %p, new db: %p\n",
- ns->zone_db, *db_old, db_new);
+ dbg_zones_detail("zones: old db in nameserver: %p, old db stored: %p, "
+ "new db: %p\n", ns->zone_db, *db_old, db_new);
/* Switch the databases. */
(void)rcu_xchg_pointer(&ns->zone_db, db_new);
@@ -1452,6 +1639,7 @@ int zones_zonefile_sync(knot_zone_t *zone)
}
/* Fetch zone data. */
+ int ret = KNOTD_EOK;
zonedata_t *zd = (zonedata_t *)zone->data;
/* Lock zone data. */
@@ -1471,6 +1659,7 @@ int zones_zonefile_sync(knot_zone_t *zone)
soa_rr = knot_rrset_rdata(soa_rrs);
int64_t serial_ret = knot_rdata_soa_serial(soa_rr);
if (serial_ret < 0) {
+ pthread_mutex_unlock(&zd->lock);
return KNOTD_EINVAL;
}
uint32_t serial_to = (uint32_t)serial_ret;
@@ -1499,11 +1688,52 @@ int zones_zonefile_sync(knot_zone_t *zone)
} else {
dbg_zones_verb("zones: '%s' zonefile is in sync "
"with differences\n", zd->conf->name);
+ ret = KNOTD_ERANGE;
}
/* Unlock zone data. */
pthread_mutex_unlock(&zd->lock);
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int zones_query_check_zone(const knot_zone_t *zone, const sockaddr_t *addr,
+ knot_key_t **tsig_key, knot_rcode_t *rcode)
+{
+ if (addr == NULL || tsig_key == NULL || rcode == NULL) {
+ dbg_zones_verb("Wrong arguments.\n");
+ *rcode = KNOT_RCODE_SERVFAIL;
+ return KNOTD_EINVAL;
+ }
+
+ /* Check zone data. */
+ const zonedata_t *zd = (const zonedata_t *)knot_zone_data(zone);
+ if (zd == NULL) {
+ dbg_zones("zones: invalid zone data for zone %p\n", zone);
+ *rcode = KNOT_RCODE_SERVFAIL;
+ return KNOTD_ERROR;
+ }
+
+ /* Check xfr-out ACL */
+ acl_key_t *match = NULL;
+ if (acl_match(zd->xfr_out, addr, &match) == ACL_DENY) {
+ log_answer_warning("Unauthorized query or request for XFR "
+ "'%s/OUT'.\n", zd->conf->name);
+ *rcode = KNOT_RCODE_REFUSED;
+ return KNOTD_EACCES;
+ } else {
+ dbg_zones("zones: authorized query or request for XFR "
+ "'%s/OUT'. match=%p\n", zd->conf->name, match);
+ if (match) {
+ /* Save configured TSIG key for comparison. */
+ conf_iface_t *iface = (conf_iface_t*)(match->val);
+ dbg_zones_detail("iface=%p, iface->key=%p\n",
+ iface, iface->key);
+ *tsig_key = iface->key;
+ }
+ }
return KNOTD_EOK;
}
@@ -1512,7 +1742,6 @@ int zones_zonefile_sync(knot_zone_t *zone)
int zones_xfr_check_zone(knot_ns_xfr_t *xfr, knot_rcode_t *rcode)
{
if (xfr == NULL || rcode == NULL) {
- *rcode = KNOT_RCODE_SERVFAIL;
return KNOTD_EINVAL;
}
@@ -1521,33 +1750,239 @@ int zones_xfr_check_zone(knot_ns_xfr_t *xfr, knot_rcode_t *rcode)
*rcode = KNOT_RCODE_REFUSED;
return KNOTD_EACCES;
}
-
- /* Check zone data. */
- zonedata_t *zd = (zonedata_t *)xfr->zone->data;
- if (zd == NULL) {
- dbg_zones("zones: invalid zone data for zone %p\n", xfr->zone);
- *rcode = KNOT_RCODE_SERVFAIL;
- return KNOTD_ERROR;
- }
-
+
/* Check zone contents. */
if (knot_zone_contents(xfr->zone) == NULL) {
- dbg_zones("zones: invalid zone contents for zone %p\n", xfr->zone);
+ dbg_zones("zones: invalid zone contents for zone %p\n",
+ xfr->zone);
*rcode = KNOT_RCODE_SERVFAIL;
return KNOTD_EEXPIRED;
}
- // Check xfr-out ACL
- if (acl_match(zd->xfr_out, &xfr->addr) == ACL_DENY) {
- log_answer_warning("Unauthorized request for XFR '%s/OUT'.\n",
- zd->conf->name);
- *rcode = KNOT_RCODE_REFUSED;
- return KNOTD_EACCES;
+ return zones_query_check_zone(xfr->zone, &xfr->addr, &xfr->tsig_key,
+ rcode);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int zones_normal_query_answer(knot_nameserver_t *nameserver,
+ knot_packet_t *query, const sockaddr_t *addr,
+ uint8_t *resp_wire, size_t *rsize)
+{
+ rcu_read_lock();
+
+ knot_packet_t *resp = NULL;
+ const knot_zone_t *zone = NULL;
+
+ dbg_zones_verb("Preparing response structure.\n");
+ int ret = knot_ns_prep_normal_response(nameserver, query, &resp, &zone);
+
+ // check for TSIG in the query
+ if (knot_packet_additional_rrset_count(query) > 0) {
+ /*! \todo warning */
+ const knot_rrset_t *tsig = knot_packet_additional_rrset(query,
+ knot_packet_additional_rrset_count(query) - 1);
+ if (knot_rrset_type(tsig) == KNOT_RRTYPE_TSIG) {
+ dbg_zones_verb("found TSIG in normal query\n");
+ knot_packet_set_tsig(query, tsig);
+ }
+ }
+
+ knot_rcode_t rcode = 0;
+
+ switch (ret) {
+ case KNOT_EOK:
+ rcode = KNOT_RCODE_NOERROR;
+ break;
+ case KNOT_EMALF:
+ // no TSIG signing in this case
+ rcode = KNOT_RCODE_FORMERR;
+ break;
+ default:
+ // no TSIG signing in this case
+ rcode = KNOT_RCODE_SERVFAIL;
+ break;
+ }
+
+ if (zone == NULL && knot_packet_tsig(query) == NULL) {
+ /*! \todo If there is TSIG, this should be probably handled
+ * as a key error.
+ */
+ rcode = KNOT_RCODE_REFUSED;
+ }
+
+ assert(resp != NULL);
+
+ if (rcode != KNOT_RCODE_NOERROR) {
+ dbg_zones_verb("Failed preparing response structure: %s.\n",
+ knot_strerror(rcode));
+ knot_ns_error_response(nameserver, knot_packet_id(query),
+ rcode, resp_wire, rsize);
} else {
- dbg_zones("zones: authorized XFR '%s/OUT'\n",
- zd->conf->name);
+ /*
+ * Now we have zone. Verify TSIG if it is in the packet.
+ */
+ assert(rcode == KNOT_RCODE_NOERROR);
+ uint16_t tsig_rcode = 0;
+ knot_key_t *tsig_key_zone = NULL;
+ uint64_t tsig_prev_time_signed = 0; /*! \todo Verify, as it was uninitialized! */
+
+ size_t answer_size = *rsize;
+ int ret = KNOT_EOK;
+
+ if (zone == NULL) {
+ assert(knot_packet_tsig(query) != NULL);
+ // treat as BADKEY error
+ rcode = KNOT_RCODE_NOTAUTH;
+ tsig_rcode = KNOT_TSIG_RCODE_BADKEY;
+ ret = KNOT_TSIG_EBADKEY;
+ } else {
+ dbg_zones_verb("Checking TSIG in query.\n");
+ ret = zones_check_tsig_query(zone, query, addr,
+ &rcode, &tsig_rcode,
+ &tsig_key_zone,
+ &tsig_prev_time_signed);
+ }
+
+ if (ret == KNOT_EOK) {
+ dbg_zones_verb("TSIG check successful. Answering "
+ "query.\n");
+ assert(tsig_rcode == 0);
+
+ // reserve place for the TSIG
+ if (tsig_key_zone != NULL) {
+ size_t tsig_max_size =
+ tsig_wire_maxsize(tsig_key_zone);
+ knot_packet_set_tsig_size(resp, tsig_max_size);
+ }
+ ret = knot_ns_answer_normal(nameserver, zone, resp,
+ resp_wire, &answer_size);
+
+ dbg_zones_detail("rsize = %zu\n", *rsize);
+ dbg_zones_detail("answer_size = %zu\n", answer_size);
+
+ assert(ret == KNOT_EOK);
+
+ // sign the message
+ if (tsig_key_zone != NULL) {
+ dbg_zones_verb("Signing message with TSIG.\n");
+ // TODO check
+ //*rsize = answer_size;
+
+ const knot_rrset_t *tsig =
+ knot_packet_tsig(knot_packet_query(resp));
+
+ size_t digest_max_size =
+ tsig_alg_digest_length(
+ tsig_key_zone->algorithm);
+ uint8_t *digest = (uint8_t *)malloc(
+ digest_max_size);
+ if (digest == NULL) {
+ knot_packet_free(&resp);
+ rcu_read_unlock();
+ return KNOT_ENOMEM;
+ }
+ size_t digest_size = digest_max_size;
+
+ ret = knot_tsig_sign(resp_wire, &answer_size,
+ *rsize, tsig_rdata_mac(tsig),
+ tsig_rdata_mac_length(tsig),
+ digest, &digest_size,
+ tsig_key_zone, tsig_rcode,
+ tsig_prev_time_signed);
+
+ free(digest);
+
+ dbg_zones_detail("answer_size = %zu\n",
+ answer_size);
+
+ if (ret != KNOT_EOK) {
+ dbg_zones_verb("Failed to sign message:"
+ "%s\n", knot_strerror(ret));
+ rcode = KNOT_RCODE_SERVFAIL;
+ } else {
+ *rsize = answer_size;
+ }
+ } else {
+ *rsize = answer_size;
+ }
+ } else {
+ dbg_zones_verb("Failed TSIG check: %s, TSIG err: %u.\n",
+ knot_strerror(ret), tsig_rcode);
+
+ if (tsig_rcode != 0) {
+ dbg_zones_verb("Sending TSIG error.\n");
+ // first, convert the response to wire format
+ answer_size = *rsize;
+ knot_response_set_rcode(resp, rcode);
+
+ ret = ns_response_to_wire(resp, resp_wire,
+ &answer_size);
+
+ dbg_zones_detail("Packet to wire returned %d\n",
+ ret);
+
+ // then add the TSIG to the wire format
+ if (ret == KNOT_EOK &&
+ tsig_rcode != KNOT_TSIG_RCODE_BADTIME) {
+ dbg_zones_verb("Adding TSIG.\n");
+ ret = knot_tsig_add(resp_wire,
+ &answer_size,
+ *rsize, tsig_rcode,
+ knot_packet_tsig(
+ query));
+
+ *rsize = answer_size;
+
+ } else if (tsig_rcode
+ == KNOT_TSIG_RCODE_BADTIME) {
+ dbg_zones_verb("Signing error resp.\n");
+ //*rsize = answer_size;
+
+ const knot_rrset_t *tsig =
+ knot_packet_tsig(
+ knot_packet_query(resp));
+
+ size_t digest_max_size =
+ tsig_alg_digest_length(
+ tsig_key_zone->algorithm);
+ uint8_t *digest = (uint8_t *)malloc(
+ digest_max_size);
+ if (digest == NULL) {
+ knot_packet_free(&resp);
+ rcu_read_unlock();
+ return KNOT_ENOMEM;
+ }
+ size_t digest_size = digest_max_size;
+
+ ret = knot_tsig_sign(resp_wire,
+ &answer_size, *rsize,
+ tsig_rdata_mac(tsig),
+ tsig_rdata_mac_length(tsig),
+ digest, &digest_size, tsig_key_zone,
+ tsig_rcode, tsig_prev_time_signed);
+
+ *rsize = answer_size;
+ } else {
+ dbg_zones_verb("Failed.\n");
+ rcode = KNOT_RCODE_SERVFAIL;
+ }
+ }
+ // in other case the RCODE is set and ret != KNOT_EOK
+ // and a normal error is returned below
+ }
+
+ if (ret != KNOT_EOK) {
+ knot_ns_error_response_full(nameserver, resp,
+ rcode, resp_wire,
+ rsize);
+ }
}
- return KNOTD_EOK;
+
+ knot_packet_free(&resp);
+ rcu_read_unlock();
+
+ return KNOT_EOK;
}
/*----------------------------------------------------------------------------*/
@@ -1647,12 +2082,9 @@ int zones_process_response(knot_nameserver_t *nameserver,
xfr_req.type = zones_transfer_to_use(contents);
/* Select TSIG key. */
- /*!< \todo [TSIG] DISABLED */
- xfr_req.tsig_key = 0;
- /* CLEANUP */
-// if (zd->xfr_in.tsig_key.name) {
-// xfr_req.tsig_key = &zd->xfr_in.tsig_key;
-// }
+ if (zd->xfr_in.tsig_key.name) {
+ xfr_req.tsig_key = &zd->xfr_in.tsig_key;
+ }
/* Unlock zone contents. */
rcu_read_unlock();
@@ -1767,39 +2199,34 @@ static int zones_dump_xfr_zone_text(knot_zone_contents_t *zone,
char *new_zonefile = zones_find_free_filename(zonefile);
if (new_zonefile == NULL) {
- dbg_zones("zones: failed to find free filename for temporary "
- "storage of the zone text file.\n");
+ log_zone_warning("Failed to find filename for temporary "
+ "storage of the transferred zone.\n");
return KNOTD_ERROR; /*! \todo New error code? */
}
int rc = zone_dump_text(zone, new_zonefile);
if (rc != KNOTD_EOK) {
- dbg_zones("Failed to save the zone to text zone file '%s'.\n",
- new_zonefile);
+ log_zone_warning("Failed to save the transferred zone to '%s'.\n",
+ new_zonefile);
free(new_zonefile);
return KNOTD_ERROR;
}
/*! \todo this would also need locking as well. */
- struct stat s;
- rc = stat(zonefile, &s);
- if (rc < 0 || remove(zonefile) == 0) {
- if (rename(new_zonefile, zonefile) != 0) {
- dbg_zones("Failed to replace old zonefile '%s'' with new"
- " zone file '%s'.\n", zonefile, new_zonefile);
- /*! \todo with proper locking, this shouldn't happen,
- * revise it later on.
- */
- zone_dump_text(zone, zonefile);
- return KNOTD_ERROR;
- }
- } else {
- dbg_zones("zones: failed to replace old zonefile '%s'.\n",
- zonefile);
+ remove(zonefile); /* Don't care, as the rename will trigger the error. */
+ if (rename(new_zonefile, zonefile) != 0) {
+ log_zone_warning("Failed to replace old zone file '%s'' with a new"
+ " zone file '%s'.\n", zonefile, new_zonefile);
+ /*! \todo with proper locking, this shouldn't happen,
+ * revise it later on.
+ */
+ zone_dump_text(zone, zonefile);
+ free(new_zonefile);
return KNOTD_ERROR;
}
+
free(new_zonefile);
return KNOTD_EOK;
}
@@ -1944,7 +2371,7 @@ static int zones_changeset_rrset_to_binary(uint8_t **data, size_t *size,
uint8_t *binary = NULL;
size_t actual_size = 0;
int ret = knot_zdump_rrset_serialize(rrset, &binary, &actual_size);
- if (ret != KNOT_EOK) {
+ if (ret != KNOT_EOK || binary == NULL) {
return KNOTD_ERROR; /*! \todo Other code? */
}
@@ -2094,7 +2521,7 @@ int zones_store_changesets(knot_ns_xfr_t *xfr)
"of '%s'\n",
zd->conf->name);
ret = zones_zonefile_sync(zone);
- if (ret != KNOTD_EOK) {
+ if (ret != KNOTD_EOK && ret != KNOTD_ERANGE) {
continue;
}
@@ -2166,6 +2593,7 @@ int zones_xfr_load_changesets(knot_ns_xfr_t *xfr, uint32_t serial_from,
if (ret != KNOTD_EOK) {
dbg_zones_verb("Loading changesets failed: %s\n",
knotd_strerror(ret));
+ knot_free_changesets(&chgsets);
return ret;
}
@@ -2291,7 +2719,7 @@ int zones_timers_update(knot_zone_t *zone, conf_zone_t *cfzone, evsched_t *sch)
ev->timeout = cfzone->notify_timeout;
/* Schedule request (30 - 60s random delay). */
- int tmr_s = 30 + (int)(30.0 * (rand() / (RAND_MAX + 1.0)));
+ int tmr_s = 30 + (int)(30.0 * tls_rand());
pthread_mutex_lock(&zd->lock);
ev->timer = evsched_schedule_cb(sch, zones_notify_send, ev,
tmr_s * 1000);
diff --git a/src/knot/server/zones.h b/src/knot/server/zones.h
index 525a78a..9fcab7d 100644
--- a/src/knot/server/zones.h
+++ b/src/knot/server/zones.h
@@ -123,14 +123,31 @@ int zones_update_db_from_config(const conf_t *conf, knot_nameserver_t *ns,
* \param zone Evaluated zone.
*
* \retval KNOTD_EOK if successful.
+ * \retval KNOTD_ERANGE if zonefile is in sync with journal.
* \retval KNOTD_EINVAL on invalid parameter.
* \retval KNOTD_ERROR on unspecified error during processing.
*/
int zones_zonefile_sync(knot_zone_t *zone);
+/*!
+ * \todo Document me.
+ */
+int zones_query_check_zone(const knot_zone_t *zone, const sockaddr_t *addr,
+ knot_key_t **tsig_key, knot_rcode_t *rcode);
+
+/*!
+ * \todo Document me.
+ */
int zones_xfr_check_zone(knot_ns_xfr_t *xfr, knot_rcode_t *rcode);
/*!
+ * \todo Document me.
+ */
+int zones_normal_query_answer(knot_nameserver_t *nameserver,
+ knot_packet_t *query, const sockaddr_t *addr,
+ uint8_t *response_wire, size_t *rsize);
+
+/*!
* \brief Processes normal response packet.
*
* \param nameserver Name server structure to provide the needed data.
diff --git a/src/knot/zone/semantic-check.c b/src/knot/zone/semantic-check.c
new file mode 100644
index 0000000..3ca38a2
--- /dev/null
+++ b/src/knot/zone/semantic-check.c
@@ -0,0 +1,1244 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+
+#include "knot/common.h"
+#include "knot/other/error.h"
+#include "libknot/libknot.h"
+#include "common/base32hex.h"
+#include "common/crc.h"
+
+#include "semantic-check.h"
+
+static const uint MAX_CNAME_CYCLE_DEPTH = 15;
+
+err_handler_t *handler_new(char log_cname, char log_glue,
+ char log_rrsigs, char log_nsec,
+ char log_nsec3)
+{
+ err_handler_t *handler = malloc(sizeof(err_handler_t));
+ CHECK_ALLOC_LOG(handler, NULL);
+
+ /* It should be initialized, but to be safe */
+ memset(handler->errors, 0, sizeof(uint) * (-ZC_ERR_ALLOC + 1));
+
+ handler->options.log_cname = log_cname;
+ handler->options.log_glue = log_glue;
+ handler->options.log_rrsigs = log_rrsigs;
+ handler->options.log_nsec = log_nsec;
+ handler->options.log_nsec3 = log_nsec3;
+
+ return handler;
+}
+
+/*!
+ * \brief Prints error message with node information.
+ *
+ * \note If \a node is NULL, only total number of errors is printed.
+ *
+ * \param handler Error handler.
+ * \param node Node with semantic error in it.
+ * \param error Type of error.
+ */
+static void log_error_from_node(err_handler_t *handler,
+ const knot_node_t *node,
+ uint error)
+{
+ if (node != NULL) {
+ char *name =
+ knot_dname_to_str(knot_node_owner(node));
+ fprintf(stderr, "Semantic warning in node: %s: ", name);
+ fprintf(stderr, "%s", error_messages[-error]);
+ free(name);
+ } else {
+ fprintf(stderr, "Total number of warnings is: %d for error: %s",
+ handler->errors[-error],
+ error_messages[-error]);
+ }
+}
+
+int err_handler_handle_error(err_handler_t *handler,
+ const knot_node_t *node,
+ uint error)
+{
+ assert(handler && node);
+ if ((error != 0) &&
+ (error > ZC_ERR_GLUE_GENERAL_ERROR)) {
+ return ZC_ERR_UNKNOWN;
+ }
+
+ if (error == ZC_ERR_ALLOC) {
+ ERR_ALLOC_FAILED;
+ return ZC_ERR_ALLOC;
+ }
+
+ /* missing SOA can only occur once, so there
+ * needn't to be an option for it */
+
+ if ((error != 0) &&
+ (error < ZC_ERR_GENERIC_GENERAL_ERROR)) {
+ /* The two errors before SOA were handled */
+ log_error_from_node(handler, node, error);
+
+ } else if ((error < ZC_ERR_RRSIG_GENERAL_ERROR) &&
+ ((handler->errors[-error] == 0) ||
+ (handler->options.log_rrsigs))) {
+
+ log_error_from_node(handler, node, error);
+
+ } else if ((error > ZC_ERR_RRSIG_GENERAL_ERROR) &&
+ (error < ZC_ERR_NSEC_GENERAL_ERROR) &&
+ ((handler->errors[-error] == 0) ||
+ (handler->options.log_nsec))) {
+
+ log_error_from_node(handler, node, error);
+
+ } else if ((error > ZC_ERR_NSEC_GENERAL_ERROR) &&
+ (error < ZC_ERR_NSEC3_GENERAL_ERROR) &&
+ ((handler->errors[-error] == 0) ||
+ (handler->options.log_nsec3))) {
+
+ log_error_from_node(handler, node, error);
+
+ } else if ((error > ZC_ERR_NSEC3_GENERAL_ERROR) &&
+ (error < ZC_ERR_CNAME_GENERAL_ERROR) &&
+ ((handler->errors[-error] == 0) ||
+ (handler->options.log_cname))) {
+
+ log_error_from_node(handler, node, error);
+
+ } else if ((error > ZC_ERR_CNAME_GENERAL_ERROR) &&
+ (error < ZC_ERR_GLUE_GENERAL_ERROR) &&
+ handler->options.log_glue) {
+
+ log_error_from_node(handler, node, error);
+
+ }
+
+ handler->errors[-error]++;
+
+ return KNOT_EOK;
+}
+
+void err_handler_log_all(err_handler_t *handler)
+{
+ if (handler == NULL) {
+ return;
+ }
+
+ for (int i = ZC_ERR_ALLOC; i < ZC_ERR_GLUE_GENERAL_ERROR; i++) {
+ if (handler->errors[-i] > 0) {
+ log_error_from_node(handler, NULL, i);
+ }
+ }
+}
+
+
+/*!
+ * \brief Semantic check - CNAME cycles. Uses constant value with maximum
+ * allowed CNAME chain depth.
+ *
+ * \param zone Zone containing the RRSet.
+ * \param rrset RRSet to be tested.
+ *
+ * \retval KNOT_EOK when there is no cycle.
+ * \retval ZC_ERR_CNAME_CYCLE when cycle is present.
+ */
+static int check_cname_cycles_in_zone(knot_zone_contents_t *zone,
+ const knot_rrset_t *rrset)
+{
+ const knot_rrset_t *next_rrset = rrset;
+ assert(rrset);
+ const knot_rdata_t *tmp_rdata = knot_rrset_rdata(next_rrset);
+ const knot_node_t *next_node = NULL;
+
+ uint i = 0;
+
+ assert(tmp_rdata);
+
+ const knot_dname_t *next_dname =
+ knot_rdata_cname_name(tmp_rdata);
+
+ assert(next_dname);
+
+ while (i < MAX_CNAME_CYCLE_DEPTH && next_dname != NULL) {
+ next_node = knot_zone_contents_get_node(zone, next_dname);
+ if (next_node == NULL) {
+ next_node =
+ knot_zone_contents_get_nsec3_node(zone, next_dname);
+ }
+
+ if (next_node != NULL) {
+ next_rrset = knot_node_rrset(next_node,
+ KNOT_RRTYPE_CNAME);
+ if (next_rrset != NULL) {
+ next_dname =
+ knot_rdata_cname_name(next_rrset->rdata);
+ } else {
+ next_node = NULL;
+ next_dname = NULL;
+ }
+ } else {
+ next_dname = NULL;
+ }
+ i++;
+ }
+
+ /* even if the length is 0, i will be 1 */
+ if (i >= MAX_CNAME_CYCLE_DEPTH) {
+ return ZC_ERR_CNAME_CYCLE;
+ }
+
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief Return raw data from rdata item structure (without length).
+ *
+ * \param item Item to get rdata from.
+ * \return uint16_t * raw data without length.
+ */
+static inline uint16_t *rdata_item_data(const knot_rdata_item_t *item)
+{
+ return (uint16_t *)(item->raw_data + 1);
+}
+
+/*!
+ * \brief Returns type covered field from RRSIG RRSet's rdata.
+ *
+ * \param rdata RRSIG rdata.
+ * \return uint16_t Type covered.
+ */
+uint16_t type_covered_from_rdata(const knot_rdata_t *rdata)
+{
+ return ntohs(*(uint16_t *) rdata_item_data(&(rdata->items[0])));
+}
+
+/*!
+ * \brief Check whether DNSKEY rdata are valid.
+ *
+ * \param rdata DNSKEY rdata to be checked.
+ *
+ * \retval KNOT_EOK when rdata are OK.
+ * \retval ZC_ERR_RRSIG_RDATA_DNSKEY_OWNER when rdata are not OK.
+ */
+static int check_dnskey_rdata(const knot_rdata_t *rdata)
+{
+ /* check that Zone key bit it set - position 7 in net order */
+ /*! \todo FIXME: endian? I swear I've fixed this already, it was 7 i guesss*/
+ uint16_t mask = 1 << 8; //0b0000000100000000;
+
+ uint16_t flags =
+ knot_wire_read_u16((uint8_t *)rdata_item_data
+ (knot_rdata_item(rdata, 0)));
+
+ if (flags & mask) {
+ return KNOT_EOK;
+ } else {
+ /* This error does not exactly fit, but it's better
+ * than a new one */
+ return ZC_ERR_RRSIG_RDATA_DNSKEY_OWNER;
+ }
+}
+
+/*!
+ * \brief Calculates keytag for RSA/SHA algorithm.
+ *
+ * \param key Key wireformat.
+ * \param keysize Wireformat size.
+ *
+ * \return uint16_t Calculated keytag.
+ */
+static uint16_t keytag_1(uint8_t *key, uint16_t keysize)
+{
+ uint16_t ac = 0;
+ if (keysize > 4) {
+ memmove(&ac, key + keysize - 3, 2);
+ }
+
+ ac = ntohs(ac);
+ return ac;
+}
+
+/*!
+ * \brief Calculates keytag from key wire.
+ *
+ * \param key Key wireformat.
+ * \param keysize Wireformat size.
+ *
+ * \return uint16_t Calculated keytag.
+ */
+static uint16_t keytag(uint8_t *key, uint16_t keysize )
+{
+ uint32_t ac = 0; /* assumed to be 32 bits or larger */
+
+ /* algorithm RSA/SHA */
+ if (key[3] == 1) {
+ return keytag_1(key, keysize);
+ } else {
+ for(int i = 0; i < keysize; i++) {
+ ac += (i & 1) ? key[i] : key[i] << 8;
+ }
+
+ ac += (ac >> 16) & 0xFFFF;
+ return (uint16_t)ac & 0xFFFF;
+ }
+}
+
+/*!
+ * \brief Returns size of raw data item.
+ *
+ * \param item Raw data item.
+ *
+ * \return uint16_t Size of raw data item.
+ */
+static inline uint16_t rdata_item_size(const knot_rdata_item_t *item)
+{
+ return item->raw_data[0];
+}
+
+/*!
+ * \brief Converts DNSKEY rdata to wireformat.
+ *
+ * \param rdata DNSKEY rdata to be converted.
+ * \param wire Created wire.
+ * \param size Size of created wire.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval KNOT_ENOMEM on memory error.
+ */
+static int dnskey_to_wire(const knot_rdata_t *rdata, uint8_t **wire,
+ uint *size)
+{
+ assert(*wire == NULL);
+ /* flags + algorithm + protocol + keysize */
+ *size = 2 + 1 + 1 + knot_rdata_item(rdata, 3)->raw_data[0];
+ *wire = malloc(sizeof(uint8_t) * *size);
+ CHECK_ALLOC_LOG(*wire, KNOT_ENOMEM);
+
+ /* copy the wire octet by octet */
+
+ /* TODO check if we really have that many items */
+ if (rdata->count < 4) {
+ return KNOT_ERROR;
+ }
+
+ (*wire)[0] = ((uint8_t *)(knot_rdata_item(rdata, 0)->raw_data))[2];
+ (*wire)[1] = ((uint8_t *)(knot_rdata_item(rdata, 0)->raw_data))[3];
+
+ (*wire)[2] = ((uint8_t *)(knot_rdata_item(rdata, 1)->raw_data))[2];
+ (*wire)[3] = ((uint8_t *)(knot_rdata_item(rdata, 2)->raw_data))[2];
+
+ memcpy(*wire + 4, knot_rdata_item(rdata, 3)->raw_data + 1,
+ knot_rdata_item(rdata, 3)->raw_data[0]);
+
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief Semantic check - RRSIG rdata.
+ *
+ * \param rdata_rrsig RRSIG rdata to be checked.
+ * \param rrset RRSet containing the rdata.
+ * \param dnskey_rrset RRSet containing zone's DNSKEY
+ *
+ * \retval KNOT_EOK if rdata are OK.
+ *
+ * \return Appropriate error code if error was found.
+ */
+static int check_rrsig_rdata(const knot_rdata_t *rdata_rrsig,
+ const knot_rrset_t *rrset,
+ const knot_rrset_t *dnskey_rrset)
+{
+ if (rdata_rrsig == NULL) {
+ return ZC_ERR_RRSIG_NO_RRSIG;
+ }
+
+ if (type_covered_from_rdata(rdata_rrsig) !=
+ knot_rrset_type(rrset)) {
+ /* zoneparser would not let this happen
+ * but to be on the safe side
+ */
+ return ZC_ERR_RRSIG_RDATA_TYPE_COVERED;
+ }
+
+ /* label number at the 2nd index should be same as owner's */
+ uint16_t *raw_data =
+ rdata_item_data(knot_rdata_item(rdata_rrsig, 2));
+
+ uint8_t labels_rdata = ((uint8_t *)raw_data)[0];
+
+ int tmp = knot_dname_label_count(knot_rrset_owner(rrset)) -
+ labels_rdata;
+
+ if (tmp != 0) {
+ /* if name has wildcard, label must not be included */
+ if (!knot_dname_is_wildcard(knot_rrset_owner(rrset))) {
+ return ZC_ERR_RRSIG_RDATA_LABELS;
+ } else {
+ if (abs(tmp) != 1) {
+ return ZC_ERR_RRSIG_RDATA_LABELS;
+ }
+ }
+ }
+
+ /* check original TTL */
+ uint32_t original_ttl =
+ knot_wire_read_u32((uint8_t *)rdata_item_data(
+ knot_rdata_item(rdata_rrsig, 3)));
+
+ if (original_ttl != knot_rrset_ttl(rrset)) {
+ return ZC_ERR_RRSIG_RDATA_TTL;
+ }
+
+ /* signer's name is same as in the zone apex */
+ knot_dname_t *signer_name =
+ knot_rdata_item(rdata_rrsig, 7)->dname;
+
+ /* dnskey is in the apex node */
+ if (knot_dname_compare(signer_name,
+ knot_rrset_owner(dnskey_rrset)) != 0) {
+ return ZC_ERR_RRSIG_RDATA_DNSKEY_OWNER;
+ }
+
+ /* Compare algorithm, key tag and signer's name with DNSKEY rrset
+ * one of the records has to match. Signer name has been checked
+ * before */
+ char match = 0;
+ const knot_rdata_t *tmp_dnskey_rdata =
+ knot_rrset_rdata(dnskey_rrset);
+ do {
+ uint8_t alg =
+ ((uint8_t *)(knot_rdata_item(rdata_rrsig, 1)->raw_data))[2];
+ uint8_t alg_dnskey =
+ ((uint8_t *)(knot_rdata_item(tmp_dnskey_rdata,
+ 2)->raw_data))[2];
+
+ raw_data = rdata_item_data(knot_rdata_item(rdata_rrsig, 6));
+ uint16_t key_tag_rrsig =
+ knot_wire_read_u16((uint8_t *)raw_data);
+
+/* raw_data =
+ rdata_item_data(knot_rdata_item(
+ tmp_dnskey_rdata, 3));
+
+ uint16_t raw_length = rdata_item_size(knot_rdata_item(
+ tmp_dnskey_rdata, 3)); */
+
+ uint8_t *dnskey_wire = NULL;
+ uint dnskey_wire_size = 0;
+
+ int ret = 0;
+ if ((ret = dnskey_to_wire(tmp_dnskey_rdata, &dnskey_wire,
+ &dnskey_wire_size)) != KNOT_EOK) {
+ return ret;
+ }
+
+ uint16_t key_tag_dnskey =
+ keytag(dnskey_wire, dnskey_wire_size);
+
+ free(dnskey_wire);
+
+ match = (alg == alg_dnskey) &&
+ (key_tag_rrsig == key_tag_dnskey) &&
+ !check_dnskey_rdata(tmp_dnskey_rdata);
+
+ } while (!match &&
+ ((tmp_dnskey_rdata =
+ knot_rrset_rdata_next(dnskey_rrset,
+ tmp_dnskey_rdata))
+ != NULL));
+
+ if (!match) {
+ return ZC_ERR_RRSIG_RDATA_SIGNED_WRONG;
+ }
+
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief Semantic check - RRSet's RRSIG.
+ *
+ * \param rrset RRSet containing RRSIG.
+ * \param dnskey_rrset
+ * \param nsec3 NSEC3 active.
+ *
+ * \retval KNOT_EOK on success.
+ *
+ * \return Appropriate error code if error was found.
+ */
+static int check_rrsig_in_rrset(const knot_rrset_t *rrset,
+ const knot_rrset_t *dnskey_rrset,
+ char nsec3)
+{
+ assert(dnskey_rrset && rrset);
+
+ const knot_rrset_t *rrsigs = knot_rrset_rrsigs(rrset);
+
+ if (rrsigs == NULL) {
+ return ZC_ERR_RRSIG_NO_RRSIG;
+ }
+
+ /* signed rrsig - nonsense */
+ if (knot_rrset_rrsigs(rrsigs) != NULL) {
+ return ZC_ERR_RRSIG_SIGNED;
+ }
+
+ /* Different owner, class, ttl */
+
+ if (knot_dname_compare(knot_rrset_owner(rrset),
+ knot_rrset_owner(rrsigs)) != 0) {
+ return ZC_ERR_RRSIG_OWNER;
+ }
+
+ if (knot_rrset_class(rrset) != knot_rrset_class(rrsigs)) {
+ return ZC_ERR_RRSIG_CLASS;
+ }
+
+ if (knot_rrset_ttl(rrset) != knot_rrset_ttl(rrset)) {
+ return ZC_ERR_RRSIG_TTL;
+ }
+
+ /* Check whether all rrsets have their rrsigs */
+ const knot_rdata_t *tmp_rdata = knot_rrset_rdata(rrset);
+ const knot_rdata_t *tmp_rrsig_rdata = knot_rrset_rdata(rrsigs);
+
+ assert(tmp_rdata);
+ assert(tmp_rrsig_rdata);
+ int ret = 0;
+ char all_signed = tmp_rdata && tmp_rrsig_rdata;
+ do {
+ if ((ret = check_rrsig_rdata(tmp_rrsig_rdata,
+ rrset,
+ dnskey_rrset)) != 0) {
+ return ret;
+ }
+
+ all_signed = tmp_rdata && tmp_rrsig_rdata;
+ } while (((tmp_rdata = knot_rrset_rdata_next(rrset, tmp_rdata))
+ != NULL) &&
+ ((tmp_rrsig_rdata =
+ knot_rrset_rdata_next(rrsigs, tmp_rrsig_rdata))
+ != NULL));
+
+ if (!all_signed) {
+ return ZC_ERR_RRSIG_NOT_ALL;
+ }
+
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief Returns bit on index from array in network order. Taken from NSD.
+ *
+ * \param bits Array in network order.
+ * \param index Index to return from array.
+ *
+ * \return int Bit on given index.
+ */
+static int get_bit(uint8_t *bits, size_t index)
+{
+ /*
+ * The bits are counted from left to right, so bit #0 is the
+ * leftmost bit.
+ */
+ return bits[index / 8] & (1 << (7 - index % 8));
+}
+
+/*!
+ * \brief Converts NSEC bitmap to array of integers. (Inspired by NSD code)
+ *
+ * \param item Item containing the bitmap.
+ * \param array Array to be created.
+ * \param count Count of items in array.
+ *
+ * \retval KNOT_OK on success.
+ * \retval KNOT_NOMEM on memory error.
+ */
+static int rdata_nsec_to_type_array(const knot_rdata_item_t *item,
+ uint16_t **array,
+ uint *count)
+{
+ assert(*array == NULL);
+
+ uint8_t *data = (uint8_t *)rdata_item_data(item);
+
+ int increment = 0;
+ *count = 0;
+
+ for (int i = 0; i < rdata_item_size(item); i += increment) {
+ increment = 0;
+ uint8_t window = data[i];
+ increment++;
+
+ uint8_t bitmap_size = data[i + increment];
+ increment++;
+
+ uint8_t *bitmap =
+ malloc(sizeof(uint8_t) * (bitmap_size));
+ if (bitmap == NULL) {
+ ERR_ALLOC_FAILED;
+ free(*array);
+ return KNOT_ENOMEM;
+ }
+
+ memcpy(bitmap, data + i + increment,
+ bitmap_size);
+
+ increment += bitmap_size;
+
+ for (int j = 0; j < bitmap_size * 8; j++) {
+ if (get_bit(bitmap, j)) {
+ (*count)++;
+ void *tmp = realloc(*array,
+ sizeof(uint16_t) *
+ *count);
+ if (tmp == NULL) {
+ ERR_ALLOC_FAILED;
+ free(bitmap);
+ free(*array);
+ return KNOT_ENOMEM;
+ }
+ *array = tmp;
+ (*array)[*count - 1] = j + window * 256;
+ }
+ }
+ free(bitmap);
+ }
+
+ return KNOT_EOK;
+}
+
+/* should write error, not return values !!! */
+
+/*!
+ * \brief Semantic check - check node's NSEC node.
+ *
+ * \param zone Current zone.
+ * \param node Node to be checked.
+ * \param handler Error handler
+ *
+ * \retval KNOT_EOK if no error was found.
+ *
+ * \return Appropriate error code if error was found.
+ */
+static int check_nsec3_node_in_zone(knot_zone_contents_t *zone, knot_node_t *node,
+ err_handler_t *handler)
+{
+ assert(handler);
+ const knot_node_t *nsec3_node = knot_node_nsec3_node(node, 0);
+
+ if (nsec3_node == NULL) {
+ /* I know it's probably not what RFCs say, but it will have to
+ * do for now. */
+ if (knot_node_rrset(node, KNOT_RRTYPE_DS) != NULL) {
+ err_handler_handle_error(handler, node,
+ ZC_ERR_NSEC3_UNSECURED_DELEGATION);
+ } else {
+ /* Unsecured delegation, check whether it is part of
+ * opt-out span */
+ const knot_node_t *nsec3_previous;
+ const knot_node_t *nsec3_node;
+
+ if (knot_zone_contents_find_nsec3_for_name(zone,
+ knot_node_owner(node),
+ &nsec3_node,
+ &nsec3_previous, 0) != 0) {
+ err_handler_handle_error(handler, node,
+ ZC_ERR_NSEC3_NOT_FOUND);
+ }
+
+ if (nsec3_node == NULL) {
+ /* Probably should not ever happen */
+ return ZC_ERR_NSEC3_NOT_FOUND;
+ }
+
+ assert(nsec3_previous);
+
+ const knot_rrset_t *previous_rrset =
+ knot_node_rrset(nsec3_previous,
+ KNOT_RRTYPE_NSEC3);
+
+ assert(previous_rrset);
+
+ /* check for Opt-Out flag */
+ uint8_t flags =
+ ((uint8_t *)(previous_rrset->rdata->items[1].raw_data))[2];
+
+ uint8_t opt_out_mask = 1;
+
+ if (!(flags & opt_out_mask)) {
+ err_handler_handle_error(handler, node,
+ ZC_ERR_NSEC3_UNSECURED_DELEGATION_OPT);
+ }
+ }
+ }
+
+ const knot_rrset_t *nsec3_rrset =
+ knot_node_rrset(nsec3_node, KNOT_RRTYPE_NSEC3);
+
+ assert(nsec3_rrset);
+
+ const knot_rrset_t *soa_rrset =
+ knot_node_rrset(knot_zone_contents_apex(zone),
+ KNOT_RRTYPE_SOA);
+ assert(soa_rrset);
+
+ uint32_t minimum_ttl =
+ knot_wire_read_u32((uint8_t *)
+ rdata_item_data(
+ knot_rdata_item(
+ knot_rrset_rdata(
+ knot_node_rrset(
+ knot_zone_contents_apex(zone), KNOT_RRTYPE_SOA)), 6)));
+ /* Are those getters even worth this?
+ * Now I have no idea what this code does. */
+
+ if (knot_rrset_ttl(nsec3_rrset) != minimum_ttl) {
+ err_handler_handle_error(handler, node,
+ ZC_ERR_NSEC3_RDATA_TTL);
+ }
+
+ /* check that next dname is in the zone */
+ uint8_t *next_dname_decoded = NULL;
+ size_t real_size = 0;
+
+ if (((real_size = base32hex_encode_alloc(((char *)
+ rdata_item_data(&(nsec3_rrset->rdata->items[4]))) + 1,
+ rdata_item_size(&nsec3_rrset->rdata->items[4]) - 1,
+ (char **)&next_dname_decoded)) <= 0) ||
+ (next_dname_decoded == NULL)) {
+ fprintf(stderr, "Could not encode base32 string!\n");
+ return KNOT_ERROR;
+ }
+
+ /* This is why we allocate maximum length of decoded string + 1 */
+ memmove(next_dname_decoded + 1, next_dname_decoded, real_size);
+ next_dname_decoded[0] = real_size;
+
+ /* Local allocation, will be discarded. */
+ knot_dname_t *next_dname =
+ knot_dname_new_from_wire(next_dname_decoded,
+ real_size + 1, NULL);
+ CHECK_ALLOC_LOG(next_dname, KNOT_ENOMEM);
+
+ free(next_dname_decoded);
+
+ if (knot_dname_cat(next_dname,
+ knot_node_owner(knot_zone_contents_apex(zone))) == NULL) {
+ fprintf(stderr, "Could not concatenate dnames!\n");
+ return KNOT_ERROR;
+
+ }
+
+ if (knot_zone_contents_find_nsec3_node(zone, next_dname) == NULL) {
+ err_handler_handle_error(handler, node,
+ ZC_ERR_NSEC3_RDATA_CHAIN);
+ }
+
+ /* Directly discard. */
+ knot_dname_free(&next_dname);
+
+ /* This is probably not sufficient, but again, it is covered in
+ * zone load time */
+
+ uint count;
+ uint16_t *array = NULL;
+ if (rdata_nsec_to_type_array(
+ knot_rdata_item(
+ knot_rrset_rdata(nsec3_rrset), 5),
+ &array, &count) != 0) {
+ err_handler_handle_error(handler, node,
+ ZC_ERR_ALLOC);
+ return KNOT_ERROR;
+ }
+
+ uint16_t type = 0;
+ for (int j = 0; j < count; j++) {
+ /* test for each type's presence */
+ type = array[j];
+ if (type == KNOT_RRTYPE_RRSIG) {
+ continue;
+ }
+ if (knot_node_rrset(node,
+ type) == NULL) {
+ err_handler_handle_error(handler, node,
+ ZC_ERR_NSEC3_RDATA_BITMAP);
+ break;
+/* char *name =
+ knot_dname_to_str(
+ log_zone_error("Node %s does "
+ "not contain RRSet of type %s "
+ "but NSEC bitmap says "
+ "it does!\n", name,
+ knot_rrtype_to_string(type));
+ free(name); */
+ }
+ }
+
+ free(array);
+
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief Run semantic checks for node without DNSSEC-related types.
+ *
+ * \param zone Current zone.
+ * \param node Node to be checked.
+ * \param do_checks Level of checks to be done.
+ * \param handler Error handler.
+ *
+ * \retval KNOT_EOK if no error was found.
+ *
+ * \return Appropriate error code if error was found.
+ */
+static int semantic_checks_plain(knot_zone_contents_t *zone,
+ knot_node_t *node,
+ char do_checks,
+ err_handler_t *handler)
+{
+ assert(handler);
+ const knot_rrset_t *cname_rrset =
+ knot_node_rrset(node, KNOT_RRTYPE_CNAME);
+ if (cname_rrset != NULL) {
+ if (check_cname_cycles_in_zone(zone, cname_rrset) !=
+ KNOT_EOK) {
+ err_handler_handle_error(handler, node,
+ ZC_ERR_CNAME_CYCLE);
+ }
+
+ /* No DNSSEC and yet there is more than one rrset in node */
+ if (do_checks == 1 &&
+ knot_node_rrset_count(node) != 1) {
+ err_handler_handle_error(handler, node,
+ ZC_ERR_CNAME_EXTRA_RECORDS);
+ } else if (knot_node_rrset_count(node) != 1) {
+ /* With DNSSEC node can contain RRSIG or NSEC */
+ if (!(knot_node_rrset(node, KNOT_RRTYPE_RRSIG) ||
+ knot_node_rrset(node, KNOT_RRTYPE_NSEC)) ||
+ knot_node_rrset_count(node) > 3) {
+ err_handler_handle_error(handler, node,
+ ZC_ERR_CNAME_EXTRA_RECORDS_DNSSEC);
+ }
+ }
+
+ if (knot_rrset_rdata(cname_rrset)->next !=
+ knot_rrset_rdata(cname_rrset)) {
+ err_handler_handle_error(handler, node,
+ ZC_ERR_CNAME_MULTIPLE);
+ }
+ }
+
+ const knot_rrset_t *dname_rrset =
+ knot_node_rrset(node, KNOT_RRTYPE_DNAME);
+ if (dname_rrset != NULL) {
+ if (check_cname_cycles_in_zone(zone, dname_rrset) !=
+ KNOT_EOK) {
+ err_handler_handle_error(handler, node,
+ ZC_ERR_DNAME_CYCLE);
+ }
+
+ if (knot_node_rrset(node, KNOT_RRTYPE_CNAME)) {
+ err_handler_handle_error(handler, node,
+ ZC_ERR_DNAME_EXTRA_RECORDS);
+ }
+
+ if (knot_rrset_rdata(dname_rrset)->next !=
+ knot_rrset_rdata(dname_rrset)) {
+ err_handler_handle_error(handler, node,
+ ZC_ERR_DNAME_MULTIPLE);
+ }
+ }
+
+ /* check for glue records at zone cuts */
+ if (knot_node_is_deleg_point(node)) {
+ const knot_rrset_t *ns_rrset =
+ knot_node_rrset(node, KNOT_RRTYPE_NS);
+ assert(ns_rrset);
+ //FIXME this should be an error as well ! (i guess)
+
+ const knot_dname_t *ns_dname =
+ knot_rdata_get_item(knot_rrset_rdata
+ (ns_rrset), 0)->dname;
+
+ assert(ns_dname);
+
+ const knot_node_t *glue_node =
+ knot_zone_contents_find_node(zone, ns_dname);
+
+ if (knot_dname_is_subdomain(ns_dname,
+ knot_node_owner(knot_zone_contents_apex(zone)))) {
+ if (glue_node == NULL) {
+ err_handler_handle_error(handler, node,
+ ZC_ERR_GLUE_NODE);
+ } else {
+ if ((knot_node_rrset(glue_node,
+ KNOT_RRTYPE_A) == NULL) &&
+ (knot_node_rrset(glue_node,
+ KNOT_RRTYPE_AAAA) == NULL)) {
+ err_handler_handle_error(handler, node,
+ ZC_ERR_GLUE_RECORD);
+ }
+ }
+ }
+ }
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief Run semantic checks for node without DNSSEC-related types.
+ *
+ * \param zone Current zone.
+ * \param node Node to be checked.
+ * \param first_node First node in canonical order.
+ * \param last_node Last node in canonical order.
+ * \param handler Error handler.
+ * \param nsec3 NSEC3 used.
+ *
+ * \retval KNOT_EOK if no error was found.
+ *
+ * \return Appropriate error code if error was found.
+ */
+static int semantic_checks_dnssec(knot_zone_contents_t *zone,
+ knot_node_t *node,
+ knot_node_t *first_node,
+ knot_node_t **last_node,
+ err_handler_t *handler,
+ char nsec3)
+{
+ assert(handler);
+ assert(node);
+ char auth = !knot_node_is_non_auth(node);
+ char deleg = knot_node_is_deleg_point(node);
+ uint rrset_count = knot_node_rrset_count(node);
+ const knot_rrset_t **rrsets = knot_node_rrsets(node);
+ const knot_rrset_t *dnskey_rrset =
+ knot_node_rrset(knot_zone_contents_apex(zone),
+ KNOT_RRTYPE_DNSKEY);
+
+ int ret = 0;
+
+ for (int i = 0; i < rrset_count; i++) {
+ const knot_rrset_t *rrset = rrsets[i];
+ if (auth && !deleg &&
+ (ret = check_rrsig_in_rrset(rrset, dnskey_rrset,
+ nsec3)) != 0) {
+ /* CLEANUP */
+/* log_zone_error("RRSIG %d node %s\n", ret,
+ knot_dname_to_str(node->owner));*/
+
+ err_handler_handle_error(handler, node, ret);
+ }
+
+ if (!nsec3 && auth) {
+ /* check for NSEC record */
+ const knot_rrset_t *nsec_rrset =
+ knot_node_rrset(node,
+ KNOT_RRTYPE_NSEC);
+
+ if (nsec_rrset == NULL) {
+ err_handler_handle_error(handler, node,
+ ZC_ERR_NO_NSEC);
+ /* CLEANUP */
+/* char *name =
+ knot_dname_to_str(node->owner);
+ log_zone_error("Missing NSEC in node: "
+ "%s\n", name);
+ free(name);
+ return; */
+ } else {
+
+ /* check NSEC/NSEC3 bitmap */
+
+ uint count;
+
+ uint16_t *array = NULL;
+
+ if (rdata_nsec_to_type_array(
+ knot_rdata_item(
+ knot_rrset_rdata(nsec_rrset),
+ 1),
+ &array, &count) != 0) {
+ err_handler_handle_error(handler,
+ NULL,
+ ZC_ERR_ALLOC);
+ return ZC_ERR_ALLOC; /* ... */
+ /*return; */
+ }
+
+ uint16_t type = 0;
+ for (int j = 0; j < count; j++) {
+ /* test for each type's presence */
+ type = array[j];
+ if (type == KNOT_RRTYPE_RRSIG) {
+ continue;
+ }
+ if (knot_node_rrset(node,
+ type) == NULL) {
+ err_handler_handle_error(
+ handler,
+ node,
+ ZC_ERR_NSEC_RDATA_BITMAP);
+ /* CLEANUP */
+ /* char *name =
+ knot_dname_to_str(
+ knot_node_owner(node));
+
+ log_zone_error("Node %s does "
+ "not contain RRSet of type %s "
+ "but NSEC bitmap says "
+ "it does!\n", name,
+ knot_rrtype_to_string(type));
+
+ free(name); */
+ }
+ }
+ free(array);
+ }
+
+ /* Test that only one record is in the
+ * NSEC RRSet */
+
+ if ((nsec_rrset != NULL) &&
+ knot_rrset_rdata(nsec_rrset)->next !=
+ knot_rrset_rdata(nsec_rrset)) {
+ err_handler_handle_error(handler,
+ node,
+ ZC_ERR_NSEC_RDATA_MULTIPLE);
+ /* CLEANUP */
+/* char *name =
+ knot_dname_to_str(
+ knot_node_owner(node));
+ log_zone_error("Node %s contains more "
+ "than one NSEC "
+ "record!\n", name);
+ knot_rrset_dump(nsec_rrset, 0);
+ free(name); */
+ }
+
+ /*
+ * Test that NSEC chain is coherent.
+ * We have already checked that every
+ * authoritative node contains NSEC record
+ * so checking should only be matter of testing
+ * the next link in each node.
+ */
+
+ if (nsec_rrset != NULL) {
+ knot_dname_t *next_domain =
+ knot_rdata_item(
+ knot_rrset_rdata(nsec_rrset),
+ 0)->dname;
+
+ assert(next_domain);
+
+ if (knot_zone_contents_find_node(zone, next_domain) ==
+ NULL) {
+ err_handler_handle_error(handler,
+ node,
+ ZC_ERR_NSEC_RDATA_CHAIN);
+ /* CLEANUP */
+/* log_zone_error("NSEC chain is not "
+ "coherent!\n"); */
+ }
+
+ if (knot_dname_compare(next_domain,
+ knot_node_owner(knot_zone_contents_apex(zone)))
+ == 0) {
+ /* saving the last node */
+ *last_node = node;
+ }
+
+ }
+ } else if (nsec3 && (auth || deleg)) { /* nsec3 */
+ int ret = check_nsec3_node_in_zone(zone, node,
+ handler);
+ if (ret != KNOT_EOK) {
+ free(rrsets);
+ return ret;
+ }
+ }
+ }
+ free(rrsets);
+
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief Function called by zone traversal function. Used to call
+ * knot_zone_save_enclosers.
+ *
+ * \param node Node to be searched.
+ * \param data Arguments.
+ */
+static void do_checks_in_tree(knot_node_t *node, void *data)
+{
+ assert(data != NULL);
+ arg_t *args = (arg_t *)data;
+
+ knot_rrset_t **rrsets = knot_node_get_rrsets(node);
+ short count = knot_node_rrset_count(node);
+
+ assert(count == 0 || rrsets != NULL);
+
+ knot_zone_contents_t *zone = (knot_zone_contents_t *)args->arg1;
+
+ assert(zone);
+
+
+/* for (int i = 0; i < count; ++i) {
+ assert(rrsets[i] != NULL);
+ knot_zone_save_enclosers_rrset(rrsets[i],
+ zone,
+ (skip_list_t *)args->arg2);
+ } */
+
+ knot_node_t *first_node = (knot_node_t *)args->arg4;
+ knot_node_t **last_node = (knot_node_t **)args->arg5;
+
+ err_handler_t *handler = (err_handler_t *)args->arg6;
+
+ char do_checks = *((char *)(args->arg3));
+
+ if (do_checks) {
+ semantic_checks_plain(zone, node, do_checks, handler);
+ }
+
+ if (do_checks > 1) {
+ semantic_checks_dnssec(zone, node, first_node, last_node,
+ handler, do_checks == 3);
+ }
+
+ free(rrsets);
+}
+
+void zone_do_sem_checks(knot_zone_contents_t *zone, char do_checks,
+ err_handler_t *handler,
+ knot_node_t **last_node)
+{
+ if (!do_checks) {
+ return;
+ }
+
+ arg_t arguments;
+ arguments.arg1 = zone;
+ arguments.arg3 = &do_checks;
+ arguments.arg4 = NULL;
+ arguments.arg5 = last_node;
+ arguments.arg6 = handler;
+
+ knot_zone_contents_tree_apply_inorder(zone,
+ do_checks_in_tree,
+ (void *)&arguments);
+}
+
+void log_cyclic_errors_in_zone(err_handler_t *handler,
+ knot_zone_contents_t *zone,
+ knot_node_t *last_node,
+ const knot_node_t *first_nsec3_node,
+ const knot_node_t *last_nsec3_node,
+ char do_checks)
+{
+ if (do_checks == 3) {
+ /* Each NSEC3 node should only contain one RRSET. */
+ assert(last_nsec3_node && first_nsec3_node);
+ const knot_rrset_t *nsec3_rrset =
+ knot_node_rrset(last_nsec3_node,
+ KNOT_RRTYPE_NSEC3);
+ if (nsec3_rrset == NULL) {
+ err_handler_handle_error(handler, last_nsec3_node,
+ ZC_ERR_NSEC3_RDATA_CHAIN);
+ return;
+ }
+
+ /* check that next dname is in the zone */
+ uint8_t *next_dname_decoded = NULL;
+ size_t real_size = 0;
+
+ if (((real_size = base32hex_encode_alloc(((char *)
+ rdata_item_data(&(nsec3_rrset->rdata->items[4]))) + 1,
+ rdata_item_size(&nsec3_rrset->rdata->items[4]) - 1,
+ (char **)&next_dname_decoded)) <= 0) ||
+ (next_dname_decoded == NULL)) {
+ fprintf(stderr, "Could not encode base32 string!\n");
+ err_handler_handle_error(handler, last_nsec3_node,
+ ZC_ERR_NSEC3_RDATA_CHAIN);
+ return;
+ }
+
+ /* This is why allocate maximum length of decoded string + 1 */
+ memmove(next_dname_decoded + 1, next_dname_decoded, real_size);
+ next_dname_decoded[0] = real_size;
+
+ /* Local allocation, will be discarded. */
+ knot_dname_t *next_dname =
+ knot_dname_new_from_wire(next_dname_decoded,
+ real_size + 1, NULL);
+ if (next_dname == NULL) {
+ fprintf(stderr, "Could not allocate dname!\n");
+ err_handler_handle_error(handler, last_nsec3_node,
+ ZC_ERR_ALLOC);
+ return;
+ }
+
+ free(next_dname_decoded);
+
+ /*! \todo Free result and dname! */
+ if (knot_dname_cat(next_dname,
+ knot_node_owner(knot_zone_contents_apex(zone))) ==
+ NULL) {
+ fprintf(stderr, "Could not concatenate dnames!\n");
+ err_handler_handle_error(handler, last_nsec3_node,
+ ZC_ERR_NSEC3_RDATA_CHAIN);
+ return;
+ }
+
+ /* Check it points somewhere first. */
+ if (knot_zone_contents_find_nsec3_node(zone, next_dname) == NULL) {
+ err_handler_handle_error(handler, last_nsec3_node,
+ ZC_ERR_NSEC3_RDATA_CHAIN);
+ }
+
+ /* Compare with the actual first NSEC3 node. */
+ if (knot_dname_compare(first_nsec3_node->owner,
+ next_dname) != 0) {
+ err_handler_handle_error(handler, last_nsec3_node,
+ ZC_ERR_NSEC3_RDATA_CHAIN);
+ }
+
+ /* Directly discard. */
+ knot_dname_free(&next_dname);
+
+ } else if (do_checks == 2 ) {
+ if (last_node == NULL) {
+ err_handler_handle_error(handler, last_node,
+ ZC_ERR_NSEC_RDATA_CHAIN_NOT_CYCLIC);
+ return;
+ } else {
+ const knot_rrset_t *nsec_rrset =
+ knot_node_rrset(last_node,
+ KNOT_RRTYPE_NSEC);
+
+ if (nsec_rrset == NULL) {
+ err_handler_handle_error(handler, last_node,
+ ZC_ERR_NSEC_RDATA_CHAIN_NOT_CYCLIC);
+ return;
+ }
+
+ const knot_dname_t *next_dname =
+ knot_rdata_item(
+ knot_rrset_rdata(nsec_rrset), 0)->dname;
+ assert(next_dname);
+
+ const knot_dname_t *apex_dname =
+ knot_node_owner(knot_zone_contents_apex(zone));
+ assert(apex_dname);
+
+ if (knot_dname_compare(next_dname, apex_dname) !=0) {
+ err_handler_handle_error(handler, last_node,
+ ZC_ERR_NSEC_RDATA_CHAIN_NOT_CYCLIC);
+ }
+ }
+ }
+}
diff --git a/src/knot/zone/semantic-check.h b/src/knot/zone/semantic-check.h
new file mode 100644
index 0000000..88d828a
--- /dev/null
+++ b/src/knot/zone/semantic-check.h
@@ -0,0 +1,276 @@
+/* Copyright (C) 2011 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/>.
+ */
+/*!
+ * \file semantic-check.h
+ *
+ * \author Jan Kadlec <jan.kadlec@nic.cz>
+ *
+ * \brief DNS zone semantic checks.
+ *
+ * \addtogroup dnslib
+ * @{
+ */
+
+#ifndef _KNOT_SEMANTIC_CHECK_H_
+#define _KNOT_SEMANTIC_CHECK_H_
+
+/*!
+ *\brief Internal error constants. General errors are added for convenience,
+ * so that code does not have to change if new errors are added.
+ */
+enum zonechecks_errors {
+ ZC_ERR_ALLOC = -40,
+ ZC_ERR_UNKNOWN,
+
+ ZC_ERR_MISSING_SOA,
+
+ ZC_ERR_GENERIC_GENERAL_ERROR, /* isn't there a better name? */
+
+ ZC_ERR_RRSIG_RDATA_TYPE_COVERED,
+ ZC_ERR_RRSIG_RDATA_TTL,
+ ZC_ERR_RRSIG_RDATA_LABELS,
+ ZC_ERR_RRSIG_RDATA_DNSKEY_OWNER,
+ ZC_ERR_RRSIG_RDATA_SIGNED_WRONG,
+ ZC_ERR_RRSIG_NO_RRSIG,
+ ZC_ERR_RRSIG_SIGNED,
+ ZC_ERR_RRSIG_OWNER,
+ ZC_ERR_RRSIG_CLASS,
+ ZC_ERR_RRSIG_TTL,
+ ZC_ERR_RRSIG_NOT_ALL,
+
+ ZC_ERR_RRSIG_GENERAL_ERROR,
+
+ ZC_ERR_NO_NSEC,
+ ZC_ERR_NSEC_RDATA_BITMAP,
+ ZC_ERR_NSEC_RDATA_MULTIPLE,
+ ZC_ERR_NSEC_RDATA_CHAIN,
+ ZC_ERR_NSEC_RDATA_CHAIN_NOT_CYCLIC,
+
+ ZC_ERR_NSEC_GENERAL_ERROR,
+
+ ZC_ERR_NSEC3_UNSECURED_DELEGATION,
+ ZC_ERR_NSEC3_NOT_FOUND,
+ ZC_ERR_NSEC3_UNSECURED_DELEGATION_OPT,
+ ZC_ERR_NSEC3_RDATA_TTL,
+ ZC_ERR_NSEC3_RDATA_CHAIN,
+ ZC_ERR_NSEC3_RDATA_BITMAP,
+
+ ZC_ERR_NSEC3_GENERAL_ERROR,
+
+ ZC_ERR_CNAME_CYCLE,
+ ZC_ERR_DNAME_CYCLE,
+ ZC_ERR_CNAME_EXTRA_RECORDS,
+ ZC_ERR_DNAME_EXTRA_RECORDS,
+ ZC_ERR_CNAME_EXTRA_RECORDS_DNSSEC,
+ ZC_ERR_CNAME_MULTIPLE,
+ ZC_ERR_DNAME_MULTIPLE,
+
+ ZC_ERR_CNAME_GENERAL_ERROR,
+
+ ZC_ERR_GLUE_NODE,
+ ZC_ERR_GLUE_RECORD,
+
+ ZC_ERR_GLUE_GENERAL_ERROR,
+};
+
+static char *error_messages[(-ZC_ERR_ALLOC) + 1] = {
+ [-ZC_ERR_ALLOC] = "Memory allocation error!\n",
+
+ [-ZC_ERR_MISSING_SOA] = "SOA record missing in zone!\n",
+
+ [-ZC_ERR_RRSIG_RDATA_TYPE_COVERED] =
+ "RRSIG: Type covered rdata field is wrong!\n",
+ [-ZC_ERR_RRSIG_RDATA_TTL] =
+ "RRSIG: TTL rdata field is wrong!\n",
+ [-ZC_ERR_RRSIG_RDATA_LABELS] =
+ "RRSIG: Labels rdata field is wrong!\n",
+ [-ZC_ERR_RRSIG_RDATA_DNSKEY_OWNER] =
+ "RRSIG: Signer name is different than in DNSKEY!\n",
+ [-ZC_ERR_RRSIG_RDATA_SIGNED_WRONG] =
+ "RRSIG: Key error!\n",
+ [-ZC_ERR_RRSIG_NO_RRSIG] =
+ "RRSIG: No RRSIG!\n",
+ [-ZC_ERR_RRSIG_SIGNED] =
+ "RRSIG: Signed RRSIG!\n",
+ [-ZC_ERR_RRSIG_OWNER] =
+ "RRSIG: Owner name rdata field is wrong!\n",
+ [-ZC_ERR_RRSIG_CLASS] =
+ "RRSIG: Class is wrong!\n",
+ [-ZC_ERR_RRSIG_TTL] =
+ "RRSIG: TTL is wrong!\n",
+ [-ZC_ERR_RRSIG_NOT_ALL] =
+ "RRSIG: Not all RRs are signed!\n",
+
+ [-ZC_ERR_NO_NSEC] =
+ "NSEC: Missing NSEC record\n",
+ [-ZC_ERR_NSEC_RDATA_BITMAP] =
+ "NSEC: Wrong NSEC bitmap!\n",
+ [-ZC_ERR_NSEC_RDATA_MULTIPLE] =
+ "NSEC: Multiple NSEC records!\n",
+ [-ZC_ERR_NSEC_RDATA_CHAIN] =
+ "NSEC: NSEC chain is not coherent!\n",
+ [-ZC_ERR_NSEC_RDATA_CHAIN_NOT_CYCLIC] =
+ "NSEC: NSEC chain is not cyclic!\n",
+
+ [-ZC_ERR_NSEC3_UNSECURED_DELEGATION] =
+ "NSEC3: Zone contains unsecured delegation!\n",
+ [-ZC_ERR_NSEC3_NOT_FOUND] =
+ "NSEC3: Could not find previous NSEC3 record in the zone!\n",
+ [-ZC_ERR_NSEC3_UNSECURED_DELEGATION_OPT] =
+ "NSEC3: Unsecured delegation is not part "
+ "of the Opt-Out span!\n",
+ [-ZC_ERR_NSEC3_RDATA_TTL] =
+ "NSEC3: Original TTL rdata field is wrong!\n",
+ [-ZC_ERR_NSEC3_RDATA_CHAIN] =
+ "NSEC3: NSEC3 chain is not coherent!\n",
+ [-ZC_ERR_NSEC3_RDATA_BITMAP] =
+ "NSEC3: NSEC3 bitmap error!\n",
+
+ [-ZC_ERR_CNAME_CYCLE] =
+ "CNAME: CNAME cycle!\n",
+ [-ZC_ERR_DNAME_CYCLE] =
+ "CNAME: DNAME cycle!\n",
+ [-ZC_ERR_CNAME_EXTRA_RECORDS] =
+ "CNAME: Node with CNAME record has other records!\n",
+ [-ZC_ERR_DNAME_EXTRA_RECORDS] =
+ "DNAME: Node with DNAME record has other records!\n",
+ [-ZC_ERR_CNAME_EXTRA_RECORDS_DNSSEC] =
+ "CNAME: Node with CNAME record has other "
+ "records than RRSIG and NSEC/NSEC3!\n",
+ [-ZC_ERR_CNAME_MULTIPLE] = "CNAME: Multiple CNAME records!\n",
+ [-ZC_ERR_DNAME_MULTIPLE] = "DNAME: Multiple DNAME records!\n",
+
+ /* ^
+ | Important errors (to be logged on first occurence and counted) */
+
+
+ /* Below are errors of lesser importance, to be counted unless
+ specified otherwise */
+
+ [-ZC_ERR_GLUE_NODE] =
+ "GLUE: Node with Glue record missing!\n",
+ [-ZC_ERR_GLUE_RECORD] =
+ "GLUE: Record with Glue address missing\n",
+};
+
+/*!
+ * \brief Arguments to be used with tree traversal functions. Uses void pointers
+ * to be more versatile.
+ *
+ */
+struct arg {
+ void *arg1; /* FILE *f / zone */
+ void *arg2; /* skip_list_t */
+ void *arg3; /* zone */
+ void *arg4; /* first node */
+ void *arg5; /* last node */
+ void *arg6; /* error handler */
+ void *arg7; /* CRC */
+};
+
+typedef struct arg arg_t;
+
+/*!
+ * \brief Structure representing handle options.
+ */
+struct handler_options {
+ char log_cname; /*!< Log all CNAME related semantic errors. */
+ char log_glue; /*!< Log all glue related semantic errors. */
+ char log_rrsigs; /*!< Log all RRSIG related semantic errors. */
+ char log_nsec; /*!< Log all NSEC related semantic errors. */
+ char log_nsec3; /*!< Log all NSEC3 related semantic errors. */
+};
+
+/*!
+ * \brief Structure for handling semantic errors.
+ */
+struct err_handler {
+ /* Consider moving error messages here */
+ struct handler_options options; /*!< Handler options. */
+ uint errors[(-ZC_ERR_ALLOC) + 1]; /*!< Array with error messages */
+};
+
+typedef struct err_handler err_handler_t;
+
+/*!
+ * \brief Creates new semantic error handler.
+ *
+ * \param log_cname If true, log all CNAME related events.
+ * \param log_glue If true, log all Glue related events.
+ * \param log_rrsigs If true, log all RRSIGs related events.
+ * \param log_nsec If true, log all NSEC related events.
+ * \param log_nsec3 If true, log all NSEC3 related events.
+ *
+ * \return err_handler_t * Created error handler.
+ */
+err_handler_t *handler_new(char log_cname, char log_glue,
+ char log_rrsigs, char log_nsec,
+ char log_nsec3);
+
+/*!
+ * \brief Called when error has been encountered in node. Will either log error
+ * or print it, depending on handler's options.
+ *
+ * \param handler Error handler.
+ * \param node Node with semantic error in it.
+ * \param error Type of error.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval ZC_ERR_UNKNOWN if unknown error.
+ * \retval ZC_ERR_ALLOC if memory error.
+ */
+int err_handler_handle_error(err_handler_t *handler,
+ const knot_node_t *node,
+ uint error);
+
+/*!
+ * \brief Checks if last node in NSEC/NSEC3 chain points to first node in the
+ * chain and prints possible errors.
+ *
+ * \param handler Semantic error handler.
+ * \param zone Current zone.
+ * \param last_node Last node in NSEC/NSEC3 chain.
+ * \param do_checks Level of semantic checks.
+ */
+void log_cyclic_errors_in_zone(err_handler_t *handler,
+ knot_zone_contents_t *zone,
+ knot_node_t *last_node,
+ const knot_node_t *first_nsec3_node,
+ const knot_node_t *last_nsec3_node,
+ char do_checks);
+
+/*!
+ * \brief This function prints all errors that occured in zone.
+ *
+ * \param handler Error handler containing found errors.
+ */
+void err_handler_log_all(err_handler_t *handler);
+
+/*!
+ * \brief Helper function - wraps its arguments into arg_t structure and
+ * calls function that does the actual work.
+ *
+ * \param zone Zone to be searched / checked
+ * \param list Skip list of closests enclosers.
+ * \param do_checks Level of semantic checks.
+ * \param handler Semantic error handler.
+ * \param last_node Last checked node, which is part of NSEC(3) chain.
+ */
+void zone_do_sem_checks(knot_zone_contents_t *zone, char do_checks,
+ err_handler_t *handler,
+ knot_node_t **last_node);
+
+#endif // _KNOT_SEMANTIC_CHECK_H_
diff --git a/src/knot/zone/zone-dump.c b/src/knot/zone/zone-dump.c
index afc577d..de4a5a1 100644
--- a/src/knot/zone/zone-dump.c
+++ b/src/knot/zone/zone-dump.c
@@ -26,11 +26,11 @@
#include "libknot/common.h"
#include "knot/zone/zone-dump.h"
#include "libknot/libknot.h"
+#include "common/crc.h"
#include "knot/other/debug.h"
#include "common/skip-list.h"
-#include "common/base32hex.h"
-#include "common/crc.h"
#include "libknot/util/error.h"
+#include "semantic-check.h"
#define ZONECHECKS_VERBOSE
@@ -51,1344 +51,6 @@
* or raw data stored like this: data_len [data]
*/
-static const uint MAX_CNAME_CYCLE_DEPTH = 15;
-
-/*!
- *\brief Internal error constants. General errors are added for convenience,
- * so that code does not have to change if new errors are added.
- */
-enum zonechecks_errors {
- ZC_ERR_ALLOC = -40,
- ZC_ERR_UNKNOWN,
-
- ZC_ERR_MISSING_SOA,
-
- ZC_ERR_GENERIC_GENERAL_ERROR, /* isn't there a better name? */
-
- ZC_ERR_RRSIG_RDATA_TYPE_COVERED,
- ZC_ERR_RRSIG_RDATA_TTL,
- ZC_ERR_RRSIG_RDATA_LABELS,
- ZC_ERR_RRSIG_RDATA_DNSKEY_OWNER,
- ZC_ERR_RRSIG_RDATA_SIGNED_WRONG,
- ZC_ERR_RRSIG_NO_RRSIG,
- ZC_ERR_RRSIG_SIGNED,
- ZC_ERR_RRSIG_OWNER,
- ZC_ERR_RRSIG_CLASS,
- ZC_ERR_RRSIG_TTL,
- ZC_ERR_RRSIG_NOT_ALL,
-
- ZC_ERR_RRSIG_GENERAL_ERROR,
-
- ZC_ERR_NO_NSEC,
- ZC_ERR_NSEC_RDATA_BITMAP,
- ZC_ERR_NSEC_RDATA_MULTIPLE,
- ZC_ERR_NSEC_RDATA_CHAIN,
- ZC_ERR_NSEC_RDATA_CHAIN_NOT_CYCLIC,
-
- ZC_ERR_NSEC_GENERAL_ERROR,
-
- ZC_ERR_NSEC3_UNSECURED_DELEGATION,
- ZC_ERR_NSEC3_NOT_FOUND,
- ZC_ERR_NSEC3_UNSECURED_DELEGATION_OPT,
- ZC_ERR_NSEC3_RDATA_TTL,
- ZC_ERR_NSEC3_RDATA_CHAIN,
- ZC_ERR_NSEC3_RDATA_BITMAP,
-
- ZC_ERR_NSEC3_GENERAL_ERROR,
-
- ZC_ERR_CNAME_CYCLE,
- ZC_ERR_DNAME_CYCLE,
- ZC_ERR_CNAME_EXTRA_RECORDS,
- ZC_ERR_DNAME_EXTRA_RECORDS,
- ZC_ERR_CNAME_EXTRA_RECORDS_DNSSEC,
- ZC_ERR_CNAME_MULTIPLE,
- ZC_ERR_DNAME_MULTIPLE,
-
- ZC_ERR_CNAME_GENERAL_ERROR,
-
- ZC_ERR_GLUE_NODE,
- ZC_ERR_GLUE_RECORD,
-
- ZC_ERR_GLUE_GENERAL_ERROR,
-};
-
-static char *error_messages[(-ZC_ERR_ALLOC) + 1] = {
- [-ZC_ERR_ALLOC] = "Memory allocation error!\n",
-
- [-ZC_ERR_MISSING_SOA] = "SOA record missing in zone!\n",
-
- [-ZC_ERR_RRSIG_RDATA_TYPE_COVERED] =
- "RRSIG: Type covered rdata field is wrong!\n",
- [-ZC_ERR_RRSIG_RDATA_TTL] =
- "RRSIG: TTL rdata field is wrong!\n",
- [-ZC_ERR_RRSIG_RDATA_LABELS] =
- "RRSIG: Labels rdata field is wrong!\n",
- [-ZC_ERR_RRSIG_RDATA_DNSKEY_OWNER] =
- "RRSIG: Signer name is different than in DNSKEY!\n",
- [-ZC_ERR_RRSIG_RDATA_SIGNED_WRONG] =
- "RRSIG: Key error!\n",
- [-ZC_ERR_RRSIG_NO_RRSIG] =
- "RRSIG: No RRSIG!\n",
- [-ZC_ERR_RRSIG_SIGNED] =
- "RRSIG: Signed RRSIG!\n",
- [-ZC_ERR_RRSIG_OWNER] =
- "RRSIG: Owner name rdata field is wrong!\n",
- [-ZC_ERR_RRSIG_CLASS] =
- "RRSIG: Class is wrong!\n",
- [-ZC_ERR_RRSIG_TTL] =
- "RRSIG: TTL is wrong!\n",
- [-ZC_ERR_RRSIG_NOT_ALL] =
- "RRSIG: Not all RRs are signed!\n",
-
- [-ZC_ERR_NO_NSEC] =
- "NSEC: Missing NSEC record\n",
- [-ZC_ERR_NSEC_RDATA_BITMAP] =
- "NSEC: Wrong NSEC bitmap!\n",
- [-ZC_ERR_NSEC_RDATA_MULTIPLE] =
- "NSEC: Multiple NSEC records!\n",
- [-ZC_ERR_NSEC_RDATA_CHAIN] =
- "NSEC: NSEC chain is not coherent!\n",
- [-ZC_ERR_NSEC_RDATA_CHAIN_NOT_CYCLIC] =
- "NSEC: NSEC chain is not cyclic!\n",
-
- [-ZC_ERR_NSEC3_UNSECURED_DELEGATION] =
- "NSEC3: Zone contains unsecured delegation!\n",
- [-ZC_ERR_NSEC3_NOT_FOUND] =
- "NSEC3: Could not find previous NSEC3 record in the zone!\n",
- [-ZC_ERR_NSEC3_UNSECURED_DELEGATION_OPT] =
- "NSEC3: Unsecured delegation is not part "
- "of the Opt-Out span!\n",
- [-ZC_ERR_NSEC3_RDATA_TTL] =
- "NSEC3: Original TTL rdata field is wrong!\n",
- [-ZC_ERR_NSEC3_RDATA_CHAIN] =
- "NSEC3: NSEC3 chain is not coherent!\n",
- [-ZC_ERR_NSEC3_RDATA_BITMAP] =
- "NSEC3: NSEC3 bitmap error!\n",
-
- [-ZC_ERR_CNAME_CYCLE] =
- "CNAME: CNAME cycle!\n",
- [-ZC_ERR_DNAME_CYCLE] =
- "CNAME: DNAME cycle!\n",
- [-ZC_ERR_CNAME_EXTRA_RECORDS] =
- "CNAME: Node with CNAME record has other records!\n",
- [-ZC_ERR_DNAME_EXTRA_RECORDS] =
- "DNAME: Node with DNAME record has other records!\n",
- [-ZC_ERR_CNAME_EXTRA_RECORDS_DNSSEC] =
- "CNAME: Node with CNAME record has other "
- "records than RRSIG and NSEC/NSEC3!\n",
- [-ZC_ERR_CNAME_MULTIPLE] = "CNAME: Multiple CNAME records!\n",
- [-ZC_ERR_DNAME_MULTIPLE] = "DNAME: Multiple DNAME records!\n",
-
- /* ^
- | Important errors (to be logged on first occurence and counted) */
-
-
- /* Below are errors of lesser importance, to be counted unless
- specified otherwise */
-
- [-ZC_ERR_GLUE_NODE] =
- "GLUE: Node with Glue record missing!\n",
- [-ZC_ERR_GLUE_RECORD] =
- "GLUE: Record with Glue address missing\n",
-};
-
-/*!
- * \brief Structure representing handle options.
- */
-struct handler_options {
- char log_cname; /*!< Log all CNAME related semantic errors. */
- char log_glue; /*!< Log all glue related semantic errors. */
- char log_rrsigs; /*!< Log all RRSIG related semantic errors. */
- char log_nsec; /*!< Log all NSEC related semantic errors. */
- char log_nsec3; /*!< Log all NSEC3 related semantic errors. */
-};
-
-/*!
- * \brief Structure for handling semantic errors.
- */
-struct err_handler {
- /* Consider moving error messages here */
- struct handler_options options; /*!< Handler options. */
- uint errors[(-ZC_ERR_ALLOC) + 1]; /*!< Array with error messages */
-};
-
-typedef struct err_handler err_handler_t;
-
-/*!
- * \brief Creates new semantic error handler.
- *
- * \param log_cname If true, log all CNAME related events.
- * \param log_glue If true, log all Glue related events.
- * \param log_rrsigs If true, log all RRSIGs related events.
- * \param log_nsec If true, log all NSEC related events.
- * \param log_nsec3 If true, log all NSEC3 related events.
- *
- * \return err_handler_t * Created error handler.
- */
-static err_handler_t *handler_new(char log_cname, char log_glue,
- char log_rrsigs, char log_nsec,
- char log_nsec3)
-{
- err_handler_t *handler = malloc(sizeof(err_handler_t));
- CHECK_ALLOC_LOG(handler, NULL);
-
- /* It should be initialized, but to be safe */
- memset(handler->errors, 0, sizeof(uint) * (-ZC_ERR_ALLOC + 1));
-
- handler->options.log_cname = log_cname;
- handler->options.log_glue = log_glue;
- handler->options.log_rrsigs = log_rrsigs;
- handler->options.log_nsec = log_nsec;
- handler->options.log_nsec3 = log_nsec3;
-
- return handler;
-}
-
-/*!
- * \brief Prints error message with node information.
- *
- * \note If \a node is NULL, only total number of errors is printed.
- *
- * \param handler Error handler.
- * \param node Node with semantic error in it.
- * \param error Type of error.
- */
-static void log_error_from_node(err_handler_t *handler,
- const knot_node_t *node,
- uint error)
-{
- if (node != NULL) {
- char *name =
- knot_dname_to_str(knot_node_owner(node));
- fprintf(stderr, "Semantic warning in node: %s: ", name);
- fprintf(stderr, "%s", error_messages[-error]);
- free(name);
- } else {
- fprintf(stderr, "Total number of warnings is: %d for error: %s",
- handler->errors[-error],
- error_messages[-error]);
- }
-}
-
-/*!
- * \brief Called when error has been encountered in node. Will either log error
- * or print it, depending on handler's options.
- *
- * \param handler Error handler.
- * \param node Node with semantic error in it.
- * \param error Type of error.
- *
- * \retval KNOT_EOK on success.
- * \retval ZC_ERR_UNKNOWN if unknown error.
- * \retval ZC_ERR_ALLOC if memory error.
- */
-static int err_handler_handle_error(err_handler_t *handler,
- const knot_node_t *node,
- uint error)
-{
- assert(handler && node);
- if ((error != 0) &&
- (error > ZC_ERR_GLUE_GENERAL_ERROR)) {
- return ZC_ERR_UNKNOWN;
- }
-
- if (error == ZC_ERR_ALLOC) {
- ERR_ALLOC_FAILED;
- return ZC_ERR_ALLOC;
- }
-
- /* missing SOA can only occur once, so there
- * needn't to be an option for it */
-
- if ((error != 0) &&
- (error < ZC_ERR_GENERIC_GENERAL_ERROR)) {
- /* The two errors before SOA were handled */
- log_error_from_node(handler, node, error);
-
- } else if ((error < ZC_ERR_RRSIG_GENERAL_ERROR) &&
- ((handler->errors[-error] == 0) ||
- (handler->options.log_rrsigs))) {
-
- log_error_from_node(handler, node, error);
-
- } else if ((error > ZC_ERR_RRSIG_GENERAL_ERROR) &&
- (error < ZC_ERR_NSEC_GENERAL_ERROR) &&
- ((handler->errors[-error] == 0) ||
- (handler->options.log_nsec))) {
-
- log_error_from_node(handler, node, error);
-
- } else if ((error > ZC_ERR_NSEC_GENERAL_ERROR) &&
- (error < ZC_ERR_NSEC3_GENERAL_ERROR) &&
- ((handler->errors[-error] == 0) ||
- (handler->options.log_nsec3))) {
-
- log_error_from_node(handler, node, error);
-
- } else if ((error > ZC_ERR_NSEC3_GENERAL_ERROR) &&
- (error < ZC_ERR_CNAME_GENERAL_ERROR) &&
- ((handler->errors[-error] == 0) ||
- (handler->options.log_cname))) {
-
- log_error_from_node(handler, node, error);
-
- } else if ((error > ZC_ERR_CNAME_GENERAL_ERROR) &&
- (error < ZC_ERR_GLUE_GENERAL_ERROR) &&
- handler->options.log_glue) {
-
- log_error_from_node(handler, node, error);
-
- }
-
- handler->errors[-error]++;
-
- return KNOT_EOK;
-}
-
-/*!
- * \brief This function prints all errors that occured in zone.
- *
- * \param handler Error handler containing found errors.
- */
-static void err_handler_log_all(err_handler_t *handler)
-{
- if (handler == NULL) {
- return;
- }
-
- for (int i = ZC_ERR_ALLOC; i < ZC_ERR_GLUE_GENERAL_ERROR; i++) {
- if (handler->errors[-i] > 0) {
- log_error_from_node(handler, NULL, i);
- }
- }
-}
-
-/*!
- * \brief Arguments to be used with tree traversal functions. Uses void pointers
- * to be more versatile.
- *
- */
-struct arg {
- void *arg1; /* FILE *f / zone */
- void *arg2; /* skip_list_t */
- void *arg3; /* zone */
- void *arg4; /* first node */
- void *arg5; /* last node */
- void *arg6; /* error handler */
- void *arg7; /* CRC */
-};
-
-typedef struct arg arg_t;
-
-/*!
- * \brief Semantic check - CNAME cycles. Uses constant value with maximum
- * allowed CNAME chain depth.
- *
- * \param zone Zone containing the RRSet.
- * \param rrset RRSet to be tested.
- *
- * \retval KNOT_EOK when there is no cycle.
- * \retval ZC_ERR_CNAME_CYCLE when cycle is present.
- */
-static int check_cname_cycles_in_zone(knot_zone_contents_t *zone,
- const knot_rrset_t *rrset)
-{
- const knot_rrset_t *next_rrset = rrset;
- assert(rrset);
- const knot_rdata_t *tmp_rdata = knot_rrset_rdata(next_rrset);
- const knot_node_t *next_node = NULL;
-
- uint i = 0;
-
- assert(tmp_rdata);
-
- const knot_dname_t *next_dname =
- knot_rdata_cname_name(tmp_rdata);
-
- assert(next_dname);
-
- while (i < MAX_CNAME_CYCLE_DEPTH && next_dname != NULL) {
- next_node = knot_zone_contents_get_node(zone, next_dname);
- if (next_node == NULL) {
- next_node =
- knot_zone_contents_get_nsec3_node(zone, next_dname);
- }
-
- if (next_node != NULL) {
- next_rrset = knot_node_rrset(next_node,
- KNOT_RRTYPE_CNAME);
- if (next_rrset != NULL) {
- next_dname =
- knot_rdata_cname_name(next_rrset->rdata);
- } else {
- next_node = NULL;
- next_dname = NULL;
- }
- } else {
- next_dname = NULL;
- }
- i++;
- }
-
- /* even if the length is 0, i will be 1 */
- if (i >= MAX_CNAME_CYCLE_DEPTH) {
- return ZC_ERR_CNAME_CYCLE;
- }
-
- return KNOT_EOK;
-}
-
-/*!
- * \brief Return raw data from rdata item structure (without length).
- *
- * \param item Item to get rdata from.
- * \return uint16_t * raw data without length.
- */
-static inline uint16_t *rdata_item_data(const knot_rdata_item_t *item)
-{
- return (uint16_t *)(item->raw_data + 1);
-}
-
-/*!
- * \brief Returns type covered field from RRSIG RRSet's rdata.
- *
- * \param rdata RRSIG rdata.
- * \return uint16_t Type covered.
- */
-uint16_t type_covered_from_rdata(const knot_rdata_t *rdata)
-{
- return ntohs(*(uint16_t *) rdata_item_data(&(rdata->items[0])));
-}
-
-/*!
- * \brief Check whether DNSKEY rdata are valid.
- *
- * \param rdata DNSKEY rdata to be checked.
- *
- * \retval KNOT_EOK when rdata are OK.
- * \retval ZC_ERR_RRSIG_RDATA_DNSKEY_OWNER when rdata are not OK.
- */
-static int check_dnskey_rdata(const knot_rdata_t *rdata)
-{
- /* check that Zone key bit it set - position 7 in net order */
- /*! \todo FIXME: endian? I swear I've fixed this already, it was 7 i guesss*/
- uint16_t mask = 1 << 8; //0b0000000100000000;
-
- uint16_t flags =
- knot_wire_read_u16((uint8_t *)rdata_item_data
- (knot_rdata_item(rdata, 0)));
-
- if (flags & mask) {
- return KNOT_EOK;
- } else {
- /* This error does not exactly fit, but it's better
- * than a new one */
- return ZC_ERR_RRSIG_RDATA_DNSKEY_OWNER;
- }
-}
-
-/*!
- * \brief Calculates keytag for RSA/SHA algorithm.
- *
- * \param key Key wireformat.
- * \param keysize Wireformat size.
- *
- * \return uint16_t Calculated keytag.
- */
-static uint16_t keytag_1(uint8_t *key, uint16_t keysize)
-{
- uint16_t ac = 0;
- if (keysize > 4) {
- memmove(&ac, key + keysize - 3, 2);
- }
-
- ac = ntohs(ac);
- return ac;
-}
-
-/*!
- * \brief Calculates keytag from key wire.
- *
- * \param key Key wireformat.
- * \param keysize Wireformat size.
- *
- * \return uint16_t Calculated keytag.
- */
-static uint16_t keytag(uint8_t *key, uint16_t keysize )
-{
- uint32_t ac = 0; /* assumed to be 32 bits or larger */
-
- /* algorithm RSA/SHA */
- if (key[3] == 1) {
- return keytag_1(key, keysize);
- } else {
- for(int i = 0; i < keysize; i++) {
- ac += (i & 1) ? key[i] : key[i] << 8;
- }
-
- ac += (ac >> 16) & 0xFFFF;
- return (uint16_t)ac & 0xFFFF;
- }
-}
-
-/*!
- * \brief Returns size of raw data item.
- *
- * \param item Raw data item.
- *
- * \return uint16_t Size of raw data item.
- */
-static inline uint16_t rdata_item_size(const knot_rdata_item_t *item)
-{
- return item->raw_data[0];
-}
-
-/*!
- * \brief Converts DNSKEY rdata to wireformat.
- *
- * \param rdata DNSKEY rdata to be converted.
- * \param wire Created wire.
- * \param size Size of created wire.
- *
- * \retval KNOT_EOK on success.
- * \retval KNOT_ENOMEM on memory error.
- */
-static int dnskey_to_wire(const knot_rdata_t *rdata, uint8_t **wire,
- uint *size)
-{
- assert(*wire == NULL);
- /* flags + algorithm + protocol + keysize */
- *size = 2 + 1 + 1 + knot_rdata_item(rdata, 3)->raw_data[0];
- *wire = malloc(sizeof(uint8_t) * *size);
- CHECK_ALLOC_LOG(*wire, KNOT_ENOMEM);
-
- /* copy the wire octet by octet */
-
- /* TODO check if we really have that many items */
- if (rdata->count < 4) {
- return KNOT_ERROR;
- }
-
- (*wire)[0] = ((uint8_t *)(knot_rdata_item(rdata, 0)->raw_data))[2];
- (*wire)[1] = ((uint8_t *)(knot_rdata_item(rdata, 0)->raw_data))[3];
-
- (*wire)[2] = ((uint8_t *)(knot_rdata_item(rdata, 1)->raw_data))[2];
- (*wire)[3] = ((uint8_t *)(knot_rdata_item(rdata, 2)->raw_data))[2];
-
- memcpy(*wire + 4, knot_rdata_item(rdata, 3)->raw_data + 1,
- knot_rdata_item(rdata, 3)->raw_data[0]);
-
- return KNOT_EOK;
-}
-
-/*!
- * \brief Semantic check - RRSIG rdata.
- *
- * \param rdata_rrsig RRSIG rdata to be checked.
- * \param rrset RRSet containing the rdata.
- * \param dnskey_rrset RRSet containing zone's DNSKEY
- *
- * \retval KNOT_EOK if rdata are OK.
- *
- * \return Appropriate error code if error was found.
- */
-static int check_rrsig_rdata(const knot_rdata_t *rdata_rrsig,
- const knot_rrset_t *rrset,
- const knot_rrset_t *dnskey_rrset)
-{
- if (rdata_rrsig == NULL) {
- return ZC_ERR_RRSIG_NO_RRSIG;
- }
-
- if (type_covered_from_rdata(rdata_rrsig) !=
- knot_rrset_type(rrset)) {
- /* zoneparser would not let this happen
- * but to be on the safe side
- */
- return ZC_ERR_RRSIG_RDATA_TYPE_COVERED;
- }
-
- /* label number at the 2nd index should be same as owner's */
- uint16_t *raw_data =
- rdata_item_data(knot_rdata_item(rdata_rrsig, 2));
-
- uint8_t labels_rdata = ((uint8_t *)raw_data)[0];
-
- int tmp = knot_dname_label_count(knot_rrset_owner(rrset)) -
- labels_rdata;
-
- if (tmp != 0) {
- /* if name has wildcard, label must not be included */
- if (!knot_dname_is_wildcard(knot_rrset_owner(rrset))) {
- return ZC_ERR_RRSIG_RDATA_LABELS;
- } else {
- if (abs(tmp) != 1) {
- return ZC_ERR_RRSIG_RDATA_LABELS;
- }
- }
- }
-
- /* check original TTL */
- uint32_t original_ttl =
- knot_wire_read_u32((uint8_t *)rdata_item_data(
- knot_rdata_item(rdata_rrsig, 3)));
-
- if (original_ttl != knot_rrset_ttl(rrset)) {
- return ZC_ERR_RRSIG_RDATA_TTL;
- }
-
- /* signer's name is same as in the zone apex */
- knot_dname_t *signer_name =
- knot_rdata_item(rdata_rrsig, 7)->dname;
-
- /* dnskey is in the apex node */
- if (knot_dname_compare(signer_name,
- knot_rrset_owner(dnskey_rrset)) != 0) {
- return ZC_ERR_RRSIG_RDATA_DNSKEY_OWNER;
- }
-
- /* Compare algorithm, key tag and signer's name with DNSKEY rrset
- * one of the records has to match. Signer name has been checked
- * before */
- char match = 0;
- const knot_rdata_t *tmp_dnskey_rdata =
- knot_rrset_rdata(dnskey_rrset);
- do {
- uint8_t alg =
- ((uint8_t *)(knot_rdata_item(rdata_rrsig, 1)->raw_data))[2];
- uint8_t alg_dnskey =
- ((uint8_t *)(knot_rdata_item(tmp_dnskey_rdata,
- 2)->raw_data))[2];
-
- raw_data = rdata_item_data(knot_rdata_item(rdata_rrsig, 6));
- uint16_t key_tag_rrsig =
- knot_wire_read_u16((uint8_t *)raw_data);
-
-/* raw_data =
- rdata_item_data(knot_rdata_item(
- tmp_dnskey_rdata, 3));
-
- uint16_t raw_length = rdata_item_size(knot_rdata_item(
- tmp_dnskey_rdata, 3)); */
-
- uint8_t *dnskey_wire = NULL;
- uint dnskey_wire_size = 0;
-
- int ret = 0;
- if ((ret = dnskey_to_wire(tmp_dnskey_rdata, &dnskey_wire,
- &dnskey_wire_size)) != KNOT_EOK) {
- return ret;
- }
-
- uint16_t key_tag_dnskey =
- keytag(dnskey_wire, dnskey_wire_size);
-
- free(dnskey_wire);
-
- match = (alg == alg_dnskey) &&
- (key_tag_rrsig == key_tag_dnskey) &&
- !check_dnskey_rdata(tmp_dnskey_rdata);
-
- } while (!match &&
- ((tmp_dnskey_rdata =
- knot_rrset_rdata_next(dnskey_rrset,
- tmp_dnskey_rdata))
- != NULL));
-
- if (!match) {
- return ZC_ERR_RRSIG_RDATA_SIGNED_WRONG;
- }
-
- return KNOT_EOK;
-}
-
-/*!
- * \brief Semantic check - RRSet's RRSIG.
- *
- * \param rrset RRSet containing RRSIG.
- * \param dnskey_rrset
- * \param nsec3 NSEC3 active.
- *
- * \retval KNOT_EOK on success.
- *
- * \return Appropriate error code if error was found.
- */
-static int check_rrsig_in_rrset(const knot_rrset_t *rrset,
- const knot_rrset_t *dnskey_rrset,
- char nsec3)
-{
- assert(dnskey_rrset && rrset);
-
- const knot_rrset_t *rrsigs = knot_rrset_rrsigs(rrset);
-
- if (rrsigs == NULL) {
- return ZC_ERR_RRSIG_NO_RRSIG;
- }
-
- /* signed rrsig - nonsense */
- if (knot_rrset_rrsigs(rrsigs) != NULL) {
- return ZC_ERR_RRSIG_SIGNED;
- }
-
- /* Different owner, class, ttl */
-
- if (knot_dname_compare(knot_rrset_owner(rrset),
- knot_rrset_owner(rrsigs)) != 0) {
- return ZC_ERR_RRSIG_OWNER;
- }
-
- if (knot_rrset_class(rrset) != knot_rrset_class(rrsigs)) {
- return ZC_ERR_RRSIG_CLASS;
- }
-
- if (knot_rrset_ttl(rrset) != knot_rrset_ttl(rrset)) {
- return ZC_ERR_RRSIG_TTL;
- }
-
- /* Check whether all rrsets have their rrsigs */
- const knot_rdata_t *tmp_rdata = knot_rrset_rdata(rrset);
- const knot_rdata_t *tmp_rrsig_rdata = knot_rrset_rdata(rrsigs);
-
- assert(tmp_rdata);
- assert(tmp_rrsig_rdata);
- int ret = 0;
- char all_signed = tmp_rdata && tmp_rrsig_rdata;
- do {
- if ((ret = check_rrsig_rdata(tmp_rrsig_rdata,
- rrset,
- dnskey_rrset)) != 0) {
- return ret;
- }
-
- all_signed = tmp_rdata && tmp_rrsig_rdata;
- } while (((tmp_rdata = knot_rrset_rdata_next(rrset, tmp_rdata))
- != NULL) &&
- ((tmp_rrsig_rdata =
- knot_rrset_rdata_next(rrsigs, tmp_rrsig_rdata))
- != NULL));
-
- if (!all_signed) {
- return ZC_ERR_RRSIG_NOT_ALL;
- }
-
- return KNOT_EOK;
-}
-
-/*!
- * \brief Returns bit on index from array in network order. Taken from NSD.
- *
- * \param bits Array in network order.
- * \param index Index to return from array.
- *
- * \return int Bit on given index.
- */
-static int get_bit(uint8_t *bits, size_t index)
-{
- /*
- * The bits are counted from left to right, so bit #0 is the
- * leftmost bit.
- */
- return bits[index / 8] & (1 << (7 - index % 8));
-}
-
-/*!
- * \brief Converts NSEC bitmap to array of integers. (Inspired by NSD code)
- *
- * \param item Item containing the bitmap.
- * \param array Array to be created.
- * \param count Count of items in array.
- *
- * \retval KNOT_OK on success.
- * \retval KNOT_NOMEM on memory error.
- */
-static int rdata_nsec_to_type_array(const knot_rdata_item_t *item,
- uint16_t **array,
- uint *count)
-{
- assert(*array == NULL);
-
- uint8_t *data = (uint8_t *)rdata_item_data(item);
-
- int increment = 0;
- *count = 0;
-
- for (int i = 0; i < rdata_item_size(item); i += increment) {
- increment = 0;
- uint8_t window = data[i];
- increment++;
-
- uint8_t bitmap_size = data[i + increment];
- increment++;
-
- uint8_t *bitmap =
- malloc(sizeof(uint8_t) * (bitmap_size));
- if (bitmap == NULL) {
- ERR_ALLOC_FAILED;
- free(*array);
- return KNOT_ENOMEM;
- }
-
- memcpy(bitmap, data + i + increment,
- bitmap_size);
-
- increment += bitmap_size;
-
- for (int j = 0; j < bitmap_size * 8; j++) {
- if (get_bit(bitmap, j)) {
- (*count)++;
- void *tmp = realloc(*array,
- sizeof(uint16_t) *
- *count);
- if (tmp == NULL) {
- ERR_ALLOC_FAILED;
- free(bitmap);
- free(*array);
- return KNOT_ENOMEM;
- }
- *array = tmp;
- (*array)[*count - 1] = j + window * 256;
- }
- }
- free(bitmap);
- }
-
- return KNOT_EOK;
-}
-
-/* should write error, not return values !!! */
-
-/*!
- * \brief Semantic check - check node's NSEC node.
- *
- * \param zone Current zone.
- * \param node Node to be checked.
- * \param handler Error handler
- *
- * \retval KNOT_EOK if no error was found.
- *
- * \return Appropriate error code if error was found.
- */
-static int check_nsec3_node_in_zone(knot_zone_contents_t *zone, knot_node_t *node,
- err_handler_t *handler)
-{
- assert(handler);
- const knot_node_t *nsec3_node = knot_node_nsec3_node(node, 0);
-
- if (nsec3_node == NULL) {
- /* I know it's probably not what RFCs say, but it will have to
- * do for now. */
- if (knot_node_rrset(node, KNOT_RRTYPE_DS) != NULL) {
- err_handler_handle_error(handler, node,
- ZC_ERR_NSEC3_UNSECURED_DELEGATION);
- } else {
- /* Unsecured delegation, check whether it is part of
- * opt-out span */
- const knot_node_t *nsec3_previous;
- const knot_node_t *nsec3_node;
-
- if (knot_zone_contents_find_nsec3_for_name(zone,
- knot_node_owner(node),
- &nsec3_node,
- &nsec3_previous, 0) != 0) {
- err_handler_handle_error(handler, node,
- ZC_ERR_NSEC3_NOT_FOUND);
- }
-
- if (nsec3_node == NULL) {
- /* Probably should not ever happen */
- return ZC_ERR_NSEC3_NOT_FOUND;
- }
-
- assert(nsec3_previous);
-
- const knot_rrset_t *previous_rrset =
- knot_node_rrset(nsec3_previous,
- KNOT_RRTYPE_NSEC3);
-
- assert(previous_rrset);
-
- /* check for Opt-Out flag */
- uint8_t flags =
- ((uint8_t *)(previous_rrset->rdata->items[1].raw_data))[2];
-
- uint8_t opt_out_mask = 1;
-
- if (!(flags & opt_out_mask)) {
- err_handler_handle_error(handler, node,
- ZC_ERR_NSEC3_UNSECURED_DELEGATION_OPT);
- }
- }
- }
-
- const knot_rrset_t *nsec3_rrset =
- knot_node_rrset(nsec3_node, KNOT_RRTYPE_NSEC3);
-
- assert(nsec3_rrset);
-
- const knot_rrset_t *soa_rrset =
- knot_node_rrset(knot_zone_contents_apex(zone),
- KNOT_RRTYPE_SOA);
- assert(soa_rrset);
-
- uint32_t minimum_ttl =
- knot_wire_read_u32((uint8_t *)
- rdata_item_data(
- knot_rdata_item(
- knot_rrset_rdata(
- knot_node_rrset(
- knot_zone_contents_apex(zone), KNOT_RRTYPE_SOA)), 6)));
- /* Are those getters even worth this?
- * Now I have no idea what this code does. */
-
- if (knot_rrset_ttl(nsec3_rrset) != minimum_ttl) {
- err_handler_handle_error(handler, node,
- ZC_ERR_NSEC3_RDATA_TTL);
- }
-
- /* check that next dname is in the zone */
- uint8_t *next_dname_decoded = NULL;
- size_t real_size = 0;
-
- if (((real_size = base32hex_encode_alloc(((char *)
- rdata_item_data(&(nsec3_rrset->rdata->items[4]))) + 1,
- rdata_item_size(&nsec3_rrset->rdata->items[4]) - 1,
- (char **)&next_dname_decoded)) <= 0) ||
- (next_dname_decoded == NULL)) {
- fprintf(stderr, "Could not encode base32 string!\n");
- return KNOT_ERROR;
- }
-
- /* This is why we allocate maximum length of decoded string + 1 */
- memmove(next_dname_decoded + 1, next_dname_decoded, real_size);
- next_dname_decoded[0] = real_size;
-
- /* Local allocation, will be discarded. */
- knot_dname_t *next_dname =
- knot_dname_new_from_wire(next_dname_decoded,
- real_size + 1, NULL);
- CHECK_ALLOC_LOG(next_dname, KNOT_ENOMEM);
-
- free(next_dname_decoded);
-
- if (knot_dname_cat(next_dname,
- knot_node_owner(knot_zone_contents_apex(zone))) == NULL) {
- fprintf(stderr, "Could not concatenate dnames!\n");
- return KNOT_ERROR;
-
- }
-
- if (knot_zone_contents_find_nsec3_node(zone, next_dname) == NULL) {
- err_handler_handle_error(handler, node,
- ZC_ERR_NSEC3_RDATA_CHAIN);
- }
-
- /* Directly discard. */
- knot_dname_free(&next_dname);
-
- /* This is probably not sufficient, but again, it is covered in
- * zone load time */
-
- uint count;
- uint16_t *array = NULL;
- if (rdata_nsec_to_type_array(
- knot_rdata_item(
- knot_rrset_rdata(nsec3_rrset), 5),
- &array, &count) != 0) {
- err_handler_handle_error(handler, node,
- ZC_ERR_ALLOC);
- return KNOT_ERROR;
- }
-
- uint16_t type = 0;
- for (int j = 0; j < count; j++) {
- /* test for each type's presence */
- type = array[j];
- if (type == KNOT_RRTYPE_RRSIG) {
- continue;
- }
- if (knot_node_rrset(node,
- type) == NULL) {
- err_handler_handle_error(handler, node,
- ZC_ERR_NSEC3_RDATA_BITMAP);
- break;
-/* char *name =
- knot_dname_to_str(
- log_zone_error("Node %s does "
- "not contain RRSet of type %s "
- "but NSEC bitmap says "
- "it does!\n", name,
- knot_rrtype_to_string(type));
- free(name); */
- }
- }
-
- free(array);
-
- return KNOT_EOK;
-}
-
-/*!
- * \brief Run semantic checks for node without DNSSEC-related types.
- *
- * \param zone Current zone.
- * \param node Node to be checked.
- * \param do_checks Level of checks to be done.
- * \param handler Error handler.
- *
- * \retval KNOT_EOK if no error was found.
- *
- * \return Appropriate error code if error was found.
- */
-static int semantic_checks_plain(knot_zone_contents_t *zone,
- knot_node_t *node,
- char do_checks,
- err_handler_t *handler)
-{
- assert(handler);
- const knot_rrset_t *cname_rrset =
- knot_node_rrset(node, KNOT_RRTYPE_CNAME);
- if (cname_rrset != NULL) {
- if (check_cname_cycles_in_zone(zone, cname_rrset) !=
- KNOT_EOK) {
- err_handler_handle_error(handler, node,
- ZC_ERR_CNAME_CYCLE);
- }
-
- /* No DNSSEC and yet there is more than one rrset in node */
- if (do_checks == 1 &&
- knot_node_rrset_count(node) != 1) {
- err_handler_handle_error(handler, node,
- ZC_ERR_CNAME_EXTRA_RECORDS);
- } else if (knot_node_rrset_count(node) != 1) {
- /* With DNSSEC node can contain RRSIG or NSEC */
- if (!(knot_node_rrset(node, KNOT_RRTYPE_RRSIG) ||
- knot_node_rrset(node, KNOT_RRTYPE_NSEC)) ||
- knot_node_rrset_count(node) > 3) {
- err_handler_handle_error(handler, node,
- ZC_ERR_CNAME_EXTRA_RECORDS_DNSSEC);
- }
- }
-
- if (knot_rrset_rdata(cname_rrset)->next !=
- knot_rrset_rdata(cname_rrset)) {
- err_handler_handle_error(handler, node,
- ZC_ERR_CNAME_MULTIPLE);
- }
- }
-
- const knot_rrset_t *dname_rrset =
- knot_node_rrset(node, KNOT_RRTYPE_DNAME);
- if (dname_rrset != NULL) {
- if (check_cname_cycles_in_zone(zone, dname_rrset) !=
- KNOT_EOK) {
- err_handler_handle_error(handler, node,
- ZC_ERR_DNAME_CYCLE);
- }
-
- if (knot_node_rrset(node, KNOT_RRTYPE_CNAME)) {
- err_handler_handle_error(handler, node,
- ZC_ERR_DNAME_EXTRA_RECORDS);
- }
-
- if (knot_rrset_rdata(dname_rrset)->next !=
- knot_rrset_rdata(dname_rrset)) {
- err_handler_handle_error(handler, node,
- ZC_ERR_DNAME_MULTIPLE);
- }
- }
-
- /* check for glue records at zone cuts */
- if (knot_node_is_deleg_point(node)) {
- const knot_rrset_t *ns_rrset =
- knot_node_rrset(node, KNOT_RRTYPE_NS);
- assert(ns_rrset);
- //FIXME this should be an error as well ! (i guess)
-
- const knot_dname_t *ns_dname =
- knot_rdata_get_item(knot_rrset_rdata
- (ns_rrset), 0)->dname;
-
- assert(ns_dname);
-
- const knot_node_t *glue_node =
- knot_zone_contents_find_node(zone, ns_dname);
-
- if (knot_dname_is_subdomain(ns_dname,
- knot_node_owner(knot_zone_contents_apex(zone)))) {
- if (glue_node == NULL) {
- err_handler_handle_error(handler, node,
- ZC_ERR_GLUE_NODE);
- } else {
- if ((knot_node_rrset(glue_node,
- KNOT_RRTYPE_A) == NULL) &&
- (knot_node_rrset(glue_node,
- KNOT_RRTYPE_AAAA) == NULL)) {
- err_handler_handle_error(handler, node,
- ZC_ERR_GLUE_RECORD);
- }
- }
- }
- }
- return KNOT_EOK;
-}
-
-/*!
- * \brief Run semantic checks for node without DNSSEC-related types.
- *
- * \param zone Current zone.
- * \param node Node to be checked.
- * \param first_node First node in canonical order.
- * \param last_node Last node in canonical order.
- * \param handler Error handler.
- * \param nsec3 NSEC3 used.
- *
- * \retval KNOT_EOK if no error was found.
- *
- * \return Appropriate error code if error was found.
- */
-static int semantic_checks_dnssec(knot_zone_contents_t *zone,
- knot_node_t *node,
- knot_node_t *first_node,
- knot_node_t **last_node,
- err_handler_t *handler,
- char nsec3)
-{
- assert(handler);
- assert(node);
- char auth = !knot_node_is_non_auth(node);
- char deleg = knot_node_is_deleg_point(node);
- uint rrset_count = knot_node_rrset_count(node);
- const knot_rrset_t **rrsets = knot_node_rrsets(node);
- const knot_rrset_t *dnskey_rrset =
- knot_node_rrset(knot_zone_contents_apex(zone),
- KNOT_RRTYPE_DNSKEY);
-
- int ret = 0;
-
- for (int i = 0; i < rrset_count; i++) {
- const knot_rrset_t *rrset = rrsets[i];
- if (auth && !deleg &&
- (ret = check_rrsig_in_rrset(rrset, dnskey_rrset,
- nsec3)) != 0) {
- /* CLEANUP */
-/* log_zone_error("RRSIG %d node %s\n", ret,
- knot_dname_to_str(node->owner));*/
-
- err_handler_handle_error(handler, node, ret);
- }
-
- if (!nsec3 && auth) {
- /* check for NSEC record */
- const knot_rrset_t *nsec_rrset =
- knot_node_rrset(node,
- KNOT_RRTYPE_NSEC);
-
- if (nsec_rrset == NULL) {
- err_handler_handle_error(handler, node,
- ZC_ERR_NO_NSEC);
- /* CLEANUP */
-/* char *name =
- knot_dname_to_str(node->owner);
- log_zone_error("Missing NSEC in node: "
- "%s\n", name);
- free(name);
- return; */
- } else {
-
- /* check NSEC/NSEC3 bitmap */
-
- uint count;
-
- uint16_t *array = NULL;
-
- if (rdata_nsec_to_type_array(
- knot_rdata_item(
- knot_rrset_rdata(nsec_rrset),
- 1),
- &array, &count) != 0) {
- err_handler_handle_error(handler,
- NULL,
- ZC_ERR_ALLOC);
- return ZC_ERR_ALLOC; /* ... */
- /*return; */
- }
-
- uint16_t type = 0;
- for (int j = 0; j < count; j++) {
- /* test for each type's presence */
- type = array[j];
- if (type == KNOT_RRTYPE_RRSIG) {
- continue;
- }
- if (knot_node_rrset(node,
- type) == NULL) {
- err_handler_handle_error(
- handler,
- node,
- ZC_ERR_NSEC_RDATA_BITMAP);
- /* CLEANUP */
- /* char *name =
- knot_dname_to_str(
- knot_node_owner(node));
-
- log_zone_error("Node %s does "
- "not contain RRSet of type %s "
- "but NSEC bitmap says "
- "it does!\n", name,
- knot_rrtype_to_string(type));
-
- free(name); */
- }
- }
- free(array);
- }
-
- /* Test that only one record is in the
- * NSEC RRSet */
-
- if ((nsec_rrset != NULL) &&
- knot_rrset_rdata(nsec_rrset)->next !=
- knot_rrset_rdata(nsec_rrset)) {
- err_handler_handle_error(handler,
- node,
- ZC_ERR_NSEC_RDATA_MULTIPLE);
- /* CLEANUP */
-/* char *name =
- knot_dname_to_str(
- knot_node_owner(node));
- log_zone_error("Node %s contains more "
- "than one NSEC "
- "record!\n", name);
- knot_rrset_dump(nsec_rrset, 0);
- free(name); */
- }
-
- /*
- * Test that NSEC chain is coherent.
- * We have already checked that every
- * authoritative node contains NSEC record
- * so checking should only be matter of testing
- * the next link in each node.
- */
-
- if (nsec_rrset != NULL) {
- knot_dname_t *next_domain =
- knot_rdata_item(
- knot_rrset_rdata(nsec_rrset),
- 0)->dname;
-
- assert(next_domain);
-
- if (knot_zone_contents_find_node(zone, next_domain) ==
- NULL) {
- err_handler_handle_error(handler,
- node,
- ZC_ERR_NSEC_RDATA_CHAIN);
- /* CLEANUP */
-/* log_zone_error("NSEC chain is not "
- "coherent!\n"); */
- }
-
- if (knot_dname_compare(next_domain,
- knot_node_owner(knot_zone_contents_apex(zone)))
- == 0) {
- /* saving the last node */
- *last_node = node;
- }
-
- }
- } else if (nsec3 && (auth || deleg)) { /* nsec3 */
- int ret = check_nsec3_node_in_zone(zone, node,
- handler);
- if (ret != KNOT_EOK) {
- free(rrsets);
- return ret;
- }
- }
- }
- free(rrsets);
-
- return KNOT_EOK;
-}
-
-/*!
- * \brief Function called by zone traversal function. Used to call
- * knot_zone_save_enclosers.
- *
- * \param node Node to be searched.
- * \param data Arguments.
- */
-static void do_checks_in_tree(knot_node_t *node, void *data)
-{
- assert(data != NULL);
- arg_t *args = (arg_t *)data;
-
- knot_rrset_t **rrsets = knot_node_get_rrsets(node);
- short count = knot_node_rrset_count(node);
-
- assert(count == 0 || rrsets != NULL);
-
- knot_zone_contents_t *zone = (knot_zone_contents_t *)args->arg1;
-
- assert(zone);
-
-
-/* for (int i = 0; i < count; ++i) {
- assert(rrsets[i] != NULL);
- knot_zone_save_enclosers_rrset(rrsets[i],
- zone,
- (skip_list_t *)args->arg2);
- } */
-
- knot_node_t *first_node = (knot_node_t *)args->arg4;
- knot_node_t **last_node = (knot_node_t **)args->arg5;
-
- err_handler_t *handler = (err_handler_t *)args->arg6;
-
- char do_checks = *((char *)(args->arg3));
-
- if (do_checks) {
- semantic_checks_plain(zone, node, do_checks, handler);
- }
-
- if (do_checks > 1) {
- semantic_checks_dnssec(zone, node, first_node, last_node,
- handler, do_checks == 3);
- }
-
- free(rrsets);
-}
-
-/*!
- * \brief Helper function - wraps its arguments into arg_t structure and
- * calls function that does the actual work.
- *
- * \param zone Zone to be searched / checked
- * \param list Skip list of closests enclosers.
- * \param do_checks Level of semantic checks.
- * \param handler Semantic error handler.
- * \param last_node Last checked node, which is part of NSEC(3) chain.
- */
-void zone_do_sem_checks(knot_zone_contents_t *zone, char do_checks,
- err_handler_t *handler,
- knot_node_t **last_node)
-{
- if (!do_checks) {
- return;
- }
-
- arg_t arguments;
- arguments.arg1 = zone;
- arguments.arg3 = &do_checks;
- arguments.arg4 = NULL;
- arguments.arg5 = last_node;
- arguments.arg6 = handler;
-
- knot_zone_contents_tree_apply_inorder(zone,
- do_checks_in_tree,
- (void *)&arguments);
-}
-
static inline int fwrite_to_file_crc(const void *src,
size_t size, size_t n, FILE *f,
crc_t *crc)
@@ -1428,6 +90,7 @@ static inline int fwrite_to_stream(const void *src,
return KNOT_EOK;
} else {
free(*stream);
+ *stream = NULL;
return KNOT_ENOMEM;
}
@@ -1834,125 +497,6 @@ static int zone_is_secure(knot_zone_contents_t *zone)
}
/*!
- * \brief Checks if last node in NSEC/NSEC3 chain points to first node in the
- * chain and prints possible errors.
- *
- * \param handler Semantic error handler.
- * \param zone Current zone.
- * \param last_node Last node in NSEC/NSEC3 chain.
- * \param do_checks Level of semantic checks.
- */
-static void log_cyclic_errors_in_zone(err_handler_t *handler,
- knot_zone_contents_t *zone,
- knot_node_t *last_node,
- const knot_node_t *first_nsec3_node,
- const knot_node_t *last_nsec3_node,
- char do_checks)
-{
- if (do_checks == 3) {
- /* Each NSEC3 node should only contain one RRSET. */
- assert(last_nsec3_node && first_nsec3_node);
- const knot_rrset_t *nsec3_rrset =
- knot_node_rrset(last_nsec3_node,
- KNOT_RRTYPE_NSEC3);
- if (nsec3_rrset == NULL) {
- err_handler_handle_error(handler, last_nsec3_node,
- ZC_ERR_NSEC3_RDATA_CHAIN);
- return;
- }
-
- /* check that next dname is in the zone */
- uint8_t *next_dname_decoded = NULL;
- size_t real_size = 0;
-
- if (((real_size = base32hex_encode_alloc(((char *)
- rdata_item_data(&(nsec3_rrset->rdata->items[4]))) + 1,
- rdata_item_size(&nsec3_rrset->rdata->items[4]) - 1,
- (char **)&next_dname_decoded)) <= 0) ||
- (next_dname_decoded == NULL)) {
- fprintf(stderr, "Could not encode base32 string!\n");
- err_handler_handle_error(handler, last_nsec3_node,
- ZC_ERR_NSEC3_RDATA_CHAIN);
- return;
- }
-
- /* This is why allocate maximum length of decoded string + 1 */
- memmove(next_dname_decoded + 1, next_dname_decoded, real_size);
- next_dname_decoded[0] = real_size;
-
- /* Local allocation, will be discarded. */
- knot_dname_t *next_dname =
- knot_dname_new_from_wire(next_dname_decoded,
- real_size + 1, NULL);
- if (next_dname == NULL) {
- fprintf(stderr, "Could not allocate dname!\n");
- err_handler_handle_error(handler, last_nsec3_node,
- ZC_ERR_ALLOC);
- return;
- }
-
- free(next_dname_decoded);
-
- /*! \todo Free result and dname! */
- if (knot_dname_cat(next_dname,
- knot_node_owner(knot_zone_contents_apex(zone))) ==
- NULL) {
- fprintf(stderr, "Could not concatenate dnames!\n");
- err_handler_handle_error(handler, last_nsec3_node,
- ZC_ERR_NSEC3_RDATA_CHAIN);
- return;
- }
-
- /* Check it points somewhere first. */
- if (knot_zone_contents_find_nsec3_node(zone, next_dname) == NULL) {
- err_handler_handle_error(handler, last_nsec3_node,
- ZC_ERR_NSEC3_RDATA_CHAIN);
- }
-
- /* Compare with the actual first NSEC3 node. */
- if (knot_dname_compare(first_nsec3_node->owner,
- next_dname) != 0) {
- err_handler_handle_error(handler, last_nsec3_node,
- ZC_ERR_NSEC3_RDATA_CHAIN);
- }
-
- /* Directly discard. */
- knot_dname_free(&next_dname);
-
- } else if (do_checks == 2 ) {
- if (last_node == NULL) {
- err_handler_handle_error(handler, last_node,
- ZC_ERR_NSEC_RDATA_CHAIN_NOT_CYCLIC);
- return;
- } else {
- const knot_rrset_t *nsec_rrset =
- knot_node_rrset(last_node,
- KNOT_RRTYPE_NSEC);
-
- if (nsec_rrset == NULL) {
- err_handler_handle_error(handler, last_node,
- ZC_ERR_NSEC_RDATA_CHAIN_NOT_CYCLIC);
- return;
- }
-
- const knot_dname_t *next_dname =
- knot_rdata_item(
- knot_rrset_rdata(nsec_rrset), 0)->dname;
- assert(next_dname);
-
- const knot_dname_t *apex_dname =
- knot_node_owner(knot_zone_contents_apex(zone));
- assert(apex_dname);
-
- if (knot_dname_compare(next_dname, apex_dname) !=0) {
- err_handler_handle_error(handler, last_node,
- ZC_ERR_NSEC_RDATA_CHAIN_NOT_CYCLIC);
- }
- }
- }
-}
-
-/*!
* \brief Safe wrapper around fwrite.
*
* \param dst Destination pointer.
diff --git a/src/libknot/nameserver/name-server.c b/src/libknot/nameserver/name-server.c
index f88f802..f353ee7 100644
--- a/src/libknot/nameserver/name-server.c
+++ b/src/libknot/nameserver/name-server.c
@@ -619,17 +619,35 @@ static void ns_put_authority_ns(const knot_zone_contents_t *zone,
* \param zone Zone to take the SOA RRSet from.
* \param resp Response where to add the RRSet.
*/
-static void ns_put_authority_soa(const knot_zone_contents_t *zone,
+static int ns_put_authority_soa(const knot_zone_contents_t *zone,
knot_packet_t *resp)
{
const knot_rrset_t *soa_rrset = knot_node_rrset(
knot_zone_contents_apex(zone), KNOT_RRTYPE_SOA);
assert(soa_rrset != NULL);
- knot_response_add_rrset_authority(resp, soa_rrset, 0, 0, 0);
- ns_add_rrsigs(soa_rrset, resp,
- knot_node_owner(knot_zone_contents_apex(zone)),
- knot_response_add_rrset_authority, 1);
+ // if SOA's TTL is larger than MINIMUM, copy the RRSet and set
+ // MINIMUM as TTL
+ uint32_t min = knot_rdata_soa_minimum(knot_rrset_rdata(soa_rrset));
+ if (min < knot_rrset_ttl(soa_rrset)) {
+ knot_rrset_t *soa_copy = NULL;
+ knot_rrset_deep_copy(soa_rrset, &soa_copy);
+ CHECK_ALLOC_LOG(soa_copy, KNOT_ENOMEM);
+
+ knot_rrset_set_ttl(soa_copy, min);
+ soa_rrset = soa_copy;
+ }
+
+ assert(soa_rrset != NULL);
+
+ int ret = knot_response_add_rrset_authority(resp, soa_rrset, 0, 0, 0);
+ if (ret == KNOT_EOK) {
+ ret = ns_add_rrsigs(soa_rrset, resp,
+ knot_node_owner(knot_zone_contents_apex(zone)),
+ knot_response_add_rrset_authority, 1);
+ }
+
+ return ret;
}
/*----------------------------------------------------------------------------*/
@@ -1637,13 +1655,15 @@ static void ns_add_dnskey(const knot_node_t *apex, knot_packet_t *resp)
* \todo Describe the answering logic in detail.
*/
static int ns_answer_from_zone(const knot_zone_contents_t *zone,
- const knot_dname_t *qname, uint16_t qtype,
knot_packet_t *resp)
{
const knot_node_t *node = NULL, *closest_encloser = NULL,
*previous = NULL;
int cname = 0, auth_soa = 0, ret = 0, find_ret = 0;
+ const knot_dname_t *qname = knot_packet_qname(resp);
+ uint16_t qtype = knot_packet_qtype(resp);
+
search:
#ifdef USE_HASH_TABLE
/*! \todo Check version. */
@@ -1839,20 +1859,20 @@ finalize:
* \retval KNOT_EOK
* \retval NS_ERR_SERVFAIL
*/
-static int ns_answer(knot_zonedb_t *db, knot_packet_t *resp)
-{
- const knot_dname_t *qname = knot_packet_qname(resp);
- assert(qname != NULL);
-
- uint16_t qtype = knot_packet_qtype(resp);
-dbg_ns_exec(
- char *name_str = knot_dname_to_str(qname);
- dbg_ns("Trying to find zone for QNAME %s\n", name_str);
- free(name_str);
-);
- // find zone in which to search for the name
- const knot_zone_t *zone =
- ns_get_zone_for_qname(db, qname, qtype);
+static int ns_answer(const knot_zone_t *zone, knot_packet_t *resp)
+{
+// const knot_dname_t *qname = knot_packet_qname(resp);
+// assert(qname != NULL);
+
+// uint16_t qtype = knot_packet_qtype(resp);
+//dbg_ns_exec(
+// char *name_str = knot_dname_to_str(qname);
+// dbg_ns("Trying to find zone for QNAME %s\n", name_str);
+// free(name_str);
+//);
+// // find zone in which to search for the name
+// const knot_zone_t *zone =
+// ns_get_zone_for_qname(db, qname, qtype);
const knot_zone_contents_t *contents = knot_zone_contents(zone);
// if no zone found, return REFUSED
@@ -1875,25 +1895,15 @@ dbg_ns_exec(
// take the zone contents and use only them for answering
- return ns_answer_from_zone(contents, qname, qtype, resp);
+ return ns_answer_from_zone(contents, resp);
//knot_dname_free(&qname);
}
/*----------------------------------------------------------------------------*/
-/*!
- * \brief Converts the response to wire format.
- *
- * \param resp Response to convert.
- * \param wire Place for the wire format of the response.
- * \param wire_size In: space available for the wire format in bytes.
- * Out: actual size of the wire format in bytes.
- *
- * \retval KNOT_EOK
- * \retval NS_ERR_SERVFAIL
- */
-static int ns_response_to_wire(knot_packet_t *resp, uint8_t *wire,
- size_t *wire_size)
+
+int ns_response_to_wire(knot_packet_t *resp, uint8_t *wire,
+ size_t *wire_size)
{
uint8_t *rwire = NULL;
size_t rsize = 0;
@@ -2001,11 +2011,6 @@ static int ns_xfr_send_and_clear(knot_ns_xfr_t *xfr, int add_tsig)
size_t real_size = xfr->wire_size;
if (ns_response_to_wire(xfr->response, xfr->wire, &real_size) != 0) {
return NS_ERR_SERVFAIL;
-// // send back SERVFAIL (as this is our problem)
-// ns_error_response(nameserver,
-// knot_wire_get_id(query_wire),
-// KNOT_RCODE_SERVFAIL, response_wire,
-// rsize);
}
int res = 0;
@@ -2013,6 +2018,17 @@ static int ns_xfr_send_and_clear(knot_ns_xfr_t *xfr, int add_tsig)
size_t digest_real_size = xfr->digest_max_size;
dbg_ns_detail("xfr->tsig_key=%p\n", xfr->tsig_key);
+ dbg_ns_detail("xfr->tsig_rcode=%d\n", xfr->tsig_rcode);
+
+ if (xfr->tsig_key) {
+ // add the data to TSIG data
+ assert(KNOT_NS_TSIG_DATA_MAX_SIZE - xfr->tsig_data_size
+ >= xfr->wire_size);
+ memcpy(xfr->tsig_data + xfr->tsig_data_size,
+ xfr->wire, real_size);
+ xfr->tsig_data_size += real_size;
+ }
+
/*! \note [TSIG] Generate TSIG if required (during XFR/IN). */
if (xfr->tsig_key && add_tsig) {
if (xfr->packet_nr == 0) {
@@ -2026,7 +2042,8 @@ static int ns_xfr_send_and_clear(knot_ns_xfr_t *xfr, int add_tsig)
xfr->wire_size, xfr->digest,
xfr->digest_size, xfr->digest,
&digest_real_size,
- xfr->tsig_key);
+ xfr->tsig_key, xfr->tsig_rcode,
+ xfr->tsig_prev_time_signed);
} else {
/* Add key, digest and digest length. */
dbg_ns_detail("Calling tsig_sign_next()\n");
@@ -2036,7 +2053,8 @@ static int ns_xfr_send_and_clear(knot_ns_xfr_t *xfr, int add_tsig)
xfr->digest_size,
xfr->digest,
&digest_real_size,
- xfr->tsig_key);
+ xfr->tsig_key, xfr->tsig_data,
+ xfr->tsig_data_size);
}
dbg_ns_detail("Sign function returned: %s\n",
@@ -2050,6 +2068,27 @@ static int ns_xfr_send_and_clear(knot_ns_xfr_t *xfr, int add_tsig)
assert(digest_real_size > 0);
// save the new previous digest size
xfr->digest_size = digest_real_size;
+
+ // clear the TSIG data
+ xfr->tsig_data_size = 0;
+
+ } else if (xfr->tsig_rcode != 0) {
+ dbg_ns_detail("Adding TSIG without signing, TSIG RCODE: %d.\n",
+ xfr->tsig_rcode);
+ assert(xfr->tsig_rcode != KNOT_TSIG_RCODE_BADTIME);
+ // add TSIG without signing
+ assert(xfr->query != NULL);
+ assert(knot_packet_additional_rrset_count(xfr->query) > 0);
+
+ const knot_rrset_t *tsig = knot_packet_additional_rrset(
+ xfr->query,
+ knot_packet_additional_rrset_count(xfr->query) - 1);
+
+ res = knot_tsig_add(xfr->wire, &real_size, xfr->wire_size,
+ xfr->tsig_rcode, tsig);
+ if (res != KNOT_EOK) {
+ return res;
+ }
}
// Send the response
@@ -2071,7 +2110,9 @@ static int ns_xfr_send_and_clear(knot_ns_xfr_t *xfr, int add_tsig)
// increment the packet number
++xfr->packet_nr;
- if (xfr->tsig_key && add_tsig) {
+ if ((xfr->tsig_key && knot_ns_tsig_required(xfr->packet_nr))
+ || xfr->tsig_rcode != 0) {
+ /*! \todo Where is xfr->tsig_size set?? */
knot_packet_set_tsig_size(xfr->response, xfr->tsig_size);
} else {
knot_packet_set_tsig_size(xfr->response, 0);
@@ -2312,7 +2353,7 @@ static int ns_ixfr_put_rrset(knot_ns_xfr_t *xfr, const knot_rrset_t *rrset)
/*! \todo Probably rename the function. */
ns_xfr_send_and_clear(xfr, 1);
//socket_close(xfr->session); /*! \todo Remove for UDP.*/
- return KNOT_ERROR;
+ return res;
}
return KNOT_EOK;
@@ -2363,38 +2404,9 @@ static int ns_ixfr_from_zone(knot_ns_xfr_t *xfr)
assert(xfr->response != NULL);
assert(knot_packet_authority_rrset_count(xfr->query) > 0);
assert(xfr->data != NULL);
-
- /*! \todo REMOVE start */
-// const knot_rrset_t *zone_soa =
-// knot_node_rrset(knot_zone_contents_apex(
-// knot_zone_contents(xfr->zone)),
-// KNOT_RRTYPE_SOA);
-// // retrieve origin (xfr) serial and target (zone) serial
-// uint32_t zone_serial = knot_rdata_soa_serial(
-// knot_rrset_rdata(zone_soa));
-// uint32_t xfr_serial = knot_rdata_soa_serial(knot_rrset_rdata(
-// knot_packet_authority_rrset(xfr->query, 0)));
-
-// // 3) load changesets from journal
-// knot_changesets_t *chgsets = (knot_changesets_t *)
-// calloc(1, sizeof(knot_changesets_t));
-// int res = xfr_load_changesets(xfr->zone, chgsets, xfr_serial,
-// zone_serial);
-// if (res != KNOT_EOK) {
-// dbg_ns("IXFR query cannot be answered: %s.\n",
-// knot_strerror(res));
-// /*! \todo Probably send back AXFR instead. */
-// knot_response_set_rcode(xfr->response, KNOT_RCODE_SERVFAIL);
-// /*! \todo Probably rename the function. */
-// ns_axfr_send_and_clear(xfr);
-// //socket_close(xfr->session); /*! \todo Remove for UDP. */
-// return 1;
-// }
-
- /*! \todo REMOVE end */
knot_changesets_t *chgsets = (knot_changesets_t *)xfr->data;
- knot_zone_contents_t* contents = knot_zone_get_contents(xfr->zone);
+ knot_zone_contents_t *contents = knot_zone_get_contents(xfr->zone);
assert(contents);
const knot_rrset_t *zone_soa =
knot_node_rrset(knot_zone_contents_apex(contents),
@@ -2411,15 +2423,15 @@ static int ns_ixfr_from_zone(knot_ns_xfr_t *xfr)
/*! \todo Probably rename the function. */
ns_xfr_send_and_clear(xfr, 1);
// socket_close(xfr->session); /*! \todo Remove for UDP.*/
- return 1;
+ return res;
}
// 5) put the changesets into the response while they fit in
for (int i = 0; i < chgsets->count; ++i) {
res = ns_ixfr_put_changeset(xfr, &chgsets->sets[i]);
if (res != KNOT_EOK) {
- // answer is sent, socket is closed
- return KNOT_EOK;
+ // answer is sent
+ return res;
}
}
@@ -2431,7 +2443,7 @@ static int ns_ixfr_from_zone(knot_ns_xfr_t *xfr)
/*! \todo Probably rename the function. */
ns_xfr_send_and_clear(xfr, 1);
//socket_close(xfr->session); /*! \todo Remove for UDP.*/
- return 1;
+// return 1;
}
return KNOT_EOK;
@@ -2454,7 +2466,7 @@ static int ns_ixfr(knot_ns_xfr_t *xfr)
/*! \todo Probably rename the function. */
ns_xfr_send_and_clear(xfr, 1);
//socket_close(xfr->session);
- return 1;
+ return KNOT_EMALF;
}
const knot_rrset_t *soa = knot_packet_authority_rrset(xfr->query, 0);
@@ -2470,7 +2482,7 @@ static int ns_ixfr(knot_ns_xfr_t *xfr)
/*! \todo Probably rename the function. */
ns_xfr_send_and_clear(xfr, 1);
//socket_close(xfr->session); /*! \todo Remove for UDP. */
- return 1;
+ return KNOT_EMALF;
}
return ns_ixfr_from_zone(xfr);
@@ -2728,10 +2740,16 @@ void knot_ns_error_response_full(knot_nameserver_t *nameserver,
/*----------------------------------------------------------------------------*/
-int knot_ns_answer_normal(knot_nameserver_t *nameserver, knot_packet_t *query,
- uint8_t *response_wire, size_t *rsize)
+int knot_ns_prep_normal_response(knot_nameserver_t *nameserver,
+ knot_packet_t *query, knot_packet_t **resp,
+ const knot_zone_t **zone)
{
- dbg_ns("ns_answer_normal()\n");
+ dbg_ns("knot_ns_prep_normal_response()\n");
+
+ if (nameserver == NULL || query == NULL || resp == NULL
+ || zone == NULL) {
+ return KNOT_EBADARG;
+ }
// first, parse the rest of the packet
assert(knot_packet_is_query(query));
@@ -2743,12 +2761,7 @@ int knot_ns_answer_normal(knot_nameserver_t *nameserver, knot_packet_t *query,
if (ret != KNOT_EOK) {
dbg_ns("Failed to parse rest of the query: "
"%s.\n", knot_strerror(ret));
- knot_ns_error_response(nameserver, knot_packet_id(query),
- (ret == KNOT_EMALF)
- ? KNOT_RCODE_FORMERR
- : KNOT_RCODE_SERVFAIL, response_wire,
- rsize);
- return KNOT_EOK;
+ return ret;
}
/*
@@ -2764,18 +2777,15 @@ int knot_ns_answer_normal(knot_nameserver_t *nameserver, knot_packet_t *query,
|| knot_packet_nscount(query) > 0
|| knot_packet_qdcount(query) != 1) {
dbg_ns("ANCOUNT or NSCOUNT not 0 in query, reply FORMERR.\n");
- knot_ns_error_response(nameserver, knot_packet_id(query),
- KNOT_RCODE_FORMERR, response_wire,
- rsize);
- return KNOT_EOK;
+ return KNOT_EMALF;
}
size_t resp_max_size = 0;
-
- assert(*rsize >= MAX_UDP_PAYLOAD);
+
+ //assert(*rsize >= MAX_UDP_PAYLOAD);
knot_packet_dump(query);
-
+
if (knot_query_edns_supported(query)) {
if (knot_edns_get_payload(&query->opt_rr) <
knot_edns_get_payload(nameserver->opt_rr)) {
@@ -2785,28 +2795,23 @@ int knot_ns_answer_normal(knot_nameserver_t *nameserver, knot_packet_t *query,
nameserver->opt_rr);
}
}
-
+
if (resp_max_size < MAX_UDP_PAYLOAD) {
resp_max_size = MAX_UDP_PAYLOAD;
}
-
- knot_packet_t *response;
- ret = knot_ns_prepare_response(nameserver, query, &response,
+
+ ret = knot_ns_prepare_response(nameserver, query, resp,
resp_max_size);
if (ret != KNOT_EOK) {
- knot_ns_error_response(nameserver, knot_packet_id(query),
- KNOT_RCODE_SERVFAIL, response_wire,
- rsize);
- return KNOT_EOK;
+ return KNOT_ERROR;
}
- dbg_ns("Query - parsed: %zu, total wire size: %zu\n",
+ dbg_ns("Query - parsed: %zu, total wire size: %zu\n",
query->parsed, query->size);
- dbg_ns("Opt RR: version: %d, payload: %d\n",
+ dbg_ns("Opt RR: version: %d, payload: %d\n",
query->opt_rr.version, query->opt_rr.payload);
// get the answer for the query
- rcu_read_lock();
knot_zonedb_t *zonedb = rcu_dereference(nameserver->zone_db);
dbg_ns("EDNS supported in query: %d\n",
@@ -2814,74 +2819,66 @@ int knot_ns_answer_normal(knot_nameserver_t *nameserver, knot_packet_t *query,
// set the OPT RR to the response
if (knot_query_edns_supported(query)) {
- /*! \todo API. */
-// if (knot_edns_get_payload(&query->opt_rr) > MAX_UDP_PAYLOAD) {
-// ret = knot_packet_set_max_size(response,
-// knot_edns_get_payload(&query->opt_rr));
-// } else {
-// ret = knot_packet_set_max_size(response,
-// MAX_UDP_PAYLOAD);
-// }
-
-// if (ret != KNOT_EOK) {
-// dbg_ns("Failed to set max size.\n");
-// knot_ns_error_response_full(nameserver, response,
-// KNOT_RCODE_SERVFAIL,
-// response_wire, rsize);
-// return KNOT_EOK;
-// }
-
- ret = knot_response_add_opt(response, nameserver->opt_rr, 1);
+ ret = knot_response_add_opt(*resp, nameserver->opt_rr, 1);
if (ret != KNOT_EOK) {
dbg_ns("Failed to set OPT RR to the response"
- ": %s\n",knot_strerror(ret));
+ ": %s\n", knot_strerror(ret));
} else {
// copy the DO bit from the query
if (knot_query_dnssec_requested(query)) {
/*! \todo API for this. */
- knot_edns_set_do(&response->opt_rr);
+ knot_edns_set_do(&(*resp)->opt_rr);
}
}
- }/* else {
- dbg_ns("Setting max size to %u.\n", MAX_UDP_PAYLOAD);
- ret = knot_packet_set_max_size(response, MAX_UDP_PAYLOAD);
- if (ret != KNOT_EOK) {
- dbg_ns("Failed to set max size to %u\n",
- MAX_UDP_PAYLOAD);
- knot_ns_error_response_full(nameserver, response,
- KNOT_RCODE_SERVFAIL,
- response_wire, rsize);
- return KNOT_EOK;
- }
- }*/
-
- dbg_ns("Response max size: %zu\n", response->max_size);
+ }
+
+ dbg_ns("Response max size: %zu\n", (*resp)->max_size);
+
+ const knot_dname_t *qname = knot_packet_qname(*resp);
+ assert(qname != NULL);
+
+ uint16_t qtype = knot_packet_qtype(*resp);
+dbg_ns_exec(
+ char *name_str = knot_dname_to_str(qname);
+ dbg_ns("Trying to find zone for QNAME %s\n", name_str);
+ free(name_str);
+);
+ // find zone in which to search for the name
+ *zone = ns_get_zone_for_qname(zonedb, qname, qtype);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_ns_answer_normal(knot_nameserver_t *nameserver,
+ const knot_zone_t *zone, knot_packet_t *resp,
+ uint8_t *response_wire, size_t *rsize)
+{
+ dbg_ns("ns_answer_normal()\n");
+
+ int ret = ns_answer(zone, resp);
- ret = ns_answer(zonedb, response);
if (ret != 0) {
// now only one type of error (SERVFAIL), later maybe more
- knot_ns_error_response_full(nameserver, response,
+ knot_ns_error_response_full(nameserver, resp,
KNOT_RCODE_SERVFAIL,
response_wire, rsize);
} else {
dbg_ns("Created response packet.\n");
//knot_response_dump(resp);
- knot_packet_dump(response);
+ knot_packet_dump(resp);
// 4) Transform the packet into wire format
- if (ns_response_to_wire(response, response_wire, rsize) != 0) {
+ if (ns_response_to_wire(resp, response_wire, rsize) != 0) {
// send back SERVFAIL (as this is our problem)
- knot_ns_error_response_full(nameserver, response,
+ knot_ns_error_response_full(nameserver, resp,
KNOT_RCODE_SERVFAIL,
response_wire, rsize);
}
}
- rcu_read_unlock();
- knot_packet_free(&response);
-
dbg_ns("Returning response with wire size %zu\n", *rsize);
- //dbg_ns_hex((char *)response_wire, *rsize);
return KNOT_EOK;
}
@@ -3158,12 +3155,7 @@ int knot_ns_answer_ixfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr)
int ret = knot_packet_parse_rest(xfr->query);
if (ret != KNOT_EOK) {
dbg_ns("Failed to parse rest of the packet. Reply FORMERR.\n");
-// knot_ns_error_response_full(nameserver, xfr->response,
-// KNOT_RCODE_FORMERR, xfr->wire,
-// &size);
knot_ns_xfr_send_error(nameserver, xfr, KNOT_RCODE_FORMERR);
-
- //ret = xfr->send(xfr->session, &xfr->addr, xfr->wire, size);
knot_packet_free(&xfr->response);
return ret;
}
@@ -3172,11 +3164,6 @@ int knot_ns_answer_ixfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr)
if (knot_zone_contents(xfr->zone) == NULL) {
dbg_ns("Zone expired or not bootstrapped. Reply SERVFAIL.\n");
ret = knot_ns_xfr_send_error(nameserver, xfr, KNOT_RCODE_SERVFAIL);
-// knot_ns_error_response_full(nameserver, xfr->response,
-// KNOT_RCODE_SERVFAIL, xfr->wire,
-// &size);
-
-// ret = xfr->send(xfr->session, &xfr->addr, xfr->wire, size);
knot_packet_free(&xfr->response);
return ret;
}
@@ -3195,35 +3182,15 @@ int knot_ns_answer_ixfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr)
ret = ns_ixfr(xfr);
- /*! \todo Somehow distinguish when it makes sense to send the SERVFAIL
- * and when it does not. E.g. if there was problem in sending
- * packet, it will probably fail when sending the SERVFAIL also.
- */
- if (ret < 0) {
- dbg_ns("IXFR failed, sending SERVFAIL.\n");
- // now only one type of error (SERVFAIL), later maybe more
-
- /*! \todo Extract this to some function. */
-// knot_response_set_rcode(xfr->response, KNOT_RCODE_SERVFAIL);
-// uint8_t *wire = NULL;
-// ret = knot_packet_to_wire(xfr->response, &wire, &size);
-// if (ret != KNOT_EOK) {
-//// knot_ns_error_response(nameserver,
-//// xfr->query->header.id,
-//// KNOT_RCODE_SERVFAIL, xfr->wire,
-//// &size);
-//// ret = xfr->send(xfr->session, &xfr->addr, xfr->wire,
-//// size);
-// knot_ns_xfr_send_error(xfr, KNOT_RCODE_SERVFAIL);
-// knot_packet_free(&xfr->response);
-// return ret;
-// } else {
-// ret = xfr->send(xfr->session, &xfr->addr, wire, size);
-// }
- knot_ns_xfr_send_error(nameserver, xfr, KNOT_RCODE_SERVFAIL);
- } /*else if (ret > 0) {
- ret = KNOT_ERROR;
- }*/
+// /*! \todo Somehow distinguish when it makes sense to send the SERVFAIL
+// * and when it does not. E.g. if there was problem in sending
+// * packet, it will probably fail when sending the SERVFAIL also.
+// */
+// if (ret < 0) {
+// dbg_ns("IXFR failed, sending SERVFAIL.\n");
+// // now only one type of error (SERVFAIL), later maybe more
+// knot_ns_xfr_send_error(nameserver, xfr, KNOT_RCODE_SERVFAIL);
+// }
knot_packet_free(&xfr->response);
@@ -3363,16 +3330,11 @@ int knot_ns_process_ixfrin(knot_nameserver_t *nameserver,
* digest.
*/
- int ret = xfrin_process_ixfr_packet(xfr/*xfr->wire, xfr->wire_size,
- (knot_changesets_t **)(&xfr->data)*/);
+ int ret = xfrin_process_ixfr_packet(xfr);
if (ret == XFRIN_RES_FALLBACK) {
dbg_ns("ns_process_ixfrin: Fallback to AXFR.\n");
- assert(xfr->data == NULL);
-// dbg_ns("xfr->zone = %p\n", xfr->zone);
-// dbg_ns("Zone name: %.*s\n",
-// xfr->zone->name->size, xfr->zone->name->name);
-// assert(xfr->zone == NULL);
+ knot_free_changesets((knot_changesets_t **)&xfr->data);
knot_packet_free(&xfr->query);
return KNOT_ENOIXFR;
}
@@ -3417,12 +3379,24 @@ int knot_ns_process_ixfrin(knot_nameserver_t *nameserver,
return KNOT_ERROR;
}
- if (knot_rdata_soa_serial(knot_rrset_rdata(
- chgsets->first_soa))
- != knot_rdata_soa_serial(knot_rrset_rdata(
- zone_soa))) {
- dbg_ns("Update did not fit.\n");
- return KNOT_EAGAIN;
+ if (ns_serial_compare(knot_rdata_soa_serial(
+ knot_rrset_rdata(chgsets->first_soa)),
+ knot_rdata_soa_serial(knot_rrset_rdata(zone_soa)))
+ > 0) {
+ if ((xfr->flags & XFR_FLAG_UDP) > 0) {
+ // IXFR over UDP
+ dbg_ns("Update did not fit.\n");
+ return KNOT_EIXFRSPACE;
+ } else {
+ // fallback to AXFR
+ dbg_ns("ns_process_ixfrin: "
+ "Fallback to AXFR.\n");
+ knot_free_changesets(
+ (knot_changesets_t **)&xfr->data);
+ knot_packet_free(&xfr->query);
+ return KNOT_ENOIXFR;
+ }
+
} else {
// free changesets
dbg_ns("No update needed.\n");
diff --git a/src/libknot/nameserver/name-server.h b/src/libknot/nameserver/name-server.h
index 0d93df6..928d79c 100644
--- a/src/libknot/nameserver/name-server.h
+++ b/src/libknot/nameserver/name-server.h
@@ -118,6 +118,9 @@ typedef struct knot_ns_xfr {
uint8_t *digest; /*!< Buffer for counting digest. */
size_t digest_size; /*!< Size of the digest. */
size_t digest_max_size; /*!< Size of the buffer. */
+
+ uint16_t tsig_rcode;
+ uint64_t tsig_prev_time_signed;
/*! \brief Previous digest or request digest.
*
@@ -219,6 +222,14 @@ int knot_ns_parse_packet(const uint8_t *query_wire, size_t qsize,
void knot_ns_error_response(const knot_nameserver_t *nameserver, uint16_t query_id,
uint8_t rcode, uint8_t *response_wire, size_t *rsize);
+void knot_ns_error_response_full(knot_nameserver_t *nameserver,
+ knot_packet_t *response, uint8_t rcode,
+ uint8_t *response_wire, size_t *rsize);
+
+int knot_ns_prep_normal_response(knot_nameserver_t *nameserver,
+ knot_packet_t *query, knot_packet_t **resp,
+ const knot_zone_t **zone);
+
/*!
* \brief Creates a response for the given normal query using the data of the
* nameserver.
@@ -232,8 +243,9 @@ void knot_ns_error_response(const knot_nameserver_t *nameserver, uint16_t query_
* \retval KNOT_EOK if a valid response was created.
* \retval KNOT_EMALF if an error occured and the response is not valid.
*/
-int knot_ns_answer_normal(knot_nameserver_t *nameserver, knot_packet_t *query,
- uint8_t *response_wire, size_t *rsize);
+int knot_ns_answer_normal(knot_nameserver_t *nameserver,
+ const knot_zone_t *zone, knot_packet_t *resp,
+ uint8_t *response_wire, size_t *rsize);
int knot_ns_init_xfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr);
@@ -346,6 +358,20 @@ void knot_ns_set_data(knot_nameserver_t *nameserver, void *data);
int knot_ns_tsig_required(int packet_nr);
/*!
+ * \brief Converts the response to wire format.
+ *
+ * \param resp Response to convert.
+ * \param wire Place for the wire format of the response.
+ * \param wire_size In: space available for the wire format in bytes.
+ * Out: actual size of the wire format in bytes.
+ *
+ * \retval KNOT_EOK
+ * \retval NS_ERR_SERVFAIL
+ */
+int ns_response_to_wire(knot_packet_t *resp, uint8_t *wire,
+ size_t *wire_size);
+
+/*!
* \brief Properly destroys the name server structure.
*
* \param nameserver Nameserver to destroy.
diff --git a/src/libknot/packet/packet.c b/src/libknot/packet/packet.c
index 82e818f..59c1a0d 100644
--- a/src/libknot/packet/packet.c
+++ b/src/libknot/packet/packet.c
@@ -1159,6 +1159,20 @@ void knot_packet_set_tsig_size(knot_packet_t *packet, size_t tsig_size)
/*----------------------------------------------------------------------------*/
+const knot_rrset_t *knot_packet_tsig(const knot_packet_t *packet)
+{
+ return packet->tsig_rr;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_packet_set_tsig(knot_packet_t *packet, const knot_rrset_t *tsig_rr)
+{
+ packet->tsig_rr = (knot_rrset_t *)tsig_rr;
+}
+
+/*----------------------------------------------------------------------------*/
+
short knot_packet_answer_rrset_count(const knot_packet_t *packet)
{
if (packet == NULL) {
@@ -1217,7 +1231,7 @@ const knot_rrset_t *knot_packet_authority_rrset(
/*----------------------------------------------------------------------------*/
const knot_rrset_t *knot_packet_additional_rrset(
- knot_packet_t *packet, short pos)
+ knot_packet_t *packet, short pos)
{
if (packet == NULL || pos > packet->ar_rrsets) {
return NULL;
diff --git a/src/libknot/packet/packet.h b/src/libknot/packet/packet.h
index 1bf74a9..81b1812 100644
--- a/src/libknot/packet/packet.h
+++ b/src/libknot/packet/packet.h
@@ -143,6 +143,7 @@ struct knot_packet {
knot_packet_prealloc_type_t prealloc_type;
size_t tsig_size; /*!< Space to reserve for the TSIG RR. */
+ knot_rrset_t *tsig_rr; /*!< TSIG RR stored in the packet. */
};
typedef struct knot_packet knot_packet_t;
@@ -378,6 +379,10 @@ int knot_packet_arcount(const knot_packet_t *packet);
void knot_packet_set_tsig_size(knot_packet_t *packet, size_t tsig_size);
+const knot_rrset_t *knot_packet_tsig(const knot_packet_t *packet);
+
+void knot_packet_set_tsig(knot_packet_t *packet, const knot_rrset_t *tsig_rr);
+
/*!
* \brief Returns number of RRSets in Answer section of the packet.
*
@@ -439,7 +444,7 @@ const knot_rrset_t *knot_packet_authority_rrset(
* or NULL if there is no such RRSet.
*/
const knot_rrset_t *knot_packet_additional_rrset(
- knot_packet_t *packet, short pos);
+ knot_packet_t *packet, short pos);
/*!
* \brief Checks if the packet already contains the given RRSet.
diff --git a/src/libknot/rdata.c b/src/libknot/rdata.c
index 0c51f5b..27fa6ec 100644
--- a/src/libknot/rdata.c
+++ b/src/libknot/rdata.c
@@ -828,6 +828,22 @@ uint32_t knot_rdata_soa_expire(const knot_rdata_t *rdata)
/*---------------------------------------------------------------------------*/
+uint32_t knot_rdata_soa_minimum(const knot_rdata_t *rdata)
+{
+ if (!rdata) {
+ return -1;
+ }
+
+ if (rdata->count < 7) {
+ return 0; /*! \todo Some other error value. */
+ }
+
+ // the number is in network byte order, transform it
+ return knot_wire_read_u32((uint8_t *)(rdata->items[6].raw_data + 1));
+}
+
+/*---------------------------------------------------------------------------*/
+
uint16_t knot_rdata_rrsig_type_covered(const knot_rdata_t *rdata)
{
if (rdata->count < 1) {
diff --git a/src/libknot/rdata.h b/src/libknot/rdata.h
index 5d328c9..6d9907d 100644
--- a/src/libknot/rdata.h
+++ b/src/libknot/rdata.h
@@ -331,6 +331,7 @@ int64_t knot_rdata_soa_serial(const knot_rdata_t *rdata);
uint32_t knot_rdata_soa_refresh(const knot_rdata_t *rdata);
uint32_t knot_rdata_soa_retry(const knot_rdata_t *rdata);
uint32_t knot_rdata_soa_expire(const knot_rdata_t *rdata);
+uint32_t knot_rdata_soa_minimum(const knot_rdata_t *rdata);
uint16_t knot_rdata_rrsig_type_covered(const knot_rdata_t *rdata);
diff --git a/src/libknot/rrset.c b/src/libknot/rrset.c
index 6083f77..f037716 100644
--- a/src/libknot/rrset.c
+++ b/src/libknot/rrset.c
@@ -24,6 +24,7 @@
#include "rrset.h"
#include "util/descriptor.h"
#include "util/error.h"
+#include "util/debug.h"
#include "util/utils.h"
/*----------------------------------------------------------------------------*/
@@ -211,6 +212,15 @@ void knot_rrset_set_owner(knot_rrset_t *rrset, knot_dname_t* owner)
/*----------------------------------------------------------------------------*/
+void knot_rrset_set_ttl(knot_rrset_t *rrset, uint32_t ttl)
+{
+ if (rrset) {
+ rrset->ttl = ttl;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
uint16_t knot_rrset_type(const knot_rrset_t *rrset)
{
return rrset->type;
@@ -385,7 +395,7 @@ static int knot_rrset_rr_to_wire(const knot_rrset_t *rrset,
assert(pos != NULL);
assert(*pos != NULL);
- fprintf(stderr, "Max size: %zu, owner: %p, owner size: %d\n",
+ dbg_rrset_detail("Max size: %zu, owner: %p, owner size: %d\n",
max_size, rrset->owner, rrset->owner->size);
// check if owner fits
@@ -398,21 +408,21 @@ static int knot_rrset_rr_to_wire(const knot_rrset_t *rrset,
*pos += knot_dname_size(rrset->owner);
size += knot_dname_size(rrset->owner);
- fprintf(stderr, "Max size: %zu, size: %d\n", max_size, size);
+ dbg_rrset_detail("Max size: %zu, size: %d\n", max_size, size);
- fprintf(stderr, "Wire format:\n");
+ dbg_rrset_detail("Wire format:\n");
// put rest of RR 'header'
knot_wire_write_u16(*pos, rrset->type);
- fprintf(stderr, " Type: %u\n", rrset->type);
+ dbg_rrset_detail(" Type: %u\n", rrset->type);
*pos += 2;
knot_wire_write_u16(*pos, rrset->rclass);
- fprintf(stderr, " Class: %u\n", rrset->rclass);
+ dbg_rrset_detail(" Class: %u\n", rrset->rclass);
*pos += 2;
knot_wire_write_u32(*pos, rrset->ttl);
- fprintf(stderr, " TTL: %u\n", rrset->ttl);
+ dbg_rrset_detail(" TTL: %u\n", rrset->ttl);
*pos += 4;
// save space for RDLENGTH
@@ -422,7 +432,7 @@ static int knot_rrset_rr_to_wire(const knot_rrset_t *rrset,
size += 10;
// compr->wire_pos += size;
- fprintf(stderr, "Max size: %zu, size: %d\n", max_size, size);
+ dbg_rrset_detail("Max size: %zu, size: %d\n", max_size, size);
knot_rrtype_descriptor_t *desc =
knot_rrtype_descriptor_by_type(rrset->type);
@@ -447,8 +457,9 @@ static int knot_rrset_rr_to_wire(const knot_rrset_t *rrset,
// save whole domain name
memcpy(*pos, knot_dname_name(dname),
knot_dname_size(dname));
- fprintf(stderr, "Uncompressed dname size: %d\n",
- knot_dname_size(dname));
+ dbg_rrset_detail(stderr,
+ "Uncompressed dname size: %d\n",
+ knot_dname_size(dname));
*pos += knot_dname_size(dname);
rdlength += knot_dname_size(dname);
// compr->wire_pos += dname->size;
@@ -464,7 +475,7 @@ static int knot_rrset_rr_to_wire(const knot_rrset_t *rrset,
// copy just the rdata item data (without size)
memcpy(*pos, raw_data + 1, raw_data[0]);
- fprintf(stderr, "Raw data size: %d\n", raw_data[0]);
+ dbg_rrset_detail("Raw data size: %d\n", raw_data[0]);
*pos += raw_data[0];
rdlength += raw_data[0];
// compr->wire_pos += raw_data[0];
@@ -473,7 +484,7 @@ static int knot_rrset_rr_to_wire(const knot_rrset_t *rrset,
}
}
- fprintf(stderr, "Max size: %zu, size: %d\n", max_size, size);
+ dbg_rrset_detail("Max size: %zu, size: %d\n", max_size, size);
assert(size + rdlength <= max_size);
size += rdlength;
@@ -509,11 +520,11 @@ int knot_rrset_to_wire(const knot_rrset_t *rrset, uint8_t *wire, size_t *size,
if (ret < 0) {
// some RR didn't fit in, so no RRs should be used
// TODO: remove last entries from compression table
- fprintf(stderr, "Some RR didn't fit in.\n");
+ dbg_rrset_detail("Some RR didn't fit in.\n");
return KNOT_ESPACE;
}
- fprintf(stderr, "RR of size %d added.\n", ret);
+ dbg_rrset_detail("RR of size %d added.\n", ret);
rrset_size += ret;
++rrs;
} while ((rdata = knot_rrset_rdata_next(rrset, rdata)) != NULL);
@@ -523,7 +534,7 @@ int knot_rrset_to_wire(const knot_rrset_t *rrset, uint8_t *wire, size_t *size,
assert(pos - wire == rrset_size);
*size = rrset_size;
- fprintf(stderr, " Size after: %zu\n", *size);
+ dbg_rrset_detail("Size after: %zu\n", *size);
*rr_count = rrs;
diff --git a/src/libknot/rrset.h b/src/libknot/rrset.h
index 7754c7f..e13cbba 100644
--- a/src/libknot/rrset.h
+++ b/src/libknot/rrset.h
@@ -146,6 +146,8 @@ knot_dname_t *knot_rrset_get_owner(const knot_rrset_t *rrset);
*/
void knot_rrset_set_owner(knot_rrset_t *rrset, knot_dname_t* owner);
+void knot_rrset_set_ttl(knot_rrset_t *rrset, uint32_t ttl);
+
/*!
* \brief Returns the TYPE of the RRSet.
*
diff --git a/src/libknot/tsig-op.c b/src/libknot/tsig-op.c
index 3178a23..edc68d8 100644
--- a/src/libknot/tsig-op.c
+++ b/src/libknot/tsig-op.c
@@ -25,305 +25,10 @@
#include "tsig.h"
#include "tsig-op.h"
#include "util/wire.h"
+#include "libknot/util/conv.h"
#include "util/error.h"
#include "util/debug.h"
-
-
-static const char Base64[] =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-static const char Pad64 = '=';
-
-
-static int b64rmap_initialized = 0;
-static uint8_t b64rmap[256];
-
-static const uint8_t b64rmap_special = 0xf0;
-static const uint8_t b64rmap_end = 0xfd;
-static const uint8_t b64rmap_space = 0xfe;
-static const uint8_t b64rmap_invalid = 0xff;
-
-/**
- * Initializing the reverse map is not thread safe.
- * Which is fine for NSD. For now...
- **/
-void b64_initialize_rmap()
-{
- int i;
- char ch;
-
- /* Null: end of string, stop parsing */
- b64rmap[0] = b64rmap_end;
-
- for (i = 1; i < 256; ++i) {
- ch = (char)i;
- /* Whitespaces */
- if (isspace(ch)) {
- b64rmap[i] = b64rmap_space;
- }
- /* Padding: stop parsing */
- else if (ch == Pad64) {
- b64rmap[i] = b64rmap_end;
- }
- /* Non-base64 char */
- else {
- b64rmap[i] = b64rmap_invalid;
- }
- }
-
- /* Fill reverse mapping for base64 chars */
- for (i = 0; Base64[i] != '\0'; ++i) {
- b64rmap[(uint8_t)Base64[i]] = i;
- }
-
- b64rmap_initialized = 1;
-}
-
-int b64_pton_do(char const *src, uint8_t *target, size_t targsize)
-{
- int tarindex, state, ch;
- uint8_t ofs;
-
- state = 0;
- tarindex = 0;
-
- while (1) {
- ch = *src++;
- ofs = b64rmap[ch];
-
- if (ofs >= b64rmap_special) {
- /* Ignore whitespaces */
- if (ofs == b64rmap_space) {
- continue;
- }
- /* End of base64 characters */
- if (ofs == b64rmap_end) {
- break;
- }
- /* A non-base64 character. */
- return (-1);
- }
-
- switch (state) {
- case 0:
- if ((size_t)tarindex >= targsize) {
- return (-1);
- }
- target[tarindex] = ofs << 2;
- state = 1;
- break;
- case 1:
- if ((size_t)tarindex + 1 >= targsize) {
- return (-1);
- }
- target[tarindex] |= ofs >> 4;
- target[tarindex+1] = (ofs & 0x0f)
- << 4 ;
- tarindex++;
- state = 2;
- break;
- case 2:
- if ((size_t)tarindex + 1 >= targsize) {
- return (-1);
- }
- target[tarindex] |= ofs >> 2;
- target[tarindex+1] = (ofs & 0x03)
- << 6;
- tarindex++;
- state = 3;
- break;
- case 3:
- if ((size_t)tarindex >= targsize) {
- return (-1);
- }
- target[tarindex] |= ofs;
- tarindex++;
- state = 0;
- break;
- default:
- abort();
- }
- }
-
- /*
- * We are done decoding Base-64 chars. Let's see if we ended
- * on a byte boundary, and/or with erroneous trailing characters.
- */
-
- if (ch == Pad64) { /* We got a pad char. */
- ch = *src++; /* Skip it, get next. */
- switch (state) {
- case 0: /* Invalid = in first position */
- case 1: /* Invalid = in second position */
- return (-1);
-
- case 2: /* Valid, means one byte of info */
- /* Skip any number of spaces. */
- for ((void)NULL; ch != '\0'; ch = *src++)
- if (b64rmap[ch] != b64rmap_space) {
- break;
- }
- /* Make sure there is another trailing = sign. */
- if (ch != Pad64) {
- return (-1);
- }
- ch = *src++; /* Skip the = */
- /* Fall through to "single trailing =" case. */
- /* FALLTHROUGH */
-
- case 3: /* Valid, means two bytes of info */
- /*
- * We know this char is an =. Is there anything but
- * whitespace after it?
- */
- for ((void)NULL; ch != '\0'; ch = *src++)
- if (b64rmap[ch] != b64rmap_space) {
- return (-1);
- }
-
- /*
- * Now make sure for cases 2 and 3 that the "extra"
- * bits that slopped past the last full byte were
- * zeros. If we don't check them, they become a
- * subliminal channel.
- */
- if (target[tarindex] != 0) {
- return (-1);
- }
- }
- } else {
- /*
- * We ended by seeing the end of the string. Make sure we
- * have no partial bytes lying around.
- */
- if (state != 0) {
- return (-1);
- }
- }
-
- return (tarindex);
-}
-
-
-int b64_pton_len(char const *src)
-{
- int tarindex, state, ch;
- uint8_t ofs;
-
- state = 0;
- tarindex = 0;
-
- while (1) {
- ch = *src++;
- ofs = b64rmap[ch];
-
- if (ofs >= b64rmap_special) {
- /* Ignore whitespaces */
- if (ofs == b64rmap_space) {
- continue;
- }
- /* End of base64 characters */
- if (ofs == b64rmap_end) {
- break;
- }
- /* A non-base64 character. */
- return (-1);
- }
-
- switch (state) {
- case 0:
- state = 1;
- break;
- case 1:
- tarindex++;
- state = 2;
- break;
- case 2:
- tarindex++;
- state = 3;
- break;
- case 3:
- tarindex++;
- state = 0;
- break;
- default:
- abort();
- }
- }
-
- /*
- * We are done decoding Base-64 chars. Let's see if we ended
- * on a byte boundary, and/or with erroneous trailing characters.
- */
-
- if (ch == Pad64) { /* We got a pad char. */
- ch = *src++; /* Skip it, get next. */
- switch (state) {
- case 0: /* Invalid = in first position */
- case 1: /* Invalid = in second position */
- return (-1);
-
- case 2: /* Valid, means one byte of info */
- /* Skip any number of spaces. */
- for ((void)NULL; ch != '\0'; ch = *src++)
- if (b64rmap[ch] != b64rmap_space) {
- break;
- }
- /* Make sure there is another trailing = sign. */
- if (ch != Pad64) {
- return (-1);
- }
- ch = *src++; /* Skip the = */
- /* Fall through to "single trailing =" case. */
- /* FALLTHROUGH */
-
- case 3: /* Valid, means two bytes of info */
- /*
- * We know this char is an =. Is there anything but
- * whitespace after it?
- */
- for ((void)NULL; ch != '\0'; ch = *src++)
- if (b64rmap[ch] != b64rmap_space) {
- return (-1);
- }
-
- }
- } else {
- /*
- * We ended by seeing the end of the string. Make sure we
- * have no partial bytes lying around.
- */
- if (state != 0) {
- return (-1);
- }
- }
-
- return (tarindex);
-}
-
-int b64_pton(char const *src, uint8_t *target, size_t targsize)
-{
- if (!b64rmap_initialized) {
- b64_initialize_rmap();
- }
-
- if (target) {
- return b64_pton_do(src, target, targsize);
- } else {
- return b64_pton_len(src);
- }
-}
-
-#define B64BUFSIZE 65535 /*!< Buffer size for b64 conversion. */
-
-
-
-
-
-
-
-
-
-
+#include "consts.h"
const int KNOT_TSIG_MAX_DIGEST_SIZE = 64; // size of HMAC-SHA512 digest
@@ -354,7 +59,7 @@ static int knot_tsig_check_key(const knot_rrset_t *tsig_rr,
return KNOT_EMALF;
}
- const char *name = knot_dname_to_str(tsig_name);
+ char *name = knot_dname_to_str(tsig_name);
if (!name) {
return KNOT_EMALF;
}
@@ -362,9 +67,11 @@ static int knot_tsig_check_key(const knot_rrset_t *tsig_rr,
if (knot_dname_compare(tsig_name, tsig_key->name) != 0) {
/*!< \todo which error. */
dbg_tsig("TSIG: unknown key: %s\n", name);
+ free(name);
return KNOT_TSIG_EBADKEY;
}
+ free(name);
return KNOT_EOK;
}
@@ -402,15 +109,15 @@ static int knot_tsig_compute_digest(const uint8_t *wire, size_t wire_len,
B64BUFSIZE);
if (decoded_key_size < 0) {
dbg_tsig("TSIG: Could not decode Base64\n");
- return KNOT_EMALF;
+ return KNOT_ERROR;
}
- dbg_tsig("TSIG: decoded key size: %d\n", decoded_key_size);
- dbg_tsig("TSIG: decoded key: '%*s'\n", decoded_key_size, decoded_key);
-
- dbg_tsig("TSIG: using this wire for digest calculation\n");
+ dbg_tsig_detail("TSIG: decoded key size: %d\n", decoded_key_size);
+ dbg_tsig_detail("TSIG: decoded key: '%*s'\n", decoded_key_size, decoded_key);
- //dbg_tsig_hex(wire, wire_len);
+// dbg_tsig_detail("TSIG: using this wire for digest calculation\n");
+// dbg_tsig_hex_detail(wire, wire_len);
+ dbg_tsig_detail("Wire for signing is %zu bytes long.\n", wire_len);
/* Compute digest. */
HMAC_CTX ctx;
@@ -420,6 +127,14 @@ static int knot_tsig_compute_digest(const uint8_t *wire, size_t wire_len,
HMAC_Init(&ctx, decoded_key,
decoded_key_size, EVP_md5());
break;
+ case KNOT_TSIG_ALG_HMAC_SHA1:
+ HMAC_Init(&ctx, decoded_key,
+ decoded_key_size, EVP_sha1());
+ break;
+ case KNOT_TSIG_ALG_HMAC_SHA256:
+ HMAC_Init(&ctx, decoded_key,
+ decoded_key_size, EVP_sha256());
+ break;
default:
return KNOT_ENOTSUP;
} /* switch */
@@ -428,11 +143,14 @@ static int knot_tsig_compute_digest(const uint8_t *wire, size_t wire_len,
HMAC_Update(&ctx, (const unsigned char *)wire, wire_len);
HMAC_Final(&ctx, digest, &tmp_dig_len);
*digest_len = tmp_dig_len;
+
+ HMAC_CTX_cleanup(&ctx);
return KNOT_EOK;
}
-static int knot_tsig_check_time_signed(const knot_rrset_t *tsig_rr)
+static int knot_tsig_check_time_signed(const knot_rrset_t *tsig_rr,
+ uint64_t prev_time_signed)
{
if (!tsig_rr) {
return KNOT_EBADARG;
@@ -452,7 +170,15 @@ static int knot_tsig_check_time_signed(const knot_rrset_t *tsig_rr)
time_t curr_time = time(NULL);
/*!< \todo bleeding eyes. */
- if (difftime(curr_time, (time_t)time_signed) > fudge) {
+ double diff = difftime(curr_time, (time_t)time_signed);
+
+ if (diff > fudge || diff < -fudge) {
+ return KNOT_TSIG_EBADTIME;
+ }
+
+ diff = difftime((time_t)time_signed, prev_time_signed);
+
+ if (diff < 0) {
return KNOT_TSIG_EBADTIME;
}
@@ -569,7 +295,7 @@ static int knot_tsig_wire_write_timers(uint8_t *wire,
return KNOT_EOK;
}
-int knot_tsig_create_sign_wire(const uint8_t *msg, size_t msg_len,
+static int knot_tsig_create_sign_wire(const uint8_t *msg, size_t msg_len,
/*size_t msg_max_len, */const uint8_t *request_mac,
size_t request_mac_len,
uint8_t *digest, size_t *digest_len,
@@ -635,6 +361,7 @@ int knot_tsig_create_sign_wire(const uint8_t *msg, size_t msg_len,
if (ret != KNOT_EOK) {
dbg_tsig("TSIG: create wire: failed to write TSIG "
"variables: %s\n", knot_strerror(ret));
+ free(wire);
return ret;
}
@@ -645,6 +372,7 @@ int knot_tsig_create_sign_wire(const uint8_t *msg, size_t msg_len,
dbg_tsig("TSIG: create wire: failed to compute digest: %s\n",
knot_strerror(ret));
*digest_len = 0;
+ free(wire);
return ret;
}
@@ -666,11 +394,11 @@ int knot_tsig_create_sign_wire(const uint8_t *msg, size_t msg_len,
}
static int knot_tsig_create_sign_wire_next(const uint8_t *msg, size_t msg_len,
- const uint8_t *prev_mac,
- size_t prev_mac_len,
- uint8_t *digest, size_t *digest_len,
- const knot_rrset_t *tmp_tsig,
- const knot_key_t *key)
+ const uint8_t *prev_mac,
+ size_t prev_mac_len,
+ uint8_t *digest, size_t *digest_len,
+ const knot_rrset_t *tmp_tsig,
+ const knot_key_t *key)
{
if (!msg || !key || digest_len == NULL) {
dbg_tsig("TSIG: create wire: bad args.\n");
@@ -689,7 +417,7 @@ static int knot_tsig_create_sign_wire_next(const uint8_t *msg, size_t msg_len,
tsig_rdata_tsig_timers_length());
size_t wire_len = sizeof(uint8_t) *
(msg_len + prev_mac_len +
- tsig_rdata_tsig_timers_length());
+ tsig_rdata_tsig_timers_length() + 2);
uint8_t *wire = malloc(wire_len);
if (!wire) {
ERR_ALLOC_FAILED;
@@ -699,19 +427,21 @@ static int knot_tsig_create_sign_wire_next(const uint8_t *msg, size_t msg_len,
memset(wire, 0, wire_len);
/* Copy the request MAC - should work even if NULL. */
+ dbg_tsig("Copying request mac size.\n");
+ knot_wire_write_u16(wire, prev_mac_len);
dbg_tsig("Copying request mac.\n");
- memcpy(wire, prev_mac, sizeof(uint8_t) * prev_mac_len);
+ memcpy(wire + 2, prev_mac, sizeof(uint8_t) * prev_mac_len);
dbg_tsig_detail("TSIG: create wire: request mac: ");
- dbg_tsig_hex_detail(wire, prev_mac_len);
+ dbg_tsig_hex_detail(wire + 2, prev_mac_len);
/* Copy the original message. */
dbg_tsig("Copying original message.\n");
- memcpy(wire + prev_mac_len, msg, msg_len);
+ memcpy(wire + prev_mac_len + 2, msg, msg_len);
dbg_tsig_detail("TSIG: create wire: original message: \n");
//dbg_tsig_hex_detail(wire + prev_mac_len, msg_len);
/* Copy TSIG variables. */
dbg_tsig("Writing TSIG timers.\n");
- ret = knot_tsig_write_tsig_timers(wire + prev_mac_len + msg_len,
+ ret = knot_tsig_write_tsig_timers(wire + prev_mac_len + msg_len + 2,
tmp_tsig);
// ret = knot_tsig_write_tsig_variables(wire + prev_mac_len + msg_len,
// tmp_tsig);
@@ -740,7 +470,8 @@ int knot_tsig_sign(uint8_t *msg, size_t *msg_len,
size_t msg_max_len, const uint8_t *request_mac,
size_t request_mac_len,
uint8_t *digest, size_t *digest_len,
- const knot_key_t *key)
+ const knot_key_t *key, uint16_t tsig_rcode,
+ uint64_t request_time_signed)
{
if (!msg || !msg_len || !key || digest == NULL || digest_len == NULL) {
return KNOT_EBADARG;
@@ -755,6 +486,8 @@ int knot_tsig_sign(uint8_t *msg, size_t *msg_len,
knot_rrset_t *tmp_tsig =
knot_rrset_new(key_name_copy,
KNOT_RRTYPE_TSIG, KNOT_CLASS_ANY, 0);
+ /* Should be retained by rrsig or freed, release. */
+ knot_dname_release(key_name_copy);
if (!tmp_tsig) {
dbg_tsig_detail("TSIG: tmp_tsig = NULL\n");
return KNOT_ENOMEM;
@@ -764,6 +497,7 @@ int knot_tsig_sign(uint8_t *msg, size_t *msg_len,
knot_rdata_t *rdata = knot_rdata_new();
if (!rdata) {
dbg_tsig_detail("TSIG: rdata = NULL\n");
+ knot_rrset_free(&tmp_tsig);
return KNOT_ENOMEM;
}
@@ -779,6 +513,8 @@ int knot_tsig_sign(uint8_t *msg, size_t *msg_len,
if (!items) {
dbg_tsig_detail("TSIG: items = NULL\n");
ERR_ALLOC_FAILED;
+ knot_rrset_free(&tmp_tsig);
+ knot_rdata_free(&rdata);
return KNOT_ENOMEM;
}
@@ -787,23 +523,42 @@ int knot_tsig_sign(uint8_t *msg, size_t *msg_len,
int ret = knot_rdata_set_items(rdata, items, desc->length);
if (ret != KNOT_EOK) {
dbg_tsig_detail("TSIG: rdata_set_items returned %s\n", knot_strerror(ret));
+ free(items);
+ knot_rrset_free(&tmp_tsig);
+ knot_rdata_free(&rdata);
return ret;
}
free(items);
tsig_rdata_set_alg(tmp_tsig, key->algorithm);
- tsig_rdata_store_current_time(tmp_tsig);
- tsig_rdata_set_fudge(tmp_tsig, 300);
- /* Set original ID */
- tsig_rdata_set_orig_id(tmp_tsig, knot_wire_get_id(msg));
+ /* Distinguish BADTIME response. */
+ if (tsig_rcode == KNOT_TSIG_RCODE_BADTIME) {
+ /* Set error */
+ tsig_rdata_set_tsig_error(tmp_tsig, tsig_rcode);
+ /* Set client's time signed into the time signed field. */
+ tsig_rdata_set_time_signed(tmp_tsig, request_time_signed);
- /* Set error */
- /*! \todo [TSIG] Set error and other data if appropriate. */
- tsig_rdata_set_tsig_error(tmp_tsig, 0);
+ /* Store current time into Other data. */
+ uint8_t time_signed[3];
+ time_t curr_time = time(NULL);
- /* Set other len. */
- tsig_rdata_set_other_data(tmp_tsig, 0, 0);
+ /*! \todo bleeding eyes. */
+ knot_wire_write_u48(time_signed, (uint64_t)curr_time);
+
+ tsig_rdata_set_other_data(tmp_tsig, 6, time_signed);
+ } else {
+ tsig_rdata_store_current_time(tmp_tsig);
+ tsig_rdata_set_tsig_error(tmp_tsig, 0);
+
+ /* Set other len. */
+ tsig_rdata_set_other_data(tmp_tsig, 0, 0);
+ }
+
+ tsig_rdata_set_fudge(tmp_tsig, 300); /*! \todo Bleeding eyes :-) */
+
+ /* Set original ID */
+ tsig_rdata_set_orig_id(tmp_tsig, knot_wire_get_id(msg));
uint8_t digest_tmp[KNOT_TSIG_MAX_DIGEST_SIZE];
size_t digest_tmp_len = 0;
@@ -818,6 +573,10 @@ int knot_tsig_sign(uint8_t *msg, size_t *msg_len,
if (ret != KNOT_EOK) {
dbg_tsig("TSIG: could not create wire or sign wire: %s\n",
knot_strerror(ret));
+ free(items);
+ knot_rrset_free(&tmp_tsig);
+ knot_rdata_free(&rdata);
+
return ret;
}
@@ -834,6 +593,9 @@ int knot_tsig_sign(uint8_t *msg, size_t *msg_len,
if (ret != KNOT_EOK) {
dbg_tsig_detail("TSIG: rrset_to_wire = %s\n", knot_strerror(ret));
*digest_len = 0;
+ free(items);
+ knot_rrset_free(&tmp_tsig);
+ knot_rdata_free(&rdata);
return ret;
}
@@ -854,7 +616,8 @@ int knot_tsig_sign(uint8_t *msg, size_t *msg_len,
int knot_tsig_sign_next(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
const uint8_t *prev_digest, size_t prev_digest_len,
uint8_t *digest, size_t *digest_len,
- const knot_key_t *key)
+ const knot_key_t *key, uint8_t *to_sign,
+ size_t to_sign_len)
{
if (!msg || !msg_len || !key || !key || !digest || !digest_len) {
return KNOT_EBADARG;
@@ -869,52 +632,135 @@ int knot_tsig_sign_next(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
if (!tmp_tsig) {
return KNOT_ENOMEM;
}
+
+ /* Create rdata for TSIG RR. */
+ knot_rdata_t *rdata = knot_rdata_new();
+ if (!rdata) {
+ dbg_tsig_detail("TSIG: rdata = NULL\n");
+ knot_rrset_free(&tmp_tsig);
+ return KNOT_ENOMEM;
+ }
+
+ int ret = 0;
- tsig_rdata_store_current_time(tmp_tsig);
+ ret = knot_rrset_add_rdata(tmp_tsig, rdata);
+ if (ret != KNOT_EOK) {
+ dbg_tsig_detail("TSIG: could not add rdata\n");
+ knot_rrset_free(&tmp_tsig);
+ knot_rdata_free(&rdata);
+ return ret;
+ }
+
+ /* Create items for TSIG RR. */
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(KNOT_RRTYPE_TSIG);
+ assert(desc);
+
+ knot_rdata_item_t *items =
+ malloc(sizeof(knot_rdata_item_t) * desc->length);
+ if (!items) {
+ dbg_tsig_detail("TSIG: items = NULL\n");
+ ERR_ALLOC_FAILED;
+ knot_rrset_free(&tmp_tsig);
+ knot_rdata_free(&rdata);
+ return KNOT_ENOMEM;
+ }
+
+ memset(items, 0, sizeof(knot_rdata_item_t) * desc->length);
+ ret = knot_rdata_set_items(rdata, items, desc->length);
+ if (ret != KNOT_EOK) {
+ dbg_tsig_detail("TSIG: rdata_set_items returned %s\n", knot_strerror(ret));
+ knot_rrset_free(&tmp_tsig);
+ knot_rdata_free(&rdata);
+ free(items);
+ return ret;
+ }
+ free(items);
+
+ tsig_rdata_store_current_time(tmp_tsig);
+ tsig_rdata_set_fudge(tmp_tsig, 300);
+
/* Create wire to be signed. */
- size_t wire_len = prev_digest_len + *msg_len + KNOT_TSIG_TIMERS_LENGTH;
+ size_t wire_len = prev_digest_len + to_sign_len
+ + KNOT_TSIG_TIMERS_LENGTH + 2;
uint8_t *wire = malloc(wire_len);
if (!wire) {
ERR_ALLOC_FAILED;
+ knot_rrset_free(&tmp_tsig);
+ knot_rdata_deep_free(&rdata, KNOT_RRTYPE_TSIG, 0);
return KNOT_ENOMEM;
}
memset(wire, 0, wire_len);
+ /* Write previous digest length. */
+ knot_wire_write_u16(wire, prev_digest_len);
/* Write previous digest. */
- memcpy(wire, prev_digest, sizeof(uint8_t) * prev_digest_len);
+ memcpy(wire + 2, prev_digest, sizeof(uint8_t) * prev_digest_len);
/* Write original message. */
- memcpy(msg + prev_digest_len, msg, *msg_len);
+ memcpy(wire + prev_digest_len + 2, to_sign, to_sign_len);
/* Write timers. */
- knot_tsig_wire_write_timers(msg + prev_digest_len + *msg_len, tmp_tsig);
+ knot_tsig_wire_write_timers(wire + prev_digest_len + to_sign_len + 2,
+ tmp_tsig);
+
+ dbg_tsig_detail("Previous digest: \n");
+ dbg_tsig_hex_detail(prev_digest, prev_digest_len);
+
+ dbg_tsig_detail("Timers: \n");
+ dbg_tsig_hex_detail(wire + prev_digest_len + *msg_len,
+ KNOT_TSIG_TIMERS_LENGTH);
- int ret = 0;
ret = knot_tsig_compute_digest(wire, wire_len,
digest_tmp, &digest_tmp_len, key);
+
+ /* No matter how the function did, this data is no longer needed. */
+ free(wire);
if (ret != KNOT_EOK) {
+ knot_rrset_deep_free(&tmp_tsig, 1, 1, 1);
*digest_len = 0;
return ret;
}
if (digest_tmp_len > *digest_len) {
+ knot_rrset_deep_free(&tmp_tsig, 1, 1, 1);
*digest_len = 0;
return KNOT_ESPACE;
}
- free(wire);
-
/* Set the MAC. */
- tsig_rdata_set_mac(tmp_tsig, *digest_len, digest);
+ tsig_rdata_set_mac(tmp_tsig, digest_tmp_len, digest_tmp);
+
+ /* Set algorithm. */
+ tsig_rdata_set_alg(tmp_tsig, key->algorithm);
+
+ /* Set original id. */
+ tsig_rdata_set_orig_id(tmp_tsig, knot_wire_get_id(msg));
+
+ /* Set TSIG error. */
+ tsig_rdata_set_tsig_error(tmp_tsig, 0);
+
+ /* Set other data. */
+ tsig_rdata_set_other_data(tmp_tsig, 0, NULL);
+
+ dbg_tsig_detail("Message max length: %zu, message length: %zu\n",
+ msg_max_len, *msg_len);
size_t tsig_wire_size = msg_max_len - *msg_len;
int rr_count = 0;
ret = knot_rrset_to_wire(tmp_tsig, msg + *msg_len,
&tsig_wire_size, &rr_count);
if (ret != KNOT_EOK) {
+ knot_rrset_deep_free(&tmp_tsig, 1, 1, 1);
*digest_len = 0;
return ret;
}
+ /* This should not happen, at least one rr has to be converted. */
+ if (rr_count == 0) {
+ knot_rrset_deep_free(&tmp_tsig, 1, 1, 1);
+ return KNOT_EBADARG;
+ }
+
knot_rrset_deep_free(&tmp_tsig, 1, 1, 1);
*msg_len += tsig_wire_size;
@@ -932,6 +778,7 @@ static int knot_tsig_check_digest(const knot_rrset_t *tsig_rr,
const uint8_t *request_mac,
size_t request_mac_len,
const knot_key_t *tsig_key,
+ uint64_t prev_time_signed,
int use_times)
{
if (!tsig_rr || !wire || !tsig_key) {
@@ -939,7 +786,7 @@ static int knot_tsig_check_digest(const knot_rrset_t *tsig_rr,
}
/* Check time signed. */
- int ret = knot_tsig_check_time_signed(tsig_rr);
+ int ret = knot_tsig_check_time_signed(tsig_rr, prev_time_signed);
if (ret != KNOT_EOK) {
return ret;
}
@@ -1061,29 +908,140 @@ int knot_tsig_server_check(const knot_rrset_t *tsig_rr,
const knot_key_t *tsig_key)
{
dbg_tsig_verb("tsig_server_check()\n");
- return knot_tsig_check_digest(tsig_rr, wire, size, NULL, 0, tsig_key, 0);
+ return knot_tsig_check_digest(tsig_rr, wire, size, NULL, 0, tsig_key,
+ 0, 0);
}
int knot_tsig_client_check(const knot_rrset_t *tsig_rr,
const uint8_t *wire, size_t size,
const uint8_t *request_mac, size_t request_mac_len,
- const knot_key_t *tsig_key)
+ const knot_key_t *tsig_key,
+ uint64_t prev_time_signed)
{
dbg_tsig_verb("tsig_client_check()\n");
return knot_tsig_check_digest(tsig_rr, wire, size, request_mac,
- request_mac_len, tsig_key, 0);
+ request_mac_len, tsig_key,
+ prev_time_signed, 0);
}
int knot_tsig_client_check_next(const knot_rrset_t *tsig_rr,
const uint8_t *wire, size_t size,
const uint8_t *prev_digest,
size_t prev_digest_len,
- const knot_key_t *tsig_key)
+ const knot_key_t *tsig_key,
+ uint64_t prev_time_signed)
{
// return knot_tsig_client_check(tsig_rr, wire, size, prev_digest,
// prev_digest_len, tsig_key);
dbg_tsig_verb("tsig_client_check_next()\n");
return knot_tsig_check_digest(tsig_rr, wire, size, prev_digest,
- prev_digest_len, tsig_key, 1);
+ prev_digest_len, tsig_key,
+ prev_time_signed, 1);
return KNOT_ENOTSUP;
}
+
+int knot_tsig_add(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
+ uint16_t tsig_rcode, const knot_rrset_t *tsig_rr)
+{
+ /*! \todo Revise!! */
+
+ if (!msg || !msg_len || !tsig_rr) {
+ return KNOT_EBADARG;
+ }
+
+ /*! \todo What key to use, when we do not sign? Does this even work? */
+ knot_dname_t *key_name =
+ knot_dname_deep_copy(knot_rrset_owner(tsig_rr));
+ if (key_name == NULL) {
+ dbg_tsig_detail("TSIG: failed to copy owner\n");
+ return KNOT_ERROR;
+ }
+
+ knot_rrset_t *tmp_tsig =
+ knot_rrset_new(key_name,
+ KNOT_RRTYPE_TSIG, KNOT_CLASS_ANY, 0);
+ if (!tmp_tsig) {
+ dbg_tsig_detail("TSIG: tmp_tsig = NULL\n");
+ knot_dname_free(&key_name);
+ return KNOT_ENOMEM;
+ }
+
+ /* Create rdata for TSIG RR. */
+ knot_rdata_t *rdata = knot_rdata_new();
+ if (!rdata) {
+ dbg_tsig_detail("TSIG: rdata = NULL\n");
+ knot_rrset_deep_free(&tmp_tsig, 1, 1, 1);
+ return KNOT_ENOMEM;
+ }
+
+ knot_rrset_add_rdata(tmp_tsig, rdata);
+
+ /* Create items for TSIG RR. */
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(KNOT_RRTYPE_TSIG);
+ assert(desc);
+
+ knot_rdata_item_t *items =
+ malloc(sizeof(knot_rdata_item_t) * desc->length);
+ if (!items) {
+ dbg_tsig_detail("TSIG: items = NULL\n");
+ ERR_ALLOC_FAILED;
+ knot_rrset_deep_free(&tmp_tsig, 1, 1, 1);
+ return KNOT_ENOMEM;
+ }
+
+ memset(items, 0, sizeof(knot_rdata_item_t) * desc->length);
+
+ int ret = knot_rdata_set_items(rdata, items, desc->length);
+ if (ret != KNOT_EOK) {
+ dbg_tsig_detail("TSIG: rdata_set_items returned %s\n",
+ knot_strerror(ret));
+ knot_rrset_deep_free(&tmp_tsig, 1, 1, 1);
+ return ret;
+ }
+ free(items);
+
+ knot_dname_t *alg_name =
+ knot_dname_deep_copy(tsig_rdata_alg_name(tsig_rr));
+ if (alg_name == NULL) {
+ dbg_tsig_detail("TSIG: failed to copy alg name\n");
+ return KNOT_ERROR;
+ }
+
+ tsig_rdata_set_alg_name(tmp_tsig, alg_name);
+ tsig_rdata_set_time_signed(tmp_tsig, tsig_rdata_time_signed(tsig_rr));
+ tsig_rdata_set_fudge(tmp_tsig, tsig_rdata_fudge(tsig_rr));
+ tsig_rdata_set_mac(tmp_tsig, 0, NULL);
+
+ /* Set original ID */
+ tsig_rdata_set_orig_id(tmp_tsig, knot_wire_get_id(msg));
+
+ /* Set error */
+ tsig_rdata_set_tsig_error(tmp_tsig, tsig_rcode);
+
+ assert(tsig_rcode != KNOT_TSIG_RCODE_BADTIME);
+ /* Set other len. */
+ tsig_rdata_set_other_data(tmp_tsig, 0, 0);
+
+ size_t tsig_wire_len = msg_max_len - *msg_len;
+ int rr_count = 0;
+
+ /* Write RRSet to wire */
+ ret = knot_rrset_to_wire(tmp_tsig, msg + *msg_len,
+ &tsig_wire_len, &rr_count);
+ if (ret != KNOT_EOK) {
+ dbg_tsig_detail("TSIG: rrset_to_wire = %s\n", knot_strerror(ret));
+ knot_rrset_deep_free(&tmp_tsig, 1, 1, 1);
+ return ret;
+ }
+
+ knot_rrset_deep_free(&tmp_tsig, 1, 1, 1);
+
+ *msg_len += tsig_wire_len;
+
+ uint16_t arcount = knot_wire_get_arcount(msg);
+ knot_wire_set_arcount(msg, ++arcount);
+
+ return KNOT_EOK;
+}
+
diff --git a/src/libknot/tsig-op.h b/src/libknot/tsig-op.h
index b206dc7..07a84a8 100644
--- a/src/libknot/tsig-op.h
+++ b/src/libknot/tsig-op.h
@@ -63,7 +63,8 @@
int knot_tsig_sign(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
const uint8_t *request_mac, size_t request_mac_len,
uint8_t *digest, size_t *digest_len,
- const knot_key_t *key);
+ const knot_key_t *key, uint16_t tsig_rcode,
+ uint64_t request_time_signed);
/*!
* \brief Generate TSIG signature of a 2nd or later message in a TCP session.
@@ -96,7 +97,8 @@ int knot_tsig_sign(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
int knot_tsig_sign_next(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
const uint8_t *prev_digest, size_t prev_digest_len,
uint8_t *digest, size_t *digest_len,
- const knot_key_t *key);
+ const knot_key_t *key, uint8_t *to_sign,
+ size_t to_sign_len);
/*!
* \brief Checks incoming request.
@@ -133,7 +135,8 @@ int knot_tsig_server_check(const knot_rrset_t *tsig_rr,
int knot_tsig_client_check(const knot_rrset_t *tsig_rr,
const uint8_t *wire, size_t size,
const uint8_t *request_mac, size_t request_mac_len,
- const knot_key_t *key);
+ const knot_key_t *key,
+ uint64_t prev_time_signed);
/*!
* \brief Checks signature of 2nd or next packet in a TCP session.
@@ -154,7 +157,11 @@ int knot_tsig_client_check_next(const knot_rrset_t *tsig_rr,
const uint8_t *wire, size_t size,
const uint8_t *prev_digest,
size_t prev_digest_len,
- const knot_key_t *key);
+ const knot_key_t *key,
+ uint64_t prev_time_signed);
+
+int knot_tsig_add(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
+ uint16_t tsig_rcode, const knot_rrset_t *tsig_rr);
#endif /* _KNOT_TSIG_H_ */
diff --git a/src/libknot/tsig.c b/src/libknot/tsig.c
index 432539f..5790b68 100644
--- a/src/libknot/tsig.c
+++ b/src/libknot/tsig.c
@@ -32,7 +32,7 @@
/*! \brief TSIG algorithms table. */
#define TSIG_ALG_TABLE_SIZE 8
static knot_lookup_table_t tsig_alg_table[TSIG_ALG_TABLE_SIZE] = {
- { KNOT_TSIG_ALG_GSS_TSIG, "gss-tsig." },
+ { KNOT_TSIG_ALG_NULL, "gss-tsig." },
{ KNOT_TSIG_ALG_HMAC_MD5, "hmac-md5.sig-alg.reg.int." },
{ KNOT_TSIG_ALG_HMAC_SHA1, "hmac-sha1." },
{ KNOT_TSIG_ALG_HMAC_SHA224, "hmac-sha224." },
@@ -84,6 +84,9 @@ int tsig_rdata_set_alg_name(knot_rrset_t *tsig, knot_dname_t *alg_name)
}
knot_rdata_item_set_dname(rdata, 0, alg_name_copy);
+
+ /* Release the dname. We want it to have 1 reference only. */
+ knot_dname_release(alg_name_copy);
return KNOT_EOK;
}
@@ -103,12 +106,15 @@ int tsig_rdata_set_alg(knot_rrset_t *tsig, tsig_algorithm_t alg)
const char *alg_str = tsig_alg_to_str(alg);
knot_dname_t *alg_name_copy = knot_dname_new_from_str(alg_str,
strlen(alg_str),
- 0);
+ NULL);
if (!alg_name_copy) {
return KNOT_ENOMEM;
}
-
+
knot_rdata_item_set_dname(rdata, 0, alg_name_copy);
+
+ /* Release the dname. We want it to have 1 reference only. */
+ knot_dname_release(alg_name_copy);
return KNOT_EOK;
}
@@ -306,8 +312,35 @@ const knot_dname_t *tsig_rdata_alg_name(const knot_rrset_t *tsig)
tsig_algorithm_t tsig_rdata_alg(const knot_rrset_t *tsig)
{
- /*! \todo [TSIG] Implement me. */
- return KNOT_TSIG_ALG_HMAC_MD5;
+ if (!tsig) {
+ return KNOT_TSIG_ALG_NULL;
+ }
+
+ /* Get the algorithm name. */
+ const knot_dname_t *alg_name = tsig_rdata_alg_name(tsig);
+ if (!alg_name) {
+ dbg_tsig_detail("TSIG: rdata: cannot get algorithm name.\n");
+ return KNOT_TSIG_ALG_NULL;
+ }
+
+ /* Convert alg name to string. */
+ char *name = knot_dname_to_str(alg_name);
+ if (!name) {
+ dbg_tsig_detail("TSIG: rdata: cannot convert alg name.\n");
+ return KNOT_TSIG_ALG_NULL;
+ }
+
+ knot_lookup_table_t *item = knot_lookup_by_name(tsig_alg_table, name);
+ if (!item) {
+ dbg_tsig_detail("TSIG: rdata: unknown algorithm.\n");
+ return KNOT_TSIG_ALG_NULL;
+ }
+ free(name);
+
+ int id = item->id;
+
+
+ return id;
}
uint64_t tsig_rdata_time_signed(const knot_rrset_t *tsig)
@@ -581,7 +614,7 @@ const char* tsig_alg_to_str(tsig_algorithm_t alg)
size_t tsig_wire_maxsize(const knot_key_t* key)
{
size_t alg_name_size = strlen(tsig_alg_to_str(key->algorithm)) + 1;
-
+
return knot_dname_size(key->name) +
sizeof(uint16_t) + /* TYPE */
sizeof(uint16_t) + /* CLASS */
diff --git a/src/libknot/tsig.h b/src/libknot/tsig.h
index eafcfab..73fa832 100644
--- a/src/libknot/tsig.h
+++ b/src/libknot/tsig.h
@@ -59,9 +59,9 @@ typedef struct knot_key knot_key_t;
enum tsig_algorithm_digest_length {
KNOT_TSIG_ALG_DIG_LENGTH_GSS_TSIG = 0,
KNOT_TSIG_ALG_DIG_LENGTH_HMAC_MD5 = 16,
- KNOT_TSIG_ALG_DIG_LENGTH_SHA1 = 0,
+ KNOT_TSIG_ALG_DIG_LENGTH_SHA1 = 20,
KNOT_TSIG_ALG_DIG_LENGTH_SHA224 = 0,
- KNOT_TSIG_ALG_DIG_LENGTH_SHA256 = 0,
+ KNOT_TSIG_ALG_DIG_LENGTH_SHA256 = 32,
KNOT_TSIG_ALG_DIG_LENGTH_SHA384 = 0,
KNOT_TSIG_ALG_DIG_LENGTH_SHA512 = 0
};
diff --git a/src/libknot/updates/xfr-in.c b/src/libknot/updates/xfr-in.c
index 51be430..b1feddb 100644
--- a/src/libknot/updates/xfr-in.c
+++ b/src/libknot/updates/xfr-in.c
@@ -121,7 +121,8 @@ static int xfrin_create_query(knot_dname_t *qname, uint16_t qtype,
xfr->digest_size = xfr->digest_max_size;
rc = knot_tsig_sign(wire, &wire_size, *size, NULL, 0,
- xfr->digest, &xfr->digest_size, xfr->tsig_key);
+ xfr->digest, &xfr->digest_size, xfr->tsig_key,
+ 0, 0);
if (rc != KNOT_EOK) {
/*! \todo [TSIG] Handle TSIG errors. */
knot_packet_free(&pkt);
@@ -315,7 +316,7 @@ static int xfrin_process_orphan_rrsigs(knot_zone_contents_t *zone,
/*----------------------------------------------------------------------------*/
-static void xfrin_free_orphan_rrsigs(xfrin_orphan_rrsig_t **rrsigs)
+void xfrin_free_orphan_rrsigs(xfrin_orphan_rrsig_t **rrsigs)
{
xfrin_orphan_rrsig_t *r = *rrsigs;
while (r != NULL) {
@@ -358,6 +359,15 @@ static int xfrin_check_tsig(knot_packet_t *packet, knot_ns_xfr_t *xfr,
}
if (xfr->tsig_key) {
+ // just append the wireformat to the TSIG data
+ assert(KNOT_NS_TSIG_DATA_MAX_SIZE - xfr->tsig_data_size
+ >= xfr->wire_size);
+ memcpy(xfr->tsig_data + xfr->tsig_data_size,
+ xfr->wire, xfr->wire_size);
+ xfr->tsig_data_size += xfr->wire_size;
+ }
+
+ if (xfr->tsig_key) {
if (tsig_req && tsig == NULL) {
// TSIG missing!!
return KNOT_EMALF;
@@ -367,12 +377,14 @@ static int xfrin_check_tsig(knot_packet_t *packet, knot_ns_xfr_t *xfr,
ret = knot_tsig_client_check(tsig,
xfr->wire, xfr->wire_size,
xfr->digest, xfr->digest_size,
- xfr->tsig_key);
+ xfr->tsig_key,
+ xfr->tsig_prev_time_signed);
} else {
ret = knot_tsig_client_check_next(tsig,
- xfr->wire, xfr->wire_size,
+ xfr->tsig_data, xfr->tsig_data_size,
xfr->digest, xfr->digest_size,
- xfr->tsig_key);
+ xfr->tsig_key,
+ xfr->tsig_prev_time_signed);
}
if (ret != KNOT_EOK) {
@@ -392,20 +404,23 @@ static int xfrin_check_tsig(knot_packet_t *packet, knot_ns_xfr_t *xfr,
memcpy(xfr->digest, tsig_rdata_mac(tsig),
tsig_rdata_mac_length(tsig));
xfr->digest_size = tsig_rdata_mac_length(tsig);
+
+ // Extract the time signed from the TSIG and store it
+ // We may rewrite the tsig_req_time_signed field
+ xfr->tsig_prev_time_signed =
+ tsig_rdata_time_signed(tsig);
+
- } else { // TSIG not required and not there
- // just append the wireformat to the TSIG data
- assert(KNOT_NS_TSIG_DATA_MAX_SIZE - xfr->tsig_data_size
- >= xfr->wire_size);
- memcpy(xfr->tsig_data + xfr->tsig_data_size,
- xfr->wire, xfr->wire_size);
- xfr->tsig_data_size += xfr->wire_size;
- }
+ }/* else { // TSIG not required and not there
+
+ }*/
} else if (tsig != NULL) {
// TSIG where it should not be
return KNOT_EMALF;
}
+ knot_rrset_deep_free(&tsig, 1, 1, 1);
+
return KNOT_EOK;
}
@@ -482,8 +497,8 @@ int xfrin_process_axfr_packet(/*const uint8_t *pkt, size_t size,
// this should be the first packet
/*! \note [TSIG] Packet number for checking TSIG validation. */
xfr->packet_nr = 0;
- /*! \note [TSIG] Storing total size of data for TSIG digest. */
- xfr->tsig_data_size = 0;
+// /*! \note [TSIG] Storing total size of data for TSIG digest. */
+// xfr->tsig_data_size = 0;
// create new zone
/*! \todo Ensure that the packet is the first one. */
@@ -586,16 +601,16 @@ dbg_xfrin_exec(
/*! \note [TSIG] add the packet wire size to the data to be verified by
* TSIG
*/
- if (xfr->tsig_key) {
- dbg_xfrin("Adding packet wire to TSIG data (size till now: %zu,"
- " adding: %zu).\n", xfr->tsig_data_size,
- xfr->wire_size);
- assert(KNOT_NS_TSIG_DATA_MAX_SIZE - xfr->tsig_data_size
- >= xfr->wire_size);
- memcpy(xfr->tsig_data + xfr->tsig_data_size, xfr->wire,
- xfr->wire_size);
- xfr->tsig_data_size += xfr->wire_size;
- }
+// if (xfr->tsig_key) {
+// dbg_xfrin("Adding packet wire to TSIG data (size till now: %zu,"
+// " adding: %zu).\n", xfr->tsig_data_size,
+// xfr->wire_size);
+// assert(KNOT_NS_TSIG_DATA_MAX_SIZE - xfr->tsig_data_size
+// >= xfr->wire_size);
+// memcpy(xfr->tsig_data + xfr->tsig_data_size, xfr->wire,
+// xfr->wire_size);
+// xfr->tsig_data_size += xfr->wire_size;
+// }
assert(zone != NULL);
@@ -904,8 +919,7 @@ static int xfrin_parse_first_rr(knot_packet_t **packet, const uint8_t *pkt,
/*----------------------------------------------------------------------------*/
-int xfrin_process_ixfr_packet(/*const uint8_t *pkt, size_t size,
- knot_changesets_t **chs*/knot_ns_xfr_t *xfr)
+int xfrin_process_ixfr_packet(knot_ns_xfr_t *xfr)
{
size_t size = xfr->wire_size;
const uint8_t *pkt = xfr->wire;
@@ -922,8 +936,6 @@ int xfrin_process_ixfr_packet(/*const uint8_t *pkt, size_t size,
}
knot_packet_t *packet = NULL;
-// knot_rrset_t *soa1 = NULL;
-// knot_rrset_t *soa2 = NULL;
knot_rrset_t *rr = NULL;
int ret;
@@ -977,13 +989,16 @@ int xfrin_process_ixfr_packet(/*const uint8_t *pkt, size_t size,
/*
* If there is no other records in the response than the SOA, it
- * means one of these two cases:
+ * means one of these three cases:
*
* 1) The server does not have newer zone than ours.
* This is indicated by serial equal to the one of our zone.
* 2) The server wants to send the transfer but is unable to fit
* it in the packet. This is indicated by serial different
- * (newer) from the one of our zone.
+ * (newer) from the one of our zone, but applies only for
+ * IXFR/UDP.
+ * 3) The master is weird and sends only SOA in the first packet
+ * of a fallback to AXFR answer (PowerDNS does this).
*
* The serials must be compared in other parts of the server, so
* just indicate that the answer contains only one SOA.
@@ -996,8 +1011,6 @@ int xfrin_process_ixfr_packet(/*const uint8_t *pkt, size_t size,
knot_rrset_deep_free(&rr, 1, 1, 1);
dbg_xfrin("Fallback to AXFR.\n");
ret = XFRIN_RES_FALLBACK;
- knot_free_changesets(chs);
- xfr->data = 0;
return ret;
}
} else {
@@ -2014,7 +2027,6 @@ static knot_node_t *xfrin_add_new_node(knot_zone_contents_t *contents,
// dbg_xfrin("Adding new node to zone. From owner: %s type %s\n",
// knot_dname_to_str(node->owner),
// knot_rrtype_to_string(rrset->type));
-// getchar();
if (knot_rrset_type(rrset) == KNOT_RRTYPE_NSEC3) {
ret = knot_zone_contents_add_nsec3_node(contents, node, 1, 0,
1);
@@ -2067,7 +2079,6 @@ static int xfrin_apply_add_normal(xfrin_changes_t *changes,
dbg_xfrin("applying rrset:\n");
knot_rrset_dump(add, 0);
-// getchar();
if (!*rrset
|| knot_dname_compare(knot_rrset_owner(*rrset),
@@ -2089,7 +2100,6 @@ dbg_xfrin_exec_verb(
knot_rrtype_to_string(add->type));
free(name);
);
-// getchar();
// add the RRSet from the changeset to the node
/*! \todo What about domain names?? Shouldn't we use the
* zone-contents' version of this function??
diff --git a/src/libknot/updates/xfr-in.h b/src/libknot/updates/xfr-in.h
index 8a7c64b..bb1b98b 100644
--- a/src/libknot/updates/xfr-in.h
+++ b/src/libknot/updates/xfr-in.h
@@ -151,6 +151,8 @@ int xfrin_process_axfr_packet(/*const uint8_t *pkt, size_t size,
xfrin_constructed_zone_t **zone*/
knot_ns_xfr_t *xfr);
+void xfrin_free_orphan_rrsigs(xfrin_orphan_rrsig_t **rrsigs);
+
/*!
* \brief Destroys the whole changesets structure.
*
diff --git a/src/libknot/util/conv.c b/src/libknot/util/conv.c
new file mode 100644
index 0000000..6626ddd
--- /dev/null
+++ b/src/libknot/util/conv.c
@@ -0,0 +1,325 @@
+/*
+ * Copyright (c) 2001-2011, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <ctype.h>
+#include "conv.h"
+
+#define B64BUFSIZE 65535 /*!< Buffer size for b64 conversion. */
+
+
+static const char Base64[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const char Pad64 = '=';
+
+static int b64rmap_initialized = 0;
+static uint8_t b64rmap[256];
+
+static const uint8_t b64rmap_special = 0xf0;
+static const uint8_t b64rmap_end = 0xfd;
+static const uint8_t b64rmap_space = 0xfe;
+static const uint8_t b64rmap_invalid = 0xff;
+
+/**
+ * Initializing the reverse map is not thread safe.
+ * Which is fine for NSD. For now...
+ **/
+static void b64_initialize_rmap()
+{
+ int i;
+ char ch;
+
+ /* Null: end of string, stop parsing */
+ b64rmap[0] = b64rmap_end;
+
+ for (i = 1; i < 256; ++i) {
+ ch = (char)i;
+ /* Whitespaces */
+ if (isspace(ch)) {
+ b64rmap[i] = b64rmap_space;
+ }
+ /* Padding: stop parsing */
+ else if (ch == Pad64) {
+ b64rmap[i] = b64rmap_end;
+ }
+ /* Non-base64 char */
+ else {
+ b64rmap[i] = b64rmap_invalid;
+ }
+ }
+
+ /* Fill reverse mapping for base64 chars */
+ for (i = 0; Base64[i] != '\0'; ++i) {
+ b64rmap[(uint8_t)Base64[i]] = i;
+ }
+
+ b64rmap_initialized = 1;
+}
+
+static int b64_pton_do(char const *src, uint8_t *target, size_t targsize)
+{
+ int tarindex, state, ch;
+ uint8_t ofs;
+
+ state = 0;
+ tarindex = 0;
+
+ while (1) {
+ ch = *src++;
+ ofs = b64rmap[ch];
+
+ if (ofs >= b64rmap_special) {
+ /* Ignore whitespaces */
+ if (ofs == b64rmap_space) {
+ continue;
+ }
+ /* End of base64 characters */
+ if (ofs == b64rmap_end) {
+ break;
+ }
+ /* A non-base64 character. */
+ return (-1);
+ }
+
+ switch (state) {
+ case 0:
+ if ((size_t)tarindex >= targsize) {
+ return (-1);
+ }
+ target[tarindex] = ofs << 2;
+ state = 1;
+ break;
+ case 1:
+ if ((size_t)tarindex + 1 >= targsize) {
+ return (-1);
+ }
+ target[tarindex] |= ofs >> 4;
+ target[tarindex+1] = (ofs & 0x0f)
+ << 4 ;
+ tarindex++;
+ state = 2;
+ break;
+ case 2:
+ if ((size_t)tarindex + 1 >= targsize) {
+ return (-1);
+ }
+ target[tarindex] |= ofs >> 2;
+ target[tarindex+1] = (ofs & 0x03)
+ << 6;
+ tarindex++;
+ state = 3;
+ break;
+ case 3:
+ if ((size_t)tarindex >= targsize) {
+ return (-1);
+ }
+ target[tarindex] |= ofs;
+ tarindex++;
+ state = 0;
+ break;
+ default:
+ abort();
+ }
+ }
+
+ /*
+ * We are done decoding Base-64 chars. Let's see if we ended
+ * on a byte boundary, and/or with erroneous trailing characters.
+ */
+
+ if (ch == Pad64) { /* We got a pad char. */
+ ch = *src++; /* Skip it, get next. */
+ switch (state) {
+ case 0: /* Invalid = in first position */
+ case 1: /* Invalid = in second position */
+ return (-1);
+
+ case 2: /* Valid, means one byte of info */
+ /* Skip any number of spaces. */
+ for ((void)NULL; ch != '\0'; ch = *src++)
+ if (b64rmap[ch] != b64rmap_space) {
+ break;
+ }
+ /* Make sure there is another trailing = sign. */
+ if (ch != Pad64) {
+ return (-1);
+ }
+ ch = *src++; /* Skip the = */
+ /* Fall through to "single trailing =" case. */
+ /* FALLTHROUGH */
+
+ case 3: /* Valid, means two bytes of info */
+ /*
+ * We know this char is an =. Is there anything but
+ * whitespace after it?
+ */
+ for ((void)NULL; ch != '\0'; ch = *src++)
+ if (b64rmap[ch] != b64rmap_space) {
+ return (-1);
+ }
+
+ /*
+ * Now make sure for cases 2 and 3 that the "extra"
+ * bits that slopped past the last full byte were
+ * zeros. If we don't check them, they become a
+ * subliminal channel.
+ */
+ if (target[tarindex] != 0) {
+ return (-1);
+ }
+ }
+ } else {
+ /*
+ * We ended by seeing the end of the string. Make sure we
+ * have no partial bytes lying around.
+ */
+ if (state != 0) {
+ return (-1);
+ }
+ }
+
+ return (tarindex);
+}
+
+
+static int b64_pton_len(char const *src)
+{
+ int tarindex, state, ch;
+ uint8_t ofs;
+
+ state = 0;
+ tarindex = 0;
+
+ while (1) {
+ ch = *src++;
+ ofs = b64rmap[ch];
+
+ if (ofs >= b64rmap_special) {
+ /* Ignore whitespaces */
+ if (ofs == b64rmap_space) {
+ continue;
+ }
+ /* End of base64 characters */
+ if (ofs == b64rmap_end) {
+ break;
+ }
+ /* A non-base64 character. */
+ return (-1);
+ }
+
+ switch (state) {
+ case 0:
+ state = 1;
+ break;
+ case 1:
+ tarindex++;
+ state = 2;
+ break;
+ case 2:
+ tarindex++;
+ state = 3;
+ break;
+ case 3:
+ tarindex++;
+ state = 0;
+ break;
+ default:
+ abort();
+ }
+ }
+
+ /*
+ * We are done decoding Base-64 chars. Let's see if we ended
+ * on a byte boundary, and/or with erroneous trailing characters.
+ */
+
+ if (ch == Pad64) { /* We got a pad char. */
+ ch = *src++; /* Skip it, get next. */
+ switch (state) {
+ case 0: /* Invalid = in first position */
+ case 1: /* Invalid = in second position */
+ return (-1);
+
+ case 2: /* Valid, means one byte of info */
+ /* Skip any number of spaces. */
+ for ((void)NULL; ch != '\0'; ch = *src++)
+ if (b64rmap[ch] != b64rmap_space) {
+ break;
+ }
+ /* Make sure there is another trailing = sign. */
+ if (ch != Pad64) {
+ return (-1);
+ }
+ ch = *src++; /* Skip the = */
+ /* Fall through to "single trailing =" case. */
+ /* FALLTHROUGH */
+
+ case 3: /* Valid, means two bytes of info */
+ /*
+ * We know this char is an =. Is there anything but
+ * whitespace after it?
+ */
+ for ((void)NULL; ch != '\0'; ch = *src++)
+ if (b64rmap[ch] != b64rmap_space) {
+ return (-1);
+ }
+
+ }
+ } else {
+ /*
+ * We ended by seeing the end of the string. Make sure we
+ * have no partial bytes lying around.
+ */
+ if (state != 0) {
+ return (-1);
+ }
+ }
+
+ return (tarindex);
+}
+
+int b64_pton(char const *src, uint8_t *target, size_t targsize)
+{
+ if (!b64rmap_initialized) {
+ b64_initialize_rmap();
+ }
+
+ if (target) {
+ return b64_pton_do(src, target, targsize);
+ } else {
+ return b64_pton_len(src);
+ }
+}
+
+#define B64BUFSIZE 65535 /*!< Buffer size for b64 conversion. */
diff --git a/src/libknot/util/conv.h b/src/libknot/util/conv.h
new file mode 100644
index 0000000..d1e6dae
--- /dev/null
+++ b/src/libknot/util/conv.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2001-2011, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _KNOT_CONV_H_
+#define _KNOT_CONV_H_
+
+#define B64BUFSIZE 65535 /*!< Buffer size for b64 conversion. */
+
+/*!
+ * \brief Base64 presentation to wire conversion.
+ */
+int b64_pton(char const *src, uint8_t *target, size_t targsize);
+
+#endif // _KNOT_CONV_H_
diff --git a/src/libknot/util/debug.h b/src/libknot/util/debug.h
index 2f9f5fd..2c03c9c 100644
--- a/src/libknot/util/debug.h
+++ b/src/libknot/util/debug.h
@@ -57,10 +57,10 @@
//#define KNOT_NSEC3_DEBUG
//#define CUCKOO_DEBUG
//#define CUCKOO_DEBUG_HASH
-//#define KNOT_NS_DEBUG
+#define KNOT_NS_DEBUG
//#define KNOT_XFRIN_DEBUG
//#define KNOT_DDNS_DEBUG
-//#define KNOT_TSIG_DEBUG
+#define KNOT_TSIG_DEBUG
/*!
* \brief Dumps RDATA of the given type.
@@ -748,6 +748,45 @@ void knot_zone_contents_dump(knot_zone_contents_t *zone, char loaded_zone);
#define dbg_tsig_hex_detail(data, len)
#endif
+#ifdef KNOT_RRSET_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_rrset(msg...) fprintf(stderr, msg)
+#define dbg_rrset_hex(data, len) hex_print((data), (len))
+#else
+#define dbg_rrset(msg...)
+#define dbg_rrset_hex(data, len)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_rrset_verb(msg...) fprintf(stderr, msg)
+#define dbg_rrset_hex_verb(data, len) hex_print((data), (len))
+#else
+#define dbg_rrset_verb(msg...)
+#define dbg_rrset_hex_verb(data, len)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_rrset_detail(msg...) fprintf(stderr, msg)
+#define dbg_rrset_hex_detail(data, len) hex_print((data), (len))
+#else
+#define dbg_rrset_detail(msg...)
+#define dbg_rrset_hex_detail(data, len)
+#endif
+
+/* No messages. */
+#else
+#define dbg_rrset(msg...)
+#define dbg_rrset_hex(data, len)
+#define dbg_rrset_verb(msg...)
+#define dbg_rrset_hex_verb(data, len)
+#define dbg_rrset_detail(msg...)
+#define dbg_rrset_hex_detail(data, len)
+#endif
+
/******************************************************************************/
#endif /* _KNOT_DEBUG_H_ */
diff --git a/src/libknot/util/error.h b/src/libknot/util/error.h
index da45151..2f34a28 100644
--- a/src/libknot/util/error.h
+++ b/src/libknot/util/error.h
@@ -64,7 +64,8 @@ enum knot_error {
KNOT_ENOIXFR, /*!< Transfer is not IXFR (is in AXFR format). */
KNOT_EXFRREFUSED, /*!< Zone transfer refused by the server. */
KNOT_ECONN, /*!< Connection reset. */
- KNOT_ERROR_COUNT = 30
+ KNOT_EIXFRSPACE, /*!< IXFR reply did not fit in. */
+ KNOT_ERROR_COUNT = 31
};
/*! \brief Table linking error messages to error codes. */
diff --git a/src/libknot/util/libknot_error.c b/src/libknot/util/libknot_error.c
index bc2bed2..5795a23 100644
--- a/src/libknot/util/libknot_error.c
+++ b/src/libknot/util/libknot_error.c
@@ -49,5 +49,6 @@ const error_table_t knot_error_msgs[KNOT_ERROR_COUNT] = {
{KNOT_TSIG_EBADKEY, "TSIG key not recognized or invalid." },
{KNOT_TSIG_EBADTIME, "TSIG signing time out of range." },
{KNOT_ECONN, "Connection reset."},
+ {KNOT_EIXFRSPACE, "IXFR reply did not fit in."},
{KNOT_ERROR, 0}
};
diff --git a/src/libknot/zone/zone-contents.c b/src/libknot/zone/zone-contents.c
index d550728..feec8e5 100644
--- a/src/libknot/zone/zone-contents.c
+++ b/src/libknot/zone/zone-contents.c
@@ -676,7 +676,9 @@ static int knot_zone_contents_dnames_from_rdata_to_table(
{
unsigned int count = knot_rdata_item_count(rdata);
int rc = 0;
- assert(count <= d->length);
+ if (d->fixed_items) {
+ assert(count <= d->length);
+ }
// for each RDATA item
for (unsigned int j = 0; j < count; ++j) {
if (d->wireformat[j]
diff --git a/src/tests/common/acl_tests.c b/src/tests/common/acl_tests.c
index b4232ac..e9af20c 100644
--- a/src/tests/common/acl_tests.c
+++ b/src/tests/common/acl_tests.c
@@ -53,55 +53,55 @@ static int acl_tests_run(int argc, char *argv[])
ok(ret > 0, "acl: new IPv6 address");
// 4. Create simple IPv4 rule
- ret = acl_create(acl, &test_v4, ACL_ACCEPT);
+ ret = acl_create(acl, &test_v4, ACL_ACCEPT, 0);
ok(ret == ACL_ACCEPT, "acl: inserted IPv4 rule");
// 5. Create simple IPv6 rule
- ret = acl_create(acl, &test_v6, ACL_ACCEPT);
+ ret = acl_create(acl, &test_v6, ACL_ACCEPT, 0);
ok(ret == ACL_ACCEPT, "acl: inserted IPv6 rule");
// 6. Create simple IPv4 'any port' rule
sockaddr_t test_v4a;
sockaddr_set(&test_v4a, AF_INET, "20.20.20.20", 0);
- ret = acl_create(acl, &test_v4a, ACL_ACCEPT);
+ ret = acl_create(acl, &test_v4a, ACL_ACCEPT, 0);
ok(ret == ACL_ACCEPT, "acl: inserted IPv4 'any port' rule");
// 7. Attempt to match unmatching address
sockaddr_t unmatch_v4;
sockaddr_set(&unmatch_v4, AF_INET, "10.10.10.10", 24424);
- ret = acl_match(acl, &unmatch_v4);
+ ret = acl_match(acl, &unmatch_v4, 0);
ok(ret == ACL_DENY, "acl: matching non-existing address");
// 8. Attempt to match unmatching IPv6 address
sockaddr_t unmatch_v6;
sockaddr_set(&unmatch_v6, AF_INET6, "2001:db8::1428:57ab", 24424);
- ret = acl_match(acl, &unmatch_v6);
+ ret = acl_match(acl, &unmatch_v6, 0);
ok(ret == ACL_DENY, "acl: matching non-existing IPv6 address");
// 9. Attempt to match matching address
- ret = acl_match(acl, &test_v4);
+ ret = acl_match(acl, &test_v4, 0);
ok(ret == ACL_ACCEPT, "acl: matching existing address");
// 10. Attempt to match matching address
- ret = acl_match(acl, &test_v6);
+ ret = acl_match(acl, &test_v6, 0);
ok(ret == ACL_ACCEPT, "acl: matching existing IPv6 address");
// 11. Attempt to match matching 'any port' address
sockaddr_t match_v4a;
sockaddr_set(&match_v4a, AF_INET, "20.20.20.20", 24424);
- ret = acl_match(acl, &match_v4a);
+ ret = acl_match(acl, &match_v4a, 0);
ok(ret == ACL_ACCEPT, "acl: matching existing IPv4 'any port' address");
// 12. Attempt to match matching address without matching port
sockaddr_set(&unmatch_v4, AF_INET, "127.0.0.1", 54321);
- ret = acl_match(acl, &unmatch_v4);
+ ret = acl_match(acl, &unmatch_v4, 0);
ok(ret == ACL_DENY, "acl: matching address without matching port");
// 13. Invalid parameters
lives_ok({
acl_delete(0);
- acl_create(0, 0, ACL_ERROR);
- acl_match(0, 0);
+ acl_create(0, 0, ACL_ERROR, 0);
+ acl_match(0, 0, 0);
acl_truncate(0);
acl_name(0);
}, "acl: won't crash with NULL parameters");
diff --git a/src/tests/libknot/libknot/tsig_tests.c b/src/tests/libknot/libknot/tsig_tests.c
new file mode 100644
index 0000000..284579f
--- /dev/null
+++ b/src/tests/libknot/libknot/tsig_tests.c
@@ -0,0 +1,685 @@
+/* Copyright (C) 2011 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 <assert.h>
+#include <time.h>
+
+#include "libknot/rrset.h"
+#include "libknot/packet/response.h"
+#include "libknot/dname.h"
+#include "libknot/util/error.h"
+#include "libknot/util/wire.h"
+#include "libknot/tsig-op.h"
+
+#include "tsig_tests.h"
+
+static int knot_tsig_tests_count(int argc, char *argv[]);
+static int knot_tsig_tests_run(int argc, char *argv[]);
+
+/*! Exported unit API.
+ */
+unit_api tsig_tests_api = {
+ "DNS library - tsig", //! Unit name
+ &knot_tsig_tests_count, //! Count scheduled tests
+ &knot_tsig_tests_run //! Run scheduled tests
+};
+
+static const int KNOT_TSIG_TEST_COUNT = 6;
+
+static knot_rrset_t *create_dummy_tsig_rr()
+{
+ knot_dname_t *tsig_owner =
+ knot_dname_new_from_str("dummy.key.name.",
+ strlen("dummy.key.name."), NULL);
+ assert(tsig_owner);
+
+ /* Create dummy TSIG rr. */
+ knot_rrset_t *tsig_rr = knot_rrset_new(tsig_owner, KNOT_RRTYPE_TSIG,
+ KNOT_CLASS_ANY, 0);
+ assert(tsig_rr);
+
+ knot_rdata_t *tsig_rdata = knot_rdata_new();
+ assert(tsig_rr);
+ /* Create TSIG items. */
+ knot_rdata_item_t items[9];
+
+ /*
+ * I am not sure if 9 is the right count in our impl,
+ * but it should work fine.
+ */
+ knot_rdata_set_items(tsig_rdata, items, 9);
+ knot_dname_t *alg_name =
+ knot_dname_new_from_str("hmac-md5.sig-alg.reg.int.",
+ strlen("hmac-md5.sig-alg.reg.int."),
+ NULL);
+ assert(alg_name);
+ tsig_rdata_set_alg_name(tsig_rr, alg_name);
+
+ /* Get current time and save it to TSIG rr. */
+ time_t current_time = time(NULL);
+ tsig_rdata_set_time_signed(tsig_rr, current_time);
+ tsig_rdata_set_fudge(tsig_rr, 300);
+ tsig_rdata_set_orig_id(tsig_rr, 0);
+ tsig_rdata_set_tsig_error(tsig_rr, 0);
+ tsig_rdata_set_mac(tsig_rr, strlen("nonsensemac"),
+ (uint8_t *)"nonsensemac");
+
+ return tsig_rr;
+}
+
+static int test_knot_tsig_sign()
+{
+ int errors = 0;
+ /* Test bad arguments. */
+ int lived = 0;
+ lives_ok_silent({
+ int ret = knot_tsig_sign(NULL, NULL, 0, NULL, 0, NULL,
+ NULL, NULL, 0, 0);
+ if (ret != KNOT_EBADARG) {
+ diag("NULL argument did not return KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+
+ lived = 0;
+ ret = knot_tsig_sign((uint8_t *)0x1, NULL, 0, NULL, 0, NULL,
+ NULL, NULL, 0, 0);
+ if (ret != KNOT_EBADARG) {
+ diag("NULL argument did not return KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+
+ lived = 0;
+ ret = knot_tsig_sign((uint8_t *)0x1, (size_t *)0x1, 0, NULL,
+ 0, NULL,
+ NULL, NULL, 0, 0);
+ if (ret != KNOT_EBADARG) {
+ diag("NULL argument did not return KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+
+ lived = 0;
+ ret = knot_tsig_sign((uint8_t *)0x1, (size_t *)0x1, 0,
+ (uint8_t *)0x1, 0, NULL,
+ NULL, NULL, 0, 0);
+ if (ret != KNOT_EBADARG) {
+ diag("NULL argument did not return KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+
+ lived = 0;
+ ret = knot_tsig_sign((uint8_t *)0x12345678, (size_t *)0x1,
+ 0,(uint8_t *)0x1, 0,(uint8_t *) 0x1,
+ NULL, NULL, 0, 0);
+ if (ret != KNOT_EBADARG) {
+ diag("NULL argument did not return KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+
+ lived = 0;
+ ret = knot_tsig_sign((uint8_t *)0x12345678, (size_t *)0x1, 0,
+ (uint8_t *)0x1, 0,(uint8_t *) 0x1,
+ (size_t *)0x1, NULL, 0, 0);
+ if (ret != KNOT_EBADARG) {
+ diag("NULL argument did not return KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+ }, NULL);
+
+ errors += !lived;
+
+ if (errors) {
+ diag("NULL tests crashed!");
+ }
+
+ /* Create some dummy variables. */
+ /* One NS rrset. */
+ knot_dname_t *ns_dname = knot_dname_new_from_str("test.cz.",
+ strlen("test.cz."),
+ NULL);
+ assert(ns_dname);
+ knot_rrset_t *ns_rrset = knot_rrset_new(ns_dname, KNOT_RRTYPE_NS,
+ KNOT_CLASS_IN, 3600);
+ assert(ns_rrset);
+ knot_packet_t *packet = knot_packet_new(KNOT_PACKET_PREALLOC_RESPONSE);
+ assert(packet);
+
+ /* Add rdata. */
+ knot_rdata_t *ns_rdata = knot_rdata_new();
+ assert(ns_rdata);
+
+ knot_rdata_item_t items[1];
+ items[0].dname = ns_dname;
+
+ int ret = knot_rdata_set_items(ns_rdata, items, 1);
+ assert(ret == KNOT_EOK);
+ ret = knot_rrset_add_rdata(ns_rrset, ns_rdata);
+ assert(ret == KNOT_EOK);
+
+ knot_packet_set_max_size(packet, 2048);
+
+ if ((ret = knot_response_add_rrset_answer(packet, ns_rrset,
+ 0, 0, 0)) != KNOT_EOK) {
+ diag("Could not add rrset to packet!"
+ " %s\n", knot_strerror(ret));
+ /* No point in continuing. */
+ return 0;
+ }
+
+ uint8_t *msg = NULL;
+ size_t msg_len;
+ ret = knot_packet_to_wire(packet, &msg, &msg_len);
+ assert(ret == KNOT_EOK);
+
+ size_t msg_copy_length = msg_len;
+ uint8_t msg_copy[msg_len];
+ memcpy(msg_copy, msg, msg_len);
+
+ size_t msg_max_len = 1024;
+ uint8_t request_mac[16];
+ size_t request_mac_length = 0;
+ uint8_t digest[512];
+ size_t digest_len;
+
+ knot_key_t key;
+ key.algorithm = KNOT_TSIG_ALG_HMAC_MD5;
+ key.name = knot_dname_new_from_str("test.",
+ strlen("test."), NULL);
+ key.secret = "abcdefgh";
+ key.secret_size = strlen("abcdefgh");
+
+ /* Test not enough space for wire. */
+ ret = knot_tsig_sign(msg, &msg_len, msg_len + 1, request_mac,
+ request_mac_length,
+ digest, &digest_len, &key, 0, 0);
+ if (ret != KNOT_ESPACE) {
+ diag("knot_tsig_sign did not return error when given too"
+ " litle space for wire!");
+ errors++;
+ }
+
+ /* Test normal operation. */
+ ret = knot_tsig_sign(msg, &msg_len, msg_max_len, request_mac,
+ request_mac_length,
+ digest, &digest_len, &key, 0, 0);
+ if (ret != KNOT_EOK) {
+ diag("knot_tsig_sign failed when given right arguments!");
+ return 0;
+ }
+
+ /*
+ * Now check that the initial wire remained the same.
+ * (Except for arcount)
+ */
+
+ /* Read arcount. Should be 1. */
+ if (knot_wire_get_arcount(msg) != 1) {
+ diag("Signed wire did not have its arcount changed!");
+ errors++;
+ }
+
+ knot_wire_set_arcount(msg, 0);
+ /* Wire now should be identical. Compare with its pre-signing copy. */
+ if (strncmp((char *)msg, (char *)msg_copy, msg_len) != 0) {
+ hex_print(msg, msg_len);
+ hex_print(msg_copy, msg_len);
+ diag("knot_tsig_sign has changed the signed wire!");
+ errors++;
+ }
+
+ /* Do exactly the same, but add the request_mac variable. */
+ request_mac_length = 16;
+ memcpy(msg, msg_copy, msg_copy_length);
+ msg = msg_copy;
+ ret = knot_tsig_sign(msg, &msg_len, msg_max_len, request_mac,
+ request_mac_length,
+ digest, &digest_len, &key, 0, 0);
+ if (ret != KNOT_EOK) {
+ diag("knot_tsig_sign failed when given right arguments "
+ "(request mac set)!");
+ return 0;
+ }
+
+ /* Read arcount. Should be 1. */
+ if (knot_wire_get_arcount(msg) != 1) {
+ diag("Signed wire did not have its arcount changed!");
+ errors++;
+ }
+
+ knot_wire_set_arcount(msg, 0);
+ /* Wire now should be identical. Compare with its pre-signing copy. */
+ if (strncmp((char *)msg, (char *)msg_copy, msg_len) != 0) {
+ hex_print(msg, msg_len);
+ hex_print(msg_copy, msg_len);
+ diag("knot_tsig_sign has changed the signed wire!");
+ errors++;
+ }
+
+ /*
+ * Check that the wire is correctly signed
+ * using knot_tsig_server_check.
+ */
+
+ /* Create dummy tsig_rr. */
+ knot_rrset_t *tsig_rr = create_dummy_tsig_rr();
+ assert(tsig_rr);
+
+ /* Set the created digest. */
+ tsig_rdata_set_mac(tsig_rr, digest_len, digest);
+
+ ret = knot_tsig_server_check(tsig_rr, msg, msg_len, &key);
+ if (ret != KNOT_EOK) {
+ diag("Signed wire did not pass check!");
+ errors++;
+ }
+
+// free(msg);
+ return errors == 0;
+}
+
+static int test_knot_tsig_sign_next()
+{
+ int errors = 0;
+ /* Test bad arguments. */
+ int lived = 0;
+ lives_ok_silent({
+ int ret = knot_tsig_sign_next(NULL, NULL, 0, NULL, 0, NULL,
+ NULL, NULL, NULL, 0); /*! \todo FIX */
+ if (ret != KNOT_EBADARG) {
+ diag("NULL argument did not return KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+
+ lived = 0;
+ ret = knot_tsig_sign_next((uint8_t *)0x1, NULL, 0, NULL, 0,
+ NULL,
+ NULL, NULL, NULL, 0); /*! \todo FIX */
+ if (ret != KNOT_EBADARG) {
+ diag("NULL argument did not return KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+
+ lived = 0;
+ ret = knot_tsig_sign_next((uint8_t *)0x1, (size_t *)0x1, 0,
+ NULL, 0, NULL,
+ NULL, NULL, NULL, 0); /*! \todo FIX */
+ if (ret != KNOT_EBADARG) {
+ diag("NULL argument did not return KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+
+ lived = 0;
+ ret = knot_tsig_sign_next((uint8_t *)0x1, (size_t *)0x1, 0,
+ (uint8_t *)0x1, 0, NULL,
+ NULL, NULL, NULL, 0); /*! \todo FIX */
+ if (ret != KNOT_EBADARG) {
+ diag("NULL argument did not return KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+
+ lived = 0;
+ ret = knot_tsig_sign_next((uint8_t *)0x12345678, (size_t *)0x1,
+ 0,(uint8_t *)0x1, 0,(uint8_t *) 0x1,
+ NULL, NULL, NULL, 0); /*! \todo FIX */
+ if (ret != KNOT_EBADARG) {
+ diag("NULL argument did not return KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+
+ lived = 0;
+ ret = knot_tsig_sign_next((uint8_t *)0x12345678, (size_t *)0x1,
+ 0, (uint8_t *)0x1, 0,(uint8_t *) 0x1,
+ (size_t *)0x1, NULL, NULL, 0); /*! \todo FIX */
+ if (ret != KNOT_EBADARG) {
+ diag("NULL argument did not return KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+ }, NULL);
+
+ errors += !lived;
+
+ if (errors) {
+ diag("NULL tests crashed!");
+ }
+
+ /* Create some dummy variables. */
+ uint8_t msg[2048]; /* Should be random. */
+ size_t msg_len = 512;
+ size_t msg_max_len = 2048;
+ uint8_t *prev_digest = NULL;
+ size_t prev_digest_len = 0;
+ uint8_t digest[512];
+ size_t digest_len = 512;
+
+ knot_key_t key;
+ key.algorithm = KNOT_TSIG_ALG_HMAC_MD5;
+ key.name = knot_dname_new_from_str("test.",
+ strlen("test."), NULL);
+ key.secret = "abcdefgh";
+ key.secret_size = strlen("abcdefgh");
+
+ /* Test not enough space for wire. */
+ int ret = knot_tsig_sign_next(msg, &msg_len, 513, prev_digest,
+ prev_digest_len,
+ digest, &digest_len, &key, NULL, 0); /*! \todo FIX */
+ if (ret != KNOT_ESPACE) {
+ diag("knot_tsig_sign_next did not return error when "
+ "given too litle space for wire!"
+ " returned: %s", knot_strerror(ret));
+ errors++;
+ }
+
+ digest_len = 512;
+
+ /* Test normal operation. */
+ ret = knot_tsig_sign_next(msg, &msg_len, msg_max_len, prev_digest,
+ prev_digest_len,
+ digest, &digest_len, &key, NULL, 0); /*! \todo FIX */
+ if (ret != KNOT_EOK) {
+ diag("knot_tsig_sign_next failed when given right arguments!"
+ " returned: %s", knot_strerror(ret));
+ errors++;
+ }
+
+ /*!< \todo test that the variables have changed and so on. */
+
+ return errors == 0;
+}
+
+static int test_knot_tsig_server_check()
+{
+ int errors = 0;
+ /* Test bad arguments. */
+ int lived = 0;
+ lives_ok_silent({
+ int ret = knot_tsig_server_check(NULL, NULL, 0, NULL);
+ if (ret != KNOT_EBADARG) {
+ diag("NULL argument did not return KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+
+ lived = 0;
+ ret = knot_tsig_server_check((knot_rrset_t *)0x1,
+ (uint8_t *)0x1, 0,
+ NULL);
+ if (ret != KNOT_EBADARG) {
+ diag("NULL argument did not return KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+ }, NULL);
+
+ errors += !lived;
+
+ if (errors) {
+ diag("NULL tests crashed!");
+ }
+
+ /* Create dummy TSIG rr. */
+ knot_rrset_t *tsig_rr = create_dummy_tsig_rr();
+ assert(tsig_rr);
+
+
+ /* Create dummy key. */
+ knot_key_t key;
+ key.algorithm = KNOT_TSIG_ALG_HMAC_MD5;
+ key.secret = "supersecretsecret";
+ key.secret_size = strlen("supersecretsecret");
+ /* Bleeding eyes, I know. */
+ key.name = (knot_dname_t *)knot_rrset_owner(tsig_rr);
+
+ /* Create dummy wire. */
+ uint8_t wire[500];
+ size_t wire_size = 500;
+
+ /*!< \note
+ * Since there are no meaningful data in the wire,
+ * the function should fail.
+ */
+ int ret = knot_tsig_server_check(tsig_rr, wire, wire_size, &key);
+ if (ret != KNOT_TSIG_EBADSIG) {
+ diag("tsig_server_check did not return "
+ "TSIG_EBADSIG when given random wire!"
+ " returned: %s", knot_strerror(ret));
+ errors++;
+ }
+
+ /* Set 0 time - the error should be TSIG_EBADTIME. */
+ tsig_rdata_set_time_signed(tsig_rr, 0);
+ ret = knot_tsig_server_check(tsig_rr, wire, wire_size, &key);
+ if (ret != KNOT_TSIG_EBADTIME) {
+ diag("tsig_server_check did not return TSIG_EBADTIME "
+ "when given zero time!");
+ errors++;
+ }
+
+ return errors == 0;
+}
+
+static int test_knot_tsig_client_check()
+{
+ int errors = 0;
+ /* Test bad arguments. */
+ int lived = 0;
+ lives_ok({
+ int ret = knot_tsig_client_check(NULL, NULL, 0, NULL,
+ 0, NULL, 0);
+ if (ret != KNOT_EBADARG) {
+ diag("NULL argument did not return KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+
+ lived = 0;
+ ret = knot_tsig_client_check((knot_rrset_t *)0x1, NULL, 0, NULL,
+ 0, NULL, 0);
+ if (ret != KNOT_EBADARG) {
+ diag("NULL argument did not return KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+
+ lived = 0;
+ ret = knot_tsig_client_check((knot_rrset_t *)0x1,
+ (uint8_t *)0x1, 0, NULL,
+ 0, NULL, 0);
+ if (ret != KNOT_EBADARG) {
+ diag("NULL argument did not return KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+
+ lived = 0;
+ ret = knot_tsig_client_check((knot_rrset_t *)0x1,
+ (uint8_t *)0x1, 0, NULL,
+ 0, NULL, 0);
+ if (ret != KNOT_EBADARG) {
+ diag("NULL argument did not return KNOT_EBADARG!");
+ errors++;
+ }
+ lived = 1;
+ }, NULL);
+
+ errors += !lived;
+
+ if (errors) {
+ diag("NULL tests crashed!");
+ }
+
+ knot_dname_t *tsig_owner =
+ knot_dname_new_from_str("dummy.key.name.",
+ strlen("dummy.key.name."), NULL);
+ assert(tsig_owner);
+ /* Create dummy key. */
+ knot_key_t key;
+ key.algorithm = KNOT_TSIG_ALG_HMAC_MD5;
+ key.secret = "supersecretsecret";
+ key.secret_size = strlen("supersecretsecret");
+ key.name = tsig_owner;
+
+ /* Create dummy TSIG rr. */
+ knot_rrset_t *tsig_rr = knot_rrset_new(tsig_owner,
+ KNOT_RRTYPE_TSIG,
+ KNOT_CLASS_ANY, 0);
+ assert(tsig_rr);
+
+ knot_rdata_t *tsig_rdata = knot_rdata_new();
+ assert(tsig_rr);
+ /* Create TSIG items. */
+ knot_rdata_item_t items[9];
+
+ /*
+ * I am not sure if 9 is the right count in our impl.,
+ * but is should work fine.
+ */
+ knot_rdata_set_items(tsig_rdata, items, 9);
+ knot_dname_t *alg_name =
+ knot_dname_new_from_str("hmac-md5.sig-alg.reg.int.",
+ strlen("hmac-md5.sig-alg.reg.int."),
+ NULL);
+ assert(alg_name);
+ tsig_rdata_set_alg_name(tsig_rr, alg_name);
+ /* Get current time and save it to TSIG rr. */
+ time_t current_time = time(NULL);
+ tsig_rdata_set_time_signed(tsig_rr, current_time);
+ tsig_rdata_set_fudge(tsig_rr, 300);
+ tsig_rdata_set_orig_id(tsig_rr, 0);
+ tsig_rdata_set_tsig_error(tsig_rr, 0);
+ tsig_rdata_set_mac(tsig_rr, strlen("nonsensemac"),
+ (uint8_t *)"nonsensemac");
+
+ /* Create dummy wire. */
+ uint8_t wire[500];
+ size_t wire_size = 500;
+
+ /*!< \note
+ * Since there are no meaningful data in the wire,
+ * the function should fail.
+ */
+ int ret = knot_tsig_client_check(tsig_rr,
+ wire, wire_size, NULL, 0, &key, 0);
+ if (ret != KNOT_TSIG_EBADSIG) {
+ diag("tsig_server_check did not return TSIG_EBADSIG when "
+ "given random wire!");
+ errors++;
+ }
+
+ /* Set 0 time - the error should be TSIG_EBADTIME. */
+ tsig_rdata_set_time_signed(tsig_rr, 0);
+ ret = knot_tsig_client_check(tsig_rr, wire, wire_size, NULL,
+ 0, &key, 0);
+ if (ret != KNOT_TSIG_EBADTIME) {
+ diag("tsig_server_check did not return "
+ "TSIG_EBADTIME when given zero time!");
+ errors++;
+ }
+
+ return errors == 0;
+}
+
+static int test_knot_tsig_client_check_next()
+{
+ /*!< \todo think of extra test cases. */
+ return test_knot_tsig_client_check();
+}
+
+static int test_knot_tsig_test_tsig_add()
+{
+ int errors = 0;
+
+ /* Faulty arguments. */
+ int lived = 0;
+ lives_ok({
+ int ret = knot_tsig_add(NULL, NULL, 0, 0, NULL);
+ if (ret != KNOT_EBADARG) {
+ diag("tsig_add did not return EBADARG "
+ "when given NULL parameters.");
+ errors++;
+ }
+ lived = 1;
+
+ lived = 0;
+ ret = knot_tsig_add((uint8_t *)0x1, NULL, 0, 0, NULL);
+ if (ret != KNOT_EBADARG) {
+ diag("tsig_add did not return EBADARG when "
+ "given NULL parameters.");
+ errors++;
+ }
+ lived = 1;
+ }, NULL);
+
+ errors += !lived;
+
+ if (errors) {
+ diag("NULL tests failed!");
+ }
+
+ size_t wire_size = 512;
+ uint8_t wire[wire_size * 2];
+
+ /*! \todo Fix */
+ int ret = knot_tsig_add(wire, &wire_size, wire_size * 2, 0, NULL);
+ if (ret != KNOT_EOK) {
+ diag("tsig_add did not return EOK when given valid parameters."
+ " returned: %s", knot_strerror(ret));
+ errors++;
+ }
+
+ return errors == 0;
+}
+
+/*! This helper routine should report number of
+ * scheduled tests for given parameters.
+ */
+static int knot_tsig_tests_count(int argc, char *argv[])
+{
+ return KNOT_TSIG_TEST_COUNT;
+}
+
+/*! Run all scheduled tests for given parameters.
+ */
+static int knot_tsig_tests_run(int argc, char *argv[])
+{
+ int res_final = 0;
+ int res = 0;
+
+ ok(res = test_knot_tsig_sign(), "tsig: sign");
+ res_final *= res;
+ ok(res = test_knot_tsig_sign_next(), "tsig: sign next");
+ res_final *= res;
+ ok(res = test_knot_tsig_server_check(), "tsig: server check");
+ res_final *= res;
+ ok(res = test_knot_tsig_client_check(), "tsig: client check");
+ res_final *= res;
+ ok(res = test_knot_tsig_client_check_next(), "tsig: client check next");
+ res_final *= res;
+ ok(res = test_knot_tsig_test_tsig_add(), "tsig: tsig add");
+ res_final *= res;
+
+ return res_final;
+}
diff --git a/src/tests/libknot/libknot/tsig_tests.h b/src/tests/libknot/libknot/tsig_tests.h
new file mode 100644
index 0000000..8ea15f6
--- /dev/null
+++ b/src/tests/libknot/libknot/tsig_tests.h
@@ -0,0 +1,9 @@
+#ifndef TSIH_TESTS_H
+#define TSIH_TESTS_H
+
+#include "common/libtap/tap_unit.h"
+
+/* Unit API. */
+unit_api tsig_tests_api;
+
+#endif // TSIH_TESTS_H
diff --git a/src/tests/libknot/realdata/libknot_tests_loader_realdata.c b/src/tests/libknot/realdata/libknot_tests_loader_realdata.c
index 71a9c3d..e972855 100644
--- a/src/tests/libknot/realdata/libknot_tests_loader_realdata.c
+++ b/src/tests/libknot/realdata/libknot_tests_loader_realdata.c
@@ -988,9 +988,6 @@ static int add_rrset_to_node(const test_rrset_t *rrset, test_data_t *data)
tmp_node->next = NULL;
-// printf("%s\n", rrset->owner->wire);
-// getchar();
-
/* tmp_node->avl_left = NULL;
tmp_node->avl_right = NULL;
tmp_node->avl_height = 0; */
diff --git a/src/tests/libknot/unittests_libknot.c b/src/tests/libknot/unittests_libknot.c
index 62d4b90..d522e1d 100644
--- a/src/tests/libknot/unittests_libknot.c
+++ b/src/tests/libknot/unittests_libknot.c
@@ -33,6 +33,7 @@
#include "tests/libknot/libknot/query_tests.h"
#include "tests/libknot/libknot/zonedb_tests.h"
#include "tests/libknot/libknot/zone_tree_tests.h"
+#include "tests/libknot/libknot/tsig_tests.h"
// Run all loaded units
int main(int argc, char *argv[])
@@ -58,6 +59,7 @@ int main(int argc, char *argv[])
&query_tests_api,
&zonedb_tests_api, //! DNS library (zonedb) unit
&zone_tree_tests_api,
+ &tsig_tests_api,
NULL
};
diff --git a/src/tests/xfr_tests.c b/src/tests/xfr_tests.c
new file mode 100644
index 0000000..8a0b703
--- /dev/null
+++ b/src/tests/xfr_tests.c
@@ -0,0 +1,369 @@
+/* Copyright (C) 2011 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/>.
+ */
+
+/*
+ * This test is basically a copy of the whole server, with following exceptions:
+ * - conf file path is hardcoded, because it is generated by script
+ * invoking this binary.
+ * - signal handler now handles one more signal
+ * SIGCONT is used to signal this
+ * binary that an integrity check should be done
+ * - once the integrity check is completed, the server signal the script
+*/
+
+#include <time.h>
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#include "knot/common.h"
+#include "knot/other/error.h"
+#include "knot/server/server.h"
+#include "knot/ctl/process.h"
+#include "knot/conf/conf.h"
+#include "knot/conf/logconf.h"
+#include "common/evqueue.h"
+#include "knot/server/zones.h"
+
+/*----------------------------------------------------------------------------*/
+
+/* Signal flags. */
+static volatile short sig_req_stop = 0;
+static volatile short sig_req_reload = 0;
+static volatile short sig_stopping = 0;
+
+/* Global PID of invoking script. */
+static pid_t script_pid;
+static const char *zone;
+
+// SIGINT signal handler
+void interrupt_handle(int s)
+{
+ // Reload configuration
+ if (s == SIGHUP) {
+ sig_req_reload = 1;
+ return;
+ }
+
+ // Stop server
+ if (s == SIGINT || s == SIGTERM) {
+ if (sig_stopping == 0) {
+ sig_req_stop = 1;
+ sig_stopping = 1;
+ } else {
+ log_server_notice("OK! Exiting immediately.\n");
+ exit(1);
+ }
+ }
+
+ // Start zone integrity check
+ if (s == SIGCONT) {
+ /*!< \todo Insert actual integrity check! */
+ if (1) {
+ kill(script_pid, SIGCONT);
+ } else {
+ kill(script_pid, SIGHUP);
+ }
+ }
+}
+
+void help(int argc, char **argv)
+{
+ printf("Usage: %sd [parameters]\n",
+ PACKAGE_NAME);
+ printf("Parameters:\n"
+ " -c, --config [file] Select configuration file.\n"
+ " -d, --daemonize Run server as a daemon.\n"
+ " -v, --verbose Verbose mode - additional runtime information.\n"
+ " -V, --version Print version of the server.\n"
+ " -h, --help Print help and usage.\n");
+}
+
+int main(int argc, char **argv)
+{
+ // Parse command line arguments
+ int c = 0, li = 0;
+ int verbose = 0;
+ int daemonize = 0;
+ char* config_fn = 0;
+
+ /* Long options. */
+ struct option opts[] = {
+ {"config", required_argument, 0, 'c'},
+ {"script_pid",required_argument, 0, 'p'},
+ {"daemonize", no_argument, 0, 'd'},
+ {"verbose", no_argument, 0, 'v'},
+ {"version", no_argument, 0, 'V'},
+ {"help", no_argument, 0, 'h'},
+ {0, 0, 0, 0}
+ };
+
+ while ((c = getopt_long(argc, argv, "c:p:dvVh", opts, &li)) != -1) {
+ switch (c)
+ {
+ case 'c':
+ config_fn = strdup(optarg);
+ break;
+ case 'd':
+ daemonize = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'V':
+ printf("%s, version %s\n", "Knot DNS", PACKAGE_VERSION);
+ return 0;
+ case 'p':
+ script_pid = atoi(optarg);
+ break;
+ case 'z':
+ zone = strdup(optarg);
+ break;
+ case 'h':
+ case '?':
+ default:
+ help(argc, argv);
+ return 1;
+ }
+ }
+
+ // Now check if we want to daemonize
+ if (daemonize) {
+ if (daemon(1, 0) != 0) {
+ fprintf(stderr, "Daemonization failed, "
+ "shutting down...\n");
+ return 1;
+ }
+ }
+
+ // Register service and signal handler
+ struct sigaction emptyset;
+ memset(&emptyset, 0, sizeof(struct sigaction));
+ emptyset.sa_handler = interrupt_handle;
+ sigemptyset(&emptyset.sa_mask);
+ emptyset.sa_flags = 0;
+ sigaction(SIGALRM, &emptyset, NULL); // Interrupt
+
+ // Setup event queue
+ evqueue_set(evqueue_new());
+
+ // Initialize log
+ log_init();
+
+ // Verbose mode
+ if (verbose) {
+ int mask = LOG_MASK(LOG_INFO)|LOG_MASK(LOG_DEBUG);
+ log_levels_add(LOGT_STDOUT, LOG_ANY, mask);
+ }
+
+ // Initialize pseudorandom number generator
+ srand(time(0));
+
+ // Create server
+ server_t *server = server_create();
+
+ // Initialize configuration
+ conf_read_lock();
+ conf_add_hook(conf(), CONF_LOG, log_conf_hook, 0);
+ conf_add_hook(conf(), CONF_LOG, zones_ns_conf_hook, server->nameserver);
+ conf_add_hook(conf(), CONF_LOG, server_conf_hook, server);
+ conf_read_unlock();
+
+ // Find implicit configuration file
+ if (!config_fn) {
+ config_fn = conf_find_default();
+ }
+
+ // Find absolute path for config file
+ if (config_fn[0] != '/')
+ {
+ // Get absolute path to cwd
+ size_t cwbuflen = 64;
+ char *cwbuf = malloc((cwbuflen + 2) * sizeof(char));
+ while (getcwd(cwbuf, cwbuflen) == 0) {
+ cwbuflen *= 2;
+ cwbuf = realloc(cwbuf, (cwbuflen + 2) * sizeof(char));
+ }
+ cwbuflen = strlen(cwbuf);
+
+ // Append ending slash
+ if (cwbuf[cwbuflen - 1] != '/') {
+ cwbuf = strncat(cwbuf, "/", 1);
+ }
+
+ // Assemble path to config file
+ char *abs_cfg = strcdup(cwbuf, config_fn);
+ free(config_fn);
+ free(cwbuf);
+ config_fn = abs_cfg;
+ }
+
+ // Open configuration
+ log_server_info("Parsing configuration '%s' ...\n", config_fn);
+ if (conf_open(config_fn) != KNOTD_EOK) {
+
+ log_server_error("Failed to parse configuration file '%s'.\n",
+ config_fn);
+ server_destroy(&server);
+ free(config_fn);
+ return 1;
+ } else {
+ log_server_info("Configured %d interfaces and %d zones.\n",
+ conf()->ifaces_count, conf()->zones_count);
+ }
+ log_server_info("\n");
+
+ // Create server instance
+ char* pidfile = pid_filename();
+
+ // Run server
+ int res = 0;
+ log_server_info("Starting server...\n");
+ if ((res = server_start(server)) == KNOTD_EOK) {
+
+ // Save PID
+ int has_pid = 1;
+ int rc = pid_write(pidfile);
+ if (rc < 0) {
+ has_pid = 0;
+ log_server_warning("Failed to create "
+ "PID file '%s'.\n", pidfile);
+ }
+
+ // Change directory if daemonized
+ if (daemonize) {
+ log_server_info("Server started as a daemon, "
+ "PID = %ld\n", (long)getpid());
+ res = chdir("/");
+ } else {
+ log_server_info("Server started in foreground, "
+ "PID = %ld\n", (long)getpid());
+ }
+ if (has_pid) {
+ log_server_info("PID stored in %s\n", pidfile);
+ } else {
+ log_server_warning("Server running without PID file.\n");
+ }
+ size_t zcount = server->nameserver->zone_db->zone_count;
+ if (!zcount) {
+ log_server_warning("Server started, but no zones served.\n");
+ }
+
+ // Setup signal handler
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = interrupt_handle;
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGHUP, &sa, NULL);
+ sigaction(SIGCONT, &sa, NULL);
+ sa.sa_flags = 0;
+ pthread_sigmask(SIG_BLOCK, &sa.sa_mask, NULL);
+
+ /* Run event loop. */
+ for(;;) {
+ pthread_sigmask(SIG_UNBLOCK, &sa.sa_mask, NULL);
+ int ret = evqueue_poll(evqueue(), 0, 0);
+ pthread_sigmask(SIG_BLOCK, &sa.sa_mask, NULL);
+
+ /* Interrupts. */
+ /*! \todo More robust way to exit evloop.
+ * Event loop should exit with a special
+ * event.
+ */
+ if (sig_req_stop) {
+ sig_req_stop = 0;
+ server_stop(server);
+ break;
+ }
+ if (sig_req_reload) {
+ log_server_info("Reloading configuration...\n");
+ sig_req_reload = 0;
+ int cf_ret = conf_open(config_fn);
+ switch (cf_ret) {
+ case KNOTD_EOK:
+ log_server_info("Configuration "
+ "reloaded.\n");
+ break;
+ case KNOTD_ENOENT:
+ log_server_error("Configuration "
+ "file '%s' "
+ "not found.\n",
+ conf()->filename);
+ break;
+ default:
+ log_server_error("Configuration "
+ "reload failed.\n");
+ break;
+ }
+ }
+
+ /* Events. */
+ if (ret > 0) {
+ event_t ev;
+ if (evqueue_get(evqueue(), &ev) == 0) {
+ dbg_server_verb("Event: "
+ "received new event.\n");
+ if (ev.cb) {
+ ev.cb(&ev);
+ }
+ }
+ }
+ }
+ pthread_sigmask(SIG_UNBLOCK, &sa.sa_mask, NULL);
+
+ if ((res = server_wait(server)) != KNOTD_EOK) {
+ log_server_error("An error occured while "
+ "waiting for server to finish.\n");
+ } else {
+ log_server_info("Server finished.\n");
+ }
+
+ } else {
+ log_server_fatal("An error occured while "
+ "starting the server.\n");
+ }
+
+ // Stop server and close log
+ server_destroy(&server);
+
+ // Remove PID file
+ if (pid_remove(pidfile) < 0) {
+ log_server_warning("Failed to remove PID file.\n");
+ }
+
+ log_server_info("Shut down.\n");
+ log_close();
+ free(pidfile);
+
+ // Destroy event loop
+ evqueue_t *q = evqueue();
+ evqueue_free(&q);
+
+ // Free default config filename if exists
+ free(config_fn);
+
+ if (!daemonize) {
+ fflush(stdout);
+ fflush(stderr);
+ }
+
+ return res;
+}
+
diff --git a/src/tests/xfr_tests.h b/src/tests/xfr_tests.h
new file mode 100644
index 0000000..29de11d
--- /dev/null
+++ b/src/tests/xfr_tests.h
@@ -0,0 +1,10 @@
+#ifndef XFR_TESTS_H
+#define XFR_TESTS_H
+
+class xfr_tests
+{
+public:
+ xfr_tests();
+};
+
+#endif // XFR_TESTS_H