Commit 9bedc5ab authored by Mukund Sivaraman's avatar Mukund Sivaraman
Browse files

Merge branch 'master' into trac2120

parents f0775ced 73ef6ee5
454. [bug] jelte
b10-cfgmgr now loads its configuration check plugins directly from
the plugin search path, as opposed to importing them from the
general python system module path list; this prevents naming
conflicts with real python modules.
(Trac #2119, git 2f68d7ac5c3c7cc88a3663191113eece32d46a3d)
453. [bug] jelte
b10-auth no longer tries to send DDNS UPDATE messages to b10-ddns if
b10-ddns is not running. Sending an UPDATE to BIND 10 that is not
configured to run DDNS will now result in a response with rcode
NOTIMP instead of SERVFAIL.
(Trac #1986, git bd6b0a5ed3481f78fb4e5cb0b18c7b6e5920f9f8)
452. [func]* muks
b10-showtech: An initial implementation of the b10-showtech tool
is now available. It gathers and outputs system information which
......@@ -291,8 +305,8 @@ bind10-devel-20120329 released on March 29, 2012
407. [build] haikuo
Remove "--enable-boost-threads" switch in configure command. This
thread lock mechanism is useless for bind10 and causes performance
hits.
thread lock mechanism is useless for bind10 and causes performance
hits.
(Trac #1680, git 9c4d0cadf4adc802cc41a2610dc2c30b25aad728)
406. [bug] muks
......@@ -788,26 +802,26 @@ bind10-devel-20120119 released on January 19, 2012
(Trac #1350, git cc20ff993da1ddb1c6e8a98370438b45a2be9e0a)
336. [func] jelte
libdns++ (and its python wrapper) now includes a class Serial, for
SOA SERIAL comparison and addition. Operations on instances of this
class follow the specification from RFC 1982.
Rdata::SOA::getSerial() now returns values of this type (and not
libdns++ (and its python wrapper) now includes a class Serial, for
SOA SERIAL comparison and addition. Operations on instances of this
class follow the specification from RFC 1982.
Rdata::SOA::getSerial() now returns values of this type (and not
uint32_t).
(Trac #1278, git 2ae72d76c74f61a67590722c73ebbf631388acbd)
335. [bug]* jelte
The DataSourceClientContainer class that dynamically loads
datasource backend libraries no longer provides just a .so file name
to its call to dlopen(), but passes it an absolute path. This means
that it is no longer an system implementation detail that depends on
[DY]LD_LIBRARY_PATH which file is chosen, should there be multiple
options (for instance, when test-running a new build while a
The DataSourceClientContainer class that dynamically loads
datasource backend libraries no longer provides just a .so file name
to its call to dlopen(), but passes it an absolute path. This means
that it is no longer an system implementation detail that depends on
[DY]LD_LIBRARY_PATH which file is chosen, should there be multiple
options (for instance, when test-running a new build while a
different version is installed).
These loadable libraries are also no longer installed in the default
library path, but in a subdirectory of the libexec directory of the
These loadable libraries are also no longer installed in the default
library path, but in a subdirectory of the libexec directory of the
target ($prefix/libexec/[version]/backends).
This also removes the need to handle b10-xfin and b10-xfrout as
'special' hardcoded components, and they are now started as regular
This also removes the need to handle b10-xfin and b10-xfrout as
'special' hardcoded components, and they are now started as regular
components as dictated by the configuration of the boss process.
(Trac #1292, git 83ce13c2d85068a1bec015361e4ef8c35590a5d0)
......@@ -952,12 +966,12 @@ bind10-devel-20111128 released on November 28, 2011
(Trac #1228, git 31d5a4f66b18cca838ca1182b9f13034066427a7)
314. [bug] jelte
b10-xfrin would previously initiate incoming transfers upon
receiving NOTIFY messages from any address (if the zone was
known to b10-xfrin, and using the configured address). It now
only starts a transfer if the source address from the NOTIFY
packet matches the configured master address and port. This was
really already fixed in release bind10-devel-20111014, but there
b10-xfrin would previously initiate incoming transfers upon
receiving NOTIFY messages from any address (if the zone was
known to b10-xfrin, and using the configured address). It now
only starts a transfer if the source address from the NOTIFY
packet matches the configured master address and port. This was
really already fixed in release bind10-devel-20111014, but there
were some deferred cleanups to add.
(Trac #1298, git 1177bfe30e17a76bea6b6447e14ae9be9e1ca8c2)
......@@ -971,7 +985,7 @@ bind10-devel-20111128 released on November 28, 2011
(Trac #1329, git 1aa233fab1d74dc776899df61181806679d14013)
312. [func] jelte
Added an initial framework for doing system tests using the
Added an initial framework for doing system tests using the
cucumber-based BDD tool Lettuce. A number of general steps are
included, for instance running bind10 with specific
configurations, sending queries, and inspecting query answers. A
......@@ -1349,7 +1363,7 @@ bind10-devel-20110705 released on July 05, 2011
(Trac #710, git dae1d2e24f993e1eef9ab429326652f40a006dfb)
257. [bug] y-aharen
Fixed a bug an instance of IntervalTimerImpl may be destructed
Fixed a bug an instance of IntervalTimerImpl may be destructed
while deadline_timer is holding the handler. This fix addresses
occasional failure of IntervalTimerTest.destructIntervalTimer.
(Trac #957, git e59c215e14b5718f62699ec32514453b983ff603)
......@@ -2019,7 +2033,7 @@ bind10-devel-20110120 released on January 20, 2011
(Trac #513, git 285c5ee3d5582ed6df02d1aa00387f92a74e3695)
151. [bug] smann
lib/log/dummylog.h:
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
......@@ -2327,11 +2341,11 @@ bind10-devel-20101201 released on December 01, 2010
104. [bug] jerry
bin/zonemgr: zonemgr should be attempting to refresh expired zones.
(Trac #336, r3139)
103. [bug] jerry
lib/python/isc/log: Fixed an issue with python logging,
python log shouldn't die with OSError. (Trac #267, r3137)
102. [build] jinmei
Disable threads in ASIO to minimize build time dependency.
(Trac #345, r3100)
......@@ -2366,7 +2380,7 @@ bind10-devel-20101201 released on December 01, 2010
it can be customized; Make sure --disable-static works.
(Trac #325, r2976)
bind10-devel-20100917 released on September 17, 2010
bind10-devel-20100917 released on September 17, 2010
95. [doc] jreed
Add b10-zonemgr manual page. Update other docs to introduce
......@@ -2419,14 +2433,14 @@ bind10-devel-20100917 released on September 17, 2010
reason. (Trac #296, r2761)
87. [func] zhanglikun
lib/python/isc/notifyout: Add the feature of notify-out, when
lib/python/isc/notifyout: Add the feature of notify-out, when
zone axfr/ixfr finishing, the server will notify its slaves.
(Trac #289, svn r2737)
86. [func] jerry
bin/zonemgr: Added zone manager module. The zone manager is one
of the co-operating processes of BIND10, which keeps track of
timers and other information necessary for BIND10 to act as a
bin/zonemgr: Added zone manager module. The zone manager is one
of the co-operating processes of BIND10, which keeps track of
timers and other information necessary for BIND10 to act as a
slave. (Trac #215, svn r2737)
85. [build]* jinmei
......@@ -2439,7 +2453,7 @@ bind10-devel-20100917 released on September 17, 2010
bind10-devel-20100812 released on August 12, 2010
84. [bug] jinmei, jerry
This is a quick fix patch for the issue: AXFR fails half the
This is a quick fix patch for the issue: AXFR fails half the
time because of connection problems. xfrout client will make
a new connection every time. (Trac #299, svn r2697)
......@@ -2489,7 +2503,7 @@ bind10-devel-20100812 released on August 12, 2010
(Trac #256, r2549)
77. [func] zhanglikun
Make error message be more friendly when running cmdctl and it's
Make error message be more friendly when running cmdctl and it's
already running (listening on same port)(Trac #277, r2540)
76. [bug] jelte
......@@ -2545,8 +2559,8 @@ bind10-devel-20100701 released on July 1, 2010
68. [func] zhanglikun
Add options -c (--certificate-chain) to bindctl. Override class
HTTPSConnection to support server certificate validation.
Add support to cmdctl.spec file, now there are three configurable
items for cmdctl: 'key_file', 'cert_file' and 'accounts_file',
Add support to cmdctl.spec file, now there are three configurable
items for cmdctl: 'key_file', 'cert_file' and 'accounts_file',
all of them can be changed in runtime.
(Trac #127, svn r2357)
......@@ -2566,7 +2580,7 @@ bind10-devel-20100701 released on July 1, 2010
section; this, among other things, will prevent multiple copies
of the same CNAME from showing up when there's a loop. (Trac #69,
svn r2350)
65. [func] shentingting
Various loadzone improvements: allow optional comment for
$TTL, allow optional origin and comment for $INCLUDE, allow
......@@ -2587,7 +2601,7 @@ bind10-devel-20100701 released on July 1, 2010
63. [func] shane
Added initial support for setuid(), using the "-u" flag. This will
be replaced in the future, but for now provides a reasonable
be replaced in the future, but for now provides a reasonable
starting point.
(Trac #180, svn r2330)
......@@ -2636,17 +2650,17 @@ bind10-devel-20100701 released on July 1, 2010
(Trac #224, svn r2103)
53. [bug] zhanglikun
bin/bindctl: Generate a unique session ID by using
socket.gethostname() instead of socket.gethostbyname(),
since the latter one could make bindctl stall if its own
bin/bindctl: Generate a unique session ID by using
socket.gethostname() instead of socket.gethostbyname(),
since the latter one could make bindctl stall if its own
host name can't be resolved.
(Trac #228, svn r2096)
52. [func] zhanglikun
bin/xfrout: When xfrout is launched, check whether the
socket file is being used by one running xfrout process,
if it is, exit from python. If the file isn't a socket file
or nobody is listening, it will be removed. If it can't
socket file is being used by one running xfrout process,
if it is, exit from python. If the file isn't a socket file
or nobody is listening, it will be removed. If it can't
be removed, exit from python.
(Trac #151, svn r2091)
......@@ -2659,7 +2673,7 @@ bind10-devel-20100602 released on June 2, 2010
(Trac #223)
50. [bug] zhanglikun
bin/xfrin: a regression in xfrin: it can't communicate with
bin/xfrin: a regression in xfrin: it can't communicate with
a remote server. (Trac #218, svn r2038)
49. [func]* jelte
......
......@@ -131,6 +131,16 @@
"item_optional": true, "item_default": "memory"
}
]
},
{
"command_name": "start_ddns_forwarder",
"command_description": "(Re)start internal forwarding of DDNS Update messages. This is automatically called if b10-ddns is started, and is not expected to be called by administrators; it will be removed as a public command in the future.",
"command_args": []
},
{
"command_name": "stop_ddns_forwarder",
"command_description": "Stop internal forwarding of DDNS Update messages. This is automatically called if b10-ddns is stopped, and is not expected to be called by administrators; it will be removed as a public command in the future.",
"command_args": []
}
],
"statistics": [
......
......@@ -110,9 +110,6 @@ look into the cause and address the issue. The log message includes
the client's address (and port), and the error message sent from the
lower layer that detects the failure.
% AUTH_RECEIVED_NOTIFY received incoming NOTIFY for zone name %1, zone class %2
This is a debug message reporting that an incoming NOTIFY was received.
% AUTH_NOTIFY_QUESTIONS invalid number of questions (%1) in incoming NOTIFY
This debug message is logged by the authoritative server when it receives
a NOTIFY packet that contains zero or more than one question. (A valid
......@@ -169,6 +166,9 @@ bug ticket for this issue.
This is a debug message issued when the authoritative server has received
a command on the command channel.
% AUTH_RECEIVED_NOTIFY received incoming NOTIFY for zone name %1, zone class %2
This is a debug message reporting that an incoming NOTIFY was received.
% AUTH_RECEIVED_SENDSTATS command 'sendstats' received
This is a debug message issued when the authoritative server has received
a command from the statistics module to send it data. The 'sendstats'
......@@ -235,6 +235,13 @@ This is a debug message indicating that the authoritative server has
found that the data source it is loading is an SQLite3 data source,
so no further validation is needed.
% AUTH_START_DDNS_FORWARDER DDNS UPDATE handling started
This is a debug message indicating that b10-auth has received a message
that it should internally forward UPDATE message to b10-ddns. When b10-ddns
is not running, b10-auth will respond to UPDATE requests with rcode NOTIMP.
When b10-ddns is running, b10-ddns will handle and respond to the UPDATE
message.
% AUTH_STATS_CHANNEL_CREATED STATS session channel created
This is a debug message indicating that the authoritative server has
created a channel to the statistics process. It is issued during server
......@@ -266,6 +273,15 @@ This is a debug message indicating that the statistics timer has been
enabled and that the authoritative server will produce statistics data
at the specified interval.
% AUTH_STOP_DDNS_FORWARDER DDNS UPDATE handling stopped
This is a debug message indicating that b10-auth has received a message
that it should stop internally forwarding UPDATE message to b10-ddns.
b10-auth will no longer forward UPDATE messages to b10-ddns, but will
respond itself with error code NOTIMP.
This message is also logged when the forwarding is restarted (for instance
if b10-ddns is restarted and the internal connection needs to be created
again), in which case it should be followed by AUTH_START_DDNS_FORWARDER.
% AUTH_UNSUPPORTED_OPCODE unsupported opcode: %1
This is a debug message, produced when a received DNS packet being
processed by the authoritative server has been found to contain an
......
......@@ -58,6 +58,7 @@
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/scoped_ptr.hpp>
#include <algorithm>
#include <cassert>
......@@ -270,6 +271,14 @@ public:
/// isc:config::ModuleSpec::validateStatistics.
void registerStatisticsValidator();
/// Socket session forwarder for dynamic update requests
BaseSocketSessionForwarder& ddns_base_forwarder_;
/// Holder for the DDNS Forwarder, which is used to send
/// DDNS messages to b10-ddns, but can be set to empty if
/// b10-ddns is not running
boost::scoped_ptr<SocketSessionForwarderHolder> ddns_forwarder_;
/// \brief Resume the server
///
/// This is a wrapper call for DNSServer::resume(done), if 'done' is true,
......@@ -285,6 +294,7 @@ public:
void resumeServer(isc::asiodns::DNSServer* server,
isc::dns::Message& message,
bool done);
private:
std::string db_file_;
......@@ -297,9 +307,6 @@ private:
bool xfrout_connected_;
AbstractXfroutClient& xfrout_client_;
// Socket session forwarder for dynamic update requests
SocketSessionForwarderHolder ddns_forwarder_;
/// Increment query counter
void incCounter(const int protocol);
......@@ -318,9 +325,10 @@ AuthSrvImpl::AuthSrvImpl(const bool use_cache,
statistics_timer_(io_service_),
counters_(),
keyring_(NULL),
ddns_base_forwarder_(ddns_forwarder),
ddns_forwarder_(NULL),
xfrout_connected_(false),
xfrout_client_(xfrout_client),
ddns_forwarder_("update", ddns_forwarder)
xfrout_client_(xfrout_client)
{
// cur_datasrc_ is automatically initialized by the default constructor,
// effectively being an empty (sqlite) data source. once ccsession is up
......@@ -456,7 +464,7 @@ makeErrorMessage(MessageRenderer& renderer, Message& message,
for_each(questions.begin(), questions.end(), QuestionInserter(message));
message.setRcode(rcode);
RendererHolder holder(renderer, &buffer);
if (tsig_context.get() != NULL) {
message.toWire(renderer, *tsig_context);
......@@ -653,7 +661,12 @@ AuthSrv::processMessage(const IOMessage& io_message, Message& message,
send_answer = impl_->processNotify(io_message, message, buffer,
tsig_context);
} else if (opcode == Opcode::UPDATE()) {
send_answer = impl_->processUpdate(io_message);
if (impl_->ddns_forwarder_) {
send_answer = impl_->processUpdate(io_message);
} else {
makeErrorMessage(impl_->renderer_, message, buffer,
Rcode::NOTIMP(), tsig_context);
}
} else if (opcode != Opcode::QUERY()) {
LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_UNSUPPORTED_OPCODE)
.arg(message.getOpcode().toText());
......@@ -880,7 +893,7 @@ AuthSrvImpl::processUpdate(const IOMessage& io_message) {
// Push the update request to a separate process via the forwarder.
// On successful push, the request shouldn't be responded from b10-auth,
// so we return false.
ddns_forwarder_.push(io_message);
ddns_forwarder_->push(io_message);
return (false);
}
......@@ -1027,3 +1040,20 @@ void
AuthSrv::setTSIGKeyRing(const boost::shared_ptr<TSIGKeyRing>* keyring) {
impl_->keyring_ = keyring;
}
void
AuthSrv::createDDNSForwarder() {
LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_START_DDNS_FORWARDER);
impl_->ddns_forwarder_.reset(
new SocketSessionForwarderHolder("update", impl_->ddns_base_forwarder_));
}
void
AuthSrv::destroyDDNSForwarder() {
if (impl_->ddns_forwarder_) {
LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_STOP_DDNS_FORWARDER);
impl_->ddns_forwarder_.reset();
}
}
......@@ -111,7 +111,7 @@ public:
/// This method should never throw an exception.
void stop();
/// \brief Process an incoming DNS message, then signal 'server' to resume
/// \brief Process an incoming DNS message, then signal 'server' to resume
///
/// A DNS query (or other message) has been received by a \c DNSServer
/// object. Find an answer, then post the \c DNSServer object on the
......@@ -355,13 +355,13 @@ public:
bool submitStatistics() const;
/// \brief Get the value of counter in the AuthCounters.
///
///
/// This function calls AuthCounters::getCounter() and
/// returns its return value.
///
/// This function never throws an exception as far as
/// AuthCounters::getCounter() doesn't throw.
///
///
/// Note: Currently this function is for testing purpose only.
///
/// \param type Type of a counter to get the value of
......@@ -418,6 +418,25 @@ public:
void setTSIGKeyRing(const boost::shared_ptr<isc::dns::TSIGKeyRing>*
keyring);
/// \brief Create the internal forwarder for DDNS update messages
///
/// Until this method is called (it is called when the
/// start_ddns_forwarder command is sent to b10-auth), b10-auth will
/// respond to UPDATE messages with a NOTIMP rcode.
/// If the internal forwarder was already created, it is destroyed and
/// created again. This is useful for instance when b10-ddns is shut
/// down and restarted.
void createDDNSForwarder();
/// \brief Destroy the internal forwarder for DDNS update messages
///
/// After this method has been called (it is called when the
/// stop_ddns_forwarder command is sent to b10-auth), DDNS Update
/// messages are no longer forwarded internally, but b10-auth will
/// immediately respond with a NOTIMP rcode.
/// If there was no forwarder yet, this method does nothing.
void destroyDDNSForwarder();
private:
AuthSrvImpl* impl_;
isc::asiolink::SimpleCallback* checkin_;
......@@ -428,6 +447,6 @@ private:
#endif // __AUTH_SRV_H
// Local Variables:
// Local Variables:
// mode: c++
// End:
// End:
......@@ -141,6 +141,20 @@ public:
}
};
class StartDDNSForwarderCommand : public AuthCommand {
public:
virtual void exec(AuthSrv& server, isc::data::ConstElementPtr) {
server.createDDNSForwarder();
}
};
class StopDDNSForwarderCommand : public AuthCommand {
public:
virtual void exec(AuthSrv& server, isc::data::ConstElementPtr) {
server.destroyDDNSForwarder();
}
};
// Handle the "loadzone" command.
class LoadZoneCommand : public AuthCommand {
public:
......@@ -309,6 +323,10 @@ createAuthCommand(const string& command_id) {
return (new SendStatsCommand());
} else if (command_id == "loadzone") {
return (new LoadZoneCommand());
} else if (command_id == "start_ddns_forwarder") {
return (new StartDDNSForwarderCommand());
} else if (command_id == "stop_ddns_forwarder") {
return (new StopDDNSForwarderCommand());
} else if (false && command_id == "_throw_exception") {
// This is for testing purpose only and should not appear in the
// actual configuration syntax.
......
......@@ -57,3 +57,4 @@ getDDNSSocketPath() {
}
const char* const AUTH_NAME = "b10-auth";
const char* const AUTH_STARTED_NOTIFICATION = "auth_started";
......@@ -57,6 +57,11 @@ std::string getDDNSSocketPath();
/// This is currently b10-auth, but it can be changed easily in one place.
extern const char* const AUTH_NAME;
/// \brief Notification string that is used to inform auth is starting
///
/// This is sent to interested modules (currently only b10-ddns)
extern const char* const AUTH_STARTED_NOTIFICATION;
#endif // __COMMON_H
// Local Variables:
......
......@@ -210,6 +210,12 @@ main(int argc, char* argv[]) {
// Successfully initialized.
LOG_INFO(auth_logger, AUTH_SERVER_STARTED);
// Ping any interested module that (a new) auth is up
// Currently, only the DDNS module is notified, but we could consider
// make an announcement channel for these (one-way) messages
cc_session->group_sendmsg(
isc::config::createCommand(AUTH_STARTED_NOTIFICATION), "DDNS");
io_service.run();
} catch (const std::exception& ex) {
......
......@@ -31,6 +31,7 @@
#include <datasrc/memory_datasrc.h>
#include <auth/auth_srv.h>
#include <auth/command.h>
#include <auth/common.h>
#include <auth/statistics.h>
......@@ -99,6 +100,7 @@ protected:
server.setDNSService(dnss_);
server.setXfrinSession(&notify_session);
server.setStatisticsSession(&statistics_session);
server.createDDNSForwarder();
}
~AuthSrvTest() {
......@@ -106,6 +108,7 @@ protected:
// type information may be lost if the message is cleared
// automatically later, so as a precaution we do it now.
parse_message->clear(Message::PARSE);
server.destroyDDNSForwarder();
}
virtual void processMessage() {
......@@ -1625,6 +1628,7 @@ TEST_F(AuthSrvTest, DDNSForwardPushFail) {
TEST_F(AuthSrvTest, DDNSForwardClose) {
scoped_ptr<AuthSrv> tmp_server(new AuthSrv(true, xfrout, ddns_forwarder));
tmp_server->createDDNSForwarder();
UnitTestUtil::createRequestMessage(request_message, Opcode::UPDATE(),
default_qid, Name("example.com"),
RRClass::IN(), RRType::SOA());
......@@ -1639,4 +1643,85 @@ TEST_F(AuthSrvTest, DDNSForwardClose) {
EXPECT_FALSE(ddns_forwarder.isConnected());
}
namespace {
// Send a basic command without arguments, and check the response has
// result code 0
void sendSimpleCommand(AuthSrv& server, const std::string& command) {
ConstElementPtr response = execAuthServerCommand(server, command,
ConstElementPtr());
int command_result = -1;
isc::config::parseAnswer(command_result, response);
EXPECT_EQ(0, command_result);
}
} // end anonymous namespace
TEST_F(AuthSrvTest, DDNSForwardCreateDestroy) {
// Test that AuthSrv returns NOTIMP before ddns forwarder is created,
// that the ddns_forwarder is connected when the 'start_ddns_forwarder'
// command has been sent, and that it is no longer connected and auth
// returns NOTIMP after the stop_ddns_forwarding command is sent.
scoped_ptr<AuthSrv> tmp_server(new AuthSrv(true, xfrout, ddns_forwarder));
// Prepare update message to send
UnitTestUtil::createRequestMessage(request_message, Opcode::UPDATE(),
default_qid, Name("example.com"),
RRClass::IN(), RRType::SOA());
createRequestPacket(request_message, IPPROTO_UDP);
// before creating forwarder. isConnected() should be false and
// rcode to UPDATE should be NOTIMP
parse_message->clear(Message::PARSE);
tmp_server->processMessage(*io_message, *parse_message, *response_obuffer,
&dnsserv);
EXPECT_FALSE(ddns_forwarder.isConnected());
EXPECT_TRUE(dnsserv.hasAnswer());
headerCheck(*parse_message, default_qid, Rcode::NOTIMP(),
Opcode::UPDATE().getCode(), QR_FLAG, 0, 0, 0, 0);
// now create forwarder
sendSimpleCommand(*tmp_server, "start_ddns_forwarder");
// our mock does not respond, and since auth is supposed to send it on,
// there should now be no result when an UPDATE is sent
parse_message->clear(Message::PARSE);
tmp_server->processMessage(*io_message, *parse_message, *response_obuffer,
&dnsserv);
EXPECT_FALSE(dnsserv.hasAnswer());
EXPECT_TRUE(ddns_forwarder.isConnected());
// If we send a start again, the connection should be recreated,
// visible because isConnected() reports false until an actual message
// has been forwarded
sendSimpleCommand(*tmp_server, "start_ddns_forwarder");
EXPECT_FALSE(ddns_forwarder.isConnected());
parse_message->clear(Message::PARSE);
tmp_server->processMessage(*io_message, *parse_message, *response_obuffer,
&dnsserv);
EXPECT_FALSE(dnsserv.hasAnswer());
EXPECT_TRUE(ddns_forwarder.isConnected());
// Now tell it to stop forwarder, should respond with NOTIMP again
sendSimpleCommand(*tmp_server, "stop_ddns_forwarder");
parse_message->clear(Message::PARSE);
tmp_server->processMessage(*io_message, *parse_message, *response_obuffer,
&dnsserv);
EXPECT_FALSE(ddns_forwarder.isConnected());
EXPECT_TRUE(dnsserv.hasAnswer());
headerCheck(*parse_message, default_qid, Rcode::NOTIMP(),
Opcode::UPDATE().getCode(), QR_FLAG, 0, 0, 0, 0);
// Sending stop again should make no difference
sendSimpleCommand(*tmp_server, "stop_ddns_forwarder");
parse_message->clear(Message::PARSE);
tmp_server->processMessage(*io_message, *parse_message, *response_obuffer,
&dnsserv);
EXPECT_FALSE(ddns_forwarder.isConnected());
EXPECT_TRUE(dnsserv.hasAnswer());
headerCheck(*parse_message, default_qid, Rcode::NOTIMP(),
Opcode::UPDATE().getCode(), QR_FLAG, 0, 0, 0, 0);
}
}