Browse Source

Merge branch 'experiments/resolver'

Conflicts:
	src/bin/resolver/resolver.cc
Jelte Jansen 14 years ago
parent
commit
7b84de4c0e

+ 7 - 3
src/bin/auth/auth_srv.cc

@@ -159,9 +159,13 @@ AuthSrvImpl::~AuthSrvImpl() {
 class MessageLookup : public DNSLookup {
 public:
     MessageLookup(AuthSrv* srv) : server_(srv) {}
-    virtual void operator()(const IOMessage& io_message, MessagePtr message,
-                            OutputBufferPtr buffer, DNSServer* server) const
+    virtual void operator()(const IOMessage& io_message,
+                            MessagePtr message,
+                            MessagePtr answer_message,
+                            OutputBufferPtr buffer,
+                            DNSServer* server) const
     {
+        (void) answer_message;
         server_->processMessage(io_message, message, buffer, server);
     }
 private:
@@ -180,7 +184,7 @@ class MessageAnswer : public DNSAnswer {
 public:
     MessageAnswer(AuthSrv*) {}
     virtual void operator()(const IOMessage&, MessagePtr,
-                            OutputBufferPtr) const
+                            MessagePtr, OutputBufferPtr) const
     {}
 };
 

+ 4 - 2
src/bin/auth/tests/auth_srv_unittest.cc

@@ -138,9 +138,10 @@ TEST_F(AuthSrvTest, builtInQueryViaDNSServer) {
     createRequestPacket(request_message, IPPROTO_UDP);
 
     (*server.getDNSLookupProvider())(*io_message, parse_message,
+                                     response_message,
                                      response_obuffer, &dnsserv);
     (*server.getDNSAnswerProvider())(*io_message, parse_message,
-                                     response_obuffer);
+                                     response_message, response_obuffer);
 
     createBuiltinVersionResponse(default_qid, response_data);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
@@ -153,9 +154,10 @@ TEST_F(AuthSrvTest, builtInQueryViaDNSServer) {
 TEST_F(AuthSrvTest, iqueryViaDNSServer) {
     createDataFromFile("iquery_fromWire.wire");
     (*server.getDNSLookupProvider())(*io_message, parse_message,
+                                     response_message,
                                      response_obuffer, &dnsserv);
     (*server.getDNSAnswerProvider())(*io_message, parse_message,
-                                     response_obuffer);
+                                     response_message, response_obuffer);
 
     UnitTestUtil::readWireData("iquery_response_fromWire.wire",
                                response_data);

+ 77 - 107
src/bin/resolver/resolver.cc

@@ -95,20 +95,20 @@ public:
     {
         upstream_ = upstream;
         if (dnss) {
-            if (upstream_.empty()) {
-                dlog("Asked to do full recursive, but not implemented yet. "
-                    "I'll do nothing.",true);
-            } else {
+            if (!upstream_.empty()) {
                 dlog("Setting forward addresses:");
                 BOOST_FOREACH(const addr_t& address, upstream) {
                     dlog(" " + address.first + ":" +
                         boost::lexical_cast<string>(address.second));
                 }
+            } else {
+                dlog("No forward addresses, running in recursive mode");
             }
         }
     }
 
-    void processNormalQuery(const Question& question, MessagePtr message,
+    void processNormalQuery(const Question& question,
+                            MessagePtr answer_message,
                             OutputBufferPtr buffer,
                             DNSServer* server);
 
@@ -149,20 +149,6 @@ public:
     MessagePtr message_;
 };
 
-class SectionInserter {
-public:
-    SectionInserter(MessagePtr message, const Message::Section sect) :
-        message_(message), section_(sect)
-    {}
-    void operator()(const RRsetPtr rrset) {
-        //dlog("Adding RRSet to message section " +
-        //    boost::lexical_cast<string>(section_));
-        message_->addRRset(section_, rrset, true);
-    }
-    MessagePtr message_;
-    const Message::Section section_;
-};
-
 void
 makeErrorMessage(MessagePtr message, OutputBufferPtr buffer,
                  const Rcode& rcode)
@@ -210,10 +196,14 @@ public:
     MessageLookup(Resolver* srv) : server_(srv) {}
 
     // \brief Handle the DNS Lookup
-    virtual void operator()(const IOMessage& io_message, MessagePtr message,
-                            OutputBufferPtr buffer, DNSServer* server) const
+    virtual void operator()(const IOMessage& io_message,
+                            MessagePtr query_message,
+                            MessagePtr answer_message,
+                            OutputBufferPtr buffer,
+                            DNSServer* server) const
     {
-        server_->processMessage(io_message, message, buffer, server);
+        server_->processMessage(io_message, query_message,
+                                answer_message, buffer, server);
     }
 private:
     Resolver* server_;
@@ -226,76 +216,62 @@ private:
 class MessageAnswer : public DNSAnswer {
 public:
     virtual void operator()(const IOMessage& io_message,
-                            MessagePtr message,
+                            MessagePtr query_message,
+                            MessagePtr answer_message,
                             OutputBufferPtr buffer) const
     {
-        const qid_t qid = message->getQid();
-        const bool rd = message->getHeaderFlag(Message::HEADERFLAG_RD);
-        const bool cd = message->getHeaderFlag(Message::HEADERFLAG_CD);
-        const Opcode& opcode = message->getOpcode();
-        const Rcode& rcode = message->getRcode();
-        vector<QuestionPtr> questions;
-        questions.assign(message->beginQuestion(), message->endQuestion());
+        const qid_t qid = query_message->getQid();
+        const bool rd = query_message->getHeaderFlag(Message::HEADERFLAG_RD);
+        const bool cd = query_message->getHeaderFlag(Message::HEADERFLAG_CD);
+        const Opcode& opcode = query_message->getOpcode();
 
-        message->clear(Message::RENDER);
-        message->setQid(qid);
-        message->setOpcode(opcode);
-        message->setRcode(rcode);
+        // Fill in the final details of the answer message
+        answer_message->setQid(qid);
+        answer_message->setOpcode(opcode);
 
-        message->setHeaderFlag(Message::HEADERFLAG_QR);
-        message->setHeaderFlag(Message::HEADERFLAG_RA);
+        answer_message->setHeaderFlag(Message::HEADERFLAG_QR);
+        answer_message->setHeaderFlag(Message::HEADERFLAG_RA);
         if (rd) {
-            message->setHeaderFlag(Message::HEADERFLAG_RD);
+            answer_message->setHeaderFlag(Message::HEADERFLAG_RD);
         }
         if (cd) {
-            message->setHeaderFlag(Message::HEADERFLAG_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);
-                message->setRcode(incoming.getRcode());
-                for_each(incoming.beginSection(Message::SECTION_ANSWER),
-                         incoming.endSection(Message::SECTION_ANSWER),
-                         SectionInserter(message, Message::SECTION_ANSWER));
-                for_each(incoming.beginSection(Message::SECTION_AUTHORITY),
-                         incoming.endSection(Message::SECTION_AUTHORITY),
-                         SectionInserter(message, Message::SECTION_AUTHORITY));
-                for_each(incoming.beginSection(Message::SECTION_ADDITIONAL),
-                         incoming.endSection(Message::SECTION_ADDITIONAL),
-                         SectionInserter(message, Message::SECTION_ADDITIONAL));
-            } catch (const Exception& ex) {
-                // Incoming message couldn't be read, we just SERVFAIL
-                message->setRcode(Rcode::SERVFAIL());
-            }
+            answer_message->setHeaderFlag(Message::HEADERFLAG_CD);
         }
 
+        vector<QuestionPtr> questions;
+        questions.assign(query_message->beginQuestion(), query_message->endQuestion());
+        for_each(questions.begin(), questions.end(), QuestionInserter(answer_message));
+        
         // Now we can clear the buffer and render the new message into it
         buffer->clear();
         MessageRenderer renderer(*buffer);
 
+        ConstEDNSPtr edns(query_message->getEDNS());
+        const bool dnssec_ok = edns && edns->getDNSSECAwareness();
+        if (edns) {
+            EDNSPtr edns_response(new EDNS());
+            edns_response->setDNSSECAwareness(dnssec_ok);
+
+            // TODO: We should make our own edns bufsize length configurable
+            edns_response->setUDPSize(Message::DEFAULT_MAX_EDNS0_UDPSIZE);
+            answer_message->setEDNS(edns_response);
+        }
+        
         if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
-            ConstEDNSPtr edns(message->getEDNS());
-            renderer.setLengthLimit(edns ? edns->getUDPSize() :
-                Message::DEFAULT_MAX_UDPSIZE);
+            if (edns) {
+                renderer.setLengthLimit(edns->getUDPSize());
+            } else {
+                renderer.setLengthLimit(Message::DEFAULT_MAX_UDPSIZE);
+            }
         } else {
             renderer.setLengthLimit(65535);
         }
 
-        message->toWire(renderer);
+        answer_message->toWire(renderer);
 
         dlog(string("sending a response (") +
             boost::lexical_cast<string>(renderer.getLength()) + "bytes): \n" +
-            message->toText());
+            answer_message->toText());
     }
 };
 
@@ -345,18 +321,21 @@ Resolver::getConfigSession() const {
 }
 
 void
-Resolver::processMessage(const IOMessage& io_message, MessagePtr message,
-                        OutputBufferPtr buffer, DNSServer* server)
+Resolver::processMessage(const IOMessage& io_message,
+                         MessagePtr query_message,
+                         MessagePtr answer_message,
+                         OutputBufferPtr buffer,
+                         DNSServer* server)
 {
     dlog("Got a DNS message");
     InputBuffer request_buffer(io_message.getData(), io_message.getDataSize());
     // First, check the header part.  If we fail even for the base header,
     // just drop the message.
     try {
-        message->parseHeader(request_buffer);
+        query_message->parseHeader(request_buffer);
 
         // Ignore all responses.
-        if (message->getHeaderFlag(Message::HEADERFLAG_QR)) {
+        if (query_message->getHeaderFlag(Message::HEADERFLAG_QR)) {
             dlog("Received unexpected response, ignoring");
             server->resume(false);
             return;
@@ -369,52 +348,53 @@ Resolver::processMessage(const IOMessage& io_message, MessagePtr message,
 
     // Parse the message.  On failure, return an appropriate error.
     try {
-        message->fromWire(request_buffer);
+        query_message->fromWire(request_buffer);
     } catch (const DNSProtocolError& error) {
         dlog(string("returning ") + error.getRcode().toText() + ": " + 
             error.what());
-        makeErrorMessage(message, buffer, error.getRcode());
+        makeErrorMessage(query_message, buffer, error.getRcode());
         server->resume(true);
         return;
     } catch (const Exception& ex) {
         dlog(string("returning SERVFAIL: ") + ex.what());
-        makeErrorMessage(message, buffer, Rcode::SERVFAIL());
+        makeErrorMessage(query_message, buffer, Rcode::SERVFAIL());
         server->resume(true);
         return;
     } // other exceptions will be handled at a higher layer.
 
-    dlog("received a message:\n" + message->toText());
+    dlog("received a message:\n" + query_message->toText());
 
     // Perform further protocol-level validation.
     bool sendAnswer = true;
-    if (message->getOpcode() == Opcode::NOTIFY()) {
-        makeErrorMessage(message, buffer, Rcode::NOTAUTH());
+    if (query_message->getOpcode() == Opcode::NOTIFY()) {
+        makeErrorMessage(query_message, buffer, Rcode::NOTAUTH());
         dlog("Notify arrived, but we are not authoritative");
-    } else if (message->getOpcode() != Opcode::QUERY()) {
-        dlog("Unsupported opcode (got: " + message->getOpcode().toText() +
+    } else if (query_message->getOpcode() != Opcode::QUERY()) {
+        dlog("Unsupported opcode (got: " + query_message->getOpcode().toText() +
             ", expected: " + Opcode::QUERY().toText());
-        makeErrorMessage(message, buffer, Rcode::NOTIMP());
-    } else if (message->getRRCount(Message::SECTION_QUESTION) != 1) {
+        makeErrorMessage(query_message, buffer, Rcode::NOTIMP());
+    } else if (query_message->getRRCount(Message::SECTION_QUESTION) != 1) {
         dlog("The query contained " +
-            boost::lexical_cast<string>(message->getRRCount(
+            boost::lexical_cast<string>(query_message->getRRCount(
             Message::SECTION_QUESTION) + " questions, exactly one expected"));
-        makeErrorMessage(message, buffer, Rcode::FORMERR());
+        makeErrorMessage(query_message, buffer, Rcode::FORMERR());
     } else {
-        ConstQuestionPtr question = *message->beginQuestion();
+        ConstQuestionPtr question = *query_message->beginQuestion();
         const RRType &qtype = question->getType();
         if (qtype == RRType::AXFR()) {
             if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
-                makeErrorMessage(message, buffer, Rcode::FORMERR());
+                makeErrorMessage(query_message, buffer, Rcode::FORMERR());
             } else {
-                makeErrorMessage(message, buffer, Rcode::NOTIMP());
+                makeErrorMessage(query_message, buffer, Rcode::NOTIMP());
             }
         } else if (qtype == RRType::IXFR()) {
-            makeErrorMessage(message, buffer, Rcode::NOTIMP());
+            makeErrorMessage(query_message, buffer, Rcode::NOTIMP());
         } else {
             // The RecursiveQuery object will post the "resume" event to the
             // DNSServer when an answer arrives, so we don't have to do it now.
             sendAnswer = false;
-            impl_->processNormalQuery(*question, message, buffer, server);
+            impl_->processNormalQuery(*question, answer_message,
+                                      buffer, server);
         }
     }
 
@@ -424,23 +404,13 @@ Resolver::processMessage(const IOMessage& io_message, MessagePtr message,
 }
 
 void
-ResolverImpl::processNormalQuery(const Question& question, MessagePtr message,
-                                 OutputBufferPtr buffer, DNSServer* server)
+ResolverImpl::processNormalQuery(const Question& question,
+                                 MessagePtr answer_message,
+                                 OutputBufferPtr buffer,
+                                 DNSServer* server)
 {
     dlog("Processing normal query");
-    ConstEDNSPtr edns(message->getEDNS());
-    const bool dnssec_ok = edns && edns->getDNSSECAwareness();
-
-    message->makeResponse();
-    message->setHeaderFlag(Message::HEADERFLAG_RA);
-    message->setRcode(Rcode::NOERROR());
-    if (edns) {
-        EDNSPtr edns_response(new EDNS());
-        edns_response->setDNSSECAwareness(dnssec_ok);
-        edns_response->setUDPSize(ResolverImpl::DEFAULT_LOCAL_UDPSIZE);
-        message->setEDNS(edns_response);
-    }
-    rec_query_->sendQuery(question, buffer, server);
+    rec_query_->sendQuery(question, answer_message, buffer, server);
 }
 
 namespace {

+ 2 - 1
src/bin/resolver/resolver.h

@@ -63,7 +63,8 @@ public:
     /// \param buffer Pointer to an \c OutputBuffer for the resposne
     /// \param server Pointer to the \c DNSServer
     void processMessage(const asiolink::IOMessage& io_message,
-                        isc::dns::MessagePtr message,
+                        isc::dns::MessagePtr query_message,
+                        isc::dns::MessagePtr answer_message,
                         isc::dns::OutputBufferPtr buffer,
                         asiolink::DNSServer* server);
 

+ 6 - 6
src/bin/resolver/tests/resolver_config_unittest.cc

@@ -134,8 +134,8 @@ TEST_F(ResolverConfig, listenAddresses) {
 
     // Try putting there some addresses
     vector<pair<string, uint16_t> > addresses;
-    addresses.push_back(pair<string, uint16_t>("127.0.0.1", 5300));
-    addresses.push_back(pair<string, uint16_t>("::1", 5300));
+    addresses.push_back(pair<string, uint16_t>("127.0.0.1", 5321));
+    addresses.push_back(pair<string, uint16_t>("::1", 5321));
     server.setListenAddresses(addresses);
     EXPECT_EQ(2, server.getListenAddresses().size());
     EXPECT_EQ("::1", server.getListenAddresses()[1].first);
@@ -155,7 +155,7 @@ TEST_F(ResolverConfig, DISABLED_listenAddressConfig) {
         "\"listen_on\": ["
         "   {"
         "       \"address\": \"127.0.0.1\","
-        "       \"port\": 5300"
+        "       \"port\": 5321"
         "   }"
         "]"
         "}"));
@@ -163,7 +163,7 @@ TEST_F(ResolverConfig, DISABLED_listenAddressConfig) {
     EXPECT_EQ(result->toWire(), isc::config::createAnswer()->toWire());
     ASSERT_EQ(1, server.getListenAddresses().size());
     EXPECT_EQ("127.0.0.1", server.getListenAddresses()[0].first);
-    EXPECT_EQ(5300, server.getListenAddresses()[0].second);
+    EXPECT_EQ(5321, server.getListenAddresses()[0].second);
 
     // As this is example address, the machine should not have it on
     // any interface
@@ -174,7 +174,7 @@ TEST_F(ResolverConfig, DISABLED_listenAddressConfig) {
         "\"listen_on\": ["
         "   {"
         "       \"address\": \"192.0.2.0\","
-        "       \"port\": 5300"
+        "       \"port\": 5321"
         "   }"
         "]"
         "}");
@@ -182,7 +182,7 @@ TEST_F(ResolverConfig, DISABLED_listenAddressConfig) {
     EXPECT_FALSE(result->equals(*isc::config::createAnswer()));
     ASSERT_EQ(1, server.getListenAddresses().size());
     EXPECT_EQ("127.0.0.1", server.getListenAddresses()[0].first);
-    EXPECT_EQ(5300, server.getListenAddresses()[0].second);
+    EXPECT_EQ(5321, server.getListenAddresses()[0].second);
 }
 
 TEST_F(ResolverConfig, invalidListenAddresses) {

+ 14 - 3
src/bin/resolver/tests/resolver_unittest.cc

@@ -30,7 +30,10 @@ class ResolverTest : public SrvTestBase{
 protected:
     ResolverTest() : server(){}
     virtual void processMessage() {
-        server.processMessage(*io_message, parse_message, response_obuffer,
+        server.processMessage(*io_message,
+                              parse_message,
+                              response_message,
+                              response_obuffer,
                               &dnsserv);
     }
     Resolver server;
@@ -83,7 +86,11 @@ TEST_F(ResolverTest, AXFRFail) {
                                        RRType::AXFR());
     createRequestPacket(request_message, IPPROTO_TCP);
     // AXFR is not implemented and should always send NOTIMP.
-    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+    server.processMessage(*io_message,
+                          parse_message,
+                          response_message,
+                          response_obuffer,
+                          &dnsserv);
     EXPECT_TRUE(dnsserv.hasAnswer());
     headerCheck(*parse_message, default_qid, Rcode::NOTIMP(), opcode.getCode(),
                 QR_FLAG, 1, 0, 0, 0);
@@ -98,7 +105,11 @@ TEST_F(ResolverTest, notifyFail) {
     request_message.setQid(default_qid);
     request_message.setHeaderFlag(Message::HEADERFLAG_AA);
     createRequestPacket(request_message, IPPROTO_UDP);
-    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+    server.processMessage(*io_message,
+                          parse_message,
+                          response_message,
+                          response_obuffer,
+                          &dnsserv);
     EXPECT_TRUE(dnsserv.hasAnswer());
     headerCheck(*parse_message, default_qid, Rcode::NOTAUTH(),
                 Opcode::NOTIFY().getCode(), QR_FLAG, 0, 0, 0, 0);

+ 177 - 8
src/lib/asiolink/asiolink.cc

@@ -30,6 +30,7 @@
 
 #include <dns/buffer.h>
 #include <dns/message.h>
+#include <dns/rcode.h>
 
 #include <asiolink/asiolink.h>
 #include <asiolink/internal/tcpdns.h>
@@ -37,6 +38,7 @@
 
 #include <log/dummylog.h>
 
+
 using namespace asio;
 using asio::ip::udp;
 using asio::ip::tcp;
@@ -46,8 +48,46 @@ using namespace isc::dns;
 using isc::log::dlog;
 using namespace boost;
 
+// Is this something we can use in libdns++?
+namespace {
+    class SectionInserter {
+    public:
+        SectionInserter(MessagePtr message, const Message::Section sect) :
+            message_(message), section_(sect)
+        {}
+        void operator()(const RRsetPtr rrset) {
+            message_->addRRset(section_, rrset, true);
+        }
+        MessagePtr message_;
+        const Message::Section section_;
+    };
+
+
+    /// \brief Copies the parts relevant for a DNS answer to the
+    /// target message
+    ///
+    /// This adds all the RRsets in the answer, authority and
+    /// additional sections to the target, as well as the response
+    /// code
+    void copyAnswerMessage(const Message& source, MessagePtr target) {
+        target->setRcode(source.getRcode());
+
+        for_each(source.beginSection(Message::SECTION_ANSWER),
+                 source.endSection(Message::SECTION_ANSWER),
+                 SectionInserter(target, Message::SECTION_ANSWER));
+        for_each(source.beginSection(Message::SECTION_AUTHORITY),
+                 source.endSection(Message::SECTION_AUTHORITY),
+                 SectionInserter(target, Message::SECTION_AUTHORITY));
+        for_each(source.beginSection(Message::SECTION_ADDITIONAL),
+                 source.endSection(Message::SECTION_ADDITIONAL),
+                 SectionInserter(target, Message::SECTION_ADDITIONAL));
+    }
+}
+
 namespace asiolink {
 
+typedef pair<string, uint16_t> addr_t;
+
 class IOServiceImpl {
 private:
     IOServiceImpl(const IOService& source);
@@ -296,6 +336,12 @@ private:
 
     // Info for (re)sending the query (the question and destination)
     Question question_;
+
+    // This is where we build and store our final answer
+    MessagePtr answer_message_;
+
+    // currently we use upstream as the current list of NS records
+    // we should differentiate between forwarding and resolving
     shared_ptr<AddressVector> upstream_;
 
     // Buffer to store the result.
@@ -312,9 +358,26 @@ private:
     int timeout_;
     unsigned retries_;
 
+    // normal query state
+
+    // if we change this to running and add a sent, we can do
+    // decoupled timeouts i think
+    bool done;
+
+    // Not using NSAS at this moment, so we keep a list
+    // of 'current' zone servers
+    vector<addr_t> zone_servers_;
+
+    // Update the question that will be sent to the server
+    void setQuestion(const Question& new_question) {
+        question_ = new_question;
+    }
+
     // (re)send the query to the server.
     void send() {
         const int uc = upstream_->size();
+        const int zs = zone_servers_.size();
+        buffer_->clear();
         if (uc > 0) {
             int serverIndex = rand() % uc;
             dlog("Sending upstream query (" + question_.toText() +
@@ -324,34 +387,138 @@ private:
                 upstream_->at(serverIndex).second, buffer_, this,
                 timeout_);
             io_.post(query);
+        } else if (zs > 0) {
+            int serverIndex = rand() % zs;
+            dlog("Sending query to zone server (" + question_.toText() +
+                ") to " + zone_servers_.at(serverIndex).first);
+            UDPQuery query(io_, question_,
+                zone_servers_.at(serverIndex).first,
+                zone_servers_.at(serverIndex).second, buffer_, this,
+                timeout_);
+            io_.post(query);
         } else {
             dlog("Error, no upstream servers to send to.");
         }
     }
+    
+    // This function is called by operator() if there is an actual
+    // answer from a server and we are in recursive mode
+    // depending on the contents, we go on recursing or return
+    //
+    // Note that the footprint may change as this function may
+    // need to append data to the answer we are building later.
+    //
+    // returns true if we are done
+    // returns false if we are not done
+    bool handleRecursiveAnswer(const Message& incoming) {
+        if (incoming.getRRCount(Message::SECTION_ANSWER) > 0) {
+            dlog("Got final result, copying answer.");
+            copyAnswerMessage(incoming, answer_message_);
+            return true;
+        } else {
+            dlog("Got delegation, continuing");
+            // ok we need to do some more processing.
+            // the ns list should contain all nameservers
+            // while the additional may contain addresses for
+            // them.
+            // this needs to tie into NSAS of course
+            // for this very first mockup, hope there is an
+            // address in additional and just use that
+
+            // send query to the addresses in the delegation
+            bool found_ns_address = false;
+            zone_servers_.clear();
+
+            for (RRsetIterator rrsi = incoming.beginSection(Message::SECTION_ADDITIONAL);
+                 rrsi != incoming.endSection(Message::SECTION_ADDITIONAL) && !found_ns_address;
+                 rrsi++) {
+                ConstRRsetPtr rrs = *rrsi;
+                if (rrs->getType() == RRType::A()) {
+                    // found address
+                    RdataIteratorPtr rdi = rrs->getRdataIterator();
+                    // just use the first for now
+                    if (!rdi->isLast()) {
+                        std::string addr_str = rdi->getCurrent().toText();
+                        dlog("[XX] first address found: " + addr_str);
+                        // now we have one address, simply
+                        // resend that exact same query
+                        // to that address and yield, when it
+                        // returns, loop again.
+                        
+                        // should use NSAS
+                        zone_servers_.push_back(addr_t(addr_str, 53));
+                        found_ns_address = true;
+                    }
+                }
+            }
+            if (found_ns_address) {
+                // next resolver round
+                send();
+                return false;
+            } else {
+                dlog("[XX] no ready-made addresses in additional. need nsas.");
+                // this will result in answering with the delegation. oh well
+                copyAnswerMessage(incoming, answer_message_);
+                return true;
+            }
+        }
+    }
+    
+
 public:
     RunningQuery(asio::io_service& io, const Question &question,
-        shared_ptr<AddressVector> upstream,
+        MessagePtr answer_message, shared_ptr<AddressVector> upstream,
         OutputBufferPtr buffer, DNSServer* server, int timeout,
         unsigned retries) :
         io_(io),
         question_(question),
+        answer_message_(answer_message),
         upstream_(upstream),
         buffer_(buffer),
         server_(server->clone()),
         timeout_(timeout),
-        retries_(retries)
+        retries_(retries),
+        zone_servers_()
     {
+        dlog("Started a new RunningQuery");
+        done = false;
+
+        // hardcoded f.root-servers.net now, should use NSAS
+        if (upstream_->empty()) {
+            zone_servers_.push_back(addr_t("192.5.5.241", 53));
+        }
         send();
     }
 
+
     // This function is used as callback from DNSQuery.
     virtual void operator()(UDPQuery::Result result) {
-        if (result == UDPQuery::TIME_OUT && retries_ --) {
-            dlog("Resending query");
+        // XXX is this the place for TCP retry?
+        if (result != UDPQuery::TIME_OUT) {
+            // we got an answer
+            Message incoming(Message::PARSE);
+            InputBuffer ibuf(buffer_->getData(), buffer_->getLength());
+            incoming.fromWire(ibuf);
+
+            if (upstream_->size() == 0 &&
+                incoming.getRcode() == Rcode::NOERROR()) {
+                done = handleRecursiveAnswer(incoming);
+            } else {
+                copyAnswerMessage(incoming, answer_message_);
+                done = true;
+            }
+            
+            if (done) {
+                server_->resume(result == UDPQuery::SUCCESS);
+                delete this;
+            }
+        } else if (retries_--) {
             // We timed out, but we have some retries, so send again
+            dlog("Timeout, resending query");
             send();
         } else {
-            server_->resume(result == UDPQuery::SUCCESS);
+            // out of retries, give up for now
+            server_->resume(false);
             delete this;
         }
     }
@@ -360,7 +527,9 @@ public:
 }
 
 void
-RecursiveQuery::sendQuery(const Question& question, OutputBufferPtr buffer,
+RecursiveQuery::sendQuery(const Question& question,
+                          MessagePtr answer_message,
+                          OutputBufferPtr buffer,
                           DNSServer* server)
 {
     // XXX: eventually we will need to be able to determine whether
@@ -369,8 +538,8 @@ RecursiveQuery::sendQuery(const Question& question, OutputBufferPtr buffer,
     // we're only going to handle UDP.
     asio::io_service& io = dns_service_.get_io_service();
     // It will delete itself when it is done
-    new RunningQuery(io, question, upstream_, buffer, server,
-         timeout_, retries_);
+    new RunningQuery(io, question, answer_message, upstream_, buffer,
+                     server, timeout_, retries_);
 }
 
 class IntervalTimerImpl {

+ 4 - 1
src/lib/asiolink/asiolink.h

@@ -412,10 +412,11 @@ public:
     /// \param DNSServer DNSServer object to use
     virtual void operator()(const IOMessage& io_message,
                             isc::dns::MessagePtr message,
+                            isc::dns::MessagePtr answer_message,
                             isc::dns::OutputBufferPtr buffer,
                             DNSServer* server) const
     {
-        (*self_)(io_message, message, buffer, server);
+        (*self_)(io_message, message, answer_message, buffer, server);
     }
 private:
     DNSLookup* self_;
@@ -465,6 +466,7 @@ public:
     /// \param buffer The result is put here
     virtual void operator()(const IOMessage& io_message,
                             isc::dns::MessagePtr message,
+                            isc::dns::MessagePtr answer_message,
                             isc::dns::OutputBufferPtr buffer) const = 0;
 };
 
@@ -557,6 +559,7 @@ public:
     /// \param buffer An output buffer into which the response can be copied
     /// \param server A pointer to the \c DNSServer object handling the client
     void sendQuery(const isc::dns::Question& question,
+                   isc::dns::MessagePtr answer_message,
                    isc::dns::OutputBufferPtr buffer,
                    DNSServer* server);
 private:

+ 2 - 1
src/lib/asiolink/internal/tcpdns.h

@@ -195,7 +195,8 @@ private:
     // \c IOMessage and \c Message objects to be passed to the
     // DNS lookup and answer providers
     boost::shared_ptr<asiolink::IOMessage> io_message_;
-    isc::dns::MessagePtr message_;
+    isc::dns::MessagePtr query_message_;
+    isc::dns::MessagePtr answer_message_;
 
     // The buffer into which the query packet is written
     boost::shared_array<char>data_;

+ 6 - 1
src/lib/asiolink/internal/udpdns.h

@@ -208,7 +208,12 @@ private:
     // \c IOMessage and \c Message objects to be passed to the
     // DNS lookup and answer providers
     boost::shared_ptr<asiolink::IOMessage> io_message_;
-    isc::dns::MessagePtr message_;
+
+    // The original query as sent by the client
+    isc::dns::MessagePtr query_message_;
+
+    // The response message we are building
+    isc::dns::MessagePtr answer_message_;
 
     // The buffer into which the response is written
     isc::dns::OutputBufferPtr respbuf_;

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

@@ -142,7 +142,8 @@ TCPServer::operator()(error_code ec, size_t length) {
         // Reset or instantiate objects that will be needed by the
         // DNS lookup and the write call.
         respbuf_.reset(new OutputBuffer(0));
-        message_.reset(new Message(Message::PARSE));
+        query_message_.reset(new Message(Message::PARSE));
+        answer_message_.reset(new Message(Message::RENDER));
 
         // Schedule a DNS lookup, and yield.  When the lookup is
         // finished, the coroutine will resume immediately after
@@ -157,7 +158,8 @@ TCPServer::operator()(error_code ec, size_t length) {
 
         // Call the DNS answer provider to render the answer into
         // wire format
-        (*answer_callback_)(*io_message_, message_, respbuf_);
+        (*answer_callback_)(*io_message_, query_message_,
+                            answer_message_, respbuf_);
 
         // Set up the response, beginning with two length bytes.
         lenbuf.writeUint16(respbuf_->getLength());
@@ -176,7 +178,8 @@ TCPServer::operator()(error_code ec, size_t length) {
 /// AsyncLookup<TCPServer> handler.)
 void
 TCPServer::asyncLookup() {
-    (*lookup_callback_)(*io_message_, message_, respbuf_, this);
+    (*lookup_callback_)(*io_message_, query_message_,
+                        answer_message_, respbuf_, this);
 }
 
 /// Post this coroutine on the ASIO service queue so that it will

+ 62 - 5
src/lib/asiolink/tests/asiolink_unittest.cc

@@ -28,6 +28,7 @@
 #include <exceptions/exceptions.h>
 
 #include <dns/tests/unittest_util.h>
+#include <dns/rcode.h>
 
 #include <dns/buffer.h>
 #include <dns/message.h>
@@ -439,6 +440,7 @@ protected:
                             DNSAnswer* answer = NULL) :
             io_(io_service),
             message_(new Message(Message::PARSE)),
+            answer_message_(new Message(Message::RENDER)),
             respbuf_(new OutputBuffer(0)),
             checkin_(checkin), lookup_(lookup), answer_(answer)
         {}
@@ -447,7 +449,8 @@ protected:
                         size_t length = 0)
         {}
 
-        void resume(const bool) { // in our test this shouldn't be called
+        void resume(const bool) {
+          // should never be called in our tests
         }
 
         DNSServer* clone() {
@@ -457,7 +460,8 @@ protected:
 
         inline void asyncLookup() {
             if (lookup_) {
-                (*lookup_)(*io_message_, message_, respbuf_, this);
+                (*lookup_)(*io_message_, message_, answer_message_,
+                           respbuf_, this);
             }
         }
 
@@ -470,6 +474,7 @@ protected:
         // asynchronous lookup calls via the asyncLookup() method
         boost::shared_ptr<asiolink::IOMessage> io_message_;
         isc::dns::MessagePtr message_;
+        isc::dns::MessagePtr answer_message_;
         isc::dns::OutputBufferPtr respbuf_;
 
         // Callback functions provided by the caller
@@ -656,7 +661,7 @@ TEST_F(ASIOLinkTest, recursiveSetupV6) {
 // a routine that can do this with variable address family, address, and
 // port, and with the various callbacks defined in such a way as to ensure
 // full code coverage including error cases.
-TEST_F(ASIOLinkTest, recursiveSend) {
+TEST_F(ASIOLinkTest, forwarderSend) {
     setDNSService(true, false);
 
     // Note: We use the test prot plus one to ensure we aren't binding
@@ -668,7 +673,8 @@ TEST_F(ASIOLinkTest, recursiveSend) {
 
     Question q(Name("example.com"), RRClass::IN(), RRType::TXT());
     OutputBufferPtr buffer(new OutputBuffer(0));
-    rq.sendQuery(q, buffer, &server);
+    MessagePtr answer(new Message(Message::RENDER));
+    rq.sendQuery(q, answer, buffer, &server);
 
     char data[4096];
     size_t size = sizeof(data);
@@ -713,7 +719,8 @@ TEST_F(ASIOLinkTest, recursiveTimeout) {
         10, 2);
     Question question(Name("example.net"), RRClass::IN(), RRType::A());
     OutputBufferPtr buffer(new OutputBuffer(0));
-    query.sendQuery(question, buffer, &server);
+    MessagePtr answer(new Message(Message::RENDER));
+    query.sendQuery(question, answer, buffer, &server);
 
     // Run the test
     io_service_->run();
@@ -743,6 +750,56 @@ TEST_F(ASIOLinkTest, recursiveTimeout) {
     EXPECT_EQ(3, num);
 }
 
+// as mentioned above, we need a more better framework for this,
+// in addition to that, this sends out queries into the world
+// (which we should catch somehow and fake replies for)
+// for the skeleton code, it shouldn't be too much of a problem
+TEST_F(ASIOLinkTest, recursiveSendOk) {
+    setDNSService(true, false);
+    bool done;
+    
+    MockServerStop server(*io_service_, &done);
+    vector<pair<string, uint16_t> > empty_vector;
+    RecursiveQuery rq(*dns_service_, empty_vector, 10000, 0);
+
+    Question q(Name("www.isc.org"), RRClass::IN(), RRType::A());
+    OutputBufferPtr buffer(new OutputBuffer(0));
+    MessagePtr answer(new Message(Message::RENDER));
+    rq.sendQuery(q, answer, buffer, &server);
+    io_service_->run();
+
+    // Check that the answer we got matches the one we wanted
+    EXPECT_EQ(Rcode::NOERROR(), answer->getRcode());
+    ASSERT_EQ(1, answer->getRRCount(Message::SECTION_ANSWER));
+    RRsetPtr a = *answer->beginSection(Message::SECTION_ANSWER);
+    EXPECT_EQ(q.getName(), a->getName());
+    EXPECT_EQ(q.getType(), a->getType());
+    EXPECT_EQ(q.getClass(), a->getClass());
+    EXPECT_EQ(1, a->getRdataCount());
+}
+
+// see comments at previous test
+TEST_F(ASIOLinkTest, recursiveSendNXDOMAIN) {
+    setDNSService(true, false);
+    bool done;
+    
+    MockServerStop server(*io_service_, &done);
+    vector<pair<string, uint16_t> > empty_vector;
+    RecursiveQuery rq(*dns_service_, empty_vector, 10000, 0);
+
+    Question q(Name("wwwdoesnotexist.isc.org"), RRClass::IN(), RRType::A());
+    OutputBufferPtr buffer(new OutputBuffer(0));
+    MessagePtr answer(new Message(Message::RENDER));
+    rq.sendQuery(q, answer, buffer, &server);
+    io_service_->run();
+
+    // Check that the answer we got matches the one we wanted
+    EXPECT_EQ(Rcode::NXDOMAIN(), answer->getRcode());
+    EXPECT_EQ(0, answer->getRRCount(Message::SECTION_ANSWER));
+}
+
+
+
 // This fixture is for testing IntervalTimer. Some callback functors are 
 // registered as callback function of the timer to test if they are called
 // or not.

+ 9 - 3
src/lib/asiolink/udpdns.cc

@@ -130,13 +130,16 @@ UDPServer::operator()(error_code ec, size_t length) {
         // Instantiate objects that will be needed by the
         // asynchronous DNS lookup and/or by the send call.
         respbuf_.reset(new OutputBuffer(0));
-        message_.reset(new Message(Message::PARSE));
+        query_message_.reset(new Message(Message::PARSE));
+        answer_message_.reset(new Message(Message::RENDER));
 
         // Schedule a DNS lookup, and yield.  When the lookup is
         // finished, the coroutine will resume immediately after
         // this point.
         CORO_YIELD io_.post(AsyncLookup<UDPServer>(*this));
 
+        dlog("[XX] got an answer");
+
         // The 'done_' flag indicates whether we have an answer
         // to send back.  If not, exit the coroutine permanently.
         if (!done_) {
@@ -145,7 +148,8 @@ UDPServer::operator()(error_code ec, size_t length) {
 
         // Call the DNS answer provider to render the answer into
         // wire format
-        (*answer_callback_)(*io_message_, message_, respbuf_);
+        (*answer_callback_)(*io_message_, query_message_,
+                            answer_message_, respbuf_);
 
         // Begin an asynchronous send, and then yield.  When the
         // send completes, we will resume immediately after this point
@@ -161,7 +165,8 @@ UDPServer::operator()(error_code ec, size_t length) {
 /// AsyncLookup<UDPServer> handler.)
 void
 UDPServer::asyncLookup() {
-    (*lookup_callback_)(*io_message_, message_, respbuf_, this);
+    (*lookup_callback_)(*io_message_, query_message_, answer_message_,
+                        respbuf_, this);
 }
 
 /// Post this coroutine on the ASIO service queue so that it will
@@ -251,6 +256,7 @@ UDPQuery::operator()(error_code ec, size_t length) {
                 data_->remote.address().to_string());
         }
 
+
         // If we timeout, we stop, which will shutdown everything and
         // cancel all other attempts to run inside the coroutine
         if (data_->timeout != -1) {

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

@@ -509,6 +509,9 @@ public:
     ///
     /// With EDNS the maximum size can be increased per message.
     static const uint16_t DEFAULT_MAX_UDPSIZE = 512;
+
+    /// \brief The default maximum size of UDP DNS messages we can handle
+    static const uint16_t DEFAULT_MAX_EDNS0_UDPSIZE = 4096;
     //@}
 
 private:

+ 1 - 0
src/lib/testutils/srv_test.h

@@ -89,6 +89,7 @@ protected:
     MockSession notify_session;
     MockServer dnsserv;
     isc::dns::Message request_message;
+    isc::dns::MessagePtr response_message;
     isc::dns::MessagePtr parse_message;
     const isc::dns::qid_t default_qid;
     const isc::dns::Opcode opcode;