]>
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" |
20829585 | 50 | #include "query-local-address.hh" |
2a17f6c6 | 51 | #include "tcpiohandler.hh" |
5abc5e10 | 52 | #include "ednsoptions.hh" |
1050a5c8 | 53 | #include "ednspadding.hh" |
00b3e94a | 54 | #include "rec-protozero.hh" |
d61aa945 | 55 | #include "uuid-utils.hh" |
631cb36b O |
56 | #include "rec-tcpout.hh" |
57 | ||
58 | thread_local TCPOutConnectionManager t_tcp_manager; | |
91092a9f | 59 | std::shared_ptr<Logr::Logger> g_slogout; |
fcf47f49 | 60 | bool g_paddingOutgoing; |
d61aa945 | 61 | |
4d7db3d7 OM |
62 | void remoteLoggerQueueData(RemoteLoggerInterface& r, const std::string& data) |
63 | { | |
64 | auto ret = r.queueData(data); | |
65 | ||
66 | switch (ret) { | |
67 | case RemoteLoggerInterface::Result::Queued: | |
68 | break; | |
69 | case RemoteLoggerInterface::Result::PipeFull: { | |
74b9e43d | 70 | const auto msg = RemoteLoggerInterface::toErrorString(ret); |
4d7db3d7 | 71 | const auto name = r.name(); |
4d7db3d7 OM |
72 | SLOG(g_log << Logger::Debug << name << ": " << msg <<std::endl, |
73 | g_slog->withName(name)->info(Logr::Debug, msg)); | |
74 | break; | |
75 | } | |
76 | case RemoteLoggerInterface::Result::TooLarge: { | |
74b9e43d | 77 | const auto msg = RemoteLoggerInterface::toErrorString(ret); |
4d7db3d7 | 78 | const auto name = r.name(); |
4d7db3d7 OM |
79 | SLOG(g_log << Logger::Notice << name << ": " << msg <<endl, |
80 | g_slog->withName(name)->info(Logr::Debug, msg)); | |
81 | break; | |
82 | } | |
83 | case RemoteLoggerInterface::Result::OtherError: { | |
74b9e43d | 84 | const auto msg = RemoteLoggerInterface::toErrorString(ret); |
4d7db3d7 | 85 | const auto name = r.name(); |
4d7db3d7 OM |
86 | SLOG(g_log << Logger::Warning << name << ": " << msg << std::endl, |
87 | g_slog->withName(name)->info(Logr::Warning, msg)); | |
88 | break; | |
89 | } | |
90 | } | |
91 | } | |
92 | ||
66927b32 OM |
93 | #ifdef HAVE_FSTRM |
94 | #include "dnstap.hh" | |
95 | #include "fstrm_logger.hh" | |
96 | ||
97 | bool g_syslog; | |
98 | ||
10ba6d01 | 99 | static bool isEnabledForQueries(const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstreamLoggers) |
573f4ff0 OM |
100 | { |
101 | if (fstreamLoggers == nullptr) { | |
102 | return false; | |
103 | } | |
104 | for (auto& logger : *fstreamLoggers) { | |
10ba6d01 | 105 | if (logger->logQueries()) { |
573f4ff0 OM |
106 | return true; |
107 | } | |
108 | } | |
109 | return false; | |
110 | } | |
111 | ||
46314dcf | 112 | static void logFstreamQuery(const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstreamLoggers, const struct timeval &queryTime, const ComboAddress& localip, const ComboAddress& ip, DnstapMessage::ProtocolType protocol, boost::optional<const DNSName&> auth, const vector<uint8_t>& packet) |
4898a348 | 113 | { |
b9fa43e0 | 114 | if (fstreamLoggers == nullptr) |
4898a348 RG |
115 | return; |
116 | ||
b9fa43e0 OM |
117 | struct timespec ts; |
118 | TIMEVAL_TO_TIMESPEC(&queryTime, &ts); | |
b9fa43e0 | 119 | std::string str; |
46314dcf | 120 | DnstapMessage message(str, DnstapMessage::MessageType::resolver_query, SyncRes::s_serverID, &localip, &ip, protocol, reinterpret_cast<const char*>(&*packet.begin()), packet.size(), &ts, nullptr, auth); |
c165308b | 121 | |
b9fa43e0 | 122 | for (auto& logger : *fstreamLoggers) { |
4d7db3d7 | 123 | remoteLoggerQueueData(*logger, str); |
b9fa43e0 OM |
124 | } |
125 | } | |
4898a348 | 126 | |
10ba6d01 | 127 | static bool isEnabledForResponses(const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstreamLoggers) |
573f4ff0 OM |
128 | { |
129 | if (fstreamLoggers == nullptr) { | |
130 | return false; | |
131 | } | |
132 | for (auto& logger : *fstreamLoggers) { | |
10ba6d01 | 133 | if (logger->logResponses()) { |
573f4ff0 OM |
134 | return true; |
135 | } | |
136 | } | |
137 | return false; | |
138 | } | |
139 | ||
46314dcf | 140 | static void logFstreamResponse(const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstreamLoggers, const ComboAddress&localip, const ComboAddress& ip, DnstapMessage::ProtocolType protocol, boost::optional<const DNSName&> auth, const PacketBuffer& packet, const struct timeval& queryTime, const struct timeval& replyTime) |
b9fa43e0 OM |
141 | { |
142 | if (fstreamLoggers == nullptr) | |
143 | return; | |
0ff13512 | 144 | |
b9fa43e0 OM |
145 | struct timespec ts1, ts2; |
146 | TIMEVAL_TO_TIMESPEC(&queryTime, &ts1); | |
147 | TIMEVAL_TO_TIMESPEC(&replyTime, &ts2); | |
b9fa43e0 | 148 | std::string str; |
46314dcf | 149 | DnstapMessage message(str, DnstapMessage::MessageType::resolver_response, SyncRes::s_serverID, &localip, &ip, protocol, reinterpret_cast<const char*>(packet.data()), packet.size(), &ts1, &ts2, auth); |
b773359c | 150 | |
b9fa43e0 | 151 | for (auto& logger : *fstreamLoggers) { |
4d7db3d7 | 152 | remoteLoggerQueueData(*logger, str); |
b773359c | 153 | } |
4898a348 RG |
154 | } |
155 | ||
b9fa43e0 | 156 | #endif // HAVE_FSTRM |
ebd67986 | 157 | |
cffd3a17 | 158 | 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, bool tls, size_t bytes, boost::optional<Netmask>& srcmask) |
4898a348 | 159 | { |
5d6c7a46 | 160 | if (!outgoingLoggers) { |
4898a348 | 161 | return; |
5d6c7a46 RG |
162 | } |
163 | ||
164 | bool log = false; | |
165 | for (auto& logger : *outgoingLoggers) { | |
166 | if (logger->logQueries()) { | |
167 | log = true; | |
168 | break; | |
169 | } | |
170 | } | |
171 | ||
172 | if (!log) { | |
173 | return; | |
174 | } | |
4898a348 | 175 | |
00b3e94a RG |
176 | static thread_local std::string buffer; |
177 | buffer.clear(); | |
178 | pdns::ProtoZero::Message m{buffer}; | |
89addb82 | 179 | m.setType(pdns::ProtoZero::Message::MessageType::DNSOutgoingQueryType); |
00b3e94a RG |
180 | m.setMessageIdentity(uuid); |
181 | m.setSocketFamily(ip.sin4.sin_family); | |
cffd3a17 RG |
182 | if (!doTCP) { |
183 | m.setSocketProtocol(pdns::ProtoZero::Message::TransportProtocol::UDP); | |
184 | } | |
185 | else if (!tls) { | |
186 | m.setSocketProtocol(pdns::ProtoZero::Message::TransportProtocol::TCP); | |
187 | } | |
188 | else { | |
189 | m.setSocketProtocol(pdns::ProtoZero::Message::TransportProtocol::DoT); | |
190 | } | |
191 | ||
00b3e94a RG |
192 | m.setTo(ip); |
193 | m.setInBytes(bytes); | |
194 | m.setTime(); | |
195 | m.setId(qid); | |
196 | m.setQuestion(domain, type, QClass::IN); | |
197 | m.setToPort(ip.getPort()); | |
198 | m.setServerIdentity(SyncRes::s_serverID); | |
c165308b | 199 | |
4898a348 | 200 | if (initialRequestId) { |
00b3e94a | 201 | m.setInitialRequestID(*initialRequestId); |
4898a348 RG |
202 | } |
203 | ||
0ff13512 | 204 | if (srcmask) { |
00b3e94a | 205 | m.setEDNSSubnet(*srcmask, 128); |
0ff13512 RG |
206 | } |
207 | ||
b773359c | 208 | for (auto& logger : *outgoingLoggers) { |
5d6c7a46 | 209 | if (logger->logQueries()) { |
4d7db3d7 | 210 | remoteLoggerQueueData(*logger, buffer); |
5d6c7a46 | 211 | } |
b773359c | 212 | } |
4898a348 RG |
213 | } |
214 | ||
cffd3a17 | 215 | 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, bool tls, boost::optional<Netmask>& srcmask, size_t bytes, int rcode, const std::vector<DNSRecord>& records, const struct timeval& queryTime, const std::set<uint16_t>& exportTypes) |
4898a348 | 216 | { |
5d6c7a46 RG |
217 | if (!outgoingLoggers) { |
218 | return; | |
219 | } | |
220 | ||
221 | bool log = false; | |
222 | for (auto& logger : *outgoingLoggers) { | |
223 | if (logger->logResponses()) { | |
224 | log = true; | |
225 | break; | |
226 | } | |
227 | } | |
228 | ||
229 | if (!log) { | |
4898a348 | 230 | return; |
5d6c7a46 RG |
231 | } |
232 | ||
00b3e94a RG |
233 | static thread_local std::string buffer; |
234 | buffer.clear(); | |
235 | pdns::ProtoZero::RecMessage m{buffer}; | |
89addb82 | 236 | m.setType(pdns::ProtoZero::Message::MessageType::DNSIncomingResponseType); |
00b3e94a RG |
237 | m.setMessageIdentity(uuid); |
238 | m.setSocketFamily(ip.sin4.sin_family); | |
cffd3a17 RG |
239 | if (!doTCP) { |
240 | m.setSocketProtocol(pdns::ProtoZero::Message::TransportProtocol::UDP); | |
241 | } | |
242 | else if (!tls) { | |
243 | m.setSocketProtocol(pdns::ProtoZero::Message::TransportProtocol::TCP); | |
244 | } | |
245 | else { | |
246 | m.setSocketProtocol(pdns::ProtoZero::Message::TransportProtocol::DoT); | |
247 | } | |
00b3e94a RG |
248 | m.setTo(ip); |
249 | m.setInBytes(bytes); | |
250 | m.setTime(); | |
251 | m.setId(qid); | |
252 | m.setQuestion(domain, type, QClass::IN); | |
253 | m.setToPort(ip.getPort()); | |
254 | m.setServerIdentity(SyncRes::s_serverID); | |
5d6c7a46 | 255 | |
00b3e94a RG |
256 | if (initialRequestId) { |
257 | m.setInitialRequestID(*initialRequestId); | |
5d6c7a46 | 258 | } |
00b3e94a RG |
259 | |
260 | if (srcmask) { | |
261 | m.setEDNSSubnet(*srcmask, 128); | |
5d6c7a46 | 262 | } |
4898a348 | 263 | |
00b3e94a RG |
264 | m.startResponse(); |
265 | m.setQueryTime(queryTime.tv_sec, queryTime.tv_usec); | |
57f8413e | 266 | if (rcode == -1) { |
00b3e94a | 267 | m.setNetworkErrorResponseCode(); |
57f8413e RG |
268 | } |
269 | else { | |
00b3e94a | 270 | m.setResponseCode(rcode); |
57f8413e | 271 | } |
4898a348 | 272 | |
00b3e94a RG |
273 | for (const auto& record : records) { |
274 | m.addRR(record, exportTypes, false); | |
275 | } | |
276 | m.commitResponse(); | |
b773359c RG |
277 | |
278 | for (auto& logger : *outgoingLoggers) { | |
5d6c7a46 | 279 | if (logger->logResponses()) { |
4d7db3d7 | 280 | remoteLoggerQueueData(*logger, buffer); |
5d6c7a46 | 281 | } |
b773359c | 282 | } |
4898a348 | 283 | } |
4898a348 | 284 | |
086c80cd | 285 | static bool tcpconnect(const struct timeval& now, const ComboAddress& ip, TCPOutConnectionManager::Connection& connection, bool& dnsOverTLS, const std::string& nsName) |
caaea007 O |
286 | { |
287 | dnsOverTLS = SyncRes::s_dot_to_port_853 && ip.getPort() == 853; | |
288 | ||
050b64ac O |
289 | connection = t_tcp_manager.get(ip); |
290 | if (connection.d_handler) { | |
291 | return false; | |
292 | } | |
f3a69323 | 293 | |
050b64ac O |
294 | const struct timeval timeout{ g_networkTimeoutMsec / 1000, static_cast<suseconds_t>(g_networkTimeoutMsec) % 1000 * 1000}; |
295 | Socket s(ip.sin4.sin_family, SOCK_STREAM); | |
296 | s.setNonBlocking(); | |
34b7ae04 | 297 | setTCPNoDelay(s.getHandle()); |
050b64ac O |
298 | ComboAddress localip = pdns::getQueryLocalAddress(ip.sin4.sin_family, 0); |
299 | s.bind(localip); | |
300 | ||
301 | std::shared_ptr<TLSCtx> tlsCtx{nullptr}; | |
302 | if (dnsOverTLS) { | |
303 | TLSContextParameters tlsParams; | |
304 | tlsParams.d_provider = "openssl"; | |
305 | tlsParams.d_validateCertificates = false; | |
306 | // tlsParams.d_caStore | |
307 | tlsCtx = getTLSContext(tlsParams); | |
308 | if (tlsCtx == nullptr) { | |
91092a9f OM |
309 | SLOG(g_log << Logger::Error << "DoT to " << ip << " requested but not available" << endl, |
310 | g_slogout->info(Logr::Error, "DoT requested but not available", "server", Logging::Loggable(ip))); | |
050b64ac | 311 | dnsOverTLS = false; |
caaea007 | 312 | } |
caaea007 | 313 | } |
74b08b2a | 314 | connection.d_handler = std::make_shared<TCPIOHandler>(nsName, false, s.releaseHandle(), timeout, tlsCtx, now.tv_sec); |
050b64ac O |
315 | // Returned state ignored |
316 | // This can throw an exception, retry will need to happen at higher level | |
317 | connection.d_handler->tryConnect(SyncRes::s_tcp_fast_open_connect, ip); | |
318 | return true; | |
caaea007 O |
319 | } |
320 | ||
f3a69323 O |
321 | static LWResult::Result tcpsendrecv(const ComboAddress& ip, TCPOutConnectionManager::Connection& connection, |
322 | ComboAddress& localip, const vector<uint8_t>& vpacket, size_t& len, PacketBuffer& buf) | |
323 | { | |
324 | socklen_t slen = ip.getSocklen(); | |
325 | uint16_t tlen = htons(vpacket.size()); | |
326 | const char *lenP = reinterpret_cast<const char*>(&tlen); | |
f3a69323 | 327 | |
da886835 | 328 | len = 0; // in case of error |
f3a69323 | 329 | localip.sin4.sin_family = ip.sin4.sin_family; |
0344192c OM |
330 | if (getsockname(connection.d_handler->getDescriptor(), reinterpret_cast<sockaddr*>(&localip), &slen) != 0) { |
331 | return LWResult::Result::PermanentError; | |
332 | } | |
f3a69323 O |
333 | |
334 | PacketBuffer packet; | |
335 | packet.reserve(2 + vpacket.size()); | |
336 | packet.insert(packet.end(), lenP, lenP + 2); | |
050b64ac | 337 | packet.insert(packet.end(), vpacket.begin(), vpacket.end()); |
f3a69323 O |
338 | |
339 | LWResult::Result ret = asendtcp(packet, connection.d_handler); | |
340 | if (ret != LWResult::Result::Success) { | |
341 | return ret; | |
342 | } | |
343 | ||
344 | ret = arecvtcp(packet, 2, connection.d_handler, false); | |
345 | if (ret != LWResult::Result::Success) { | |
346 | return ret; | |
347 | } | |
348 | ||
349 | memcpy(&tlen, packet.data(), sizeof(tlen)); | |
350 | len = ntohs(tlen); // switch to the 'len' shared with the rest of the calling function | |
351 | ||
352 | // XXX receive into buf directly? | |
353 | packet.resize(len); | |
354 | ret = arecvtcp(packet, len, connection.d_handler, false); | |
355 | if (ret != LWResult::Result::Success) { | |
356 | return ret; | |
357 | } | |
358 | buf.resize(len); | |
359 | memcpy(buf.data(), packet.data(), len); | |
360 | return LWResult::Result::Success; | |
361 | } | |
362 | ||
1050a5c8 OM |
363 | static void addPadding(const DNSPacketWriter& pw, size_t bufsize, DNSPacketWriter::optvect_t& opts) |
364 | { | |
365 | const size_t currentSize = pw.getSizeWithOpts(opts); | |
366 | if (currentSize < (bufsize - 4)) { | |
367 | const size_t remaining = bufsize - (currentSize + 4); | |
368 | /* from rfc8647, "4.1. Recommended Strategy: Block-Length Padding": | |
369 | Clients SHOULD pad queries to the closest multiple of 128 octets. | |
370 | Note we are in the client role here. | |
371 | */ | |
372 | const size_t blockSize = 128; | |
373 | const size_t modulo = (currentSize + 4) % blockSize; | |
374 | size_t padSize = 0; | |
375 | if (modulo > 0) { | |
376 | padSize = std::min(blockSize - modulo, remaining); | |
377 | } | |
378 | opts.emplace_back(EDNSOptionCode::PADDING, makeEDNSPaddingOptString(padSize)); | |
379 | } | |
380 | } | |
381 | ||
81883dcc BH |
382 | /** lwr is only filled out in case 1 was returned, and even when returning 1 for 'success', lwr might contain DNS errors |
383 | Never throws! | |
384 | */ | |
80252248 | 385 | static LWResult::Result 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::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstrmLoggers, const std::set<uint16_t>& exportTypes, LWResult *lwr, bool* chained, TCPOutConnectionManager::Connection& connection) |
e14f094b | 386 | { |
a683e8bd RG |
387 | size_t len; |
388 | size_t bufsize=g_outgoingEDNSBufsize; | |
2a17f6c6 | 389 | PacketBuffer buf; |
78f56b38 | 390 | buf.resize(bufsize); |
ea634573 | 391 | vector<uint8_t> vpacket; |
51e2144e | 392 | // string mapped0x20=dns0x20(domain); |
a410b176 | 393 | uint16_t qid = dns_random_uint16(); |
ea634573 | 394 | DNSPacketWriter pw(vpacket, domain, type); |
1050a5c8 | 395 | bool dnsOverTLS = SyncRes::s_dot_to_port_853 && ip.getPort() == 853; |
ea634573 | 396 | |
c1d73d94 | 397 | pw.getHeader()->rd=sendRDQuery; |
4898a348 | 398 | pw.getHeader()->id=qid; |
5a7d2a18 PL |
399 | /* RFC 6840 section 5.9: |
400 | * This document further specifies that validating resolvers SHOULD set | |
401 | * the CD bit on every upstream query. This is regardless of whether | |
402 | * the CD bit was set on the incoming query [...] | |
403 | * | |
404 | * sendRDQuery is only true if the qname is part of a forward-zone-recurse (or | |
405 | * set in the forward-zone-file), so we use this as an indicator for it being | |
406 | * an "upstream query". To stay true to "dnssec=off means 3.X behaviour", we | |
407 | * only set +CD on forwarded query in any mode other than dnssec=off. | |
408 | */ | |
e7b18884 | 409 | pw.getHeader()->cd=(sendRDQuery && g_dnssecmode != DNSSECMode::Off); |
5a7d2a18 | 410 | |
81883dcc | 411 | string ping; |
7bb598a0 | 412 | bool weWantEDNSSubnet=false; |
30d4402d RG |
413 | uint8_t outgoingECSBits = 0; |
414 | ComboAddress outgoingECSAddr; | |
fe61f5d8 | 415 | if(EDNS0Level > 0) { |
81883dcc | 416 | DNSPacketWriter::optvect_t opts; |
376effcf | 417 | if(srcmask) { |
418 | EDNSSubnetOpts eo; | |
419 | eo.source = *srcmask; | |
30d4402d RG |
420 | outgoingECSBits = srcmask->getBits(); |
421 | outgoingECSAddr = srcmask->getNetwork(); | |
bf4ab707 | 422 | // cout<<"Adding request mask: "<<eo.source.toString()<<endl; |
e32a8d46 | 423 | opts.emplace_back(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(eo)); |
7bb598a0 | 424 | weWantEDNSSubnet=true; |
376effcf | 425 | } |
93d4a890 | 426 | |
fcf47f49 | 427 | if (dnsOverTLS && g_paddingOutgoing) { |
1050a5c8 OM |
428 | addPadding(pw, bufsize, opts); |
429 | } | |
430 | ||
431 | pw.addOpt(g_outgoingEDNSBufsize, 0, g_dnssecmode == DNSSECMode::Off ? 0 : EDNSOpts::DNSSECOK, opts); | |
2188dcc3 BH |
432 | pw.commit(); |
433 | } | |
81883dcc | 434 | lwr->d_rcode = 0; |
81883dcc | 435 | lwr->d_haveEDNS = false; |
308f4c43 | 436 | LWResult::Result ret; |
eefd15f9 BH |
437 | |
438 | DTime dt; | |
4c4765c1 | 439 | dt.set(); |
440 | *now=dt.getTimeval(); | |
4898a348 | 441 | |
4898a348 RG |
442 | boost::uuids::uuid uuid; |
443 | const struct timeval queryTime = *now; | |
444 | ||
b773359c | 445 | if (outgoingLoggers) { |
d61aa945 | 446 | uuid = getUniqueID(); |
cffd3a17 | 447 | logOutgoingQuery(outgoingLoggers, context ? context->d_initialRequestId : boost::none, uuid, ip, domain, type, qid, doTCP, dnsOverTLS, vpacket.size(), srcmask); |
4898a348 | 448 | } |
00b3e94a | 449 | |
82c0899c O |
450 | srcmask = boost::none; // this is also our return value, even if EDNS0Level == 0 |
451 | ||
452 | // We only store the localip if needed for fstrm logging | |
453 | ComboAddress localip; | |
bc82867a | 454 | #ifdef HAVE_FSTRM |
82c0899c O |
455 | bool fstrmQEnabled = false; |
456 | bool fstrmREnabled = false; | |
5a00d3a3 | 457 | |
573f4ff0 | 458 | if (isEnabledForQueries(fstrmLoggers)) { |
82c0899c | 459 | fstrmQEnabled = true; |
b9fa43e0 | 460 | } |
82c0899c O |
461 | if (isEnabledForResponses(fstrmLoggers)) { |
462 | fstrmREnabled = true; | |
463 | } | |
464 | #endif | |
0ff13512 | 465 | |
5c633640 | 466 | if(!doTCP) { |
4ef015cd | 467 | int queryfd; |
308f4c43 | 468 | if (ip.sin4.sin_family==AF_INET6) { |
7d3d2f4f | 469 | t_Counters.at(rec::Counter::ipv6queries)++; |
308f4c43 RG |
470 | } |
471 | ||
82c0899c | 472 | ret = asendto((const char*)&*vpacket.begin(), vpacket.size(), 0, ip, qid, domain, type, &queryfd); |
996c89cc | 473 | |
308f4c43 RG |
474 | if (ret != LWResult::Result::Success) { |
475 | return ret; | |
5c633640 | 476 | } |
deca7d8f RG |
477 | |
478 | if (queryfd == -1) { | |
479 | *chained = true; | |
480 | } | |
481 | ||
82c0899c O |
482 | #ifdef HAVE_FSTRM |
483 | if (!*chained) { | |
484 | if (fstrmQEnabled || fstrmREnabled) { | |
485 | localip.sin4.sin_family = ip.sin4.sin_family; | |
486 | socklen_t slen = ip.getSocklen(); | |
487 | getsockname(queryfd, reinterpret_cast<sockaddr*>(&localip), &slen); | |
488 | } | |
489 | if (fstrmQEnabled) { | |
46314dcf | 490 | logFstreamQuery(fstrmLoggers, queryTime, localip, ip, DnstapMessage::ProtocolType::DoUDP, context ? context->d_auth : boost::none, vpacket); |
82c0899c O |
491 | } |
492 | } | |
493 | #endif /* HAVE_FSTRM */ | |
494 | ||
5c633640 | 495 | // sleep until we see an answer to this, interface to mtasker |
82c0899c | 496 | ret = arecvfrom(buf, 0, ip, &len, qid, domain, type, queryfd, now); |
e14f094b | 497 | } |
5c633640 | 498 | else { |
5db4dcaa OM |
499 | bool isNew; |
500 | do { | |
501 | try { | |
502 | // If we get a new (not re-used) TCP connection that does not | |
503 | // work, we give up. For reused connections, we assume the | |
504 | // peer has closed it on error, so we retry. At some point we | |
505 | // *will* get a new connection, so this loop is not endless. | |
8ae4f2bc | 506 | isNew = true; // tcpconnect() might throw for new connections. In that case, we want to break the loop, scanbuild complains here, which is a false positive afaik |
086c80cd OM |
507 | std::string nsName; |
508 | if (context && !context->d_nsName.empty()) { | |
509 | nsName = context->d_nsName.toStringNoDot(); | |
510 | } | |
511 | isNew = tcpconnect(*now, ip, connection, dnsOverTLS, nsName); | |
5db4dcaa | 512 | ret = tcpsendrecv(ip, connection, localip, vpacket, len, buf); |
82c0899c | 513 | #ifdef HAVE_FSTRM |
5db4dcaa OM |
514 | if (fstrmQEnabled) { |
515 | logFstreamQuery(fstrmLoggers, queryTime, localip, ip, !dnsOverTLS ? DnstapMessage::ProtocolType::DoTCP : DnstapMessage::ProtocolType::DoT, context ? context->d_auth : boost::none, vpacket); | |
8daba80b | 516 | } |
5db4dcaa OM |
517 | #endif /* HAVE_FSTRM */ |
518 | if (ret == LWResult::Result::Success) { | |
519 | break; | |
f3a69323 | 520 | } |
5db4dcaa OM |
521 | connection.d_handler->close(); |
522 | } | |
523 | catch (const NetworkError&) { | |
524 | ret = LWResult::Result::OSLimitError; // OS limits error | |
525 | } | |
526 | catch (const runtime_error&) { | |
527 | ret = LWResult::Result::OSLimitError; // OS limits error (PermanentError is transport related) | |
528 | } | |
529 | } while (!isNew); | |
5c633640 | 530 | } |
998a4334 | 531 | |
26de3092 BH |
532 | lwr->d_usec=dt.udiff(); |
533 | *now=dt.getTimeval(); | |
534 | ||
308f4c43 | 535 | if (ret != LWResult::Result::Success) { // includes 'timeout' |
57f8413e | 536 | if (outgoingLoggers) { |
cffd3a17 | 537 | logIncomingResponse(outgoingLoggers, context ? context->d_initialRequestId : boost::none, uuid, ip, domain, type, qid, doTCP, dnsOverTLS, srcmask, 0, -1, {}, queryTime, exportTypes); |
57f8413e | 538 | } |
263f6a5a | 539 | return ret; |
57f8413e | 540 | } |
e14f094b | 541 | |
78f56b38 | 542 | buf.resize(len); |
b9fa43e0 OM |
543 | |
544 | #ifdef HAVE_FSTRM | |
82c0899c | 545 | if (fstrmREnabled && (!*chained || doTCP)) { |
46314dcf RG |
546 | DnstapMessage::ProtocolType protocol = doTCP ? DnstapMessage::ProtocolType::DoTCP : DnstapMessage::ProtocolType::DoUDP; |
547 | if (dnsOverTLS) { | |
548 | protocol = DnstapMessage::ProtocolType::DoT; | |
549 | } | |
550 | logFstreamResponse(fstrmLoggers, localip, ip, protocol, context ? context->d_auth : boost::none, buf, queryTime, *now); | |
b9fa43e0 OM |
551 | } |
552 | #endif /* HAVE_FSTRM */ | |
553 | ||
e325f20c | 554 | lwr->d_records.clear(); |
c836dc19 | 555 | try { |
f1f85f12 | 556 | lwr->d_tcbit=0; |
2a17f6c6 | 557 | MOADNSParser mdp(false, reinterpret_cast<const char*>(buf.data()), buf.size()); |
263f6a5a BH |
558 | lwr->d_aabit=mdp.d_header.aa; |
559 | lwr->d_tcbit=mdp.d_header.tc; | |
560 | lwr->d_rcode=mdp.d_header.rcode; | |
561 | ||
81883dcc | 562 | if(mdp.d_header.rcode == RCode::FormErr && mdp.d_qname.empty() && mdp.d_qtype == 0 && mdp.d_qclass == 0) { |
b773359c | 563 | if(outgoingLoggers) { |
cffd3a17 | 564 | logIncomingResponse(outgoingLoggers, context ? context->d_initialRequestId : boost::none, uuid, ip, domain, type, qid, doTCP, dnsOverTLS, srcmask, len, lwr->d_rcode, lwr->d_records, queryTime, exportTypes); |
4898a348 | 565 | } |
308f4c43 RG |
566 | lwr->d_validpacket = true; |
567 | return LWResult::Result::Success; // this is "success", the error is set in lwr->d_rcode | |
81883dcc BH |
568 | } |
569 | ||
e325f20c | 570 | if(domain != mdp.d_qname) { |
c5c066bf | 571 | if(!mdp.d_qname.empty() && domain.toString().find((char)0) == string::npos /* ugly */) {// embedded nulls are too noisy, plus empty domains are too |
91092a9f OM |
572 | SLOG(g_log<<Logger::Notice<<"Packet purporting to come from remote server "<<ip.toString()<<" contained wrong answer: '" << domain << "' != '" << mdp.d_qname << "'" << endl, |
573 | g_slogout->info(Logr::Notice, "Packet purporting to come from remote server contained wrong answer", | |
574 | "server", Logging::Loggable(ip), | |
575 | "qname", Logging::Loggable(domain), | |
576 | "onwire", Logging::Loggable(mdp.d_qname))); | |
01608dca | 577 | } |
284aa5c2 | 578 | // unexpected count has already been done @ pdns_recursor.cc |
2353fffa BH |
579 | goto out; |
580 | } | |
f128d20d RG |
581 | |
582 | lwr->d_records.reserve(mdp.d_answers.size()); | |
e325f20c | 583 | for(const auto& a : mdp.d_answers) |
584 | lwr->d_records.push_back(a.first); | |
81883dcc BH |
585 | |
586 | EDNSOpts edo; | |
57769f13 | 587 | if(EDNS0Level > 0 && getEDNSOpts(mdp, &edo)) { |
81883dcc | 588 | lwr->d_haveEDNS = true; |
376effcf | 589 | |
7bb598a0 | 590 | if(weWantEDNSSubnet) { |
591 | for(const auto& opt : edo.d_options) { | |
30d4402d | 592 | if(opt.first==EDNSOptionCode::ECS) { |
7bb598a0 | 593 | EDNSSubnetOpts reso; |
594 | if(getEDNSSubnetOptsFromString(opt.second, &reso)) { | |
595 | // cerr<<"EDNS Subnet response: "<<reso.source.toString()<<", scope: "<<reso.scope.toString()<<", family = "<<reso.scope.getNetwork().sin4.sin_family<<endl; | |
fe61f5d8 RG |
596 | /* rfc7871 states that 0 "indicate[s] that the answer is suitable for all addresses in FAMILY", |
597 | so we might want to still pass the information along to be able to differentiate between | |
598 | IPv4 and IPv6. Still I'm pretty sure it doesn't matter in real life, so let's not duplicate | |
599 | entries in our cache. */ | |
30d4402d RG |
600 | if(reso.scope.getBits()) { |
601 | uint8_t bits = std::min(reso.scope.getBits(), outgoingECSBits); | |
602 | outgoingECSAddr.truncate(bits); | |
603 | srcmask = Netmask(outgoingECSAddr, bits); | |
604 | } | |
7bb598a0 | 605 | } |
606 | } | |
607 | } | |
376effcf | 608 | } |
81883dcc BH |
609 | } |
610 | ||
b773359c | 611 | if(outgoingLoggers) { |
cffd3a17 | 612 | logIncomingResponse(outgoingLoggers, context ? context->d_initialRequestId : boost::none, uuid, ip, domain, type, qid, doTCP, dnsOverTLS, srcmask, len, lwr->d_rcode, lwr->d_records, queryTime, exportTypes); |
4898a348 | 613 | } |
8daba80b | 614 | |
308f4c43 RG |
615 | lwr->d_validpacket = true; |
616 | return LWResult::Result::Success; | |
c836dc19 | 617 | } |
308f4c43 RG |
618 | catch (const std::exception &mde) { |
619 | if (::arg().mustDo("log-common-errors")) { | |
91092a9f OM |
620 | SLOG(g_log<<Logger::Notice<<"Unable to parse packet from remote server "<<ip.toString()<<": "<<mde.what()<<endl, |
621 | g_slogout->error(Logr::Notice, mde.what(), "Unable to parse packet from remote server", "server", Logging::Loggable(ip), | |
622 | "exception", Logging::Loggable("std::exception"))); | |
308f4c43 RG |
623 | } |
624 | ||
81883dcc | 625 | lwr->d_rcode = RCode::FormErr; |
308f4c43 | 626 | lwr->d_validpacket = false; |
7d3d2f4f | 627 | t_Counters.at(rec::Counter::serverParseError)++; |
308f4c43 | 628 | |
b773359c | 629 | if(outgoingLoggers) { |
cffd3a17 | 630 | logIncomingResponse(outgoingLoggers, context ? context->d_initialRequestId : boost::none, uuid, ip, domain, type, qid, doTCP, dnsOverTLS, srcmask, len, lwr->d_rcode, lwr->d_records, queryTime, exportTypes); |
4898a348 | 631 | } |
308f4c43 RG |
632 | |
633 | return LWResult::Result::Success; // success - oddly enough | |
aab4adb0 | 634 | } |
308f4c43 | 635 | catch (...) { |
91092a9f OM |
636 | SLOG(g_log<<Logger::Notice<<"Unknown error parsing packet from remote server"<<endl, |
637 | g_slogout->info(Logr::Notice, "Unknown error parsing packet from remote server", "server", Logging::Loggable(ip))); | |
c836dc19 | 638 | } |
91092a9f | 639 | |
7d3d2f4f | 640 | t_Counters.at(rec::Counter::serverParseError)++; |
91092a9f | 641 | |
2353fffa | 642 | out: |
308f4c43 | 643 | if (!lwr->d_rcode) { |
81883dcc | 644 | lwr->d_rcode=RCode::ServFail; |
308f4c43 | 645 | } |
263f6a5a | 646 | |
308f4c43 | 647 | return LWResult::Result::PermanentError; |
e14f094b BH |
648 | } |
649 | ||
8daba80b O |
650 | LWResult::Result 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::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstrmLoggers, const std::set<uint16_t>& exportTypes, LWResult *lwr, bool* chained) |
651 | { | |
652 | TCPOutConnectionManager::Connection connection; | |
f3a69323 | 653 | auto ret = asyncresolve(ip, domain, type, doTCP, sendRDQuery, EDNS0Level, now, srcmask, context, outgoingLoggers, fstrmLoggers, exportTypes, lwr, chained, connection); |
8daba80b O |
654 | |
655 | if (doTCP) { | |
8daba80b | 656 | if (connection.d_handler && lwr->d_validpacket) { |
2a863502 | 657 | t_tcp_manager.store(*now, ip, std::move(connection)); |
8daba80b O |
658 | } |
659 | } | |
660 | return ret; | |
661 | } | |
662 |