diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am index 2d444bc2c1..6d5bb39374 100644 --- a/src/bin/dhcp6/tests/Makefile.am +++ b/src/bin/dhcp6/tests/Makefile.am @@ -113,6 +113,7 @@ dhcp6_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la dhcp6_unittests_LDADD += $(top_builddir)/src/lib/util/io/libkea-util-io.la dhcp6_unittests_LDADD += $(top_builddir)/src/bin/cfgrpt/libcfgrpt.la dhcp6_unittests_LDADD += $(top_builddir)/src/lib/stats/libkea-stats.la +dhcp6_unittests_LDADD += $(top_builddir)/src/lib/testutils/libkea-testutils.la endif diff --git a/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc index 31025fd810..559b2158d4 100644 --- a/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc +++ b/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc @@ -19,6 +19,7 @@ #include #include #include +#include #include "marker_file.h" #include "test_libraries.h" @@ -39,150 +40,6 @@ using namespace isc::hooks; namespace { -/// Class that acts as a UnixCommandSocket client -/// It can connect to an open UnixCommandSocket and exchange ControlChannel -/// commands and responses. -class UnixControlClient { -public: - UnixControlClient() { - socket_fd_ = -1; - } - - ~UnixControlClient() { - disconnectFromServer(); - } - - /// @brief Closes the Control Channel socket - void disconnectFromServer() { - if (socket_fd_ >= 0) { - static_cast(close(socket_fd_)); - socket_fd_ = -1; - } - } - - /// @brief Connects to a Unix socket at the given path - /// @param socket_path pathname of the socket to open - /// @return true if the connect was successful, false otherwise - bool connectToServer(const std::string& socket_path) { - // Create UNIX socket - socket_fd_ = socket(AF_UNIX, SOCK_STREAM, 0); - if (socket_fd_ < 0) { - const char* errmsg = strerror(errno); - ADD_FAILURE() << "Failed to open unix stream socket: " << errmsg; - return (false); - } - - struct sockaddr_un srv_addr; - if (socket_path.size() > sizeof(srv_addr.sun_path) - 1) { - ADD_FAILURE() << "Socket path specified (" << socket_path - << ") is larger than " << (sizeof(srv_addr.sun_path) - 1) - << " allowed."; - disconnectFromServer(); - return (false); - } - - // Prepare socket address - memset(&srv_addr, 0, sizeof(srv_addr)); - srv_addr.sun_family = AF_UNIX; - strncpy(srv_addr.sun_path, socket_path.c_str(), - sizeof(srv_addr.sun_path)); - socklen_t len = sizeof(srv_addr); - - // Connect to the specified UNIX socket - int status = connect(socket_fd_, (struct sockaddr*)&srv_addr, len); - if (status == -1) { - const char* errmsg = strerror(errno); - ADD_FAILURE() << "Failed to connect unix socket: fd=" << socket_fd_ - << ", path=" << socket_path << " : " << errmsg; - disconnectFromServer(); - return (false); - } - - return (true); - } - - /// @brief Sends the given command across the open Control Channel - /// @param command the command text to execute in JSON form - /// @return true if the send succeeds, false otherwise - bool sendCommand(const std::string& command) { - // Send command - int bytes_sent = send(socket_fd_, command.c_str(), command.length(), 0); - if (bytes_sent < command.length()) { - const char* errmsg = strerror(errno); - ADD_FAILURE() << "Failed to send " << command.length() - << " bytes, send() returned " << bytes_sent - << " : " << errmsg; - return (false); - } - - return (true); - } - - /// @brief Reads the response text from the open Control Channel - /// @param response variable into which the received response should be - /// placed. - /// @return true if data was successfully read from the socket, - /// false otherwise - bool getResponse(std::string& response) { - // Receive response - // @todo implement select check to see if data is waiting - char buf[65536]; - memset(buf, 0, sizeof(buf)); - switch (selectCheck()) { - case -1: { - const char* errmsg = strerror(errno); - ADD_FAILURE() << "getResponse - select failed: " << errmsg; - return (false); - } - case 0: - ADD_FAILURE() << "No response data sent"; - return (false); - - default: - break; - } - - int bytes_rcvd = recv(socket_fd_, buf, sizeof(buf), 0); - if (bytes_rcvd < 0) { - const char* errmsg = strerror(errno); - ADD_FAILURE() << "Failed to receive a response. recv() returned " - << bytes_rcvd << " : " << errmsg; - return (false); - } - - if (bytes_rcvd >= sizeof(buf)) { - ADD_FAILURE() << "Response size too large: " << bytes_rcvd; - return (false); - } - - // Convert the response to a string - response = string(buf, bytes_rcvd); - return (true); - } - - - /// @brief Uses select to poll the Control Channel for data waiting - /// @return -1 on error, 0 if no data is available, 1 if data is ready - int selectCheck() { - int maxfd = 0; - - fd_set read_fds; - FD_ZERO(&read_fds); - - // Add this socket to listening set - FD_SET(socket_fd_, &read_fds); - maxfd = socket_fd_; - - struct timeval select_timeout; - select_timeout.tv_sec = 0; - select_timeout.tv_usec = 0; - - return (select(maxfd + 1, &read_fds, NULL, NULL, &select_timeout)); - } - - /// @brief Retains the fd of the open socket - int socket_fd_; -}; class NakedControlledDhcpv6Srv: public ControlledDhcpv6Srv { diff --git a/src/lib/testutils/Makefile.am b/src/lib/testutils/Makefile.am index 90f4419027..769b5cff3f 100644 --- a/src/lib/testutils/Makefile.am +++ b/src/lib/testutils/Makefile.am @@ -11,6 +11,7 @@ noinst_LTLIBRARIES = libkea-testutils.la libkea_testutils_la_SOURCES = srv_test.h srv_test.cc libkea_testutils_la_SOURCES += dnsmessage_test.h dnsmessage_test.cc +libkea_testutils_la_SOURCES += unix_control_client.h unix_control_client.cc libkea_testutils_la_SOURCES += mockups.h libkea_testutils_la_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) libkea_testutils_la_LIBADD = $(top_builddir)/src/lib/asiolink/libkea-asiolink.la diff --git a/src/lib/testutils/unix_control_client.cc b/src/lib/testutils/unix_control_client.cc new file mode 100644 index 0000000000..d22f3e471f --- /dev/null +++ b/src/lib/testutils/unix_control_client.cc @@ -0,0 +1,150 @@ +// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include +#include +#include +#include +#include +#include +#include + +namespace isc { +namespace dhcp { +namespace test { + +UnixControlClient::UnixControlClient() { + socket_fd_ = -1; +} + +UnixControlClient::~UnixControlClient() { + disconnectFromServer(); +} + + /// @brief Closes the Control Channel socket +void UnixControlClient::disconnectFromServer() { + if (socket_fd_ >= 0) { + static_cast(close(socket_fd_)); + socket_fd_ = -1; + } +} + +bool UnixControlClient::connectToServer(const std::string& socket_path) { + // Create UNIX socket + socket_fd_ = socket(AF_UNIX, SOCK_STREAM, 0); + if (socket_fd_ < 0) { + const char* errmsg = strerror(errno); + ADD_FAILURE() << "Failed to open unix stream socket: " << errmsg; + return (false); + } + + struct sockaddr_un srv_addr; + if (socket_path.size() > sizeof(srv_addr.sun_path) - 1) { + ADD_FAILURE() << "Socket path specified (" << socket_path + << ") is larger than " << (sizeof(srv_addr.sun_path) - 1) + << " allowed."; + disconnectFromServer(); + return (false); + } + + // Prepare socket address + memset(&srv_addr, 0, sizeof(srv_addr)); + srv_addr.sun_family = AF_UNIX; + strncpy(srv_addr.sun_path, socket_path.c_str(), + sizeof(srv_addr.sun_path)); + socklen_t len = sizeof(srv_addr); + + // Connect to the specified UNIX socket + int status = connect(socket_fd_, (struct sockaddr*)&srv_addr, len); + if (status == -1) { + const char* errmsg = strerror(errno); + ADD_FAILURE() << "Failed to connect unix socket: fd=" << socket_fd_ + << ", path=" << socket_path << " : " << errmsg; + disconnectFromServer(); + return (false); + } + + return (true); +} + +bool UnixControlClient::sendCommand(const std::string& command) { + // Send command + int bytes_sent = send(socket_fd_, command.c_str(), command.length(), 0); + if (bytes_sent < command.length()) { + const char* errmsg = strerror(errno); + ADD_FAILURE() << "Failed to send " << command.length() + << " bytes, send() returned " << bytes_sent + << " : " << errmsg; + return (false); + } + + return (true); +} + +bool UnixControlClient::getResponse(std::string& response) { + // Receive response + char buf[65536]; + memset(buf, 0, sizeof(buf)); + switch (selectCheck()) { + case -1: { + const char* errmsg = strerror(errno); + ADD_FAILURE() << "getResponse - select failed: " << errmsg; + return (false); + } + case 0: + ADD_FAILURE() << "No response data sent"; + return (false); + + default: + break; + } + + int bytes_rcvd = recv(socket_fd_, buf, sizeof(buf), 0); + if (bytes_rcvd < 0) { + const char* errmsg = strerror(errno); + ADD_FAILURE() << "Failed to receive a response. recv() returned " + << bytes_rcvd << " : " << errmsg; + return (false); + } + + if (bytes_rcvd >= sizeof(buf)) { + ADD_FAILURE() << "Response size too large: " << bytes_rcvd; + return (false); + } + + // Convert the response to a string + response = std::string(buf, bytes_rcvd); + return (true); +} + +int UnixControlClient::selectCheck() { + int maxfd = 0; + + fd_set read_fds; + FD_ZERO(&read_fds); + + // Add this socket to listening set + FD_SET(socket_fd_, &read_fds); + maxfd = socket_fd_; + + struct timeval select_timeout; + select_timeout.tv_sec = 0; + select_timeout.tv_usec = 0; + + return (select(maxfd + 1, &read_fds, NULL, NULL, &select_timeout)); +} + +}; +}; +}; diff --git a/src/lib/testutils/unix_control_client.h b/src/lib/testutils/unix_control_client.h new file mode 100644 index 0000000000..c08e51d6e5 --- /dev/null +++ b/src/lib/testutils/unix_control_client.h @@ -0,0 +1,71 @@ +// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#ifndef UNIX_CONTROL_CLIENT_H +#define UNIX_CONTROL_CLIENT_H + +#include + +namespace isc { +namespace dhcp { +namespace test { + +/// @brief Class that acts as a UnixCommandSocket client +/// +/// This class is expected to be used unit-tests that attempt to communicate +/// with the servers that use control channel (see src/lib/config/command_mgr.h) +/// It can connect to an open UnixCommandSocket and exchange ControlChannel +/// commands and responses. +class UnixControlClient { +public: + + /// @brief Default constructor + UnixControlClient(); + + /// @brief Destructor + ~UnixControlClient(); + + /// @brief Closes the Control Channel socket + void disconnectFromServer(); + + /// @brief Connects to a Unix socket at the given path + /// @param socket_path pathname of the socket to open + /// @return true if the connect was successful, false otherwise + bool connectToServer(const std::string& socket_path); + + /// @brief Sends the given command across the open Control Channel + /// @param command the command text to execute in JSON form + /// @return true if the send succeeds, false otherwise + bool sendCommand(const std::string& command); + + /// @brief Reads the response text from the open Control Channel + /// @param response variable into which the received response should be + /// placed. + /// @return true if data was successfully read from the socket, + /// false otherwise + bool getResponse(std::string& response); + + /// @brief Uses select to poll the Control Channel for data waiting + /// @return -1 on error, 0 if no data is available, 1 if data is ready + int selectCheck(); + + /// @brief Retains the fd of the open socket + int socket_fd_; +}; + +}; // end of isc::dhcp::test namespace +}; // end of isc::dhcp namespace +}; // end of isc namespace + +#endif // UNIX_CONTROL_CLIENT_H