2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
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.
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.
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.
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.
28 #include <sys/types.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>
38 #include <boost/utility.hpp>
40 #include "namespaces.hh"
41 #include "noinitvector.hh"
43 using ProtocolType = int; //!< Supported protocol types
45 //! Representation of a Socket and many of the Berkeley functions available
46 class Socket : public boost::noncopyable
49 Socket(int fd): d_socket(fd)
53 //! Construct a socket of specified address family and socket type.
54 Socket(int af, int st, ProtocolType pt=0)
56 if((d_socket=socket(af, st, pt))<0)
57 throw NetworkError(stringerror());
58 setCloseOnExec(d_socket);
61 Socket(Socket&& rhs) noexcept :
62 d_buffer(std::move(rhs.d_buffer)), d_socket(rhs.d_socket)
67 Socket& operator=(Socket&& rhs) noexcept
72 d_socket = rhs.d_socket;
74 d_buffer = std::move(rhs.d_buffer);
82 closesocket(d_socket);
85 catch(const PDNSException& e) {
89 //! If the socket is capable of doing so, this function will wait for a connection
90 std::unique_ptr<Socket> accept()
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);
100 throw NetworkError("Accepting a connection: "+stringerror());
103 return std::make_unique<Socket>(s);
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);
112 //! Check remote address against netmaskgroup ng
113 bool acl(const NetmaskGroup &ng)
116 if (getRemote(remote))
117 return ng.match(remote);
122 //! Set the socket to non-blocking
123 void setNonBlocking()
125 ::setNonBlocking(d_socket);
128 //! Set the socket to blocking
131 ::setBlocking(d_socket);
137 ::setReuseAddr(d_socket);
138 } catch (const PDNSException &e) {
139 throw NetworkError(e.reason);
143 void setFastOpenConnect()
145 #ifdef TCP_FASTOPEN_CONNECT
147 if (setsockopt(d_socket, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, &on, sizeof(on)) < 0) {
148 throw NetworkError("While setting TCP_FASTOPEN_CONNECT: " + stringerror());
151 throw NetworkError("While setting TCP_FASTOPEN_CONNECT: not compiled in");
155 //! Bind the socket to a specified endpoint
156 void bind(const ComboAddress &local, bool reuseaddr=true)
159 if(reuseaddr && setsockopt(d_socket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&tmp), sizeof tmp)<0)
160 throw NetworkError("Setsockopt failed: "+stringerror());
162 if(::bind(d_socket, reinterpret_cast<const struct sockaddr *>(&local), local.getSocklen())<0)
163 throw NetworkError("While binding: "+stringerror());
166 //! Connect the socket to a specified endpoint
167 void connect(const ComboAddress &ep, int timeout=0)
169 SConnectWithTimeout(d_socket, ep, timeval{timeout,0});
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)
179 socklen_t remlen = sizeof(remote);
180 if (dgram.size() < s_buflen) {
181 dgram.resize(s_buflen);
183 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
184 auto bytes = recvfrom(d_socket, dgram.data(), dgram.size(), 0, reinterpret_cast<sockaddr *>(&remote) , &remlen);
186 throw NetworkError("After recvfrom: " + stringerror());
188 dgram.resize(static_cast<size_t>(bytes));
191 bool recvFromAsync(PacketBuffer& dgram, ComboAddress& remote)
193 socklen_t remlen = sizeof(remote);
194 if (dgram.size() < s_buflen) {
195 dgram.resize(s_buflen);
197 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
198 auto bytes = recvfrom(d_socket, dgram.data(), dgram.size(), 0, reinterpret_cast<sockaddr *>(&remote), &remlen);
200 if (errno != EAGAIN) {
201 throw NetworkError("After async recvfrom: " + stringerror());
207 dgram.resize(static_cast<size_t>(bytes));
211 //! For datagram sockets, send a datagram to a destination
212 void sendTo(const char* msg, size_t len, const ComboAddress& remote)
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());
220 //! For connected datagram sockets, send a datagram
221 void send(const std::string& msg)
223 if (::send(d_socket, msg.data(), msg.size(), 0) < 0) {
224 throw NetworkError("After send: "+stringerror());
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)
234 sendTo(dgram.data(), dgram.length(), remote);
238 //! Write this data to the socket, taking care that all bytes are written out
239 void writen(const string &data)
244 size_t toWrite=data.length();
246 const char *ptr=data.c_str();
249 res=::send(d_socket, ptr, toWrite, 0);
251 throw NetworkError("Writing to a socket: "+stringerror());
253 throw NetworkError("EOF on socket");
254 toWrite -= static_cast<size_t>(res);
255 ptr += static_cast<size_t>(res);
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
265 size_t tryWrite(const char *ptr, size_t toWrite)
268 res=::send(d_socket,ptr,toWrite,0);
270 throw NetworkError("EOF on writing to a socket");
278 throw NetworkError("Writing to a socket: "+stringerror());
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)
286 res=::send(d_socket,ptr,toWrite,0);
288 throw NetworkError("Writing to a socket: "+stringerror());
293 void writenWithTimeout(const void *buffer, size_t n, int timeout)
296 const char *ptr = reinterpret_cast<const char*>(buffer);
299 ret=::write(d_socket, ptr, bytes);
301 if(errno == EAGAIN) {
302 ret=waitForRWData(d_socket, false, timeout, 0);
304 throw NetworkError("Waiting for data write");
306 throw NetworkError("Timeout writing data");
310 throw NetworkError("Writing data: "+stringerror());
313 throw NetworkError("Did not fulfill TCP write due to EOF");
316 ptr += static_cast<size_t>(ret);
317 bytes -= static_cast<size_t>(ret);
321 //! reads one character from the socket
326 ssize_t res=::recv(d_socket,&c,1,0);
332 void getline(string &data)
336 while((c=getChar())!=-1) {
343 //! Reads a block of data from the socket to a string
344 void read(string &data)
346 d_buffer.resize(s_buflen);
347 ssize_t res=::recv(d_socket, &d_buffer[0], s_buflen, 0);
349 throw NetworkError("Reading from a socket: "+stringerror());
350 data.assign(d_buffer, 0, static_cast<size_t>(res));
353 //! Reads a block of data from the socket to a block of memory
354 size_t read(char *buffer, size_t bytes)
356 ssize_t res=::recv(d_socket, buffer, bytes, 0);
358 throw NetworkError("Reading from a socket: "+stringerror());
359 return static_cast<size_t>(res);
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
367 ssize_t readWithTimeout(char* buffer, size_t n, int timeout)
369 int err = waitForRWData(d_socket, true, timeout, 0);
372 throw NetworkError("timeout reading");
374 throw NetworkError("nonblocking read failed: "+stringerror());
376 return read(buffer, n);
379 //! Sets the socket to listen with a default listen backlog of 10 pending connections
380 void listen(unsigned int length=10)
382 if(::listen(d_socket,length)<0)
383 throw NetworkError("Setting socket to listen: "+stringerror());
386 //! Returns the internal file descriptor of the socket
387 int getHandle() const
400 static const size_t s_buflen{4096};
401 std::string d_buffer;