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