]>
Commit | Line | Data |
---|---|---|
12c86877 | 1 | /* |
12471842 PL |
2 | * This file is part of PowerDNS or dnsdist. |
3 | * Copyright -- PowerDNS.COM B.V. and its contributors | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of version 2 of the GNU General Public License as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | * In addition, for the avoidance of any doubt, permission is granted to | |
10 | * link this program with OpenSSL and to (re)distribute the binaries | |
11 | * produced as the result of such linking. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program; if not, write to the Free Software | |
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
21 | */ | |
870a0fe4 AT |
22 | #ifdef HAVE_CONFIG_H |
23 | #include "config.h" | |
24 | #endif | |
12c86877 BH |
25 | #include "utility.hh" |
26 | #include <cstdio> | |
12c86877 BH |
27 | #include <cstdlib> |
28 | #include <sys/types.h> | |
12c86877 | 29 | #include <iostream> |
12c86877 | 30 | #include <string> |
b8e0f341 | 31 | #include <boost/tokenizer.hpp> |
14155043 | 32 | #include <boost/functional/hash.hpp> |
b8e0f341 | 33 | #include <boost/algorithm/string.hpp> |
e0db069a | 34 | #include <openssl/hmac.h> |
12c86877 | 35 | #include <algorithm> |
fa8fd4d2 | 36 | |
9c92ad4b | 37 | #include "dnsseckeeper.hh" |
12c86877 BH |
38 | #include "dns.hh" |
39 | #include "dnsbackend.hh" | |
ea892f70 | 40 | #include "ednsoptions.hh" |
37063755 | 41 | #include "ednscookies.hh" |
5c409fa2 | 42 | #include "pdnsexception.hh" |
12c86877 BH |
43 | #include "dnspacket.hh" |
44 | #include "logger.hh" | |
45 | #include "arguments.hh" | |
b8e0f341 BH |
46 | #include "dnswriter.hh" |
47 | #include "dnsparser.hh" | |
7f7b8d55 | 48 | #include "dnsrecords.hh" |
af184eb0 | 49 | #include "dnssecinfra.hh" |
20665beb | 50 | #include "base64.hh" |
af7d3ea6 | 51 | #include "ednssubnet.hh" |
b08f1315 | 52 | #include "gss_context.hh" |
d2116c15 | 53 | #include "dns_random.hh" |
bbec1961 | 54 | #include "shuffle.hh" |
af7d3ea6 BH |
55 | |
56 | bool DNSPacket::s_doEDNSSubnetProcessing; | |
37063755 PL |
57 | bool DNSPacket::s_doEDNSCookieProcessing; |
58 | string DNSPacket::s_EDNSCookieKey; | |
15668f6a | 59 | uint16_t DNSPacket::s_udpTruncationThreshold; |
f15149ab | 60 | |
eace2c24 | 61 | DNSPacket::DNSPacket(bool isQuery): d_isQuery(isQuery) |
12c86877 | 62 | { |
b8874063 | 63 | memset(&d, 0, sizeof(d)); |
12c86877 BH |
64 | } |
65 | ||
1f07a63f | 66 | const string& DNSPacket::getString(bool throwsOnTruncation) |
b8e0f341 BH |
67 | { |
68 | if(!d_wrapped) | |
1f07a63f | 69 | wrapup(throwsOnTruncation); |
b8e0f341 | 70 | |
78bcb858 | 71 | return d_rawpacket; |
b8e0f341 BH |
72 | } |
73 | ||
4172a5b2 PD |
74 | string DNSPacket::getRemoteString() const |
75 | { | |
76 | string ret; | |
77 | ||
78 | ret = getRemote().toString(); | |
79 | ||
80 | if (d_inner_remote) { | |
81 | ret += "(" + d_inner_remote->toString() + ")"; | |
82 | } | |
83 | ||
84 | if(hasEDNSSubnet()) { | |
85 | ret += "<-" + getRealRemote().toString(); | |
86 | } | |
87 | ||
88 | return ret; | |
89 | } | |
90 | ||
91 | string DNSPacket::getRemoteStringWithPort() const | |
92 | { | |
93 | string ret; | |
94 | ||
95 | ret = getRemote().toStringWithPort(); | |
96 | ||
97 | if (d_inner_remote) { | |
98 | ret += "(" + d_inner_remote->toStringWithPort() + ")"; | |
99 | } | |
100 | ||
101 | if(hasEDNSSubnet()) { | |
102 | ret += "<-" + getRealRemote().toString(); | |
103 | } | |
104 | ||
105 | return ret; | |
106 | } | |
107 | ||
b9b4da23 | 108 | ComboAddress DNSPacket::getRemote() const |
12c86877 | 109 | { |
b9b4da23 | 110 | return d_remote; |
12c86877 BH |
111 | } |
112 | ||
4172a5b2 PD |
113 | ComboAddress DNSPacket::getInnerRemote() const |
114 | { | |
115 | if (d_inner_remote) | |
116 | return *d_inner_remote; | |
117 | return d_remote; | |
118 | } | |
119 | ||
092f210a | 120 | uint16_t DNSPacket::getRemotePort() const |
288f4aa9 | 121 | { |
d06799d4 | 122 | return d_remote.sin4.sin_port; |
288f4aa9 | 123 | } |
12c86877 | 124 | |
12c86877 BH |
125 | void DNSPacket::setRcode(int v) |
126 | { | |
127 | d.rcode=v; | |
128 | } | |
129 | ||
130 | void DNSPacket::setAnswer(bool b) | |
131 | { | |
132 | if(b) { | |
78bcb858 | 133 | d_rawpacket.assign(12,(char)0); |
12c86877 BH |
134 | memset((void *)&d,0,sizeof(d)); |
135 | ||
136 | d.qr=b; | |
137 | } | |
138 | } | |
139 | ||
140 | void DNSPacket::setA(bool b) | |
141 | { | |
142 | d.aa=b; | |
143 | } | |
144 | ||
092f210a | 145 | void DNSPacket::setID(uint16_t id) |
12c86877 BH |
146 | { |
147 | d.id=id; | |
148 | } | |
149 | ||
150 | void DNSPacket::setRA(bool b) | |
151 | { | |
152 | d.ra=b; | |
153 | } | |
154 | ||
155 | void DNSPacket::setRD(bool b) | |
156 | { | |
157 | d.rd=b; | |
158 | } | |
159 | ||
092f210a | 160 | void DNSPacket::setOpcode(uint16_t opcode) |
12c86877 BH |
161 | { |
162 | d.opcode=opcode; | |
163 | } | |
164 | ||
77235722 BH |
165 | void DNSPacket::clearRecords() |
166 | { | |
9c92ad4b | 167 | d_rrs.clear(); |
55b524a0 | 168 | d_dedup.clear(); |
77235722 BH |
169 | } |
170 | ||
9bbcf03a | 171 | void DNSPacket::addRecord(DNSZoneRecord&& rr) |
12c86877 | 172 | { |
a4a67412 | 173 | // this removes duplicates from the packet. |
174 | // in case we are not compressing for AXFR, no such checking is performed! | |
55b524a0 | 175 | |
3d00ef90 | 176 | if(d_compress) { |
d06dcda4 | 177 | std::string ser = rr.dr.getContent()->serialize(rr.dr.d_name); |
9d40dd1f | 178 | auto hash = boost::hash< std::pair<DNSName, std::string> >()({rr.dr.d_name, ser}); |
179 | if(d_dedup.count(hash)) { // might be a dup | |
f80ebc05 O |
180 | for(auto & i : d_rrs) { |
181 | if(rr.dr == i.dr) // XXX SUPER SLOW | |
4957a608 | 182 | return; |
55b524a0 | 183 | } |
90ba52e0 | 184 | } |
9d40dd1f | 185 | d_dedup.insert(hash); |
a4a67412 | 186 | } |
9bbcf03a | 187 | d_rrs.push_back(std::move(rr)); |
12c86877 BH |
188 | } |
189 | ||
90ba52e0 | 190 | vector<DNSZoneRecord*> DNSPacket::getAPRecords() |
12c86877 | 191 | { |
90ba52e0 | 192 | vector<DNSZoneRecord*> arrs; |
12c86877 | 193 | |
f80ebc05 | 194 | for(auto & i : d_rrs) |
12c86877 | 195 | { |
f80ebc05 O |
196 | if(i.dr.d_place!=DNSResourceRecord::ADDITIONAL && |
197 | (i.dr.d_type==QType::MX || | |
198 | i.dr.d_type==QType::NS || | |
199 | i.dr.d_type==QType::SRV)) | |
4957a608 | 200 | { |
f80ebc05 | 201 | arrs.push_back(&i); |
4957a608 | 202 | } |
12c86877 | 203 | } |
12c86877 | 204 | return arrs; |
12c86877 BH |
205 | } |
206 | ||
bdbee377 PL |
207 | vector<DNSZoneRecord*> DNSPacket::getServiceRecords() |
208 | { | |
209 | vector<DNSZoneRecord*> arrs; | |
210 | ||
211 | for(auto & i : d_rrs) { | |
212 | if (i.dr.d_type==QType::SVCB || | |
213 | i.dr.d_type==QType::HTTPS) { | |
214 | arrs.push_back(&i); | |
215 | } | |
216 | } | |
217 | return arrs; | |
218 | } | |
219 | ||
90ba52e0 | 220 | vector<DNSZoneRecord*> DNSPacket::getAnswerRecords() |
9c92ad4b | 221 | { |
90ba52e0 | 222 | vector<DNSZoneRecord*> arrs; |
9c92ad4b | 223 | |
403a3a42 | 224 | for(auto & rr : d_rrs) |
9c92ad4b | 225 | { |
403a3a42 O |
226 | if(rr.dr.d_place!=DNSResourceRecord::ADDITIONAL) |
227 | arrs.push_back(&rr); | |
9c92ad4b BH |
228 | } |
229 | return arrs; | |
230 | } | |
231 | ||
232 | ||
12c86877 BH |
233 | void DNSPacket::setCompress(bool compress) |
234 | { | |
235 | d_compress=compress; | |
78bcb858 | 236 | d_rawpacket.reserve(65000); |
9c92ad4b | 237 | d_rrs.reserve(200); |
12c86877 BH |
238 | } |
239 | ||
c2826d2e | 240 | bool DNSPacket::couldBeCached() const |
7f7b8d55 | 241 | { |
37063755 PL |
242 | return !d_wantsnsid && qclass==QClass::IN && !d_havetsig && |
243 | !(d_haveednscookie && s_doEDNSCookieProcessing); | |
7f7b8d55 | 244 | } |
20665beb | 245 | |
b35ea8ec PD |
246 | unsigned int DNSPacket::getMinTTL() |
247 | { | |
248 | unsigned int minttl = UINT_MAX; | |
90ba52e0 | 249 | for(const DNSZoneRecord& rr : d_rrs) { |
250 | if (rr.dr.d_ttl < minttl) | |
251 | minttl = rr.dr.d_ttl; | |
b35ea8ec PD |
252 | } |
253 | ||
254 | return minttl; | |
255 | } | |
256 | ||
9951e2d0 KM |
257 | bool DNSPacket::isEmpty() |
258 | { | |
259 | return (d_rrs.empty()); | |
260 | } | |
261 | ||
12c86877 BH |
262 | /** Must be called before attempting to access getData(). This function stuffs all resource |
263 | * records found in rrs into the data buffer. It also frees resource records queued for us. | |
264 | */ | |
1f07a63f | 265 | void DNSPacket::wrapup(bool throwsOnTruncation) |
12c86877 BH |
266 | { |
267 | if(d_wrapped) { | |
268 | return; | |
269 | } | |
51a3a4d4 | 270 | |
90ba52e0 | 271 | DNSZoneRecord rr; |
272 | vector<DNSZoneRecord>::iterator pos; | |
12c86877 | 273 | |
12c86877 BH |
274 | // we now need to order rrs so that the different sections come at the right place |
275 | // we want a stable sort, based on the d_place field | |
276 | ||
90ba52e0 | 277 | stable_sort(d_rrs.begin(),d_rrs.end(), [](const DNSZoneRecord& a, const DNSZoneRecord& b) { |
278 | return a.dr.d_place < b.dr.d_place; | |
279 | }); | |
4c5d6da9 | 280 | static bool mustNotShuffle = ::arg().mustDo("no-shuffle"); |
da73ef3c | 281 | |
4c5d6da9 | 282 | if(!d_tcp && !mustNotShuffle) { |
bbec1961 | 283 | pdns::shuffle(d_rrs); |
ff6a1e7b | 284 | } |
12c86877 BH |
285 | d_wrapped=true; |
286 | ||
b8e0f341 | 287 | vector<uint8_t> packet; |
adf13442 | 288 | DNSPacketWriter pw(packet, qdomain, qtype.getCode(), qclass); |
12c86877 | 289 | |
6f966e0b | 290 | pw.getHeader()->rcode=d.rcode; |
da22f3df | 291 | pw.getHeader()->opcode = d.opcode; |
b8e0f341 BH |
292 | pw.getHeader()->aa=d.aa; |
293 | pw.getHeader()->ra=d.ra; | |
294 | pw.getHeader()->qr=d.qr; | |
295 | pw.getHeader()->id=d.id; | |
296 | pw.getHeader()->rd=d.rd; | |
abc8f3f9 | 297 | pw.getHeader()->tc=d.tc; |
298 | ||
7f7b8d55 | 299 | DNSPacketWriter::optvect_t opts; |
e0db069a PD |
300 | |
301 | /* optsize is expected to hold an upper bound of data that will be | |
302 | added after actual record data - i.e. OPT, TSIG, perhaps one day | |
303 | XPF. Because of the way `pw` incrementally writes the packet, we | |
304 | cannot easily 'go back' and remove a few records. So, to prevent | |
305 | going over our maximum size, we keep our (potential) extra data | |
306 | in mind. | |
307 | ||
308 | This means that sometimes we'll send TC even if we'd end up with | |
309 | a few bytes to spare, but so be it. | |
310 | */ | |
311 | size_t optsize = 0; | |
312 | ||
e65929c6 PL |
313 | if (d_haveednssection || d_dnssecOk) { |
314 | /* root label (1), type (2), class (2), ttl (4) + rdlen (2) */ | |
e0db069a | 315 | optsize = 11; |
e65929c6 | 316 | } |
e0db069a | 317 | |
7f7b8d55 | 318 | if(d_wantsnsid) { |
8ca1a435 | 319 | const static string mode_server_id=::arg()["server-id"]; |
49e8d5d5 | 320 | if(mode_server_id != "disabled") { |
e32a8d46 | 321 | opts.emplace_back(EDNSOptionCode::NSID, mode_server_id); |
e65929c6 | 322 | optsize += EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE + mode_server_id.size(); |
8ca1a435 | 323 | } |
7f7b8d55 BH |
324 | } |
325 | ||
e0db069a PD |
326 | if (d_haveednssubnet) |
327 | { | |
328 | // this is an upper bound | |
e65929c6 | 329 | optsize += EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE + 2 + 1 + 1; // code+len+family+src len+scope len |
d14121a8 | 330 | optsize += d_eso.source.isIPv4() ? 4 : 16; |
e0db069a PD |
331 | } |
332 | ||
37063755 PL |
333 | if (d_haveednscookie) { |
334 | if (d_eco.isWellFormed()) { | |
ca250bda | 335 | optsize += EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE + EDNSCookiesOpt::EDNSCookieOptSize; |
37063755 PL |
336 | } |
337 | } | |
338 | ||
e0db069a PD |
339 | if (d_trc.d_algoName.countLabels()) |
340 | { | |
341 | // TSIG is not OPT, but we count it in optsize anyway | |
342 | optsize += d_trc.d_algoName.wirelength() + 3 + 1 + 2; // algo + time + fudge + maclen | |
343 | optsize += EVP_MAX_MD_SIZE + 2 + 2 + 2 + 0; // mac + origid + ercode + otherdatalen + no other data | |
344 | ||
345 | static_assert(EVP_MAX_MD_SIZE <= 64, "EVP_MAX_MD_SIZE is overly huge on this system, please check"); | |
346 | } | |
347 | ||
37063755 | 348 | if(!d_rrs.empty() || !opts.empty() || d_haveednssubnet || d_haveednssection || d_haveednscookie) { |
6f966e0b | 349 | try { |
af7d3ea6 | 350 | uint8_t maxScopeMask=0; |
9c92ad4b | 351 | for(pos=d_rrs.begin(); pos < d_rrs.end(); ++pos) { |
af7d3ea6 | 352 | maxScopeMask = max(maxScopeMask, pos->scopeMask); |
50e2abc0 | 353 | |
90ba52e0 | 354 | pw.startRecord(pos->dr.d_name, pos->dr.d_type, pos->dr.d_ttl, pos->dr.d_class, pos->dr.d_place); |
d06dcda4 | 355 | pos->dr.getContent()->toPacket(pw); |
e0db069a | 356 | if(pw.size() + optsize > (d_tcp ? 65535 : getMaxReplyLen())) { |
1f07a63f PD |
357 | if (throwsOnTruncation) { |
358 | throw PDNSException("attempt to write an oversized chunk"); | |
359 | } | |
95369951 | 360 | pw.rollback(); |
06ba4702 KM |
361 | pw.truncate(); |
362 | pw.getHeader()->tc=1; | |
96b59a50 | 363 | goto noCommit; |
95369951 BH |
364 | } |
365 | } | |
732b7143 PD |
366 | |
367 | // if(!pw.getHeader()->tc) // protect against double commit from addSignature | |
368 | ||
369 | if(!d_rrs.empty()) pw.commit(); | |
96b59a50 | 370 | |
732b7143 | 371 | noCommit:; |
af7d3ea6 BH |
372 | |
373 | if(d_haveednssubnet) { | |
af7d3ea6 | 374 | EDNSSubnetOpts eso = d_eso; |
50e2abc0 | 375 | // use the scopeMask from the resolver, if it is greater - issue #5469 |
376 | maxScopeMask = max(maxScopeMask, eso.scope.getBits()); | |
af7d3ea6 BH |
377 | eso.scope = Netmask(eso.source.getNetwork(), maxScopeMask); |
378 | ||
379 | string opt = makeEDNSSubnetOptsString(eso); | |
e32a8d46 | 380 | opts.emplace_back(8, opt); // 'EDNS SUBNET' |
af7d3ea6 | 381 | } |
9c92ad4b | 382 | |
37063755 | 383 | if (d_haveednscookie && d_eco.isWellFormed()) { |
344f7c07 | 384 | d_eco.makeServerCookie(s_EDNSCookieKey, getInnerRemote()); |
e32a8d46 | 385 | opts.emplace_back(EDNSOptionCode::COOKIE, d_eco.makeOptString()); |
37063755 PL |
386 | } |
387 | ||
f20d5371 | 388 | if(!opts.empty() || d_haveednssection || d_dnssecOk) |
75fc7cbc | 389 | { |
298fabc3 | 390 | pw.addOpt(s_udpTruncationThreshold, d_ednsrcode, d_dnssecOk ? EDNSOpts::DNSSECOK : 0, opts); |
95369951 | 391 | pw.commit(); |
75fc7cbc | 392 | } |
6f966e0b | 393 | } |
5172cb78 | 394 | catch(std::exception& e) { |
e6a9dde5 | 395 | g_log<<Logger::Warning<<"Exception: "<<e.what()<<endl; |
6f966e0b | 396 | throw; |
12c86877 | 397 | } |
b8e0f341 | 398 | } |
78bcb858 | 399 | |
4a51ff72 | 400 | if(d_trc.d_algoName.countLabels()) |
ea3816cf | 401 | addTSIG(pw, d_trc, d_tsigkeyname, d_tsigsecret, d_tsigprevious, d_tsigtimersonly); |
78bcb858 | 402 | |
90ba52e0 | 403 | d_rawpacket.assign((char*)&packet[0], packet.size()); // XXX we could do this natively on a vector.. |
3e8216c8 | 404 | |
da286f66 | 405 | // copy RR counts so they can be read later |
3e8216c8 PD |
406 | d.qdcount = pw.getHeader()->qdcount; |
407 | d.ancount = pw.getHeader()->ancount; | |
408 | d.nscount = pw.getHeader()->nscount; | |
409 | d.arcount = pw.getHeader()->arcount; | |
12c86877 BH |
410 | } |
411 | ||
c2f3be9d | 412 | void DNSPacket::setQuestion(int op, const DNSName &qd, int newqtype) |
12c86877 BH |
413 | { |
414 | memset(&d,0,sizeof(d)); | |
a410b176 | 415 | d.id=dns_random_uint16(); |
12c86877 BH |
416 | d.rd=d.tc=d.aa=false; |
417 | d.qr=false; | |
418 | d.qdcount=1; // is htons'ed later on | |
419 | d.ancount=d.arcount=d.nscount=0; | |
420 | d.opcode=op; | |
288f4aa9 BH |
421 | qdomain=qd; |
422 | qtype=newqtype; | |
12c86877 BH |
423 | } |
424 | ||
c2826d2e RG |
425 | /** convenience function for creating a reply packet from a question packet. */ |
426 | std::unique_ptr<DNSPacket> DNSPacket::replyPacket() const | |
12c86877 | 427 | { |
c2826d2e | 428 | auto r=make_unique<DNSPacket>(false); |
12c86877 | 429 | r->setSocket(d_socket); |
2b6f1436 | 430 | r->d_anyLocal=d_anyLocal; |
d06799d4 | 431 | r->setRemote(&d_remote); |
4172a5b2 | 432 | r->d_inner_remote=d_inner_remote; |
12c86877 BH |
433 | r->setAnswer(true); // this implies the allocation of the header |
434 | r->setA(true); // and we are authoritative | |
d563b11a | 435 | r->setRA(false); // no recursion available |
78bcb858 | 436 | r->setRD(d.rd); // if you wanted to recurse, answer will say you wanted it |
12c86877 BH |
437 | r->setID(d.id); |
438 | r->setOpcode(d.opcode); | |
439 | ||
12c86877 | 440 | r->d_dt=d_dt; |
092c9cc4 | 441 | r->d.qdcount=1; |
f28307ad | 442 | r->d_tcp = d_tcp; |
b8e0f341 BH |
443 | r->qdomain = qdomain; |
444 | r->qtype = qtype; | |
adf13442 | 445 | r->qclass = qclass; |
657e9124 | 446 | r->d_maxreplylen = d_maxreplylen; |
7f7b8d55 | 447 | r->d_wantsnsid = d_wantsnsid; |
9c92ad4b | 448 | r->d_dnssecOk = d_dnssecOk; |
af7d3ea6 | 449 | r->d_eso = d_eso; |
37063755 | 450 | r->d_eco = d_eco; |
af7d3ea6 | 451 | r->d_haveednssubnet = d_haveednssubnet; |
f20d5371 | 452 | r->d_haveednssection = d_haveednssection; |
37063755 | 453 | r->d_haveednscookie = d_haveednscookie; |
298fabc3 AT |
454 | r->d_ednsversion = 0; |
455 | r->d_ednsrcode = 0; | |
456 | ||
675fa24c | 457 | if(d_tsigkeyname.countLabels()) { |
78bcb858 BH |
458 | r->d_tsigkeyname = d_tsigkeyname; |
459 | r->d_tsigprevious = d_tsigprevious; | |
460 | r->d_trc = d_trc; | |
461 | r->d_tsigsecret = d_tsigsecret; | |
462 | r->d_tsigtimersonly = d_tsigtimersonly; | |
463 | } | |
6dc26cf0 | 464 | r->d_havetsig = d_havetsig; |
12c86877 BH |
465 | return r; |
466 | } | |
467 | ||
c2826d2e | 468 | void DNSPacket::spoofQuestion(const DNSPacket& qd) |
12c86877 | 469 | { |
b8e0f341 | 470 | d_wrapped=true; // if we do this, don't later on wrapup |
7de6b0d5 | 471 | |
63e365db PD |
472 | int labellen; |
473 | string::size_type i=sizeof(d); | |
474 | ||
475 | for(;;) { | |
c2826d2e | 476 | labellen = qd.d_rawpacket[i]; |
63e365db PD |
477 | if(!labellen) break; |
478 | i++; | |
c2826d2e | 479 | d_rawpacket.replace(i, labellen, qd.d_rawpacket, i, labellen); |
63e365db | 480 | i = i + labellen; |
7de6b0d5 | 481 | } |
b8e0f341 | 482 | } |
12c86877 | 483 | |
a683e8bd | 484 | int DNSPacket::noparse(const char *mesg, size_t length) |
07676510 | 485 | { |
78bcb858 | 486 | d_rawpacket.assign(mesg,length); |
07676510 | 487 | if(length < 12) { |
e6a9dde5 | 488 | g_log << Logger::Debug << "Ignoring packet: too short ("<<length<<" < 12) from " |
4172a5b2 | 489 | << getRemoteStringWithPort(); |
07676510 BH |
490 | return -1; |
491 | } | |
492 | d_wantsnsid=false; | |
07676510 | 493 | d_maxreplylen=512; |
78bcb858 | 494 | memcpy((void *)&d,(const void *)d_rawpacket.c_str(),12); |
07676510 BH |
495 | return 0; |
496 | } | |
497 | ||
675fa24c | 498 | void DNSPacket::setTSIGDetails(const TSIGRecordContent& tr, const DNSName& keyname, const string& secret, const string& previous, bool timersonly) |
78bcb858 BH |
499 | { |
500 | d_trc=tr; | |
e49d0a2f | 501 | d_trc.d_origID = (((d.id & 0xFF)<<8) | ((d.id & 0xFF00)>>8)); |
78bcb858 BH |
502 | d_tsigkeyname = keyname; |
503 | d_tsigsecret = secret; | |
504 | d_tsigprevious = previous; | |
505 | d_tsigtimersonly=timersonly; | |
506 | } | |
507 | ||
ea3816cf | 508 | bool DNSPacket::getTSIGDetails(TSIGRecordContent* trc, DNSName* keyname, uint16_t* tsigPosOut) const |
78bcb858 | 509 | { |
27c0050c | 510 | MOADNSParser mdp(d_isQuery, d_rawpacket); |
ea3816cf RG |
511 | uint16_t tsigPos = mdp.getTSIGPos(); |
512 | if(!tsigPos) | |
78bcb858 | 513 | return false; |
5ad07f9e | 514 | |
78bcb858 | 515 | bool gotit=false; |
5ad07f9e | 516 | for(const auto & answer : mdp.d_answers) { |
57e6c93a | 517 | if(answer.first.d_type == QType::TSIG && answer.first.d_class == QType::ANY) { |
6b3e413d | 518 | // cast can fail, f.e. if d_content is an UnknownRecordContent. |
d06dcda4 | 519 | auto content = getRR<TSIGRecordContent>(answer.first); |
6b3e413d | 520 | if (!content) { |
e6a9dde5 | 521 | g_log<<Logger::Error<<"TSIG record has no or invalid content (invalid packet)"<<endl; |
6b3e413d CH |
522 | return false; |
523 | } | |
524 | *trc = *content; | |
5ad07f9e | 525 | *keyname = answer.first.d_name; |
6b3e413d | 526 | gotit=true; |
78bcb858 BH |
527 | } |
528 | } | |
529 | if(!gotit) | |
530 | return false; | |
ea3816cf RG |
531 | |
532 | if (tsigPosOut) { | |
533 | *tsigPosOut = tsigPos; | |
534 | } | |
5ad07f9e | 535 | |
78bcb858 BH |
536 | return true; |
537 | } | |
538 | ||
675fa24c | 539 | bool DNSPacket::getTKEYRecord(TKEYRecordContent *tr, DNSName *keyname) const |
4429ed62 | 540 | { |
27c0050c | 541 | MOADNSParser mdp(d_isQuery, d_rawpacket); |
4429ed62 AT |
542 | bool gotit=false; |
543 | ||
5ad07f9e | 544 | for(const auto & answer : mdp.d_answers) { |
4429ed62 | 545 | if (gotit) { |
e6a9dde5 | 546 | g_log<<Logger::Error<<"More than one TKEY record found in query"<<endl; |
4429ed62 AT |
547 | return false; |
548 | } | |
549 | ||
5ad07f9e | 550 | if(answer.first.d_type == QType::TKEY) { |
6b3e413d | 551 | // cast can fail, f.e. if d_content is an UnknownRecordContent. |
d06dcda4 | 552 | auto content = getRR<TKEYRecordContent>(answer.first); |
6b3e413d | 553 | if (!content) { |
e6a9dde5 | 554 | g_log<<Logger::Error<<"TKEY record has no or invalid content (invalid packet)"<<endl; |
6b3e413d CH |
555 | return false; |
556 | } | |
557 | *tr = *content; | |
5ad07f9e | 558 | *keyname = answer.first.d_name; |
4429ed62 AT |
559 | gotit=true; |
560 | } | |
561 | } | |
562 | ||
563 | return gotit; | |
564 | } | |
565 | ||
b8e0f341 BH |
566 | /** This function takes data from the network, possibly received with recvfrom, and parses |
567 | it into our class. Results of calling this function multiple times on one packet are | |
568 | unknown. Returns -1 if the packet cannot be parsed. | |
569 | */ | |
a683e8bd | 570 | int DNSPacket::parse(const char *mesg, size_t length) |
9f064e90 | 571 | try |
b8e0f341 | 572 | { |
78bcb858 | 573 | d_rawpacket.assign(mesg,length); |
0e7f49b5 | 574 | d_wrapped=true; |
b8e0f341 | 575 | if(length < 12) { |
77562377 | 576 | g_log << Logger::Debug << "Ignoring packet: too short from " |
80464692 | 577 | << getRemoteString() << endl; |
b8e0f341 BH |
578 | return -1; |
579 | } | |
7f7b8d55 | 580 | |
27c0050c | 581 | MOADNSParser mdp(d_isQuery, d_rawpacket); |
7f7b8d55 BH |
582 | EDNSOpts edo; |
583 | ||
584 | // ANY OPTION WHICH *MIGHT* BE SET DOWN BELOW SHOULD BE CLEARED FIRST! | |
585 | ||
586 | d_wantsnsid=false; | |
9c92ad4b | 587 | d_dnssecOk=false; |
78bcb858 | 588 | d_havetsig = mdp.getTSIGPos(); |
af7d3ea6 | 589 | d_haveednssubnet = false; |
f20d5371 | 590 | d_haveednssection = false; |
37063755 | 591 | d_haveednscookie = false; |
29992caa | 592 | d_ednscookievalid = false; |
9c92ad4b | 593 | |
7f7b8d55 | 594 | if(getEDNSOpts(mdp, &edo)) { |
f20d5371 | 595 | d_haveednssection=true; |
7a9b7c95 RG |
596 | /* rfc6891 6.2.3: |
597 | "Values lower than 512 MUST be treated as equal to 512." | |
598 | */ | |
779b8a25 | 599 | d_ednsRawPacketSizeLimit=edo.d_packetsize; |
7a9b7c95 | 600 | d_maxreplylen=std::min(std::max(static_cast<uint16_t>(512), edo.d_packetsize), s_udpTruncationThreshold); |
50e2abc0 | 601 | if((edo.d_extFlags & EDNSOpts::DNSSECOK) != 0) { |
9c92ad4b | 602 | d_dnssecOk=true; |
50e2abc0 | 603 | } |
7f7b8d55 | 604 | |
403a3a42 O |
605 | for(const auto & option : edo.d_options) { |
606 | if(option.first == EDNSOptionCode::NSID) { | |
ea892f70 | 607 | d_wantsnsid=true; |
7f7b8d55 | 608 | } |
403a3a42 O |
609 | else if(s_doEDNSSubnetProcessing && (option.first == EDNSOptionCode::ECS)) { // 'EDNS SUBNET' |
610 | if(getEDNSSubnetOptsFromString(option.second, &d_eso)) { | |
af7d3ea6 BH |
611 | //cerr<<"Parsed, source: "<<d_eso.source.toString()<<", scope: "<<d_eso.scope.toString()<<", family = "<<d_eso.scope.getNetwork().sin4.sin_family<<endl; |
612 | d_haveednssubnet=true; | |
613 | } | |
614 | } | |
37063755 PL |
615 | else if (s_doEDNSCookieProcessing && option.first == EDNSOptionCode::COOKIE) { |
616 | d_haveednscookie = true; | |
617 | d_eco.makeFromString(option.second); | |
29992caa | 618 | d_ednscookievalid = d_eco.isValid(s_EDNSCookieKey, d_remote); |
37063755 | 619 | } |
af7d3ea6 BH |
620 | else { |
621 | // cerr<<"Have an option #"<<iter->first<<": "<<makeHexDump(iter->second)<<endl; | |
622 | } | |
7f7b8d55 | 623 | } |
298fabc3 AT |
624 | d_ednsversion = edo.d_version; |
625 | d_ednsrcode = edo.d_extRCode; | |
779b8a25 | 626 | } |
7f7b8d55 | 627 | else { |
657e9124 | 628 | d_maxreplylen=512; |
779b8a25 | 629 | d_ednsRawPacketSizeLimit=-1; |
7f7b8d55 | 630 | } |
12c86877 | 631 | |
78bcb858 | 632 | memcpy((void *)&d,(const void *)d_rawpacket.c_str(),12); |
b8e0f341 | 633 | qdomain=mdp.d_qname; |
c2f3be9d PD |
634 | // if(!qdomain.empty()) // strip dot |
635 | // boost::erase_tail(qdomain, 1); | |
12c86877 | 636 | |
b8e0f341 BH |
637 | if(!ntohs(d.qdcount)) { |
638 | if(!d_tcp) { | |
80464692 | 639 | g_log << Logger::Debug << "No question section in packet from " << getRemoteString() <<", RCode="<<RCode::to_s(d.rcode)<<endl; |
b8e0f341 | 640 | return -1; |
12c86877 BH |
641 | } |
642 | } | |
643 | ||
b8e0f341 BH |
644 | qtype=mdp.d_qtype; |
645 | qclass=mdp.d_qclass; | |
b7992721 RG |
646 | |
647 | d_trc = TSIGRecordContent(); | |
648 | ||
b8e0f341 | 649 | return 0; |
12c86877 | 650 | } |
5172cb78 | 651 | catch(std::exception& e) { |
9f064e90 BH |
652 | return -1; |
653 | } | |
12c86877 | 654 | |
78bcb858 | 655 | unsigned int DNSPacket::getMaxReplyLen() |
657e9124 BH |
656 | { |
657 | return d_maxreplylen; | |
658 | } | |
659 | ||
80397312 BH |
660 | void DNSPacket::setMaxReplyLen(int bytes) |
661 | { | |
662 | d_maxreplylen=bytes; | |
663 | } | |
664 | ||
b8e0f341 | 665 | //! Use this to set where this packet was received from or should be sent to |
4172a5b2 | 666 | void DNSPacket::setRemote(const ComboAddress *outer, std::optional<ComboAddress> inner) |
12c86877 | 667 | { |
4172a5b2 PD |
668 | d_remote=*outer; |
669 | if (inner) { | |
670 | d_inner_remote=*inner; | |
671 | } | |
672 | else { | |
673 | d_inner_remote.reset(); | |
674 | } | |
b8e0f341 | 675 | } |
12c86877 | 676 | |
02980dc2 | 677 | bool DNSPacket::hasEDNSSubnet() const |
fe498ace BH |
678 | { |
679 | return d_haveednssubnet; | |
680 | } | |
681 | ||
c2826d2e | 682 | bool DNSPacket::hasEDNS() const |
17d0b1e6 PD |
683 | { |
684 | return d_haveednssection; | |
685 | } | |
686 | ||
37063755 PL |
687 | bool DNSPacket::hasEDNSCookie() const |
688 | { | |
689 | return d_haveednscookie; | |
690 | } | |
691 | ||
692 | bool DNSPacket::hasWellFormedEDNSCookie() const | |
693 | { | |
694 | if (!d_haveednscookie) { | |
695 | return false; | |
696 | } | |
697 | return d_eco.isWellFormed(); | |
698 | } | |
699 | ||
38118dcb | 700 | bool DNSPacket::hasValidEDNSCookie() const |
37063755 PL |
701 | { |
702 | if (!hasWellFormedEDNSCookie()) { | |
703 | return false; | |
704 | } | |
29992caa | 705 | return d_ednscookievalid; |
37063755 PL |
706 | } |
707 | ||
af7d3ea6 BH |
708 | Netmask DNSPacket::getRealRemote() const |
709 | { | |
710 | if(d_haveednssubnet) | |
711 | return d_eso.source; | |
4172a5b2 | 712 | return Netmask(getInnerRemote()); |
af7d3ea6 BH |
713 | } |
714 | ||
b8e0f341 | 715 | void DNSPacket::setSocket(Utility::sock_t sock) |
12c86877 | 716 | { |
b8e0f341 | 717 | d_socket=sock; |
12c86877 BH |
718 | } |
719 | ||
b8e0f341 | 720 | void DNSPacket::commitD() |
12c86877 | 721 | { |
78bcb858 | 722 | d_rawpacket.replace(0,12,(char *)&d,12); // copy in d |
12c86877 BH |
723 | } |
724 | ||
ea3816cf | 725 | bool DNSPacket::checkForCorrectTSIG(UeberBackend* B, DNSName* keyname, string* secret, TSIGRecordContent* trc) const |
78bcb858 | 726 | { |
ea3816cf | 727 | uint16_t tsigPos; |
60a1c204 | 728 | |
ea3816cf | 729 | if (!this->getTSIGDetails(trc, keyname, &tsigPos)) { |
78bcb858 BH |
730 | return false; |
731 | } | |
785594c9 | 732 | |
ea3816cf RG |
733 | TSIGTriplet tt; |
734 | tt.name = *keyname; | |
735 | tt.algo = trc->d_algoName; | |
736 | if (tt.algo == DNSName("hmac-md5.sig-alg.reg.int")) | |
737 | tt.algo = DNSName("hmac-md5"); | |
9a459f10 | 738 | |
b08f1315 OM |
739 | if (tt.algo != DNSName("gss-tsig")) { |
740 | string secret64; | |
741 | if(!B->getTSIGKey(*keyname, tt.algo, secret64)) { | |
742 | g_log << Logger::Error << "Packet for domain '" << this->qdomain << "' denied: can't find TSIG key with name '" << *keyname << "' and algorithm '" << tt.algo << "'" << endl; | |
743 | return false; | |
744 | } | |
745 | B64Decode(secret64, *secret); | |
746 | tt.secret = *secret; | |
7f9ac49b AT |
747 | } |
748 | ||
ea3816cf | 749 | bool result; |
3213be1e | 750 | |
ea3816cf RG |
751 | try { |
752 | result = validateTSIG(d_rawpacket, tsigPos, tt, *trc, "", trc->d_mac, false); | |
9f782f99 | 753 | } |
ea3816cf | 754 | catch(const std::runtime_error& err) { |
e6a9dde5 | 755 | g_log<<Logger::Error<<"Packet for '"<<this->qdomain<<"' denied: "<<err.what()<<endl; |
ea3816cf | 756 | return false; |
78bcb858 | 757 | } |
785594c9 | 758 | |
78bcb858 BH |
759 | return result; |
760 | } | |
1a5ac5d7 | 761 | |
6fe866b4 | 762 | const DNSName& DNSPacket::getTSIGKeyname() const { |
1a5ac5d7 AT |
763 | return d_tsigkeyname; |
764 | } | |
dd0ef5ed | 765 | |
15e39ee4 | 766 | #ifdef ENABLE_GSS_TSIG |
dd0ef5ed OM |
767 | void DNSPacket::cleanupGSS(int rcode) |
768 | { | |
c113acc3 OM |
769 | // We cannot check g_doGssTSIG here, as this code is also included in other executables |
770 | // than pdns_server. | |
dd0ef5ed OM |
771 | if (rcode != RCode::NoError && d_tsig_algo == TSIG_GSS && !getTSIGKeyname().empty()) { |
772 | GssContext ctx(getTSIGKeyname()); | |
773 | ctx.destroy(); | |
774 | } | |
775 | } | |
15e39ee4 OM |
776 | #endif |
777 |