]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/lwres.cc
Merge pull request #6620 from Habbie/nit
[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
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 */
deca7d8f 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;
30d4402d
RG
119 uint8_t outgoingECSBits = 0;
120 ComboAddress outgoingECSAddr;
fe61f5d8 121 if(EDNS0Level > 0) {
81883dcc 122 DNSPacketWriter::optvect_t opts;
376effcf 123 if(srcmask) {
124 EDNSSubnetOpts eo;
125 eo.source = *srcmask;
30d4402d
RG
126 outgoingECSBits = srcmask->getBits();
127 outgoingECSAddr = srcmask->getNetwork();
bf4ab707 128 // cout<<"Adding request mask: "<<eo.source.toString()<<endl;
30d4402d 129 opts.push_back(make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(eo)));
7bb598a0 130 weWantEDNSSubnet=true;
376effcf 131 }
93d4a890 132
12ce523e 133 pw.addOpt(g_outgoingEDNSBufsize, 0, g_dnssecmode == DNSSECMode::Off ? 0 : EDNSOpts::DNSSECOK, opts);
2188dcc3
BH
134 pw.commit();
135 }
fe61f5d8 136 srcmask = boost::none; // this is also our return value, even if EDNS0Level == 0
81883dcc 137 lwr->d_rcode = 0;
81883dcc 138 lwr->d_haveEDNS = false;
eefd15f9
BH
139 int ret;
140
141 DTime dt;
4c4765c1 142 dt.set();
143 *now=dt.getTimeval();
4898a348
RG
144
145#ifdef HAVE_PROTOBUF
146 boost::uuids::uuid uuid;
147 const struct timeval queryTime = *now;
148
149 if (outgoingLogger) {
150 uuid = (*t_uuidGenerator)();
151 logOutgoingQuery(outgoingLogger, context ? context->d_initialRequestId : boost::none, uuid, ip, domain, type, qid, doTCP, vpacket.size());
152 }
153#endif
154
263f6a5a 155 errno=0;
5c633640 156 if(!doTCP) {
4ef015cd 157 int queryfd;
996c89cc
BH
158 if(ip.sin4.sin_family==AF_INET6)
159 g_stats.ipv6queries++;
160
4898a348 161 if((ret=asendto((const char*)&*vpacket.begin(), vpacket.size(), 0, ip, qid,
232f0877 162 domain, type, &queryfd)) < 0) {
998a4334 163 return ret; // passes back the -2 EMFILE
5c633640 164 }
deca7d8f
RG
165
166 if (queryfd == -1) {
167 *chained = true;
168 }
169
5c633640
BH
170 // sleep until we see an answer to this, interface to mtasker
171
4185f089 172 ret=arecvfrom(reinterpret_cast<char *>(buf.get()), bufsize, 0, ip, &len, qid,
232f0877 173 domain, type, queryfd, now);
e14f094b 174 }
5c633640 175 else {
998a4334 176 try {
93f4e5ce 177 Socket s(ip.sin4.sin_family, SOCK_STREAM);
fdf05fd4 178
998a4334 179 s.setNonBlocking();
1652a63e 180 ComboAddress local = getQueryLocalAddress(ip.sin4.sin_family, 0);
3ac2a2ab 181
5a38281c 182 s.bind(local);
4957a608 183
1bde6efa 184 s.connect(ip);
998a4334 185
263f6a5a
BH
186 uint16_t tlen=htons(vpacket.size());
187 char *lenP=(char*)&tlen;
998a4334
BH
188 const char *msgP=(const char*)&*vpacket.begin();
189 string packet=string(lenP, lenP+2)+string(msgP, msgP+vpacket.size());
190
191 ret=asendtcp(packet, &s);
192 if(!(ret>0))
4957a608 193 return ret;
998a4334
BH
194
195 packet.clear();
825fa717 196 ret=arecvtcp(packet, 2, &s, false);
998a4334 197 if(!(ret > 0))
4957a608 198 return ret;
998a4334 199
a683e8bd 200 memcpy(&tlen, packet.c_str(), sizeof(tlen));
263f6a5a 201 len=ntohs(tlen); // switch to the 'len' shared with the rest of the function
998a4334 202
825fa717 203 ret=arecvtcp(packet, len, &s, false);
998a4334 204 if(!(ret > 0))
4957a608 205 return ret;
998a4334 206
263f6a5a 207 if(len > bufsize) {
4957a608
BH
208 bufsize=len;
209 scoped_array<unsigned char> narray(new unsigned char[bufsize]);
210 buf.swap(narray);
998a4334 211 }
263f6a5a
BH
212 memcpy(buf.get(), packet.c_str(), len);
213
998a4334
BH
214 ret=1;
215 }
216 catch(NetworkError& ne) {
217 ret = -2; // OS limits error
66ab6a63 218 }
5c633640 219 }
998a4334 220
81883dcc 221
26de3092
BH
222 lwr->d_usec=dt.udiff();
223 *now=dt.getTimeval();
224
263f6a5a
BH
225 if(ret <= 0) // includes 'timeout'
226 return ret;
e14f094b 227
e325f20c 228 lwr->d_records.clear();
c836dc19 229 try {
f1f85f12 230 lwr->d_tcbit=0;
27c0050c 231 MOADNSParser mdp(false, (const char*)buf.get(), len);
263f6a5a
BH
232 lwr->d_aabit=mdp.d_header.aa;
233 lwr->d_tcbit=mdp.d_header.tc;
234 lwr->d_rcode=mdp.d_header.rcode;
235
81883dcc 236 if(mdp.d_header.rcode == RCode::FormErr && mdp.d_qname.empty() && mdp.d_qtype == 0 && mdp.d_qclass == 0) {
4898a348
RG
237#ifdef HAVE_PROTOBUF
238 if(outgoingLogger) {
239 logIncomingResponse(outgoingLogger, context ? context->d_initialRequestId : boost::none, uuid, ip, domain, type, qid, doTCP, len, lwr->d_rcode, lwr->d_records, queryTime);
240 }
241#endif
81883dcc
BH
242 return 1; // this is "success", the error is set in lwr->d_rcode
243 }
244
e325f20c 245 if(domain != mdp.d_qname) {
c5c066bf 246 if(!mdp.d_qname.empty() && domain.toString().find((char)0) == string::npos /* ugly */) {// embedded nulls are too noisy, plus empty domains are too
e6a9dde5 247 g_log<<Logger::Notice<<"Packet purporting to come from remote server "<<ip.toString()<<" contained wrong answer: '" << domain << "' != '" << mdp.d_qname << "'" << endl;
01608dca 248 }
284aa5c2 249 // unexpected count has already been done @ pdns_recursor.cc
2353fffa
BH
250 goto out;
251 }
e325f20c 252
253 for(const auto& a : mdp.d_answers)
254 lwr->d_records.push_back(a.first);
81883dcc
BH
255
256 EDNSOpts edo;
57769f13 257 if(EDNS0Level > 0 && getEDNSOpts(mdp, &edo)) {
81883dcc 258 lwr->d_haveEDNS = true;
376effcf 259
7bb598a0 260 if(weWantEDNSSubnet) {
261 for(const auto& opt : edo.d_options) {
30d4402d 262 if(opt.first==EDNSOptionCode::ECS) {
7bb598a0 263 EDNSSubnetOpts reso;
264 if(getEDNSSubnetOptsFromString(opt.second, &reso)) {
265 // cerr<<"EDNS Subnet response: "<<reso.source.toString()<<", scope: "<<reso.scope.toString()<<", family = "<<reso.scope.getNetwork().sin4.sin_family<<endl;
fe61f5d8
RG
266 /* rfc7871 states that 0 "indicate[s] that the answer is suitable for all addresses in FAMILY",
267 so we might want to still pass the information along to be able to differentiate between
268 IPv4 and IPv6. Still I'm pretty sure it doesn't matter in real life, so let's not duplicate
269 entries in our cache. */
30d4402d
RG
270 if(reso.scope.getBits()) {
271 uint8_t bits = std::min(reso.scope.getBits(), outgoingECSBits);
272 outgoingECSAddr.truncate(bits);
273 srcmask = Netmask(outgoingECSAddr, bits);
274 }
7bb598a0 275 }
276 }
277 }
376effcf 278 }
81883dcc
BH
279 }
280
4898a348
RG
281#ifdef HAVE_PROTOBUF
282 if(outgoingLogger) {
283 logIncomingResponse(outgoingLogger, context ? context->d_initialRequestId : boost::none, uuid, ip, domain, type, qid, doTCP, len, lwr->d_rcode, lwr->d_records, queryTime);
284 }
285#endif
263f6a5a 286 return 1;
c836dc19 287 }
fdbf35ac 288 catch(std::exception &mde) {
aab4adb0 289 if(::arg().mustDo("log-common-errors"))
e6a9dde5 290 g_log<<Logger::Notice<<"Unable to parse packet from remote server "<<ip.toString()<<": "<<mde.what()<<endl;
81883dcc 291 lwr->d_rcode = RCode::FormErr;
4898a348
RG
292 g_stats.serverParseError++;
293#ifdef HAVE_PROTOBUF
294 if(outgoingLogger) {
295 logIncomingResponse(outgoingLogger, context ? context->d_initialRequestId : boost::none, uuid, ip, domain, type, qid, doTCP, len, lwr->d_rcode, lwr->d_records, queryTime);
296 }
297#endif
81883dcc 298 return 1; // success - oddly enough
aab4adb0 299 }
c836dc19 300 catch(...) {
e6a9dde5 301 g_log<<Logger::Notice<<"Unknown error parsing packet from remote server"<<endl;
c836dc19 302 }
263f6a5a 303
aab4adb0 304 g_stats.serverParseError++;
263f6a5a 305
2353fffa 306 out:
81883dcc
BH
307 if(!lwr->d_rcode)
308 lwr->d_rcode=RCode::ServFail;
263f6a5a
BH
309
310 return -1;
e14f094b
BH
311}
312