]>
Commit | Line | Data |
---|---|---|
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 | 27 | static SharedLockGuarded<vector<ComboAddress>> s_resolversForStub; |
9a987ad1 | 28 | static bool s_stubResolvConfigured = false; |
9cc98a19 CHB |
29 | |
30 | // /etc/resolv.conf last modification time | |
bcf07d98 | 31 | static time_t s_localResolvConfMtime = 0; |
c1e81673 | 32 | static time_t s_localResolvConfLastCheck = 0; |
24317c7f | 33 | |
c972f5fe PL |
34 | static string logPrefix = "[stub-resolver] "; |
35 | ||
2b78726c PL |
36 | /* |
37 | * Returns false if no resolvers are configured, while emitting a warning about this | |
38 | */ | |
39 | bool 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 | 51 | static 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 |
73 | static 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 | 91 | void 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 | 111 | int 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 |
193 | int 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 | } |