Browse Source

Merge branch 'master' into trac1144

Dima Volodin 13 years ago
parent
commit
4102716ab6
52 changed files with 1205 additions and 217 deletions
  1. 10 0
      ChangeLog
  2. 2 1
      configure.ac
  3. 1 0
      src/bin/auth/Makefile.am
  4. 5 5
      src/bin/auth/auth_messages.mes
  5. 1 0
      src/bin/auth/benchmarks/Makefile.am
  6. 1 0
      src/bin/auth/tests/Makefile.am
  7. 1 5
      src/bin/bind10/Makefile.am
  8. 1 1
      src/bin/bind10/bind10_messages.mes
  9. 3 3
      src/bin/bind10/bind10_src.py.in
  10. 1 1
      src/bin/bind10/run_bind10.sh.in
  11. 1 1
      src/bin/bind10/tests/Makefile.am
  12. 1 1
      src/bin/cmdctl/cmdctl_messages.mes
  13. 1 0
      src/bin/dhcp6/Makefile.am
  14. 1 0
      src/bin/host/Makefile.am
  15. 2 2
      src/bin/resolver/resolver_messages.mes
  16. 6 6
      src/bin/stats/stats_httpd_messages.mes
  17. 1 1
      src/bin/xfrout/xfrout_messages.mes
  18. 8 2
      src/bin/zonemgr/Makefile.am
  19. 51 30
      src/bin/zonemgr/zonemgr.py.in
  20. 145 0
      src/bin/zonemgr/zonemgr_messages.mes
  21. 5 5
      src/lib/asiodns/asiodns_messages.mes
  22. 1 0
      src/lib/bench/tests/Makefile.am
  23. 1 0
      src/lib/cache/tests/Makefile.am
  24. 3 3
      src/lib/cc/cc_messages.mes
  25. 103 26
      src/lib/config/ccsession.cc
  26. 2 2
      src/lib/config/ccsession.h
  27. 8 0
      src/lib/config/config_log.h
  28. 25 0
      src/lib/config/config_messages.mes
  29. 33 25
      src/lib/config/tests/ccsession_unittests.cc
  30. 1 0
      src/lib/datasrc/tests/Makefile.am
  31. 2 0
      src/lib/dns/Makefile.am
  32. 1 0
      src/lib/dns/benchmarks/Makefile.am
  33. 47 79
      src/lib/dns/rdata/any_255/tsig_250.cc
  34. 245 0
      src/lib/dns/rdata/in_1/srv_33.cc
  35. 93 0
      src/lib/dns/rdata/in_1/srv_33.h
  36. 1 0
      src/lib/dns/tests/Makefile.am
  37. 173 0
      src/lib/dns/tests/rdata_srv_unittest.cc
  38. 1 0
      src/lib/dns/tests/testdata/Makefile.am
  39. 36 0
      src/lib/dns/tests/testdata/rdata_srv_fromWire
  40. 1 1
      src/lib/python/isc/Makefile.am
  41. 4 0
      src/lib/python/isc/bind10/Makefile.am
  42. 0 0
      src/lib/python/isc/bind10/__init__.py
  43. 0 0
      src/lib/python/isc/bind10/sockcreator.py
  44. 29 0
      src/lib/python/isc/bind10/tests/Makefile.am
  45. 1 1
      src/bin/bind10/tests/sockcreator_test.py.in
  46. 2 2
      src/lib/python/isc/config/ccsession.py
  47. 2 0
      src/lib/python/isc/config/tests/ccsession_test.py
  48. 1 1
      src/lib/python/isc/notify/notify_out_messages.mes
  49. 1 0
      src/lib/resolve/tests/Makefile.am
  50. 11 0
      src/lib/util/strutil.cc
  51. 62 0
      src/lib/util/strutil.h
  52. 67 13
      src/lib/util/tests/strutil_unittest.cc

+ 10 - 0
ChangeLog

@@ -1,3 +1,13 @@
+277.	[func]		jerry
+	Implement the SRV rrtype according to RFC2782.
+	(Trac #1128, git 5fd94aa027828c50e63ae1073d9d6708e0a9c223)
+
+276.	[func]		stephen
+	Although the top-level loggers are named after the program (e.g.
+	b10-auth, b10-resolver), allow the logger configuration to omit the
+	"b10-" prefix and use just the module name.
+	(Trac #1003, git a01cd4ac5a68a1749593600c0f338620511cae2d)
+
 275.	[func]		jinmei
 	Added support for TSIG key matching in ACLs.  The xfrout ACL can
 	now refer to TSIG key names using the "key" attribute.  For

+ 2 - 1
configure.ac

@@ -839,6 +839,8 @@ AC_CONFIG_FILES([Makefile
                  src/lib/python/isc/notify/Makefile
                  src/lib/python/isc/notify/tests/Makefile
                  src/lib/python/isc/testutils/Makefile
+                 src/lib/python/isc/bind10/Makefile
+                 src/lib/python/isc/bind10/tests/Makefile
                  src/lib/config/Makefile
                  src/lib/config/tests/Makefile
                  src/lib/config/tests/testdata/Makefile
@@ -907,7 +909,6 @@ AC_OUTPUT([doc/version.ent
            src/bin/bind10/bind10_src.py
            src/bin/bind10/run_bind10.sh
            src/bin/bind10/tests/bind10_test.py
-           src/bin/bind10/tests/sockcreator_test.py
            src/bin/bindctl/run_bindctl.sh
            src/bin/bindctl/bindctl_main.py
            src/bin/bindctl/tests/bindctl_test

+ 1 - 0
src/bin/auth/Makefile.am

@@ -56,6 +56,7 @@ EXTRA_DIST += auth_messages.mes
 
 b10_auth_LDADD =  $(top_builddir)/src/lib/datasrc/libdatasrc.la
 b10_auth_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
+b10_auth_LDADD += $(top_builddir)/src/lib/util/libutil.la
 b10_auth_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
 b10_auth_LDADD += $(top_builddir)/src/lib/cc/libcc.la
 b10_auth_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la

+ 5 - 5
src/bin/auth/auth_messages.mes

@@ -63,7 +63,7 @@ datebase data source, listing the file that is being accessed.
 
 % AUTH_DNS_SERVICES_CREATED DNS services created
 This is a debug message indicating that the component that will handling
-incoming queries for the authoritiative server (DNSServices) has been
+incoming queries for the authoritative server (DNSServices) has been
 successfully created. It is issued during server startup is an indication
 that the initialization is proceeding normally.
 
@@ -74,7 +74,7 @@ reason for the failure is given in the message.) The server will drop the
 packet.
 
 % AUTH_LOAD_TSIG loading TSIG keys
-This is a debug message indicating that the authoritiative server
+This is a debug message indicating that the authoritative server
 has requested the keyring holding TSIG keys from the configuration
 database. It is issued during server startup is an indication that the
 initialization is proceeding normally.
@@ -141,8 +141,8 @@ encountered an internal error whilst processing a received packet:
 the cause of the error is included in the message.
 
 The server will return a SERVFAIL error code to the sender of the packet.
-However, this message indicates a potential error in the server.
-Please open a bug ticket for this issue.
+This message indicates a potential error in the server.  Please open a
+bug ticket for this issue.
 
 % AUTH_RECEIVED_COMMAND command '%1' received
 This is a debug message issued when the authoritative server has received
@@ -209,7 +209,7 @@ channel.  It is issued during server startup is an indication that the
 initialization is proceeding normally.
 
 % AUTH_STATS_COMMS communication error in sending statistics data: %1
-An error was encountered when the authoritiative server tried to send data
+An error was encountered when the authoritative server tried to send data
 to the statistics daemon. The message includes additional information
 describing the reason for the failure.
 

+ 1 - 0
src/bin/auth/benchmarks/Makefile.am

@@ -17,6 +17,7 @@ query_bench_SOURCES += ../auth_log.h ../auth_log.cc
 nodist_query_bench_SOURCES = ../auth_messages.h ../auth_messages.cc
 
 query_bench_LDADD = $(top_builddir)/src/lib/dns/libdns++.la
+query_bench_LDADD += $(top_builddir)/src/lib/util/libutil.la
 query_bench_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 query_bench_LDADD += $(top_builddir)/src/lib/bench/libbench.la
 query_bench_LDADD += $(top_builddir)/src/lib/datasrc/libdatasrc.la

+ 1 - 0
src/bin/auth/tests/Makefile.am

@@ -47,6 +47,7 @@ run_unittests_LDADD += $(SQLITE_LIBS)
 run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libtestutils.la
 run_unittests_LDADD +=  $(top_builddir)/src/lib/datasrc/libdatasrc.la
 run_unittests_LDADD +=  $(top_builddir)/src/lib/dns/libdns++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
 run_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
 run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
 run_unittests_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la

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

@@ -1,11 +1,7 @@
 SUBDIRS = . tests
 
 sbin_SCRIPTS = bind10
-CLEANFILES = bind10 bind10_src.pyc bind10_messages.py bind10_messages.pyc \
-	sockcreator.pyc
-
-python_PYTHON = __init__.py sockcreator.py
-pythondir = $(pyexecdir)/bind10
+CLEANFILES = bind10 bind10_src.pyc bind10_messages.py bind10_messages.pyc
 
 pkglibexecdir = $(libexecdir)/@PACKAGE@
 pyexec_DATA = bind10_messages.py

+ 1 - 1
src/bin/bind10/bind10_messages.mes

@@ -122,7 +122,7 @@ The boss requested a socket from the creator, but the answer is unknown. This
 looks like a programmer error.
 
 % BIND10_SOCKCREATOR_CRASHED the socket creator crashed
-The socket creator terminated unexpectadly. It is not possible to restart it
+The socket creator terminated unexpectedly. It is not possible to restart it
 (because the boss already gave up root privileges), so the system is going
 to terminate.
 

+ 3 - 3
src/bin/bind10/bind10_src.py.in

@@ -67,7 +67,7 @@ import isc.util.process
 import isc.net.parse
 import isc.log
 from bind10_messages import *
-import bind10.sockcreator
+import isc.bind10.sockcreator
 
 isc.log.init("b10-boss")
 logger = isc.log.Logger("boss")
@@ -337,8 +337,8 @@ class BoB:
 
     def start_creator(self):
         self.curproc = 'b10-sockcreator'
-        self.sockcreator = bind10.sockcreator.Creator("@@LIBEXECDIR@@:" +
-                                                      os.environ['PATH'])
+        self.sockcreator = isc.bind10.sockcreator.Creator("@@LIBEXECDIR@@:" +
+                                                          os.environ['PATH'])
 
     def stop_creator(self, kill=False):
         if self.sockcreator is None:

+ 1 - 1
src/bin/bind10/run_bind10.sh.in

@@ -20,7 +20,7 @@ export PYTHON_EXEC
 
 BIND10_PATH=@abs_top_builddir@/src/bin/bind10
 
-PATH=@abs_top_builddir@/src/bin/msgq:@abs_top_builddir@/src/bin/auth:@abs_top_builddir@/src/bin/resolver:@abs_top_builddir@/src/bin/cfgmgr:@abs_top_builddir@/src/bin/cmdctl:@abs_top_builddir@/src/bin/stats:@abs_top_builddir@/src/bin/xfrin:@abs_top_builddir@/src/bin/xfrout:@abs_top_builddir@/src/bin/zonemgr:@abs_top_builddir@/src/bin/dhcp6:$PATH
+PATH=@abs_top_builddir@/src/bin/msgq:@abs_top_builddir@/src/bin/auth:@abs_top_builddir@/src/bin/resolver:@abs_top_builddir@/src/bin/cfgmgr:@abs_top_builddir@/src/bin/cmdctl:@abs_top_builddir@/src/bin/stats:@abs_top_builddir@/src/bin/xfrin:@abs_top_builddir@/src/bin/xfrout:@abs_top_builddir@/src/bin/zonemgr:@abs_top_builddir@/src/bin/dhcp6:@abs_top_builddir@/src/bin/sockcreator:$PATH
 export PATH
 
 PYTHONPATH=@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/xfr/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/python/isc/config:@abs_top_builddir@/src/lib/python/isc/acl/.libs:

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

@@ -1,7 +1,7 @@
 PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
 #PYTESTS = args_test.py bind10_test.py
 # NOTE: this has a generated test found in the builddir
-PYTESTS = bind10_test.py sockcreator_test.py
+PYTESTS = bind10_test.py
 
 # If necessary (rare cases), explicitly specify paths to dynamic libraries
 # required by loadable python modules.

+ 1 - 1
src/bin/cmdctl/cmdctl_messages.mes

@@ -69,7 +69,7 @@ There was a keyboard interrupt signal to stop the cmdctl daemon. The
 daemon will now shut down.
 
 % CMDCTL_UNCAUGHT_EXCEPTION uncaught exception: %1
-The b10-cdmctl daemon encountered an uncaught exception and
+The b10-cmdctl daemon encountered an uncaught exception and
 will now shut down. This is indicative of a programming error and
 should not happen under normal circumstances. The exception message
 is printed.

+ 1 - 0
src/bin/dhcp6/Makefile.am

@@ -35,6 +35,7 @@ b10_dhcp6_SOURCES = main.cc
 b10_dhcp6_SOURCES += dhcp6.h
 b10_dhcp6_LDADD =  $(top_builddir)/src/lib/datasrc/libdatasrc.la
 b10_dhcp6_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
+b10_dhcp6_LDADD += $(top_builddir)/src/lib/util/libutil.la
 b10_dhcp6_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
 b10_dhcp6_LDADD += $(top_builddir)/src/lib/cc/libcc.la
 b10_dhcp6_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la

+ 1 - 0
src/bin/host/Makefile.am

@@ -13,6 +13,7 @@ CLEANFILES = *.gcno *.gcda
 bin_PROGRAMS = b10-host
 b10_host_SOURCES = host.cc
 b10_host_LDADD = $(top_builddir)/src/lib/dns/libdns++.la
+b10_host_LDADD += $(top_builddir)/src/lib/util/libutil.la
 b10_host_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 
 man_MANS = b10-host.1

+ 2 - 2
src/bin/resolver/resolver_messages.mes

@@ -78,7 +78,7 @@ specified, it will appear once for each address.
 % RESOLVER_FORWARD_QUERY processing forward query
 This is a debug message indicating that a query received by the resolver
 has passed a set of checks (message is well-formed, it is allowed by the
-ACL, it is a supported opcode etc.) and is being forwarded to upstream
+ACL, it is a supported opcode, etc.) and is being forwarded to upstream
 servers.
 
 % RESOLVER_HEADER_ERROR message received, exception when processing header: %1
@@ -116,7 +116,7 @@ so is returning a REFUSED response to the sender.
 % RESOLVER_NORMAL_QUERY processing normal query
 This is a debug message indicating that the query received by the resolver
 has passed a set of checks (message is well-formed, it is allowed by the
-ACL, it is a supported opcode etc.) and is being processed the resolver.
+ACL, it is a supported opcode, etc.) and is being processed by the resolver.
 
 % RESOLVER_NOTIFY_RECEIVED NOTIFY arrived but server is not authoritative
 The resolver has received a NOTIFY message.  As the server is not

+ 6 - 6
src/bin/stats/stats_httpd_messages.mes

@@ -49,14 +49,14 @@ An unknown command has been sent to the stats-httpd module. The
 stats-httpd module will respond with an error, and the command will
 be ignored.
 
-% STATHTTPD_SERVER_ERROR http server error: %1
-An internal error occurred while handling an http request. A HTTP 500
+% STATHTTPD_SERVER_ERROR HTTP server error: %1
+An internal error occurred while handling an HTTP request. An HTTP 500
 response will be sent back, and the specific error is printed. This
 is an error condition that likely points to a module that is not
 responding correctly to statistic requests.
 
-% STATHTTPD_SERVER_INIT_ERROR http server initialization error: %1
-There was a problem initializing the http server in the stats-httpd
+% STATHTTPD_SERVER_INIT_ERROR HTTP server initialization error: %1
+There was a problem initializing the HTTP server in the stats-httpd
 module upon receiving its configuration data. The most likely cause
 is a port binding problem or a bad configuration value. The specific
 error is printed in the message. The new configuration is ignored,
@@ -65,8 +65,8 @@ and an error is sent back.
 % STATHTTPD_SHUTDOWN shutting down
 The stats-httpd daemon is shutting down.
 
-% STATHTTPD_START_SERVER_INIT_ERROR http server initialization error: %1
-There was a problem initializing the http server in the stats-httpd
+% STATHTTPD_START_SERVER_INIT_ERROR HTTP server initialization error: %1
+There was a problem initializing the HTTP server in the stats-httpd
 module upon startup. The most likely cause is that it was not able
 to bind to the listening port. The specific error is printed, and the
 module will shut down.

+ 1 - 1
src/bin/xfrout/xfrout_messages.mes

@@ -48,7 +48,7 @@ There was a problem reading from the command and control channel. The
 most likely cause is that the msgq daemon is not running.
 
 % XFROUT_CC_SESSION_TIMEOUT_ERROR timeout waiting for cc response
-There was a problem reading a response from antoher module over the
+There was a problem reading a response from another module over the
 command and control channel. The most likely cause is that the
 configuration manager b10-cfgmgr is not running.
 

+ 8 - 2
src/bin/zonemgr/Makefile.am

@@ -6,11 +6,13 @@ pkglibexec_SCRIPTS = b10-zonemgr
 
 b10_zonemgrdir = $(pkgdatadir)
 b10_zonemgr_DATA = zonemgr.spec
+pyexec_DATA = zonemgr_messages.py
 
-CLEANFILES = b10-zonemgr zonemgr.pyc zonemgr.spec
+CLEANFILES  = b10-zonemgr zonemgr.pyc zonemgr.spec
+CLEANFILES += zonemgr_messages.py zonemgr_messages.pyc
 
 man_MANS = b10-zonemgr.8
-EXTRA_DIST = $(man_MANS) b10-zonemgr.xml
+EXTRA_DIST = $(man_MANS) b10-zonemgr.xml zonemgr_messages.mes
 
 if ENABLE_MAN
 
@@ -19,6 +21,10 @@ b10-zonemgr.8: b10-zonemgr.xml
 
 endif
 
+# Build logging source file from message files
+zonemgr_messages.py: zonemgr_messages.mes
+	$(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/bin/zonemgr/zonemgr_messages.mes
+
 zonemgr.spec: zonemgr.spec.pre
 	$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" zonemgr.spec.pre >$@
 

+ 51 - 30
src/bin/zonemgr/zonemgr.py.in

@@ -37,6 +37,16 @@ from isc.datasrc import sqlite3_ds
 from optparse import OptionParser, OptionValueError
 from isc.config.ccsession import *
 import isc.util.process
+from zonemgr_messages import *
+
+# Initialize logging for called modules.
+isc.log.init("b10-zonemgr")
+logger = isc.log.Logger("zonemgr")
+
+# Constants for debug levels, to be removed when we have #1074.
+DBG_START_SHUT = 0
+DBG_ZONEMGR_COMMAND = 10
+DBG_ZONEMGR_BASIC = 40
 
 isc.util.process.rename()
 
@@ -77,13 +87,6 @@ REFRESH_OFFSET = 3
 RETRY_OFFSET = 4
 EXPIRED_OFFSET = 5
 
-# verbose mode
-VERBOSE_MODE = False
-
-def log_msg(msg):
-    if VERBOSE_MODE:
-        sys.stdout.write("[b10-zonemgr] %s\n" % str(msg))
-
 class ZonemgrException(Exception):
     pass
 
@@ -93,7 +96,6 @@ class ZonemgrRefresh:
     do zone refresh.
     Zone timers can be started by calling run_timer(), and it
     can be stopped by calling shutdown() in another thread.
-
     """
 
     def __init__(self, cc, db_file, slave_socket, config_data):
@@ -157,6 +159,7 @@ class ZonemgrRefresh:
     def zone_refresh_success(self, zone_name_class):
         """Update zone info after zone refresh success"""
         if (self._zone_not_exist(zone_name_class)):
+            logger.error(ZONEMGR_UNKNOWN_ZONE_SUCCESS, zone_name_class[0], zone_name_class[1])
             raise ZonemgrException("[b10-zonemgr] Zone (%s, %s) doesn't "
                                    "belong to zonemgr" % zone_name_class)
         self.zonemgr_reload_zone(zone_name_class)
@@ -167,6 +170,7 @@ class ZonemgrRefresh:
     def zone_refresh_fail(self, zone_name_class):
         """Update zone info after zone refresh fail"""
         if (self._zone_not_exist(zone_name_class)):
+            logger.error(ZONEMGR_UNKNOWN_ZONE_FAIL, zone_name_class[0], zone_name_class[1])
             raise ZonemgrException("[b10-zonemgr] Zone (%s, %s) doesn't "
                                    "belong to zonemgr" % zone_name_class)
         # Is zone expired?
@@ -179,6 +183,7 @@ class ZonemgrRefresh:
     def zone_handle_notify(self, zone_name_class, master):
         """Handle zone notify"""
         if (self._zone_not_exist(zone_name_class)):
+            logger.error(ZONEMGR_UNKNOWN_ZONE_NOTIFIED, zone_name_class[0], zone_name_class[1])
             raise ZonemgrException("[b10-zonemgr] Notified zone (%s, %s) "
                                    "doesn't belong to zonemgr" % zone_name_class)
         self._set_zone_notifier_master(zone_name_class, master)
@@ -191,10 +196,12 @@ class ZonemgrRefresh:
 
     def zonemgr_add_zone(self, zone_name_class):
         """ Add a zone into zone manager."""
-        log_msg("Loading zone (%s, %s)" % zone_name_class)
+
+        logger.debug(DBG_ZONEMGR_BASIC, ZONEMGR_LOAD_ZONE, zone_name_class[0], zone_name_class[1])
         zone_info = {}
         zone_soa = sqlite3_ds.get_zone_soa(str(zone_name_class[0]), self._db_file)
         if not zone_soa:
+            logger.error(ZONEMGR_NO_SOA, zone_name_class[0], zone_name_class[1])
             raise ZonemgrException("[b10-zonemgr] zone (%s, %s) doesn't have soa." % zone_name_class)
         zone_info["zone_soa_rdata"] = zone_soa[7]
         zone_info["zone_state"] = ZONE_OK
@@ -265,7 +272,7 @@ class ZonemgrRefresh:
             except isc.cc.session.SessionTimeout:
                 pass        # for now we just ignore the failure
         except socket.error:
-            sys.stderr.write("[b10-zonemgr] Failed to send to module %s, the session has been closed." % module_name)
+            logger.error(ZONEMGR_SEND_FAIL, module_name)
 
     def _find_need_do_refresh_zone(self):
         """Find the first zone need do refresh, if no zone need
@@ -274,7 +281,8 @@ class ZonemgrRefresh:
         zone_need_refresh = None
         for zone_name_class in self._zonemgr_refresh_info.keys():
             zone_state = self._get_zone_state(zone_name_class)
-            # If hasn't received refresh response but are within refresh timeout, skip the zone
+            # If hasn't received refresh response but are within refresh
+            # timeout, skip the zone
             if (ZONE_REFRESHING == zone_state and
                 (self._get_zone_refresh_timeout(zone_name_class) > self._get_current_time())):
                 continue
@@ -294,7 +302,7 @@ class ZonemgrRefresh:
 
     def _do_refresh(self, zone_name_class):
         """Do zone refresh."""
-        log_msg("Do refresh for zone (%s, %s)." % zone_name_class)
+        logger.debug(DBG_ZONEMGR_BASIC, ZONEMGR_REFRESH_ZONE, zone_name_class[0], zone_name_class[1])
         self._set_zone_state(zone_name_class, ZONE_REFRESHING)
         self._set_zone_refresh_timeout(zone_name_class, self._get_current_time() + self._max_transfer_timeout)
         notify_master = self._get_zone_notifier_master(zone_name_class)
@@ -351,7 +359,7 @@ class ZonemgrRefresh:
                 if e.args[0] == errno.EINTR:
                     (rlist, wlist, xlist) = ([], [], [])
                 else:
-                    sys.stderr.write("[b10-zonemgr] Error with select(); %s\n" % e)
+                    logger.error(ZONEMGR_SELECT_ERROR, e);
                     break
 
             for fd in rlist:
@@ -365,12 +373,14 @@ class ZonemgrRefresh:
 
     def run_timer(self, daemon=False):
         """
-        Keep track of zone timers. Spawns and starts a thread. The thread object is returned.
+        Keep track of zone timers. Spawns and starts a thread. The thread object
+        is returned.
 
         You can stop it by calling shutdown().
         """
         # Small sanity check
         if self._running:
+            logger.error(ZONEMGR_TIMER_THREAD_RUNNING)
             raise RuntimeError("Trying to run the timers twice at the same time")
 
         # Prepare the launch
@@ -395,6 +405,7 @@ class ZonemgrRefresh:
         called from a different thread.
         """
         if not self._running:
+            logger.error(ZONEMGR_NO_TIMER_THREAD)
             raise RuntimeError("Trying to shutdown, but not running")
 
         # Ask the thread to stop
@@ -515,8 +526,8 @@ class Zonemgr:
         return db_file
 
     def shutdown(self):
-        """Shutdown the zonemgr process. the thread which is keeping track of zone
-        timers should be terminated.
+        """Shutdown the zonemgr process. The thread which is keeping track of
+           zone timers should be terminated.
         """
         self._zone_refresh.shutdown()
 
@@ -556,17 +567,17 @@ class Zonemgr:
         # jitter should not be bigger than half of the original value
         if config_data.get('refresh_jitter') > 0.5:
             config_data['refresh_jitter'] = 0.5
-            log_msg("[b10-zonemgr] refresh_jitter is too big, its value will "
-                      "be set to 0.5")
-
+            logger.warn(ZONEMGR_JITTER_TOO_BIG)
 
     def _parse_cmd_params(self, args, command):
         zone_name = args.get("zone_name")
         if not zone_name:
+            logger.error(ZONEMGR_NO_ZONE_NAME)
             raise ZonemgrException("zone name should be provided")
 
         zone_class = args.get("zone_class")
         if not zone_class:
+            logger.error(ZONEMGR_NO_ZONE_CLASS)
             raise ZonemgrException("zone class should be provided")
 
         if (command != ZONE_NOTIFY_COMMAND):
@@ -574,6 +585,7 @@ class Zonemgr:
 
         master_str = args.get("master")
         if not master_str:
+            logger.error(ZONEMGR_NO_MASTER_ADDRESS)
             raise ZonemgrException("master address should be provided")
 
         return ((zone_name, zone_class), master_str)
@@ -581,15 +593,16 @@ class Zonemgr:
 
     def command_handler(self, command, args):
         """Handle command receivd from command channel.
-        ZONE_NOTIFY_COMMAND is issued by Auth process; ZONE_XFRIN_SUCCESS_COMMAND
-        and ZONE_XFRIN_FAILED_COMMAND are issued by Xfrin process; shutdown is issued
-        by a user or Boss process. """
+        ZONE_NOTIFY_COMMAND is issued by Auth process;
+        ZONE_XFRIN_SUCCESS_COMMAND and ZONE_XFRIN_FAILED_COMMAND are issued by
+        Xfrin process;
+        shutdown is issued by a user or Boss process. """
         answer = create_answer(0)
         if command == ZONE_NOTIFY_COMMAND:
             """ Handle Auth notify command"""
             # master is the source sender of the notify message.
             zone_name_class, master = self._parse_cmd_params(args, command)
-            log_msg("Received notify command for zone (%s, %s)." % zone_name_class)
+            logger.debug(DBG_ZONEMGR_COMMAND, ZONEMGR_RECEIVE_NOTIFY, zone_name_class[0], zone_name_class[1])
             with self._lock:
                 self._zone_refresh.zone_handle_notify(zone_name_class, master)
             # Send notification to zonemgr timer thread
@@ -598,6 +611,7 @@ class Zonemgr:
         elif command == ZONE_XFRIN_SUCCESS_COMMAND:
             """ Handle xfrin success command"""
             zone_name_class = self._parse_cmd_params(args, command)
+            logger.debug(DBG_ZONEMGR_COMMAND, ZONEMGR_RECEIVE_XFRIN_SUCCESS, zone_name_class[0], zone_name_class[1])
             with self._lock:
                 self._zone_refresh.zone_refresh_success(zone_name_class)
             self._master_socket.send(b" ")# make self._slave_socket readble
@@ -605,14 +619,17 @@ class Zonemgr:
         elif command == ZONE_XFRIN_FAILED_COMMAND:
             """ Handle xfrin fail command"""
             zone_name_class = self._parse_cmd_params(args, command)
+            logger.debug(DBG_ZONEMGR_COMMAND, ZONEMGR_RECEIVE_XFRIN_FAILED, zone_name_class[0], zone_name_class[1])
             with self._lock:
                 self._zone_refresh.zone_refresh_fail(zone_name_class)
             self._master_socket.send(b" ")# make self._slave_socket readble
 
         elif command == "shutdown":
+            logger.debug(DBG_ZONEMGR_COMMAND, ZONEMGR_RECEIVE_SHUTDOWN)
             self.shutdown()
 
         else:
+            logger.warn(ZONEMGR_RECEIVE_UNKNOWN, str(command))
             answer = create_answer(1, "Unknown command:" + str(command))
 
         return answer
@@ -639,25 +656,29 @@ def set_cmd_options(parser):
 
 if '__main__' == __name__:
     try:
+        logger.debug(DBG_START_SHUT, ZONEMGR_STARTING)
         parser = OptionParser()
         set_cmd_options(parser)
         (options, args) = parser.parse_args()
-        VERBOSE_MODE = options.verbose
+        if options.verbose:
+            logger.set_severity("DEBUG", 99)
 
         set_signal_handler()
         zonemgrd = Zonemgr()
         zonemgrd.run()
     except KeyboardInterrupt:
-        sys.stderr.write("[b10-zonemgr] exit zonemgr process\n")
+        logger.info(ZONEMGR_KEYBOARD_INTERRUPT)
+
     except isc.cc.session.SessionError as e:
-        sys.stderr.write("[b10-zonemgr] Error creating zonemgr, "
-                           "is the command channel daemon running?\n")
+        logger.error(ZONEMGR_SESSION_ERROR)
+
     except isc.cc.session.SessionTimeout as e:
-        sys.stderr.write("[b10-zonemgr] Error creating zonemgr, "
-                           "is the configuration manager running?\n")
+        logger.error(ZONEMGR_SESSION_TIMEOUT)
+
     except isc.config.ModuleCCSessionError as e:
-        sys.stderr.write("[b10-zonemgr] exit zonemgr process: %s\n" % str(e))
+        logger.error(ZONEMGR_CCSESSION_ERROR, str(e))
 
     if zonemgrd and zonemgrd.running:
         zonemgrd.shutdown()
 
+    logger.debug(DBG_START_SHUT, ZONEMGR_SHUTDOWN)

+ 145 - 0
src/bin/zonemgr/zonemgr_messages.mes

@@ -0,0 +1,145 @@
+# Copyright (C) 2011  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.
+
+# No namespace declaration - these constants go in the global namespace
+# of the zonemgr messages python module.
+
+% ZONEMGR_CCSESSION_ERROR command channel session error: %1
+An error was encountered on the command channel.  The message indicates
+the nature of the error.
+
+% ZONEMGR_JITTER_TOO_BIG refresh_jitter is too big, setting to 0.5
+The value specified in the configuration for the refresh jitter is too large
+so its value has been set to the maximum of 0.5.
+
+% ZONEMGR_KEYBOARD_INTERRUPT exiting zonemgr process as result of keyboard interrupt
+An informational message output when the zone manager was being run at a
+terminal and it was terminated via a keyboard interrupt signal.
+
+% ZONEMGR_LOAD_ZONE loading zone %1 (class %2)
+This is a debug message indicating that the zone of the specified class
+is being loaded.
+
+% ZONEMGR_NO_MASTER_ADDRESS internal BIND 10 command did not contain address of master
+A command received by the zone manager from the Auth module did not
+contain the address of the master server from which a NOTIFY message
+was received.  This may be due to an internal programming error; please
+submit a bug report.
+
+% ZONEMGR_NO_SOA zone %1 (class %2) does not have an SOA record
+When loading the named zone of the specified class the zone manager
+discovered that the data did not contain an SOA record.  The load has
+been abandoned.
+
+% ZONEMGR_NO_TIMER_THREAD trying to stop zone timer thread but it is not running
+An attempt was made to stop the timer thread (used to track when zones
+should be refreshed) but it was not running.  This may indicate an
+internal program error.  Please submit a bug report.
+
+% ZONEMGR_NO_ZONE_CLASS internal BIND 10 command did not contain class of zone
+A command received by the zone manager from another BIND 10 module did
+not contain the class of the zone on which the zone manager should act.
+This may be due to an internal programming error; please submit a
+bug report.
+
+% ZONEMGR_NO_ZONE_NAME internal BIND 10 command did not contain name of zone
+A command received by the zone manager from another BIND 10 module did
+not contain the name of the zone on which the zone manager should act.
+This may be due to an internal programming error; please submit a
+bug report.
+
+% ZONEMGR_RECEIVE_NOTIFY received NOTIFY command for zone %1 (class %2)
+This is a debug message indicating that the zone manager has received a
+NOTIFY command over the command channel.  The command is sent by the Auth
+process when it is acting as a slave server for the zone and causes the
+zone manager to record the master server for the zone and start a timer;
+when the timer expires, the master will be polled to see if it contains
+new data.
+
+% ZONEMGR_RECEIVE_SHUTDOWN received SHUTDOWN command
+This is a debug message indicating that the zone manager has received
+a SHUTDOWN command over the command channel from the Boss process.
+It will act on this command and shut down.
+
+% ZONEMGR_RECEIVE_UNKNOWN received unknown command '%1'
+This is a warning message indicating that the zone manager has received
+the stated command over the command channel.  The command is not known
+to the zone manager and although the command is ignored, its receipt
+may indicate an internal error.  Please submit a bug report.
+
+% ZONEMGR_RECEIVE_XFRIN_FAILED received XFRIN FAILED command for zone %1 (class %2)
+This is a debug message indicating that the zone manager has received
+an XFRIN FAILED command over the command channel.  The command is sent
+by the Xfrin process when a transfer of zone data into the system has
+failed, and causes the zone manager to schedule another transfer attempt.
+
+% ZONEMGR_RECEIVE_XFRIN_SUCCESS received XFRIN SUCCESS command for zone %1 (class %2)
+This is a debug message indicating that the zone manager has received
+an XFRIN SUCCESS command over the command channel.  The command is sent
+by the Xfrin process when the transfer of zone data into the system has
+succeeded, and causes the data to be loaded and served by BIND 10.
+
+% ZONEMGR_REFRESH_ZONE refreshing zone %1 (class %2)
+The zone manager is refreshing the named zone of the specified class
+with updated information.
+
+% ZONEMGR_SELECT_ERROR error with select(): %1
+An attempt to wait for input from a socket failed.  The failing operation
+is a call to the operating system's select() function, which failed for
+the given reason.
+
+% ZONEMGR_SEND_FAIL failed to send command to %1, session has been closed
+The zone manager attempted to send a command to the named BIND 10 module,
+but the send failed.  The session between the modules has been closed.
+
+% ZONEMGR_SESSION_ERROR unable to establish session to command channel daemon
+The zonemgr process was not able to be started because it could not
+connect to the command channel daemon.  The most usual cause of this
+problem is that the daemon is not running.
+
+% ZONEMGR_SESSION_TIMEOUT timeout on session to command channel daemon
+The zonemgr process was not able to be started because it timed out when
+connecting to the command channel daemon.  The most usual cause of this
+problem is that the daemon is not running.
+
+% ZONEMGR_SHUTDOWN zone manager has shut down
+A debug message, output when the zone manager has shut down completely.
+
+% ZONEMGR_STARTING zone manager starting
+A debug message output when the zone manager starts up.
+
+% ZONEMGR_TIMER_THREAD_RUNNING trying to start timer thread but one is already running
+This message is issued when an attempt is made to start the timer
+thread (which keeps track of when zones need a refresh) but one is
+already running.  It indicates either an error in the program logic or
+a problem with stopping a previous instance of the timer.  Please submit
+a bug report.
+
+% ZONEMGR_UNKNOWN_ZONE_FAIL zone %1 (class %2) is not known to the zone manager
+An XFRIN operation has failed but the zone that was the subject of the
+operation is not being managed by the zone manager.  This may indicate
+an error in the program (as the operation should not have been initiated
+if this were the case).  Please submit a bug report.
+
+% ZONEMGR_UNKNOWN_ZONE_NOTIFIED notified zone %1 (class %2) is not known to the zone manager
+A NOTIFY was received but the zone that was the subject of the operation
+is not being managed by the zone manager.  This may indicate an error
+in the program (as the operation should not have been initiated if this
+were the case).  Please submit a bug report.
+
+% ZONEMGR_UNKNOWN_ZONE_SUCCESS zone %1 (class %2) is not known to the zone manager
+An XFRIN operation has succeeded but the zone received is not being
+managed by the zone manager.  This may indicate an error in the program
+(as the operation should not have been initiated if this were the case).
+Please submit a bug report.

+ 5 - 5
src/lib/asiodns/asiodns_messages.mes

@@ -26,13 +26,13 @@ enabled.
 % ASIODNS_OPEN_SOCKET error %1 opening %2 socket to %3(%4)
 The asynchronous I/O code encountered an error when trying to open a socket
 of the specified protocol in order to send a message to the target address.
-The number of the system error that cause the problem is given in the
+The number of the system error that caused the problem is given in the
 message.
 
 % ASIODNS_READ_DATA error %1 reading %2 data from %3(%4)
 The asynchronous I/O code encountered an error when trying to read data from
 the specified address on the given protocol.  The number of the system
-error that cause the problem is given in the message.
+error that caused the problem is given in the message.
 
 % ASIODNS_READ_TIMEOUT receive timeout while waiting for data from %1(%2)
 An upstream fetch from the specified address timed out.  This may happen for
@@ -41,9 +41,9 @@ or a problem on the network.  The message will only appear if debug is
 enabled.
 
 % ASIODNS_SEND_DATA error %1 sending data using %2 to %3(%4)
-The asynchronous I/O code encountered an error when trying send data to
-the specified address on the given protocol.  The the number of the system
-error that cause the problem is given in the message.
+The asynchronous I/O code encountered an error when trying to send data to
+the specified address on the given protocol.  The number of the system
+error that caused the problem is given in the message.
 
 % ASIODNS_UNKNOWN_ORIGIN unknown origin for ASIO error code %1 (protocol: %2, address %3)
 An internal consistency check on the origin of a message from the

+ 1 - 0
src/lib/bench/tests/Makefile.am

@@ -16,6 +16,7 @@ run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 run_unittests_LDADD  = $(top_builddir)/src/lib/bench/libbench.la
 run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 run_unittests_LDADD += $(GTEST_LDADD)

+ 1 - 0
src/lib/cache/tests/Makefile.am

@@ -56,6 +56,7 @@ run_unittests_LDADD += $(top_builddir)/src/lib/cache/libcache.la
 run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
 run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
 run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
 run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la

+ 3 - 3
src/lib/cc/cc_messages.mes

@@ -53,11 +53,11 @@ Debug message, we're about to send a message over the command channel.
 This happens when garbage comes over the command channel or some kind of
 confusion happens in the program. The data received from the socket make no
 sense if we interpret it as lengths of message. The first one is total length
-of message, the second length of the header. The header and it's length
-(2 bytes) is counted in the total length.
+of the message; the second is the length of the header. The header
+and its length (2 bytes) is counted in the total length.
 
 % CC_LENGTH_NOT_READY length not ready
-There should be data representing length of message on the socket, but it
+There should be data representing the length of message on the socket, but it
 is not there.
 
 % CC_NO_MESSAGE no message ready to be received yet

+ 103 - 26
src/lib/config/ccsession.cc

@@ -18,12 +18,15 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/time.h>
+#include <ctype.h>
 
-#include <iostream>
-#include <fstream>
-#include <sstream>
+#include <algorithm>
 #include <cerrno>
+#include <fstream>
+#include <iostream>
 #include <set>
+#include <sstream>
+#include <string>
 
 #include <boost/bind.hpp>
 #include <boost/foreach.hpp>
@@ -175,6 +178,36 @@ ConstElementPtr getValueOrDefault(ConstElementPtr config_part,
     }
 }
 
+// Prefix name with "b10-".
+//
+// In BIND 10, modules have names taken from the .spec file, which are typically
+// names starting with a capital letter (e.g. "Resolver", "Auth" etc.).  The
+// names of the associated binaries are derived from the module names, being
+// prefixed "b10-" and having the first letter of the module name lower-cased
+// (e.g. "b10-resolver", "b10-auth").  (It is a required convention that there
+// be this relationship between the names.)
+//
+// Within the binaries the root loggers are named after the binaries themselves.
+// (The reason for this is that the name of the logger is included in the
+// message logged, so making it clear which message comes from which BIND 10
+// process.) As logging is configured using module names, the configuration code
+// has to match these with the corresponding logger names. This function
+// converts a module name to a root logger name by lowercasing the first letter
+// of the module name and prepending "b10-".
+//
+// \param instring String to convert.  (This may be empty, in which case
+//        "b10-" will be returned.)
+//
+// \return Converted string.
+std::string
+b10Prefix(const std::string& instring) {
+    std::string result = instring;
+    if (!result.empty()) {
+        result[0] = tolower(result[0]);
+    }
+    return (std::string("b10-") + result);
+}
+
 // Reads a output_option subelement of a logger configuration,
 // and sets the values thereing to the given OutputOption struct,
 // or defaults values if they are not provided (from config_data).
@@ -215,6 +248,7 @@ readLoggersConf(std::vector<isc::log::LoggerSpecification>& specs,
                 ConstElementPtr logger,
                 const ConfigData& config_data)
 {
+    // Read name, adding prefix as required.
     std::string lname = logger->get("name")->stringValue();
 
     ConstElementPtr severity_el = getValueOrDefault(logger,
@@ -247,6 +281,27 @@ readLoggersConf(std::vector<isc::log::LoggerSpecification>& specs,
     specs.push_back(logger_spec);
 }
 
+// Copies the map for a logger, changing the name of the logger in the process.
+// This is used because the map being copied is "const", so in order to
+// change the name we need to create a new one.
+//
+// \param cur_logger Logger being copied.
+// \param new_name New value of the "name" element at the top level.
+//
+// \return Pointer to the map with the updated element.
+ConstElementPtr
+copyLogger(ConstElementPtr& cur_logger, const std::string& new_name) {
+
+    // Since we'll only be updating one first-level element and subsequent
+    // use won't change the contents of the map, a shallow map copy is enough.
+    ElementPtr new_logger(Element::createMap());
+    new_logger->setValue(cur_logger->mapValue());
+    new_logger->set("name", Element::create(new_name));
+
+    return (new_logger);
+}
+
+
 } // end anonymous namespace
 
 
@@ -259,38 +314,60 @@ getRelatedLoggers(ConstElementPtr loggers) {
     ElementPtr result = isc::data::Element::createList();
 
     BOOST_FOREACH(ConstElementPtr cur_logger, loggers->listValue()) {
+        // Need to add the b10- prefix to names ready from the spec file.
         const std::string cur_name = cur_logger->get("name")->stringValue();
-        if (cur_name == root_name || cur_name.find(root_name + ".") == 0) {
-            our_names.insert(cur_name);
-            result->add(cur_logger);
+        const std::string mod_name = b10Prefix(cur_name);
+        if (mod_name == root_name || mod_name.find(root_name + ".") == 0) {
+
+            // Note this name so that we don't add a wildcard that matches it.
+            our_names.insert(mod_name);
+
+            // We want to store the logger with the modified name (i.e. with
+            // the b10- prefix).  As we are dealing with const loggers, we
+            // store a modified copy of the data.
+            result->add(copyLogger(cur_logger, mod_name));
+            LOG_DEBUG(config_logger, DBG_CONFIG_PROCESS, CONFIG_LOG_EXPLICIT)
+                      .arg(cur_name);
+
+        } else if (!cur_name.empty() && (cur_name[0] != '*')) {
+            // Not a wildcard logger and we are ignoring it.
+            LOG_DEBUG(config_logger, DBG_CONFIG_PROCESS,
+                      CONFIG_LOG_IGNORE_EXPLICIT).arg(cur_name);
         }
     }
 
-    // now find the * names
+    // Now find the wildcard names (the one that start with "*").
     BOOST_FOREACH(ConstElementPtr cur_logger, loggers->listValue()) {
         std::string cur_name = cur_logger->get("name")->stringValue();
-        // if name is '*', or starts with '*.', replace * with root
-        // logger name
+        // If name is '*', or starts with '*.', replace * with root
+        // logger name.
         if (cur_name == "*" || cur_name.length() > 1 &&
             cur_name[0] == '*' && cur_name[1] == '.') {
 
-            cur_name = root_name + cur_name.substr(1);
-            // now add it to the result list, but only if a logger with
-            // that name was not configured explicitely
-            if (our_names.find(cur_name) == our_names.end()) {
-                // we substitute the name here already, but as
-                // we are dealing with consts, we copy the data
-                ElementPtr new_logger(Element::createMap());
-                // since we'll only be updating one first-level element,
-                // and we return as const again, a shallow map copy is
-                // enough
-                new_logger->setValue(cur_logger->mapValue());
-                new_logger->set("name", Element::create(cur_name));
-                result->add(new_logger);
+            // Substitute the "*" with the root name
+            std::string mod_name = cur_name;
+            mod_name.replace(0, 1, root_name);
+
+            // Now add it to the result list, but only if a logger with
+            // that name was not configured explicitly.
+            if (our_names.find(mod_name) == our_names.end()) {
+
+                // We substitute the name here, but as we are dealing with
+                // consts, we need to copy the data.
+                result->add(copyLogger(cur_logger, mod_name));
+                LOG_DEBUG(config_logger, DBG_CONFIG_PROCESS,
+                          CONFIG_LOG_WILD_MATCH).arg(cur_name);
+
+            } else if (!cur_name.empty() && (cur_name[0] == '*')) {
+                // Is a wildcard and we are ignoring it (because the wildcard
+                // expands to a specification that we already encountered when
+                // processing explicit names).
+                LOG_DEBUG(config_logger, DBG_CONFIG_PROCESS,
+                          CONFIG_LOG_IGNORE_WILD).arg(cur_name);
             }
         }
     }
-    return result;
+    return (result);
 }
 
 void
@@ -318,7 +395,7 @@ ModuleSpec
 ModuleCCSession::readModuleSpecification(const std::string& filename) {
     std::ifstream file;
     ModuleSpec module_spec;
-    
+
     // this file should be declared in a @something@ directive
     file.open(filename.c_str());
     if (!file) {
@@ -385,7 +462,7 @@ ModuleCCSession::ModuleCCSession(
         LOG_ERROR(config_logger, CONFIG_MOD_SPEC_REJECT).arg(answer->str());
         isc_throw(CCSessionInitError, answer->str());
     }
-    
+
     setLocalConfig(Element::fromJSON("{}"));
     // get any stored configuration from the manager
     if (config_handler_) {
@@ -511,7 +588,7 @@ int
 ModuleCCSession::checkCommand() {
     ConstElementPtr cmd, routing, data;
     if (session_.group_recvmsg(routing, data, true)) {
-        
+
         /* ignore result messages (in case we're out of sync, to prevent
          * pingpongs */
         if (data->getType() != Element::map || data->contains("result")) {

+ 2 - 2
src/lib/config/ccsession.h

@@ -377,10 +377,10 @@ default_logconfig_handler(const std::string& module_name,
 /// \brief Returns the loggers related to this module
 ///
 /// This function does two things;
-/// - it drops the configuration parts for loggers for other modules
+/// - it drops the configuration parts for loggers for other modules.
 /// - it replaces the '*' in the name of the loggers by the name of
 ///   this module, but *only* if the expanded name is not configured
-///   explicitely
+///   explicitly.
 ///
 /// Examples: if this is the module b10-resolver,
 /// For the config names ['*', 'b10-auth']

+ 8 - 0
src/lib/config/config_log.h

@@ -32,6 +32,14 @@ namespace config {
 /// space.
 extern isc::log::Logger config_logger;    // isc::config::config_logger is the CONFIG logger
 
+/// \brief Debug Levels
+///
+/// Debug levels used in the configuration library
+enum {
+    DBG_CONFIG_PROCESS = 40     // Enumerate configuration elements as they
+                                // ... are processed.
+};
+
 } // namespace config
 } // namespace isc
 

+ 25 - 0
src/lib/config/config_messages.mes

@@ -37,6 +37,31 @@ manager is appended to the log error. The most likely cause is that
 the module is of a different (command specification) version than the
 running configuration manager.
 
+% CONFIG_LOG_EXPLICIT will use logging configuration for explicitly-named logger %1
+This is a debug message.  When processing the "loggers" part of the
+configuration file, the configuration library found an entry for the named
+logger that matches the logger specification for the program.  The logging
+configuration for the program will be updated with the information.
+
+% CONFIG_LOG_IGNORE_EXPLICIT ignoring logging configuration for explicitly-named logger %1
+This is a debug message.  When processing the "loggers" part of the
+configuration file, the configuration library found an entry for the
+named logger.  As this does not match the logger specification for the
+program, it has been ignored.
+
+% CONFIG_LOG_IGNORE_WILD ignoring logging configuration for wildcard logger %1
+This is a debug message.  When processing the "loggers" part of the
+configuration file, the configuration library found the named wildcard
+entry (one containing the "*" character) that matched a logger already
+matched by an explicitly named entry.  The configuration is ignored.
+
+% CONFIG_LOG_WILD_MATCH will use logging configuration for wildcard logger %1
+This is a debug message.  When processing the "loggers" part of
+the configuration file, the configuration library found the named
+wildcard entry (one containing the "*" character) that matches a logger
+specification in the program. The logging configuration for the program
+will be updated with the information.
+
 % CONFIG_JSON_PARSE JSON parse error in %1: %2
 There was an error parsing the JSON file. The given file does not appear
 to be in valid JSON format. Please verify that the filename is correct

+ 33 - 25
src/lib/config/tests/ccsession_unittests.cc

@@ -44,7 +44,9 @@ el(const std::string& str) {
 
 class CCSessionTest : public ::testing::Test {
 protected:
-    CCSessionTest() : session(el("[]"), el("[]"), el("[]")) {
+    CCSessionTest() : session(el("[]"), el("[]"), el("[]")),
+                      root_name(isc::log::getRootLoggerName())
+    {
         // upon creation of a ModuleCCSession, the class
         // sends its specification to the config manager.
         // it expects an ok answer back, so everytime we
@@ -52,8 +54,11 @@ protected:
         // ok answer.
         session.getMessages()->add(createAnswer());
     }
-    ~CCSessionTest() {}
+    ~CCSessionTest() {
+        isc::log::setRootLoggerName(root_name);
+    }
     FakeSession session;
+    const std::string root_name;
 };
 
 TEST_F(CCSessionTest, createAnswer) {
@@ -652,41 +657,44 @@ void doRelatedLoggersTest(const char* input, const char* expected) {
 TEST(LogConfigTest, relatedLoggersTest) {
     // make sure logger configs for 'other' programs are ignored,
     // and that * is substituted correctly
-    // The default root logger name is "bind10"
+    // We'll use a root logger name of "b10-test".
+    isc::log::setRootLoggerName("b10-test");
+
     doRelatedLoggersTest("[{ \"name\": \"other_module\" }]",
                          "[]");
     doRelatedLoggersTest("[{ \"name\": \"other_module.somelib\" }]",
                          "[]");
-    doRelatedLoggersTest("[{ \"name\": \"bind10_other\" }]",
+    doRelatedLoggersTest("[{ \"name\": \"test_other\" }]",
                          "[]");
-    doRelatedLoggersTest("[{ \"name\": \"bind10_other.somelib\" }]",
+    doRelatedLoggersTest("[{ \"name\": \"test_other.somelib\" }]",
                          "[]");
     doRelatedLoggersTest("[ { \"name\": \"other_module\" },"
-                         "  { \"name\": \"bind10\" }]",
-                         "[ { \"name\": \"bind10\" } ]");
-    doRelatedLoggersTest("[ { \"name\": \"bind10\" }]",
-                         "[ { \"name\": \"bind10\" } ]");
-    doRelatedLoggersTest("[ { \"name\": \"bind10.somelib\" }]",
-                         "[ { \"name\": \"bind10.somelib\" } ]");
+                         "  { \"name\": \"test\" }]",
+                         "[ { \"name\": \"b10-test\" } ]");
+    doRelatedLoggersTest("[ { \"name\": \"test\" }]",
+                         "[ { \"name\": \"b10-test\" } ]");
+    doRelatedLoggersTest("[ { \"name\": \"test.somelib\" }]",
+                         "[ { \"name\": \"b10-test.somelib\" } ]");
     doRelatedLoggersTest("[ { \"name\": \"other_module.somelib\" },"
-                         "  { \"name\": \"bind10.somelib\" }]",
-                         "[ { \"name\": \"bind10.somelib\" } ]");
+                         "  { \"name\": \"test.somelib\" }]",
+                         "[ { \"name\": \"b10-test.somelib\" } ]");
     doRelatedLoggersTest("[ { \"name\": \"other_module.somelib\" },"
-                         "  { \"name\": \"bind10\" },"
-                         "  { \"name\": \"bind10.somelib\" }]",
-                         "[ { \"name\": \"bind10\" },"
-                         "  { \"name\": \"bind10.somelib\" } ]");
+                         "  { \"name\": \"test\" },"
+                         "  { \"name\": \"test.somelib\" }]",
+                         "[ { \"name\": \"b10-test\" },"
+                         "  { \"name\": \"b10-test.somelib\" } ]");
     doRelatedLoggersTest("[ { \"name\": \"*\" }]",
-                         "[ { \"name\": \"bind10\" } ]");
+                         "[ { \"name\": \"b10-test\" } ]");
     doRelatedLoggersTest("[ { \"name\": \"*.somelib\" }]",
-                         "[ { \"name\": \"bind10.somelib\" } ]");
+                         "[ { \"name\": \"b10-test.somelib\" } ]");
     doRelatedLoggersTest("[ { \"name\": \"*\", \"severity\": \"DEBUG\" },"
-                         "  { \"name\": \"bind10\", \"severity\": \"WARN\"}]",
-                         "[ { \"name\": \"bind10\", \"severity\": \"WARN\"} ]");
+                         "  { \"name\": \"test\", \"severity\": \"WARN\"}]",
+                         "[ { \"name\": \"b10-test\", \"severity\": \"WARN\"} ]");
     doRelatedLoggersTest("[ { \"name\": \"*\", \"severity\": \"DEBUG\" },"
                          "  { \"name\": \"some_module\", \"severity\": \"WARN\"}]",
-                         "[ { \"name\": \"bind10\", \"severity\": \"DEBUG\"} ]");
-
+                         "[ { \"name\": \"b10-test\", \"severity\": \"DEBUG\"} ]");
+    doRelatedLoggersTest("[ { \"name\": \"b10-test\" }]",
+                         "[]");
     // make sure 'bad' things like '*foo.x' or '*lib' are ignored
     // (cfgmgr should have already caught it in the logconfig plugin
     // check, and is responsible for reporting the error)
@@ -696,8 +704,8 @@ TEST(LogConfigTest, relatedLoggersTest) {
                          "[ ]");
     doRelatedLoggersTest("[ { \"name\": \"*foo\" },"
                          "  { \"name\": \"*foo.lib\" },"
-                         "  { \"name\": \"bind10\" } ]",
-                         "[ { \"name\": \"bind10\" } ]");
+                         "  { \"name\": \"test\" } ]",
+                         "[ { \"name\": \"b10-test\" } ]");
 }
 
 }

+ 1 - 0
src/lib/datasrc/tests/Makefile.am

@@ -36,6 +36,7 @@ run_unittests_LDADD  = $(GTEST_LDADD)
 run_unittests_LDADD += $(SQLITE_LIBS)
 run_unittests_LDADD += $(top_builddir)/src/lib/datasrc/libdatasrc.la
 run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
 run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la

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

@@ -58,6 +58,8 @@ EXTRA_DIST += rdata/in_1/a_1.cc
 EXTRA_DIST += rdata/in_1/a_1.h
 EXTRA_DIST += rdata/in_1/aaaa_28.cc
 EXTRA_DIST += rdata/in_1/aaaa_28.h
+EXTRA_DIST += rdata/in_1/srv_33.cc
+EXTRA_DIST += rdata/in_1/srv_33.h
 #EXTRA_DIST += rdata/template.cc
 #EXTRA_DIST += rdata/template.h
 

+ 1 - 0
src/lib/dns/benchmarks/Makefile.am

@@ -13,5 +13,6 @@ noinst_PROGRAMS = rdatarender_bench
 rdatarender_bench_SOURCES = rdatarender_bench.cc
 
 rdatarender_bench_LDADD = $(top_builddir)/src/lib/dns/libdns++.la
+rdatarender_bench_LDADD += $(top_builddir)/src/lib/util/libutil.la
 rdatarender_bench_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 rdatarender_bench_LDADD += $(SQLITE_LIBS)

+ 47 - 79
src/lib/dns/rdata/any_255/tsig_250.cc

@@ -19,6 +19,7 @@
 #include <boost/lexical_cast.hpp>
 
 #include <util/buffer.h>
+#include <util/strutil.h>
 #include <util/encode/base64.h>
 
 #include <dns/messagerenderer.h>
@@ -30,6 +31,7 @@ using namespace std;
 using namespace boost;
 using namespace isc::util;
 using namespace isc::util::encode;
+using namespace isc::util::str;
 
 // BEGIN_ISC_NAMESPACE
 // BEGIN_RDATA_NAMESPACE
@@ -65,45 +67,6 @@ struct TSIG::TSIGImpl {
     const vector<uint8_t> other_data_;
 };
 
-namespace {
-string
-getToken(istringstream& iss, const string& full_input) {
-    string token;
-    iss >> token;
-    if (iss.bad() || iss.fail()) {
-        isc_throw(InvalidRdataText, "Invalid TSIG text: parse error " <<
-                  full_input);
-    }
-    return (token);
-}
-
-// This helper function converts a string token to an *unsigned* integer.
-// NumType is a *signed* integral type (e.g. int32_t) that is sufficiently
-// wide to store resulting integers.
-// BitSize is the maximum number of bits that the resulting integer can take.
-// This function first checks whether the given token can be converted to
-// an integer of NumType type.  It then confirms the conversion result is
-// within the valid range, i.e., [0, 2^NumType - 1].  The second check is
-// necessary because lexical_cast<T> where T is an unsigned integer type
-// doesn't correctly reject negative numbers when compiled with SunStudio.
-template <typename NumType, int BitSize>
-NumType
-tokenToNum(const string& num_token) {
-    NumType num;
-    try {
-        num = lexical_cast<NumType>(num_token);
-    } catch (const boost::bad_lexical_cast& ex) {
-        isc_throw(InvalidRdataText, "Invalid TSIG numeric parameter: " <<
-                  num_token);
-    }
-    if (num < 0 || num >= (static_cast<NumType>(1) << BitSize)) {
-        isc_throw(InvalidRdataText, "Numeric TSIG parameter out of range: " <<
-                  num);
-    }
-    return (num);
-}
-}
-
 /// \brief Constructor from string.
 ///
 /// \c tsig_str must be formatted as follows:
@@ -148,47 +111,52 @@ tokenToNum(const string& num_token) {
 TSIG::TSIG(const std::string& tsig_str) : impl_(NULL) {
     istringstream iss(tsig_str);
 
-    const Name algorithm(getToken(iss, tsig_str));
-    const int64_t time_signed = tokenToNum<int64_t, 48>(getToken(iss,
-                                                                 tsig_str));
-    const int32_t fudge = tokenToNum<int32_t, 16>(getToken(iss, tsig_str));
-    const int32_t macsize = tokenToNum<int32_t, 16>(getToken(iss, tsig_str));
-
-    const string mac_txt = (macsize > 0) ? getToken(iss, tsig_str) : "";
-    vector<uint8_t> mac;
-    decodeBase64(mac_txt, mac);
-    if (mac.size() != macsize) {
-        isc_throw(InvalidRdataText, "TSIG MAC size and data are inconsistent");
-    }
-
-    const int32_t orig_id = tokenToNum<int32_t, 16>(getToken(iss, tsig_str));
-
-    const string error_txt = getToken(iss, tsig_str);
-    int32_t error = 0;
-    // XXX: In the initial implementation we hardcode the mnemonics.
-    // We'll soon generalize this.
-    if (error_txt == "BADSIG") {
-        error = 16;
-    } else if (error_txt == "BADKEY") {
-        error = 17;
-    } else if (error_txt == "BADTIME") {
-        error = 18;
-    } else {
-        error = tokenToNum<int32_t, 16>(error_txt);
-    }
-
-    const int32_t otherlen = tokenToNum<int32_t, 16>(getToken(iss, tsig_str));
-    const string otherdata_txt = (otherlen > 0) ? getToken(iss, tsig_str) : "";
-    vector<uint8_t> other_data;
-    decodeBase64(otherdata_txt, other_data);
-
-    if (!iss.eof()) {
-        isc_throw(InvalidRdataText, "Unexpected input for TSIG RDATA: " <<
-                  tsig_str);
+    try {
+        const Name algorithm(getToken(iss));
+        const int64_t time_signed = tokenToNum<int64_t, 48>(getToken(iss));
+        const int32_t fudge = tokenToNum<int32_t, 16>(getToken(iss));
+        const int32_t macsize = tokenToNum<int32_t, 16>(getToken(iss));
+
+        const string mac_txt = (macsize > 0) ? getToken(iss) : "";
+        vector<uint8_t> mac;
+        decodeBase64(mac_txt, mac);
+        if (mac.size() != macsize) {
+            isc_throw(InvalidRdataText, "TSIG MAC size and data are inconsistent");
+        }
+
+        const int32_t orig_id = tokenToNum<int32_t, 16>(getToken(iss));
+
+        const string error_txt = getToken(iss);
+        int32_t error = 0;
+        // XXX: In the initial implementation we hardcode the mnemonics.
+        // We'll soon generalize this.
+        if (error_txt == "BADSIG") {
+            error = 16;
+        } else if (error_txt == "BADKEY") {
+            error = 17;
+        } else if (error_txt == "BADTIME") {
+            error = 18;
+        } else {
+            error = tokenToNum<int32_t, 16>(error_txt);
+        }
+
+        const int32_t otherlen = tokenToNum<int32_t, 16>(getToken(iss));
+        const string otherdata_txt = (otherlen > 0) ? getToken(iss) : "";
+        vector<uint8_t> other_data;
+        decodeBase64(otherdata_txt, other_data);
+
+        if (!iss.eof()) {
+            isc_throw(InvalidRdataText, "Unexpected input for TSIG RDATA: " <<
+                    tsig_str);
+        }
+
+        impl_ = new TSIGImpl(algorithm, time_signed, fudge, mac, orig_id,
+                            error, other_data);
+
+    } catch (const StringTokenError& ste) {
+        isc_throw(InvalidRdataText, "Invalid TSIG text: " << ste.what() <<
+                  ": " << tsig_str);
     }
-
-    impl_ = new TSIGImpl(algorithm, time_signed, fudge, mac, orig_id,
-                         error, other_data);
 }
 
 /// \brief Constructor from wire-format data.

+ 245 - 0
src/lib/dns/rdata/in_1/srv_33.cc

@@ -0,0 +1,245 @@
+// Copyright (C) 2011  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.
+
+#include <iostream>
+#include <sstream>
+
+#include <boost/lexical_cast.hpp>
+
+#include <util/buffer.h>
+#include <util/strutil.h>
+
+#include <dns/messagerenderer.h>
+#include <dns/name.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+
+using namespace std;
+using namespace isc::util;
+using namespace isc::util::str;
+
+// BEGIN_ISC_NAMESPACE
+// BEGIN_RDATA_NAMESPACE
+
+struct SRVImpl {
+    // straightforward representation of SRV RDATA fields
+    SRVImpl(uint16_t priority, uint16_t weight, uint16_t port,
+           const Name& target) :
+        priority_(priority), weight_(weight), port_(port),
+        target_(target)
+    {}
+
+    uint16_t priority_;
+    uint16_t weight_;
+    uint16_t port_;
+    Name target_;
+};
+
+/// \brief Constructor from string.
+///
+/// \c srv_str must be formatted as follows:
+/// \code <Priority> <Weight> <Port> <Target>
+/// \endcode
+/// where
+/// - <Priority>, <Weight>, and <Port> are an unsigned 16-bit decimal
+///   integer.
+/// - <Target> is a valid textual representation of domain name.
+///
+/// An example of valid string is:
+/// \code "1 5 1500 example.com." \endcode
+///
+/// <b>Exceptions</b>
+///
+/// If <Target> is not a valid domain name, a corresponding exception from
+/// the \c Name class will be thrown;
+/// if %any of the other bullet points above is not met, an exception of
+/// class \c InvalidRdataText will be thrown.
+/// This constructor internally involves resource allocation, and if it fails
+/// a corresponding standard exception will be thrown.
+SRV::SRV(const string& srv_str) :
+    impl_(NULL)
+{
+    istringstream iss(srv_str);
+
+    try {
+        const int32_t priority = tokenToNum<int32_t, 16>(getToken(iss));
+        const int32_t weight = tokenToNum<int32_t, 16>(getToken(iss));
+        const int32_t port = tokenToNum<int32_t, 16>(getToken(iss));
+        const Name targetname(getToken(iss));
+
+        if (!iss.eof()) {
+            isc_throw(InvalidRdataText, "Unexpected input for SRV RDATA: " <<
+                    srv_str);
+        }
+
+        impl_ = new SRVImpl(priority, weight, port, targetname);
+    } catch (const StringTokenError& ste) {
+        isc_throw(InvalidRdataText, "Invalid SRV text: " <<
+                  ste.what() << ": " << srv_str);
+    }
+}
+
+/// \brief Constructor from wire-format data.
+///
+/// When a read operation on \c buffer fails (e.g., due to a corrupted
+/// message) a corresponding exception from the \c InputBuffer class will
+/// be thrown.
+/// If the wire-format data does not end with a valid domain name,
+/// a corresponding exception from the \c Name class will be thrown.
+/// In addition, this constructor internally involves resource allocation,
+/// and if it fails a corresponding standard exception will be thrown.
+///
+/// According to RFC2782, the Target field must be a non compressed form
+/// of domain name.  But this implementation accepts a %SRV RR even if that
+/// field is compressed as suggested in RFC3597.
+///
+/// \param buffer A buffer storing the wire format data.
+/// \param rdata_len The length of the RDATA in bytes, normally expected
+/// to be the value of the RDLENGTH field of the corresponding RR.
+SRV::SRV(InputBuffer& buffer, size_t rdata_len) {
+    if (rdata_len < 6) {
+        isc_throw(InvalidRdataLength, "SRV too short");
+    }
+
+    uint16_t priority = buffer.readUint16();
+    uint16_t weight = buffer.readUint16();
+    uint16_t port = buffer.readUint16();
+    const Name targetname(buffer);
+
+    impl_ = new SRVImpl(priority, weight, port, targetname);
+}
+
+/// \brief The copy constructor.
+///
+/// It internally allocates a resource, and if it fails a corresponding
+/// standard exception will be thrown.
+/// This constructor never throws an exception otherwise.
+SRV::SRV(const SRV& source) :
+    Rdata(), impl_(new SRVImpl(*source.impl_))
+{}
+
+SRV&
+SRV::operator=(const SRV& source) {
+    if (impl_ == source.impl_) {
+        return (*this);
+    }
+
+    SRVImpl* newimpl = new SRVImpl(*source.impl_);
+    delete impl_;
+    impl_ = newimpl;
+
+    return (*this);
+}
+
+SRV::~SRV() {
+    delete impl_;
+}
+
+/// \brief Convert the \c SRV to a string.
+///
+/// The output of this method is formatted as described in the "from string"
+/// constructor (\c SRV(const std::string&))).
+///
+/// If internal resource allocation fails, a corresponding
+/// standard exception will be thrown.
+///
+/// \return A \c string object that represents the \c SRV object.
+string
+SRV::toText() const {
+    using namespace boost;
+    return (lexical_cast<string>(impl_->priority_) +
+        " " + lexical_cast<string>(impl_->weight_) +
+        " " + lexical_cast<string>(impl_->port_) +
+        " " + impl_->target_.toText());
+}
+
+/// \brief Render the \c SRV in the wire format without name compression.
+///
+/// If internal resource allocation fails, a corresponding
+/// standard exception will be thrown.
+/// This method never throws an exception otherwise.
+///
+/// \param buffer An output buffer to store the wire data.
+void
+SRV::toWire(OutputBuffer& buffer) const {
+    buffer.writeUint16(impl_->priority_);
+    buffer.writeUint16(impl_->weight_);
+    buffer.writeUint16(impl_->port_);
+    impl_->target_.toWire(buffer);
+}
+
+/// \brief Render the \c SRV in the wire format with taking into account
+/// compression.
+///
+/// As specified in RFC2782, the Target field (a domain name) will not be
+/// compressed.  However, the domain name could be a target of compression
+/// of other compressible names (though pretty unlikely), the offset
+/// information of the algorithm name may be recorded in \c renderer.
+///
+/// If internal resource allocation fails, a corresponding
+/// standard exception will be thrown.
+/// This method never throws an exception otherwise.
+///
+/// \param renderer DNS message rendering context that encapsulates the
+/// output buffer and name compression information.
+void
+SRV::toWire(AbstractMessageRenderer& renderer) const {
+    renderer.writeUint16(impl_->priority_);
+    renderer.writeUint16(impl_->weight_);
+    renderer.writeUint16(impl_->port_);
+    renderer.writeName(impl_->target_, false);
+}
+
+/// \brief Compare two instances of \c SRV RDATA.
+///
+/// See documentation in \c Rdata.
+int
+SRV::compare(const Rdata& other) const {
+    const SRV& other_srv = dynamic_cast<const SRV&>(other);
+
+    if (impl_->priority_ != other_srv.impl_->priority_) {
+        return (impl_->priority_ < other_srv.impl_->priority_ ? -1 : 1);
+    }
+    if (impl_->weight_ != other_srv.impl_->weight_) {
+        return (impl_->weight_ < other_srv.impl_->weight_ ? -1 : 1);
+    }
+    if (impl_->port_ != other_srv.impl_->port_) {
+        return (impl_->port_ < other_srv.impl_->port_ ? -1 : 1);
+    }
+
+    return (compareNames(impl_->target_, other_srv.impl_->target_));
+}
+
+uint16_t
+SRV::getPriority() const {
+    return (impl_->priority_);
+}
+
+uint16_t
+SRV::getWeight() const {
+    return (impl_->weight_);
+}
+
+uint16_t
+SRV::getPort() const {
+    return (impl_->port_);
+}
+
+const Name&
+SRV::getTarget() const {
+    return (impl_->target_);
+}
+
+// END_RDATA_NAMESPACE
+// END_ISC_NAMESPACE

+ 93 - 0
src/lib/dns/rdata/in_1/srv_33.h

@@ -0,0 +1,93 @@
+// Copyright (C) 2011  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.
+
+#include <stdint.h>
+
+#include <dns/name.h>
+#include <dns/rdata.h>
+
+// BEGIN_HEADER_GUARD
+
+// BEGIN_ISC_NAMESPACE
+
+// BEGIN_COMMON_DECLARATIONS
+// END_COMMON_DECLARATIONS
+
+// BEGIN_RDATA_NAMESPACE
+
+struct SRVImpl;
+
+/// \brief \c rdata::SRV class represents the SRV RDATA as defined %in
+/// RFC2782.
+///
+/// This class implements the basic interfaces inherited from the abstract
+/// \c rdata::Rdata class, and provides trivial accessors specific to the
+/// SRV RDATA.
+class SRV : public Rdata {
+public:
+    // BEGIN_COMMON_MEMBERS
+    // END_COMMON_MEMBERS
+
+    /// \brief Assignment operator.
+    ///
+    /// It internally allocates a resource, and if it fails a corresponding
+    /// standard exception will be thrown.
+    /// This operator never throws an exception otherwise.
+    ///
+    /// This operator provides the strong exception guarantee: When an
+    /// exception is thrown the content of the assignment target will be
+    /// intact.
+    SRV& operator=(const SRV& source);
+
+    /// \brief The destructor.
+    ~SRV();
+
+    ///
+    /// Specialized methods
+    ///
+
+    /// \brief Return the value of the priority field.
+    ///
+    /// This method never throws an exception.
+    uint16_t getPriority() const;
+
+    /// \brief Return the value of the weight field.
+    ///
+    /// This method never throws an exception.
+    uint16_t getWeight() const;
+
+    /// \brief Return the value of the port field.
+    ///
+    /// This method never throws an exception.
+    uint16_t getPort() const;
+
+    /// \brief Return the value of the target field.
+    ///
+    /// \return A reference to a \c Name class object corresponding to the
+    /// internal target name.
+    ///
+    /// This method never throws an exception.
+    const Name& getTarget() const;
+
+private:
+    SRVImpl* impl_;
+};
+
+// END_RDATA_NAMESPACE
+// END_ISC_NAMESPACE
+// END_HEADER_GUARD
+
+// Local Variables:
+// mode: c++
+// End:

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

@@ -41,6 +41,7 @@ run_unittests_SOURCES += rdata_nsecbitmap_unittest.cc
 run_unittests_SOURCES += rdata_nsec3param_unittest.cc
 run_unittests_SOURCES += rdata_rrsig_unittest.cc
 run_unittests_SOURCES += rdata_rp_unittest.cc
+run_unittests_SOURCES += rdata_srv_unittest.cc
 run_unittests_SOURCES += rdata_tsig_unittest.cc
 run_unittests_SOURCES += rrset_unittest.cc rrsetlist_unittest.cc
 run_unittests_SOURCES += question_unittest.cc

+ 173 - 0
src/lib/dns/tests/rdata_srv_unittest.cc

@@ -0,0 +1,173 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for generic
+// 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.
+
+#include <util/buffer.h>
+#include <dns/exceptions.h>
+#include <dns/messagerenderer.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+
+#include <gtest/gtest.h>
+
+#include <dns/tests/unittest_util.h>
+#include <dns/tests/rdata_unittest.h>
+
+using isc::UnitTestUtil;
+using namespace std;
+using namespace isc::dns;
+using namespace isc::util;
+using namespace isc::dns::rdata;
+
+namespace {
+class Rdata_SRV_Test : public RdataTest {
+    // there's nothing to specialize
+};
+
+string srv_txt("1 5 1500 a.example.com.");
+string srv_txt2("1 5 1400 example.com.");
+string too_long_label("012345678901234567890123456789"
+    "0123456789012345678901234567890123");
+
+// 1 5 1500 a.example.com.
+const uint8_t wiredata_srv[] = {
+    0x00, 0x01, 0x00, 0x05, 0x05, 0xdc, 0x01, 0x61, 0x07, 0x65, 0x78,
+    0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00};
+// 1 5 1400 example.com.
+const uint8_t wiredata_srv2[] = {
+    0x00, 0x01, 0x00, 0x05, 0x05, 0x78, 0x07, 0x65, 0x78, 0x61, 0x6d,
+    0x70, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00};
+
+const in::SRV rdata_srv(srv_txt);
+const in::SRV rdata_srv2(srv_txt2);
+
+TEST_F(Rdata_SRV_Test, createFromText) {
+    EXPECT_EQ(1, rdata_srv.getPriority());
+    EXPECT_EQ(5, rdata_srv.getWeight());
+    EXPECT_EQ(1500, rdata_srv.getPort());
+    EXPECT_EQ(Name("a.example.com."), rdata_srv.getTarget());
+}
+
+TEST_F(Rdata_SRV_Test, badText) {
+    // priority is too large (2814...6 is 2^48)
+    EXPECT_THROW(in::SRV("281474976710656 5 1500 a.example.com."),
+                 InvalidRdataText);
+    // weight is too large
+    EXPECT_THROW(in::SRV("1 281474976710656 1500 a.example.com."),
+                 InvalidRdataText);
+    // port is too large
+    EXPECT_THROW(in::SRV("1 5 281474976710656 a.example.com."),
+                 InvalidRdataText);
+    // incomplete text
+    EXPECT_THROW(in::SRV("1 5 a.example.com."),
+                 InvalidRdataText);
+    EXPECT_THROW(in::SRV("1 5 1500a.example.com."),
+                 InvalidRdataText);
+    // bad name
+    EXPECT_THROW(in::SRV("1 5 1500 a.example.com." + too_long_label),
+                 TooLongLabel);
+}
+
+TEST_F(Rdata_SRV_Test, assignment) {
+    in::SRV copy((string(srv_txt2)));
+    copy = rdata_srv;
+    EXPECT_EQ(0, copy.compare(rdata_srv));
+
+    // Check if the copied data is valid even after the original is deleted
+    in::SRV* copy2 = new in::SRV(rdata_srv);
+    in::SRV copy3((string(srv_txt2)));
+    copy3 = *copy2;
+    delete copy2;
+    EXPECT_EQ(0, copy3.compare(rdata_srv));
+
+    // Self assignment
+    copy = copy;
+    EXPECT_EQ(0, copy.compare(rdata_srv));
+}
+
+TEST_F(Rdata_SRV_Test, createFromWire) {
+    EXPECT_EQ(0, rdata_srv.compare(
+                  *rdataFactoryFromFile(RRType("SRV"), RRClass("IN"),
+                                        "rdata_srv_fromWire")));
+    // RDLENGTH is too short
+    EXPECT_THROW(rdataFactoryFromFile(RRType("SRV"), RRClass("IN"),
+                                      "rdata_srv_fromWire", 23),
+                 InvalidRdataLength);
+    // RDLENGTH is too long
+    EXPECT_THROW(rdataFactoryFromFile(RRType("SRV"), RRClass("IN"),
+                                      "rdata_srv_fromWire", 46),
+                 InvalidRdataLength);
+    // incomplete name.  the error should be detected in the name constructor
+    EXPECT_THROW(rdataFactoryFromFile(RRType("SRV"), RRClass("IN"),
+                                      "rdata_cname_fromWire", 69),
+                 DNSMessageFORMERR);
+    // parse compressed target name
+    EXPECT_EQ(0, rdata_srv.compare(
+                  *rdataFactoryFromFile(RRType("SRV"), RRClass("IN"),
+                                      "rdata_srv_fromWire", 89)));
+}
+
+TEST_F(Rdata_SRV_Test, toWireBuffer) {
+    rdata_srv.toWire(obuffer);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+                        obuffer.getData(), obuffer.getLength(),
+                        wiredata_srv, sizeof(wiredata_srv));
+    obuffer.clear();
+    rdata_srv2.toWire(obuffer);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+                        obuffer.getData(), obuffer.getLength(),
+                        wiredata_srv2, sizeof(wiredata_srv2));
+}
+
+TEST_F(Rdata_SRV_Test, toWireRenderer) {
+    rdata_srv.toWire(renderer);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+                        obuffer.getData(), obuffer.getLength(),
+                        wiredata_srv, sizeof(wiredata_srv));
+    renderer.clear();
+    rdata_srv2.toWire(renderer);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+                        obuffer.getData(), obuffer.getLength(),
+                        wiredata_srv2, sizeof(wiredata_srv2));
+}
+
+TEST_F(Rdata_SRV_Test, toText) {
+    EXPECT_EQ(srv_txt, rdata_srv.toText());
+    EXPECT_EQ(srv_txt2, rdata_srv2.toText());
+}
+
+TEST_F(Rdata_SRV_Test, compare) {
+    // test RDATAs, sorted in the ascendent order.
+    vector<in::SRV> compare_set;
+    compare_set.push_back(in::SRV("1 5 1500 a.example.com."));
+    compare_set.push_back(in::SRV("2 5 1500 a.example.com."));
+    compare_set.push_back(in::SRV("2 6 1500 a.example.com."));
+    compare_set.push_back(in::SRV("2 6 1600 a.example.com."));
+    compare_set.push_back(in::SRV("2 6 1600 example.com."));
+
+    EXPECT_EQ(0, compare_set[0].compare(
+                  in::SRV("1 5 1500 a.example.com.")));
+
+    vector<in::SRV>::const_iterator it;
+    vector<in::SRV>::const_iterator it_end = compare_set.end();
+    for (it = compare_set.begin(); it != it_end - 1; ++it) {
+        EXPECT_GT(0, (*it).compare(*(it + 1)));
+        EXPECT_LT(0, (*(it + 1)).compare(*it));
+    }
+
+    // comparison attempt between incompatible RR types should be rejected
+    EXPECT_THROW(rdata_srv.compare(*RdataTest::rdata_nomatch), bad_cast);
+}
+}

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

@@ -101,6 +101,7 @@ EXTRA_DIST += rdata_rp_fromWire3.spec rdata_rp_fromWire4.spec
 EXTRA_DIST += rdata_rp_fromWire5.spec rdata_rp_fromWire6.spec
 EXTRA_DIST += rdata_rp_toWire1.spec rdata_rp_toWire2.spec
 EXTRA_DIST += rdata_soa_fromWire rdata_soa_toWireUncompressed.spec
+EXTRA_DIST += rdata_srv_fromWire
 EXTRA_DIST += rdata_txt_fromWire1 rdata_txt_fromWire2.spec
 EXTRA_DIST += rdata_txt_fromWire3.spec rdata_txt_fromWire4.spec
 EXTRA_DIST += rdata_txt_fromWire5.spec rdata_unknown_fromWire

+ 36 - 0
src/lib/dns/tests/testdata/rdata_srv_fromWire

@@ -0,0 +1,36 @@
+#
+# various kinds of SRV RDATA stored in an input buffer
+#
+# RDLENGHT=21 bytes
+# 0  1
+ 00 15
+# 2  3  4  5  6  7  8  9 10  1  2  3  4  5  6  7  8  9 20  1  2(bytes)
+ 00 01 00 05 05 dc 01 61 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+#
+# short length
+# 3  4
+ 00 12
+# 5  6  7  8  9 30  1  2  3  4  5  6  7  8  9 40  1  2  3  4  5
+ 00 01 00 05 05 dc 01 61 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+#
+# length too long
+# 6  7
+ 00 19
+#
+# 8  9 50  1  2  3  4  5  6  7  8  9 60  1  2  3  4  5  6  7  8
+ 00 01 00 05 05 dc 01 61 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+#
+#
+# incomplete target name
+# 9 70
+ 00 12
+# 1  2  3  4  5  6  7  8  9 80  1  2  3  4  5  6  7  8
+ 00 01 00 05 05 dc 01 61 07 65 78 61 6d 70 6c 65 03 63
+#
+#
+# Valid compressed target name: 'a' + pointer
+# 9 90
+ 00 0a
+#
+# 1  2  3  4  5  6  7  8  9 100
+ 00 01 00 05 05 dc 01 61 c0 0a

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

@@ -1,4 +1,4 @@
-SUBDIRS = datasrc cc config log net notify util testutils acl
+SUBDIRS = datasrc cc config log net notify util testutils acl bind10
 
 python_PYTHON = __init__.py
 

+ 4 - 0
src/lib/python/isc/bind10/Makefile.am

@@ -0,0 +1,4 @@
+SUBDIRS = . tests
+
+python_PYTHON = __init__.py sockcreator.py
+pythondir = $(pyexecdir)/isc/bind10

src/bin/bind10/__init__.py → src/lib/python/isc/bind10/__init__.py


src/bin/bind10/sockcreator.py → src/lib/python/isc/bind10/sockcreator.py


+ 29 - 0
src/lib/python/isc/bind10/tests/Makefile.am

@@ -0,0 +1,29 @@
+PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
+#PYTESTS = args_test.py bind10_test.py
+# NOTE: this has a generated test found in the builddir
+PYTESTS = sockcreator_test.py
+
+EXTRA_DIST = $(PYTESTS)
+
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+LIBRARY_PATH_PLACEHOLDER =
+if SET_ENV_LIBRARY_PATH
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$$$(ENV_LIBRARY_PATH)
+endif
+
+# test using command-line arguments, so use check-local target instead of TESTS
+check-local:
+if ENABLE_PYTHON_COVERAGE
+	touch $(abs_top_srcdir)/.coverage 
+	rm -f .coverage
+	${LN_S} $(abs_top_srcdir)/.coverage .coverage
+endif
+	for pytest in $(PYTESTS) ; do \
+	echo Running test: $$pytest ; \
+	$(LIBRARY_PATH_PLACEHOLDER) \
+	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_srcdir)/src/bin:$(abs_top_builddir)/src/bin/bind10:$(abs_top_builddir)/src/lib/util/io/.libs \
+	BIND10_MSGQ_SOCKET_FILE=$(abs_top_builddir)/msgq_socket \
+		$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
+	done
+

+ 1 - 1
src/bin/bind10/tests/sockcreator_test.py.in

@@ -26,7 +26,7 @@ import socket
 from isc.net.addr import IPAddr
 import isc.log
 from libutil_io_python import send_fd
-from bind10.sockcreator import Parser, CreatorError, WrappedSocket
+from isc.bind10.sockcreator import Parser, CreatorError, WrappedSocket
 
 class FakeCreator:
     """

+ 2 - 2
src/lib/python/isc/config/ccsession.py

@@ -425,7 +425,7 @@ class UIModuleCCSession(MultiConfigData):
             raise ModuleCCSessionError("Bad config version")
         self._set_current_config(config)
 
-    def _add_value_to_list(self, identifier, value):
+    def _add_value_to_list(self, identifier, value, module_spec):
         cur_list, status = self.get_value(identifier)
         if not cur_list:
             cur_list = []
@@ -491,7 +491,7 @@ class UIModuleCCSession(MultiConfigData):
                 if set_value_str is not None:
                     value_str += set_value_str
                 value = isc.cc.data.parse_value_str(value_str)
-            self._add_value_to_list(identifier, value)
+            self._add_value_to_list(identifier, value, module_spec)
         elif 'named_set_item_spec' in module_spec:
             item_name = None
             item_value = None

+ 2 - 0
src/lib/python/isc/config/tests/ccsession_test.py

@@ -745,6 +745,8 @@ class TestUIModuleCCSession(unittest.TestCase):
                           uccs.remove_value, "Spec2/item5[123]", None)
         uccs.remove_value("Spec2/item5[0]", None)
         self.assertEqual({'Spec2': {'item5': []}}, uccs._local_changes)
+        uccs.add_value("Spec2/item5", None);
+        self.assertEqual({'Spec2': {'item5': ['']}}, uccs._local_changes)
 
     def test_add_remove_value_named_set(self):
         fake_conn = fakeUIConn()

+ 1 - 1
src/lib/python/isc/notify/notify_out_messages.mes

@@ -78,6 +78,6 @@ message, either in the message parser, or while trying to extract data
 from the parsed message. The error is printed, and notify_out will
 treat the response as a bad message, but this does point to a
 programming error, since all exceptions should have been caught
-explicitely. Please file a bug report. Since there was a response,
+explicitly. Please file a bug report. Since there was a response,
 no more notifies will be sent to this server for this notification
 event.

+ 1 - 0
src/lib/resolve/tests/Makefile.am

@@ -31,6 +31,7 @@ run_unittests_LDADD +=  $(top_builddir)/src/lib/asiolink/libasiolink.la
 run_unittests_LDADD +=  $(top_builddir)/src/lib/asiodns/libasiodns.la
 run_unittests_LDADD +=  $(top_builddir)/src/lib/resolve/libresolve.la
 run_unittests_LDADD +=  $(top_builddir)/src/lib/dns/libdns++.la
+run_unittests_LDADD +=  $(top_builddir)/src/lib/util/libutil.la
 run_unittests_LDADD +=  $(top_builddir)/src/lib/log/liblog.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
 run_unittests_LDADD +=  $(top_builddir)/src/lib/exceptions/libexceptions.la

+ 11 - 0
src/lib/util/strutil.cc

@@ -132,6 +132,17 @@ format(const std::string& format, const std::vector<std::string>& args) {
     return (result);
 }
 
+std::string
+getToken(std::istringstream& iss) {
+    string token;
+    iss >> token;
+    if (iss.bad() || iss.fail()) {
+        isc_throw(StringTokenError, "could not read token from string");
+    }
+    return (token);
+}
+
+
 } // namespace str
 } // namespace util
 } // namespace isc

+ 62 - 0
src/lib/util/strutil.h

@@ -18,7 +18,10 @@
 #include <algorithm>
 #include <cctype>
 #include <string>
+#include <sstream>
 #include <vector>
+#include <exceptions/exceptions.h>
+#include <boost/lexical_cast.hpp>
 
 namespace isc {
 namespace util {
@@ -26,6 +29,16 @@ namespace str {
 
 /// \brief A Set of C++ Utilities for Manipulating Strings
 
+///
+/// \brief A standard string util exception that is thrown if getToken or
+/// numToToken are called with bad input data
+///
+class StringTokenError : public Exception {
+public:
+    StringTokenError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
 /// \brief Normalize Backslash
 ///
 /// Only relevant to Windows, this replaces all "\" in a string with "/" and
@@ -140,6 +153,55 @@ std::string format(const std::string& format,
     const std::vector<std::string>& args);
 
 
+/// \brief Returns one token from the given stringstream
+///
+/// Using the >> operator, with basic error checking
+///
+/// \exception StringTokenError if the token cannot be read from the stream
+///
+/// \param iss stringstream to read one token from
+///
+/// \return the first token read from the stringstream
+std::string getToken(std::istringstream& iss);
+
+/// \brief Converts a string token to an *unsigned* integer.
+///
+/// The value is converted using a lexical cast, with error and bounds
+/// checking.
+///
+/// NumType is a *signed* integral type (e.g. int32_t) that is sufficiently
+/// wide to store resulting integers.
+///
+/// BitSize is the maximum number of bits that the resulting integer can take.
+/// This function first checks whether the given token can be converted to
+/// an integer of NumType type.  It then confirms the conversion result is
+/// within the valid range, i.e., [0, 2^BitSize - 1].  The second check is
+/// necessary because lexical_cast<T> where T is an unsigned integer type
+/// doesn't correctly reject negative numbers when compiled with SunStudio.
+///
+/// \exception StringTokenError if the value is out of range, or if it
+///            could not be converted
+///
+/// \param num_token the string token to convert
+///
+/// \return the converted value, of type NumType
+template <typename NumType, int BitSize>
+NumType
+tokenToNum(const std::string& num_token) {
+    NumType num;
+    try {
+        num = boost::lexical_cast<NumType>(num_token);
+    } catch (const boost::bad_lexical_cast& ex) {
+        isc_throw(StringTokenError, "Invalid SRV numeric parameter: " <<
+                  num_token);
+    }
+    if (num < 0 || num >= (static_cast<NumType>(1) << BitSize)) {
+        isc_throw(StringTokenError, "Numeric SRV parameter out of range: " <<
+                  num);
+    }
+    return (num);
+}
+
 } // namespace str
 } // namespace util
 } // namespace isc

+ 67 - 13
src/lib/util/tests/strutil_unittest.cc

@@ -12,6 +12,8 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <stdint.h>
+
 #include <string>
 
 #include <gtest/gtest.h>
@@ -22,17 +24,9 @@ using namespace isc;
 using namespace isc::util;
 using namespace std;
 
-class StringUtilTest : public ::testing::Test {
-protected:
-    StringUtilTest()
-    {
-    }
-};
-
-
 // Check for slash replacement
 
-TEST_F(StringUtilTest, Slash) {
+TEST(StringUtilTest, Slash) {
 
     string instring = "";
     isc::util::str::normalizeSlash(instring);
@@ -49,7 +43,7 @@ TEST_F(StringUtilTest, Slash) {
 
 // Check that leading and trailing space trimming works
 
-TEST_F(StringUtilTest, Trim) {
+TEST(StringUtilTest, Trim) {
 
     // Empty and full string.
     EXPECT_EQ("", isc::util::str::trim(""));
@@ -71,7 +65,7 @@ TEST_F(StringUtilTest, Trim) {
 // returned vector; if not as expected, the following references may be invalid
 // so should not be used.
 
-TEST_F(StringUtilTest, Tokens) {
+TEST(StringUtilTest, Tokens) {
     vector<string>  result;
 
     // Default delimiters
@@ -157,7 +151,7 @@ TEST_F(StringUtilTest, Tokens) {
 
 // Changing case
 
-TEST_F(StringUtilTest, ChangeCase) {
+TEST(StringUtilTest, ChangeCase) {
     string mixed("abcDEFghiJKLmno123[]{=+--+]}");
     string upper("ABCDEFGHIJKLMNO123[]{=+--+]}");
     string lower("abcdefghijklmno123[]{=+--+]}");
@@ -173,7 +167,7 @@ TEST_F(StringUtilTest, ChangeCase) {
 
 // Formatting
 
-TEST_F(StringUtilTest, Formatting) {
+TEST(StringUtilTest, Formatting) {
 
     vector<string> args;
     args.push_back("arg1");
@@ -213,3 +207,63 @@ TEST_F(StringUtilTest, Formatting) {
     string format9 = "%s %s";
     EXPECT_EQ(format9, isc::util::str::format(format9, args));
 }
+
+TEST(StringUtilTest, getToken) {
+    string s("a b c");
+    istringstream ss(s);
+    EXPECT_EQ("a", isc::util::str::getToken(ss));
+    EXPECT_EQ("b", isc::util::str::getToken(ss));
+    EXPECT_EQ("c", isc::util::str::getToken(ss));
+    EXPECT_THROW(isc::util::str::getToken(ss), isc::util::str::StringTokenError);
+}
+
+int32_t tokenToNumCall_32_16(const string& token) {
+    return isc::util::str::tokenToNum<int32_t, 16>(token);
+}
+
+int16_t tokenToNumCall_16_8(const string& token) {
+    return isc::util::str::tokenToNum<int16_t, 8>(token);
+}
+
+TEST(StringUtilTest, tokenToNum) {
+    uint32_t num32 = tokenToNumCall_32_16("0");
+    EXPECT_EQ(0, num32);
+    num32 = tokenToNumCall_32_16("123");
+    EXPECT_EQ(123, num32);
+    num32 = tokenToNumCall_32_16("65535");
+    EXPECT_EQ(65535, num32);
+
+    EXPECT_THROW(tokenToNumCall_32_16(""),
+                 isc::util::str::StringTokenError);
+    EXPECT_THROW(tokenToNumCall_32_16("a"),
+                 isc::util::str::StringTokenError);
+    EXPECT_THROW(tokenToNumCall_32_16("-1"),
+                 isc::util::str::StringTokenError);
+    EXPECT_THROW(tokenToNumCall_32_16("65536"),
+                 isc::util::str::StringTokenError);
+    EXPECT_THROW(tokenToNumCall_32_16("1234567890"),
+                 isc::util::str::StringTokenError);
+    EXPECT_THROW(tokenToNumCall_32_16("-1234567890"),
+                 isc::util::str::StringTokenError);
+
+    uint16_t num16 = tokenToNumCall_16_8("123");
+    EXPECT_EQ(123, num16);
+    num16 = tokenToNumCall_16_8("0");
+    EXPECT_EQ(0, num16);
+    num16 = tokenToNumCall_16_8("255");
+    EXPECT_EQ(255, num16);
+
+    EXPECT_THROW(tokenToNumCall_16_8(""),
+                 isc::util::str::StringTokenError);
+    EXPECT_THROW(tokenToNumCall_16_8("a"),
+                 isc::util::str::StringTokenError);
+    EXPECT_THROW(tokenToNumCall_16_8("-1"),
+                 isc::util::str::StringTokenError);
+    EXPECT_THROW(tokenToNumCall_16_8("256"),
+                 isc::util::str::StringTokenError);
+    EXPECT_THROW(tokenToNumCall_16_8("1234567890"),
+                 isc::util::str::StringTokenError);
+    EXPECT_THROW(tokenToNumCall_16_8("-1234567890"),
+                 isc::util::str::StringTokenError);
+
+}