]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/lwres.cc
Merge pull request #5523 from rubenk/fix-typos-in-logmessage
[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 */
4898a348 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)
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
BH
160 }
161
162 // sleep until we see an answer to this, interface to mtasker
163
4898a348 164 ret=arecvfrom(reinterpret_cast<char *>(buf.get()), bufsize-1,0, ip, &len, qid,
232f0877 165 domain, type, queryfd, now);
e14f094b 166 }
5c633640 167 else {
998a4334 168 try {
93f4e5ce 169 Socket s(ip.sin4.sin_family, SOCK_STREAM);
fdf05fd4 170
998a4334 171 s.setNonBlocking();
1652a63e 172 ComboAddress local = getQueryLocalAddress(ip.sin4.sin_family, 0);
3ac2a2ab 173
5a38281c 174 s.bind(local);
4957a608 175
1bde6efa 176 s.connect(ip);
998a4334 177
263f6a5a
BH
178 uint16_t tlen=htons(vpacket.size());
179 char *lenP=(char*)&tlen;
998a4334
BH
180 const char *msgP=(const char*)&*vpacket.begin();
181 string packet=string(lenP, lenP+2)+string(msgP, msgP+vpacket.size());
182
183 ret=asendtcp(packet, &s);
184 if(!(ret>0))
4957a608 185 return ret;
998a4334
BH
186
187 packet.clear();
825fa717 188 ret=arecvtcp(packet, 2, &s, false);
998a4334 189 if(!(ret > 0))
4957a608 190 return ret;
998a4334 191
a683e8bd 192 memcpy(&tlen, packet.c_str(), sizeof(tlen));
263f6a5a 193 len=ntohs(tlen); // switch to the 'len' shared with the rest of the function
998a4334 194
825fa717 195 ret=arecvtcp(packet, len, &s, false);
998a4334 196 if(!(ret > 0))
4957a608 197 return ret;
998a4334 198
263f6a5a 199 if(len > bufsize) {
4957a608
BH
200 bufsize=len;
201 scoped_array<unsigned char> narray(new unsigned char[bufsize]);
202 buf.swap(narray);
998a4334 203 }
263f6a5a
BH
204 memcpy(buf.get(), packet.c_str(), len);
205
998a4334
BH
206 ret=1;
207 }
208 catch(NetworkError& ne) {
209 ret = -2; // OS limits error
66ab6a63 210 }
5c633640 211 }
998a4334 212
81883dcc 213
26de3092
BH
214 lwr->d_usec=dt.udiff();
215 *now=dt.getTimeval();
216
263f6a5a
BH
217 if(ret <= 0) // includes 'timeout'
218 return ret;
e14f094b 219
e325f20c 220 lwr->d_records.clear();
c836dc19 221 try {
f1f85f12 222 lwr->d_tcbit=0;
27c0050c 223 MOADNSParser mdp(false, (const char*)buf.get(), len);
263f6a5a
BH
224 lwr->d_aabit=mdp.d_header.aa;
225 lwr->d_tcbit=mdp.d_header.tc;
226 lwr->d_rcode=mdp.d_header.rcode;
227
81883dcc 228 if(mdp.d_header.rcode == RCode::FormErr && mdp.d_qname.empty() && mdp.d_qtype == 0 && mdp.d_qclass == 0) {
4898a348
RG
229#ifdef HAVE_PROTOBUF
230 if(outgoingLogger) {
231 logIncomingResponse(outgoingLogger, context ? context->d_initialRequestId : boost::none, uuid, ip, domain, type, qid, doTCP, len, lwr->d_rcode, lwr->d_records, queryTime);
232 }
233#endif
81883dcc
BH
234 return 1; // this is "success", the error is set in lwr->d_rcode
235 }
236
e325f20c 237 if(domain != mdp.d_qname) {
c5c066bf 238 if(!mdp.d_qname.empty() && domain.toString().find((char)0) == string::npos /* ugly */) {// embedded nulls are too noisy, plus empty domains are too
4957a608 239 L<<Logger::Notice<<"Packet purporting to come from remote server "<<ip.toString()<<" contained wrong answer: '" << domain << "' != '" << mdp.d_qname << "'" << endl;
01608dca 240 }
284aa5c2 241 // unexpected count has already been done @ pdns_recursor.cc
2353fffa
BH
242 goto out;
243 }
e325f20c 244
245 for(const auto& a : mdp.d_answers)
246 lwr->d_records.push_back(a.first);
81883dcc
BH
247
248 EDNSOpts edo;
57769f13 249 if(EDNS0Level > 0 && getEDNSOpts(mdp, &edo)) {
81883dcc 250 lwr->d_haveEDNS = true;
376effcf 251
7bb598a0 252 if(weWantEDNSSubnet) {
253 for(const auto& opt : edo.d_options) {
254 if(opt.first==8) {
255 EDNSSubnetOpts reso;
256 if(getEDNSSubnetOptsFromString(opt.second, &reso)) {
257 // cerr<<"EDNS Subnet response: "<<reso.source.toString()<<", scope: "<<reso.scope.toString()<<", family = "<<reso.scope.getNetwork().sin4.sin_family<<endl;
fe61f5d8
RG
258 /* rfc7871 states that 0 "indicate[s] that the answer is suitable for all addresses in FAMILY",
259 so we might want to still pass the information along to be able to differentiate between
260 IPv4 and IPv6. Still I'm pretty sure it doesn't matter in real life, so let's not duplicate
261 entries in our cache. */
7bb598a0 262 if(reso.scope.getBits())
263 srcmask = reso.scope;
264 }
265 }
266 }
376effcf 267 }
81883dcc
BH
268 }
269
4898a348
RG
270#ifdef HAVE_PROTOBUF
271 if(outgoingLogger) {
272 logIncomingResponse(outgoingLogger, context ? context->d_initialRequestId : boost::none, uuid, ip, domain, type, qid, doTCP, len, lwr->d_rcode, lwr->d_records, queryTime);
273 }
274#endif
263f6a5a 275 return 1;
c836dc19 276 }
fdbf35ac 277 catch(std::exception &mde) {
aab4adb0 278 if(::arg().mustDo("log-common-errors"))
263f6a5a 279 L<<Logger::Notice<<"Unable to parse packet from remote server "<<ip.toString()<<": "<<mde.what()<<endl;
81883dcc 280 lwr->d_rcode = RCode::FormErr;
4898a348
RG
281 g_stats.serverParseError++;
282#ifdef HAVE_PROTOBUF
283 if(outgoingLogger) {
284 logIncomingResponse(outgoingLogger, context ? context->d_initialRequestId : boost::none, uuid, ip, domain, type, qid, doTCP, len, lwr->d_rcode, lwr->d_records, queryTime);
285 }
286#endif
81883dcc 287 return 1; // success - oddly enough
aab4adb0 288 }
c836dc19 289 catch(...) {
ad1bb608 290 L<<Logger::Notice<<"Unknown error parsing packet from remote server"<<endl;
c836dc19 291 }
263f6a5a 292
aab4adb0 293 g_stats.serverParseError++;
263f6a5a 294
2353fffa 295 out:
81883dcc
BH
296 if(!lwr->d_rcode)
297 lwr->d_rcode=RCode::ServFail;
263f6a5a
BH
298
299 return -1;
e14f094b
BH
300}
301