server/transaction: Fix message hash

Our message hashes used to generate transaction id's for dbus services
have been relying on an invalid cookie when messages don't have cookies
present. This was exposed by 20adbc2d459123772ecd2d17009e1db0dd6314f2
which enabled error handling for message.get_cookie().

In order to generate a hash of the message, we need to consider the type
of message we have. In the context of an sdbusplus server, METHOD_CALL
type messages will always be incoming and sealed. They will always
provide a valid .get_cookie(). Messages of METHOD_RETURN or METHOD_ERROR
will not be sealed, but will always be generated from an incoming
METHOD_CALL. Therefore they will always have a valid
.get_reply_cookie(). SIGNAL type messages will always be outgoing so
they will not have a cookie yet. We will just use a monotonic source for
generating a transaction id for those.

Tested:
    Run through the unit test suite and booted in a romulus virtual
    machine. Ensured that none of the daemons were ABRTing like they
    were with just 20adbc2d459123772ecd2d17009e1db0dd6314f2 applied.

Change-Id: Ic54ae3b3cfa75d2f419579e1cb02f57eacb87c57
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/sdbusplus/server/transaction.hpp b/sdbusplus/server/transaction.hpp
index 87af07b..4f717ea 100644
--- a/sdbusplus/server/transaction.hpp
+++ b/sdbusplus/server/transaction.hpp
@@ -1,6 +1,10 @@
 #pragma once
 
+#include <systemd/sd-bus.h>
+
+#include <chrono>
 #include <sdbusplus/bus.hpp>
+#include <stdexcept>
 #include <thread>
 
 namespace sdbusplus
@@ -62,8 +66,27 @@
 {
     auto operator()(sdbusplus::message::message& m) const
     {
-        auto cookie = m.get_cookie();
-        return std::hash<uint64_t>{}(cookie);
+        switch (m.get_type())
+        {
+            // Reply messages will always embed the cookie of the original
+            // message in a separate location. We want to use this cookie
+            // to correlate messages as one transaction.
+            case SD_BUS_MESSAGE_METHOD_RETURN:
+            case SD_BUS_MESSAGE_METHOD_ERROR:
+                return std::hash<uint64_t>{}(m.get_reply_cookie());
+            // Method calls will have the cookie in the header when sealed.
+            // Since we are on the server side that should always be the case.
+            case SD_BUS_MESSAGE_METHOD_CALL:
+                return std::hash<uint64_t>{}(m.get_cookie());
+            // Outgoing signals don't have a cookie so we need to use
+            // something else as an id. Just use a monotonic unique one.
+            case SD_BUS_MESSAGE_SIGNAL:
+                return std::hash<uint64_t>{}(std::chrono::steady_clock::now()
+                                                 .time_since_epoch()
+                                                 .count());
+            default:
+                throw std::runtime_error("hash message: Unknown message type");
+        }
     }
 };