BIND 10 trac826, updated. 330ee238b6e3132d721e6c1130269990aa7a84ef [trac826] log_* and acl updates

BIND 10 source code commits bind10-changes at lists.isc.org
Thu Sep 29 03:17:07 UTC 2011


The branch, trac826 has been updated
       via  330ee238b6e3132d721e6c1130269990aa7a84ef (commit)
      from  18f7b4e77b33207a54aaa19616cad1468a41c5f0 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 330ee238b6e3132d721e6c1130269990aa7a84ef
Author: Francis Dupont <fdupont at isc.org>
Date:   Thu Sep 29 05:16:40 2011 +0200

    [trac826] log_* and acl updates
    
    lib/acl (including dnsacl and tests)
    lib/log/*
    
    not fully checked (nor git back syslog)

-----------------------------------------------------------------------

Summary of changes:
 src/lib/acl/Makefile.am                            |   27 +
 src/lib/acl/acl.h                                  |  143 +++++
 src/lib/acl/check.h                                |  195 ++++++
 src/lib/acl/dns.cc                                 |  139 +++++
 src/lib/acl/dns.h                                  |  157 +++++
 src/lib/acl/dnsname_check.h                        |   85 +++
 src/lib/acl/ip_check.cc                            |  143 +++++
 src/lib/acl/ip_check.h                             |  419 +++++++++++++
 src/lib/acl/loader.cc                              |   46 ++
 src/lib/acl/loader.h                               |  482 +++++++++++++++
 src/lib/acl/logic_check.h                          |  295 ++++++++++
 src/lib/acl/tests/Makefile.am                      |   40 ++
 src/lib/acl/tests/acl_test.cc                      |   90 +++
 src/lib/acl/tests/check_test.cc                    |   70 +++
 src/lib/acl/tests/creators.h                       |  163 +++++
 src/lib/acl/tests/dns_test.cc                      |  271 +++++++++
 src/lib/acl/tests/dnsname_check_unittest.cc        |   59 ++
 src/lib/acl/tests/ip_check_unittest.cc             |  619 ++++++++++++++++++++
 src/lib/acl/tests/loader_test.cc                   |  383 ++++++++++++
 src/lib/acl/tests/logcheck.h                       |   94 +++
 src/lib/acl/tests/logic_check_test.cc              |  291 +++++++++
 src/lib/acl/tests/run_unittests.cc                 |   24 +
 src/lib/acl/tests/sockaddr.h                       |   73 +++
 src/lib/dns/tsigrecord.cc                          |  147 +++++
 src/lib/dns/tsigrecord.h                           |  308 ++++++++++
 src/lib/log/compiler/Makefile.am                   |    8 +-
 src/lib/log/compiler/message.cc                    |  373 +++++++-----
 src/lib/log/logger_manager.cc                      |    2 +-
 src/lib/log/tests/Makefile.am                      |   64 ++-
 src/lib/log/tests/console_test.sh.in               |   67 +++
 src/lib/log/tests/console_test.sh.win32            |   67 +++
 src/lib/log/tests/destination_test.sh.in           |   91 +++
 src/lib/log/tests/destination_test.sh.win32        |   91 +++
 src/lib/log/tests/init_logger_test.cc              |   42 ++
 src/lib/log/tests/init_logger_test.sh.in           |  110 ++++
 src/lib/log/tests/init_logger_test.sh.win32        |  110 ++++
 src/lib/log/tests/local_file_test.sh.in            |   83 +++
 src/lib/log/tests/local_file_test.sh.win32         |   83 +++
 src/lib/log/tests/log_formatter_unittest.cc        |  126 ++++
 src/lib/log/tests/logger_example.cc                |  309 ++++++++++
 src/lib/log/tests/logger_impl_log4cxx_unittest.cc  |   91 ---
 src/lib/log/tests/logger_level_impl_unittest.cc    |  178 ++++++
 src/lib/log/tests/logger_level_unittest.cc         |   84 +++
 src/lib/log/tests/logger_manager_unittest.cc       |  329 +++++++++++
 src/lib/log/tests/logger_name_unittest.cc          |   77 +++
 src/lib/log/tests/logger_specification_unittest.cc |   96 +++
 src/lib/log/tests/logger_support_test.cc           |  108 ----
 src/lib/log/tests/logger_support_unittest.cc       |   83 +++
 src/lib/log/tests/logger_unittest.cc               |  113 ++--
 src/lib/log/tests/message_dictionary_unittest.cc   |    4 +-
 src/lib/log/tests/message_reader_unittest.cc       |   67 ++-
 src/lib/log/tests/output_option_unittest.cc        |   66 ++
 src/lib/log/tests/root_logger_name_unittest.cc     |   50 --
 src/lib/log/tests/run_time_init_test.sh.in         |   89 ---
 src/lib/log/tests/run_time_init_test.sh.win32      |   89 ---
 src/lib/log/tests/run_unittests.cc                 |    6 +-
 src/lib/log/tests/severity_test.sh.in              |   89 +++
 src/lib/log/tests/severity_test.sh.win32           |   89 +++
 src/lib/log/tests/tempdir.h.in                     |   29 +
 src/lib/log/tests/tempdir.h.win32                  |   29 +
 src/lib/log/tests/xdebuglevel_unittest.cc          |  203 -------
 win32build/VS2010/bind10.sln                       |   76 ++-
 win32build/VS2010/libacl/libacl.vcxproj            |   92 +++
 win32build/VS2010/libacl/libacl.vcxproj.filters    |   42 ++
 .../libacl.vcxproj.user}                           |    0 
 .../VS2010/libacl_tests/libacl_tests.vcxproj       |  106 ++++
 .../libacl_tests/libacl_tests.vcxproj.filters      |   54 ++
 .../libacl_tests.vcxproj.user}                     |    0 
 win32build/VS2010/libdnsacl/libdnsacl.vcxproj      |   88 +++
 .../VS2010/libdnsacl/libdnsacl.vcxproj.filters     |   30 +
 .../libdnsacl.vcxproj.user}                        |    0 
 win32build/VS2010/liblog/liblog.vcxproj            |    2 +-
 .../VS2010/liblog_compiler/liblog_compiler.vcxproj |   10 +-
 .../VS2010/liblog_example/liblog_example.vcxproj   |  114 ++++
 .../liblog_example/liblog_example.vcxproj.filters  |   30 +
 .../liblog_example.vcxproj.user}                   |    0 
 .../VS2010/liblog_iltest/liblog_iltest.vcxproj     |  104 ++++
 .../liblog_iltest/liblog_iltest.vcxproj.filters    |   22 +
 .../liblog_iltest.vcxproj.user}                    |    0 
 .../VS2010/liblog_rtitest/liblog_rtitest.vcxproj   |  114 ----
 .../liblog_rtitest/liblog_rtitest.vcxproj.filters  |   30 -
 .../VS2010/liblog_tests/liblog_tests.vcxproj       |   33 +-
 .../liblog_tests/liblog_tests.vcxproj.filters      |   25 +-
 83 files changed, 8274 insertions(+), 1056 deletions(-)
 create mode 100644 src/lib/acl/Makefile.am
 create mode 100644 src/lib/acl/acl.h
 create mode 100644 src/lib/acl/check.h
 create mode 100644 src/lib/acl/dns.cc
 create mode 100644 src/lib/acl/dns.h
 create mode 100644 src/lib/acl/dnsname_check.h
 create mode 100644 src/lib/acl/ip_check.cc
 create mode 100644 src/lib/acl/ip_check.h
 create mode 100644 src/lib/acl/loader.cc
 create mode 100644 src/lib/acl/loader.h
 create mode 100644 src/lib/acl/logic_check.h
 create mode 100644 src/lib/acl/tests/Makefile.am
 create mode 100644 src/lib/acl/tests/acl_test.cc
 create mode 100644 src/lib/acl/tests/check_test.cc
 create mode 100644 src/lib/acl/tests/creators.h
 create mode 100644 src/lib/acl/tests/dns_test.cc
 create mode 100644 src/lib/acl/tests/dnsname_check_unittest.cc
 create mode 100644 src/lib/acl/tests/ip_check_unittest.cc
 create mode 100644 src/lib/acl/tests/loader_test.cc
 create mode 100644 src/lib/acl/tests/logcheck.h
 create mode 100644 src/lib/acl/tests/logic_check_test.cc
 create mode 100644 src/lib/acl/tests/run_unittests.cc
 create mode 100644 src/lib/acl/tests/sockaddr.h
 create mode 100644 src/lib/dns/tsigrecord.cc
 create mode 100644 src/lib/dns/tsigrecord.h
 create mode 100755 src/lib/log/tests/console_test.sh.in
 create mode 100755 src/lib/log/tests/console_test.sh.win32
 create mode 100755 src/lib/log/tests/destination_test.sh.in
 create mode 100755 src/lib/log/tests/destination_test.sh.win32
 create mode 100644 src/lib/log/tests/init_logger_test.cc
 create mode 100755 src/lib/log/tests/init_logger_test.sh.in
 create mode 100755 src/lib/log/tests/init_logger_test.sh.win32
 create mode 100755 src/lib/log/tests/local_file_test.sh.in
 create mode 100755 src/lib/log/tests/local_file_test.sh.win32
 create mode 100644 src/lib/log/tests/log_formatter_unittest.cc
 create mode 100644 src/lib/log/tests/logger_example.cc
 delete mode 100644 src/lib/log/tests/logger_impl_log4cxx_unittest.cc
 create mode 100644 src/lib/log/tests/logger_level_impl_unittest.cc
 create mode 100644 src/lib/log/tests/logger_level_unittest.cc
 create mode 100644 src/lib/log/tests/logger_manager_unittest.cc
 create mode 100644 src/lib/log/tests/logger_name_unittest.cc
 create mode 100644 src/lib/log/tests/logger_specification_unittest.cc
 delete mode 100644 src/lib/log/tests/logger_support_test.cc
 create mode 100644 src/lib/log/tests/logger_support_unittest.cc
 create mode 100644 src/lib/log/tests/output_option_unittest.cc
 delete mode 100644 src/lib/log/tests/root_logger_name_unittest.cc
 delete mode 100755 src/lib/log/tests/run_time_init_test.sh.in
 delete mode 100755 src/lib/log/tests/run_time_init_test.sh.win32
 create mode 100755 src/lib/log/tests/severity_test.sh.in
 create mode 100755 src/lib/log/tests/severity_test.sh.win32
 create mode 100644 src/lib/log/tests/tempdir.h.in
 create mode 100644 src/lib/log/tests/tempdir.h.win32
 delete mode 100644 src/lib/log/tests/xdebuglevel_unittest.cc
 create mode 100755 win32build/VS2010/libacl/libacl.vcxproj
 create mode 100755 win32build/VS2010/libacl/libacl.vcxproj.filters
 rename win32build/VS2010/{liblog_rtitest/liblog_rtitest.vcxproj.user => libacl/libacl.vcxproj.user} (100%)
 create mode 100755 win32build/VS2010/libacl_tests/libacl_tests.vcxproj
 create mode 100755 win32build/VS2010/libacl_tests/libacl_tests.vcxproj.filters
 copy win32build/VS2010/{BINDInstall/BINDInstall.vcxproj.user => libacl_tests/libacl_tests.vcxproj.user} (100%)
 create mode 100755 win32build/VS2010/libdnsacl/libdnsacl.vcxproj
 create mode 100755 win32build/VS2010/libdnsacl/libdnsacl.vcxproj.filters
 copy win32build/VS2010/{BINDInstall/BINDInstall.vcxproj.user => libdnsacl/libdnsacl.vcxproj.user} (100%)
 create mode 100755 win32build/VS2010/liblog_example/liblog_example.vcxproj
 create mode 100755 win32build/VS2010/liblog_example/liblog_example.vcxproj.filters
 copy win32build/VS2010/{BINDInstall/BINDInstall.vcxproj.user => liblog_example/liblog_example.vcxproj.user} (100%)
 create mode 100755 win32build/VS2010/liblog_iltest/liblog_iltest.vcxproj
 create mode 100755 win32build/VS2010/liblog_iltest/liblog_iltest.vcxproj.filters
 copy win32build/VS2010/{BINDInstall/BINDInstall.vcxproj.user => liblog_iltest/liblog_iltest.vcxproj.user} (100%)
 delete mode 100755 win32build/VS2010/liblog_rtitest/liblog_rtitest.vcxproj
 delete mode 100755 win32build/VS2010/liblog_rtitest/liblog_rtitest.vcxproj.filters

-----------------------------------------------------------------------
diff --git a/src/lib/acl/Makefile.am b/src/lib/acl/Makefile.am
new file mode 100644
index 0000000..92b7869
--- /dev/null
+++ b/src/lib/acl/Makefile.am
@@ -0,0 +1,27 @@
+SUBDIRS = . tests
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+# The core library
+lib_LTLIBRARIES = libacl.la
+libacl_la_SOURCES  = acl.h
+libacl_la_SOURCES += check.h
+libacl_la_SOURCES += ip_check.h ip_check.cc
+libacl_la_SOURCES += logic_check.h
+libacl_la_SOURCES += loader.h loader.cc
+
+libacl_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
+libacl_la_LIBADD += $(top_builddir)/src/lib/cc/libcc.la
+libacl_la_LIBADD += $(top_builddir)/src/lib/util/libutil.la
+
+# DNS specialized one
+lib_LTLIBRARIES += libdnsacl.la
+
+libdnsacl_la_SOURCES = dns.h dns.cc dnsname_check.h
+
+libdnsacl_la_LIBADD = libacl.la
+libdnsacl_la_LIBADD += $(top_builddir)/src/lib/dns/libdns++.la
+
+CLEANFILES = *.gcno *.gcda
diff --git a/src/lib/acl/acl.h b/src/lib/acl/acl.h
new file mode 100644
index 0000000..76039c9
--- /dev/null
+++ b/src/lib/acl/acl.h
@@ -0,0 +1,143 @@
+// 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.
+
+#ifndef ACL_ACL_H
+#define ACL_ACL_H
+
+#include "check.h"
+#include <vector>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/noncopyable.hpp>
+
+namespace isc {
+namespace acl {
+
+/**
+ * \brief Default actions an ACL could perform.
+ *
+ * This is the default for the ACL class. It is possible to specify any other
+ * data type, as the ACL class does nothing about them, but these look
+ * reasonable, so they are provided for convenience. It is not specified what
+ * exactly these mean and it's up to whoever uses them.
+ */
+enum BasicAction {
+    ACCEPT,
+    REJECT,
+    DROP
+};
+
+/**
+ * \brief The ACL itself.
+ *
+ * It holds bunch of ordered entries, each one consisting of a check (
+ * of any kind, it might be even compound) and an action that is returned
+ * whenever the action matches. They are tested in the order and first
+ * match counts.
+ *
+ * This is non-copyable. It seems that there's no need to copy them (even
+ * when it would be technically possible), so we forbid it just to prevent
+ * copying it by accident. If there really is legitimate use, this restriction
+ * can be removed.
+ *
+ * The class is template. It is possible to specify on which context the checks
+ * match and which actions it returns. The actions must be copyable
+ * for this to work and it is expected to be something small, usually an enum
+ * (but other objects are also possible).
+ *
+ * \note There are protected functions. In fact, you should consider them
+ *     private, they are protected so tests can get inside. This class
+ *     is not expected to be subclassed in real applications.
+ */
+template<typename Context, typename Action = BasicAction> class ACL :
+    public boost::noncopyable {
+public:
+    /**
+     * \brief Constructor.
+     *
+     * \param default_action It is the action that is returned when the checked
+     *     things "falls off" the end of the list (when no rule matched).
+     */
+    ACL(const Action& default_action) : default_action_(default_action)
+    {}
+
+    /**
+     * \brief Pointer to the check.
+     *
+     * We use the shared pointer, because we are not able to copy the checks.
+     * However, we might need to copy the entries (when we concatenate ACLs
+     * together in future).
+     */
+    typedef boost::shared_ptr<const Check<Context> > ConstCheckPtr;
+
+    /**
+     * \brief The actual main function that decides.
+     *
+     * This is the function that takes the entries one by one, checks
+     * the context against conditions and if it matches, returns the
+     * action that belongs to the first matched entry or default action
+     * if nothing matches.
+     *
+     * \param context The thing that should be checked. It is directly
+     *     passed to the checks.
+     *
+     * \return The action for the ACL entry that first matches the context.
+     */
+    const Action& execute(const Context& context) const {
+        const typename Entries::const_iterator end(entries_.end());
+        for (typename Entries::const_iterator i(entries_.begin()); i != end;
+             ++i) {
+            if (i->first->matches(context)) {
+                return (i->second);
+            }
+        }
+        return (default_action_);
+    }
+
+    /**
+     * \brief Add new entry at the end of the list.
+     *
+     * \note We currently allow only adding at the end. This is enough for now,
+     * but we may need more when we start implementing some kind optimisations,
+     * including replacements, reorderings and removals.
+     *
+     * \param check The check to test if the thing matches.
+     * \param action The action to return when the thing matches this check.
+     */
+    void append(ConstCheckPtr check, const Action& action) {
+        entries_.push_back(Entry(check, action));
+    }
+private:
+    // Just type abbreviations.
+    typedef std::pair<ConstCheckPtr, Action> Entry;
+    typedef std::vector<Entry> Entries;
+    /// \brief The default action, when nothing mathes.
+    const Action default_action_;
+    /// \brief The entries we have.
+    Entries entries_;
+protected:
+    /**
+     * \brief Get the default action.
+     *
+     * This is for testing purposes only.
+     */
+    const Action& getDefaultAction() const {
+        return (default_action_);
+    }
+};
+
+}
+}
+
+#endif
diff --git a/src/lib/acl/check.h b/src/lib/acl/check.h
new file mode 100644
index 0000000..3297d4b
--- /dev/null
+++ b/src/lib/acl/check.h
@@ -0,0 +1,195 @@
+// 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.
+
+#ifndef ACL_CHECK_H
+#define ACL_CHECK_H
+
+#include <vector>
+#include <typeinfo>
+#include <sstream>
+
+namespace isc {
+namespace acl {
+
+/**
+ * \brief ACL check base class.
+ *
+ * It is intended that all ACL checks are inherited (maybe indirectly) from
+ * this base class. This will allow us to define new types of checks without
+ * changing any of the code that is using it and with the correct
+ * implementation even without changing the thing that parses configuration
+ * and creates instances of the checks.
+ *
+ * It is implemented as a template. This allows easy reuse of the code for
+ * checking of different types of things (packets of different protocols, etc).
+ * We'll implement the loader and compound checks as templates as well (
+ * and just make sure they are instantiated for each type of thing we want
+ * to check). While most of concrete checks will be specific for one protocol
+ * (or whatever the entity we check is), it makes sense to implement some of
+ * these as templates as well (for example the IP address check, for whatever
+ * context that contains member called ip and has the right methods).
+ *
+ * The Context carries whatever information might be checked for that protocol
+ * (eg. the packet, information where it came from, to what port, ...).
+ */
+template<typename Context> class Check {
+protected:
+    /// \brief Constructor.
+    ///
+    /// Just to make sure this thing is not directly instantiated.
+    Check() { }
+public:
+    /**
+     * \brief The check itself.
+     *
+     * The actual check will be performed here. Every concrete child class
+     * will reimplement it and decide based on the context passed if it
+     * matches.
+     *
+     * The caller should expect this method can throw. The list of exceptions
+     * isn't restricted, as we don't know what kind of checks will be needed.
+     * An exception should be considered as it is impossible to check the
+     * condition. It should lead to either blackholing the packet or returning
+     * some 500-like error (ServFail).
+     *
+     * \param context The thing we are trying to match against this check.
+     * \return true if the context satisfies the check, false otherwise.
+     */
+    virtual bool matches(const Context& context) const = 0;
+
+    /**
+     * \brief Cost for unknown cost estimate.
+     *
+     * This indicates that the estimate for cost is not provided. This
+     * is arbitrary large value, meaning "somehow longish time". To be
+     * on the safe side, we guess more and be just happily suprirised
+     * if it turns out to run faster.
+     */
+    static const unsigned UNKNOWN_COST;
+
+    /**
+     * \brief The expected cost of single match.
+     *
+     * This is here to provide some kind of cost information to optimising
+     * routines. It is in units without any real size, just bigger number
+     * means the check takes longer time. It is expected to be linear scale.
+     * It doesn't need to be exact, but better accuracy might lead to better
+     * optimisations. As of writing this, no optimisations exist yet, but
+     * are expected to exist in future.
+     *
+     * The default is UNKNOWN_COST.
+     */
+    virtual unsigned cost() const {
+        return (UNKNOWN_COST);
+    }
+
+    /// \brief Virtual destructor, as we're virtual
+    virtual ~ Check() { }
+
+    /**
+     * \brief Conversion to text.
+     *
+     * This is meant for debugging purposes, it doesn't have to
+     * serialise the whole information stored in this Check.
+     *
+     * If the check is compound, it should not include the subexpressions
+     * (while we're able to build whatever treeish representation using
+     * CompoundCheck::subexpressions, we're not able to separate them
+     * automatically, as this may produce any kind of free-form string).
+     */
+    virtual std::string toText() const {
+        std::stringstream output;
+        output << typeid(*this).name() << "@" << this;
+        return (output.rdbuf()->str());
+    }
+};
+
+// This seems to be the propper way for static template members
+template<typename Context> const unsigned Check<Context>::UNKNOWN_COST = 10000;
+
+/**
+ * \brief Base class for compound checks.
+ *
+ * While some checks will be a match against some property of the information
+ * passed (eg. the sender's IP address must be in some range), others will
+ * combine results of more checks together to get their own. This is base class
+ * for the second type, allowing listing of the subexpressions (mostly for
+ * debugging purposes to print the whole tree of matches and possible future
+ * optimisations which would like to crawl the expression tree).
+ */
+template<typename Context> class CompoundCheck : public Check<Context> {
+public:
+    /// \brief Abbreviated name for list of subexpressions
+    typedef std::vector<const Check<Context>*> Checks;
+
+    /**
+     * \brief Get the list of subexpressions.
+     *
+     * The result contains pointers to the all subexpressions this check holds
+     * (and therefore might call during its own match() function).
+     *
+     * Using shared pointers looks an overkill here. All the checks must be
+     * alive for the whole life of this one and this check will hold their
+     * ownership. Therefore the only thing the caller needs to do is to make
+     * sure this check is not deleted while it's still using the ones from the
+     * result.
+     *
+     * This method must not throw except for the standard allocation exceptions
+     * to allocate the result.
+     */
+    virtual Checks getSubexpressions() const = 0;
+
+    /**
+     * \brief If the result depends only on results of subexpressions.
+     *
+     * Some optimisations might use the fact that a compound expression is
+     * a function of results of its subexpressions (subchecks) only. But
+     * some compound checks might want to look into the provided context in
+     * their match() as well as looking at the results of the subexpressions.
+     *
+     * This function informs the optimisation routines if it is safe to use
+     * these optimisations.
+     *
+     * \return true if the check depends only on results of subexpressions
+     *    only, false if it examines the context itself as well.
+     * \note The default implementation returns true, as it is expected to
+     *    be the case in majority of cases.
+     */
+    virtual bool pure() const { return (true); }
+
+    /**
+     * \brief Default compound cost function.
+     *
+     * It is simply sum of all subexpressions, as an expected upper bound
+     * on the cost. This expects that the combining itself is cheap relatively
+     * to the checks performed by the subexpressions. In most cases, this
+     * should be good enough, but it can be reimplemented in situations
+     * where most of the subexpressions will be avoided in usual situations.
+     * Replacing the default of 10000 from Check.
+     */
+    virtual unsigned cost() const {
+        Checks checks(getSubexpressions());
+        unsigned result(0);
+        for (typename Checks::const_iterator i(checks.begin());
+             i != checks.end(); ++ i) {
+            result += (*i)->cost();
+        }
+        return (result);
+    }
+};
+
+}
+}
+
+#endif
diff --git a/src/lib/acl/dns.cc b/src/lib/acl/dns.cc
new file mode 100644
index 0000000..5352de6
--- /dev/null
+++ b/src/lib/acl/dns.cc
@@ -0,0 +1,139 @@
+// 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 <memory>
+#include <string>
+#include <vector>
+
+#include <boost/shared_ptr.hpp>
+
+#include <exceptions/exceptions.h>
+
+#include <dns/name.h>
+#include <dns/tsigrecord.h>
+
+#include <cc/data.h>
+
+#include <acl/dns.h>
+#include <acl/ip_check.h>
+#include <acl/dnsname_check.h>
+#include <acl/loader.h>
+#include <acl/logic_check.h>
+
+using namespace std;
+using namespace isc::dns;
+using namespace isc::data;
+
+namespace isc {
+namespace acl {
+
+/// The specialization of \c IPCheck for access control with \c RequestContext.
+///
+/// It returns \c true if the remote (source) IP address of the request
+/// matches the expression encapsulated in the \c IPCheck, and returns
+/// \c false if not.
+template <>
+bool
+IPCheck<dns::RequestContext>::matches(
+    const dns::RequestContext& request) const
+{
+    return (compare(request.remote_address.getData(),
+                    request.remote_address.getFamily()));
+}
+
+namespace dns {
+
+/// The specialization of \c NameCheck for access control with
+/// \c RequestContext.
+///
+/// It returns \c true if the request contains a TSIG record and its key
+/// (owner) name is equal to the name stored in the check; otherwise
+/// it returns \c false.
+template<>
+bool
+NameCheck<RequestContext>::matches(const RequestContext& request) const {
+    return (request.tsig != NULL && request.tsig->getName() == name_);
+}
+
+vector<string>
+internal::RequestCheckCreator::names() const {
+    // Probably we should eventually build this vector in a more
+    // sophisticated way.  For now, it's simple enough to hardcode
+    // everything.
+    vector<string> supported_names;
+    supported_names.push_back("from");
+    supported_names.push_back("key");
+    return (supported_names);
+}
+
+boost::shared_ptr<RequestCheck>
+internal::RequestCheckCreator::create(const string& name,
+                                      ConstElementPtr definition,
+                                      // unused:
+                                      const acl::Loader<RequestContext>&)
+{
+    if (!definition) {
+        isc_throw(LoaderError,
+                  "NULL pointer is passed to RequestCheckCreator");
+    }
+
+    if (name == "from") {
+        return (boost::shared_ptr<internal::RequestIPCheck>(
+                    new internal::RequestIPCheck(definition->stringValue())));
+    } else if (name == "key") {
+        return (boost::shared_ptr<internal::RequestKeyCheck>(
+                    new internal::RequestKeyCheck(
+                        Name(definition->stringValue()))));
+    } else {
+        // This case shouldn't happen (normally) as it should have been
+        // rejected at the loader level.  But we explicitly catch the case
+        // and throw an exception for that.
+        isc_throw(LoaderError, "Invalid check name for RequestCheck: " <<
+                  name);
+    }
+}
+
+RequestLoader&
+getRequestLoader() {
+    static RequestLoader* loader(NULL);
+    if (loader == NULL) {
+        // Creator registration may throw, so we first store the new loader
+        // in an auto pointer in order to provide the strong exception
+        // guarantee.
+        auto_ptr<RequestLoader> loader_ptr =
+            auto_ptr<RequestLoader>(new RequestLoader(REJECT));
+
+        // Register default check creator(s)
+        loader_ptr->registerCreator(boost::shared_ptr<internal::RequestCheckCreator>(
+                                        new internal::RequestCheckCreator()));
+        loader_ptr->registerCreator(
+            boost::shared_ptr<NotCreator<RequestContext> >(
+                new NotCreator<RequestContext>("NOT")));
+        loader_ptr->registerCreator(
+            boost::shared_ptr<LogicCreator<AnyOfSpec, RequestContext> >(
+                new LogicCreator<AnyOfSpec, RequestContext>("ANY")));
+        loader_ptr->registerCreator(
+            boost::shared_ptr<LogicCreator<AllOfSpec, RequestContext> >(
+                new LogicCreator<AllOfSpec, RequestContext>("ALL")));
+
+        // From this point there shouldn't be any exception thrown
+        loader = loader_ptr.release();
+    }
+
+    return (*loader);
+}
+
+}
+}
+}
diff --git a/src/lib/acl/dns.h b/src/lib/acl/dns.h
new file mode 100644
index 0000000..4eba905
--- /dev/null
+++ b/src/lib/acl/dns.h
@@ -0,0 +1,157 @@
+// 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.
+
+#ifndef ACL_DNS_H
+#define ACL_DNS_H 1
+
+#include <string>
+#include <vector>
+
+#include <boost/shared_ptr.hpp>
+
+#include <cc/data.h>
+
+#include <acl/ip_check.h>
+#include <acl/dnsname_check.h>
+#include <acl/loader.h>
+
+namespace isc {
+namespace dns {
+class TSIGRecord;
+}
+namespace acl {
+namespace dns {
+
+/**
+ * \brief DNS request to be checked.
+ *
+ * This plays the role of Context of the generic template ACLs (in namespace
+ * isc::acl).
+ *
+ * It is a simple structure holding just the bunch of information. Therefore
+ * the names don't end up with an underscore; there are no methods so they
+ * can't be confused with local variables.
+ *
+ * This structure is generally expected to be ephemeral and read-only: It
+ * would be constructed immediately before a particular ACL is checked
+ * and used only for the ACL match purposes.  Due to this nature, and since
+ * ACL processing is often performance sensitive (typically it's performed
+ * against all incoming packets), the construction is designed to be
+ * lightweight: it tries to avoid expensive data copies or dynamic memory
+ * allocation as much as possible.  Specifically, the constructor can
+ * take a pointer or reference to an object and keeps it as a reference
+ * (not making a local copy).  This also means the caller is responsible for
+ * keeping the passed parameters valid while this structure is used.
+ * This should generally be reasonable as this structure is expected to be
+ * used only for a very short period as stated above.
+ *
+ * Based on the minimalist philosophy, the initial implementation only
+ * maintains the remote (source) IP address of the request and (optionally)
+ * the TSIG record included in the request.  We may add more parameters of
+ * the request as we see the need for them.  Possible additional parameters
+ * are the local (destination) IP address, the remote and local port numbers,
+ * various fields of the DNS request (e.g. a particular header flag value).
+ */
+struct RequestContext {
+    /// The constructor
+    ///
+    /// This is a trivial constructor that perform straightforward
+    /// initialization of the member variables from the given parameters.
+    ///
+    /// \exception None
+    ///
+    /// \parameter remote_address_param The remote IP address
+    /// \parameter tsig_param A valid pointer to the TSIG record included in
+    /// the request or NULL if the request doesn't contain a TSIG.
+    RequestContext(const IPAddress& remote_address_param,
+                   const isc::dns::TSIGRecord* tsig_param) :
+        remote_address(remote_address_param),
+        tsig(tsig_param)
+    {}
+
+    ///
+    /// \name Parameter variables
+    ///
+    /// These member variables must be immutable so that the integrity of
+    /// the structure is kept throughout its lifetime.  The easiest way is
+    /// to declare the variable as const.  If it's not possible for a
+    /// particular variable, it must be defined as private and accessible
+    /// only via an accessor method.
+    //@{
+    /// \brief The remote IP address (eg. the client's IP address).
+    const IPAddress& remote_address;
+
+    /// \brief The TSIG record included in the request message, if any.
+    ///
+    /// If the request doesn't include a TSIG, this member will be NULL.
+    const isc::dns::TSIGRecord* const tsig;
+    //@}
+private:
+    // silence MSVC warning C4512: assignment operator could not be generated
+    RequestContext& operator=(RequestContext const&);
+};
+
+/// \brief DNS based check.
+typedef acl::Check<RequestContext> RequestCheck;
+/// \brief DNS based compound check.
+typedef acl::CompoundCheck<RequestContext> CompoundCheck;
+/// \brief DNS based ACL.
+typedef acl::ACL<RequestContext> RequestACL;
+/// \brief DNS based ACL loader.
+typedef acl::Loader<RequestContext> RequestLoader;
+
+/**
+ * \brief Loader singleton access function.
+ *
+ * This function returns a loader of ACLs. It is expected applications
+ * will use this function instead of creating their own loaders, because
+ * one is enough, this one will have registered default checks and it
+ * is known one, so any plugins can registrer additional checks as well.
+ */
+RequestLoader& getRequestLoader();
+
+// The following is essentially private to the implementation and could
+// be hidden in the implementation file.  But it's visible via this header
+// file for testing purposes.  They are not supposed to be used by normal
+// applications directly, and to signal the intent, they are given inside
+// a separate namespace.
+namespace internal {
+
+// Shortcut typedef
+typedef isc::acl::IPCheck<RequestContext> RequestIPCheck;
+typedef isc::acl::dns::NameCheck<RequestContext> RequestKeyCheck;
+
+class RequestCheckCreator : public acl::Loader<RequestContext>::CheckCreator {
+public:
+    virtual std::vector<std::string> names() const;
+
+    virtual boost::shared_ptr<RequestCheck>
+    create(const std::string& name, isc::data::ConstElementPtr definition,
+           const acl::Loader<RequestContext>& loader);
+
+    /// Until we are sure how the various rules work for this case, we won't
+    /// allow unexpected special interpretation for list definitions.
+    virtual bool allowListAbbreviation() const { return (false); }
+};
+} // end of namespace "internal"
+
+} // end of namespace "dns"
+} // end of namespace "acl"
+} // end of namespace "isc"
+
+#endif
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/acl/dnsname_check.h b/src/lib/acl/dnsname_check.h
new file mode 100644
index 0000000..56115b9
--- /dev/null
+++ b/src/lib/acl/dnsname_check.h
@@ -0,0 +1,85 @@
+// 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.
+
+#ifndef __DNSNAME_CHECK_H
+#define __DNSNAME_CHECK_H 1
+
+#include <dns/name.h>
+
+#include <acl/check.h>
+
+namespace isc {
+namespace acl {
+namespace dns {
+
+/// ACL check for DNS names
+///
+/// This class is intended to perform a match between a domain name
+/// specified in an ACL and a given name.  The primary usage of this class
+/// is an ACL match for TSIG keys, where an ACL would contain a list of
+/// acceptable key names and the \c match() method would compare the owner
+/// name of a TSIG record against the specified names.
+///
+/// This class could be used for other kinds of names such as the query name
+/// of normal DNS queries.
+///
+/// The class is templated on the type of a context structure passed to the
+/// matches() method, and a template specialisation for that method must be
+/// supplied for the class to be used.
+template <typename Context>
+class NameCheck : public Check<Context> {
+public:
+    /// The constructor
+    ///
+    /// \exception std::bad_alloc Resource allocation fails in copying the
+    /// name
+    ///
+    /// \param name The domain name to be matched in \c matches().
+    NameCheck(const isc::dns::Name& name) : name_(name) {}
+
+    /// Destructor
+    virtual ~NameCheck() {}
+
+    /// The check method
+    ///
+    /// Matches the passed argument to the condition stored here.  Different
+    /// specializations must be provided for different argument types, and the
+    /// program will fail to compile if a required specialisation is not
+    /// provided.
+    ///
+    /// \param context Information to be matched
+    virtual bool matches(const Context& context) const;
+
+    /// Returns the name specified on construction.
+    ///
+    /// This is mainly for testing purposes.
+    ///
+    /// \exception None
+    const isc::dns::Name& getName() const { return (name_); }
+
+private:
+    const isc::dns::Name name_;
+    // silence MSVC warning C4512: assignment operator could not be generated
+    NameCheck& operator=(NameCheck const&);
+};
+
+} // namespace dns
+} // namespace acl
+} // namespace isc
+
+#endif // __DNSNAME_CHECK_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/acl/ip_check.cc b/src/lib/acl/ip_check.cc
new file mode 100644
index 0000000..867ad71
--- /dev/null
+++ b/src/lib/acl/ip_check.cc
@@ -0,0 +1,143 @@
+// 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.
+
+#ifndef _WIN32
+#include <sys/socket.h>
+#endif
+
+#include <exceptions/exceptions.h>
+
+#include <boost/lexical_cast.hpp>
+
+#include <acl/ip_check.h>
+
+using namespace std;
+using namespace isc;
+
+namespace isc {
+namespace acl {
+namespace internal {
+
+uint8_t
+createMask(size_t prefixlen) {
+
+    if (prefixlen == 0) {
+        return (0);
+
+    } else if (prefixlen <= 8) {
+
+        // In the following discussion:
+        //
+        // w is the width of the data type in bits.
+        // m is the value of prefixlen, the number of most signifcant bits we
+        // want to set.
+        // ** is exponentiation (i.e. 2**n is 2 raised to the power of n).
+        //
+        // We note that the value of 2**m - 1 gives a value with the least
+        // significant m bits set.  For a data type of width w, this means that
+        // the most signficant (w-m) bits are clear.
+        //
+        // Hence the value 2**(w-m) - 1 gives a result with the least signficant
+        // w-m bits set and the most significant m bits clear.  The 1's
+        // complement of this value gives is the result we want.
+        //
+        // Final note: at this point in the logic, m is non-zero, so w-m < w.
+        // This means 1<<(w-m) will fit into a variable of width w bits.  In
+        // other words, in the expression below, no term will cause an integer
+        // overflow.
+        return (~((1 << (8 - prefixlen)) - 1));
+    }
+
+    // Mask size is too large. (Note that prefixlen is unsigned, so can't be
+    // negative.)
+    isc_throw(isc::OutOfRange, "prefixlen argument must be between 0 and 8");
+}
+
+pair<string, int>
+splitIPAddress(const string& ipprefix) {
+
+    // Split string into its components - an address and a prefix length.
+    // We initialize by assuming that there is no slash in the string given.
+    string address = ipprefix;
+    string prefixlen = "";
+
+    const size_t slashpos = ipprefix.find('/');
+    if ((ipprefix.size() == 0) || (slashpos == 0) ||
+        (slashpos == (ipprefix.size() - 1))) {
+        // Nothing in prefix, or it starts with or ends with a slash.
+        isc_throw(isc::InvalidParameter, "address prefix of " << ipprefix <<
+                                         " is not valid");
+
+    } else if (slashpos != string::npos) {
+        // There is a slash somewhere in the string, split the string on it.
+        // Don't worry about multiple slashes - if there are some, they will
+        // appear in the prefixlen segment and will be detected when an attempt
+        // is made to convert it to a number.
+        address = ipprefix.substr(0, slashpos);
+        prefixlen = ipprefix.substr(slashpos + 1);
+    }
+
+    // Set the default value for the prefix length.  As the type of the address
+    // is not known at the point this function is called, the maximum
+    // allowable value is also not known.  The value of 0 is reserved for
+    // a "match any address" match.
+    int prefix_size = -1;
+
+    // If there is a prefixlength, attempt to convert it.
+    if (!prefixlen.empty()) {
+        try {
+            prefix_size = boost::lexical_cast<int>(prefixlen);
+            if (prefix_size < 0) {
+                isc_throw(isc::InvalidParameter, "address prefix of " <<
+                          ipprefix << " is not valid");
+            }
+        } catch (boost::bad_lexical_cast&) {
+            isc_throw(isc::InvalidParameter, "prefix length of '" <<
+                      prefixlen << "' is not valid");
+        }
+    }
+
+    return (make_pair(address, prefix_size));
+}
+} // namespace internal
+
+namespace {
+const uint8_t*
+getSockAddrData(const struct sockaddr& sa) {
+    const void* sa_ptr = &sa;
+    const void* data_ptr;
+    if (sa.sa_family == AF_INET) {
+        const struct sockaddr_in* sin =
+            static_cast<const struct sockaddr_in*>(sa_ptr);
+        data_ptr = &sin->sin_addr;
+    } else if (sa.sa_family == AF_INET6) {
+        const struct sockaddr_in6* sin6 =
+            static_cast<const struct sockaddr_in6*>(sa_ptr);
+        data_ptr = &sin6->sin6_addr;
+    } else {
+        isc_throw(BadValue, "Unsupported address family for IPAddress: " <<
+                  static_cast<int>(sa.sa_family));
+    }
+    return (static_cast<const uint8_t*>(data_ptr));
+}
+}
+
+IPAddress::IPAddress(const struct sockaddr& sa) :
+    family(sa.sa_family),
+    data(getSockAddrData(sa)),
+    length(family == AF_INET ?
+           sizeof(struct in_addr) : sizeof(struct in6_addr))
+{}
+} // namespace acl
+} // namespace isc
diff --git a/src/lib/acl/ip_check.h b/src/lib/acl/ip_check.h
new file mode 100644
index 0000000..88d08f0
--- /dev/null
+++ b/src/lib/acl/ip_check.h
@@ -0,0 +1,419 @@
+// 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.
+
+#ifndef __IP_CHECK_H
+#define __IP_CHECK_H
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <vector>
+
+#include <boost/static_assert.hpp>
+
+#include <stdint.h>
+#ifdef _WIN32
+#include <ws2tcpip.h>
+#else
+#include <arpa/inet.h>
+#include <sys/socket.h> // for AF_INET/AF_INET6
+#include <netinet/in.h>
+#endif
+
+#include <acl/check.h>
+#include <exceptions/exceptions.h>
+#include <util/strutil.h>
+
+namespace isc {
+namespace acl {
+
+// Free functions.  These are not supposed to be used outside this module,
+// but are declared public for testing.  To try to conceal them, they are
+// put in an "internal" namespace.
+
+namespace internal {
+
+/// \brief Convert prefix length to mask
+///
+/// Given a prefix length and a data type, return a value of that data type
+/// with the most significant "prefix length" bits set.  For example, if the
+/// data type is an uint8_t and the prefix length is 3, the function would
+/// return a uint8_t holding the binary value 11100000.  This value is used as
+/// a mask in the address checks.
+///
+/// \param prefixlen number of bits to be set in the mask.  This must be
+///        between 0 and 8.
+///
+/// \return uint8_t with the most significant "prefixlen" bits set.
+///
+/// \exception OutOfRange prefixlen is too large for the data type.
+
+uint8_t createMask(size_t prefixlen);
+
+/// \brief Split IP Address Prefix
+///
+/// Splits an IP address prefix (given in the form of "xxxxxx/n" or "xxxxx" into
+/// a string representing the IP address and a number giving the length of the
+/// prefix. (In the latter case, the prefix is equal in length to the width in
+/// width in bits of the data type holding the address.) An exception will be
+/// thrown if the string format is invalid or if the prefix length is invalid.
+///
+/// N.B. This function does NOT check that the address component is a valid IP
+/// address; this is done elsewhere in the address parsing process.
+///
+/// \param ipprefix Address or address prefix.  The string should be passed
+///                 without leading or trailing spaces.
+///
+/// \return Pair of (string, int) holding the address string and the prefix
+///         length.  The second element is -1 if no prefix was given.
+///
+/// \exception InvalidParameter Address prefix not of the expected syntax
+
+std::pair<std::string, int>
+splitIPAddress(const std::string& ipprefix);
+
+} // namespace internal
+
+/// \brief A simple representation of IP address.
+///
+/// This structure provides address family independent interfaces of an
+/// IP(v4 or v6) address, so that the application can perform
+/// \c IPCheck::matches without knowing which version of address it is
+/// handling.  (For example, consider the standard socket API: it uses
+/// the generic \c sockaddr structure to represent endpoints).
+///
+/// An object of this class could be constructed from various types of
+/// sources, but in the initial implementation there's only one constructor,
+/// which takes a \c sockaddr structure.  For efficiency the \c IPAddress
+/// object only retains a reference to the necessary part of \c sockaddr.
+/// Therefore the corresponding \c sockaddr instance must be valid while the
+/// \c IPAddress object is used.
+///
+/// This class is copyable so that a fixed object can be easily reused for
+/// different addresses.  To ensure internal integrity, specific member
+/// variables are kept private and only accessible via read-only accessor
+/// methods.  Due to this, it is ensured, for example, that if \c getFamily()
+/// returns \c AF_INET6, \c getLength() always returns 16.
+///
+/// All accessor methods are straightforward and exception free.
+///
+/// In future, we may introduce the default constructor to further improve
+/// reusability.
+struct IPAddress {
+    /// The constructor from socket address structure.
+    ///
+    /// This constructor set up the internal data based on the actual type
+    /// \c sa.  For example, if \c sa.sa_family is \c AF_INET, it assumes
+    /// \c sa actually refers to a \c sockaddr_in structure.
+    /// The behavior when this assumption isn't held is undefined.
+    ///
+    /// \param sa A reference to the socket address structure from which the
+    /// \c IPAddress is to be constructed.
+    explicit IPAddress(const struct sockaddr& sa);
+
+    /// Return the address family of the address
+    ///
+    /// It's AF_INET for IPv4 and AF_INET6 for IPv6.
+    int getFamily() const { return (family); }
+
+    /// Return the binary representation of the address in network byte order.
+    ///
+    /// Only the \c getLength() bytes from the returned pointer are ensured
+    /// to be valid.  In addition, if the \c sockaddr structure given on
+    /// construction was dynamically allocated, the data is valid only until
+    /// the \c sockaddr is invalidated.
+    const uint8_t* getData() const { return (data); }
+
+    /// Return the length of the address.
+    size_t getLength() const { return (length); }
+private:
+    int family;
+    const uint8_t* data;
+    size_t length;
+};
+
+/// \brief IP Check
+///
+/// This class performs a match between an IP address prefix specified in an ACL
+/// and a given IP address.  The check works for both IPv4 and IPv6 addresses.
+///
+/// The class is templated on the type of a context structure passed to the
+/// matches() method, and a template specialisation for that method must be
+/// supplied for the class to be used.
+
+template <typename Context>
+class IPCheck : public Check<Context> {
+private:
+    // Size of uint8_t array needed to hold different address types
+    static const size_t IPV6_SIZE = sizeof(struct in6_addr);
+    static const size_t IPV4_SIZE = sizeof(struct in_addr);
+
+    // Confirm our assumption of relative sizes - this allows us to assume that
+    // an array sized for an IPv6 address can hold an IPv4 address.
+    BOOST_STATIC_ASSERT(sizeof(struct in6_addr) > sizeof(struct in_addr));
+
+public:
+    /// \brief String Constructor
+    ///
+    /// Constructs an IP Check object from an address or address prefix in the
+    /// form <ip-address>/n".
+    ///
+    /// Also allowed are the special keywords "any4" and "any6", which match
+    /// any IPv4 or IPv6 address.  These must be specified in lowercase.
+    ///
+    /// \param ipprefix IP address prefix in the form "<ip-address>/n"
+    ///        (where the "/n" part is optional and should be valid for the
+    ///        address).  If "n" is specified as zero, the match is for any
+    ///        address in that address family.  The address can also be
+    ///        given as "any4" or "any6".
+    IPCheck(const std::string& ipprefix) : family_(0) {
+
+        // Ensure array elements are correctly initialized with zeroes.
+        std::fill(address_, address_ + IPV6_SIZE, 0);
+        std::fill(mask_, mask_ + IPV6_SIZE, 0);
+
+        // Only deal with the string after we've removed leading and trailing
+        // spaces.
+        const std::string mod_prefix = isc::util::str::trim(ipprefix);
+
+        // Check for special cases first.
+        if (mod_prefix == "any4") {
+            family_ = AF_INET;
+
+        } else if (mod_prefix == "any6") {
+            family_ = AF_INET6;
+
+        } else {
+
+            // General address prefix.  Split into address part and prefix
+            // length.
+            const std::pair<std::string, int> result =
+                internal::splitIPAddress(mod_prefix);
+
+            // Try to convert the address.  If successful, the result is in
+            // network-byte order (most significant components at lower
+            // addresses).
+            int status = inet_pton(AF_INET6, result.first.c_str(), address_);
+            if (status == 1) {
+                // It was an IPv6 address.
+                family_ = AF_INET6;
+            } else {
+                // IPv6 interpretation failed, try IPv4.
+                status = inet_pton(AF_INET, result.first.c_str(), address_);
+                if (status == 1) {
+                    family_ = AF_INET;
+                }
+            }
+
+            // Handle errors.
+            if (status == 0) {
+                isc_throw(isc::InvalidParameter, "address prefix of " <<
+                          ipprefix << " is not valid");
+            } else if (status < 0) {
+                isc_throw(isc::Unexpected, "address conversion of " <<
+                          ipprefix << " failed due to a system error");
+            }
+
+            // All done, so set the mask used in the address comparison.
+            setMask(result.second);
+        }
+    }
+
+    /// \brief Destructor
+    virtual ~IPCheck() {}
+
+    /// \brief The check itself
+    ///
+    /// Matches the passed argument to the condition stored here.  Different
+    /// specialisations must be provided for different argument types, and the
+    /// program will fail to compile if a required specialisation is not
+    /// provided.
+    ///
+    /// It is expected that matches() will extract the address information from
+    /// the Context structure, and use compare() to actually perform the
+    /// comparison.
+    ///
+    /// \param context Information to be matched
+    virtual bool matches(const Context& context) const;
+
+    /// \brief Estimated cost
+    ///
+    /// Assume that the cost of the match is linear and depends on the
+    /// maximum number of comparison operations.
+    ///
+    /// \return Estimated cost of the comparison
+    virtual unsigned cost() const {
+        return ((family_ == AF_INET) ? IPV4_SIZE : IPV6_SIZE);
+    }
+
+    ///@{
+    /// Access methods - mainly for testing
+
+    /// \return Stored IP address
+    std::vector<uint8_t> getAddress() const {
+        const size_t vector_len = (family_ == AF_INET ? IPV4_SIZE : IPV6_SIZE);
+        return (std::vector<uint8_t>(address_, address_ + vector_len));
+    }
+
+    /// \return Network mask applied to match
+    std::vector<uint8_t> getMask() const {
+        const size_t vector_len = (family_ == AF_INET ? IPV4_SIZE : IPV6_SIZE);
+        return (std::vector<uint8_t>(mask_, mask_ + vector_len));
+    }
+
+    /// \return Prefix length of the match
+    size_t getPrefixlen() const {
+        // Work this out by counting bits in the mask.
+        size_t count = 0;
+        for (size_t i = 0; i < IPV6_SIZE; ++i) {
+            if (mask_[i] == 0xff) {
+                // All bits set in this byte
+                count += 8;
+                continue;
+
+            } else if (mask_[i] != 0) {
+                // Only some bits set in this byte.  Count them.
+                uint8_t byte = mask_[i];
+                for (int j = 0; j < 8; ++j) {
+                    count += byte & 0x01;   // Add one if the bit is set
+                    byte >>= 1;             // Go for next bit
+                }
+            }
+            break;
+        }
+        return (count);
+    }
+
+    /// \return Address family
+    int getFamily() const {
+        return (family_);
+    }
+    ///@}
+
+protected:
+    /// \brief Comparison
+    ///
+    /// This is the actual comparison function that checks the IP address passed
+    /// to this class with the matching information in the class itself.  It is
+    /// expected to be called from matches().
+    ///
+    /// \param testaddr Address (in network byte order) to test against the
+    ///                 check condition in the class.  This is expected to
+    ///                 be IPV6_SIZE or IPV4_SIZE bytes long.
+    /// \param family   Address family of testaddr.
+    ///
+    /// \return true if the address matches, false if it does not.
+    virtual bool compare(const uint8_t* testaddr, int family) const {
+
+        if (family != family_) {
+            // Can't match if the address is of the wrong family
+            return (false);
+        }
+
+        // Simple check failed, so have to do a complete match.  To check that
+        // the address given matches the stored network address and mask, we
+        // check the simple condition that:
+        //
+        //     address_given & mask_ == stored_address & mask_
+        //
+        // The result is checked for all bytes for which there are bits set in
+        // the mask.  We stop at the first non-match (or when we run out of bits
+        // in the mask).
+        //
+        // Note that the mask represents a contiguous set of bits.  As such, as
+        // soon as we find a mask byte of zeroes, we have run past the part of
+        // the address where we need to match.
+        //
+        // Note also that when checking an IPv4 address, the constructor has
+        // set all bytes in the mask beyond the first four bytes to zero.
+        // As the loop stops when it encounters a zero mask byte, if the
+        // ACL is for an IPV4 address, the loop will never check more than four
+        // bytes.
+
+        bool match = true;
+        for (int i = 0; match && (i < IPV6_SIZE) && (mask_[i] != 0); ++i) {
+             match = ((testaddr[i] & mask_[i]) == (address_[i] & mask_[i]));
+        }
+        return (match);
+    }
+
+private:
+    /// \brief Set Mask
+    ///
+    /// Sets up the mask from the prefix length.  This involves setting
+    /// an individual mask in each byte of the mask array.
+    ///
+    /// The actual allowed value of the prefix length depends on the address
+    /// family.
+    ///
+    /// \param requested Requested prefix length size.  If negative, the
+    ///        maximum for the address family is assumed.  (A negative value
+    ///        will arise if the string constructor was used and no mask size
+    ///        was given.)
+    void setMask(int requested) {
+
+        // Set the maximum number of bits allowed in the mask, and request
+        // that number of bits if no prefix length was given in the constructor.
+        const int maxmask = 8 * ((family_ == AF_INET) ? IPV4_SIZE : IPV6_SIZE);
+        if (requested < 0) {
+            requested = maxmask;
+        }
+
+        // Validate that the mask is valid.
+        if (requested <= maxmask) {
+
+            // Loop, setting the bits in the set of mask bytes until all the
+            // specified bits have been used up.  As both IPv4 and IPv6
+            // addresses are stored in network-byte order, this works in
+            // both cases.
+            size_t bits_left = requested;   // Bits remaining to set
+            int i = -1;
+            while (bits_left > 0) {
+                if (bits_left >= 8) {
+                    mask_[++i] = ~0;  // All bits set
+                    bits_left -= 8;
+
+                } else if (bits_left > 0) {
+                    mask_[++i] = internal::createMask(bits_left);
+                    bits_left = 0;
+                }
+            }
+        } else {
+            isc_throw(isc::OutOfRange,
+                      "mask size of " << requested << " is invalid " <<
+                      "for the given address family");
+        }
+    }
+
+    // Member variables.
+    uint8_t address_[IPV6_SIZE];  ///< Address in binary form
+    uint8_t mask_[IPV6_SIZE];     ///< Address mask
+    int     family_;              ///< Address family
+};
+
+// Some compilers seem to need this to be explicitly defined outside the class
+template <typename Context>
+const size_t IPCheck<Context>::IPV6_SIZE;
+
+template <typename Context>
+const size_t IPCheck<Context>::IPV4_SIZE;
+
+} // namespace acl
+} // namespace isc
+
+#endif // __IP_CHECK_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/acl/loader.cc b/src/lib/acl/loader.cc
new file mode 100644
index 0000000..8ca7e28
--- /dev/null
+++ b/src/lib/acl/loader.cc
@@ -0,0 +1,46 @@
+// 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 "loader.h"
+
+using namespace std;
+
+namespace isc {
+namespace acl {
+
+BasicAction defaultActionLoader(data::ConstElementPtr actionEl) {
+    try {
+        const string action(actionEl->stringValue());
+        if (action == "ACCEPT") {
+            return (ACCEPT);
+        } else if (action == "REJECT") {
+            return (REJECT);
+        } else if (action == "DROP") {
+            return (DROP);
+        } else {
+            throw LoaderError(__FILE__, __LINE__,
+                              string("Unknown action '" + action + "'").
+                                  c_str(),
+                              actionEl);
+        }
+    }
+    catch (const data::TypeError&) {
+        throw LoaderError(__FILE__, __LINE__,
+                          "Invalid element type for action, must be string",
+                          actionEl);
+    }
+}
+
+}
+}
diff --git a/src/lib/acl/loader.h b/src/lib/acl/loader.h
new file mode 100644
index 0000000..9f663e3
--- /dev/null
+++ b/src/lib/acl/loader.h
@@ -0,0 +1,482 @@
+// 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.
+
+#ifndef ACL_LOADER_H
+#define ACL_LOADER_H
+
+#include <exceptions/exceptions.h>
+#include <acl/acl.h>
+#include <cc/data.h>
+#include <boost/function.hpp>
+#include <boost/shared_ptr.hpp>
+#include <map>
+
+namespace isc {
+namespace acl {
+
+class AnyOfSpec;
+class AllOfSpec;
+template<typename Mode, typename Context> class LogicOperator;
+
+/**
+ * \brief Exception for bad ACL specifications.
+ *
+ * This will be thrown by the Loader if the ACL description is malformed
+ * in some way.
+ *
+ * It also can hold optional JSON element where was the error detected, so
+ * it can be examined.
+ *
+ * Checks may subclass this exception for similar errors if they see it fit.
+ */
+class LoaderError : public BadValue {
+private:
+    const data::ConstElementPtr element_;
+public:
+    /**
+     * \brief Constructor.
+     *
+     * Should be used with isc_throw if the fourth argument isn't used.
+     *
+     * \param file The file where the throw happened.
+     * \param line Similar as file, just for the line number.
+     * \param what Human readable description of what happened.
+     * \param element This might be passed to hold the JSON element where
+     *     the error was detected.
+     */
+    LoaderError(const char* file, size_t line, const char* what,
+                data::ConstElementPtr element = data::ConstElementPtr()) :
+        BadValue(file, line, what),
+        element_(element)
+    {}
+
+    ~ LoaderError() throw() {}
+
+    /**
+     * \brief Get the element.
+     *
+     * This returns the element where the error was detected. Note that it
+     * might be NULL in some situations.
+     */
+    const data::ConstElementPtr& element() const {
+        return (element_);
+    }
+};
+
+/**
+ * \brief Loader of the default actions of ACLs.
+ *
+ * Declared outside the Loader class, as this one does not need to be
+ * templated. This will throw LoaderError if the parameter isn't string
+ * or if it doesn't contain one of the accepted values.
+ *
+ * \param action The JSON representation of the action. It must be a string
+ *     and contain one of "ACCEPT", "REJECT" or "DROP.
+ * \note We could define different names or add aliases if needed.
+ */
+BasicAction defaultActionLoader(data::ConstElementPtr action);
+
+/**
+ * \brief Loader of ACLs.
+ *
+ * The goal of this class is to convert JSON description of an ACL to object
+ * of the ACL class (including the checks inside it).
+ *
+ * The class can be used to load the checks only. This is supposed to be used
+ * by compound checks to create the subexpressions.
+ *
+ * To allow any kind of checks to exist in the application, creators are
+ * registered for the names of the checks.
+ *
+ * An ACL definition looks like this:
+ * \verbatim
+ [
+   {
+      "action": "ACCEPT",
+      "match-type": <parameter>
+   },
+   {
+      "action": "REJECT",
+      "match-type": <parameter>,
+      "another-match-type": [<parameter1>, <parameter2>]
+   },
+   {
+      "action": "DROP"
+   }
+ ]
+ \endverbatim
+ *
+ * This is a list of elements. Each element must have an "action"
+ * entry/keyword. That one specifies which action is returned if this
+ * element matches (the value of the key is passed to the action loader
+ * (see the constructor). It may be any piece of JSON which the action
+ * loader expects.
+ *
+ * The rest of the element are matches. The left side is the name of the
+ * match type (for example match for source IP address or match for message
+ * size). The <parameter> is whatever is needed to describe the match and
+ * depends on the match type, the loader passes it verbatim to creator
+ * of that match type.
+ *
+ * There may be multiple match types in single element. In such case, all
+ * of the matches must match for the element to take action (so, in the second
+ * element, both "match-type" and "another-match-type" must be satisfied).
+ * If there's no match in the element, the action is taken/returned without
+ * conditions, every time (makes sense as the last entry, as the ACL will
+ * never get past it).
+ *
+ * The second entry shows another thing - if there's a list as the value
+ * for some match and the match itself is not expecting a list, it is taken
+ * as an "or" - a match for at last one of the choices in the list must match.
+ * So, for the second entry, both "match-type" and "another-match-type" must
+ * be satisfied, but the another one is satisfied by either parameter1 or
+ * parameter2.
+ */
+template<typename Context, typename Action = BasicAction> class Loader {
+public:
+    /**
+     * \brief Constructor.
+     *
+     * \param default_action The default action for created ACLs.
+     * \param actionLoader is the loader which will be used to convert actions
+     *     from their JSON representation. The default value is suitable for
+     *     the BasicAction enum. If you did not specify the second
+     *     template argument, you don't need to specify this loader.
+     */
+    Loader(const Action& defaultAction,
+           const boost::function1<Action, data::ConstElementPtr>
+               &actionLoader = &defaultActionLoader) :
+        default_action_(defaultAction),
+        action_loader_(actionLoader)
+    {}
+
+    /**
+     * \brief Creator of the checks.
+     *
+     * This can be registered within the Loader and will be used to create the
+     * checks. It is expected multiple creators (for multiple types, one can
+     * handle even multiple names) will be created and registered to support
+     * range of things we could check. This allows for customizing/extending
+     * the loader.
+     */
+    class CheckCreator {
+    public:
+        /** \brief Virtual class needs virtual destructor */
+        virtual ~CheckCreator() {}
+
+        /**
+         * \brief List of names supported by this loader.
+         *
+         * List of all names for which this loader is able to create the
+         * checks. There can be multiple names, to support both aliases
+         * to the same checks and creators capable of creating multiple
+         * types of checks.
+         */
+        virtual std::vector<std::string> names() const = 0;
+
+        /**
+         * \brief Creates the check.
+         *
+         * This function does the actual creation. It is passed all the
+         * relevant data and is supposed to return shared pointer to the
+         * check.
+         *
+         * It is expected to throw the LoaderError exception when the
+         * definition is invalid.
+         *
+         * \param name The type name of the check. If the creator creates
+         *     only one type of check, it can safely ignore this parameter.
+         * \param definition The part of JSON describing the parameters of
+         *     check. As there's no way for the loader to know how the
+         *     parameters might look like, they are not checked in any way.
+         *     Therefore it's up to the creator (or the check being created)
+         *     to validate the data and throw if it is bad.
+         * \param Current loader calling this creator. This can be used
+         *     to load subexpressions in case of compound check.
+         */
+        virtual boost::shared_ptr<Check<Context> > create(
+            const std::string& name, data::ConstElementPtr definition,
+            const Loader<Context, Action>& loader) = 0;
+
+        /**
+         * \brief Is list or-abbreviation allowed?
+         *
+         * If this returns true and the parameter (eg. the value we check
+         * against, the one that is passed as the second parameter of create)
+         * is list, the loader will call the create method with each element of
+         * the list and aggregate all the results in OR compound check. If it
+         * is false, the parameter is passed verbatim no matter if it is or
+         * isn't a list. For example, IP check will have this as true (so
+         * multiple IP addresses can be passed as options), but AND operator
+         * will return false and handle the list of subexpressions itself.
+         *
+         * The rationale behind this is that it is common to specify list of
+         * something that matches (eg. list of IP addresses).
+         */
+        virtual bool allowListAbbreviation() const {
+            return (true);
+        }
+    };
+
+    /**
+     * \brief Register another check creator.
+     *
+     * Adds a creator to the list of known ones. The creator's list of names
+     * must be disjoint with the names already known to the creator or the
+     * LoaderError exception is thrown. In such case, the creator is not
+     * registered under any of the names. In case of other exceptions, like
+     * bad_alloc, only weak exception safety is guaranteed.
+     *
+     * \param creator Shared pointer to the creator.
+     * \note We don't support deregistration yet, but it is expected it will
+     *     be needed in future, when we have some kind of plugins. These
+     *     plugins might want to unload, in which case they would need to
+     *     deregister their creators. It is expected they would pass the same
+     *     pointer to such method as they pass here.
+     */
+    void registerCreator(boost::shared_ptr<CheckCreator> creator) {
+        // First check we can insert all the names
+        typedef std::vector<std::string> Strings;
+        const Strings names(creator->names());
+        for (Strings::const_iterator i(names.begin()); i != names.end();
+             ++i) {
+            if (creators_.find(*i) != creators_.end()) {
+                isc_throw(LoaderError, "The loader already contains creator "
+                          "named " << *i);
+            }
+        }
+        // Now insert them
+        for (Strings::const_iterator i(names.begin()); i != names.end();
+             ++i) {
+            creators_[*i] = creator;
+        }
+    }
+
+    /**
+     * \brief Load a check.
+     *
+     * This parses a check dict (block, the one element of ACL) and calls a
+     * creator (or creators, if more than one check is found inside) for it. It
+     * ignores the "action" key, as it is a reserved keyword used to specify
+     * actions inside the ACL.
+     *
+     * This may throw LoaderError if it is not a dict or if some of the type
+     * names is not known (there's no creator registered for it). The
+     * exceptions from creators aren't caught.
+     *
+     * \param description The JSON description of the check.
+     */
+    boost::shared_ptr<Check<Context> > loadCheck(const data::ConstElementPtr&
+                                                 description) const
+    {
+        // Get the description as a map
+        typedef std::map<std::string, data::ConstElementPtr> Map;
+        Map map;
+        try {
+            map = description->mapValue();
+        }
+        catch (const data::TypeError&) {
+            isc_throw_1(LoaderError, "Check description is not a map",
+                        description);
+        }
+        // Call the internal part with extracted map
+        return (loadCheck(description, map));
+    }
+
+    /**
+     * \brief Load an ACL.
+     *
+     * This parses an ACL list, creates the checks and actions of each element
+     * and returns it.
+     *
+     * No exceptions from \c loadCheck (therefore from whatever creator is
+     * used) and from the actionLoader passed to constructor are caught.
+     *
+     * \exception InvalidParameter The given element is NULL (most likely a
+     * caller's bug)
+     * \exception LoaderError The given element isn't a list or the
+     * "action" key is missing in some element
+     *
+     * \param description The JSON list of ACL.
+     *
+     * \return The newly created ACL object
+     */
+    boost::shared_ptr<ACL<Context, Action> > load(const data::ConstElementPtr&
+                                                  description) const
+    {
+        if (!description) {
+            isc_throw(isc::InvalidParameter,
+                      "Null description is passed to ACL loader");
+        }
+
+        // We first check it's a list, so we can use the list reference
+        // (the list may be huge)
+        if (description->getType() != data::Element::list) {
+            isc_throw_1(LoaderError, "ACL not a list", description);
+        }
+        // First create an empty ACL
+        const List &list(description->listValue());
+        boost::shared_ptr<ACL<Context, Action> > result(
+            new ACL<Context, Action>(default_action_));
+        // Run trough the list of elements
+        for (List::const_iterator i(list.begin()); i != list.end(); ++i) {
+            Map map;
+            try {
+                map = (*i)->mapValue();
+            }
+            catch (const data::TypeError&) {
+                isc_throw_1(LoaderError, "ACL element not a map", *i);
+            }
+            // Create an action for the element
+            const Map::const_iterator action(map.find("action"));
+            if (action == map.end()) {
+                isc_throw_1(LoaderError, "No action in ACL element", *i);
+            }
+            const Action acValue(action_loader_(action->second));
+            // Now create the check if there's one
+            if (map.size() >= 2) { // One is the action, another one the check
+                result->append(loadCheck(*i, map), acValue);
+            } else {
+                // In case there's no check, this matches every time. We
+                // simulate it by our own private "True" check.
+                result->append(boost::shared_ptr<Check<Context> >(new True()),
+                               acValue);
+            }
+        }
+        return (result);
+    }
+
+private:
+    // Some type aliases to save typing
+    typedef std::map<std::string, boost::shared_ptr<CheckCreator> > Creators;
+    typedef std::map<std::string, data::ConstElementPtr> Map;
+    typedef std::vector<data::ConstElementPtr> List;
+    // Private members
+    Creators creators_;
+    const Action default_action_;
+    const boost::function1<Action, data::ConstElementPtr> action_loader_;
+
+    /**
+     * \brief Internal version of loadCheck.
+     *
+     * This is the internal part, shared between load and loadCheck.
+     * \param description The bit of JSON (used in exceptions).
+     * \param map The extracted map describing the check. It does change
+     *     the map.
+     */
+    boost::shared_ptr<Check<Context> > loadCheck(const data::ConstElementPtr&
+                                                 description, Map& map) const
+    {
+        // Remove the action keyword
+        map.erase("action");
+        // Now, do we have any definition? Or is it and abbreviation?
+        switch (map.size()) {
+            case 0:
+                isc_throw_1(LoaderError, "Check description is empty",
+                            description);
+            case 1: {
+                // Get the first and only item
+                const Map::const_iterator checkDesc(map.begin());
+                const std::string& name(checkDesc->first);
+                const typename Creators::const_iterator
+                    creatorIt(creators_.find(name));
+                if (creatorIt == creators_.end()) {
+                    isc_throw_1(LoaderError, "No creator for ACL check " <<
+                                name, description);
+                }
+                if (creatorIt->second->allowListAbbreviation() &&
+                    checkDesc->second->getType() == data::Element::list) {
+                    // Or-abbreviated form - create an OR and put everything
+                    // inside.
+                    const std::vector<data::ConstElementPtr>&
+                        params(checkDesc->second->listValue());
+                    boost::shared_ptr<LogicOperator<AnyOfSpec, Context> >
+                        oper(new LogicOperator<AnyOfSpec, Context>);
+                    for (std::vector<data::ConstElementPtr>::const_iterator
+                             i(params.begin());
+                         i != params.end(); ++i) {
+                        oper->addSubexpression(
+                            creatorIt->second->create(name, *i, *this));
+                    }
+                    return (oper);
+                }
+                // Create the check and return it
+                return (creatorIt->second->create(name, checkDesc->second,
+                                                  *this));
+            }
+            default: {
+                // This is the AND-abbreviated form. We need to create an
+                // AND (or "ALL") operator, loop trough the whole map and
+                // fill it in. We do a small trick - we create bunch of
+                // single-item maps, call this loader recursively (therefore
+                // it will get into the "case 1" branch, where there is
+                // the actual loading) and use the results to fill the map.
+                //
+                // We keep the description the same, there's nothing we could
+                // take out (we could create a new one, but that would be
+                // confusing, as it is used for error messages only).
+                boost::shared_ptr<LogicOperator<AllOfSpec, Context> >
+                    oper(new LogicOperator<AllOfSpec, Context>);
+                for (Map::const_iterator i(map.begin()); i != map.end(); ++i) {
+                    Map singleSubexpr;
+                    singleSubexpr.insert(*i);
+                    oper->addSubexpression(loadCheck(description,
+                                                     singleSubexpr));
+                }
+                return (oper);
+            }
+        }
+    }
+
+    /**
+     * \brief Check that always matches.
+     *
+     * This one is used internally for ACL elements without condition. We may
+     * want to make this publicly accesible sometime maybe, but for now,
+     * there's no need.
+     */
+    class True : public Check<Context> {
+    public:
+        virtual bool matches(const Context&) const { return (true); };
+        virtual unsigned cost() const { return (1); }
+        // We don't write "true" here, as this one was created using empty
+        // input
+        virtual std::string toText() const { return ""; }
+    };
+
+    // silence MSVC warning C4512: assignment operator could not be generated
+    Loader& operator=(Loader const&);
+};
+
+}
+}
+
+/*
+ * This include at the end of the file is unusual. But we need to include it,
+ * we use template classes from there. However, they need to be present only
+ * at instantiation of our class, which will happen below this header.
+ *
+ * The problem is, the header uses us as well, therefore there's a circular
+ * dependency. If we loaded it at the beginning and someone loaded us first,
+ * the logic_check header wouldn't have our definitions. This way, no matter
+ * in which order they are loaded, the definitions from this header will be
+ * above the ones from logic_check.
+ */
+#include "logic_check.h"
+
+#endif
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/acl/logic_check.h b/src/lib/acl/logic_check.h
new file mode 100644
index 0000000..22c0251
--- /dev/null
+++ b/src/lib/acl/logic_check.h
@@ -0,0 +1,295 @@
+// 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.
+
+#ifndef ACL_LOGIC_CHECK_H
+#define ACL_LOGIC_CHECK_H
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 4512)
+#endif
+
+#include "check.h"
+#include "loader.h"
+
+namespace isc {
+namespace acl {
+
+/// \brief Constants for the AnyOf implementation
+class AnyOfSpec {
+public:
+    static bool start() { return (false); }
+    static bool terminate(const bool another) {
+        return (another);
+    }
+};
+
+/// \brief Constants for the AllOf implementation
+class AllOfSpec {
+public:
+    static bool start() { return (true); }
+    static bool terminate(const bool another) {
+        return (!another);
+    }
+};
+
+/**
+ * \brief Logic operators
+ *
+ * This class implements the AllOf and AnyOf compound checks. As their
+ * behaviour is almost the same, the same template class is used. Which
+ * one it is depends on the Mode template parameter. The Mode should be
+ * one of AnyOfSpec or AllOfSpec, which provide some commands for the
+ * internal implementation. It would be nice to provide typedefs for
+ * them, but it is impossible to do so, as we have the Context template
+ * parameter as well and C++ doesn't like templated typedefs.
+ *
+ * The object holds several subexpressions and returns true if all
+ * of the subexpressions return true (in case of AllOfSpec Mode) or
+ * at last one of them return true (in case of AnyOfSpec Mode). If
+ * some subexpression guarantees the result (eg. some returns false
+ * in case of AllOfSpec), the rest is not tried for performance
+ * reasons.
+ */
+template<typename Mode, typename Context>
+class LogicOperator : public CompoundCheck<Context> {
+public:
+    /**
+     * \brief Add another subexpression.
+     *
+     * This adds another subexpression to the list of checked expressions.
+     * This is usually done shortly after the creation, before using the
+     * check for matches.
+     *
+     * Currently there's no way to place the expression into arbitrary place
+     * or to remove it. It might turn out it would be needed in future to
+     * optimise or it might even turn out we need shared pointers for it.
+     *
+     * \param expr The new expression to put inside.
+     */
+    void addSubexpression(const boost::shared_ptr<Check<Context> >& expr) {
+        checks_.push_back(expr);
+    }
+    /**
+     * \brief The current list of subexpressions.
+     */
+    virtual typename CompoundCheck<Context>::Checks getSubexpressions() const {
+        typename CompoundCheck<Context>::Checks result;
+        for (typename Checks::const_iterator i(checks_.begin());
+             i != checks_.end(); ++i) {
+            result.push_back(i->get());
+        }
+        return (result);
+    }
+    /**
+     * \brief The match of the check.
+     *
+     * Runs the subexpressions, one by one, and then decides based on that
+     * what to return.
+     */
+    virtual bool matches(const Context& context) const {
+        /*
+         * This might look slightly complicated. However, this is just
+         * generalized version of multi-and or multi-or. The usual
+         * implementation of multi-and starts with true and if one with
+         * false is found, it turns to be false forever and false is
+         * returned. It is exactly the other way around with or.
+         *
+         * So, if we ever find one that makes it the other one than start
+         * (false in case of and, true in case of or), we can just stop and
+         * return that one right away. If it meets no such expression, we
+         * get to the end and return the default.
+         */
+        for (typename Checks::const_iterator i(checks_.begin());
+             i != checks_.end(); ++i) {
+            if (Mode::terminate((*i)->matches(context))) {
+                return (!Mode::start());
+            }
+        }
+        return (Mode::start());
+    }
+private:
+    /// \brief List of subexpressions
+    typedef typename std::vector<boost::shared_ptr<Check<Context> > > Checks;
+    Checks checks_;
+};
+
+/**
+ * \brief Creator for the LogicOperator compound check.
+ *
+ * This class can load the ANY and ALL operators from JSON. They expect
+ * a list of subexpressions as a parameter, eg. like this:
+ *
+ * \verbatim
+ * {"ANY": [
+ *    {"ip": "1.2.3.4"},
+ *    {"ip": "5.6.7.8"}
+ * ]}
+ * \endverbatim
+ *
+ * It uses the loader to load the subexpressions, therefore whatever is
+ * supported there is supported here as well.
+ *
+ * The Mode template parameter has the same meaning as with LogicOperator,
+ * it is used to know which operators to create.
+ */
+template<typename Mode, typename Context, typename Action = BasicAction>
+class LogicCreator : public Loader<Context, Action>::CheckCreator {
+public:
+    /**
+     * \brief Constructor.
+     *
+     * \param name The name for which the loader will work. In practice,
+     *     it will usually be ANY or ALL (depending on the mode), but
+     *     anything else can be used as well.
+     */
+    LogicCreator(const std::string& name) :
+        name_(name)
+    {}
+    /// \brief Returns vector containing the name.
+    virtual std::vector<std::string> names() const {
+        std::vector<std::string> result;
+        result.push_back(name_);
+        return (result);
+    }
+    /**
+     * \brief Converts a JSON description into the logic operator.
+     *
+     * This is the place where the actual loading happens. It creates
+     * the logic operator and calls the loader on each of the list
+     * elements, placing the result into the logic operator.
+     *
+     * The first parameter is ignored and is there only to match interface.
+     *
+     * \param definition The JSON definition of the subexpressions. This must
+     *     be a list (if it isn't, the LoaderError is thrown) and the elements
+     *     must be loadable by the loader (the exceptions from it are not
+     *     caught).
+     * \param loader The loader to use for loading of subexpressions.
+     */
+    virtual boost::shared_ptr<Check<Context> > create(const std::string&,
+                                                      data::ConstElementPtr
+                                                      definition,
+                                                      const Loader<Context,
+                                                      Action>& loader)
+    {
+        std::vector<data::ConstElementPtr> subexprs;
+        try {
+            subexprs = definition->listValue();
+        }
+        catch (const data::TypeError&) {
+            isc_throw_1(LoaderError, "Logic operator takes list", definition);
+        }
+        boost::shared_ptr<LogicOperator<Mode, Context> >
+            result(new LogicOperator<Mode, Context>);
+        for (std::vector<data::ConstElementPtr>::const_iterator
+                 i(subexprs.begin());
+             i != subexprs.end(); ++i) {
+            result->addSubexpression(loader.loadCheck(*i));
+        }
+        return (result);
+    }
+    virtual bool allowListAbbreviation() const { return (false); }
+private:
+    const std::string name_;
+};
+
+/**
+ * \brief The NOT operator for ACLs.
+ *
+ * This simply returns the negation of whatever returns the subexpression.
+ */
+template<typename Context>
+class NotOperator : public CompoundCheck<Context> {
+public:
+    /**
+     * \brief Constructor
+     *
+     * \param expr The subexpression to be negated by this NOT.
+     */
+    NotOperator(const boost::shared_ptr<Check<Context> >& expr) :
+        expr_(expr)
+    { }
+    /**
+     * \brief The list of subexpressions
+     *
+     * \return The vector will contain single value and it is the expression
+     *     passed by constructor.
+     */
+    virtual typename CompoundCheck<Context>::Checks getSubexpressions() const {
+        typename CompoundCheck<Context>::Checks result;
+        result.push_back(expr_.get());
+        return (result);
+    }
+    /// \brief The matching function
+    virtual bool matches(const Context& context) const {
+        return (!expr_->matches(context));
+    }
+private:
+    /// \brief The subexpression
+    const boost::shared_ptr<Check<Context> > expr_;
+};
+
+template<typename Context, typename Action = BasicAction>
+class NotCreator : public Loader<Context, Action>::CheckCreator {
+public:
+    /**
+     * \brief Constructor
+     *
+     * \param name The name of the NOT operator to be loaded as.
+     */
+    NotCreator(const std::string& name) :
+        name_(name)
+    { }
+    /**
+     * \brief List of the names this loads
+     *
+     * \return Single-value vector containing the name passed to the
+     *     constructor.
+     */
+    virtual std::vector<std::string> names() const {
+        std::vector<std::string> result;
+        result.push_back(name_);
+        return (result);
+    }
+    /// \brief Create the check.
+    virtual boost::shared_ptr<Check<Context> > create(const std::string&,
+                                                      data::ConstElementPtr
+                                                      definition,
+                                                      const Loader<Context,
+                                                      Action>& loader)
+    {
+        return (boost::shared_ptr<Check<Context> >(new NotOperator<Context>(
+                    loader.loadCheck(definition))));
+    }
+    /**
+     * \brief Or-abbreviated form.
+     *
+     * This returns false. In theory, the NOT operator could be used with
+     * the abbreviated form, but it would be confusing. Such syntax is
+     * therefore explicitly forbidden.
+     */
+    virtual bool allowListAbbreviation() const { return (false); }
+public:
+    const std::string name_;
+};
+
+}
+}
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#endif
diff --git a/src/lib/acl/tests/Makefile.am b/src/lib/acl/tests/Makefile.am
new file mode 100644
index 0000000..6369511
--- /dev/null
+++ b/src/lib/acl/tests/Makefile.am
@@ -0,0 +1,40 @@
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+run_unittests_SOURCES = run_unittests.cc
+run_unittests_SOURCES += acl_test.cc
+run_unittests_SOURCES += check_test.cc
+run_unittests_SOURCES += dns_test.cc
+run_unittests_SOURCES += ip_check_unittest.cc
+run_unittests_SOURCES += dnsname_check_unittest.cc
+run_unittests_SOURCES += loader_test.cc
+run_unittests_SOURCES += logcheck.h
+run_unittests_SOURCES += creators.h
+run_unittests_SOURCES += logic_check_test.cc
+run_unittests_SOURCES += sockaddr.h
+
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+
+run_unittests_LDADD = $(GTEST_LDADD)
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+run_unittests_LDADD += $(top_builddir)/src/lib/acl/libacl.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
+run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
+run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.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/acl/libdnsacl.la
+endif
+
+noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/acl/tests/acl_test.cc b/src/lib/acl/tests/acl_test.cc
new file mode 100644
index 0000000..15ffef6
--- /dev/null
+++ b/src/lib/acl/tests/acl_test.cc
@@ -0,0 +1,90 @@
+// 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 <boost/shared_ptr.hpp>
+
+#include "logcheck.h"
+
+using namespace isc::acl;
+using namespace isc::acl::tests;
+using boost::shared_ptr;
+
+namespace {
+
+// Test version of the Acl class. It adds few methods to examine the protected
+// data, but does not change the implementation.
+class TestACL : public ACL<Log> {
+public:
+    TestACL() :
+        ACL<Log>(DROP)
+    {}
+    // Check the stored default action there
+    void checkDefaultAction(BasicAction ac) {
+        EXPECT_EQ(getDefaultAction(), ac);
+    }
+};
+
+// The test fixture. Contains some members so they don't need to be manually
+// created each time and some convenience functions.
+class ACLTest : public ::testing::Test {
+public:
+    ACLTest() :
+        next_check_(0)
+    {}
+    TestACL acl_;
+    Log log_;
+    size_t next_check_;
+    boost::shared_ptr<Check<Log> > getCheck(bool accepts) {
+        return (shared_ptr<Check<Log> >(new ConstCheck(accepts,
+                                                       next_check_++)));
+    }
+};
+
+/*
+ * This tests the default action and that nothing is run if nothing is
+ * inserted (it's hard to imagine otherwise though).
+ *
+ * We use the default ACL unchanged from the test class.
+ */
+TEST_F(ACLTest, emptyRule) {
+    acl_.checkDefaultAction(DROP);
+    EXPECT_EQ(DROP, acl_.execute(log_));
+    // No test was run
+    log_.checkFirst(0);
+}
+
+/*
+ * This tests the default action in case no check matches.
+ */
+TEST_F(ACLTest, noMatch) {
+    acl_.append(getCheck(false), ACCEPT);
+    acl_.append(getCheck(false), REJECT);
+    EXPECT_EQ(DROP, acl_.execute(log_));
+    // The first two checks were actually run (and didn't match)
+    log_.checkFirst(2);
+}
+
+/*
+ * Checks that it takes the first matching check and returns the
+ * value. Also checks that the others aren't run at all.
+ */
+TEST_F(ACLTest, firstMatch) {
+    acl_.append(getCheck(false), ACCEPT);
+    acl_.append(getCheck(true), REJECT);
+    acl_.append(getCheck(true), ACCEPT);
+    EXPECT_EQ(REJECT, acl_.execute(log_));
+    log_.checkFirst(2);
+}
+
+}
diff --git a/src/lib/acl/tests/check_test.cc b/src/lib/acl/tests/check_test.cc
new file mode 100644
index 0000000..e83e8f2
--- /dev/null
+++ b/src/lib/acl/tests/check_test.cc
@@ -0,0 +1,70 @@
+// 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 <gtest/gtest.h>
+#include <acl/check.h>
+
+using namespace isc::acl;
+
+namespace {
+
+// This test has two function. For one, it checks the default implementations
+// do what they should and it makes sure the template actually compiles
+// (as templates are syntax-checked upon instantiation).
+
+// This is a test check that just passes the boolean it gets.
+class Pass : public Check<bool> {
+public:
+    virtual bool matches(const bool& value) const { return (value); }
+};
+
+// This is a simple test compound check. It contains two Pass checks
+// and passes result of the first one.
+
+class First : public CompoundCheck<bool> {
+public:
+    // The internal checks are public, so we can check the addresses
+    Pass first, second;
+    virtual Checks getSubexpressions() const {
+        Checks result;
+        result.push_back(&first);
+        result.push_back(&second);
+        return (result);
+    }
+    virtual bool matches(const bool& value) const {
+        return (first.matches(value));
+    }
+};
+
+TEST(Check, defaultCheckValues) {
+    Pass p;
+    EXPECT_EQ(Check<bool>::UNKNOWN_COST, p.cost());
+    EXPECT_TRUE(p.matches(true));
+    EXPECT_FALSE(p.matches(false));
+    // The exact text is compiler dependant, but we check it returns something
+    // and can be compiled
+    EXPECT_FALSE(p.toText().empty());
+}
+
+TEST(Check, defaultCompoundValues) {
+    First f;
+    EXPECT_EQ(2 * Check<bool>::UNKNOWN_COST, f.cost());
+    EXPECT_TRUE(f.pure());
+    First::Checks c(f.getSubexpressions());
+    ASSERT_EQ(2, c.size());
+    EXPECT_EQ(&f.first, c[0]);
+    EXPECT_EQ(&f.second, c[1]);
+}
+
+}
diff --git a/src/lib/acl/tests/creators.h b/src/lib/acl/tests/creators.h
new file mode 100644
index 0000000..a74c724
--- /dev/null
+++ b/src/lib/acl/tests/creators.h
@@ -0,0 +1,163 @@
+// 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.
+
+// This is not a public header, but some code shared between tests
+// This one contains various creators to test the loader and other creators
+
+#ifndef CREATORS_H
+#define CREATORS_H
+
+#include "logcheck.h"
+
+#include <cc/data.h>
+#include <acl/loader.h>
+#include <string>
+
+namespace isc {
+namespace acl {
+namespace tests {
+
+// A check that doesn't check anything but remembers it's own name
+// and data
+class NamedCheck : public Check<Log> {
+public:
+    NamedCheck(const std::string& name, isc::data::ConstElementPtr data) :
+        name_(name),
+        data_(data)
+    {}
+    virtual bool matches(const Log&) const { return (true); }
+    const std::string name_;
+    const isc::data::ConstElementPtr data_;
+private:
+    // silence MSVC warning C4512: assignment operator could not be generated
+    NamedCheck& operator=(NamedCheck const&);
+};
+
+// The creator of NamedCheck
+class NamedCreator : public Loader<Log>::CheckCreator {
+public:
+    NamedCreator(const std::string& name, bool abbreviatedList = true) :
+        abbreviated_list_(abbreviatedList)
+    {
+        names_.push_back(name);
+    }
+    NamedCreator(const std::vector<std::string>& names) :
+        names_(names),
+        abbreviated_list_(true)
+    {}
+    std::vector<std::string> names() const {
+        return (names_);
+    }
+    boost::shared_ptr<Check<Log> > create(const std::string& name,
+                                          isc::data::ConstElementPtr data,
+                                          const Loader<Log>&)
+    {
+        bool found(false);
+        for (std::vector<std::string>::const_iterator i(names_.begin());
+             i != names_.end(); ++i) {
+            if (*i == name) {
+                found = true;
+                break;
+            }
+        }
+        EXPECT_TRUE(found) << "Name " << name << " passed to creator which "
+            "doesn't handle it.";
+        return (boost::shared_ptr<Check<Log> >(new NamedCheck(name, data)));
+    }
+    bool allowListAbbreviation() const {
+        return (abbreviated_list_);
+    }
+private:
+    std::vector<std::string> names_;
+    const bool abbreviated_list_;
+    // silence MSVC warning C4512: assignment operator could not be generated
+    NamedCreator& operator=(NamedCreator const&);
+};
+
+// To be thrown in tests internally
+class TestCreatorError {};
+
+// This will throw every time it should create something
+class ThrowCreator : public Loader<Log>::CheckCreator {
+public:
+    std::vector<std::string> names() const {
+        std::vector<std::string> result;
+        result.push_back("throw");
+        return (result);
+    }
+    boost::shared_ptr<Check<Log> > create(const std::string&,
+                                          isc::data::ConstElementPtr,
+                                          const Loader<Log>&)
+    {
+        throw TestCreatorError();
+    }
+};
+
+// This throws whenever the match is called on it
+class ThrowCheck : public Check<Log> {
+public:
+    virtual bool matches(const Log&) const {
+        throw TestCreatorError();
+    }
+};
+
+// And creator for it
+class ThrowCheckCreator : public Loader<Log>::CheckCreator {
+public:
+    std::vector<std::string> names() const {
+        std::vector<std::string> result;
+        result.push_back("throwcheck");
+        return (result);
+    }
+    boost::shared_ptr<Check<Log> > create(const std::string&,
+                                          isc::data::ConstElementPtr,
+                                          const Loader<Log>&)
+    {
+        return (boost::shared_ptr<Check<Log> >(new ThrowCheck()));
+    }
+};
+
+class LogCreator : public Loader<Log>::CheckCreator {
+public:
+    std::vector<std::string> names() const {
+        std::vector<std::string> result;
+        result.push_back("logcheck");
+        return (result);
+    }
+    /*
+     * For simplicity, we just take two values as a list, first is the
+     * logging cell used, the second is result of the check. No error checking
+     * is done, if there's bug in the test, it will throw TypeError for us.
+     */
+    boost::shared_ptr<Check<Log> > create(const std::string&,
+                                          isc::data::ConstElementPtr definition,
+                                          const Loader<Log>&)
+    {
+        std::vector<isc::data::ConstElementPtr> list(definition->listValue());
+        int logpos(list[0]->intValue());
+        bool accept(list[1]->boolValue());
+        return (boost::shared_ptr<ConstCheck>(new ConstCheck(accept, logpos)));
+    }
+    // We take a list, so don't interpret it for us
+    virtual bool allowListAbbreviation() const { return (false); }
+};
+
+}
+}
+}
+#endif
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/acl/tests/dns_test.cc b/src/lib/acl/tests/dns_test.cc
new file mode 100644
index 0000000..b3ddbf4
--- /dev/null
+++ b/src/lib/acl/tests/dns_test.cc
@@ -0,0 +1,271 @@
+// 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 <algorithm>
+#include <vector>
+#include <string>
+
+#include <boost/scoped_ptr.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <exceptions/exceptions.h>
+
+#include <dns/name.h>
+#include <dns/tsigkey.h>
+#include <dns/tsigrecord.h>
+#include <dns/rdataclass.h>
+
+#include <cc/data.h>
+#include <acl/dns.h>
+#include <acl/loader.h>
+#include <acl/check.h>
+#include <acl/ip_check.h>
+
+#include "sockaddr.h"
+
+#include <gtest/gtest.h>
+
+using namespace std;
+using boost::scoped_ptr;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::data;
+using namespace isc::acl;
+using namespace isc::acl::dns;
+using isc::acl::LoaderError;
+
+namespace {
+
+TEST(DNSACL, getRequestLoader) {
+    dns::RequestLoader* l(&getRequestLoader());
+    ASSERT_TRUE(l != NULL);
+    EXPECT_EQ(l, &getRequestLoader());
+    EXPECT_NO_THROW(l->load(Element::fromJSON("[{\"action\": \"DROP\"}]")));
+
+    // Confirm it can load the ACl syntax acceptable to a default creator.
+    // Tests to see whether the loaded rules work correctly will be in
+    // other dedicated tests below.
+    EXPECT_NO_THROW(l->load(Element::fromJSON("[{\"action\": \"DROP\","
+                                              "  \"from\": \"192.0.2.1\"}]")));
+}
+
+class RequestCheckCreatorTest : public ::testing::Test {
+protected:
+    dns::internal::RequestCheckCreator creator_;
+
+    typedef boost::shared_ptr<const dns::RequestCheck> ConstRequestCheckPtr;
+    ConstRequestCheckPtr check_;
+};
+
+TEST_F(RequestCheckCreatorTest, names) {
+    const vector<string> names = creator_.names();
+    EXPECT_EQ(2, names.size());
+    EXPECT_TRUE(find(names.begin(), names.end(), "from") != names.end());
+    EXPECT_TRUE(find(names.begin(), names.end(), "key") != names.end());
+}
+
+TEST_F(RequestCheckCreatorTest, allowListAbbreviation) {
+    EXPECT_FALSE(creator_.allowListAbbreviation());
+}
+
+// The following two tests check the creator for the form of
+// 'from: "IP prefix"'.  We don't test many variants of prefixes, which
+// are done in the tests for IPCheck.
+TEST_F(RequestCheckCreatorTest, createIPv4Check) {
+    check_ = creator_.create("from", Element::fromJSON("\"192.0.2.1\""),
+                             getRequestLoader());
+    const dns::internal::RequestIPCheck& ipcheck_ =
+        dynamic_cast<const dns::internal::RequestIPCheck&>(*check_);
+    EXPECT_EQ(AF_INET, ipcheck_.getFamily());
+    EXPECT_EQ(32, ipcheck_.getPrefixlen());
+    const vector<uint8_t> check_address(ipcheck_.getAddress());
+    ASSERT_EQ(4, check_address.size());
+    const uint8_t expected_address[] = { 192, 0, 2, 1 };
+    EXPECT_TRUE(equal(check_address.begin(), check_address.end(),
+                      expected_address));
+}
+
+TEST_F(RequestCheckCreatorTest, createIPv6Check) {
+    check_ = creator_.create("from",
+                             Element::fromJSON("\"2001:db8::5300/120\""),
+                             getRequestLoader());
+    const dns::internal::RequestIPCheck& ipcheck =
+        dynamic_cast<const dns::internal::RequestIPCheck&>(*check_);
+    EXPECT_EQ(AF_INET6, ipcheck.getFamily());
+    EXPECT_EQ(120, ipcheck.getPrefixlen());
+    const vector<uint8_t> check_address(ipcheck.getAddress());
+    ASSERT_EQ(16, check_address.size());
+    const uint8_t expected_address[] = { 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00,
+                                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                         0x00, 0x00, 0x53, 0x00 };
+    EXPECT_TRUE(equal(check_address.begin(), check_address.end(),
+                      expected_address));
+}
+
+TEST_F(RequestCheckCreatorTest, createTSIGKeyCheck) {
+    check_ = creator_.create("key", Element::fromJSON("\"key.example.com\""),
+                             getRequestLoader());
+    const dns::internal::RequestKeyCheck& keycheck =
+        dynamic_cast<const dns::internal::RequestKeyCheck&>(*check_);
+    EXPECT_EQ(Name("key.example.com"), keycheck.getName());
+}
+
+TEST_F(RequestCheckCreatorTest, badCreate) {
+    // Invalid name
+    EXPECT_THROW(creator_.create("bad", Element::fromJSON("\"192.0.2.1\""),
+                                 getRequestLoader()), LoaderError);
+
+    // Invalid type of parameter
+    EXPECT_THROW(creator_.create("from", Element::fromJSON("4"),
+                                 getRequestLoader()),
+                 isc::data::TypeError);
+    EXPECT_THROW(creator_.create("from", Element::fromJSON("[]"),
+                                 getRequestLoader()),
+                 isc::data::TypeError);
+    EXPECT_THROW(creator_.create("key", Element::fromJSON("1"),
+                                 getRequestLoader()),
+                 isc::data::TypeError);
+    EXPECT_THROW(creator_.create("key", Element::fromJSON("{}"),
+                                 getRequestLoader()),
+                 isc::data::TypeError);
+
+    // Syntax error for IPCheck
+    EXPECT_THROW(creator_.create("from", Element::fromJSON("\"bad\""),
+                                 getRequestLoader()),
+                 isc::InvalidParameter);
+
+    // Syntax error for Name (key) Check
+    EXPECT_THROW(creator_.create("key", Element::fromJSON("\"bad..name\""),
+                                 getRequestLoader()),
+                 EmptyLabel);
+
+    // NULL pointer
+    EXPECT_THROW(creator_.create("from", ConstElementPtr(), getRequestLoader()),
+                 LoaderError);
+}
+
+class RequestCheckTest : public ::testing::Test {
+protected:
+    typedef boost::shared_ptr<const dns::RequestCheck> ConstRequestCheckPtr;
+
+    // A helper shortcut to create a single IP check for the given prefix.
+    ConstRequestCheckPtr createIPCheck(const string& prefix) {
+        return (creator_.create("from", Element::fromJSON(
+                                    string("\"") + prefix + string("\"")),
+                                getRequestLoader()));
+    }
+
+    // A helper shortcut to create a single Name (key) check for the given
+    // name.
+    ConstRequestCheckPtr createKeyCheck(const string& key_name) {
+        return (creator_.create("key", Element::fromJSON(
+                                    string("\"") + key_name + string("\"")),
+                                getRequestLoader()));
+    }
+
+    // create a one time request context for a specific test.  Note that
+    // getSockaddr() uses a static storage, so it cannot be called more than
+    // once in a single test.
+    const dns::RequestContext& getRequest4(const TSIGRecord* tsig = NULL) {
+        ipaddr.reset(new IPAddress(tests::getSockAddr("192.0.2.1")));
+        request.reset(new dns::RequestContext(*ipaddr, tsig));
+        return (*request);
+    }
+    const dns::RequestContext& getRequest6(const TSIGRecord* tsig = NULL) {
+        ipaddr.reset(new IPAddress(tests::getSockAddr("2001:db8::1")));
+        request.reset(new dns::RequestContext(*ipaddr, tsig));
+        return (*request);
+    }
+
+    // create a one time TSIG Record for a specific test.  The only parameter
+    // of the record that matters is the key name; others are hardcoded with
+    // arbitrarily chosen values.
+    const TSIGRecord* getTSIGRecord(const string& key_name) {
+        tsig_rdata.reset(new any::TSIG(TSIGKey::HMACMD5_NAME(), 0, 0, 0, NULL,
+                                       0, 0, 0, NULL));
+        tsig.reset(new TSIGRecord(Name(key_name), *tsig_rdata));
+        return (tsig.get());
+    }
+
+private:
+    scoped_ptr<IPAddress> ipaddr;
+    scoped_ptr<dns::RequestContext> request;
+    scoped_ptr<any::TSIG> tsig_rdata;
+    scoped_ptr<TSIGRecord> tsig;
+    dns::internal::RequestCheckCreator creator_;
+};
+
+TEST_F(RequestCheckTest, checkIPv4) {
+    // Exact match
+    EXPECT_TRUE(createIPCheck("192.0.2.1")->matches(getRequest4()));
+    // Exact match (negative)
+    EXPECT_FALSE(createIPCheck("192.0.2.53")->matches(getRequest4()));
+    // Prefix match
+    EXPECT_TRUE(createIPCheck("192.0.2.0/24")->matches(getRequest4()));
+    // Prefix match (negative)
+    EXPECT_FALSE(createIPCheck("192.0.1.0/24")->matches(getRequest4()));
+    // Address family mismatch (the first 4 bytes of the IPv6 address has the
+    // same binary representation as the client's IPv4 address, which
+    // shouldn't confuse the match logic)
+    EXPECT_FALSE(createIPCheck("c000:0201::")->matches(getRequest4()));
+}
+
+TEST_F(RequestCheckTest, checkIPv6) {
+    // The following are a set of tests of the same concept as checkIPv4
+    EXPECT_TRUE(createIPCheck("2001:db8::1")->matches(getRequest6()));
+    EXPECT_FALSE(createIPCheck("2001:db8::53")->matches(getRequest6()));
+    EXPECT_TRUE(createIPCheck("2001:db8::/64")->matches(getRequest6()));
+    EXPECT_FALSE(createIPCheck("2001:db8:1::/64")->matches(getRequest6()));
+    EXPECT_FALSE(createIPCheck("32.1.13.184")->matches(getRequest6()));
+}
+
+TEST_F(RequestCheckTest, checkTSIGKey) {
+    EXPECT_TRUE(createKeyCheck("key.example.com")->matches(
+                    getRequest4(getTSIGRecord("key.example.com"))));
+    EXPECT_FALSE(createKeyCheck("key.example.com")->matches(
+                     getRequest4(getTSIGRecord("badkey.example.com"))));
+
+    // Same for IPv6 (which shouldn't matter)
+    EXPECT_TRUE(createKeyCheck("key.example.com")->matches(
+                    getRequest6(getTSIGRecord("key.example.com"))));
+    EXPECT_FALSE(createKeyCheck("key.example.com")->matches(
+                     getRequest6(getTSIGRecord("badkey.example.com"))));
+
+    // by default the test request doesn't have a TSIG key, which shouldn't
+    // match any key checks.
+    EXPECT_FALSE(createKeyCheck("key.example.com")->matches(getRequest4()));
+    EXPECT_FALSE(createKeyCheck("key.example.com")->matches(getRequest6()));
+}
+
+// The following tests test only the creators are registered, they are tested
+// elsewhere
+
+TEST(DNSACL, notLoad) {
+    EXPECT_NO_THROW(getRequestLoader().loadCheck(isc::data::Element::fromJSON(
+        "{\"NOT\": {\"from\": \"192.0.2.1\"}}")));
+}
+
+TEST(DNSACL, allLoad) {
+    EXPECT_NO_THROW(getRequestLoader().loadCheck(isc::data::Element::fromJSON(
+        "{\"ALL\": [{\"from\": \"192.0.2.1\"}]}")));
+}
+
+TEST(DNSACL, anyLoad) {
+    EXPECT_NO_THROW(getRequestLoader().loadCheck(isc::data::Element::fromJSON(
+        "{\"ANY\": [{\"from\": \"192.0.2.1\"}]}")));
+}
+
+}
diff --git a/src/lib/acl/tests/dnsname_check_unittest.cc b/src/lib/acl/tests/dnsname_check_unittest.cc
new file mode 100644
index 0000000..95b5314
--- /dev/null
+++ b/src/lib/acl/tests/dnsname_check_unittest.cc
@@ -0,0 +1,59 @@
+// 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 <gtest/gtest.h>
+
+#include <dns/name.h>
+
+#include <acl/dnsname_check.h>
+
+using namespace isc::dns;
+using namespace isc::acl::dns;
+
+// Provide a specialization of the DNSNameCheck::matches() method.
+namespace isc  {
+namespace acl {
+namespace dns {
+template <>
+bool NameCheck<Name>::matches(const Name& name) const {
+    return (name_ == name);
+}
+} // namespace dns
+} // namespace acl
+} // namespace isc
+
+namespace {
+TEST(DNSNameCheck, construct) {
+    EXPECT_EQ(Name("example.com"),
+              NameCheck<Name>(Name("example.com")).getName());
+
+    // Construct the same check with an explicit trailing dot.  Should result
+    // in the same result.
+    EXPECT_EQ(Name("example.com"),
+              NameCheck<Name>(Name("example.com.")).getName());
+}
+
+TEST(DNSNameCheck, match) {
+    NameCheck<Name> check(Name("example.com"));
+    EXPECT_TRUE(check.matches(Name("example.com")));
+    EXPECT_FALSE(check.matches(Name("example.org")));
+
+    // comparison is case insensitive
+    EXPECT_TRUE(check.matches(Name("EXAMPLE.COM")));
+
+    // this is exact match.  so super/sub domains don't match
+    EXPECT_FALSE(check.matches(Name("com")));
+    EXPECT_FALSE(check.matches(Name("www.example.com")));
+}
+} // Unnamed namespace
diff --git a/src/lib/acl/tests/ip_check_unittest.cc b/src/lib/acl/tests/ip_check_unittest.cc
new file mode 100644
index 0000000..404d70b
--- /dev/null
+++ b/src/lib/acl/tests/ip_check_unittest.cc
@@ -0,0 +1,619 @@
+// 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.
+
+#ifndef _WIN32
+#include <sys/types.h>
+#include <sys/socket.h>
+#endif
+#include <string.h>
+
+#include <gtest/gtest.h>
+#include <acl/ip_check.h>
+
+#include "sockaddr.h"
+
+using namespace isc::acl;
+using namespace isc::acl::internal;
+using namespace std;
+
+namespace {
+const size_t IPV4_SIZE = 4;
+const size_t IPV6_SIZE = 16;
+
+// Simple struct holding either an IPV4 or IPV6 address.  This is the "Context"
+// used for the tests.
+//
+// The structure is also used for converting an IPV4 address to a four-byte
+// array.
+struct GeneralAddress {
+    int             family;     // Family of the address
+    vector<uint8_t> addr;       // Address type.  Size indicates what it holds
+
+    // Convert uint32_t address in host-byte order to a uint8_t vector in
+    // network-byte order.
+    vector<uint8_t> convertUint32(uint32_t address) {
+        BOOST_STATIC_ASSERT(sizeof(uint32_t) == IPV4_SIZE);
+
+        vector<uint8_t> result(IPV4_SIZE);
+
+        // Address is in network-byte order, so copy to the array.  The
+        // MS byte is at the lowest address.
+        result[3] = address & 0xff;
+        result[2] = (address >> 8) & 0xff;
+        result[1] = (address >> 16) & 0xff;
+        result[0] = (address >> 24) & 0xff;
+
+        return (result);
+    }
+
+    // Convenience constructor for V4 address.  As it is not marked as explicit,
+    // it allows the automatic promotion of a uint32_t to a GeneralAddress data
+    // type in calls to matches().
+    GeneralAddress(uint32_t address) : family(AF_INET), addr()
+    {
+        addr = convertUint32(address);
+    }
+
+    // Convenience constructor for V6 address.  As it is not marked as explicit,
+    // it allows the automatic promotion of a vector<uint8_t> to a
+    // GeneralAddress data type in calls to matches().
+    GeneralAddress(const vector<uint8_t>& address) : family(AF_INET6),
+                                                     addr(address)
+    {
+        if (address.size() != IPV6_SIZE) {
+            isc_throw(isc::InvalidParameter, "vector passed to GeneralAddress "
+                      "constructor is " << address.size() << " bytes long - it "
+                      "should be " << IPV6_SIZE << " bytes instead");
+        }
+    }
+
+    // A couple of convenience methods for checking equality with different
+    // representations of an address.
+
+    // Check that the IPV4 address is the same as that given.
+    bool equals(uint32_t address) {
+        if (family == AF_INET) {
+            const vector<uint8_t> byte_address = convertUint32(address);
+            return (equal(byte_address.begin(), byte_address.end(),
+                           addr.begin()));
+        }
+        return (false);
+    }
+
+    // Check that the array is equal to that given.
+    bool equals(const vector<uint8_t>& byte_address) {
+        if (addr.size() == byte_address.size()) {
+            return (equal(byte_address.begin(), byte_address.end(),
+                           addr.begin()));
+        }
+        return (false);
+    }
+};
+} // Unnamed namespace
+
+// Provide a specialisation of the IPCheck::matches() method for the
+// GeneralAddress class.
+
+namespace isc  {
+namespace acl {
+template <>
+bool IPCheck<GeneralAddress>::matches(const GeneralAddress& address) const {
+    return (compare(&address.addr[0], address.family));
+}
+} // namespace acl
+} // namespace isc
+
+namespace {
+/// *** Free Function Tests ***
+
+// Test the createMask() function.
+TEST(IPFunctionCheck, CreateMask) {
+
+    // Invalid arguments should throw.
+    EXPECT_THROW(createMask(9), isc::OutOfRange);
+
+    // Check on all possible 8-bit values.
+    uint16_t expected = 0xff00;
+    for (size_t i = 0; i <= 8; ++i, expected >>= 1) {
+        EXPECT_EQ(static_cast<uint8_t>(expected & 0xff), createMask(i));
+    }
+}
+
+// Test the splitIPAddress() function.
+TEST(IPFunctionCheck, SplitIPAddress) {
+    pair<string, uint32_t> result;
+
+    result = splitIPAddress("192.0.2.1");
+    EXPECT_EQ(string("192.0.2.1"), result.first);
+    EXPECT_EQ(-1, result.second);
+
+    result = splitIPAddress("192.0.2.1/24");
+    EXPECT_EQ(string("192.0.2.1"), result.first);
+    EXPECT_EQ(24, result.second);
+
+    result = splitIPAddress("2001:db8::/128");
+    EXPECT_EQ(string("2001:db8::"), result.first);
+    EXPECT_EQ(128, result.second);
+
+    result = splitIPAddress("192.0.2.1/0");
+    EXPECT_EQ(string("192.0.2.1"), result.first);
+    EXPECT_EQ(0, result.second);
+
+    EXPECT_THROW(splitIPAddress("192.0.2.43/27 "), isc::InvalidParameter);
+    EXPECT_THROW(splitIPAddress("192.0.2.43/-1"), isc::InvalidParameter);
+    EXPECT_THROW(splitIPAddress("192.0.2.43//1"), isc::InvalidParameter);
+    EXPECT_THROW(splitIPAddress("192.0.2.43/1/"), isc::InvalidParameter);
+    EXPECT_THROW(splitIPAddress("/192.0.2.43/1"), isc::InvalidParameter);
+    EXPECT_THROW(splitIPAddress("2001:db8::/xxxx"), isc::InvalidParameter);
+    EXPECT_THROW(splitIPAddress("2001:db8::/32/s"), isc::InvalidParameter);
+    EXPECT_THROW(splitIPAddress("1/"), isc::InvalidParameter);
+    EXPECT_THROW(splitIPAddress("/1"), isc::InvalidParameter);
+    EXPECT_THROW(splitIPAddress(" 1/ "), isc::InvalidParameter);
+}
+
+TEST(IPAddress, constructIPv4) {
+    IPAddress ipaddr(tests::getSockAddr("192.0.2.1"));
+    const char expected_data[4] = { 192, 0, 2, 1 };
+    EXPECT_EQ(AF_INET, ipaddr.getFamily());
+    EXPECT_EQ(4, ipaddr.getLength());
+    EXPECT_EQ(0, memcmp(expected_data, ipaddr.getData(), 4));
+}
+
+TEST(IPAddress, constructIPv6) {
+    IPAddress ipaddr(tests::getSockAddr("2001:db8:1234:abcd::53"));
+    const char expected_data[16] = { 0x20, 0x01, 0x0d, 0xb8, 0x12, 0x34, 0xab,
+                                     0xcd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                     0x00, 0x53 };
+    EXPECT_EQ(AF_INET6, ipaddr.getFamily());
+    EXPECT_EQ(16, ipaddr.getLength());
+    EXPECT_EQ(0, memcmp(expected_data, ipaddr.getData(), 16));
+}
+
+TEST(IPAddress, badConstruct) {
+    struct sockaddr sa;
+    sa.sa_family = AF_UNSPEC;
+    EXPECT_THROW(IPAddress ipaddr(sa), isc::BadValue);
+}
+
+// *** IPv4 Tests ***
+
+TEST(IPCheck, V4StringConstructor) {
+
+    // Constructor with no prefix length given (32 is assumed).
+    IPCheck<GeneralAddress> acl1("192.0.2.255");
+    EXPECT_EQ(32, acl1.getPrefixlen());
+    EXPECT_EQ(AF_INET, acl1.getFamily());
+
+    vector<uint8_t> stored1 = acl1.getAddress();
+    EXPECT_EQ(IPV4_SIZE, stored1.size());
+    GeneralAddress expected1(0xc00002ff);
+    EXPECT_TRUE(expected1.equals(stored1));
+
+    // Constructor with valid mask given
+    IPCheck<GeneralAddress> acl2("192.0.2.0/24");
+    EXPECT_EQ(24, acl2.getPrefixlen());
+    EXPECT_EQ(AF_INET, acl2.getFamily());
+
+    vector<uint8_t> stored2 = acl2.getAddress();
+    EXPECT_EQ(IPV4_SIZE, stored2.size());
+    GeneralAddress expected2(0xc0000200);
+    EXPECT_TRUE(expected2.equals(stored2));
+
+    // More valid masks
+    IPCheck<GeneralAddress> acl3("192.0.2.1/0");
+    EXPECT_EQ(0, acl3.getPrefixlen());
+    EXPECT_EQ(AF_INET, acl3.getFamily());
+
+    vector<uint8_t> stored3 = acl3.getAddress();
+    EXPECT_EQ(IPV4_SIZE, stored3.size());
+    GeneralAddress expected3(0xc0000201);
+    EXPECT_TRUE(expected3.equals(stored3));
+
+    IPCheck<GeneralAddress> acl4("192.0.2.2/32");
+    EXPECT_EQ(32, acl4.getPrefixlen());
+    EXPECT_EQ(AF_INET, acl4.getFamily());
+
+    vector<uint8_t> stored4 = acl4.getAddress();
+    EXPECT_EQ(IPV4_SIZE, stored4.size());
+    GeneralAddress expected4(0xc0000202);
+    EXPECT_TRUE(expected4.equals(stored4));
+
+    // Any match
+    IPCheck<GeneralAddress> acl5("any4");
+    EXPECT_EQ(0, acl5.getPrefixlen());
+    EXPECT_EQ(AF_INET, acl5.getFamily());
+
+    vector<uint8_t> stored5 = acl5.getAddress();
+    EXPECT_EQ(IPV4_SIZE, stored5.size());
+    GeneralAddress expected5(0);
+    EXPECT_TRUE(expected5.equals(stored5));
+
+    // Invalid prefix lengths
+    EXPECT_THROW(IPCheck<GeneralAddress>("192.0.2.0/33"), isc::OutOfRange);
+
+    // ... and invalid strings
+    EXPECT_THROW(IPCheck<GeneralAddress>("192.0.2.0/-1"),
+                 isc::InvalidParameter);
+    EXPECT_THROW(IPCheck<GeneralAddress>("192.0.2.0/24/3"),
+                 isc::InvalidParameter);
+    EXPECT_THROW(IPCheck<GeneralAddress>("192.0.2.0/ww"),
+                 isc::InvalidParameter);
+    EXPECT_THROW(IPCheck<GeneralAddress>("aa.255.255.0/ww"),
+                 isc::InvalidParameter);
+}
+
+TEST(IPCheck, V4CopyConstructor) {
+    IPCheck<GeneralAddress> acl1("192.0.2.1/24");
+    IPCheck<GeneralAddress> acl2(acl1);
+
+    EXPECT_EQ(acl1.getPrefixlen(), acl2.getPrefixlen());
+    EXPECT_EQ(acl1.getFamily(), acl2.getFamily());
+
+    vector<uint8_t> net1 = acl1.getMask();
+    vector<uint8_t> net2 = acl2.getMask();
+    EXPECT_EQ(net1.size(), net2.size());
+    EXPECT_TRUE(equal(net1.begin(), net1.end(), net2.begin()));
+
+    net1 = acl1.getAddress();
+    net2 = acl2.getAddress();
+    EXPECT_EQ(net1.size(), net2.size());
+    EXPECT_TRUE(equal(net1.begin(), net1.end(), net2.begin()));
+}
+
+TEST(IPCheck, V4AssignmentOperator) {
+    IPCheck<GeneralAddress> acl1("192.0.2.0/24");
+    IPCheck<GeneralAddress> acl2("192.0.2.128/25");
+    acl2 = acl1;
+
+    EXPECT_EQ(acl1.getPrefixlen(), acl2.getPrefixlen());
+    EXPECT_EQ(acl1.getFamily(), acl2.getFamily());
+
+    vector<uint8_t> net1 = acl1.getMask();
+    vector<uint8_t> net2 = acl2.getMask();
+    EXPECT_EQ(net1.size(), net2.size());
+    EXPECT_TRUE(equal(net1.begin(), net1.end(), net2.begin()));
+
+    net1 = acl1.getAddress();
+    net2 = acl2.getAddress();
+    EXPECT_EQ(net1.size(), net2.size());
+    EXPECT_TRUE(equal(net1.begin(), net1.end(), net2.begin()));
+}
+
+// Check that the comparison works - note that "matches" just calls the
+// internal compare() code. (Also note that the argument to matches() will be
+// automatically converted to the GeneralAddress data type used for the tests
+// because of its constructor taking a uint32_t argument.
+
+TEST(IPCheck, V4Compare) {
+    // Exact address - match if given address matches stored address.
+    IPCheck<GeneralAddress> acl1("192.0.2.255/32");
+    EXPECT_TRUE(acl1.matches(0xc00002ff));
+    EXPECT_FALSE(acl1.matches(0xc00002fe));
+    EXPECT_FALSE(acl1.matches(0x13457f13));
+
+    IPCheck<GeneralAddress> acl2("192.0.2.255/27");
+    EXPECT_TRUE(acl2.matches(0xc00002ff));
+    EXPECT_TRUE(acl2.matches(0xc00002fe));
+    EXPECT_TRUE(acl2.matches(0xc00002ee));
+    EXPECT_FALSE(acl2.matches(0xc00002de));
+    EXPECT_FALSE(acl2.matches(0xd00002fe));
+    EXPECT_FALSE(acl2.matches(0x13457f13));
+
+    // Match if "any4" is specified
+    IPCheck<GeneralAddress> acl3("any4");
+    EXPECT_TRUE(acl3.matches(0xc00002ff));
+    EXPECT_TRUE(acl3.matches(0xc00002fe));
+    EXPECT_TRUE(acl3.matches(0xc00002ee));
+    EXPECT_TRUE(acl3.matches(0xc00002de));
+    EXPECT_TRUE(acl3.matches(0xd00002fe));
+    EXPECT_TRUE(acl3.matches(0x13457f13));
+
+    IPCheck<GeneralAddress> acl4("0.0.0.0/0");
+    EXPECT_TRUE(acl4.matches(0xc00002ff));
+    EXPECT_TRUE(acl4.matches(0xc00002fe));
+    EXPECT_TRUE(acl4.matches(0xc00002ee));
+    EXPECT_TRUE(acl4.matches(0xc00002de));
+    EXPECT_TRUE(acl4.matches(0xd00002fe));
+    EXPECT_TRUE(acl4.matches(0x13457f13));
+
+    IPCheck<GeneralAddress> acl5("192.0.2.255/0");
+    EXPECT_TRUE(acl5.matches(0xc00002ff));
+    EXPECT_TRUE(acl5.matches(0xc00002fe));
+    EXPECT_TRUE(acl5.matches(0xc00002ee));
+    EXPECT_TRUE(acl5.matches(0xc00002de));
+    EXPECT_TRUE(acl5.matches(0xd00002fe));
+    EXPECT_TRUE(acl5.matches(0x13457f13));
+}
+
+// *** IPV6 Tests ***
+
+// Some constants used in the tests
+
+const char* V6ADDR_1_STRING = "2001:0db8:1122:3344:5566:7788:99aa:bbcc";
+const uint8_t V6ADDR_1[] = {
+    0x20, 0x01, 0x0d, 0xb8, 0x11, 0x22, 0x33, 0x44,
+    0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc
+};
+
+const char* V6ADDR_2_STRING = "2001:0db8::dead:beef";
+const uint8_t V6ADDR_2[] = {
+    0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef
+};
+
+// Identical to V6ADDR_2 to 48 bits
+const uint8_t V6ADDR_2_48[] = {
+    0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0xff, 0x66,
+    0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef
+};
+
+// Identical to V6ADDR_2 to 49 bits
+const uint8_t V6ADDR_2_49[] = {
+    0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x7f, 0x66,
+    0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef
+};
+
+// Identical to V6ADDR_2 to 50 bits
+const uint8_t V6ADDR_2_50[] = {
+    0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x3f, 0x66,
+    0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef
+};
+
+// Identical to V6ADDR_2 to 51 bits
+const uint8_t V6ADDR_2_51[] = {
+    0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x1f, 0x66,
+    0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef
+};
+
+// Identical to V6ADDR_2 to 51 bits
+const uint8_t V6ADDR_2_52[] = {
+    0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x0f, 0x66,
+    0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef
+};
+
+// Identical to V6ADDR_2 to 127 bits
+const uint8_t V6ADDR_2_127[] = {
+    0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xee
+};
+
+const uint8_t V6ADDR_3[] = {
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
+};
+
+const uint8_t V6ADDR_4[] = {
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+TEST(IPCheck, V6StringConstructor) {
+    IPCheck<GeneralAddress> acl1(V6ADDR_1_STRING);
+    vector<uint8_t> address = acl1.getAddress();
+
+    EXPECT_EQ(128, acl1.getPrefixlen());
+    EXPECT_EQ(AF_INET6, acl1.getFamily());
+    EXPECT_EQ(IPV6_SIZE, address.size());
+    EXPECT_TRUE(equal(address.begin(), address.end(), V6ADDR_1));
+
+    IPCheck<GeneralAddress> acl2(string(V6ADDR_2_STRING) + string("/51"));
+    address = acl2.getAddress();
+    EXPECT_EQ(IPV6_SIZE, address.size());
+    EXPECT_EQ(51, acl2.getPrefixlen());
+    EXPECT_EQ(AF_INET6, acl2.getFamily());
+    EXPECT_TRUE(equal(address.begin(), address.end(), V6ADDR_2));
+
+    IPCheck<GeneralAddress> acl3(string(V6ADDR_2_STRING) + string("/127"));
+    address = acl3.getAddress();
+    EXPECT_EQ(IPV6_SIZE, address.size());
+    EXPECT_EQ(127, acl3.getPrefixlen());
+    EXPECT_EQ(AF_INET6, acl3.getFamily());
+    EXPECT_TRUE(equal(address.begin(), address.end(), V6ADDR_2));
+
+    IPCheck<GeneralAddress> acl4("::1");
+    address = acl4.getAddress();
+    EXPECT_EQ(IPV6_SIZE, address.size());
+    EXPECT_EQ(128, acl4.getPrefixlen());
+    EXPECT_EQ(AF_INET6, acl4.getFamily());
+    EXPECT_TRUE(equal(address.begin(), address.end(), V6ADDR_3));
+
+    // Any match.  In these cases, the address should all be zeroes.
+    IPCheck<GeneralAddress> acl5("any6");
+    address = acl5.getAddress();
+    EXPECT_EQ(IPV6_SIZE, address.size());
+    EXPECT_EQ(0, acl5.getPrefixlen());
+    EXPECT_EQ(AF_INET6, acl5.getFamily());
+    EXPECT_TRUE(equal(address.begin(), address.end(), V6ADDR_4));
+
+    IPCheck<GeneralAddress> acl6("::/0");
+    address = acl6.getAddress();
+    EXPECT_EQ(0, acl6.getPrefixlen());
+    EXPECT_EQ(AF_INET6, acl6.getFamily());
+    EXPECT_TRUE(equal(address.begin(), address.end(), V6ADDR_4));
+
+    // Some invalid strings
+    EXPECT_THROW(IPCheck<GeneralAddress>("::1/129"), isc::OutOfRange);
+    EXPECT_THROW(IPCheck<GeneralAddress>("::1/24/3"), isc::InvalidParameter);
+    EXPECT_THROW(IPCheck<GeneralAddress>(":::1/24"), isc::InvalidParameter);
+    EXPECT_THROW(IPCheck<GeneralAddress>("2001:0db8::abcd/ww"),
+                 isc::InvalidParameter);
+    EXPECT_THROW(IPCheck<GeneralAddress>("2xx1:0db8::abcd/32"),
+                 isc::InvalidParameter);
+}
+
+TEST(IPCheck, V6CopyConstructor) {
+    IPCheck<GeneralAddress> acl1(string(V6ADDR_2_STRING) + string("/52"));
+    IPCheck<GeneralAddress> acl2(acl1);
+
+    vector<uint8_t> acl1_address = acl1.getAddress();
+    vector<uint8_t> acl2_address = acl1.getAddress();
+    EXPECT_EQ(sizeof(V6ADDR_1), acl1_address.size());
+    EXPECT_EQ(acl1_address.size(), acl2_address.size());
+    EXPECT_TRUE(equal(acl1_address.begin(), acl1_address.end(),
+                acl2_address.begin()));
+
+    EXPECT_EQ(acl1.getPrefixlen(), acl2.getPrefixlen());
+
+    vector<uint8_t> acl1_mask = acl1.getMask();
+    vector<uint8_t> acl2_mask = acl1.getMask();
+    EXPECT_EQ(sizeof(V6ADDR_1), acl1_mask.size());
+    EXPECT_EQ(acl1_mask.size(), acl2_mask.size());
+    EXPECT_TRUE(equal(acl1_mask.begin(), acl1_mask.end(),
+                acl2_mask.begin()));
+}
+
+TEST(IPCheck, V6AssignmentOperator) {
+    IPCheck<GeneralAddress> acl1(string(V6ADDR_2_STRING) + string("/52"));
+    IPCheck<GeneralAddress> acl2(string(V6ADDR_1_STRING) + string("/48"));
+
+    acl2 = acl1;
+
+    vector<uint8_t> acl1_address = acl1.getAddress();
+    vector<uint8_t> acl2_address = acl2.getAddress();
+    EXPECT_EQ(sizeof(V6ADDR_1), acl1_address.size());
+    EXPECT_EQ(acl1_address.size(), acl2_address.size());
+    EXPECT_TRUE(equal(acl1_address.begin(), acl1_address.end(),
+                acl2_address.begin()));
+
+    EXPECT_EQ(acl1.getPrefixlen(), acl2.getPrefixlen());
+
+    vector<uint8_t> acl1_mask = acl1.getMask();
+    vector<uint8_t> acl2_mask = acl2.getMask();
+    EXPECT_EQ(sizeof(V6ADDR_1), acl1_mask.size());
+    EXPECT_EQ(acl1_mask.size(), acl2_mask.size());
+    EXPECT_TRUE(equal(acl1_mask.begin(), acl1_mask.end(),
+                acl2_mask.begin()));
+}
+
+TEST(IPCheck, V6Compare) {
+    // Set up some data.
+    vector<uint8_t> v6addr_2(V6ADDR_2, V6ADDR_2 + IPV6_SIZE);
+    vector<uint8_t> v6addr_2_48(V6ADDR_2_48, V6ADDR_2_48 + IPV6_SIZE);
+    vector<uint8_t> v6addr_2_49(V6ADDR_2_49, V6ADDR_2_49 + IPV6_SIZE);
+    vector<uint8_t> v6addr_2_50(V6ADDR_2_50, V6ADDR_2_50 + IPV6_SIZE);
+    vector<uint8_t> v6addr_2_51(V6ADDR_2_51, V6ADDR_2_51 + IPV6_SIZE);
+    vector<uint8_t> v6addr_2_52(V6ADDR_2_52, V6ADDR_2_52 + IPV6_SIZE);
+    vector<uint8_t> v6addr_2_127(V6ADDR_2_127, V6ADDR_2_127 + IPV6_SIZE);
+    vector<uint8_t> v6addr_3(V6ADDR_3, V6ADDR_3 + IPV6_SIZE);
+
+    // Exact address - match if given address matches stored address.
+    IPCheck<GeneralAddress> acl1(string(V6ADDR_2_STRING) + string("/128"));
+    EXPECT_TRUE(acl1.matches(v6addr_2));
+    EXPECT_FALSE(acl1.matches(v6addr_2_127));
+    EXPECT_FALSE(acl1.matches(v6addr_2_52));
+    EXPECT_FALSE(acl1.matches(v6addr_2_51));
+    EXPECT_FALSE(acl1.matches(v6addr_2_50));
+    EXPECT_FALSE(acl1.matches(v6addr_2_49));
+    EXPECT_FALSE(acl1.matches(v6addr_2_48));
+    EXPECT_FALSE(acl1.matches(v6addr_3));
+
+    // Match to various prefixes.
+    IPCheck<GeneralAddress> acl2(string(V6ADDR_2_STRING) + string("/127"));
+    EXPECT_TRUE(acl2.matches(v6addr_2));
+    EXPECT_TRUE(acl2.matches(v6addr_2_127));
+    EXPECT_FALSE(acl2.matches(v6addr_2_52));
+    EXPECT_FALSE(acl2.matches(v6addr_2_51));
+    EXPECT_FALSE(acl2.matches(v6addr_2_50));
+    EXPECT_FALSE(acl2.matches(v6addr_2_49));
+    EXPECT_FALSE(acl2.matches(v6addr_2_48));
+    EXPECT_FALSE(acl2.matches(v6addr_3));
+
+    IPCheck<GeneralAddress> acl3(string(V6ADDR_2_STRING) + string("/52"));
+    EXPECT_TRUE(acl3.matches(v6addr_2));
+    EXPECT_TRUE(acl3.matches(v6addr_2_127));
+    EXPECT_TRUE(acl3.matches(v6addr_2_52));
+    EXPECT_FALSE(acl3.matches(v6addr_2_51));
+    EXPECT_FALSE(acl3.matches(v6addr_2_50));
+    EXPECT_FALSE(acl3.matches(v6addr_2_49));
+    EXPECT_FALSE(acl3.matches(v6addr_2_48));
+    EXPECT_FALSE(acl3.matches(v6addr_3));
+
+    IPCheck<GeneralAddress> acl4(string(V6ADDR_2_STRING) + string("/51"));
+    EXPECT_TRUE(acl4.matches(v6addr_2));
+    EXPECT_TRUE(acl4.matches(v6addr_2_127));
+    EXPECT_TRUE(acl4.matches(v6addr_2_52));
+    EXPECT_TRUE(acl4.matches(v6addr_2_51));
+    EXPECT_FALSE(acl4.matches(v6addr_2_50));
+    EXPECT_FALSE(acl4.matches(v6addr_2_49));
+    EXPECT_FALSE(acl4.matches(v6addr_2_48));
+    EXPECT_FALSE(acl4.matches(v6addr_3));
+
+    IPCheck<GeneralAddress> acl5(string(V6ADDR_2_STRING) + string("/50"));
+    EXPECT_TRUE(acl5.matches(v6addr_2));
+    EXPECT_TRUE(acl5.matches(v6addr_2_127));
+    EXPECT_TRUE(acl5.matches(v6addr_2_52));
+    EXPECT_TRUE(acl5.matches(v6addr_2_51));
+    EXPECT_TRUE(acl5.matches(v6addr_2_50));
+    EXPECT_FALSE(acl5.matches(v6addr_2_49));
+    EXPECT_FALSE(acl5.matches(v6addr_2_48));
+    EXPECT_FALSE(acl5.matches(v6addr_3));
+
+    IPCheck<GeneralAddress> acl6(string(V6ADDR_2_STRING) + string("/0"));
+    EXPECT_TRUE(acl6.matches(v6addr_2));
+    EXPECT_TRUE(acl6.matches(v6addr_2_127));
+    EXPECT_TRUE(acl6.matches(v6addr_2_52));
+    EXPECT_TRUE(acl6.matches(v6addr_2_51));
+    EXPECT_TRUE(acl6.matches(v6addr_2_50));
+    EXPECT_TRUE(acl6.matches(v6addr_2_49));
+    EXPECT_TRUE(acl6.matches(v6addr_2_48));
+    EXPECT_TRUE(acl6.matches(v6addr_3));
+
+    // Match on any address
+    IPCheck<GeneralAddress> acl7("any6");
+    EXPECT_TRUE(acl7.matches(v6addr_2));
+    EXPECT_TRUE(acl7.matches(v6addr_2_127));
+    EXPECT_TRUE(acl7.matches(v6addr_2_52));
+    EXPECT_TRUE(acl7.matches(v6addr_2_51));
+    EXPECT_TRUE(acl7.matches(v6addr_2_50));
+    EXPECT_TRUE(acl7.matches(v6addr_2_49));
+    EXPECT_TRUE(acl7.matches(v6addr_2_48));
+}
+
+// *** Mixed-mode tests - mainly to check that no exception is thrown ***
+
+TEST(IPCheck, MixedMode) {
+
+    // ACL has a V4 address specified, check against a V6 address.
+    IPCheck<GeneralAddress> acl1("192.0.2.255/24");
+    GeneralAddress test1(vector<uint8_t>(V6ADDR_1, V6ADDR_1 + IPV6_SIZE));
+    EXPECT_NO_THROW(acl1.matches(test1));
+    EXPECT_FALSE(acl1.matches(test1));
+
+    // Now the reverse - the ACL is specified with a V6 address.
+    IPCheck<GeneralAddress> acl2(V6ADDR_2_STRING);
+    GeneralAddress test2(0x12345678);
+    EXPECT_FALSE(acl2.matches(test2));
+
+    // Ensure only a V4 address matches "any4".
+    IPCheck<GeneralAddress> acl3("any4");
+    EXPECT_FALSE(acl3.matches(test1));
+    EXPECT_TRUE(acl3.matches(test2));
+
+    // ... and check the reverse
+    IPCheck<GeneralAddress> acl4("any6");
+    EXPECT_TRUE(acl4.matches(test1));
+    EXPECT_FALSE(acl4.matches(test2));
+
+    // Check where the bit pattern of an IPv4 address matches that of an IPv6
+    // one.
+    IPCheck<GeneralAddress> acl5("2001:db8::/32");
+    GeneralAddress test5(0x20010db8);
+    EXPECT_FALSE(acl5.matches(test5));
+
+    // ... and where the reverse is true. (2001:db8 corresponds to 32.1.13.184).
+    IPCheck<GeneralAddress> acl6("32.1.13.184");
+    GeneralAddress test6(vector<uint8_t>(V6ADDR_1, V6ADDR_1 + IPV6_SIZE));
+    EXPECT_FALSE(acl6.matches(test6));
+}
+} // Unnamed namespace
diff --git a/src/lib/acl/tests/loader_test.cc b/src/lib/acl/tests/loader_test.cc
new file mode 100644
index 0000000..d8f67f4
--- /dev/null
+++ b/src/lib/acl/tests/loader_test.cc
@@ -0,0 +1,383 @@
+// 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 "creators.h"
+#include <exceptions/exceptions.h>
+#include <acl/loader.h>
+#include <string>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace boost;
+using namespace isc::acl;
+using namespace isc::acl::tests;
+using isc::data::Element;
+using isc::data::ConstElementPtr;
+
+namespace {
+
+// We don't use the EXPECT_THROW macro, as it doesn't allow us
+// to examine the exception. We want to check the element is stored
+// there as well.
+void testActionLoaderException(const string& JSON) {
+    SCOPED_TRACE("Should throw with input: " + JSON);
+    ConstElementPtr elem(Element::fromJSON(JSON));
+    try {
+        defaultActionLoader(elem);
+        FAIL() << "It did not throw";
+    }
+    catch (const LoaderError& error) {
+        // Yes, comparing for pointer equality, that is enough, it
+        // should return the exact instance of the JSON object
+        EXPECT_EQ(elem, error.element());
+    }
+}
+
+// Test the defaultActionLoader function
+TEST(LoaderHelpers, DefaultActionLoader) {
+    // First the three valid inputs
+    EXPECT_EQ(ACCEPT, defaultActionLoader(Element::fromJSON("\"ACCEPT\"")));
+    EXPECT_EQ(REJECT, defaultActionLoader(Element::fromJSON("\"REJECT\"")));
+    EXPECT_EQ(DROP, defaultActionLoader(Element::fromJSON("\"DROP\"")));
+    // Now few invalid ones
+    // String, but unknown one
+    testActionLoaderException("\"UNKNOWN\"");
+    testActionLoaderException("42");
+    testActionLoaderException("true");
+    testActionLoaderException("null");
+    testActionLoaderException("[]");
+    testActionLoaderException("{}");
+}
+
+class LoaderTest : public ::testing::Test {
+public:
+    LoaderTest() :
+        loader_(REJECT)
+    {}
+    Loader<Log> loader_;
+    Log log_;
+    // Some convenience functions to set up
+
+    // Create a NamedCreator, convert to shared pointer
+    boost::shared_ptr<NamedCreator> namedCreator(const string& name,
+                                          bool abbreviatedList = true)
+    {
+        return (boost::shared_ptr<NamedCreator>(new NamedCreator(name,
+                                                          abbreviatedList)));
+    }
+    // Create and add a NamedCreator
+    void addNamed(const string& name, bool abbreviatedList = true) {
+        EXPECT_NO_THROW(loader_.registerCreator(
+            namedCreator(name, abbreviatedList)));
+    }
+    template<class Result> boost::shared_ptr<Result> loadCheckAny(const string&
+                                                               definition)
+    {
+        SCOPED_TRACE("Loading check " + definition);
+        boost::shared_ptr<Check<Log> > loaded;
+        EXPECT_NO_THROW(loaded = loader_.loadCheck(
+                            Element::fromJSON(definition)));
+        boost::shared_ptr<Result> result(dynamic_pointer_cast<Result>(
+            loaded));
+        EXPECT_TRUE(result);
+        return (result);
+    }
+    // Load a check and convert it to named check to examine it
+    boost::shared_ptr<NamedCheck> loadCheck(const string& definition) {
+        return (loadCheckAny<NamedCheck>(definition));
+    }
+    // The loadCheck throws an exception
+    void checkException(const string& JSON) {
+        SCOPED_TRACE("Loading check exception: " + JSON);
+        ConstElementPtr input(Element::fromJSON(JSON));
+        // Not using EXPECT_THROW, we want to examine the exception
+        try {
+            loader_.loadCheck(input);
+            FAIL() << "Should have thrown";
+        }
+        catch (const LoaderError& e) {
+            // It should be identical copy, so checking pointers
+            EXPECT_EQ(input, e.element());
+        }
+    }
+    // Insert the throw, throwcheck and logcheck checks into the loader
+    void aclSetup() {
+        try {
+            loader_.registerCreator(boost::shared_ptr<ThrowCreator>(new
+                                                             ThrowCreator()));
+            loader_.registerCreator(boost::shared_ptr<ThrowCheckCreator>(
+                new ThrowCheckCreator()));
+            loader_.registerCreator(boost::shared_ptr<LogCreator>(new LogCreator()));
+        }
+        // We ignore this exception here, because it happens when we try to
+        // insert the creators multiple times. This is harmless.
+        catch (const LoaderError&) {}
+    }
+    // Create an ACL, run it, check it's result and how many first
+    // log items it marked
+    //
+    // Works with preset names throw and logcheck
+    void aclRun(const string& JSON, BasicAction expectedResult,
+                size_t logged)
+    {
+        SCOPED_TRACE("Running ACL for " + JSON);
+        aclSetup();
+        boost::shared_ptr<ACL<Log> > acl;
+        EXPECT_NO_THROW(acl = loader_.load(Element::fromJSON(JSON)));
+        EXPECT_EQ(expectedResult, acl->execute(log_));
+        log_.checkFirst(logged);
+    }
+    // Check it throws an error when creating the ACL
+    void aclException(const string& JSON) {
+        SCOPED_TRACE("Trying to load bad " + JSON);
+        aclSetup();
+        EXPECT_THROW(loader_.load(Element::fromJSON(JSON)), LoaderError);
+    }
+    // Check that the subexpression is NamedCheck with correct data
+    void isSubexprNamed(const CompoundCheck<Log>* compound, size_t index,
+                        const string& name, ConstElementPtr data)
+    {
+        if (index < compound->getSubexpressions().size()) {
+            const NamedCheck*
+                check(dynamic_cast<const NamedCheck*>(compound->
+                                                      getSubexpressions()
+                                                      [index]));
+            ASSERT_TRUE(check) << "The subexpression is of different type";
+            EXPECT_EQ(name, check->name_);
+            EXPECT_TRUE(data->equals(*check->data_));
+        }
+    }
+};
+
+// Test that it does not accept duplicate creator
+TEST_F(LoaderTest, CreatorDuplicity) {
+    addNamed("name");
+    EXPECT_THROW(loader_.registerCreator(namedCreator("name")), LoaderError);
+}
+
+// Test that when it does not accept a duplicate, nothing is inserted
+TEST_F(LoaderTest, CreatorDuplicateUnchanged) {
+    addNamed("name1");
+    vector<string> names;
+    names.push_back("name2");
+    names.push_back("name1");
+    names.push_back("name3");
+    EXPECT_THROW(loader_.registerCreator(
+        boost::shared_ptr<NamedCreator>(new NamedCreator(names))), LoaderError);
+    // It should now reject both name2 and name3 as not known
+    checkException("{\"name2\": null}");
+    checkException("{\"name3\": null}");
+}
+
+// Test that we can register a creator and load a check with the name
+TEST_F(LoaderTest, SimpleCheckLoad) {
+    addNamed("name");
+    boost::shared_ptr<NamedCheck> check(loadCheck("{\"name\": 42}"));
+    EXPECT_EQ("name", check->name_);
+    EXPECT_TRUE(check->data_->equals(*Element::fromJSON("42")));
+}
+
+// As above, but there are multiple creators registered within the loader
+TEST_F(LoaderTest, MultiCreatorCheckLoad) {
+    addNamed("name1");
+    addNamed("name2");
+    boost::shared_ptr<NamedCheck> check(loadCheck("{\"name2\": 42}"));
+    EXPECT_EQ("name2", check->name_);
+    EXPECT_TRUE(check->data_->equals(*Element::fromJSON("42")));
+}
+
+// Similar to above, but there's a creator with multiple names
+TEST_F(LoaderTest, MultiNameCheckLoad) {
+    addNamed("name1");
+    vector<string> names;
+    names.push_back("name2");
+    names.push_back("name3");
+    EXPECT_NO_THROW(loader_.registerCreator(boost::shared_ptr<NamedCreator>(
+        new NamedCreator(names))));
+    boost::shared_ptr<NamedCheck> check(loadCheck("{\"name3\": 42}"));
+    EXPECT_EQ("name3", check->name_);
+    EXPECT_TRUE(check->data_->equals(*Element::fromJSON("42")));
+}
+
+// Invalid format is rejected
+TEST_F(LoaderTest, InvalidFormatCheck) {
+    checkException("[]");
+    checkException("42");
+    checkException("\"hello\"");
+    checkException("null");
+}
+
+// Empty check is rejected
+TEST_F(LoaderTest, EmptyCheck) {
+    checkException("{}");
+}
+
+// The name isn't known
+TEST_F(LoaderTest, UnkownName) {
+    checkException("{\"unknown\": null}");
+}
+
+// Exception from the creator is propagated
+TEST_F(LoaderTest, CheckPropagate) {
+    loader_.registerCreator(boost::shared_ptr<ThrowCreator>(new ThrowCreator()));
+    EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"throw\": null}")),
+                 TestCreatorError);
+}
+
+// The abbreviated form of check
+TEST_F(LoaderTest, AndAbbrev) {
+    addNamed("name1");
+    addNamed("name2");
+    boost::shared_ptr<LogicOperator<AllOfSpec, Log> > oper(
+        loadCheckAny<LogicOperator<AllOfSpec, Log> >("{\"name1\": 1, \"name2\": 2}"));
+    // If we don't have anything loaded, the rest would crash. It is already
+    // reported from within loadCheckAny if it isn't loaded.
+    if (oper) {
+        // The subexpressions are correct
+        EXPECT_EQ(2, oper->getSubexpressions().size());
+        // Note: this test relies on the ordering in which map returns it's
+        // elements, which is in the lexicographical order of the strings.
+        // This is not required from our interface, but is easier to write
+        // the test.
+        isSubexprNamed(&*oper, 0, "name1", Element::fromJSON("1"));
+        isSubexprNamed(&*oper, 1, "name2", Element::fromJSON("2"));
+    }
+}
+
+// The abbreviated form of parameters
+TEST_F(LoaderTest, OrAbbrev) {
+    addNamed("name1");
+    boost::shared_ptr<LogicOperator<AnyOfSpec, Log> > oper(
+        loadCheckAny<LogicOperator<AnyOfSpec, Log> >("{\"name1\": [1, 2]}"));
+    // If we don't have anything loaded, the rest would crash. It is already
+    // reported from within loadCheckAny if it isn't loaded.
+    if (oper) {
+        // The subexpressions are correct
+        EXPECT_EQ(2, oper->getSubexpressions().size());
+        isSubexprNamed(&*oper, 0, "name1", Element::fromJSON("1"));
+        isSubexprNamed(&*oper, 1, "name1", Element::fromJSON("2"));
+    }
+}
+
+// Combined abbreviated form, both at once
+
+// The abbreviated form of check
+TEST_F(LoaderTest, BothAbbrev) {
+    addNamed("name1");
+    addNamed("name2");
+    boost::shared_ptr<LogicOperator<AllOfSpec, Log> > oper(
+        loadCheckAny<LogicOperator<AllOfSpec, Log> >("{\"name1\": 1, \"name2\": [3, 4]}"));
+    // If we don't have anything loaded, the rest would crash. It is already
+    // reported from within loadCheckAny if it isn't loaded.
+    if (oper) {
+        // The subexpressions are correct
+        ASSERT_EQ(2, oper->getSubexpressions().size());
+        // Note: this test relies on the ordering in which map returns it's
+        // elements, which is in the lexicographical order of the strings.
+        // This is not required from our interface, but is easier to write
+        // the test.
+        isSubexprNamed(&*oper, 0, "name1", Element::fromJSON("1"));
+        const LogicOperator<AnyOfSpec, Log>*
+            orOper(dynamic_cast<const LogicOperator<AnyOfSpec, Log>*>(
+            oper->getSubexpressions()[1]));
+        ASSERT_TRUE(orOper) << "Different type than AnyOf operator";
+        EXPECT_EQ(2, orOper->getSubexpressions().size());
+        isSubexprNamed(orOper, 0, "name2", Element::fromJSON("3"));
+        isSubexprNamed(orOper, 1, "name2", Element::fromJSON("4"));
+    }
+}
+
+// But this is not abbreviated form, this should be passed directly to the
+// creator
+TEST_F(LoaderTest, ListCheck) {
+    addNamed("name1", false);
+    boost::shared_ptr<NamedCheck> check(loadCheck("{\"name1\": [1, 2]}"));
+    EXPECT_EQ("name1", check->name_);
+    EXPECT_TRUE(check->data_->equals(*Element::fromJSON("[1, 2]")));
+}
+
+// Check the action key is ignored as it should be
+TEST_F(LoaderTest, CheckNoAction) {
+    addNamed("name1");
+    boost::shared_ptr<NamedCheck> check(loadCheck("{\"name1\": 1, \"action\": 2}"));
+    EXPECT_EQ("name1", check->name_);
+    EXPECT_TRUE(check->data_->equals(*Element::fromJSON("1")));
+}
+
+// The empty ACL can be created and run, providing the default action
+TEST_F(LoaderTest, EmptyACL) {
+    aclRun("[]", REJECT, 0);
+}
+
+// We can create a simple ACL, which will return the correct default
+// action
+TEST_F(LoaderTest, NoMatchACL) {
+    aclRun("[{\"logcheck\": [0, false], \"action\": \"ACCEPT\"}]",
+           REJECT, 1);
+}
+
+// We can created more complicated ACL, it will match at the second
+// check
+TEST_F(LoaderTest, MatchACL) {
+    aclRun("["
+           "  {\"logcheck\": [0, false], \"action\": \"DROP\"},"
+           "  {\"logcheck\": [1, true], \"action\": \"ACCEPT\"}"
+           "]", ACCEPT, 2);
+}
+
+// ACL without a check (matches unconditionally)
+// We add another one check after it, to make sure it is really not run
+TEST_F(LoaderTest, NoCheckACL) {
+    aclRun("["
+           "  {\"action\": \"DROP\"},"
+           "  {\"throwcheck\": 1, \"action\": \"ACCEPT\"}"
+           "]", DROP, 0);
+}
+
+// Malformed things are rejected
+TEST_F(LoaderTest, InvalidACLFormat) {
+    // Not a list
+    aclException("{}");
+    aclException("42");
+    aclException("true");
+    aclException("null");
+    aclException("\"hello\"");
+    // Malformed element
+    aclException("[42]");
+    aclException("[\"hello\"]");
+    aclException("[[]]");
+    aclException("[true]");
+    aclException("[null]");
+}
+
+// If there's no action keyword, it is rejected
+TEST_F(LoaderTest, NoAction) {
+    aclException("[{}]");
+    aclException("[{\"logcheck\": [0, true]}]");
+}
+
+// Exceptions from check creation is propagated
+TEST_F(LoaderTest, ACLPropagate) {
+    aclSetup();
+    EXPECT_THROW(loader_.load(
+                     Element::fromJSON(
+                         "[{\"action\": \"ACCEPT\", \"throw\": 1}]")),
+                 TestCreatorError);
+}
+
+TEST_F(LoaderTest, nullDescription) {
+    EXPECT_THROW(loader_.load(ConstElementPtr()), isc::InvalidParameter);
+}
+
+}
diff --git a/src/lib/acl/tests/logcheck.h b/src/lib/acl/tests/logcheck.h
new file mode 100644
index 0000000..424c53d
--- /dev/null
+++ b/src/lib/acl/tests/logcheck.h
@@ -0,0 +1,94 @@
+// 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.
+
+#ifndef LOGCHECK_H
+#define LOGCHECK_H
+
+#include <gtest/gtest.h>
+#include <acl/acl.h>
+#include <cassert>
+
+// This is not a public header, it is used only inside the tests.
+
+namespace isc {
+namespace acl {
+namespace tests {
+
+// This is arbitrary guess of size for the log. If it's too small for your
+// test, just make it bigger.
+const size_t LOG_SIZE = 10;
+
+// This will remember which checks did run already.
+struct Log {
+    // The actual log cells, if i-th check did run
+    mutable bool run[LOG_SIZE];
+    Log() {
+        // Nothing run yet
+        for (size_t i(0); i < LOG_SIZE; ++ i) {
+            run[i] = false;
+        }
+    }
+    // Checks that the first amount of checks did run and the rest didn't.
+    void checkFirst(size_t amount) const {
+        ASSERT_LE(amount, LOG_SIZE) << "Wrong test: amount bigger than size "
+            "of log";
+        {
+            SCOPED_TRACE("Checking that the first amount of checks did run");
+            for (size_t i(0); i < amount; ++ i) {
+                EXPECT_TRUE(run[i]) << "Check #" << i << " did not run.";
+            }
+        }
+
+        {
+            SCOPED_TRACE("Checking that the rest did not run");
+            for (size_t i(amount); i < LOG_SIZE; ++ i) {
+                EXPECT_FALSE(run[i]) << "Check #" << i << "did run.";
+            }
+        }
+    }
+};
+
+// This returns true or false every time, no matter what is passed to it.
+// But it logs that it did run.
+class ConstCheck : public Check<Log> {
+public:
+    ConstCheck(bool accepts, size_t logNum) :
+        logNum_(logNum),
+        accepts_(accepts)
+    {
+        assert(logNum < LOG_SIZE); // If this fails, the LOG_SIZE is too small
+    }
+    virtual bool matches(const Log& log) const {
+        /*
+         * This is abuse of the context. It is designed to carry the
+         * information to check, not to modify it. However, this is the
+         * easiest way to do the test, so we go against the design.
+         */
+        log.run[logNum_] = true;
+        return (accepts_);
+    }
+private:
+    size_t logNum_;
+    bool accepts_;
+};
+
+}
+}
+}
+
+#endif
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/acl/tests/logic_check_test.cc b/src/lib/acl/tests/logic_check_test.cc
new file mode 100644
index 0000000..18f663f
--- /dev/null
+++ b/src/lib/acl/tests/logic_check_test.cc
@@ -0,0 +1,291 @@
+// 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 "creators.h"
+#include <acl/logic_check.h>
+#include <typeinfo>
+#include <boost/shared_ptr.hpp> // for static_pointer_cast
+
+using namespace std;
+using namespace boost;
+using namespace isc::acl;
+using namespace isc::acl::tests;
+using isc::data::Element;
+
+namespace {
+
+// Test the defs in AnyOfSpec
+TEST(LogicOperators, AnyOfSpec) {
+    EXPECT_FALSE(AnyOfSpec::start());
+    EXPECT_FALSE(AnyOfSpec::terminate(false));
+    EXPECT_TRUE(AnyOfSpec::terminate(true));
+}
+
+// Test the defs in AllOfSpec
+TEST(LogicOperators, AllOfSpec) {
+    EXPECT_TRUE(AllOfSpec::start());
+    EXPECT_TRUE(AllOfSpec::terminate(false));
+    EXPECT_FALSE(AllOfSpec::terminate(true));
+}
+
+// Generic test of one check
+template<typename Mode>
+void
+testCheck(bool emptyResult) {
+    // It can be created
+    LogicOperator<Mode, Log> oper;
+    // It is empty by default
+    EXPECT_EQ(0, oper.getSubexpressions().size());
+    // And returns true, as all 0 of the subexpressions return true
+    Log log;
+    EXPECT_EQ(emptyResult, oper.matches(log));
+    log.checkFirst(0);
+    // Fill it with some subexpressions
+    typedef boost::shared_ptr<ConstCheck> CheckPtr;
+    oper.addSubexpression(CheckPtr(new ConstCheck(emptyResult, 0)));
+    oper.addSubexpression(CheckPtr(new ConstCheck(emptyResult, 1)));
+    // Check what happens when only the default-valued are there
+    EXPECT_EQ(2, oper.getSubexpressions().size());
+    EXPECT_EQ(emptyResult, oper.matches(log));
+    log.checkFirst(2);
+    oper.addSubexpression(CheckPtr(new ConstCheck(!emptyResult, 2)));
+    oper.addSubexpression(CheckPtr(new ConstCheck(!emptyResult, 3)));
+    // They are listed there
+    EXPECT_EQ(4, oper.getSubexpressions().size());
+    // Now, the last one kills it, but the first ones will run, the fourth
+    // won't
+    EXPECT_EQ(!emptyResult, oper.matches(log));
+    log.checkFirst(3);
+}
+
+TEST(LogicOperators, AllOf) {
+    testCheck<AllOfSpec>(true);
+}
+
+TEST(LogicOperators, AnyOf) {
+    testCheck<AnyOfSpec>(false);
+}
+
+// Fixture for the tests of the creators
+class LogicCreatorTest : public ::testing::Test {
+private:
+    typedef boost::shared_ptr<Loader<Log>::CheckCreator> CreatorPtr;
+public:
+    // Register some creators, both tested ones and some auxiliary ones for
+    // help
+    LogicCreatorTest():
+        loader_(REJECT)
+    {
+        loader_.registerCreator(CreatorPtr(new
+            LogicCreator<AnyOfSpec, Log>("ANY")));
+        loader_.registerCreator(CreatorPtr(new
+            LogicCreator<AllOfSpec, Log>("ALL")));
+        loader_.registerCreator(CreatorPtr(new ThrowCreator));
+        loader_.registerCreator(CreatorPtr(new LogCreator));
+        loader_.registerCreator(CreatorPtr(new NotCreator<Log>("NOT")));
+    }
+    // To mark which parts of the check did run
+    Log log_;
+    // The loader
+    Loader<Log> loader_;
+    // Some convenience shortcut names
+    typedef LogicOperator<AnyOfSpec, Log> AnyOf;
+    typedef LogicOperator<AllOfSpec, Log> AllOf;
+    typedef boost::shared_ptr<AnyOf> AnyOfPtr;
+    typedef boost::shared_ptr<AllOf> AllOfPtr;
+    // Loads the JSON as a check and tries to convert it to the given check
+    // subclass
+    template<typename Result> boost::shared_ptr<Result> load(const string& JSON) {
+        boost::shared_ptr<Check<Log> > result;
+        EXPECT_NO_THROW(result = loader_.loadCheck(Element::fromJSON(JSON)));
+        /*
+         * Optimally, we would use a dynamic_pointer_cast here to both
+         * convert the pointer and to check the type is correct. However,
+         * clang++ seems to be confused by templates and creates two typeids
+         * for the same templated type (even with the same parameters),
+         * therfore considering the types different, even if they are the same.
+         * This leads to false alarm in the test. Luckily, it generates the
+         * same name for both typeids, so we use them instead (which is enough
+         * to test the correct type of Check is returned). Then we can safely
+         * cast statically, as we don't use any kind of nasty things like
+         * multiple inheritance.
+         */
+        EXPECT_STREQ(typeid(Result).name(), typeid(*result.get()).name());
+        boost::shared_ptr<Result>
+            resultConverted(static_pointer_cast<Result>(result));
+        EXPECT_NE(boost::shared_ptr<Result>(), resultConverted);
+        return (resultConverted);
+    }
+};
+
+// Test it can load empty ones
+TEST_F(LogicCreatorTest, empty) {
+    AnyOfPtr emptyAny(load<AnyOf>("{\"ANY\": []}"));
+    EXPECT_EQ(0, emptyAny->getSubexpressions().size());
+    AllOfPtr emptyAll(load<AllOf>("{\"ALL\": []}"));
+    EXPECT_EQ(0, emptyAll->getSubexpressions().size());
+}
+
+// Test it rejects invalid inputs (not a list as a parameter)
+TEST_F(LogicCreatorTest, invalid) {
+    EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"ANY\": null}")),
+                 LoaderError);
+    EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"ANY\": {}}")),
+                 LoaderError);
+    EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"ANY\": true}")),
+                 LoaderError);
+    EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"ANY\": 42}")),
+                 LoaderError);
+    EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"ANY\": \"hello\"}")),
+                 LoaderError);
+    EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"ALL\": null}")),
+                 LoaderError);
+    EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"ALL\": {}}")),
+                 LoaderError);
+    EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"ALL\": true}")),
+                 LoaderError);
+    EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"ALL\": 42}")),
+                 LoaderError);
+    EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"ALL\": \"hello\"}")),
+                 LoaderError);
+}
+
+// Exceptions from subexpression creation isn't caught
+TEST_F(LogicCreatorTest, propagate) {
+    EXPECT_THROW(loader_.loadCheck(
+                     Element::fromJSON("{\"ANY\": [{\"throw\": null}]}")),
+                 TestCreatorError);
+    EXPECT_THROW(loader_.loadCheck(
+                     Element::fromJSON("{\"ALL\": [{\"throw\": null}]}")),
+                 TestCreatorError);
+}
+
+// We can create more complex ANY check and run it correctly
+TEST_F(LogicCreatorTest, anyRun) {
+    AnyOfPtr any(load<AnyOf>("{\"ANY\": ["
+                             "    {\"logcheck\": [0, false]},"
+                             "    {\"logcheck\": [1, true]},"
+                             "    {\"logcheck\": [2, true]}"
+                             "]}"));
+    EXPECT_EQ(3, any->getSubexpressions().size());
+    EXPECT_TRUE(any->matches(log_));
+    log_.checkFirst(2);
+}
+
+// We can create more complex ALL check and run it correctly
+TEST_F(LogicCreatorTest, allRun) {
+    AllOfPtr any(load<AllOf>("{\"ALL\": ["
+                             "    {\"logcheck\": [0, true]},"
+                             "    {\"logcheck\": [1, false]},"
+                             "    {\"logcheck\": [2, false]}"
+                             "]}"));
+    EXPECT_EQ(3, any->getSubexpressions().size());
+    EXPECT_FALSE(any->matches(log_));
+    log_.checkFirst(2);
+}
+
+// Or is able to return false
+TEST_F(LogicCreatorTest, anyFalse) {
+    AnyOfPtr any(load<AnyOf>("{\"ANY\": ["
+                             "    {\"logcheck\": [0, false]},"
+                             "    {\"logcheck\": [1, false]},"
+                             "    {\"logcheck\": [2, false]}"
+                             "]}"));
+    EXPECT_EQ(3, any->getSubexpressions().size());
+    EXPECT_FALSE(any->matches(log_));
+    log_.checkFirst(3);
+}
+
+// And is able to return true
+TEST_F(LogicCreatorTest, andTrue) {
+    AllOfPtr all(load<AllOf>("{\"ALL\": ["
+                             "    {\"logcheck\": [0, true]},"
+                             "    {\"logcheck\": [1, true]},"
+                             "    {\"logcheck\": [2, true]}"
+                             "]}"));
+    EXPECT_EQ(3, all->getSubexpressions().size());
+    EXPECT_TRUE(all->matches(log_));
+    log_.checkFirst(3);
+}
+
+// We can nest them together
+TEST_F(LogicCreatorTest, nested) {
+    AllOfPtr all(load<AllOf>("{\"ALL\": ["
+                             "    {\"ANY\": ["
+                             "        {\"logcheck\": [0, true]},"
+                             "        {\"logcheck\": [2, true]},"
+                             "    ]},"
+                             "    {\"logcheck\": [1, false]}"
+                             "]}"));
+    EXPECT_EQ(2, all->getSubexpressions().size());
+    /*
+     * This has the same problem as load function above, and we use the
+     * same solution here.
+     */
+    ASSERT_STREQ(typeid(LogicOperator<AnyOfSpec, Log>).name(),
+                 typeid(*all->getSubexpressions()[0]).name());
+    const LogicOperator<AnyOfSpec, Log>*
+        any(static_cast<const LogicOperator<AnyOfSpec, Log>*>
+            (all->getSubexpressions()[0]));
+    EXPECT_EQ(2, any->getSubexpressions().size());
+    EXPECT_FALSE(all->matches(log_));
+    log_.checkFirst(2);
+}
+
+void notTest(bool value) {
+    NotOperator<Log> notOp(boost::shared_ptr<Check<Log> >(new ConstCheck(value, 0)));
+    Log log;
+    // It returns negated value
+    EXPECT_EQ(!value, notOp.matches(log));
+    // And runs the only one thing there
+    log.checkFirst(1);
+    // Check the getSubexpressions does sane things
+    ASSERT_EQ(1, notOp.getSubexpressions().size());
+    EXPECT_EQ(value, notOp.getSubexpressions()[0]->matches(log));
+}
+
+TEST(Not, trueValue) {
+    notTest(true);
+}
+
+TEST(Not, falseValue) {
+    notTest(false);
+}
+
+TEST_F(LogicCreatorTest, notInvalid) {
+    EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"NOT\": null}")),
+                 LoaderError);
+    EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"NOT\": \"hello\"}")),
+                 LoaderError);
+    EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"NOT\": true}")),
+                 LoaderError);
+    EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"NOT\": 42}")),
+                 LoaderError);
+    EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"NOT\": []}")),
+                 LoaderError);
+    EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"NOT\": [{"
+                                                     "\"logcheck\": [0, true]"
+                                                     "}]}")),
+                 LoaderError);
+}
+
+TEST_F(LogicCreatorTest, notValid) {
+    boost::shared_ptr<NotOperator<Log> > notOp(load<NotOperator<Log> >("{\"NOT\":"
+                                                                "  {\"logcheck\":"
+                                                                "     [0, true]}}"));
+    EXPECT_FALSE(notOp->matches(log_));
+    log_.checkFirst(1);
+}
+
+}
diff --git a/src/lib/acl/tests/run_unittests.cc b/src/lib/acl/tests/run_unittests.cc
new file mode 100644
index 0000000..8dc59a2
--- /dev/null
+++ b/src/lib/acl/tests/run_unittests.cc
@@ -0,0 +1,24 @@
+// 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 <gtest/gtest.h>
+#include <log/logger_support.h>
+#include <util/unittests/run_all.h>
+
+int
+main(int argc, char* argv[]) {
+    ::testing::InitGoogleTest(&argc, argv);
+    isc::log::initLogger();
+    return (isc::util::unittests::run_all());
+}
diff --git a/src/lib/acl/tests/sockaddr.h b/src/lib/acl/tests/sockaddr.h
new file mode 100644
index 0000000..a222f4f
--- /dev/null
+++ b/src/lib/acl/tests/sockaddr.h
@@ -0,0 +1,73 @@
+// 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.
+
+#ifndef __ACL_TEST_SOCKADDR_H
+#define __ACL_TEST_SOCKADDR_H 1
+
+#ifdef _WIN32
+#include <ws2tcpip.h>
+#else
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#endif
+#include <string.h>
+
+#include <exceptions/exceptions.h>
+
+namespace isc {
+namespace acl {
+namespace tests {
+
+// This is a helper function that returns a sockaddr for the given textual
+// IP address.  Note that "inline" is crucial because this function is defined
+// in a header file included in multiple .cc files.  Without inline it would
+// produce an external linkage and cause troubles at link time.
+//
+// Note that this function uses a static storage for the return value.
+// So if it's called more than once in a singe context (e.g., in the same
+// EXPECT_xx()), it's unlikely to work as expected.
+inline const struct sockaddr&
+getSockAddr(const char* const addr) {
+    struct addrinfo hints, *res;
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = AF_UNSPEC;
+    hints.ai_socktype = SOCK_STREAM;
+    hints.ai_flags = AI_NUMERICHOST;
+
+    if (getaddrinfo(addr, NULL, &hints, &res) == 0) {
+        static struct sockaddr_storage ss;
+        void* ss_ptr = &ss;
+        memcpy(ss_ptr, res->ai_addr, res->ai_addrlen);
+        freeaddrinfo(res);
+        return (*static_cast<struct sockaddr*>(ss_ptr));
+    }
+
+    // We don't expect getaddrinfo to fail for our tests.  But if that
+    // ever happens we throw an exception to make sure the corresponding test
+    // fail (either due to a failure of *_NO_THROW or the uncaught exception).
+    isc_throw(Unexpected,
+              "failed to convert textual IP address to sockaddr for " <<
+              addr);
+}
+
+} // end of namespace "tests"
+} // end of namespace "acl"
+} // end of namespace "isc"
+
+#endif  // __ACL_TEST_SOCKADDR_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/tsigrecord.cc b/src/lib/dns/tsigrecord.cc
new file mode 100644
index 0000000..9dd3f78
--- /dev/null
+++ b/src/lib/dns/tsigrecord.cc
@@ -0,0 +1,147 @@
+// 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 <ostream>
+#include <string>
+
+#include <util/buffer.h>
+
+#include <dns/exceptions.h>
+#include <dns/messagerenderer.h>
+#include <dns/rrclass.h>
+#include <dns/rrttl.h>
+#include <dns/tsigrecord.h>
+
+using namespace isc::util;
+using namespace isc::dns::rdata;
+
+namespace {
+// Internally used constants:
+
+// Size in octets for the RR type, class TTL, RDLEN fields.
+const size_t RR_COMMON_LEN = 10;
+
+// Size in octets for the fixed part of TSIG RDATAs.
+// - Time Signed (6)
+// - Fudge (2)
+// - MAC Size (2)
+// - Original ID (2)
+// - Error (2)
+// - Other Len (2)
+const size_t RDATA_COMMON_LEN = 16;
+}
+
+namespace isc {
+namespace dns {
+TSIGRecord::TSIGRecord(const Name& key_name,
+                       const rdata::any::TSIG& tsig_rdata) :
+    key_name_(key_name), rdata_(tsig_rdata),
+    length_(RR_COMMON_LEN + RDATA_COMMON_LEN + key_name_.getLength() +
+            rdata_.getAlgorithm().getLength() +
+            rdata_.getMACSize() + rdata_.getOtherLen())
+{}
+
+namespace {
+// This is a straightforward wrapper of dynamic_cast<const any::TSIG&>.
+// We use this so that we can throw the DNSMessageFORMERR exception when
+// unexpected type of RDATA is detected in the member initialization list
+// of the constructor below.
+const any::TSIG&
+castToTSIGRdata(const rdata::Rdata& rdata) {
+    try {
+        return (dynamic_cast<const any::TSIG&>(rdata));
+    } catch (std::bad_cast&) {
+        isc_throw(DNSMessageFORMERR,
+                  "TSIG record is being constructed from "
+                  "incompatible RDATA:" << rdata.toText());
+    }
+}
+}
+
+TSIGRecord::TSIGRecord(const Name& name, const RRClass& rrclass,
+                       const RRTTL& ttl, const rdata::Rdata& rdata,
+                       size_t length) :
+    key_name_(name), rdata_(castToTSIGRdata(rdata)), length_(length)
+{
+    if (rrclass != getClass()) {
+        isc_throw(DNSMessageFORMERR, "Unexpected TSIG RR class: " << rrclass);
+    }
+    if (ttl != RRTTL(TSIG_TTL)) {
+        isc_throw(DNSMessageFORMERR, "Unexpected TSIG TTL: " << ttl);
+    }
+}
+
+const RRClass&
+TSIGRecord::getClass() {
+    return (RRClass::ANY());
+}
+
+const RRTTL&
+TSIGRecord::getTTL() {
+    static RRTTL ttl(TSIG_TTL);
+    return (ttl);
+}
+
+namespace {
+template <typename OUTPUT>
+void
+toWireCommon(OUTPUT& output, const rdata::any::TSIG& rdata) {
+    // RR type, class, TTL are fixed constants.
+    RRType::TSIG().toWire(output);
+    TSIGRecord::getClass().toWire(output);
+    output.writeUint32(TSIGRecord::TSIG_TTL);
+
+    // RDLEN
+    output.writeUint16(RDATA_COMMON_LEN + rdata.getAlgorithm().getLength() +
+                       rdata.getMACSize() + rdata.getOtherLen());
+
+    // TSIG RDATA
+    rdata.toWire(output);
+}
+}
+
+int
+TSIGRecord::toWire(AbstractMessageRenderer& renderer) const {
+    // If adding the TSIG would exceed the size limit, don't do it.
+    if (renderer.getLength() + length_ > renderer.getLengthLimit()) {
+        renderer.setTruncated();
+        return (0);
+    }
+
+    // key name = owner.  note that we disable compression.
+    renderer.writeName(key_name_, false);
+    toWireCommon(renderer, rdata_);
+    return (1);
+}
+
+int
+TSIGRecord::toWire(OutputBuffer& buffer) const {
+    key_name_.toWire(buffer);
+    toWireCommon(buffer, rdata_);
+    return (1);
+}
+
+std::string
+TSIGRecord::toText() const {
+    return (key_name_.toText() + " " + RRTTL(TSIG_TTL).toText() + " " +
+            getClass().toText() + " " + RRType::TSIG().toText() + " " +
+            rdata_.toText() + "\n");
+}
+
+std::ostream&
+operator<<(std::ostream& os, const TSIGRecord& record) {
+    return (os << record.toText());
+}
+} // namespace dns
+} // namespace isc
diff --git a/src/lib/dns/tsigrecord.h b/src/lib/dns/tsigrecord.h
new file mode 100644
index 0000000..03de746
--- /dev/null
+++ b/src/lib/dns/tsigrecord.h
@@ -0,0 +1,308 @@
+// 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.
+
+#ifndef __TSIGRECORD_H
+#define __TSIGRECORD_H 1
+
+#include <ostream>
+#include <string>
+
+#include <boost/shared_ptr.hpp>
+
+#include <util/buffer.h>
+
+#include <dns/name.h>
+#include <dns/rdataclass.h>
+
+namespace isc {
+namespace util {
+class OutputBuffer;
+}
+namespace dns {
+class AbstractMessageRenderer;
+
+/// TSIG resource record.
+///
+/// A \c TSIGRecord class object represents a TSIG resource record and is
+/// responsible for conversion to and from wire format TSIG record based on
+/// the protocol specification (RFC2845).
+/// This class is provided so that other classes and applications can handle
+/// TSIG without knowing protocol details of TSIG, such as that it uses a
+/// fixed constant of TTL.
+///
+/// \todo So the plan is to eventually provide  the "from wire" constructor.
+/// It's not yet provided in the current phase of development.
+///
+/// \note
+/// This class could be a derived class of \c AbstractRRset.  That way
+/// it would be able to be used in a polymorphic way; for example,
+/// an application can construct a TSIG RR by itself and insert it to a
+/// \c Message object as a generic RRset.  On the other hand, it would mean
+/// this class would have to implement an \c RdataIterator (even though it
+/// can be done via straightforward forwarding) while the iterator is mostly
+/// redundant since there should be one and only one RDATA for a valid TSIG
+/// RR.  Likewise, some methods such as \c setTTL() method wouldn't be well
+/// defined due to such special rules for TSIG as using a fixed TTL.
+/// Overall, TSIG is a very special RR type that simply uses the compatible
+/// resource record format, and it will be unlikely that a user wants to
+/// handle it through a generic interface in a polymorphic way.
+/// We therefore chose to define it as a separate class.  This is also
+/// similar to why \c EDNS is a separate class.
+class TSIGRecord {
+public:
+    ///
+    /// \name Constructors
+    ///
+    /// We use the default copy constructor, default copy assignment operator,
+    /// (and default destructor) intentionally.
+    //@{
+    /// Constructor from TSIG key name and RDATA
+    ///
+    /// \exception std::bad_alloc Resource allocation for copying the name or
+    /// RDATA fails
+    TSIGRecord(const Name& key_name, const rdata::any::TSIG& tsig_rdata);
+
+    /// Constructor from resource record (RR) parameters.
+    ///
+    /// This constructor is intended to be used in the context of parsing
+    /// an incoming DNS message that contains a TSIG.  The parser would
+    /// first extract the owner name, RR type (which is TSIG) class, TTL and
+    /// the TSIG RDATA from the message.  This constructor is expected to
+    /// be given these RR parameters (except the RR type, because it must be
+    /// TSIG).
+    ///
+    /// According to RFC2845, a TSIG RR uses fixed RR class (ANY) and TTL (0).
+    /// If the RR class or TTL is different from the expected one, this
+    /// implementation considers it an invalid record and throws an exception
+    /// of class \c DNSMessageFORMERR.
+    ///
+    /// \note This behavior is not specified in the protocol specification,
+    /// but this implementation rejects unexpected values for the following
+    /// reasons (but in any case, this won't matter much in practice as
+    /// RFC2848 clearly states these fields always have the fixed values and
+    /// any sane implementation of TSIG signer will follow that):
+    /// According to the RFC editor (in a private communication), the intended
+    /// use of the TSIG TTL field is to signal protocol extensions (currently
+    /// no such extension is defined), so this field may actually be
+    /// validly non 0 in future.  However, until the implementation supports
+    /// that extension it may not always be able to handle the extended
+    /// TSIG as intended; the extension may even affect digest computation.
+    /// There's a related issue on this point.  Different implementations
+    /// interpret the RFC in different ways on the received TTL when
+    /// digesting the message: BIND 9 uses the received value (even if
+    /// it's not 0) as part of the TSIG variables; NLnet Labs' LDNS and NSD
+    /// always use a fixed constant of 0 regardless of the received TTL value.
+    /// This means if and when an extension with non 0 TTL is introduced
+    /// there will be interoperability problems in the form of verification
+    /// failure.  By explicitly rejecting it (and subsequently returning
+    /// a response with a format error) we can indicate the source of the
+    /// problem more clearly than a "bad signature" TSIG error, which can
+    /// happen for various reasons.  On the other hand, rejecting unexpected
+    /// RR classes is mostly for consistency; the RFC lists these two fields
+    /// in the same way, so it makes more sense to handle them equally.
+    /// (BIND 9 rejects unexpected RR classes for TSIG, but that is part of
+    /// general check on RR classes on received RRs; it generally requests
+    /// all classes are the same, and if the protocol specifies the use of
+    /// a particular class for a particular type of RR, it requests that
+    /// class be used).
+    ///
+    /// Likewise, if \c rdata is not of type \c any::TSIG, an exception of
+    /// class DNSMessageFORMERR will be thrown.  When the caller is a
+    /// DNS message parser and builds \c rdata from incoming wire format
+    /// data as described above, this case happens when the RR class is
+    /// different from ANY (in the implementation, the type check takes place
+    /// before the explicit check against the RR class explained in the
+    /// previous paragraph).
+    ///
+    /// The \c length parameter is intended to be the length of the TSIG RR
+    /// (from the beginning of the owner name to the end of the RDATA) when
+    /// the caller is a DNS message parser.  Note that it is the actual length
+    /// for the RR in the format; if the owner name or the algorithm name
+    /// (in the RDATA) is compressed (although the latter should not be
+    /// compressed according to RFC3597), the length must be the size of the
+    /// compressed data.  The length is recorded inside the class and will
+    /// be returned via subsequent calls to \c getLength().  It's intended to
+    /// be used in the context TSIG verification; in the verify process
+    /// the MAC computation must be performed for the original data without
+    /// TSIG, so, to avoid parsing the entire data in the verify process
+    /// again, it's necessary to record information that can identify the
+    /// length to be digested for the MAC.  This parameter serves for that
+    /// purpose.
+    ///
+    /// \note Since the constructor doesn't take the wire format data per se,
+    /// it doesn't (and cannot) check the validity of \c length, and simply
+    /// accepts any given value.  It even accepts obviously invalid values
+    /// such as 0.  It's caller's responsibility to provide a valid value of
+    /// length, and, the verifier's responsibility to use the length safely.
+    ///
+    /// <b>DISCUSSION:</b> this design is fragile in that it introduces
+    /// a tight coupling between message parsing and TSIG verification via
+    /// the \c TSIGRecord class.  In terms of responsibility decoupling,
+    /// the ideal way to have \c TSIGRecord remember the entire wire data
+    /// along with the length of the TSIG.  Then in the TSIG verification
+    /// we could refer to the necessary potion of data solely from a
+    /// \c TSIGRecord object.  However, this approach would require expensive
+    /// heavy copy of the original data or introduce another kind of coupling
+    /// between the data holder and this class (if the original data is freed
+    /// while a \c TSIGRecord object referencing the data still exists, the
+    /// result will be catastrophic).  As a "best current compromise", we
+    /// use the current design.  We may reconsider it if it turns out to
+    /// cause a big problem or we come up with a better idea.
+    ///
+    /// \exception DNSMessageFORMERR Given RR parameters are invalid for TSIG.
+    /// \exception std::bad_alloc Internal resource allocation fails.
+    ///
+    /// \param name The owner name of the TSIG RR
+    /// \param rrclass The RR class of the RR.  Must be \c RRClass::ANY()
+    /// (see above)
+    /// \param ttl The TTL of the RR.  Must be 0 (see above)
+    /// \param rdata The RDATA of the RR.  Must be of type \c any::TSIG.
+    /// \param length The size of the RR (see above)
+    TSIGRecord(const Name& name, const RRClass& rrclass, const RRTTL& ttl,
+               const rdata::Rdata& rdata, size_t length);
+    //@}
+
+    /// Return the owner name of the TSIG RR, which is the TSIG key name
+    ///
+    /// \exception None
+    const Name& getName() const { return (key_name_); }
+
+    /// Return the RDATA of the TSIG RR
+    ///
+    /// \exception None
+    const rdata::any::TSIG& getRdata() const { return (rdata_); }
+
+    /// \name Protocol constants and defaults
+    ///
+    //@{
+    /// Return the RR class of TSIG
+    ///
+    /// TSIG always uses the ANY RR class.  This static method returns it,
+    /// when, though unlikely, an application wants to know which class TSIG
+    /// is supposed to use.
+    ///
+    /// \exception None
+    static const RRClass& getClass();
+
+    /// Return the TTL value of TSIG
+    ///
+    /// TSIG always uses 0 TTL.  This static method returns it,
+    /// when, though unlikely, an application wants to know the TTL TSIG
+    /// is supposed to use.
+    ///
+    /// \exception None
+    static const RRTTL& getTTL();
+    //@}
+
+    /// Return the length of the TSIG record
+    ///
+    /// When constructed from the key name and RDATA, it is the length of
+    /// the record when it is rendered by the \c toWire() method.
+    ///
+    /// \note When constructed "from wire", that will mean the length of
+    /// the wire format data for the TSIG RR.  The length will be necessary
+    /// to verify the message once parse is completed.
+    ///
+    /// \exception None
+    size_t getLength() const { return (length_); }
+
+    /// \brief Render the \c TSIG RR in the wire format.
+    ///
+    /// This method renders the TSIG record as a form of a DNS TSIG RR
+    /// via \c renderer, which encapsulates output buffer and other rendering
+    /// contexts.
+    ///
+    /// Normally this version of \c toWire() method tries to compress the
+    /// owner name of the RR whenever possible, but this method intentionally
+    /// skips owner name compression.  This is due to a report that some
+    /// Windows clients refuse a TSIG if its owner name is compressed
+    /// (See http://marc.info/?l=bind-workers&m=126637138430819&w=2).
+    /// Reportedly this seemed to be specific to GSS-TSIG, but this
+    /// implementation skip compression regardless of the algorithm.
+    ///
+    /// If by adding the TSIG RR the message size would exceed the limit
+    /// maintained in \c renderer, this method skips rendering the RR
+    /// and returns 0 and mark \c renderer as "truncated" (so that a
+    /// subsequent call to \c isTruncated() on \c renderer will result in
+    /// \c true); otherwise it returns 1, which is the number of RR
+    /// rendered.
+    ///
+    /// \note If the caller follows the specification of adding TSIG
+    /// as described in RFC2845, this should not happen; the caller is
+    /// generally expected to leave a sufficient room in the message for
+    /// the TSIG.  But this method checks the unexpected case nevertheless.
+    ///
+    /// \exception std::bad_alloc Internal resource allocation fails (this
+    /// should be rare).
+    ///
+    /// \param renderer DNS message rendering context that encapsulates the
+    /// output buffer and name compression information.
+    /// \return 1 if the TSIG RR fits in the message size limit; otherwise 0.
+    int toWire(AbstractMessageRenderer& renderer) const;
+
+    /// \brief Render the \c TSIG RR in the wire format.
+    ///
+    /// This method is same as \c toWire(AbstractMessageRenderer&)const
+    /// except it renders the RR in an \c OutputBuffer and therefore
+    /// does not care about message size limit.
+    /// As a consequence it always returns 1.
+    int toWire(isc::util::OutputBuffer& buffer) const;
+
+    /// Convert the TSIG record to a string.
+    ///
+    /// The output format is the same as the result of \c toText() for
+    /// other normal types of RRsets (with always using the same RR class
+    /// and TTL).  It also ends with a newline.
+    ///
+    /// \exception std::bad_alloc Internal resource allocation fails (this
+    /// should be rare).
+    ///
+    /// \return A string representation of \c TSIG record
+    std::string toText() const;
+
+    /// The TTL value to be used in TSIG RRs.
+    static const uint32_t TSIG_TTL = 0;
+    //@}
+
+private:
+    const Name key_name_;
+    const rdata::any::TSIG rdata_;
+    const size_t length_;
+};
+
+/// A pointer-like type pointing to a \c TSIGRecord object.
+typedef boost::shared_ptr<TSIGRecord> TSIGRecordPtr;
+
+/// A pointer-like type pointing to an immutable \c TSIGRecord object.
+typedef boost::shared_ptr<const TSIGRecord> ConstTSIGRecordPtr;
+
+/// Insert the \c TSIGRecord as a string into stream.
+///
+/// This method convert \c record into a string and inserts it into the
+/// output stream \c os.
+///
+/// \param os A \c std::ostream object on which the insertion operation is
+/// performed.
+/// \param record A \c TSIGRecord object output by the operation.
+/// \return A reference to the same \c std::ostream object referenced by
+/// parameter \c os after the insertion operation.
+std::ostream& operator<<(std::ostream& os, const TSIGRecord& record);
+}
+}
+
+#endif  // __TSIGRECORD_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/log/compiler/Makefile.am b/src/lib/log/compiler/Makefile.am
index d51ba05..1f47ba9 100644
--- a/src/lib/log/compiler/Makefile.am
+++ b/src/lib/log/compiler/Makefile.am
@@ -1,8 +1,6 @@
 SUBDIRS = .
 
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
-AM_CPPFLAGS += -I$(top_srcdir)/src/lib/log -I$(top_builddir)/src/lib/log
-AM_CPPFLAGS += -I$(top_srcdir)/src/lib/util -I$(top_builddir)/src/lib/util
 AM_CPPFLAGS += $(BOOST_INCLUDES)
 
 AM_CXXFLAGS = $(B10_CXXFLAGS)
@@ -14,7 +12,7 @@ endif
 CLEANFILES = *.gcno *.gcda
 
 noinst_PROGRAMS = message
-message_SOURCES = message.cc
-message_LDADD  = $(top_builddir)/src/lib/log/liblog.la
-message_LDADD += $(top_builddir)/src/lib/util/libutil.la
 
+message_SOURCES = message.cc
+message_LDADD   = $(top_builddir)/src/lib/log/liblog.la
+message_LDADD  += $(top_builddir)/src/lib/util/libutil.la
diff --git a/src/lib/log/compiler/message.cc b/src/lib/log/compiler/message.cc
index 2e4996a..1b3807e 100644
--- a/src/lib/log/compiler/message.cc
+++ b/src/lib/log/compiler/message.cc
@@ -30,19 +30,22 @@
 #include <util/filename.h>
 #include <util/strutil.h>
 
+#include <log/log_messages.h>
 #include <log/message_dictionary.h>
 #include <log/message_exception.h>
 #include <log/message_reader.h>
-#include <log/messagedef.h>
 
 #include <log/logger.h>
 
+#include <boost/foreach.hpp>
+
 using namespace std;
 using namespace isc::log;
 using namespace isc::util;
 
 static const char* VERSION = "1.0-0";
 
+/// \file log/compiler/message.cc
 /// \brief Message Compiler
 ///
 /// \b Overview<BR>
@@ -52,20 +55,19 @@ static const char* VERSION = "1.0-0";
 /// \li A .cc file containing code that adds the messages to the program's
 /// message dictionary at start-up time.
 ///
-/// Alternatively, the program can produce a .py file that contains the
-/// message definitions.
-///
-
 /// \b Invocation<BR>
 /// The program is invoked with the command:
 ///
-/// <tt>message [-v | -h | \<message-file\>]</tt>
+/// <tt>message [-v | -h | -p | -d <dir> | <message-file>]</tt>
 ///
-/// It reads the message file and writes out two files of the same name but with
-/// extensions of .h and .cc.
+/// It reads the message file and writes out two files of the same
+/// name in the current working directory (unless -d is used) but
+/// with extensions of .h and .cc, or .py if -p is used.
 ///
-/// \-v causes it to print the version number and exit. \-h prints a help
-/// message (and exits).
+/// -v causes it to print the version number and exit. -h prints a help
+/// message (and exits). -p sets the output to python. -d <dir> will make
+/// it write the output file(s) to dir instead of current working
+/// directory
 
 
 /// \brief Print Version
@@ -84,10 +86,12 @@ version() {
 void
 usage() {
     cout <<
-        "Usage: message [-h] [-v] <message-file>\n" <<
+        "Usage: message [-h] [-v] [-p] [-d dir] <message-file>\n" <<
         "\n" <<
         "-h       Print this message and exit\n" <<
         "-v       Print the program version and exit\n" <<
+        "-p       Output python source instead of C++ ones\n" <<
+        "-d <dir> Place output files in given directory\n" <<
         "\n" <<
         "<message-file> is the name of the input message file.\n";
 }
@@ -109,7 +113,7 @@ currentTime() {
 
     // Convert to string and strip out the trailing newline
     string current_time = buffer;
-    return isc::util::str::trim(current_time);
+    return (isc::util::str::trim(current_time));
 }
 
 
@@ -130,7 +134,7 @@ sentinel(Filename& file) {
     string ext = file.extension();
     string sentinel_text = "__" + name + "_" + ext.substr(1);
     isc::util::str::uppercase(sentinel_text);
-    return sentinel_text;
+    return (sentinel_text);
 }
 
 
@@ -157,7 +161,7 @@ quoteString(const string& instring) {
         outstring += instring[i];
     }
 
-    return outstring;
+    return (outstring);
 }
 
 
@@ -180,7 +184,7 @@ sortedIdentifiers(MessageDictionary& dictionary) {
     }
     sort(ident.begin(), ident.end());
 
-    return ident;
+    return (ident);
 }
 
 
@@ -210,7 +214,7 @@ splitNamespace(string ns) {
 
     // ... and return the vector of namespace components split on the single
     // colon.
-    return isc::util::str::tokens(ns, ":");
+    return (isc::util::str::tokens(ns, ":"));
 }
 
 
@@ -222,7 +226,7 @@ writeOpeningNamespace(ostream& output, const vector<string>& ns) {
     if (!ns.empty()) {
 
         // Output namespaces in correct order
-        for (unsigned int i = 0; i < ns.size(); ++i) {
+        for (int i = 0; i < ns.size(); ++i) {
             output << "namespace " << ns[i] << " {\n";
         }
         output << "\n";
@@ -243,6 +247,52 @@ writeClosingNamespace(ostream& output, const vector<string>& ns) {
     }
 }
 
+/// \breif Write python file
+///
+/// Writes the python file containing the symbol definitions as module level
+/// constants. These are objects which register themself at creation time,
+/// so they can be replaced by dictionary later.
+///
+/// \param file Name of the message file. The source code is written to a file
+///     file of the same name but with a .py suffix.
+/// \param dictionary The dictionary holding the message definitions.
+/// \param output_directory if not null NULL, output files are written
+///     to the given directory. If NULL, they are written to the current
+///     working directory.
+///
+/// \note We don't use the namespace as in C++. We don't need it, because
+///     python file/module works as implicit namespace as well.
+
+void
+writePythonFile(const string& file, MessageDictionary& dictionary,
+                const char* output_directory)
+{
+    Filename message_file(file);
+    Filename python_file(Filename(message_file.name()).useAsDefault(".py"));
+    if (output_directory != NULL) {
+        python_file.setDirectory(output_directory);
+    }
+
+    // Open the file for writing
+    ofstream pyfile(python_file.fullName().c_str());
+
+    // Write the comment and imports
+    pyfile <<
+        "# File created from " << message_file.fullName() << " on " <<
+            currentTime() << "\n" <<
+        "\n" <<
+        "import isc.log\n" <<
+        "\n";
+
+    vector<string> idents(sortedIdentifiers(dictionary));
+    BOOST_FOREACH(const string& ident, idents) {
+        pyfile << ident << " = isc.log.create_message(\"" <<
+            ident << "\", \"" << quoteString(dictionary.getText(ident)) <<
+            "\")\n";
+    }
+
+    pyfile.close();
+}
 
 /// \brief Write Header File
 ///
@@ -253,17 +303,22 @@ writeClosingNamespace(ostream& output, const vector<string>& ns) {
 ///
 /// \param file Name of the message file.  The header file is written to a
 /// file of the same name but with a .h suffix.
-/// \param prefix Prefix string to use in symbols
 /// \param ns Namespace in which the definitions are to be placed.  An empty
 /// string indicates no namespace.
 /// \param dictionary Dictionary holding the message definitions.
+/// \param output_directory if not null NULL, output files are written
+///     to the given directory. If NULL, they are written to the current
+///     working directory.
 
 void
-writeHeaderFile(const string& file, const string& prefix,
-        const vector<string>& ns_components, MessageDictionary& dictionary)
+writeHeaderFile(const string& file, const vector<string>& ns_components,
+                MessageDictionary& dictionary, const char* output_directory)
 {
     Filename message_file(file);
-    Filename header_file(message_file.useAsDefault(".h"));
+    Filename header_file(Filename(message_file.name()).useAsDefault(".h"));
+    if (output_directory != NULL) {
+        header_file.setDirectory(output_directory);
+    }
 
     // Text to use as the sentinels.
     string sentinel_text = sentinel(header_file);
@@ -271,52 +326,46 @@ writeHeaderFile(const string& file, const string& prefix,
     // Open the output file for writing
     ofstream hfile(header_file.fullName().c_str());
 
-    try {
-        if (hfile.fail()) {
-            throw MessageException(MSG_OPNMSGOUT, header_file.fullName(),
-                strerror(errno));
-        }
-
-        // Write the header preamble.  If there is an error, we'll pick it up
-        // after the last write.
-
-        hfile <<
-            "// File created from " << message_file.fullName() << " on " <<
-                currentTime() << "\n" <<
-             "\n" <<
-             "#ifndef " << sentinel_text << "\n" <<
-             "#define "  << sentinel_text << "\n" <<
-             "\n" <<
-             "#include <log/message_types.h>\n" <<
-             "\n";
-
-        // Write the message identifiers, bounded by a namespace declaration
-        writeOpeningNamespace(hfile, ns_components);
-
-        vector<string> idents = sortedIdentifiers(dictionary);
-        for (vector<string>::const_iterator j = idents.begin();
-            j != idents.end(); ++j) {
-            hfile << "extern const isc::log::MessageID " << prefix << *j << ";\n";
-        }
-        hfile << "\n";
+    if (hfile.fail()) {
+        throw MessageException(LOG_OPEN_OUTPUT_FAIL, header_file.fullName(),
+            strerror(errno));
+    }
 
-        writeClosingNamespace(hfile, ns_components);
+    // Write the header preamble.  If there is an error, we'll pick it up
+    // after the last write.
+
+    hfile <<
+        "// File created from " << message_file.fullName() << " on " <<
+            currentTime() << "\n" <<
+         "\n" <<
+         "#ifndef " << sentinel_text << "\n" <<
+         "#define "  << sentinel_text << "\n" <<
+         "\n" <<
+         "#include <log/message_types.h>\n" <<
+         "\n";
+
+    // Write the message identifiers, bounded by a namespace declaration
+    writeOpeningNamespace(hfile, ns_components);
+
+    vector<string> idents = sortedIdentifiers(dictionary);
+    for (vector<string>::const_iterator j = idents.begin();
+        j != idents.end(); ++j) {
+        hfile << "extern const isc::log::MessageID " << *j << ";\n";
+    }
+    hfile << "\n";
 
-        // ... and finally the postamble
-        hfile << "#endif // " << sentinel_text << "\n";
+    writeClosingNamespace(hfile, ns_components);
 
-        // Report errors (if any) and exit
-        if (hfile.fail()) {
-            throw MessageException(MSG_MSGWRTERR, header_file.fullName(),
-                strerror(errno));
-        }
+    // ... and finally the postamble
+    hfile << "#endif // " << sentinel_text << "\n";
 
-        hfile.close();
-    }
-    catch (MessageException&) {
-        hfile.close();
-        throw;
+    // Report errors (if any) and exit
+    if (hfile.fail()) {
+        throw MessageException(LOG_WRITE_ERROR, header_file.fullName(),
+            strerror(errno));
     }
+
+    hfile.close();
 }
 
 
@@ -354,86 +403,93 @@ replaceNonAlphaNum(char c) {
 /// optimisation is done at link-time, not compiler-time.  In this it _may_
 /// decide to remove the initializer object because of a lack of references
 /// to it.  But until BIND-10 is ported to Windows, we won't know.
-
+///
+/// \param file Name of the message file.  The header file is written to a
+/// file of the same name but with a .h suffix.
+/// \param ns Namespace in which the definitions are to be placed.  An empty
+/// string indicates no namespace.
+/// \param dictionary Dictionary holding the message definitions.
+/// \param output_directory if not null NULL, output files are written
+///     to the given directory. If NULL, they are written to the current
+///     working directory.
 void
-writeProgramFile(const string& file, const string& prefix,
-    const vector<string>& ns_components, MessageDictionary& dictionary)
+writeProgramFile(const string& file, const vector<string>& ns_components,
+                 MessageDictionary& dictionary,
+                 const char* output_directory)
 {
     Filename message_file(file);
-    Filename program_file(message_file.useAsDefault(".cc"));
+    Filename program_file(Filename(message_file.name()).useAsDefault(".cc"));
+    if (output_directory) {
+        program_file.setDirectory(output_directory);
+    }
 
     // Open the output file for writing
     ofstream ccfile(program_file.fullName().c_str());
-    try {
-        if (ccfile.fail()) {
-            throw MessageException(MSG_OPNMSGOUT, program_file.fullName(),
-                strerror(errno));
-        }
 
-        // Write the preamble.  If there is an error, we'll pick it up after
-        // the last write.
+    if (ccfile.fail()) {
+        throw MessageException(LOG_OPEN_OUTPUT_FAIL, program_file.fullName(),
+            strerror(errno));
+    }
 
-        ccfile <<
-            "// File created from " << message_file.fullName() << " on " <<
-                currentTime() << "\n" <<
-             "\n" <<
-             "#include <cstddef>\n" <<
-             "#include <log/message_types.h>\n" <<
-             "#include <log/message_initializer.h>\n" <<
-             "\n";
+    // Write the preamble.  If there is an error, we'll pick it up after
+    // the last write.
 
-        // Declare the message symbols themselves.
+    ccfile <<
+        "// File created from " << message_file.fullName() << " on " <<
+            currentTime() << "\n" <<
+         "\n" <<
+         "#include <cstddef>\n" <<
+         "#include <log/message_types.h>\n" <<
+         "#include <log/message_initializer.h>\n" <<
+         "\n";
 
-        writeOpeningNamespace(ccfile, ns_components);
+    // Declare the message symbols themselves.
 
-        vector<string> idents = sortedIdentifiers(dictionary);
-        for (vector<string>::const_iterator j = idents.begin();
-            j != idents.end(); ++j) {
-            ccfile << "extern const isc::log::MessageID " << prefix << *j <<
-                " = \"" << *j << "\";\n";
-        }
-        ccfile << "\n";
+    writeOpeningNamespace(ccfile, ns_components);
 
-        writeClosingNamespace(ccfile, ns_components);
+    vector<string> idents = sortedIdentifiers(dictionary);
+    for (vector<string>::const_iterator j = idents.begin();
+        j != idents.end(); ++j) {
+        ccfile << "extern const isc::log::MessageID " << *j <<
+            " = \"" << *j << "\";\n";
+    }
+    ccfile << "\n";
 
-        // Now the code for the message initialization.
+    writeClosingNamespace(ccfile, ns_components);
 
-        ccfile <<
-             "namespace {\n" <<
-             "\n" <<
-             "const char* values[] = {\n";
+    // Now the code for the message initialization.
 
-        // Output the identifiers and the associated text.
-        idents = sortedIdentifiers(dictionary);
-        for (vector<string>::const_iterator i = idents.begin();
-            i != idents.end(); ++i) {
-                ccfile << "    \"" << *i << "\", \"" <<
-                    quoteString(dictionary.getText(*i)) << "\",\n";
-        }
+    ccfile <<
+         "namespace {\n" <<
+         "\n" <<
+         "const char* values[] = {\n";
 
+    // Output the identifiers and the associated text.
+    idents = sortedIdentifiers(dictionary);
+    for (vector<string>::const_iterator i = idents.begin();
+        i != idents.end(); ++i) {
+            ccfile << "    \"" << *i << "\", \"" <<
+                quoteString(dictionary.getText(*i)) << "\",\n";
+    }
 
-        // ... and the postamble
-        ccfile <<
-            "    NULL\n" <<
-            "};\n" <<
-            "\n" <<
-            "const isc::log::MessageInitializer initializer(values);\n" <<
-            "\n" <<
-            "} // Anonymous namespace\n" <<
-            "\n";
-
-        // Report errors (if any) and exit
-        if (ccfile.fail()) {
-            throw MessageException(MSG_MSGWRTERR, program_file.fullName(),
-                strerror(errno));
-        }
 
-        ccfile.close();
-    }
-    catch (MessageException&) {
-        ccfile.close();
-        throw;
+    // ... and the postamble
+    ccfile <<
+        "    NULL\n" <<
+        "};\n" <<
+        "\n" <<
+        "const isc::log::MessageInitializer initializer(values);\n" <<
+        "\n" <<
+        "} // Anonymous namespace\n" <<
+        "\n";
+
+    // Report errors (if any) and exit
+    if (ccfile.fail()) {
+        throw MessageException(LOG_WRITE_ERROR, program_file.fullName(),
+            strerror(errno));
     }
+
+    ccfile.close();
 }
 
 
@@ -473,24 +529,35 @@ warnDuplicates(MessageReader& reader) {
 int
 main(int argc, char* argv[]) {
 
-    const char* soptions = "hv";               // Short options
+    const char* soptions = "hvpd:";               // Short options
 
     optind = 1;             // Ensure we start a new scan
     int  opt;               // Value of the option
 
+    bool doPython = false;
+    const char *output_directory = NULL;
+
     while ((opt = getopt(argc, argv, soptions)) != -1) {
         switch (opt) {
+            case 'd':
+                output_directory = optarg;
+                break;
+
+            case 'p':
+                doPython = true;
+                break;
+
             case 'h':
                 usage();
-                return 0;
+                return (0);
 
             case 'v':
                 version();
-                return 0;
+                return (0);
 
             default:
                 // A message will have already been output about the error.
-                return 1;
+                return (1);
         }
     }
 
@@ -498,11 +565,11 @@ main(int argc, char* argv[]) {
     if (optind < (argc - 1)) {
         cout << "Error: excess arguments in command line\n";
         usage();
-        return 1;
+        return (1);
     } else if (optind >= argc) {
         cout << "Error: missing message file\n";
         usage();
-        return 1;
+        return (1);
     }
     string message_file = argv[optind];
 
@@ -515,36 +582,50 @@ main(int argc, char* argv[]) {
         MessageReader reader(&dictionary);
         reader.readFile(message_file);
 
-        // Get the namespace into which the message definitions will be put and
-        // split it into components.
-        vector<string> ns_components = splitNamespace(reader.getNamespace());
-
-        // Write the header file.
-        writeHeaderFile(message_file, reader.getPrefix(), ns_components,
-            dictionary);
-
-        // Write the file that defines the message symbols and text
-        writeProgramFile(message_file, reader.getPrefix(), ns_components,
-            dictionary);
-
+        if (doPython) {
+            // Warn in case of ignored directives
+            if (!reader.getNamespace().empty()) {
+                cerr << "Python mode, ignoring the $NAMESPACE directive" <<
+                    endl;
+            }
+
+            // Write the whole python file
+            writePythonFile(message_file, dictionary, output_directory);
+        } else {
+            // Get the namespace into which the message definitions will be put and
+            // split it into components.
+            vector<string> ns_components =
+                splitNamespace(reader.getNamespace());
+
+            // Write the header file.
+            writeHeaderFile(message_file, ns_components, dictionary,
+                            output_directory);
+
+            // Write the file that defines the message symbols and text
+            writeProgramFile(message_file, ns_components, dictionary,
+                             output_directory);
+        }
 
         // Finally, warn of any duplicates encountered.
         warnDuplicates(reader);
     }
-    catch (MessageException& e) {
+    catch (const MessageException& e) {
         // Create an error message from the ID and the text
         MessageDictionary& global = MessageDictionary::globalDictionary();
         string text = e.id();
         text += ", ";
         text += global.getText(e.id());
-
         // Format with arguments
-        text = isc::util::str::format(text, e.arguments());
+        vector<string> args(e.arguments());
+        for (size_t i(0); i < args.size(); ++ i) {
+            replacePlaceholder(&text, args[i], i + 1);
+        }
+
         cerr << text << "\n";
 
-        return 1;
+        return (1);
     }
 
-    return 0;
+    return (0);
 
 }
diff --git a/src/lib/log/logger_manager.cc b/src/lib/log/logger_manager.cc
index 70e0d6f..1d5f40f 100644
--- a/src/lib/log/logger_manager.cc
+++ b/src/lib/log/logger_manager.cc
@@ -167,7 +167,7 @@ LoggerManager::readLocalMessageFile(const char* file) {
         // Log the variable number of arguments.  The actual message will be
         // logged when the error_message variable is destroyed.
         Formatter<isc::log::Logger> error_message = logger.error(ident);
-        for (int i = 0; i < args.size(); ++i) {
+        for (unsigned int i = 0; i < args.size(); ++i) {
             error_message = error_message.arg(args[i]);
         }
     }
diff --git a/src/lib/log/tests/Makefile.am b/src/lib/log/tests/Makefile.am
index b9fe150..069a7b4 100644
--- a/src/lib/log/tests/Makefile.am
+++ b/src/lib/log/tests/Makefile.am
@@ -2,8 +2,6 @@ SUBDIRS = .
 
 AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
 AM_CPPFLAGS += $(BOOST_INCLUDES)
-AM_CPPFLAGS += -I$(top_srcdir)/src/lib/log -I$(top_builddir)/src/lib/log
-AM_CPPFLAGS += -I$(top_srcdir)/src/lib/util -I$(top_builddir)/src/lib/util
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 
 if USE_STATIC_LINK
@@ -15,32 +13,64 @@ CLEANFILES = *.gcno *.gcda
 TESTS =
 if HAVE_GTEST
 TESTS += run_unittests
-run_unittests_SOURCES  = root_logger_name_unittest.cc
+run_unittests_SOURCES  = run_unittests.cc
+run_unittests_SOURCES += log_formatter_unittest.cc
+run_unittests_SOURCES += logger_level_impl_unittest.cc
+run_unittests_SOURCES += logger_level_unittest.cc
+run_unittests_SOURCES += logger_manager_unittest.cc
+run_unittests_SOURCES += logger_name_unittest.cc
+run_unittests_SOURCES += logger_support_unittest.cc
 run_unittests_SOURCES += logger_unittest.cc
+run_unittests_SOURCES += logger_specification_unittest.cc
 run_unittests_SOURCES += message_dictionary_unittest.cc
-run_unittests_SOURCES += message_reader_unittest.cc
-run_unittests_SOURCES += message_initializer_unittest.cc
 run_unittests_SOURCES += message_initializer_unittest_2.cc
-run_unittests_SOURCES += run_unittests.cc
+run_unittests_SOURCES += message_initializer_unittest.cc
+run_unittests_SOURCES += message_reader_unittest.cc
+run_unittests_SOURCES += output_option_unittest.cc
 
-run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
+run_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
 
+run_unittests_CXXFLAGS = $(AM_CXXFLAGS)
+if USE_CLANGPP
+# This is to workaround unused variables tcout and tcerr in
+# log4cplus's streams.h.
+run_unittests_CXXFLAGS += -Wno-unused-variable
+endif
 run_unittests_LDADD  = $(GTEST_LDADD)
 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/util/libutil.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
 endif
 
-TESTS += logger_support_test
-logger_support_test_SOURCES = logger_support_test.cc
-logger_support_test_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-logger_support_test_LDFLAGS = $(AM_LDFLAGS)
-logger_support_test_LDADD  = $(top_builddir)/src/lib/log/liblog.la
-logger_support_test_LDADD += $(top_builddir)/src/lib/util/libutil.la
+check_PROGRAMS = logger_example
+logger_example_SOURCES = logger_example.cc
+logger_example_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+logger_example_LDFLAGS = $(AM_LDFLAGS) $(LOG4CPLUS_LDFLAGS)
+logger_example_LDADD  = $(top_builddir)/src/lib/log/liblog.la
+logger_example_LDADD += $(top_builddir)/src/lib/util/libutil.la
+logger_example_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+
+check_PROGRAMS += init_logger_test
+init_logger_test_SOURCES = init_logger_test.cc
+init_logger_test_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+init_logger_test_LDFLAGS = $(AM_LDFLAGS) $(LOG4CPLUS_LDFLAGS)
+init_logger_test_LDADD  = $(top_builddir)/src/lib/log/liblog.la
+init_logger_test_LDADD += $(top_builddir)/src/lib/util/libutil.la
+init_logger_test_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 
 noinst_PROGRAMS = $(TESTS)
 
-# Additional test using the shell
-PYTESTS = run_time_init_test.sh
+# Additional test using the shell.  These are principally tests
+# where the global logging environment is affected, and where the
+# output needs to be compared with stored output (where "cut" and
+# "diff" are useful utilities).
+
 check-local:
-	$(SHELL) $(abs_builddir)/run_time_init_test.sh
+	$(SHELL) $(abs_builddir)/console_test.sh
+	$(SHELL) $(abs_builddir)/destination_test.sh
+	$(SHELL) $(abs_builddir)/init_logger_test.sh
+	$(SHELL) $(abs_builddir)/local_file_test.sh
+	$(SHELL) $(abs_builddir)/severity_test.sh
diff --git a/src/lib/log/tests/console_test.sh.in b/src/lib/log/tests/console_test.sh.in
new file mode 100755
index 0000000..a16dc23
--- /dev/null
+++ b/src/lib/log/tests/console_test.sh.in
@@ -0,0 +1,67 @@
+#!/bin/sh
+# 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.
+
+# The logger supports the idea of a "console" logger than logs to either stdout
+# or stderr.  This test checks that both these options work.
+
+testname="Console output test"
+echo $testname
+
+failcount=0
+tempfile=@abs_builddir@/console_test_tempfile_$$
+
+# Look at tempfile and check that the count equals the expected count
+passfail() {
+    count=`wc -l $tempfile | awk '{print $1}'`
+    if [ $count -eq $1 ]; then
+        echo " pass"
+    else
+        echo " FAIL"
+        failcount=`expr $failcount + $1`
+    fi
+}
+
+echo -n "1. Checking that console output to stdout goes to stdout:"
+rm -f $tempfile
+./logger_example -c stdout -s error 1> $tempfile 2> /dev/null
+passfail 4
+
+echo -n "2. Checking that console output to stdout does not go to stderr:"
+rm -f $tempfile
+./logger_example -c stdout -s error 1> /dev/null 2> $tempfile
+passfail 0
+
+echo -n "3. Checking that console output to stderr goes to stderr:"
+rm -f $tempfile
+./logger_example -c stderr -s error 1> /dev/null 2> $tempfile
+passfail 4
+
+echo -n "4. Checking that console output to stderr does not go to stdout:"
+rm -f $tempfile
+./logger_example -c stderr -s error 1> $tempfile 2> /dev/null
+passfail 0
+
+if [ $failcount -eq 0 ]; then
+    echo "PASS: $testname"
+elif [ $failcount -eq 1 ]; then
+    echo "FAIL: $testname - 1 test failed"
+else
+    echo "FAIL: $testname - $failcount tests failed"
+fi
+
+# Tidy up
+rm -f $tempfile
+
+exit $failcount
diff --git a/src/lib/log/tests/console_test.sh.win32 b/src/lib/log/tests/console_test.sh.win32
new file mode 100755
index 0000000..36ca4e7
--- /dev/null
+++ b/src/lib/log/tests/console_test.sh.win32
@@ -0,0 +1,67 @@
+#!/bin/sh
+# 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.
+
+# The logger supports the idea of a "console" logger than logs to either stdout
+# or stderr.  This test checks that both these options work.
+
+testname="Console output test"
+echo $testname
+
+failcount=0
+tempfile=c:/cygwin/home/fdupont/bind10.trac826/src/lib/log/tests/console_test_tempfile_$$
+
+# Look at tempfile and check that the count equals the expected count
+passfail() {
+    count=`wc -l $tempfile | awk '{print $1}'`
+    if [ $count -eq $1 ]; then
+        echo " pass"
+    else
+        echo " FAIL"
+        failcount=`expr $failcount + $1`
+    fi
+}
+
+echo -n "1. Checking that console output to stdout goes to stdout:"
+rm -f $tempfile
+./logger_example.exe -c stdout -s error 1> $tempfile 2> /dev/null
+passfail 4
+
+echo -n "2. Checking that console output to stdout does not go to stderr:"
+rm -f $tempfile
+./logger_example.exe -c stdout -s error 1> /dev/null 2> $tempfile
+passfail 0
+
+echo -n "3. Checking that console output to stderr goes to stderr:"
+rm -f $tempfile
+./logger_example.exe -c stderr -s error 1> /dev/null 2> $tempfile
+passfail 4
+
+echo -n "4. Checking that console output to stderr does not go to stdout:"
+rm -f $tempfile
+./logger_example.exe -c stderr -s error 1> $tempfile 2> /dev/null
+passfail 0
+
+if [ $failcount -eq 0 ]; then
+    echo "PASS: $testname"
+elif [ $failcount -eq 1 ]; then
+    echo "FAIL: $testname - 1 test failed"
+else
+    echo "FAIL: $testname - $failcount tests failed"
+fi
+
+# Tidy up
+rm -f $tempfile
+
+exit $failcount
diff --git a/src/lib/log/tests/destination_test.sh.in b/src/lib/log/tests/destination_test.sh.in
new file mode 100755
index 0000000..1cfb9fb
--- /dev/null
+++ b/src/lib/log/tests/destination_test.sh.in
@@ -0,0 +1,91 @@
+#!/bin/sh
+# 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.
+
+# Checks that the logger will route messages to the chosen destination.
+
+testname="Destination test"
+echo $testname
+
+failcount=0
+tempfile=@abs_builddir@/destination_test_tempfile_$$
+destfile1=@abs_builddir@/destination_test_destfile_1_$$
+destfile2=@abs_builddir@/destination_test_destfile_2_$$
+
+passfail() {
+    if [ $1 -eq 0 ]; then
+        echo " pass"
+    else
+        echo " FAIL"
+        failcount=`expr $failcount + $1`
+    fi
+}
+
+echo "1. One logger, multiple destinations:"
+cat > $tempfile << .
+FATAL [example] LOG_WRITE_ERROR error writing to test1: 42
+ERROR [example] LOG_READING_LOCAL_FILE reading local message file dummy/file
+FATAL [example.beta] LOG_BAD_SEVERITY unrecognized log severity: beta_fatal
+ERROR [example.beta] LOG_BAD_DESTINATION unrecognized log destination: beta_error
+.
+rm -f $destfile1 $destfile2
+./logger_example -s error -f $destfile1 -f $destfile2
+
+echo -n  "   - destination 1:"
+cut -d' ' -f3- $destfile1 | diff $tempfile -
+passfail $?
+
+echo -n  "   - destination 2:"
+cut -d' ' -f3- $destfile2 | diff $tempfile -
+passfail $?
+
+echo     "2. Two loggers, different destinations and severities"
+rm -f $destfile1 $destfile2
+./logger_example -l example -s info -f $destfile1 -l alpha -s warn -f $destfile2
+
+# All output for example and example.beta should have gone to destfile1.
+# Output for example.alpha should have done to destfile2.
+
+cat > $tempfile << .
+FATAL [example] LOG_WRITE_ERROR error writing to test1: 42
+ERROR [example] LOG_READING_LOCAL_FILE reading local message file dummy/file
+WARN  [example] LOG_BAD_STREAM bad log console output stream: example
+FATAL [example.beta] LOG_BAD_SEVERITY unrecognized log severity: beta_fatal
+ERROR [example.beta] LOG_BAD_DESTINATION unrecognized log destination: beta_error
+WARN  [example.beta] LOG_BAD_STREAM bad log console output stream: beta_warn
+INFO  [example.beta] LOG_READ_ERROR error reading from message file beta: info
+.
+echo -n  "   - destination 1:"
+cut -d' ' -f3- $destfile1 | diff $tempfile -
+passfail $?
+
+echo -n  "   - destination 2:"
+cat > $tempfile << .
+WARN  [example.alpha] LOG_READ_ERROR error reading from message file a.txt: dummy reason
+.
+cut -d' ' -f3- $destfile2 | diff $tempfile -
+passfail $?
+
+if [ $failcount -eq 0 ]; then
+    echo "PASS: $testname"
+elif [ $failcount -eq 1 ]; then
+    echo "FAIL: $testname - 1 test failed"
+else
+    echo "FAIL: $testname - $failcount tests failed"
+fi
+
+# Tidy up.
+rm -f $tempfile $destfile1 $destfile2
+
+exit $failcount
diff --git a/src/lib/log/tests/destination_test.sh.win32 b/src/lib/log/tests/destination_test.sh.win32
new file mode 100755
index 0000000..d076924
--- /dev/null
+++ b/src/lib/log/tests/destination_test.sh.win32
@@ -0,0 +1,91 @@
+#!/bin/sh
+# 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.
+
+# Checks that the logger will route messages to the chosen destination.
+
+testname="Destination test"
+echo $testname
+
+failcount=0
+tempfile=c:/cygwin/home/fdupont/bind10.trac826/src/lib/log/tests/destination_test_tempfile_$$
+destfile1=c:/cygwin/home/fdupont/bind10.trac826/src/lib/log/tests/destination_test_destfile_1_$$
+destfile2=c:/cygwin/home/fdupont/bind10.trac826/src/lib/log/tests/destination_test_destfile_2_$$
+
+passfail() {
+    if [ $1 -eq 0 ]; then
+        echo " pass"
+    else
+        echo " FAIL"
+        failcount=`expr $failcount + $1`
+    fi
+}
+
+echo "1. One logger, multiple destinations:"
+cat > $tempfile << .
+FATAL [example] LOG_WRITE_ERROR error writing to test1: 42
+ERROR [example] LOG_READING_LOCAL_FILE reading local message file dummy/file
+FATAL [example.beta] LOG_BAD_SEVERITY unrecognized log severity: beta_fatal
+ERROR [example.beta] LOG_BAD_DESTINATION unrecognized log destination: beta_error
+.
+rm -f $destfile1 $destfile2
+./logger_example.exe -s error -f $destfile1 -f $destfile2
+
+echo -n  "   - destination 1:"
+cut -d' ' -f3- $destfile1 | diff $tempfile -
+passfail $?
+
+echo -n  "   - destination 2:"
+cut -d' ' -f3- $destfile2 | diff $tempfile -
+passfail $?
+
+echo     "2. Two loggers, different destinations and severities"
+rm -f $destfile1 $destfile2
+./logger_example.exe -l example -s info -f $destfile1 -l alpha -s warn -f $destfile2
+
+# All output for example and example.beta should have gone to destfile1.
+# Output for example.alpha should have done to destfile2.
+
+cat > $tempfile << .
+FATAL [example] LOG_WRITE_ERROR error writing to test1: 42
+ERROR [example] LOG_READING_LOCAL_FILE reading local message file dummy/file
+WARN  [example] LOG_BAD_STREAM bad log console output stream: example
+FATAL [example.beta] LOG_BAD_SEVERITY unrecognized log severity: beta_fatal
+ERROR [example.beta] LOG_BAD_DESTINATION unrecognized log destination: beta_error
+WARN  [example.beta] LOG_BAD_STREAM bad log console output stream: beta_warn
+INFO  [example.beta] LOG_READ_ERROR error reading from message file beta: info
+.
+echo -n  "   - destination 1:"
+cut -d' ' -f3- $destfile1 | diff $tempfile -
+passfail $?
+
+echo -n  "   - destination 2:"
+cat > $tempfile << .
+WARN  [example.alpha] LOG_READ_ERROR error reading from message file a.txt: dummy reason
+.
+cut -d' ' -f3- $destfile2 | diff $tempfile -
+passfail $?
+
+if [ $failcount -eq 0 ]; then
+    echo "PASS: $testname"
+elif [ $failcount -eq 1 ]; then
+    echo "FAIL: $testname - 1 test failed"
+else
+    echo "FAIL: $testname - $failcount tests failed"
+fi
+
+# Tidy up.
+rm -f $tempfile $destfile1 $destfile2
+
+exit $failcount
diff --git a/src/lib/log/tests/init_logger_test.cc b/src/lib/log/tests/init_logger_test.cc
new file mode 100644
index 0000000..104c078
--- /dev/null
+++ b/src/lib/log/tests/init_logger_test.cc
@@ -0,0 +1,42 @@
+// 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 <log/macros.h>
+#include <log/logger_support.h>
+#include <log/log_messages.h>
+
+using namespace isc::log;
+
+/// \brief Test InitLogger
+///
+/// A program used in testing the logger that initializes logging using
+/// initLogger(), then outputs several messages at different severities and
+/// debug levels.  An external script sets the environment variables and checks
+/// that they have the desired effect.
+
+int
+main(int, char**) {
+    initLogger();
+    Logger logger("log");
+
+    LOG_DEBUG(logger, 0, LOG_BAD_DESTINATION).arg("debug-0");
+    LOG_DEBUG(logger, 50, LOG_BAD_DESTINATION).arg("debug-50");
+    LOG_DEBUG(logger, 99, LOG_BAD_DESTINATION).arg("debug-99");
+    LOG_INFO(logger, LOG_BAD_SEVERITY).arg("info");
+    LOG_WARN(logger, LOG_BAD_STREAM).arg("warn");
+    LOG_ERROR(logger, LOG_DUPLICATE_MESSAGE_ID).arg("error");
+    LOG_FATAL(logger, LOG_NO_MESSAGE_ID).arg("fatal");
+
+    return (0);
+}
diff --git a/src/lib/log/tests/init_logger_test.sh.in b/src/lib/log/tests/init_logger_test.sh.in
new file mode 100755
index 0000000..795419b
--- /dev/null
+++ b/src/lib/log/tests/init_logger_test.sh.in
@@ -0,0 +1,110 @@
+#!/bin/sh
+# 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.
+
+# Checks that the initLogger() call uses for unit tests respects the setting of
+# the environment variables.
+
+testname="initLogger test"
+echo $testname
+
+failcount=0
+tempfile=@abs_builddir@/init_logger_test_tempfile_$$
+destfile=@abs_builddir@/init_logger_test_destfile_$$
+
+passfail() {
+    if [ $1 -eq 0 ]; then
+        echo " pass"
+    else
+        echo " FAIL"
+        failcount=`expr $failcount + $1`
+    fi
+}
+
+echo "1. Checking that B10_LOGGER_SEVERITY/B10_LOGGER_DBGLEVEL work"
+
+echo -n  "   - severity=DEBUG, dbglevel=99: "
+cat > $tempfile << .
+DEBUG [bind10.log] LOG_BAD_DESTINATION unrecognized log destination: debug-0
+DEBUG [bind10.log] LOG_BAD_DESTINATION unrecognized log destination: debug-50
+DEBUG [bind10.log] LOG_BAD_DESTINATION unrecognized log destination: debug-99
+INFO  [bind10.log] LOG_BAD_SEVERITY unrecognized log severity: info
+WARN  [bind10.log] LOG_BAD_STREAM bad log console output stream: warn
+ERROR [bind10.log] LOG_DUPLICATE_MESSAGE_ID duplicate message ID (error) in compiled code
+FATAL [bind10.log] LOG_NO_MESSAGE_ID line fatal: message definition line found without a message ID
+.
+B10_LOGGER_DESTINATION=stdout B10_LOGGER_SEVERITY=DEBUG B10_LOGGER_DBGLEVEL=99 ./init_logger_test | \
+    cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+echo -n  "   - severity=DEBUG, dbglevel=50: "
+cat > $tempfile << .
+DEBUG [bind10.log] LOG_BAD_DESTINATION unrecognized log destination: debug-0
+DEBUG [bind10.log] LOG_BAD_DESTINATION unrecognized log destination: debug-50
+INFO  [bind10.log] LOG_BAD_SEVERITY unrecognized log severity: info
+WARN  [bind10.log] LOG_BAD_STREAM bad log console output stream: warn
+ERROR [bind10.log] LOG_DUPLICATE_MESSAGE_ID duplicate message ID (error) in compiled code
+FATAL [bind10.log] LOG_NO_MESSAGE_ID line fatal: message definition line found without a message ID
+.
+B10_LOGGER_DESTINATION=stdout B10_LOGGER_SEVERITY=DEBUG B10_LOGGER_DBGLEVEL=50 ./init_logger_test | \
+    cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+echo -n  "   - severity=WARN: "
+cat > $tempfile << .
+WARN  [bind10.log] LOG_BAD_STREAM bad log console output stream: warn
+ERROR [bind10.log] LOG_DUPLICATE_MESSAGE_ID duplicate message ID (error) in compiled code
+FATAL [bind10.log] LOG_NO_MESSAGE_ID line fatal: message definition line found without a message ID
+.
+B10_LOGGER_DESTINATION=stdout B10_LOGGER_SEVERITY=WARN ./init_logger_test | \
+    cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+echo "2. Checking that B10_LOGGER_DESTINATION works"
+
+echo -n  "   - stdout: "
+cat > $tempfile << .
+FATAL [bind10.log] LOG_NO_MESSAGE_ID line fatal: message definition line found without a message ID
+.
+rm -f $destfile
+B10_LOGGER_SEVERITY=FATAL B10_LOGGER_DESTINATION=stdout ./init_logger_test 1> $destfile
+cut -d' ' -f3- $destfile | diff $tempfile -
+passfail $?
+
+echo -n  "   - stderr: "
+rm -f $destfile
+B10_LOGGER_SEVERITY=FATAL B10_LOGGER_DESTINATION=stderr ./init_logger_test 2> $destfile
+cut -d' ' -f3- $destfile | diff $tempfile -
+passfail $?
+
+echo -n  "   - file: "
+rm -f $destfile
+B10_LOGGER_SEVERITY=FATAL B10_LOGGER_DESTINATION=$destfile ./init_logger_test
+cut -d' ' -f3- $destfile | diff $tempfile -
+passfail $?
+
+# Note: can't automatically test syslog output.
+
+if [ $failcount -eq 0 ]; then
+    echo "PASS: $testname"
+elif [ $failcount -eq 1 ]; then
+    echo "FAIL: $testname - 1 test failed"
+else
+    echo "FAIL: $testname - $failcount tests failed"
+fi
+
+# Tidy up.
+rm -f $tempfile $destfile
+
+exit $failcount
diff --git a/src/lib/log/tests/init_logger_test.sh.win32 b/src/lib/log/tests/init_logger_test.sh.win32
new file mode 100755
index 0000000..ad532ad
--- /dev/null
+++ b/src/lib/log/tests/init_logger_test.sh.win32
@@ -0,0 +1,110 @@
+#!/bin/sh
+# 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.
+
+# Checks that the initLogger() call uses for unit tests respects the setting of
+# the environment variables.
+
+testname="initLogger test"
+echo $testname
+
+failcount=0
+tempfile=c:/cygwin/home/fdupont/bind10.trac826/src/lib/log/tests/init_logger_test_tempfile_$$
+destfile=c:/cygwin/home/fdupont/bind10.trac826/src/lib/log/tests/init_logger_test_destfile_$$
+
+passfail() {
+    if [ $1 -eq 0 ]; then
+        echo " pass"
+    else
+        echo " FAIL"
+        failcount=`expr $failcount + $1`
+    fi
+}
+
+echo "1. Checking that B10_LOGGER_SEVERITY/B10_LOGGER_DBGLEVEL work"
+
+echo -n  "   - severity=DEBUG, dbglevel=99: "
+cat > $tempfile << .
+DEBUG [bind10.log] LOG_BAD_DESTINATION unrecognized log destination: debug-0
+DEBUG [bind10.log] LOG_BAD_DESTINATION unrecognized log destination: debug-50
+DEBUG [bind10.log] LOG_BAD_DESTINATION unrecognized log destination: debug-99
+INFO  [bind10.log] LOG_BAD_SEVERITY unrecognized log severity: info
+WARN  [bind10.log] LOG_BAD_STREAM bad log console output stream: warn
+ERROR [bind10.log] LOG_DUPLICATE_MESSAGE_ID duplicate message ID (error) in compiled code
+FATAL [bind10.log] LOG_NO_MESSAGE_ID line fatal: message definition line found without a message ID
+.
+B10_LOGGER_DESTINATION=stdout B10_LOGGER_SEVERITY=DEBUG B10_LOGGER_DBGLEVEL=99 ./init_logger_test.exe | \
+    cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+echo -n  "   - severity=DEBUG, dbglevel=50: "
+cat > $tempfile << .
+DEBUG [bind10.log] LOG_BAD_DESTINATION unrecognized log destination: debug-0
+DEBUG [bind10.log] LOG_BAD_DESTINATION unrecognized log destination: debug-50
+INFO  [bind10.log] LOG_BAD_SEVERITY unrecognized log severity: info
+WARN  [bind10.log] LOG_BAD_STREAM bad log console output stream: warn
+ERROR [bind10.log] LOG_DUPLICATE_MESSAGE_ID duplicate message ID (error) in compiled code
+FATAL [bind10.log] LOG_NO_MESSAGE_ID line fatal: message definition line found without a message ID
+.
+B10_LOGGER_DESTINATION=stdout B10_LOGGER_SEVERITY=DEBUG B10_LOGGER_DBGLEVEL=50 ./init_logger_test.exe | \
+    cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+echo -n  "   - severity=WARN: "
+cat > $tempfile << .
+WARN  [bind10.log] LOG_BAD_STREAM bad log console output stream: warn
+ERROR [bind10.log] LOG_DUPLICATE_MESSAGE_ID duplicate message ID (error) in compiled code
+FATAL [bind10.log] LOG_NO_MESSAGE_ID line fatal: message definition line found without a message ID
+.
+B10_LOGGER_DESTINATION=stdout B10_LOGGER_SEVERITY=WARN ./init_logger_test.exe | \
+    cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+echo "2. Checking that B10_LOGGER_DESTINATION works"
+
+echo -n  "   - stdout: "
+cat > $tempfile << .
+FATAL [bind10.log] LOG_NO_MESSAGE_ID line fatal: message definition line found without a message ID
+.
+rm -f $destfile
+B10_LOGGER_SEVERITY=FATAL B10_LOGGER_DESTINATION=stdout ./init_logger_test.exe 1> $destfile
+cut -d' ' -f3- $destfile | diff $tempfile -
+passfail $?
+
+echo -n  "   - stderr: "
+rm -f $destfile
+B10_LOGGER_SEVERITY=FATAL B10_LOGGER_DESTINATION=stderr ./init_logger_test.exe 2> $destfile
+cut -d' ' -f3- $destfile | diff $tempfile -
+passfail $?
+
+echo -n  "   - file: "
+rm -f $destfile
+B10_LOGGER_SEVERITY=FATAL B10_LOGGER_DESTINATION=$destfile ./init_logger_test.exe
+cut -d' ' -f3- $destfile | diff $tempfile -
+passfail $?
+
+# Note: can't automatically test syslog output.
+
+if [ $failcount -eq 0 ]; then
+    echo "PASS: $testname"
+elif [ $failcount -eq 1 ]; then
+    echo "FAIL: $testname - 1 test failed"
+else
+    echo "FAIL: $testname - $failcount tests failed"
+fi
+
+# Tidy up.
+rm -f $tempfile $destfile
+
+exit $failcount
diff --git a/src/lib/log/tests/local_file_test.sh.in b/src/lib/log/tests/local_file_test.sh.in
new file mode 100755
index 0000000..9b898e6
--- /dev/null
+++ b/src/lib/log/tests/local_file_test.sh.in
@@ -0,0 +1,83 @@
+#!/bin/sh
+# 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.
+
+# Checks that a local message file can override the definitions in the message
+# dictionary.
+
+testname="Local message file test"
+echo $testname
+
+failcount=0
+localmes=@abs_builddir@/localdef_mes_$$
+tempfile=@abs_builddir@/run_time_init_test_tempfile_$$
+
+passfail() {
+    if [ $1 -eq 0 ]; then
+        echo " pass"
+    else
+        echo " FAIL"
+        failcount=`expr $failcount + $1`
+    fi
+}
+
+# Create the local message file for testing
+
+cat > $localmes << .
+% LOG_NOTHERE this message is not in the global dictionary
+% LOG_READ_ERROR replacement read error, parameters: '%1' and '%2'
+% LOG_READING_LOCAL_FILE replacement read local message file, parameter is '%1'
+.
+
+echo -n "1. Local message replacement:"
+cat > $tempfile << .
+WARN  [example.log] LOG_NO_SUCH_MESSAGE could not replace message text for 'LOG_NOTHERE': no such message
+FATAL [example] LOG_WRITE_ERROR error writing to test1: 42
+ERROR [example] LOG_READING_LOCAL_FILE replacement read local message file, parameter is 'dummy/file'
+WARN  [example] LOG_BAD_STREAM bad log console output stream: example
+WARN  [example.alpha] LOG_READ_ERROR replacement read error, parameters: 'a.txt' and 'dummy reason'
+FATAL [example.beta] LOG_BAD_SEVERITY unrecognized log severity: beta_fatal
+ERROR [example.beta] LOG_BAD_DESTINATION unrecognized log destination: beta_error
+WARN  [example.beta] LOG_BAD_STREAM bad log console output stream: beta_warn
+.
+./logger_example -c stdout -s warn $localmes | cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+echo -n "2. Report error if unable to read local message file:"
+cat > $tempfile << .
+ERROR [example.log] LOG_INPUT_OPEN_FAIL unable to open message file $localmes for input: No such file or directory
+FATAL [example] LOG_WRITE_ERROR error writing to test1: 42
+ERROR [example] LOG_READING_LOCAL_FILE reading local message file dummy/file
+WARN  [example] LOG_BAD_STREAM bad log console output stream: example
+WARN  [example.alpha] LOG_READ_ERROR error reading from message file a.txt: dummy reason
+FATAL [example.beta] LOG_BAD_SEVERITY unrecognized log severity: beta_fatal
+ERROR [example.beta] LOG_BAD_DESTINATION unrecognized log destination: beta_error
+WARN  [example.beta] LOG_BAD_STREAM bad log console output stream: beta_warn
+.
+rm -f $localmes
+./logger_example -c stdout -s warn $localmes | cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+if [ $failcount -eq 0 ]; then
+    echo "PASS: $testname"
+elif [ $failcount -eq 1 ]; then
+    echo "FAIL: $testname - 1 test failed"
+else
+    echo "FAIL: $testname - $failcount tests failed"
+fi
+
+# Tidy up.
+rm -f $tempfile
+
+exit $failcount
diff --git a/src/lib/log/tests/local_file_test.sh.win32 b/src/lib/log/tests/local_file_test.sh.win32
new file mode 100755
index 0000000..6ca52bd
--- /dev/null
+++ b/src/lib/log/tests/local_file_test.sh.win32
@@ -0,0 +1,83 @@
+#!/bin/sh
+# 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.
+
+# Checks that a local message file can override the definitions in the message
+# dictionary.
+
+testname="Local message file test"
+echo $testname
+
+failcount=0
+localmes=c:/cygwin/home/fdupont/bind10.trac826/src/lib/log/tests/localdef_mes_$$
+tempfile=c:/cygwin/home/fdupont/bind10.trac826/src/lib/log/tests/run_time_init_test_tempfile_$$
+
+passfail() {
+    if [ $1 -eq 0 ]; then
+        echo " pass"
+    else
+        echo " FAIL"
+        failcount=`expr $failcount + $1`
+    fi
+}
+
+# Create the local message file for testing
+
+cat > $localmes << .
+% LOG_NOTHERE this message is not in the global dictionary
+% LOG_READ_ERROR replacement read error, parameters: '%1' and '%2'
+% LOG_READING_LOCAL_FILE replacement read local message file, parameter is '%1'
+.
+
+echo -n "1. Local message replacement:"
+cat > $tempfile << .
+WARN  [example.log] LOG_NO_SUCH_MESSAGE could not replace message text for 'LOG_NOTHERE': no such message
+FATAL [example] LOG_WRITE_ERROR error writing to test1: 42
+ERROR [example] LOG_READING_LOCAL_FILE replacement read local message file, parameter is 'dummy/file'
+WARN  [example] LOG_BAD_STREAM bad log console output stream: example
+WARN  [example.alpha] LOG_READ_ERROR replacement read error, parameters: 'a.txt' and 'dummy reason'
+FATAL [example.beta] LOG_BAD_SEVERITY unrecognized log severity: beta_fatal
+ERROR [example.beta] LOG_BAD_DESTINATION unrecognized log destination: beta_error
+WARN  [example.beta] LOG_BAD_STREAM bad log console output stream: beta_warn
+.
+./logger_example.exe -c stdout -s warn $localmes | cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+echo -n "2. Report error if unable to read local message file:"
+cat > $tempfile << .
+ERROR [example.log] LOG_INPUT_OPEN_FAIL unable to open message file $localmes for input: No such file or directory
+FATAL [example] LOG_WRITE_ERROR error writing to test1: 42
+ERROR [example] LOG_READING_LOCAL_FILE reading local message file dummy/file
+WARN  [example] LOG_BAD_STREAM bad log console output stream: example
+WARN  [example.alpha] LOG_READ_ERROR error reading from message file a.txt: dummy reason
+FATAL [example.beta] LOG_BAD_SEVERITY unrecognized log severity: beta_fatal
+ERROR [example.beta] LOG_BAD_DESTINATION unrecognized log destination: beta_error
+WARN  [example.beta] LOG_BAD_STREAM bad log console output stream: beta_warn
+.
+rm -f $localmes
+./logger_example.exe -c stdout -s warn $localmes | cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+if [ $failcount -eq 0 ]; then
+    echo "PASS: $testname"
+elif [ $failcount -eq 1 ]; then
+    echo "FAIL: $testname - 1 test failed"
+else
+    echo "FAIL: $testname - $failcount tests failed"
+fi
+
+# Tidy up.
+rm -f $tempfile
+
+exit $failcount
diff --git a/src/lib/log/tests/log_formatter_unittest.cc b/src/lib/log/tests/log_formatter_unittest.cc
new file mode 100644
index 0000000..b91665d
--- /dev/null
+++ b/src/lib/log/tests/log_formatter_unittest.cc
@@ -0,0 +1,126 @@
+// 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 <gtest/gtest.h>
+#include <log/log_formatter.h>
+#include <log/logger_level.h>
+
+#include <vector>
+#include <string>
+
+using namespace std;
+
+namespace {
+
+class FormatterTest : public ::testing::Test {
+protected:
+    typedef pair<isc::log::Severity, string> Output;
+    typedef isc::log::Formatter<FormatterTest> Formatter;
+    vector<Output> outputs;
+public:
+    void output(const isc::log::Severity& prefix, const string& message) {
+        outputs.push_back(Output(prefix, message));
+    }
+    // Just shortcut for new string
+    string* s(const char* text) {
+        return (new string(text));
+    }
+};
+
+// Create an inactive formatter and check it doesn't produce any output
+TEST_F(FormatterTest, inactive) {
+    Formatter();
+    EXPECT_EQ(0, outputs.size());
+}
+
+// Create an active formatter and check it produces output. Does no arg
+// substitution yet
+TEST_F(FormatterTest, active) {
+    Formatter(isc::log::INFO, s("Text of message"), this);
+    ASSERT_EQ(1, outputs.size());
+    EXPECT_EQ(isc::log::INFO, outputs[0].first);
+    EXPECT_EQ("Text of message", outputs[0].second);
+}
+
+// No output even when we have an arg on the inactive formatter
+TEST_F(FormatterTest, inactiveArg) {
+    Formatter().arg("Hello");
+    EXPECT_EQ(0, outputs.size());
+}
+
+// Create an active formatter and replace a placeholder with string
+TEST_F(FormatterTest, stringArg) {
+    {
+        SCOPED_TRACE("C++ string");
+        Formatter(isc::log::INFO, s("Hello %1"), this).arg(string("World"));
+        ASSERT_EQ(1, outputs.size());
+        EXPECT_EQ(isc::log::INFO, outputs[0].first);
+        EXPECT_EQ("Hello World", outputs[0].second);
+    }
+    {
+        SCOPED_TRACE("C++ string");
+        Formatter(isc::log::INFO, s("Hello %1"), this).arg(string("Internet"));
+        ASSERT_EQ(2, outputs.size());
+        EXPECT_EQ(isc::log::INFO, outputs[1].first);
+        EXPECT_EQ("Hello Internet", outputs[1].second);
+    }
+}
+
+// Can convert to string
+TEST_F(FormatterTest, intArg) {
+    Formatter(isc::log::INFO, s("The answer is %1"), this).arg(42);
+    ASSERT_EQ(1, outputs.size());
+    EXPECT_EQ(isc::log::INFO, outputs[0].first);
+    EXPECT_EQ("The answer is 42", outputs[0].second);
+}
+
+// Can use multiple arguments at different places
+TEST_F(FormatterTest, multiArg) {
+    Formatter(isc::log::INFO, s("The %2 are %1"), this).arg("switched").
+        arg("arguments");
+    ASSERT_EQ(1, outputs.size());
+    EXPECT_EQ(isc::log::INFO, outputs[0].first);
+    EXPECT_EQ("The arguments are switched", outputs[0].second);
+}
+
+// Can survive and complains if placeholder is missing
+TEST_F(FormatterTest, missingPlace) {
+    EXPECT_NO_THROW(Formatter(isc::log::INFO, s("Missing the first %2"), this).
+                    arg("missing").arg("argument"));
+    ASSERT_EQ(1, outputs.size());
+    EXPECT_EQ(isc::log::INFO, outputs[0].first);
+    EXPECT_EQ("Missing the first argument "
+              "@@Missing placeholder %1 for 'missing'@@", outputs[0].second);
+}
+
+// Can replace multiple placeholders
+TEST_F(FormatterTest, multiPlaceholder) {
+    Formatter(isc::log::INFO, s("The %1 is the %1"), this).
+        arg("first rule of tautology club");
+    ASSERT_EQ(1, outputs.size());
+    EXPECT_EQ(isc::log::INFO, outputs[0].first);
+    EXPECT_EQ("The first rule of tautology club is "
+              "the first rule of tautology club", outputs[0].second);
+}
+
+// Test we can cope with replacement containing the placeholder
+TEST_F(FormatterTest, noRecurse) {
+    // If we recurse, this will probably eat all the memory and crash
+    Formatter(isc::log::INFO, s("%1"), this).arg("%1 %1");
+    ASSERT_EQ(1, outputs.size());
+    EXPECT_EQ(isc::log::INFO, outputs[0].first);
+    EXPECT_EQ("%1 %1", outputs[0].second);
+}
+
+}
diff --git a/src/lib/log/tests/logger_example.cc b/src/lib/log/tests/logger_example.cc
new file mode 100644
index 0000000..28b4e68
--- /dev/null
+++ b/src/lib/log/tests/logger_example.cc
@@ -0,0 +1,309 @@
+// 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.
+
+/// \brief Example Program
+///
+/// Simple example program showing how to use the logger.  The various
+/// command-line options let most aspects of the logger be exercised, so
+/// making this a useful tool for testing.
+///
+/// See the usage() method for details of use.
+
+#include <stdlib.h>
+#ifdef _WIN32
+#include <getopt.h>
+#else
+#include <unistd.h>
+#endif
+#include <string.h>
+
+#include <boost/lexical_cast.hpp>
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <util/strutil.h>
+
+#include <log/logger.h>
+#include <log/logger_level.h>
+#include <log/logger_manager.h>
+#include <log/logger_name.h>
+#include <log/logger_specification.h>
+#include <log/macros.h>
+
+// Include a set of message definitions.
+#include <log/log_messages.h>
+
+using namespace isc::log;
+using namespace std;
+
+
+// Print usage information
+
+void usage() {
+    cout <<
+"logger_support_test [-h | [logger_spec] [[logger_spec]...]]\n"
+"\n"
+"     -h            Print this message and exit\n"
+"\n"
+"The rest of the command line comprises the set of logger specifications.\n"
+"Each specification is of the form:\n"
+"\n"
+"    -l logger  [-s severity] [-d dbglevel] output_spec] [[output_spec] ...\n"
+"\n"
+"where:\n"
+"\n"
+"    -l logger      Give the name of the logger to which the following\n"
+"                   output specifications will apply.\n"
+"\n"
+"Each logger is followed by the indication of the serverity it is logging\n"
+"and, if applicable, its debug level:\n"
+"\n"
+"   -d dbglevel     Debug level.  Only interpreted if the severity is 'debug'\n"
+"                   this is a number between 0 and 99.\n"
+"   -s severity     Set the severity of messages output.  'severity' is one\n"
+"                   of 'debug', 'info', 'warn', 'error', 'fatal', the default\n"
+"                   being 'info'.\n"
+"\n"
+"The output specifications - there may be more than one per logger - detail\n"
+"the output streams attached to the logger.  These are of the form:\n"
+"\n"
+"   -c stream | -f file [-m maxver] [-z maxsize] | -y facility\n"
+"\n"
+"These are:\n"
+"\n"
+"   -c stream       Send output to the console.  'stream' is one of 'stdout'\n"
+"                   of 'stderr'.\n"
+"   -f file         Send output to specified file, appending to existing file\n"
+"                   if one exists.\n"
+"   -y facility     Send output to the syslog file with the given facility\n"
+"                   name (e.g. local1, cron etc.)\n"
+"\n"
+"The following can be specified for the file logger:\n"
+"\n"
+"   -m maxver       If file rolling is selected (by the maximum file size being\n"
+"                   non-zero), the maximum number of versions to keep (defaults\n"
+"                   to 0)\n"
+"   -z maxsize      Maximum size of the file before the file is closed and a\n"
+"                   new one opened.  The default of 0 means no maximum size.\n"
+"\n"
+"If none of -c, -f or -y is given, by default, output is sent to stdout.  If no\n"
+"logger is specified, the default is the program's root logger ('example').\n";
+
+}
+
+
+// The program sets the attributes on the root logger and logs a set of
+// messages.  Looking at the output determines whether the program worked.
+
+int main(int argc, char** argv) {
+    const string ROOT_NAME = "example";
+
+    bool                sw_found = false;   // Set true if switch found
+    bool                c_found = false;    // Set true if "-c" found
+    bool                f_found = false;    // Set true if "-f" found
+    bool                y_found = false;    // Set true if "-y" found
+    int                 option;             // For getopt() processing
+    OutputOption        def_opt;            // Default output option - used
+                                            //    for initialization
+    LoggerSpecification cur_spec(ROOT_NAME);// Current specification
+    OutputOption        cur_opt;            // Current output option
+    vector<LoggerSpecification> loggers;    // Set of logger specifications
+    vector<OutputOption>        options;    // Output options for logger
+    std::string                 severity;   // Severity set for logger
+
+    // Initialize logging system - set the root logger name.
+    LoggerManager manager;
+    manager.init(ROOT_NAME);
+
+    // In the parsing loop that follows, the construction of the logging
+    // specification is always "one behind".  In other words, the parsing of
+    // command-line options updates thge current logging specification/output
+    // options.  When the flag indicating a new logger or output specification
+    // is encountered, the previous one is added to the list.
+    //
+    // One complication is that there is deemed to be a default active when
+    // the parsing starts (console output for the BIND 10 root logger).  This
+    // is included in the logging specifications UNLESS the first switch on
+    // the command line is a "-l" flag starting a new logger.  To track this,
+    // the "sw_found" flag is set when a switch is completey processed. The
+    // processing of "-l" will only add information for a previous logger to
+    // the list if this flag is set.
+    while ((option = getopt(argc, argv, "hc:d:f:l:m:s:y:z:")) != -1) {
+        switch (option) {
+        case 'c':   // Console output
+            // New output spec.  If one was currently active, add it to the
+            // list and reset the current output option to the defaults.
+            if (c_found || f_found || y_found) {
+                cur_spec.addOutputOption(cur_opt);
+                cur_opt = def_opt;
+                c_found = f_found = y_found = false;
+            }
+
+            // Set the output option for this switch.
+            c_found = true;
+            cur_opt.destination = OutputOption::DEST_CONSOLE;
+            if (strcmp(optarg, "stdout") == 0) {
+                cur_opt.stream = OutputOption::STR_STDOUT;
+
+            } else if (strcmp(optarg, "stderr") == 0) {
+                cur_opt.stream = OutputOption::STR_STDERR;
+
+            } else {
+                cerr << "Unrecognised console option: " << optarg << "\n";
+                return (1);
+            }
+            break;
+
+        case 'd':   // Debug level
+            cur_spec.setDbglevel(boost::lexical_cast<int>(optarg));
+            break;
+
+        case 'f':   // File output specification
+            // New output spec.  If one was currently active, add it to the
+            // list and reset the current output option to the defaults.
+            if (c_found || f_found || y_found) {
+                cur_spec.addOutputOption(cur_opt);
+                cur_opt = def_opt;
+                c_found = f_found = y_found = false;
+            }
+
+            // Set the output option for this switch.
+            f_found = true;
+            cur_opt.destination = OutputOption::DEST_FILE;
+            cur_opt.filename = optarg;
+            break;
+
+        case 'h':   // Help
+            usage();
+            return (0);
+
+        case 'l':   // Logger
+            // If a current specification is active, add the last output option
+            // to it, add it to the list and reset.  A specification is active
+            // if at least one switch has been previously found.
+            if (sw_found) {
+                cur_spec.addOutputOption(cur_opt);
+                loggers.push_back(cur_spec);
+                cur_spec.reset();
+            }
+
+            // Set the logger name
+            cur_spec.setName(std::string(optarg));
+
+            // Reset the output option to the default.
+            cur_opt = def_opt;
+
+            // Indicate nothing is found to prevent the console option (the
+            // default output option) being added to the output list if an
+            // output option is found.
+            c_found = f_found = y_found = false;
+            break;
+
+        case 'm':   // Maximum file version
+            if (!f_found) {
+                std::cerr << "Attempt to set maximum version (-m) "
+                             "outside of file output specification\n";
+                return (1);
+            }
+            try {
+                cur_opt.maxsize = boost::lexical_cast<unsigned int>(optarg);
+            } catch (boost::bad_lexical_cast&) {
+                std::cerr << "Maximum version (-m) argument must be a positive "
+                             "integer\n";
+                return (1);
+            }
+            break;
+
+        case 's':   // Severity
+            severity = optarg;
+            isc::util::str::uppercase(severity);
+            cur_spec.setSeverity(getSeverity(severity));
+            break;
+
+        case 'y':   // Syslog output
+            // New output spec.  If one was currently active, add it to the
+            // list and reset the current output option to the defaults.
+            if (c_found || f_found || y_found) {
+                cur_spec.addOutputOption(cur_opt);
+                cur_opt = def_opt;
+                c_found = f_found = y_found = false;
+            }
+            y_found = true;
+            cur_opt.destination = OutputOption::DEST_SYSLOG;
+            cur_opt.facility = optarg;
+            break;
+
+        case 'z':   // Maximum size
+            if (! f_found) {
+                std::cerr << "Attempt to set file size (-z) "
+                             "outside of file output specification\n";
+                return (1);
+            }
+            try {
+                cur_opt.maxsize = boost::lexical_cast<size_t>(optarg);
+            } catch (boost::bad_lexical_cast&) {
+                std::cerr << "File size (-z) argument must be a positive "
+                             "integer\n";
+                return (1);
+            }
+            break;
+
+
+        default:
+            std::cerr << "Unrecognised option: " <<
+                static_cast<char>(option) << "\n";
+            return (1);
+        }
+
+        // Have found at least one command-line switch, so note the fact.
+        sw_found = true;
+    }
+
+    // Add the current (unfinished specification) to the list.
+    cur_spec.addOutputOption(cur_opt);
+    loggers.push_back(cur_spec);
+
+    // Set the logging options.
+    manager.process(loggers.begin(), loggers.end());
+
+    // Set the local file
+    if (optind < argc) {
+        LoggerManager::readLocalMessageFile(argv[optind]);
+    }
+
+    // Log a few messages to different loggers.
+    isc::log::Logger logger_ex(ROOT_NAME);
+    isc::log::Logger logger_alpha("alpha");
+    isc::log::Logger logger_beta("beta");
+
+    LOG_FATAL(logger_ex, LOG_WRITE_ERROR).arg("test1").arg("42");
+    LOG_ERROR(logger_ex, LOG_READING_LOCAL_FILE).arg("dummy/file");
+    LOG_WARN(logger_ex, LOG_BAD_STREAM).arg("example");
+    LOG_WARN(logger_alpha, LOG_READ_ERROR).arg("a.txt").arg("dummy reason");
+    LOG_INFO(logger_alpha, LOG_INPUT_OPEN_FAIL).arg("example.msg").arg("dummy reason");
+    LOG_DEBUG(logger_ex, 0, LOG_READING_LOCAL_FILE).arg("example/0");
+    LOG_DEBUG(logger_ex, 24, LOG_READING_LOCAL_FILE).arg("example/24");
+    LOG_DEBUG(logger_ex, 25, LOG_READING_LOCAL_FILE).arg("example/25");
+    LOG_DEBUG(logger_ex, 26, LOG_READING_LOCAL_FILE).arg("example/26");
+    LOG_FATAL(logger_beta, LOG_BAD_SEVERITY).arg("beta_fatal");
+    LOG_ERROR(logger_beta, LOG_BAD_DESTINATION).arg("beta_error");
+    LOG_WARN(logger_beta, LOG_BAD_STREAM).arg("beta_warn");
+    LOG_INFO(logger_beta, LOG_READ_ERROR).arg("beta").arg("info");
+    LOG_DEBUG(logger_beta, 25, LOG_BAD_SEVERITY).arg("beta/25");
+    LOG_DEBUG(logger_beta, 26, LOG_BAD_SEVERITY).arg("beta/26");
+
+    return (0);
+}
diff --git a/src/lib/log/tests/logger_impl_log4cxx_unittest.cc b/src/lib/log/tests/logger_impl_log4cxx_unittest.cc
deleted file mode 100644
index cab2678..0000000
--- a/src/lib/log/tests/logger_impl_log4cxx_unittest.cc
+++ /dev/null
@@ -1,91 +0,0 @@
-// 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 <string>
-
-#include <gtest/gtest.h>
-
-#include <log/root_logger_name.h>
-#include <log/logger.h>
-#include <log/logger_impl.h>
-#include <log/messagedef.h>
-
-using namespace isc;
-using namespace isc::log;
-using namespace std;
-
-/// \brief Log4cxx Implementation Tests
-///
-/// Some tests of methods that are not directly tested by the logger unit tests
-/// (when the logger is configured to use log4cxx)
-
-namespace isc {
-namespace log {
-
-/// \brief Test Logger
-///
-/// This logger is a subclass of the logger implementation class under test, but
-/// makes protected methods public (for testing)
-
-class TestLoggerImpl : public LoggerImpl {
-public:
-    /// \brief constructor
-    TestLoggerImpl(const string& name) : LoggerImpl(name, true)
-    {}
-
-
-    /// \brief Conversion Between log4cxx Number and BIND-10 Severity
-    Severity convertLevel(int value) {
-        return (LoggerImpl::convertLevel(value));
-    }
-};
-
-} // namespace log
-} // namespace isc
-
-
-class LoggerImplTest : public ::testing::Test {
-protected:
-    LoggerImplTest()
-    {
-    }
-};
-
-// Test the number to severity conversion function
-
-TEST_F(LoggerImplTest, ConvertLevel) {
-
-    // Create a logger
-    RootLoggerName::setName("test3");
-    TestLoggerImpl logger("alpha");
-
-    // Basic 1:1
-    EXPECT_EQ(isc::log::DEBUG, logger.convertLevel(log4cxx::Level::DEBUG_INT));
-    EXPECT_EQ(isc::log::INFO, logger.convertLevel(log4cxx::Level::INFO_INT));
-    EXPECT_EQ(isc::log::WARN, logger.convertLevel(log4cxx::Level::WARN_INT));
-    EXPECT_EQ(isc::log::WARN, logger.convertLevel(log4cxx::Level::WARN_INT));
-    EXPECT_EQ(isc::log::ERROR, logger.convertLevel(log4cxx::Level::ERROR_INT));
-    EXPECT_EQ(isc::log::FATAL, logger.convertLevel(log4cxx::Level::FATAL_INT));
-    EXPECT_EQ(isc::log::FATAL, logger.convertLevel(log4cxx::Level::FATAL_INT));
-    EXPECT_EQ(isc::log::NONE, logger.convertLevel(log4cxx::Level::OFF_INT));
-
-    // Now some debug levels
-    EXPECT_EQ(isc::log::DEBUG,
-        logger.convertLevel(log4cxx::Level::DEBUG_INT - 1));
-    EXPECT_EQ(isc::log::DEBUG,
-        logger.convertLevel(log4cxx::Level::DEBUG_INT - MAX_DEBUG_LEVEL));
-    EXPECT_EQ(isc::log::DEBUG,
-        logger.convertLevel(log4cxx::Level::DEBUG_INT - 2 * MAX_DEBUG_LEVEL));
-}
diff --git a/src/lib/log/tests/logger_level_impl_unittest.cc b/src/lib/log/tests/logger_level_impl_unittest.cc
new file mode 100644
index 0000000..8660f2e
--- /dev/null
+++ b/src/lib/log/tests/logger_level_impl_unittest.cc
@@ -0,0 +1,178 @@
+// 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 <string>
+
+#include <gtest/gtest.h>
+#include <boost/static_assert.hpp>
+#include <boost/lexical_cast.hpp>
+
+#ifdef UNICODE
+#undef UNICODE
+#endif
+
+#include <log/logger_level_impl.h>
+#include <log/logger_support.h>
+#include <log4cplus/logger.h>
+
+using namespace isc::log;
+using namespace std;
+
+class LoggerLevelImplTest : public ::testing::Test {
+protected:
+    LoggerLevelImplTest() {
+        // Ensure logging set to default for unit tests
+        resetUnitTestRootLogger();
+    }
+
+    ~LoggerLevelImplTest()
+    {}
+};
+
+
+// Checks that the log4cplus and BIND 10 levels convert correctly
+TEST_F(LoggerLevelImplTest, DefaultConversionFromBind) {
+    log4cplus::LogLevel fatal =
+        LoggerLevelImpl::convertFromBindLevel(Level(FATAL));
+    EXPECT_EQ(log4cplus::FATAL_LOG_LEVEL, fatal);
+
+    log4cplus::LogLevel error =
+        LoggerLevelImpl::convertFromBindLevel(Level(ERROR));
+    EXPECT_EQ(log4cplus::ERROR_LOG_LEVEL, error);
+
+    log4cplus::LogLevel warn =
+        LoggerLevelImpl::convertFromBindLevel(Level(WARN));
+    EXPECT_EQ(log4cplus::WARN_LOG_LEVEL, warn);
+
+    log4cplus::LogLevel info =
+        LoggerLevelImpl::convertFromBindLevel(Level(INFO));
+    EXPECT_EQ(log4cplus::INFO_LOG_LEVEL, info);
+
+    log4cplus::LogLevel debug =
+        LoggerLevelImpl::convertFromBindLevel(Level(DEBUG));
+    EXPECT_EQ(log4cplus::DEBUG_LOG_LEVEL, debug);
+}
+
+// Checks that the debug severity and level converts correctly
+TEST_F(LoggerLevelImplTest, DebugConversionFromBind) {
+    log4cplus::LogLevel debug0 =
+        LoggerLevelImpl::convertFromBindLevel(Level(DEBUG, 0));
+    EXPECT_EQ(log4cplus::DEBUG_LOG_LEVEL - 0, debug0);
+
+    log4cplus::LogLevel debug1 =
+        LoggerLevelImpl::convertFromBindLevel(Level(DEBUG, 1));
+    EXPECT_EQ(log4cplus::DEBUG_LOG_LEVEL - 1, debug1);
+
+    log4cplus::LogLevel debug99 =
+        LoggerLevelImpl::convertFromBindLevel(Level(DEBUG, 99));
+    EXPECT_EQ(log4cplus::DEBUG_LOG_LEVEL - 99, debug99);
+
+    // Out of range should be coerced to the nearest boundary
+    log4cplus::LogLevel debug_1 =
+        LoggerLevelImpl::convertFromBindLevel(Level(DEBUG, MIN_DEBUG_LEVEL - 1));
+    EXPECT_EQ(log4cplus::DEBUG_LOG_LEVEL, debug_1);
+
+    log4cplus::LogLevel debug100 =
+        LoggerLevelImpl::convertFromBindLevel(Level(DEBUG, MAX_DEBUG_LEVEL + 1));
+    EXPECT_EQ(log4cplus::DEBUG_LOG_LEVEL - MAX_DEBUG_LEVEL, debug100);
+}
+
+// Do the checks the other way
+static void
+test_convert_to(const char* trace, isc::log::Severity severity, int dbglevel,
+                log4cplus::LogLevel level)
+{
+    SCOPED_TRACE(trace);
+    Level test = LoggerLevelImpl::convertToBindLevel(level);
+    EXPECT_EQ(severity, test.severity);
+    EXPECT_EQ(dbglevel, test.dbglevel);
+}
+
+TEST_F(LoggerLevelImplTest, ConversionToBind) {
+    test_convert_to("FATAL", FATAL, MIN_DEBUG_LEVEL, log4cplus::FATAL_LOG_LEVEL);
+    test_convert_to("ERROR", ERROR, MIN_DEBUG_LEVEL, log4cplus::ERROR_LOG_LEVEL);
+    test_convert_to("WARN",  WARN , MIN_DEBUG_LEVEL, log4cplus::WARN_LOG_LEVEL);
+    test_convert_to("INFO",  INFO , MIN_DEBUG_LEVEL, log4cplus::INFO_LOG_LEVEL);
+    test_convert_to("DEBUG",  DEBUG, MIN_DEBUG_LEVEL, log4cplus::DEBUG_LOG_LEVEL);
+
+    test_convert_to("DEBUG0",  DEBUG, MIN_DEBUG_LEVEL + 0,
+            (log4cplus::DEBUG_LOG_LEVEL));
+    test_convert_to("DEBUG1",  DEBUG, MIN_DEBUG_LEVEL + 1,
+            (log4cplus::DEBUG_LOG_LEVEL - 1));
+    test_convert_to("DEBUG2",  DEBUG, MIN_DEBUG_LEVEL + 2,
+            (log4cplus::DEBUG_LOG_LEVEL - 2));
+    test_convert_to("DEBUG99",  DEBUG, MIN_DEBUG_LEVEL + 99,
+            (log4cplus::DEBUG_LOG_LEVEL - 99));
+
+    // ... and some invalid valid values
+    test_convert_to("DEBUG-1",  INFO, MIN_DEBUG_LEVEL,
+            (log4cplus::DEBUG_LOG_LEVEL + 1));
+    BOOST_STATIC_ASSERT(MAX_DEBUG_LEVEL == 99);
+    test_convert_to("DEBUG+100",  DEFAULT, 0,
+            (log4cplus::DEBUG_LOG_LEVEL - MAX_DEBUG_LEVEL - 1));
+}
+
+// Check that we can convert from a string to the new log4cplus levels
+TEST_F(LoggerLevelImplTest, FromString) {
+
+    // Test all valid values
+    for (int i = MIN_DEBUG_LEVEL; i <= MAX_DEBUG_LEVEL; ++i) {
+        std::string token = string("DEBUG") + boost::lexical_cast<std::string>(i);
+        EXPECT_EQ(log4cplus::DEBUG_LOG_LEVEL - i,
+                  LoggerLevelImpl::logLevelFromString(token));
+    }
+
+    // ... in lowercase too
+    for (int i = MIN_DEBUG_LEVEL; i <= MAX_DEBUG_LEVEL; ++i) {
+        std::string token = string("debug") + boost::lexical_cast<std::string>(i);
+        EXPECT_EQ(log4cplus::DEBUG_LOG_LEVEL - i,
+                  LoggerLevelImpl::logLevelFromString(token));
+    }
+
+    // A few below the minimum
+    for (int i = MIN_DEBUG_LEVEL - 5; i < MIN_DEBUG_LEVEL; ++i) {
+        std::string token = string("DEBUG") + boost::lexical_cast<std::string>(i);
+        EXPECT_EQ(log4cplus::DEBUG_LOG_LEVEL, LoggerLevelImpl::logLevelFromString(token));
+    }
+
+    // ... and above the maximum
+    for (int i = MAX_DEBUG_LEVEL + 1; i < MAX_DEBUG_LEVEL + 5; ++i) {
+        std::string token = string("DEBUG") + boost::lexical_cast<std::string>(i);
+        EXPECT_EQ(log4cplus::DEBUG_LOG_LEVEL - MAX_DEBUG_LEVEL,
+                  LoggerLevelImpl::logLevelFromString(token));
+    }
+
+    // Invalid strings.
+    EXPECT_EQ(log4cplus::NOT_SET_LOG_LEVEL,
+              LoggerLevelImpl::logLevelFromString("DEBU"));
+    EXPECT_EQ(log4cplus::NOT_SET_LOG_LEVEL,
+              LoggerLevelImpl::logLevelFromString("unrecognised"));
+}
+
+// ... and check the conversion back again.  All levels should convert to "DEBUG".
+TEST_F(LoggerLevelImplTest, ToString) {
+
+    for (int i = MIN_DEBUG_LEVEL; i <= MAX_DEBUG_LEVEL; ++i) {
+        EXPECT_EQ(std::string("DEBUG"),
+                  LoggerLevelImpl::logLevelToString(log4cplus::DEBUG_LOG_LEVEL - i));
+    }
+
+    // ... and that out of range stuff returns an empty string.
+    EXPECT_EQ(std::string(),
+              LoggerLevelImpl::logLevelToString(log4cplus::DEBUG_LOG_LEVEL + 1));
+    EXPECT_EQ(std::string(),
+              LoggerLevelImpl::logLevelToString(
+                               log4cplus::DEBUG_LOG_LEVEL - MAX_DEBUG_LEVEL - 100));
+}
diff --git a/src/lib/log/tests/logger_level_unittest.cc b/src/lib/log/tests/logger_level_unittest.cc
new file mode 100644
index 0000000..641a6cc
--- /dev/null
+++ b/src/lib/log/tests/logger_level_unittest.cc
@@ -0,0 +1,84 @@
+// 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 <string>
+
+#include <gtest/gtest.h>
+
+#include <log/logger.h>
+#include <log/logger_manager.h>
+#include <log/log_messages.h>
+#include <log/logger_support.h>
+
+using namespace isc;
+using namespace isc::log;
+using namespace std;
+
+class LoggerLevelTest : public ::testing::Test {
+protected:
+    LoggerLevelTest() {
+        // Logger initialization is done in main().  As logging tests may
+        // alter the default logging output, it is reset here.
+        resetUnitTestRootLogger();
+    }
+    ~LoggerLevelTest() {
+        LoggerManager::reset();
+    }
+};
+
+
+// Checks that the logger is named correctly.
+
+TEST_F(LoggerLevelTest, Creation) {
+
+    // Default
+    isc::log::Level level1;
+    EXPECT_EQ(isc::log::DEFAULT, level1.severity);
+    EXPECT_EQ(isc::log::MIN_DEBUG_LEVEL, level1.dbglevel);
+
+    // Single argument constructor.
+    isc::log::Level level2(isc::log::FATAL);
+    EXPECT_EQ(isc::log::FATAL, level2.severity);
+    EXPECT_EQ(isc::log::MIN_DEBUG_LEVEL, level2.dbglevel);
+
+    // Two-argument constructor
+    isc::log::Level level3(isc::log::DEBUG, 42);
+    EXPECT_EQ(isc::log::DEBUG, level3.severity);
+    EXPECT_EQ(42, level3.dbglevel);
+}
+
+TEST_F(LoggerLevelTest, getSeverity) {
+    EXPECT_EQ(DEBUG, getSeverity("DEBUG"));
+    EXPECT_EQ(DEBUG, getSeverity("debug"));
+    EXPECT_EQ(DEBUG, getSeverity("DeBuG"));
+    EXPECT_EQ(INFO, getSeverity("INFO"));
+    EXPECT_EQ(INFO, getSeverity("info"));
+    EXPECT_EQ(INFO, getSeverity("iNfO"));
+    EXPECT_EQ(WARN, getSeverity("WARN"));
+    EXPECT_EQ(WARN, getSeverity("warn"));
+    EXPECT_EQ(WARN, getSeverity("wARn"));
+    EXPECT_EQ(ERROR, getSeverity("ERROR"));
+    EXPECT_EQ(ERROR, getSeverity("error"));
+    EXPECT_EQ(ERROR, getSeverity("ERRoR"));
+    EXPECT_EQ(FATAL, getSeverity("FATAL"));
+    EXPECT_EQ(FATAL, getSeverity("fatal"));
+    EXPECT_EQ(FATAL, getSeverity("FAtaL"));
+
+    // bad values should default to stdout
+    EXPECT_EQ(INFO, getSeverity("some bad value"));
+    EXPECT_EQ(INFO, getSeverity(""));
+
+    LoggerManager::reset();
+}
diff --git a/src/lib/log/tests/logger_manager_unittest.cc b/src/lib/log/tests/logger_manager_unittest.cc
new file mode 100644
index 0000000..1d5a342
--- /dev/null
+++ b/src/lib/log/tests/logger_manager_unittest.cc
@@ -0,0 +1,329 @@
+// 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 <stdio.h>
+#ifdef _WIN32
+#define unlink _unlink
+#else
+#include <unistd.h>
+#endif
+
+#include <fstream>
+#include <iostream>
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <boost/scoped_array.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <exceptions/exceptions.h>
+
+#include <log/macros.h>
+#include <log/log_messages.h>
+#include <log/logger.h>
+#include <log/logger_level.h>
+#include <log/logger_manager.h>
+#include <log/logger_specification.h>
+#include <log/output_option.h>
+
+#include "tempdir.h"
+
+using namespace isc;
+using namespace isc::log;
+using namespace std;
+
+/// \brief LoggerManager Test
+class LoggerManagerTest : public ::testing::Test {
+public:
+    LoggerManagerTest() {
+        // Initialization of logging is done in main()
+    }
+
+    ~LoggerManagerTest() {
+        LoggerManager::reset();
+    }
+};
+
+
+
+// Convenience class to create the specification for the logger "filelogger",
+// which, as the name suggests, logs to a file.  It remembers the file name and
+// deletes the file when instance of the class is destroyed.
+class SpecificationForFileLogger {
+public:
+
+    // Constructor - allocate file and create the specification object
+    SpecificationForFileLogger() : spec_(), name_(""), logname_("filelogger") {
+
+        // Set the output to a temporary file.
+        OutputOption option;
+        option.destination = OutputOption::DEST_FILE;
+        option.filename = name_ = createTempFilename();
+
+        // Set target output to the file logger.  The defauls indicate
+        // INFO severity.
+        spec_.setName(logname_);
+        spec_.addOutputOption(option);
+    }
+
+    // Destructor, remove the file.  This is only a test, so ignore failures
+    ~SpecificationForFileLogger() {
+        if (! name_.empty()) {
+            (void) unlink(name_.c_str());
+        }
+    }
+
+    // Return reference to the logging specification for this loggger
+    LoggerSpecification& getSpecification() {
+        return spec_;
+    }
+
+    // Return name of the logger
+    string getLoggerName() const {
+        return logname_;
+    }
+
+    // Return name of the file
+    string getFileName() const {
+        return name_;
+    }
+
+
+    // Create temporary filename
+    //
+    // The compiler warns against tmpnam() and suggests mkstemp instead.
+    // Unfortunately, this creates the filename and opens it.  So we need to
+    // close and delete the file before returning the name.  Also, the name
+    // is based on the template supplied and the name of the temporary
+    // directory may vary between systems.  So translate TMPDIR and if that
+    // does not exist, use /tmp.
+    //
+    // \return Temporary file name
+    std::string createTempFilename() {
+        string filename = TEMP_DIR + "/bind10_logger_manager_test_XXXXXX";
+
+        // Copy into writeable storage for the call to mkstemp
+        boost::scoped_array<char> tname(new char[filename.size() + 1]);
+        strcpy(tname.get(), filename.c_str());
+
+        // Create file, close and delete it, and store the name for later.
+        // There is still a race condition here, albeit a small one.
+#ifdef _WIN32
+	_mktemp_s(tname.get(), filename.size());
+#else
+        int filenum = mkstemp(tname.get());
+        if (filenum == -1) {
+            isc_throw(Exception, "Unable to obtain unique filename");
+        }
+        close(filenum);
+
+        return (string(tname.get()));
+    }
+#endif
+
+private:
+    LoggerSpecification     spec_;      // Specification for this file logger
+    string                  name_;      // Name of the output file
+    string                  logname_;   // Name of this logger
+};
+
+
+// Convenience function to read an output log file and check that each line
+// contains the expected message ID
+//
+// \param filename Name of the file to check
+// \param start Iterator pointing to first expected message ID
+// \param finish Iterator pointing to last expected message ID
+template <typename T>
+void checkFileContents(const std::string& filename, T start, T finish) {
+
+    // Access the file for input
+    ifstream infile(filename.c_str());
+    if (! infile.good()) {
+        FAIL() << "Unable to open the logging file " << filename;
+    }
+
+    // Iterate round the expected message IDs and check that they appear in
+    // the string.
+    string line;    // Line read from the file
+
+    T i = start;    // Iterator
+    getline(infile, line);
+    int lineno = 1;
+
+    while ((i != finish) && (infile.good())) {
+
+        // Check that the message ID appears in the line.
+        EXPECT_TRUE(line.find(string(*i)) != string::npos)
+            << "Expected to find " << string(*i) << " on line " << lineno
+            << " of logging file " << filename;
+
+        // Go for the next line
+        ++i;
+        getline(infile, line);
+        ++lineno;
+    }
+
+    // Why did the loop end?
+    EXPECT_TRUE(i == finish) << "Did not reach the end of the message ID list";
+    EXPECT_TRUE(infile.eof()) << "Did not reach the end of the logging file";
+
+    // File will close when the instream is deleted at the end of this
+    // function.
+}
+
+// Check that the logger correctly creates something logging to a file.
+TEST_F(LoggerManagerTest, FileLogger) {
+
+    // Create a specification for the file logger and use the manager to
+    // connect the "filelogger" logger to it.
+    SpecificationForFileLogger file_spec;
+
+    // For the first test, we want to check that the file is created
+    // if it does not already exist.  So delete the temporary file before
+    // logging the first message.
+    unlink(file_spec.getFileName().c_str());
+
+    // Set up the file appenders.
+    LoggerManager manager;
+    manager.process(file_spec.getSpecification());
+
+    // Try logging to the file.  Local scope is set to ensure that the logger
+    // is destroyed before we reset the global logging.  We record what we
+    // put in the file for a later comparison.
+    vector<MessageID> ids;
+    {
+
+        // Scope-limit the logger to ensure it is destroyed after the brief
+        // check.  This adds weight to the idea that the logger will not
+        // keep the file open.
+        Logger logger(file_spec.getLoggerName());
+
+        LOG_FATAL(logger, LOG_DUPLICATE_MESSAGE_ID).arg("test");
+        ids.push_back(LOG_DUPLICATE_MESSAGE_ID);
+
+        LOG_FATAL(logger, LOG_DUPLICATE_NAMESPACE).arg("test");
+        ids.push_back(LOG_DUPLICATE_NAMESPACE);
+    }
+    LoggerManager::reset();
+
+    // At this point, the output file should contain two lines with messages
+    // LOG_DUPLICATE_MESSAGE_ID and LOG_DUPLICATE_NAMESPACE messages - test this.
+    checkFileContents(file_spec.getFileName(), ids.begin(), ids.end());
+
+    // Re-open the file (we have to assume that it was closed when we
+    // reset the logger - there is no easy way to check) and check that
+    // new messages are appended to it.  We use the alternative
+    // invocation of process() here to check it works.
+    vector<LoggerSpecification> spec(1, file_spec.getSpecification());
+    manager.process(spec.begin(), spec.end());
+
+    // Create a new instance of the logger and log three more messages.
+    Logger logger(file_spec.getLoggerName());
+
+    LOG_FATAL(logger, LOG_NO_SUCH_MESSAGE).arg("test");
+    ids.push_back(LOG_NO_SUCH_MESSAGE);
+
+    LOG_FATAL(logger, LOG_INVALID_MESSAGE_ID).arg("test").arg("test2");
+    ids.push_back(LOG_INVALID_MESSAGE_ID);
+
+    LOG_FATAL(logger, LOG_NO_MESSAGE_ID).arg("42");
+    ids.push_back(LOG_NO_MESSAGE_ID);
+
+    // Close the file and check again
+    LoggerManager::reset();
+    checkFileContents(file_spec.getFileName(), ids.begin(), ids.end());
+}
+
+// Check if the file rolls over when it gets above a certain size.
+TEST_F(LoggerManagerTest, FileSizeRollover) {
+    // Set to a suitable minimum that log4cplus can copy with
+    static const size_t SIZE_LIMIT = 204800;
+
+    // Set up the name of the file.
+    SpecificationForFileLogger file_spec;
+    LoggerSpecification& spec = file_spec.getSpecification();
+
+    // Expand the option to ensure that a maximum version size is set.
+    LoggerSpecification::iterator opt = spec.begin();
+    EXPECT_TRUE(opt != spec.end());
+    opt->maxsize = SIZE_LIMIT;    // Bytes
+    opt->maxver = 2;
+
+    // The current current output file does not exist (the creation of file_spec
+    // ensures that.  Check that previous versions don't either.
+    vector<string> prev_name;
+    for (int i = 0; i < 3; ++i) {
+        prev_name.push_back(file_spec.getFileName() + "." +
+                            boost::lexical_cast<string>(i + 1));
+        (void) unlink(prev_name[i].c_str());
+    }
+
+    // Generate an argument for a message that ensures that the message when
+    // logged will be over that size.
+    string big_arg(SIZE_LIMIT, 'x');
+
+    // Set up the file logger
+    LoggerManager manager;
+    manager.process(spec);
+
+    // Log the message twice using different message IDs.  This should generate
+    // three files as for the log4cplus implementation, the files appear to
+    // be rolled after the message is logged.
+    {
+        Logger logger(file_spec.getLoggerName());
+        LOG_FATAL(logger, LOG_NO_SUCH_MESSAGE).arg(big_arg);
+        LOG_FATAL(logger, LOG_DUPLICATE_NAMESPACE).arg(big_arg);
+    }
+
+    // Check them.
+    LoggerManager::reset();     // Ensure files are closed
+
+    vector<MessageID> ids;
+    ids.push_back(LOG_NO_SUCH_MESSAGE);
+    checkFileContents(prev_name[1], ids.begin(), ids.end());
+
+    ids.clear();
+    ids.push_back(LOG_DUPLICATE_NAMESPACE);
+    checkFileContents(prev_name[0], ids.begin(), ids.end());
+
+    // Log another message and check that the files have rotated and that
+    // a .3 version does not exist.
+    manager.process(spec);
+    {
+        Logger logger(file_spec.getLoggerName());
+        LOG_FATAL(logger, LOG_NO_MESSAGE_TEXT).arg(big_arg);
+    }
+
+    LoggerManager::reset();     // Ensure files are closed
+
+    // Check that the files have moved.
+    ids.clear();
+    ids.push_back(LOG_DUPLICATE_NAMESPACE);
+    checkFileContents(prev_name[1], ids.begin(), ids.end());
+
+    ids.clear();
+    ids.push_back(LOG_NO_MESSAGE_TEXT);
+    checkFileContents(prev_name[0], ids.begin(), ids.end());
+
+    // ... and check that the .3 version does not exist.
+    ifstream file3(prev_name[2].c_str(), ifstream::in);
+    EXPECT_FALSE(file3.good());
+
+    // Tidy up
+    for (unsigned int i = 0; i < prev_name.size(); ++i) {
+       (void) unlink(prev_name[i].c_str());
+    }
+}
diff --git a/src/lib/log/tests/logger_name_unittest.cc b/src/lib/log/tests/logger_name_unittest.cc
new file mode 100644
index 0000000..51fead5
--- /dev/null
+++ b/src/lib/log/tests/logger_name_unittest.cc
@@ -0,0 +1,77 @@
+// 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 <string>
+
+#include <gtest/gtest.h>
+
+#include <log/logger_name.h>
+
+using namespace isc;
+using namespace isc::log;
+
+// Test class.  To avoid disturbing the root logger configuration in other
+// tests in the suite, the root logger name is saved in the constructor and
+// restored in the destructor.  However, this is a bit chicken and egg, as the
+// functions used to do the save and restore are those being tested...
+//
+// Note that the root name is originally set by the initialization of the
+// logging configuration done in main().
+
+class LoggerNameTest : public ::testing::Test {
+public:
+    LoggerNameTest() {
+        name_ = getRootLoggerName();
+    }
+    ~LoggerNameTest() {
+        setRootLoggerName(name_);
+    }
+
+private:
+    std::string     name_;  ///< Saved name
+};
+
+// Check setting and getting of root name
+
+TEST_F(LoggerNameTest, RootNameSetGet) {
+    const std::string name1 = "test1";
+    const std::string name2 = "test2";
+
+    // Check that Set/Get works
+    setRootLoggerName(name1);
+    EXPECT_EQ(name1, getRootLoggerName());
+
+    // We could not test that the root logger name is initialised
+    // correctly (as there is one instance of it and we don't know
+    // when this test will be run) so to check that setName() actually
+    // does change the name, run the test again with a different name.
+    //
+    // (There was always the outside chance that the root logger name
+    // was initialised with name1 and that setName() has no effect.)
+    setRootLoggerName(name2);
+    EXPECT_EQ(name2, getRootLoggerName());
+}
+
+// Check expansion of name
+
+TEST_F(LoggerNameTest, ExpandLoggerName) {
+    const std::string ROOT = "example";
+    const std::string NAME = "something";
+    const std::string FULL_NAME = ROOT + "." + NAME;
+
+    setRootLoggerName(ROOT);
+    EXPECT_EQ(ROOT, expandLoggerName(ROOT));
+    EXPECT_EQ(FULL_NAME, expandLoggerName(NAME));
+    EXPECT_EQ(FULL_NAME, expandLoggerName(FULL_NAME));
+}
diff --git a/src/lib/log/tests/logger_specification_unittest.cc b/src/lib/log/tests/logger_specification_unittest.cc
new file mode 100644
index 0000000..e416c32
--- /dev/null
+++ b/src/lib/log/tests/logger_specification_unittest.cc
@@ -0,0 +1,96 @@
+// 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 <string>
+
+#include <gtest/gtest.h>
+
+#include <log/logger_specification.h>
+#include <log/output_option.h>
+
+using namespace isc::log;
+using namespace std;
+
+// Check default initialization.
+TEST(LoggerSpecificationTest, DefaultInitialization) {
+    LoggerSpecification spec;
+
+    EXPECT_EQ(string(""), spec.getName());
+    EXPECT_EQ(isc::log::INFO, spec.getSeverity());
+    EXPECT_EQ(0, spec.getDbglevel());
+    EXPECT_FALSE(spec.getAdditive());
+    EXPECT_EQ(0, spec.optionCount());
+}
+
+// Non-default initialization
+TEST(LoggerSpecificationTest, Initialization) {
+    LoggerSpecification spec("alpha", isc::log::ERROR, 42, true);
+
+    EXPECT_EQ(string("alpha"), spec.getName());
+    EXPECT_EQ(isc::log::ERROR, spec.getSeverity());
+    EXPECT_EQ(42, spec.getDbglevel());
+    EXPECT_TRUE(spec.getAdditive());
+    EXPECT_EQ(0, spec.optionCount());
+}
+
+// Get/Set tests
+TEST(LoggerSpecificationTest, SetGet) {
+    LoggerSpecification spec;
+
+    spec.setName("gamma");
+    EXPECT_EQ(string("gamma"), spec.getName());
+
+    spec.setSeverity(isc::log::FATAL);
+    EXPECT_EQ(isc::log::FATAL, spec.getSeverity());
+
+    spec.setDbglevel(7);
+    EXPECT_EQ(7, spec.getDbglevel());
+
+    spec.setAdditive(true);
+    EXPECT_TRUE(spec.getAdditive());
+
+    // Should not affect option count
+    EXPECT_EQ(0, spec.optionCount());
+}
+
+// Check option setting
+TEST(LoggerSpecificationTest, AddOption) {
+    OutputOption option1;
+    option1.destination = OutputOption::DEST_FILE;
+    option1.filename = "/tmp/example.log";
+    option1.maxsize = 123456;
+
+    OutputOption option2;
+    option2.destination = OutputOption::DEST_SYSLOG;
+    option2.facility = "LOCAL7";
+
+    LoggerSpecification spec;
+    spec.addOutputOption(option1);
+    spec.addOutputOption(option2);
+    EXPECT_EQ(2, spec.optionCount());
+
+    // Iterate through them
+    LoggerSpecification::const_iterator i = spec.begin();
+
+    EXPECT_EQ(OutputOption::DEST_FILE, i->destination);
+    EXPECT_EQ(string("/tmp/example.log"), i->filename);
+    EXPECT_EQ(123456, i->maxsize);
+
+    ++i;
+    EXPECT_EQ(OutputOption::DEST_SYSLOG, i->destination);
+    EXPECT_EQ(string("LOCAL7"), i->facility);
+
+    ++i;
+    EXPECT_TRUE(i == spec.end());
+}
diff --git a/src/lib/log/tests/logger_support_test.cc b/src/lib/log/tests/logger_support_test.cc
deleted file mode 100644
index fc571ba..0000000
--- a/src/lib/log/tests/logger_support_test.cc
+++ /dev/null
@@ -1,108 +0,0 @@
-// 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.
-
-/// \brief Example Program
-///
-/// Simple example program showing how to use the logger.
-
-#include <stdlib.h>
-#include <string.h>
-#ifdef _WIN32
-#include <getopt.h>
-#else
-#include <unistd.h>
-#endif
-
-#include <iostream>
-
-#include <log/logger.h>
-#include <log/logger_support.h>
-#include <log/root_logger_name.h>
-
-// Include a set of message definitions.
-#include <log/messagedef.h>
-
-using namespace isc::log;
-
-// Declare logger to use an example.
-Logger logger_ex("example");
-
-// The program is invoked:
-//
-// logger_support_test [-s severity] [-d level ] [local_file]
-//
-// "severity" is one of "debug", "info", "warn", "error", "fatal"
-// "level" is the debug level, a number between 0 and 99
-// "local_file" is the name of a local file.
-//
-// The program sets the attributes on the root logger and logs a set of
-// messages.  Looking at the output determines whether the program worked.
-
-int main(int argc, char** argv) {
-
-    isc::log::Severity  severity = isc::log::INFO;  // Default logger severity
-    int                 dbglevel = -1;              // Logger debug level
-    const char*         localfile = NULL;           // Local message file
-    int                 option;                     // For getopt() processing
-    Logger              logger_dlm("dlm", true);    // Another example logger
-
-    // Parse options
-    while ((option = getopt(argc, argv, "s:d:")) != -1) {
-        switch (option) {
-            case 's':
-                if (strcmp(optarg, "debug") == 0) {
-                    severity = isc::log::DEBUG;
-                } else if (strcmp(optarg, "info") == 0) {
-                    severity = isc::log::INFO;
-                } else if (strcmp(optarg, "warn") == 0) {
-                    severity = isc::log::WARN;
-                } else if (strcmp(optarg, "error") == 0) {
-                    severity = isc::log::ERROR;
-                } else if (strcmp(optarg, "fatal") == 0) {
-                    severity = isc::log::FATAL;
-                } else {
-                    std::cout << "Unrecognised severity option: " <<
-                        optarg << "\n";
-                    exit(1);
-                }
-                break;
-
-            case 'd':
-                dbglevel = atoi(optarg);
-                break;
-
-            default:
-                std::cout << "Unrecognised option: " <<
-                    static_cast<char>(option) << "\n";
-        }
-    }
-
-    if (optind < argc) {
-        localfile = argv[optind];
-    }
-
-    // Update the logging parameters
-    initLogger("alpha", severity, dbglevel, localfile);
-
-    // Log a few messages
-    logger_ex.fatal(MSG_MSGWRTERR, "test1", "42");
-    logger_ex.error(MSG_UNRECDIR, "false");
-    logger_dlm.warn(MSG_MSGRDERR, "a.txt", "dummy test");
-    logger_dlm.info(MSG_OPNMSGIN, "example.msg", "dummy test");
-    logger_ex.debug(0, MSG_UNRECDIR, "[abc]");
-    logger_ex.debug(24, MSG_UNRECDIR, "[24]");
-    logger_ex.debug(25, MSG_UNRECDIR, "[25]");
-    logger_ex.debug(26, MSG_UNRECDIR, "[26]");
-    return (0);
-}
diff --git a/src/lib/log/tests/logger_support_unittest.cc b/src/lib/log/tests/logger_support_unittest.cc
new file mode 100644
index 0000000..b418906
--- /dev/null
+++ b/src/lib/log/tests/logger_support_unittest.cc
@@ -0,0 +1,83 @@
+// 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 <gtest/gtest.h>
+#include <log/logger_support.h>
+#include <log/log_messages.h>
+
+using namespace isc::log;
+
+class LoggerSupportTest : public ::testing::Test {
+protected:
+    LoggerSupportTest() {
+        // Logger initialization is done in main().  As logging tests may
+        // alter the default logging output, it is reset here.
+        resetUnitTestRootLogger();
+    }
+    ~LoggerSupportTest() {
+    }
+};
+
+// Check that the initialized flag can be manipulated.  This is a bit chicken-
+// -and-egg: we want to reset to the flag to the original value at the end
+// of the test, so use the functions to do that.  But we are trying to check
+// that these functions in fact work.
+
+TEST_F(LoggerSupportTest, InitializedFlag) {
+    bool current_flag = isLoggingInitialized();
+
+    // check we can flip the flag.
+    setLoggingInitialized(!current_flag);
+    EXPECT_NE(current_flag, isLoggingInitialized());
+    setLoggingInitialized(!isLoggingInitialized());
+    EXPECT_EQ(current_flag, isLoggingInitialized());
+
+    // Check we can set it to explicit values (tests that a call to the "set"
+    // function does not just flip the flag).
+    setLoggingInitialized(false);
+    EXPECT_FALSE(isLoggingInitialized());
+    setLoggingInitialized(false);
+    EXPECT_FALSE(isLoggingInitialized());
+
+    setLoggingInitialized(true);
+    EXPECT_TRUE(isLoggingInitialized());
+    setLoggingInitialized(true);
+    EXPECT_TRUE(isLoggingInitialized());
+
+    // Reset to original value
+    setLoggingInitialized(current_flag);
+}
+
+// Check that a logger will throw an exception if logging has not been
+// initialized.
+
+TEST_F(LoggerSupportTest, LoggingInitializationCheck) {
+
+    // Assert that logging has been initialized (it should be in main()).
+    bool current_flag = isLoggingInitialized();
+    EXPECT_TRUE(current_flag);
+
+    // Flag that it has not been initialized and declare a logger. Any logging
+    // operation should then throw.
+    setLoggingInitialized(false);
+    isc::log::Logger test_logger("test");
+
+    EXPECT_THROW(test_logger.isDebugEnabled(), LoggingNotInitialized);
+    EXPECT_THROW(test_logger.info(LOG_INPUT_OPEN_FAIL), LoggingNotInitialized);
+
+    // ... and check that they work when logging is initialized.
+    setLoggingInitialized(true);
+    EXPECT_NO_THROW(test_logger.isDebugEnabled());
+    EXPECT_NO_THROW(test_logger.info(LOG_INPUT_OPEN_FAIL));
+}
diff --git a/src/lib/log/tests/logger_unittest.cc b/src/lib/log/tests/logger_unittest.cc
index 4eff622..edca9ce 100644
--- a/src/lib/log/tests/logger_unittest.cc
+++ b/src/lib/log/tests/logger_unittest.cc
@@ -17,45 +17,27 @@
 
 #include <gtest/gtest.h>
 
-#include <log/root_logger_name.h>
 #include <log/logger.h>
-#include <log/messagedef.h>
+#include <log/logger_manager.h>
+#include <log/logger_name.h>
+#include <log/log_messages.h>
 
 using namespace isc;
 using namespace isc::log;
 using namespace std;
 
-namespace isc {
-namespace log {
-
-/// \brief Test Logger
+/// \brief Logger Test
 ///
-/// This logger is a subclass of the logger class under test, but makes
-/// protected methods public (for testing)
-
-class TestLogger : public Logger {
-public:
-    /// \brief constructor
-    TestLogger(const string& name) : Logger(name, true)
-    {}
-
-    static void reset() {
-        Logger::reset();
-    }
-};
-
-} // namespace log
-} // namespace isc
-
+/// As the logger is only a shell around the implementation, this tests also
+/// checks the logger implementation class as well.
 
 class LoggerTest : public ::testing::Test {
-protected:
-    LoggerTest()
-    {
+public:
+    LoggerTest() {
+        // Initialization of logging is done in main()
     }
-
     ~LoggerTest() {
-        TestLogger::reset();
+        LoggerManager::reset();
     }
 };
 
@@ -65,11 +47,10 @@ protected:
 TEST_F(LoggerTest, Name) {
 
     // Create a logger
-    setRootLoggerName("test1");
     Logger logger("alpha");
 
     // ... and check the name
-    EXPECT_EQ(string("test1.alpha"), logger.getName());
+    EXPECT_EQ(getRootLoggerName() + string(".alpha"), logger.getName());
 }
 
 // This test attempts to get two instances of a logger with the same name
@@ -77,22 +58,18 @@ TEST_F(LoggerTest, Name) {
 
 TEST_F(LoggerTest, GetLogger) {
 
-    // Set the root logger name (not strictly needed, but this will be the
-    // case in the program(.
-    setRootLoggerName("test2");
-
     const string name1 = "alpha";
     const string name2 = "beta";
 
     // Instantiate two loggers that should be the same
-    TestLogger logger1(name1);
-    TestLogger logger2(name1);
+    Logger logger1(name1);
+    Logger logger2(name1);
     // And check they equal
     EXPECT_TRUE(logger1 == logger2);
 
     // Instantiate another logger with another name and check that it
     // is different to the previously instantiated ones.
-    TestLogger logger3(name2);
+    Logger logger3(name2);
     EXPECT_FALSE(logger1 == logger3);
 }
 
@@ -101,8 +78,7 @@ TEST_F(LoggerTest, GetLogger) {
 TEST_F(LoggerTest, Severity) {
 
     // Create a logger
-    setRootLoggerName("test3");
-    TestLogger logger("alpha");
+    Logger logger("alpha");
 
     // Now check the levels
     logger.setSeverity(isc::log::NONE);
@@ -132,8 +108,7 @@ TEST_F(LoggerTest, Severity) {
 TEST_F(LoggerTest, DebugLevels) {
 
     // Create a logger
-    setRootLoggerName("test4");
-    TestLogger logger("alpha");
+    Logger logger("alpha");
 
     // Debug level should be 0 if not at debug severity
     logger.setSeverity(isc::log::NONE, 20);
@@ -174,13 +149,47 @@ TEST_F(LoggerTest, DebugLevels) {
 
 TEST_F(LoggerTest, SeverityInheritance) {
 
-    // Create to loggers.  We cheat here as we know that the underlying
-    // implementation (in this case log4cxx) will set a parent-child
-    // relationship if the loggers are named <parent> and <parent>.<child>.
+    // Create two loggers.  We cheat here as we know that the underlying
+    // implementation will set a parent-child relationship if the loggers
+    // are named <parent> and <parent>.<child>.
+    Logger parent("alpha");
+    Logger child("alpha.beta");
 
-    setRootLoggerName("test5");
-    TestLogger parent("alpha");
-    TestLogger child("alpha.beta");
+    // By default, newly created loggers should have a level of DEFAULT
+    // (i.e. default to parent)
+    EXPECT_EQ(isc::log::DEFAULT, parent.getSeverity());
+    EXPECT_EQ(isc::log::DEFAULT, child.getSeverity());
+
+    // Set the severity of the parent to debug and check what is
+    // reported by the child.
+    parent.setSeverity(isc::log::DEBUG, 42);
+    EXPECT_EQ(42, parent.getDebugLevel());
+    EXPECT_EQ(0,  child.getDebugLevel());
+    EXPECT_EQ(42, child.getEffectiveDebugLevel());
+
+    // Setting the child to DEBUG severity should set its own
+    // debug level.
+    child.setSeverity(isc::log::DEBUG, 53);
+    EXPECT_EQ(53,  child.getDebugLevel());
+    EXPECT_EQ(53, child.getEffectiveDebugLevel());
+
+    // If the child severity is set to something other than DEBUG,
+    // the debug level should be reported as 0.
+    child.setSeverity(isc::log::ERROR);
+    EXPECT_EQ(0,  child.getDebugLevel());
+    EXPECT_EQ(0, child.getEffectiveDebugLevel());
+}
+
+// Check that changing the parent and child debug level does not affect
+// the other.
+
+TEST_F(LoggerTest, DebugLevelInheritance) {
+
+    // Create two loggers.  We cheat here as we know that the underlying
+    // implementation will set a parent-child relationship if the loggers
+    // are named <parent> and <parent>.<child>.
+    Logger parent("alpha");
+    Logger child("alpha.beta");
 
     // By default, newly created loggers should have a level of DEFAULT
     // (i.e. default to parent)
@@ -206,11 +215,9 @@ TEST_F(LoggerTest, SeverityInheritance) {
 
 TEST_F(LoggerTest, EffectiveSeverityInheritance) {
 
-    // Create to loggers.  We cheat here as we know that the underlying
-    // implementation (in this case log4cxx) will set a parent-child
-    // relationship if the loggers are named <parent> and <parent>.<child>.
-
-    setRootLoggerName("test6");
+    // Create two loggers.  We cheat here as we know that the underlying
+    // implementation will set a parent-child relationship if the loggers
+    // are named <parent> and <parent>.<child>.
     Logger parent("test6");
     Logger child("test6.beta");
 
@@ -245,7 +252,6 @@ TEST_F(LoggerTest, EffectiveSeverityInheritance) {
 
 TEST_F(LoggerTest, IsXxxEnabled) {
 
-    setRootLoggerName("test7");
     Logger logger("test7");
 
     logger.setSeverity(isc::log::INFO);
@@ -316,7 +322,6 @@ TEST_F(LoggerTest, IsXxxEnabled) {
 
 TEST_F(LoggerTest, IsDebugEnabledLevel) {
 
-    setRootLoggerName("test8");
     Logger logger("test8");
 
     int MID_LEVEL = (MIN_DEBUG_LEVEL + MAX_DEBUG_LEVEL) / 2;
diff --git a/src/lib/log/tests/message_dictionary_unittest.cc b/src/lib/log/tests/message_dictionary_unittest.cc
index a92585c..394fea0 100644
--- a/src/lib/log/tests/message_dictionary_unittest.cc
+++ b/src/lib/log/tests/message_dictionary_unittest.cc
@@ -29,7 +29,7 @@ using namespace std;
 // and the latter should be present.
 
 static const char* values[] = {
-    "DUPLNS", "duplicate $NAMESPACE directive found",
+    "LOG_DUPLICATE_NAMESPACE", "duplicate $NAMESPACE directive found",
     "NEWSYM", "new symbol added",
     NULL
 };
@@ -190,7 +190,7 @@ TEST_F(MessageDictionaryTest, GlobalTest) {
 TEST_F(MessageDictionaryTest, GlobalLoadTest) {
     vector<string>& duplicates = MessageInitializer::getDuplicates();
     ASSERT_EQ(1, duplicates.size());
-    EXPECT_EQ(string("DUPLNS"), duplicates[0]);
+    EXPECT_EQ(string("LOG_DUPLICATE_NAMESPACE"), duplicates[0]);
 
     string text = MessageDictionary::globalDictionary().getText("NEWSYM");
     EXPECT_EQ(string("new symbol added"), text);
diff --git a/src/lib/log/tests/message_reader_unittest.cc b/src/lib/log/tests/message_reader_unittest.cc
index 36288f2..d0214a4 100644
--- a/src/lib/log/tests/message_reader_unittest.cc
+++ b/src/lib/log/tests/message_reader_unittest.cc
@@ -16,7 +16,7 @@
 #include <string>
 #include <gtest/gtest.h>
 
-#include <log/messagedef.h>
+#include <log/log_messages.h>
 #include <log/message_dictionary.h>
 #include <log/message_exception.h>
 #include <log/message_reader.h>
@@ -68,8 +68,8 @@ TEST_F(MessageReaderTest, BlanksAndComments) {
     EXPECT_NO_THROW(reader_.processLine(" \n "));
     EXPECT_NO_THROW(reader_.processLine("# This is a comment"));
     EXPECT_NO_THROW(reader_.processLine("\t\t # Another comment"));
-    EXPECT_NO_THROW(reader_.processLine("  + A description line"));
-    EXPECT_NO_THROW(reader_.processLine("#+ A comment"));
+    EXPECT_NO_THROW(reader_.processLine("  A description line"));
+    EXPECT_NO_THROW(reader_.processLine("# A comment"));
     EXPECT_NO_THROW(reader_.processLine("  +# A description line"));
 
     // ... and (b) nothing gets added to either the map or the not-added section.
@@ -97,6 +97,15 @@ processLineException(MessageReader& reader, const char* what,
     }
 }
 
+// Check that it recognises invalid directives
+
+TEST_F(MessageReaderTest, InvalidDirectives) {
+
+    // Check that a "$" with nothing else generates an error
+    processLineException(reader_, "$", LOG_UNRECOGNISED_DIRECTIVE);
+    processLineException(reader_, "$xyz", LOG_UNRECOGNISED_DIRECTIVE);
+}
+
 // Check that it can parse a prefix
 
 TEST_F(MessageReaderTest, Prefix) {
@@ -104,31 +113,33 @@ TEST_F(MessageReaderTest, Prefix) {
     // Check that no $PREFIX is present
     EXPECT_EQ(string(""), reader_.getPrefix());
 
-    // Check that a $PREFIX directive with no argument generates an error.
-    processLineException(reader_, "$PREFIX", MSG_PRFNOARG);
+    // Check that a $PREFIX directive with no argument is OK
+    EXPECT_NO_THROW(reader_.processLine("$PREFIX"));
 
     // Check a $PREFIX with multiple arguments is invalid
-    processLineException(reader_, "$prefix A B", MSG_PRFEXTRARG);
+    processLineException(reader_, "$prefix A B", LOG_PREFIX_EXTRA_ARGS);
 
     // Prefixes should be alphanumeric (with underscores) and not start
     // with a number.
-    processLineException(reader_, "$prefix ab[cd", MSG_PRFINVARG);
-    processLineException(reader_, "$prefix 123", MSG_PRFINVARG);
-    processLineException(reader_, "$prefix 1ABC", MSG_PRFINVARG);
+    processLineException(reader_, "$prefix ab[cd", LOG_PREFIX_INVALID_ARG);
+    processLineException(reader_, "$prefix 123", LOG_PREFIX_INVALID_ARG);
+    processLineException(reader_, "$prefix 1ABC", LOG_PREFIX_INVALID_ARG);
 
     // A valid prefix should be accepted
     EXPECT_NO_THROW(reader_.processLine("$PREFIX   dlm__"));
-    EXPECT_EQ(string("DLM__"), reader_.getPrefix());
+    EXPECT_EQ(string("dlm__"), reader_.getPrefix());
 
     // And check that the parser fails on invalid prefixes...
-    processLineException(reader_, "$prefix 1ABC", MSG_PRFINVARG);
-
-    // ... and rejects another valid one
-    processLineException(reader_, "$PREFIX ABC", MSG_DUPLPRFX);
+    processLineException(reader_, "$prefix 1ABC", LOG_PREFIX_INVALID_ARG);
 
     // Check that we can clear the prefix as well
     reader_.clearPrefix();
     EXPECT_EQ(string(""), reader_.getPrefix());
+
+    EXPECT_NO_THROW(reader_.processLine("$PREFIX   dlm__"));
+    EXPECT_EQ(string("dlm__"), reader_.getPrefix());
+    EXPECT_NO_THROW(reader_.processLine("$PREFIX"));
+    EXPECT_EQ(string(""), reader_.getPrefix());
 }
 
 // Check that it can parse a namespace
@@ -139,13 +150,13 @@ TEST_F(MessageReaderTest, Namespace) {
     EXPECT_EQ(string(""), reader_.getNamespace());
 
     // Check that a $NAMESPACE directive with no argument generates an error.
-    processLineException(reader_, "$NAMESPACE", MSG_NSNOARG);
+    processLineException(reader_, "$NAMESPACE", LOG_NAMESPACE_NO_ARGS);
 
     // Check a $NAMESPACE with multiple arguments is invalid
-    processLineException(reader_, "$namespace A B", MSG_NSEXTRARG);
+    processLineException(reader_, "$namespace A B", LOG_NAMESPACE_EXTRA_ARGS);
 
     // Namespaces should be alphanumeric (with underscores and colons)
-    processLineException(reader_, "$namespace ab[cd", MSG_NSINVARG);
+    processLineException(reader_, "$namespace ab[cd", LOG_NAMESPACE_INVALID_ARG);
 
     // A valid $NAMESPACE should be accepted
     EXPECT_NO_THROW(reader_.processLine("$NAMESPACE isc"));
@@ -165,7 +176,7 @@ TEST_F(MessageReaderTest, Namespace) {
     EXPECT_EQ(string("::"), reader_.getNamespace());
 
     // ... and that another $NAMESPACE is rejected
-    processLineException(reader_, "$NAMESPACE ABC", MSG_DUPLNS);
+    processLineException(reader_, "$NAMESPACE ABC", LOG_DUPLICATE_NAMESPACE);
 }
 
 // Check that it can parse a line
@@ -173,8 +184,8 @@ TEST_F(MessageReaderTest, Namespace) {
 TEST_F(MessageReaderTest, ValidMessageAddDefault) {
 
     // Add a couple of valid messages
-    reader_.processLine("GLOBAL1\t\tthis is message global one\n");
-    reader_.processLine("GLOBAL2 this is message global two");
+    reader_.processLine("% GLOBAL1\t\tthis is message global one\n");
+    reader_.processLine("%GLOBAL2 this is message global two");
 
     // ... and check them
     EXPECT_EQ(string("this is message global one"),
@@ -191,9 +202,9 @@ TEST_F(MessageReaderTest, ValidMessageAddDefault) {
 TEST_F(MessageReaderTest, ValidMessageAdd) {
 
     // Add a couple of valid messages
-    reader_.processLine("GLOBAL1\t\tthis is message global one\n",
+    reader_.processLine("%GLOBAL1\t\tthis is message global one\n",
         MessageReader::ADD);
-    reader_.processLine("GLOBAL2 this is message global two",
+    reader_.processLine("% GLOBAL2 this is message global two",
         MessageReader::ADD);
 
     // ... and check them
@@ -214,9 +225,9 @@ TEST_F(MessageReaderTest, ValidMessageReplace) {
     dictionary_->add("GLOBAL2", "original global2 message");
 
     // Replace a couple of valid messages
-    reader_.processLine("GLOBAL1\t\tthis is message global one\n",
+    reader_.processLine("% GLOBAL1\t\tthis is message global one\n",
         MessageReader::REPLACE);
-    reader_.processLine("GLOBAL2 this is message global two",
+    reader_.processLine("% GLOBAL2 this is message global two",
         MessageReader::REPLACE);
 
     // ... and check them
@@ -237,14 +248,14 @@ TEST_F(MessageReaderTest, ValidMessageReplace) {
 TEST_F(MessageReaderTest, Overflows) {
 
     // Add a couple of valid messages
-    reader_.processLine("GLOBAL1\t\tthis is message global one\n");
-    reader_.processLine("GLOBAL2 this is message global two");
+    reader_.processLine("% GLOBAL1\t\tthis is message global one\n");
+    reader_.processLine("% GLOBAL2 this is message global two");
 
     // Add a duplicate in ADD mode.
-    reader_.processLine("GLOBAL1\t\tthis is a replacement for global one");
+    reader_.processLine("% GLOBAL1\t\tthis is a replacement for global one");
 
     // Replace a non-existent one in REPLACE mode
-    reader_.processLine("LOCAL\t\tthis is a new message",
+    reader_.processLine("% LOCAL\t\tthis is a new message",
         MessageReader::REPLACE);
 
     // Check what is in the dictionary.
diff --git a/src/lib/log/tests/output_option_unittest.cc b/src/lib/log/tests/output_option_unittest.cc
new file mode 100644
index 0000000..8f0e0de
--- /dev/null
+++ b/src/lib/log/tests/output_option_unittest.cc
@@ -0,0 +1,66 @@
+// 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 <string>
+
+#include <gtest/gtest.h>
+
+#include <log/output_option.h>
+
+using namespace isc::log;
+using namespace std;
+
+// As OutputOption is a struct, the only meaningful test is to check that it
+// initializes correctly.
+
+TEST(OutputOptionTest, Initialization) {
+    OutputOption option;
+
+    EXPECT_EQ(OutputOption::DEST_CONSOLE, option.destination);
+    EXPECT_EQ(OutputOption::STR_STDERR, option.stream);
+    EXPECT_FALSE(option.flush);
+    EXPECT_EQ(string("LOCAL0"), option.facility);
+    EXPECT_EQ(string(""), option.filename);
+    EXPECT_EQ(0, option.maxsize);
+    EXPECT_EQ(0, option.maxver);
+}
+
+TEST(OutputOption, getDestination) {
+    EXPECT_EQ(OutputOption::DEST_CONSOLE, getDestination("console"));
+    EXPECT_EQ(OutputOption::DEST_CONSOLE, getDestination("CONSOLE"));
+    EXPECT_EQ(OutputOption::DEST_CONSOLE, getDestination("CoNSoLE"));
+    EXPECT_EQ(OutputOption::DEST_FILE, getDestination("file"));
+    EXPECT_EQ(OutputOption::DEST_FILE, getDestination("FILE"));
+    EXPECT_EQ(OutputOption::DEST_FILE, getDestination("fIlE"));
+    EXPECT_EQ(OutputOption::DEST_SYSLOG, getDestination("syslog"));
+    EXPECT_EQ(OutputOption::DEST_SYSLOG, getDestination("SYSLOG"));
+    EXPECT_EQ(OutputOption::DEST_SYSLOG, getDestination("SYSlog"));
+
+    // bad values should default to DEST_CONSOLE
+    EXPECT_EQ(OutputOption::DEST_CONSOLE, getDestination("SOME_BAD_VALUE"));
+}
+
+TEST(OutputOption, getStream) {
+    EXPECT_EQ(OutputOption::STR_STDOUT, getStream("stdout"));
+    EXPECT_EQ(OutputOption::STR_STDOUT, getStream("STDOUT"));
+    EXPECT_EQ(OutputOption::STR_STDOUT, getStream("STdouT"));
+    EXPECT_EQ(OutputOption::STR_STDERR, getStream("stderr"));
+    EXPECT_EQ(OutputOption::STR_STDERR, getStream("STDERR"));
+    EXPECT_EQ(OutputOption::STR_STDERR, getStream("StDeRR"));
+
+    // bad values should default to stdout
+    EXPECT_EQ(OutputOption::STR_STDOUT, getStream("some bad value"));
+    EXPECT_EQ(OutputOption::STR_STDOUT, getStream(""));
+}
+
diff --git a/src/lib/log/tests/root_logger_name_unittest.cc b/src/lib/log/tests/root_logger_name_unittest.cc
deleted file mode 100644
index 8665794..0000000
--- a/src/lib/log/tests/root_logger_name_unittest.cc
+++ /dev/null
@@ -1,50 +0,0 @@
-// 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 <string>
-
-#include <gtest/gtest.h>
-
-#include <log/root_logger_name.h>
-
-using namespace isc;
-using namespace isc::log;
-
-class RootLoggerNameTest : public ::testing::Test {
-protected:
-    RootLoggerNameTest()
-    {
-    }
-};
-
-// Check of the (only) functionality of the class.
-
-TEST_F(RootLoggerNameTest, SetGet) {
-    const std::string name1 = "test1";
-    const std::string name2 = "test2";
-
-    // Check that Set/Get works
-    setRootLoggerName(name1);
-    EXPECT_EQ(name1, getRootLoggerName());
-
-    // We could not test that the root logger name is initialised
-    // correctly (as there is one instance of it and we don't know
-    // when this test will be run) so to check that setName() actually
-    // does change the name, run the test again with a different name.
-    //
-    // (There was always the outside chance that the root logger name
-    // was initialised with name1 and that setName() has no effect.)
-    setRootLoggerName(name2);
-    EXPECT_EQ(name2, getRootLoggerName());
-}
diff --git a/src/lib/log/tests/run_time_init_test.sh.in b/src/lib/log/tests/run_time_init_test.sh.in
deleted file mode 100755
index e2bdf6f..0000000
--- a/src/lib/log/tests/run_time_init_test.sh.in
+++ /dev/null
@@ -1,89 +0,0 @@
-#!/bin/sh
-# 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.
-
-failcount=0
-localmes=@abs_builddir@/localdef_mes_$$
-tempfile=@abs_builddir@/run_time_init_test_tempfile_$$
-
-passfail() {
-    if [ $1 -eq 0 ]; then
-        echo "pass"
-    else
-        echo "FAIL"
-    fi
-    failcount=`expr $failcount + $1`
-}
-
-# Create the local message file for testing
-
-cat > $localmes << .
-NOTHERE     this message is not in the global dictionary
-MSGRDERR    replacement read error, parameters: '%s' and '%s'
-UNRECDIR    replacement unrecognised directive message, parameter is '%s'
-.
-
-echo -n "1. runInitTest default parameters: "
-cat > $tempfile << .
-FATAL [alpha.example] MSGWRTERR, error writing to test1: 42
-ERROR [alpha.example] UNRECDIR, unrecognised directive 'false'
-WARN  [alpha.dlm] MSGRDERR, error reading from message file a.txt: dummy test
-INFO  [alpha.dlm] OPNMSGIN, unable to open message file example.msg for input: dummy test
-.
-./logger_support_test | cut -d' ' -f3- | diff $tempfile -
-passfail $?
-
-echo -n "2. Severity filter: "
-cat > $tempfile << .
-FATAL [alpha.example] MSGWRTERR, error writing to test1: 42
-ERROR [alpha.example] UNRECDIR, unrecognised directive 'false'
-.
-./logger_support_test -s error | cut -d' ' -f3- | diff $tempfile -
-passfail $?
-
-echo -n "3. Debug level: "
-cat > $tempfile << .
-FATAL [alpha.example] MSGWRTERR, error writing to test1: 42
-ERROR [alpha.example] UNRECDIR, unrecognised directive 'false'
-WARN  [alpha.dlm] MSGRDERR, error reading from message file a.txt: dummy test
-INFO  [alpha.dlm] OPNMSGIN, unable to open message file example.msg for input: dummy test
-DEBUG [alpha.example] UNRECDIR, unrecognised directive '[abc]'
-DEBUG [alpha.example] UNRECDIR, unrecognised directive '[24]'
-DEBUG [alpha.example] UNRECDIR, unrecognised directive '[25]'
-.
-./logger_support_test -s debug -d 25 | cut -d' ' -f3- | diff $tempfile -
-passfail $?
-
-echo -n "4. Local message replacement: "
-cat > $tempfile << .
-WARN  [alpha.log] IDNOTFND, could not replace message for 'NOTHERE': no such message identification
-FATAL [alpha.example] MSGWRTERR, error writing to test1: 42
-ERROR [alpha.example] UNRECDIR, replacement unrecognised directive message, parameter is 'false'
-WARN  [alpha.dlm] MSGRDERR, replacement read error, parameters: 'a.txt' and 'dummy test'
-.
-./logger_support_test -s warn $localmes | cut -d' ' -f3- | diff $tempfile -
-passfail $?
-
-rm -f $localmes
-rm -f $tempfile
-
-if [ $failcount -eq 0 ]; then
-    echo "PASS: run_time_init_test"
-elif [ $failcount -eq 1 ]; then
-    echo "FAIL: run_time_init_test - 1 test failed"
-else
-    echo "FAIL: run_time_init_test - $failcount tests failed"
-fi
-
-exit $failcount
diff --git a/src/lib/log/tests/run_time_init_test.sh.win32 b/src/lib/log/tests/run_time_init_test.sh.win32
deleted file mode 100755
index 86b01f7..0000000
--- a/src/lib/log/tests/run_time_init_test.sh.win32
+++ /dev/null
@@ -1,89 +0,0 @@
-#!/bin/sh
-# 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.
-
-failcount=0
-localmes=c:/cygwin/home/fdupont/bind10.trac826/src/lib/log/tests/localdef_mes_$$
-tempfile=c:/cygwin/home/fdupont/bind10.trac826/src/lib/log/tests/run_time_init_test_tempfile_$$
-
-passfail() {
-    if [ $1 -eq 0 ]; then
-        echo "pass"
-    else
-        echo "FAIL"
-    fi
-    failcount=`expr $failcount + $1`
-}
-
-# Create the local message file for testing
-
-cat > $localmes << .
-NOTHERE     this message is not in the global dictionary
-MSGRDERR    replacement read error, parameters: '%s' and '%s'
-UNRECDIR    replacement unrecognised directive message, parameter is '%s'
-.
-
-echo -n "1. runInitTest default parameters: "
-cat > $tempfile << .
-FATAL [alpha.example] MSGWRTERR, error writing to test1: 42
-ERROR [alpha.example] UNRECDIR, unrecognised directive 'false'
-WARN  [alpha.dlm] MSGRDERR, error reading from message file a.txt: dummy test
-INFO  [alpha.dlm] OPNMSGIN, unable to open message file example.msg for input: dummy test
-.
-./logger_support_test.exe | cut -d' ' -f3- | diff $tempfile -
-passfail $?
-
-echo -n "2. Severity filter: "
-cat > $tempfile << .
-FATAL [alpha.example] MSGWRTERR, error writing to test1: 42
-ERROR [alpha.example] UNRECDIR, unrecognised directive 'false'
-.
-./logger_support_test.exe -s error | cut -d' ' -f3- | diff $tempfile -
-passfail $?
-
-echo -n "3. Debug level: "
-cat > $tempfile << .
-FATAL [alpha.example] MSGWRTERR, error writing to test1: 42
-ERROR [alpha.example] UNRECDIR, unrecognised directive 'false'
-WARN  [alpha.dlm] MSGRDERR, error reading from message file a.txt: dummy test
-INFO  [alpha.dlm] OPNMSGIN, unable to open message file example.msg for input: dummy test
-DEBUG [alpha.example] UNRECDIR, unrecognised directive '[abc]'
-DEBUG [alpha.example] UNRECDIR, unrecognised directive '[24]'
-DEBUG [alpha.example] UNRECDIR, unrecognised directive '[25]'
-.
-./logger_support_test.exe -s debug -d 25 | cut -d' ' -f3- | diff $tempfile -
-passfail $?
-
-echo -n "4. Local message replacement: "
-cat > $tempfile << .
-WARN  [alpha.log] IDNOTFND, could not replace message for 'NOTHERE': no such message identification
-FATAL [alpha.example] MSGWRTERR, error writing to test1: 42
-ERROR [alpha.example] UNRECDIR, replacement unrecognised directive message, parameter is 'false'
-WARN  [alpha.dlm] MSGRDERR, replacement read error, parameters: 'a.txt' and 'dummy test'
-.
-./logger_support_test.exe -s warn $localmes | cut -d' ' -f3- | diff $tempfile -
-passfail $?
-
-rm -f $localmes
-rm -f $tempfile
-
-if [ $failcount -eq 0 ]; then
-    echo "PASS: run_time_init_test"
-elif [ $failcount -eq 1 ]; then
-    echo "FAIL: run_time_init_test - 1 test failed"
-else
-    echo "FAIL: run_time_init_test - $failcount tests failed"
-fi
-
-exit $failcount
diff --git a/src/lib/log/tests/run_unittests.cc b/src/lib/log/tests/run_unittests.cc
index bd3c4c9..8a9d1e5 100644
--- a/src/lib/log/tests/run_unittests.cc
+++ b/src/lib/log/tests/run_unittests.cc
@@ -13,9 +13,13 @@
 // PERFORMANCE OF THIS SOFTWARE.
 
 #include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
+
+#include <log/logger_support.h>
 
 int
 main(int argc, char* argv[]) {
     ::testing::InitGoogleTest(&argc, argv);
-    return (RUN_ALL_TESTS());
+    isc::log::initLogger();
+    return (isc::util::unittests::run_all());
 }
diff --git a/src/lib/log/tests/severity_test.sh.in b/src/lib/log/tests/severity_test.sh.in
new file mode 100755
index 0000000..78d5050
--- /dev/null
+++ b/src/lib/log/tests/severity_test.sh.in
@@ -0,0 +1,89 @@
+#!/bin/sh
+# 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.
+
+# Checks that the logger will limit the output of messages less severe than
+# the severity/debug setting.
+
+testname="Severity test"
+echo $testname
+
+failcount=0
+tempfile=@abs_builddir@/severity_test_tempfile_$$
+
+passfail() {
+    if [ $1 -eq 0 ]; then
+        echo " pass"
+    else
+        echo " FAIL"
+        failcount=`expr $failcount + $1`
+    fi
+}
+
+echo -n "1. Default parameters:"
+cat > $tempfile << .
+FATAL [example] LOG_WRITE_ERROR error writing to test1: 42
+ERROR [example] LOG_READING_LOCAL_FILE reading local message file dummy/file
+WARN  [example] LOG_BAD_STREAM bad log console output stream: example
+WARN  [example.alpha] LOG_READ_ERROR error reading from message file a.txt: dummy reason
+INFO  [example.alpha] LOG_INPUT_OPEN_FAIL unable to open message file example.msg for input: dummy reason
+FATAL [example.beta] LOG_BAD_SEVERITY unrecognized log severity: beta_fatal
+ERROR [example.beta] LOG_BAD_DESTINATION unrecognized log destination: beta_error
+WARN  [example.beta] LOG_BAD_STREAM bad log console output stream: beta_warn
+INFO  [example.beta] LOG_READ_ERROR error reading from message file beta: info
+.
+./logger_example -c stdout | cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+echo -n "2. Severity filter:"
+cat > $tempfile << .
+FATAL [example] LOG_WRITE_ERROR error writing to test1: 42
+ERROR [example] LOG_READING_LOCAL_FILE reading local message file dummy/file
+FATAL [example.beta] LOG_BAD_SEVERITY unrecognized log severity: beta_fatal
+ERROR [example.beta] LOG_BAD_DESTINATION unrecognized log destination: beta_error
+.
+./logger_example -c stdout -s error | cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+echo -n "3. Debug level:"
+cat > $tempfile << .
+FATAL [example] LOG_WRITE_ERROR error writing to test1: 42
+ERROR [example] LOG_READING_LOCAL_FILE reading local message file dummy/file
+WARN  [example] LOG_BAD_STREAM bad log console output stream: example
+WARN  [example.alpha] LOG_READ_ERROR error reading from message file a.txt: dummy reason
+INFO  [example.alpha] LOG_INPUT_OPEN_FAIL unable to open message file example.msg for input: dummy reason
+DEBUG [example] LOG_READING_LOCAL_FILE reading local message file example/0
+DEBUG [example] LOG_READING_LOCAL_FILE reading local message file example/24
+DEBUG [example] LOG_READING_LOCAL_FILE reading local message file example/25
+FATAL [example.beta] LOG_BAD_SEVERITY unrecognized log severity: beta_fatal
+ERROR [example.beta] LOG_BAD_DESTINATION unrecognized log destination: beta_error
+WARN  [example.beta] LOG_BAD_STREAM bad log console output stream: beta_warn
+INFO  [example.beta] LOG_READ_ERROR error reading from message file beta: info
+DEBUG [example.beta] LOG_BAD_SEVERITY unrecognized log severity: beta/25
+.
+./logger_example -c stdout -s debug -d 25 | cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+if [ $failcount -eq 0 ]; then
+    echo "PASS: $testname"
+elif [ $failcount -eq 1 ]; then
+    echo "FAIL: $testname - 1 test failed"
+else
+    echo "FAIL: $testname - $failcount tests failed"
+fi
+
+# Tidy up
+rm -f $tempfile
+
+exit $failcount
diff --git a/src/lib/log/tests/severity_test.sh.win32 b/src/lib/log/tests/severity_test.sh.win32
new file mode 100755
index 0000000..0c7eb93
--- /dev/null
+++ b/src/lib/log/tests/severity_test.sh.win32
@@ -0,0 +1,89 @@
+#!/bin/sh
+# 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.
+
+# Checks that the logger will limit the output of messages less severe than
+# the severity/debug setting.
+
+testname="Severity test"
+echo $testname
+
+failcount=0
+tempfile=c:/cygwin/home/fdupont/bind10.trac826/src/lib/log/tests/severity_test_tempfile_$$
+
+passfail() {
+    if [ $1 -eq 0 ]; then
+        echo " pass"
+    else
+        echo " FAIL"
+        failcount=`expr $failcount + $1`
+    fi
+}
+
+echo -n "1. Default parameters:"
+cat > $tempfile << .
+FATAL [example] LOG_WRITE_ERROR error writing to test1: 42
+ERROR [example] LOG_READING_LOCAL_FILE reading local message file dummy/file
+WARN  [example] LOG_BAD_STREAM bad log console output stream: example
+WARN  [example.alpha] LOG_READ_ERROR error reading from message file a.txt: dummy reason
+INFO  [example.alpha] LOG_INPUT_OPEN_FAIL unable to open message file example.msg for input: dummy reason
+FATAL [example.beta] LOG_BAD_SEVERITY unrecognized log severity: beta_fatal
+ERROR [example.beta] LOG_BAD_DESTINATION unrecognized log destination: beta_error
+WARN  [example.beta] LOG_BAD_STREAM bad log console output stream: beta_warn
+INFO  [example.beta] LOG_READ_ERROR error reading from message file beta: info
+.
+./logger_example.exe -c stdout | cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+echo -n "2. Severity filter:"
+cat > $tempfile << .
+FATAL [example] LOG_WRITE_ERROR error writing to test1: 42
+ERROR [example] LOG_READING_LOCAL_FILE reading local message file dummy/file
+FATAL [example.beta] LOG_BAD_SEVERITY unrecognized log severity: beta_fatal
+ERROR [example.beta] LOG_BAD_DESTINATION unrecognized log destination: beta_error
+.
+./logger_example.exe -c stdout -s error | cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+echo -n "3. Debug level:"
+cat > $tempfile << .
+FATAL [example] LOG_WRITE_ERROR error writing to test1: 42
+ERROR [example] LOG_READING_LOCAL_FILE reading local message file dummy/file
+WARN  [example] LOG_BAD_STREAM bad log console output stream: example
+WARN  [example.alpha] LOG_READ_ERROR error reading from message file a.txt: dummy reason
+INFO  [example.alpha] LOG_INPUT_OPEN_FAIL unable to open message file example.msg for input: dummy reason
+DEBUG [example] LOG_READING_LOCAL_FILE reading local message file example/0
+DEBUG [example] LOG_READING_LOCAL_FILE reading local message file example/24
+DEBUG [example] LOG_READING_LOCAL_FILE reading local message file example/25
+FATAL [example.beta] LOG_BAD_SEVERITY unrecognized log severity: beta_fatal
+ERROR [example.beta] LOG_BAD_DESTINATION unrecognized log destination: beta_error
+WARN  [example.beta] LOG_BAD_STREAM bad log console output stream: beta_warn
+INFO  [example.beta] LOG_READ_ERROR error reading from message file beta: info
+DEBUG [example.beta] LOG_BAD_SEVERITY unrecognized log severity: beta/25
+.
+./logger_example.exe -c stdout -s debug -d 25 | cut -d' ' -f3- | diff $tempfile -
+passfail $?
+
+if [ $failcount -eq 0 ]; then
+    echo "PASS: $testname"
+elif [ $failcount -eq 1 ]; then
+    echo "FAIL: $testname - 1 test failed"
+else
+    echo "FAIL: $testname - $failcount tests failed"
+fi
+
+# Tidy up
+rm -f $tempfile
+
+exit $failcount
diff --git a/src/lib/log/tests/tempdir.h.in b/src/lib/log/tests/tempdir.h.in
new file mode 100644
index 0000000..366fea3
--- /dev/null
+++ b/src/lib/log/tests/tempdir.h.in
@@ -0,0 +1,29 @@
+// 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.
+
+#ifndef __TEMPDIR_H
+#define __TEMPDIR_H
+
+/// \brief Define temporary directory
+///
+/// Defines the temporary directory in which temporary files used by the
+/// unit tests are created.
+
+#include <string>
+
+namespace {
+std::string TEMP_DIR("@builddir@");
+}
+
+#endif // __TEMPDIR_H
diff --git a/src/lib/log/tests/tempdir.h.win32 b/src/lib/log/tests/tempdir.h.win32
new file mode 100644
index 0000000..357e5e4
--- /dev/null
+++ b/src/lib/log/tests/tempdir.h.win32
@@ -0,0 +1,29 @@
+// 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.
+
+#ifndef __TEMPDIR_H
+#define __TEMPDIR_H
+
+/// \brief Define temporary directory
+///
+/// Defines the temporary directory in which temporary files used by the
+/// unit tests are created.
+
+#include <string>
+
+namespace {
+std::string TEMP_DIR("c:/cygwin/home/fdupont/bind10.trac826/src/lib/log/tests");
+}
+
+#endif // __TEMPDIR_H
diff --git a/src/lib/log/tests/xdebuglevel_unittest.cc b/src/lib/log/tests/xdebuglevel_unittest.cc
deleted file mode 100644
index ca80e5a..0000000
--- a/src/lib/log/tests/xdebuglevel_unittest.cc
+++ /dev/null
@@ -1,203 +0,0 @@
-// 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 <string>
-
-#include <gtest/gtest.h>
-
-#include <log4cxx/level.h>
-#include <log/xdebuglevel.h>
-#include <log/debug_levels.h>
-
-/// \brief XDebugLevel (Debug Extension to Level Class)
-///
-/// The class is an extension of the log4cxx Level class; this set of tests
-/// only test the extensions, they do not test the underlying Level class
-/// itself.
-
-using namespace log4cxx;
-
-class XDebugLevelTest : public ::testing::Test {
-protected:
-    XDebugLevelTest()
-    {
-    }
-};
-
-// Check a basic assertion about the numeric values of the debug levels
-
-TEST_F(XDebugLevelTest, NumericValues) {
-    EXPECT_EQ(XDebugLevel::XDEBUG_MIN_LEVEL_INT, Level::DEBUG_INT);
-    EXPECT_EQ(XDebugLevel::XDEBUG_MAX_LEVEL_INT,
-        Level::DEBUG_INT - MAX_DEBUG_LEVEL);
-
-    // ... and check that assumptions used below - that the debug levels
-    // range from 0 to 99 - are valid.
-    EXPECT_EQ(0, MIN_DEBUG_LEVEL);
-    EXPECT_EQ(99, MAX_DEBUG_LEVEL);
-}
-
-
-// Checks that the main function for generating logging level objects from
-// debug levels is working.
-
-TEST_F(XDebugLevelTest, GetExtendedDebug) {
-
-    // Get a debug level of 0.  This should be the same as the main DEBUG
-    // level.
-    LevelPtr debug0 = XDebugLevel::getExtendedDebug(0);
-    EXPECT_EQ(std::string("DEBUG"), debug0->toString());
-    EXPECT_EQ(Level::DEBUG_INT, debug0->toInt());
-    EXPECT_TRUE(*Level::getDebug() == *debug0);
-
-    // Get an arbitrary debug level in the allowed range.
-    LevelPtr debug32 = XDebugLevel::getExtendedDebug(32);
-    EXPECT_EQ(std::string("DEBUG32"), debug32->toString());
-    EXPECT_TRUE((XDebugLevel::XDEBUG_MIN_LEVEL_INT - 32) == debug32->toInt());
-
-    // Check that a value outside the range gives the nearest level.
-    LevelPtr debug_more = XDebugLevel::getExtendedDebug(MAX_DEBUG_LEVEL + 1);
-    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(MAX_DEBUG_LEVEL) == *debug_more);
-
-    LevelPtr debug_less = XDebugLevel::getExtendedDebug(MIN_DEBUG_LEVEL - 1);
-    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(MIN_DEBUG_LEVEL) == *debug_less);
-}
-
-
-// Creation of a level from an int - should return the default debug level
-// if outside the range.
-
-TEST_F(XDebugLevelTest, FromIntOneArg) {
-
-    // Check that a valid debug level is as expected
-    LevelPtr debug42 = XDebugLevel::toLevel(
-        XDebugLevel::XDEBUG_MIN_LEVEL_INT - 42);
-    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(42) == *debug42);
-
-    // ... and that an invalid one returns an object of type debug.
-    LevelPtr debug_invalid = XDebugLevel::toLevel(Level::getInfo()->toInt());
-    EXPECT_TRUE(*Level::getDebug() == *debug_invalid);
-}
-
-
-// Creation of a level from an int - should return the default level
-// if outside the range.
-
-TEST_F(XDebugLevelTest, FromIntTwoArg) {
-
-    // Check that a valid debug level is as expected
-    LevelPtr debug42 = XDebugLevel::toLevel(
-        (XDebugLevel::XDEBUG_MIN_LEVEL_INT - 42), Level::getFatal());
-    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(42) == *debug42);
-
-    // ... and that an invalid one returns an object of type debug.
-    LevelPtr debug_invalid = XDebugLevel::toLevel(
-        Level::getInfo()->toInt(), Level::getFatal());
-    EXPECT_TRUE(*Level::getFatal() == *debug_invalid);
-}
-
-
-// Creation of a level from a string - should return the default debug level
-// if outside the range.
-
-TEST_F(XDebugLevelTest, FromStringOneArg) {
-
-    // Check that a valid debug levels are as expected
-    LevelPtr debug85 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG85"));
-    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(85) == *debug85);
-
-    LevelPtr debug92 = XDebugLevel::toLevelLS(LOG4CXX_STR("debug92"));
-    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(92) == *debug92);
-
-    LevelPtr debug27 = XDebugLevel::toLevelLS(LOG4CXX_STR("Debug27"));
-    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(27) == *debug27);
-
-    LevelPtr debug0 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG"));
-    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(0) == *debug0);
-
-    // ... and that an invalid one returns an object of type debug (which is
-    // the equivalent of a debug level 0 object).
-    LevelPtr debug_invalid1 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBU"));
-    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(0) == *debug_invalid1);
-
-    LevelPtr debug_invalid2 = XDebugLevel::toLevelLS(LOG4CXX_STR("EBU"));
-    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(0) == *debug_invalid2);
-
-    LevelPtr debug_invalid3 = XDebugLevel::toLevelLS(LOG4CXX_STR(""));
-    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(0) == *debug_invalid3);
-
-    LevelPtr debug_invalid4 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUGTEN"));
-    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(0) == *debug_invalid4);
-
-    LevelPtr debug_invalid5 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG105"));
-    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(MAX_DEBUG_LEVEL) ==
-        *debug_invalid5);
-
-    LevelPtr debug_invalid6 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG-7"));
-    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(MIN_DEBUG_LEVEL) ==
-        *debug_invalid6);
-}
-
-
-// Creation of a level from a string - should return the default level
-// if outside the range.
-
-TEST_F(XDebugLevelTest, FromStringTwoArg) {
-
-    // Check that a valid debug levels are as expected
-    LevelPtr debug85 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG85"),
-            Level::getFatal());
-    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(85) == *debug85);
-
-    LevelPtr debug92 = XDebugLevel::toLevelLS(LOG4CXX_STR("debug92"),
-            Level::getFatal());
-    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(92) == *debug92);
-
-    LevelPtr debug27 = XDebugLevel::toLevelLS(LOG4CXX_STR("Debug27"),
-            Level::getFatal());
-    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(27) == *debug27);
-
-    LevelPtr debug0 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG"),
-            Level::getFatal());
-    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(0) == *debug0);
-
-    // ... and that an invalid one returns an object of type debug (which is
-    // the equivalent of a debug level 0 object).
-    LevelPtr debug_invalid1 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBU"),
-            Level::getFatal());
-    EXPECT_TRUE(*Level::getFatal() == *debug_invalid1);
-
-    LevelPtr debug_invalid2 = XDebugLevel::toLevelLS(LOG4CXX_STR("EBU"),
-            Level::getFatal());
-    EXPECT_TRUE(*Level::getFatal() == *debug_invalid2);
-
-    LevelPtr debug_invalid3 = XDebugLevel::toLevelLS(LOG4CXX_STR(""),
-            Level::getFatal());
-    EXPECT_TRUE(*Level::getFatal() == *debug_invalid3);
-
-    LevelPtr debug_invalid4 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUGTEN"),
-            Level::getFatal());
-    EXPECT_TRUE(*Level::getFatal() == *debug_invalid4);
-
-    LevelPtr debug_invalid5 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG105"),
-            Level::getFatal());
-    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(MAX_DEBUG_LEVEL) ==
-        *debug_invalid5);
-
-    LevelPtr debug_invalid6 = XDebugLevel::toLevelLS(LOG4CXX_STR("DEBUG-7"),
-            Level::getFatal());
-    EXPECT_TRUE(*XDebugLevel::getExtendedDebug(MIN_DEBUG_LEVEL) ==
-        *debug_invalid6);
-}
diff --git a/win32build/VS2010/bind10.sln b/win32build/VS2010/bind10.sln
index cd51477..0f504ef 100755
--- a/win32build/VS2010/bind10.sln
+++ b/win32build/VS2010/bind10.sln
@@ -13,8 +13,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BINDInstall", "BINDInstall\
 		{D781E925-FE57-4C75-9E26-BBC102B6A24A} = {D781E925-FE57-4C75-9E26-BBC102B6A24A}
 		{7D04222B-643C-446C-A2B8-93AF74A86246} = {7D04222B-643C-446C-A2B8-93AF74A86246}
 		{DCF4ED2E-FFD1-4432-AFEF-8D6EC96B79A2} = {DCF4ED2E-FFD1-4432-AFEF-8D6EC96B79A2}
+		{D728B037-F63F-4CB5-B840-9AC54BDFBED5} = {D728B037-F63F-4CB5-B840-9AC54BDFBED5}
 		{6D55503E-0B43-4273-B6A5-4CEB39E114AC} = {6D55503E-0B43-4273-B6A5-4CEB39E114AC}
 		{13215E3E-E75D-463D-A0EF-93A1C9A20896} = {13215E3E-E75D-463D-A0EF-93A1C9A20896}
+		{635B804D-1B52-433E-9ECD-84F507FDB1F1} = {635B804D-1B52-433E-9ECD-84F507FDB1F1}
 		{67046450-CCEA-4CAC-A05B-17516F3FB540} = {67046450-CCEA-4CAC-A05B-17516F3FB540}
 		{55BCB364-62B0-4F93-8B88-38F3349B22C8} = {55BCB364-62B0-4F93-8B88-38F3349B22C8}
 		{8F120666-1A69-4506-8546-0F665E80FFB7} = {8F120666-1A69-4506-8546-0F665E80FFB7}
@@ -28,8 +30,9 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BINDInstall", "BINDInstall\
 		{6280D58A-5E05-45D1-8B79-DF677C114CD4} = {6280D58A-5E05-45D1-8B79-DF677C114CD4}
 		{D09B618B-D0E4-468D-A4BD-E204B4344C18} = {D09B618B-D0E4-468D-A4BD-E204B4344C18}
 		{6AC4F7A4-9BDC-415F-81DB-6332CACA38B3} = {6AC4F7A4-9BDC-415F-81DB-6332CACA38B3}
+		{D85833AC-23A9-4710-9911-8AAEC4BF6E8F} = {D85833AC-23A9-4710-9911-8AAEC4BF6E8F}
 		{B5D971AD-D95B-4A15-9E31-38AEF4F69627} = {B5D971AD-D95B-4A15-9E31-38AEF4F69627}
-		{0723DEB6-E2C3-4082-91E5-0F8F4B0649F6} = {0723DEB6-E2C3-4082-91E5-0F8F4B0649F6}
+		{EB54F7B8-FAEF-4348-989C-D4E6B42CEFB1} = {EB54F7B8-FAEF-4348-989C-D4E6B42CEFB1}
 		{13D541BC-8365-42F7-840F-A117CA0413DD} = {13D541BC-8365-42F7-840F-A117CA0413DD}
 		{813BA1C9-8CD8-4B06-B1C0-FDAB576AC4B6} = {813BA1C9-8CD8-4B06-B1C0-FDAB576AC4B6}
 		{FEFFE0CB-CD6B-4E61-854C-39506D6DCD5D} = {FEFFE0CB-CD6B-4E61-854C-39506D6DCD5D}
@@ -40,6 +43,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BINDInstall", "BINDInstall\
 		{F6E728D3-A0B2-40F6-9B91-7D4474D778F3} = {F6E728D3-A0B2-40F6-9B91-7D4474D778F3}
 		{564B0ADE-76A4-4833-9610-8DEEA6A15423} = {564B0ADE-76A4-4833-9610-8DEEA6A15423}
 		{7EB244E7-D381-4CF4-A2D4-739B81F77588} = {7EB244E7-D381-4CF4-A2D4-739B81F77588}
+		{C90961EC-3DDF-432F-8E3D-BDA06E4859D9} = {C90961EC-3DDF-432F-8E3D-BDA06E4859D9}
 		{66C9A5EC-514B-4BDC-AC74-ED4CB465CAAF} = {66C9A5EC-514B-4BDC-AC74-ED4CB465CAAF}
 		{C7DEAFEC-423B-486D-BBD8-896B0DE69DDE} = {C7DEAFEC-423B-486D-BBD8-896B0DE69DDE}
 		{9C5774EF-E833-4150-8B7A-B6082D879775} = {9C5774EF-E833-4150-8B7A-B6082D879775}
@@ -142,6 +146,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblog", "liblog\liblog.vcx
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblog_tests", "liblog_tests\liblog_tests.vcxproj", "{9F69DE07-D285-4B5C-8528-DF975C59ED3B}"
 	ProjectSection(ProjectDependencies) = postProject
+		{7D04222B-643C-446C-A2B8-93AF74A86246} = {7D04222B-643C-446C-A2B8-93AF74A86246}
 		{8F120666-1A69-4506-8546-0F665E80FFB7} = {8F120666-1A69-4506-8546-0F665E80FFB7}
 		{2844FDFB-A0A1-4FA4-A654-15D69CC717DD} = {2844FDFB-A0A1-4FA4-A654-15D69CC717DD}
 		{AEF3DFFE-B566-4E6A-B299-B59B81022C06} = {AEF3DFFE-B566-4E6A-B299-B59B81022C06}
@@ -149,6 +154,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblog_tests", "liblog_test
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblog_compiler", "liblog_compiler\liblog_compiler.vcxproj", "{2E64F6CC-3AD9-4DA7-8E05-ABBB83F9AFC4}"
 	ProjectSection(ProjectDependencies) = postProject
+		{7D04222B-643C-446C-A2B8-93AF74A86246} = {7D04222B-643C-446C-A2B8-93AF74A86246}
 		{8F120666-1A69-4506-8546-0F665E80FFB7} = {8F120666-1A69-4506-8546-0F665E80FFB7}
 		{AEF3DFFE-B566-4E6A-B299-B59B81022C06} = {AEF3DFFE-B566-4E6A-B299-B59B81022C06}
 	EndProjectSection
@@ -257,7 +263,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libdatasrc_tests", "libdata
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libserver_common", "libserver_common\libserver_common.vcxproj", "{66C9A5EC-514B-4BDC-AC74-ED4CB465CAAF}"
 	ProjectSection(ProjectDependencies) = postProject
-		{32624520-5341-471B-B88D-2599DBCDABF5} = {32624520-5341-471B-B88D-2599DBCDABF5}
+		{EB54F7B8-FAEF-4348-989C-D4E6B42CEFB1} = {EB54F7B8-FAEF-4348-989C-D4E6B42CEFB1}
 	EndProjectSection
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libserver_common_tests", "libserver_common_tests\libserver_common_tests.vcxproj", "{D09B618B-D0E4-468D-A4BD-E204B4344C18}"
@@ -298,12 +304,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libdns++_benchmarks", "libd
 		{F6E728D3-A0B2-40F6-9B91-7D4474D778F3} = {F6E728D3-A0B2-40F6-9B91-7D4474D778F3}
 	EndProjectSection
 EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblog_rtitest", "liblog_rtitest\liblog_rtitest.vcxproj", "{0723DEB6-E2C3-4082-91E5-0F8F4B0649F6}"
-	ProjectSection(ProjectDependencies) = postProject
-		{8F120666-1A69-4506-8546-0F665E80FFB7} = {8F120666-1A69-4506-8546-0F665E80FFB7}
-		{AEF3DFFE-B566-4E6A-B299-B59B81022C06} = {AEF3DFFE-B566-4E6A-B299-B59B81022C06}
-	EndProjectSection
-EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libutil_io", "libutil_io\libutil_io.vcxproj", "{AC4806D1-C2CC-444B-8F0D-209851A969D2}"
 	ProjectSection(ProjectDependencies) = postProject
 		{8F120666-1A69-4506-8546-0F665E80FFB7} = {8F120666-1A69-4506-8546-0F665E80FFB7}
@@ -346,6 +346,42 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "b10-dhcp6_tests", "b10-dhcp
 		{831EDB24-3B0A-44AE-A192-6F3DEA1A9BA6} = {831EDB24-3B0A-44AE-A192-6F3DEA1A9BA6}
 	EndProjectSection
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libacl", "libacl\libacl.vcxproj", "{635B804D-1B52-433E-9ECD-84F507FDB1F1}"
+	ProjectSection(ProjectDependencies) = postProject
+		{32624520-5341-471B-B88D-2599DBCDABF5} = {32624520-5341-471B-B88D-2599DBCDABF5}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libdnsacl", "libdnsacl\libdnsacl.vcxproj", "{EB54F7B8-FAEF-4348-989C-D4E6B42CEFB1}"
+	ProjectSection(ProjectDependencies) = postProject
+		{635B804D-1B52-433E-9ECD-84F507FDB1F1} = {635B804D-1B52-433E-9ECD-84F507FDB1F1}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libacl_tests", "libacl_tests\libacl_tests.vcxproj", "{D728B037-F63F-4CB5-B840-9AC54BDFBED5}"
+	ProjectSection(ProjectDependencies) = postProject
+		{7D04222B-643C-446C-A2B8-93AF74A86246} = {7D04222B-643C-446C-A2B8-93AF74A86246}
+		{635B804D-1B52-433E-9ECD-84F507FDB1F1} = {635B804D-1B52-433E-9ECD-84F507FDB1F1}
+		{8F120666-1A69-4506-8546-0F665E80FFB7} = {8F120666-1A69-4506-8546-0F665E80FFB7}
+		{EB54F7B8-FAEF-4348-989C-D4E6B42CEFB1} = {EB54F7B8-FAEF-4348-989C-D4E6B42CEFB1}
+		{F6E728D3-A0B2-40F6-9B91-7D4474D778F3} = {F6E728D3-A0B2-40F6-9B91-7D4474D778F3}
+		{CC29C1F9-A77B-476C-803E-8830F8312571} = {CC29C1F9-A77B-476C-803E-8830F8312571}
+		{2844FDFB-A0A1-4FA4-A654-15D69CC717DD} = {2844FDFB-A0A1-4FA4-A654-15D69CC717DD}
+		{AEF3DFFE-B566-4E6A-B299-B59B81022C06} = {AEF3DFFE-B566-4E6A-B299-B59B81022C06}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblog_example", "liblog_example\liblog_example.vcxproj", "{C90961EC-3DDF-432F-8E3D-BDA06E4859D9}"
+	ProjectSection(ProjectDependencies) = postProject
+		{7D04222B-643C-446C-A2B8-93AF74A86246} = {7D04222B-643C-446C-A2B8-93AF74A86246}
+		{8F120666-1A69-4506-8546-0F665E80FFB7} = {8F120666-1A69-4506-8546-0F665E80FFB7}
+		{AEF3DFFE-B566-4E6A-B299-B59B81022C06} = {AEF3DFFE-B566-4E6A-B299-B59B81022C06}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblog_iltest", "liblog_iltest\liblog_iltest.vcxproj", "{D85833AC-23A9-4710-9911-8AAEC4BF6E8F}"
+	ProjectSection(ProjectDependencies) = postProject
+		{7D04222B-643C-446C-A2B8-93AF74A86246} = {7D04222B-643C-446C-A2B8-93AF74A86246}
+		{8F120666-1A69-4506-8546-0F665E80FFB7} = {8F120666-1A69-4506-8546-0F665E80FFB7}
+		{AEF3DFFE-B566-4E6A-B299-B59B81022C06} = {AEF3DFFE-B566-4E6A-B299-B59B81022C06}
+	EndProjectSection
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Win32 = Debug|Win32
@@ -500,10 +536,6 @@ Global
 		{C7DEAFEC-423B-486D-BBD8-896B0DE69DDE}.Debug|Win32.Build.0 = Debug|Win32
 		{C7DEAFEC-423B-486D-BBD8-896B0DE69DDE}.Release|Win32.ActiveCfg = Release|Win32
 		{C7DEAFEC-423B-486D-BBD8-896B0DE69DDE}.Release|Win32.Build.0 = Release|Win32
-		{0723DEB6-E2C3-4082-91E5-0F8F4B0649F6}.Debug|Win32.ActiveCfg = Debug|Win32
-		{0723DEB6-E2C3-4082-91E5-0F8F4B0649F6}.Debug|Win32.Build.0 = Debug|Win32
-		{0723DEB6-E2C3-4082-91E5-0F8F4B0649F6}.Release|Win32.ActiveCfg = Release|Win32
-		{0723DEB6-E2C3-4082-91E5-0F8F4B0649F6}.Release|Win32.Build.0 = Release|Win32
 		{AC4806D1-C2CC-444B-8F0D-209851A969D2}.Debug|Win32.ActiveCfg = Debug|Win32
 		{AC4806D1-C2CC-444B-8F0D-209851A969D2}.Debug|Win32.Build.0 = Debug|Win32
 		{AC4806D1-C2CC-444B-8F0D-209851A969D2}.Release|Win32.ActiveCfg = Release|Win32
@@ -532,6 +564,26 @@ Global
 		{E5640378-81D0-4769-B108-4DF4E2B0AD0C}.Debug|Win32.Build.0 = Debug|Win32
 		{E5640378-81D0-4769-B108-4DF4E2B0AD0C}.Release|Win32.ActiveCfg = Release|Win32
 		{E5640378-81D0-4769-B108-4DF4E2B0AD0C}.Release|Win32.Build.0 = Release|Win32
+		{635B804D-1B52-433E-9ECD-84F507FDB1F1}.Debug|Win32.ActiveCfg = Debug|Win32
+		{635B804D-1B52-433E-9ECD-84F507FDB1F1}.Debug|Win32.Build.0 = Debug|Win32
+		{635B804D-1B52-433E-9ECD-84F507FDB1F1}.Release|Win32.ActiveCfg = Release|Win32
+		{635B804D-1B52-433E-9ECD-84F507FDB1F1}.Release|Win32.Build.0 = Release|Win32
+		{EB54F7B8-FAEF-4348-989C-D4E6B42CEFB1}.Debug|Win32.ActiveCfg = Debug|Win32
+		{EB54F7B8-FAEF-4348-989C-D4E6B42CEFB1}.Debug|Win32.Build.0 = Debug|Win32
+		{EB54F7B8-FAEF-4348-989C-D4E6B42CEFB1}.Release|Win32.ActiveCfg = Release|Win32
+		{EB54F7B8-FAEF-4348-989C-D4E6B42CEFB1}.Release|Win32.Build.0 = Release|Win32
+		{D728B037-F63F-4CB5-B840-9AC54BDFBED5}.Debug|Win32.ActiveCfg = Debug|Win32
+		{D728B037-F63F-4CB5-B840-9AC54BDFBED5}.Debug|Win32.Build.0 = Debug|Win32
+		{D728B037-F63F-4CB5-B840-9AC54BDFBED5}.Release|Win32.ActiveCfg = Release|Win32
+		{D728B037-F63F-4CB5-B840-9AC54BDFBED5}.Release|Win32.Build.0 = Release|Win32
+		{C90961EC-3DDF-432F-8E3D-BDA06E4859D9}.Debug|Win32.ActiveCfg = Debug|Win32
+		{C90961EC-3DDF-432F-8E3D-BDA06E4859D9}.Debug|Win32.Build.0 = Debug|Win32
+		{C90961EC-3DDF-432F-8E3D-BDA06E4859D9}.Release|Win32.ActiveCfg = Release|Win32
+		{C90961EC-3DDF-432F-8E3D-BDA06E4859D9}.Release|Win32.Build.0 = Release|Win32
+		{D85833AC-23A9-4710-9911-8AAEC4BF6E8F}.Debug|Win32.ActiveCfg = Debug|Win32
+		{D85833AC-23A9-4710-9911-8AAEC4BF6E8F}.Debug|Win32.Build.0 = Debug|Win32
+		{D85833AC-23A9-4710-9911-8AAEC4BF6E8F}.Release|Win32.ActiveCfg = Release|Win32
+		{D85833AC-23A9-4710-9911-8AAEC4BF6E8F}.Release|Win32.Build.0 = Release|Win32
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
diff --git a/win32build/VS2010/libacl/libacl.vcxproj b/win32build/VS2010/libacl/libacl.vcxproj
new file mode 100755
index 0000000..9a2b8f3
--- /dev/null
+++ b/win32build/VS2010/libacl/libacl.vcxproj
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\..\src\lib\acl\acl.h" />
+    <ClInclude Include="..\..\..\src\lib\acl\check.h" />
+    <ClInclude Include="..\..\..\src\lib\acl\ip_check.h" />
+    <ClInclude Include="..\..\..\src\lib\acl\loader.h" />
+    <ClInclude Include="..\..\..\src\lib\acl\logic_check.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\..\src\lib\acl\ip_check.cc" />
+    <ClCompile Include="..\..\..\src\lib\acl\loader.cc" />
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{635B804D-1B52-433E-9ECD-84F507FDB1F1}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>libacl</RootNamespace>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <OutDir>$(Configuration)\</OutDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <OutDir>$(Configuration)\</OutDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level4</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>..\..;..\..\..\src\lib;%BOOST%;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <CompileAs>CompileAsCpp</CompileAs>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>..\..;..\..\..\src\lib;%BOOST%;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <CompileAs>CompileAsCpp</CompileAs>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/win32build/VS2010/libacl/libacl.vcxproj.filters b/win32build/VS2010/libacl/libacl.vcxproj.filters
new file mode 100755
index 0000000..f7c2dd3
--- /dev/null
+++ b/win32build/VS2010/libacl/libacl.vcxproj.filters
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\..\src\lib\acl\acl.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\..\src\lib\acl\check.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\..\src\lib\acl\ip_check.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\..\src\lib\acl\loader.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\..\src\lib\acl\logic_check.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\..\src\lib\acl\ip_check.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\src\lib\acl\loader.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/win32build/VS2010/libacl/libacl.vcxproj.user b/win32build/VS2010/libacl/libacl.vcxproj.user
new file mode 100755
index 0000000..695b5c7
--- /dev/null
+++ b/win32build/VS2010/libacl/libacl.vcxproj.user
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+</Project>
\ No newline at end of file
diff --git a/win32build/VS2010/libacl_tests/libacl_tests.vcxproj b/win32build/VS2010/libacl_tests/libacl_tests.vcxproj
new file mode 100755
index 0000000..36e334e
--- /dev/null
+++ b/win32build/VS2010/libacl_tests/libacl_tests.vcxproj
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{D728B037-F63F-4CB5-B840-9AC54BDFBED5}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>libacl_tests</RootNamespace>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+    <OutDir>$(Configuration)\</OutDir>
+    <TargetName>run_unittests</TargetName>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LinkIncremental>false</LinkIncremental>
+    <OutDir>$(Configuration)\</OutDir>
+    <TargetName>run_unittests</TargetName>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level4</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>..\..;..\..\..\src\lib;..\..\..\..\gtest\include;%BOOST%;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <OutputFile>$(OutDir)run_unittests$(TargetExt)</OutputFile>
+      <AdditionalLibraryDirectories>..\libexceptions\Debug;..\libutil\Debug;..\libutil_unittests\Debug;..\liblog\Debug;..\libdns++\Debug;..\libcc\Debug;..\libacl\Debug;..\libdnsacl\Debug;..\..\..\..\log4cplus\md10\Debug;..\..\..\..\gtest\md10\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>libexceptions.lib;libutil.lib;libutil_unittests.lib;liblog.lib;libdns++.lib;libcc.lib;libacl.lib;libdnsacl.lib;log4cplusSD.lib;gtestd.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>..\..;..\..\..\src\lib;..\..\..\..\gtest\include;%BOOST%;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <OutputFile>$(OutDir)run_unittests$(TargetExt)</OutputFile>
+      <AdditionalLibraryDirectories>..\libexceptions\Release;..\libutil\Release;..\libutil_unittests\Release;..\liblog\Release;..\libdns++\Release;..\libcc\Release;..\libacl\Release;..\libdnsacl\Release;..\..\..\..\log4cplus\md10\Release;..\..\..\..\gtest\md10\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>libexceptions.lib;libutil.lib;libutil_unittests.lib;liblog.lib;libdns++.lib;libcc.lib;libacl.lib;libdnsacl.lib;log4cplusS.lib;gtest.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\..\src\lib\acl\tests\creators.h" />
+    <ClInclude Include="..\..\..\src\lib\acl\tests\logcheck.h" />
+    <ClInclude Include="..\..\..\src\lib\acl\tests\sockaddr.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\..\src\lib\acl\tests\acl_test.cc" />
+    <ClCompile Include="..\..\..\src\lib\acl\tests\check_test.cc" />
+    <ClCompile Include="..\..\..\src\lib\acl\tests\dnsname_check_unittest.cc" />
+    <ClCompile Include="..\..\..\src\lib\acl\tests\dns_test.cc" />
+    <ClCompile Include="..\..\..\src\lib\acl\tests\ip_check_unittest.cc" />
+    <ClCompile Include="..\..\..\src\lib\acl\tests\loader_test.cc" />
+    <ClCompile Include="..\..\..\src\lib\acl\tests\logic_check_test.cc" />
+    <ClCompile Include="..\..\..\src\lib\acl\tests\run_unittests.cc" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/win32build/VS2010/libacl_tests/libacl_tests.vcxproj.filters b/win32build/VS2010/libacl_tests/libacl_tests.vcxproj.filters
new file mode 100755
index 0000000..6887139
--- /dev/null
+++ b/win32build/VS2010/libacl_tests/libacl_tests.vcxproj.filters
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\..\src\lib\acl\tests\creators.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\..\src\lib\acl\tests\logcheck.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\..\src\lib\acl\tests\sockaddr.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\..\src\lib\acl\tests\acl_test.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\src\lib\acl\tests\check_test.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\src\lib\acl\tests\dns_test.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\src\lib\acl\tests\dnsname_check_unittest.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\src\lib\acl\tests\ip_check_unittest.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\src\lib\acl\tests\loader_test.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\src\lib\acl\tests\logic_check_test.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\src\lib\acl\tests\run_unittests.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/win32build/VS2010/libacl_tests/libacl_tests.vcxproj.user b/win32build/VS2010/libacl_tests/libacl_tests.vcxproj.user
new file mode 100755
index 0000000..695b5c7
--- /dev/null
+++ b/win32build/VS2010/libacl_tests/libacl_tests.vcxproj.user
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+</Project>
\ No newline at end of file
diff --git a/win32build/VS2010/libdnsacl/libdnsacl.vcxproj b/win32build/VS2010/libdnsacl/libdnsacl.vcxproj
new file mode 100755
index 0000000..151d93b
--- /dev/null
+++ b/win32build/VS2010/libdnsacl/libdnsacl.vcxproj
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\..\src\lib\acl\dns.h" />
+    <ClInclude Include="..\..\..\src\lib\acl\dnsname_check.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\..\src\lib\acl\dns.cc" />
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{EB54F7B8-FAEF-4348-989C-D4E6B42CEFB1}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>libdnsacl</RootNamespace>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <OutDir>$(Configuration)\</OutDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <OutDir>$(Configuration)\</OutDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level4</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>..\..;..\..\..\src\lib;%BOOST%;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <CompileAs>CompileAsCpp</CompileAs>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>..\..;..\..\..\src\lib;%BOOST%;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <CompileAs>CompileAsCpp</CompileAs>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/win32build/VS2010/libdnsacl/libdnsacl.vcxproj.filters b/win32build/VS2010/libdnsacl/libdnsacl.vcxproj.filters
new file mode 100755
index 0000000..72fc04d
--- /dev/null
+++ b/win32build/VS2010/libdnsacl/libdnsacl.vcxproj.filters
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\..\src\lib\acl\dns.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\..\src\lib\acl\dnsname_check.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\..\src\lib\acl\dns.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/win32build/VS2010/libdnsacl/libdnsacl.vcxproj.user b/win32build/VS2010/libdnsacl/libdnsacl.vcxproj.user
new file mode 100755
index 0000000..695b5c7
--- /dev/null
+++ b/win32build/VS2010/libdnsacl/libdnsacl.vcxproj.user
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+</Project>
\ No newline at end of file
diff --git a/win32build/VS2010/liblog/liblog.vcxproj b/win32build/VS2010/liblog/liblog.vcxproj
index eba50f8..7966e16 100755
--- a/win32build/VS2010/liblog/liblog.vcxproj
+++ b/win32build/VS2010/liblog/liblog.vcxproj
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
 <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
diff --git a/win32build/VS2010/liblog_compiler/liblog_compiler.vcxproj b/win32build/VS2010/liblog_compiler/liblog_compiler.vcxproj
index 72f739b..48f78fd 100755
--- a/win32build/VS2010/liblog_compiler/liblog_compiler.vcxproj
+++ b/win32build/VS2010/liblog_compiler/liblog_compiler.vcxproj
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
 <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
@@ -60,8 +60,8 @@
       <SubSystem>Console</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <OutputFile>$(OutDir)message$(TargetExt)</OutputFile>
-      <AdditionalDependencies>libutil.lib;liblog.lib;%(AdditionalDependencies)</AdditionalDependencies>
-      <AdditionalLibraryDirectories>..\libutil\Debug;..\liblog\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>libexceptions.lib;libutil.lib;liblog.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>..\libexceptions\Debug;..\libutil\Debug;..\liblog\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@@ -81,8 +81,8 @@
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <OptimizeReferences>true</OptimizeReferences>
       <OutputFile>$(OutDir)message$(TargetExt)</OutputFile>
-      <AdditionalDependencies>libutil.lib;liblog.lib;%(AdditionalDependencies)</AdditionalDependencies>
-      <AdditionalLibraryDirectories>..\libutil\Release;..\liblog\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>libexceptions.lib;libutil.lib;liblog.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>..\libexceptions\Release;..\libutil\Release;..\liblog\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
     </Link>
   </ItemDefinitionGroup>
   <ItemGroup>
diff --git a/win32build/VS2010/liblog_example/liblog_example.vcxproj b/win32build/VS2010/liblog_example/liblog_example.vcxproj
new file mode 100755
index 0000000..8c26729
--- /dev/null
+++ b/win32build/VS2010/liblog_example/liblog_example.vcxproj
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{C90961EC-3DDF-432F-8E3D-BDA06E4859D9}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>liblog_example</RootNamespace>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+    <OutDir>$(Configuration)\</OutDir>
+    <TargetName>logger_example</TargetName>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LinkIncremental>false</LinkIncremental>
+    <OutDir>$(Configuration)\</OutDir>
+    <TargetName>logger_example</TargetName>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level4</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>..\..;..\..\..\src\lib;%BOOST%;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <CompileAs>CompileAsCpp</CompileAs>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <OutputFile>$(OutDir)logger_example$(TargetExt)</OutputFile>
+      <AdditionalLibraryDirectories>..\libexceptions\Debug;..\libutil\Debug;..\liblog\Debug;..\..\..\..\log4cplus\md10\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>libexceptions.lib;libutil.lib;liblog.lib;log4cplusSD.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+    <PostBuildEvent>
+      <Command>cd ..\..\..\src\lib\log\tests
+copy console_test.sh.win32 console_test.sh
+copy destination_test.sh.win32 destination_test.sh
+copy local_file_test.sh.win32 local_file_test.sh
+copy severity_test.sh.win32 severity_test.sh
+</Command>
+    </PostBuildEvent>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>..\..;..\..\..\src\lib;%BOOST%;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <CompileAs>CompileAsCpp</CompileAs>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <OutputFile>$(OutDir)logger_example$(TargetExt)</OutputFile>
+      <AdditionalLibraryDirectories>..\libexceptions\Release;..\libutil\Release;..\liblog\Release;..\..\..\..\log4cplus\md10\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>libexceptions.lib;libutil.lib;liblog.lib;log4cplusS.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+    <PostBuildEvent>
+      <Command>cd ..\..\..\src\lib\log\tests
+copy console_test.sh.win32 console_test.sh
+copy destination_test.sh.win32 destination_test.sh
+copy local_file_test.sh.win32 local_file_test.sh
+copy severity_test.sh.win32 severity_test.sh
+</Command>
+    </PostBuildEvent>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\getopt.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\..\src\lib\log\tests\logger_example.cc" />
+    <ClCompile Include="..\..\getopt.cc" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/win32build/VS2010/liblog_example/liblog_example.vcxproj.filters b/win32build/VS2010/liblog_example/liblog_example.vcxproj.filters
new file mode 100755
index 0000000..29f856a
--- /dev/null
+++ b/win32build/VS2010/liblog_example/liblog_example.vcxproj.filters
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\getopt.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\getopt.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\src\lib\log\tests\logger_example.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/win32build/VS2010/liblog_example/liblog_example.vcxproj.user b/win32build/VS2010/liblog_example/liblog_example.vcxproj.user
new file mode 100755
index 0000000..695b5c7
--- /dev/null
+++ b/win32build/VS2010/liblog_example/liblog_example.vcxproj.user
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+</Project>
\ No newline at end of file
diff --git a/win32build/VS2010/liblog_iltest/liblog_iltest.vcxproj b/win32build/VS2010/liblog_iltest/liblog_iltest.vcxproj
new file mode 100755
index 0000000..65da740
--- /dev/null
+++ b/win32build/VS2010/liblog_iltest/liblog_iltest.vcxproj
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{D85833AC-23A9-4710-9911-8AAEC4BF6E8F}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>liblog_iltest</RootNamespace>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+    <OutDir>$(Configuration)\</OutDir>
+    <TargetName>init_logger_test</TargetName>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LinkIncremental>false</LinkIncremental>
+    <OutDir>$(Configuration)\</OutDir>
+    <TargetName>init_logger_test</TargetName>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level4</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>..\..;..\..\..\src\lib;%BOOST%;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <CompileAs>CompileAsCpp</CompileAs>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <OutputFile>$(OutDir)init_logger_test$(TargetExt)</OutputFile>
+      <AdditionalLibraryDirectories>..\libexceptions\Debug;..\libutil\Debug;..\liblog\Debug;..\..\..\..\log4cplus\md10\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>libexceptions.lib;libutil.lib;liblog.lib;log4cplusSD.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+    <PostBuildEvent>
+      <Command>cd ..\..\..\src\lib\log\tests
+copy init_logger_test.sh.win32 init_logger_test.sh
+</Command>
+    </PostBuildEvent>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>..\..;..\..\..\src\lib;%BOOST%;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <CompileAs>CompileAsCpp</CompileAs>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <OutputFile>$(OutDir)init_logger_test$(TargetExt)</OutputFile>
+      <AdditionalLibraryDirectories>..\libexceptions\Release;..\libutil\Release;..\liblog\Release;..\..\..\..\log4cplus\md10\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>libexceptions.lib;libutil.lib;liblog.lib;log4cplusS.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+    <PostBuildEvent>
+      <Command>cd ..\..\..\src\lib\log\tests
+copy init_logger_test.sh.win32 init_logger_test.sh
+</Command>
+    </PostBuildEvent>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\..\src\lib\log\tests\init_logger_test.cc" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/win32build/VS2010/liblog_iltest/liblog_iltest.vcxproj.filters b/win32build/VS2010/liblog_iltest/liblog_iltest.vcxproj.filters
new file mode 100755
index 0000000..5311d05
--- /dev/null
+++ b/win32build/VS2010/liblog_iltest/liblog_iltest.vcxproj.filters
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\..\src\lib\log\tests\init_logger_test.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/win32build/VS2010/liblog_iltest/liblog_iltest.vcxproj.user b/win32build/VS2010/liblog_iltest/liblog_iltest.vcxproj.user
new file mode 100755
index 0000000..695b5c7
--- /dev/null
+++ b/win32build/VS2010/liblog_iltest/liblog_iltest.vcxproj.user
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+</Project>
\ No newline at end of file
diff --git a/win32build/VS2010/liblog_rtitest/liblog_rtitest.vcxproj b/win32build/VS2010/liblog_rtitest/liblog_rtitest.vcxproj
deleted file mode 100755
index 8fb5b65..0000000
--- a/win32build/VS2010/liblog_rtitest/liblog_rtitest.vcxproj
+++ /dev/null
@@ -1,114 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <ItemGroup Label="ProjectConfigurations">
-    <ProjectConfiguration Include="Debug|Win32">
-      <Configuration>Debug</Configuration>
-      <Platform>Win32</Platform>
-    </ProjectConfiguration>
-    <ProjectConfiguration Include="Release|Win32">
-      <Configuration>Release</Configuration>
-      <Platform>Win32</Platform>
-    </ProjectConfiguration>
-  </ItemGroup>
-  <PropertyGroup Label="Globals">
-    <ProjectGuid>{0723DEB6-E2C3-4082-91E5-0F8F4B0649F6}</ProjectGuid>
-    <Keyword>Win32Proj</Keyword>
-    <RootNamespace>liblog_rtitest</RootNamespace>
-  </PropertyGroup>
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
-    <ConfigurationType>Application</ConfigurationType>
-    <UseDebugLibraries>true</UseDebugLibraries>
-    <CharacterSet>Unicode</CharacterSet>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
-    <ConfigurationType>Application</ConfigurationType>
-    <UseDebugLibraries>false</UseDebugLibraries>
-    <WholeProgramOptimization>true</WholeProgramOptimization>
-    <CharacterSet>Unicode</CharacterSet>
-  </PropertyGroup>
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
-  <ImportGroup Label="ExtensionSettings">
-  </ImportGroup>
-  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
-  </ImportGroup>
-  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
-  </ImportGroup>
-  <PropertyGroup Label="UserMacros" />
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
-    <LinkIncremental>true</LinkIncremental>
-    <OutDir>$(Configuration)\</OutDir>
-    <TargetName>logger_support_test</TargetName>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
-    <LinkIncremental>false</LinkIncremental>
-    <OutDir>$(Configuration)\</OutDir>
-    <TargetName>logger_support_test</TargetName>
-  </PropertyGroup>
-  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
-    <ClCompile>
-      <PrecompiledHeader>NotUsing</PrecompiledHeader>
-      <WarningLevel>Level4</WarningLevel>
-      <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <AdditionalIncludeDirectories>..\..;..\..\..\src\lib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
-      <CompileAs>CompileAsCpp</CompileAs>
-    </ClCompile>
-    <Link>
-      <SubSystem>Console</SubSystem>
-      <GenerateDebugInformation>true</GenerateDebugInformation>
-      <OutputFile>$(OutDir)logger_support_test$(TargetExt)</OutputFile>
-      <AdditionalDependencies>libutil.lib;liblog.lib;%(AdditionalDependencies)</AdditionalDependencies>
-      <AdditionalLibraryDirectories>..\libutil\Debug;..\liblog\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
-    </Link>
-    <PostBuildEvent>
-      <Command>copy Debug\logger_support_test.exe ..\..\..\src\lib\log\tests</Command>
-    </PostBuildEvent>
-    <PreBuildEvent>
-      <Command>cd ..\..\..\src\lib\log\tests
-copy run_time_init_test.sh.win32 run_time_init_test.sh
-</Command>
-    </PreBuildEvent>
-  </ItemDefinitionGroup>
-  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
-    <ClCompile>
-      <WarningLevel>Level3</WarningLevel>
-      <PrecompiledHeader>NotUsing</PrecompiledHeader>
-      <Optimization>MaxSpeed</Optimization>
-      <FunctionLevelLinking>true</FunctionLevelLinking>
-      <IntrinsicFunctions>true</IntrinsicFunctions>
-      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <AdditionalIncludeDirectories>..\..;..\..\..\src\lib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
-      <CompileAs>CompileAsCpp</CompileAs>
-    </ClCompile>
-    <Link>
-      <SubSystem>Console</SubSystem>
-      <GenerateDebugInformation>true</GenerateDebugInformation>
-      <EnableCOMDATFolding>true</EnableCOMDATFolding>
-      <OptimizeReferences>true</OptimizeReferences>
-      <OutputFile>$(OutDir)logger_support_test$(TargetExt)</OutputFile>
-      <AdditionalDependencies>libutil.lib;liblog.lib;%(AdditionalDependencies)</AdditionalDependencies>
-      <AdditionalLibraryDirectories>..\libutil\Release;..\liblog\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
-    </Link>
-    <PostBuildEvent>
-      <Command>copy Release\logger_support_test.exe ..\..\..\src\lib\log\tests</Command>
-    </PostBuildEvent>
-    <PreBuildEvent>
-      <Command>cd ..\..\..\src\lib\log\tests
-copy run_time_init_test.sh.win32 run_time_init_test.sh
-</Command>
-    </PreBuildEvent>
-  </ItemDefinitionGroup>
-  <ItemGroup>
-    <ClInclude Include="..\..\getopt.h" />
-  </ItemGroup>
-  <ItemGroup>
-    <ClCompile Include="..\..\..\src\lib\log\tests\logger_support_test.cc" />
-    <ClCompile Include="..\..\getopt.cc" />
-  </ItemGroup>
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
-  <ImportGroup Label="ExtensionTargets">
-  </ImportGroup>
-</Project>
\ No newline at end of file
diff --git a/win32build/VS2010/liblog_rtitest/liblog_rtitest.vcxproj.filters b/win32build/VS2010/liblog_rtitest/liblog_rtitest.vcxproj.filters
deleted file mode 100755
index b4ddd61..0000000
--- a/win32build/VS2010/liblog_rtitest/liblog_rtitest.vcxproj.filters
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <ItemGroup>
-    <Filter Include="Source Files">
-      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
-      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
-    </Filter>
-    <Filter Include="Header Files">
-      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
-      <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
-    </Filter>
-    <Filter Include="Resource Files">
-      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
-      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
-    </Filter>
-  </ItemGroup>
-  <ItemGroup>
-    <ClInclude Include="..\..\getopt.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
-  </ItemGroup>
-  <ItemGroup>
-    <ClCompile Include="..\..\getopt.cc">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\..\src\lib\log\tests\logger_support_test.cc">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-  </ItemGroup>
-</Project>
\ No newline at end of file
diff --git a/win32build/VS2010/liblog_rtitest/liblog_rtitest.vcxproj.user b/win32build/VS2010/liblog_rtitest/liblog_rtitest.vcxproj.user
deleted file mode 100755
index 695b5c7..0000000
--- a/win32build/VS2010/liblog_rtitest/liblog_rtitest.vcxproj.user
+++ /dev/null
@@ -1,3 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-</Project>
\ No newline at end of file
diff --git a/win32build/VS2010/liblog_tests/liblog_tests.vcxproj b/win32build/VS2010/liblog_tests/liblog_tests.vcxproj
index 796af71..22a949d 100755
--- a/win32build/VS2010/liblog_tests/liblog_tests.vcxproj
+++ b/win32build/VS2010/liblog_tests/liblog_tests.vcxproj
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
 <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
@@ -11,12 +11,19 @@
     </ProjectConfiguration>
   </ItemGroup>
   <ItemGroup>
+    <ClCompile Include="..\..\..\src\lib\log\tests\logger_level_impl_unittest.cc" />
+    <ClCompile Include="..\..\..\src\lib\log\tests\logger_level_unittest.cc" />
+    <ClCompile Include="..\..\..\src\lib\log\tests\logger_manager_unittest.cc" />
+    <ClCompile Include="..\..\..\src\lib\log\tests\logger_name_unittest.cc" />
+    <ClCompile Include="..\..\..\src\lib\log\tests\logger_specification_unittest.cc" />
+    <ClCompile Include="..\..\..\src\lib\log\tests\logger_support_unittest.cc" />
     <ClCompile Include="..\..\..\src\lib\log\tests\logger_unittest.cc" />
+    <ClCompile Include="..\..\..\src\lib\log\tests\log_formatter_unittest.cc" />
     <ClCompile Include="..\..\..\src\lib\log\tests\message_dictionary_unittest.cc" />
     <ClCompile Include="..\..\..\src\lib\log\tests\message_initializer_unittest.cc" />
     <ClCompile Include="..\..\..\src\lib\log\tests\message_initializer_unittest_2.cc" />
     <ClCompile Include="..\..\..\src\lib\log\tests\message_reader_unittest.cc" />
-    <ClCompile Include="..\..\..\src\lib\log\tests\root_logger_name_unittest.cc" />
+    <ClCompile Include="..\..\..\src\lib\log\tests\output_option_unittest.cc" />
     <ClCompile Include="..\..\..\src\lib\log\tests\run_unittests.cc" />
   </ItemGroup>
   <PropertyGroup Label="Globals">
@@ -66,16 +73,21 @@
       <WarningLevel>Level4</WarningLevel>
       <Optimization>Disabled</Optimization>
       <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <AdditionalIncludeDirectories>..\..;..\..\..\src\lib;..\..\..\src\lib\log;..\..\..\..\gtest\include;%BOOST%;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>..\..;..\..\..\src\lib;..\..\..\src\lib\log;..\..\..\..\log4cplus\include;..\..\..\..\gtest\include;%BOOST%;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <CompileAs>CompileAsCpp</CompileAs>
     </ClCompile>
     <Link>
       <SubSystem>Console</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <OutputFile>$(OutDir)run_unittests$(TargetExt)</OutputFile>
-      <AdditionalDependencies>libutil.lib;liblog.lib;gtestd.lib;%(AdditionalDependencies)</AdditionalDependencies>
-      <AdditionalLibraryDirectories>..\libutil\Debug;..\liblog\Debug;..\..\..\..\gtest\md10\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>libexceptions.lib;libutil.lib;libutil_unittests.lib;liblog.lib;log4cplusSD.lib;gtestd.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>..\libexceptions\Debug;..\libutil\Debug;..\libutil_unittests\Debug;..\liblog\Debug;..\..\..\..\log4cplus\md10\Debug;..\..\..\..\gtest\md10\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
     </Link>
+    <PreBuildEvent>
+      <Command>cd ..\..\..\src\lib\log\tests
+copy tempdir.h.win32 tempdir.h
+</Command>
+    </PreBuildEvent>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
     <ClCompile>
@@ -86,7 +98,7 @@
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <AdditionalIncludeDirectories>..\..;..\..\..\src\lib;..\..\..\src\lib\log;..\..\..\..\gtest\include;%BOOST%;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>..\..;..\..\..\src\lib;..\..\..\src\lib\log;..\..\..\..\log4cplus\include;..\..\..\..\gtest\include;%BOOST%;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>
       <SubSystem>Console</SubSystem>
@@ -94,9 +106,14 @@
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <OptimizeReferences>true</OptimizeReferences>
       <OutputFile>$(OutDir)run_unittests$(TargetExt)</OutputFile>
-      <AdditionalDependencies>libutil.lib;liblog.lib;gtest.lib;%(AdditionalDependencies)</AdditionalDependencies>
-      <AdditionalLibraryDirectories>..\libutil\Release;..\liblog\Release;..\..\..\..\gtest\md10\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>libexceptions.lib;libutil.lib;libutil_unittests.lib;liblog.lib;log4cplusS.lib;gtest.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>..\libexceptions\Release;..\libutil\Release;..\libutil_unittests\Release;..\liblog\Release;..\..\..\..\log4cplus\md10\Release;..\..\..\..\gtest\md10\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
     </Link>
+    <PreBuildEvent>
+      <Command>cd ..\..\..\src\lib\log\tests
+copy tempdir.h.win32 tempdir.h
+</Command>
+    </PreBuildEvent>
   </ItemDefinitionGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
diff --git a/win32build/VS2010/liblog_tests/liblog_tests.vcxproj.filters b/win32build/VS2010/liblog_tests/liblog_tests.vcxproj.filters
index c4b121e..492d647 100755
--- a/win32build/VS2010/liblog_tests/liblog_tests.vcxproj.filters
+++ b/win32build/VS2010/liblog_tests/liblog_tests.vcxproj.filters
@@ -30,10 +30,31 @@
     <ClCompile Include="..\..\..\src\lib\log\tests\message_reader_unittest.cc">
       <Filter>Source Files</Filter>
     </ClCompile>
-    <ClCompile Include="..\..\..\src\lib\log\tests\root_logger_name_unittest.cc">
+    <ClCompile Include="..\..\..\src\lib\log\tests\run_unittests.cc">
       <Filter>Source Files</Filter>
     </ClCompile>
-    <ClCompile Include="..\..\..\src\lib\log\tests\run_unittests.cc">
+    <ClCompile Include="..\..\..\src\lib\log\tests\log_formatter_unittest.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\src\lib\log\tests\logger_level_impl_unittest.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\src\lib\log\tests\logger_level_unittest.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\src\lib\log\tests\logger_manager_unittest.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\src\lib\log\tests\logger_name_unittest.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\src\lib\log\tests\logger_specification_unittest.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\src\lib\log\tests\logger_support_unittest.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\src\lib\log\tests\output_option_unittest.cc">
       <Filter>Source Files</Filter>
     </ClCompile>
   </ItemGroup>




More information about the bind10-changes mailing list