Socket Class to encapsulate the socket operations.

The operations provided are reading the UDP packet, writing the packet.

Change-Id: I087dcbdf715213edf787c3053be76813ce8d9802
Signed-off-by: Ratan Gupta <ratagupt@in.ibm.com>
diff --git a/endian.hpp b/endian.hpp
new file mode 100644
index 0000000..284fd03
--- /dev/null
+++ b/endian.hpp
@@ -0,0 +1,49 @@
+#pragma once
+
+#include <endian.h>
+#include <stdint.h>
+
+namespace endian
+{
+namespace details
+{
+template <typename T>
+struct convert
+{
+    static T to_network(T) = delete;
+    static T from_network(T) = delete;
+};
+
+template<> struct convert<uint16_t>
+{
+    static uint16_t to_network(uint16_t i)
+    {
+        return htobe16(i);
+    };
+    static uint16_t from_network(uint16_t i)
+    {
+        return be16toh(i);
+    };
+};
+
+template<> struct convert<uint32_t>
+{
+    static uint32_t to_network(uint32_t i)
+    {
+        return htobe32(i);
+    };
+    static uint32_t from_network(uint32_t i)
+    {
+        return be32toh(i);
+    };
+};
+}
+template<typename T> T to_network(T i)
+{
+    return details::convert<T>::to_network(i);
+}
+template<typename T> T from_network(T i)
+{
+    return details::convert<T>::from_network(i);
+}
+}
diff --git a/sock_channel.cpp b/sock_channel.cpp
new file mode 100644
index 0000000..ef86029
--- /dev/null
+++ b/sock_channel.cpp
@@ -0,0 +1,156 @@
+#include "sock_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/sock_channel.hpp b/sock_channel.hpp
new file mode 100644
index 0000000..e992564
--- /dev/null
+++ b/sock_channel.hpp
@@ -0,0 +1,116 @@
+#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);
+
+        ~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