Browse Source

[5317] Only a single control command connection is now allowed.

Marcin Siodelski 8 years ago
parent
commit
c4fb6001e7

+ 46 - 0
src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc

@@ -1066,4 +1066,50 @@ TEST_F(CtrlChannelDhcpv4SrvTest, configReloadValid) {
     ::remove("test8.json");
 }
 
+// Verify that server returns an error if more than one connection is established.
+TEST_F(CtrlChannelDhcpv4SrvTest, concurrentConnections) {
+    createUnixChannelServer();
+
+    boost::scoped_ptr<UnixControlClient> client1(new UnixControlClient());
+    ASSERT_TRUE(client1);
+
+    boost::scoped_ptr<UnixControlClient> client2(new UnixControlClient());
+    ASSERT_TRUE(client1);
+
+    // Client 1 connects.
+    ASSERT_TRUE(client1->connectToServer(socket_path_));
+    ASSERT_NO_THROW(getIOService()->poll());
+
+    // Client 2 connects.
+    ASSERT_TRUE(client2->connectToServer(socket_path_));
+    ASSERT_NO_THROW(getIOService()->poll());
+
+    // Send the command while another client is connected.
+    ASSERT_TRUE(client2->sendCommand("{ \"command\": \"list-commands\" }"));
+    ASSERT_NO_THROW(getIOService()->poll());
+
+    // The server should not allow for concurrent connections and should send
+    // out an error message.
+    std::string response;
+    ASSERT_TRUE(client2->getResponse(response));
+    EXPECT_EQ("{ \"result\": 1, \"text\": \"exceeded maximum number of concurrent"
+              " connections\" }", response);
+
+    // Now disconnect the first server and retry.
+    client1->disconnectFromServer();
+    ASSERT_NO_THROW(getIOService()->poll());
+
+    ASSERT_TRUE(client2->connectToServer(socket_path_));
+    ASSERT_NO_THROW(getIOService()->poll());
+
+    ASSERT_TRUE(client2->sendCommand("{ \"command\": \"list-commands\" }"));
+    ASSERT_NO_THROW(getIOService()->poll());
+
+    // The server should now respond ok.
+    ASSERT_TRUE(client2->getResponse(response));
+    EXPECT_TRUE(response.find("\"result\": 0") != std::string::npos);
+
+    client2->disconnectFromServer();
+}
+
 } // End of anonymous namespace

+ 47 - 0
src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc

@@ -1088,4 +1088,51 @@ TEST_F(CtrlChannelDhcpv6SrvTest, configReloadValid) {
     ::remove("test8.json");
 }
 
+// Verify that server returns an error if more than one connection is established.
+TEST_F(CtrlChannelDhcpv6SrvTest, concurrentConnections) {
+    createUnixChannelServer();
+
+    boost::scoped_ptr<UnixControlClient> client1(new UnixControlClient());
+    ASSERT_TRUE(client1);
+
+    boost::scoped_ptr<UnixControlClient> client2(new UnixControlClient());
+    ASSERT_TRUE(client1);
+
+    // Client 1 connects.
+    ASSERT_TRUE(client1->connectToServer(socket_path_));
+    ASSERT_NO_THROW(getIOService()->poll());
+
+    // Client 2 connects.
+    ASSERT_TRUE(client2->connectToServer(socket_path_));
+    ASSERT_NO_THROW(getIOService()->poll());
+
+    // Send the command while another client is connected.
+    ASSERT_TRUE(client2->sendCommand("{ \"command\": \"list-commands\" }"));
+    ASSERT_NO_THROW(getIOService()->poll());
+
+    // The server should not allow for concurrent connections and should send
+    // out an error message.
+    std::string response;
+    ASSERT_TRUE(client2->getResponse(response));
+    EXPECT_EQ("{ \"result\": 1, \"text\": \"exceeded maximum number of concurrent"
+              " connections\" }", response);
+
+    // Now disconnect the first server and retry.
+    client1->disconnectFromServer();
+    ASSERT_NO_THROW(getIOService()->poll());
+
+    ASSERT_TRUE(client2->connectToServer(socket_path_));
+    ASSERT_NO_THROW(getIOService()->poll());
+
+    ASSERT_TRUE(client2->sendCommand("{ \"command\": \"list-commands\" }"));
+    ASSERT_NO_THROW(getIOService()->poll());
+
+    // The server should now respond ok.
+    ASSERT_TRUE(client2->getResponse(response));
+    EXPECT_TRUE(response.find("\"result\": 0") != std::string::npos);
+
+    client2->disconnectFromServer();
+}
+
+
 } // End of anonymous namespace

+ 19 - 3
src/lib/config/command_mgr.cc

@@ -97,6 +97,10 @@ public:
         connections_.clear();
     }
 
+    size_t getConnectionsNum() const {
+        return (connections_.size());
+    }
+
 private:
 
     std::set<ConnectionPtr> connections_;
@@ -109,7 +113,14 @@ Connection::receiveHandler(const boost::system::error_code& ec,
                            size_t bytes_transferred) {
     if (ec) {
 
-        if (ec.value() != boost::asio::error::operation_aborted) {
+        if (ec.value() == boost::asio::error::eof) {
+            // Foreign host has closed the connection. We should remove it from the
+            // connection pool.
+            LOG_INFO(command_logger, COMMAND_SOCKET_CLOSED_BY_FOREIGN_HOST)
+                .arg(socket_->getNative());
+            connection_pool_.stop(shared_from_this());
+
+        } else if (ec.value() != boost::asio::error::operation_aborted) {
             LOG_ERROR(command_logger, COMMAND_SOCKET_READ_FAIL)
                 .arg(ec.value()).arg(socket_->getNative());
         }
@@ -137,8 +148,13 @@ Connection::receiveHandler(const boost::system::error_code& ec,
         std::string sbuf(&buf_[0], bytes_transferred);
         cmd = Element::fromJSON(sbuf, true);
 
-        // If successful, then process it as a command.
-        rsp = CommandMgr::instance().processCommand(cmd);
+        if (connection_pool_.getConnectionsNum() > 1) {
+            rsp = createAnswer(CONTROL_RESULT_ERROR, "exceeded maximum number of concurrent"
+                               " connections");
+        } else {
+            // If successful, then process it as a command.
+            rsp = CommandMgr::instance().processCommand(cmd);
+        }
 
     } catch (const Exception& ex) {
         LOG_WARN(command_logger, COMMAND_PROCESS_ERROR1).arg(ex.what());

+ 4 - 0
src/lib/config/config_messages.mes

@@ -55,6 +55,10 @@ This error indicates that the server detected incoming connection and executed
 accept system call on said socket, but this call returned an error. Additional
 information may be provided by the system as second parameter.
 
+% COMMAND_SOCKET_CLOSED_BY_FOREIGN_HOST Closed command socket %1 by foreign host
+This is an information message indicating that the command connection has been
+closed by a command control client.
+
 % COMMAND_SOCKET_CONNECTION_CLOSED Closed socket %1 for existing command connection
 This is an informational message that the socket created for handling
 client's connection is closed. This usually means that the client disconnected,