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

PowerDNS / pdns / 11049952298

26 Sep 2024 09:59AM UTC coverage: 64.7% (+0.005%) from 64.695%
11049952298

Pull #14594

github

web-flow
Merge b1726e0c0 into 42c3a1e1b
Pull Request #14594: auth udp: use stubDoResolve for ALIAS

37040 of 87934 branches covered (42.12%)

Branch coverage included in aggregate %.

25 of 54 new or added lines in 2 files covered. (46.3%)

53 existing lines in 14 files now uncovered.

124664 of 161993 relevant lines covered (76.96%)

4875762.83 hits per line

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

56.83
/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)
53
  {
12,572✔
54
  }
12,572✔
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))
59
  {
57,344✔
60
    if (d_socket < 0) {
57,344!
61
      throw NetworkError(stringerror());
×
62
    }
×
63
    setCloseOnExec(d_socket);
57,344✔
64
  }
57,344✔
65

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

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

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

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

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

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

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

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

142
  void setReuseAddr() const
143
  {
53✔
144
    try {
53✔
145
      ::setReuseAddr(d_socket);
53✔
146
    }
53✔
147
    catch (const PDNSException& e) {
53✔
148
      throw NetworkError(e.reason);
×
149
    }
×
150
  }
53✔
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
  void bind(const ComboAddress& local, bool reuseaddr = true) const
166
  {
755✔
167
    int tmp = 1;
755✔
168
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
169
    if (reuseaddr && setsockopt(d_socket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&tmp), sizeof tmp) < 0) {
755!
170
      throw NetworkError("Setsockopt failed: " + stringerror());
×
171
    }
×
172
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
173
    if (::bind(d_socket, reinterpret_cast<const struct sockaddr*>(&local), local.getSocklen()) < 0) {
755!
174
      throw NetworkError("While binding: " + stringerror());
×
175
    }
×
176
  }
755✔
177

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

394
    return read(buffer, n);
1,974✔
395
  }
1,974✔
396

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

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

411
  int releaseHandle()
412
  {
7,401✔
413
    int ret = d_socket;
7,401✔
414
    d_socket = -1;
7,401✔
415
    return ret;
7,401✔
416
  }
7,401✔
417

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