]>
Commit | Line | Data |
---|---|---|
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 | ||
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) | |
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 | ||
69 | 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) | |
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 | 93 | 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, 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 |