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