summaryrefslogtreecommitdiffstats
path: root/src/bin/d2/d2_update_message.h
blob: ad01a9ce6907ed4c40dc49911e992d68260eb223 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
// Copyright (C) 2013-2015,2017 Internet Systems Consortium, Inc. ("ISC")
//
// 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/.

#ifndef D2_UPDATE_MESSAGE_H
#define D2_UPDATE_MESSAGE_H

#include <d2/d2_zone.h>
#include <dns/message.h>
#include <dns/name.h>
#include <dns/rcode.h>
#include <dns/rrclass.h>
#include <dns/rrset.h>
#include <dns/tsig.h>

#include <map>

namespace isc {
namespace d2 {

/// @brief Exception indicating that Zone section contains invalid content.
///
/// This exception is thrown when ZONE section of the DNS Update message
/// is invalid. According to RFC2136, section 2.3, the zone section is
/// allowed to contain exactly one record. When Request message contains
/// more records or is empty, this exception is thrown.
class InvalidZoneSection : public Exception {
public:
    InvalidZoneSection(const char* file, size_t line, const char* what) :
        isc::Exception(file, line, what) {}
};

/// @brief Exception indicating that QR flag has invalid value.
///
/// This exception is thrown when QR flag has invalid value for
/// the operation performed on the particular message. For instance,
/// the QR flag must be set to indicate that the given message is
/// a RESPONSE when @c D2UpdateMessage::fromWire is performed.
/// The QR flag must be cleared when @c D2UpdateMessage::toWire
/// is executed.
class InvalidQRFlag : public Exception {
public:
    InvalidQRFlag(const char* file, size_t line, const char* what) :
        isc::Exception(file, line, what) {}
};

/// @brief Exception indicating that the parsed message is not DNS Update.
///
/// This exception is thrown when decoding the DNS message which is not
/// a DNS Update.
class NotUpdateMessage : public Exception {
public:
    NotUpdateMessage(const char* file, size_t line, const char* what) :
        isc::Exception(file, line, what) {}
};

/// @brief Exception indicating that a signed, inbound message failed to verify
///
/// This exception is thrown when TSIG verification of a DNS server's response
/// fails.
class TSIGVerifyError : public Exception {
public:
    TSIGVerifyError(const char* file, size_t line, const char* what) :
        isc::Exception(file, line, what) {}
};


class D2UpdateMessage;

/// @brief Pointer to the DNS Update Message.
typedef boost::shared_ptr<D2UpdateMessage> D2UpdateMessagePtr;

/// @brief The @c D2UpdateMessage encapsulates a DNS Update message.
///
/// This class represents the DNS Update message. Functions exposed by this
/// class allow to specify the data sections carried by the message and create
/// an on-wire format of this message. This class is also used to decode
/// messages received from the DNS server in the on-wire format.
///
/// <b>Design choice:</b> A dedicated class has been created to encapsulate
/// DNS Update message because existing @c isc::dns::Message is designed to
/// support regular DNS messages (described in RFC 1035) only. Although DNS
/// Update has the same format, particular sections serve different purposes.
/// In order to avoid rewrite of significant portions of @c isc::dns::Message
/// class, this class is implemented in-terms-of @c isc::dns::Message class
/// to reuse its functionality where possible.
class D2UpdateMessage {
public:

    /// @brief Indicates if the @c D2UpdateMessage object encapsulates Inbound
    /// or Outbound message.
    enum Direction {
        INBOUND,
        OUTBOUND
    };

    /// @brief Indicates whether DNS Update message is a REQUEST or RESPONSE.
    enum QRFlag {
        REQUEST,
        RESPONSE
    };

    /// @brief Identifies sections in the DNS Update Message.
    ///
    /// Each message comprises message Header and may contain the following
    /// sections:
    /// - ZONE
    /// - PREREQUISITE
    /// - UPDATE
    /// - ADDITIONAL
    ///
    /// The enum elements are used by functions such as @c getRRCount (to get
    /// the number of records in a corresponding section) and @c beginSection
    /// and @c endSection (to access data in the corresponding section).
    enum UpdateMsgSection {
        SECTION_ZONE,
        SECTION_PREREQUISITE,
        SECTION_UPDATE,
        SECTION_ADDITIONAL
    };

public:
    /// @brief Constructor used to create an instance of the DNS Update Message
    /// (either outgoing or incoming).
    ///
    /// This constructor is used to create an instance of either incoming or
    /// outgoing DNS Update message. The boolean argument indicates whether it
    /// is incoming (true) or outgoing (false) message. For incoming messages
    /// the @c D2UpdateMessage::fromWire function is used to parse on-wire data.
    /// For outgoing messages, modifier functions should be used to set the
    /// message contents and @c D2UpdateMessage::toWire function to create
    /// on-wire data.
    ///
    /// @param direction indicates if this is an inbound or outbound message.
    D2UpdateMessage(const Direction direction = OUTBOUND);

    ///
    /// @name Copy constructor and assignment operator
    ///
    /// Copy constructor and assignment operator are private because we assume
    /// there will be no need to copy messages on the client side.
    //@{
private:
    D2UpdateMessage(const D2UpdateMessage& source);
    D2UpdateMessage& operator=(const D2UpdateMessage& source);
    //@}

public:

    /// @brief Returns enum value indicating if the message is a
    /// REQUEST or RESPONSE
    ///
    /// The returned value is REQUEST if the message is created as an outgoing
    /// message. In such case the QR flag bit in the message header is cleared.
    /// The returned value is RESPONSE if the message is created as an incoming
    /// message and the QR flag bit was set in the received message header.
    ///
    /// @return An enum value indicating whether the message is a
    /// REQUEST or RESPONSE.
    QRFlag getQRFlag() const;

    /// @brief Returns message ID.
    ///
    /// @return message ID.
    uint16_t getId() const;

    /// @brief Sets message ID.
    ///
    /// @param id 16-bit value of the message id.
    void setId(const uint16_t id);

    /// @brief Returns an object representing message RCode.
    ///
    /// @return An object representing message RCode.
    const dns::Rcode& getRcode() const;

    /// @brief Sets message RCode.
    ///
    /// @param rcode An object representing message RCode.
    void setRcode(const dns::Rcode& rcode);

    /// @brief Returns number of RRsets in the specified message section.
    ///
    /// @param section An @c UpdateMsgSection enum specifying a message section
    /// for which the number of RRsets is to be returned.
    ///
    /// @return A number of RRsets in the specified message section.
    unsigned int getRRCount(const UpdateMsgSection section) const;

    /// @name Functions returning iterators to RRsets in message sections.
    ///
    //@{
    /// @brief Return iterators pointing to the beginning of the list of RRsets,
    /// which belong to the specified section.
    ///
    /// @param section An @c UpdateMsgSection enum specifying a message section
    /// for which the iterator should be returned.
    ///
    /// @return An iterator pointing to the beginning of the list of the
    /// RRsets, which belong to the specified section.
    const dns::RRsetIterator beginSection(const UpdateMsgSection section) const;

    /// @brief Return iterators pointing to the end of the list of RRsets,
    /// which belong to the specified section.
    ///
    /// @param section An @c UpdateMsgSection enum specifying a message section
    /// for which the iterator should be returned.
    ///
    /// @return An iterator pointing to the end of the list of the
    /// RRsets, which belong to the specified section.
    const dns::RRsetIterator endSection(const UpdateMsgSection section) const;
    //@}

    /// @brief Sets the Zone record.
    ///
    /// This function creates the @c D2Zone object, representing a Zone record
    /// for the outgoing message. If the Zone record is already set, it is
    /// replaced by the new record being set by this function. The RRType for
    /// the record is always SOA.
    ///
    /// @param zone A name of the zone being updated.
    /// @param rrclass A class of the zone record.
    void setZone(const dns::Name& zone, const dns::RRClass& rrclass);

    /// @brief Returns a pointer to the object representing Zone record.
    ///
    /// @return A pointer to the object representing Zone record.
    D2ZonePtr getZone() const;

    /// @brief Adds an RRset to the specified section.
    ///
    /// This function may throw exception if the specified section is
    /// out of bounds or Zone section update is attempted. For Zone
    /// section @c D2UpdateMessage::setZone function should be used instead.
    /// Also, this function expects that @c rrset argument is non-NULL.
    ///
    /// @param section A message section where the RRset should be added.
    /// @param rrset A reference to a RRset which should be added.
    void addRRset(const UpdateMsgSection section, const dns::RRsetPtr& rrset);

    /// @name Functions to handle message encoding and decoding.
    ///
    //@{
    /// @brief Encode outgoing message into wire format.
    ///
    /// This function encodes the DNS Update into the wire format. The format of
    /// such a message is described in the RFC2136, section 2. Some of the
    /// sections which belong to encoded message may be empty. If a particular
    /// message section is empty (does not comprise any RRs), the corresponding
    /// counter in the message header is set to 0. These counters are: PRCOUNT,
    /// UPCOUNT, ADCOUNT for the Prerequisites, Update RRs and Additional Data
    /// RRs respectively. The ZOCOUNT must be equal to 1 because RFC2136
    /// requires that the message comprises exactly one Zone record.
    ///
    /// If given a TSIG context, this method will pass the context down into
    /// dns::Message.toWire() method which signs the message using the context.
    ///
    /// This function does not guarantee exception safety. However, exceptions
    /// should be rare because @c D2UpdateMessage class API prevents invalid
    /// use of the class. The typical case, when this function may throw an
    /// exception is when this it is called on the object representing
    /// incoming (instead of outgoing) message. In such case, the QR field
    /// will be set to RESPONSE, which is invalid setting when calling this
    /// function.
    ///
    /// @param renderer A renderer object used to generate the message wire
    /// format.
    /// @param tsig_ctx A TSIG context that is to be used for signing the
    /// message. If NULL the message will not be signed.
    void toWire(dns::AbstractMessageRenderer& renderer,
                dns::TSIGContext* const tsig_ctx = NULL);

    /// @brief Decode incoming message from the wire format.
    ///
    /// This function decodes the DNS Update message stored in the buffer
    /// specified by the function argument.  If given a TSIG context, then
    /// the function will first attempt to use that context to verify the
    /// message signature.  If verification fails a TSIGVerifyError exception
    /// will be thrown. The function then parses message header and extracts
    /// the section counters: ZOCOUNT, PRCOUNT, UPCOUNT and ADCOUNT. Using
    /// these counters, function identifies message sections, which follow
    /// message header. These sections can be later accessed using:
    /// @c D2UpdateMessage::getZone, @c D2UpdateMessage::beginSection and
    /// @c D2UpdateMessage::endSection functions.
    ///
    /// This function is NOT exception safe. It signals message decoding errors
    /// through exceptions. Message decoding error may occur if the received
    /// message does not conform to the general DNS Message format, specified in
    /// RFC 1035. Errors which are specific to DNS Update messages include:
    /// - Invalid Opcode - not an UPDATE.
    /// - Invalid QR flag - the QR bit should be set to indicate that the
    /// message is the server response.
    /// - The number of records in the Zone section is greater than 1.
    ///
    /// @param received_data buffer holding DNS Update message to be parsed.
    /// @param bytes_received the number of bytes in received_data
    /// @param tsig_context A TSIG context that is to be used to verify the
    /// message. If NULL TSIG verification will not be attempted.
    void fromWire(const void* received_data, size_t bytes_received,
                  dns::TSIGContext* const tsig_context = NULL);
    //@}

private:
    /// Maps the values of the @c UpdateMessageSection field to the
    /// corresponding values in the @c isc::dns::Message class. This
    /// mapping is required here because this class uses @c isc::dns::Message
    /// class to do the actual processing of the DNS Update message.
    ///
    /// @param section An enum indicating the section for which the
    /// corresponding  enum value from @c isc::dns::Message will be returned.
    ///
    /// @return The enum value indicating the section in the DNS message
    /// represented by the @c isc::dns::Message class.
    static
    dns::Message::Section ddnsToDnsSection(const UpdateMsgSection section);

    /// @brief Checks received response message for correctness.
    ///
    /// This function verifies that the received response from a server is
    /// correct. Currently this function checks the following:
    /// - Opcode is 'DNS Update',
    /// - QR flag is RESPONSE (flag bit is set),
    /// - Zone section comprises at most one record.
    ///
    /// The function will throw exception if any of the conditions above are
    /// not met.
    ///
    /// @throw isc::d2::NotUpdateMessage if invalid Opcode.
    /// @throw isc::d2::InvalidQRFlag if QR flag is not set to RESPONSE
    /// @throw isc::d2::InvalidZone section, if Zone section comprises more
    /// than one record.
    void validateResponse() const;

    /// @brief An object representing DNS Message which is used by the
    /// implementation of @c D2UpdateMessage to perform low level.
    ///
    /// Declaration of this object pollutes the header with the details
    /// of @c D2UpdateMessage implementation. It might be cleaner to use
    /// Pimpl idiom to hide this object in an D2UpdateMessageImpl. However,
    /// it would bring additional complications to the implementation
    /// while the benefit would low - this header is not a part of any
    /// common library. Therefore, if implementation is changed, modification of
    /// private members of this class in the header has low impact.
    dns::Message message_;

    /// @brief Holds a pointer to the object, representing Zone in the DNS
    /// Update.
    D2ZonePtr zone_;

};

} // namespace d2
} // namespace isc

#endif // D2_UPDATE_MESSAGE_H