]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/lwres.cc
rec: Use a string instead of a fixed 65k buffer for TCP connections
[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 */
4f3e77c2 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, const 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
269ac73d 242 lwr->d_validpacket=true;
81883dcc
BH
243 return 1; // this is "success", the error is set in lwr->d_rcode
244 }
245
e325f20c 246 if(domain != mdp.d_qname) {
c5c066bf 247 if(!mdp.d_qname.empty() && domain.toString().find((char)0) == string::npos /* ugly */) {// embedded nulls are too noisy, plus empty domains are too
e6a9dde5 248 g_log<<Logger::Notice<<"Packet purporting to come from remote server "<<ip.toString()<<" contained wrong answer: '" << domain << "' != '" << mdp.d_qname << "'" << endl;
01608dca 249 }
284aa5c2 250 // unexpected count has already been done @ pdns_recursor.cc
2353fffa
BH
251 goto out;
252 }
e325f20c 253
254 for(const auto& a : mdp.d_answers)
255 lwr->d_records.push_back(a.first);
81883dcc
BH
256
257 EDNSOpts edo;
57769f13 258 if(EDNS0Level > 0 && getEDNSOpts(mdp, &edo)) {
81883dcc 259 lwr->d_haveEDNS = true;
376effcf 260
7bb598a0 261 if(weWantEDNSSubnet) {
262 for(const auto& opt : edo.d_options) {
30d4402d 263 if(opt.first==EDNSOptionCode::ECS) {
7bb598a0 264 EDNSSubnetOpts reso;
265 if(getEDNSSubnetOptsFromString(opt.second, &reso)) {
266 // cerr<<"EDNS Subnet response: "<<reso.source.toString()<<", scope: "<<reso.scope.toString()<<", family = "<<reso.scope.getNetwork().sin4.sin_family<<endl;
fe61f5d8
RG
267 /* rfc7871 states that 0 "indicate[s] that the answer is suitable for all addresses in FAMILY",
268 so we might want to still pass the information along to be able to differentiate between
269 IPv4 and IPv6. Still I'm pretty sure it doesn't matter in real life, so let's not duplicate
270 entries in our cache. */
30d4402d
RG
271 if(reso.scope.getBits()) {
272 uint8_t bits = std::min(reso.scope.getBits(), outgoingECSBits);
273 outgoingECSAddr.truncate(bits);
274 srcmask = Netmask(outgoingECSAddr, bits);
275 }
7bb598a0 276 }
277 }
278 }
376effcf 279 }
81883dcc
BH
280 }
281
4898a348
RG
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
269ac73d 287 lwr->d_validpacket=true;
263f6a5a 288 return 1;
c836dc19 289 }
fdbf35ac 290 catch(std::exception &mde) {
aab4adb0 291 if(::arg().mustDo("log-common-errors"))
e6a9dde5 292 g_log<<Logger::Notice<<"Unable to parse packet from remote server "<<ip.toString()<<": "<<mde.what()<<endl;
81883dcc 293 lwr->d_rcode = RCode::FormErr;
4898a348
RG
294 g_stats.serverParseError++;
295#ifdef HAVE_PROTOBUF
296 if(outgoingLogger) {
297 logIncomingResponse(outgoingLogger, context ? context->d_initialRequestId : boost::none, uuid, ip, domain, type, qid, doTCP, len, lwr->d_rcode, lwr->d_records, queryTime);
298 }
299#endif
269ac73d 300 lwr->d_validpacket=false;
81883dcc 301 return 1; // success - oddly enough
aab4adb0 302 }
c836dc19 303 catch(...) {
e6a9dde5 304 g_log<<Logger::Notice<<"Unknown error parsing packet from remote server"<<endl;
c836dc19 305 }
263f6a5a 306
aab4adb0 307 g_stats.serverParseError++;
263f6a5a 308
2353fffa 309 out:
81883dcc
BH
310 if(!lwr->d_rcode)
311 lwr->d_rcode=RCode::ServFail;
263f6a5a
BH
312
313 return -1;
e14f094b
BH
314}
315