diff options
37 files changed, 515 insertions, 321 deletions
@@ -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> — 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> — 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> — - 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> — - 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> — - 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> — - 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> — + general tools and diagnostic clients. + </simpara> + </listitem> + <listitem> + <simpara> + <filename>etc/bind10-devel/</filename> — + configuration files. + </simpara> + </listitem> + <listitem> + <simpara> + <filename>lib/</filename> — + libraries and python modules. + </simpara> + </listitem> + <listitem> + <simpara> + <filename>libexec/bind10-devel/</filename> — + 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> — + commands used by the system administrator. + </simpara> + </listitem> + <listitem> + <simpara> + <filename>share/bind10-devel/</filename> — + configuration specifications. + </simpara> + </listitem> + <listitem> + <simpara> + <filename>share/doc/bind10-devel/</filename> — + this guide and other supplementary documentation. + </simpara> + </listitem> + <listitem> + <simpara> + <filename>share/man/</filename> — + manual pages (online documentation). + </simpara> + </listitem> + <listitem> + <simpara> + <filename>var/bind10-devel/</filename> — + 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> — - general tools and diagnostic clients. - </simpara> - </listitem> - <listitem> - <simpara> - <filename>etc/bind10-devel/</filename> — - configuration files. - </simpara> - </listitem> - <listitem> - <simpara> - <filename>lib/</filename> — - libraries and python modules. - </simpara> - </listitem> - <listitem> - <simpara> - <filename>libexec/bind10-devel/</filename> — - 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> — - commands used by the system administrator. - </simpara> - </listitem> - <listitem> - <simpara> - <filename>share/bind10-devel/</filename> — - configuration specifications. - </simpara> - </listitem> - <listitem> - <simpara> - <filename>share/man/</filename> — - manual pages (online documentation). - </simpara> - </listitem> - <listitem> - <simpara> - <filename>var/bind10-devel/</filename> — - 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>> <userinput>config add Boss/components b10-resolver</userinput> > <userinput>config set Boss/components/b10-resolver/special resolver</userinput> > <userinput>config set Boss/components/b10-resolver/kind needed</userinput> @@ -831,27 +865,32 @@ as a dependency earlier --> > <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 Binary files differnew file mode 100644 index 0000000000..3c38322c08 --- /dev/null +++ b/tests/lettuce/data/xfrin-notify.sqlite3.orig 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 |