Parcourir la source

sync with trunk

git-svn-id: svn://bind10.isc.org/svn/bind10/branches/trac345@3099 e5f2f494-b856-4b98-b285-d166d9295462
JINMEI Tatuya il y a 14 ans
Parent
commit
3363091b87
100 fichiers modifiés avec 4455 ajouts et 2018 suppressions
  1. 21 0
      ChangeLog
  2. 41 4
      configure.ac
  3. 4 1
      src/bin/Makefile.am
  4. 14 5
      src/bin/auth/auth_srv.cc
  5. 3 22
      src/bin/auth/tests/Makefile.am
  6. 24 13
      src/bin/auth/tests/auth_srv_unittest.cc
  7. 1 0
      src/bin/auth/tests/run_unittests.cc
  8. 26 0
      src/bin/auth/tests/testdata/Makefile.am
  9. 0 13
      src/bin/auth/tests/testdata/badExampleQuery_fromWire
  10. 0 13
      src/bin/auth/tests/testdata/examplequery_fromWire
  11. 0 13
      src/bin/auth/tests/testdata/iqueryresponse_fromWire
  12. 0 17
      src/bin/auth/tests/testdata/multiquestion_fromWire
  13. 0 19
      src/bin/auth/tests/testdata/queryBadEDNS_fromWire
  14. 0 13
      src/bin/auth/tests/testdata/shortanswer_fromWire
  15. 0 13
      src/bin/auth/tests/testdata/simplequery_fromWire
  16. 0 13
      src/bin/auth/tests/testdata/simpleresponse_fromWire
  17. 1 1
      src/bin/bind10/Makefile.am
  18. 20 2
      src/bin/bind10/bind10.8
  19. 12 1
      src/bin/bind10/bind10.py.in
  20. 12 0
      src/bin/bind10/bind10.xml
  21. 23 0
      src/bin/bind10/tests/args_test.py
  22. 1 1
      src/bin/bindctl/Makefile.am
  23. 3 0
      src/bin/bindctl/bindctl-source.py.in
  24. 1 1
      src/bin/cfgmgr/Makefile.am
  25. 3 0
      src/bin/cfgmgr/b10-cfgmgr.py.in
  26. 1 1
      src/bin/cmdctl/Makefile.am
  27. 3 0
      src/bin/cmdctl/cmdctl.py.in
  28. 2 0
      src/bin/host/host.cc
  29. 1 2
      src/bin/loadzone/Makefile.am
  30. 4 0
      src/bin/loadzone/b10-loadzone.py.in
  31. 1 1
      src/bin/msgq/Makefile.am
  32. 3 0
      src/bin/msgq/msgq.py.in
  33. 13 0
      src/bin/tests/Makefile.am
  34. 3 0
      src/bin/tests/README
  35. 61 0
      src/bin/tests/process_rename_test.py.in
  36. 3 0
      src/bin/usermgr/b10-cmdctl-usermgr.py.in
  37. 1 1
      src/bin/xfrin/Makefile.am
  38. 1 0
      src/bin/xfrin/TODO
  39. 3 0
      src/bin/xfrin/xfrin.py.in
  40. 1 1
      src/bin/xfrout/Makefile.am
  41. 2 1
      src/bin/xfrout/TODO
  42. 3 0
      src/bin/xfrout/xfrout.py.in
  43. 1 1
      src/bin/zonemgr/Makefile.am
  44. 1 0
      src/bin/zonemgr/TODO
  45. 4 1
      src/bin/zonemgr/zonemgr.py.in
  46. 2 0
      src/lib/bench/benchmark_util.cc
  47. 2 1
      src/lib/bench/tests/loadquery_unittest.cc
  48. 1 0
      src/lib/datasrc/data_source.cc
  49. 2 0
      src/lib/datasrc/tests/datasrc_unittest.cc
  50. 1 0
      src/lib/datasrc/tests/query_unittest.cc
  51. 3 0
      src/lib/dns/Makefile.am
  52. 174 0
      src/lib/dns/edns.cc
  53. 449 0
      src/lib/dns/edns.h
  54. 1 1
      src/lib/dns/exceptions.cc
  55. 140 290
      src/lib/dns/message.cc
  56. 41 346
      src/lib/dns/message.h
  57. 69 0
      src/lib/dns/opcode.cc
  58. 292 0
      src/lib/dns/opcode.h
  59. 4 1
      src/lib/dns/python/Makefile.am
  60. 373 0
      src/lib/dns/python/edns_python.cc
  61. 74 678
      src/lib/dns/python/message_python.cc
  62. 10 1
      src/lib/dns/python/messagerenderer_python.cc
  63. 4 4
      src/lib/dns/python/name_python.cc
  64. 392 0
      src/lib/dns/python/opcode_python.cc
  65. 25 1
      src/lib/dns/python/pydnspp.cc
  66. 1 2
      src/lib/dns/python/question_python.cc
  67. 433 0
      src/lib/dns/python/rcode_python.cc
  68. 11 2
      src/lib/dns/python/rdata_python.cc
  69. 1 1
      src/lib/dns/python/rrclass_python.cc
  70. 1 1
      src/lib/dns/python/rrset_python.cc
  71. 1 1
      src/lib/dns/python/rrttl_python.cc
  72. 1 1
      src/lib/dns/python/rrtype_python.cc
  73. 7 3
      src/lib/dns/python/tests/Makefile.am
  74. 180 0
      src/lib/dns/python/tests/edns_python_test.py
  75. 42 273
      src/lib/dns/python/tests/message_python_test.py
  76. 1 0
      src/lib/dns/python/tests/messagerenderer_python_test.py
  77. 112 0
      src/lib/dns/python/tests/opcode_python_test.py
  78. 1 14
      src/lib/dns/python/tests/question_python_test.py
  79. 137 0
      src/lib/dns/python/tests/rcode_python_test.py
  80. 45 0
      src/lib/dns/python/tests/testutil.py
  81. 103 0
      src/lib/dns/rcode.cc
  82. 348 0
      src/lib/dns/rcode.h
  83. 1 1
      src/lib/dns/rrclass-placeholder.h
  84. 4 4
      src/lib/dns/rrset.h
  85. 1 1
      src/lib/dns/rrtype-placeholder.h
  86. 7 97
      src/lib/dns/tests/Makefile.am
  87. 265 0
      src/lib/dns/tests/edns_unittest.cc
  88. 46 104
      src/lib/dns/tests/message_unittest.cc
  89. 2 2
      src/lib/dns/tests/messagerenderer_unittest.cc
  90. 108 0
      src/lib/dns/tests/opcode_unittest.cc
  91. 134 0
      src/lib/dns/tests/rcode_unittest.cc
  92. 7 7
      src/lib/dns/tests/rdata_nsec_unittest.cc
  93. 1 1
      src/lib/dns/tests/rdata_rrsig_unittest.cc
  94. 1 1
      src/lib/dns/tests/rdata_soa_unittest.cc
  95. 4 4
      src/lib/dns/tests/rdata_txt_unittest.cc
  96. 2 1
      src/lib/dns/tests/run_unittests.cc
  97. 56 0
      src/lib/dns/tests/testdata/Makefile.am
  98. 5 0
      src/lib/dns/tests/testdata/edns_toWire1.spec
  99. 5 0
      src/lib/dns/tests/testdata/edns_toWire2.spec
  100. 0 0
      src/lib/dns/tests/testdata/edns_toWire3.spec

+ 21 - 0
ChangeLog

@@ -1,3 +1,24 @@
+  101.	[func]		jinmei
+	src/lib/dns: Completed Opcode and Rcode implementation with more
+	tests and documentation.  API is mostly the same but the
+	validation was a bit tightened. (Trac #351, svn r3056)
+
+  100.  [func]      Michal Vaner
+	Python processes: support naming of python processes so
+	they're not all called python3.
+	(Trac #322, svn r3052)
+
+  99.	[func]*		jinmei
+	Introduced a separate EDNS class to encapsulate EDNS related
+	information more cleanly.  The related APIs are changed a bit,
+	although it won't affect most of higher level applications.
+	(Trac #311, svn r3020)
+
+  98.	[build]		jinmei
+	The ./configure script now tries to search some common include
+	paths for boost header files to minimize the need for explicit
+	configuration with --with-boost-include. (Trac #323, svn r3006)
+
   97.	[func]		jinmei
 	Added a micro benchmark test for query processing of b10-auth.
 	(Trac #308, svn r2982)

+ 41 - 4
configure.ac

@@ -48,6 +48,12 @@ if test $enable_shared = no; then
 	AC_MSG_ERROR([BIND 10 requires shared libraries to be built])
 fi
 
+# allow configuring without setproctitle.
+AC_ARG_ENABLE(setproctitle-check,
+AC_HELP_STRING([--disable-setproctitle-check],
+  [do not check for python setproctitle module (used to give nice names to python processes)]),
+  setproctitle_check=$enableval, setproctitle_check=yes)
+
 # OS dependent configuration
 SET_ENV_LIBRARY_PATH=no
 ENV_LIBRARY_PATH=LD_LIBRARY_PATH
@@ -162,6 +168,18 @@ fi
 AC_SUBST(PYTHON_LIB)
 LDFLAGS=$LDFLAGS_SAVED
 
+# Check for the setproctitle module
+if test "$setproctitle_check" = "yes" ; then
+    AC_MSG_CHECKING(for setproctitle module)
+    if "$PYTHON" -c 'import setproctitle' 2>/dev/null ; then
+        AC_MSG_RESULT(ok)
+    else
+        AC_MSG_RESULT(missing)
+        AC_MSG_ERROR([Missing setproctitle module. Either install it or provide --disable-setproctitle-check.
+In that case we will continue, but naming of python processes will not work.])
+    fi
+fi
+
 # TODO: check for _sqlite3.py module
 
 # Compiler dependent settings: define some mandatory CXXFLAGS here.
@@ -277,14 +295,31 @@ if test "$lcov" != "no"; then
 fi
 AC_SUBST(USE_LCOV)
 
+#
+# Configure Boost header path
+#
+# If explicitly specified, use it.
 AC_ARG_WITH([boost-include],
   AC_HELP_STRING([--with-boost-include=PATH],
     [specify exact directory for Boost headers]),
     [boost_include_path="$withval"])
+# If not specified, try some common paths.
+if test -z "$with_boost_include"; then
+	boostdirs="/usr/local /usr/pkg /opt /opt/local"
+	for d in $boostdirs
+	do
+		if test -f $d/include/boost/shared_ptr.hpp; then
+			boost_include_path=$d/include
+			break
+		fi
+	done
+fi
 if test "${boost_include_path}" ; then
 	BOOST_INCLUDES="-I${boost_include_path}"
 	CPPFLAGS="$CPPFLAGS $BOOST_INCLUDES"
 fi
+AC_CHECK_HEADERS([boost/shared_ptr.hpp boost/foreach.hpp],,
+  AC_MSG_ERROR([Missing required header files.]))
 AC_SUBST(BOOST_INCLUDES)
 
 #
@@ -399,10 +434,6 @@ if test "X$ac_cv_have_devpoll" = "Xyes" -a "X$GXX" = "Xyes"; then
 	CPPFLAGS="$CPPFLAGS -DASIO_DISABLE_DEV_POLL=1"
 fi
 
-# Check for headers from required devel kits.
-AC_CHECK_HEADERS([boost/shared_ptr.hpp boost/foreach.hpp],,
-  AC_MSG_ERROR([Missing required header files.]))
-
 AC_ARG_ENABLE(man, [AC_HELP_STRING([--enable-man],
   [regenerate man pages [default=no]])] ,enable_man=yes, enable_man=no)
 
@@ -433,6 +464,7 @@ AC_CONFIG_FILES([Makefile
                  src/bin/msgq/tests/Makefile
                  src/bin/auth/Makefile
                  src/bin/auth/tests/Makefile
+                 src/bin/auth/tests/testdata/Makefile
                  src/bin/auth/benchmarks/Makefile
                  src/bin/xfrin/Makefile
                  src/bin/xfrin/tests/Makefile
@@ -441,6 +473,7 @@ AC_CONFIG_FILES([Makefile
                  src/bin/zonemgr/Makefile
                  src/bin/zonemgr/tests/Makefile
                  src/bin/usermgr/Makefile
+                 src/bin/tests/Makefile
                  src/lib/Makefile
                  src/lib/bench/Makefile
                  src/lib/bench/example/Makefile
@@ -449,6 +482,8 @@ AC_CONFIG_FILES([Makefile
                  src/lib/cc/tests/Makefile
                  src/lib/python/Makefile
                  src/lib/python/isc/Makefile
+                 src/lib/python/isc/utils/Makefile
+                 src/lib/python/isc/utils/tests/Makefile
                  src/lib/python/isc/datasrc/Makefile
                  src/lib/python/isc/cc/Makefile
                  src/lib/python/isc/cc/tests/Makefile
@@ -463,6 +498,7 @@ AC_CONFIG_FILES([Makefile
                  src/lib/config/testdata/Makefile
                  src/lib/dns/Makefile
                  src/lib/dns/tests/Makefile
+                 src/lib/dns/tests/testdata/Makefile
                  src/lib/dns/python/Makefile
                  src/lib/dns/python/tests/Makefile
                  src/lib/exceptions/Makefile
@@ -506,6 +542,7 @@ AC_OUTPUT([src/bin/cfgmgr/b10-cfgmgr.py
            src/bin/msgq/run_msgq.sh
            src/bin/auth/auth.spec.pre
            src/bin/auth/spec_config.h.pre
+           src/bin/tests/process_rename_test.py
            src/lib/config/tests/data_def_unittests_config.h
            src/lib/python/isc/config/tests/config_test
            src/lib/python/isc/cc/tests/cc_test

+ 4 - 1
src/bin/Makefile.am

@@ -1 +1,4 @@
-SUBDIRS = bind10 bindctl cfgmgr loadzone msgq host cmdctl auth xfrin xfrout usermgr zonemgr
+SUBDIRS = bind10 bindctl cfgmgr loadzone msgq host cmdctl auth xfrin xfrout \
+	usermgr zonemgr tests
+
+check-recursive: all-recursive

+ 14 - 5
src/bin/auth/auth_srv.cc

@@ -24,10 +24,13 @@
 #include <exceptions/exceptions.h>
 
 #include <dns/buffer.h>
+#include <dns/edns.h>
 #include <dns/exceptions.h>
 #include <dns/messagerenderer.h>
 #include <dns/name.h>
 #include <dns/question.h>
+#include <dns/opcode.h>
+#include <dns/rcode.h>
 #include <dns/rrset.h>
 #include <dns/rrttl.h>
 #include <dns/message.h>
@@ -164,7 +167,6 @@ makeErrorMessage(Message& message, MessageRenderer& renderer,
     message.setQid(qid);
     message.setOpcode(opcode);
     message.setHeaderFlag(MessageFlag::QR());
-    message.setUDPSize(AuthSrvImpl::DEFAULT_LOCAL_UDPSIZE);
     if (rd) {
         message.setHeaderFlag(MessageFlag::RD());
     }
@@ -302,14 +304,21 @@ bool
 AuthSrvImpl::processNormalQuery(const IOMessage& io_message, Message& message,
                                 MessageRenderer& response_renderer)
 {
-    const bool dnssec_ok = message.isDNSSECSupported();
-    const uint16_t remote_bufsize = message.getUDPSize();
+    ConstEDNSPtr remote_edns = message.getEDNS();
+    const bool dnssec_ok = remote_edns && remote_edns->getDNSSECAwareness();
+    const uint16_t remote_bufsize = remote_edns ? remote_edns->getUDPSize() :
+        Message::DEFAULT_MAX_UDPSIZE; 
 
     message.makeResponse();
     message.setHeaderFlag(MessageFlag::AA());
     message.setRcode(Rcode::NOERROR());
-    message.setDNSSECSupported(dnssec_ok);
-    message.setUDPSize(AuthSrvImpl::DEFAULT_LOCAL_UDPSIZE);
+
+    if (remote_edns) {
+        EDNSPtr local_edns = EDNSPtr(new EDNS());
+        local_edns->setDNSSECAwareness(dnssec_ok);
+        local_edns->setUDPSize(AuthSrvImpl::DEFAULT_LOCAL_UDPSIZE);
+        message.setEDNS(local_edns);
+    }
 
     try {
         Query query(message, cache_, dnssec_ok);

+ 3 - 22
src/bin/auth/tests/Makefile.am

@@ -1,7 +1,10 @@
+SUBDIRS = testdata .
+
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/bin
 AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc
 AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(srcdir)/testdata\"
+AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(top_builddir)/src/bin/auth/tests/testdata\"
 
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 
@@ -36,25 +39,3 @@ run_unittests_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
 endif
 
 noinst_PROGRAMS = $(TESTS)
-
-EXTRA_DIST =  testdata/badExampleQuery_fromWire
-EXTRA_DIST += testdata/badExampleQuery_fromWire.spec
-EXTRA_DIST += testdata/example.com
-EXTRA_DIST += testdata/examplequery_fromWire
-EXTRA_DIST += testdata/examplequery_fromWire.spec
-EXTRA_DIST += testdata/example.sqlite3
-EXTRA_DIST += testdata/iqueryresponse_fromWire
-EXTRA_DIST += testdata/iqueryresponse_fromWire.spec
-EXTRA_DIST += testdata/multiquestion_fromWire
-EXTRA_DIST += testdata/multiquestion_fromWire.spec
-EXTRA_DIST += testdata/queryBadEDNS_fromWire
-EXTRA_DIST += testdata/queryBadEDNS_fromWire.spec
-EXTRA_DIST += testdata/shortanswer_fromWire
-EXTRA_DIST += testdata/shortanswer_fromWire.spec
-EXTRA_DIST += testdata/shortmessage_fromWire
-EXTRA_DIST += testdata/shortquestion_fromWire
-EXTRA_DIST += testdata/shortresponse_fromWire
-EXTRA_DIST += testdata/simplequery_fromWire
-EXTRA_DIST += testdata/simplequery_fromWire.spec
-EXTRA_DIST += testdata/simpleresponse_fromWire
-EXTRA_DIST += testdata/simpleresponse_fromWire.spec

+ 24 - 13
src/bin/auth/tests/auth_srv_unittest.cc

@@ -22,6 +22,8 @@
 #include <dns/name.h>
 #include <dns/message.h>
 #include <dns/messagerenderer.h>
+#include <dns/opcode.h>
+#include <dns/rcode.h>
 #include <dns/rrclass.h>
 #include <dns/rrtype.h>
 
@@ -114,7 +116,7 @@ protected:
     AuthSrvTest() : server(true, xfrout),
                     request_message(Message::RENDER),
                     parse_message(Message::PARSE), default_qid(0x1035),
-                    opcode(Opcode(Opcode::QUERY())), qname("www.example.com"),
+                    opcode(Opcode::QUERY()), qname("www.example.com"),
                     qclass(RRClass::IN()), qtype(RRType::A()),
                     io_message(NULL), endpoint(NULL), request_obuffer(0),
                     request_renderer(request_obuffer),
@@ -280,6 +282,7 @@ AuthSrvTest::createRequestMessage(const Opcode& opcode,
 {
     request_message.clear(Message::RENDER);
     request_message.setOpcode(opcode);
+    request_message.setRcode(Rcode::NOERROR());
     request_message.setQid(default_qid);
     request_message.addQuestion(Question(request_name, rrclass, rrtype));
 }
@@ -341,7 +344,7 @@ TEST_F(AuthSrvTest, unsupportedRequest) {
             i == Opcode::NOTIFY().getCode()) {
             continue;
         }
-        createDataFromFile("simplequery_fromWire");
+        createDataFromFile("simplequery_fromWire.wire");
         data[2] = ((i << 3) & 0xff);
 
         parse_message.clear(Message::PARSE);
@@ -363,7 +366,7 @@ TEST_F(AuthSrvTest, verbose) {
 
 // Multiple questions.  Should result in FORMERR.
 TEST_F(AuthSrvTest, multiQuestion) {
-    createDataFromFile("multiquestion_fromWire");
+    createDataFromFile("multiquestion_fromWire.wire");
     EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                           response_renderer));
     headerCheck(parse_message, default_qid, Rcode::FORMERR(), opcode.getCode(),
@@ -393,7 +396,7 @@ TEST_F(AuthSrvTest, shortMessage) {
 // or malformed or could otherwise cause a protocol error.
 TEST_F(AuthSrvTest, response) {
     // A valid (although unusual) response
-    createDataFromFile("simpleresponse_fromWire");
+    createDataFromFile("simpleresponse_fromWire.wire");
     EXPECT_EQ(false, server.processMessage(*io_message, parse_message,
                                            response_renderer));
 
@@ -404,7 +407,7 @@ TEST_F(AuthSrvTest, response) {
                                            response_renderer));
 
     // A response to iquery.  must be dropped rather than returning NOTIMP.
-    createDataFromFile("iqueryresponse_fromWire");
+    createDataFromFile("iqueryresponse_fromWire.wire");
     EXPECT_EQ(false, server.processMessage(*io_message, parse_message,
                                            response_renderer));
 }
@@ -422,7 +425,7 @@ TEST_F(AuthSrvTest, shortQuestion) {
 
 // Query with a broken answer section
 TEST_F(AuthSrvTest, shortAnswer) {
-    createDataFromFile("shortanswer_fromWire");
+    createDataFromFile("shortanswer_fromWire.wire");
     EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                           response_renderer));
 
@@ -441,17 +444,24 @@ TEST_F(AuthSrvTest, shortAnswer) {
 
 // Query with unsupported version of EDNS.
 TEST_F(AuthSrvTest, ednsBadVers) {
-    createDataFromFile("queryBadEDNS_fromWire");
+    createDataFromFile("queryBadEDNS_fromWire.wire");
     EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                           response_renderer));
 
-    // The response must have an EDNS OPT RR in the additional section.
+    // The response must have an EDNS OPT RR in the additional section, but
+    // it will be added automatically at the render time.
     // Note that the DNSSEC DO bit is cleared even if this bit in the query
     // is set.  This is a limitation of the current implementation.
     headerCheck(parse_message, default_qid, Rcode::BADVERS(), opcode.getCode(),
                 QR_FLAG, 1, 0, 0, 1);
-    EXPECT_EQ(4096, parse_message.getUDPSize());
-    EXPECT_FALSE(parse_message.isDNSSECSupported());
+    EXPECT_FALSE(parse_message.getEDNS()); // EDNS isn't added at this point
+
+    parse_message.clear(Message::PARSE);
+    InputBuffer ib(response_renderer.getData(), response_renderer.getLength());
+    parse_message.fromWire(ib);
+    EXPECT_EQ(Rcode::BADVERS(), parse_message.getRcode());
+    EXPECT_TRUE(parse_message.getEDNS());
+    EXPECT_FALSE(parse_message.getEDNS()->getDNSSECAwareness());
 }
 
 TEST_F(AuthSrvTest, AXFROverUDP) {
@@ -577,6 +587,7 @@ TEST_F(AuthSrvTest, notifyForCHClass) {
 TEST_F(AuthSrvTest, notifyEmptyQuestion) {
     request_message.clear(Message::RENDER);
     request_message.setOpcode(Opcode::NOTIFY());
+    request_message.setRcode(Rcode::NOERROR());
     request_message.setHeaderFlag(MessageFlag::AA());
     request_message.setQid(default_qid);
     request_message.toWire(request_renderer);
@@ -715,7 +726,7 @@ TEST_F(AuthSrvTest, updateConfig) {
     // query for existent data in the installed data source.  The resulting
     // response should have the AA flag on, and have an RR in each answer
     // and authority section.
-    createDataFromFile("examplequery_fromWire");
+    createDataFromFile("examplequery_fromWire.wire");
     EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                           response_renderer));
     headerCheck(parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
@@ -729,7 +740,7 @@ TEST_F(AuthSrvTest, datasourceFail) {
     // tool and the data source itself naively accept it).  This will result
     // in a SERVFAIL response, and the answer and authority sections should
     // be empty.
-    createDataFromFile("badExampleQuery_fromWire");
+    createDataFromFile("badExampleQuery_fromWire.wire");
     EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                           response_renderer));
     headerCheck(parse_message, default_qid, Rcode::SERVFAIL(),
@@ -744,7 +755,7 @@ TEST_F(AuthSrvTest, updateConfigFail) {
     updateConfig(&server, BADCONFIG_TESTDB, false);
 
     // The original data source should still exist.
-    createDataFromFile("examplequery_fromWire");
+    createDataFromFile("examplequery_fromWire.wire");
     EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                           response_renderer));
     headerCheck(parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),

+ 1 - 0
src/bin/auth/tests/run_unittests.cc

@@ -23,6 +23,7 @@ main(int argc, char* argv[])
 {
     ::testing::InitGoogleTest(&argc, argv);
     isc::UnitTestUtil::addDataPath(TEST_DATA_DIR);
+    isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR);
 
     return (RUN_ALL_TESTS());
 }

+ 26 - 0
src/bin/auth/tests/testdata/Makefile.am

@@ -0,0 +1,26 @@
+CLEANFILES = *.wire
+
+BUILT_SOURCES = badExampleQuery_fromWire.wire examplequery_fromWire.wire
+BUILT_SOURCES += iqueryresponse_fromWire.wire multiquestion_fromWire.wire
+BUILT_SOURCES += queryBadEDNS_fromWire.wire shortanswer_fromWire.wire
+BUILT_SOURCES += simplequery_fromWire.wire simpleresponse_fromWire.wire
+
+# NOTE: keep this in sync with real file listing
+# so is included in tarball
+EXTRA_DIST = badExampleQuery_fromWire.spec
+EXTRA_DIST += examplequery_fromWire.spec
+EXTRA_DIST += iqueryresponse_fromWire.spec
+EXTRA_DIST += multiquestion_fromWire.spec
+EXTRA_DIST += queryBadEDNS_fromWire.spec
+EXTRA_DIST += shortanswer_fromWire.spec
+EXTRA_DIST += shortmessage_fromWire
+EXTRA_DIST += shortquestion_fromWire
+EXTRA_DIST += shortresponse_fromWire
+EXTRA_DIST += simplequery_fromWire.spec
+EXTRA_DIST += simpleresponse_fromWire.spec
+
+EXTRA_DIST += example.com
+EXTRA_DIST += example.sqlite3
+
+.spec.wire:
+	$(abs_top_builddir)/src/lib/dns/tests/testdata/gen-wiredata.py -o $@ $<

+ 0 - 13
src/bin/auth/tests/testdata/badExampleQuery_fromWire

@@ -1,13 +0,0 @@
-###
-### This data file was auto-generated from badExampleQuery_fromWire.spec
-###
-
-# Header Section
-# ID=4149 QR=Query Opcode=QUERY(0) Rcode=NOERROR(0)
-1035 0000
-# QDCNT=1, ANCNT=0, NSCNT=0, ARCNT=0
-0001 0000 0000 0000
-
-# Question Section
-# QNAME=broken.example.com QTYPE=AAAA(28) QCLASS=IN(1)
-0662726f6b656e076578616d706c6503636f6d00 001c 0001

+ 0 - 13
src/bin/auth/tests/testdata/examplequery_fromWire

@@ -1,13 +0,0 @@
-###
-### This data file was auto-generated from examplequery_fromWire.spec
-###
-
-# Header Section
-# ID=4149 QR=Query Opcode=QUERY(0) Rcode=NOERROR(0)
-1035 0000
-# QDCNT=1, ANCNT=0, NSCNT=0, ARCNT=0
-0001 0000 0000 0000
-
-# Question Section
-# QNAME=ns.example.com QTYPE=A(1) QCLASS=IN(1)
-026e73076578616d706c6503636f6d00 0001 0001

+ 0 - 13
src/bin/auth/tests/testdata/iqueryresponse_fromWire

@@ -1,13 +0,0 @@
-###
-### This data file was auto-generated from iqueryresponse_fromWire.spec
-###
-
-# Header Section
-# ID=4149 QR=Response Opcode=IQUERY(1) Rcode=NOERROR(0)
-1035 c000
-# QDCNT=1, ANCNT=0, NSCNT=0, ARCNT=0
-0001 0000 0000 0000
-
-# Question Section
-# QNAME=example.com. QTYPE=A(1) QCLASS=IN(1)
-076578616d706c6503636f6d00 0001 0001

+ 0 - 17
src/bin/auth/tests/testdata/multiquestion_fromWire

@@ -1,17 +0,0 @@
-###
-### This data file was auto-generated from multiquestion_fromWire.spec
-###
-
-# Header Section
-# ID=4149 QR=Query Opcode=QUERY(0) Rcode=NOERROR(0)
-1035 0000
-# QDCNT=2, ANCNT=0, NSCNT=0, ARCNT=0
-0002 0000 0000 0000
-
-# Question Section
-# QNAME=example.com. QTYPE=A(1) QCLASS=IN(1)
-076578616d706c6503636f6d00 0001 0001
-
-# Question Section
-# QNAME=example.com. QTYPE=AAAA(28) QCLASS=IN(1)
-076578616d706c6503636f6d00 001c 0001

+ 0 - 19
src/bin/auth/tests/testdata/queryBadEDNS_fromWire

@@ -1,19 +0,0 @@
-###
-### This data file was auto-generated from queryBadEDNS_fromWire.spec
-###
-
-# Header Section
-# ID=4149 QR=Query Opcode=QUERY(0) Rcode=NOERROR(0)
-1035 0000
-# QDCNT=1, ANCNT=0, NSCNT=0, ARCNT=1
-0001 0000 0000 0001
-
-# Question Section
-# QNAME=example.com. QTYPE=A(1) QCLASS=IN(1)
-076578616d706c6503636f6d00 0001 0001
-
-# EDNS OPT RR
-# NAME=. TYPE=OPT(41) UDPSize=4096 ExtRcode=0 Version=1 DO=1
-00 0029 1000 0001 8000
-# RDLEN=0
-0000

+ 0 - 13
src/bin/auth/tests/testdata/shortanswer_fromWire

@@ -1,13 +0,0 @@
-###
-### This data file was auto-generated from shortanswer_fromWire.spec
-###
-
-# Header Section
-# ID=4149 QR=Query Opcode=QUERY(0) Rcode=NOERROR(0)
-1035 0000
-# QDCNT=1, ANCNT=1, NSCNT=0, ARCNT=0
-0001 0001 0000 0000
-
-# Question Section
-# QNAME=example.com. QTYPE=A(1) QCLASS=IN(1)
-076578616d706c6503636f6d00 0001 0001

+ 0 - 13
src/bin/auth/tests/testdata/simplequery_fromWire

@@ -1,13 +0,0 @@
-###
-### This data file was auto-generated from simplequery_fromWire.spec
-###
-
-# Header Section
-# ID=4149 QR=Query Opcode=QUERY(0) Rcode=NOERROR(0)
-1035 0000
-# QDCNT=1, ANCNT=0, NSCNT=0, ARCNT=0
-0001 0000 0000 0000
-
-# Question Section
-# QNAME=example.com. QTYPE=A(1) QCLASS=IN(1)
-076578616d706c6503636f6d00 0001 0001

+ 0 - 13
src/bin/auth/tests/testdata/simpleresponse_fromWire

@@ -1,13 +0,0 @@
-###
-### This data file was auto-generated from simpleresponse_fromWire.spec
-###
-
-# Header Section
-# ID=4149 QR=Response Opcode=QUERY(0) Rcode=NOERROR(0)
-1035 8000
-# QDCNT=1, ANCNT=0, NSCNT=0, ARCNT=0
-0001 0000 0000 0000
-
-# Question Section
-# QNAME=example.com. QTYPE=A(1) QCLASS=IN(1)
-076578616d706c6503636f6d00 0001 0001

+ 1 - 1
src/bin/bind10/Makefile.am

@@ -1,4 +1,4 @@
-SUBDIRS = tests
+SUBDIRS = . tests
 
 sbin_SCRIPTS = bind10
 CLEANFILES = bind10 bind10.pyc

+ 20 - 2
src/bin/bind10/bind10.8

@@ -1,7 +1,7 @@
 '\" t
 .\"     Title: bind10
 .\"    Author: [see the "AUTHORS" section]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
+.\" Generator: DocBook XSL Stylesheets v1.76.0 <http://docbook.sf.net/>
 .\"      Date: July 29, 2010
 .\"    Manual: BIND10
 .\"    Source: BIND10
@@ -9,6 +9,15 @@
 .\"
 .TH "BIND10" "8" "July 29, 2010" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
 .\" disable hyphenation
@@ -22,7 +31,7 @@
 bind10 \- BIND 10 boss process
 .SH "SYNOPSIS"
 .HP \w'\fBbind10\fR\ 'u
-\fBbind10\fR [\fB\-a\ \fR\fB\fIaddress\fR\fR] [\fB\-m\ \fR\fB\fIfile\fR\fR] [\fB\-n\fR] [\fB\-p\ \fR\fB\fInumber\fR\fR] [\fB\-u\ \fR\fB\fIuser\fR\fR] [\fB\-v\fR] [\fB\-\-address\ \fR\fB\fIaddress\fR\fR] [\fB\-\-msgq\-socket\-file\ \fR\fB\fIfile\fR\fR] [\fB\-\-no\-cache\fR] [\fB\-\-port\ \fR\fB\fInumber\fR\fR] [\fB\-\-user\ \fR\fB\fIuser\fR\fR] [\fB\-\-verbose\fR]
+\fBbind10\fR [\fB\-a\ \fR\fB\fIaddress\fR\fR] [\fB\-m\ \fR\fB\fIfile\fR\fR] [\fB\-n\fR] [\fB\-p\ \fR\fB\fInumber\fR\fR] [\fB\-u\ \fR\fB\fIuser\fR\fR] [\fB\-v\fR] [\fB\-\-address\ \fR\fB\fIaddress\fR\fR] [\fB\-\-msgq\-socket\-file\ \fR\fB\fIfile\fR\fR] [\fB\-\-no\-cache\fR] [\fB\-\-port\ \fR\fB\fInumber\fR\fR] [\fB\-\-user\ \fR\fB\fIuser\fR\fR] [\fB\-\-pretty\-name\ \fR\fB\fIname\fR\fR] [\fB\-\-verbose\fR]
 .SH "DESCRIPTION"
 .PP
 The
@@ -86,6 +95,15 @@ to run as\&.
 must be initially ran as the root user to use this option\&. The default is to run as the current user\&.
 .RE
 .PP
+\fB\-\-pretty\-name \fR\fB\fIname\fR\fR
+.RS 4
+The name this process should have in tools like
+\fBps\fR
+or
+\fBtop\fR\&. This is handy if you have multiple versions/installations of
+\fBbind10\fR\&.
+.RE
+.PP
 \fB\-v\fR, \fB\-\-verbose\fR
 .RS 4
 Display more about what is going on for

+ 12 - 1
src/bin/bind10/bind10.py.in

@@ -63,6 +63,10 @@ import pwd
 import posix
 
 import isc.cc
+import isc.utils.process
+
+# Assign this process some longer name
+isc.utils.process.rename(sys.argv[0])
 
 # This is the version that gets displayed to the user.
 # The VERSION string consists of the module name, the module version
@@ -620,7 +624,11 @@ def check_addr(option, opt_str, value, parser):
         parser.values.address = value
     else:
         raise OptionValueError("Unknown option " + opt_str)
-  
+
+def process_rename(option, opt_str, value, parser):
+    """Function that renames the process if it is requested by a option."""
+    isc.utils.process.rename(value)
+
 def main():
     global options
     global boss_of_bind
@@ -646,6 +654,9 @@ def main():
                       help="Change user after startup (must run as root)")
     parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
                       help="display more about what is going on")
+    parser.add_option("--pretty-name", type="string", action="callback",
+                      callback=process_rename,
+                      help="Set the process name (displayed in ps, top, ...)")
     (options, args) = parser.parse_args()
     if args:
         parser.print_help()

+ 12 - 0
src/bin/bind10/bind10.xml

@@ -56,6 +56,7 @@
       <arg><option>--no-cache</option></arg>
       <arg><option>--port <replaceable>number</replaceable></option></arg>
       <arg><option>--user <replaceable>user</replaceable></option></arg>
+      <arg><option>--pretty-name <replaceable>name</replaceable></option></arg>
       <arg><option>--verbose</option></arg>
     </cmdsynopsis>
   </refsynopsisdiv>
@@ -149,6 +150,17 @@
       </varlistentry>
 
       <varlistentry>
+        <term><option>--pretty-name <replaceable>name</replaceable></option></term>
+
+        <listitem>
+          <para>The name this process should have in tools like
+          <command>ps</command> or <command>top</command>. This
+          is handy if you have multiple versions/installations
+          of <command>bind10</command>.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
         <term><option>-v</option>, <option>--verbose</option></term>
         <listitem>
 	  <para>Display more about what is going on for

+ 23 - 0
src/bin/bind10/tests/args_test.py

@@ -130,5 +130,28 @@ class TestBossArgs(unittest.TestCase):
         x = bob.wait()
         self.assertTrue(bob.wait() == 0)
 
+    def testPrettyName(self):
+        """Try the --pretty-name option."""
+        CMD_PRETTY_NAME = b'bob-name-test'
+        bob = subprocess.Popen(args=(BIND10_EXE, '--pretty-name',
+            CMD_PRETTY_NAME), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+        started_ok = self._waitForString(bob, '[bind10] BIND 10 started')
+        self.assertTrue(started_ok)
+        ps = subprocess.Popen(args=("ps", "axo", "pid,comm"),
+                              stdout=subprocess.PIPE)
+        s = ps.stdout.readline()
+        command = None
+        while True:
+            s = ps.stdout.readline()
+            if s == '': break
+            (pid,comm) = s.split(None, 1)
+            if int(pid) == bob.pid:
+                command = comm
+                break
+        self.assertEqual(command, CMD_PRETTY_NAME + b'\n')
+        time.sleep(0.1)
+        bob.terminate()
+        bob.wait()
+
 if __name__ == '__main__':
     unittest.main()

+ 1 - 1
src/bin/bindctl/Makefile.am

@@ -1,4 +1,4 @@
-SUBDIRS = tests
+SUBDIRS = . tests
 
 bin_SCRIPTS = bindctl
 man_MANS = bindctl.1

+ 3 - 0
src/bin/bindctl/bindctl-source.py.in

@@ -24,6 +24,9 @@ from bindctl.moduleinfo import *
 from bindctl.bindcmd import *
 import pprint
 from optparse import OptionParser, OptionValueError
+import isc.utils.process
+
+isc.utils.process.rename()
 
 __version__ = 'Bindctl'
 

+ 1 - 1
src/bin/cfgmgr/Makefile.am

@@ -1,4 +1,4 @@
-SUBDIRS = tests
+SUBDIRS = . tests
 
 pkglibexecdir = $(libexecdir)/@PACKAGE@
 

+ 3 - 0
src/bin/cfgmgr/b10-cfgmgr.py.in

@@ -21,9 +21,12 @@ import sys; sys.path.append ('@@PYTHONPATH@@')
 
 from isc.config.cfgmgr import ConfigManager, ConfigManagerDataReadError
 from isc.cc import SessionError
+import isc.utils.process
 import signal
 import os
 
+isc.utils.process.rename()
+
 # If B10_FROM_SOURCE is set in the environment, we use data files
 # from a directory relative to that, otherwise we use the ones
 # installed on the system

+ 1 - 1
src/bin/cmdctl/Makefile.am

@@ -1,4 +1,4 @@
-SUBDIRS = tests
+SUBDIRS = . tests
 
 pkglibexecdir = $(libexecdir)/@PACKAGE@
 

+ 3 - 0
src/bin/cmdctl/cmdctl.py.in

@@ -42,6 +42,7 @@ import random
 import time
 import signal
 from isc.config import ccsession
+import isc.utils.process
 from optparse import OptionParser, OptionValueError
 from hashlib import sha1
 try:
@@ -49,6 +50,8 @@ try:
 except ImportError:
     import dummy_threading as threading
 
+isc.utils.process.rename()
+
 __version__ = 'BIND10'
 URL_PATTERN = re.compile('/([\w]+)(?:/([\w]+))?/?')
 CONFIG_DATA_URL = 'config_data'

+ 2 - 0
src/bin/host/host.cc

@@ -28,6 +28,8 @@
 #include <dns/name.h>
 #include <dns/message.h>
 #include <dns/messagerenderer.h>
+#include <dns/opcode.h>
+#include <dns/rcode.h>
 #include <dns/rrclass.h>
 #include <dns/rrtype.h>
 #include <dns/rrset.h>

+ 1 - 2
src/bin/loadzone/Makefile.am

@@ -1,5 +1,4 @@
-SUBDIRS = tests/correct
-SUBDIRS += tests/error
+SUBDIRS = . tests/correct tests/error
 bin_SCRIPTS = b10-loadzone
 
 CLEANFILES = b10-loadzone

+ 4 - 0
src/bin/loadzone/b10-loadzone.py.in

@@ -18,9 +18,13 @@
 import sys; sys.path.append ('@@PYTHONPATH@@')
 import re, getopt
 import isc.datasrc
+import isc.utils.process
 from isc.datasrc.master import MasterFile
 import time
 import os
+
+isc.utils.process.rename()
+
 #########################################################################
 # usage: print usage note and exit
 #########################################################################

+ 1 - 1
src/bin/msgq/Makefile.am

@@ -1,4 +1,4 @@
-SUBDIRS = tests
+SUBDIRS = . tests
 
 pkglibexecdir = $(libexecdir)/@PACKAGE@
  

+ 3 - 0
src/bin/msgq/msgq.py.in

@@ -31,9 +31,12 @@ import select
 import pprint
 import random
 from optparse import OptionParser, OptionValueError
+import isc.utils.process
 
 import isc.cc
 
+isc.utils.process.rename()
+
 # This is the version that gets displayed to the user.
 __version__ = "v20091030 (Paving the DNS Parking Lot)"
 

+ 13 - 0
src/bin/tests/Makefile.am

@@ -0,0 +1,13 @@
+PYTESTS = process_rename_test.py
+# .py will be generated by configure, so we don't have to include it
+# in EXTRA_DIST.
+
+# later will have configure option to choose this, like: coverage run --branch
+PYCOVERAGE = $(PYTHON)
+# test using command-line arguments, so use check-local target instead of TESTS
+check-local:
+	for pytest in $(PYTESTS) ; do \
+	echo Running test: $$pytest ; \
+	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs \
+	$(PYCOVERAGE) $(abs_builddir)/$$pytest || exit ; \
+	done

+ 3 - 0
src/bin/tests/README

@@ -0,0 +1,3 @@
+This directory does not contain any code a user might want to run. It contains
+"global" tests -- tests that are not specific to a single library or program.
+Do not expect to find b10-tests here, there's no such program.

+ 61 - 0
src/bin/tests/process_rename_test.py.in

@@ -0,0 +1,61 @@
+# Copyright (C) 2010  CZ NIC
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""Tests to see if every python process renames itself."""
+import unittest
+import os
+import os.path
+import isc.utils.process
+import re
+
+class TestRename(unittest.TestCase):
+    """Test scanning all python scripts if they rename themself."""
+    def __scan(self, directory, script, fun):
+        # Scan one script if it contains call to the renaming function
+        filename = os.path.join(directory, script)
+        data = ''.join(open(filename).readlines())
+        prettyname = 'src' + filename[filename.rfind('../') + 2:]
+        self.assertTrue(fun.search(data),
+            "Didn't find a call to isc.utils.process.rename in " + prettyname)
+
+    def test_calls(self):
+        """
+        Test if every script renames itself.
+
+        Scan all Makefile and look for scripts.
+        Then scan them by looking at the source text
+        (without actually running them)
+        """
+        # Regexp to find all the *_SCRIPTS = something lines,
+        # including line continuations (backslash and newline)
+        lines = re.compile(r'^\w+_SCRIPTS\s*=\s*((.|\\\n)*)$',
+            re.MULTILINE)
+        # Script name regular expression
+        scripts = re.compile(r'((\w|[-.0-9])+)')
+        # Line with the call
+        fun = re.compile(r'^\s*isc\.utils\.process\.rename\s*\(.*\)\s*(|#.*)$',
+            re.MULTILINE)
+
+        # Find all Makefile and extract names of scripts
+        for (d, _, fs) in os.walk('@top_builddir@'):
+            if 'Makefile' in fs:
+                makefile = ''.join(open(os.path.join(d,
+                    "Makefile")).readlines())
+                for (var, _) in lines.findall(makefile):
+                    for (script, _) in scripts.findall(var):
+                        self.__scan(d, script, fun)
+
+if __name__ == "__main__":
+    unittest.main()

+ 3 - 0
src/bin/usermgr/b10-cmdctl-usermgr.py.in

@@ -25,6 +25,9 @@ import csv
 import getpass
 import getopt
 import sys
+import isc.utils.process
+
+isc.utils.process.rename()
 
 VERSION_NUMBER = 'bind10'
 DEFAULT_FILE = 'cmdctl-accounts.csv'

+ 1 - 1
src/bin/xfrin/Makefile.am

@@ -1,4 +1,4 @@
-SUBDIRS = tests
+SUBDIRS = . tests
 
 pkglibexecdir = $(libexecdir)/@PACKAGE@
 

+ 1 - 0
src/bin/xfrin/TODO

@@ -66,4 +66,5 @@
 17. Do zone transfer from notifyfrom address first, if it's one master of the zone.
 18. Check soa serial first when doing zone refreshment.
 19. Add configuration items to seperate zone, including ACL, multiple masters, etc.
+20. Be able to cancel the ongoing zone transfer, and be able to disable zone transfer.
 

+ 3 - 0
src/bin/xfrin/xfrin.py.in

@@ -29,6 +29,7 @@ import random
 from optparse import OptionParser, OptionValueError
 from isc.config.ccsession import *
 from isc.notify import notify_out
+import isc.utils.process
 try:
     from pydnspp import *
 except ImportError as e:
@@ -36,6 +37,8 @@ except ImportError as e:
     # must keep running, so we warn about it and move forward.
     sys.stderr.write('[b10-xfrin] failed to import DNS module: %s\n' % str(e))
 
+isc.utils.process.rename()
+
 # If B10_FROM_BUILD is set in the environment, we use data files
 # from a directory relative to that, otherwise we use the ones
 # installed on the system

+ 1 - 1
src/bin/xfrout/Makefile.am

@@ -1,4 +1,4 @@
-SUBDIRS = tests
+SUBDIRS = . tests
 
 pkglibexecdir = $(libexecdir)/@PACKAGE@
 

+ 2 - 1
src/bin/xfrout/TODO

@@ -1 +1,2 @@
-Add unittest code.
+Add unittest code.
+Be able to cancel the outgoing zone transfer, and also be able to disable outgoing zone transfer.

+ 3 - 0
src/bin/xfrout/xfrout.py.in

@@ -29,6 +29,7 @@ from isc.config.ccsession import *
 from isc.log.log import *
 from isc.cc import SessionError, SessionTimeout
 from isc.notify import notify_out
+import isc.utils.process
 import socket
 import select
 import errno
@@ -41,6 +42,8 @@ except ImportError as e:
     # must keep running, so we warn about it and move forward.
     sys.stderr.write('[b10-xfrout] failed to import DNS or XFR module: %s\n' % str(e))
 
+isc.utils.process.rename()
+
 if "B10_FROM_BUILD" in os.environ:
     SPECFILE_PATH = os.environ["B10_FROM_BUILD"] + "/src/bin/xfrout"
     AUTH_SPECFILE_PATH = os.environ["B10_FROM_BUILD"] + "/src/bin/auth"

+ 1 - 1
src/bin/zonemgr/Makefile.am

@@ -1,4 +1,4 @@
-SUBDIRS = tests
+SUBDIRS = . tests
 
 pkglibexecdir = $(libexecdir)/@PACKAGE@
 

+ 1 - 0
src/bin/zonemgr/TODO

@@ -1,5 +1,6 @@
 1. Zonemgr should support adding/deleting zones dynamically.
 2. Make zonemgr  has customizable configurations for LOWERBOUND_REFRESH, LOWERBOUND_RETRY, MAX_TRANSFER_TIMEOUT, REFRESH_OFFSET, RETRY_OFFSET, EXPIRED_OFFSET, and/or jitter?
 3. There should be one way to see the current counters/timers and other data for each zone managed by zonemgr.
+4. There should be one way to turn off zonemgr.(Does user really need it? not sure what's the purpose of user)
 
 

+ 4 - 1
src/bin/zonemgr/zonemgr.py.in

@@ -36,6 +36,9 @@ import errno
 from isc.datasrc import sqlite3_ds
 from optparse import OptionParser, OptionValueError
 from isc.config.ccsession import *
+import isc.utils.process
+
+isc.utils.process.rename()
 
 # If B10_FROM_BUILD is set in the environment, we use data files
 # from a directory relative to that, otherwise we use the ones
@@ -261,7 +264,7 @@ class ZonemgrRefresh:
         try:
             self._cc.group_sendmsg(msg, module_name)
         except socket.error:
-            sys.err.write("[b10-zonemgr] Failed to send to module %s, the session has been closed." % module_name) 
+            sys.stderr.write("[b10-zonemgr] Failed to send to module %s, the session has been closed." % module_name) 
 
     def _find_need_do_refresh_zone(self):
         """Find the first zone need do refresh, if no zone need

+ 2 - 0
src/lib/bench/benchmark_util.cc

@@ -26,6 +26,8 @@
 #include <dns/name.h>
 #include <dns/message.h>
 #include <dns/messagerenderer.h>
+#include <dns/opcode.h>
+#include <dns/rcode.h>
 #include <dns/rrtype.h>
 #include <dns/rrclass.h>
 #include <dns/question.h>

+ 2 - 1
src/lib/bench/tests/loadquery_unittest.cc

@@ -22,6 +22,8 @@
 #include <dns/buffer.h>
 #include <dns/message.h>
 #include <dns/name.h>
+#include <dns/opcode.h>
+#include <dns/rcode.h>
 #include <dns/rrclass.h>
 #include <dns/rrtype.h>
 
@@ -80,7 +82,6 @@ public:
         EXPECT_EQ(0, message.getQid());
         EXPECT_EQ(Opcode::QUERY(), message.getOpcode());
         EXPECT_EQ(Rcode::NOERROR(), message.getRcode());
-        EXPECT_EQ(Rcode::NOERROR(), message.getRcode());
         EXPECT_FALSE(message.getHeaderFlag(MessageFlag::QR()));
         EXPECT_FALSE(message.getHeaderFlag(MessageFlag::AA()));
         EXPECT_EQ(1, message.getRRCount(Section::QUESTION()));

+ 1 - 0
src/lib/datasrc/data_source.cc

@@ -32,6 +32,7 @@
 #include <dns/buffer.h>
 #include <dns/message.h>
 #include <dns/name.h>
+#include <dns/rcode.h>
 #include <dns/rdataclass.h>
 #include <dns/rrset.h>
 #include <dns/rrsetlist.h>

+ 2 - 0
src/lib/datasrc/tests/datasrc_unittest.cc

@@ -26,6 +26,8 @@
 #include <dns/message.h>
 #include <dns/messagerenderer.h>
 #include <dns/question.h>
+#include <dns/opcode.h>
+#include <dns/rcode.h>
 #include <dns/rdata.h>
 #include <dns/rdataclass.h>
 #include <dns/rrclass.h>

+ 1 - 0
src/lib/datasrc/tests/query_unittest.cc

@@ -19,6 +19,7 @@
 #include <dns/buffer.h>
 #include <dns/message.h>
 #include <dns/name.h>
+#include <dns/opcode.h>
 #include <dns/rrtype.h>
 #include <dns/rrclass.h>
 

+ 3 - 0
src/lib/dns/Makefile.am

@@ -63,11 +63,14 @@ libdns___la_SOURCES += util/binary_from_base32hex.h
 libdns___la_SOURCES += util/base16_from_binary.h util/binary_from_base16.h
 libdns___la_SOURCES += buffer.h
 libdns___la_SOURCES += dnssectime.h dnssectime.cc
+libdns___la_SOURCES += edns.h edns.cc
 libdns___la_SOURCES += exceptions.h exceptions.cc
 libdns___la_SOURCES += util/hex.h
 libdns___la_SOURCES += message.h message.cc
 libdns___la_SOURCES += messagerenderer.h messagerenderer.cc
 libdns___la_SOURCES += name.h name.cc
+libdns___la_SOURCES += opcode.h opcode.cc
+libdns___la_SOURCES += rcode.h rcode.cc
 libdns___la_SOURCES += rdata.h rdata.cc
 libdns___la_SOURCES += rrclass.cc
 libdns___la_SOURCES += rrparamregistry.h

+ 174 - 0
src/lib/dns/edns.cc

@@ -0,0 +1,174 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <config.h>
+
+#include <stdint.h>
+
+#include <cassert>
+
+#include <boost/lexical_cast.hpp>
+
+#include <exceptions/exceptions.h>
+
+#include <dns/edns.h>
+#include <dns/exceptions.h>
+#include <dns/message.h>
+#include <dns/messagerenderer.h>
+#include <dns/name.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rrclass.h>
+#include <dns/rrttl.h>
+#include <dns/rrtype.h>
+
+using namespace std;
+using namespace boost;
+using namespace isc::dns::rdata;
+
+namespace isc {
+namespace dns {
+
+namespace {
+// This diagram shows the wire-format representation of the TTL field of
+// OPT RR and its relationship with implementation specific parameters.
+//
+//           0             7               15                              31
+//          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//          | EXTENDED-RCODE|    VERSION     |D|               Z             |
+//          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//                                          <= VERSION_SHIFT (16 bits)
+//                          <= EXTRCODE_SHIFT (24 bits)
+//EXTFLAG_DO:0 0 0 ....................... 0 1 0 0 0 0.....................0
+//VER_MASK:  0 0 0 ........0 1 1 1 1 1 1 1 1 0 0 ..........................0
+
+const unsigned int VERSION_SHIFT = 16;
+const unsigned int EXTRCODE_SHIFT = 24;
+const uint32_t VERSION_MASK = 0x00ff0000;
+const uint32_t EXTFLAG_DO = 0x00008000;
+}
+
+EDNS::EDNS(const uint8_t version) :
+    version_(version),
+    udp_size_(Message::DEFAULT_MAX_UDPSIZE),
+    dnssec_aware_(false)
+{
+    if (version_ > SUPPORTED_VERSION) {
+        isc_throw(isc::InvalidParameter,
+                  "failed to construct EDNS: unsupported version: " <<
+                  static_cast<unsigned int>(version_));
+    }
+}
+
+EDNS::EDNS(const Name& name, const RRClass& rrclass, const RRType& rrtype,
+           const RRTTL& ttl, const Rdata& rdata UNUSED_PARAM) :
+    version_((ttl.getValue() & VERSION_MASK) >> VERSION_SHIFT)
+{
+    if (rrtype != RRType::OPT()) {
+        isc_throw(isc::InvalidParameter,
+                  "EDNS is being created with incompatible RR type: "
+                  << rrtype);
+    }
+    
+    if (version_ > EDNS::SUPPORTED_VERSION) {
+        isc_throw(DNSMessageBADVERS, "unsupported EDNS version: " <<
+                  static_cast<unsigned int>(version_));
+    }
+
+    if (name != Name::ROOT_NAME()) {
+        isc_throw(DNSMessageFORMERR, "invalid owner name for EDNS OPT RR: " <<
+                  name);
+    }
+
+    dnssec_aware_ = ((ttl.getValue() & EXTFLAG_DO) != 0);
+    udp_size_ = rrclass.getCode();
+}
+
+string
+EDNS::toText() const {
+    string ret = "; EDNS: version: ";
+
+    ret += lexical_cast<string>(static_cast<int>(getVersion()));
+    ret += ", flags:";
+    if (getDNSSECAwareness()) {
+        ret += " do";
+    }
+    ret += "; udp: " + lexical_cast<string>(getUDPSize()) + "\n";
+
+    return (ret);
+}
+
+template <typename Output>
+int
+EDNS::toWire(Output& output, const uint8_t extended_rcode) const {
+    // Render EDNS OPT RR
+    uint32_t extrcode_flags = extended_rcode << EXTRCODE_SHIFT;
+    extrcode_flags |= (version_ << VERSION_SHIFT) & VERSION_MASK;
+    if (dnssec_aware_) {
+        extrcode_flags |= EXTFLAG_DO;
+    }
+
+    // Construct an RRset corresponding to the EDNS.
+    // We don't support any options for now, so the OPT RR can be empty.
+    RRsetPtr edns_rrset(new RRset(Name::ROOT_NAME(), RRClass(udp_size_),
+                                  RRType::OPT(), RRTTL(extrcode_flags)));
+    edns_rrset->addRdata(ConstRdataPtr(new generic::OPT()));
+
+    edns_rrset->toWire(output);
+
+    return (1);
+}
+
+unsigned int
+EDNS::toWire(MessageRenderer& renderer, const uint8_t extended_rcode) const {
+    // If adding the OPT RR would exceed the size limit, don't do it.
+    // 11 = len(".") + type(2byte) + class(2byte) + TTL(4byte) + RDLEN(2byte)
+    // (RDATA is empty in this simple implementation)
+    if (renderer.getLength() + 11 > renderer.getLengthLimit()) {
+        return (0);
+    }
+
+    return (toWire<MessageRenderer>(renderer, extended_rcode));
+}
+
+unsigned int
+EDNS::toWire(OutputBuffer& buffer, const uint8_t extended_rcode) const {
+    return (toWire<OutputBuffer>(buffer, extended_rcode));
+}
+
+EDNS*
+createEDNSFromRR(const Name& name, const RRClass& rrclass,
+                 const RRType& rrtype, const RRTTL& ttl,
+                 const Rdata& rdata UNUSED_PARAM,
+                 uint8_t& extended_rcode)
+{
+    // Create a new EDNS object first for exception guarantee.
+    EDNS* edns = new EDNS(name, rrclass, rrtype, ttl, rdata);
+
+    // At this point we can update extended_rcode safely.
+    extended_rcode = ttl.getValue() >> EXTRCODE_SHIFT;
+
+    return (edns);
+}
+
+ostream&
+operator<<(std::ostream& os, const EDNS& edns) {
+    os << edns.toText();
+    return (os);
+}
+
+} // end of namespace dns
+} // end of namespace isc

+ 449 - 0
src/lib/dns/edns.h

@@ -0,0 +1,449 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __EDNS_H
+#define __EDNS_H 1
+
+#include <stdint.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include <ostream>
+
+#include <dns/rdata.h>
+
+namespace isc {
+namespace dns {
+
+class EDNS;
+class Name;
+class MessageRenderer;
+class RRClass;
+class RRTTL;
+class RRType;
+class Rcode;
+
+/// \brief A pointer-like type pointing to an \c EDNS object.
+typedef boost::shared_ptr<EDNS> EDNSPtr;
+
+/// \brief A pointer-like type pointing to an immutable \c EDNS object.
+typedef boost::shared_ptr<const EDNS> ConstEDNSPtr;
+
+/// The \c EDNS class represents the %EDNS OPT RR defined in RFC2671.
+///
+/// This class encapsulates various optional features of %EDNS such as
+/// the UDP payload size or the DNSSEC DO bit, and provides interfaces
+/// to manage these features.  It is also responsible for conversion
+/// to and from wire-format OPT RR.
+/// One important exception is about the extended RCODE:
+/// The \c EDNS class is only responsible for extracting the 8-bit part
+/// of the 12-bit extended RCODE from the OPT RR's TTL field of an
+/// incoming message, and for setting the 8-bit part into the OPT RR TTL
+/// of an outgoing message.  It's not supposed to know how to construct the
+/// complete RCODE, much less maintain the RCODE in it.
+/// It is the caller's responsibility (typically the \c Message class).
+///
+/// When converting wire-format OPT RR into an \c EDNS object, it normalizes
+/// the information, i.e., unknown flags will be ignored on construction.
+///
+/// This class is also supposed to support %EDNS options such as NSID,
+/// but the initial implementation does not include it.  This is a near term
+/// TODO item.
+///
+/// <b>Notes to developers</b>
+///
+/// The rest of the description is for developers who need to or want to
+/// understand the design of this API.
+///
+/// Representing %EDNS is tricky.  An OPT RR is no different from other RRs
+/// in terms of the wire format syntax, and in that sense we could use the
+/// generic \c RRset class to represent an OPT RR (BIND 9 adopts this
+/// approach).  But the resulting interface would be inconvenient for
+/// developers.  For example, the developer would need to know that the
+/// UDP size is encoded in the RR Class field.  It's better to provide
+/// a more abstract interface along with the special semantics of OPT RR.
+///
+/// Another approach would be to realize each optional feature of EDNS
+/// as an attribute of the DNS message.
+/// NLnet Labs' ldns takes this approach.
+/// This way an operation for specifying the UDP size would be written
+/// like this:
+/// \code message->setUDPSize(4096); \endcode
+/// which should be more intuitive.
+/// A drawback of this approach is that OPT RR is itself optional and the
+/// separate parameters may not necessarily indicate whether to include an
+/// OPT RR per se.
+/// For example, consider what should be done with this code:
+/// \code message->setUDPSize(512); \endcode
+/// Since the payload size of 512 is the default, it may mean the OPT RR
+/// should be skipped.  But it might also mean the caller intentionally
+/// (for some reason) wants to insert an OPT RR specifying the default UDP
+/// size explicitly.
+///
+/// So, we use a separate class that encapsulates the EDNS semantics and
+/// knows the mapping between the semantics and the wire format representation.
+/// This way the interface can be semantics-based and is intuitive:
+/// \code edns->setUDPSize(4096); \endcode
+/// while we can explicitly specify whether to include an OPT RR by setting
+/// (or not setting) an \c EDNS object in a message:
+/// \code message->setEDNS(edns); // unless we do this OPT RR is skipped
+/// \endcode
+///
+/// There is still a non trivial point: How to manage extended RCODEs.
+/// An OPT RR encodes the upper 8 bits of extended 12-bit RCODE.
+/// In general, it would be better to provide a unified interface to get
+/// access to RCODEs whether or not they are traditional 4 bit codes or
+/// extended ones that have non 0 upper bits.
+/// However, since an OPT RR may not appear in a message the RCODE cannot be
+/// maintained in the \c EDNS class.
+/// But it would not be desirable to maintain the extended RCODEs completely
+/// in the \c Message class, either, because we wanted to hide the mapping
+/// between %EDNS semantics and its wire format representation within the
+///  \c EDNS class; if we moved the responsibility about RCODEs to the
+/// \c Message class, it would have to parse and render the upper 8 bits of
+/// the RCODEs, dealing with wire representation of OPT RR.
+/// This is suboptimal in the sense of encapsulation.
+///
+/// As a compromise, our decision is to separate the knowledge about the
+/// relationship with RCODE from the knowledge about the wire format as
+/// noted in the beginning of this description.
+///
+/// This decoupling is based on the observation that the extended RCODE
+/// is a very special case where %EDNS only has partial information.
+/// If a future version of the %EDNS protocol introduces further relationship
+/// between the message and the %EDNS, we might reconsider the interface,
+/// probably with higher abstraction.
+class EDNS {
+public:
+    ///
+    /// \name Constructors and Destructor
+    ///
+    /// We use the default copy constructor, default copy assignment operator,
+    /// and default destructors intentionally.
+    ///
+    /// Note about copyability: This version of this class is copyable,
+    /// but we may want to change it once we support EDNS options, when
+    /// we want to revise this class using the pimpl idiom.
+    /// But we should be careful about that: the python binding currently
+    /// assumes this class is copyable.
+    //@{
+    /// Constructor with the EDNS version.
+    /// An application would use this constructor to specify EDNS parameters
+    /// and/or options for outgoing DNS messages.
+    ///
+    /// All other parameters than the version number will be initialized to
+    /// reasonable defaults.
+    /// Specifically, the UDP payload size is set to
+    /// \c Message::DEFAULT_MAX_UDPSIZE, and DNSSEC is assumed to be not
+    /// supported.
+    /// These parameters can be altered via setter methods of this class.
+    /// Note, however, that the version number cannot be changed once
+    /// constructed.
+    ///
+    /// The version number parameter can be omitted, in which case the highest
+    /// supported version in this implementation will be assumed.
+    /// When specified, if it is larger than the highest supported version,
+    /// an exception of class \c isc::InvalidParameter will be thrown.
+    ///
+    /// This constructor throws no other exception.
+    ///
+    /// \param version The version number of the EDNS to be constructed.
+    explicit EDNS(const uint8_t version = SUPPORTED_VERSION);
+
+    /// \brief Constructor from resource record (RR) parameters.
+    ///
+    /// This constructor is intended to be used to construct an EDNS object
+    /// from an OPT RR contained in an incoming DNS message.
+    ///
+    /// Unlike many other constructors for this purpose, this constructor
+    /// does not take the bare wire-format %data in the form of an
+    /// \c InputBuffer object.  This is because parsing incoming EDNS is
+    /// highly context dependent and it's not feasible to handle it in a
+    /// completely polymorphic way.  For example, a DNS message parser would
+    /// have to check an OPT RR appears at most once in the message, and if
+    /// it appears it should be in the additional section.  So, the parser
+    /// needs to have an explicit check to see if an RR is of type OPT, and
+    /// then (if other conditions are met) construct a corresponding \c EDNS
+    /// object.  At that point the parser would have already converted the
+    /// wire %data into corresponding objects of \c Name, \c RRClass,
+    /// \c RRType, etc, and it makes more sense to pass them directly to the
+    /// constructor.
+    ///
+    /// In practice, top level applications rarely need to use this
+    /// constructor directly.  It should normally suffice to have a higher
+    /// level class such as \c Message do that job.
+    ///
+    /// This constructor checks the passed parameters to see if they are
+    /// valid in terms of the EDNS protocol specification.
+    /// \c name must be the root name ("."); otherwise, an exception of
+    /// class \c DNSMessageFORMERR will be thrown.
+    /// \c rrtype must specify the OPT RR type; otherwise, an exception of
+    /// class \c isc::InvalidParameter will be thrown.
+    /// The ENDS version number is extracted from \c rrttl.  If it is larger
+    /// than the higher supported version, an exception of class
+    /// \c DNSMessageBADVERS will be thrown.  Note that this is different from
+    /// the case of the same error in the other constructor.
+    /// This is intentional, so that the application can transparently convert
+    /// the exception to a response RCODE according to the protocol
+    /// specification.
+    ///
+    /// This initial implementation does not support EDNS options at all,
+    /// and \c rdata is simply ignored.  Future versions will support
+    /// options, and may throw exceptions while validating the given parameter.
+    ///
+    /// \b Note: since no other type than OPT for \c rrtype is allowed, this
+    /// parameter could actually have been omitted.  But it is intentionally
+    /// included as a parameter so that invalid usage of the construction
+    /// can be detected.  As noted above the caller should normally have
+    /// the corresponding \c RRType object at the time of call to this
+    /// constructor, so the overhead of having the additional parameter
+    /// should be marginal.
+    ///
+    /// \param name The owner name of the OPT RR.  This must be the root name.
+    /// \param rrclass The RR class of the OPT RR.
+    /// \param rrtype This must specify the OPT RR type.
+    /// \param rrttl The TTL of the OPT RR.
+    /// \param rdata The RDATA of the OPT RR.
+    EDNS(const Name& name, const RRClass& rrclass, const RRType& rrtype,
+         const RRTTL& ttl, const rdata::Rdata& rdata);
+    //@}
+
+    ///
+    /// \name Getter and Setter Methods
+    ///
+    //@{
+    /// \brief Returns the version of EDNS.
+    ///
+    /// This method never throws an exception.
+    uint8_t getVersion() const { return (version_); }
+
+    /// \brief Returns the maximum payload size of UDP messages for the sender
+    /// of the message containing this \c EDNS.
+    ///
+    /// This method never throws an exception.
+    uint16_t getUDPSize() const { return (udp_size_); }
+
+    /// \brief Specify the maximum payload size of UDP messages that use
+    /// this EDNS.
+    ///
+    /// Unless explicitly specified, \c DEFAULT_MAX_UDPSIZE will be assumed
+    /// for the maximum payload size, regardless of whether EDNS OPT RR is
+    /// included or not.  This means if an application wants to send a message
+    /// with an EDNS OPT RR for specifying a larger UDP size, it must
+    /// explicitly specify the value using this method.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \param udp_size The maximum payload size of UDP messages for the sender
+    /// of the message containing this \c EDNS.
+    void setUDPSize(const uint16_t udp_size) { udp_size_ = udp_size; }
+
+    /// \brief Returns whether the message sender is DNSSEC aware.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \return true if DNSSEC is supported; otherwise false.
+    bool getDNSSECAwareness() const { return (dnssec_aware_); }
+
+    /// \brief Specifies whether the sender of the message containing this
+    /// \c EDNS is DNSSEC aware.
+    ///
+    /// If the parameter is true, a subsequent call to \c toWire() will
+    /// set the DNSSEC DO bit on for the corresponding OPT RR.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \param is_aware \c true if DNSSEC is supported; \c false otherwise.
+    void setDNSSECAwareness(const bool is_aware) { dnssec_aware_ = is_aware; }
+    //@}
+
+    ///
+    /// \name Converter Methods
+    ///
+    //@{
+    /// \brief Render the \c EDNS in the wire format.
+    ///
+    /// This method renders the \c EDNS object as a form of DNS OPT RR
+    /// via \c renderer, which encapsulates output buffer and other rendering
+    /// contexts.
+    /// Since the \c EDNS object does not maintain the extended RCODE
+    /// information, a separate parameter \c extended_rcode must be passed to
+    /// this method.
+    ///
+    /// If by adding the OPT RR the message size would exceed the limit
+    /// maintained in \c renderer, this method skips rendering the RR
+    /// and returns 0; otherwise it returns 1, which is the number of RR
+    /// rendered.
+    ///
+    /// In the current implementation the return value is either 0 or 1, but
+    /// the return type is <code>unsigned int</code> to be consistent with
+    /// \c RRset::toWire().  In any case the caller shouldn't assume these are
+    /// only possible return values from this method.
+    ///
+    /// This method is mostly exception free, but it requires memory
+    /// allocation and if it fails a corresponding standard exception will be
+    /// thrown.
+    ///
+    /// In practice, top level applications rarely need to use this
+    /// method directly.  It should normally suffice to have a higher
+    /// level class such as \c Message do that job.
+    ///
+    /// <b>Note to developer:</b> the current implementation constructs an
+    /// \c RRset object for the OPT RR and calls its \c toWire() method,
+    /// which is inefficient.  In future, we may want to optimize this method
+    /// by caching the rendered image and having the application reuse the
+    /// same \c EDNS object when possible.
+    ///
+    /// \param renderer DNS message rendering context that encapsulates the
+    /// output buffer and name compression information.
+    /// \param extended_rcode Upper 8 bits of extended RCODE to be rendered as
+    /// part of the EDNS OPT RR.
+    /// \return 1 if the OPT RR fits in the message size limit; otherwise 0.
+    unsigned int toWire(MessageRenderer& renderer,
+                        const uint8_t extended_rcode) const;
+
+    /// \brief Render the \c EDNS in the wire format.
+    ///
+    /// This method is same as \c toWire(MessageRenderer&,uint8_t)const
+    /// except it renders the OPT RR in an \c OutputBuffer and therefore
+    /// does not care about message size limit.
+    /// As a consequence it always returns 1.
+    unsigned int toWire(OutputBuffer& buffer,
+                        const uint8_t extended_rcode) const;
+
+    /// \brief Convert the EDNS to a string.
+    ///
+    /// The format of the resulting string is as follows:
+    /// \code ; EDNS: version: <version>, flags: <edns flags>; udp: <udp size>
+    /// \endcode
+    /// where
+    ///  - \em version is the EDNS version number (integer).
+    ///  - <em>edns flags</em> is a sequence of EDNS flag bits.  The only
+    ///    possible flag is the "DNSSEC OK", which is represented as "do".
+    ///  - <em>udp size</em> is sender's UDP payload size in bytes.
+    ///
+    /// The string will be terminated with a trailing newline character.
+    ///
+    /// When EDNS options are supported the output of this method will be
+    /// extended.
+    ///
+    /// This method is mostly exception free, but it may require memory
+    /// allocation and if it fails a corresponding standard exception will be
+    /// thrown.
+    ///
+    /// \return A string representation of \c EDNS.  See above for the format.
+    std::string toText() const;
+    //@}
+
+    // TBD: This method is currently not implemented.  We'll eventually need
+    // something like this.
+    //void addOption();
+
+private:
+    /// Helper method to define unified implementation for the public versions
+    /// of toWire().
+    template <typename Output>
+    int toWire(Output& output, const uint8_t extended_rcode) const;
+
+public:
+    /// \brief The highest EDNS version this implementation supports.
+    static const uint8_t SUPPORTED_VERSION = 0;
+private:
+    // We may eventually want to migrate to pimpl, especially when we support
+    // EDNS options.  In this initial implementation, we keep it simple.
+    const uint8_t version_;
+    uint16_t udp_size_;
+    bool dnssec_aware_;
+};
+
+/// \brief Create a new \c EDNS object from a set of RR parameters, also
+/// providing the extended RCODE value.
+///
+/// This function is similar to the EDNS class constructor
+/// \c EDNS::EDNS(const Name&, const RRClass&, const RRType&, const RRTTL&, const rdata::Rdata&)
+/// but is different in that
+/// - It dynamically creates a new object
+/// - It returns (via a reference argument) the topmost 8 bits of the extended
+/// RCODE encoded in the \c ttl.
+///
+/// On success, \c extended_rcode will be updated with the 8-bit part of
+/// the extended RCODE encoded in the TTL of the OPT RR.
+///
+/// The intended usage of this function is to parse an OPT RR of an incoming
+/// DNS message, while updating the RCODE of the message.
+/// One common usage patter is as follows:
+///
+/// \code Message msg;
+/// ...
+/// uint8_t extended_rcode;
+/// ConstEDNSPtr edns = ConstEDNSPtr(createEDNSFromRR(..., extended_rcode));
+/// rcode = Rcode(msg.getRcode().getCode(), extended_rcode);
+/// \endcode
+/// (although, like the \c EDNS constructor, normal applications wouldn't have
+/// to use this function directly).
+///
+/// This function provides the strong exception guarantee: Unless an
+/// exception is thrown \c extended_code won't be modified.
+///
+/// This function validates the given parameters and throws exceptions on
+/// failure in the same way as the \c EDNS class constructor.
+/// In addition, if memory allocation for the new object fails it throws the
+/// corresponding standard exception.
+///
+/// Note that this function returns a bare pointer to the newly allocated
+/// object, not a shared pointer object enclosing the pointer.
+/// The caller is responsible for deleting the object after the use of it
+/// (typically, the caller would immediately encapsulate the returned pointer
+/// in a shared pointer object, \c EDNSPtr or \c ConstEDNSPtr).
+/// It returns a bare pointer so that it can be used where the use of a shared
+/// pointer is impossible or not desirable.
+///
+/// Note to developers: there is no strong technical reason why this function
+/// cannot be a constructor of the \c EDNS class or even integrated into the
+/// constructor.  But we decided to make it a separate free function so that
+/// constructors will be free from side effects (which is in itself a matter
+/// of preference).
+///
+/// \param name The owner name of the OPT RR.  This must be the root name.
+/// \param rrclass The RR class of the OPT RR.
+/// \param rrtype This must specify the OPT RR type.
+/// \param rrttl The TTL of the OPT RR.
+/// \param rdata The RDATA of the OPT RR.
+/// \param extended_rcode A placeholder to store the topmost 8 bits of the
+/// extended Rcode.
+/// \return A pointer to the created \c EDNS object.
+EDNS* createEDNSFromRR(const Name& name, const RRClass& rrclass,
+                       const RRType& rrtype, const RRTTL& ttl,
+                       const rdata::Rdata& rdata, uint8_t& extended_rcode);
+
+/// \brief Insert the \c EDNS as a string into stream.
+///
+/// This method convert \c edns into a string and inserts it into the
+/// output stream \c os.
+///
+/// \param os A \c std::ostream object on which the insertion operation is
+/// performed.
+/// \param edns A reference to an \c EDNS object output by the operation.
+/// \return A reference to the same \c std::ostream object referenced by
+/// parameter \c os after the insertion operation.
+std::ostream& operator<<(std::ostream& os, const EDNS& edns);
+}
+}
+#endif  // __EDNS_H
+
+// Local Variables: 
+// mode: c++
+// End: 

+ 1 - 1
src/lib/dns/exceptions.cc

@@ -15,7 +15,7 @@
 // $Id$
 
 #include <dns/exceptions.h>
-#include <dns/message.h>
+#include <dns/rcode.h>
 
 namespace isc {
 namespace dns {

+ 140 - 290
src/lib/dns/message.cc

@@ -28,10 +28,13 @@
 #include <exceptions/exceptions.h>
 
 #include <dns/buffer.h>
+#include <dns/edns.h>
 #include <dns/exceptions.h>
 #include <dns/message.h>
 #include <dns/messagerenderer.h>
 #include <dns/name.h>
+#include <dns/opcode.h>
+#include <dns/rcode.h>
 #include <dns/question.h>
 #include <dns/rdataclass.h>
 #include <dns/rrclass.h>
@@ -61,14 +64,6 @@ const flags_t FLAG_RA = 0x0080;
 const flags_t FLAG_AD = 0x0020;
 const flags_t FLAG_CD = 0x0010;
 
-//
-// EDNS related constants
-//
-const flags_t EXTFLAG_MASK = 0xffff;
-const flags_t EXTFLAG_DO = 0x8000;
-const uint32_t EXTRCODE_MASK = 0xff000000; 
-const uint32_t EDNSVERSION_MASK = 0x00ff0000;
-
 const unsigned int OPCODE_MASK = 0x7800;
 const unsigned int OPCODE_SHIFT = 11;
 const unsigned int RCODE_MASK = 0x000f;
@@ -76,84 +71,6 @@ const unsigned int FLAG_MASK = 0x8ff0;
 
 const unsigned int MESSAGE_REPLYPRESERVE = (FLAG_RD | FLAG_CD);
 
-const Rcode rcodes[] = {
-    Rcode::NOERROR(),
-    Rcode::FORMERR(),
-    Rcode::SERVFAIL(),
-    Rcode::NXDOMAIN(),
-    Rcode::NOTIMP(),
-    Rcode::REFUSED(),
-    Rcode::YXDOMAIN(),
-    Rcode::YXRRSET(),
-    Rcode::NXRRSET(),
-    Rcode::NOTAUTH(),
-    Rcode::NOTZONE(),
-    Rcode::RESERVED11(),
-    Rcode::RESERVED12(),
-    Rcode::RESERVED13(),
-    Rcode::RESERVED14(),
-    Rcode::RESERVED15(),
-    Rcode::BADVERS()
-};
-
-const char *rcodetext[] = {
-    "NOERROR",
-    "FORMERR",
-    "SERVFAIL",
-    "NXDOMAIN",
-    "NOTIMP",
-    "REFUSED",
-    "YXDOMAIN",
-    "YXRRSET",
-    "NXRRSET",
-    "NOTAUTH",
-    "NOTZONE",
-    "RESERVED11",
-    "RESERVED12",
-    "RESERVED13",
-    "RESERVED14",
-    "RESERVED15",
-    "BADVERS"
-};
-
-const Opcode* opcodes[] = {
-    &Opcode::QUERY(),
-    &Opcode::IQUERY(),
-    &Opcode::STATUS(),
-    &Opcode::RESERVED3(),
-    &Opcode::NOTIFY(),
-    &Opcode::UPDATE(),
-    &Opcode::RESERVED6(),
-    &Opcode::RESERVED7(),
-    &Opcode::RESERVED8(),
-    &Opcode::RESERVED9(),
-    &Opcode::RESERVED10(),
-    &Opcode::RESERVED11(),
-    &Opcode::RESERVED12(),
-    &Opcode::RESERVED13(),
-    &Opcode::RESERVED14(),
-    &Opcode::RESERVED15()
-};
-
-const char *opcodetext[] = {
-    "QUERY",
-    "IQUERY",
-    "STATUS",
-    "RESERVED3",
-    "NOTIFY",
-    "UPDATE",
-    "RESERVED6",
-    "RESERVED7",
-    "RESERVED8",
-    "RESERVED9",
-    "RESERVED10",
-    "RESERVED11",
-    "RESERVED12",
-    "RESERVED13",
-    "RESERVED14",
-    "RESERVED15"
-};
-
 const char *sectiontext[] = {
     "QUESTION",
     "ANSWER",
@@ -162,28 +79,6 @@ const char *sectiontext[] = {
 };
 }
 
-string
-Opcode::toText() const {
-    return (opcodetext[code_]);
-}
-
-Rcode::Rcode(uint16_t code) : code_(code) {
-    if (code_ > MAX_RCODE) {
-        isc_throw(OutOfRange, "Rcode is too large to construct");
-    }
-}
-
-string
-Rcode::toText() const {
-    if (code_ < sizeof(rcodetext) / sizeof (const char *)) {
-        return (rcodetext[code_]);
-    }
-
-    ostringstream oss;
-    oss << code_;
-    return (oss.str());
-}
-
 namespace {
 inline unsigned int
 sectionCodeToId(const Section& section) {
@@ -200,20 +95,24 @@ public:
     // for efficiency?
     Message::Mode mode_;
     qid_t qid_;
-    Rcode rcode_;
+
+    // We want to use NULL for [op,r]code_ to mean the code being not
+    // correctly parsed or set.  We store the real code object in
+    // xxcode_placeholder_ and have xxcode_ refer to it when the object
+    // is valid.
+    const Rcode* rcode_;
+    Rcode rcode_placeholder_;
     const Opcode* opcode_;
+    Opcode opcode_placeholder_;
+
     flags_t flags_;
-    bool dnssec_ok_;
 
     bool header_parsed_;
     static const unsigned int SECTION_MAX = 4; // TODO: revisit this design
     int counts_[SECTION_MAX];   // TODO: revisit this definition
     vector<QuestionPtr> questions_;
     vector<RRsetPtr> rrsets_[SECTION_MAX];
-    RRsetPtr remote_edns_;
-    uint16_t remote_udpsize_;
-    RRsetPtr local_edns_;
-    uint16_t udpsize_;
+    ConstEDNSPtr edns_;
 
 #ifdef notyet
     // tsig/sig0: TODO
@@ -221,12 +120,16 @@ public:
 #endif
 
     void init();
+    void setOpcode(const Opcode& opcode);
+    void setRcode(const Rcode& rcode);
     int parseQuestion(InputBuffer& buffer);
     int parseSection(const Section& section, InputBuffer& buffer);
 };
 
 MessageImpl::MessageImpl(Message::Mode mode) :
-    mode_(mode), rcode_(Rcode::NOERROR())
+    mode_(mode),
+    rcode_placeholder_(Rcode(0)), // as a placeholder the value doesn't matter
+    opcode_placeholder_(Opcode(0)) // ditto
 {
     init();
 }
@@ -235,13 +138,9 @@ void
 MessageImpl::init() {
     flags_ = 0;
     qid_ = 0;
-    rcode_ = Rcode::NOERROR();  // XXX
+    rcode_ = NULL;
     opcode_ = NULL;
-    dnssec_ok_ = false;
-    remote_edns_ = RRsetPtr();
-    remote_udpsize_ = Message::DEFAULT_MAX_UDPSIZE;
-    local_edns_ = RRsetPtr();
-    udpsize_ = Message::DEFAULT_MAX_UDPSIZE;
+    edns_ = EDNSPtr();
 
     for (int i = 0; i < SECTION_MAX; ++i) {
         counts_[i] = 0;
@@ -254,6 +153,18 @@ MessageImpl::init() {
     rrsets_[sectionCodeToId(Section::ADDITIONAL())].clear();
 }
 
+void
+MessageImpl::setOpcode(const Opcode& opcode) {
+    opcode_placeholder_ = opcode;
+    opcode_ = &opcode_placeholder_;
+}
+
+void
+MessageImpl::setRcode(const Rcode& rcode) {
+    rcode_placeholder_ = rcode;
+    rcode_ = &rcode_placeholder_;
+}
+
 Message::Message(Mode mode) :
     impl_(new MessageImpl(mode))
 {}
@@ -285,38 +196,6 @@ Message::clearHeaderFlag(const MessageFlag& flag) {
     impl_->flags_ &= ~flag.getBit();
 }
 
-bool
-Message::isDNSSECSupported() const {
-    return (impl_->dnssec_ok_);
-}
-
-void
-Message::setDNSSECSupported(bool on) {
-    if (impl_->mode_ != Message::RENDER) {
-        isc_throw(InvalidMessageOperation,
-                  "setDNSSECSupported performed in non-render mode");
-    }
-    impl_->dnssec_ok_ = on;
-}
-
-uint16_t
-Message::getUDPSize() const {
-    return (impl_->udpsize_);
-}
-
-void
-Message::setUDPSize(uint16_t size) {
-    if (impl_->mode_ != Message::RENDER) {
-        isc_throw(InvalidMessageOperation,
-                  "setUDPSize performed in non-render mode");
-    }
-    if (size < DEFAULT_MAX_UDPSIZE) {
-        isc_throw(InvalidMessageUDPSize,
-                  "Specified UDP message size is too small");
-    }
-    impl_->udpsize_ = size;
-}
-
 qid_t
 Message::getQid() const {
     return (impl_->qid_);
@@ -333,7 +212,10 @@ Message::setQid(qid_t qid) {
 
 const Rcode&
 Message::getRcode() const {
-    return (impl_->rcode_);
+    if (impl_->rcode_ == NULL) {
+        isc_throw(InvalidMessageOperation, "getRcode attempted before set");
+    }
+    return (*impl_->rcode_);
 }
 
 void
@@ -342,11 +224,14 @@ Message::setRcode(const Rcode& rcode) {
         isc_throw(InvalidMessageOperation,
                   "setRcode performed in non-render mode");
     }
-    impl_->rcode_ = rcode;
+    impl_->setRcode(rcode);
 }
 
 const Opcode&
 Message::getOpcode() const {
+    if (impl_->opcode_ == NULL) {
+        isc_throw(InvalidMessageOperation, "getOpcode attempted before set");
+    }
     return (*impl_->opcode_);
 }
 
@@ -356,7 +241,21 @@ Message::setOpcode(const Opcode& opcode) {
         isc_throw(InvalidMessageOperation,
                   "setOpcode performed in non-render mode");
     }
-    impl_->opcode_ = &opcode;
+    impl_->setOpcode(opcode);
+}
+
+ConstEDNSPtr
+Message::getEDNS() const {
+    return (impl_->edns_);
+}
+
+void
+Message::setEDNS(ConstEDNSPtr edns) {
+    if (impl_->mode_ != Message::RENDER) {
+        isc_throw(InvalidMessageOperation,
+                  "setEDNS performed in non-render mode");
+    }
+    impl_->edns_ = edns;
 }
 
 unsigned int
@@ -441,60 +340,20 @@ struct RenderSection {
 };
 }
 
-namespace {
-bool
-addEDNS(MessageImpl* mimpl, MessageRenderer& renderer) {
-    const bool is_query = ((mimpl->flags_ & MessageFlag::QR().getBit()) == 0); 
-
-    // If this is a reply, add EDNS either when the request had it, or
-    // if the Rcode is BADVERS, which is EDNS specific.
-    // XXX: this logic is tricky.  We should revisit this later.
-    if (!is_query) {
-        if (mimpl->remote_edns_ == NULL && mimpl->rcode_ != Rcode::BADVERS()) {
-            return (false);
-        }
-    } else {
-        // For queries, we add EDNS only when necessary:
-        // Local UDP size is not the default value, or
-        // DNSSEC DO bit is to be set, or
-        // Extended Rcode is to be specified.
-        if (mimpl->udpsize_ == Message::DEFAULT_MAX_UDPSIZE &&
-            !mimpl->dnssec_ok_ &&
-            mimpl->rcode_.getCode() < 0x10) {
-            return (false);
-        }
-    }
-
-    // If adding the OPT RR would exceed the size limit, don't do it.
-    // 11 = len(".") + type(2byte) + class(2byte) + TTL(4byte) + RDLEN(2byte)
-    // (RDATA is empty in this simple implementation)
-    if (renderer.getLength() + 11 > renderer.getLengthLimit()) {
-        return (false);
-    }
-
-    // Render EDNS OPT RR
-    uint32_t extrcode_flags = ((mimpl->rcode_.getCode() & 0xff0) << 24);
-    if (mimpl->dnssec_ok_) {
-        extrcode_flags |= 0x8000; // set DO bit
-    }
-    mimpl->local_edns_ = RRsetPtr(new RRset(Name::ROOT_NAME(),
-                                            RRClass(mimpl->udpsize_),
-                                            RRType::OPT(),
-                                            RRTTL(extrcode_flags)));
-    // We don't support any options in this simple implementation
-    mimpl->local_edns_->addRdata(ConstRdataPtr(new generic::OPT()));
-    mimpl->local_edns_->toWire(renderer);
-
-    return (true);
-}
-}
-
 void
 Message::toWire(MessageRenderer& renderer) {
     if (impl_->mode_ != Message::RENDER) {
         isc_throw(InvalidMessageOperation,
                   "Message rendering attempted in non render mode");
     }
+    if (impl_->rcode_ == NULL) {
+        isc_throw(InvalidMessageOperation,
+                  "Message rendering attempted without Rcode set");
+    }
+    if (impl_->opcode_ == NULL) {
+        isc_throw(InvalidMessageOperation,
+                  "Message rendering attempted without Opcode set");
+    }
 
     // reserve room for the header
     renderer.skip(HEADERLEN);
@@ -528,12 +387,20 @@ Message::toWire(MessageRenderer& renderer) {
                      RenderSection<RRsetPtr>(renderer, false)).getTotalCount();
     }
 
-    // Added EDNS OPT RR if necessary (we want to avoid hardcoding specialized
-    // logic, see the parser case)
-    if (!renderer.isTruncated() && addEDNS(this->impl_, renderer)) {
-        ++arcount;
+    // Add EDNS OPT RR if necessary.  Basically, we add it only when EDNS
+    // has been explicitly set.  However, if the RCODE would require it and
+    // no EDNS has been set we generate a temporary local EDNS and use it.
+    if (!renderer.isTruncated()) {
+        ConstEDNSPtr local_edns = impl_->edns_;
+        if (!local_edns && impl_->rcode_->getExtendedCode() != 0) {
+            local_edns = ConstEDNSPtr(new EDNS());
+        }
+        if (local_edns) {
+            arcount += local_edns->toWire(renderer,
+                                          impl_->rcode_->getExtendedCode());
+        }
     }
-
+ 
     // Adjust the counter buffer.
     // XXX: these may not be equal to the number of corresponding entries
     // in rrsets_[] or questions_ if truncation occurred or an EDNS OPT RR
@@ -553,7 +420,7 @@ Message::toWire(MessageRenderer& renderer) {
 
     uint16_t codes_and_flags =
         (impl_->opcode_->getCode() << OPCODE_SHIFT) & OPCODE_MASK;
-    codes_and_flags |= (impl_->rcode_.getCode() & RCODE_MASK);
+    codes_and_flags |= (impl_->rcode_->getCode() & RCODE_MASK);
     codes_and_flags |= (impl_->flags_ & FLAG_MASK);
     renderer.writeUint16At(codes_and_flags, header_pos);
     header_pos += sizeof(uint16_t);
@@ -582,8 +449,8 @@ Message::parseHeader(InputBuffer& buffer) {
 
     impl_->qid_ = buffer.readUint16();
     const uint16_t codes_and_flags = buffer.readUint16();
-    impl_->opcode_ = opcodes[((codes_and_flags & OPCODE_MASK) >> OPCODE_SHIFT)];
-    impl_->rcode_ = rcodes[(codes_and_flags & RCODE_MASK)];
+    impl_->setOpcode(Opcode((codes_and_flags & OPCODE_MASK) >> OPCODE_SHIFT));
+    impl_->setRcode(Rcode(codes_and_flags & RCODE_MASK));
     impl_->flags_ = (codes_and_flags & FLAG_MASK);
     impl_->counts_[Section::QUESTION().getCode()] = buffer.readUint16();
     impl_->counts_[Section::ANSWER().getCode()] = buffer.readUint16();
@@ -657,6 +524,34 @@ struct MatchRR : public unary_function<RRsetPtr, bool> {
 };
 }
 
+// Note about design decision:
+// we need some type specific processing here, including EDNS and TSIG.
+// how much we should generalize/hardcode the special logic is subject
+// to discussion.  In terms of modularity it would be ideal to introduce
+// an abstract class (say "MessageAttribute") and let other such
+// concrete notions as EDNS or TSIG inherit from it.  Then we would
+// just do:
+// message->addAttribute(rrtype, rrclass, buffer);
+// to create and attach type-specific concrete object to the message.
+//
+// A major downside of this approach is, as usual, complexity due to
+// indirection and performance penalty.  Also, it may not be so easy
+// to separate the processing logic because in many cases we'll need
+// parse context for which the message class is responsible (e.g.
+// to check the EDNS OPT RR only appears in the additional section,
+// and appears only once).
+//
+// Another point to consider is that we may not need so many special
+// types other than EDNS and TSIG (and when and if we implement it,
+// SIG(0)); newer optional attributes of the message would more likely
+// be standardized as new flags or options of EDNS.  If that's the case,
+// introducing an abstract class with all the overhead and complexity
+// may not make much sense.
+//
+// Conclusion: don't over-generalize type-specific logic for now.
+// introduce separate concrete classes, and move context-independent
+// logic to that class; processing logic dependent on parse context
+// is hardcoded here.
 int
 MessageImpl::parseSection(const Section& section, InputBuffer& buffer) {
     unsigned int added = 0;
@@ -678,60 +573,36 @@ MessageImpl::parseSection(const Section& section, InputBuffer& buffer) {
         const size_t rdlen = buffer.readUint16();
         ConstRdataPtr rdata = createRdata(rrtype, rrclass, buffer, rdlen);
 
-        // XXX: we wanted to avoid hardcoding type-specific logic here,
-        // but this would be the fastest way for a proof-of-concept
-        // implementation.  We'll revisit this part later.
         if (rrtype == RRType::OPT()) {
             if (section != Section::ADDITIONAL()) {
                 isc_throw(DNSMessageFORMERR,
                           "EDNS OPT RR found in an invalid section");
             }
-            if (remote_edns_ != NULL) {
+            if (edns_) {
                 isc_throw(DNSMessageFORMERR, "multiple EDNS OPT RR found");
             }
-            if (((ttl.getValue() & EDNSVERSION_MASK) >> 16) >
-                Message::EDNS_SUPPORTED_VERSION) {
-                // XXX: we should probably not reject the message yet, because
-                // it's better to let the requestor know the responder-side
-                // highest version as indicated in Section 4.6 of RFC2671.
-                // This is probably because why BIND 9 does the version check
-                // in the client code.
-                // This is a TODO item.  Right now we simply reject it.
-                const unsigned int ver =
-                    (ttl.getValue() & EDNSVERSION_MASK) >> 16;
-                isc_throw(DNSMessageBADVERS, "unsupported EDNS version: " <<
-                          ver);
-            }
-            if (name != Name::ROOT_NAME()) {
-                isc_throw(DNSMessageFORMERR,
-                          "invalid owner name for EDNS OPT RR");
-            }
-
-            remote_edns_ = RRsetPtr(new RRset(name, rrclass, rrtype, ttl));
-            remote_edns_->addRdata(rdata);
 
-            dnssec_ok_ = (((ttl.getValue() & EXTFLAG_MASK) & EXTFLAG_DO) != 0);
-            if (rrclass.getCode() > Message::DEFAULT_MAX_UDPSIZE) {
-                udpsize_ = rrclass.getCode();
-            }
-            rcode_ = Rcode(((ttl.getValue() & EXTRCODE_MASK) >> 20) |
-                           rcode_.getCode());
+            uint8_t extended_rcode;
+            edns_ = ConstEDNSPtr(createEDNSFromRR(name, rrclass, rrtype, ttl,
+                                                  *rdata, extended_rcode));
+            setRcode(Rcode(rcode_->getCode(), extended_rcode));
             continue;
-        }
-
-        vector<RRsetPtr>::iterator it =
-            find_if(rrsets_[sectionCodeToId(section)].begin(),
-                    rrsets_[sectionCodeToId(section)].end(),
-                    MatchRR(name, rrtype, rrclass));
-        if (it != rrsets_[sectionCodeToId(section)].end()) {
-            (*it)->setTTL(min((*it)->getTTL(), ttl));
-            (*it)->addRdata(rdata);
         } else {
-            RRsetPtr rrset = RRsetPtr(new RRset(name, rrclass, rrtype, ttl)); 
-            rrset->addRdata(rdata);
-            rrsets_[sectionCodeToId(section)].push_back(rrset);
+            vector<RRsetPtr>::iterator it =
+                find_if(rrsets_[sectionCodeToId(section)].begin(),
+                        rrsets_[sectionCodeToId(section)].end(),
+                        MatchRR(name, rrtype, rrclass));
+            if (it != rrsets_[sectionCodeToId(section)].end()) {
+                (*it)->setTTL(min((*it)->getTTL(), ttl));
+                (*it)->addRdata(rdata);
+            } else {
+                RRsetPtr rrset =
+                    RRsetPtr(new RRset(name, rrclass, rrtype, ttl)); 
+                rrset->addRdata(rdata);
+                rrsets_[sectionCodeToId(section)].push_back(rrset);
+            }
+            ++added;
         }
-        ++added;
     }
 
     return (added);
@@ -755,11 +626,20 @@ struct SectionFormatter {
 
 string
 Message::toText() const {
+    if (impl_->rcode_ == NULL) {
+        isc_throw(InvalidMessageOperation,
+                  "Message::toText() attempted without Rcode set");
+    }
+    if (impl_->opcode_ == NULL) {
+        isc_throw(InvalidMessageOperation,
+                  "Message::toText() attempted without Opcode set");
+    }
+
     string s;
 
     s += ";; ->>HEADER<<- opcode: " + impl_->opcode_->toText();
     // for simplicity we don't consider extended rcode (unlike BIND9)
-    s += ", status: " + impl_->rcode_.toText();
+    s += ", status: " + impl_->rcode_->toText();
     s += ", id: " + boost::lexical_cast<string>(impl_->qid_);
     s += "\n;; flags: ";
     if (getHeaderFlag(MessageFlag::QR()))
@@ -786,30 +666,14 @@ Message::toText() const {
         lexical_cast<string>(impl_->counts_[Section::AUTHORITY().getCode()]);
 
     unsigned int arcount = impl_->counts_[Section::ADDITIONAL().getCode()];
-    RRsetPtr edns_rrset;
-    if (!getHeaderFlag(MessageFlag::QR()) && impl_->remote_edns_ != NULL) {
-        edns_rrset = impl_->remote_edns_;
+    if (impl_->edns_ != NULL) {
         ++arcount;
     }
     s += ", ADDITIONAL: " + lexical_cast<string>(arcount) + "\n";
 
-    if (edns_rrset != NULL) {
+    if (impl_->edns_ != NULL) {
         s += "\n;; OPT PSEUDOSECTION:\n";
-        s += "; EDNS: version: ";
-        s += lexical_cast<string>(
-            (edns_rrset->getTTL().getValue() & 0x00ff0000) >> 16);
-        s += ", flags:";
-        if ((edns_rrset->getTTL().getValue() & 0x8000) != 0) {
-            s += " do";
-        }
-        const uint32_t mbz = edns_rrset->getTTL().getValue() & ~0x8000 & 0xffff;
-        if (mbz != 0) {
-            s += "; MBZ: " + lexical_cast<string>(mbz) + ", udp: ";
-        } else {
-            s += "; udp: " +
-                lexical_cast<string>(edns_rrset->getClass().getCode());
-        }
-        s += "\n";
+        s += impl_->edns_->toText();
     }
 
     if (!impl_->questions_.empty()) {
@@ -860,11 +724,7 @@ Message::makeResponse()
 
     impl_->mode_ = Message::RENDER;
 
-    impl_->dnssec_ok_ = false;
-    impl_->remote_udpsize_ = impl_->udpsize_;
-    impl_->local_edns_ = RRsetPtr();
-    impl_->udpsize_ = DEFAULT_MAX_UDPSIZE;
-
+    impl_->edns_ = EDNSPtr();
     impl_->flags_ &= MESSAGE_REPLYPRESERVE;
     setHeaderFlag(MessageFlag::QR());
 
@@ -1005,16 +865,6 @@ Message::endSection(const Section& section) const {
 }
 
 ostream&
-operator<<(ostream& os, const Opcode& opcode) {
-    return (os << opcode.toText());
-}
-
-ostream&
-operator<<(ostream& os, const Rcode& rcode) {
-    return (os << rcode.toText());
-}
-
-ostream&
 operator<<(ostream& os, const Message& message) {
     return (os << message.toText());
 }

+ 41 - 346
src/lib/dns/message.h

@@ -25,6 +25,7 @@
 
 #include <exceptions/exceptions.h>
 
+#include <dns/edns.h>
 #include <dns/question.h>
 #include <dns/rrset.h>
 
@@ -81,6 +82,8 @@ class InputBuffer;
 class MessageRenderer;
 class Message;
 class MessageImpl;
+class Opcode;
+class Rcode;
 
 template <typename T>
 struct SectionIteratorImpl;
@@ -159,310 +162,6 @@ MessageFlag::CD()
     return (f);
 }
 
-/// \brief The \c Opcode class objects represent standard OPCODEs
-/// of the header section of DNS messages.
-///
-/// Note: since there are only 15 possible values, it may make more sense to
-/// simply define an enum type to represent these values.
-///
-/// Constant objects are defined for standard flags.
-class Opcode {
-public:
-    uint16_t getCode() const { return (code_); }
-    bool operator==(const Opcode& other) const
-    { return (code_ == other.code_); }
-    bool operator!=(const Opcode& other) const
-    { return (code_ != other.code_); }
-    std::string toText() const;
-    static const Opcode& QUERY();
-    static const Opcode& IQUERY();
-    static const Opcode& STATUS();
-    static const Opcode& RESERVED3();
-    static const Opcode& NOTIFY();
-    static const Opcode& UPDATE();
-    static const Opcode& RESERVED6();
-    static const Opcode& RESERVED7();
-    static const Opcode& RESERVED8();
-    static const Opcode& RESERVED9();
-    static const Opcode& RESERVED10();
-    static const Opcode& RESERVED11();
-    static const Opcode& RESERVED12();
-    static const Opcode& RESERVED13();
-    static const Opcode& RESERVED14();
-    static const Opcode& RESERVED15();
-private:
-    Opcode(uint16_t code) : code_(code) {}
-    uint16_t code_;
-};
-
-inline const Opcode&
-Opcode::QUERY()
-{
-    static Opcode c(0);
-    return (c);
-}
-
-inline const Opcode&
-Opcode::IQUERY()
-{
-    static Opcode c(1);
-    return (c);
-}
-
-inline const Opcode&
-Opcode::STATUS()
-{
-    static Opcode c(2);
-    return (c);
-}
-
-inline const Opcode&
-Opcode::RESERVED3()
-{
-    static Opcode c(3);
-    return (c);
-}
-
-inline const Opcode&
-Opcode::NOTIFY()
-{
-    static Opcode c(4);
-    return (c);
-}
-
-inline const Opcode&
-Opcode::UPDATE()
-{
-    static Opcode c(5);
-    return (c);
-}
-
-inline const Opcode&
-Opcode::RESERVED6()
-{
-    static Opcode c(6);
-    return (c);
-}
-
-inline const Opcode&
-Opcode::RESERVED7()
-{
-    static Opcode c(7);
-    return (c);
-}
-
-inline const Opcode&
-Opcode::RESERVED8()
-{
-    static Opcode c(8);
-    return (c);
-}
-
-inline const Opcode&
-Opcode::RESERVED9()
-{
-    static Opcode c(9);
-    return (c);
-}
-
-inline const Opcode&
-Opcode::RESERVED10()
-{
-    static Opcode c(10);
-    return (c);
-}
-
-inline const Opcode&
-Opcode::RESERVED11()
-{
-    static Opcode c(11);
-    return (c);
-}
-
-inline const Opcode&
-Opcode::RESERVED12()
-{
-    static Opcode c(12);
-    return (c);
-}
-
-inline const Opcode&
-Opcode::RESERVED13()
-{
-    static Opcode c(13);
-    return (c);
-}
-
-inline const Opcode&
-Opcode::RESERVED14()
-{
-    static Opcode c(14);
-    return (c);
-}
-
-inline const Opcode&
-Opcode::RESERVED15()
-{
-    static Opcode c(15);
-    return (c);
-}
-
-/// \brief The \c Rcode class objects represent standard Response Codes
-/// (RCODEs) of the header section of DNS messages, and extended response
-/// codes as defined in the EDNS specification.
-///
-/// Constant objects are defined for standard flags.
-class Rcode {
-public:
-    Rcode(uint16_t code);
-    uint16_t getCode() const { return (code_); }
-    bool operator==(const Rcode& other) const { return (code_ == other.code_); }
-    bool operator!=(const Rcode& other) const { return (code_ != other.code_); }
-    std::string toText() const;
-    static const Rcode& NOERROR();
-    static const Rcode& FORMERR();
-    static const Rcode& SERVFAIL();
-    static const Rcode& NXDOMAIN();
-    static const Rcode& NOTIMP();
-    static const Rcode& REFUSED();
-    static const Rcode& YXDOMAIN();
-    static const Rcode& YXRRSET();
-    static const Rcode& NXRRSET();
-    static const Rcode& NOTAUTH();
-    static const Rcode& NOTZONE();
-    static const Rcode& RESERVED11();
-    static const Rcode& RESERVED12();
-    static const Rcode& RESERVED13();
-    static const Rcode& RESERVED14();
-    static const Rcode& RESERVED15();
-    // Extended Rcodes follow (EDNS required):
-    static const Rcode& BADVERS();
-private:
-    uint16_t code_;
-
-    // EDNS-extended RCODEs are 12-bit unsigned integers.
-    static const uint16_t MAX_RCODE = 0xfff;
-};
-
-inline const Rcode&
-Rcode::NOERROR()
-{
-    static Rcode c(0);
-    return (c);
-}
-
-inline const Rcode&
-Rcode::FORMERR()
-{
-    static Rcode c(1);
-    return (c);
-}
-
-inline const Rcode&
-Rcode::SERVFAIL()
-{
-    static Rcode c(2);
-    return (c);
-}
-
-inline const Rcode&
-Rcode::NXDOMAIN()
-{
-    static Rcode c(3);
-    return (c);
-}
-
-inline const Rcode&
-Rcode::NOTIMP()
-{
-    static Rcode c(4);
-    return (c);
-}
-
-inline const Rcode&
-Rcode::REFUSED()
-{
-    static Rcode c(5);
-    return (c);
-}
-
-inline const Rcode&
-Rcode::YXDOMAIN()
-{
-    static Rcode c(6);
-    return (c);
-}
-
-inline const Rcode&
-Rcode::YXRRSET()
-{
-    static Rcode c(7);
-    return (c);
-}
-
-inline const Rcode&
-Rcode::NXRRSET()
-{
-    static Rcode c(8);
-    return (c);
-}
-
-inline const Rcode&
-Rcode::NOTAUTH()
-{
-    static Rcode c(9);
-    return (c);
-}
-
-inline const Rcode&
-Rcode::NOTZONE()
-{
-    static Rcode c(10);
-    return (c);
-}
-
-inline const Rcode&
-Rcode::RESERVED11()
-{
-    static Rcode c(11);
-    return (c);
-}
-
-inline const Rcode&
-Rcode::RESERVED12()
-{
-    static Rcode c(12);
-    return (c);
-}
-
-inline const Rcode&
-Rcode::RESERVED13()
-{
-    static Rcode c(13);
-    return (c);
-}
-
-inline const Rcode&
-Rcode::RESERVED14()
-{
-    static Rcode c(14);
-    return (c);
-}
-
-inline const Rcode&
-Rcode::RESERVED15()
-{
-    static Rcode c(15);
-    return (c);
-}
-
-inline const Rcode&
-Rcode::BADVERS()
-{
-    static Rcode c(16);
-    return (c);
-}
-
 /// \brief The \c Section class objects represent DNS message sections such
 /// as the header, question, or answer.
 ///
@@ -627,45 +326,6 @@ public:
     /// \c setHeaderFlag() with an additional argument.
     void clearHeaderFlag(const MessageFlag& flag);
 
-    /// \brief Return whether the message sender indicates DNSSEC is supported.
-    /// If EDNS is included, this corresponds to the value of the DO bit.
-    /// Otherwise, DNSSEC is considered not supported.
-    bool isDNSSECSupported() const;
-
-    /// \brief Specify whether DNSSEC is supported in the message.
-    ///
-    /// Only allowed in the \c RENDER mode.
-    /// If EDNS is included in the message, the DO bit is set or cleared
-    /// according to the specified value of this method.
-    void setDNSSECSupported(bool on);
-
-    /// \brief Return the maximum buffer size of UDP messages for the sender
-    /// of the message.
-    ///
-    /// The semantics of this value is different based on the mode:
-    /// In the \c PARSE mode, it means the buffer size of the remote node;
-    /// in the \c RENDER mode, it means the buffer size of the local node.
-    ///
-    /// In either case, its value is the value of the UDP payload size field
-    /// of EDNS (when it's included) or \c DEFAULT_MAX_UDPSIZE.
-    ///
-    /// Note: this interface may be confusing and may have to be revisited.
-    uint16_t getUDPSize() const;
-
-    /// \brief Specify the maximum buffer size of UDP messages of the local
-    /// node.
-    ///
-    /// Only allowed in the \c RENDER mode.
-    /// If EDNS OPT RR is included in the message, its UDP payload size field
-    /// will be set to the specified value.
-    ///
-    /// Unless explicitly specified, \c DEFAULT_MAX_UDPSIZE will be assumed
-    /// for the maximum buffer size, regardless of whether EDNS OPT RR is
-    /// included or not.  This means if an application wants to send a message
-    /// with an EDNS OPT RR for specifying a larger UDP size, it must explicitly
-    /// specify the value using this method.
-    void setUDPSize(uint16_t size);
-
     /// \brief Return the query ID given in the header section of the message.
     qid_t getQid() const;
 
@@ -680,16 +340,27 @@ public:
     /// included).  In the \c PARSE mode, if the received message contains
     /// an EDNS OPT RR, the corresponding extended code is identified and
     /// returned.
+    ///
+    /// The message must have been properly parsed (in the case of the
+    /// \c PARSE mode) or an \c Rcode has been set (in the case of the
+    /// \c RENDER mode) beforehand.  Otherwise, an exception of class
+    /// \c InvalidMessageOperation will be thrown.
     const Rcode& getRcode() const;
 
-    /// \brief Return the Response Code of the message.
+    /// \brief Set the Response Code of the message.
     ///
     /// Only allowed in the \c RENDER mode.
+    ///
     /// If the specified code is an EDNS extended RCODE, an EDNS OPT RR will be
     /// included in the message.
     void setRcode(const Rcode& rcode);
 
     /// \brief Return the OPCODE given in the header section of the message.
+    ///
+    /// The message must have been properly parsed (in the case of the
+    /// \c PARSE mode) or an \c Opcode has been set (in the case of the
+    /// \c RENDER mode) beforehand.  Otherwise, an exception of class
+    /// \c InvalidMessageOperation will be thrown.
     const Opcode& getOpcode() const;
 
     /// \brief Set the OPCODE of the header section of the message.
@@ -697,6 +368,23 @@ public:
     /// Only allowed in the \c RENDER mode.
     void setOpcode(const Opcode& opcode);
 
+    /// \brief Return, if any, the EDNS associated with the message.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \return A shared pointer to the EDNS.  This will be a null shared
+    /// pointer if the message is not associated with EDNS.
+    ConstEDNSPtr getEDNS() const;
+
+    /// \brief Set EDNS for the message.
+    ///
+    /// Only allowed in the \c RENDER mode; otherwise an exception of class
+    /// \c InvalidMessageOperation will be thrown.
+    ///
+    /// \param edns A shared pointer to an \c EDNS object to be set in
+    /// \c Message.
+    void setEDNS(ConstEDNSPtr edns);
+
     /// \brief Returns the number of RRs contained in the given section.
     unsigned int getRRCount(const Section& section) const;
 
@@ -768,10 +456,19 @@ public:
     void makeResponse();
 
     /// \brief Convert the Message to a string.
+    ///
+    /// At least \c Opcode and \c Rcode must be validly set in the \c Message
+    /// (as a result of parse in the \c PARSE mode or by explicitly setting
+    /// in the \c RENDER mode);  otherwise, an exception of
+    /// class \c InvalidMessageOperation will be thrown.
     std::string toText() const;
 
     /// \brief Render the message in wire formant into a \c MessageRenderer
     /// object.
+    ///
+    /// This \c Message must be in the \c RENDER mode and both \c Opcode and
+    /// \c Rcode must have been set beforehand; otherwise, an exception of
+    /// class \c InvalidMessageOperation will be thrown.
     void toWire(MessageRenderer& renderer);
 
     /// \brief Parse the header section of the \c Message.
@@ -798,8 +495,6 @@ private:
     MessageImpl* impl_;
 };
 
-std::ostream& operator<<(std::ostream& os, const Opcode& opcode);
-std::ostream& operator<<(std::ostream& os, const Rcode& rcode);
 std::ostream& operator<<(std::ostream& os, const Message& message);
 }
 }

+ 69 - 0
src/lib/dns/opcode.cc

@@ -0,0 +1,69 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <string>
+#include <ostream>
+
+#include <exceptions/exceptions.h>
+
+#include <dns/opcode.h>
+
+using namespace std;
+
+namespace isc {
+namespace dns {
+namespace {
+const char *opcodetext[] = {
+    "QUERY",
+    "IQUERY",
+    "STATUS",
+    "RESERVED3",
+    "NOTIFY",
+    "UPDATE",
+    "RESERVED6",
+    "RESERVED7",
+    "RESERVED8",
+    "RESERVED9",
+    "RESERVED10",
+    "RESERVED11",
+    "RESERVED12",
+    "RESERVED13",
+    "RESERVED14",
+    "RESERVED15"
+};
+
+// OPCODEs are 4-bit values.  So 15 is the highest code.
+const uint8_t MAX_OPCODE = 15;
+}
+
+Opcode::Opcode(const uint8_t code) : code_(static_cast<CodeValue>(code)) {
+    if (code > MAX_OPCODE) {
+        isc_throw(OutOfRange,
+                  "DNS Opcode is too large to construct: " << code);
+    }
+}
+
+string
+Opcode::toText() const {
+    return (opcodetext[code_]);
+}
+
+ostream&
+operator<<(std::ostream& os, const Opcode& opcode) {
+    return (os << opcode.toText());
+}
+}
+}

+ 292 - 0
src/lib/dns/opcode.h

@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id$ */
+
+#include <stdint.h>
+
+#include <ostream>
+
+#ifndef __OPCODE_H
+#define __OPCODE_H 1
+
+namespace isc {
+namespace dns {
+
+/// \brief The \c Opcode class objects represent standard OPCODEs
+/// of the header section of DNS messages as defined in RFC1035.
+///
+/// This is a straightforward value class encapsulating the OPCODE code
+/// values.  Since OPCODEs are 4-bit integers that are used in limited
+/// places and it's unlikely that new code values will be assigned, we could
+/// represent them as simple integers (via constant variables or enums).
+/// However, we define a separate class so that we can benefit from C++
+/// type safety as much as possible.  For convenience we also provide
+/// an enum type for standard OPCDE values, but it is generally advisable
+/// to handle OPCODEs through this class.  In fact, public interfaces of
+/// this library uses this class to pass or return OPCODEs instead of the
+/// bare code values.
+class Opcode {
+public:
+    /// Constants for standard OPCODE values.
+    enum CodeValue {
+        QUERY_CODE = 0,         ///< 0: Standard query (RFC1035)
+        IQUERY_CODE = 1,        ///< 1: Inverse query (RFC1035)
+        STATUS_CODE = 2,        ///< 2: Server status request (RFC1035)
+        RESERVED3_CODE = 3,     ///< 3: Reserved for future use (RFC1035)
+        NOTIFY_CODE = 4,        ///< 4: Notify (RFC1996)
+        UPDATE_CODE = 5,        ///< 5: Dynamic update (RFC2136)
+        RESERVED6_CODE = 6,     ///< 6: Reserved for future use (RFC1035)
+        RESERVED7_CODE = 7,     ///< 7: Reserved for future use (RFC1035)
+        RESERVED8_CODE = 8,     ///< 8: Reserved for future use (RFC1035)
+        RESERVED9_CODE = 9,     ///< 9: Reserved for future use (RFC1035)
+        RESERVED10_CODE = 10,   ///< 10: Reserved for future use (RFC1035)
+        RESERVED11_CODE = 11,   ///< 11: Reserved for future use (RFC1035)
+        RESERVED12_CODE = 12,   ///< 12: Reserved for future use (RFC1035)
+        RESERVED13_CODE = 13,   ///< 13: Reserved for future use (RFC1035)
+        RESERVED14_CODE = 14,   ///< 14: Reserved for future use (RFC1035)
+        RESERVED15_CODE = 15    ///< 15: Reserved for future use (RFC1035)
+    };
+
+    /// \name Constructors and Destructor
+    ///
+    /// We use the default versions of destructor, copy constructor,
+    /// and assignment operator.
+    ///
+    /// The default constructor is hidden as a result of defining the other
+    /// constructors.  This is intentional; we don't want to allow an
+    /// \c Opcode object to be constructed with an invalid state.
+    //@{
+    /// \brief Constructor from the code value.
+    ///
+    /// Since OPCODEs are 4-bit values, parameters larger than 15 are invalid.
+    /// If \c code is larger than 15 an exception of class \c isc::OutOfRange
+    /// will be thrown.
+    ///
+    /// \param code The underlying code value of the \c Opcode.
+    explicit Opcode(const uint8_t code);
+    //@}
+
+    /// \brief Returns the \c Opcode code value.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \return The underlying code value corresponding to the \c Opcode.
+    CodeValue getCode() const { return (code_); }
+
+    /// \brief Return true iff two Opcodes are equal.
+    ///
+    /// Two Opcodes are equal iff their type codes are equal.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \param other the \c Opcode object to compare against.
+    /// \return true if the two Opcodes are equal; otherwise false.
+    bool equals(const Opcode& other) const
+    { return (code_ == other.code_); }
+
+    /// \brief Same as \c equals().
+    bool operator==(const Opcode& other) const { return (equals(other)); }
+    
+    /// \brief Return true iff two Opcodes are not equal.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \param other the \c Opcode object to compare against.
+    /// \return true if the two Opcodes are not equal; otherwise false.
+    bool nequals(const Opcode& other) const
+    { return (code_ != other.code_); }
+
+    /// \brief Same as \c nequals().
+    bool operator!=(const Opcode& other) const { return (nequals(other)); }
+
+    /// \brief Convert the \c Opcode to a string.
+    ///
+    /// This method returns a string representation of the "mnemonic' used
+    /// for the enum and constant objects.  For example, the string for
+    /// code value 0 is "QUERY", etc.
+    ///
+    /// If resource allocation for the string fails, a corresponding standard
+    /// exception will be thrown.
+    ///
+    /// \return A string representation of the \c Opcode.
+    std::string toText() const;
+
+    /// A constant object for the QUERY Opcode.
+    static const Opcode& QUERY();
+
+    /// A constant object for the IQUERY Opcode.
+    static const Opcode& IQUERY();
+
+    /// A constant object for the STATUS Opcode.
+    static const Opcode& STATUS();
+
+    /// A constant object for a reserved (code 3) Opcode.
+    static const Opcode& RESERVED3();
+
+    /// A constant object for the NOTIFY Opcode.
+    static const Opcode& NOTIFY();
+
+    /// A constant object for the UPDATE Opcode.
+    static const Opcode& UPDATE();
+
+    /// A constant object for a reserved (code 6) Opcode.
+    static const Opcode& RESERVED6();
+
+    /// A constant object for a reserved (code 7) Opcode.
+    static const Opcode& RESERVED7();
+
+    /// A constant object for a reserved (code 8) Opcode.
+    static const Opcode& RESERVED8();
+
+    /// A constant object for a reserved (code 9) Opcode.
+    static const Opcode& RESERVED9();
+
+    /// A constant object for a reserved (code 10) Opcode.
+    static const Opcode& RESERVED10();
+
+    /// A constant object for a reserved (code 11) Opcode.
+    static const Opcode& RESERVED11();
+
+    /// A constant object for a reserved (code 12) Opcode.
+    static const Opcode& RESERVED12();
+
+    /// A constant object for a reserved (code 13) Opcode.
+    static const Opcode& RESERVED13();
+
+    /// A constant object for a reserved (code 14) Opcode.
+    static const Opcode& RESERVED14();
+
+    /// A constant object for a reserved (code 15) Opcode.
+    static const Opcode& RESERVED15();
+private:
+    CodeValue code_;
+};
+
+inline const Opcode&
+Opcode::QUERY() {
+    static Opcode c(0);
+    return (c);
+}
+
+inline const Opcode&
+Opcode::IQUERY() {
+    static Opcode c(1);
+    return (c);
+}
+
+inline const Opcode&
+Opcode::STATUS() {
+    static Opcode c(2);
+    return (c);
+}
+
+inline const Opcode&
+Opcode::RESERVED3() {
+    static Opcode c(3);
+    return (c);
+}
+
+inline const Opcode&
+Opcode::NOTIFY() {
+    static Opcode c(4);
+    return (c);
+}
+
+inline const Opcode&
+Opcode::UPDATE() {
+    static Opcode c(5);
+    return (c);
+}
+
+inline const Opcode&
+Opcode::RESERVED6() {
+    static Opcode c(6);
+    return (c);
+}
+
+inline const Opcode&
+Opcode::RESERVED7() {
+    static Opcode c(7);
+    return (c);
+}
+
+inline const Opcode&
+Opcode::RESERVED8() {
+    static Opcode c(8);
+    return (c);
+}
+
+inline const Opcode&
+Opcode::RESERVED9() {
+    static Opcode c(9);
+    return (c);
+}
+
+inline const Opcode&
+Opcode::RESERVED10() {
+    static Opcode c(10);
+    return (c);
+}
+
+inline const Opcode&
+Opcode::RESERVED11() {
+    static Opcode c(11);
+    return (c);
+}
+
+inline const Opcode&
+Opcode::RESERVED12() {
+    static Opcode c(12);
+    return (c);
+}
+
+inline const Opcode&
+Opcode::RESERVED13() {
+    static Opcode c(13);
+    return (c);
+}
+
+inline const Opcode&
+Opcode::RESERVED14() {
+    static Opcode c(14);
+    return (c);
+}
+
+inline const Opcode&
+Opcode::RESERVED15() {
+    static Opcode c(15);
+    return (c);
+}
+
+/// \brief Insert the \c Opcode as a string into stream.
+///
+/// This method convert \c opcode into a string and inserts it into the
+/// output stream \c os.
+///
+/// \param os A \c std::ostream object on which the insertion operation is
+/// performed.
+/// \param opcode A reference to an \c Opcode object output by the operation.
+/// \return A reference to the same \c std::ostream object referenced by
+/// parameter \c os after the insertion operation.
+std::ostream& operator<<(std::ostream& os, const Opcode& opcode);
+}
+}
+#endif  // OPCODE_H
+
+// Local Variables: 
+// mode: c++
+// End: 

+ 4 - 1
src/lib/dns/python/Makefile.am

@@ -1,4 +1,4 @@
-SUBDIRS = tests
+SUBDIRS = . tests
 
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CXXFLAGS = $(B10_CXXFLAGS)
@@ -11,10 +11,13 @@ pydnspp_la_LDFLAGS = $(PYTHON_LDFLAGS)
 # directly included from source files, so these don't have their own
 # rules
 EXTRA_DIST = pydnspp_common.h
+EXTRA_DIST += edns_python.cc
 EXTRA_DIST += messagerenderer_python.cc
 EXTRA_DIST += message_python.cc
 EXTRA_DIST += rrclass_python.cc
 EXTRA_DIST += name_python.cc
+EXTRA_DIST += opcode_python.cc
+EXTRA_DIST += rcode_python.cc
 EXTRA_DIST += rrset_python.cc
 EXTRA_DIST += question_python.cc
 EXTRA_DIST += rrttl_python.cc

+ 373 - 0
src/lib/dns/python/edns_python.cc

@@ -0,0 +1,373 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <cassert>
+
+#include <dns/edns.h>
+
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+
+//
+// Definition of the classes
+//
+
+// For each class, we need a struct, a helper functions (init, destroy,
+// and static wrappers around the methods we export), a list of methods,
+// and a type description
+
+namespace {
+//
+// EDNS
+//
+
+// The s_* Class simply covers one instantiation of the object
+
+class s_EDNS : public PyObject {
+public:
+    EDNS* edns;
+};
+
+//
+// We declare the functions here, the definitions are below
+// the type definition of the object, since both can use the other
+//
+
+// General creation and destruction
+int EDNS_init(s_EDNS* self, PyObject* args);
+void EDNS_destroy(s_EDNS* self);
+
+// These are the functions we export
+PyObject* EDNS_toText(const s_EDNS* self);
+// This is a second version of toText, we need one where the argument
+// is a PyObject*, for the str() function in python.
+PyObject* EDNS_str(PyObject* self);
+PyObject* EDNS_toWire(const s_EDNS* self, PyObject* args);
+PyObject* EDNS_getVersion(const s_EDNS* self);
+PyObject* EDNS_getDNSSECAwareness(const s_EDNS* self);
+PyObject* EDNS_setDNSSECAwareness(s_EDNS* self, PyObject* args);
+PyObject* EDNS_getUDPSize(const s_EDNS* self);
+PyObject* EDNS_setUDPSize(s_EDNS* self, PyObject* args);
+PyObject* EDNS_createFromRR(const s_EDNS* null_self, PyObject* args);
+
+// This list contains the actual set of functions we have in
+// python. Each entry has
+// 1. Python method name
+// 2. Our static function here
+// 3. Argument type
+// 4. Documentation
+PyMethodDef EDNS_methods[] = {
+    { "to_text", reinterpret_cast<PyCFunction>(EDNS_toText), METH_NOARGS,
+      "Returns the string representation" },
+    { "to_wire", reinterpret_cast<PyCFunction>(EDNS_toWire), METH_VARARGS,
+      "Converts the EDNS object to wire format.\n"
+      "The argument can be either a MessageRenderer or an object that "
+      "implements the sequence interface. If the object is mutable "
+      "(for instance a bytearray()), the wire data is added in-place.\n"
+      "If it is not (for instance a bytes() object), a new object is "
+      "returned" },
+    { "get_version",
+      reinterpret_cast<PyCFunction>(EDNS_getVersion), METH_NOARGS,
+      "Returns the version of EDNS." },
+    { "get_dnssec_awareness",
+      reinterpret_cast<PyCFunction>(EDNS_getDNSSECAwareness), METH_NOARGS,
+      "Returns whether the message sender is DNSSEC aware." },
+    { "set_dnssec_awareness",
+      reinterpret_cast<PyCFunction>(EDNS_setDNSSECAwareness), METH_VARARGS,
+      "Specifies whether the sender of the message containing this "
+      "EDNS is DNSSEC aware." },
+    { "get_udp_size",
+      reinterpret_cast<PyCFunction>(EDNS_getUDPSize), METH_NOARGS,
+      "Return the maximum buffer size of UDP messages for the sender "
+      "of the message." },
+    { "set_udp_size",
+      reinterpret_cast<PyCFunction>(EDNS_setUDPSize), METH_VARARGS,
+      "Specify the maximum buffer size of UDP messages that use this EDNS." },
+    { "create_from_rr",
+      reinterpret_cast<PyCFunction>(EDNS_createFromRR),
+      METH_VARARGS | METH_STATIC,
+      "Create a new EDNS object from a set of RR parameters, also providing "
+      "the extended RCODE value." },
+    { NULL, NULL, 0, NULL }
+};
+
+// This defines the complete type for reflection in python and
+// parsing of PyObject* to s_EDNS
+// Most of the functions are not actually implemented and NULL here.
+PyTypeObject edns_type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "libdns_python.EDNS",
+    sizeof(s_EDNS),                     // tp_basicsize
+    0,                                  // tp_itemsize
+    (destructor)EDNS_destroy,           // tp_dealloc
+    NULL,                               // tp_print
+    NULL,                               // tp_getattr
+    NULL,                               // tp_setattr
+    NULL,                               // tp_reserved
+    NULL,                               // tp_repr
+    NULL,                               // tp_as_number
+    NULL,                               // tp_as_sequence
+    NULL,                               // tp_as_mapping
+    NULL,                               // tp_hash 
+    NULL,                               // tp_call
+    EDNS_str,                           // tp_str
+    NULL,                               // tp_getattro
+    NULL,                               // tp_setattro
+    NULL,                               // tp_as_buffer
+    Py_TPFLAGS_DEFAULT,                 // tp_flags
+    "The EDNS class encapsulates DNS extensions "
+    "provided by the EDNSx protocol.",
+    NULL,                               // tp_traverse
+    NULL,                               // tp_clear
+    NULL,                               // tp_richcompare
+    0,                                  // tp_weaklistoffset
+    NULL,                               // tp_iter
+    NULL,                               // tp_iternext
+    EDNS_methods,                       // tp_methods
+    NULL,                               // tp_members
+    NULL,                               // tp_getset
+    NULL,                               // tp_base
+    NULL,                               // tp_dict
+    NULL,                               // tp_descr_get
+    NULL,                               // tp_descr_set
+    0,                                  // tp_dictoffset
+    (initproc)EDNS_init,                // tp_init
+    NULL,                               // tp_alloc
+    PyType_GenericNew,                  // tp_new
+    NULL,                               // tp_free
+    NULL,                               // tp_is_gc
+    NULL,                               // tp_bases
+    NULL,                               // tp_mro
+    NULL,                               // tp_cache
+    NULL,                               // tp_subclasses
+    NULL,                               // tp_weaklist
+    NULL,                               // tp_del
+    0                                   // tp_version_tag
+};
+
+EDNS*
+createFromRR(const Name& name, const RRClass& rrclass, const RRType& rrtype,
+             const RRTTL& rrttl, const Rdata& rdata, uint8_t& extended_rcode)
+{
+    try {
+        return (createEDNSFromRR(name, rrclass, rrtype, rrttl, rdata,
+                                 extended_rcode));
+    } catch (const isc::InvalidParameter& ex) {
+        PyErr_SetString(po_InvalidParameter, ex.what());
+    } catch (const DNSMessageFORMERR& ex) {
+        PyErr_SetString(po_DNSMessageFORMERR, ex.what());
+    } catch (const DNSMessageBADVERS& ex) {
+        PyErr_SetString(po_DNSMessageBADVERS, ex.what());
+    } catch (...) {
+        PyErr_SetString(po_IscException, "Unexpected exception");
+    }
+
+    return (NULL);
+}
+int
+EDNS_init(s_EDNS* self, PyObject* args) {
+    uint8_t version = EDNS::SUPPORTED_VERSION;
+    const s_Name* name;
+    const s_RRClass* rrclass;
+    const s_RRType* rrtype;
+    const s_RRTTL* rrttl;
+    const s_Rdata* rdata;
+
+    if (PyArg_ParseTuple(args, "|b", &version)) {
+        try {
+            self->edns = new EDNS(version);
+        } catch (const isc::InvalidParameter& ex) {
+            PyErr_SetString(po_InvalidParameter, ex.what());
+            return (-1);
+        } catch (...) {
+            PyErr_SetString(po_IscException, "Unexpected exception");
+            return (-1);
+        }
+        return (0);
+    } else if (PyArg_ParseTuple(args, "O!O!O!O!O!", &name_type, &name,
+                                &rrclass_type, &rrclass, &rrtype_type, &rrtype,
+                                &rrttl_type, &rrttl, &rdata_type, &rdata)) {
+        // We use createFromRR() even if we don't need to know extended_rcode
+        // in this context so that we can share the try-catch logic with
+        // EDNS_createFromRR() (see below).
+        uint8_t extended_rcode;
+        self->edns = createFromRR(*name->name, *rrclass->rrclass,
+                                  *rrtype->rrtype, *rrttl->rrttl,
+                                  *rdata->rdata, extended_rcode);
+        return (self->edns != NULL ? 0 : -1);
+    }
+
+    PyErr_Clear();
+    PyErr_SetString(PyExc_TypeError, "Invalid arguments to EDNS constructor");
+
+    return (-1);
+}
+
+void
+EDNS_destroy(s_EDNS* const self) {
+    delete self->edns;
+    self->edns = NULL;
+    Py_TYPE(self)->tp_free(self);
+}
+
+PyObject*
+EDNS_toText(const s_EDNS* const self) {
+    // Py_BuildValue makes python objects from native data
+    return (Py_BuildValue("s", self->edns->toText().c_str()));
+}
+
+PyObject*
+EDNS_str(PyObject* const self) {
+    // Simply call the to_text method we already defined
+    return (PyObject_CallMethod(self,
+                                const_cast<char*>("to_text"),
+                                const_cast<char*>("")));
+}
+
+PyObject*
+EDNS_toWire(const s_EDNS* const self, PyObject* args) {
+    PyObject* bytes;
+    uint8_t extended_rcode;
+    s_MessageRenderer* renderer;
+
+    if (PyArg_ParseTuple(args, "Ob", &bytes, &extended_rcode) &&
+        PySequence_Check(bytes)) {
+        PyObject* bytes_o = bytes;
+        
+        OutputBuffer buffer(0);
+        self->edns->toWire(buffer, extended_rcode);
+        PyObject* rd_bytes = PyBytes_FromStringAndSize(
+            static_cast<const char*>(buffer.getData()), buffer.getLength());
+        PyObject* result = PySequence_InPlaceConcat(bytes_o, rd_bytes);
+        // We need to release the object we temporarily created here
+        // to prevent memory leak
+        Py_DECREF(rd_bytes);
+        return (result);
+    } else if (PyArg_ParseTuple(args, "O!b", &messagerenderer_type,
+                                &renderer, &extended_rcode)) {
+        const unsigned int n = self->edns->toWire(*renderer->messagerenderer,
+                                                  extended_rcode);
+
+        return (Py_BuildValue("I", n));
+    }
+    PyErr_Clear();
+    PyErr_SetString(PyExc_TypeError, "Incorrect arguments for EDNS.to_wire()");
+    return (NULL);
+}
+
+PyObject*
+EDNS_getVersion(const s_EDNS* const self) {
+    return (Py_BuildValue("B", self->edns->getVersion()));
+}
+
+PyObject*
+EDNS_getDNSSECAwareness(const s_EDNS* const self) {
+    if (self->edns->getDNSSECAwareness()) {
+        Py_RETURN_TRUE;
+    } else {
+        Py_RETURN_FALSE;
+    }
+}
+
+PyObject*
+EDNS_setDNSSECAwareness(s_EDNS* self, PyObject* args) {
+    const PyObject *b;
+    if (!PyArg_ParseTuple(args, "O!", &PyBool_Type, &b)) {
+        return (NULL);
+    }
+    self->edns->setDNSSECAwareness(b == Py_True);
+    Py_RETURN_NONE;
+}
+
+PyObject*
+EDNS_getUDPSize(const s_EDNS* const self) {
+    return (Py_BuildValue("I", self->edns->getUDPSize()));
+}
+
+PyObject*
+EDNS_setUDPSize(s_EDNS* self, PyObject* args) {
+    unsigned int size;
+    if (!PyArg_ParseTuple(args, "I", &size)) {
+        return (NULL);
+    }
+    if (size > 65535) {
+        PyErr_SetString(PyExc_OverflowError,
+                        "UDP size is not an unsigned 16-bit integer");
+        return (NULL);
+    }
+    self->edns->setUDPSize(size);
+    Py_RETURN_NONE;
+}
+
+PyObject*
+EDNS_createFromRR(const s_EDNS* null_self, PyObject* args) {
+    const s_Name* name;
+    const s_RRClass* rrclass;
+    const s_RRType* rrtype;
+    const s_RRTTL* rrttl;
+    const s_Rdata* rdata;
+    s_EDNS* edns_obj = NULL;
+
+    assert(null_self == NULL);
+
+    if (PyArg_ParseTuple(args, "O!O!O!O!O!", &name_type, &name,
+                         &rrclass_type, &rrclass, &rrtype_type, &rrtype,
+                         &rrttl_type, &rrttl, &rdata_type, &rdata)) {
+        uint8_t extended_rcode;
+        edns_obj = PyObject_New(s_EDNS, &edns_type);
+        if (edns_obj == NULL) {
+            return (NULL);
+        }
+
+        edns_obj->edns = createFromRR(*name->name, *rrclass->rrclass,
+                                      *rrtype->rrtype, *rrttl->rrttl,
+                                      *rdata->rdata, extended_rcode);
+        if (edns_obj->edns != NULL) {
+            PyObject* extrcode_obj = Py_BuildValue("B", extended_rcode);
+            return (Py_BuildValue("OO", edns_obj, extrcode_obj));
+        }
+        
+        Py_DECREF(edns_obj);
+        return (NULL);
+    }
+
+    PyErr_Clear();
+    PyErr_SetString(PyExc_TypeError,
+                    "Incorrect arguments for EDNS.create_from_rr()");
+    return (NULL);
+}
+
+} // end of anonymous namespace
+// end of EDNS
+
+// Module Initialization, all statics are initialized here
+bool
+initModulePart_EDNS(PyObject* mod) {
+    // We initialize the static description object with PyType_Ready(),
+    // then add it to the module. This is not just a check! (leaving
+    // this out results in segmentation faults)
+    if (PyType_Ready(&edns_type) < 0) {
+        return (false);
+    }
+    Py_INCREF(&edns_type);
+    void* p = &edns_type;
+    PyModule_AddObject(mod, "EDNS", static_cast<PyObject*>(p));
+
+    addClassVariable(edns_type, "SUPPORTED_VERSION",
+                     Py_BuildValue("B", EDNS::SUPPORTED_VERSION));
+
+    return (true);
+}

+ 74 - 678
src/lib/dns/python/message_python.cc

@@ -26,7 +26,6 @@ static PyObject* po_MessageTooShort;
 static PyObject* po_InvalidMessageSection;
 static PyObject* po_InvalidMessageOperation;
 static PyObject* po_InvalidMessageUDPSize;
-static PyObject* po_DNSMessageBADVERS;
 
 //
 // Definition of the classes
@@ -120,7 +119,6 @@ static PyTypeObject messageflag_type = {
     0                                   // tp_version_tag
 };
 
-
 static int
 MessageFlag_init(s_MessageFlag* self UNUSED_PARAM,
                  PyObject* args UNUSED_PARAM)
@@ -191,585 +189,6 @@ MessageFlag_CD(s_MessageFlag* self UNUSED_PARAM) {
 // End of MessageFlag wrapper
 //
 
-
-//
-// Opcode
-//
-class s_Opcode : public PyObject {
-public:
-    const Opcode* opcode;
-};
-
-static int Opcode_init(s_Opcode* self, PyObject* args);
-static void Opcode_destroy(s_Opcode* self);
-
-static PyObject* Opcode_getCode(s_Opcode* self);
-static PyObject* Opcode_toText(s_Opcode* self);
-static PyObject* Opcode_str(PyObject* self);
-static PyObject* Opcode_QUERY(s_Opcode* self);
-static PyObject* Opcode_IQUERY(s_Opcode* self);
-static PyObject* Opcode_STATUS(s_Opcode* self);
-static PyObject* Opcode_RESERVED3(s_Opcode* self);
-static PyObject* Opcode_NOTIFY(s_Opcode* self);
-static PyObject* Opcode_UPDATE(s_Opcode* self);
-static PyObject* Opcode_RESERVED6(s_Opcode* self);
-static PyObject* Opcode_RESERVED7(s_Opcode* self);
-static PyObject* Opcode_RESERVED8(s_Opcode* self);
-static PyObject* Opcode_RESERVED9(s_Opcode* self);
-static PyObject* Opcode_RESERVED10(s_Opcode* self);
-static PyObject* Opcode_RESERVED11(s_Opcode* self);
-static PyObject* Opcode_RESERVED12(s_Opcode* self);
-static PyObject* Opcode_RESERVED13(s_Opcode* self);
-static PyObject* Opcode_RESERVED14(s_Opcode* self);
-static PyObject* Opcode_RESERVED15(s_Opcode* self);
-static PyObject* Opcode_richcmp(s_Opcode* self, s_Opcode* other, int op);
-
-static PyMethodDef Opcode_methods[] = {
-    { "get_code", reinterpret_cast<PyCFunction>(Opcode_getCode), METH_NOARGS, "Returns the code value" },
-    { "to_text", reinterpret_cast<PyCFunction>(Opcode_toText), METH_NOARGS, "Returns the text representation" },
-    { "QUERY", reinterpret_cast<PyCFunction>(Opcode_QUERY), METH_NOARGS | METH_STATIC, "Creates a QUERY Opcode" },
-    { "IQUERY", reinterpret_cast<PyCFunction>(Opcode_IQUERY), METH_NOARGS | METH_STATIC, "Creates a IQUERY Opcode" },
-    { "STATUS", reinterpret_cast<PyCFunction>(Opcode_STATUS), METH_NOARGS | METH_STATIC, "Creates a STATUS Opcode" },
-    { "RESERVED3", reinterpret_cast<PyCFunction>(Opcode_RESERVED3), METH_NOARGS | METH_STATIC, "Creates a RESERVED3 Opcode" },
-    { "NOTIFY", reinterpret_cast<PyCFunction>(Opcode_NOTIFY), METH_NOARGS | METH_STATIC, "Creates a NOTIFY Opcode" },
-    { "UPDATE", reinterpret_cast<PyCFunction>(Opcode_UPDATE), METH_NOARGS | METH_STATIC, "Creates a UPDATE Opcode" },
-    { "RESERVED6", reinterpret_cast<PyCFunction>(Opcode_RESERVED6), METH_NOARGS | METH_STATIC, "Creates a RESERVED Opcode" },
-    { "RESERVED7", reinterpret_cast<PyCFunction>(Opcode_RESERVED7), METH_NOARGS | METH_STATIC, "Creates a RESERVED Opcode" },
-    { "RESERVED8", reinterpret_cast<PyCFunction>(Opcode_RESERVED8), METH_NOARGS | METH_STATIC, "Creates a RESERVED Opcode" },
-    { "RESERVED9", reinterpret_cast<PyCFunction>(Opcode_RESERVED9), METH_NOARGS | METH_STATIC, "Creates a RESERVED Opcode" },
-    { "RESERVED10", reinterpret_cast<PyCFunction>(Opcode_RESERVED10), METH_NOARGS | METH_STATIC, "Creates a RESERVED Opcode" },
-    { "RESERVED11", reinterpret_cast<PyCFunction>(Opcode_RESERVED11), METH_NOARGS | METH_STATIC, "Creates a RESERVED Opcode" },
-    { "RESERVED12", reinterpret_cast<PyCFunction>(Opcode_RESERVED12), METH_NOARGS | METH_STATIC, "Creates a RESERVED Opcode" },
-    { "RESERVED13", reinterpret_cast<PyCFunction>(Opcode_RESERVED13), METH_NOARGS | METH_STATIC, "Creates a RESERVED Opcode" },
-    { "RESERVED14", reinterpret_cast<PyCFunction>(Opcode_RESERVED14), METH_NOARGS | METH_STATIC, "Creates a RESERVED Opcode" },
-    { "RESERVED15", reinterpret_cast<PyCFunction>(Opcode_RESERVED15), METH_NOARGS | METH_STATIC, "Creates a RESERVED Opcode" },
-    { NULL, NULL, 0, NULL }
-};
-
-static PyTypeObject opcode_type = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "pydnspp.Opcode",
-    sizeof(s_Opcode),                   // tp_basicsize
-    0,                                  // tp_itemsize
-    (destructor)Opcode_destroy,         // tp_dealloc
-    NULL,                               // tp_print
-    NULL,                               // tp_getattr
-    NULL,                               // tp_setattr
-    NULL,                               // tp_reserved
-    NULL,                               // tp_repr
-    NULL,                               // tp_as_number
-    NULL,                               // tp_as_sequence
-    NULL,                               // tp_as_mapping
-    NULL,                               // tp_hash 
-    NULL,                               // tp_call
-    Opcode_str,                         // tp_str
-    NULL,                               // tp_getattro
-    NULL,                               // tp_setattro
-    NULL,                               // tp_as_buffer
-    Py_TPFLAGS_DEFAULT,                 // tp_flags
-    "The Opcode class objects represent standard OPCODEs "
-    "of the header section of DNS messages.",
-    NULL,                               // tp_traverse
-    NULL,                               // tp_clear
-    (richcmpfunc)Opcode_richcmp,        // tp_richcompare
-    0,                                  // tp_weaklistoffset
-    NULL,                               // tp_iter
-    NULL,                               // tp_iternext
-    Opcode_methods,                     // tp_methods
-    NULL,                               // tp_members
-    NULL,                               // tp_getset
-    NULL,                               // tp_base
-    NULL,                               // tp_dict
-    NULL,                               // tp_descr_get
-    NULL,                               // tp_descr_set
-    0,                                  // tp_dictoffset
-    (initproc)Opcode_init,              // tp_init
-    NULL,                               // tp_alloc
-    PyType_GenericNew,                  // tp_new
-    NULL,                               // tp_free
-    NULL,                               // tp_is_gc
-    NULL,                               // tp_bases
-    NULL,                               // tp_mro
-    NULL,                               // tp_cache
-    NULL,                               // tp_subclasses
-    NULL,                               // tp_weaklist
-    NULL,                               // tp_del
-    0                                   // tp_version_tag
-};
-
-
-static int
-Opcode_init(s_Opcode* self UNUSED_PARAM, PyObject* args UNUSED_PARAM) {
-    PyErr_SetString(PyExc_NotImplementedError,
-                    "Opcode can't be built directly");
-    return (-1);
-}
-
-static void
-Opcode_destroy(s_Opcode* self) {
-    // We only use the consts from Opcode, so don't
-    // delete self->opcode here
-    self->opcode = NULL;
-    Py_TYPE(self)->tp_free(self);
-}
-
-static PyObject*
-Opcode_getCode(s_Opcode* self) {
-    return (Py_BuildValue("I", self->opcode->getCode()));
-}
-
-static PyObject*
-Opcode_toText(s_Opcode* self) {
-    return (Py_BuildValue("s", self->opcode->toText().c_str()));
-}
-
-static PyObject*
-Opcode_str(PyObject* self) {
-    // Simply call the to_text method we already defined
-    return (PyObject_CallMethod(self,
-                               const_cast<char*>("to_text"),
-                                const_cast<char*>("")));
-}
-
-static PyObject*
-Opcode_createStatic(const Opcode& opcode) {
-    s_Opcode* ret = PyObject_New(s_Opcode, &opcode_type);
-    if (ret != NULL) {
-        ret->opcode = &opcode;
-    }
-    return (ret);
-}
-
-static PyObject*
-Opcode_QUERY(s_Opcode* self UNUSED_PARAM) {
-    return (Opcode_createStatic(Opcode::QUERY()));
-}
-
-static PyObject*
-Opcode_IQUERY(s_Opcode* self UNUSED_PARAM) {
-    return (Opcode_createStatic(Opcode::IQUERY()));
-}
-
-static PyObject*
-Opcode_STATUS(s_Opcode* self UNUSED_PARAM) {
-    return (Opcode_createStatic(Opcode::STATUS()));
-}
-
-static PyObject*
-Opcode_RESERVED3(s_Opcode* self UNUSED_PARAM) {
-    return (Opcode_createStatic(Opcode::RESERVED3()));
-}
-
-static PyObject*
-Opcode_NOTIFY(s_Opcode* self UNUSED_PARAM) {
-    return (Opcode_createStatic(Opcode::NOTIFY()));
-}
-
-static PyObject*
-Opcode_UPDATE(s_Opcode* self UNUSED_PARAM) {
-    return (Opcode_createStatic(Opcode::UPDATE()));
-}
-
-static PyObject*
-Opcode_RESERVED6(s_Opcode* self UNUSED_PARAM) {
-    return (Opcode_createStatic(Opcode::RESERVED6()));
-}
-
-static PyObject*
-Opcode_RESERVED7(s_Opcode* self UNUSED_PARAM) {
-    return (Opcode_createStatic(Opcode::RESERVED7()));
-}
-
-static PyObject*
-Opcode_RESERVED8(s_Opcode* self UNUSED_PARAM) {
-    return (Opcode_createStatic(Opcode::RESERVED8()));
-}
-
-static PyObject*
-Opcode_RESERVED9(s_Opcode* self UNUSED_PARAM) {
-    return (Opcode_createStatic(Opcode::RESERVED9()));
-}
-
-static PyObject*
-Opcode_RESERVED10(s_Opcode* self UNUSED_PARAM) {
-    return (Opcode_createStatic(Opcode::RESERVED10()));
-}
-
-static PyObject*
-Opcode_RESERVED11(s_Opcode* self UNUSED_PARAM) {
-    return (Opcode_createStatic(Opcode::RESERVED11()));
-}
-
-static PyObject*
-Opcode_RESERVED12(s_Opcode* self UNUSED_PARAM) {
-    return (Opcode_createStatic(Opcode::RESERVED12()));
-}
-
-static PyObject*
-Opcode_RESERVED13(s_Opcode* self UNUSED_PARAM) {
-    return (Opcode_createStatic(Opcode::RESERVED13()));
-}
-
-static PyObject*
-Opcode_RESERVED14(s_Opcode* self UNUSED_PARAM) {
-    return (Opcode_createStatic(Opcode::RESERVED14()));
-}
-
-static PyObject*
-Opcode_RESERVED15(s_Opcode* self UNUSED_PARAM) {
-    return (Opcode_createStatic(Opcode::RESERVED15()));
-}
-
-static PyObject* 
-Opcode_richcmp(s_Opcode* self, s_Opcode* other, int op) {
-    bool c = false;
-
-    // Check for null and if the types match. If different type,
-    // simply return False
-    if (!other || (self->ob_type != other->ob_type)) {
-        Py_RETURN_FALSE;
-    }
-
-    // Only equals and not equals here, unorderable type
-    switch (op) {
-    case Py_LT:
-        PyErr_SetString(PyExc_TypeError, "Unorderable type; Opcode");
-        return (NULL);
-        break;
-    case Py_LE:
-        PyErr_SetString(PyExc_TypeError, "Unorderable type; Opcode");
-        return (NULL);
-        break;
-    case Py_EQ:
-        c = (*self->opcode == *other->opcode);
-        break;
-    case Py_NE:
-        c = (*self->opcode != *other->opcode);
-        break;
-    case Py_GT:
-        PyErr_SetString(PyExc_TypeError, "Unorderable type; Opcode");
-        return (NULL);
-        break;
-    case Py_GE:
-        PyErr_SetString(PyExc_TypeError, "Unorderable type; Opcode");
-        return (NULL);
-        break;
-    }
-    if (c)
-        Py_RETURN_TRUE;
-    else
-        Py_RETURN_FALSE;
-}
-
-//
-// End of Opcode wrapper
-//
-
-//
-// Rcode
-//
-
-// We added a helper variable static_code here
-// Since we can create Rcodes dynamically with Rcode(int), but also
-// use the static globals (Rcode::NOERROR() etc), we use this
-// variable to see if the code came from one of the latter, in which
-// case Rcode_destroy should not free it (the other option is to
-// allocate new Rcodes for every use of the static ones, but this
-// seems more efficient).
-class s_Rcode : public PyObject {
-public:
-    const Rcode* rcode;
-    bool static_code;
-};
-
-static int Rcode_init(s_Rcode* self, PyObject* args);
-static void Rcode_destroy(s_Rcode* self);
-
-static PyObject* Rcode_getCode(s_Rcode* self);
-static PyObject* Rcode_toText(s_Rcode* self);
-static PyObject* Rcode_str(PyObject* self);
-static PyObject* Rcode_NOERROR(s_Rcode* self);
-static PyObject* Rcode_FORMERR(s_Rcode* self);
-static PyObject* Rcode_SERVFAIL(s_Rcode* self);
-static PyObject* Rcode_NXDOMAIN(s_Rcode* self);
-static PyObject* Rcode_NOTIMP(s_Rcode* self);
-static PyObject* Rcode_REFUSED(s_Rcode* self);
-static PyObject* Rcode_YXDOMAIN(s_Rcode* self);
-static PyObject* Rcode_YXRRSET(s_Rcode* self);
-static PyObject* Rcode_NXRRSET(s_Rcode* self);
-static PyObject* Rcode_NOTAUTH(s_Rcode* self);
-static PyObject* Rcode_NOTZONE(s_Rcode* self);
-static PyObject* Rcode_RESERVED11(s_Rcode* self);
-static PyObject* Rcode_RESERVED12(s_Rcode* self);
-static PyObject* Rcode_RESERVED13(s_Rcode* self);
-static PyObject* Rcode_RESERVED14(s_Rcode* self);
-static PyObject* Rcode_RESERVED15(s_Rcode* self);
-static PyObject* Rcode_BADVERS(s_Rcode* self);
-static PyObject* Rcode_richcmp(s_Rcode* self, s_Rcode* other, int op);
-
-static PyMethodDef Rcode_methods[] = {
-    { "get_code", reinterpret_cast<PyCFunction>(Rcode_getCode), METH_NOARGS, "Returns the code value" },
-    { "to_text", reinterpret_cast<PyCFunction>(Rcode_toText), METH_NOARGS, "Returns the text representation" },
-    { "NOERROR", reinterpret_cast<PyCFunction>(Rcode_NOERROR), METH_NOARGS | METH_STATIC, "Creates a NOERROR Rcode" },
-    { "FORMERR", reinterpret_cast<PyCFunction>(Rcode_FORMERR), METH_NOARGS | METH_STATIC, "Creates a FORMERR Rcode" },
-    { "SERVFAIL", reinterpret_cast<PyCFunction>(Rcode_SERVFAIL), METH_NOARGS | METH_STATIC, "Creates a SERVFAIL Rcode" },
-    { "NXDOMAIN", reinterpret_cast<PyCFunction>(Rcode_NXDOMAIN), METH_NOARGS | METH_STATIC, "Creates a NXDOMAIN Rcode" },
-    { "NOTIMP", reinterpret_cast<PyCFunction>(Rcode_NOTIMP), METH_NOARGS | METH_STATIC, "Creates a NOTIMP Rcode" },
-    { "REFUSED", reinterpret_cast<PyCFunction>(Rcode_REFUSED), METH_NOARGS | METH_STATIC, "Creates a REFUSED Rcode" },
-    { "YXDOMAIN", reinterpret_cast<PyCFunction>(Rcode_YXDOMAIN), METH_NOARGS | METH_STATIC, "Creates a RESERVED Rcode" },
-    { "YXRRSET", reinterpret_cast<PyCFunction>(Rcode_YXRRSET), METH_NOARGS | METH_STATIC, "Creates a RESERVED Rcode" },
-    { "NXRRSET", reinterpret_cast<PyCFunction>(Rcode_NXRRSET), METH_NOARGS | METH_STATIC, "Creates a RESERVED Rcode" },
-    { "NOTAUTH", reinterpret_cast<PyCFunction>(Rcode_NOTAUTH), METH_NOARGS | METH_STATIC, "Creates a RESERVED Rcode" },
-    { "NOTZONE", reinterpret_cast<PyCFunction>(Rcode_NOTZONE), METH_NOARGS | METH_STATIC, "Creates a RESERVED Rcode" },
-    { "RESERVED11", reinterpret_cast<PyCFunction>(Rcode_RESERVED11), METH_NOARGS | METH_STATIC, "Creates a RESERVED Rcode" },
-    { "RESERVED12", reinterpret_cast<PyCFunction>(Rcode_RESERVED12), METH_NOARGS | METH_STATIC, "Creates a RESERVED Rcode" },
-    { "RESERVED13", reinterpret_cast<PyCFunction>(Rcode_RESERVED13), METH_NOARGS | METH_STATIC, "Creates a RESERVED Rcode" },
-    { "RESERVED14", reinterpret_cast<PyCFunction>(Rcode_RESERVED14), METH_NOARGS | METH_STATIC, "Creates a RESERVED Rcode" },
-    { "RESERVED15", reinterpret_cast<PyCFunction>(Rcode_RESERVED15), METH_NOARGS | METH_STATIC, "Creates a RESERVED Rcode" },
-    { "BADVERS", reinterpret_cast<PyCFunction>(Rcode_BADVERS), METH_NOARGS | METH_STATIC, "Creates a BADVERS Rcode" },
-    { NULL, NULL, 0, NULL }
-};
-
-static PyTypeObject rcode_type = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "pydnspp.Rcode",
-    sizeof(s_Rcode),                    // tp_basicsize
-    0,                                  // tp_itemsize
-    (destructor)Rcode_destroy,          // tp_dealloc
-    NULL,                               // tp_print
-    NULL,                               // tp_getattr
-    NULL,                               // tp_setattr
-    NULL,                               // tp_reserved
-    NULL,                               // tp_repr
-    NULL,                               // tp_as_number
-    NULL,                               // tp_as_sequence
-    NULL,                               // tp_as_mapping
-    NULL,                               // tp_hash 
-    NULL,                               // tp_call
-    Rcode_str,                          // tp_str
-    NULL,                               // tp_getattro
-    NULL,                               // tp_setattro
-    NULL,                               // tp_as_buffer
-    Py_TPFLAGS_DEFAULT,                 // tp_flags
-    "The Rcode class objects represent standard RCODEs"
-    "of the header section of DNS messages.",
-    NULL,                               // tp_traverse
-    NULL,                               // tp_clear
-    (richcmpfunc)Rcode_richcmp,         // tp_richcompare
-    0,                                  // tp_weaklistoffset
-    NULL,                               // tp_iter
-    NULL,                               // tp_iternext
-    Rcode_methods,                      // tp_methods
-    NULL,                               // tp_members
-    NULL,                               // tp_getset
-    NULL,                               // tp_base
-    NULL,                               // tp_dict
-    NULL,                               // tp_descr_get
-    NULL,                               // tp_descr_set
-    0,                                  // tp_dictoffset
-    (initproc)Rcode_init,               // tp_init
-    NULL,                               // tp_alloc
-    PyType_GenericNew,                  // tp_new
-    NULL,                               // tp_free
-    NULL,                               // tp_is_gc
-    NULL,                               // tp_bases
-    NULL,                               // tp_mro
-    NULL,                               // tp_cache
-    NULL,                               // tp_subclasses
-    NULL,                               // tp_weaklist
-    NULL,                               // tp_del
-    0                                   // tp_version_tag
-};
-
-
-static int
-Rcode_init(s_Rcode* self UNUSED_PARAM, PyObject* args UNUSED_PARAM) {
-    uint16_t code = 0;
-    if (PyArg_ParseTuple(args, "h", &code)) {
-        try {
-            self->rcode = new Rcode(code);
-            self->static_code = false;
-        } catch (const isc::OutOfRange&) {
-            PyErr_SetString(PyExc_OverflowError,
-                            "rcode out of range");
-            return (-1);
-        }
-        return (0);
-    } else {
-        return (-1);
-    }
-}
-
-static void
-Rcode_destroy(s_Rcode* self) {
-    // Depending on whether we created the rcode or are referring
-    // to a global static one, we do or do not delete self->rcode here
-    if (!self->static_code) {
-        delete self->rcode;
-    }
-    self->rcode = NULL;
-    Py_TYPE(self)->tp_free(self);
-}
-
-static PyObject*
-Rcode_getCode(s_Rcode* self) {
-    return (Py_BuildValue("I", self->rcode->getCode()));
-}
-
-static PyObject*
-Rcode_toText(s_Rcode* self) {
-    return (Py_BuildValue("s", self->rcode->toText().c_str()));
-}
-
-static PyObject*
-Rcode_str(PyObject* self) {
-    // Simply call the to_text method we already defined
-    return (PyObject_CallMethod(self,
-                               const_cast<char*>("to_text"),
-                                const_cast<char*>("")));
-}
-
-static PyObject*
-Rcode_createStatic(const Rcode& rcode) {
-    s_Rcode* ret = PyObject_New(s_Rcode, &rcode_type);
-    if (ret != NULL) {
-        ret->rcode = &rcode;
-        ret->static_code = true;
-    }
-    return (ret);
-}
-
-static PyObject*
-Rcode_NOERROR(s_Rcode* self UNUSED_PARAM) {
-    return (Rcode_createStatic(Rcode::NOERROR()));
-}
-
-static PyObject*
-Rcode_FORMERR(s_Rcode* self UNUSED_PARAM) {
-    return (Rcode_createStatic(Rcode::FORMERR()));
-}
-
-static PyObject*
-Rcode_SERVFAIL(s_Rcode* self UNUSED_PARAM) {
-    return (Rcode_createStatic(Rcode::SERVFAIL()));
-}
-
-static PyObject*
-Rcode_NXDOMAIN(s_Rcode* self UNUSED_PARAM) {
-    return (Rcode_createStatic(Rcode::NXDOMAIN()));
-}
-
-static PyObject*
-Rcode_NOTIMP(s_Rcode* self UNUSED_PARAM) {
-    return (Rcode_createStatic(Rcode::NOTIMP()));
-}
-
-static PyObject*
-Rcode_REFUSED(s_Rcode* self UNUSED_PARAM) {
-    return (Rcode_createStatic(Rcode::REFUSED()));
-}
-
-static PyObject*
-Rcode_YXDOMAIN(s_Rcode* self UNUSED_PARAM) {
-    return (Rcode_createStatic(Rcode::YXDOMAIN()));
-}
-
-static PyObject*
-Rcode_YXRRSET(s_Rcode* self UNUSED_PARAM) {
-    return (Rcode_createStatic(Rcode::YXRRSET()));
-}
-
-static PyObject*
-Rcode_NXRRSET(s_Rcode* self UNUSED_PARAM) {
-    return (Rcode_createStatic(Rcode::NXRRSET()));
-}
-
-static PyObject*
-Rcode_NOTAUTH(s_Rcode* self UNUSED_PARAM) {
-    return (Rcode_createStatic(Rcode::NOTAUTH()));
-}
-
-static PyObject*
-Rcode_NOTZONE(s_Rcode* self UNUSED_PARAM) {
-    return (Rcode_createStatic(Rcode::NOTZONE()));
-}
-
-static PyObject*
-Rcode_RESERVED11(s_Rcode* self UNUSED_PARAM) {
-    return (Rcode_createStatic(Rcode::RESERVED11()));
-}
-
-static PyObject*
-Rcode_RESERVED12(s_Rcode* self UNUSED_PARAM) {
-    return (Rcode_createStatic(Rcode::RESERVED12()));
-}
-
-static PyObject*
-Rcode_RESERVED13(s_Rcode* self UNUSED_PARAM) {
-    return (Rcode_createStatic(Rcode::RESERVED13()));
-}
-
-static PyObject*
-Rcode_RESERVED14(s_Rcode* self UNUSED_PARAM) {
-    return (Rcode_createStatic(Rcode::RESERVED14()));
-}
-
-static PyObject*
-Rcode_RESERVED15(s_Rcode* self UNUSED_PARAM) {
-    return (Rcode_createStatic(Rcode::RESERVED15()));
-}
-
-static PyObject*
-Rcode_BADVERS(s_Rcode* self UNUSED_PARAM) {
-    return (Rcode_createStatic(Rcode::BADVERS()));
-}
-
-static PyObject* 
-Rcode_richcmp(s_Rcode* self, s_Rcode* other, int op) {
-    bool c = false;
-
-    // Check for null and if the types match. If different type,
-    // simply return False
-    if (!other || (self->ob_type != other->ob_type)) {
-        Py_RETURN_FALSE;
-    }
-
-    // Only equals and not equals here, unorderable type
-    switch (op) {
-    case Py_LT:
-        PyErr_SetString(PyExc_TypeError, "Unorderable type; Rcode");
-        return (NULL);
-        break;
-    case Py_LE:
-        PyErr_SetString(PyExc_TypeError, "Unorderable type; Rcode");
-        return (NULL);
-        break;
-    case Py_EQ:
-        c = (*self->rcode == *other->rcode);
-        break;
-    case Py_NE:
-        c = (*self->rcode != *other->rcode);
-        break;
-    case Py_GT:
-        PyErr_SetString(PyExc_TypeError, "Unorderable type; Rcode");
-        return (NULL);
-        break;
-    case Py_GE:
-        PyErr_SetString(PyExc_TypeError, "Unorderable type; Rcode");
-        return (NULL);
-        break;
-    }
-    if (c)
-        Py_RETURN_TRUE;
-    else
-        Py_RETURN_FALSE;
-}
-
-//
-// End of Rcode wrapper
-//
-
-
 //
 // Section
 //
@@ -973,16 +392,14 @@ static void Message_destroy(s_Message* self);
 static PyObject* Message_getHeaderFlag(s_Message* self, PyObject* args);
 static PyObject* Message_setHeaderFlag(s_Message* self, PyObject* args);
 static PyObject* Message_clearHeaderFlag(s_Message* self, PyObject* args);
-static PyObject* Message_isDNSSECSupported(s_Message* self);
-static PyObject* Message_setDNSSECSupported(s_Message* self, PyObject* args);
-static PyObject* Message_getUDPSize(s_Message* self);
-static PyObject* Message_setUDPSize(s_Message* self, PyObject* args);
 static PyObject* Message_getQid(s_Message* self);
 static PyObject* Message_setQid(s_Message* self, PyObject* args);
 static PyObject* Message_getRcode(s_Message* self);
 static PyObject* Message_setRcode(s_Message* self, PyObject* args);
 static PyObject* Message_getOpcode(s_Message* self);
 static PyObject* Message_setOpcode(s_Message* self, PyObject* args);
+static PyObject* Message_getEDNS(s_Message* self);
+static PyObject* Message_setEDNS(s_Message* self, PyObject* args);
 static PyObject* Message_getRRCount(s_Message* self, PyObject* args);
 // use direct iterators for these? (or simply lists for now?)
 static PyObject* Message_getQuestion(s_Message* self);
@@ -1019,35 +436,6 @@ static PyMethodDef Message_methods[] = {
       "Sets the specified header flag bit to 0. The message must be in "
       "RENDER mode. If not, an InvalidMessageOperation is raised. "
       "Takes a MessageFlag object as the only argument." },
-    { "is_dnssec_supported", reinterpret_cast<PyCFunction>(Message_isDNSSECSupported), METH_NOARGS,
-      "Returns True if the message sender indicates DNSSEC is supported. "
-      "If EDNS is included, this corresponds to the value of the DO bit. "
-      "Otherwise, DNSSEC is considered not supported." },
-    { "set_dnssec_supported", reinterpret_cast<PyCFunction>(Message_setDNSSECSupported), METH_VARARGS,
-      "Specify whether DNSSEC is supported in the message. "
-      "The message must be in RENDER mode. If not, an "
-      "InvalidMessageOperation is raised."
-      "If EDNS is included in the message, the DO bit is set or cleared "
-      "according to given argument (True or False) of this method."},
-    { "get_udp_size", reinterpret_cast<PyCFunction>(Message_getUDPSize), METH_NOARGS,
-      "Return the maximum buffer size of UDP messages for the sender "
-      "of the message.\n\n"
-      "The semantics of this value is different based on the mode:\n"
-      "In the PARSE mode, it means the buffer size of the remote node;\n"
-      "in the RENDER mode, it means the buffer size of the local node.\n\n"
-      "In either case, its value is the value of the UDP payload size field "
-      "of EDNS (when it's included) or DEFAULT_MAX_UDPSIZE." },
-    { "set_udp_size", reinterpret_cast<PyCFunction>(Message_setUDPSize), METH_VARARGS,
-      "Specify the maximum buffer size of UDP messages of the local "
-      "node. If the message is not in RENDER mode, an "
-      "InvalidMessageOperation is raised.\n\n"
-      "If EDNS OPT RR is included in the message, its UDP payload size field "
-      "will be set to the specified value.\n"
-      "Unless explicitly specified, DEFAULT_MAX_UDPSIZE will be assumed "
-      "for the maximum buffer size, regardless of whether EDNS OPT RR is "
-      "included or not.  This means if an application wants to send a message "
-      "with an EDNS OPT RR for specifying a larger UDP size, it must explicitly "
-      "specify the value using this method. "},
     { "get_qid", reinterpret_cast<PyCFunction>(Message_getQid), METH_NOARGS,
       "Returns the query id" },
     { "set_qid", reinterpret_cast<PyCFunction>(Message_setQid), METH_VARARGS,
@@ -1066,6 +454,12 @@ static PyMethodDef Message_methods[] = {
       "Sets the message opcode (an Opcode object).\n"
       "If the message is not in RENDER mode, an "
       "InvalidMessageOperation is raised."},
+    { "get_edns", reinterpret_cast<PyCFunction>(Message_getEDNS), METH_NOARGS,
+      "Return, if any, the EDNS associated with the message."
+    },
+    { "set_edns", reinterpret_cast<PyCFunction>(Message_setEDNS), METH_VARARGS,
+      "Set EDNS for the message."
+    },
     { "get_rr_count", reinterpret_cast<PyCFunction>(Message_getRRCount), METH_VARARGS,
       "Returns the number of RRs contained in the given section." },
     { "get_question", reinterpret_cast<PyCFunction>(Message_getQuestion), METH_NOARGS,
@@ -1248,57 +642,6 @@ Message_clearHeaderFlag(s_Message* self, PyObject* args) {
 }
 
 static PyObject*
-Message_isDNSSECSupported(s_Message* self) {
-    if (self->message->isDNSSECSupported()) {
-        Py_RETURN_TRUE;
-    } else {
-        Py_RETURN_FALSE;
-    }
-}
-
-static PyObject*
-Message_setDNSSECSupported(s_Message* self, PyObject* args) {
-    PyObject *b;
-    if (!PyArg_ParseTuple(args, "O!", &PyBool_Type, &b)) {
-        return (NULL);
-    }
-    try {
-        if (b == Py_True) {
-            self->message->setDNSSECSupported(true);
-        } else {
-            self->message->setDNSSECSupported(false);
-        }
-        Py_RETURN_NONE;
-    } catch (const InvalidMessageOperation& imo) {
-        PyErr_SetString(po_InvalidMessageOperation, imo.what());
-        return (NULL);
-    }
-}
-
-static PyObject*
-Message_getUDPSize(s_Message* self) {
-    return (Py_BuildValue("I", self->message->getUDPSize()));
-}
-
-static PyObject*
-Message_setUDPSize(s_Message* self, PyObject* args) {
-    uint16_t size;
-    if (!PyArg_ParseTuple(args, "H", &size)) {
-        return (NULL);
-    }
-    try {
-        self->message->setUDPSize(size);
-        Py_RETURN_NONE;
-    } catch (const InvalidMessageUDPSize& imus) {
-        PyErr_SetString(po_InvalidMessageUDPSize, imus.what());
-        return (NULL);
-    } catch (const InvalidMessageOperation& imo) {
-        PyErr_SetString(po_InvalidMessageOperation, imo.what());
-        return (NULL);
-    }
-}
-
-static PyObject*
 Message_getQid(s_Message* self) {
     return (Py_BuildValue("I", self->message->getQid()));
 }
@@ -1324,12 +667,18 @@ Message_getRcode(s_Message* self) {
 
     rcode = static_cast<s_Rcode*>(rcode_type.tp_alloc(&rcode_type, 0));
     if (rcode != NULL) {
-        rcode->rcode = new Rcode(self->message->getRcode());
-        if (rcode->rcode == NULL)
-          {
+        rcode->rcode = NULL;
+        try {
+            rcode->rcode = new Rcode(self->message->getRcode());
+        } catch (const InvalidMessageOperation& imo) {
+            PyErr_SetString(po_InvalidMessageOperation, imo.what());
+        } catch (...) {
+            PyErr_SetString(po_IscException, "Unexpected exception");
+        }
+        if (rcode->rcode == NULL) {
             Py_DECREF(rcode);
             return (NULL);
-          }
+        }
     }
 
     return (rcode);
@@ -1356,15 +705,18 @@ Message_getOpcode(s_Message* self) {
 
     opcode = static_cast<s_Opcode*>(opcode_type.tp_alloc(&opcode_type, 0));
     if (opcode != NULL) {
-        // Note that we do not new and delete for opcodes.
-        // all rcodes point to the statics defined in
-        // message.cc
-        opcode->opcode = &self->message->getOpcode();
-        if (opcode->opcode == NULL)
-          {
+        opcode->opcode = NULL;
+        try {
+            opcode->opcode = new Opcode(self->message->getOpcode());
+        } catch (const InvalidMessageOperation& imo) {
+            PyErr_SetString(po_InvalidMessageOperation, imo.what());
+        } catch (...) {
+            PyErr_SetString(po_IscException, "Unexpected exception");
+        }
+        if (opcode->opcode == NULL) {
             Py_DECREF(opcode);
             return (NULL);
-          }
+        }
     }
 
     return (opcode);
@@ -1386,6 +738,41 @@ Message_setOpcode(s_Message* self, PyObject* args) {
 }
 
 static PyObject*
+Message_getEDNS(s_Message* self) {
+    s_EDNS* edns;
+    EDNS* edns_body;
+    ConstEDNSPtr src = self->message->getEDNS();
+
+    if (!src) {
+        Py_RETURN_NONE;
+    }
+    if ((edns_body = new(nothrow) EDNS(*src)) == NULL) {
+        return (PyErr_NoMemory());
+    }
+    edns = static_cast<s_EDNS*>(opcode_type.tp_alloc(&edns_type, 0));
+    if (edns != NULL) {
+        edns->edns = edns_body;
+    }
+
+    return (edns);
+}
+
+static PyObject*
+Message_setEDNS(s_Message* self, PyObject* args) {
+    s_EDNS* edns;
+    if (!PyArg_ParseTuple(args, "O!", &edns_type, &edns)) {
+        return (NULL);
+    }
+    try {
+        self->message->setEDNS(EDNSPtr(new EDNS(*edns->edns)));
+        Py_RETURN_NONE;
+    } catch (const InvalidMessageOperation& imo) {
+        PyErr_SetString(po_InvalidMessageOperation, imo.what());
+        return (NULL);
+    }
+}
+
+static PyObject*
 Message_getRRCount(s_Message* self, PyObject* args) {
     s_Section *section;
     if (!PyArg_ParseTuple(args, "O!", &section_type, &section)) {
@@ -1520,7 +907,16 @@ Message_makeResponse(s_Message* self) {
 static PyObject*
 Message_toText(s_Message* self) {
     // Py_BuildValue makes python objects from native data
-    return (Py_BuildValue("s", self->message->toText().c_str()));
+    try {
+        return (Py_BuildValue("s", self->message->toText().c_str()));
+    } catch (const InvalidMessageOperation& imo) {
+        PyErr_Clear();
+        PyErr_SetString(po_InvalidMessageOperation, imo.what());
+        return (NULL);
+    } catch (...) {
+        PyErr_SetString(po_IscException, "Unexpected exception");
+        return (NULL);
+    }
 }
 
 static PyObject*
@@ -1535,7 +931,7 @@ static PyObject*
 Message_toWire(s_Message* self, PyObject* args) {
     s_MessageRenderer* mr;
     
-    if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, (PyObject**) &mr)) {
+    if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
         try {
             self->message->toWire(*mr->messagerenderer);
             // If we return NULL it is seen as an error, so use this for

+ 10 - 1
src/lib/dns/python/messagerenderer_python.cc

@@ -42,7 +42,7 @@ static PyObject* MessageRenderer_getLengthLimit(s_MessageRenderer* self);
 // TODO: set/get compressmode
 static PyObject* MessageRenderer_setTruncated(s_MessageRenderer* self);
 static PyObject* MessageRenderer_setLengthLimit(s_MessageRenderer* self, PyObject* args);
-
+static PyObject* MessageRenderer_clear(s_MessageRenderer* self);
 
 static PyMethodDef MessageRenderer_methods[] = {
     { "get_data", reinterpret_cast<PyCFunction>(MessageRenderer_getData), METH_NOARGS,
@@ -57,6 +57,9 @@ static PyMethodDef MessageRenderer_methods[] = {
       "Sets truncated to true" },
     { "set_length_limit", reinterpret_cast<PyCFunction>(MessageRenderer_setLengthLimit), METH_VARARGS,
       "Sets the length limit of the data to the given number" },
+    { "clear", reinterpret_cast<PyCFunction>(MessageRenderer_clear),
+      METH_NOARGS,
+      "Clear the internal buffer and other internal resources." },
     { NULL, NULL, 0, NULL }
 };
 
@@ -175,6 +178,12 @@ MessageRenderer_setLengthLimit(s_MessageRenderer* self,
     Py_RETURN_NONE;
 }
 
+static PyObject*
+MessageRenderer_clear(s_MessageRenderer* self) {
+    self->messagerenderer->clear();
+    Py_RETURN_NONE;
+}
+
 // end of MessageRenderer
 
 

+ 4 - 4
src/lib/dns/python/name_python.cc

@@ -421,7 +421,7 @@ Name_toWire(s_Name* self, PyObject* args) {
         // to prevent memory leak
         Py_DECREF(name_bytes);
         return (result);
-    } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, (PyObject**) &mr)) {
+    } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
         self->name->toWire(*mr->messagerenderer);
         // If we return NULL it is seen as an error, so use this for
         // None returns
@@ -437,7 +437,7 @@ static PyObject*
 Name_compare(s_Name* self, PyObject* args) {
     s_Name* other;
 
-    if (!PyArg_ParseTuple(args, "O!", &name_type, (PyObject* *) &other))
+    if (!PyArg_ParseTuple(args, "O!", &name_type, &other))
         return (NULL);
 
     s_NameComparisonResult* ret = PyObject_New(s_NameComparisonResult, &name_comparison_result_type);
@@ -452,7 +452,7 @@ static PyObject*
 Name_equals(s_Name* self, PyObject* args) {
     s_Name* other;
 
-    if (!PyArg_ParseTuple(args, "O!", &name_type, (PyObject* *) &other))
+    if (!PyArg_ParseTuple(args, "O!", &name_type, &other))
         return (NULL);
 
     if (self->name->equals(*other->name))
@@ -565,7 +565,7 @@ static PyObject*
 Name_concatenate(s_Name* self, PyObject* args) {
     s_Name* other;
 
-    if (!PyArg_ParseTuple(args, "O!", &name_type, (PyObject**) &other))
+    if (!PyArg_ParseTuple(args, "O!", &name_type, &other))
         return (NULL);
 
     s_Name* ret = PyObject_New(s_Name, &name_type);

+ 392 - 0
src/lib/dns/python/opcode_python.cc

@@ -0,0 +1,392 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <dns/opcode.h>
+
+using namespace isc::dns;
+
+//
+// Declaration of the custom exceptions (None for this class)
+
+//
+// Definition of the classes
+//
+
+// For each class, we need a struct, a helper functions (init, destroy,
+// and static wrappers around the methods we export), a list of methods,
+// and a type description
+
+namespace {
+//
+// Opcode
+//
+class s_Opcode : public PyObject {
+public:
+    s_Opcode() : opcode(NULL), static_code(false) {}
+    const Opcode* opcode;
+    bool static_code;
+};
+
+int Opcode_init(s_Opcode* const self, PyObject* args);
+void Opcode_destroy(s_Opcode* const self);
+
+PyObject* Opcode_getCode(const s_Opcode* const self);
+PyObject* Opcode_toText(const s_Opcode* const self);
+PyObject* Opcode_str(PyObject* self);
+PyObject* Opcode_QUERY(const s_Opcode* self);
+PyObject* Opcode_IQUERY(const s_Opcode* self);
+PyObject* Opcode_STATUS(const s_Opcode* self);
+PyObject* Opcode_RESERVED3(const s_Opcode* self);
+PyObject* Opcode_NOTIFY(const s_Opcode* self);
+PyObject* Opcode_UPDATE(const s_Opcode* self);
+PyObject* Opcode_RESERVED6(const s_Opcode* self);
+PyObject* Opcode_RESERVED7(const s_Opcode* self);
+PyObject* Opcode_RESERVED8(const s_Opcode* self);
+PyObject* Opcode_RESERVED9(const s_Opcode* self);
+PyObject* Opcode_RESERVED10(const s_Opcode* self);
+PyObject* Opcode_RESERVED11(const s_Opcode* self);
+PyObject* Opcode_RESERVED12(const s_Opcode* self);
+PyObject* Opcode_RESERVED13(const s_Opcode* self);
+PyObject* Opcode_RESERVED14(const s_Opcode* self);
+PyObject* Opcode_RESERVED15(const s_Opcode* self);
+PyObject* Opcode_richcmp(const s_Opcode* const self,
+                         const s_Opcode* const other, int op);
+
+PyMethodDef Opcode_methods[] = {
+    { "get_code", reinterpret_cast<PyCFunction>(Opcode_getCode), METH_NOARGS,
+      "Returns the code value" },
+    { "to_text", reinterpret_cast<PyCFunction>(Opcode_toText), METH_NOARGS,
+      "Returns the text representation" },
+    { "QUERY", reinterpret_cast<PyCFunction>(Opcode_QUERY),
+      METH_NOARGS | METH_STATIC, "Creates a QUERY Opcode" },
+    { "IQUERY", reinterpret_cast<PyCFunction>(Opcode_IQUERY),
+      METH_NOARGS | METH_STATIC, "Creates a IQUERY Opcode" },
+    { "STATUS", reinterpret_cast<PyCFunction>(Opcode_STATUS),
+      METH_NOARGS | METH_STATIC, "Creates a STATUS Opcode" },
+    { "RESERVED3", reinterpret_cast<PyCFunction>(Opcode_RESERVED3),
+      METH_NOARGS | METH_STATIC, "Creates a RESERVED3 Opcode" },
+    { "NOTIFY", reinterpret_cast<PyCFunction>(Opcode_NOTIFY),
+      METH_NOARGS | METH_STATIC, "Creates a NOTIFY Opcode" },
+    { "UPDATE", reinterpret_cast<PyCFunction>(Opcode_UPDATE),
+      METH_NOARGS | METH_STATIC, "Creates a UPDATE Opcode" },
+    { "RESERVED6", reinterpret_cast<PyCFunction>(Opcode_RESERVED6),
+      METH_NOARGS | METH_STATIC, "Creates a RESERVED6 Opcode" },
+    { "RESERVED7", reinterpret_cast<PyCFunction>(Opcode_RESERVED7),
+      METH_NOARGS | METH_STATIC, "Creates a RESERVED7 Opcode" },
+    { "RESERVED8", reinterpret_cast<PyCFunction>(Opcode_RESERVED8),
+      METH_NOARGS | METH_STATIC, "Creates a RESERVED8 Opcode" },
+    { "RESERVED9", reinterpret_cast<PyCFunction>(Opcode_RESERVED9),
+      METH_NOARGS | METH_STATIC, "Creates a RESERVED9 Opcode" },
+    { "RESERVED10", reinterpret_cast<PyCFunction>(Opcode_RESERVED10),
+      METH_NOARGS | METH_STATIC, "Creates a RESERVED10 Opcode" },
+    { "RESERVED11", reinterpret_cast<PyCFunction>(Opcode_RESERVED11),
+      METH_NOARGS | METH_STATIC, "Creates a RESERVED11 Opcode" },
+    { "RESERVED12", reinterpret_cast<PyCFunction>(Opcode_RESERVED12),
+      METH_NOARGS | METH_STATIC, "Creates a RESERVED12 Opcode" },
+    { "RESERVED13", reinterpret_cast<PyCFunction>(Opcode_RESERVED13),
+      METH_NOARGS | METH_STATIC, "Creates a RESERVED13 Opcode" },
+    { "RESERVED14", reinterpret_cast<PyCFunction>(Opcode_RESERVED14),
+      METH_NOARGS | METH_STATIC, "Creates a RESERVED14 Opcode" },
+    { "RESERVED15", reinterpret_cast<PyCFunction>(Opcode_RESERVED15),
+      METH_NOARGS | METH_STATIC, "Creates a RESERVED15 Opcode" },
+    { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject opcode_type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "pydnspp.Opcode",
+    sizeof(s_Opcode),                   // tp_basicsize
+    0,                                  // tp_itemsize
+    (destructor)Opcode_destroy,         // tp_dealloc
+    NULL,                               // tp_print
+    NULL,                               // tp_getattr
+    NULL,                               // tp_setattr
+    NULL,                               // tp_reserved
+    NULL,                               // tp_repr
+    NULL,                               // tp_as_number
+    NULL,                               // tp_as_sequence
+    NULL,                               // tp_as_mapping
+    NULL,                               // tp_hash 
+    NULL,                               // tp_call
+    Opcode_str,                         // tp_str
+    NULL,                               // tp_getattro
+    NULL,                               // tp_setattro
+    NULL,                               // tp_as_buffer
+    Py_TPFLAGS_DEFAULT,                 // tp_flags
+    "The Opcode class objects represent standard OPCODEs "
+    "of the header section of DNS messages.",
+    NULL,                               // tp_traverse
+    NULL,                               // tp_clear
+    (richcmpfunc)Opcode_richcmp,        // tp_richcompare
+    0,                                  // tp_weaklistoffset
+    NULL,                               // tp_iter
+    NULL,                               // tp_iternext
+    Opcode_methods,                     // tp_methods
+    NULL,                               // tp_members
+    NULL,                               // tp_getset
+    NULL,                               // tp_base
+    NULL,                               // tp_dict
+    NULL,                               // tp_descr_get
+    NULL,                               // tp_descr_set
+    0,                                  // tp_dictoffset
+    (initproc)Opcode_init,              // tp_init
+    NULL,                               // tp_alloc
+    PyType_GenericNew,                  // tp_new
+    NULL,                               // tp_free
+    NULL,                               // tp_is_gc
+    NULL,                               // tp_bases
+    NULL,                               // tp_mro
+    NULL,                               // tp_cache
+    NULL,                               // tp_subclasses
+    NULL,                               // tp_weaklist
+    NULL,                               // tp_del
+    0                                   // tp_version_tag
+};
+
+
+int
+Opcode_init(s_Opcode* const self, PyObject* args) {
+    uint8_t code = 0;
+    if (PyArg_ParseTuple(args, "b", &code)) {
+        try {
+            self->opcode = new Opcode(code);
+            self->static_code = false;
+        } catch (const isc::OutOfRange& ex) {
+            PyErr_SetString(PyExc_OverflowError, ex.what());
+            return (-1);
+        } catch (...) {
+            PyErr_SetString(po_IscException, "Unexpected exception");
+            return (-1);
+        }
+        return (0);
+    }
+
+    PyErr_Clear();
+    PyErr_SetString(PyExc_TypeError, "Invalid arguments to Opcode constructor");
+
+    return (-1);
+}
+
+void
+Opcode_destroy(s_Opcode* const self) {
+    // Depending on whether we created the rcode or are referring
+    // to a global static one, we do or do not delete self->opcode here
+    if (!self->static_code) {
+        delete self->opcode;
+    }
+    self->opcode = NULL;
+    Py_TYPE(self)->tp_free(self);
+}
+
+PyObject*
+Opcode_getCode(const s_Opcode* const self) {
+    return (Py_BuildValue("I", self->opcode->getCode()));
+}
+
+PyObject*
+Opcode_toText(const s_Opcode* const self) {
+    return (Py_BuildValue("s", self->opcode->toText().c_str()));
+}
+
+PyObject*
+Opcode_str(PyObject* self) {
+    // Simply call the to_text method we already defined
+    return (PyObject_CallMethod(self,
+                                const_cast<char*>("to_text"),
+                                const_cast<char*>("")));
+}
+
+PyObject*
+Opcode_createStatic(const Opcode& opcode) {
+    s_Opcode* ret = PyObject_New(s_Opcode, &opcode_type);
+    if (ret != NULL) {
+        ret->opcode = &opcode;
+        ret->static_code = true;
+    }
+    return (ret);
+}
+
+PyObject*
+Opcode_QUERY(const s_Opcode* self UNUSED_PARAM) {
+    return (Opcode_createStatic(Opcode::QUERY()));
+}
+
+PyObject*
+Opcode_IQUERY(const s_Opcode* self UNUSED_PARAM) {
+    return (Opcode_createStatic(Opcode::IQUERY()));
+}
+
+PyObject*
+Opcode_STATUS(const s_Opcode* self UNUSED_PARAM) {
+    return (Opcode_createStatic(Opcode::STATUS()));
+}
+
+PyObject*
+Opcode_RESERVED3(const s_Opcode* self UNUSED_PARAM) {
+    return (Opcode_createStatic(Opcode::RESERVED3()));
+}
+
+PyObject*
+Opcode_NOTIFY(const s_Opcode* self UNUSED_PARAM) {
+    return (Opcode_createStatic(Opcode::NOTIFY()));
+}
+
+PyObject*
+Opcode_UPDATE(const s_Opcode* self UNUSED_PARAM) {
+    return (Opcode_createStatic(Opcode::UPDATE()));
+}
+
+PyObject*
+Opcode_RESERVED6(const s_Opcode* self UNUSED_PARAM) {
+    return (Opcode_createStatic(Opcode::RESERVED6()));
+}
+
+PyObject*
+Opcode_RESERVED7(const s_Opcode* self UNUSED_PARAM) {
+    return (Opcode_createStatic(Opcode::RESERVED7()));
+}
+
+PyObject*
+Opcode_RESERVED8(const s_Opcode* self UNUSED_PARAM) {
+    return (Opcode_createStatic(Opcode::RESERVED8()));
+}
+
+PyObject*
+Opcode_RESERVED9(const s_Opcode* self UNUSED_PARAM) {
+    return (Opcode_createStatic(Opcode::RESERVED9()));
+}
+
+PyObject*
+Opcode_RESERVED10(const s_Opcode* self UNUSED_PARAM) {
+    return (Opcode_createStatic(Opcode::RESERVED10()));
+}
+
+PyObject*
+Opcode_RESERVED11(const s_Opcode* self UNUSED_PARAM) {
+    return (Opcode_createStatic(Opcode::RESERVED11()));
+}
+
+PyObject*
+Opcode_RESERVED12(const s_Opcode* self UNUSED_PARAM) {
+    return (Opcode_createStatic(Opcode::RESERVED12()));
+}
+
+PyObject*
+Opcode_RESERVED13(const s_Opcode* self UNUSED_PARAM) {
+    return (Opcode_createStatic(Opcode::RESERVED13()));
+}
+
+PyObject*
+Opcode_RESERVED14(const s_Opcode* self UNUSED_PARAM) {
+    return (Opcode_createStatic(Opcode::RESERVED14()));
+}
+
+PyObject*
+Opcode_RESERVED15(const s_Opcode* self UNUSED_PARAM) {
+    return (Opcode_createStatic(Opcode::RESERVED15()));
+}
+
+PyObject* 
+Opcode_richcmp(const s_Opcode* const self, const s_Opcode* const other,
+               const int op)
+{
+    bool c = false;
+
+    // Check for null and if the types match. If different type,
+    // simply return False
+    if (!other || (self->ob_type != other->ob_type)) {
+        Py_RETURN_FALSE;
+    }
+
+    // Only equals and not equals here, unorderable type
+    switch (op) {
+    case Py_LT:
+        PyErr_SetString(PyExc_TypeError, "Unorderable type; Opcode");
+        return (NULL);
+    case Py_LE:
+        PyErr_SetString(PyExc_TypeError, "Unorderable type; Opcode");
+        return (NULL);
+    case Py_EQ:
+        c = (*self->opcode == *other->opcode);
+        break;
+    case Py_NE:
+        c = (*self->opcode != *other->opcode);
+        break;
+    case Py_GT:
+        PyErr_SetString(PyExc_TypeError, "Unorderable type; Opcode");
+        return (NULL);
+    case Py_GE:
+        PyErr_SetString(PyExc_TypeError, "Unorderable type; Opcode");
+        return (NULL);
+    }
+    if (c)
+        Py_RETURN_TRUE;
+    else
+        Py_RETURN_FALSE;
+}
+
+// Module Initialization, all statics are initialized here
+bool
+initModulePart_Opcode(PyObject* mod) {
+    // We initialize the static description object with PyType_Ready(),
+    // then add it to the module. This is not just a check! (leaving
+    // this out results in segmentation faults)
+    if (PyType_Ready(&opcode_type) < 0) {
+        return (false);
+    }
+    Py_INCREF(&opcode_type);
+    void* p = &opcode_type;
+    if (PyModule_AddObject(mod, "Opcode", static_cast<PyObject*>(p)) != 0) {
+        Py_DECREF(&opcode_type);
+        return (false);
+    }
+
+    addClassVariable(opcode_type, "QUERY_CODE",
+                     Py_BuildValue("h", Opcode::QUERY_CODE));
+    addClassVariable(opcode_type, "IQUERY_CODE",
+                     Py_BuildValue("h", Opcode::IQUERY_CODE));
+    addClassVariable(opcode_type, "STATUS_CODE",
+                     Py_BuildValue("h", Opcode::STATUS_CODE));
+    addClassVariable(opcode_type, "RESERVED3_CODE",
+                     Py_BuildValue("h", Opcode::RESERVED3_CODE));
+    addClassVariable(opcode_type, "NOTIFY_CODE",
+                     Py_BuildValue("h", Opcode::NOTIFY_CODE));
+    addClassVariable(opcode_type, "UPDATE_CODE",
+                     Py_BuildValue("h", Opcode::UPDATE_CODE));
+    addClassVariable(opcode_type, "RESERVED6_CODE",
+                     Py_BuildValue("h", Opcode::RESERVED6_CODE));
+    addClassVariable(opcode_type, "RESERVED7_CODE",
+                     Py_BuildValue("h", Opcode::RESERVED7_CODE));
+    addClassVariable(opcode_type, "RESERVED8_CODE",
+                     Py_BuildValue("h", Opcode::RESERVED8_CODE));
+    addClassVariable(opcode_type, "RESERVED9_CODE",
+                     Py_BuildValue("h", Opcode::RESERVED9_CODE));
+    addClassVariable(opcode_type, "RESERVED10_CODE",
+                     Py_BuildValue("h", Opcode::RESERVED10_CODE));
+    addClassVariable(opcode_type, "RESERVED11_CODE",
+                     Py_BuildValue("h", Opcode::RESERVED11_CODE));
+    addClassVariable(opcode_type, "RESERVED12_CODE",
+                     Py_BuildValue("h", Opcode::RESERVED12_CODE));
+    addClassVariable(opcode_type, "RESERVED13_CODE",
+                     Py_BuildValue("h", Opcode::RESERVED13_CODE));
+    addClassVariable(opcode_type, "RESERVED14_CODE",
+                     Py_BuildValue("h", Opcode::RESERVED14_CODE));
+    addClassVariable(opcode_type, "RESERVED15_CODE",
+                     Py_BuildValue("h", Opcode::RESERVED15_CODE));
+
+    return (true);
+}
+} // end of unnamed namespace

+ 25 - 1
src/lib/dns/python/pydnspp.cc

@@ -40,8 +40,12 @@
 
 #include <dns/python/pydnspp_common.h>
 
-// For our 'general' isc::Exception
+// For our 'general' isc::Exceptions
 static PyObject* po_IscException;
+static PyObject* po_InvalidParameter;
+
+// For our own isc::dns::Exception
+static PyObject* po_DNSMessageBADVERS;
 
 // order is important here!
 #include <dns/python/messagerenderer_python.cc>
@@ -53,6 +57,9 @@ static PyObject* po_IscException;
 #include <dns/python/rrset_python.cc>          // needs Rdata, RRTTL
 #include <dns/python/question_python.cc>       // needs RRClass, RRType, RRTTL,
                                                // Name
+#include <dns/python/opcode_python.cc>
+#include <dns/python/rcode_python.cc>
+#include <dns/python/edns_python.cc>           // needs Messagerenderer, Rcode
 #include <dns/python/message_python.cc>        // needs RRset, Question
 
 //
@@ -81,9 +88,14 @@ PyInit_pydnspp(void) {
         return (NULL);
     }
 
+    // Add the exceptions to the class
     po_IscException = PyErr_NewException("pydnspp.IscException", NULL, NULL);
     PyModule_AddObject(mod, "IscException", po_IscException);
 
+    po_InvalidParameter = PyErr_NewException("pydnspp.InvalidParameter",
+                                             NULL, NULL);
+    PyModule_AddObject(mod, "InvalidParameter", po_InvalidParameter);
+
     // for each part included above, we call its specific initializer
 
     if (!initModulePart_Name(mod)) {
@@ -118,10 +130,22 @@ PyInit_pydnspp(void) {
         return (NULL);
     }
 
+    if (!initModulePart_Opcode(mod)) {
+        return (NULL);
+    }
+
+    if (!initModulePart_Rcode(mod)) {
+        return (NULL);
+    }
+
     if (!initModulePart_Message(mod)) {
         return (NULL);
     }
 
+    if (!initModulePart_EDNS(mod)) {
+        return (NULL);
+    }
+
     return (mod);
 }
 

+ 1 - 2
src/lib/dns/python/question_python.cc

@@ -254,8 +254,7 @@ Question_toWire(s_Question* self, PyObject* args) {
         // to prevent memory leak
         Py_DECREF(n);
         return (result);
-    } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type,
-                                reinterpret_cast<PyObject**>(&mr))) {
+    } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
         self->question->toWire(*mr->messagerenderer);
         // If we return NULL it is seen as an error, so use this for
         // None returns

+ 433 - 0
src/lib/dns/python/rcode_python.cc

@@ -0,0 +1,433 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <dns/rcode.h>
+
+using namespace isc::dns;
+
+//
+// Declaration of the custom exceptions (None for this class)
+
+//
+// Definition of the classes
+//
+
+// For each class, we need a struct, a helper functions (init, destroy,
+// and static wrappers around the methods we export), a list of methods,
+// and a type description
+
+namespace {
+//
+// Rcode
+//
+
+// We added a helper variable static_code here
+// Since we can create Rcodes dynamically with Rcode(int), but also
+// use the static globals (Rcode::NOERROR() etc), we use this
+// variable to see if the code came from one of the latter, in which
+// case Rcode_destroy should not free it (the other option is to
+// allocate new Rcodes for every use of the static ones, but this
+// seems more efficient).
+class s_Rcode : public PyObject {
+public:
+    s_Rcode() : rcode(NULL), static_code(false) {}
+    const Rcode* rcode;
+    bool static_code;
+};
+
+int Rcode_init(s_Rcode* const self, PyObject* args);
+void Rcode_destroy(s_Rcode* const self);
+
+PyObject* Rcode_getCode(const s_Rcode* const self);
+PyObject* Rcode_getExtendedCode(const s_Rcode* const self);
+PyObject* Rcode_toText(const s_Rcode* const self);
+PyObject* Rcode_str(PyObject* self);
+PyObject* Rcode_NOERROR(const s_Rcode* self);
+PyObject* Rcode_FORMERR(const s_Rcode* self);
+PyObject* Rcode_SERVFAIL(const s_Rcode* self);
+PyObject* Rcode_NXDOMAIN(const s_Rcode* self);
+PyObject* Rcode_NOTIMP(const s_Rcode* self);
+PyObject* Rcode_REFUSED(const s_Rcode* self);
+PyObject* Rcode_YXDOMAIN(const s_Rcode* self);
+PyObject* Rcode_YXRRSET(const s_Rcode* self);
+PyObject* Rcode_NXRRSET(const s_Rcode* self);
+PyObject* Rcode_NOTAUTH(const s_Rcode* self);
+PyObject* Rcode_NOTZONE(const s_Rcode* self);
+PyObject* Rcode_RESERVED11(const s_Rcode* self);
+PyObject* Rcode_RESERVED12(const s_Rcode* self);
+PyObject* Rcode_RESERVED13(const s_Rcode* self);
+PyObject* Rcode_RESERVED14(const s_Rcode* self);
+PyObject* Rcode_RESERVED15(const s_Rcode* self);
+PyObject* Rcode_BADVERS(const s_Rcode* self);
+PyObject* Rcode_richcmp(const s_Rcode* const self,
+                         const s_Rcode* const other, int op);
+
+PyMethodDef Rcode_methods[] = {
+    { "get_code", reinterpret_cast<PyCFunction>(Rcode_getCode), METH_NOARGS,
+      "Returns the code value" },
+    { "get_extended_code",
+      reinterpret_cast<PyCFunction>(Rcode_getExtendedCode), METH_NOARGS,
+      "Returns the upper 8-bit part of the extended code value" },
+    { "to_text", reinterpret_cast<PyCFunction>(Rcode_toText), METH_NOARGS,
+      "Returns the text representation" },
+    { "NOERROR", reinterpret_cast<PyCFunction>(Rcode_NOERROR),
+      METH_NOARGS | METH_STATIC, "Creates a NOERROR Rcode" },
+    { "FORMERR", reinterpret_cast<PyCFunction>(Rcode_FORMERR),
+      METH_NOARGS | METH_STATIC, "Creates a FORMERR Rcode" },
+    { "SERVFAIL", reinterpret_cast<PyCFunction>(Rcode_SERVFAIL),
+      METH_NOARGS | METH_STATIC, "Creates a SERVFAIL Rcode" },
+    { "NXDOMAIN", reinterpret_cast<PyCFunction>(Rcode_NXDOMAIN),
+      METH_NOARGS | METH_STATIC, "Creates a NXDOMAIN Rcode" },
+    { "NOTIMP", reinterpret_cast<PyCFunction>(Rcode_NOTIMP),
+      METH_NOARGS | METH_STATIC, "Creates a NOTIMP Rcode" },
+    { "REFUSED", reinterpret_cast<PyCFunction>(Rcode_REFUSED),
+      METH_NOARGS | METH_STATIC, "Creates a REFUSED Rcode" },
+    { "YXDOMAIN", reinterpret_cast<PyCFunction>(Rcode_YXDOMAIN),
+      METH_NOARGS | METH_STATIC, "Creates a YXDOMAIN Rcode" },
+    { "YXRRSET", reinterpret_cast<PyCFunction>(Rcode_YXRRSET),
+      METH_NOARGS | METH_STATIC, "Creates a YYRRSET Rcode" },
+    { "NXRRSET", reinterpret_cast<PyCFunction>(Rcode_NXRRSET),
+      METH_NOARGS | METH_STATIC, "Creates a NXRRSET Rcode" },
+    { "NOTAUTH", reinterpret_cast<PyCFunction>(Rcode_NOTAUTH),
+      METH_NOARGS | METH_STATIC, "Creates a NOTAUTH Rcode" },
+    { "NOTZONE", reinterpret_cast<PyCFunction>(Rcode_NOTZONE),
+      METH_NOARGS | METH_STATIC, "Creates a NOTZONE Rcode" },
+    { "RESERVED11", reinterpret_cast<PyCFunction>(Rcode_RESERVED11),
+      METH_NOARGS | METH_STATIC, "Creates a RESERVED11 Rcode" },
+    { "RESERVED12", reinterpret_cast<PyCFunction>(Rcode_RESERVED12),
+      METH_NOARGS | METH_STATIC, "Creates a RESERVED12 Rcode" },
+    { "RESERVED13", reinterpret_cast<PyCFunction>(Rcode_RESERVED13),
+      METH_NOARGS | METH_STATIC, "Creates a RESERVED13 Rcode" },
+    { "RESERVED14", reinterpret_cast<PyCFunction>(Rcode_RESERVED14),
+      METH_NOARGS | METH_STATIC, "Creates a RESERVED14 Rcode" },
+    { "RESERVED15", reinterpret_cast<PyCFunction>(Rcode_RESERVED15),
+      METH_NOARGS | METH_STATIC, "Creates a RESERVED15 Rcode" },
+    { "BADVERS", reinterpret_cast<PyCFunction>(Rcode_BADVERS),
+      METH_NOARGS | METH_STATIC, "Creates a BADVERS Rcode" },
+    { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject rcode_type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "pydnspp.Rcode",
+    sizeof(s_Rcode),                    // tp_basicsize
+    0,                                  // tp_itemsize
+    (destructor)Rcode_destroy,          // tp_dealloc
+    NULL,                               // tp_print
+    NULL,                               // tp_getattr
+    NULL,                               // tp_setattr
+    NULL,                               // tp_reserved
+    NULL,                               // tp_repr
+    NULL,                               // tp_as_number
+    NULL,                               // tp_as_sequence
+    NULL,                               // tp_as_mapping
+    NULL,                               // tp_hash 
+    NULL,                               // tp_call
+    Rcode_str,                          // tp_str
+    NULL,                               // tp_getattro
+    NULL,                               // tp_setattro
+    NULL,                               // tp_as_buffer
+    Py_TPFLAGS_DEFAULT,                 // tp_flags
+    "The Rcode class objects represent standard RCODEs"
+    "of the header section of DNS messages.",
+    NULL,                               // tp_traverse
+    NULL,                               // tp_clear
+    (richcmpfunc)Rcode_richcmp,         // tp_richcompare
+    0,                                  // tp_weaklistoffset
+    NULL,                               // tp_iter
+    NULL,                               // tp_iternext
+    Rcode_methods,                      // tp_methods
+    NULL,                               // tp_members
+    NULL,                               // tp_getset
+    NULL,                               // tp_base
+    NULL,                               // tp_dict
+    NULL,                               // tp_descr_get
+    NULL,                               // tp_descr_set
+    0,                                  // tp_dictoffset
+    (initproc)Rcode_init,               // tp_init
+    NULL,                               // tp_alloc
+    PyType_GenericNew,                  // tp_new
+    NULL,                               // tp_free
+    NULL,                               // tp_is_gc
+    NULL,                               // tp_bases
+    NULL,                               // tp_mro
+    NULL,                               // tp_cache
+    NULL,                               // tp_subclasses
+    NULL,                               // tp_weaklist
+    NULL,                               // tp_del
+    0                                   // tp_version_tag
+};
+
+int
+Rcode_init(s_Rcode* const self, PyObject* args) {
+    int code = 0;
+    int ext_code = 0;
+
+    if (PyArg_ParseTuple(args, "i", &code)) {
+        if (code < 0 || code > 0xffff) {
+            PyErr_SetString(PyExc_OverflowError, "Rcode out of range");
+            return (-1);
+        }
+        ext_code = -1;
+    } else if (PyArg_ParseTuple(args, "ii", &code, &ext_code)) {
+        if (code < 0 || code > 0xff || ext_code < 0 || ext_code > 0xff) {
+            PyErr_SetString(PyExc_OverflowError, "Rcode out of range");
+            return (-1);
+        }
+    } else {
+        PyErr_Clear();
+        PyErr_SetString(PyExc_TypeError,
+                        "Invalid arguments to Rcode constructor");
+        return (-1);
+    }
+    try {
+        if (ext_code == -1) {
+            self->rcode = new Rcode(code);
+        } else {
+            self->rcode = new Rcode(code, ext_code);
+        }
+        self->static_code = false;
+    } catch (const isc::OutOfRange& ex) {
+            PyErr_SetString(PyExc_OverflowError, ex.what());
+            return (-1);
+    } catch (...) {
+        PyErr_SetString(po_IscException, "Unexpected exception");
+        return (-1);
+    }
+    return (0);
+}
+
+void
+Rcode_destroy(s_Rcode* const self) {
+    // Depending on whether we created the rcode or are referring
+    // to a global one, we do or do not delete self->rcode here
+    if (!self->static_code) {
+        delete self->rcode;
+    }
+    self->rcode = NULL;
+    Py_TYPE(self)->tp_free(self);
+}
+
+PyObject*
+Rcode_getCode(const s_Rcode* const self) {
+    return (Py_BuildValue("I", self->rcode->getCode()));
+}
+
+PyObject*
+Rcode_getExtendedCode(const s_Rcode* const self) {
+    return (Py_BuildValue("I", self->rcode->getExtendedCode()));
+}
+
+PyObject*
+Rcode_toText(const s_Rcode* const self) {
+    return (Py_BuildValue("s", self->rcode->toText().c_str()));
+}
+
+PyObject*
+Rcode_str(PyObject* self) {
+    // Simply call the to_text method we already defined
+    return (PyObject_CallMethod(self, const_cast<char*>("to_text"),
+                                const_cast<char*>("")));
+}
+
+PyObject*
+Rcode_createStatic(const Rcode& rcode) {
+    s_Rcode* ret = PyObject_New(s_Rcode, &rcode_type);
+    if (ret != NULL) {
+        ret->rcode = &rcode;
+        ret->static_code = true;
+    }
+    return (ret);
+}
+
+PyObject*
+Rcode_NOERROR(const s_Rcode* self UNUSED_PARAM) {
+    return (Rcode_createStatic(Rcode::NOERROR()));
+}
+
+PyObject*
+Rcode_FORMERR(const s_Rcode* self UNUSED_PARAM) {
+    return (Rcode_createStatic(Rcode::FORMERR()));
+}
+
+PyObject*
+Rcode_SERVFAIL(const s_Rcode* self UNUSED_PARAM) {
+    return (Rcode_createStatic(Rcode::SERVFAIL()));
+}
+
+PyObject*
+Rcode_NXDOMAIN(const s_Rcode* self UNUSED_PARAM) {
+    return (Rcode_createStatic(Rcode::NXDOMAIN()));
+}
+
+PyObject*
+Rcode_NOTIMP(const s_Rcode* self UNUSED_PARAM) {
+    return (Rcode_createStatic(Rcode::NOTIMP()));
+}
+
+PyObject*
+Rcode_REFUSED(const s_Rcode* self UNUSED_PARAM) {
+    return (Rcode_createStatic(Rcode::REFUSED()));
+}
+
+PyObject*
+Rcode_YXDOMAIN(const s_Rcode* self UNUSED_PARAM) {
+    return (Rcode_createStatic(Rcode::YXDOMAIN()));
+}
+
+PyObject*
+Rcode_YXRRSET(const s_Rcode* self UNUSED_PARAM) {
+    return (Rcode_createStatic(Rcode::YXRRSET()));
+}
+
+PyObject*
+Rcode_NXRRSET(const s_Rcode* self UNUSED_PARAM) {
+    return (Rcode_createStatic(Rcode::NXRRSET()));
+}
+
+PyObject*
+Rcode_NOTAUTH(const s_Rcode* self UNUSED_PARAM) {
+    return (Rcode_createStatic(Rcode::NOTAUTH()));
+}
+
+PyObject*
+Rcode_NOTZONE(const s_Rcode* self UNUSED_PARAM) {
+    return (Rcode_createStatic(Rcode::NOTZONE()));
+}
+
+PyObject*
+Rcode_RESERVED11(const s_Rcode* self UNUSED_PARAM) {
+    return (Rcode_createStatic(Rcode::RESERVED11()));
+}
+
+PyObject*
+Rcode_RESERVED12(const s_Rcode* self UNUSED_PARAM) {
+    return (Rcode_createStatic(Rcode::RESERVED12()));
+}
+
+PyObject*
+Rcode_RESERVED13(const s_Rcode* self UNUSED_PARAM) {
+    return (Rcode_createStatic(Rcode::RESERVED13()));
+}
+
+PyObject*
+Rcode_RESERVED14(const s_Rcode* self UNUSED_PARAM) {
+    return (Rcode_createStatic(Rcode::RESERVED14()));
+}
+
+PyObject*
+Rcode_RESERVED15(const s_Rcode* self UNUSED_PARAM) {
+    return (Rcode_createStatic(Rcode::RESERVED15()));
+}
+
+PyObject*
+Rcode_BADVERS(const s_Rcode* self UNUSED_PARAM) {
+    return (Rcode_createStatic(Rcode::BADVERS()));
+}
+
+PyObject* 
+Rcode_richcmp(const s_Rcode* const self, const s_Rcode* const other,
+              const int op)
+{
+    bool c = false;
+
+    // Check for null and if the types match. If different type,
+    // simply return False
+    if (!other || (self->ob_type != other->ob_type)) {
+        Py_RETURN_FALSE;
+    }
+
+    // Only equals and not equals here, unorderable type
+    switch (op) {
+    case Py_LT:
+        PyErr_SetString(PyExc_TypeError, "Unorderable type; Rcode");
+        return (NULL);
+    case Py_LE:
+        PyErr_SetString(PyExc_TypeError, "Unorderable type; Rcode");
+        return (NULL);
+    case Py_EQ:
+        c = (*self->rcode == *other->rcode);
+        break;
+    case Py_NE:
+        c = (*self->rcode != *other->rcode);
+        break;
+    case Py_GT:
+        PyErr_SetString(PyExc_TypeError, "Unorderable type; Rcode");
+        return (NULL);
+    case Py_GE:
+        PyErr_SetString(PyExc_TypeError, "Unorderable type; Rcode");
+        return (NULL);
+    }
+    if (c)
+        Py_RETURN_TRUE;
+    else
+        Py_RETURN_FALSE;
+}
+
+// Module Initialization, all statics are initialized here
+bool
+initModulePart_Rcode(PyObject* mod) {
+    // We initialize the static description object with PyType_Ready(),
+    // then add it to the module. This is not just a check! (leaving
+    // this out results in segmentation faults)
+    if (PyType_Ready(&rcode_type) < 0) {
+        return (false);
+    }
+    Py_INCREF(&rcode_type);
+    void* p = &rcode_type;
+    if (PyModule_AddObject(mod, "Rcode", static_cast<PyObject*>(p)) != 0) {
+        Py_DECREF(&rcode_type);
+        return (false);
+    }
+
+    addClassVariable(rcode_type, "NOERROR_CODE",
+                     Py_BuildValue("h", Rcode::NOERROR_CODE));
+    addClassVariable(rcode_type, "FORMERR_CODE",
+                     Py_BuildValue("h", Rcode::FORMERR_CODE));
+    addClassVariable(rcode_type, "SERVFAIL_CODE",
+                     Py_BuildValue("h", Rcode::SERVFAIL_CODE));
+    addClassVariable(rcode_type, "NXDOMAIN_CODE",
+                     Py_BuildValue("h", Rcode::NXDOMAIN_CODE));
+    addClassVariable(rcode_type, "NOTIMP_CODE",
+                     Py_BuildValue("h", Rcode::NOTIMP_CODE));
+    addClassVariable(rcode_type, "REFUSED_CODE",
+                     Py_BuildValue("h", Rcode::REFUSED_CODE));
+    addClassVariable(rcode_type, "YXDOMAIN_CODE",
+                     Py_BuildValue("h", Rcode::YXDOMAIN_CODE));
+    addClassVariable(rcode_type, "YXRRSET_CODE",
+                     Py_BuildValue("h", Rcode::YXRRSET_CODE));
+    addClassVariable(rcode_type, "NXRRSET_CODE",
+                     Py_BuildValue("h", Rcode::NXRRSET_CODE));
+    addClassVariable(rcode_type, "NOTAUTH_CODE",
+                     Py_BuildValue("h", Rcode::NOTAUTH_CODE));
+    addClassVariable(rcode_type, "NOTZONE_CODE",
+                     Py_BuildValue("h", Rcode::NOTZONE_CODE));
+    addClassVariable(rcode_type, "RESERVED11_CODE",
+                     Py_BuildValue("h", Rcode::RESERVED11_CODE));
+    addClassVariable(rcode_type, "RESERVED12_CODE",
+                     Py_BuildValue("h", Rcode::RESERVED12_CODE));
+    addClassVariable(rcode_type, "RESERVED13_CODE",
+                     Py_BuildValue("h", Rcode::RESERVED13_CODE));
+    addClassVariable(rcode_type, "RESERVED14_CODE",
+                     Py_BuildValue("h", Rcode::RESERVED14_CODE));
+    addClassVariable(rcode_type, "RESERVED15_CODE",
+                     Py_BuildValue("h", Rcode::RESERVED15_CODE));
+    addClassVariable(rcode_type, "BADVERS_CODE",
+                     Py_BuildValue("h", Rcode::BADVERS_CODE));
+
+    return (true);
+}
+} // end of unnamed namespace

+ 11 - 2
src/lib/dns/python/rdata_python.cc

@@ -146,12 +146,21 @@ Rdata_init(s_Rdata* self, PyObject* args) {
     s_RRType* rrtype;
     s_RRClass* rrclass;
     const char* s;
-    
+    const char* data;
+    Py_ssize_t len;
+
+    // Create from string
     if (PyArg_ParseTuple(args, "O!O!s", &rrtype_type, &rrtype,
                                         &rrclass_type, &rrclass,
                                         &s)) {
         self->rdata = createRdata(*rrtype->rrtype, *rrclass->rrclass, s);
         return (0);
+    } else if (PyArg_ParseTuple(args, "O!O!y#", &rrtype_type, &rrtype,
+                                &rrclass_type, &rrclass, &data, &len)) {
+        InputBuffer input_buffer(data, len);
+        self->rdata = createRdata(*rrtype->rrtype, *rrclass->rrclass,
+                                  input_buffer, len);
+        return (0);
     }
 
     return (-1);
@@ -195,7 +204,7 @@ Rdata_toWire(s_Rdata* self, PyObject* args) {
         // to prevent memory leak
         Py_DECREF(rd_bytes);
         return (result);
-    } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, (PyObject**) &mr)) {
+    } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
         self->rdata->toWire(*mr->messagerenderer);
         // If we return NULL it is seen as an error, so use this for
         // None returns

+ 1 - 1
src/lib/dns/python/rrclass_python.cc

@@ -236,7 +236,7 @@ RRClass_toWire(s_RRClass* self, PyObject* args) {
         // to prevent memory leak
         Py_DECREF(n);
         return (result);
-    } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, (PyObject**) &mr)) {
+    } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
         self->rrclass->toWire(*mr->messagerenderer);
         // If we return NULL it is seen as an error, so use this for
         // None returns

+ 1 - 1
src/lib/dns/python/rrset_python.cc

@@ -315,7 +315,7 @@ RRset_toWire(s_RRset* self, PyObject* args) {
             // to prevent memory leak
             Py_DECREF(n);
             return (result);
-        } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, (PyObject**) &mr)) {
+        } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
             self->rrset->toWire(*mr->messagerenderer);
             // If we return NULL it is seen as an error, so use this for
             // None returns

+ 1 - 1
src/lib/dns/python/rrttl_python.cc

@@ -235,7 +235,7 @@ RRTTL_toWire(s_RRTTL* self, PyObject* args) {
         // to prevent memory leak
         Py_DECREF(n);
         return (result);
-    } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, (PyObject**) &mr)) {
+    } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
         self->rrttl->toWire(*mr->messagerenderer);
         // If we return NULL it is seen as an error, so use this for
         // None returns

+ 1 - 1
src/lib/dns/python/rrtype_python.cc

@@ -273,7 +273,7 @@ RRType_toWire(s_RRType* self, PyObject* args) {
         // to prevent memory leak
         Py_DECREF(n);
         return (result);
-    } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, (PyObject**) &mr)) {
+    } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
         self->rrtype->toWire(*mr->messagerenderer);
         // If we return NULL it is seen as an error, so use this for
         // None returns

+ 7 - 3
src/lib/dns/python/tests/Makefile.am

@@ -1,7 +1,10 @@
-PYTESTS = message_python_test.py
+PYTESTS = edns_python_test.py
+PYTESTS += message_python_test.py
 PYTESTS += messagerenderer_python_test.py
 PYTESTS += name_python_test.py
 PYTESTS += question_python_test.py
+PYTESTS += opcode_python_test.py
+PYTESTS += rcode_python_test.py
 PYTESTS += rdata_python_test.py
 PYTESTS += rrclass_python_test.py
 PYTESTS += rrset_python_test.py
@@ -9,6 +12,7 @@ PYTESTS += rrttl_python_test.py
 PYTESTS += rrtype_python_test.py
 
 EXTRA_DIST = $(PYTESTS)
+EXTRA_DIST += testutil.py
 
 # If necessary (rare cases), explicitly specify paths to dynamic libraries
 # required by loadable python modules.
@@ -23,8 +27,8 @@ PYCOVERAGE = $(PYTHON)
 check-local:
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
-	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs \
-	TESTDATA_PATH=$(abs_top_srcdir)/src/lib/dns/tests/testdata \
+	env PYTHONPATH=$(abs_top_srcdir)/src/lib/dns/.libs:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs \
+	TESTDATA_PATH=$(abs_top_srcdir)/src/lib/dns/tests/testdata:$(abs_top_builddir)/src/lib/dns/tests/testdata \
 	$(LIBRARY_PATH_PLACEHOLDER) \
 	$(PYCOVERAGE) $(abs_srcdir)/$$pytest || exit ; \
 	done

+ 180 - 0
src/lib/dns/python/tests/edns_python_test.py

@@ -0,0 +1,180 @@
+# Copyright (C) 2010  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+# $Id$
+
+import unittest
+import os
+from pydnspp import *
+from testutil import *
+
+class EDNSTest(unittest.TestCase):
+
+    def setUp(self):
+        self.rrtype = RRType("OPT")
+        self.rrclass = RRClass(4096)
+        self.rrttl_do_on = RRTTL(0x00008000)
+        self.rrttl_do_off = RRTTL(0)
+        self.rrttl_badver = RRTTL(0x00018000)
+        self.opt_rdata = Rdata(self.rrtype, self.rrclass, bytes())
+        self.edns_base = EDNS(Name.ROOT_NAME, self.rrclass, self.rrtype,
+                              self.rrttl_do_off, self.opt_rdata)
+
+    def test_badver_construct(self):
+        self.assertRaises(InvalidParameter, EDNS, 1)
+        self.assertRaises(TypeError, EDNS, 1, 2) # signature mismatch
+        self.assertRaises(TypeError, EDNS, 256) # invalid arguments
+
+    def test_dnssec_dobit(self):
+        edns = EDNS(Name.ROOT_NAME, self.rrclass, self.rrtype,
+                    self.rrttl_do_on, self.opt_rdata)
+        self.assertTrue(edns.get_dnssec_awareness())
+
+        edns = EDNS(Name.ROOT_NAME, self.rrclass, self.rrtype,
+                    self.rrttl_do_off, self.opt_rdata)
+        self.assertFalse(edns.get_dnssec_awareness())
+
+        edns = EDNS()
+        self.assertFalse(edns.get_dnssec_awareness())
+        edns.set_dnssec_awareness(True)
+        self.assertTrue(edns.get_dnssec_awareness())
+        edns.set_dnssec_awareness(False);
+        self.assertFalse(edns.get_dnssec_awareness())
+
+        self.assertRaises(TypeError, edns.set_dnssec_awareness, "wrong")
+        self.assertRaises(TypeError, edns.set_dnssec_awareness, 1)
+
+    def test_udpsize(self):
+        edns = EDNS(Name.ROOT_NAME, self.rrclass, self.rrtype,
+                    self.rrttl_do_on, self.opt_rdata)
+        self.assertEqual(4096, edns.get_udp_size())
+
+        edns = EDNS()
+        edns.set_udp_size(511)
+        self.assertEqual(511, edns.get_udp_size())
+        edns.set_udp_size(0)
+        self.assertEqual(0, edns.get_udp_size())
+        edns.set_udp_size(65535)
+        self.assertEqual(65535, edns.get_udp_size())
+
+        self.assertRaises(TypeError, edns.set_udp_size, "wrong")
+
+        # Range check.  We need to do this at the binding level, so we need
+        # explicit tests for it.
+        self.assertRaises(OverflowError, edns.set_udp_size, 65536)
+        self.assertRaises(OverflowError, edns.set_udp_size, -1)
+
+    def test_get_version(self):
+        self.assertEqual(EDNS.SUPPORTED_VERSION, EDNS().get_version())
+
+    def test_bad_wiredata(self):
+        self.assertRaises(InvalidParameter, EDNS, Name.ROOT_NAME,
+                          self.rrclass, RRType("A"),
+                          self.rrttl_do_on, self.opt_rdata)
+        self.assertRaises(DNSMessageFORMERR, EDNS, Name("example.com"),
+                          self.rrclass, self.rrtype, self.rrttl_do_on,
+                          self.opt_rdata)
+        self.assertRaises(DNSMessageBADVERS, EDNS, Name.ROOT_NAME,
+                          self.rrclass, self.rrtype, self.rrttl_badver,
+                          self.opt_rdata)
+
+    def test_to_text(self):
+        edns = EDNS()
+        edns.set_udp_size(4096)
+        expected_str = "; EDNS: version: 0, flags:; udp: 4096\n"
+        self.assertEqual(expected_str, edns.to_text())
+        self.assertEqual(expected_str, str(edns))
+
+        edns.set_dnssec_awareness(True)
+        self.assertEqual("; EDNS: version: 0, flags: do; udp: 4096\n",
+                         edns.to_text())
+
+        self.assertEqual("; EDNS: version: 0, flags: do; udp: 4096\n",
+                         EDNS(Name.ROOT_NAME, self.rrclass, self.rrtype,
+                              RRTTL(0x01008000), self.opt_rdata).to_text())
+
+        self.assertEqual("; EDNS: version: 0, flags: do; udp: 4096\n",
+                         EDNS(Name.ROOT_NAME, self.rrclass, self.rrtype,
+                              RRTTL(0x00008001), self.opt_rdata).to_text())
+
+    def test_towire_renderer(self):
+        renderer = MessageRenderer()
+        extrcode_noerror = Rcode.NOERROR().get_extended_code()
+        extrcode_badvers = Rcode.BADVERS().get_extended_code()
+
+        self.assertEqual(1, self.edns_base.to_wire(renderer, extrcode_noerror))
+        wiredata = read_wire_data("edns_toWire1.wire")
+        self.assertEqual(wiredata, renderer.get_data())
+
+        renderer.clear()
+        self.edns_base.set_dnssec_awareness(True)
+        self.assertEqual(1, self.edns_base.to_wire(renderer, extrcode_noerror))
+        wiredata = read_wire_data("edns_toWire2.wire")
+        self.assertEqual(wiredata, renderer.get_data())
+
+        renderer.clear()
+        self.edns_base.set_dnssec_awareness(True)
+        self.assertEqual(1, self.edns_base.to_wire(renderer, extrcode_badvers))
+        wiredata = read_wire_data("edns_toWire3.wire")
+        self.assertEqual(wiredata, renderer.get_data())
+
+        renderer.clear()
+        self.edns_base.set_dnssec_awareness(True)
+        self.edns_base.set_udp_size(511)
+        self.assertEqual(1, self.edns_base.to_wire(renderer, extrcode_noerror))
+        wiredata = read_wire_data("edns_toWire4.wire")
+        self.assertEqual(wiredata, renderer.get_data())
+
+        renderer.clear()
+        edns = EDNS(Name.ROOT_NAME, self.rrclass, self.rrtype,
+                    RRTTL(0x00008001), self.opt_rdata)
+        self.assertEqual(1, edns.to_wire(renderer, extrcode_noerror))
+        wiredata = read_wire_data("edns_toWire2.wire")
+        self.assertEqual(wiredata, renderer.get_data())
+
+        renderer.clear()
+        renderer.set_length_limit(10)
+        self.edns_base.set_dnssec_awareness(True)
+        self.assertEqual(0, self.edns_base.to_wire(renderer, extrcode_noerror))
+        self.assertEqual(0, renderer.get_length())
+
+    def test_towire_buffer(self):
+        extrcode_noerror = Rcode.NOERROR().get_extended_code()
+
+        obuffer = bytes()
+        obuffer = self.edns_base.to_wire(obuffer, extrcode_noerror)
+        wiredata = read_wire_data("edns_toWire1.wire")
+        self.assertEqual(wiredata, obuffer)
+
+    def test_create_from_rr(self):
+        (edns, extrcode) = EDNS.create_from_rr(Name.ROOT_NAME, self.rrclass,
+                                               self.rrtype, self.rrttl_do_on,
+                                               self.opt_rdata)
+        self.assertEqual(EDNS.SUPPORTED_VERSION, edns.get_version())
+        self.assertTrue(edns.get_dnssec_awareness())
+        self.assertEqual(4096, edns.get_udp_size())
+        self.assertEqual(0, extrcode)
+
+        (edns, extrcode) = EDNS.create_from_rr(Name.ROOT_NAME, self.rrclass,
+                                               self.rrtype, RRTTL(0x01008000),
+                                               self.opt_rdata)
+        self.assertEqual(1, extrcode)
+
+        self.assertRaises(DNSMessageBADVERS, EDNS.create_from_rr,
+                          Name.ROOT_NAME, self.rrclass, self.rrtype,
+                          self.rrttl_badver, self.opt_rdata)
+
+if __name__ == '__main__':
+    unittest.main()

+ 42 - 273
src/lib/dns/python/tests/message_python_test.py

@@ -20,7 +20,7 @@
 import unittest
 import os
 from pydnspp import *
-
+from testutil import *
 
 class MessageFlagTest(unittest.TestCase):
     def test_init(self):
@@ -35,163 +35,6 @@ class MessageFlagTest(unittest.TestCase):
         self.assertEqual(0x0020, MessageFlag.AD().get_bit())
         self.assertEqual(0x0010, MessageFlag.CD().get_bit())
 
-class OpcodeTest(unittest.TestCase):
-    def test_init(self):
-        self.assertRaises(NotImplementedError, Opcode)
-
-    def test_get_code(self):
-        self.assertEqual(0, Opcode.QUERY().get_code())
-        self.assertEqual(1, Opcode.IQUERY().get_code())
-        self.assertEqual(2, Opcode.STATUS().get_code())
-        self.assertEqual(3, Opcode.RESERVED3().get_code())
-        self.assertEqual(4, Opcode.NOTIFY().get_code())
-        self.assertEqual(5, Opcode.UPDATE().get_code())
-        self.assertEqual(6, Opcode.RESERVED6().get_code())
-        self.assertEqual(7, Opcode.RESERVED7().get_code())
-        self.assertEqual(8, Opcode.RESERVED8().get_code())
-        self.assertEqual(9, Opcode.RESERVED9().get_code())
-        self.assertEqual(10, Opcode.RESERVED10().get_code())
-        self.assertEqual(11, Opcode.RESERVED11().get_code())
-        self.assertEqual(12, Opcode.RESERVED12().get_code())
-        self.assertEqual(13, Opcode.RESERVED13().get_code())
-        self.assertEqual(14, Opcode.RESERVED14().get_code())
-        self.assertEqual(15, Opcode.RESERVED15().get_code())
-
-    def test_to_text(self):
-        self.assertEqual("QUERY", Opcode.QUERY().to_text())
-        self.assertEqual("QUERY", str(Opcode.QUERY()))
-        self.assertEqual("IQUERY", Opcode.IQUERY().to_text())
-        self.assertEqual("STATUS", Opcode.STATUS().to_text())
-        self.assertEqual("RESERVED3", Opcode.RESERVED3().to_text())
-        self.assertEqual("NOTIFY", Opcode.NOTIFY().to_text())
-        self.assertEqual("UPDATE", Opcode.UPDATE().to_text())
-        self.assertEqual("RESERVED6", Opcode.RESERVED6().to_text())
-        self.assertEqual("RESERVED7", Opcode.RESERVED7().to_text())
-        self.assertEqual("RESERVED8", Opcode.RESERVED8().to_text())
-        self.assertEqual("RESERVED9", Opcode.RESERVED9().to_text())
-        self.assertEqual("RESERVED10", Opcode.RESERVED10().to_text())
-        self.assertEqual("RESERVED11", Opcode.RESERVED11().to_text())
-        self.assertEqual("RESERVED12", Opcode.RESERVED12().to_text())
-        self.assertEqual("RESERVED13", Opcode.RESERVED13().to_text())
-        self.assertEqual("RESERVED14", Opcode.RESERVED14().to_text())
-        self.assertEqual("RESERVED15", Opcode.RESERVED15().to_text())
-
-    def test_richcmp(self):
-        o1 = Opcode.QUERY()
-        o2 = Opcode.NOTIFY()
-        o3 = Opcode.NOTIFY()
-        self.assertTrue(o2 == o3)
-        self.assertFalse(o2 != o3)
-        self.assertTrue(o1 != o2)
-        self.assertFalse(o1 == 1)
-        self.assertFalse(o1 == o2)
-        # can't use assertRaises here...
-        try:
-            o1 < o2
-            self.fail("operation that should have raised an error unexpectedly succeeded")
-        except Exception as err:
-            self.assertEqual(TypeError, type(err))
-        try:
-            o1 <= o2
-            self.fail("operation that should have raised an error unexpectedly succeeded")
-        except Exception as err:
-            self.assertEqual(TypeError, type(err))
-        try:
-            o1 > o2
-            self.fail("operation that should have raised an error unexpectedly succeeded")
-        except Exception as err:
-            self.assertEqual(TypeError, type(err))
-        try:
-            o1 >= o2
-            self.fail("operation that should have raised an error unexpectedly succeeded")
-        except Exception as err:
-            self.assertEqual(TypeError, type(err))
-
-class RcodeTest(unittest.TestCase):
-    def test_init(self):
-        self.assertRaises(TypeError, Rcode, "wrong")
-        self.assertRaises(OverflowError, Rcode, 65536)
-        self.assertEqual(Rcode(0).get_code(), 0)
-    
-        self.assertEqual(0, Rcode(0).get_code())
-        self.assertEqual(0xfff, Rcode(0xfff).get_code()) # possible max code
-    
-        # should fail on attempt of construction with an out of range code
-        self.assertRaises(OverflowError, Rcode, 0x1000)
-        self.assertRaises(OverflowError, Rcode, 0xffff)
-
-    def test_get_code(self):
-        self.assertEqual(0, Rcode.NOERROR().get_code())
-        self.assertEqual(1, Rcode.FORMERR().get_code())
-        self.assertEqual(2, Rcode.SERVFAIL().get_code())
-        self.assertEqual(3, Rcode.NXDOMAIN().get_code())
-        self.assertEqual(4, Rcode.NOTIMP().get_code())
-        self.assertEqual(5, Rcode.REFUSED().get_code())
-        self.assertEqual(6, Rcode.YXDOMAIN().get_code())
-        self.assertEqual(7, Rcode.YXRRSET().get_code())
-        self.assertEqual(8, Rcode.NXRRSET().get_code())
-        self.assertEqual(9, Rcode.NOTAUTH().get_code())
-        self.assertEqual(10, Rcode.NOTZONE().get_code())
-        self.assertEqual(11, Rcode.RESERVED11().get_code())
-        self.assertEqual(12, Rcode.RESERVED12().get_code())
-        self.assertEqual(13, Rcode.RESERVED13().get_code())
-        self.assertEqual(14, Rcode.RESERVED14().get_code())
-        self.assertEqual(15, Rcode.RESERVED15().get_code())
-        self.assertEqual(16, Rcode.BADVERS().get_code())
-
-    def test_to_text(self):
-        self.assertEqual("NOERROR", Rcode(0).to_text())
-        self.assertEqual("NOERROR", str(Rcode(0)))
-        self.assertEqual("FORMERR", Rcode(1).to_text())
-        self.assertEqual("SERVFAIL", Rcode(2).to_text())
-        self.assertEqual("NXDOMAIN", Rcode(3).to_text())
-        self.assertEqual("NOTIMP", Rcode(4).to_text())
-        self.assertEqual("REFUSED", Rcode(5).to_text())
-        self.assertEqual("YXDOMAIN", Rcode(6).to_text())
-        self.assertEqual("YXRRSET", Rcode(7).to_text())
-        self.assertEqual("NXRRSET", Rcode(8).to_text())
-        self.assertEqual("NOTAUTH", Rcode(9).to_text())
-        self.assertEqual("NOTZONE", Rcode(10).to_text())
-        self.assertEqual("RESERVED11", Rcode(11).to_text())
-        self.assertEqual("RESERVED12", Rcode(12).to_text())
-        self.assertEqual("RESERVED13", Rcode(13).to_text())
-        self.assertEqual("RESERVED14", Rcode(14).to_text())
-        self.assertEqual("RESERVED15", Rcode(15).to_text())
-        self.assertEqual("BADVERS", Rcode(16).to_text())
-        
-        self.assertEqual("17", Rcode(Rcode.BADVERS().get_code() + 1).to_text())
-        self.assertEqual("4095", Rcode(0xfff).to_text())
-
-    def test_richcmp(self):
-        r1 = Rcode.NOERROR()
-        r2 = Rcode.FORMERR()
-        r3 = Rcode.FORMERR()
-        self.assertTrue(r2 == r3)
-        self.assertTrue(r1 != r2)
-        self.assertFalse(r1 == r2)
-        self.assertFalse(r1 != 1)
-        # can't use assertRaises here...
-        try:
-            r1 < r2
-            self.fail("operation that should have raised an error unexpectedly succeeded")
-        except Exception as err:
-            self.assertEqual(TypeError, type(err))
-        try:
-            r1 <= r2
-            self.fail("operation that should have raised an error unexpectedly succeeded")
-        except Exception as err:
-            self.assertEqual(TypeError, type(err))
-        try:
-            r1 > r2
-            self.fail("operation that should have raised an error unexpectedly succeeded")
-        except Exception as err:
-            self.assertEqual(TypeError, type(err))
-        try:
-            r1 >= r2
-            self.fail("operation that should have raised an error unexpectedly succeeded")
-        except Exception as err:
-            self.assertEqual(TypeError, type(err))
-
 class SectionTest(unittest.TestCase):
 
     def test_init(self):
@@ -240,19 +83,6 @@ if "TESTDATA_PATH" in os.environ:
 else:
     testdata_path = "../tests/testdata"
 
-def read_wire_data(filename):
-    data = bytes()
-    file = open(testdata_path + os.sep + filename, "r")
-    for line in file:
-        line = line.strip()
-        if line == "" or line.startswith("#"):
-            pass
-        else:
-            cur_data = bytes.fromhex(line)
-            data += cur_data
-
-    return data
-
 def factoryFromFile(message, file):
     data = read_wire_data(file)
     message.from_wire(data)
@@ -316,28 +146,6 @@ class MessageTest(unittest.TestCase):
         self.assertRaises(InvalidMessageOperation,
                           self.p.clear_header_flag, MessageFlag.AA())
 
-    def test_set_DNSSEC_supported(self):
-        self.assertRaises(TypeError, self.r.set_dnssec_supported, "wrong")
-
-        self.assertFalse(self.r.is_dnssec_supported())
-        self.r.set_dnssec_supported(True)
-        self.assertTrue(self.r.is_dnssec_supported())
-        self.r.set_dnssec_supported(False)
-        self.assertFalse(self.r.is_dnssec_supported())
-
-        self.assertRaises(InvalidMessageOperation,
-                          self.p.set_dnssec_supported, True)
-        self.assertRaises(InvalidMessageOperation,
-                          self.p.set_dnssec_supported, False)
-
-    def test_set_udp_size(self):
-        self.assertRaises(TypeError, self.r.set_udp_size, "wrong")
-        self.assertRaises(InvalidMessageUDPSize, self.r.set_udp_size, 0)
-        self.assertRaises(InvalidMessageUDPSize, self.r.set_udp_size, 65536)
-        self.assertRaises(InvalidMessageOperation, self.p.set_udp_size, 1024)
-        self.r.set_udp_size(2048)
-        self.assertEqual(2048, self.r.get_udp_size())
-
     def test_set_qid(self):
         self.assertRaises(TypeError, self.r.set_qid, "wrong")
         self.assertRaises(InvalidMessageOperation,
@@ -355,6 +163,7 @@ class MessageTest(unittest.TestCase):
         self.assertRaises(InvalidMessageOperation,
                           self.p.set_rcode, rcode)
         
+        self.assertRaises(InvalidMessageOperation, self.p.get_rcode)
 
     def test_set_opcode(self):
         self.assertRaises(TypeError, self.r.set_opcode, "wrong")
@@ -366,6 +175,26 @@ class MessageTest(unittest.TestCase):
         self.assertRaises(InvalidMessageOperation,
                           self.p.set_opcode, opcode)
 
+        self.assertRaises(InvalidMessageOperation, self.p.get_opcode)
+
+    def test_get_edns(self):
+        self.assertEqual(None, self.p.get_edns())
+
+        message_parse = Message(Message.PARSE)
+        factoryFromFile(message_parse, "message_fromWire10.wire")
+        edns = message_parse.get_edns()
+        self.assertEqual(0, edns.get_version())
+        self.assertEqual(4096, edns.get_udp_size())
+        self.assertTrue(edns.get_dnssec_awareness())
+
+    def test_set_edns(self):
+        self.assertRaises(InvalidMessageOperation, self.p.set_edns, EDNS())
+
+        edns = EDNS()
+        edns.set_udp_size(1024)
+        self.r.set_edns(edns)
+        self.assertEqual(1024, self.r.get_edns().get_udp_size())
+
     def test_get_section(self):
         self.assertRaises(TypeError, self.r.get_section, "wrong")
 
@@ -430,6 +259,16 @@ class MessageTest(unittest.TestCase):
         self.assertEqual(b'\x105\x85\x00\x00\x01\x00\x02\x00\x00\x00\x00\x04test\x07example\x03com\x00\x00\x01\x00\x01\xc0\x0c\x00\x01\x00\x01\x00\x00\x0e\x10\x00\x04\xc0\x00\x02\x01\xc0\x0c\x00\x01\x00\x01\x00\x00\x0e\x10\x00\x04\xc0\x00\x02\x02',
                          renderer.get_data())
 
+    def test_to_wire_without_opcode(self):
+        self.r.set_rcode(Rcode.NOERROR())
+        self.assertRaises(InvalidMessageOperation, self.r.to_wire,
+                          MessageRenderer())
+
+    def test_to_wire_without_rcode(self):
+        self.r.set_opcode(Opcode.QUERY())
+        self.assertRaises(InvalidMessageOperation, self.r.to_wire,
+                          MessageRenderer())
+
     def test_to_text(self):
         message_render = create_message()
         
@@ -447,6 +286,14 @@ test.example.com. 3600 IN A 192.0.2.2
         self.assertEqual(msg_str, message_render.to_text())
         self.assertEqual(msg_str, str(message_render))
 
+    def test_to_text_without_opcode(self):
+        self.r.set_rcode(Rcode.NOERROR())
+        self.assertRaises(InvalidMessageOperation, self.r.to_text)
+
+    def test_to_text_without_rcode(self):
+        self.r.set_opcode(Opcode.QUERY())
+        self.assertRaises(InvalidMessageOperation, self.r.to_text)
+
     def test_from_wire(self):
         self.assertRaises(TypeError, self.r.from_wire, 1)
         self.assertRaises(InvalidMessageOperation,
@@ -487,93 +334,15 @@ test.example.com. 3600 IN A 192.0.2.2
         self.assertEqual("192.0.2.2", rdata[1].to_text())
         self.assertEqual(2, len(rdata))
 
-    def test_GetEDNS0DOBit(self):
-        message_parse = Message(Message.PARSE)
-        ## Without EDNS0, DNSSEC is considered to be unsupported.
-        factoryFromFile(message_parse, "message_fromWire1")
-        self.assertFalse(message_parse.is_dnssec_supported())
-    
-        ## If DO bit is on, DNSSEC is considered to be supported.
-        message_parse.clear(Message.PARSE)
-        factoryFromFile(message_parse, "message_fromWire2")
-        self.assertTrue(message_parse.is_dnssec_supported())
-    
-        ## If DO bit is off, DNSSEC is considered to be unsupported.
-        message_parse.clear(Message.PARSE)
-        factoryFromFile(message_parse, "message_fromWire3")
-        self.assertFalse(message_parse.is_dnssec_supported())
-    
-    def test_SetEDNS0DOBit(self):
-        # By default, it's false, and we can enable/disable it.
-        message_parse = Message(Message.PARSE)
-        message_render = Message(Message.RENDER)
-        self.assertFalse(message_render.is_dnssec_supported())
-        message_render.set_dnssec_supported(True)
-        self.assertTrue(message_render.is_dnssec_supported())
-        message_render.set_dnssec_supported(False)
-        self.assertFalse(message_render.is_dnssec_supported())
-    
-        ## A message in the parse mode doesn't allow this flag to be set.
-        self.assertRaises(InvalidMessageOperation,
-                          message_parse.set_dnssec_supported,
-                          True)
-        ## Once converted to the render mode, it works as above
-        message_parse.make_response()
-        self.assertFalse(message_parse.is_dnssec_supported())
-        message_parse.set_dnssec_supported(True)
-        self.assertTrue(message_parse.is_dnssec_supported())
-        message_parse.set_dnssec_supported(False)
-        self.assertFalse(message_parse.is_dnssec_supported())
-    
-    def test_GetEDNS0UDPSize(self):
-        # Without EDNS0, the default max UDP size is used.
-        message_parse = Message(Message.PARSE)
-        factoryFromFile(message_parse, "message_fromWire1")
-        self.assertEqual(Message.DEFAULT_MAX_UDPSIZE, message_parse.get_udp_size())
-    
-        ## If the size specified in EDNS0 > default max, use it.
-        message_parse.clear(Message.PARSE)
-        factoryFromFile(message_parse, "message_fromWire2")
-        self.assertEqual(4096, message_parse.get_udp_size())
-    
-        ## If the size specified in EDNS0 < default max, keep using the default.
-        message_parse.clear(Message.PARSE)
-        factoryFromFile(message_parse, "message_fromWire8")
-        self.assertEqual(Message.DEFAULT_MAX_UDPSIZE, message_parse.get_udp_size())
-    
-    def test_SetEDNS0UDPSize(self):
-        # The default size if unspecified
-        message_render = Message(Message.RENDER)
-        message_parse = Message(Message.PARSE)
-        self.assertEqual(Message.DEFAULT_MAX_UDPSIZE, message_render.get_udp_size())
-        # A common buffer size with EDNS, should succeed
-        message_render.set_udp_size(4096)
-        self.assertEqual(4096, message_render.get_udp_size())
-        # Unusual large value, but accepted
-        message_render.set_udp_size(0xffff)
-        self.assertEqual(0xffff, message_render.get_udp_size())
-        # Too small is value is rejected
-        self.assertRaises(InvalidMessageUDPSize, message_render.set_udp_size, 511)
-    
-        # A message in the parse mode doesn't allow the set operation.
-        self.assertRaises(InvalidMessageOperation, message_parse.set_udp_size, 4096)
-        ## Once converted to the render mode, it works as above.
-        message_parse.make_response()
-        message_parse.set_udp_size(4096)
-        self.assertEqual(4096, message_parse.get_udp_size())
-        message_parse.set_udp_size(0xffff)
-        self.assertEqual(0xffff, message_parse.get_udp_size())
-        self.assertRaises(InvalidMessageUDPSize, message_parse.set_udp_size, 511)
-    
     def test_EDNS0ExtCode(self):
         # Extended Rcode = BADVERS
         message_parse = Message(Message.PARSE)
-        factoryFromFile(message_parse, "message_fromWire10")
+        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")
+        factoryFromFile(message_parse, "message_fromWire11.wire")
         self.assertEqual(0xfff, message_parse.get_rcode().get_code())
     
     def test_BadEDNS0(self):

+ 1 - 0
src/lib/dns/python/tests/messagerenderer_python_test.py

@@ -32,6 +32,7 @@ class MessageRendererTest(unittest.TestCase):
         message = Message(Message.RENDER)
         message.set_qid(123)
         message.set_opcode(Opcode.QUERY())
+        message.set_rcode(Rcode.NOERROR())
         message.add_question(Question(name, c, t))
 
         self.message1 = message

+ 112 - 0
src/lib/dns/python/tests/opcode_python_test.py

@@ -0,0 +1,112 @@
+# Copyright (C) 2010  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+#
+# Tests for the opcode part of the pydnspp module
+#
+
+import unittest
+from pydnspp import *
+
+class OpcodeTest(unittest.TestCase):
+    def test_init(self):
+        self.assertRaises(TypeError, Opcode, "wrong")
+        self.assertEqual(Rcode(0).get_code(), 0)
+        self.assertEqual(Rcode(Opcode.RESERVED15_CODE).get_code(), 15)
+        self.assertRaises(OverflowError, Opcode, 16)
+
+    def test_constants(self):
+        self.assertEqual(Opcode.QUERY_CODE, Opcode(0).get_code())
+        self.assertEqual(Opcode.IQUERY_CODE, Opcode(1).get_code())
+        self.assertEqual(Opcode.NOTIFY_CODE, Opcode(4).get_code())
+        self.assertEqual(Opcode.UPDATE_CODE, Opcode(5).get_code())
+        self.assertEqual(Opcode.RESERVED15_CODE, Opcode(15).get_code())
+
+        self.assertEqual(Opcode.QUERY_CODE, Opcode.QUERY().get_code())
+        self.assertEqual(Opcode.IQUERY_CODE, Opcode.IQUERY().get_code())
+        self.assertEqual(Opcode.NOTIFY_CODE, Opcode.NOTIFY().get_code())
+        self.assertEqual(Opcode.UPDATE_CODE, Opcode.UPDATE().get_code())
+        self.assertEqual(Opcode.RESERVED15_CODE, Opcode.RESERVED15().get_code())
+
+    def test_get_code(self):
+        self.assertEqual(0, Opcode.QUERY().get_code())
+        self.assertEqual(1, Opcode.IQUERY().get_code())
+        self.assertEqual(2, Opcode.STATUS().get_code())
+        self.assertEqual(3, Opcode.RESERVED3().get_code())
+        self.assertEqual(4, Opcode.NOTIFY().get_code())
+        self.assertEqual(5, Opcode.UPDATE().get_code())
+        self.assertEqual(6, Opcode.RESERVED6().get_code())
+        self.assertEqual(7, Opcode.RESERVED7().get_code())
+        self.assertEqual(8, Opcode.RESERVED8().get_code())
+        self.assertEqual(9, Opcode.RESERVED9().get_code())
+        self.assertEqual(10, Opcode.RESERVED10().get_code())
+        self.assertEqual(11, Opcode.RESERVED11().get_code())
+        self.assertEqual(12, Opcode.RESERVED12().get_code())
+        self.assertEqual(13, Opcode.RESERVED13().get_code())
+        self.assertEqual(14, Opcode.RESERVED14().get_code())
+        self.assertEqual(15, Opcode.RESERVED15().get_code())
+
+    def test_to_text(self):
+        self.assertEqual("QUERY", Opcode.QUERY().to_text())
+        self.assertEqual("QUERY", str(Opcode.QUERY()))
+        self.assertEqual("IQUERY", Opcode.IQUERY().to_text())
+        self.assertEqual("STATUS", Opcode.STATUS().to_text())
+        self.assertEqual("RESERVED3", Opcode.RESERVED3().to_text())
+        self.assertEqual("NOTIFY", Opcode.NOTIFY().to_text())
+        self.assertEqual("UPDATE", Opcode.UPDATE().to_text())
+        self.assertEqual("RESERVED6", Opcode.RESERVED6().to_text())
+        self.assertEqual("RESERVED7", Opcode.RESERVED7().to_text())
+        self.assertEqual("RESERVED8", Opcode.RESERVED8().to_text())
+        self.assertEqual("RESERVED9", Opcode.RESERVED9().to_text())
+        self.assertEqual("RESERVED10", Opcode.RESERVED10().to_text())
+        self.assertEqual("RESERVED11", Opcode.RESERVED11().to_text())
+        self.assertEqual("RESERVED12", Opcode.RESERVED12().to_text())
+        self.assertEqual("RESERVED13", Opcode.RESERVED13().to_text())
+        self.assertEqual("RESERVED14", Opcode.RESERVED14().to_text())
+        self.assertEqual("RESERVED15", Opcode.RESERVED15().to_text())
+
+    def test_richcmp(self):
+        o1 = Opcode.QUERY()
+        o2 = Opcode.NOTIFY()
+        o3 = Opcode.NOTIFY()
+        self.assertTrue(o2 == o3)
+        self.assertFalse(o2 != o3)
+        self.assertTrue(o1 != o2)
+        self.assertFalse(o1 == 1)
+        self.assertFalse(o1 == o2)
+        # can't use assertRaises here...
+        try:
+            o1 < o2
+            self.fail("operation that should have raised an error unexpectedly succeeded")
+        except Exception as err:
+            self.assertEqual(TypeError, type(err))
+        try:
+            o1 <= o2
+            self.fail("operation that should have raised an error unexpectedly succeeded")
+        except Exception as err:
+            self.assertEqual(TypeError, type(err))
+        try:
+            o1 > o2
+            self.fail("operation that should have raised an error unexpectedly succeeded")
+        except Exception as err:
+            self.assertEqual(TypeError, type(err))
+        try:
+            o1 >= o2
+            self.fail("operation that should have raised an error unexpectedly succeeded")
+        except Exception as err:
+            self.assertEqual(TypeError, type(err))
+
+if __name__ == '__main__':
+    unittest.main()

+ 1 - 14
src/lib/dns/python/tests/question_python_test.py

@@ -20,25 +20,13 @@
 import unittest
 import os
 from pydnspp import *
+from testutil import *
 
 if "TESTDATA_PATH" in os.environ:
     testdata_path = os.environ["TESTDATA_PATH"]
 else:
     testdata_path = "../tests/testdata"
 
-def read_wire_data(filename):
-    data = bytes()
-    file = open(testdata_path + os.sep + filename, "r")
-    for line in file:
-        line = line.strip()
-        if line == "" or line.startswith("#"):
-            pass
-        else:
-            cur_data = bytes.fromhex(line)
-            data += cur_data
-
-    return data
-
 def question_from_wire(file, position = 0):
     data = read_wire_data(file)
     return Question(data, position)
@@ -102,7 +90,6 @@ class QuestionTest(unittest.TestCase):
         wiredata = read_wire_data("question_toWire2")
         self.assertEqual(renderer.get_data(), wiredata)
         self.assertRaises(TypeError, self.test_question1.to_wire, 1)
-    
 
 if __name__ == '__main__':
     unittest.main()

+ 137 - 0
src/lib/dns/python/tests/rcode_python_test.py

@@ -0,0 +1,137 @@
+# Copyright (C) 2010  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+#
+# Tests for the rcode part of the pydnspp module
+#
+
+import unittest
+from pydnspp import *
+
+class RcodeTest(unittest.TestCase):
+    def test_init(self):
+        self.assertRaises(TypeError, Rcode, "wrong")
+        self.assertRaises(OverflowError, Rcode, 65536)
+        self.assertEqual(Rcode(0).get_code(), 0)
+    
+        self.assertEqual(0, Rcode(0).get_code())
+        self.assertEqual(0xfff, Rcode(0xfff).get_code()) # possible max code
+    
+        # should fail on attempt of construction with an out of range code
+        self.assertRaises(OverflowError, Rcode, 0x1000)
+        self.assertRaises(OverflowError, Rcode, 0xffff)
+
+        # constructor with a pair of code portions
+        self.assertEqual(3, Rcode(Rcode.NXDOMAIN_CODE, 0).get_code())
+        self.assertEqual(Rcode.BADVERS_CODE, Rcode(0, 1).get_code())
+        self.assertEqual(0xfff, Rcode(0xf, 0xff).get_code())
+        self.assertRaises(OverflowError, Rcode, 0x10, 0xff)
+        
+    def test_constants(self):
+        self.assertEqual(Rcode.NOERROR_CODE, Rcode(0).get_code())
+        self.assertEqual(Rcode.FORMERR_CODE, Rcode(1).get_code())
+        self.assertEqual(Rcode.NOTIMP_CODE, Rcode(4).get_code())
+        self.assertEqual(Rcode.REFUSED_CODE, Rcode(5).get_code())
+        self.assertEqual(Rcode.RESERVED15_CODE, Rcode(15).get_code())
+        self.assertEqual(Rcode.BADVERS_CODE, Rcode(16).get_code())
+
+        self.assertEqual(Rcode.NOERROR_CODE, Rcode.NOERROR().get_code())
+        self.assertEqual(Rcode.FORMERR_CODE, Rcode.FORMERR().get_code())
+        self.assertEqual(Rcode.NOTIMP_CODE, Rcode.NOTIMP().get_code())
+        self.assertEqual(Rcode.REFUSED_CODE, Rcode.REFUSED().get_code())
+        self.assertEqual(Rcode.RESERVED15_CODE, Rcode.RESERVED15().get_code())
+        self.assertEqual(Rcode.BADVERS_CODE, Rcode.BADVERS().get_code())
+
+    def test_get_code(self):
+        self.assertEqual(0, Rcode.NOERROR().get_code())
+        self.assertEqual(1, Rcode.FORMERR().get_code())
+        self.assertEqual(2, Rcode.SERVFAIL().get_code())
+        self.assertEqual(3, Rcode.NXDOMAIN().get_code())
+        self.assertEqual(4, Rcode.NOTIMP().get_code())
+        self.assertEqual(5, Rcode.REFUSED().get_code())
+        self.assertEqual(6, Rcode.YXDOMAIN().get_code())
+        self.assertEqual(7, Rcode.YXRRSET().get_code())
+        self.assertEqual(8, Rcode.NXRRSET().get_code())
+        self.assertEqual(9, Rcode.NOTAUTH().get_code())
+        self.assertEqual(10, Rcode.NOTZONE().get_code())
+        self.assertEqual(11, Rcode.RESERVED11().get_code())
+        self.assertEqual(12, Rcode.RESERVED12().get_code())
+        self.assertEqual(13, Rcode.RESERVED13().get_code())
+        self.assertEqual(14, Rcode.RESERVED14().get_code())
+        self.assertEqual(15, Rcode.RESERVED15().get_code())
+        self.assertEqual(16, Rcode.BADVERS().get_code())
+
+    def test_get_extended_code(self):
+        self.assertEqual(0, Rcode.NOERROR().get_extended_code())
+        self.assertEqual(0, Rcode.YXRRSET().get_extended_code())
+        self.assertEqual(1, Rcode.BADVERS().get_extended_code())
+        self.assertEqual(0xab, Rcode(0xabf).get_extended_code())
+        self.assertEqual(0xff, Rcode(0xfff).get_extended_code())
+
+    def test_to_text(self):
+        self.assertEqual("NOERROR", Rcode(0).to_text())
+        self.assertEqual("NOERROR", str(Rcode(0)))
+        self.assertEqual("FORMERR", Rcode(1).to_text())
+        self.assertEqual("SERVFAIL", Rcode(2).to_text())
+        self.assertEqual("NXDOMAIN", Rcode(3).to_text())
+        self.assertEqual("NOTIMP", Rcode(4).to_text())
+        self.assertEqual("REFUSED", Rcode(5).to_text())
+        self.assertEqual("YXDOMAIN", Rcode(6).to_text())
+        self.assertEqual("YXRRSET", Rcode(7).to_text())
+        self.assertEqual("NXRRSET", Rcode(8).to_text())
+        self.assertEqual("NOTAUTH", Rcode(9).to_text())
+        self.assertEqual("NOTZONE", Rcode(10).to_text())
+        self.assertEqual("RESERVED11", Rcode(11).to_text())
+        self.assertEqual("RESERVED12", Rcode(12).to_text())
+        self.assertEqual("RESERVED13", Rcode(13).to_text())
+        self.assertEqual("RESERVED14", Rcode(14).to_text())
+        self.assertEqual("RESERVED15", Rcode(15).to_text())
+        self.assertEqual("BADVERS", Rcode(16).to_text())
+        
+        self.assertEqual("17", Rcode(Rcode.BADVERS().get_code() + 1).to_text())
+        self.assertEqual("4095", Rcode(0xfff).to_text())
+
+    def test_richcmp(self):
+        r1 = Rcode.NOERROR()
+        r2 = Rcode.FORMERR()
+        r3 = Rcode.FORMERR()
+        self.assertTrue(r2 == r3)
+        self.assertTrue(r1 != r2)
+        self.assertFalse(r1 == r2)
+        self.assertFalse(r1 != 1)
+        # can't use assertRaises here...
+        try:
+            r1 < r2
+            self.fail("operation that should have raised an error unexpectedly succeeded")
+        except Exception as err:
+            self.assertEqual(TypeError, type(err))
+        try:
+            r1 <= r2
+            self.fail("operation that should have raised an error unexpectedly succeeded")
+        except Exception as err:
+            self.assertEqual(TypeError, type(err))
+        try:
+            r1 > r2
+            self.fail("operation that should have raised an error unexpectedly succeeded")
+        except Exception as err:
+            self.assertEqual(TypeError, type(err))
+        try:
+            r1 >= r2
+            self.fail("operation that should have raised an error unexpectedly succeeded")
+        except Exception as err:
+            self.assertEqual(TypeError, type(err))
+
+if __name__ == '__main__':
+    unittest.main()

+ 45 - 0
src/lib/dns/python/tests/testutil.py

@@ -0,0 +1,45 @@
+# Copyright (C) 2010  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+# $Id$
+
+#
+# helper functions for tests taken from C++ unittests
+#
+
+import os
+
+if "TESTDATA_PATH" in os.environ:
+    testdata_path = os.environ["TESTDATA_PATH"]
+else:
+    testdata_path = "../tests/testdata"
+
+def read_wire_data(filename):
+    data = bytes()
+    for path in testdata_path.split(":"):
+        try:
+            file = open(path + os.sep + filename, "r")
+            for line in file:
+                line = line.strip()
+                if line == "" or line.startswith("#"):
+                    pass
+                else:
+                    cur_data = bytes.fromhex(line)
+                    data += cur_data
+
+            return data
+        except IOError:
+            pass
+    raise ValueError("Data file " + filename + "cannot be found")

+ 103 - 0
src/lib/dns/rcode.cc

@@ -0,0 +1,103 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <string>
+#include <sstream>
+#include <ostream>
+
+#include <exceptions/exceptions.h>
+
+#include <dns/rcode.h>
+
+using namespace std;
+
+namespace isc {
+namespace dns {
+namespace {
+// This diagram shows the wire-format representation of the 12-bit extended
+// form RCODEs and its relationship with implementation specific parameters.
+//
+//     0     3               11      15
+//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//    |UNUSED | EXTENDED-RCODE | RCODE |
+//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//                            <= EXTRCODE_SHIFT (4 bits)
+const unsigned int EXTRCODE_SHIFT = 4;
+const unsigned int RCODE_MASK = 0x000f;
+
+
+// EDNS-extended RCODEs are 12-bit unsigned integers.  0xfff is the highest.
+const uint16_t MAX_RCODE = 0xfff;
+
+const char* const rcodetext[] = {
+    "NOERROR",
+    "FORMERR",
+    "SERVFAIL",
+    "NXDOMAIN",
+    "NOTIMP",
+    "REFUSED",
+    "YXDOMAIN",
+    "YXRRSET",
+    "NXRRSET",
+    "NOTAUTH",
+    "NOTZONE",
+    "RESERVED11",
+    "RESERVED12",
+    "RESERVED13",
+    "RESERVED14",
+    "RESERVED15",
+    "BADVERS"
+};
+}
+
+Rcode::Rcode(const uint16_t code) : code_(code) {
+    if (code_ > MAX_RCODE) {
+        isc_throw(OutOfRange, "Rcode is too large to construct");
+    }
+}
+
+Rcode::Rcode(const uint8_t code, const uint8_t extended_code) :
+    code_((extended_code << EXTRCODE_SHIFT) | (code & RCODE_MASK))
+{
+    if (code > RCODE_MASK) {
+        isc_throw(OutOfRange,
+                  "Base Rcode is too large to construct: "
+                  << static_cast<unsigned int>(code));
+    }
+}
+
+uint8_t
+Rcode::getExtendedCode() const {
+    return (code_ >> EXTRCODE_SHIFT);
+}
+
+string
+Rcode::toText() const {
+    if (code_ < sizeof(rcodetext) / sizeof (const char*)) {
+        return (rcodetext[code_]);
+    }
+
+    ostringstream oss;
+    oss << code_;
+    return (oss.str());
+}
+
+ostream&
+operator<<(std::ostream& os, const Rcode& rcode) {
+    return (os << rcode.toText());
+}
+}
+}

+ 348 - 0
src/lib/dns/rcode.h

@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id$ */
+
+#include <stdint.h>
+
+#include <ostream>
+
+#ifndef __RCODE_H
+#define __RCODE_H 1
+
+namespace isc {
+namespace dns {
+
+/// \brief DNS Response Codes (RCODEs) class.
+///
+/// The \c Rcode class objects represent standard Response Codes
+/// (RCODEs) of the header section of DNS messages, and extended response
+/// codes as defined in the EDNS specification.
+///
+/// Originally RCODEs were defined as 4-bit integers (RFC1035), and then
+/// extended to 12 bits as part of the %EDNS specification (RFC2671).
+/// This API uses the 12-bit version of the definition from the beginning;
+/// applications don't have to aware of the original definition except when
+/// dealing with the wire-format representation of the %EDNS OPT RR
+/// (which is rare).
+///
+/// Like the \c Opcode class, Rcodes could be represented as bare integers,
+/// but we define a separate class to benefit from C++ type safety.
+///
+/// For convenience we also provide
+/// an enum type for pre-defined RCODE values, but it is generally advisable
+/// to handle RCODEs through this class.  In fact, public interfaces of
+/// this library uses this class to pass or return RCODEs instead of the
+/// bare code values.
+class Rcode {
+public:
+    /// Constants for pre-defined RCODE values.
+    enum CodeValue {
+        NOERROR_CODE = 0,       ///< 0: No error (RFC1035)
+        FORMERR_CODE = 1,       ///< 1: Format error (RFC1035)
+        SERVFAIL_CODE = 2,      ///< 2: Server failure (RFC1035)
+        NXDOMAIN_CODE = 3,      ///< 3: Name Error (RFC1035)
+        NOTIMP_CODE = 4,        ///< 4: Not Implemented (RFC1035)
+        REFUSED_CODE = 5,       ///< 5: Refused (RFC1035)
+        YXDOMAIN_CODE = 6,      ///< 6: Name unexpectedly exists (RFC2136)
+        YXRRSET_CODE = 7,       ///< 7: RRset unexpectedly exists (RFC2136)
+        NXRRSET_CODE = 8,       ///< 8: RRset should exist but not (RFC2136)
+        NOTAUTH_CODE = 9,       ///< 9: Server isn't authoritative (RFC2136)
+        NOTZONE_CODE = 10,      ///< 10: Name is not within the zone (RFC2136)
+        RESERVED11_CODE = 11,   ///< 11: Reserved for future use (RFC1035)
+        RESERVED12_CODE = 12,   ///< 12: Reserved for future use (RFC1035)
+        RESERVED13_CODE = 13,   ///< 13: Reserved for future use (RFC1035)
+        RESERVED14_CODE = 14,   ///< 14: Reserved for future use (RFC1035)
+        RESERVED15_CODE = 15,   ///< 15: Reserved for future use (RFC1035)
+        BADVERS_CODE = 16       ///< 16: EDNS version not implemented (RFC2671)
+    };
+
+    /// \name Constructors and Destructor
+    ///
+    /// We use the default versions of destructor, copy constructor,
+    /// and assignment operator.
+    ///
+    /// The default constructor is hidden as a result of defining the other
+    /// constructors.  This is intentional; we don't want to allow an
+    /// \c Rcode object to be constructed with an invalid state.
+    //@{
+    /// \brief Constructor from the code value.
+    ///
+    /// Since RCODEs are 12-bit values, parameters larger than 0xfff are
+    /// invalid.
+    /// If \c code is larger than 0xfff an exception of class
+    /// \c isc::OutOfRange will be thrown.
+    ///
+    /// \param code The underlying 12-bit code value of the \c Rcode.
+    explicit Rcode(const uint16_t code);
+
+    /// \brief Constructor from a pair of base and extended parts of code.
+    ///
+    /// This constructor takes two parameters, one for the lower 4 bits of
+    /// the code value, the other for the upper 8 bits, and combines them
+    /// to build a complete 12-bit code value.
+    ///
+    /// The first parameter, \c code, is the lower 4 bits, and therefore must
+    /// not exceed 15.  Otherwise, an exception of class
+    /// \c isc::OutOfRange will be thrown.
+    ///
+    /// This version of constructor is provided specifically for constructing
+    /// an Rcode from a DNS header and an %EDNS OPT RR.  Normal applications
+    /// won't have to use this constructor.
+    ///
+    /// \param code The lower 4 bits of the underlying code value.
+    /// \param extended_code The upper 8 bits of the underlying code value.
+    Rcode(const uint8_t code, const uint8_t extended_code);
+    //@}
+
+    /// \brief Returns the \c Rcode code value.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \return The underlying code value corresponding to the \c Rcode.
+    uint16_t getCode() const { return (code_); }
+
+    /// \brief Returns the upper 8-bit of the \c Rcode code value.
+    ///
+    /// Normal applications won't have to use this method.  This is provided
+    /// in case the upper 8 bits are necessary for the EDNS protocol
+    /// processing.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \return The upper 8-bit of the underlying code value.
+    uint8_t getExtendedCode() const;
+
+    /// \brief Return true iff two Rcodes are equal.
+    ///
+    /// Two Rcodes are equal iff their type codes are equal.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \param other the \c Rcode object to compare against.
+    /// \return true if the two Rcodes are equal; otherwise false.
+    bool equals(const Rcode& other) const
+    { return (code_ == other.code_); }
+
+    /// \brief Same as \c equals().
+    bool operator==(const Rcode& other) const { return (equals(other)); }
+
+    /// \brief Return true iff two Rcodes are not equal.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \param other the \c Rcode object to compare against.
+    /// \return true if the two Rcodes are not equal; otherwise false.
+    bool nequals(const Rcode& other) const
+    { return (code_ != other.code_); }
+
+    /// \brief Same as \c nequals().
+    bool operator!=(const Rcode& other) const { return (nequals(other)); }
+
+    /// \brief Convert the \c Rcode to a string.
+    ///
+    /// For pre-defined code values (see Rcode::CodeValue),
+    /// this method returns a string representation of the "mnemonic' used
+    /// for the enum and constant objects.  For example, the string for
+    /// code value 0 is "NOERROR", etc.
+    /// For other code values it returns a string representation of the decimal
+    /// number of the value, e.g. "32", "100", etc.
+    ///
+    /// If resource allocation for the string fails, a corresponding standard
+    /// exception will be thrown.
+    ///
+    /// \return A string representation of the \c Rcode.
+    std::string toText() const;
+
+    /// A constant object for the NOERROR Rcode (see \c Rcode::NOERROR_CODE).
+    static const Rcode& NOERROR();
+
+    /// A constant object for the FORMERR Rcode (see \c Rcode::FORMERR_CODE).
+    static const Rcode& FORMERR();
+
+    /// A constant object for the SERVFAIL Rcode (see \c Rcode::SERVFAIL_CODE).
+    static const Rcode& SERVFAIL();
+
+    /// A constant object for the NXDOMAIN Rcode (see \c Rcode::NXDOMAIN_CODE).
+    static const Rcode& NXDOMAIN();
+
+    /// A constant object for the NOTIMP Rcode (see \c Rcode::NOTIMP_CODE).
+    static const Rcode& NOTIMP();
+
+    /// A constant object for the REFUSED Rcode (see \c Rcode::REFUSED_CODE).
+    static const Rcode& REFUSED();
+
+    /// A constant object for the YXDOMAIN Rcode (see \c Rcode::YXDOMAIN_CODE).
+    static const Rcode& YXDOMAIN();
+
+    /// A constant object for the YXRRSET Rcode (see \c Rcode::YXRRSET_CODE).
+    static const Rcode& YXRRSET();
+
+    /// A constant object for the NXRRSET Rcode (see \c Rcode::NXRRSET_CODE).
+    static const Rcode& NXRRSET();
+
+    /// A constant object for the NOTAUTH Rcode (see \c Rcode::NOTAUTH_CODE).
+    static const Rcode& NOTAUTH();
+
+    /// A constant object for the NOTZONE Rcode (see \c Rcode::NOTZONE_CODE).
+    static const Rcode& NOTZONE();
+
+    /// A constant object for a reserved (code 11) Rcode.
+    /// (see \c Rcode::RESERVED11_CODE).
+    static const Rcode& RESERVED11();
+
+    /// A constant object for a reserved (code 12) Rcode.
+    /// (see \c Rcode::RESERVED12_CODE).
+    static const Rcode& RESERVED12();
+
+    /// A constant object for a reserved (code 13) Rcode.
+    /// (see \c Rcode::RESERVED13_CODE).
+    static const Rcode& RESERVED13();
+
+    /// A constant object for a reserved (code 14) Rcode.
+    /// (see \c Rcode::RESERVED14_CODE).
+    static const Rcode& RESERVED14();
+
+    /// A constant object for a reserved (code 15) Rcode.
+    /// (see \c Rcode::RESERVED15_CODE).
+    static const Rcode& RESERVED15();
+
+    /// A constant object for the BADVERS Rcode (see \c Rcode::BADVERS_CODE).
+    static const Rcode& BADVERS();
+private:
+    uint16_t code_;
+};
+
+inline const Rcode&
+Rcode::NOERROR() {
+    static Rcode c(0);
+    return (c);
+}
+
+inline const Rcode&
+Rcode::FORMERR() {
+    static Rcode c(1);
+    return (c);
+}
+
+inline const Rcode&
+Rcode::SERVFAIL() {
+    static Rcode c(2);
+    return (c);
+}
+
+inline const Rcode&
+Rcode::NXDOMAIN() {
+    static Rcode c(3);
+    return (c);
+}
+
+inline const Rcode&
+Rcode::NOTIMP() {
+    static Rcode c(4);
+    return (c);
+}
+
+inline const Rcode&
+Rcode::REFUSED() {
+    static Rcode c(5);
+    return (c);
+}
+
+inline const Rcode&
+Rcode::YXDOMAIN() {
+    static Rcode c(6);
+    return (c);
+}
+
+inline const Rcode&
+Rcode::YXRRSET() {
+    static Rcode c(7);
+    return (c);
+}
+
+inline const Rcode&
+Rcode::NXRRSET() {
+    static Rcode c(8);
+    return (c);
+}
+
+inline const Rcode&
+Rcode::NOTAUTH() {
+    static Rcode c(9);
+    return (c);
+}
+
+inline const Rcode&
+Rcode::NOTZONE() {
+    static Rcode c(10);
+    return (c);
+}
+
+inline const Rcode&
+Rcode::RESERVED11() {
+    static Rcode c(11);
+    return (c);
+}
+
+inline const Rcode&
+Rcode::RESERVED12() {
+    static Rcode c(12);
+    return (c);
+}
+
+inline const Rcode&
+Rcode::RESERVED13() {
+    static Rcode c(13);
+    return (c);
+}
+
+inline const Rcode&
+Rcode::RESERVED14() {
+    static Rcode c(14);
+    return (c);
+}
+
+inline const Rcode&
+Rcode::RESERVED15() {
+    static Rcode c(15);
+    return (c);
+}
+
+inline const Rcode&
+Rcode::BADVERS() {
+    static Rcode c(16);
+    return (c);
+}
+
+/// \brief Insert the \c Rcode as a string into stream.
+///
+/// This method convert \c rcode into a string and inserts it into the
+/// output stream \c os.
+///
+/// \param os A \c std::ostream object on which the insertion operation is
+/// performed.
+/// \param rcode A reference to an \c Rcode object output by the operation.
+/// \return A reference to the same \c std::ostream object referenced by
+/// parameter \c os after the insertion operation.
+std::ostream& operator<<(std::ostream& os, const Rcode& rcode);
+}
+}
+#endif  // RCODE_H
+
+// Local Variables: 
+// mode: c++
+// End: 

+ 1 - 1
src/lib/dns/rrclass-placeholder.h

@@ -211,7 +211,7 @@ public:
     /// \brief Same as \c equals().
     bool operator==(const RRClass& other) const { return (equals(other)); }
 
-    /// \brief Return true iff two RRClasses are equal.
+    /// \brief Return true iff two RRClasses are not equal.
     ///
     /// This method never throws an exception.
     ///

+ 4 - 4
src/lib/dns/rrset.h

@@ -692,8 +692,8 @@ public:
     /// \brief Adds an RRSIG RR to this RRset's signatures
     virtual void addRRsig(const rdata::RdataPtr rdata) {
         if (!rrsig_) {
-            rrsig_ = RRsetPtr(new RRset(this->getName(), this->getClass(),
-                                        RRType::RRSIG(), this->getTTL()));
+            rrsig_ = RRsetPtr(new RRset(getName(), getClass(),
+                                        RRType::RRSIG(), getTTL()));
         }
         rrsig_->addRdata(rdata);
     }
@@ -703,8 +703,8 @@ public:
         RdataIteratorPtr it = sigs.getRdataIterator();
 
         if (!rrsig_) {
-            rrsig_ = RRsetPtr(new RRset(this->getName(), this->getClass(),
-                                        RRType::RRSIG(), this->getTTL()));
+            rrsig_ = RRsetPtr(new RRset(getName(), getClass(),
+                                        RRType::RRSIG(), getTTL()));
         }
 
         for (it->first(); !it->isLast(); it->next()) {

+ 1 - 1
src/lib/dns/rrtype-placeholder.h

@@ -223,7 +223,7 @@ public:
     /// \brief Same as \c equals().
     bool operator==(const RRType& other) const { return (equals(other)); }
 
-    /// \brief Return true iff two RRTypes are equal.
+    /// \brief Return true iff two RRTypes are not equal.
     ///
     /// This method never throws an exception.
     ///

+ 7 - 97
src/lib/dns/tests/Makefile.am

@@ -1,6 +1,9 @@
+SUBDIRS = testdata .
+
 AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
 AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
-AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(srcdir)/testdata\"
+AM_CPPFLAGS += -DTEST_DATA_SRCDIR=\"$(srcdir)/testdata\"
+AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(top_builddir)/src/lib/dns/tests/testdata\"
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 
 if USE_STATIC_LINK
@@ -14,10 +17,13 @@ if HAVE_GTEST
 TESTS += run_unittests
 run_unittests_SOURCES = unittest_util.h unittest_util.cc
 run_unittests_SOURCES += buffer_unittest.cc name_unittest.cc
+run_unittests_SOURCES += edns_unittest.cc
 run_unittests_SOURCES += messagerenderer_unittest.cc
 run_unittests_SOURCES += rrclass_unittest.cc rrtype_unittest.cc
 run_unittests_SOURCES += rrttl_unittest.cc
 run_unittests_SOURCES += dnssectime_unittest.cc
+run_unittests_SOURCES += opcode_unittest.cc
+run_unittests_SOURCES += rcode_unittest.cc
 run_unittests_SOURCES += rdata_unittest.h rdata_unittest.cc
 run_unittests_SOURCES += rdata_in_a_unittest.cc rdata_in_aaaa_unittest.cc
 run_unittests_SOURCES += rdata_ns_unittest.cc rdata_soa_unittest.cc
@@ -49,99 +55,3 @@ run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 endif
 
 noinst_PROGRAMS = $(TESTS)
-
-# NOTE: keep this in sync with real file listing
-# so is included in tarball
-EXTRA_DIST = testdata/gen-wiredata.py.in
-EXTRA_DIST += testdata/message_fromWire1
-EXTRA_DIST += testdata/message_fromWire10
-EXTRA_DIST += testdata/message_fromWire10.spec
-EXTRA_DIST += testdata/message_fromWire11
-EXTRA_DIST += testdata/message_fromWire11.spec
-EXTRA_DIST += testdata/message_fromWire2
-EXTRA_DIST += testdata/message_fromWire3
-EXTRA_DIST += testdata/message_fromWire4
-EXTRA_DIST += testdata/message_fromWire5
-EXTRA_DIST += testdata/message_fromWire6
-EXTRA_DIST += testdata/message_fromWire7
-EXTRA_DIST += testdata/message_fromWire8
-EXTRA_DIST += testdata/message_fromWire9
-EXTRA_DIST += testdata/message_toWire1
-EXTRA_DIST += testdata/name_fromWire1
-EXTRA_DIST += testdata/name_fromWire10
-EXTRA_DIST += testdata/name_fromWire11
-EXTRA_DIST += testdata/name_fromWire12
-EXTRA_DIST += testdata/name_fromWire13
-EXTRA_DIST += testdata/name_fromWire14
-EXTRA_DIST += testdata/name_fromWire2
-EXTRA_DIST += testdata/name_fromWire3_1
-EXTRA_DIST += testdata/name_fromWire3_2
-EXTRA_DIST += testdata/name_fromWire4
-EXTRA_DIST += testdata/name_fromWire6
-EXTRA_DIST += testdata/name_fromWire7
-EXTRA_DIST += testdata/name_fromWire8
-EXTRA_DIST += testdata/name_fromWire9
-EXTRA_DIST += testdata/name_toWire1
-EXTRA_DIST += testdata/name_toWire2
-EXTRA_DIST += testdata/name_toWire3
-EXTRA_DIST += testdata/name_toWire4
-EXTRA_DIST += testdata/name_toWire5
-EXTRA_DIST += testdata/name_toWire5.spec
-EXTRA_DIST += testdata/name_toWire6
-EXTRA_DIST += testdata/name_toWire6.spec
-EXTRA_DIST += testdata/question_fromWire
-EXTRA_DIST += testdata/question_toWire1
-EXTRA_DIST += testdata/question_toWire2
-EXTRA_DIST += testdata/rdata_cname_fromWire
-EXTRA_DIST += testdata/rdata_dname_fromWire
-EXTRA_DIST += testdata/rdata_dnskey_fromWire
-EXTRA_DIST += testdata/rdata_ds_fromWire
-EXTRA_DIST += testdata/rdata_in_a_fromWire
-EXTRA_DIST += testdata/rdata_in_aaaa_fromWire
-EXTRA_DIST += testdata/rdata_mx_fromWire
-EXTRA_DIST += testdata/rdata_mx_toWire1
-EXTRA_DIST += testdata/rdata_ns_fromWire
-EXTRA_DIST += testdata/rdata_nsec3_fromWire1
-EXTRA_DIST += testdata/rdata_nsec3_fromWire2
-EXTRA_DIST += testdata/rdata_nsec3_fromWire3
-EXTRA_DIST += testdata/rdata_nsec3param_fromWire1
-EXTRA_DIST += testdata/rdata_nsec_fromWire1
-EXTRA_DIST += testdata/rdata_nsec_fromWire10
-EXTRA_DIST += testdata/rdata_nsec_fromWire10.spec
-EXTRA_DIST += testdata/rdata_nsec_fromWire2
-EXTRA_DIST += testdata/rdata_nsec_fromWire3
-EXTRA_DIST += testdata/rdata_nsec_fromWire4
-EXTRA_DIST += testdata/rdata_nsec_fromWire4.spec
-EXTRA_DIST += testdata/rdata_nsec_fromWire5
-EXTRA_DIST += testdata/rdata_nsec_fromWire5.spec
-EXTRA_DIST += testdata/rdata_nsec_fromWire6
-EXTRA_DIST += testdata/rdata_nsec_fromWire6.spec
-EXTRA_DIST += testdata/rdata_nsec_fromWire7
-EXTRA_DIST += testdata/rdata_nsec_fromWire7.spec
-EXTRA_DIST += testdata/rdata_nsec_fromWire8
-EXTRA_DIST += testdata/rdata_nsec_fromWire8.spec
-EXTRA_DIST += testdata/rdata_nsec_fromWire9
-EXTRA_DIST += testdata/rdata_nsec_fromWire9.spec
-EXTRA_DIST += testdata/rdata_opt_fromWire
-EXTRA_DIST += testdata/rdata_rrsig_fromWire1
-EXTRA_DIST += testdata/rdata_rrsig_fromWire2
-EXTRA_DIST += testdata/rdata_rrsig_fromWire2.spec
-EXTRA_DIST += testdata/rdata_soa_fromWire
-EXTRA_DIST += testdata/rdata_soa_toWireUncompressed
-EXTRA_DIST += testdata/rdata_soa_toWireUncompressed.spec
-EXTRA_DIST += testdata/rdata_txt_fromWire1
-EXTRA_DIST += testdata/rdata_txt_fromWire2
-EXTRA_DIST += testdata/rdata_txt_fromWire2.spec
-EXTRA_DIST += testdata/rdata_txt_fromWire3
-EXTRA_DIST += testdata/rdata_txt_fromWire3.spec
-EXTRA_DIST += testdata/rdata_txt_fromWire4
-EXTRA_DIST += testdata/rdata_txt_fromWire4.spec
-EXTRA_DIST += testdata/rdata_txt_fromWire5
-EXTRA_DIST += testdata/rdata_txt_fromWire5.spec
-EXTRA_DIST += testdata/rdata_unknown_fromWire
-EXTRA_DIST += testdata/rrcode16_fromWire1
-EXTRA_DIST += testdata/rrcode16_fromWire2
-EXTRA_DIST += testdata/rrcode32_fromWire1
-EXTRA_DIST += testdata/rrcode32_fromWire2
-EXTRA_DIST += testdata/rrset_toWire1
-EXTRA_DIST += testdata/rrset_toWire2

+ 265 - 0
src/lib/dns/tests/edns_unittest.cc

@@ -0,0 +1,265 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <sstream>
+
+#include <exceptions/exceptions.h>
+
+#include <dns/buffer.h>
+#include <dns/edns.h>
+#include <dns/exceptions.h>
+#include <dns/message.h>
+#include <dns/messagerenderer.h>
+#include <dns/name.h>
+#include <dns/rcode.h>
+#include <dns/rdataclass.h>
+#include <dns/rrclass.h>
+#include <dns/rrttl.h>
+#include <dns/rrtype.h>
+
+#include <gtest/gtest.h>
+
+#include "unittest_util.h"
+
+using isc::UnitTestUtil;
+using namespace std;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+
+const uint8_t EDNS::SUPPORTED_VERSION;
+
+namespace {
+class EDNSTest : public ::testing::Test {
+protected:
+    EDNSTest() : rrtype(RRType::OPT()), buffer(NULL, 0), obuffer(0),
+                 renderer(obuffer), rcode(0)
+    {
+        opt_rdata = ConstRdataPtr(new generic::OPT());
+        edns_base.setUDPSize(4096);
+    }
+    RRType rrtype;
+    EDNS edns_base;
+    ConstEDNSPtr edns;
+    InputBuffer buffer;
+    OutputBuffer obuffer;
+    MessageRenderer renderer;
+    ConstRdataPtr opt_rdata;
+    Rcode rcode;
+    vector<unsigned char> wiredata;
+};
+
+// RRClass of EDNS OPT means UDP buffer size
+const RRClass rrclass(4096);
+// RRTTL of EDNS OPT encodes extended-rcode, version, and DO bit
+const RRTTL rrttl_do_on(0x00008000); // DO=on
+const RRTTL rrttl_do_off(0);         // DO=off
+const RRTTL rrttl_badver(0x00018000); // version=1, DO=on
+
+TEST_F(EDNSTest, badVerConstruct) {
+    EXPECT_THROW(EDNS(1), isc::InvalidParameter);
+}
+
+TEST_F(EDNSTest, DNSSECDOBit) {
+    // tests for EDNS from RR
+
+    // DO bit is on, so DNSSEC should be considered to be supported.
+    EXPECT_TRUE(EDNS(Name::ROOT_NAME(), rrclass, rrtype,
+                     rrttl_do_on, *opt_rdata).getDNSSECAwareness());
+
+    // DO bit is off.  DNSSEC should be considered to be unsupported.
+    EXPECT_FALSE(EDNS(Name::ROOT_NAME(), rrclass, rrtype,
+                      rrttl_do_off, *opt_rdata).getDNSSECAwareness());
+
+    // tests for EDNS constructed by hand
+    EXPECT_FALSE(edns_base.getDNSSECAwareness()); // false by default
+    edns_base.setDNSSECAwareness(true);          // enable by hand
+    EXPECT_TRUE(edns_base.getDNSSECAwareness());
+    edns_base.setDNSSECAwareness(false); // disable by hand
+    EXPECT_FALSE(edns_base.getDNSSECAwareness());
+}
+
+TEST_F(EDNSTest, UDPSize) {
+    EXPECT_EQ(4096, EDNS(Name::ROOT_NAME(), rrclass, rrtype, rrttl_do_on,
+                         *opt_rdata).getUDPSize());
+
+    // EDNS UDP size smaller than the traditional max, 512.  Unusual, but
+    // not prohibited.
+    edns_base.setUDPSize(511);
+    EXPECT_EQ(511, edns_base.getUDPSize());
+
+    // Even 0 is okay.
+    edns_base.setUDPSize(0);
+    EXPECT_EQ(0, edns_base.getUDPSize());
+
+    // Possible max value is also okay, although the higher layer app may
+    // adjust it to a reasonably lower value
+    edns_base.setUDPSize(65535);
+    EXPECT_EQ(65535, edns_base.getUDPSize());
+}
+
+TEST_F(EDNSTest, getVersion) {
+    // Constructed by hand
+    EXPECT_EQ(EDNS::SUPPORTED_VERSION, EDNS().getVersion());
+
+    // From RR
+    EXPECT_EQ(EDNS::SUPPORTED_VERSION,
+              EDNS(Name::ROOT_NAME(), rrclass, rrtype, rrttl_do_on,
+                   *opt_rdata).getVersion());
+}
+
+TEST_F(EDNSTest, BadWireData) {
+    // Incompatible RR type
+    EXPECT_THROW(EDNS(Name::ROOT_NAME(), rrclass, RRType::A(),
+                      rrttl_do_on, *opt_rdata), isc::InvalidParameter);
+
+    // OPT RR of a non root name
+    EXPECT_THROW(EDNS(Name("example.com"), rrclass, rrtype,
+                      rrttl_do_on, *opt_rdata), DNSMessageFORMERR);
+                 
+    // Unsupported Version
+    EXPECT_THROW(EDNS(Name::ROOT_NAME(), rrclass, rrtype,
+                      rrttl_badver, *opt_rdata), DNSMessageBADVERS);
+}
+
+TEST_F(EDNSTest, toText) {
+    // Typical case, disabling DNSSEC
+    EXPECT_EQ("; EDNS: version: 0, flags:; udp: 4096\n",
+              EDNS(Name::ROOT_NAME(), rrclass, rrtype, rrttl_do_off,
+                   *opt_rdata).toText());
+
+    // Typical case, enabling DNSSEC
+    EXPECT_EQ("; EDNS: version: 0, flags: do; udp: 4096\n",
+              EDNS(Name::ROOT_NAME(), rrclass, rrtype, rrttl_do_on,
+                   *opt_rdata).toText());
+
+    // Non-0 extended Rcode: ignored in the toText() output.
+    EXPECT_EQ("; EDNS: version: 0, flags: do; udp: 4096\n",
+              EDNS(Name::ROOT_NAME(), rrclass, rrtype,
+                   RRTTL(0x01008000), *opt_rdata).toText());
+
+    // Unknown flag: ignored in the toText() output.
+    EXPECT_EQ("; EDNS: version: 0, flags: do; udp: 4096\n",
+              EDNS(Name::ROOT_NAME(), rrclass, rrtype,
+                   RRTTL(0x00008001), *opt_rdata).toText());
+}
+
+TEST_F(EDNSTest, toWireRenderer) {
+    // Typical case, (explicitly) disabling DNSSEC
+    edns_base.setDNSSECAwareness(false);
+    EXPECT_EQ(1, edns_base.toWire(renderer,
+                                  Rcode::NOERROR().getExtendedCode()));
+    UnitTestUtil::readWireData("edns_toWire1.wire", wiredata);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
+                        renderer.getLength(), &wiredata[0], wiredata.size());
+
+    // Typical case, enabling DNSSEC
+    renderer.clear();
+    wiredata.clear();
+    edns_base.setDNSSECAwareness(true);
+    EXPECT_EQ(1, edns_base.toWire(renderer,
+                                  Rcode::NOERROR().getExtendedCode()));
+    UnitTestUtil::readWireData("edns_toWire2.wire", wiredata);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
+                        renderer.getLength(), &wiredata[0], wiredata.size());
+
+    // Non-0 extended Rcode
+    renderer.clear();
+    wiredata.clear();
+    edns_base.setDNSSECAwareness(true);
+    EXPECT_EQ(1, edns_base.toWire(renderer,
+                                  Rcode::BADVERS().getExtendedCode()));
+    UnitTestUtil::readWireData("edns_toWire3.wire", wiredata);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
+                        renderer.getLength(), &wiredata[0], wiredata.size());
+
+    // Uncommon UDP buffer size
+    renderer.clear();
+    wiredata.clear();
+    edns_base.setDNSSECAwareness(true);
+    edns_base.setUDPSize(511);
+    EXPECT_EQ(1, edns_base.toWire(renderer,
+                                  Rcode::NOERROR().getExtendedCode()));
+    UnitTestUtil::readWireData("edns_toWire4.wire", wiredata);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
+                        renderer.getLength(), &wiredata[0], wiredata.size());
+
+    // From RR with unknown flag.  If used for toWire(), the unknown flag
+    // should disappear.
+    renderer.clear();
+    wiredata.clear();
+    EXPECT_EQ(1, EDNS(Name::ROOT_NAME(), rrclass, rrtype, RRTTL(0x00008001),
+                      *opt_rdata).toWire(renderer,
+                                         Rcode::NOERROR().getExtendedCode()));
+    UnitTestUtil::readWireData("edns_toWire2.wire", wiredata);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
+                        renderer.getLength(), &wiredata[0], wiredata.size());
+
+    // If the available length in the renderer is not sufficient for the OPT
+    // RR, it shouldn't be inserted.
+    renderer.clear();
+    renderer.setLengthLimit(10); // 10 = minimum length of OPT RR - 1
+    edns_base.setDNSSECAwareness(true);
+    edns_base.setUDPSize(4096);
+    EXPECT_EQ(0, edns_base.toWire(renderer,
+                                  Rcode::NOERROR().getExtendedCode()));
+    // renderer should be intact
+    EXPECT_EQ(0, renderer.getLength());
+}
+
+TEST_F(EDNSTest, toWireBuffer) {
+    // "to renderer" and "to buffer" should generally produce the same result.
+    // for simplicity we only check one typical case to confirm that.
+    EXPECT_EQ(1, edns_base.toWire(obuffer,
+                                  Rcode::NOERROR().getExtendedCode()));
+    UnitTestUtil::readWireData("edns_toWire1.wire", wiredata);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(),
+                        obuffer.getLength(), &wiredata[0], wiredata.size());
+}
+
+TEST_F(EDNSTest, createFromRR) {
+    uint8_t extended_rcode;
+
+    // normal case
+    edns = ConstEDNSPtr(createEDNSFromRR(Name::ROOT_NAME(), rrclass, rrtype,
+                                         rrttl_do_on, *opt_rdata,
+                                         extended_rcode));
+    EXPECT_EQ(EDNS::SUPPORTED_VERSION, edns->getVersion());
+    EXPECT_TRUE(edns->getDNSSECAwareness());
+    EXPECT_EQ(4096, edns->getUDPSize());
+    EXPECT_EQ(0, static_cast<int>(extended_rcode));
+
+    // non-0 extended rcode
+    extended_rcode = 0;
+    ConstEDNSPtr(createEDNSFromRR(Name::ROOT_NAME(), rrclass, rrtype,
+                                  RRTTL(0x01008000), *opt_rdata,
+                                  extended_rcode));
+    EXPECT_EQ(1, static_cast<int>(extended_rcode));
+
+    // creation triggers an exception.  extended_rcode must be intact.
+    extended_rcode = 0;
+    EXPECT_THROW(createEDNSFromRR(Name::ROOT_NAME(), rrclass, rrtype,
+                                  rrttl_badver, *opt_rdata, extended_rcode),
+                 DNSMessageBADVERS);
+    EXPECT_EQ(0, static_cast<int>(extended_rcode));
+}
+
+// test operator<<.  We simply confirm it appends the result of toText().
+TEST_F(EDNSTest, LeftShiftOperator) {
+    ostringstream oss;
+    oss << edns_base;
+    EXPECT_EQ(edns_base.toText(), oss.str());
+}
+}

+ 46 - 104
src/lib/dns/tests/message_unittest.cc

@@ -17,10 +17,13 @@
 #include <exceptions/exceptions.h>
 
 #include <dns/buffer.h>
+#include <dns/edns.h>
 #include <dns/exceptions.h>
 #include <dns/message.h>
 #include <dns/messagerenderer.h>
 #include <dns/question.h>
+#include <dns/opcode.h>
+#include <dns/rcode.h>
 #include <dns/rdataclass.h>
 #include <dns/rrclass.h>
 #include <dns/rrttl.h>
@@ -76,23 +79,25 @@ MessageTest::factoryFromFile(Message& message, const char* datafile) {
     message.fromWire(buffer);
 }
 
-TEST_F(MessageTest, RcodeConstruct) {
-    // normal cases
-    EXPECT_EQ(0, Rcode(0).getCode());
-    EXPECT_EQ(0xfff, Rcode(0xfff).getCode()); // possible max code
+TEST_F(MessageTest, getEDNS) {
+    EXPECT_FALSE(message_parse.getEDNS()); // by default EDNS isn't set
 
-    // should fail on attempt of construction with an out of range code
-    EXPECT_THROW(Rcode(0x1000), isc::OutOfRange);
-    EXPECT_THROW(Rcode(0xffff), isc::OutOfRange);
+    factoryFromFile(message_parse, "message_fromWire10.wire");
+    EXPECT_TRUE(message_parse.getEDNS());
+    EXPECT_EQ(0, message_parse.getEDNS()->getVersion());
+    EXPECT_EQ(4096, message_parse.getEDNS()->getUDPSize());
+    EXPECT_TRUE(message_parse.getEDNS()->getDNSSECAwareness());
 }
 
-TEST_F(MessageTest, RcodeToText) {
-    EXPECT_EQ("NOERROR", Rcode::NOERROR().toText());
-    EXPECT_EQ("BADVERS", Rcode::BADVERS().toText());
-    EXPECT_EQ("17", Rcode(Rcode::BADVERS().getCode() + 1).toText());
-    EXPECT_EQ("4095", Rcode(Rcode(0xfff)).toText());
-}
+TEST_F(MessageTest, setEDNS) {
+    // setEDNS() isn't allowed in the parse mode
+    EXPECT_THROW(message_parse.setEDNS(EDNSPtr(new EDNS())),
+                 InvalidMessageOperation);
 
+    EDNSPtr edns = EDNSPtr(new EDNS());
+    message_render.setEDNS(edns);
+    EXPECT_EQ(edns, message_render.getEDNS());
+}
 
 TEST_F(MessageTest, fromWire) {
     factoryFromFile(message_parse, "message_fromWire1");
@@ -127,89 +132,14 @@ TEST_F(MessageTest, fromWire) {
     EXPECT_TRUE(it->isLast());
 }
 
-TEST_F(MessageTest, GetEDNS0DOBit) {
-    // Without EDNS0, DNSSEC is considered to be unsupported.
-    factoryFromFile(message_parse, "message_fromWire1");
-    EXPECT_FALSE(message_parse.isDNSSECSupported());
-
-    // If DO bit is on, DNSSEC is considered to be supported.
-    message_parse.clear(Message::PARSE);
-    factoryFromFile(message_parse, "message_fromWire2");
-    EXPECT_TRUE(message_parse.isDNSSECSupported());
-
-    // If DO bit is off, DNSSEC is considered to be unsupported.
-    message_parse.clear(Message::PARSE);
-    factoryFromFile(message_parse, "message_fromWire3");
-    EXPECT_FALSE(message_parse.isDNSSECSupported());
-}
-
-TEST_F(MessageTest, SetEDNS0DOBit) {
-    // By default, it's false, and we can enable/disable it.
-    EXPECT_FALSE(message_render.isDNSSECSupported());
-    message_render.setDNSSECSupported(true);
-    EXPECT_TRUE(message_render.isDNSSECSupported());
-    message_render.setDNSSECSupported(false);
-    EXPECT_FALSE(message_render.isDNSSECSupported());
-
-    // A message in the parse mode doesn't allow this flag to be set.
-    EXPECT_THROW(message_parse.setDNSSECSupported(true),
-                 InvalidMessageOperation);
-    // Once converted to the render mode, it works as above
-    message_parse.makeResponse();
-    EXPECT_FALSE(message_parse.isDNSSECSupported());
-    message_parse.setDNSSECSupported(true);
-    EXPECT_TRUE(message_parse.isDNSSECSupported());
-    message_parse.setDNSSECSupported(false);
-    EXPECT_FALSE(message_parse.isDNSSECSupported());
-}
-
-TEST_F(MessageTest, GetEDNS0UDPSize) {
-    // Without EDNS0, the default max UDP size is used.
-    factoryFromFile(message_parse, "message_fromWire1");
-    EXPECT_EQ(Message::DEFAULT_MAX_UDPSIZE, message_parse.getUDPSize());
-
-    // If the size specified in EDNS0 > default max, use it.
-    message_parse.clear(Message::PARSE);
-    factoryFromFile(message_parse, "message_fromWire2");
-    EXPECT_EQ(4096, message_parse.getUDPSize());
-
-    // If the size specified in EDNS0 < default max, keep using the default.
-    message_parse.clear(Message::PARSE);
-    factoryFromFile(message_parse, "message_fromWire8");
-    EXPECT_EQ(Message::DEFAULT_MAX_UDPSIZE, message_parse.getUDPSize());
-}
-
-TEST_F(MessageTest, SetEDNS0UDPSize) {
-    // The default size if unspecified
-    EXPECT_EQ(Message::DEFAULT_MAX_UDPSIZE, message_render.getUDPSize());
-    // A common buffer size with EDNS, should succeed
-    message_render.setUDPSize(4096);
-    EXPECT_EQ(4096, message_render.getUDPSize());
-    // Unusual large value, but accepted
-    message_render.setUDPSize(0xffff);
-    EXPECT_EQ(0xffff, message_render.getUDPSize());
-    // Too small is value is rejected
-    EXPECT_THROW(message_render.setUDPSize(511), InvalidMessageUDPSize);
-
-    // A message in the parse mode doesn't allow the set operation.
-    EXPECT_THROW(message_parse.setUDPSize(4096), InvalidMessageOperation);
-    // Once converted to the render mode, it works as above.
-    message_parse.makeResponse();
-    message_parse.setUDPSize(4096);
-    EXPECT_EQ(4096, message_parse.getUDPSize());
-    message_parse.setUDPSize(0xffff);
-    EXPECT_EQ(0xffff, message_parse.getUDPSize());
-    EXPECT_THROW(message_parse.setUDPSize(511), InvalidMessageUDPSize);
-}
-
-TEST_F(MessageTest, EDNS0ExtCode) {
+TEST_F(MessageTest, EDNS0ExtRcode) {
     // Extended Rcode = BADVERS
-    factoryFromFile(message_parse, "message_fromWire10");
+    factoryFromFile(message_parse, "message_fromWire10.wire");
     EXPECT_EQ(Rcode::BADVERS(), message_parse.getRcode());
 
     // Maximum extended Rcode
     message_parse.clear(Message::PARSE);
-    factoryFromFile(message_parse, "message_fromWire11");
+    factoryFromFile(message_parse, "message_fromWire11.wire");
     EXPECT_EQ(0xfff, message_parse.getRcode().getCode());
 }
 
@@ -221,19 +151,6 @@ TEST_F(MessageTest, BadEDNS0) {
     message_parse.clear(Message::PARSE);
     EXPECT_THROW(factoryFromFile(message_parse, "message_fromWire5"),
                  DNSMessageFORMERR);
-    // OPT RR of a non root name
-    message_parse.clear(Message::PARSE);
-    EXPECT_THROW(factoryFromFile(message_parse, "message_fromWire6"),
-                 DNSMessageFORMERR);
-    // 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?
-    message_parse.clear(Message::PARSE);
-    EXPECT_NO_THROW(factoryFromFile(message_parse, "message_fromWire7"));
-    // Unsupported Version
-    message_parse.clear(Message::PARSE);
-    EXPECT_THROW(factoryFromFile(message_parse, "message_fromWire9"),
-                 DNSMessageBADVERS);
 }
 
 TEST_F(MessageTest, toWire) {
@@ -262,4 +179,29 @@ TEST_F(MessageTest, toWire) {
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(),
                         obuffer.getLength(), &data[0], data.size());
 }
+
+TEST_F(MessageTest, toWireInParseMode) {
+    // toWire() isn't allowed in the parse mode.
+    EXPECT_THROW(message_parse.toWire(renderer), InvalidMessageOperation);
+}
+
+TEST_F(MessageTest, toWireWithoutOpcode) {
+    message_render.setRcode(Rcode::NOERROR());
+    EXPECT_THROW(message_render.toWire(renderer), InvalidMessageOperation);
+}
+
+TEST_F(MessageTest, toWireWithoutRcode) {
+    message_render.setOpcode(Opcode::QUERY());
+    EXPECT_THROW(message_render.toWire(renderer), InvalidMessageOperation);
+}
+
+TEST_F(MessageTest, toTextWithoutOpcode) {
+    message_render.setRcode(Rcode::NOERROR());
+    EXPECT_THROW(message_render.toText(), InvalidMessageOperation);
+}
+
+TEST_F(MessageTest, toTextWithoutRcode) {
+    message_render.setOpcode(Opcode::QUERY());
+    EXPECT_THROW(message_render.toText(), InvalidMessageOperation);
+}
 }

+ 2 - 2
src/lib/dns/tests/messagerenderer_unittest.cc

@@ -130,7 +130,7 @@ TEST_F(MessageRendererTest, writeNameCaseSensitiveCompress) {
     // name compression in case sensitive manner.  See the data file
     // description for details.
     renderer.setCompressMode(MessageRenderer::CASE_SENSITIVE);
-    UnitTestUtil::readWireData("name_toWire5", data);
+    UnitTestUtil::readWireData("name_toWire5.wire", data);
     renderer.writeName(Name("a.example.com."));
     renderer.writeName(Name("b.eXample.com."));
     renderer.writeName(Name("c.eXample.com."));
@@ -140,7 +140,7 @@ TEST_F(MessageRendererTest, writeNameCaseSensitiveCompress) {
 
 TEST_F(MessageRendererTest, writeNameMixedCaseCompress) {
     renderer.setCompressMode(MessageRenderer::CASE_SENSITIVE);
-    UnitTestUtil::readWireData("name_toWire6", data);
+    UnitTestUtil::readWireData("name_toWire6.wire", data);
     renderer.writeName(Name("a.example.com."));
     renderer.writeName(Name("b.eXample.com."));
 

+ 108 - 0
src/lib/dns/tests/opcode_unittest.cc

@@ -0,0 +1,108 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <vector>
+#include <sstream>
+
+#include <exceptions/exceptions.h>
+
+#include <dns/opcode.h>
+
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc::dns;
+
+namespace {
+TEST(OpcodeTest, construct) {
+    // This test also tests getCode()
+    EXPECT_EQ(0, Opcode(0).getCode());
+    EXPECT_EQ(15, Opcode(Opcode::RESERVED15_CODE).getCode());
+
+    EXPECT_THROW(Opcode(16), isc::OutOfRange);
+}
+
+TEST(OpcodeTest, constants) {
+    // We'll only test arbitrarily chosen subsets of the codes.
+    // This class is quite simple, so it should be suffice.
+
+    EXPECT_EQ(Opcode::QUERY_CODE, Opcode(0).getCode());
+    EXPECT_EQ(Opcode::IQUERY_CODE, Opcode(1).getCode());
+    EXPECT_EQ(Opcode::NOTIFY_CODE, Opcode(4).getCode());
+    EXPECT_EQ(Opcode::UPDATE_CODE, Opcode(5).getCode());
+    EXPECT_EQ(Opcode::RESERVED15_CODE, Opcode(15).getCode());
+
+    EXPECT_EQ(Opcode::QUERY_CODE, Opcode::QUERY().getCode());
+    EXPECT_EQ(Opcode::IQUERY_CODE, Opcode::IQUERY().getCode());
+    EXPECT_EQ(Opcode::NOTIFY_CODE, Opcode::NOTIFY().getCode());
+    EXPECT_EQ(Opcode::UPDATE_CODE, Opcode::UPDATE().getCode());
+    EXPECT_EQ(Opcode::RESERVED15_CODE, Opcode::RESERVED15().getCode());
+}
+
+TEST(OpcodeTest, equal) {
+    EXPECT_TRUE(Opcode::QUERY() == Opcode(Opcode::QUERY_CODE));
+    EXPECT_TRUE(Opcode::QUERY().equals(Opcode(Opcode::QUERY_CODE)));
+    EXPECT_TRUE(Opcode::IQUERY() == Opcode(Opcode::IQUERY_CODE));
+    EXPECT_TRUE(Opcode::IQUERY().equals(Opcode(Opcode::IQUERY_CODE)));
+    EXPECT_TRUE(Opcode::NOTIFY() == Opcode(Opcode::NOTIFY_CODE));
+    EXPECT_TRUE(Opcode::NOTIFY().equals(Opcode(Opcode::NOTIFY_CODE)));
+    EXPECT_TRUE(Opcode::UPDATE() == Opcode(Opcode::UPDATE_CODE));
+    EXPECT_TRUE(Opcode::UPDATE().equals(Opcode(Opcode::UPDATE_CODE)));
+    EXPECT_TRUE(Opcode::RESERVED15() == Opcode(Opcode::RESERVED15()));
+    EXPECT_TRUE(Opcode::RESERVED15().equals(Opcode(Opcode::RESERVED15())));
+}
+
+TEST(OpcodeTest, nequal) {
+    EXPECT_TRUE(Opcode::QUERY() != Opcode::IQUERY());
+    EXPECT_TRUE(Opcode::QUERY().nequals(Opcode::IQUERY()));
+    EXPECT_TRUE(Opcode::NOTIFY() != Opcode(1));
+    EXPECT_TRUE(Opcode::NOTIFY().nequals(Opcode(1)));
+    EXPECT_TRUE(Opcode(10) != Opcode(11));
+    EXPECT_TRUE(Opcode(10).nequals(Opcode(11)));
+}
+
+TEST(OpcodeTest, toText) {
+    vector<const char*> expects;
+    expects.resize(Opcode::RESERVED15_CODE + 1);
+    expects[Opcode::QUERY_CODE] = "QUERY";
+    expects[Opcode::IQUERY_CODE] = "IQUERY";
+    expects[Opcode::STATUS_CODE] = "STATUS";
+    expects[Opcode::RESERVED3_CODE] = "RESERVED3";
+    expects[Opcode::NOTIFY_CODE] = "NOTIFY";
+    expects[Opcode::UPDATE_CODE] = "UPDATE";
+    expects[Opcode::RESERVED6_CODE] = "RESERVED6";
+    expects[Opcode::RESERVED7_CODE] = "RESERVED7";
+    expects[Opcode::RESERVED8_CODE] = "RESERVED8";
+    expects[Opcode::RESERVED9_CODE] = "RESERVED9";
+    expects[Opcode::RESERVED10_CODE] = "RESERVED10";
+    expects[Opcode::RESERVED11_CODE] = "RESERVED11";
+    expects[Opcode::RESERVED12_CODE] = "RESERVED12";
+    expects[Opcode::RESERVED13_CODE] = "RESERVED13";
+    expects[Opcode::RESERVED14_CODE] = "RESERVED14";
+    expects[Opcode::RESERVED15_CODE] = "RESERVED15";
+
+    for (unsigned int i = 0; i <= Opcode::RESERVED15_CODE; ++i) {
+        EXPECT_EQ(expects.at(i), Opcode(i).toText());
+    }
+}
+
+// test operator<<.  We simply confirm it appends the result of toText().
+TEST(OpcodeTest, LeftShiftOperator) {
+    ostringstream oss;
+    oss << Opcode::NOTIFY();
+    EXPECT_EQ(Opcode::NOTIFY().toText(), oss.str());
+}
+}

+ 134 - 0
src/lib/dns/tests/rcode_unittest.cc

@@ -0,0 +1,134 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <vector>
+#include <sstream>
+
+#include <exceptions/exceptions.h>
+
+#include <dns/rcode.h>
+
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc::dns;
+
+namespace {
+TEST(RcodeTest, constructFromCode) {
+    // Normal cases.  This test also tests getCode()
+    EXPECT_EQ(0, Rcode(0).getCode());
+    EXPECT_EQ(0xfff, Rcode(0xfff).getCode()); // possible max code
+
+    // should fail on attempt of construction with an out of range code
+    EXPECT_THROW(Rcode(0x1000), isc::OutOfRange);
+    EXPECT_THROW(Rcode(0xffff), isc::OutOfRange);
+}
+
+TEST(RcodeTest, constructFromCodePair) {
+    EXPECT_EQ(3, Rcode(Rcode::NXDOMAIN_CODE, 0).getCode());
+    EXPECT_EQ(Rcode::BADVERS_CODE, Rcode(0, 1).getCode());
+    EXPECT_EQ(0xfff, Rcode(0xf, 0xff).getCode());
+    EXPECT_THROW(Rcode(0x10, 0xff), isc::OutOfRange);
+}
+
+TEST(RcodeTest, getExtendedCode) {
+    EXPECT_EQ(0, Rcode::NOERROR().getExtendedCode());
+    EXPECT_EQ(0, Rcode::YXRRSET().getExtendedCode());
+    EXPECT_EQ(1, Rcode::BADVERS().getExtendedCode());
+    EXPECT_EQ(0xab, Rcode(0xabf).getExtendedCode());
+    EXPECT_EQ(0xff, Rcode(0xfff).getExtendedCode());
+}
+
+TEST(RcodeTest, constants) {
+    // We'll only test arbitrarily chosen subsets of the codes.
+    // This class is quite simple, so it should be suffice.
+
+    EXPECT_EQ(Rcode::NOERROR_CODE, Rcode(0).getCode());
+    EXPECT_EQ(Rcode::FORMERR_CODE, Rcode(1).getCode());
+    EXPECT_EQ(Rcode::NOTIMP_CODE, Rcode(4).getCode());
+    EXPECT_EQ(Rcode::REFUSED_CODE, Rcode(5).getCode());
+    EXPECT_EQ(Rcode::RESERVED15_CODE, Rcode(15).getCode());
+    EXPECT_EQ(Rcode::BADVERS_CODE, Rcode(16).getCode());
+
+    EXPECT_EQ(Rcode::NOERROR_CODE, Rcode::NOERROR().getCode());
+    EXPECT_EQ(Rcode::FORMERR_CODE, Rcode::FORMERR().getCode());
+    EXPECT_EQ(Rcode::NOTIMP_CODE, Rcode::NOTIMP().getCode());
+    EXPECT_EQ(Rcode::REFUSED_CODE, Rcode::REFUSED().getCode());
+    EXPECT_EQ(Rcode::RESERVED15_CODE, Rcode::RESERVED15().getCode());
+    EXPECT_EQ(Rcode::BADVERS_CODE, Rcode::BADVERS().getCode());
+}
+
+TEST(RcodeTest, equal) {
+    EXPECT_TRUE(Rcode::NOERROR() == Rcode(Rcode::NOERROR_CODE));
+    EXPECT_TRUE(Rcode::NOERROR().equals(Rcode(Rcode::NOERROR_CODE)));
+    EXPECT_TRUE(Rcode::FORMERR() == Rcode(Rcode::FORMERR_CODE));
+    EXPECT_TRUE(Rcode::FORMERR().equals(Rcode(Rcode::FORMERR_CODE)));
+    EXPECT_TRUE(Rcode::NOTIMP() == Rcode(Rcode::NOTIMP_CODE));
+    EXPECT_TRUE(Rcode::NOTIMP().equals(Rcode(Rcode::NOTIMP_CODE)));
+    EXPECT_TRUE(Rcode::REFUSED() == Rcode(Rcode::REFUSED_CODE));
+    EXPECT_TRUE(Rcode::REFUSED().equals(Rcode(Rcode::REFUSED_CODE)));
+    EXPECT_TRUE(Rcode::RESERVED15() == Rcode(Rcode::RESERVED15()));
+    EXPECT_TRUE(Rcode::RESERVED15().equals(Rcode(Rcode::RESERVED15())));
+    EXPECT_TRUE(Rcode::BADVERS() == Rcode(Rcode::BADVERS_CODE));
+    EXPECT_TRUE(Rcode::BADVERS().equals(Rcode(Rcode::BADVERS_CODE)));
+}
+
+TEST(RcodeTest, nequal) {
+    EXPECT_TRUE(Rcode::NOERROR() != Rcode::FORMERR());
+    EXPECT_TRUE(Rcode::NOERROR().nequals(Rcode::FORMERR()));
+    EXPECT_TRUE(Rcode::NOTIMP() != Rcode(1));
+    EXPECT_TRUE(Rcode::NOTIMP().nequals(Rcode(1)));
+    EXPECT_TRUE(Rcode(10) != Rcode(11));
+    EXPECT_TRUE(Rcode(10).nequals(Rcode(11)));
+}
+
+TEST(RcodeTest, toText) {
+    vector<const char*> expects;
+    expects.resize(Rcode::BADVERS_CODE + 1);
+    expects[Rcode::NOERROR_CODE] = "NOERROR";
+    expects[Rcode::FORMERR_CODE] = "FORMERR";
+    expects[Rcode::SERVFAIL_CODE] = "SERVFAIL";
+    expects[Rcode::NXDOMAIN_CODE] = "NXDOMAIN";
+    expects[Rcode::NOTIMP_CODE] = "NOTIMP";
+    expects[Rcode::REFUSED_CODE] = "REFUSED";
+    expects[Rcode::YXDOMAIN_CODE] = "YXDOMAIN";
+    expects[Rcode::YXRRSET_CODE] = "YXRRSET";
+    expects[Rcode::NXRRSET_CODE] = "NXRRSET";
+    expects[Rcode::NOTAUTH_CODE] = "NOTAUTH";
+    expects[Rcode::NOTZONE_CODE] = "NOTZONE";
+    expects[Rcode::RESERVED11_CODE] = "RESERVED11";
+    expects[Rcode::RESERVED12_CODE] = "RESERVED12";
+    expects[Rcode::RESERVED13_CODE] = "RESERVED13";
+    expects[Rcode::RESERVED14_CODE] = "RESERVED14";
+    expects[Rcode::RESERVED15_CODE] = "RESERVED15";
+    expects[Rcode::BADVERS_CODE] = "BADVERS";
+
+    for (unsigned int i = 0; i <= Rcode::BADVERS_CODE; ++i) {
+        EXPECT_EQ(expects.at(i), Rcode(i).toText());
+    }
+
+    // Non well-known Rcodes
+    EXPECT_EQ("17", Rcode(Rcode::BADVERS().getCode() + 1).toText());
+    EXPECT_EQ("4095", Rcode(Rcode(0xfff)).toText());
+}
+
+// test operator<<.  We simply confirm it appends the result of toText().
+TEST(RcodeTest, LeftShiftOperator) {
+    ostringstream oss;
+    oss << Rcode::SERVFAIL();
+    EXPECT_EQ(Rcode::SERVFAIL().toText(), oss.str());
+}
+}

+ 7 - 7
src/lib/dns/tests/rdata_nsec_unittest.cc

@@ -70,39 +70,39 @@ TEST_F(Rdata_NSEC_Test, createFromWire_NSEC) {
 
     // A malformed NSEC bitmap length field that could cause overflow.
     EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC(), RRClass::IN(),
-                                      "rdata_nsec_fromWire4"),
+                                      "rdata_nsec_fromWire4.wire"),
                  DNSMessageFORMERR);
 
     // The bitmap field is incomplete (only the first byte is included)
     EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC(), RRClass::IN(),
-                                      "rdata_nsec_fromWire5"),
+                                      "rdata_nsec_fromWire5.wire"),
                  DNSMessageFORMERR);
 
     // Bitmap length is 0, which is invalid.
     EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC(), RRClass::IN(),
-                                      "rdata_nsec_fromWire6"),
+                                      "rdata_nsec_fromWire6.wire"),
                  DNSMessageFORMERR);
 
     // A boundary case: longest possible bitmaps (32 maps).  This should be
     // accepted.
     EXPECT_NO_THROW(rdataFactoryFromFile(RRType::NSEC(), RRClass::IN(),
-                                         "rdata_nsec_fromWire7"));
+                                         "rdata_nsec_fromWire7.wire"));
 
     // Another boundary condition: 33 bitmaps, which should be rejected.
     EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC(), RRClass::IN(),
-                                      "rdata_nsec_fromWire8"),
+                                      "rdata_nsec_fromWire8.wire"),
                  DNSMessageFORMERR);
 
     // Disordered bitmap window blocks.
     EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC(), RRClass::IN(),
-                                      "rdata_nsec_fromWire9"),
+                                      "rdata_nsec_fromWire9.wire"),
                  DNSMessageFORMERR);
 
     // Bitmap ending with all-zero bytes.  Not necessarily harmful except
     // the additional overhead of parsing, but invalid according to the
     // spec anyway.
     EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC(), RRClass::IN(),
-                                      "rdata_nsec_fromWire10"),
+                                      "rdata_nsec_fromWire10.wire"),
                  DNSMessageFORMERR);
 }
 

+ 1 - 1
src/lib/dns/tests/rdata_rrsig_unittest.cc

@@ -131,7 +131,7 @@ TEST_F(Rdata_RRSIG_Test, createFromWire) {
 
     // RDLEN is too short
     EXPECT_THROW(rdataFactoryFromFile(RRType::RRSIG(), RRClass::IN(),
-                                      "rdata_rrsig_fromWire2"),
+                                      "rdata_rrsig_fromWire2.wire"),
                  InvalidRdataLength);
 }
 }

+ 1 - 1
src/lib/dns/tests/rdata_soa_unittest.cc

@@ -65,7 +65,7 @@ TEST_F(Rdata_SOA_Test, toWireBuffer) {
     obuffer.skip(2);
     rdata_soa.toWire(obuffer);
     vector<unsigned char> data;
-    UnitTestUtil::readWireData("rdata_soa_toWireUncompressed", data);
+    UnitTestUtil::readWireData("rdata_soa_toWireUncompressed.wire", data);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
                         static_cast<const uint8_t *>(obuffer.getData()) + 2,
                         obuffer.getLength() - 2, &data[2], data.size() - 2);

+ 4 - 4
src/lib/dns/tests/rdata_txt_unittest.cc

@@ -109,12 +109,12 @@ TEST_F(Rdata_TXT_Test, createFromWire) {
     // Empty character string
     EXPECT_EQ(0, rdata_txt_empty.compare(
                   *rdataFactoryFromFile(RRType("TXT"), RRClass("IN"),
-                                        "rdata_txt_fromWire2")));
+                                        "rdata_txt_fromWire2.wire")));
 
     // Multiple character strings
     obuffer.clear();
     rdataFactoryFromFile(RRType("TXT"), RRClass("IN"),
-                         "rdata_txt_fromWire3")->toWire(obuffer);
+                         "rdata_txt_fromWire3.wire")->toWire(obuffer);
     // the result should be 'wiredata_txt' repeated twice
     vector<uint8_t> expected_data(wiredata_txt, wiredata_txt +
                                   sizeof(wiredata_txt));
@@ -145,12 +145,12 @@ TEST_F(Rdata_TXT_Test, createFromWire) {
 
     // RDATA is empty, which is invalid for TXT.
     EXPECT_THROW(rdataFactoryFromFile(RRType("TXT"), RRClass("IN"),
-                                      "rdata_txt_fromWire4"),
+                                      "rdata_txt_fromWire4.wire"),
                  DNSMessageFORMERR);
 
     // character-string length is too large, which could cause overrun.
     EXPECT_THROW(rdataFactoryFromFile(RRType("TXT"), RRClass("IN"),
-                                      "rdata_txt_fromWire5"),
+                                      "rdata_txt_fromWire5.wire"),
                  DNSMessageFORMERR);
 }
 

+ 2 - 1
src/lib/dns/tests/run_unittests.cc

@@ -21,7 +21,8 @@
 int
 main(int argc, char* argv[]) {
     ::testing::InitGoogleTest(&argc, argv);
-    isc::UnitTestUtil::addDataPath(TEST_DATA_DIR);
+    isc::UnitTestUtil::addDataPath(TEST_DATA_SRCDIR);
+    isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR);
 
     return (RUN_ALL_TESTS());
 }

+ 56 - 0
src/lib/dns/tests/testdata/Makefile.am

@@ -0,0 +1,56 @@
+CLEANFILES = *.wire
+
+BUILT_SOURCES = edns_toWire1.wire edns_toWire2.wire edns_toWire3.wire
+BUILT_SOURCES += edns_toWire4.wire
+BUILT_SOURCES += message_fromWire10.wire message_fromWire11.wire
+BUILT_SOURCES += name_toWire5.wire name_toWire6.wire
+BUILT_SOURCES += rdata_nsec_fromWire4.wire rdata_nsec_fromWire5.wire
+BUILT_SOURCES += rdata_nsec_fromWire6.wire rdata_nsec_fromWire7.wire
+BUILT_SOURCES += rdata_nsec_fromWire8.wire rdata_nsec_fromWire9.wire
+BUILT_SOURCES += rdata_nsec_fromWire10.wire
+BUILT_SOURCES += rdata_rrsig_fromWire2.wire
+BUILT_SOURCES += rdata_soa_toWireUncompressed.wire
+BUILT_SOURCES +=  rdata_txt_fromWire2.wire rdata_txt_fromWire3.wire
+BUILT_SOURCES += rdata_txt_fromWire4.wire rdata_txt_fromWire5.wire
+
+# NOTE: keep this in sync with real file listing
+# so is included in tarball
+EXTRA_DIST = gen-wiredata.py.in
+EXTRA_DIST += edns_toWire1.spec edns_toWire2.spec
+EXTRA_DIST += edns_toWire3.spec edns_toWire4.spec
+EXTRA_DIST += message_fromWire1 message_fromWire2
+EXTRA_DIST += message_fromWire3 message_fromWire4
+EXTRA_DIST += message_fromWire5 message_fromWire6
+EXTRA_DIST += message_fromWire7 message_fromWire8
+EXTRA_DIST += message_fromWire9 message_fromWire10.spec
+EXTRA_DIST += message_fromWire11.spec
+EXTRA_DIST += message_toWire1
+EXTRA_DIST += name_fromWire1 name_fromWire2 name_fromWire3_1 name_fromWire3_2
+EXTRA_DIST += name_fromWire4 name_fromWire6 name_fromWire7 name_fromWire8
+EXTRA_DIST += name_fromWire9 name_fromWire10 name_fromWire11 name_fromWire12
+EXTRA_DIST += name_fromWire13 name_fromWire14
+EXTRA_DIST += name_toWire1 name_toWire2 name_toWire3 name_toWire4
+EXTRA_DIST += name_toWire5.spec name_toWire6.spec
+EXTRA_DIST += question_fromWire question_toWire1 question_toWire2
+EXTRA_DIST += rdata_cname_fromWire rdata_dname_fromWire rdata_dnskey_fromWire
+EXTRA_DIST += rdata_ds_fromWire rdata_in_a_fromWire rdata_in_aaaa_fromWire
+EXTRA_DIST += rdata_mx_fromWire rdata_mx_toWire1 rdata_ns_fromWire
+EXTRA_DIST += rdata_nsec3_fromWire1 rdata_nsec3_fromWire2 rdata_nsec3_fromWire3
+EXTRA_DIST += rdata_nsec3param_fromWire1 rdata_nsec_fromWire1
+EXTRA_DIST += rdata_nsec_fromWire2 rdata_nsec_fromWire3
+EXTRA_DIST += rdata_nsec_fromWire4.spec rdata_nsec_fromWire5.spec
+EXTRA_DIST += rdata_nsec_fromWire6.spec rdata_nsec_fromWire7.spec
+EXTRA_DIST += rdata_nsec_fromWire8.spec rdata_nsec_fromWire9.spec
+EXTRA_DIST += rdata_nsec_fromWire10.spec
+EXTRA_DIST += rdata_opt_fromWire rdata_rrsig_fromWire1
+EXTRA_DIST += rdata_rrsig_fromWire2.spec
+EXTRA_DIST += rdata_soa_fromWire rdata_soa_toWireUncompressed.spec
+EXTRA_DIST += rdata_txt_fromWire1 rdata_txt_fromWire2.spec
+EXTRA_DIST += rdata_txt_fromWire3.spec rdata_txt_fromWire4.spec
+EXTRA_DIST += rdata_txt_fromWire5.spec rdata_unknown_fromWire
+EXTRA_DIST += rrcode16_fromWire1 rrcode16_fromWire2
+EXTRA_DIST += rrcode32_fromWire1 rrcode32_fromWire2
+EXTRA_DIST += rrset_toWire1 rrset_toWire2
+
+.spec.wire:
+	./gen-wiredata.py -o $@ $<

+ 5 - 0
src/lib/dns/tests/testdata/edns_toWire1.spec

@@ -0,0 +1,5 @@
+#
+# A simplest form of EDNS: all default parameters
+#
+[edns]
+

+ 5 - 0
src/lib/dns/tests/testdata/edns_toWire2.spec

@@ -0,0 +1,5 @@
+#
+# Same as edns_toWire1 but setting the DO bit
+#
+[edns]
+do: 1

+ 0 - 0
src/lib/dns/tests/testdata/edns_toWire3.spec


Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff