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

PowerDNS / pdns / 18834649492

27 Oct 2025 08:32AM UTC coverage: 73.012% (+4.2%) from 68.781%
18834649492

Pull #16368

github

web-flow
Merge a290d76a2 into 82ea647b4
Pull Request #16368: ci(dnsdist): Build and test on arm64/aarch64

38267 of 63120 branches covered (60.63%)

Branch coverage included in aggregate %.

127456 of 163860 relevant lines covered (77.78%)

5005850.05 hits per line

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

62.02
/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,617✔
53
  {
4,193✔
54
  }
4,193✔
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,825✔
59
  {
37,585✔
60
    if (d_socket < 0) {
37,585!
61
      throw NetworkError(stringerror());
×
62
    }
×
63
    setCloseOnExec(d_socket);
37,585✔
64
  }
37,585✔
65

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

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

83
  ~Socket()
84
  {
25,958✔
85
    try {
25,958✔
86
      if (d_socket != -1) {
25,958✔
87
        closesocket(d_socket);
19,830✔
88
      }
19,830✔
89
    }
25,958✔
90
    catch (const PDNSException& e) {
25,958✔
91
    }
×
92
  }
25,958✔
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
      return netmaskGroup.match(remote);
1,576✔
125
    }
1,576✔
126

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

130
  //! Set the socket to non-blocking
131
  void setNonBlocking() const
132
  {
8,107✔
133
    ::setNonBlocking(d_socket);
8,107✔
134
  }
8,107✔
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
  {
28✔
144
    try {
28✔
145
      ::setReuseAddr(d_socket);
28✔
146
    }
28✔
147
    catch (const PDNSException& e) {
28✔
148
      throw NetworkError(e.reason);
×
149
    }
×
150
  }
28✔
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
  {
246✔
168
    int tmp = 1;
246✔
169
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
170
    if (reuseaddr && setsockopt(d_socket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&tmp), sizeof tmp) < 0) {
246!
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) {
246!
175
      throw NetworkError("While binding: " + stringerror());
×
176
    }
×
177
  }
246✔
178

179
  //! Connect the socket to a specified endpoint
180
  void connect(const ComboAddress& address, int timeout = 0) const
181
  {
18,703✔
182
    SConnectWithTimeout(d_socket, false, address, timeval{timeout, 0});
18,703✔
183
  }
18,703✔
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
  {
22✔
249
    if (data.empty()) {
22!
250
      return;
251
    }
252

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

256
    do {
22✔
257
      auto res = ::send(d_socket, ptr, toWrite, 0);
22✔
258
      if (res < 0) {
22!
259
        throw NetworkError("Writing to a socket: " + stringerror());
260
      }
261
      if (res == 0) {
22!
262
        throw NetworkError("EOF on socket");
263
      }
264
      toWrite -= static_cast<size_t>(res);
22✔
265
      ptr += static_cast<size_t>(res); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
22✔
266
    } while (toWrite > 0);
22!
267
  }
22✔
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,895✔
304
    size_t bytes = n;
1,895✔
305
    const char* ptr = static_cast<const char*>(buffer);
1,895✔
306

307
    while (bytes > 0) {
3,798✔
308
      auto ret = ::write(d_socket, ptr, bytes);
1,903✔
309
      if (ret < 0) {
1,903✔
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,899!
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,899✔
327
      bytes -= static_cast<size_t>(ret);
1,899✔
328
    }
1,899✔
329
  }
1,895✔
330

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

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

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

361
    if (err == 0) {
2,127!
362
      throw NetworkError("timeout reading");
×
363
    }
×
364
    if (err < 0) {
2,127!
365
      throw NetworkError("nonblocking read failed: " + stringerror());
×
366
    }
×
367

368
    return read(buffer, n);
2,127✔
369
  }
2,127✔
370

371
  //! Sets the socket to listen with a default listen backlog of 10 pending connections
372
  void listen(int length = 10) const
373
  {
144✔
374
    if (::listen(d_socket, length) < 0) {
144!
375
      throw NetworkError("Setting socket to listen: " + stringerror());
×
376
    }
×
377
  }
144✔
378

379
  //! Returns the internal file descriptor of the socket
380
  [[nodiscard]] int getHandle() const
381
  {
34,761✔
382
    return d_socket;
34,761✔
383
  }
34,761✔
384

385
  int releaseHandle()
386
  {
2,870✔
387
    int ret = d_socket;
2,870✔
388
    d_socket = -1;
2,870✔
389
    return ret;
2,870✔
390
  }
2,870✔
391

392
private:
393
  static constexpr size_t s_buflen{4096};
394
  std::string d_buffer;
395
  int d_socket;
396
};
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