summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog8
-rw-r--r--doc/guide/bind10-guide.xml384
-rw-r--r--src/bin/auth/auth_messages.mes3
-rw-r--r--src/bin/auth/auth_srv.cc3
-rwxr-xr-xsrc/bin/cmdctl/cmdctl.py.in2
-rw-r--r--src/bin/msgq/msgq.xml8
-rwxr-xr-xsrc/bin/xfrin/xfrin.py.in4
-rwxr-xr-xsrc/bin/xfrout/xfrout.py.in3
-rw-r--r--src/bin/xfrout/xfrout.spec.pre.in27
-rw-r--r--src/lib/config/tests/ccsession_unittests.cc11
-rw-r--r--src/lib/datasrc/database.cc57
-rw-r--r--src/lib/datasrc/database.h15
-rw-r--r--src/lib/datasrc/tests/database_unittest.cc37
-rw-r--r--src/lib/datasrc/tests/zone_finder_context_unittest.cc13
-rw-r--r--src/lib/datasrc/zone.h10
-rw-r--r--src/lib/dns/labelsequence.cc16
-rw-r--r--src/lib/dns/labelsequence.h2
-rw-r--r--src/lib/dns/messagerenderer.cc6
-rw-r--r--src/lib/dns/name.cc32
-rw-r--r--src/lib/dns/name.h9
-rw-r--r--src/lib/dns/name_internal.h2
-rw-r--r--src/lib/dns/tests/labelsequence_unittest.cc46
-rw-r--r--src/lib/python/isc/notify/notify_out.py6
-rw-r--r--tests/lettuce/configurations/xfrin/inmem_slave.conf4
-rw-r--r--tests/lettuce/configurations/xfrin/retransfer_master.conf8
-rw-r--r--tests/lettuce/configurations/xfrin/retransfer_slave.conf4
-rw-r--r--tests/lettuce/configurations/xfrin/retransfer_slave_notify.conf38
-rw-r--r--tests/lettuce/data/.gitignore1
-rw-r--r--tests/lettuce/data/example.org1
-rw-r--r--tests/lettuce/data/xfrin-notify.sqlite3.origbin0 -> 13312 bytes
-rw-r--r--tests/lettuce/features/inmemory_over_sqlite3.feature8
-rw-r--r--tests/lettuce/features/queries.feature25
-rw-r--r--tests/lettuce/features/terrain/bind10_control.py4
-rw-r--r--tests/lettuce/features/terrain/terrain.py2
-rw-r--r--tests/lettuce/features/terrain/transfer.py4
-rw-r--r--tests/lettuce/features/xfrin_bind10.feature4
-rw-r--r--tests/lettuce/features/xfrin_notify_handling.feature29
37 files changed, 515 insertions, 321 deletions
diff --git a/ChangeLog b/ChangeLog
index 8fb267fc41..8af82996ee 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+451. [bug] muks, jinmei
+ libdatasrc: the database-based data source now correctly returns
+ glue records on (not under) a zone cut, such as in the case where
+ the NS name of an NS record is identical to its owner name. (Note:
+ libdatasrc itself doesn't judge what kind of record type can be a
+ "glue"; it's the caller's responsibility.)
+ (Trac #1771, git 483f1075942965f0340291e7ff7dae7806df22af)
+
450. [func]* tomek
b10-dhcp4: DHCPv4 server component is now integrated into
BIND10 framework. It can be started from BIND10 (using bindctl)
diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml
index 1bdc0053f5..c2e518689d 100644
--- a/doc/guide/bind10-guide.xml
+++ b/doc/guide/bind10-guide.xml
@@ -36,8 +36,8 @@
<abstract>
<para>BIND 10 is a framework that features Domain Name System
(DNS) suite and Dynamic Host Configuration Protocol (DHCP)
- servers managed by Internet Systems Consortium (ISC). It
- includes DNS libraries, modular components for controlling
+ servers with development managed by Internet Systems Consortium (ISC).
+ It includes DNS libraries, modular components for controlling
authoritative and recursive DNS servers, and experimental DHCPv4
and DHCPv6 servers.
</para>
@@ -59,6 +59,8 @@
<section id="acknowledgements">
<title>Acknowledgements</title>
+<!-- TODO: acknowledge all sponsors and CNNIC and CZNIC too -->
+
<para>ISC would like to acknowledge generous support for
BIND 10 development of DHCPv4 and DHCPv6 components provided
by <ulink url="http://www.comcast.com/">Comcast</ulink>.</para>
@@ -72,11 +74,13 @@
<para>
BIND is the popular implementation of a DNS server, developer
interfaces, and DNS tools.
- BIND 10 is a rewrite of BIND 9. BIND 10 is written in C++ and Python
- and provides a modular environment for serving and maintaining DNS.
+ BIND 10 is a rewrite of BIND 9 and ISC DHCP.
+ BIND 10 is written in C++ and Python and provides a modular
+ environment for serving, maintaining, and developing DNS and DHCP.
BIND 10 provides a EDNS0- and DNSSEC-capable authoritative
DNS server and a caching recursive name server which also
provides forwarding.
+ It also provides experimental DHCPv4 and DHCPv6 servers.
</para>
<para>
@@ -88,7 +92,7 @@
<title>Supported Platforms</title>
<para>
BIND 10 builds have been tested on (in no particular order)
- Debian GNU/Linux 5 and unstable, Ubuntu 9.10, NetBSD 5,
+ Debian GNU/Linux 6 and unstable, Ubuntu 9.10, NetBSD 5,
Solaris 10 and 11, FreeBSD 7 and 8, CentOS Linux 5.3,
MacOS 10.6 and 10.7, and OpenBSD 5.1.
@@ -101,11 +105,21 @@
</section>
<section id="required-software">
- <title>Required Software</title>
+ <title>Required Software at Run-time</title>
+
+ <para>
+ Running BIND 10 uses various extra software which may
+ not be provided in some operating systems' default
+ installations nor standard packages collections. You may
+ need to install this required software separately.
+ (For the build requirements, also see
+ <xref linkend="build-requirements"/>.)
+ </para>
+
<para>
BIND 10 requires at least Python 3.1
(<ulink url="http://www.python.org/"/>).
- It has also been tested with Python 3.2.
+ It also works with Python 3.2.
</para>
<para>
@@ -118,6 +132,7 @@
BIND 10 uses the log4cplus C++ logging library
(<ulink url="http://log4cplus.sourceforge.net/"/>).
It requires at least log4cplus version 1.0.3.
+<!-- TODO: It is recommended to use at least version .... -->
</para>
<para>
@@ -132,20 +147,9 @@
<command>b10-xfrout</command>, and <command>b10-zonemgr</command>
components require the libpython3 library and the Python
_sqlite3.so module (which is included with Python).
- The <command>b10-stats-httpd</command> component uses the
- Python pyexpat.so module.
- The Python modules need to be built for the corresponding Python 3.
+ Python modules need to be built for the corresponding Python 3.
</para>
<!-- TODO: this will change ... -->
-
- <note>
- <para>
- Some operating systems do not provide these dependencies
- in their default installation nor standard packages
- collections.
- You may need to install them separately.
- </para>
- </note>
</section>
<section id="starting_stopping">
@@ -220,8 +224,8 @@
<simpara>
<command>b10-resolver</command> &mdash;
Recursive name server.
- This process handles incoming queries.
-<!-- TODO: -->
+ This process handles incoming DNS queries and provides
+ answers from its cache or by recursively doing remote lookups.
</simpara>
</listitem>
@@ -264,15 +268,14 @@
<command>b10-xfrout</command> &mdash;
Outgoing zone transfer service.
This process is used to handle transfer requests to
- send a local zone to a remote secondary server,
- when acting as a master server.
+ send a local zone to a remote secondary server.
</simpara>
</listitem>
<listitem>
<simpara>
<command>b10-zonemgr</command> &mdash;
- Secondary manager.
+ Secondary zone manager.
This process keeps track of timers and other
necessary information for BIND 10 to act as a slave server.
</simpara>
@@ -282,8 +285,8 @@
</para>
<para>
- These are ran automatically by <command>bind10</command>
- and do not need to be run manually.
+ These are ran by <command>bind10</command>
+ and do not need to be manually started independently.
</para>
</section>
@@ -298,7 +301,7 @@
<listitem>
<simpara>
<command>bindctl</command> &mdash;
- interactive administration interface.
+ Interactive administration interface.
This is a low-level command-line tool which allows
a developer or an experienced administrator to control
BIND 10.
@@ -307,7 +310,7 @@
<listitem>
<simpara>
<command>b10-loadzone</command> &mdash;
- zone file loader.
+ Zone file loader.
This tool will load standard masterfile-format zone files into
BIND 10.
</simpara>
@@ -315,7 +318,7 @@
<listitem>
<simpara>
<command>b10-cmdctl-usermgr</command> &mdash;
- user access control.
+ User access control.
This tool allows an administrator to authorize additional users
to manage BIND 10.
</simpara>
@@ -358,6 +361,7 @@ var/
for C++ and Python for the message bus, configuration backend,
and, of course, DNS. These include detailed developer
documentation and code examples.
+<!-- TODO: DHCP also but no Python yet. -->
<!-- TODO point to this -->
</para>
@@ -366,12 +370,100 @@ var/
<chapter id="installation">
<title>Installation</title>
+ <section id="packages">
+ <title>Packages</title>
+
+ <para>
+ Some operating systems or softare package vendors may
+ provide ready-to-use, pre-built software packages for
+ the BIND 10 suite.
+ Installing a pre-built package means you do not need to
+ install build-only prerequisites and do not need to
+ <emphasis>make</emphasis> the software.
+ </para>
+
+ <para>
+ FreeBSD ports, NetBSD pkgsrc, and Debian
+ <emphasis>testing</emphasis> package collections provide
+ all the prerequisite packages.
+ </para>
+ </section>
+
+ <section id="install-hierarchy">
+ <title>Install Hierarchy</title>
+ <para>
+ The following is the standard, common layout of the
+ complete BIND 10 installation:
+ <itemizedlist>
+ <listitem>
+ <simpara>
+ <filename>bin/</filename> &mdash;
+ general tools and diagnostic clients.
+ </simpara>
+ </listitem>
+ <listitem>
+ <simpara>
+ <filename>etc/bind10-devel/</filename> &mdash;
+ configuration files.
+ </simpara>
+ </listitem>
+ <listitem>
+ <simpara>
+ <filename>lib/</filename> &mdash;
+ libraries and python modules.
+ </simpara>
+ </listitem>
+ <listitem>
+ <simpara>
+ <filename>libexec/bind10-devel/</filename> &mdash;
+ executables that a user wouldn't normally run directly and
+ are not run independently.
+ These are the BIND 10 modules which are daemons started by
+ the <command>bind10</command> tool.
+ </simpara>
+ </listitem>
+ <listitem>
+ <simpara>
+ <filename>sbin/</filename> &mdash;
+ commands used by the system administrator.
+ </simpara>
+ </listitem>
+ <listitem>
+ <simpara>
+ <filename>share/bind10-devel/</filename> &mdash;
+ configuration specifications.
+ </simpara>
+ </listitem>
+ <listitem>
+ <simpara>
+ <filename>share/doc/bind10-devel/</filename> &mdash;
+ this guide and other supplementary documentation.
+ </simpara>
+ </listitem>
+ <listitem>
+ <simpara>
+ <filename>share/man/</filename> &mdash;
+ manual pages (online documentation).
+ </simpara>
+ </listitem>
+ <listitem>
+ <simpara>
+ <filename>var/bind10-devel/</filename> &mdash;
+ data source and configuration databases.
+ </simpara>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </section>
+
<section id="build-requirements">
<title>Building Requirements</title>
<para>
- In addition to the run-time requirements, building BIND 10
- from source code requires various development include headers.
+ In addition to the run-time requirements (listed in
+ <xref linkend="required-software"/>), building BIND 10
+ from source code requires various development include headers and
+ program development tools.
</para>
<note>
@@ -415,7 +507,7 @@ as a dependency earlier -->
</para>
<para>
- Visit the wiki at <ulink
+ Visit the user-contributed wiki at <ulink
url="http://bind10.isc.org/wiki/SystemSpecificNotes" />
for system-specific installation tips.
</para>
@@ -484,7 +576,7 @@ as a dependency earlier -->
</listitem>
<listitem>
-
+<!-- TODO: this is wrong; b10-auth is not started by default any more -->
<para>Test it; for example:
<screen>$ <userinput>dig @127.0.0.1 -c CH -t TXT authors.bind</userinput></screen>
</para>
@@ -510,10 +602,10 @@ as a dependency earlier -->
<title>Installation from source</title>
<para>
BIND 10 is open source software written in C++ and Python.
- It is freely available in source code form from ISC via
- the Git code revision control system or as a downloadable
- tar file. It may also be available in pre-compiled ready-to-use
- packages from operating system vendors.
+ It is freely available in source code form from ISC as a
+ downloadable tar file or via BIND 10's Git code revision control
+ service. (It may also be available in pre-compiled ready-to-use
+ packages from operating system vendors.)
</para>
<section>
@@ -541,7 +633,7 @@ as a dependency earlier -->
<note>
<para>
- When using source code retrieved via Git additional
+ When using source code retrieved via Git, additional
software will be required: automake (v1.11 or newer),
libtoolize, and autoconf (2.59 or newer).
These may need to be installed.
@@ -549,11 +641,12 @@ as a dependency earlier -->
</note>
<para>
- The latest development code, including temporary experiments
- and un-reviewed code, is available via the BIND 10 code revision
+ The latest development code (and temporary experiments
+ and un-reviewed code) is available via the BIND 10 code revision
control system. This is powered by Git and all the BIND 10
development is public.
- The leading development is done in the <quote>master</quote>.
+ The leading development is done in the <quote>master</quote>
+ branch.
</para>
<para>
The code can be checked out from
@@ -566,8 +659,8 @@ as a dependency earlier -->
<para>
When checking out the code from
the code version control system, it doesn't include the
- generated configure script, Makefile.in files, nor the
- related configure files.
+ generated configure script, Makefile.in files, nor their
+ related build files.
They can be created by running <command>autoreconf</command>
with the <option>--install</option> switch.
This will run <command>autoconf</command>,
@@ -591,7 +684,7 @@ as a dependency earlier -->
</para>
<para>
Run <command>./configure</command> with the <option>--help</option>
- switch to view the different options. The commonly-used options are:
+ switch to view the different options. Some commonly-used options are:
<variablelist>
@@ -679,65 +772,6 @@ as a dependency earlier -->
<!-- TODO: tests -->
- <section>
- <title>Install Hierarchy</title>
- <para>
- The following is the layout of the complete BIND 10 installation:
- <itemizedlist>
- <listitem>
- <simpara>
- <filename>bin/</filename> &mdash;
- general tools and diagnostic clients.
- </simpara>
- </listitem>
- <listitem>
- <simpara>
- <filename>etc/bind10-devel/</filename> &mdash;
- configuration files.
- </simpara>
- </listitem>
- <listitem>
- <simpara>
- <filename>lib/</filename> &mdash;
- libraries and python modules.
- </simpara>
- </listitem>
- <listitem>
- <simpara>
- <filename>libexec/bind10-devel/</filename> &mdash;
- executables that a user wouldn't normally run directly and
- are not run independently.
- These are the BIND 10 modules which are daemons started by
- the <command>bind10</command> tool.
- </simpara>
- </listitem>
- <listitem>
- <simpara>
- <filename>sbin/</filename> &mdash;
- commands used by the system administrator.
- </simpara>
- </listitem>
- <listitem>
- <simpara>
- <filename>share/bind10-devel/</filename> &mdash;
- configuration specifications.
- </simpara>
- </listitem>
- <listitem>
- <simpara>
- <filename>share/man/</filename> &mdash;
- manual pages (online documentation).
- </simpara>
- </listitem>
- <listitem>
- <simpara>
- <filename>var/bind10-devel/</filename> &mdash;
- data source and configuration databases.
- </simpara>
- </listitem>
- </itemizedlist>
- </para>
- </section>
</section>
<!--
@@ -775,8 +809,9 @@ as a dependency earlier -->
The <command>b10-cfgmgr</command> daemon is always needed by every
module, if only to send information about themselves somewhere,
but more importantly to ask about their own settings, and
- about other modules. The <command>b10-sockcreator</command> will
- allocate sockets for the rest of the system.
+ about other modules. The <command>b10-sockcreator</command> daemon
+ helps allocate Internet addresses and ports as needed for BIND 10
+ network services.
</para>
<para>
@@ -807,23 +842,22 @@ as a dependency earlier -->
</section>
<section id="bind10.config">
- <title>Configuration of started processes</title>
- <para>
- The processes to be started can be configured, with the exception
- of the <command>b10-sockcreator</command>, <command>b10-msgq</command>
- and <command>b10-cfgmgr</command>.
- </para>
+ <title>Configuration to start processes</title>
<para>
- The configuration is in the Boss/components section. Each element
- represents one component, which is an abstraction of a process
- (currently there's also one component which doesn't represent
- a process).
+ The processes to be used can be configured for
+ <command>bind10</command> to start, with the exception
+ of the required <command>b10-sockcreator</command>,
+ <command>b10-msgq</command> and <command>b10-cfgmgr</command>
+ components.
+ The configuration is in the <varname>Boss/components</varname>
+ section. Each element represents one component, which is
+ an abstraction of a process.
</para>
<para>
- To add a process to the set, let's say the resolver (which not started
- by default), you would do this:
+ To add a process to the set, let's say the resolver (which
+ is not started by default), you would do this:
<screen>&gt; <userinput>config add Boss/components b10-resolver</userinput>
&gt; <userinput>config set Boss/components/b10-resolver/special resolver</userinput>
&gt; <userinput>config set Boss/components/b10-resolver/kind needed</userinput>
@@ -831,27 +865,32 @@ as a dependency earlier -->
&gt; <userinput>config commit</userinput></screen></para>
<para>
- Now, what it means. We add an entry called b10-resolver. It is both a
- name used to reference this component in the configuration and the
- name of the process to start. Then we set some parameters on how to
- start it.
+ Now, what it means. We add an entry called
+ <quote>b10-resolver</quote>. It is both a name used to
+ reference this component in the configuration and the name
+ of the process to start. Then we set some parameters on
+ how to start it.
</para>
<para>
- The special one is for components that need some kind of special care
- during startup or shutdown. Unless specified, the component is started
- in usual way. This is the list of components that need to be started
- in a special way, with the value of special used for them:
+ The <varname>special</varname> setting is for components
+ that need some kind of special care during startup or
+ shutdown. Unless specified, the component is started in a
+ usual way. This is the list of components that need to be
+ started in a special way, with the value of special used
+ for them:
+<!-- TODO: this still doesn't explain why they are special -->
<table>
+ <title>Special startup components</title>
<tgroup cols='3' align='left'>
<colspec colname='component'/>
<colspec colname='special'/>
<colspec colname='description'/>
<thead><row><entry>Component</entry><entry>Special</entry><entry>Description</entry></row></thead>
<tbody>
- <row><entry>b10-auth</entry><entry>auth</entry><entry>Authoritative server</entry></row>
- <row><entry>b10-resolver</entry><entry>resolver</entry><entry>The resolver</entry></row>
- <row><entry>b10-cmdctl</entry><entry>cmdctl</entry><entry>The command control (remote control interface)</entry></row>
+ <row><entry>b10-auth</entry><entry>auth</entry><entry>Authoritative DNS server</entry></row>
+ <row><entry>b10-resolver</entry><entry>resolver</entry><entry>DNS resolver</entry></row>
+ <row><entry>b10-cmdctl</entry><entry>cmdctl</entry><entry>Command control (remote control interface)</entry></row>
<!-- TODO Either add xfrin and xfrout as well or clean up the workarounds in boss before the release -->
</tbody>
</tgroup>
@@ -859,32 +898,34 @@ as a dependency earlier -->
</para>
<para>
- The kind specifies how a failure of the component should
- be handled. If it is set to <quote>dispensable</quote>
- (the default unless you set something else), it will get
- started again if it fails. If it is set to <quote>needed</quote>
- and it fails at startup, the whole <command>bind10</command>
- shuts down and exits with error exit code. But if it fails
- some time later, it is just started again. If you set it
- to <quote>core</quote>, you indicate that the system is
- not usable without the component and if such component
- fails, the system shuts down no matter when the failure
- happened. This is the behaviour of the core components
- (the ones you can't turn off), but you can declare any
- other components as core as well if you wish (but you can
- turn these off, they just can't fail).
+ The <varname>kind</varname> specifies how a failure of the
+ component should be handled. If it is set to
+ <quote>dispensable</quote> (the default unless you set
+ something else), it will get started again if it fails. If
+ it is set to <quote>needed</quote> and it fails at startup,
+ the whole <command>bind10</command> shuts down and exits
+ with an error exit code. But if it fails some time later, it
+ is just started again. If you set it to <quote>core</quote>,
+ you indicate that the system is not usable without the
+ component and if such component fails, the system shuts
+ down no matter when the failure happened. This is the
+ behaviour of the core components (the ones you can't turn
+ off), but you can declare any other components as core as
+ well if you wish (but you can turn these off, they just
+ can't fail).
</para>
<para>
- The priority defines order in which the components should start.
- The ones with higher number are started sooner than the ones with
- lower ones. If you don't set it, 0 (zero) is used as the priority.
- Usually, leaving it at the default is enough.
+ The <varname>priority</varname> defines order in which the
+ components should start. The ones with higher numbers are
+ started sooner than the ones with lower ones. If you don't
+ set it, 0 (zero) is used as the priority. Usually, leaving
+ it at the default is enough.
</para>
<para>
There are other parameters we didn't use in our example.
- One of them is <quote>address</quote>. It is the address
+ One of them is <varname>address</varname>. It is the address
used by the component on the <command>b10-msgq</command>
message bus. The special components already know their
address, but the usual ones don't. The address is by
@@ -900,25 +941,17 @@ address, but the usual ones don't." mean? -->
<!-- TODO: document params when is enabled -->
<para>
- The last one is process. It is the name of the process to be started.
- It defaults to the name of the component if not set, but you can use
- this to override it.
+ The last one is <varname>process</varname>. It is the name
+ of the process to be started. It defaults to the name of
+ the component if not set, but you can use this to override
+ it. (The special components also already know their
+ executable name.)
</para>
<!-- TODO Add parameters when they work, not implemented yet-->
<note>
<para>
- This system allows you to start the same component multiple times
- (by including it in the configuration with different names, but the
- same process setting). However, the rest of the system doesn't expect
- such a situation, so it would probably not do what you want. Such
- support is yet to be implemented.
- </para>
- </note>
-
- <note>
- <para>
The configuration is quite powerful, but that includes
a lot of space for mistakes. You could turn off the
<command>b10-cmdctl</command>, but then you couldn't
@@ -938,7 +971,7 @@ address, but the usual ones don't." mean? -->
</note>
<para>
It is possible to start some components multiple times (currently
- <command>b10-auth</command> and <command>b10-resolzer</command>).
+ <command>b10-auth</command> and <command>b10-resolver</command>).
You might want to do that to gain more performance (each one uses only
single core). Just put multiple entries under different names, like
this, with the same config:
@@ -953,6 +986,9 @@ address, but the usual ones don't." mean? -->
server will keep its own copy of in-memory data and there could be
problems with locking the sqlite database, if used. The configuration
might be changed to something more convenient in future.
+ Other components don't expect such a situation, so it would
+ probably not do what you want. Such support is yet to be
+ implemented.
</para>
</section>
@@ -977,23 +1013,11 @@ address, but the usual ones don't." mean? -->
<para>
Administrators do not communicate directly with the
<command>b10-msgq</command> daemon.
- By default, BIND 10 uses port 9912 for the
- <command>b10-msgq</command> service.
- It listens on 127.0.0.1.
+ By default, BIND 10 uses a UNIX domain socket file named
+ <filename>/usr/local/var/bind10-devel/msg_socket</filename>
+ for this interprocess communication.
</para>
-<!-- TODO: this is broken, see Trac #111
- <para>
- To select an alternate port for the <command>b10-msgq</command> to
- use, run <command>bind10</command> specifying the option:
- <screen> $ <userinput>bind10 -TODO-msgq-port 9912</userinput></screen>
- </para>
--->
-
-<!-- TODO: upcoming plans:
-Unix domain sockets
--->
-
</chapter>
<chapter id="cfgmgr">
@@ -1035,9 +1059,6 @@ Unix domain sockets
specifications and all current settings to the
<command>bindctl</command> client (via
<command>b10-cmdctl</command>).
- </para>
-
- <para>
<command>b10-cfgmgr</command> relays configurations received
from <command>b10-cmdctl</command> to the appropriate modules.
</para>
@@ -1056,7 +1077,7 @@ config changes are actually commands to cfgmgr
<para>
The stored configuration file is at
<filename>/usr/local/var/bind10-devel/b10-config.db</filename>.
- (The full path is what was defined at build configure time for
+ (The directory is what was defined at build configure time for
<option>--localstatedir</option>.
The default is <filename>/usr/local/var/</filename>.)
The format is loosely based on JSON and is directly parseable
@@ -1174,7 +1195,7 @@ but you might wanna check with likun
<!-- TODO
openssl req -new -x509 -keyout server.pem -out server.pem -days 365 -nodes
-but that is a single file, maybethis should go back to that format?
+but that is a single file, maybe this should go back to that format?
-->
<!--
@@ -1241,7 +1262,6 @@ shutdown
<!--
TODO
(12:21:30) jinmei: I'd like to have sample session using a command line www client such as wget
-(12:21:33) jinmei: btw
-->
</chapter>
diff --git a/src/bin/auth/auth_messages.mes b/src/bin/auth/auth_messages.mes
index 9ac2c499ef..61de3ee0f5 100644
--- a/src/bin/auth/auth_messages.mes
+++ b/src/bin/auth/auth_messages.mes
@@ -110,6 +110,9 @@ look into the cause and address the issue. The log message includes
the client's address (and port), and the error message sent from the
lower layer that detects the failure.
+% AUTH_RECEIVED_NOTIFY received incoming NOTIFY for zone name %1, zone class %2
+This is a debug message reporting that an incoming NOTIFY was received.
+
% AUTH_NOTIFY_QUESTIONS invalid number of questions (%1) in incoming NOTIFY
This debug message is logged by the authoritative server when it receives
a NOTIFY packet that contains zero or more than one question. (A valid
diff --git a/src/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc
index 2a47c38822..7b8e963365 100644
--- a/src/bin/auth/auth_srv.cc
+++ b/src/bin/auth/auth_srv.cc
@@ -828,6 +828,9 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message,
return (false);
}
+ LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RECEIVED_NOTIFY)
+ .arg(question->getName()).arg(question->getClass());
+
const string remote_ip_address =
io_message.getRemoteEndpoint().getAddress().toText();
static const string command_template_start =
diff --git a/src/bin/cmdctl/cmdctl.py.in b/src/bin/cmdctl/cmdctl.py.in
index 74fc364392..4076f4cbbb 100755
--- a/src/bin/cmdctl/cmdctl.py.in
+++ b/src/bin/cmdctl/cmdctl.py.in
@@ -541,7 +541,7 @@ class SecureHTTPServer(socketserver_mixin.NoPollMixIn,
ssl_version = ssl.PROTOCOL_SSLv23)
return ssl_sock
except (ssl.SSLError, CmdctlException) as err :
- logger.info(CMDCTL_SSL_SETUP_FAILURE_USER_DENIED, err)
+ logger.error(CMDCTL_SSL_SETUP_FAILURE_USER_DENIED, err)
self.close_request(sock)
# raise socket error to finish the request
raise socket.error
diff --git a/src/bin/msgq/msgq.xml b/src/bin/msgq/msgq.xml
index fd9518d757..fed4c27c06 100644
--- a/src/bin/msgq/msgq.xml
+++ b/src/bin/msgq/msgq.xml
@@ -20,7 +20,7 @@
<refentry>
<refentryinfo>
- <date>August 4, 2010</date>
+ <date>June 25, 2012</date>
</refentryinfo>
<refmeta>
@@ -92,10 +92,6 @@
</para>
<para>
- It listens on 127.0.0.1.
- </para>
-
- <para>
The <command>b10-msgq</command> daemon may be cleanly stopped by
sending the SIGTERM signal to the process.
This shutdown does not notify the subscribers.
@@ -168,5 +164,3 @@
- End:
-->
-
-
diff --git a/src/bin/xfrin/xfrin.py.in b/src/bin/xfrin/xfrin.py.in
index 652f870a5a..1f04bee94d 100755
--- a/src/bin/xfrin/xfrin.py.in
+++ b/src/bin/xfrin/xfrin.py.in
@@ -889,6 +889,10 @@ class XfrinConnection(asyncore.dispatcher):
req_str = 'IXFR' if request_type == RRType.IXFR() else 'AXFR'
if check_soa:
self._check_soa_serial()
+ self.close()
+ self.init_socket()
+ if not self.connect_to_master():
+ raise XfrinException('Unable to reconnect to master')
logger.info(XFRIN_XFR_TRANSFER_STARTED, req_str, self.zone_str())
self._send_query(self._request_type)
diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in
index 46ae687323..6576432d78 100755
--- a/src/bin/xfrout/xfrout.py.in
+++ b/src/bin/xfrout/xfrout.py.in
@@ -952,6 +952,9 @@ class XfroutServer:
def _start_notifier(self):
datasrc = self._unix_socket_server.get_db_file()
self._notifier = notify_out.NotifyOut(datasrc)
+ if 'also_notify' in self._config_data:
+ for slave in self._config_data['also_notify']:
+ self._notifier.add_slave(slave['address'], slave['port'])
self._notifier.dispatcher()
def send_notify(self, zone_name, zone_class):
diff --git a/src/bin/xfrout/xfrout.spec.pre.in b/src/bin/xfrout/xfrout.spec.pre.in
index 30c9d56fae..dfcc6d98f4 100644
--- a/src/bin/xfrout/xfrout.spec.pre.in
+++ b/src/bin/xfrout/xfrout.spec.pre.in
@@ -21,6 +21,33 @@
}
},
{
+ "item_name": "also_notify",
+ "item_type": "list",
+ "item_optional": true,
+ "item_default": [],
+ "list_item_spec":
+ {
+ "item_name": "also_notify_element",
+ "item_type": "map",
+ "item_optional": true,
+ "item_default": {},
+ "map_item_spec": [
+ {
+ "item_name": "address",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": ""
+ },
+ {
+ "item_name": "port",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0
+ }
+ ]
+ }
+ },
+ {
"item_name": "zone_config",
"item_type": "list",
"item_optional": true,
diff --git a/src/lib/config/tests/ccsession_unittests.cc b/src/lib/config/tests/ccsession_unittests.cc
index 3fca741bbb..e07c5a329b 100644
--- a/src/lib/config/tests/ccsession_unittests.cc
+++ b/src/lib/config/tests/ccsession_unittests.cc
@@ -33,7 +33,6 @@ using namespace isc::data;
using namespace isc::config;
using namespace isc::cc;
using namespace std;
-using namespace boost;
namespace {
std::string
@@ -52,6 +51,7 @@ protected:
root_name(isc::log::getRootLoggerName())
{
// upon creation of a ModuleCCSession, the class
+
// sends its specification to the config manager.
// it expects an ok answer back, so everytime we
// create a ModuleCCSession, we must set an initial
@@ -740,8 +740,9 @@ protected:
registerCommand(const string& recipient)
{
return (mccs_.groupRecvMsgAsync(
- bind(&AsyncReceiveCCSessionTest::callback, this, next_flag_ ++, _1,
- _2, _3), false, -1, recipient));
+ boost::bind(&AsyncReceiveCCSessionTest::callback, this,
+ next_flag_++, _1, _2, _3), false, -1,
+ recipient));
}
/// \brief Convenience function to queue a request to get a reply
/// message.
@@ -749,8 +750,8 @@ protected:
registerReply(int seq)
{
return (mccs_.groupRecvMsgAsync(
- bind(&AsyncReceiveCCSessionTest::callback, this, next_flag_ ++, _1,
- _2, _3), true, seq));
+ boost::bind(&AsyncReceiveCCSessionTest::callback, this,
+ next_flag_++, _1, _2, _3), true, seq));
}
/// \brief Check the next called callback was with this flag
void called(int flag) {
diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc
index 61dab39db9..a8ba55e7fa 100644
--- a/src/lib/datasrc/database.cc
+++ b/src/lib/datasrc/database.cc
@@ -179,8 +179,7 @@ private:
DatabaseClient::Finder::FoundRRsets
DatabaseClient::Finder::getRRsets(const string& name, const WantedTypes& types,
- bool check_ns, const string* construct_name,
- bool any,
+ const string* construct_name, bool any,
DatabaseAccessor::IteratorContextPtr context)
{
RRsigStore sig_store;
@@ -204,9 +203,7 @@ DatabaseClient::Finder::getRRsets(const string& name, const WantedTypes& types,
const Name construct_name_object(*construct_name);
bool seen_cname(false);
- bool seen_ds(false);
bool seen_other(false);
- bool seen_ns(false);
while (context->getNext(columns)) {
// The domain is not empty
@@ -249,16 +246,12 @@ DatabaseClient::Finder::getRRsets(const string& name, const WantedTypes& types,
if (cur_type == RRType::CNAME()) {
seen_cname = true;
- } else if (cur_type == RRType::NS()) {
- seen_ns = true;
- } else if (cur_type == RRType::DS()) {
- seen_ds = true;
} else if (cur_type != RRType::RRSIG() &&
cur_type != RRType::NSEC3() &&
cur_type != RRType::NSEC()) {
// NSEC and RRSIG can coexist with anything, otherwise
// we've seen something that can't live together with potential
- // CNAME or NS
+ // CNAME.
//
// NSEC3 lives in separate namespace from everything, therefore
// we just ignore it here for these checks as well.
@@ -278,14 +271,10 @@ DatabaseClient::Finder::getRRsets(const string& name, const WantedTypes& types,
RDATA_COLUMN]);
}
}
- if (seen_cname && (seen_other || seen_ns || seen_ds)) {
+ if (seen_cname && seen_other) {
isc_throw(DataSourceError, "CNAME shares domain " << name <<
" with something else");
}
- if (check_ns && seen_ns && seen_other) {
- isc_throw(DataSourceError, "NS shares domain " << name <<
- " with something else");
- }
// Add signatures to all found RRsets
for (std::map<RRType, RRsetPtr>::iterator i(result.begin());
i != result.end(); ++ i) {
@@ -455,20 +444,20 @@ DatabaseClient::Finder::findDelegationPoint(const isc::dns::Name& name,
for (int i = remove_labels; i > 0; --i) {
const Name superdomain(name.split(i));
- // Note if this is the origin. (We don't count NS records at the origin
- // as a delegation so this controls whether NS RRs are included in
- // the results of some searches.)
- const bool not_origin = (i != remove_labels);
-
// Look if there's NS or DNAME at this point of the tree, but ignore
// the NS RRs at the apex of the zone.
const FoundRRsets found = getRRsets(superdomain.toText(),
- DELEGATION_TYPES(), not_origin);
+ DELEGATION_TYPES());
if (found.first) {
// This node contains either NS or DNAME RRs so it does exist.
const FoundIterator nsi(found.second.find(RRType::NS()));
const FoundIterator dni(found.second.find(RRType::DNAME()));
+ // Note if this is the origin. (We don't count NS records at the
+ // origin as a delegation so this controls whether NS RRs are
+ // included in the results of some searches.)
+ const bool not_origin = (i != remove_labels);
+
// An optimisation. We know that there is an exact match for
// something at this point in the tree so remember it. If we have
// to do a wildcard search, as we search upwards through the tree
@@ -477,7 +466,7 @@ DatabaseClient::Finder::findDelegationPoint(const isc::dns::Name& name,
last_known = superdomain.getLabelCount();
if (glue_ok && !first_ns && not_origin &&
- nsi != found.second.end()) {
+ nsi != found.second.end()) {
// If we are searching for glue ("glue OK" mode), store the
// highest NS record that we find that is not the apex. This
// is another optimisation for later, where we need the
@@ -590,8 +579,9 @@ DatabaseClient::Finder::findWildcardMatch(
// TODO Add a check for DNAME, as DNAME wildcards are discouraged (see
// RFC 4592 section 4.4).
// Search for a match. The types are the same as with original query.
- FoundRRsets found = getRRsets(wildcard, final_types, true,
- &construct_name, type == RRType::ANY());
+ const FoundRRsets found = getRRsets(wildcard, final_types,
+ &construct_name,
+ type == RRType::ANY());
if (found.first) {
// Found something - but what?
@@ -694,7 +684,7 @@ DatabaseClient::Finder::FindDNSSECContext::probe() {
// such cases).
const string origin = finder_.getOrigin().toText();
const FoundRRsets nsec3_found =
- finder_.getRRsets(origin, NSEC3PARAM_TYPES(), false);
+ finder_.getRRsets(origin, NSEC3PARAM_TYPES());
const FoundIterator nfi=
nsec3_found.second.find(RRType::NSEC3PARAM());
is_nsec3_ = (nfi != nsec3_found.second.end());
@@ -705,7 +695,7 @@ DatabaseClient::Finder::FindDNSSECContext::probe() {
// described in Section 10.4 of RFC 5155.
if (!is_nsec3_) {
const FoundRRsets nsec_found =
- finder_.getRRsets(origin, NSEC_TYPES(), false);
+ finder_.getRRsets(origin, NSEC_TYPES());
const FoundIterator nfi =
nsec_found.second.find(RRType::NSEC());
is_nsec_ = (nfi != nsec_found.second.end());
@@ -757,10 +747,8 @@ DatabaseClient::Finder::FindDNSSECContext::getDNSSECRRset(const Name &name,
try {
const Name& nsec_name =
covering ? finder_.findPreviousName(name) : name;
- const bool need_nscheck = (nsec_name != finder_.getOrigin());
const FoundRRsets found = finder_.getRRsets(nsec_name.toText(),
- NSEC_TYPES(),
- need_nscheck);
+ NSEC_TYPES());
const FoundIterator nci = found.second.find(RRType::NSEC());
if (nci != found.second.end()) {
return (nci->second);
@@ -984,16 +972,15 @@ DatabaseClient::Finder::findInternal(const Name& name, const RRType& type,
// - Requested name is a delegation point (NS only but not at the zone
// apex - DNAME is ignored here as it redirects DNS names subordinate to
// the owner name - the owner name itself is not redirected.)
- const bool is_origin = (name == getOrigin());
WantedTypes final_types(FINAL_TYPES());
final_types.insert(type);
const FoundRRsets found = getRRsets(name.toText(), final_types,
- !is_origin, NULL,
- type == RRType::ANY());
+ NULL, type == RRType::ANY());
FindDNSSECContext dnssec_ctx(*this, options);
if (found.first) {
// Something found at the domain name. Look into it further to get
// the final result.
+ const bool is_origin = (name == getOrigin());
return (findOnNameResult(name, type, options, is_origin, found, NULL,
target, dnssec_ctx));
} else {
@@ -1021,7 +1008,7 @@ DatabaseClient::Finder::findNSEC3(const Name& name, bool recursive) {
// Now, we need to get the NSEC3 params from the apex and create the hash
// creator for it.
const FoundRRsets nsec3param(getRRsets(getOrigin().toText(),
- NSEC3PARAM_TYPES(), false));
+ NSEC3PARAM_TYPES()));
const FoundIterator param(nsec3param.second.find(RRType::NSEC3PARAM()));
if (!nsec3param.first || param == nsec3param.second.end()) {
// No NSEC3 params? :-(
@@ -1061,7 +1048,7 @@ DatabaseClient::Finder::findNSEC3(const Name& name, bool recursive) {
}
const FoundRRsets nsec3(getRRsets(hash + "." + otext, NSEC3_TYPES(),
- false, NULL, false, context));
+ NULL, false, context));
if (nsec3.first) {
// We found an exact match against the current label.
@@ -1086,8 +1073,8 @@ DatabaseClient::Finder::findNSEC3(const Name& name, bool recursive) {
arg(labels).arg(prevHash);
context = accessor_->getNSEC3Records(prevHash, zone_id_);
const FoundRRsets prev_nsec3(getRRsets(prevHash + "." + otext,
- NSEC3_TYPES(), false, NULL,
- false, context));
+ NSEC3_TYPES(), NULL, false,
+ context));
if (!prev_nsec3.first) {
isc_throw(DataSourceError, "Hash " + prevHash + " returned "
diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h
index 8ad1c5b86f..65ddfccc44 100644
--- a/src/lib/datasrc/database.h
+++ b/src/lib/datasrc/database.h
@@ -963,17 +963,14 @@ public:
///
/// \param name Which domain name should be scanned.
/// \param types List of types the caller is interested in.
- /// \param check_ns If this is set to true, it checks nothing lives
- /// together with NS record (with few little exceptions, like RRSIG
- /// or NSEC). This check is meant for non-apex NS records.
/// \param construct_name If this is NULL, the resulting RRsets have
- /// their name set to name. If it is not NULL, it overrides the name
- /// and uses this one (this can be used for wildcard synthesized
- /// records).
+ /// their name set to name. If it is not NULL, it overrides the
+ /// name and uses this one (this can be used for wildcard
+ /// synthesized records).
/// \param any If this is true, it records all the types, not only the
/// ones requested by types. It also puts a NULL pointer under the
- /// ANY type into the result, if it finds any RRs at all, to easy the
- /// identification of success.
+ /// ANY type into the result, if it finds any RRs at all, to easy
+ /// the identification of success.
/// \param srcContext This can be set to non-NULL value to override the
/// iterator context used for obtaining the data. This can be used,
/// for example, to get data from the NSEC3 namespace.
@@ -986,7 +983,7 @@ public:
/// \throw DataSourceError If there's a low-level error with the
/// database or the database contains bad data.
FoundRRsets getRRsets(const std::string& name,
- const WantedTypes& types, bool check_ns,
+ const WantedTypes& types,
const std::string* construct_name = NULL,
bool any = false,
DatabaseAccessor::IteratorContextPtr srcContext =
diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc
index c96a1d28a8..5f18283995 100644
--- a/src/lib/datasrc/tests/database_unittest.cc
+++ b/src/lib/datasrc/tests/database_unittest.cc
@@ -168,13 +168,16 @@ const char* const TEST_RECORDS[][5] = {
{"child.insecdelegation.example.org.", "DS", "3600", "", "DS 5 3 3600 "
"20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
- // Broken NS
+ // Delegation NS and other ordinary type of RR coexist at the same
+ // name. This is deviant (except for some special cases like the other
+ // RR could be used for addressing the NS name), but as long as the
+ // other records are hidden behind the delegation for normal queries
+ // it's not necessarily harmful. (so "broken" may be too strong, but we
+ // keep the name since it could be in a chain of sorted names for DNSSEC
+ // processing and renaming them may have other bad effects for tests).
{"brokenns1.example.org.", "A", "3600", "", "192.0.2.1"},
{"brokenns1.example.org.", "NS", "3600", "", "ns.example.com."},
- {"brokenns2.example.org.", "NS", "3600", "", "ns.example.com."},
- {"brokenns2.example.org.", "A", "3600", "", "192.0.2.1"},
-
// Now double DNAME, to test failure mode
{"baddname.example.org.", "DNAME", "3600", "", "dname1.example.com."},
{"baddname.example.org.", "DNAME", "3600", "", "dname2.example.com."},
@@ -2202,15 +2205,23 @@ TYPED_TEST(DatabaseClientTest, findDelegation) {
ZoneFinder::FIND_DEFAULT),
DataSourceError);
- // Broken NS - it lives together with something else
- EXPECT_THROW(finder->find(isc::dns::Name("brokenns1.example.org."),
- this->qtype_,
- ZoneFinder::FIND_DEFAULT),
- DataSourceError);
- EXPECT_THROW(finder->find(isc::dns::Name("brokenns2.example.org."),
- this->qtype_,
- ZoneFinder::FIND_DEFAULT),
- DataSourceError);
+ // NS and other type coexist: deviant and not necessarily harmful.
+ // It should normally just result in DELEGATION; if GLUE_OK is specified,
+ // the other RR should be visible.
+ this->expected_rdatas_.clear();
+ this->expected_rdatas_.push_back("ns.example.com");
+ doFindTest(*finder, Name("brokenns1.example.org"), this->qtype_,
+ RRType::NS(), this->rrttl_, ZoneFinder::DELEGATION,
+ this->expected_rdatas_, this->empty_rdatas_,
+ ZoneFinder::RESULT_DEFAULT);
+
+ this->expected_rdatas_.clear();
+ this->expected_rdatas_.push_back("192.0.2.1");
+ doFindTest(*finder, Name("brokenns1.example.org"), this->qtype_,
+ this->qtype_, this->rrttl_, ZoneFinder::SUCCESS,
+ this->expected_rdatas_, this->empty_rdatas_,
+ ZoneFinder::RESULT_DEFAULT, Name("brokenns1.example.org"),
+ ZoneFinder::FIND_GLUE_OK);
}
TYPED_TEST(DatabaseClientTest, findDS) {
diff --git a/src/lib/datasrc/tests/zone_finder_context_unittest.cc b/src/lib/datasrc/tests/zone_finder_context_unittest.cc
index 50d409eb4c..b3c9b2d2fe 100644
--- a/src/lib/datasrc/tests/zone_finder_context_unittest.cc
+++ b/src/lib/datasrc/tests/zone_finder_context_unittest.cc
@@ -208,13 +208,6 @@ TEST_P(ZoneFinderContextTest, getAdditionalDelegation) {
TEST_P(ZoneFinderContextTest, getAdditionalDelegationAtZoneCut) {
// Similar to the previous case, but one of the NS addresses is at the
// zone cut.
-
- // XXX: the current database-based data source incorrectly rejects this
- // setup (see #1771)
- if (GetParam() == createSQLite3Client) {
- return;
- }
-
ZoneFinderContextPtr ctx = finder_->find(Name("www.b.example.org"),
RRType::SOA());
EXPECT_EQ(ZoneFinder::DELEGATION, ctx->code);
@@ -316,12 +309,6 @@ TEST_P(ZoneFinderContextTest, getAdditionalMX) {
}
TEST_P(ZoneFinderContextTest, getAdditionalMXAtZoneCut) {
- // XXX: the current database-based data source incorrectly rejects this
- // setup (see #1771)
- if (GetParam() == createSQLite3Client) {
- return;
- }
-
ZoneFinderContextPtr ctx = finder_->find(Name("mxatcut.example.org."),
RRType::MX());
EXPECT_EQ(ZoneFinder::SUCCESS, ctx->code);
diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h
index bf4b1eac76..c41a2c20a0 100644
--- a/src/lib/datasrc/zone.h
+++ b/src/lib/datasrc/zone.h
@@ -376,6 +376,16 @@ public:
/// RRsets for that name are searched just like the normal case;
/// otherwise, if the search has encountered a zone cut, \c DELEGATION
/// with the information of the highest zone cut will be returned.
+ /// Note: the term "glue" in the DNS protocol standard may sometimes
+ /// cause confusion: some people use this term strictly for an address
+ /// record (type AAAA or A) for the name used in the RDATA of an NS RR;
+ /// some others seem to give it broader flexibility. Nevertheless,
+ /// in this API the "GLUE OK" simply means the search by find() can
+ /// continue beyond a zone cut; the derived class implementation does
+ /// not have to, and should not, check whether the type is an address
+ /// record or whether the query name is pointed by some NS RR.
+ /// It's up to the caller with which definition of "glue" the search
+ /// result with this option should be used.
/// - \c FIND_DNSSEC Request that DNSSEC data (like NSEC, RRSIGs) are
/// returned with the answer. It is allowed for the data source to
/// include them even when not requested.
diff --git a/src/lib/dns/labelsequence.cc b/src/lib/dns/labelsequence.cc
index acbafe47f9..a9be6bcc00 100644
--- a/src/lib/dns/labelsequence.cc
+++ b/src/lib/dns/labelsequence.cc
@@ -23,7 +23,7 @@
namespace isc {
namespace dns {
-const char*
+const uint8_t*
LabelSequence::getData(size_t *len) const {
*len = getDataLength();
return (&name_.ndata_[name_.offsets_[first_label_]]);
@@ -47,22 +47,22 @@ LabelSequence::getDataLength() const {
bool
LabelSequence::equals(const LabelSequence& other, bool case_sensitive) const {
size_t len, other_len;
- const char* data = getData(&len);
- const char* other_data = other.getData(&other_len);
+ const uint8_t* data = getData(&len);
+ const uint8_t* other_data = other.getData(&other_len);
if (len != other_len) {
return (false);
}
if (case_sensitive) {
- return (std::strncmp(data, other_data, len) == 0);
+ return (std::memcmp(data, other_data, len) == 0);
}
// As long as the data was originally validated as (part of) a name,
// label length must never be a capital ascii character, so we can
// simply compare them after converting to lower characters.
for (size_t i = 0; i < len; ++i) {
- const unsigned char ch = data[i];
- const unsigned char other_ch = other_data[i];
+ const uint8_t ch = data[i];
+ const uint8_t other_ch = other_data[i];
if (isc::dns::name::internal::maptolower[ch] !=
isc::dns::name::internal::maptolower[other_ch]) {
return (false);
@@ -112,14 +112,14 @@ LabelSequence::isAbsolute() const {
size_t
LabelSequence::getHash(bool case_sensitive) const {
size_t length;
- const char* s = getData(&length);
+ const uint8_t* s = getData(&length);
if (length > 16) {
length = 16;
}
size_t hash_val = 0;
while (length > 0) {
- const unsigned char c = *s++;
+ const uint8_t c = *s++;
boost::hash_combine(hash_val, case_sensitive ? c :
isc::dns::name::internal::maptolower[c]);
--length;
diff --git a/src/lib/dns/labelsequence.h b/src/lib/dns/labelsequence.h
index 62986753e9..cf5495bfc2 100644
--- a/src/lib/dns/labelsequence.h
+++ b/src/lib/dns/labelsequence.h
@@ -67,7 +67,7 @@ public:
/// \param len Pointer to a size_t where the length of the data
/// will be stored (in number of octets)
/// \return Pointer to the wire-format data of this label sequence
- const char* getData(size_t* len) const;
+ const uint8_t* getData(size_t* len) const;
/// \brief Return the length of the wire-format data of this LabelSequence
///
diff --git a/src/lib/dns/messagerenderer.cc b/src/lib/dns/messagerenderer.cc
index d5c7c69abe..ca4ea542b6 100644
--- a/src/lib/dns/messagerenderer.cc
+++ b/src/lib/dns/messagerenderer.cc
@@ -100,8 +100,8 @@ struct NameCompare {
uint16_t item_label_len = 0;
for (size_t i = 0; i < item.len_; ++i, ++item_pos) {
item_pos = nextPosition(*buffer_, item_pos, item_label_len);
- const unsigned char ch1 = (*buffer_)[item_pos];
- const unsigned char ch2 = name_buf_->readUint8();
+ const uint8_t ch1 = (*buffer_)[item_pos];
+ const uint8_t ch2 = name_buf_->readUint8();
if (CASE_SENSITIVE) {
if (ch1 != ch2) {
return (false);
@@ -293,7 +293,7 @@ MessageRenderer::writeName(const Name& name, const bool compress) {
LabelSequence sequence(name);
const size_t nlabels = sequence.getLabelCount();
size_t data_len;
- const char* data;
+ const uint8_t* data;
// Find the offset in the offset table whose name gives the longest
// match against the name to be rendered.
diff --git a/src/lib/dns/name.cc b/src/lib/dns/name.cc
index a28f8d33bd..a8b080c438 100644
--- a/src/lib/dns/name.cc
+++ b/src/lib/dns/name.cc
@@ -75,7 +75,7 @@ const char digitvalue[256] = {
namespace name {
namespace internal {
-const unsigned char maptolower[] = {
+const uint8_t maptolower[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
@@ -147,11 +147,11 @@ Name::Name(const std::string &namestring, bool downcase) {
bool is_root = false;
ft_state state = ft_init;
- std::vector<unsigned char> offsets;
+ NameOffsets offsets;
offsets.reserve(Name::MAX_LABELS);
offsets.push_back(0);
- std::string ndata;
+ NameString ndata;
ndata.reserve(Name::MAX_WIRE);
// should we refactor this code using, e.g, the state pattern? Probably
@@ -310,7 +310,7 @@ typedef enum {
}
Name::Name(InputBuffer& buffer, bool downcase) {
- std::vector<unsigned char> offsets;
+ NameOffsets offsets;
offsets.reserve(Name::MAX_LABELS);
/*
@@ -436,8 +436,8 @@ Name::toText(bool omit_final_dot) const {
return (".");
}
- std::string::const_iterator np = ndata_.begin();
- std::string::const_iterator np_end = ndata_.end();
+ NameString::const_iterator np = ndata_.begin();
+ NameString::const_iterator np_end = ndata_.end();
unsigned int labels = labelcount_; // use for integrity check
// init with an impossible value to catch error cases in the end:
unsigned int count = MAX_LABELLEN + 1;
@@ -467,7 +467,7 @@ Name::toText(bool omit_final_dot) const {
}
while (count-- > 0) {
- unsigned char c = *np++;
+ uint8_t c = *np++;
switch (c) {
case 0x22: // '"'
case 0x28: // '('
@@ -554,8 +554,8 @@ Name::compare(const Name& other,
unsigned int count = (cdiff < 0) ? count1 : count2;
while (count > 0) {
- unsigned char label1 = ndata_[pos1];
- unsigned char label2 = other.ndata_[pos2];
+ uint8_t label1 = ndata_[pos1];
+ uint8_t label2 = other.ndata_[pos2];
int chdiff;
if (case_sensitive) {
@@ -611,15 +611,15 @@ Name::equals(const Name& other) const {
}
for (unsigned int l = labelcount_, pos = 0; l > 0; --l) {
- unsigned char count = ndata_[pos];
+ uint8_t count = ndata_[pos];
if (count != other.ndata_[pos]) {
return (false);
}
++pos;
while (count-- > 0) {
- unsigned char label1 = ndata_[pos];
- unsigned char label2 = other.ndata_[pos];
+ uint8_t label1 = ndata_[pos];
+ uint8_t label2 = other.ndata_[pos];
if (maptolower[label1] != maptolower[label2]) {
return (false);
@@ -703,9 +703,9 @@ Name::reverse() const {
retname.ndata_.reserve(length_);
// Copy the original name, label by label, from tail to head.
- vector<unsigned char>::const_reverse_iterator rit0 = offsets_.rbegin();
- vector<unsigned char>::const_reverse_iterator rit1 = rit0 + 1;
- string::const_iterator n0 = ndata_.begin();
+ NameOffsets::const_reverse_iterator rit0 = offsets_.rbegin();
+ NameOffsets::const_reverse_iterator rit1 = rit0 + 1;
+ NameString::const_iterator n0 = ndata_.begin();
retname.offsets_.push_back(0);
while (rit1 != offsets_.rend()) {
retname.ndata_.append(n0 + *rit1, n0 + *rit0);
@@ -786,7 +786,7 @@ Name::downcase() {
while (count > 0) {
ndata_.at(pos) =
- maptolower[static_cast<unsigned char>(ndata_.at(pos))];
+ maptolower[ndata_.at(pos)];
++pos;
--nlen;
--count;
diff --git a/src/lib/dns/name.h b/src/lib/dns/name.h
index 508921452c..6cfa5460be 100644
--- a/src/lib/dns/name.h
+++ b/src/lib/dns/name.h
@@ -230,6 +230,11 @@ class Name {
///
//@{
private:
+ /// \brief Name data string
+ typedef std::basic_string<uint8_t> NameString;
+ /// \brief Name offsets type
+ typedef std::vector<uint8_t> NameOffsets;
+
/// The default constructor
///
/// This is used internally in the class implementation, but at least at
@@ -710,8 +715,8 @@ public:
//@}
private:
- std::string ndata_;
- std::vector<unsigned char> offsets_;
+ NameString ndata_;
+ NameOffsets offsets_;
unsigned int length_;
unsigned int labelcount_;
};
diff --git a/src/lib/dns/name_internal.h b/src/lib/dns/name_internal.h
index e1eab8ce39..8595df14a0 100644
--- a/src/lib/dns/name_internal.h
+++ b/src/lib/dns/name_internal.h
@@ -31,7 +31,7 @@ namespace isc {
namespace dns {
namespace name {
namespace internal {
-extern const unsigned char maptolower[];
+extern const uint8_t maptolower[];
} // end of internal
} // end of name
} // end of dns
diff --git a/src/lib/dns/tests/labelsequence_unittest.cc b/src/lib/dns/tests/labelsequence_unittest.cc
index 228439acbb..d1671a250f 100644
--- a/src/lib/dns/tests/labelsequence_unittest.cc
+++ b/src/lib/dns/tests/labelsequence_unittest.cc
@@ -34,14 +34,21 @@ public:
n3("example.org"), n4("foo.bar.test.example"),
n5("example.ORG"), n6("ExAmPlE.org"),
n7("."), n8("foo.example.org.bar"),
+ n9("\\000xample.org"),
+ n10("\\000xample.org"),
+ n11("\\000xample.com"),
+ n12("\\000xamplE.com"),
ls1(n1), ls2(n2), ls3(n3), ls4(n4), ls5(n5),
- ls6(n6), ls7(n7), ls8(n8)
+ ls6(n6), ls7(n7), ls8(n8),
+ ls9(n9), ls10(n10), ls11(n11), ls12(n12)
{};
// Need to keep names in scope for at least the lifetime of
// the labelsequences
Name n1, n2, n3, n4, n5, n6, n7, n8;
+ Name n9, n10, n11, n12;
LabelSequence ls1, ls2, ls3, ls4, ls5, ls6, ls7, ls8;
+ LabelSequence ls9, ls10, ls11, ls12;
};
// Basic equality tests
@@ -81,6 +88,11 @@ TEST_F(LabelSequenceTest, equals_sensitive) {
EXPECT_FALSE(ls5.equals(ls6, true));
EXPECT_FALSE(ls5.equals(ls7, true));
EXPECT_FALSE(ls5.equals(ls8, true));
+
+ EXPECT_TRUE(ls9.equals(ls10, true));
+ EXPECT_FALSE(ls9.equals(ls11, true));
+ EXPECT_FALSE(ls9.equals(ls12, true));
+ EXPECT_FALSE(ls11.equals(ls12, true));
}
TEST_F(LabelSequenceTest, equals_insensitive) {
@@ -123,6 +135,11 @@ TEST_F(LabelSequenceTest, equals_insensitive) {
EXPECT_TRUE(ls5.equals(ls5));
EXPECT_TRUE(ls5.equals(ls6));
EXPECT_FALSE(ls5.equals(ls7));
+
+ EXPECT_TRUE(ls9.equals(ls10));
+ EXPECT_FALSE(ls9.equals(ls11));
+ EXPECT_FALSE(ls9.equals(ls12));
+ EXPECT_TRUE(ls11.equals(ls12));
}
// Compare tests
@@ -343,25 +360,36 @@ TEST_F(LabelSequenceTest, compare) {
}
void
-getDataCheck(const char* expected_data, size_t expected_len,
+getDataCheck(const uint8_t* expected_data, size_t expected_len,
const LabelSequence& ls)
{
size_t len;
- const char* data = ls.getData(&len);
+ const uint8_t* data = ls.getData(&len);
ASSERT_EQ(expected_len, len) << "Expected data: " << expected_data <<
" name: " << ls.getName().toText();
EXPECT_EQ(expected_len, ls.getDataLength()) <<
"Expected data: " << expected_data <<
" name: " << ls.getName().toText();
for (size_t i = 0; i < len; ++i) {
- EXPECT_EQ(expected_data[i], data[i]) << "Difference at pos " << i <<
- ": Expected data: " <<
- expected_data <<
- " name: " <<
- ls.getName().toText();;
+ EXPECT_EQ(expected_data[i], data[i]) <<
+ "Difference at pos " << i << ": Expected data: " << expected_data <<
+ " name: " << ls.getName().toText();;
}
}
+// Convenient data converter for expected data. Label data must be of
+// uint8_t*, while it's convenient if we can specify some test data in
+// plain string (which is of char*). This wrapper converts the latter to
+// the former in a safer way.
+void
+getDataCheck(const char* expected_char_data, size_t expected_len,
+ const LabelSequence& ls)
+{
+ const vector<uint8_t> expected_data(expected_char_data,
+ expected_char_data + expected_len);
+ getDataCheck(&expected_data[0], expected_len, ls);
+}
+
TEST_F(LabelSequenceTest, getData) {
getDataCheck("\007example\003org\000", 13, ls1);
getDataCheck("\007example\003com\000", 13, ls2);
@@ -460,7 +488,7 @@ TEST_F(LabelSequenceTest, comparePart) {
// Data comparison
size_t len;
- const char* data = ls1.getData(&len);
+ const uint8_t* data = ls1.getData(&len);
getDataCheck(data, len, ls8);
}
diff --git a/src/lib/python/isc/notify/notify_out.py b/src/lib/python/isc/notify/notify_out.py
index 83ac1d0014..34db14c5c3 100644
--- a/src/lib/python/isc/notify/notify_out.py
+++ b/src/lib/python/isc/notify/notify_out.py
@@ -161,6 +161,12 @@ class NotifyOut:
for item in slaves:
self._notify_infos[zone_id].notify_slaves.append((item, 53))
+ def add_slave(self, address, port):
+ for zone_name, zone_class in sqlite3_ds.get_zones_info(self._db_file):
+ zone_id = (zone_name, zone_class)
+ if zone_id in self._notify_infos:
+ self._notify_infos[zone_id].notify_slaves.append((address, port))
+
def send_notify(self, zone_name, zone_class='IN'):
'''Send notify to one zone's slaves, this function is
the only interface for class NotifyOut which can be called
diff --git a/tests/lettuce/configurations/xfrin/inmem_slave.conf b/tests/lettuce/configurations/xfrin/inmem_slave.conf
index a6d88ee013..9b693005da 100644
--- a/tests/lettuce/configurations/xfrin/inmem_slave.conf
+++ b/tests/lettuce/configurations/xfrin/inmem_slave.conf
@@ -19,8 +19,8 @@
} ]
} ],
"listen_on": [ {
- "port": 47806,
- "address": "127.0.0.1"
+ "address": "::1",
+ "port": 47806
} ]
},
"Boss": {
diff --git a/tests/lettuce/configurations/xfrin/retransfer_master.conf b/tests/lettuce/configurations/xfrin/retransfer_master.conf
index eae47a6f84..378cff9977 100644
--- a/tests/lettuce/configurations/xfrin/retransfer_master.conf
+++ b/tests/lettuce/configurations/xfrin/retransfer_master.conf
@@ -10,13 +10,17 @@
"Auth": {
"database_file": "data/example.org.sqlite3",
"listen_on": [ {
- "port": 47807,
- "address": "::1"
+ "address": "::1",
+ "port": 47807
} ]
},
"Xfrout": {
"zone_config": [ {
"origin": "example.org"
+ } ],
+ "also_notify": [ {
+ "address": "::1",
+ "port": 47806
} ]
},
"Boss": {
diff --git a/tests/lettuce/configurations/xfrin/retransfer_slave.conf b/tests/lettuce/configurations/xfrin/retransfer_slave.conf
index 2296b8f9a3..260147dfbe 100644
--- a/tests/lettuce/configurations/xfrin/retransfer_slave.conf
+++ b/tests/lettuce/configurations/xfrin/retransfer_slave.conf
@@ -10,8 +10,8 @@
"Auth": {
"database_file": "data/test_nonexistent_db.sqlite3",
"listen_on": [ {
- "port": 47806,
- "address": "127.0.0.1"
+ "address": "::1",
+ "port": 47806
} ]
},
"Boss": {
diff --git a/tests/lettuce/configurations/xfrin/retransfer_slave_notify.conf b/tests/lettuce/configurations/xfrin/retransfer_slave_notify.conf
new file mode 100644
index 0000000000..57244a49f8
--- /dev/null
+++ b/tests/lettuce/configurations/xfrin/retransfer_slave_notify.conf
@@ -0,0 +1,38 @@
+{
+ "version": 2,
+ "Logging": {
+ "loggers": [ {
+ "debuglevel": 99,
+ "severity": "DEBUG",
+ "name": "*"
+ } ]
+ },
+ "Auth": {
+ "database_file": "data/xfrin-notify.sqlite3",
+ "listen_on": [ {
+ "address": "::1",
+ "port": 47806
+ } ]
+ },
+ "Xfrin": {
+ "zones": [ {
+ "name": "example.org",
+ "master_addr": "::1",
+ "master_port": 47807
+ } ]
+ },
+ "Zonemgr": {
+ "secondary_zones": [ {
+ "name": "example.org",
+ "class": "IN"
+ } ]
+ },
+ "Boss": {
+ "components": {
+ "b10-auth": { "kind": "needed", "special": "auth" },
+ "b10-xfrin": { "address": "Xfrin", "kind": "dispensable" },
+ "b10-zonemgr": { "address": "Zonemgr", "kind": "dispensable" },
+ "b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
+ }
+ }
+}
diff --git a/tests/lettuce/data/.gitignore b/tests/lettuce/data/.gitignore
index 8c542001bd..888175e1b1 100644
--- a/tests/lettuce/data/.gitignore
+++ b/tests/lettuce/data/.gitignore
@@ -1,2 +1,3 @@
/inmem-xfrin.sqlite3
/test_nonexistent_db.sqlite3
+/xfrin-notify.sqlite3
diff --git a/tests/lettuce/data/example.org b/tests/lettuce/data/example.org
index 20a93bef50..77f8f53152 100644
--- a/tests/lettuce/data/example.org
+++ b/tests/lettuce/data/example.org
@@ -10,3 +10,4 @@ dname.example.org. 3600 IN DNAME dname.example.info.
dname2.foo.example.org. 3600 IN DNAME dname2.example.info.
ns1.example.org. 3600 IN A 192.0.2.3
ns2.example.org. 3600 IN A 192.0.2.4
+shell.example.org. 3600 IN SSHFP 2 1 123456789abcdef67890123456789abcdef67890
diff --git a/tests/lettuce/data/xfrin-notify.sqlite3.orig b/tests/lettuce/data/xfrin-notify.sqlite3.orig
new file mode 100644
index 0000000000..3c38322c08
--- /dev/null
+++ b/tests/lettuce/data/xfrin-notify.sqlite3.orig
Binary files differ
diff --git a/tests/lettuce/features/inmemory_over_sqlite3.feature b/tests/lettuce/features/inmemory_over_sqlite3.feature
index 2e48689564..851cacaa42 100644
--- a/tests/lettuce/features/inmemory_over_sqlite3.feature
+++ b/tests/lettuce/features/inmemory_over_sqlite3.feature
@@ -26,18 +26,18 @@ Feature: In-memory zone using SQLite3 backend
And wait for bind10 stderr message XFRIN_STARTED
And wait for bind10 stderr message ZONEMGR_STARTED
- A query for www.example.org should have rcode NOERROR
+ A query for www.example.org to [::1]:47806 should have rcode NOERROR
"""
www.example.org. 3600 IN A 192.0.2.63
"""
- A query for mail.example.org should have rcode NXDOMAIN
+ A query for mail.example.org to [::1]:47806 should have rcode NXDOMAIN
When I send bind10 the command Xfrin retransfer example.org IN ::1 47807
Then wait for new bind10 stderr message XFRIN_TRANSFER_SUCCESS not XFRIN_XFR_PROCESS_FAILURE
Then wait for new bind10 stderr message AUTH_LOAD_ZONE
- A query for www.example.org should have rcode NOERROR
+ A query for www.example.org to [::1]:47807 should have rcode NOERROR
The answer section of the last query response should be
"""
www.example.org. 3600 IN A 192.0.2.1
"""
- A query for mail.example.org should have rcode NOERROR
+ A query for mail.example.org to [::1]:47806 should have rcode NOERROR
diff --git a/tests/lettuce/features/queries.feature b/tests/lettuce/features/queries.feature
index de6f0fa4be..7ae22849a3 100644
--- a/tests/lettuce/features/queries.feature
+++ b/tests/lettuce/features/queries.feature
@@ -103,6 +103,7 @@ Feature: Querying feature
ns2.example.org. 3600 IN A 192.0.2.4
mail.example.org. 3600 IN A 192.0.2.10
"""
+
Scenario: Delegation query for unsigned child zone
Given I have bind10 running with configuration example.org.inmem.config
And wait for bind10 stderr message BIND10_STARTED_CC
@@ -122,3 +123,27 @@ Feature: Querying feature
"""
ns.sub.example.org. 3600 IN A 192.0.2.101
"""
+
+ Scenario: SSHFP query
+ # We are testing one more RR type for a normal successful case
+ Given I have bind10 running with configuration example.org.inmem.config
+ And wait for bind10 stderr message BIND10_STARTED_CC
+ And wait for bind10 stderr message CMDCTL_STARTED
+ And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+ bind10 module Auth should be running
+ And bind10 module Resolver should not be running
+ And bind10 module Xfrout should not be running
+ And bind10 module Zonemgr should not be running
+ And bind10 module Xfrin should not be running
+ And bind10 module Stats should not be running
+ And bind10 module StatsHttpd should not be running
+
+ A query for example.org type SSHFP should have rcode NOERROR
+ The last query response should have ancount 0
+ A query for shell.example.org type SSHFP should have rcode NOERROR
+ The last query response should have ancount 1
+ The answer section of the last query response should be
+ """
+ shell.example.org. 3600 IN SSHFP 2 1 123456789abcdef67890123456789abcdef67890
+ """
diff --git a/tests/lettuce/features/terrain/bind10_control.py b/tests/lettuce/features/terrain/bind10_control.py
index a08a887ca4..248c6ecf06 100644
--- a/tests/lettuce/features/terrain/bind10_control.py
+++ b/tests/lettuce/features/terrain/bind10_control.py
@@ -306,8 +306,8 @@ def config_remove_command(step, name, value, cmdctl_port):
"quit"]
run_bindctl(commands, cmdctl_port)
-@step('send bind10 the command (.+)(?: with cmdctl port (\d+))?')
-def send_command(step, command, cmdctl_port):
+@step('send bind10(?: with cmdctl port (\d+))? the command (.+)')
+def send_command(step, cmdctl_port, command):
"""
Run bindctl, send the given command, and exit bindctl.
Parameters:
diff --git a/tests/lettuce/features/terrain/terrain.py b/tests/lettuce/features/terrain/terrain.py
index a35d0de2b2..876e7cfd48 100644
--- a/tests/lettuce/features/terrain/terrain.py
+++ b/tests/lettuce/features/terrain/terrain.py
@@ -59,6 +59,8 @@ copylist = [
"configurations/ddns/noddns.config"],
["data/inmem-xfrin.sqlite3.orig",
"data/inmem-xfrin.sqlite3"],
+ ["data/xfrin-notify.sqlite3.orig",
+ "data/xfrin-notify.sqlite3"],
["data/ddns/example.org.sqlite3.orig",
"data/ddns/example.org.sqlite3"]
]
diff --git a/tests/lettuce/features/terrain/transfer.py b/tests/lettuce/features/terrain/transfer.py
index 4ba45b89be..eb4ddc0b72 100644
--- a/tests/lettuce/features/terrain/transfer.py
+++ b/tests/lettuce/features/terrain/transfer.py
@@ -67,11 +67,11 @@ def perform_axfr(step, zone_name, address, port):
Step definition:
An AXFR transfer of <zone_name> [from <address>:<port>]
- Address defaults to 127.0.0.1
+ Address defaults to ::1
Port defaults to 47806
"""
if address is None:
- address = "127.0.0.1"
+ address = "::1"
# convert [IPv6_addr] to IPv6_addr:
address = re.sub(r"\[(.+)\]", r"\1", address)
if port is None:
diff --git a/tests/lettuce/features/xfrin_bind10.feature b/tests/lettuce/features/xfrin_bind10.feature
index 27dfb83f5b..7a45ddd8b5 100644
--- a/tests/lettuce/features/xfrin_bind10.feature
+++ b/tests/lettuce/features/xfrin_bind10.feature
@@ -23,11 +23,11 @@ Feature: Xfrin
# Now we use the first step again to see if the file has been created
The file data/test_nonexistent_db.sqlite3 should exist
- A query for www.example.org should have rcode REFUSED
+ A query for www.example.org to [::1]:47806 should have rcode REFUSED
When I send bind10 the command Xfrin retransfer example.org IN ::1 47807
Then wait for new bind10 stderr message XFRIN_TRANSFER_SUCCESS not XFRIN_XFR_PROCESS_FAILURE
Then wait for new bind10 stderr message ZONEMGR_RECEIVE_XFRIN_SUCCESS
- A query for www.example.org should have rcode NOERROR
+ A query for www.example.org to [::1]:47806 should have rcode NOERROR
# The transferred zone should have 11 non-NSEC3 RRs and 1 NSEC3 RR.
# The following check will get these by AXFR, so the total # of RRs
diff --git a/tests/lettuce/features/xfrin_notify_handling.feature b/tests/lettuce/features/xfrin_notify_handling.feature
new file mode 100644
index 0000000000..e514b83c1d
--- /dev/null
+++ b/tests/lettuce/features/xfrin_notify_handling.feature
@@ -0,0 +1,29 @@
+Feature: Xfrin incoming notify handling
+ Tests for Xfrin incoming notify handling.
+
+ Scenario: Handle incoming notify
+ Given I have bind10 running with configuration xfrin/retransfer_master.conf with cmdctl port 47804 as master
+ And wait for master stderr message BIND10_STARTED_CC
+ And wait for master stderr message CMDCTL_STARTED
+ And wait for master stderr message AUTH_SERVER_STARTED
+ And wait for master stderr message XFROUT_STARTED
+ And wait for master stderr message ZONEMGR_STARTED
+
+ And I have bind10 running with configuration xfrin/retransfer_slave_notify.conf
+ And wait for bind10 stderr message BIND10_STARTED_CC
+ And wait for bind10 stderr message CMDCTL_STARTED
+ And wait for bind10 stderr message AUTH_SERVER_STARTED
+ And wait for bind10 stderr message XFRIN_STARTED
+ And wait for bind10 stderr message ZONEMGR_STARTED
+
+ A query for www.example.org to [::1]:47806 should have rcode NXDOMAIN
+
+ When I send bind10 with cmdctl port 47804 the command Xfrout notify example.org IN
+ Then wait for new master stderr message XFROUT_NOTIFY_COMMAND
+ Then wait for new bind10 stderr message AUTH_RECEIVED_NOTIFY
+ Then wait for new bind10 stderr message ZONEMGR_RECEIVE_NOTIFY
+ Then wait for new bind10 stderr message XFRIN_XFR_TRANSFER_STARTED
+ Then wait for new bind10 stderr message XFRIN_TRANSFER_SUCCESS not XFRIN_XFR_PROCESS_FAILURE
+ Then wait for new bind10 stderr message ZONEMGR_RECEIVE_XFRIN_SUCCESS
+
+ A query for www.example.org to [::1]:47806 should have rcode NOERROR