]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/stubresolver.cc
Merge pull request #9070 from rgacogne/boost-173
[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 ReadWriteLock s_resolversForStubLock;
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 std::vector<ComboAddress> resolvers = getResolvers(LOCAL_RESOLV_CONF_PATH);
57
58 s_localResolvConfMtime = st.st_mtime;
59
60 if (resolvers.empty()) {
61 return;
62 }
63
64 s_resolversForStub = std::move(resolvers);
65 }
66 }
67 }
68
69 static void parseLocalResolvConf()
70 {
71 const time_t now = time(nullptr);
72 if ((s_localResolvConfLastCheck + LOCAL_RESOLV_CONF_MAX_CHECK_INTERVAL) > now)
73 return ;
74
75 WriteLock wl(&s_resolversForStubLock);
76 parseLocalResolvConf_locked(now);
77 }
78
79
80 /*
81 * Fill the s_resolversForStub vector with addresses for the upstream resolvers.
82 * First, parse the `resolver` configuration option for IP addresses to use.
83 * If that doesn't work, parse /etc/resolv.conf and add those nameservers to
84 * s_resolversForStub.
85 *
86 * mainthread() calls this so you don't have to.
87 */
88 void stubParseResolveConf()
89 {
90 if(::arg().mustDo("resolver")) {
91 WriteLock wl(&s_resolversForStubLock);
92 vector<string> parts;
93 stringtok(parts, ::arg()["resolver"], " ,\t");
94 for (const auto& addr : parts)
95 s_resolversForStub.push_back(ComboAddress(addr, 53));
96 }
97
98 if (s_resolversForStub.empty()) {
99 parseLocalResolvConf();
100 }
101 // Emit a warning if there are no stubs.
102 resolversDefined();
103 s_stubResolvConfigured = true;
104 }
105
106 // s_resolversForStub contains the ComboAddresses that are used to resolve the
107 int stubDoResolve(const DNSName& qname, uint16_t qtype, vector<DNSZoneRecord>& ret)
108 {
109 // ensure resolver gets always configured
110 if (!s_stubResolvConfigured) {
111 stubParseResolveConf();
112 }
113 // only check if resolvers come from local resolv.conf in the first place
114 if (s_localResolvConfMtime != 0) {
115 parseLocalResolvConf();
116 }
117 if (!resolversDefined())
118 return RCode::ServFail;
119
120 ReadLock l(&s_resolversForStubLock);
121 vector<uint8_t> packet;
122
123 DNSPacketWriter pw(packet, qname, qtype);
124 pw.getHeader()->id=dns_random_uint16();
125 pw.getHeader()->rd=1;
126
127 string msg ="Doing stub resolving, using resolvers: ";
128 for (const auto& server : s_resolversForStub) {
129 msg += server.toString() + ", ";
130 }
131 g_log<<Logger::Debug<<msg.substr(0, msg.length() - 2)<<endl;
132
133 for(const ComboAddress& dest : s_resolversForStub) {
134 Socket sock(dest.sin4.sin_family, SOCK_DGRAM);
135 sock.setNonBlocking();
136 sock.connect(dest);
137 sock.send(string(packet.begin(), packet.end()));
138
139 string reply;
140
141 waitForData(sock.getHandle(), 2, 0);
142 try {
143 retry:
144 sock.read(reply); // this calls recv
145 if(reply.size() > sizeof(struct dnsheader)) {
146 struct dnsheader d;
147 memcpy(&d, reply.c_str(), sizeof(d));
148 if(d.id != pw.getHeader()->id)
149 goto retry;
150 }
151 }
152 catch(...) {
153 continue;
154 }
155 MOADNSParser mdp(false, reply);
156 if(mdp.d_header.rcode == RCode::ServFail)
157 continue;
158
159 for(MOADNSParser::answers_t::const_iterator i=mdp.d_answers.begin(); i!=mdp.d_answers.end(); ++i) {
160 if(i->first.d_place == 1 && i->first.d_type==qtype) {
161 DNSZoneRecord zrr;
162 zrr.dr = i->first;
163 zrr.auth=true;
164 ret.push_back(zrr);
165 }
166 }
167 g_log<<Logger::Debug<<"Question got answered by "<<dest.toString()<<endl;
168 return mdp.d_header.rcode;
169 }
170 return RCode::ServFail;
171 }
172
173 int stubDoResolve(const DNSName& qname, uint16_t qtype, vector<DNSRecord>& ret) {
174 vector<DNSZoneRecord> ret2;
175 int res = stubDoResolve(qname, qtype, ret2);
176 for (const auto &r : ret2) {
177 ret.push_back(r.dr);
178 }
179 return res;
180 }