]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/lwres.cc
Merge pull request #8223 from PowerDNS/omoerbeek-patch-1
[thirdparty/pdns.git] / pdns / lwres.cc
1 /*
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 */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include "utility.hh"
26 #include "lwres.hh"
27 #include <iostream>
28 #include "dnsrecords.hh"
29 #include <errno.h>
30 #include "misc.hh"
31 #include <algorithm>
32 #include <sstream>
33 #include <cstring>
34 #include <string>
35 #include <vector>
36 #include "dns.hh"
37 #include "qtype.hh"
38 #include "pdnsexception.hh"
39 #include "arguments.hh"
40 #include "sstuff.hh"
41 #include "syncres.hh"
42 #include "dnswriter.hh"
43 #include "dnsparser.hh"
44 #include "logger.hh"
45 #include "dns_random.hh"
46 #include <boost/scoped_array.hpp>
47 #include <boost/algorithm/string.hpp>
48 #include "validate-recursor.hh"
49 #include "ednssubnet.hh"
50
51 #ifdef HAVE_PROTOBUF
52
53 #include "uuid-utils.hh"
54
55 #ifdef HAVE_FSTRM
56 #include "rec-dnstap.hh"
57 #include "fstrm_logger.hh"
58 bool g_syslog;
59
60 static bool isEnabledForQueries(const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstreamLoggers)
61 {
62 if (fstreamLoggers == nullptr) {
63 return false;
64 }
65 for (auto& logger : *fstreamLoggers) {
66 if (logger->logQueries()) {
67 return true;
68 }
69 }
70 return false;
71 }
72
73 static void logFstreamQuery(const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstreamLoggers, const struct timeval &queryTime, const ComboAddress& ip, bool doTCP,
74 boost::optional<const DNSName&> auth, const vector<uint8_t>& packet)
75 {
76 if (fstreamLoggers == nullptr)
77 return;
78
79 struct timespec ts;
80 TIMEVAL_TO_TIMESPEC(&queryTime, &ts);
81 RecDnstapMessage message(SyncRes::s_serverID, nullptr, &ip, doTCP, auth, reinterpret_cast<const char*>(&*packet.begin()), packet.size(), &ts, nullptr);
82 std::string str;
83 message.serialize(str);
84
85 for (auto& logger : *fstreamLoggers) {
86 logger->queueData(str);
87 }
88 }
89
90 static bool isEnabledForResponses(const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstreamLoggers)
91 {
92 if (fstreamLoggers == nullptr) {
93 return false;
94 }
95 for (auto& logger : *fstreamLoggers) {
96 if (logger->logResponses()) {
97 return true;
98 }
99 }
100 return false;
101 }
102
103 static void logFstreamResponse(const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstreamLoggers, const ComboAddress& ip, bool doTCP,
104 boost::optional<const DNSName&> auth, const std::string& packet, const struct timeval& queryTime, const struct timeval& replyTime)
105 {
106 if (fstreamLoggers == nullptr)
107 return;
108
109 struct timespec ts1, ts2;
110 TIMEVAL_TO_TIMESPEC(&queryTime, &ts1);
111 TIMEVAL_TO_TIMESPEC(&replyTime, &ts2);
112 RecDnstapMessage message(SyncRes::s_serverID, nullptr, &ip, doTCP, auth, static_cast<const char*>(&*packet.begin()), packet.size(), &ts1, &ts2);
113 std::string str;
114 message.serialize(str);
115
116 for (auto& logger : *fstreamLoggers) {
117 logger->queueData(str);
118 }
119 }
120
121 #endif // HAVE_FSTRM
122
123 static void logOutgoingQuery(const std::shared_ptr<std::vector<std::unique_ptr<RemoteLogger>>>& outgoingLoggers, boost::optional<RecProtoBufMessage>& message, boost::optional<const boost::uuids::uuid&> initialRequestId, const boost::uuids::uuid& uuid, const ComboAddress& ip, const DNSName& domain, int type, uint16_t qid, bool doTCP, size_t bytes, boost::optional<Netmask>& srcmask)
124 {
125 if (!outgoingLoggers) {
126 return;
127 }
128
129 bool log = false;
130 for (auto& logger : *outgoingLoggers) {
131 if (logger->logQueries()) {
132 log = true;
133 break;
134 }
135 }
136
137 if (!log) {
138 return;
139 }
140
141 message = RecProtoBufMessage(DNSProtoBufMessage::OutgoingQuery, uuid, nullptr, &ip, domain, type, QClass::IN, qid, doTCP, bytes);
142 message->setServerIdentity(SyncRes::s_serverID);
143
144 if (initialRequestId) {
145 message->setInitialRequestID(*initialRequestId);
146 }
147
148 if (srcmask) {
149 message->setEDNSSubnet(*srcmask);
150 }
151
152 // cerr <<message.toDebugString()<<endl;
153 std::string str;
154 message->serialize(str);
155
156 for (auto& logger : *outgoingLoggers) {
157 if (logger->logQueries()) {
158 logger->queueData(str);
159 }
160 }
161 }
162
163 static void logIncomingResponse(const std::shared_ptr<std::vector<std::unique_ptr<RemoteLogger>>>& outgoingLoggers, boost::optional<RecProtoBufMessage>& message, boost::optional<const boost::uuids::uuid&> initialRequestId, const boost::uuids::uuid& uuid, const ComboAddress& ip, const DNSName& domain, int type, uint16_t qid, bool doTCP, boost::optional<Netmask>& srcmask, size_t bytes, int rcode, const std::vector<DNSRecord>& records, const struct timeval& queryTime, const std::set<uint16_t>& exportTypes)
164 {
165 if (!outgoingLoggers) {
166 return;
167 }
168
169 bool log = false;
170 for (auto& logger : *outgoingLoggers) {
171 if (logger->logResponses()) {
172 log = true;
173 break;
174 }
175 }
176
177 if (!log) {
178 return;
179 }
180
181 if (!message) {
182 message = RecProtoBufMessage(DNSProtoBufMessage::IncomingResponse, uuid, nullptr, &ip, domain, type, QClass::IN, qid, doTCP, bytes);
183 message->setServerIdentity(SyncRes::s_serverID);
184
185 if (initialRequestId) {
186 message->setInitialRequestID(*initialRequestId);
187 }
188
189 if (srcmask) {
190 message->setEDNSSubnet(*srcmask);
191 }
192 }
193 else {
194 message->updateTime();
195 message->setType(DNSProtoBufMessage::IncomingResponse);
196 message->setBytes(bytes);
197 }
198
199 message->setQueryTime(queryTime.tv_sec, queryTime.tv_usec);
200 if (rcode == -1) {
201 message->setNetworkErrorResponseCode();
202 }
203 else {
204 message->setResponseCode(rcode);
205 }
206 message->addRRs(records, exportTypes);
207
208 // cerr <<message.toDebugString()<<endl;
209 std::string str;
210 message->serialize(str);
211
212 for (auto& logger : *outgoingLoggers) {
213 if (logger->logResponses()) {
214 logger->queueData(str);
215 }
216 }
217 }
218 #endif /* HAVE_PROTOBUF */
219
220 //! returns -2 for OS limits error, -1 for permanent error that has to do with remote **transport**, 0 for timeout, 1 for success
221 /** lwr is only filled out in case 1 was returned, and even when returning 1 for 'success', lwr might contain DNS errors
222 Never throws!
223 */
224 int asyncresolve(const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, const std::shared_ptr<std::vector<std::unique_ptr<RemoteLogger>>>& outgoingLoggers, const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstrmLoggers, const std::set<uint16_t>& exportTypes, LWResult *lwr, bool* chained)
225 {
226 size_t len;
227 size_t bufsize=g_outgoingEDNSBufsize;
228 std::string buf;
229 buf.resize(bufsize);
230 vector<uint8_t> vpacket;
231 // string mapped0x20=dns0x20(domain);
232 uint16_t qid = dns_random(0xffff);
233 DNSPacketWriter pw(vpacket, domain, type);
234
235 pw.getHeader()->rd=sendRDQuery;
236 pw.getHeader()->id=qid;
237 /* RFC 6840 section 5.9:
238 * This document further specifies that validating resolvers SHOULD set
239 * the CD bit on every upstream query. This is regardless of whether
240 * the CD bit was set on the incoming query [...]
241 *
242 * sendRDQuery is only true if the qname is part of a forward-zone-recurse (or
243 * set in the forward-zone-file), so we use this as an indicator for it being
244 * an "upstream query". To stay true to "dnssec=off means 3.X behaviour", we
245 * only set +CD on forwarded query in any mode other than dnssec=off.
246 */
247 pw.getHeader()->cd=(sendRDQuery && g_dnssecmode != DNSSECMode::Off);
248
249 string ping;
250 bool weWantEDNSSubnet=false;
251 uint8_t outgoingECSBits = 0;
252 ComboAddress outgoingECSAddr;
253 if(EDNS0Level > 0) {
254 DNSPacketWriter::optvect_t opts;
255 if(srcmask) {
256 EDNSSubnetOpts eo;
257 eo.source = *srcmask;
258 outgoingECSBits = srcmask->getBits();
259 outgoingECSAddr = srcmask->getNetwork();
260 // cout<<"Adding request mask: "<<eo.source.toString()<<endl;
261 opts.push_back(make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(eo)));
262 weWantEDNSSubnet=true;
263 }
264
265 pw.addOpt(g_outgoingEDNSBufsize, 0, g_dnssecmode == DNSSECMode::Off ? 0 : EDNSOpts::DNSSECOK, opts);
266 pw.commit();
267 }
268 lwr->d_rcode = 0;
269 lwr->d_haveEDNS = false;
270 int ret;
271
272 DTime dt;
273 dt.set();
274 *now=dt.getTimeval();
275
276 #ifdef HAVE_PROTOBUF
277 boost::uuids::uuid uuid;
278 const struct timeval queryTime = *now;
279 boost::optional<RecProtoBufMessage> pbMessage = boost::none;
280
281 if (outgoingLoggers) {
282 uuid = getUniqueID();
283 logOutgoingQuery(outgoingLoggers, pbMessage, context ? context->d_initialRequestId : boost::none, uuid, ip, domain, type, qid, doTCP, vpacket.size(), srcmask);
284 }
285 #endif /* HAVE_PROTOBUF */
286 #ifdef HAVE_FSTRM
287 if (isEnabledForQueries(fstrmLoggers)) {
288 logFstreamQuery(fstrmLoggers, queryTime, ip, doTCP, context ? context->d_auth : boost::none, vpacket);
289 }
290 #endif /* HAVE_FSTRM */
291
292 srcmask = boost::none; // this is also our return value, even if EDNS0Level == 0
293
294 if(!doTCP) {
295 int queryfd;
296 if(ip.sin4.sin_family==AF_INET6)
297 g_stats.ipv6queries++;
298
299 if((ret=asendto((const char*)&*vpacket.begin(), vpacket.size(), 0, ip, qid,
300 domain, type, &queryfd)) < 0) {
301 return ret; // passes back the -2 EMFILE
302 }
303
304 if (queryfd == -1) {
305 *chained = true;
306 }
307
308 // sleep until we see an answer to this, interface to mtasker
309
310 ret=arecvfrom(buf, 0, ip, &len, qid,
311 domain, type, queryfd, now);
312 }
313 else {
314 try {
315 Socket s(ip.sin4.sin_family, SOCK_STREAM);
316
317 s.setNonBlocking();
318 ComboAddress local = getQueryLocalAddress(ip.sin4.sin_family, 0);
319
320 s.bind(local);
321
322 s.connect(ip);
323
324 uint16_t tlen=htons(vpacket.size());
325 char *lenP=(char*)&tlen;
326 const char *msgP=(const char*)&*vpacket.begin();
327 string packet=string(lenP, lenP+2)+string(msgP, msgP+vpacket.size());
328
329 ret=asendtcp(packet, &s);
330 if(!(ret>0))
331 return ret;
332
333 packet.clear();
334 ret=arecvtcp(packet, 2, &s, false);
335 if(!(ret > 0))
336 return ret;
337
338 memcpy(&tlen, packet.c_str(), sizeof(tlen));
339 len=ntohs(tlen); // switch to the 'len' shared with the rest of the function
340
341 ret=arecvtcp(packet, len, &s, false);
342 if(!(ret > 0))
343 return ret;
344
345 buf.resize(len);
346 memcpy(const_cast<char*>(buf.data()), packet.c_str(), len);
347
348 ret=1;
349 }
350 catch(NetworkError& ne) {
351 ret = -2; // OS limits error
352 }
353 }
354
355
356 lwr->d_usec=dt.udiff();
357 *now=dt.getTimeval();
358
359 if(ret <= 0) { // includes 'timeout'
360 #ifdef HAVE_PROTOBUF
361 if (outgoingLoggers) {
362 logIncomingResponse(outgoingLoggers, pbMessage, context ? context->d_initialRequestId : boost::none, uuid, ip, domain, type, qid, doTCP, srcmask, 0, -1, {}, queryTime, exportTypes);
363 }
364 #endif
365 return ret;
366 }
367
368 buf.resize(len);
369
370 #ifdef HAVE_FSTRM
371 if (isEnabledForResponses(fstrmLoggers)) {
372 logFstreamResponse(fstrmLoggers, ip, doTCP, context ? context->d_auth : boost::none, buf, queryTime, *now);
373 }
374 #endif /* HAVE_FSTRM */
375
376 lwr->d_records.clear();
377 try {
378 lwr->d_tcbit=0;
379 MOADNSParser mdp(false, buf);
380 lwr->d_aabit=mdp.d_header.aa;
381 lwr->d_tcbit=mdp.d_header.tc;
382 lwr->d_rcode=mdp.d_header.rcode;
383
384 if(mdp.d_header.rcode == RCode::FormErr && mdp.d_qname.empty() && mdp.d_qtype == 0 && mdp.d_qclass == 0) {
385 #ifdef HAVE_PROTOBUF
386 if(outgoingLoggers) {
387 logIncomingResponse(outgoingLoggers, pbMessage, context ? context->d_initialRequestId : boost::none, uuid, ip, domain, type, qid, doTCP, srcmask, len, lwr->d_rcode, lwr->d_records, queryTime, exportTypes);
388 }
389 #endif
390 lwr->d_validpacket=true;
391 return 1; // this is "success", the error is set in lwr->d_rcode
392 }
393
394 if(domain != mdp.d_qname) {
395 if(!mdp.d_qname.empty() && domain.toString().find((char)0) == string::npos /* ugly */) {// embedded nulls are too noisy, plus empty domains are too
396 g_log<<Logger::Notice<<"Packet purporting to come from remote server "<<ip.toString()<<" contained wrong answer: '" << domain << "' != '" << mdp.d_qname << "'" << endl;
397 }
398 // unexpected count has already been done @ pdns_recursor.cc
399 goto out;
400 }
401
402 lwr->d_records.reserve(mdp.d_answers.size());
403 for(const auto& a : mdp.d_answers)
404 lwr->d_records.push_back(a.first);
405
406 EDNSOpts edo;
407 if(EDNS0Level > 0 && getEDNSOpts(mdp, &edo)) {
408 lwr->d_haveEDNS = true;
409
410 if(weWantEDNSSubnet) {
411 for(const auto& opt : edo.d_options) {
412 if(opt.first==EDNSOptionCode::ECS) {
413 EDNSSubnetOpts reso;
414 if(getEDNSSubnetOptsFromString(opt.second, &reso)) {
415 // cerr<<"EDNS Subnet response: "<<reso.source.toString()<<", scope: "<<reso.scope.toString()<<", family = "<<reso.scope.getNetwork().sin4.sin_family<<endl;
416 /* rfc7871 states that 0 "indicate[s] that the answer is suitable for all addresses in FAMILY",
417 so we might want to still pass the information along to be able to differentiate between
418 IPv4 and IPv6. Still I'm pretty sure it doesn't matter in real life, so let's not duplicate
419 entries in our cache. */
420 if(reso.scope.getBits()) {
421 uint8_t bits = std::min(reso.scope.getBits(), outgoingECSBits);
422 outgoingECSAddr.truncate(bits);
423 srcmask = Netmask(outgoingECSAddr, bits);
424 }
425 }
426 }
427 }
428 }
429 }
430
431 #ifdef HAVE_PROTOBUF
432 if(outgoingLoggers) {
433 logIncomingResponse(outgoingLoggers, pbMessage, context ? context->d_initialRequestId : boost::none, uuid, ip, domain, type, qid, doTCP, srcmask, len, lwr->d_rcode, lwr->d_records, queryTime, exportTypes);
434 }
435 #endif
436 lwr->d_validpacket=true;
437 return 1;
438 }
439 catch(std::exception &mde) {
440 if(::arg().mustDo("log-common-errors"))
441 g_log<<Logger::Notice<<"Unable to parse packet from remote server "<<ip.toString()<<": "<<mde.what()<<endl;
442 lwr->d_rcode = RCode::FormErr;
443 g_stats.serverParseError++;
444 #ifdef HAVE_PROTOBUF
445 if(outgoingLoggers) {
446 logIncomingResponse(outgoingLoggers, pbMessage, context ? context->d_initialRequestId : boost::none, uuid, ip, domain, type, qid, doTCP, srcmask, len, lwr->d_rcode, lwr->d_records, queryTime, exportTypes);
447 }
448 #endif
449 lwr->d_validpacket=false;
450 return 1; // success - oddly enough
451 }
452 catch(...) {
453 g_log<<Logger::Notice<<"Unknown error parsing packet from remote server"<<endl;
454 }
455
456 g_stats.serverParseError++;
457
458 out:
459 if(!lwr->d_rcode)
460 lwr->d_rcode=RCode::ServFail;
461
462 return -1;
463 }
464