response_classifier.cc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. // Copyright (C) 2011 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. // $Id$
  15. #include <cstddef>
  16. #include <vector>
  17. #include <resolver/response_classifier.h>
  18. #include <dns/name.h>
  19. #include <dns/opcode.h>
  20. #include <dns/rcode.h>
  21. #include <dns/rrset.h>
  22. using namespace isc::dns;
  23. using namespace std;
  24. // Classify the response in the "message" object.
  25. ResponseClassifier::Category ResponseClassifier::classify(
  26. const Question& question, const MessagePtr& message, bool tcignore)
  27. {
  28. // Check header bits
  29. if (!message->getHeaderFlag(Message::HEADERFLAG_QR)) {
  30. return (NOTRESPONSE); // Query-response bit not set in the response
  31. }
  32. // We only recognise responses to queries here
  33. if (message->getOpcode() != Opcode::QUERY()) {
  34. return (OPCODE);
  35. }
  36. // Apparently have a response. There must be a single question in it...
  37. const vector<QuestionPtr> msgquestion(message->beginQuestion(),
  38. message->endQuestion());
  39. if (msgquestion.size() != 1) {
  40. return (NOTONEQUEST); // Not one question in response question section
  41. }
  42. // ... and the question should be equal to the question given.
  43. // XXX: This means that "question" may not be the question sent by the
  44. // client. In the case of a CNAME response, the qname of subsequent
  45. // questions needs to be altered.
  46. if (question != *(msgquestion[0])) {
  47. return (MISMATQUEST);
  48. }
  49. // Check for Rcode-related errors.
  50. const Rcode& rcode = message->getRcode();
  51. if (rcode != Rcode::NOERROR()) {
  52. if (rcode == Rcode::NXDOMAIN()) {
  53. // No such domain. According to RFC2308, the domain referred to by
  54. // the QNAME does not exist, although there may be a CNAME in the
  55. // answer section and there may be an SOA and/or NS RRs in the
  56. // authority section (ignoring any DNSSEC RRs for now).
  57. //
  58. // Note the "may". There may not be anything. Also, note that if
  59. // there is a CNAME in the answer section, the authoritative server
  60. // has verified that the name given in the CNAME's RDATA field does
  61. // not exist. And that if a CNAME is returned in the answer, then
  62. // the QNAME of the RRs in the authority section will refer to the
  63. // authority for the CNAME's RDATA and not to the original question.
  64. //
  65. // Without doing further classification, it is sufficient to say
  66. // that if an NXDOMAIN is received, there was no translation of the
  67. // QNAME available.
  68. return (NXDOMAIN); // Received NXDOMAIN from parent.
  69. } else {
  70. // Not NXDOMAIN but not NOERROR either. Must be an RCODE-related
  71. // error.
  72. return (RCODE);
  73. }
  74. }
  75. // All seems OK and we can start looking at the content. However, one
  76. // more header check remains - was the response truncated? If so, we'll
  77. // probably want to re-query over TCP. However, in some circumstances we
  78. // might want to go with what we have. So give the caller the option of
  79. // ignoring the TC bit.
  80. if (message->getHeaderFlag(Message::HEADERFLAG_TC) && (!tcignore)) {
  81. return (TRUNCATED);
  82. }
  83. // By the time we get here, we're assured that the packet format is correct.
  84. // We now need to decide as to whether it is an answer, a CNAME, or a
  85. // referral. For this, we need to inspect the contents of the answer
  86. // and authority sections.
  87. const vector<RRsetPtr> answer(
  88. message->beginSection(Message::SECTION_ANSWER),
  89. message->endSection(Message::SECTION_ANSWER)
  90. );
  91. const vector<RRsetPtr> authority(
  92. message->beginSection(Message::SECTION_AUTHORITY),
  93. message->endSection(Message::SECTION_AUTHORITY)
  94. );
  95. // If there is nothing in the answer section, it is a referral - unless
  96. // there is nothing in the authority section
  97. if (answer.empty()) {
  98. if (authority.empty()) {
  99. return (EMPTY);
  100. } else {
  101. return (REFERRAL);
  102. }
  103. }
  104. // Look at two cases - one RRset in the answer and multiple RRsets in
  105. // the answer.
  106. if (answer.size() == 1) {
  107. // Does the name and class of the answer match that of the question?
  108. if ((answer[0]->getName() == question.getName()) &&
  109. (answer[0]->getClass() == question.getClass())) {
  110. // It does. How about the type of the response? The response
  111. // is an answer if the type matches that of the question, or if the
  112. // question was for type ANY. It is a CNAME reply if the answer
  113. // type is CNAME. And it is an error for anything else.
  114. if ((answer[0]->getType() == question.getType()) ||
  115. (question.getType() == RRType::ANY())) {
  116. return (ANSWER);
  117. } else if (answer[0]->getType() == RRType::CNAME()) {
  118. return (CNAME);
  119. } else {
  120. return (INVTYPE);
  121. }
  122. }
  123. else {
  124. // Either the name and/or class of the reply don't match that of
  125. // the question.
  126. return (INVNAMCLASS);
  127. }
  128. }
  129. // There are multiple RRsets in the answer. They should all have the same
  130. // QCLASS, else there is some error in the response.
  131. for (int i = 1; i < answer.size(); ++i) {
  132. if (answer[0]->getClass() != answer[i]->getClass()) {
  133. return (MULTICLASS);
  134. }
  135. }
  136. // If the request type was ANY and they all have the same QNAME, we have
  137. // an answer. But if they don't have the same QNAME, we must have an error;
  138. // the only way we could get different QNAMES in an answer is if one were a
  139. // CNAME - in which case there should no other record types at that QNAME.
  140. if (question.getType() == RRType::ANY()) {
  141. bool all_same = true;
  142. for (int i = 1; (i < answer.size()) && all_same; ++i) {
  143. all_same = (answer[0]->getName() == answer[i]->getName());
  144. }
  145. if (all_same) {
  146. return (ANSWER);
  147. } else {
  148. return (EXTRADATA);
  149. }
  150. }
  151. // Multiple RRs in the answer, and not all the same QNAME. This
  152. // is either an answer, a CNAME (in either case, there could be multiple
  153. // CNAMEs in the chain) or an error.
  154. //
  155. // So we need to follow the CNAME chain to resolve this. For this to work:
  156. //
  157. // a) There must be one RR that matches the name, class and type of
  158. // the question, and this is a CNAME.
  159. // b) The CNAME chain is followed until the end of the chain does not
  160. // exist (answer is a CNAME) or it is not of type CNAME (ANSWER).
  161. //
  162. // In the latter case, if there are additional RRs, it must be an error.
  163. vector<RRsetPtr> ansrrset(answer);
  164. vector<int> present(ansrrset.size(), 1);
  165. return cnameChase(question.getName(), question.getType(), ansrrset, present,
  166. ansrrset.size());
  167. }
  168. // Search the CNAME chain.
  169. ResponseClassifier::Category ResponseClassifier::cnameChase(
  170. const Name& qname, const RRType& qtype, vector<RRsetPtr>& ansrrset,
  171. vector<int>& present, size_t size)
  172. {
  173. // Search through the vector of RRset pointers until we find one with the
  174. // right QNAME.
  175. for (int i = 0; i < ansrrset.size(); ++i) {
  176. if (present[i]) {
  177. // This entry has not been logically removed, so look at it.
  178. if (ansrrset[i]->getName() == qname) {
  179. // QNAME match. If this RRset is a CNAME, remove it from
  180. // further consideration. If nothing is left, the end of the
  181. // chain is a CNAME so this is a CNAME. Otherwise replace
  182. // the name with the RDATA of the CNAME and call ourself
  183. // recursively.
  184. if (ansrrset[i]->getType() == RRType::CNAME()) {
  185. // Don't consider it in the next iteration (although we
  186. // can still access it for now).
  187. present[i] = 0;
  188. --size;
  189. if (size == 0) {
  190. return (CNAME);
  191. }
  192. else {
  193. if (ansrrset[i]->getRdataCount() != 1) {
  194. // Multiple RDATA for a CNAME? This is invalid.
  195. return (NOTSINGLE);
  196. }
  197. RdataIteratorPtr it = ansrrset[i]->getRdataIterator();
  198. Name newname(it->getCurrent().toText());
  199. return cnameChase(newname, qtype, ansrrset, present,
  200. size);
  201. }
  202. } else {
  203. // We've got here because the element is not a CNAME. If
  204. // this is the last element and the type is the one we are
  205. // after, we've found the answer, or it is an error. If
  206. // there is more than one RRset left in the list we are
  207. // searching, we have extra data in the answer.
  208. if (ansrrset[i]->getType() == qtype) {
  209. if (size == 1) {
  210. return (ANSWERCNAME);
  211. } else {
  212. return (EXTRADATA);
  213. }
  214. }
  215. return (INVTYPE);
  216. }
  217. }
  218. }
  219. }
  220. // We get here if we've dropped off the end of the list without finding the
  221. // QNAME we are looking for. This means that the CNAME chain has ended
  222. // but there are additional RRsets in the data.
  223. return (EXTRADATA);
  224. }