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