IPMI Socket Class to encapsulate the socket operations

This class represents the details regarding an IPMI client like IP
address, port number and the file descriptor associated with it. The
operations provided are reading the UDP packet, writing the packet.

Change-Id: I58cde0edb04fd2fbedd3068de83379eebea8508d
Signed-off-by: Tom Joseph <tomjoseph@in.ibm.com>
diff --git a/socket_channel.cpp b/socket_channel.cpp
new file mode 100644
index 0000000..4a6d827
--- /dev/null
+++ b/socket_channel.cpp
@@ -0,0 +1,156 @@
+#include "socket_channel.hpp"
+
+#include <errno.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <string>
+
+namespace udpsocket
+{
+
+std::string Channel::getRemoteAddress() const
+{
+    char tmp[INET_ADDRSTRLEN] = { 0 };
+    inet_ntop(AF_INET, &address.inAddr.sin_addr, tmp, sizeof(tmp));
+    return std::string(tmp);
+}
+
+std::tuple<int, buffer> Channel::read()
+{
+    int rc = 0;
+    int readSize = 0;
+    ssize_t readDataLen = 0;
+    buffer outBuffer(0);
+
+    if (ioctl(sockfd, FIONREAD, &readSize) < 0)
+    {
+        std::cerr << "E> Channel::Read : ioctl failed with errno = " << errno;
+        rc = -errno;
+        return std::make_tuple(rc, std::move(outBuffer));
+    }
+
+    outBuffer.resize(readSize);
+    auto bufferSize = outBuffer.size();
+    auto outputPtr = outBuffer.data();
+
+    address.addrSize = sizeof(address.inAddr);
+
+    do
+    {
+        readDataLen = recvfrom(sockfd,               // File Descriptor
+                               outputPtr ,           // Buffer
+                               bufferSize,           // Bytes requested
+                               0,                    // Flags
+                               &address.sockAddr,    // Address
+                               &address.addrSize);   // Address Length
+
+        if (readDataLen > 0) // Data read from the socket
+        {
+            std::cout << "I> Channel::Read : DataIn Fd[" << sockfd << "] Req["
+                      << bufferSize << "] Recv[" << readDataLen << "]\n";
+        }
+        else if (readDataLen == 0) // Peer has performed an orderly shutdown
+        {
+            std::cerr << "E> Channel::Read : Connection Closed Fd[" << sockfd
+                      << "]\n";
+            outBuffer.resize(0);
+            rc = -1;
+        }
+        else if (readDataLen < 0) // Error
+        {
+            rc = -errno;
+            std::cerr << "E> Channel::Read : Receive Error Fd[" << sockfd << "]"
+                      << "errno = " << rc << "\n";
+            outBuffer.resize(0);
+        }
+    }
+    while ((readDataLen < 0) && (-(rc) == EINTR));
+
+    // Resize the vector to the actual data read from the socket
+    outBuffer.resize(readDataLen);
+    return std::make_tuple(rc, std::move(outBuffer));
+}
+
+int Channel::write(buffer& inBuffer)
+{
+    int rc = 0;
+    auto outputPtr = inBuffer.data();
+    auto bufferSize = inBuffer.size();
+    auto spuriousWakeup = false;
+    ssize_t writeDataLen = 0;
+    timeval varTimeout = timeout;
+
+    fd_set writeSet;
+    FD_ZERO(&writeSet);
+    FD_SET(sockfd, &writeSet);
+
+    do
+    {
+        spuriousWakeup = false;
+
+        rc = select((sockfd + 1), nullptr, &writeSet, NULL, &varTimeout);
+
+        if (rc > 0)
+        {
+            if (FD_ISSET(sockfd, &writeSet))
+            {
+                address.addrSize = sizeof(address.inAddr);
+                do
+                {
+                    writeDataLen = sendto(sockfd,           // File Descriptor
+                                          outputPtr,        // Message
+                                          bufferSize,       // Length
+                                          MSG_NOSIGNAL,     // Flags
+                                          &address.sockAddr,// Destination Address
+                                          address.addrSize);// Address Length
+
+                    if (writeDataLen < 0)
+                    {
+                        rc = -errno;
+                        std::cerr << "Channel::Write: Write failed with errno:"
+                                  << rc << "\n";
+                    }
+                    else if (static_cast<size_t>(writeDataLen) < bufferSize)
+                    {
+                        rc = -1;
+                        std::cerr << "Channel::Write: Complete data not written"
+                                  " to the socket\n";
+                    }
+                }
+                while ((writeDataLen < 0) && (-(rc) == EINTR));
+            }
+            else
+            {
+                // Spurious wake up
+                std::cerr << "E> Spurious wake up on select (writeset)\n";
+                spuriousWakeup = true;
+            }
+        }
+        else
+        {
+            if (rc == 0)
+            {
+                // Timed out
+                rc = -1;
+                std::cerr << "E> We timed out on select call (writeset)\n";
+            }
+            else
+            {
+                // Error
+                rc  = -errno;
+                std::cerr << "E> select call (writeset) had an error : "
+                          << rc << "\n";
+            }
+
+        }
+    }
+    while (spuriousWakeup);
+
+    return rc;
+}
+
+} // namespace udpsocket
diff --git a/socket_channel.hpp b/socket_channel.hpp
new file mode 100644
index 0000000..de95bfd
--- /dev/null
+++ b/socket_channel.hpp
@@ -0,0 +1,124 @@
+#pragma once
+
+#include <arpa/inet.h>
+#include <unistd.h>
+
+#include <string>
+#include <tuple>
+#include <vector>
+
+namespace udpsocket
+{
+
+using buffer = std::vector<uint8_t>;
+/** @class Channel
+ *
+ *  @brief Provides encapsulation for UDP socket operations like Read, Peek,
+ *         Write, Remote peer's IP Address and Port.
+ */
+class Channel
+{
+    public:
+        struct SockAddr_t
+        {
+            union
+            {
+                sockaddr sockAddr;
+                sockaddr_in inAddr;
+            };
+            size_t addrSize;
+        };
+
+        /**
+         * @brief Constructor
+         *
+         * Initialize the IPMI socket object with the socket descriptor
+         *
+         * @param [in] File Descriptor for the socket
+         * @param [in] Timeout parameter for the select call
+         *
+         * @return None
+         */
+        Channel(int insockfd, timeval& inTimeout)
+        {
+            sockfd = insockfd;
+            timeout = inTimeout;
+        }
+
+        /**
+         * @brief Fetch the IP address of the remote peer
+         *
+         * Returns the IP address of the remote peer which is connected to this
+         * socket
+         *
+         * @return IP address of the remote peer
+         */
+        std::string getRemoteAddress() const;
+
+        /**
+         * @brief Fetch the port number of the remote peer
+         *
+         * Returns the port number of the remote peer
+         *
+         * @return Port number
+         *
+         */
+        auto getPort() const
+        {
+            return address.inAddr.sin_port;
+        }
+
+        /**
+         * @brief Read the incoming packet
+         *
+         * Reads the data available on the socket
+         *
+         * @return A tuple with return code and vector with the buffer
+         *         In case of success, the vector is populated with the data
+         *         available on the socket and return code is 0.
+         *         In case of error, the return code is < 0 and vector is set
+         *         to size 0.
+         */
+        std::tuple<int, buffer> read();
+
+        /**
+         *  @brief Write the outgoing packet
+         *
+         *  Writes the data in the vector to the socket
+         *
+         *  @param [in] inBuffer
+         *      The vector would be the buffer of data to write to the socket.
+         *
+         *  @return In case of success the return code is 0 and return code is
+         *          < 0 in case of failure.
+         */
+        int write(buffer& inBuffer);
+
+        /**
+         * @brief Returns file descriptor for the socket
+         */
+        auto getHandle(void) const
+        {
+            return sockfd;
+        }
+
+        ~Channel() = default;
+        Channel(const Channel& right) = delete;
+        Channel& operator=(const Channel& right) = delete;
+        Channel(Channel&&) = default;
+        Channel& operator=(Channel&&) = default;
+
+    private:
+        /*
+         * The socket descriptor is the UDP server socket for the IPMI port.
+         * The same socket descriptor is used for multiple ipmi clients and the
+         * life of the descriptor is lifetime of the net-ipmid server. So we
+         * do not need to close the socket descriptor in the cleanup of the
+         * udpsocket class.
+         */
+        int sockfd;
+        SockAddr_t address;
+        timeval timeout;
+};
+
+} // namespace udpsocket