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"
50 #include "query-local-address.hh"
54 #include "uuid-utils.hh"
57 #include "rec-dnstap.hh"
58 #include "fstrm_logger.hh"
63 static bool isEnabledForQueries(const std::shared_ptr
<std::vector
<std::unique_ptr
<FrameStreamLogger
>>>& fstreamLoggers
)
65 if (fstreamLoggers
== nullptr) {
68 for (auto& logger
: *fstreamLoggers
) {
69 if (logger
->logQueries()) {
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
)
79 if (fstreamLoggers
== nullptr)
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);
86 message
.serialize(str
);
88 for (auto& logger
: *fstreamLoggers
) {
89 logger
->queueData(str
);
93 static bool isEnabledForResponses(const std::shared_ptr
<std::vector
<std::unique_ptr
<FrameStreamLogger
>>>& fstreamLoggers
)
95 if (fstreamLoggers
== nullptr) {
98 for (auto& logger
: *fstreamLoggers
) {
99 if (logger
->logResponses()) {
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
)
109 if (fstreamLoggers
== nullptr)
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
);
117 message
.serialize(str
);
119 for (auto& logger
: *fstreamLoggers
) {
120 logger
->queueData(str
);
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
)
128 if (!outgoingLoggers
) {
133 for (auto& logger
: *outgoingLoggers
) {
134 if (logger
->logQueries()) {
144 message
= RecProtoBufMessage(DNSProtoBufMessage::OutgoingQuery
, uuid
, nullptr, &ip
, domain
, type
, QClass::IN
, qid
, doTCP
, bytes
);
145 message
->setServerIdentity(SyncRes::s_serverID
);
147 if (initialRequestId
) {
148 message
->setInitialRequestID(*initialRequestId
);
152 message
->setEDNSSubnet(*srcmask
);
155 // cerr <<message.toDebugString()<<endl;
157 message
->serialize(str
);
159 for (auto& logger
: *outgoingLoggers
) {
160 if (logger
->logQueries()) {
161 logger
->queueData(str
);
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
)
168 if (!outgoingLoggers
) {
173 for (auto& logger
: *outgoingLoggers
) {
174 if (logger
->logResponses()) {
185 message
= RecProtoBufMessage(DNSProtoBufMessage::IncomingResponse
, uuid
, nullptr, &ip
, domain
, type
, QClass::IN
, qid
, doTCP
, bytes
);
186 message
->setServerIdentity(SyncRes::s_serverID
);
188 if (initialRequestId
) {
189 message
->setInitialRequestID(*initialRequestId
);
193 message
->setEDNSSubnet(*srcmask
);
197 message
->updateTime();
198 message
->setType(DNSProtoBufMessage::IncomingResponse
);
199 message
->setBytes(bytes
);
202 message
->setQueryTime(queryTime
.tv_sec
, queryTime
.tv_usec
);
204 message
->setNetworkErrorResponseCode();
207 message
->setResponseCode(rcode
);
209 message
->addRRs(records
, exportTypes
);
211 // cerr <<message.toDebugString()<<endl;
213 message
->serialize(str
);
215 for (auto& logger
: *outgoingLoggers
) {
216 if (logger
->logResponses()) {
217 logger
->queueData(str
);
221 #endif /* HAVE_PROTOBUF */
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
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
)
230 size_t bufsize
=g_outgoingEDNSBufsize
;
233 vector
<uint8_t> vpacket
;
234 // string mapped0x20=dns0x20(domain);
235 uint16_t qid
= dns_random_uint16();
236 DNSPacketWriter
pw(vpacket
, domain
, type
);
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 [...]
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.
250 pw
.getHeader()->cd
=(sendRDQuery
&& g_dnssecmode
!= DNSSECMode::Off
);
253 bool weWantEDNSSubnet
=false;
254 uint8_t outgoingECSBits
= 0;
255 ComboAddress outgoingECSAddr
;
257 DNSPacketWriter::optvect_t opts
;
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;
268 pw
.addOpt(g_outgoingEDNSBufsize
, 0, g_dnssecmode
== DNSSECMode::Off
? 0 : EDNSOpts::DNSSECOK
, opts
);
272 lwr
->d_haveEDNS
= false;
277 *now
=dt
.getTimeval();
280 boost::uuids::uuid uuid
;
281 const struct timeval queryTime
= *now
;
282 boost::optional
<RecProtoBufMessage
> pbMessage
= boost::none
;
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
);
288 #endif /* HAVE_PROTOBUF */
290 if (isEnabledForQueries(fstrmLoggers
)) {
291 logFstreamQuery(fstrmLoggers
, queryTime
, ip
, doTCP
, context
? context
->d_auth
: boost::none
, vpacket
);
293 #endif /* HAVE_FSTRM */
295 srcmask
= boost::none
; // this is also our return value, even if EDNS0Level == 0
299 if(ip
.sin4
.sin_family
==AF_INET6
)
300 g_stats
.ipv6queries
++;
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
311 // sleep until we see an answer to this, interface to mtasker
313 ret
=arecvfrom(buf
, 0, ip
, &len
, qid
,
314 domain
, type
, queryfd
, now
);
318 Socket
s(ip
.sin4
.sin_family
, SOCK_STREAM
);
321 ComboAddress local
= pdns::getQueryLocalAddress(ip
.sin4
.sin_family
, 0);
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());
332 ret
=asendtcp(packet
, &s
);
337 ret
=arecvtcp(packet
, 2, &s
, false);
341 memcpy(&tlen
, packet
.c_str(), sizeof(tlen
));
342 len
=ntohs(tlen
); // switch to the 'len' shared with the rest of the function
344 ret
=arecvtcp(packet
, len
, &s
, false);
349 memcpy(const_cast<char*>(buf
.data()), packet
.c_str(), len
);
353 catch(NetworkError
& ne
) {
354 ret
= -2; // OS limits error
359 lwr
->d_usec
=dt
.udiff();
360 *now
=dt
.getTimeval();
362 if(ret
<= 0) { // includes 'timeout'
364 if (outgoingLoggers
) {
365 logIncomingResponse(outgoingLoggers
, pbMessage
, context
? context
->d_initialRequestId
: boost::none
, uuid
, ip
, domain
, type
, qid
, doTCP
, srcmask
, 0, -1, {}, queryTime
, exportTypes
);
374 if (isEnabledForResponses(fstrmLoggers
)) {
375 logFstreamResponse(fstrmLoggers
, ip
, doTCP
, context
? context
->d_auth
: boost::none
, buf
, queryTime
, *now
);
377 #endif /* HAVE_FSTRM */
379 lwr
->d_records
.clear();
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
;
387 if(mdp
.d_header
.rcode
== RCode::FormErr
&& mdp
.d_qname
.empty() && mdp
.d_qtype
== 0 && mdp
.d_qclass
== 0) {
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
);
393 lwr
->d_validpacket
=true;
394 return 1; // this is "success", the error is set in lwr->d_rcode
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
;
401 // unexpected count has already been done @ pdns_recursor.cc
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
);
410 if(EDNS0Level
> 0 && getEDNSOpts(mdp
, &edo
)) {
411 lwr
->d_haveEDNS
= true;
413 if(weWantEDNSSubnet
) {
414 for(const auto& opt
: edo
.d_options
) {
415 if(opt
.first
==EDNSOptionCode::ECS
) {
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
);
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
);
439 lwr
->d_validpacket
=true;
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
++;
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
);
452 lwr
->d_validpacket
=false;
453 return 1; // success - oddly enough
456 g_log
<<Logger::Notice
<<"Unknown error parsing packet from remote server"<<endl
;
459 g_stats
.serverParseError
++;
463 lwr
->d_rcode
=RCode::ServFail
;