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