Browse Source

checkpoint: supported EDNS0 DO bit, receive side.

git-svn-id: svn://bind10.isc.org/svn/bind10/trunk@1039 e5f2f494-b856-4b98-b285-d166d9295462
JINMEI Tatuya 15 years ago
parent
commit
6caebd56b2

+ 74 - 10
src/lib/dns/cpp/message.cc

@@ -55,6 +55,8 @@ static const flags_t FLAG_RA = 0x0080;
 static const flags_t FLAG_AD = 0x0020;
 static const flags_t FLAG_CD = 0x0010;
 
+static const flags_t EXTFLAG_DO = 0x8000;
+
 static const unsigned int OPCODE_MASK = 0x7800;
 static const unsigned int OPCODE_SHIFT = 11;
 static const unsigned int RCODE_MASK = 0x000f;
@@ -151,7 +153,18 @@ Rcode::toText() const
     return (rcodetext[code_]);
 }
 
-struct MessageImpl {
+namespace {
+inline unsigned int
+sectionCodeToId(const Section& section)
+{
+    unsigned int code = section.getCode();
+    assert(code > 0);
+    return (section.getCode() - 1);
+}
+}
+
+class MessageImpl {
+public:
     MessageImpl();
     // Open issues: should we rather have a header in wire-format
     // for efficiency?
@@ -159,39 +172,51 @@ struct MessageImpl {
     const Rcode* rcode_;
     const Opcode* opcode_;
     flags_t flags_;
+    bool dnssec_ok_;
 
     static const unsigned int SECTION_MAX = 4; // TODO: revisit this design
     int counts_[SECTION_MAX];   // TODO: revisit this definition
     vector<QuestionPtr> questions_;
     vector<RRsetPtr> rrsets_[SECTION_MAX];
+    RRsetPtr edns_;
 
 #ifdef notyet
     // tsig/sig0: TODO
-    EDNS* edns_;
     RRsetsSorter* sorter_;
 #endif
 
+    void init();
     void parseQuestion(Message& message, InputBuffer& buffer);
     void parseSection(Message& messge, const Section& section,
                       InputBuffer& buffer);
 };
 
 MessageImpl::MessageImpl() :
-    qid_(0), rcode_(NULL), opcode_(NULL), flags_(0)
+    qid_(0), rcode_(NULL), opcode_(NULL), flags_(0), dnssec_ok_(false)
 {
     for (int i = 0; i < SECTION_MAX; i++) {
         counts_[i] = 0;
     }
 }
 
-namespace {
-inline unsigned int
-sectionCodeToId(const Section& section)
+void
+MessageImpl::init()
 {
-    unsigned int code = section.getCode();
-    assert(code > 0);
-    return (section.getCode() - 1);
-}
+    flags_ = 0;
+    qid_ = 0;
+    rcode_ = NULL;
+    opcode_ = NULL;
+    dnssec_ok_ = false;
+    edns_ = RRsetPtr();
+
+    for (int i = 0; i < SECTION_MAX; i++) {
+        counts_[i] = 0;
+    }
+
+    questions_.clear();
+    rrsets_[sectionCodeToId(Section::ANSWER())].clear();
+    rrsets_[sectionCodeToId(Section::AUTHORITY())].clear();
+    rrsets_[sectionCodeToId(Section::ADDITIONAL())].clear();
 }
 
 Message::Message() :
@@ -222,6 +247,12 @@ Message::clearHeaderFlag(const MessageFlag& flag)
     impl_->flags_ &= ~flag.getBit();
 }
 
+bool
+Message::isDNSSECSupported() const
+{
+    return (impl_->dnssec_ok_);
+}
+
 qid_t
 Message::getQid() const
 {
@@ -437,6 +468,30 @@ MessageImpl::parseSection(Message& messge, const Section& section,
         size_t rdlen = buffer.readUint16();
         RdataPtr rdata = createRdata(rrtype, rrclass, buffer, rdlen);
 
+        // XXX: we wanted to avoid hardcoding type-specific logic here,
+        // but this would be the fastest way for a proof-of-concept
+        // implementation.  We'll revisit this part later.
+        if (rrtype == RRType::OPT()) {
+            if (section != Section::ADDITIONAL()) {
+                dns_throw(DNSMessageFORMERR,
+                          "EDNS OPT RR found in an invalid section");
+            }
+            if (edns_ != NULL) {
+                dns_throw(DNSMessageFORMERR, "multiple EDNS OPT RR found");
+            }
+            if (name != Name::ROOT_NAME()) {
+                dns_throw(DNSMessageFORMERR,
+                          "invalid owner name for  EDNS OPT RR");
+            }
+
+            edns_ = RRsetPtr(new RRset(name, rrclass, rrtype, ttl));
+            edns_->addRdata(rdata);
+
+            dnssec_ok_ = (((ttl.getValue() & 0xffff) & EXTFLAG_DO) != 0);
+
+            continue;
+        }
+
         vector<RRsetPtr>::iterator it =
             find_if(rrsets_[sectionCodeToId(section)].begin(),
                     rrsets_[sectionCodeToId(section)].end(),
@@ -546,14 +601,23 @@ Message::toText() const
 }
 
 void
+Message::clear()
+{
+    impl_->init();
+}
+
+void
 Message::makeResponse()
 {
     impl_->flags_ &= MESSAGE_REPLYPRESERVE;
     setHeaderFlag(MessageFlag::QR());
 
     impl_->rrsets_[sectionCodeToId(Section::ANSWER())].clear();
+    impl_->counts_[Section::ANSWER().getCode()] = 0;
     impl_->rrsets_[sectionCodeToId(Section::AUTHORITY())].clear();
+    impl_->counts_[Section::AUTHORITY().getCode()] = 0;
     impl_->rrsets_[sectionCodeToId(Section::ADDITIONAL())].clear();
+    impl_->counts_[Section::ADDITIONAL().getCode()] = 0;
 }
 
 ///

+ 25 - 27
src/lib/dns/cpp/message.h

@@ -20,8 +20,6 @@
 #include <iterator>
 #include <string>
 
-#include <boost/shared_ptr.hpp>
-
 #include <exceptions/exceptions.h>
 #include "question.h"
 #include "rrset.h"
@@ -32,6 +30,21 @@ namespace dns {
 ///
 /// \brief A standard DNS module exception ...[TBD]
 ///
+class DNSProtocolError : public Exception {
+public:
+    DNSProtocolError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+class DNSMessageFORMERR : public DNSProtocolError {
+public:
+    DNSMessageFORMERR(const char* file, size_t line, const char* what) :
+        DNSProtocolError(file, line, what) {}
+};
+
+///
+/// \brief A standard DNS module exception ...[TBD]
+///
 class MessageTooShort : public Exception {
 public:
     MessageTooShort(const char* file, size_t line, const char* what) :
@@ -403,6 +416,8 @@ public:
     unsigned int getCode() const { return (code_); }
     bool operator==(const Section& other) const
         { return (code_ == other.code_); }
+    bool operator!=(const Section& other) const
+        { return (code_ != other.code_); }
 
     static const Section& QUESTION();
     static const Section& ANSWER();
@@ -474,11 +489,13 @@ public:
     ~Message();
 private:
     Message(const Message& source);
-    void operator=(const Message& source);
+    Message& operator=(const Message& source);
 public:
     bool getHeaderFlag(const MessageFlag& flag) const;
     void setHeaderFlag(const MessageFlag& flag);
     void clearHeaderFlag(const MessageFlag& flag);
+    bool isDNSSECSupported() const;
+    void setDNSSECSupported(bool on);
     qid_t getQid() const;
     void setQid(qid_t qid);
     const Rcode& getRcode() const;
@@ -509,45 +526,26 @@ public:
     //void addRR(const Section& section, const RR& rr);
     //void removeRR(const Section& section, const RR& rr);
 
+    void clear();
+
     // prepare for making a response from a request.  This will clear the
     // DNS header except those fields that should be kept for the response,
     // and clear answer and the following sections.
     // see also dns_message_reply() of BIND9.
     void makeResponse();
 
-    /// Render message
+    /// \brief Render message.
     void toWire(MessageRenderer& renderer);
 
     /// \brief Parse a DNS message.
     void fromWire(InputBuffer& buffer);
 
-public:
-    // public protocol constants
-    static const rcode_t RCODE_NOERROR = 0;
-    static const rcode_t RCODE_FORMERR = 1;
-    static const rcode_t RCODE_SERVFAIL = 2;
-    static const rcode_t RCODE_NXDOMAIN = 3;
-    static const rcode_t RCODE_NOTIMP = 4;
-    static const rcode_t RCODE_REFUSED = 5;
-    static const rcode_t RCODE_YXDOMAIN = 6;
-    static const rcode_t RCODE_YXRRSET = 7;
-    static const rcode_t RCODE_NXRRSET = 8;
-    static const rcode_t RCODE_NOTAUTH = 9;
-    static const rcode_t RCODE_NOTZONE = 10;
-    // ...more to follow
-
-    static const opcode_t OPCODE_QUERY = 0;
-    static const opcode_t OPCODE_IQUERY = 1;
-    static const opcode_t OPCODE_STATUS = 2;
-    static const opcode_t OPCODE_NOTIFY = 4;
-    static const opcode_t OPCODE_UPDATE = 5;
-
 private:
     MessageImpl* impl_;
 };
 
-std::ostream& operator<<(std::ostream& os, const Opcode& rrset);
-std::ostream& operator<<(std::ostream& os, const Rcode& rrset);
+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 Message& message);
 }
 }

+ 15 - 0
src/lib/dns/cpp/name.h

@@ -599,6 +599,14 @@ public:
     static const uint16_t COMPRESS_POINTER_MARK16 = 0xc000;
     //@}
 
+    ///
+    /// \name Well-known name constants
+    ///
+    //@{
+    /// \brief Root name (i.e. ".").
+    static const Name& ROOT_NAME();
+    //@}
+
 private:
     std::string ndata_;
     std::vector<unsigned char> offsets_;
@@ -606,6 +614,13 @@ private:
     unsigned int labelcount_;
 };
 
+inline const Name&
+Name::ROOT_NAME()
+{
+    static Name root_name(".");
+    return (root_name);
+}
+
 ///
 /// \brief Insert the name as a string into stream.
 ///

+ 42 - 2
src/lib/dns/cpp/tests/message_unittest.cc

@@ -44,6 +44,8 @@ protected:
     static void factoryFromFile(Message& message, const char* datafile);
 };
 
+const Name test_name("test.example.com");
+
 void
 MessageTest::factoryFromFile(Message& message, const char* datafile)
 {
@@ -65,12 +67,16 @@ TEST_F(MessageTest, fromWire)
     EXPECT_TRUE(message.getHeaderFlag(MessageFlag::AA()));
 
     QuestionPtr q = *message.beginQuestion();
-    EXPECT_EQ(Name("test.example.com"), q->getName());
+    EXPECT_EQ(test_name, q->getName());
     EXPECT_EQ(RRType::A(), q->getType());
     EXPECT_EQ(RRClass::IN(), q->getClass());
+    EXPECT_EQ(1, message.getRRCount(Section::QUESTION()));
+    EXPECT_EQ(2, message.getRRCount(Section::ANSWER()));
+    EXPECT_EQ(0, message.getRRCount(Section::AUTHORITY()));
+    EXPECT_EQ(0, message.getRRCount(Section::ADDITIONAL()));
 
     RRsetPtr rrset = *message.beginSection(Section::ANSWER());
-    EXPECT_EQ(Name("test.example.com"), rrset->getName());
+    EXPECT_EQ(test_name, rrset->getName());
     EXPECT_EQ(RRType::A(), rrset->getType());
     EXPECT_EQ(RRClass::IN(), rrset->getClass());
     // TTL should be 3600, even though that of the 2nd RR is 7200
@@ -84,6 +90,40 @@ TEST_F(MessageTest, fromWire)
     EXPECT_TRUE(it->isLast());
 }
 
+TEST_F(MessageTest, EDNS0DOBit)
+{
+    factoryFromFile(message, "testdata/message_fromWire1");
+    EXPECT_FALSE(message.isDNSSECSupported());
+
+    message.clear();
+    factoryFromFile(message, "testdata/message_fromWire2");
+    EXPECT_TRUE(message.isDNSSECSupported());
+
+    message.clear();
+    factoryFromFile(message, "testdata/message_fromWire3");
+    EXPECT_FALSE(message.isDNSSECSupported());
+}
+
+TEST_F(MessageTest, BadEDNS0)
+{
+    // OPT RR in the answer section
+    EXPECT_THROW(factoryFromFile(message, "testdata/message_fromWire4"),
+                 DNSMessageFORMERR);
+    // multiple OPT RRs (in the additional section)
+    message.clear();
+    EXPECT_THROW(factoryFromFile(message, "testdata/message_fromWire5"),
+                 DNSMessageFORMERR);
+    // OPT RR of a non root name
+    message.clear();
+    EXPECT_THROW(factoryFromFile(message, "testdata/message_fromWire6"),
+                 DNSMessageFORMERR);
+    // Compressed owner name of OPT RR points to a root name.
+    // Not necessarily bogus, but very unusual and mostly pathological.
+    // We accept it, but is it okay?
+    message.clear();
+    EXPECT_NO_THROW(factoryFromFile(message, "testdata/message_fromWire7"));
+}
+
 TEST_F(MessageTest, toWire)
 {
     message.setQid(0x1035);

+ 5 - 0
src/lib/dns/cpp/tests/name_unittest.cc

@@ -583,6 +583,11 @@ TEST_F(NameTest, gthan)
     EXPECT_FALSE(small_name > large_name);
 }
 
+TEST_F(NameTest, constants)
+{
+    EXPECT_EQ(Name("."), Name::ROOT_NAME());
+}
+
 // test operator<<.  We simply confirm it appends the result of toText().
 TEST_F(NameTest, LeftShiftOperator)
 {

+ 22 - 0
src/lib/dns/cpp/tests/testdata/message_fromWire2

@@ -0,0 +1,22 @@
+#
+# A simple DNS query message with a valid EDNS0 OPT RR
+# ID = 0x1035
+# QR=0 (query), Opcode=0, RD=1 (other fields are 0)
+# QDCOUNT=1, ANCOUNT=0, NSCOUNT=0, ARCOUNT=1
+# Question: test.example.com. IN A
+1035 0100
+0001 0000 0000 0001
+#(4) t  e  s  t (7) e  x  a  m  p  l  e (3) c  o  m  .
+ 04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+0001 0001
+# EDNS0 OPT RR
+# owner name: "."
+00
+# TYPE: OPT (41 = 0x29)
+00 29
+# CLASS (= UDP size): 4096
+1000
+# TTL (extended RCODE and flags): RCODE=0, version=0, flags=DO
+0000 8000
+# RDLEN = 0
+0000

+ 22 - 0
src/lib/dns/cpp/tests/testdata/message_fromWire3

@@ -0,0 +1,22 @@
+#
+# A simple DNS query message with a valid EDNS0 OPT RR, DO bit off
+# ID = 0x1035
+# QR=0 (query), Opcode=0, RD=1 (other fields are 0)
+# QDCOUNT=1, ANCOUNT=0, NSCOUNT=0, ARCOUNT=1
+# Question: test.example.com. IN A
+1035 0100
+0001 0000 0000 0001
+#(4) t  e  s  t (7) e  x  a  m  p  l  e (3) c  o  m  .
+ 04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+0001 0001
+# EDNS0 OPT RR
+# owner name: "."
+00
+# TYPE: OPT (41 = 0x29)
+00 29
+# CLASS (= UDP size): 4096
+1000
+# TTL (extended RCODE and flags): RCODE=0, version=0, flags=0
+0000 0000
+# RDLEN = 0
+0000

+ 23 - 0
src/lib/dns/cpp/tests/testdata/message_fromWire4

@@ -0,0 +1,23 @@
+#
+# A simple DNS query message with a bogus EDNS0 OPT RR (included in the
+# answer section)
+# ID = 0x1035
+# QR=0 (query), Opcode=0, RD=1 (other fields are 0)
+# QDCOUNT=1, ANCOUNT=1, NSCOUNT=0, ARCOUNT=0
+# Question: test.example.com. IN A
+1035 0100
+0001 0001 0000 0000
+#(4) t  e  s  t (7) e  x  a  m  p  l  e (3) c  o  m  .
+ 04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+0001 0001
+# EDNS0 OPT RR
+# owner name: "."
+00
+# TYPE: OPT (41 = 0x29)
+00 29
+# CLASS (= UDP size): 4096
+1000
+# TTL (extended RCODE and flags): RCODE=0, version=0, flags=DO
+0000 8000
+# RDLEN = 0
+0000

+ 33 - 0
src/lib/dns/cpp/tests/testdata/message_fromWire5

@@ -0,0 +1,33 @@
+#
+# A simple DNS query message with multiple EDNS0 OPT RRs (bogus)
+# ID = 0x1035
+# QR=0 (query), Opcode=0, RD=1 (other fields are 0)
+# QDCOUNT=1, ANCOUNT=0, NSCOUNT=0, ARCOUNT=2
+# Question: test.example.com. IN A
+1035 0100
+0001 0000 0000 0002
+#(4) t  e  s  t (7) e  x  a  m  p  l  e (3) c  o  m  .
+ 04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+0001 0001
+# EDNS0 OPT RR (1st)
+# owner name: "."
+00
+# TYPE: OPT (41 = 0x29)
+00 29
+# CLASS (= UDP size): 4096
+1000
+# TTL (extended RCODE and flags): RCODE=0, version=0, flags=DO
+0000 8000
+# RDLEN = 0
+0000
+# EDNS0 OPT RR (2nd)
+# owner name: "."
+00
+# TYPE: OPT (41 = 0x29)
+00 29
+# CLASS (= UDP size): 4096
+1000
+# TTL (extended RCODE and flags): RCODE=0, version=0, flags=DO
+0000 8000
+# RDLEN = 0
+0000

+ 23 - 0
src/lib/dns/cpp/tests/testdata/message_fromWire6

@@ -0,0 +1,23 @@
+#
+# A simple DNS query message with EDNS0 OPT RRs of non root name (bogus)
+# ID = 0x1035
+# QR=0 (query), Opcode=0, RD=1 (other fields are 0)
+# QDCOUNT=1, ANCOUNT=0, NSCOUNT=0, ARCOUNT=1
+1035 0100
+0001 0000 0000 0001
+# Question: test.example.com. IN A
+#(4) t  e  s  t (7) e  x  a  m  p  l  e (3) c  o  m  .
+ 04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+0001 0001
+# EDNS0 OPT RR
+# owner name: "example.com"
+#(7) e  x  a  m  p  l  e (3) c  o  m  .
+ 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+# TYPE: OPT (41 = 0x29)
+00 29
+# CLASS (= UDP size): 4096
+1000
+# TTL (extended RCODE and flags): RCODE=0, version=0, flags=DO
+0000 8000
+# RDLEN = 0
+0000

+ 27 - 0
src/lib/dns/cpp/tests/testdata/message_fromWire7

@@ -0,0 +1,27 @@
+#
+# A simple DNS query message with EDNS0 OPT RRs of compressed owner name
+# pointing to root (is this bogus?)
+# ID = 0x1035
+# QR=0 (query), Opcode=0, RD=1 (other fields are 0)
+# QDCOUNT=1, ANCOUNT=0, NSCOUNT=0, ARCOUNT=1
+#0 1  2 3
+1035 0100
+#4 5  6 7  8 9 10 1
+0001 0000 0000 0001
+# Question: test.example.com. IN A
+# 2  3  4  5  6  7  8  9 20  1  2  3  4  5  6  7  8  9
+#(4) t  e  s  t (7) e  x  a  m  p  l  e (3) c  o  m  .
+ 04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+0001 0001
+# EDNS0 OPT RR
+# owner name: "example.com"
+# pointer = 29 (end of question section)
+ c0 1d
+# TYPE: OPT (41 = 0x29)
+00 29
+# CLASS (= UDP size): 4096
+1000
+# TTL (extended RCODE and flags): RCODE=0, version=0, flags=DO
+0000 8000
+# RDLEN = 0
+0000