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

PowerDNS / pdns / 18743945403

23 Oct 2025 09:29AM UTC coverage: 65.845% (+0.02%) from 65.829%
18743945403

Pull #16356

github

web-flow
Merge 8a2027ef1 into efa3637e8
Pull Request #16356: auth 5.0: backport "pdnsutil: fix b2b-migrate to from sql to non-sql"

42073 of 92452 branches covered (45.51%)

Branch coverage included in aggregate %.

128008 of 165855 relevant lines covered (77.18%)

6379935.17 hits per line

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

61.5
/pdns/recursordist/rec_channel.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

23
#include "config.h"
24

25
#include "rec_channel.hh"
26

27
#include <sys/socket.h>
28
#include <cerrno>
29
#include <cstdlib>
30
#include <unistd.h>
31
#include <sys/types.h>
32
#include <sys/stat.h>
33

34
#include "misc.hh"
35
#include "namespaces.hh"
36
#include "pdnsexception.hh"
37

38
/* g++ defines __SANITIZE_THREAD__
39
   clang++ supports the nice __has_feature(thread_sanitizer),
40
   let's merge them */
41
#if defined(__has_feature)
42
#if __has_feature(thread_sanitizer)
43
#define __SANITIZE_THREAD__ 1
44
#endif
45
#if __has_feature(address_sanitizer)
46
#define __SANITIZE_ADDRESS__ 1
47
#endif
48
#endif
49

50
std::atomic<bool> RecursorControlChannel::stop = false;
51

52
RecursorControlChannel::RecursorControlChannel() :
53
  d_fd(-1)
54
{
830✔
55
  memset(&d_local, 0, sizeof(d_local));
830✔
56
}
830✔
57

58
RecursorControlChannel::~RecursorControlChannel()
59
{
653✔
60
  if (d_fd > 0) {
653!
61
    close(d_fd);
653✔
62
  }
653✔
63
  if (d_local.sun_path[0] != '\0') {
653!
64
    unlink(d_local.sun_path); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
×
65
  }
×
66
}
653✔
67

68
int RecursorControlChannel::listen(const string& filename)
69
{
175✔
70
  d_fd = socket(AF_UNIX, SOCK_STREAM, 0);
175✔
71

72
  if (d_fd < 0) {
175!
73
    throw PDNSException("Creating UNIX domain socket: " + stringerror());
×
74
  }
×
75
  setCloseOnExec(d_fd);
175✔
76

77
  int tmp = 1;
175✔
78
  if (setsockopt(d_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof tmp) < 0) {
175!
79
    throw PDNSException("Setsockopt failed: " + stringerror());
×
80
  }
×
81

82
  int err = unlink(filename.c_str());
175✔
83
  if (err < 0 && errno != ENOENT) {
175!
84
    throw PDNSException("Can't remove (previous) controlsocket '" + filename + "': " + stringerror() + " (try --socket-dir)");
×
85
  }
×
86

87
  if (makeUNsockaddr(filename, &d_local) != 0) {
175!
88
    throw PDNSException("Unable to bind to controlsocket, path '" + filename + "' is not a valid UNIX socket path.");
×
89
  }
×
90

91
  if (bind(d_fd, reinterpret_cast<sockaddr*>(&d_local), sizeof(d_local)) < 0) { // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
175!
92
    throw PDNSException("Unable to bind to controlsocket '" + filename + "': " + stringerror());
×
93
  }
×
94
  if (::listen(d_fd, 0) == -1) {
175!
95
    throw PDNSException("Unable to listen on controlsocket '" + filename + "': " + stringerror());
×
96
  }
×
97
  return d_fd;
175✔
98
}
175✔
99

100
void RecursorControlChannel::connect(const string& path, const string& filename)
101
{
653✔
102
  struct sockaddr_un remote{};
653✔
103

104
  d_fd = socket(AF_UNIX, SOCK_STREAM, 0);
653✔
105
  setCloseOnExec(d_fd);
653✔
106

107
  if (d_fd < 0) {
653!
108
    throw PDNSException("Creating UNIX domain socket: " + stringerror());
×
109
  }
×
110
  try {
653✔
111
    int tmp = 1;
653✔
112
    if (setsockopt(d_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof tmp) < 0) {
653!
113
      throw PDNSException("Setsockopt failed: " + stringerror());
×
114
    }
×
115

116
    string remotename = path + "/" + filename;
653✔
117
    if (makeUNsockaddr(remotename, &remote) != 0) {
653!
118
      throw PDNSException("Unable to connect to controlsocket, path '" + remotename + "' is not a valid UNIX socket path.");
×
119
    }
×
120

121
    if (::connect(d_fd, reinterpret_cast<const sockaddr*>(&remote), sizeof(remote)) < 0) { // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
653!
122
      if (d_local.sun_path[0] != '\0') {
×
123
        unlink(d_local.sun_path); // NOLINT
×
124
      }
×
125
      throw PDNSException("Unable to connect to remote '" + remotename + "': " + stringerror());
×
126
    }
×
127
  }
653✔
128
  catch (...) {
653✔
129
    close(d_fd);
×
130
    d_fd = -1;
×
131
    d_local.sun_path[0] = 0;
×
132
    throw;
×
133
  }
×
134
}
653✔
135

136
static void sendfd(int socket, int fd_to_pass)
137
{
5✔
138
  struct msghdr msg{};
5✔
139
  struct cmsghdr* cmsg{};
5✔
140
  union
5✔
141
  {
5✔
142
    struct cmsghdr hdr;
5✔
143
    std::array<unsigned char, CMSG_SPACE(sizeof(int))> buf;
5✔
144
  } cmsgbuf{};
5✔
145
  std::array<iovec, 1> io_vector{};
5✔
146
  char character = 'X';
5✔
147

148
  io_vector[0].iov_base = &character;
5✔
149
  io_vector[0].iov_len = 1;
5✔
150

151
  memset(&msg, 0, sizeof(msg));
5✔
152
  msg.msg_control = cmsgbuf.buf.data();
5✔
153
  msg.msg_controllen = cmsgbuf.buf.size();
5✔
154
  msg.msg_iov = io_vector.data();
5✔
155
  msg.msg_iovlen = io_vector.size();
5✔
156

157
  cmsg = CMSG_FIRSTHDR(&msg);
5✔
158
  cmsg->cmsg_len = CMSG_LEN(sizeof(int));
5✔
159
  cmsg->cmsg_level = SOL_SOCKET;
5✔
160
  cmsg->cmsg_type = SCM_RIGHTS;
5✔
161
  *reinterpret_cast<int*>(CMSG_DATA(cmsg)) = fd_to_pass; // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
5✔
162

163
  if (sendmsg(socket, &msg, 0) == -1) {
5!
164
    throw PDNSException("Unable to send fd message over control channel: " + stringerror());
×
165
  }
×
166
}
5✔
167

168
void RecursorControlChannel::send(int fileDesc, const Answer& msg, unsigned int timeout, int fd_to_pass)
169
{
1,305✔
170
  int ret = waitForRWData(fileDesc, false, static_cast<int>(timeout), 0);
1,305✔
171
  if (ret == 0) {
1,305!
172
    throw PDNSException("Timeout sending message over control channel");
×
173
  }
×
174
  if (ret < 0) {
1,305!
175
    throw PDNSException("Error sending message over control channel:" + stringerror());
×
176
  }
×
177

178
  if (::send(fileDesc, &msg.d_ret, sizeof(msg.d_ret), 0) < 0) {
1,305!
179
    throw PDNSException("Unable to send return code over control channel: " + stringerror());
×
180
  }
×
181
  size_t len = msg.d_str.length();
1,305✔
182
  if (::send(fileDesc, &len, sizeof(len), 0) < 0) {
1,305!
183
    throw PDNSException("Unable to send length over control channel: " + stringerror());
×
184
  }
×
185
  if (::send(fileDesc, msg.d_str.c_str(), len, 0) != static_cast<ssize_t>(len)) {
1,305!
186
    throw PDNSException("Unable to send message over control channel: " + stringerror());
×
187
  }
×
188

189
  if (fd_to_pass != -1) {
1,305✔
190
    sendfd(fileDesc, fd_to_pass);
5✔
191
  }
5✔
192
}
1,305✔
193

194
static void waitForRead(int fileDesc, unsigned int timeout, time_t start)
195
{
4,100✔
196
  time_t elapsed = time(nullptr) - start;
4,100✔
197
  if (elapsed >= timeout) {
4,100!
198
    throw PDNSException("Timeout waiting for control channel data");
×
199
  }
×
200
  // coverity[store_truncates_time_t]
201
  int ret = waitForData(fileDesc, static_cast<int>(timeout - static_cast<unsigned int>(elapsed)), 0);
4,100✔
202
  if (ret == 0) {
4,100!
203
    throw PDNSException("Timeout waiting for control channel data");
×
204
  }
×
205
}
4,100✔
206

207
static size_t getArgMax()
208
{
1,305✔
209
#if defined(ARG_MAX)
210
  return ARG_MAX;
211
#endif
212

213
#if defined(_SC_ARG_MAX)
1,305✔
214
  auto tmp = sysconf(_SC_ARG_MAX);
1,305✔
215
  if (tmp != -1) {
1,305!
216
    return tmp;
1,305✔
217
  }
1,305✔
218
#endif
×
219
  /* _POSIX_ARG_MAX */
220
  return 4096;
×
221
}
1,305✔
222

223
RecursorControlChannel::Answer RecursorControlChannel::recv(int fileDesc, unsigned int timeout)
224
{
1,305✔
225
  // timeout covers the operation of all read ops combined
226
  const time_t start = time(nullptr);
1,305✔
227

228
  waitForRead(fileDesc, timeout, start);
1,305✔
229
  int err{};
1,305✔
230
  auto ret = ::recv(fileDesc, &err, sizeof(err), 0);
1,305✔
231
  if (ret == 0) {
1,305!
232
#if defined(__SANITIZE_THREAD__)
233
    return {0, "bye nicely\n"}; // Hack because TSAN enabled build justs _exits on quit-nicely
234
#endif
235
    throw PDNSException("Unable to receive status over control connection: EOF");
×
236
  }
×
237
  if (ret != sizeof(err)) {
1,305!
238
    throw PDNSException("Unable to receive return status over control channel: " + stringerror());
×
239
  }
×
240

241
  waitForRead(fileDesc, timeout, start);
1,305✔
242
  size_t len{};
1,305✔
243
  if (::recv(fileDesc, &len, sizeof(len), 0) != sizeof(len)) {
1,305!
244
    throw PDNSException("Unable to receive length over control channel: " + stringerror());
×
245
  }
×
246

247
  if (len > getArgMax()) {
1,305!
248
    throw PDNSException("Length of control channel message too large");
×
249
  }
×
250

251
  string str;
1,305✔
252
  str.reserve(len);
1,305✔
253
  while (str.length() < len) {
2,795✔
254
    std::array<char, 1024> buffer{};
1,490✔
255
    waitForRead(fileDesc, timeout, start);
1,490✔
256
    size_t toRead = std::min(len - str.length(), buffer.size());
1,490✔
257
    ssize_t recvd = ::recv(fileDesc, buffer.data(), toRead, 0);
1,490✔
258
    if (recvd <= 0) {
1,490!
259
      // EOF means we have a length error
260
      throw PDNSException("Unable to receive message over control channel: " + stringerror());
×
261
    }
×
262
    str.append(buffer.data(), recvd);
1,490✔
263
  }
1,490✔
264

265
  return {err, std::move(str)};
1,305✔
266
}
1,305✔
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