2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
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.
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.
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.
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.
28 #include "dnsrecords.hh"
38 #include "pdnsexception.hh"
39 #include "arguments.hh"
42 #include "dnswriter.hh"
43 #include "dnsparser.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"
53 #include "uuid-utils.hh"
56 #include "rec-dnstap.hh"
57 #include "fstrm_logger.hh"
60 static bool isEnabledForQueries(const std::shared_ptr
<std::vector
<std::unique_ptr
<FrameStreamLogger
>>>& fstreamLoggers
)
62 if (fstreamLoggers
== nullptr) {
65 for (auto& logger
: *fstreamLoggers
) {
66 if (logger
->logQueries()) {
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
)
76 if (fstreamLoggers
== nullptr)
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);
83 message
.serialize(str
);
85 for (auto& logger
: *fstreamLoggers
) {
86 logger
->queueData(str
);
90 static bool isEnabledForResponses(const std::shared_ptr
<std::vector
<std::unique_ptr
<FrameStreamLogger
>>>& fstreamLoggers
)
92 if (fstreamLoggers
== nullptr) {
95 for (auto& logger
: *fstreamLoggers
) {
96 if (logger
->logResponses()) {
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
)
106 if (fstreamLoggers
== nullptr)
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
);
114 message
.serialize(str
);
116 for (auto& logger
: *fstreamLoggers
) {
117 logger
->queueData(str
);
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
)
125 if (!outgoingLoggers
) {
130 for (auto& logger
: *outgoingLoggers
) {
131 if (logger
->logQueries()) {
141 message
= RecProtoBufMessage(DNSProtoBufMessage::OutgoingQuery
, uuid
, nullptr, &ip
, domain
, type
, QClass::IN
, qid
, doTCP
, bytes
);
142 message
->setServerIdentity(SyncRes::s_serverID
);
144 if (initialRequestId
) {
145 message
->setInitialRequestID(*initialRequestId
);
149 message
->setEDNSSubnet(*srcmask
);
152 // cerr <<message.toDebugString()<<endl;
154 message
->serialize(str
);
156 for (auto& logger
: *outgoingLoggers
) {
157 if (logger
->logQueries()) {
158 logger
->queueData(str
);
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
)
165 if (!outgoingLoggers
) {
170 for (auto& logger
: *outgoingLoggers
) {
171 if (logger
->logResponses()) {
182 message
= RecProtoBufMessage(DNSProtoBufMessage::IncomingResponse
, uuid
, nullptr, &ip
, domain
, type
, QClass::IN
, qid
, doTCP
, bytes
);
183 message
->setServerIdentity(SyncRes::s_serverID
);
185 if (initialRequestId
) {
186 message
->setInitialRequestID(*initialRequestId
);
190 message
->setEDNSSubnet(*srcmask
);
194 message
->updateTime();
195 message
->setType(DNSProtoBufMessage::IncomingResponse
);
196 message
->setBytes(bytes
);
199 message
->setQueryTime(queryTime
.tv_sec
, queryTime
.tv_usec
);
201 message
->setNetworkErrorResponseCode();
204 message
->setResponseCode(rcode
);
206 message
->addRRs(records
, exportTypes
);
208 // cerr <<message.toDebugString()<<endl;
210 message
->serialize(str
);
212 for (auto& logger
: *outgoingLoggers
) {
213 if (logger
->logResponses()) {
214 logger
->queueData(str
);
218 #endif /* HAVE_PROTOBUF */
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
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
)
227 size_t bufsize
=g_outgoingEDNSBufsize
;
230 vector
<uint8_t> vpacket
;
231 // string mapped0x20=dns0x20(domain);
232 uint16_t qid
= dns_random(0xffff);
233 DNSPacketWriter
pw(vpacket
, domain
, type
);
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 [...]
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.
247 pw
.getHeader()->cd
=(sendRDQuery
&& g_dnssecmode
!= DNSSECMode::Off
);
250 bool weWantEDNSSubnet
=false;
251 uint8_t outgoingECSBits
= 0;
252 ComboAddress outgoingECSAddr
;
254 DNSPacketWriter::optvect_t opts
;
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;
265 pw
.addOpt(g_outgoingEDNSBufsize
, 0, g_dnssecmode
== DNSSECMode::Off
? 0 : EDNSOpts::DNSSECOK
, opts
);
269 lwr
->d_haveEDNS
= false;
274 *now
=dt
.getTimeval();
277 boost::uuids::uuid uuid
;
278 const struct timeval queryTime
= *now
;
279 boost::optional
<RecProtoBufMessage
> pbMessage
= boost::none
;
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
);
285 #endif /* HAVE_PROTOBUF */
287 if (isEnabledForQueries(fstrmLoggers
)) {
288 logFstreamQuery(fstrmLoggers
, queryTime
, ip
, doTCP
, context
? context
->d_auth
: boost::none
, vpacket
);
290 #endif /* HAVE_FSTRM */
292 srcmask
= boost::none
; // this is also our return value, even if EDNS0Level == 0
296 if(ip
.sin4
.sin_family
==AF_INET6
)
297 g_stats
.ipv6queries
++;
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
308 // sleep until we see an answer to this, interface to mtasker
310 ret
=arecvfrom(buf
, 0, ip
, &len
, qid
,
311 domain
, type
, queryfd
, now
);
315 Socket
s(ip
.sin4
.sin_family
, SOCK_STREAM
);
318 ComboAddress local
= getQueryLocalAddress(ip
.sin4
.sin_family
, 0);
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());
329 ret
=asendtcp(packet
, &s
);
334 ret
=arecvtcp(packet
, 2, &s
, false);
338 memcpy(&tlen
, packet
.c_str(), sizeof(tlen
));
339 len
=ntohs(tlen
); // switch to the 'len' shared with the rest of the function
341 ret
=arecvtcp(packet
, len
, &s
, false);
346 memcpy(const_cast<char*>(buf
.data()), packet
.c_str(), len
);
350 catch(NetworkError
& ne
) {
351 ret
= -2; // OS limits error
356 lwr
->d_usec
=dt
.udiff();
357 *now
=dt
.getTimeval();
359 if(ret
<= 0) { // includes 'timeout'
361 if (outgoingLoggers
) {
362 logIncomingResponse(outgoingLoggers
, pbMessage
, context
? context
->d_initialRequestId
: boost::none
, uuid
, ip
, domain
, type
, qid
, doTCP
, srcmask
, 0, -1, {}, queryTime
, exportTypes
);
371 if (isEnabledForResponses(fstrmLoggers
)) {
372 logFstreamResponse(fstrmLoggers
, ip
, doTCP
, context
? context
->d_auth
: boost::none
, buf
, queryTime
, *now
);
374 #endif /* HAVE_FSTRM */
376 lwr
->d_records
.clear();
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
;
384 if(mdp
.d_header
.rcode
== RCode::FormErr
&& mdp
.d_qname
.empty() && mdp
.d_qtype
== 0 && mdp
.d_qclass
== 0) {
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
);
390 lwr
->d_validpacket
=true;
391 return 1; // this is "success", the error is set in lwr->d_rcode
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
;
398 // unexpected count has already been done @ pdns_recursor.cc
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
);
407 if(EDNS0Level
> 0 && getEDNSOpts(mdp
, &edo
)) {
408 lwr
->d_haveEDNS
= true;
410 if(weWantEDNSSubnet
) {
411 for(const auto& opt
: edo
.d_options
) {
412 if(opt
.first
==EDNSOptionCode::ECS
) {
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
);
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
);
436 lwr
->d_validpacket
=true;
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
++;
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
);
449 lwr
->d_validpacket
=false;
450 return 1; // success - oddly enough
453 g_log
<<Logger::Notice
<<"Unknown error parsing packet from remote server"<<endl
;
456 g_stats
.serverParseError
++;
460 lwr
->d_rcode
=RCode::ServFail
;