]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/lwres.cc
21093ea8e57ea6d54c5cf0ee0a2d2d662898dddc
[thirdparty/pdns.git] / pdns / lwres.cc
1 /*
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 */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include "utility.hh"
26 #include "lwres.hh"
27 #include <iostream>
28 #include "dnsrecords.hh"
29 #include <errno.h>
30 #include "misc.hh"
31 #include <algorithm>
32 #include <sstream>
33 #include <cstring>
34 #include <string>
35 #include <vector>
36 #include "dns.hh"
37 #include "qtype.hh"
38 #include "pdnsexception.hh"
39 #include "arguments.hh"
40 #include "sstuff.hh"
41 #include "syncres.hh"
42 #include "dnswriter.hh"
43 #include "dnsparser.hh"
44 #include "logger.hh"
45 #include "dns_random.hh"
46 #include <boost/scoped_array.hpp>
47 #include <boost/algorithm/string.hpp>
48 #include "validate-recursor.hh"
49 #include "ednssubnet.hh"
50
51 #ifdef HAVE_PROTOBUF
52
53 static 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)
54 {
55 if(!outgoingLogger)
56 return;
57
58 RecProtoBufMessage message(DNSProtoBufMessage::OutgoingQuery, uuid, nullptr, &ip, domain, type, QClass::IN, qid, doTCP, bytes);
59 message.setServerIdentity(SyncRes::s_serverID);
60
61 if (initialRequestId) {
62 message.setInitialRequestID(*initialRequestId);
63 }
64
65 if (srcmask) {
66 message.setEDNSSubnet(*srcmask);
67 }
68
69 // cerr <<message.toDebugString()<<endl;
70 std::string str;
71 message.serialize(str);
72 outgoingLogger->queueData(str);
73 }
74
75 static 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);
81 message.setServerIdentity(SyncRes::s_serverID);
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
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 */
100 int 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)
101 {
102 size_t len;
103 size_t bufsize=g_outgoingEDNSBufsize;
104 std::string buf;
105 buf.resize(bufsize);
106 vector<uint8_t> vpacket;
107 // string mapped0x20=dns0x20(domain);
108 uint16_t qid = dns_random(0xffff);
109 DNSPacketWriter pw(vpacket, domain, type);
110
111 pw.getHeader()->rd=sendRDQuery;
112 pw.getHeader()->id=qid;
113 /* RFC 6840 section 5.9:
114 * This document further specifies that validating resolvers SHOULD set
115 * the CD bit on every upstream query. This is regardless of whether
116 * the CD bit was set on the incoming query [...]
117 *
118 * sendRDQuery is only true if the qname is part of a forward-zone-recurse (or
119 * set in the forward-zone-file), so we use this as an indicator for it being
120 * an "upstream query". To stay true to "dnssec=off means 3.X behaviour", we
121 * only set +CD on forwarded query in any mode other than dnssec=off.
122 */
123 pw.getHeader()->cd=(sendRDQuery && g_dnssecmode != DNSSECMode::Off);
124
125 string ping;
126 bool weWantEDNSSubnet=false;
127 uint8_t outgoingECSBits = 0;
128 ComboAddress outgoingECSAddr;
129 if(EDNS0Level > 0) {
130 DNSPacketWriter::optvect_t opts;
131 if(srcmask) {
132 EDNSSubnetOpts eo;
133 eo.source = *srcmask;
134 outgoingECSBits = srcmask->getBits();
135 outgoingECSAddr = srcmask->getNetwork();
136 // cout<<"Adding request mask: "<<eo.source.toString()<<endl;
137 opts.push_back(make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(eo)));
138 weWantEDNSSubnet=true;
139 }
140
141 pw.addOpt(g_outgoingEDNSBufsize, 0, g_dnssecmode == DNSSECMode::Off ? 0 : EDNSOpts::DNSSECOK, opts);
142 pw.commit();
143 }
144 lwr->d_rcode = 0;
145 lwr->d_haveEDNS = false;
146 int ret;
147
148 DTime dt;
149 dt.set();
150 *now=dt.getTimeval();
151
152 #ifdef HAVE_PROTOBUF
153 boost::uuids::uuid uuid;
154 const struct timeval queryTime = *now;
155
156 if (outgoingLogger) {
157 uuid = (*t_uuidGenerator)();
158 logOutgoingQuery(outgoingLogger, context ? context->d_initialRequestId : boost::none, uuid, ip, domain, type, qid, doTCP, vpacket.size(), srcmask);
159 }
160 #endif
161
162 srcmask = boost::none; // this is also our return value, even if EDNS0Level == 0
163
164 errno=0;
165 if(!doTCP) {
166 int queryfd;
167 if(ip.sin4.sin_family==AF_INET6)
168 g_stats.ipv6queries++;
169
170 if((ret=asendto((const char*)&*vpacket.begin(), vpacket.size(), 0, ip, qid,
171 domain, type, &queryfd)) < 0) {
172 return ret; // passes back the -2 EMFILE
173 }
174
175 if (queryfd == -1) {
176 *chained = true;
177 }
178
179 // sleep until we see an answer to this, interface to mtasker
180
181 ret=arecvfrom(buf, 0, ip, &len, qid,
182 domain, type, queryfd, now);
183 }
184 else {
185 try {
186 Socket s(ip.sin4.sin_family, SOCK_STREAM);
187
188 s.setNonBlocking();
189 ComboAddress local = getQueryLocalAddress(ip.sin4.sin_family, 0);
190
191 s.bind(local);
192
193 s.connect(ip);
194
195 uint16_t tlen=htons(vpacket.size());
196 char *lenP=(char*)&tlen;
197 const char *msgP=(const char*)&*vpacket.begin();
198 string packet=string(lenP, lenP+2)+string(msgP, msgP+vpacket.size());
199
200 ret=asendtcp(packet, &s);
201 if(!(ret>0))
202 return ret;
203
204 packet.clear();
205 ret=arecvtcp(packet, 2, &s, false);
206 if(!(ret > 0))
207 return ret;
208
209 memcpy(&tlen, packet.c_str(), sizeof(tlen));
210 len=ntohs(tlen); // switch to the 'len' shared with the rest of the function
211
212 ret=arecvtcp(packet, len, &s, false);
213 if(!(ret > 0))
214 return ret;
215
216 buf.resize(len);
217 memcpy(const_cast<char*>(buf.data()), packet.c_str(), len);
218
219 ret=1;
220 }
221 catch(NetworkError& ne) {
222 ret = -2; // OS limits error
223 }
224 }
225
226
227 lwr->d_usec=dt.udiff();
228 *now=dt.getTimeval();
229
230 if(ret <= 0) // includes 'timeout'
231 return ret;
232
233 buf.resize(len);
234 lwr->d_records.clear();
235 try {
236 lwr->d_tcbit=0;
237 MOADNSParser mdp(false, buf);
238 lwr->d_aabit=mdp.d_header.aa;
239 lwr->d_tcbit=mdp.d_header.tc;
240 lwr->d_rcode=mdp.d_header.rcode;
241
242 if(mdp.d_header.rcode == RCode::FormErr && mdp.d_qname.empty() && mdp.d_qtype == 0 && mdp.d_qclass == 0) {
243 #ifdef HAVE_PROTOBUF
244 if(outgoingLogger) {
245 logIncomingResponse(outgoingLogger, context ? context->d_initialRequestId : boost::none, uuid, ip, domain, type, qid, doTCP, len, lwr->d_rcode, lwr->d_records, queryTime);
246 }
247 #endif
248 lwr->d_validpacket=true;
249 return 1; // this is "success", the error is set in lwr->d_rcode
250 }
251
252 if(domain != mdp.d_qname) {
253 if(!mdp.d_qname.empty() && domain.toString().find((char)0) == string::npos /* ugly */) {// embedded nulls are too noisy, plus empty domains are too
254 g_log<<Logger::Notice<<"Packet purporting to come from remote server "<<ip.toString()<<" contained wrong answer: '" << domain << "' != '" << mdp.d_qname << "'" << endl;
255 }
256 // unexpected count has already been done @ pdns_recursor.cc
257 goto out;
258 }
259
260 lwr->d_records.reserve(mdp.d_answers.size());
261 for(const auto& a : mdp.d_answers)
262 lwr->d_records.push_back(a.first);
263
264 EDNSOpts edo;
265 if(EDNS0Level > 0 && getEDNSOpts(mdp, &edo)) {
266 lwr->d_haveEDNS = true;
267
268 if(weWantEDNSSubnet) {
269 for(const auto& opt : edo.d_options) {
270 if(opt.first==EDNSOptionCode::ECS) {
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;
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. */
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 }
283 }
284 }
285 }
286 }
287 }
288
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
294 lwr->d_validpacket=true;
295 return 1;
296 }
297 catch(std::exception &mde) {
298 if(::arg().mustDo("log-common-errors"))
299 g_log<<Logger::Notice<<"Unable to parse packet from remote server "<<ip.toString()<<": "<<mde.what()<<endl;
300 lwr->d_rcode = RCode::FormErr;
301 g_stats.serverParseError++;
302 #ifdef HAVE_PROTOBUF
303 if(outgoingLogger) {
304 logIncomingResponse(outgoingLogger, context ? context->d_initialRequestId : boost::none, uuid, ip, domain, type, qid, doTCP, len, lwr->d_rcode, lwr->d_records, queryTime);
305 }
306 #endif
307 lwr->d_validpacket=false;
308 return 1; // success - oddly enough
309 }
310 catch(...) {
311 g_log<<Logger::Notice<<"Unknown error parsing packet from remote server"<<endl;
312 }
313
314 g_stats.serverParseError++;
315
316 out:
317 if(!lwr->d_rcode)
318 lwr->d_rcode=RCode::ServFail;
319
320 return -1;
321 }
322