Only allow message_bridge to connect with matching config sha256

We've been seeing a ton of crashes becasue the config doesn't match, and
a node is out of range, or the client is asking for a channel which
doesn't exist.  Honestly, there is no real use case at this point in
time for accepting connections from clients who aren't running the same
code.  We can't read the logs if we were to allow it, and the effort
required to support that is massive.  We'll probably run into send too
fast issues, would run into flatbuffer version problems (maybe), and all
sorts of other problems.  The cost to reward ratio doesn't work.

So, as part of connecting, send the sha256 sum of the config.  The
server will disconnect any clients who don't have a matching config, and
increment a counter in the status message.

Change-Id: I99520713efc644252f2c7cf5dc53720c4fc19974
Signed-off-by: Austin Schuh <austin.schuh@bluerivertech.com>
diff --git a/aos/network/message_bridge_test.cc b/aos/network/message_bridge_test.cc
index 226daf7..00033f9 100644
--- a/aos/network/message_bridge_test.cc
+++ b/aos/network/message_bridge_test.cc
@@ -8,6 +8,7 @@
 #include "aos/network/message_bridge_client_lib.h"
 #include "aos/network/message_bridge_server_lib.h"
 #include "aos/network/team_number.h"
+#include "aos/sha256.h"
 #include "aos/testing/path.h"
 #include "aos/util/file.h"
 #include "gtest/gtest.h"
@@ -53,6 +54,7 @@
   MessageBridgeParameterizedTest()
       : config(aos::configuration::ReadConfig(
             ArtifactPath(absl::StrCat("aos/network/", GetParam().config)))),
+        config_sha256(Sha256(config.span())),
         pi1_boot_uuid_(UUID::Random()),
         pi2_boot_uuid_(UUID::Random()) {
     util::UnlinkRecursive(ShmBase("pi1"));
@@ -73,14 +75,16 @@
     FLAGS_boot_uuid = pi2_boot_uuid_.ToString();
   }
 
-  void MakePi1Server() {
+  void MakePi1Server(std::string server_config_sha256 = "") {
     OnPi1();
     FLAGS_application_name = "pi1_message_bridge_server";
     pi1_server_event_loop =
         std::make_unique<aos::ShmEventLoop>(&config.message());
     pi1_server_event_loop->SetRuntimeRealtimePriority(1);
-    pi1_message_bridge_server =
-        std::make_unique<MessageBridgeServer>(pi1_server_event_loop.get());
+    pi1_message_bridge_server = std::make_unique<MessageBridgeServer>(
+        pi1_server_event_loop.get(), server_config_sha256.size() == 0
+                                         ? config_sha256
+                                         : server_config_sha256);
   }
 
   void RunPi1Server(chrono::nanoseconds duration) {
@@ -118,8 +122,8 @@
     pi1_client_event_loop =
         std::make_unique<aos::ShmEventLoop>(&config.message());
     pi1_client_event_loop->SetRuntimeRealtimePriority(1);
-    pi1_message_bridge_client =
-        std::make_unique<MessageBridgeClient>(pi1_client_event_loop.get());
+    pi1_message_bridge_client = std::make_unique<MessageBridgeClient>(
+        pi1_client_event_loop.get(), config_sha256);
   }
 
   void StartPi1Client() {
@@ -183,8 +187,8 @@
     pi2_server_event_loop =
         std::make_unique<aos::ShmEventLoop>(&config.message());
     pi2_server_event_loop->SetRuntimeRealtimePriority(1);
-    pi2_message_bridge_server =
-        std::make_unique<MessageBridgeServer>(pi2_server_event_loop.get());
+    pi2_message_bridge_server = std::make_unique<MessageBridgeServer>(
+        pi2_server_event_loop.get(), config_sha256);
   }
 
   void RunPi2Server(chrono::nanoseconds duration) {
@@ -222,8 +226,8 @@
     pi2_client_event_loop =
         std::make_unique<aos::ShmEventLoop>(&config.message());
     pi2_client_event_loop->SetRuntimeRealtimePriority(1);
-    pi2_message_bridge_client =
-        std::make_unique<MessageBridgeClient>(pi2_client_event_loop.get());
+    pi2_message_bridge_client = std::make_unique<MessageBridgeClient>(
+        pi2_client_event_loop.get(), config_sha256);
   }
 
   void RunPi2Client(chrono::nanoseconds duration) {
@@ -297,6 +301,8 @@
   }
 
   aos::FlatbufferDetachedBuffer<aos::Configuration> config;
+  std::string config_sha256;
+
   const UUID pi1_boot_uuid_;
   const UUID pi2_boot_uuid_;
 
@@ -1342,6 +1348,108 @@
   pi1_remote_timestamp_thread.join();
 }
 
+// Test that differing config sha256's result in no connection.
+TEST_P(MessageBridgeParameterizedTest, MismatchedSha256) {
+  // This is rather annoying to set up.  We need to start up a client and
+  // server, on the same node, but get them to think that they are on different
+  // nodes.
+  //
+  // We need the client to not post directly to "/test" like it would in a
+  // real system, otherwise we will re-send the ping message... So, use an
+  // application specific map to have the client post somewhere else.
+  //
+  // To top this all off, each of these needs to be done with a ShmEventLoop,
+  // which needs to run in a separate thread...  And it is really hard to get
+  // everything started up reliably.  So just be super generous on timeouts and
+  // hope for the best.  We can be more generous in the future if we need to.
+  //
+  // We are faking the application names by passing in --application_name=foo
+  OnPi1();
+
+  MakePi1Server("dummy sha256");
+  MakePi1Client();
+
+  // And build the app for testing.
+  MakePi1Test();
+  aos::Fetcher<ServerStatistics> pi1_server_statistics_fetcher =
+      pi1_test_event_loop->MakeFetcher<ServerStatistics>("/pi1/aos");
+  aos::Fetcher<ClientStatistics> pi1_client_statistics_fetcher =
+      pi1_test_event_loop->MakeFetcher<ClientStatistics>("/pi1/aos");
+
+  // Now do it for "raspberrypi2", the client.
+  OnPi2();
+  MakePi2Server();
+
+  // And build the app for testing.
+  MakePi2Test();
+  aos::Fetcher<ServerStatistics> pi2_server_statistics_fetcher =
+      pi2_test_event_loop->MakeFetcher<ServerStatistics>("/pi2/aos");
+  aos::Fetcher<ClientStatistics> pi2_client_statistics_fetcher =
+      pi2_test_event_loop->MakeFetcher<ClientStatistics>("/pi2/aos");
+
+  // Wait until we are connected, then send.
+
+  StartPi1Test();
+  StartPi2Test();
+  StartPi1Server();
+  StartPi1Client();
+  StartPi2Server();
+
+  {
+    MakePi2Client();
+
+    RunPi2Client(chrono::milliseconds(3050));
+
+    // Now confirm we are synchronized.
+    EXPECT_TRUE(pi1_server_statistics_fetcher.Fetch());
+    EXPECT_TRUE(pi1_client_statistics_fetcher.Fetch());
+    EXPECT_TRUE(pi2_server_statistics_fetcher.Fetch());
+    EXPECT_TRUE(pi2_client_statistics_fetcher.Fetch());
+
+    const ServerConnection *const pi1_connection =
+        pi1_server_statistics_fetcher->connections()->Get(0);
+    const ClientConnection *const pi1_client_connection =
+        pi1_client_statistics_fetcher->connections()->Get(0);
+    const ServerConnection *const pi2_connection =
+        pi2_server_statistics_fetcher->connections()->Get(0);
+    const ClientConnection *const pi2_client_connection =
+        pi2_client_statistics_fetcher->connections()->Get(0);
+
+    // Make sure one direction is disconnected with a bunch of connection
+    // attempts and failures.
+    EXPECT_EQ(pi1_connection->state(), State::DISCONNECTED);
+    EXPECT_EQ(pi1_connection->connection_count(), 0u);
+    EXPECT_GT(pi1_connection->invalid_connection_count(), 10u);
+
+    EXPECT_EQ(pi2_client_connection->state(), State::DISCONNECTED);
+    EXPECT_GT(pi2_client_connection->connection_count(), 10u);
+
+    // And the other direction is happy.
+    EXPECT_EQ(pi2_connection->state(), State::CONNECTED);
+    EXPECT_EQ(pi2_connection->connection_count(), 1u);
+    EXPECT_TRUE(pi2_connection->has_connected_since_time());
+    EXPECT_FALSE(pi2_connection->has_monotonic_offset());
+    EXPECT_TRUE(pi2_connection->has_boot_uuid());
+
+    EXPECT_EQ(pi1_client_connection->state(), State::CONNECTED);
+    EXPECT_EQ(pi1_client_connection->connection_count(), 1u);
+
+    VLOG(1) << aos::FlatbufferToJson(pi2_server_statistics_fetcher.get());
+    VLOG(1) << aos::FlatbufferToJson(pi1_server_statistics_fetcher.get());
+    VLOG(1) << aos::FlatbufferToJson(pi2_client_statistics_fetcher.get());
+    VLOG(1) << aos::FlatbufferToJson(pi1_client_statistics_fetcher.get());
+
+    StopPi2Client();
+  }
+
+  // Shut everyone else down
+  StopPi1Server();
+  StopPi1Client();
+  StopPi2Server();
+  StopPi1Test();
+  StopPi2Test();
+}
+
 INSTANTIATE_TEST_SUITE_P(
     MessageBridgeTests, MessageBridgeParameterizedTest,
     ::testing::Values(