Browse Source

[3186] Initial classes in DHCP user check hook library

Adds new source tree branch, bind10/src/hooks, for maintaining
custom hooks libraries.  Adds bare bones implementation of UserRegistry
for use in custom DHCP hooks library.
Thomas Markwalder 11 years ago
parent
commit
f0edd52c24

+ 4 - 0
configure.ac

@@ -1288,6 +1288,10 @@ AC_CONFIG_FILES([Makefile
                  src/bin/usermgr/Makefile
                  src/bin/usermgr/tests/Makefile
                  src/bin/tests/Makefile
+                 src/hooks/Makefile
+                 src/hooks/dhcp/Makefile
+                 src/hooks/dhcp/user_chk/Makefile
+                 src/hooks/dhcp/user_chk/tests/Makefile
                  src/lib/Makefile
                  src/lib/asiolink/Makefile
                  src/lib/asiolink/tests/Makefile

+ 2 - 0
src/Makefile.am

@@ -1,4 +1,6 @@
 SUBDIRS = lib bin
+# hooks lib could be a configurable switch
+SUBDIRS += hooks
 
 EXTRA_DIST = \
 	cppcheck-suppress.lst		\

+ 1 - 0
src/hooks/Makefile.am

@@ -0,0 +1 @@
+SUBDIRS = dhcp

+ 1 - 0
src/hooks/dhcp/Makefile.am

@@ -0,0 +1 @@
+SUBDIRS = user_chk

+ 59 - 0
src/hooks/dhcp/user_chk/Makefile.am

@@ -0,0 +1,59 @@
+SUBDIRS = . tests
+
+AM_CPPFLAGS  = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS  = $(B10_CXXFLAGS)
+
+# Some versions of GCC warn about some versions of Boost regarding
+# missing initializer for members in its posix_time.
+# https://svn.boost.org/trac/boost/ticket/3477
+# But older GCC compilers don't have the flag.
+AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
+
+# Define rule to build logging source files from message file
+# hooks_messages.h hooks_messages.cc: s-messages
+
+# s-messages: hooks_messages.mes
+# 	$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/hooks/hooks_messages.mes
+# 	touch $@
+
+# Tell automake that the message files are built as part of the build process
+# (so that they are built before the main library is built).
+# BUILT_SOURCES = hooks_messages.h hooks_messages.cc
+BUILT_SOURCES =
+
+# Ensure that the message file is included in the distribution
+EXTRA_DIST =
+
+# Get rid of generated message files on a clean
+# CLEANFILES = *.gcno *.gcda hooks_messages.h hooks_messages.cc s-messages
+CLEANFILES = *.gcno *.gcda
+
+lib_LTLIBRARIES = libdhcp_user_chk.la
+libdhcp_user_chk_la_SOURCES  =
+libdhcp_user_chk_la_SOURCES += load_unload.cc
+libdhcp_user_chk_la_SOURCES += user.cc user.h 
+libdhcp_user_chk_la_SOURCES += user_data_source.cc user_data_source.h
+libdhcp_user_chk_la_SOURCES += user_registry.cc user_registry.h
+libdhcp_user_chk_la_SOURCES += version.cc
+
+#nodist_libdhcp_user_chk_la_SOURCES = hooks_messages.cc hooks_messages.h
+nodist_libdhcp_user_chk_la_SOURCES =
+
+libdhcp_user_chk_la_CXXFLAGS = $(AM_CXXFLAGS)
+libdhcp_user_chk_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+libdhcp_user_chk_la_LDFLAGS  = $(AM_LDFLAGS)
+libdhcp_user_chk_la_LDFLAGS  += -avoid-version -export-dynamic -module
+libdhcp_user_chk_la_LIBADD  =
+libdhcp_user_chk_la_LIBADD  += $(top_builddir)/src/lib/hooks/libb10-hooks.la
+libdhcp_user_chk_la_LIBADD  += $(top_builddir)/src/lib/log/libb10-log.la
+libdhcp_user_chk_la_LIBADD  += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+libdhcp_user_chk_la_LIBADD  += $(top_builddir)/src/lib/util/libb10-util.la
+libdhcp_user_chk_la_LIBADD  += $(top_builddir)/src/lib/util/threads/libb10-threads.la
+
+
+if USE_CLANGPP
+# Disable unused parameter warning caused by some of the
+# Boost headers when compiling with clang.
+libdhcp_user_chk_la_CXXFLAGS += -Wno-unused-parameter
+endif

+ 32 - 0
src/hooks/dhcp/user_chk/load_unload.cc

@@ -0,0 +1,32 @@
+// Copyright (C) 2013 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.
+// load_unload.cc
+
+#include <hooks/hooks.h>
+
+using namespace isc::hooks;
+
+extern "C" {
+
+int load(LibraryHandle&) {
+    // @todo instantiate registry here
+    return (0);
+}
+
+int unload() {
+    // @todo destruct registry here
+    return (0);
+}
+
+}

+ 62 - 0
src/hooks/dhcp/user_chk/tests/Makefile.am

@@ -0,0 +1,62 @@
+SUBDIRS = .
+
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += -I$(top_builddir)/src/hooks/dhcp/user_chk
+AM_CPPFLAGS += $(BOOST_INCLUDES) $(BOTAN_INCLUDES)
+AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/hooks/dhcp/user_chk/tests\"
+AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\"
+
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+# Some versions of GCC warn about some versions of Boost regarding
+# missing initializer for members in its posix_time.
+# https://svn.boost.org/trac/boost/ticket/3477
+# But older GCC compilers don't have the flag.
+AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
+
+USER_CHK_LIB = $(top_builddir)/src/hooks/dhcp/user_chk/libdhcp_user_chk.la
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+TESTS_ENVIRONMENT = \
+	$(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
+
+TESTS =
+if HAVE_GTEST
+TESTS += libdhcp_user_chk_unittests
+
+libdhcp_user_chk_unittests_SOURCES  = 
+libdhcp_user_chk_unittests_SOURCES += ../user.cc ../user.h
+libdhcp_user_chk_unittests_SOURCES += ../user_data_source.cc ../user_data_source.h
+libdhcp_user_chk_unittests_SOURCES += ../user_registry.cc ../user_registry.h
+libdhcp_user_chk_unittests_SOURCES += run_unittests.cc
+libdhcp_user_chk_unittests_SOURCES += userid_unittests.cc
+libdhcp_user_chk_unittests_SOURCES += user_unittests.cc
+libdhcp_user_chk_unittests_SOURCES += user_registry_unittests.cc
+
+libdhcp_user_chk_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
+
+libdhcp_user_chk_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
+
+libdhcp_user_chk_unittests_CXXFLAGS = $(AM_CXXFLAGS)
+if USE_CLANGPP
+# This is to workaround unused variables tcout and tcerr in
+# log4cplus's streams.h and unused parameters from some of the
+# Boost headers.
+libdhcp_user_chk_unittests_CXXFLAGS += -Wno-unused-parameter
+endif
+
+libdhcp_user_chk_unittests_LDADD = $(top_builddir)/src/lib/log/libb10-log.la
+libdhcp_user_chk_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
+libdhcp_user_chk_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
+libdhcp_user_chk_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+#libdhcp_user_chk_unittests_LDADD += $(top_builddir)/src/hooks/dhcp/user_chk/libdhcp_user_chk.la
+libdhcp_user_chk_unittests_LDADD += ${BOTAN_LIBS} ${BOTAN_RPATH}
+libdhcp_user_chk_unittests_LDADD += $(GTEST_LDADD)
+endif
+
+noinst_PROGRAMS = $(TESTS)

+ 27 - 0
src/hooks/dhcp/user_chk/tests/run_unittests.cc

@@ -0,0 +1,27 @@
+// Copyright (C) 2013 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/logger_support.h>
+
+#include <gtest/gtest.h>
+
+int
+main(int argc, char* argv[]) {
+    ::testing::InitGoogleTest(&argc, argv);
+    isc::log::initLogger();
+
+    int result = RUN_ALL_TESTS();
+
+    return (result);
+}

+ 133 - 0
src/hooks/dhcp/user_chk/tests/user_registry_unittests.cc

@@ -0,0 +1,133 @@
+// Copyright (C) 2013  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 <dhcp/hwaddr.h>
+#include <exceptions/exceptions.h>
+#include <user_registry.h>
+
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+#include <gtest/gtest.h>
+
+using namespace std;
+
+namespace {
+
+TEST(UserRegistry, constructor) {
+    UserRegistryPtr reg;
+    ASSERT_NO_THROW(reg.reset(new UserRegistry()));
+}
+
+TEST(UserRegistry, userBasics) {
+    UserRegistryPtr reg;
+    ASSERT_NO_THROW(reg.reset(new UserRegistry()));
+
+    UserIdPtr id, id2;
+    // Make user ids.
+    ASSERT_NO_THROW(id.reset(new UserId(UserId::HW_ADDRESS, "01010101")));
+    ASSERT_NO_THROW(id2.reset(new UserId(UserId::HW_ADDRESS, "02020202")));
+
+    UserPtr user, user2;
+    // Verify that a blank user cannot be added.
+    ASSERT_THROW(reg->addUser(user), isc::BadValue);
+
+    // Make new users.
+    ASSERT_NO_THROW(user.reset(new User(*id)));
+    ASSERT_NO_THROW(user2.reset(new User(*id)));
+
+    // Add first user.
+    ASSERT_NO_THROW(reg->addUser(user));
+
+    // Verify user added can be found.
+    UserPtr found_user;
+    ASSERT_NO_THROW(found_user = reg->findUser(*id));
+    EXPECT_TRUE(found_user);
+    EXPECT_EQ(*id, found_user->getUserId());
+
+    // Verify user not added cannot be found.
+    ASSERT_NO_THROW(found_user = reg->findUser(*id2));
+    EXPECT_FALSE(found_user);
+
+    // Verfiy user can no longer be found.
+    ASSERT_NO_THROW(reg->removeUser(*id));
+    ASSERT_NO_THROW(found_user = reg->findUser(*id));
+    EXPECT_FALSE(found_user);
+}
+
+TEST(UserRegistry, findByHWAddr) {
+    UserRegistryPtr reg;
+    ASSERT_NO_THROW(reg.reset(new UserRegistry()));
+
+    // Make a user.
+    UserPtr user;
+    ASSERT_NO_THROW(user.reset(new User(UserId::HW_ADDRESS, "01010101")));
+
+    // Verify user can be added.
+    ASSERT_NO_THROW(reg->addUser(user));
+
+    // Make a HWAddr instance using the same id value.
+    isc::dhcp::HWAddr hwaddr(user->getUserId().getId(), 
+                             isc::dhcp::HTYPE_ETHER);
+
+    // Verify user can be found by HWAddr.
+    UserPtr found_user;
+    ASSERT_NO_THROW(found_user = reg->findUser(hwaddr));
+    EXPECT_TRUE(found_user);
+    EXPECT_EQ(user->getUserId(), found_user->getUserId());
+}
+
+TEST(UserRegistry, findByDUID) {
+    UserRegistryPtr reg;
+    ASSERT_NO_THROW(reg.reset(new UserRegistry()));
+
+    // Make a user.
+    UserPtr user;
+    ASSERT_NO_THROW(user.reset(new User(UserId::DUID, "01010101")));
+
+    // Verify user can be added.
+    ASSERT_NO_THROW(reg->addUser(user));
+
+    // Make a HWAddr instance using the same id value.
+    isc::dhcp::DUID duid(user->getUserId().getId());
+
+    // Verify user can be found by HWAddr.
+    UserPtr found_user;
+    ASSERT_NO_THROW(found_user = reg->findUser(duid));
+    EXPECT_TRUE(found_user);
+    EXPECT_EQ(user->getUserId(), found_user->getUserId());
+}
+
+TEST(UserRegistry, findByClientId) {
+    UserRegistryPtr reg;
+    ASSERT_NO_THROW(reg.reset(new UserRegistry()));
+
+    // Make a user.
+    UserPtr user;
+    ASSERT_NO_THROW(user.reset(new User(UserId::CLIENT_ID, "01010101")));
+
+    // Verify user can be added.
+    ASSERT_NO_THROW(reg->addUser(user));
+
+    // Make a HWAddr instance using the same id value.
+    isc::dhcp::ClientId client_id(user->getUserId().getId());
+
+    // Verify user can be found by HWAddr.
+    UserPtr found_user;
+    ASSERT_NO_THROW(found_user = reg->findUser(client_id));
+    EXPECT_TRUE(found_user);
+    EXPECT_EQ(user->getUserId(), found_user->getUserId());
+}
+
+} // end of anonymous namespace

+ 72 - 0
src/hooks/dhcp/user_chk/tests/user_unittests.cc

@@ -0,0 +1,72 @@
+// Copyright (C) 2013  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 <exceptions/exceptions.h>
+#include <user.h>
+
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+#include <gtest/gtest.h>
+
+using namespace std;
+
+namespace {
+
+TEST(UserTest, construction) {
+    std::string test_address("01FF02AC030B0709");
+
+    UserIdPtr id;
+    ASSERT_NO_THROW(id.reset(new UserId(UserId::HW_ADDRESS, test_address)));
+
+    UserPtr user;
+    // Verify construction from a UserId.
+    ASSERT_NO_THROW(user.reset(new User(*id)));
+
+    // Verify construction from a type and an address vector.
+    ASSERT_NO_THROW(user.reset(new User(UserId::HW_ADDRESS, id->getId())));   
+    ASSERT_NO_THROW(user.reset(new User(UserId::DUID, id->getId())));   
+    ASSERT_NO_THROW(user.reset(new User(UserId::CLIENT_ID, id->getId())));   
+
+    // Verify construction from a type and an address string.
+    ASSERT_NO_THROW(user.reset(new User(UserId::HW_ADDRESS, test_address)));   
+    ASSERT_NO_THROW(user.reset(new User(UserId::DUID, test_address)));   
+    ASSERT_NO_THROW(user.reset(new User(UserId::CLIENT_ID, test_address)));   
+}
+
+TEST(UserTest, properties) {
+    UserPtr user;
+    ASSERT_NO_THROW(user.reset(new User(UserId::DUID, "01020304050607")));   
+
+    std::string value = "";
+    EXPECT_NO_THROW(user->setProperty("one","1"));
+    EXPECT_NO_THROW(value = user->getProperty("one"));
+    EXPECT_EQ(value, "1");
+
+    EXPECT_NO_THROW(user->setProperty("one","1.0"));
+    EXPECT_NO_THROW(value = user->getProperty("one"));
+    EXPECT_EQ(value, "1.0");
+
+    EXPECT_NO_THROW(user->delProperty("one"));
+    EXPECT_NO_THROW(value = user->getProperty("one"));
+    EXPECT_TRUE(value.empty());
+
+    EXPECT_THROW(user->setProperty("", "blah"), isc::BadValue);
+
+    EXPECT_NO_THROW(user->setProperty("one", ""));
+    EXPECT_NO_THROW(value = user->getProperty("one"));
+    EXPECT_TRUE(value.empty());
+}
+
+} // end of anonymous namespace

+ 165 - 0
src/hooks/dhcp/user_chk/tests/userid_unittests.cc

@@ -0,0 +1,165 @@
+// Copyright (C) 2013  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 <exceptions/exceptions.h>
+#include <user.h>
+
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+#include <gtest/gtest.h>
+
+using namespace std;
+
+namespace {
+
+TEST(UserIdTest, invalidConstructors) {
+    // Verify that constructor does not allow empty id vector.
+    std::vector<uint8_t> empty_bytes;
+    ASSERT_THROW(UserId(UserId::HW_ADDRESS, empty_bytes), isc::BadValue);
+    ASSERT_THROW(UserId(UserId::DUID, empty_bytes), isc::BadValue);
+    ASSERT_THROW(UserId(UserId::CLIENT_ID, empty_bytes), isc::BadValue);
+
+    // Verify that constructor does not allow empty id string.
+    ASSERT_THROW(UserId(UserId::HW_ADDRESS, ""), isc::BadValue);
+    ASSERT_THROW(UserId(UserId::DUID, ""), isc::BadValue);
+    ASSERT_THROW(UserId(UserId::CLIENT_ID, ""), isc::BadValue);
+}
+
+TEST(UserIdTest, hwAddress_type) {
+
+    EXPECT_EQ(std::string(UserId::HW_ADDRESS_STR),
+              UserId::lookupTypeStr(UserId::HW_ADDRESS));
+
+    EXPECT_EQ(UserId::HW_ADDRESS,
+              UserId::lookupType(UserId::HW_ADDRESS_STR));
+
+    uint8_t tmp[] = { 0x01, 0xFF, 0x02, 0xAC, 0x03, 0x0B, 0x07, 0x08 };
+    std::vector<uint8_t> bytes(tmp, tmp + (sizeof(tmp)/sizeof(uint8_t)));
+
+    UserIdPtr id;
+    ASSERT_NO_THROW(id.reset(new UserId(UserId::HW_ADDRESS, bytes)));
+    EXPECT_EQ(id->getType(), UserId::HW_ADDRESS);
+    EXPECT_EQ(bytes, id->getId());
+
+    // Verify a == b;
+    UserIdPtr id2;
+    ASSERT_NO_THROW(id2.reset(new UserId(UserId::HW_ADDRESS, id->toText())));
+    EXPECT_TRUE(*id == *id2);
+    EXPECT_FALSE(*id != *id2);
+    EXPECT_FALSE(*id < *id2);
+
+    // Verify a < b;
+    ASSERT_NO_THROW(id2.reset(new UserId(UserId::HW_ADDRESS,
+                                         "01FF02AC030B0709")));
+    EXPECT_FALSE(*id == *id2);
+    EXPECT_TRUE(*id != *id2);
+    EXPECT_TRUE(*id < *id2);
+
+    // Verify a > b;
+    ASSERT_NO_THROW(id2.reset(new UserId(UserId::HW_ADDRESS,
+                                         "01FF02AC030B0707")));
+    EXPECT_FALSE(*id == *id2);
+    EXPECT_TRUE(*id != *id2);
+    EXPECT_FALSE(*id < *id2);
+}
+
+TEST(UserIdTest, duid_type) {
+    EXPECT_EQ(std::string(UserId::DUID_STR),
+              UserId::lookupTypeStr(UserId::DUID));
+
+    EXPECT_EQ(UserId::DUID,
+              UserId::lookupType(UserId::DUID_STR));
+
+    uint8_t tmp[] = { 0x01, 0xFF, 0x02, 0xAC, 0x03, 0x0B, 0x07, 0x08 };
+    std::vector<uint8_t> bytes(tmp, tmp + (sizeof(tmp)/sizeof(uint8_t)));
+
+    UserIdPtr id;
+    ASSERT_NO_THROW(id.reset(new UserId(UserId::DUID, bytes)));
+    EXPECT_EQ(id->getType(), UserId::DUID);
+    EXPECT_EQ(bytes, id->getId());
+
+    // Verify a == b;
+    UserIdPtr id2;
+    ASSERT_NO_THROW(id2.reset(new UserId(UserId::DUID, id->toText())));
+    EXPECT_TRUE(*id == *id2);
+    EXPECT_FALSE(*id != *id2);
+    EXPECT_FALSE(*id < *id2);
+
+    // Verify a < b;
+    ASSERT_NO_THROW(id2.reset(new UserId(UserId::DUID, "01FF02AC030B0709")));
+    EXPECT_FALSE(*id == *id2);
+    EXPECT_TRUE(*id != *id2);
+    EXPECT_TRUE(*id < *id2);
+
+    // Verify a > b;
+    ASSERT_NO_THROW(id2.reset(new UserId(UserId::DUID, "01FF02AC030B0707")));
+    EXPECT_FALSE(*id == *id2);
+    EXPECT_TRUE(*id != *id2);
+    EXPECT_FALSE(*id < *id2);
+}
+
+TEST(UserIdTest, client_id_type) {
+    EXPECT_EQ(std::string(UserId::CLIENT_ID_STR),
+              UserId::lookupTypeStr(UserId::CLIENT_ID));
+
+    EXPECT_EQ(UserId::CLIENT_ID,
+              UserId::lookupType(UserId::CLIENT_ID_STR));
+
+    uint8_t tmp[] = { 0x01, 0xFF, 0x02, 0xAC, 0x03, 0x0B, 0x07, 0x08 };
+    std::vector<uint8_t> bytes(tmp, tmp + (sizeof(tmp)/sizeof(uint8_t)));
+
+    UserIdPtr id;
+    ASSERT_NO_THROW(id.reset(new UserId(UserId::CLIENT_ID, bytes)));
+    EXPECT_EQ(id->getType(), UserId::CLIENT_ID);
+    EXPECT_EQ(bytes, id->getId());
+
+    // Verify a == b;
+    UserIdPtr id2;
+    ASSERT_NO_THROW(id2.reset(new UserId(UserId::CLIENT_ID, id->toText())));
+    EXPECT_TRUE(*id == *id2);
+    EXPECT_FALSE(*id != *id2);
+    EXPECT_FALSE(*id < *id2);
+
+    // Verify a < b;
+    ASSERT_NO_THROW(id2.reset(new UserId(UserId::CLIENT_ID,
+                                         "01FF02AC030B0709")));
+    EXPECT_FALSE(*id == *id2);
+    EXPECT_TRUE(*id != *id2);
+    EXPECT_TRUE(*id < *id2);
+
+    // Verify a > b;
+    ASSERT_NO_THROW(id2.reset(new UserId(UserId::CLIENT_ID,
+                                         "01FF02AC030B0707")));
+    EXPECT_FALSE(*id == *id2);
+    EXPECT_TRUE(*id != *id2);
+    EXPECT_FALSE(*id < *id2);
+}
+
+TEST(UserIdTest, type_compare) {
+    UserIdPtr hw, duid, client;
+    ASSERT_NO_THROW(hw.reset(new UserId(UserId::HW_ADDRESS,
+                                        "01FF02AC030B0709")));
+    ASSERT_NO_THROW(duid.reset(new UserId(UserId::DUID,
+                                          "01FF02AC030B0709")));
+    ASSERT_NO_THROW(client.reset(new UserId(UserId::CLIENT_ID,
+                                            "01FF02AC030B0709")));
+
+    // Verify that UserIdType influences logical comparators.
+    EXPECT_NE(*hw, *duid);
+    EXPECT_NE(*hw , *client);
+    EXPECT_NE(*duid, *client);
+}
+
+} // end of anonymous namespace

+ 226 - 0
src/hooks/dhcp/user_chk/user.cc

@@ -0,0 +1,226 @@
+// Copyright (C) 2013 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 <dhcp/hwaddr.h>
+#include <dhcp/duid.h>
+#include <exceptions/exceptions.h>
+
+#include <user.h>
+
+#include <iomanip>
+#include <sstream>
+
+//********************************* UserId ******************************
+const char* UserId::HW_ADDRESS_STR = "HW_ADDR";
+const char* UserId::DUID_STR = "DUID";
+const char* UserId::CLIENT_ID_STR = "CLIENT_ID";
+
+UserId::UserId(UserIdType id_type, const std::vector<uint8_t>& id)
+    : id_type_(id_type), id_(id) {
+    if (id.size() == 0) {
+        isc_throw(isc::BadValue, "UserId id may not be blank");
+    }
+}
+
+UserId::UserId(UserIdType id_type, const std::string & id_str) :
+    id_type_(id_type) {
+
+    if (id_str.empty()) {
+        isc_throw(isc::BadValue, "UserId id string may not be blank");
+    }
+
+    // logic to convert id_str etc...
+    // input str is expected to be 2-digits per bytes, no delims
+    std::vector<uint8_t> addr_bytes;
+    decodeHex(id_str, addr_bytes);
+
+    // Attempt to instantiate the appropriate id class to leverage validation.
+    switch (id_type) {
+        case HW_ADDRESS: {
+            isc::dhcp::HWAddr hwaddr(addr_bytes, isc::dhcp::HTYPE_ETHER);
+            break;
+            }
+        case CLIENT_ID: {
+            isc::dhcp::ClientId client_id(addr_bytes);
+            break;
+            }
+        case DUID: {
+            isc::dhcp::DUID duid(addr_bytes);
+            break;
+            }
+        default:
+            isc_throw (isc::BadValue, "Invalid id_type: " << id_type);
+            break;
+    }
+
+    // It's a valid id.
+    id_ = addr_bytes;
+}
+
+UserId::~UserId() {
+}
+
+const std::vector<uint8_t>&
+UserId::getId() const {
+    return (id_);
+}
+
+UserId::UserIdType
+UserId::getType() const {
+    return (id_type_);
+}
+
+std::string
+UserId::toText(char delim_char) const {
+    std::stringstream tmp;
+    tmp << std::hex;
+    bool delim = false;
+    for (std::vector<uint8_t>::const_iterator it = id_.begin();
+         it != id_.end(); ++it) {
+        if (delim_char && delim) {
+            tmp << delim_char;
+        }
+
+        tmp << std::setw(2) << std::setfill('0')
+            << static_cast<unsigned int>(*it);
+        delim = true;
+    }
+
+    return (tmp.str());
+}
+
+bool
+UserId::operator ==(const UserId & other) const {
+    return ((this->id_type_ == other.id_type_) && (this->id_ == other.id_));
+}
+
+bool
+UserId::operator !=(const UserId & other) const {
+    return (!(*this == other));
+}
+
+bool
+UserId::operator <(const UserId & other) const {
+    return ((this->id_type_ == other.id_type_) && (this->id_ < other.id_));
+}
+
+void
+UserId::decodeHex(const std::string& input, std::vector<uint8_t>& bytes)
+const {
+    int len = input.size();
+    if ((len % 2) != 0) {
+        isc_throw (isc::BadValue,
+                   "input string must be event number of digits: "
+                             << input);
+    }
+
+    for (int i = 0; i < len / 2; i++) {
+        unsigned int tmp;
+        if (sscanf (&input[i*2], "%02x", &tmp) != 1) {
+            isc_throw (isc::BadValue,
+                       "input string contains invalid characters: "
+                                 << input);
+        }
+
+        bytes.push_back(static_cast<uint8_t>(tmp));
+    }
+}
+
+std::string
+UserId::lookupTypeStr(UserIdType type) {
+    const char* tmp = NULL;
+    switch (type) {
+        case HW_ADDRESS:
+            tmp = HW_ADDRESS_STR;
+            break;
+        case DUID:
+            tmp = DUID_STR;
+            break;
+        case CLIENT_ID:
+            tmp = CLIENT_ID_STR;
+            break;
+        default:
+            isc_throw(isc::BadValue, "Invalid UserIdType:" << type);
+            break;
+    }
+
+    return (std::string(tmp));
+}
+
+UserId::UserIdType
+UserId::lookupType(const std::string& type_str) {
+    if (type_str == HW_ADDRESS_STR) {
+        return (HW_ADDRESS);
+    } else if (type_str == DUID_STR) {
+        return (DUID);
+    } else if (type_str == CLIENT_ID_STR) {
+        return (CLIENT_ID);
+    }
+
+    isc_throw(isc::BadValue, "Invalid UserIdType string:" << type_str);
+}
+
+std::ostream&
+operator<<(std::ostream& os, const UserId& user_id) {
+    std::string tmp = UserId::lookupTypeStr(user_id.getType());
+    os << tmp << "=" << user_id.toText();
+    return (os);
+}
+
+//********************************* User ******************************
+
+User::User(const UserId& user_id) : user_id_(user_id) {
+}
+
+User::User(UserId::UserIdType id_type, const std::vector<uint8_t>& id)
+    : user_id_(id_type, id) {
+}
+
+User::User(UserId::UserIdType id_type, const std::string& id_str) 
+    : user_id_(id_type, id_str) {
+}
+
+User::~User() {
+}
+
+void User::setProperty(const std::string& name, const std::string& value) {
+    if (name.empty()) {
+        isc_throw (isc::BadValue, "User property name cannot be blank");
+    }
+
+    properties_[name]=value;
+}
+
+std::string
+User::getProperty(const std::string& name) const {
+    PropertyMap::const_iterator it = properties_.find(name);
+    if (it != properties_.end()) {
+        return ((*it).second);
+    }
+
+    return ("");
+}
+
+void
+User::delProperty(const std::string & name) {
+    PropertyMap::iterator it = properties_.find(name);
+    if (it != properties_.end()) {
+        properties_.erase(it);
+    }
+}
+
+const UserId&
+User::getUserId() const {
+    return (user_id_);
+}

+ 109 - 0
src/hooks/dhcp/user_chk/user.h

@@ -0,0 +1,109 @@
+// Copyright (C) 2013 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 _USER_H
+#define _USER_H
+
+#include <boost/shared_ptr.hpp>
+
+#include <vector>
+#include <map>
+
+class UserId {
+public:
+    enum UserIdType {
+        HW_ADDRESS,
+        DUID,
+        CLIENT_ID
+    };
+
+    static const char* HW_ADDRESS_STR;
+    static const char* DUID_STR;
+    static const char* CLIENT_ID_STR; 
+
+    UserId(UserIdType id_type, const std::vector<uint8_t>& id);
+
+    UserId(UserIdType id_type, const std::string& id_str);
+
+    ~UserId();
+
+    /// @brief Returns a const reference to the actual id value
+    const std::vector<uint8_t>& getId() const;
+
+    /// @brief Returns the UserIdType 
+    UserIdType getType() const;
+
+    /// @brief Returns textual representation of a id (e.g. 00:01:02:03:ff)
+    std::string toText(char delim_char=0x0) const;
+
+    /// @brief Compares two UserIds for equality
+    bool operator ==(const UserId & other) const;
+
+    /// @brief Compares two UserIds for inequality
+    bool operator !=(const UserId & other) const;
+
+    /// @brief Performs less than comparision of two UserIds
+    bool operator <(const UserId & other) const;
+
+    static std::string lookupTypeStr(UserIdType type);
+
+    static UserIdType lookupType(const std::string& type_str);
+
+private:
+    void decodeHex(const std::string& input, std::vector<uint8_t>& bytes) const;
+
+    /// @brief The type of id value 
+    UserIdType id_type_;
+
+    /// @brief The id value 
+    std::vector<uint8_t> id_;
+
+};
+
+std::ostream&
+operator<<(std::ostream& os, const UserId& user_id);
+
+typedef boost::shared_ptr<UserId> UserIdPtr;
+
+typedef std::map<std::string, std::string> PropertyMap;
+
+class User {
+public:
+
+    User(const UserId & user_id);
+
+    User(UserId::UserIdType id_type, const std::vector<uint8_t>& id);
+
+    User(UserId::UserIdType id_type, const std::string& id_str);
+
+    ~User();
+
+    void setProperty(const std::string& name, const std::string& value);
+
+    std::string getProperty(const std::string& name) const;
+
+    void delProperty(const std::string& name);
+
+    const UserId& getUserId() const;
+
+private:
+
+    UserId user_id_;
+
+    PropertyMap properties_;
+};
+
+typedef boost::shared_ptr<User> UserPtr;
+
+#endif

+ 49 - 0
src/hooks/dhcp/user_chk/user_data_source.cc

@@ -0,0 +1,49 @@
+// Copyright (C) 2013 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 <user_data_source.h>
+
+UserDataSource::UserDataSource() {
+}
+
+UserDataSource::~UserDataSource() {
+    if (open_flag_) {
+        close();
+    }
+}
+
+void 
+UserDataSource::open() {
+    open_flag_ = false;
+}
+
+UserPtr 
+UserDataSource::readNextUser() {
+    return (UserPtr());
+}
+
+void 
+UserDataSource::close() {
+    open_flag_ = false;
+}
+
+bool 
+UserDataSource::isOpen() const {
+    return open_flag_;
+}
+
+void 
+UserDataSource::setOpenFlag(bool value) {
+  open_flag_ = value;
+}

+ 42 - 0
src/hooks/dhcp/user_chk/user_data_source.h

@@ -0,0 +1,42 @@
+// Copyright (C) 2013 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 _USER_DATA_SOURCE_H
+#define _USER_DATA_SOURCE_H
+
+#include <user.h>
+
+class UserDataSource {
+
+public:
+    UserDataSource();
+
+    ~UserDataSource();
+
+    void open();
+
+    UserPtr readNextUser();
+
+    void close();
+
+    bool isOpen() const;
+
+    void setOpenFlag(bool value);
+
+private:
+    bool open_flag_;
+};
+
+typedef boost::shared_ptr<UserDataSource> UserDataSourcePtr;
+
+#endif

+ 89 - 0
src/hooks/dhcp/user_chk/user_registry.cc

@@ -0,0 +1,89 @@
+// Copyright (C) 2013 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 <user_registry.h>
+#include <user.h>
+
+UserRegistry::UserRegistry() {
+}
+
+UserRegistry::~UserRegistry(){
+}
+
+void 
+UserRegistry::addUser(UserPtr& user) {
+    if (!user) {
+        isc_throw (isc::BadValue, "UserRegistry cannot add blank user");
+    }
+
+    if (findUser(user->getUserId())) {
+        isc_throw (isc::BadValue, "Duplicate User!");
+    }
+
+    users_[user->getUserId()] = user;
+}
+
+const UserPtr& 
+UserRegistry::findUser(const UserId& id) const {
+    static UserPtr empty;
+    UserMap::const_iterator it = users_.find(id);
+    if (it != users_.end()) {
+        return ((*it).second);
+    }
+
+    return empty;
+}
+
+void 
+UserRegistry::removeUser(const UserId& id) {
+    static UserPtr empty;
+    UserMap::iterator it = users_.find(id);
+    if (it != users_.end()) {
+        users_.erase(it);
+    }
+}
+
+const UserPtr& 
+UserRegistry::findUser(const isc::dhcp::HWAddr& hwaddr) const {
+    UserId id(UserId::HW_ADDRESS, hwaddr.hwaddr_);
+    return (findUser(id));
+}
+
+const UserPtr& 
+UserRegistry::findUser(const isc::dhcp::ClientId& client_id) const {
+    UserId id(UserId::CLIENT_ID, client_id.getClientId());
+    return (findUser(id));
+}
+
+const UserPtr& 
+UserRegistry::findUser(const isc::dhcp::DUID& duid) const {
+    UserId id(UserId::DUID, duid.getDuid());
+    return (findUser(id));
+}
+
+void UserRegistry::refresh() {
+}
+
+void UserRegistry::clearall() {
+    users_.clear();
+}
+
+void UserRegistry::setSource(UserDataSourcePtr& source) {
+    source_ = source;
+}
+
+const UserDataSourcePtr& UserRegistry::getSource() {
+    return (source_);
+}
+

+ 62 - 0
src/hooks/dhcp/user_chk/user_registry.h

@@ -0,0 +1,62 @@
+// Copyright (C) 2013 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 _USER_REGISTRY_H
+#define _USER_REGISTRY_H
+
+#include <dhcp/hwaddr.h>
+#include <dhcp/duid.h>
+#include <user.h>
+#include <user_data_source.h>
+
+#include <string>
+
+using namespace std;
+
+typedef std::map<UserId,UserPtr> UserMap;
+
+class UserRegistry {
+public:
+    UserRegistry();
+
+    ~UserRegistry();
+
+    void addUser(UserPtr& user);
+
+    const UserPtr& findUser(const UserId& id) const;
+
+    void removeUser(const UserId&  id);
+
+    const UserPtr& findUser(const isc::dhcp::HWAddr& hwaddr) const;
+
+    const UserPtr& findUser(const isc::dhcp::ClientId& client_id) const;
+
+    const UserPtr& findUser(const isc::dhcp::DUID& duid) const;
+
+    void refresh();
+
+    void clearall();
+
+    const UserDataSourcePtr& getSource();
+
+    void setSource(UserDataSourcePtr& source);
+
+private:
+    UserMap users_;
+
+    UserDataSourcePtr source_;
+};
+
+typedef boost::shared_ptr<UserRegistry> UserRegistryPtr;
+
+#endif

+ 24 - 0
src/hooks/dhcp/user_chk/version.cc

@@ -0,0 +1,24 @@
+// Copyright (C) 2013 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.
+// version.cc
+
+#include <hooks/hooks.h>
+
+extern "C" {
+
+int version() {
+    return (BIND10_HOOKS_VERSION);
+}
+
+}