]>
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" |
5c409fa2 | 41 | #include "pdnsexception.hh" |
12c86877 BH |
42 | #include "dnspacket.hh" |
43 | #include "logger.hh" | |
44 | #include "arguments.hh" | |
b8e0f341 BH |
45 | #include "dnswriter.hh" |
46 | #include "dnsparser.hh" | |
7f7b8d55 | 47 | #include "dnsrecords.hh" |
af184eb0 | 48 | #include "dnssecinfra.hh" |
20665beb | 49 | #include "base64.hh" |
af7d3ea6 | 50 | #include "ednssubnet.hh" |
7f9ac49b | 51 | #include "gss_context.hh" |
d2116c15 | 52 | #include "dns_random.hh" |
bbec1961 | 53 | #include "shuffle.hh" |
af7d3ea6 BH |
54 | |
55 | bool DNSPacket::s_doEDNSSubnetProcessing; | |
15668f6a | 56 | uint16_t DNSPacket::s_udpTruncationThreshold; |
f15149ab | 57 | |
eace2c24 | 58 | DNSPacket::DNSPacket(bool isQuery): d_isQuery(isQuery) |
12c86877 | 59 | { |
b8874063 | 60 | memset(&d, 0, sizeof(d)); |
12c86877 BH |
61 | } |
62 | ||
78bcb858 | 63 | const string& DNSPacket::getString() |
b8e0f341 BH |
64 | { |
65 | if(!d_wrapped) | |
e02d0a59 | 66 | wrapup(); |
b8e0f341 | 67 | |
78bcb858 | 68 | return d_rawpacket; |
b8e0f341 BH |
69 | } |
70 | ||
b9b4da23 | 71 | ComboAddress DNSPacket::getRemote() const |
12c86877 | 72 | { |
b9b4da23 | 73 | return d_remote; |
12c86877 BH |
74 | } |
75 | ||
092f210a | 76 | uint16_t DNSPacket::getRemotePort() const |
288f4aa9 | 77 | { |
d06799d4 | 78 | return d_remote.sin4.sin_port; |
288f4aa9 | 79 | } |
12c86877 | 80 | |
12c86877 BH |
81 | void DNSPacket::setRcode(int v) |
82 | { | |
83 | d.rcode=v; | |
84 | } | |
85 | ||
86 | void DNSPacket::setAnswer(bool b) | |
87 | { | |
88 | if(b) { | |
78bcb858 | 89 | d_rawpacket.assign(12,(char)0); |
12c86877 BH |
90 | memset((void *)&d,0,sizeof(d)); |
91 | ||
92 | d.qr=b; | |
93 | } | |
94 | } | |
95 | ||
96 | void DNSPacket::setA(bool b) | |
97 | { | |
98 | d.aa=b; | |
99 | } | |
100 | ||
092f210a | 101 | void DNSPacket::setID(uint16_t id) |
12c86877 BH |
102 | { |
103 | d.id=id; | |
104 | } | |
105 | ||
106 | void DNSPacket::setRA(bool b) | |
107 | { | |
108 | d.ra=b; | |
109 | } | |
110 | ||
111 | void DNSPacket::setRD(bool b) | |
112 | { | |
113 | d.rd=b; | |
114 | } | |
115 | ||
092f210a | 116 | void DNSPacket::setOpcode(uint16_t opcode) |
12c86877 BH |
117 | { |
118 | d.opcode=opcode; | |
119 | } | |
120 | ||
77235722 BH |
121 | void DNSPacket::clearRecords() |
122 | { | |
9c92ad4b | 123 | d_rrs.clear(); |
55b524a0 | 124 | d_dedup.clear(); |
77235722 BH |
125 | } |
126 | ||
9bbcf03a | 127 | void DNSPacket::addRecord(DNSZoneRecord&& rr) |
12c86877 | 128 | { |
a4a67412 | 129 | // this removes duplicates from the packet. |
130 | // in case we are not compressing for AXFR, no such checking is performed! | |
55b524a0 | 131 | |
3d00ef90 | 132 | if(d_compress) { |
a4a67412 | 133 | std::string ser = const_cast<DNSZoneRecord&>(rr).dr.d_content->serialize(rr.dr.d_name); |
9d40dd1f | 134 | auto hash = boost::hash< std::pair<DNSName, std::string> >()({rr.dr.d_name, ser}); |
135 | if(d_dedup.count(hash)) { // might be a dup | |
55b524a0 | 136 | for(auto i=d_rrs.begin();i!=d_rrs.end();++i) { |
137 | if(rr.dr == i->dr) // XXX SUPER SLOW | |
4957a608 | 138 | return; |
55b524a0 | 139 | } |
90ba52e0 | 140 | } |
9d40dd1f | 141 | d_dedup.insert(hash); |
a4a67412 | 142 | } |
90ba52e0 | 143 | |
9bbcf03a | 144 | d_rrs.push_back(std::move(rr)); |
12c86877 BH |
145 | } |
146 | ||
90ba52e0 | 147 | vector<DNSZoneRecord*> DNSPacket::getAPRecords() |
12c86877 | 148 | { |
90ba52e0 | 149 | vector<DNSZoneRecord*> arrs; |
12c86877 | 150 | |
90ba52e0 | 151 | for(vector<DNSZoneRecord>::iterator i=d_rrs.begin(); |
9c92ad4b | 152 | i!=d_rrs.end(); |
12c86877 BH |
153 | ++i) |
154 | { | |
90ba52e0 | 155 | if(i->dr.d_place!=DNSResourceRecord::ADDITIONAL && |
156 | (i->dr.d_type==QType::MX || | |
157 | i->dr.d_type==QType::NS || | |
158 | i->dr.d_type==QType::SRV)) | |
4957a608 BH |
159 | { |
160 | arrs.push_back(&*i); | |
161 | } | |
12c86877 | 162 | } |
12c86877 | 163 | return arrs; |
12c86877 BH |
164 | } |
165 | ||
90ba52e0 | 166 | vector<DNSZoneRecord*> DNSPacket::getAnswerRecords() |
9c92ad4b | 167 | { |
90ba52e0 | 168 | vector<DNSZoneRecord*> arrs; |
9c92ad4b | 169 | |
90ba52e0 | 170 | for(vector<DNSZoneRecord>::iterator i=d_rrs.begin(); |
9c92ad4b BH |
171 | i!=d_rrs.end(); |
172 | ++i) | |
173 | { | |
90ba52e0 | 174 | if(i->dr.d_place!=DNSResourceRecord::ADDITIONAL) |
232f0877 | 175 | arrs.push_back(&*i); |
9c92ad4b BH |
176 | } |
177 | return arrs; | |
178 | } | |
179 | ||
180 | ||
12c86877 BH |
181 | void DNSPacket::setCompress(bool compress) |
182 | { | |
183 | d_compress=compress; | |
78bcb858 | 184 | d_rawpacket.reserve(65000); |
9c92ad4b | 185 | d_rrs.reserve(200); |
12c86877 BH |
186 | } |
187 | ||
c2826d2e | 188 | bool DNSPacket::couldBeCached() const |
7f7b8d55 | 189 | { |
ea892f70 | 190 | return !d_wantsnsid && qclass==QClass::IN && !d_havetsig; |
7f7b8d55 | 191 | } |
20665beb | 192 | |
b35ea8ec PD |
193 | unsigned int DNSPacket::getMinTTL() |
194 | { | |
195 | unsigned int minttl = UINT_MAX; | |
90ba52e0 | 196 | for(const DNSZoneRecord& rr : d_rrs) { |
197 | if (rr.dr.d_ttl < minttl) | |
198 | minttl = rr.dr.d_ttl; | |
b35ea8ec PD |
199 | } |
200 | ||
201 | return minttl; | |
202 | } | |
203 | ||
9951e2d0 KM |
204 | bool DNSPacket::isEmpty() |
205 | { | |
206 | return (d_rrs.empty()); | |
207 | } | |
208 | ||
12c86877 BH |
209 | /** Must be called before attempting to access getData(). This function stuffs all resource |
210 | * records found in rrs into the data buffer. It also frees resource records queued for us. | |
211 | */ | |
e02d0a59 | 212 | void DNSPacket::wrapup() |
12c86877 BH |
213 | { |
214 | if(d_wrapped) { | |
215 | return; | |
216 | } | |
51a3a4d4 | 217 | |
90ba52e0 | 218 | DNSZoneRecord rr; |
219 | vector<DNSZoneRecord>::iterator pos; | |
12c86877 | 220 | |
12c86877 BH |
221 | // we now need to order rrs so that the different sections come at the right place |
222 | // we want a stable sort, based on the d_place field | |
223 | ||
90ba52e0 | 224 | stable_sort(d_rrs.begin(),d_rrs.end(), [](const DNSZoneRecord& a, const DNSZoneRecord& b) { |
225 | return a.dr.d_place < b.dr.d_place; | |
226 | }); | |
4c5d6da9 | 227 | static bool mustNotShuffle = ::arg().mustDo("no-shuffle"); |
da73ef3c | 228 | |
4c5d6da9 | 229 | if(!d_tcp && !mustNotShuffle) { |
bbec1961 | 230 | pdns::shuffle(d_rrs); |
ff6a1e7b | 231 | } |
12c86877 BH |
232 | d_wrapped=true; |
233 | ||
b8e0f341 | 234 | vector<uint8_t> packet; |
adf13442 | 235 | DNSPacketWriter pw(packet, qdomain, qtype.getCode(), qclass); |
12c86877 | 236 | |
6f966e0b | 237 | pw.getHeader()->rcode=d.rcode; |
da22f3df | 238 | pw.getHeader()->opcode = d.opcode; |
b8e0f341 BH |
239 | pw.getHeader()->aa=d.aa; |
240 | pw.getHeader()->ra=d.ra; | |
241 | pw.getHeader()->qr=d.qr; | |
242 | pw.getHeader()->id=d.id; | |
243 | pw.getHeader()->rd=d.rd; | |
abc8f3f9 | 244 | pw.getHeader()->tc=d.tc; |
245 | ||
7f7b8d55 | 246 | DNSPacketWriter::optvect_t opts; |
e0db069a PD |
247 | |
248 | /* optsize is expected to hold an upper bound of data that will be | |
249 | added after actual record data - i.e. OPT, TSIG, perhaps one day | |
250 | XPF. Because of the way `pw` incrementally writes the packet, we | |
251 | cannot easily 'go back' and remove a few records. So, to prevent | |
252 | going over our maximum size, we keep our (potential) extra data | |
253 | in mind. | |
254 | ||
255 | This means that sometimes we'll send TC even if we'd end up with | |
256 | a few bytes to spare, but so be it. | |
257 | */ | |
258 | size_t optsize = 0; | |
259 | ||
e65929c6 PL |
260 | if (d_haveednssection || d_dnssecOk) { |
261 | /* root label (1), type (2), class (2), ttl (4) + rdlen (2) */ | |
e0db069a | 262 | optsize = 11; |
e65929c6 | 263 | } |
e0db069a | 264 | |
7f7b8d55 | 265 | if(d_wantsnsid) { |
8ca1a435 | 266 | const static string mode_server_id=::arg()["server-id"]; |
49e8d5d5 | 267 | if(mode_server_id != "disabled") { |
e65929c6 PL |
268 | opts.push_back(make_pair(EDNSOptionCode::NSID, mode_server_id)); |
269 | optsize += EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE + mode_server_id.size(); | |
8ca1a435 | 270 | } |
7f7b8d55 BH |
271 | } |
272 | ||
e0db069a PD |
273 | if (d_haveednssubnet) |
274 | { | |
275 | // this is an upper bound | |
e65929c6 | 276 | optsize += EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE + 2 + 1 + 1; // code+len+family+src len+scope len |
d14121a8 | 277 | optsize += d_eso.source.isIPv4() ? 4 : 16; |
e0db069a PD |
278 | } |
279 | ||
280 | if (d_trc.d_algoName.countLabels()) | |
281 | { | |
282 | // TSIG is not OPT, but we count it in optsize anyway | |
283 | optsize += d_trc.d_algoName.wirelength() + 3 + 1 + 2; // algo + time + fudge + maclen | |
284 | optsize += EVP_MAX_MD_SIZE + 2 + 2 + 2 + 0; // mac + origid + ercode + otherdatalen + no other data | |
285 | ||
286 | static_assert(EVP_MAX_MD_SIZE <= 64, "EVP_MAX_MD_SIZE is overly huge on this system, please check"); | |
287 | } | |
288 | ||
f20d5371 | 289 | if(!d_rrs.empty() || !opts.empty() || d_haveednssubnet || d_haveednssection) { |
6f966e0b | 290 | try { |
af7d3ea6 | 291 | uint8_t maxScopeMask=0; |
9c92ad4b | 292 | for(pos=d_rrs.begin(); pos < d_rrs.end(); ++pos) { |
66ddf73d | 293 | // cerr<<"during wrapup, content=["<<pos->content<<"]"<<endl; |
af7d3ea6 | 294 | maxScopeMask = max(maxScopeMask, pos->scopeMask); |
e02d0a59 | 295 | |
90ba52e0 | 296 | pw.startRecord(pos->dr.d_name, pos->dr.d_type, pos->dr.d_ttl, pos->dr.d_class, pos->dr.d_place); |
297 | pos->dr.d_content->toPacket(pw); | |
e0db069a | 298 | if(pw.size() + optsize > (d_tcp ? 65535 : getMaxReplyLen())) { |
95369951 | 299 | pw.rollback(); |
06ba4702 KM |
300 | pw.truncate(); |
301 | pw.getHeader()->tc=1; | |
96b59a50 | 302 | goto noCommit; |
95369951 BH |
303 | } |
304 | } | |
732b7143 PD |
305 | |
306 | // if(!pw.getHeader()->tc) // protect against double commit from addSignature | |
307 | ||
308 | if(!d_rrs.empty()) pw.commit(); | |
96b59a50 | 309 | |
732b7143 | 310 | noCommit:; |
af7d3ea6 BH |
311 | |
312 | if(d_haveednssubnet) { | |
af7d3ea6 BH |
313 | EDNSSubnetOpts eso = d_eso; |
314 | eso.scope = Netmask(eso.source.getNetwork(), maxScopeMask); | |
315 | ||
316 | string opt = makeEDNSSubnetOptsString(eso); | |
48981c2e | 317 | opts.push_back(make_pair(8, opt)); // 'EDNS SUBNET' |
af7d3ea6 | 318 | } |
9c92ad4b | 319 | |
f20d5371 | 320 | if(!opts.empty() || d_haveednssection || d_dnssecOk) |
75fc7cbc | 321 | { |
298fabc3 | 322 | pw.addOpt(s_udpTruncationThreshold, d_ednsrcode, d_dnssecOk ? EDNSOpts::DNSSECOK : 0, opts); |
95369951 | 323 | pw.commit(); |
75fc7cbc | 324 | } |
6f966e0b | 325 | } |
5172cb78 | 326 | catch(std::exception& e) { |
e6a9dde5 | 327 | g_log<<Logger::Warning<<"Exception: "<<e.what()<<endl; |
6f966e0b | 328 | throw; |
12c86877 | 329 | } |
b8e0f341 | 330 | } |
78bcb858 | 331 | |
4a51ff72 | 332 | if(d_trc.d_algoName.countLabels()) |
ea3816cf | 333 | addTSIG(pw, d_trc, d_tsigkeyname, d_tsigsecret, d_tsigprevious, d_tsigtimersonly); |
78bcb858 | 334 | |
90ba52e0 | 335 | d_rawpacket.assign((char*)&packet[0], packet.size()); // XXX we could do this natively on a vector.. |
3e8216c8 | 336 | |
da286f66 | 337 | // copy RR counts so they can be read later |
3e8216c8 PD |
338 | d.qdcount = pw.getHeader()->qdcount; |
339 | d.ancount = pw.getHeader()->ancount; | |
340 | d.nscount = pw.getHeader()->nscount; | |
341 | d.arcount = pw.getHeader()->arcount; | |
12c86877 BH |
342 | } |
343 | ||
c2f3be9d | 344 | void DNSPacket::setQuestion(int op, const DNSName &qd, int newqtype) |
12c86877 BH |
345 | { |
346 | memset(&d,0,sizeof(d)); | |
a410b176 | 347 | d.id=dns_random_uint16(); |
12c86877 BH |
348 | d.rd=d.tc=d.aa=false; |
349 | d.qr=false; | |
350 | d.qdcount=1; // is htons'ed later on | |
351 | d.ancount=d.arcount=d.nscount=0; | |
352 | d.opcode=op; | |
288f4aa9 BH |
353 | qdomain=qd; |
354 | qtype=newqtype; | |
12c86877 BH |
355 | } |
356 | ||
c2826d2e RG |
357 | /** convenience function for creating a reply packet from a question packet. */ |
358 | std::unique_ptr<DNSPacket> DNSPacket::replyPacket() const | |
12c86877 | 359 | { |
c2826d2e | 360 | auto r=make_unique<DNSPacket>(false); |
12c86877 | 361 | r->setSocket(d_socket); |
2b6f1436 | 362 | r->d_anyLocal=d_anyLocal; |
d06799d4 | 363 | r->setRemote(&d_remote); |
12c86877 BH |
364 | r->setAnswer(true); // this implies the allocation of the header |
365 | r->setA(true); // and we are authoritative | |
366 | r->setRA(0); // no recursion available | |
78bcb858 | 367 | r->setRD(d.rd); // if you wanted to recurse, answer will say you wanted it |
12c86877 BH |
368 | r->setID(d.id); |
369 | r->setOpcode(d.opcode); | |
370 | ||
12c86877 | 371 | r->d_dt=d_dt; |
092c9cc4 | 372 | r->d.qdcount=1; |
f28307ad | 373 | r->d_tcp = d_tcp; |
b8e0f341 BH |
374 | r->qdomain = qdomain; |
375 | r->qtype = qtype; | |
adf13442 | 376 | r->qclass = qclass; |
657e9124 | 377 | r->d_maxreplylen = d_maxreplylen; |
7f7b8d55 | 378 | r->d_wantsnsid = d_wantsnsid; |
9c92ad4b | 379 | r->d_dnssecOk = d_dnssecOk; |
af7d3ea6 BH |
380 | r->d_eso = d_eso; |
381 | r->d_haveednssubnet = d_haveednssubnet; | |
f20d5371 | 382 | r->d_haveednssection = d_haveednssection; |
298fabc3 AT |
383 | r->d_ednsversion = 0; |
384 | r->d_ednsrcode = 0; | |
385 | ||
675fa24c | 386 | if(d_tsigkeyname.countLabels()) { |
78bcb858 BH |
387 | r->d_tsigkeyname = d_tsigkeyname; |
388 | r->d_tsigprevious = d_tsigprevious; | |
389 | r->d_trc = d_trc; | |
390 | r->d_tsigsecret = d_tsigsecret; | |
391 | r->d_tsigtimersonly = d_tsigtimersonly; | |
392 | } | |
6dc26cf0 | 393 | r->d_havetsig = d_havetsig; |
12c86877 BH |
394 | return r; |
395 | } | |
396 | ||
c2826d2e | 397 | void DNSPacket::spoofQuestion(const DNSPacket& qd) |
12c86877 | 398 | { |
b8e0f341 | 399 | d_wrapped=true; // if we do this, don't later on wrapup |
7de6b0d5 | 400 | |
63e365db PD |
401 | int labellen; |
402 | string::size_type i=sizeof(d); | |
403 | ||
404 | for(;;) { | |
c2826d2e | 405 | labellen = qd.d_rawpacket[i]; |
63e365db PD |
406 | if(!labellen) break; |
407 | i++; | |
c2826d2e | 408 | d_rawpacket.replace(i, labellen, qd.d_rawpacket, i, labellen); |
63e365db | 409 | i = i + labellen; |
7de6b0d5 | 410 | } |
b8e0f341 | 411 | } |
12c86877 | 412 | |
a683e8bd | 413 | int DNSPacket::noparse(const char *mesg, size_t length) |
07676510 | 414 | { |
78bcb858 | 415 | d_rawpacket.assign(mesg,length); |
07676510 | 416 | if(length < 12) { |
e6a9dde5 | 417 | g_log << Logger::Debug << "Ignoring packet: too short ("<<length<<" < 12) from " |
d06e6c8b | 418 | << d_remote.toStringWithPort()<< endl; |
07676510 BH |
419 | return -1; |
420 | } | |
421 | d_wantsnsid=false; | |
07676510 | 422 | d_maxreplylen=512; |
78bcb858 | 423 | memcpy((void *)&d,(const void *)d_rawpacket.c_str(),12); |
07676510 BH |
424 | return 0; |
425 | } | |
426 | ||
675fa24c | 427 | void DNSPacket::setTSIGDetails(const TSIGRecordContent& tr, const DNSName& keyname, const string& secret, const string& previous, bool timersonly) |
78bcb858 BH |
428 | { |
429 | d_trc=tr; | |
e49d0a2f | 430 | d_trc.d_origID = (((d.id & 0xFF)<<8) | ((d.id & 0xFF00)>>8)); |
78bcb858 BH |
431 | d_tsigkeyname = keyname; |
432 | d_tsigsecret = secret; | |
433 | d_tsigprevious = previous; | |
434 | d_tsigtimersonly=timersonly; | |
435 | } | |
436 | ||
ea3816cf | 437 | bool DNSPacket::getTSIGDetails(TSIGRecordContent* trc, DNSName* keyname, uint16_t* tsigPosOut) const |
78bcb858 | 438 | { |
27c0050c | 439 | MOADNSParser mdp(d_isQuery, d_rawpacket); |
ea3816cf RG |
440 | uint16_t tsigPos = mdp.getTSIGPos(); |
441 | if(!tsigPos) | |
78bcb858 BH |
442 | return false; |
443 | ||
444 | bool gotit=false; | |
445 | for(MOADNSParser::answers_t::const_iterator i=mdp.d_answers.begin(); i!=mdp.d_answers.end(); ++i) { | |
60a1c204 | 446 | if(i->first.d_type == QType::TSIG && i->first.d_class == QType::ANY) { |
6b3e413d CH |
447 | // cast can fail, f.e. if d_content is an UnknownRecordContent. |
448 | shared_ptr<TSIGRecordContent> content = std::dynamic_pointer_cast<TSIGRecordContent>(i->first.d_content); | |
449 | if (!content) { | |
e6a9dde5 | 450 | g_log<<Logger::Error<<"TSIG record has no or invalid content (invalid packet)"<<endl; |
6b3e413d CH |
451 | return false; |
452 | } | |
453 | *trc = *content; | |
f809c028 | 454 | *keyname = i->first.d_name; |
6b3e413d | 455 | gotit=true; |
78bcb858 BH |
456 | } |
457 | } | |
458 | if(!gotit) | |
459 | return false; | |
ea3816cf RG |
460 | |
461 | if (tsigPosOut) { | |
462 | *tsigPosOut = tsigPos; | |
463 | } | |
78bcb858 | 464 | |
78bcb858 BH |
465 | return true; |
466 | } | |
467 | ||
675fa24c | 468 | bool DNSPacket::getTKEYRecord(TKEYRecordContent *tr, DNSName *keyname) const |
4429ed62 | 469 | { |
27c0050c | 470 | MOADNSParser mdp(d_isQuery, d_rawpacket); |
4429ed62 AT |
471 | bool gotit=false; |
472 | ||
473 | for(MOADNSParser::answers_t::const_iterator i=mdp.d_answers.begin(); i!=mdp.d_answers.end(); ++i) { | |
474 | if (gotit) { | |
e6a9dde5 | 475 | g_log<<Logger::Error<<"More than one TKEY record found in query"<<endl; |
4429ed62 AT |
476 | return false; |
477 | } | |
478 | ||
479 | if(i->first.d_type == QType::TKEY) { | |
6b3e413d CH |
480 | // cast can fail, f.e. if d_content is an UnknownRecordContent. |
481 | shared_ptr<TKEYRecordContent> content = std::dynamic_pointer_cast<TKEYRecordContent>(i->first.d_content); | |
482 | if (!content) { | |
e6a9dde5 | 483 | g_log<<Logger::Error<<"TKEY record has no or invalid content (invalid packet)"<<endl; |
6b3e413d CH |
484 | return false; |
485 | } | |
486 | *tr = *content; | |
f809c028 | 487 | *keyname = i->first.d_name; |
4429ed62 AT |
488 | gotit=true; |
489 | } | |
490 | } | |
491 | ||
492 | return gotit; | |
493 | } | |
494 | ||
b8e0f341 BH |
495 | /** This function takes data from the network, possibly received with recvfrom, and parses |
496 | it into our class. Results of calling this function multiple times on one packet are | |
497 | unknown. Returns -1 if the packet cannot be parsed. | |
498 | */ | |
a683e8bd | 499 | int DNSPacket::parse(const char *mesg, size_t length) |
9f064e90 | 500 | try |
b8e0f341 | 501 | { |
78bcb858 | 502 | d_rawpacket.assign(mesg,length); |
0e7f49b5 | 503 | d_wrapped=true; |
b8e0f341 | 504 | if(length < 12) { |
77562377 | 505 | g_log << Logger::Debug << "Ignoring packet: too short from " |
b8e0f341 BH |
506 | << getRemote() << endl; |
507 | return -1; | |
508 | } | |
7f7b8d55 | 509 | |
27c0050c | 510 | MOADNSParser mdp(d_isQuery, d_rawpacket); |
7f7b8d55 BH |
511 | EDNSOpts edo; |
512 | ||
513 | // ANY OPTION WHICH *MIGHT* BE SET DOWN BELOW SHOULD BE CLEARED FIRST! | |
514 | ||
515 | d_wantsnsid=false; | |
9c92ad4b | 516 | d_dnssecOk=false; |
78bcb858 | 517 | d_havetsig = mdp.getTSIGPos(); |
af7d3ea6 | 518 | d_haveednssubnet = false; |
f20d5371 | 519 | d_haveednssection = false; |
9c92ad4b | 520 | |
7f7b8d55 | 521 | if(getEDNSOpts(mdp, &edo)) { |
f20d5371 | 522 | d_haveednssection=true; |
7a9b7c95 RG |
523 | /* rfc6891 6.2.3: |
524 | "Values lower than 512 MUST be treated as equal to 512." | |
525 | */ | |
779b8a25 | 526 | d_ednsRawPacketSizeLimit=edo.d_packetsize; |
7a9b7c95 | 527 | d_maxreplylen=std::min(std::max(static_cast<uint16_t>(512), edo.d_packetsize), s_udpTruncationThreshold); |
d6c335ab PL |
528 | // cerr<<edo.d_extFlags<<endl; |
529 | if(edo.d_extFlags & EDNSOpts::DNSSECOK) | |
9c92ad4b | 530 | d_dnssecOk=true; |
7f7b8d55 BH |
531 | |
532 | for(vector<pair<uint16_t, string> >::const_iterator iter = edo.d_options.begin(); | |
4957a608 BH |
533 | iter != edo.d_options.end(); |
534 | ++iter) { | |
ea892f70 CH |
535 | if(iter->first == EDNSOptionCode::NSID) { |
536 | d_wantsnsid=true; | |
7f7b8d55 | 537 | } |
ea892f70 | 538 | else if(s_doEDNSSubnetProcessing && (iter->first == EDNSOptionCode::ECS)) { // 'EDNS SUBNET' |
17d6efc0 | 539 | if(getEDNSSubnetOptsFromString(iter->second, &d_eso)) { |
af7d3ea6 BH |
540 | //cerr<<"Parsed, source: "<<d_eso.source.toString()<<", scope: "<<d_eso.scope.toString()<<", family = "<<d_eso.scope.getNetwork().sin4.sin_family<<endl; |
541 | d_haveednssubnet=true; | |
542 | } | |
543 | } | |
544 | else { | |
545 | // cerr<<"Have an option #"<<iter->first<<": "<<makeHexDump(iter->second)<<endl; | |
546 | } | |
7f7b8d55 | 547 | } |
298fabc3 AT |
548 | d_ednsversion = edo.d_version; |
549 | d_ednsrcode = edo.d_extRCode; | |
779b8a25 | 550 | } |
7f7b8d55 | 551 | else { |
657e9124 | 552 | d_maxreplylen=512; |
779b8a25 | 553 | d_ednsRawPacketSizeLimit=-1; |
7f7b8d55 | 554 | } |
12c86877 | 555 | |
78bcb858 | 556 | memcpy((void *)&d,(const void *)d_rawpacket.c_str(),12); |
b8e0f341 | 557 | qdomain=mdp.d_qname; |
c2f3be9d PD |
558 | // if(!qdomain.empty()) // strip dot |
559 | // boost::erase_tail(qdomain, 1); | |
12c86877 | 560 | |
b8e0f341 BH |
561 | if(!ntohs(d.qdcount)) { |
562 | if(!d_tcp) { | |
d8fc9f7e | 563 | g_log << Logger::Debug << "No question section in packet from " << getRemote() <<", RCode="<<RCode::to_s(d.rcode)<<endl; |
b8e0f341 | 564 | return -1; |
12c86877 BH |
565 | } |
566 | } | |
567 | ||
b8e0f341 BH |
568 | qtype=mdp.d_qtype; |
569 | qclass=mdp.d_qclass; | |
b7992721 RG |
570 | |
571 | d_trc = TSIGRecordContent(); | |
572 | ||
b8e0f341 | 573 | return 0; |
12c86877 | 574 | } |
5172cb78 | 575 | catch(std::exception& e) { |
9f064e90 BH |
576 | return -1; |
577 | } | |
12c86877 | 578 | |
78bcb858 | 579 | unsigned int DNSPacket::getMaxReplyLen() |
657e9124 BH |
580 | { |
581 | return d_maxreplylen; | |
582 | } | |
583 | ||
80397312 BH |
584 | void DNSPacket::setMaxReplyLen(int bytes) |
585 | { | |
586 | d_maxreplylen=bytes; | |
587 | } | |
588 | ||
b8e0f341 BH |
589 | //! Use this to set where this packet was received from or should be sent to |
590 | void DNSPacket::setRemote(const ComboAddress *s) | |
12c86877 | 591 | { |
d06799d4 | 592 | d_remote=*s; |
b8e0f341 | 593 | } |
12c86877 | 594 | |
02980dc2 | 595 | bool DNSPacket::hasEDNSSubnet() const |
fe498ace BH |
596 | { |
597 | return d_haveednssubnet; | |
598 | } | |
599 | ||
c2826d2e | 600 | bool DNSPacket::hasEDNS() const |
17d0b1e6 PD |
601 | { |
602 | return d_haveednssection; | |
603 | } | |
604 | ||
af7d3ea6 BH |
605 | Netmask DNSPacket::getRealRemote() const |
606 | { | |
607 | if(d_haveednssubnet) | |
608 | return d_eso.source; | |
609 | return Netmask(d_remote); | |
610 | } | |
611 | ||
b8e0f341 | 612 | void DNSPacket::setSocket(Utility::sock_t sock) |
12c86877 | 613 | { |
b8e0f341 | 614 | d_socket=sock; |
12c86877 BH |
615 | } |
616 | ||
b8e0f341 | 617 | void DNSPacket::commitD() |
12c86877 | 618 | { |
78bcb858 | 619 | d_rawpacket.replace(0,12,(char *)&d,12); // copy in d |
12c86877 BH |
620 | } |
621 | ||
ea3816cf | 622 | bool DNSPacket::checkForCorrectTSIG(UeberBackend* B, DNSName* keyname, string* secret, TSIGRecordContent* trc) const |
78bcb858 | 623 | { |
ea3816cf | 624 | uint16_t tsigPos; |
60a1c204 | 625 | |
ea3816cf | 626 | if (!this->getTSIGDetails(trc, keyname, &tsigPos)) { |
78bcb858 BH |
627 | return false; |
628 | } | |
785594c9 | 629 | |
ea3816cf RG |
630 | TSIGTriplet tt; |
631 | tt.name = *keyname; | |
632 | tt.algo = trc->d_algoName; | |
633 | if (tt.algo == DNSName("hmac-md5.sig-alg.reg.int")) | |
634 | tt.algo = DNSName("hmac-md5"); | |
9a459f10 | 635 | |
ea3816cf RG |
636 | string secret64; |
637 | if (tt.algo != DNSName("gss-tsig")) { | |
638 | if(!B->getTSIGKey(*keyname, &tt.algo, &secret64)) { | |
e6a9dde5 | 639 | g_log<<Logger::Error<<"Packet for domain '"<<this->qdomain<<"' denied: can't find TSIG key with name '"<<*keyname<<"' and algorithm '"<<tt.algo<<"'"<<endl; |
7f9ac49b AT |
640 | return false; |
641 | } | |
ea3816cf RG |
642 | B64Decode(secret64, *secret); |
643 | tt.secret = *secret; | |
7f9ac49b AT |
644 | } |
645 | ||
ea3816cf | 646 | bool result; |
3213be1e | 647 | |
ea3816cf RG |
648 | try { |
649 | result = validateTSIG(d_rawpacket, tsigPos, tt, *trc, "", trc->d_mac, false); | |
9f782f99 | 650 | } |
ea3816cf | 651 | catch(const std::runtime_error& err) { |
e6a9dde5 | 652 | g_log<<Logger::Error<<"Packet for '"<<this->qdomain<<"' denied: "<<err.what()<<endl; |
ea3816cf | 653 | return false; |
78bcb858 | 654 | } |
785594c9 | 655 | |
78bcb858 BH |
656 | return result; |
657 | } | |
1a5ac5d7 | 658 | |
6fe866b4 | 659 | const DNSName& DNSPacket::getTSIGKeyname() const { |
1a5ac5d7 AT |
660 | return d_tsigkeyname; |
661 | } |