]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[5317] Only a single control command connection is now allowed.
authorMarcin Siodelski <marcin@isc.org>
Thu, 22 Jun 2017 11:39:19 +0000 (13:39 +0200)
committerMarcin Siodelski <marcin@isc.org>
Thu, 22 Jun 2017 11:39:19 +0000 (13:39 +0200)
src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc
src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc
src/lib/config/command_mgr.cc
src/lib/config/config_messages.mes

index cd080b07df1b7d0f154f2c6ab46c4399a805b9a3..a7c03e214e458c0a436c51f7b4c42a7d9214e57a 100644 (file)
@@ -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
index dc71f31d8d44a612a07efcd6875ee0f7a9473a2d..f0ea2a5b1a9cbcf02e73502705ffcf45a0f7a498 100644 (file)
@@ -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
index 17b4553ff9214e485d0043e3b0a98ace08ae2297..916415bd6a7c1fa6f0aeb63e3ea6832161c0f853 100644 (file)
@@ -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());
index 9bcc62d1a336a6f177c2469150db41eb3e223393..891248013ffb8bb15a1e87964129232e73aa4b23 100644 (file)
@@ -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,