d2_update_message.cc 8.26 KB
Newer Older
1
// Copyright (C) 2013-2015,2017 Internet Systems Consortium, Inc. ("ISC")
2
//
3 4 5
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6

7
#include <d2/d2_update_message.h>
8 9 10 11 12 13 14 15 16 17
#include <dns/messagerenderer.h>
#include <dns/name.h>
#include <dns/opcode.h>
#include <dns/question.h>

namespace isc {
namespace d2 {

using namespace isc::dns;

18 19 20
D2UpdateMessage::D2UpdateMessage(const Direction direction)
    : message_(direction == INBOUND ?
               dns::Message::PARSE : dns::Message::RENDER) {
21 22
    // If this object is to create an outgoing message, we have to
    // set the proper Opcode field and QR flag here.
23
    if (direction == OUTBOUND) {
24
        message_.setOpcode(Opcode(Opcode::UPDATE_CODE));
25
        message_.setHeaderFlag(dns::Message::HEADERFLAG_QR, false);
26
        message_.setRcode(Rcode::NOERROR());
27
    }
28 29 30 31 32 33 34 35 36
}

D2UpdateMessage::QRFlag
D2UpdateMessage::getQRFlag() const {
    return (message_.getHeaderFlag(dns::Message::HEADERFLAG_QR) ?
            RESPONSE : REQUEST);
}

uint16_t
37
D2UpdateMessage::getId() const {
38 39 40 41
    return (message_.getQid());
}

void
42 43
D2UpdateMessage::setId(const uint16_t id) {
    message_.setQid(id);
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
}


const dns::Rcode&
D2UpdateMessage::getRcode() const {
    return (message_.getRcode());
}

void
D2UpdateMessage::setRcode(const dns::Rcode& rcode) {
    message_.setRcode(rcode);
}

unsigned int
D2UpdateMessage::getRRCount(const UpdateMsgSection section) const {
    return (message_.getRRCount(ddnsToDnsSection(section)));
}

62 63 64 65 66 67 68 69 70 71
const dns::RRsetIterator
D2UpdateMessage::beginSection(const UpdateMsgSection section) const {
    return (message_.beginSection(ddnsToDnsSection(section)));
}

const dns::RRsetIterator
D2UpdateMessage::endSection(const UpdateMsgSection section) const {
    return (message_.endSection(ddnsToDnsSection(section)));
}

72 73
void
D2UpdateMessage::setZone(const Name& zone, const RRClass& rrclass) {
74 75 76
    // The Zone data is kept in the underlying Question class. If there
    // is a record stored there already, we need to remove it, because
    // we may have at most one Zone record in the DNS Update message.
77 78 79
    if (message_.getRRCount(dns::Message::SECTION_QUESTION) > 0) {
        message_.clearSection(dns::Message::SECTION_QUESTION);
    }
80
    // Add the new record...
81 82
    Question question(zone, rrclass, RRType::SOA());
    message_.addQuestion(question);
83
    // ... and update the local class member holding the D2Zone object.
84 85 86 87 88 89
    zone_.reset(new D2Zone(question.getName(), question.getClass()));
}

D2ZonePtr
D2UpdateMessage::getZone() const {
    return (zone_);
90 91 92 93 94
}

void
D2UpdateMessage::addRRset(const UpdateMsgSection section,
                          const dns::RRsetPtr& rrset) {
95 96 97 98
    if (section == SECTION_ZONE) {
        isc_throw(isc::BadValue, "unable to add RRset to the Zone section"
                  " of the DNS Update message, use setZone instead");
    }
99 100 101 102
    message_.addRRset(ddnsToDnsSection(section), rrset);
}

void
103 104
D2UpdateMessage::toWire(AbstractMessageRenderer& renderer,
                        TSIGContext* const tsig_context) {
105 106 107 108 109 110 111 112 113
    // We are preparing the wire format of the message, meaning
    // that this message will be sent as a request to the DNS.
    // Therefore, we expect that this message is a REQUEST.
    if (getQRFlag() != REQUEST) {
        isc_throw(InvalidQRFlag, "QR flag must be cleared for the outgoing"
                  " DNS Update message");
    }
    // According to RFC2136, the ZONE section may contain exactly one
    // record.
114 115 116 117
    if (getRRCount(SECTION_ZONE) != 1) {
        isc_throw(InvalidZoneSection, "Zone section of the DNS Update message"
                  " must comprise exactly one record (RFC2136, section 2.3)");
    }
118
    message_.toWire(renderer, tsig_context);
119 120 121
}

void
122 123
D2UpdateMessage::fromWire(const void* received_data, size_t bytes_received,
                          dns::TSIGContext* const tsig_context) {
124 125 126 127
    // First, use the underlying dns::Message implementation to get the
    // contents of the DNS response message. Note that it may or may
    // not be the message that we are interested in, but needs to be
    // parsed so as we can check its ID, Opcode etc.
128 129 130
    isc::util::InputBuffer received_data_buffer(received_data, bytes_received);
    message_.fromWire(received_data_buffer);

131
    // If tsig_context is not NULL, then we need to verify the message.
132 133 134 135 136 137 138 139 140
    if (tsig_context) {
        TSIGError error = tsig_context->verify(message_.getTSIGRecord(),
                                               received_data, bytes_received);
        if (error != TSIGError::NOERROR()) {
            isc_throw(TSIGVerifyError, "TSIG verification failed: "
                      << error.toText());
        }
    }

141 142 143 144 145
    // This class exposes the getZone() function. This function will return
    // pointer to the D2Zone object if non-empty Zone section exists in the
    // received message. It will return NULL pointer if it doesn't exist.
    // The pointer is held in the D2UpdateMessage class member. We need to
    // update this pointer every time we parse the message.
146
    if (getRRCount(D2UpdateMessage::SECTION_ZONE) > 0) {
147 148
        // There is a Zone section in the received message. Replace
        // Zone pointer with the new value.
149
        QuestionPtr question = *message_.beginQuestion();
150 151 152
        // If the Zone counter is greater than 0 (which we have checked)
        // there must be a valid Question pointer stored in the message_
        // object. If there isn't, it is a programming error.
153 154 155 156
        assert(question);
        zone_.reset(new D2Zone(question->getName(), question->getClass()));

    } else {
157
        // Zone section doesn't hold any pointers, so set the pointer to NULL.
158 159 160
        zone_.reset();

    }
161 162 163 164 165 166
    // Check that the content of the received message is sane.
    // One of the basic checks to do is to verify that we have
    // received the DNS update message. If not, it can be dropped
    // or an error message can be printed. Other than that, we
    // will check that there is at most one Zone record and QR flag
    // is set.
167
    validateResponse();
168 169 170 171
}

dns::Message::Section
D2UpdateMessage::ddnsToDnsSection(const UpdateMsgSection section) {
172 173 174
    /// The following switch maps the enumerator values from the
    /// DNS Update message to the corresponding enumerator values
    /// representing fields of the DNS message.
175 176 177
    switch(section) {
    case SECTION_ZONE :
        return (dns::Message::SECTION_QUESTION);
178

179 180
    case SECTION_PREREQUISITE:
        return (dns::Message::SECTION_ANSWER);
181

182 183
    case SECTION_UPDATE:
        return (dns::Message::SECTION_AUTHORITY);
184

185 186
    case SECTION_ADDITIONAL:
        return (dns::Message::SECTION_ADDITIONAL);
187

188 189 190
    default:
        ;
    }
191 192
    isc_throw(dns::InvalidMessageSection,
              "unknown message section " << section);
193 194
}

195
void
196
D2UpdateMessage::validateResponse() const {
197 198 199 200 201 202
    // Verify that we are dealing with the DNS Update message. According to
    // RFC 2136, section 3.8 server will copy the Opcode from the query.
    // If we are dealing with a different type of message, we may simply
    // stop further processing, because it is likely that the message was
    // directed to someone else.
    if (message_.getOpcode() != Opcode::UPDATE()) {
203 204 205
        isc_throw(NotUpdateMessage, "received message is not a DDNS update,"
                  << " received message code is "
                  << message_.getOpcode().getCode());
206 207 208 209
    }
    // Received message should have QR flag set, which indicates that it is
    // a RESPONSE.
    if (getQRFlag() == REQUEST) {
210 211 212
        isc_throw(InvalidQRFlag, "received message should have QR flag set,"
                  " to indicate that it is a RESPONSE message; the QR"
                  << " flag in received message is unset");
213
    }
214 215 216 217
    // DNS server may copy a Zone record from the query message. Since query
    // must comprise exactly one Zone record (RFC 2136, section 2.3), the
    // response message may contain 1 record at most. It may also contain no
    // records if a server chooses not to copy Zone section.
218
    if (getRRCount(SECTION_ZONE) > 1) {
219 220 221
        isc_throw(InvalidZoneSection, "received message contains "
                  << getRRCount(SECTION_ZONE) << " Zone records,"
                  << " it should contain at most 1 record");
222 223 224
    }
}

225 226 227
} // namespace d2
} // namespace isc