Browse Source

b10-recurse can now act as a simple forwarder. To switch this on,
use the "-f nameserver" option to the bind10 boss script (eventually
that will have to be a config system option). This can currently only
send upstream queries via UDP, without any niceties like EDNS0, DNSSEC,
checking for QID match, etc. Also, still very poorly documented. More
to come...

git-svn-id: svn://bind10.isc.org/svn/bind10/branches/trac327@3108 e5f2f494-b856-4b98-b285-d166d9295462

Evan Hunt 14 years ago
parent
commit
f60239a803

+ 96 - 107
src/bin/auth/auth_srv.cc

@@ -72,12 +72,12 @@ public:
     ~AuthSrvImpl();
     ~AuthSrvImpl();
     isc::data::ConstElementPtr setDbFile(isc::data::ConstElementPtr config);
     isc::data::ConstElementPtr setDbFile(isc::data::ConstElementPtr config);
 
 
-    bool processNormalQuery(const IOMessage& io_message, Message& message,
-                            MessageRenderer& response_renderer);
-    bool processAxfrQuery(const IOMessage& io_message, Message& message,
-                            MessageRenderer& response_renderer);
-    bool processNotify(const IOMessage& io_message, Message& message, 
-                            MessageRenderer& response_renderer);
+    bool processNormalQuery(const IOMessage& io_message, MessagePtr message,
+                            OutputBufferPtr buffer);
+    bool processAxfrQuery(const IOMessage& io_message, MessagePtr message,
+                          OutputBufferPtr buffer);
+    bool processNotify(const IOMessage& io_message, MessagePtr message, 
+                       OutputBufferPtr buffer);
     std::string db_file_;
     std::string db_file_;
     ModuleCCSession* config_session_;
     ModuleCCSession* config_session_;
     MetaDataSrc data_sources_;
     MetaDataSrc data_sources_;
@@ -131,13 +131,10 @@ AuthSrvImpl::~AuthSrvImpl() {
 class MessageLookup : public DNSLookup {
 class MessageLookup : public DNSLookup {
 public:
 public:
     MessageLookup(AuthSrv* srv) : server_(srv) {}
     MessageLookup(AuthSrv* srv) : server_(srv) {}
-    virtual void operator()(const IOMessage& io_message,
-                            isc::dns::Message& dns_message,
-                            isc::dns::MessageRenderer& renderer,
-                            BasicServer* server, bool& complete) const
+    virtual void operator()(const IOMessage& io_message, MessagePtr message,
+                            OutputBufferPtr buffer, IOServer* server) const
     {
     {
-        server_->processMessage(io_message, dns_message, renderer,
-                                server, complete);
+        server_->processMessage(io_message, message, buffer, server);
     }
     }
 private:
 private:
     AuthSrv* server_;
     AuthSrv* server_;
@@ -150,19 +147,19 @@ private:
 class MessageAnswer : public DNSAnswer {
 class MessageAnswer : public DNSAnswer {
 public:
 public:
     MessageAnswer(AuthSrv* srv) : server_(srv) {}
     MessageAnswer(AuthSrv* srv) : server_(srv) {}
-    virtual void operator()(const IOMessage& io_message,
-                            isc::dns::Message& message,
-                            isc::dns::MessageRenderer& renderer) const
+    virtual void operator()(const IOMessage& io_message, MessagePtr message,
+                            OutputBufferPtr buffer) const
     {
     {
+        MessageRenderer renderer(*buffer);
         if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
         if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
-            renderer.setLengthLimit(message.getUDPSize());
+            renderer.setLengthLimit(message->getUDPSize());
         } else {
         } else {
             renderer.setLengthLimit(65535);
             renderer.setLengthLimit(65535);
         }
         }
-        message.toWire(renderer);
+        message->toWire(renderer);
         if (server_->getVerbose()) {
         if (server_->getVerbose()) {
             cerr << "[b10-recurse] sending a response (" << renderer.getLength()
             cerr << "[b10-recurse] sending a response (" << renderer.getLength()
-                 << " bytes):\n" << message.toText() << endl;
+                 << " bytes):\n" << message->toText() << endl;
         }
         }
     }
     }
 
 
@@ -202,50 +199,52 @@ AuthSrv::~AuthSrv() {
 namespace {
 namespace {
 class QuestionInserter {
 class QuestionInserter {
 public:
 public:
-    QuestionInserter(Message* message) : message_(message) {}
+    QuestionInserter(MessagePtr message) : message_(message) {}
     void operator()(const QuestionPtr question) {
     void operator()(const QuestionPtr question) {
         message_->addQuestion(question);
         message_->addQuestion(question);
     }
     }
-    Message* message_;
+    MessagePtr message_;
 };
 };
 
 
 void
 void
-makeErrorMessage(Message& message, MessageRenderer& renderer,
+makeErrorMessage(MessagePtr message, OutputBufferPtr buffer,
                  const Rcode& rcode, const bool verbose_mode)
                  const Rcode& rcode, const bool verbose_mode)
 {
 {
     // extract the parameters that should be kept.
     // extract the parameters that should be kept.
     // XXX: with the current implementation, it's not easy to set EDNS0
     // XXX: with the current implementation, it's not easy to set EDNS0
     // depending on whether the query had it.  So we'll simply omit it.
     // depending on whether the query had it.  So we'll simply omit it.
-    const qid_t qid = message.getQid();
-    const bool rd = message.getHeaderFlag(MessageFlag::RD());
-    const bool cd = message.getHeaderFlag(MessageFlag::CD());
-    const Opcode& opcode = message.getOpcode();
+    const qid_t qid = message->getQid();
+    const bool rd = message->getHeaderFlag(MessageFlag::RD());
+    const bool cd = message->getHeaderFlag(MessageFlag::CD());
+    const Opcode& opcode = message->getOpcode();
     vector<QuestionPtr> questions;
     vector<QuestionPtr> questions;
 
 
     // If this is an error to a query or notify, we should also copy the
     // If this is an error to a query or notify, we should also copy the
     // question section.
     // question section.
     if (opcode == Opcode::QUERY() || opcode == Opcode::NOTIFY()) {
     if (opcode == Opcode::QUERY() || opcode == Opcode::NOTIFY()) {
-        questions.assign(message.beginQuestion(), message.endQuestion());
+        questions.assign(message->beginQuestion(), message->endQuestion());
     }
     }
 
 
-    message.clear(Message::RENDER);
-    message.setQid(qid);
-    message.setOpcode(opcode);
-    message.setHeaderFlag(MessageFlag::QR());
-    message.setUDPSize(AuthSrvImpl::DEFAULT_LOCAL_UDPSIZE);
+    message->clear(Message::RENDER);
+    message->setQid(qid);
+    message->setOpcode(opcode);
+    message->setHeaderFlag(MessageFlag::QR());
+    message->setUDPSize(AuthSrvImpl::DEFAULT_LOCAL_UDPSIZE);
     if (rd) {
     if (rd) {
-        message.setHeaderFlag(MessageFlag::RD());
+        message->setHeaderFlag(MessageFlag::RD());
     }
     }
     if (cd) {
     if (cd) {
-        message.setHeaderFlag(MessageFlag::CD());
+        message->setHeaderFlag(MessageFlag::CD());
     }
     }
-    for_each(questions.begin(), questions.end(), QuestionInserter(&message));
-    message.setRcode(rcode);
-    message.toWire(renderer);
+    for_each(questions.begin(), questions.end(), QuestionInserter(message));
+    message->setRcode(rcode);
+
+    MessageRenderer renderer(*buffer);
+    message->toWire(renderer);
 
 
     if (verbose_mode) {
     if (verbose_mode) {
         cerr << "[b10-auth] sending an error response (" <<
         cerr << "[b10-auth] sending an error response (" <<
-            renderer.getLength() << " bytes):\n" << message.toText() << endl;
+            renderer.getLength() << " bytes):\n" << message->toText() << endl;
     }
     }
 }
 }
 }
 }
@@ -276,148 +275,139 @@ AuthSrv::configSession() const {
 }
 }
 
 
 void
 void
-AuthSrv::processMessage(const IOMessage& io_message, Message& message,
-                        MessageRenderer& response_renderer,
-                        BasicServer* server, bool& complete)
+AuthSrv::processMessage(const IOMessage& io_message, MessagePtr message,
+                        OutputBufferPtr buffer, IOServer* server)
 {
 {
     InputBuffer request_buffer(io_message.getData(), io_message.getDataSize());
     InputBuffer request_buffer(io_message.getData(), io_message.getDataSize());
 
 
     // First, check the header part.  If we fail even for the base header,
     // First, check the header part.  If we fail even for the base header,
     // just drop the message.
     // just drop the message.
     try {
     try {
-        message.parseHeader(request_buffer);
+        message->parseHeader(request_buffer);
 
 
         // Ignore all responses.
         // Ignore all responses.
-        if (message.getHeaderFlag(MessageFlag::QR())) {
+        if (message->getHeaderFlag(MessageFlag::QR())) {
             if (impl_->verbose_mode_) {
             if (impl_->verbose_mode_) {
                 cerr << "[b10-auth] received unexpected response, ignoring"
                 cerr << "[b10-auth] received unexpected response, ignoring"
                      << endl;
                      << endl;
             }
             }
-            complete = false;
-            server->resume();
+            server->resume(false);
             return;
             return;
         }
         }
     } catch (const Exception& ex) {
     } catch (const Exception& ex) {
         if (impl_->verbose_mode_) {
         if (impl_->verbose_mode_) {
             cerr << "[b10-auth] DNS packet exception: " << ex.what() << endl;
             cerr << "[b10-auth] DNS packet exception: " << ex.what() << endl;
         }
         }
-        complete = false;
-        server->resume();
+        server->resume(false);
         return;
         return;
     }
     }
 
 
     try {
     try {
         // Parse the message.
         // Parse the message.
-        message.fromWire(request_buffer);
+        message->fromWire(request_buffer);
     } catch (const DNSProtocolError& error) {
     } catch (const DNSProtocolError& error) {
         if (impl_->verbose_mode_) {
         if (impl_->verbose_mode_) {
             cerr << "[b10-auth] returning " <<  error.getRcode().toText()
             cerr << "[b10-auth] returning " <<  error.getRcode().toText()
                  << ": " << error.what() << endl;
                  << ": " << error.what() << endl;
         }
         }
-        makeErrorMessage(message, response_renderer, error.getRcode(),
+        makeErrorMessage(message, buffer, error.getRcode(),
                          impl_->verbose_mode_);
                          impl_->verbose_mode_);
-        complete = true;
-        server->resume();
+        server->resume(true);
         return;
         return;
     } catch (const Exception& ex) {
     } catch (const Exception& ex) {
         if (impl_->verbose_mode_) {
         if (impl_->verbose_mode_) {
             cerr << "[b10-auth] returning SERVFAIL: " << ex.what() << endl;
             cerr << "[b10-auth] returning SERVFAIL: " << ex.what() << endl;
         }
         }
-        makeErrorMessage(message, response_renderer, Rcode::SERVFAIL(),
+        makeErrorMessage(message, buffer, Rcode::SERVFAIL(),
                          impl_->verbose_mode_);
                          impl_->verbose_mode_);
-        complete = true;
-        server->resume();
+        server->resume(true);
         return;
         return;
     } // other exceptions will be handled at a higher layer.
     } // other exceptions will be handled at a higher layer.
 
 
     if (impl_->verbose_mode_) {
     if (impl_->verbose_mode_) {
-        cerr << "[b10-auth] received a message:\n" << message.toText() << endl;
+        cerr << "[b10-auth] received a message:\n" << message->toText() << endl;
     }
     }
 
 
     // Perform further protocol-level validation.
     // Perform further protocol-level validation.
 
 
-    if (message.getOpcode() == Opcode::NOTIFY()) {
-        complete = impl_->processNotify(io_message, message,
-                                         response_renderer);
-    } else if (message.getOpcode() != Opcode::QUERY()) {
+    bool sendAnswer = true;
+    if (message->getOpcode() == Opcode::NOTIFY()) {
+        sendAnswer = impl_->processNotify(io_message, message, buffer);
+    } else if (message->getOpcode() != Opcode::QUERY()) {
         if (impl_->verbose_mode_) {
         if (impl_->verbose_mode_) {
             cerr << "[b10-auth] unsupported opcode" << endl;
             cerr << "[b10-auth] unsupported opcode" << endl;
         }
         }
-        makeErrorMessage(message, response_renderer, Rcode::NOTIMP(),
+        makeErrorMessage(message, buffer, Rcode::NOTIMP(),
                          impl_->verbose_mode_);
                          impl_->verbose_mode_);
-        complete = true;
-    } else if (message.getRRCount(Section::QUESTION()) != 1) {
-        makeErrorMessage(message, response_renderer, Rcode::FORMERR(),
+    } else if (message->getRRCount(Section::QUESTION()) != 1) {
+        makeErrorMessage(message, buffer, Rcode::FORMERR(),
                          impl_->verbose_mode_);
                          impl_->verbose_mode_);
-        complete = true;
     } else {
     } else {
-        ConstQuestionPtr question = *message.beginQuestion();
+        ConstQuestionPtr question = *message->beginQuestion();
         const RRType &qtype = question->getType();
         const RRType &qtype = question->getType();
         if (qtype == RRType::AXFR()) {
         if (qtype == RRType::AXFR()) {
-            complete = impl_->processAxfrQuery(io_message, message,
-                                                response_renderer);
+            sendAnswer = impl_->processAxfrQuery(io_message, message, buffer);
         } else if (qtype == RRType::IXFR()) {
         } else if (qtype == RRType::IXFR()) {
-            makeErrorMessage(message, response_renderer, Rcode::NOTIMP(),
-                         impl_->verbose_mode_);
-            complete = true;
+            makeErrorMessage(message, buffer, Rcode::NOTIMP(),
+                             impl_->verbose_mode_);
         } else {
         } else {
-            complete = impl_->processNormalQuery(io_message, message,
-                                               response_renderer);
+            sendAnswer = impl_->processNormalQuery(io_message, message, buffer);
         }
         }
     }
     }
 
 
-    server->resume();
+    server->resume(sendAnswer);
 }
 }
 
 
 bool
 bool
-AuthSrvImpl::processNormalQuery(const IOMessage& io_message, Message& message,
-                                MessageRenderer& response_renderer)
+AuthSrvImpl::processNormalQuery(const IOMessage& io_message, MessagePtr message,
+                                OutputBufferPtr buffer)
 {
 {
-    const bool dnssec_ok = message.isDNSSECSupported();
-    const uint16_t remote_bufsize = message.getUDPSize();
+    const bool dnssec_ok = message->isDNSSECSupported();
+    const uint16_t remote_bufsize = message->getUDPSize();
 
 
-    message.makeResponse();
-    message.setHeaderFlag(MessageFlag::AA());
-    message.setRcode(Rcode::NOERROR());
-    message.setDNSSECSupported(dnssec_ok);
-    message.setUDPSize(AuthSrvImpl::DEFAULT_LOCAL_UDPSIZE);
+    message->makeResponse();
+    message->setHeaderFlag(MessageFlag::AA());
+    message->setRcode(Rcode::NOERROR());
+    message->setDNSSECSupported(dnssec_ok);
+    message->setUDPSize(AuthSrvImpl::DEFAULT_LOCAL_UDPSIZE);
 
 
     try {
     try {
-        Query query(message, cache_, dnssec_ok);
+        Query query(*message, cache_, dnssec_ok);
         data_sources_.doQuery(query);
         data_sources_.doQuery(query);
     } catch (const Exception& ex) {
     } catch (const Exception& ex) {
         if (verbose_mode_) {
         if (verbose_mode_) {
             cerr << "[b10-auth] Internal error, returning SERVFAIL: " <<
             cerr << "[b10-auth] Internal error, returning SERVFAIL: " <<
                 ex.what() << endl;
                 ex.what() << endl;
         }
         }
-        makeErrorMessage(message, response_renderer, Rcode::SERVFAIL(),
-                         verbose_mode_);
+        makeErrorMessage(message, buffer, Rcode::SERVFAIL(), verbose_mode_);
         return (true);
         return (true);
     }
     }
 
 
+
+    MessageRenderer renderer(*buffer);
     const bool udp_buffer =
     const bool udp_buffer =
         (io_message.getSocket().getProtocol() == IPPROTO_UDP);
         (io_message.getSocket().getProtocol() == IPPROTO_UDP);
-    response_renderer.setLengthLimit(udp_buffer ? remote_bufsize : 65535);
-    message.toWire(response_renderer);
+    renderer.setLengthLimit(udp_buffer ? remote_bufsize : 65535);
+    message->toWire(renderer);
+
     if (verbose_mode_) {
     if (verbose_mode_) {
         cerr << "[b10-auth] sending a response ("
         cerr << "[b10-auth] sending a response ("
-             << response_renderer.getLength()
-             << " bytes):\n" << message.toText() << endl;
+             << renderer.getLength()
+             << " bytes):\n" << message->toText() << endl;
     }
     }
 
 
     return (true);
     return (true);
 }
 }
 
 
 bool
 bool
-AuthSrvImpl::processAxfrQuery(const IOMessage& io_message, Message& message,
-                            MessageRenderer& response_renderer)
+AuthSrvImpl::processAxfrQuery(const IOMessage& io_message, MessagePtr message,
+                              OutputBufferPtr buffer)
 {
 {
     if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
     if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
         if (verbose_mode_) {
         if (verbose_mode_) {
             cerr << "[b10-auth] AXFR query over UDP isn't allowed" << endl;
             cerr << "[b10-auth] AXFR query over UDP isn't allowed" << endl;
         }
         }
-        makeErrorMessage(message, response_renderer, Rcode::FORMERR(),
-                         verbose_mode_);
+        makeErrorMessage(message, buffer, Rcode::FORMERR(), verbose_mode_);
         return (true);
         return (true);
     }
     }
 
 
@@ -442,8 +432,7 @@ AuthSrvImpl::processAxfrQuery(const IOMessage& io_message, Message& message,
             cerr << "[b10-auth] Error in handling XFR request: " << err.what()
             cerr << "[b10-auth] Error in handling XFR request: " << err.what()
                  << endl;
                  << endl;
         }
         }
-        makeErrorMessage(message, response_renderer, Rcode::SERVFAIL(),
-                         verbose_mode_);
+        makeErrorMessage(message, buffer, Rcode::SERVFAIL(), verbose_mode_);
         return (true);
         return (true);
     }
     }
 
 
@@ -454,28 +443,26 @@ AuthSrvImpl::processAxfrQuery(const IOMessage& io_message, Message& message,
 }
 }
 
 
 bool
 bool
-AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message, 
-                           MessageRenderer& response_renderer) 
+AuthSrvImpl::processNotify(const IOMessage& io_message, MessagePtr message, 
+                           OutputBufferPtr buffer)
 {
 {
     // The incoming notify must contain exactly one question for SOA of the
     // The incoming notify must contain exactly one question for SOA of the
     // zone name.
     // zone name.
-    if (message.getRRCount(Section::QUESTION()) != 1) {
+    if (message->getRRCount(Section::QUESTION()) != 1) {
         if (verbose_mode_) {
         if (verbose_mode_) {
                 cerr << "[b10-auth] invalid number of questions in notify: "
                 cerr << "[b10-auth] invalid number of questions in notify: "
-                     << message.getRRCount(Section::QUESTION()) << endl;
+                     << message->getRRCount(Section::QUESTION()) << endl;
         }
         }
-        makeErrorMessage(message, response_renderer, Rcode::FORMERR(),
-                         verbose_mode_);
+        makeErrorMessage(message, buffer, Rcode::FORMERR(), verbose_mode_);
         return (true);
         return (true);
     }
     }
-    ConstQuestionPtr question = *message.beginQuestion();
+    ConstQuestionPtr question = *message->beginQuestion();
     if (question->getType() != RRType::SOA()) {
     if (question->getType() != RRType::SOA()) {
         if (verbose_mode_) {
         if (verbose_mode_) {
                 cerr << "[b10-auth] invalid question RR type in notify: "
                 cerr << "[b10-auth] invalid question RR type in notify: "
                      << question->getType() << endl;
                      << question->getType() << endl;
         }
         }
-        makeErrorMessage(message, response_renderer, Rcode::FORMERR(),
-                         verbose_mode_);
+        makeErrorMessage(message, buffer, Rcode::FORMERR(), verbose_mode_);
         return (true);
         return (true);
     }
     }
 
 
@@ -533,10 +520,12 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message,
         return (false);
         return (false);
     }
     }
 
 
-    message.makeResponse();
-    message.setHeaderFlag(MessageFlag::AA());
-    message.setRcode(Rcode::NOERROR());
-    message.toWire(response_renderer);
+    message->makeResponse();
+    message->setHeaderFlag(MessageFlag::AA());
+    message->setRcode(Rcode::NOERROR());
+
+    MessageRenderer renderer(*buffer);
+    message->toWire(renderer);
     return (true);
     return (true);
 }
 }
 
 

+ 3 - 9
src/bin/auth/auth_srv.h

@@ -25,12 +25,6 @@
 #include <asiolink/asiolink.h>
 #include <asiolink/asiolink.h>
 
 
 namespace isc {
 namespace isc {
-namespace dns {
-class InputBuffer;
-class Message;
-class MessageRenderer;
-}
-
 namespace xfr {
 namespace xfr {
 class AbstractXfroutClient;
 class AbstractXfroutClient;
 }
 }
@@ -67,9 +61,9 @@ public:
     /// \return \c true if the \message contains a response to be returned;
     /// \return \c true if the \message contains a response to be returned;
     /// otherwise \c false.
     /// otherwise \c false.
     void processMessage(const asiolink::IOMessage& io_message,
     void processMessage(const asiolink::IOMessage& io_message,
-                        isc::dns::Message& message,
-                        isc::dns::MessageRenderer& response_renderer,
-                        asiolink::BasicServer* server, bool& complete);
+                        isc::dns::MessagePtr message,
+                        isc::dns::OutputBufferPtr buffer,
+                        asiolink::IOServer* server);
     void setVerbose(bool on);
     void setVerbose(bool on);
     bool getVerbose() const;
     bool getVerbose() const;
     isc::data::ConstElementPtr updateConfig(isc::data::ConstElementPtr config);
     isc::data::ConstElementPtr updateConfig(isc::data::ConstElementPtr config);

+ 104 - 152
src/bin/auth/tests/auth_srv_unittest.cc

@@ -123,21 +123,29 @@ private:
     };
     };
 
 
     // A nonoperative task object to be used in calls to processMessage()
     // A nonoperative task object to be used in calls to processMessage()
-    class MockTask : public BasicServer {
+    class MockTask : public IOServer {
+    public:
+        MockTask() : done_(false) {}
         void operator()(asio::error_code ec UNUSED_PARAM,
         void operator()(asio::error_code ec UNUSED_PARAM,
                         size_t length UNUSED_PARAM)
                         size_t length UNUSED_PARAM)
         {}
         {}
+        // virtual void doLookup() { return; }
+        virtual void resume(const bool done) { done_ = done; }
+        virtual bool hasAnswer() { return (done_); }
+        virtual int value() { return (0); }
+    private:
+        bool done_;
     };
     };
 
 
 protected:
 protected:
     AuthSrvTest() : server(true, xfrout),
     AuthSrvTest() : server(true, xfrout),
                     request_message(Message::RENDER),
                     request_message(Message::RENDER),
-                    parse_message(Message::PARSE), default_qid(0x1035),
-                    opcode(Opcode(Opcode::QUERY())), qname("www.example.com"),
-                    qclass(RRClass::IN()), qtype(RRType::A()),
-                    io_message(NULL), endpoint(NULL), request_obuffer(0),
-                    request_renderer(request_obuffer),
-                    response_obuffer(0), response_renderer(response_obuffer)
+                    parse_message(new Message(Message::PARSE)),
+                    default_qid(0x1035), opcode(Opcode(Opcode::QUERY())),
+                    qname("www.example.com"), qclass(RRClass::IN()),
+                    qtype(RRType::A()), io_message(NULL), endpoint(NULL),
+                    request_obuffer(0), request_renderer(request_obuffer),
+                    response_obuffer(new OutputBuffer(0))
     {
     {
         server.setXfrinSession(&notify_session);
         server.setXfrinSession(&notify_session);
     }
     }
@@ -147,10 +155,10 @@ protected:
     }
     }
     MockSession notify_session;
     MockSession notify_session;
     MockXfroutClient xfrout;
     MockXfroutClient xfrout;
-    MockTask noOp;
+    MockTask task;
     AuthSrv server;
     AuthSrv server;
     Message request_message;
     Message request_message;
-    Message parse_message;
+    MessagePtr parse_message;
     const qid_t default_qid;
     const qid_t default_qid;
     const Opcode opcode;
     const Opcode opcode;
     const Name qname;
     const Name qname;
@@ -161,8 +169,7 @@ protected:
     const IOEndpoint* endpoint;
     const IOEndpoint* endpoint;
     OutputBuffer request_obuffer;
     OutputBuffer request_obuffer;
     MessageRenderer request_renderer;
     MessageRenderer request_renderer;
-    OutputBuffer response_obuffer;
-    MessageRenderer response_renderer;
+    OutputBufferPtr response_obuffer;
     vector<uint8_t> data;
     vector<uint8_t> data;
 
 
     void createDataFromFile(const char* const datafile, int protocol);
     void createDataFromFile(const char* const datafile, int protocol);
@@ -365,12 +372,11 @@ TEST_F(AuthSrvTest, unsupportedRequest) {
         createDataFromFile("simplequery_fromWire");
         createDataFromFile("simplequery_fromWire");
         data[2] = ((i << 3) & 0xff);
         data[2] = ((i << 3) & 0xff);
 
 
-        parse_message.clear(Message::PARSE);
-        bool done;
-        server.processMessage(*io_message, parse_message, response_renderer,
-                              &noOp, done);
-    EXPECT_TRUE(done);
-        headerCheck(parse_message, default_qid, Rcode::NOTIMP(), i, QR_FLAG,
+        parse_message->clear(Message::PARSE);
+        server.processMessage(*io_message, parse_message, response_obuffer,
+                              &task);
+    EXPECT_TRUE(task.hasAnswer());
+        headerCheck(*parse_message, default_qid, Rcode::NOTIMP(), i, QR_FLAG,
                     0, 0, 0, 0);
                     0, 0, 0, 0);
     }
     }
 }
 }
@@ -387,14 +393,12 @@ TEST_F(AuthSrvTest, verbose) {
 // Multiple questions.  Should result in FORMERR.
 // Multiple questions.  Should result in FORMERR.
 TEST_F(AuthSrvTest, multiQuestion) {
 TEST_F(AuthSrvTest, multiQuestion) {
     createDataFromFile("multiquestion_fromWire");
     createDataFromFile("multiquestion_fromWire");
-    bool done;
-    server.processMessage(*io_message, parse_message, response_renderer,
-                          &noOp, done);
-    EXPECT_TRUE(done);
-    headerCheck(parse_message, default_qid, Rcode::FORMERR(), opcode.getCode(),
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_TRUE(task.hasAnswer());
+    headerCheck(*parse_message, default_qid, Rcode::FORMERR(), opcode.getCode(),
                 QR_FLAG, 2, 0, 0, 0);
                 QR_FLAG, 2, 0, 0, 0);
 
 
-    QuestionIterator qit = parse_message.beginQuestion();
+    QuestionIterator qit = parse_message->beginQuestion();
     EXPECT_EQ(Name("example.com"), (*qit)->getName());
     EXPECT_EQ(Name("example.com"), (*qit)->getName());
     EXPECT_EQ(RRClass::IN(), (*qit)->getClass());
     EXPECT_EQ(RRClass::IN(), (*qit)->getClass());
     EXPECT_EQ(RRType::A(), (*qit)->getType());
     EXPECT_EQ(RRType::A(), (*qit)->getType());
@@ -403,17 +407,15 @@ TEST_F(AuthSrvTest, multiQuestion) {
     EXPECT_EQ(RRClass::IN(), (*qit)->getClass());
     EXPECT_EQ(RRClass::IN(), (*qit)->getClass());
     EXPECT_EQ(RRType::AAAA(), (*qit)->getType());
     EXPECT_EQ(RRType::AAAA(), (*qit)->getType());
     ++qit;
     ++qit;
-    EXPECT_TRUE(qit == parse_message.endQuestion());
+    EXPECT_TRUE(qit == parse_message->endQuestion());
 }
 }
 
 
 // Incoming data doesn't even contain the complete header.  Must be silently
 // Incoming data doesn't even contain the complete header.  Must be silently
 // dropped.
 // dropped.
 TEST_F(AuthSrvTest, shortMessage) {
 TEST_F(AuthSrvTest, shortMessage) {
     createDataFromFile("shortmessage_fromWire");
     createDataFromFile("shortmessage_fromWire");
-    bool done;
-    server.processMessage(*io_message, parse_message, response_renderer,
-                          &noOp, done);
-    EXPECT_FALSE(done);
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_FALSE(task.hasAnswer());
 }
 }
 
 
 // Response messages.  Must be silently dropped, whether it's a valid response
 // Response messages.  Must be silently dropped, whether it's a valid response
@@ -421,85 +423,73 @@ TEST_F(AuthSrvTest, shortMessage) {
 TEST_F(AuthSrvTest, response) {
 TEST_F(AuthSrvTest, response) {
     // A valid (although unusual) response
     // A valid (although unusual) response
     createDataFromFile("simpleresponse_fromWire");
     createDataFromFile("simpleresponse_fromWire");
-    bool done;
-    server.processMessage(*io_message, parse_message, response_renderer,
-                          &noOp, done);
-    EXPECT_FALSE(done);
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_FALSE(task.hasAnswer());
 
 
     // A response with a broken question section.  must be dropped rather than
     // A response with a broken question section.  must be dropped rather than
     // returning FORMERR.
     // returning FORMERR.
     createDataFromFile("shortresponse_fromWire");
     createDataFromFile("shortresponse_fromWire");
-    server.processMessage(*io_message, parse_message, response_renderer,
-                          &noOp, done);
-    EXPECT_FALSE(done);
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_FALSE(task.hasAnswer());
 
 
     // A response to iquery.  must be dropped rather than returning NOTIMP.
     // A response to iquery.  must be dropped rather than returning NOTIMP.
     createDataFromFile("iqueryresponse_fromWire");
     createDataFromFile("iqueryresponse_fromWire");
-    server.processMessage(*io_message, parse_message, response_renderer,
-                          &noOp, done);
-    EXPECT_FALSE(done);
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_FALSE(task.hasAnswer());
 }
 }
 
 
 // Query with a broken question
 // Query with a broken question
 TEST_F(AuthSrvTest, shortQuestion) {
 TEST_F(AuthSrvTest, shortQuestion) {
     createDataFromFile("shortquestion_fromWire");
     createDataFromFile("shortquestion_fromWire");
-    bool done;
-    server.processMessage(*io_message, parse_message, response_renderer,
-                          &noOp, done);
-    EXPECT_TRUE(done);
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_TRUE(task.hasAnswer());
     // Since the query's question is broken, the question section of the
     // Since the query's question is broken, the question section of the
     // response should be empty.
     // response should be empty.
-    headerCheck(parse_message, default_qid, Rcode::FORMERR(), opcode.getCode(),
+    headerCheck(*parse_message, default_qid, Rcode::FORMERR(), opcode.getCode(),
                 QR_FLAG, 0, 0, 0, 0);
                 QR_FLAG, 0, 0, 0, 0);
 }
 }
 
 
 // Query with a broken answer section
 // Query with a broken answer section
 TEST_F(AuthSrvTest, shortAnswer) {
 TEST_F(AuthSrvTest, shortAnswer) {
     createDataFromFile("shortanswer_fromWire");
     createDataFromFile("shortanswer_fromWire");
-    bool done;
-    server.processMessage(*io_message, parse_message, response_renderer,
-                          &noOp, done);
-    EXPECT_TRUE(done);
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_TRUE(task.hasAnswer());
 
 
     // This is a bogus query, but question section is valid.  So the response
     // This is a bogus query, but question section is valid.  So the response
     // should copy the question section.
     // should copy the question section.
-    headerCheck(parse_message, default_qid, Rcode::FORMERR(), opcode.getCode(),
+    headerCheck(*parse_message, default_qid, Rcode::FORMERR(), opcode.getCode(),
                 QR_FLAG, 1, 0, 0, 0);
                 QR_FLAG, 1, 0, 0, 0);
 
 
-    QuestionIterator qit = parse_message.beginQuestion();
+    QuestionIterator qit = parse_message->beginQuestion();
     EXPECT_EQ(Name("example.com"), (*qit)->getName());
     EXPECT_EQ(Name("example.com"), (*qit)->getName());
     EXPECT_EQ(RRClass::IN(), (*qit)->getClass());
     EXPECT_EQ(RRClass::IN(), (*qit)->getClass());
     EXPECT_EQ(RRType::A(), (*qit)->getType());
     EXPECT_EQ(RRType::A(), (*qit)->getType());
     ++qit;
     ++qit;
-    EXPECT_TRUE(qit == parse_message.endQuestion());
+    EXPECT_TRUE(qit == parse_message->endQuestion());
 }
 }
 
 
 // Query with unsupported version of EDNS.
 // Query with unsupported version of EDNS.
 TEST_F(AuthSrvTest, ednsBadVers) {
 TEST_F(AuthSrvTest, ednsBadVers) {
     createDataFromFile("queryBadEDNS_fromWire");
     createDataFromFile("queryBadEDNS_fromWire");
-    bool done;
-    server.processMessage(*io_message, parse_message, response_renderer,
-                          &noOp, done);
-    EXPECT_TRUE(done);
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_TRUE(task.hasAnswer());
 
 
     // The response must have an EDNS OPT RR in the additional section.
     // The response must have an EDNS OPT RR in the additional section.
     // Note that the DNSSEC DO bit is cleared even if this bit in the query
     // Note that the DNSSEC DO bit is cleared even if this bit in the query
     // is set.  This is a limitation of the current implementation.
     // is set.  This is a limitation of the current implementation.
-    headerCheck(parse_message, default_qid, Rcode::BADVERS(), opcode.getCode(),
+    headerCheck(*parse_message, default_qid, Rcode::BADVERS(), opcode.getCode(),
                 QR_FLAG, 1, 0, 0, 1);
                 QR_FLAG, 1, 0, 0, 1);
-    EXPECT_EQ(4096, parse_message.getUDPSize());
-    EXPECT_FALSE(parse_message.isDNSSECSupported());
+    EXPECT_EQ(4096, parse_message->getUDPSize());
+    EXPECT_FALSE(parse_message->isDNSSECSupported());
 }
 }
 
 
 TEST_F(AuthSrvTest, AXFROverUDP) {
 TEST_F(AuthSrvTest, AXFROverUDP) {
     // AXFR over UDP is invalid and should result in FORMERR.
     // AXFR over UDP is invalid and should result in FORMERR.
     createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
     createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
                         RRType::AXFR(), IPPROTO_UDP);
                         RRType::AXFR(), IPPROTO_UDP);
-    bool done;
-    server.processMessage(*io_message, parse_message, response_renderer,
-                          &noOp, done);
-    EXPECT_TRUE(done);
-    headerCheck(parse_message, default_qid, Rcode::FORMERR(), opcode.getCode(),
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_TRUE(task.hasAnswer());
+    headerCheck(*parse_message, default_qid, Rcode::FORMERR(), opcode.getCode(),
                 QR_FLAG, 1, 0, 0, 0);
                 QR_FLAG, 1, 0, 0, 0);
 }
 }
 
 
@@ -509,10 +499,8 @@ TEST_F(AuthSrvTest, AXFRSuccess) {
                         RRType::AXFR(), IPPROTO_TCP);
                         RRType::AXFR(), IPPROTO_TCP);
     // On success, the AXFR query has been passed to a separate process,
     // On success, the AXFR query has been passed to a separate process,
     // so we shouldn't have to respond.
     // so we shouldn't have to respond.
-    bool done;
-    server.processMessage(*io_message, parse_message, response_renderer,
-                          &noOp, done);
-    EXPECT_FALSE(done);
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_FALSE(task.hasAnswer());
     EXPECT_FALSE(xfrout.isConnected());
     EXPECT_FALSE(xfrout.isConnected());
 }
 }
 
 
@@ -521,11 +509,9 @@ TEST_F(AuthSrvTest, AXFRConnectFail) {
     xfrout.disableConnect();
     xfrout.disableConnect();
     createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
     createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
                         RRType::AXFR(), IPPROTO_TCP);
                         RRType::AXFR(), IPPROTO_TCP);
-    bool done;
-    server.processMessage(*io_message, parse_message, response_renderer,
-                          &noOp, done);
-    EXPECT_TRUE(done);
-    headerCheck(parse_message, default_qid, Rcode::SERVFAIL(),
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_TRUE(task.hasAnswer());
+    headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
                 opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
                 opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
     // For a shot term workaround with xfrout we currently close the connection
     // For a shot term workaround with xfrout we currently close the connection
     // for each AXFR attempt
     // for each AXFR attempt
@@ -537,20 +523,17 @@ TEST_F(AuthSrvTest, AXFRSendFail) {
     // open.
     // open.
     createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
     createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
                         RRType::AXFR(), IPPROTO_TCP);
                         RRType::AXFR(), IPPROTO_TCP);
-    bool done;
-    server.processMessage(*io_message, parse_message, response_renderer,
-                          &noOp, done);
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
     EXPECT_FALSE(xfrout.isConnected()); // see above
     EXPECT_FALSE(xfrout.isConnected()); // see above
 
 
     xfrout.disableSend();
     xfrout.disableSend();
-    parse_message.clear(Message::PARSE);
-    response_renderer.clear();
+    parse_message->clear(Message::PARSE);
+    response_obuffer->clear();
     createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
     createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
                         RRType::AXFR(), IPPROTO_TCP);
                         RRType::AXFR(), IPPROTO_TCP);
-    server.processMessage(*io_message, parse_message, response_renderer,
-                          &noOp, done);
-    EXPECT_TRUE(done);
-    headerCheck(parse_message, default_qid, Rcode::SERVFAIL(),
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_TRUE(task.hasAnswer());
+    headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
                 opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
                 opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
 
 
     // The connection should have been closed due to the send failure.
     // The connection should have been closed due to the send failure.
@@ -564,9 +547,8 @@ TEST_F(AuthSrvTest, AXFRDisconnectFail) {
     xfrout.disableDisconnect();
     xfrout.disableDisconnect();
     createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
     createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
                         RRType::AXFR(), IPPROTO_TCP);
                         RRType::AXFR(), IPPROTO_TCP);
-    bool done;
     EXPECT_THROW(server.processMessage(*io_message, parse_message,
     EXPECT_THROW(server.processMessage(*io_message, parse_message,
-                                       response_renderer, &noOp, done),
+                                       response_obuffer, &task),
                  XfroutError);
                  XfroutError);
     EXPECT_TRUE(xfrout.isConnected());
     EXPECT_TRUE(xfrout.isConnected());
     // XXX: we need to re-enable disconnect.  otherwise an exception would be
     // XXX: we need to re-enable disconnect.  otherwise an exception would be
@@ -579,10 +561,8 @@ TEST_F(AuthSrvTest, notify) {
                         RRType::SOA());
                         RRType::SOA());
     request_message.setHeaderFlag(MessageFlag::AA());
     request_message.setHeaderFlag(MessageFlag::AA());
     createRequestPacket(IPPROTO_UDP);
     createRequestPacket(IPPROTO_UDP);
-    bool done;
-    server.processMessage(*io_message, parse_message, response_renderer,
-                          &noOp, done);
-    EXPECT_TRUE(done);
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_TRUE(task.hasAnswer());
 
 
     // An internal command message should have been created and sent to an
     // An internal command message should have been created and sent to an
     // external module.  Check them.
     // external module.  Check them.
@@ -597,11 +577,11 @@ TEST_F(AuthSrvTest, notify) {
     EXPECT_EQ("IN", notify_args->get("zone_class")->stringValue());
     EXPECT_EQ("IN", notify_args->get("zone_class")->stringValue());
 
 
     // On success, the server should return a response to the notify.
     // On success, the server should return a response to the notify.
-    headerCheck(parse_message, default_qid, Rcode::NOERROR(),
+    headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
                 Opcode::NOTIFY().getCode(), QR_FLAG | AA_FLAG, 1, 0, 0, 0);
                 Opcode::NOTIFY().getCode(), QR_FLAG | AA_FLAG, 1, 0, 0, 0);
 
 
     // The question must be identical to that of the received notify
     // The question must be identical to that of the received notify
-    ConstQuestionPtr question = *parse_message.beginQuestion();
+    ConstQuestionPtr question = *parse_message->beginQuestion();
     EXPECT_EQ(Name("example.com"), question->getName());
     EXPECT_EQ(Name("example.com"), question->getName());
     EXPECT_EQ(RRClass::IN(), question->getClass());
     EXPECT_EQ(RRClass::IN(), question->getClass());
     EXPECT_EQ(RRType::SOA(), question->getType());
     EXPECT_EQ(RRType::SOA(), question->getType());
@@ -613,10 +593,8 @@ TEST_F(AuthSrvTest, notifyForCHClass) {
                         RRType::SOA());
                         RRType::SOA());
     request_message.setHeaderFlag(MessageFlag::AA());
     request_message.setHeaderFlag(MessageFlag::AA());
     createRequestPacket(IPPROTO_UDP);
     createRequestPacket(IPPROTO_UDP);
-    bool done;
-    server.processMessage(*io_message, parse_message, response_renderer,
-                          &noOp, done);
-    EXPECT_TRUE(done);
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_TRUE(task.hasAnswer());
 
 
     // Other conditions should be the same, so simply confirm the RR class is
     // Other conditions should be the same, so simply confirm the RR class is
     // set correctly.
     // set correctly.
@@ -632,11 +610,9 @@ TEST_F(AuthSrvTest, notifyEmptyQuestion) {
     request_message.setQid(default_qid);
     request_message.setQid(default_qid);
     request_message.toWire(request_renderer);
     request_message.toWire(request_renderer);
     createRequestPacket(IPPROTO_UDP);
     createRequestPacket(IPPROTO_UDP);
-    bool done;
-    server.processMessage(*io_message, parse_message, response_renderer,
-                          &noOp, done);
-    EXPECT_TRUE(done);
-    headerCheck(parse_message, default_qid, Rcode::FORMERR(),
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_TRUE(task.hasAnswer());
+    headerCheck(*parse_message, default_qid, Rcode::FORMERR(),
                 Opcode::NOTIFY().getCode(), QR_FLAG, 0, 0, 0, 0);
                 Opcode::NOTIFY().getCode(), QR_FLAG, 0, 0, 0, 0);
 }
 }
 
 
@@ -648,11 +624,9 @@ TEST_F(AuthSrvTest, notifyMultiQuestions) {
                                          RRType::SOA()));
                                          RRType::SOA()));
     request_message.setHeaderFlag(MessageFlag::AA());
     request_message.setHeaderFlag(MessageFlag::AA());
     createRequestPacket(IPPROTO_UDP);
     createRequestPacket(IPPROTO_UDP);
-    bool done;
-    server.processMessage(*io_message, parse_message, response_renderer,
-                          &noOp, done);
-    EXPECT_TRUE(done);
-    headerCheck(parse_message, default_qid, Rcode::FORMERR(),
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_TRUE(task.hasAnswer());
+    headerCheck(*parse_message, default_qid, Rcode::FORMERR(),
                 Opcode::NOTIFY().getCode(), QR_FLAG, 2, 0, 0, 0);
                 Opcode::NOTIFY().getCode(), QR_FLAG, 2, 0, 0, 0);
 }
 }
 
 
@@ -661,11 +635,9 @@ TEST_F(AuthSrvTest, notifyNonSOAQuestion) {
                         RRType::NS());
                         RRType::NS());
     request_message.setHeaderFlag(MessageFlag::AA());
     request_message.setHeaderFlag(MessageFlag::AA());
     createRequestPacket(IPPROTO_UDP);
     createRequestPacket(IPPROTO_UDP);
-    bool done;
-    server.processMessage(*io_message, parse_message, response_renderer,
-                          &noOp, done);
-    EXPECT_TRUE(done);
-    headerCheck(parse_message, default_qid, Rcode::FORMERR(),
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_TRUE(task.hasAnswer());
+    headerCheck(*parse_message, default_qid, Rcode::FORMERR(),
                 Opcode::NOTIFY().getCode(), QR_FLAG, 1, 0, 0, 0);
                 Opcode::NOTIFY().getCode(), QR_FLAG, 1, 0, 0, 0);
 }
 }
 
 
@@ -673,11 +645,9 @@ TEST_F(AuthSrvTest, notifyWithoutAA) {
     // implicitly leave the AA bit off.  our implementation will accept it.
     // implicitly leave the AA bit off.  our implementation will accept it.
     createRequestPacket(Opcode::NOTIFY(), Name("example.com"), RRClass::IN(),
     createRequestPacket(Opcode::NOTIFY(), Name("example.com"), RRClass::IN(),
                         RRType::SOA());
                         RRType::SOA());
-    bool done;
-    server.processMessage(*io_message, parse_message, response_renderer,
-                          &noOp, done);
-    EXPECT_TRUE(done);
-    headerCheck(parse_message, default_qid, Rcode::NOERROR(),
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_TRUE(task.hasAnswer());
+    headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
                 Opcode::NOTIFY().getCode(), QR_FLAG | AA_FLAG, 1, 0, 0, 0);
                 Opcode::NOTIFY().getCode(), QR_FLAG | AA_FLAG, 1, 0, 0, 0);
 }
 }
 
 
@@ -687,11 +657,9 @@ TEST_F(AuthSrvTest, notifyWithErrorRcode) {
     request_message.setHeaderFlag(MessageFlag::AA());
     request_message.setHeaderFlag(MessageFlag::AA());
     request_message.setRcode(Rcode::SERVFAIL());
     request_message.setRcode(Rcode::SERVFAIL());
     createRequestPacket(IPPROTO_UDP);
     createRequestPacket(IPPROTO_UDP);
-    bool done;
-    server.processMessage(*io_message, parse_message, response_renderer,
-                          &noOp, done);
-    EXPECT_TRUE(done);
-    headerCheck(parse_message, default_qid, Rcode::NOERROR(),
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_TRUE(task.hasAnswer());
+    headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
                 Opcode::NOTIFY().getCode(), QR_FLAG | AA_FLAG, 1, 0, 0, 0);
                 Opcode::NOTIFY().getCode(), QR_FLAG | AA_FLAG, 1, 0, 0, 0);
 }
 }
 
 
@@ -705,10 +673,8 @@ TEST_F(AuthSrvTest, notifyWithoutSession) {
 
 
     // we simply ignore the notify and let it be resent if an internal error
     // we simply ignore the notify and let it be resent if an internal error
     // happens.
     // happens.
-    bool done;
-    server.processMessage(*io_message, parse_message, response_renderer,
-                          &noOp, done);
-    EXPECT_FALSE(done);
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_FALSE(task.hasAnswer());
 }
 }
 
 
 TEST_F(AuthSrvTest, notifySendFail) {
 TEST_F(AuthSrvTest, notifySendFail) {
@@ -719,10 +685,8 @@ TEST_F(AuthSrvTest, notifySendFail) {
     request_message.setHeaderFlag(MessageFlag::AA());
     request_message.setHeaderFlag(MessageFlag::AA());
     createRequestPacket(IPPROTO_UDP);
     createRequestPacket(IPPROTO_UDP);
 
 
-    bool done;
-    server.processMessage(*io_message, parse_message, response_renderer,
-                          &noOp, done);
-    EXPECT_FALSE(done);
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_FALSE(task.hasAnswer());
 }
 }
 
 
 TEST_F(AuthSrvTest, notifyReceiveFail) {
 TEST_F(AuthSrvTest, notifyReceiveFail) {
@@ -732,10 +696,8 @@ TEST_F(AuthSrvTest, notifyReceiveFail) {
                         RRType::SOA());
                         RRType::SOA());
     request_message.setHeaderFlag(MessageFlag::AA());
     request_message.setHeaderFlag(MessageFlag::AA());
     createRequestPacket(IPPROTO_UDP);
     createRequestPacket(IPPROTO_UDP);
-    bool done;
-    server.processMessage(*io_message, parse_message, response_renderer,
-                          &noOp, done);
-    EXPECT_FALSE(done);
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_FALSE(task.hasAnswer());
 }
 }
 
 
 TEST_F(AuthSrvTest, notifyWithBogusSessionMessage) {
 TEST_F(AuthSrvTest, notifyWithBogusSessionMessage) {
@@ -745,10 +707,8 @@ TEST_F(AuthSrvTest, notifyWithBogusSessionMessage) {
                         RRType::SOA());
                         RRType::SOA());
     request_message.setHeaderFlag(MessageFlag::AA());
     request_message.setHeaderFlag(MessageFlag::AA());
     createRequestPacket(IPPROTO_UDP);
     createRequestPacket(IPPROTO_UDP);
-    bool done;
-    server.processMessage(*io_message, parse_message, response_renderer,
-                          &noOp, done);
-    EXPECT_FALSE(done);
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_FALSE(task.hasAnswer());
 }
 }
 
 
 TEST_F(AuthSrvTest, notifyWithSessionMessageError) {
 TEST_F(AuthSrvTest, notifyWithSessionMessageError) {
@@ -759,10 +719,8 @@ TEST_F(AuthSrvTest, notifyWithSessionMessageError) {
                         RRType::SOA());
                         RRType::SOA());
     request_message.setHeaderFlag(MessageFlag::AA());
     request_message.setHeaderFlag(MessageFlag::AA());
     createRequestPacket(IPPROTO_UDP);
     createRequestPacket(IPPROTO_UDP);
-    bool done;
-    server.processMessage(*io_message, parse_message, response_renderer,
-                          &noOp, done);
-    EXPECT_FALSE(done);
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_FALSE(task.hasAnswer());
 }
 }
 
 
 void
 void
@@ -787,11 +745,9 @@ TEST_F(AuthSrvTest, updateConfig) {
     // response should have the AA flag on, and have an RR in each answer
     // response should have the AA flag on, and have an RR in each answer
     // and authority section.
     // and authority section.
     createDataFromFile("examplequery_fromWire");
     createDataFromFile("examplequery_fromWire");
-    bool done;
-    server.processMessage(*io_message, parse_message, response_renderer,
-                          &noOp, done);
-    EXPECT_TRUE(done);
-    headerCheck(parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_TRUE(task.hasAnswer());
+    headerCheck(*parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
                 QR_FLAG | AA_FLAG, 1, 1, 1, 0);
                 QR_FLAG | AA_FLAG, 1, 1, 1, 0);
 }
 }
 
 
@@ -803,11 +759,9 @@ TEST_F(AuthSrvTest, datasourceFail) {
     // in a SERVFAIL response, and the answer and authority sections should
     // in a SERVFAIL response, and the answer and authority sections should
     // be empty.
     // be empty.
     createDataFromFile("badExampleQuery_fromWire");
     createDataFromFile("badExampleQuery_fromWire");
-    bool done;
-    server.processMessage(*io_message, parse_message, response_renderer,
-                          &noOp, done);
-    EXPECT_TRUE(done);
-    headerCheck(parse_message, default_qid, Rcode::SERVFAIL(),
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_TRUE(task.hasAnswer());
+    headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
                 opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
                 opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
 }
 }
 
 
@@ -820,11 +774,9 @@ TEST_F(AuthSrvTest, updateConfigFail) {
 
 
     // The original data source should still exist.
     // The original data source should still exist.
     createDataFromFile("examplequery_fromWire");
     createDataFromFile("examplequery_fromWire");
-    bool done;
-    server.processMessage(*io_message, parse_message, response_renderer,
-                          &noOp, done);
-    EXPECT_TRUE(done);
-    headerCheck(parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_TRUE(task.hasAnswer());
+    headerCheck(*parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
                 QR_FLAG | AA_FLAG, 1, 1, 1, 0);
                 QR_FLAG | AA_FLAG, 1, 1, 1, 0);
 }
 }
 }
 }

+ 120 - 89
src/bin/bind10/bind10.py.in

@@ -202,8 +202,9 @@ class IPAddr:
 class BoB:
 class BoB:
     """Boss of BIND class."""
     """Boss of BIND class."""
     
     
-    def __init__(self, msgq_socket_file=None, auth_port=5300, address='',
-                 nocache=False, verbose=False, setuid=None, username=None):
+    def __init__(self, msgq_socket_file=None, dns_port=5300, address='',
+                 forward=None, nocache=False, verbose=False, setuid=None,
+                 username=None):
         """Initialize the Boss of BIND. This is a singleton (only one
         """Initialize the Boss of BIND. This is a singleton (only one
         can run).
         can run).
         
         
@@ -213,10 +214,17 @@ class BoB:
         """
         """
         self.verbose = verbose
         self.verbose = verbose
         self.msgq_socket_file = msgq_socket_file
         self.msgq_socket_file = msgq_socket_file
-        self.auth_port = auth_port
+        self.dns_port = dns_port
         self.address = None
         self.address = None
+        self.nocache = nocache
         if address:
         if address:
             self.address = IPAddr(address)
             self.address = IPAddr(address)
+        self.forward = None
+        self.recursive = False
+        if forward:
+            self.forward = IPAddr(forward)
+            self.recursive = True
+            self.nocache = False
         self.cc_session = None
         self.cc_session = None
         self.ccs = None
         self.ccs = None
         self.processes = {}
         self.processes = {}
@@ -224,7 +232,6 @@ class BoB:
         self.runnable = False
         self.runnable = False
         self.uid = setuid
         self.uid = setuid
         self.username = username
         self.username = username
-        self.nocache = nocache
 
 
     def config_handler(self, new_config):
     def config_handler(self, new_config):
         if self.verbose:
         if self.verbose:
@@ -329,94 +336,111 @@ class BoB:
         if self.verbose:
         if self.verbose:
             sys.stdout.write("[bind10] ccsession started\n")
             sys.stdout.write("[bind10] ccsession started\n")
 
 
-        # start b10-auth
+        # if we're running a recursive-only server, we skip the xfrout
+        # modules. otherwise, start xfrout before the DNS server, to make
+        # sure every xfr-query can be processed properly.
+        xfrout=None
+        if not self.recursive:
+            xfrout_args = ['b10-xfrout']
+            if self.verbose:
+                sys.stdout.write("[bind10] Starting b10-xfrout\n")
+                xfrout_args += ['-v']
+            try:
+                xfrout = ProcessInfo("b10-xfrout", xfrout_args, 
+                                     c_channel_env )
+            except Exception as e:
+                c_channel.process.kill()
+                bind_cfgd.process.kill()
+                return "Unable to start b10-xfrout; " + str(e)
+            self.processes[xfrout.pid] = xfrout
+            if self.verbose:
+                sys.stdout.write("[bind10] Started b10-xfrout (PID %d)\n" % 
+                                 xfrout.pid)
+
+        # start DNS server
         # XXX: this must be read from the configuration manager in the future
         # XXX: this must be read from the configuration manager in the future
-        authargs = ['b10-auth', '-p', str(self.auth_port)]
+        if self.recursive:
+            dns_prog = 'b10-recurse'
+        else:
+            dns_prog = 'b10-auth'
+        dnsargs = [dns_prog, '-p', str(self.dns_port)]
+        if self.forward:
+            dnsargs += ['-f', str(self.forward)]
         if self.address:
         if self.address:
-            authargs += ['-a', str(self.address)]
+            dnsargs += ['-a', str(self.address)]
         if self.nocache:
         if self.nocache:
-            authargs += ['-n']
+            dnsargs += ['-n']
         if self.uid:
         if self.uid:
-            authargs += ['-u', str(self.uid)]
+            dnsargs += ['-u', str(self.uid)]
         if self.verbose:
         if self.verbose:
-            authargs += ['-v']
-            sys.stdout.write("Starting b10-auth using port %d" %
-                             self.auth_port)
+            dnsargs += ['-v']
+            sys.stdout.write("Starting %s using port %d" %
+                             (dns_prog, self.dns_port))
             if self.address:
             if self.address:
                 sys.stdout.write(" on %s" % str(self.address))
                 sys.stdout.write(" on %s" % str(self.address))
             sys.stdout.write("\n")
             sys.stdout.write("\n")
         try:
         try:
-            auth = ProcessInfo("b10-auth", authargs,
-                               c_channel_env)
+            dns = ProcessInfo(dns_prog, dnsargs, c_channel_env)
         except Exception as e:
         except Exception as e:
             c_channel.process.kill()
             c_channel.process.kill()
             bind_cfgd.process.kill()
             bind_cfgd.process.kill()
-            xfrout.process.kill()
-            return "Unable to start b10-auth; " + str(e)
-        self.processes[auth.pid] = auth
+            if xfrout:
+                xfrout.process.kill()
+            return "Unable to start " + dns_prog + ": " + str(e)
+        self.processes[dns.pid] = dns
         if self.verbose:
         if self.verbose:
-            sys.stdout.write("[bind10] Started b10-auth (PID %d)\n" % auth.pid)
+            sys.stdout.write("[bind10] Started %s (PID %d)\n" %
+                             (dns_prog, dns.pid))
 
 
-        # everything after the authoritative server can run as non-root
+        # everything after the DNS server can run as non-root
         if self.uid is not None:
         if self.uid is not None:
             posix.setuid(self.uid)
             posix.setuid(self.uid)
 
 
-        # start the xfrout before auth-server, to make sure every xfr-query can
-        # be processed properly.
-        xfrout_args = ['b10-xfrout']
-        if self.verbose:
-            sys.stdout.write("[bind10] Starting b10-xfrout\n")
-            xfrout_args += ['-v']
-        try:
-            xfrout = ProcessInfo("b10-xfrout", xfrout_args, 
-                                 c_channel_env )
-        except Exception as e:
-            c_channel.process.kill()
-            bind_cfgd.process.kill()
-            return "Unable to start b10-xfrout; " + str(e)
-        self.processes[xfrout.pid] = xfrout
-        if self.verbose:
-            sys.stdout.write("[bind10] Started b10-xfrout (PID %d)\n" % 
-                             xfrout.pid)
-
-        # start b10-xfrin
-        xfrin_args = ['b10-xfrin']
-        if self.verbose:
-            sys.stdout.write("[bind10] Starting b10-xfrin\n")
-            xfrin_args += ['-v']
-        try:
-            xfrind = ProcessInfo("b10-xfrin", xfrin_args,
-                                 c_channel_env)
-        except Exception as e:
-            c_channel.process.kill()
-            bind_cfgd.process.kill()
-            xfrout.process.kill()
-            auth.process.kill()
-            return "Unable to start b10-xfrin; " + str(e)
-        self.processes[xfrind.pid] = xfrind
-        if self.verbose:
-            sys.stdout.write("[bind10] Started b10-xfrin (PID %d)\n" % 
-                             xfrind.pid)
+        xfrind=None
+        if not self.recursive:
+            # If we're running an authoritative server, start b10-xfrin
+            xfrin_args = ['b10-xfrin']
+            if self.verbose:
+                sys.stdout.write("[bind10] Starting b10-xfrin\n")
+                xfrin_args += ['-v']
+            try:
+                xfrind = ProcessInfo("b10-xfrin", xfrin_args,
+                                     c_channel_env)
+            except Exception as e:
+                c_channel.process.kill()
+                bind_cfgd.process.kill()
+                if xfrout:
+                    xfrout.process.kill()
+                dns.process.kill()
+                return "Unable to start b10-xfrin; " + str(e)
+            self.processes[xfrind.pid] = xfrind
+            if self.verbose:
+                sys.stdout.write("[bind10] Started b10-xfrin (PID %d)\n" % 
+                                 xfrind.pid)
 
 
-        # start b10-zonemgr
-        zonemgr_args = ['b10-zonemgr']
-        if self.verbose:
-            sys.stdout.write("[bind10] Starting b10-zonemgr\n")
-            zonemgr_args += ['-v']
-        try:
-            zonemgr = ProcessInfo("b10-zonemgr", zonemgr_args,
-                                 c_channel_env)
-        except Exception as e:
-            c_channel.process.kill()
-            bind_cfgd.process.kill()
-            xfrout.process.kill()
-            auth.process.kill()
-            xfrind.process.kill()
-            return "Unable to start b10-zonemgr; " + str(e)
-        self.processes[zonemgr.pid] = zonemgr 
-        if self.verbose:
-            sys.stdout.write("[bind10] Started b10-zonemgr(PID %d)\n" % 
-                             zonemgr.pid)
+        zonemgr=None
+        if not self.recursive:
+            # If we're running an authoritative server, start b10-zonemgr
+            zonemgr_args = ['b10-zonemgr']
+            if self.verbose:
+                sys.stdout.write("[bind10] Starting b10-zonemgr\n")
+                zonemgr_args += ['-v']
+            try:
+                zonemgr = ProcessInfo("b10-zonemgr", zonemgr_args,
+                                     c_channel_env)
+            except Exception as e:
+                c_channel.process.kill()
+                bind_cfgd.process.kill()
+                dns.process.kill()
+                if xfrout:
+                    xfrout.process.kill()
+                if xfrind:
+                    xfrind.process.kill()
+                return "Unable to start b10-zonemgr; " + str(e)
+            self.processes[zonemgr.pid] = zonemgr 
+            if self.verbose:
+                sys.stdout.write("[bind10] Started b10-zonemgr(PID %d)\n" % 
+                                 zonemgr.pid)
 
 
         # start the b10-cmdctl
         # start the b10-cmdctl
         # XXX: we hardcode port 8080
         # XXX: we hardcode port 8080
@@ -430,10 +454,13 @@ class BoB:
         except Exception as e:
         except Exception as e:
             c_channel.process.kill()
             c_channel.process.kill()
             bind_cfgd.process.kill()
             bind_cfgd.process.kill()
-            xfrout.process.kill()
-            auth.process.kill()
-            xfrind.process.kill()
-            zonemgr.process.kill()
+            dns.process.kill()
+            if xfrout:
+                xfrout.process.kill()
+            if xfrind:
+                xfrind.process.kill()
+            if zonemgr:
+                zonemgr.process.kill()
             return "Unable to start b10-cmdctl; " + str(e)
             return "Unable to start b10-cmdctl; " + str(e)
         self.processes[cmd_ctrld.pid] = cmd_ctrld
         self.processes[cmd_ctrld.pid] = cmd_ctrld
         if self.verbose:
         if self.verbose:
@@ -450,6 +477,7 @@ class BoB:
         self.cc_session.group_sendmsg(cmd, 'Boss', 'Cmdctl')
         self.cc_session.group_sendmsg(cmd, 'Boss', 'Cmdctl')
         self.cc_session.group_sendmsg(cmd, "Boss", "ConfigManager")
         self.cc_session.group_sendmsg(cmd, "Boss", "ConfigManager")
         self.cc_session.group_sendmsg(cmd, "Boss", "Auth")
         self.cc_session.group_sendmsg(cmd, "Boss", "Auth")
+        self.cc_session.group_sendmsg(cmd, "Boss", "Recurse")
         self.cc_session.group_sendmsg(cmd, "Boss", "Xfrout")
         self.cc_session.group_sendmsg(cmd, "Boss", "Xfrout")
         self.cc_session.group_sendmsg(cmd, "Boss", "Xfrin")
         self.cc_session.group_sendmsg(cmd, "Boss", "Xfrin")
         self.cc_session.group_sendmsg(cmd, "Boss", "Zonemgr")
         self.cc_session.group_sendmsg(cmd, "Boss", "Zonemgr")
@@ -603,7 +631,7 @@ def check_port(option, opt_str, value, parser):
     if (opt_str == '-m' or opt_str == '--msgq-port'):
     if (opt_str == '-m' or opt_str == '--msgq-port'):
         parser.values.msgq_port = value
         parser.values.msgq_port = value
     elif (opt_str == '-p' or opt_str == '--port'):
     elif (opt_str == '-p' or opt_str == '--port'):
-        parser.values.auth_port = value
+        parser.values.dns_port = value
     else:
     else:
         raise OptionValueError("Unknown option " + opt_str)
         raise OptionValueError("Unknown option " + opt_str)
   
   
@@ -616,6 +644,8 @@ def check_addr(option, opt_str, value, parser):
         raise OptionValueError("%s requires a valid IPv4 or IPv6 address" % opt_str)
         raise OptionValueError("%s requires a valid IPv4 or IPv6 address" % opt_str)
     if (opt_str == '-a' or opt_str == '--address'):
     if (opt_str == '-a' or opt_str == '--address'):
         parser.values.address = value
         parser.values.address = value
+    elif (opt_str == '-f' or opt_str == '--forward'):
+        parser.values.forward = value
     else:
     else:
         raise OptionValueError("Unknown option " + opt_str)
         raise OptionValueError("Unknown option " + opt_str)
   
   
@@ -625,22 +655,23 @@ def main():
     # Enforce line buffering on stdout, even when not a TTY
     # Enforce line buffering on stdout, even when not a TTY
     sys.stdout = io.TextIOWrapper(sys.stdout.detach(), line_buffering=True)
     sys.stdout = io.TextIOWrapper(sys.stdout.detach(), line_buffering=True)
 
 
-
     # Parse any command-line options.
     # Parse any command-line options.
     parser = OptionParser(version=__version__)
     parser = OptionParser(version=__version__)
     parser.add_option("-a", "--address", dest="address", type="string",
     parser.add_option("-a", "--address", dest="address", type="string",
                       action="callback", callback=check_addr, default='',
                       action="callback", callback=check_addr, default='',
-                      help="address the b10-auth daemon will use (default: listen on all addresses)")
+                      help="address the DNS server will use (default: listen on all addresses)")
+    parser.add_option("-f", "--forward", dest="forward", type="string",
+                      action="callback", callback=check_addr, default='',
+                      help="nameserver to which DNS queries should be forwarded")
     parser.add_option("-m", "--msgq-socket-file", dest="msgq_socket_file",
     parser.add_option("-m", "--msgq-socket-file", dest="msgq_socket_file",
                       type="string", default=None,
                       type="string", default=None,
                       help="UNIX domain socket file the b10-msgq daemon will use")
                       help="UNIX domain socket file the b10-msgq daemon will use")
     parser.add_option("-n", "--no-cache", action="store_true", dest="nocache",
     parser.add_option("-n", "--no-cache", action="store_true", dest="nocache",
-                      default=False, help="disable hot-spot cache in b10-auth")
-    parser.add_option("-p", "--port", dest="auth_port", type="string",
+                      default=False, help="disable hot-spot cache in authoritative DNS server")
+    parser.add_option("-p", "--port", dest="dns_port", type="string",
                       action="callback", callback=check_port, default="5300",
                       action="callback", callback=check_port, default="5300",
-                      help="port the b10-auth daemon will use (default 5300)")
-    parser.add_option("-u", "--user", dest="user",
-                      type="string", default=None,
+                      help="port the DNS server will use (default 5300)")
+    parser.add_option("-u", "--user", dest="user", type="string", default=None,
                       help="Change user after startup (must run as root)")
                       help="Change user after startup (must run as root)")
     parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
     parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
                       help="display more about what is going on")
                       help="display more about what is going on")
@@ -698,9 +729,9 @@ def main():
     signal.signal(signal.SIGTERM, fatal_signal)
     signal.signal(signal.SIGTERM, fatal_signal)
 
 
     # Go bob!
     # Go bob!
-    boss_of_bind = BoB(options.msgq_socket_file, int(options.auth_port),
-                       options.address, options.nocache, options.verbose,
-                       setuid, username)
+    boss_of_bind = BoB(options.msgq_socket_file, int(options.dns_port),
+                       options.address, options.forward, options.nocache,
+                       options.verbose, setuid, username)
     startup_result = boss_of_bind.startup()
     startup_result = boss_of_bind.startup()
     if startup_result:
     if startup_result:
         sys.stderr.write("[bind10] Error on startup: %s\n" % startup_result)
         sys.stderr.write("[bind10] Error on startup: %s\n" % startup_result)

+ 1 - 1
src/bin/bind10/run_bind10.sh.in

@@ -20,7 +20,7 @@ export PYTHON_EXEC
 
 
 BIND10_PATH=@abs_top_builddir@/src/bin/bind10
 BIND10_PATH=@abs_top_builddir@/src/bin/bind10
 
 
-PATH=@abs_top_builddir@/src/bin/msgq:@abs_top_builddir@/src/bin/auth:@abs_top_builddir@/src/bin/cfgmgr:@abs_top_builddir@/src/bin/cmdctl:@abs_top_builddir@/src/bin/xfrin:@abs_top_builddir@/src/bin/xfrout:@abs_top_builddir@/src/bin/zonemgr:$PATH
+PATH=@abs_top_builddir@/src/bin/msgq:@abs_top_builddir@/src/bin/auth:@abs_top_builddir@/src/bin/recurse:@abs_top_builddir@/src/bin/cfgmgr:@abs_top_builddir@/src/bin/cmdctl:@abs_top_builddir@/src/bin/xfrin:@abs_top_builddir@/src/bin/xfrout:@abs_top_builddir@/src/bin/zonemgr:$PATH
 export PATH
 export PATH
 
 
 PYTHONPATH=@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/xfr/.libs
 PYTHONPATH=@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/xfr/.libs

+ 17 - 6
src/bin/recurse/main.cc

@@ -86,7 +86,8 @@ my_command_handler(const string& command, ConstElementPtr args) {
 
 
 void
 void
 usage() {
 usage() {
-    cerr << "Usage: b10-recurse [-a address] [-p port] [-4|-6] [-nv]" << endl;
+    cerr << "Usage: b10-recurse -f nameserver [-a address] [-p port] "
+            "[-4|-6] [-nv]" << endl;
     exit(1);
     exit(1);
 }
 }
 } // end of anonymous namespace
 } // end of anonymous namespace
@@ -96,10 +97,11 @@ main(int argc, char* argv[]) {
     int ch;
     int ch;
     const char* port = DNSPORT;
     const char* port = DNSPORT;
     const char* address = NULL;
     const char* address = NULL;
+    const char* forward = NULL;
     const char* uid = NULL;
     const char* uid = NULL;
     bool use_ipv4 = true, use_ipv6 = true, cache = true;
     bool use_ipv4 = true, use_ipv6 = true, cache = true;
 
 
-    while ((ch = getopt(argc, argv, "46a:np:u:v")) != -1) {
+    while ((ch = getopt(argc, argv, "46a:f:np:u:v")) != -1) {
         switch (ch) {
         switch (ch) {
         case '4':
         case '4':
             // Note that -4 means "ipv4 only", we need to set "use_ipv6" here,
             // Note that -4 means "ipv4 only", we need to set "use_ipv6" here,
@@ -118,6 +120,9 @@ main(int argc, char* argv[]) {
         case 'a':
         case 'a':
             address = optarg;
             address = optarg;
             break;
             break;
+        case 'f':
+            forward = optarg;
+            break;
         case 'p':
         case 'p':
             port = optarg;
             port = optarg;
             break;
             break;
@@ -147,6 +152,11 @@ main(int argc, char* argv[]) {
         usage();
         usage();
     }
     }
 
 
+    if (forward == NULL) {
+        cerr << "[b10-recurse] No forward name server specified" << endl;
+        usage();
+    }
+
     int ret = 0;
     int ret = 0;
 
 
     // XXX: we should eventually pass io_service here.
     // XXX: we should eventually pass io_service here.
@@ -161,6 +171,10 @@ main(int argc, char* argv[]) {
             specfile = string(RECURSE_SPECFILE_LOCATION);
             specfile = string(RECURSE_SPECFILE_LOCATION);
         }
         }
 
 
+        recursor = new Recursor(*forward);
+        recursor ->setVerbose(verbose_mode);
+        cout << "[b10-recurse] Server created." << endl;
+
         IOCallback* checkin = recursor->getCheckinProvider();
         IOCallback* checkin = recursor->getCheckinProvider();
         DNSLookup* lookup = recursor->getDNSLookupProvider();
         DNSLookup* lookup = recursor->getDNSLookupProvider();
         DNSAnswer* answer = recursor->getDNSAnswerProvider();
         DNSAnswer* answer = recursor->getDNSAnswerProvider();
@@ -178,12 +192,9 @@ main(int argc, char* argv[]) {
             io_service = new IOService(*port, use_ipv4, use_ipv6,
             io_service = new IOService(*port, use_ipv4, use_ipv6,
                                        checkin, lookup, answer);
                                        checkin, lookup, answer);
         }
         }
+        recursor->setIOService(*io_service);
         cout << "[b10-recurse] IOService created." << endl;
         cout << "[b10-recurse] IOService created." << endl;
 
 
-        recursor = new Recursor(*io_service);
-        recursor ->setVerbose(verbose_mode);
-        cout << "[b10-recurse] Server created." << endl;
-
         cc_session = new Session(io_service->get_io_service());
         cc_session = new Session(io_service->get_io_service());
         cout << "[b10-recurse] Configuration session channel created." << endl;
         cout << "[b10-recurse] Configuration session channel created." << endl;
 
 

+ 211 - 123
src/bin/recurse/recursor.cc

@@ -25,6 +25,8 @@
 
 
 #include <asiolink/asiolink.h>
 #include <asiolink/asiolink.h>
 
 
+#include <boost/foreach.hpp>
+
 #include <config/ccsession.h>
 #include <config/ccsession.h>
 
 
 #include <cc/data.h>
 #include <cc/data.h>
@@ -33,7 +35,6 @@
 
 
 #include <dns/buffer.h>
 #include <dns/buffer.h>
 #include <dns/exceptions.h>
 #include <dns/exceptions.h>
-#include <dns/messagerenderer.h>
 #include <dns/name.h>
 #include <dns/name.h>
 #include <dns/question.h>
 #include <dns/question.h>
 #include <dns/rrset.h>
 #include <dns/rrset.h>
@@ -61,25 +62,105 @@ private:
     RecursorImpl(const RecursorImpl& source);
     RecursorImpl(const RecursorImpl& source);
     RecursorImpl& operator=(const RecursorImpl& source);
     RecursorImpl& operator=(const RecursorImpl& source);
 public:
 public:
-    RecursorImpl(asiolink::IOService& io_service);
-    bool processNormalQuery(const IOMessage& io_message,
-                            const Question& question, Message& message,
-                            MessageRenderer& renderer,
-                            BasicServer* server);
+    RecursorImpl(const char& forward) :
+        config_session_(NULL), verbose_mode_(false),
+        forward_(forward), ioquery_()
+    {}
+
+    ~RecursorImpl() {
+        queryShutdown();
+    }
+
+    void querySetup(IOService& ios) {
+        ioquery_ = new IOQuery(ios, forward_);
+    }
+
+    void queryShutdown() {
+        if (ioquery_) {
+            delete ioquery_;
+        }
+    }
+
+    void processNormalQuery(const IOMessage& io_message,
+                            const Question& question, MessagePtr message,
+                            OutputBufferPtr buffer,
+                            IOServer* server);
     ModuleCCSession* config_session_;
     ModuleCCSession* config_session_;
 
 
     bool verbose_mode_;
     bool verbose_mode_;
 
 
+    /// Address of the forward nameserver
+    const char& forward_;
+
     /// Object to handle upstream queries
     /// Object to handle upstream queries
-    IOQuery ioquery_;
+    IOQuery* ioquery_;
 
 
     /// Currently non-configurable, but will be.
     /// Currently non-configurable, but will be.
     static const uint16_t DEFAULT_LOCAL_UDPSIZE = 4096;
     static const uint16_t DEFAULT_LOCAL_UDPSIZE = 4096;
 };
 };
 
 
-RecursorImpl::RecursorImpl(asiolink::IOService& io_service) :
-    config_session_(NULL), verbose_mode_(false), ioquery_(io_service)
-{}
+class QuestionInserter {
+public:
+    QuestionInserter(MessagePtr message) : message_(message) {}
+    void operator()(const QuestionPtr question) {
+        message_->addQuestion(question);
+    }
+    MessagePtr message_;
+};
+
+class SectionInserter {
+public:
+    SectionInserter(MessagePtr message, const Section& sect, bool sign) :
+        message_(message), section_(sect), sign_(sign)
+    {}
+    void operator()(const RRsetPtr rrset) {
+        message_->addRRset(section_, rrset, true);
+    }
+    MessagePtr message_;
+    const Section& section_;
+    bool sign_;
+};
+
+void
+makeErrorMessage(MessagePtr message, OutputBufferPtr buffer,
+                 const Rcode& rcode, const bool verbose_mode)
+{
+    // extract the parameters that should be kept.
+    // XXX: with the current implementation, it's not easy to set EDNS0
+    // depending on whether the query had it.  So we'll simply omit it.
+    const qid_t qid = message->getQid();
+    const bool rd = message->getHeaderFlag(MessageFlag::RD());
+    const bool cd = message->getHeaderFlag(MessageFlag::CD());
+    const Opcode& opcode = message->getOpcode();
+    vector<QuestionPtr> questions;
+
+    // If this is an error to a query or notify, we should also copy the
+    // question section.
+    if (opcode == Opcode::QUERY() || opcode == Opcode::NOTIFY()) {
+        questions.assign(message->beginQuestion(), message->endQuestion());
+    }
+
+    message->clear(Message::RENDER);
+    message->setQid(qid);
+    message->setOpcode(opcode);
+    message->setHeaderFlag(MessageFlag::QR());
+    message->setUDPSize(RecursorImpl::DEFAULT_LOCAL_UDPSIZE);
+    if (rd) {
+        message->setHeaderFlag(MessageFlag::RD());
+    }
+    if (cd) {
+        message->setHeaderFlag(MessageFlag::CD());
+    }
+    for_each(questions.begin(), questions.end(), QuestionInserter(message));
+    message->setRcode(rcode);
+    MessageRenderer renderer(*buffer);
+    message->toWire(renderer);
+
+    if (verbose_mode) {
+        cerr << "[b10-recurse] sending an error response (" <<
+            renderer.getLength() << " bytes):\n" << message->toText() << endl;
+    }
+}
 
 
 // This is a derived class of \c DNSLookup, to serve as a
 // This is a derived class of \c DNSLookup, to serve as a
 // callback in the asiolink module.  It calls
 // callback in the asiolink module.  It calls
@@ -87,13 +168,12 @@ RecursorImpl::RecursorImpl(asiolink::IOService& io_service) :
 class MessageLookup : public DNSLookup {
 class MessageLookup : public DNSLookup {
 public:
 public:
     MessageLookup(Recursor* srv) : server_(srv) {}
     MessageLookup(Recursor* srv) : server_(srv) {}
-    virtual void operator()(const IOMessage& io_message,
-                            isc::dns::Message& dns_message,
-                            isc::dns::MessageRenderer& renderer,
-                            BasicServer* server, bool& complete) const
+
+    // \brief Handle the DNS Lookup
+    virtual void operator()(const IOMessage& io_message, MessagePtr message,
+                            OutputBufferPtr buffer, IOServer* server) const
     {
     {
-        server_->processMessage(io_message, dns_message, renderer,
-                                server, complete);
+        server_->processMessage(io_message, message, buffer, server);
     }
     }
 private:
 private:
     Recursor* server_;
     Recursor* server_;
@@ -107,18 +187,76 @@ class MessageAnswer : public DNSAnswer {
 public:
 public:
     MessageAnswer(Recursor* srv) : server_(srv) {}
     MessageAnswer(Recursor* srv) : server_(srv) {}
     virtual void operator()(const IOMessage& io_message,
     virtual void operator()(const IOMessage& io_message,
-                            isc::dns::Message& message,
-                            isc::dns::MessageRenderer& renderer) const
+                            MessagePtr message,
+                            OutputBufferPtr buffer) const
     {
     {
+        const qid_t qid = message->getQid();
+        const bool rd = message->getHeaderFlag(MessageFlag::RD());
+        const bool cd = message->getHeaderFlag(MessageFlag::CD());
+        const Opcode& opcode = message->getOpcode();
+        const Rcode& rcode = message->getRcode();
+        vector<QuestionPtr> questions;
+        questions.assign(message->beginQuestion(), message->endQuestion());
+
+        message->clear(Message::RENDER);
+        message->setQid(qid);
+        message->setOpcode(opcode);
+        message->setRcode(rcode);
+        message->setUDPSize(RecursorImpl::DEFAULT_LOCAL_UDPSIZE);
+
+        message->setHeaderFlag(MessageFlag::QR());
+        message->setHeaderFlag(MessageFlag::RA());
+        if (rd) {
+            message->setHeaderFlag(MessageFlag::RD());
+        }
+        if (cd) {
+            message->setHeaderFlag(MessageFlag::CD());
+        }
+
+
+        // Copy the question section.
+        for_each(questions.begin(), questions.end(), QuestionInserter(message));
+
+        // If the buffer already has an answer in it, copy RRsets from
+        // that into the new message, then clear the buffer and render
+        // the new message into it.
+        if (buffer->getLength() != 0) {
+            try {
+                Message incoming(Message::PARSE);
+                InputBuffer ibuf(buffer->getData(), buffer->getLength());
+                incoming.fromWire(ibuf);
+                for_each(incoming.beginSection(Section::ANSWER()), 
+                         incoming.endSection(Section::ANSWER()),
+                         SectionInserter(message, Section::ANSWER(), true));
+                for_each(incoming.beginSection(Section::ADDITIONAL()), 
+                         incoming.endSection(Section::ADDITIONAL()),
+                         SectionInserter(message, Section::ADDITIONAL(), true));
+                for_each(incoming.beginSection(Section::AUTHORITY()), 
+                         incoming.endSection(Section::AUTHORITY()),
+                         SectionInserter(message, Section::AUTHORITY(), true));
+            } catch (const Exception& ex) {
+                // Incoming message couldn't be read, we just SERVFAIL
+                message->setRcode(Rcode::SERVFAIL());
+            }
+
+        }
+
+        // Now we can clear the buffer and render the new message into it
+        buffer->clear();
+        MessageRenderer renderer(*buffer);
+
         if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
         if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
-            renderer.setLengthLimit(message.getUDPSize());
+            renderer.setLengthLimit(message->getUDPSize());
         } else {
         } else {
             renderer.setLengthLimit(65535);
             renderer.setLengthLimit(65535);
         }
         }
-        message.toWire(renderer);
+
+        message->toWire(renderer);
+
         if (server_->getVerbose()) {
         if (server_->getVerbose()) {
-            cerr << "[b10-recurse] sending a response (" << renderer.getLength()
-                 << " bytes):\n" << message.toText() << endl;
+            cerr << "[b10-recurse] sending a response ("
+                 << renderer.getLength() << " bytes):\n"
+                 << message->toText() << endl;
         }
         }
     }
     }
 
 
@@ -129,9 +267,9 @@ private:
 // This is a derived class of \c IOCallback, to serve
 // This is a derived class of \c IOCallback, to serve
 // as a callback in the asiolink module.  It checks for queued
 // as a callback in the asiolink module.  It checks for queued
 // configuration messages, and executes them if found.
 // configuration messages, and executes them if found.
-class ConfigChecker : public IOCallback {
+class ConfigCheck : public IOCallback {
 public:
 public:
-    ConfigChecker(Recursor* srv) : server_(srv) {}
+    ConfigCheck(Recursor* srv) : server_(srv) {}
     virtual void operator()(const IOMessage& io_message UNUSED_PARAM) const {
     virtual void operator()(const IOMessage& io_message UNUSED_PARAM) const {
         if (server_->configSession()->hasQueuedMsgs()) {
         if (server_->configSession()->hasQueuedMsgs()) {
             server_->configSession()->checkCommand();
             server_->configSession()->checkCommand();
@@ -141,9 +279,9 @@ private:
     Recursor* server_;
     Recursor* server_;
 };
 };
 
 
-Recursor::Recursor(asiolink::IOService& io_service) :
-    impl_(new RecursorImpl(io_service)),
-    checkin_(new ConfigChecker(this)),
+Recursor::Recursor(const char& forward) :
+    impl_(new RecursorImpl(forward)),
+    checkin_(new ConfigCheck(this)),
     dns_lookup_(new MessageLookup(this)),
     dns_lookup_(new MessageLookup(this)),
     dns_answer_(new MessageAnswer(this))
     dns_answer_(new MessageAnswer(this))
 {}
 {}
@@ -155,55 +293,11 @@ Recursor::~Recursor() {
     delete dns_answer_;
     delete dns_answer_;
 }
 }
 
 
-namespace {
-class QuestionInserter {
-public:
-    QuestionInserter(Message* message) : message_(message) {}
-    void operator()(const QuestionPtr question) {
-        message_->addQuestion(question);
-    }
-    Message* message_;
-};
-
 void
 void
-makeErrorMessage(Message& message, MessageRenderer& renderer,
-                 const Rcode& rcode, const bool verbose_mode)
-{
-    // extract the parameters that should be kept.
-    // XXX: with the current implementation, it's not easy to set EDNS0
-    // depending on whether the query had it.  So we'll simply omit it.
-    const qid_t qid = message.getQid();
-    const bool rd = message.getHeaderFlag(MessageFlag::RD());
-    const bool cd = message.getHeaderFlag(MessageFlag::CD());
-    const Opcode& opcode = message.getOpcode();
-    vector<QuestionPtr> questions;
-
-    // If this is an error to a query or notify, we should also copy the
-    // question section.
-    if (opcode == Opcode::QUERY() || opcode == Opcode::NOTIFY()) {
-        questions.assign(message.beginQuestion(), message.endQuestion());
-    }
-
-    message.clear(Message::RENDER);
-    message.setQid(qid);
-    message.setOpcode(opcode);
-    message.setHeaderFlag(MessageFlag::QR());
-    message.setUDPSize(RecursorImpl::DEFAULT_LOCAL_UDPSIZE);
-    if (rd) {
-        message.setHeaderFlag(MessageFlag::RD());
-    }
-    if (cd) {
-        message.setHeaderFlag(MessageFlag::CD());
-    }
-    for_each(questions.begin(), questions.end(), QuestionInserter(&message));
-    message.setRcode(rcode);
-    message.toWire(renderer);
-
-    if (verbose_mode) {
-        cerr << "[b10-recurse] sending an error response (" <<
-            renderer.getLength() << " bytes):\n" << message.toText() << endl;
-    }
-}
+Recursor::setIOService(asiolink::IOService& ios) {
+    impl_->queryShutdown();
+    impl_->querySetup(ios);
+    io_ = &ios;
 }
 }
 
 
 void
 void
@@ -227,120 +321,114 @@ Recursor::configSession() const {
 }
 }
 
 
 void
 void
-Recursor::processMessage(const IOMessage& io_message, Message& message,
-                        MessageRenderer& renderer,
-                        BasicServer* server, bool& complete)
+Recursor::processMessage(const IOMessage& io_message, MessagePtr message,
+                        OutputBufferPtr buffer, IOServer* server)
 {
 {
     InputBuffer request_buffer(io_message.getData(), io_message.getDataSize());
     InputBuffer request_buffer(io_message.getData(), io_message.getDataSize());
-
     // First, check the header part.  If we fail even for the base header,
     // First, check the header part.  If we fail even for the base header,
     // just drop the message.
     // just drop the message.
     try {
     try {
-        message.parseHeader(request_buffer);
+        message->parseHeader(request_buffer);
 
 
         // Ignore all responses.
         // Ignore all responses.
-        if (message.getHeaderFlag(MessageFlag::QR())) {
+        if (message->getHeaderFlag(MessageFlag::QR())) {
             if (impl_->verbose_mode_) {
             if (impl_->verbose_mode_) {
                 cerr << "[b10-recurse] received unexpected response, ignoring"
                 cerr << "[b10-recurse] received unexpected response, ignoring"
                      << endl;
                      << endl;
             }
             }
-            complete = false;
-            server->resume();
+            server->resume(false);
             return;
             return;
         }
         }
     } catch (const Exception& ex) {
     } catch (const Exception& ex) {
         if (impl_->verbose_mode_) {
         if (impl_->verbose_mode_) {
             cerr << "[b10-recurse] DNS packet exception: " << ex.what() << endl;
             cerr << "[b10-recurse] DNS packet exception: " << ex.what() << endl;
         }
         }
-        complete = false;
-        server->resume();
+        server->resume(false);
         return;
         return;
     }
     }
 
 
     // Parse the message.  On failure, return an appropriate error.
     // Parse the message.  On failure, return an appropriate error.
     try {
     try {
-        message.fromWire(request_buffer);
+        message->fromWire(request_buffer);
     } catch (const DNSProtocolError& error) {
     } catch (const DNSProtocolError& error) {
         if (impl_->verbose_mode_) {
         if (impl_->verbose_mode_) {
             cerr << "[b10-recurse] returning " <<  error.getRcode().toText()
             cerr << "[b10-recurse] returning " <<  error.getRcode().toText()
                  << ": " << error.what() << endl;
                  << ": " << error.what() << endl;
         }
         }
-        makeErrorMessage(message, renderer, error.getRcode(),
+        makeErrorMessage(message, buffer, error.getRcode(),
                          impl_->verbose_mode_);
                          impl_->verbose_mode_);
-        complete = true;
-        server->resume();
+        server->resume(true);
         return;
         return;
     } catch (const Exception& ex) {
     } catch (const Exception& ex) {
         if (impl_->verbose_mode_) {
         if (impl_->verbose_mode_) {
             cerr << "[b10-recurse] returning SERVFAIL: " << ex.what() << endl;
             cerr << "[b10-recurse] returning SERVFAIL: " << ex.what() << endl;
         }
         }
-        makeErrorMessage(message, renderer, Rcode::SERVFAIL(),
+        makeErrorMessage(message, buffer, Rcode::SERVFAIL(),
                          impl_->verbose_mode_);
                          impl_->verbose_mode_);
-        complete = true;
-        server->resume();
+        server->resume(true);
         return;
         return;
     } // other exceptions will be handled at a higher layer.
     } // other exceptions will be handled at a higher layer.
 
 
     if (impl_->verbose_mode_) {
     if (impl_->verbose_mode_) {
         cerr << "[b10-recurse] received a message:\n"
         cerr << "[b10-recurse] received a message:\n"
-             << message.toText() << endl;
+             << message->toText() << endl;
     }
     }
 
 
     // Perform further protocol-level validation.
     // Perform further protocol-level validation.
-    if (message.getOpcode() == Opcode::NOTIFY()) {
-        makeErrorMessage(message, renderer, Rcode::NOTAUTH(),
+    bool sendAnswer = true;
+    if (message->getOpcode() == Opcode::NOTIFY()) {
+        makeErrorMessage(message, buffer, Rcode::NOTAUTH(),
                          impl_->verbose_mode_);
                          impl_->verbose_mode_);
-        complete = true;
-    } else if (message.getOpcode() != Opcode::QUERY()) {
+    } else if (message->getOpcode() != Opcode::QUERY()) {
         if (impl_->verbose_mode_) {
         if (impl_->verbose_mode_) {
             cerr << "[b10-recurse] unsupported opcode" << endl;
             cerr << "[b10-recurse] unsupported opcode" << endl;
         }
         }
-        makeErrorMessage(message, renderer, Rcode::NOTIMP(),
+        makeErrorMessage(message, buffer, Rcode::NOTIMP(),
                          impl_->verbose_mode_);
                          impl_->verbose_mode_);
-        complete = true;
-    } else if (message.getRRCount(Section::QUESTION()) != 1) {
-        makeErrorMessage(message, renderer, Rcode::FORMERR(),
+    } else if (message->getRRCount(Section::QUESTION()) != 1) {
+        makeErrorMessage(message, buffer, Rcode::FORMERR(),
                          impl_->verbose_mode_);
                          impl_->verbose_mode_);
-        complete = true;
     } else {
     } else {
-        ConstQuestionPtr question = *message.beginQuestion();
+        ConstQuestionPtr question = *message->beginQuestion();
         const RRType &qtype = question->getType();
         const RRType &qtype = question->getType();
         if (qtype == RRType::AXFR()) {
         if (qtype == RRType::AXFR()) {
             if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
             if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
-                makeErrorMessage(message, renderer, Rcode::FORMERR(),
+                makeErrorMessage(message, buffer, Rcode::FORMERR(),
                                  impl_->verbose_mode_);
                                  impl_->verbose_mode_);
             } else {
             } else {
-                makeErrorMessage(message, renderer, Rcode::NOTIMP(),
+                makeErrorMessage(message, buffer, Rcode::NOTIMP(),
                                  impl_->verbose_mode_);
                                  impl_->verbose_mode_);
             }
             }
-            complete = true;
         } else if (qtype == RRType::IXFR()) {
         } else if (qtype == RRType::IXFR()) {
-            makeErrorMessage(message, renderer, Rcode::NOTIMP(),
+            makeErrorMessage(message, buffer, Rcode::NOTIMP(),
                          impl_->verbose_mode_);
                          impl_->verbose_mode_);
-            complete = true;
         } else {
         } else {
-            complete = impl_->processNormalQuery(io_message, *question,
-                                                 message, renderer, server);
+            // The IOQuery object will post the "resume" event to the
+            // IOServer when an answer arrives, so we don't have to do it now.
+            sendAnswer = false;
+            impl_->processNormalQuery(io_message, *question, message,
+                                      buffer, server);
         }
         }
     }
     }
 
 
-    server->resume();
+    if (sendAnswer) {
+        server->resume(true);
+    }
 }
 }
 
 
-bool
+void
 RecursorImpl::processNormalQuery(const IOMessage& io_message,
 RecursorImpl::processNormalQuery(const IOMessage& io_message,
-                                 const Question& question, Message& message,
-                                 MessageRenderer& renderer,
-                                 BasicServer* server)
+                                 const Question& question, MessagePtr message,
+                                 OutputBufferPtr buffer, IOServer* server)
 {
 {
-    const bool dnssec_ok = message.isDNSSECSupported();
-
-    message.makeResponse();
-    message.setRcode(Rcode::NOERROR());
-    message.setDNSSECSupported(dnssec_ok);
-    message.setUDPSize(RecursorImpl::DEFAULT_LOCAL_UDPSIZE);
-    ioquery_.sendQuery(io_message, question, renderer, server);
-    return (true);
+    const bool dnssec_ok = message->isDNSSECSupported();
+
+    message->makeResponse();
+    message->setHeaderFlag(MessageFlag::RA());
+    message->setRcode(Rcode::NOERROR());
+    message->setDNSSECSupported(dnssec_ok);
+    message->setUDPSize(RecursorImpl::DEFAULT_LOCAL_UDPSIZE);
+    ioquery_->sendQuery(io_message, question, buffer, server);
 }
 }
 
 
 ConstElementPtr
 ConstElementPtr

+ 12 - 11
src/bin/recurse/recursor.h

@@ -27,8 +27,6 @@
 namespace isc {
 namespace isc {
 namespace dns {
 namespace dns {
 class InputBuffer;
 class InputBuffer;
-class Message;
-class MessageRenderer;
 }
 }
 }
 }
 
 
@@ -51,26 +49,28 @@ private:
 public:
 public:
     /// The constructor.
     /// The constructor.
     ///
     ///
-    /// \param use_cache Whether to enable hot spot cache for lookup results.
-    /// \param xfrout_client Communication interface with a separate xfrout
-    /// process.  It's normally a reference to an xfr::XfroutClient object,
-    /// but can refer to a local mock object for testing (or other
-    /// experimental) purposes.
-    Recursor(asiolink::IOService& io_service);
+    /// \param forward The address of the name server to which requests
+    /// should be forwarded.  (In the future, when the server is running
+    /// in forwarding mode, the forward nameserver addresses will be set
+    /// via the config channel instaed.)
+    Recursor(const char& forward);
     ~Recursor();
     ~Recursor();
     //@}
     //@}
     /// \return \c true if the \message contains a response to be returned;
     /// \return \c true if the \message contains a response to be returned;
     /// otherwise \c false.
     /// otherwise \c false.
     void processMessage(const asiolink::IOMessage& io_message,
     void processMessage(const asiolink::IOMessage& io_message,
-                        isc::dns::Message& message,
-                        isc::dns::MessageRenderer& response_renderer,
-                        asiolink::BasicServer* server, bool& complete);
+                        isc::dns::MessagePtr message,
+                        isc::dns::OutputBufferPtr buffer,
+                        asiolink::IOServer* server);
     void setVerbose(bool on);
     void setVerbose(bool on);
     bool getVerbose() const;
     bool getVerbose() const;
     isc::data::ConstElementPtr updateConfig(isc::data::ConstElementPtr config);
     isc::data::ConstElementPtr updateConfig(isc::data::ConstElementPtr config);
     isc::config::ModuleCCSession* configSession() const;
     isc::config::ModuleCCSession* configSession() const;
     void setConfigSession(isc::config::ModuleCCSession* config_session);
     void setConfigSession(isc::config::ModuleCCSession* config_session);
 
 
+    void setIOService(asiolink::IOService& ios);
+    asiolink::IOService& getIOService() const { return (*io_); }
+
     asiolink::DNSLookup* getDNSLookupProvider() {
     asiolink::DNSLookup* getDNSLookupProvider() {
         return (dns_lookup_);
         return (dns_lookup_);
     }
     }
@@ -83,6 +83,7 @@ public:
 
 
 private:
 private:
     RecursorImpl* impl_;
     RecursorImpl* impl_;
+    asiolink::IOService* io_;
     asiolink::IOCallback* checkin_;
     asiolink::IOCallback* checkin_;
     asiolink::DNSLookup* dns_lookup_;
     asiolink::DNSLookup* dns_lookup_;
     asiolink::DNSAnswer* dns_answer_;
     asiolink::DNSAnswer* dns_answer_;

+ 54 - 58
src/bin/recurse/tests/recursor_unittest.cc

@@ -95,34 +95,42 @@ private:
     };
     };
 
 
     // A nonoperative task object to be used in calls to processMessage()
     // A nonoperative task object to be used in calls to processMessage()
-    class MockTask : public BasicServer {
+    class MockTask : public IOServer {
+    public:
+        MockTask() : done_(false) {}
         void operator()(asio::error_code ec UNUSED_PARAM,
         void operator()(asio::error_code ec UNUSED_PARAM,
                         size_t length UNUSED_PARAM)
                         size_t length UNUSED_PARAM)
         {}
         {}
+        // virtual void doLookup() { return; }
+        virtual void resume(const bool done) { done_ = done; }
+        virtual bool hasAnswer() { return (done_); }
+        virtual int value() { return (0); }
+    private:
+        bool done_;
     };
     };
 
 
 protected:
 protected:
     RecursorTest() : ios(*TEST_PORT, true, false, NULL, NULL, NULL),
     RecursorTest() : ios(*TEST_PORT, true, false, NULL, NULL, NULL),
-                    server(ios),
+                    server(*DEFAULT_REMOTE_ADDRESS),
                     request_message(Message::RENDER),
                     request_message(Message::RENDER),
-                    parse_message(Message::PARSE),
+                    parse_message(new Message(Message::PARSE)),
                     default_qid(0x1035), opcode(Opcode(Opcode::QUERY())),
                     default_qid(0x1035), opcode(Opcode(Opcode::QUERY())),
                     qname("www.example.com"),
                     qname("www.example.com"),
                     qclass(RRClass::IN()), qtype(RRType::A()),
                     qclass(RRClass::IN()), qtype(RRType::A()),
                     io_message(NULL), endpoint(NULL), request_obuffer(0),
                     io_message(NULL), endpoint(NULL), request_obuffer(0),
                     request_renderer(request_obuffer),
                     request_renderer(request_obuffer),
-                    response_obuffer(0), response_renderer(response_obuffer)
+                    response_obuffer(new OutputBuffer(0))
     {}
     {}
     ~RecursorTest() {
     ~RecursorTest() {
         delete io_message;
         delete io_message;
         delete endpoint;
         delete endpoint;
     }
     }
     MockSession notify_session;
     MockSession notify_session;
-    MockTask noOp;
+    MockTask task;
     IOService ios;
     IOService ios;
     Recursor server;
     Recursor server;
     Message request_message;
     Message request_message;
-    Message parse_message;
+    MessagePtr parse_message;
     const qid_t default_qid;
     const qid_t default_qid;
     const Opcode opcode;
     const Opcode opcode;
     const Name qname;
     const Name qname;
@@ -133,8 +141,7 @@ protected:
     const IOEndpoint* endpoint;
     const IOEndpoint* endpoint;
     OutputBuffer request_obuffer;
     OutputBuffer request_obuffer;
     MessageRenderer request_renderer;
     MessageRenderer request_renderer;
-    OutputBuffer response_obuffer;
-    MessageRenderer response_renderer;
+    OutputBufferPtr response_obuffer;
     vector<uint8_t> data;
     vector<uint8_t> data;
 
 
     void createDataFromFile(const char* const datafile, int protocol);
     void createDataFromFile(const char* const datafile, int protocol);
@@ -305,13 +312,11 @@ TEST_F(RecursorTest, unsupportedRequest) {
         createDataFromFile("simplequery_fromWire");
         createDataFromFile("simplequery_fromWire");
         data[2] = ((i << 3) & 0xff);
         data[2] = ((i << 3) & 0xff);
 
 
-        parse_message.clear(Message::PARSE);
-        bool done;
+        parse_message->clear(Message::PARSE);
         server.processMessage(*io_message, parse_message,
         server.processMessage(*io_message, parse_message,
-                              response_renderer, &noOp,
-                              done);
-        EXPECT_TRUE(done);
-        headerCheck(parse_message, default_qid, Rcode::NOTIMP(), i, QR_FLAG,
+                              response_obuffer, &task);
+        EXPECT_TRUE(task.hasAnswer());
+        headerCheck(*parse_message, default_qid, Rcode::NOTIMP(), i, QR_FLAG,
                     0, 0, 0, 0);
                     0, 0, 0, 0);
     }
     }
 }
 }
@@ -328,13 +333,12 @@ TEST_F(RecursorTest, verbose) {
 // Multiple questions.  Should result in FORMERR.
 // Multiple questions.  Should result in FORMERR.
 TEST_F(RecursorTest, multiQuestion) {
 TEST_F(RecursorTest, multiQuestion) {
     createDataFromFile("multiquestion_fromWire");
     createDataFromFile("multiquestion_fromWire");
-    bool done;
-    server.processMessage(*io_message, parse_message, response_renderer, &noOp, done);
-    EXPECT_TRUE(done);
-    headerCheck(parse_message, default_qid, Rcode::FORMERR(), opcode.getCode(),
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_TRUE(task.hasAnswer());
+    headerCheck(*parse_message, default_qid, Rcode::FORMERR(), opcode.getCode(),
                 QR_FLAG, 2, 0, 0, 0);
                 QR_FLAG, 2, 0, 0, 0);
 
 
-    QuestionIterator qit = parse_message.beginQuestion();
+    QuestionIterator qit = parse_message->beginQuestion();
     EXPECT_EQ(Name("example.com"), (*qit)->getName());
     EXPECT_EQ(Name("example.com"), (*qit)->getName());
     EXPECT_EQ(RRClass::IN(), (*qit)->getClass());
     EXPECT_EQ(RRClass::IN(), (*qit)->getClass());
     EXPECT_EQ(RRType::A(), (*qit)->getType());
     EXPECT_EQ(RRType::A(), (*qit)->getType());
@@ -343,16 +347,15 @@ TEST_F(RecursorTest, multiQuestion) {
     EXPECT_EQ(RRClass::IN(), (*qit)->getClass());
     EXPECT_EQ(RRClass::IN(), (*qit)->getClass());
     EXPECT_EQ(RRType::AAAA(), (*qit)->getType());
     EXPECT_EQ(RRType::AAAA(), (*qit)->getType());
     ++qit;
     ++qit;
-    EXPECT_TRUE(qit == parse_message.endQuestion());
+    EXPECT_TRUE(qit == parse_message->endQuestion());
 }
 }
 
 
 // Incoming data doesn't even contain the complete header.  Must be silently
 // Incoming data doesn't even contain the complete header.  Must be silently
 // dropped.
 // dropped.
 TEST_F(RecursorTest, shortMessage) {
 TEST_F(RecursorTest, shortMessage) {
     createDataFromFile("shortmessage_fromWire");
     createDataFromFile("shortmessage_fromWire");
-    bool done;
-    server.processMessage(*io_message, parse_message, response_renderer, &noOp, done);
-    EXPECT_FALSE(done);
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_FALSE(task.hasAnswer());
 }
 }
 
 
 // Response messages.  Must be silently dropped, whether it's a valid response
 // Response messages.  Must be silently dropped, whether it's a valid response
@@ -360,78 +363,73 @@ TEST_F(RecursorTest, shortMessage) {
 TEST_F(RecursorTest, response) {
 TEST_F(RecursorTest, response) {
     // A valid (although unusual) response
     // A valid (although unusual) response
     createDataFromFile("simpleresponse_fromWire");
     createDataFromFile("simpleresponse_fromWire");
-    bool done;
-    server.processMessage(*io_message, parse_message, response_renderer, &noOp, done);
-    EXPECT_FALSE(done);
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_FALSE(task.hasAnswer());
 
 
     // A response with a broken question section.  must be dropped rather than
     // A response with a broken question section.  must be dropped rather than
     // returning FORMERR.
     // returning FORMERR.
     createDataFromFile("shortresponse_fromWire");
     createDataFromFile("shortresponse_fromWire");
-    server.processMessage(*io_message, parse_message, response_renderer, &noOp, done);
-    EXPECT_FALSE(done);
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_FALSE(task.hasAnswer());
 
 
     // A response to iquery.  must be dropped rather than returning NOTIMP.
     // A response to iquery.  must be dropped rather than returning NOTIMP.
     createDataFromFile("iqueryresponse_fromWire");
     createDataFromFile("iqueryresponse_fromWire");
-    server.processMessage(*io_message, parse_message, response_renderer, &noOp, done);
-    EXPECT_FALSE(done);
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_FALSE(task.hasAnswer());
 }
 }
 
 
 // Query with a broken question
 // Query with a broken question
 TEST_F(RecursorTest, shortQuestion) {
 TEST_F(RecursorTest, shortQuestion) {
     createDataFromFile("shortquestion_fromWire");
     createDataFromFile("shortquestion_fromWire");
-    bool done;
-    server.processMessage(*io_message, parse_message, response_renderer, &noOp, done);
-    EXPECT_TRUE(done);
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_TRUE(task.hasAnswer());
     // Since the query's question is broken, the question section of the
     // Since the query's question is broken, the question section of the
     // response should be empty.
     // response should be empty.
-    headerCheck(parse_message, default_qid, Rcode::FORMERR(), opcode.getCode(),
+    headerCheck(*parse_message, default_qid, Rcode::FORMERR(), opcode.getCode(),
                 QR_FLAG, 0, 0, 0, 0);
                 QR_FLAG, 0, 0, 0, 0);
 }
 }
 
 
 // Query with a broken answer section
 // Query with a broken answer section
 TEST_F(RecursorTest, shortAnswer) {
 TEST_F(RecursorTest, shortAnswer) {
     createDataFromFile("shortanswer_fromWire");
     createDataFromFile("shortanswer_fromWire");
-    bool done;
-    server.processMessage(*io_message, parse_message, response_renderer, &noOp, done);
-    EXPECT_TRUE(done);
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_TRUE(task.hasAnswer());
 
 
     // This is a bogus query, but question section is valid.  So the response
     // This is a bogus query, but question section is valid.  So the response
     // should copy the question section.
     // should copy the question section.
-    headerCheck(parse_message, default_qid, Rcode::FORMERR(), opcode.getCode(),
+    headerCheck(*parse_message, default_qid, Rcode::FORMERR(), opcode.getCode(),
                 QR_FLAG, 1, 0, 0, 0);
                 QR_FLAG, 1, 0, 0, 0);
 
 
-    QuestionIterator qit = parse_message.beginQuestion();
+    QuestionIterator qit = parse_message->beginQuestion();
     EXPECT_EQ(Name("example.com"), (*qit)->getName());
     EXPECT_EQ(Name("example.com"), (*qit)->getName());
     EXPECT_EQ(RRClass::IN(), (*qit)->getClass());
     EXPECT_EQ(RRClass::IN(), (*qit)->getClass());
     EXPECT_EQ(RRType::A(), (*qit)->getType());
     EXPECT_EQ(RRType::A(), (*qit)->getType());
     ++qit;
     ++qit;
-    EXPECT_TRUE(qit == parse_message.endQuestion());
+    EXPECT_TRUE(qit == parse_message->endQuestion());
 }
 }
 
 
 // Query with unsupported version of EDNS.
 // Query with unsupported version of EDNS.
 TEST_F(RecursorTest, ednsBadVers) {
 TEST_F(RecursorTest, ednsBadVers) {
     createDataFromFile("queryBadEDNS_fromWire");
     createDataFromFile("queryBadEDNS_fromWire");
-    bool done;
-    server.processMessage(*io_message, parse_message, response_renderer, &noOp, done);
-    EXPECT_TRUE(done);
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_TRUE(task.hasAnswer());
 
 
     // The response must have an EDNS OPT RR in the additional section.
     // The response must have an EDNS OPT RR in the additional section.
     // Note that the DNSSEC DO bit is cleared even if this bit in the query
     // Note that the DNSSEC DO bit is cleared even if this bit in the query
     // is set.  This is a limitation of the current implementation.
     // is set.  This is a limitation of the current implementation.
-    headerCheck(parse_message, default_qid, Rcode::BADVERS(), opcode.getCode(),
+    headerCheck(*parse_message, default_qid, Rcode::BADVERS(), opcode.getCode(),
                 QR_FLAG, 1, 0, 0, 1);
                 QR_FLAG, 1, 0, 0, 1);
-    EXPECT_EQ(4096, parse_message.getUDPSize());
-    EXPECT_FALSE(parse_message.isDNSSECSupported());
+    EXPECT_EQ(4096, parse_message->getUDPSize());
+    EXPECT_FALSE(parse_message->isDNSSECSupported());
 }
 }
 
 
 TEST_F(RecursorTest, AXFROverUDP) {
 TEST_F(RecursorTest, AXFROverUDP) {
     // AXFR over UDP is invalid and should result in FORMERR.
     // AXFR over UDP is invalid and should result in FORMERR.
     createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
     createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
                         RRType::AXFR(), IPPROTO_UDP);
                         RRType::AXFR(), IPPROTO_UDP);
-    bool done;
-    server.processMessage(*io_message, parse_message, response_renderer, &noOp, done);
-    EXPECT_TRUE(done);
-    headerCheck(parse_message, default_qid, Rcode::FORMERR(), opcode.getCode(),
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_TRUE(task.hasAnswer());
+    headerCheck(*parse_message, default_qid, Rcode::FORMERR(), opcode.getCode(),
                 QR_FLAG, 1, 0, 0, 0);
                 QR_FLAG, 1, 0, 0, 0);
 }
 }
 
 
@@ -439,10 +437,9 @@ TEST_F(RecursorTest, AXFRFail) {
     createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
     createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
                         RRType::AXFR(), IPPROTO_TCP);
                         RRType::AXFR(), IPPROTO_TCP);
     // AXFR is not implemented and should always send NOTIMP.
     // AXFR is not implemented and should always send NOTIMP.
-    bool done;
-    server.processMessage(*io_message, parse_message, response_renderer, &noOp, done);
-    EXPECT_TRUE(done);
-    headerCheck(parse_message, default_qid, Rcode::NOTIMP(), opcode.getCode(),
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_TRUE(task.hasAnswer());
+    headerCheck(*parse_message, default_qid, Rcode::NOTIMP(), opcode.getCode(),
                 QR_FLAG, 1, 0, 0, 0);
                 QR_FLAG, 1, 0, 0, 0);
 }
 }
 
 
@@ -454,10 +451,9 @@ TEST_F(RecursorTest, notifyFail) {
     request_message.setQid(default_qid);
     request_message.setQid(default_qid);
     request_message.toWire(request_renderer);
     request_message.toWire(request_renderer);
     createRequestPacket(IPPROTO_UDP);
     createRequestPacket(IPPROTO_UDP);
-    bool done;
-    server.processMessage(*io_message, parse_message, response_renderer, &noOp, done);
-    EXPECT_TRUE(done);
-    headerCheck(parse_message, default_qid, Rcode::NOTAUTH(),
+    server.processMessage(*io_message, parse_message, response_obuffer, &task);
+    EXPECT_TRUE(task.hasAnswer());
+    headerCheck(*parse_message, default_qid, Rcode::NOTAUTH(),
                 Opcode::NOTIFY().getCode(), QR_FLAG, 0, 0, 0, 0);
                 Opcode::NOTIFY().getCode(), QR_FLAG, 0, 0, 0, 0);
 }
 }
 
 

+ 15 - 10
src/lib/asiolink/asiolink.cc

@@ -28,7 +28,6 @@
 
 
 #include <dns/buffer.h>
 #include <dns/buffer.h>
 #include <dns/message.h>
 #include <dns/message.h>
-#include <dns/messagerenderer.h>
 
 
 #include <asiolink/asiolink.h>
 #include <asiolink/asiolink.h>
 #include <asiolink/internal/tcpdns.h>
 #include <asiolink/internal/tcpdns.h>
@@ -94,24 +93,30 @@ IOMessage::IOMessage(const void* data, const size_t data_size,
     remote_endpoint_(remote_endpoint)
     remote_endpoint_(remote_endpoint)
 {}
 {}
 
 
-IOQuery::IOQuery(IOService& io_service) : io_service_(io_service) {}
+IOQuery::IOQuery(IOService& io_service, const char& forward) :
+    io_service_(io_service)
+{
+    error_code err;
+    ns_addr_ = ip::address::from_string(&forward, err);
+    if (err) {
+        isc_throw(IOError, "Invalid IP address '" << &ns_addr_ << "': "
+                  << err.message());
+    }
+}
 
 
 void
 void
 IOQuery::sendQuery(const IOMessage& io_message,
 IOQuery::sendQuery(const IOMessage& io_message,
-                   const Question& question, MessageRenderer& renderer,
-                   BasicServer* completer)
+                   const Question& question, OutputBufferPtr buffer,
+                   IOServer* server)
 {
 {
-    error_code err;
-    // XXX: hard-code the address for now:
-    const ip::address addr = ip::address::from_string("192.168.1.12", err);
 
 
     // XXX: eventually we will need to be able to determine whether
     // XXX: eventually we will need to be able to determine whether
     // the message should be sent via TCP or UDP, or sent initially via
     // the message should be sent via TCP or UDP, or sent initially via
     // UDP and then fall back to TCP on failure, but for the moment
     // UDP and then fall back to TCP on failure, but for the moment
     // we're only going to handle UDP.
     // we're only going to handle UDP.
-    UDPQuery* query = new UDPQuery(io_service_.get_io_service(), io_message,
-                                   question, addr, renderer, completer);
-    (*query)();
+    asio::io_service& io = io_service_.get_io_service();
+    UDPQuery q(io, io_message, question, ns_addr_, buffer, server);
+    io.post(q);
 }
 }
 
 
 class IOServiceImpl {
 class IOServiceImpl {

+ 34 - 21
src/lib/asiolink/asiolink.h

@@ -28,6 +28,7 @@
 
 
 #include <boost/function.hpp>
 #include <boost/function.hpp>
 
 
+#include <dns/buffer.h>
 #include <dns/message.h>
 #include <dns/message.h>
 #include <dns/messagerenderer.h>
 #include <dns/messagerenderer.h>
 #include <dns/question.h>
 #include <dns/question.h>
@@ -388,19 +389,29 @@ private:
 };
 };
 
 
 /// XXX: need to add doc
 /// XXX: need to add doc
-class BasicServer {
+class IOServer;
+typedef boost::shared_ptr<IOServer> IOServerPtr;
+class IOServer {
 public:
 public:
-    BasicServer() : self(this) {}
+    IOServer() : self_(this), cloned_(false) {}
+
     virtual void operator()(asio::error_code ec = asio::error_code(),
     virtual void operator()(asio::error_code ec = asio::error_code(),
                             size_t length = 0)
                             size_t length = 0)
     {
     {
-        (*self)(ec, length);
+        (*self_)(ec, length);
     }
     }
 
 
-    virtual void doLookup() {}
-    virtual void resume() {}
+    virtual void doLookup() { self_->doLookup(); }
+    virtual void resume(const bool done) { self_->resume(done); }
+    virtual bool hasAnswer() { return (self_->hasAnswer()); }
+    virtual int value() { return (self_->value()); }
+    virtual IOServer* clone() { return (self_->clone()); }
+
 private:
 private:
-    BasicServer* self;
+    IOServer* self_;
+
+protected:
+    bool cloned_;
 };
 };
 
 
 template <typename T>
 template <typename T>
@@ -437,7 +448,7 @@ protected:
     ///
     ///
     /// This is intentionally defined as \c protected as this base class
     /// This is intentionally defined as \c protected as this base class
     /// should never be instantiated (except as part of a derived class).
     /// should never be instantiated (except as part of a derived class).
-    DNSLookup() : self(this) {}
+    DNSLookup() : self_(this) {}
 public:
 public:
     /// \brief The destructor
     /// \brief The destructor
     virtual ~DNSLookup() {}
     virtual ~DNSLookup() {}
@@ -447,16 +458,15 @@ public:
     /// that the function ultimately invoked will be the one in the derived
     /// that the function ultimately invoked will be the one in the derived
     /// class.
     /// class.
     virtual void operator()(const IOMessage& io_message,
     virtual void operator()(const IOMessage& io_message,
-                            isc::dns::Message& dns_message,
-                            isc::dns::MessageRenderer& renderer,
-                            BasicServer* server, bool& success)
-                            const
+                            isc::dns::MessagePtr message,
+                            isc::dns::OutputBufferPtr buffer,
+                            IOServer* server) const
     {
     {
-        (*self)(io_message, dns_message, renderer, server, success);
+        (*self_)(io_message, message, buffer, server);
     }
     }
     //@}
     //@}
 private:
 private:
-    DNSLookup* self;
+    DNSLookup* self_;
 };
 };
 
 
 /// \brief The \c DNSAnswer class is an abstract base class for a DNS
 /// \brief The \c DNSAnswer class is an abstract base class for a DNS
@@ -488,8 +498,8 @@ public:
     virtual ~DNSAnswer() {}
     virtual ~DNSAnswer() {}
     /// \brief The function operator
     /// \brief The function operator
     virtual void operator()(const IOMessage& io_message,
     virtual void operator()(const IOMessage& io_message,
-                            isc::dns::Message& dns_message,
-                            isc::dns::MessageRenderer& renderer) const = 0;
+                            isc::dns::MessagePtr message,
+                            isc::dns::OutputBufferPtr buffer) const = 0;
     //@}
     //@}
 };
 };
 
 
@@ -516,7 +526,7 @@ protected:
     ///
     ///
     /// This is intentionally defined as \c protected as this base class
     /// This is intentionally defined as \c protected as this base class
     /// should never be instantiated (except as part of a derived class).
     /// should never be instantiated (except as part of a derived class).
-    IOCallback() : self(this) {}
+    IOCallback() : self_(this) {}
 public:
 public:
     /// \brief The destructor
     /// \brief The destructor
     virtual ~IOCallback() {}
     virtual ~IOCallback() {}
@@ -526,11 +536,11 @@ public:
     /// that the function ultimately invoked will be the one in the derived
     /// that the function ultimately invoked will be the one in the derived
     /// class.
     /// class.
     virtual void operator()(const IOMessage& io_message) const {
     virtual void operator()(const IOMessage& io_message) const {
-        (*self)(io_message);
+        (*self_)(io_message);
     }
     }
     //@}
     //@}
 private:
 private:
-    IOCallback* self;
+    IOCallback* self_;
 };
 };
 
 
 /// \brief The \c IOService class is a wrapper for the ASIO \c io_service
 /// \brief The \c IOService class is a wrapper for the ASIO \c io_service
@@ -600,13 +610,16 @@ private:
 /// the ASIO code that carries out upstream queries.
 /// the ASIO code that carries out upstream queries.
 class IOQuery {
 class IOQuery {
 public:
 public:
-    IOQuery(IOService& io_service);
+    IOQuery(IOService& io_service, const char& forward);
+
+    /// \brief Sends a query to the IOQuery object.
     void sendQuery(const IOMessage& io_message,
     void sendQuery(const IOMessage& io_message,
                    const isc::dns::Question& question,
                    const isc::dns::Question& question,
-                   isc::dns::MessageRenderer& renderer,
-                   BasicServer* caller);
+                   isc::dns::OutputBufferPtr buffer,
+                   IOServer* server);
 private:
 private:
     IOService& io_service_;
     IOService& io_service_;
+    asio::ip::address ns_addr_;
 };
 };
 
 
 }      // asiolink
 }      // asiolink

+ 11 - 4
src/lib/asiolink/internal/tcpdns.h

@@ -25,7 +25,6 @@
 
 
 #include <dns/buffer.h>
 #include <dns/buffer.h>
 #include <dns/message.h>
 #include <dns/message.h>
-#include <dns/messagerenderer.h>
 
 
 #include <asiolink/asiolink.h>
 #include <asiolink/asiolink.h>
 #include <asiolink/internal/coroutine.h>
 #include <asiolink/internal/coroutine.h>
@@ -73,7 +72,7 @@ private:
 //
 //
 // Asynchronous TCP server coroutine
 // Asynchronous TCP server coroutine
 //
 //
-class TCPServer : public virtual BasicServer, public virtual coroutine {
+class TCPServer : public virtual IOServer, public virtual coroutine {
 public:
 public:
     explicit TCPServer(asio::io_service& io_service,
     explicit TCPServer(asio::io_service& io_service,
                        const asio::ip::address& addr, const uint16_t port, 
                        const asio::ip::address& addr, const uint16_t port, 
@@ -85,7 +84,15 @@ public:
                     size_t length = 0);
                     size_t length = 0);
 
 
     void doLookup();
     void doLookup();
-    void resume();
+    void resume(const bool done);
+    bool hasAnswer() { return (done_); }
+    int value() { return (get_value()); }
+
+    IOServer* clone() {
+        TCPServer* s = new TCPServer(*this);
+        s->cloned_ = true;
+        return (s);
+    }
 
 
 private:
 private:
     enum { MAX_LENGTH = 65535 };
     enum { MAX_LENGTH = 65535 };
@@ -100,12 +107,12 @@ private:
     // constructor or in the coroutine.
     // constructor or in the coroutine.
     boost::shared_ptr<asio::ip::tcp::acceptor> acceptor_;
     boost::shared_ptr<asio::ip::tcp::acceptor> acceptor_;
     boost::shared_ptr<asio::ip::tcp::socket> socket_;
     boost::shared_ptr<asio::ip::tcp::socket> socket_;
-    boost::shared_ptr<isc::dns::MessageRenderer> renderer_;
     boost::shared_ptr<isc::dns::OutputBuffer> lenbuf_;
     boost::shared_ptr<isc::dns::OutputBuffer> lenbuf_;
     boost::shared_ptr<isc::dns::OutputBuffer> respbuf_;
     boost::shared_ptr<isc::dns::OutputBuffer> respbuf_;
     boost::shared_ptr<asiolink::IOEndpoint> peer_;
     boost::shared_ptr<asiolink::IOEndpoint> peer_;
     boost::shared_ptr<asiolink::IOSocket> iosock_;
     boost::shared_ptr<asiolink::IOSocket> iosock_;
     boost::shared_ptr<asiolink::IOMessage> io_message_;
     boost::shared_ptr<asiolink::IOMessage> io_message_;
+    isc::dns::MessagePtr message_;
     boost::shared_ptr<char> data_;
     boost::shared_ptr<char> data_;
 
 
     // State information that is entirely internal to a given instance
     // State information that is entirely internal to a given instance

+ 25 - 12
src/lib/asiolink/internal/udpdns.h

@@ -20,6 +20,7 @@
 #include <config.h>
 #include <config.h>
 
 
 #include <asio.hpp>
 #include <asio.hpp>
+#include <boost/shared_array.hpp>
 #include <boost/shared_ptr.hpp>
 #include <boost/shared_ptr.hpp>
 
 
 #include <dns/buffer.h>
 #include <dns/buffer.h>
@@ -72,7 +73,7 @@ private:
 //
 //
 // Asynchronous UDP server coroutine
 // Asynchronous UDP server coroutine
 //
 //
-class UDPServer : public virtual BasicServer, public virtual coroutine {
+class UDPServer : public virtual IOServer, public virtual coroutine {
 public:
 public:
     explicit UDPServer(asio::io_service& io_service,
     explicit UDPServer(asio::io_service& io_service,
                        const asio::ip::address& addr, const uint16_t port,
                        const asio::ip::address& addr, const uint16_t port,
@@ -84,11 +85,18 @@ public:
                     size_t length = 0);
                     size_t length = 0);
 
 
     enum { MAX_LENGTH = 4096 };
     enum { MAX_LENGTH = 4096 };
-    char answer[MAX_LENGTH];
     asio::ip::udp::endpoint peer;
     asio::ip::udp::endpoint peer;
 
 
     void doLookup();
     void doLookup();
-    void resume();
+    void resume(const bool done);
+    bool hasAnswer() { return (done_); }
+    int value() { return (get_value()); }
+
+    IOServer* clone() {
+        UDPServer* s = new UDPServer(*this);
+        s->cloned_ = true;
+        return (s);
+    }
 
 
 private:
 private:
     asio::io_service& io_;
     asio::io_service& io_;
@@ -101,15 +109,14 @@ private:
     boost::shared_ptr<asio::ip::udp::socket> socket_;
     boost::shared_ptr<asio::ip::udp::socket> socket_;
     boost::shared_ptr<char> data_;
     boost::shared_ptr<char> data_;
     boost::shared_ptr<asio::ip::udp::endpoint> sender_;
     boost::shared_ptr<asio::ip::udp::endpoint> sender_;
-    boost::shared_ptr<isc::dns::MessageRenderer> renderer_;
-    boost::shared_ptr<isc::dns::Message> message_;
     boost::shared_ptr<asiolink::IOEndpoint> peer_;
     boost::shared_ptr<asiolink::IOEndpoint> peer_;
     boost::shared_ptr<asiolink::IOSocket> iosock_;
     boost::shared_ptr<asiolink::IOSocket> iosock_;
     boost::shared_ptr<asiolink::IOMessage> io_message_;
     boost::shared_ptr<asiolink::IOMessage> io_message_;
+    isc::dns::MessagePtr message_;
+    isc::dns::OutputBufferPtr respbuf_;
 
 
     // State information that is entirely internal to a given instance
     // State information that is entirely internal to a given instance
     // of the coroutine can be declared here.
     // of the coroutine can be declared here.
-    isc::dns::OutputBuffer respbuf_;
     size_t bytes_;
     size_t bytes_;
     bool done_;
     bool done_;
 
 
@@ -128,21 +135,27 @@ public:
                       const IOMessage& io_message,
                       const IOMessage& io_message,
                       const isc::dns::Question& q,
                       const isc::dns::Question& q,
                       const asio::ip::address& addr,
                       const asio::ip::address& addr,
-                      isc::dns::MessageRenderer& renderer,
-                      BasicServer* caller);
+                      isc::dns::OutputBufferPtr buffer,
+                      IOServer* server);
     void operator()(asio::error_code ec = asio::error_code(),
     void operator()(asio::error_code ec = asio::error_code(),
                     size_t length = 0); 
                     size_t length = 0); 
 private:
 private:
+    enum { MAX_LENGTH = 4096 };
+
     boost::shared_ptr<asio::ip::udp::socket> socket_;
     boost::shared_ptr<asio::ip::udp::socket> socket_;
-    asio::ip::udp::endpoint server_;
+    asio::ip::udp::endpoint remote_;
     isc::dns::Question question_;
     isc::dns::Question question_;
-    char* data_;
-    size_t datalen_;
     isc::dns::OutputBuffer msgbuf_;
     isc::dns::OutputBuffer msgbuf_;
-    BasicServer* caller_;
+    isc::dns::OutputBufferPtr buffer_;;
+    boost::shared_array<char> data_;
+
+    /// \brief The UDP or TCP Server object from which the query originated.
+    // IOServerPtr server_;
+    IOServer* server_;
 };
 };
 }
 }
 
 
+
 #endif // __UDPDNS_H
 #endif // __UDPDNS_H
 
 
 // Local Variables: 
 // Local Variables: 

+ 6 - 5
src/lib/asiolink/tcpdns.cc

@@ -29,7 +29,6 @@
 
 
 #include <dns/buffer.h>
 #include <dns/buffer.h>
 #include <dns/message.h>
 #include <dns/message.h>
-#include <dns/messagerenderer.h>
 
 
 #include <asiolink.h>
 #include <asiolink.h>
 #include <internal/coroutine.h>
 #include <internal/coroutine.h>
@@ -153,7 +152,7 @@ TCPServer::operator()(error_code ec, size_t length) {
         // Reset or instantiate objects that will be needed by the
         // Reset or instantiate objects that will be needed by the
         // DNS lookup and the write call.
         // DNS lookup and the write call.
         respbuf_->clear();
         respbuf_->clear();
-        renderer_.reset(new MessageRenderer(*respbuf_));
+        message_.reset(new Message(Message::PARSE));
 
 
         // Process the DNS message.
         // Process the DNS message.
         bytes_ = length;
         bytes_ = length;
@@ -163,6 +162,8 @@ TCPServer::operator()(error_code ec, size_t length) {
             CORO_YIELD return;
             CORO_YIELD return;
         }
         }
 
 
+        (*answer_callback_)(*io_message_, message_, respbuf_);
+
         // Send the response.
         // Send the response.
         lenbuf_->clear();
         lenbuf_->clear();
         lenbuf_->writeUint16(respbuf_->getLength());
         lenbuf_->writeUint16(respbuf_->getLength());
@@ -174,12 +175,12 @@ TCPServer::operator()(error_code ec, size_t length) {
 
 
 void
 void
 TCPServer::doLookup() {
 TCPServer::doLookup() {
-    Message message(Message::PARSE);
-    (*lookup_callback_)(*io_message_, message, *renderer_, this, done_);
+    (*lookup_callback_)(*io_message_, message_, respbuf_, this);
 }
 }
 
 
 void
 void
-TCPServer::resume() {
+TCPServer::resume(const bool done) {
+    done_ = done;
     io_.post(*this);
     io_.post(*this);
 }
 }
 
 

+ 18 - 17
src/lib/asiolink/udpdns.cc

@@ -80,7 +80,7 @@ UDPServer::UDPServer(io_service& io_service,
                      IOCallback* checkin,
                      IOCallback* checkin,
                      DNSLookup* lookup,
                      DNSLookup* lookup,
                      DNSAnswer* answer) :
                      DNSAnswer* answer) :
-    io_(io_service), respbuf_(0), done_(false),
+    io_(io_service), done_(false),
     checkin_callback_(checkin), lookup_callback_(lookup),
     checkin_callback_(checkin), lookup_callback_(lookup),
     answer_callback_(answer)
     answer_callback_(answer)
 {
 {
@@ -135,8 +135,7 @@ UDPServer::operator()(error_code ec, size_t length) {
 
 
         // Instantiate objects that will be needed by the
         // Instantiate objects that will be needed by the
         // asynchronous send call.
         // asynchronous send call.
-        respbuf_.clear();
-        renderer_.reset(new MessageRenderer(respbuf_));
+        respbuf_.reset(new OutputBuffer(0));
         message_.reset(new Message(Message::PARSE));
         message_.reset(new Message(Message::PARSE));
 
 
         CORO_YIELD io_.post(LookupHandler<UDPServer>(*this));
         CORO_YIELD io_.post(LookupHandler<UDPServer>(*this));
@@ -145,33 +144,32 @@ UDPServer::operator()(error_code ec, size_t length) {
             CORO_YIELD return;
             CORO_YIELD return;
         }
         }
 
 
-        (*answer_callback_)(*io_message_, *message_, *renderer_);
-        CORO_YIELD socket_->async_send_to(buffer(respbuf_.getData(),
-                                                 respbuf_.getLength()),
+        (*answer_callback_)(*io_message_, message_, respbuf_);
+        CORO_YIELD socket_->async_send_to(buffer(respbuf_->getData(),
+                                                 respbuf_->getLength()),
                                      *sender_, *this);
                                      *sender_, *this);
     }
     }
 }
 }
 
 
 void
 void
 UDPServer::doLookup() {
 UDPServer::doLookup() {
-    (*lookup_callback_)(*io_message_, *message_, *renderer_, this, done_);
+    (*lookup_callback_)(*io_message_, message_, respbuf_, this);
 }
 }
 
 
 void
 void
-UDPServer::resume() {
+UDPServer::resume(const bool done) {
+    done_ = done;
     io_.post(*this);
     io_.post(*this);
 }
 }
 
 
 UDPQuery::UDPQuery(io_service& io_service, const IOMessage& io_message,
 UDPQuery::UDPQuery(io_service& io_service, const IOMessage& io_message,
                    const Question& q, const ip::address& addr,
                    const Question& q, const ip::address& addr,
-                   MessageRenderer& renderer, BasicServer* caller) :
-    question_(q),
-    data_((char*) renderer.getData()), datalen_(renderer.getLength()),
-    msgbuf_(512), caller_(caller)
+                   OutputBufferPtr buffer, IOServer* server) :
+    question_(q), msgbuf_(512), buffer_(buffer), server_(server->clone())
 {
 {
     udp proto = addr.is_v4() ? udp::v4() : udp::v6();
     udp proto = addr.is_v4() ? udp::v4() : udp::v6();
     socket_.reset(new udp::socket(io_service, proto));
     socket_.reset(new udp::socket(io_service, proto));
-    server_ = udp::endpoint(addr, 53);
+    remote_ = udp::endpoint(addr, 53);
 }
 }
 
 
 void
 void
@@ -194,13 +192,16 @@ UDPQuery::operator()(error_code ec, size_t length) {
 
 
         CORO_YIELD socket_->async_send_to(buffer(msgbuf_.getData(),
         CORO_YIELD socket_->async_send_to(buffer(msgbuf_.getData(),
                                                  msgbuf_.getLength()),
                                                  msgbuf_.getLength()),
-                                           server_, *this);
+                                           remote_, *this);
 
 
-        CORO_YIELD socket_->async_receive_from(buffer(data_, datalen_),
-                                               server_, *this);
+        data_.reset(new char[MAX_LENGTH]);
+        CORO_YIELD socket_->async_receive_from(buffer(data_.get(), MAX_LENGTH),
+                                               remote_, *this);
+
+        buffer_->writeData(data_.get(), length);
+        server_->resume(true);
     }
     }
 
 
-    caller_->resume();
 }
 }
 
 
 }
 }

+ 12 - 0
src/lib/dns/buffer.h

@@ -25,6 +25,8 @@
 
 
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 
 
+#include <boost/shared_ptr.hpp>
+
 namespace isc {
 namespace isc {
 namespace dns {
 namespace dns {
 
 
@@ -412,6 +414,16 @@ public:
 private:
 private:
     std::vector<uint8_t> data_;
     std::vector<uint8_t> data_;
 };
 };
+
+/// \brief Pointer-like types pointing to \c InputBuffer or \c OutputBuffer
+///
+/// These types are expected to be used as an argument in asynchronous
+/// callback functions.  The internal reference-counting will ensure that
+/// that ongoing state information will not be lost if the object
+/// that originated the asynchronous call falls out of scope.
+typedef boost::shared_ptr<InputBuffer> InputBufferPtr;
+typedef boost::shared_ptr<OutputBuffer> OutputBufferPtr;
+
 }
 }
 }
 }
 #endif  // __BUFFER_H
 #endif  // __BUFFER_H

+ 8 - 0
src/lib/dns/message.h

@@ -798,6 +798,14 @@ private:
     MessageImpl* impl_;
     MessageImpl* impl_;
 };
 };
 
 
+/// \brief Pointer-like type pointing to a \c Message
+///
+/// This type is expected to be used as an argument in asynchronous
+/// callback functions.  The internal reference-counting will ensure that
+/// that ongoing state information will not be lost if the object
+/// that originated the asynchronous call falls out of scope.
+typedef boost::shared_ptr<Message> MessagePtr;
+
 std::ostream& operator<<(std::ostream& os, const Opcode& opcode);
 std::ostream& operator<<(std::ostream& os, const Opcode& opcode);
 std::ostream& operator<<(std::ostream& os, const Rcode& rcode);
 std::ostream& operator<<(std::ostream& os, const Rcode& rcode);
 std::ostream& operator<<(std::ostream& os, const Message& message);
 std::ostream& operator<<(std::ostream& os, const Message& message);

+ 1 - 0
src/lib/dns/messagerenderer.h

@@ -258,6 +258,7 @@ public:
     /// \param name A \c Name object to be written.
     /// \param name A \c Name object to be written.
     /// \param compress A boolean indicating whether to enable name compression.
     /// \param compress A boolean indicating whether to enable name compression.
     void writeName(const Name& name, bool compress = true);
     void writeName(const Name& name, bool compress = true);
+    //@}
 private:
 private:
     struct MessageRendererImpl;
     struct MessageRendererImpl;
     MessageRendererImpl* impl_;
     MessageRendererImpl* impl_;