/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/socket.hpp" #include "base/objectlock.hpp" #include "base/utility.hpp" #include "base/exception.hpp" #include "base/logger.hpp" #include #include #include #include #include #ifndef _WIN32 # include #endif /* _WIN32 */ using namespace icinga; /** * Constructor for the Socket class. */ Socket::Socket(SOCKET fd) { SetFD(fd); } /** * Destructor for the Socket class. */ Socket::~Socket() { Close(); } /** * Sets the file descriptor for this socket object. * * @param fd The file descriptor. */ void Socket::SetFD(SOCKET fd) { if (fd != INVALID_SOCKET) { #ifndef _WIN32 /* mark the socket as close-on-exec */ Utility::SetCloExec(fd); #endif /* _WIN32 */ } ObjectLock olock(this); m_FD = fd; } /** * Retrieves the file descriptor for this socket object. * * @returns The file descriptor. */ SOCKET Socket::GetFD() const { ObjectLock olock(this); return m_FD; } /** * Closes the socket. */ void Socket::Close() { ObjectLock olock(this); if (m_FD != INVALID_SOCKET) { closesocket(m_FD); m_FD = INVALID_SOCKET; } } /** * Retrieves the last error that occurred for the socket. * * @returns An error code. */ int Socket::GetError() const { int opt; socklen_t optlen = sizeof(opt); int rc = getsockopt(GetFD(), SOL_SOCKET, SO_ERROR, (char *)&opt, &optlen); if (rc >= 0) return opt; return 0; } /** * Formats a sockaddr in a human-readable way. * * @returns A pair of host and service. */ String Socket::GetHumanReadableAddress(const std::pair& socketDetails) { std::ostringstream s; s << "[" << socketDetails.first << "]:" << socketDetails.second; return s.str(); } /** * Returns host and service as pair. * * @returns A pair with host and service. */ std::pair Socket::GetDetailsFromSockaddr(sockaddr *address, socklen_t len) { char host[NI_MAXHOST]; char service[NI_MAXSERV]; if (getnameinfo(address, len, host, sizeof(host), service, sizeof(service), NI_NUMERICHOST | NI_NUMERICSERV) < 0) { #ifndef _WIN32 Log(LogCritical, "Socket") << "getnameinfo() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\""; BOOST_THROW_EXCEPTION(socket_error() << boost::errinfo_api_function("getnameinfo") << boost::errinfo_errno(errno)); #else /* _WIN32 */ Log(LogCritical, "Socket") << "getnameinfo() failed with error code " << WSAGetLastError() << ", \"" << Utility::FormatErrorNumber(WSAGetLastError()) << "\""; BOOST_THROW_EXCEPTION(socket_error() << boost::errinfo_api_function("getnameinfo") << errinfo_win32_error(WSAGetLastError())); #endif /* _WIN32 */ } return std::make_pair(host, service); } /** * Returns a pair describing the local host and service of the socket. * * @returns A pair describing the local host and service. */ std::pair Socket::GetClientAddressDetails() { std::unique_lock lock(m_SocketMutex); sockaddr_storage sin; socklen_t len = sizeof(sin); if (getsockname(GetFD(), (sockaddr *)&sin, &len) < 0) { #ifndef _WIN32 Log(LogCritical, "Socket") << "getsockname() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\""; BOOST_THROW_EXCEPTION(socket_error() << boost::errinfo_api_function("getsockname") << boost::errinfo_errno(errno)); #else /* _WIN32 */ Log(LogCritical, "Socket") << "getsockname() failed with error code " << WSAGetLastError() << ", \"" << Utility::FormatErrorNumber(WSAGetLastError()) << "\""; BOOST_THROW_EXCEPTION(socket_error() << boost::errinfo_api_function("getsockname") << errinfo_win32_error(WSAGetLastError())); #endif /* _WIN32 */ } std::pair details; try { details = GetDetailsFromSockaddr((sockaddr *)&sin, len); } catch (const std::exception&) { /* already logged */ } return details; } /** * Returns a String describing the local address of the socket. * * @returns A String describing the local address. */ String Socket::GetClientAddress() { return GetHumanReadableAddress(GetClientAddressDetails()); } /** * Returns a pair describing the peer host and service of the socket. * * @returns A pair describing the peer host and service. */ std::pair Socket::GetPeerAddressDetails() { std::unique_lock lock(m_SocketMutex); sockaddr_storage sin; socklen_t len = sizeof(sin); if (getpeername(GetFD(), (sockaddr *)&sin, &len) < 0) { #ifndef _WIN32 Log(LogCritical, "Socket") << "getpeername() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\""; BOOST_THROW_EXCEPTION(socket_error() << boost::errinfo_api_function("getpeername") << boost::errinfo_errno(errno)); #else /* _WIN32 */ Log(LogCritical, "Socket") << "getpeername() failed with error code " << WSAGetLastError() << ", \"" << Utility::FormatErrorNumber(WSAGetLastError()) << "\""; BOOST_THROW_EXCEPTION(socket_error() << boost::errinfo_api_function("getpeername") << errinfo_win32_error(WSAGetLastError())); #endif /* _WIN32 */ } std::pair details; try { details = GetDetailsFromSockaddr((sockaddr *)&sin, len); } catch (const std::exception&) { /* already logged */ } return details; } /** * Returns a String describing the peer address of the socket. * * @returns A String describing the peer address. */ String Socket::GetPeerAddress() { return GetHumanReadableAddress(GetPeerAddressDetails()); } /** * Starts listening for incoming client connections. */ void Socket::Listen() { if (listen(GetFD(), SOMAXCONN) < 0) { #ifndef _WIN32 Log(LogCritical, "Socket") << "listen() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\""; BOOST_THROW_EXCEPTION(socket_error() << boost::errinfo_api_function("listen") << boost::errinfo_errno(errno)); #else /* _WIN32 */ Log(LogCritical, "Socket") << "listen() failed with error code " << WSAGetLastError() << ", \"" << Utility::FormatErrorNumber(WSAGetLastError()) << "\""; BOOST_THROW_EXCEPTION(socket_error() << boost::errinfo_api_function("listen") << errinfo_win32_error(WSAGetLastError())); #endif /* _WIN32 */ } } /** * Sends data for the socket. */ size_t Socket::Write(const void *buffer, size_t count) { int rc; #ifndef _WIN32 rc = write(GetFD(), (const char *)buffer, count); #else /* _WIN32 */ rc = send(GetFD(), (const char *)buffer, count, 0); #endif /* _WIN32 */ if (rc < 0) { #ifndef _WIN32 Log(LogCritical, "Socket") << "send() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\""; BOOST_THROW_EXCEPTION(socket_error() << boost::errinfo_api_function("send") << boost::errinfo_errno(errno)); #else /* _WIN32 */ Log(LogCritical, "Socket") << "send() failed with error code " << WSAGetLastError() << ", \"" << Utility::FormatErrorNumber(WSAGetLastError()) << "\""; BOOST_THROW_EXCEPTION(socket_error() << boost::errinfo_api_function("send") << errinfo_win32_error(WSAGetLastError())); #endif /* _WIN32 */ } return rc; } /** * Processes data that can be written for this socket. */ size_t Socket::Read(void *buffer, size_t count) { int rc; #ifndef _WIN32 rc = read(GetFD(), (char *)buffer, count); #else /* _WIN32 */ rc = recv(GetFD(), (char *)buffer, count, 0); #endif /* _WIN32 */ if (rc < 0) { #ifndef _WIN32 Log(LogCritical, "Socket") << "recv() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\""; BOOST_THROW_EXCEPTION(socket_error() << boost::errinfo_api_function("recv") << boost::errinfo_errno(errno)); #else /* _WIN32 */ Log(LogCritical, "Socket") << "recv() failed with error code " << WSAGetLastError() << ", \"" << Utility::FormatErrorNumber(WSAGetLastError()) << "\""; BOOST_THROW_EXCEPTION(socket_error() << boost::errinfo_api_function("recv") << errinfo_win32_error(WSAGetLastError())); #endif /* _WIN32 */ } return rc; } /** * Accepts a new client and creates a new client object for it. */ Socket::Ptr Socket::Accept() { sockaddr_storage addr; socklen_t addrlen = sizeof(addr); SOCKET fd = accept(GetFD(), (sockaddr *)&addr, &addrlen); #ifndef _WIN32 if (fd < 0) { Log(LogCritical, "Socket") << "accept() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\""; BOOST_THROW_EXCEPTION(socket_error() << boost::errinfo_api_function("accept") << boost::errinfo_errno(errno)); } #else /* _WIN32 */ if (fd == INVALID_SOCKET) { Log(LogCritical, "Socket") << "accept() failed with error code " << WSAGetLastError() << ", \"" << Utility::FormatErrorNumber(WSAGetLastError()) << "\""; BOOST_THROW_EXCEPTION(socket_error() << boost::errinfo_api_function("accept") << errinfo_win32_error(WSAGetLastError())); } #endif /* _WIN32 */ return new Socket(fd); } bool Socket::Poll(bool read, bool write, struct timeval *timeout) { int rc; #ifdef _WIN32 fd_set readfds, writefds, exceptfds; FD_ZERO(&readfds); if (read) FD_SET(GetFD(), &readfds); FD_ZERO(&writefds); if (write) FD_SET(GetFD(), &writefds); FD_ZERO(&exceptfds); FD_SET(GetFD(), &exceptfds); rc = select(GetFD() + 1, &readfds, &writefds, &exceptfds, timeout); if (rc < 0) { Log(LogCritical, "Socket") << "select() failed with error code " << WSAGetLastError() << ", \"" << Utility::FormatErrorNumber(WSAGetLastError()) << "\""; BOOST_THROW_EXCEPTION(socket_error() << boost::errinfo_api_function("select") << errinfo_win32_error(WSAGetLastError())); } #else /* _WIN32 */ pollfd pfd; pfd.fd = GetFD(); pfd.events = (read ? POLLIN : 0) | (write ? POLLOUT : 0); pfd.revents = 0; rc = poll(&pfd, 1, timeout ? (timeout->tv_sec + 1000 + timeout->tv_usec / 1000) : -1); if (rc < 0) { Log(LogCritical, "Socket") << "poll() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\""; BOOST_THROW_EXCEPTION(socket_error() << boost::errinfo_api_function("poll") << boost::errinfo_errno(errno)); } #endif /* _WIN32 */ return (rc != 0); } void Socket::MakeNonBlocking() { #ifdef _WIN32 Utility::SetNonBlockingSocket(GetFD()); #else /* _WIN32 */ Utility::SetNonBlocking(GetFD()); #endif /* _WIN32 */ } void Socket::SocketPair(SOCKET s[2]) { if (dumb_socketpair(s, 0) < 0) BOOST_THROW_EXCEPTION(socket_error() << boost::errinfo_api_function("socketpair") << boost::errinfo_errno(errno)); }