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