summaryrefslogtreecommitdiff
path: root/louloulibs/network
diff options
context:
space:
mode:
Diffstat (limited to 'louloulibs/network')
-rw-r--r--louloulibs/network/dns_socket_handler.cpp5
-rw-r--r--louloulibs/network/dns_socket_handler.hpp1
-rw-r--r--louloulibs/network/socket_handler.hpp1
-rw-r--r--louloulibs/network/tcp_client_socket_handler.cpp232
-rw-r--r--louloulibs/network/tcp_client_socket_handler.hpp75
-rw-r--r--louloulibs/network/tcp_socket_handler.cpp230
-rw-r--r--louloulibs/network/tcp_socket_handler.hpp93
7 files changed, 347 insertions, 290 deletions
diff --git a/louloulibs/network/dns_socket_handler.cpp b/louloulibs/network/dns_socket_handler.cpp
index 403a5be..af39f49 100644
--- a/louloulibs/network/dns_socket_handler.cpp
+++ b/louloulibs/network/dns_socket_handler.cpp
@@ -40,6 +40,11 @@ bool DNSSocketHandler::is_connected() const
return true;
}
+bool DNSSocketHandler::is_connecting() const
+{
+ return false;
+}
+
void DNSSocketHandler::remove_from_poller()
{
if (this->poller->is_managing_socket(this->socket))
diff --git a/louloulibs/network/dns_socket_handler.hpp b/louloulibs/network/dns_socket_handler.hpp
index 0570196..14ba177 100644
--- a/louloulibs/network/dns_socket_handler.hpp
+++ b/louloulibs/network/dns_socket_handler.hpp
@@ -40,6 +40,7 @@ public:
* Always true, see the comment for connect()
*/
bool is_connected() const override final;
+ bool is_connecting() const override final;
void remove_from_poller();
private:
diff --git a/louloulibs/network/socket_handler.hpp b/louloulibs/network/socket_handler.hpp
index ea79a18..02e3b80 100644
--- a/louloulibs/network/socket_handler.hpp
+++ b/louloulibs/network/socket_handler.hpp
@@ -24,6 +24,7 @@ public:
virtual void on_send() = 0;
virtual void connect() = 0;
virtual bool is_connected() const = 0;
+ virtual bool is_connecting() const = 0;
socket_t get_socket() const
{ return this->socket; }
diff --git a/louloulibs/network/tcp_client_socket_handler.cpp b/louloulibs/network/tcp_client_socket_handler.cpp
new file mode 100644
index 0000000..2de9696
--- /dev/null
+++ b/louloulibs/network/tcp_client_socket_handler.cpp
@@ -0,0 +1,232 @@
+#include <network/tcp_client_socket_handler.hpp>
+#include <utils/timed_events.hpp>
+#include <utils/scopeguard.hpp>
+#include <network/poller.hpp>
+
+#include <logger/logger.hpp>
+
+#include <unistd.h>
+#include <fcntl.h>
+
+using namespace std::string_literals;
+
+TCPClientSocketHandler::TCPClientSocketHandler(std::shared_ptr<Poller> poller):
+ TCPSocketHandler(poller),
+ hostname_resolution_failed(false),
+ connected(false),
+ connecting(false)
+{}
+
+TCPClientSocketHandler::~TCPClientSocketHandler()
+{
+ this->close();
+}
+
+void TCPClientSocketHandler::init_socket(const struct addrinfo* rp)
+{
+ if (this->socket != -1)
+ ::close(this->socket);
+ if ((this->socket = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) == -1)
+ throw std::runtime_error("Could not create socket: "s + strerror(errno));
+ // Bind the socket to a specific address, if specified
+ if (!this->bind_addr.empty())
+ {
+ // Convert the address from string format to a sockaddr that can be
+ // used in bind()
+ struct addrinfo* result;
+ int err = ::getaddrinfo(this->bind_addr.data(), nullptr, nullptr, &result);
+ if (err != 0 || !result)
+ log_error("Failed to bind socket to ", this->bind_addr, ": ",
+ gai_strerror(err));
+ else
+ {
+ utils::ScopeGuard sg([result](){ freeaddrinfo(result); });
+ struct addrinfo* rp;
+ int bind_error = 0;
+ for (rp = result; rp; rp = rp->ai_next)
+ {
+ if ((bind_error = ::bind(this->socket,
+ reinterpret_cast<const struct sockaddr*>(rp->ai_addr),
+ rp->ai_addrlen)) == 0)
+ break;
+ }
+ if (!rp)
+ log_error("Failed to bind socket to ", this->bind_addr, ": ",
+ strerror(errno));
+ else
+ log_info("Socket successfully bound to ", this->bind_addr);
+ }
+ }
+ int optval = 1;
+ if (::setsockopt(this->socket, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval)) == -1)
+ log_warning("Failed to enable TCP keepalive on socket: ", strerror(errno));
+ // Set the socket on non-blocking mode. This is useful to receive a EAGAIN
+ // error when connect() would block, to not block the whole process if a
+ // remote is not responsive.
+ const int existing_flags = ::fcntl(this->socket, F_GETFL, 0);
+ if ((existing_flags == -1) ||
+ (::fcntl(this->socket, F_SETFL, existing_flags | O_NONBLOCK) == -1))
+ throw std::runtime_error("Could not initialize socket: "s + strerror(errno));
+}
+
+void TCPClientSocketHandler::connect(const std::string& address, const std::string& port, const bool tls)
+{
+ this->address = address;
+ this->port = port;
+ this->use_tls = tls;
+
+ struct addrinfo* addr_res;
+
+ if (!this->connecting)
+ {
+ // Get the addrinfo from getaddrinfo (or ares_gethostbyname), only if
+ // this is the first call of this function.
+ if (!this->resolver.is_resolved())
+ {
+ log_info("Trying to connect to ", address, ":", port);
+ // Start the asynchronous process of resolving the hostname. Once
+ // the addresses have been found and `resolved` has been set to true
+ // (but connecting will still be false), TCPClientSocketHandler::connect()
+ // needs to be called, again.
+ this->resolver.resolve(address, port,
+ [this](const struct addrinfo*)
+ {
+ log_debug("Resolution success, calling connect() again");
+ this->connect();
+ },
+ [this](const char*)
+ {
+ log_debug("Resolution failed, calling connect() again");
+ this->connect();
+ });
+ return;
+ }
+ else
+ {
+ // The c-ares resolved the hostname and the available addresses
+ // where saved in the cares_addrinfo linked list. Now, just use
+ // this list to try to connect.
+ addr_res = this->resolver.get_result().get();
+ if (!addr_res)
+ {
+ this->hostname_resolution_failed = true;
+ const auto msg = this->resolver.get_error_message();
+ this->close();
+ this->on_connection_failed(msg);
+ return ;
+ }
+ }
+ }
+ else
+ { // This function is called again, use the saved addrinfo structure,
+ // instead of re-doing the whole getaddrinfo process.
+ addr_res = &this->addrinfo;
+ }
+
+ for (struct addrinfo* rp = addr_res; rp; rp = rp->ai_next)
+ {
+ if (!this->connecting)
+ {
+ try {
+ this->init_socket(rp);
+ }
+ catch (const std::runtime_error& error) {
+ log_error("Failed to init socket: ", error.what());
+ break;
+ }
+ }
+
+ this->display_resolved_ip(rp);
+
+ if (::connect(this->socket, rp->ai_addr, rp->ai_addrlen) == 0
+ || errno == EISCONN)
+ {
+ log_info("Connection success.");
+ TimedEventsManager::instance().cancel("connection_timeout"s +
+ std::to_string(this->socket));
+ this->poller->add_socket_handler(this);
+ this->connected = true;
+ this->connecting = false;
+#ifdef BOTAN_FOUND
+ if (this->use_tls)
+ this->start_tls(this->address, this->port);
+#endif
+ this->connection_date = std::chrono::system_clock::now();
+
+ this->on_connected();
+ return ;
+ }
+ else if (errno == EINPROGRESS || errno == EALREADY)
+ { // retry this process later, when the socket
+ // is ready to be written on.
+ this->connecting = true;
+ this->poller->add_socket_handler(this);
+ this->poller->watch_send_events(this);
+ // Save the addrinfo structure, to use it on the next call
+ this->ai_addrlen = rp->ai_addrlen;
+ memcpy(&this->ai_addr, rp->ai_addr, this->ai_addrlen);
+ memcpy(&this->addrinfo, rp, sizeof(struct addrinfo));
+ this->addrinfo.ai_addr = reinterpret_cast<struct sockaddr*>(&this->ai_addr);
+ this->addrinfo.ai_next = nullptr;
+ // If the connection has not succeeded or failed in 5s, we consider
+ // it to have failed
+ TimedEventsManager::instance().add_event(
+ TimedEvent(std::chrono::steady_clock::now() + 5s,
+ std::bind(&TCPClientSocketHandler::on_connection_timeout, this),
+ "connection_timeout"s + std::to_string(this->socket)));
+ return ;
+ }
+ log_info("Connection failed:", strerror(errno));
+ }
+ log_error("All connection attempts failed.");
+ this->close();
+ this->on_connection_failed(strerror(errno));
+ return ;
+}
+
+void TCPClientSocketHandler::on_connection_timeout()
+{
+ this->close();
+ this->on_connection_failed("connection timed out");
+}
+
+void TCPClientSocketHandler::connect()
+{
+ this->connect(this->address, this->port, this->use_tls);
+}
+
+void TCPClientSocketHandler::close()
+{
+ TimedEventsManager::instance().cancel("connection_timeout"s +
+ std::to_string(this->socket));
+
+ TCPSocketHandler::close();
+
+ this->connected = false;
+ this->connecting = false;
+ this->port.clear();
+ this->resolver.clear();
+}
+
+void TCPClientSocketHandler::display_resolved_ip(struct addrinfo* rp) const
+{
+ if (rp->ai_family == AF_INET)
+ log_debug("Trying IPv4 address ", addr_to_string(rp));
+ else if (rp->ai_family == AF_INET6)
+ log_debug("Trying IPv6 address ", addr_to_string(rp));
+}
+
+bool TCPClientSocketHandler::is_connected() const
+{
+ return this->connected;
+}
+
+bool TCPClientSocketHandler::is_connecting() const
+{
+ return this->connecting || this->resolver.is_resolving();
+}
+
+std::string TCPClientSocketHandler::get_port() const
+{
+ return this->port;
+}
diff --git a/louloulibs/network/tcp_client_socket_handler.hpp b/louloulibs/network/tcp_client_socket_handler.hpp
new file mode 100644
index 0000000..7dd476c
--- /dev/null
+++ b/louloulibs/network/tcp_client_socket_handler.hpp
@@ -0,0 +1,75 @@
+#pragma once
+
+#include <network/tcp_socket_handler.hpp>
+
+class TCPClientSocketHandler: public TCPSocketHandler
+{
+ public:
+ TCPClientSocketHandler(std::shared_ptr<Poller> poller);
+ ~TCPClientSocketHandler();
+ /**
+ * Connect to the remote server, and call on_connected() if this
+ * succeeds. If tls is true, we set use_tls to true and will also call
+ * start_tls() when the connection succeeds.
+ */
+ void connect(const std::string& address, const std::string& port, const bool tls);
+ void connect() override final;
+ /**
+ * Called by a TimedEvent, when the connection did not succeed or fail
+ * after a given time.
+ */
+ void on_connection_timeout();
+ /**
+ * Called when the connection is successful.
+ */
+ virtual void on_connected() = 0;
+ bool is_connected() const override;
+ bool is_connecting() const;
+
+ std::string get_port() const;
+
+ void close() override final;
+ std::chrono::system_clock::time_point connection_date;
+
+ protected:
+ bool hostname_resolution_failed;
+ /**
+ * Address to bind the socket to, before calling connect().
+ * If empty, it’s equivalent to binding to INADDR_ANY.
+ */
+ std::string bind_addr;
+ /**
+ * Display the resolved IP, just for information purpose.
+ */
+ void display_resolved_ip(struct addrinfo* rp) const;
+ private:
+ /**
+ * Initialize the socket with the parameters contained in the given
+ * addrinfo structure.
+ */
+ void init_socket(const struct addrinfo* rp);
+ /**
+ * DNS resolver
+ */
+ Resolver resolver;
+ /**
+ * Keep the details of the addrinfo returned by the resolver that
+ * triggered a EINPROGRESS error when connect()ing to it, to reuse it
+ * directly when connect() is called again.
+ */
+ struct addrinfo addrinfo;
+ struct sockaddr_in6 ai_addr;
+ socklen_t ai_addrlen;
+
+ /**
+ * Hostname we are connected/connecting to
+ */
+ std::string address;
+ /**
+ * Port we are connected/connecting to
+ */
+ std::string port;
+
+ bool connected;
+ bool connecting;
+};
diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp
index 1dddde5..5782f66 100644
--- a/louloulibs/network/tcp_socket_handler.cpp
+++ b/louloulibs/network/tcp_socket_handler.cpp
@@ -1,8 +1,6 @@
#include <network/tcp_socket_handler.hpp>
#include <network/dns_handler.hpp>
-#include <utils/timed_events.hpp>
-#include <utils/scopeguard.hpp>
#include <network/poller.hpp>
#include <logger/logger.hpp>
@@ -12,7 +10,6 @@
#include <unistd.h>
#include <errno.h>
#include <cstring>
-#include <fcntl.h>
#ifdef BOTAN_FOUND
# include <botan/hex.h>
@@ -35,10 +32,7 @@ namespace ph = std::placeholders;
TCPSocketHandler::TCPSocketHandler(std::shared_ptr<Poller> poller):
SocketHandler(poller, -1),
- use_tls(false),
- connected(false),
- connecting(false),
- hostname_resolution_failed(false)
+ use_tls(false)
#ifdef BOTAN_FOUND
,credential_manager(this)
#endif
@@ -46,181 +40,13 @@ TCPSocketHandler::TCPSocketHandler(std::shared_ptr<Poller> poller):
TCPSocketHandler::~TCPSocketHandler()
{
- this->close();
-}
-
-
-void TCPSocketHandler::init_socket(const struct addrinfo* rp)
-{
+ if (this->poller->is_managing_socket(this->get_socket()))
+ this->poller->remove_socket_handler(this->get_socket());
if (this->socket != -1)
- ::close(this->socket);
- if ((this->socket = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) == -1)
- throw std::runtime_error("Could not create socket: "s + strerror(errno));
- // Bind the socket to a specific address, if specified
- if (!this->bind_addr.empty())
{
- // Convert the address from string format to a sockaddr that can be
- // used in bind()
- struct addrinfo* result;
- int err = ::getaddrinfo(this->bind_addr.data(), nullptr, nullptr, &result);
- if (err != 0 || !result)
- log_error("Failed to bind socket to ", this->bind_addr, ": ",
- gai_strerror(err));
- else
- {
- utils::ScopeGuard sg([result](){ freeaddrinfo(result); });
- struct addrinfo* rp;
- int bind_error = 0;
- for (rp = result; rp; rp = rp->ai_next)
- {
- if ((bind_error = ::bind(this->socket,
- reinterpret_cast<const struct sockaddr*>(rp->ai_addr),
- rp->ai_addrlen)) == 0)
- break;
- }
- if (!rp)
- log_error("Failed to bind socket to ", this->bind_addr, ": ",
- strerror(errno));
- else
- log_info("Socket successfully bound to ", this->bind_addr);
- }
- }
- int optval = 1;
- if (::setsockopt(this->socket, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval)) == -1)
- log_warning("Failed to enable TCP keepalive on socket: ", strerror(errno));
- // Set the socket on non-blocking mode. This is useful to receive a EAGAIN
- // error when connect() would block, to not block the whole process if a
- // remote is not responsive.
- const int existing_flags = ::fcntl(this->socket, F_GETFL, 0);
- if ((existing_flags == -1) ||
- (::fcntl(this->socket, F_SETFL, existing_flags | O_NONBLOCK) == -1))
- throw std::runtime_error("Could not initialize socket: "s + strerror(errno));
-}
-
-void TCPSocketHandler::connect(const std::string& address, const std::string& port, const bool tls)
-{
- this->address = address;
- this->port = port;
- this->use_tls = tls;
-
- struct addrinfo* addr_res;
-
- if (!this->connecting)
- {
- // Get the addrinfo from getaddrinfo (or ares_gethostbyname), only if
- // this is the first call of this function.
- if (!this->resolver.is_resolved())
- {
- log_info("Trying to connect to ", address, ":", port);
- // Start the asynchronous process of resolving the hostname. Once
- // the addresses have been found and `resolved` has been set to true
- // (but connecting will still be false), TCPSocketHandler::connect()
- // needs to be called, again.
- this->resolver.resolve(address, port,
- [this](const struct addrinfo*)
- {
- log_debug("Resolution success, calling connect() again");
- this->connect();
- },
- [this](const char*)
- {
- log_debug("Resolution failed, calling connect() again");
- this->connect();
- });
- return;
- }
- else
- {
- // The c-ares resolved the hostname and the available addresses
- // where saved in the cares_addrinfo linked list. Now, just use
- // this list to try to connect.
- addr_res = this->resolver.get_result().get();
- if (!addr_res)
- {
- this->hostname_resolution_failed = true;
- const auto msg = this->resolver.get_error_message();
- this->close();
- this->on_connection_failed(msg);
- return ;
- }
- }
- }
- else
- { // This function is called again, use the saved addrinfo structure,
- // instead of re-doing the whole getaddrinfo process.
- addr_res = &this->addrinfo;
- }
-
- for (struct addrinfo* rp = addr_res; rp; rp = rp->ai_next)
- {
- if (!this->connecting)
- {
- try {
- this->init_socket(rp);
- }
- catch (const std::runtime_error& error) {
- log_error("Failed to init socket: ", error.what());
- break;
- }
- }
-
- this->display_resolved_ip(rp);
-
- if (::connect(this->socket, rp->ai_addr, rp->ai_addrlen) == 0
- || errno == EISCONN)
- {
- log_info("Connection success.");
- TimedEventsManager::instance().cancel("connection_timeout"s +
- std::to_string(this->socket));
- this->poller->add_socket_handler(this);
- this->connected = true;
- this->connecting = false;
-#ifdef BOTAN_FOUND
- if (this->use_tls)
- this->start_tls();
-#endif
- this->connection_date = std::chrono::system_clock::now();
-
- this->on_connected();
- return ;
- }
- else if (errno == EINPROGRESS || errno == EALREADY)
- { // retry this process later, when the socket
- // is ready to be written on.
- this->connecting = true;
- this->poller->add_socket_handler(this);
- this->poller->watch_send_events(this);
- // Save the addrinfo structure, to use it on the next call
- this->ai_addrlen = rp->ai_addrlen;
- memcpy(&this->ai_addr, rp->ai_addr, this->ai_addrlen);
- memcpy(&this->addrinfo, rp, sizeof(struct addrinfo));
- this->addrinfo.ai_addr = reinterpret_cast<struct sockaddr*>(&this->ai_addr);
- this->addrinfo.ai_next = nullptr;
- // If the connection has not succeeded or failed in 5s, we consider
- // it to have failed
- TimedEventsManager::instance().add_event(
- TimedEvent(std::chrono::steady_clock::now() + 5s,
- std::bind(&TCPSocketHandler::on_connection_timeout, this),
- "connection_timeout"s + std::to_string(this->socket)));
- return ;
- }
- log_info("Connection failed:", strerror(errno));
+ ::close(this->socket);
+ this->socket = -1;
}
- log_error("All connection attempts failed.");
- this->close();
- this->on_connection_failed(strerror(errno));
- return ;
-}
-
-void TCPSocketHandler::on_connection_timeout()
-{
- this->close();
- this->on_connection_failed("connection timed out");
-}
-
-void TCPSocketHandler::connect()
-{
- this->connect(this->address, this->port, this->use_tls);
}
void TCPSocketHandler::on_recv()
@@ -267,13 +93,13 @@ ssize_t TCPSocketHandler::do_recv(void* recv_buf, const size_t buf_size)
}
else if (-1 == size)
{
- if (this->connecting)
+ if (this->is_connecting())
log_warning("Error connecting: ", strerror(errno));
else
log_warning("Error while reading from socket: ", strerror(errno));
// Remember if we were connecting, or already connected when this
// happened, because close() sets this->connecting to false
- const auto were_connecting = this->connecting;
+ const auto were_connecting = this->is_connecting();
this->close();
if (were_connecting)
this->on_connection_failed(strerror(errno));
@@ -333,29 +159,15 @@ void TCPSocketHandler::on_send()
void TCPSocketHandler::close()
{
- TimedEventsManager::instance().cancel("connection_timeout"s +
- std::to_string(this->socket));
- if (this->connected || this->connecting)
+ if (this->is_connected() || this->is_connecting())
this->poller->remove_socket_handler(this->get_socket());
if (this->socket != -1)
{
::close(this->socket);
this->socket = -1;
}
- this->connected = false;
- this->connecting = false;
this->in_buf.clear();
this->out_buf.clear();
- this->port.clear();
- this->resolver.clear();
-}
-
-void TCPSocketHandler::display_resolved_ip(struct addrinfo* rp) const
-{
- if (rp->ai_family == AF_INET)
- log_debug("Trying IPv4 address ", addr_to_string(rp));
- else if (rp->ai_family == AF_INET6)
- log_debug("Trying IPv6 address ", addr_to_string(rp));
}
void TCPSocketHandler::send_data(std::string&& data)
@@ -379,45 +191,35 @@ void TCPSocketHandler::raw_send(std::string&& data)
if (data.empty())
return ;
this->out_buf.emplace_back(std::move(data));
- if (this->connected)
+ if (this->is_connected())
this->poller->watch_send_events(this);
}
void TCPSocketHandler::send_pending_data()
{
- if (this->connected && !this->out_buf.empty())
+ if (this->is_connected() && !this->out_buf.empty())
this->poller->watch_send_events(this);
}
-bool TCPSocketHandler::is_connected() const
-{
- return this->connected;
-}
-
-bool TCPSocketHandler::is_connecting() const
-{
- return this->connecting || this->resolver.is_resolving();
-}
-
bool TCPSocketHandler::is_using_tls() const
{
return this->use_tls;
}
-std::string TCPSocketHandler::get_port() const
+void* TCPSocketHandler::get_receive_buffer(const size_t) const
{
- return this->port;
+ return nullptr;
}
-void* TCPSocketHandler::get_receive_buffer(const size_t) const
+void TCPSocketHandler::consume_in_buffer(const std::size_t size)
{
- return nullptr;
+ this->in_buf = this->in_buf.substr(size, std::string::npos);
}
#ifdef BOTAN_FOUND
-void TCPSocketHandler::start_tls()
+void TCPSocketHandler::start_tls(const std::string& address, const std::string& port)
{
- Botan::TLS::Server_Information server_info(this->address, "irc", std::stoul(this->port));
+ Botan::TLS::Server_Information server_info(address, "irc", std::stoul(port));
this->tls = std::make_unique<Botan::TLS::Client>(
std::bind(&TCPSocketHandler::tls_output_fn, this, ph::_1, ph::_2),
std::bind(&TCPSocketHandler::tls_data_cb, this, ph::_1, ph::_2),
diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp
index 20a3e5a..d5e24ee 100644
--- a/louloulibs/network/tcp_socket_handler.hpp
+++ b/louloulibs/network/tcp_socket_handler.hpp
@@ -20,10 +20,8 @@
#include <list>
/**
- * An interface, with a series of callbacks that should be implemented in
- * subclasses that deal with a socket. These callbacks are called on various events
- * (read/write/timeout, etc) when they are notified to a poller
- * (select/poll/epoll etc)
+ * Does all the read/write, buffering etc. With optional tls.
+ * But doesn’t do any connect() or accept() or anything else.
*/
class TCPSocketHandler: public SocketHandler
{
@@ -37,13 +35,6 @@ public:
TCPSocketHandler& operator=(TCPSocketHandler&&) = delete;
/**
- * Connect to the remote server, and call on_connected() if this
- * succeeds. If tls is true, we set use_tls to true and will also call
- * start_tls() when the connection succeeds.
- */
- void connect(const std::string& address, const std::string& port, const bool tls);
- void connect() override final;
- /**
* Reads raw data from the socket. And pass it to parse_in_buffer()
* If we are using TLS on this connection, we call tls_recv()
*/
@@ -67,25 +58,7 @@ public:
/**
* Close the connection, remove us from the poller
*/
- void close();
- /**
- * Called by a TimedEvent, when the connection did not succeed or fail
- * after a given time.
- */
- void on_connection_timeout();
- /**
- * Called when the connection is successful.
- */
- virtual void on_connected() = 0;
- /**
- * Called when the connection fails. Not when it is closed later, just at
- * the connect() call.
- */
- virtual void on_connection_failed(const std::string& reason) = 0;
- /**
- * Called when we detect a disconnection from the remote host.
- */
- virtual void on_connection_close(const std::string& error) = 0;
+ virtual void close();
/**
* Handle/consume (some of) the data received so far. The data to handle
* may be in the in_buf buffer, or somewhere else, depending on what
@@ -93,6 +66,9 @@ public:
* should be truncated, only the unused data should be left untouched.
*
* The size argument is the size of the last chunk of data that was added to the buffer.
+ *
+ * The function should call consume_in_buffer, with the size that was consumed by the
+ * “parsing”, and thus to be removed from the input buffer.
*/
virtual void parse_in_buffer(const size_t size) = 0;
#ifdef BOTAN_FOUND
@@ -105,19 +81,10 @@ public:
return true;
}
#endif
- bool is_connected() const override final;
- bool is_connecting() const;
bool is_using_tls() const;
- std::string get_port() const;
- std::chrono::system_clock::time_point connection_date;
private:
/**
- * Initialize the socket with the parameters contained in the given
- * addrinfo structure.
- */
- void init_socket(const struct addrinfo* rp);
- /**
* Reads from the socket into the provided buffer. If an error occurs
* (read returns <= 0), the handling of the error is done here (close the
* connection, log a message, etc).
@@ -137,12 +104,14 @@ private:
void raw_send(std::string&& data);
#ifdef BOTAN_FOUND
+ protected:
/**
* Create the TLS::Client object, with all the callbacks etc. This must be
* called only when we know we are able to send TLS-encrypted data over
* the socket.
*/
- void start_tls();
+ void start_tls(const std::string& address, const std::string& port);
+ private:
/**
* An additional step to pass the data into our tls object to decrypt it
* before passing it to parse_in_buffer.
@@ -185,20 +154,11 @@ private:
* Where data is added, when we want to send something to the client.
*/
std::vector<std::string> out_buf;
+protected:
/**
- * DNS resolver
- */
- Resolver resolver;
- /**
- * Keep the details of the addrinfo returned by the resolver that
- * triggered a EINPROGRESS error when connect()ing to it, to reuse it
- * directly when connect() is called again.
+ * Whether we are using TLS on this connection or not.
*/
- struct addrinfo addrinfo;
- struct sockaddr_in6 ai_addr;
- socklen_t ai_addrlen;
-
-protected:
+ bool use_tls;
/**
* Where data read from the socket is added until we can extract a full
* and meaningful “message” from it.
@@ -207,9 +167,9 @@ protected:
*/
std::string in_buf;
/**
- * Whether we are using TLS on this connection or not.
+ * Remove the given “size” first bytes from our in_buf.
*/
- bool use_tls;
+ void consume_in_buffer(const std::size_t size);
/**
* Provide a buffer in which data can be directly received. This can be
* used to avoid copying data into in_buf before using it. If no buffer
@@ -219,31 +179,12 @@ protected:
*/
virtual void* get_receive_buffer(const size_t size) const;
/**
- * Hostname we are connected/connecting to
- */
- std::string address;
- /**
- * Port we are connected/connecting to
- */
- std::string port;
-
- bool connected;
- bool connecting;
-
- bool hostname_resolution_failed;
-
- /**
- * Address to bind the socket to, before calling connect().
- * If empty, it’s equivalent to binding to INADDR_ANY.
+ * Called when we detect a disconnection from the remote host.
*/
- std::string bind_addr;
+ virtual void on_connection_close(const std::string& error) = 0;
+ virtual void on_connection_failed(const std::string& error) = 0;
private:
- /**
- * Display the resolved IP, just for information purpose.
- */
- void display_resolved_ip(struct addrinfo* rp) const;
-
#ifdef BOTAN_FOUND
/**
* Botan stuff to manipulate a TLS session.