diff options
author | louiz’ <louiz@louiz.org> | 2016-11-11 02:54:48 +0100 |
---|---|---|
committer | louiz’ <louiz@louiz.org> | 2016-11-11 02:54:48 +0100 |
commit | 0c8adc85f7373a85de8b3edc6cac87d5f7389bb3 (patch) | |
tree | aefa90f4325424716b1304b4eac42d43371df1ca | |
parent | c41d003cbf3b14b83e0f9bf6c4787c3bd60bb7ee (diff) | |
download | biboumi-0c8adc85f7373a85de8b3edc6cac87d5f7389bb3.tar.gz biboumi-0c8adc85f7373a85de8b3edc6cac87d5f7389bb3.tar.bz2 biboumi-0c8adc85f7373a85de8b3edc6cac87d5f7389bb3.tar.xz biboumi-0c8adc85f7373a85de8b3edc6cac87d5f7389bb3.zip |
Move all the connect() logic from TCPSocketHandler into a subclass
This way, TCPSocketHandler only deal with the message sending/receiving, not
the connect() or anything else. This will be used for implementing servers
(because when a client is accepted, we don’t need all the connect() and dns
resolution stuff).
-rw-r--r-- | louloulibs/network/dns_socket_handler.cpp | 5 | ||||
-rw-r--r-- | louloulibs/network/dns_socket_handler.hpp | 1 | ||||
-rw-r--r-- | louloulibs/network/socket_handler.hpp | 1 | ||||
-rw-r--r-- | louloulibs/network/tcp_client_socket_handler.cpp | 232 | ||||
-rw-r--r-- | louloulibs/network/tcp_client_socket_handler.hpp | 75 | ||||
-rw-r--r-- | louloulibs/network/tcp_socket_handler.cpp | 230 | ||||
-rw-r--r-- | louloulibs/network/tcp_socket_handler.hpp | 93 | ||||
-rw-r--r-- | louloulibs/xmpp/xmpp_component.cpp | 2 | ||||
-rw-r--r-- | louloulibs/xmpp/xmpp_component.hpp | 4 | ||||
-rw-r--r-- | src/irc/irc_client.cpp | 4 | ||||
-rw-r--r-- | src/irc/irc_client.hpp | 4 |
11 files changed, 354 insertions, 297 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. diff --git a/louloulibs/xmpp/xmpp_component.cpp b/louloulibs/xmpp/xmpp_component.cpp index fa8b0a5..1d1c58b 100644 --- a/louloulibs/xmpp/xmpp_component.cpp +++ b/louloulibs/xmpp/xmpp_component.cpp @@ -39,7 +39,7 @@ static std::set<std::string> kickable_errors{ }; XmppComponent::XmppComponent(std::shared_ptr<Poller> poller, const std::string& hostname, const std::string& secret): - TCPSocketHandler(poller), + TCPClientSocketHandler(poller), ever_auth(false), first_connection_try(true), secret(secret), diff --git a/louloulibs/xmpp/xmpp_component.hpp b/louloulibs/xmpp/xmpp_component.hpp index 5f5f937..e3f8ce2 100644 --- a/louloulibs/xmpp/xmpp_component.hpp +++ b/louloulibs/xmpp/xmpp_component.hpp @@ -2,7 +2,7 @@ #include <xmpp/adhoc_commands_handler.hpp> -#include <network/tcp_socket_handler.hpp> +#include <network/tcp_client_socket_handler.hpp> #include <xmpp/xmpp_parser.hpp> #include <xmpp/body.hpp> @@ -40,7 +40,7 @@ * * TODO: implement XEP-0225: Component Connections */ -class XmppComponent: public TCPSocketHandler +class XmppComponent: public TCPClientSocketHandler { public: explicit XmppComponent(std::shared_ptr<Poller> poller, const std::string& hostname, const std::string& secret); diff --git a/src/irc/irc_client.cpp b/src/irc/irc_client.cpp index b0d3a47..b13e5ab 100644 --- a/src/irc/irc_client.cpp +++ b/src/irc/irc_client.cpp @@ -131,7 +131,7 @@ IrcClient::IrcClient(std::shared_ptr<Poller> poller, const std::string& hostname const std::string& nickname, const std::string& username, const std::string& realname, const std::string& user_hostname, Bridge& bridge): - TCPSocketHandler(poller), + TCPClientSocketHandler(poller), hostname(hostname), user_hostname(user_hostname), username(username), @@ -338,7 +338,7 @@ void IrcClient::parse_in_buffer(const size_t) if (pos == std::string::npos) break ; IrcMessage message(this->in_buf.substr(0, pos)); - this->in_buf = this->in_buf.substr(pos + 2, std::string::npos); + this->consume_in_buffer(pos + 2); log_debug("IRC RECEIVING: (", this->get_hostname(), ") ", message); // Call the standard callback (if any), associated with the command diff --git a/src/irc/irc_client.hpp b/src/irc/irc_client.hpp index 1b4d892..4edc32c 100644 --- a/src/irc/irc_client.hpp +++ b/src/irc/irc_client.hpp @@ -5,7 +5,7 @@ #include <irc/irc_channel.hpp> #include <irc/iid.hpp> -#include <network/tcp_socket_handler.hpp> +#include <network/tcp_client_socket_handler.hpp> #include <network/resolver.hpp> #include <unordered_map> @@ -23,7 +23,7 @@ class Bridge; * Represent one IRC client, i.e. an endpoint connected to a single IRC * server, through a TCP socket, receiving and sending commands to it. */ -class IrcClient: public TCPSocketHandler +class IrcClient: public TCPClientSocketHandler { public: explicit IrcClient(std::shared_ptr<Poller> poller, const std::string& hostname, |