From: Marcin Siodelski Date: Thu, 22 Jun 2017 11:39:19 +0000 (+0200) Subject: [5317] Only a single control command connection is now allowed. X-Git-Tag: trac5227_base~26^2~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c4fb6001e7de756bdbb0cf488e21d2d9f815c4e7;p=thirdparty%2Fkea.git [5317] Only a single control command connection is now allowed. --- diff --git a/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc index cd080b07df..a7c03e214e 100644 --- a/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc +++ b/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 client1(new UnixControlClient()); + ASSERT_TRUE(client1); + + boost::scoped_ptr 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 diff --git a/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc index dc71f31d8d..f0ea2a5b1a 100644 --- a/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc +++ b/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 client1(new UnixControlClient()); + ASSERT_TRUE(client1); + + boost::scoped_ptr 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 diff --git a/src/lib/config/command_mgr.cc b/src/lib/config/command_mgr.cc index 17b4553ff9..916415bd6a 100644 --- a/src/lib/config/command_mgr.cc +++ b/src/lib/config/command_mgr.cc @@ -97,6 +97,10 @@ public: connections_.clear(); } + size_t getConnectionsNum() const { + return (connections_.size()); + } + private: std::set 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()); diff --git a/src/lib/config/config_messages.mes b/src/lib/config/config_messages.mes index 9bcc62d1a3..891248013f 100644 --- a/src/lib/config/config_messages.mes +++ b/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,