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

PowerDNS / pdns / 18776176249

24 Oct 2025 09:51AM UTC coverage: 73.028% (+15.6%) from 57.383%
18776176249

Pull #16370

github

web-flow
Merge 0f309894e into 39f7f1b27
Pull Request #16370: auth: xfr churning

38273 of 63104 branches covered (60.65%)

Branch coverage included in aggregate %.

186 of 264 new or added lines in 1 file covered. (70.45%)

14471 existing lines in 217 files now uncovered.

127487 of 163878 relevant lines covered (77.79%)

5632363.83 hits per line

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

91.94
/pdns/dnsdistdist/dnsdist-lua-network.cc
1
/*
2
 * This file is part of PowerDNS or dnsdist.
3
 * Copyright -- PowerDNS.COM B.V. and its contributors
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of version 2 of the GNU General Public License as
7
 * published by the Free Software Foundation.
8
 *
9
 * In addition, for the avoidance of any doubt, permission is granted to
10
 * link this program with OpenSSL and to (re)distribute the binaries
11
 * produced as the result of such linking.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21
 */
22
#include <sys/socket.h>
23
#include <sys/un.h>
24

25
#include "dnsdist-lua-network.hh"
26
#include "dolog.hh"
27
#include "threadname.hh"
28

29
namespace dnsdist
30
{
31
NetworkListener::ListenerData::ListenerData() :
32
  d_mplexer(std::unique_ptr<FDMultiplexer>(FDMultiplexer::getMultiplexerSilent(10)))
21✔
33
{
21✔
34
}
21✔
35

36
NetworkListener::NetworkListener() :
37
  d_data(std::make_shared<ListenerData>())
21✔
38
{
21✔
39
}
21✔
40

41
NetworkListener::~NetworkListener()
42
{
20✔
43
  d_data->d_exiting = true;
20✔
44

45
  /* wake up the listening thread */
46
  for (const auto& socket : d_data->d_sockets) {
20✔
47
    shutdown(socket.second.getHandle(), SHUT_RD);
16✔
48
  }
16✔
49
}
20✔
50

51
void NetworkListener::readCB(int desc, FDMultiplexer::funcparam_t& param)
52
{
330✔
53
  auto cbData = boost::any_cast<std::shared_ptr<NetworkListener::CBData>>(param);
330✔
54
  std::string packet;
330✔
55

56
#ifdef MSG_TRUNC
330✔
57
  /* first we peek to avoid allocating a very large buffer. "MSG_TRUNC [...] return the real length of the datagram, even when it was longer than the passed buffer" */
58
  auto peeked = recvfrom(desc, nullptr, 0, MSG_PEEK | MSG_TRUNC, nullptr, nullptr);
330✔
59
  if (peeked > 0) {
330✔
60
    packet.resize(static_cast<size_t>(peeked));
326✔
61
  }
326✔
62
#endif
330✔
63
  if (packet.empty()) {
330✔
64
    packet.resize(65535);
4✔
65
  }
4✔
66

67
  sockaddr_un from{};
330✔
68
  memset(&from, 0, sizeof(from));
330✔
69

70
  socklen_t fromLen = sizeof(from);
330✔
71
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
72
  auto got = recvfrom(desc, &packet.at(0), packet.size(), 0, reinterpret_cast<sockaddr*>(&from), &fromLen);
330✔
73
  if (got > 0) {
330✔
74
    packet.resize(static_cast<size_t>(got));
326✔
75
    std::string fromAddr;
326✔
76
    if (fromLen <= sizeof(from)) {
326!
77
      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
78
      fromAddr = std::string(from.sun_path, strlen(from.sun_path));
326✔
79
    }
326✔
80
    try {
326✔
81
      cbData->d_cb(cbData->d_endpoint, std::move(packet), fromAddr);
326✔
82
    }
326✔
83
    catch (const std::exception& e) {
326✔
84
      vinfolog("Exception in the read callback of a NetworkListener: %s", e.what());
2!
85
    }
2✔
86
    catch (...) {
326✔
87
      vinfolog("Exception in the read callback of a NetworkListener");
2!
88
    }
2✔
89
  }
326✔
90
}
330✔
91

92
bool NetworkListener::addUnixListeningEndpoint(const std::string& path, NetworkListener::EndpointID endpointID, NetworkListener::NetworkDatagramCB callback)
93
{
23✔
94
  if (d_data->d_running) {
23✔
95
    throw std::runtime_error("NetworkListener should not be altered at runtime");
2✔
96
  }
2✔
97

98
  sockaddr_un sun{};
21✔
99
  if (makeUNsockaddr(path, &sun) != 0) {
21✔
100
    throw std::runtime_error("Invalid Unix socket path '" + path + "'");
2✔
101
  }
2✔
102

103
  bool abstractPath = path.at(0) == '\0';
19✔
104
  if (!abstractPath) {
19✔
105
    int err = unlink(path.c_str());
13✔
106
    if (err != 0) {
13✔
107
      err = errno;
2✔
108
      if (err != ENOENT) {
2!
109
        vinfolog("Error removing Unix socket to path '%s': %s", path, stringerror(err));
×
110
      }
×
111
    }
2✔
112
  }
13✔
113

114
  Socket sock(sun.sun_family, SOCK_DGRAM, 0);
19✔
115
  socklen_t sunLength = sizeof(sun);
19✔
116
  if (abstractPath) {
19✔
117
    /* abstract paths can contain null bytes so we need to set the actual size */
118
    sunLength = sizeof(sa_family_t) + path.size();
6✔
119
  }
6✔
120

121
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
122
  if (bind(sock.getHandle(), reinterpret_cast<const struct sockaddr*>(&sun), sunLength) != 0) {
19✔
123
    std::string sanitizedPath(path);
2✔
124
    if (abstractPath) {
2!
125
      sanitizedPath[0] = '@';
2✔
126
    }
2✔
127
    throw std::runtime_error("Error binding Unix socket to path '" + sanitizedPath + "': " + stringerror());
2✔
128
  }
2✔
129

130
  sock.setNonBlocking();
17✔
131

132
  auto cbData = std::make_shared<CBData>();
17✔
133
  cbData->d_endpoint = endpointID;
17✔
134
  cbData->d_cb = std::move(callback);
17✔
135
  d_data->d_mplexer->addReadFD(sock.getHandle(), readCB, cbData);
17✔
136

137
  d_data->d_sockets.insert({path, std::move(sock)});
17✔
138
  return true;
17✔
139
}
19✔
140

141
void NetworkListener::runOnce(ListenerData& data, timeval& now, uint32_t timeout)
142
{
332✔
143
  if (data.d_exiting) {
332!
144
    return;
×
145
  }
×
146

147
  dnsdist::configuration::refreshLocalRuntimeConfiguration();
332✔
148
  data.d_running = true;
332✔
149
  if (data.d_sockets.empty()) {
332✔
150
    throw runtime_error("NetworkListener started with no sockets");
2✔
151
  }
2✔
152

153
  data.d_mplexer->run(&now, static_cast<int>(timeout));
330✔
154
}
330✔
155

156
void NetworkListener::runOnce(timeval& now, uint32_t timeout)
157
{
12✔
158
  runOnce(*d_data, now, timeout);
12✔
159
}
12✔
160

161
void NetworkListener::mainThread(std::shared_ptr<ListenerData>& dataArg)
162
{
4✔
163
  /* take our own copy of the shared_ptr so it's still alive if the NetworkListener object
164
     gets destroyed while we are still running */
165
  // NOLINTNEXTLINE(performance-unnecessary-copy-initialization): we really need a copy here, or we end up with use-after-free as explained above
166
  auto data = dataArg;
4✔
167
  setThreadName("dnsdist/lua-net");
4✔
168
  timeval now{};
4✔
169

170
  while (!data->d_exiting) {
324✔
171
    runOnce(*data, now, -1);
320✔
172
  }
320✔
173
}
4✔
174

175
void NetworkListener::start()
176
{
4✔
177
  std::thread main = std::thread([this] {
4✔
178
    mainThread(d_data);
4✔
179
  });
4✔
180
  main.detach();
4✔
181
}
4✔
182

183
NetworkEndpoint::NetworkEndpoint(const std::string& path) :
184
  d_socket(AF_UNIX, SOCK_DGRAM, 0)
22✔
185
{
22✔
186
  sockaddr_un sun{};
22✔
187
  if (makeUNsockaddr(path, &sun) != 0) {
22✔
188
    throw std::runtime_error("Invalid Unix socket path '" + path + "'");
2✔
189
  }
2✔
190

191
  socklen_t sunLength = sizeof(sun);
20✔
192
  bool abstractPath = path.at(0) == '\0';
20✔
193

194
  if (abstractPath) {
20✔
195
    /* abstract paths can contain null bytes so we need to set the actual size */
196
    sunLength = sizeof(sa_family_t) + path.size();
4✔
197
  }
4✔
198
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
199
  if (connect(d_socket.getHandle(), reinterpret_cast<const struct sockaddr*>(&sun), sunLength) != 0) {
20✔
200
    std::string sanitizedPath(path);
5✔
201
    if (abstractPath) {
5✔
202
      sanitizedPath[0] = '@';
2✔
203
    }
2✔
204
    throw std::runtime_error("Error connecting Unix socket to path '" + sanitizedPath + "': " + stringerror());
5✔
205
  }
5✔
206

207
  d_socket.setNonBlocking();
15✔
208
}
15✔
209

210
bool NetworkEndpoint::send(const std::string_view& payload) const
211
{
379✔
212
  auto sent = ::send(d_socket.getHandle(), payload.data(), payload.size(), 0);
379✔
213
  if (sent <= 0) {
379!
214
    return false;
×
UNCOV
215
  }
×
216

217
  return static_cast<size_t>(sent) == payload.size();
379✔
218
}
379✔
219
}
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