]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/lwres.cc
rec: Don't account chained queries more than once
[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
dad05827 25#include "syncres.hh"
36c5ee42 26#include "utility.hh"
e14f094b 27#include "lwres.hh"
e14f094b 28#include <iostream>
81883dcc 29#include "dnsrecords.hh"
e14f094b
BH
30#include <errno.h>
31#include "misc.hh"
32#include <algorithm>
33#include <sstream>
34#include <cstring>
35#include <string>
36#include <vector>
e14f094b
BH
37#include "dns.hh"
38#include "qtype.hh"
5c409fa2 39#include "pdnsexception.hh"
e14f094b 40#include "arguments.hh"
5c633640 41#include "sstuff.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
53static void logOutgoingQuery(std::shared_ptr<RemoteLogger> outgoingLogger, 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)
54{
55 if(!outgoingLogger)
56 return;
57
58 RecProtoBufMessage message(DNSProtoBufMessage::OutgoingQuery, uuid, nullptr, &ip, domain, type, QClass::IN, qid, doTCP, bytes);
59 if (initialRequestId) {
60 message.setInitialRequestID(*initialRequestId);
61 }
62
63// cerr <<message.toDebugString()<<endl;
64 std::string str;
65 message.serialize(str);
66 outgoingLogger->queueData(str);
67}
68
69static void logIncomingResponse(std::shared_ptr<RemoteLogger> outgoingLogger, 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)
70{
71 if(!outgoingLogger)
72 return;
73
74 RecProtoBufMessage message(DNSProtoBufMessage::IncomingResponse, uuid, nullptr, &ip, domain, type, QClass::IN, qid, doTCP, bytes);
75 if (initialRequestId) {
76 message.setInitialRequestID(*initialRequestId);
77 }
78 message.setQueryTime(queryTime.tv_sec, queryTime.tv_usec);
79 message.setResponseCode(rcode);
80 message.addRRs(records);
81
82// cerr <<message.toDebugString()<<endl;
83 std::string str;
84 message.serialize(str);
85 outgoingLogger->queueData(str);
86}
87#endif /* HAVE_PROTOBUF */
88
81883dcc
BH
89//! returns -2 for OS limits error, -1 for permanent error that has to do with remote **transport**, 0 for timeout, 1 for success
90/** lwr is only filled out in case 1 was returned, and even when returning 1 for 'success', lwr might contain DNS errors
91 Never throws!
92 */
18d5b679 93int 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, std::shared_ptr<RemoteLogger> outgoingLogger, LWResult *lwr, bool* chained)
e14f094b 94{
a683e8bd
RG
95 size_t len;
96 size_t bufsize=g_outgoingEDNSBufsize;
263f6a5a 97 scoped_array<unsigned char> buf(new unsigned char[bufsize]);
ea634573 98 vector<uint8_t> vpacket;
51e2144e 99 // string mapped0x20=dns0x20(domain);
4898a348 100 uint16_t qid = dns_random(0xffff);
ea634573
BH
101 DNSPacketWriter pw(vpacket, domain, type);
102
c1d73d94 103 pw.getHeader()->rd=sendRDQuery;
4898a348 104 pw.getHeader()->id=qid;
5a7d2a18
PL
105 /* RFC 6840 section 5.9:
106 * This document further specifies that validating resolvers SHOULD set
107 * the CD bit on every upstream query. This is regardless of whether
108 * the CD bit was set on the incoming query [...]
109 *
110 * sendRDQuery is only true if the qname is part of a forward-zone-recurse (or
111 * set in the forward-zone-file), so we use this as an indicator for it being
112 * an "upstream query". To stay true to "dnssec=off means 3.X behaviour", we
113 * only set +CD on forwarded query in any mode other than dnssec=off.
114 */
e7b18884 115 pw.getHeader()->cd=(sendRDQuery && g_dnssecmode != DNSSECMode::Off);
5a7d2a18 116
81883dcc 117 string ping;
7bb598a0 118 bool weWantEDNSSubnet=false;
fe61f5d8 119 if(EDNS0Level > 0) {
81883dcc 120 DNSPacketWriter::optvect_t opts;
376effcf 121 if(srcmask) {
122 EDNSSubnetOpts eo;
123 eo.source = *srcmask;
bf4ab707 124 // cout<<"Adding request mask: "<<eo.source.toString()<<endl;
376effcf 125 opts.push_back(make_pair(8, makeEDNSSubnetOptsString(eo)));
7bb598a0 126 weWantEDNSSubnet=true;
376effcf 127 }
93d4a890 128
12ce523e 129 pw.addOpt(g_outgoingEDNSBufsize, 0, g_dnssecmode == DNSSECMode::Off ? 0 : EDNSOpts::DNSSECOK, opts);
2188dcc3
BH
130 pw.commit();
131 }
fe61f5d8 132 srcmask = boost::none; // this is also our return value, even if EDNS0Level == 0
81883dcc 133 lwr->d_rcode = 0;
81883dcc 134 lwr->d_haveEDNS = false;
eefd15f9
BH
135 int ret;
136
137 DTime dt;
4c4765c1 138 dt.set();
139 *now=dt.getTimeval();
4898a348
RG
140
141#ifdef HAVE_PROTOBUF
142 boost::uuids::uuid uuid;
143 const struct timeval queryTime = *now;
144
145 if (outgoingLogger) {
146 uuid = (*t_uuidGenerator)();
147 logOutgoingQuery(outgoingLogger, context ? context->d_initialRequestId : boost::none, uuid, ip, domain, type, qid, doTCP, vpacket.size());
148 }
149#endif
150
263f6a5a 151 errno=0;
5c633640 152 if(!doTCP) {
4ef015cd 153 int queryfd;
996c89cc
BH
154 if(ip.sin4.sin_family==AF_INET6)
155 g_stats.ipv6queries++;
156
4898a348 157 if((ret=asendto((const char*)&*vpacket.begin(), vpacket.size(), 0, ip, qid,
232f0877 158 domain, type, &queryfd)) < 0) {
998a4334 159 return ret; // passes back the -2 EMFILE
5c633640 160 }
18d5b679
RG
161
162 if (queryfd == -1) {
163 *chained = true;
164 }
165
5c633640
BH
166 // sleep until we see an answer to this, interface to mtasker
167
ba9200a3 168 ret=arecvfrom(reinterpret_cast<char *>(buf.get()), bufsize, 0, ip, &len, qid,
232f0877 169 domain, type, queryfd, now);
e14f094b 170 }
5c633640 171 else {
998a4334 172 try {
93f4e5ce 173 Socket s(ip.sin4.sin_family, SOCK_STREAM);
fdf05fd4 174
998a4334 175 s.setNonBlocking();
1652a63e 176 ComboAddress local = getQueryLocalAddress(ip.sin4.sin_family, 0);
3ac2a2ab 177
5a38281c 178 s.bind(local);
4957a608 179
1bde6efa 180 s.connect(ip);
998a4334 181
263f6a5a
BH
182 uint16_t tlen=htons(vpacket.size());
183 char *lenP=(char*)&tlen;
998a4334
BH
184 const char *msgP=(const char*)&*vpacket.begin();
185 string packet=string(lenP, lenP+2)+string(msgP, msgP+vpacket.size());
186
187 ret=asendtcp(packet, &s);
188 if(!(ret>0))
4957a608 189 return ret;
998a4334
BH
190
191 packet.clear();
825fa717 192 ret=arecvtcp(packet, 2, &s, false);
998a4334 193 if(!(ret > 0))
4957a608 194 return ret;
998a4334 195
a683e8bd 196 memcpy(&tlen, packet.c_str(), sizeof(tlen));
263f6a5a 197 len=ntohs(tlen); // switch to the 'len' shared with the rest of the function
998a4334 198
825fa717 199 ret=arecvtcp(packet, len, &s, false);
998a4334 200 if(!(ret > 0))
4957a608 201 return ret;
998a4334 202
263f6a5a 203 if(len > bufsize) {
4957a608
BH
204 bufsize=len;
205 scoped_array<unsigned char> narray(new unsigned char[bufsize]);
206 buf.swap(narray);
998a4334 207 }
263f6a5a
BH
208 memcpy(buf.get(), packet.c_str(), len);
209
998a4334
BH
210 ret=1;
211 }
212 catch(NetworkError& ne) {
213 ret = -2; // OS limits error
66ab6a63 214 }
5c633640 215 }
998a4334 216
81883dcc 217
26de3092
BH
218 lwr->d_usec=dt.udiff();
219 *now=dt.getTimeval();
220
263f6a5a
BH
221 if(ret <= 0) // includes 'timeout'
222 return ret;
e14f094b 223
e325f20c 224 lwr->d_records.clear();
c836dc19 225 try {
f1f85f12 226 lwr->d_tcbit=0;
27c0050c 227 MOADNSParser mdp(false, (const char*)buf.get(), len);
263f6a5a
BH
228 lwr->d_aabit=mdp.d_header.aa;
229 lwr->d_tcbit=mdp.d_header.tc;
230 lwr->d_rcode=mdp.d_header.rcode;
231
81883dcc 232 if(mdp.d_header.rcode == RCode::FormErr && mdp.d_qname.empty() && mdp.d_qtype == 0 && mdp.d_qclass == 0) {
4898a348
RG
233#ifdef HAVE_PROTOBUF
234 if(outgoingLogger) {
235 logIncomingResponse(outgoingLogger, context ? context->d_initialRequestId : boost::none, uuid, ip, domain, type, qid, doTCP, len, lwr->d_rcode, lwr->d_records, queryTime);
236 }
237#endif
81883dcc
BH
238 return 1; // this is "success", the error is set in lwr->d_rcode
239 }
240
e325f20c 241 if(domain != mdp.d_qname) {
c5c066bf 242 if(!mdp.d_qname.empty() && domain.toString().find((char)0) == string::npos /* ugly */) {// embedded nulls are too noisy, plus empty domains are too
4957a608 243 L<<Logger::Notice<<"Packet purporting to come from remote server "<<ip.toString()<<" contained wrong answer: '" << domain << "' != '" << mdp.d_qname << "'" << endl;
01608dca 244 }
284aa5c2 245 // unexpected count has already been done @ pdns_recursor.cc
2353fffa
BH
246 goto out;
247 }
e325f20c 248
249 for(const auto& a : mdp.d_answers)
250 lwr->d_records.push_back(a.first);
81883dcc
BH
251
252 EDNSOpts edo;
57769f13 253 if(EDNS0Level > 0 && getEDNSOpts(mdp, &edo)) {
81883dcc 254 lwr->d_haveEDNS = true;
376effcf 255
7bb598a0 256 if(weWantEDNSSubnet) {
257 for(const auto& opt : edo.d_options) {
258 if(opt.first==8) {
259 EDNSSubnetOpts reso;
260 if(getEDNSSubnetOptsFromString(opt.second, &reso)) {
261 // cerr<<"EDNS Subnet response: "<<reso.source.toString()<<", scope: "<<reso.scope.toString()<<", family = "<<reso.scope.getNetwork().sin4.sin_family<<endl;
fe61f5d8
RG
262 /* rfc7871 states that 0 "indicate[s] that the answer is suitable for all addresses in FAMILY",
263 so we might want to still pass the information along to be able to differentiate between
264 IPv4 and IPv6. Still I'm pretty sure it doesn't matter in real life, so let's not duplicate
265 entries in our cache. */
7bb598a0 266 if(reso.scope.getBits())
267 srcmask = reso.scope;
268 }
269 }
270 }
376effcf 271 }
81883dcc
BH
272 }
273
4898a348
RG
274#ifdef HAVE_PROTOBUF
275 if(outgoingLogger) {
276 logIncomingResponse(outgoingLogger, context ? context->d_initialRequestId : boost::none, uuid, ip, domain, type, qid, doTCP, len, lwr->d_rcode, lwr->d_records, queryTime);
277 }
278#endif
263f6a5a 279 return 1;
c836dc19 280 }
fdbf35ac 281 catch(std::exception &mde) {
aab4adb0 282 if(::arg().mustDo("log-common-errors"))
263f6a5a 283 L<<Logger::Notice<<"Unable to parse packet from remote server "<<ip.toString()<<": "<<mde.what()<<endl;
81883dcc 284 lwr->d_rcode = RCode::FormErr;
4898a348
RG
285 g_stats.serverParseError++;
286#ifdef HAVE_PROTOBUF
287 if(outgoingLogger) {
288 logIncomingResponse(outgoingLogger, context ? context->d_initialRequestId : boost::none, uuid, ip, domain, type, qid, doTCP, len, lwr->d_rcode, lwr->d_records, queryTime);
289 }
290#endif
81883dcc 291 return 1; // success - oddly enough
aab4adb0 292 }
c836dc19 293 catch(...) {
ad1bb608 294 L<<Logger::Notice<<"Unknown error parsing packet from remote server"<<endl;
c836dc19 295 }
263f6a5a 296
aab4adb0 297 g_stats.serverParseError++;
263f6a5a 298
2353fffa 299 out:
81883dcc
BH
300 if(!lwr->d_rcode)
301 lwr->d_rcode=RCode::ServFail;
263f6a5a
BH
302
303 return -1;
e14f094b
BH
304}
305