netipmid: consolidate message-related things in message.hpp

The message::Handler class was directly manipulating a bunch of stuff
on behalf of the message::Message class. This change moves more of the
changes into the message::Message class so it can manage its own data.

Change-Id: I5d31f6c3c5760207408238d048853e36a60c73e0
Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com>
diff --git a/message.hpp b/message.hpp
index db46c06..ebad490 100644
--- a/message.hpp
+++ b/message.hpp
@@ -1,6 +1,7 @@
 #pragma once
 
 #include <memory>
+#include <numeric>
 #include <vector>
 
 namespace message
@@ -19,50 +20,6 @@
     INVALID = 0xFF,
 };
 
-/**
- * @struct Message
- *
- * IPMI message is data encapsulated in an IPMI Session packet. The IPMI
- * Session packets are encapsulated in RMCP packets, which are encapsulated in
- * UDP datagrams. Refer Section 13.5 of IPMI specification(IPMI Messages
- * Encapsulation Under RMCP). IPMI payload is a special class of data
- * encapsulated in an IPMI session packet.
- */
-struct Message
-{
-    static constexpr uint32_t MESSAGE_INVALID_SESSION_ID = 0xBADBADFF;
-
-    Message() :
-        payloadType(PayloadType::INVALID),
-        rcSessionID(Message::MESSAGE_INVALID_SESSION_ID),
-        bmcSessionID(Message::MESSAGE_INVALID_SESSION_ID)
-    {
-    }
-
-    ~Message() = default;
-    Message(const Message&) = default;
-    Message& operator=(const Message&) = default;
-    Message(Message&&) = default;
-    Message& operator=(Message&&) = default;
-
-    bool isPacketEncrypted;     // Message's Encryption Status
-    bool isPacketAuthenticated; // Message's Authentication Status
-    PayloadType payloadType;    // Type of message payload (IPMI,SOL ..etc)
-    uint32_t rcSessionID;       // Remote Client's Session ID
-    uint32_t bmcSessionID;      // BMC's session ID
-    uint32_t sessionSeqNum;     // Session Sequence Number
-
-    /** @brief Message payload
-     *
-     *  “Payloads” are a capability specified for RMCP+ that enable an IPMI
-     *  session to carry types of traffic that are in addition to IPMI Messages.
-     *  Payloads can be ‘standard’ or ‘OEM’.Standard payload types include IPMI
-     *  Messages, messages for session setup under RMCP+, and the payload for
-     *  the “Serial Over LAN” capability introduced in IPMI v2.0.
-     */
-    std::vector<uint8_t> payload;
-};
-
 namespace LAN
 {
 
@@ -117,4 +74,168 @@
 
 } // namespace LAN
 
+/**
+ * @brief Calculate 8 bit 2's complement checksum
+ *
+ * Initialize checksum to 0. For each byte, checksum = (checksum + byte)
+ * modulo 256. Then checksum = - checksum. When the checksum and the
+ * bytes are added together, modulo 256, the result should be 0.
+ */
+static inline uint8_t crc8bit(const uint8_t* ptr, const size_t len)
+{
+    return (0x100 - std::accumulate(ptr, ptr + len, 0));
+}
+
+/**
+ * @struct Message
+ *
+ * IPMI message is data encapsulated in an IPMI Session packet. The IPMI
+ * Session packets are encapsulated in RMCP packets, which are encapsulated in
+ * UDP datagrams. Refer Section 13.5 of IPMI specification(IPMI Messages
+ * Encapsulation Under RMCP). IPMI payload is a special class of data
+ * encapsulated in an IPMI session packet.
+ */
+struct Message
+{
+    static constexpr uint32_t MESSAGE_INVALID_SESSION_ID = 0xBADBADFF;
+
+    Message() :
+        payloadType(PayloadType::INVALID),
+        rcSessionID(Message::MESSAGE_INVALID_SESSION_ID),
+        bmcSessionID(Message::MESSAGE_INVALID_SESSION_ID)
+    {
+    }
+
+    /**
+     * @brief Special behavior for copy constructor
+     *
+     * Based on incoming message state, the resulting message will have a
+     * pre-baked state. This is used to simplify the flows for creating a
+     * response message. For each pre-session state, the response message is
+     * actually a different type of message. Once the session has been
+     * established, the response type is the same as the request type.
+     */
+    Message(const Message& other) :
+        isPacketEncrypted(other.isPacketEncrypted),
+        isPacketAuthenticated(other.isPacketAuthenticated),
+        payloadType(other.payloadType), rcSessionID(other.rcSessionID),
+        bmcSessionID(other.bmcSessionID)
+    {
+        // special behavior for rmcp+ session creation
+        if (PayloadType::OPEN_SESSION_REQUEST == other.payloadType)
+        {
+            payloadType = PayloadType::OPEN_SESSION_RESPONSE;
+        }
+        else if (PayloadType::RAKP1 == other.payloadType)
+        {
+            payloadType = PayloadType::RAKP2;
+        }
+        else if (PayloadType::RAKP3 == other.payloadType)
+        {
+            payloadType = PayloadType::RAKP4;
+        }
+    }
+    Message& operator=(const Message&) = default;
+    Message(Message&&) = default;
+    Message& operator=(Message&&) = default;
+    ~Message() = default;
+
+    /**
+     * @brief Extract the command from the IPMI payload
+     *
+     * @return Command ID in the incoming message
+     */
+    uint32_t getCommand()
+    {
+        uint32_t command = 0;
+
+        command |= (static_cast<uint8_t>(payloadType) << 16);
+        if (payloadType == PayloadType::IPMI)
+        {
+            auto request =
+                reinterpret_cast<LAN::header::Request*>(payload.data());
+            command |= request->netfn << 8;
+            command |= request->cmd;
+        }
+        return command;
+    }
+
+    /**
+     * @brief Create the response IPMI message
+     *
+     * The IPMI outgoing message is constructed out of payload and the
+     * corresponding fields are populated. For the payload type IPMI, the
+     * LAN message header and trailer are added.
+     *
+     * @param[in] output - Payload for outgoing message
+     *
+     * @return Outgoing message on success and nullptr on failure
+     */
+    std::shared_ptr<Message> createResponse(std::vector<uint8_t>& output)
+    {
+        // SOL packets don't reply; return NULL
+        if (payloadType == PayloadType::SOL)
+        {
+            return nullptr;
+        }
+        auto outMessage = std::make_shared<Message>(*this);
+
+        if (payloadType == PayloadType::IPMI)
+        {
+            outMessage->payloadType = PayloadType::IPMI;
+
+            outMessage->payload.resize(sizeof(LAN::header::Response) +
+                                       output.size() +
+                                       sizeof(LAN::trailer::Response));
+
+            auto reqHeader =
+                reinterpret_cast<LAN::header::Request*>(payload.data());
+            auto respHeader = reinterpret_cast<LAN::header::Response*>(
+                outMessage->payload.data());
+
+            // Add IPMI LAN Message Response Header
+            respHeader->rqaddr = reqHeader->rqaddr;
+            respHeader->netfn = reqHeader->netfn | 0x04;
+            respHeader->cs = crc8bit(&(respHeader->rqaddr), 2);
+            respHeader->rsaddr = reqHeader->rsaddr;
+            respHeader->rqseq = reqHeader->rqseq;
+            respHeader->cmd = reqHeader->cmd;
+
+            auto assembledSize = sizeof(LAN::header::Response);
+
+            // Copy the output by the execution of the command
+            std::copy(output.begin(), output.end(),
+                      outMessage->payload.begin() + assembledSize);
+            assembledSize += output.size();
+
+            // Add the IPMI LAN Message Trailer
+            auto trailer = reinterpret_cast<LAN::trailer::Response*>(
+                outMessage->payload.data() + assembledSize);
+            trailer->checksum = crc8bit(&respHeader->rsaddr, assembledSize - 3);
+        }
+        else
+        {
+            outMessage->payload = output;
+        }
+        return outMessage;
+    }
+
+    bool isPacketEncrypted;     // Message's Encryption Status
+    bool isPacketAuthenticated; // Message's Authentication Status
+    PayloadType payloadType;    // Type of message payload (IPMI,SOL ..etc)
+    uint32_t rcSessionID;       // Remote Client's Session ID
+    uint32_t bmcSessionID;      // BMC's session ID
+    uint32_t sessionSeqNum;     // Session Sequence Number
+
+    /** @brief Message payload
+     *
+     *  “Payloads” are a capability specified for RMCP+ that enable an IPMI
+     *  session to carry types of traffic that are in addition to IPMI Messages.
+     *  Payloads can be ‘standard’ or ‘OEM’.Standard payload types include IPMI
+     *  Messages, messages for session setup under RMCP+, and the payload for
+     *  the “Serial Over LAN” capability introduced in IPMI v2.0.
+     */
+    std::vector<uint8_t> payload;
+};
+
 } // namespace message
diff --git a/message_handler.cpp b/message_handler.cpp
index e98955b..58630d9 100644
--- a/message_handler.cpp
+++ b/message_handler.cpp
@@ -47,49 +47,11 @@
     return message;
 }
 
-template <>
-std::shared_ptr<Message> Handler::createResponse<PayloadType::IPMI>(
-    std::vector<uint8_t>& output, std::shared_ptr<Message> inMessage)
-{
-    auto outMessage = std::make_shared<Message>();
-    outMessage->payloadType = PayloadType::IPMI;
-
-    outMessage->payload.resize(sizeof(LAN::header::Response) + output.size() +
-                               sizeof(LAN::trailer::Response));
-
-    auto reqHeader =
-        reinterpret_cast<LAN::header::Request*>(inMessage->payload.data());
-    auto respHeader =
-        reinterpret_cast<LAN::header::Response*>(outMessage->payload.data());
-
-    // Add IPMI LAN Message Response Header
-    respHeader->rqaddr = reqHeader->rqaddr;
-    respHeader->netfn = reqHeader->netfn | 0x04;
-    respHeader->cs = crc8bit(&(respHeader->rqaddr), 2);
-    respHeader->rsaddr = reqHeader->rsaddr;
-    respHeader->rqseq = reqHeader->rqseq;
-    respHeader->cmd = reqHeader->cmd;
-
-    auto assembledSize = sizeof(LAN::header::Response);
-
-    // Copy the output by the execution of the command
-    std::copy(output.begin(), output.end(),
-              outMessage->payload.begin() + assembledSize);
-    assembledSize += output.size();
-
-    // Add the IPMI LAN Message Trailer
-    auto trailer = reinterpret_cast<LAN::trailer::Response*>(
-        outMessage->payload.data() + assembledSize);
-    trailer->checksum = crc8bit(&respHeader->rsaddr, assembledSize - 3);
-
-    return outMessage;
-}
-
 std::shared_ptr<Message>
     Handler::executeCommand(std::shared_ptr<Message> inMessage)
 {
     // Get the CommandID to map into the command table
-    auto command = getCommand(inMessage);
+    auto command = inMessage->getCommand();
     std::vector<uint8_t> output{};
 
     if (inMessage->payloadType == PayloadType::IPMI)
@@ -112,56 +74,7 @@
         output = std::get<command::Table&>(singletonPool)
                      .executeCommand(command, inMessage->payload, *this);
     }
-
-    std::shared_ptr<Message> outMessage = nullptr;
-
-    switch (inMessage->payloadType)
-    {
-        case PayloadType::IPMI:
-            outMessage = createResponse<PayloadType::IPMI>(output, inMessage);
-            break;
-        case PayloadType::OPEN_SESSION_REQUEST:
-            outMessage = createResponse<PayloadType::OPEN_SESSION_RESPONSE>(
-                output, inMessage);
-            break;
-        case PayloadType::RAKP1:
-            outMessage = createResponse<PayloadType::RAKP2>(output, inMessage);
-            break;
-        case PayloadType::RAKP3:
-            outMessage = createResponse<PayloadType::RAKP4>(output, inMessage);
-            break;
-        case PayloadType::SOL:
-            return outMessage;
-            break;
-        default:
-            break;
-    }
-
-    outMessage->isPacketEncrypted = inMessage->isPacketEncrypted;
-    outMessage->isPacketAuthenticated = inMessage->isPacketAuthenticated;
-    outMessage->rcSessionID = inMessage->rcSessionID;
-    outMessage->bmcSessionID = inMessage->bmcSessionID;
-
-    return outMessage;
-}
-
-uint32_t Handler::getCommand(std::shared_ptr<Message> message)
-{
-    uint32_t command = 0;
-
-    command |= (static_cast<uint8_t>(message->payloadType) << 16);
-    if (message->payloadType == PayloadType::IPMI)
-    {
-        command |=
-            ((reinterpret_cast<LAN::header::Request*>(message->payload.data()))
-                 ->netfn)
-            << 8;
-        command |=
-            (reinterpret_cast<LAN::header::Request*>(message->payload.data()))
-                ->cmd;
-    }
-
-    return command;
+    return inMessage->createResponse(output);
 }
 
 void Handler::send(std::shared_ptr<Message> outMessage)
diff --git a/message_handler.hpp b/message_handler.hpp
index c620a9d..599b3d6 100644
--- a/message_handler.hpp
+++ b/message_handler.hpp
@@ -6,7 +6,6 @@
 #include "sol/console_buffer.hpp"
 
 #include <memory>
-#include <numeric>
 
 namespace message
 {
@@ -24,10 +23,10 @@
 
     Handler() = delete;
     ~Handler() = default;
-    Handler(const Handler&) = default;
-    Handler& operator=(const Handler&) = default;
-    Handler(Handler&&) = default;
-    Handler& operator=(Handler&&) = default;
+    Handler(const Handler&) = delete;
+    Handler& operator=(const Handler&) = delete;
+    Handler(Handler&&) = delete;
+    Handler& operator=(Handler&&) = delete;
 
     /**
      * @brief Receive the IPMI packet
@@ -94,50 +93,6 @@
     std::shared_ptr<udpsocket::Channel> channel;
 
     parser::SessionHeader sessionHeader = parser::SessionHeader::IPMI20;
-
-    /**
-     * @brief Create the response IPMI message
-     *
-     * The IPMI outgoing message is constructed out of payload and the
-     * corresponding fields are populated.For the payload type IPMI, the
-     * LAN message header and trailer are added.
-     *
-     * @tparam[in] T - Outgoing message payload type
-     * @param[in] output - Payload for outgoing message
-     * @param[in] inMessage - Incoming IPMI message
-     *
-     * @return Outgoing message on success and nullptr on failure
-     */
-    template <PayloadType T>
-    std::shared_ptr<Message> createResponse(std::vector<uint8_t>& output,
-                                            std::shared_ptr<Message> inMessage)
-    {
-        auto outMessage = std::make_shared<Message>();
-        outMessage->payloadType = T;
-        outMessage->payload = output;
-        return outMessage;
-    }
-
-    /**
-     * @brief Extract the command from the IPMI payload
-     *
-     * @param[in] message - Incoming message
-     *
-     * @return Command ID in the incoming message
-     */
-    uint32_t getCommand(std::shared_ptr<Message> message);
-
-    /**
-     * @brief Calculate 8 bit 2's complement checksum
-     *
-     * Initialize checksum to 0. For each byte, checksum = (checksum + byte)
-     * modulo 256. Then checksum = - checksum. When the checksum and the
-     * bytes are added together, modulo 256, the result should be 0.
-     */
-    uint8_t crc8bit(const uint8_t* ptr, const size_t len)
-    {
-        return (0x100 - std::accumulate(ptr, ptr + len, 0));
-    }
 };
 
 } // namespace message