2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-09-04 07:55:18 +00:00

[trac749] Merge 'master' into trac749

Conflicts:
	src/bin/auth/auth_srv.h
	src/bin/resolver/resolver.h
	src/lib/Makefile.am
	src/lib/asiodns/dns_lookup.h
	src/lib/asiodns/io_fetch.cc
	src/lib/asiodns/io_fetch.h
	src/lib/asiodns/qid_gen.cc
	src/lib/asiodns/qid_gen.h
	src/lib/asiodns/tcp_server.cc
	src/lib/asiodns/tests/qid_gen_unittest.cc
	src/lib/asiodns/udp_server.cc
	src/lib/asiolink/Makefile.am
	src/lib/asiolink/qid_gen.cc
	src/lib/asiolink/qid_gen.h
	src/lib/asiolink/tests/Makefile.am
	src/lib/asiolink/tests/qid_gen_unittest.cc
	src/lib/asiolink/tests/tcp_socket_unittest.cc
	src/lib/asiolink/tests/udp_socket_unittest.cc
	src/lib/dns/rdata.h
	src/lib/resolve/recursive_query.cc
	src/lib/resolve/recursive_query.h
	src/lib/resolve/tests/recursive_query_unittest_2.cc
	src/lib/testutils/srv_test.cc
	src/lib/util/Makefile.am
	src/lib/util/io_utilities.h
	src/lib/util/random/qid_gen.cc
	src/lib/util/random/qid_gen.h
	src/lib/util/tests/io_utilities_unittest.cc
	src/lib/util/tests/qid_gen_unittest.cc
This commit is contained in:
chenzhengzhang
2011-04-19 13:56:30 +08:00
219 changed files with 16434 additions and 1058 deletions

168
ChangeLog
View File

@@ -1,8 +1,71 @@
220. [func] stephen
Added the 'badpacket' program for testing; it sends a set of
(potentially) bad packets to a nameserver and prints the responses.
(Trac #703, git 1b666838b6c0fe265522b30971e878d9f0d21fde)
219. [func] ocean
src/lib: move some dns related code out of asiolink library to
asiodns library
(Trac #751, git 262ac6c6fc61224d54705ed4c700dadb606fcb1c)
218. [func] jinmei
src/lib/dns: added support for RP RDATA.
(Trac #806, git 4e47d5f6b692c63c907af6681a75024450884a88)
217. [bug] jerry
src/lib/dns/python: Use a signed version of larger size of integer and
perform more strict range checks with PyArg_ParseTuple() in case of
overflows.
(Trac #363, git ce281e646be9f0f273229d94ccd75bf7e08d17cf)
216. [func] vorner
The BIND10_XFROUT_SOCKET_FILE environment variable can be
used to specify which socket should be used for communication
between b10-auth and b10-xfrout. Mostly for testing reasons.
(Trac #615, git 28b01ad5bf72472c824a7b8fc4a8dc394e22e462)
215. [func] vorner
A new process, b10-sockcreator, is added, which will create
sockets for the rest of the system. It is the only part
which will need to keep the root privileges. However, only
the process exists, nothing can talk to it yet.
(Trac #366, git b509cbb77d31e388df68dfe52709d6edef93df3f)
214. [func]* vorner
Zone manager no longer thinks it is secondary master for
all zones in the database. They are listed in
Zonemgr/secondary_zones configuration variable (in the form
[{"name": "example.com", "class": "IN"}]).
(Trac #670, git 7c1e4d5e1e28e556b1d10a8df8d9486971a3f052)
213. [bug] naokikambe
Solved incorrect datetime of "bind10.boot_time" and also
added a new command "sendstats" for Bob. This command is
to send statistics data to the stats daemon immediately.
The solved problem is that statistics data doesn't surely
reach to the daemon because Bob sent statistics data to
the daemon while it is starting. So the daemon invokes the
command for Bob after it starts up. This command is also
useful for resending statistics data via bindctl manually.
(Trac #521, git 1c269cbdc76f5dc2baeb43387c4d7ccc6dc863d2)
212. [bug] naokikambe
Fixed that the ModuleCCSession object may group_unsubscribe in the
closed CC session in being deleted.
(Trac #698, git 0355bddc92f6df66ef50b920edd6ec3b27920d61)
211. [func] shane
Implement "--brittle" option, which causes the server to exit
if any of BIND 10's processes dies.
(Trac #788, git 88c0d241fe05e5ea91b10f046f307177cc2f5bc5)
210. [bug] jerry
src/bin/auth: fixed a bug where type ANY queries don't provide
additional glue records for ANSWER section.
(Trac #699, git 510924ebc57def8085cc0e5413deda990b2abeee)
bind10-devel-20110322 released on March 22, 2011
209. [func] jelte
Resolver now uses the NSAS when looking for a nameserver to
query for any specific zone. This also includes keeping track of
@@ -200,7 +263,7 @@ bind10-devel-20110224 released on February 24, 2011
indicates truncation.
(Trac #554, git 9739cbce2eaffc7e80640db58a8513295cf684de)
176. [func] zhang likun
176. [func] likun
src/lib/cache: Rename one interface: from lookupClosestRRset()
to lookupDeepestNS(), and remove one parameter of it.
(Trac #492, git ecbfb7cf929d62a018dd4cdc7a841add3d5a35ae)
@@ -231,12 +294,12 @@ bind10-devel-20110224 released on February 24, 2011
and internal help.
(Trac #384, git e5fb3bc1ed5f3c0aec6eb40a16c63f3d0fc6a7b2)
171. [func] feng, jerry, jinmei, vorner
171. [func] vorner
b10-auth, src/lib/datasrc: in memory data source now works as a
complete data source for authoritative DNS servers and b10-auth
uses it. It still misses major features, however, including
DNSSEC support and zone transfer.
(Last trac #553, but many more,
(Last Trac #553, but many more,
git 6f031a09a248e7684723c000f3e8cc981dcdb349)
170. [bug] jinmei
@@ -246,7 +309,7 @@ bind10-devel-20110224 released on February 24, 2011
correctly rejected.
(Trac #117, git 9c690982f24fef19c747a72f43c4298333a58f48)
169. [func] zhang likun, jelte
169. [func] jelte
Added a basic implementation for a resolver cache (though not
used yet).
(Trac #449, git 8aa3b2246ae095bbe7f855fd11656ae3bdb98986)
@@ -259,7 +322,7 @@ bind10-devel-20110224 released on February 24, 2011
167. [bug] naokikambe
Fixed failure of termination of msgq_test.py with python3
coverage(3.3.1)
coverage (3.3.1).
(Trac #573, git 0e6a18e12f61cc482e07078776234f32605312e5)
166. [func] jelte
@@ -366,10 +429,11 @@ bind10-devel-20110120 released on January 20, 2011
151. [bug] smann
lib/log/dummylog.h:
lib/log/dummylog.cc: Modify dlog so that it takes an optional 2nd
argument of type bool (true or false). This flag, if set, will cause
the message to be printed whether or not -v is chosen.
(trac #432, git 880220478c3e8702d56d761b1e0b21b77d08ee5a)
lib/log/dummylog.cc: Modify dlog so that it takes an optional
2nd argument of type bool (true or false). This flag, if
set, will cause the message to be printed whether or not
-v is chosen.
(Trac #432, git 880220478c3e8702d56d761b1e0b21b77d08ee5a)
150. [bug] jelte
b10-cfgmgr: No longer save the configuration on exit. Configuration
@@ -382,7 +446,7 @@ bind10-devel-20110120 released on January 20, 2011
bindctl: Check if the user session has disappeared (either by a
timeout or by a server restart), and reauthenticate if so. This
fixes the 'cmdctl not running' problem.
(trac #431, git b929be82fec5f92e115d8985552f84b4fdd385b9)
(Trac #431, git b929be82fec5f92e115d8985552f84b4fdd385b9)
148. [func] jelte
bindctl: Command results are now pretty-printed (i.e. printed in
@@ -505,7 +569,7 @@ bind10-devel-20110120 released on January 20, 2011
It has "listen_on" and "forward_addresses" options.
(Trac #389, r3448)
131. [func] feng, jerry
131. [func] jerry
src/lib/datasrc: Introduced two template classes RBTree and RBNode
to provide the generic map with domain name as key and anything as
the value. Because of some unresolved design issue, the new classes
@@ -516,8 +580,8 @@ bind10-devel-20110120 released on January 20, 2011
src/lib/datasrc: Introduced a new class MemoryDataSrc to provide
the general interface for memory data source. For the initial
implementation, we don't make it a derived class of AbstractDataSrc
because the interface is so different(we'll eventually consider this
as part of the generalization work).
because the interface is so different (we'll eventually
consider this as part of the generalization work).
(Trac #422, svn r3866)
129. [func] jinmei
@@ -537,7 +601,7 @@ bind10-devel-20110120 released on January 20, 2011
are now output regardless of the state of the verbose flag.
(Trac #229, svn r3828)
126. [func] stephen, vorner, ocean
126. [func] ocean
The Nameserver Address Store (NSAS) component has been added. It takes
care of choosing an IP address of a nameserver when a zone needs to be
contacted.
@@ -548,7 +612,7 @@ bind10-devel-20101201 released on December 01, 2010
125. [func] jelte
Added support for addressing individual list items in bindctl
configuration commands; If you have an element that is a list, you
can use foo[X] to address a specific item, where X is an integer
can use foo[X] integer
(starting at 0)
(Trac #405, svn r3739)
@@ -629,14 +693,14 @@ bind10-devel-20101201 released on December 01, 2010
every poll_interval seconds, one socketpair is used to wake up
the waiting server. (Trac #352, svn r3366)
111. [bug]* zhanglikun, Michal Vaner
111. [bug]* Vaner
Make sure process xfrin/xfrout/zonemgr/cmdctl can be stopped
properly when user enter "ctrl+c" or 'Boss shutdown' command
through bindctl. The ZonemgrRefresh.run_timer and
NotifyOut.dispatcher spawn a thread themselves.
(Trac #335, svn r3273)
110. [func] Michal Vaner
110. [func] Vaner
Added isc.net.check module to check ip addresses and ports for
correctness and isc.net.addr to hold IP address. The bind10, xfrin
and cmdctl programs are modified to use it.
@@ -654,17 +718,17 @@ bind10-devel-20101201 released on December 01, 2010
lowerbound_refresh, lowerbound_retry, max_transfer_timeout and
jitter_scope. (Trac #340, r3205)
107. [func] zhang likun
107. [func] likun
Remove the parameter 'db_file' for command 'retransfer' of
xfrin module. xfrin.spec will not be generated by script.
(Trac #329, r3171)
106. [bug] zhang likun
106. [bug] likun
When xfrin can't connect with one zone's master, it should tell
the bad news to zonemgr, so that zonemgr can reset the timer for
that zone. (Trac #329, r3170)
105. [bug] Michal Vaner
105. [bug] Vaner
Python processes: they no longer take 100% CPU while idle
due to a busy loop in reading command session in a nonblocking way.
(Trac #349, svn r3153), (Trac #382, svn r3294)
@@ -686,7 +750,7 @@ bind10-devel-20101201 released on December 01, 2010
tests and documentation. API is mostly the same but the
validation was a bit tightened. (Trac #351, svn r3056)
100. [func] Michal Vaner
100. [func] Vaner
Python processes: support naming of python processes so
they're not all called python3.
(Trac #322, svn r3052)
@@ -1097,110 +1161,110 @@ bind10-devel-20100602 released on June 2, 2010
bind10-devel-20100421 released on April 21, 2010
29. [build]
29. [build] jreed
Enable Python unit tests for "make check". (svn r1762)
28. [bug]
28. [bug] jreed
Fix msgq CC test so it can find its module. (svn r1751)
27. [build]
27. [build] jelte
Add missing copyright license statements to various source
files. (svn r1750)
26. [func]
26. [func] jelte
Use PACKAGE_STRING (name + version) from config.h instead
of hard-coded value in CH TXT version.bind replies (Trac
#114, svn r1749)
25. [func]*
25. [func]* jreed
Renamed msgq to b10-msgq. (Trac #25, svn r1747, r1748)
24. [func]
24. [func] jinmei
Support case-sensitive name compression in MessageRenderer.
(Trac #142, svn r1704)
23. [func]
23. [func] jinmei
Support a simple name with possible compression. (svn r1701)
22. [func]
22. [func] zhanglikun
b10-xfrout for AXFR-out support added. (svn r1629, r1630)
21. [bug]
21. [bug] zhanglikun
Make log message more readable when xfrin failed. (svn
r1697)
20. [bug]
20. [bug] jinmei
Keep stderr for child processes if -v is specified. (svn
r1690, r1698)
19. [bug]
19. [bug] jinmei
Allow bind10 boss to pass environment variables from parent.
(svn r1689)
18. [bug]
18. [bug] jinmei
Xfrin warn if bind10_dns load failed. (svn r1688)
17. [bug]
17. [bug] jinmei
Use sqlite3_ds.load() in xfrin module and catch Sqlite3DSError
explicitly. (svn r1684)
16. [func]*
16. [func]* zhanglikun
Removed print_message and print_settings configuration
commands from Xfrin. (Trac #136, svn r1682)
15. [func]*
15. [func]* jinmei
Changed zone loader/updater so trailing dot is not required.
(svn r1681)
14. [bug]
14. [bug] shane
Change shutdown to actually SIGKILL properly. (svn r1675)
13. [bug]
13. [bug] jinmei
Don't ignore other RRs than SOA even if the second SOA is
found. (svn r1674)
12. [build]
12. [build] jreed
Fix tests and testdata so can be used from a read-only
source directory.
11. [build]
11. [build] jreed
Make sure python tests scripts are included in tarball.
(svn r1648)
10. [build]
10. [build] jinmei
Improve python detection for configure. (svn r1622)
9. [build]
9. [build] jinmei
Automake the python binding of libdns. (svn r1617)
8. [bug]
8. [bug] zhanglikun
Fix log errors which may cause xfrin module to crash. (svn
r1613)
7. [func]
7. [func] zhanglikun
New API for inserting zone data to sqlite3 database for
AXFR-in. (svn r1612, r1613)
6. [bug]
6. [bug] jreed
More code review, miscellaneous cleanups, style guidelines,
and new and improved unit tests added.
5. [doc]
5. [doc] jreed
Manual page cleanups and improvements.
4. [bug]
4. [bug] jinmei
NSEC RDATA fixes for buffer overrun lookups, incorrect
boundary checks, spec-non-conformant behaviors. (svn r1611)
3. [bug]
3. [bug] jelte
Remove a re-raise of an exception that should only have
been included in an error answer on the cc channel. (svn
r1601)
2. [bug]
2. [bug] mgraff
Removed unnecessary sleep() from ccsession.cc. (svn r1528)
1. [build]*
1. [build]* jreed
The configure --with-boostlib option changed to --with-boost-lib.
bind10-devel-20100319 released on March 19, 2010
@@ -1222,7 +1286,7 @@ LEGEND
[doc] update to documentation. This shouldn't change run time behavior.
[func] new feature. In some cases this may be a backward incompatible
change, which would require a bump of major version.
[security] security hole fix. This is no different than a general bug fix
except that it will be handled as confidential and will cause
[security] security hole fix. This is no different than a general bug
fix except that it will be handled as confidential and will cause
security patch releases.
*: Backward incompatible or operational change.

View File

@@ -624,6 +624,8 @@ AC_CONFIG_FILES([Makefile
src/bin/auth/benchmarks/Makefile
src/bin/resolver/Makefile
src/bin/resolver/tests/Makefile
src/bin/sockcreator/Makefile
src/bin/sockcreator/tests/Makefile
src/bin/xfrin/Makefile
src/bin/xfrin/tests/Makefile
src/bin/xfrout/Makefile
@@ -642,6 +644,8 @@ AC_CONFIG_FILES([Makefile
src/lib/Makefile
src/lib/asiolink/Makefile
src/lib/asiolink/tests/Makefile
src/lib/asiodns/Makefile
src/lib/asiodns/tests/Makefile
src/lib/bench/Makefile
src/lib/bench/example/Makefile
src/lib/bench/tests/Makefile
@@ -667,6 +671,11 @@ AC_CONFIG_FILES([Makefile
src/lib/config/Makefile
src/lib/config/tests/Makefile
src/lib/config/tests/testdata/Makefile
src/lib/util/Makefile
src/lib/util/io/Makefile
src/lib/util/io/tests/Makefile
src/lib/util/unittests/Makefile
src/lib/util/tests/Makefile
src/lib/dns/Makefile
src/lib/dns/tests/Makefile
src/lib/dns/tests/testdata/Makefile
@@ -677,8 +686,6 @@ AC_CONFIG_FILES([Makefile
src/lib/datasrc/Makefile
src/lib/datasrc/tests/Makefile
src/lib/xfr/Makefile
src/lib/util/Makefile
src/lib/util/tests/Makefile
src/lib/log/Makefile
src/lib/log/compiler/Makefile
src/lib/log/tests/Makefile
@@ -694,6 +701,9 @@ AC_CONFIG_FILES([Makefile
src/lib/server_common/tests/Makefile
tests/Makefile
tests/system/Makefile
tests/tools/Makefile
tests/tools/badpacket/Makefile
tests/tools/badpacket/tests/Makefile
])
AC_OUTPUT([doc/version.ent
src/bin/cfgmgr/b10-cfgmgr.py
@@ -708,6 +718,7 @@ AC_OUTPUT([doc/version.ent
src/bin/xfrout/xfrout.py
src/bin/xfrout/xfrout.spec.pre
src/bin/xfrout/tests/xfrout_test
src/bin/xfrout/tests/xfrout_test.py
src/bin/xfrout/run_b10-xfrout.sh
src/bin/resolver/resolver.spec.pre
src/bin/resolver/spec_config.h.pre

View File

@@ -568,7 +568,11 @@ WARN_LOGFILE =
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
INPUT = ../src/lib/cc ../src/lib/config ../src/lib/dns ../src/lib/exceptions ../src/lib/datasrc ../src/bin/auth ../src/bin/resolver ../src/lib/bench ../src/lib/log ../src/lib/asiolink/ ../src/lib/nsas ../src/lib/testutils ../src/lib/cache ../src/lib/server_common/
INPUT = ../src/lib/cc ../src/lib/config ../src/lib/dns \
../src/lib/exceptions ../src/lib/datasrc ../src/bin/auth \
../src/bin/resolver ../src/lib/bench ../src/lib/log ../src/lib/asiolink/ \
../src/lib/nsas ../src/lib/testutils ../src/lib/cache \
../src/lib/server_common/ ../src/bin/sockcreator/ ../src/lib/util/
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is

View File

@@ -1,4 +1,4 @@
SUBDIRS = bind10 bindctl cfgmgr loadzone msgq host cmdctl auth xfrin xfrout \
usermgr zonemgr stats tests resolver
usermgr zonemgr stats tests resolver sockcreator
check-recursive: all-recursive

View File

@@ -39,9 +39,9 @@ pkglibexec_PROGRAMS = b10-auth
b10_auth_SOURCES = query.cc query.h
b10_auth_SOURCES += auth_srv.cc auth_srv.h
b10_auth_SOURCES += change_user.cc change_user.h
b10_auth_SOURCES += config.cc config.h
b10_auth_SOURCES += auth_config.cc auth_config.h
b10_auth_SOURCES += command.cc command.h
b10_auth_SOURCES += common.h
b10_auth_SOURCES += common.h common.cc
b10_auth_SOURCES += statistics.cc statistics.h
b10_auth_SOURCES += main.cc
b10_auth_LDADD = $(top_builddir)/src/lib/datasrc/libdatasrc.la
@@ -49,6 +49,7 @@ b10_auth_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
b10_auth_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
b10_auth_LDADD += $(top_builddir)/src/lib/cc/libcc.la
b10_auth_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
b10_auth_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
b10_auth_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
b10_auth_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
b10_auth_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la

View File

@@ -29,7 +29,7 @@
#include <datasrc/zonetable.h>
#include <auth/auth_srv.h>
#include <auth/config.h>
#include <auth/auth_config.h>
#include <auth/common.h>
#include <server_common/portconfig.h>

View File

@@ -53,7 +53,7 @@
#include <xfr/xfrout_client.h>
#include <auth/common.h>
#include <auth/config.h>
#include <auth/auth_config.h>
#include <auth/auth_srv.h>
#include <auth/query.h>
#include <auth/statistics.h>
@@ -70,7 +70,8 @@ using namespace isc::dns::rdata;
using namespace isc::data;
using namespace isc::config;
using namespace isc::xfr;
using namespace asiolink;
using namespace isc::asiolink;
using namespace isc::asiodns;
using namespace isc::server_common::portconfig;
class AuthSrvImpl {
@@ -768,6 +769,6 @@ AuthSrv::setListenAddresses(const AddressList& addresses) {
}
void
AuthSrv::setDNSService(asiolink::DNSService& dnss) {
AuthSrv::setDNSService(isc::asiodns::DNSService& dnss) {
dnss_ = &dnss;
}

View File

@@ -23,6 +23,15 @@
#include <cc/data.h>
#include <config/ccsession.h>
#include <dns/message.h>
#include <util/buffer.h>
#include <asiodns/dns_server.h>
#include <asiodns/dns_lookup.h>
#include <asiodns/dns_answer.h>
#include <asiolink/io_message.h>
#include <asiolink/io_service.h>
#include <asiolink/simple_callback.h>
#include <asiolink/asiolink.h>
#include <server_common/portconfig.h>
@@ -107,10 +116,10 @@ public:
/// \param server Pointer to the \c DNSServer
///
/// \throw isc::Unexpected Protocol type of \a message is unexpected
void processMessage(const asiolink::IOMessage& io_message,
void processMessage(const isc::asiolink::IOMessage& io_message,
isc::dns::MessagePtr message,
isc::util::OutputBufferPtr buffer,
asiolink::DNSServer* server);
isc::asiodns::DNSServer* server);
/// \brief Set verbose flag
///
@@ -193,16 +202,16 @@ public:
void setConfigSession(isc::config::ModuleCCSession* config_session);
/// \brief Return this object's ASIO IO Service queue
asiolink::IOService& getIOService();
isc::asiolink::IOService& getIOService();
/// \brief Return pointer to the DNS Lookup callback function
asiolink::DNSLookup* getDNSLookupProvider() const { return (dns_lookup_); }
isc::asiodns::DNSLookup* getDNSLookupProvider() const { return (dns_lookup_); }
/// \brief Return pointer to the DNS Answer callback function
asiolink::DNSAnswer* getDNSAnswerProvider() const { return (dns_answer_); }
isc::asiodns::DNSAnswer* getDNSAnswerProvider() const { return (dns_answer_); }
/// \brief Return pointer to the Checkin callback function
asiolink::SimpleCallback* getCheckinProvider() const { return (checkin_); }
isc::asiolink::SimpleCallback* getCheckinProvider() const { return (checkin_); }
/// \brief Set or update the size (number of slots) of hot spot cache.
///
@@ -363,15 +372,15 @@ public:
const;
/// \brief Assign an ASIO DNS Service queue to this Auth object
void setDNSService(asiolink::DNSService& dnss);
void setDNSService(isc::asiodns::DNSService& dnss);
private:
AuthSrvImpl* impl_;
asiolink::SimpleCallback* checkin_;
asiolink::DNSLookup* dns_lookup_;
asiolink::DNSAnswer* dns_answer_;
asiolink::DNSService* dnss_;
isc::asiolink::SimpleCallback* checkin_;
isc::asiodns::DNSLookup* dns_lookup_;
isc::asiodns::DNSAnswer* dns_answer_;
isc::asiodns::DNSService* dnss_;
};
#endif // __AUTH_SRV_H

View File

@@ -10,7 +10,7 @@ noinst_PROGRAMS = query_bench
query_bench_SOURCES = query_bench.cc
query_bench_SOURCES += ../query.h ../query.cc
query_bench_SOURCES += ../auth_srv.h ../auth_srv.cc
query_bench_SOURCES += ../config.h ../config.cc
query_bench_SOURCES += ../auth_config.h ../auth_config.cc
query_bench_SOURCES += ../statistics.h ../statistics.cc
query_bench_LDADD = $(top_builddir)/src/lib/dns/libdns++.la
@@ -22,6 +22,7 @@ query_bench_LDADD += $(top_builddir)/src/lib/cc/libcc.la
query_bench_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
query_bench_LDADD += $(top_builddir)/src/lib/log/liblog.la
query_bench_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
query_bench_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
query_bench_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
query_bench_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
query_bench_LDADD += $(SQLITE_LIBS)

View File

@@ -12,6 +12,8 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <config.h>
#include <stdlib.h>
#include <iostream>
@@ -31,9 +33,10 @@
#include <xfr/xfrout_client.h>
#include <auth/auth_srv.h>
#include <auth/config.h>
#include <auth/auth_config.h>
#include <auth/query.h>
#include <asiodns/asiodns.h>
#include <asiolink/asiolink.h>
using namespace std;
@@ -44,7 +47,8 @@ using namespace isc::dns;
using namespace isc::util;
using namespace isc::xfr;
using namespace isc::bench;
using namespace asiolink;
using namespace isc::asiodns;
using namespace isc::asiolink;
namespace {
// Commonly used constant:

36
src/bin/auth/common.cc Normal file
View File

@@ -0,0 +1,36 @@
// Copyright (C) 2009-2011 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <auth/common.h>
#include <auth/spec_config.h>
#include <stdlib.h>
using std::string;
string getXfroutSocketPath() {
if (getenv("B10_FROM_BUILD") != NULL) {
if (getenv("B10_FROM_SOURCE_LOCALSTATEDIR")) {
return (string(getenv("B10_FROM_SOURCE_LOCALSTATEDIR")) +
"/auth_xfrout_conn");
} else {
return (string(getenv("B10_FROM_BUILD")) + "/auth_xfrout_conn");
}
} else {
if (getenv("BIND10_XFROUT_SOCKET_FILE")) {
return (getenv("BIND10_XFROUT_SOCKET_FILE"));
} else {
return (UNIX_SOCKET_FILE);
}
}
}

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2009-2011 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
@@ -29,6 +29,15 @@ public:
{}
};
/// \short Get the path of socket to talk to xfrout
///
/// It takes some environment variables into account (B10_FROM_BUILD,
/// B10_FROM_SOURCE_LOCALSTATEDIR and BIND10_XFROUT_SOCKET_FILE). It
/// also considers the installation prefix.
///
/// The logic should be the same as in b10-xfrout, so they find each other.
std::string getXfroutSocketPath();
#endif // __COMMON_H
// Local Variables:

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2009-2011 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
@@ -12,6 +12,8 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <config.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
@@ -38,10 +40,11 @@
#include <auth/spec_config.h>
#include <auth/common.h>
#include <auth/config.h>
#include <auth/auth_config.h>
#include <auth/command.h>
#include <auth/change_user.h>
#include <auth/auth_srv.h>
#include <asiodns/asiodns.h>
#include <asiolink/asiolink.h>
#include <log/dummylog.h>
@@ -52,7 +55,8 @@ using namespace isc::config;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::xfr;
using namespace asiolink;
using namespace isc::asiolink;
using namespace isc::asiodns;
namespace {
@@ -122,19 +126,7 @@ main(int argc, char* argv[]) {
bool xfrin_session_established = false; // XXX (see Trac #287)
bool statistics_session_established = false; // XXX (see Trac #287)
ModuleCCSession* config_session = NULL;
string xfrout_socket_path;
if (getenv("B10_FROM_BUILD") != NULL) {
if (getenv("B10_FROM_SOURCE_LOCALSTATEDIR")) {
xfrout_socket_path = string("B10_FROM_SOURCE_LOCALSTATEDIR") +
"/auth_xfrout_conn";
} else {
xfrout_socket_path = string(getenv("B10_FROM_BUILD")) +
"/auth_xfrout_conn";
}
} else {
xfrout_socket_path = UNIX_SOCKET_FILE;
}
XfroutClient xfrout_client(xfrout_socket_path);
XfroutClient xfrout_client(getXfroutSocketPath());
try {
string specfile;
if (getenv("B10_FROM_BUILD")) {

View File

@@ -1,4 +1,5 @@
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += -I$(top_builddir)/src/bin # for generated spec_config.h header
AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/bin
AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc
AM_CPPFLAGS += $(BOOST_INCLUDES)
@@ -16,18 +17,23 @@ CLEANFILES = *.gcno *.gcda
TESTS =
if HAVE_GTEST
BUILT_SOURCES = ../spec_config.h
TESTS += run_unittests
run_unittests_SOURCES = $(top_srcdir)/src/lib/dns/tests/unittest_util.h
run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
run_unittests_SOURCES += ../auth_srv.h ../auth_srv.cc
run_unittests_SOURCES += ../query.h ../query.cc
run_unittests_SOURCES += ../change_user.h ../change_user.cc
run_unittests_SOURCES += ../config.h ../config.cc
run_unittests_SOURCES += ../auth_config.h ../auth_config.cc
run_unittests_SOURCES += ../command.h ../command.cc
run_unittests_SOURCES += ../common.h ../common.cc
run_unittests_SOURCES += ../spec_config.h
run_unittests_SOURCES += ../statistics.h ../statistics.cc
run_unittests_SOURCES += auth_srv_unittest.cc
run_unittests_SOURCES += config_unittest.cc
run_unittests_SOURCES += command_unittest.cc
run_unittests_SOURCES += common_unittest.cc
run_unittests_SOURCES += query_unittest.cc
run_unittests_SOURCES += change_user_unittest.cc
run_unittests_SOURCES += statistics_unittest.cc
@@ -39,6 +45,7 @@ run_unittests_LDADD += $(SQLITE_LIBS)
run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libtestutils.la
run_unittests_LDADD += $(top_builddir)/src/lib/datasrc/libdatasrc.la
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
run_unittests_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la

View File

@@ -45,7 +45,8 @@ using namespace isc::util;
using namespace isc::dns::rdata;
using namespace isc::data;
using namespace isc::xfr;
using namespace asiolink;
using namespace isc::asiodns;
using namespace isc::asiolink;
using namespace isc::testutils;
using namespace isc::server_common::portconfig;
using isc::UnitTestUtil;

View File

@@ -12,6 +12,8 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <config.h>
#include <cassert>
#include <cstdlib>
#include <string>
@@ -32,7 +34,7 @@
#include <datasrc/memory_datasrc.h>
#include <auth/auth_srv.h>
#include <auth/config.h>
#include <auth/auth_config.h>
#include <auth/command.h>
#include <asiolink/asiolink.h>
@@ -97,7 +99,7 @@ AuthConmmandTest::stopServer() {
}
TEST_F(AuthConmmandTest, shutdown) {
asiolink::IntervalTimer itimer(server.getIOService());
isc::asiolink::IntervalTimer itimer(server.getIOService());
itimer.setup(boost::bind(&AuthConmmandTest::stopServer, this), 1);
server.getIOService().run();
EXPECT_EQ(0, rcode);

View File

@@ -0,0 +1,96 @@
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
#include <auth/common.h>
#include <auth/spec_config.h>
#include <vector>
#include <string>
#include <cstdio>
#include <boost/foreach.hpp>
using std::pair;
using std::vector;
using std::string;
namespace {
class Paths : public ::testing::Test {
private:
typedef pair<string, string*> Environ;
vector<Environ> restoreEnviron;
public:
void TearDown() {
// Restore the original environment
BOOST_FOREACH(const Environ &env, restoreEnviron) {
if (env.second == NULL) {
EXPECT_EQ(0, unsetenv(env.first.c_str())) <<
"Couldn't restore environment, results of other tests"
"are uncertain";
} else {
EXPECT_EQ(0, setenv(env.first.c_str(), env.second->c_str(),
1)) << "Couldn't restore environment, "
"results of other tests are uncertain";
}
}
}
protected:
// Sets a temporary value into environment. If value is empty, it deletes
// the variable from environment (just for simplicity).
void setEnv(const string& name, const string& value) {
// Backup the original environment
char* env(getenv(name.c_str()));
restoreEnviron.push_back(Environ(name, env == NULL ? NULL :
new string(env)));
// Set the new value
if (value.empty()) {
EXPECT_EQ(0, unsetenv(name.c_str()));
} else {
EXPECT_EQ(0, setenv(name.c_str(), value.c_str(), 1));
}
}
// Test getXfroutSocketPath under given environment
void testXfrout(const string& fromBuild, const string& localStateDir,
const string& socketFile, const string& expected)
{
setEnv("B10_FROM_BUILD", fromBuild);
setEnv("B10_FROM_SOURCE_LOCALSTATEDIR", localStateDir);
setEnv("BIND10_XFROUT_SOCKET_FILE", socketFile);
EXPECT_EQ(expected, getXfroutSocketPath());
}
};
// Test that when we have no special environment, we get the default from prefix
TEST_F(Paths, xfroutNoEnv) {
testXfrout("", "", "", UNIX_SOCKET_FILE);
}
// Override by B10_FROM_BUILD
TEST_F(Paths, xfroutFromBuild) {
testXfrout("/from/build", "", "/wrong/path",
"/from/build/auth_xfrout_conn");
}
// Override by B10_FROM_SOURCE_LOCALSTATEDIR
TEST_F(Paths, xfroutLocalStatedir) {
testXfrout("/wrong/path", "/state/dir", "/wrong/path",
"/state/dir/auth_xfrout_conn");
}
// Override by BIND10_XFROUT_SOCKET_FILE explicitly
TEST_F(Paths, xfroutFromEnv) {
testXfrout("", "", "/the/path/to/file", "/the/path/to/file");
}
}

View File

@@ -26,7 +26,7 @@
#include <xfr/xfrout_client.h>
#include <auth/auth_srv.h>
#include <auth/config.h>
#include <auth/auth_config.h>
#include <auth/common.h>
#include <testutils/mockups.h>
@@ -35,7 +35,8 @@
using namespace isc::dns;
using namespace isc::data;
using namespace isc::datasrc;
using namespace asiolink;
using namespace isc::asiodns;
using namespace isc::asiolink;
namespace {
class AuthConfigTest : public ::testing::Test {

View File

@@ -2,12 +2,12 @@
.\" Title: bind10
.\" Author: [see the "AUTHORS" section]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
.\" Date: February 22, 2011
.\" Date: March 31, 2011
.\" Manual: BIND10
.\" Source: BIND10
.\" Language: English
.\"
.TH "BIND10" "8" "February 22, 2011" "BIND10" "BIND10"
.TH "BIND10" "8" "March 31, 2011" "BIND10" "BIND10"
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
@@ -22,7 +22,7 @@
bind10 \- BIND 10 boss process
.SH "SYNOPSIS"
.HP \w'\fBbind10\fR\ 'u
\fBbind10\fR [\fB\-m\ \fR\fB\fIfile\fR\fR] [\fB\-n\fR] [\fB\-u\ \fR\fB\fIuser\fR\fR] [\fB\-v\fR] [\fB\-\-msgq\-socket\-file\ \fR\fB\fIfile\fR\fR] [\fB\-\-no\-cache\fR] [\fB\-\-user\ \fR\fB\fIuser\fR\fR] [\fB\-\-pretty\-name\ \fR\fB\fIname\fR\fR] [\fB\-\-verbose\fR]
\fBbind10\fR [\fB\-c\ \fR\fB\fIconfig\-filename\fR\fR] [\fB\-m\ \fR\fB\fIfile\fR\fR] [\fB\-n\fR] [\fB\-p\ \fR\fB\fIdata_path\fR\fR] [\fB\-u\ \fR\fB\fIuser\fR\fR] [\fB\-v\fR] [\fB\-\-brittle\fR] [\fB\-\-cmdctl\-port\fR\ \fIport\fR] [\fB\-\-config\-file\fR\ \fIconfig\-filename\fR] [\fB\-\-data\-path\fR\ \fIdirectory\fR] [\fB\-\-msgq\-socket\-file\ \fR\fB\fIfile\fR\fR] [\fB\-\-no\-cache\fR] [\fB\-\-pid\-file\fR\ \fIfilename\fR] [\fB\-\-pretty\-name\ \fR\fB\fIname\fR\fR] [\fB\-\-user\ \fR\fB\fIuser\fR\fR] [\fB\-\-verbose\fR]
.SH "DESCRIPTION"
.PP
The
@@ -32,6 +32,34 @@ daemon starts up other BIND 10 required daemons\&. It handles restarting of exit
.PP
The arguments are as follows:
.PP
\fB\-\-brittle\fR
.RS 4
Shutdown if any of the child processes of
\fBbind10\fR
exit\&. This is intended to help developers debug the server, and should not be used in production\&.
.RE
.PP
\fB\-c\fR \fIconfig\-filename\fR, \fB\-\-config\-file\fR \fIconfig\-filename\fR
.RS 4
The configuration filename to use\&. Can be either absolute or relative to data path\&. In case it is absolute, value of data path is not considered\&.
.sp
Defaults to b10\-config\&.db\&.
.RE
.PP
\fB\-\-cmdctl\-port\fR \fIport\fR
.RS 4
The
\fBb10\-cmdctl\fR
daemon will listen on this port\&. (See
b10\-cmdctl(8)
for the default\&.)
.RE
.PP
\fB\-p\fR \fIdirectory\fR, \fB\-\-data\-path\fR \fIdirectory\fR
.RS 4
The path where BIND 10 programs look for various data files\&. Currently only b10\-cfgmgr uses it to locate the configuration file, but the usage might be extended for other programs and other types of files\&.
.RE
.PP
\fB\-m\fR \fIfile\fR, \fB\-\-msgq\-socket\-file\fR \fIfile\fR
.RS 4
The UNIX domain socket file for the
@@ -57,6 +85,13 @@ to run as\&.
must be initially ran as the root user to use this option\&. The default is to run as the current user\&.
.RE
.PP
\fB\-\-pid\-file\fR \fIfilename\fR
.RS 4
If defined, the PID of the
\fBbind10\fR
is stored in this file\&. This is used for testing purposes\&.
.RE
.PP
\fB\-\-pretty\-name \fR\fB\fIname\fR\fR
.RS 4
The name this process should have in tools like

View File

@@ -202,7 +202,7 @@ class BoB:
def __init__(self, msgq_socket_file=None, data_path=None,
config_filename=None, nocache=False, verbose=False, setuid=None,
username=None, cmdctl_port=None):
username=None, cmdctl_port=None, brittle=False):
"""
Initialize the Boss of BIND. This is a singleton (only one can run).
@@ -235,6 +235,7 @@ class BoB:
self.data_path = data_path
self.config_filename = config_filename
self.cmdctl_port = cmdctl_port
self.brittle = brittle
def config_handler(self, new_config):
# If this is initial update, don't do anything now, leave it to startup
@@ -296,7 +297,7 @@ class BoB:
def command_handler(self, command, args):
if self.verbose:
sys.stdout.write("[bind10] Boss got command: " + command + "\n")
sys.stdout.write("[bind10] Boss got command: " + str(command) + "\n")
answer = isc.config.ccsession.create_answer(1, "command not implemented")
if type(command) != str:
answer = isc.config.ccsession.create_answer(1, "bad command")
@@ -304,6 +305,15 @@ class BoB:
if command == "shutdown":
self.runnable = False
answer = isc.config.ccsession.create_answer(0)
elif command == "sendstats":
# send statistics data to the stats daemon immediately
cmd = isc.config.ccsession.create_command(
'set', { "stats_data": {
'bind10.boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', _BASETIME)
}})
seq = self.cc_session.group_sendmsg(cmd, 'Stats')
self.cc_session.group_recvmsg(True, seq)
answer = isc.config.ccsession.create_answer(0)
elif command == "ping":
answer = isc.config.ccsession.create_answer(0, "pong")
elif command == "show_processes":
@@ -710,20 +720,22 @@ class BoB:
if self.verbose:
sys.stdout.write("[bind10] All processes ended, server done.\n")
def _get_process_exit_status(self):
return os.waitpid(-1, os.WNOHANG)
def reap_children(self):
"""Check to see if any of our child processes have exited,
and note this for later handling.
"""
while True:
try:
(pid, exit_status) = os.waitpid(-1, os.WNOHANG)
(pid, exit_status) = self._get_process_exit_status()
except OSError as o:
if o.errno == errno.ECHILD: break
# XXX: should be impossible to get any other error here
raise
if pid == 0: break
if pid in self.processes:
# One of the processes we know about. Get information on it.
proc_info = self.processes.pop(pid)
proc_info.restart_schedule.set_run_stop_time()
@@ -747,6 +759,11 @@ class BoB:
sys.stdout.write(
"[bind10] The b10-msgq process died, shutting down.\n")
self.runnable = False
# If we're in 'brittle' mode, we want to shutdown after
# any process dies.
if self.brittle:
self.runnable = False
else:
sys.stdout.write("[bind10] Unknown child pid %d exited.\n" % pid)
@@ -856,6 +873,8 @@ def parse_args(args=sys.argv[1:], Parser=OptionParser):
parser.add_option("--pid-file", dest="pid_file", type="string",
default=None,
help="file to dump the PID of the BIND 10 process")
parser.add_option("--brittle", dest="brittle", action="store_true",
help="debugging flag: exit if any component dies")
(options, args) = parser.parse_args(args)
@@ -959,7 +978,7 @@ def main():
# Go bob!
boss_of_bind = BoB(options.msgq_socket_file, options.data_path,
options.config_file, options.nocache, options.verbose,
setuid, username, options.cmdctl_port)
setuid, username, options.cmdctl_port, options.brittle)
startup_result = boss_of_bind.startup()
if startup_result:
sys.stderr.write("[bind10] Error on startup: %s\n" % startup_result)
@@ -967,17 +986,6 @@ def main():
sys.stdout.write("[bind10] BIND 10 started\n")
dump_pid(options.pid_file)
# send "bind10.boot_time" to b10-stats
time.sleep(1) # wait a second
if options.verbose:
sys.stdout.write("[bind10] send \"bind10.boot_time\" to b10-stats\n")
cmd = isc.config.ccsession.create_command('set',
{ "stats_data": {
'bind10.boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', _BASETIME)
}
})
boss_of_bind.cc_session.group_sendmsg(cmd, 'Stats')
# In our main loop, we check for dead processes or messages
# on the c-channel.
wakeup_fd = wakeup_pipe[0]

View File

@@ -20,7 +20,7 @@
<refentry>
<refentryinfo>
<date>February 22, 2011</date>
<date>March 31, 2011</date>
</refentryinfo>
<refmeta>
@@ -44,16 +44,21 @@
<refsynopsisdiv>
<cmdsynopsis>
<command>bind10</command>
<arg><option>-c <replaceable>config-filename</replaceable></option></arg>
<arg><option>-m <replaceable>file</replaceable></option></arg>
<arg><option>-n</option></arg>
<arg><option>-p <replaceable>data_path</replaceable></option></arg>
<arg><option>-u <replaceable>user</replaceable></option></arg>
<arg><option>-v</option></arg>
<arg><option>-c<replaceable>config-filename</replaceable></option></arg>
<arg><option>-p<replaceable>data_path</replaceable></option></arg>
<arg><option>--brittle</option></arg>
<arg><option>--cmdctl-port</option> <replaceable>port</replaceable></arg>
<arg><option>--config-file</option> <replaceable>config-filename</replaceable></arg>
<arg><option>--data-path</option> <replaceable>directory</replaceable></arg>
<arg><option>--msgq-socket-file <replaceable>file</replaceable></option></arg>
<arg><option>--no-cache</option></arg>
<arg><option>--user <replaceable>user</replaceable></option></arg>
<arg><option>--pid-file</option> <replaceable>filename</replaceable></arg>
<arg><option>--pretty-name <replaceable>name</replaceable></option></arg>
<arg><option>--user <replaceable>user</replaceable></option></arg>
<arg><option>--verbose</option></arg>
</cmdsynopsis>
</refsynopsisdiv>
@@ -82,6 +87,21 @@
<para>The arguments are as follows:</para>
<variablelist>
<varlistentry>
<term>
<option>--brittle</option>
</term>
<listitem>
<para>
Shutdown if any of the child processes of
<command>bind10</command> exit. This is intended to
help developers debug the server, and should not be
used in production.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<option>-c</option> <replaceable>config-filename</replaceable>,
@@ -97,8 +117,22 @@
<varlistentry>
<term>
<option>-p</option><replaceable>data-path</replaceable>,
<option>--data-path</option> <replaceable>data-path</replaceable>
<option>--cmdctl-port</option> <replaceable>port</replaceable>
</term>
<listitem>
<para>The <command>b10-cmdctl</command> daemon will listen
on this port.
(See
<refentrytitle>b10-cmdctl</refentrytitle><manvolnum>8</manvolnum>
for the default.)
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<option>-p</option> <replaceable>directory</replaceable>,
<option>--data-path</option> <replaceable>directory</replaceable>
</term>
<listitem>
<para>The path where BIND 10 programs look for various data files.
@@ -134,7 +168,6 @@
<varlistentry>
<term><option>-u</option> <replaceable>user</replaceable>, <option>--user</option> <replaceable>name</replaceable></term>
<listitem>
<para>The username for <command>bind10</command> to run as.
<!-- TODO: example more detail. -->
@@ -144,6 +177,16 @@
</listitem>
</varlistentry>
<varlistentry>
<term><option>--pid-file</option> <replaceable>filename</replaceable></term>
<listitem>
<para>If defined, the PID of the <command>bind10</command> is stored
in this file.
This is used for testing purposes.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--pretty-name <replaceable>name</replaceable></option></term>

View File

@@ -22,6 +22,11 @@
"command_description": "Shut down BIND 10",
"command_args": []
},
{
"command_name": "sendstats",
"command_description": "Send data to a statistics module at once",
"command_args": []
},
{
"command_name": "ping",
"command_description": "Ping the boss process",

View File

@@ -1,5 +1,6 @@
PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
#PYTESTS = args_test.py bind10_test.py
# NOTE: this has a generated test found in the builddir
PYTESTS = bind10_test.py
EXTRA_DIST = $(PYTESTS)
@@ -14,5 +15,5 @@ endif
echo Running test: $$pytest ; \
env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/bind10 \
BIND10_MSGQ_SOCKET_FILE=$(abs_top_builddir)/msgq_socket \
$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
$(PYCOVERAGE_RUN) $(abs_builddir)/$$pytest || exit ; \
done

View File

@@ -13,7 +13,7 @@
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
from bind10 import ProcessInfo, BoB, parse_args, dump_pid, unlink_pid_file
from bind10 import ProcessInfo, BoB, parse_args, dump_pid, unlink_pid_file, _BASETIME
# XXX: environment tests are currently disabled, due to the preprocessor
# setup that we have now complicating the environment
@@ -24,6 +24,9 @@ import os
import signal
import socket
from isc.net.addr import IPAddr
import time
import isc
from isc.testutils.parse_args import TestOptParser, OptsError
class TestProcessInfo(unittest.TestCase):
@@ -123,6 +126,41 @@ class TestBoB(unittest.TestCase):
self.assertEqual(bob.cfg_start_auth, True)
self.assertEqual(bob.cfg_start_resolver, False)
def test_command_handler(self):
class DummySession():
def group_sendmsg(self, msg, group):
(self.msg, self.group) = (msg, group)
def group_recvmsg(self, nonblock, seq): pass
bob = BoB()
bob.verbose = True
bob.cc_session = DummySession()
# a bad command
self.assertEqual(bob.command_handler(-1, None),
isc.config.ccsession.create_answer(1, "bad command"))
# "shutdown" command
self.assertEqual(bob.command_handler("shutdown", None),
isc.config.ccsession.create_answer(0))
self.assertFalse(bob.runnable)
# "sendstats" command
self.assertEqual(bob.command_handler("sendstats", None),
isc.config.ccsession.create_answer(0))
self.assertEqual(bob.cc_session.group, "Stats")
self.assertEqual(bob.cc_session.msg,
isc.config.ccsession.create_command(
'set', { "stats_data": {
'bind10.boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', _BASETIME)
}}))
# "ping" command
self.assertEqual(bob.command_handler("ping", None),
isc.config.ccsession.create_answer(0, "pong"))
# "show_processes" command
self.assertEqual(bob.command_handler("show_processes", None),
isc.config.ccsession.create_answer(0,
bob.get_processes()))
# an unknown command
self.assertEqual(bob.command_handler("__UNKNOWN__", None),
isc.config.ccsession.create_answer(1, "Unknown command"))
# Class for testing the BoB without actually starting processes.
# This is used for testing the start/stop components routines and
# the BoB commands.
@@ -156,42 +194,52 @@ class MockBob(BoB):
def start_msgq(self, c_channel_env):
self.msgq = True
self.processes[2] = ProcessInfo('b10-msgq', ['/bin/false'])
self.processes[2].pid = 2
def start_cfgmgr(self, c_channel_env):
self.cfgmgr = True
self.processes[3] = ProcessInfo('b10-cfgmgr', ['/bin/false'])
self.processes[3].pid = 3
def start_ccsession(self, c_channel_env):
self.ccsession = True
self.processes[4] = ProcessInfo('b10-ccsession', ['/bin/false'])
self.processes[4].pid = 4
def start_auth(self, c_channel_env):
self.auth = True
self.processes[5] = ProcessInfo('b10-auth', ['/bin/false'])
self.processes[5].pid = 5
def start_resolver(self, c_channel_env):
self.resolver = True
self.processes[6] = ProcessInfo('b10-resolver', ['/bin/false'])
self.processes[6].pid = 6
def start_xfrout(self, c_channel_env):
self.xfrout = True
self.processes[7] = ProcessInfo('b10-xfrout', ['/bin/false'])
self.processes[7].pid = 7
def start_xfrin(self, c_channel_env):
self.xfrin = True
self.processes[8] = ProcessInfo('b10-xfrin', ['/bin/false'])
self.processes[8].pid = 8
def start_zonemgr(self, c_channel_env):
self.zonemgr = True
self.processes[9] = ProcessInfo('b10-zonemgr', ['/bin/false'])
self.processes[9].pid = 9
def start_stats(self, c_channel_env):
self.stats = True
self.processes[10] = ProcessInfo('b10-stats', ['/bin/false'])
self.processes[10].pid = 10
def start_cmdctl(self, c_channel_env):
self.cmdctl = True
self.processes[11] = ProcessInfo('b10-cmdctl', ['/bin/false'])
self.processes[11].pid = 11
# We don't really use all of these stop_ methods. But it might turn out
# someone would add some stop_ method to BoB and we want that one overriden
@@ -550,6 +598,15 @@ class TestParseArgs(unittest.TestCase):
options = parse_args(['--cmdctl-port=1234'], TestOptParser)
self.assertEqual(1234, options.cmdctl_port)
def test_brittle(self):
"""
Test we can use the "brittle" flag.
"""
options = parse_args([], TestOptParser)
self.assertFalse(options.brittle)
options = parse_args(['--brittle'], TestOptParser)
self.assertTrue(options.brittle)
class TestPIDFile(unittest.TestCase):
def setUp(self):
self.pid_file = '@builddir@' + os.sep + 'bind10.pid'
@@ -597,5 +654,34 @@ class TestPIDFile(unittest.TestCase):
self.assertRaises(IOError, dump_pid,
'nonexistent_dir' + os.sep + 'bind10.pid')
class TestBrittle(unittest.TestCase):
def test_brittle_disabled(self):
bob = MockBob()
bob.start_all_processes()
bob.runnable = True
bob.reap_children()
self.assertTrue(bob.runnable)
def simulated_exit(self):
ret_val = self.exit_info
self.exit_info = (0, 0)
return ret_val
def test_brittle_enabled(self):
bob = MockBob()
bob.start_all_processes()
bob.runnable = True
bob.brittle = True
self.exit_info = (5, 0)
bob._get_process_exit_status = self.simulated_exit
old_stdout = sys.stdout
sys.stdout = open("/dev/null", "w")
bob.reap_children()
sys.stdout = old_stdout
self.assertFalse(bob.runnable)
if __name__ == '__main__':
unittest.main()

View File

@@ -4,6 +4,8 @@ AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += -I$(top_srcdir)/src/bin -I$(top_builddir)/src/bin
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/cc -I$(top_builddir)/src/lib/cc
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/asiodns
AM_CPPFLAGS += -I$(top_builddir)/src/lib/asiodns
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/asiolink
AM_CPPFLAGS += -I$(top_builddir)/src/lib/asiolink
AM_CPPFLAGS += $(BOOST_INCLUDES)
@@ -45,6 +47,7 @@ b10_resolver_LDADD = $(top_builddir)/src/lib/dns/libdns++.la
b10_resolver_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
b10_resolver_LDADD += $(top_builddir)/src/lib/cc/libcc.la
b10_resolver_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
b10_resolver_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
b10_resolver_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
b10_resolver_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
b10_resolver_LDADD += $(top_builddir)/src/lib/log/liblog.la

View File

@@ -12,6 +12,8 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <config.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
@@ -25,6 +27,7 @@
#include <boost/foreach.hpp>
#include <asiodns/asiodns.h>
#include <asiolink/asiolink.h>
#include <exceptions/exceptions.h>
@@ -56,7 +59,8 @@ using namespace isc::cc;
using namespace isc::config;
using namespace isc::data;
using isc::log::dlog;
using namespace asiolink;
using namespace isc::asiodns;
using namespace isc::asiolink;
namespace {

View File

@@ -20,6 +20,7 @@
#include <vector>
#include <cassert>
#include <asiodns/asiodns.h>
#include <asiolink/asiolink.h>
#include <boost/foreach.hpp>
@@ -56,7 +57,8 @@ using namespace isc::dns;
using namespace isc::data;
using namespace isc::config;
using isc::log::dlog;
using namespace asiolink;
using namespace isc::asiodns;
using namespace isc::asiolink;
using namespace isc::server_common::portconfig;
class ResolverImpl {
@@ -347,7 +349,7 @@ Resolver::~Resolver() {
}
void
Resolver::setDNSService(asiolink::DNSService& dnss) {
Resolver::setDNSService(isc::asiodns::DNSService& dnss) {
dnss_ = &dnss;
}

View File

@@ -21,8 +21,16 @@
#include <cc/data.h>
#include <config/ccsession.h>
#include <dns/message.h>
#include <util/buffer.h>
#include <asiolink/asiolink.h>
#include <asiodns/dns_server.h>
#include <asiodns/dns_service.h>
#include <asiodns/dns_lookup.h>
#include <asiodns/dns_answer.h>
#include <asiolink/io_message.h>
#include <asiolink/io_service.h>
#include <asiolink/simple_callback.h>
#include <nsas/nameserver_address_store.h>
#include <cache/resolver_cache.h>
@@ -74,11 +82,11 @@ public:
/// shall return to the client
/// \param buffer Pointer to an \c OutputBuffer for the resposne
/// \param server Pointer to the \c DNSServer
void processMessage(const asiolink::IOMessage& io_message,
void processMessage(const isc::asiolink::IOMessage& io_message,
isc::dns::MessagePtr query_message,
isc::dns::MessagePtr answer_message,
isc::util::OutputBufferPtr buffer,
asiolink::DNSServer* server);
isc::asiodns::DNSServer* server);
/// \brief Set and get the config session
isc::config::ModuleCCSession* getConfigSession() const;
@@ -88,7 +96,7 @@ public:
isc::data::ConstElementPtr updateConfig(isc::data::ConstElementPtr config);
/// \brief Assign an ASIO IO Service queue to this Resolver object
void setDNSService(asiolink::DNSService& dnss);
void setDNSService(isc::asiodns::DNSService& dnss);
/// \brief Assign a NameserverAddressStore to this Resolver object
void setNameserverAddressStore(isc::nsas::NameserverAddressStore &nsas);
@@ -97,7 +105,7 @@ public:
void setCache(isc::cache::ResolverCache& cache);
/// \brief Return this object's ASIO IO Service queue
asiolink::DNSService& getDNSService() const { return (*dnss_); }
isc::asiodns::DNSService& getDNSService() const { return (*dnss_); }
/// \brief Returns this object's NSAS
isc::nsas::NameserverAddressStore& getNameserverAddressStore() const {
@@ -110,13 +118,13 @@ public:
};
/// \brief Return pointer to the DNS Lookup callback function
asiolink::DNSLookup* getDNSLookupProvider() { return (dns_lookup_); }
isc::asiodns::DNSLookup* getDNSLookupProvider() { return (dns_lookup_); }
/// \brief Return pointer to the DNS Answer callback function
asiolink::DNSAnswer* getDNSAnswerProvider() { return (dns_answer_); }
isc::asiodns::DNSAnswer* getDNSAnswerProvider() { return (dns_answer_); }
/// \brief Return pointer to the Checkin callback function
asiolink::SimpleCallback* getCheckinProvider() { return (checkin_); }
isc::asiolink::SimpleCallback* getCheckinProvider() { return (checkin_); }
/**
* \brief Tell the Resolver that is has already been configured
@@ -230,10 +238,10 @@ public:
private:
ResolverImpl* impl_;
asiolink::DNSService* dnss_;
asiolink::SimpleCallback* checkin_;
asiolink::DNSLookup* dns_lookup_;
asiolink::DNSAnswer* dns_answer_;
isc::asiodns::DNSService* dnss_;
isc::asiolink::SimpleCallback* checkin_;
isc::asiodns::DNSLookup* dns_lookup_;
isc::asiodns::DNSAnswer* dns_answer_;
isc::nsas::NameserverAddressStore* nsas_;
isc::cache::ResolverCache* cache_;
// This value is initally false, and will be set to true

View File

@@ -26,7 +26,7 @@ using namespace std;
// Compare addresses etc.
ResponseScrubber::Category ResponseScrubber::addressCheck(
const asiolink::IOEndpoint& to, const asiolink::IOEndpoint& from)
const isc::asiolink::IOEndpoint& to, const isc::asiolink::IOEndpoint& from)
{
if (from.getProtocol() == to.getProtocol()) {
if (from.getAddress() == to.getAddress()) {

View File

@@ -282,8 +282,8 @@ public:
///
/// \return SUCCESS if the two endpoints match, otherwise an error status
/// indicating what was incorrect.
static Category addressCheck(const asiolink::IOEndpoint& to,
const asiolink::IOEndpoint& from);
static Category addressCheck(const isc::asiolink::IOEndpoint& to,
const isc::asiolink::IOEndpoint& from);
/// \brief Check QID
///

View File

@@ -31,6 +31,7 @@ run_unittests_LDADD += $(SQLITE_LIBS)
run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libtestutils.la
run_unittests_LDADD += $(top_builddir)/src/lib/datasrc/libdatasrc.la
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
run_unittests_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la

View File

@@ -12,12 +12,15 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <config.h>
#include <string>
#include <gtest/gtest.h>
#include <cc/data.h>
#include <asiodns/asiodns.h>
#include <asiolink/asiolink.h>
#include <resolver/resolver.h>
@@ -29,7 +32,8 @@
using namespace std;
using namespace isc::data;
using namespace isc::testutils;
using namespace asiolink;
using namespace isc::asiodns;
using namespace isc::asiolink;
using isc::UnitTestUtil;
namespace {

View File

@@ -41,6 +41,7 @@
// Class for endpoint checks. The family of the endpoint is set in the
// constructor; the address family by the string provided for the address.
namespace isc {
namespace asiolink {
class GenericEndpoint : public IOEndpoint {
@@ -73,13 +74,14 @@ private:
short protocol_; // Protocol of the endpoint
};
}
}
using namespace asio::ip;
using namespace isc::dns;
using namespace rdata;
using namespace isc::dns::rdata::generic;
using namespace isc::dns::rdata::in;
using namespace asiolink;
using namespace isc::asiolink;
// Test class

View File

@@ -0,0 +1,18 @@
SUBDIRS = tests
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CXXFLAGS = $(B10_CXXFLAGS)
if USE_STATIC_LINK
AM_LDFLAGS = -static
endif
pkglibexecdir = $(libexecdir)/@PACKAGE@
CLEANFILES = *.gcno *.gcda
pkglibexec_PROGRAMS = b10-sockcreator
b10_sockcreator_SOURCES = sockcreator.cc sockcreator.h main.cc
b10_sockcreator_LDADD = $(top_builddir)/src/lib/util/io/libutil_io.la

View File

@@ -0,0 +1,49 @@
The socket creator
==================
The only thing we need higher rights than standard user is binding sockets to
ports lower than 1024. So we will have a separate process that keeps the
rights, while the rests drop them for security reasons.
This process is the socket creator. Its goal is to be as simple as possible
and to contain as little code as possible to minimise the amount of code
running with higher privileges (to minimize the number of bugs and make
checking/auditing it easier). It uses low-level OS API instead of some
fancy library for that reason. It has only fixed-length reads so there's no
place for buffer overruns.
Protocol
--------
It talks with whoever started it by its stdin/stdout. It reads simple
binary protocol from stdin and does what the commands ask. Command is a single
byte (usually from the printable range, so it is easier to debug and guess
what it does), followed by parameters.
Note that as send_fd and recv_fd works only with unix domain socket, it's stdio
must be a socket, not pipe.
* 'T': It has no parameters. It asks the socket creator to terminate.
* 'S' 'U|T' '4|6' port address: Asks it to create a port. First parameter
tels the socket type (either UDP or TCP). The second one is address family
(either IPv4 or IPv6). Then there's 2 bytes of the port number, in the
network byte order. The last one is either 4 or 16 bytes of address, as
they would be passed to bind (note that both parameters are already prepared,
like hton called on them).
The answer to this is either 'S' directly followed by the socket (using
sendmsg) if it is successful. If it fails, 'E' is returned instead, followed
by either 'S' or 'B' (either socket() or bind() call failed). Then there is
one int (architecture-dependent length and endianess), which is the errno
value after the failure.
The creator may also send these messages at any time (but not in the middle
of another message):
* 'F': A fatal error has been detected. It is followed by one byte of error
condition code and then the creator terminates with non-zero status.
The conditions are:
* 'I': Invalid input (eg. someone sent a wrong letter and it does not
understand it).

View File

@@ -0,0 +1,26 @@
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include "sockcreator.h"
using namespace isc::socket_creator;
int
main() {
/*
* TODO Maybe use some OS-specific caps interface and drop everything
* but ability to bind ports? It would be nice.
*/
return run(0, 1); // Read commands from stdin, output to stdout
}

View File

@@ -0,0 +1,151 @@
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include "sockcreator.h"
#include <util/io/fd.h>
#include <unistd.h>
#include <cerrno>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
using namespace isc::util::io;
namespace isc {
namespace socket_creator {
int
get_sock(const int type, struct sockaddr *bind_addr, const socklen_t addr_len)
{
int sock(socket(bind_addr->sa_family, type, 0));
if (sock == -1) {
return -1;
}
if (bind(sock, bind_addr, addr_len) == -1) {
return -2;
}
return sock;
}
// These are macros so they can exit the function
#define READ(WHERE, HOW_MANY) do { \
size_t how_many = (HOW_MANY); \
if (read_data(input_fd, (WHERE), how_many) < how_many) { \
return 1; \
} \
} while (0)
#define WRITE(WHAT, HOW_MANY) do { \
if (!write_data(output_fd, (WHAT), (HOW_MANY))) { \
return 2; \
} \
} while (0)
#define DEFAULT \
default: /* Unrecognized part of protocol */ \
WRITE("FI", 2); \
return 3;
int
run(const int input_fd, const int output_fd, const get_sock_t get_sock,
const send_fd_t send_fd)
{
for (;;) {
// Read the command
char command;
READ(&command, 1);
switch (command) {
case 'T': // The "terminate" command
return 0;
case 'S': { // Create a socket
// Read what type of socket they want
char type[2];
READ(type, 2);
// Read the address they ask for
struct sockaddr *addr(NULL);
size_t addr_len(0);
struct sockaddr_in addr_in;
struct sockaddr_in6 addr_in6;
switch (type[1]) { // The address family
/*
* Here are some casts. They are required by C++ and
* the low-level interface (they are implicit in C).
*/
case '4':
addr = static_cast<struct sockaddr *>(
static_cast<void *>(&addr_in));
addr_len = sizeof addr_in;
memset(&addr_in, 0, sizeof addr_in);
addr_in.sin_family = AF_INET;
READ(static_cast<char *>(static_cast<void *>(
&addr_in.sin_port)), 2);
READ(static_cast<char *>(static_cast<void *>(
&addr_in.sin_addr.s_addr)), 4);
break;
case '6':
addr = static_cast<struct sockaddr *>(
static_cast<void *>(&addr_in6));
addr_len = sizeof addr_in6;
memset(&addr_in6, 0, sizeof addr_in6);
addr_in6.sin6_family = AF_INET6;
READ(static_cast<char *>(static_cast<void *>(
&addr_in6.sin6_port)), 2);
READ(static_cast<char *>(static_cast<void *>(
&addr_in6.sin6_addr.s6_addr)), 16);
break;
DEFAULT
}
int sock_type;
switch (type[0]) { // Translate the type
case 'T':
sock_type = SOCK_STREAM;
break;
case 'U':
sock_type = SOCK_DGRAM;
break;
DEFAULT
}
int result(get_sock(sock_type, addr, addr_len));
if (result >= 0) { // We got the socket
WRITE("S", 1);
// FIXME: Check the output and write a test for it
send_fd(output_fd, result);
} else {
WRITE("E", 1);
switch (result) {
case -1:
WRITE("S", 1);
break;
case -2:
WRITE("B", 1);
break;
default:
return 4;
}
int error(errno);
WRITE(static_cast<char *>(static_cast<void *>(&error)),
sizeof error);
}
break;
}
DEFAULT
}
}
}
} // End of the namespaces
}

View File

@@ -0,0 +1,100 @@
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
/**
* \file sockcreator.h
* \short Socket creator functionality.
*
* This module holds the functionality of the socket creator. It is
* a separate module from main to ease up the tests.
*/
#ifndef __SOCKCREATOR_H
#define __SOCKCREATOR_H 1
#include <util/io/fd_share.h>
#include <sys/types.h>
#include <sys/socket.h>
namespace isc {
namespace socket_creator {
/**
* \short Create a socket and bind it.
*
* This is just a bundle of socket() and bind() calls. The sa_family of
* bind_addr is used to determine the domain of the socket.
*
* \return The file descriptor of the newly created socket, if everything
* goes well. A negative number is returned if an error occurs -
* -1 if the socket() call fails or -2 if bind() fails. In case of error,
* errno is set (or better, left intact from socket() or bind()).
* \param type The type of socket to create (SOCK_STREAM, SOCK_DGRAM, etc).
* \param bind_addr The address to bind.
* \param addr_len The actual length of bind_addr.
*/
int
get_sock(const int type, struct sockaddr *bind_addr, const socklen_t addr_len);
/**
* Type of the get_sock function, to pass it as parameter.
*/
typedef
int
(*get_sock_t)(const int, struct sockaddr *, const socklen_t);
/**
* Type of the send_fd() function, so it can be passed as a parameter.
*/
typedef
int
(*send_fd_t)(const int, const int);
/**
* \short Infinite loop parsing commands and returning the sockets.
*
* This reads commands and socket descriptions from the input_fd
* file descriptor, creates sockets and writes the results (socket or
* error) to output_fd.
*
* Current errors are:
* - 1: Read error
* - 2: Write error
* - 3: Protocol error (unknown command, etc)
* - 4: Some internal inconsistency detected
*
* It terminates either if a command asks it to or when unrecoverable
* error happens.
*
* \return Like a return value of a main - 0 means everything OK, anything
* else is error.
* \param input_fd Here is where it reads the commads.
* \param output_fd Here is where it writes the results.
* \param get_sock_fun The function that is used to create the sockets.
* This should be left on the default value, the parameter is here
* for testing purposes.
* \param send_fd_fun The function that is used to send the socket over
* a file descriptor. This should be left on the default value, it is
* here for testing purposes.
*/
int
run(const int input_fd, const int output_fd,
const get_sock_t get_sock_fun = get_sock,
const send_fd_t send_fd_fun = isc::util::io::send_fd);
} // End of the namespaces
}
#endif // __SOCKCREATOR_H

View File

@@ -0,0 +1,25 @@
CLEANFILES = *.gcno *.gcda
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CXXFLAGS = $(B10_CXXFLAGS)
if USE_STATIC_LINK
AM_LDFLAGS = -static
endif
TESTS =
if HAVE_GTEST
TESTS += run_unittests
run_unittests_SOURCES = ../sockcreator.cc ../sockcreator.h
run_unittests_SOURCES += sockcreator_tests.cc
run_unittests_SOURCES += run_unittests.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(top_builddir)/src/lib/util/io/libutil_io.la
run_unittests_LDADD += \
$(top_builddir)/src/lib/util/unittests/libutil_unittests.la
endif
noinst_PROGRAMS = $(TESTS)

View File

@@ -0,0 +1,22 @@
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
int
main(int argc, char *argv[]) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@@ -0,0 +1,273 @@
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include "../sockcreator.h"
#include <util/unittests/fork.h>
#include <util/io/fd.h>
#include <gtest/gtest.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <cstring>
#include <cerrno>
using namespace isc::socket_creator;
using namespace isc::util::unittests;
using namespace isc::util::io;
namespace {
/*
* Generic version of the creation of socket test. It just tries to
* create the socket and checks the result is not negative (eg.
* it is valid descriptor) and that it can listen.
*
* This is a macro so ASSERT_* does abort the TEST, not just the
* function inside.
*/
#define TEST_ANY_CREATE(SOCK_TYPE, ADDR_TYPE, ADDR_FAMILY, FAMILY_FIELD, \
ADDR_SET, CHECK_SOCK) \
do { \
/*
* This should create an address that binds on all interfaces
* and lets the OS choose a free port.
*/ \
struct ADDR_TYPE addr; \
memset(&addr, 0, sizeof addr); \
ADDR_SET(addr); \
addr.FAMILY_FIELD = ADDR_FAMILY; \
struct sockaddr *addr_ptr = static_cast<struct sockaddr *>( \
static_cast<void *>(&addr)); \
\
int socket = get_sock(SOCK_TYPE, addr_ptr, sizeof addr); \
/* Provide even nice error message. */ \
ASSERT_GE(socket, 0) << "Couldn't create a socket of type " \
#SOCK_TYPE " and family " #ADDR_FAMILY ", failed with " \
<< socket << " and error " << strerror(errno); \
CHECK_SOCK(ADDR_TYPE, socket); \
EXPECT_EQ(0, close(socket)); \
} while (0)
// Just helper macros
#define INADDR_SET(WHAT) do { WHAT.sin_addr.s_addr = INADDR_ANY; } while (0)
#define IN6ADDR_SET(WHAT) do { WHAT.sin6_addr = in6addr_loopback; } while (0)
// If the get_sock returned something useful, listen must work
#define TCP_CHECK(UNUSED, SOCKET) do { \
EXPECT_EQ(0, listen(SOCKET, 1)); \
} while (0)
// More complicated with UDP, so we send a packet to ourselfs and se if it
// arrives
#define UDP_CHECK(ADDR_TYPE, SOCKET) do { \
struct ADDR_TYPE addr; \
memset(&addr, 0, sizeof addr); \
struct sockaddr *addr_ptr = static_cast<struct sockaddr *>( \
static_cast<void *>(&addr)); \
\
socklen_t len = sizeof addr; \
ASSERT_EQ(0, getsockname(SOCKET, addr_ptr, &len)); \
ASSERT_EQ(5, sendto(SOCKET, "test", 5, 0, addr_ptr, sizeof addr)) << \
"Send failed with error " << strerror(errno) << " on socket " << \
SOCKET; \
char buffer[5]; \
ASSERT_EQ(5, recv(SOCKET, buffer, 5, 0)) << \
"Recv failed with error " << strerror(errno) << " on socket " << \
SOCKET; \
EXPECT_STREQ("test", buffer); \
} while (0)
/*
* Several tests to ensure we can create the sockets.
*/
TEST(get_sock, udp4_create) {
TEST_ANY_CREATE(SOCK_DGRAM, sockaddr_in, AF_INET, sin_family, INADDR_SET,
UDP_CHECK);
}
TEST(get_sock, tcp4_create) {
TEST_ANY_CREATE(SOCK_STREAM, sockaddr_in, AF_INET, sin_family, INADDR_SET,
TCP_CHECK);
}
TEST(get_sock, udp6_create) {
TEST_ANY_CREATE(SOCK_DGRAM, sockaddr_in6, AF_INET6, sin6_family,
IN6ADDR_SET, UDP_CHECK);
}
TEST(get_sock, tcp6_create) {
TEST_ANY_CREATE(SOCK_STREAM, sockaddr_in6, AF_INET6, sin6_family,
IN6ADDR_SET, TCP_CHECK);
}
/*
* Try to ask the get_sock function some nonsense and test if it
* is able to report error.
*/
TEST(get_sock, fail_with_nonsense) {
struct sockaddr addr;
memset(&addr, 0, sizeof addr);
ASSERT_LT(get_sock(0, &addr, sizeof addr), 0);
}
/*
* Helper functions to pass to run during testing.
*/
int
get_sock_dummy(const int type, struct sockaddr *addr, const socklen_t)
{
int result(0);
int port(0);
/*
* We encode the type and address family into the int and return it.
* Lets ignore the port and address for now
* First bit is 1 if it is known type. Second tells if TCP or UDP.
* The familly is similar - third bit is known address family,
* the fourth is the family.
*/
switch (type) {
case SOCK_STREAM:
result += 1;
break;
case SOCK_DGRAM:
result += 3;
break;
}
switch (addr->sa_family) {
case AF_INET:
result += 4;
port = static_cast<struct sockaddr_in *>(
static_cast<void *>(addr))->sin_port;
break;
case AF_INET6:
result += 12;
port = static_cast<struct sockaddr_in6 *>(
static_cast<void *>(addr))->sin6_port;
break;
}
/*
* The port should be 0xffff. If it's not, we change the result.
* The port of 0xbbbb means bind should fail and 0xcccc means
* socket should fail.
*/
if (port != 0xffff) {
errno = 0;
if (port == 0xbbbb) {
return -2;
} else if (port == 0xcccc) {
return -1;
} else {
result += 16;
}
}
return result;
}
int
send_fd_dummy(const int destination, const int what)
{
/*
* Make sure it is 1 byte so we know the length. We do not use more during
* the test anyway.
*/
char fd_data(what);
if (!write_data(destination, &fd_data, 1)) {
return -1;
} else {
return 0;
}
}
/*
* Generic test that it works, with various inputs and outputs.
* It uses different functions to create the socket and send it and pass
* data to it and check it returns correct data back, to see if the run()
* parses the commands correctly.
*/
void run_test(const char *input_data, const size_t input_size,
const char *output_data, const size_t output_size,
bool should_succeed = true)
{
// Prepare the input feeder and output checker processes
int input_fd(0), output_fd(0);
pid_t input(provide_input(&input_fd, input_data, input_size)),
output(check_output(&output_fd, output_data, output_size));
ASSERT_NE(-1, input) << "Couldn't start input feeder";
ASSERT_NE(-1, output) << "Couldn't start output checker";
// Run the body
int result(run(input_fd, output_fd, get_sock_dummy, send_fd_dummy));
// Close the pipes
close(input_fd);
close(output_fd);
// Did it run well?
if (should_succeed) {
EXPECT_EQ(0, result);
} else {
EXPECT_NE(0, result);
}
// Check the subprocesses say everything is OK too
EXPECT_TRUE(process_ok(input));
EXPECT_TRUE(process_ok(output));
}
/*
* Check it terminates successfully when asked to.
*/
TEST(run, terminate) {
run_test("T", 1, NULL, 0);
}
/*
* Check it rejects incorrect input.
*/
TEST(run, bad_input) {
run_test("XXX", 3, "FI", 2, false);
}
/*
* Check it correctly parses queries to create sockets.
*/
TEST(run, sockets) {
run_test(
"SU4\xff\xff\0\0\0\0" // This has 9 bytes
"ST4\xff\xff\0\0\0\0" // This has 9 bytes
"ST6\xff\xff\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" // This has 21 bytes
"SU6\xff\xff\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" // This has 21 bytes
"T", 61,
"S\x07S\x05S\x0dS\x0f", 8);
}
/*
* Check if failures of get_socket are handled correctly.
*/
TEST(run, bad_sockets) {
// We need to construct the answer, but it depends on int length.
size_t int_len(sizeof(int));
size_t result_len(4 + 2 * int_len);
char result[4 + sizeof(int) * 2];
// Both errno parts should be 0
memset(result, 0, result_len);
// Fill the 2 control parts
strcpy(result, "EB");
strcpy(result + 2 + int_len, "ES");
// Run the test
run_test(
"SU4\xbb\xbb\0\0\0\0"
"SU4\xcc\xcc\0\0\0\0"
"T", 19,
result, result_len);
}
}

View File

@@ -1,7 +1,7 @@
'\" t
.\" Title: b10-stats
.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
.\" Generator: DocBook XSL Stylesheets v1.76.1 <http://docbook.sf.net/>
.\" Date: Oct 15, 2010
.\" Manual: BIND10
.\" Source: BIND10
@@ -9,6 +9,15 @@
.\"
.TH "B10\-STATS" "8" "Oct 15, 2010" "BIND10" "BIND10"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" http://bugs.debian.org/507673
.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
.\" disable hyphenation
@@ -35,6 +44,11 @@ with other modules like
\fBbind10\fR,
\fBb10\-auth\fR
and so on\&. It waits for coming data from other modules, then other modules send data to stats module periodically\&. Other modules send stats data to stats module independently from implementation of stats module, so the frequency of sending data may not be constant\&. Stats module collects data and aggregates it\&.
\fBb10\-stats\fR
invokes "sendstats" command for
\fBbind10\fR
after its initial starting because it\*(Aqs sure to collect statistics data from
\fBbind10\fR\&.
.SH "OPTIONS"
.PP
The arguments are as follows:
@@ -49,7 +63,8 @@ switches to verbose mode\&. It sends verbose messages to STDOUT\&.
.PP
/usr/local/share/bind10\-devel/stats\&.spec
\(em This is a spec file for
\fBb10\-stats\fR\&. It contains definitions of statistics items of BIND 10 and commands received vi bindctl\&.
\fBb10\-stats\fR\&. It contains definitions of statistics items of BIND 10 and commands received via
bindctl(1)\&.
.SH "SEE ALSO"
.PP

View File

@@ -2,7 +2,7 @@
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
[<!ENTITY mdash "&#8212;">]>
<!--
- Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
- Copyright (C) 2010,2011 Internet Systems Consortium, Inc. ("ISC")
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
@@ -64,7 +64,9 @@
send stats data to stats module independently from
implementation of stats module, so the frequency of sending data
may not be constant. Stats module collects data and aggregates
it.
it. <command>b10-stats</command> invokes "sendstats" command
for <command>bind10</command> after its initial starting because it's
sure to collect statistics data from <command>bind10</command>.
</para>
</refsect1>

View File

@@ -1,6 +1,6 @@
#!@PYTHON@
# Copyright (C) 2010 Internet Systems Consortium.
# Copyright (C) 2010, 2011 Internet Systems Consortium.
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
@@ -15,8 +15,6 @@
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
__version__ = "$Revision$"
import sys; sys.path.append ('@@PYTHONPATH@@')
import os
import signal
@@ -220,7 +218,13 @@ class CCSessionListener(Listener):
self.stats_data['stats.start_time'] = get_datetime()
self.stats_data['stats.last_update_time'] = get_datetime()
self.stats_data['stats.lname'] = self.session.lname
return self.cc_session.start()
self.cc_session.start()
# request Bob to send statistics data
if self.verbose:
sys.stdout.write("[b10-stats] request Bob to send statistics data\n")
cmd = isc.config.ccsession.create_command("sendstats", None)
seq = self.session.group_sendmsg(cmd, 'Boss')
self.session.group_recvmsg(True, seq)
def stop(self):
"""

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2010 Internet Systems Consortium.
# Copyright (C) 2010,2011 Internet Systems Consortium.
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
@@ -13,8 +13,6 @@
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
__version__ = "$Revision$"
#
# Tests for the stats module
#
@@ -504,6 +502,13 @@ class TestStats(unittest.TestCase):
self.assertEqual(result_ok(),
self.session.get_message("Stats", None))
def test_for_boss(self):
last_queue = self.session.old_message_queue.pop()
self.assertEqual(
last_queue.msg, {'command': ['sendstats']})
self.assertEqual(
last_queue.env['group'], 'Boss')
class TestStats2(unittest.TestCase):
def setUp(self):

View File

@@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS)
# required by loadable python modules.
LIBRARY_PATH_PLACEHOLDER =
if SET_ENV_LIBRARY_PATH
LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/xfr/.libs:$$$(ENV_LIBRARY_PATH)
LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$$$(ENV_LIBRARY_PATH)
endif
# test using command-line arguments, so use check-local target instead of TESTS
@@ -18,7 +18,7 @@ if ENABLE_PYTHON_COVERAGE
endif
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
env PYTHONPATH=$(abs_top_builddir)/src/bin/xfrout:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/xfr/.libs \
env PYTHONPATH=$(abs_top_builddir)/src/bin/xfrout:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/util/io/.libs \
$(LIBRARY_PATH_PLACEHOLDER) \
$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
done

View File

@@ -21,6 +21,7 @@ import os
from isc.cc.session import *
from pydnspp import *
from xfrout import *
import xfrout
# our fake socket, where we can read and insert messages
class MySocket():
@@ -433,5 +434,36 @@ class TestUnixSockServer(unittest.TestCase):
sys.stdout = old_stdout
os.rmdir(dir_name)
class TestInitialization(unittest.TestCase):
def setEnv(self, name, value):
if value is None:
if name in os.environ:
del os.environ[name]
else:
os.environ[name] = value
def setUp(self):
self._oldSocket = os.getenv("BIND10_XFROUT_SOCKET_FILE")
self._oldFromBuild = os.getenv("B10_FROM_BUILD")
def tearDown(self):
self.setEnv("B10_FROM_BUILD", self._oldFromBuild)
self.setEnv("BIND10_XFROUT_SOCKET_FILE", self._oldSocket)
# Make sure even the computed values are back
xfrout.init_paths()
def testNoEnv(self):
self.setEnv("B10_FROM_BUILD", None)
self.setEnv("BIND10_XFROUT_SOCKET_FILE", None)
xfrout.init_paths()
self.assertEqual(xfrout.UNIX_SOCKET_FILE,
"@@LOCALSTATEDIR@@/auth_xfrout_conn")
def testProvidedSocket(self):
self.setEnv("B10_FROM_BUILD", None)
self.setEnv("BIND10_XFROUT_SOCKET_FILE", "The/Socket/File")
xfrout.init_paths()
self.assertEqual(xfrout.UNIX_SOCKET_FILE, "The/Socket/File")
if __name__== "__main__":
unittest.main()

View File

@@ -37,15 +37,19 @@ from optparse import OptionParser, OptionValueError
from isc.util import socketserver_mixin
try:
from libxfr_python import *
from libutil_io_python import *
from pydnspp import *
except ImportError as e:
# C++ loadable module may not be installed; even so the xfrout process
# must keep running, so we warn about it and move forward.
sys.stderr.write('[b10-xfrout] failed to import DNS or XFR module: %s\n' % str(e))
sys.stderr.write('[b10-xfrout] failed to import DNS or isc.util.io module: %s\n' % str(e))
isc.util.process.rename()
def init_paths():
global SPECFILE_PATH
global AUTH_SPECFILE_PATH
global UNIX_SOCKET_FILE
if "B10_FROM_BUILD" in os.environ:
SPECFILE_PATH = os.environ["B10_FROM_BUILD"] + "/src/bin/xfrout"
AUTH_SPECFILE_PATH = os.environ["B10_FROM_BUILD"] + "/src/bin/auth"
@@ -59,8 +63,13 @@ else:
DATAROOTDIR = "@datarootdir@"
SPECFILE_PATH = "@datadir@/@PACKAGE@".replace("${datarootdir}", DATAROOTDIR).replace("${prefix}", PREFIX)
AUTH_SPECFILE_PATH = SPECFILE_PATH
if "BIND10_XFROUT_SOCKET_FILE" in os.environ:
UNIX_SOCKET_FILE = os.environ["BIND10_XFROUT_SOCKET_FILE"]
else:
UNIX_SOCKET_FILE = "@@LOCALSTATEDIR@@/auth_xfrout_conn"
init_paths()
SPECFILE_LOCATION = SPECFILE_PATH + "/xfrout.spec"
AUTH_SPECFILE_LOCATION = AUTH_SPECFILE_PATH + os.sep + "auth.spec"
MAX_TRANSFERS_OUT = 10
@@ -376,7 +385,7 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
# This may happen when one xfrout process try to connect to
# xfrout unix socket server, to check whether there is another
# xfrout running.
if sock_fd == XFR_FD_RECEIVE_FAIL:
if sock_fd == FD_COMM_ERROR:
self._log.log_message("error", "Failed to receive the file descriptor for XFR connection")
return

View File

@@ -22,10 +22,10 @@ import tempfile
from zonemgr import *
ZONE_NAME_CLASS1_IN = ("sd.cn.", "IN")
ZONE_NAME_CLASS2_CH = ("tw.cn", "CH")
ZONE_NAME_CLASS2_CH = ("tw.cn.", "CH")
ZONE_NAME_CLASS3_IN = ("example.com", "IN")
ZONE_NAME_CLASS1_CH = ("sd.cn.", "CH")
ZONE_NAME_CLASS2_IN = ("tw.cn", "IN")
ZONE_NAME_CLASS2_IN = ("tw.cn.", "IN")
MAX_TRANSFER_TIMEOUT = 14400
LOWERBOUND_REFRESH = 10
@@ -46,9 +46,13 @@ class MySession():
def group_recvmsg(self, nonblock, seq):
return None, None
class MyZonemgrRefresh(ZonemgrRefresh):
def __init__(self):
class FakeConfig:
def __init__(self):
self.zone_list = []
self.set_zone_list_from_name_classes([ZONE_NAME_CLASS1_IN,
ZONE_NAME_CLASS2_CH])
def set_zone_list_from_name_classes(self, zones):
self.zone_list = map(lambda nc: {"name": nc[0], "class": nc[1]}, zones)
def get(self, name):
if name == 'lowerbound_refresh':
return LOWERBOUND_REFRESH
@@ -58,9 +62,27 @@ class MyZonemgrRefresh(ZonemgrRefresh):
return MAX_TRANSFER_TIMEOUT
elif name == 'jitter_scope':
return JITTER_SCOPE
elif name == 'secondary_zones':
return self.zone_list
else:
raise ValueError('Uknown config option')
class MyZonemgrRefresh(ZonemgrRefresh):
def __init__(self):
self._master_socket, self._slave_socket = socket.socketpair()
self._zonemgr_refresh_info = {}
def get_zone_soa(zone_name, db_file):
if zone_name == 'sd.cn.':
return (1, 2, 'sd.cn.', 'cn.sd.', 21600, 'SOA', None,
'a.dns.cn. root.cnnic.cn. 2009073106 7200 3600 2419200 21600')
elif zone_name == 'tw.cn.':
return (1, 2, 'tw.cn.', 'cn.sd.', 21600, 'SOA', None,
'a.dns.cn. root.cnnic.cn. 2009073112 7200 3600 2419200 21600')
else:
return None
sqlite3_ds.get_zone_soa = get_zone_soa
ZonemgrRefresh.__init__(self, MySession(), "initdb.file",
self._slave_socket, FakeConfig())
current_time = time.time()
@@ -70,7 +92,7 @@ class MyZonemgrRefresh(ZonemgrRefresh):
'next_refresh_time': current_time + 6500,
'zone_soa_rdata': 'a.dns.cn. root.cnnic.cn. 2009073105 7200 3600 2419200 21600',
'zone_state': 0},
('tw.cn', 'CH'): {
('tw.cn.', 'CH'): {
'last_refresh_time': current_time,
'next_refresh_time': current_time + 6900,
'zone_soa_rdata': 'a.dns.cn. root.cnnic.cn. 2009073112 7200 3600 2419200 21600',
@@ -272,28 +294,6 @@ class TestZonemgrRefresh(unittest.TestCase):
ZONE_NAME_CLASS1_IN)
sqlite3_ds.get_zone_soa = old_get_zone_soa
def test_build_zonemgr_refresh_info(self):
soa_rdata = 'a.dns.cn. root.cnnic.cn. 2009073106 1800 900 2419200 21600'
def get_zones_info(db_file):
return [("sd.cn.", "IN")]
def get_zone_soa(zone_name, db_file):
return (1, 2, 'sd.cn.', 'cn.sd.', 21600, 'SOA', None,
'a.dns.cn. root.cnnic.cn. 2009073106 1800 900 2419200 21600')
sqlite3_ds.get_zones_info = get_zones_info
sqlite3_ds.get_zone_soa = get_zone_soa
self.zone_refresh._zonemgr_refresh_info = {}
self.zone_refresh._build_zonemgr_refresh_info()
self.assertEqual(1, len(self.zone_refresh._zonemgr_refresh_info))
zone_soa_rdata = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_soa_rdata"]
self.assertEqual(soa_rdata, zone_soa_rdata)
self.assertEqual(ZONE_OK, self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_state"])
self.assertTrue("last_refresh_time" in self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN].keys())
self.assertTrue("next_refresh_time" in self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN].keys())
def test_zone_handle_notify(self):
self.zone_refresh.zone_handle_notify(ZONE_NAME_CLASS1_IN,"127.0.0.1")
notify_master = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["notify_master"]
@@ -356,7 +356,7 @@ class TestZonemgrRefresh(unittest.TestCase):
'next_refresh_time': time1 + 7200,
'zone_soa_rdata': 'a.dns.cn. root.cnnic.cn. 2009073105 7200 3600 2419200 21600',
'zone_state': ZONE_OK},
("tw.cn","CH"):{
("tw.cn.","CH"):{
'last_refresh_time': time1 - 7200,
'next_refresh_time': time1,
'refresh_timeout': time1 + MAX_TRANSFER_TIMEOUT,
@@ -424,7 +424,8 @@ class TestZonemgrRefresh(unittest.TestCase):
"lowerbound_refresh" : 60,
"lowerbound_retry" : 30,
"max_transfer_timeout" : 19800,
"jitter_scope" : 0.25
"jitter_scope" : 0.25,
"secondary_zones": []
}
self.zone_refresh.update_config_data(config_data)
self.assertEqual(60, self.zone_refresh._lowerbound_refresh)
@@ -440,6 +441,31 @@ class TestZonemgrRefresh(unittest.TestCase):
self.zone_refresh.shutdown()
self.assertFalse(listener.is_alive())
def test_secondary_zones(self):
"""Test that we can modify the list of secondary zones"""
config = FakeConfig()
config.zone_list = []
# First, remove everything
self.zone_refresh.update_config_data(config)
self.assertEqual(self.zone_refresh._zonemgr_refresh_info, {})
# Put something in
config.set_zone_list_from_name_classes([ZONE_NAME_CLASS1_IN])
self.zone_refresh.update_config_data(config)
self.assertTrue(("sd.cn.", "IN") in
self.zone_refresh._zonemgr_refresh_info)
# This one does not exist
config.set_zone_list_from_name_classes(["example.net", "CH"])
self.assertRaises(ZonemgrException,
self.zone_refresh.update_config_data, config)
# So it should not affect the old ones
self.assertTrue(("sd.cn.", "IN") in
self.zone_refresh._zonemgr_refresh_info)
# Make sure it works even when we "accidentally" forget the final dot
config.set_zone_list_from_name_classes([("sd.cn", "IN")])
self.zone_refresh.update_config_data(config)
self.assertTrue(("sd.cn.", "IN") in
self.zone_refresh._zonemgr_refresh_info)
def tearDown(self):
sys.stderr= self.stderr_backup
@@ -467,7 +493,8 @@ class MyZonemgr(Zonemgr):
"lowerbound_refresh" : 10,
"lowerbound_retry" : 5,
"max_transfer_timeout" : 14400,
"jitter_scope" : 0.1
"jitter_scope" : 0.1,
"secondary_zones": []
}
def _start_zone_refresh_timer(self):
@@ -483,9 +510,11 @@ class TestZonemgr(unittest.TestCase):
"lowerbound_refresh" : 60,
"lowerbound_retry" : 30,
"max_transfer_timeout" : 14400,
"jitter_scope" : 0.1
"jitter_scope" : 0.1,
"secondary_zones": []
}
self.zonemgr.config_handler(config_data1)
self.assertEqual(self.zonemgr.config_handler(config_data1),
{"result": [0]})
self.assertEqual(config_data1, self.zonemgr._config_data)
config_data2 = {"zone_name" : "sd.cn.", "port" : "53", "master" : "192.168.1.1"}
self.zonemgr.config_handler(config_data2)
@@ -494,6 +523,15 @@ class TestZonemgr(unittest.TestCase):
config_data3 = {"jitter_scope" : 0.7}
self.zonemgr.config_handler(config_data3)
self.assertEqual(0.5, self.zonemgr._config_data.get("jitter_scope"))
# The zone doesn't exist in database, it should be rejected
self.zonemgr._zone_refresh = ZonemgrRefresh(None, "initdb.file", None,
config_data1)
config_data1["secondary_zones"] = [{"name": "nonexistent.example",
"class": "IN"}]
self.assertNotEqual(self.zonemgr.config_handler(config_data1),
{"result": [0]})
# As it is rejected, the old value should be kept
self.assertEqual(0.5, self.zonemgr._config_data.get("jitter_scope"))
def test_get_db_file(self):
self.assertEqual("initdb.file", self.zonemgr.get_db_file())

View File

@@ -100,9 +100,8 @@ class ZonemgrRefresh:
self._cc = cc
self._check_sock = slave_socket
self._db_file = db_file
self.update_config_data(config_data)
self._zonemgr_refresh_info = {}
self._build_zonemgr_refresh_info()
self.update_config_data(config_data)
self._running = False
def _random_jitter(self, max, jitter):
@@ -148,16 +147,13 @@ class ZonemgrRefresh:
def _zone_not_exist(self, zone_name_class):
""" Zone doesn't belong to zonemgr"""
if zone_name_class in self._zonemgr_refresh_info.keys():
return False
return True
return not zone_name_class in self._zonemgr_refresh_info
def zone_refresh_success(self, zone_name_class):
"""Update zone info after zone refresh success"""
if (self._zone_not_exist(zone_name_class)):
raise ZonemgrException("[b10-zonemgr] Zone (%s, %s) doesn't "
"belong to zonemgr" % zone_name_class)
return
self.zonemgr_reload_zone(zone_name_class)
self._set_zone_refresh_timer(zone_name_class)
self._set_zone_state(zone_name_class, ZONE_OK)
@@ -168,7 +164,6 @@ class ZonemgrRefresh:
if (self._zone_not_exist(zone_name_class)):
raise ZonemgrException("[b10-zonemgr] Zone (%s, %s) doesn't "
"belong to zonemgr" % zone_name_class)
return
# Is zone expired?
if (self._zone_is_expired(zone_name_class)):
self._set_zone_state(zone_name_class, ZONE_EXPIRED)
@@ -181,7 +176,6 @@ class ZonemgrRefresh:
if (self._zone_not_exist(zone_name_class)):
raise ZonemgrException("[b10-zonemgr] Notified zone (%s, %s) "
"doesn't belong to zonemgr" % zone_name_class)
return
self._set_zone_notifier_master(zone_name_class, master)
self._set_zone_notify_timer(zone_name_class)
@@ -192,6 +186,7 @@ class ZonemgrRefresh:
def zonemgr_add_zone(self, zone_name_class):
""" Add a zone into zone manager."""
log_msg("Loading zone (%s, %s)" % zone_name_class)
zone_info = {}
zone_soa = sqlite3_ds.get_zone_soa(str(zone_name_class[0]), self._db_file)
if not zone_soa:
@@ -203,14 +198,6 @@ class ZonemgrRefresh:
float(zone_soa[7].split(" ")[REFRESH_OFFSET])
self._zonemgr_refresh_info[zone_name_class] = zone_info
def _build_zonemgr_refresh_info(self):
""" Build zonemgr refresh info map."""
log_msg("Start loading zone into zonemgr.")
for zone_name, zone_class in sqlite3_ds.get_zones_info(self._db_file):
zone_name_class = (zone_name, zone_class)
self.zonemgr_add_zone(zone_name_class)
log_msg("Finish loading zone into zonemgr.")
def _zone_is_expired(self, zone_name_class):
"""Judge whether a zone is expired or not."""
zone_expired_time = float(self._get_zone_soa_rdata(zone_name_class).split(" ")[EXPIRED_OFFSET])
@@ -415,6 +402,32 @@ class ZonemgrRefresh:
def update_config_data(self, new_config):
""" update ZonemgrRefresh config """
backup = self._zonemgr_refresh_info.copy()
try:
required = {}
# Add new zones
for secondary_zone in new_config.get('secondary_zones'):
name = secondary_zone['name']
# Be tolerant to sclerotic users who forget the final dot
if name[-1] != '.':
name = name + '.'
name_class = (name, secondary_zone['class'])
required[name_class] = True
# Add it only if it isn't there already
if not name_class in self._zonemgr_refresh_info:
self.zonemgr_add_zone(name_class)
# Drop the zones that are no longer there
# Do it in two phases, python doesn't like deleting while iterating
to_drop = []
for old_zone in self._zonemgr_refresh_info:
if not old_zone in required:
to_drop.append(old_zone)
for drop in to_drop:
del self._zonemgr_refresh_info[drop]
# If we are not able to find it in database, restore the original
except:
self._zonemgr_refresh_info = backup
raise
self._lowerbound_refresh = new_config.get('lowerbound_refresh')
self._lowerbound_retry = new_config.get('lowerbound_retry')
self._max_transfer_timeout = new_config.get('max_transfer_timeout')
@@ -471,27 +484,38 @@ class Zonemgr:
def config_handler(self, new_config):
""" Update config data. """
answer = create_answer(0)
ok = True
complete = self._config_data.copy()
for key in new_config:
if key not in self._config_data:
if key not in complete:
answer = create_answer(1, "Unknown config data: " + str(key))
ok = False
continue
self._config_data[key] = new_config[key]
complete[key] = new_config[key]
self._config_data_check(self._config_data)
if (self._zone_refresh):
self._zone_refresh.update_config_data(self._config_data)
self._config_data_check(complete)
if self._zone_refresh is not None:
try:
self._zone_refresh.update_config_data(complete)
except Exception as e:
answer = create_answer(1, str(e))
ok = False
if ok:
self._config_data = complete
return answer
def _config_data_check(self, config_data):
"""Check whether the new config data is valid or
not. """
not. It contains only basic logic, not full check against
database."""
# jitter should not be bigger than half of the original value
if config_data.get('jitter_scope') > 0.5:
config_data['jitter_scope'] = 0.5
log_msg("[b10-zonemgr] jitter_scope is too big, its value will "
"be set to 0.5")
def _parse_cmd_params(self, args, command):
zone_name = args.get("zone_name")
if not zone_name:

View File

@@ -25,6 +25,32 @@
"item_type": "real",
"item_optional": false,
"item_default": 0.25
},
{
"item_name": "secondary_zones",
"item_type": "list",
"item_optional": false,
"item_default": [],
"list_item_spec": {
"item_name": "secondary_zone",
"item_type": "map",
"item_optional": false,
"item_default": {},
"map_item_spec": [
{
"item_name": "class",
"item_type": "string",
"item_optional": false,
"item_default": "IN"
},
{
"item_name": "name",
"item_type": "string",
"item_optional": false,
"item_default": ""
}
]
}
}
],
"commands": [

View File

@@ -1,2 +1,2 @@
SUBDIRS = exceptions util dns cc config python xfr bench log \
asiolink nsas cache resolve testutils datasrc server_common
SUBDIRS = exceptions util dns cc config python xfr bench log asiolink \
asiodns nsas cache resolve testutils datasrc server_common

View File

@@ -0,0 +1,34 @@
SUBDIRS = . tests
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/asiolink -I$(top_builddir)/src/lib/asiolink
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/util -I$(top_builddir)/src/lib/util
AM_CXXFLAGS = $(B10_CXXFLAGS)
CLEANFILES = *.gcno *.gcda
lib_LTLIBRARIES = libasiodns.la
libasiodns_la_SOURCES = dns_answer.h
libasiodns_la_SOURCES += asiodns.h
libasiodns_la_SOURCES += asiodef.cc asiodef.h
libasiodns_la_SOURCES += dns_lookup.h
libasiodns_la_SOURCES += dns_server.h
libasiodns_la_SOURCES += dns_service.cc dns_service.h
libasiodns_la_SOURCES += tcp_server.cc tcp_server.h
libasiodns_la_SOURCES += udp_server.cc udp_server.h
libasiodns_la_SOURCES += io_fetch.cc io_fetch.h
EXTRA_DIST = asiodef.msg
# Note: the ordering matters: -Wno-... must follow -Wextra (defined in
# B10_CXXFLAGS)
libasiodns_la_CXXFLAGS = $(AM_CXXFLAGS)
if USE_CLANGPP
# Same for clang++, but we need to turn off -Werror completely.
libasiodns_la_CXXFLAGS += -Wno-error
endif
libasiodns_la_CPPFLAGS = $(AM_CPPFLAGS)
libasiodns_la_LIBADD = $(top_builddir)/src/lib/log/liblog.la

157
src/lib/asiodns/README Normal file
View File

@@ -0,0 +1,157 @@
The asiodns library is intended to provide an abstraction layer between
BIND10 modules and asiolink library.
These DNS server and client routines are written using the "stackless
coroutine" pattern invented by Chris Kohlhoff and described at
http://blog.think-async.com/2010/03/potted-guide-to-stackless-coroutines.html.
This is intended to simplify development a bit, since it allows the
routines to be written in a straightfowrard step-step-step fashion rather
than as a complex chain of separate handler functions.
Coroutine objects (i.e., UDPServer, TCPServer and IOFetch) are objects
with reenterable operator() members. When an instance of one of these
classes is called as a function, it resumes at the position where it left
off. Thus, a UDPServer can issue an asynchronous I/O call and specify
itself as the handler object; when the call completes, the UDPServer
carries on at the same position. As a result, the code can look as
if it were using synchronous, not asynchronous, I/O, providing some of
the benefit of threading but with minimal switching overhead.
So, in simplified form, the behavior of a DNS Server is:
REENTER:
while true:
YIELD packet = read_packet
FORK
if not parent:
break
# This callback informs the caller that a packet has arrived, and
# gives it a chance to update configuration, etc
SimpleCallback(packet)
YIELD answer = DNSLookup(packet, this)
response = DNSAnswer(answer)
YIELD send(response)
At each "YIELD" point, the coroutine initiates an asynchronous operation,
then pauses and turns over control to some other task on the ASIO service
queue. When the operation completes, the coroutine resumes.
DNSLookup, DNSAnswer and SimpleCallback define callback methods
used by a DNS Server to communicate with the module that called it.
They are abstract-only classes whose concrete implementations
are supplied by the calling module.
The DNSLookup callback always runs asynchronously. Concrete
implementations must be sure to call the server's "resume" method when
it is finished.
In an authoritative server, the DNSLookup implementation would examine
the query, look up the answer, then call "resume". (See the diagram
in doc/auth_process.jpg.)
In a recursive server, the DNSLookup impelemtation would initiate a
DNSQuery, which in turn would be responsible for calling the server's
"resume" method. (See the diagram in doc/recursive_process.jpg.)
A DNSQuery object is intended to handle resolution of a query over
the network when the local authoritative data sources or cache are not
sufficient. The plan is that it will make use of subsidiary DNSFetch
calls to get data from particular authoritative servers, and when it has
gotten a complete answer, it calls "resume".
In current form, however, DNSQuery is much simpler; it forwards queries
to a single upstream resolver and passes the answers back to the client.
It is constructed with the address of the forward server. Queries are
initiated with the question to ask the forward server, a buffer into
which to write the answer, and a pointer to the coroutine to be resumed
when the answer has arrived. In simplified form, the DNSQuery routine is:
REENTER:
render the question into a wire-format query packet
YIELD send(query)
YIELD response = read_packet
server->resume
Currently, DNSQuery is only implemented for UDP queries. In future work
it will be necessary to write code to fall back to TCP when circumstances
require it.
Upstream Fetches
================
Upstream fetches (queries by the resolver on behalf of a client) are made
using a slightly-modified version of the pattern described above.
Sockets
-------
First, it will be useful to understand the class hierarchy used in the
fetch logic:
IOSocket
|
IOAsioSocket
|
+-----+-----+
| |
UDPSocket TCPSocket
IOSocket is a wrapper class for a socket and is used by the authoritative
server code. It is an abstract base class, providing little more that the ability to hold the socket and to return the protocol in use.
Built on this is IOAsioSocket, which adds the open, close, asyncSend and
asyncReceive methods. This is a template class, which takes as template
argument the class of the object that will be used as the callback when the
asynchronous operation completes. This object can be of any type, but must
include an operator() method with the signature:
operator()(asio::error_code ec, size_t length)
... the two arguments being the status of the completed I/O operation and
the number of bytes transferred. (In the case of the open method, the second
argument will be zero.)
Finally, the TCPSocket and UDPSocket classes provide the body of the
asynchronous operations.
Fetch Sequence
--------------
The fetch is implemented by the IOFetch class, which takes as argument the
protocol to use. The sequence is:
REENTER:
render the question into a wire-format query packet
open() // Open socket and optionally connect
if (! synchronous) {
YIELD;
}
YIELD asyncSend(query) // Send query
do {
YIELD asyncReceive(response) // Read response
} while (! complete(response))
close() // Drop connection and close socket
server->resume
The open() method opens a socket for use. On TCP, it also makes a
connection to the remote end. So under UDP the operation will complete
immediately, but under TCP it could take a long time. One solution would be
for the open operation to post an event to the I/O queue; then both cases
could be regarded as being equivalent, with the completion being signalled
by the posting of the completion event. However UDP is the most common case
and that would involve extra overhead. So the open() returns a status
indicating whether the operation completed asynchronously. If it did, the
code yields back to the coroutine; if not the yield is bypassed.
The asynchronous send is straightforward, invoking the underlying ASIO
function. (Note that the address/port is supplied to both the open() and
asyncSend() methods - it is used by the TCPSocket in open() and by the
UDPSocket in asyncSend().)
The asyncReceive() method issues an asynchronous read and waits for completion.
The fetch object keeps track of the amount of data received so far and when
the receive completes it calls a method on the socket to determine if the
entire message has been received. (This will always be the case for UDP. On
TCP though, the message is preceded by a count field as several reads may be
required to read all the data.) The fetch loops until all the data is read.
Finally, the socket is closed and the server called to resume operation.

View File

@@ -4,18 +4,20 @@
#include <log/message_types.h>
#include <log/message_initializer.h>
namespace asiolink {
namespace isc {
namespace asiodns {
extern const isc::log::MessageID ASIO_FETCHCOMP = "FETCHCOMP";
extern const isc::log::MessageID ASIO_FETCHSTOP = "FETCHSTOP";
extern const isc::log::MessageID ASIO_OPENSOCK = "OPENSOCK";
extern const isc::log::MessageID ASIO_RECVSOCK = "RECVSOCK";
extern const isc::log::MessageID ASIO_RECVTMO = "RECVTMO";
extern const isc::log::MessageID ASIO_SENDSOCK = "SENDSOCK";
extern const isc::log::MessageID ASIO_UNKORIGIN = "UNKORIGIN";
extern const isc::log::MessageID ASIO_UNKRESULT = "UNKRESULT";
extern const isc::log::MessageID ASIODNS_FETCHCOMP = "FETCHCOMP";
extern const isc::log::MessageID ASIODNS_FETCHSTOP = "FETCHSTOP";
extern const isc::log::MessageID ASIODNS_OPENSOCK = "OPENSOCK";
extern const isc::log::MessageID ASIODNS_RECVSOCK = "RECVSOCK";
extern const isc::log::MessageID ASIODNS_RECVTMO = "RECVTMO";
extern const isc::log::MessageID ASIODNS_SENDSOCK = "SENDSOCK";
extern const isc::log::MessageID ASIODNS_UNKORIGIN = "UNKORIGIN";
extern const isc::log::MessageID ASIODNS_UNKRESULT = "UNKRESULT";
} // namespace asiolink
} // namespace asiodns
} // namespace isc
namespace {

23
src/lib/asiodns/asiodef.h Normal file
View File

@@ -0,0 +1,23 @@
// File created from asiodef.msg on Mon Feb 28 17:15:30 2011
#ifndef __ASIODEF_H
#define __ASIODEF_H
#include <log/message_types.h>
namespace isc {
namespace asiodns {
extern const isc::log::MessageID ASIODNS_FETCHCOMP;
extern const isc::log::MessageID ASIODNS_FETCHSTOP;
extern const isc::log::MessageID ASIODNS_OPENSOCK;
extern const isc::log::MessageID ASIODNS_RECVSOCK;
extern const isc::log::MessageID ASIODNS_RECVTMO;
extern const isc::log::MessageID ASIODNS_SENDSOCK;
extern const isc::log::MessageID ASIODNS_UNKORIGIN;
extern const isc::log::MessageID ASIODNS_UNKRESULT;
} // namespace asiodns
} // namespace isc
#endif // __ASIODEF_H

View File

@@ -12,8 +12,8 @@
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
$PREFIX ASIO_
$NAMESPACE asiolink
$PREFIX ASIODNS_
$NAMESPACE isc::asiodns
FETCHCOMP upstream fetch to %s(%d) has now completed
+ A debug message, this records the the upstream fetch (a query made by the

23
src/lib/asiodns/asiodns.h Normal file
View File

@@ -0,0 +1,23 @@
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef __ASIODNS_H
#define __ASIODNS_H 1
#include <asiodns/dns_service.h>
#include <asiodns/dns_server.h>
#include <asiodns/dns_lookup.h>
#include <asiodns/dns_answer.h>
#endif // __ASIODNS_H

View File

@@ -16,8 +16,11 @@
#define __ASIOLINK_DNS_ANSWER_H 1
#include <asiolink/io_message.h>
#include <util/buffer.h>
#include <dns/message.h>
namespace asiolink {
namespace isc {
namespace asiodns {
/// \brief The \c DNSAnswer class is an abstract base class for a DNS
/// Answer provider function.
@@ -63,11 +66,12 @@ public:
/// \param answer_message The DNS MessagePtr of the answer we are
/// building
/// \param buffer Intermediate data results are put here
virtual void operator()(const IOMessage& io_message,
virtual void operator()(const asiolink::IOMessage& io_message,
isc::dns::MessagePtr query_message,
isc::dns::MessagePtr answer_message,
isc::util::OutputBufferPtr buffer) const = 0;
};
} // namespace asiolink
} // namespace asiodns
} // namespace isc
#endif // __ASIOLINK_DNS_ANSWER_H

View File

@@ -16,11 +16,12 @@
#define __ASIOLINK_DNS_LOOKUP_H 1
#include <asiolink/io_message.h>
#include <asiolink/dns_server.h>
#include <util/buffer.h>
#include <asiodns/dns_server.h>
#include <dns/message.h>
#include <util/buffer.h>
namespace asiolink {
namespace isc {
namespace asiodns {
/// \brief The \c DNSLookup class is an abstract base class for a DNS
/// Lookup provider function.
@@ -67,7 +68,7 @@ public:
/// this MessagePtr
/// \param buffer The final answer is put here
/// \param server DNSServer object to use
virtual void operator()(const IOMessage& io_message,
virtual void operator()(const asiolink::IOMessage& io_message,
isc::dns::MessagePtr message,
isc::dns::MessagePtr answer_message,
isc::util::OutputBufferPtr buffer,
@@ -79,5 +80,6 @@ private:
DNSLookup* self_;
};
} // namespace asiolink
} // namespace asiodns
} // namespace isc
#endif // __ASIOLINK_DNS_LOOKUP_H

View File

@@ -17,7 +17,8 @@
#include <asiolink/io_message.h>
namespace asiolink {
namespace isc {
namespace asiodns {
/// \brief The \c DNSServer class is a wrapper (and base class) for
/// classes which provide DNS server functionality.
@@ -34,7 +35,7 @@ namespace asiolink {
/// instantiated through a base class) are sometimes passed by
/// reference (as this superclass); calls to methods in the base
/// class are then rerouted via this pointer to methods in the derived
/// class. This allows code from outside asiolink, with no specific
/// class. This allows code from outside asiodns, with no specific
/// knowledge of \c TCPServer or \c UDPServer, to access their methods.
///
/// This class is both assignable and copy-constructable. Its subclasses
@@ -151,5 +152,6 @@ private:
};
} // asiolink
} // namespace asiodns
} // namespace isc
#endif // __ASIOLINK_DNS_SERVER_H

View File

@@ -23,11 +23,11 @@
#include <log/dummylog.h>
#include <asio.hpp>
#include <asiolink/dns_service.h>
#include <dns_service.h>
#include <asiolink/io_service.h>
#include <asiolink/io_service.h>
#include <asiolink/tcp_server.h>
#include <asiolink/udp_server.h>
#include <tcp_server.h>
#include <udp_server.h>
#include <log/dummylog.h>
@@ -36,9 +36,11 @@
using isc::log::dlog;
namespace asiolink {
using namespace isc::asiolink;
namespace isc {
namespace asiodns {
class SimpleCallback;
class DNSLookup;
class DNSAnswer;
@@ -195,6 +197,5 @@ DNSService::clearServers() {
impl_->servers_.clear();
}
} // namespace asiolink
} // namespace asiodns
} // namespace isc

View File

@@ -18,10 +18,11 @@
#include <resolve/resolver_interface.h>
#include <asiolink/io_service.h>
#include <asiolink/simple_callback.h>
namespace asiolink {
namespace isc {
namespace asiodns {
class SimpleCallback;
class DNSLookup;
class DNSAnswer;
class DNSServiceImpl;
@@ -54,8 +55,8 @@ public:
/// \param checkin Provider for cc-channel events (see \c SimpleCallback)
/// \param lookup The lookup provider (see \c DNSLookup)
/// \param answer The answer provider (see \c DNSAnswer)
DNSService(IOService& io_service, const char& port,
const char& address, SimpleCallback* checkin,
DNSService(asiolink::IOService& io_service, const char& port,
const char& address, isc::asiolink::SimpleCallback* checkin,
DNSLookup* lookup, DNSAnswer* answer);
/// \brief The constructor with a specific port on which the services
/// listen on.
@@ -71,14 +72,14 @@ public:
/// \param checkin Provider for cc-channel events (see \c SimpleCallback)
/// \param lookup The lookup provider (see \c DNSLookup)
/// \param answer The answer provider (see \c DNSAnswer)
DNSService(IOService& io_service, const char& port,
DNSService(asiolink::IOService& io_service, const char& port,
const bool use_ipv4, const bool use_ipv6,
SimpleCallback* checkin, DNSLookup* lookup,
isc::asiolink::SimpleCallback* checkin, DNSLookup* lookup,
DNSAnswer* answer);
/// \brief The constructor without any servers.
///
/// Use addServer() to add some servers.
DNSService(IOService& io_service, SimpleCallback* checkin,
DNSService(asiolink::IOService& io_service, isc::asiolink::SimpleCallback* checkin,
DNSLookup* lookup, DNSAnswer* answer);
/// \brief The destructor.
~DNSService();
@@ -105,8 +106,9 @@ public:
private:
DNSServiceImpl* impl_;
IOService& io_service_;
asiolink::IOService& io_service_;
};
} // namespace asiolink
} // namespace asiodns
} // namespace isc
#endif // __ASIOLINK_DNS_SERVICE_H

View File

@@ -14,47 +14,50 @@
#include <config.h>
#include <unistd.h> // for some IPC/network system calls
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdint.h>
#include <sys/socket.h>
#include <unistd.h> // for some IPC/network system calls
#include <boost/bind.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <asio.hpp>
#include <asio/deadline_timer.hpp>
#include <asiolink/io_address.h>
#include <asiolink/io_asio_socket.h>
#include <asiolink/io_endpoint.h>
#include <asiolink/io_service.h>
#include <asiolink/tcp_endpoint.h>
#include <asiolink/tcp_socket.h>
#include <asiolink/udp_endpoint.h>
#include <asiolink/udp_socket.h>
#include <dns/message.h>
#include <dns/messagerenderer.h>
#include <dns/opcode.h>
#include <dns/rcode.h>
#include <log/logger.h>
#include <asiodns/asiodef.h>
#include <asiodns/io_fetch.h>
#include <util/buffer.h>
#include <util/random/qid_gen.h>
#include <asio.hpp>
#include <asio/deadline_timer.hpp>
#include <asiolink/asiodef.h>
#include <asiolink/io_address.h>
#include <asiolink/io_asio_socket.h>
#include <asiolink/io_endpoint.h>
#include <asiolink/io_fetch.h>
#include <asiolink/io_service.h>
#include <asiolink/tcp_endpoint.h>
#include <asiolink/tcp_socket.h>
#include <asiolink/udp_endpoint.h>
#include <asiolink/udp_socket.h>
#include <stdint.h>
using namespace asio;
using namespace isc::asiolink;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::util::random;
using namespace isc::log;
using namespace std;
namespace asiolink {
namespace isc {
namespace asiodns {
/// Use the ASIO logger
@@ -88,6 +91,7 @@ struct IOFetchData {
size_t offset; ///< Offset to receive data
bool stopped; ///< Have we stopped running?
int timeout; ///< Timeout in ms
bool packet; ///< true if packet was supplied
// In case we need to log an error, the origin of the last asynchronous
// I/O is recorded. To save time and simplify the code, this is recorded
@@ -138,7 +142,6 @@ struct IOFetchData {
question(query),
msgbuf(new OutputBuffer(512)),
received(buff),
callback(cb),
timer(service.get_io_service()),
protocol(proto),
@@ -147,7 +150,8 @@ struct IOFetchData {
offset(0),
stopped(false),
timeout(wait),
origin(ASIO_UNKORIGIN),
packet(false),
origin(ASIODNS_UNKORIGIN),
staging(),
qid(QidGenerator::getInstance().generateQid())
{}
@@ -176,6 +180,19 @@ IOFetch::IOFetch(Protocol protocol, IOService& service,
{
}
IOFetch::IOFetch(Protocol protocol, IOService& service,
OutputBufferPtr& outpkt, const IOAddress& address, uint16_t port,
OutputBufferPtr& buff, Callback* cb, int wait)
:
data_(new IOFetchData(protocol, service,
isc::dns::Question(isc::dns::Name("dummy.example.org"),
isc::dns::RRClass::IN(), isc::dns::RRType::A()),
address, port, buff, cb, wait))
{
data_->msgbuf = outpkt;
data_->packet = true;
}
// Return protocol in use.
IOFetch::Protocol
@@ -202,6 +219,13 @@ IOFetch::operator()(asio::error_code ec, size_t length) {
/// This is done in a different scope to allow inline variable
/// declarations.
{
if (data_->packet) {
// A packet was given, overwrite the QID (which is in the
// first two bytes of the packet).
data_->msgbuf->writeUint16At(data_->qid, 0);
} else {
// A question was given, construct the packet
Message msg(Message::RENDER);
msg.setQid(data_->qid);
msg.setOpcode(Opcode::QUERY());
@@ -211,6 +235,7 @@ IOFetch::operator()(asio::error_code ec, size_t length) {
MessageRenderer renderer(*data_->msgbuf);
msg.toWire(renderer);
}
}
// If we timeout, we stop, which will can cancel outstanding I/Os and
// shutdown everything.
@@ -223,7 +248,7 @@ IOFetch::operator()(asio::error_code ec, size_t length) {
// Open a connection to the target system. For speed, if the operation
// is synchronous (i.e. UDP operation) we bypass the yield.
data_->origin = ASIO_OPENSOCK;
data_->origin = ASIODNS_OPENSOCK;
if (data_->socket->isOpenSynchronous()) {
data_->socket->open(data_->remote_snd.get(), *this);
} else {
@@ -233,7 +258,7 @@ IOFetch::operator()(asio::error_code ec, size_t length) {
do {
// Begin an asynchronous send, and then yield. When the send completes,
// we will resume immediately after this point.
data_->origin = ASIO_SENDSOCK;
data_->origin = ASIODNS_SENDSOCK;
CORO_YIELD data_->socket->asyncSend(data_->msgbuf->getData(),
data_->msgbuf->getLength(), data_->remote_snd.get(), *this);
@@ -256,7 +281,7 @@ IOFetch::operator()(asio::error_code ec, size_t length) {
// received all the data before copying it back to the user's buffer.
// And we want to minimise the amount of copying...
data_->origin = ASIO_RECVSOCK;
data_->origin = ASIODNS_RECVSOCK;
data_->cumulative = 0; // No data yet received
data_->offset = 0; // First data into start of buffer
data_->received->clear(); // Clear the receive buffer
@@ -272,7 +297,7 @@ IOFetch::operator()(asio::error_code ec, size_t length) {
// Finished with this socket, so close it. This will not generate an
// I/O error, but reset the origin to unknown in case we change this.
data_->origin = ASIO_UNKORIGIN;
data_->origin = ASIODNS_UNKORIGIN;
data_->socket->close();
/// We are done
@@ -315,7 +340,7 @@ IOFetch::stop(Result result) {
switch (result) {
case TIME_OUT:
if (logger.isDebugEnabled(1)) {
logger.debug(20, ASIO_RECVTMO,
logger.debug(20, ASIODNS_RECVTMO,
data_->remote_snd->getAddress().toText().c_str(),
static_cast<int>(data_->remote_snd->getPort()));
}
@@ -323,7 +348,7 @@ IOFetch::stop(Result result) {
case SUCCESS:
if (logger.isDebugEnabled(50)) {
logger.debug(30, ASIO_FETCHCOMP,
logger.debug(30, ASIODNS_FETCHCOMP,
data_->remote_rcv->getAddress().toText().c_str(),
static_cast<int>(data_->remote_rcv->getPort()));
}
@@ -333,13 +358,13 @@ IOFetch::stop(Result result) {
// Fetch has been stopped for some other reason. This is
// allowed but as it is unusual it is logged, but with a lower
// debug level than a timeout (which is totally normal).
logger.debug(1, ASIO_FETCHSTOP,
logger.debug(1, ASIODNS_FETCHSTOP,
data_->remote_snd->getAddress().toText().c_str(),
static_cast<int>(data_->remote_snd->getPort()));
break;
default:
logger.error(ASIO_UNKRESULT, static_cast<int>(result),
logger.error(ASIODNS_UNKRESULT, static_cast<int>(result),
data_->remote_snd->getAddress().toText().c_str(),
static_cast<int>(data_->remote_snd->getPort()));
}
@@ -363,10 +388,10 @@ IOFetch::stop(Result result) {
void IOFetch::logIOFailure(asio::error_code ec) {
// Should only get here with a known error code.
assert((data_->origin == ASIO_OPENSOCK) ||
(data_->origin == ASIO_SENDSOCK) ||
(data_->origin == ASIO_RECVSOCK) ||
(data_->origin == ASIO_UNKORIGIN));
assert((data_->origin == ASIODNS_OPENSOCK) ||
(data_->origin == ASIODNS_SENDSOCK) ||
(data_->origin == ASIODNS_RECVSOCK) ||
(data_->origin == ASIODNS_UNKORIGIN));
static const char* PROTOCOL[2] = {"TCP", "UDP"};
logger.error(data_->origin,
@@ -377,5 +402,6 @@ void IOFetch::logIOFailure(asio::error_code ec) {
static_cast<int>(data_->remote_snd->getPort()));
}
} // namespace asiolink
} // namespace asiodns
} // namespace isc {

View File

@@ -24,16 +24,17 @@
#include <coroutine.h>
#include <asio/error_code.hpp>
#include <asiolink/io_address.h>
#include <asiolink/io_service.h>
#include <util/buffer.h>
#include <dns/question.h>
namespace asiolink {
namespace isc {
namespace asiodns {
// Forward declarations
class IOAddress;
class IOFetchData;
class IOService;
/// \brief Upstream Fetch Processing
///
@@ -116,12 +117,34 @@ public:
///
/// Creates the object that will handle the upstream fetch.
///
/// TODO: Need to randomise the source port
///
/// \param protocol Fetch protocol, either IOFetch::TCP or IOFetch::UDP
/// \param service I/O Service object to handle the asynchronous
/// operations.
/// \param question DNS question to send to the upstream server.
/// \param address IP address of upstream server
/// \param port Port to which to connect on the upstream server
/// \param buff Output buffer into which the response (in wire format)
/// is written (if a response is received).
/// \param cb Callback object containing the callback to be called when we
/// terminate. The caller is responsible for managing this object
/// and deleting it if necessary.
/// \param wait Timeout for the fetch (in ms). The default value of
/// -1 indicates no timeout.
IOFetch(Protocol protocol, isc::asiolink::IOService& service,
const isc::dns::Question& question,
const isc::asiolink::IOAddress& address,
uint16_t port, isc::util::OutputBufferPtr& buff, Callback* cb,
int wait = -1);
/// \brief Constructor.
///
/// Creates the object that will handle the upstream fetch.
///
/// \param protocol Fetch protocol, either IOFetch::TCP or IOFetch::UDP
/// \param service I/O Service object to handle the asynchronous
/// operations.
/// \param outpkt Packet to send to upstream server. Note that the
/// QID (first two bytes of the packet) may be altered in the sending.
/// \param buff Output buffer into which the response (in wire format)
/// is written (if a response is received).
/// \param cb Callback object containing the callback to be called
@@ -132,8 +155,9 @@ public:
/// (default = 53)
/// \param wait Timeout for the fetch (in ms). The default value of
/// -1 indicates no timeout.
IOFetch(Protocol protocol, IOService& service,
const isc::dns::Question& question, const IOAddress& address,
IOFetch(Protocol protocol, isc::asiolink::IOService& service,
isc::util::OutputBufferPtr& outpkt,
const isc::asiolink::IOAddress& address,
uint16_t port, isc::util::OutputBufferPtr& buff, Callback* cb,
int wait = -1);
@@ -174,6 +198,7 @@ private:
};
} // namespace asiolink
} // namespace asiodns
} // namespace isc
#endif // __IO_FETCH_H

View File

@@ -29,7 +29,7 @@
#include <asiolink/dummy_io_cb.h>
#include <asiolink/tcp_endpoint.h>
#include <asiolink/tcp_socket.h>
#include <asiolink/tcp_server.h>
#include <tcp_server.h>
using namespace asio;
@@ -39,8 +39,10 @@ using asio::ip::tcp;
using namespace std;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::asiolink;
namespace asiolink {
namespace isc {
namespace asiodns {
/// The following functions implement the \c TCPServer class.
///
@@ -238,5 +240,5 @@ TCPServer::resume(const bool done) {
io_.post(*this);
}
} // namespace asiolink
} // namespace asiodns
} // namespace isc

View File

@@ -24,9 +24,12 @@
#include <asiolink/asiolink.h>
#include <coroutine.h>
#include "dns_server.h"
#include "dns_lookup.h"
#include "dns_answer.h"
namespace asiolink {
namespace isc {
namespace asiodns {
/// \brief A TCP-specific \c DNSServer object.
///
@@ -36,7 +39,7 @@ class TCPServer : public virtual DNSServer, public virtual coroutine {
public:
explicit TCPServer(asio::io_service& io_service,
const asio::ip::address& addr, const uint16_t port,
const SimpleCallback* checkin = NULL,
const isc::asiolink::SimpleCallback* checkin = NULL,
const DNSLookup* lookup = NULL,
const DNSAnswer* answer = NULL);
@@ -95,7 +98,7 @@ private:
// \c IOMessage and \c Message objects to be passed to the
// DNS lookup and answer providers
boost::shared_ptr<asiolink::IOMessage> io_message_;
boost::shared_ptr<isc::asiolink::IOMessage> io_message_;
isc::dns::MessagePtr query_message_;
isc::dns::MessagePtr answer_message_;
@@ -108,13 +111,14 @@ private:
bool done_;
// Callback functions provided by the caller
const SimpleCallback* checkin_callback_;
const isc::asiolink::SimpleCallback* checkin_callback_;
const DNSLookup* lookup_callback_;
const DNSAnswer* answer_callback_;
boost::shared_ptr<IOEndpoint> peer_;
boost::shared_ptr<IOSocket> iosock_;
boost::shared_ptr<isc::asiolink::IOEndpoint> peer_;
boost::shared_ptr<isc::asiolink::IOSocket> iosock_;
};
} // namespace asiolink
} // namespace asiodns
} // namespace isc
#endif // __TCP_SERVER_H

View File

@@ -0,0 +1,50 @@
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/bin
AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc -I$(top_builddir)/src/lib/util
AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(srcdir)/testdata\"
AM_CXXFLAGS = $(B10_CXXFLAGS)
if USE_STATIC_LINK
AM_LDFLAGS = -static
endif
CLEANFILES = *.gcno *.gcda
TESTS =
if HAVE_GTEST
TESTS += run_unittests
run_unittests_SOURCES = run_unittests.cc
run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.h
run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
run_unittests_SOURCES += io_service_unittest.cc
run_unittests_SOURCES += dns_server_unittest.cc
run_unittests_SOURCES += io_fetch_unittest.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(SQLITE_LIBS)
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
# Note: the ordering matters: -Wno-... must follow -Wextra (defined in
# B10_CXXFLAGS)
run_unittests_CXXFLAGS = $(AM_CXXFLAGS)
if USE_GXX
run_unittests_CXXFLAGS += -Wno-unused-parameter
endif
if USE_CLANGPP
# Same for clang++, but we need to turn off -Werror completely.
run_unittests_CXXFLAGS += -Wno-error
endif
endif
noinst_PROGRAMS = $(TESTS)

View File

@@ -18,10 +18,10 @@
#include <asio.hpp>
#include <asiolink/io_endpoint.h>
#include <asiolink/io_error.h>
#include <asiolink/udp_server.h>
#include <asiolink/tcp_server.h>
#include <asiolink/dns_answer.h>
#include <asiolink/dns_lookup.h>
#include <asiodns/udp_server.h>
#include <asiodns/tcp_server.h>
#include <asiodns/dns_answer.h>
#include <asiodns/dns_lookup.h>
#include <string>
#include <csignal>
#include <unistd.h> //for alarm
@@ -65,7 +65,8 @@
/// involved so the message sending between client and server is plain text
/// And the valid checker, question lookup and answer composition are dummy.
using namespace asiolink;
using namespace isc::asiolink;
using namespace isc::asiodns;
using namespace asio;
namespace {

View File

@@ -38,16 +38,18 @@
#include <asiolink/io_address.h>
#include <asiolink/io_endpoint.h>
#include <asiolink/io_fetch.h>
#include <asiolink/io_service.h>
#include <asiodns/io_fetch.h>
using namespace asio;
using namespace isc::dns;
using namespace isc::util;
using namespace asio::ip;
using namespace std;
using namespace isc::asiolink;
namespace asiolink {
namespace isc {
namespace asiodns {
const asio::ip::address TEST_HOST(asio::ip::address::from_string("127.0.0.1"));
const uint16_t TEST_PORT(5301);
@@ -722,4 +724,5 @@ TEST_F(IOFetchTest, TcpSendReceive8192ShortSend) {
}
} // namespace asiolink
} // namespace asiodns
} // namespace isc

View File

@@ -17,8 +17,10 @@
#include <asio.hpp>
#include <asiolink/asiolink.h>
#include <asiodns/asiodns.h>
using namespace asiolink;
using namespace isc::asiolink;
using namespace isc::asiodns;
const char* const TEST_SERVER_PORT = "53535";
const char* const TEST_CLIENT_PORT = "53536";

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
@@ -12,21 +12,17 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <boost/python.hpp>
#include <boost/python/class.hpp>
#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
#include <boost/python/exception_translator.hpp>
#include <boost/python/return_internal_reference.hpp>
#include <boost/python/copy_const_reference.hpp>
#include <boost/shared_ptr.hpp>
#include <gtest/gtest.h>
#include <xfr/fd_share.h>
#include <log/root_logger_name.h>
#include <dns/tests/unittest_util.h>
using namespace isc::xfr;
using namespace boost::python;
int
main(int argc, char* argv[])
{
::testing::InitGoogleTest(&argc, argv); // Initialize Google test
isc::log::setRootLoggerName("unittest"); // Set a root logger name
isc::UnitTestUtil::addDataPath(TEST_DATA_DIR); // Add location of test data
BOOST_PYTHON_MODULE(bind10_xfr) {
def("recv_fd", &recv_fd);
def("send_fd", &send_fd);
return (RUN_ALL_TESTS());
}

View File

@@ -27,8 +27,8 @@
#include <asio/error.hpp>
#include <asiolink/dummy_io_cb.h>
#include <asiolink/udp_endpoint.h>
#include <asiolink/udp_server.h>
#include <asiolink/udp_socket.h>
#include "udp_server.h"
#include <dns/opcode.h>
@@ -39,8 +39,10 @@ using isc::log::dlog;
using namespace std;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::asiolink;
namespace asiolink {
namespace isc {
namespace asiodns {
/*
* Some of the member variables here are shared_ptrs and some are
@@ -319,4 +321,5 @@ UDPServer::hasAnswer() {
return (data_->done_);
}
} // namespace asiolink
} // namespace asiodns
} // namespace isc

View File

@@ -19,14 +19,15 @@
#error "asio.hpp must be included before including this, see asiolink.h as to why"
#endif
#include <asiolink/dns_server.h>
#include <asiolink/simple_callback.h>
#include <asiolink/dns_lookup.h>
#include <asiolink/dns_answer.h>
#include <asiodns/dns_answer.h>
#include <asiodns/dns_lookup.h>
#include <asiodns/dns_server.h>
#include <coroutine.h>
namespace asiolink {
namespace isc {
namespace asiodns {
//
// Asynchronous UDP server coroutine
@@ -47,7 +48,7 @@ public:
/// \param answer the callbackprovider for DNS answer events
explicit UDPServer(asio::io_service& io_service,
const asio::ip::address& addr, const uint16_t port,
SimpleCallback* checkin = NULL,
isc::asiolink::SimpleCallback* checkin = NULL,
DNSLookup* lookup = NULL,
DNSAnswer* answer = NULL);
@@ -102,5 +103,6 @@ private:
boost::shared_ptr<Data> data_;
};
} // namespace asiolink
} // namespace asiodns
} // namespace isc
#endif // __UDP_SERVER_H

View File

@@ -13,31 +13,21 @@ CLEANFILES = *.gcno *.gcda
# which would make the build fail with -Werror (our default setting).
lib_LTLIBRARIES = libasiolink.la
libasiolink_la_SOURCES = asiolink.h
libasiolink_la_SOURCES += asiodef.cc asiodef.h
libasiolink_la_SOURCES += dns_answer.h
libasiolink_la_SOURCES += dns_lookup.h
libasiolink_la_SOURCES += dns_server.h
libasiolink_la_SOURCES += dns_service.cc dns_service.h
libasiolink_la_SOURCES += dummy_io_cb.h
libasiolink_la_SOURCES += interval_timer.cc interval_timer.h
libasiolink_la_SOURCES += io_address.cc io_address.h
libasiolink_la_SOURCES += io_asio_socket.h
libasiolink_la_SOURCES += io_endpoint.cc io_endpoint.h
libasiolink_la_SOURCES += io_error.h
libasiolink_la_SOURCES += io_fetch.cc io_fetch.h
libasiolink_la_SOURCES += io_message.h
libasiolink_la_SOURCES += io_service.h io_service.cc
libasiolink_la_SOURCES += io_socket.h io_socket.cc
libasiolink_la_SOURCES += simple_callback.h
libasiolink_la_SOURCES += tcp_endpoint.h
libasiolink_la_SOURCES += tcp_server.cc tcp_server.h
libasiolink_la_SOURCES += tcp_socket.h
libasiolink_la_SOURCES += udp_endpoint.h
libasiolink_la_SOURCES += udp_server.cc udp_server.h
libasiolink_la_SOURCES += udp_socket.h
EXTRA_DIST = asiodef.msg
# Note: the ordering matters: -Wno-... must follow -Wextra (defined in
# B10_CXXFLAGS)
libasiolink_la_CXXFLAGS = $(AM_CXXFLAGS)

View File

@@ -16,167 +16,7 @@ including:
them in only one place allows us to relax strictness here, while
leaving it in place elsewhere.
Currently, the asiolink library only supports DNS servers (i.e., b10-auth
and b10-resolver). The plan is to make it more generic and allow it to
support other modules as well.
Some of the classes defined here--for example, IOSocket, IOEndpoint,
and IOAddress--are to be used by BIND 10 modules as wrappers around
ASIO-specific classes.
Other classes implement the DNS protocol on behalf of BIND 10 modules.
These DNS server and client routines are written using the "stackless
coroutine" pattern invented by Chris Kohlhoff and described at
http://blog.think-async.com/2010/03/potted-guide-to-stackless-coroutines.html.
This is intended to simplify development a bit, since it allows the
routines to be written in a straightfowrard step-step-step fashion rather
than as a complex chain of separate handler functions.
Coroutine objects (i.e., UDPServer, TCPServer and IOFetch) are objects
with reenterable operator() members. When an instance of one of these
classes is called as a function, it resumes at the position where it left
off. Thus, a UDPServer can issue an asynchronous I/O call and specify
itself as the handler object; when the call completes, the UDPServer
carries on at the same position. As a result, the code can look as
if it were using synchronous, not asynchronous, I/O, providing some of
the benefit of threading but with minimal switching overhead.
So, in simplified form, the behavior of a DNS Server is:
REENTER:
while true:
YIELD packet = read_packet
FORK
if not parent:
break
# This callback informs the caller that a packet has arrived, and
# gives it a chance to update configuration, etc
SimpleCallback(packet)
YIELD answer = DNSLookup(packet, this)
response = DNSAnswer(answer)
YIELD send(response)
At each "YIELD" point, the coroutine initiates an asynchronous operation,
then pauses and turns over control to some other task on the ASIO service
queue. When the operation completes, the coroutine resumes.
DNSLookup, DNSAnswer and SimpleCallback define callback methods
used by a DNS Server to communicate with the module that called it.
They are abstract-only classes whose concrete implementations
are supplied by the calling module.
The DNSLookup callback always runs asynchronously. Concrete
implementations must be sure to call the server's "resume" method when
it is finished.
In an authoritative server, the DNSLookup implementation would examine
the query, look up the answer, then call "resume". (See the diagram
in doc/auth_process.jpg.)
In a recursive server, the DNSLookup impelemtation would initiate a
DNSQuery, which in turn would be responsible for calling the server's
"resume" method. (See the diagram in doc/recursive_process.jpg.)
A DNSQuery object is intended to handle resolution of a query over
the network when the local authoritative data sources or cache are not
sufficient. The plan is that it will make use of subsidiary DNSFetch
calls to get data from particular authoritative servers, and when it has
gotten a complete answer, it calls "resume".
In current form, however, DNSQuery is much simpler; it forwards queries
to a single upstream resolver and passes the answers back to the client.
It is constructed with the address of the forward server. Queries are
initiated with the question to ask the forward server, a buffer into
which to write the answer, and a pointer to the coroutine to be resumed
when the answer has arrived. In simplified form, the DNSQuery routine is:
REENTER:
render the question into a wire-format query packet
YIELD send(query)
YIELD response = read_packet
server->resume
Currently, DNSQuery is only implemented for UDP queries. In future work
it will be necessary to write code to fall back to TCP when circumstances
require it.
Upstream Fetches
================
Upstream fetches (queries by the resolver on behalf of a client) are made
using a slightly-modified version of the pattern described above.
Sockets
-------
First, it will be useful to understand the class hierarchy used in the
fetch logic:
IOSocket
|
IOAsioSocket
|
+-----+-----+
| |
UDPSocket TCPSocket
IOSocket is a wrapper class for a socket and is used by the authoritative
server code. It is an abstract base class, providing little more that the ability to hold the socket and to return the protocol in use.
Built on this is IOAsioSocket, which adds the open, close, asyncSend and
asyncReceive methods. This is a template class, which takes as template
argument the class of the object that will be used as the callback when the
asynchronous operation completes. This object can be of any type, but must
include an operator() method with the signature:
operator()(asio::error_code ec, size_t length)
... the two arguments being the status of the completed I/O operation and
the number of bytes transferred. (In the case of the open method, the second
argument will be zero.)
Finally, the TCPSocket and UDPSocket classes provide the body of the
asynchronous operations.
Fetch Sequence
--------------
The fetch is implemented by the IOFetch class, which takes as argument the
protocol to use. The sequence is:
REENTER:
render the question into a wire-format query packet
open() // Open socket and optionally connect
if (! synchronous) {
YIELD;
}
YIELD asyncSend(query) // Send query
do {
YIELD asyncReceive(response) // Read response
} while (! complete(response))
close() // Drop connection and close socket
server->resume
The open() method opens a socket for use. On TCP, it also makes a
connection to the remote end. So under UDP the operation will complete
immediately, but under TCP it could take a long time. One solution would be
for the open operation to post an event to the I/O queue; then both cases
could be regarded as being equivalent, with the completion being signalled
by the posting of the completion event. However UDP is the most common case
and that would involve extra overhead. So the open() returns a status
indicating whether the operation completed asynchronously. If it did, the
code yields back to the coroutine; if not the yield is bypassed.
The asynchronous send is straightforward, invoking the underlying ASIO
function. (Note that the address/port is supplied to both the open() and
asyncSend() methods - it is used by the TCPSocket in open() and by the
UDPSocket in asyncSend().)
The asyncReceive() method issues an asynchronous read and waits for completion.
The fetch object keeps track of the amount of data received so far and when
the receive completes it calls a method on the socket to determine if the
entire message has been received. (This will always be the case for UDP. On
TCP though, the message is preceded by a count field as several reads may be
required to read all the data.) The fetch loops until all the data is read.
Finally, the socket is closed and the server called to resume operation.

View File

@@ -1,21 +0,0 @@
// File created from asiodef.msg on Mon Feb 28 17:15:30 2011
#ifndef __ASIODEF_H
#define __ASIODEF_H
#include <log/message_types.h>
namespace asiolink {
extern const isc::log::MessageID ASIO_FETCHCOMP;
extern const isc::log::MessageID ASIO_FETCHSTOP;
extern const isc::log::MessageID ASIO_OPENSOCK;
extern const isc::log::MessageID ASIO_RECVSOCK;
extern const isc::log::MessageID ASIO_RECVTMO;
extern const isc::log::MessageID ASIO_SENDSOCK;
extern const isc::log::MessageID ASIO_UNKORIGIN;
extern const isc::log::MessageID ASIO_UNKRESULT;
} // namespace asiolink
#endif // __ASIODEF_H

View File

@@ -20,10 +20,6 @@
// See the description of the namespace below.
#include <asiolink/io_service.h>
#include <asiolink/dns_service.h>
#include <asiolink/dns_server.h>
#include <asiolink/dns_lookup.h>
#include <asiolink/dns_answer.h>
#include <asiolink/simple_callback.h>
#include <asiolink/interval_timer.h>
@@ -62,11 +58,6 @@
/// this module. The resulting interfaces are thus straightforward mapping
/// to the ASIO counterparts.
///
/// Notes to developers:
/// Currently the wrapper interface is fairly specific to use by a
/// DNS server, i.e., b10-auth or b10-resolver. But the plan is to
/// generalize it and have other modules use it as well.
///
/// One obvious drawback of this approach is performance overhead
/// due to the additional layer. We should eventually evaluate the cost
/// of the wrapper abstraction in benchmark tests. Another drawback is

View File

@@ -20,6 +20,7 @@
#include <asio/error.hpp>
#include <asio/error_code.hpp>
namespace isc {
namespace asiolink {
/// \brief Asynchronous I/O Completion Callback
@@ -55,5 +56,6 @@ public:
};
} // namespace asiolink
} // namespace isc
#endif // __DUMMY_IO_CB_H

View File

@@ -26,6 +26,7 @@
#include <asiolink/interval_timer.h>
#include <asiolink/io_service.h>
namespace isc {
namespace asiolink {
class IntervalTimerImpl {
@@ -133,4 +134,5 @@ IntervalTimer::getInterval() const {
return (impl_->getInterval());
}
}
} // namespace asiolink
} // namespace isc

View File

@@ -19,6 +19,7 @@
#include <asiolink/io_service.h>
namespace isc {
namespace asiolink {
struct IntervalTimerImpl;
@@ -130,4 +131,5 @@ private:
};
} // namespace asiolink
} // namespace isc
#endif // __ASIOLINK_INTERVAL_TIMER_H

View File

@@ -31,6 +31,7 @@ using asio::ip::tcp;
using namespace std;
namespace isc {
namespace asiolink {
// XXX: we cannot simply construct the address in the initialization list,
@@ -62,4 +63,5 @@ IOAddress::getFamily() const {
}
}
}
} // namespace asiolink
} // namespace isc

View File

@@ -26,6 +26,7 @@
#include <exceptions/exceptions.h>
namespace isc {
namespace asiolink {
/// \brief The \c IOAddress class represents an IP addresses (version
@@ -119,5 +120,6 @@ private:
asio::ip::address asio_address_;
};
} // asiolink
} // namespace asiolink
} // namespace isc
#endif // __IO_ADDRESS_H

View File

@@ -31,7 +31,7 @@
#include <asiolink/io_error.h>
#include <asiolink/io_socket.h>
namespace isc {
namespace asiolink {
/// \brief Socket not open
@@ -395,5 +395,6 @@ private:
};
} // namespace asiolink
} // namespace isc
#endif // __IO_ASIO_SOCKET_H

View File

@@ -28,6 +28,7 @@
using namespace std;
namespace isc {
namespace asiolink {
const IOEndpoint*
@@ -57,4 +58,5 @@ IOEndpoint::operator!=(const IOEndpoint& other) const {
return (!operator==(other));
}
}
} // namespace asiolink
} // namespace isc

View File

@@ -26,6 +26,7 @@
#include <exceptions/exceptions.h>
#include <asiolink/io_address.h>
namespace isc {
namespace asiolink {
/// \brief The \c IOEndpoint class is an abstract base class to represent
@@ -117,5 +118,6 @@ public:
const unsigned short port);
};
} // asiolink
} // namespace asiolink
} // namespace isc
#endif // __IO_ENDPOINT_H

View File

@@ -18,6 +18,7 @@
#include <exceptions/exceptions.h>
namespace isc {
namespace asiolink {
/// \brief An exception that is thrown if an error occurs within the IO
@@ -30,6 +31,7 @@ public:
};
} // asiolink
} // namespace asiolink
} // namespace isc
#endif // __IO_ERROR_H

View File

@@ -28,6 +28,7 @@
#include <asiolink/io_endpoint.h>
#include <asiolink/io_socket.h>
namespace isc {
namespace asiolink {
/// \brief The \c IOMessage class encapsulates an incoming message received
@@ -96,5 +97,6 @@ private:
};
} // asiolink
} // namespace asiolink
} // namespace isc
#endif // __IO_MESSAGE_H

View File

@@ -21,6 +21,7 @@
#include <asio.hpp>
#include <asiolink/io_service.h>
namespace isc {
namespace asiolink {
class IOServiceImpl {
@@ -95,4 +96,5 @@ IOService::get_io_service() {
return (io_impl_->get_io_service());
}
} // namepsace asiolink
} // namespace asiolink
} // namespace isc

View File

@@ -19,6 +19,7 @@ namespace asio {
class io_service;
}
namespace isc {
namespace asiolink {
struct IOServiceImpl;
@@ -74,4 +75,5 @@ private:
};
} // namespace asiolink
} // namespace isc
#endif // __ASIOLINK_IO_SERVICE_H

View File

@@ -16,8 +16,7 @@
#include <asio.hpp>
using namespace asio;
namespace isc {
namespace asiolink {
/// \brief The \c DummySocket class is a concrete derived class of
@@ -62,4 +61,5 @@ IOSocket::getDummyTCPSocket() {
return (socket);
}
}
} // namespace asiolink
} // namespace isc

View File

@@ -25,6 +25,7 @@
#include <exceptions/exceptions.h>
namespace isc {
namespace asiolink {
/// \brief The \c IOSocket class is an abstract base class to represent
@@ -120,5 +121,6 @@ public:
};
} // namespace asiolink
} // namespace isc
#endif // __IO_SOCKET_H

View File

@@ -17,6 +17,7 @@
#include <asiolink/io_message.h>
namespace isc {
namespace asiolink {
/// \brief The \c SimpleCallback class is an abstract base class for a
@@ -68,4 +69,5 @@ private:
};
} // namespace asiolink
} // namespace isc
#endif // __ASIOLINK_SIMPLE_CALLBACK_H

View File

@@ -21,6 +21,7 @@
#include <asiolink/io_endpoint.h>
namespace isc {
namespace asiolink {
/// \brief The \c TCPEndpoint class is a concrete derived class of
@@ -110,4 +111,5 @@ private:
};
} // namespace asiolink
} // namespace isc
#endif // __TCP_ENDPOINT_H

View File

@@ -41,6 +41,7 @@
#include <asiolink/io_service.h>
#include <asiolink/tcp_endpoint.h>
namespace isc {
namespace asiolink {
/// \brief Buffer Too Large
@@ -412,5 +413,6 @@ TCPSocket<C>::close() {
}
} // namespace asiolink
} // namespace isc
#endif // __TCP_SOCKET_H

View File

@@ -21,15 +21,12 @@ run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.h
run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
run_unittests_SOURCES += io_address_unittest.cc
run_unittests_SOURCES += io_endpoint_unittest.cc
run_unittests_SOURCES += io_fetch_unittest.cc
run_unittests_SOURCES += io_socket_unittest.cc
run_unittests_SOURCES += io_service_unittest.cc
run_unittests_SOURCES += interval_timer_unittest.cc
run_unittests_SOURCES += tcp_endpoint_unittest.cc
run_unittests_SOURCES += tcp_socket_unittest.cc
run_unittests_SOURCES += udp_endpoint_unittest.cc
run_unittests_SOURCES += udp_socket_unittest.cc
run_unittests_SOURCES += dns_server_unittest.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)

View File

@@ -26,7 +26,7 @@ const boost::posix_time::time_duration TIMER_MARGIN_MSEC =
boost::posix_time::milliseconds(50);
}
using namespace asiolink;
using namespace isc::asiolink;
// This fixture is for testing IntervalTimer. Some callback functors are
// registered as callback function of the timer to test if they are called

View File

@@ -18,7 +18,7 @@
#include <asiolink/io_error.h>
#include <asiolink/io_address.h>
using namespace asiolink;
using namespace isc::asiolink;
TEST(IOAddressTest, fromText) {
IOAddress io_address_v4("192.0.2.1");

View File

@@ -18,7 +18,7 @@
#include <asiolink/io_endpoint.h>
#include <asiolink/io_error.h>
using namespace asiolink;
using namespace isc::asiolink;
TEST(IOEndpointTest, createUDPv4) {
const IOEndpoint* ep;

Some files were not shown because too many files have changed in this diff Show More