BIND 10 trac826, updated. a345a7b3ab571567f79decba3a8488c8ace56258 [trac826] dhcp lib updates

BIND 10 source code commits bind10-changes at lists.isc.org
Wed Sep 28 19:01:00 UTC 2011


The branch, trac826 has been updated
       via  a345a7b3ab571567f79decba3a8488c8ace56258 (commit)
      from  01493a27e861561f8b83dd9b95711cbfbe5b0929 (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 a345a7b3ab571567f79decba3a8488c8ace56258
Author: Francis Dupont <fdupont at isc.org>
Date:   Wed Sep 28 21:00:21 2011 +0200

    [trac826] dhcp lib updates
    
    update lib/util (including io, tests, unittests but not python stuff)
    update lib/asiolink according to trac1186 (including tests)
    import lib/dhcp from trac1186 (including tests)
    
    lib/dhcp[/tests] compiles and unit tests work.

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

Summary of changes:
 src/lib/Makefile.am                                |    6 +-
 src/lib/asiolink/io_address.cc                     |   26 +
 src/lib/asiolink/io_address.h                      |   18 +
 src/lib/asiolink/simple_callback.h                 |    2 +-
 src/lib/asiolink/tests/io_address_unittest.cc      |   20 +
 src/lib/dhcp/Makefile.am                           |   35 +
 src/lib/dhcp/README                                |   11 +
 src/lib/dhcp/dhcp6.h                               |  184 +++
 src/lib/dhcp/libdhcp.cc                            |  138 +++
 src/lib/dhcp/libdhcp.h                             |   90 ++
 src/lib/dhcp/option.cc                             |  256 ++++
 src/lib/dhcp/option.h                              |  230 ++++
 src/lib/dhcp/option6_addrlst.cc                    |  136 +++
 src/lib/dhcp/option6_addrlst.h                     |  127 ++
 src/lib/dhcp/option6_ia.cc                         |  136 +++
 src/lib/dhcp/option6_ia.h                          |   87 ++
 src/lib/dhcp/option6_iaaddr.cc                     |  144 +++
 src/lib/dhcp/option6_iaaddr.h                      |   80 ++
 src/lib/dhcp/pkt6.cc                               |  314 +++++
 src/lib/dhcp/pkt6.h                                |  106 ++
 src/lib/dhcp/tests/Makefile.am                     |   44 +
 src/lib/dhcp/tests/libdhcp_unittest.cc             |  155 +++
 src/lib/dhcp/tests/option6_addrlst_unittest.cc     |  212 ++++
 src/lib/dhcp/tests/option6_ia_unittest.cc          |  261 +++++
 src/lib/dhcp/tests/option6_iaaddr_unittest.cc      |  100 ++
 src/lib/dhcp/tests/option_unittest.cc              |  259 ++++
 src/lib/dhcp/tests/pkt6_unittest.cc                |  155 +++
 src/lib/dhcp/tests/run_unittests.cc                |   25 +
 src/lib/util/Makefile.am                           |    6 +-
 src/lib/util/encode/base_n.cc                      |   41 +-
 src/lib/util/filename.cc                           |   18 +
 src/lib/util/filename.h                            |   12 +
 src/lib/util/io/Makefile.am                        |    6 +-
 src/lib/util/io/tests/Makefile.am                  |   25 -
 src/lib/util/io/tests/fd_share_tests.cc            |   74 --
 src/lib/util/io/tests/fd_tests.cc                  |   70 --
 src/lib/util/io/tests/run_unittests.cc             |   22 -
 src/lib/util/python/Makefile.am                    |    1 +
 src/lib/util/python/gen_wiredata.py.in             | 1232 ++++++++++++++++++++
 src/lib/util/python/gen_wiredata.py.win32          | 1232 ++++++++++++++++++++
 src/lib/util/python/mkpywrapper.py.in              |  100 ++
 src/lib/util/python/mkpywrapper.py.win32           |  100 ++
 src/lib/util/python/pycppwrapper_util.h            |  335 ++++++
 src/lib/util/python/wrapper_template.cc            |  309 +++++
 src/lib/util/python/wrapper_template.h             |   59 +
 src/lib/util/pyunittests/Makefile.am               |   17 +
 src/lib/util/pyunittests/pyunittests_util.cc       |   84 ++
 src/lib/util/strutil.cc                            |   11 +
 src/lib/util/strutil.h                             |   62 +
 src/lib/util/tests/Makefile.am                     |   24 +-
 src/lib/util/tests/base32hex_unittest.cc           |    5 +
 src/lib/util/tests/base64_unittest.cc              |    6 +
 src/lib/util/tests/fd_share_tests.cc               |   74 ++
 src/lib/util/tests/fd_tests.cc                     |   70 ++
 src/lib/util/tests/filename_unittest.cc            |   52 +
 src/lib/util/tests/run_unittests.cc                |    4 +-
 src/lib/util/tests/strutil_unittest.cc             |   80 ++-
 src/lib/util/time_utilities.cc                     |    7 +-
 src/lib/util/time_utilities.h                      |   28 +
 src/lib/util/unittests/Makefile.am                 |   14 +
 src/lib/util/unittests/run_all.cc                  |   95 ++
 src/lib/util/unittests/run_all.h                   |   52 +
 src/lib/util/unittests/testdata.cc                 |   61 +
 src/lib/util/unittests/testdata.h                  |   54 +
 src/lib/util/unittests/textdata.h                  |  103 ++
 win32build/VS2010/bind10.sln                       |   29 +-
 .../libasiolink_tests/libasiolink_tests.vcxproj    |   10 +-
 win32build/VS2010/libdhcp/libdhcp.vcxproj          |   98 ++
 win32build/VS2010/libdhcp/libdhcp.vcxproj.filters  |   60 +
 .../libdhcp.vcxproj.user}                          |    0 
 .../VS2010/libdhcp_tests/libdhcp_tests.vcxproj     |  100 ++
 .../libdhcp_tests/libdhcp_tests.vcxproj.filters    |   40 +
 .../libdhcp_tests.vcxproj.user}                    |    0 
 .../libutil_io_tests/libutil_io_tests.vcxproj      |   95 --
 .../libutil_io_tests.vcxproj.filters               |   25 -
 .../VS2010/libutil_tests/libutil_tests.vcxproj     |   11 +-
 .../libutil_tests/libutil_tests.vcxproj.filters    |    3 +
 .../libutil_unittests/libutil_unittests.vcxproj    |    9 +-
 .../libutil_unittests.vcxproj.filters              |   15 +
 79 files changed, 8013 insertions(+), 384 deletions(-)
 create mode 100644 src/lib/dhcp/Makefile.am
 create mode 100644 src/lib/dhcp/README
 create mode 100644 src/lib/dhcp/dhcp6.h
 create mode 100644 src/lib/dhcp/libdhcp.cc
 create mode 100644 src/lib/dhcp/libdhcp.h
 create mode 100644 src/lib/dhcp/option.cc
 create mode 100644 src/lib/dhcp/option.h
 create mode 100644 src/lib/dhcp/option6_addrlst.cc
 create mode 100644 src/lib/dhcp/option6_addrlst.h
 create mode 100644 src/lib/dhcp/option6_ia.cc
 create mode 100644 src/lib/dhcp/option6_ia.h
 create mode 100644 src/lib/dhcp/option6_iaaddr.cc
 create mode 100644 src/lib/dhcp/option6_iaaddr.h
 create mode 100644 src/lib/dhcp/pkt6.cc
 create mode 100644 src/lib/dhcp/pkt6.h
 create mode 100644 src/lib/dhcp/tests/Makefile.am
 create mode 100644 src/lib/dhcp/tests/libdhcp_unittest.cc
 create mode 100644 src/lib/dhcp/tests/option6_addrlst_unittest.cc
 create mode 100644 src/lib/dhcp/tests/option6_ia_unittest.cc
 create mode 100644 src/lib/dhcp/tests/option6_iaaddr_unittest.cc
 create mode 100644 src/lib/dhcp/tests/option_unittest.cc
 create mode 100644 src/lib/dhcp/tests/pkt6_unittest.cc
 create mode 100644 src/lib/dhcp/tests/run_unittests.cc
 delete mode 100644 src/lib/util/io/tests/Makefile.am
 delete mode 100644 src/lib/util/io/tests/PARTIAL_PORT_ON_WINDOWS
 delete mode 100644 src/lib/util/io/tests/fd_share_tests.cc
 delete mode 100644 src/lib/util/io/tests/fd_tests.cc
 delete mode 100644 src/lib/util/io/tests/run_unittests.cc
 create mode 100644 src/lib/util/python/Makefile.am
 create mode 100755 src/lib/util/python/gen_wiredata.py.in
 create mode 100755 src/lib/util/python/gen_wiredata.py.win32
 create mode 100755 src/lib/util/python/mkpywrapper.py.in
 create mode 100755 src/lib/util/python/mkpywrapper.py.win32
 create mode 100644 src/lib/util/python/pycppwrapper_util.h
 create mode 100644 src/lib/util/python/wrapper_template.cc
 create mode 100644 src/lib/util/python/wrapper_template.h
 create mode 100644 src/lib/util/pyunittests/Makefile.am
 create mode 100644 src/lib/util/pyunittests/pyunittests_util.cc
 create mode 100644 src/lib/util/tests/fd_share_tests.cc
 create mode 100644 src/lib/util/tests/fd_tests.cc
 create mode 100644 src/lib/util/unittests/run_all.cc
 create mode 100644 src/lib/util/unittests/run_all.h
 create mode 100644 src/lib/util/unittests/testdata.cc
 create mode 100644 src/lib/util/unittests/testdata.h
 create mode 100644 src/lib/util/unittests/textdata.h
 create mode 100755 win32build/VS2010/libdhcp/libdhcp.vcxproj
 create mode 100755 win32build/VS2010/libdhcp/libdhcp.vcxproj.filters
 rename win32build/VS2010/{libutil_io_tests/libutil_io_tests.vcxproj.user => libdhcp/libdhcp.vcxproj.user} (100%)
 create mode 100755 win32build/VS2010/libdhcp_tests/libdhcp_tests.vcxproj
 create mode 100755 win32build/VS2010/libdhcp_tests/libdhcp_tests.vcxproj.filters
 copy win32build/VS2010/{BINDInstall/BINDInstall.vcxproj.user => libdhcp_tests/libdhcp_tests.vcxproj.user} (100%)
 delete mode 100755 win32build/VS2010/libutil_io_tests/libutil_io_tests.vcxproj
 delete mode 100755 win32build/VS2010/libutil_io_tests/libutil_io_tests.vcxproj.filters

-----------------------------------------------------------------------
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index 184b090..c825e66 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -1,3 +1,3 @@
-SUBDIRS = exceptions util cryptolink dns cc config python xfr bench \
-          log asiolink asiodns nsas cache resolve testutils datasrc \
-          server_common
+SUBDIRS = exceptions util log cryptolink dns cc config python xfr \
+          bench asiolink asiodns nsas cache resolve testutils datasrc \
+          acl server_common dhcp
diff --git a/src/lib/asiolink/io_address.cc b/src/lib/asiolink/io_address.cc
index 5c3dfa8..da9223e 100644
--- a/src/lib/asiolink/io_address.cc
+++ b/src/lib/asiolink/io_address.cc
@@ -58,6 +58,27 @@ IOAddress::toText() const {
     return (asio_address_.to_string());
 }
 
+IOAddress
+IOAddress::from_bytes(short family, const char* data) {
+    static char addr_str[INET6_ADDRSTRLEN];
+    if (data == NULL) {
+        isc_throw(BadValue, "NULL pointer received.");
+    }
+    if ( (family != AF_INET) && (family != AF_INET6) ) {
+        isc_throw(BadValue, "Invalid family type. Only AF_INET and AF_INET6"
+                  << "are supported");
+    }
+
+#ifdef _WIN32
+#define DECONST (void *)
+#else
+#define DECONST
+#endif
+
+    inet_ntop(family, DECONST data, addr_str,INET6_ADDRSTRLEN);
+    return IOAddress(string(addr_str));
+}
+
 short
 IOAddress::getFamily() const {
     if (asio_address_.is_v4()) {
@@ -67,5 +88,10 @@ IOAddress::getFamily() const {
     }
 }
 
+const asio::ip::address&
+IOAddress::getAddress() const {
+    return asio_address_;
+}
+
 } // namespace asiolink
 } // namespace isc
diff --git a/src/lib/asiolink/io_address.h b/src/lib/asiolink/io_address.h
index 0e119b6..59ba8e7 100644
--- a/src/lib/asiolink/io_address.h
+++ b/src/lib/asiolink/io_address.h
@@ -76,11 +76,29 @@ public:
     /// \return A string representation of the address.
     std::string toText() const;
 
+    /// \brief Returns const reference to the underlying address object.
+    ///
+    /// This is useful, when access to interface offerted by
+    //  asio::ip::address_v4 and asio::ip::address_v6 is beneficial.
+    /// 
+    /// \return A const reference to asio::ip::address object
+    const asio::ip::address& getAddress() const;
+
     /// \brief Returns the address family
     ///
     /// \return AF_INET for IPv4 or AF_INET6 for IPv6.
     short getFamily() const;
 
+
+    /// \brief Creates an address from over wire data.
+    ///
+    /// \param family AF_NET for IPv4 or AF_NET6 for IPv6.
+    /// \param data pointer to first char of data
+    ///
+    /// \return Created IOAddress object
+    static IOAddress
+    from_bytes(short family, const char* data);
+
     /// \brief Compare addresses for equality
     ///
     /// \param other Address to compare against.
diff --git a/src/lib/asiolink/simple_callback.h b/src/lib/asiolink/simple_callback.h
index 92093ec..6cb07e2 100644
--- a/src/lib/asiolink/simple_callback.h
+++ b/src/lib/asiolink/simple_callback.h
@@ -49,7 +49,7 @@ protected:
     ///
     /// This is intentionally defined as \c protected as this base class
     /// should never be instantiated (except as part of a derived class).
-    SimpleCallback() : self_(this) {}
+    SimpleCallback() { self_ = this; }
 public:
     /// \brief The destructor
     virtual ~SimpleCallback() {}
diff --git a/src/lib/asiolink/tests/io_address_unittest.cc b/src/lib/asiolink/tests/io_address_unittest.cc
index 18b181e..3abd535 100644
--- a/src/lib/asiolink/tests/io_address_unittest.cc
+++ b/src/lib/asiolink/tests/io_address_unittest.cc
@@ -61,3 +61,23 @@ TEST(IOAddressTest, Family) {
     EXPECT_EQ(AF_INET, IOAddress("192.0.2.1").getFamily());
     EXPECT_EQ(AF_INET6, IOAddress("2001:0DB8:0:0::0012").getFamily());
 }
+
+TEST(IOAddressTest, from_bytes) {
+    // 2001:db8:1::dead:beef
+    char v6[] = {
+        0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0, 0,
+        0, 0, 0, 0, 0xde, 0xad, 0xbe, 0xef };
+
+    char v4[] = { 192, 0 , 2, 3 };
+
+    IOAddress addr("::");
+    EXPECT_NO_THROW({
+        addr = IOAddress::from_bytes(AF_INET6, v6);
+    });
+    EXPECT_EQ("2001:db8:1::dead:beef", addr.toText());
+
+    EXPECT_NO_THROW({
+        addr = IOAddress::from_bytes(AF_INET, v4);
+    });
+    EXPECT_EQ(addr, IOAddress("192.0.2.3"));
+}
diff --git a/src/lib/dhcp/Makefile.am b/src/lib/dhcp/Makefile.am
new file mode 100644
index 0000000..5170c1c
--- /dev/null
+++ b/src/lib/dhcp/Makefile.am
@@ -0,0 +1,35 @@
+SUBDIRS = . tests
+
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/asiolink
+AM_CPPFLAGS += -I$(top_builddir)/src/lib/asiolink
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+
+CLEANFILES = *.gcno *.gcda
+
+lib_LTLIBRARIES = libdhcp.la
+libdhcp_la_SOURCES  =
+libdhcp_la_SOURCES += libdhcp.cc libdhcp.h
+libdhcp_la_SOURCES += option.cc option.h
+libdhcp_la_SOURCES += option6_ia.cc option6_ia.h
+libdhcp_la_SOURCES += option6_iaaddr.cc option6_iaaddr.h
+libdhcp_la_SOURCES += option6_addrlst.cc option6_addrlst.h
+libdhcp_la_SOURCES += dhcp6.h
+libdhcp_la_SOURCES += pkt6.cc pkt6.h
+
+EXTRA_DIST  = README
+#EXTRA_DIST += log_messages.mes
+
+# Note: the ordering matters: -Wno-... must follow -Wextra (defined in
+# B10_CXXFLAGS)
+libdhcp_la_CXXFLAGS = $(AM_CXXFLAGS)
+if USE_GXX
+libdhcp_la_CXXFLAGS += -Wall
+endif
+if USE_CLANGPP
+# Same for clang++, but we need to turn off -Werror completely.
+libdhcp_la_CXXFLAGS += -Wall
+endif
+libdhcp_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+libdhcp_la_LDFLAGS  = $(LOG4CPLUS_LDFLAGS)
+libdhcp_la_LIBADD   = $(top_builddir)/src/lib/util/libutil.la
diff --git a/src/lib/dhcp/README b/src/lib/dhcp/README
new file mode 100644
index 0000000..6c5353d
--- /dev/null
+++ b/src/lib/dhcp/README
@@ -0,0 +1,11 @@
+This directory holds implementation for libdhcp.
+
+
+Basic Ideas
+===========
+
+
+Notes
+=====
+This work just begun. Don't expect to see much useful code here.
+We are working on it.
\ No newline at end of file
diff --git a/src/lib/dhcp/dhcp6.h b/src/lib/dhcp/dhcp6.h
new file mode 100644
index 0000000..b3ee0bf
--- /dev/null
+++ b/src/lib/dhcp/dhcp6.h
@@ -0,0 +1,184 @@
+// Copyright (C) 2006-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 DHCP6_H
+#define DHCP6_H
+
+/* DHCPv6 Option codes: */
+
+#define D6O_CLIENTID                            1 /* RFC3315 */
+#define D6O_SERVERID                            2
+#define D6O_IA_NA                               3
+#define D6O_IA_TA                               4
+#define D6O_IAADDR                              5
+#define D6O_ORO                                         6
+#define D6O_PREFERENCE                          7
+#define D6O_ELAPSED_TIME                        8
+#define D6O_RELAY_MSG                           9
+/* Option code 10 unassigned. */
+#define D6O_AUTH                                11
+#define D6O_UNICAST                             12
+#define D6O_STATUS_CODE                                 13
+#define D6O_RAPID_COMMIT                        14
+#define D6O_USER_CLASS                          15
+#define D6O_VENDOR_CLASS                        16
+#define D6O_VENDOR_OPTS                                 17
+#define D6O_INTERFACE_ID                        18
+#define D6O_RECONF_MSG                          19
+#define D6O_RECONF_ACCEPT                       20
+#define D6O_SIP_SERVERS_DNS                     21 /* RFC3319 */
+#define D6O_SIP_SERVERS_ADDR                    22 /* RFC3319 */
+#define D6O_NAME_SERVERS                        23 /* RFC3646 */
+#define D6O_DOMAIN_SEARCH                       24 /* RFC3646 */
+#define D6O_IA_PD                               25 /* RFC3633 */
+#define D6O_IAPREFIX                            26 /* RFC3633 */
+#define D6O_NIS_SERVERS                                 27 /* RFC3898 */
+#define D6O_NISP_SERVERS                        28 /* RFC3898 */
+#define D6O_NIS_DOMAIN_NAME                     29 /* RFC3898 */
+#define D6O_NISP_DOMAIN_NAME                    30 /* RFC3898 */
+#define D6O_SNTP_SERVERS                        31 /* RFC4075 */
+#define D6O_INFORMATION_REFRESH_TIME            32 /* RFC4242 */
+#define D6O_BCMCS_SERVER_D                      33 /* RFC4280 */
+#define D6O_BCMCS_SERVER_A                      34 /* RFC4280 */
+/* 35 is unassigned */
+#define D6O_GEOCONF_CIVIC                       36 /* RFC4776 */
+#define D6O_REMOTE_ID                           37 /* RFC4649 */
+#define D6O_SUBSCRIBER_ID                       38 /* RFC4580 */
+#define D6O_CLIENT_FQDN                                 39 /* RFC4704 */
+#define D6O_PANA_AGENT                          40 /* paa-option */
+#define D6O_NEW_POSIX_TIMEZONE                  41 /* RFC4833 */
+#define D6O_NEW_TZDB_TIMEZONE                   42 /* RFC4833 */
+#define D6O_ERO                                         43 /* RFC4994 */
+#define D6O_LQ_QUERY                            44 /* RFC5007 */
+#define D6O_CLIENT_DATA                                 45 /* RFC5007 */
+#define D6O_CLT_TIME                            46 /* RFC5007 */
+#define D6O_LQ_RELAY_DATA                       47 /* RFC5007 */
+#define D6O_LQ_CLIENT_LINK                      48 /* RFC5007 */
+
+/*
+ * Status Codes, from RFC 3315 section 24.4, and RFC 3633, 5007.
+ */
+#define STATUS_Success           0
+#define STATUS_UnspecFail        1
+#define STATUS_NoAddrsAvail      2
+#define STATUS_NoBinding         3
+#define STATUS_NotOnLink         4
+#define STATUS_UseMulticast      5
+#define STATUS_NoPrefixAvail     6
+#define STATUS_UnknownQueryType          7
+#define STATUS_MalformedQuery    8
+#define STATUS_NotConfigured     9
+#define STATUS_NotAllowed       10
+
+/*
+ * DHCPv6 message types, defined in section 5.3 of RFC 3315
+ */
+#define DHCPV6_SOLICIT              1
+#define DHCPV6_ADVERTISE            2
+#define DHCPV6_REQUEST              3
+#define DHCPV6_CONFIRM              4
+#define DHCPV6_RENEW                5
+#define DHCPV6_REBIND               6
+#define DHCPV6_REPLY                7
+#define DHCPV6_RELEASE              8
+#define DHCPV6_DECLINE              9
+#define DHCPV6_RECONFIGURE         10
+#define DHCPV6_INFORMATION_REQUEST 11
+#define DHCPV6_RELAY_FORW          12
+#define DHCPV6_RELAY_REPL          13
+#define DHCPV6_LEASEQUERY          14
+#define DHCPV6_LEASEQUERY_REPLY    15
+
+extern const char *dhcpv6_type_names[];
+extern const int dhcpv6_type_name_max;
+
+/* DUID type definitions (RFC3315 section 9).
+ */
+#define DUID_LLT        1
+#define DUID_EN                 2
+#define DUID_LL                 3
+
+/* Offsets into IA_*'s where Option spaces commence.  */
+#define IA_NA_OFFSET 12 /* IAID, T1, T2, all 4 octets each */
+#define IA_TA_OFFSET  4 /* IAID only, 4 octets */
+#define IA_PD_OFFSET 12 /* IAID, T1, T2, all 4 octets each */
+
+/* Offset into IAADDR's where Option spaces commence. */
+#define IAADDR_OFFSET 24
+
+/* Offset into IAPREFIX's where Option spaces commence. */
+#define IAPREFIX_OFFSET 25
+
+/* Offset into LQ_QUERY's where Option spaces commence. */
+#define LQ_QUERY_OFFSET 17
+
+/*
+ * DHCPv6 well-known multicast addressess, from section 5.1 of RFC 3315
+ */
+#define ALL_DHCP_RELAY_AGENTS_AND_SERVERS "ff02::1:2"
+#define ALL_DHCP_SERVERS "ff05::1:3"
+
+#define DHCP6_CLIENT_PORT 546
+#define DHCP6_SERVER_PORT 547
+
+/*
+ * DHCPv6 Retransmission Constants (RFC3315 section 5.5, RFC 5007)
+ */
+
+#define SOL_MAX_DELAY     1
+#define SOL_TIMEOUT       1
+#define SOL_MAX_RT      120
+#define REQ_TIMEOUT       1
+#define REQ_MAX_RT       30
+#define REQ_MAX_RC       10
+#define CNF_MAX_DELAY     1
+#define CNF_TIMEOUT       1
+#define CNF_MAX_RT        4
+#define CNF_MAX_RD       10
+#define REN_TIMEOUT      10
+#define REN_MAX_RT      600
+#define REB_TIMEOUT      10
+#define REB_MAX_RT      600
+#define INF_MAX_DELAY     1
+#define INF_TIMEOUT       1
+#define INF_MAX_RT      120
+#define REL_TIMEOUT       1
+#define REL_MAX_RC        5
+#define DEC_TIMEOUT       1
+#define DEC_MAX_RC        5
+#define REC_TIMEOUT       2
+#define REC_MAX_RC        8
+#define HOP_COUNT_LIMIT  32
+#define LQ6_TIMEOUT       1
+#define LQ6_MAX_RT       10
+#define LQ6_MAX_RC        5
+
+/* Leasequery query-types (RFC 5007) */
+
+#define LQ6QT_BY_ADDRESS        1
+#define LQ6QT_BY_CLIENTID       2
+
+/*
+ * DUID time starts 2000-01-01.
+ * This constant is the number of seconds since 1970-01-01,
+ * when the Unix epoch began.
+ */
+#define DUID_TIME_EPOCH 946684800
+
+/* Information-Request Time option (RFC 4242) */
+
+#define IRT_DEFAULT     86400
+#define IRT_MINIMUM     600
+
+#endif
diff --git a/src/lib/dhcp/libdhcp.cc b/src/lib/dhcp/libdhcp.cc
new file mode 100644
index 0000000..3685bc0
--- /dev/null
+++ b/src/lib/dhcp/libdhcp.cc
@@ -0,0 +1,138 @@
+// 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 <config.h>
+
+#include <boost/shared_array.hpp>
+#include <boost/shared_ptr.hpp>
+#include <dhcp/libdhcp.h>
+#include <dhcp/dhcp6.h>
+
+#include <dhcp/option.h>
+#include <dhcp/option6_ia.h>
+#include <dhcp/option6_iaaddr.h>
+
+using namespace std;
+using namespace isc::dhcp;
+
+// static array with factories for options
+std::map<unsigned short, Option::Factory*> LibDHCP::v6factories_;
+
+std::string
+LibDHCP::version() {
+    return PACKAGE_VERSION;
+}
+
+unsigned int
+LibDHCP::unpackOptions6(boost::shared_array<char> buf, unsigned int buf_len,
+                        unsigned int offset, unsigned int parse_len,
+                        isc::dhcp::Option::Option6Lst& options) {
+    if (offset + parse_len > buf_len) {
+        isc_throw(OutOfRange, "Option parse failed. Tried to parse "
+                  << parse_len << " bytes at offset " << offset
+                  << ":  out of buffer");
+    }
+    unsigned int end = offset + parse_len;
+
+    while (offset<end) {
+        unsigned int opt_type = static_cast<unsigned char>(buf[offset])*256
+            + static_cast<unsigned char>(buf[offset+1]);
+        offset += 2;
+        unsigned int opt_len = static_cast<unsigned char>(buf[offset]*256)
+            + static_cast<unsigned char>(buf[offset+1]);
+        offset += 2;
+
+        if (offset + opt_len > end ) {
+            cout << "Option " << opt_type << " truncated." << endl;
+            return (offset);
+        }
+        boost::shared_ptr<Option> opt;
+        switch (opt_type) {
+        case D6O_IA_NA:
+        case D6O_IA_PD:
+            // cout << "Creating Option6IA" << endl;
+            opt = boost::shared_ptr<Option>(new Option6IA(Option::V6,
+                                                          opt_type,
+                                                          buf, buf_len,
+                                                          offset,
+                                                          opt_len));
+            break;
+        case D6O_IAADDR:
+            // cout << "Creating Option6IAAddr" << endl;
+            opt = boost::shared_ptr<Option>(new Option6IAAddr(opt_type,
+                                                              buf, buf_len,
+                                                              offset, opt_len));
+            break;
+        default:
+            // cout << "Creating Option" << endl;
+            opt = boost::shared_ptr<Option>(new Option(Option::V6,
+                                                       opt_type,
+                                                       buf,
+                                                       offset,
+                                                       opt_len));
+            break;
+        }
+        // add option to options
+        options.insert(pair<int, boost::shared_ptr<Option> >(opt_type, opt));
+        offset += opt_len;
+    }
+
+    return (offset);
+}
+
+unsigned int
+LibDHCP::packOptions6(boost::shared_array<char> data,
+                      unsigned int data_len,
+                      unsigned int offset,
+                      isc::dhcp::Option::Option6Lst& options) {
+    try {
+        for (isc::dhcp::Option::Option6Lst::iterator it = options.begin();
+             it != options.end();
+             ++it) {
+            unsigned short opt_len = (*it).second->len();
+            if (offset + opt_len > data_len) {
+                isc_throw(OutOfRange, "Failed to build option " <<
+                          (*it).first << ": out of buffer");
+            }
+            offset = (*it).second->pack(data, data_len, offset);
+        }
+    }
+    catch (Exception e) {
+        cout << "Packet build failed." << endl;
+        return (-1);
+    }
+    return (offset);
+}
+
+bool
+LibDHCP::OptionFactoryRegister(Option::Universe u,
+                               unsigned short opt_type,
+                               Option::Factory * factory) {
+    switch (u) {
+    case Option::V6: {
+        if (v6factories_.find(opt_type)!=v6factories_.end()) {
+            isc_throw(BadValue, "There is already DHCPv6 factory registered "
+                     << "for option type "  << opt_type);
+        }
+        v6factories_[opt_type]=factory;
+        return true;
+    }
+    case Option::V4:
+    default:{
+        isc_throw(BadValue, "This universe type is not supported yet.");
+        return false; // never happens
+    }
+    }
+
+}
diff --git a/src/lib/dhcp/libdhcp.h b/src/lib/dhcp/libdhcp.h
new file mode 100644
index 0000000..edc2d6c
--- /dev/null
+++ b/src/lib/dhcp/libdhcp.h
@@ -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.
+
+#ifndef LIBDHCP_H_
+#define LIBDHCP_H_
+
+#include <iostream>
+#include <dhcp/pkt6.h>
+
+namespace isc {
+namespace dhcp {
+
+class LibDHCP {
+
+public:
+    /// Returns version of the library.
+    ///
+    /// @return string that contains libdhcp version.
+    ///
+    static std::string
+    version();
+
+    /// Builds collection of options.
+    ///
+    /// Builds raw (on-wire) data for provided collection of options.
+    ///
+    /// @param buf shared pointer to buffer. Data will be stored there.
+    /// @param buf_len buffer length. Used for buffer overflow protection.
+    /// @param offset Offset from beginning of the buffer, where store options
+    /// @param options collection of options to store to
+    ///
+    /// @return offset to the first unused byte in buffer (next one after last
+    ///         used byte)
+    ///
+    static unsigned int
+    packOptions6(boost::shared_array<char> buf, unsigned int buf_len,
+                 unsigned int offset,
+                 isc::dhcp::Option::Option6Lst& options);
+
+    ///
+    /// Parses provided buffer and creates Option objects.
+    ///
+    /// Parses provided buf array and stores created Option objects
+    /// in options container.
+    ///
+    /// @param buf Buffer to be parsed.
+    /// @param offset Specifies offset for the first option.
+    /// @param options Reference to option container. Options will be
+    ///        put here.
+    ///
+    /// @return offset to first byte after last parsed option
+    ///
+    static unsigned int
+    unpackOptions6(boost::shared_array<char> buf, unsigned int buf_len,
+                   unsigned int offset, unsigned int parse_len,
+                   isc::dhcp::Option::Option6Lst& options_);
+
+    ///
+    /// Registers factory method that produces options of specific option types.
+    ///
+    /// @param u universe of the option (V4 or V6)
+    /// @param opt_type option-type
+    /// @param factory function pointer
+    ///
+    /// @return true, if registration was successful, false otherwise
+    ///
+    static bool
+    OptionFactoryRegister(Option::Universe u,
+                          unsigned short type,
+                          Option::Factory * factory);
+protected:
+    // pointers to factories that produce DHCPv6 options
+    static std::map<unsigned short, Option::Factory*> v6factories_;
+};
+
+}
+}
+
+#endif
diff --git a/src/lib/dhcp/option.cc b/src/lib/dhcp/option.cc
new file mode 100644
index 0000000..6a52055
--- /dev/null
+++ b/src/lib/dhcp/option.cc
@@ -0,0 +1,256 @@
+// 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.h>
+#include <stdint.h>
+#ifdef _WIN32
+#include <ws2tcpip.h>
+#else
+#include <arpa/inet.h>
+#endif
+#include <sstream>
+#include <iomanip>
+#include <boost/shared_array.hpp>
+#include <exceptions/exceptions.h>
+
+#include <dhcp/option.h>
+#include <dhcp/libdhcp.h>
+
+using namespace std;
+using namespace isc::dhcp;
+
+Option::Option(Universe u, unsigned short type)
+    :universe_(u), type_(type), data_len_(0) {
+
+
+}
+
+Option::Option(Universe u, unsigned short type, boost::shared_array<char> buf,
+               unsigned int offset, unsigned int len)
+    :universe_(u), type_(type), data_(buf),
+     data_len_(len), offset_(offset)
+      {
+
+    // sanity checks
+    // TODO: universe must be in V4 and V6
+}
+
+unsigned int
+Option::pack(boost::shared_array<char> buf,
+             unsigned int buf_len,
+             unsigned int offset) {
+    switch (universe_) {
+    case V4:
+        return pack4(buf, buf_len, offset);
+    case V6:
+        return pack6(buf, buf_len, offset);
+    default:
+        isc_throw(BadValue, "Unknown universe defined for Option " << type_);
+    }
+}
+
+
+unsigned int
+Option::pack4(boost::shared_array<char> buf,
+             unsigned int buf_len,
+             unsigned int offset) {
+    if ( offset+len() > buf_len ) {
+        isc_throw(OutOfRange, "Failed to pack v4 option=" <<
+                  type_ << ",len=" << data_len_ << ": too small buffer.");
+    }
+    char *ptr = &buf[offset];
+    ptr[0] = type_;
+    ptr[1] = data_len_;
+    ptr += 2;
+    memcpy(ptr, &data_[0], data_len_+4);
+
+    return offset + len();
+}
+
+unsigned int
+Option::pack6(boost::shared_array<char> buf,
+             unsigned int buf_len,
+             unsigned int offset) {
+    if ( offset+len() > buf_len ) {
+        isc_throw(OutOfRange, "Failed to pack v6 option=" <<
+                  type_ << ",len=" << len() << ": too small buffer.");
+    }
+
+    int length = len() - getHeaderLen();
+
+    char * ptr = &buf[offset];
+    *(uint16_t*)ptr = htons(type_);
+    ptr += 2;
+    *(uint16_t*)ptr = htons(length);
+    ptr += 2;
+    if (data_len_)
+        memcpy(ptr, &data_[offset_], data_len_);
+
+    offset += 4 + data_len_; // end of this option
+
+    return LibDHCP::packOptions6(buf, buf_len, offset, optionLst_);
+}
+
+unsigned int
+Option::unpack(boost::shared_array<char> buf,
+               unsigned int buf_len,
+               unsigned int offset,
+               unsigned int parse_len) {
+    switch (universe_) {
+    case V4:
+        return unpack4(buf, buf_len, offset, parse_len);
+    case V6:
+        return unpack6(buf, buf_len, offset, parse_len);
+    default:
+        isc_throw(BadValue, "Unknown universe defined for Option " << type_);
+    }
+
+    return 0; // should not happen
+}
+
+unsigned int
+Option::unpack4(boost::shared_array<char>,
+                unsigned int ,
+                unsigned int ,
+                unsigned int ) {
+    isc_throw(Unexpected, "IPv4 support not implemented yet.");
+    return 0;
+}
+
+unsigned int
+Option::unpack6(boost::shared_array<char> buf,
+                unsigned int buf_len,
+                unsigned int offset,
+                unsigned int parse_len) {
+
+    if (buf_len < offset+parse_len) {
+        isc_throw(OutOfRange, "Failed to unpack DHCPv6 option len="
+                  << parse_len << " offset=" << offset
+                  << " from buffer (length=" << buf_len
+                  << "): too small buffer.");
+    }
+
+    data_ = buf;
+    offset_ = offset;
+    data_len_ = buf_len;
+
+    return LibDHCP::unpackOptions6(buf, buf_len, offset, parse_len,
+                                   optionLst_);
+}
+
+unsigned short Option::len() {
+    int length = getHeaderLen() + data_len_;
+
+    for (Option::Option6Lst::iterator it = optionLst_.begin();
+         it != optionLst_.end();
+         ++it) {
+        length += (*it).second->len();
+    }
+
+    return (length);
+}
+
+bool Option::valid() {
+    // total length of buffer is not stored. shared_array is not very useful.
+    // we should either add buf_len field or better replace shared_array
+    // with shared_ptr to array
+    if (universe_ != V4 &&
+        universe_ != V6) {
+        return (false);
+    }
+
+    return (true);
+}
+
+void
+isc::dhcp::Option::addOption(boost::shared_ptr<isc::dhcp::Option> opt) {
+    optionLst_.insert(pair<int, boost::shared_ptr<Option> >(opt->getType(),
+                                                            opt));
+
+}
+
+boost::shared_ptr<isc::dhcp::Option>
+Option::getOption(unsigned short opt_type) {
+    isc::dhcp::Option::Option6Lst::const_iterator x = optionLst_.find(opt_type);
+    if (x!=optionLst_.end()) {
+        return (*x).second;
+    }
+    return boost::shared_ptr<isc::dhcp::Option>(); // NULL
+}
+
+bool
+Option::delOption(unsigned short opt_type) {
+    isc::dhcp::Option::Option6Lst::iterator x = optionLst_.find(opt_type);
+    if (x!=optionLst_.end()) {
+        optionLst_.erase(x);
+        return true; // delete successful
+    }
+    return (false); // option not found, can't delete
+}
+
+
+std::string Option::toText(int indent /* =0 */ ) {
+    std::stringstream tmp;
+
+    for (int i=0; i<indent; i++)
+        tmp << " ";
+
+    tmp << "type=" << type_ << ", len=" << data_len_ << ":";
+
+    for (unsigned int i=0; i<data_len_; i++) {
+        if (i) {
+            tmp << ":";
+        }
+        tmp << setfill('0') << setw(2) << hex
+            << (unsigned short)(unsigned char)data_[offset_+i];
+    }
+
+    // print suboptions
+    for (Option6Lst::const_iterator opt=optionLst_.begin();
+         opt!=optionLst_.end();
+         ++opt) {
+        tmp << (*opt).second->toText(indent+2);
+    }
+    return tmp.str();
+}
+
+unsigned short
+Option::getType() {
+    return type_;
+}
+
+char*
+Option::getData() {
+    if (data_len_) {
+        return (&data_[offset_]);
+    } else {
+        return (NULL);
+    }
+}
+
+unsigned short
+Option::getHeaderLen() {
+    switch (universe_) {
+    case V4:
+        return 2; // header length for v4
+    case V6:
+        return 4; // header length for v6
+    }
+    return 0; // should not happen
+}
+
+
+Option::~Option() {
+
+}
diff --git a/src/lib/dhcp/option.h b/src/lib/dhcp/option.h
new file mode 100644
index 0000000..65f1ba8
--- /dev/null
+++ b/src/lib/dhcp/option.h
@@ -0,0 +1,230 @@
+// 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 OPTION_H_
+#define OPTION_H_
+
+#include <string>
+#include <map>
+#include <boost/shared_array.hpp>
+
+namespace isc {
+namespace dhcp {
+
+class Option {
+public:
+    enum Universe { V4, V6 };
+    typedef std::map<unsigned int, boost::shared_ptr<Option> > Option4Lst;
+    typedef std::multimap<unsigned int, boost::shared_ptr<Option> > Option6Lst;
+    typedef boost::shared_ptr<Option> Factory(Option::Universe u,
+                                              unsigned short type,
+                                              boost::shared_array<char> buf,
+                                              unsigned int offset,
+                                              unsigned int len);
+
+    // ctor, used for options constructed, usually during transmission
+    Option(Universe u, unsigned short type);
+
+    // ctor, used for received options
+    // boost::shared_array allows sharing a buffer, but it requires that
+    // different instances share pointer to the whole array, not point
+    // to different elements in shared array. Therefore we need to share
+    // pointer to the whole array and remember offset where data for
+    // this option begins
+    Option(Universe u, unsigned short type, boost::shared_array<char> buf,
+           unsigned int offset,
+           unsigned int len);
+
+    // writes option in wire-format to buf, returns pointer to first unused
+    // byte after stored option
+    virtual unsigned int
+    pack(boost::shared_array<char> buf,
+         unsigned int buf_len,
+         unsigned int offset);
+
+    ///
+    /// @brief Parses buffer.
+    ///
+    /// Parses received buffer, returns offset to the first unused byte after
+    /// parsed option.
+    ///
+    /// @param buf pointer to buffer
+    /// @param buf_len length of buf
+    /// @param offset offset, where start parsing option
+    /// @param parse_len how many bytes should be parsed
+    ///
+    /// @return offset after last parsed option
+    ///
+    virtual unsigned int
+    unpack(boost::shared_array<char> buf,
+           unsigned int buf_len,
+           unsigned int offset,
+           unsigned int parse_len);
+
+    ///
+    /// Returns string representation of the option.
+    ///
+    /// @param indent number of spaces before printing text
+    ///
+    /// @return string with text representation.
+    ///
+    virtual std::string
+    toText(int indent = 0);
+
+    ///
+    /// Returns option type (0-255 for DHCPv4, 0-65535 for DHCPv6)
+    ///
+    /// @return option type
+    ///
+    unsigned short
+    getType();
+
+    /// Returns length of the complete option (data length + DHCPv4/DHCPv6
+    /// option header)
+    ///
+    /// @return length of the option
+    ///
+    virtual unsigned short
+    len();
+
+    /// @brief Returns length of header (2 for v4, 4 for v6)
+    ///
+    /// @return length of option header
+    ///
+    virtual unsigned short
+    getHeaderLen();
+
+    /// returns if option is valid (e.g. option may be truncated)
+    virtual bool
+    valid();
+
+    /// Returns pointer to actual data.
+    ///
+    /// @return pointer to actual data (or NULL if there is no data)
+    ///
+    virtual char*
+    getData();
+
+    /// Adds a sub-option.
+    ///
+    /// @param opt shared pointer to a suboption that is going to be added.
+    ///
+    void
+    addOption(boost::shared_ptr<Option> opt);
+
+    /// Returns shared_ptr to suboption of specific type
+    ///
+    /// @param type type of requested suboption
+    ///
+    /// @return shared_ptr to requested suoption
+    ///
+    boost::shared_ptr<isc::dhcp::Option>
+    getOption(unsigned short type);
+
+    /// Attempts to delete first suboption of requested type
+    ///
+    /// @param type Type of option to be deleted.
+    ///
+    /// @return true if option was deleted, false if no such option existed
+    ///
+    bool
+    delOption(unsigned short type);
+
+    /// TODO Need to implement getOptions() as well
+
+    // just to force that every option has virtual dtor
+    virtual
+    ~Option();
+
+protected:
+
+    ///
+    /// Builds raw (over-wire) buffer of this option, including all
+    /// defined suboptions. Version for building DHCPv4 options.
+    ///
+    /// @param buf output buffer (built options will be stored here)
+    /// @param buf_len buffer length (used for buffer overflow checks)
+    /// @param offset offset from start of the buf buffer
+    ///
+    /// @return offset to the next byte after last used byte
+    ///
+    virtual unsigned int
+    pack4(boost::shared_array<char> buf,
+          unsigned int buf_len,
+          unsigned int offset);
+
+    ///
+    /// Builds raw (over-wire) buffer of this option, including all
+    /// defined suboptions. Version for building DHCPv4 options.
+    ///
+    /// @param buf output buffer (built options will be stored here)
+    /// @param buf_len buffer length (used for buffer overflow checks)
+    /// @param offset offset from start of the buf buffer
+    ///
+    /// @return offset to the next byte after last used byte
+    ///
+    virtual unsigned int
+    pack6(boost::shared_array<char> buf,
+          unsigned int buf_len,
+          unsigned int offset);
+
+    ///
+    /// Parses provided buffer and creates DHCPv4 options.
+    ///
+    /// @param buf buffer that contains raw buffer to parse (on-wire format)
+    /// @param buf_len buffer length (used for buffer overflow checks)
+    /// @param offset offset from start of the buf buffer
+    ///
+    /// @return offset to the next byte after last parsed byte
+    ///
+    virtual unsigned int
+    unpack4(boost::shared_array<char> buf,
+            unsigned int buf_len,
+            unsigned int offset,
+            unsigned int parse_len);
+
+    ///
+    /// Parses provided buffer and creates DHCPv6 options.
+    ///
+    /// @param buf buffer that contains raw buffer to parse (on-wire format)
+    /// @param buf_len buffer length (used for buffer overflow checks)
+    /// @param offset offset from start of the buf buffer
+    ///
+    /// @return offset to the next byte after last parsed byte
+    ///
+    virtual unsigned int
+    unpack6(boost::shared_array<char> buf,
+            unsigned int buf_len,
+            unsigned int offset,
+            unsigned int parse_len);
+
+    Universe universe_; // option universe (V4 or V6)
+    unsigned short type_; // option type (0-255 for DHCPv4, 0-65535 for DHCPv6)
+
+    boost::shared_array<char> data_;
+    unsigned int data_len_; // length of data only. Use len() if you want to
+                            // know proper length with option header overhead
+    unsigned int offset_; // data is a shared_pointer that points out to the
+                          // whole packet. offset_ specifies where data for
+                          // this option begins.
+
+    // TODO: probably 2 different containers have to be used for v4 (unique
+    // options) and v6 (options with the same type can repeat)
+    Option6Lst optionLst_;
+};
+
+} // namespace isc::dhcp
+} // namespace isc
+
+#endif
diff --git a/src/lib/dhcp/option6_addrlst.cc b/src/lib/dhcp/option6_addrlst.cc
new file mode 100644
index 0000000..a4d7e3c
--- /dev/null
+++ b/src/lib/dhcp/option6_addrlst.cc
@@ -0,0 +1,136 @@
+// 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>
+#ifdef _WIN32
+#include <ws2tcpip.h>
+#else
+#include <arpa/inet.h>
+#endif
+#include <sstream>
+#include <exceptions/exceptions.h>
+
+#include <dhcp/libdhcp.h>
+#include <dhcp/option6_addrlst.h>
+#include <dhcp/dhcp6.h>
+#include <asiolink/io_address.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::asiolink;
+
+
+Option6AddrLst::Option6AddrLst(unsigned short type,
+                               std::vector<isc::asiolink::IOAddress>& addrs)
+    :Option(V6, type) {
+    addrs_ = addrs;
+}
+
+Option6AddrLst::Option6AddrLst(unsigned short type,
+                               isc::asiolink::IOAddress addr)
+    :Option(V6, type) {
+    addrs_.push_back(addr);
+}
+
+Option6AddrLst::Option6AddrLst(unsigned short type,
+                               boost::shared_array<char> buf,
+                               unsigned int buf_len,
+                               unsigned int offset,
+                               unsigned int option_len)
+    :Option(V6, type) {
+    unpack(buf, buf_len, offset, option_len);
+}
+
+void
+Option6AddrLst::setAddress(isc::asiolink::IOAddress addr) {
+    addrs_.clear();
+    addrs_.push_back(addr);
+}
+
+void
+Option6AddrLst::setAddresses(std::vector<isc::asiolink::IOAddress>& addrs) {
+    addrs_ = addrs;
+}
+
+unsigned int
+Option6AddrLst::pack(boost::shared_array<char> buf,
+                    unsigned int buf_len,
+                    unsigned int offset) {
+    if (len() > buf_len) {
+        isc_throw(OutOfRange, "Failed to pack IA option: len=" << len()
+                  << ", buffer=" << buf_len << ": too small buffer.");
+    }
+
+    *(uint16_t*)&buf[offset] = htons(type_);
+    offset += 2;
+    *(uint16_t*)&buf[offset] = htons(len()-4); // len() returns complete option
+    // length. len field contains length without 4-byte option header
+    offset += 2;
+
+    for (std::vector<IOAddress>::const_iterator addr=addrs_.begin();
+         addr!=addrs_.end();
+         ++addr) {
+        memcpy(&buf[offset],
+               addr->getAddress().to_v6().to_bytes().data(),
+               16);
+        offset += 16;
+    }
+
+    return offset;
+}
+
+unsigned int
+Option6AddrLst::unpack(boost::shared_array<char> buf,
+                  unsigned int buf_len,
+                  unsigned int offset,
+                  unsigned int option_len) {
+    if (offset+option_len > buf_len) {
+        isc_throw(OutOfRange, "Option " << type_
+                  << " truncated.");
+    }
+
+    if (option_len%16) {
+        isc_throw(OutOfRange, "Option " << type_
+                  << " malformed: len=" << option_len
+                  << " is not divisible by 16.");
+    }
+    while (option_len > 0) {
+        addrs_.push_back(IOAddress::from_bytes(AF_INET6, &buf[offset]));
+        offset += 16;
+        option_len -= 16;
+    }
+
+    return offset;
+}
+
+std::string Option6AddrLst::toText(int indent /* =0 */) {
+    stringstream tmp;
+    for (int i=0; i<indent; i++)
+        tmp << " ";
+
+    tmp << "type=" << type_ << " " << addrs_.size() << "addr(s): ";
+
+    for (AddrsContainer::const_iterator addr=addrs_.begin();
+         addr!=addrs_.end();
+         ++addr) {
+        tmp << addr->toText() << " ";
+    }
+    return tmp.str();
+}
+
+unsigned short Option6AddrLst::len() {
+
+    return (4 /* DHCPv6 option header len */ + addrs_.size()*16);
+}
diff --git a/src/lib/dhcp/option6_addrlst.h b/src/lib/dhcp/option6_addrlst.h
new file mode 100644
index 0000000..f17e28d
--- /dev/null
+++ b/src/lib/dhcp/option6_addrlst.h
@@ -0,0 +1,127 @@
+// 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 OPTION6_ADDRLST_H_
+#define OPTION6_ADDRLST_H_
+
+#include <vector>
+#include <asiolink/io_address.h>
+#include <dhcp/option.h>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Option class for handling list of IPv6 addresses.
+///
+/// This class handles a list of IPv6 addresses. An example of such option
+/// is dns-servers option. It can also be used to handle single address.
+///
+class Option6AddrLst: public Option {
+
+
+public:
+    typedef std::vector<isc::asiolink::IOAddress> AddrsContainer;
+
+    /// @brief Constructor used during option generation.
+    ///
+    /// @param type option type
+    /// @param addrs vector of addresses to be stored
+    ///
+    Option6AddrLst(unsigned short type,
+                   AddrsContainer& addrs);
+
+    /// @brief Simplified constructor for a single address
+    ///
+    /// @param type option type
+    /// @param addr a single address to be stored
+    ///
+    Option6AddrLst(unsigned short type,
+                   isc::asiolink::IOAddress addr);
+
+    /// @brief Constructor used for parsing received option
+    ///
+    /// @param type option type
+    /// @param buf pointer to packet buffer
+    /// @param buf_len length of packet buffer
+    /// @param offset offset to beginning of option data
+    /// @param len length of option data
+    ///
+    Option6AddrLst(unsigned short type, boost::shared_array<char> buf,
+                   unsigned int buf_len,
+                   unsigned int offset,
+                   unsigned int len);
+
+    /// @brief Assembles on-wire form of this option
+    ///
+    /// @param buf pointer to packet buffer
+    /// @param buf_len length of packet buffer
+    /// @param offset offset to place, where option is to be stored
+    ///
+    /// @return offset to the next unused char (just after stored option)
+    ///
+    unsigned int
+    pack(boost::shared_array<char> buf, unsigned int buf_len,
+         unsigned int offset);
+
+    /// @brief Parses received data
+    ///
+    /// @param buf pointer to packet buffer
+    /// @param buf_len length of packet buffer
+    /// @param offset offset to option data
+    /// @param parse_len specified option data length
+    ///
+    /// @return offset to the next unparsed char (just after parsed option)
+    ///
+    virtual unsigned int
+    unpack(boost::shared_array<char> buf,
+           unsigned int buf_len,
+           unsigned int offset,
+           unsigned int parse_len);
+
+    virtual std::string toText(int indent = 0);
+
+    /// @brief Sets a single address.
+    ///
+    /// @param addr a single address to be added
+    ///
+    void setAddress(isc::asiolink::IOAddress addr);
+
+    /// @brief Sets list of addresses.
+    ///
+    /// @param addrs a vector of addresses to be added
+    ///
+    void setAddresses(std::vector<isc::asiolink::IOAddress>& addrs);
+
+    /// @brief Returns vector with addresses.
+    ///
+    /// As user may want to use/modify this list, it is better to return
+    /// a copy rather than const reference to the original. This is
+    /// usually one or two addresses long, so it is not a big deal.
+    ///
+    /// @return vector with addresses
+    ///
+    AddrsContainer
+    getAddresses() { return addrs_; };
+
+    // returns data length (data length + DHCPv4/DHCPv6 option header)
+    virtual unsigned short len();
+
+protected:
+    AddrsContainer addrs_;
+};
+
+} // isc::dhcp namespace
+} // isc namespace
+
+#endif /* OPTION_ADDRLST_H_ */
diff --git a/src/lib/dhcp/option6_ia.cc b/src/lib/dhcp/option6_ia.cc
new file mode 100644
index 0000000..8f01191
--- /dev/null
+++ b/src/lib/dhcp/option6_ia.cc
@@ -0,0 +1,136 @@
+// 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>
+#ifdef _WIN32
+#include <ws2tcpip.h>
+#else
+#include <arpa/inet.h>
+#endif
+#include <sstream>
+#include <exceptions/exceptions.h>
+
+#include <dhcp/libdhcp.h>
+#include <dhcp/option6_ia.h>
+#include <dhcp/dhcp6.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+
+Option6IA::Option6IA(Universe u, unsigned short type, unsigned int iaid)
+    :Option(u, type), iaid_(iaid) {
+
+}
+
+
+Option6IA::Option6IA(Universe u, unsigned short type, 
+                   boost::shared_array<char> buf, 
+                   unsigned int buf_len,
+                   unsigned int offset, 
+                   unsigned int option_len)
+    :Option(u, type) {
+    unpack(buf, buf_len, offset, option_len);
+}
+
+unsigned int
+Option6IA::pack(boost::shared_array<char> buf,
+                unsigned int buf_len,
+                unsigned int offset) {
+    if (offset + len() > buf_len) {
+        isc_throw(OutOfRange, "Failed to pack IA option: len=" << len() 
+                  << ", buffer=" << buf_len << ": too small buffer.");
+    }
+    
+    char* ptr = &buf[offset];
+    *(uint16_t*)ptr = htons(type_);
+    ptr += 2;
+    *(uint16_t*)ptr = htons(len() - 4); // len() returns complete option length
+    // len field contains length without 4-byte option header
+    ptr += 2;
+    
+    *(uint32_t*)ptr = htonl(iaid_);
+    ptr += 4;
+
+    *(uint32_t*)ptr = htonl(t1_);
+    ptr += 4;
+
+    *(uint32_t*)ptr = htonl(t2_);
+    ptr += 4;
+
+    offset = LibDHCP::packOptions6(buf, buf_len, offset+16, optionLst_);
+    return offset;
+}
+
+unsigned int 
+Option6IA::unpack(boost::shared_array<char> buf,
+                  unsigned int buf_len,
+                  unsigned int offset, 
+                  unsigned int parse_len) {
+    if (parse_len<12 || offset+12>buf_len) {
+        isc_throw(OutOfRange, "Option " << type_ << " truncated");
+    }
+    iaid_ = ntohl(*(uint32_t*)&buf[offset]);
+    offset +=4;
+    t1_ = ntohl(*(uint32_t*)&buf[offset]);
+    offset +=4;
+    t2_ = ntohl(*(uint32_t*)&buf[offset]);
+    offset +=4;
+    offset = LibDHCP::unpackOptions6(buf, buf_len, offset, 
+                                     parse_len - 12, optionLst_);
+
+    return (offset);
+}
+
+std::string Option6IA::toText(int indent /* = 0*/) {
+    stringstream tmp;
+
+    for (int i=0; i<indent; i++)
+        tmp << " ";
+    tmp << "type=" << type_;
+
+    switch (type_) {
+    case D6O_IA_NA:
+        tmp << "(IA_NA)";
+        break;
+    case D6O_IA_PD:
+        tmp << "(IA_PD)";
+        break;
+    default:
+        tmp << "(unknown)";
+    }
+    tmp << " iaid=" << iaid_ << ", t1=" << t1_ << ", t2=" << t2_
+        << " " << optionLst_.size() << " sub-options:" << endl;
+
+    for (Option6Lst::const_iterator opt=optionLst_.begin();
+         opt!=optionLst_.end();
+         ++opt) {
+        tmp << (*opt).second->toText(indent+2);
+    }
+    return tmp.str();
+}
+
+unsigned short Option6IA::len() {
+    
+    unsigned short length = 4/*header*/ + 12 /* option content */; // header
+
+    // length of all suboptions
+    for (Option::Option6Lst::iterator it = optionLst_.begin();
+         it != optionLst_.end();
+         ++it) {
+        length += (*it).second->len();
+    }
+    return (length);
+}
+
diff --git a/src/lib/dhcp/option6_ia.h b/src/lib/dhcp/option6_ia.h
new file mode 100644
index 0000000..802f39c
--- /dev/null
+++ b/src/lib/dhcp/option6_ia.h
@@ -0,0 +1,87 @@
+// 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 OPTION_IA_H_
+#define OPTION_IA_H_
+
+#include <dhcp/option.h>
+
+namespace isc {
+namespace dhcp {
+
+class Option6IA: public Option {
+
+public:
+    // ctor, used for options constructed, usually during transmission
+    Option6IA(Universe u, unsigned short type, unsigned int iaid);
+
+    // ctor, used for received options
+    // boost::shared_array allows sharing a buffer, but it requires that
+    // different instances share pointer to the whole array, not point
+    // to different elements in shared array. Therefore we need to share
+    // pointer to the whole array and remember offset where data for
+    // this option begins
+    Option6IA(Universe u, unsigned short type, boost::shared_array<char> buf,
+              unsigned int buf_len,
+              unsigned int offset,
+              unsigned int len);
+
+    // writes option in wire-format to buf, returns pointer to first unused
+    // byte after stored option
+    unsigned int
+    pack(boost::shared_array<char> buf, unsigned int buf_len,
+         unsigned int offset);
+
+    // parses received buffer, returns offset to the first unused byte after
+    // parsed option
+    virtual unsigned int
+    unpack(boost::shared_array<char> buf,
+           unsigned int buf_len,
+           unsigned int offset,
+           unsigned int parse_len);
+
+    /// Provides human readable text representation
+    ///
+    /// @param indent number of leading space characterss
+    ///
+    /// @return string with text represenation
+    ///
+    virtual std::string
+    toText(int indent = 0);
+
+    void setT1(unsigned int t1) { t1_=t1; }
+    void setT2(unsigned int t2) { t2_=t2; }
+
+    unsigned int getIAID() { return iaid_; }
+    unsigned int getT1()   { return t1_; }
+    unsigned int getT2()   { return t2_; }
+
+    /// @brief returns complete length of option
+    ///
+    /// Returns length of this option, including option header and suboptions
+    ///
+    /// @return length
+    virtual unsigned short
+    len();
+
+protected:
+    unsigned int iaid_;
+    unsigned int t1_;
+    unsigned int t2_;
+};
+
+} // isc::dhcp namespace
+} // isc namespace
+
+#endif /* OPTION_IA_H_ */
diff --git a/src/lib/dhcp/option6_iaaddr.cc b/src/lib/dhcp/option6_iaaddr.cc
new file mode 100644
index 0000000..0216d5f
--- /dev/null
+++ b/src/lib/dhcp/option6_iaaddr.cc
@@ -0,0 +1,144 @@
+// 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>
+#ifdef _WIN32
+#include <ws2tcpip.h>
+#else
+#include <arpa/inet.h>
+#endif
+#include <sstream>
+#include <exceptions/exceptions.h>
+
+#include <dhcp/libdhcp.h>
+#include <dhcp/option6_iaaddr.h>
+#include <dhcp/dhcp6.h>
+#include <asiolink/io_address.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::asiolink;
+
+Option6IAAddr::Option6IAAddr(unsigned short type,
+                             isc::asiolink::IOAddress addr,
+                             unsigned int pref,
+                             unsigned int valid)
+    :Option(V6, type), addr_(addr), preferred_(pref),
+     valid_(valid) {
+}
+
+Option6IAAddr::Option6IAAddr(unsigned short type,
+                             boost::shared_array<char> buf,
+                             unsigned int buf_len,
+                             unsigned int offset,
+                             unsigned int option_len)
+    :Option(V6, type), addr_(IOAddress("::")) {
+    unpack(buf, buf_len, offset, option_len);
+}
+
+unsigned int
+Option6IAAddr::pack(boost::shared_array<char> buf,
+                    unsigned int buf_len,
+                    unsigned int offset) {
+    if (len() > buf_len) {
+        isc_throw(OutOfRange, "Failed to pack IA option: len=" << len()
+                  << ", buffer=" << buf_len << ": too small buffer.");
+    }
+
+    *(uint16_t*)&buf[offset] = htons(type_);
+    offset += 2;
+    *(uint16_t*)&buf[offset] = htons(len()-4); // len() returns complete option
+    // length. len field contains length without 4-byte option header
+    offset += 2;
+
+    memcpy(&buf[offset], addr_.getAddress().to_v6().to_bytes().data(), 16);
+    offset += 16;
+
+    *(uint32_t*)&buf[offset] = htonl(preferred_);
+    offset += 4;
+    *(uint32_t*)&buf[offset] = htonl(valid_);
+    offset += 4;
+
+    // parse suboption (there shouldn't be any)
+    offset = LibDHCP::packOptions6(buf, buf_len, offset, optionLst_);
+    return offset;
+}
+
+unsigned int
+Option6IAAddr::unpack(boost::shared_array<char> buf,
+                  unsigned int buf_len,
+                  unsigned int offset,
+                  unsigned int parse_len) {
+    if (parse_len<24 || offset+24>buf_len) {
+        isc_throw(OutOfRange, "Option " << type_ << " truncated");
+    }
+
+    // 16 bytes: IPv6 address
+    /// TODO Implement fromBytes() method in IOAddress
+    char addr_str[INET6_ADDRSTRLEN];
+
+#ifdef _WIN32
+#define DECONST (void *)
+#else
+#define DECONST
+#endif
+
+    inet_ntop(AF_INET6, DECONST &buf[offset], addr_str,INET6_ADDRSTRLEN);
+    addr_ = IOAddress(string(addr_str));
+    offset += 16;
+
+    preferred_ = ntohl(*(uint32_t*)&buf[offset]);
+    offset +=4;
+
+    valid_ = ntohl(*(uint32_t*)&buf[offset]);
+    offset +=4;
+    offset = LibDHCP::unpackOptions6(buf, buf_len, offset,
+                                     parse_len - 24, optionLst_);
+
+    return offset;
+}
+
+std::string Option6IAAddr::toText(int indent /* =0 */) {
+    stringstream tmp;
+    for (int i=0; i<indent; i++)
+        tmp << " ";
+
+    tmp << "type=" << type_ << "(IAADDR) addr=" << addr_.toText()
+        << ", preferred-lft=" << preferred_  << ", valid-lft="
+        << valid_ << endl;
+
+    for (Option6Lst::const_iterator opt=optionLst_.begin();
+         opt!=optionLst_.end();
+         ++opt) {
+        tmp << (*opt).second->toText(indent+2);
+    }
+    return tmp.str();
+}
+
+unsigned short Option6IAAddr::len() {
+
+    unsigned short length = 4/*header*/ + 24 /* content */; // header
+
+    // length of all suboptions
+    // TODO implement:
+    // protected: unsigned short Option::lenHelper(int header_size);
+
+    for (Option::Option6Lst::iterator it = optionLst_.begin();
+         it != optionLst_.end();
+         ++it) {
+        length += (*it).second->len();
+    }
+    return (length);
+}
diff --git a/src/lib/dhcp/option6_iaaddr.h b/src/lib/dhcp/option6_iaaddr.h
new file mode 100644
index 0000000..bf2d289
--- /dev/null
+++ b/src/lib/dhcp/option6_iaaddr.h
@@ -0,0 +1,80 @@
+// 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 OPTION6_IAADDR_H_
+#define OPTION6_IAADDR_H_
+
+#include <asiolink/io_address.h>
+#include <dhcp/option.h>
+
+namespace isc {
+namespace dhcp {
+
+class Option6IAAddr: public Option {
+        
+public:
+    // ctor, used for options constructed, usually during transmission
+    Option6IAAddr(unsigned short type, 
+                  isc::asiolink::IOAddress addr,
+                  unsigned int prefered,
+                  unsigned int valid); 
+
+    // ctor, used for received options
+    // boost::shared_array allows sharing a buffer, but it requires that 
+    // different instances share pointer to the whole array, not point
+    // to different elements in shared array. Therefore we need to share
+    // pointer to the whole array and remember offset where data for
+    // this option begins
+    Option6IAAddr(unsigned short type, boost::shared_array<char> buf, 
+                  unsigned int buf_len,
+                  unsigned int offset, 
+                  unsigned int len);
+    
+    // writes option in wire-format to buf, returns pointer to first unused 
+    // byte after stored option
+    unsigned int
+    pack(boost::shared_array<char> buf, unsigned int buf_len, 
+         unsigned int offset);
+
+    // parses received buffer, returns offset to the first unused byte after
+    // parsed option
+    virtual unsigned int 
+    unpack(boost::shared_array<char> buf, 
+           unsigned int buf_len,
+           unsigned int offset, 
+           unsigned int parse_len);
+
+    virtual std::string toText(int indent = 0);
+
+    void setAddress(isc::asiolink::IOAddress addr) { addr_ = addr; }
+    void setPreferred(unsigned int pref) { preferred_=pref; }
+    void setValid(unsigned int valid) { valid_=valid; }
+
+    isc::asiolink::IOAddress getAddress() { return addr_; }
+    unsigned int getPreferred()   { return preferred_; }
+    unsigned int getValid()   { return valid_; }
+
+    // returns data length (data length + DHCPv4/DHCPv6 option header)
+    virtual unsigned short len();
+
+protected:
+    isc::asiolink::IOAddress addr_;
+    unsigned int preferred_;
+    unsigned int valid_;
+};
+
+} // isc::dhcp namespace
+} // isc namespace
+    
+#endif /* OPTION_IA_H_ */
diff --git a/src/lib/dhcp/pkt6.cc b/src/lib/dhcp/pkt6.cc
new file mode 100644
index 0000000..8dc3f6f
--- /dev/null
+++ b/src/lib/dhcp/pkt6.cc
@@ -0,0 +1,314 @@
+// 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 <config.h>
+#include <dhcp/dhcp6.h>
+#include <dhcp/pkt6.h>
+#include <dhcp/libdhcp.h>
+#include <exceptions/exceptions.h>
+#include <iostream>
+#include <sstream>
+
+using namespace std;
+using namespace isc::dhcp;
+
+namespace isc {
+
+/**
+ * Constructor.
+ *
+ * @param dataLen size of buffer to be allocated for this packet.
+ * @param proto protocol (usually UDP, but TCP will be supported eventually)
+ */
+Pkt6::Pkt6(unsigned int dataLen, DHCPv6Proto_ proto /* = UDP */)
+    :local_addr_("::"),
+     remote_addr_("::"),
+     proto_(proto)
+{
+    try {
+        data_ = boost::shared_array<char>(new char[dataLen]);
+        data_len_ = dataLen;
+    } catch (const std::exception&) {
+        // TODO move to LOG_FATAL()
+        // let's continue with empty pkt for now
+        std::cout << "Failed to allocate " << dataLen << " bytes."
+                  << std::endl;
+        data_len_ = 0;
+    }
+}
+
+
+Pkt6::Pkt6(unsigned char msg_type,
+           unsigned int transid,
+           DHCPv6Proto_ proto /*= UDP*/)
+    :local_addr_("::"),
+     remote_addr_("::"),
+     proto_(proto),
+     msg_type_(msg_type),
+     transid_(transid) {
+
+    try {
+        data_ = boost::shared_array<char>(new char[4]);
+        data_len_ = 4;
+    } catch (Exception e) {
+        cout << "Packet creation failed:" << e.what() << endl;
+    }
+    data_len_ = 0;
+}
+
+/**
+ * Returns calculated length of the packet.
+ *
+ * This function returns size of required buffer to buld this packet.
+ * To use that function, options_ field must be set.
+ *
+ * @return number of bytes required to build this packet
+ */
+unsigned short Pkt6::len() {
+    unsigned int length = 4; // DHCPv6 header
+
+    for (Option::Option6Lst::iterator it = options_.begin();
+         it != options_.end();
+         ++it) {
+        length += (*it).second->len();
+    }
+
+    return (length);
+}
+
+
+/**
+ * Builds on wire packet.
+ *
+ * Prepares on wire packet format.
+ *
+ * @return true if preparation was successful
+ */
+bool
+Pkt6::pack() {
+    switch (proto_) {
+    case UDP:
+        return packUDP();
+    case TCP:
+        return packTCP();
+    default:
+        isc_throw(BadValue, "Invalid protocol specified (non-TCP, non-UDP)");
+    }
+    return (false); // never happens
+}
+
+
+/**
+ * Build on wire packet (in UDP format).
+ *
+ * @return true if packet build was successful, false otherwise
+ */
+bool
+Pkt6::packUDP() {
+    unsigned short length = len();
+    if (data_len_ < length) {
+        cout << "Previous len=" << data_len_ << ", allocating new buffer: len="
+             << length << endl;
+
+        try {
+            data_ = boost::shared_array<char>(new char[length]);
+            data_len_ = length;
+        } catch (Exception e) {
+            cout << "Failed to allocate " << length << "-byte buffer:"
+                 << e.what() << endl;
+            return (false);
+        }
+    }
+
+    try {
+        // DHCPv6 header: message-type (1 octect) + transaction id (3 octets)
+        data_[0] = msg_type_;
+
+        // store 3-octet transaction-id
+        data_[1] = (transid_ >> 16) & 0xff;
+        data_[2] = (transid_ >> 8) & 0xff;
+        data_[3] = (transid_) & 0xff;
+
+        // the rest are options
+        unsigned short offset = LibDHCP::packOptions6(data_, length,
+                                                      4/*offset*/,
+                                                      options_);
+
+
+        // sanity check
+        if (offset != length) {
+            isc_throw(OutOfRange, "Packet build failed: expected size="
+                      << length << ", actual len=" << offset);
+        }
+    }
+    catch (Exception e) {
+        cout << "Packet build failed:" << e.what() << endl;
+        return (false);
+    }
+    cout << "Packet built, len=" << len() << endl;
+    return (true);
+}
+
+
+/**
+ * Builds on wire packet for TCP transmission.
+ *
+ * @note This function is not implemented yet.
+ *
+ * @return
+ */
+bool
+Pkt6::packTCP() {
+    /// TODO Implement this function.
+    isc_throw(Unexpected, "DHCPv6 over TCP (bulk leasequery and failover)"
+              "not implemented yet.");
+}
+
+/**
+ * Dispatch method that handles binary packet parsing.
+ *
+ * This method calls appropriate dispatch function (unpackUDP or unpackTCP)
+ *
+ * @return true, if parsing was successful, false otherwise
+ */
+bool
+Pkt6::unpack() {
+    switch (proto_) {
+    case UDP:
+        return unpackUDP();
+    case TCP:
+        return unpackTCP();
+    default:
+        isc_throw(BadValue, "Invalid protocol specified (non-TCP, non-UDP)");
+    }
+    return (false); // never happens
+}
+
+/**
+ * This method unpacks UDP packet.
+ *
+ * @return true, if parsing was successful, false otherwise
+ */
+bool
+Pkt6::unpackUDP() {
+    if (data_len_ < 4) {
+        std::cout << "DHCPv6 packet truncated. Only " << data_len_
+                  << " bytes. Need at least 4." << std::endl;
+        return (false);
+    }
+    msg_type_ = data_[0];
+    transid_ = ( ((unsigned char)data_[1]) << 16 ) + 
+        (((unsigned char)data_[2]) << 8) + ((unsigned char)data_[3]);
+    transid_ = transid_ & 0xffffff;
+
+    unsigned int offset = LibDHCP::unpackOptions6(data_,
+                                                  data_len_,
+                                                  4, //offset
+                                                  data_len_ - 4,
+                                                  options_);
+    if (offset != data_len_) {
+        cout << "DHCPv6 packet contains trailing garbage. Parsed "
+             << offset << " bytes, packet is " << data_len_ << " bytes."
+             << endl;
+        // just a warning. Ignore trailing garbage and continue
+    }
+    return (true);
+}
+
+/**
+ * This method unpacks TDP packet.
+ *
+ * @return true, if parsing was successful, false otherwise
+ */
+bool
+Pkt6::unpackTCP() {
+    isc_throw(Unexpected, "DHCPv6 over TCP (bulk leasequery and failover) "
+              "not implemented yet.");
+}
+
+
+/**
+ * Returns text representation of the packet.
+ *
+ * This function is useful mainly for debugging.
+ *
+ * @return string with text representation
+ */
+std::string
+Pkt6::toText() {
+    stringstream tmp;
+    tmp << "localAddr=[" << local_addr_.toText() << "]:" << local_port_
+        << " remoteAddr=[" << remote_addr_.toText()
+        << "]:" << remote_port_ << endl;
+    tmp << "msgtype=" << msg_type_ << ", transid=0x" << hex << transid_
+        << dec << endl;
+    for (isc::dhcp::Option::Option6Lst::iterator opt=options_.begin();
+         opt != options_.end();
+         ++opt) {
+        tmp << opt->second->toText() << std::endl;
+    }
+    return tmp.str();
+}
+
+/**
+ * Returns the first option of specified type.
+ *
+ * Returns the first option of specified type. Note that in DHCPv6 several
+ * instances of the same option are allowed (and frequently used).
+ * See getOptions().
+ *
+ * @param opt_type option type we are looking for
+ *
+ * @return pointer to found option (or NULL)
+ */
+boost::shared_ptr<isc::dhcp::Option>
+Pkt6::getOption(unsigned short opt_type) {
+    isc::dhcp::Option::Option6Lst::const_iterator x = options_.find(opt_type);
+    if (x!=options_.end()) {
+        return (*x).second;
+    }
+    return boost::shared_ptr<isc::dhcp::Option>(); // NULL
+}
+
+/**
+ * Returns message type.
+ *
+ * @return message type.
+ */
+unsigned char
+Pkt6::getType() {
+    return (msg_type_);
+}
+
+void
+Pkt6::addOption(boost::shared_ptr<Option> opt) {
+    options_.insert(pair<int, boost::shared_ptr<Option> >(opt->getType(), opt));
+}
+
+bool
+Pkt6::delOption(unsigned short type) {
+    isc::dhcp::Option::Option6Lst::iterator x = options_.find(type);
+    if (x!=options_.end()) {
+        options_.erase(x);
+        return (true); // delete successful
+    }
+    return (false); // can't find option to be deleted
+}
+
+Pkt6::~Pkt6() {
+    // no need to delete anything shared_ptr will take care of data_
+}
+
+};
diff --git a/src/lib/dhcp/pkt6.h b/src/lib/dhcp/pkt6.h
new file mode 100644
index 0000000..d24328d
--- /dev/null
+++ b/src/lib/dhcp/pkt6.h
@@ -0,0 +1,106 @@
+// 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 PKT6_H
+#define PKT6_H
+
+#include <iostream>
+#include <boost/shared_ptr.hpp>
+#include <boost/shared_array.hpp>
+#include <asiolink/io_address.h>
+#include <dhcp/option.h>
+
+namespace isc {
+
+
+    class Pkt6 {
+    public:
+        enum DHCPv6Proto_ {
+            UDP = 0, // most packets are UDP
+            TCP = 1  // there are TCP DHCPv6 packet (bulk leasequery, failover)
+        };
+
+        Pkt6(unsigned char msg_type,
+             unsigned int transid,
+             DHCPv6Proto_ proto = UDP);
+        Pkt6(unsigned int len, DHCPv6Proto_ proto = UDP);
+        ~Pkt6();
+
+        bool pack();
+        bool unpack();
+
+        DHCPv6Proto_ getProto();
+        void setProto(DHCPv6Proto_ proto = Pkt6::UDP);
+
+        // returns text representation, useful for debugging
+        std::string toText();
+
+        unsigned short len();
+
+        unsigned char getType();
+        void setType(unsigned char type) { msg_type_=type; };
+        unsigned int getTransid() { return transid_; };
+
+        /// TODO need getter/setter wrappers
+        ///      and hide following fields as protected
+        /// buffer that holds memory. It is shared_array as options may
+        /// share pointer to this buffer
+        boost::shared_array<char> data_;
+
+        // length of the data
+        unsigned int data_len_;
+
+        // local address (dst if receiving packet, src if sending packet)
+        isc::asiolink::IOAddress local_addr_;
+
+        // remote address (src if receiving packet, dst if sending packet)
+        isc::asiolink::IOAddress remote_addr_;
+
+        // name of the network interface the packet was received/to be sent over
+        std::string iface_;
+
+        // interface index (each network interface has assigned unique ifindex
+        // it is functional equvalent of name, but sometimes more useful, e.g.
+        // when using crazy systems that allow spaces in interface names
+        // e.g. windows
+        int ifindex_;
+
+        // local TDP or UDP port
+        int local_port_;
+
+        // remote TCP or UDP port
+        int remote_port_;
+
+        void addOption(boost::shared_ptr<isc::dhcp::Option> opt);
+        boost::shared_ptr<isc::dhcp::Option> getOption(unsigned short type);
+        bool delOption(unsigned short type);
+        /// TODO Need to implement getOptions() as well
+
+        // XXX: add *a lot* here
+        isc::dhcp::Option::Option6Lst options_;
+
+    protected:
+        bool packTCP();
+        bool packUDP();
+        bool unpackTCP();
+        bool unpackUDP();
+
+        DHCPv6Proto_ proto_; // UDP (most) or TCP (bulk leasequery or failover)
+        int msg_type_; // DHCPv6 message type
+        unsigned int transid_;  // DHCPv6 transaction-id
+
+    };
+}
+
+#endif
diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am
new file mode 100644
index 0000000..c00bef5
--- /dev/null
+++ b/src/lib/dhcp/tests/Makefile.am
@@ -0,0 +1,44 @@
+SUBDIRS = .
+
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/asiolink
+AM_CPPFLAGS += -I$(top_builddir)/src/lib/asiolink
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+TESTS =
+if HAVE_GTEST
+TESTS += libdhcp_unittests
+libdhcp_unittests_SOURCES  = run_unittests.cc
+libdhcp_unittests_SOURCES += ../libdhcp.h ../libdhcp.cc libdhcp_unittest.cc
+libdhcp_unittests_SOURCES += ../option6_iaaddr.h ../option6_iaaddr.cc option6_iaaddr_unittest.cc
+libdhcp_unittests_SOURCES += ../option6_ia.h ../option6_ia.cc option6_ia_unittest.cc
+libdhcp_unittests_SOURCES += ../option6_addrlst.h ../option6_addrlst.cc option6_addrlst_unittest.cc
+libdhcp_unittests_SOURCES += ../option.h ../option.cc option_unittest.cc
+libdhcp_unittests_SOURCES += ../pkt6.h ../pkt6.cc pkt6_unittest.cc
+
+libdhcp_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
+libdhcp_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
+
+libdhcp_unittests_CXXFLAGS = $(AM_CXXFLAGS)
+if USE_CLANGPP
+# This is to workaround unused variables tcout and tcerr in
+# log4cplus's streams.h.
+libdhcp_unittests_CXXFLAGS += -Wno-unused-variable
+endif
+libdhcp_unittests_LDADD  = $(GTEST_LDADD)
+libdhcp_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
+libdhcp_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+libdhcp_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
+libdhcp_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
+libdhcp_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+libdhcp_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+endif
+
+noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/dhcp/tests/libdhcp_unittest.cc b/src/lib/dhcp/tests/libdhcp_unittest.cc
new file mode 100644
index 0000000..d68da77
--- /dev/null
+++ b/src/lib/dhcp/tests/libdhcp_unittest.cc
@@ -0,0 +1,155 @@
+// 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 <config.h>
+#include <iostream>
+#include <sstream>
+
+#ifdef _WIN32
+#include <ws2tcpip.h>
+#else
+#include <arpa/inet.h>
+#endif
+#include <gtest/gtest.h>
+
+#include <dhcp/libdhcp.h>
+
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+
+namespace {
+class LibDhcpTest : public ::testing::Test {
+public:
+    LibDhcpTest() {
+    }
+};
+
+TEST_F(LibDhcpTest, basic) {
+    // dummy test
+
+    EXPECT_EQ(LibDHCP::version(), PACKAGE_VERSION);
+}
+
+TEST_F(LibDhcpTest, packOptions6) {
+    boost::shared_array<char> buf(new char[512]);
+    isc::dhcp::Option::Option6Lst opts; // list of options
+
+    // generate content for options
+    for (int i=0;i<64;i++) {
+        buf[i]=i+100;
+    }
+
+    boost::shared_ptr<Option> opt1(new Option(Option::V6, 12, buf, 0, 5));
+    boost::shared_ptr<Option> opt2(new Option(Option::V6, 13, buf, 5, 3));
+    boost::shared_ptr<Option> opt3(new Option(Option::V6, 14, buf, 8, 2));
+    boost::shared_ptr<Option> opt4(new Option(Option::V6,256, buf,10, 4));
+    boost::shared_ptr<Option> opt5(new Option(Option::V6,257, buf,14, 1));
+
+    char expected[] = {
+        0, 12, 0, 5, 100, 101, 102, 103, 104, // opt1
+        0, 13, 0, 3, 105, 106, 107, // opt2
+        0, 14, 0, 2, 108, 109, // opt3
+        1,  0, 0, 4, 110, 111, 112, 113, // opt4
+        1,  1, 0, 1, 114
+    };
+
+    opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt1));
+    opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt2));
+    opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt3));
+    opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt4));
+    opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt5));
+
+    unsigned int offset;
+    EXPECT_NO_THROW ({
+         offset = LibDHCP::packOptions6(buf, 512, 100, opts);
+    });
+    EXPECT_EQ(135, offset); // options should take 35 bytes
+    EXPECT_EQ(0, memcmp(&buf[100], expected, 35) );
+}
+
+TEST_F(LibDhcpTest, unpackOptions6) {
+
+    // just couple of random options
+    char packed[] = {
+        0, 12, 0, 5, 100, 101, 102, 103, 104, // opt1 (9 bytes)
+        0, 13, 0, 3, 105, 106, 107, // opt2 (7 bytes)
+        0, 14, 0, 2, 108, 109, // opt3 (6 bytes)
+        1,  0, 0, 4, 110, 111, 112, 113, // opt4 (8 bytes)
+        1,  1, 0, 1, 114 // opt5 (5 bytes)
+    };
+    // Option is used as a simple option implementation
+    // More advanced uses are validated in tests dedicated for
+    // specific derived classes.
+
+    isc::dhcp::Option::Option6Lst options; // list of options
+
+    // we can't use packed directly, as shared_array would try to
+    // free it eventually
+    boost::shared_array<char> buf(new char[512]);
+    memcpy(&buf[0], packed, 35);
+
+    unsigned int offset;
+    EXPECT_NO_THROW ({
+        offset = LibDHCP::unpackOptions6(buf, 512, 0, 35, options);
+    });
+
+    EXPECT_EQ(35, offset); // parsed first 35 bytes (offset 0..34)
+    EXPECT_EQ(options.size(), 5); // there should be 5 options
+
+    isc::dhcp::Option::Option6Lst::const_iterator x = options.find(12);
+    ASSERT_NE(x, options.end()); // option 1 should exist
+    EXPECT_EQ(12, x->second->getType());  // this should be option 12
+    ASSERT_EQ(9, x->second->len()); // it should be of length 9
+    EXPECT_EQ(0, memcmp(x->second->getData(), packed+4, 5)); // data len=5
+
+    x = options.find(13);
+    ASSERT_NE(x, options.end()); // option 13 should exist
+    EXPECT_EQ(13, x->second->getType());  // this should be option 13
+    ASSERT_EQ(7, x->second->len()); // it should be of length 7
+    EXPECT_EQ(0, memcmp(x->second->getData(), packed+13, 3)); // data len=3
+
+    x = options.find(14);
+    ASSERT_NE(x, options.end()); // option 3 should exist
+    EXPECT_EQ(14, x->second->getType());  // this should be option 14
+    ASSERT_EQ(6, x->second->len()); // it should be of length 6
+    EXPECT_EQ(0, memcmp(x->second->getData(), packed+20, 2)); // data len=2
+
+    x = options.find(256);
+    ASSERT_NE(x, options.end()); // option 256 should exist
+    EXPECT_EQ(256, x->second->getType());  // this should be option 256
+    ASSERT_EQ(8, x->second->len()); // it should be of length 7
+    EXPECT_EQ(0, memcmp(x->second->getData(), packed+26, 4)); // data len=4
+
+    x = options.find(257);
+    ASSERT_NE(x, options.end()); // option 257 should exist
+    EXPECT_EQ(257, x->second->getType());  // this should be option 257
+    ASSERT_EQ(5, x->second->len()); // it should be of length 5
+    EXPECT_EQ(0, memcmp(x->second->getData(), packed+34, 1)); // data len=1
+
+    x = options.find(0);
+    EXPECT_EQ(x, options.end()); // option 0 not found
+
+    x = options.find(1); // 1 is htons(256) on little endians. Worth checking
+    EXPECT_EQ(x, options.end()); // option 1 not found
+
+    x = options.find(2);
+    EXPECT_EQ(x, options.end()); // option 2 not found
+
+    x = options.find(32000);
+    EXPECT_EQ(x, options.end()); // option 32000 not found
+}
+
+}
diff --git a/src/lib/dhcp/tests/option6_addrlst_unittest.cc b/src/lib/dhcp/tests/option6_addrlst_unittest.cc
new file mode 100644
index 0000000..08e9189
--- /dev/null
+++ b/src/lib/dhcp/tests/option6_addrlst_unittest.cc
@@ -0,0 +1,212 @@
+// 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 <config.h>
+#include <iostream>
+#include <sstream>
+
+#ifdef _WIN32
+#include <ws2tcpip.h>
+#else
+#include <arpa/inet.h>
+#endif
+#include <gtest/gtest.h>
+
+#include <asiolink/io_address.h>
+#include <dhcp/dhcp6.h>
+#include <dhcp/option.h>
+#include <dhcp/option6_addrlst.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::asiolink;
+
+namespace {
+class Option6AddrLstTest : public ::testing::Test {
+public:
+    Option6AddrLstTest() {
+    }
+};
+
+TEST_F(Option6AddrLstTest, basic) {
+
+    char sampledata[] = {
+        // 2001:db8:1::dead:beef
+        0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0, 0,
+        0, 0, 0, 0, 0xde, 0xad, 0xbe, 0xef,
+
+        // ff02::face:b00c
+        0xff, 02, 0, 0, 0, 0, 0 , 0,
+        0, 0, 0, 0, 0xfa, 0xce, 0xb0, 0x0c,
+
+        // ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+    };
+
+    char expected1[] = {
+        D6O_NAME_SERVERS/256, D6O_NAME_SERVERS%256,//type
+        0, 16, // len = 16 (1 address)
+        0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0, 0,
+        0, 0, 0, 0, 0xde, 0xad, 0xbe, 0xef,
+
+    };
+
+    char expected2[] = {
+        D6O_SIP_SERVERS_ADDR/256, D6O_SIP_SERVERS_ADDR%256,
+        0, 32, // len = 32 (2 addresses)
+        // 2001:db8:1::dead:beef
+        0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0, 0,
+        0, 0, 0, 0, 0xde, 0xad, 0xbe, 0xef,
+
+        // ff02::face:b00c
+        0xff, 02, 0, 0, 0, 0, 0 , 0,
+        0, 0, 0, 0, 0xfa, 0xce, 0xb0, 0x0c,
+
+        // ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+    };
+
+    char expected3[] = {
+        D6O_NIS_SERVERS/256, D6O_NIS_SERVERS%256,
+        0, 48,
+        // 2001:db8:1::dead:beef
+        0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0, 0,
+        0, 0, 0, 0, 0xde, 0xad, 0xbe, 0xef,
+
+        // ff02::face:b00c
+        0xff, 02, 0, 0, 0, 0, 0 , 0,
+        0, 0, 0, 0, 0xfa, 0xce, 0xb0, 0x0c,
+
+        // ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+    };
+
+    boost::shared_array<char> buf(new char[300]);
+    for (int i=0; i<300; i++)
+        buf[i] = 0;
+
+    memcpy(&buf[0], sampledata, 48);
+
+    // just a single address
+    Option6AddrLst* opt1 = new Option6AddrLst(D6O_NAME_SERVERS,
+                                              buf,
+                                              128,
+                                              0,
+                                              16);
+
+
+    EXPECT_EQ(D6O_NAME_SERVERS, opt1->getType());
+    EXPECT_EQ(20, opt1->len());
+    Option6AddrLst::AddrsContainer addrs = opt1->getAddresses();
+    ASSERT_EQ(1, addrs.size());
+    IOAddress addr = addrs[0];
+    EXPECT_EQ("2001:db8:1::dead:beef", addr.toText());
+
+    // pack this option again in the same buffer, but in
+    // different place
+    int offset = opt1->pack(buf,300, 100);
+
+    EXPECT_EQ(120, offset);
+    EXPECT_EQ( 0, memcmp(expected1, &buf[100], 20) );
+
+    // two addresses
+    Option6AddrLst* opt2 = new Option6AddrLst(D6O_SIP_SERVERS_ADDR,
+                                              buf,
+                                              128,
+                                              0,
+                                              32);
+    EXPECT_EQ(D6O_SIP_SERVERS_ADDR, opt2->getType());
+    EXPECT_EQ(36, opt2->len());
+    addrs = opt2->getAddresses();
+    ASSERT_EQ(2, addrs.size());
+    EXPECT_EQ("2001:db8:1::dead:beef", addrs[0].toText());
+    EXPECT_EQ("ff02::face:b00c", addrs[1].toText());
+
+    // pack this option again in the same buffer, but in
+    // different place
+    offset = opt2->pack(buf,300, 150);
+
+    EXPECT_EQ(150+36, offset);
+    EXPECT_EQ( 0, memcmp(expected2, &buf[150], 36));
+
+
+    // three addresses
+    Option6AddrLst* opt3 = new Option6AddrLst(D6O_NIS_SERVERS,
+                                              buf,
+                                              128,
+                                              0,
+                                              48);
+
+    EXPECT_EQ(D6O_NIS_SERVERS, opt3->getType());
+    EXPECT_EQ(52, opt3->len());
+    addrs = opt3->getAddresses();
+    ASSERT_EQ(3, addrs.size());
+    EXPECT_EQ("2001:db8:1::dead:beef", addrs[0].toText());
+    EXPECT_EQ("ff02::face:b00c", addrs[1].toText());
+    EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", addrs[2].toText());
+
+    // pack this option again in the same buffer, but in
+    // different place
+    offset = opt3->pack(buf,300, 200);
+
+    EXPECT_EQ(252, offset);
+    EXPECT_EQ( 0, memcmp(expected3, &buf[200], 52) );
+
+    delete opt1;
+    delete opt2;
+    delete opt3;
+}
+
+TEST_F(Option6AddrLstTest, constructors) {
+     Option6AddrLst * opt1 = new Option6AddrLst(1234,
+                                                IOAddress("::1"));
+     EXPECT_EQ(1234, opt1->getType());
+
+     Option6AddrLst::AddrsContainer addrs = opt1->getAddresses();
+     ASSERT_EQ(1, addrs.size() );
+     EXPECT_EQ("::1", addrs[0].toText());
+
+     addrs.clear();
+     addrs.push_back(IOAddress(string("fe80::1234")));
+     addrs.push_back(IOAddress(string("2001:db8:1::baca")));
+
+     Option6AddrLst * opt2 = new Option6AddrLst(5678,
+                                                addrs);
+
+     Option6AddrLst::AddrsContainer check = opt2->getAddresses();
+     ASSERT_EQ(2, check.size() );
+     EXPECT_EQ("fe80::1234", check[0].toText());
+     EXPECT_EQ("2001:db8:1::baca", check[1].toText());
+
+     delete opt1;
+     delete opt2;
+}
+
+TEST_F(Option6AddrLstTest, setAddress) {
+    Option6AddrLst * opt1 = new Option6AddrLst(1234,
+                                                IOAddress("::1"));
+    opt1->setAddress(IOAddress("::2"));
+
+    Option6AddrLst::AddrsContainer addrs = opt1->getAddresses();
+    ASSERT_EQ(1, addrs.size() );
+    EXPECT_EQ("::2", addrs[0].toText());
+
+    delete opt1;
+}
+
+} // namespace
diff --git a/src/lib/dhcp/tests/option6_ia_unittest.cc b/src/lib/dhcp/tests/option6_ia_unittest.cc
new file mode 100644
index 0000000..d2f8f21
--- /dev/null
+++ b/src/lib/dhcp/tests/option6_ia_unittest.cc
@@ -0,0 +1,261 @@
+// 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 <config.h>
+#include <iostream>
+#include <sstream>
+#include <boost/shared_ptr.hpp>
+
+#ifdef _WIN32
+#include <ws2tcpip.h>
+#else
+#include <arpa/inet.h>
+#endif
+#include <gtest/gtest.h>
+
+#include <dhcp/dhcp6.h>
+#include <dhcp/option.h>
+#include <dhcp/option6_ia.h>
+#include <dhcp/option6_iaaddr.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::asiolink;
+
+namespace {
+class Option6IATest : public ::testing::Test {
+public:
+    Option6IATest() {
+    }
+};
+
+TEST_F(Option6IATest, basic) {
+
+    boost::shared_array<char> simple_buf(new char[128]);
+    for (int i=0; i<128; i++)
+        simple_buf[i] = 0;
+    simple_buf[0]=0xa1; // iaid
+    simple_buf[1]=0xa2;
+    simple_buf[2]=0xa3;
+    simple_buf[3]=0xa4;
+
+    simple_buf[4]=0x81; // T1
+    simple_buf[5]=0x02;
+    simple_buf[6]=0x03;
+    simple_buf[7]=0x04;
+
+    simple_buf[8]=0x84; // T2
+    simple_buf[9]=0x03;
+    simple_buf[10]=0x02;
+    simple_buf[11]=0x01;
+
+    // create an option
+    // unpack() is called from constructor
+    Option6IA* opt = new Option6IA(Option::V6,
+                                   D6O_IA_NA,
+                                   simple_buf,
+                                   128,
+                                   0,
+                                   12);
+
+    EXPECT_EQ(D6O_IA_NA, opt->getType());
+    EXPECT_EQ(0xa1a2a3a4, opt->getIAID());
+    EXPECT_EQ(0x81020304, opt->getT1());
+    EXPECT_EQ(0x84030201, opt->getT2());
+
+    // pack this option again in the same buffer, but in
+    // different place
+
+    // test for pack()
+    int offset = opt->pack(simple_buf, 128, 60);
+
+    // 4 bytes header + 4 bytes content
+    EXPECT_EQ(12, opt->len() - 4);
+    EXPECT_EQ(D6O_IA_NA, opt->getType());
+
+    EXPECT_EQ(offset, 76); // 60 + lenght(IA_NA) = 76
+
+    // check if pack worked properly:
+    // if option type is correct
+    EXPECT_EQ(D6O_IA_NA, simple_buf[60]*256 + simple_buf[61]);
+
+    // if option length is correct
+    EXPECT_EQ(12, simple_buf[62]*256 + simple_buf[63]);
+
+    // if iaid is correct
+    unsigned int iaid = htonl(*(unsigned int*)&simple_buf[64]);
+    EXPECT_EQ(0xa1a2a3a4, iaid );
+
+   // if T1 is correct
+    EXPECT_EQ(0x81020304, (simple_buf[68] << 24) +
+                          (simple_buf[69] << 16) +
+                          (simple_buf[70] << 8) +
+                          (simple_buf[71]) );
+
+    // if T1 is correct
+    EXPECT_EQ(0x84030201, (simple_buf[72] << 24) +
+                          (simple_buf[73] << 16) +
+                          (simple_buf[74] << 8) +
+                          (simple_buf[75]) );
+
+    delete opt;
+}
+
+TEST_F(Option6IATest, simple) {
+    boost::shared_array<char> simple_buf(new char[128]);
+    for (int i=0; i<128; i++)
+        simple_buf[i] = 0;
+
+    Option6IA * ia = new Option6IA(Option::V6, D6O_IA_NA,
+                                   1234);
+    ia->setT1(2345);
+    ia->setT2(3456);
+
+    EXPECT_EQ(D6O_IA_NA, ia->getType());
+    EXPECT_EQ(1234, ia->getIAID());
+    EXPECT_EQ(2345, ia->getT1());
+    EXPECT_EQ(3456, ia->getT2());
+
+    delete ia;
+}
+
+// test if option can build suboptions
+TEST_F(Option6IATest, suboptions_pack) {
+    boost::shared_array<char> buf(new char[128]);
+    for (int i=0; i<128; i++)
+        buf[i] = 0;
+    buf[0] = 0xff;
+    buf[1] = 0xfe;
+    buf[2] = 0xfc;
+
+    Option6IA * ia = new Option6IA(Option::V6, D6O_IA_NA,
+                                   0x13579ace);
+    ia->setT1(0x2345);
+    ia->setT2(0x3456);
+
+    boost::shared_ptr<Option> sub1(new Option(Option::V6,
+                                              0xcafe));
+
+    boost::shared_ptr<Option6IAAddr> addr1(
+        new Option6IAAddr(D6O_IAADDR, IOAddress("2001:db8:1234:5678::abcd"),
+                          0x5000, 0x7000));
+
+    ia->addOption(sub1);
+    ia->addOption(addr1);
+
+    ASSERT_EQ(28, addr1->len());
+    ASSERT_EQ(4, sub1->len());
+    ASSERT_EQ(48, ia->len());
+
+    char expected[] = {
+        D6O_IA_NA/256, D6O_IA_NA%256, // type
+        0, 44, // length
+        0x13, 0x57, 0x9a, 0xce, // iaid
+        0, 0, 0x23, 0x45,  // T1
+        0, 0, 0x34, 0x56,  // T2
+
+        // iaaddr suboption
+        D6O_IAADDR/256, D6O_IAADDR%256, // type
+        0, 24, // len
+        0x20, 0x01, 0xd, 0xb8, 0x12,0x34, 0x56, 0x78,
+        0, 0, 0, 0, 0, 0, 0xab, 0xcd, // IP address
+        0, 0, 0x50, 0, // preferred-lifetime
+        0, 0, 0x70, 0, // valid-lifetime
+
+        // suboption
+        0xca, 0xfe, // type
+        0, 0 // len
+    };
+
+    int offset = ia->pack(buf, 128, 10);
+    ASSERT_EQ(offset, 10 + 48);
+
+    EXPECT_EQ(0, memcmp(&buf[10], expected, 48));
+
+    delete ia;
+}
+
+// test if option can parse suboptions
+TEST_F(Option6IATest, suboptions_unpack) {
+
+
+    char expected[] = {
+        D6O_IA_NA/256, D6O_IA_NA%256, // type
+        0, 28, // length
+        0x13, 0x57, 0x9a, 0xce, // iaid
+        0, 0, 0x23, 0x45,  // T1
+        0, 0, 0x34, 0x56,  // T2
+
+        // iaaddr suboption
+        D6O_IAADDR/256, D6O_IAADDR%256, // type
+        0, 24, // len
+        0x20, 0x01, 0xd, 0xb8, 0x12,0x34, 0x56, 0x78,
+        0, 0, 0, 0, 0, 0, 0xab, 0xcd, // IP address
+        0, 0, 0x50, 0, // preferred-lifetime
+        0, 0, 0x70, 0, // valid-lifetime
+
+        // suboption
+        0xca, 0xfe, // type
+        0, 0 // len
+    };
+
+    boost::shared_array<char> buf(new char[128]);
+    for (int i=0; i<128; i++)
+        buf[i] = 0;
+    memcpy(&buf[0], expected, 48);
+
+    Option6IA * ia;
+    EXPECT_NO_THROW({
+            ia = new Option6IA(Option::V6, D6O_IA_NA,
+                               buf, 128, 4, 44);
+    });
+
+    cout << "Parsed option:" << endl << ia->toText() << endl;
+
+    EXPECT_EQ(D6O_IA_NA, ia->getType());
+    EXPECT_EQ(0x13579ace, ia->getIAID());
+    EXPECT_EQ(0x2345, ia->getT1());
+    EXPECT_EQ(0x3456, ia->getT2());
+
+    boost::shared_ptr<Option> subopt = ia->getOption(D6O_IAADDR);
+    ASSERT_NE(boost::shared_ptr<Option>(), subopt); // non-NULL
+
+    // checks for address option
+    Option6IAAddr * addr = dynamic_cast<Option6IAAddr*>(subopt.get());
+    ASSERT_TRUE(NULL != addr);
+
+    EXPECT_EQ(D6O_IAADDR, addr->getType());
+    EXPECT_EQ(28, addr->len());
+    EXPECT_EQ(0x5000, addr->getPreferred());
+    EXPECT_EQ(0x7000, addr->getValid());
+    EXPECT_EQ("2001:db8:1234:5678::abcd", addr->getAddress().toText());
+
+    // checks for dummy option
+    subopt = ia->getOption(0xcafe);
+    ASSERT_FALSE(subopt == boost::shared_ptr<Option>()); // non-NULL
+
+    EXPECT_EQ(0xcafe, subopt->getType());
+    EXPECT_EQ(4, subopt->len());
+    EXPECT_EQ(NULL, subopt->getData());
+
+    subopt = ia->getOption(1); // get option 1
+    ASSERT_EQ(subopt, boost::shared_ptr<Option>()); // NULL
+
+    delete ia;
+}
+
+
+
+}
diff --git a/src/lib/dhcp/tests/option6_iaaddr_unittest.cc b/src/lib/dhcp/tests/option6_iaaddr_unittest.cc
new file mode 100644
index 0000000..92e92ab
--- /dev/null
+++ b/src/lib/dhcp/tests/option6_iaaddr_unittest.cc
@@ -0,0 +1,100 @@
+// 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 <config.h>
+#include <iostream>
+#include <sstream>
+
+#ifdef _WIN32
+#include <ws2tcpip.h>
+#else
+#include <arpa/inet.h>
+#endif
+#include <gtest/gtest.h>
+
+#include <dhcp/dhcp6.h>
+#include <dhcp/option.h>
+#include <dhcp/option6_iaaddr.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+
+namespace {
+class Option6IAAddrTest : public ::testing::Test {
+public:
+    Option6IAAddrTest() {
+    }
+};
+
+TEST_F(Option6IAAddrTest, basic) {
+
+    boost::shared_array<char> simple_buf(new char[128]);
+    for (int i=0; i<128; i++)
+        simple_buf[i] = 0;
+    simple_buf[0]=0x20;
+    simple_buf[1]=0x01;
+    simple_buf[2]=0x0d;
+    simple_buf[3]=0xb8;
+    simple_buf[4]=0x00;
+    simple_buf[5]=0x01;
+    simple_buf[12]=0xde;
+    simple_buf[13]=0xad;
+    simple_buf[14]=0xbe;
+    simple_buf[15]=0xef; // 2001:db8:1::dead:beef
+
+    simple_buf[16]=0x00;
+    simple_buf[17]=0x00;
+    simple_buf[18]=0x03;
+    simple_buf[19]=0xe8; // 1000
+
+    simple_buf[20]=0xb2;
+    simple_buf[21]=0xd0;
+    simple_buf[22]=0x5e;
+    simple_buf[23]=0x00; // 3.000.000.000 
+
+    // create an option (unpack content)
+    Option6IAAddr* opt = new Option6IAAddr(D6O_IAADDR,
+                                           simple_buf,
+                                           128,
+                                           0, 
+                                           24);
+    
+    // pack this option again in the same buffer, but in
+    // different place
+    int offset = opt->pack(simple_buf, 128, 50);
+
+    EXPECT_EQ(78, offset);
+
+    // 4 bytes header + 4 bytes content
+    EXPECT_EQ("2001:db8:1::dead:beef", opt->getAddress().toText());
+    EXPECT_EQ(1000, opt->getPreferred());
+    EXPECT_EQ(3000000000, opt->getValid());
+
+    EXPECT_EQ(D6O_IAADDR, opt->getType());
+
+    // check if pack worked properly:
+    // if option type is correct
+    EXPECT_EQ(D6O_IAADDR, simple_buf[50]*256 + simple_buf[51]);
+
+    // if option length is correct
+    EXPECT_EQ(24, simple_buf[52]*256 + simple_buf[53]);
+    
+    // if option content is correct
+    EXPECT_EQ(0, memcmp(&simple_buf[0], &simple_buf[54],24));
+
+    delete opt;
+}
+
+}
diff --git a/src/lib/dhcp/tests/option_unittest.cc b/src/lib/dhcp/tests/option_unittest.cc
new file mode 100644
index 0000000..38e4b56
--- /dev/null
+++ b/src/lib/dhcp/tests/option_unittest.cc
@@ -0,0 +1,259 @@
+// 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 <config.h>
+#include <iostream>
+#include <sstream>
+
+#ifdef _WIN32
+#include <ws2tcpip.h>
+#else
+#include <arpa/inet.h>
+#endif
+#include <gtest/gtest.h>
+#include <boost/shared_ptr.hpp>
+
+#include <dhcp/dhcp6.h>
+#include <dhcp/option.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+
+namespace {
+class OptionTest : public ::testing::Test {
+public:
+    OptionTest() {
+    }
+};
+
+// v4 is not really implemented yet. A simple test will do for now
+TEST_F(OptionTest, basic4) {
+
+    Option* opt = new Option(Option::V4, 17);
+
+    EXPECT_EQ(17, opt->getType());
+    EXPECT_EQ(NULL, opt->getData());
+    EXPECT_EQ(2, opt->len()); // just v4 header
+
+    delete opt;
+}
+
+// tests simple constructor
+TEST_F(OptionTest, basic6) {
+
+    Option* opt = new Option(Option::V6, 1);
+
+    EXPECT_EQ(1, opt->getType());
+    EXPECT_EQ(NULL, opt->getData());
+    EXPECT_EQ(4, opt->len()); // just v6 header
+
+    delete opt;
+}
+
+// tests contructor used in pkt reception
+// option contains actual data
+TEST_F(OptionTest, data1) {
+    boost::shared_array<char> buf(new char[32]);
+    for (int i=0; i<32; i++)
+        buf[i] = 100+i;
+    Option* opt = new Option(Option::V6, 333, //type
+                             buf,
+                             3, // offset
+                             7); // 7 bytes of data
+    EXPECT_EQ(333, opt->getType());
+    ASSERT_EQ(&buf[3], opt->getData());
+    ASSERT_EQ(11, opt->len());
+    EXPECT_EQ(0, memcmp(&buf[3], opt->getData(), 7) );
+
+    int offset = opt->pack(buf, 32, 20);
+    EXPECT_EQ(31, offset);
+
+    EXPECT_EQ(buf[20], 333/256); // type
+    EXPECT_EQ(buf[21], 333%256);
+
+    EXPECT_EQ(buf[22], 0); // len
+    EXPECT_EQ(buf[23], 7);
+
+    // payload
+    EXPECT_EQ(0, memcmp(&buf[3], &buf[24], 7) );
+
+    delete opt;
+}
+
+// another text that tests the same thing, just
+// with different input parameters
+TEST_F(OptionTest, data2) {
+
+    boost::shared_array<char> simple_buf(new char[128]);
+    for (int i=0; i<128; i++)
+        simple_buf[i] = 0;
+    simple_buf[0]=0xa1;
+    simple_buf[1]=0xa2;
+    simple_buf[2]=0xa3;
+    simple_buf[3]=0xa4;
+
+    // create an option (unpack content)
+    Option* opt = new Option(Option::V6, 
+                             D6O_CLIENTID,
+                             simple_buf,
+                             0, 
+                             4);
+    
+    // pack this option again in the same buffer, but in
+    // different place
+    int offset18 = opt->pack(simple_buf, 128, 10);
+
+    // 4 bytes header + 4 bytes content
+    EXPECT_EQ(8, opt->len());
+    EXPECT_EQ(D6O_CLIENTID, opt->getType());
+
+    EXPECT_EQ(offset18, 18);
+
+    // check if pack worked properly:
+    // if option type is correct
+    EXPECT_EQ(D6O_CLIENTID, simple_buf[10]*256 + simple_buf[11]);
+
+    // if option length is correct
+    EXPECT_EQ(4, simple_buf[12]*256 + simple_buf[13]);
+    
+    // if option content is correct
+    EXPECT_EQ(0, memcmp(&simple_buf[0], &simple_buf[14],4));
+
+    delete opt;
+}
+
+// check that an option can contain 2 suboptions:
+// opt1
+//  +----opt2
+//  |
+//  +----opt3
+//
+TEST_F(OptionTest, suboptions1) {
+    boost::shared_array<char> buf(new char[128]);
+    for (int i=0; i<128; i++)
+        buf[i] = 100+i;
+    Option* opt1 = new Option(Option::V6, 65535, //type
+                              buf,
+                              0, // offset
+                              3); // 3 bytes of data
+    boost::shared_ptr<Option> opt2(new Option(Option::V6, 13));
+    boost::shared_ptr<Option> opt3(new Option(Option::V6, 7,
+                                              buf,
+                                              3, // offset
+                                              5)); // 5 bytes of data
+    opt1->addOption(opt2);
+    opt1->addOption(opt3);
+    // opt2 len = 4 (just header)
+    // opt3 len = 9 4(header)+5(data)
+    // opt1 len = 7 + suboptions() = 7 + 4 + 9 = 20
+
+    EXPECT_EQ(4, opt2->len());
+    EXPECT_EQ(9, opt3->len());
+    EXPECT_EQ(20, opt1->len());
+
+    char expected[] = {
+        0xff, 0xff, 0, 16, 100, 101, 102,
+        0, 7, 0, 5, 103, 104, 105, 106, 107,
+        0, 13, 0, 0 // no data at all
+    };
+
+    int offset = opt1->pack(buf, 128, 20);
+    EXPECT_EQ(40, offset);
+
+    // payload
+    EXPECT_EQ(0, memcmp(&buf[20], expected, 20) );
+
+    delete opt1;
+}
+
+// check that an option can contain 2 suboptions:
+// opt1
+//  +----opt2
+//        |
+//        +----opt3
+//
+TEST_F(OptionTest, suboptions2) {
+    boost::shared_array<char> buf(new char[128]);
+    for (int i=0; i<128; i++)
+        buf[i] = 100+i;
+    Option* opt1 = new Option(Option::V6, 65535, //type
+                              buf,
+                              0, // offset
+                              3); // 3 bytes of data
+    boost::shared_ptr<Option> opt2(new Option(Option::V6, 13));
+    boost::shared_ptr<Option> opt3(new Option(Option::V6, 7,
+                                              buf,
+                                              3, // offset
+                                              5)); // 5 bytes of data
+    opt1->addOption(opt2);
+    opt2->addOption(opt3);
+    // opt3 len = 9 4(header)+5(data)
+    // opt2 len = 4 (just header) + len(opt3)
+    // opt1 len = 7 + len(opt2)
+
+    char expected[] = {
+        0xff, 0xff, 0, 16, 100, 101, 102,
+        0, 13, 0, 9,
+        0, 7, 0, 5, 103, 104, 105, 106, 107,
+    };
+
+    int offset = opt1->pack(buf, 128, 20);
+    EXPECT_EQ(40, offset);
+
+    // payload
+    EXPECT_EQ(0, memcmp(&buf[20], expected, 20) );
+
+    delete opt1;
+}
+
+TEST_F(OptionTest, addgetdel) {
+    boost::shared_array<char> buf(new char[128]);
+    for (int i=0; i<128; i++)
+        buf[i] = 100+i;
+    Option* parent = new Option(Option::V6, 65535); //type
+    boost::shared_ptr<Option> opt1(new Option(Option::V6, 1));
+    boost::shared_ptr<Option> opt2(new Option(Option::V6, 2));
+    boost::shared_ptr<Option> opt3(new Option(Option::V6, 2));
+
+    parent->addOption(opt1);
+    parent->addOption(opt2);
+
+    // getOption() test
+    EXPECT_EQ(opt1, parent->getOption(1));
+    EXPECT_EQ(opt2, parent->getOption(2));
+
+    // expect NULL
+    EXPECT_EQ(boost::shared_ptr<Option>(), parent->getOption(4));
+
+    // now there are 2 options of type 2
+    parent->addOption(opt3);
+
+    // let's delete one of them
+    EXPECT_EQ(true, parent->delOption(2));
+
+    // there still should be the other option 2
+    EXPECT_NE(boost::shared_ptr<Option>(), parent->getOption(2));
+
+    // let's delete the other option 2
+    EXPECT_EQ(true, parent->delOption(2));
+
+    // no more options with type=2
+    EXPECT_EQ(boost::shared_ptr<Option>(), parent->getOption(2));
+
+    // let's try to delete - should fail
+    EXPECT_TRUE(false ==  parent->delOption(2));
+}
+
+}
diff --git a/src/lib/dhcp/tests/pkt6_unittest.cc b/src/lib/dhcp/tests/pkt6_unittest.cc
new file mode 100644
index 0000000..b31d89a
--- /dev/null
+++ b/src/lib/dhcp/tests/pkt6_unittest.cc
@@ -0,0 +1,155 @@
+// 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 <config.h>
+#include <iostream>
+#include <sstream>
+#ifdef _WIN32
+#include <ws2tcpip.h>
+#else
+#include <arpa/inet.h>
+#endif
+#include <gtest/gtest.h>
+
+#include <asiolink/io_address.h>
+#include <dhcp/option.h>
+#include <dhcp/pkt6.h>
+#include <dhcp/dhcp6.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+
+namespace {
+// empty class for now, but may be extended once Addr6 becomes bigger
+class Pkt6Test : public ::testing::Test {
+public:
+    Pkt6Test() {
+    }
+};
+
+TEST_F(Pkt6Test, constructor) {
+    Pkt6 * pkt1 = new Pkt6(17);
+
+    ASSERT_EQ(pkt1->data_len_, 17);
+
+    delete pkt1;
+}
+
+// captured actual SOLICIT packet: transid=0x3d79fb
+// options: client-id, in_na, dns-server, elapsed-time, option-request
+// this code is autogenerated (see src/bin/dhcp6/tests/iface_mgr_unittest.c)
+Pkt6 *capture1() {
+    Pkt6* pkt;
+    pkt = new Pkt6(98);
+    pkt->remote_port_ = 546;
+    pkt->remote_addr_ = IOAddress("fe80::21e:8cff:fe9b:7349");
+    pkt->local_port_ = 0;
+    pkt->local_addr_ = IOAddress("ff02::1:2");
+    pkt->ifindex_ = 2;
+    pkt->iface_ = "eth0";
+    pkt->data_[0]=1;
+    pkt->data_[1]=01;     pkt->data_[2]=02;     pkt->data_[3]=03;     pkt->data_[4]=0;
+    pkt->data_[5]=1;     pkt->data_[6]=0;     pkt->data_[7]=14;     pkt->data_[8]=0;
+    pkt->data_[9]=1;     pkt->data_[10]=0;     pkt->data_[11]=1;     pkt->data_[12]=21;
+    pkt->data_[13]=158;     pkt->data_[14]=60;     pkt->data_[15]=22;     pkt->data_[16]=0;
+    pkt->data_[17]=30;     pkt->data_[18]=140;     pkt->data_[19]=155;     pkt->data_[20]=115;
+    pkt->data_[21]=73;     pkt->data_[22]=0;     pkt->data_[23]=3;     pkt->data_[24]=0;
+    pkt->data_[25]=40;     pkt->data_[26]=0;     pkt->data_[27]=0;     pkt->data_[28]=0;
+    pkt->data_[29]=1;     pkt->data_[30]=255;     pkt->data_[31]=255;     pkt->data_[32]=255;
+    pkt->data_[33]=255;     pkt->data_[34]=255;     pkt->data_[35]=255;     pkt->data_[36]=255;
+    pkt->data_[37]=255;     pkt->data_[38]=0;     pkt->data_[39]=5;     pkt->data_[40]=0;
+    pkt->data_[41]=24;     pkt->data_[42]=32;     pkt->data_[43]=1;     pkt->data_[44]=13;
+    pkt->data_[45]=184;     pkt->data_[46]=0;     pkt->data_[47]=1;     pkt->data_[48]=0;
+    pkt->data_[49]=0;     pkt->data_[50]=0;     pkt->data_[51]=0;     pkt->data_[52]=0;
+    pkt->data_[53]=0;     pkt->data_[54]=0;     pkt->data_[55]=0;     pkt->data_[56]=18;
+    pkt->data_[57]=52;     pkt->data_[58]=255;     pkt->data_[59]=255;     pkt->data_[60]=255;
+    pkt->data_[61]=255;     pkt->data_[62]=255;     pkt->data_[63]=255;     pkt->data_[64]=255;
+    pkt->data_[65]=255;     pkt->data_[66]=0;     pkt->data_[67]=23;     pkt->data_[68]=0;
+    pkt->data_[69]=16;     pkt->data_[70]=32;     pkt->data_[71]=1;     pkt->data_[72]=13;
+    pkt->data_[73]=184;     pkt->data_[74]=0;     pkt->data_[75]=1;     pkt->data_[76]=0;
+    pkt->data_[77]=0;     pkt->data_[78]=0;     pkt->data_[79]=0;     pkt->data_[80]=0;
+    pkt->data_[81]=0;     pkt->data_[82]=0;     pkt->data_[83]=0;     pkt->data_[84]=221;
+    pkt->data_[85]=221;     pkt->data_[86]=0;     pkt->data_[87]=8;     pkt->data_[88]=0;
+    pkt->data_[89]=2;     pkt->data_[90]=0;     pkt->data_[91]=100;     pkt->data_[92]=0;
+    pkt->data_[93]=6;     pkt->data_[94]=0;     pkt->data_[95]=2;     pkt->data_[96]=0;
+    pkt->data_[97]=23;
+    return (pkt);
+}
+
+TEST_F(Pkt6Test, parse_solicit1) {
+    Pkt6 * sol = capture1();
+
+    ASSERT_EQ(true, sol->unpack());
+
+    boost::shared_ptr<isc::dhcp::Option> null;
+
+    // check that all present options are returned
+    EXPECT_NE(null, sol->getOption(D6O_CLIENTID)); // client-id is present
+    EXPECT_NE(null, sol->getOption(D6O_IA_NA));    // IA_NA is present
+    EXPECT_NE(null, sol->getOption(D6O_ELAPSED_TIME));  // elapsed is present
+    EXPECT_NE(null, sol->getOption(D6O_NAME_SERVERS));
+    EXPECT_NE(null, sol->getOption(D6O_ORO));
+
+    // let's check that non-present options are not returned
+    EXPECT_EQ(null, sol->getOption(D6O_SERVERID)); // server-id is missing
+    EXPECT_EQ(null, sol->getOption(D6O_IA_TA));
+    EXPECT_EQ(null, sol->getOption(D6O_IAADDR));
+
+    std::cout << sol->toText();
+
+    delete sol;
+}
+
+TEST_F(Pkt6Test, addGetDelOptions) {
+    Pkt6 * parent = new Pkt6(100);
+
+    boost::shared_ptr<Option> opt1(new Option(Option::V6, 1));
+    boost::shared_ptr<Option> opt2(new Option(Option::V6, 2));
+    boost::shared_ptr<Option> opt3(new Option(Option::V6, 2));
+
+    parent->addOption(opt1);
+    parent->addOption(opt2);
+
+    // getOption() test
+    EXPECT_EQ(opt1, parent->getOption(1));
+    EXPECT_EQ(opt2, parent->getOption(2));
+
+    // expect NULL
+    EXPECT_EQ(boost::shared_ptr<Option>(), parent->getOption(4));
+
+    // now there are 2 options of type 2
+    parent->addOption(opt3);
+
+    // let's delete one of them
+    EXPECT_EQ(true, parent->delOption(2));
+
+    // there still should be the other option 2
+    EXPECT_NE(boost::shared_ptr<Option>(), parent->getOption(2));
+
+    // let's delete the other option 2
+    EXPECT_EQ(true, parent->delOption(2));
+
+    // no more options with type=2
+    EXPECT_EQ(boost::shared_ptr<Option>(), parent->getOption(2));
+
+    // let's try to delete - should fail
+    EXPECT_TRUE(false ==  parent->delOption(2));
+
+    delete parent;
+}
+
+
+}
diff --git a/src/lib/dhcp/tests/run_unittests.cc b/src/lib/dhcp/tests/run_unittests.cc
new file mode 100644
index 0000000..8a9d1e5
--- /dev/null
+++ b/src/lib/dhcp/tests/run_unittests.cc
@@ -0,0 +1,25 @@
+// 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 <util/unittests/run_all.h>
+
+#include <log/logger_support.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/util/Makefile.am b/src/lib/util/Makefile.am
index 77a3e4a..0b78b29 100644
--- a/src/lib/util/Makefile.am
+++ b/src/lib/util/Makefile.am
@@ -1,6 +1,4 @@
-SUBDIRS = . tests unittests io
-# The io/tests is hack, because otherwise we can not order these directories
-# properly. Unittests use io and io/tests use unittest.
+SUBDIRS = . io unittests tests pyunittests python
 
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS += -I$(top_srcdir)/src/lib/util -I$(top_builddir)/src/lib/util
@@ -24,5 +22,7 @@ libutil_la_SOURCES += encode/binary_from_base16.h
 libutil_la_SOURCES += random/qid_gen.h random/qid_gen.cc
 libutil_la_SOURCES += random/random_number_generator.h
 
+EXTRA_DIST = python/pycppwrapper_util.h
+
 libutil_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
 CLEANFILES = *.gcno *.gcda
diff --git a/src/lib/util/encode/base_n.cc b/src/lib/util/encode/base_n.cc
index 0d26f16..d9507e4 100644
--- a/src/lib/util/encode/base_n.cc
+++ b/src/lib/util/encode/base_n.cc
@@ -113,7 +113,7 @@ class EncodeNormalizer : public iterator<input_iterator_tag, uint8_t> {
 public:
     EncodeNormalizer(const vector<uint8_t>::const_iterator& base,
                      const vector<uint8_t>::const_iterator& base_end) :
-        base_(base), base_end_(base_end), in_pad_(base == base_end)
+        base_(base), base_end_(base_end), in_pad_(false)
     {}
     EncodeNormalizer& operator++() {
         if (!in_pad_) {
@@ -164,22 +164,43 @@ public:
                      const string::const_iterator& base_end) :
         base_zero_code_(base_zero_code),
         base_(base), base_beginpad_(base_beginpad), base_end_(base_end),
-        in_pad_(base == base_beginpad)
-    {}
+        in_pad_(false)
+    {
+        // Skip beginning spaces, if any.  We need do it here because
+        // otherwise the first call to operator*() would be confused.
+        skipSpaces();
+    }
     DecodeNormalizer& operator++() {
         ++base_;
-        while (base_ != base_end_ && isspace((int)*base_ & 0xff)) {
-            ++base_;
-        }
+        skipSpaces();
         if (base_ == base_beginpad_) {
             in_pad_ = true;
         }
         return (*this);
     }
+    void skipSpaces() {
+        // If (char is signed and) *base_ < 0, on Windows platform with Visual
+        // Studio compiler it may trigger _ASSERTE((unsigned)(c + 1) <= 256);
+        // so make sure that the parameter of isspace() is larger than 0.
+        // We don't simply cast it to unsigned char to avoid confusing the
+        // isspace() implementation with a possible extension for values
+        // larger than 127.  Also note the check is not ">= 0"; for systems
+        // where char is unsigned that would always be true and would possibly
+        // trigger a compiler warning that could stop the build.
+        while (base_ != base_end_ && *base_ > 0 && isspace(*base_)) {
+            ++base_;
+        }
+    }
     const char& operator*() const {
-        if (base_ == base_end_)
-            isc_throw(BadValue, "end of input");
-        if (in_pad_ && *base_ == BASE_PADDING_CHAR) {
+        if (base_ == base_end_) {
+            // binary_from_baseX calls this operator when it needs more bits
+            // even if the internal iterator (base_) has reached its end
+            // (if that happens it means the input is an incomplete baseX
+            // string and should be rejected).  So this is the only point
+            // we can catch and reject this type of invalid input.
+            isc_throw(BadValue, "Unexpected end of input in BASE decoder");
+        }
+        if (in_pad_) {
             return (base_zero_code_);
         } else {
             return (*base_);
@@ -275,7 +296,7 @@ BaseNTransformer<BitsPerChunk, BaseZeroCode, Encoder, Decoder>::decode(
                 isc_throw(BadValue, "Too many " << algorithm
                           << " padding characters: " << input);
             }
-        } else if (!isspace((int)ch & 0xff)) {
+        } else if (ch < 0 || !isspace(ch)) {
             break;
         }
         ++srit;
diff --git a/src/lib/util/filename.cc b/src/lib/util/filename.cc
index 1f2e5db..d7da9c8 100644
--- a/src/lib/util/filename.cc
+++ b/src/lib/util/filename.cc
@@ -132,6 +132,24 @@ Filename::useAsDefault(const string& name) const {
     return (retstring);
 }
 
+void
+Filename::setDirectory(const std::string& new_directory) {
+    std::string directory(new_directory);
+
+    if (directory.length() > 0) {
+        // append '/' if necessary
+        size_t sep = directory.rfind('/');
+        if (sep == std::string::npos || sep < directory.size() - 1) {
+            directory += "/";
+        }
+    }
+    // and regenerate the full name
+    std::string full_name = directory + name_ + extension_;
+
+    directory_.swap(directory);
+    full_name_.swap(full_name);
+}
+
 
 } // namespace log
 } // namespace isc
diff --git a/src/lib/util/filename.h b/src/lib/util/filename.h
index 984ecb0..f625938 100644
--- a/src/lib/util/filename.h
+++ b/src/lib/util/filename.h
@@ -86,6 +86,13 @@ public:
         return (directory_);
     }
 
+    /// \brief Set directory for the file
+    ///
+    /// \param new_directory The directory to set. If this is an empty
+    ///        string, the directory this filename object currently
+    ///        has will be removed.
+    void setDirectory(const std::string& new_directory);
+
     /// \return Name of Given File Name
     std::string name() const {
         return (name_);
@@ -96,6 +103,11 @@ public:
         return (extension_);
     }
 
+    /// \return Name + extension of Given File Name
+    std::string nameAndExtension() const {
+        return (name_ + extension_);
+    }
+
     /// \brief Expand Name with Default
     ///
     /// A default file specified is supplied and used to fill in any missing
diff --git a/src/lib/util/io/Makefile.am b/src/lib/util/io/Makefile.am
index 9f06ef9..cbcd54d 100644
--- a/src/lib/util/io/Makefile.am
+++ b/src/lib/util/io/Makefile.am
@@ -1,5 +1,3 @@
-SUBDIRS = . tests
-
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 
 lib_LTLIBRARIES = libutil_io.la
@@ -15,4 +13,6 @@ libutil_io_python_la_LDFLAGS = -module
 libutil_io_python_la_SOURCES = fdshare_python.cc
 libutil_io_python_la_LIBADD = libutil_io.la
 libutil_io_python_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
-libutil_io_python_la_CXXFLAGS = $(AM_CXXFLAGS)
+# Note: PYTHON_CXXFLAGS may have some -Wno... workaround, which must be
+# placed after -Wextra defined in AM_CXXFLAGS
+libutil_io_python_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS)
diff --git a/src/lib/util/io/tests/Makefile.am b/src/lib/util/io/tests/Makefile.am
deleted file mode 100644
index 56d50cf..0000000
--- a/src/lib/util/io/tests/Makefile.am
+++ /dev/null
@@ -1,25 +0,0 @@
-CLEANFILES = *.gcno *.gcda
-
-AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
-AM_CXXFLAGS = $(B10_CXXFLAGS)
-
-if USE_STATIC_LINK
-AM_LDFLAGS = -static
-endif
-
-TESTS =
-if HAVE_GTEST
-TESTS += run_unittests
-run_unittests_SOURCES = run_unittests.cc
-run_unittests_SOURCES += fd_tests.cc
-run_unittests_SOURCES += fd_share_tests.cc
-
-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/io/libutil_io.la
-run_unittests_LDADD += \
-	$(top_builddir)/src/lib/util/unittests/libutil_unittests.la
-endif
-
-noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/util/io/tests/PARTIAL_PORT_ON_WINDOWS b/src/lib/util/io/tests/PARTIAL_PORT_ON_WINDOWS
deleted file mode 100644
index e69de29..0000000
diff --git a/src/lib/util/io/tests/fd_share_tests.cc b/src/lib/util/io/tests/fd_share_tests.cc
deleted file mode 100644
index 0902ce0..0000000
--- a/src/lib/util/io/tests/fd_share_tests.cc
+++ /dev/null
@@ -1,74 +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 "../fd.h"
-#include "../fd_share.h"
-
-#include <util/unittests/fork.h>
-
-#include <gtest/gtest.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <cstdio>
-
-using namespace isc::util::io;
-using namespace isc::util::unittests;
-
-namespace {
-
-// We test that we can transfer a pipe over other pipe
-TEST(FDShare, transfer) {
-    // Get a pipe and fork
-    int pipes[2];
-    ASSERT_NE(-1, socketpair(AF_UNIX, SOCK_STREAM, 0, pipes));
-    pid_t sender(fork());
-    ASSERT_NE(-1, sender);
-    if(sender) { // We are in parent
-        // Close the other side of pipe, we want only writible one
-        EXPECT_NE(-1, close(pipes[0]));
-        // Get a process to check data
-        int fd(0);
-        pid_t checker(check_output(&fd, "data", 4));
-        ASSERT_NE(-1, checker);
-        // Now, send the file descriptor, close it and close the pipe
-        EXPECT_NE(-1, send_fd(pipes[1], fd));
-        EXPECT_NE(-1, close(pipes[1]));
-        EXPECT_NE(-1, close(fd));
-        // Check both subprocesses ended well
-        EXPECT_TRUE(process_ok(sender));
-        EXPECT_TRUE(process_ok(checker));
-    } else { // We are in child. We do not use ASSERT here
-        // Close the write end, we only read
-        if(close(pipes[1])) {
-            exit(1);
-        }
-        // Get the file descriptor
-        int fd(recv_fd(pipes[0]));
-        if(fd == -1) {
-            exit(1);
-        }
-        // This pipe is not needed
-        if(close(pipes[0])) {
-            exit(1);
-        }
-        // Send "data" trough the received fd, close it and be done
-        if(!write_data(fd, "data", 4) || close(fd) == -1) {
-            exit(1);
-        }
-        exit(0);
-    }
-}
-
-}
diff --git a/src/lib/util/io/tests/fd_tests.cc b/src/lib/util/io/tests/fd_tests.cc
deleted file mode 100644
index a7e9654..0000000
--- a/src/lib/util/io/tests/fd_tests.cc
+++ /dev/null
@@ -1,70 +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 "../fd.h"
-
-#include <util/unittests/fork.h>
-
-#include <gtest/gtest.h>
-
-#ifdef _WIN32
-#define close(x) _close(x)
-#endif
-
-using namespace isc::util::io;
-using namespace isc::util::unittests;
-
-namespace {
-
-// Make sure the test is large enough and does not fit into one
-// read or write request
-const size_t TEST_DATA_SIZE = 8 * 1024 * 1024;
-
-class FDTest : public ::testing::Test {
-    public:
-        unsigned char *data, *buffer;
-        FDTest() :
-            // We do not care what is inside, we just need it to be the same
-            data(new unsigned char[TEST_DATA_SIZE]),
-            buffer(NULL)
-        { }
-        ~ FDTest() {
-            delete[] data;
-            delete[] buffer;
-        }
-};
-
-// Test we read what was sent
-TEST_F(FDTest, read) {
-    int read_pipe(0);
-    buffer = new unsigned char[TEST_DATA_SIZE];
-    pid_t feeder(provide_input(&read_pipe, data, TEST_DATA_SIZE));
-    ASSERT_NE(feeder, -1);
-    ssize_t received(read_data(read_pipe, buffer, TEST_DATA_SIZE));
-    EXPECT_TRUE(process_ok(feeder));
-    EXPECT_EQ(TEST_DATA_SIZE, received);
-    EXPECT_EQ(0, memcmp(data, buffer, received));
-}
-
-// Test we write the correct thing
-TEST_F(FDTest, write) {
-    int write_pipe(0);
-    pid_t checker(check_output(&write_pipe, data, TEST_DATA_SIZE));
-    ASSERT_NE(checker, -1);
-    EXPECT_TRUE(write_data(write_pipe, data, TEST_DATA_SIZE));
-    EXPECT_EQ(0, close(write_pipe));
-    EXPECT_TRUE(process_ok(checker));
-}
-
-}
diff --git a/src/lib/util/io/tests/run_unittests.cc b/src/lib/util/io/tests/run_unittests.cc
deleted file mode 100644
index e787ab1..0000000
--- a/src/lib/util/io/tests/run_unittests.cc
+++ /dev/null
@@ -1,22 +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 <gtest/gtest.h>
-
-int
-main(int argc, char *argv[]) {
-    ::testing::InitGoogleTest(&argc, argv);
-
-    return RUN_ALL_TESTS();
-}
diff --git a/src/lib/util/python/Makefile.am b/src/lib/util/python/Makefile.am
new file mode 100644
index 0000000..81d528c
--- /dev/null
+++ b/src/lib/util/python/Makefile.am
@@ -0,0 +1 @@
+noinst_SCRIPTS = gen_wiredata.py mkpywrapper.py
diff --git a/src/lib/util/python/gen_wiredata.py.in b/src/lib/util/python/gen_wiredata.py.in
new file mode 100755
index 0000000..8bd2b3c
--- /dev/null
+++ b/src/lib/util/python/gen_wiredata.py.in
@@ -0,0 +1,1232 @@
+#!@PYTHON@
+
+# Copyright (C) 2010  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""
+Generator of various types of DNS data in the hex format.
+
+This script reads a human readable specification file (called "spec
+file" hereafter) that defines some type of DNS data (an RDATA, an RR,
+or a complete message) and dumps the defined data to a separate file
+as a "wire format" sequence parsable by the
+UnitTestUtil::readWireData() function (currently defined as part of
+libdns++ tests).  Many DNS related tests involve wire format test
+data, so it will be convenient if we can define the data in a more
+intuitive way than writing the entire hex sequence by hand.
+
+Here is a simple example.  Consider the following spec file:
+
+  [custom]
+  sections: a
+  [a]
+  as_rr: True
+
+When the script reads this file, it detects the file specifies a single
+component (called "section" here) that consists of a single A RDATA,
+which must be dumped as an RR (not only the part of RDATA).  It then
+dumps the following content:
+
+  # A RR (QNAME=example.com Class=IN(1) TTL=86400 RDLEN=4)
+  076578616d706c6503636f6d00 0001 0001 00015180 0004
+  # Address=192.0.2.1
+  c0000201
+
+As can be seen, the script automatically completes all variable
+parameters of RRs: owner name, class, TTL, RDATA length and data.  For
+testing purposes many of these will be the same common one (like
+"example.com" or 192.0.2.1), so it would be convenient if we only have
+to specify non default parameters.  To change the RDATA (i.e., the
+IPv4 address), we should add the following line at the end of the spec
+file:
+
+  address: 192.0.2.2
+
+Then the last two lines of the output file will be as follows:
+
+  # Address=192.0.2.2
+  c0000202
+
+In some cases we would rather specify malformed data for tests.  This
+script has the ability to specify broken parameters for many types of
+data.  For example, we can generate data that would look like an A RR
+but the RDLEN is 3 by adding the following line to the spec file:
+
+  rdlen: 3
+
+Then the first two lines of the output file will be as follows:
+
+  # A RR (QNAME=example.com Class=IN(1) TTL=86400 RDLEN=3)
+  076578616d706c6503636f6d00 0001 0001 00015180 0003
+
+** USAGE **
+
+  gen_wiredata.py [-o output_file] spec_file
+
+If the -o option is missing, and if the spec_file has a suffix (such as
+in the form of "data.spec"), the output file name will be the prefix
+part of it (as in "data"); if -o is missing and the spec_file does not
+have a suffix, the script will fail.
+
+** SPEC FILE SYNTAX **
+
+A spec file accepted in this script should be in the form of a
+configuration file that is parsable by the Python's standard
+configparser module.  In short, it consists of sections; each section
+is identified in the form of [section_name] followed by "name: value"
+entries.  Lines beginning with # or ; will be treated as comments.
+Refer to the configparser module documentation for further details of
+the general syntax.
+
+This script has two major modes: the custom mode and the DNS query
+mode.  The former generates an arbitrary combination of DNS message
+header, question section, RDATAs or RRs.  It is mainly intended to
+generate a test data for a single type of RDATA or RR, or for
+complicated complete DNS messages.  The DNS query mode is actually a
+special case of the custom mode, which is a shortcut to generate a
+simple DNS query message (with or without EDNS).
+
+* Custom mode syntax *
+
+By default this script assumes the DNS query mode.  To specify the
+custom mode, there must be a special "custom" section in the spec
+file, which should contain 'sections' entry.  This value of this
+entryis colon-separated string fields, each of which is either
+"header", "question", "edns", "name", or a string specifying an RR
+type.  For RR types the string is lower-cased string mnemonic that
+identifies the type: 'a' for type A, 'ns' for type NS, and so on
+(note: in the current implementation it's case sensitive, and must be
+lower cased).
+
+Each of these fields is interpreted as a section name of the spec
+(configuration), and in that section parameters specific to the
+semantics of the field can be configured.
+
+A "header" section specifies the content of a DNS message header.
+See the documentation of the DNSHeader class of this module for
+configurable parameters.
+
+A "question" section specifies the content of a single question that
+is normally to be placed in the Question section of a DNS message.
+See the documentation of the DNSQuestion class of this module for
+configurable parameters.
+
+An "edns" section specifies the content of an EDNS OPT RR.  See the
+documentation of the EDNS class of this module for configurable
+parameters.
+
+A "name" section specifies a domain name with or without compression.
+This is specifically intended to be used for testing name related
+functionalities and would rarely be used with other sections.  See the
+documentation of the Name class of this module for configurable
+parameters.
+
+In a specific section for an RR or RDATA, possible entries depend on
+the type.  But there are some common configurable entries.  See the
+description of the RR class.  The most important one would be "as_rr".
+It controls whether the entry should be treated as an RR (with name,
+type, class and TTL) or only as an RDATA.  By default as_rr is
+"False", so if an entry is to be intepreted as an RR, an as_rr entry
+must be explicitly specified with a value of "True".
+
+Another common entry is "rdlen".  It specifies the RDLEN field value
+of the RR (note: this is included when the entry is interpreted as
+RDATA, too).  By default this value is automatically determined by the
+RR type and (it has a variable length) from other fields of RDATA, but
+as shown in the above example, it can be explicitly set, possibly to a
+bogus value for testing against invalid data.
+
+For type specific entries (and their defaults when provided), see the
+documentation of the corresponding Python class defined in this
+module.  In general, there should be a class named the same mnemonic
+of the corresponding RR type for each supported type, and they are a
+subclass of the RR class.  For example, the "NS" class is defined for
+RR type NS.
+
+Look again at the A RR example shown at the beginning of this
+description.  There's a "custom" section, which consists of a
+"sections" entry whose value is a single "a", which means the data to
+be generated is an A RR or RDATA.  There's a corresponding "a"
+section, which only specifies that it should be interpreted as an RR
+(all field values of the RR are derived from the default).
+
+If you want to generate a data sequence for two ore more RRs or
+RDATAs, you can specify them in the form of colon-separated fields for
+the "sections" entry.  For example, to generate a sequence of A and NS
+RRs in that order, the "custom" section would be something like this:
+
+  [custom]
+  sections: a:ns
+
+and there must be an "ns" section in addtion to "a".
+
+If a sequence of two or more RRs/RDATAs of the same RR type should be
+generated, these should be uniquely indexed with the "/" separator.
+For example, to generate two A RRs, the "custom" section would be as
+follows:
+
+  [custom]
+  sections: a/1:a/2
+
+and there must be "a/1" and "a/2" sections.
+
+Another practical example that would be used for many tests is to
+generate data for a complete DNS ressponse message.  The spec file of
+such an example configuration would look like as follows:
+
+  [custom]
+  sections: header:question:a
+  [header]
+  qr: 1
+  ancount: 1
+  [question]
+  [a]
+  as_rr: True
+
+With this configuration, this script will generate test data for a DNS
+response to a query for example.com/IN/A containing one corresponding
+A RR in the answer section.
+
+* DNS query mode syntax *
+
+If the spec file does not contain a "custom" section (that has a
+"sections" entry), this script assumes the DNS query mode.  This mode
+is actually a special case of custom mode; it implicitly assumes the
+"sections" entry whose value is "header:question:edns".
+
+In this mode it is expected that the spec file also contains at least
+a "header" and "question" sections, and optionally an "edns" section.
+But the script does not warn or fail even if the expected sections are
+missing.
+
+* Entry value types *
+
+As described above, a section of the spec file accepts entries
+specific to the semantics of the section.  They generally correspond
+to DNS message or RR fields.
+
+Many of them are expected to be integral values, for which either decimal or
+hexadecimal representation is accepted, for example:
+
+  rr_ttl: 3600
+  tag: 0x1234
+
+Some others are expected to be string.  A string value does not have
+to be quated:
+
+  address: 192.0.2.2
+
+but can also be quoated with single quotes:
+
+  address: '192.0.2.2'
+
+Note 1: a string that can be interpreted as an integer must be quated.
+For example, if you want to set a "string" entry to "3600", it should
+be:
+
+  string: '3600'
+
+instead of
+
+  string: 3600
+
+Note 2: a string enclosed with double quotes is not accepted:
+
+  # This doesn't work:
+  address: "192.0.2.2"
+
+In general, string values are converted to hexadecimal sequences
+according to the semantics of the entry.  For instance, a textual IPv4
+address in the above example will be converted to a hexadecimal
+sequence corresponding to a 4-byte integer.  So, in many cases, the
+acceptable syntax for a particular string entry value should be
+obvious from the context.  There are still some exceptional cases
+especially for complicated RR field values, for which the
+corresponding class documentation should be referenced.
+
+One special string syntax that would be worth noting is domain names,
+which would natually be used in many kinds of entries.  The simplest
+form of acceptable syntax is a textual representation of domain names
+such as "example.com" (note: names are always assumed to be
+"absolute", so the trailing dot can be omitted).  But a domain name in
+the wire format can also contain a compression pointer.  This script
+provides a simple support for name compression with a special notation
+of "ptr=nn" where nn is the numeric pointer value (decimal).  For example,
+if the NSDNAME field of an NS RDATA is specified as follows:
+
+  nsname: ns.ptr=12
+
+this script will generate the following output:
+
+  # NS name=ns.ptr=12
+  026e73c00c
+
+** EXTEND THE SCRIPT **
+
+This script is expected to be extended as we add more support for
+various types of RR.  It is encouraged to add support for a new type
+of RR to this script as we see the need for testing that type.  Here
+is a simple instruction of how to do that.
+
+Assume you are adding support for "FOO" RR.  Also assume that the FOO
+RDATA contains a single field named "value".
+
+What you are expected to do is as follows:
+
+- Define a new class named "FOO" inherited from the RR class.  Also
+  define a class variable named "value" for the FOO RDATA field (the
+  variable name can be different from the field name, but it's
+  convenient if it can be easily identifiable.) with an appropriate
+  default value (if possible):
+
+    class FOO(RR):
+        value = 10
+
+  The name of the variable will be (automatically) used as the
+  corresponding entry name in the spec file.  So, a spec file that
+  sets this field to 20 would look like this:
+
+    [foo]
+    value: 20
+
+- Define the "dump()" method for class FOO.  It must call
+  self.dump_header() (which is derived from class RR) at the
+  beginning.  It then prints the RDATA field values in an appropriate
+  way.  Assuming the value is a 16-bit integer field, a complete
+  dump() method would look like this:
+
+    def dump(self, f):
+        if self.rdlen is None:
+            self.rdlen = 2
+        self.dump_header(f, self.rdlen)
+        f.write('# Value=%d\\n' % (self.value))
+        f.write('%04x\\n' % (self.value))
+
+  The first f.write() call is not mandatory, but is encouraged to
+  be provided so that the generated files will be more human readable.
+  Depending on the complexity of the RDATA fields, the dump()
+  implementation would be more complicated.  In particular, if the
+  RDATA length is variable and the RDLEN field value is not specified
+  in the spec file, the dump() method is normally expected to
+  calculate the correct length and pass it to dump_header().  See the
+  implementation of various derived classes of class RR for actual
+  examples.
+"""
+
+import configparser, re, time, socket, sys
+from datetime import datetime
+from optparse import OptionParser
+
+re_hex = re.compile(r'^0x[0-9a-fA-F]+')
+re_decimal = re.compile(r'^\d+$')
+re_string = re.compile(r"\'(.*)\'$")
+
+dnssec_timefmt = '%Y%m%d%H%M%S'
+
+dict_qr = { 'query' : 0, 'response' : 1 }
+dict_opcode = { 'query' : 0, 'iquery' : 1, 'status' : 2, 'notify' : 4,
+                'update' : 5 }
+rdict_opcode = dict([(dict_opcode[k], k.upper()) for k in dict_opcode.keys()])
+dict_rcode = { 'noerror' : 0, 'formerr' : 1, 'servfail' : 2, 'nxdomain' : 3,
+               'notimp' : 4, 'refused' : 5, 'yxdomain' : 6, 'yxrrset' : 7,
+               'nxrrset' : 8, 'notauth' : 9, 'notzone' : 10 }
+rdict_rcode = dict([(dict_rcode[k], k.upper()) for k in dict_rcode.keys()])
+dict_rrtype = { 'none' : 0, 'a' : 1, 'ns' : 2, 'md' : 3, 'mf' : 4, 'cname' : 5,
+                'soa' : 6, 'mb' : 7, 'mg' : 8, 'mr' : 9, 'null' : 10,
+                'wks' : 11, 'ptr' : 12, 'hinfo' : 13, 'minfo' : 14, 'mx' : 15,
+                'txt' : 16, 'rp' : 17, 'afsdb' : 18, 'x25' : 19, 'isdn' : 20,
+                'rt' : 21, 'nsap' : 22, 'nsap_tr' : 23, 'sig' : 24, 'key' : 25,
+                'px' : 26, 'gpos' : 27, 'aaaa' : 28, 'loc' : 29, 'nxt' : 30,
+                'srv' : 33, 'naptr' : 35, 'kx' : 36, 'cert' : 37, 'a6' : 38,
+                'dname' : 39, 'opt' : 41, 'apl' : 42, 'ds' : 43, 'sshfp' : 44,
+                'ipseckey' : 45, 'rrsig' : 46, 'nsec' : 47, 'dnskey' : 48,
+                'dhcid' : 49, 'nsec3' : 50, 'nsec3param' : 51, 'hip' : 55,
+                'spf' : 99, 'unspec' : 103, 'tkey' : 249, 'tsig' : 250,
+                'dlv' : 32769, 'ixfr' : 251, 'axfr' : 252, 'mailb' : 253,
+                'maila' : 254, 'any' : 255 }
+rdict_rrtype = dict([(dict_rrtype[k], k.upper()) for k in dict_rrtype.keys()])
+dict_rrclass = { 'in' : 1, 'ch' : 3, 'hs' : 4, 'any' : 255 }
+rdict_rrclass = dict([(dict_rrclass[k], k.upper()) for k in \
+                          dict_rrclass.keys()])
+dict_algorithm = { 'rsamd5' : 1, 'dh' : 2, 'dsa' : 3, 'ecc' : 4,
+                   'rsasha1' : 5 }
+dict_nsec3_algorithm = { 'reserved' : 0, 'sha1' : 1 }
+rdict_algorithm = dict([(dict_algorithm[k], k.upper()) for k in \
+                            dict_algorithm.keys()])
+rdict_nsec3_algorithm = dict([(dict_nsec3_algorithm[k], k.upper()) for k in \
+                                  dict_nsec3_algorithm.keys()])
+
+header_xtables = { 'qr' : dict_qr, 'opcode' : dict_opcode,
+                   'rcode' : dict_rcode }
+question_xtables = { 'rrtype' : dict_rrtype, 'rrclass' : dict_rrclass }
+
+def parse_value(value, xtable = {}):
+    if re.search(re_hex, value):
+        return int(value, 16)
+    if re.search(re_decimal, value):
+        return int(value)
+    m = re.match(re_string, value)
+    if m:
+        return m.group(1)
+    lovalue = value.lower()
+    if lovalue in xtable:
+        return xtable[lovalue]
+    return value
+
+def code_totext(code, dict):
+    if code in dict.keys():
+        return dict[code] + '(' + str(code) + ')'
+    return str(code)
+
+def encode_name(name, absolute=True):
+    # make sure the name is dot-terminated.  duplicate dots will be ignored
+    # below.
+    name += '.'
+    labels = name.split('.')
+    wire = ''
+    for l in labels:
+        if len(l) > 4 and l[0:4] == 'ptr=':
+            # special meta-syntax for compression pointer
+            wire += '%04x' % (0xc000 | int(l[4:]))
+            break
+        if absolute or len(l) > 0:
+            wire += '%02x' % len(l)
+            wire += ''.join(['%02x' % ord(ch) for ch in l])
+        if len(l) == 0:
+            break
+    return wire
+
+def encode_string(name, len=None):
+    if type(name) is int and len is not None:
+        return '%0.*x' % (len * 2, name)
+    return ''.join(['%02x' % ord(ch) for ch in name])
+
+def count_namelabels(name):
+    if name == '.':             # special case
+        return 0
+    m = re.match('^(.*)\.$', name)
+    if m:
+        name = m.group(1)
+    return len(name.split('.'))
+
+def get_config(config, section, configobj, xtables = {}):
+    try:
+        for field in config.options(section):
+            value = config.get(section, field)
+            if field in xtables.keys():
+                xtable = xtables[field]
+            else:
+                xtable = {}
+            configobj.__dict__[field] = parse_value(value, xtable)
+    except configparser.NoSectionError:
+        return False
+    return True
+
+def print_header(f, input_file):
+    f.write('''###
+### This data file was auto-generated from ''' + input_file + '''
+###
+''')
+
+class Name:
+    '''Implements rendering a single domain name in the test data format.
+
+    Configurable parameter is as follows (see the description of the
+    same name of attribute for the default value):
+    - name (string): A textual representation of the name, such as
+      'example.com'.
+    - pointer (int): If specified, compression pointer will be
+      prepended to the generated data with the offset being the value
+      of this parameter.
+    '''
+
+    name = 'example.com'
+    pointer = None                # no compression by default
+    def dump(self, f):
+        name = self.name
+        if self.pointer is not None:
+            if len(name) > 0 and name[-1] != '.':
+                name += '.'
+            name += 'ptr=%d' % self.pointer
+        name_wire = encode_name(name)
+        f.write('\n# DNS Name: %s' % self.name)
+        if self.pointer is not None:
+            f.write(' + compression pointer: %d' % self.pointer)
+        f.write('\n')
+        f.write('%s' % name_wire)
+        f.write('\n')
+
+class DNSHeader:
+    '''Implements rendering a DNS Header section in the test data format.
+
+    Configurable parameter is as follows (see the description of the
+    same name of attribute for the default value):
+    - id (16-bit int):
+    - qr, aa, tc, rd, ra, ad, cd (0 or 1): Standard header bits as
+      defined in RFC1035 and RFC4035.  If set to 1, the corresponding
+      bit will be set; if set to 0, it will be cleared.
+    - mbz (0-3): The reserved field of the 3rd and 4th octets of the
+      header.
+    - rcode (4-bit int or string): The RCODE field.  If specified as a
+      string, it must be the commonly used textual mnemonic of the RCODEs
+      (NOERROR, FORMERR, etc, case insensitive).
+    - opcode (4-bit int or string): The OPCODE field.  If specified as
+      a string, it must be the commonly used textual mnemonic of the
+      OPCODEs (QUERY, NOTIFY, etc, case insensitive).
+    - qdcount, ancount, nscount, arcount (16-bit int): The QD/AN/NS/AR
+      COUNT fields, respectively.
+    '''
+
+    id = 0x1035
+    (qr, aa, tc, rd, ra, ad, cd) = 0, 0, 0, 0, 0, 0, 0
+    mbz = 0
+    rcode = 0                   # noerror
+    opcode = 0                  # query
+    (qdcount, ancount, nscount, arcount) = 1, 0, 0, 0
+
+    def dump(self, f):
+        f.write('\n# Header Section\n')
+        f.write('# ID=' + str(self.id))
+        f.write(' QR=' + ('Response' if self.qr else 'Query'))
+        f.write(' Opcode=' + code_totext(self.opcode, rdict_opcode))
+        f.write(' Rcode=' + code_totext(self.rcode, rdict_rcode))
+        f.write('%s' % (' AA' if self.aa else ''))
+        f.write('%s' % (' TC' if self.tc else ''))
+        f.write('%s' % (' RD' if self.rd else ''))
+        f.write('%s' % (' AD' if self.ad else ''))
+        f.write('%s' % (' CD' if self.cd else ''))
+        f.write('\n')
+        f.write('%04x ' % self.id)
+        flag_and_code = 0
+        flag_and_code |= (self.qr << 15 | self.opcode << 14 | self.aa << 10 |
+                          self.tc << 9 | self.rd << 8 | self.ra << 7 |
+                          self.mbz << 6 | self.ad << 5 | self.cd << 4 |
+                          self.rcode)
+        f.write('%04x\n' % flag_and_code)
+        f.write('# QDCNT=%d, ANCNT=%d, NSCNT=%d, ARCNT=%d\n' %
+                (self.qdcount, self.ancount, self.nscount, self.arcount))
+        f.write('%04x %04x %04x %04x\n' % (self.qdcount, self.ancount,
+                                           self.nscount, self.arcount))
+
+class DNSQuestion:
+    '''Implements rendering a DNS question in the test data format.
+
+    Configurable parameter is as follows (see the description of the
+    same name of attribute for the default value):
+    - name (string): The QNAME.  The string must be interpreted as a
+      valid domain name.
+    - rrtype (int or string): The question type.  If specified
+      as an integer, it must be the 16-bit RR type value of the
+      covered type.  If specifed as a string, it must be the textual
+      mnemonic of the type.
+    - rrclass (int or string): The question class.  If specified as an
+      integer, it must be the 16-bit RR class value of the covered
+      type.  If specifed as a string, it must be the textual mnemonic
+      of the class.
+    '''
+    name = 'example.com.'
+    rrtype = parse_value('A', dict_rrtype)
+    rrclass = parse_value('IN', dict_rrclass)
+
+    def dump(self, f):
+        f.write('\n# Question Section\n')
+        f.write('# QNAME=%s QTYPE=%s QCLASS=%s\n' %
+                (self.name,
+                 code_totext(self.rrtype, rdict_rrtype),
+                 code_totext(self.rrclass, rdict_rrclass)))
+        f.write(encode_name(self.name))
+        f.write(' %04x %04x\n' % (self.rrtype, self.rrclass))
+
+class EDNS:
+    '''Implements rendering EDNS OPT RR in the test data format.
+
+    Configurable parameter is as follows (see the description of the
+    same name of attribute for the default value):
+    - name (string): The owner name of the OPT RR.  The string must be
+      interpreted as a valid domain name.
+    - udpsize (16-bit int): The UDP payload size (set as the RR class)
+    - extrcode (8-bit int): The upper 8 bits of the extended RCODE.
+    - version (8-bit int): The EDNS version.
+    - do (int): The DNSSEC DO bit.  The bit will be set if this value
+      is 1; otherwise the bit will be unset.
+    - mbz (15-bit int): The rest of the flags field.
+    - rdlen (16-bit int): The RDLEN field.  Note: right now specifying
+      a non 0 value (except for making bogus data) doesn't make sense
+      because there is no way to configure RDATA.
+    '''
+    name = '.'
+    udpsize = 4096
+    extrcode = 0
+    version = 0
+    do = 0
+    mbz = 0
+    rdlen = 0
+    def dump(self, f):
+        f.write('\n# EDNS OPT RR\n')
+        f.write('# NAME=%s TYPE=%s UDPSize=%d ExtRcode=%s Version=%s DO=%d\n' %
+                (self.name, code_totext(dict_rrtype['opt'], rdict_rrtype),
+                 self.udpsize, self.extrcode, self.version,
+                 1 if self.do else 0))
+        
+        code_vers = (self.extrcode << 8) | (self.version & 0x00ff)
+        extflags = (self.do << 15) | (self.mbz & ~0x8000)
+        f.write('%s %04x %04x %04x %04x\n' %
+                (encode_name(self.name), dict_rrtype['opt'], self.udpsize,
+                 code_vers, extflags))
+        f.write('# RDLEN=%d\n' % self.rdlen)
+        f.write('%04x\n' % self.rdlen)
+
+class RR:
+    '''This is a base class for various types of RR test data.
+    For each RR type (A, AAAA, NS, etc), we define a derived class of RR
+    to dump type specific RDATA parameters.  This class defines parameters
+    common to all types of RDATA, namely the owner name, RR class and TTL.
+    The dump() method of derived classes are expected to call dump_header(),
+    whose default implementation is provided in this class.  This method
+    decides whether to dump the test data as an RR (with name, type, class)
+    or only as RDATA (with its length), and dumps the corresponding data
+    via the specified file object.
+
+    By convention we assume derived classes are named after the common
+    standard mnemonic of the corresponding RR types.  For example, the
+    derived class for the RR type SOA should be named "SOA".
+
+    Configurable parameters are as follows:
+    - as_rr (bool): Whether or not the data is to be dumped as an RR.
+      False by default.
+    - rr_name (string): The owner name of the RR.  The string must be
+      interpreted as a valid domain name (compression pointer can be
+      contained).  Default is 'example.com.'
+    - rr_class (string): The RR class of the data.  Only meaningful
+      when the data is dumped as an RR.  Default is 'IN'.
+    - rr_ttl (int): The TTL value of the RR.  Only meaningful when
+      the data is dumped as an RR.  Default is 86400 (1 day).
+    - rdlen (int): 16-bit RDATA length.  It can be None (i.e. omitted
+      in the spec file), in which case the actual length of the
+      generated RDATA is automatically determined and used; if
+      negative, the RDLEN field will be omitted from the output data.
+      (Note that omitting RDLEN with as_rr being True is mostly
+      meaningless, although the script doesn't complain about it).
+      Default is None.
+    '''
+
+    def __init__(self):
+        self.as_rr = False
+        # only when as_rr is True, same for class/TTL:
+        self.rr_name = 'example.com'
+        self.rr_class = 'IN'
+        self.rr_ttl = 86400
+        self.rdlen = None
+
+    def dump_header(self, f, rdlen):
+        type_txt = self.__class__.__name__
+        type_code = parse_value(type_txt, dict_rrtype)
+        rdlen_spec = ''
+        rdlen_data = ''
+        if rdlen >= 0:
+            rdlen_spec = ', RDLEN=%d' % rdlen
+            rdlen_data = '%04x' % rdlen
+        if self.as_rr:
+            rrclass = parse_value(self.rr_class, dict_rrclass)
+            f.write('\n# %s RR (QNAME=%s Class=%s TTL=%d%s)\n' %
+                    (type_txt, self.rr_name,
+                     code_totext(rrclass, rdict_rrclass), self.rr_ttl,
+                     rdlen_spec))
+            f.write('%s %04x %04x %08x %s\n' %
+                    (encode_name(self.rr_name), type_code, rrclass,
+                     self.rr_ttl, rdlen_data))
+        else:
+            f.write('\n# %s RDATA%s\n' % (type_txt, rdlen_spec))
+            f.write('%s\n' % rdlen_data)
+
+class A(RR):
+    '''Implements rendering A RDATA (of class IN) in the test data format.
+
+    Configurable parameter is as follows (see the description of the
+    same name of attribute for the default value):
+    - address (string): The address field.  This must be a valid textual
+      IPv4 address.
+    '''
+    RDLEN_DEFAULT = 4           # fixed by default
+    address = '192.0.2.1'
+
+    def dump(self, f):
+        if self.rdlen is None:
+            self.rdlen = self.RDLEN_DEFAULT
+        self.dump_header(f, self.rdlen)
+        f.write('# Address=%s\n' % (self.address))
+        bin_address = socket.inet_aton(self.address)
+        f.write('%02x%02x%02x%02x\n' % (bin_address[0], bin_address[1],
+                                        bin_address[2], bin_address[3]))
+
+class AAAA(RR):
+    '''Implements rendering AAAA RDATA (of class IN) in the test data
+    format.
+
+    Configurable parameter is as follows (see the description of the
+    same name of attribute for the default value):
+    - address (string): The address field.  This must be a valid textual
+      IPv6 address.
+    '''
+    RDLEN_DEFAULT = 16          # fixed by default
+    address = '2001:db8::1'
+
+    def dump(self, f):
+        if self.rdlen is None:
+            self.rdlen = self.RDLEN_DEFAULT
+        self.dump_header(f, self.rdlen)
+        f.write('# Address=%s\n' % (self.address))
+        bin_address = socket.inet_pton(socket.AF_INET6, self.address)
+        [f.write('%02x' % x) for x in bin_address]
+        f.write('\n')
+
+class NS(RR):
+    '''Implements rendering NS RDATA in the test data format.
+
+    Configurable parameter is as follows (see the description of the
+    same name of attribute for the default value):
+    - nsname (string): The NSDNAME field.  The string must be
+      interpreted as a valid domain name.
+    '''
+
+    nsname = 'ns.example.com'
+
+    def dump(self, f):
+        nsname_wire = encode_name(self.nsname)
+        if self.rdlen is None:
+            self.rdlen = len(nsname_wire) / 2
+        self.dump_header(f, self.rdlen)
+        f.write('# NS name=%s\n' % (self.nsname))
+        f.write('%s\n' % nsname_wire)
+
+class SOA(RR):
+    '''Implements rendering SOA RDATA in the test data format.
+
+    Configurable parameters are as follows (see the description of the
+    same name of attribute for the default value):
+    - mname/rname (string): The MNAME/RNAME fields, respectively.  The
+      string must be interpreted as a valid domain name.
+    - serial (32-bit int): The SERIAL field
+    - refresh (32-bit int): The REFRESH field
+    - retry (32-bit int): The RETRY field
+    - expire (32-bit int): The EXPIRE field
+    - minimum (32-bit int): The MINIMUM field
+    '''
+
+    mname = 'ns.example.com'
+    rname = 'root.example.com'
+    serial = 2010012601
+    refresh = 3600
+    retry = 300
+    expire = 3600000
+    minimum = 1200
+    def dump(self, f):
+        mname_wire = encode_name(self.mname)
+        rname_wire = encode_name(self.rname)
+        if self.rdlen is None:
+            self.rdlen = int(20 + len(mname_wire) / 2 + len(str(rname_wire)) / 2)
+        self.dump_header(f, self.rdlen)
+        f.write('# NNAME=%s RNAME=%s\n' % (self.mname, self.rname))
+        f.write('%s %s\n' % (mname_wire, rname_wire))
+        f.write('# SERIAL(%d) REFRESH(%d) RETRY(%d) EXPIRE(%d) MINIMUM(%d)\n' %
+                (self.serial, self.refresh, self.retry, self.expire,
+                 self.minimum))
+        f.write('%08x %08x %08x %08x %08x\n' % (self.serial, self.refresh,
+                                                self.retry, self.expire,
+                                                self.minimum))
+
+class TXT(RR):
+    '''Implements rendering TXT RDATA in the test data format.
+
+    Configurable parameters are as follows (see the description of the
+    same name of attribute for the default value):
+    - nstring (int): number of character-strings
+    - stringlenN (int) (int, N = 0, ..., nstring-1): the length of the
+      N-th character-string.
+    - stringN (string, N = 0, ..., nstring-1): the N-th
+      character-string.
+    - stringlen (int): the default string.  If nstring >= 1 and the
+      corresponding stringlenN isn't specified in the spec file, this
+      value will be used.  If this parameter isn't specified either,
+      the length of the string will be used.  Note that it means
+      this parameter (or any stringlenN) doesn't have to be specified
+      unless you want to intentially build a broken character string.
+    - string (string): the default string.  If nstring >= 1 and the
+      corresponding stringN isn't specified in the spec file, this
+      string will be used.
+    '''
+
+    nstring = 1
+    stringlen = None
+    string = 'Test String'
+
+    def dump(self, f):
+        stringlen_list = []
+        string_list = []
+        wirestring_list = []
+        for i in range(0, self.nstring):
+            key_string = 'string' + str(i)
+            if key_string in self.__dict__:
+                string_list.append(self.__dict__[key_string])
+            else:
+                string_list.append(self.string)
+            wirestring_list.append(encode_string(string_list[-1]))
+            key_stringlen = 'stringlen' + str(i)
+            if key_stringlen in self.__dict__:
+                stringlen_list.append(self.__dict__[key_stringlen])
+            else:
+                stringlen_list.append(self.stringlen)
+            if stringlen_list[-1] is None:
+                stringlen_list[-1] = int(len(wirestring_list[-1]) / 2)
+        if self.rdlen is None:
+            self.rdlen = int(len(''.join(wirestring_list)) / 2) + self.nstring
+        self.dump_header(f, self.rdlen)
+        for i in range(0, self.nstring):
+            f.write('# String Len=%d, String=\"%s\"\n' %
+                    (stringlen_list[i], string_list[i]))
+            f.write('%02x%s%s\n' % (stringlen_list[i],
+                                    ' ' if len(wirestring_list[i]) > 0 else '',
+                                    wirestring_list[i]))
+
+class RP(RR):
+    '''Implements rendering RP RDATA in the test data format.
+
+    Configurable parameters are as follows (see the description of the
+    same name of attribute for the default value):
+    - mailbox (string): The mailbox field.
+    - text (string): The text field.
+    These strings must be interpreted as a valid domain name.
+    '''
+    mailbox = 'root.example.com'
+    text = 'rp-text.example.com'
+    def dump(self, f):
+        mailbox_wire = encode_name(self.mailbox)
+        text_wire = encode_name(self.text)
+        if self.rdlen is None:
+            self.rdlen = (len(mailbox_wire) + len(text_wire)) / 2
+        else:
+            self.rdlen = int(self.rdlen)
+        self.dump_header(f, self.rdlen)
+        f.write('# MAILBOX=%s TEXT=%s\n' % (self.mailbox, self.text))
+        f.write('%s %s\n' % (mailbox_wire, text_wire))
+
+class MINFO(RR):
+    '''Implements rendering MINFO RDATA in the test data format.
+
+    Configurable parameters are as follows (see the description of the
+    same name of attribute for the default value):
+    - rmailbox (string): The rmailbox field.
+    - emailbox (string): The emailbox field.
+    These strings must be interpreted as a valid domain name.
+    '''
+    rmailbox = 'rmailbox.example.com'
+    emailbox = 'emailbox.example.com'
+    def dump(self, f):
+        rmailbox_wire = encode_name(self.rmailbox)
+        emailbox_wire = encode_name(self.emailbox)
+        if self.rdlen is None:
+            self.rdlen = (len(rmailbox_wire) + len(emailbox_wire)) / 2
+        else:
+            self.rdlen = int(self.rdlen)
+        self.dump_header(f, self.rdlen)
+        f.write('# RMAILBOX=%s EMAILBOX=%s\n' % (self.rmailbox, self.emailbox))
+        f.write('%s %s\n' % (rmailbox_wire, emailbox_wire))
+
+class AFSDB(RR):
+    '''Implements rendering AFSDB RDATA in the test data format.
+
+    Configurable parameters are as follows (see the description of the
+    same name of attribute for the default value):
+    - subtype (16 bit int): The subtype field.
+    - server (string): The server field.
+    The string must be interpreted as a valid domain name.
+    '''
+    subtype = 1
+    server = 'afsdb.example.com'
+    def dump(self, f):
+        server_wire = encode_name(self.server)
+        if self.rdlen is None:
+            self.rdlen = 2 + len(server_wire) / 2
+        else:
+            self.rdlen = int(self.rdlen)
+        self.dump_header(f, self.rdlen)
+        f.write('# SUBTYPE=%d SERVER=%s\n' % (self.subtype, self.server))
+        f.write('%04x %s\n' % (self.subtype, server_wire))
+
+class NSECBASE(RR):
+    '''Implements rendering NSEC/NSEC3 type bitmaps commonly used for
+    these RRs.  The NSEC and NSEC3 classes will be inherited from this
+    class.
+
+    Configurable parameters are as follows (see the description of the
+    same name of attribute for the default value):
+    - nbitmap (int): The number of type bitmaps.
+    The following three define the bitmaps.  If suffixed with "N"
+    (0 <= N < nbitmaps), it means the definition for the N-th bitmap.
+    If there is no suffix (e.g., just "block", it means the default
+    for any unspecified values)
+    - block[N] (8-bit int): The Window Block.
+    - maplen[N] (8-bit int): The Bitmap Length.  The default "maplen"
+      can also be unspecified (with being set to None), in which case
+      the corresponding length will be calculated from the bitmap.
+    - bitmap[N] (string): The Bitmap.  This must be the hexadecimal
+      representation of the bitmap field.  For example, for a bitmap
+      where the 7th and 15th bits (and only these bits) are set, it
+      must be '0101'.  Note also that the value must be quated with
+      single quatations because it could also be interpreted as an
+      integer.
+    '''
+    nbitmap = 1                 # number of bitmaps
+    block = 0
+    maplen = None              # default bitmap length, auto-calculate
+    bitmap = '040000000003'     # an arbtrarily chosen bitmap sample
+    def dump(self, f):
+        # first, construct the bitmpa data
+        block_list = []
+        maplen_list = []
+        bitmap_list = []
+        for i in range(0, self.nbitmap):
+            key_bitmap = 'bitmap' + str(i)
+            if key_bitmap in self.__dict__:
+                bitmap_list.append(self.__dict__[key_bitmap])
+            else:
+                bitmap_list.append(self.bitmap)
+            key_maplen = 'maplen' + str(i)
+            if key_maplen in self.__dict__:
+                maplen_list.append(self.__dict__[key_maplen])
+            else:
+                maplen_list.append(self.maplen)
+            if maplen_list[-1] is None: # calculate it if not specified
+                maplen_list[-1] = int(len(bitmap_list[-1]) / 2)
+            key_block = 'block' + str(i)
+            if key_block in self.__dict__:
+               block_list.append(self.__dict__[key_block])
+            else:
+                block_list.append(self.block)
+
+        # dump RR-type specific part (NSEC or NSEC3)
+        self.dump_fixedpart(f, 2 * self.nbitmap + \
+                                int(len(''.join(bitmap_list)) / 2))
+
+        # dump the bitmap
+        for i in range(0, self.nbitmap):
+            f.write('# Bitmap: Block=%d, Length=%d\n' %
+                    (block_list[i], maplen_list[i]))
+            f.write('%02x %02x %s\n' %
+                    (block_list[i], maplen_list[i], bitmap_list[i]))
+
+class NSEC(NSECBASE):
+    '''Implements rendering NSEC RDATA in the test data format.
+
+    Configurable parameters are as follows (see the description of the
+    same name of attribute for the default value):
+    - Type bitmap related parameters: see class NSECBASE
+    - nextname (string): The Next Domain Name field.  The string must be
+      interpreted as a valid domain name.
+    '''
+
+    nextname = 'next.example.com'
+    def dump_fixedpart(self, f, bitmap_totallen):
+        name_wire = encode_name(self.nextname)
+        if self.rdlen is None:
+            # if rdlen needs to be calculated, it must be based on the bitmap
+            # length, because the configured maplen can be fake.
+            self.rdlen = int(len(name_wire) / 2) + bitmap_totallen
+        self.dump_header(f, self.rdlen)
+        f.write('# Next Name=%s (%d bytes)\n' % (self.nextname,
+                                                 int(len(name_wire) / 2)))
+        f.write('%s\n' % name_wire)
+
+class NSEC3(NSECBASE):
+    '''Implements rendering NSEC3 RDATA in the test data format.
+
+    Configurable parameters are as follows (see the description of the
+    same name of attribute for the default value):
+    - Type bitmap related parameters: see class NSECBASE
+    - hashalg (8-bit int): The Hash Algorithm field.  Note that
+      currently the only defined algorithm is SHA-1, for which a value
+      of 1 will be used, and it's the default.  So this implementation
+      does not support any string representation right now.
+    - optout (bool): The Opt-Out flag of the Flags field.
+    - mbz (7-bit int): The rest of the Flags field.  This value will
+      be left shifted for 1 bit and then OR-ed with optout to
+      construct the complete Flags field.
+    - iterations (16-bit int): The Iterations field.
+    - saltlen (int): The Salt Length field.
+    - salt (string): The Salt field.  It is converted to a sequence of
+      ascii codes and its hexadecimal representation will be used.
+    - hashlen (int): The Hash Length field.
+    - hash (string): The Next Hashed Owner Name field.  This parameter
+      is interpreted as "salt".
+    '''
+
+    hashalg = 1                 # SHA-1
+    optout = False              # opt-out flag
+    mbz = 0                     # other flag fields (none defined yet)
+    iterations = 1
+    saltlen = 5
+    salt = 's' * saltlen
+    hashlen = 20
+    hash = 'h' * hashlen
+    def dump_fixedpart(self, f, bitmap_totallen):
+        if self.rdlen is None:
+            # if rdlen needs to be calculated, it must be based on the bitmap
+            # length, because the configured maplen can be fake.
+            self.rdlen = 4 + 1 + len(self.salt) + 1 + len(self.hash) \
+                + bitmap_totallen
+        self.dump_header(f, self.rdlen)
+        optout_val = 1 if self.optout else 0
+        f.write('# Hash Alg=%s, Opt-Out=%d, Other Flags=%0x, Iterations=%d\n' %
+                (code_totext(self.hashalg, rdict_nsec3_algorithm),
+                 optout_val, self.mbz, self.iterations))
+        f.write('%02x %02x %04x\n' %
+                (self.hashalg, (self.mbz << 1) | optout_val, self.iterations))
+        f.write("# Salt Len=%d, Salt='%s'\n" % (self.saltlen, self.salt))
+        f.write('%02x%s%s\n' % (self.saltlen,
+                                ' ' if len(self.salt) > 0 else '',
+                                encode_string(self.salt)))
+        f.write("# Hash Len=%d, Hash='%s'\n" % (self.hashlen, self.hash))
+        f.write('%02x%s%s\n' % (self.hashlen,
+                                ' ' if len(self.hash) > 0 else '',
+                                encode_string(self.hash)))
+
+class RRSIG(RR):
+    '''Implements rendering RRSIG RDATA in the test data format.
+
+    Configurable parameters are as follows (see the description of the
+    same name of attribute for the default value):
+    - covered (int or string): The Type Covered field.  If specified
+      as an integer, it must be the 16-bit RR type value of the
+      covered type.  If specifed as a string, it must be the textual
+      mnemonic of the type.
+    - algorithm (int or string): The Algorithm field.   If specified
+      as an integer, it must be the 8-bit algorithm number as defined
+      in RFC4034.  If specifed as a string, it must be one of the keys
+      of dict_algorithm (case insensitive).
+    - labels (int): The Labels field.  If omitted (the corresponding
+      variable being set to None), the number of labels of "signer"
+      (excluding the trailing null label as specified in RFC4034) will
+      be used.
+    - originalttl (32-bit int): The Original TTL field.
+    - expiration (32-bit int): The Expiration TTL field.
+    - inception (32-bit int): The Inception TTL field.
+    - tag (16-bit int): The Key Tag field.
+    - signer (string): The Signer's Name field.  The string must be
+      interpreted as a valid domain name.
+    - signature (int): The Signature field.  Right now only a simple
+      integer form is supported.  A prefix of "0" will be prepended if
+      the resulting hexadecimal representation consists of an odd
+      number of characters.
+    '''
+
+    covered = 'A'
+    algorithm = 'RSASHA1'
+    labels = None                 # auto-calculate (#labels of signer)
+    originalttl = 3600
+    expiration = int(time.mktime(datetime.strptime('20100131120000',
+                                                   dnssec_timefmt).timetuple()))
+    inception = int(time.mktime(datetime.strptime('20100101120000',
+                                                  dnssec_timefmt).timetuple()))
+    tag = 0x1035
+    signer = 'example.com'
+    signature = 0x123456789abcdef123456789abcdef
+
+    def dump(self, f):
+        name_wire = encode_name(self.signer)
+        sig_wire = '%x' % self.signature
+        if len(sig_wire) % 2 != 0:
+            sig_wire = '0' + sig_wire
+        if self.rdlen is None:
+            self.rdlen = int(18 + len(name_wire) / 2 + len(str(sig_wire)) / 2)
+        self.dump_header(f, self.rdlen)
+
+        if type(self.covered) is str:
+            self.covered = dict_rrtype[self.covered.lower()]
+        if type(self.algorithm) is str:
+            self.algorithm = dict_algorithm[self.algorithm.lower()]
+        if self.labels is None:
+            self.labels = count_namelabels(self.signer)
+        f.write('# Covered=%s Algorithm=%s Labels=%d OrigTTL=%d\n' %
+                (code_totext(self.covered, rdict_rrtype),
+                 code_totext(self.algorithm, rdict_algorithm), self.labels,
+                 self.originalttl))
+        f.write('%04x %02x %02x %08x\n' % (self.covered, self.algorithm,
+                                           self.labels, self.originalttl))
+        f.write('# Expiration=%s, Inception=%s\n' %
+                (str(self.expiration), str(self.inception)))
+        f.write('%08x %08x\n' % (self.expiration, self.inception))
+        f.write('# Tag=%d Signer=%s and Signature\n' % (self.tag, self.signer))
+        f.write('%04x %s %s\n' % (self.tag, name_wire, sig_wire))
+
+class TSIG(RR):
+    '''Implements rendering TSIG RDATA in the test data format.
+
+    As a meta RR type TSIG uses some non common parameters.  This
+    class overrides some of the default attributes of the RR class
+    accordingly:
+    - rr_class is set to 'ANY'
+    - rr_ttl is set to 0
+    Like other derived classes these can be overridden via the spec
+    file.
+
+    Other configurable parameters are as follows (see the description
+    of the same name of attribute for the default value):
+    - algorithm (string): The Algorithm Name field.  The value is
+      generally interpreted as a domain name string, and will
+      typically be one of the standard algorithm names defined in
+      RFC4635.  For convenience, however, a shortcut value "hmac-md5"
+      is allowed instead of the standard "hmac-md5.sig-alg.reg.int".
+    - time_signed (48-bit int): The Time Signed field.
+    - fudge (16-bit int): The Fudge field.
+    - mac_size (int): The MAC Size field.  If omitted, the common value
+      determined by the algorithm will be used.
+    - mac (int or string): The MAC field.  If specified as an integer,
+      the integer value is used as the MAC, possibly with prepended
+      0's so that the total length will be mac_size.  If specifed as a
+      string, it is converted to a sequence of ascii codes and its
+      hexadecimal representation will be used.  So, for example, if
+      "mac" is set to 'abc', it will be converted to '616263'.  Note
+      that in this case the length of "mac" may not be equal to
+      mac_size.  If unspecified, the mac_size number of '78' (ascii
+      code of 'x') will be used.
+    - original_id (16-bit int): The Original ID field.
+    - error (16-bit int): The Error field.
+    - other_len (int): The Other Len field.
+    - other_data (int or string): The Other Data field.  This is
+      interpreted just like "mac" except that other_len is used
+      instead of mac_size.  If unspecified this will be empty unless
+      the "error" is set to 18 (which means the "BADTIME" error), in
+      which case a hexadecimal representation of "time_signed + fudge
+      + 1" will be used.
+    '''
+
+    algorithm = 'hmac-sha256'
+    time_signed = 1286978795    # arbitrarily chosen default
+    fudge = 300
+    mac_size = None             # use a common value for the algorithm
+    mac = None                  # use 'x' * mac_size
+    original_id = 2845          # arbitrarily chosen default
+    error = 0
+    other_len = None         # 6 if error is BADTIME; otherwise 0
+    other_data = None        # use time_signed + fudge + 1 for BADTIME
+    dict_macsize = { 'hmac-md5' : 16, 'hmac-sha1' : 20, 'hmac-sha256' : 32 }
+
+    # TSIG has some special defaults
+    def __init__(self):
+        super().__init__()
+        self.rr_class = 'ANY'
+        self.rr_ttl = 0
+
+    def dump(self, f):
+        if str(self.algorithm) == 'hmac-md5':
+            name_wire = encode_name('hmac-md5.sig-alg.reg.int')
+        else:
+            name_wire = encode_name(self.algorithm)
+        mac_size = self.mac_size
+        if mac_size is None:
+            if self.algorithm in self.dict_macsize.keys():
+                mac_size = self.dict_macsize[self.algorithm]
+            else:
+                raise RuntimeError('TSIG Mac Size cannot be determined')
+        mac = encode_string('x' * mac_size) if self.mac is None else \
+            encode_string(self.mac, mac_size)
+        other_len = self.other_len
+        if other_len is None:
+            # 18 = BADTIME
+            other_len = 6 if self.error == 18 else 0
+        other_data = self.other_data
+        if other_data is None:
+            other_data = '%012x' % (self.time_signed + self.fudge + 1) \
+                if self.error == 18 else ''
+        else:
+            other_data = encode_string(self.other_data, other_len)
+        if self.rdlen is None:
+            self.rdlen = int(len(name_wire) / 2 + 16 + len(mac) / 2 + \
+                                 len(other_data) / 2)
+        self.dump_header(f, self.rdlen)
+        f.write('# Algorithm=%s Time-Signed=%d Fudge=%d\n' %
+                (self.algorithm, self.time_signed, self.fudge))
+        f.write('%s %012x %04x\n' % (name_wire, self.time_signed, self.fudge))
+        f.write('# MAC Size=%d MAC=(see hex)\n' % mac_size)
+        f.write('%04x%s\n' % (mac_size, ' ' + mac if len(mac) > 0 else ''))
+        f.write('# Original-ID=%d Error=%d\n' % (self.original_id, self.error))
+        f.write('%04x %04x\n' %  (self.original_id, self.error))
+        f.write('# Other-Len=%d Other-Data=(see hex)\n' % other_len)
+        f.write('%04x%s\n' % (other_len,
+                              ' ' + other_data if len(other_data) > 0 else ''))
+
+# Build section-class mapping
+config_param = { 'name' : (Name, {}),
+                 'header' : (DNSHeader, header_xtables),
+                 'question' : (DNSQuestion, question_xtables),
+                 'edns' : (EDNS, {}) }
+for rrtype in dict_rrtype.keys():
+    # For any supported RR types add the tuple of (RR_CLASS, {}).
+    # We expect KeyError as not all the types are supported, and simply
+    # ignore them.
+    try:
+        cur_mod = sys.modules[__name__]
+        config_param[rrtype] = (cur_mod.__dict__[rrtype.upper()], {})
+    except KeyError:
+        pass
+
+def get_config_param(section):
+    s = section
+    m = re.match('^([^:]+)/\d+$', section)
+    if m:
+        s = m.group(1)
+    return config_param[s]
+
+usage = '''usage: %prog [options] input_file'''
+
+if __name__ == "__main__":
+    parser = OptionParser(usage=usage)
+    parser.add_option('-o', '--output', action='store', dest='output',
+                      default=None, metavar='FILE',
+                      help='output file name [default: prefix of input_file]')
+    (options, args) = parser.parse_args()
+
+    if len(args) == 0:
+        parser.error('input file is missing')
+    configfile = args[0]
+
+    outputfile = options.output
+    if not outputfile:
+        m = re.match('(.*)\.[^.]+$', configfile)
+        if m:
+            outputfile = m.group(1)
+        else:
+            raise ValueError('output file is not specified and input file is not in the form of "output_file.suffix"')
+
+    config = configparser.SafeConfigParser()
+    config.read(configfile)
+
+    output = open(outputfile, 'w')
+
+    print_header(output, configfile)
+
+    # First try the 'custom' mode; if it fails assume the query mode.
+    try:
+        sections = config.get('custom', 'sections').split(':')
+    except configparser.NoSectionError:
+        sections = ['header', 'question', 'edns']
+
+    for s in sections:
+        section_param = get_config_param(s)
+        (obj, xtables) = (section_param[0](), section_param[1])
+        if get_config(config, s, obj, xtables):
+            obj.dump(output)
+
+    output.close()
diff --git a/src/lib/util/python/gen_wiredata.py.win32 b/src/lib/util/python/gen_wiredata.py.win32
new file mode 100755
index 0000000..214353f
--- /dev/null
+++ b/src/lib/util/python/gen_wiredata.py.win32
@@ -0,0 +1,1232 @@
+#!/usr/bin/python
+
+# Copyright (C) 2010  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""
+Generator of various types of DNS data in the hex format.
+
+This script reads a human readable specification file (called "spec
+file" hereafter) that defines some type of DNS data (an RDATA, an RR,
+or a complete message) and dumps the defined data to a separate file
+as a "wire format" sequence parsable by the
+UnitTestUtil::readWireData() function (currently defined as part of
+libdns++ tests).  Many DNS related tests involve wire format test
+data, so it will be convenient if we can define the data in a more
+intuitive way than writing the entire hex sequence by hand.
+
+Here is a simple example.  Consider the following spec file:
+
+  [custom]
+  sections: a
+  [a]
+  as_rr: True
+
+When the script reads this file, it detects the file specifies a single
+component (called "section" here) that consists of a single A RDATA,
+which must be dumped as an RR (not only the part of RDATA).  It then
+dumps the following content:
+
+  # A RR (QNAME=example.com Class=IN(1) TTL=86400 RDLEN=4)
+  076578616d706c6503636f6d00 0001 0001 00015180 0004
+  # Address=192.0.2.1
+  c0000201
+
+As can be seen, the script automatically completes all variable
+parameters of RRs: owner name, class, TTL, RDATA length and data.  For
+testing purposes many of these will be the same common one (like
+"example.com" or 192.0.2.1), so it would be convenient if we only have
+to specify non default parameters.  To change the RDATA (i.e., the
+IPv4 address), we should add the following line at the end of the spec
+file:
+
+  address: 192.0.2.2
+
+Then the last two lines of the output file will be as follows:
+
+  # Address=192.0.2.2
+  c0000202
+
+In some cases we would rather specify malformed data for tests.  This
+script has the ability to specify broken parameters for many types of
+data.  For example, we can generate data that would look like an A RR
+but the RDLEN is 3 by adding the following line to the spec file:
+
+  rdlen: 3
+
+Then the first two lines of the output file will be as follows:
+
+  # A RR (QNAME=example.com Class=IN(1) TTL=86400 RDLEN=3)
+  076578616d706c6503636f6d00 0001 0001 00015180 0003
+
+** USAGE **
+
+  gen_wiredata.py [-o output_file] spec_file
+
+If the -o option is missing, and if the spec_file has a suffix (such as
+in the form of "data.spec"), the output file name will be the prefix
+part of it (as in "data"); if -o is missing and the spec_file does not
+have a suffix, the script will fail.
+
+** SPEC FILE SYNTAX **
+
+A spec file accepted in this script should be in the form of a
+configuration file that is parsable by the Python's standard
+configparser module.  In short, it consists of sections; each section
+is identified in the form of [section_name] followed by "name: value"
+entries.  Lines beginning with # or ; will be treated as comments.
+Refer to the configparser module documentation for further details of
+the general syntax.
+
+This script has two major modes: the custom mode and the DNS query
+mode.  The former generates an arbitrary combination of DNS message
+header, question section, RDATAs or RRs.  It is mainly intended to
+generate a test data for a single type of RDATA or RR, or for
+complicated complete DNS messages.  The DNS query mode is actually a
+special case of the custom mode, which is a shortcut to generate a
+simple DNS query message (with or without EDNS).
+
+* Custom mode syntax *
+
+By default this script assumes the DNS query mode.  To specify the
+custom mode, there must be a special "custom" section in the spec
+file, which should contain 'sections' entry.  This value of this
+entryis colon-separated string fields, each of which is either
+"header", "question", "edns", "name", or a string specifying an RR
+type.  For RR types the string is lower-cased string mnemonic that
+identifies the type: 'a' for type A, 'ns' for type NS, and so on
+(note: in the current implementation it's case sensitive, and must be
+lower cased).
+
+Each of these fields is interpreted as a section name of the spec
+(configuration), and in that section parameters specific to the
+semantics of the field can be configured.
+
+A "header" section specifies the content of a DNS message header.
+See the documentation of the DNSHeader class of this module for
+configurable parameters.
+
+A "question" section specifies the content of a single question that
+is normally to be placed in the Question section of a DNS message.
+See the documentation of the DNSQuestion class of this module for
+configurable parameters.
+
+An "edns" section specifies the content of an EDNS OPT RR.  See the
+documentation of the EDNS class of this module for configurable
+parameters.
+
+A "name" section specifies a domain name with or without compression.
+This is specifically intended to be used for testing name related
+functionalities and would rarely be used with other sections.  See the
+documentation of the Name class of this module for configurable
+parameters.
+
+In a specific section for an RR or RDATA, possible entries depend on
+the type.  But there are some common configurable entries.  See the
+description of the RR class.  The most important one would be "as_rr".
+It controls whether the entry should be treated as an RR (with name,
+type, class and TTL) or only as an RDATA.  By default as_rr is
+"False", so if an entry is to be intepreted as an RR, an as_rr entry
+must be explicitly specified with a value of "True".
+
+Another common entry is "rdlen".  It specifies the RDLEN field value
+of the RR (note: this is included when the entry is interpreted as
+RDATA, too).  By default this value is automatically determined by the
+RR type and (it has a variable length) from other fields of RDATA, but
+as shown in the above example, it can be explicitly set, possibly to a
+bogus value for testing against invalid data.
+
+For type specific entries (and their defaults when provided), see the
+documentation of the corresponding Python class defined in this
+module.  In general, there should be a class named the same mnemonic
+of the corresponding RR type for each supported type, and they are a
+subclass of the RR class.  For example, the "NS" class is defined for
+RR type NS.
+
+Look again at the A RR example shown at the beginning of this
+description.  There's a "custom" section, which consists of a
+"sections" entry whose value is a single "a", which means the data to
+be generated is an A RR or RDATA.  There's a corresponding "a"
+section, which only specifies that it should be interpreted as an RR
+(all field values of the RR are derived from the default).
+
+If you want to generate a data sequence for two ore more RRs or
+RDATAs, you can specify them in the form of colon-separated fields for
+the "sections" entry.  For example, to generate a sequence of A and NS
+RRs in that order, the "custom" section would be something like this:
+
+  [custom]
+  sections: a:ns
+
+and there must be an "ns" section in addtion to "a".
+
+If a sequence of two or more RRs/RDATAs of the same RR type should be
+generated, these should be uniquely indexed with the "/" separator.
+For example, to generate two A RRs, the "custom" section would be as
+follows:
+
+  [custom]
+  sections: a/1:a/2
+
+and there must be "a/1" and "a/2" sections.
+
+Another practical example that would be used for many tests is to
+generate data for a complete DNS ressponse message.  The spec file of
+such an example configuration would look like as follows:
+
+  [custom]
+  sections: header:question:a
+  [header]
+  qr: 1
+  ancount: 1
+  [question]
+  [a]
+  as_rr: True
+
+With this configuration, this script will generate test data for a DNS
+response to a query for example.com/IN/A containing one corresponding
+A RR in the answer section.
+
+* DNS query mode syntax *
+
+If the spec file does not contain a "custom" section (that has a
+"sections" entry), this script assumes the DNS query mode.  This mode
+is actually a special case of custom mode; it implicitly assumes the
+"sections" entry whose value is "header:question:edns".
+
+In this mode it is expected that the spec file also contains at least
+a "header" and "question" sections, and optionally an "edns" section.
+But the script does not warn or fail even if the expected sections are
+missing.
+
+* Entry value types *
+
+As described above, a section of the spec file accepts entries
+specific to the semantics of the section.  They generally correspond
+to DNS message or RR fields.
+
+Many of them are expected to be integral values, for which either decimal or
+hexadecimal representation is accepted, for example:
+
+  rr_ttl: 3600
+  tag: 0x1234
+
+Some others are expected to be string.  A string value does not have
+to be quated:
+
+  address: 192.0.2.2
+
+but can also be quoated with single quotes:
+
+  address: '192.0.2.2'
+
+Note 1: a string that can be interpreted as an integer must be quated.
+For example, if you want to set a "string" entry to "3600", it should
+be:
+
+  string: '3600'
+
+instead of
+
+  string: 3600
+
+Note 2: a string enclosed with double quotes is not accepted:
+
+  # This doesn't work:
+  address: "192.0.2.2"
+
+In general, string values are converted to hexadecimal sequences
+according to the semantics of the entry.  For instance, a textual IPv4
+address in the above example will be converted to a hexadecimal
+sequence corresponding to a 4-byte integer.  So, in many cases, the
+acceptable syntax for a particular string entry value should be
+obvious from the context.  There are still some exceptional cases
+especially for complicated RR field values, for which the
+corresponding class documentation should be referenced.
+
+One special string syntax that would be worth noting is domain names,
+which would natually be used in many kinds of entries.  The simplest
+form of acceptable syntax is a textual representation of domain names
+such as "example.com" (note: names are always assumed to be
+"absolute", so the trailing dot can be omitted).  But a domain name in
+the wire format can also contain a compression pointer.  This script
+provides a simple support for name compression with a special notation
+of "ptr=nn" where nn is the numeric pointer value (decimal).  For example,
+if the NSDNAME field of an NS RDATA is specified as follows:
+
+  nsname: ns.ptr=12
+
+this script will generate the following output:
+
+  # NS name=ns.ptr=12
+  026e73c00c
+
+** EXTEND THE SCRIPT **
+
+This script is expected to be extended as we add more support for
+various types of RR.  It is encouraged to add support for a new type
+of RR to this script as we see the need for testing that type.  Here
+is a simple instruction of how to do that.
+
+Assume you are adding support for "FOO" RR.  Also assume that the FOO
+RDATA contains a single field named "value".
+
+What you are expected to do is as follows:
+
+- Define a new class named "FOO" inherited from the RR class.  Also
+  define a class variable named "value" for the FOO RDATA field (the
+  variable name can be different from the field name, but it's
+  convenient if it can be easily identifiable.) with an appropriate
+  default value (if possible):
+
+    class FOO(RR):
+        value = 10
+
+  The name of the variable will be (automatically) used as the
+  corresponding entry name in the spec file.  So, a spec file that
+  sets this field to 20 would look like this:
+
+    [foo]
+    value: 20
+
+- Define the "dump()" method for class FOO.  It must call
+  self.dump_header() (which is derived from class RR) at the
+  beginning.  It then prints the RDATA field values in an appropriate
+  way.  Assuming the value is a 16-bit integer field, a complete
+  dump() method would look like this:
+
+    def dump(self, f):
+        if self.rdlen is None:
+            self.rdlen = 2
+        self.dump_header(f, self.rdlen)
+        f.write('# Value=%d\\n' % (self.value))
+        f.write('%04x\\n' % (self.value))
+
+  The first f.write() call is not mandatory, but is encouraged to
+  be provided so that the generated files will be more human readable.
+  Depending on the complexity of the RDATA fields, the dump()
+  implementation would be more complicated.  In particular, if the
+  RDATA length is variable and the RDLEN field value is not specified
+  in the spec file, the dump() method is normally expected to
+  calculate the correct length and pass it to dump_header().  See the
+  implementation of various derived classes of class RR for actual
+  examples.
+"""
+
+import configparser, re, time, socket, sys
+from datetime import datetime
+from optparse import OptionParser
+
+re_hex = re.compile(r'^0x[0-9a-fA-F]+')
+re_decimal = re.compile(r'^\d+$')
+re_string = re.compile(r"\'(.*)\'$")
+
+dnssec_timefmt = '%Y%m%d%H%M%S'
+
+dict_qr = { 'query' : 0, 'response' : 1 }
+dict_opcode = { 'query' : 0, 'iquery' : 1, 'status' : 2, 'notify' : 4,
+                'update' : 5 }
+rdict_opcode = dict([(dict_opcode[k], k.upper()) for k in dict_opcode.keys()])
+dict_rcode = { 'noerror' : 0, 'formerr' : 1, 'servfail' : 2, 'nxdomain' : 3,
+               'notimp' : 4, 'refused' : 5, 'yxdomain' : 6, 'yxrrset' : 7,
+               'nxrrset' : 8, 'notauth' : 9, 'notzone' : 10 }
+rdict_rcode = dict([(dict_rcode[k], k.upper()) for k in dict_rcode.keys()])
+dict_rrtype = { 'none' : 0, 'a' : 1, 'ns' : 2, 'md' : 3, 'mf' : 4, 'cname' : 5,
+                'soa' : 6, 'mb' : 7, 'mg' : 8, 'mr' : 9, 'null' : 10,
+                'wks' : 11, 'ptr' : 12, 'hinfo' : 13, 'minfo' : 14, 'mx' : 15,
+                'txt' : 16, 'rp' : 17, 'afsdb' : 18, 'x25' : 19, 'isdn' : 20,
+                'rt' : 21, 'nsap' : 22, 'nsap_tr' : 23, 'sig' : 24, 'key' : 25,
+                'px' : 26, 'gpos' : 27, 'aaaa' : 28, 'loc' : 29, 'nxt' : 30,
+                'srv' : 33, 'naptr' : 35, 'kx' : 36, 'cert' : 37, 'a6' : 38,
+                'dname' : 39, 'opt' : 41, 'apl' : 42, 'ds' : 43, 'sshfp' : 44,
+                'ipseckey' : 45, 'rrsig' : 46, 'nsec' : 47, 'dnskey' : 48,
+                'dhcid' : 49, 'nsec3' : 50, 'nsec3param' : 51, 'hip' : 55,
+                'spf' : 99, 'unspec' : 103, 'tkey' : 249, 'tsig' : 250,
+                'dlv' : 32769, 'ixfr' : 251, 'axfr' : 252, 'mailb' : 253,
+                'maila' : 254, 'any' : 255 }
+rdict_rrtype = dict([(dict_rrtype[k], k.upper()) for k in dict_rrtype.keys()])
+dict_rrclass = { 'in' : 1, 'ch' : 3, 'hs' : 4, 'any' : 255 }
+rdict_rrclass = dict([(dict_rrclass[k], k.upper()) for k in \
+                          dict_rrclass.keys()])
+dict_algorithm = { 'rsamd5' : 1, 'dh' : 2, 'dsa' : 3, 'ecc' : 4,
+                   'rsasha1' : 5 }
+dict_nsec3_algorithm = { 'reserved' : 0, 'sha1' : 1 }
+rdict_algorithm = dict([(dict_algorithm[k], k.upper()) for k in \
+                            dict_algorithm.keys()])
+rdict_nsec3_algorithm = dict([(dict_nsec3_algorithm[k], k.upper()) for k in \
+                                  dict_nsec3_algorithm.keys()])
+
+header_xtables = { 'qr' : dict_qr, 'opcode' : dict_opcode,
+                   'rcode' : dict_rcode }
+question_xtables = { 'rrtype' : dict_rrtype, 'rrclass' : dict_rrclass }
+
+def parse_value(value, xtable = {}):
+    if re.search(re_hex, value):
+        return int(value, 16)
+    if re.search(re_decimal, value):
+        return int(value)
+    m = re.match(re_string, value)
+    if m:
+        return m.group(1)
+    lovalue = value.lower()
+    if lovalue in xtable:
+        return xtable[lovalue]
+    return value
+
+def code_totext(code, dict):
+    if code in dict.keys():
+        return dict[code] + '(' + str(code) + ')'
+    return str(code)
+
+def encode_name(name, absolute=True):
+    # make sure the name is dot-terminated.  duplicate dots will be ignored
+    # below.
+    name += '.'
+    labels = name.split('.')
+    wire = ''
+    for l in labels:
+        if len(l) > 4 and l[0:4] == 'ptr=':
+            # special meta-syntax for compression pointer
+            wire += '%04x' % (0xc000 | int(l[4:]))
+            break
+        if absolute or len(l) > 0:
+            wire += '%02x' % len(l)
+            wire += ''.join(['%02x' % ord(ch) for ch in l])
+        if len(l) == 0:
+            break
+    return wire
+
+def encode_string(name, len=None):
+    if type(name) is int and len is not None:
+        return '%0.*x' % (len * 2, name)
+    return ''.join(['%02x' % ord(ch) for ch in name])
+
+def count_namelabels(name):
+    if name == '.':             # special case
+        return 0
+    m = re.match('^(.*)\.$', name)
+    if m:
+        name = m.group(1)
+    return len(name.split('.'))
+
+def get_config(config, section, configobj, xtables = {}):
+    try:
+        for field in config.options(section):
+            value = config.get(section, field)
+            if field in xtables.keys():
+                xtable = xtables[field]
+            else:
+                xtable = {}
+            configobj.__dict__[field] = parse_value(value, xtable)
+    except configparser.NoSectionError:
+        return False
+    return True
+
+def print_header(f, input_file):
+    f.write('''###
+### This data file was auto-generated from ''' + input_file + '''
+###
+''')
+
+class Name:
+    '''Implements rendering a single domain name in the test data format.
+
+    Configurable parameter is as follows (see the description of the
+    same name of attribute for the default value):
+    - name (string): A textual representation of the name, such as
+      'example.com'.
+    - pointer (int): If specified, compression pointer will be
+      prepended to the generated data with the offset being the value
+      of this parameter.
+    '''
+
+    name = 'example.com'
+    pointer = None                # no compression by default
+    def dump(self, f):
+        name = self.name
+        if self.pointer is not None:
+            if len(name) > 0 and name[-1] != '.':
+                name += '.'
+            name += 'ptr=%d' % self.pointer
+        name_wire = encode_name(name)
+        f.write('\n# DNS Name: %s' % self.name)
+        if self.pointer is not None:
+            f.write(' + compression pointer: %d' % self.pointer)
+        f.write('\n')
+        f.write('%s' % name_wire)
+        f.write('\n')
+
+class DNSHeader:
+    '''Implements rendering a DNS Header section in the test data format.
+
+    Configurable parameter is as follows (see the description of the
+    same name of attribute for the default value):
+    - id (16-bit int):
+    - qr, aa, tc, rd, ra, ad, cd (0 or 1): Standard header bits as
+      defined in RFC1035 and RFC4035.  If set to 1, the corresponding
+      bit will be set; if set to 0, it will be cleared.
+    - mbz (0-3): The reserved field of the 3rd and 4th octets of the
+      header.
+    - rcode (4-bit int or string): The RCODE field.  If specified as a
+      string, it must be the commonly used textual mnemonic of the RCODEs
+      (NOERROR, FORMERR, etc, case insensitive).
+    - opcode (4-bit int or string): The OPCODE field.  If specified as
+      a string, it must be the commonly used textual mnemonic of the
+      OPCODEs (QUERY, NOTIFY, etc, case insensitive).
+    - qdcount, ancount, nscount, arcount (16-bit int): The QD/AN/NS/AR
+      COUNT fields, respectively.
+    '''
+
+    id = 0x1035
+    (qr, aa, tc, rd, ra, ad, cd) = 0, 0, 0, 0, 0, 0, 0
+    mbz = 0
+    rcode = 0                   # noerror
+    opcode = 0                  # query
+    (qdcount, ancount, nscount, arcount) = 1, 0, 0, 0
+
+    def dump(self, f):
+        f.write('\n# Header Section\n')
+        f.write('# ID=' + str(self.id))
+        f.write(' QR=' + ('Response' if self.qr else 'Query'))
+        f.write(' Opcode=' + code_totext(self.opcode, rdict_opcode))
+        f.write(' Rcode=' + code_totext(self.rcode, rdict_rcode))
+        f.write('%s' % (' AA' if self.aa else ''))
+        f.write('%s' % (' TC' if self.tc else ''))
+        f.write('%s' % (' RD' if self.rd else ''))
+        f.write('%s' % (' AD' if self.ad else ''))
+        f.write('%s' % (' CD' if self.cd else ''))
+        f.write('\n')
+        f.write('%04x ' % self.id)
+        flag_and_code = 0
+        flag_and_code |= (self.qr << 15 | self.opcode << 14 | self.aa << 10 |
+                          self.tc << 9 | self.rd << 8 | self.ra << 7 |
+                          self.mbz << 6 | self.ad << 5 | self.cd << 4 |
+                          self.rcode)
+        f.write('%04x\n' % flag_and_code)
+        f.write('# QDCNT=%d, ANCNT=%d, NSCNT=%d, ARCNT=%d\n' %
+                (self.qdcount, self.ancount, self.nscount, self.arcount))
+        f.write('%04x %04x %04x %04x\n' % (self.qdcount, self.ancount,
+                                           self.nscount, self.arcount))
+
+class DNSQuestion:
+    '''Implements rendering a DNS question in the test data format.
+
+    Configurable parameter is as follows (see the description of the
+    same name of attribute for the default value):
+    - name (string): The QNAME.  The string must be interpreted as a
+      valid domain name.
+    - rrtype (int or string): The question type.  If specified
+      as an integer, it must be the 16-bit RR type value of the
+      covered type.  If specifed as a string, it must be the textual
+      mnemonic of the type.
+    - rrclass (int or string): The question class.  If specified as an
+      integer, it must be the 16-bit RR class value of the covered
+      type.  If specifed as a string, it must be the textual mnemonic
+      of the class.
+    '''
+    name = 'example.com.'
+    rrtype = parse_value('A', dict_rrtype)
+    rrclass = parse_value('IN', dict_rrclass)
+
+    def dump(self, f):
+        f.write('\n# Question Section\n')
+        f.write('# QNAME=%s QTYPE=%s QCLASS=%s\n' %
+                (self.name,
+                 code_totext(self.rrtype, rdict_rrtype),
+                 code_totext(self.rrclass, rdict_rrclass)))
+        f.write(encode_name(self.name))
+        f.write(' %04x %04x\n' % (self.rrtype, self.rrclass))
+
+class EDNS:
+    '''Implements rendering EDNS OPT RR in the test data format.
+
+    Configurable parameter is as follows (see the description of the
+    same name of attribute for the default value):
+    - name (string): The owner name of the OPT RR.  The string must be
+      interpreted as a valid domain name.
+    - udpsize (16-bit int): The UDP payload size (set as the RR class)
+    - extrcode (8-bit int): The upper 8 bits of the extended RCODE.
+    - version (8-bit int): The EDNS version.
+    - do (int): The DNSSEC DO bit.  The bit will be set if this value
+      is 1; otherwise the bit will be unset.
+    - mbz (15-bit int): The rest of the flags field.
+    - rdlen (16-bit int): The RDLEN field.  Note: right now specifying
+      a non 0 value (except for making bogus data) doesn't make sense
+      because there is no way to configure RDATA.
+    '''
+    name = '.'
+    udpsize = 4096
+    extrcode = 0
+    version = 0
+    do = 0
+    mbz = 0
+    rdlen = 0
+    def dump(self, f):
+        f.write('\n# EDNS OPT RR\n')
+        f.write('# NAME=%s TYPE=%s UDPSize=%d ExtRcode=%s Version=%s DO=%d\n' %
+                (self.name, code_totext(dict_rrtype['opt'], rdict_rrtype),
+                 self.udpsize, self.extrcode, self.version,
+                 1 if self.do else 0))
+        
+        code_vers = (self.extrcode << 8) | (self.version & 0x00ff)
+        extflags = (self.do << 15) | (self.mbz & ~0x8000)
+        f.write('%s %04x %04x %04x %04x\n' %
+                (encode_name(self.name), dict_rrtype['opt'], self.udpsize,
+                 code_vers, extflags))
+        f.write('# RDLEN=%d\n' % self.rdlen)
+        f.write('%04x\n' % self.rdlen)
+
+class RR:
+    '''This is a base class for various types of RR test data.
+    For each RR type (A, AAAA, NS, etc), we define a derived class of RR
+    to dump type specific RDATA parameters.  This class defines parameters
+    common to all types of RDATA, namely the owner name, RR class and TTL.
+    The dump() method of derived classes are expected to call dump_header(),
+    whose default implementation is provided in this class.  This method
+    decides whether to dump the test data as an RR (with name, type, class)
+    or only as RDATA (with its length), and dumps the corresponding data
+    via the specified file object.
+
+    By convention we assume derived classes are named after the common
+    standard mnemonic of the corresponding RR types.  For example, the
+    derived class for the RR type SOA should be named "SOA".
+
+    Configurable parameters are as follows:
+    - as_rr (bool): Whether or not the data is to be dumped as an RR.
+      False by default.
+    - rr_name (string): The owner name of the RR.  The string must be
+      interpreted as a valid domain name (compression pointer can be
+      contained).  Default is 'example.com.'
+    - rr_class (string): The RR class of the data.  Only meaningful
+      when the data is dumped as an RR.  Default is 'IN'.
+    - rr_ttl (int): The TTL value of the RR.  Only meaningful when
+      the data is dumped as an RR.  Default is 86400 (1 day).
+    - rdlen (int): 16-bit RDATA length.  It can be None (i.e. omitted
+      in the spec file), in which case the actual length of the
+      generated RDATA is automatically determined and used; if
+      negative, the RDLEN field will be omitted from the output data.
+      (Note that omitting RDLEN with as_rr being True is mostly
+      meaningless, although the script doesn't complain about it).
+      Default is None.
+    '''
+
+    def __init__(self):
+        self.as_rr = False
+        # only when as_rr is True, same for class/TTL:
+        self.rr_name = 'example.com'
+        self.rr_class = 'IN'
+        self.rr_ttl = 86400
+        self.rdlen = None
+
+    def dump_header(self, f, rdlen):
+        type_txt = self.__class__.__name__
+        type_code = parse_value(type_txt, dict_rrtype)
+        rdlen_spec = ''
+        rdlen_data = ''
+        if rdlen >= 0:
+            rdlen_spec = ', RDLEN=%d' % rdlen
+            rdlen_data = '%04x' % rdlen
+        if self.as_rr:
+            rrclass = parse_value(self.rr_class, dict_rrclass)
+            f.write('\n# %s RR (QNAME=%s Class=%s TTL=%d%s)\n' %
+                    (type_txt, self.rr_name,
+                     code_totext(rrclass, rdict_rrclass), self.rr_ttl,
+                     rdlen_spec))
+            f.write('%s %04x %04x %08x %s\n' %
+                    (encode_name(self.rr_name), type_code, rrclass,
+                     self.rr_ttl, rdlen_data))
+        else:
+            f.write('\n# %s RDATA%s\n' % (type_txt, rdlen_spec))
+            f.write('%s\n' % rdlen_data)
+
+class A(RR):
+    '''Implements rendering A RDATA (of class IN) in the test data format.
+
+    Configurable parameter is as follows (see the description of the
+    same name of attribute for the default value):
+    - address (string): The address field.  This must be a valid textual
+      IPv4 address.
+    '''
+    RDLEN_DEFAULT = 4           # fixed by default
+    address = '192.0.2.1'
+
+    def dump(self, f):
+        if self.rdlen is None:
+            self.rdlen = self.RDLEN_DEFAULT
+        self.dump_header(f, self.rdlen)
+        f.write('# Address=%s\n' % (self.address))
+        bin_address = socket.inet_aton(self.address)
+        f.write('%02x%02x%02x%02x\n' % (bin_address[0], bin_address[1],
+                                        bin_address[2], bin_address[3]))
+
+class AAAA(RR):
+    '''Implements rendering AAAA RDATA (of class IN) in the test data
+    format.
+
+    Configurable parameter is as follows (see the description of the
+    same name of attribute for the default value):
+    - address (string): The address field.  This must be a valid textual
+      IPv6 address.
+    '''
+    RDLEN_DEFAULT = 16          # fixed by default
+    address = '2001:db8::1'
+
+    def dump(self, f):
+        if self.rdlen is None:
+            self.rdlen = self.RDLEN_DEFAULT
+        self.dump_header(f, self.rdlen)
+        f.write('# Address=%s\n' % (self.address))
+        bin_address = socket.inet_pton(socket.AF_INET6, self.address)
+        [f.write('%02x' % x) for x in bin_address]
+        f.write('\n')
+
+class NS(RR):
+    '''Implements rendering NS RDATA in the test data format.
+
+    Configurable parameter is as follows (see the description of the
+    same name of attribute for the default value):
+    - nsname (string): The NSDNAME field.  The string must be
+      interpreted as a valid domain name.
+    '''
+
+    nsname = 'ns.example.com'
+
+    def dump(self, f):
+        nsname_wire = encode_name(self.nsname)
+        if self.rdlen is None:
+            self.rdlen = len(nsname_wire) / 2
+        self.dump_header(f, self.rdlen)
+        f.write('# NS name=%s\n' % (self.nsname))
+        f.write('%s\n' % nsname_wire)
+
+class SOA(RR):
+    '''Implements rendering SOA RDATA in the test data format.
+
+    Configurable parameters are as follows (see the description of the
+    same name of attribute for the default value):
+    - mname/rname (string): The MNAME/RNAME fields, respectively.  The
+      string must be interpreted as a valid domain name.
+    - serial (32-bit int): The SERIAL field
+    - refresh (32-bit int): The REFRESH field
+    - retry (32-bit int): The RETRY field
+    - expire (32-bit int): The EXPIRE field
+    - minimum (32-bit int): The MINIMUM field
+    '''
+
+    mname = 'ns.example.com'
+    rname = 'root.example.com'
+    serial = 2010012601
+    refresh = 3600
+    retry = 300
+    expire = 3600000
+    minimum = 1200
+    def dump(self, f):
+        mname_wire = encode_name(self.mname)
+        rname_wire = encode_name(self.rname)
+        if self.rdlen is None:
+            self.rdlen = int(20 + len(mname_wire) / 2 + len(str(rname_wire)) / 2)
+        self.dump_header(f, self.rdlen)
+        f.write('# NNAME=%s RNAME=%s\n' % (self.mname, self.rname))
+        f.write('%s %s\n' % (mname_wire, rname_wire))
+        f.write('# SERIAL(%d) REFRESH(%d) RETRY(%d) EXPIRE(%d) MINIMUM(%d)\n' %
+                (self.serial, self.refresh, self.retry, self.expire,
+                 self.minimum))
+        f.write('%08x %08x %08x %08x %08x\n' % (self.serial, self.refresh,
+                                                self.retry, self.expire,
+                                                self.minimum))
+
+class TXT(RR):
+    '''Implements rendering TXT RDATA in the test data format.
+
+    Configurable parameters are as follows (see the description of the
+    same name of attribute for the default value):
+    - nstring (int): number of character-strings
+    - stringlenN (int) (int, N = 0, ..., nstring-1): the length of the
+      N-th character-string.
+    - stringN (string, N = 0, ..., nstring-1): the N-th
+      character-string.
+    - stringlen (int): the default string.  If nstring >= 1 and the
+      corresponding stringlenN isn't specified in the spec file, this
+      value will be used.  If this parameter isn't specified either,
+      the length of the string will be used.  Note that it means
+      this parameter (or any stringlenN) doesn't have to be specified
+      unless you want to intentially build a broken character string.
+    - string (string): the default string.  If nstring >= 1 and the
+      corresponding stringN isn't specified in the spec file, this
+      string will be used.
+    '''
+
+    nstring = 1
+    stringlen = None
+    string = 'Test String'
+
+    def dump(self, f):
+        stringlen_list = []
+        string_list = []
+        wirestring_list = []
+        for i in range(0, self.nstring):
+            key_string = 'string' + str(i)
+            if key_string in self.__dict__:
+                string_list.append(self.__dict__[key_string])
+            else:
+                string_list.append(self.string)
+            wirestring_list.append(encode_string(string_list[-1]))
+            key_stringlen = 'stringlen' + str(i)
+            if key_stringlen in self.__dict__:
+                stringlen_list.append(self.__dict__[key_stringlen])
+            else:
+                stringlen_list.append(self.stringlen)
+            if stringlen_list[-1] is None:
+                stringlen_list[-1] = int(len(wirestring_list[-1]) / 2)
+        if self.rdlen is None:
+            self.rdlen = int(len(''.join(wirestring_list)) / 2) + self.nstring
+        self.dump_header(f, self.rdlen)
+        for i in range(0, self.nstring):
+            f.write('# String Len=%d, String=\"%s\"\n' %
+                    (stringlen_list[i], string_list[i]))
+            f.write('%02x%s%s\n' % (stringlen_list[i],
+                                    ' ' if len(wirestring_list[i]) > 0 else '',
+                                    wirestring_list[i]))
+
+class RP(RR):
+    '''Implements rendering RP RDATA in the test data format.
+
+    Configurable parameters are as follows (see the description of the
+    same name of attribute for the default value):
+    - mailbox (string): The mailbox field.
+    - text (string): The text field.
+    These strings must be interpreted as a valid domain name.
+    '''
+    mailbox = 'root.example.com'
+    text = 'rp-text.example.com'
+    def dump(self, f):
+        mailbox_wire = encode_name(self.mailbox)
+        text_wire = encode_name(self.text)
+        if self.rdlen is None:
+            self.rdlen = (len(mailbox_wire) + len(text_wire)) / 2
+        else:
+            self.rdlen = int(self.rdlen)
+        self.dump_header(f, self.rdlen)
+        f.write('# MAILBOX=%s TEXT=%s\n' % (self.mailbox, self.text))
+        f.write('%s %s\n' % (mailbox_wire, text_wire))
+
+class MINFO(RR):
+    '''Implements rendering MINFO RDATA in the test data format.
+
+    Configurable parameters are as follows (see the description of the
+    same name of attribute for the default value):
+    - rmailbox (string): The rmailbox field.
+    - emailbox (string): The emailbox field.
+    These strings must be interpreted as a valid domain name.
+    '''
+    rmailbox = 'rmailbox.example.com'
+    emailbox = 'emailbox.example.com'
+    def dump(self, f):
+        rmailbox_wire = encode_name(self.rmailbox)
+        emailbox_wire = encode_name(self.emailbox)
+        if self.rdlen is None:
+            self.rdlen = (len(rmailbox_wire) + len(emailbox_wire)) / 2
+        else:
+            self.rdlen = int(self.rdlen)
+        self.dump_header(f, self.rdlen)
+        f.write('# RMAILBOX=%s EMAILBOX=%s\n' % (self.rmailbox, self.emailbox))
+        f.write('%s %s\n' % (rmailbox_wire, emailbox_wire))
+
+class AFSDB(RR):
+    '''Implements rendering AFSDB RDATA in the test data format.
+
+    Configurable parameters are as follows (see the description of the
+    same name of attribute for the default value):
+    - subtype (16 bit int): The subtype field.
+    - server (string): The server field.
+    The string must be interpreted as a valid domain name.
+    '''
+    subtype = 1
+    server = 'afsdb.example.com'
+    def dump(self, f):
+        server_wire = encode_name(self.server)
+        if self.rdlen is None:
+            self.rdlen = 2 + len(server_wire) / 2
+        else:
+            self.rdlen = int(self.rdlen)
+        self.dump_header(f, self.rdlen)
+        f.write('# SUBTYPE=%d SERVER=%s\n' % (self.subtype, self.server))
+        f.write('%04x %s\n' % (self.subtype, server_wire))
+
+class NSECBASE(RR):
+    '''Implements rendering NSEC/NSEC3 type bitmaps commonly used for
+    these RRs.  The NSEC and NSEC3 classes will be inherited from this
+    class.
+
+    Configurable parameters are as follows (see the description of the
+    same name of attribute for the default value):
+    - nbitmap (int): The number of type bitmaps.
+    The following three define the bitmaps.  If suffixed with "N"
+    (0 <= N < nbitmaps), it means the definition for the N-th bitmap.
+    If there is no suffix (e.g., just "block", it means the default
+    for any unspecified values)
+    - block[N] (8-bit int): The Window Block.
+    - maplen[N] (8-bit int): The Bitmap Length.  The default "maplen"
+      can also be unspecified (with being set to None), in which case
+      the corresponding length will be calculated from the bitmap.
+    - bitmap[N] (string): The Bitmap.  This must be the hexadecimal
+      representation of the bitmap field.  For example, for a bitmap
+      where the 7th and 15th bits (and only these bits) are set, it
+      must be '0101'.  Note also that the value must be quated with
+      single quatations because it could also be interpreted as an
+      integer.
+    '''
+    nbitmap = 1                 # number of bitmaps
+    block = 0
+    maplen = None              # default bitmap length, auto-calculate
+    bitmap = '040000000003'     # an arbtrarily chosen bitmap sample
+    def dump(self, f):
+        # first, construct the bitmpa data
+        block_list = []
+        maplen_list = []
+        bitmap_list = []
+        for i in range(0, self.nbitmap):
+            key_bitmap = 'bitmap' + str(i)
+            if key_bitmap in self.__dict__:
+                bitmap_list.append(self.__dict__[key_bitmap])
+            else:
+                bitmap_list.append(self.bitmap)
+            key_maplen = 'maplen' + str(i)
+            if key_maplen in self.__dict__:
+                maplen_list.append(self.__dict__[key_maplen])
+            else:
+                maplen_list.append(self.maplen)
+            if maplen_list[-1] is None: # calculate it if not specified
+                maplen_list[-1] = int(len(bitmap_list[-1]) / 2)
+            key_block = 'block' + str(i)
+            if key_block in self.__dict__:
+               block_list.append(self.__dict__[key_block])
+            else:
+                block_list.append(self.block)
+
+        # dump RR-type specific part (NSEC or NSEC3)
+        self.dump_fixedpart(f, 2 * self.nbitmap + \
+                                int(len(''.join(bitmap_list)) / 2))
+
+        # dump the bitmap
+        for i in range(0, self.nbitmap):
+            f.write('# Bitmap: Block=%d, Length=%d\n' %
+                    (block_list[i], maplen_list[i]))
+            f.write('%02x %02x %s\n' %
+                    (block_list[i], maplen_list[i], bitmap_list[i]))
+
+class NSEC(NSECBASE):
+    '''Implements rendering NSEC RDATA in the test data format.
+
+    Configurable parameters are as follows (see the description of the
+    same name of attribute for the default value):
+    - Type bitmap related parameters: see class NSECBASE
+    - nextname (string): The Next Domain Name field.  The string must be
+      interpreted as a valid domain name.
+    '''
+
+    nextname = 'next.example.com'
+    def dump_fixedpart(self, f, bitmap_totallen):
+        name_wire = encode_name(self.nextname)
+        if self.rdlen is None:
+            # if rdlen needs to be calculated, it must be based on the bitmap
+            # length, because the configured maplen can be fake.
+            self.rdlen = int(len(name_wire) / 2) + bitmap_totallen
+        self.dump_header(f, self.rdlen)
+        f.write('# Next Name=%s (%d bytes)\n' % (self.nextname,
+                                                 int(len(name_wire) / 2)))
+        f.write('%s\n' % name_wire)
+
+class NSEC3(NSECBASE):
+    '''Implements rendering NSEC3 RDATA in the test data format.
+
+    Configurable parameters are as follows (see the description of the
+    same name of attribute for the default value):
+    - Type bitmap related parameters: see class NSECBASE
+    - hashalg (8-bit int): The Hash Algorithm field.  Note that
+      currently the only defined algorithm is SHA-1, for which a value
+      of 1 will be used, and it's the default.  So this implementation
+      does not support any string representation right now.
+    - optout (bool): The Opt-Out flag of the Flags field.
+    - mbz (7-bit int): The rest of the Flags field.  This value will
+      be left shifted for 1 bit and then OR-ed with optout to
+      construct the complete Flags field.
+    - iterations (16-bit int): The Iterations field.
+    - saltlen (int): The Salt Length field.
+    - salt (string): The Salt field.  It is converted to a sequence of
+      ascii codes and its hexadecimal representation will be used.
+    - hashlen (int): The Hash Length field.
+    - hash (string): The Next Hashed Owner Name field.  This parameter
+      is interpreted as "salt".
+    '''
+
+    hashalg = 1                 # SHA-1
+    optout = False              # opt-out flag
+    mbz = 0                     # other flag fields (none defined yet)
+    iterations = 1
+    saltlen = 5
+    salt = 's' * saltlen
+    hashlen = 20
+    hash = 'h' * hashlen
+    def dump_fixedpart(self, f, bitmap_totallen):
+        if self.rdlen is None:
+            # if rdlen needs to be calculated, it must be based on the bitmap
+            # length, because the configured maplen can be fake.
+            self.rdlen = 4 + 1 + len(self.salt) + 1 + len(self.hash) \
+                + bitmap_totallen
+        self.dump_header(f, self.rdlen)
+        optout_val = 1 if self.optout else 0
+        f.write('# Hash Alg=%s, Opt-Out=%d, Other Flags=%0x, Iterations=%d\n' %
+                (code_totext(self.hashalg, rdict_nsec3_algorithm),
+                 optout_val, self.mbz, self.iterations))
+        f.write('%02x %02x %04x\n' %
+                (self.hashalg, (self.mbz << 1) | optout_val, self.iterations))
+        f.write("# Salt Len=%d, Salt='%s'\n" % (self.saltlen, self.salt))
+        f.write('%02x%s%s\n' % (self.saltlen,
+                                ' ' if len(self.salt) > 0 else '',
+                                encode_string(self.salt)))
+        f.write("# Hash Len=%d, Hash='%s'\n" % (self.hashlen, self.hash))
+        f.write('%02x%s%s\n' % (self.hashlen,
+                                ' ' if len(self.hash) > 0 else '',
+                                encode_string(self.hash)))
+
+class RRSIG(RR):
+    '''Implements rendering RRSIG RDATA in the test data format.
+
+    Configurable parameters are as follows (see the description of the
+    same name of attribute for the default value):
+    - covered (int or string): The Type Covered field.  If specified
+      as an integer, it must be the 16-bit RR type value of the
+      covered type.  If specifed as a string, it must be the textual
+      mnemonic of the type.
+    - algorithm (int or string): The Algorithm field.   If specified
+      as an integer, it must be the 8-bit algorithm number as defined
+      in RFC4034.  If specifed as a string, it must be one of the keys
+      of dict_algorithm (case insensitive).
+    - labels (int): The Labels field.  If omitted (the corresponding
+      variable being set to None), the number of labels of "signer"
+      (excluding the trailing null label as specified in RFC4034) will
+      be used.
+    - originalttl (32-bit int): The Original TTL field.
+    - expiration (32-bit int): The Expiration TTL field.
+    - inception (32-bit int): The Inception TTL field.
+    - tag (16-bit int): The Key Tag field.
+    - signer (string): The Signer's Name field.  The string must be
+      interpreted as a valid domain name.
+    - signature (int): The Signature field.  Right now only a simple
+      integer form is supported.  A prefix of "0" will be prepended if
+      the resulting hexadecimal representation consists of an odd
+      number of characters.
+    '''
+
+    covered = 'A'
+    algorithm = 'RSASHA1'
+    labels = None                 # auto-calculate (#labels of signer)
+    originalttl = 3600
+    expiration = int(time.mktime(datetime.strptime('20100131120000',
+                                                   dnssec_timefmt).timetuple()))
+    inception = int(time.mktime(datetime.strptime('20100101120000',
+                                                  dnssec_timefmt).timetuple()))
+    tag = 0x1035
+    signer = 'example.com'
+    signature = 0x123456789abcdef123456789abcdef
+
+    def dump(self, f):
+        name_wire = encode_name(self.signer)
+        sig_wire = '%x' % self.signature
+        if len(sig_wire) % 2 != 0:
+            sig_wire = '0' + sig_wire
+        if self.rdlen is None:
+            self.rdlen = int(18 + len(name_wire) / 2 + len(str(sig_wire)) / 2)
+        self.dump_header(f, self.rdlen)
+
+        if type(self.covered) is str:
+            self.covered = dict_rrtype[self.covered.lower()]
+        if type(self.algorithm) is str:
+            self.algorithm = dict_algorithm[self.algorithm.lower()]
+        if self.labels is None:
+            self.labels = count_namelabels(self.signer)
+        f.write('# Covered=%s Algorithm=%s Labels=%d OrigTTL=%d\n' %
+                (code_totext(self.covered, rdict_rrtype),
+                 code_totext(self.algorithm, rdict_algorithm), self.labels,
+                 self.originalttl))
+        f.write('%04x %02x %02x %08x\n' % (self.covered, self.algorithm,
+                                           self.labels, self.originalttl))
+        f.write('# Expiration=%s, Inception=%s\n' %
+                (str(self.expiration), str(self.inception)))
+        f.write('%08x %08x\n' % (self.expiration, self.inception))
+        f.write('# Tag=%d Signer=%s and Signature\n' % (self.tag, self.signer))
+        f.write('%04x %s %s\n' % (self.tag, name_wire, sig_wire))
+
+class TSIG(RR):
+    '''Implements rendering TSIG RDATA in the test data format.
+
+    As a meta RR type TSIG uses some non common parameters.  This
+    class overrides some of the default attributes of the RR class
+    accordingly:
+    - rr_class is set to 'ANY'
+    - rr_ttl is set to 0
+    Like other derived classes these can be overridden via the spec
+    file.
+
+    Other configurable parameters are as follows (see the description
+    of the same name of attribute for the default value):
+    - algorithm (string): The Algorithm Name field.  The value is
+      generally interpreted as a domain name string, and will
+      typically be one of the standard algorithm names defined in
+      RFC4635.  For convenience, however, a shortcut value "hmac-md5"
+      is allowed instead of the standard "hmac-md5.sig-alg.reg.int".
+    - time_signed (48-bit int): The Time Signed field.
+    - fudge (16-bit int): The Fudge field.
+    - mac_size (int): The MAC Size field.  If omitted, the common value
+      determined by the algorithm will be used.
+    - mac (int or string): The MAC field.  If specified as an integer,
+      the integer value is used as the MAC, possibly with prepended
+      0's so that the total length will be mac_size.  If specifed as a
+      string, it is converted to a sequence of ascii codes and its
+      hexadecimal representation will be used.  So, for example, if
+      "mac" is set to 'abc', it will be converted to '616263'.  Note
+      that in this case the length of "mac" may not be equal to
+      mac_size.  If unspecified, the mac_size number of '78' (ascii
+      code of 'x') will be used.
+    - original_id (16-bit int): The Original ID field.
+    - error (16-bit int): The Error field.
+    - other_len (int): The Other Len field.
+    - other_data (int or string): The Other Data field.  This is
+      interpreted just like "mac" except that other_len is used
+      instead of mac_size.  If unspecified this will be empty unless
+      the "error" is set to 18 (which means the "BADTIME" error), in
+      which case a hexadecimal representation of "time_signed + fudge
+      + 1" will be used.
+    '''
+
+    algorithm = 'hmac-sha256'
+    time_signed = 1286978795    # arbitrarily chosen default
+    fudge = 300
+    mac_size = None             # use a common value for the algorithm
+    mac = None                  # use 'x' * mac_size
+    original_id = 2845          # arbitrarily chosen default
+    error = 0
+    other_len = None         # 6 if error is BADTIME; otherwise 0
+    other_data = None        # use time_signed + fudge + 1 for BADTIME
+    dict_macsize = { 'hmac-md5' : 16, 'hmac-sha1' : 20, 'hmac-sha256' : 32 }
+
+    # TSIG has some special defaults
+    def __init__(self):
+        super().__init__()
+        self.rr_class = 'ANY'
+        self.rr_ttl = 0
+
+    def dump(self, f):
+        if str(self.algorithm) == 'hmac-md5':
+            name_wire = encode_name('hmac-md5.sig-alg.reg.int')
+        else:
+            name_wire = encode_name(self.algorithm)
+        mac_size = self.mac_size
+        if mac_size is None:
+            if self.algorithm in self.dict_macsize.keys():
+                mac_size = self.dict_macsize[self.algorithm]
+            else:
+                raise RuntimeError('TSIG Mac Size cannot be determined')
+        mac = encode_string('x' * mac_size) if self.mac is None else \
+            encode_string(self.mac, mac_size)
+        other_len = self.other_len
+        if other_len is None:
+            # 18 = BADTIME
+            other_len = 6 if self.error == 18 else 0
+        other_data = self.other_data
+        if other_data is None:
+            other_data = '%012x' % (self.time_signed + self.fudge + 1) \
+                if self.error == 18 else ''
+        else:
+            other_data = encode_string(self.other_data, other_len)
+        if self.rdlen is None:
+            self.rdlen = int(len(name_wire) / 2 + 16 + len(mac) / 2 + \
+                                 len(other_data) / 2)
+        self.dump_header(f, self.rdlen)
+        f.write('# Algorithm=%s Time-Signed=%d Fudge=%d\n' %
+                (self.algorithm, self.time_signed, self.fudge))
+        f.write('%s %012x %04x\n' % (name_wire, self.time_signed, self.fudge))
+        f.write('# MAC Size=%d MAC=(see hex)\n' % mac_size)
+        f.write('%04x%s\n' % (mac_size, ' ' + mac if len(mac) > 0 else ''))
+        f.write('# Original-ID=%d Error=%d\n' % (self.original_id, self.error))
+        f.write('%04x %04x\n' %  (self.original_id, self.error))
+        f.write('# Other-Len=%d Other-Data=(see hex)\n' % other_len)
+        f.write('%04x%s\n' % (other_len,
+                              ' ' + other_data if len(other_data) > 0 else ''))
+
+# Build section-class mapping
+config_param = { 'name' : (Name, {}),
+                 'header' : (DNSHeader, header_xtables),
+                 'question' : (DNSQuestion, question_xtables),
+                 'edns' : (EDNS, {}) }
+for rrtype in dict_rrtype.keys():
+    # For any supported RR types add the tuple of (RR_CLASS, {}).
+    # We expect KeyError as not all the types are supported, and simply
+    # ignore them.
+    try:
+        cur_mod = sys.modules[__name__]
+        config_param[rrtype] = (cur_mod.__dict__[rrtype.upper()], {})
+    except KeyError:
+        pass
+
+def get_config_param(section):
+    s = section
+    m = re.match('^([^:]+)/\d+$', section)
+    if m:
+        s = m.group(1)
+    return config_param[s]
+
+usage = '''usage: %prog [options] input_file'''
+
+if __name__ == "__main__":
+    parser = OptionParser(usage=usage)
+    parser.add_option('-o', '--output', action='store', dest='output',
+                      default=None, metavar='FILE',
+                      help='output file name [default: prefix of input_file]')
+    (options, args) = parser.parse_args()
+
+    if len(args) == 0:
+        parser.error('input file is missing')
+    configfile = args[0]
+
+    outputfile = options.output
+    if not outputfile:
+        m = re.match('(.*)\.[^.]+$', configfile)
+        if m:
+            outputfile = m.group(1)
+        else:
+            raise ValueError('output file is not specified and input file is not in the form of "output_file.suffix"')
+
+    config = configparser.SafeConfigParser()
+    config.read(configfile)
+
+    output = open(outputfile, 'w')
+
+    print_header(output, configfile)
+
+    # First try the 'custom' mode; if it fails assume the query mode.
+    try:
+        sections = config.get('custom', 'sections').split(':')
+    except configparser.NoSectionError:
+        sections = ['header', 'question', 'edns']
+
+    for s in sections:
+        section_param = get_config_param(s)
+        (obj, xtables) = (section_param[0](), section_param[1])
+        if get_config(config, s, obj, xtables):
+            obj.dump(output)
+
+    output.close()
diff --git a/src/lib/util/python/mkpywrapper.py.in b/src/lib/util/python/mkpywrapper.py.in
new file mode 100755
index 0000000..4bf7752
--- /dev/null
+++ b/src/lib/util/python/mkpywrapper.py.in
@@ -0,0 +1,100 @@
+#!@PYTHON@
+
+# Copyright (C) 2011  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""This utility program generates a C++ header and implementation files
+that can be used as a template of C++ python binding for a C++ class.
+
+Usage: ./mkpywrapper.py ClassName
+(the script should be run on this directory)
+
+It will generate two files: classname_python.h and classname_python.cc,
+many of whose definitions are in the namespace isc::MODULE_NAME::python.
+By default MODULE_NAME will be 'dns' (because this tool is originally
+intended to be used for the C++ python binding of the DNS library), but
+can be changed via the -m command line option.
+
+The generated files contain code fragments that are commonly used in
+C++ python binding implementations.  It will define a class named
+s_ClassName which is a derived class of PyModule and can meet the
+requirement of the CPPPyObjectContainer template class (see
+pycppwrapper_util.h).  It also defines (and declares in the header file)
+"classname_type", which is of PyTypeObject and is intended to be used
+to define details of the python bindings for the ClassName class.
+
+In many cases the header file can be used as a startpoint of the
+binding development without modification.  But you may want to make
+ClassName::cppobj a constant variable (and you should if you can).
+Many definitions of classname_python.cc should also be able to be used
+just as defined, but some will need to be changed or removed.  In
+particular, you should at least adjust ClassName_init().  You'll
+probably also need to add more definitions to that file to provide
+complete features of the C++ class.
+"""
+
+import datetime, string, sys
+from optparse import OptionParser
+
+# Remember the current year to produce the copyright boilerplate
+YEAR = datetime.date.today().timetuple()[0]
+
+def dump_file(out_file, temp_file, class_name, module):
+    for line in temp_file.readlines():
+        line = line.replace("@YEAR@", str(YEAR))
+        line = line.replace("@CPPCLASS at _H", class_name.upper() + "_H")
+        line = line.replace("@CPPCLASS@", class_name)
+        line = line.replace("@cppclass@", class_name.lower())
+        line = line.replace("@MODULE@", module)
+        out_file.write(line)
+
+def dump_wrappers(class_name, output, module):
+    try:
+        if output == "-":
+            header_file = sys.stdout
+        else:
+            header_file = open(output + "_python.h", "w")
+        header_template_file = open("wrapper_template.h", "r")
+        if output == "-":
+            impl_file = sys.stdout
+        else:
+            impl_file = open(output + "_python.cc", "w")
+        impl_template_file = open("wrapper_template.cc", "r")
+    except:
+        sys.stderr.write('Failed to open C++ file(s)\n')
+        sys.exit(1)
+    dump_file(header_file, header_template_file, class_name, module)
+    dump_file(impl_file, impl_template_file, class_name, module)
+
+usage = '''usage: %prog [options] class_name'''
+
+if __name__ == "__main__":
+    parser = OptionParser(usage=usage)
+    parser.add_option('-o', '--output', action='store', dest='output',
+                      default=None, metavar='FILE',
+                      help='prefix of output file names [default: derived from the class name]')
+    parser.add_option('-m', '--module', action='store', dest='module',
+                      default='dns',
+                      help='C++ module name of the wrapper (for namespaces) [default: dns]')
+    (options, args) = parser.parse_args()
+
+    if len(args) == 0:
+        parser.error('input file is missing')
+
+    class_name = args[0]
+    if not options.output:
+        options.output = class_name.lower()
+
+    dump_wrappers(class_name, options.output, options.module)
diff --git a/src/lib/util/python/mkpywrapper.py.win32 b/src/lib/util/python/mkpywrapper.py.win32
new file mode 100755
index 0000000..8761ad5
--- /dev/null
+++ b/src/lib/util/python/mkpywrapper.py.win32
@@ -0,0 +1,100 @@
+#!/usr/bin/python
+
+# Copyright (C) 2011  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""This utility program generates a C++ header and implementation files
+that can be used as a template of C++ python binding for a C++ class.
+
+Usage: ./mkpywrapper.py ClassName
+(the script should be run on this directory)
+
+It will generate two files: classname_python.h and classname_python.cc,
+many of whose definitions are in the namespace isc::MODULE_NAME::python.
+By default MODULE_NAME will be 'dns' (because this tool is originally
+intended to be used for the C++ python binding of the DNS library), but
+can be changed via the -m command line option.
+
+The generated files contain code fragments that are commonly used in
+C++ python binding implementations.  It will define a class named
+s_ClassName which is a derived class of PyModule and can meet the
+requirement of the CPPPyObjectContainer template class (see
+pycppwrapper_util.h).  It also defines (and declares in the header file)
+"classname_type", which is of PyTypeObject and is intended to be used
+to define details of the python bindings for the ClassName class.
+
+In many cases the header file can be used as a startpoint of the
+binding development without modification.  But you may want to make
+ClassName::cppobj a constant variable (and you should if you can).
+Many definitions of classname_python.cc should also be able to be used
+just as defined, but some will need to be changed or removed.  In
+particular, you should at least adjust ClassName_init().  You'll
+probably also need to add more definitions to that file to provide
+complete features of the C++ class.
+"""
+
+import datetime, string, sys
+from optparse import OptionParser
+
+# Remember the current year to produce the copyright boilerplate
+YEAR = datetime.date.today().timetuple()[0]
+
+def dump_file(out_file, temp_file, class_name, module):
+    for line in temp_file.readlines():
+        line = line.replace("@YEAR@", str(YEAR))
+        line = line.replace("@CPPCLASS at _H", class_name.upper() + "_H")
+        line = line.replace("@CPPCLASS@", class_name)
+        line = line.replace("@cppclass@", class_name.lower())
+        line = line.replace("@MODULE@", module)
+        out_file.write(line)
+
+def dump_wrappers(class_name, output, module):
+    try:
+        if output == "-":
+            header_file = sys.stdout
+        else:
+            header_file = open(output + "_python.h", "w")
+        header_template_file = open("wrapper_template.h", "r")
+        if output == "-":
+            impl_file = sys.stdout
+        else:
+            impl_file = open(output + "_python.cc", "w")
+        impl_template_file = open("wrapper_template.cc", "r")
+    except:
+        sys.stderr.write('Failed to open C++ file(s)\n')
+        sys.exit(1)
+    dump_file(header_file, header_template_file, class_name, module)
+    dump_file(impl_file, impl_template_file, class_name, module)
+
+usage = '''usage: %prog [options] class_name'''
+
+if __name__ == "__main__":
+    parser = OptionParser(usage=usage)
+    parser.add_option('-o', '--output', action='store', dest='output',
+                      default=None, metavar='FILE',
+                      help='prefix of output file names [default: derived from the class name]')
+    parser.add_option('-m', '--module', action='store', dest='module',
+                      default='dns',
+                      help='C++ module name of the wrapper (for namespaces) [default: dns]')
+    (options, args) = parser.parse_args()
+
+    if len(args) == 0:
+        parser.error('input file is missing')
+
+    class_name = args[0]
+    if not options.output:
+        options.output = class_name.lower()
+
+    dump_wrappers(class_name, options.output, options.module)
diff --git a/src/lib/util/python/pycppwrapper_util.h b/src/lib/util/python/pycppwrapper_util.h
new file mode 100644
index 0000000..462e715
--- /dev/null
+++ b/src/lib/util/python/pycppwrapper_util.h
@@ -0,0 +1,335 @@
+// 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 __PYCPPWRAPPER_UTIL_H
+#define __PYCPPWRAPPER_UTIL_H 1
+
+#include <Python.h>
+
+#include <exceptions/exceptions.h>
+
+/**
+ * @file pycppwrapper_util.h
+ * @short Shared definitions for python/C(++) API
+ *
+ * This utility defines a set of convenient wrappers for the python C API
+ * to use it safely from our C++ bindings.  The python C API has many pitfalls
+ * such as not-so-consistent reference count policies.  Also, many existing
+ * examples are careless about error handling.  It's easy to find on the net
+ * example (even of "production use") python extensions like this:
+ *
+ * \code
+ *     new_exception = PyErr_NewException("mymodule.Exception", NULL, NULL);
+ *     // new_exception can be NULL, in which case the call to
+ *     // PyModule_AddObject will cause a surprising disruption.
+ *     PyModule_AddObject(mymodule, "Exception", new_exception); \endcode
+ *
+ * When using the python C API with C++, we should also be careful about
+ * exception safety.  The underlying C++ code (including standard C++ libraries
+ * and memory allocation) can throw exceptions, in which case we need to
+ * make sure any intermediate python objects are cleaned up (we also need to
+ * catch the C++ exceptions inside the binding and convert them to python
+ * errors, but that's a different subject).  This is not a trivial task
+ * because the python objects are represented as bare C pointers (so there's
+ * no destructor) and we need to address the exception safety along with python
+ * reference counters (so we cannot naively apply standard smart pointers).
+ *
+ * This utility tries to help address these issues.
+ *
+ * Also, it's intentional that this is a header-only utility.  This way the
+ * C++ loadable module won't depend on another C++ library (which is not
+ * necessarily wrong, but would increase management cost such as link-time
+ * troubles only for a small utility feature).
+ */
+
+namespace isc {
+namespace util {
+namespace python {
+
+/// This is thrown inside this utility when it finds a NULL pointer is passed
+/// when it should not be NULL.
+class PyCPPWrapperException : public isc::Exception {
+public:
+    PyCPPWrapperException(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+/// This helper class is similar to the standard autoptr and manages PyObject
+/// using some kind of RAII techniques.  It is, however, customized for the
+/// python C API.
+///
+/// A PyObjectContainer object is constructed with a pointer to PyObject,
+/// which is often just created dynamically.  The caller will eventually
+/// attach the object to a different python object (often a module or class)
+/// via specific methods or directly return it to the python interpreter.
+///
+/// There are two cases in destructing the object: with or without decreasing
+/// a reference to the PyObject.  If the object is intended to be an argument
+/// to another python C library that increases the reference to the object for
+/// itself, we should normally release our own reference; otherwise the
+/// reference will leak and the object won't be garbage collected.  Also, when
+/// an unexpected error happens in the form of C++ exception, we should
+/// release the reference to prevent resource leak.
+///
+/// In some other cases, we should simply give our reference to the caller.
+/// That is the case when the created object itself is a return value of
+/// an extended python method written in the C++ binding.  Likewise, some
+/// python C library functions "steal" the reference.  In these cases we
+/// should not decrease the reference; otherwise it would cause duplicate free.
+///
+/// By default, the destructor of this class releases the reference to the
+/// PyObject.  If this behavior is desirable, you can extract the original
+/// bare pointer to the PyObject by the \c get() method.  If you don't want
+/// the reference to be decreased, the original bare pointer should be
+/// extracted using the \c release() method.
+///
+/// In some other cases, it would be convenient if it's possible to create
+/// an "empty" container and reset it with a Python object later.
+/// For example, we may want to create a temporary Python object in the
+/// middle of a function and make sure that it's valid within the rest of
+/// the function scope, while we want to make sure its reference is released
+/// when the function returns (either normally or as a result of exception).
+/// To allow this scenario, this class defines the default constructor
+/// and the \c reset() method.  The default constructor allows the class
+/// object with an "empty" (NULL) Python object, while \c reset() allows
+/// the stored object to be replaced with a new one.  If there's a valid
+/// object was already set, \c reset() releases its reference.
+/// In general, it's safer to construct the container object with a valid
+/// Python object pointer.  The use of the default constructor and
+/// \c reset() should therefore be restricted to cases where it's
+/// absolutely necessary.
+///
+/// There are two convenience methods for commonly used operations:
+/// \c installAsClassVariable() to add the PyObject as a class variable
+/// and \c installToModule to add the PyObject to a specified python module.
+/// These methods (at least to some extent) take care of the reference to
+/// the object (either release or keep) depending on the usage context so
+/// that the user don't have to worry about it.
+///
+/// On construction, this class expects the pointer can be NULL.
+/// If it happens it immediately throws a \c PyCPPWrapperException exception.
+/// This behavior is to convert failures in the python C API (such as
+/// PyObject_New() returning NULL) to C++ exception so that we can unify
+/// error handling in the style of C++ exceptions.
+///
+/// Examples 1: To create a tuple of two python objects, do this:
+///
+/// \code
+///     try {
+///         PyObjectContainer container0(Py_BuildValue("I", 0));
+///         PyObjectContainer container1(Py_BuildValue("s", cppobj.toText().c_str()));
+///         return (Py_BuildValue("OO", container0.get(), container1.get()));
+///     } catch { ... set python exception, etc ... } \endcode
+///
+/// Commonly deployed buggy implementation to achieve this would be like this:
+/// \code
+///    return (Py_BuildValue("OO", Py_BuildValue("I", 0),
+///                          Py_BuildValue("s", cppobj.toText().c_str())));
+/// \endcode
+/// One clear bug of this code is that references to the element objects of
+/// the tuple will leak.
+/// (Assuming \c cppobj.toText() can throw) this code is also not exception
+/// safe; if \c cppobj.toText() throws the reference to the first object
+/// will leak, even if the code tried to do the necessary cleanup in the
+/// successful case.
+/// Further, this code naively passes the result of the first two calls to
+/// \c Py_BuildValue() to the third one even if they can be NULL.
+/// In this specific case, it happens to be okay because \c Py_BuildValue()
+/// accepts NULL and treats it as an indication of error.  But not all
+/// python C library works that way (remember, the API is so inconsistent)
+/// and we need to refer to the API manual every time we have to worry about
+/// passing a NULL object to a library function.  We'd certainly like to
+/// avoid such development overhead.  The code using \c PyObjectContainer
+/// addresses all these problems.
+///
+/// Examples 2: Install a (constant) variable to a class.
+///
+/// \code
+///    try {
+///        // installClassVariable is a wrapper of
+///        // PyObjectContainer::installAsClassVariable.  See below.
+///        installClassVariable(myclass_type, "SOME_CONSTANT",
+///                             Py_BuildValue("I", 0));
+///    } catch { ... }
+/// \endcode
+///
+/// Examples 3: Install a custom exception to a module.
+///
+/// \code
+///    PyObject* new_exception; // publicly visible
+///    ...
+///    try {
+///        new_exception = PyErr_NewException("mymodule.NewException",
+///                                           NULL, NULL);
+///        PyObjectContainer(new_exception).installToModule(mymodule,
+///                                                         "NewException");
+///    } catch { ... }
+/// \endcode
+///
+/// Note that \c installToModule() keeps the reference to \c new_exception
+/// by default.  This is a common practice when we introduce a custom
+/// exception in a python biding written in C/C++.  See the code comment
+/// of the method for more details.
+struct PyObjectContainer {
+    PyObjectContainer() : obj_(NULL) {}
+    PyObjectContainer(PyObject* obj) : obj_(obj) {
+        if (obj_ == NULL) {
+            isc_throw(PyCPPWrapperException, "Unexpected NULL PyObject, "
+                      "probably due to short memory");
+        }
+    }
+    ~PyObjectContainer() {
+        if (obj_ != NULL) {
+            Py_DECREF(obj_);
+        }
+    }
+    void reset(PyObject* obj) {
+        if (obj == NULL) {
+            isc_throw(PyCPPWrapperException, "Unexpected NULL PyObject, "
+                      "probably due to short memory");
+        }
+        if (obj_ != NULL) {
+            Py_DECREF(obj_);
+        }
+        obj_ = obj;
+    }
+    PyObject* get() {
+        return (obj_);
+    }
+    PyObject* release() {
+        PyObject* ret = obj_;
+        obj_ = NULL;
+        return (ret);
+    }
+
+    // Install the enclosed PyObject to the specified python class 'pyclass'
+    // as a variable named 'name'.
+    void installAsClassVariable(PyTypeObject& pyclass, const char* name) {
+        if (PyDict_SetItemString(pyclass.tp_dict, name, obj_) < 0) {
+            isc_throw(PyCPPWrapperException, "Failed to set a class variable, "
+                      "probably due to short memory");
+        }
+        // Ownership successfully transferred to the class object.  We'll let
+        // it be released in the destructor.
+    }
+
+    // Install the enclosed PyObject to the specified module 'mod' as an
+    // object named 'name'.
+    // By default, this method explicitly keeps the reference to the object
+    // even after the module "steals" it.  To cancel this behavior and give
+    // the reference to the module completely, the third parameter 'keep_ref'
+    // should be set to false.
+    void installToModule(PyObject* mod, const char* name,
+                         bool keep_ref = true)
+    {
+        if (PyModule_AddObject(mod, name, obj_) < 0) {
+            isc_throw(PyCPPWrapperException, "Failed to add an object to "
+                      "module, probably due to short memory");
+        }
+        // PyModule_AddObject has "stolen" the reference, so unless we
+        // have to retain it ourselves we don't (shouldn't) decrease it.
+        // However, we actually often need to keep our own reference because
+        // objects added to a module are often referenced via non local
+        // C/C++ variables in various places of the C/C++ code.  In order
+        // for the code to run safely even if some buggy/evil python program
+        // performs 'del mod.obj', we need the extra reference.  See, e.g.:
+        // http://docs.python.org/py3k/c-api/init.html#Py_Initialize
+        // http://mail.python.org/pipermail/python-dev/2005-June/054238.html
+        if (keep_ref) {
+            Py_INCREF(obj_);
+        }
+        obj_ = NULL;
+    }
+
+protected:
+    PyObject* obj_;
+};
+
+/// This templated class is a derived class of \c PyObjectContainer and
+/// manages C++-class based python objects.
+///
+/// The template parameter \c PYSTRUCT must be a derived class (structure) of
+/// \c PyObject that has a member variable named \c cppobj, which must be a
+/// a pointer to \c CPPCLASS (the second template parameter).
+///
+/// For example, to define a custom python class based on a C++ class, MyClass,
+/// we'd define a class (struct) named \c s_MyClass like this:
+/// \code
+///    class s_MyClass : public PyObject {
+///    public:
+///       s_MyClass() : cppobj(NULL) {}
+///       MyClass* cppobj;
+///    };
+/// \endcode
+///
+/// And, to build and return a python version of MyClass object, write the
+/// following C++ code:
+/// \code
+///    typedef CPPPyObjectContainer<s_MyClass, MyClass> MyContainer;
+///    try {
+///        // below, myclass_type is of \c PyTypeObject that defines
+///        // a python class (type) for MyClass
+///        MyContainer container(PyObject_New(s_MyClass, myclass_type));
+///        container.set(new MyClass());
+///        return (container.release()); // give the reference to the caller
+///    } catch { ... }
+/// \endcode
+///
+/// This code prevents bugs like NULL pointer dereference when \c PyObject_New
+/// fails or resource leaks when new'ing \c MyClass results in an exception.
+/// Note that we use \c release() (derived from the base class) instead of
+/// \c get(); in this case we should simply pass the reference generated in
+/// \c PyObject_New() to the caller.
+template <typename PYSTRUCT, typename CPPCLASS>
+struct CPPPyObjectContainer : public PyObjectContainer {
+    explicit CPPPyObjectContainer(PYSTRUCT* obj) : PyObjectContainer(obj) {}
+
+    // This method associates a C++ object with the corresponding python
+    // object enclosed in this class.
+    void set(CPPCLASS* value) {
+        if (value == NULL) {
+            isc_throw(PyCPPWrapperException, "Unexpected NULL C++ object, "
+                      "probably due to short memory");
+        }
+        static_cast<PYSTRUCT*>(obj_)->cppobj = value;
+    }
+
+    // This is a convenience short cut to associate a C++ object with the
+    // python object and install it to the specified python class \c pyclass
+    // as a variable named \c name.
+    void installAsClassVariable(PyTypeObject& pyclass, const char* name,
+                                CPPCLASS* value)
+    {
+        set(value);
+        PyObjectContainer::installAsClassVariable(pyclass, name);
+    }
+};
+
+/// A shortcut function to install a python class variable.
+///
+/// It installs a python object \c obj to a specified class \c pyclass
+/// as a variable named \c name.
+inline void
+installClassVariable(PyTypeObject& pyclass, const char* name, PyObject* obj) {
+    PyObjectContainer(obj).installAsClassVariable(pyclass, name);
+}
+
+} // namespace python
+} // namespace util
+} // namespace isc
+#endif // __PYCPPWRAPPER_UTIL_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/util/python/wrapper_template.cc b/src/lib/util/python/wrapper_template.cc
new file mode 100644
index 0000000..426ced5
--- /dev/null
+++ b/src/lib/util/python/wrapper_template.cc
@@ -0,0 +1,309 @@
+// Copyright (C) @YEAR@  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.
+
+// Enable this if you use s# variants with PyArg_ParseTuple(), see
+// http://docs.python.org/py3k/c-api/arg.html#strings-and-buffers
+//#define PY_SSIZE_T_CLEAN
+
+// Python.h needs to be placed at the head of the program file, see:
+// http://docs.python.org/py3k/extending/extending.html#a-simple-example
+#include <Python.h>
+
+#include <string>
+#include <stdexcept>
+
+#include <util/python/pycppwrapper_util.h>
+
+#include "@cppclass at _python.h"
+
+using namespace std;
+using namespace isc::util::python;
+using namespace isc::@MODULE@;
+using namespace isc::@MODULE@::python;
+
+//
+// Definition of the classes
+//
+
+// For each class, we need a struct, a helper functions (init, destroy,
+// and static wrappers around the methods we export), a list of methods,
+// and a type description
+
+//
+// @CPPCLASS@
+//
+
+// Trivial constructor.
+s_ at CPPCLASS@::s_ at CPPCLASS@() : cppobj(NULL) {
+}
+
+namespace {
+// Shortcut type which would be convenient for adding class variables safely.
+typedef CPPPyObjectContainer<s_ at CPPCLASS@, @CPPCLASS@> @CPPCLASS at Container;
+
+//
+// We declare the functions here, the definitions are below
+// the type definition of the object, since both can use the other
+//
+
+// General creation and destruction
+int @CPPCLASS at _init(s_ at CPPCLASS@* self, PyObject* args);
+void @CPPCLASS at _destroy(s_ at CPPCLASS@* self);
+
+// These are the functions we export
+// ADD/REMOVE/MODIFY THE FOLLOWING AS APPROPRIATE FOR THE ACTUAL CLASS.
+//
+PyObject* @CPPCLASS at _toText(const s_ at CPPCLASS@* const self);
+PyObject* @CPPCLASS at _str(PyObject* self);
+PyObject* @CPPCLASS at _richcmp(const s_ at CPPCLASS@* const self,
+                            const s_ at CPPCLASS@* const other, int op);
+
+// This is quite specific pydnspp.  For other wrappers this should probably
+// be removed.
+PyObject* @CPPCLASS at _toWire(const s_ at CPPCLASS@* self, PyObject* args);
+
+// These are the functions we export
+// For a minimal support, we don't need them.
+
+// This list contains the actual set of functions we have in
+// python. Each entry has
+// 1. Python method name
+// 2. Our static function here
+// 3. Argument type
+// 4. Documentation
+PyMethodDef @CPPCLASS at _methods[] = {
+    { "to_text", reinterpret_cast<PyCFunction>(@CPPCLASS at _toText), METH_NOARGS,
+      "Returns the text representation" },
+    // This is quite specific pydnspp.  For other wrappers this should probably
+    // be removed:
+    { "to_wire", reinterpret_cast<PyCFunction>(@CPPCLASS at _toWire), METH_VARARGS,
+      "Converts the @CPPCLASS@ object to wire format.\n"
+      "The argument can be either a MessageRenderer or an object that "
+      "implements the sequence interface. If the object is mutable "
+      "(for instance a bytearray()), the wire data is added in-place.\n"
+      "If it is not (for instance a bytes() object), a new object is "
+      "returned" },
+    { NULL, NULL, 0, NULL }
+};
+
+// This is a template of typical code logic of python class initialization
+// with C++ backend.  You'll need to adjust it according to details of the
+// actual C++ class.
+int
+ at CPPCLASS@_init(s_ at CPPCLASS@* self, PyObject* args) {
+    try {
+        if (PyArg_ParseTuple(args, "REPLACE ME")) {
+            // YOU'LL NEED SOME VALIDATION, PREPARATION, ETC, HERE.
+            self->cppobj = new @CPPCLASS@(/*NECESSARY PARAMS*/);
+            return (0);
+        }
+    } catch (const exception& ex) {
+        const string ex_what = "Failed to construct @CPPCLASS@ object: " +
+            string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+        return (-1);
+    } catch (...) {
+        PyErr_SetString(po_IscException,
+                        "Unexpected exception in constructing @CPPCLASS@");
+        return (-1);
+    }
+
+    PyErr_SetString(PyExc_TypeError,
+                    "Invalid arguments to @CPPCLASS@ constructor");
+
+    return (-1);
+}
+
+// This is a template of typical code logic of python object destructor.
+// In many cases you can use it without modification, but check that carefully.
+void
+ at CPPCLASS@_destroy(s_ at CPPCLASS@* const self) {
+    delete self->cppobj;
+    self->cppobj = NULL;
+    Py_TYPE(self)->tp_free(self);
+}
+
+// This should be able to be used without modification as long as the
+// underlying C++ class has toText().
+PyObject*
+ at CPPCLASS@_toText(const s_ at CPPCLASS@* const self) {
+    try {
+        // toText() could throw, so we need to catch any exceptions below.
+        return (Py_BuildValue("s", self->cppobj->toText().c_str()));
+    } catch (const exception& ex) {
+        const string ex_what =
+            "Failed to convert @CPPCLASS@ object to text: " +
+            string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError, "Unexpected failure in "
+                        "converting @CPPCLASS@ object to text");
+    }
+    return (NULL);
+}
+
+PyObject*
+ at CPPCLASS@_str(PyObject* self) {
+    // Simply call the to_text method we already defined
+    return (PyObject_CallMethod(self, const_cast<char*>("to_text"),
+                                const_cast<char*>("")));
+}
+
+PyObject* 
+ at CPPCLASS@_richcmp(const s_ at CPPCLASS@* const self,
+                   const s_ at CPPCLASS@* const other,
+                   const int op)
+{
+    bool c = false;
+
+    // Check for null and if the types match. If different type,
+    // simply return False
+    if (other == NULL || (self->ob_type != other->ob_type)) {
+        Py_RETURN_FALSE;
+    }
+
+    // Only equals and not equals here, unorderable type
+    switch (op) {
+    case Py_LT:
+        PyErr_SetString(PyExc_TypeError, "Unorderable type; @CPPCLASS@");
+        return (NULL);
+    case Py_LE:
+        PyErr_SetString(PyExc_TypeError, "Unorderable type; @CPPCLASS@");
+        return (NULL);
+    case Py_EQ:
+        c = (*self->cppobj == *other->cppobj);
+        break;
+    case Py_NE:
+        c = (*self->cppobj != *other->cppobj);
+        break;
+    case Py_GT:
+        PyErr_SetString(PyExc_TypeError, "Unorderable type; @CPPCLASS@");
+        return (NULL);
+    case Py_GE:
+        PyErr_SetString(PyExc_TypeError, "Unorderable type; @CPPCLASS@");
+        return (NULL);
+    }
+    if (c) {
+        Py_RETURN_TRUE;
+    } else {
+        Py_RETURN_FALSE;
+    }
+}
+} // end of unnamed namespace
+
+namespace isc {
+namespace @MODULE@ {
+namespace python {
+// This defines the complete type for reflection in python and
+// parsing of PyObject* to s_ at CPPCLASS@
+// Most of the functions are not actually implemented and NULL here.
+PyTypeObject @cppclass at _type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "@MODULE at .@CPPCLASS@",
+    sizeof(s_ at CPPCLASS@),                 // tp_basicsize
+    0,                                  // tp_itemsize
+    reinterpret_cast<destructor>(@CPPCLASS at _destroy),       // tp_dealloc
+    NULL,                               // tp_print
+    NULL,                               // tp_getattr
+    NULL,                               // tp_setattr
+    NULL,                               // tp_reserved
+    NULL,                               // tp_repr
+    NULL,                               // tp_as_number
+    NULL,                               // tp_as_sequence
+    NULL,                               // tp_as_mapping
+    NULL,                               // tp_hash
+    NULL,                               // tp_call
+    // THIS MAY HAVE TO BE CHANGED TO NULL:
+    @CPPCLASS at _str,                       // tp_str
+    NULL,                               // tp_getattro
+    NULL,                               // tp_setattro
+    NULL,                               // tp_as_buffer
+    Py_TPFLAGS_DEFAULT,                 // tp_flags
+    "The @CPPCLASS@ class objects is...(COMPLETE THIS)",
+    NULL,                               // tp_traverse
+    NULL,                               // tp_clear
+    // THIS MAY HAVE TO BE CHANGED TO NULL:
+    reinterpret_cast<richcmpfunc>(@CPPCLASS at _richcmp), // tp_richcompare
+    0,                                  // tp_weaklistoffset
+    NULL,                               // tp_iter
+    NULL,                               // tp_iternext
+    @CPPCLASS at _methods,                   // tp_methods
+    NULL,                               // tp_members
+    NULL,                               // tp_getset
+    NULL,                               // tp_base
+    NULL,                               // tp_dict
+    NULL,                               // tp_descr_get
+    NULL,                               // tp_descr_set
+    0,                                  // tp_dictoffset
+    reinterpret_cast<initproc>(@CPPCLASS at _init),            // tp_init
+    NULL,                               // tp_alloc
+    PyType_GenericNew,                  // tp_new
+    NULL,                               // tp_free
+    NULL,                               // tp_is_gc
+    NULL,                               // tp_bases
+    NULL,                               // tp_mro
+    NULL,                               // tp_cache
+    NULL,                               // tp_subclasses
+    NULL,                               // tp_weaklist
+    NULL,                               // tp_del
+    0                                   // tp_version_tag
+};
+
+// Module Initialization, all statics are initialized here
+bool
+initModulePart_ at CPPCLASS@(PyObject* mod) {
+    // We initialize the static description object with PyType_Ready(),
+    // then add it to the module. This is not just a check! (leaving
+    // this out results in segmentation faults)
+    if (PyType_Ready(&@cppclass at _type) < 0) {
+        return (false);
+    }
+    void* p = &@cppclass at _type;
+    if (PyModule_AddObject(mod, "@CPPCLASS@", static_cast<PyObject*>(p)) < 0) {
+        return (false);
+    }
+    Py_INCREF(&@cppclass at _type);
+
+    // The following template is the typical procedure for installing class
+    // variables.  If the class doesn't have a class variable, remove the
+    // entire try-catch clauses.
+    try {
+        // Constant class variables
+        installClassVariable(@cppclass at _type, "REPLACE_ME",
+                             Py_BuildValue("REPLACE ME"));
+    } catch (const exception& ex) {
+        const string ex_what =
+            "Unexpected failure in @CPPCLASS@ initialization: " +
+            string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+        return (false);
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError,
+                        "Unexpected failure in @CPPCLASS@ initialization");
+        return (false);
+    }
+
+    return (true);
+}
+
+PyObject*
+create at CPPCLASS@Object(const @CPPCLASS@& source) {
+    @CPPCLASS at Container container(PyObject_New(s_ at CPPCLASS@,
+                                               &@cppclass at _type));
+    container.set(new @CPPCLASS@(source));
+    return (container.release());
+}
+} // namespace python
+} // namespace @MODULE@
+} // namespace isc
diff --git a/src/lib/util/python/wrapper_template.h b/src/lib/util/python/wrapper_template.h
new file mode 100644
index 0000000..be701e1
--- /dev/null
+++ b/src/lib/util/python/wrapper_template.h
@@ -0,0 +1,59 @@
+// Copyright (C) @YEAR@  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 __PYTHON_ at CPPCLASS@_H
+#define __PYTHON_ at CPPCLASS@_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace @MODULE@ {
+class @CPPCLASS@;
+
+namespace python {
+
+// The s_* Class simply covers one instantiation of the object
+class s_ at CPPCLASS@ : public PyObject {
+public:
+    s_ at CPPCLASS@();
+    @CPPCLASS@* cppobj;
+};
+
+extern PyTypeObject @cppclass at _type;
+
+bool initModulePart_ at CPPCLASS@(PyObject* mod);
+
+// Note: this utility function works only when @CPPCLASS@ is a copy
+// constructable.
+// And, it would only be useful when python binding needs to create this
+// object frequently.  Otherwise, it would (or should) probably be better to
+// remove the declaration and definition of this function.
+//
+/// This is a simple shortcut to create a python @CPPCLASS@ object (in the
+/// form of a pointer to PyObject) with minimal exception safety.
+/// On success, it returns a valid pointer to PyObject with a reference
+/// counter of 1; if something goes wrong it throws an exception (it never
+/// returns a NULL pointer).
+/// This function is expected to be called within a try block
+/// followed by necessary setup for python exception.
+PyObject* create at CPPCLASS@Object(const @CPPCLASS@& source);
+
+} // namespace python
+} // namespace @MODULE@
+} // namespace isc
+#endif // __PYTHON_ at CPPCLASS@_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/util/pyunittests/Makefile.am b/src/lib/util/pyunittests/Makefile.am
new file mode 100644
index 0000000..63ccf2a
--- /dev/null
+++ b/src/lib/util/pyunittests/Makefile.am
@@ -0,0 +1,17 @@
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+pyexec_LTLIBRARIES = pyunittests_util.la
+pyunittests_util_la_SOURCES = pyunittests_util.cc
+pyunittests_util_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
+pyunittests_util_la_LDFLAGS = $(PYTHON_LDFLAGS)
+# Note: PYTHON_CXXFLAGS may have some -Wno... workaround, which must be
+# placed after -Wextra defined in AM_CXXFLAGS
+pyunittests_util_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS)
+
+# Python prefers .so, while some OSes (specifically MacOS) use a different
+# suffix for dynamic objects.  -module is necessary to work this around.
+pyunittests_util_la_LDFLAGS += -module
+pyunittests_util_la_LIBADD = $(top_builddir)/src/lib/util/libutil.la
+pyunittests_util_la_LIBADD += $(PYTHON_LIB)
diff --git a/src/lib/util/pyunittests/pyunittests_util.cc b/src/lib/util/pyunittests/pyunittests_util.cc
new file mode 100644
index 0000000..d266c84
--- /dev/null
+++ b/src/lib/util/pyunittests/pyunittests_util.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 <Python.h>
+
+#include <stdint.h>
+
+// see util/time_utilities.h
+namespace isc {
+namespace util {
+namespace detail {
+extern int64_t (*gettimeFunction)();
+}
+}
+}
+
+namespace {
+int64_t fake_current_time;
+
+int64_t
+getFakeTime() {
+    return (fake_current_time);
+}
+
+PyObject*
+fixCurrentTime(PyObject*, PyObject* args) {
+    PyObject* maybe_none;
+    if (PyArg_ParseTuple(args, "L", &fake_current_time)) {
+        isc::util::detail::gettimeFunction = getFakeTime;
+    } else if (PyArg_ParseTuple(args, "O", &maybe_none) &&
+               maybe_none == Py_None) {
+        isc::util::detail::gettimeFunction = NULL;
+    } else {
+         PyErr_SetString(PyExc_TypeError, "Invalid arguments to "
+                         "pyunittests_util.fix_current_time");
+         return (NULL);
+    }
+
+    PyErr_Clear();
+    Py_RETURN_NONE;
+}
+
+PyMethodDef PyUnittestsUtilMethods[] = {
+    { "fix_current_time", fixCurrentTime, METH_VARARGS,
+      "Fix the current system time at the specified (fake) value.\n\n"
+      "This is useful for testing modules that depend on the current time.\n"
+      "Note that it only affects C++ modules that use gettimeWrapper() "
+      "defined in libutil, which allows a hook for testing.\n"
+      "If an integer (signed 64bit) is given, the current time will be fixed "
+      "to that value; if None is specified (which is the default) the use of "
+      "faked time will be canceled."
+    },
+    { NULL, NULL, 0, NULL}
+};
+
+PyModuleDef pyunittests_util = {
+    { PyObject_HEAD_INIT(NULL) NULL, 0, NULL},
+    "pyunittests_util",
+    "This module is a collection of utilities useful for testing "
+    "the BIND 10 C++ binding modules.",
+    -1,
+    PyUnittestsUtilMethods,
+    NULL,
+    NULL,
+    NULL,
+    NULL
+};
+} // end of unnamed namespace
+
+PyMODINIT_FUNC
+PyInit_pyunittests_util(void) {
+    return (PyModule_Create(&pyunittests_util));
+}
diff --git a/src/lib/util/strutil.cc b/src/lib/util/strutil.cc
index 53ec236..e0fc1d9 100644
--- a/src/lib/util/strutil.cc
+++ b/src/lib/util/strutil.cc
@@ -132,6 +132,17 @@ format(const std::string& format, const std::vector<std::string>& args) {
     return (result);
 }
 
+std::string
+getToken(std::istringstream& iss) {
+    string token;
+    iss >> token;
+    if (iss.bad() || iss.fail()) {
+        isc_throw(StringTokenError, "could not read token from string");
+    }
+    return (token);
+}
+
+
 } // namespace str
 } // namespace util
 } // namespace isc
diff --git a/src/lib/util/strutil.h b/src/lib/util/strutil.h
index e044c15..0dbbe96 100644
--- a/src/lib/util/strutil.h
+++ b/src/lib/util/strutil.h
@@ -18,7 +18,10 @@
 #include <algorithm>
 #include <cctype>
 #include <string>
+#include <sstream>
 #include <vector>
+#include <exceptions/exceptions.h>
+#include <boost/lexical_cast.hpp>
 
 namespace isc {
 namespace util {
@@ -26,6 +29,16 @@ namespace str {
 
 /// \brief A Set of C++ Utilities for Manipulating Strings
 
+///
+/// \brief A standard string util exception that is thrown if getToken or
+/// numToToken are called with bad input data
+///
+class StringTokenError : public Exception {
+public:
+    StringTokenError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
 /// \brief Normalize Backslash
 ///
 /// Only relevant to Windows, this replaces all "\" in a string with "/" and
@@ -140,6 +153,55 @@ std::string format(const std::string& format,
     const std::vector<std::string>& args);
 
 
+/// \brief Returns one token from the given stringstream
+///
+/// Using the >> operator, with basic error checking
+///
+/// \exception StringTokenError if the token cannot be read from the stream
+///
+/// \param iss stringstream to read one token from
+///
+/// \return the first token read from the stringstream
+std::string getToken(std::istringstream& iss);
+
+/// \brief Converts a string token to an *unsigned* integer.
+///
+/// The value is converted using a lexical cast, with error and bounds
+/// checking.
+///
+/// NumType is a *signed* integral type (e.g. int32_t) that is sufficiently
+/// wide to store resulting integers.
+///
+/// BitSize is the maximum number of bits that the resulting integer can take.
+/// This function first checks whether the given token can be converted to
+/// an integer of NumType type.  It then confirms the conversion result is
+/// within the valid range, i.e., [0, 2^BitSize - 1].  The second check is
+/// necessary because lexical_cast<T> where T is an unsigned integer type
+/// doesn't correctly reject negative numbers when compiled with SunStudio.
+///
+/// \exception StringTokenError if the value is out of range, or if it
+///            could not be converted
+///
+/// \param num_token the string token to convert
+///
+/// \return the converted value, of type NumType
+template <typename NumType, int BitSize>
+NumType
+tokenToNum(const std::string& num_token) {
+    NumType num;
+    try {
+        num = boost::lexical_cast<NumType>(num_token);
+    } catch (const boost::bad_lexical_cast&) {
+        isc_throw(StringTokenError, "Invalid SRV numeric parameter: " <<
+                  num_token);
+    }
+    if (num < 0 || num >= (static_cast<NumType>(1) << BitSize)) {
+        isc_throw(StringTokenError, "Numeric SRV parameter out of range: " <<
+                  num);
+    }
+    return (num);
+}
+
 } // namespace str
 } // namespace util
 } // namespace isc
diff --git a/src/lib/util/tests/Makefile.am b/src/lib/util/tests/Makefile.am
index 7b97202..47243f8 100644
--- a/src/lib/util/tests/Makefile.am
+++ b/src/lib/util/tests/Makefile.am
@@ -1,8 +1,6 @@
 SUBDIRS = .
 
 AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
-AM_CPPFLAGS += -I$(top_srcdir)/src/lib/util -I$(top_builddir)/src/lib/util
-AM_CPPFLAGS += -I$(top_srcdir)/src/lib/exceptions -I$(top_builddir)/src/lib/exceptions
 AM_CPPFLAGS += $(BOOST_INCLUDES)
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 
@@ -15,26 +13,30 @@ CLEANFILES = *.gcno *.gcda
 TESTS =
 if HAVE_GTEST
 TESTS += run_unittests
-run_unittests_SOURCES  =
-run_unittests_SOURCES += filename_unittest.cc
-run_unittests_SOURCES += strutil_unittest.cc
-run_unittests_SOURCES += run_unittests.cc
+run_unittests_SOURCES  = run_unittests.cc
 run_unittests_SOURCES += base32hex_unittest.cc
 run_unittests_SOURCES += base64_unittest.cc
-run_unittests_SOURCES += hex_unittest.cc
-run_unittests_SOURCES += sha1_unittest.cc
 run_unittests_SOURCES += buffer_unittest.cc
-run_unittests_SOURCES += time_utilities_unittest.cc
-run_unittests_SOURCES += random_number_generator_unittest.cc
-run_unittests_SOURCES += lru_list_unittest.cc
+run_unittests_SOURCES += fd_share_tests.cc
+run_unittests_SOURCES += fd_tests.cc
+run_unittests_SOURCES += filename_unittest.cc
+run_unittests_SOURCES += hex_unittest.cc
 run_unittests_SOURCES += io_utilities_unittest.cc
+run_unittests_SOURCES += lru_list_unittest.cc
 run_unittests_SOURCES += qid_gen_unittest.cc
+run_unittests_SOURCES += random_number_generator_unittest.cc
+run_unittests_SOURCES += sha1_unittest.cc
+run_unittests_SOURCES += strutil_unittest.cc
+run_unittests_SOURCES += time_utilities_unittest.cc
 
 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/libutil.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/io/libutil_io.la
+run_unittests_LDADD += \
+	$(top_builddir)/src/lib/util/unittests/libutil_unittests.la
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 endif
 
diff --git a/src/lib/util/tests/base32hex_unittest.cc b/src/lib/util/tests/base32hex_unittest.cc
index 74063d5..89d441e 100644
--- a/src/lib/util/tests/base32hex_unittest.cc
+++ b/src/lib/util/tests/base32hex_unittest.cc
@@ -79,6 +79,11 @@ TEST_F(Base32HexTest, decode) {
     // whitespace should be allowed
     decodeCheck("CP NM\tUOG=", decoded_data, "foob");
     decodeCheck("CPNMU===\n", decoded_data, "foo");
+    decodeCheck("  CP NM\tUOG=", decoded_data, "foob");
+    decodeCheck(" ", decoded_data, "");
+
+    // Incomplete input
+    EXPECT_THROW(decodeBase32Hex("CPNMUOJ", decoded_data), BadValue);
 
     // invalid number of padding characters
     EXPECT_THROW(decodeBase32Hex("CPNMU0==", decoded_data), BadValue);
diff --git a/src/lib/util/tests/base64_unittest.cc b/src/lib/util/tests/base64_unittest.cc
index 2e3d1f2..b0c926d 100644
--- a/src/lib/util/tests/base64_unittest.cc
+++ b/src/lib/util/tests/base64_unittest.cc
@@ -66,6 +66,12 @@ TEST_F(Base64Test, decode) {
     decodeCheck("Zm 9v\tYmF\ny", decoded_data, "foobar");
     decodeCheck("Zm9vYg==", decoded_data, "foob");
     decodeCheck("Zm9vYmE=\n", decoded_data, "fooba");
+    decodeCheck(" Zm9vYmE=\n", decoded_data, "fooba");
+    decodeCheck(" ", decoded_data, "");
+    decodeCheck("\n\t", decoded_data, "");
+
+    // incomplete input
+    EXPECT_THROW(decodeBase64("Zm9vYmF", decoded_data), BadValue);
 
     // only up to 2 padding characters are allowed
     EXPECT_THROW(decodeBase64("A===", decoded_data), BadValue);
diff --git a/src/lib/util/tests/fd_share_tests.cc b/src/lib/util/tests/fd_share_tests.cc
new file mode 100644
index 0000000..cc92e47
--- /dev/null
+++ b/src/lib/util/tests/fd_share_tests.cc
@@ -0,0 +1,74 @@
+// 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 <util/io/fd.h>
+#include <util/io/fd_share.h>
+
+#include <util/unittests/fork.h>
+
+#include <gtest/gtest.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <cstdio>
+
+using namespace isc::util::io;
+using namespace isc::util::unittests;
+
+namespace {
+
+// We test that we can transfer a pipe over other pipe
+TEST(FDShare, transfer) {
+    // Get a pipe and fork
+    int pipes[2];
+    ASSERT_NE(-1, socketpair(AF_UNIX, SOCK_STREAM, 0, pipes));
+    pid_t sender(fork());
+    ASSERT_NE(-1, sender);
+    if(sender) { // We are in parent
+        // Close the other side of pipe, we want only writible one
+        EXPECT_NE(-1, close(pipes[0]));
+        // Get a process to check data
+        int fd(0);
+        pid_t checker(check_output(&fd, "data", 4));
+        ASSERT_NE(-1, checker);
+        // Now, send the file descriptor, close it and close the pipe
+        EXPECT_NE(-1, send_fd(pipes[1], fd));
+        EXPECT_NE(-1, close(pipes[1]));
+        EXPECT_NE(-1, close(fd));
+        // Check both subprocesses ended well
+        EXPECT_TRUE(process_ok(sender));
+        EXPECT_TRUE(process_ok(checker));
+    } else { // We are in child. We do not use ASSERT here
+        // Close the write end, we only read
+        if(close(pipes[1])) {
+            exit(1);
+        }
+        // Get the file descriptor
+        int fd(recv_fd(pipes[0]));
+        if(fd == -1) {
+            exit(1);
+        }
+        // This pipe is not needed
+        if(close(pipes[0])) {
+            exit(1);
+        }
+        // Send "data" trough the received fd, close it and be done
+        if(!write_data(fd, "data", 4) || close(fd) == -1) {
+            exit(1);
+        }
+        exit(0);
+    }
+}
+
+}
diff --git a/src/lib/util/tests/fd_tests.cc b/src/lib/util/tests/fd_tests.cc
new file mode 100644
index 0000000..42a61f5
--- /dev/null
+++ b/src/lib/util/tests/fd_tests.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 <util/io/fd.h>
+
+#include <util/unittests/fork.h>
+
+#include <gtest/gtest.h>
+
+#ifdef _WIN32
+#define close(x) _close(x)
+#endif
+
+using namespace isc::util::io;
+using namespace isc::util::unittests;
+
+namespace {
+
+// Make sure the test is large enough and does not fit into one
+// read or write request
+const size_t TEST_DATA_SIZE = 8 * 1024 * 1024;
+
+class FDTest : public ::testing::Test {
+    public:
+        unsigned char *data, *buffer;
+        FDTest() :
+            // We do not care what is inside, we just need it to be the same
+            data(new unsigned char[TEST_DATA_SIZE]),
+            buffer(NULL)
+        { }
+        ~ FDTest() {
+            delete[] data;
+            delete[] buffer;
+        }
+};
+
+// Test we read what was sent
+TEST_F(FDTest, read) {
+    int read_pipe(0);
+    buffer = new unsigned char[TEST_DATA_SIZE];
+    pid_t feeder(provide_input(&read_pipe, data, TEST_DATA_SIZE));
+    ASSERT_GE(feeder, 0);
+    ssize_t received(read_data(read_pipe, buffer, TEST_DATA_SIZE));
+    EXPECT_TRUE(process_ok(feeder));
+    EXPECT_EQ(TEST_DATA_SIZE, received);
+    EXPECT_EQ(0, memcmp(data, buffer, received));
+}
+
+// Test we write the correct thing
+TEST_F(FDTest, write) {
+    int write_pipe(0);
+    pid_t checker(check_output(&write_pipe, data, TEST_DATA_SIZE));
+    ASSERT_GE(checker, 0);
+    EXPECT_TRUE(write_data(write_pipe, data, TEST_DATA_SIZE));
+    EXPECT_EQ(0, close(write_pipe));
+    EXPECT_TRUE(process_ok(checker));
+}
+
+}
diff --git a/src/lib/util/tests/filename_unittest.cc b/src/lib/util/tests/filename_unittest.cc
index 33e6456..07f3525 100644
--- a/src/lib/util/tests/filename_unittest.cc
+++ b/src/lib/util/tests/filename_unittest.cc
@@ -51,42 +51,49 @@ TEST_F(FilenameTest, Components) {
     EXPECT_EQ("/alpha/beta/", fname.directory());
     EXPECT_EQ("gamma", fname.name());
     EXPECT_EQ(".delta", fname.extension());
+    EXPECT_EQ("gamma.delta", fname.nameAndExtension());
 
     // Directory only
     fname.setName("/gamma/delta/");
     EXPECT_EQ("/gamma/delta/", fname.directory());
     EXPECT_EQ("", fname.name());
     EXPECT_EQ("", fname.extension());
+    EXPECT_EQ("", fname.nameAndExtension());
 
     // Filename only
     fname.setName("epsilon");
     EXPECT_EQ("", fname.directory());
     EXPECT_EQ("epsilon", fname.name());
     EXPECT_EQ("", fname.extension());
+    EXPECT_EQ("epsilon", fname.nameAndExtension());
 
     // Extension only
     fname.setName(".zeta");
     EXPECT_EQ("", fname.directory());
     EXPECT_EQ("", fname.name());
     EXPECT_EQ(".zeta", fname.extension());
+    EXPECT_EQ(".zeta", fname.nameAndExtension());
 
     // Missing directory
     fname.setName("eta.theta");
     EXPECT_EQ("", fname.directory());
     EXPECT_EQ("eta", fname.name());
     EXPECT_EQ(".theta", fname.extension());
+    EXPECT_EQ("eta.theta", fname.nameAndExtension());
 
     // Missing filename
     fname.setName("/iota/.kappa");
     EXPECT_EQ("/iota/", fname.directory());
     EXPECT_EQ("", fname.name());
     EXPECT_EQ(".kappa", fname.extension());
+    EXPECT_EQ(".kappa", fname.nameAndExtension());
 
     // Missing extension
     fname.setName("lambda/mu/nu");
     EXPECT_EQ("lambda/mu/", fname.directory());
     EXPECT_EQ("nu", fname.name());
     EXPECT_EQ("", fname.extension());
+    EXPECT_EQ("nu", fname.nameAndExtension());
 
     // Check that the decomposition can occur in the presence of leading and
     // trailing spaces
@@ -94,18 +101,21 @@ TEST_F(FilenameTest, Components) {
     EXPECT_EQ("lambda/mu/", fname.directory());
     EXPECT_EQ("nu", fname.name());
     EXPECT_EQ("", fname.extension());
+    EXPECT_EQ("nu", fname.nameAndExtension());
 
     // Empty string
     fname.setName("");
     EXPECT_EQ("", fname.directory());
     EXPECT_EQ("", fname.name());
     EXPECT_EQ("", fname.extension());
+    EXPECT_EQ("", fname.nameAndExtension());
 
     // ... and just spaces
     fname.setName("  ");
     EXPECT_EQ("", fname.directory());
     EXPECT_EQ("", fname.name());
     EXPECT_EQ("", fname.extension());
+    EXPECT_EQ("", fname.nameAndExtension());
 
     // Check corner cases - where separators are present, but strings are
     // absent.
@@ -113,16 +123,19 @@ TEST_F(FilenameTest, Components) {
     EXPECT_EQ("/", fname.directory());
     EXPECT_EQ("", fname.name());
     EXPECT_EQ("", fname.extension());
+    EXPECT_EQ("", fname.nameAndExtension());
 
     fname.setName(".");
     EXPECT_EQ("", fname.directory());
     EXPECT_EQ("", fname.name());
     EXPECT_EQ(".", fname.extension());
+    EXPECT_EQ(".", fname.nameAndExtension());
 
     fname.setName("/.");
     EXPECT_EQ("/", fname.directory());
     EXPECT_EQ("", fname.name());
     EXPECT_EQ(".", fname.extension());
+    EXPECT_EQ(".", fname.nameAndExtension());
 
     // Note that the space is a valid filename here; only leading and trailing
     // spaces should be trimmed.
@@ -130,11 +143,13 @@ TEST_F(FilenameTest, Components) {
     EXPECT_EQ("/", fname.directory());
     EXPECT_EQ(" ", fname.name());
     EXPECT_EQ(".", fname.extension());
+    EXPECT_EQ(" .", fname.nameAndExtension());
 
     fname.setName(" / . ");
     EXPECT_EQ("/", fname.directory());
     EXPECT_EQ(" ", fname.name());
     EXPECT_EQ(".", fname.extension());
+    EXPECT_EQ(" .", fname.nameAndExtension());
 }
 
 // Check that the expansion with a default works.
@@ -177,3 +192,40 @@ TEST_F(FilenameTest, UseAsDefault) {
     EXPECT_EQ("/s/t/u", fname.useAsDefault("/s/t/u"));
     EXPECT_EQ("/a/b/c", fname.useAsDefault(""));
 }
+
+TEST_F(FilenameTest, setDirectory) {
+    Filename fname("a.b");
+    EXPECT_EQ("", fname.directory());
+    EXPECT_EQ("a.b", fname.fullName());
+    EXPECT_EQ("a.b", fname.expandWithDefault(""));
+
+    fname.setDirectory("/just/some/dir/");
+    EXPECT_EQ("/just/some/dir/", fname.directory());
+    EXPECT_EQ("/just/some/dir/a.b", fname.fullName());
+    EXPECT_EQ("/just/some/dir/a.b", fname.expandWithDefault(""));
+
+    fname.setDirectory("/just/some/dir");
+    EXPECT_EQ("/just/some/dir/", fname.directory());
+    EXPECT_EQ("/just/some/dir/a.b", fname.fullName());
+    EXPECT_EQ("/just/some/dir/a.b", fname.expandWithDefault(""));
+
+    fname.setDirectory("/");
+    EXPECT_EQ("/", fname.directory());
+    EXPECT_EQ("/a.b", fname.fullName());
+    EXPECT_EQ("/a.b", fname.expandWithDefault(""));
+
+    fname.setDirectory("");
+    EXPECT_EQ("", fname.directory());
+    EXPECT_EQ("a.b", fname.fullName());
+    EXPECT_EQ("a.b", fname.expandWithDefault(""));
+
+    fname = Filename("/first/a.b");
+    EXPECT_EQ("/first/", fname.directory());
+    EXPECT_EQ("/first/a.b", fname.fullName());
+    EXPECT_EQ("/first/a.b", fname.expandWithDefault(""));
+
+    fname.setDirectory("/just/some/dir");
+    EXPECT_EQ("/just/some/dir/", fname.directory());
+    EXPECT_EQ("/just/some/dir/a.b", fname.fullName());
+    EXPECT_EQ("/just/some/dir/a.b", fname.expandWithDefault(""));
+}
diff --git a/src/lib/util/tests/run_unittests.cc b/src/lib/util/tests/run_unittests.cc
index bd3c4c9..a2181cf 100644
--- a/src/lib/util/tests/run_unittests.cc
+++ b/src/lib/util/tests/run_unittests.cc
@@ -13,9 +13,11 @@
 // PERFORMANCE OF THIS SOFTWARE.
 
 #include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
 
 int
 main(int argc, char* argv[]) {
     ::testing::InitGoogleTest(&argc, argv);
-    return (RUN_ALL_TESTS());
+
+    return (isc::util::unittests::run_all());
 }
diff --git a/src/lib/util/tests/strutil_unittest.cc b/src/lib/util/tests/strutil_unittest.cc
index cd3a9ca..74bc17d 100644
--- a/src/lib/util/tests/strutil_unittest.cc
+++ b/src/lib/util/tests/strutil_unittest.cc
@@ -12,6 +12,8 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <stdint.h>
+
 #include <string>
 
 #include <gtest/gtest.h>
@@ -22,17 +24,9 @@ using namespace isc;
 using namespace isc::util;
 using namespace std;
 
-class StringUtilTest : public ::testing::Test {
-protected:
-    StringUtilTest()
-    {
-    }
-};
-
-
 // Check for slash replacement
 
-TEST_F(StringUtilTest, Slash) {
+TEST(StringUtilTest, Slash) {
 
     string instring = "";
     isc::util::str::normalizeSlash(instring);
@@ -49,7 +43,7 @@ TEST_F(StringUtilTest, Slash) {
 
 // Check that leading and trailing space trimming works
 
-TEST_F(StringUtilTest, Trim) {
+TEST(StringUtilTest, Trim) {
 
     // Empty and full string.
     EXPECT_EQ("", isc::util::str::trim(""));
@@ -71,7 +65,7 @@ TEST_F(StringUtilTest, Trim) {
 // returned vector; if not as expected, the following references may be invalid
 // so should not be used.
 
-TEST_F(StringUtilTest, Tokens) {
+TEST(StringUtilTest, Tokens) {
     vector<string>  result;
 
     // Default delimiters
@@ -157,7 +151,7 @@ TEST_F(StringUtilTest, Tokens) {
 
 // Changing case
 
-TEST_F(StringUtilTest, ChangeCase) {
+TEST(StringUtilTest, ChangeCase) {
     string mixed("abcDEFghiJKLmno123[]{=+--+]}");
     string upper("ABCDEFGHIJKLMNO123[]{=+--+]}");
     string lower("abcdefghijklmno123[]{=+--+]}");
@@ -173,7 +167,7 @@ TEST_F(StringUtilTest, ChangeCase) {
 
 // Formatting
 
-TEST_F(StringUtilTest, Formatting) {
+TEST(StringUtilTest, Formatting) {
 
     vector<string> args;
     args.push_back("arg1");
@@ -213,3 +207,63 @@ TEST_F(StringUtilTest, Formatting) {
     string format9 = "%s %s";
     EXPECT_EQ(format9, isc::util::str::format(format9, args));
 }
+
+TEST(StringUtilTest, getToken) {
+    string s("a b c");
+    istringstream ss(s);
+    EXPECT_EQ("a", isc::util::str::getToken(ss));
+    EXPECT_EQ("b", isc::util::str::getToken(ss));
+    EXPECT_EQ("c", isc::util::str::getToken(ss));
+    EXPECT_THROW(isc::util::str::getToken(ss), isc::util::str::StringTokenError);
+}
+
+int32_t tokenToNumCall_32_16(const string& token) {
+    return isc::util::str::tokenToNum<int32_t, 16>(token);
+}
+
+int16_t tokenToNumCall_16_8(const string& token) {
+    return isc::util::str::tokenToNum<int16_t, 8>(token);
+}
+
+TEST(StringUtilTest, tokenToNum) {
+    uint32_t num32 = tokenToNumCall_32_16("0");
+    EXPECT_EQ(0, num32);
+    num32 = tokenToNumCall_32_16("123");
+    EXPECT_EQ(123, num32);
+    num32 = tokenToNumCall_32_16("65535");
+    EXPECT_EQ(65535, num32);
+
+    EXPECT_THROW(tokenToNumCall_32_16(""),
+                 isc::util::str::StringTokenError);
+    EXPECT_THROW(tokenToNumCall_32_16("a"),
+                 isc::util::str::StringTokenError);
+    EXPECT_THROW(tokenToNumCall_32_16("-1"),
+                 isc::util::str::StringTokenError);
+    EXPECT_THROW(tokenToNumCall_32_16("65536"),
+                 isc::util::str::StringTokenError);
+    EXPECT_THROW(tokenToNumCall_32_16("1234567890"),
+                 isc::util::str::StringTokenError);
+    EXPECT_THROW(tokenToNumCall_32_16("-1234567890"),
+                 isc::util::str::StringTokenError);
+
+    uint16_t num16 = tokenToNumCall_16_8("123");
+    EXPECT_EQ(123, num16);
+    num16 = tokenToNumCall_16_8("0");
+    EXPECT_EQ(0, num16);
+    num16 = tokenToNumCall_16_8("255");
+    EXPECT_EQ(255, num16);
+
+    EXPECT_THROW(tokenToNumCall_16_8(""),
+                 isc::util::str::StringTokenError);
+    EXPECT_THROW(tokenToNumCall_16_8("a"),
+                 isc::util::str::StringTokenError);
+    EXPECT_THROW(tokenToNumCall_16_8("-1"),
+                 isc::util::str::StringTokenError);
+    EXPECT_THROW(tokenToNumCall_16_8("256"),
+                 isc::util::str::StringTokenError);
+    EXPECT_THROW(tokenToNumCall_16_8("1234567890"),
+                 isc::util::str::StringTokenError);
+    EXPECT_THROW(tokenToNumCall_16_8("-1234567890"),
+                 isc::util::str::StringTokenError);
+
+}
diff --git a/src/lib/util/time_utilities.cc b/src/lib/util/time_utilities.cc
index d4298e9..4ea37c9 100644
--- a/src/lib/util/time_utilities.cc
+++ b/src/lib/util/time_utilities.cc
@@ -114,12 +114,9 @@ timeToText64(uint64_t value) {
 // library, it's not even declared in a header file.
 namespace detail {
 int64_t (*gettimeFunction)() = NULL;
-}
 
-namespace {
 int64_t
-gettimeofdayWrapper() {
-    using namespace detail;
+gettimeWrapper() {
     if (gettimeFunction != NULL) {
         return (gettimeFunction());
     }
@@ -151,7 +148,7 @@ timeToText32(const uint32_t value) {
     // We first adjust the time to the closest epoch based on the current time.
     // Note that the following variables must be signed in order to handle
     // time until year 2038 correctly.
-    const int64_t start = gettimeofdayWrapper() - 0x7fffffff;
+    const int64_t start = detail::gettimeWrapper() - 0x7fffffff;
     int64_t base = 0;
     int64_t t;
     while ((t = (base + value)) < start) {
diff --git a/src/lib/util/time_utilities.h b/src/lib/util/time_utilities.h
index 0558f16..a53089d 100644
--- a/src/lib/util/time_utilities.h
+++ b/src/lib/util/time_utilities.h
@@ -15,6 +15,8 @@
 #ifndef __TIME_UTILITIES_H
 #define __TIME_UTILITIES_H 1
 
+#include <string>
+
 #include <sys/types.h>
 #include <stdint.h>
 
@@ -39,6 +41,32 @@ public:
         isc::Exception(file, line, what) {}
 };
 
+namespace detail {
+/// Return the current time in seconds
+///
+/// This function returns the "current" time in seconds from epoch
+/// (00:00:00 January 1, 1970) as a 64-bit signed integer.  The return
+/// value can represent a point of time before epoch as a negative number.
+///
+/// This function is provided to help test time conscious implementations
+/// such as DNSSEC and TSIG signatures.  It is difficult to test them with
+/// an unusual or a specifically chosen "current" via system-provided
+/// library functions to get time.  This function acts as a straightforward
+/// wrapper of such a library function, but provides test code with a hook
+/// to return an arbitrary time value: if \c isc::util::detail::gettimeFunction
+/// is set to a pointer of function that returns 64-bit signed integer,
+/// \c gettimeWrapper() calls that function instead of the system library.
+///
+/// This hook variable is specifically intended for testing purposes, so,
+/// even if it's visible outside of this library, it's not even declared in a
+/// header file.
+///
+/// If the implementation doesn't need to be tested with faked current time,
+/// it should simply use the system supplied library function instead of
+/// this one.
+int64_t gettimeWrapper();
+}
+
 ///
 /// \name DNSSEC time conversion functions.
 ///
diff --git a/src/lib/util/unittests/Makefile.am b/src/lib/util/unittests/Makefile.am
index 84d7d21..83235f2 100644
--- a/src/lib/util/unittests/Makefile.am
+++ b/src/lib/util/unittests/Makefile.am
@@ -4,5 +4,19 @@ AM_CXXFLAGS = $(B10_CXXFLAGS)
 lib_LTLIBRARIES = libutil_unittests.la
 libutil_unittests_la_SOURCES = fork.h fork.cc resolver.h
 libutil_unittests_la_SOURCES += newhook.h newhook.cc
+libutil_unittests_la_SOURCES += testdata.h testdata.cc
+if HAVE_GTEST
+libutil_unittests_la_SOURCES += run_all.h run_all.cc
+libutil_unittests_la_SOURCES += textdata.h
+endif
+
+libutil_unittests_la_CPPFLAGS = $(AM_CPPFLAGS)
+if HAVE_GTEST
+libutil_unittests_la_CPPFLAGS += $(GTEST_INCLUDES)
+endif
+
+libutil_unittests_la_LIBADD  = $(top_builddir)/src/lib/util/libutil.la
+libutil_unittests_la_LIBADD += $(top_builddir)/src/lib/util/io/libutil_io.la
+libutil_unittests_la_LIBADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 
 CLEANFILES = *.gcno *.gcda
diff --git a/src/lib/util/unittests/run_all.cc b/src/lib/util/unittests/run_all.cc
new file mode 100644
index 0000000..5f50f77
--- /dev/null
+++ b/src/lib/util/unittests/run_all.cc
@@ -0,0 +1,95 @@
+// 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 <stdlib.h>
+
+#include <iostream>
+#include <iomanip>
+
+#include <gtest/gtest.h>
+#include <exceptions/exceptions.h>
+#include <util/unittests/run_all.h>
+
+namespace isc {
+namespace util {
+namespace unittests {
+
+int
+run_all() {
+    int ret = 0;
+
+    // The catching of exceptions generated in tests is controlled by the
+    // B10TEST_CATCH_EXCEPTION environment variable.  Setting this to
+    // 1 enables the cacthing of exceptions; setting it to 0 disables it.
+    // Anything else causes a message to be printed to stderr and the default
+    // taken.  (The default is to catch exceptions if compiling with clang
+    // and false if not.)
+#ifdef __clang__
+    bool catch_exception = true;
+#else
+    bool catch_exception = false;
+#endif
+
+    const char* b10test_catch_exception = getenv("B10TEST_CATCH_EXCEPTION");
+    if (b10test_catch_exception != NULL) {
+        if (strcmp(b10test_catch_exception, "1") == 0) {
+            catch_exception = true;
+        } else if (strcmp(b10test_catch_exception, "0") == 0) {
+            catch_exception = false;
+        } else {
+            std::cerr << "***ERROR: B10TEST_CATCH_EXCEPTION is '"
+                         << b10test_catch_exception
+                         << "': allowed values are '1' or '0'.\n"
+                      << "          The default value of "
+                         << (catch_exception ?
+                                "1 (exception catching enabled)":
+                                "0 (exception catching disabled)")
+                         << " will be used.\n";
+        }
+    }
+
+    // Actually run the code
+    if (catch_exception) {
+        try {
+            ret = RUN_ALL_TESTS();
+        } catch (const isc::Exception& ex) {
+            // Could output more information with typeid(), but there is no
+            // guarantee that all compilers will support it without an explicit
+            // flag on the command line.
+            std::cerr << "*** Exception derived from isc::exception thrown:\n"
+                      << "    file: " << ex.getFile() << "\n"
+                      << "    line: " << ex.getLine() << "\n"
+                      << "    what: " << ex.what() << std::endl;
+            throw;
+        } catch (const std::exception& ex) {
+            std::cerr << "*** Exception derived from std::exception thrown:\n"
+                      << "    what: " << ex.what() << std::endl;
+            throw;
+        }
+    } else {
+        // This is a separate path for the case where the exception is not
+        // being caught.  Although the other code path re-throws the exception
+        // after catching it, there is no guarantee that the state of the
+        // stack is preserved - a compiler might have unwound the stack to
+        // the point at which the exception is caught.  This would prove
+        // awkward if trying to debug the program using a debugger.
+        ret = RUN_ALL_TESTS();
+    }
+
+    return (ret);
+}
+
+} // namespace unittests
+} // namespace util
+} // namespace isc
diff --git a/src/lib/util/unittests/run_all.h b/src/lib/util/unittests/run_all.h
new file mode 100644
index 0000000..94c7cb0
--- /dev/null
+++ b/src/lib/util/unittests/run_all.h
@@ -0,0 +1,52 @@
+// 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 __RUN_ALL_H
+#define __RUN_ALL_H
+
+// Avoid need for user to include this header file.
+
+#include <gtest/gtest.h>
+
+namespace isc {
+namespace util {
+namespace unittests {
+
+/// \brief Run All Tests
+///
+/// A wrapper for the Google Test RUN_ALL_TESTS() macro, this calls the macro
+/// but wraps the call in a try...catch block if the environment variable
+/// B10TEST_CATCH_EXCEPTION is defined, and calls the macro directly if not.
+///
+/// The catch block catches exceptions of types isc::Exception and
+/// std::exception and prints some information about them to stderr. (In the
+/// case of isc::Exception, this includes the file and line number from which
+/// the exception was raised.)  It then re-throws the exception.
+///
+/// See: https://lists.isc.org/pipermail/bind10-dev/2011-January/001867.html
+/// for some context.
+///
+/// \return Return value from RUN_ALL_TESTS().
+
+int run_all();
+
+} // namespace unittests
+} // namespace util
+} // namespace isc
+
+
+
+#endif // __RUN_ALL_H
diff --git a/src/lib/util/unittests/testdata.cc b/src/lib/util/unittests/testdata.cc
new file mode 100644
index 0000000..2148d31
--- /dev/null
+++ b/src/lib/util/unittests/testdata.cc
@@ -0,0 +1,61 @@
+// 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 <stdexcept>
+#include <fstream>
+#include <vector>
+
+#include "testdata.h"
+
+using namespace std;
+
+namespace {
+vector<string>&
+getDataPaths() {
+    static vector<string> data_path;
+    return (data_path);
+}
+}
+
+namespace isc {
+namespace util {
+namespace unittests {
+
+void
+addTestDataPath(const string& path) {
+    getDataPaths().push_back(path);
+}
+
+void
+openTestData(const char* const datafile, ifstream& ifs) {
+    vector<string>::const_iterator it = getDataPaths().begin();
+    for (; it != getDataPaths().end(); ++it) {
+        string data_path = *it;
+        if (data_path.empty() || *data_path.rbegin() != '/') {
+            data_path.push_back('/');
+        }
+        ifs.open((data_path + datafile).c_str(), ios_base::in);
+        if (!ifs.fail()) {
+            return;
+        }
+    }
+
+    throw runtime_error("failed to open data file in data paths: " +
+                        string(datafile));
+}
+
+}
+}
+}
diff --git a/src/lib/util/unittests/testdata.h b/src/lib/util/unittests/testdata.h
new file mode 100644
index 0000000..03bd83a
--- /dev/null
+++ b/src/lib/util/unittests/testdata.h
@@ -0,0 +1,54 @@
+// 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 __UTIL_UNITTESTS_TESTDATA_H
+#define __UTIL_UNITTESTS_TESTDATA_H 1
+
+/**
+ * @file testdata.h
+ * @short Manipulating test data files.
+ *
+ * This utility defines functions that help test case handle test data
+ * stored in a file.
+ */
+
+namespace isc {
+namespace util {
+namespace unittests {
+/// Add a path (directory) that \c openTestData() will search for test data
+/// files.
+void addTestDataPath(const std::string& path);
+
+/// Open a file specified by 'datafile' using the data paths registered via
+/// addTestDataPath().  On success, ifs will be ready for reading the data
+/// stored in 'datafile'.  If the data file cannot be open with any of the
+/// registered paths, a runtime_error exception will be thrown.
+///
+/// \note Care should be taken if you want to reuse the same single \c ifs
+/// for multiple input data.  Some standard C++ library implementations retain
+/// the failure bit if the first stream reaches the end of the first file,
+/// and make the second call to \c ifstream::open() fail.  The safest way
+/// is to use a different \c ifstream object for a new call to this function;
+/// alternatively make sure you explicitly clear the error bit by calling
+/// \c ifstream::clear() on \c ifs.
+void openTestData(const char* const datafile, std::ifstream& ifs);
+}
+}
+}
+
+#endif // __UTIL_UNITTESTS_TESTDATA_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/util/unittests/textdata.h b/src/lib/util/unittests/textdata.h
new file mode 100644
index 0000000..3e9b1aa
--- /dev/null
+++ b/src/lib/util/unittests/textdata.h
@@ -0,0 +1,103 @@
+// 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 <istream>
+#include <string>
+#include <sstream>
+
+#include <gtest/gtest.h>
+
+#ifndef __UTIL_UNITTESTS_TEXTDATA_H
+#define __UTIL_UNITTESTS_TEXTDATA_H 1
+
+/**
+ * @file textdata.h
+ * @short Utilities for tests with text data.
+ *
+ * This utility provides convenient helper functions for unit tests using
+ * textual data.
+ */
+
+namespace isc {
+namespace util {
+namespace unittests {
+
+/// Line-by-line text comparison.
+///
+/// This templated function takes two standard input streams, extracts
+/// strings from them, and compares the two sets of strings line by line.
+template <typename EXPECTED_STREAM, typename ACTUAL_STREAM>
+void
+matchTextData(EXPECTED_STREAM& expected, ACTUAL_STREAM& actual) {
+    std::string actual_line;
+    std::string expected_line;
+    while (std::getline(actual, actual_line), !actual.eof()) {
+        std::getline(expected, expected_line);
+        if (expected.eof()) {
+            FAIL() << "Redundant line in actual output: " << actual_line;
+            break;
+        }
+        if (actual.bad() || actual.fail() ||
+            expected.bad() || expected.fail()) {
+            throw std::runtime_error("Unexpected error in data streams");
+        }
+        EXPECT_EQ(expected_line, actual_line);
+    }
+    while (std::getline(expected, expected_line), !expected.eof()) {
+        ADD_FAILURE() << "Missing line in actual output: " << expected_line;
+    }
+}
+
+/// Similar to the fully templated version, but takes string for the second
+/// (actual) data.
+///
+/// Due to the nature of textual data, it will often be the case that test
+/// data is given as a string object.  This shortcut version helps such cases
+/// so that the test code doesn't have to create a string stream with the
+/// string data just for testing.
+template <typename EXPECTED_STREAM>
+void
+matchTextData(EXPECTED_STREAM& expected, const std::string& actual_text) {
+    std::istringstream iss(actual_text);
+    matchTextData(expected, iss);
+}
+
+/// Same for the previous version, but the first argument is string.
+template <typename ACTUAL_STREAM>
+void
+matchTextData(const std::string& expected_text, ACTUAL_STREAM& actual) {
+    std::istringstream iss(expected_text);
+    matchTextData(iss, actual);
+}
+
+/// Same for the previous two, but takes strings for both expected and
+/// actual data.
+void
+matchTextData(const std::string& expected_text,
+              const std::string& actual_text)
+{
+    std::istringstream expected_is(expected_text);
+    std::istringstream actual_is(actual_text);
+    matchTextData(expected_is, actual_is);
+}
+
+}
+}
+}
+
+#endif // __UTIL_UNITTESTS_TEXTDATA_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/win32build/VS2010/bind10.sln b/win32build/VS2010/bind10.sln
index cdcb60b..b84eac5 100755
--- a/win32build/VS2010/bind10.sln
+++ b/win32build/VS2010/bind10.sln
@@ -26,7 +26,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BINDInstall", "BINDInstall\
 		{D09B618B-D0E4-468D-A4BD-E204B4344C18} = {D09B618B-D0E4-468D-A4BD-E204B4344C18}
 		{6AC4F7A4-9BDC-415F-81DB-6332CACA38B3} = {6AC4F7A4-9BDC-415F-81DB-6332CACA38B3}
 		{B5D971AD-D95B-4A15-9E31-38AEF4F69627} = {B5D971AD-D95B-4A15-9E31-38AEF4F69627}
-		{805598B5-9E7E-4CB1-952C-C0060FBE630D} = {805598B5-9E7E-4CB1-952C-C0060FBE630D}
 		{0723DEB6-E2C3-4082-91E5-0F8F4B0649F6} = {0723DEB6-E2C3-4082-91E5-0F8F4B0649F6}
 		{13D541BC-8365-42F7-840F-A117CA0413DD} = {13D541BC-8365-42F7-840F-A117CA0413DD}
 		{813BA1C9-8CD8-4B06-B1C0-FDAB576AC4B6} = {813BA1C9-8CD8-4B06-B1C0-FDAB576AC4B6}
@@ -72,6 +71,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libutil_tests", "libutil_te
 	ProjectSection(ProjectDependencies) = postProject
 		{7D04222B-643C-446C-A2B8-93AF74A86246} = {7D04222B-643C-446C-A2B8-93AF74A86246}
 		{8F120666-1A69-4506-8546-0F665E80FFB7} = {8F120666-1A69-4506-8546-0F665E80FFB7}
+		{AC4806D1-C2CC-444B-8F0D-209851A969D2} = {AC4806D1-C2CC-444B-8F0D-209851A969D2}
+		{2844FDFB-A0A1-4FA4-A654-15D69CC717DD} = {2844FDFB-A0A1-4FA4-A654-15D69CC717DD}
 	EndProjectSection
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libutil_unittests", "libutil_unittests\libutil_unittests.vcxproj", "{2844FDFB-A0A1-4FA4-A654-15D69CC717DD}"
@@ -167,6 +168,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libasiodns_tests", "libasio
 		{8F120666-1A69-4506-8546-0F665E80FFB7} = {8F120666-1A69-4506-8546-0F665E80FFB7}
 		{F6E728D3-A0B2-40F6-9B91-7D4474D778F3} = {F6E728D3-A0B2-40F6-9B91-7D4474D778F3}
 		{7EB244E7-D381-4CF4-A2D4-739B81F77588} = {7EB244E7-D381-4CF4-A2D4-739B81F77588}
+		{2844FDFB-A0A1-4FA4-A654-15D69CC717DD} = {2844FDFB-A0A1-4FA4-A654-15D69CC717DD}
 		{AEF3DFFE-B566-4E6A-B299-B59B81022C06} = {AEF3DFFE-B566-4E6A-B299-B59B81022C06}
 	EndProjectSection
 EndProject
@@ -290,12 +292,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libutil_io", "libutil_io\li
 		{8F120666-1A69-4506-8546-0F665E80FFB7} = {8F120666-1A69-4506-8546-0F665E80FFB7}
 	EndProjectSection
 EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libutil_io_tests", "libutil_io_tests\libutil_io_tests.vcxproj", "{805598B5-9E7E-4CB1-952C-C0060FBE630D}"
-	ProjectSection(ProjectDependencies) = postProject
-		{AC4806D1-C2CC-444B-8F0D-209851A969D2} = {AC4806D1-C2CC-444B-8F0D-209851A969D2}
-		{2844FDFB-A0A1-4FA4-A654-15D69CC717DD} = {2844FDFB-A0A1-4FA4-A654-15D69CC717DD}
-	EndProjectSection
-EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libutil_io_python", "libutil_io_python\libutil_io_python.vcxproj", "{564B0ADE-76A4-4833-9610-8DEEA6A15423}"
 	ProjectSection(ProjectDependencies) = postProject
 		{AC4806D1-C2CC-444B-8F0D-209851A969D2} = {AC4806D1-C2CC-444B-8F0D-209851A969D2}
@@ -306,6 +302,13 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libxfr", "libxfr\libxfr.vcx
 		{813BA1C9-8CD8-4B06-B1C0-FDAB576AC4B6} = {813BA1C9-8CD8-4B06-B1C0-FDAB576AC4B6}
 	EndProjectSection
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libdhcp", "libdhcp\libdhcp.vcxproj", "{F27BC0D0-A334-4DC0-9DC9-880D5DA74524}"
+	ProjectSection(ProjectDependencies) = postProject
+		{66C9A5EC-514B-4BDC-AC74-ED4CB465CAAF} = {66C9A5EC-514B-4BDC-AC74-ED4CB465CAAF}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libdhcp_tests", "libdhcp_tests\libdhcp_tests.vcxproj", "{D9178188-26B3-466B-A85A-C3C3344438BE}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Win32 = Debug|Win32
@@ -468,10 +471,6 @@ Global
 		{AC4806D1-C2CC-444B-8F0D-209851A969D2}.Debug|Win32.Build.0 = Debug|Win32
 		{AC4806D1-C2CC-444B-8F0D-209851A969D2}.Release|Win32.ActiveCfg = Release|Win32
 		{AC4806D1-C2CC-444B-8F0D-209851A969D2}.Release|Win32.Build.0 = Release|Win32
-		{805598B5-9E7E-4CB1-952C-C0060FBE630D}.Debug|Win32.ActiveCfg = Debug|Win32
-		{805598B5-9E7E-4CB1-952C-C0060FBE630D}.Debug|Win32.Build.0 = Debug|Win32
-		{805598B5-9E7E-4CB1-952C-C0060FBE630D}.Release|Win32.ActiveCfg = Release|Win32
-		{805598B5-9E7E-4CB1-952C-C0060FBE630D}.Release|Win32.Build.0 = Release|Win32
 		{564B0ADE-76A4-4833-9610-8DEEA6A15423}.Debug|Win32.ActiveCfg = Debug|Win32
 		{564B0ADE-76A4-4833-9610-8DEEA6A15423}.Debug|Win32.Build.0 = Debug|Win32
 		{564B0ADE-76A4-4833-9610-8DEEA6A15423}.Release|Win32.ActiveCfg = Release|Win32
@@ -480,6 +479,14 @@ Global
 		{761E7D88-6CCB-4E41-9F1E-6C1FBBD062F5}.Debug|Win32.Build.0 = Debug|Win32
 		{761E7D88-6CCB-4E41-9F1E-6C1FBBD062F5}.Release|Win32.ActiveCfg = Release|Win32
 		{761E7D88-6CCB-4E41-9F1E-6C1FBBD062F5}.Release|Win32.Build.0 = Release|Win32
+		{F27BC0D0-A334-4DC0-9DC9-880D5DA74524}.Debug|Win32.ActiveCfg = Debug|Win32
+		{F27BC0D0-A334-4DC0-9DC9-880D5DA74524}.Debug|Win32.Build.0 = Debug|Win32
+		{F27BC0D0-A334-4DC0-9DC9-880D5DA74524}.Release|Win32.ActiveCfg = Release|Win32
+		{F27BC0D0-A334-4DC0-9DC9-880D5DA74524}.Release|Win32.Build.0 = Release|Win32
+		{D9178188-26B3-466B-A85A-C3C3344438BE}.Debug|Win32.ActiveCfg = Debug|Win32
+		{D9178188-26B3-466B-A85A-C3C3344438BE}.Debug|Win32.Build.0 = Debug|Win32
+		{D9178188-26B3-466B-A85A-C3C3344438BE}.Release|Win32.ActiveCfg = Release|Win32
+		{D9178188-26B3-466B-A85A-C3C3344438BE}.Release|Win32.Build.0 = Release|Win32
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
diff --git a/win32build/VS2010/libasiolink_tests/libasiolink_tests.vcxproj b/win32build/VS2010/libasiolink_tests/libasiolink_tests.vcxproj
index a6289a7..cde8a72 100755
--- a/win32build/VS2010/libasiolink_tests/libasiolink_tests.vcxproj
+++ b/win32build/VS2010/libasiolink_tests/libasiolink_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">
@@ -60,8 +60,8 @@
       <SubSystem>Console</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <OutputFile>$(OutDir)run_unittests$(TargetExt)</OutputFile>
-      <AdditionalDependencies>libexceptions.lib;libdns++.lib;liblog.lib;libasiolink.lib;gtestd.lib;%(AdditionalDependencies)</AdditionalDependencies>
-      <AdditionalLibraryDirectories>..\libexceptions\Debug;..\libdns++\Debug;..\liblog\Debug;..\libasiolink\Debug;..\..\..\..\gtest\md10\Debug;%BOOST%\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>libexceptions.lib;libutil.lib;libutil_unittests.lib;libdns++.lib;liblog.lib;libasiolink.lib;log4cplusSD.lib;gtestd.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>..\libexceptions\Debug;..\libutil\Debug;..\libutil_unittests\Debug;..\libdns++\Debug;..\liblog\Debug;..\libasiolink\Debug;..\..\..\..\log4cplus\md10\Debug;..\..\..\..\gtest\md10\Debug;%BOOST%\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@@ -81,8 +81,8 @@
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <OptimizeReferences>true</OptimizeReferences>
       <OutputFile>$(OutDir)run_unittests$(TargetExt)</OutputFile>
-      <AdditionalDependencies>libexceptions.lib;libdns++.lib;liblog.lib;libasiolink.lib;gtest.lib;%(AdditionalDependencies)</AdditionalDependencies>
-      <AdditionalLibraryDirectories>..\libexceptions\Release;..\libdns++\Release;..\liblog\Release;..\libasiolink\Release;..\..\..\..\gtest\md10\Release;%BOOST%\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>libexceptions.lib;libutil.lib;libutil_unittests.lib;libdns++.lib;liblog.lib;libasiolink.lib;log4cplusS.lib;gtest.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>..\libexceptions\Release;..\libutil\Release;..\libutil_unittests\Release;..\libdns++\Release;..\liblog\Release;..\libasiolink\Release;..\..\..\..\log4cplus\md10\Release;..\..\..\..\gtest\md10\Release;%BOOST%\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
     </Link>
   </ItemDefinitionGroup>
   <ItemGroup>
diff --git a/win32build/VS2010/libdhcp/libdhcp.vcxproj b/win32build/VS2010/libdhcp/libdhcp.vcxproj
new file mode 100755
index 0000000..dff25f4
--- /dev/null
+++ b/win32build/VS2010/libdhcp/libdhcp.vcxproj
@@ -0,0 +1,98 @@
+<?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\dhcp\dhcp6.h" />
+    <ClInclude Include="..\..\..\src\lib\dhcp\libdhcp.h" />
+    <ClInclude Include="..\..\..\src\lib\dhcp\option.h" />
+    <ClInclude Include="..\..\..\src\lib\dhcp\option6_addrlst.h" />
+    <ClInclude Include="..\..\..\src\lib\dhcp\option6_ia.h" />
+    <ClInclude Include="..\..\..\src\lib\dhcp\option6_iaaddr.h" />
+    <ClInclude Include="..\..\..\src\lib\dhcp\pkt6.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\..\src\lib\dhcp\libdhcp.cc" />
+    <ClCompile Include="..\..\..\src\lib\dhcp\option.cc" />
+    <ClCompile Include="..\..\..\src\lib\dhcp\option6_addrlst.cc" />
+    <ClCompile Include="..\..\..\src\lib\dhcp\option6_ia.cc" />
+    <ClCompile Include="..\..\..\src\lib\dhcp\option6_iaaddr.cc" />
+    <ClCompile Include="..\..\..\src\lib\dhcp\pkt6.cc" />
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{F27BC0D0-A334-4DC0-9DC9-880D5DA74524}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>libdhcp</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;..\..\..\src\lib\dhcp;..\..\..\ext\asio;%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;..\..\..\src\lib\dhcp;..\..\..\ext\asio;%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/libdhcp/libdhcp.vcxproj.filters b/win32build/VS2010/libdhcp/libdhcp.vcxproj.filters
new file mode 100755
index 0000000..7f69742
--- /dev/null
+++ b/win32build/VS2010/libdhcp/libdhcp.vcxproj.filters
@@ -0,0 +1,60 @@
+<?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\dhcp\dhcp6.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\..\src\lib\dhcp\libdhcp.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\..\src\lib\dhcp\option.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\..\src\lib\dhcp\option6_addrlst.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\..\src\lib\dhcp\option6_ia.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\..\src\lib\dhcp\option6_iaaddr.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\..\src\lib\dhcp\pkt6.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\..\src\lib\dhcp\libdhcp.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\src\lib\dhcp\option.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\src\lib\dhcp\option6_addrlst.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\src\lib\dhcp\option6_ia.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\src\lib\dhcp\option6_iaaddr.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\src\lib\dhcp\pkt6.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/win32build/VS2010/libdhcp/libdhcp.vcxproj.user b/win32build/VS2010/libdhcp/libdhcp.vcxproj.user
new file mode 100755
index 0000000..695b5c7
--- /dev/null
+++ b/win32build/VS2010/libdhcp/libdhcp.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/libdhcp_tests/libdhcp_tests.vcxproj b/win32build/VS2010/libdhcp_tests/libdhcp_tests.vcxproj
new file mode 100755
index 0000000..280fa83
--- /dev/null
+++ b/win32build/VS2010/libdhcp_tests/libdhcp_tests.vcxproj
@@ -0,0 +1,100 @@
+<?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>{D9178188-26B3-466B-A85A-C3C3344438BE}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>libdhcp_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>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level4</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>..\..;..\..\..\src\lib;..\..\..\ext\asio;..\..\..\..\gtest\include;%BOOST%;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <CompileAs>CompileAsCpp</CompileAs>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <OutputFile>$(OutDir)run_unittests$(TargetExt)</OutputFile>
+      <AdditionalDependencies>libexceptions.lib;libutil.lib;libutil_unittests.lib;liblog.lib;libasiolink.lib;libdhcp.lib;log4cplusSD.lib;gtestd.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>..\libexceptions\Debug;..\libutil\Debug;..\libutil_unittests\Debug;..\liblog\Debug;..\libasiolink\Debug;..\libdhcp\Debug;..\..\..\..\log4cplus\md10\Debug;..\..\..\..\gtest\md10\Debug;%BOOST%\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+    </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;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>..\..;..\..\..\src\lib;..\..\..\ext\asio;..\..\..\..\gtest\include;%BOOST%;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <CompileAs>CompileAsCpp</CompileAs>
+    </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;..\libasiolink\Release;..\libdhcp\Release;..\..\..\..\log4cplus\md10\Release;..\..\..\..\gtest\md10\Release;%BOOST%\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>libexceptions.lib;libutil.lib;libutil_unittests.lib;liblog.lib;libasiolink.lib;libdhcp.lib;log4cplusS.lib;gtest.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\..\src\lib\dhcp\tests\libdhcp_unittest.cc" />
+    <ClCompile Include="..\..\..\src\lib\dhcp\tests\option6_addrlst_unittest.cc" />
+    <ClCompile Include="..\..\..\src\lib\dhcp\tests\option6_iaaddr_unittest.cc" />
+    <ClCompile Include="..\..\..\src\lib\dhcp\tests\option6_ia_unittest.cc" />
+    <ClCompile Include="..\..\..\src\lib\dhcp\tests\option_unittest.cc" />
+    <ClCompile Include="..\..\..\src\lib\dhcp\tests\pkt6_unittest.cc" />
+    <ClCompile Include="..\..\..\src\lib\dhcp\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/libdhcp_tests/libdhcp_tests.vcxproj.filters b/win32build/VS2010/libdhcp_tests/libdhcp_tests.vcxproj.filters
new file mode 100755
index 0000000..060fbe5
--- /dev/null
+++ b/win32build/VS2010/libdhcp_tests/libdhcp_tests.vcxproj.filters
@@ -0,0 +1,40 @@
+<?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\dhcp\tests\libdhcp_unittest.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\src\lib\dhcp\tests\option_unittest.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\src\lib\dhcp\tests\option6_addrlst_unittest.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\src\lib\dhcp\tests\option6_ia_unittest.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\src\lib\dhcp\tests\option6_iaaddr_unittest.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\src\lib\dhcp\tests\pkt6_unittest.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\src\lib\dhcp\tests\run_unittests.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/win32build/VS2010/libdhcp_tests/libdhcp_tests.vcxproj.user b/win32build/VS2010/libdhcp_tests/libdhcp_tests.vcxproj.user
new file mode 100755
index 0000000..695b5c7
--- /dev/null
+++ b/win32build/VS2010/libdhcp_tests/libdhcp_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/libutil_io_tests/libutil_io_tests.vcxproj b/win32build/VS2010/libutil_io_tests/libutil_io_tests.vcxproj
deleted file mode 100755
index 8e782ca..0000000
--- a/win32build/VS2010/libutil_io_tests/libutil_io_tests.vcxproj
+++ /dev/null
@@ -1,95 +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>{805598B5-9E7E-4CB1-952C-C0060FBE630D}</ProjectGuid>
-    <Keyword>Win32Proj</Keyword>
-    <RootNamespace>libutil_io_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>NotUsing</PrecompiledHeader>
-      <WarningLevel>Level4</WarningLevel>
-      <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <AdditionalIncludeDirectories>..\..;..\..\..\src\lib;..\..\..\..\gtest\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
-      <CompileAs>CompileAsCpp</CompileAs>
-    </ClCompile>
-    <Link>
-      <SubSystem>Console</SubSystem>
-      <GenerateDebugInformation>true</GenerateDebugInformation>
-      <AdditionalDependencies>libutil_io.lib;libutil_unittests.lib;gtestd.lib;%(AdditionalDependencies)</AdditionalDependencies>
-      <OutputFile>$(OutDir)run_unittests$(TargetExt)</OutputFile>
-      <AdditionalLibraryDirectories>..\libutil_io\Debug;..\libutil_unittests\Debug;..\..\..\..\gtest\md10\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
-    </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;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <AdditionalIncludeDirectories>..\..;..\..\..\src\lib;..\..\..\..\gtest\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
-      <CompileAs>CompileAsCpp</CompileAs>
-    </ClCompile>
-    <Link>
-      <SubSystem>Console</SubSystem>
-      <GenerateDebugInformation>true</GenerateDebugInformation>
-      <EnableCOMDATFolding>true</EnableCOMDATFolding>
-      <OptimizeReferences>true</OptimizeReferences>
-      <AdditionalDependencies>libutil_io.lib;libutil_unittests.lib;gtest.lib;%(AdditionalDependencies)</AdditionalDependencies>
-      <OutputFile>$(OutDir)run_unittests$(TargetExt)</OutputFile>
-      <AdditionalLibraryDirectories>..\libutil_io\Release;..\libutil_unittests\Release;..\..\..\..\gtest\md10\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
-    </Link>
-  </ItemDefinitionGroup>
-  <ItemGroup>
-    <ClCompile Include="..\..\..\src\lib\util\io\tests\fd_tests.cc" />
-    <ClCompile Include="..\..\..\src\lib\util\io\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/libutil_io_tests/libutil_io_tests.vcxproj.filters b/win32build/VS2010/libutil_io_tests/libutil_io_tests.vcxproj.filters
deleted file mode 100755
index 2929bb4..0000000
--- a/win32build/VS2010/libutil_io_tests/libutil_io_tests.vcxproj.filters
+++ /dev/null
@@ -1,25 +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>
-    <ClCompile Include="..\..\..\src\lib\util\io\tests\fd_tests.cc">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\..\src\lib\util\io\tests\run_unittests.cc">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-  </ItemGroup>
-</Project>
\ No newline at end of file
diff --git a/win32build/VS2010/libutil_io_tests/libutil_io_tests.vcxproj.user b/win32build/VS2010/libutil_io_tests/libutil_io_tests.vcxproj.user
deleted file mode 100755
index 695b5c7..0000000
--- a/win32build/VS2010/libutil_io_tests/libutil_io_tests.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/libutil_tests/libutil_tests.vcxproj b/win32build/VS2010/libutil_tests/libutil_tests.vcxproj
index 640abfa..9d55164 100755
--- a/win32build/VS2010/libutil_tests/libutil_tests.vcxproj
+++ b/win32build/VS2010/libutil_tests/libutil_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">
@@ -60,8 +60,8 @@
       <SubSystem>Console</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <OutputFile>$(OutDir)run_unittests$(TargetExt)</OutputFile>
-      <AdditionalDependencies>libexceptions.lib;libutil.lib;gtestd.lib;%(AdditionalDependencies)</AdditionalDependencies>
-      <AdditionalLibraryDirectories>..\libexceptions\Debug;..\libutil\Debug;..\..\..\..\gtest\md10\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>libexceptions.lib;libutil.lib;libutil_io.lib;libutil_unittests.lib;gtestd.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>..\libexceptions\Debug;..\libutil\Debug;..\libutil_io\Debug;..\libutil_unittests\Debug;..\..\..\..\gtest\md10\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@@ -81,14 +81,15 @@
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <OptimizeReferences>true</OptimizeReferences>
       <OutputFile>$(OutDir)run_unittests$(TargetExt)</OutputFile>
-      <AdditionalDependencies>libexceptions.lib;libutil.lib;gtest.lib;%(AdditionalDependencies)</AdditionalDependencies>
-      <AdditionalLibraryDirectories>..\libexceptions\Release;..\libutil\Release;..\..\..\..\gtest\md10\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>libexceptions.lib;libutil.lib;libutil_io.lib;libutil_unittests.lib;gtest.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>..\libexceptions\Release;..\libutil\Release;..\libutil_io\Release;..\libutil_unittests\Release;..\..\..\..\gtest\md10\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
     </Link>
   </ItemDefinitionGroup>
   <ItemGroup>
     <ClCompile Include="..\..\..\src\lib\util\tests\base32hex_unittest.cc" />
     <ClCompile Include="..\..\..\src\lib\util\tests\base64_unittest.cc" />
     <ClCompile Include="..\..\..\src\lib\util\tests\buffer_unittest.cc" />
+    <ClCompile Include="..\..\..\src\lib\util\tests\fd_tests.cc" />
     <ClCompile Include="..\..\..\src\lib\util\tests\filename_unittest.cc" />
     <ClCompile Include="..\..\..\src\lib\util\tests\hex_unittest.cc" />
     <ClCompile Include="..\..\..\src\lib\util\tests\io_utilities_unittest.cc" />
diff --git a/win32build/VS2010/libutil_tests/libutil_tests.vcxproj.filters b/win32build/VS2010/libutil_tests/libutil_tests.vcxproj.filters
index 106631b..eab8f72 100755
--- a/win32build/VS2010/libutil_tests/libutil_tests.vcxproj.filters
+++ b/win32build/VS2010/libutil_tests/libutil_tests.vcxproj.filters
@@ -54,5 +54,8 @@
     <ClCompile Include="..\..\..\src\lib\util\tests\time_utilities_unittest.cc">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\..\src\lib\util\tests\fd_tests.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>
\ No newline at end of file
diff --git a/win32build/VS2010/libutil_unittests/libutil_unittests.vcxproj b/win32build/VS2010/libutil_unittests/libutil_unittests.vcxproj
index c6b8254..8f5a811 100755
--- a/win32build/VS2010/libutil_unittests/libutil_unittests.vcxproj
+++ b/win32build/VS2010/libutil_unittests/libutil_unittests.vcxproj
@@ -14,10 +14,15 @@
     <ClInclude Include="..\..\..\src\lib\util\unittests\fork.h" />
     <ClInclude Include="..\..\..\src\lib\util\unittests\newhook.h" />
     <ClInclude Include="..\..\..\src\lib\util\unittests\resolver.h" />
+    <ClInclude Include="..\..\..\src\lib\util\unittests\run_all.h" />
+    <ClInclude Include="..\..\..\src\lib\util\unittests\testdata.h" />
+    <ClInclude Include="..\..\..\src\lib\util\unittests\textdata.h" />
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\..\src\lib\util\unittests\fork.cc" />
     <ClCompile Include="..\..\..\src\lib\util\unittests\newhook.cc" />
+    <ClCompile Include="..\..\..\src\lib\util\unittests\run_all.cc" />
+    <ClCompile Include="..\..\..\src\lib\util\unittests\testdata.cc" />
   </ItemGroup>
   <PropertyGroup Label="Globals">
     <ProjectGuid>{2844FDFB-A0A1-4FA4-A654-15D69CC717DD}</ProjectGuid>
@@ -58,7 +63,7 @@
       <WarningLevel>Level4</WarningLevel>
       <Optimization>Disabled</Optimization>
       <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <AdditionalIncludeDirectories>..\..;..\..\..\src\lib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>..\..;..\..\..\src\lib;..\..\..\..\gtest\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <CompileAs>CompileAsCpp</CompileAs>
     </ClCompile>
     <Link>
@@ -74,7 +79,7 @@
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <AdditionalIncludeDirectories>..\..;..\..\..\src\lib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>..\..;..\..\..\src\lib;..\..\..\..\gtest\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <CompileAs>CompileAsCpp</CompileAs>
     </ClCompile>
     <Link>
diff --git a/win32build/VS2010/libutil_unittests/libutil_unittests.vcxproj.filters b/win32build/VS2010/libutil_unittests/libutil_unittests.vcxproj.filters
index b66f736..d7510f3 100755
--- a/win32build/VS2010/libutil_unittests/libutil_unittests.vcxproj.filters
+++ b/win32build/VS2010/libutil_unittests/libutil_unittests.vcxproj.filters
@@ -24,6 +24,15 @@
     <ClInclude Include="..\..\..\src\lib\util\unittests\fork.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\..\src\lib\util\unittests\run_all.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\..\src\lib\util\unittests\testdata.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\..\src\lib\util\unittests\textdata.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\..\src\lib\util\unittests\newhook.cc">
@@ -32,5 +41,11 @@
     <ClCompile Include="..\..\..\src\lib\util\unittests\fork.cc">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\..\src\lib\util\unittests\run_all.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\src\lib\util\unittests\testdata.cc">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>
\ No newline at end of file




More information about the bind10-changes mailing list