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