]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/sstuff.hh
dnsdist: Fix DNS over plain HTTP broken by `reloadAllCertificates()`
[thirdparty/pdns.git] / 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 <boost/utility.hpp>
39 #include <csignal>
40 #include "namespaces.hh"
41 #include "noinitvector.hh"
42
43 using ProtocolType = int; //!< Supported protocol types
44
45 //! Representation of a Socket and many of the Berkeley functions available
46 class Socket : public boost::noncopyable
47 {
48 public:
49 Socket(int fd): d_socket(fd)
50 {
51 }
52
53 //! Construct a socket of specified address family and socket type.
54 Socket(int af, int st, ProtocolType pt=0)
55 {
56 if((d_socket=socket(af, st, pt))<0)
57 throw NetworkError(stringerror());
58 setCloseOnExec(d_socket);
59 }
60
61 Socket(Socket&& rhs) noexcept :
62 d_buffer(std::move(rhs.d_buffer)), d_socket(rhs.d_socket)
63 {
64 rhs.d_socket = -1;
65 }
66
67 Socket& operator=(Socket&& rhs) noexcept
68 {
69 if (d_socket != -1) {
70 close(d_socket);
71 }
72 d_socket = rhs.d_socket;
73 rhs.d_socket = -1;
74 d_buffer = std::move(rhs.d_buffer);
75 return *this;
76 }
77
78 ~Socket()
79 {
80 try {
81 if (d_socket != -1) {
82 closesocket(d_socket);
83 }
84 }
85 catch(const PDNSException& e) {
86 }
87 }
88
89 //! If the socket is capable of doing so, this function will wait for a connection
90 std::unique_ptr<Socket> accept()
91 {
92 struct sockaddr_in remote;
93 socklen_t remlen=sizeof(remote);
94 memset(&remote, 0, sizeof(remote));
95 int s=::accept(d_socket, reinterpret_cast<sockaddr *>(&remote), &remlen);
96 if(s<0) {
97 if(errno==EAGAIN)
98 return nullptr;
99
100 throw NetworkError("Accepting a connection: "+stringerror());
101 }
102
103 return std::make_unique<Socket>(s);
104 }
105
106 //! Get remote address
107 bool getRemote(ComboAddress &remote) {
108 socklen_t remotelen=sizeof(remote);
109 return (getpeername(d_socket, reinterpret_cast<struct sockaddr *>(&remote), &remotelen) >= 0);
110 }
111
112 //! Check remote address against netmaskgroup ng
113 bool acl(const NetmaskGroup &ng)
114 {
115 ComboAddress remote;
116 if (getRemote(remote))
117 return ng.match(remote);
118
119 return false;
120 }
121
122 //! Set the socket to non-blocking
123 void setNonBlocking()
124 {
125 ::setNonBlocking(d_socket);
126 }
127
128 //! Set the socket to blocking
129 void setBlocking()
130 {
131 ::setBlocking(d_socket);
132 }
133
134 void setReuseAddr()
135 {
136 try {
137 ::setReuseAddr(d_socket);
138 } catch (const PDNSException &e) {
139 throw NetworkError(e.reason);
140 }
141 }
142
143 void setFastOpenConnect()
144 {
145 #ifdef TCP_FASTOPEN_CONNECT
146 int on = 1;
147 if (setsockopt(d_socket, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, &on, sizeof(on)) < 0) {
148 throw NetworkError("While setting TCP_FASTOPEN_CONNECT: " + stringerror());
149 }
150 #else
151 throw NetworkError("While setting TCP_FASTOPEN_CONNECT: not compiled in");
152 #endif
153 }
154
155 //! Bind the socket to a specified endpoint
156 void bind(const ComboAddress &local, bool reuseaddr=true)
157 {
158 int tmp=1;
159 if(reuseaddr && setsockopt(d_socket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&tmp), sizeof tmp)<0)
160 throw NetworkError("Setsockopt failed: "+stringerror());
161
162 if(::bind(d_socket, reinterpret_cast<const struct sockaddr *>(&local), local.getSocklen())<0)
163 throw NetworkError("While binding: "+stringerror());
164 }
165
166 //! Connect the socket to a specified endpoint
167 void connect(const ComboAddress &ep, int timeout=0)
168 {
169 SConnectWithTimeout(d_socket, ep, timeval{timeout,0});
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 */
177 void recvFrom(string &dgram, ComboAddress& remote)
178 {
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));
189 }
190
191 bool recvFromAsync(PacketBuffer& dgram, ComboAddress& remote)
192 {
193 socklen_t remlen = sizeof(remote);
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);
199 if (bytes < 0) {
200 if (errno != EAGAIN) {
201 throw NetworkError("After async recvfrom: " + stringerror());
202 }
203 else {
204 return false;
205 }
206 }
207 dgram.resize(static_cast<size_t>(bytes));
208 return true;
209 }
210
211 //! For datagram sockets, send a datagram to a destination
212 void sendTo(const char* msg, size_t len, const ComboAddress& remote)
213 {
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 }
218 }
219
220 //! For connected datagram sockets, send a datagram
221 void send(const std::string& msg)
222 {
223 if (::send(d_socket, msg.data(), msg.size(), 0) < 0) {
224 throw NetworkError("After send: "+stringerror());
225 }
226 }
227
228
229 /** For datagram sockets, send a datagram to a destination
230 \param dgram The datagram
231 \param remote The intended destination of the datagram */
232 void sendTo(const string& dgram, const ComboAddress& remote)
233 {
234 sendTo(dgram.data(), dgram.length(), remote);
235 }
236
237
238 //! Write this data to the socket, taking care that all bytes are written out
239 void writen(const string &data)
240 {
241 if(data.empty())
242 return;
243
244 size_t toWrite=data.length();
245 ssize_t res;
246 const char *ptr=data.c_str();
247
248 do {
249 res=::send(d_socket, ptr, toWrite, 0);
250 if(res<0)
251 throw NetworkError("Writing to a socket: "+stringerror());
252 if(!res)
253 throw NetworkError("EOF on socket");
254 toWrite -= static_cast<size_t>(res);
255 ptr += static_cast<size_t>(res);
256 } while(toWrite);
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 */
265 size_t tryWrite(const char *ptr, size_t toWrite)
266 {
267 ssize_t res;
268 res=::send(d_socket,ptr,toWrite,0);
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;
277
278 throw NetworkError("Writing to a socket: "+stringerror());
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 */
283 size_t write(const char *ptr, size_t toWrite)
284 {
285 ssize_t res;
286 res=::send(d_socket,ptr,toWrite,0);
287 if(res<0) {
288 throw NetworkError("Writing to a socket: "+stringerror());
289 }
290 return res;
291 }
292
293 void writenWithTimeout(const void *buffer, size_t n, int timeout)
294 {
295 size_t bytes=n;
296 const char *ptr = reinterpret_cast<const char*>(buffer);
297 ssize_t ret;
298 while(bytes) {
299 ret=::write(d_socket, ptr, bytes);
300 if(ret < 0) {
301 if(errno == EAGAIN) {
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
316 ptr += static_cast<size_t>(ret);
317 bytes -= static_cast<size_t>(ret);
318 }
319 }
320
321 //! reads one character from the socket
322 int getChar()
323 {
324 char c;
325
326 ssize_t res=::recv(d_socket,&c,1,0);
327 if(res)
328 return c;
329 return -1;
330 }
331
332 void getline(string &data)
333 {
334 data="";
335 int c;
336 while((c=getChar())!=-1) {
337 data+=(char)c;
338 if(c=='\n')
339 break;
340 }
341 }
342
343 //! Reads a block of data from the socket to a string
344 void read(string &data)
345 {
346 d_buffer.resize(s_buflen);
347 ssize_t res=::recv(d_socket, &d_buffer[0], s_buflen, 0);
348 if(res<0)
349 throw NetworkError("Reading from a socket: "+stringerror());
350 data.assign(d_buffer, 0, static_cast<size_t>(res));
351 }
352
353 //! Reads a block of data from the socket to a block of memory
354 size_t read(char *buffer, size_t bytes)
355 {
356 ssize_t res=::recv(d_socket, buffer, bytes, 0);
357 if(res<0)
358 throw NetworkError("Reading from a socket: "+stringerror());
359 return static_cast<size_t>(res);
360 }
361
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 */
367 ssize_t readWithTimeout(char* buffer, size_t n, int timeout)
368 {
369 int err = waitForRWData(d_socket, true, timeout, 0);
370
371 if(err == 0)
372 throw NetworkError("timeout reading");
373 if(err < 0)
374 throw NetworkError("nonblocking read failed: "+stringerror());
375
376 return read(buffer, n);
377 }
378
379 //! Sets the socket to listen with a default listen backlog of 10 pending connections
380 void listen(unsigned int length=10)
381 {
382 if(::listen(d_socket,length)<0)
383 throw NetworkError("Setting socket to listen: "+stringerror());
384 }
385
386 //! Returns the internal file descriptor of the socket
387 int getHandle() const
388 {
389 return d_socket;
390 }
391
392 int releaseHandle()
393 {
394 int ret = d_socket;
395 d_socket = -1;
396 return ret;
397 }
398
399 private:
400 static const size_t s_buflen{4096};
401 std::string d_buffer;
402 int d_socket;
403 };