1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
3 #include "base/socket.hpp"
4 #include "base/objectlock.hpp"
5 #include "base/utility.hpp"
6 #include "base/exception.hpp"
7 #include "base/logger.hpp"
10 #include <boost/exception/errinfo_api_function.hpp>
11 #include <boost/exception/errinfo_errno.hpp>
12 #include <socketpair.h>
18 using namespace icinga;
21 * Constructor for the Socket class.
23 Socket::Socket(SOCKET fd)
29 * Destructor for the Socket class.
37 * Sets the file descriptor for this socket object.
39 * @param fd The file descriptor.
41 void Socket::SetFD(SOCKET fd)
43 if (fd != INVALID_SOCKET) {
45 /* mark the socket as close-on-exec */
46 Utility::SetCloExec(fd);
50 ObjectLock olock(this);
55 * Retrieves the file descriptor for this socket object.
57 * @returns The file descriptor.
59 SOCKET Socket::GetFD() const
61 ObjectLock olock(this);
71 ObjectLock olock(this);
73 if (m_FD != INVALID_SOCKET) {
75 m_FD = INVALID_SOCKET;
80 * Retrieves the last error that occurred for the socket.
82 * @returns An error code.
84 int Socket::GetError() const
87 socklen_t optlen = sizeof(opt);
89 int rc = getsockopt(GetFD(), SOL_SOCKET, SO_ERROR, (char *)&opt, &optlen);
98 * Formats a sockaddr in a human-readable way.
100 * @returns A pair of host and service.
102 String Socket::GetHumanReadableAddress(const std::pair<String, String>& socketDetails)
104 std::ostringstream s;
105 s << "[" << socketDetails.first << "]:" << socketDetails.second;
110 * Returns host and service as pair.
112 * @returns A pair with host and service.
114 std::pair<String, String> Socket::GetDetailsFromSockaddr(sockaddr *address, socklen_t len)
116 char host[NI_MAXHOST];
117 char service[NI_MAXSERV];
119 if (getnameinfo(address, len, host, sizeof(host), service,
120 sizeof(service), NI_NUMERICHOST | NI_NUMERICSERV) < 0) {
122 Log(LogCritical, "Socket")
123 << "getnameinfo() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
125 BOOST_THROW_EXCEPTION(socket_error()
126 << boost::errinfo_api_function("getnameinfo")
127 << boost::errinfo_errno(errno));
129 Log(LogCritical, "Socket")
130 << "getnameinfo() failed with error code " << WSAGetLastError() << ", \"" << Utility::FormatErrorNumber(WSAGetLastError()) << "\"";
132 BOOST_THROW_EXCEPTION(socket_error()
133 << boost::errinfo_api_function("getnameinfo")
134 << errinfo_win32_error(WSAGetLastError()));
138 return std::make_pair(host, service);
142 * Returns a pair describing the local host and service of the socket.
144 * @returns A pair describing the local host and service.
146 std::pair<String, String> Socket::GetClientAddressDetails()
148 boost::mutex::scoped_lock lock(m_SocketMutex);
150 sockaddr_storage sin;
151 socklen_t len = sizeof(sin);
153 if (getsockname(GetFD(), (sockaddr *)&sin, &len) < 0) {
155 Log(LogCritical, "Socket")
156 << "getsockname() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
158 BOOST_THROW_EXCEPTION(socket_error()
159 << boost::errinfo_api_function("getsockname")
160 << boost::errinfo_errno(errno));
162 Log(LogCritical, "Socket")
163 << "getsockname() failed with error code " << WSAGetLastError() << ", \"" << Utility::FormatErrorNumber(WSAGetLastError()) << "\"";
165 BOOST_THROW_EXCEPTION(socket_error()
166 << boost::errinfo_api_function("getsockname")
167 << errinfo_win32_error(WSAGetLastError()));
171 std::pair<String, String> details;
173 details = GetDetailsFromSockaddr((sockaddr *)&sin, len);
174 } catch (const std::exception&) {
182 * Returns a String describing the local address of the socket.
184 * @returns A String describing the local address.
186 String Socket::GetClientAddress()
188 return GetHumanReadableAddress(GetClientAddressDetails());
192 * Returns a pair describing the peer host and service of the socket.
194 * @returns A pair describing the peer host and service.
196 std::pair<String, String> Socket::GetPeerAddressDetails()
198 boost::mutex::scoped_lock lock(m_SocketMutex);
200 sockaddr_storage sin;
201 socklen_t len = sizeof(sin);
203 if (getpeername(GetFD(), (sockaddr *)&sin, &len) < 0) {
205 Log(LogCritical, "Socket")
206 << "getpeername() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
208 BOOST_THROW_EXCEPTION(socket_error()
209 << boost::errinfo_api_function("getpeername")
210 << boost::errinfo_errno(errno));
212 Log(LogCritical, "Socket")
213 << "getpeername() failed with error code " << WSAGetLastError() << ", \"" << Utility::FormatErrorNumber(WSAGetLastError()) << "\"";
215 BOOST_THROW_EXCEPTION(socket_error()
216 << boost::errinfo_api_function("getpeername")
217 << errinfo_win32_error(WSAGetLastError()));
221 std::pair<String, String> details;
223 details = GetDetailsFromSockaddr((sockaddr *)&sin, len);
224 } catch (const std::exception&) {
232 * Returns a String describing the peer address of the socket.
234 * @returns A String describing the peer address.
236 String Socket::GetPeerAddress()
238 return GetHumanReadableAddress(GetPeerAddressDetails());
242 * Starts listening for incoming client connections.
244 void Socket::Listen()
246 if (listen(GetFD(), SOMAXCONN) < 0) {
248 Log(LogCritical, "Socket")
249 << "listen() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
251 BOOST_THROW_EXCEPTION(socket_error()
252 << boost::errinfo_api_function("listen")
253 << boost::errinfo_errno(errno));
255 Log(LogCritical, "Socket")
256 << "listen() failed with error code " << WSAGetLastError() << ", \"" << Utility::FormatErrorNumber(WSAGetLastError()) << "\"";
258 BOOST_THROW_EXCEPTION(socket_error()
259 << boost::errinfo_api_function("listen")
260 << errinfo_win32_error(WSAGetLastError()));
266 * Sends data for the socket.
268 size_t Socket::Write(const void *buffer, size_t count)
273 rc = write(GetFD(), (const char *)buffer, count);
275 rc = send(GetFD(), (const char *)buffer, count, 0);
280 Log(LogCritical, "Socket")
281 << "send() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
283 BOOST_THROW_EXCEPTION(socket_error()
284 << boost::errinfo_api_function("send")
285 << boost::errinfo_errno(errno));
287 Log(LogCritical, "Socket")
288 << "send() failed with error code " << WSAGetLastError() << ", \"" << Utility::FormatErrorNumber(WSAGetLastError()) << "\"";
290 BOOST_THROW_EXCEPTION(socket_error()
291 << boost::errinfo_api_function("send")
292 << errinfo_win32_error(WSAGetLastError()));
300 * Processes data that can be written for this socket.
302 size_t Socket::Read(void *buffer, size_t count)
307 rc = read(GetFD(), (char *)buffer, count);
309 rc = recv(GetFD(), (char *)buffer, count, 0);
314 Log(LogCritical, "Socket")
315 << "recv() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
317 BOOST_THROW_EXCEPTION(socket_error()
318 << boost::errinfo_api_function("recv")
319 << boost::errinfo_errno(errno));
321 Log(LogCritical, "Socket")
322 << "recv() failed with error code " << WSAGetLastError() << ", \"" << Utility::FormatErrorNumber(WSAGetLastError()) << "\"";
324 BOOST_THROW_EXCEPTION(socket_error()
325 << boost::errinfo_api_function("recv")
326 << errinfo_win32_error(WSAGetLastError()));
334 * Accepts a new client and creates a new client object for it.
336 Socket::Ptr Socket::Accept()
338 sockaddr_storage addr;
339 socklen_t addrlen = sizeof(addr);
341 SOCKET fd = accept(GetFD(), (sockaddr *)&addr, &addrlen);
345 Log(LogCritical, "Socket")
346 << "accept() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
348 BOOST_THROW_EXCEPTION(socket_error()
349 << boost::errinfo_api_function("accept")
350 << boost::errinfo_errno(errno));
353 if (fd == INVALID_SOCKET) {
354 Log(LogCritical, "Socket")
355 << "accept() failed with error code " << WSAGetLastError() << ", \"" << Utility::FormatErrorNumber(WSAGetLastError()) << "\"";
357 BOOST_THROW_EXCEPTION(socket_error()
358 << boost::errinfo_api_function("accept")
359 << errinfo_win32_error(WSAGetLastError()));
363 return new Socket(fd);
366 bool Socket::Poll(bool read, bool write, struct timeval *timeout)
371 fd_set readfds, writefds, exceptfds;
375 FD_SET(GetFD(), &readfds);
379 FD_SET(GetFD(), &writefds);
382 FD_SET(GetFD(), &exceptfds);
384 rc = select(GetFD() + 1, &readfds, &writefds, &exceptfds, timeout);
387 Log(LogCritical, "Socket")
388 << "select() failed with error code " << WSAGetLastError() << ", \"" << Utility::FormatErrorNumber(WSAGetLastError()) << "\"";
390 BOOST_THROW_EXCEPTION(socket_error()
391 << boost::errinfo_api_function("select")
392 << errinfo_win32_error(WSAGetLastError()));
397 pfd.events = (read ? POLLIN : 0) | (write ? POLLOUT : 0);
400 rc = poll(&pfd, 1, timeout ? (timeout->tv_sec + 1000 + timeout->tv_usec / 1000) : -1);
403 Log(LogCritical, "Socket")
404 << "poll() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
406 BOOST_THROW_EXCEPTION(socket_error()
407 << boost::errinfo_api_function("poll")
408 << boost::errinfo_errno(errno));
415 void Socket::MakeNonBlocking()
418 Utility::SetNonBlockingSocket(GetFD());
420 Utility::SetNonBlocking(GetFD());
424 void Socket::SocketPair(SOCKET s[2])
426 if (dumb_socketpair(s, 0) < 0)
427 BOOST_THROW_EXCEPTION(socket_error()
428 << boost::errinfo_api_function("socketpair")
429 << boost::errinfo_errno(errno));