tsig_250.cc 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
  1. // Copyright (C) 2010-2016 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this
  5. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. #include <config.h>
  7. #include <string>
  8. #include <sstream>
  9. #include <vector>
  10. #include <boost/lexical_cast.hpp>
  11. #include <util/buffer.h>
  12. #include <util/encode/base64.h>
  13. #include <dns/messagerenderer.h>
  14. #include <dns/name.h>
  15. #include <dns/rdata.h>
  16. #include <dns/rdataclass.h>
  17. #include <dns/rcode.h>
  18. #include <dns/tsigkey.h>
  19. #include <dns/tsigerror.h>
  20. #include <dns/rdata/generic/detail/lexer_util.h>
  21. using namespace std;
  22. using boost::lexical_cast;
  23. using namespace isc::util;
  24. using namespace isc::util::encode;
  25. using namespace isc::dns;
  26. using isc::dns::rdata::generic::detail::createNameFromLexer;
  27. // BEGIN_ISC_NAMESPACE
  28. // BEGIN_RDATA_NAMESPACE
  29. // straightforward representation of TSIG RDATA fields
  30. struct TSIGImpl {
  31. TSIGImpl(const Name& algorithm, uint64_t time_signed, uint16_t fudge,
  32. vector<uint8_t>& mac, uint16_t original_id, uint16_t error,
  33. vector<uint8_t>& other_data) :
  34. algorithm_(algorithm), time_signed_(time_signed), fudge_(fudge),
  35. mac_(mac), original_id_(original_id), error_(error),
  36. other_data_(other_data)
  37. {}
  38. TSIGImpl(const Name& algorithm, uint64_t time_signed, uint16_t fudge,
  39. size_t macsize, const void* mac, uint16_t original_id,
  40. uint16_t error, size_t other_len, const void* other_data) :
  41. algorithm_(algorithm), time_signed_(time_signed), fudge_(fudge),
  42. mac_(static_cast<const uint8_t*>(mac),
  43. static_cast<const uint8_t*>(mac) + macsize),
  44. original_id_(original_id), error_(error),
  45. other_data_(static_cast<const uint8_t*>(other_data),
  46. static_cast<const uint8_t*>(other_data) + other_len)
  47. {}
  48. template <typename Output>
  49. void toWireCommon(Output& output) const;
  50. const Name algorithm_;
  51. const uint64_t time_signed_;
  52. const uint16_t fudge_;
  53. const vector<uint8_t> mac_;
  54. const uint16_t original_id_;
  55. const uint16_t error_;
  56. const vector<uint8_t> other_data_;
  57. };
  58. // helper function for string and lexer constructors
  59. TSIGImpl*
  60. TSIG::constructFromLexer(MasterLexer& lexer, const Name* origin) {
  61. const Name& algorithm =
  62. createNameFromLexer(lexer, origin ? origin : &Name::ROOT_NAME());
  63. const Name& canonical_algorithm_name =
  64. (algorithm == TSIGKey::HMACMD5_SHORT_NAME()) ?
  65. TSIGKey::HMACMD5_NAME() : algorithm;
  66. const string& time_txt =
  67. lexer.getNextToken(MasterToken::STRING).getString();
  68. uint64_t time_signed;
  69. try {
  70. time_signed = boost::lexical_cast<uint64_t>(time_txt);
  71. } catch (const boost::bad_lexical_cast&) {
  72. isc_throw(InvalidRdataText, "Invalid TSIG Time");
  73. }
  74. if ((time_signed >> 48) != 0) {
  75. isc_throw(InvalidRdataText, "TSIG Time out of range");
  76. }
  77. const uint32_t fudge = lexer.getNextToken(MasterToken::NUMBER).getNumber();
  78. if (fudge > 0xffff) {
  79. isc_throw(InvalidRdataText, "TSIG Fudge out of range");
  80. }
  81. const uint32_t macsize =
  82. lexer.getNextToken(MasterToken::NUMBER).getNumber();
  83. if (macsize > 0xffff) {
  84. isc_throw(InvalidRdataText, "TSIG MAC Size out of range");
  85. }
  86. const string& mac_txt = (macsize > 0) ?
  87. lexer.getNextToken(MasterToken::STRING).getString() : "";
  88. vector<uint8_t> mac;
  89. decodeBase64(mac_txt, mac);
  90. if (mac.size() != macsize) {
  91. isc_throw(InvalidRdataText, "TSIG MAC Size and data are inconsistent");
  92. }
  93. const uint32_t orig_id =
  94. lexer.getNextToken(MasterToken::NUMBER).getNumber();
  95. if (orig_id > 0xffff) {
  96. isc_throw(InvalidRdataText, "TSIG Original ID out of range");
  97. }
  98. const string& error_txt =
  99. lexer.getNextToken(MasterToken::STRING).getString();
  100. uint32_t error = 0;
  101. // XXX: In the initial implementation we hardcode the mnemonics.
  102. // We'll soon generalize this.
  103. if (error_txt == "NOERROR") {
  104. error = Rcode::NOERROR_CODE;
  105. } else if (error_txt == "BADSIG") {
  106. error = TSIGError::BAD_SIG_CODE;
  107. } else if (error_txt == "BADKEY") {
  108. error = TSIGError::BAD_KEY_CODE;
  109. } else if (error_txt == "BADTIME") {
  110. error = TSIGError::BAD_TIME_CODE;
  111. } else if (error_txt == "BADMODE") {
  112. error = TSIGError::BAD_MODE_CODE;
  113. } else if (error_txt == "BADNAME") {
  114. error = TSIGError::BAD_NAME_CODE;
  115. } else if (error_txt == "BADALG") {
  116. error = TSIGError::BAD_ALG_CODE;
  117. } else if (error_txt == "BADTRUNC") {
  118. error = TSIGError::BAD_TRUNC_CODE;
  119. } else {
  120. /// we cast to uint32_t and range-check, because casting directly to
  121. /// uint16_t will convert negative numbers to large positive numbers
  122. try {
  123. error = boost::lexical_cast<uint32_t>(error_txt);
  124. } catch (const boost::bad_lexical_cast&) {
  125. isc_throw(InvalidRdataText, "Invalid TSIG Error");
  126. }
  127. if (error > 0xffff) {
  128. isc_throw(InvalidRdataText, "TSIG Error out of range");
  129. }
  130. }
  131. const uint32_t otherlen =
  132. lexer.getNextToken(MasterToken::NUMBER).getNumber();
  133. if (otherlen > 0xffff) {
  134. isc_throw(InvalidRdataText, "TSIG Other Len out of range");
  135. }
  136. const string otherdata_txt = (otherlen > 0) ?
  137. lexer.getNextToken(MasterToken::STRING).getString() : "";
  138. vector<uint8_t> other_data;
  139. decodeBase64(otherdata_txt, other_data);
  140. if (other_data.size() != otherlen) {
  141. isc_throw(InvalidRdataText,
  142. "TSIG Other Data length does not match Other Len");
  143. }
  144. // RFC2845 says Other Data is "empty unless Error == BADTIME".
  145. // However, we don't enforce that.
  146. return (new TSIGImpl(canonical_algorithm_name, time_signed, fudge, mac,
  147. orig_id, error, other_data));
  148. }
  149. /// \brief Constructor from string.
  150. ///
  151. /// The given string must represent a valid TSIG RDATA. There can be extra
  152. /// space characters at the beginning or end of the text (which are simply
  153. /// ignored), but other extra text, including a new line, will make the
  154. /// construction fail with an exception.
  155. ///
  156. /// \c tsig_str must be formatted as follows:
  157. /// \code <Algorithm Name> <Time Signed> <Fudge> <MAC Size> [<MAC>]
  158. /// <Original ID> <Error> <Other Len> [<Other Data>]
  159. /// \endcode
  160. ///
  161. /// Note that, since the Algorithm Name field is defined to be "in domain name
  162. /// syntax", but it is not actually a domain name, it does not have to be
  163. /// fully qualified.
  164. ///
  165. /// The Error field is an unsigned 16-bit decimal integer or a valid mnemonic
  166. /// as specified in RFC2845. Currently, "NOERROR", "BADSIG", "BADKEY", and
  167. /// "BADTIME" are supported (case sensitive). In future versions other
  168. /// representations that are compatible with the DNS RCODE may be supported.
  169. ///
  170. /// The MAC and Other Data fields are base-64 encoded strings that do not
  171. /// contain space characters.
  172. /// If the MAC Size field is 0, the MAC field must not appear in \c tsig_str.
  173. /// If the Other Len field is 0, the Other Data field must not appear in
  174. /// \c tsig_str.
  175. /// The decoded data of the MAC field is MAC Size bytes of binary stream.
  176. /// The decoded data of the Other Data field is Other Len bytes of binary
  177. /// stream.
  178. ///
  179. /// An example of valid string is:
  180. /// \code "hmac-sha256. 853804800 300 3 AAAA 2845 0 0" \endcode
  181. /// In this example Other Data is missing because Other Len is 0.
  182. ///
  183. /// Note that RFC2845 does not define the standard presentation format
  184. /// of %TSIG RR, so the above syntax is implementation specific.
  185. /// This is, however, compatible with the format acceptable to BIND 9's
  186. /// RDATA parser.
  187. ///
  188. /// \throw Others Exception from the Name constructors.
  189. /// \throw InvalidRdataText if any fields are out of their valid range,
  190. /// or are incorrect.
  191. /// \throw BadValue if MAC or Other Data is not validly encoded in base-64.
  192. ///
  193. /// \param tsig_str A string containing the RDATA to be created
  194. TSIG::TSIG(const std::string& tsig_str) : impl_(NULL) {
  195. // We use unique_ptr here because if there is an exception in this
  196. // constructor, the destructor is not called and there could be a
  197. // leak of the TSIGImpl that constructFromLexer() returns.
  198. std::unique_ptr<TSIGImpl> impl_ptr;
  199. try {
  200. std::istringstream ss(tsig_str);
  201. MasterLexer lexer;
  202. lexer.pushSource(ss);
  203. impl_ptr.reset(constructFromLexer(lexer, NULL));
  204. if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) {
  205. isc_throw(InvalidRdataText,
  206. "Extra input text for TSIG: " << tsig_str);
  207. }
  208. } catch (const MasterLexer::LexerError& ex) {
  209. isc_throw(InvalidRdataText,
  210. "Failed to construct TSIG from '" << tsig_str << "': "
  211. << ex.what());
  212. }
  213. impl_ = impl_ptr.release();
  214. }
  215. /// \brief Constructor with a context of MasterLexer.
  216. ///
  217. /// The \c lexer should point to the beginning of valid textual
  218. /// representation of an TSIG RDATA.
  219. ///
  220. /// See \c TSIG::TSIG(const std::string&) for description of the
  221. /// expected RDATA fields.
  222. ///
  223. /// \throw MasterLexer::LexerError General parsing error such as
  224. /// missing field.
  225. /// \throw InvalidRdataText if any fields are out of their valid range,
  226. /// or are incorrect.
  227. ///
  228. /// \param lexer A \c MasterLexer object parsing a master file for the
  229. /// RDATA to be created
  230. TSIG::TSIG(MasterLexer& lexer, const Name* origin,
  231. MasterLoader::Options, MasterLoaderCallbacks&) :
  232. impl_(constructFromLexer(lexer, origin))
  233. {
  234. }
  235. /// \brief Constructor from wire-format data.
  236. ///
  237. /// When a read operation on \c buffer fails (e.g., due to a corrupted
  238. /// message) a corresponding exception from the \c InputBuffer class will
  239. /// be thrown.
  240. /// If the wire-format data does not begin with a valid domain name,
  241. /// a corresponding exception from the \c Name class will be thrown.
  242. /// In addition, this constructor internally involves resource allocation,
  243. /// and if it fails a corresponding standard exception will be thrown.
  244. ///
  245. /// According to RFC3597, the Algorithm field must be a non compressed form
  246. /// of domain name. But this implementation accepts a %TSIG RR even if that
  247. /// field is compressed.
  248. ///
  249. /// \param buffer A buffer storing the wire format data.
  250. /// \param rdata_len The length of the RDATA in bytes, normally expected
  251. /// to be the value of the RDLENGTH field of the corresponding RR.
  252. /// But this constructor does not use this parameter; if necessary, the caller
  253. /// must check consistency between the length parameter and the actual
  254. /// RDATA length.
  255. TSIG::TSIG(InputBuffer& buffer, size_t) :
  256. impl_(NULL)
  257. {
  258. Name algorithm(buffer);
  259. uint8_t time_signed_buf[6];
  260. buffer.readData(time_signed_buf, sizeof(time_signed_buf));
  261. const uint64_t time_signed =
  262. (static_cast<uint64_t>(time_signed_buf[0]) << 40 |
  263. static_cast<uint64_t>(time_signed_buf[1]) << 32 |
  264. static_cast<uint64_t>(time_signed_buf[2]) << 24 |
  265. static_cast<uint64_t>(time_signed_buf[3]) << 16 |
  266. static_cast<uint64_t>(time_signed_buf[4]) << 8 |
  267. static_cast<uint64_t>(time_signed_buf[5]));
  268. const uint16_t fudge = buffer.readUint16();
  269. const uint16_t mac_size = buffer.readUint16();
  270. vector<uint8_t> mac(mac_size);
  271. if (mac_size > 0) {
  272. buffer.readData(&mac[0], mac_size);
  273. }
  274. const uint16_t original_id = buffer.readUint16();
  275. const uint16_t error = buffer.readUint16();
  276. const uint16_t other_len = buffer.readUint16();
  277. vector<uint8_t> other_data(other_len);
  278. if (other_len > 0) {
  279. buffer.readData(&other_data[0], other_len);
  280. }
  281. const Name& canonical_algorithm_name =
  282. (algorithm == TSIGKey::HMACMD5_SHORT_NAME()) ?
  283. TSIGKey::HMACMD5_NAME() : algorithm;
  284. impl_ = new TSIGImpl(canonical_algorithm_name, time_signed, fudge, mac,
  285. original_id, error, other_data);
  286. }
  287. TSIG::TSIG(const Name& algorithm, uint64_t time_signed, uint16_t fudge,
  288. uint16_t mac_size, const void* mac, uint16_t original_id,
  289. uint16_t error, uint16_t other_len, const void* other_data) :
  290. impl_(NULL)
  291. {
  292. // Time Signed is a 48-bit value.
  293. if ((time_signed >> 48) != 0) {
  294. isc_throw(OutOfRange, "TSIG Time Signed is too large: " <<
  295. time_signed);
  296. }
  297. if ((mac_size == 0 && mac != NULL) || (mac_size > 0 && mac == NULL)) {
  298. isc_throw(InvalidParameter, "TSIG MAC size and data inconsistent");
  299. }
  300. if ((other_len == 0 && other_data != NULL) ||
  301. (other_len > 0 && other_data == NULL)) {
  302. isc_throw(InvalidParameter,
  303. "TSIG Other data length and data inconsistent");
  304. }
  305. const Name& canonical_algorithm_name =
  306. (algorithm == TSIGKey::HMACMD5_SHORT_NAME()) ?
  307. TSIGKey::HMACMD5_NAME() : algorithm;
  308. impl_ = new TSIGImpl(canonical_algorithm_name, time_signed, fudge, mac_size,
  309. mac, original_id, error, other_len, other_data);
  310. }
  311. /// \brief The copy constructor.
  312. ///
  313. /// It internally allocates a resource, and if it fails a corresponding
  314. /// standard exception will be thrown.
  315. /// This constructor never throws an exception otherwise.
  316. TSIG::TSIG(const TSIG& source) : Rdata(), impl_(new TSIGImpl(*source.impl_))
  317. {}
  318. TSIG&
  319. TSIG::operator=(const TSIG& source) {
  320. if (this == &source) {
  321. return (*this);
  322. }
  323. TSIGImpl* newimpl = new TSIGImpl(*source.impl_);
  324. delete impl_;
  325. impl_ = newimpl;
  326. return (*this);
  327. }
  328. TSIG::~TSIG() {
  329. delete impl_;
  330. }
  331. /// \brief Convert the \c TSIG to a string.
  332. ///
  333. /// The output of this method is formatted as described in the "from string"
  334. /// constructor (\c TSIG(const std::string&))).
  335. ///
  336. /// If internal resource allocation fails, a corresponding
  337. /// standard exception will be thrown.
  338. ///
  339. /// \return A \c string object that represents the \c TSIG object.
  340. std::string
  341. TSIG::toText() const {
  342. string result;
  343. result += impl_->algorithm_.toText() + " " +
  344. lexical_cast<string>(impl_->time_signed_) + " " +
  345. lexical_cast<string>(impl_->fudge_) + " " +
  346. lexical_cast<string>(impl_->mac_.size()) + " ";
  347. if (!impl_->mac_.empty()) {
  348. result += encodeBase64(impl_->mac_) + " ";
  349. }
  350. result += lexical_cast<string>(impl_->original_id_) + " ";
  351. result += TSIGError(impl_->error_).toText() + " ";
  352. result += lexical_cast<string>(impl_->other_data_.size());
  353. if (!impl_->other_data_.empty()) {
  354. result += " " + encodeBase64(impl_->other_data_);
  355. }
  356. return (result);
  357. }
  358. // Common sequence of toWire() operations used for the two versions of
  359. // toWire().
  360. template <typename Output>
  361. void
  362. TSIGImpl::toWireCommon(Output& output) const {
  363. output.writeUint16(time_signed_ >> 32);
  364. output.writeUint32(time_signed_ & 0xffffffff);
  365. output.writeUint16(fudge_);
  366. const uint16_t mac_size = mac_.size();
  367. output.writeUint16(mac_size);
  368. if (mac_size > 0) {
  369. output.writeData(&mac_[0], mac_size);
  370. }
  371. output.writeUint16(original_id_);
  372. output.writeUint16(error_);
  373. const uint16_t other_len = other_data_.size();
  374. output.writeUint16(other_len);
  375. if (other_len > 0) {
  376. output.writeData(&other_data_[0], other_len);
  377. }
  378. }
  379. /// \brief Render the \c TSIG in the wire format without name compression.
  380. ///
  381. /// If internal resource allocation fails, a corresponding
  382. /// standard exception will be thrown.
  383. /// This method never throws an exception otherwise.
  384. ///
  385. /// \param buffer An output buffer to store the wire data.
  386. void
  387. TSIG::toWire(OutputBuffer& buffer) const {
  388. impl_->algorithm_.toWire(buffer);
  389. impl_->toWireCommon<OutputBuffer>(buffer);
  390. }
  391. /// \brief Render the \c TSIG in the wire format with taking into account
  392. /// compression.
  393. ///
  394. /// As specified in RFC3597, the Algorithm field (a domain name) will not
  395. /// be compressed. However, the domain name could be a target of compression
  396. /// of other compressible names (though pretty unlikely), the offset
  397. /// information of the algorithm name may be recorded in \c renderer.
  398. ///
  399. /// If internal resource allocation fails, a corresponding
  400. /// standard exception will be thrown.
  401. /// This method never throws an exception otherwise.
  402. ///
  403. /// \param renderer DNS message rendering context that encapsulates the
  404. /// output buffer and name compression information.
  405. void
  406. TSIG::toWire(AbstractMessageRenderer& renderer) const {
  407. renderer.writeName(impl_->algorithm_, false);
  408. impl_->toWireCommon<AbstractMessageRenderer>(renderer);
  409. }
  410. // A helper function commonly used for TSIG::compare().
  411. int
  412. vectorComp(const vector<uint8_t>& v1, const vector<uint8_t>& v2) {
  413. const size_t this_size = v1.size();
  414. const size_t other_size = v2.size();
  415. if (this_size != other_size) {
  416. return (this_size < other_size ? -1 : 1);
  417. }
  418. if (this_size > 0) {
  419. return (memcmp(&v1[0], &v2[0], this_size));
  420. }
  421. return (0);
  422. }
  423. /// \brief Compare two instances of \c TSIG RDATA.
  424. ///
  425. /// This method compares \c this and the \c other \c TSIG objects
  426. /// in terms of the DNSSEC sorting order as defined in RFC4034, and returns
  427. /// the result as an integer.
  428. ///
  429. /// This method is expected to be used in a polymorphic way, and the
  430. /// parameter to compare against is therefore of the abstract \c Rdata class.
  431. /// However, comparing two \c Rdata objects of different RR types
  432. /// is meaningless, and \c other must point to a \c TSIG object;
  433. /// otherwise, the standard \c bad_cast exception will be thrown.
  434. /// This method never throws an exception otherwise.
  435. ///
  436. /// \param other the right-hand operand to compare against.
  437. /// \return < 0 if \c this would be sorted before \c other.
  438. /// \return 0 if \c this is identical to \c other in terms of sorting order.
  439. /// \return > 0 if \c this would be sorted after \c other.
  440. int
  441. TSIG::compare(const Rdata& other) const {
  442. const TSIG& other_tsig = dynamic_cast<const TSIG&>(other);
  443. const int ncmp = compareNames(impl_->algorithm_,
  444. other_tsig.impl_->algorithm_);
  445. if (ncmp != 0) {
  446. return (ncmp);
  447. }
  448. if (impl_->time_signed_ != other_tsig.impl_->time_signed_) {
  449. return (impl_->time_signed_ < other_tsig.impl_->time_signed_ ? -1 : 1);
  450. }
  451. if (impl_->fudge_ != other_tsig.impl_->fudge_) {
  452. return (impl_->fudge_ < other_tsig.impl_->fudge_ ? -1 : 1);
  453. }
  454. const int vcmp = vectorComp(impl_->mac_, other_tsig.impl_->mac_);
  455. if (vcmp != 0) {
  456. return (vcmp);
  457. }
  458. if (impl_->original_id_ != other_tsig.impl_->original_id_) {
  459. return (impl_->original_id_ < other_tsig.impl_->original_id_ ? -1 : 1);
  460. }
  461. if (impl_->error_ != other_tsig.impl_->error_) {
  462. return (impl_->error_ < other_tsig.impl_->error_ ? -1 : 1);
  463. }
  464. return (vectorComp(impl_->other_data_, other_tsig.impl_->other_data_));
  465. }
  466. const Name&
  467. TSIG::getAlgorithm() const {
  468. return (impl_->algorithm_);
  469. }
  470. uint64_t
  471. TSIG::getTimeSigned() const {
  472. return (impl_->time_signed_);
  473. }
  474. uint16_t
  475. TSIG::getFudge() const {
  476. return (impl_->fudge_);
  477. }
  478. uint16_t
  479. TSIG::getMACSize() const {
  480. return (impl_->mac_.size());
  481. }
  482. const void*
  483. TSIG::getMAC() const {
  484. if (!impl_->mac_.empty()) {
  485. return (&impl_->mac_[0]);
  486. } else {
  487. return (NULL);
  488. }
  489. }
  490. uint16_t
  491. TSIG::getOriginalID() const {
  492. return (impl_->original_id_);
  493. }
  494. uint16_t
  495. TSIG::getError() const {
  496. return (impl_->error_);
  497. }
  498. uint16_t
  499. TSIG::getOtherLen() const {
  500. return (impl_->other_data_.size());
  501. }
  502. const void*
  503. TSIG::getOtherData() const {
  504. if (!impl_->other_data_.empty()) {
  505. return (&impl_->other_data_[0]);
  506. } else {
  507. return (NULL);
  508. }
  509. }
  510. // END_RDATA_NAMESPACE
  511. // END_ISC_NAMESPACE