Browse Source

ported message API from the experimental code and added some test cases.

git-svn-id: svn://bind10.isc.org/svn/bind10/branches/f2f200910@141 e5f2f494-b856-4b98-b285-d166d9295462
JINMEI Tatuya 15 years ago
parent
commit
7adb804ba3

+ 135 - 56
Makefile.in

@@ -1,8 +1,9 @@
-# Makefile.in generated by automake 1.10.1 from Makefile.am.
+# Makefile.in generated by automake 1.11 from Makefile.am.
 # @configure_input@
 
 # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006, 2007, 2008  Free Software Foundation, Inc.
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009  Free Software Foundation,
+# Inc.
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
 # with or without modifications, as long as this notice is preserved.
@@ -15,8 +16,9 @@
 @SET_MAKE@
 VPATH = @srcdir@
 pkgdatadir = $(datadir)/@PACKAGE@
-pkglibdir = $(libdir)/@PACKAGE@
 pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
 am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
 install_sh_DATA = $(install_sh) -c -m 644
 install_sh_PROGRAM = $(install_sh) -c
@@ -44,6 +46,7 @@ am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
 mkinstalldirs = $(install_sh) -d
 CONFIG_HEADER = config.h
 CONFIG_CLEAN_FILES = src/bin/bind-cfgd
+CONFIG_CLEAN_VPATH_FILES =
 SOURCES =
 DIST_SOURCES =
 RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
@@ -55,6 +58,9 @@ RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
 	ps-recursive uninstall-recursive
 RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive	\
   distclean-recursive maintainer-clean-recursive
+AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \
+	$(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \
+	distdir dist dist-all distcheck
 ETAGS = etags
 CTAGS = ctags
 DIST_SUBDIRS = $(SUBDIRS)
@@ -62,9 +68,34 @@ DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 distdir = $(PACKAGE)-$(VERSION)
 top_distdir = $(distdir)
 am__remove_distdir = \
-  { test ! -d $(distdir) \
-    || { find $(distdir) -type d ! -perm -200 -exec chmod u+w {} ';' \
-         && rm -fr $(distdir); }; }
+  { test ! -d "$(distdir)" \
+    || { find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \
+         && rm -fr "$(distdir)"; }; }
+am__relativize = \
+  dir0=`pwd`; \
+  sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+  sed_rest='s,^[^/]*/*,,'; \
+  sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+  sed_butlast='s,/*[^/]*$$,,'; \
+  while test -n "$$dir1"; do \
+    first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+    if test "$$first" != "."; then \
+      if test "$$first" = ".."; then \
+        dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+        dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+      else \
+        first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+        if test "$$first2" = "$$first"; then \
+          dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+        else \
+          dir2="../$$dir2"; \
+        fi; \
+        dir0="$$dir0"/"$$first"; \
+      fi; \
+    fi; \
+    dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+  done; \
+  reldir="$$dir2"
 DIST_ARCHIVES = $(distdir).tar.gz
 GZIP_ENV = --best
 distuninstallcheck_listfiles = find . -type f -print
@@ -112,6 +143,7 @@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
 PACKAGE_NAME = @PACKAGE_NAME@
 PACKAGE_STRING = @PACKAGE_STRING@
 PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
 PACKAGE_VERSION = @PACKAGE_VERSION@
 PATH_SEPARATOR = @PATH_SEPARATOR@
 RANLIB = @RANLIB@
@@ -159,6 +191,7 @@ sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
 sysconfdir = @sysconfdir@
 target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
 top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
 SUBDIRS = src
@@ -172,15 +205,15 @@ $(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
 	@for dep in $?; do \
 	  case '$(am__configure_deps)' in \
 	    *$$dep*) \
-	      echo ' cd $(srcdir) && $(AUTOMAKE) --gnu '; \
-	      cd $(srcdir) && $(AUTOMAKE) --gnu  \
+	      echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \
+	      $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \
 		&& exit 0; \
 	      exit 1;; \
 	  esac; \
 	done; \
-	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu  Makefile'; \
-	cd $(top_srcdir) && \
-	  $(AUTOMAKE) --gnu  Makefile
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --foreign Makefile
 .PRECIOUS: Makefile
 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
 	@case '$?' in \
@@ -196,9 +229,10 @@ $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENC
 	$(SHELL) ./config.status --recheck
 
 $(top_srcdir)/configure:  $(am__configure_deps)
-	cd $(srcdir) && $(AUTOCONF)
+	$(am__cd) $(srcdir) && $(AUTOCONF)
 $(ACLOCAL_M4):  $(am__aclocal_m4_deps)
-	cd $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS)
+	$(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS)
+$(am__aclocal_m4_deps):
 
 config.h: stamp-h1
 	@if test ! -f $@; then \
@@ -210,7 +244,7 @@ stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status
 	@rm -f stamp-h1
 	cd $(top_builddir) && $(SHELL) ./config.status config.h
 $(srcdir)/config.h.in:  $(am__configure_deps) 
-	cd $(top_srcdir) && $(AUTOHEADER)
+	($(am__cd) $(top_srcdir) && $(AUTOHEADER))
 	rm -f stamp-h1
 	touch $@
 
@@ -243,7 +277,7 @@ $(RECURSIVE_TARGETS):
 	  else \
 	    local_target="$$target"; \
 	  fi; \
-	  (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+	  ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
 	  || eval $$failcom; \
 	done; \
 	if test "$$dot_seen" = "no"; then \
@@ -277,16 +311,16 @@ $(RECURSIVE_CLEAN_TARGETS):
 	  else \
 	    local_target="$$target"; \
 	  fi; \
-	  (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+	  ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
 	  || eval $$failcom; \
 	done && test -z "$$fail"
 tags-recursive:
 	list='$(SUBDIRS)'; for subdir in $$list; do \
-	  test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
+	  test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
 	done
 ctags-recursive:
 	list='$(SUBDIRS)'; for subdir in $$list; do \
-	  test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
+	  test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
 	done
 
 ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
@@ -294,14 +328,14 @@ ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
 	unique=`for i in $$list; do \
 	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
 	  done | \
-	  $(AWK) '{ files[$$0] = 1; nonemtpy = 1; } \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
 	      END { if (nonempty) { for (i in files) print i; }; }'`; \
 	mkid -fID $$unique
 tags: TAGS
 
 TAGS: tags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
 		$(TAGS_FILES) $(LISP)
-	tags=; \
+	set x; \
 	here=`pwd`; \
 	if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
 	  include_option=--etags-include; \
@@ -313,7 +347,7 @@ TAGS: tags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
 	list='$(SUBDIRS)'; for subdir in $$list; do \
 	  if test "$$subdir" = .; then :; else \
 	    test ! -f $$subdir/TAGS || \
-	      tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \
+	      set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
 	  fi; \
 	done; \
 	list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
@@ -322,36 +356,41 @@ TAGS: tags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
 	  done | \
 	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
 	      END { if (nonempty) { for (i in files) print i; }; }'`; \
-	if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
 	  test -n "$$unique" || unique=$$empty_fix; \
-	  $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
-	    $$tags $$unique; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
 	fi
 ctags: CTAGS
 CTAGS: ctags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
 		$(TAGS_FILES) $(LISP)
-	tags=; \
 	list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
 	unique=`for i in $$list; do \
 	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
 	  done | \
 	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
 	      END { if (nonempty) { for (i in files) print i; }; }'`; \
-	test -z "$(CTAGS_ARGS)$$tags$$unique" \
+	test -z "$(CTAGS_ARGS)$$unique" \
 	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
-	     $$tags $$unique
+	     $$unique
 
 GTAGS:
 	here=`$(am__cd) $(top_builddir) && pwd` \
-	  && cd $(top_srcdir) \
-	  && gtags -i $(GTAGS_ARGS) $$here
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
 
 distclean-tags:
 	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
 
 distdir: $(DISTFILES)
 	$(am__remove_distdir)
-	test -d $(distdir) || mkdir $(distdir)
+	test -d "$(distdir)" || mkdir "$(distdir)"
 	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
 	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
 	list='$(DISTFILES)'; \
@@ -367,38 +406,54 @@ distdir: $(DISTFILES)
 	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
 	  if test -d $$d/$$file; then \
 	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
 	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
-	      cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
 	    fi; \
-	    cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
 	  else \
-	    test -f $(distdir)/$$file \
-	    || cp -p $$d/$$file $(distdir)/$$file \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
 	    || exit 1; \
 	  fi; \
 	done
-	list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+	@list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
 	  if test "$$subdir" = .; then :; else \
 	    test -d "$(distdir)/$$subdir" \
 	    || $(MKDIR_P) "$(distdir)/$$subdir" \
 	    || exit 1; \
-	    distdir=`$(am__cd) $(distdir) && pwd`; \
-	    top_distdir=`$(am__cd) $(top_distdir) && pwd`; \
-	    (cd $$subdir && \
+	  fi; \
+	done
+	@list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+	    $(am__relativize); \
+	    new_distdir=$$reldir; \
+	    dir1=$$subdir; dir2="$(top_distdir)"; \
+	    $(am__relativize); \
+	    new_top_distdir=$$reldir; \
+	    echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+	    echo "     am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+	    ($(am__cd) $$subdir && \
 	      $(MAKE) $(AM_MAKEFLAGS) \
-	        top_distdir="$$top_distdir" \
-	        distdir="$$distdir/$$subdir" \
+	        top_distdir="$$new_top_distdir" \
+	        distdir="$$new_distdir" \
 		am__remove_distdir=: \
 		am__skip_length_check=: \
+		am__skip_mode_fix=: \
 	        distdir) \
 	      || exit 1; \
 	  fi; \
 	done
-	-find $(distdir) -type d ! -perm -777 -exec chmod a+rwx {} \; -o \
+	-test -n "$(am__skip_mode_fix)" \
+	|| find "$(distdir)" -type d ! -perm -777 -exec chmod a+rwx {} \; -o \
 	  ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
 	  ! -type d ! -perm -400 -exec chmod a+r {} \; -o \
 	  ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \
-	|| chmod -R a+r $(distdir)
+	|| chmod -R a+r "$(distdir)"
 dist-gzip: distdir
 	tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
 	$(am__remove_distdir)
@@ -411,6 +466,10 @@ dist-lzma: distdir
 	tardir=$(distdir) && $(am__tar) | lzma -9 -c >$(distdir).tar.lzma
 	$(am__remove_distdir)
 
+dist-xz: distdir
+	tardir=$(distdir) && $(am__tar) | xz -c >$(distdir).tar.xz
+	$(am__remove_distdir)
+
 dist-tarZ: distdir
 	tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z
 	$(am__remove_distdir)
@@ -439,6 +498,8 @@ distcheck: dist
 	  bunzip2 -c $(distdir).tar.bz2 | $(am__untar) ;;\
 	*.tar.lzma*) \
 	  unlzma -c $(distdir).tar.lzma | $(am__untar) ;;\
+	*.tar.xz*) \
+	  xz -dc $(distdir).tar.xz | $(am__untar) ;;\
 	*.tar.Z*) \
 	  uncompress -c $(distdir).tar.Z | $(am__untar) ;;\
 	*.shar.gz*) \
@@ -450,9 +511,11 @@ distcheck: dist
 	mkdir $(distdir)/_build
 	mkdir $(distdir)/_inst
 	chmod a-w $(distdir)
+	test -d $(distdir)/_build || exit 0; \
 	dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \
 	  && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \
-	  && cd $(distdir)/_build \
+	  && am__cwd=`pwd` \
+	  && $(am__cd) $(distdir)/_build \
 	  && ../configure --srcdir=.. --prefix="$$dc_install_base" \
 	    $(DISTCHECK_CONFIGURE_FLAGS) \
 	  && $(MAKE) $(AM_MAKEFLAGS) \
@@ -474,13 +537,15 @@ distcheck: dist
 	  && rm -rf "$$dc_destdir" \
 	  && $(MAKE) $(AM_MAKEFLAGS) dist \
 	  && rm -rf $(DIST_ARCHIVES) \
-	  && $(MAKE) $(AM_MAKEFLAGS) distcleancheck
+	  && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \
+	  && cd "$$am__cwd" \
+	  || exit 1
 	$(am__remove_distdir)
 	@(echo "$(distdir) archives ready for distribution: "; \
 	  list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \
 	  sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x'
 distuninstallcheck:
-	@cd $(distuninstallcheck_dir) \
+	@$(am__cd) '$(distuninstallcheck_dir)' \
 	&& test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \
 	   || { echo "ERROR: files left after uninstall:" ; \
 	        if test -n "$(DESTDIR)"; then \
@@ -522,6 +587,7 @@ clean-generic:
 
 distclean-generic:
 	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
 
 maintainer-clean-generic:
 	@echo "This command is intended for maintainers to use"
@@ -541,6 +607,8 @@ dvi-am:
 
 html: html-recursive
 
+html-am:
+
 info: info-recursive
 
 info-am:
@@ -549,18 +617,28 @@ install-data-am:
 
 install-dvi: install-dvi-recursive
 
+install-dvi-am:
+
 install-exec-am:
 
 install-html: install-html-recursive
 
+install-html-am:
+
 install-info: install-info-recursive
 
+install-info-am:
+
 install-man:
 
 install-pdf: install-pdf-recursive
 
+install-pdf-am:
+
 install-ps: install-ps-recursive
 
+install-ps-am:
+
 installcheck-am:
 
 maintainer-clean: maintainer-clean-recursive
@@ -583,24 +661,25 @@ ps-am:
 
 uninstall-am:
 
-.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) install-am \
-	install-strip
+.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) all \
+	ctags-recursive install-am install-strip tags-recursive
 
 .PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \
 	all all-am am--refresh check check-am clean clean-generic \
 	ctags ctags-recursive dist dist-all dist-bzip2 dist-gzip \
-	dist-lzma dist-shar dist-tarZ dist-zip distcheck distclean \
-	distclean-generic distclean-hdr distclean-tags distcleancheck \
-	distdir distuninstallcheck dvi dvi-am html html-am info \
-	info-am install install-am install-data install-data-am \
-	install-dvi install-dvi-am install-exec install-exec-am \
-	install-html install-html-am install-info install-info-am \
-	install-man install-pdf install-pdf-am install-ps \
-	install-ps-am install-strip installcheck installcheck-am \
-	installdirs installdirs-am maintainer-clean \
+	dist-lzma dist-shar dist-tarZ dist-xz dist-zip distcheck \
+	distclean distclean-generic distclean-hdr distclean-tags \
+	distcleancheck distdir distuninstallcheck dvi dvi-am html \
+	html-am info info-am install install-am install-data \
+	install-data-am install-dvi install-dvi-am install-exec \
+	install-exec-am install-html install-html-am install-info \
+	install-info-am install-man install-pdf install-pdf-am \
+	install-ps install-ps-am install-strip installcheck \
+	installcheck-am installdirs installdirs-am maintainer-clean \
 	maintainer-clean-generic mostlyclean mostlyclean-generic pdf \
 	pdf-am ps ps-am tags tags-recursive uninstall uninstall-am
 
+
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
 .NOEXPORT:

+ 3 - 1
src/lib/dns/Makefile.am

@@ -1,12 +1,14 @@
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib
 
 lib_LIBRARIES = libdns.a
-libdns_a_SOURCES = name.cc buffer.cc rrset.cc name.h buffer.h rrset.h
+libdns_a_SOURCES = name.cc buffer.cc rrset.cc message.cc \
+	name.h buffer.h rrset.h message.h
 
 TESTS =
 if HAVE_GTEST
 TESTS += run_unittests
 run_unittests_SOURCES = run_unittests.cc name_unittest.cc rrset_unittest.cc
+run_unittests_SOURCES += message_unittest.cc
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS = $(GTEST_LDFLAGS)
 run_unittests_LDADD = ./libdns.a $(GTEST_LDADD)

+ 1 - 2
src/lib/dns/buffer.cc

@@ -19,7 +19,7 @@
 
 #include <dns/buffer.h>
 
-using namespace ISC;
+using ISC::SingleBuffer;
 
 // The interface should be revisited.
 int
@@ -48,4 +48,3 @@ SingleBuffer::recv_from(int s, struct sockaddr* from, socklen_t* from_len)
 
     return (cc);
 }
-

+ 3 - 2
src/lib/dns/exceptions.h

@@ -37,14 +37,15 @@ class DNSBadEscape : public DNSException {};
 class DNSBadLabelType : public DNSException {};
 class DNSInvalidRRClass : public DNSException {};
 class DNSInvalidRRType : public DNSException {};
-class DNSRdtypeMismatch : public DNSException {};
+class DNSRRtypeMismatch : public DNSException {};
 class DNSInvalidWireRdata : public DNSException {};
-class DNSNoMessageIOBuffer : public DNSException {};
+class DNSNoMessageBuffer : public DNSException {};
 class DNSNoNameCompressor : public DNSException {};
 class DNSNoNameDecompressor : public DNSException {};
 class DNSNoMessageParser : public DNSException {};
 class DNSInvalidMessageSection : public DNSException {};
 class DNSInvalidRendererPosition : public DNSException {};
+class DNSMessageTooShort : public DNSException {};
 }
 }
 #endif  // __EXCEPTIONS_HH

+ 328 - 0
src/lib/dns/message.cc

@@ -0,0 +1,328 @@
+// Copyright (C) 2009  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <stdexcept>
+#include <functional>
+#include <algorithm>
+
+#include <boost/lexical_cast.hpp>
+
+#include <dns/buffer.h>
+#include <dns/rrset.h>
+#include <dns/message.h>
+
+using ISC::DNS::Name;
+using ISC::DNS::Message;
+using ISC::DNS::RRType;
+using ISC::DNS::RRClass;
+using ISC::DNS::RRsetPtr;
+using ISC::DNS::RR;
+using ISC::DNS::TTL;
+
+Message::Message()
+{
+    initialize();
+
+    default_buffer_ = new ISC::SingleBuffer;
+    buffer_ = default_buffer_;
+
+    default_compressor_ = new NameCompressor;
+    compressor_ = default_compressor_;
+
+    default_decompressor_ = new NameDecompressor;
+    decompressor_ = default_decompressor_;
+}
+
+Message::~Message()
+{
+    delete default_buffer_;
+    delete default_compressor_;
+    delete default_decompressor_;
+}
+
+void
+Message::initialize()
+{
+    qid_ = 0;
+    rcode_ = RCODE_NOERROR;
+    opcode_ = OPCODE_QUERY;     // default
+    flags_ = 0;
+    for (int section = SECTION_QUESTION; section < SECTION_MAX; ++section) {
+        counts_[section] = 0;
+    }
+
+    edns_ = NULL;
+    buffer_ = NULL;
+    compressor_ = NULL;
+    decompressor_ = NULL;
+    sorter_ = NULL;
+    default_compressor_ = NULL;
+    default_decompressor_ = NULL;
+}
+
+void
+Message::add_rrset(section_t section, RRsetPtr rrsetp)
+{
+    if (section >= SECTION_MAX)
+        throw DNSInvalidMessageSection();
+
+    // Note: should check duplicate (TBD)
+    sections_[section].push_back(rrsetp);
+}
+
+void
+Message::add_question(const Name& qname, const RRClass& qclass,
+                      const RRType& qtype)
+{
+    add_rrset(SECTION_QUESTION, RRsetPtr(new Question(qname, qclass, qtype)));
+}
+
+void
+Message::to_wire()
+{
+    uint16_t codes_and_flags;
+
+    if (buffer_ == NULL)
+        throw DNSNoMessageBuffer();
+
+    // reserve room for the header
+    buffer_->reserve(HEADERLEN);
+
+    for (int section = SECTION_QUESTION; section < SECTION_MAX; ++section) {
+        if (sorter_ != NULL)
+            sorter_->sort(*this, (section_t)section); //TBD
+        counts_[section] = 0;
+        for (std::vector<RRsetPtr>::const_iterator it =
+                 get_section((section_t)section).begin();
+             it != get_section((section_t)section).end();
+             ++it)
+        {
+            int counter = (*it)->to_wire(*buffer_, get_compressor(),
+                                         (section_t)section);
+
+            // TBD: if truncation is necessary, do something special.
+            // throw an exception, return an error code (in which case the
+            // function signature should be changed), etc.
+
+            counts_[section] += counter;
+        }
+    }
+
+    // EDNS, TSIG, etc.
+
+    // fill in the header
+    size_t header_pos = 0;
+    buffer_->write_uint16_at(qid_, header_pos);
+    header_pos += sizeof(uint16_t);
+    codes_and_flags = (opcode_ << OPCODE_SHIFT) & OPCODE_MASK;
+    codes_and_flags |= (rcode_ & RCODE_MASK);
+    codes_and_flags |= (flags_ & FLAG_MASK);
+    buffer_->write_uint16_at(codes_and_flags, header_pos);
+    header_pos += sizeof(uint16_t);
+    for (int section = SECTION_QUESTION; section < SECTION_MAX; ++section) {
+        buffer_->write_uint16_at(counts_[section], header_pos);
+        header_pos += sizeof(uint16_t);
+    }
+}
+
+void
+Message::from_wire()
+{
+    if (buffer_ == NULL)
+        throw DNSNoMessageBuffer();
+
+    if (buffer_->get_space() < HEADERLEN)
+        throw DNSMessageTooShort();
+
+    qid_ = buffer_->read_uint16();
+    uint16_t codes_and_flags = buffer_->read_uint16();
+    opcode_ = ((codes_and_flags & OPCODE_MASK) >> OPCODE_SHIFT);
+    rcode_ = (codes_and_flags & RCODE_MASK);
+    flags_ = (codes_and_flags & FLAG_MASK);
+    counts_[SECTION_QUESTION] = buffer_->read_uint16();
+    counts_[SECTION_ANSWER] = buffer_->read_uint16();
+    counts_[SECTION_AUTHORITY] = buffer_->read_uint16();
+    counts_[SECTION_ADDITIONAL] = buffer_->read_uint16();
+
+    parse_question();
+    // parse other sections (TBD)
+}
+
+void
+Message::parse_question()
+{
+    Name name;
+
+    if (buffer_ == NULL)
+        throw DNSNoMessageBuffer();
+
+    for (int count = 0; count < this->counts_[SECTION_QUESTION]; count++) {
+        Name name(*buffer_, get_decompressor());
+
+        // Get type and class
+        if (buffer_->get_space() < 2 * sizeof(uint16_t))
+            throw DNSMessageTooShort();
+
+        // XXX: need a duplicate check.  We might also want to have an optimized
+        // algorithm that requires the question section contain exactly one
+        // RR.
+
+        RRType rrtype(buffer_->read_uint16());
+        RRClass rrclass(buffer_->read_uint16());
+        add_rrset(SECTION_QUESTION,
+                  RRsetPtr(new Question(name, rrclass, rrtype))); 
+    }
+}
+
+static const char *opcodetext[] = {
+    "QUERY",
+    "IQUERY",
+    "STATUS",
+    "RESERVED3",
+    "NOTIFY",
+    "UPDATE",
+    "RESERVED6",
+    "RESERVED7",
+    "RESERVED8",
+    "RESERVED9",
+    "RESERVED10",
+    "RESERVED11",
+    "RESERVED12",
+    "RESERVED13",
+    "RESERVED14",
+    "RESERVED15"
+};
+
+static const char *rcodetext[] = {
+    "NOERROR",
+    "FORMERR",
+    "SERVFAIL",
+    "NXDOMAIN",
+    "NOTIMP",
+    "REFUSED",
+    "YXDOMAIN",
+    "YXRRSET",
+    "NXRRSET",
+    "NOTAUTH",
+    "NOTZONE",
+    "RESERVED11",
+    "RESERVED12",
+    "RESERVED13",
+    "RESERVED14",
+    "RESERVED15"
+};
+
+static const char *sectiontext[] = {
+    "QUESTION",
+    "ANSWER",
+    "AUTHORITY",
+    "ADDITIONAL"
+};
+
+std::string
+Message::to_text() const
+{
+    std::string s;
+
+    s += ";; ->>HEADER<<- opcode: " + std::string(opcodetext[opcode_]);
+    // for simplicity we don't consider extended rcode (unlike BIND9)
+    s += ", status: " + std::string(rcodetext[rcode_]);
+    s += ", id: " + boost::lexical_cast<std::string>(qid_);
+    s += "\n;; flags: ";
+    if (get_qr())
+        s += "qr ";
+    if (get_aa())
+        s += "aa ";
+    if (get_tc())
+        s += "tc ";
+    if (get_rd())
+        s += "rd ";
+    if (get_ra())
+        s += "ra ";
+    if (get_ad())
+        s += "ad ";
+    if (get_cd())
+        s += "cd ";
+
+    // for simply, don't consider the update case
+    s += "; QUESTION: " +
+        boost::lexical_cast<std::string>(counts_[SECTION_QUESTION]);
+    s += ", ANSWER: " +
+        boost::lexical_cast<std::string>(counts_[SECTION_ANSWER]);
+    s += ", AUTHORITY: " +
+        boost::lexical_cast<std::string>(counts_[SECTION_AUTHORITY]);
+    s += ", ADDITIONAL: " +
+        boost::lexical_cast<std::string>(counts_[SECTION_ADDITIONAL]) + "\n";
+
+    for (int section = SECTION_QUESTION; section < SECTION_MAX; ++section) {
+        if (sections_[section].empty())
+            continue;
+
+        s += "\n;; " + std::string(sectiontext[section]) + " SECTION:\n";
+
+        std::vector<RRsetPtr>::const_iterator it;
+        for (it = sections_[section].begin();
+             it !=  sections_[section].end();
+             ++it)
+        {
+            if (section == SECTION_QUESTION)
+                s += ";";
+            s += (**it).to_text() + "\n";
+        }
+    }
+
+    return (s);
+}
+
+struct MatchRR : public std::binary_function<RRsetPtr, RR, bool> {
+    bool operator()(const RRsetPtr& rrset, const RR& rr) const
+    {
+        return (rrset->get_type() == rr.get_type() &&
+                rrset->get_class() == rr.get_class() &&
+                rrset->get_name() == rr.get_name());
+    }
+};
+
+void
+Message::add_rr(section_t section, const RR& rr)
+{
+    std::vector<RRsetPtr>::iterator it;
+    it = find_if(sections_[section].begin(), sections_[section].end(),
+                 std::bind2nd(MatchRR(), rr));
+    if (it != sections_[section].end()) {
+        (*it)->set_ttl(std::min((*it)->get_ttl(), rr.get_ttl()));
+        (*it)->add_rdata(Rdata::RDATAPTR(rr.get_rdata()->copy()));
+    } else {
+        RRset *rrset = new RRset(rr.get_name(), rr.get_class(), rr.get_type(),
+                                 rr.get_ttl());
+        rrset->add_rdata(Rdata::RDATAPTR(rr.get_rdata()->copy()));
+        sections_[section].push_back(RRsetPtr(rrset));
+    }
+}
+
+void
+Message::make_response()
+{
+    flags_ &= MESSAGE_REPLYPRESERVE;
+    set_qr(true);
+
+    for (int section = SECTION_ANSWER; section < SECTION_MAX; ++section) {
+        sections_[section].clear();
+    }
+}

+ 215 - 0
src/lib/dns/message.h

@@ -0,0 +1,215 @@
+// Copyright (C) 2009  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __MESSAGE_H
+#define __MESSAGE_H 1
+
+#include <sys/socket.h>
+
+#include <vector>
+#include <string>
+
+#include <boost/shared_ptr.hpp>
+
+#include <dns/exceptions.h>
+#include <dns/buffer.h>
+#include <dns/name.h>
+#include <dns/rrset.h>
+
+namespace ISC {
+namespace DNS {
+typedef uint8_t rcode_t; // we actually only need 4 bits of it
+typedef uint8_t opcode_t; // we actually only need 4 bits of it
+typedef uint16_t flags_t;
+typedef uint16_t qid_t;
+
+class Message;
+class EDNS;             // see below
+
+typedef boost::shared_ptr<AbstractRRset> RRsetPtr;
+
+class RRsetsSorter {
+public:
+    void sort(Message& message, section_t section) {} // dummy for now.
+};
+
+#define get_msgflg(flg, capflg) \
+bool get_ ## flg() const { return ((flags_ & FLAG_ ## capflg) != 0); }
+#define set_msgflg(flg, capflg) \
+bool set_ ## flg(bool on) { \
+        if (on) \
+            flags_ |= FLAG_ ## capflg; \
+        else \
+            flags_ &= ~FLAG_ ## capflg; \
+    }
+
+class Message {
+public:
+    Message();
+    ~Message();
+    qid_t get_qid() const { return (qid_); }
+    void set_qid(qid_t qid) { qid_ = qid; }
+    get_msgflg(rd, RD)
+    set_msgflg(rd, RD)
+    get_msgflg(aa, AA)
+    set_msgflg(aa, AA)
+    get_msgflg(qr, QR)
+    set_msgflg(qr, QR)
+    get_msgflg(tc, TC)
+    set_msgflg(tc, TC)
+    get_msgflg(ra, RA)
+    set_msgflg(ra, RA)
+    get_msgflg(ad, AD)
+    set_msgflg(ad, AD)
+    get_msgflg(cd, CD)
+    set_msgflg(cd, CD)
+    rcode_t get_rcode() const { return (rcode_); }
+    void set_rcode(rcode_t rcode) { rcode_ = rcode; }
+    opcode_t get_opcode() const { return (opcode_); }
+    void set_opcode(opcode_t opcode) { opcode_ = opcode; }
+    std::string to_text() const;
+
+    // we don't provide accessors to QD/AN/NS/AR counters as this information
+    // is included in the corresponding RRsets.
+    // Open issues:
+    //   - should we handle the question section separately?
+    //   - may want to provide an "iterator" for all RRsets/RRs
+    //   - may want to provide a "find" method for a specified type
+    //     of RR in the message
+    const std::vector<RRsetPtr>& get_section(section_t section) const
+    { return (sections_[section]); }
+    void add_rrset(section_t section, RRsetPtr rrset);
+    // add_question() is redundant in that it's a special case of add_rrset,
+    // but it'd be convenient for general purpose applications.
+    void add_question(const Name& qname, const RRClass& qclass,
+                      const RRType& qtype);
+    void remove_rrset(section_t section, RRsetPtr rrset);
+    void add_rr(section_t section, const RR& rr);
+    void remove_rr(section_t section, const RR& rr);
+
+    // should we separate methods for different EDNS0-related
+    // parameters/options?  it would probably be better to have a
+    // separate class for future extensibility.
+    // on the other hand, considering we're going to use EDNS0 pretty
+    // ubiquitously, perhaps we should include currently-defined EDNS0
+    // parameters and options by default.
+    void set_edns(EDNS* edns);    // TBD
+    const EDNS* get_edns() const; // TBD
+
+    // prepare for making a response from a request.  This will clear the
+    // DNS header except those fields that should be kept for the response,
+    // and clear answer and the following sections.
+    // see also dns_message_reply() of BIND9.
+    void make_response();
+
+    // Render message
+    void to_wire();      // should probably return some result code?
+
+    Buffer& get_buffer()
+    {
+        if (buffer_ == NULL)
+            throw DNSNoMessageBuffer();
+        return (*buffer_);
+    }
+
+    NameCompressor& get_compressor()
+    {
+        if (compressor_ == NULL)
+            throw DNSNoNameCompressor();
+        return (*compressor_);
+    }
+
+    NameDecompressor& get_decompressor()
+    {
+        if (decompressor_ == NULL)
+            throw DNSNoNameDecompressor();
+        return (*decompressor_);
+    }
+
+    // Parse message:
+    // This function may throw exceptions.  We may revisit this design.
+    void from_wire();
+
+public:
+    // public protocol constants
+    static const rcode_t RCODE_NOERROR = 0;
+    static const rcode_t RCODE_FORMERR = 1;
+    static const rcode_t RCODE_SERVFAIL = 2;
+    static const rcode_t RCODE_NXDOMAIN = 3;
+    static const rcode_t RCODE_NOTIMP = 4;
+    static const rcode_t RCODE_REFUSED = 5;
+    // ...more to follow
+
+    static const opcode_t OPCODE_QUERY = 0;
+    static const opcode_t OPCODE_IQUERY = 1;
+    static const opcode_t OPCODE_STATUS = 2;
+    static const opcode_t OPCODE_NOTIFY = 4;
+    static const opcode_t OPCODE_UPDATE = 5;
+
+private:
+    void initialize();
+    void parse_question();
+
+private:
+    // Open issues: should we rather have a header in wire-format
+    // for efficiency?
+    qid_t qid_;
+    rcode_t rcode_;
+    opcode_t opcode_;
+    flags_t flags_;
+
+    int counts_[SECTION_MAX];   // TODO: revisit this definition
+
+    // TODO: revisit the type of vector items
+    std::vector<RRsetPtr> sections_[SECTION_MAX]; 
+
+    // tsig/sig0: TODO
+    EDNS* edns_;
+    Buffer* buffer_;
+    NameCompressor* compressor_;
+    NameDecompressor* decompressor_;
+    RRsetsSorter* sorter_;
+
+    // created internally with the default constructor.  TBD: revisit this idea.
+    Buffer* default_buffer_;
+    NameCompressor* default_compressor_;
+    NameDecompressor* default_decompressor_;
+
+    // protocol constants
+    static const size_t HEADERLEN = 12;
+
+    static const flags_t FLAG_QR = 0x8000;
+    static const flags_t FLAG_AA = 0x0400;
+    static const flags_t FLAG_TC = 0x0200;
+    static const flags_t FLAG_RD = 0x0100;
+    static const flags_t FLAG_RA = 0x0080;
+    static const flags_t FLAG_AD = 0x0020;
+    static const flags_t FLAG_CD = 0x0010;
+
+    static const unsigned int OPCODE_MASK = 0x7800;
+    static const unsigned int OPCODE_SHIFT = 11;
+    static const unsigned int RCODE_MASK = 0x000f;
+    static const unsigned int FLAG_MASK = 0x8ff0;
+
+    static const unsigned int MESSAGE_REPLYPRESERVE = (FLAG_RD | FLAG_CD);
+};
+}
+}
+#endif  // __MESSAGE_H
+
+// Local Variables: 
+// mode: c++
+// End: 

+ 124 - 0
src/lib/dns/message_unittest.cc

@@ -0,0 +1,124 @@
+// Copyright (C) 2009  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <dns/buffer.h>
+#include <dns/rrset.h>
+#include <dns/message.h>
+
+#include <gtest/gtest.h>
+
+namespace {
+
+using ISC::DNS::Name;
+using ISC::DNS::RRClass;
+using ISC::DNS::RRType;
+using ISC::DNS::TTL;
+using ISC::DNS::Rdata::IN::A;
+using ISC::DNS::RRset;
+using ISC::DNS::RRsetPtr;
+using ISC::DNS::RR;
+using ISC::DNS::Message;
+using ISC::DNS::SECTION_QUESTION;
+using ISC::DNS::SECTION_ANSWER;
+using ISC::DNS::SECTION_AUTHORITY;
+using ISC::DNS::SECTION_ADDITIONAL;
+
+// The fixture for testing class RRClass.
+class MessageTest : public ::testing::Test {
+protected:
+    MessageTest()
+    {
+        query.add_question(Name("www.example.com"), RRClass::IN, RRType::A);
+    }
+    Message query;
+    Message response;
+};
+
+TEST_F(MessageTest, check_flags)
+{
+    EXPECT_EQ(false, query.get_qr());
+    query.set_qr(true);
+    EXPECT_EQ(true, query.get_qr());
+
+    EXPECT_EQ(false, query.get_aa());
+    query.set_aa(true);
+    EXPECT_EQ(true, query.get_aa());
+
+    EXPECT_EQ(false, query.get_tc());
+    query.set_tc(true);
+    EXPECT_EQ(true, query.get_tc());
+
+    EXPECT_EQ(false, query.get_rd());
+    query.set_rd(true);
+    EXPECT_EQ(true, query.get_rd());
+
+    EXPECT_EQ(false, query.get_ra());
+    query.set_ra(true);
+    EXPECT_EQ(true, query.get_ra());
+
+    EXPECT_EQ(false, query.get_ad());
+    query.set_ad(true);
+    EXPECT_EQ(true, query.get_ad());
+
+    EXPECT_EQ(false, query.get_cd());
+    query.set_cd(true);
+    EXPECT_EQ(true, query.get_cd());
+}
+
+TEST_F(MessageTest, get_question)
+{
+    const std::vector<RRsetPtr>& question = query.get_section(SECTION_QUESTION);
+    EXPECT_EQ(1, question.size());
+    EXPECT_EQ("www.example.com. IN A", (**question.begin()).to_text());
+}
+
+TEST_F(MessageTest, make_response)
+{
+    query.make_response();
+    EXPECT_EQ(true, query.get_qr());
+    EXPECT_EQ("www.example.com. IN A",
+              (**query.get_section(SECTION_QUESTION).begin()).to_text());
+    EXPECT_EQ(0, query.get_section(SECTION_ANSWER).size());
+    EXPECT_EQ(0, query.get_section(SECTION_AUTHORITY).size());
+    EXPECT_EQ(0, query.get_section(SECTION_ADDITIONAL).size());
+}
+
+TEST_F(MessageTest, add_rr)
+{
+    // Add one RR to the answer section.
+    response.add_rr(SECTION_ANSWER, RR(Name("www.example.com"), RRClass::IN,
+                                       RRType::A, TTL(3600), A("192.0.2.1")));
+    EXPECT_EQ("www.example.com. 3600 IN A 192.0.2.1",
+              (**response.get_section(SECTION_ANSWER).begin()).to_text());
+
+    // Add another RR of the same RRset with a larger TTL.  The original
+    // (smaller) TTL should remain.
+    response.add_rr(SECTION_ANSWER, RR(Name("www.example.com"), RRClass::IN,
+                                       RRType::A, TTL(7200), A("192.0.2.2")));
+    EXPECT_EQ("www.example.com. 3600 IN A 192.0.2.1\n"
+              "www.example.com. 3600 IN A 192.0.2.2",
+              (**response.get_section(SECTION_ANSWER).begin()).to_text());
+
+    // Add one more RR of the same RRset with a smaller TTL.  The new (smaller)
+    // TTL should replace with the old one.
+    response.add_rr(SECTION_ANSWER, RR(Name("www.example.com"), RRClass::IN,
+                                       RRType::A, TTL(1800), A("192.0.2.3")));
+    EXPECT_EQ("www.example.com. 1800 IN A 192.0.2.1\n"
+              "www.example.com. 1800 IN A 192.0.2.2\n"
+              "www.example.com. 1800 IN A 192.0.2.3",
+              (**response.get_section(SECTION_ANSWER).begin()).to_text());
+}
+}

+ 29 - 10
src/lib/dns/rrset.cc

@@ -32,6 +32,7 @@ using ISC::DNS::Rdata::IN::A;
 using ISC::DNS::Rdata::IN::AAAA;
 using ISC::DNS::Rdata::Generic::NS;
 using ISC::DNS::RRset;
+using ISC::DNS::Rdata::Rdata;
 using ISC::DNS::Question;
 using ISC::DNS::RR;
 
@@ -140,6 +141,12 @@ A::to_text() const
     return (std::string(addrbuf));
 }
 
+Rdata*
+A::copy() const
+{
+    return (new A(to_text()));
+}
+
 AAAA::AAAA(const std::string& addrstr)
 {
     if (inet_pton(AF_INET6, addrstr.c_str(), &addr_) != 1)
@@ -170,6 +177,12 @@ AAAA::to_text() const
     return (std::string(addrbuf));
 }
 
+Rdata*
+AAAA::copy() const
+{
+    return (new AAAA(to_text()));
+}
+
 void
 NS::from_wire(Buffer& buffer, NameDecompressor& decompressor)
 {
@@ -191,6 +204,12 @@ NS::to_text() const
     return (nsname_.to_text());
 }
 
+Rdata*
+NS::copy() const
+{
+    return (new NS(to_text()));
+}
+
 std::string
 RRset::to_text() const
 {
@@ -203,8 +222,8 @@ RRset::to_text() const
         if (!s.empty())
             s.push_back('\n');
         s += name_.to_text() + " ";
-        s += ttl_.to_text() + " " + rdclass_.to_text() + " " +
-            rdtype_.to_text() + " " + (**it).to_text();
+        s += ttl_.to_text() + " " + rrclass_.to_text() + " " +
+            rrtype_.to_text() + " " + (**it).to_text();
     }
 
     return (s);
@@ -213,8 +232,8 @@ RRset::to_text() const
 void
 RRset::add_rdata(Rdata::RDATAPTR rdata)
 {
-    if (rdata->get_type() != rdtype_)
-        throw DNSRdtypeMismatch();
+    if (rdata->get_type() != rrtype_)
+        throw DNSRRtypeMismatch();
     rdatalist_.push_back(rdata);
 }
 
@@ -233,8 +252,8 @@ RRset::to_wire(Buffer& buffer, NameCompressor& compressor, section_t section)
          ++it, ++num_rrs)
     {
         name_.to_wire(buffer, compressor);
-        rdtype_.to_wire(buffer);
-        rdclass_.to_wire(buffer);
+        rrtype_.to_wire(buffer);
+        rrclass_.to_wire(buffer);
         ttl_.to_wire(buffer);
         (**it).to_wire(buffer, compressor);
 
@@ -249,16 +268,16 @@ Question::to_text() const
 {
     // return in dig-style format.  note that in the wire format class follows
     // type.
-    return (name_.to_text() + " " + rdclass_.to_text() + " " +
-            rdtype_.to_text());
+    return (name_.to_text() + " " + rrclass_.to_text() + " " +
+            rrtype_.to_text());
 }
 
 int
 Question::to_wire(Buffer& buffer, NameCompressor& compressor, section_t section)
 {
     name_.to_wire(buffer, compressor);
-    rdtype_.to_wire(buffer);
-    rdclass_.to_wire(buffer);
+    rrtype_.to_wire(buffer);
+    rrclass_.to_wire(buffer);
 
     return (1);
 }

+ 56 - 26
src/lib/dns/rrset.h

@@ -54,7 +54,7 @@ public:
     bool operator!=(const RRClass& other) const
     { return (classval_ != other.classval_); }
 
-    // (Some) well-known Rdclass constants
+    // (Some) well-known RRclass constants
     static const RRClass IN;
     static const RRClass CH;
 private:
@@ -74,7 +74,7 @@ public:
     bool operator!=(const RRType& other) const
     { return (typeval_ != other.typeval_); }
 
-    // (Some) Well-known Rdtype constants
+    // (Some) Well-known RRtype constants
     static const RRType A;
     static const RRType NS;
     static const RRType AAAA;
@@ -96,11 +96,14 @@ public:
     { return (ttlval_ == other.ttlval_); }
     bool operator!=(const TTL& other) const
     { return (ttlval_ != other.ttlval_); }
-    // define the following using serial number arithmetic:
-    bool operator<=(const TTL &other) const;
-    bool operator>=(const TTL &other) const;
-    bool operator<(const TTL &other) const;
-    bool operator>(const TTL &other) const;
+    bool operator<=(const TTL &other) const
+    { return (ttlval_ <= other.ttlval_); }
+    bool operator>=(const TTL &other) const
+    { return (ttlval_ >= other.ttlval_); }
+    bool operator<(const TTL &other) const
+    { return (ttlval_ < other.ttlval_); }
+    bool operator>(const TTL &other) const
+    { return (ttlval_ > other.ttlval_); }
 private:
     uint32_t ttlval_;
 };
@@ -126,6 +129,9 @@ public:
     virtual void to_wire(Buffer& b, NameCompressor& c) const = 0;
     // need generic method for getting n-th field? c.f. ldns
     // e.g. string getField(int n);
+
+    // polymorphic copy constructor (XXX should revisit it)
+    virtual Rdata* copy() const = 0;
 };
 
 namespace Generic {
@@ -144,6 +150,7 @@ public:
     bool operator==(const NS &other) const
     { return (nsname_ == other.nsname_); }
     virtual bool operator!=(const NS &other) const { return !(*this == other); }
+    virtual Rdata* copy() const;
 private:
     Name nsname_;
 };
@@ -167,6 +174,7 @@ public:
     { return (addr_.s_addr == other.addr_.s_addr); }
     virtual bool operator!=(const A &other) const
     { return !(*this == other); }
+    virtual Rdata* copy() const;
 private:
     // XXX: should probably define an "address class" and use it.
     struct in_addr addr_;
@@ -188,6 +196,7 @@ public:
     { return (IN6_ARE_ADDR_EQUAL(&addr_, &other.addr_)); }
     virtual bool operator!=(const AAAA &other) const
     { return !(*this == other); }
+    virtual Rdata* copy() const;
 private:
     // XXX: should probably define an "address class" and use it.
     struct in6_addr addr_;
@@ -200,6 +209,12 @@ private:
 // This is a primary class internally used in our major software such as name
 // servers.
 //
+// Note about terminology: there has been a discussion at the IETF namedroppers
+// ML about RRset vs RRSet (case of "s").  While RFC2181 uses the latter,
+// many other RFCs use the former, and most of the list members who showed
+// their opinion seem to prefer RRset.  We follow that preference in this
+// implementation.
+//
 // Open Issues:
 //   - add more set-like operations, e.g, merge?
 //   - add a "sort" method?  "search(find)" method?
@@ -217,54 +232,67 @@ public:
     virtual std::string to_text() const = 0;
     virtual int to_wire(Buffer& buffer, NameCompressor& compressor,
                         section_t section) = 0;
+    virtual void add_rdata(Rdata::RDATAPTR rdata) = 0;
     virtual unsigned int count_rdata() const = 0;
     virtual const Name& get_name() const = 0;
     virtual const RRClass& get_class() const = 0;
     virtual const RRType& get_type() const = 0;
+    virtual const TTL& get_ttl() const = 0;
+    virtual void set_ttl(const TTL& ttl) = 0;
 };
 
 class RRset : public AbstractRRset {
 public:
     RRset() {}
-    explicit RRset(const Name &name, const RRClass &rdclass,
-                   const RRType &rdtype, const TTL &ttl) :
-        name_(name), rdclass_(rdclass), rdtype_(rdtype), ttl_(ttl) {}
+    explicit RRset(const Name &name, const RRClass &rrclass,
+                   const RRType &rrtype, const TTL &ttl) :
+        name_(name), rrclass_(rrclass), rrtype_(rrtype), ttl_(ttl) {}
     unsigned int count_rdata() const { return (rdatalist_.size()); }
     void add_rdata(Rdata::RDATAPTR rdata);
     void remove_rdata(const Rdata::Rdata& rdata);
     std::string to_text() const;
     int to_wire(Buffer& buffer, NameCompressor& compressor, section_t section);
     const Name& get_name() const { return (name_); } 
-    const RRClass& get_class() const { return (rdclass_); }
-    const RRType& get_type() const { return (rdtype_); }
+    const RRClass& get_class() const { return (rrclass_); }
+    const RRType& get_type() const { return (rrtype_); }
     const TTL& get_ttl() const { return (ttl_); }
+    // once constructed, only TTL and rdatalist can be modified.
+    void set_ttl(const TTL& ttl) { ttl_ = ttl; }
+    const std::vector<Rdata::RDATAPTR>& get_rdatalist() const
+    { return (rdatalist_); }
     template <typename T> void get_rdatalist(std::vector<T>&) const;
 private:
     Name name_;
-    RRClass rdclass_;
-    RRType rdtype_;
+    RRClass rrclass_;
+    RRType rrtype_;
     TTL ttl_;
     std::vector<Rdata::RDATAPTR> rdatalist_;
 };
 
 //
 // Generic Question section entry
+// XXX: it seems to be not a good idea to inherit from RRset.  We should
+// probably re-define it as a separate class.
 //
 class Question : public AbstractRRset {
 public:
-    explicit Question(const Name& name, const RRClass& rdclass,
-             const RRType& rdtype) :
-        name_(name), rdclass_(rdclass), rdtype_(rdtype) {}
+    explicit Question(const Name& name, const RRClass& rrclass,
+             const RRType& rrtype) :
+        name_(name), rrclass_(rrclass), rrtype_(rrtype), ttl_(0) {}
     std::string to_text() const;
     int to_wire(Buffer& buffer, NameCompressor& compressor, section_t section);
     unsigned int count_rdata() const { return (0); }
-    const Name& get_name() const { return (name_); } 
-    const RRClass& get_class() const { return (rdclass_); }
-    const RRType& get_type() const { return (rdtype_); }
+    const Name& get_name() const { return (name_); }
+    const RRClass& get_class() const { return (rrclass_); }
+    const RRType& get_type() const { return (rrtype_); }
+    const TTL& get_ttl() const { return (ttl_); } // XXX
+    void set_ttl(const TTL& ttl) {}              // XXX
+    void add_rdata(Rdata::RDATAPTR rdata) {}     // XXX
 private:
     Name name_;
-    RRClass rdclass_;
-    RRType rdtype_;
+    RRClass rrclass_;
+    RRType rrtype_;
+    TTL ttl_;
 };
 
 // TBD: this interface should be revisited.
@@ -276,7 +304,7 @@ RRset::get_rdatalist(std::vector<T>& v) const
     for (it = rdatalist_.begin(); it != rdatalist_.end(); ++it) {
         const T& concreteRdata = static_cast<const T&>(**it); // XXX
         if (T::get_type_static() != (**it).get_type()) {
-            throw DNSRdtypeMismatch();
+            throw DNSRRtypeMismatch();
         }
         v.push_back(concreteRdata);
     }
@@ -291,14 +319,17 @@ class RR {
 public:
     RR() {}
     explicit RR(const std::string& rrstr);
-    explicit RR(const Name& name, const RRClass& rdclass,
-                const RRType& rdtype, const TTL& ttl,
+    explicit RR(const Name& name, const RRClass& rrclass,
+                const RRType& rrtype, const TTL& ttl,
                 const Rdata::Rdata& rdata);
     std::string to_text() const { return (rrset_.to_text()); }
     const Name& get_name() const { return (rrset_.get_name()); }
     const RRClass& get_class() const { return (rrset_.get_class()); }
     const RRType& get_type() const { return (rrset_.get_type()); }
     const TTL& get_ttl() const { return (rrset_.get_ttl()); }
+    const Rdata::RDATAPTR get_rdata() const
+    { return (*rrset_.get_rdatalist().begin()); }
+    void set_ttl(const TTL& ttl) { rrset_.set_ttl(ttl); }
 private:
     // An RR is (could be) actually implemented as an RRset containing at most
     // one RR.
@@ -311,4 +342,3 @@ private:
 // Local Variables: 
 // mode: c++
 // End: 
-