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