]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/stubresolver.cc
Merge pull request #11431 from jroessler-ox/docs-kskzskroll-update
[thirdparty/pdns.git] / pdns / stubresolver.cc
CommitLineData
9cc98a19
CHB
1#include <sys/stat.h>
2
24317c7f
PD
3#ifdef HAVE_CONFIG_H
4#include "config.h"
5#endif
6
7#include "logger.hh"
8#include "arguments.hh"
9#include "version.hh"
10#include "misc.hh"
11
12#include "sstuff.hh"
13#include "dnswriter.hh"
14#include "dns_random.hh"
15#include "namespaces.hh"
16#include "statbag.hh"
17#include "stubresolver.hh"
50e2abc0 18#include "ednsoptions.hh"
19#include "ednssubnet.hh"
24317c7f 20
9cc98a19 21#define LOCAL_RESOLV_CONF_PATH "/etc/resolv.conf"
c1e81673
CHB
22// don't stat() for local resolv.conf more than once every INTERVAL secs.
23#define LOCAL_RESOLV_CONF_MAX_CHECK_INTERVAL 60
9cc98a19 24
2b78726c 25// s_resolversForStub contains the ComboAddresses that are used by
150e318e 26// stubDoResolve
128f7248 27static SharedLockGuarded<vector<ComboAddress>> s_resolversForStub;
9a987ad1 28static bool s_stubResolvConfigured = false;
9cc98a19
CHB
29
30// /etc/resolv.conf last modification time
bcf07d98 31static time_t s_localResolvConfMtime = 0;
c1e81673 32static time_t s_localResolvConfLastCheck = 0;
24317c7f 33
c972f5fe
PL
34static string logPrefix = "[stub-resolver] ";
35
2b78726c
PL
36/*
37 * Returns false if no resolvers are configured, while emitting a warning about this
38 */
39bool resolversDefined()
40{
128f7248 41 if (s_resolversForStub.read_lock()->empty()) {
c0ebe1da 42 g_log << Logger::Warning << logPrefix << "No upstream resolvers configured, stub resolving (including secpoll and ALIAS) impossible." << endl;
2b78726c
PL
43 return false;
44 }
45 return true;
46}
47
9cc98a19
CHB
48/*
49 * Parse /etc/resolv.conf and add those nameservers to s_resolversForStub
50 */
128f7248 51static void parseLocalResolvConf_locked(vector<ComboAddress>& resolversForStub, const time_t& now)
9cc98a19 52{
b7349b96
PD
53 struct stat statResult
54 {
55 };
c1e81673
CHB
56 s_localResolvConfLastCheck = now;
57
b7349b96
PD
58 if (stat(LOCAL_RESOLV_CONF_PATH, &statResult) != -1) {
59 if (statResult.st_mtime != s_localResolvConfMtime) {
5d4e1ef8 60 std::vector<ComboAddress> resolvers = getResolvers(LOCAL_RESOLV_CONF_PATH);
c1e81673 61
b7349b96 62 s_localResolvConfMtime = statResult.st_mtime;
c1e81673 63
5d4e1ef8
RG
64 if (resolvers.empty()) {
65 return;
9cc98a19 66 }
5d4e1ef8 67
128f7248 68 resolversForStub = std::move(resolvers);
9cc98a19
CHB
69 }
70 }
9cc98a19
CHB
71}
72
9a987ad1
AT
73static void parseLocalResolvConf()
74{
75 const time_t now = time(nullptr);
b7349b96 76 if ((s_localResolvConfLastCheck + LOCAL_RESOLV_CONF_MAX_CHECK_INTERVAL) > now) {
c0ebe1da 77 return;
b7349b96 78 }
9a987ad1 79
128f7248 80 parseLocalResolvConf_locked(*(s_resolversForStub.write_lock()), now);
9a987ad1
AT
81}
82
2b78726c
PL
83/*
84 * Fill the s_resolversForStub vector with addresses for the upstream resolvers.
85 * First, parse the `resolver` configuration option for IP addresses to use.
86 * If that doesn't work, parse /etc/resolv.conf and add those nameservers to
87 * s_resolversForStub.
faf7f65b
PD
88 *
89 * mainthread() calls this so you don't have to.
24317c7f 90 */
150e318e 91void stubParseResolveConf()
24317c7f 92{
c0ebe1da 93 if (::arg().mustDo("resolver")) {
128f7248 94 auto resolversForStub = s_resolversForStub.write_lock();
2b78726c
PL
95 vector<string> parts;
96 stringtok(parts, ::arg()["resolver"], " ,\t");
b7349b96 97 for (const auto& addr : parts) {
128f7248 98 resolversForStub->push_back(ComboAddress(addr, 53));
b7349b96 99 }
2b78726c 100 }
24317c7f 101
128f7248 102 if (s_resolversForStub.read_lock()->empty()) {
9cc98a19 103 parseLocalResolvConf();
24317c7f 104 }
2b78726c
PL
105 // Emit a warning if there are no stubs.
106 resolversDefined();
9a987ad1 107 s_stubResolvConfigured = true;
24317c7f
PD
108}
109
2b78726c 110// s_resolversForStub contains the ComboAddresses that are used to resolve the
b9b7d770 111int stubDoResolve(const DNSName& qname, uint16_t qtype, vector<DNSZoneRecord>& ret, const EDNSSubnetOpts* d_eso)
24317c7f 112{
9a987ad1
AT
113 // ensure resolver gets always configured
114 if (!s_stubResolvConfigured) {
115 stubParseResolveConf();
116 }
3ad680b1 117 // only check if resolvers come from local resolv.conf in the first place
9cc98a19 118 if (s_localResolvConfMtime != 0) {
c0ebe1da 119 parseLocalResolvConf();
9cc98a19 120 }
b7349b96 121 if (!resolversDefined()) {
2b78726c 122 return RCode::ServFail;
b7349b96 123 }
2b78726c 124
128f7248 125 auto resolversForStub = s_resolversForStub.read_lock();
24317c7f
PD
126 vector<uint8_t> packet;
127
b7349b96
PD
128 DNSPacketWriter packetWriter(packet, qname, qtype);
129 packetWriter.getHeader()->id = dns_random_uint16();
130 packetWriter.getHeader()->rd = 1;
c0ebe1da
PD
131
132 if (d_eso != nullptr) {
50e2abc0 133 // pass along EDNS subnet from client if given - issue #5469
134 string origECSOptionStr = makeEDNSSubnetOptsString(*d_eso);
135 DNSPacketWriter::optvect_t opts;
136 opts.emplace_back(EDNSOptionCode::ECS, origECSOptionStr);
b7349b96
PD
137 packetWriter.addOpt(512, 0, 0, opts);
138 packetWriter.commit();
50e2abc0 139 }
24317c7f 140
d5fcd583 141 string queryNameType = qname.toString() + "|" + QType(qtype).toString();
c0ebe1da 142 string msg = "Doing stub resolving for '" + queryNameType + "', using resolvers: ";
128f7248 143 for (const auto& server : *resolversForStub) {
24317c7f
PD
144 msg += server.toString() + ", ";
145 }
c0ebe1da 146 g_log << Logger::Debug << logPrefix << msg.substr(0, msg.length() - 2) << endl;
24317c7f 147
c0ebe1da 148 for (const ComboAddress& dest : *resolversForStub) {
24317c7f
PD
149 Socket sock(dest.sin4.sin_family, SOCK_DGRAM);
150 sock.setNonBlocking();
90ba52e0 151 sock.connect(dest);
152 sock.send(string(packet.begin(), packet.end()));
24317c7f
PD
153
154 string reply;
155
31778765
AT
156 // error handled after this
157 (void)waitForData(sock.getHandle(), 2, 0);
24317c7f
PD
158 try {
159 retry:
90ba52e0 160 sock.read(reply); // this calls recv
c0ebe1da 161 if (reply.size() > sizeof(struct dnsheader)) {
b7349b96
PD
162 struct dnsheader dHeader
163 {
164 };
165 memcpy(&dHeader, reply.c_str(), sizeof(dHeader));
166 if (dHeader.id != packetWriter.getHeader()->id) {
24317c7f 167 goto retry;
b7349b96 168 }
24317c7f
PD
169 }
170 }
c0ebe1da 171 catch (...) {
24317c7f
PD
172 continue;
173 }
27c0050c 174 MOADNSParser mdp(false, reply);
b7349b96 175 if (mdp.d_header.rcode == RCode::ServFail) {
24317c7f 176 continue;
b7349b96 177 }
24317c7f 178
c0ebe1da
PD
179 for (const auto& answer : mdp.d_answers) {
180 if (answer.first.d_place == 1 && answer.first.d_type == qtype) {
90ba52e0 181 DNSZoneRecord zrr;
403a3a42 182 zrr.dr = answer.first;
c0ebe1da 183 zrr.auth = true;
90ba52e0 184 ret.push_back(zrr);
24317c7f
PD
185 }
186 }
c0ebe1da 187 g_log << Logger::Debug << logPrefix << "Question for '" << queryNameType << "' got answered by " << dest.toString() << endl;
24317c7f
PD
188 return mdp.d_header.rcode;
189 }
190 return RCode::ServFail;
90ba52e0 191}
2d40d42b 192
c0ebe1da
PD
193int stubDoResolve(const DNSName& qname, uint16_t qtype, vector<DNSRecord>& ret, const EDNSSubnetOpts* d_eso)
194{
2d40d42b 195 vector<DNSZoneRecord> ret2;
50e2abc0 196 int res = stubDoResolve(qname, qtype, ret2, d_eso);
b7349b96
PD
197 for (const auto& record : ret2) {
198 ret.push_back(record.dr);
2d40d42b
PL
199 }
200 return res;
201}