1 /******************************************************************************
3 * Copyright (C) 2012-2016 Icinga Development Team (https://www.icinga.org/) *
5 * This program is free software; you can redistribute it and/or *
6 * modify it under the terms of the GNU General Public License *
7 * as published by the Free Software Foundation; either version 2 *
8 * of the License, or (at your option) any later version. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the Free Software Foundation *
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
18 ******************************************************************************/
20 #include "base/socket.hpp"
21 #include "base/objectlock.hpp"
22 #include "base/utility.hpp"
23 #include "base/exception.hpp"
24 #include "base/logger.hpp"
27 #include <boost/exception/errinfo_api_function.hpp>
28 #include <boost/exception/errinfo_errno.hpp>
29 #include <socketpair.h>
35 using namespace icinga;
38 * Constructor for the Socket class.
41 : m_FD(INVALID_SOCKET)
45 * Constructor for the Socket class.
47 Socket::Socket(SOCKET fd)
48 : m_FD(INVALID_SOCKET)
54 * Destructor for the Socket class.
62 * Sets the file descriptor for this socket object.
64 * @param fd The file descriptor.
66 void Socket::SetFD(SOCKET fd)
68 if (fd != INVALID_SOCKET) {
70 /* mark the socket as close-on-exec */
71 Utility::SetCloExec(fd);
75 ObjectLock olock(this);
80 * Retrieves the file descriptor for this socket object.
82 * @returns The file descriptor.
84 SOCKET Socket::GetFD(void) const
86 ObjectLock olock(this);
94 void Socket::Close(void)
96 ObjectLock olock(this);
98 if (m_FD != INVALID_SOCKET) {
100 m_FD = INVALID_SOCKET;
105 * Retrieves the last error that occured for the socket.
107 * @returns An error code.
109 int Socket::GetError(void) const
112 socklen_t optlen = sizeof(opt);
114 int rc = getsockopt(GetFD(), SOL_SOCKET, SO_ERROR, (char *)&opt, &optlen);
123 * Formats a sockaddr in a human-readable way.
125 * @returns A String describing the sockaddr.
127 String Socket::GetAddressFromSockaddr(sockaddr *address, socklen_t len)
129 char host[NI_MAXHOST];
130 char service[NI_MAXSERV];
132 if (getnameinfo(address, len, host, sizeof(host), service,
133 sizeof(service), NI_NUMERICHOST | NI_NUMERICSERV) < 0) {
135 Log(LogCritical, "Socket")
136 << "getnameinfo() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
138 BOOST_THROW_EXCEPTION(socket_error()
139 << boost::errinfo_api_function("getnameinfo")
140 << boost::errinfo_errno(errno));
142 Log(LogCritical, "Socket")
143 << "getnameinfo() failed with error code " << WSAGetLastError() << ", \"" << Utility::FormatErrorNumber(WSAGetLastError()) << "\"";
145 BOOST_THROW_EXCEPTION(socket_error()
146 << boost::errinfo_api_function("getnameinfo")
147 << errinfo_win32_error(WSAGetLastError()));
151 std::ostringstream s;
152 s << "[" << host << "]:" << service;
157 * Returns a String describing the local address of the socket.
159 * @returns A String describing the local address.
161 String Socket::GetClientAddress(void)
163 boost::mutex::scoped_lock lock(m_SocketMutex);
165 sockaddr_storage sin;
166 socklen_t len = sizeof(sin);
168 if (getsockname(GetFD(), (sockaddr *)&sin, &len) < 0) {
170 Log(LogCritical, "Socket")
171 << "getsockname() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
173 BOOST_THROW_EXCEPTION(socket_error()
174 << boost::errinfo_api_function("getsockname")
175 << boost::errinfo_errno(errno));
177 Log(LogCritical, "Socket")
178 << "getsockname() failed with error code " << WSAGetLastError() << ", \"" << Utility::FormatErrorNumber(WSAGetLastError()) << "\"";
180 BOOST_THROW_EXCEPTION(socket_error()
181 << boost::errinfo_api_function("getsockname")
182 << errinfo_win32_error(WSAGetLastError()));
188 address = GetAddressFromSockaddr((sockaddr *)&sin, len);
189 } catch (const std::exception&) {
197 * Returns a String describing the peer address of the socket.
199 * @returns A String describing the peer address.
201 String Socket::GetPeerAddress(void)
203 boost::mutex::scoped_lock lock(m_SocketMutex);
205 sockaddr_storage sin;
206 socklen_t len = sizeof(sin);
208 if (getpeername(GetFD(), (sockaddr *)&sin, &len) < 0) {
210 Log(LogCritical, "Socket")
211 << "getpeername() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
213 BOOST_THROW_EXCEPTION(socket_error()
214 << boost::errinfo_api_function("getpeername")
215 << boost::errinfo_errno(errno));
217 Log(LogCritical, "Socket")
218 << "getpeername() failed with error code " << WSAGetLastError() << ", \"" << Utility::FormatErrorNumber(WSAGetLastError()) << "\"";
220 BOOST_THROW_EXCEPTION(socket_error()
221 << boost::errinfo_api_function("getpeername")
222 << errinfo_win32_error(WSAGetLastError()));
228 address = GetAddressFromSockaddr((sockaddr *)&sin, len);
229 } catch (const std::exception&) {
237 * Starts listening for incoming client connections.
239 void Socket::Listen(void)
241 if (listen(GetFD(), SOMAXCONN) < 0) {
243 Log(LogCritical, "Socket")
244 << "listen() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
246 BOOST_THROW_EXCEPTION(socket_error()
247 << boost::errinfo_api_function("listen")
248 << boost::errinfo_errno(errno));
250 Log(LogCritical, "Socket")
251 << "listen() failed with error code " << WSAGetLastError() << ", \"" << Utility::FormatErrorNumber(WSAGetLastError()) << "\"";
253 BOOST_THROW_EXCEPTION(socket_error()
254 << boost::errinfo_api_function("listen")
255 << errinfo_win32_error(WSAGetLastError()));
261 * Sends data for the socket.
263 size_t Socket::Write(const void *buffer, size_t count)
268 rc = write(GetFD(), (const char *)buffer, count);
270 rc = send(GetFD(), (const char *)buffer, count, 0);
275 Log(LogCritical, "Socket")
276 << "send() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
278 BOOST_THROW_EXCEPTION(socket_error()
279 << boost::errinfo_api_function("send")
280 << boost::errinfo_errno(errno));
282 Log(LogCritical, "Socket")
283 << "send() failed with error code " << WSAGetLastError() << ", \"" << Utility::FormatErrorNumber(WSAGetLastError()) << "\"";
285 BOOST_THROW_EXCEPTION(socket_error()
286 << boost::errinfo_api_function("send")
287 << errinfo_win32_error(WSAGetLastError()));
295 * Processes data that can be written for this socket.
297 size_t Socket::Read(void *buffer, size_t count)
302 rc = read(GetFD(), (char *)buffer, count);
304 rc = recv(GetFD(), (char *)buffer, count, 0);
309 Log(LogCritical, "Socket")
310 << "recv() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
312 BOOST_THROW_EXCEPTION(socket_error()
313 << boost::errinfo_api_function("recv")
314 << boost::errinfo_errno(errno));
316 Log(LogCritical, "Socket")
317 << "recv() failed with error code " << WSAGetLastError() << ", \"" << Utility::FormatErrorNumber(WSAGetLastError()) << "\"";
319 BOOST_THROW_EXCEPTION(socket_error()
320 << boost::errinfo_api_function("recv")
321 << errinfo_win32_error(WSAGetLastError()));
329 * Accepts a new client and creates a new client object for it.
331 Socket::Ptr Socket::Accept(void)
334 sockaddr_storage addr;
335 socklen_t addrlen = sizeof(addr);
337 fd = accept(GetFD(), (sockaddr *)&addr, &addrlen);
341 Log(LogCritical, "Socket")
342 << "accept() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
344 BOOST_THROW_EXCEPTION(socket_error()
345 << boost::errinfo_api_function("accept")
346 << boost::errinfo_errno(errno));
348 Log(LogCritical, "Socket")
349 << "accept() failed with error code " << WSAGetLastError() << ", \"" << Utility::FormatErrorNumber(WSAGetLastError()) << "\"";
351 BOOST_THROW_EXCEPTION(socket_error()
352 << boost::errinfo_api_function("accept")
353 << errinfo_win32_error(WSAGetLastError()));
357 return new Socket(fd);
360 bool Socket::Poll(bool read, bool write, struct timeval *timeout)
365 fd_set readfds, writefds, exceptfds;
369 FD_SET(GetFD(), &readfds);
373 FD_SET(GetFD(), &writefds);
376 FD_SET(GetFD(), &exceptfds);
378 rc = select(GetFD() + 1, &readfds, &writefds, &exceptfds, timeout);
381 Log(LogCritical, "Socket")
382 << "select() failed with error code " << WSAGetLastError() << ", \"" << Utility::FormatErrorNumber(WSAGetLastError()) << "\"";
384 BOOST_THROW_EXCEPTION(socket_error()
385 << boost::errinfo_api_function("select")
386 << errinfo_win32_error(WSAGetLastError()));
391 pfd.events = (read ? POLLIN : 0) | (write ? POLLOUT : 0);
394 rc = poll(&pfd, 1, timeout ? (timeout->tv_sec + 1000 + timeout->tv_usec / 1000) : -1);
397 Log(LogCritical, "Socket")
398 << "poll() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
400 BOOST_THROW_EXCEPTION(socket_error()
401 << boost::errinfo_api_function("poll")
402 << boost::errinfo_errno(errno));
409 void Socket::MakeNonBlocking(void)
412 Utility::SetNonBlockingSocket(GetFD());
414 Utility::SetNonBlocking(GetFD());
418 void Socket::SocketPair(SOCKET s[2])
420 if (dumb_socketpair(s, 0) < 0)
421 BOOST_THROW_EXCEPTION(socket_error()
422 << boost::errinfo_api_function("socketpair")
423 << boost::errinfo_errno(errno));