masterload.cc 8.4 KB

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