summaryrefslogtreecommitdiffstats
path: root/lib/base/socket.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/base/socket.cpp')
-rw-r--r--lib/base/socket.cpp430
1 files changed, 430 insertions, 0 deletions
diff --git a/lib/base/socket.cpp b/lib/base/socket.cpp
new file mode 100644
index 0000000..4c967de
--- /dev/null
+++ b/lib/base/socket.cpp
@@ -0,0 +1,430 @@
+/* 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 <sstream>
+#include <iostream>
+#include <boost/exception/errinfo_api_function.hpp>
+#include <boost/exception/errinfo_errno.hpp>
+#include <socketpair.h>
+
+#ifndef _WIN32
+# include <poll.h>
+#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<String, String>& 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<String, String> 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<String, String> Socket::GetClientAddressDetails()
+{
+ std::unique_lock<std::mutex> 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<String, String> 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<String, String> Socket::GetPeerAddressDetails()
+{
+ std::unique_lock<std::mutex> 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<String, String> 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));
+}