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

PowerDNS / pdns / 19741624072

27 Nov 2025 03:45PM UTC coverage: 73.086% (+0.02%) from 73.065%
19741624072

Pull #16570

github

web-flow
Merge 08a2cdb1d into f94a3f63f
Pull Request #16570: rec: rewrite all unwrap calls in web.rs

38523 of 63408 branches covered (60.75%)

Branch coverage included in aggregate %.

128044 of 164496 relevant lines covered (77.84%)

6531485.83 hits per line

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

60.67
/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,647✔
53
  {
4,223✔
54
  }
4,223✔
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,773✔
59
  {
37,540✔
60
    if (d_socket < 0) {
37,540!
61
      throw NetworkError(stringerror());
×
62
    }
×
63
    setCloseOnExec(d_socket);
37,540✔
64
  }
37,540✔
65

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

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

83
  ~Socket()
84
  {
25,923✔
85
    try {
25,923✔
86
      if (d_socket != -1) {
25,923✔
87
        closesocket(d_socket);
19,840✔
88
      }
19,840✔
89
    }
25,923✔
90
    catch (const PDNSException& e) {
25,923✔
91
    }
×
92
  }
25,923✔
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,591✔
97
    sockaddr_in remote{};
1,591✔
98
    socklen_t remlen = sizeof(remote);
1,591✔
99
    memset(&remote, 0, sizeof(remote));
1,591✔
100
    int sock = ::accept(d_socket, reinterpret_cast<sockaddr*>(&remote), &remlen); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast): it's the API
1,591✔
101
    if (sock < 0) {
1,591!
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,591✔
110
  }
1,591✔
111

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

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

128
      if (remote.isMappedIPv4()) {
×
129
        return netmaskGroup.match(remote.mapToIPv4());
×
130
      }
×
131
    }
×
132

133
    return false;
×
134
  }
1,576✔
135

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

142
  //! Set the socket to blocking
143
  void setBlocking() const
144
  {
8✔
145
    ::setBlocking(d_socket);
8✔
146
  }
8✔
147

148
  void setReuseAddr() const
149
  {
28✔
150
    try {
28✔
151
      ::setReuseAddr(d_socket);
28✔
152
    }
28✔
153
    catch (const PDNSException& e) {
28✔
154
      throw NetworkError(e.reason);
×
155
    }
×
156
  }
28✔
157

158
  void setFastOpenConnect()
159
  {
×
160
#ifdef TCP_FASTOPEN_CONNECT
×
161
    int on = 1;
×
162
    if (setsockopt(d_socket, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, &on, sizeof(on)) < 0) {
×
163
      throw NetworkError("While setting TCP_FASTOPEN_CONNECT: " + stringerror());
×
164
    }
×
165
#else
166
    throw NetworkError("While setting TCP_FASTOPEN_CONNECT: not compiled in");
167
#endif
168
  }
×
169

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

185
  //! Connect the socket to a specified endpoint
186
  void connect(const ComboAddress& address, int timeout = 0) const
187
  {
18,731✔
188
    SConnectWithTimeout(d_socket, false, address, timeval{timeout, 0});
18,731✔
189
  }
18,731✔
190

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

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

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

236
  //! For connected datagram sockets, send a datagram
237
  void send(const std::string& msg) const
238
  {
352✔
239
    if (::send(d_socket, msg.data(), msg.size(), 0) < 0) {
352!
240
      throw NetworkError("After send: " + stringerror());
×
241
    }
×
242
  }
352✔
243

244
  /** For datagram sockets, send a datagram to a destination
245
      \param dgram The datagram
246
      \param remote The intended destination of the datagram */
247
  void sendTo(const string& dgram, const ComboAddress& remote) const
248
  {
249
    sendTo(dgram.data(), dgram.length(), remote);
250
  }
251

252
  //! Write this data to the socket, taking care that all bytes are written out
253
  void writen(const string& data) const
254
  {
22✔
255
    if (data.empty()) {
22!
256
      return;
257
    }
258

259
    size_t toWrite = data.length();
22✔
260
    const char* ptr = data.data();
22✔
261

262
    do {
22✔
263
      auto res = ::send(d_socket, ptr, toWrite, 0);
22✔
264
      if (res < 0) {
22!
265
        throw NetworkError("Writing to a socket: " + stringerror());
266
      }
267
      if (res == 0) {
22!
268
        throw NetworkError("EOF on socket");
269
      }
270
      toWrite -= static_cast<size_t>(res);
22✔
271
      ptr += static_cast<size_t>(res); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
22✔
272
    } while (toWrite > 0);
22!
273
  }
22✔
274

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

×
290
    if (errno == EAGAIN) {
×
291
      return 0;
×
292
    }
×
293

×
294
    throw NetworkError("Writing to a socket: " + stringerror());
×
295
  }
×
296

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

308
  void writenWithTimeout(const void* buffer, size_t n, int timeout) const
309
  {
1,896✔
310
    size_t bytes = n;
1,896✔
311
    const char* ptr = static_cast<const char*>(buffer);
1,896✔
312

313
    while (bytes > 0) {
3,800✔
314
      auto ret = ::write(d_socket, ptr, bytes);
1,904✔
315
      if (ret < 0) {
1,904✔
316
        if (errno == EAGAIN) {
4!
317
          ret = waitForRWData(d_socket, false, timeout, 0);
4✔
318
          if (ret < 0) {
4!
319
            throw NetworkError("Waiting for data write");
×
320
          }
×
321
          if (ret == 0) {
4!
322
            throw NetworkError("Timeout writing data");
×
323
          }
×
324
          continue;
4✔
325
        }
4✔
326
        throw NetworkError("Writing data: " + stringerror());
×
327
      }
4✔
328
      if (ret == 0) {
1,900!
329
        throw NetworkError("Did not fulfill TCP write due to EOF");
×
330
      }
×
331

332
      ptr += static_cast<size_t>(ret); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1,900✔
333
      bytes -= static_cast<size_t>(ret);
1,900✔
334
    }
1,900✔
335
  }
1,896✔
336

337
  //! Reads a block of data from the socket to a string
338
  void read(string& data)
339
  {
348✔
340
    d_buffer.resize(s_buflen);
348✔
341
    ssize_t res = ::recv(d_socket, d_buffer.data(), s_buflen, 0);
348✔
342
    if (res < 0) {
348!
343
      throw NetworkError("Reading from a socket: " + stringerror());
×
344
    }
×
345
    data.assign(d_buffer, 0, static_cast<size_t>(res));
348✔
346
  }
348✔
347

348
  //! Reads a block of data from the socket to a block of memory
349
  size_t read(char* buffer, size_t bytes) const
350
  {
35,055✔
351
    auto res = ::recv(d_socket, buffer, bytes, 0);
35,055✔
352
    if (res < 0) {
35,055✔
353
      throw NetworkError("Reading from a socket: " + stringerror());
4✔
354
    }
4✔
355
    return static_cast<size_t>(res);
35,051✔
356
  }
35,055✔
357

358
  /** Read a bock of data from the socket to a block of memory,
359
   *   waiting at most 'timeout' seconds for the data to become
360
   *   available. Be aware that this does _NOT_ handle partial reads
361
   *   for you.
362
   */
363
  size_t readWithTimeout(char* buffer, size_t n, int timeout) const
364
  {
2,127✔
365
    int err = waitForRWData(d_socket, true, timeout, 0);
2,127✔
366

367
    if (err == 0) {
2,127!
368
      throw NetworkError("timeout reading");
×
369
    }
×
370
    if (err < 0) {
2,127!
371
      throw NetworkError("nonblocking read failed: " + stringerror());
×
372
    }
×
373

374
    return read(buffer, n);
2,127✔
375
  }
2,127✔
376

377
  //! Sets the socket to listen with a default listen backlog of 10 pending connections
378
  void listen(int length = 10) const
379
  {
147✔
380
    if (::listen(d_socket, length) < 0) {
147!
381
      throw NetworkError("Setting socket to listen: " + stringerror());
×
382
    }
×
383
  }
147✔
384

385
  //! Returns the internal file descriptor of the socket
386
  [[nodiscard]] int getHandle() const
387
  {
34,742✔
388
    return d_socket;
34,742✔
389
  }
34,742✔
390

391
  int releaseHandle()
392
  {
2,810✔
393
    int ret = d_socket;
2,810✔
394
    d_socket = -1;
2,810✔
395
    return ret;
2,810✔
396
  }
2,810✔
397

398
private:
399
  static constexpr size_t s_buflen{4096};
400
  std::string d_buffer;
401
  int d_socket;
402
};
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