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"
55 static void logOutgoingQuery(const std::shared_ptr
<std::vector
<std::unique_ptr
<RemoteLogger
>>>& outgoingLoggers
, 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
)
60 RecProtoBufMessage
message(DNSProtoBufMessage::OutgoingQuery
, uuid
, nullptr, &ip
, domain
, type
, QClass::IN
, qid
, doTCP
, bytes
);
61 message
.setServerIdentity(SyncRes::s_serverID
);
63 if (initialRequestId
) {
64 message
.setInitialRequestID(*initialRequestId
);
68 message
.setEDNSSubnet(*srcmask
);
71 // cerr <<message.toDebugString()<<endl;
73 message
.serialize(str
);
75 for (auto& logger
: *outgoingLoggers
) {
76 logger
->queueData(str
);
80 static void logIncomingResponse(const std::shared_ptr
<std::vector
<std::unique_ptr
<RemoteLogger
>>>& outgoingLoggers
, 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
, int rcode
, const std::vector
<DNSRecord
>& records
, const struct timeval
& queryTime
, const std::set
<uint16_t>& exportTypes
)
85 RecProtoBufMessage
message(DNSProtoBufMessage::IncomingResponse
, uuid
, nullptr, &ip
, domain
, type
, QClass::IN
, qid
, doTCP
, bytes
);
86 message
.setServerIdentity(SyncRes::s_serverID
);
87 if (initialRequestId
) {
88 message
.setInitialRequestID(*initialRequestId
);
90 message
.setQueryTime(queryTime
.tv_sec
, queryTime
.tv_usec
);
91 message
.setResponseCode(rcode
);
92 message
.addRRs(records
, exportTypes
);
94 // cerr <<message.toDebugString()<<endl;
96 message
.serialize(str
);
98 for (auto& logger
: *outgoingLoggers
) {
99 logger
->queueData(str
);
102 #endif /* HAVE_PROTOBUF */
104 //! returns -2 for OS limits error, -1 for permanent error that has to do with remote **transport**, 0 for timeout, 1 for success
105 /** lwr is only filled out in case 1 was returned, and even when returning 1 for 'success', lwr might contain DNS errors
108 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::set
<uint16_t>& exportTypes
, LWResult
*lwr
, bool* chained
)
111 size_t bufsize
=g_outgoingEDNSBufsize
;
114 vector
<uint8_t> vpacket
;
115 // string mapped0x20=dns0x20(domain);
116 uint16_t qid
= dns_random(0xffff);
117 DNSPacketWriter
pw(vpacket
, domain
, type
);
119 pw
.getHeader()->rd
=sendRDQuery
;
120 pw
.getHeader()->id
=qid
;
121 /* RFC 6840 section 5.9:
122 * This document further specifies that validating resolvers SHOULD set
123 * the CD bit on every upstream query. This is regardless of whether
124 * the CD bit was set on the incoming query [...]
126 * sendRDQuery is only true if the qname is part of a forward-zone-recurse (or
127 * set in the forward-zone-file), so we use this as an indicator for it being
128 * an "upstream query". To stay true to "dnssec=off means 3.X behaviour", we
129 * only set +CD on forwarded query in any mode other than dnssec=off.
131 pw
.getHeader()->cd
=(sendRDQuery
&& g_dnssecmode
!= DNSSECMode::Off
);
134 bool weWantEDNSSubnet
=false;
135 uint8_t outgoingECSBits
= 0;
136 ComboAddress outgoingECSAddr
;
138 DNSPacketWriter::optvect_t opts
;
141 eo
.source
= *srcmask
;
142 outgoingECSBits
= srcmask
->getBits();
143 outgoingECSAddr
= srcmask
->getNetwork();
144 // cout<<"Adding request mask: "<<eo.source.toString()<<endl;
145 opts
.push_back(make_pair(EDNSOptionCode::ECS
, makeEDNSSubnetOptsString(eo
)));
146 weWantEDNSSubnet
=true;
149 pw
.addOpt(g_outgoingEDNSBufsize
, 0, g_dnssecmode
== DNSSECMode::Off
? 0 : EDNSOpts::DNSSECOK
, opts
);
153 lwr
->d_haveEDNS
= false;
158 *now
=dt
.getTimeval();
161 boost::uuids::uuid uuid
;
162 const struct timeval queryTime
= *now
;
164 if (outgoingLoggers
) {
165 uuid
= getUniqueID();
166 logOutgoingQuery(outgoingLoggers
, context
? context
->d_initialRequestId
: boost::none
, uuid
, ip
, domain
, type
, qid
, doTCP
, vpacket
.size(), srcmask
);
170 srcmask
= boost::none
; // this is also our return value, even if EDNS0Level == 0
175 if(ip
.sin4
.sin_family
==AF_INET6
)
176 g_stats
.ipv6queries
++;
178 if((ret
=asendto((const char*)&*vpacket
.begin(), vpacket
.size(), 0, ip
, qid
,
179 domain
, type
, &queryfd
)) < 0) {
180 return ret
; // passes back the -2 EMFILE
187 // sleep until we see an answer to this, interface to mtasker
189 ret
=arecvfrom(buf
, 0, ip
, &len
, qid
,
190 domain
, type
, queryfd
, now
);
194 Socket
s(ip
.sin4
.sin_family
, SOCK_STREAM
);
197 ComboAddress local
= getQueryLocalAddress(ip
.sin4
.sin_family
, 0);
203 uint16_t tlen
=htons(vpacket
.size());
204 char *lenP
=(char*)&tlen
;
205 const char *msgP
=(const char*)&*vpacket
.begin();
206 string packet
=string(lenP
, lenP
+2)+string(msgP
, msgP
+vpacket
.size());
208 ret
=asendtcp(packet
, &s
);
213 ret
=arecvtcp(packet
, 2, &s
, false);
217 memcpy(&tlen
, packet
.c_str(), sizeof(tlen
));
218 len
=ntohs(tlen
); // switch to the 'len' shared with the rest of the function
220 ret
=arecvtcp(packet
, len
, &s
, false);
225 memcpy(const_cast<char*>(buf
.data()), packet
.c_str(), len
);
229 catch(NetworkError
& ne
) {
230 ret
= -2; // OS limits error
235 lwr
->d_usec
=dt
.udiff();
236 *now
=dt
.getTimeval();
238 if(ret
<= 0) // includes 'timeout'
242 lwr
->d_records
.clear();
245 MOADNSParser
mdp(false, buf
);
246 lwr
->d_aabit
=mdp
.d_header
.aa
;
247 lwr
->d_tcbit
=mdp
.d_header
.tc
;
248 lwr
->d_rcode
=mdp
.d_header
.rcode
;
250 if(mdp
.d_header
.rcode
== RCode::FormErr
&& mdp
.d_qname
.empty() && mdp
.d_qtype
== 0 && mdp
.d_qclass
== 0) {
252 if(outgoingLoggers
) {
253 logIncomingResponse(outgoingLoggers
, context
? context
->d_initialRequestId
: boost::none
, uuid
, ip
, domain
, type
, qid
, doTCP
, len
, lwr
->d_rcode
, lwr
->d_records
, queryTime
, exportTypes
);
256 lwr
->d_validpacket
=true;
257 return 1; // this is "success", the error is set in lwr->d_rcode
260 if(domain
!= mdp
.d_qname
) {
261 if(!mdp
.d_qname
.empty() && domain
.toString().find((char)0) == string::npos
/* ugly */) {// embedded nulls are too noisy, plus empty domains are too
262 g_log
<<Logger::Notice
<<"Packet purporting to come from remote server "<<ip
.toString()<<" contained wrong answer: '" << domain
<< "' != '" << mdp
.d_qname
<< "'" << endl
;
264 // unexpected count has already been done @ pdns_recursor.cc
268 lwr
->d_records
.reserve(mdp
.d_answers
.size());
269 for(const auto& a
: mdp
.d_answers
)
270 lwr
->d_records
.push_back(a
.first
);
273 if(EDNS0Level
> 0 && getEDNSOpts(mdp
, &edo
)) {
274 lwr
->d_haveEDNS
= true;
276 if(weWantEDNSSubnet
) {
277 for(const auto& opt
: edo
.d_options
) {
278 if(opt
.first
==EDNSOptionCode::ECS
) {
280 if(getEDNSSubnetOptsFromString(opt
.second
, &reso
)) {
281 // cerr<<"EDNS Subnet response: "<<reso.source.toString()<<", scope: "<<reso.scope.toString()<<", family = "<<reso.scope.getNetwork().sin4.sin_family<<endl;
282 /* rfc7871 states that 0 "indicate[s] that the answer is suitable for all addresses in FAMILY",
283 so we might want to still pass the information along to be able to differentiate between
284 IPv4 and IPv6. Still I'm pretty sure it doesn't matter in real life, so let's not duplicate
285 entries in our cache. */
286 if(reso
.scope
.getBits()) {
287 uint8_t bits
= std::min(reso
.scope
.getBits(), outgoingECSBits
);
288 outgoingECSAddr
.truncate(bits
);
289 srcmask
= Netmask(outgoingECSAddr
, bits
);
298 if(outgoingLoggers
) {
299 logIncomingResponse(outgoingLoggers
, context
? context
->d_initialRequestId
: boost::none
, uuid
, ip
, domain
, type
, qid
, doTCP
, len
, lwr
->d_rcode
, lwr
->d_records
, queryTime
, exportTypes
);
302 lwr
->d_validpacket
=true;
305 catch(std::exception
&mde
) {
306 if(::arg().mustDo("log-common-errors"))
307 g_log
<<Logger::Notice
<<"Unable to parse packet from remote server "<<ip
.toString()<<": "<<mde
.what()<<endl
;
308 lwr
->d_rcode
= RCode::FormErr
;
309 g_stats
.serverParseError
++;
311 if(outgoingLoggers
) {
312 logIncomingResponse(outgoingLoggers
, context
? context
->d_initialRequestId
: boost::none
, uuid
, ip
, domain
, type
, qid
, doTCP
, len
, lwr
->d_rcode
, lwr
->d_records
, queryTime
, exportTypes
);
315 lwr
->d_validpacket
=false;
316 return 1; // success - oddly enough
319 g_log
<<Logger::Notice
<<"Unknown error parsing packet from remote server"<<endl
;
322 g_stats
.serverParseError
++;
326 lwr
->d_rcode
=RCode::ServFail
;