diff options
-rw-r--r-- | socket_channel.cpp | 156 | ||||
-rw-r--r-- | socket_channel.hpp | 124 |
2 files changed, 280 insertions, 0 deletions
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 |