masterload.cc 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. // Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <istream>
  15. #include <fstream>
  16. #include <sstream>
  17. #include <string>
  18. #include <cctype>
  19. #include <boost/scoped_ptr.hpp>
  20. #include <exceptions/exceptions.h>
  21. #include <dns/masterload.h>
  22. #include <dns/name.h>
  23. #include <dns/rdata.h>
  24. #include <dns/rdataclass.h>
  25. #include <dns/rrclass.h>
  26. #include <dns/rrset.h>
  27. #include <dns/rrttl.h>
  28. #include <dns/rrtype.h>
  29. using namespace std;
  30. using namespace boost;
  31. using namespace isc::dns::rdata;
  32. namespace isc {
  33. namespace dns {
  34. namespace {
  35. // A helper function that strips off any comment or whitespace at the end of
  36. // an RR.
  37. // This is an incomplete implementation, and cannot handle all such comments;
  38. // it's considered a short term workaround to deal with some real world
  39. // cases.
  40. string
  41. stripLine(string& s, const Exception& ex) {
  42. // Find any ';' in the text data, and locate the position of the last
  43. // occurrence. Note that unless/until we support empty RDATA it
  44. // shouldn't be placed at the beginning of the data.
  45. const size_t pos_semicolon = s.rfind(';');
  46. if (pos_semicolon == 0) {
  47. throw ex;
  48. } else if (pos_semicolon != string::npos) {
  49. s.resize(pos_semicolon);
  50. }
  51. // Remove any trailing whitespace return the resulting text.
  52. s.resize(s.find_last_not_of(" \t") + 1);
  53. return (s);
  54. }
  55. }
  56. void
  57. masterLoad(const char* const filename, const Name& origin,
  58. const RRClass& zone_class, MasterLoadCallback callback)
  59. {
  60. if ((filename == NULL) || (*filename == '\0')) {
  61. isc_throw(MasterLoadError, "Name of master file must not be null");
  62. }
  63. ifstream ifs;
  64. ifs.open(filename, ios_base::in);
  65. if (ifs.fail()) {
  66. isc_throw(MasterLoadError, "Failed to open master file: " << filename);
  67. }
  68. masterLoad(ifs, origin, zone_class, callback);
  69. ifs.close();
  70. }
  71. void
  72. masterLoad(istream& input, const Name& origin, const RRClass& zone_class,
  73. MasterLoadCallback callback)
  74. {
  75. RRsetPtr rrset;
  76. ConstRdataPtr prev_rdata; // placeholder for special case of RRSIGs
  77. string line;
  78. unsigned int line_count = 1;
  79. do {
  80. getline(input, line);
  81. if (input.bad() || (input.fail() && !input.eof())) {
  82. isc_throw(MasterLoadError, "Unexpectedly failed to read a line");
  83. }
  84. // blank/comment lines should be simply skipped.
  85. if (line.empty() || line[0] == ';') {
  86. continue;
  87. }
  88. // The line shouldn't have leading space (which means omitting the
  89. // owner name).
  90. if (isspace(line[0])) {
  91. isc_throw(MasterLoadError, "Leading space at line " << line_count);
  92. }
  93. // Parse a single RR
  94. istringstream iss(line);
  95. string owner_txt, ttl_txt, rrclass_txt, rrtype_txt;
  96. stringbuf rdatabuf;
  97. iss >> owner_txt >> ttl_txt >> rrclass_txt >> rrtype_txt >> &rdatabuf;
  98. if (iss.bad() || iss.fail()) {
  99. isc_throw(MasterLoadError, "Parse failure for a valid RR at line "
  100. << line_count);
  101. }
  102. // This simple version doesn't support relative owner names with a
  103. // separate origin.
  104. if (owner_txt.empty() || *(owner_txt.end() - 1) != '.') {
  105. isc_throw(MasterLoadError, "Owner name is not absolute at line "
  106. << line_count);
  107. }
  108. // XXX: this part is a bit tricky (and less efficient). We are going
  109. // to validate the text for the RR parameters, and throw an exception
  110. // if any of them is invalid by converting an underlying exception
  111. // to MasterLoadError. To do that, we need to define the corresponding
  112. // variables used for RRset construction outside the try-catch block,
  113. // but we don't like to use a temporary variable with a meaningless
  114. // initial value. So we define pointers outside the try block
  115. // and allocate/initialize the actual objects within the block.
  116. // To make it exception safe we use Boost.scoped_ptr.
  117. scoped_ptr<const Name> owner;
  118. scoped_ptr<const RRTTL> ttl;
  119. scoped_ptr<const RRClass> rrclass;
  120. scoped_ptr<const RRType> rrtype;
  121. ConstRdataPtr rdata;
  122. try {
  123. owner.reset(new Name(owner_txt));
  124. ttl.reset(new RRTTL(ttl_txt));
  125. rrclass.reset(new RRClass(rrclass_txt));
  126. rrtype.reset(new RRType(rrtype_txt));
  127. string rdtext = rdatabuf.str();
  128. try {
  129. rdata = createRdata(*rrtype, *rrclass, rdtext);
  130. } catch (const Exception& ex) {
  131. // If the parse for the RDATA fails, check if it has comments
  132. // or whitespace at the end, and if so, retry the conversion
  133. // after stripping off the comment or whitespace
  134. rdata = createRdata(*rrtype, *rrclass, stripLine(rdtext, ex));
  135. }
  136. } catch (const Exception& ex) {
  137. isc_throw(MasterLoadError, "Invalid RR text at line " << line_count
  138. << ": " << ex.what());
  139. }
  140. // Origin related validation:
  141. // - reject out-of-zone data
  142. // - reject SOA whose owner is not at the top of zone
  143. const NameComparisonResult cmp_result = owner->compare(origin);
  144. if (cmp_result.getRelation() != NameComparisonResult::EQUAL &&
  145. cmp_result.getRelation() != NameComparisonResult::SUBDOMAIN) {
  146. isc_throw(MasterLoadError, "Out-of-zone data at line "
  147. << line_count);
  148. }
  149. if (*rrtype == RRType::SOA() &&
  150. cmp_result.getRelation() != NameComparisonResult::EQUAL) {
  151. isc_throw(MasterLoadError, "SOA not at top of zone at line "
  152. << line_count);
  153. }
  154. // Reject RR class mismatching
  155. if (*rrclass != zone_class) {
  156. isc_throw(MasterLoadError, "RR class (" << rrclass_txt
  157. << ") does not match the zone class (" << zone_class
  158. << ") at line " << line_count);
  159. }
  160. // Everything is okay. Now create/update RRset with the new RR.
  161. // If this is the first RR or the RR type/name is new, we are seeing
  162. // a new RRset.
  163. bool new_rrset = false;
  164. if (!rrset || rrset->getType() != *rrtype ||
  165. rrset->getName() != *owner) {
  166. new_rrset = true;
  167. } else if (rrset->getType() == RRType::RRSIG()) {
  168. // We are seeing two consecutive RRSIGs of the same name.
  169. // They can be combined iff they have the same type covered.
  170. if (dynamic_cast<const generic::RRSIG&>(*rdata).typeCovered() !=
  171. dynamic_cast<const generic::RRSIG&>(*prev_rdata).typeCovered())
  172. {
  173. new_rrset = true;
  174. }
  175. }
  176. if (new_rrset) {
  177. // Commit the previous RRset, if any.
  178. if (rrset) {
  179. callback(rrset);
  180. }
  181. rrset = RRsetPtr(new RRset(*owner, *rrclass, *rrtype, *ttl));
  182. }
  183. rrset->addRdata(rdata);
  184. prev_rdata = rdata;
  185. } while (++line_count, !input.eof());
  186. // Commit the last RRset, if any.
  187. if (rrset) {
  188. callback(rrset);
  189. }
  190. }
  191. } // namespace dns
  192. } // namespace isc