Browse Source

[trac999] apply query ACL for incoming queries

JINMEI Tatuya 14 years ago
parent
commit
5aabfb971d

+ 36 - 13
src/bin/resolver/resolver.cc

@@ -155,6 +155,14 @@ public:
                             OutputBufferPtr buffer,
                             DNSServer* server);
 
+    const Resolver::ClientACL& getQueryACL() const {
+        return (*query_acl_);
+    }
+
+    void setQueryACL(shared_ptr<const Resolver::ClientACL> new_acl) {
+        query_acl_ = new_acl;
+    }
+
     /// Currently non-configurable, but will be.
     static const uint16_t DEFAULT_LOCAL_UDPSIZE = 4096;
 
@@ -177,11 +185,10 @@ public:
     /// Number of retries after timeout
     unsigned retries_;
 
+private:
     /// TBD
     shared_ptr<const Resolver::ClientACL> query_acl_;
 
-private:
-
     /// Object to handle upstream queries
     RecursiveQuery* rec_query_;
 };
@@ -453,25 +460,44 @@ Resolver::processMessage(const IOMessage& io_message,
                          buffer, Rcode::NOTAUTH());
         // Notify arrived, but we are not authoritative.
         LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS, RESOLVER_NFYNOTAUTH);
-
     } else if (query_message->getOpcode() != Opcode::QUERY()) {
-
         // Unsupported opcode.
         LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS, RESOLVER_OPCODEUNS)
                   .arg(query_message->getOpcode());
         makeErrorMessage(query_message, answer_message,
                          buffer, Rcode::NOTIMP());
-
     } else if (query_message->getRRCount(Message::SECTION_QUESTION) != 1) {
-
         // Not one question
         LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS, RESOLVER_NOTONEQUES)
                   .arg(query_message->getRRCount(Message::SECTION_QUESTION));
         makeErrorMessage(query_message, answer_message,
                          buffer, Rcode::FORMERR());
     } else {
-        ConstQuestionPtr question = *query_message->beginQuestion();
-        const RRType &qtype = question->getType();
+        const ConstQuestionPtr question = *query_message->beginQuestion();
+        const RRType qtype = question->getType();
+
+        Client client(io_message);
+        const BasicAction query_action(impl_->getQueryACL().execute(client));
+        if (query_action == REJECT) {
+            LOG_INFO(resolver_logger, RESOLVER_QUERYREJECTED)
+                .arg(question->getName()).arg(qtype).arg(question->getClass())
+                .arg(client);
+            makeErrorMessage(query_message, answer_message, buffer,
+                             Rcode::REFUSED());
+            // the following should be refactored
+            server->resume(true);
+            return;
+        } else if (query_action == DROP) {
+            LOG_INFO(resolver_logger, RESOLVER_QUERYDROPPED)
+                .arg(question->getName()).arg(qtype).arg(question->getClass())
+                .arg(client);
+            server->resume(false);
+            return;
+        }
+        LOG_DEBUG(resolver_logger, RESOLVER_DBG_IO, RESOLVER_QUERYACCEPTED)
+            .arg(question->getName()).arg(qtype).arg(question->getClass())
+            .arg(client);
+
         if (qtype == RRType::AXFR()) {
             if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
 
@@ -481,7 +507,6 @@ Resolver::processMessage(const IOMessage& io_message,
                 makeErrorMessage(query_message, answer_message,
                                  buffer, Rcode::FORMERR());
             } else {
-
                 // ... or over TCP for that matter
                 LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS,
                           RESOLVER_AXFRTCP);
@@ -489,12 +514,10 @@ Resolver::processMessage(const IOMessage& io_message,
                                  buffer, Rcode::NOTIMP());
             }
         } else if (qtype == RRType::IXFR()) {
-
             // Can't process IXFR request
             LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS, RESOLVER_IXFR);
             makeErrorMessage(query_message, answer_message,
                              buffer, Rcode::NOTIMP());
-
         } else if (question->getClass() != RRClass::IN()) {
 
             // Non-IN message received, refuse it.
@@ -754,11 +777,11 @@ Resolver::getListenAddresses() const {
 
 const Resolver::ClientACL&
 Resolver::getQueryACL() const {
-    return (*impl_->query_acl_);
+    return (impl_->getQueryACL());
 }
 
 void
 Resolver::setQueryACL(shared_ptr<const ClientACL> new_acl) {
     LOG_INFO(resolver_logger, RESOLVER_SETQUERYACL);
-    impl_->query_acl_ = new_acl;
+    impl_->setQueryACL(new_acl);
 }

+ 9 - 0
src/bin/resolver/resolverdef.mes

@@ -195,3 +195,12 @@ query and is ignoring it.
 % SETQUERYACL   query ACL is configured
 A debug message that appears when a new query ACL is configured for the
 resolver.
+
+%QUERYREJECTED   query rejected: '%1/%2/%3' from %4
+TBD
+
+%QUERYDROPPED    query dropped: '%1/%2/%3' from %4
+TBD
+
+%QUERYACCEPTED   query accepted: '%1/%2/%3' from %4
+TBD

+ 50 - 1
src/bin/resolver/tests/resolver_unittest.cc

@@ -12,14 +12,19 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <string>
+
 #include <dns/name.h>
 
+#include <cc/data.h>
 #include <resolver/resolver.h>
 #include <dns/tests/unittest_util.h>
 #include <testutils/dnsmessage_test.h>
 #include <testutils/srv_test.h>
 
+using namespace std;
 using namespace isc::dns;
+using namespace isc::data;
 using namespace isc::testutils;
 using isc::UnitTestUtil;
 
@@ -28,7 +33,17 @@ const char* const TEST_PORT = "53535";
 
 class ResolverTest : public SrvTestBase{
 protected:
-    ResolverTest() : server(){}
+    ResolverTest() : server() {
+        // By default queries from the "default remote address" will be
+        // rejected, so we'll need to add an explicit ACL entry to allow that.
+        server.setConfigured();
+        server.updateConfig(Element::fromJSON(
+                                "{ \"query_acl\": "
+                                "  [ {\"action\": \"ACCEPT\","
+                                "     \"from\": \"" +
+                                string(DEFAULT_REMOTE_ADDRESS) +
+                                "\"} ] }"));
+    }
     virtual void processMessage() {
         server.processMessage(*io_message,
                               parse_message,
@@ -136,4 +151,38 @@ TEST_F(ResolverTest, notifyFail) {
                 Opcode::NOTIFY().getCode(), QR_FLAG, 0, 0, 0, 0);
 }
 
+TEST_F(ResolverTest, queryACL) {
+    // The "ACCEPT" cases are covered in other tests.  Here we explicitly
+    // test "REJECT" and "DROP" cases.
+
+    // Clear the existing ACL, reverting to the "default reject" rule.
+
+    // AXFR over UDP.  This would otherwise result in FORMERR.
+    server.updateConfig(Element::fromJSON("{ \"query_acl\": [] }"));
+    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
+                                       Name("example.com"), RRClass::IN(),
+                                       RRType::AXFR());
+    createRequestPacket(request_message, IPPROTO_UDP);
+    server.processMessage(*io_message, parse_message, response_message,
+                          response_obuffer, &dnsserv);
+    EXPECT_TRUE(dnsserv.hasAnswer());
+    headerCheck(*parse_message, default_qid, Rcode::REFUSED(),
+                Opcode::QUERY().getCode(), QR_FLAG, 1, 0, 0, 0);
+
+    // Same query, but with an explicit "DROP" ACL entry.  There should be
+    // no response.
+    parse_message->clear(Message::PARSE);
+    response_message->clear(Message::RENDER);
+    response_obuffer->clear();
+    server.updateConfig(Element::fromJSON("{ \"query_acl\": "
+                                          "  [ {\"action\": \"DROP\","
+                                          "     \"from\": \"" +
+                                          string(DEFAULT_REMOTE_ADDRESS) +
+                                          "\"} ] }"));
+    server.processMessage(*io_message, parse_message, response_message,
+                          response_obuffer, &dnsserv);
+    EXPECT_FALSE(dnsserv.hasAnswer());
+}
+
+
 }

+ 2 - 1
src/bin/resolver/tests/run_unittests.cc

@@ -23,7 +23,8 @@ main(int argc, char* argv[]) {
     ::testing::InitGoogleTest(&argc, argv);
     isc::UnitTestUtil::addDataPath(TEST_DATA_DIR);
     isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR);
-    isc::log::initLogger();
+    isc::log::initLogger("resolver_test", isc::log::DEBUG,
+                         isc::log::MAX_DEBUG_LEVEL);
 
     return (isc::util::unittests::run_all());
 }

+ 1 - 1
src/lib/Makefile.am

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