123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259 |
- // Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
- //
- // Permission to use, copy, modify, and/or distribute this software for any
- // purpose with or without fee is hereby granted, provided that the above
- // copyright notice and this permission notice appear in all copies.
- //
- // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
- // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
- // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
- // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- // PERFORMANCE OF THIS SOFTWARE.
- // $Id$
- #include <cstddef>
- #include <vector>
- #include <resolver/response_classifier.h>
- #include <dns/name.h>
- #include <dns/opcode.h>
- #include <dns/rcode.h>
- #include <dns/rrset.h>
- using namespace isc::dns;
- using namespace std;
- // Classify the response in the "message" object.
- ResponseClassifier::Category ResponseClassifier::classify(
- const Question& question, const MessagePtr& message, bool tcignore)
- {
- // Check header bits
- if (!message->getHeaderFlag(Message::HEADERFLAG_QR)) {
- return (NOTRESPONSE); // Query-response bit not set in the response
- }
- // We only recognise responses to queries here
- if (message->getOpcode() != Opcode::QUERY()) {
- return (OPCODE);
- }
- // Apparently have a response. There must be a single question in it...
- const vector<QuestionPtr> msgquestion(message->beginQuestion(),
- message->endQuestion());
- if (msgquestion.size() != 1) {
- return (NOTONEQUEST); // Not one question in response question section
- }
- // ... and the question should be equal to the question given.
- // XXX: This means that "question" may not be the question sent by the
- // client. In the case of a CNAME response, the qname of subsequent
- // questions needs to be altered.
- if (question != *(msgquestion[0])) {
- return (MISMATQUEST);
- }
- // Check for Rcode-related errors.
- const Rcode& rcode = message->getRcode();
- if (rcode != Rcode::NOERROR()) {
- if (rcode == Rcode::NXDOMAIN()) {
- // No such domain. According to RFC2308, the domain referred to by
- // the QNAME does not exist, although there may be a CNAME in the
- // answer section and there may be an SOA and/or NS RRs in the
- // authority section (ignoring any DNSSEC RRs for now).
- //
- // Note the "may". There may not be anything. Also, note that if
- // there is a CNAME in the answer section, the authoritative server
- // has verified that the name given in the CNAME's RDATA field does
- // not exist. And that if a CNAME is returned in the answer, then
- // the QNAME of the RRs in the authority section will refer to the
- // authority for the CNAME's RDATA and not to the original question.
- //
- // Without doing further classification, it is sufficient to say
- // that if an NXDOMAIN is received, there was no translation of the
- // QNAME available.
- return (NXDOMAIN); // Received NXDOMAIN from parent.
- } else {
- // Not NXDOMAIN but not NOERROR either. Must be an RCODE-related
- // error.
- return (RCODE);
- }
- }
- // All seems OK and we can start looking at the content. However, one
- // more header check remains - was the response truncated? If so, we'll
- // probably want to re-query over TCP. However, in some circumstances we
- // might want to go with what we have. So give the caller the option of
- // ignoring the TC bit.
- if (message->getHeaderFlag(Message::HEADERFLAG_TC) && (!tcignore)) {
- return (TRUNCATED);
- }
- // By the time we get here, we're assured that the packet format is correct.
- // We now need to decide as to whether it is an answer, a CNAME, or a
- // referral. For this, we need to inspect the contents of the answer
- // and authority sections.
- const vector<RRsetPtr> answer(
- message->beginSection(Message::SECTION_ANSWER),
- message->endSection(Message::SECTION_ANSWER)
- );
- const vector<RRsetPtr> authority(
- message->beginSection(Message::SECTION_AUTHORITY),
- message->endSection(Message::SECTION_AUTHORITY)
- );
- // If there is nothing in the answer section, it is a referral - unless
- // there is nothing in the authority section
- if (answer.empty()) {
- if (authority.empty()) {
- return (EMPTY);
- } else {
- return (REFERRAL);
- }
- }
- // Look at two cases - one RRset in the answer and multiple RRsets in
- // the answer.
- if (answer.size() == 1) {
- // Does the name and class of the answer match that of the question?
- if ((answer[0]->getName() == question.getName()) &&
- (answer[0]->getClass() == question.getClass())) {
- // It does. How about the type of the response? The response
- // is an answer if the type matches that of the question, or if the
- // question was for type ANY. It is a CNAME reply if the answer
- // type is CNAME. And it is an error for anything else.
- if ((answer[0]->getType() == question.getType()) ||
- (question.getType() == RRType::ANY())) {
- return (ANSWER);
- } else if (answer[0]->getType() == RRType::CNAME()) {
- return (CNAME);
- } else {
- return (INVTYPE);
- }
- }
- else {
- // Either the name and/or class of the reply don't match that of
- // the question.
- return (INVNAMCLASS);
- }
- }
- // There are multiple RRsets in the answer. They should all have the same
- // QCLASS, else there is some error in the response.
- for (int i = 1; i < answer.size(); ++i) {
- if (answer[0]->getClass() != answer[i]->getClass()) {
- return (MULTICLASS);
- }
- }
- // If the request type was ANY and they all have the same QNAME, we have
- // an answer. But if they don't have the same QNAME, we must have an error;
- // the only way we could get different QNAMES in an answer is if one were a
- // CNAME - in which case there should no other record types at that QNAME.
- if (question.getType() == RRType::ANY()) {
- bool all_same = true;
- for (int i = 1; (i < answer.size()) && all_same; ++i) {
- all_same = (answer[0]->getName() == answer[i]->getName());
- }
- if (all_same) {
- return (ANSWER);
- } else {
- return (EXTRADATA);
- }
- }
- // Multiple RRs in the answer, and not all the same QNAME. This
- // is either an answer, a CNAME (in either case, there could be multiple
- // CNAMEs in the chain) or an error.
- //
- // So we need to follow the CNAME chain to resolve this. For this to work:
- //
- // a) There must be one RR that matches the name, class and type of
- // the question, and this is a CNAME.
- // b) The CNAME chain is followed until the end of the chain does not
- // exist (answer is a CNAME) or it is not of type CNAME (ANSWER).
- //
- // In the latter case, if there are additional RRs, it must be an error.
- vector<RRsetPtr> ansrrset(answer);
- vector<int> present(ansrrset.size(), 1);
- return cnameChase(question.getName(), question.getType(), ansrrset, present,
- ansrrset.size());
- }
- // Search the CNAME chain.
- ResponseClassifier::Category ResponseClassifier::cnameChase(
- const Name& qname, const RRType& qtype, vector<RRsetPtr>& ansrrset,
- vector<int>& present, size_t size)
- {
- // Search through the vector of RRset pointers until we find one with the
- // right QNAME.
- for (int i = 0; i < ansrrset.size(); ++i) {
- if (present[i]) {
- // This entry has not been logically removed, so look at it.
- if (ansrrset[i]->getName() == qname) {
- // QNAME match. If this RRset is a CNAME, remove it from
- // further consideration. If nothing is left, the end of the
- // chain is a CNAME so this is a CNAME. Otherwise replace
- // the name with the RDATA of the CNAME and call ourself
- // recursively.
- if (ansrrset[i]->getType() == RRType::CNAME()) {
- // Don't consider it in the next iteration (although we
- // can still access it for now).
- present[i] = 0;
- --size;
- if (size == 0) {
- return (CNAME);
- }
- else {
- if (ansrrset[i]->getRdataCount() != 1) {
- // Multiple RDATA for a CNAME? This is invalid.
- return (NOTSINGLE);
- }
- RdataIteratorPtr it = ansrrset[i]->getRdataIterator();
- Name newname(it->getCurrent().toText());
- return cnameChase(newname, qtype, ansrrset, present,
- size);
- }
- } else {
- // We've got here because the element is not a CNAME. If
- // this is the last element and the type is the one we are
- // after, we've found the answer, or it is an error. If
- // there is more than one RRset left in the list we are
- // searching, we have extra data in the answer.
- if (ansrrset[i]->getType() == qtype) {
- if (size == 1) {
- return (ANSWERCNAME);
- } else {
- return (EXTRADATA);
- }
- }
- return (INVTYPE);
- }
- }
- }
- }
- // We get here if we've dropped off the end of the list without finding the
- // QNAME we are looking for. This means that the CNAME chain has ended
- // but there are additional RRsets in the data.
- return (EXTRADATA);
- }
|