]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/stubresolver.cc
Merge pull request #6495 from Habbie/parse-resolvconf-once
[thirdparty/pdns.git] / pdns / stubresolver.cc
1 #include <sys/stat.h>
2
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"
18
19 #define LOCAL_RESOLV_CONF_PATH "/etc/resolv.conf"
20 // don't stat() for local resolv.conf more than once every INTERVAL secs.
21 #define LOCAL_RESOLV_CONF_MAX_CHECK_INTERVAL 60
22
23 // s_resolversForStub contains the ComboAddresses that are used by
24 // stubDoResolve
25 static vector<ComboAddress> s_resolversForStub;
26 static pthread_rwlock_t s_resolversForStubLock = PTHREAD_RWLOCK_INITIALIZER;
27 static bool s_stubResolvConfigured = false;
28
29 // /etc/resolv.conf last modification time
30 static time_t s_localResolvConfMtime = 0;
31 static time_t s_localResolvConfLastCheck = 0;
32
33 /*
34 * Returns false if no resolvers are configured, while emitting a warning about this
35 */
36 bool resolversDefined()
37 {
38 ReadLock l(&s_resolversForStubLock);
39 if (s_resolversForStub.empty()) {
40 g_log<<Logger::Warning<<"No upstream resolvers configured, stub resolving (including secpoll and ALIAS) impossible."<<endl;
41 return false;
42 }
43 return true;
44 }
45
46 /*
47 * Parse /etc/resolv.conf and add those nameservers to s_resolversForStub
48 */
49 static void parseLocalResolvConf_locked(const time_t& now)
50 {
51 struct stat st;
52 s_localResolvConfLastCheck = now;
53
54 if (stat(LOCAL_RESOLV_CONF_PATH, &st) != -1) {
55 if (st.st_mtime != s_localResolvConfMtime) {
56 ifstream ifs(LOCAL_RESOLV_CONF_PATH);
57 string line;
58
59 s_localResolvConfMtime = st.st_mtime;
60 if(!ifs)
61 return;
62
63 s_resolversForStub.clear();
64 while(std::getline(ifs, line)) {
65 boost::trim_right_if(line, is_any_of(" \r\n\x1a"));
66 boost::trim_left(line); // leading spaces, let's be nice
67
68 string::size_type tpos = line.find_first_of(";#");
69 if(tpos != string::npos)
70 line.resize(tpos);
71
72 if(boost::starts_with(line, "nameserver ") || boost::starts_with(line, "nameserver\t")) {
73 vector<string> parts;
74 stringtok(parts, line, " \t,"); // be REALLY nice
75 for(vector<string>::const_iterator iter = parts.begin()+1; iter != parts.end(); ++iter) {
76 try {
77 s_resolversForStub.push_back(ComboAddress(*iter, 53));
78 }
79 catch(...)
80 {
81 }
82 }
83 }
84 }
85 }
86 }
87 }
88
89 static void parseLocalResolvConf()
90 {
91 const time_t now = time(nullptr);
92 if ((s_localResolvConfLastCheck + LOCAL_RESOLV_CONF_MAX_CHECK_INTERVAL) > now)
93 return ;
94
95 WriteLock wl(&s_resolversForStubLock);
96 parseLocalResolvConf_locked(now);
97 }
98
99
100 /*
101 * Fill the s_resolversForStub vector with addresses for the upstream resolvers.
102 * First, parse the `resolver` configuration option for IP addresses to use.
103 * If that doesn't work, parse /etc/resolv.conf and add those nameservers to
104 * s_resolversForStub.
105 *
106 * mainthread() calls this so you don't have to.
107 */
108 void stubParseResolveConf()
109 {
110 if(::arg().mustDo("resolver")) {
111 WriteLock wl(&s_resolversForStubLock);
112 vector<string> parts;
113 stringtok(parts, ::arg()["resolver"], " ,\t");
114 for (const auto& addr : parts)
115 s_resolversForStub.push_back(ComboAddress(addr, 53));
116 }
117
118 if (s_resolversForStub.empty()) {
119 parseLocalResolvConf();
120 }
121 // Emit a warning if there are no stubs.
122 resolversDefined();
123 s_stubResolvConfigured = true;
124 }
125
126 // s_resolversForStub contains the ComboAddresses that are used to resolve the
127 int stubDoResolve(const DNSName& qname, uint16_t qtype, vector<DNSZoneRecord>& ret)
128 {
129 // ensure resolver gets always configured
130 if (!s_stubResolvConfigured) {
131 stubParseResolveConf();
132 }
133 // only check if resolvers come from local resolv.conf in the first place
134 if (s_localResolvConfMtime != 0) {
135 parseLocalResolvConf();
136 }
137 if (!resolversDefined())
138 return RCode::ServFail;
139
140 ReadLock l(&s_resolversForStubLock);
141 vector<uint8_t> packet;
142
143 DNSPacketWriter pw(packet, qname, qtype);
144 pw.getHeader()->id=dns_random(0xffff);
145 pw.getHeader()->rd=1;
146
147 string msg ="Doing stub resolving, using resolvers: ";
148 for (const auto& server : s_resolversForStub) {
149 msg += server.toString() + ", ";
150 }
151 g_log<<Logger::Debug<<msg.substr(0, msg.length() - 2)<<endl;
152
153 for(const ComboAddress& dest : s_resolversForStub) {
154 Socket sock(dest.sin4.sin_family, SOCK_DGRAM);
155 sock.setNonBlocking();
156 sock.connect(dest);
157 sock.send(string(packet.begin(), packet.end()));
158
159 string reply;
160
161 waitForData(sock.getHandle(), 2, 0);
162 try {
163 retry:
164 sock.read(reply); // this calls recv
165 if(reply.size() > sizeof(struct dnsheader)) {
166 struct dnsheader d;
167 memcpy(&d, reply.c_str(), sizeof(d));
168 if(d.id != pw.getHeader()->id)
169 goto retry;
170 }
171 }
172 catch(...) {
173 continue;
174 }
175 MOADNSParser mdp(false, reply);
176 if(mdp.d_header.rcode == RCode::ServFail)
177 continue;
178
179 for(MOADNSParser::answers_t::const_iterator i=mdp.d_answers.begin(); i!=mdp.d_answers.end(); ++i) {
180 if(i->first.d_place == 1 && i->first.d_type==qtype) {
181 DNSZoneRecord zrr;
182 zrr.dr = i->first;
183 zrr.auth=true;
184 ret.push_back(zrr);
185 }
186 }
187 g_log<<Logger::Debug<<"Question got answered by "<<dest.toString()<<endl;
188 return mdp.d_header.rcode;
189 }
190 return RCode::ServFail;
191 }