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