]>
Commit | Line | Data |
---|---|---|
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 | 43 | using ProtocolType = int; //!< Supported protocol types |
a640a9d4 BH |
44 | |
45 | //! Representation of a Socket and many of the Berkeley functions available | |
fdf05fd4 | 46 | class Socket : public boost::noncopyable |
a640a9d4 | 47 | { |
88d4fe87 | 48 | public: |
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 | 399 | private: |
86c3ac4a RG |
400 | static const size_t s_buflen{4096}; |
401 | std::string d_buffer; | |
d47832b0 | 402 | int d_socket; |
a640a9d4 | 403 | }; |