Browse Source

sync with trunk

git-svn-id: svn://bind10.isc.org/svn/bind10/branches/trac354@3093 e5f2f494-b856-4b98-b285-d166d9295462
JINMEI Tatuya 14 years ago
parent
commit
71a686f2d3
56 changed files with 2628 additions and 1245 deletions
  1. 10 0
      ChangeLog
  2. 22 0
      configure.ac
  3. 4 1
      src/bin/Makefile.am
  4. 2 0
      src/bin/auth/auth_srv.cc
  5. 5 1
      src/bin/auth/tests/auth_srv_unittest.cc
  6. 20 2
      src/bin/bind10/bind10.8
  7. 12 1
      src/bin/bind10/bind10.py.in
  8. 12 0
      src/bin/bind10/bind10.xml
  9. 23 0
      src/bin/bind10/tests/args_test.py
  10. 3 0
      src/bin/bindctl/bindctl-source.py.in
  11. 3 0
      src/bin/cfgmgr/b10-cfgmgr.py.in
  12. 3 0
      src/bin/cmdctl/cmdctl.py.in
  13. 2 0
      src/bin/host/host.cc
  14. 4 0
      src/bin/loadzone/b10-loadzone.py.in
  15. 3 0
      src/bin/msgq/msgq.py.in
  16. 12 0
      src/bin/tests/Makefile.am
  17. 3 0
      src/bin/tests/README
  18. 61 0
      src/bin/tests/process_rename_test.py.in
  19. 3 0
      src/bin/usermgr/b10-cmdctl-usermgr.py.in
  20. 3 0
      src/bin/xfrin/xfrin.py.in
  21. 3 0
      src/bin/xfrout/xfrout.py.in
  22. 3 0
      src/bin/zonemgr/zonemgr.py.in
  23. 2 0
      src/lib/bench/benchmark_util.cc
  24. 2 1
      src/lib/bench/tests/loadquery_unittest.cc
  25. 1 0
      src/lib/datasrc/data_source.cc
  26. 2 0
      src/lib/datasrc/tests/datasrc_unittest.cc
  27. 1 0
      src/lib/datasrc/tests/query_unittest.cc
  28. 2 0
      src/lib/dns/Makefile.am
  29. 1 1
      src/lib/dns/exceptions.cc
  30. 62 155
      src/lib/dns/message.cc
  31. 21 308
      src/lib/dns/message.h
  32. 69 0
      src/lib/dns/opcode.cc
  33. 292 0
      src/lib/dns/opcode.h
  34. 2 0
      src/lib/dns/python/Makefile.am
  35. 30 600
      src/lib/dns/python/message_python.cc
  36. 392 0
      src/lib/dns/python/opcode_python.cc
  37. 10 0
      src/lib/dns/python/pydnspp.cc
  38. 433 0
      src/lib/dns/python/rcode_python.cc
  39. 2 0
      src/lib/dns/python/tests/Makefile.am
  40. 21 157
      src/lib/dns/python/tests/message_python_test.py
  41. 1 0
      src/lib/dns/python/tests/messagerenderer_python_test.py
  42. 112 0
      src/lib/dns/python/tests/opcode_python_test.py
  43. 137 0
      src/lib/dns/python/tests/rcode_python_test.py
  44. 103 0
      src/lib/dns/rcode.cc
  45. 348 0
      src/lib/dns/rcode.h
  46. 2 0
      src/lib/dns/tests/Makefile.am
  47. 1 0
      src/lib/dns/tests/edns_unittest.cc
  48. 27 17
      src/lib/dns/tests/message_unittest.cc
  49. 108 0
      src/lib/dns/tests/opcode_unittest.cc
  50. 134 0
      src/lib/dns/tests/rcode_unittest.cc
  51. 1 1
      src/lib/python/isc/Makefile.am
  52. 5 0
      src/lib/python/isc/utils/Makefile.am
  53. 0 0
      src/lib/python/isc/utils/__init__.py
  54. 37 0
      src/lib/python/isc/utils/process.py
  55. 12 0
      src/lib/python/isc/utils/tests/Makefile.am
  56. 39 0
      src/lib/python/isc/utils/tests/process_test.py

+ 10 - 0
ChangeLog

@@ -1,3 +1,13 @@
+  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,

+ 22 - 0
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.
@@ -452,6 +470,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
@@ -460,6 +479,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
@@ -518,6 +539,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

+ 2 - 0
src/bin/auth/auth_srv.cc

@@ -29,6 +29,8 @@
 #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>

+ 5 - 1
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));
 }
@@ -584,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);

+ 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()

+ 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'
 

+ 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

+ 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>

+ 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
 #########################################################################

+ 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)"
 

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

@@ -0,0 +1,12 @@
+PYTESTS = process_rename_test.py
+EXTRA_DIST = $(PYTESTS)
+
+# 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_srcdir)/$$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.in 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.in and extract names of scripts
+        for (d, _, fs) in os.walk('@top_srcdir@'):
+            if 'Makefile.in' in fs:
+                makefile = ''.join(open(os.path.join(d,
+                    "Makefile.in")).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'

+ 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

+ 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"

+ 3 - 0
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

+ 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>
 

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

@@ -69,6 +69,8 @@ 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

+ 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 {

+ 62 - 155
src/lib/dns/message.cc

@@ -33,6 +33,8 @@
 #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>
@@ -62,11 +64,6 @@ const flags_t FLAG_RA = 0x0080;
 const flags_t FLAG_AD = 0x0020;
 const flags_t FLAG_CD = 0x0010;
 
-//
-// EDNS related constants
-//
-const uint32_t EXTRCODE_MASK = 0xff000000; 
-
 const unsigned int OPCODE_MASK = 0x7800;
 const unsigned int OPCODE_SHIFT = 11;
 const unsigned int RCODE_MASK = 0x000f;
@@ -74,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",
@@ -160,55 +79,6 @@ const char *sectiontext[] = {
 };
 }
 
-string
-Opcode::toText() const {
-    return (opcodetext[code_]);
-}
-
-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;
-}
-
-Rcode::Rcode(const uint16_t code) : code_(code) {
-    if (code_ > MAX_RCODE) {
-        isc_throw(OutOfRange, "Rcode is too large to construct: " << code_);
-    }
-}
-
-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());
-}
-
 namespace {
 inline unsigned int
 sectionCodeToId(const Section& section) {
@@ -225,8 +95,16 @@ 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 header_parsed_;
@@ -242,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();
 }
@@ -256,7 +138,7 @@ void
 MessageImpl::init() {
     flags_ = 0;
     qid_ = 0;
-    rcode_ = Rcode::NOERROR();  // XXX
+    rcode_ = NULL;
     opcode_ = NULL;
     edns_ = EDNSPtr();
 
@@ -271,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))
 {}
@@ -318,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
@@ -327,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_);
 }
 
@@ -341,7 +241,7 @@ Message::setOpcode(const Opcode& opcode) {
         isc_throw(InvalidMessageOperation,
                   "setOpcode performed in non-render mode");
     }
-    impl_->opcode_ = &opcode;
+    impl_->setOpcode(opcode);
 }
 
 ConstEDNSPtr
@@ -446,6 +346,14 @@ Message::toWire(MessageRenderer& renderer) {
         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);
@@ -484,12 +392,12 @@ Message::toWire(MessageRenderer& renderer) {
     // 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) {
+        if (!local_edns && impl_->rcode_->getExtendedCode() != 0) {
             local_edns = ConstEDNSPtr(new EDNS());
         }
         if (local_edns) {
             arcount += local_edns->toWire(renderer,
-                                          impl_->rcode_.getExtendedCode());
+                                          impl_->rcode_->getExtendedCode());
         }
     }
  
@@ -512,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);
@@ -541,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();
@@ -677,7 +585,7 @@ MessageImpl::parseSection(const Section& section, InputBuffer& buffer) {
             uint8_t extended_rcode;
             edns_ = ConstEDNSPtr(createEDNSFromRR(name, rrclass, rrtype, ttl,
                                                   *rdata, extended_rcode));
-            rcode_ = Rcode(rcode_.getCode(), extended_rcode);
+            setRcode(Rcode(rcode_->getCode(), extended_rcode));
             continue;
         } else {
             vector<RRsetPtr>::iterator it =
@@ -718,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()))
@@ -948,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());
 }

+ 21 - 308
src/lib/dns/message.h

@@ -82,6 +82,8 @@ class InputBuffer;
 class MessageRenderer;
 class Message;
 class MessageImpl;
+class Opcode;
+class Rcode;
 
 template <typename T>
 struct SectionIteratorImpl;
@@ -160,312 +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(const uint16_t code);
-    Rcode(const uint8_t code, const uint8_t extended_code);
-    uint16_t getCode() const { return (code_); }
-    uint8_t getExtendedCode() const;
-    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.
 ///
@@ -644,6 +340,11 @@ 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 Set the Response Code of the message.
@@ -655,6 +356,11 @@ public:
     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.
@@ -750,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.
@@ -780,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: 

+ 2 - 0
src/lib/dns/python/Makefile.am

@@ -16,6 +16,8 @@ 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

+ 30 - 600
src/lib/dns/python/message_python.cc

@@ -189,594 +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_getExtendedCode(const 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" },
-    { "get_extended_code",
-      reinterpret_cast<PyCFunction>(Rcode_getExtendedCode),
-      METH_NOARGS, "Returns 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 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_getExtendedCode(const s_Rcode* self) {
-    return (Py_BuildValue("B", self->rcode->getExtendedCode()));
-}
-
-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
 //
@@ -1255,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);
@@ -1287,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);
@@ -1486,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*

+ 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

+ 10 - 0
src/lib/dns/python/pydnspp.cc

@@ -57,6 +57,8 @@ static PyObject* po_DNSMessageBADVERS;
 #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
 
@@ -128,6 +130,14 @@ PyInit_pydnspp(void) {
         return (NULL);
     }
 
+    if (!initModulePart_Opcode(mod)) {
+        return (NULL);
+    }
+
+    if (!initModulePart_Rcode(mod)) {
+        return (NULL);
+    }
+
     if (!initModulePart_Message(mod)) {
         return (NULL);
     }

+ 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

+ 2 - 0
src/lib/dns/python/tests/Makefile.am

@@ -3,6 +3,8 @@ 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

+ 21 - 157
src/lib/dns/python/tests/message_python_test.py

@@ -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):
@@ -320,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")
@@ -331,6 +175,8 @@ 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())
 
@@ -413,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()
         
@@ -430,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,

+ 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()

+ 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()

+ 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: 

+ 2 - 0
src/lib/dns/tests/Makefile.am

@@ -22,6 +22,8 @@ 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

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

@@ -24,6 +24,7 @@
 #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>

+ 27 - 17
src/lib/dns/tests/message_unittest.cc

@@ -22,6 +22,8 @@
 #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>
@@ -77,23 +79,6 @@ 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
-
-    // 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_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, getEDNS) {
     EXPECT_FALSE(message_parse.getEDNS()); // by default EDNS isn't set
 
@@ -194,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);
+}
 }

+ 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());
+}
+}

+ 1 - 1
src/lib/python/isc/Makefile.am

@@ -1,4 +1,4 @@
-SUBDIRS = datasrc cc config log notify # Util
+SUBDIRS = datasrc cc config log notify utils # Util
 
 python_PYTHON = __init__.py
 

+ 5 - 0
src/lib/python/isc/utils/Makefile.am

@@ -0,0 +1,5 @@
+SUBDIRS = tests
+
+python_PYTHON = __init__.py process.py
+
+pythondir = $(pyexecdir)/isc/utils

+ 0 - 0
src/lib/python/isc/utils/__init__.py


+ 37 - 0
src/lib/python/isc/utils/process.py

@@ -0,0 +1,37 @@
+# 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.
+
+"""
+Module to manipulate the python processes.
+
+It contains only function to rename the process, which is currently
+wrapper around setproctitle library. Does not fail if the setproctitle
+module is missing, but does nothing in that case.
+"""
+try:
+    from setproctitle import setproctitle
+except ImportError:
+    def setproctitle(_): pass
+import sys
+import os.path
+
+"""
+Rename the current process to given name (so it can be found in ps).
+If name is None, use zero'th command line argument.
+"""
+def rename(name=None):
+    if name is None:
+        name = os.path.basename(sys.argv[0])
+    setproctitle(name)

+ 12 - 0
src/lib/python/isc/utils/tests/Makefile.am

@@ -0,0 +1,12 @@
+PYTESTS = process_test.py
+EXTRA_DIST = $(PYTESTS)
+
+# 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_srcdir)/$$pytest || exit ; \
+	done

+ 39 - 0
src/lib/python/isc/utils/tests/process_test.py

@@ -0,0 +1,39 @@
+# 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 for isc.utils.process."""
+import unittest
+import isc.utils.process
+run_tests = True
+try:
+    import setproctitle
+except ImportError:
+    run_tests = False
+
+class TestRename(unittest.TestCase):
+    """Testcase for isc.process.rename."""
+    def __get_self_name(self):
+        return setproctitle.getproctitle()
+
+    @unittest.skipIf(not run_tests, "Setproctitle not installed, not testing")
+    def test_rename(self):
+        """Test if the renaming function works."""
+        isc.utils.process.rename("rename-test")
+        self.assertEqual("rename-test", self.__get_self_name())
+        isc.utils.process.rename()
+        self.assertEqual("process_test.py", self.__get_self_name())
+
+if __name__ == "__main__":
+    unittest.main()