2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-29 21:18:02 +00:00

[#2583] Initial addition of src/lib/tcp

Added first draft of TcpListener classes.
They compile but don't do anything useful.

configure.ac
src/lib/Makefile.am
    added tcp

src/lib/tcp/README
src/lib/tcp/tcp_connection.cc
src/lib/tcp/tcp_connection.h
src/lib/tcp/tcp_connection_acceptor.h
src/lib/tcp/tcp_connection_pool.cc
src/lib/tcp/tcp_connection_pool.h
src/lib/tcp/tcp_listener.cc
src/lib/tcp/tcp_listener.h
src/lib/tcp/tcp_log.cc
src/lib/tcp/tcp_log.h
src/lib/tcp/tcp_messages.cc
src/lib/tcp/tcp_messages.h
src/lib/tcp/tcp_messages.mes
src/lib/tcp/tests/.gitignore
src/lib/tcp/tests/Makefile.am
src/lib/tcp/tests/run_unittests.cc
    - new files
This commit is contained in:
Thomas Markwalder 2022-10-04 06:51:58 -04:00
parent 6fb9bb04a3
commit 32d7bf4e93
19 changed files with 1669 additions and 1 deletions

View File

@ -1583,6 +1583,8 @@ AC_CONFIG_FILES([src/lib/process/testutils/Makefile])
AC_CONFIG_FILES([src/lib/stats/Makefile])
AC_CONFIG_FILES([src/lib/stats/tests/Makefile])
AC_CONFIG_FILES([src/lib/stats/testutils/Makefile])
AC_CONFIG_FILES([src/lib/tcp/Makefile])
AC_CONFIG_FILES([src/lib/tcp/tests/Makefile])
AC_CONFIG_FILES([src/lib/testutils/Makefile])
AC_CONFIG_FILES([src/lib/testutils/dhcp_test_lib.sh],
[chmod +x src/lib/testutils/dhcp_test_lib.sh])

View File

@ -9,7 +9,7 @@ if HAVE_PGSQL
SUBDIRS += pgsql
endif
SUBDIRS += config_backend hooks dhcp http config stats
SUBDIRS += config_backend hooks dhcp tcp http config stats
if HAVE_NETCONF
SUBDIRS += yang

76
src/lib/tcp/Makefile.am Normal file
View File

@ -0,0 +1,76 @@
SUBDIRS = . tests
AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES) $(CRYPTO_CFLAGS) $(CRYPTO_INCLUDES)
AM_CXXFLAGS = $(KEA_CXXFLAGS)
EXTRA_DIST = # tcp.dox
# Ensure that the message file is included in the distribution
EXTRA_DIST += tcp_messages.mes
CLEANFILES = *.gcno *.gcda
lib_LTLIBRARIES = libkea-tcp.la
libkea_tcp_la_SOURCES = tcp_connection.cc tcp_connection.h
libkea_tcp_la_SOURCES += tcp_connection_pool.cc tcp_connection_pool.h
libkea_tcp_la_SOURCES += tcp_listener.cc tcp_listener.h
libkea_tcp_la_SOURCES += tcp_log.cc tcp_log.h
libkea_tcp_la_SOURCES += tcp_messages.cc tcp_messages.h
libkea_tcp_la_CXXFLAGS = $(AM_CXXFLAGS)
libkea_tcp_la_CPPFLAGS = $(AM_CPPFLAGS)
libkea_tcp_la_LDFLAGS = $(AM_LDFLAGS)
libkea_tcp_la_LDFLAGS += -no-undefined -version-info 52:0:0
libkea_tcp_la_LIBADD =
libkea_tcp_la_LIBADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
libkea_tcp_la_LIBADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
libkea_tcp_la_LIBADD += $(top_builddir)/src/lib/cc/libkea-cc.la
libkea_tcp_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la
libkea_tcp_la_LIBADD += $(top_builddir)/src/lib/util/libkea-util.la
libkea_tcp_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
libkea_tcp_la_LIBADD += $(LOG4CPLUS_LIBS) $(BOOST_LIBS) $(CRYPTO_LIBS)
# If we want to get rid of all generated messages files, we need to use
# make maintainer-clean. The proper way to introduce custom commands for
# that operation is to define maintainer-clean-local target. However,
# make maintainer-clean also removes Makefile, so running configure script
# is required. To make it easy to rebuild messages without going through
# reconfigure, a new target messages-clean has been added.
maintainer-clean-local:
rm -f tcp_messages.h tcp_messages.cc
# To regenerate messages files, one can do:
#
# make messages-clean
# make messages
#
# This is needed only when a .mes file is modified.
messages-clean: maintainer-clean-local
if GENERATE_MESSAGES
# Define rule to build logging source files from message file
messages: tcp_messages.h tcp_messages.cc
@echo Message files regenerated
tcp_messages.h tcp_messages.cc: tcp_messages.mes
$(top_builddir)/src/lib/log/compiler/kea-msg-compiler $(top_srcdir)/src/lib/tcp/tcp_messages.mes
else
messages tcp_messages.h tcp_messages.cc:
@echo Messages generation disabled. Configure with --enable-generate-messages to enable it.
endif
# Specify the headers for copying into the installation directory tree.
libkea_tcp_includedir = $(pkgincludedir)/tcp
libkea_tcp_include_HEADERS = \
tcp_messages.h \
tcp_connection.h \
tcp_connection_pool.h \
tcp_listener.h \
tcp_log.h

1
src/lib/tcp/README Normal file
View File

@ -0,0 +1 @@
The tcp library is intended to provide support for TCP server/listeners.

View File

@ -0,0 +1,578 @@
// Copyright (C) 2022 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#if 0
#include <config.h>
#include <asiolink/asio_wrapper.h>
#include <tcp/tcp_connection.h>
#include <tcp/tcp_connection_pool.h>
#include <tcp/tcp_log.h>
#include <tcp/tcp_messages.h>
#include <boost/make_shared.hpp>
#include <functional>
using namespace isc::asiolink;
namespace ph = std::placeholders;
namespace {
/// @brief Maximum size of the HTTP message that can be logged.
///
/// The part of the HTTP message beyond this value is truncated.
constexpr size_t MAX_LOGGED_MESSAGE_SIZE = 1024;
}
namespace isc {
namespace http {
void
TcpConnection::
SocketCallback::operator()(boost::system::error_code ec, size_t length) {
if (ec.value() == boost::asio::error::operation_aborted) {
return;
}
callback_(ec, length);
}
TcpConnection::TcpConnection(asiolink::IOService& io_service,
const TcpAcceptorPtr& acceptor,
const TlsContextPtr& tls_context,
TcpConnectionPool& connection_pool,
const TcpResponseCreatorPtr& response_creator,
const TcpAcceptorCallback& callback,
const long request_timeout,
const long idle_timeout)
: request_timer_(io_service),
request_timeout_(request_timeout),
tls_context_(tls_context),
idle_timeout_(idle_timeout),
tcp_socket_(),
tls_socket_(),
acceptor_(acceptor),
connection_pool_(connection_pool),
response_creator_(response_creator),
acceptor_callback_(callback) {
if (!tls_context) {
tcp_socket_.reset(new asiolink::TCPSocket<SocketCallback>(io_service));
} else {
tls_socket_.reset(new asiolink::TLSSocket<SocketCallback>(io_service,
tls_context));
}
}
TcpConnection::~TcpConnection() {
close();
}
void
TcpConnection::recordParameters(const TcpRequestPtr& request) const {
if (!request) {
// Should never happen.
return;
}
// Record the remote address.
request->setRemote(getRemoteEndpointAddressAsText());
// Record TLS parameters.
if (!tls_socket_) {
return;
}
// The connection uses HTTPS aka HTTP over TLS.
request->setTls(true);
// Record the first commonName of the subjectName of the client
// certificate when wanted.
if (TcpRequest::recordSubject_) {
request->setSubject(tls_socket_->getTlsStream().getSubject());
}
// Record the first commonName of the issuerName of the client
// certificate when wanted.
if (TcpRequest::recordIssuer_) {
request->setIssuer(tls_socket_->getTlsStream().getIssuer());
}
}
void
TcpConnection::shutdownCallback(const boost::system::error_code&) {
tls_socket_->close();
}
void
TcpConnection::shutdown() {
request_timer_.cancel();
if (tcp_socket_) {
tcp_socket_->close();
return;
}
if (tls_socket_) {
// Create instance of the callback to close the socket.
SocketCallback cb(std::bind(&TcpConnection::shutdownCallback,
shared_from_this(),
ph::_1)); // error_code
tls_socket_->shutdown(cb);
return;
}
// Not reachable?
isc_throw(Unexpected, "internal error: unable to shutdown the socket");
}
void
TcpConnection::close() {
request_timer_.cancel();
if (tcp_socket_) {
tcp_socket_->close();
return;
}
if (tls_socket_) {
tls_socket_->close();
return;
}
// Not reachable?
isc_throw(Unexpected, "internal error: unable to close the socket");
}
void
TcpConnection::shutdownConnection() {
try {
LOG_DEBUG(asiolink_logger, isc::log::DBGLVL_TRACE_BASIC,
TCP_CONNECTION_SHUTDOWN)
.arg(getRemoteEndpointAddressAsText());
connection_pool_.shutdown(shared_from_this());
} catch (...) {
LOG_ERROR(asiolink_logger, TCP_CONNECTION_SHUTDOWN_FAILED);
}
}
void
TcpConnection::stopThisConnection() {
try {
LOG_DEBUG(asiolink_logger, isc::log::DBGLVL_TRACE_BASIC,
TCP_CONNECTION_STOP)
.arg(getRemoteEndpointAddressAsText());
connection_pool_.stop(shared_from_this());
} catch (...) {
LOG_ERROR(asiolink_logger, TCP_CONNECTION_STOP_FAILED);
}
}
void
TcpConnection::asyncAccept() {
// Create instance of the callback. It is safe to pass the local instance
// of the callback, because the underlying boost functions make copies
// as needed.
TcpAcceptorCallback cb = std::bind(&TcpConnection::acceptorCallback,
shared_from_this(),
ph::_1); // error
try {
TcpsAcceptorPtr tls_acceptor =
boost::dynamic_pointer_cast<TcpsAcceptor>(acceptor_);
if (!tls_acceptor) {
if (!tcp_socket_) {
isc_throw(Unexpected, "internal error: TCP socket is null");
}
acceptor_->asyncAccept(*tcp_socket_, cb);
} else {
if (!tls_socket_) {
isc_throw(Unexpected, "internal error: TLS socket is null");
}
tls_acceptor->asyncAccept(*tls_socket_, cb);
}
} catch (const std::exception& ex) {
isc_throw(TcpConnectionError, "unable to start accepting TCP "
"connections: " << ex.what());
}
}
void
TcpConnection::doHandshake() {
// Skip the handshake if the socket is not a TLS one.
if (!tls_socket_) {
doRead();
return;
}
// Create instance of the callback. It is safe to pass the local instance
// of the callback, because the underlying boost functions make copies
// as needed.
SocketCallback cb(std::bind(&TcpConnection::handshakeCallback,
shared_from_this(),
ph::_1)); // error
try {
tls_socket_->handshake(cb);
} catch (const std::exception& ex) {
isc_throw(TcpConnectionError, "unable to perform TLS handshake: "
<< ex.what());
}
}
void
TcpConnection::doRead(TransactionPtr transaction) {
try {
TCPEndpoint endpoint;
// Transaction hasn't been created if we are starting to read the
// new request.
if (!transaction) {
transaction = Transaction::create(response_creator_);
recordParameters(transaction->getRequest());
}
// Create instance of the callback. It is safe to pass the local instance
// of the callback, because the underlying std functions make copies
// as needed.
SocketCallback cb(std::bind(&TcpConnection::socketReadCallback,
shared_from_this(),
transaction,
ph::_1, // error
ph::_2)); //bytes_transferred
if (tcp_socket_) {
tcp_socket_->asyncReceive(static_cast<void*>(transaction->getInputBufData()),
transaction->getInputBufSize(),
0, &endpoint, cb);
return;
}
if (tls_socket_) {
tls_socket_->asyncReceive(static_cast<void*>(transaction->getInputBufData()),
transaction->getInputBufSize(),
0, &endpoint, cb);
return;
}
} catch (...) {
stopThisConnection();
}
}
void
TcpConnection::doWrite(TcpConnection::TransactionPtr transaction) {
try {
if (transaction->outputDataAvail()) {
// Create instance of the callback. It is safe to pass the local instance
// of the callback, because the underlying std functions make copies
// as needed.
SocketCallback cb(std::bind(&TcpConnection::socketWriteCallback,
shared_from_this(),
transaction,
ph::_1, // error
ph::_2)); // bytes_transferred
if (tcp_socket_) {
tcp_socket_->asyncSend(transaction->getOutputBufData(),
transaction->getOutputBufSize(),
cb);
return;
}
if (tls_socket_) {
tls_socket_->asyncSend(transaction->getOutputBufData(),
transaction->getOutputBufSize(),
cb);
return;
}
} else {
// The isPersistent() function may throw if the request hasn't
// been created, i.e. the HTTP headers weren't parsed. We catch
// this exception below and close the connection since we're
// unable to tell if the connection should remain persistent
// or not. The default is to close it.
if (!transaction->getRequest()->isPersistent()) {
stopThisConnection();
} else {
// The connection is persistent and we are done sending
// the previous response. Start listening for the next
// requests.
setupIdleTimer();
doRead();
}
}
} catch (...) {
stopThisConnection();
}
}
void
TcpConnection::asyncSendResponse(const ConstTcpResponsePtr& response,
TransactionPtr transaction) {
transaction->setOutputBuf(response->toString());
doWrite(transaction);
}
void
TcpConnection::acceptorCallback(const boost::system::error_code& ec) {
if (!acceptor_->isOpen()) {
return;
}
if (ec) {
stopThisConnection();
}
acceptor_callback_(ec);
if (!ec) {
if (!tls_context_) {
LOG_DEBUG(asiolink_logger, isc::log::DBGLVL_TRACE_DETAIL,
TCP_REQUEST_RECEIVE_START)
.arg(getRemoteEndpointAddressAsText())
.arg(static_cast<unsigned>(request_timeout_/1000));
} else {
LOG_DEBUG(asiolink_logger, isc::log::DBGLVL_TRACE_DETAIL,
TCP_CONNECTION_HANDSHAKE_START)
.arg(getRemoteEndpointAddressAsText())
.arg(static_cast<unsigned>(request_timeout_/1000));
}
setupRequestTimer();
doHandshake();
}
}
void
TcpConnection::handshakeCallback(const boost::system::error_code& ec) {
if (ec) {
LOG_INFO(asiolink_logger, TCP_CONNECTION_HANDSHAKE_FAILED)
.arg(getRemoteEndpointAddressAsText())
.arg(ec.message());
stopThisConnection();
} else {
LOG_DEBUG(asiolink_logger, isc::log::DBGLVL_TRACE_DETAIL,
HTTPS_REQUEST_RECEIVE_START)
.arg(getRemoteEndpointAddressAsText());
doRead();
}
}
void
TcpConnection::socketReadCallback(TcpConnection::TransactionPtr transaction,
boost::system::error_code ec, size_t length) {
if (ec) {
// IO service has been stopped and the connection is probably
// going to be shutting down.
if (ec.value() == boost::asio::error::operation_aborted) {
return;
// EWOULDBLOCK and EAGAIN are special cases. Everything else is
// treated as fatal error.
} else if ((ec.value() != boost::asio::error::try_again) &&
(ec.value() != boost::asio::error::would_block)) {
stopThisConnection();
// We got EWOULDBLOCK or EAGAIN which indicate that we may be able to
// read something from the socket on the next attempt. Just make sure
// we don't try to read anything now in case there is any garbage
// passed in length.
} else {
length = 0;
}
}
// Receiving is in progress, so push back the timeout.
setupRequestTimer(transaction);
if (length != 0) {
LOG_DEBUG(asiolink_logger, isc::log::DBGLVL_TRACE_DETAIL_DATA,
TCP_DATA_RECEIVED)
.arg(length)
.arg(getRemoteEndpointAddressAsText());
transaction->getParser()->postBuffer(static_cast<void*>(transaction->getInputBufData()),
length);
transaction->getParser()->poll();
}
if (transaction->getParser()->needData()) {
// The parser indicates that the some part of the message being
// received is still missing, so continue to read.
doRead(transaction);
} else {
try {
// The whole message has been received, so let's finalize it.
transaction->getRequest()->finalize();
LOG_DEBUG(asiolink_logger, isc::log::DBGLVL_TRACE_BASIC,
TCP_CLIENT_REQUEST_RECEIVED)
.arg(getRemoteEndpointAddressAsText());
LOG_DEBUG(asiolink_logger, isc::log::DBGLVL_TRACE_BASIC_DATA,
TCP_CLIENT_REQUEST_RECEIVED_DETAILS)
.arg(getRemoteEndpointAddressAsText())
.arg(transaction->getParser()->getBufferAsString(MAX_LOGGED_MESSAGE_SIZE));
} catch (const std::exception& ex) {
LOG_DEBUG(asiolink_logger, isc::log::DBGLVL_TRACE_BASIC,
TCP_BAD_CLIENT_REQUEST_RECEIVED)
.arg(getRemoteEndpointAddressAsText())
.arg(ex.what());
LOG_DEBUG(asiolink_logger, isc::log::DBGLVL_TRACE_BASIC_DATA,
TCP_BAD_CLIENT_REQUEST_RECEIVED_DETAILS)
.arg(getRemoteEndpointAddressAsText())
.arg(transaction->getParser()->getBufferAsString(MAX_LOGGED_MESSAGE_SIZE));
}
// Don't want to timeout if creation of the response takes long.
request_timer_.cancel();
// Create the response from the received request using the custom
// response creator.
TcpResponsePtr response = response_creator_->createTcpResponse(transaction->getRequest());
LOG_DEBUG(asiolink_logger, isc::log::DBGLVL_TRACE_BASIC,
TCP_SERVER_RESPONSE_SEND)
.arg(response->toBriefString())
.arg(getRemoteEndpointAddressAsText());
LOG_DEBUG(asiolink_logger, isc::log::DBGLVL_TRACE_BASIC_DATA,
TCP_SERVER_RESPONSE_SEND_DETAILS)
.arg(getRemoteEndpointAddressAsText())
.arg(TcpMessageParserBase::logFormatTcpMessage(response->toString(),
MAX_LOGGED_MESSAGE_SIZE));
// Response created. Activate the timer again.
setupRequestTimer(transaction);
// Start sending the response.
asyncSendResponse(response, transaction);
}
}
void
TcpConnection::socketWriteCallback(TcpConnection::TransactionPtr transaction,
boost::system::error_code ec, size_t length) {
if (ec) {
// IO service has been stopped and the connection is probably
// going to be shutting down.
if (ec.value() == boost::asio::error::operation_aborted) {
return;
// EWOULDBLOCK and EAGAIN are special cases. Everything else is
// treated as fatal error.
} else if ((ec.value() != boost::asio::error::try_again) &&
(ec.value() != boost::asio::error::would_block)) {
stopThisConnection();
// We got EWOULDBLOCK or EAGAIN which indicate that we may be able to
// read something from the socket on the next attempt.
} else {
// Sending is in progress, so push back the timeout.
setupRequestTimer(transaction);
doWrite(transaction);
}
}
// Since each transaction has its own output buffer, it is not really
// possible that the number of bytes written is larger than the size
// of the buffer. But, let's be safe and set the length to the size
// of the buffer if that unexpected condition occurs.
if (length > transaction->getOutputBufSize()) {
length = transaction->getOutputBufSize();
}
if (length <= transaction->getOutputBufSize()) {
// Sending is in progress, so push back the timeout.
setupRequestTimer(transaction);
}
// Eat the 'length' number of bytes from the output buffer and only
// leave the part of the response that hasn't been sent.
transaction->consumeOutputBuf(length);
// Schedule the write of the unsent data.
doWrite(transaction);
}
void
TcpConnection::setupRequestTimer(TransactionPtr transaction) {
// Pass raw pointer rather than shared_ptr to this object,
// because IntervalTimer already passes shared pointer to the
// IntervalTimerImpl to make sure that the callback remains
// valid.
request_timer_.setup(std::bind(&TcpConnection::requestTimeoutCallback,
this, transaction),
request_timeout_, IntervalTimer::ONE_SHOT);
}
void
TcpConnection::setupIdleTimer() {
request_timer_.setup(std::bind(&TcpConnection::idleTimeoutCallback,
this),
idle_timeout_, IntervalTimer::ONE_SHOT);
}
void
TcpConnection::requestTimeoutCallback(TransactionPtr transaction) {
LOG_DEBUG(asiolink_logger, isc::log::DBGLVL_TRACE_DETAIL,
TCP_CLIENT_REQUEST_TIMEOUT_OCCURRED)
.arg(getRemoteEndpointAddressAsText());
// We need to differentiate the transactions between a normal response and the
// timeout. We create new transaction from the current transaction. It is
// to preserve the request we're responding to.
auto spawned_transaction = Transaction::spawn(response_creator_, transaction);
// The new transaction inherits the request from the original transaction
// if such transaction exists.
auto request = spawned_transaction->getRequest();
// Depending on when the timeout occurred, the TCP version of the request
// may or may not be available. Therefore we check if the HTTP version is
// set in the request. If it is not available, we need to create a dummy
// request with the default HTTP/1.0 version. This version will be used
// in the response.
if (request->context()->http_version_major_ == 0) {
request.reset(new TcpRequest(TcpRequest::Method::HTTP_POST, "/",
TcpVersion::HTTP_10(),
HostTcpHeader("dummy")));
request->finalize();
}
// Create the timeout response.
TcpResponsePtr response =
response_creator_->createStockTcpResponse(request,
TcpStatusCode::REQUEST_TIMEOUT);
// Send the HTTP 408 status.
asyncSendResponse(response, spawned_transaction);
}
void
TcpConnection::idleTimeoutCallback() {
LOG_DEBUG(asiolink_logger, isc::log::DBGLVL_TRACE_DETAIL,
TCP_IDLE_CONNECTION_TIMEOUT_OCCURRED)
.arg(getRemoteEndpointAddressAsText());
// In theory we should shutdown first and stop/close after but
// it is better to put the connection management responsibility
// on the client... so simply drop idle connections.
stopThisConnection();
}
std::string
TcpConnection::getRemoteEndpointAddressAsText() const {
try {
if (tcp_socket_) {
if (tcp_socket_->getASIOSocket().is_open()) {
return (tcp_socket_->getASIOSocket().remote_endpoint().address().to_string());
}
} else if (tls_socket_) {
if (tls_socket_->getASIOSocket().is_open()) {
return (tls_socket_->getASIOSocket().remote_endpoint().address().to_string());
}
}
} catch (...) {
}
return ("(unknown address)");
}
} // end of namespace isc::http
} // end of namespace isc
#endif

View File

@ -0,0 +1,281 @@
// Copyright (C) 2022 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef TCP_CONNECTION_H
#define TCP_CONNECTION_H
#include <asiolink/interval_timer.h>
#include <asiolink/io_service.h>
#include <tcp/tcp_connection_acceptor.h>
#include <boost/enable_shared_from_this.hpp>
#include <boost/system/error_code.hpp>
#include <boost/shared_ptr.hpp>
#include <array>
#include <functional>
#include <string>
namespace isc {
namespace tcp {
/// @todo TKM these are place holders while I think output how it should work
typedef util::InputBuffer TcpRequest;
typedef boost::shared_ptr<TcpRequest> TcpRequestPtr;
typedef util::OutputBuffer TcpResponse;
typedef boost::shared_ptr<TcpResponse> TcpResponsePtr;
/// @brief Generic error reported within @ref TcpConnection class.
class TcpConnectionError : public Exception {
public:
TcpConnectionError(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) { };
};
/// @brief Forward declaration to the @ref TcpConnectionPool.
///
/// This declaration is needed because we don't include the header file
/// declaring @ref TcpConnectionPool to avoid circular inclusion.
class TcpConnectionPool;
class TcpConnection;
/// @brief Pointer to the @ref TcpConnection.
typedef boost::shared_ptr<TcpConnection> TcpConnectionPtr;
/// @brief Accepts and handles a single TCP connection.
class TcpConnection : public boost::enable_shared_from_this<TcpConnection> {
private:
/// @brief Type of the function implementing a callback invoked by the
/// @c SocketCallback functor.
typedef std::function<void(boost::system::error_code ec, size_t length)>
SocketCallbackFunction;
/// @brief Functor associated with the socket object.
///
/// This functor calls a callback function specified in the constructor.
class SocketCallback {
public:
/// @brief Constructor.
///
/// @param socket_callback Callback to be invoked by the functor upon
/// an event associated with the socket.
SocketCallback(SocketCallbackFunction socket_callback)
: callback_(socket_callback) {
}
/// @brief Operator called when event associated with a socket occurs.
///
/// This operator returns immediately when received error code is
/// @c boost::system::error_code is equal to
/// @c boost::asio::error::operation_aborted, i.e. the callback is not
/// invoked.
///
/// @param ec Error code.
/// @param length Data length.
void operator()(boost::system::error_code ec, size_t length = 0);
private:
/// @brief Supplied callback.
SocketCallbackFunction callback_;
};
public:
/// @brief Constructor.
///
/// @param io_service IO service to be used by the connection.
/// @param acceptor Pointer to the TCP acceptor object used to listen for
/// new TCP connections.
/// @param tls_context TLS context.
/// @param connection_pool Connection pool in which this connection is
/// stored.
/// @param response_creator Pointer to the response creator object used to
/// create TCP response from the TCP request received.
/// @param callback Callback invoked when new connection is accepted.
/// @param request_timeout Configured timeout for a TCP request.
/// @param idle_timeout Timeout after which persistent TCP connection is
/// closed by the server.
TcpConnection(asiolink::IOService& io_service,
const TcpConnectionAcceptorPtr& acceptor,
const asiolink::TlsContextPtr& tls_context,
TcpConnectionPool& connection_pool,
const TcpConnectionAcceptorCallback& callback,
const long idle_timeout);
/// @brief Destructor.
///
/// Closes current connection.
virtual ~TcpConnection();
/// @brief Asynchronously accepts new connection.
///
/// When the connection is established successfully, the timeout timer is
/// setup and the asynchronous handshake with client is performed.
void asyncAccept();
/// @brief Shutdown the socket.
void shutdown();
/// @brief Closes the socket.
void close();
/// @brief Asynchronously performs TLS handshake.
///
/// When the handshake is performed successfully or skipped because TLS
/// was not enabled, the asynchronous read from the socket is started.
void doHandshake();
/// @brief Starts asynchronous read from the socket.
///
/// The data received over the socket are supplied to the TCP parser until
/// the parser signals that the entire request has been received or until
/// the parser signals an error. In the former case the server creates an
/// TCP response using supplied response creator object.
///
/// In case of error the connection is stopped.
///
/// @param request Pointer to the request for which the read
/// operation should be performed. It defaults to null pointer which
/// indicates that this function should create new request.
void doRead();
protected:
/// @brief Starts asynchronous write to the socket.
///
/// The @c output_buf_ must contain the data to be sent.
///
/// In case of error the connection is stopped.
///
/// @param request Pointer to the request for which the write
/// operation should be performed.
void doWrite();
/// @brief Sends TCP response asynchronously.
///
/// Internally it calls @ref TcpConnection::doWrite to send the data.
///
/// @param response Pointer to the TCP response to be sent.
/// @param request Pointer to the request.
void asyncSendResponse(const TcpResponsePtr& response);
/// @brief Local callback invoked when new connection is accepted.
///
/// It invokes external (supplied via constructor) acceptor callback. If
/// the acceptor is not opened it returns immediately. If the connection
/// is accepted successfully the @ref TcpConnection::doRead or
/// @ref TcpConnection::doHandshake is called.
///
/// @param ec Error code.
void acceptorCallback(const boost::system::error_code& ec);
/// @brief Local callback invoked when TLS handshake is performed.
///
/// If the handshake is performed successfully the @ref
/// TcpConnection::doRead is called.
///
/// @param ec Error code.
void handshakeCallback(const boost::system::error_code& ec);
/// @brief Callback invoked when new data is received over the socket.
///
/// This callback supplies the data to the TCP parser and continues
/// parsing. When the parser signals end of the TCP request the callback
/// prepares a response and starts asynchronous send over the socket.
///
/// @param request Pointer to the request for which the callback
/// is invoked.
/// @param ec Error code.
/// @param length Length of the received data.
void socketReadCallback(TcpRequestPtr request,
boost::system::error_code ec,
size_t length);
/// @brief Callback invoked when data is sent over the socket.
///
/// @param request Pointer to the request for which the callback
/// is invoked.
/// @param ec Error code.
/// @param length Length of the data sent.
virtual void socketWriteCallback(TcpRequestPtr request,
boost::system::error_code ec,
size_t length);
/// @brief Callback invoked when TLS shutdown is performed.
///
/// The TLS socket is unconditionally closed but the callback is called
/// only when the peer has answered so the connection should be
/// explicitly closed in all cases, i.e. do not rely on this handler.
///
/// @param ec Error code (ignored).
void shutdownCallback(const boost::system::error_code& ec);
/// @brief Reset timer for detecting request timeouts.
///
/// @param request Pointer to the request to be guarded by the timeout.
void setupRequestTimer(TcpRequestPtr request = TcpRequestPtr());
/// @brief Reset timer for detecting idle timeout in persistent connections.
void setupIdleTimer();
/// @brief Callback invoked when the TCP Request Timeout occurs.
///
/// This callback creates TCP response with Request Timeout error code
/// and sends it to the client.
///
/// @param request Pointer to the request for which timeout occurs.
void requestTimeoutCallback(TcpRequestPtr request);
void idleTimeoutCallback();
/// @brief Shuts down current connection.
///
/// Copied from the next method @ref stopThisConnection
void shutdownConnection();
/// @brief Stops current connection.
void stopThisConnection();
/// @brief returns remote address in textual form
std::string getRemoteEndpointAddressAsText() const;
/// @brief Timer used to detect Request Timeout.
asiolink::IntervalTimer request_timer_;
/// @brief Configured Request Timeout in milliseconds.
long request_timeout_;
/// @brief TLS context.
asiolink::TlsContextPtr tls_context_;
/// @brief Timeout after which the persistent TCP connection is shut
/// down by the server.
long idle_timeout_;
/// @brief TCP socket used by this connection.
std::unique_ptr<asiolink::TCPSocket<SocketCallback> > tcp_socket_;
/// @brief TLS socket used by this connection.
std::unique_ptr<asiolink::TLSSocket<SocketCallback> > tls_socket_;
/// @brief Pointer to the TCP acceptor used to accept new connections.
TcpConnectionAcceptorPtr acceptor_;
/// @brief Connection pool holding this connection.
TcpConnectionPool& connection_pool_;
/// @brief External TCP acceptor callback.
TcpConnectionAcceptorCallback acceptor_callback_;
};
} // end of namespace isc::tcp
} // end of namespace isc
#endif

View File

@ -0,0 +1,38 @@
// Copyright (C) 2022 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef HTTP_ACCEPTOR_H
#define HTTP_ACCEPTOR_H
#include <asiolink/tcp_acceptor.h>
#include <asiolink/tls_acceptor.h>
#include <boost/shared_ptr.hpp>
#include <boost/system/system_error.hpp>
#include <functional>
namespace isc {
namespace tcp {
/// @brief Type of the callback for the TCP acceptor used in this library.
typedef std::function<void(const boost::system::error_code&)> TcpConnectionAcceptorCallback;
/// @brief Type of the TCP acceptor used in this library.
typedef asiolink::TCPAcceptor<TcpConnectionAcceptorCallback> TcpConnectionAcceptor;
/// @brief Type of shared pointer to TCP acceptors.
typedef boost::shared_ptr<TcpConnectionAcceptor> TcpConnectionAcceptorPtr;
/// @brief Type of the TLS acceptor used in this library.
typedef asiolink::TLSAcceptor<TcpConnectionAcceptorCallback> TlsConnectionAcceptor;
/// @brief Type of shared pointer to TLS acceptors.
typedef boost::shared_ptr<TlsConnectionAcceptor> TlsConnectionAcceptorPtr;
} // end of namespace isc::tcp
} // end of namespace isc
#endif

View File

@ -0,0 +1,78 @@
// Copyright (C) 2022 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#if 0
#include <config.h>
#include <asiolink/asio_wrapper.h>
#include <tcp/tcp_connection_pool.h>
#include <util/multi_threading_mgr.h>
namespace isc {
namespace tcp {
void
TcpConnectionPool::start(const TcpConnectionPtr& connection) {
if (util::MultiThreadingMgr::instance().getMode()) {
std::lock_guard<std::mutex> lk(mutex_);
connections_.insert(connections_.end(), connection);
} else {
connections_.insert(connections_.end(), connection);
}
connection->asyncAccept();
}
void
TcpConnectionPool::stop(const TcpConnectionPtr& connection) {
if (util::MultiThreadingMgr::instance().getMode()) {
std::lock_guard<std::mutex> lk(mutex_);
connections_.remove(connection);
} else {
connections_.remove(connection);
}
connection->close();
}
void
TcpConnectionPool::shutdown(const TcpConnectionPtr& connection) {
if (util::MultiThreadingMgr::instance().getMode()) {
std::lock_guard<std::mutex> lk(mutex_);
connections_.remove(connection);
} else {
connections_.remove(connection);
}
connection->shutdown();
}
void
TcpConnectionPool::stopAll() {
if (util::MultiThreadingMgr::instance().getMode()) {
std::lock_guard<std::mutex> lk(mutex_);
stopAllInternal();
} else {
stopAllInternal();
}
}
void
TcpConnectionPool::stopAllInternal() {
for (auto connection = connections_.begin();
connection != connections_.end();
++connection) {
(*connection)->close();
}
connections_.clear();
}
}
}
#endif

View File

@ -0,0 +1,78 @@
// Copyright (C) 2022 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef TCP_CONNECTION_POOL_H
#define TCP_CONNECTION_POOL_H
#include <tcp/tcp_connection.h>
#include <list>
#include <mutex>
namespace isc {
namespace tcp {
/// @brief Pool of active TCP connections.
///
/// The TCP server is designed to handle many connections simultaneously.
/// The communication between the client and the server may take long time
/// and the server must be able to react on other events while the communication
/// with the clients is in progress. Thus, the server must track active
/// connections and gracefully close them when needed. An obvious case when the
/// connections must be terminated by the server is when the shutdown signal
/// is received.
///
/// This object is a simple container for the server connections which provides
/// means to terminate them on request.
class TcpConnectionPool {
public:
/// @brief Start new connection.
///
/// The connection is inserted to the pool and the
/// @ref TcpConnection::asyncAccept is invoked.
///
/// @param connection Pointer to the new connection.
void start(const TcpConnectionPtr& connection);
/// @brief Removes a connection from the pool and shutdown it.
///
/// Shutdown is specific to TLS and is a first part of graceful close (note it is
/// NOT the same as TCP shutdown system call).
///
/// @note if the TLS connection stalls e.g. the peer does not try I/O
/// on it the connection has to be explicitly stopped.
///
/// @param connection Pointer to the connection.
void shutdown(const TcpConnectionPtr& connection);
/// @brief Removes a connection from the pool and stops it.
///
/// @param connection Pointer to the connection.
void stop(const TcpConnectionPtr& connection);
/// @brief Stops all connections and removes them from the pool.
void stopAll();
protected:
/// @brief Stops all connections and removes them from the pool.
///
/// Must be called from with a thread-safe context.
void stopAllInternal();
/// @brief Set of connections.
std::list<TcpConnectionPtr> connections_;
/// @brief Mutex to protect the internal state.
std::mutex mutex_;
};
}
}
#endif

110
src/lib/tcp/tcp_listener.cc Normal file
View File

@ -0,0 +1,110 @@
// Copyright (C) 2022 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <config.h>
#include <asiolink/asio_wrapper.h>
#include <tcp/tcp_listener.h>
using namespace isc::asiolink;
namespace ph = std::placeholders;
namespace isc {
namespace tcp {
TcpListener::TcpListener(IOService& io_service,
const IOAddress& server_address,
const unsigned short server_port,
const TlsContextPtr& tls_context,
const long idle_timeout)
: io_service_(io_service), tls_context_(tls_context), acceptor_(),
endpoint_(), connections_(), idle_timeout_(idle_timeout) {
// Create the TCP or TLS acceptor.
// @todo TKM - hmmm.... need to understand this better..
if (!tls_context) {
acceptor_.reset(new TcpConnectionAcceptor(io_service));
} else {
acceptor_.reset(new TlsConnectionAcceptor(io_service));
}
// Try creating an endpoint. This may cause exceptions.
try {
endpoint_.reset(new TCPEndpoint(server_address, server_port));
} catch (...) {
isc_throw(TcpListenerError, "unable to create TCP endpoint for "
<< server_address << ":" << server_port);
}
// Idle persistent connection timeout is signed and must be greater than 0.
if (idle_timeout_ <= 0) {
isc_throw(TcpListenerError, "Invalid desired TCP idle persistent connection"
" timeout " << idle_timeout_);
}
}
const TCPEndpoint&
TcpListener::getEndpoint() const {
return (*endpoint_);
}
void
TcpListener::start() {
try {
acceptor_->open(*endpoint_);
acceptor_->setOption(TcpConnectionAcceptor::ReuseAddress(true));
acceptor_->bind(*endpoint_);
acceptor_->listen();
} catch (const boost::system::system_error& ex) {
stop();
isc_throw(TcpListenerError, "unable to setup TCP acceptor for "
"listening for incoming TCP clients: " << ex.what());
}
accept();
}
void
TcpListener::stop() {
connections_.stopAll();
acceptor_->close();
}
void
TcpListener::accept() {
TcpConnectionAcceptorCallback acceptor_callback =
std::bind(&TcpListener::acceptHandler, this, ph::_1);
TcpConnectionPtr conn = createConnection(acceptor_callback);
// Add this new connection to the pool.
connections_.start(conn);
}
void
TcpListener::acceptHandler(const boost::system::error_code&) {
// The new connection has arrived. Set the acceptor to continue
// accepting new connections.
accept();
}
TcpConnectionPtr
TcpListener::createConnection(const TcpConnectionAcceptorCallback& /* callback */) {
#if 1
isc_throw(NotImplemented, "TcpListener::createConnection:");
#else
TcpConnectionPtr
/// @todo TKM - I think what we want is to define TcpConnectionFactory
/// instead of a response creator. Let TcpListener accept a factory
/// for that, which is used here to create for BLQ an LeaseQueryConnection
return (connection_factory_(io_service_, acceptor_, tls_context_,
connections_, callback, idle_timeout_));
#endif
}
} // end of namespace isc::tcp
} // end of namespace isc

122
src/lib/tcp/tcp_listener.h Normal file
View File

@ -0,0 +1,122 @@
// Copyright (C) 2022 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef TCP_LISTENER_H
#define TCP_LISTENER_H
#include <asiolink/io_service.h>
#include <asiolink/io_address.h>
#include <asiolink/tcp_endpoint.h>
#include <tcp/tcp_connection_pool.h>
#include <boost/scoped_ptr.hpp>
namespace isc {
namespace tcp {
/// @brief A generic error raised by the @ref TcpListener class.
class TcpListenerError : public Exception {
public:
TcpListenerError(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) { };
};
/// @brief ementation of the @ref TcpListener.
class TcpListener {
public:
/// @brief Constructor.
///
/// This constructor creates new server endpoint using the specified IP
/// address and port. It also validates other specified parameters.
///
/// This constructor does not start accepting new connections! To start
/// accepting connections run @ref TcpListener::start.
///
/// @param io_service IO service to be used by the listener.
/// @param server_address Address on which the TCP service should run.
/// @param server_port Port number on which the TCP service should run.
/// @param tls_context TLS context.
/// @param idle_timeout Timeout after which an idle persistent TCP
/// connection is closed by the server.
///
/// @throw TcpListenerError when any of the specified parameters is
/// invalid.
TcpListener(asiolink::IOService& io_service,
const asiolink::IOAddress& server_address,
const unsigned short server_port,
const asiolink::TlsContextPtr& tls_context,
const long idle_timeout);
/// @brief Virtual destructor.
virtual ~TcpListener() {
}
/// @brief Returns reference to the current listener endpoint.
const asiolink::TCPEndpoint& getEndpoint() const;
/// @brief Starts accepting new connections.
///
/// This method starts accepting and handling new TCP connections on
/// the IP address and port number specified in the constructor.
///
/// If the method is invoked successfully, it must not be invoked again
/// until @ref TcpListener::stop is called.
///
/// @throw TcpListenerError if an error occurred.
void start();
/// @brief Stops all active connections and shuts down the service.
void stop();
protected:
/// @brief Creates @ref TcpConnection instance and adds it to the
/// pool of active connections.
///
/// The next accepted connection will be handled by this instance.
void accept();
/// @brief Callback invoked when the new connection is accepted.
///
/// It calls @c TcpListener::accept to create new @c TcpConnection
/// instance.
///
/// @param ec Error code passed to the handler. This is currently ignored.
void acceptHandler(const boost::system::error_code& ec);
/// @brief Creates an instance of the @c TcpConnection.
///
/// This method is virtual so as it can be overridden when customized
/// connections are to be used, e.g. in case of unit testing.
///
/// @return Pointer to the created connection.
virtual TcpConnectionPtr createConnection(const TcpConnectionAcceptorCallback& callback);
/// @brief Reference to the IO service.
asiolink::IOService& io_service_;
/// @brief TLS context.
asiolink::TlsContextPtr tls_context_;
/// @brief Acceptor instance.
TcpConnectionAcceptorPtr acceptor_;
/// @brief Pointer to the endpoint representing IP address and port on
/// which the service is running.
boost::scoped_ptr<asiolink::TCPEndpoint> endpoint_;
/// @brief Pool of active connections.
TcpConnectionPool connections_;
/// @brief Timeout after which idle persistent connection is closed by
/// the server.
long idle_timeout_;
};
} // end of namespace isc::asiolink
} // end of namespace isc
#endif // TCP_LISTENER_H

21
src/lib/tcp/tcp_log.cc Normal file
View File

@ -0,0 +1,21 @@
// Copyright (C) 2022 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at tcp://mozilla.org/MPL/2.0/.
/// Defines the logger used by the libkea-tcp library.
#include <config.h>
#include <tcp/tcp_log.h>
namespace isc {
namespace tcp {
/// @brief Defines the logger used within libkea-tcp library.
isc::log::Logger tcp_logger("tcp");
} // namespace tcp
} // namespace isc

23
src/lib/tcp/tcp_log.h Normal file
View File

@ -0,0 +1,23 @@
// Copyright (C) 2022 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at tcp://mozilla.org/MPL/2.0/.
#ifndef TCP_LOG_H
#define TCP_LOG_H
#include <log/logger_support.h>
#include <log/macros.h>
#include <tcp/tcp_messages.h>
namespace isc {
namespace tcp {
/// Define the logger used within libkea-tcp library.
extern isc::log::Logger tcp_logger;
} // namespace tcp
} // namespace isc
#endif // TCP_LOG_H

View File

@ -0,0 +1,55 @@
// File created from ../../../src/lib/tcp/tcp_messages.mes
#include <cstddef>
#include <log/message_types.h>
#include <log/message_initializer.h>
namespace isc {
namespace asiolink {
extern const isc::log::MessageID TCP_BAD_CLIENT_REQUEST_RECEIVED = "TCP_BAD_CLIENT_REQUEST_RECEIVED";
extern const isc::log::MessageID TCP_CLIENT_REQUEST_RECEIVED = "TCP_CLIENT_REQUEST_RECEIVED";
extern const isc::log::MessageID TCP_CLIENT_REQUEST_RECEIVED_DETAILS = "TCP_CLIENT_REQUEST_RECEIVED_DETAILS";
extern const isc::log::MessageID TCP_CONNECTION_CLOSE_CALLBACK_FAILED = "TCP_CONNECTION_CLOSE_CALLBACK_FAILED";
extern const isc::log::MessageID TCP_CONNECTION_HANDSHAKE_FAILED = "TCP_CONNECTION_HANDSHAKE_FAILED";
extern const isc::log::MessageID TCP_CONNECTION_HANDSHAKE_START = "TCP_CONNECTION_HANDSHAKE_START";
extern const isc::log::MessageID TCP_CONNECTION_SHUTDOWN = "TCP_CONNECTION_SHUTDOWN";
extern const isc::log::MessageID TCP_CONNECTION_SHUTDOWN_FAILED = "TCP_CONNECTION_SHUTDOWN_FAILED";
extern const isc::log::MessageID TCP_CONNECTION_STOP = "TCP_CONNECTION_STOP";
extern const isc::log::MessageID TCP_CONNECTION_STOP_FAILED = "TCP_CONNECTION_STOP_FAILED";
extern const isc::log::MessageID TCP_DATA_RECEIVED = "TCP_DATA_RECEIVED";
extern const isc::log::MessageID TCP_IDLE_CONNECTION_TIMEOUT_OCCURRED = "TCP_IDLE_CONNECTION_TIMEOUT_OCCURRED";
extern const isc::log::MessageID TCP_PREMATURE_CONNECTION_TIMEOUT_OCCURRED = "TCP_PREMATURE_CONNECTION_TIMEOUT_OCCURRED";
extern const isc::log::MessageID TCP_REQUEST_RECEIVE_START = "TCP_REQUEST_RECEIVE_START";
extern const isc::log::MessageID TCP_SERVER_RESPONSE_SEND = "TCP_SERVER_RESPONSE_SEND";
extern const isc::log::MessageID TCP_SERVER_RESPONSE_SEND_DETAILS = "TCP_SERVER_RESPONSE_SEND_DETAILS";
} // namespace asiolink
} // namespace isc
namespace {
const char* values[] = {
"TCP_BAD_CLIENT_REQUEST_RECEIVED", "bad request received from %1: %2",
"TCP_CLIENT_REQUEST_RECEIVED", "received TCP request from %1",
"TCP_CLIENT_REQUEST_RECEIVED_DETAILS", "detailed information about well-formed request received from %1:\n%2",
"TCP_CONNECTION_CLOSE_CALLBACK_FAILED", "Connection close callback threw an exception",
"TCP_CONNECTION_HANDSHAKE_FAILED", "TLS handshake with %1 failed with %2",
"TCP_CONNECTION_HANDSHAKE_START", "start TLS handshake with %1 with timeout %2",
"TCP_CONNECTION_SHUTDOWN", "shutting down TCP connection from %1",
"TCP_CONNECTION_SHUTDOWN_FAILED", "shutting down TCP connection failed",
"TCP_CONNECTION_STOP", "stopping TCP connection from %1",
"TCP_CONNECTION_STOP_FAILED", "stopping TCP connection failed",
"TCP_DATA_RECEIVED", "received %1 bytes from %2",
"TCP_IDLE_CONNECTION_TIMEOUT_OCCURRED", "closing persistent connection with %1 as a result of a timeout",
"TCP_PREMATURE_CONNECTION_TIMEOUT_OCCURRED", "premature connection timeout occurred: in transaction ? %1, transid: %2, current_transid: %3",
"TCP_REQUEST_RECEIVE_START", "start receiving request from %1 with timeout %2",
"TCP_SERVER_RESPONSE_SEND", "sending TCP response %1 to %2",
"TCP_SERVER_RESPONSE_SEND_DETAILS", "detailed information about response sent to %1:\n%2",
NULL
};
const isc::log::MessageInitializer initializer(values);
} // Anonymous namespace

View File

@ -0,0 +1,31 @@
// File created from ../../../src/lib/tcp/tcp_messages.mes
#ifndef TCP_MESSAGES_H
#define TCP_MESSAGES_H
#include <log/message_types.h>
namespace isc {
namespace asiolink {
extern const isc::log::MessageID TCP_BAD_CLIENT_REQUEST_RECEIVED;
extern const isc::log::MessageID TCP_CLIENT_REQUEST_RECEIVED;
extern const isc::log::MessageID TCP_CLIENT_REQUEST_RECEIVED_DETAILS;
extern const isc::log::MessageID TCP_CONNECTION_CLOSE_CALLBACK_FAILED;
extern const isc::log::MessageID TCP_CONNECTION_HANDSHAKE_FAILED;
extern const isc::log::MessageID TCP_CONNECTION_HANDSHAKE_START;
extern const isc::log::MessageID TCP_CONNECTION_SHUTDOWN;
extern const isc::log::MessageID TCP_CONNECTION_SHUTDOWN_FAILED;
extern const isc::log::MessageID TCP_CONNECTION_STOP;
extern const isc::log::MessageID TCP_CONNECTION_STOP_FAILED;
extern const isc::log::MessageID TCP_DATA_RECEIVED;
extern const isc::log::MessageID TCP_IDLE_CONNECTION_TIMEOUT_OCCURRED;
extern const isc::log::MessageID TCP_PREMATURE_CONNECTION_TIMEOUT_OCCURRED;
extern const isc::log::MessageID TCP_REQUEST_RECEIVE_START;
extern const isc::log::MessageID TCP_SERVER_RESPONSE_SEND;
extern const isc::log::MessageID TCP_SERVER_RESPONSE_SEND_DETAILS;
} // namespace asiolink
} // namespace isc
#endif // TCP_MESSAGES_H

View File

@ -0,0 +1,96 @@
# Copyright (C) 2022 Internet Systems Consortium, Inc. ("ISC")
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
$NAMESPACE isc::asiolink
% TCP_BAD_CLIENT_REQUEST_RECEIVED bad request received from %1: %2
This debug message is issued when an TCP client sends malformed request to
the server. This includes TCP requests using unexpected content types,
including malformed JSON etc. The first argument specifies an address of
the remote endpoint which sent the request. The second argument provides
a detailed error message.
% TCP_CLIENT_REQUEST_RECEIVED received TCP request from %1
This debug message is issued when the server finished receiving a TCP
request from the remote endpoint. The address of the remote endpoint is
specified as an argument.
% TCP_CLIENT_REQUEST_RECEIVED_DETAILS detailed information about well-formed request received from %1:\n%2
This debug message is issued when the TCP server receives a well-formed
request. It includes detailed information about the received request. The
first argument specifies an address of the remote endpoint which sent the
request. The second argument provides the request in the textual format.
The request is truncated by the logger if it is too large to be printed.
% TCP_CONNECTION_CLOSE_CALLBACK_FAILED Connection close callback threw an exception
This is an error message emitted when the close connection callback
registered on the connection failed unexpectedly. This is a programmatic
error that should be submitted as a bug.
% TCP_CONNECTION_HANDSHAKE_FAILED TLS handshake with %1 failed with %2
This information message is issued when the TLS handshake failed at the
server side. The client address and the error message are displayed.
% TCP_CONNECTION_HANDSHAKE_START start TLS handshake with %1 with timeout %2
This debug message is issued when the server starts the TLS handshake
with the remote endpoint. The first argument specifies the address
of the remote endpoint. The second argument specifies request timeout in
seconds.
% TCP_CONNECTION_SHUTDOWN shutting down TCP connection from %1
This debug message is issued when one of the TCP connections is shut down.
The connection can be stopped as a result of an error or after the
successful message exchange with a client.
% TCP_CONNECTION_SHUTDOWN_FAILED shutting down TCP connection failed
This error message is issued when an error occurred during shutting down
a TCP connection with a client.
% TCP_CONNECTION_STOP stopping TCP connection from %1
This debug message is issued when one of the TCP connections is stopped.
The connection can be stopped as a result of an error or after the
successful message exchange with a client.
% TCP_CONNECTION_STOP_FAILED stopping TCP connection failed
This error message is issued when an error occurred during closing a
TCP connection with a client.
% TCP_DATA_RECEIVED received %1 bytes from %2
This debug message is issued when the server receives a chunk of data from
the remote endpoint. This may include the whole request or only a part
of the request. The first argument specifies the amount of received data.
The second argument specifies an address of the remote endpoint which
produced the data.
% TCP_IDLE_CONNECTION_TIMEOUT_OCCURRED closing persistent connection with %1 as a result of a timeout
This debug message is issued when the persistent TCP connection is being
closed as a result of being idle.
% TCP_PREMATURE_CONNECTION_TIMEOUT_OCCURRED premature connection timeout occurred: in transaction ? %1, transid: %2, current_transid: %3
This warning message is issued when unexpected timeout occurred during the
transaction. This is proven to occur when the system clock is moved manually
or as a result of synchronization with a time server. Any ongoing transactions
will be interrupted. New transactions should be conducted normally.
% TCP_REQUEST_RECEIVE_START start receiving request from %1 with timeout %2
This debug message is issued when the server starts receiving new request
over the established connection. The first argument specifies the address
of the remote endpoint. The second argument specifies request timeout in
seconds.
% TCP_SERVER_RESPONSE_SEND sending TCP response %1 to %2
This debug message is issued when the server is starting to send a TCP
response to a remote endpoint. The first argument holds basic information
about the response (TCP version number and status code). The second
argument specifies an address of the remote endpoint.
% TCP_SERVER_RESPONSE_SEND_DETAILS detailed information about response sent to %1:\n%2
This debug message is issued right before the server sends a TCP response
to the client. It includes detailed information about the response. The
first argument specifies an address of the remote endpoint to which the
response is being sent. The second argument provides a response in the
textual form. The response is truncated by the logger if it is too large
to be printed.

1
src/lib/tcp/tests/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
run_unittests

View File

@ -0,0 +1,58 @@
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES) $(CRYPTO_CFLAGS) $(CRYPTO_INCLUDES)
TEST_CA_DIR = $(abs_srcdir)/../testutils/ca
AM_CPPFLAGS += -DTEST_CA_DIR=\"$(TEST_CA_DIR)\"
AM_CXXFLAGS = $(KEA_CXXFLAGS)
if USE_STATIC_LINK
AM_LDFLAGS = -static
endif
CLEANFILES = *.gcno *.gcda test-socket
DISTCLEANFILES =
noinst_SCRIPTS =
TESTS_ENVIRONMENT = $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
TESTS =
if HAVE_GTEST
TESTS += run_unittests
run_unittests_SOURCES = run_unittests.cc
#run_unittests_SOURCES += tcp_listner_unittest.cc
#if HAVE_OPENSSL
#run_unittests_SOURCES += tls_unittest.cc
#run_unittests_SOURCES += tls_acceptor_unittest.cc
#run_unittests_SOURCES += tls_socket_unittest.cc
#endif
#if HAVE_BOTAN_BOOST
#run_unittests_SOURCES += tls_unittest.cc
#run_unittests_SOURCES += tls_acceptor_unittest.cc
#run_unittests_SOURCES += tls_socket_unittest.cc
#endif
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDADD = $(top_builddir)/src/lib/tcp/libkea-tcp.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
run_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
run_unittests_LDADD += $(LOG4CPLUS_LIBS) $(BOOST_LIBS) $(CRYPTO_LIBS)
run_unittests_LDADD += $(GTEST_LDADD)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS)
# Note: the ordering matters: -Wno-... must follow -Wextra (defined in
# KEA_CXXFLAGS)
run_unittests_CXXFLAGS = $(AM_CXXFLAGS)
if USE_GXX
run_unittests_CXXFLAGS += -Wno-unused-parameter -Wno-unused-private-field
endif
endif
noinst_PROGRAMS = $(TESTS)

View File

@ -0,0 +1,19 @@
// Copyright (C) 2022 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <config.h>
#include <gtest/gtest.h>
#include <util/unittests/run_all.h>
#include <log/logger_manager.h>
int
main(int argc, char* argv[])
{
::testing::InitGoogleTest(&argc, argv); // Initialize Google test
isc::log::LoggerManager::init("unittest"); // Set a root logger name
return (isc::util::unittests::run_all());
}