Browse Source

added support for basic level of EDNS0, and adjusted apps with the latest API.

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

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

@@ -89,7 +89,7 @@ AuthSrv::processMessage() {
     int cc;
 
     if ((cc = recvfrom(s, recvbuf, sizeof(recvbuf), 0, sa, &sa_len)) > 0) {
-        Message msg;
+        Message msg(Message::PARSE);
         InputBuffer buffer(recvbuf, cc);
 
         try {
@@ -105,17 +105,22 @@ AuthSrv::processMessage() {
             return;
         }
 
+        bool dnssec_ok = msg.isDNSSECSupported();
+        uint16_t remote_bufsize = msg.getUDPSize();
+
         QuestionPtr query = *msg.beginQuestion();
 
         msg.makeResponse();
         msg.setHeaderFlag(MessageFlag::AA());
         msg.setRcode(Rcode::NOERROR());
+        msg.setDNSSECSupported(dnssec_ok);
+        msg.setUDPSize(sizeof(recvbuf));
 
         // do the DataSource call here
         Query q = Query(msg, false);
         data_src.doQuery(q);
 
-        OutputBuffer obuffer(4096);
+        OutputBuffer obuffer(remote_bufsize);
         MessageRenderer renderer(obuffer);
         msg.toWire(renderer);
         cout << "sending a response (" <<

+ 2 - 2
src/bin/host/host.cc

@@ -31,7 +31,7 @@ int
 host_lookup(char* name, std::string type)
 {
 
-    Message msg;
+    Message msg(Message::RENDER);
 
     msg.setQid(0); // does this matter?
 
@@ -99,7 +99,7 @@ host_lookup(char* name, std::string type)
     int cc;
     if ((cc = recvfrom(s, recvbuf, sizeof(recvbuf), 0, sa, &sa_len)) > 0) {
         try {
-            Message rmsg;
+            Message rmsg(Message::PARSE);
             InputBuffer ibuffer(recvbuf, cc);
 
             rmsg.fromWire(ibuffer);

+ 2 - 1
src/lib/auth/cpp/datasrc_unittest.cc

@@ -42,7 +42,8 @@ using namespace isc::auth;
 namespace {
 class DataSrcTest : public ::testing::Test {
 protected:
-    DataSrcTest() : obuffer(0), renderer(obuffer) {}
+    DataSrcTest() : obuffer(0), renderer(obuffer), msg(Message::PARSE)
+    {}
     TestDataSrc ds;
     OutputBuffer obuffer;
     MessageRenderer renderer;

+ 154 - 27
src/lib/dns/cpp/message.cc

@@ -31,6 +31,7 @@
 #include "messagerenderer.h"
 #include "name.h"
 #include "question.h"
+#include "rdataclass.h"
 #include "rrclass.h"
 #include "rrtype.h"
 #include "rrttl.h"
@@ -59,7 +60,7 @@ static const flags_t FLAG_AD = 0x0020;
 static const flags_t FLAG_CD = 0x0010;
 
 //
-// EDNS0 related constants
+// EDNS related constants
 //
 static const flags_t EXTFLAG_MASK = 0xffff;
 static const flags_t EXTFLAG_DO = 0x8000;
@@ -189,9 +190,10 @@ sectionCodeToId(const Section& section)
 
 class MessageImpl {
 public:
-    MessageImpl();
+    MessageImpl(Message::Mode mode);
     // Open issues: should we rather have a header in wire-format
     // for efficiency?
+    Message::Mode mode_;
     qid_t qid_;
     Rcode rcode_;
     const Opcode* opcode_;
@@ -202,7 +204,9 @@ public:
     int counts_[SECTION_MAX];   // TODO: revisit this definition
     vector<QuestionPtr> questions_;
     vector<RRsetPtr> rrsets_[SECTION_MAX];
-    RRsetPtr edns_;
+    RRsetPtr remote_edns_;
+    uint16_t remote_udpsize_;
+    RRsetPtr local_edns_;
     uint16_t udpsize_;
 
 #ifdef notyet
@@ -211,13 +215,13 @@ public:
 #endif
 
     void init();
-    void parseQuestion(Message& message, InputBuffer& buffer);
-    void parseSection(Message& messge, const Section& section,
-                      InputBuffer& buffer);
+    int parseQuestion(Message& message, InputBuffer& buffer);
+    int parseSection(Message& messge, const Section& section,
+                     InputBuffer& buffer);
 };
 
-MessageImpl::MessageImpl() :
-    rcode_(Rcode::NOERROR())
+MessageImpl::MessageImpl(Message::Mode mode) :
+    mode_(mode), rcode_(Rcode::NOERROR())
 {
     init();
 }
@@ -230,7 +234,9 @@ MessageImpl::init()
     rcode_ = Rcode::NOERROR();  // XXX
     opcode_ = NULL;
     dnssec_ok_ = false;
-    edns_ = RRsetPtr();
+    remote_edns_ = RRsetPtr();
+    remote_udpsize_ = Message::DEFAULT_MAX_UDPSIZE;
+    local_edns_ = RRsetPtr();
     udpsize_ = Message::DEFAULT_MAX_UDPSIZE;
 
     for (int i = 0; i < SECTION_MAX; i++) {
@@ -243,8 +249,8 @@ MessageImpl::init()
     rrsets_[sectionCodeToId(Section::ADDITIONAL())].clear();
 }
 
-Message::Message() :
-    impl_(new MessageImpl())
+Message::Message(Mode mode) :
+    impl_(new MessageImpl(mode))
 {
 }
 
@@ -277,12 +283,36 @@ Message::isDNSSECSupported() const
     return (impl_->dnssec_ok_);
 }
 
+void
+Message::setDNSSECSupported(bool on)
+{
+    if (impl_->mode_ != Message::RENDER) {
+        dns_throw(InvalidMessageOperation,
+                  "setDNSSECSupported performed in non-render mode");
+    }
+    impl_->dnssec_ok_ = on;
+}
+
 uint16_t
 Message::getUDPSize() const
 {
     return (impl_->udpsize_);
 }
 
+void
+Message::setUDPSize(uint16_t size)
+{
+    if (impl_->mode_ != Message::RENDER) {
+        dns_throw(InvalidMessageOperation,
+                  "setUDPSize performed in non-render mode");
+    }
+    if (size < DEFAULT_MAX_UDPSIZE) {
+        dns_throw(InvalidMessageUDPSize,
+                  "Specified UDP message size is too small");
+    }
+    impl_->udpsize_ = size;
+}
+
 qid_t
 Message::getQid() const
 {
@@ -370,6 +400,44 @@ struct RenderSection
 };
 }
 
+namespace {
+bool
+addEDNS(MessageImpl* mimpl, MessageRenderer& renderer)
+{
+    bool is_query = ((mimpl->flags_ & MessageFlag::QR().getBit()) == 0); 
+
+    // If this is a reply and the request didn't have EDNS, we shouldn't add it.
+    if (mimpl->remote_edns_ == NULL && !is_query) {
+        return (false);
+    }
+
+    // For queries, we add EDNS only when necessary:
+    // Local UDP size is not the default value, or
+    // DNSSEC DO bit is to be set, or
+    // Extended Rcode is to be specified.
+    if (is_query && mimpl->udpsize_ == Message::DEFAULT_MAX_UDPSIZE &&
+        !mimpl->dnssec_ok_ &&
+        mimpl->rcode_.getCode() < 0x10) {
+        return (false);
+    }
+
+    // Render EDNS OPT RR
+    uint32_t extrcode_flags = ((mimpl->rcode_.getCode() & 0xff0) << 24);
+    if (mimpl->dnssec_ok_) {
+        extrcode_flags |= 0x8000; // set DO bit
+    }
+    mimpl->local_edns_ = RRsetPtr(new RRset(Name::ROOT_NAME(),
+                                            RRClass(mimpl->udpsize_),
+                                            RRType::OPT(),
+                                            RRTTL(extrcode_flags)));
+    // We don't support any options in this simple implementation
+    mimpl->local_edns_->addRdata(ConstRdataPtr(new generic::OPT()));
+    mimpl->local_edns_->toWire(renderer);
+
+    return (true);
+}
+}
+
 void
 Message::toWire(MessageRenderer& renderer)
 {
@@ -378,7 +446,7 @@ Message::toWire(MessageRenderer& renderer)
     // reserve room for the header
     renderer.skip(HEADERLEN);
 
-    uint16_t qrcount =
+    uint16_t qdcount =
         for_each(impl_->questions_.begin(), impl_->questions_.end(),
                  RenderSection<QuestionPtr>(renderer)).getTotalCount();
 
@@ -396,6 +464,12 @@ Message::toWire(MessageRenderer& renderer)
                  impl_->rrsets_[sectionCodeToId(Section::ADDITIONAL())].end(),
                  RenderSection<RRsetPtr>(renderer)).getTotalCount();
 
+    // Added EDNS OPT RR if necessary (we want to avoid hardcoding specialized
+    // logic, see the parser case)
+    if (addEDNS(this->impl_, renderer)) {
+        ++arcount;
+    }
+
     // TBD: EDNS, TSIG, etc.
 
     // fill in the header
@@ -408,7 +482,7 @@ Message::toWire(MessageRenderer& renderer)
     renderer.writeUint16At(codes_and_flags, header_pos);
     header_pos += sizeof(uint16_t);
     // XXX: should avoid repeated pattern (TODO)
-    renderer.writeUint16At(qrcount, header_pos);
+    renderer.writeUint16At(qdcount, header_pos);
     header_pos += sizeof(uint16_t);
     renderer.writeUint16At(ancount, header_pos);
     header_pos += sizeof(uint16_t);
@@ -435,15 +509,21 @@ Message::fromWire(InputBuffer& buffer)
     impl_->counts_[Section::AUTHORITY().getCode()] = buffer.readUint16();
     impl_->counts_[Section::ADDITIONAL().getCode()] = buffer.readUint16();
 
-    impl_->parseQuestion(*this, buffer);
-    impl_->parseSection(*this, Section::ANSWER(), buffer);
-    impl_->parseSection(*this, Section::AUTHORITY(), buffer);
-    impl_->parseSection(*this, Section::ADDITIONAL(), buffer);
+    impl_->counts_[Section::QUESTION().getCode()] =
+        impl_->parseQuestion(*this, buffer);
+    impl_->counts_[Section::ANSWER().getCode()] =
+        impl_->parseSection(*this, Section::ANSWER(), buffer);
+    impl_->counts_[Section::AUTHORITY().getCode()] =
+        impl_->parseSection(*this, Section::AUTHORITY(), buffer);
+    impl_->counts_[Section::ADDITIONAL().getCode()] =
+        impl_->parseSection(*this, Section::ADDITIONAL(), buffer);
 }
 
-void
+int
 MessageImpl::parseQuestion(Message& message, InputBuffer& buffer)
 {
+    unsigned int added = 0;
+
     for (unsigned int count = 0;
          count < counts_[Section::QUESTION().getCode()];
          count++) {
@@ -460,8 +540,11 @@ MessageImpl::parseQuestion(Message& message, InputBuffer& buffer)
         // algorithm that requires the question section contain exactly one
         // RR.
 
-        questions_.push_back(QuestionPtr(new Question(name, rrclass, rrtype))); 
+        questions_.push_back(QuestionPtr(new Question(name, rrclass, rrtype)));
+        ++added;
     }
+
+    return (added);
 }
 
 namespace {
@@ -480,10 +563,12 @@ struct MatchRR : public unary_function<RRsetPtr, bool> {
 };
 }
 
-void
+int
 MessageImpl::parseSection(Message& message, const Section& section,
                           InputBuffer& buffer)
 {
+    unsigned int added = 0;
+
     for (unsigned int count = 0; count < counts_[section.getCode()]; count++) {
         Name name(buffer);
 
@@ -507,17 +592,17 @@ MessageImpl::parseSection(Message& message, const Section& section,
                 dns_throw(DNSMessageFORMERR,
                           "EDNS OPT RR found in an invalid section");
             }
-            if (edns_ != NULL) {
+            if (remote_edns_ != NULL) {
                 dns_throw(DNSMessageFORMERR, "multiple EDNS OPT RR found");
             }
             if (((ttl.getValue() & EDNSVERSION_MASK) >> 16) >
+                Message::EDNS_SUPPORTED_VERSION) {
                 // XXX: we should probably not reject the message yet, because
                 // it's better to let the requestor know the responder-side
                 // highest version as indicated in Section 4.6 of RFC2671.
                 // This is probably because why BIND 9 does the version check
                 // in the client code.
                 // This is a TODO item.  Right now we simply reject it.
-                Message::EDNS0_SUPPORTED_VERSION) {
                 dns_throw(DNSMessageBADVERS, "unsupported EDNS version");
             }
             if (name != Name::ROOT_NAME()) {
@@ -525,8 +610,8 @@ MessageImpl::parseSection(Message& message, const Section& section,
                           "invalid owner name for EDNS OPT RR");
             }
 
-            edns_ = RRsetPtr(new RRset(name, rrclass, rrtype, ttl));
-            edns_->addRdata(rdata);
+            remote_edns_ = RRsetPtr(new RRset(name, rrclass, rrtype, ttl));
+            remote_edns_->addRdata(rdata);
 
             dnssec_ok_ = (((ttl.getValue() & EXTFLAG_MASK) & EXTFLAG_DO) != 0);
             if (rrclass.getCode() > Message::DEFAULT_MAX_UDPSIZE) {
@@ -549,7 +634,10 @@ MessageImpl::parseSection(Message& message, const Section& section,
             rrset->addRdata(rdata);
             rrsets_[sectionCodeToId(section)].push_back(rrset);
         }
+        ++added;
     }
+
+    return (added);
 }
 
 namespace {
@@ -609,9 +697,36 @@ Message::toText() const
         lexical_cast<string>(impl_->counts_[Section::ANSWER().getCode()]);
     s += ", AUTHORITY: " +
         lexical_cast<string>(impl_->counts_[Section::AUTHORITY().getCode()]);
-    s += ", ADDITIONAL: " +
-        lexical_cast<string>(impl_->counts_[Section::ADDITIONAL().getCode()])
-        + "\n";
+
+    unsigned int arcount = impl_->counts_[Section::ADDITIONAL().getCode()];
+    RRsetPtr edns_rrset;
+    if (!getHeaderFlag(MessageFlag::QR()) && impl_->remote_edns_ != NULL) {
+        edns_rrset = impl_->remote_edns_;
+        ++arcount;
+    } else if (getHeaderFlag(MessageFlag::QR()) && impl_->local_edns_ != NULL) {
+        edns_rrset = impl_->local_edns_;
+        ++arcount;
+    }
+    s += ", ADDITIONAL: " + lexical_cast<string>(arcount) + "\n";
+
+    if (edns_rrset != NULL) {
+        s += "\n;; OPT PSEUDOSECTION:\n";
+        s += "; EDNS: version: ";
+        s += lexical_cast<string>(
+            (edns_rrset->getTTL().getValue() & 0x00ff0000) >> 16);
+        s += ", flags:";
+        if ((edns_rrset->getTTL().getValue() & 0x8000) != 0) {
+            s += " do";
+        }
+        uint32_t mbz = edns_rrset->getTTL().getValue() & ~0x8000 & 0xffff;
+        if (mbz != 0) {
+            s += "; MBZ: " + lexical_cast<string>(mbz) + ", udp: ";
+        } else {
+            s += "; udp: " +
+                lexical_cast<string>(edns_rrset->getClass().getCode());
+        }
+        s += "\n";
+    }
 
     if (!impl_->questions_.empty()) {
         s += "\n;; " +
@@ -654,9 +769,21 @@ Message::clear()
 void
 Message::makeResponse()
 {
+    if (impl_->mode_ != Message::PARSE) {
+        dns_throw(InvalidMessageOperation,
+                  "makeResponse() is performed in non-parse mode");
+    }
+
+    impl_->dnssec_ok_ = false;
+    impl_->remote_udpsize_ = impl_->udpsize_;
+    impl_->local_edns_ = RRsetPtr();
+    impl_->udpsize_ = DEFAULT_MAX_UDPSIZE;
+
     impl_->flags_ &= MESSAGE_REPLYPRESERVE;
     setHeaderFlag(MessageFlag::QR());
 
+    impl_->mode_ = Message::RENDER;
+
     impl_->rrsets_[sectionCodeToId(Section::ANSWER())].clear();
     impl_->counts_[Section::ANSWER().getCode()] = 0;
     impl_->rrsets_[sectionCodeToId(Section::AUTHORITY())].clear();

+ 23 - 5
src/lib/dns/cpp/message.h

@@ -63,6 +63,18 @@ public:
         isc::Exception(file, line, what) {}
 };
 
+class InvalidMessageOperation : public Exception {
+public:
+    InvalidMessageOperation(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+class InvalidMessageUDPSize : public Exception {
+public:
+    InvalidMessageUDPSize(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
 typedef uint8_t rcode_t; // we actually only need 4 bits of it
 typedef uint8_t opcode_t; // we actually only need 4 bits of it
 typedef uint16_t qid_t;
@@ -503,7 +515,12 @@ typedef SectionIterator<RRsetPtr> RRsetIterator;
 
 class Message {
 public:
-    Message();
+    enum Mode {
+        PARSE = 0,
+        RENDER = 1
+    };
+public:
+    Message(Mode mode);
     ~Message();
 private:
     Message(const Message& source);
@@ -513,8 +530,9 @@ public:
     void setHeaderFlag(const MessageFlag& flag);
     void clearHeaderFlag(const MessageFlag& flag);
     bool isDNSSECSupported() const;
-    void setDNSSECSupported(bool on); // not yet
+    void setDNSSECSupported(bool on);
     uint16_t getUDPSize() const;
+    void setUDPSize(uint16_t size);
     qid_t getQid() const;
     void setQid(qid_t qid);
     const Rcode& getRcode() const;
@@ -566,11 +584,11 @@ public:
     /// \brief The default maximum size of UDP DNS messages that don't cause
     /// truncation.
     ///
-    /// With EDNS0 the maximum size can be increases per message.
+    /// With EDNS the maximum size can be increases per message.
     static const uint16_t DEFAULT_MAX_UDPSIZE = 512;
 
-    /// \brief The highest EDNS0 version this implementation supports.
-    static const uint8_t EDNS0_SUPPORTED_VERSION = 0;
+    /// \brief The highest EDNS version this implementation supports.
+    static const uint8_t EDNS_SUPPORTED_VERSION = 0;
     //@}
 
 private:

+ 113 - 62
src/lib/dns/cpp/tests/message_unittest.cc

@@ -39,11 +39,16 @@ const uint16_t Message::DEFAULT_MAX_UDPSIZE;
 namespace {
 class MessageTest : public ::testing::Test {
 protected:
-    MessageTest() : obuffer(0), renderer(obuffer) {}
+    MessageTest() : obuffer(0), renderer(obuffer),
+                    message_parse(Message::PARSE),
+                    message_render(Message::RENDER)
+    {}
+    
     static Question factoryFromFile(const char* datafile);
     OutputBuffer obuffer;
     MessageRenderer renderer;
-    Message message;
+    Message message_parse;
+    Message message_render;
     static void factoryFromFile(Message& message, const char* datafile);
 };
 
@@ -80,24 +85,24 @@ TEST_F(MessageTest, RcodeToText)
 
 TEST_F(MessageTest, fromWire)
 {
-    factoryFromFile(message, "testdata/message_fromWire1");
-    EXPECT_EQ(0x1035, message.getQid());
-    EXPECT_EQ(Opcode::QUERY(), message.getOpcode());
-    EXPECT_EQ(Rcode::NOERROR(), message.getRcode());
-    EXPECT_TRUE(message.getHeaderFlag(MessageFlag::QR()));
-    EXPECT_TRUE(message.getHeaderFlag(MessageFlag::RD()));
-    EXPECT_TRUE(message.getHeaderFlag(MessageFlag::AA()));
-
-    QuestionPtr q = *message.beginQuestion();
+    factoryFromFile(message_parse, "testdata/message_fromWire1");
+    EXPECT_EQ(0x1035, message_parse.getQid());
+    EXPECT_EQ(Opcode::QUERY(), message_parse.getOpcode());
+    EXPECT_EQ(Rcode::NOERROR(), message_parse.getRcode());
+    EXPECT_TRUE(message_parse.getHeaderFlag(MessageFlag::QR()));
+    EXPECT_TRUE(message_parse.getHeaderFlag(MessageFlag::RD()));
+    EXPECT_TRUE(message_parse.getHeaderFlag(MessageFlag::AA()));
+
+    QuestionPtr q = *message_parse.beginQuestion();
     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()));
+    EXPECT_EQ(1, message_parse.getRRCount(Section::QUESTION()));
+    EXPECT_EQ(2, message_parse.getRRCount(Section::ANSWER()));
+    EXPECT_EQ(0, message_parse.getRRCount(Section::AUTHORITY()));
+    EXPECT_EQ(0, message_parse.getRRCount(Section::ADDITIONAL()));
 
-    RRsetPtr rrset = *message.beginSection(Section::ANSWER());
+    RRsetPtr rrset = *message_parse.beginSection(Section::ANSWER());
     EXPECT_EQ(test_name, rrset->getName());
     EXPECT_EQ(RRType::A(), rrset->getType());
     EXPECT_EQ(RRClass::IN(), rrset->getClass());
@@ -112,98 +117,144 @@ TEST_F(MessageTest, fromWire)
     EXPECT_TRUE(it->isLast());
 }
 
-TEST_F(MessageTest, EDNS0DOBit)
+TEST_F(MessageTest, GetEDNS0DOBit)
 {
     // Without EDNS0, DNSSEC is considered to be unsupported.
-    factoryFromFile(message, "testdata/message_fromWire1");
-    EXPECT_FALSE(message.isDNSSECSupported());
+    factoryFromFile(message_parse, "testdata/message_fromWire1");
+    EXPECT_FALSE(message_parse.isDNSSECSupported());
 
     // If DO bit is on, DNSSEC is considered to be supported.
-    message.clear();
-    factoryFromFile(message, "testdata/message_fromWire2");
-    EXPECT_TRUE(message.isDNSSECSupported());
+    message_parse.clear();
+    factoryFromFile(message_parse, "testdata/message_fromWire2");
+    EXPECT_TRUE(message_parse.isDNSSECSupported());
 
     // If DO bit is off, DNSSEC is considered to be unsupported.
-    message.clear();
-    factoryFromFile(message, "testdata/message_fromWire3");
-    EXPECT_FALSE(message.isDNSSECSupported());
+    message_parse.clear();
+    factoryFromFile(message_parse, "testdata/message_fromWire3");
+    EXPECT_FALSE(message_parse.isDNSSECSupported());
+}
+
+TEST_F(MessageTest, SetEDNS0DOBit)
+{
+    // By default, it's false, and we can enable/disable it.
+    EXPECT_FALSE(message_render.isDNSSECSupported());
+    message_render.setDNSSECSupported(true);
+    EXPECT_TRUE(message_render.isDNSSECSupported());
+    message_render.setDNSSECSupported(false);
+    EXPECT_FALSE(message_render.isDNSSECSupported());
+
+    // A message in the parse mode doesn't allow this flag to be set.
+    EXPECT_THROW(message_parse.setDNSSECSupported(true),
+                 InvalidMessageOperation);
+    // Once converted to the render mode, it works as above
+    message_parse.makeResponse();
+    EXPECT_FALSE(message_parse.isDNSSECSupported());
+    message_parse.setDNSSECSupported(true);
+    EXPECT_TRUE(message_parse.isDNSSECSupported());
+    message_parse.setDNSSECSupported(false);
+    EXPECT_FALSE(message_parse.isDNSSECSupported());
 }
 
-TEST_F(MessageTest, EDNS0UDPSize)
+TEST_F(MessageTest, GetEDNS0UDPSize)
 {
     // Without EDNS0, the default max UDP size is used.
-    factoryFromFile(message, "testdata/message_fromWire1");
-    EXPECT_EQ(Message::DEFAULT_MAX_UDPSIZE, message.getUDPSize());
+    factoryFromFile(message_parse, "testdata/message_fromWire1");
+    EXPECT_EQ(Message::DEFAULT_MAX_UDPSIZE, message_parse.getUDPSize());
 
     // If the size specified in EDNS0 > default max, use it.
-    message.clear();
-    factoryFromFile(message, "testdata/message_fromWire2");
-    EXPECT_EQ(4096, message.getUDPSize());
+    message_parse.clear();
+    factoryFromFile(message_parse, "testdata/message_fromWire2");
+    EXPECT_EQ(4096, message_parse.getUDPSize());
 
     // If the size specified in EDNS0 < default max, keep using the default.
-    message.clear();
-    factoryFromFile(message, "testdata/message_fromWire8");
-    EXPECT_EQ(Message::DEFAULT_MAX_UDPSIZE, message.getUDPSize());
+    message_parse.clear();
+    factoryFromFile(message_parse, "testdata/message_fromWire8");
+    EXPECT_EQ(Message::DEFAULT_MAX_UDPSIZE, message_parse.getUDPSize());
+}
+
+TEST_F(MessageTest, SetEDNS0UDPSize)
+{
+    // The default size if unspecified
+    EXPECT_EQ(Message::DEFAULT_MAX_UDPSIZE, message_render.getUDPSize());
+    // A common buffer size with EDNS, should succeed
+    message_render.setUDPSize(4096);
+    EXPECT_EQ(4096, message_render.getUDPSize());
+    // Unusual large value, but accepted
+    message_render.setUDPSize(0xffff);
+    EXPECT_EQ(0xffff, message_render.getUDPSize());
+    // Too small is value is rejected
+    EXPECT_THROW(message_render.setUDPSize(511), InvalidMessageUDPSize);
+
+    // A message in the parse mode doesn't allow the set operation.
+    EXPECT_THROW(message_parse.setUDPSize(4096), InvalidMessageOperation);
+    // Once converted to the render mode, it works as above.
+    message_parse.makeResponse();
+    message_parse.setUDPSize(4096);
+    EXPECT_EQ(4096, message_parse.getUDPSize());
+    message_parse.setUDPSize(0xffff);
+    EXPECT_EQ(0xffff, message_parse.getUDPSize());
+    EXPECT_THROW(message_parse.setUDPSize(511), InvalidMessageUDPSize);
 }
 
 TEST_F(MessageTest, EDNS0ExtCode)
 {
     // Extended Rcode = BADVERS
-    factoryFromFile(message, "testdata/message_fromWire10");
-    EXPECT_EQ(Rcode::BADVERS(), message.getRcode());
+    factoryFromFile(message_parse, "testdata/message_fromWire10");
+    EXPECT_EQ(Rcode::BADVERS(), message_parse.getRcode());
 
     // Maximum extended Rcode
-    message.clear();
-    factoryFromFile(message, "testdata/message_fromWire11");
-    EXPECT_EQ(0xfff, message.getRcode().getCode());
+    message_parse.clear();
+    factoryFromFile(message_parse, "testdata/message_fromWire11");
+    EXPECT_EQ(0xfff, message_parse.getRcode().getCode());
 }
 
 TEST_F(MessageTest, BadEDNS0)
 {
     // OPT RR in the answer section
-    EXPECT_THROW(factoryFromFile(message, "testdata/message_fromWire4"),
+    EXPECT_THROW(factoryFromFile(message_parse, "testdata/message_fromWire4"),
                  DNSMessageFORMERR);
     // multiple OPT RRs (in the additional section)
-    message.clear();
-    EXPECT_THROW(factoryFromFile(message, "testdata/message_fromWire5"),
+    message_parse.clear();
+    EXPECT_THROW(factoryFromFile(message_parse, "testdata/message_fromWire5"),
                  DNSMessageFORMERR);
     // OPT RR of a non root name
-    message.clear();
-    EXPECT_THROW(factoryFromFile(message, "testdata/message_fromWire6"),
+    message_parse.clear();
+    EXPECT_THROW(factoryFromFile(message_parse, "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"));
+    message_parse.clear();
+    EXPECT_NO_THROW(factoryFromFile(message_parse,
+                                    "testdata/message_fromWire7"));
     // Unsupported Version
-    message.clear();
-    EXPECT_THROW(factoryFromFile(message, "testdata/message_fromWire9"),
+    message_parse.clear();
+    EXPECT_THROW(factoryFromFile(message_parse, "testdata/message_fromWire9"),
                  DNSMessageBADVERS);
 }
 
 TEST_F(MessageTest, toWire)
 {
-    message.setQid(0x1035);
-    message.setOpcode(Opcode::QUERY());
-    message.setRcode(Rcode::NOERROR());
-    message.setHeaderFlag(MessageFlag::QR());
-    message.setHeaderFlag(MessageFlag::RD());
-    message.setHeaderFlag(MessageFlag::AA());
-    message.addQuestion(Question(Name("test.example.com"), RRClass::IN(),
-                                     RRType::A()));
+    message_render.setQid(0x1035);
+    message_render.setOpcode(Opcode::QUERY());
+    message_render.setRcode(Rcode::NOERROR());
+    message_render.setHeaderFlag(MessageFlag::QR());
+    message_render.setHeaderFlag(MessageFlag::RD());
+    message_render.setHeaderFlag(MessageFlag::AA());
+    message_render.addQuestion(Question(Name("test.example.com"), RRClass::IN(),
+                                        RRType::A()));
     RRsetPtr rrset = RRsetPtr(new RRset(Name("test.example.com"), RRClass::IN(),
                                         RRType::A(), RRTTL(3600)));
     rrset->addRdata(in::A("192.0.2.1"));
     rrset->addRdata(in::A("192.0.2.2"));
-    message.addRRset(Section::ANSWER(), rrset);
+    message_render.addRRset(Section::ANSWER(), rrset);
 
-    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()));
+    EXPECT_EQ(1, message_render.getRRCount(Section::QUESTION()));
+    EXPECT_EQ(2, message_render.getRRCount(Section::ANSWER()));
+    EXPECT_EQ(0, message_render.getRRCount(Section::AUTHORITY()));
+    EXPECT_EQ(0, message_render.getRRCount(Section::ADDITIONAL()));
 
-    message.toWire(renderer);
+    message_render.toWire(renderer);
     vector<unsigned char> data;
     UnitTestUtil::readWireData("testdata/message_toWire1", data);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(),