BIND 10 trac2274, updated. 35cd79ce2cde949c18998cd2d94f6ddebeb13fc7 [2274] added introduced statistics items into the manpage of b10-xfrin
BIND 10 source code commits
bind10-changes at lists.isc.org
Wed Sep 26 10:02:19 UTC 2012
The branch, trac2274 has been updated
discards 77b251d791182ab6e7cf2a638b6abcfb22661d93 (commit)
discards d3a3475e3c3635d5eb59f46ffd175aeb6566cdb5 (commit)
discards 35d101b3bcd469e1564ed88fe782e33f24490c81 (commit)
discards c18b9a63ad8c93728fde328227650465b6efe678 (commit)
discards d640e6cd955a75b44a88c194f0558e0791db9e35 (commit)
discards ada6d18e8f388734ce8210b6c0d45e402c8fe35a (commit)
discards 1bedbb1d9ecaddadd191815798c181d036db2abc (commit)
discards 906e5adf333e4d6999cd9be9c27d8c7e22d4cabe (commit)
discards e12761aaf89bfb164d4e9e2f810b7bc804d740a8 (commit)
discards e2b13d414bdbf3c1051adb53b615b6c355cff510 (commit)
discards d591857e687fc1f21796a2fb80c5b356c2c6c6b3 (commit)
discards fbbcfb5e00c26ed5d1f25c2220a5337a804ecc57 (commit)
discards 8fefc02fece19e511ad846dee542cb1c2a0a99ab (commit)
discards 34139e404a6a75da4a318ac29caee6de7b421b78 (commit)
discards 460c217ac01563aff5ed1f8e49d07a926fda80e6 (commit)
discards c9e2cdbe304cb3c1dd2b4ae2269565c7cc885640 (commit)
discards ae6a0a5c83635bc7fa7cb555ef781bc951ca3535 (commit)
discards 73395e2950466719a7cd7058b9f7f2b21395c77c (commit)
discards 8a70337270f6cbbdbdd4a8da9bef29eb26bd28dc (commit)
discards 5ea423752e42176daf70bc1a75ee3de92e5f4f9e (commit)
discards ece194869f395753b5fcf468bf27bee277db6f7d (commit)
discards 51cbf728ef232b22b34f9a269ef2468aff5ef0ec (commit)
discards 6d2b918ef46eb247fa35bb4985917ef7efd2431b (commit)
discards e74f3c386ab1a9f7744ece87810ef74dd038e25f (commit)
discards 6469a5afbdfcddde5d498b08d6e6f327ab0e2b0c (commit)
discards d474c64e745f889742e244f612c26929d31d56b4 (commit)
discards 9ef7a4a4251aadb33bdc812403a8aadd134a28ce (commit)
discards 87e52b48844ab6e51b2b7e811eae8d8913e4a982 (commit)
discards ad05f3db1d7e3269b2255005703b23b655b4f700 (commit)
discards bea1713e0a7fcc1a2ed7b4b643f8e40a2f6f118c (commit)
discards 5726d042eb3f1cf208cedb66ae80d8ff5f038d59 (commit)
discards 52585df5aa0ed3c8c0d3d95d06251696f2fe734f (commit)
discards 5f3d3a0aa455506a87c4df13ce7a6ac23c2a203d (commit)
discards 780c008921615f0bb6294eb1f28b30d0622d2394 (commit)
discards 32ec525114d68f9e9c55655baf2096a819693b03 (commit)
discards eb4020a7f96d5d4687a2f56324de7a95cbfb20ce (commit)
discards 1231dd5b95fc8eaab5d1ada38334c5e20e9e8442 (commit)
discards 8f822cc5bd3c3f3394a08a0f3f3843a5abd4ae8f (commit)
discards 8c9404b1313eb54baabe1be30e74c90a7df82679 (commit)
discards b0942315b162dca7d333dbccd4c144a98715688c (commit)
discards 502542b26eb0dfa665c003e82f1b2fb20cf20138 (commit)
discards f4b3c3c25073c2116dcb72ff6a2da8f507a29884 (commit)
discards 432606e1dbc1e70a5f26aaeb505904ac06e112a9 (commit)
discards 444793726b58df80c2f21a30b69ba17ca5e2c2e9 (commit)
discards 10b08b43bffe38da9ba7c74ccc2dc74b47e2abad (commit)
via 35cd79ce2cde949c18998cd2d94f6ddebeb13fc7 (commit)
via ef0f1381bc8f6e90dc21811dbcbfb2f0c5a1b9b8 (commit)
via 2285ecaff624800ebaae202be8af1d5e397442a5 (commit)
via b1fc3206d5e1381bc8e5cf818ac92394cafad3fc (commit)
via 0bf254c4a5c8716d922216241a673c5b52ad6593 (commit)
via 7a80acd5d98ef5edf5266f2e58b93ba0ffd05441 (commit)
via a92bac12f88088e53b915e3d6be334524f00702a (commit)
via 5e530b77e1a118ce7ff6281654d5d04e1933f819 (commit)
via c6b68720fc37c4c9fc3e14a2a1fa2843bf173833 (commit)
via 313b4191b1100f38154f872ab8b56b765dd00d4f (commit)
via 213c732e9c492cd33be7ee9e6a98eb7f5a8320d8 (commit)
via 226e303f7a517a7aa9d4aab49c64ab7aed4ccb3f (commit)
via f32e37fafdb86d98e93a369d3e3a92d2579cdf06 (commit)
via 32bad1798f86d8ba4810cea2d5f92a2ab15bcda2 (commit)
via c4646cc34a85474864d72f7752182074ede89004 (commit)
via 8b942ae0df25f4205e5a828e13b6428856b1f6df (commit)
via 8b514265761f49e0855768bfb77e204d0ac71d94 (commit)
via c10ddc05b65641d773e160d50f5987198257ecd7 (commit)
via 7e67f21b4e7a2243ef6fad56e86a1c616292609b (commit)
via babdac4f533579f2fe8b4386b956c528e6ef331e (commit)
via fc68b5e633fb4631fc9f81204c783cdc4007eb6c (commit)
via e45e236a21be35f31fec539331877094f3adb0b9 (commit)
via e0e690cc3c4194678ca55b5e10cc4e50e9185f48 (commit)
via 9e54085ec92bd8a8af3fd1042887a973d70b2d79 (commit)
via 01ef4b9e28eba5fb58ab15383066ac2c253bfc81 (commit)
via 36d6278052e653036c101e196af3186801913447 (commit)
via 38d6d1bbf565025d372017c76b2e50f75bdb9185 (commit)
via 36d15da8ffb637b0e81fed8e11760a6d22f68883 (commit)
via b3419fdfa017ef3fd88aae1fc800ebb72ceaad02 (commit)
via f406d515e8ee18165740fbba5db95789e2cd2703 (commit)
via 6881cd3cc4fe062b1a067d0481c986eb6603a689 (commit)
via 91311bdbfea95f65c5e8bd8294ba08fac12405f1 (commit)
via 0f3f6b1f3cdc54176c1abf4b22bbdfe4dbae0a5c (commit)
via 21f612a9f5ec269d4ec4b1103b17857924cb2055 (commit)
via 37038754b11cb07ea6dfc8291586a5a5e6c4d5f8 (commit)
via 0a72b7f58d8d2ab06e4290443cf6adb4f4eef540 (commit)
via e0ffa11d49ab949ee5a4ffe7682b0e6906667baa (commit)
via 48df8a1486f00e27636febfa3487ddab9a051ca6 (commit)
via 668cdb357976bea8bc0b775459324b68dd2906b9 (commit)
via 179247735be1594337d07f0761b5fa8af2f44dc2 (commit)
via af6832161c3fc10de0239da3ddb47e7261413615 (commit)
via 704457924e2a892e012b7c750bfe08fe56da7128 (commit)
via 2fee008d989b5c60c13dbe86729e8a1ee8ac6456 (commit)
via 396bb7832e661dc597853e9f062e7094a209baf0 (commit)
via 3d13f1625ffb635f629944e8a1c0e793804d0112 (commit)
via b97e136dad599abde963d6971494d5838e1c6fbd (commit)
via 7ca65cb9ec528118f370142d7e7b792fcc31c9cf (commit)
via e9c984fb457ca225f77f8ce466e129670109169c (commit)
via 4ff65533b470925688c8c5b35af7afcacbf0cdf6 (commit)
via af808fad3a5d1de608b799a0e7491ac8eacac8ff (commit)
via d2a8a10ffc18ab3c57e4008d72fe505b62c25432 (commit)
via 3148cf2b018b222e263894b80cd7e92b20c86ceb (commit)
via 5c5f0831bee890e2245339d6f29b03ff33f94f9f (commit)
via d26707201f5c67cae4b3439ef63809da41e8d166 (commit)
via 3dc84c3cc22866efa60bb7a2701c50b05049971d (commit)
via 7feae4486ab8b722cdf897bcf629391e7c15fcf1 (commit)
via 7bedb1f498bf26780a3262a47b7850f9b92288bc (commit)
via 9a655fdd02d00a22003c6f7c9902ef80072a2332 (commit)
via a258bfbbb515f7bd3d336d14e89f2dd58412e807 (commit)
via 8015bd0bde0d4adccf7f4fc66423156cc9504011 (commit)
via f92a3289255ec12a39e9d720398b72ee97197991 (commit)
via e294cb85d5bf15e7036a0b1c4ae659513be6a101 (commit)
via 4b3d83b7b5cf42576081426ab8aaeb7e545640a0 (commit)
via 8ab3a360bb68b257c5a9bd363ce28343e409684a (commit)
via 1c9fa8a0b9ea4d328bbb6325e94ce1af95d6e99d (commit)
via 8bc72c0e77a786901f61f3fab7aa0074aa1c5b70 (commit)
via 583710591b610e704fd5739be4a4106917e35e9e (commit)
via 54ad8dd54acc965dc75513b739d604e74b4a11ee (commit)
via 64ce8ec302fd2f7afb3ece6b74e4a9b4ce127a7d (commit)
via 14727f8945873937f353915eac0e918cdb58721f (commit)
via 8e8f94c6320e47ff7856ceed21e5da31c614443b (commit)
via b117859663685e898c085750bc7a0908dc42e489 (commit)
via 5784101c08e8318a6e88bea598cf2a7ac5a389c3 (commit)
via 0187e5f3edb50f42f4f3388decd555518df71bf3 (commit)
via febd7504e90b57453ca53ef754093adf1d7a8899 (commit)
via 576ea605a69bf7edb0f4b1ba10c2db15862c9070 (commit)
via b82ca7c8a4c719c5fb5cb94c5020d5fab5da30eb (commit)
via 63e83a8f9c1ab9f7b32b76f80e65a771ee302cee (commit)
via fee99a5bb5b064ee62866f93b75b22c99905dc05 (commit)
via 9eeacfa616c813189a516f343c320d65ed9bfc7d (commit)
via 9d974a35bb76a63c7d8749d5728612ab0ef83c98 (commit)
via 71bfb14495829b1e03c79a03e6a1cd44c4d3a1b9 (commit)
via f0d4fcf168a840786ff9172ae23402920c85f3de (commit)
via dfc2f2b4dd181378d493aad1c53c8fcc53ba8152 (commit)
via 5a493720575e76a80ed6bba5cfcd2fac01364ce2 (commit)
via 869d1d20492f5221714d2fbf7c39bef800a28e60 (commit)
via a65bda660f985ff422b1528b1f7654ac254c6a79 (commit)
via 5ffdbdff90641201be36ab0ee65773df2ef6e0dd (commit)
via b90b048ba805efc450026c5192bb09aa7668d52f (commit)
via eb64f1c38d521886fef599582b4c80e892e31341 (commit)
via 6f4b4413fad5433ff41cd2b51f4784cbedfa859b (commit)
via 55f047a3e275e54e45a027631d1bd7e900b661f7 (commit)
via 7e5dbd9146f8c9a27937b733a63116d25865cf45 (commit)
via 4a78f86fffe147322f65b581a89d834b50999f39 (commit)
via ac96bd4de08738f779e79cc9cd15c88a63fb32bf (commit)
via 91a32d4c23fdb0780a78cae0fe34cec2ef64cffb (commit)
via 7230b23ea3aa18eb74d7f3eb705ef1f9ff0cee01 (commit)
via c0d2f860c8125033c4433b801559b363702f231d (commit)
via f1f86f353832e37b7987756bcba5071a5ef7101e (commit)
via e8a8a442cba80d9c9f2c0a7df1db981ccd195791 (commit)
via 803c67a839d109dea0b040ab2471a27ad995b419 (commit)
via 7028610344ad69d3e5da57575da3e2e85b257d0e (commit)
via 6f8add1167cb4781cc2f84d8c65ee5db73846ab8 (commit)
via c3c1915586223bb1db2114ad5b2bd2189f632517 (commit)
via 1f5426f7eddcaea4742fe9537fa957534b254fe6 (commit)
via 8bed689cafee235c4204c3ddeb980251bf6a9898 (commit)
via dbe150944902bc1de9f1aae199e7d55866d96d29 (commit)
via 40e0500ef5b5b39e698bf7c0ab6f8872cfdf1d07 (commit)
This update added new revisions after undoing existing revisions. That is
to say, the old revision is not a strict subset of the new revision. This
situation occurs when you --force push a change and generate a repository
containing something like this:
* -- * -- B -- O -- O -- O (77b251d791182ab6e7cf2a638b6abcfb22661d93)
\
N -- N -- N (35cd79ce2cde949c18998cd2d94f6ddebeb13fc7)
When this happens we assume that you've already had alert emails for all
of the O revisions, and so we here report only the revisions in the N
branch from the common base, B.
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 35cd79ce2cde949c18998cd2d94f6ddebeb13fc7
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Wed Sep 19 17:46:06 2012 +0900
[2274] added introduced statistics items into the manpage of b10-xfrin
commit ef0f1381bc8f6e90dc21811dbcbfb2f0c5a1b9b8
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Wed Sep 19 17:45:54 2012 +0900
[2274] updated lettuce test for new statistics items of Xfrin
- added new tests for new statistics items for Xfrin
- removed one from duplicated tests
commit 2285ecaff624800ebaae202be8af1d5e397442a5
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Wed Sep 19 17:45:37 2012 +0900
[2274] updated the BaseTestXfrCounter class for testing new statistics items
- moved up test_xfrrunning_counters() to the BaseTestXfrCounter class
- added a new variable holding a name list from target counter class
commit b1fc3206d5e1381bc8e5cf818ac92394cafad3fc
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Wed Sep 19 17:51:23 2012 +0900
[2274] updated the XfrinCounter class for adding new statistics items
- moved up _create_xfrrunning_functors() to the Counter class for sharing
- added a name list for new statistics items
commit 0bf254c4a5c8716d922216241a673c5b52ad6593
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Wed Sep 19 17:44:44 2012 +0900
[2274] added tests for addition of new Xfrin statistics items
It checks the value returned from the getter method of each item.
commit 7a80acd5d98ef5edf5266f2e58b93ba0ffd05441
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Wed Sep 19 16:59:47 2012 +0900
[2274] introduced new incrementers/decrementers into XfrinConnection
incrementers:
inc_soa_in_progress()
inc_ixfr_deferred()
inc_axfr_deferred()
inc_ixfr_running()
inc_axfr_running()
decrementers:
dec_soa_in_progress()
dec_ixfr_deferred()
dec_axfr_deferred()
dec_ixfr_running()
dec_axfr_running()
commit a92bac12f88088e53b915e3d6be334524f00702a
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Wed Sep 19 16:58:55 2012 +0900
[2274] added definitions of new statistics items for Xfrin
-----------------------------------------------------------------------
Summary of changes:
ChangeLog | 17 +
Makefile.am | 2 +-
configure.ac | 20 ++
src/bin/dhcp4/.gitignore | 4 +-
src/bin/dhcp6/.gitignore | 15 +-
src/bin/dhcp6/tests/.gitignore | 1 +
src/bin/msgq/msgq.py.in | 4 +-
src/bin/stats/tests/test_utils.py | 13 +-
src/bin/xfrin/tests/xfrin_test.py | 48 ++-
src/bin/xfrin/xfrin.py.in | 25 +-
src/bin/xfrout/tests/xfrout_test.py.in | 29 +-
src/bin/xfrout/xfrout.py.in | 1 -
src/lib/datasrc/memory/memory_client.cc | 363 ++++++++++----------
src/lib/datasrc/memory/memory_client.h | 4 +
.../datasrc/memory/tests/memory_client_unittest.cc | 121 ++++---
src/lib/datasrc/memory/tests/testdata/Makefile.am | 2 -
.../testdata/example.org-rrsig-name-unmatched.zone | 6 -
.../testdata/example.org-rrsig-type-unmatched.zone | 6 -
src/lib/datasrc/memory/zone_finder.cc | 4 -
src/lib/dhcp/tests/Makefile.am | 23 +-
src/lib/dns/message.cc | 9 +
src/lib/dns/message.h | 8 +
src/lib/dns/python/tests/message_python_test.py | 14 +-
src/lib/dns/python/tests/tsig_python_test.py | 17 +-
src/lib/dns/python/tsig_python.cc | 24 ++
src/lib/dns/tests/message_unittest.cc | 4 +-
src/lib/dns/tests/tsig_unittest.cc | 218 ++++++++++--
src/lib/dns/tsig.cc | 58 +++-
src/lib/dns/tsig.h | 27 +-
src/lib/python/isc/testutils/tsigctx_mock.py | 3 +
.../xfrin/retransfer_master.conf.orig | 3 +
.../lettuce/features/xfrin_notify_handling.feature | 30 +-
tests/tools/perfdhcp/.gitignore | 1 +
tests/tools/perfdhcp/command_options.cc | 9 +
tests/tools/perfdhcp/test_control.h | 4 +
tests/tools/perfdhcp/tests/Makefile.am | 4 +-
.../tools/perfdhcp/tests/command_options_helper.h | 16 +-
37 files changed, 762 insertions(+), 395 deletions(-)
create mode 100644 src/bin/dhcp6/tests/.gitignore
delete mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-rrsig-name-unmatched.zone
delete mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-rrsig-type-unmatched.zone
-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index 730ceb9..e266ba9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+478. [func] naokikambe
+ New statistics items added into b10-xfrout: ixfr_running and
+ axfr_running. Their values can be obtained by invoking "Stats show
+ Xfrout" via bindctl while b10-xfrout is running.
+ (Trac #2222, git 91311bdbfea95f65c5e8bd8294ba08fac12405f1)
+
+477. [bug] jelte
+ Fixed a problem with b10-msgq on OSX when using a custom Python
+ installation, that offers an unreliable select.poll() interface.
+ (Trac #2190, git e0ffa11d49ab949ee5a4ffe7682b0e6906667baa)
+
+476. [bug] vorner
+ The XfrIn now accepts transfers with some TSIG signatures omitted, as
+ allowed per RFC2845, section 4.4. This solves a compatibility
+ issues with Knot and NSD.
+ (Trac #1375, git 7ca65cb9ec528118f370142d7e7b792fcc31c9cf)
+
475. [func] naokikambe
Added Xfrout statistics counters: notifyoutv4, notifyoutv6, xfrrej, and
xfrreqdone. These are per-zone type counters. The value of these
diff --git a/Makefile.am b/Makefile.am
index 0fbb782..1ed0d63 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -30,7 +30,7 @@ endif
check-valgrind-suppress:
if HAVE_VALGRIND
- @VALGRIND_COMMAND="$(VALGRIND) -q --gen-suppressions=all --error-exitcode=1 --suppressions=$(abs_top_srcdir)/src/valgrind-suppressions --suppressions=$(abs_top_srcdir)/src/valgrind-suppressions.revisit --num-callers=48 --leak-check=full --fullpath-after=" \
+ @VALGRIND_COMMAND="$(VALGRIND) -q --gen-suppressions=all --track-origins=yes --error-exitcode=1 --suppressions=$(abs_top_srcdir)/src/valgrind-suppressions --suppressions=$(abs_top_srcdir)/src/valgrind-suppressions.revisit --num-callers=48 --leak-check=full --fullpath-after=" \
make -C $(abs_top_builddir) check
else
@echo "*** Valgrind is required for check-valgrind-suppress ***"; exit 1;
diff --git a/configure.ac b/configure.ac
index 10bae76..eb3f289 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1074,6 +1074,26 @@ if test "x$VALGRIND" != "xno"; then
found_valgrind="found"
fi
+# Check for optreset in unistd.h. On BSD systems the optreset is
+# used to reset the state of getopt() function. Resetting its state
+# is required if command line arguments are parsed multiple times
+# during a program. On Linux this variable will not exist because
+# getopt() reset is performed by setting optind = 0. On Operating
+# Systems where optreset is defined use optreset = 1 and optind = 1
+# to reset internal state of getopt(). Failing to do so will result
+# in unpredictable output from getopt().
+AC_MSG_CHECKING([whether optreset variable is defined in unistd.h])
+AC_TRY_LINK(
+ [#include <unistd.h>],
+ [extern int optreset; optreset=1;],
+ [ AC_MSG_RESULT(yes)
+ var_optreset_exists=yes],
+ [ AC_MSG_RESULT(no)
+ var_optreset_exists=no]
+)
+AM_CONDITIONAL(HAVE_OPTRESET, test "x$var_optreset_exists" != "xno")
+AM_COND_IF([HAVE_OPTRESET], [AC_DEFINE([HAVE_OPTRESET], [1], [Check for optreset?])])
+
AC_CONFIG_FILES([Makefile
doc/Makefile
doc/guide/Makefile
diff --git a/src/bin/dhcp4/.gitignore b/src/bin/dhcp4/.gitignore
index 952db06..86965b9 100644
--- a/src/bin/dhcp4/.gitignore
+++ b/src/bin/dhcp4/.gitignore
@@ -1,4 +1,6 @@
/b10-dhcp4
+/b10-dhcp4.8
+/dhcp4_messages.cc
+/dhcp4_messages.h
/spec_config.h
/spec_config.h.pre
-/b10-dhcp4.8
diff --git a/src/bin/dhcp6/.gitignore b/src/bin/dhcp6/.gitignore
index eedbb84..5878189 100644
--- a/src/bin/dhcp6/.gitignore
+++ b/src/bin/dhcp6/.gitignore
@@ -1,11 +1,6 @@
-*~
-Makefile
-Makefile.in
-*.o
-.deps
-.libs
-b10-dhcp6
-spec_config.h
-spec_config.h.pre
-tests/dhcp6_unittests
+/b10-dhcp6
/b10-dhcp6.8
+/dhcp6_messages.cc
+/dhcp6_messages.h
+/spec_config.h
+/spec_config.h.pre
diff --git a/src/bin/dhcp6/tests/.gitignore b/src/bin/dhcp6/tests/.gitignore
new file mode 100644
index 0000000..e170d18
--- /dev/null
+++ b/src/bin/dhcp6/tests/.gitignore
@@ -0,0 +1 @@
+/dhcp6_unittests
diff --git a/src/bin/msgq/msgq.py.in b/src/bin/msgq/msgq.py.in
index 58b1d87..010a1a5 100755
--- a/src/bin/msgq/msgq.py.in
+++ b/src/bin/msgq/msgq.py.in
@@ -131,9 +131,9 @@ class MsgQ:
def setup_poller(self):
"""Set up the poll thing. Internal function."""
try:
- self.poller = select.poll()
- except AttributeError:
self.kqueue = select.kqueue()
+ except AttributeError:
+ self.poller = select.poll()
def add_kqueue_socket(self, socket, write_filter=False):
"""Add a kquque filter for a socket. By default the read
diff --git a/src/bin/stats/tests/test_utils.py b/src/bin/stats/tests/test_utils.py
index f8abb57..55b08cb 100644
--- a/src/bin/stats/tests/test_utils.py
+++ b/src/bin/stats/tests/test_utils.py
@@ -107,19 +107,10 @@ class MockMsgq:
def run(self):
self._started.set()
- try:
- self.msgq.run()
- except Exception:
- pass
- finally:
- # explicitly shut down the socket of the msgq before
- # shutting down the msgq
- self.msgq.listen_socket.shutdown(msgq.socket.SHUT_RDWR)
- self.msgq.shutdown()
+ self.msgq.run()
def shutdown(self):
- # do nothing
- pass
+ self.msgq.shutdown()
class MockCfgmgr:
def __init__(self):
diff --git a/src/bin/xfrin/tests/xfrin_test.py b/src/bin/xfrin/tests/xfrin_test.py
index 9e68040..8f8131a 100644
--- a/src/bin/xfrin/tests/xfrin_test.py
+++ b/src/bin/xfrin/tests/xfrin_test.py
@@ -564,6 +564,28 @@ class TestXfrinIXFRAdd(TestXfrinState):
self.assertEqual(type(XfrinIXFRDeleteSOA()),
type(self.conn.get_xfrstate()))
+ def test_handle_new_delete_missing_sig(self):
+ self.conn._end_serial = isc.dns.Serial(1234)
+ # SOA RR whose serial is the current one means we are going to a new
+ # difference, starting with removing that SOA.
+ self.conn._diff.add_data(self.ns_rrset) # put some dummy change
+ self.conn._tsig_ctx = MockTSIGContext(TSIG_KEY)
+ self.conn._tsig_ctx.last_has_signature = lambda: False
+ # First, push a starting SOA inside. This should be OK, nothing checked
+ # yet.
+ self.state.handle_rr(self.conn, self.begin_soa)
+ end_soa_rdata = Rdata(RRType.SOA(), TEST_RRCLASS,
+ 'm. r. 1234 0 0 0 0')
+ end_soa_rrset = RRset(TEST_ZONE_NAME, TEST_RRCLASS, RRType.SOA(),
+ RRTTL(3600))
+ end_soa_rrset.add_rdata(end_soa_rdata)
+ # This would try to finish up. But the TSIG pretends not everything is
+ # signed, rejecting it.
+ self.assertRaises(xfrin.XfrinProtocolError, self.state.handle_rr,
+ self.conn, end_soa_rrset)
+ # No diffs were commited
+ self.assertEqual([], self.conn._datasrc_client.committed_diffs)
+
def test_handle_out_of_sync(self):
# getting SOA with an inconsistent serial. This is an error.
self.conn._end_serial = isc.dns.Serial(1235)
@@ -794,12 +816,14 @@ class TestAXFR(TestXfrinConnection):
# clear all statistics counters after each test
counter.clear_counters()
- def __create_mock_tsig(self, key, error):
+ def __create_mock_tsig(self, key, error, has_last_signature=True):
# This helper function creates a MockTSIGContext for a given key
# and TSIG error to be used as a result of verify (normally faked
# one)
mock_ctx = MockTSIGContext(key)
mock_ctx.error = error
+ if not has_last_signature:
+ mock_ctx.last_has_signature = lambda: False
return mock_ctx
def __match_exception(self, expected_exception, expected_msg, expression):
@@ -1460,6 +1484,16 @@ class TestAXFR(TestXfrinConnection):
counter.get_ixfr_deferred)
self.assertEqual(0, counter.get_axfr_deferred())
+ def test_do_xfrin_without_last_tsig(self):
+ # TSIG verify will succeed, but it will pretend the last message is
+ # not signed.
+ self.conn._tsig_key = TSIG_KEY
+ self.conn._tsig_ctx_creator = \
+ lambda key: self.__create_mock_tsig(key, TSIGError.NOERROR, False)
+ self.conn.response_generator = self._create_normal_response_data
+ self.assertEqual(self.conn.do_xfrin(False), XFRIN_FAIL)
+ self.assertEqual(2, self.conn._tsig_ctx.verify_called)
+
def test_do_xfrin_with_tsig_fail_for_second_message(self):
# Similar to the previous test, but first verify succeeds. There
# should be a second verify attempt, which will fail, which should
@@ -1634,16 +1668,18 @@ class TestIXFRResponse(TestXfrinConnection):
self.conn._handle_xfrin_responses()
self.assertEqual(type(XfrinIXFREnd()), type(self.conn.get_xfrstate()))
self.assertEqual([], self.conn._datasrc_client.diffs)
+ # Everything is committed as one bunch, currently we commit at the very
+ # end.
check_diffs(self.assertEqual,
[[('delete', begin_soa_rrset),
('delete', self._create_a('192.0.2.1')),
('add', self._create_soa('1231')),
- ('add', self._create_a('192.0.2.2'))],
- [('delete', self._create_soa('1231')),
+ ('add', self._create_a('192.0.2.2')),
+ ('delete', self._create_soa('1231')),
('delete', self._create_a('192.0.2.3')),
('add', self._create_soa('1232')),
- ('add', self._create_a('192.0.2.4'))],
- [('delete', self._create_soa('1232')),
+ ('add', self._create_a('192.0.2.4')),
+ ('delete', self._create_soa('1232')),
('delete', self._create_a('192.0.2.5')),
('add', soa_rrset),
('add', self._create_a('192.0.2.6'))]],
@@ -3014,7 +3050,7 @@ class TestFormatting(unittest.TestCase):
self.assertEqual("example.org/IN",
format_zone_str(isc.dns.Name("example.org"),
isc.dns.RRClass("IN")))
-
+
def test_format_addrinfo(self):
# This test may need to be updated if the input type is changed,
# right now it is a nested tuple:
diff --git a/src/bin/xfrin/xfrin.py.in b/src/bin/xfrin/xfrin.py.in
index 864c02b..3c39ca6 100755
--- a/src/bin/xfrin/xfrin.py.in
+++ b/src/bin/xfrin/xfrin.py.in
@@ -368,6 +368,7 @@ class XfrinFirstData(XfrinState):
conn._request_serial == get_soa_serial(rr.get_rdata()[0]):
logger.debug(DBG_XFRIN_TRACE, XFRIN_GOT_INCREMENTAL_RESP,
conn.zone_str())
+ conn._diff = None # Will be created on-demand
self.set_xfrstate(conn, XfrinIXFRDeleteSOA())
else:
logger.debug(DBG_XFRIN_TRACE, XFRIN_GOT_NONINCREMENTAL_RESP,
@@ -386,11 +387,13 @@ class XfrinIXFRDeleteSOA(XfrinState):
raise XfrinException(rr.get_type().to_text() +
' RR is given in IXFRDeleteSOA state')
# This is the beginning state of one difference sequence (changes
- # for one SOA update). We need to create a new Diff object now.
+ # for one SOA update). We may need to create a new Diff object now.
# Note also that we (unconditionally) enable journaling here. The
# Diff constructor may internally disable it, however, if the
# underlying data source doesn't support journaling.
- conn._diff = Diff(conn._datasrc_client, conn._zone_name, False, True)
+ if conn._diff is None:
+ conn._diff = Diff(conn._datasrc_client, conn._zone_name, False,
+ True)
conn._diff.delete_data(rr)
self.set_xfrstate(conn, XfrinIXFRDelete())
conn.get_transfer_stats().ixfr_deletion_count += 1
@@ -426,6 +429,9 @@ class XfrinIXFRAdd(XfrinState):
conn.get_transfer_stats().ixfr_changeset_count += 1
soa_serial = get_soa_serial(rr.get_rdata()[0])
if soa_serial == conn._end_serial:
+ # The final part is there. Check all was signed
+ # and commit it to the database.
+ conn._check_response_tsig_last()
conn._diff.commit()
self.set_xfrstate(conn, XfrinIXFREnd())
return True
@@ -435,7 +441,10 @@ class XfrinIXFRAdd(XfrinState):
str(conn._current_serial) +
', got ' + str(soa_serial))
else:
- conn._diff.commit()
+ # Apply a change to the database. But don't commit it yet,
+ # we can't know if the message is/will be properly signed.
+ # A complete commit will happen after the last bit.
+ conn._diff.apply()
self.set_xfrstate(conn, XfrinIXFRDeleteSOA())
return False
conn._diff.add_data(rr)
@@ -500,6 +509,7 @@ class XfrinAXFREnd(XfrinState):
indicating there will be no more message to receive.
"""
+ conn._check_response_tsig_last()
conn._diff.commit()
return False
@@ -788,6 +798,15 @@ class XfrinConnection(asyncore.dispatcher):
# strict.
raise XfrinProtocolError('Unexpected TSIG in response')
+ def _check_response_tsig_last(self):
+ """
+ Check there's a signature at the last message.
+ """
+ if self._tsig_ctx is not None:
+ if not self._tsig_ctx.last_has_signature():
+ raise XfrinProtocolError('TSIG verify fail: no TSIG on last '+
+ 'message')
+
def __parse_soa_response(self, msg, response_data):
'''Parse a response to SOA query and extract the SOA from answer.
diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in
index c910bbc..acdfbba 100644
--- a/src/bin/xfrout/tests/xfrout_test.py.in
+++ b/src/bin/xfrout/tests/xfrout_test.py.in
@@ -378,6 +378,9 @@ class TestXfroutSession(TestXfroutSessionBase):
"action": "DROP"
}
]))
+ # check the 'xfrrej' counter initially
+ self.assertRaises(isc.cc.data.DataNotFoundError,
+ counter.get_xfrrej, TEST_ZONE_NAME_STR)
# Localhost (the default in this test) is accepted
rcode, msg = self.xfrsess._parse_query_message(self.mdata)
self.assertEqual(rcode.to_text(), "NOERROR")
@@ -391,6 +394,8 @@ class TestXfroutSession(TestXfroutSessionBase):
('192.0.2.2', 12345))
rcode, msg = self.xfrsess._parse_query_message(self.mdata)
self.assertEqual(rcode.to_text(), "REFUSED")
+ # check the 'xfrrej' counter after incrementing
+ self.assertEqual(counter.get_xfrrej(TEST_ZONE_NAME_STR), 1)
# TSIG signed request
request_data = self.create_request_data(with_tsig=True)
@@ -419,6 +424,8 @@ class TestXfroutSession(TestXfroutSessionBase):
]))
[rcode, msg] = self.xfrsess._parse_query_message(request_data)
self.assertEqual(rcode.to_text(), "REFUSED")
+ # check the 'xfrrej' counter after incrementing
+ self.assertEqual(counter.get_xfrrej(TEST_ZONE_NAME_STR), 2)
# ACL using TSIG: no TSIG; should be rejected
acl_setter(isc.acl.dns.REQUEST_LOADER.load([
@@ -426,6 +433,8 @@ class TestXfroutSession(TestXfroutSessionBase):
]))
[rcode, msg] = self.xfrsess._parse_query_message(self.mdata)
self.assertEqual(rcode.to_text(), "REFUSED")
+ # check the 'xfrrej' counter after incrementing
+ self.assertEqual(counter.get_xfrrej(TEST_ZONE_NAME_STR), 3)
#
# ACL using IP + TSIG: both should match
@@ -445,26 +454,29 @@ class TestXfroutSession(TestXfroutSessionBase):
('192.0.2.2', 12345))
[rcode, msg] = self.xfrsess._parse_query_message(request_data)
self.assertEqual(rcode.to_text(), "REFUSED")
+ # check the 'xfrrej' counter after incrementing
+ self.assertEqual(counter.get_xfrrej(TEST_ZONE_NAME_STR), 4)
# Address matches, but TSIG doesn't (not included)
self.xfrsess._remote = (socket.AF_INET, socket.SOCK_STREAM,
('192.0.2.1', 12345))
[rcode, msg] = self.xfrsess._parse_query_message(self.mdata)
self.assertEqual(rcode.to_text(), "REFUSED")
+ # check the 'xfrrej' counter after incrementing
+ self.assertEqual(counter.get_xfrrej(TEST_ZONE_NAME_STR), 5)
# Neither address nor TSIG matches
self.xfrsess._remote = (socket.AF_INET, socket.SOCK_STREAM,
('192.0.2.2', 12345))
[rcode, msg] = self.xfrsess._parse_query_message(self.mdata)
self.assertEqual(rcode.to_text(), "REFUSED")
+ # check the 'xfrrej' counter after incrementing
+ self.assertEqual(counter.get_xfrrej(TEST_ZONE_NAME_STR), 6)
def test_transfer_acl(self):
# ACL checks only with the default ACL
def acl_setter(acl):
self.xfrsess._acl = acl
- self.assertRaises(isc.cc.data.DataNotFoundError,
- counter.get_xfrrej, TEST_ZONE_NAME_STR)
self.check_transfer_acl(acl_setter)
- self.assertGreater(\
- counter.get_xfrrej(TEST_ZONE_NAME_STR), 0)
+
def test_transfer_zoneacl(self):
# ACL check with a per zone ACL + default ACL. The per zone ACL
@@ -475,11 +487,7 @@ class TestXfroutSession(TestXfroutSessionBase):
self.xfrsess._zone_config[zone_key]['transfer_acl'] = acl
self.xfrsess._acl = isc.acl.dns.REQUEST_LOADER.load([
{"from": "127.0.0.1", "action": "DROP"}])
- self.assertRaises(isc.cc.data.DataNotFoundError,
- counter.get_xfrrej, TEST_ZONE_NAME_STR)
self.check_transfer_acl(acl_setter)
- self.assertGreater(\
- counter.get_xfrrej(TEST_ZONE_NAME_STR), 0)
def test_transfer_zoneacl_nomatch(self):
# similar to the previous one, but the per zone doesn't match the
@@ -491,11 +499,7 @@ class TestXfroutSession(TestXfroutSessionBase):
isc.acl.dns.REQUEST_LOADER.load([
{"from": "127.0.0.1", "action": "DROP"}])
self.xfrsess._acl = acl
- self.assertRaises(isc.cc.data.DataNotFoundError,
- counter.get_xfrrej, TEST_ZONE_NAME_STR)
self.check_transfer_acl(acl_setter)
- self.assertGreater(\
- counter.get_xfrrej(TEST_ZONE_NAME_STR), 0)
def test_get_transfer_acl(self):
# set the default ACL. If there's no specific zone ACL, this one
@@ -1667,7 +1671,6 @@ class MyXfroutServer(XfroutServer):
self._cc.get_module_spec = lambda:\
isc.config.module_spec_from_file(xfrout.SPECFILE_LOCATION)
-
class TestXfroutServer(unittest.TestCase):
def setUp(self):
self.xfrout_server = MyXfroutServer()
diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in
index 20c6b57..2f646d0 100755
--- a/src/bin/xfrout/xfrout.py.in
+++ b/src/bin/xfrout/xfrout.py.in
@@ -986,7 +986,6 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn,
self._transfers_counter -= 1
self._lock.release()
-
class XfroutServer:
def __init__(self):
self._unix_socket_server = None
diff --git a/src/lib/datasrc/memory/memory_client.cc b/src/lib/datasrc/memory/memory_client.cc
index d70f345..66822d2 100644
--- a/src/lib/datasrc/memory/memory_client.cc
+++ b/src/lib/datasrc/memory/memory_client.cc
@@ -41,6 +41,7 @@
#include <boost/scoped_ptr.hpp>
#include <boost/bind.hpp>
#include <boost/foreach.hpp>
+#include <boost/noncopyable.hpp>
#include <algorithm>
#include <map>
@@ -109,7 +110,6 @@ public:
unsigned int zone_count_;
ZoneTable* zone_table_;
FileNameTree* file_name_tree_;
- ConstRRsetPtr last_rrset_;
// Common process for zone load.
// rrset_installer is a functor that takes another functor as an argument,
@@ -171,7 +171,8 @@ public:
* If such condition is found, it throws AddError.
*/
void contextCheck(const Name& zone_name, const AbstractRRset& rrset,
- const RdataSet* set) const {
+ const RdataSet* set) const
+ {
// Ensure CNAME and other type of RR don't coexist for the same
// owner name except with NSEC, which is the only RR that can coexist
// with CNAME (and also RRSIG, which is handled separately)
@@ -247,7 +248,23 @@ public:
<< rrset->getName() << " which isn't supported");
}
- NameComparisonResult compare(zone_name.compare(rrset->getName()));
+ // For RRSIGs, check consistency of the type covered.
+ // We know the RRset isn't empty, so the following check is safe.
+ if (rrset->getType() == RRType::RRSIG()) {
+ RdataIteratorPtr rit = rrset->getRdataIterator();
+ const RRType covered = dynamic_cast<const generic::RRSIG&>(
+ rit->getCurrent()).typeCovered();
+ for (rit->next(); !rit->isLast(); rit->next()) {
+ if (dynamic_cast<const generic::RRSIG&>(
+ rit->getCurrent()).typeCovered() != covered) {
+ isc_throw(AddError, "RRSIG contains mixed covered types: "
+ << rrset->toText());
+ }
+ }
+ }
+
+ const NameComparisonResult compare =
+ zone_name.compare(rrset->getName());
if (compare.getRelation() != NameComparisonResult::SUPERDOMAIN &&
compare.getRelation() != NameComparisonResult::EQUAL)
{
@@ -262,10 +279,9 @@ public:
// Even though the protocol specifically doesn't completely ban such
// usage, we refuse to load a zone containing such RR in order to
// keep the lookup logic simpler and more predictable.
- // See RFC4592 and (for DNAME) draft-ietf-dnsext-rfc2672bis-dname
- // for more technical background. Note also that BIND 9 refuses
- // NS at a wildcard, so in that sense we simply provide compatible
- // behavior.
+ // See RFC4592 and (for DNAME) RFC6672 for more technical background.
+ // Note also that BIND 9 refuses NS at a wildcard, so in that sense
+ // we simply provide compatible behavior.
if (rrset->getName().isWildcard()) {
if (rrset->getType() == RRType::NS()) {
LOG_ERROR(logger, DATASRC_MEMORY_MEM_WILDCARD_NS).
@@ -299,7 +315,8 @@ public:
void addNSEC3(const ConstRRsetPtr rrset,
const ConstRRsetPtr rrsig,
- ZoneData& zone_data) {
+ ZoneData& zone_data)
+ {
// We know rrset has exactly one RDATA
const generic::NSEC3& nsec3_rdata =
dynamic_cast<const generic::NSEC3&>(
@@ -343,96 +360,57 @@ public:
}
void addRdataSet(const Name& zone_name, ZoneData& zone_data,
- const ConstRRsetPtr rrset, const ConstRRsetPtr rrsig) {
- // Only one of these can be passed at a time.
- assert(!(rrset && rrsig));
-
- // If rrsig is passed, validate it against the last-saved rrset.
- if (rrsig) {
- // The covered RRset should have been saved by now.
- if (!last_rrset_) {
- isc_throw(AddError,
- "RRSIG is being added, "
- "but doesn't follow its covered RR: "
- << rrsig->getName());
- }
-
- if (rrsig->getName() != last_rrset_->getName()) {
- isc_throw(AddError,
- "RRSIG is being added, "
- "but doesn't match the last RR's name: "
- << last_rrset_->getName() << " vs. "
- << rrsig->getName());
- }
-
- // Consistency of other types in rrsig are checked in addRRsig().
- RdataIteratorPtr rit = rrsig->getRdataIterator();
- const RRType covered = dynamic_cast<const generic::RRSIG&>(
- rit->getCurrent()).typeCovered();
-
- if (covered != last_rrset_->getType()) {
- isc_throw(AddError,
- "RRSIG is being added, "
- "but doesn't match the last RR's type: "
- << last_rrset_->getType() << " vs. "
- << covered);
- }
- }
-
- if (!last_rrset_) {
- last_rrset_ = rrset;
- return;
- }
-
- if (last_rrset_->getType() == RRType::NSEC3()) {
- addNSEC3(last_rrset_, rrsig, zone_data);
+ const ConstRRsetPtr rrset, const ConstRRsetPtr rrsig)
+ {
+ if (rrset->getType() == RRType::NSEC3()) {
+ addNSEC3(rrset, rrsig, zone_data);
} else {
ZoneNode* node;
- zone_data.insertName(mem_sgmt_, last_rrset_->getName(), &node);
+ zone_data.insertName(mem_sgmt_, rrset->getName(), &node);
- RdataSet* set = node->getData();
+ RdataSet* rdataset_head = node->getData();
// Checks related to the surrounding data.
// Note: when the check fails and the exception is thrown,
// it may break strong exception guarantee. At the moment
// we prefer code simplicity and don't bother to introduce
// complicated recovery code.
- contextCheck(zone_name, *last_rrset_, set);
+ contextCheck(zone_name, *rrset, rdataset_head);
- if (RdataSet::find(set, last_rrset_->getType()) != NULL) {
+ if (RdataSet::find(rdataset_head, rrset->getType()) != NULL) {
isc_throw(AddError,
"RRset of the type already exists: "
- << last_rrset_->getName() << " (type: "
- << last_rrset_->getType() << ")");
+ << rrset->getName() << " (type: "
+ << rrset->getType() << ")");
}
RdataEncoder encoder;
- RdataSet *new_set = RdataSet::create(mem_sgmt_, encoder,
- last_rrset_, rrsig);
- new_set->next = set;
- node->setData(new_set);
+ RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder, rrset,
+ rrsig);
+ rdataset->next = rdataset_head;
+ node->setData(rdataset);
// Ok, we just put it in
// If this RRset creates a zone cut at this node, mark the
// node indicating the need for callback in find().
- if (last_rrset_->getType() == RRType::NS() &&
- last_rrset_->getName() != zone_name) {
+ if (rrset->getType() == RRType::NS() &&
+ rrset->getName() != zone_name) {
node->setFlag(ZoneNode::FLAG_CALLBACK);
// If it is DNAME, we have a callback as well here
- } else if (last_rrset_->getType() == RRType::DNAME()) {
+ } else if (rrset->getType() == RRType::DNAME()) {
node->setFlag(ZoneNode::FLAG_CALLBACK);
}
// If we've added NSEC3PARAM at zone origin, set up NSEC3
// specific data or check consistency with already set up
// parameters.
- if (last_rrset_->getType() == RRType::NSEC3PARAM() &&
- last_rrset_->getName() == zone_name) {
+ if (rrset->getType() == RRType::NSEC3PARAM() &&
+ rrset->getName() == zone_name) {
// We know rrset has exactly one RDATA
const generic::NSEC3PARAM& param =
dynamic_cast<const generic::NSEC3PARAM&>
- (last_rrset_->getRdataIterator()->getCurrent());
+ (rrset->getRdataIterator()->getCurrent());
NSEC3Data* nsec3_data = zone_data.getNSEC3Data();
if (nsec3_data == NULL) {
@@ -450,91 +428,149 @@ public:
salt_data, salt_len) != 0)) {
isc_throw(AddError,
"NSEC3PARAM with inconsistent parameters: "
- << last_rrset_->toText());
+ << rrset->toText());
}
}
- } else if (last_rrset_->getType() == RRType::NSEC()) {
- // If it is NSEC signed zone, so we put a flag there
- // (flag is enough)
+ } else if (rrset->getType() == RRType::NSEC()) {
+ // If it is NSEC signed zone, we mark the zone as signed
+ // (conceptually "signed" is a broader notion but our current
+ // zone finder implementation regards "signed" as "NSEC
+ // signed")
zone_data.setSigned(true);
}
}
-
- last_rrset_ = rrset;
- }
-
- result::Result addRRsig(const ConstRRsetPtr sig_rrset,
- const Name& zone_name, ZoneData& zone_data)
- {
- // Check consistency of the type covered.
- // We know the RRset isn't empty, so the following check is safe.
- RdataIteratorPtr rit = sig_rrset->getRdataIterator();
- const RRType covered = dynamic_cast<const generic::RRSIG&>(
- rit->getCurrent()).typeCovered();
- for (rit->next(); !rit->isLast(); rit->next()) {
- if (dynamic_cast<const generic::RRSIG&>(
- rit->getCurrent()).typeCovered() != covered) {
- isc_throw(AddError, "RRSIG contains mixed covered types: "
- << sig_rrset->toText());
- }
- }
-
- addRdataSet(zone_name, zone_data, ConstRRsetPtr(), sig_rrset);
- return (result::SUCCESS);
}
- /*
- * Implementation of longer methods. We put them here, because the
- * access is without the impl_-> and it will get inlined anyway.
- */
-
// Implementation of InMemoryClient::add()
- result::Result add(const ConstRRsetPtr& rrset,
- const Name& zone_name, ZoneData& zone_data)
+ void add(const ConstRRsetPtr& rrset, const ConstRRsetPtr& sig_rrset,
+ const Name& zone_name, ZoneData& zone_data)
{
// Sanitize input. This will cause an exception to be thrown
// if the input RRset is empty.
addValidation(zone_name, rrset);
+ if (sig_rrset) {
+ addValidation(zone_name, sig_rrset);
+ }
// OK, can add the RRset.
LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEMORY_MEM_ADD_RRSET).
arg(rrset->getName()).arg(rrset->getType()).arg(zone_name);
- if (rrset->getType() == RRType::NSEC3()) {
- addRdataSet(zone_name, zone_data, rrset, ConstRRsetPtr());
- return (result::SUCCESS);
+ // Add wildcards possibly contained in the owner name to the domain
+ // tree. This can only happen for the normal (non-NSEC3) tree.
+ // Note: this can throw an exception, breaking strong exception
+ // guarantee. (see also the note for the call to contextCheck()
+ // above).
+ if (rrset->getType() != RRType::NSEC3()) {
+ addWildcards(zone_name, zone_data, rrset->getName());
}
- // RRSIGs are special in various points, so we handle it in a
- // separate dedicated method.
- if (rrset->getType() == RRType::RRSIG()) {
- return (addRRsig(rrset, zone_name, zone_data));
+ addRdataSet(zone_name, zone_data, rrset, sig_rrset);
+ }
+};
+
+// A helper internal class for load(). make it non-copyable to avoid
+// accidental copy.
+//
+// The current internal implementation expects that both a normal
+// (non RRSIG) RRset and (when signed) its RRSIG are added at once.
+// Also in the current implementation, the input sequence of RRsets
+// are grouped with their owner name (so once a new owner name is encountered,
+// no subsequent RRset has the previous owner name), but the ordering
+// in the same group is not fixed. So we hold all RRsets of the same
+// owner name in node_rrsets_ and node_rrsigsets_, and add the matching
+// pairs of RRsets to the zone when we see a new owner name.
+//
+// The caller is responsible for adding the RRsets of the last group
+// in the input sequence by explicitly calling flushNodeRRsets() at the
+// end. It's cleaner and more robust if we let the destructor of this class
+// do it, but since we cannot guarantee the adding operation is exception free,
+// we don't choose that option to maintain the common expectation for
+// destructors.
+class InMemoryClient::Loader : boost::noncopyable {
+ typedef std::map<RRType, ConstRRsetPtr> NodeRRsets;
+ typedef NodeRRsets::value_type NodeRRsetsVal;
+public:
+ Loader(InMemoryClientImpl* client_impl, const Name& zone_name,
+ ZoneData& zone_data) :
+ client_impl_(client_impl), zone_name_(zone_name), zone_data_(zone_data)
+ {}
+ void addFromLoad(const ConstRRsetPtr& rrset) {
+ // If we see a new name, flush the temporary holders, adding the
+ // pairs of RRsets and RRSIGs of the previous name to the zone.
+ if ((!node_rrsets_.empty() || !node_rrsigsets_.empty()) &&
+ getCurrentName() != rrset->getName()) {
+ flushNodeRRsets();
}
- // Add wildcards possibly contained in the owner name to the domain
- // tree.
- // Note: this can throw an exception, breaking strong exception
- // guarantee. (see also the note for contextCheck() below).
- addWildcards(zone_name, zone_data, rrset->getName());
+ // Store this RRset until it can be added to the zone. The current
+ // implementation requires RRs of the same RRset should be added at
+ // once, so we check the "duplicate" here.
+ const bool is_rrsig = rrset->getType() == RRType::RRSIG();
+ NodeRRsets& node_rrsets = is_rrsig ? node_rrsigsets_ : node_rrsets_;
+ const RRType& rrtype = is_rrsig ?
+ getCoveredType(rrset) : rrset->getType();
+ if (!node_rrsets.insert(NodeRRsetsVal(rrtype, rrset)).second) {
+ isc_throw(AddError,
+ "Duplicate add of the same type of"
+ << (is_rrsig ? " RRSIG" : "") << " RRset: "
+ << rrset->getName() << "/" << rrtype);
+ }
+ }
+ void flushNodeRRsets() {
+ BOOST_FOREACH(NodeRRsetsVal val, node_rrsets_) {
+ // Identify the corresponding RRSIG for the RRset, if any.
+ // If found add both the RRset and its RRSIG at once.
+ ConstRRsetPtr sig_rrset;
+ NodeRRsets::iterator sig_it =
+ node_rrsigsets_.find(val.first);
+ if (sig_it != node_rrsigsets_.end()) {
+ sig_rrset = sig_it->second;
+ node_rrsigsets_.erase(sig_it);
+ }
+ client_impl_->add(val.second, sig_rrset, zone_name_, zone_data_);
+ }
- addRdataSet(zone_name, zone_data, rrset, ConstRRsetPtr());
+ // Right now, we don't accept RRSIG without covered RRsets (this
+ // should eventually allowed, but to do so we'll need to update the
+ // finder).
+ if (!node_rrsigsets_.empty()) {
+ isc_throw(AddError, "RRSIG is added without covered RRset for "
+ << getCurrentName());
+ }
- return (result::SUCCESS);
+ node_rrsets_.clear();
+ node_rrsigsets_.clear();
}
-
- /*
- * Wrapper around above.
- */
- void addFromLoad(const ConstRRsetPtr& set,
- const Name& zone_name, ZoneData* zone_data)
- {
- switch (add(set, zone_name, *zone_data)) {
- case result::SUCCESS:
- return;
- default:
- assert(0);
+private:
+ // A helper to identify the covered type of an RRSIG.
+ static RRType getCoveredType(const ConstRRsetPtr& sig_rrset) {
+ RdataIteratorPtr it = sig_rrset->getRdataIterator();
+ // Empty RRSIG shouldn't be passed either via a master file or another
+ // data source iterator, but it could still happen if the iterator
+ // has a bug. We catch and reject such cases.
+ if (it->isLast()) {
+ isc_throw(isc::Unexpected,
+ "Empty RRset is passed in-memory loader, name: "
+ << sig_rrset->getName());
}
+ return (dynamic_cast<const generic::RRSIG&>(it->getCurrent()).
+ typeCovered());
}
+ const Name& getCurrentName() const {
+ if (!node_rrsets_.empty()) {
+ return (node_rrsets_.begin()->second->getName());
+ }
+ assert(!node_rrsigsets_.empty());
+ return (node_rrsigsets_.begin()->second->getName());
+ }
+
+private:
+ InMemoryClientImpl* client_impl_;
+ const Name& zone_name_;
+ ZoneData& zone_data_;
+ NodeRRsets node_rrsets_;
+ NodeRRsets node_rrsigsets_;
};
result::Result
@@ -544,31 +580,17 @@ InMemoryClient::InMemoryClientImpl::load(
boost::function<void(LoadCallback)> rrset_installer)
{
SegmentObjectHolder<ZoneData, RRClass> holder(
- mem_sgmt_, ZoneData::create(mem_sgmt_, zone_name),
- rrclass_);
-
- assert(!last_rrset_);
-
- try {
- rrset_installer(boost::bind(&InMemoryClientImpl::addFromLoad, this,
- _1, zone_name, holder.get()));
- // Add any last RRset that was left
- addRdataSet(zone_name, *holder.get(),
- ConstRRsetPtr(), ConstRRsetPtr());
- } catch (...) {
- last_rrset_ = ConstRRsetPtr();
- throw;
- }
+ mem_sgmt_, ZoneData::create(mem_sgmt_, zone_name), rrclass_);
- assert(!last_rrset_);
+ Loader loader(this, zone_name, *holder.get());
+ rrset_installer(boost::bind(&Loader::addFromLoad, &loader, _1));
+ // Add any last RRsets that were left
+ loader.flushNodeRRsets();
const ZoneNode* origin_node = holder.get()->getOriginNode();
const RdataSet* set = origin_node->getData();
// If the zone is NSEC3-signed, check if it has NSEC3PARAM
if (holder.get()->isNSEC3Signed()) {
- // Note: origin_data_ is set on creation of ZoneData, and the load
- // process only adds new nodes (and their data), so this assertion
- // should hold.
if (RdataSet::find(set, RRType::NSEC3PARAM()) == NULL) {
LOG_WARN(logger, DATASRC_MEMORY_MEM_NO_NSEC3PARAM).
arg(zone_name).arg(rrclass_);
@@ -639,30 +661,8 @@ masterLoadWrapper(const char* const filename, const Name& origin,
void
generateRRsetFromIterator(ZoneIterator* iterator, LoadCallback callback) {
ConstRRsetPtr rrset;
- vector<ConstRRsetPtr> rrsigs; // placeholder for RRSIGs until "commitable".
-
- // The current internal implementation assumes an RRSIG is always added
- // after the RRset they cover. So we store any RRSIGs in 'rrsigs' until
- // it's safe to add them; based on our assumption if the owner name
- // changes, all covered RRsets of the previous name should have been
- // installed and any pending RRSIGs can be added at that point. RRSIGs
- // of the last name from the iterator must be added separately.
while ((rrset = iterator->getNextRRset()) != NULL) {
- if (!rrsigs.empty() && rrset->getName() != rrsigs[0]->getName()) {
- BOOST_FOREACH(ConstRRsetPtr sig_rrset, rrsigs) {
- callback(sig_rrset);
- }
- rrsigs.clear();
- }
- if (rrset->getType() == RRType::RRSIG()) {
- rrsigs.push_back(rrset);
- } else {
- callback(rrset);
- }
- }
-
- BOOST_FOREACH(ConstRRsetPtr sig_rrset, rrsigs) {
- callback(sig_rrset);
+ callback(rrset);
}
}
}
@@ -736,29 +736,20 @@ InMemoryClient::getFileName(const isc::dns::Name& zone_name) const {
result::Result
InMemoryClient::add(const isc::dns::Name& zone_name,
- const ConstRRsetPtr& rrset) {
- assert(!impl_->last_rrset_);
-
- ZoneTable::FindResult result(impl_->zone_table_->findZone(zone_name));
+ const ConstRRsetPtr& rrset)
+{
+ const ZoneTable::FindResult result =
+ impl_->zone_table_->findZone(zone_name);
if (result.code != result::SUCCESS) {
isc_throw(DataSourceError, "No such zone: " + zone_name.toText());
}
- result::Result ret(impl_->add(rrset, zone_name, *result.zone_data));
- // Add any associated RRSIG too. This has to be done here, as both
- // the RRset and its RRSIG have to be passed when constructing an
- // RdataSet.
- if ((ret == result::SUCCESS) && rrset->getRRsig()) {
- impl_->add(rrset->getRRsig(), zone_name, *result.zone_data);
- }
-
- // Add any last RRset that was left
- impl_->addRdataSet(zone_name, *result.zone_data,
- ConstRRsetPtr(), ConstRRsetPtr());
-
- assert(!impl_->last_rrset_);
+ const ConstRRsetPtr sig_rrset =
+ rrset ? rrset->getRRsig() : ConstRRsetPtr();
+ impl_->add(rrset, sig_rrset, zone_name, *result.zone_data);
- return (ret);
+ // add() doesn't allow duplicate add, so we always return SUCCESS.
+ return (result::SUCCESS);
}
namespace {
diff --git a/src/lib/datasrc/memory/memory_client.h b/src/lib/datasrc/memory/memory_client.h
index d2ec4c3..4c3ad27 100644
--- a/src/lib/datasrc/memory/memory_client.h
+++ b/src/lib/datasrc/memory/memory_client.h
@@ -244,6 +244,10 @@ private:
// directly any more (it should be handled through DataSourceClient)?
class InMemoryClientImpl;
InMemoryClientImpl* impl_;
+
+ // A helper internal class used by load(). It maintains some intermediate
+ // states while loading RRs of the zone.
+ class Loader;
};
} // namespace memory
diff --git a/src/lib/datasrc/memory/tests/memory_client_unittest.cc b/src/lib/datasrc/memory/tests/memory_client_unittest.cc
index d5aa431..fcee0be 100644
--- a/src/lib/datasrc/memory/tests/memory_client_unittest.cc
+++ b/src/lib/datasrc/memory/tests/memory_client_unittest.cc
@@ -34,6 +34,8 @@
#include <testutils/dnsmessage_test.h>
+#include "memory_segment_test.h"
+
#include <gtest/gtest.h>
#include <new> // for bad_alloc
@@ -45,42 +47,53 @@ using namespace isc::datasrc::memory;
using namespace isc::testutils;
namespace {
-// Memory segment specified for tests. It normally behaves like a "local"
-// memory segment. If "throw count" is set to non 0 via setThrowCount(),
-// it continues the normal behavior up to the specified number of calls to
-// allocate(), and throws an exception at the next call.
-class TestMemorySegment : public isc::util::MemorySegmentLocal {
-public:
- TestMemorySegment() : throw_count_(0) {}
- virtual void* allocate(size_t size) {
- if (throw_count_ > 0) {
- if (--throw_count_ == 0) {
- throw std::bad_alloc();
- }
- }
- return (isc::util::MemorySegmentLocal::allocate(size));
- }
- void setThrowCount(size_t count) { throw_count_ = count; }
-private:
- size_t throw_count_;
+const char* rrset_data[] = {
+ "example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. "
+ "68 3600 300 3600000 3600",
+ "a.example.org. 3600 IN A 192.168.0.1\n" // RRset containing 2 RRs
+ "a.example.org. 3600 IN A 192.168.0.2",
+ "a.example.org. 3600 IN RRSIG A 5 3 3600 20150420235959 20051021000000 "
+ "40430 example.org. FAKEFAKE",
+ "a.example.org. 3600 IN MX 10 mail.example.org.",
+ "a.example.org. 3600 IN RRSIG MX 5 3 3600 20150420235959 20051021000000 "
+ "40430 example.org. FAKEFAKEFAKE",
+ NULL
};
-const char* rrset_data[] = {
- "example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 68 3600 300 3600000 3600",
- "a.example.org. 3600 IN A 192.168.0.1",
- "a.example.org. 3600 IN MX 10 mail.example.org.",
+// RRsets that emulate the "separate RRs" mode.
+const char* rrset_data_separated[] = {
+ "example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. "
+ "68 3600 300 3600000 3600",
+ "a.example.org. 3600 IN A 192.168.0.1", // these two belong to the same
+ "a.example.org. 3600 IN A 192.168.0.2", // RRset, but are separated.
+ NULL
+};
+
+// Similar to the previous one, but with separated RRSIGs
+const char* rrset_data_sigseparated[] = {
+ "example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. "
+ "68 3600 300 3600000 3600",
+ "a.example.org. 3600 IN A 192.168.0.1",
+ "a.example.org. 3600 IN RRSIG A 5 3 3600 20150420235959 20051021000000 "
+ "40430 example.org. FAKEFAKE",
+ "a.example.org. 3600 IN RRSIG A 5 3 3600 20150420235959 20051021000000 "
+ "53535 example.org. FAKEFAKE",
NULL
};
class MockIterator : public ZoneIterator {
private:
- MockIterator() :
- rrset_data_ptr_(rrset_data)
+ MockIterator(const char** rrset_data_ptr, bool pass_empty_rrsig) :
+ rrset_data_ptr_(rrset_data_ptr),
+ pass_empty_rrsig_(pass_empty_rrsig)
{
}
const char** rrset_data_ptr_;
+ // If true, emulate an unexpected bogus case where an RRSIG RRset is
+ // returned without the RDATA. For brevity allow tests tweak it directly.
+ bool pass_empty_rrsig_;
public:
virtual ConstRRsetPtr getNextRRset() {
@@ -88,9 +101,13 @@ public:
return (ConstRRsetPtr());
}
- RRsetPtr result(textToRRset(*rrset_data_ptr_,
- RRClass::IN(), Name("example.org")));
- rrset_data_ptr_++;
+ ConstRRsetPtr result(textToRRset(*rrset_data_ptr_,
+ RRClass::IN(), Name("example.org")));
+ if (pass_empty_rrsig_ && result->getType() == RRType::RRSIG()) {
+ result.reset(new RRset(result->getName(), result->getClass(),
+ result->getType(), result->getTTL()));
+ }
+ ++rrset_data_ptr_;
return (result);
}
@@ -99,8 +116,11 @@ public:
isc_throw(isc::NotImplemented, "Not implemented");
}
- static ZoneIteratorPtr makeIterator(void) {
- return (ZoneIteratorPtr(new MockIterator()));
+ static ZoneIteratorPtr makeIterator(const char** rrset_data_ptr,
+ bool pass_empty_rrsig = false)
+ {
+ return (ZoneIteratorPtr(new MockIterator(rrset_data_ptr,
+ pass_empty_rrsig)));
}
};
@@ -120,7 +140,7 @@ protected:
EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated()); // catch any leak here.
}
const RRClass zclass_;
- TestMemorySegment mem_sgmt_;
+ test::MemorySegmentTest mem_sgmt_;
InMemoryClient* client_;
};
@@ -184,7 +204,7 @@ TEST_F(MemoryClientTest, load) {
TEST_F(MemoryClientTest, loadFromIterator) {
client_->load(Name("example.org"),
- *MockIterator::makeIterator());
+ *MockIterator::makeIterator(rrset_data));
ZoneIteratorPtr iterator(client_->getIterator(Name("example.org")));
@@ -197,17 +217,38 @@ TEST_F(MemoryClientTest, loadFromIterator) {
rrset = iterator->getNextRRset();
EXPECT_TRUE(rrset);
EXPECT_EQ(RRType::MX(), rrset->getType());
+ EXPECT_EQ(1, rrset->getRRsigDataCount()); // this RRset is signed
// RRType::A() RRset
rrset = iterator->getNextRRset();
EXPECT_TRUE(rrset);
EXPECT_EQ(RRType::A(), rrset->getType());
+ EXPECT_EQ(1, rrset->getRRsigDataCount()); // also signed
// There's nothing else in this iterator
EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
// Iterating past the end should result in an exception
EXPECT_THROW(iterator->getNextRRset(), isc::Unexpected);
+
+ // Loading the zone with an iterator separating RRs of the same RRset
+ // will fail because the resulting sequence doesn't meet assumptions of
+ // the (current) in-memory implementation.
+ EXPECT_THROW(client_->load(Name("example.org"),
+ *MockIterator::makeIterator(
+ rrset_data_separated)),
+ InMemoryClient::AddError);
+
+ // Similar to the previous case, but with separated RRSIGs.
+ EXPECT_THROW(client_->load(Name("example.org"),
+ *MockIterator::makeIterator(
+ rrset_data_sigseparated)),
+ InMemoryClient::AddError);
+
+ // Emulating bogus iterator implementation that passes empty RRSIGs.
+ EXPECT_THROW(client_->load(Name("example.org"),
+ *MockIterator::makeIterator(rrset_data, true)),
+ isc::Unexpected);
}
TEST_F(MemoryClientTest, loadMemoryAllocationFailures) {
@@ -465,6 +506,8 @@ TEST_F(MemoryClientTest, loadDNAMEAndNSNonApex2) {
}
TEST_F(MemoryClientTest, loadRRSIGFollowsNothing) {
+ // This causes the situation where an RRSIG is added without a covered
+ // RRset. Such cases are currently rejected.
EXPECT_THROW(client_->load(Name("example.org"),
TEST_DATA_DIR
"/example.org-rrsig-follows-nothing.zone"),
@@ -472,22 +515,6 @@ TEST_F(MemoryClientTest, loadRRSIGFollowsNothing) {
// Teardown checks for memory segment leaks
}
-TEST_F(MemoryClientTest, loadRRSIGNameUnmatched) {
- EXPECT_THROW(client_->load(Name("example.org"),
- TEST_DATA_DIR
- "/example.org-rrsig-name-unmatched.zone"),
- InMemoryClient::AddError);
- // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadRRSIGTypeUnmatched) {
- EXPECT_THROW(client_->load(Name("example.org"),
- TEST_DATA_DIR
- "/example.org-rrsig-type-unmatched.zone"),
- InMemoryClient::AddError);
- // Teardown checks for memory segment leaks
-}
-
TEST_F(MemoryClientTest, loadRRSIGs) {
client_->load(Name("example.org"),
TEST_DATA_DIR "/example.org-rrsigs.zone");
diff --git a/src/lib/datasrc/memory/tests/testdata/Makefile.am b/src/lib/datasrc/memory/tests/testdata/Makefile.am
index 9fb9986..2d92266 100644
--- a/src/lib/datasrc/memory/tests/testdata/Makefile.am
+++ b/src/lib/datasrc/memory/tests/testdata/Makefile.am
@@ -24,8 +24,6 @@ EXTRA_DIST += example.org-nsec3-signed-no-param.zone
EXTRA_DIST += example.org-nsec3-signed.zone
EXTRA_DIST += example.org-out-of-zone.zone
EXTRA_DIST += example.org-rrsig-follows-nothing.zone
-EXTRA_DIST += example.org-rrsig-name-unmatched.zone
-EXTRA_DIST += example.org-rrsig-type-unmatched.zone
EXTRA_DIST += example.org-rrsigs.zone
EXTRA_DIST += example.org-wildcard-dname.zone
EXTRA_DIST += example.org-wildcard-ns.zone
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-rrsig-name-unmatched.zone b/src/lib/datasrc/memory/tests/testdata/example.org-rrsig-name-unmatched.zone
deleted file mode 100644
index dc1d728..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-rrsig-name-unmatched.zone
+++ /dev/null
@@ -1,6 +0,0 @@
-;; test zone file used for ZoneFinderContext tests.
-;; RRSIGs are (obviouslly) faked ones for testing.
-
-example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 70 3600 300 3600000 3600
-ns1.example.org. 3600 IN A 192.0.2.1
-ns2.example.org. 3600 IN RRSIG A 7 3 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKE
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-rrsig-type-unmatched.zone b/src/lib/datasrc/memory/tests/testdata/example.org-rrsig-type-unmatched.zone
deleted file mode 100644
index 300e124..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-rrsig-type-unmatched.zone
+++ /dev/null
@@ -1,6 +0,0 @@
-;; test zone file used for ZoneFinderContext tests.
-;; RRSIGs are (obviouslly) faked ones for testing.
-
-example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 72 3600 300 3600000 3600
-ns1.example.org. 3600 IN AAAA 2001:db8::1
-ns1.example.org. 3600 IN RRSIG A 7 3 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKE
diff --git a/src/lib/datasrc/memory/zone_finder.cc b/src/lib/datasrc/memory/zone_finder.cc
index 75d3187..cb56b8f 100644
--- a/src/lib/datasrc/memory/zone_finder.cc
+++ b/src/lib/datasrc/memory/zone_finder.cc
@@ -561,10 +561,6 @@ InMemoryZoneFinder::find_internal(const isc::dns::Name& name,
wild, &name));
}
- const RdataSet* currds = node->getData();
- while (currds != NULL) {
- currds = currds->getNext();
- }
found = RdataSet::find(node->getData(), type);
if (found != NULL) {
// Good, it is here
diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am
index eba7e81..9e00ab0 100644
--- a/src/lib/dhcp/tests/Makefile.am
+++ b/src/lib/dhcp/tests/Makefile.am
@@ -26,19 +26,15 @@ TESTS =
if HAVE_GTEST
TESTS += libdhcp++_unittests
libdhcp___unittests_SOURCES = run_unittests.cc
-libdhcp___unittests_SOURCES += ../libdhcp++.h ../libdhcp++.cc
libdhcp___unittests_SOURCES += libdhcp++_unittest.cc
-libdhcp___unittests_SOURCES += ../iface_mgr.cc ../iface_mgr.h iface_mgr_unittest.cc
-libdhcp___unittests_SOURCES += ../iface_mgr_linux.cc
-libdhcp___unittests_SOURCES += ../iface_mgr_bsd.cc
-libdhcp___unittests_SOURCES += ../iface_mgr_sun.cc
-libdhcp___unittests_SOURCES += ../option6_iaaddr.h ../option6_iaaddr.cc option6_iaaddr_unittest.cc
-libdhcp___unittests_SOURCES += ../option6_ia.h ../option6_ia.cc option6_ia_unittest.cc
-libdhcp___unittests_SOURCES += ../option6_addrlst.h ../option6_addrlst.cc option6_addrlst_unittest.cc
-libdhcp___unittests_SOURCES += ../option4_addrlst.cc ../option4_addrlst.h option4_addrlst_unittest.cc
-libdhcp___unittests_SOURCES += ../option.h ../option.cc option_unittest.cc
-libdhcp___unittests_SOURCES += ../pkt6.h ../pkt6.cc pkt6_unittest.cc
-libdhcp___unittests_SOURCES += ../pkt4.h ../pkt4.cc pkt4_unittest.cc
+libdhcp___unittests_SOURCES += iface_mgr_unittest.cc
+libdhcp___unittests_SOURCES += option6_iaaddr_unittest.cc
+libdhcp___unittests_SOURCES += option6_ia_unittest.cc
+libdhcp___unittests_SOURCES += option6_addrlst_unittest.cc
+libdhcp___unittests_SOURCES += option4_addrlst_unittest.cc
+libdhcp___unittests_SOURCES += option_unittest.cc
+libdhcp___unittests_SOURCES += pkt6_unittest.cc
+libdhcp___unittests_SOURCES += pkt4_unittest.cc
libdhcp___unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
libdhcp___unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
@@ -51,11 +47,12 @@ if USE_CLANGPP
# Boost headers.
libdhcp___unittests_CXXFLAGS += -Wno-unused-variable -Wno-unused-parameter
endif
-libdhcp___unittests_LDADD = $(GTEST_LDADD)
+libdhcp___unittests_LDADD = $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
libdhcp___unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
libdhcp___unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
libdhcp___unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
libdhcp___unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+libdhcp___unittests_LDADD += $(GTEST_LDADD)
endif
noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/dns/message.cc b/src/lib/dns/message.cc
index 1cb1ea8..dba0e7b 100644
--- a/src/lib/dns/message.cc
+++ b/src/lib/dns/message.cc
@@ -254,6 +254,13 @@ MessageImpl::toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx) {
const size_t orig_msg_len_limit = renderer.getLengthLimit();
const AbstractMessageRenderer::CompressMode orig_compress_mode =
renderer.getCompressMode();
+
+ // We are going to skip soon, so we need to clear the renderer
+ // But we'll leave the length limit and the compress mode intact
+ // (or shortened in case of TSIG)
+ renderer.clear();
+ renderer.setCompressMode(orig_compress_mode);
+
if (tsig_len > 0) {
if (tsig_len > orig_msg_len_limit) {
isc_throw(InvalidParameter, "Failed to render DNS message: "
@@ -261,6 +268,8 @@ MessageImpl::toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx) {
orig_msg_len_limit << ")");
}
renderer.setLengthLimit(orig_msg_len_limit - tsig_len);
+ } else {
+ renderer.setLengthLimit(orig_msg_len_limit);
}
// reserve room for the header
diff --git a/src/lib/dns/message.h b/src/lib/dns/message.h
index 3b80357..85754ac 100644
--- a/src/lib/dns/message.h
+++ b/src/lib/dns/message.h
@@ -556,6 +556,10 @@ public:
/// \c Rcode must have been set beforehand; otherwise, an exception of
/// class \c InvalidMessageOperation will be thrown.
///
+ /// \note The renderer's internal buffers and data are automatically
+ /// cleared, keeping the length limit and the compression mode intact.
+ /// In case truncation is triggered, the renderer is cleared completely.
+ ///
/// \param renderer DNS message rendering context that encapsulates the
/// output buffer and name compression information.
void toWire(AbstractMessageRenderer& renderer);
@@ -581,6 +585,10 @@ public:
/// it should mean a bug either in the TSIG context or in the renderer
/// implementation.
///
+ /// \note The renderer's internal buffers and data are automatically
+ /// cleared, keeping the length limit and the compression mode intact.
+ /// In case truncation is triggered, the renderer is cleared completely.
+ ///
/// \param renderer See the other version
/// \param tsig_ctx A TSIG context that is to be used for signing the
/// message
diff --git a/src/lib/dns/python/tests/message_python_test.py b/src/lib/dns/python/tests/message_python_test.py
index 6f32b11..b9c0d5c 100644
--- a/src/lib/dns/python/tests/message_python_test.py
+++ b/src/lib/dns/python/tests/message_python_test.py
@@ -453,7 +453,7 @@ class MessageTest(unittest.TestCase):
def test_to_text(self):
message_render = create_message()
-
+
msg_str =\
""";; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 4149
;; flags: qr aa rd; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0
@@ -484,7 +484,7 @@ test.example.com. 3600 IN A 192.0.2.2
Message.from_wire, self.p, bytes())
test_name = Name("test.example.com");
-
+
message_parse = Message(0)
factoryFromFile(message_parse, "message_fromWire1")
self.assertEqual(0x1035, message_parse.get_qid())
@@ -493,7 +493,7 @@ test.example.com. 3600 IN A 192.0.2.2
self.assertTrue(message_parse.get_header_flag(Message.HEADERFLAG_QR))
self.assertTrue(message_parse.get_header_flag(Message.HEADERFLAG_RD))
self.assertTrue(message_parse.get_header_flag(Message.HEADERFLAG_AA))
-
+
#QuestionPtr q = *message_parse.beginQuestion()
q = message_parse.get_question()[0]
self.assertEqual(test_name, q.get_name())
@@ -503,7 +503,7 @@ test.example.com. 3600 IN A 192.0.2.2
self.assertEqual(2, message_parse.get_rr_count(Message.SECTION_ANSWER))
self.assertEqual(0, message_parse.get_rr_count(Message.SECTION_AUTHORITY))
self.assertEqual(0, message_parse.get_rr_count(Message.SECTION_ADDITIONAL))
-
+
#RRsetPtr rrset = *message_parse.beginSection(Message.SECTION_ANSWER)
rrset = message_parse.get_section(Message.SECTION_ANSWER)[0]
self.assertEqual(test_name, rrset.get_name())
@@ -569,12 +569,12 @@ test.example.com. 3600 IN A 192.0.2.2
message_parse = Message(Message.PARSE)
factoryFromFile(message_parse, "message_fromWire10.wire")
self.assertEqual(Rcode.BADVERS(), message_parse.get_rcode())
-
+
# Maximum extended Rcode
message_parse.clear(Message.PARSE)
factoryFromFile(message_parse, "message_fromWire11.wire")
self.assertEqual(0xfff, message_parse.get_rcode().get_code())
-
+
def test_BadEDNS0(self):
message_parse = Message(Message.PARSE)
# OPT RR in the answer section
@@ -596,7 +596,7 @@ test.example.com. 3600 IN A 192.0.2.2
factoryFromFile,
message_parse,
"message_fromWire6")
-
+
# Compressed owner name of OPT RR points to a root name.
# Not necessarily bogus, but very unusual and mostly pathological.
# We accept it, but is it okay?
diff --git a/src/lib/dns/python/tests/tsig_python_test.py b/src/lib/dns/python/tests/tsig_python_test.py
index 7e5515d..4d99175 100644
--- a/src/lib/dns/python/tests/tsig_python_test.py
+++ b/src/lib/dns/python/tests/tsig_python_test.py
@@ -122,15 +122,23 @@ class TSIGContextTest(unittest.TestCase):
# And there should be no error code.
self.assertEqual(TSIGError(Rcode.NOERROR()), self.tsig_ctx.get_error())
+ # No message signed yet
+ self.assertRaises(TSIGContextError, self.tsig_ctx.last_had_signature)
+
# Note: intentionally use camelCase so that we can easily copy-paste
# corresponding C++ tests.
def commonVerifyChecks(self, ctx, record, data, expected_error,
expected_new_state=\
- TSIGContext.STATE_VERIFIED_RESPONSE):
+ TSIGContext.STATE_VERIFIED_RESPONSE,
+ last_should_throw=False):
self.assertEqual(expected_error, ctx.verify(record, data))
self.assertEqual(expected_error, ctx.get_error())
self.assertEqual(expected_new_state, ctx.get_state())
-
+ if last_should_throw:
+ self.assertRaises(TSIGContextError, ctx.last_had_signature)
+ else:
+ self.assertEqual(record is not None,
+ ctx.last_had_signature())
def test_from_keyring(self):
# Construct a TSIG context with an empty key ring. Key shouldn't be
# found, and the BAD_KEY error should be recorded.
@@ -354,7 +362,7 @@ class TSIGContextTest(unittest.TestCase):
tsig = self.createMessageAndSign(self.qid, self.test_name,
self.tsig_ctx, 0, RRType.SOA())
-
+
fix_current_time(0x4da8b9d6 + 301)
self.assertEqual(TSIGError.BAD_TIME,
self.tsig_verify_ctx.verify(tsig, DUMMY_DATA))
@@ -454,7 +462,8 @@ class TSIGContextTest(unittest.TestCase):
self.createMessageAndSign(self.qid, self.test_name, self.tsig_ctx)
self.commonVerifyChecks(self.tsig_ctx, None, DUMMY_DATA,
- TSIGError.FORMERR, TSIGContext.STATE_SENT_REQUEST)
+ TSIGError.FORMERR, TSIGContext.STATE_SENT_REQUEST,
+ True)
self.createMessageFromFile("tsig_verify5.wire")
self.commonVerifyChecks(self.tsig_ctx, self.message.get_tsig_record(),
diff --git a/src/lib/dns/python/tsig_python.cc b/src/lib/dns/python/tsig_python.cc
index 0764e33..abb7733 100644
--- a/src/lib/dns/python/tsig_python.cc
+++ b/src/lib/dns/python/tsig_python.cc
@@ -66,6 +66,7 @@ PyObject* TSIGContext_getState(s_TSIGContext* self);
PyObject* TSIGContext_getError(s_TSIGContext* self);
PyObject* TSIGContext_sign(s_TSIGContext* self, PyObject* args);
PyObject* TSIGContext_verify(s_TSIGContext* self, PyObject* args);
+PyObject* TSIGContext_lastHadSignature(s_TSIGContext* self);
// These are the functions we export
// For a minimal support, we don't need them.
@@ -89,6 +90,9 @@ PyMethodDef TSIGContext_methods[] = {
{ "verify",
reinterpret_cast<PyCFunction>(TSIGContext_verify), METH_VARARGS,
"Verify a DNS message." },
+ { "last_had_signature",
+ reinterpret_cast<PyCFunction>(TSIGContext_lastHadSignature), METH_NOARGS,
+ "Return True if the last verified message contained a signature" },
{ NULL, NULL, 0, NULL }
};
@@ -234,6 +238,26 @@ TSIGContext_verify(s_TSIGContext* self, PyObject* args) {
return (NULL);
}
+
+PyObject*
+TSIGContext_lastHadSignature(s_TSIGContext* self) {
+ try {
+ long result = self->cppobj->lastHadSignature();
+ return (PyBool_FromLong(result));
+ } catch (const TSIGContextError& ex) {
+ PyErr_SetString(po_TSIGContextError, ex.what());
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Unexpected failure in TSIG lastHadSignature: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError,
+ "Unexpected failure in TSIG lastHadSignature");
+ }
+
+ return (NULL);
+}
} // end of unnamed namespace
namespace isc {
diff --git a/src/lib/dns/tests/message_unittest.cc b/src/lib/dns/tests/message_unittest.cc
index 33e677f..835aa48 100644
--- a/src/lib/dns/tests/message_unittest.cc
+++ b/src/lib/dns/tests/message_unittest.cc
@@ -534,7 +534,7 @@ TEST_F(MessageTest, appendSection) {
RRClass::IN(), RRType::A()));
EXPECT_TRUE(target.hasRRset(Message::SECTION_ANSWER, test_name,
RRClass::IN(), RRType::AAAA()));
-
+
}
TEST_F(MessageTest, parseHeader) {
@@ -1091,7 +1091,7 @@ TEST_F(MessageTest, toWireWithoutRcode) {
TEST_F(MessageTest, toText) {
// Check toText() output for a typical DNS response with records in
// all sections
-
+
factoryFromFile(message_parse, "message_toText1.wire");
{
SCOPED_TRACE("Message toText test (basic case)");
diff --git a/src/lib/dns/tests/tsig_unittest.cc b/src/lib/dns/tests/tsig_unittest.cc
index ac503e5..458a6e0 100644
--- a/src/lib/dns/tests/tsig_unittest.cc
+++ b/src/lib/dns/tests/tsig_unittest.cc
@@ -66,6 +66,22 @@ testGetTime() {
return (NOW);
}
+// Thin wrapper around TSIGContext to allow access to the
+// update method.
+class TestTSIGContext : public TSIGContext {
+public:
+ TestTSIGContext(const TSIGKey& key) :
+ TSIGContext(key)
+ {}
+ TestTSIGContext(const Name& key_name, const Name& algorithm_name,
+ const TSIGKeyRing& keyring) :
+ TSIGContext(key_name, algorithm_name, keyring)
+ {}
+ void update(const void* const data, size_t len) {
+ TSIGContext::update(data, len);
+ }
+};
+
class TSIGTest : public ::testing::Test {
protected:
TSIGTest() :
@@ -83,9 +99,10 @@ protected:
isc::util::detail::gettimeFunction = NULL;
decodeBase64("SFuWd/q99SzF8Yzd1QbB9g==", secret);
- tsig_ctx.reset(new TSIGContext(TSIGKey(test_name,
- TSIGKey::HMACMD5_NAME(),
- &secret[0], secret.size())));
+ tsig_ctx.reset(new TestTSIGContext(TSIGKey(test_name,
+ TSIGKey::HMACMD5_NAME(),
+ &secret[0],
+ secret.size())));
tsig_verify_ctx.reset(new TSIGContext(TSIGKey(test_name,
TSIGKey::HMACMD5_NAME(),
&secret[0],
@@ -116,7 +133,7 @@ protected:
static const unsigned int AA_FLAG = 0x2;
static const unsigned int RD_FLAG = 0x4;
- boost::scoped_ptr<TSIGContext> tsig_ctx;
+ boost::scoped_ptr<TestTSIGContext> tsig_ctx;
boost::scoped_ptr<TSIGContext> tsig_verify_ctx;
TSIGKeyRing keyring;
const uint16_t qid;
@@ -166,16 +183,20 @@ TSIGTest::createMessageAndSign(uint16_t id, const Name& qname,
message.addRRset(Message::SECTION_ANSWER, answer_rrset);
}
renderer.clear();
- message.toWire(renderer);
TSIGContext::State expected_new_state =
(ctx->getState() == TSIGContext::INIT) ?
TSIGContext::SENT_REQUEST : TSIGContext::SENT_RESPONSE;
- ConstTSIGRecordPtr tsig = ctx->sign(id, renderer.getData(),
- renderer.getLength());
+
+ message.toWire(renderer, *ctx);
+
+ message.clear(Message::PARSE);
+ InputBuffer buffer(renderer.getData(), renderer.getLength());
+ message.fromWire(buffer);
+
EXPECT_EQ(expected_new_state, ctx->getState());
- return (tsig);
+ return (ConstTSIGRecordPtr(new TSIGRecord(*message.getTSIGRecord())));
}
void
@@ -218,11 +239,17 @@ void
commonVerifyChecks(TSIGContext& ctx, const TSIGRecord* record,
const void* data, size_t data_len, TSIGError expected_error,
TSIGContext::State expected_new_state =
- TSIGContext::VERIFIED_RESPONSE)
+ TSIGContext::VERIFIED_RESPONSE,
+ bool last_should_throw = false)
{
EXPECT_EQ(expected_error, ctx.verify(record, data, data_len));
EXPECT_EQ(expected_error, ctx.getError());
EXPECT_EQ(expected_new_state, ctx.getState());
+ if (last_should_throw) {
+ EXPECT_THROW(ctx.lastHadSignature(), TSIGContextError);
+ } else {
+ EXPECT_EQ(record != NULL, ctx.lastHadSignature());
+ }
}
TEST_F(TSIGTest, initialState) {
@@ -231,6 +258,9 @@ TEST_F(TSIGTest, initialState) {
// And there should be no error code.
EXPECT_EQ(TSIGError(Rcode::NOERROR()), tsig_ctx->getError());
+
+ // Nothing verified yet
+ EXPECT_THROW(tsig_ctx->lastHadSignature(), TSIGContextError);
}
TEST_F(TSIGTest, constructFromKeyRing) {
@@ -354,10 +384,17 @@ TEST_F(TSIGTest, verifyBadData) {
12 + dummy_record.getLength() - 1),
InvalidParameter);
+ // Still nothing verified
+ EXPECT_THROW(tsig_ctx->lastHadSignature(), TSIGContextError);
+
// And the data must not be NULL.
EXPECT_THROW(tsig_ctx->verify(&dummy_record, NULL,
12 + dummy_record.getLength()),
InvalidParameter);
+
+ // Still nothing verified
+ EXPECT_THROW(tsig_ctx->lastHadSignature(), TSIGContextError);
+
}
#ifdef ENABLE_CUSTOM_OPERATOR_NEW
@@ -726,8 +763,8 @@ TEST_F(TSIGTest, badsigResponse) {
TEST_F(TSIGTest, badkeyResponse) {
// A similar test as badsigResponse but for BADKEY
isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>;
- tsig_ctx.reset(new TSIGContext(badkey_name, TSIGKey::HMACMD5_NAME(),
- keyring));
+ tsig_ctx.reset(new TestTSIGContext(badkey_name, TSIGKey::HMACMD5_NAME(),
+ keyring));
{
SCOPED_TRACE("Verify resulting in BADKEY");
commonVerifyChecks(*tsig_ctx, &dummy_record, &dummy_data[0],
@@ -806,7 +843,7 @@ TEST_F(TSIGTest, nosigThenValidate) {
SCOPED_TRACE("Verify a response without TSIG that should exist");
commonVerifyChecks(*tsig_ctx, NULL, &dummy_data[0],
dummy_data.size(), TSIGError::FORMERR(),
- TSIGContext::SENT_REQUEST);
+ TSIGContext::SENT_REQUEST, true);
}
createMessageFromFile("tsig_verify5.wire");
@@ -936,45 +973,47 @@ TEST_F(TSIGTest, getTSIGLength) {
EXPECT_EQ(85, tsig_ctx->getTSIGLength());
// hmac-sha1: n2=11, x=20
- tsig_ctx.reset(new TSIGContext(TSIGKey(test_name, TSIGKey::HMACSHA1_NAME(),
- &dummy_data[0], 20)));
+ tsig_ctx.reset(new TestTSIGContext(TSIGKey(test_name,
+ TSIGKey::HMACSHA1_NAME(),
+ &dummy_data[0], 20)));
EXPECT_EQ(74, tsig_ctx->getTSIGLength());
// hmac-sha256: n2=13, x=32
- tsig_ctx.reset(new TSIGContext(TSIGKey(test_name,
- TSIGKey::HMACSHA256_NAME(),
- &dummy_data[0], 32)));
+ tsig_ctx.reset(new TestTSIGContext(TSIGKey(test_name,
+ TSIGKey::HMACSHA256_NAME(),
+ &dummy_data[0], 32)));
EXPECT_EQ(88, tsig_ctx->getTSIGLength());
// hmac-sha224: n2=13, x=28
- tsig_ctx.reset(new TSIGContext(TSIGKey(test_name,
- TSIGKey::HMACSHA224_NAME(),
- &dummy_data[0], 28)));
+ tsig_ctx.reset(new TestTSIGContext(TSIGKey(test_name,
+ TSIGKey::HMACSHA224_NAME(),
+ &dummy_data[0], 28)));
EXPECT_EQ(84, tsig_ctx->getTSIGLength());
// hmac-sha384: n2=13, x=48
- tsig_ctx.reset(new TSIGContext(TSIGKey(test_name,
- TSIGKey::HMACSHA384_NAME(),
- &dummy_data[0], 48)));
+ tsig_ctx.reset(new TestTSIGContext(TSIGKey(test_name,
+ TSIGKey::HMACSHA384_NAME(),
+ &dummy_data[0], 48)));
EXPECT_EQ(104, tsig_ctx->getTSIGLength());
// hmac-sha512: n2=13, x=64
- tsig_ctx.reset(new TSIGContext(TSIGKey(test_name,
- TSIGKey::HMACSHA512_NAME(),
- &dummy_data[0], 64)));
+ tsig_ctx.reset(new TestTSIGContext(TSIGKey(test_name,
+ TSIGKey::HMACSHA512_NAME(),
+ &dummy_data[0], 64)));
EXPECT_EQ(120, tsig_ctx->getTSIGLength());
// bad key case: n1=len(badkey.example.com)=20, n2=26, x=0
- tsig_ctx.reset(new TSIGContext(badkey_name, TSIGKey::HMACMD5_NAME(),
- keyring));
+ tsig_ctx.reset(new TestTSIGContext(badkey_name, TSIGKey::HMACMD5_NAME(),
+ keyring));
EXPECT_EQ(72, tsig_ctx->getTSIGLength());
// bad sig case: n1=17, n2=26, x=0
isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>;
createMessageFromFile("message_toWire2.wire");
- tsig_ctx.reset(new TSIGContext(TSIGKey(test_name, TSIGKey::HMACMD5_NAME(),
- &dummy_data[0],
- dummy_data.size())));
+ tsig_ctx.reset(new TestTSIGContext(TSIGKey(test_name,
+ TSIGKey::HMACMD5_NAME(),
+ &dummy_data[0],
+ dummy_data.size())));
{
SCOPED_TRACE("Verify resulting in BADSIG");
commonVerifyChecks(*tsig_ctx, message.getTSIGRecord(),
@@ -985,9 +1024,10 @@ TEST_F(TSIGTest, getTSIGLength) {
// bad time case: n1=17, n2=26, x=16, y=6
isc::util::detail::gettimeFunction = testGetTime<0x4da8877a - 1000>;
- tsig_ctx.reset(new TSIGContext(TSIGKey(test_name, TSIGKey::HMACMD5_NAME(),
- &dummy_data[0],
- dummy_data.size())));
+ tsig_ctx.reset(new TestTSIGContext(TSIGKey(test_name,
+ TSIGKey::HMACMD5_NAME(),
+ &dummy_data[0],
+ dummy_data.size())));
{
SCOPED_TRACE("Verify resulting in BADTIME");
commonVerifyChecks(*tsig_ctx, message.getTSIGRecord(),
@@ -998,4 +1038,114 @@ TEST_F(TSIGTest, getTSIGLength) {
EXPECT_EQ(91, tsig_ctx->getTSIGLength());
}
+// Verify a stream of multiple messages. Some of them have a signature omitted.
+//
+// We have two contexts, one that signs, another that verifies.
+TEST_F(TSIGTest, verifyMulti) {
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>;
+
+ // First, send query from the verify one to the normal one, so
+ // we initialize something like AXFR
+ {
+ SCOPED_TRACE("Query");
+ ConstTSIGRecordPtr tsig = createMessageAndSign(1234, test_name,
+ tsig_verify_ctx.get());
+ commonVerifyChecks(*tsig_ctx, tsig.get(),
+ renderer.getData(), renderer.getLength(),
+ TSIGError(Rcode::NOERROR()),
+ TSIGContext::RECEIVED_REQUEST);
+ }
+
+ {
+ SCOPED_TRACE("First message");
+ ConstTSIGRecordPtr tsig = createMessageAndSign(1234, test_name,
+ tsig_ctx.get());
+ commonVerifyChecks(*tsig_verify_ctx, tsig.get(),
+ renderer.getData(), renderer.getLength(),
+ TSIGError(Rcode::NOERROR()),
+ TSIGContext::VERIFIED_RESPONSE);
+ EXPECT_TRUE(tsig_verify_ctx->lastHadSignature());
+ }
+
+ {
+ SCOPED_TRACE("Second message");
+ ConstTSIGRecordPtr tsig = createMessageAndSign(1234, test_name,
+ tsig_ctx.get());
+ commonVerifyChecks(*tsig_verify_ctx, tsig.get(),
+ renderer.getData(), renderer.getLength(),
+ TSIGError(Rcode::NOERROR()),
+ TSIGContext::VERIFIED_RESPONSE);
+ EXPECT_TRUE(tsig_verify_ctx->lastHadSignature());
+ }
+
+ {
+ SCOPED_TRACE("Third message. Unsigned.");
+ // Another message does not carry the TSIG on it. But it should
+ // be OK, it's in the middle of stream.
+ message.clear(Message::RENDER);
+ message.setQid(1234);
+ message.setOpcode(Opcode::QUERY());
+ message.setRcode(Rcode::NOERROR());
+ RRsetPtr answer_rrset(new RRset(test_name, test_class, RRType::A(),
+ test_ttl));
+ answer_rrset->addRdata(createRdata(RRType::A(), test_class,
+ "192.0.2.1"));
+ message.addRRset(Message::SECTION_ANSWER, answer_rrset);
+ message.toWire(renderer);
+ // Update the internal state. We abuse the knowledge of
+ // internals here a little bit to generate correct test data
+ tsig_ctx->update(renderer.getData(), renderer.getLength());
+
+ commonVerifyChecks(*tsig_verify_ctx, NULL,
+ renderer.getData(), renderer.getLength(),
+ TSIGError(Rcode::NOERROR()),
+ TSIGContext::VERIFIED_RESPONSE);
+
+ EXPECT_FALSE(tsig_verify_ctx->lastHadSignature());
+ }
+
+ {
+ SCOPED_TRACE("Fourth message. Signed again.");
+ ConstTSIGRecordPtr tsig = createMessageAndSign(1234, test_name,
+ tsig_ctx.get());
+ commonVerifyChecks(*tsig_verify_ctx, tsig.get(),
+ renderer.getData(), renderer.getLength(),
+ TSIGError(Rcode::NOERROR()),
+ TSIGContext::VERIFIED_RESPONSE);
+ EXPECT_TRUE(tsig_verify_ctx->lastHadSignature());
+ }
+
+ {
+ SCOPED_TRACE("Filling in bunch of unsigned messages");
+ for (size_t i = 0; i < 100; ++i) {
+ SCOPED_TRACE(i);
+ // Another message does not carry the TSIG on it. But it should
+ // be OK, it's in the middle of stream.
+ message.clear(Message::RENDER);
+ message.setQid(1234);
+ message.setOpcode(Opcode::QUERY());
+ message.setRcode(Rcode::NOERROR());
+ RRsetPtr answer_rrset(new RRset(test_name, test_class, RRType::A(),
+ test_ttl));
+ answer_rrset->addRdata(createRdata(RRType::A(), test_class,
+ "192.0.2.1"));
+ message.addRRset(Message::SECTION_ANSWER, answer_rrset);
+ message.toWire(renderer);
+ // Update the internal state. We abuse the knowledge of
+ // internals here a little bit to generate correct test data
+ tsig_ctx->update(renderer.getData(), renderer.getLength());
+
+ // 99 unsigned messages is OK. But the 100th must be signed, according
+ // to the RFC2845, section 4.4
+ commonVerifyChecks(*tsig_verify_ctx, NULL,
+ renderer.getData(), renderer.getLength(),
+ i == 99 ? TSIGError::FORMERR() :
+ TSIGError(Rcode::NOERROR()),
+ TSIGContext::VERIFIED_RESPONSE);
+
+ EXPECT_FALSE(tsig_verify_ctx->lastHadSignature());
+ }
+ }
+}
+
} // end namespace
diff --git a/src/lib/dns/tsig.cc b/src/lib/dns/tsig.cc
index 1bda021..d7ffcf8 100644
--- a/src/lib/dns/tsig.cc
+++ b/src/lib/dns/tsig.cc
@@ -61,7 +61,8 @@ struct TSIGContext::TSIGContextImpl {
TSIGContextImpl(const TSIGKey& key,
TSIGError error = TSIGError::NOERROR()) :
state_(INIT), key_(key), error_(error),
- previous_timesigned_(0), digest_len_(0)
+ previous_timesigned_(0), digest_len_(0),
+ last_sig_dist_(-1)
{
if (error == TSIGError::NOERROR()) {
// In normal (NOERROR) case, the key should be valid, and we
@@ -137,7 +138,7 @@ struct TSIGContext::TSIGContextImpl {
// performance bottleneck, we could have this class a buffer as a member
// variable and reuse it throughout the object's lifetime. Right now,
// we prefer keeping the scope for local things as small as possible.
- void digestPreviousMAC(HMACPtr hmac) const;
+ void digestPreviousMAC(HMACPtr hmac);
void digestTSIGVariables(HMACPtr hmac, uint16_t rrclass, uint32_t rrttl,
uint64_t time_signed, uint16_t fudge,
uint16_t error, uint16_t otherlen,
@@ -152,14 +153,25 @@ struct TSIGContext::TSIGContextImpl {
uint64_t previous_timesigned_; // only meaningful for response with BADTIME
size_t digest_len_;
HMACPtr hmac_;
+ // This is the distance from the last verified signed message. Value of 0
+ // means the last message was signed. Special value -1 means there was no
+ // signed message yet.
+ int last_sig_dist_;
};
void
-TSIGContext::TSIGContextImpl::digestPreviousMAC(HMACPtr hmac) const {
+TSIGContext::TSIGContextImpl::digestPreviousMAC(HMACPtr hmac) {
// We should have ensured the digest size fits 16 bits within this class
// implementation.
assert(previous_digest_.size() <= 0xffff);
+ if (previous_digest_.empty()) {
+ // The previous digest was already used. We're in the middle of
+ // TCP stream somewhere and we already pushed some unsigned message
+ // into the HMAC state.
+ return;
+ }
+
OutputBuffer buffer(sizeof(uint16_t) + previous_digest_.size());
const uint16_t previous_digest_len(previous_digest_.size());
buffer.writeUint16(previous_digest_len);
@@ -414,11 +426,21 @@ TSIGContext::verify(const TSIGRecord* const record, const void* const data,
"TSIG verify attempt after sending a response");
}
- // This case happens when we sent a signed request and have received an
- // unsigned response. According to RFC2845 Section 4.6 this case should be
- // considered a "format error" (although the specific error code
- // wouldn't matter much for the caller).
if (record == NULL) {
+ if (impl_->last_sig_dist_ >= 0 && impl_->last_sig_dist_ < 99) {
+ // It is not signed, but in the middle of TCP stream. We just
+ // update the HMAC state and consider this message OK.
+ update(data, data_len);
+ // This one is not signed, the last signed is one message further
+ // now.
+ impl_->last_sig_dist_++;
+ // No digest to return now. Just say it's OK.
+ return (impl_->postVerifyUpdate(TSIGError::NOERROR(), NULL, 0));
+ }
+ // This case happens when we sent a signed request and have received an
+ // unsigned response. According to RFC2845 Section 4.6 this case should be
+ // considered a "format error" (although the specific error code
+ // wouldn't matter much for the caller).
return (impl_->postVerifyUpdate(TSIGError::FORMERR(), NULL, 0));
}
@@ -433,6 +455,9 @@ TSIGContext::verify(const TSIGRecord* const record, const void* const data,
isc_throw(InvalidParameter, "TSIG verify: empty data is invalid");
}
+ // This message is signed and we won't throw any more.
+ impl_->last_sig_dist_ = 0;
+
// Check key: whether we first verify it with a known key or we verify
// it using the consistent key in the context. If the check fails we are
// done with BADKEY.
@@ -520,5 +545,24 @@ TSIGContext::verify(const TSIGRecord* const record, const void* const data,
return (impl_->postVerifyUpdate(TSIGError::BAD_SIG(), NULL, 0));
}
+bool
+TSIGContext::lastHadSignature() const {
+ if (impl_->last_sig_dist_ == -1) {
+ isc_throw(TSIGContextError, "No message was verified yet");
+ }
+ return (impl_->last_sig_dist_ == 0);
+}
+
+void
+TSIGContext::update(const void* const data, size_t len) {
+ HMACPtr hmac(impl_->createHMAC());
+ // Use the previous digest and never use it again
+ impl_->digestPreviousMAC(hmac);
+ impl_->previous_digest_.clear();
+ // Push the message there
+ hmac->update(data, len);
+ impl_->hmac_ = hmac;
+}
+
} // namespace dns
} // namespace isc
diff --git a/src/lib/dns/tsig.h b/src/lib/dns/tsig.h
index 028d295..9ccc580 100644
--- a/src/lib/dns/tsig.h
+++ b/src/lib/dns/tsig.h
@@ -339,7 +339,6 @@ public:
/// returns (without an exception being thrown), the internal state of
/// the \c TSIGContext won't be modified.
///
- /// \todo Support intermediate TCP DNS messages without TSIG (RFC2845 4.4)
/// \todo Signature truncation support based on RFC4635
///
/// \exception TSIGContextError Context already signed a response.
@@ -353,6 +352,19 @@ public:
TSIGError verify(const TSIGRecord* const record, const void* const data,
const size_t data_len);
+ /// \brief Check whether the last verified message was signed.
+ ///
+ /// RFC2845 allows for some of the messages not to be signed. However,
+ /// the last message must be signed and the class has no knowledge if a
+ /// given message is the last one, therefore it can't check directly.
+ ///
+ /// It is up to the caller to check if the last verified message was signed
+ /// after all are verified by calling this function.
+ ///
+ /// \return If the last message was signed or not.
+ /// \exception TSIGContextError if no message was verified yet.
+ bool lastHadSignature() const;
+
/// Return the expected length of TSIG RR after \c sign()
///
/// This method returns the length of the TSIG RR that would be
@@ -401,6 +413,19 @@ public:
static const uint16_t DEFAULT_FUDGE = 300;
//@}
+protected:
+ /// \brief Update internal HMAC state by more data.
+ ///
+ /// This is used mostly internaly, when we need to verify a message without
+ /// TSIG signature in the middle of signed TCP stream. However, it is also
+ /// used in tests, so it's protected instead of private, to allow tests
+ /// in.
+ ///
+ /// It doesn't contain sanity checks, and it is not tested directly. But
+ /// we may want to add these one day to allow generating the skipped TSIG
+ /// messages too. Until then, do not use this method.
+ void update(const void* const data, size_t len);
+
private:
struct TSIGContextImpl;
TSIGContextImpl* impl_;
diff --git a/src/lib/python/isc/testutils/tsigctx_mock.py b/src/lib/python/isc/testutils/tsigctx_mock.py
index a9af9b9..0158987 100644
--- a/src/lib/python/isc/testutils/tsigctx_mock.py
+++ b/src/lib/python/isc/testutils/tsigctx_mock.py
@@ -51,3 +51,6 @@ class MockTSIGContext(TSIGContext):
if hasattr(self.error, '__call__'):
return self.error(self)
return self.error
+
+ def last_has_signature(self):
+ return True
diff --git a/tests/lettuce/configurations/xfrin/retransfer_master.conf.orig b/tests/lettuce/configurations/xfrin/retransfer_master.conf.orig
index b0e3ac0..c04d917 100644
--- a/tests/lettuce/configurations/xfrin/retransfer_master.conf.orig
+++ b/tests/lettuce/configurations/xfrin/retransfer_master.conf.orig
@@ -33,6 +33,9 @@
"port": 47806
} ]
},
+ "Stats": {
+ "poll-interval": 1
+ },
"Boss": {
"components": {
"b10-auth": { "kind": "needed", "special": "auth" },
diff --git a/tests/lettuce/features/xfrin_notify_handling.feature b/tests/lettuce/features/xfrin_notify_handling.feature
index c50a926..785a53a 100644
--- a/tests/lettuce/features/xfrin_notify_handling.feature
+++ b/tests/lettuce/features/xfrin_notify_handling.feature
@@ -82,22 +82,7 @@ Feature: Xfrin incoming notify handling
Then wait for new master stderr message XFROUT_NOTIFY_COMMAND
Then wait for new bind10 stderr message AUTH_RECEIVED_NOTIFY
Then wait for new bind10 stderr message ZONEMGR_RECEIVE_NOTIFY
-
- When I query statistics soa_in_progress of bind10 module Xfrin with cmdctl
- Then the statistics counter soa_in_progress should be between 0 and 1
-
Then wait for new bind10 stderr message XFRIN_XFR_TRANSFER_STARTED
-
- When I query statistics axfr_deferred of bind10 module Xfrin with cmdctl
- Then the statistics counter axfr_deferred should be between 0 and 1
-
- When I query statistics axfr_running of bind10 module Xfrout with cmdctl port 47804
- # xfering might not be started yet.
- Then the statistics counter axfr_running should be between 0 and 1
-
- When I query statistics axfr_running of bind10 module Xfrin with cmdctl
- Then the statistics counter axfr_running should be between 0 and 1
-
Then wait for new bind10 stderr message XFRIN_TRANSFER_SUCCESS not XFRIN_XFR_PROCESS_FAILURE
Then wait for new bind10 stderr message ZONEMGR_RECEIVE_XFRIN_SUCCESS
Then wait 5 times for new master stderr message NOTIFY_OUT_SENDING_NOTIFY
@@ -110,9 +95,13 @@ Feature: Xfrin incoming notify handling
#
# check statistics change
#
+
+ # wait until the last stats requesting is finished
+ wait for new master stderr message STATS_SEND_STATISTICS_REQUEST
+ wait for new master stderr message XFROUT_RECEIVED_GETSTATS_COMMAND
+
When I query statistics zones of bind10 module Xfrout with cmdctl port 47804
last bindctl output should not contain "error"
- Then wait for new master stderr message XFROUT_RECEIVED_GETSTATS_COMMAND
Then the statistics counter notifyoutv4 for the zone _SERVER_ should be 0
Then the statistics counter notifyoutv4 for the zone example.org. should be 0
Then the statistics counter notifyoutv6 for the zone _SERVER_ should be 5
@@ -241,6 +230,11 @@ Feature: Xfrin incoming notify handling
#
# check statistics change
#
+
+ # wait until the last stats requesting is finished
+ wait for new master stderr message STATS_SEND_STATISTICS_REQUEST
+ wait for new master stderr message XFROUT_RECEIVED_GETSTATS_COMMAND
+
When I query statistics zones of bind10 module Xfrout with cmdctl port 47804
last bindctl output should not contain "error"
Then the statistics counter notifyoutv4 for the zone _SERVER_ should be 0
@@ -249,8 +243,8 @@ Feature: Xfrin incoming notify handling
Then the statistics counter notifyoutv6 for the zone example.org. should be 5
# The counts of rejection would be between 1 and 2. They are not
# fixed. It would depend on timing or the platform.
- Then the statistics counter xfrrej for the zone _SERVER_ should be between 1 and 2
- Then the statistics counter xfrrej for the zone example.org. should be between 1 and 2
+ Then the statistics counter xfrrej for the zone _SERVER_ should be greater than 0
+ Then the statistics counter xfrrej for the zone example.org. should be greater than 0
Then the statistics counter xfrreqdone for the zone _SERVER_ should be 0
Then the statistics counter xfrreqdone for the zone example.org. should be 0
diff --git a/tests/tools/perfdhcp/.gitignore b/tests/tools/perfdhcp/.gitignore
index 1a8375a..3660766 100644
--- a/tests/tools/perfdhcp/.gitignore
+++ b/tests/tools/perfdhcp/.gitignore
@@ -1 +1,2 @@
/perfdhcp
+/perfdhcp2
diff --git a/tests/tools/perfdhcp/command_options.cc b/tests/tools/perfdhcp/command_options.cc
index 24e7e79..91c5819 100644
--- a/tests/tools/perfdhcp/command_options.cc
+++ b/tests/tools/perfdhcp/command_options.cc
@@ -113,6 +113,15 @@ CommandOptions::parse(int argc, char** const argv) {
optind = 1;
#endif
+ // optreset is declared on BSD systems and is used to reset internal
+ // state of getopt(). When parsing command line arguments multiple
+ // times with getopt() the optreset must be set to 1 every time before
+ // parsing starts. Failing to do so will result in random behavior of
+ // getopt().
+#ifdef HAVE_OPTRESET
+ optreset = 1;
+#endif
+
opterr = 0;
// Reset values of class members
diff --git a/tests/tools/perfdhcp/test_control.h b/tests/tools/perfdhcp/test_control.h
index 1b0b83c..f89a7be 100644
--- a/tests/tools/perfdhcp/test_control.h
+++ b/tests/tools/perfdhcp/test_control.h
@@ -141,6 +141,10 @@ public:
/// (e.g. sequencial or based on random function).
class NumberGenerator {
public:
+
+ /// \brief Destructor.
+ virtual ~NumberGenerator() { }
+
/// \brief Generate number.
///
/// \return Generate number.
diff --git a/tests/tools/perfdhcp/tests/Makefile.am b/tests/tools/perfdhcp/tests/Makefile.am
index 73ec6ba..54602af 100644
--- a/tests/tools/perfdhcp/tests/Makefile.am
+++ b/tests/tools/perfdhcp/tests/Makefile.am
@@ -39,12 +39,12 @@ if USE_CLANGPP
run_unittests_CXXFLAGS = -Wno-unused-parameter
endif
-run_unittests_LDADD = $(GTEST_LDADD)
-run_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
+run_unittests_LDADD = $(top_builddir)/src/lib/util/libb10-util.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
run_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+run_unittests_LDADD += $(GTEST_LDADD)
endif
noinst_PROGRAMS = $(TESTS)
diff --git a/tests/tools/perfdhcp/tests/command_options_helper.h b/tests/tools/perfdhcp/tests/command_options_helper.h
index 860a040..2fc70ea 100644
--- a/tests/tools/perfdhcp/tests/command_options_helper.h
+++ b/tests/tools/perfdhcp/tests/command_options_helper.h
@@ -57,10 +57,10 @@ public:
~ArgvPtr() {
if (argv_ != NULL) {
for(int i = 0; i < argc_; ++i) {
- free(argv_[i]);
+ delete[] (argv_[i]);
argv_[i] = NULL;
}
- free(argv_);
+ delete[] (argv_);
}
}
@@ -102,6 +102,7 @@ private:
///
/// \param text_to_split string to be splited.
/// \param [out] num number of substrings returned.
+ /// \param std::bad_alloc if allocation of C-strings failed.
/// \return array of C-strings created from split.
static char** tokenizeString(const std::string& text_to_split, int& num) {
char** results = NULL;
@@ -115,14 +116,13 @@ private:
if (tokens.size() > 0) {
// Allocate array of C-strings where we will store tokens
- results = static_cast<char**>(malloc(tokens.size() * sizeof(char*)));
- if (results == NULL) {
- isc_throw(Unexpected, "unable to allocate array of c-strings");
- }
+ results = new char*[tokens.size()];
// Store tokens in C-strings array
for (int i = 0; i < tokens.size(); ++i) {
- char* cs = static_cast<char*>(malloc(tokens[i].length() + 1));
- strcpy(cs, tokens[i].c_str());
+ size_t cs_size = tokens[i].length() + 1;
+ char* cs = new char[cs_size];
+ strncpy(cs, tokens[i].c_str(), cs_size);
+ assert(cs[cs_size - 1] == '\0');
results[i] = cs;
}
// Return number of tokens to calling function
More information about the bind10-changes
mailing list