• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PowerDNS / pdns / 16615247828

30 Jul 2025 06:33AM UTC coverage: 65.845% (-0.03%) from 65.87%
16615247828

Pull #15942

github

web-flow
Merge 3e4243857 into 4a7b6a621
Pull Request #15942: Optimize reload-zones logic to reduce thread scheduling times

42051 of 92438 branches covered (45.49%)

Branch coverage included in aggregate %.

4 of 4 new or added lines in 1 file covered. (100.0%)

48 existing lines in 12 files now uncovered.

127942 of 165732 relevant lines covered (77.2%)

5529729.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.02
/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
#include "ednsoptions.hh"
19
#include "ednssubnet.hh"
20

21
#define LOCAL_RESOLV_CONF_PATH "/etc/resolv.conf"
178✔
22
// don't stat() for local resolv.conf more than once every INTERVAL secs.
23
#define LOCAL_RESOLV_CONF_MAX_CHECK_INTERVAL 60
178✔
24

25
// s_resolversForStub contains the ComboAddresses that are used by
26
// stubDoResolve
27
static SharedLockGuarded<vector<ComboAddress>> s_resolversForStub;
28
static bool s_stubResolvConfigured = false;
29

30
// /etc/resolv.conf last modification time
31
static time_t s_localResolvConfMtime = 0;
32
static time_t s_localResolvConfLastCheck = 0;
33

34
static string logPrefix = "[stub-resolver] ";
35

36
/*
37
 * Returns false if no resolvers are configured, while emitting a warning about this
38
 */
39
bool resolversDefined()
40
{
520✔
41
  if (s_resolversForStub.read_lock()->empty()) {
520!
42
    g_log << Logger::Warning << logPrefix << "No upstream resolvers configured, stub resolving (including secpoll and ALIAS) impossible." << endl;
×
43
    return false;
×
44
  }
×
45
  return true;
520✔
46
}
520✔
47

48
/*
49
 * Parse /etc/resolv.conf and add those nameservers to s_resolversForStub
50
 */
51
static void parseLocalResolvConf_locked(vector<ComboAddress>& resolversForStub, const time_t& now)
52
{
89✔
53
  struct stat statResult{};
89✔
54
  s_localResolvConfLastCheck = now;
89✔
55

56
  if (stat(LOCAL_RESOLV_CONF_PATH, &statResult) != -1) {
89!
57
    if (statResult.st_mtime != s_localResolvConfMtime) {
89!
58
      std::vector<ComboAddress> resolvers = getResolvers(LOCAL_RESOLV_CONF_PATH);
89✔
59

60
      s_localResolvConfMtime = statResult.st_mtime;
89✔
61

62
      if (resolvers.empty()) {
89!
63
        return;
×
64
      }
×
65

66
      resolversForStub = std::move(resolvers);
89✔
67
    }
89✔
68
  }
89✔
69
}
89✔
70

71
static void parseLocalResolvConf()
72
{
178✔
73
  const time_t now = time(nullptr);
178✔
74
  if ((s_localResolvConfLastCheck + LOCAL_RESOLV_CONF_MAX_CHECK_INTERVAL) > now) {
178✔
75
    return;
89✔
76
  }
89✔
77

78
  parseLocalResolvConf_locked(*(s_resolversForStub.write_lock()), now);
89✔
79
}
89✔
80

81
/*
82
 * Fill the s_resolversForStub vector with addresses for the upstream resolvers.
83
 * First, parse the `resolver` configuration option for IP addresses to use.
84
 * If that doesn't work, parse /etc/resolv.conf and add those nameservers to
85
 * s_resolversForStub.
86
 *
87
 * mainthread() calls this so you don't have to.
88
 */
89
void stubParseResolveConf()
90
{
172✔
91
  if (::arg().mustDo("resolver")) {
172✔
92
    auto resolversForStub = s_resolversForStub.write_lock();
83✔
93
    vector<string> parts;
83✔
94
    stringtok(parts, ::arg()["resolver"], " ,\t");
83✔
95
    for (const auto& addr : parts) {
83✔
96
      resolversForStub->push_back(ComboAddress(addr, 53));
83✔
97
    }
83✔
98
  }
83✔
99

100
  if (s_resolversForStub.read_lock()->empty()) {
172✔
101
    parseLocalResolvConf();
89✔
102
  }
89✔
103
  // Emit a warning if there are no stubs.
104
  resolversDefined();
172✔
105
  s_stubResolvConfigured = true;
172✔
106
}
172✔
107

108
// s_resolversForStub contains the ComboAddresses that are used to resolve the
109
int stubDoResolve(const DNSName& qname, uint16_t qtype, vector<DNSZoneRecord>& ret, const EDNSSubnetOpts* d_eso)
110
{
348✔
111
  // ensure resolver gets always configured
112
  if (!s_stubResolvConfigured) {
348!
113
    stubParseResolveConf();
×
114
  }
×
115
  // only check if resolvers come from local resolv.conf in the first place
116
  if (s_localResolvConfMtime != 0) {
348✔
117
    parseLocalResolvConf();
89✔
118
  }
89✔
119
  if (!resolversDefined()) {
348!
120
    return RCode::ServFail;
×
121
  }
×
122

123
  auto resolversForStub = s_resolversForStub.read_lock();
348✔
124
  vector<uint8_t> packet;
348✔
125

126
  DNSPacketWriter packetWriter(packet, qname, qtype);
348✔
127
  packetWriter.getHeader()->id = dns_random_uint16();
348✔
128
  packetWriter.getHeader()->rd = 1;
348✔
129

130
  if (d_eso != nullptr) {
348!
131
    // pass along EDNS subnet from client if given - issue #5469
132
    string origECSOptionStr = d_eso->makeOptString();
×
133
    DNSPacketWriter::optvect_t opts;
×
134
    opts.emplace_back(EDNSOptionCode::ECS, origECSOptionStr);
×
135
    packetWriter.addOpt(512, 0, 0, opts);
×
136
    packetWriter.commit();
×
137
  }
×
138

139
  string queryNameType = qname.toString() + "|" + QType(qtype).toString();
348✔
140
  string msg = "Doing stub resolving for '" + queryNameType + "', using resolvers: ";
348✔
141
  for (const auto& server : *resolversForStub) {
348✔
142
    msg += server.toString() + ", ";
348✔
143
  }
348✔
144
  g_log << Logger::Debug << logPrefix << msg.substr(0, msg.length() - 2) << endl;
348✔
145

146
  for (const ComboAddress& dest : *resolversForStub) {
348!
147
    Socket sock(dest.sin4.sin_family, SOCK_DGRAM);
348✔
148
    sock.setNonBlocking();
348✔
149
    sock.connect(dest);
348✔
150
    sock.send(string(packet.begin(), packet.end()));
348✔
151

152
    string reply;
348✔
153

154
    // error handled after this
155
    (void)waitForData(sock.getHandle(), 2, 0);
348✔
156
    try {
348✔
157
    retry:
348✔
158
      sock.read(reply); // this calls recv
348✔
159
      if (reply.size() > sizeof(struct dnsheader)) {
348!
160
        struct dnsheader dHeader{};
348✔
161
        memcpy(&dHeader, reply.c_str(), sizeof(dHeader));
348✔
162
        if (dHeader.id != packetWriter.getHeader()->id) {
348!
163
          goto retry;
×
164
        }
×
165
      }
348✔
166
    }
348✔
167
    catch (...) {
348✔
UNCOV
168
      continue;
×
UNCOV
169
    }
×
170
    MOADNSParser mdp(false, reply);
348✔
171
    if (mdp.d_header.rcode == RCode::ServFail) {
348!
172
      continue;
×
173
    }
×
174

175
    for (const auto& answer : mdp.d_answers) {
348✔
176
      if (answer.d_place == 1 && answer.d_type == qtype) {
348!
177
        DNSZoneRecord zrr;
176✔
178
        zrr.dr = answer;
176✔
179
        zrr.auth = true;
176✔
180
        ret.push_back(zrr);
176✔
181
      }
176✔
182
    }
348✔
183
    g_log << Logger::Debug << logPrefix << "Question for '" << queryNameType << "' got answered by " << dest.toString() << endl;
348✔
184
    return mdp.d_header.rcode;
348✔
185
  }
348✔
UNCOV
186
  return RCode::ServFail;
×
187
}
348✔
188

189
int stubDoResolve(const DNSName& qname, uint16_t qtype, vector<DNSRecord>& ret, const EDNSSubnetOpts* d_eso)
190
{
172✔
191
  vector<DNSZoneRecord> ret2;
172✔
192
  int res = stubDoResolve(qname, qtype, ret2, d_eso);
172✔
193
  for (const auto& record : ret2) {
172!
194
    ret.push_back(record.dr);
×
195
  }
×
196
  return res;
172✔
197
}
172✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc