]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/lwres.cc
dnsdist: Add HTTPStatusAction to return a specific HTTP response
[thirdparty/pdns.git] / pdns / lwres.cc
CommitLineData
e14f094b 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
36c5ee42 25#include "utility.hh"
e14f094b 26#include "lwres.hh"
e14f094b 27#include <iostream>
81883dcc 28#include "dnsrecords.hh"
e14f094b
BH
29#include <errno.h>
30#include "misc.hh"
31#include <algorithm>
32#include <sstream>
33#include <cstring>
34#include <string>
35#include <vector>
e14f094b
BH
36#include "dns.hh"
37#include "qtype.hh"
5c409fa2 38#include "pdnsexception.hh"
e14f094b 39#include "arguments.hh"
5c633640
BH
40#include "sstuff.hh"
41#include "syncres.hh"
ea634573
BH
42#include "dnswriter.hh"
43#include "dnsparser.hh"
aab4adb0 44#include "logger.hh"
51e2144e 45#include "dns_random.hh"
263f6a5a 46#include <boost/scoped_array.hpp>
51e2144e 47#include <boost/algorithm/string.hpp>
12ce523e 48#include "validate-recursor.hh"
c4443ccb 49#include "ednssubnet.hh"
51e2144e 50
4898a348
RG
51#ifdef HAVE_PROTOBUF
52
d61aa945
RG
53#include "uuid-utils.hh"
54
ebd67986 55#ifdef HAVE_FSTRM
522253b0 56#include "rec-dnstap.hh"
ebd67986
OM
57#include "fstrm_logger.hh"
58bool g_syslog;
59
10ba6d01 60static bool isEnabledForQueries(const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstreamLoggers)
573f4ff0
OM
61{
62 if (fstreamLoggers == nullptr) {
63 return false;
64 }
65 for (auto& logger : *fstreamLoggers) {
10ba6d01 66 if (logger->logQueries()) {
573f4ff0
OM
67 return true;
68 }
69 }
70 return false;
71}
72
6de48f75
OM
73static 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)
4898a348 75{
b9fa43e0 76 if (fstreamLoggers == nullptr)
4898a348
RG
77 return;
78
b9fa43e0
OM
79 struct timespec ts;
80 TIMEVAL_TO_TIMESPEC(&queryTime, &ts);
6de48f75 81 RecDnstapMessage message(SyncRes::s_serverID, nullptr, &ip, doTCP, auth, reinterpret_cast<const char*>(&*packet.begin()), packet.size(), &ts, nullptr);
b9fa43e0
OM
82 std::string str;
83 message.serialize(str);
c165308b 84
b9fa43e0
OM
85 for (auto& logger : *fstreamLoggers) {
86 logger->queueData(str);
87 }
88}
4898a348 89
10ba6d01 90static bool isEnabledForResponses(const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstreamLoggers)
573f4ff0
OM
91{
92 if (fstreamLoggers == nullptr) {
93 return false;
94 }
95 for (auto& logger : *fstreamLoggers) {
10ba6d01 96 if (logger->logResponses()) {
573f4ff0
OM
97 return true;
98 }
99 }
100 return false;
101}
102
6de48f75
OM
103static 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)
b9fa43e0
OM
105{
106 if (fstreamLoggers == nullptr)
107 return;
0ff13512 108
b9fa43e0
OM
109 struct timespec ts1, ts2;
110 TIMEVAL_TO_TIMESPEC(&queryTime, &ts1);
111 TIMEVAL_TO_TIMESPEC(&replyTime, &ts2);
6de48f75 112 RecDnstapMessage message(SyncRes::s_serverID, nullptr, &ip, doTCP, auth, static_cast<const char*>(&*packet.begin()), packet.size(), &ts1, &ts2);
b9fa43e0
OM
113 std::string str;
114 message.serialize(str);
b773359c 115
b9fa43e0
OM
116 for (auto& logger : *fstreamLoggers) {
117 logger->queueData(str);
b773359c 118 }
4898a348
RG
119}
120
b9fa43e0 121#endif // HAVE_FSTRM
ebd67986 122
8623c19a 123static 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)
4898a348 124{
5d6c7a46 125 if (!outgoingLoggers) {
4898a348 126 return;
5d6c7a46
RG
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 }
4898a348 140
8623c19a
RG
141 message = RecProtoBufMessage(DNSProtoBufMessage::OutgoingQuery, uuid, nullptr, &ip, domain, type, QClass::IN, qid, doTCP, bytes);
142 message->setServerIdentity(SyncRes::s_serverID);
c165308b 143
4898a348 144 if (initialRequestId) {
8623c19a 145 message->setInitialRequestID(*initialRequestId);
4898a348
RG
146 }
147
0ff13512 148 if (srcmask) {
8623c19a 149 message->setEDNSSubnet(*srcmask);
0ff13512
RG
150 }
151
4898a348
RG
152// cerr <<message.toDebugString()<<endl;
153 std::string str;
8623c19a 154 message->serialize(str);
b773359c
RG
155
156 for (auto& logger : *outgoingLoggers) {
5d6c7a46
RG
157 if (logger->logQueries()) {
158 logger->queueData(str);
159 }
b773359c 160 }
4898a348
RG
161}
162
5d6c7a46 163static 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)
4898a348 164{
5d6c7a46
RG
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) {
4898a348 178 return;
5d6c7a46
RG
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 }
4898a348 198
8623c19a 199 message->setQueryTime(queryTime.tv_sec, queryTime.tv_usec);
57f8413e
RG
200 if (rcode == -1) {
201 message->setNetworkErrorResponseCode();
202 }
203 else {
204 message->setResponseCode(rcode);
205 }
8623c19a 206 message->addRRs(records, exportTypes);
4898a348
RG
207
208// cerr <<message.toDebugString()<<endl;
209 std::string str;
8623c19a 210 message->serialize(str);
b773359c
RG
211
212 for (auto& logger : *outgoingLoggers) {
5d6c7a46
RG
213 if (logger->logResponses()) {
214 logger->queueData(str);
215 }
b773359c 216 }
4898a348
RG
217}
218#endif /* HAVE_PROTOBUF */
219
81883dcc
BH
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 */
10ba6d01 224int 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)
e14f094b 225{
a683e8bd
RG
226 size_t len;
227 size_t bufsize=g_outgoingEDNSBufsize;
78f56b38
RG
228 std::string buf;
229 buf.resize(bufsize);
ea634573 230 vector<uint8_t> vpacket;
51e2144e 231 // string mapped0x20=dns0x20(domain);
4898a348 232 uint16_t qid = dns_random(0xffff);
ea634573
BH
233 DNSPacketWriter pw(vpacket, domain, type);
234
c1d73d94 235 pw.getHeader()->rd=sendRDQuery;
4898a348 236 pw.getHeader()->id=qid;
5a7d2a18
PL
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 */
e7b18884 247 pw.getHeader()->cd=(sendRDQuery && g_dnssecmode != DNSSECMode::Off);
5a7d2a18 248
81883dcc 249 string ping;
7bb598a0 250 bool weWantEDNSSubnet=false;
30d4402d
RG
251 uint8_t outgoingECSBits = 0;
252 ComboAddress outgoingECSAddr;
fe61f5d8 253 if(EDNS0Level > 0) {
81883dcc 254 DNSPacketWriter::optvect_t opts;
376effcf 255 if(srcmask) {
256 EDNSSubnetOpts eo;
257 eo.source = *srcmask;
30d4402d
RG
258 outgoingECSBits = srcmask->getBits();
259 outgoingECSAddr = srcmask->getNetwork();
bf4ab707 260 // cout<<"Adding request mask: "<<eo.source.toString()<<endl;
30d4402d 261 opts.push_back(make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(eo)));
7bb598a0 262 weWantEDNSSubnet=true;
376effcf 263 }
93d4a890 264
12ce523e 265 pw.addOpt(g_outgoingEDNSBufsize, 0, g_dnssecmode == DNSSECMode::Off ? 0 : EDNSOpts::DNSSECOK, opts);
2188dcc3
BH
266 pw.commit();
267 }
81883dcc 268 lwr->d_rcode = 0;
81883dcc 269 lwr->d_haveEDNS = false;
eefd15f9
BH
270 int ret;
271
272 DTime dt;
4c4765c1 273 dt.set();
274 *now=dt.getTimeval();
4898a348
RG
275
276#ifdef HAVE_PROTOBUF
277 boost::uuids::uuid uuid;
278 const struct timeval queryTime = *now;
8623c19a 279 boost::optional<RecProtoBufMessage> pbMessage = boost::none;
4898a348 280
b773359c 281 if (outgoingLoggers) {
d61aa945 282 uuid = getUniqueID();
8623c19a 283 logOutgoingQuery(outgoingLoggers, pbMessage, context ? context->d_initialRequestId : boost::none, uuid, ip, domain, type, qid, doTCP, vpacket.size(), srcmask);
4898a348 284 }
b9fa43e0
OM
285#endif /* HAVE_PROTOBUF */
286#ifdef HAVE_FSTRM
573f4ff0 287 if (isEnabledForQueries(fstrmLoggers)) {
6de48f75 288 logFstreamQuery(fstrmLoggers, queryTime, ip, doTCP, context ? context->d_auth : boost::none, vpacket);
b9fa43e0
OM
289 }
290#endif /* HAVE_FSTRM */
4898a348 291
0ff13512
RG
292 srcmask = boost::none; // this is also our return value, even if EDNS0Level == 0
293
5c633640 294 if(!doTCP) {
4ef015cd 295 int queryfd;
996c89cc
BH
296 if(ip.sin4.sin_family==AF_INET6)
297 g_stats.ipv6queries++;
298
4898a348 299 if((ret=asendto((const char*)&*vpacket.begin(), vpacket.size(), 0, ip, qid,
232f0877 300 domain, type, &queryfd)) < 0) {
998a4334 301 return ret; // passes back the -2 EMFILE
5c633640 302 }
deca7d8f
RG
303
304 if (queryfd == -1) {
305 *chained = true;
306 }
307
5c633640
BH
308 // sleep until we see an answer to this, interface to mtasker
309
f128d20d 310 ret=arecvfrom(buf, 0, ip, &len, qid,
232f0877 311 domain, type, queryfd, now);
e14f094b 312 }
5c633640 313 else {
998a4334 314 try {
93f4e5ce 315 Socket s(ip.sin4.sin_family, SOCK_STREAM);
fdf05fd4 316
998a4334 317 s.setNonBlocking();
1652a63e 318 ComboAddress local = getQueryLocalAddress(ip.sin4.sin_family, 0);
3ac2a2ab 319
5a38281c 320 s.bind(local);
4957a608 321
1bde6efa 322 s.connect(ip);
998a4334 323
263f6a5a
BH
324 uint16_t tlen=htons(vpacket.size());
325 char *lenP=(char*)&tlen;
998a4334
BH
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))
4957a608 331 return ret;
998a4334
BH
332
333 packet.clear();
825fa717 334 ret=arecvtcp(packet, 2, &s, false);
998a4334 335 if(!(ret > 0))
4957a608 336 return ret;
998a4334 337
a683e8bd 338 memcpy(&tlen, packet.c_str(), sizeof(tlen));
263f6a5a 339 len=ntohs(tlen); // switch to the 'len' shared with the rest of the function
998a4334 340
825fa717 341 ret=arecvtcp(packet, len, &s, false);
998a4334 342 if(!(ret > 0))
4957a608 343 return ret;
998a4334 344
78f56b38
RG
345 buf.resize(len);
346 memcpy(const_cast<char*>(buf.data()), packet.c_str(), len);
263f6a5a 347
998a4334
BH
348 ret=1;
349 }
350 catch(NetworkError& ne) {
351 ret = -2; // OS limits error
66ab6a63 352 }
5c633640 353 }
998a4334 354
81883dcc 355
26de3092
BH
356 lwr->d_usec=dt.udiff();
357 *now=dt.getTimeval();
358
57f8413e
RG
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
263f6a5a 365 return ret;
57f8413e 366 }
e14f094b 367
78f56b38 368 buf.resize(len);
b9fa43e0
OM
369
370#ifdef HAVE_FSTRM
573f4ff0 371 if (isEnabledForResponses(fstrmLoggers)) {
6de48f75 372 logFstreamResponse(fstrmLoggers, ip, doTCP, context ? context->d_auth : boost::none, buf, queryTime, *now);
b9fa43e0
OM
373 }
374#endif /* HAVE_FSTRM */
375
e325f20c 376 lwr->d_records.clear();
c836dc19 377 try {
f1f85f12 378 lwr->d_tcbit=0;
78f56b38 379 MOADNSParser mdp(false, buf);
263f6a5a
BH
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
81883dcc 384 if(mdp.d_header.rcode == RCode::FormErr && mdp.d_qname.empty() && mdp.d_qtype == 0 && mdp.d_qclass == 0) {
4898a348 385#ifdef HAVE_PROTOBUF
b773359c 386 if(outgoingLoggers) {
5d6c7a46 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);
4898a348
RG
388 }
389#endif
269ac73d 390 lwr->d_validpacket=true;
81883dcc
BH
391 return 1; // this is "success", the error is set in lwr->d_rcode
392 }
393
e325f20c 394 if(domain != mdp.d_qname) {
c5c066bf 395 if(!mdp.d_qname.empty() && domain.toString().find((char)0) == string::npos /* ugly */) {// embedded nulls are too noisy, plus empty domains are too
e6a9dde5 396 g_log<<Logger::Notice<<"Packet purporting to come from remote server "<<ip.toString()<<" contained wrong answer: '" << domain << "' != '" << mdp.d_qname << "'" << endl;
01608dca 397 }
284aa5c2 398 // unexpected count has already been done @ pdns_recursor.cc
2353fffa
BH
399 goto out;
400 }
f128d20d
RG
401
402 lwr->d_records.reserve(mdp.d_answers.size());
e325f20c 403 for(const auto& a : mdp.d_answers)
404 lwr->d_records.push_back(a.first);
81883dcc
BH
405
406 EDNSOpts edo;
57769f13 407 if(EDNS0Level > 0 && getEDNSOpts(mdp, &edo)) {
81883dcc 408 lwr->d_haveEDNS = true;
376effcf 409
7bb598a0 410 if(weWantEDNSSubnet) {
411 for(const auto& opt : edo.d_options) {
30d4402d 412 if(opt.first==EDNSOptionCode::ECS) {
7bb598a0 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;
fe61f5d8
RG
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. */
30d4402d
RG
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 }
7bb598a0 425 }
426 }
427 }
376effcf 428 }
81883dcc
BH
429 }
430
4898a348 431#ifdef HAVE_PROTOBUF
b773359c 432 if(outgoingLoggers) {
5d6c7a46 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);
4898a348
RG
434 }
435#endif
269ac73d 436 lwr->d_validpacket=true;
263f6a5a 437 return 1;
c836dc19 438 }
fdbf35ac 439 catch(std::exception &mde) {
aab4adb0 440 if(::arg().mustDo("log-common-errors"))
e6a9dde5 441 g_log<<Logger::Notice<<"Unable to parse packet from remote server "<<ip.toString()<<": "<<mde.what()<<endl;
81883dcc 442 lwr->d_rcode = RCode::FormErr;
4898a348
RG
443 g_stats.serverParseError++;
444#ifdef HAVE_PROTOBUF
b773359c 445 if(outgoingLoggers) {
5d6c7a46 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);
4898a348
RG
447 }
448#endif
269ac73d 449 lwr->d_validpacket=false;
81883dcc 450 return 1; // success - oddly enough
aab4adb0 451 }
c836dc19 452 catch(...) {
e6a9dde5 453 g_log<<Logger::Notice<<"Unknown error parsing packet from remote server"<<endl;
c836dc19 454 }
263f6a5a 455
aab4adb0 456 g_stats.serverParseError++;
263f6a5a 457
2353fffa 458 out:
81883dcc
BH
459 if(!lwr->d_rcode)
460 lwr->d_rcode=RCode::ServFail;
263f6a5a
BH
461
462 return -1;
e14f094b
BH
463}
464