diff options
author | Ondřej Surý <ondrej@sury.org> | 2012-01-16 09:06:06 +0100 |
---|---|---|
committer | Ondřej Surý <ondrej@sury.org> | 2012-01-16 09:06:06 +0100 |
commit | 008d2859f102613c5a1d064c97e5dc7ea753faa2 (patch) | |
tree | 3c86cc7ea7616be45fc1aa6179967af62beef1ad | |
parent | 24569f087c5ea054a9199d7532dbbc98dd760ab5 (diff) | |
download | knot-008d2859f102613c5a1d064c97e5dc7ea753faa2.tar.gz |
Imported Upstream version 0.9upstream/0.9
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 @@ -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 @@ -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 |