]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/sstuff.hh
Merge pull request #14020 from omoerbeek/rec-compiling-rust-dcos
[thirdparty/pdns.git] / pdns / sstuff.hh
CommitLineData
12471842
PL
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 */
e8c59f2d 22#pragma once
a640a9d4
BH
23#include <string>
24#include <sstream>
25#include <iostream>
fdf05fd4 26#include "iputils.hh"
dc6aa7f5 27#include <cerrno>
a640a9d4 28#include <sys/types.h>
705f31ae 29#include <unistd.h>
a640a9d4
BH
30#include <sys/socket.h>
31#include <netinet/in.h>
9ef45e51 32#include <netinet/tcp.h>
a640a9d4
BH
33#include <arpa/inet.h>
34#include <sys/select.h>
35#include <fcntl.h>
36#include <stdexcept>
dd7da6cd 37
fdf05fd4 38#include <boost/utility.hpp>
a640a9d4 39#include <csignal>
10f4eea8 40#include "namespaces.hh"
2e1c76aa 41#include "noinitvector.hh"
a640a9d4 42
2e1c76aa 43using ProtocolType = int; //!< Supported protocol types
a640a9d4
BH
44
45//! Representation of a Socket and many of the Berkeley functions available
fdf05fd4 46class Socket : public boost::noncopyable
a640a9d4 47{
88d4fe87 48public:
86c3ac4a 49 Socket(int fd): d_socket(fd)
4941d888 50 {
4941d888 51 }
52
93f4e5ce 53 //! Construct a socket of specified address family and socket type.
54 Socket(int af, int st, ProtocolType pt=0)
a640a9d4 55 {
86c3ac4a 56 if((d_socket=socket(af, st, pt))<0)
c52b8cb6 57 throw NetworkError(stringerror());
3897b9e1 58 setCloseOnExec(d_socket);
a640a9d4
BH
59 }
60
71633799
RP
61 Socket(Socket&& rhs) noexcept :
62 d_buffer(std::move(rhs.d_buffer)), d_socket(rhs.d_socket)
d0ae6360
RG
63 {
64 rhs.d_socket = -1;
65 }
66
71633799 67 Socket& operator=(Socket&& rhs) noexcept
bcf26e85 68 {
ae3b96d9
RG
69 if (d_socket != -1) {
70 close(d_socket);
71 }
bcf26e85
RG
72 d_socket = rhs.d_socket;
73 rhs.d_socket = -1;
74 d_buffer = std::move(rhs.d_buffer);
75 return *this;
76 }
77
a640a9d4
BH
78 ~Socket()
79 {
a7b68ae7 80 try {
d0ae6360
RG
81 if (d_socket != -1) {
82 closesocket(d_socket);
83 }
a7b68ae7
RG
84 }
85 catch(const PDNSException& e) {
86 }
a640a9d4
BH
87 }
88
89 //! If the socket is capable of doing so, this function will wait for a connection
86c3ac4a 90 std::unique_ptr<Socket> accept()
a640a9d4
BH
91 {
92 struct sockaddr_in remote;
93 socklen_t remlen=sizeof(remote);
94 memset(&remote, 0, sizeof(remote));
86c3ac4a 95 int s=::accept(d_socket, reinterpret_cast<sockaddr *>(&remote), &remlen);
a640a9d4
BH
96 if(s<0) {
97 if(errno==EAGAIN)
8a781bb5 98 return nullptr;
a640a9d4 99
c52b8cb6 100 throw NetworkError("Accepting a connection: "+stringerror());
a640a9d4
BH
101 }
102
2bbc9eb0 103 return std::make_unique<Socket>(s);
a640a9d4
BH
104 }
105
f0e5e114
KM
106 //! Get remote address
107 bool getRemote(ComboAddress &remote) {
108 socklen_t remotelen=sizeof(remote);
86c3ac4a 109 return (getpeername(d_socket, reinterpret_cast<struct sockaddr *>(&remote), &remotelen) >= 0);
f0e5e114
KM
110 }
111
ffac9ebc 112 //! Check remote address against netmaskgroup ng
86c3ac4a 113 bool acl(const NetmaskGroup &ng)
69e7f117
KM
114 {
115 ComboAddress remote;
f0e5e114 116 if (getRemote(remote))
86c3ac4a 117 return ng.match(remote);
69e7f117
KM
118
119 return false;
120 }
121
a640a9d4
BH
122 //! Set the socket to non-blocking
123 void setNonBlocking()
124 {
3897b9e1 125 ::setNonBlocking(d_socket);
a640a9d4 126 }
86c3ac4a 127
952d3fcb 128 //! Set the socket to blocking
129 void setBlocking()
130 {
3897b9e1 131 ::setBlocking(d_socket);
952d3fcb 132 }
a640a9d4 133
825fa717
CH
134 void setReuseAddr()
135 {
bf676f2f
PL
136 try {
137 ::setReuseAddr(d_socket);
86c3ac4a 138 } catch (const PDNSException &e) {
bf676f2f
PL
139 throw NetworkError(e.reason);
140 }
825fa717
CH
141 }
142
9ef45e51 143 void setFastOpenConnect()
2bc7ddac
O
144 {
145#ifdef TCP_FASTOPEN_CONNECT
9ef45e51 146 int on = 1;
c33192f3 147 if (setsockopt(d_socket, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, &on, sizeof(on)) < 0) {
9ef45e51 148 throw NetworkError("While setting TCP_FASTOPEN_CONNECT: " + stringerror());
c33192f3
O
149 }
150#else
151 throw NetworkError("While setting TCP_FASTOPEN_CONNECT: not compiled in");
2bc7ddac
O
152#endif
153 }
154
3e76a82e 155 //! Bind the socket to a specified endpoint
d0ae6360 156 void bind(const ComboAddress &local, bool reuseaddr=true)
3e76a82e
BH
157 {
158 int tmp=1;
d0ae6360 159 if(reuseaddr && setsockopt(d_socket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&tmp), sizeof tmp)<0)
c52b8cb6 160 throw NetworkError("Setsockopt failed: "+stringerror());
3e76a82e 161
86c3ac4a 162 if(::bind(d_socket, reinterpret_cast<const struct sockaddr *>(&local), local.getSocklen())<0)
c52b8cb6 163 throw NetworkError("While binding: "+stringerror());
3e76a82e
BH
164 }
165
a640a9d4 166 //! Connect the socket to a specified endpoint
67180942 167 void connect(const ComboAddress &ep, int timeout=0)
a640a9d4 168 {
50111728 169 SConnectWithTimeout(d_socket, ep, timeval{timeout,0});
a640a9d4
BH
170 }
171
172
173 //! For datagram sockets, receive a datagram and learn where it came from
174 /** For datagram sockets, receive a datagram and learn where it came from
175 \param dgram Will be filled with the datagram
176 \param ep Will be filled with the origin of the datagram */
2e1c76aa 177 void recvFrom(string &dgram, ComboAddress& remote)
a640a9d4 178 {
2e1c76aa
RG
179 socklen_t remlen = sizeof(remote);
180 if (dgram.size() < s_buflen) {
181 dgram.resize(s_buflen);
182 }
183 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
184 auto bytes = recvfrom(d_socket, dgram.data(), dgram.size(), 0, reinterpret_cast<sockaddr *>(&remote) , &remlen);
185 if (bytes < 0) {
186 throw NetworkError("After recvfrom: " + stringerror());
187 }
188 dgram.resize(static_cast<size_t>(bytes));
a640a9d4
BH
189 }
190
2e1c76aa 191 bool recvFromAsync(PacketBuffer& dgram, ComboAddress& remote)
9a450843 192 {
86c3ac4a 193 socklen_t remlen = sizeof(remote);
2e1c76aa
RG
194 if (dgram.size() < s_buflen) {
195 dgram.resize(s_buflen);
196 }
197 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
198 auto bytes = recvfrom(d_socket, dgram.data(), dgram.size(), 0, reinterpret_cast<sockaddr *>(&remote), &remlen);
26dc9fdf
RG
199 if (bytes < 0) {
200 if (errno != EAGAIN) {
201 throw NetworkError("After async recvfrom: " + stringerror());
8b3cfcd3 202 }
2e1c76aa
RG
203 else {
204 return false;
205 }
8b3cfcd3 206 }
2e1c76aa 207 dgram.resize(static_cast<size_t>(bytes));
9a450843
BH
208 return true;
209 }
210
a640a9d4 211 //! For datagram sockets, send a datagram to a destination
2e1c76aa 212 void sendTo(const char* msg, size_t len, const ComboAddress& remote)
4111e1ae 213 {
2e1c76aa
RG
214 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
215 if (sendto(d_socket, msg, len, 0, reinterpret_cast<const sockaddr *>(&remote), remote.getSocklen()) < 0) {
216 throw NetworkError("After sendto: " + stringerror());
217 }
4465e941 218 }
219
220 //! For connected datagram sockets, send a datagram
221 void send(const std::string& msg)
222 {
2e1c76aa 223 if (::send(d_socket, msg.data(), msg.size(), 0) < 0) {
c52b8cb6 224 throw NetworkError("After send: "+stringerror());
2e1c76aa 225 }
4111e1ae 226 }
227
bb85386d 228
a640a9d4
BH
229 /** For datagram sockets, send a datagram to a destination
230 \param dgram The datagram
2e1c76aa
RG
231 \param remote The intended destination of the datagram */
232 void sendTo(const string& dgram, const ComboAddress& remote)
a640a9d4 233 {
2e1c76aa 234 sendTo(dgram.data(), dgram.length(), remote);
a640a9d4
BH
235 }
236
4111e1ae 237
bb85386d 238 //! Write this data to the socket, taking care that all bytes are written out
a640a9d4
BH
239 void writen(const string &data)
240 {
241 if(data.empty())
242 return;
243
a683e8bd
RG
244 size_t toWrite=data.length();
245 ssize_t res;
a640a9d4
BH
246 const char *ptr=data.c_str();
247
248 do {
84e66a59 249 res=::send(d_socket, ptr, toWrite, 0);
bb85386d 250 if(res<0)
c52b8cb6 251 throw NetworkError("Writing to a socket: "+stringerror());
a640a9d4 252 if(!res)
4957a608 253 throw NetworkError("EOF on socket");
86c3ac4a
RG
254 toWrite -= static_cast<size_t>(res);
255 ptr += static_cast<size_t>(res);
256 } while(toWrite);
a640a9d4
BH
257
258 }
259
260 //! tries to write toWrite bytes from ptr to the socket
261 /** tries to write toWrite bytes from ptr to the socket, but does not make sure they al get written out
262 \param ptr Location to write from
263 \param toWrite number of bytes to try
264 */
a683e8bd 265 size_t tryWrite(const char *ptr, size_t toWrite)
a640a9d4 266 {
a683e8bd 267 ssize_t res;
705f31ae 268 res=::send(d_socket,ptr,toWrite,0);
a640a9d4
BH
269 if(res==0)
270 throw NetworkError("EOF on writing to a socket");
271
272 if(res>0)
273 return res;
274
275 if(errno==EAGAIN)
276 return 0;
bb85386d 277
c52b8cb6 278 throw NetworkError("Writing to a socket: "+stringerror());
a640a9d4
BH
279 }
280
281 //! Writes toWrite bytes from ptr to the socket
282 /** Writes toWrite bytes from ptr to the socket. Returns how many bytes were written */
a683e8bd 283 size_t write(const char *ptr, size_t toWrite)
a640a9d4 284 {
a683e8bd 285 ssize_t res;
705f31ae 286 res=::send(d_socket,ptr,toWrite,0);
a640a9d4 287 if(res<0) {
c52b8cb6 288 throw NetworkError("Writing to a socket: "+stringerror());
a640a9d4
BH
289 }
290 return res;
291 }
292
a683e8bd 293 void writenWithTimeout(const void *buffer, size_t n, int timeout)
825fa717 294 {
a683e8bd 295 size_t bytes=n;
86c3ac4a 296 const char *ptr = reinterpret_cast<const char*>(buffer);
a683e8bd 297 ssize_t ret;
825fa717
CH
298 while(bytes) {
299 ret=::write(d_socket, ptr, bytes);
300 if(ret < 0) {
f0c48196 301 if(errno == EAGAIN) {
825fa717
CH
302 ret=waitForRWData(d_socket, false, timeout, 0);
303 if(ret < 0)
304 throw NetworkError("Waiting for data write");
305 if(!ret)
306 throw NetworkError("Timeout writing data");
307 continue;
308 }
309 else
310 throw NetworkError("Writing data: "+stringerror());
311 }
312 if(!ret) {
313 throw NetworkError("Did not fulfill TCP write due to EOF");
314 }
315
86c3ac4a
RG
316 ptr += static_cast<size_t>(ret);
317 bytes -= static_cast<size_t>(ret);
825fa717
CH
318 }
319 }
a640a9d4 320
bb85386d 321 //! reads one character from the socket
ab2cee56 322 int getChar()
a640a9d4
BH
323 {
324 char c;
325
a683e8bd 326 ssize_t res=::recv(d_socket,&c,1,0);
a640a9d4
BH
327 if(res)
328 return c;
329 return -1;
330 }
331
332 void getline(string &data)
333 {
ab2cee56 334 data="";
a640a9d4 335 int c;
ab2cee56 336 while((c=getChar())!=-1) {
a640a9d4
BH
337 data+=(char)c;
338 if(c=='\n')
4957a608 339 break;
a640a9d4
BH
340 }
341 }
342
343 //! Reads a block of data from the socket to a string
344 void read(string &data)
345 {
86c3ac4a
RG
346 d_buffer.resize(s_buflen);
347 ssize_t res=::recv(d_socket, &d_buffer[0], s_buflen, 0);
bb85386d 348 if(res<0)
c52b8cb6 349 throw NetworkError("Reading from a socket: "+stringerror());
86c3ac4a 350 data.assign(d_buffer, 0, static_cast<size_t>(res));
a640a9d4
BH
351 }
352
353 //! Reads a block of data from the socket to a block of memory
a683e8bd 354 size_t read(char *buffer, size_t bytes)
a640a9d4 355 {
86c3ac4a 356 ssize_t res=::recv(d_socket, buffer, bytes, 0);
bb85386d 357 if(res<0)
c52b8cb6 358 throw NetworkError("Reading from a socket: "+stringerror());
86c3ac4a 359 return static_cast<size_t>(res);
825fa717
CH
360 }
361
9c3ff201
RG
362 /** Read a bock of data from the socket to a block of memory,
363 * waiting at most 'timeout' seconds for the data to become
364 * available. Be aware that this does _NOT_ handle partial reads
365 * for you.
366 */
a683e8bd 367 ssize_t readWithTimeout(char* buffer, size_t n, int timeout)
825fa717
CH
368 {
369 int err = waitForRWData(d_socket, true, timeout, 0);
370
371 if(err == 0)
372 throw NetworkError("timeout reading");
373 if(err < 0)
c52b8cb6 374 throw NetworkError("nonblocking read failed: "+stringerror());
a640a9d4 375
825fa717 376 return read(buffer, n);
a640a9d4
BH
377 }
378
bb85386d 379 //! Sets the socket to listen with a default listen backlog of 10 pending connections
a640a9d4
BH
380 void listen(unsigned int length=10)
381 {
382 if(::listen(d_socket,length)<0)
c52b8cb6 383 throw NetworkError("Setting socket to listen: "+stringerror());
a640a9d4
BH
384 }
385
386 //! Returns the internal file descriptor of the socket
387 int getHandle() const
388 {
389 return d_socket;
390 }
9c2ca55f
RG
391
392 int releaseHandle()
393 {
394 int ret = d_socket;
395 d_socket = -1;
396 return ret;
397 }
398
a640a9d4 399private:
86c3ac4a
RG
400 static const size_t s_buflen{4096};
401 std::string d_buffer;
d47832b0 402 int d_socket;
a640a9d4 403};