Commit b5fbf8a4 authored by Mukund Sivaraman's avatar Mukund Sivaraman
Browse files

Merge branch 'master' into trac2023

parents 4286da12 34521105
448. [func] team
b10-ddns is now functional and handles dynamic update requests
per RFC 2136. See BIND 10 guide for configuration and operation
(Multiple Trac tickets)
447. [bug] jinmei
Fixed a bug in b10-xfrout where a helper thread could fall into
an infinite loop if b10-auth stops while the thread is waiting for
forwarded requests from b10-auth.
(Trac #988 and #1833, git 95a03bbefb559615f3f6e529d408b749964d390a)
446. [bug] muks
A number of warnings reported by Python about unclosed file and
socket objects were fixed. Some related code was also made safer.
(Trac #1828, git 464682a2180c672f1ed12d8a56fd0a5ab3eb96ed)
445. [bug]* jinmei
The pre-install check for older SQLite3 DB now refers to the DB
file with the prefix of DESTDIR. This ensures that 'make install'
......@@ -16,6 +16,26 @@ DISTCHECK_CONFIGURE_FLAGS = --disable-install-configurations
# Use same --with-gtest flag if set
.PHONY: check-valgrind check-valgrind-suppress
@VALGRIND_COMMAND="$(VALGRIND) -q --gen-suppressions=all --track-origins=yes --num-callers=48 --leak-check=full --fullpath-after=" \
make -C $(abs_top_builddir) check
@echo "*** Valgrind is required for check-valgrind ***"; exit 1;
@VALGRIND_COMMAND="$(VALGRIND) -q --gen-suppressions=all --error-exitcode=1 --suppressions=$(abs_top_srcdir)/src/valgrind-suppressions --suppressions=$(abs_top_srcdir)/src/valgrind-suppressions.revisit --num-callers=48 --leak-check=full --fullpath-after=" \
make -C $(abs_top_builddir) check
@echo "*** Valgrind is required for check-valgrind-suppress ***"; exit 1;
@if [ $(USE_LCOV) = yes ] ; then \
$(LCOV) --directory . --zerocounters; \
......@@ -982,6 +982,15 @@ AC_ARG_ENABLE(logger-checks, [AC_HELP_STRING([--enable-logger-checks],
AM_CONDITIONAL(ENABLE_LOGGER_CHECKS, test x$enable_logger_checks != xno)
# Check for valgrind
AC_PATH_PROG(VALGRIND, valgrind, no)
found_valgrind="not found"
if test "x$VALGRIND" != "xno"; then
......@@ -1292,6 +1301,7 @@ Features:
Google Tests: $gtest_path
Valgrind: $found_valgrind
C++ Code Coverage: $USE_LCOV
Python Code Coverage: $USE_PYCOVERAGE
Logger checks: $enable_logger_checks
EXTRA_DIST = bind10-guide.css
EXTRA_DIST += bind10-guide.xml bind10-guide.html bind10-guide.txt
EXTRA_DIST += bind10-messages.xml bind10-messages.html
dist_doc_DATA = bind10-guide.txt
dist_html_DATA = bind10-guide.css bind10-guide.html bind10-messages.html
EXTRA_DIST = bind10-guide.xml bind10-messages.xml
# This is not a "man" manual, but reuse this for now for docbook.
......@@ -87,9 +87,10 @@
<title>Supported Platforms</title>
BIND 10 builds have been tested on Debian GNU/Linux 5 and unstable,
Ubuntu 9.10, NetBSD 5, Solaris 10, FreeBSD 7 and 8, CentOS
Linux 5.3, and MacOS 10.6.
BIND 10 builds have been tested on (in no particular order)
Debian GNU/Linux 5 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.
It has been tested on Sparc, i386, and amd64 hardware
......@@ -127,11 +128,13 @@
The <command>b10-xfrin</command>, <command>b10-xfrout</command>,
and <command>b10-zonemgr</command> components require the
libpython3 library and the Python module
(which is included with Python).
The Python module needs to be built for the corresponding Python 3.
The <command>b10-ddns</command>, <command>b10-xfrin</command>,
<command>b10-xfrout</command>, and <command>b10-zonemgr</command>
components require the libpython3 library and the Python module (which is included with Python).
The <command>b10-stats-httpd</command> component uses the
Python module.
The Python modules need to be built for the corresponding Python 3.
<!-- TODO: this will change ... -->
......@@ -194,6 +197,16 @@
<command>b10-ddns</command> &mdash;
Dynamic DNS update service.
This process is used to handle incoming DNS update
requests to allow granted clients to update zones
for which BIND 10 is serving as a primary server.
<command>b10-msgq</command> &mdash;
......@@ -1831,7 +1844,6 @@ what if a NOTIFY is sent?
<chapter id="xfrout">
<title>Outbound Zone Transfers</title>
The <command>b10-xfrout</command> process is started by
......@@ -1907,6 +1919,325 @@ what is XfroutClient xfr_client??
<chapter id="ddns">
<title>Dynamic DNS Update</title>
BIND 10 supports the server side of the Dynamic DNS Update
(DDNS) protocol as defined in RFC 2136.
This service is provided by the <command>b10-ddns</command>
component, which is started by the <command>bind10</command>
process if configured so.
When the <command>b10-auth</command> authoritative DNS server
receives an UPDATE request, it internally forwards the request
to <command>b10-ddns</command>, which handles the rest of
request processing.
When the processing is completed <command>b10-ddns</command>
will send a response to the client with the RCODE set to the
value as specified in RFC 2136 (NOERROR for successful update,
REFUSED if rejected due to ACL check, etc).
If the zone has been changed as a result, it will internally
notify <command>b10-xfrout</command> so that other secondary
servers will be notified via the DNS notify protocol.
In addition, if <command>b10-auth</command> serves the updated
zone from its in-memory cache (as described in
<xref linkend="in-memory-datasource-with-sqlite3-backend" />),
<command>b10-ddns</command> will also
notify <command>b10-auth</command> so that <command>b10-auth</command>
will re-cache the updated zone content.
The <command>b10-ddns</command> component supports requests over
both UDP and TCP, and both IPv6 and IPv4; for TCP requests,
however, it terminates the TCP connection immediately after
each single request has been processed. Clients cannot reuse the
same TCP connection for multiple requests. (This is a current
implementation limitation of <command>b10-ddns</command>.
While RFC 2136 doesn't specify anything about such reuse of TCP
connection, there is no reason for disallowing it as RFC 1035
generally allows multiple requests sent over a single TCP
connection. BIND 9 supports such reuse.)
As of this writing <command>b10-ddns</command> does not support
update forwarding for secondary zones.
If it receives an update request for a secondary zone, it will
immediately return a response with an RCODE of NOTIMP.
For feature completeness update forwarding should be
eventually supported. But right now it's considered a lower
priority task and there is no specific plan of implementing
this feature.
<!-- See Trac #2063 -->
<title>Enabling Dynamic Update</title>
First off, it must be made sure that a few components on which
<command>b10-ddns</command> depends are configured to run,
which are <command>b10-auth</command>
and <command>b10-zonemgr</command>.
In addition, <command>b10-xfrout</command> should also be
configured to run; otherwise the notification after an update
(see above) will fail with a timeout, suspending the DDNS
service while <command>b10-ddns</command> waits for the
response (see the description of the <ulink
log message for further details).
If BIND 10 is already configured to provide authoritative DNS
service they should normally be configured to run already.
Second, for the obvious reason dynamic update requires that the
underlying data source storing the zone data be writable.
In the current implementation this means the zone must be stored
in an SQLite3-based data source.
Also, right now, the <command>b10-ddns</command> component
configures itself with the data source referring to the
<quote>database_file</quote> configuration parameter of
So this information must be configured correctly before starting
The way to configure data sources is now being revised.
Configuration on the data source for DDNS will be very
likely to be changed in a backward incompatible manner in
a near future version.
In general, if something goes wrong regarding the dependency
described above, <command>b10-ddns</command> will log the
related event at the warning or error level.
It's advisable to check the log message when you first enable
DDNS or if it doesn't work as you expect to see if there's any
warning or error log message.
Next, to enable the DDNS service, <command>b10-ddns</command>
needs to be explicitly configured to run.
It can be done by using the <command>bindctl</command>
utility. For example:
&gt; <userinput>config add Boss/components b10-ddns</userinput>
&gt; <userinput>config set Boss/components/b10-ddns/address DDNS</userinput>
&gt; <userinput>config set Boss/components/b10-ddns/kind dispensable</userinput>
&gt; <userinput>config commit</userinput>
In theory "kind" could be omitted because "dispensable" is its
default. But there's some peculiar behavior (which should
be a bug and should be fixed eventually; see Trac ticket
#2064) with bindctl and you'll still need to specify that explicitly.
Likewise, "address" may look unnecessary because
<command>b10-ddns</command> would start and work without
specifying it. But for it to shutdown gracefully this
parameter should also be specified.
<title>Access Control</title>
By default <command>b10-ddns</command> rejects any update
requests from any clients by returning a response with an RCODE
To allow updates to take effect, an access control rule
(called update ACL) with a policy allowing updates must explicitly be
Update ACL must be configured per zone basis in the
<quote>zones</quote> configuration parameter of
This is a list of per-zone configuration regarding DDNS.
Each list element consists of the following parameters:
<simpara>The zone's origin name</simpara>
<simpara>The RR class of the zone
(normally <quote>IN</quote>, and in that case
can be omitted in configuration)</simpara>
<simpara>List of access control rules (ACL) for the zone</simpara>
The syntax of the ACL is the same as ACLs for other
Specific examples are given below.
In general, an update ACL rule that allows an update request
should be configured with a TSIG key.
This is an example update ACL that allows updates to the zone
named <quote></quote> of RR class <quote>IN</quote>
from clients that send requests signed with a TSIG whose
key name is "" (and refuses all others):
&gt; <userinput>config add DDNS/zones</userinput>
&gt; <userinput>config set DDNS/zones[0]/origin</userinput>
&gt; <userinput>config set DDNS/zones[0]/class IN</userinput>
(Note: "class" can be omitted)
&gt; <userinput>config add DDNS/zones[0]/update_acl {"action": "ACCEPT", "key": ""}</userinput>
&gt; <userinput>config commit</userinput>
The TSIG key must be configured system wide
(see <xref linkend="xfrout"/>.)
Multiple rules can be specified in the ACL, and an ACL rule
can consist of multiple constraints, such as a combination of
IP address and TSIG.
The following configuration sequence will add a new rule to
the ACL created in the above example. This additional rule
allows update requests sent from a client
using TSIG key name of "key.example" (different from the
key used in the previous example) and has an IPv6 address of ::1.
&gt; <userinput>config add DDNS/zones[0]/update_acl {"action": "ACCEPT", "from": "::1", "key": "key.example"}</userinput>
&gt; <userinput>config show DDNS/zones[0]/update_acl</userinput>
DDNS/zones[0]/update_acl[0] {"action": "ACCEPT", "key": ""} any (modified)
DDNS/zones[0]/update_acl[1] {"action": "ACCEPT", "from": "::1", "key": "key.example"} any (modified)
&gt; <userinput>config commit</userinput>
(Note the "add" in the first line. Before this sequence, we
have had only entry in zones[0]/update_acl. The "add" command
with a value (rule) adds a new entry and sets it to the given rule.
Due to a limitation of the current implementation, it doesn't
work if you first try to just add a new entry and then set it to
a given rule).
The <command>b10-ddns</command> component accepts an ACL
rule that just allows updates from a specific IP address
(i.e., without requiring TSIG), but this is highly
discouraged (remember that requests can be made over UDP and
spoofing the source address of a UDP packet is often pretty
Unless you know what you are doing and that you can accept
its consequence, any update ACL rule that allows updates
should have a TSIG key in its constraints.
The ACL rules will be checked in the listed order, and the
first matching one will apply.
If none of the rules matches, the default rule will apply,
which is rejecting any requests in the case of
Other actions than "ACCEPT", namely "REJECT" and "DROP", can be
used, too.
See <xref linkend="resolverserver"/> about their effects.
Currently update ACL can only control updates per zone basis;
it's not possible to specify access control with higher
granularity such as for particular domain names or specific
types of RRs.
<!-- See Trac ticket #2065 -->
Contrary to what RFC 2136 (literally) specifies,
<command>b10-ddns</command> checks the update ACL before
checking the prerequisites of the update request.
This is a deliberate implementation decision.
This counter intuitive specification has been repeatedly
discussed among implementers and in the IETF, and it is now
widely agreed that it does not make sense to strictly follow
that part of RFC.
One known specific bad result of following the RFC is that it
could leak information about which name or record exists or does not
exist in the zone as a result of prerequisite checks even if a
zone is somehow configured to reject normal queries from
arbitrary clients.
There have been other troubles that could have been avoided if
the ACL could be checked before the prerequisite check.
<title>Miscellaneous Operational Issues</title>
Unlike BIND 9, BIND 10 currently does not support automatic
resigning of DNSSEC-signed zone when it's updated via DDNS.
It could be possible to resign the updated zone afterwards
or make sure the update request also updates related DNSSEC
records, but that will be pretty error-prone operation.
In general, it's not advisable to allow DDNS for a signed zone
at this moment.
Also unlike BIND 9, it's currently not possible
to <quote>freeze</quote> a zone temporarily in order to
suspend DDNS while you manually update the zone.
If you need to make manual updates to a dynamic zone,
you'll need to temporarily reject any updates to the zone via
the update ACLs.
Dynamic updates are only applicable to primary zones.
In order to avoid updating secondary zones via DDNS requests,
<command>b10-ddns</command> refers to the
<quote>secondary_zones</quote> configuration of
<command>b10-zonemgr</command>. Zones listed in
<quote>secondary_zones</quote> will never be updated via DDNS
regardless of the update ACL configuration;
<command>b10-ddns</command> will return a response with an
RCODE of NOTAUTH as specified in RFC 2136.
If you have a "conceptual" secondary zone whose content is a
copy of some external source but is not updated via the
standard zone transfers and therefore not listed in
<quote>secondary_zones</quote>, be careful not to allow DDNS
for the zone; it would be quite likely to lead to inconsistent
state between different servers.
Normally this should not be a problem because the default
update ACL rejects any update requests, but you may want to
take an extra care about the configuration if you have such
type of secondary zones.
The difference of two versions of a zone, before and after a
DDNS transaction, is automatically recorded in the underlying
data source, and can be retrieved in the form of outbound
This is done automatically; it does not require specific
configuration to make this possible.
<chapter id="resolverserver">
<title>Recursive Name Server</title>
SUBDIRS = lib bin
cppcheck-suppress.lst \
valgrind-suppressions \
......@@ -19,6 +19,9 @@ endif
CLEANFILES = *.gcno *.gcda
libtool --mode=execute $(VALGRIND_COMMAND)
# Do not define global tests, use check-local so
# environment can be set (needed for dynamic loading)
......@@ -160,6 +160,11 @@ The boss module is sending a SIGKILL signal to the given process.
% BIND10_SEND_SIGTERM sending SIGTERM to %1 (PID %2)
The boss module is sending a SIGTERM signal to the given process.
% BIND10_SETGID setting GID to %1
The boss switches the process group ID to the given value. This happens
when BIND 10 starts with the -u option, and the group ID will be set to
that of the specified user.
% BIND10_SETUID setting UID to %1
The boss switches the user it runs as to the given UID.
......@@ -169,8 +169,8 @@ class BoB:
def __init__(self, msgq_socket_file=None, data_path=None,
config_filename=None, clear_config=False, nocache=False,
verbose=False, nokill=False, setuid=None, username=None,
cmdctl_port=None, wait_time=10):
verbose=False, nokill=False, setuid=None, setgid=None,
username=None, cmdctl_port=None, wait_time=10):
Initialize the Boss of BIND. This is a singleton (only one can run).
......@@ -208,6 +208,7 @@ class BoB:
self.components_to_restart = []
self.runnable = False
self.uid = setuid
self.gid = setgid
self.username = username
self.verbose = verbose
self.nokill = nokill
......@@ -1156,12 +1157,14 @@ def main():
# Check user ID.
setuid = None
setgid = None
username = None
if options.user:
# Try getting information about the user, assuming UID passed.
pw_ent = pwd.getpwuid(int(options.user))
setuid = pw_ent.pw_uid
setgid = pw_ent.pw_gid
username = pw_ent.pw_name
except ValueError:
......@@ -1175,6 +1178,7 @@ def main():
pw_ent = pwd.getpwnam(options.user)
setuid = pw_ent.pw_uid
setgid = pw_ent.pw_gid
username = pw_ent.pw_name
except KeyError:
......@@ -1205,7 +1209,7 @@ def main():
boss_of_bind = BoB(options.msgq_socket_file, options.data_path,
options.config_file, options.clear_config,
options.nocache, options.verbose, options.nokill,
setuid, username, options.cmdctl_port,
setuid, setgid, username, options.cmdctl_port,
startup_result = boss_of_bind.startup()
if startup_result:
......@@ -1055,22 +1055,29 @@ class TestPIDFile(unittest.TestCase):
# dump PID to the file, and confirm the content is correct
my_pid = os.getpid()
self.assertEqual(my_pid, int(open(self.pid_file, "r").read()))
with open(self.pid_file, "r") as f:
self.assertEqual(my_pid, int(
def test_dump_pid(self):
# make sure any existing content will be removed
open(self.pid_file, "w").write('dummy data\n')
with open(self.pid_file, "w") as f:
f.write('dummy data\n')
def test_unlink_pid_file_notexist(self):
dummy_data = 'dummy_data\n'
open(self.pid_file, "w").write(dummy_data)
with open(self.pid_file, "w") as f:
# the file specified for unlink_pid_file doesn't exist,
# and the original content of the file should be intact.
self.assertEqual(dummy_data, open(self.pid_file, "r").read())
with open(self.pid_file, "r") as f:
def test_dump_pid_with_none(self):
# Check the behavior of dump_pid() and unlink_pid_file() with None.
......@@ -1079,9 +1086,14 @@ class TestPIDFile(unittest.TestCase):
dummy_data = 'dummy_data\n'
open(self.pid_file, "w").write(dummy_data)
with open(self.pid_file, "w") as f:
self.assertEqual(dummy_data, open(self.pid_file, "r").read())
with open(self.pid_file, "r") as f:
def test_dump_pid_failure(self):
# the attempt to open file will fail, which should result in exception.
......@@ -425,6 +425,12 @@ class FakeBindCmdInterpreter(bindcmd.BindCmdInterpreter):
class TestBindCmdInterpreter(unittest.TestCase):
def setUp(self):
self.old_stdout = sys.stdout
def tearDown(self):
sys.stdout = self.old_stdout
def _create_invalid_csv_file(self, csvfilename):
import csv
csvfile = open(csvfilename, 'w')
......@@ -447,19 +453,17 @@ class TestBindCmdInterpreter(unittest.TestCase):
self.assertEqual(new_csv_dir, custom_cmd.csv_file_dir)
def test_get_saved_user_info(self):
old_stdout = sys.stdout
sys.stdout = open(os.devnull, 'w')
cmd = bindcmd.BindCmdInterpreter()
users = cmd._get_saved_user_info('/notexist', 'csv_file.csv')
self.assertEqual([], users)
csvfilename = 'csv_file.csv'
users = cmd._get_saved_user_info('./', csvfilename)
self.assertEqual([], users)
sys.stdout = old_stdout
with open(os.devnull, 'w') as f:
sys.stdout = f
cmd = bindcmd.BindCmdInterpreter()
users = cmd._get_saved_user_info('/notexist', 'csv_file.csv')