• 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

57.14
/pdns/sstuff.hh
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
#pragma once
23
#include <string>
24
#include <sstream>
25
#include <iostream>
26
#include "iputils.hh"
27
#include <cerrno>
28
#include <sys/types.h>
29
#include <unistd.h>
30
#include <sys/socket.h>
31
#include <netinet/in.h>
32
#include <netinet/tcp.h>
33
#include <arpa/inet.h>
34
#include <sys/select.h>
35
#include <fcntl.h>
36
#include <stdexcept>
37

38
#include <csignal>
39
#include "namespaces.hh"
40
#include "noinitvector.hh"
41

42
using ProtocolType = int; //!< Supported protocol types
43

44
//! Representation of a Socket and many of the Berkeley functions available
45
class Socket
46
{
47
public:
48
  Socket(const Socket&) = delete;
49
  Socket& operator=(const Socket&) = delete;
50

51
  Socket(int socketDesc) :
52
    d_socket(socketDesc)
2,508✔
53
  {
4,031✔
54
  }
4,031✔
55

56
  //! Construct a socket of specified address family and socket type.
57
  Socket(int addressFamily, int socketType, ProtocolType protocolType = 0) :
58
    d_socket(socket(addressFamily, socketType, protocolType))
4,516✔
59
  {
37,275✔
60
    if (d_socket < 0) {
37,275!
61
      throw NetworkError(stringerror());
×
62
    }
×
63
    setCloseOnExec(d_socket);
37,275✔
64
  }
37,275✔
65

66
  Socket(Socket&& rhs) noexcept :
67
    d_buffer(std::move(rhs.d_buffer)), d_socket(rhs.d_socket)
619✔
68
  {
619✔
69
    rhs.d_socket = -1;
619✔
70
  }
619✔
71

72
  Socket& operator=(Socket&& rhs) noexcept
73
  {
1,689✔
74
    if (d_socket != -1) {
1,689!
75
      close(d_socket);
×
76
    }
×
77
    d_socket = rhs.d_socket;
1,689✔
78
    rhs.d_socket = -1;
1,689✔
79
    d_buffer = std::move(rhs.d_buffer);
1,689✔
80
    return *this;
1,689✔
81
  }
1,689✔
82

83
  ~Socket()
84
  {
25,488✔
85
    try {
25,488✔
86
      if (d_socket != -1) {
25,488✔
87
        closesocket(d_socket);
19,652✔
88
      }
19,652✔
89
    }
25,488✔
90
    catch (const PDNSException& e) {
25,488✔
91
    }
×
92
  }
25,488✔
93

94
  //! If the socket is capable of doing so, this function will wait for a connection
95
  [[nodiscard]] std::unique_ptr<Socket> accept() const
96
  {
1,537✔
97
    sockaddr_in remote{};
1,537✔
98
    socklen_t remlen = sizeof(remote);
1,537✔
99
    memset(&remote, 0, sizeof(remote));
1,537✔
100
    int sock = ::accept(d_socket, reinterpret_cast<sockaddr*>(&remote), &remlen); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast): it's the API
1,537✔
101
    if (sock < 0) {
1,537!
102
      if (errno == EAGAIN) {
×
103
        return nullptr;
×
104
      }
×
105

106
      throw NetworkError("Accepting a connection: " + stringerror());
×
107
    }
×
108

109
    return std::make_unique<Socket>(sock);
1,537✔
110
  }
1,537✔
111

112
  //! Get remote address
113
  bool getRemote(ComboAddress& remote) const
114
  {
3,046✔
115
    socklen_t remotelen = sizeof(remote);
3,046✔
116
    return getpeername(d_socket, reinterpret_cast<struct sockaddr*>(&remote), &remotelen) >= 0; // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast): it's the API
3,046✔
117
  }
3,046✔
118

119
  //! Check remote address against netmaskgroup ng
120
  [[nodiscard]] bool acl(const NetmaskGroup& netmaskGroup) const
121
  {
1,523✔
122
    ComboAddress remote;
1,523✔
123
    if (getRemote(remote)) {
1,523!
124
      return netmaskGroup.match(remote);
1,523✔
125
    }
1,523✔
126

127
    return false;
×
128
  }
1,523✔
129

130
  //! Set the socket to non-blocking
131
  void setNonBlocking() const
132
  {
7,761✔
133
    ::setNonBlocking(d_socket);
7,761✔
134
  }
7,761✔
135

136
  //! Set the socket to blocking
137
  void setBlocking() const
138
  {
8✔
139
    ::setBlocking(d_socket);
8✔
140
  }
8✔
141

142
  void setReuseAddr() const
143
  {
27✔
144
    try {
27✔
145
      ::setReuseAddr(d_socket);
27✔
146
    }
27✔
147
    catch (const PDNSException& e) {
27✔
148
      throw NetworkError(e.reason);
×
149
    }
×
150
  }
27✔
151

152
  void setFastOpenConnect()
153
  {
×
154
#ifdef TCP_FASTOPEN_CONNECT
×
155
    int on = 1;
×
156
    if (setsockopt(d_socket, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, &on, sizeof(on)) < 0) {
×
157
      throw NetworkError("While setting TCP_FASTOPEN_CONNECT: " + stringerror());
×
158
    }
×
159
#else
160
    throw NetworkError("While setting TCP_FASTOPEN_CONNECT: not compiled in");
161
#endif
162
  }
×
163

164
  //! Bind the socket to a specified endpoint
165
  template <typename T>
166
  void bind(const T& local, bool reuseaddr = true) const
167
  {
247✔
168
    int tmp = 1;
247✔
169
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
170
    if (reuseaddr && setsockopt(d_socket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&tmp), sizeof tmp) < 0) {
247!
171
      throw NetworkError("Setsockopt failed: " + stringerror());
×
172
    }
×
173
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
174
    if (::bind(d_socket, reinterpret_cast<const struct sockaddr*>(&local), local.getSocklen()) < 0) {
247!
175
      throw NetworkError("While binding: " + stringerror());
×
176
    }
×
177
  }
247✔
178

179
  //! Connect the socket to a specified endpoint
180
  void connect(const ComboAddress& address, int timeout = 0) const
181
  {
18,606✔
182
    SConnectWithTimeout(d_socket, address, timeval{timeout, 0});
18,606✔
183
  }
18,606✔
184

185
  //! For datagram sockets, receive a datagram and learn where it came from
186
  /** For datagram sockets, receive a datagram and learn where it came from
187
      \param dgram Will be filled with the datagram
188
      \param ep Will be filled with the origin of the datagram */
189
  void recvFrom(string& dgram, ComboAddress& remote) const
190
  {
191
    socklen_t remlen = sizeof(remote);
192
    if (dgram.size() < s_buflen) {
193
      dgram.resize(s_buflen);
194
    }
195
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
196
    auto bytes = recvfrom(d_socket, dgram.data(), dgram.size(), 0, reinterpret_cast<sockaddr*>(&remote), &remlen);
197
    if (bytes < 0) {
198
      throw NetworkError("After recvfrom: " + stringerror());
199
    }
200
    dgram.resize(static_cast<size_t>(bytes));
201
  }
202

203
  bool recvFromAsync(PacketBuffer& dgram, ComboAddress& remote) const
204
  {
×
205
    socklen_t remlen = sizeof(remote);
×
206
    if (dgram.size() < s_buflen) {
×
207
      dgram.resize(s_buflen);
×
208
    }
×
209
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
×
210
    auto bytes = recvfrom(d_socket, dgram.data(), dgram.size(), 0, reinterpret_cast<sockaddr*>(&remote), &remlen);
×
211
    if (bytes < 0) {
×
212
      if (errno != EAGAIN) {
×
213
        throw NetworkError("After async recvfrom: " + stringerror());
×
214
      }
×
215
      return false;
×
216
    }
×
217
    dgram.resize(static_cast<size_t>(bytes));
×
218
    return true;
×
219
  }
×
220

221
  //! For datagram sockets, send a datagram to a destination
222
  void sendTo(const char* msg, size_t len, const ComboAddress& remote) const
223
  {
224
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
225
    if (sendto(d_socket, msg, len, 0, reinterpret_cast<const sockaddr*>(&remote), remote.getSocklen()) < 0) {
226
      throw NetworkError("After sendto: " + stringerror());
227
    }
228
  }
229

230
  //! For connected datagram sockets, send a datagram
231
  void send(const std::string& msg) const
232
  {
352✔
233
    if (::send(d_socket, msg.data(), msg.size(), 0) < 0) {
352!
234
      throw NetworkError("After send: " + stringerror());
×
235
    }
×
236
  }
352✔
237

238
  /** For datagram sockets, send a datagram to a destination
239
      \param dgram The datagram
240
      \param remote The intended destination of the datagram */
241
  void sendTo(const string& dgram, const ComboAddress& remote) const
242
  {
243
    sendTo(dgram.data(), dgram.length(), remote);
244
  }
245

246
  //! Write this data to the socket, taking care that all bytes are written out
247
  void writen(const string& data) const
248
  {
16✔
249
    if (data.empty()) {
16!
250
      return;
251
    }
252

253
    size_t toWrite = data.length();
16✔
254
    const char* ptr = data.data();
16✔
255

256
    do {
16✔
257
      auto res = ::send(d_socket, ptr, toWrite, 0);
16✔
258
      if (res < 0) {
16!
259
        throw NetworkError("Writing to a socket: " + stringerror());
260
      }
261
      if (res == 0) {
16!
262
        throw NetworkError("EOF on socket");
263
      }
264
      toWrite -= static_cast<size_t>(res);
16✔
265
      ptr += static_cast<size_t>(res); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
16✔
266
    } while (toWrite > 0);
16!
267
  }
16✔
268

269
  //! tries to write toWrite bytes from ptr to the socket
270
  /** tries to write toWrite bytes from ptr to the socket, but does not make sure they al get written out
271
      \param ptr Location to write from
272
      \param toWrite number of bytes to try
273
  */
274
  size_t tryWrite(const char* ptr, size_t toWrite) const
275
  {
×
276
    auto res = ::send(d_socket, ptr, toWrite, 0);
×
277
    if (res == 0) {
×
278
      throw NetworkError("EOF on writing to a socket");
×
279
    }
×
280
    if (res > 0) {
×
281
      return res;
×
282
    }
×
283

×
284
    if (errno == EAGAIN) {
×
285
      return 0;
×
286
    }
×
287

×
288
    throw NetworkError("Writing to a socket: " + stringerror());
×
289
  }
×
290

291
  //! Writes toWrite bytes from ptr to the socket
292
  /** Writes toWrite bytes from ptr to the socket. Returns how many bytes were written */
293
  size_t write(const char* ptr, size_t toWrite) const
294
  {
295
    auto res = ::send(d_socket, ptr, toWrite, 0);
296
    if (res < 0) {
297
      throw NetworkError("Writing to a socket: " + stringerror());
298
    }
299
    return res;
300
  }
301

302
  void writenWithTimeout(const void* buffer, size_t n, int timeout) const
303
  {
1,841✔
304
    size_t bytes = n;
1,841✔
305
    const char* ptr = static_cast<const char*>(buffer);
1,841✔
306

307
    while (bytes > 0) {
3,690✔
308
      auto ret = ::write(d_socket, ptr, bytes);
1,849✔
309
      if (ret < 0) {
1,849✔
310
        if (errno == EAGAIN) {
4!
311
          ret = waitForRWData(d_socket, false, timeout, 0);
4✔
312
          if (ret < 0) {
4!
313
            throw NetworkError("Waiting for data write");
×
314
          }
×
315
          if (ret == 0) {
4!
316
            throw NetworkError("Timeout writing data");
×
317
          }
×
318
          continue;
4✔
319
        }
4✔
320
        throw NetworkError("Writing data: " + stringerror());
×
321
      }
4✔
322
      if (ret == 0) {
1,845!
323
        throw NetworkError("Did not fulfill TCP write due to EOF");
×
324
      }
×
325

326
      ptr += static_cast<size_t>(ret); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1,845✔
327
      bytes -= static_cast<size_t>(ret);
1,845✔
328
    }
1,845✔
329
  }
1,841✔
330

331
  //! reads one character from the socket
332
  [[nodiscard]] int getChar() const
333
  {
×
334
    char character{};
×
335

×
336
    ssize_t res = ::recv(d_socket, &character, 1, 0);
×
337
    if (res == 0) {
×
338
      return character;
×
339
    }
×
340
    return -1;
×
341
  }
×
342

343
  void getline(string& data) const
344
  {
×
345
    data.clear();
×
346
    while (true) {
×
347
      int character = getChar();
×
348
      if (character == -1) {
×
349
        break;
×
350
      }
×
351
      data += (char)character;
×
352
      if (character == '\n') {
×
353
        break;
×
354
      }
×
355
    }
×
356
  }
×
357

358
  //! Reads a block of data from the socket to a string
359
  void read(string& data)
360
  {
348✔
361
    d_buffer.resize(s_buflen);
348✔
362
    ssize_t res = ::recv(d_socket, d_buffer.data(), s_buflen, 0);
348✔
363
    if (res < 0) {
348!
UNCOV
364
      throw NetworkError("Reading from a socket: " + stringerror());
×
UNCOV
365
    }
×
366
    data.assign(d_buffer, 0, static_cast<size_t>(res));
348✔
367
  }
348✔
368

369
  //! Reads a block of data from the socket to a block of memory
370
  size_t read(char* buffer, size_t bytes) const
371
  {
35,004✔
372
    auto res = ::recv(d_socket, buffer, bytes, 0);
35,004✔
373
    if (res < 0) {
35,004✔
374
      throw NetworkError("Reading from a socket: " + stringerror());
4✔
375
    }
4✔
376
    return static_cast<size_t>(res);
35,000✔
377
  }
35,004✔
378

379
  /** Read a bock of data from the socket to a block of memory,
380
   *   waiting at most 'timeout' seconds for the data to become
381
   *   available. Be aware that this does _NOT_ handle partial reads
382
   *   for you.
383
   */
384
  size_t readWithTimeout(char* buffer, size_t n, int timeout) const
385
  {
2,076✔
386
    int err = waitForRWData(d_socket, true, timeout, 0);
2,076✔
387

388
    if (err == 0) {
2,076!
389
      throw NetworkError("timeout reading");
×
390
    }
×
391
    if (err < 0) {
2,076!
392
      throw NetworkError("nonblocking read failed: " + stringerror());
×
393
    }
×
394

395
    return read(buffer, n);
2,076✔
396
  }
2,076✔
397

398
  //! Sets the socket to listen with a default listen backlog of 10 pending connections
399
  void listen(int length = 10) const
400
  {
139✔
401
    if (::listen(d_socket, length) < 0) {
139!
402
      throw NetworkError("Setting socket to listen: " + stringerror());
×
403
    }
×
404
  }
139✔
405

406
  //! Returns the internal file descriptor of the socket
407
  [[nodiscard]] int getHandle() const
408
  {
33,897✔
409
    return d_socket;
33,897✔
410
  }
33,897✔
411

412
  int releaseHandle()
413
  {
2,693✔
414
    int ret = d_socket;
2,693✔
415
    d_socket = -1;
2,693✔
416
    return ret;
2,693✔
417
  }
2,693✔
418

419
private:
420
  static constexpr size_t s_buflen{4096};
421
  std::string d_buffer;
422
  int d_socket;
423
};
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