]>
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" |
a640a9d4 BH |
41 | |
42 | ||
a640a9d4 BH |
43 | typedef int ProtocolType; //!< Supported protocol types |
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 | ||
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 | 386 | private: |
86c3ac4a RG |
387 | static const size_t s_buflen{4096}; |
388 | std::string d_buffer; | |
d47832b0 | 389 | int d_socket; |
a640a9d4 | 390 | }; |