From 0c8adc85f7373a85de8b3edc6cac87d5f7389bb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 11 Nov 2016 02:54:48 +0100 Subject: Move all the connect() logic from TCPSocketHandler into a subclass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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). --- louloulibs/network/dns_socket_handler.cpp | 5 + louloulibs/network/dns_socket_handler.hpp | 1 + louloulibs/network/socket_handler.hpp | 1 + louloulibs/network/tcp_client_socket_handler.cpp | 232 +++++++++++++++++++++++ louloulibs/network/tcp_client_socket_handler.hpp | 75 ++++++++ louloulibs/network/tcp_socket_handler.cpp | 230 ++-------------------- louloulibs/network/tcp_socket_handler.hpp | 93 ++------- 7 files changed, 347 insertions(+), 290 deletions(-) create mode 100644 louloulibs/network/tcp_client_socket_handler.cpp create mode 100644 louloulibs/network/tcp_client_socket_handler.hpp (limited to 'louloulibs/network') 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 +#include +#include +#include + +#include + +#include +#include + +using namespace std::string_literals; + +TCPClientSocketHandler::TCPClientSocketHandler(std::shared_ptr 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(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(&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 + +class TCPClientSocketHandler: public TCPSocketHandler +{ + public: + TCPClientSocketHandler(std::shared_ptr 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 #include -#include -#include #include #include @@ -12,7 +10,6 @@ #include #include #include -#include #ifdef BOTAN_FOUND # include @@ -35,10 +32,7 @@ namespace ph = std::placeholders; TCPSocketHandler::TCPSocketHandler(std::shared_ptr 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): 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(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(&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( 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 /** - * 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 { @@ -36,13 +34,6 @@ public: TCPSocketHandler& operator=(const TCPSocketHandler&) = delete; 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,18 +81,9 @@ 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 @@ -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 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. -- cgit v1.2.3 From ed7e66471f7018f2e7e1c6a469e5cd758b913255 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 11 Nov 2016 03:03:42 +0100 Subject: Add missing cstring include for strerror --- louloulibs/network/tcp_client_socket_handler.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/tcp_client_socket_handler.cpp b/louloulibs/network/tcp_client_socket_handler.cpp index 2de9696..f22d756 100644 --- a/louloulibs/network/tcp_client_socket_handler.cpp +++ b/louloulibs/network/tcp_client_socket_handler.cpp @@ -5,6 +5,7 @@ #include +#include #include #include @@ -27,7 +28,7 @@ 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)); + throw std::runtime_error("Could not create socket: "s + std::strerror(errno)); // Bind the socket to a specific address, if specified if (!this->bind_addr.empty()) { @@ -66,7 +67,7 @@ void TCPClientSocketHandler::init_socket(const struct addrinfo* rp) 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)); + throw std::runtime_error("Could not initialize socket: "s + std::strerror(errno)); } void TCPClientSocketHandler::connect(const std::string& address, const std::string& port, const bool tls) @@ -176,11 +177,11 @@ void TCPClientSocketHandler::connect(const std::string& address, const std::stri "connection_timeout"s + std::to_string(this->socket))); return ; } - log_info("Connection failed:", strerror(errno)); + log_info("Connection failed:", std::strerror(errno)); } log_error("All connection attempts failed."); this->close(); - this->on_connection_failed(strerror(errno)); + this->on_connection_failed(std::strerror(errno)); return ; } -- cgit v1.2.3 From d872c2b49214c0a4db40a9e2d860802d9eedc563 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 15 Nov 2016 00:23:19 +0100 Subject: Support the ident protocol fix #3211 --- louloulibs/network/resolver.hpp | 2 - louloulibs/network/tcp_client_socket_handler.cpp | 25 +++++++ louloulibs/network/tcp_client_socket_handler.hpp | 7 ++ louloulibs/network/tcp_server_socket.hpp | 86 ++++++++++++++++++++++++ 4 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 louloulibs/network/tcp_server_socket.hpp (limited to 'louloulibs/network') diff --git a/louloulibs/network/resolver.hpp b/louloulibs/network/resolver.hpp index 29e6f3a..7365f93 100644 --- a/louloulibs/network/resolver.hpp +++ b/louloulibs/network/resolver.hpp @@ -125,5 +125,3 @@ private: }; std::string addr_to_string(const struct addrinfo* rp); - - diff --git a/louloulibs/network/tcp_client_socket_handler.cpp b/louloulibs/network/tcp_client_socket_handler.cpp index f22d756..dc9221d 100644 --- a/louloulibs/network/tcp_client_socket_handler.cpp +++ b/louloulibs/network/tcp_client_socket_handler.cpp @@ -154,6 +154,25 @@ void TCPClientSocketHandler::connect(const std::string& address, const std::stri #endif this->connection_date = std::chrono::system_clock::now(); + // Get our local TCP port and store it + this->local_port = static_cast(-1); + if (rp->ai_family == AF_INET6) + { + struct sockaddr_in6 a; + socklen_t l = sizeof(a); + if (::getsockname(this->socket, (struct sockaddr*)&a, &l) != -1) + this->local_port = ::ntohs(a.sin6_port); + } + else if (rp->ai_family == AF_INET) + { + struct sockaddr_in a; + socklen_t l = sizeof(a); + if (::getsockname(this->socket, (struct sockaddr*)&a, &l) != -1) + this->local_port = ::ntohs(a.sin_port); + } + + log_debug("Local port: ", this->local_port, ", and remote port: ", this->port); + this->on_connected(); return ; } @@ -231,3 +250,9 @@ std::string TCPClientSocketHandler::get_port() const { return this->port; } + +bool TCPClientSocketHandler::match_port_pairt(const uint16_t local, const uint16_t remote) const +{ + const uint16_t remote_port = static_cast(std::stoi(this->port)); + return this->is_connected() && local == this->local_port && remote == remote_port; +} diff --git a/louloulibs/network/tcp_client_socket_handler.hpp b/louloulibs/network/tcp_client_socket_handler.hpp index 7dd476c..3ad8ec3 100644 --- a/louloulibs/network/tcp_client_socket_handler.hpp +++ b/louloulibs/network/tcp_client_socket_handler.hpp @@ -31,6 +31,11 @@ class TCPClientSocketHandler: public TCPSocketHandler void close() override final; std::chrono::system_clock::time_point connection_date; + /** + * Whether or not this connection is using the two given TCP ports. + */ + bool match_port_pairt(const uint16_t local, const uint16_t remote) const; + protected: bool hostname_resolution_failed; /** @@ -70,6 +75,8 @@ class TCPClientSocketHandler: public TCPSocketHandler */ std::string port; + uint16_t local_port; + bool connected; bool connecting; }; diff --git a/louloulibs/network/tcp_server_socket.hpp b/louloulibs/network/tcp_server_socket.hpp new file mode 100644 index 0000000..a54d3bf --- /dev/null +++ b/louloulibs/network/tcp_server_socket.hpp @@ -0,0 +1,86 @@ +#pragma once + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#include +#include + +using namespace std::string_literals; + +template +class TcpSocketServer: public SocketHandler +{ + public: + TcpSocketServer(std::shared_ptr poller, const uint16_t port): + SocketHandler(poller, -1) + { + if ((this->socket = ::socket(AF_INET6, SOCK_STREAM, 0)) == -1) + throw std::runtime_error("Could not create socket: "s + std::strerror(errno)); + + int opt = 1; + if (::setsockopt(this->socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) + throw std::runtime_error("Failed to set socket option: "s + std::strerror(errno)); + + struct sockaddr_in6 addr{}; + addr.sin6_family = AF_INET6; + addr.sin6_port = htons(port); + addr.sin6_addr = IN6ADDR_ANY_INIT; + if ((::bind(this->socket, (const struct sockaddr*)&addr, sizeof(addr))) == -1) + { // If we can’t listen on this port, we just give up, but this is not fatal. + log_warning("Failed to bind on port ", std::to_string(port), ": ", std::strerror(errno)); + return; + } + + if ((::listen(this->socket, 10)) == -1) + throw std::runtime_error("listen() failed"); + + this->accept(); + } + ~TcpSocketServer() = default; + + void on_recv() override + { + // Accept a RemoteSocketType + int socket = ::accept(this->socket, nullptr, nullptr); + + auto client = std::make_unique(poller, socket, *this); + this->poller->add_socket_handler(client.get()); + this->sockets.push_back(std::move(client)); + } + + protected: + std::vector> sockets; + + private: + void accept() + { + this->poller->add_socket_handler(this); + } + void on_send() override + { + assert(false); + } + void connect() override + { + assert(false); + } + bool is_connected() const override + { + return true; + } + bool is_connecting() const override + { + return false; + } +}; -- cgit v1.2.3 From 4ab3cd653f3eb19ff82838f777984c9d16e64a80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 15 Nov 2016 00:49:55 +0100 Subject: Fix some little compilation errors with some configs, from last commit --- louloulibs/network/tcp_client_socket_handler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/tcp_client_socket_handler.cpp b/louloulibs/network/tcp_client_socket_handler.cpp index dc9221d..2e90ea1 100644 --- a/louloulibs/network/tcp_client_socket_handler.cpp +++ b/louloulibs/network/tcp_client_socket_handler.cpp @@ -161,14 +161,14 @@ void TCPClientSocketHandler::connect(const std::string& address, const std::stri struct sockaddr_in6 a; socklen_t l = sizeof(a); if (::getsockname(this->socket, (struct sockaddr*)&a, &l) != -1) - this->local_port = ::ntohs(a.sin6_port); + this->local_port = ntohs(a.sin6_port); } else if (rp->ai_family == AF_INET) { struct sockaddr_in a; socklen_t l = sizeof(a); if (::getsockname(this->socket, (struct sockaddr*)&a, &l) != -1) - this->local_port = ::ntohs(a.sin_port); + this->local_port = ntohs(a.sin_port); } log_debug("Local port: ", this->local_port, ", and remote port: ", this->port); -- cgit v1.2.3 From ae445f0e599fc73559248595af06c9f04189f44b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 16 Nov 2016 20:02:44 +0100 Subject: Remove an unused variable --- louloulibs/network/tcp_client_socket_handler.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/tcp_client_socket_handler.cpp b/louloulibs/network/tcp_client_socket_handler.cpp index 2e90ea1..7f21e3f 100644 --- a/louloulibs/network/tcp_client_socket_handler.cpp +++ b/louloulibs/network/tcp_client_socket_handler.cpp @@ -43,10 +43,9 @@ void TCPClientSocketHandler::init_socket(const struct addrinfo* rp) { 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, + if ((::bind(this->socket, reinterpret_cast(rp->ai_addr), rp->ai_addrlen)) == 0) break; -- cgit v1.2.3 From d90f75dc60d041cb602d3af1feb7e7445dcaecf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 16 Nov 2016 20:03:01 +0100 Subject: Trivial include cleanup --- louloulibs/network/tcp_server_socket.hpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/tcp_server_socket.hpp b/louloulibs/network/tcp_server_socket.hpp index a54d3bf..663ae5b 100644 --- a/louloulibs/network/tcp_server_socket.hpp +++ b/louloulibs/network/tcp_server_socket.hpp @@ -1,8 +1,8 @@ #pragma once #include -#include #include +#include #include @@ -11,8 +11,6 @@ #include #include -#include - #include #include -- cgit v1.2.3 From 9dd0cf8f7aee5c6d3be163246de9f0a5282e02e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 16 Nov 2016 20:28:48 +0100 Subject: =?UTF-8?q?Fix=20an=20error=20on=20gcc=204.9,=20because=20it=20is?= =?UTF-8?q?=20so=20stupid=20it=20can=E2=80=99t=20find=20operator""s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- louloulibs/network/tcp_server_socket.hpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/tcp_server_socket.hpp b/louloulibs/network/tcp_server_socket.hpp index 663ae5b..44d474c 100644 --- a/louloulibs/network/tcp_server_socket.hpp +++ b/louloulibs/network/tcp_server_socket.hpp @@ -14,8 +14,6 @@ #include #include -using namespace std::string_literals; - template class TcpSocketServer: public SocketHandler { @@ -24,11 +22,11 @@ class TcpSocketServer: public SocketHandler SocketHandler(poller, -1) { if ((this->socket = ::socket(AF_INET6, SOCK_STREAM, 0)) == -1) - throw std::runtime_error("Could not create socket: "s + std::strerror(errno)); + throw std::runtime_error(std::string{"Could not create socket: "} + std::strerror(errno)); int opt = 1; if (::setsockopt(this->socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) - throw std::runtime_error("Failed to set socket option: "s + std::strerror(errno)); + throw std::runtime_error(std::string{"Failed to set socket option: "} + std::strerror(errno)); struct sockaddr_in6 addr{}; addr.sin6_family = AF_INET6; -- cgit v1.2.3 From 42e85d921b0c5e10889d93c24b7ebf355287ee71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 16 Nov 2016 23:07:27 +0100 Subject: Remove some useless virtual method from SocketHandler --- louloulibs/network/dns_socket_handler.cpp | 9 --------- louloulibs/network/dns_socket_handler.hpp | 6 ------ louloulibs/network/socket_handler.hpp | 7 +++---- louloulibs/network/tcp_client_socket_handler.hpp | 2 +- louloulibs/network/tcp_server_socket.hpp | 12 ------------ louloulibs/network/tcp_socket_handler.hpp | 1 + 6 files changed, 5 insertions(+), 32 deletions(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/dns_socket_handler.cpp b/louloulibs/network/dns_socket_handler.cpp index af39f49..a69f59b 100644 --- a/louloulibs/network/dns_socket_handler.cpp +++ b/louloulibs/network/dns_socket_handler.cpp @@ -15,10 +15,6 @@ DNSSocketHandler::DNSSocketHandler(std::shared_ptr poller, { } -void DNSSocketHandler::connect() -{ -} - void DNSSocketHandler::on_recv() { // always stop watching send and read events. We will re-watch them if the @@ -40,11 +36,6 @@ 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 14ba177..e3fed0c 100644 --- a/louloulibs/network/dns_socket_handler.hpp +++ b/louloulibs/network/dns_socket_handler.hpp @@ -31,16 +31,10 @@ public: */ void on_recv() override final; void on_send() override final; - /** - * Do nothing, because we are always considered to be connected, since the - * connection is done by c-ares and not by us. - */ - void connect() override final; /** * 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 02e3b80..607a106 100644 --- a/louloulibs/network/socket_handler.hpp +++ b/louloulibs/network/socket_handler.hpp @@ -20,11 +20,10 @@ public: SocketHandler& operator=(const SocketHandler&) = delete; SocketHandler& operator=(SocketHandler&&) = delete; - virtual void on_recv() = 0; - virtual void on_send() = 0; - virtual void connect() = 0; + virtual void on_recv() {} + virtual void on_send() {} + virtual void connect() {} 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.hpp b/louloulibs/network/tcp_client_socket_handler.hpp index 3ad8ec3..72f68d0 100644 --- a/louloulibs/network/tcp_client_socket_handler.hpp +++ b/louloulibs/network/tcp_client_socket_handler.hpp @@ -24,7 +24,7 @@ class TCPClientSocketHandler: public TCPSocketHandler */ virtual void on_connected() = 0; bool is_connected() const override; - bool is_connecting() const; + bool is_connecting() const override; std::string get_port() const; diff --git a/louloulibs/network/tcp_server_socket.hpp b/louloulibs/network/tcp_server_socket.hpp index 44d474c..7ea49ab 100644 --- a/louloulibs/network/tcp_server_socket.hpp +++ b/louloulibs/network/tcp_server_socket.hpp @@ -63,20 +63,8 @@ class TcpSocketServer: public SocketHandler { this->poller->add_socket_handler(this); } - void on_send() override - { - assert(false); - } - void connect() override - { - assert(false); - } bool is_connected() const override { return true; } - bool is_connecting() const override - { - return false; - } }; diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index d5e24ee..85c30f5 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -105,6 +105,7 @@ private: #ifdef BOTAN_FOUND protected: + virtual bool is_connecting() const = 0; /** * 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 -- cgit v1.2.3 From 5cb534c960faed2ed4303cd32f45fa69e4db417e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 16 Nov 2016 23:13:37 +0100 Subject: move is_connecting outside of an ifdef --- louloulibs/network/tcp_socket_handler.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index 85c30f5..d5b9c33 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -103,9 +103,9 @@ private: */ void raw_send(std::string&& data); -#ifdef BOTAN_FOUND protected: virtual bool is_connecting() const = 0; +#ifdef BOTAN_FOUND /** * 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 -- cgit v1.2.3 From 5f9568ca201832e8060dbaab3b080a758567c947 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 2 Dec 2016 11:30:27 +0100 Subject: TLS: Enable ecc point compression MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If available in Botan. There is an issue where, if botan supports it but we don’t enable it, then the TLS handshake may fail with some servers --- louloulibs/network/tcp_socket_handler.cpp | 2 +- louloulibs/network/tcp_socket_handler.hpp | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index 5782f66..7c33ab8 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -16,7 +16,7 @@ # include Botan::AutoSeeded_RNG TCPSocketHandler::rng; -Botan::TLS::Policy TCPSocketHandler::policy; +BiboumiTLSPolicy TCPSocketHandler::policy; Botan::TLS::Session_Manager_In_Memory TCPSocketHandler::session_manager(TCPSocketHandler::rng); #endif diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index d5b9c33..c37e386 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -19,6 +19,17 @@ #include #include +class BiboumiTLSPolicy: public Botan::TLS::Policy +{ +public: +#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,33) + bool use_ecc_point_compression() const override + { + return true; + } +#endif +}; + /** * Does all the read/write, buffering etc. With optional tls. * But doesn’t do any connect() or accept() or anything else. @@ -191,7 +202,7 @@ private: * Botan stuff to manipulate a TLS session. */ static Botan::AutoSeeded_RNG rng; - static Botan::TLS::Policy policy; + static BiboumiTLSPolicy policy; static Botan::TLS::Session_Manager_In_Memory session_manager; protected: BasicCredentialsManager credential_manager; -- cgit v1.2.3 From 3f53db79c0f010dfb1ac1f4aa8ed098d42c5b96f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 2 Dec 2016 16:50:28 +0100 Subject: Use the new botan 1.11.32 Tls::Client API (but stay compatible with older ones) --- louloulibs/network/tcp_socket_handler.cpp | 20 ++++++++++++-------- louloulibs/network/tcp_socket_handler.hpp | 23 +++++++++++++++++------ 2 files changed, 29 insertions(+), 14 deletions(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index 7c33ab8..27b098a 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -221,10 +221,14 @@ void TCPSocketHandler::start_tls(const std::string& address, const std::string& { Botan::TLS::Server_Information server_info(address, "irc", std::stoul(port)); this->tls = std::make_unique( - std::bind(&TCPSocketHandler::tls_output_fn, this, ph::_1, ph::_2), - std::bind(&TCPSocketHandler::tls_data_cb, this, ph::_1, ph::_2), - std::bind(&TCPSocketHandler::tls_alert_cb, this, ph::_1, ph::_2, ph::_3), - std::bind(&TCPSocketHandler::tls_handshake_cb, this, ph::_1), +# if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,32) + *this, +# else + [this](const Botan::byte* data, size_t size) { this->tls_emit_data(data, size); }, + [this](const Botan::byte* data, size_t size) { this->tls_record_received(0, data, size); }, + [this](Botan::TLS::Alert alert, const Botan::byte*, size_t) { this->tls_alert(alert); }, + [this](const Botan::TLS::Session& session) { return this->tls_session_established(session); }, +# endif session_manager, this->credential_manager, policy, rng, server_info, Botan::TLS::Protocol_Version::latest_tls_version()); } @@ -277,7 +281,7 @@ void TCPSocketHandler::tls_send(std::string&& data) std::make_move_iterator(data.end())); } -void TCPSocketHandler::tls_data_cb(const Botan::byte* data, size_t size) +void TCPSocketHandler::tls_record_received(uint64_t, const Botan::byte *data, size_t size) { this->in_buf += std::string(reinterpret_cast(data), size); @@ -285,17 +289,17 @@ void TCPSocketHandler::tls_data_cb(const Botan::byte* data, size_t size) this->parse_in_buffer(size); } -void TCPSocketHandler::tls_output_fn(const Botan::byte* data, size_t size) +void TCPSocketHandler::tls_emit_data(const Botan::byte *data, size_t size) { this->raw_send(std::string(reinterpret_cast(data), size)); } -void TCPSocketHandler::tls_alert_cb(Botan::TLS::Alert alert, const Botan::byte*, size_t) +void TCPSocketHandler::tls_alert(Botan::TLS::Alert alert) { log_debug("tls_alert: ", alert.type_string()); } -bool TCPSocketHandler::tls_handshake_cb(const Botan::TLS::Session& session) +bool TCPSocketHandler::tls_session_established(const Botan::TLS::Session& session) { log_debug("Handshake with ", session.server_info().hostname(), " complete.", " Version: ", session.version().to_string(), diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index c37e386..c33c8f2 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -19,22 +19,33 @@ #include #include +#ifdef BOTAN_FOUND class BiboumiTLSPolicy: public Botan::TLS::Policy { public: -#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,33) +# if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,33) bool use_ecc_point_compression() const override { return true; } -#endif +# endif }; +# if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,32) +# define BOTAN_TLS_CALLBACKS_OVERRIDE override final +# else +# define BOTAN_TLS_CALLBACKS_OVERRIDE +# endif +#endif + /** * 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 +#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,32) + ,public Botan::TLS::Callbacks +#endif { protected: ~TCPSocketHandler(); @@ -139,22 +150,22 @@ private: * Called by the tls object that some data has been decrypt. We call * parse_in_buffer() to handle that unencrypted data. */ - void tls_data_cb(const Botan::byte* data, size_t size); + void tls_record_received(uint64_t rec_no, const Botan::byte* data, size_t size) BOTAN_TLS_CALLBACKS_OVERRIDE; /** * Called by the tls object to indicate that some data has been encrypted * and is now ready to be sent on the socket as is. */ - void tls_output_fn(const Botan::byte* data, size_t size); + void tls_emit_data(const Botan::byte* data, size_t size) BOTAN_TLS_CALLBACKS_OVERRIDE; /** * Called by the tls object to indicate that a TLS alert has been * received. We don’t use it, we just log some message, at the moment. */ - void tls_alert_cb(Botan::TLS::Alert alert, const Botan::byte*, size_t); + void tls_alert(Botan::TLS::Alert alert) BOTAN_TLS_CALLBACKS_OVERRIDE; /** * Called by the tls object at the end of the TLS handshake. We don't do * anything here appart from logging the TLS session information. */ - bool tls_handshake_cb(const Botan::TLS::Session& session); + bool tls_session_established(const Botan::TLS::Session& session) BOTAN_TLS_CALLBACKS_OVERRIDE; /** * Called whenever the tls session goes from inactive to active. This * means that the handshake has just been successfully done, and we can -- cgit v1.2.3 From 732f53d798c86558e1e625c22e957243bb2d6467 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 18 Nov 2016 20:54:41 +0100 Subject: Clean a few more things --- louloulibs/network/dns_handler.cpp | 1 - louloulibs/network/dns_handler.hpp | 3 ++- louloulibs/network/tcp_socket_handler.hpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/dns_handler.cpp b/louloulibs/network/dns_handler.cpp index fef0cfc..b1807b5 100644 --- a/louloulibs/network/dns_handler.cpp +++ b/louloulibs/network/dns_handler.cpp @@ -8,7 +8,6 @@ #include #include -#include DNSHandler DNSHandler::instance; diff --git a/louloulibs/network/dns_handler.hpp b/louloulibs/network/dns_handler.hpp index fd1729d..53a7799 100644 --- a/louloulibs/network/dns_handler.hpp +++ b/louloulibs/network/dns_handler.hpp @@ -20,8 +20,9 @@ class DNSSocketHandler; class DNSHandler { -public: +private: DNSHandler(); +public: ~DNSHandler() = default; DNSHandler(const DNSHandler&) = delete; DNSHandler(DNSHandler&&) = delete; diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index c33c8f2..5863d4a 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -204,8 +204,8 @@ protected: /** * Called when we detect a disconnection from the remote host. */ - virtual void on_connection_close(const std::string& error) = 0; - virtual void on_connection_failed(const std::string& error) = 0; + virtual void on_connection_close(const std::string&) {} + virtual void on_connection_failed(const std::string&) {} private: #ifdef BOTAN_FOUND -- cgit v1.2.3 From 3ef78f44180c9f6ed7a98672a69ea0f3b35b1eac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sun, 4 Dec 2016 16:19:43 +0100 Subject: Add some missing botan includes --- louloulibs/network/tcp_socket_handler.hpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index 5863d4a..8825a3a 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -1,6 +1,5 @@ #pragma once - #include "louloulibs.h" #include @@ -20,6 +19,11 @@ #include #ifdef BOTAN_FOUND + +# include +# include +# include + class BiboumiTLSPolicy: public Botan::TLS::Policy { public: -- cgit v1.2.3 From a66c67e291327c3ae5d6005a38c8d257b4333581 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sun, 4 Dec 2016 16:26:32 +0100 Subject: Add a missing ifdef botan --- louloulibs/network/tcp_socket_handler.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index 8825a3a..a44036d 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -47,8 +47,10 @@ public: * But doesn’t do any connect() or accept() or anything else. */ class TCPSocketHandler: public SocketHandler -#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,32) +#ifdef BOTAN_FOUND +# if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,32) ,public Botan::TLS::Callbacks +# endif #endif { protected: -- cgit v1.2.3 From 7784c568432231c737c789b065af6b81e038c54d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sun, 4 Dec 2016 22:17:37 +0100 Subject: Update the verify_certificate_chain code to work with botan >= 1.11.34 as well --- louloulibs/network/credentials_manager.cpp | 35 +++++++++++++++++++++++------- louloulibs/network/credentials_manager.hpp | 15 +++++++++++++ louloulibs/network/tcp_socket_handler.cpp | 25 +++++++++++++++++++++ louloulibs/network/tcp_socket_handler.hpp | 9 ++++++++ 4 files changed, 76 insertions(+), 8 deletions(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/credentials_manager.cpp b/louloulibs/network/credentials_manager.cpp index ed04d24..289307b 100644 --- a/louloulibs/network/credentials_manager.cpp +++ b/louloulibs/network/credentials_manager.cpp @@ -37,6 +37,28 @@ void BasicCredentialsManager::set_trusted_fingerprint(const std::string& fingerp this->trusted_fingerprint = fingerprint; } +const std::string& BasicCredentialsManager::get_trusted_fingerprint() const +{ + return this->trusted_fingerprint; +} + +void check_tls_certificate(const std::vector& certs, + const std::string& hostname, const std::string& trusted_fingerprint, + std::exception_ptr exc) +{ + + if (!trusted_fingerprint.empty() && !certs.empty() && + trusted_fingerprint == certs[0].fingerprint() && + certs[0].matches_dns_name(hostname)) + // We trust the certificate, based on the trusted fingerprint and + // the fact that the hostname matches + return; + + if (exc) + std::rethrow_exception(exc); +} + +#if BOTAN_VERSION_CODE < BOTAN_VERSION_CODE_FOR(1,11,34) void BasicCredentialsManager::verify_certificate_chain(const std::string& type, const std::string& purported_hostname, const std::vector& certs) @@ -50,17 +72,14 @@ void BasicCredentialsManager::verify_certificate_chain(const std::string& type, catch (const std::exception& tls_exception) { log_warning("TLS certificate check failed: ", tls_exception.what()); - if (!this->trusted_fingerprint.empty() && !certs.empty() && - this->trusted_fingerprint == certs[0].fingerprint() && - certs[0].matches_dns_name(purported_hostname)) - // We trust the certificate, based on the trusted fingerprint and - // the fact that the hostname matches - return; - + std::exception_ptr exception_ptr{}; if (this->socket_handler->abort_on_invalid_cert()) - throw; + exception_ptr = std::current_exception(); + + check_tls_certificate(certs, purported_hostname, this->trusted_fingerprint, exception_ptr); } } +#endif bool BasicCredentialsManager::try_to_open_one_ca_bundle(const std::vector& paths) { diff --git a/louloulibs/network/credentials_manager.hpp b/louloulibs/network/credentials_manager.hpp index 7557372..29ee024 100644 --- a/louloulibs/network/credentials_manager.hpp +++ b/louloulibs/network/credentials_manager.hpp @@ -9,6 +9,18 @@ class TCPSocketHandler; +/** + * If the given cert isn’t valid, based on the given hostname + * and fingerprint, then throws the exception if it’s non-empty. + * + * Must be called after the standard (from Botan) way of + * checking the certificate, if we want to also accept certificates based + * on a trusted fingerprint. + */ +void check_tls_certificate(const std::vector& certs, + const std::string& hostname, const std::string& trusted_fingerprint, + std::exception_ptr exc); + class BasicCredentialsManager: public Botan::Credentials_Manager { public: @@ -19,12 +31,15 @@ public: BasicCredentialsManager& operator=(const BasicCredentialsManager&) = delete; BasicCredentialsManager& operator=(BasicCredentialsManager&&) = delete; +#if BOTAN_VERSION_CODE < BOTAN_VERSION_CODE_FOR(1,11,34) void verify_certificate_chain(const std::string& type, const std::string& purported_hostname, const std::vector&) override final; +#endif std::vector trusted_certificate_authorities(const std::string& type, const std::string& context) override final; void set_trusted_fingerprint(const std::string& fingerprint); + const std::string& get_trusted_fingerprint() const; private: const TCPSocketHandler* const socket_handler; diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index 27b098a..9d9a6aa 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -311,6 +311,31 @@ bool TCPSocketHandler::tls_session_established(const Botan::TLS::Session& sessio return true; } +#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,34) +void TCPSocketHandler::tls_verify_cert_chain(const std::vector& cert_chain, + const std::vector>& ocsp_responses, + const std::vector& trusted_roots, + Botan::Usage_Type usage, const std::string& hostname, + const Botan::TLS::Policy& policy) +{ + log_debug("Checking remote certificate for hostname ", hostname); + try + { + Botan::TLS::Callbacks::tls_verify_cert_chain(cert_chain, ocsp_responses, trusted_roots, usage, hostname, policy); + log_debug("Certificate is valid"); + } + catch (const std::exception& tls_exception) + { + log_warning("TLS certificate check failed: ", tls_exception.what()); + std::exception_ptr exception_ptr{}; + if (this->abort_on_invalid_cert()) + exception_ptr = std::current_exception(); + + check_tls_certificate(cert_chain, hostname, this->credential_manager.get_trusted_fingerprint(), exception_ptr); + } +} +#endif + void TCPSocketHandler::on_tls_activated() { this->send_data({}); diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index a44036d..3c6ff71 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -172,6 +172,15 @@ private: * anything here appart from logging the TLS session information. */ bool tls_session_established(const Botan::TLS::Session& session) BOTAN_TLS_CALLBACKS_OVERRIDE; + +#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,34) + void tls_verify_cert_chain(const std::vector& cert_chain, + const std::vector>& ocsp_responses, + const std::vector& trusted_roots, + Botan::Usage_Type usage, + const std::string& hostname, + const Botan::TLS::Policy& policy) BOTAN_TLS_CALLBACKS_OVERRIDE; +#endif /** * Called whenever the tls session goes from inactive to active. This * means that the handshake has just been successfully done, and we can -- cgit v1.2.3 From 75778f418751bf06687ccc99454683bb43a9daf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 7 Dec 2016 18:28:29 +0100 Subject: Add some missing includes --- louloulibs/network/resolver.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'louloulibs/network') diff --git a/louloulibs/network/resolver.cpp b/louloulibs/network/resolver.cpp index 2987aaa..3847d87 100644 --- a/louloulibs/network/resolver.cpp +++ b/louloulibs/network/resolver.cpp @@ -2,6 +2,8 @@ #include #include #include +#include + #include using namespace std::string_literals; -- cgit v1.2.3 From 7f2eb6073761ebddc37d7872e44d621d719d6828 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Thu, 8 Dec 2016 18:41:12 +0100 Subject: Init some members of TcpClientSocketHandler --- louloulibs/network/tcp_client_socket_handler.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/tcp_client_socket_handler.hpp b/louloulibs/network/tcp_client_socket_handler.hpp index 72f68d0..75e1364 100644 --- a/louloulibs/network/tcp_client_socket_handler.hpp +++ b/louloulibs/network/tcp_client_socket_handler.hpp @@ -62,9 +62,9 @@ class TCPClientSocketHandler: public TCPSocketHandler * 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; + struct addrinfo addrinfo{}; + struct sockaddr_in6 ai_addr{}; + socklen_t ai_addrlen{}; /** * Hostname we are connected/connecting to @@ -75,7 +75,7 @@ class TCPClientSocketHandler: public TCPSocketHandler */ std::string port; - uint16_t local_port; + uint16_t local_port{}; bool connected; bool connecting; -- cgit v1.2.3 From e1e740e35edeccfc04d2808fc215c6a12698305f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Thu, 8 Dec 2016 21:25:20 +0100 Subject: =?UTF-8?q?Don=E2=80=99t=20use=20global=20static=20members=20but?= =?UTF-8?q?=20functions=20that=20return=20a=20reference=20to=20an=20intern?= =?UTF-8?q?al=20static=20object?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/randombit/botan/issues/761 --- louloulibs/network/tcp_socket_handler.cpp | 26 ++++++++++++++++++++------ louloulibs/network/tcp_socket_handler.hpp | 7 ------- 2 files changed, 20 insertions(+), 13 deletions(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index 9d9a6aa..6aef2b1 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -15,10 +15,24 @@ # include # include -Botan::AutoSeeded_RNG TCPSocketHandler::rng; -BiboumiTLSPolicy TCPSocketHandler::policy; -Botan::TLS::Session_Manager_In_Memory TCPSocketHandler::session_manager(TCPSocketHandler::rng); - +namespace +{ + Botan::AutoSeeded_RNG& get_rng() + { + static Botan::AutoSeeded_RNG rng{}; + return rng; + } + BiboumiTLSPolicy& get_policy() + { + static BiboumiTLSPolicy policy{}; + return policy; + } + Botan::TLS::Session_Manager_In_Memory& get_session_manager() + { + static Botan::TLS::Session_Manager_In_Memory session_manager{get_rng()}; + return session_manager; + } +} #endif #ifndef UIO_FASTIOV @@ -229,8 +243,8 @@ void TCPSocketHandler::start_tls(const std::string& address, const std::string& [this](Botan::TLS::Alert alert, const Botan::byte*, size_t) { this->tls_alert(alert); }, [this](const Botan::TLS::Session& session) { return this->tls_session_established(session); }, # endif - session_manager, this->credential_manager, policy, - rng, server_info, Botan::TLS::Protocol_Version::latest_tls_version()); + get_session_manager(), this->credential_manager, get_policy(), + get_rng(), server_info, Botan::TLS::Protocol_Version::latest_tls_version()); } void TCPSocketHandler::tls_recv() diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index 3c6ff71..7fc40c2 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -222,14 +222,7 @@ protected: virtual void on_connection_close(const std::string&) {} virtual void on_connection_failed(const std::string&) {} -private: #ifdef BOTAN_FOUND - /** - * Botan stuff to manipulate a TLS session. - */ - static Botan::AutoSeeded_RNG rng; - static BiboumiTLSPolicy policy; - static Botan::TLS::Session_Manager_In_Memory session_manager; protected: BasicCredentialsManager credential_manager; private: -- cgit v1.2.3 From 5b56007828f20c763df3f36ceed809188880663e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 6 Jan 2017 22:58:18 +0100 Subject: Use udns instead of c-ares fix #3226 --- louloulibs/network/dns_handler.cpp | 125 ++-------- louloulibs/network/dns_handler.hpp | 46 +--- louloulibs/network/dns_socket_handler.cpp | 32 ++- louloulibs/network/dns_socket_handler.hpp | 29 +-- louloulibs/network/resolver.cpp | 280 ++++++++++++++--------- louloulibs/network/resolver.hpp | 45 ++-- louloulibs/network/tcp_client_socket_handler.cpp | 6 +- 7 files changed, 246 insertions(+), 317 deletions(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/dns_handler.cpp b/louloulibs/network/dns_handler.cpp index b1807b5..ad0ad38 100644 --- a/louloulibs/network/dns_handler.cpp +++ b/louloulibs/network/dns_handler.cpp @@ -1,5 +1,5 @@ #include -#ifdef CARES_FOUND +#ifdef UDNS_FOUND #include #include @@ -7,123 +7,40 @@ #include -#include +#include -DNSHandler DNSHandler::instance; +#include + +class Resolver; using namespace std::string_literals; -DNSHandler::DNSHandler(): - socket_handlers{}, - channel{nullptr} -{ - int ares_error; - if ((ares_error = ::ares_library_init(ARES_LIB_INIT_ALL)) != 0) - throw std::runtime_error("Failed to initialize c-ares lib: "s + ares_strerror(ares_error)); - struct ares_options options = {}; - // The default timeout values are way too high - options.timeout = 1000; - options.tries = 3; - if ((ares_error = ::ares_init_options(&this->channel, - &options, - ARES_OPT_TIMEOUTMS|ARES_OPT_TRIES)) != ARES_SUCCESS) - throw std::runtime_error("Failed to initialize c-ares channel: "s + ares_strerror(ares_error)); -} -ares_channel& DNSHandler::get_channel() -{ - return this->channel; -} +std::unique_ptr DNSHandler::socket_handler{}; -void DNSHandler::destroy() +DNSHandler::DNSHandler(std::shared_ptr poller) { - this->remove_all_sockets_from_poller(); - this->socket_handlers.clear(); - ::ares_destroy(this->channel); - ::ares_library_cleanup(); + dns_init(nullptr, 0); + const auto socket = dns_open(nullptr); + if (socket == -1) + throw std::runtime_error("Failed to initialize udns socket: "s + strerror(errno)); + + DNSHandler::socket_handler = std::make_unique(poller, socket); } -void DNSHandler::gethostbyname(const std::string& name, ares_host_callback callback, - void* data, int family) +void DNSHandler::destroy() { - ::ares_gethostbyname(this->channel, name.data(), family, - callback, data); + DNSHandler::socket_handler.reset(nullptr); + dns_close(nullptr); } -void DNSHandler::watch_dns_sockets(std::shared_ptr& poller) +void DNSHandler::watch() { - fd_set readers; - fd_set writers; - - FD_ZERO(&readers); - FD_ZERO(&writers); - - int ndfs = ::ares_fds(this->channel, &readers, &writers); - // For each existing DNS socket, see if we are still supposed to watch it, - // if not then erase it - this->socket_handlers.erase( - std::remove_if(this->socket_handlers.begin(), this->socket_handlers.end(), - [&readers](const auto& dns_socket) - { - return !FD_ISSET(dns_socket->get_socket(), &readers); - }), - this->socket_handlers.end()); - - for (auto i = 0; i < ndfs; ++i) - { - bool read = FD_ISSET(i, &readers); - bool write = FD_ISSET(i, &writers); - // Look for the DNSSocketHandler with this fd - auto it = std::find_if(this->socket_handlers.begin(), - this->socket_handlers.end(), - [i](const auto& socket_handler) - { - return i == socket_handler->get_socket(); - }); - if (!read && !write) // No need to read or write to it - { // If found, erase it and stop watching it because it is not - // needed anymore - if (it != this->socket_handlers.end()) - // The socket destructor removes it from the poller - this->socket_handlers.erase(it); - } - else // We need to write and/or read to it - { // If not found, create it because we need to watch it - if (it == this->socket_handlers.end()) - { - this->socket_handlers.emplace(this->socket_handlers.begin(), - std::make_unique(poller, *this, i)); - it = this->socket_handlers.begin(); - } - poller->add_socket_handler(it->get()); - if (write) - poller->watch_send_events(it->get()); - } - } - // Cancel previous timer, if any. - TimedEventsManager::instance().cancel("DNS timeout"); - struct timeval tv; - struct timeval* tvp; - tvp = ::ares_timeout(this->channel, NULL, &tv); - if (tvp) - { - auto future_time = std::chrono::steady_clock::now() + std::chrono::seconds(tvp->tv_sec) + \ - std::chrono::microseconds(tvp->tv_usec); - TimedEventsManager::instance().add_event(TimedEvent(std::move(future_time), - [this]() - { - for (auto& dns_socket_handler: this->socket_handlers) - dns_socket_handler->on_recv(); - }, - "DNS timeout")); - } + DNSHandler::socket_handler->watch(); } -void DNSHandler::remove_all_sockets_from_poller() +void DNSHandler::unwatch() { - for (const auto& socket_handler: this->socket_handlers) - { - socket_handler->remove_from_poller(); - } + DNSHandler::socket_handler->unwatch(); } -#endif /* CARES_FOUND */ +#endif /* UDNS_FOUND */ diff --git a/louloulibs/network/dns_handler.hpp b/louloulibs/network/dns_handler.hpp index 53a7799..0148156 100644 --- a/louloulibs/network/dns_handler.hpp +++ b/louloulibs/network/dns_handler.hpp @@ -1,59 +1,37 @@ #pragma once #include -#ifdef CARES_FOUND +#ifdef UDNS_FOUND -class TCPSocketHandler; class Poller; -class DNSSocketHandler; -# include -# include -# include -# include +#include -/** - * Class managing DNS resolution. It should only be statically instanciated - * once in SocketHandler. It manages ares channel and calls various - * functions of that library. - */ +#include +#include +#include class DNSHandler { -private: - DNSHandler(); public: + DNSHandler(std::shared_ptr poller); ~DNSHandler() = default; + DNSHandler(const DNSHandler&) = delete; DNSHandler(DNSHandler&&) = delete; DNSHandler& operator=(const DNSHandler&) = delete; DNSHandler& operator=(DNSHandler&&) = delete; - void gethostbyname(const std::string& name, ares_host_callback callback, - void* socket_handler, int family); - /** - * Call ares_fds to know what fd needs to be watched by the poller, create - * or destroy DNSSocketHandlers depending on the result. - */ - void watch_dns_sockets(std::shared_ptr& poller); - /** - * Destroy and stop watching all the DNS sockets. Then de-init the channel - * and library. - */ void destroy(); - void remove_all_sockets_from_poller(); - ares_channel& get_channel(); - static DNSHandler instance; + static void watch(); + static void unwatch(); private: /** - * The list of sockets that needs to be watched, according to the last - * call to ares_fds. DNSSocketHandlers are added to it or removed from it - * in the watch_dns_sockets() method + * Manager for the socket returned by udns, that we need to watch with the poller */ - std::vector> socket_handlers; - ares_channel channel; + static std::unique_ptr socket_handler; }; -#endif /* CARES_FOUND */ +#endif /* UDNS_FOUND */ diff --git a/louloulibs/network/dns_socket_handler.cpp b/louloulibs/network/dns_socket_handler.cpp index a69f59b..ad744a9 100644 --- a/louloulibs/network/dns_socket_handler.cpp +++ b/louloulibs/network/dns_socket_handler.cpp @@ -1,34 +1,27 @@ #include -#ifdef CARES_FOUND +#ifdef UDNS_FOUND #include #include #include -#include +#include DNSSocketHandler::DNSSocketHandler(std::shared_ptr poller, - DNSHandler& handler, const socket_t socket): - SocketHandler(poller, socket), - handler(handler) + SocketHandler(poller, socket) { + poller->add_socket_handler(this); } -void DNSSocketHandler::on_recv() +DNSSocketHandler::~DNSSocketHandler() { - // always stop watching send and read events. We will re-watch them if the - // next call to ares_fds tell us to - this->handler.remove_all_sockets_from_poller(); - ::ares_process_fd(DNSHandler::instance.get_channel(), this->socket, ARES_SOCKET_BAD); + this->unwatch(); } -void DNSSocketHandler::on_send() +void DNSSocketHandler::on_recv() { - // always stop watching send and read events. We will re-watch them if the - // next call to ares_fds tell us to - this->handler.remove_all_sockets_from_poller(); - ::ares_process_fd(DNSHandler::instance.get_channel(), ARES_SOCKET_BAD, this->socket); + dns_ioevent(nullptr, 0); } bool DNSSocketHandler::is_connected() const @@ -36,10 +29,15 @@ bool DNSSocketHandler::is_connected() const return true; } -void DNSSocketHandler::remove_from_poller() +void DNSSocketHandler::unwatch() { if (this->poller->is_managing_socket(this->socket)) this->poller->remove_socket_handler(this->socket); } -#endif /* CARES_FOUND */ +void DNSSocketHandler::watch() +{ + this->poller->add_socket_handler(this); +} + +#endif /* UDNS_FOUND */ diff --git a/louloulibs/network/dns_socket_handler.hpp b/louloulibs/network/dns_socket_handler.hpp index e3fed0c..e12f145 100644 --- a/louloulibs/network/dns_socket_handler.hpp +++ b/louloulibs/network/dns_socket_handler.hpp @@ -1,44 +1,33 @@ #pragma once #include -#ifdef CARES_FOUND +#ifdef UDNS_FOUND #include -#include /** - * Manage a socket returned by ares_fds. We do not create, open or close the - * socket ourself: this is done by c-ares. We just call ares_process_fd() - * with the correct parameters, depending on what can be done on that socket - * (Poller reported it to be writable or readeable) + * Manage the UDP socket provided by udns, we do not create, open or close the + * socket ourself: this is done by udns. We only watch it for readability */ - -class DNSHandler; - class DNSSocketHandler: public SocketHandler { public: - explicit DNSSocketHandler(std::shared_ptr poller, DNSHandler& handler, const socket_t socket); - ~DNSSocketHandler() = default; + explicit DNSSocketHandler(std::shared_ptr poller, const socket_t socket); + ~DNSSocketHandler(); DNSSocketHandler(const DNSSocketHandler&) = delete; DNSSocketHandler(DNSSocketHandler&&) = delete; DNSSocketHandler& operator=(const DNSSocketHandler&) = delete; DNSSocketHandler& operator=(DNSSocketHandler&&) = delete; - /** - * Just call dns_process_fd, c-ares will do its work of send()ing or - * recv()ing the data it wants on that socket. - */ void on_recv() override final; - void on_send() override final; + /** * Always true, see the comment for connect() */ bool is_connected() const override final; - void remove_from_poller(); -private: - DNSHandler& handler; + void watch(); + void unwatch(); }; -#endif // CARES_FOUND +#endif // UDNS_FOUND diff --git a/louloulibs/network/resolver.cpp b/louloulibs/network/resolver.cpp index 3847d87..efb0cf0 100644 --- a/louloulibs/network/resolver.cpp +++ b/louloulibs/network/resolver.cpp @@ -1,19 +1,32 @@ #include +#include #include #include #include #include +#include +#include #include +#include +#include +#include using namespace std::string_literals; +static std::map dns_error_messages { + {DNS_E_TEMPFAIL, "Timeout while contacting DNS servers"}, + {DNS_E_PROTOCOL, "Misformatted DNS reply"}, + {DNS_E_NXDOMAIN, "Domain name not found"}, + {DNS_E_NOMEM, "Out of memory"}, + {DNS_E_BADQUERY, "Misformatted domain name"} +}; + Resolver::Resolver(): -#ifdef CARES_FOUND +#ifdef UDNS_FOUND resolved4(false), resolved6(false), resolving(false), - cares_addrinfo(nullptr), port{}, #endif resolved(false), @@ -26,15 +39,44 @@ void Resolver::resolve(const std::string& hostname, const std::string& port, { this->error_cb = error_cb; this->success_cb = success_cb; -#ifdef CARES_FOUND +#ifdef UDNS_FOUND this->port = port; #endif this->start_resolving(hostname, port); } -#ifdef CARES_FOUND -void Resolver::start_resolving(const std::string& hostname, const std::string&) +int Resolver::call_getaddrinfo(const char *name, const char* port, int flags) +{ + struct addrinfo hints; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_flags = flags; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + + struct addrinfo* addr_res = nullptr; + const int res = ::getaddrinfo(name, port, + &hints, &addr_res); + + if (res == 0 && addr_res) + { + if (!this->addr) + this->addr.reset(addr_res); + else + { // Append this result at the end of the linked list + struct addrinfo *rp = this->addr.get(); + while (rp->ai_next) + rp = rp->ai_next; + rp->ai_next = addr_res; + } + } + + return res; +} + +#ifdef UDNS_FOUND +void Resolver::start_resolving(const std::string& hostname, const std::string& port) { this->resolving = true; this->resolved = false; @@ -42,48 +84,139 @@ void Resolver::start_resolving(const std::string& hostname, const std::string&) this->resolved6 = false; this->error_msg.clear(); - this->cares_addrinfo = nullptr; + this->addr.reset(nullptr); - auto hostname4_resolved = [](void* arg, int status, int, - struct hostent* hostent) + // We first try to use it as an IP address directly. We tell getaddrinfo + // to NOT use any DNS resolution. + if (this->call_getaddrinfo(hostname.data(), port.data(), AI_NUMERICHOST) == 0) { - Resolver* resolver = static_cast(arg); - resolver->on_hostname4_resolved(status, hostent); - }; - auto hostname6_resolved = [](void* arg, int status, int, - struct hostent* hostent) + this->on_resolved(); + return; + } + + // Then we look into /etc/hosts to translate the given hostname + const auto hosts = this->look_in_etc_hosts(hostname); + if (!hosts.empty()) + { + for (const auto &host: hosts) + this->call_getaddrinfo(host.data(), port.data(), AI_NUMERICHOST); + this->on_resolved(); + return; + } + + // And finally, we try a DNS resolution + auto hostname6_resolved = [](dns_ctx*, dns_rr_a6* result, void* data) + { + Resolver* resolver = static_cast(data); + resolver->on_hostname6_resolved(result); + }; + + auto hostname4_resolved = [](dns_ctx*, dns_rr_a4* result, void* data) + { + Resolver* resolver = static_cast(data); + resolver->on_hostname4_resolved(result); + }; + + DNSHandler::watch(); + auto res = dns_submit_a4(nullptr, hostname.data(), 0, hostname4_resolved, this); + if (!res) + this->on_hostname4_resolved(nullptr); + res = dns_submit_a6(nullptr, hostname.data(), 0, hostname6_resolved, this); + if (!res) + this->on_hostname6_resolved(nullptr); + + this->start_timer(); +} + +void Resolver::start_timer() +{ + const auto timeout = dns_timeouts(nullptr, -1, 0); + if (timeout < 0) + return; + TimedEvent event(std::chrono::steady_clock::now() + std::chrono::seconds(timeout), [this]() { this->start_timer(); }, "DNS"); + TimedEventsManager::instance().add_event(std::move(event)); +} + +std::vector Resolver::look_in_etc_hosts(const std::string &hostname) +{ + std::ifstream hosts("/etc/hosts"); + std::string line; + + std::vector results; + while (std::getline(hosts, line)) { - Resolver* resolver = static_cast(arg); - resolver->on_hostname6_resolved(status, hostent); - }; - - DNSHandler::instance.gethostbyname(hostname, hostname6_resolved, - this, AF_INET6); - DNSHandler::instance.gethostbyname(hostname, hostname4_resolved, - this, AF_INET); + if (line.empty()) + continue; + + std::string ip; + std::istringstream line_stream(line); + line_stream >> ip; + if (ip.empty() || ip[0] == '#') + continue; + + std::string host; + while (line_stream >> host && !host.empty() && host[0] != '#') + { + if (hostname == host) + { + results.push_back(ip); + break; + } + } + } + return results; } -void Resolver::on_hostname4_resolved(int status, struct hostent* hostent) +void Resolver::on_hostname4_resolved(dns_rr_a4 *result) { + if (dns_active(nullptr) == 0) + DNSHandler::unwatch(); + this->resolved4 = true; - if (status == ARES_SUCCESS) - this->fill_ares_addrinfo4(hostent); + + const auto status = dns_status(nullptr); + + if (status >= 0 && result) + { + char buf[INET6_ADDRSTRLEN]; + + for (auto i = 0; i < result->dnsa4_nrr; ++i) + { + inet_ntop(AF_INET, &result->dnsa4_addr[i], buf, sizeof(buf)); + this->call_getaddrinfo(buf, this->port.data(), AI_NUMERICHOST); + } + } else - this->error_msg = ::ares_strerror(status); + { + const auto error = dns_error_messages.find(status); + if (error != end(dns_error_messages)) + this->error_msg = error->second; + } - if (this->resolved4 && this->resolved6) + if (this->resolved6 && this->resolved4) this->on_resolved(); } -void Resolver::on_hostname6_resolved(int status, struct hostent* hostent) +void Resolver::on_hostname6_resolved(dns_rr_a6 *result) { + if (dns_active(nullptr) == 0) + DNSHandler::unwatch(); + this->resolved6 = true; - if (status == ARES_SUCCESS) - this->fill_ares_addrinfo6(hostent); - else - this->error_msg = ::ares_strerror(status); + char buf[INET6_ADDRSTRLEN]; + + const auto status = dns_status(nullptr); - if (this->resolved4 && this->resolved6) + if (status >= 0 && result) + { + for (auto i = 0; i < result->dnsa6_nrr; ++i) + { + inet_ntop(AF_INET6, &result->dnsa6_addr[i], buf, sizeof(buf)); + this->call_getaddrinfo(buf, this->port.data(), AI_NUMERICHOST); + } + } + + if (this->resolved6 && this->resolved4) this->on_resolved(); } @@ -91,100 +224,26 @@ void Resolver::on_resolved() { this->resolved = true; this->resolving = false; - if (!this->cares_addrinfo) + if (!this->addr) { if (this->error_cb) this->error_cb(this->error_msg.data()); } else { - this->addr.reset(this->cares_addrinfo); if (this->success_cb) this->success_cb(this->addr.get()); } } -void Resolver::fill_ares_addrinfo4(const struct hostent* hostent) -{ - struct addrinfo* prev = this->cares_addrinfo; - struct in_addr** address = reinterpret_cast(hostent->h_addr_list); - - while (*address) - { - // Create a new addrinfo list element, and fill it - struct addrinfo* current = new struct addrinfo; - current->ai_flags = 0; - current->ai_family = hostent->h_addrtype; - current->ai_socktype = SOCK_STREAM; - current->ai_protocol = 0; - current->ai_addrlen = sizeof(struct sockaddr_in); - - struct sockaddr_in* ai_addr = new struct sockaddr_in; - - ai_addr->sin_family = hostent->h_addrtype; - ai_addr->sin_port = htons(std::strtoul(this->port.data(), nullptr, 10)); - ai_addr->sin_addr.s_addr = (*address)->s_addr; - - current->ai_addr = reinterpret_cast(ai_addr); - current->ai_next = nullptr; - current->ai_canonname = nullptr; - - current->ai_next = prev; - this->cares_addrinfo = current; - prev = current; - ++address; - } -} - -void Resolver::fill_ares_addrinfo6(const struct hostent* hostent) -{ - struct addrinfo* prev = this->cares_addrinfo; - struct in6_addr** address = reinterpret_cast(hostent->h_addr_list); - - while (*address) - { - // Create a new addrinfo list element, and fill it - struct addrinfo* current = new struct addrinfo; - current->ai_flags = 0; - current->ai_family = hostent->h_addrtype; - current->ai_socktype = SOCK_STREAM; - current->ai_protocol = 0; - current->ai_addrlen = sizeof(struct sockaddr_in6); - - struct sockaddr_in6* ai_addr = new struct sockaddr_in6; - ai_addr->sin6_family = hostent->h_addrtype; - ai_addr->sin6_port = htons(std::strtoul(this->port.data(), nullptr, 10)); - ::memcpy(ai_addr->sin6_addr.s6_addr, (*address)->s6_addr, sizeof(ai_addr->sin6_addr.s6_addr)); - ai_addr->sin6_flowinfo = 0; - ai_addr->sin6_scope_id = 0; - - current->ai_addr = reinterpret_cast(ai_addr); - current->ai_canonname = nullptr; - - current->ai_next = prev; - this->cares_addrinfo = current; - prev = current; - ++address; - } -} - -#else // ifdef CARES_FOUND +#else // ifdef UDNS_FOUND void Resolver::start_resolving(const std::string& hostname, const std::string& port) { // If the resolution fails, the addr will be unset this->addr.reset(nullptr); - struct addrinfo hints; - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_flags = 0; - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = 0; - - struct addrinfo* addr_res = nullptr; - const int res = ::getaddrinfo(hostname.data(), port.data(), - &hints, &addr_res); + const auto res = this->call_getaddrinfo(hostname.data(), port.data(), 0); this->resolved = true; @@ -196,12 +255,11 @@ void Resolver::start_resolving(const std::string& hostname, const std::string& p } else { - this->addr.reset(addr_res); if (this->success_cb) this->success_cb(this->addr.get()); } } -#endif // ifdef CARES_FOUND +#endif // ifdef UDNS_FOUND std::string addr_to_string(const struct addrinfo* rp) { diff --git a/louloulibs/network/resolver.hpp b/louloulibs/network/resolver.hpp index 7365f93..f516da5 100644 --- a/louloulibs/network/resolver.hpp +++ b/louloulibs/network/resolver.hpp @@ -1,38 +1,31 @@ #pragma once - #include "louloulibs.h" #include +#include #include #include #include #include #include +#include class AddrinfoDeleter { public: void operator()(struct addrinfo* addr) { -#ifdef CARES_FOUND - while (addr) - { - delete addr->ai_addr; - auto next = addr->ai_next; - delete addr; - addr = next; - } -#else freeaddrinfo(addr); -#endif } }; + class Resolver { public: + using ErrorCallbackType = std::function; using SuccessCallbackType = std::function; @@ -45,7 +38,7 @@ public: bool is_resolving() const { -#ifdef CARES_FOUND +#ifdef UDNS_FOUND return this->resolving; #else return false; @@ -68,11 +61,10 @@ public: void clear() { -#ifdef CARES_FOUND +#ifdef UDNS_FOUND this->resolved6 = false; this->resolved4 = false; this->resolving = false; - this->cares_addrinfo = nullptr; this->port.clear(); #endif this->resolved = false; @@ -85,12 +77,18 @@ public: private: void start_resolving(const std::string& hostname, const std::string& port); -#ifdef CARES_FOUND - void on_hostname4_resolved(int status, struct hostent* hostent); - void on_hostname6_resolved(int status, struct hostent* hostent); + std::vector look_in_etc_hosts(const std::string& hostname); + /** + * Call getaddrinfo() on the given hostname or IP, and append the result + * to our internal addrinfo list. Return getaddrinfo()’s return value. + */ + int call_getaddrinfo(const char* name, const char* port, int flags); + +#ifdef UDNS_FOUND + void on_hostname4_resolved(dns_rr_a4 *result); + void on_hostname6_resolved(dns_rr_a6 *result); - void fill_ares_addrinfo4(const struct hostent* hostent); - void fill_ares_addrinfo6(const struct hostent* hostent); + void start_timer(); void on_resolved(); @@ -99,14 +97,6 @@ private: bool resolving; - /** - * When using c-ares to resolve the host asynchronously, we need the - * c-ares callbacks to fill a structure (a struct addrinfo, for - * compatibility with getaddrinfo and the rest of the code that works when - * c-ares is not used) with all returned values (for example an IPv6 and - * an IPv4). The pointer is given to the unique_ptr to manage its lifetime. - */ - struct addrinfo* cares_addrinfo; std::string port; #endif @@ -117,7 +107,6 @@ private: bool resolved; std::string error_msg; - std::unique_ptr addr; ErrorCallbackType error_cb; diff --git a/louloulibs/network/tcp_client_socket_handler.cpp b/louloulibs/network/tcp_client_socket_handler.cpp index 7f21e3f..4e6445c 100644 --- a/louloulibs/network/tcp_client_socket_handler.cpp +++ b/louloulibs/network/tcp_client_socket_handler.cpp @@ -79,7 +79,7 @@ void TCPClientSocketHandler::connect(const std::string& address, const std::stri if (!this->connecting) { - // Get the addrinfo from getaddrinfo (or ares_gethostbyname), only if + // Get the addrinfo from getaddrinfo (or using udns), only if // this is the first call of this function. if (!this->resolver.is_resolved()) { @@ -103,8 +103,8 @@ void TCPClientSocketHandler::connect(const std::string& address, const std::stri } else { - // The c-ares resolved the hostname and the available addresses - // where saved in the cares_addrinfo linked list. Now, just use + // The DNS resolver resolved the hostname and the available addresses + // where saved in the addrinfo linked list. Now, just use // this list to try to connect. addr_res = this->resolver.get_result().get(); if (!addr_res) -- cgit v1.2.3 From 6bf9cedbeb30740c267ea283058c93ed63648fc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 6 Jan 2017 23:13:47 +0100 Subject: Add a missing errno include --- louloulibs/network/dns_handler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/dns_handler.cpp b/louloulibs/network/dns_handler.cpp index ad0ad38..fbd2763 100644 --- a/louloulibs/network/dns_handler.cpp +++ b/louloulibs/network/dns_handler.cpp @@ -8,7 +8,7 @@ #include #include - +#include #include class Resolver; -- cgit v1.2.3 From e31ff3e9e94d943d4f307eb6ab8cee7fbd11b565 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 6 Jan 2017 23:45:26 +0100 Subject: Fix some issues found by sonar cube --- louloulibs/network/dns_handler.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/dns_handler.hpp b/louloulibs/network/dns_handler.hpp index 0148156..78ffe4d 100644 --- a/louloulibs/network/dns_handler.hpp +++ b/louloulibs/network/dns_handler.hpp @@ -14,7 +14,7 @@ class Poller; class DNSHandler { public: - DNSHandler(std::shared_ptr poller); + explicit DNSHandler(std::shared_ptr poller); ~DNSHandler() = default; DNSHandler(const DNSHandler&) = delete; -- cgit v1.2.3 From dd129366575f371bcec51b7303bfb273d6edc8a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Thu, 19 Jan 2017 18:41:29 +0100 Subject: Do not require revocation info for a certificate to be valid If anyone has any reason to believe this is a bad idea, please let me know. --- louloulibs/network/tcp_socket_handler.hpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'louloulibs/network') diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index 7fc40c2..600405d 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -32,6 +32,10 @@ public: { return true; } + bool require_cert_revocation_info() const override + { + return false; + } # endif }; -- cgit v1.2.3 From a5d35c2455d9e3e5724c9a906deb1b58e5a5f87b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 31 Jan 2017 23:47:45 +0100 Subject: Add some missing ifndef for udns --- louloulibs/network/resolver.cpp | 6 +++++- louloulibs/network/resolver.hpp | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/resolver.cpp b/louloulibs/network/resolver.cpp index efb0cf0..0655b1b 100644 --- a/louloulibs/network/resolver.cpp +++ b/louloulibs/network/resolver.cpp @@ -4,7 +4,9 @@ #include #include #include -#include +#ifdef UDNS_FOUND +# include +#endif #include #include @@ -14,6 +16,7 @@ using namespace std::string_literals; +#ifdef UDNS_FOUND static std::map dns_error_messages { {DNS_E_TEMPFAIL, "Timeout while contacting DNS servers"}, {DNS_E_PROTOCOL, "Misformatted DNS reply"}, @@ -21,6 +24,7 @@ static std::map dns_error_messages { {DNS_E_NOMEM, "Out of memory"}, {DNS_E_BADQUERY, "Misformatted domain name"} }; +#endif Resolver::Resolver(): #ifdef UDNS_FOUND diff --git a/louloulibs/network/resolver.hpp b/louloulibs/network/resolver.hpp index f516da5..800c7ec 100644 --- a/louloulibs/network/resolver.hpp +++ b/louloulibs/network/resolver.hpp @@ -10,7 +10,9 @@ #include #include #include -#include +#ifdef UDNS_FOUND +# include +#endif class AddrinfoDeleter { -- cgit v1.2.3 From fa3d44e7aaf43487f8fed62c9398ade6fa797acb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 28 Feb 2017 23:34:11 +0100 Subject: Use AI_NUMERICHOST when using getaddrinfo to bind() our client sockets --- louloulibs/network/tcp_client_socket_handler.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/tcp_client_socket_handler.cpp b/louloulibs/network/tcp_client_socket_handler.cpp index 4e6445c..530c3d9 100644 --- a/louloulibs/network/tcp_client_socket_handler.cpp +++ b/louloulibs/network/tcp_client_socket_handler.cpp @@ -35,7 +35,11 @@ void TCPClientSocketHandler::init_socket(const struct addrinfo* rp) // 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); + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST; + hints.ai_family = AF_UNSPEC; + int err = ::getaddrinfo(this->bind_addr.data(), nullptr, &hints, &result); if (err != 0 || !result) log_error("Failed to bind socket to ", this->bind_addr, ": ", gai_strerror(err)); -- cgit v1.2.3 From 6cb7787512a5e02ad2100dbdef734b36d8a8f0d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 7 Mar 2017 17:47:10 +0100 Subject: Small resolver refactor Makes the codecoverage deterministic (it does not depend on the order of v4/v6 resolution) --- louloulibs/network/resolver.cpp | 17 ++++++++--------- louloulibs/network/resolver.hpp | 4 ++++ 2 files changed, 12 insertions(+), 9 deletions(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/resolver.cpp b/louloulibs/network/resolver.cpp index 0655b1b..27a515e 100644 --- a/louloulibs/network/resolver.cpp +++ b/louloulibs/network/resolver.cpp @@ -113,12 +113,14 @@ void Resolver::start_resolving(const std::string& hostname, const std::string& p { Resolver* resolver = static_cast(data); resolver->on_hostname6_resolved(result); + resolver->after_resolved(); }; auto hostname4_resolved = [](dns_ctx*, dns_rr_a4* result, void* data) { Resolver* resolver = static_cast(data); resolver->on_hostname4_resolved(result); + resolver->after_resolved(); }; DNSHandler::watch(); @@ -173,9 +175,6 @@ std::vector Resolver::look_in_etc_hosts(const std::string &hostname void Resolver::on_hostname4_resolved(dns_rr_a4 *result) { - if (dns_active(nullptr) == 0) - DNSHandler::unwatch(); - this->resolved4 = true; const auto status = dns_status(nullptr); @@ -196,16 +195,10 @@ void Resolver::on_hostname4_resolved(dns_rr_a4 *result) if (error != end(dns_error_messages)) this->error_msg = error->second; } - - if (this->resolved6 && this->resolved4) - this->on_resolved(); } void Resolver::on_hostname6_resolved(dns_rr_a6 *result) { - if (dns_active(nullptr) == 0) - DNSHandler::unwatch(); - this->resolved6 = true; char buf[INET6_ADDRSTRLEN]; @@ -219,6 +212,12 @@ void Resolver::on_hostname6_resolved(dns_rr_a6 *result) this->call_getaddrinfo(buf, this->port.data(), AI_NUMERICHOST); } } +} + +void Resolver::after_resolved() +{ + if (dns_active(nullptr) == 0) + DNSHandler::unwatch(); if (this->resolved6 && this->resolved4) this->on_resolved(); diff --git a/louloulibs/network/resolver.hpp b/louloulibs/network/resolver.hpp index 800c7ec..a560819 100644 --- a/louloulibs/network/resolver.hpp +++ b/louloulibs/network/resolver.hpp @@ -89,6 +89,10 @@ private: #ifdef UDNS_FOUND void on_hostname4_resolved(dns_rr_a4 *result); void on_hostname6_resolved(dns_rr_a6 *result); + /** + * Called after one record (4 or 6) has been resolved. + */ + void after_resolved(); void start_timer(); -- cgit v1.2.3 From f0bc6c83a8eb548d0a3edbf7c16a6922bfd24ba5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 8 Mar 2017 19:04:15 +0100 Subject: Pass the shared_ptr by reference, to avoid useless copies --- louloulibs/network/dns_handler.cpp | 2 +- louloulibs/network/dns_handler.hpp | 2 +- louloulibs/network/dns_socket_handler.cpp | 2 +- louloulibs/network/dns_socket_handler.hpp | 2 +- louloulibs/network/socket_handler.hpp | 2 +- louloulibs/network/tcp_client_socket_handler.cpp | 2 +- louloulibs/network/tcp_client_socket_handler.hpp | 2 +- louloulibs/network/tcp_server_socket.hpp | 2 +- louloulibs/network/tcp_socket_handler.cpp | 2 +- louloulibs/network/tcp_socket_handler.hpp | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/dns_handler.cpp b/louloulibs/network/dns_handler.cpp index fbd2763..641c087 100644 --- a/louloulibs/network/dns_handler.cpp +++ b/louloulibs/network/dns_handler.cpp @@ -17,7 +17,7 @@ using namespace std::string_literals; std::unique_ptr DNSHandler::socket_handler{}; -DNSHandler::DNSHandler(std::shared_ptr poller) +DNSHandler::DNSHandler(std::shared_ptr& poller) { dns_init(nullptr, 0); const auto socket = dns_open(nullptr); diff --git a/louloulibs/network/dns_handler.hpp b/louloulibs/network/dns_handler.hpp index 78ffe4d..416f85f 100644 --- a/louloulibs/network/dns_handler.hpp +++ b/louloulibs/network/dns_handler.hpp @@ -14,7 +14,7 @@ class Poller; class DNSHandler { public: - explicit DNSHandler(std::shared_ptr poller); + explicit DNSHandler(std::shared_ptr& poller); ~DNSHandler() = default; DNSHandler(const DNSHandler&) = delete; diff --git a/louloulibs/network/dns_socket_handler.cpp b/louloulibs/network/dns_socket_handler.cpp index ad744a9..84e5625 100644 --- a/louloulibs/network/dns_socket_handler.cpp +++ b/louloulibs/network/dns_socket_handler.cpp @@ -7,7 +7,7 @@ #include -DNSSocketHandler::DNSSocketHandler(std::shared_ptr poller, +DNSSocketHandler::DNSSocketHandler(std::shared_ptr& poller, const socket_t socket): SocketHandler(poller, socket) { diff --git a/louloulibs/network/dns_socket_handler.hpp b/louloulibs/network/dns_socket_handler.hpp index e12f145..fc5f41f 100644 --- a/louloulibs/network/dns_socket_handler.hpp +++ b/louloulibs/network/dns_socket_handler.hpp @@ -12,7 +12,7 @@ class DNSSocketHandler: public SocketHandler { public: - explicit DNSSocketHandler(std::shared_ptr poller, const socket_t socket); + explicit DNSSocketHandler(std::shared_ptr& poller, const socket_t socket); ~DNSSocketHandler(); DNSSocketHandler(const DNSSocketHandler&) = delete; DNSSocketHandler(DNSSocketHandler&&) = delete; diff --git a/louloulibs/network/socket_handler.hpp b/louloulibs/network/socket_handler.hpp index 607a106..6a7220e 100644 --- a/louloulibs/network/socket_handler.hpp +++ b/louloulibs/network/socket_handler.hpp @@ -10,7 +10,7 @@ using socket_t = int; class SocketHandler { public: - explicit SocketHandler(std::shared_ptr poller, const socket_t socket): + explicit SocketHandler(std::shared_ptr& poller, const socket_t socket): poller(poller), socket(socket) {} diff --git a/louloulibs/network/tcp_client_socket_handler.cpp b/louloulibs/network/tcp_client_socket_handler.cpp index 530c3d9..4628703 100644 --- a/louloulibs/network/tcp_client_socket_handler.cpp +++ b/louloulibs/network/tcp_client_socket_handler.cpp @@ -11,7 +11,7 @@ using namespace std::string_literals; -TCPClientSocketHandler::TCPClientSocketHandler(std::shared_ptr poller): +TCPClientSocketHandler::TCPClientSocketHandler(std::shared_ptr& poller): TCPSocketHandler(poller), hostname_resolution_failed(false), connected(false), diff --git a/louloulibs/network/tcp_client_socket_handler.hpp b/louloulibs/network/tcp_client_socket_handler.hpp index 75e1364..74caca9 100644 --- a/louloulibs/network/tcp_client_socket_handler.hpp +++ b/louloulibs/network/tcp_client_socket_handler.hpp @@ -5,7 +5,7 @@ class TCPClientSocketHandler: public TCPSocketHandler { public: - TCPClientSocketHandler(std::shared_ptr poller); + TCPClientSocketHandler(std::shared_ptr& poller); ~TCPClientSocketHandler(); /** * Connect to the remote server, and call on_connected() if this diff --git a/louloulibs/network/tcp_server_socket.hpp b/louloulibs/network/tcp_server_socket.hpp index 7ea49ab..c511962 100644 --- a/louloulibs/network/tcp_server_socket.hpp +++ b/louloulibs/network/tcp_server_socket.hpp @@ -18,7 +18,7 @@ template class TcpSocketServer: public SocketHandler { public: - TcpSocketServer(std::shared_ptr poller, const uint16_t port): + TcpSocketServer(std::shared_ptr& poller, const uint16_t port): SocketHandler(poller, -1) { if ((this->socket = ::socket(AF_INET6, SOCK_STREAM, 0)) == -1) diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index 6aef2b1..7eebae0 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -44,7 +44,7 @@ using namespace std::chrono_literals; namespace ph = std::placeholders; -TCPSocketHandler::TCPSocketHandler(std::shared_ptr poller): +TCPSocketHandler::TCPSocketHandler(std::shared_ptr& poller): SocketHandler(poller, -1), use_tls(false) #ifdef BOTAN_FOUND diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index 600405d..3ee2f47 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -60,7 +60,7 @@ class TCPSocketHandler: public SocketHandler protected: ~TCPSocketHandler(); public: - explicit TCPSocketHandler(std::shared_ptr poller); + explicit TCPSocketHandler(std::shared_ptr& poller); TCPSocketHandler(const TCPSocketHandler&) = delete; TCPSocketHandler(TCPSocketHandler&&) = delete; TCPSocketHandler& operator=(const TCPSocketHandler&) = delete; -- cgit v1.2.3 From 0205076106604372e79130691dcfb7aca15b5055 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 8 Mar 2017 19:04:54 +0100 Subject: Reduce the scope of a variable --- louloulibs/network/resolver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/resolver.cpp b/louloulibs/network/resolver.cpp index 27a515e..3558800 100644 --- a/louloulibs/network/resolver.cpp +++ b/louloulibs/network/resolver.cpp @@ -200,12 +200,12 @@ void Resolver::on_hostname4_resolved(dns_rr_a4 *result) void Resolver::on_hostname6_resolved(dns_rr_a6 *result) { this->resolved6 = true; - char buf[INET6_ADDRSTRLEN]; const auto status = dns_status(nullptr); if (status >= 0 && result) { + char buf[INET6_ADDRSTRLEN]; for (auto i = 0; i < result->dnsa6_nrr; ++i) { inet_ntop(AF_INET6, &result->dnsa6_addr[i], buf, sizeof(buf)); -- cgit v1.2.3 From d44125371fb9f170ea708e96682ad376a45fc293 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Thu, 9 Mar 2017 18:49:48 +0100 Subject: Free the result pointer provided by udns Fix a memory leak that would occur on every DNS request, when using udns --- louloulibs/network/resolver.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'louloulibs/network') diff --git a/louloulibs/network/resolver.cpp b/louloulibs/network/resolver.cpp index 3558800..db7fb32 100644 --- a/louloulibs/network/resolver.cpp +++ b/louloulibs/network/resolver.cpp @@ -114,6 +114,7 @@ void Resolver::start_resolving(const std::string& hostname, const std::string& p Resolver* resolver = static_cast(data); resolver->on_hostname6_resolved(result); resolver->after_resolved(); + std::free(result); }; auto hostname4_resolved = [](dns_ctx*, dns_rr_a4* result, void* data) @@ -121,6 +122,7 @@ void Resolver::start_resolving(const std::string& hostname, const std::string& p Resolver* resolver = static_cast(data); resolver->on_hostname4_resolved(result); resolver->after_resolved(); + std::free(result); }; DNSHandler::watch(); -- cgit v1.2.3 From 0ab40dc1ab4e689921da54080b135e1d22b1c586 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 14 Mar 2017 21:45:23 +0100 Subject: Refactoring louloulibs and cmake Use OBJECT libraries Remove the louloulibs directory Write FOUND variables in the cache --- louloulibs/network/credentials_manager.cpp | 140 --------- louloulibs/network/credentials_manager.hpp | 55 ---- louloulibs/network/dns_handler.cpp | 46 --- louloulibs/network/dns_handler.hpp | 37 --- louloulibs/network/dns_socket_handler.cpp | 43 --- louloulibs/network/dns_socket_handler.hpp | 33 --- louloulibs/network/poller.cpp | 234 --------------- louloulibs/network/poller.hpp | 98 ------- louloulibs/network/resolver.cpp | 281 ------------------ louloulibs/network/resolver.hpp | 122 -------- louloulibs/network/socket_handler.hpp | 42 --- louloulibs/network/tcp_client_socket_handler.cpp | 261 ----------------- louloulibs/network/tcp_client_socket_handler.hpp | 82 ------ louloulibs/network/tcp_server_socket.hpp | 70 ----- louloulibs/network/tcp_socket_handler.cpp | 358 ----------------------- louloulibs/network/tcp_socket_handler.hpp | 251 ---------------- 16 files changed, 2153 deletions(-) delete mode 100644 louloulibs/network/credentials_manager.cpp delete mode 100644 louloulibs/network/credentials_manager.hpp delete mode 100644 louloulibs/network/dns_handler.cpp delete mode 100644 louloulibs/network/dns_handler.hpp delete mode 100644 louloulibs/network/dns_socket_handler.cpp delete mode 100644 louloulibs/network/dns_socket_handler.hpp delete mode 100644 louloulibs/network/poller.cpp delete mode 100644 louloulibs/network/poller.hpp delete mode 100644 louloulibs/network/resolver.cpp delete mode 100644 louloulibs/network/resolver.hpp delete mode 100644 louloulibs/network/socket_handler.hpp delete mode 100644 louloulibs/network/tcp_client_socket_handler.cpp delete mode 100644 louloulibs/network/tcp_client_socket_handler.hpp delete mode 100644 louloulibs/network/tcp_server_socket.hpp delete mode 100644 louloulibs/network/tcp_socket_handler.cpp delete mode 100644 louloulibs/network/tcp_socket_handler.hpp (limited to 'louloulibs/network') diff --git a/louloulibs/network/credentials_manager.cpp b/louloulibs/network/credentials_manager.cpp deleted file mode 100644 index 289307b..0000000 --- a/louloulibs/network/credentials_manager.cpp +++ /dev/null @@ -1,140 +0,0 @@ -#include "louloulibs.h" - -#ifdef BOTAN_FOUND -#include -#include -#include -#include -#include - -#ifdef USE_DATABASE -# include -#endif - -/** - * TODO find a standard way to find that out. - */ -static const std::vector default_cert_files = { - "/etc/ssl/certs/ca-bundle.crt", - "/etc/pki/tls/certs/ca-bundle.crt", - "/etc/ssl/certs/ca-certificates.crt", - "/etc/ca-certificates/extracted/tls-ca-bundle.pem" -}; - -Botan::Certificate_Store_In_Memory BasicCredentialsManager::certificate_store; -bool BasicCredentialsManager::certs_loaded = false; - -BasicCredentialsManager::BasicCredentialsManager(const TCPSocketHandler* const socket_handler): - Botan::Credentials_Manager(), - socket_handler(socket_handler), - trusted_fingerprint{} -{ - BasicCredentialsManager::load_certs(); -} - -void BasicCredentialsManager::set_trusted_fingerprint(const std::string& fingerprint) -{ - this->trusted_fingerprint = fingerprint; -} - -const std::string& BasicCredentialsManager::get_trusted_fingerprint() const -{ - return this->trusted_fingerprint; -} - -void check_tls_certificate(const std::vector& certs, - const std::string& hostname, const std::string& trusted_fingerprint, - std::exception_ptr exc) -{ - - if (!trusted_fingerprint.empty() && !certs.empty() && - trusted_fingerprint == certs[0].fingerprint() && - certs[0].matches_dns_name(hostname)) - // We trust the certificate, based on the trusted fingerprint and - // the fact that the hostname matches - return; - - if (exc) - std::rethrow_exception(exc); -} - -#if BOTAN_VERSION_CODE < BOTAN_VERSION_CODE_FOR(1,11,34) -void BasicCredentialsManager::verify_certificate_chain(const std::string& type, - const std::string& purported_hostname, - const std::vector& certs) -{ - log_debug("Checking remote certificate (", type, ") for hostname ", purported_hostname); - try - { - Botan::Credentials_Manager::verify_certificate_chain(type, purported_hostname, certs); - log_debug("Certificate is valid"); - } - catch (const std::exception& tls_exception) - { - log_warning("TLS certificate check failed: ", tls_exception.what()); - std::exception_ptr exception_ptr{}; - if (this->socket_handler->abort_on_invalid_cert()) - exception_ptr = std::current_exception(); - - check_tls_certificate(certs, purported_hostname, this->trusted_fingerprint, exception_ptr); - } -} -#endif - -bool BasicCredentialsManager::try_to_open_one_ca_bundle(const std::vector& paths) -{ - for (const auto& path: paths) - { - try - { - Botan::DataSource_Stream bundle(path); - log_debug("Using ca bundle: ", path); - while (!bundle.end_of_data() && bundle.check_available(27)) - { - // TODO: remove this work-around for Botan 1.11.29 - // https://github.com/randombit/botan/issues/438#issuecomment-192866796 - // Note that every certificate that fails to be transcoded into latin-1 - // will be ignored. As a result, some TLS connection may be refused - // because the certificate is signed by an issuer that was ignored. - try { - Botan::X509_Certificate cert(bundle); - BasicCredentialsManager::certificate_store.add_certificate(std::move(cert)); - } catch (const Botan::Decoding_Error& error) { - continue; - } - } - // Only use the first file that can successfully be read. - return true; - } - catch (const Botan::Stream_IO_Error& e) - { - log_debug(e.what()); - } - } - return false; -} - -void BasicCredentialsManager::load_certs() -{ - // Only load the certificates the first time - if (BasicCredentialsManager::certs_loaded) - return; - const std::string conf_path = Config::get("ca_file", ""); - std::vector paths; - if (conf_path.empty()) - paths = default_cert_files; - else - paths.push_back(conf_path); - - if (BasicCredentialsManager::try_to_open_one_ca_bundle(paths)) - BasicCredentialsManager::certs_loaded = true; - else - log_warning("The CA could not be loaded, TLS negociation will probably fail."); -} - -std::vector BasicCredentialsManager::trusted_certificate_authorities(const std::string&, const std::string&) -{ - return {&this->certificate_store}; -} - -#endif diff --git a/louloulibs/network/credentials_manager.hpp b/louloulibs/network/credentials_manager.hpp deleted file mode 100644 index 29ee024..0000000 --- a/louloulibs/network/credentials_manager.hpp +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -#include "louloulibs.h" - -#ifdef BOTAN_FOUND - -#include -#include - -class TCPSocketHandler; - -/** - * If the given cert isn’t valid, based on the given hostname - * and fingerprint, then throws the exception if it’s non-empty. - * - * Must be called after the standard (from Botan) way of - * checking the certificate, if we want to also accept certificates based - * on a trusted fingerprint. - */ -void check_tls_certificate(const std::vector& certs, - const std::string& hostname, const std::string& trusted_fingerprint, - std::exception_ptr exc); - -class BasicCredentialsManager: public Botan::Credentials_Manager -{ -public: - BasicCredentialsManager(const TCPSocketHandler* const socket_handler); - - BasicCredentialsManager(BasicCredentialsManager&&) = delete; - BasicCredentialsManager(const BasicCredentialsManager&) = delete; - BasicCredentialsManager& operator=(const BasicCredentialsManager&) = delete; - BasicCredentialsManager& operator=(BasicCredentialsManager&&) = delete; - -#if BOTAN_VERSION_CODE < BOTAN_VERSION_CODE_FOR(1,11,34) - void verify_certificate_chain(const std::string& type, - const std::string& purported_hostname, - const std::vector&) override final; -#endif - std::vector trusted_certificate_authorities(const std::string& type, - const std::string& context) override final; - void set_trusted_fingerprint(const std::string& fingerprint); - const std::string& get_trusted_fingerprint() const; - -private: - const TCPSocketHandler* const socket_handler; - - static bool try_to_open_one_ca_bundle(const std::vector& paths); - static void load_certs(); - static Botan::Certificate_Store_In_Memory certificate_store; - static bool certs_loaded; - std::string trusted_fingerprint; -}; - -#endif //BOTAN_FOUND - diff --git a/louloulibs/network/dns_handler.cpp b/louloulibs/network/dns_handler.cpp deleted file mode 100644 index 641c087..0000000 --- a/louloulibs/network/dns_handler.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include -#ifdef UDNS_FOUND - -#include -#include -#include - -#include - -#include -#include -#include - -class Resolver; - -using namespace std::string_literals; - -std::unique_ptr DNSHandler::socket_handler{}; - -DNSHandler::DNSHandler(std::shared_ptr& poller) -{ - dns_init(nullptr, 0); - const auto socket = dns_open(nullptr); - if (socket == -1) - throw std::runtime_error("Failed to initialize udns socket: "s + strerror(errno)); - - DNSHandler::socket_handler = std::make_unique(poller, socket); -} - -void DNSHandler::destroy() -{ - DNSHandler::socket_handler.reset(nullptr); - dns_close(nullptr); -} - -void DNSHandler::watch() -{ - DNSHandler::socket_handler->watch(); -} - -void DNSHandler::unwatch() -{ - DNSHandler::socket_handler->unwatch(); -} - -#endif /* UDNS_FOUND */ diff --git a/louloulibs/network/dns_handler.hpp b/louloulibs/network/dns_handler.hpp deleted file mode 100644 index 416f85f..0000000 --- a/louloulibs/network/dns_handler.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include -#ifdef UDNS_FOUND - -class Poller; - -#include - -#include -#include -#include - -class DNSHandler -{ -public: - explicit DNSHandler(std::shared_ptr& poller); - ~DNSHandler() = default; - - DNSHandler(const DNSHandler&) = delete; - DNSHandler(DNSHandler&&) = delete; - DNSHandler& operator=(const DNSHandler&) = delete; - DNSHandler& operator=(DNSHandler&&) = delete; - - void destroy(); - - static void watch(); - static void unwatch(); - -private: - /** - * Manager for the socket returned by udns, that we need to watch with the poller - */ - static std::unique_ptr socket_handler; -}; - -#endif /* UDNS_FOUND */ diff --git a/louloulibs/network/dns_socket_handler.cpp b/louloulibs/network/dns_socket_handler.cpp deleted file mode 100644 index 84e5625..0000000 --- a/louloulibs/network/dns_socket_handler.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include -#ifdef UDNS_FOUND - -#include -#include -#include - -#include - -DNSSocketHandler::DNSSocketHandler(std::shared_ptr& poller, - const socket_t socket): - SocketHandler(poller, socket) -{ - poller->add_socket_handler(this); -} - -DNSSocketHandler::~DNSSocketHandler() -{ - this->unwatch(); -} - -void DNSSocketHandler::on_recv() -{ - dns_ioevent(nullptr, 0); -} - -bool DNSSocketHandler::is_connected() const -{ - return true; -} - -void DNSSocketHandler::unwatch() -{ - if (this->poller->is_managing_socket(this->socket)) - this->poller->remove_socket_handler(this->socket); -} - -void DNSSocketHandler::watch() -{ - this->poller->add_socket_handler(this); -} - -#endif /* UDNS_FOUND */ diff --git a/louloulibs/network/dns_socket_handler.hpp b/louloulibs/network/dns_socket_handler.hpp deleted file mode 100644 index fc5f41f..0000000 --- a/louloulibs/network/dns_socket_handler.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include -#ifdef UDNS_FOUND - -#include - -/** - * Manage the UDP socket provided by udns, we do not create, open or close the - * socket ourself: this is done by udns. We only watch it for readability - */ -class DNSSocketHandler: public SocketHandler -{ -public: - explicit DNSSocketHandler(std::shared_ptr& poller, const socket_t socket); - ~DNSSocketHandler(); - DNSSocketHandler(const DNSSocketHandler&) = delete; - DNSSocketHandler(DNSSocketHandler&&) = delete; - DNSSocketHandler& operator=(const DNSSocketHandler&) = delete; - DNSSocketHandler& operator=(DNSSocketHandler&&) = delete; - - void on_recv() override final; - - /** - * Always true, see the comment for connect() - */ - bool is_connected() const override final; - - void watch(); - void unwatch(); -}; - -#endif // UDNS_FOUND diff --git a/louloulibs/network/poller.cpp b/louloulibs/network/poller.cpp deleted file mode 100644 index 9f5bcfb..0000000 --- a/louloulibs/network/poller.cpp +++ /dev/null @@ -1,234 +0,0 @@ -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -Poller::Poller() -{ -#if POLLER == POLL - this->nfds = 0; -#elif POLLER == EPOLL - this->epfd = ::epoll_create1(0); - if (this->epfd == -1) - { - log_error("epoll failed: ", strerror(errno)); - throw std::runtime_error("Could not create epoll instance"); - } -#endif -} - -Poller::~Poller() -{ -#if POLLER == EPOLL - if (this->epfd > 0) - ::close(this->epfd); -#endif -} - -void Poller::add_socket_handler(SocketHandler* socket_handler) -{ - // Don't do anything if the socket is already managed - const auto it = this->socket_handlers.find(socket_handler->get_socket()); - if (it != this->socket_handlers.end()) - return ; - - this->socket_handlers.emplace(socket_handler->get_socket(), socket_handler); - - // We always watch all sockets for receive events -#if POLLER == POLL - this->fds[this->nfds].fd = socket_handler->get_socket(); - this->fds[this->nfds].events = POLLIN; - this->nfds++; -#endif -#if POLLER == EPOLL - struct epoll_event event = {EPOLLIN, {socket_handler}}; - const int res = ::epoll_ctl(this->epfd, EPOLL_CTL_ADD, socket_handler->get_socket(), &event); - if (res == -1) - { - log_error("epoll_ctl failed: ", strerror(errno)); - throw std::runtime_error("Could not add socket to epoll"); - } -#endif -} - -void Poller::remove_socket_handler(const socket_t socket) -{ - const auto it = this->socket_handlers.find(socket); - if (it == this->socket_handlers.end()) - throw std::runtime_error("Trying to remove a SocketHandler that is not managed"); - this->socket_handlers.erase(it); - -#if POLLER == POLL - for (size_t i = 0; i < this->nfds; i++) - { - if (this->fds[i].fd == socket) - { - // Move all subsequent pollfd by one on the left, erasing the - // value of the one we remove - for (size_t j = i; j < this->nfds - 1; ++j) - { - this->fds[j].fd = this->fds[j+1].fd; - this->fds[j].events= this->fds[j+1].events; - } - this->nfds--; - } - } -#elif POLLER == EPOLL - const int res = ::epoll_ctl(this->epfd, EPOLL_CTL_DEL, socket, nullptr); - if (res == -1) - { - log_error("epoll_ctl failed: ", strerror(errno)); - throw std::runtime_error("Could not remove socket from epoll"); - } -#endif -} - -void Poller::watch_send_events(SocketHandler* socket_handler) -{ -#if POLLER == POLL - for (size_t i = 0; i < this->nfds; ++i) - { - if (this->fds[i].fd == socket_handler->get_socket()) - { - this->fds[i].events = POLLIN|POLLOUT; - return; - } - } - throw std::runtime_error("Cannot watch a non-registered socket for send events"); -#elif POLLER == EPOLL - struct epoll_event event = {EPOLLIN|EPOLLOUT, {socket_handler}}; - const int res = ::epoll_ctl(this->epfd, EPOLL_CTL_MOD, socket_handler->get_socket(), &event); - if (res == -1) - { - log_error("epoll_ctl failed: ", strerror(errno)); - throw std::runtime_error("Could not modify socket flags in epoll"); - } -#endif -} - -void Poller::stop_watching_send_events(SocketHandler* socket_handler) -{ -#if POLLER == POLL - for (size_t i = 0; i <= this->nfds; ++i) - { - if (this->fds[i].fd == socket_handler->get_socket()) - { - this->fds[i].events = POLLIN; - return; - } - } - throw std::runtime_error("Cannot watch a non-registered socket for send events"); -#elif POLLER == EPOLL - struct epoll_event event = {EPOLLIN, {socket_handler}}; - const int res = ::epoll_ctl(this->epfd, EPOLL_CTL_MOD, socket_handler->get_socket(), &event); - if (res == -1) - { - log_error("epoll_ctl failed: ", strerror(errno)); - throw std::runtime_error("Could not modify socket flags in epoll"); - } -#endif -} - -int Poller::poll(const std::chrono::milliseconds& timeout) -{ - if (this->socket_handlers.empty() && timeout == utils::no_timeout) - return -1; -#if POLLER == POLL - // Convert our nice timeout into this ugly struct - struct timespec timeout_ts; - struct timespec* timeout_tsp; - if (timeout > 0s) - { - auto seconds = std::chrono::duration_cast(timeout); - timeout_ts.tv_sec = seconds.count(); - timeout_ts.tv_nsec = std::chrono::duration_cast(timeout - seconds).count(); - timeout_tsp = &timeout_ts; - } - else - timeout_tsp = nullptr; - - // Unblock all signals, only during the ppoll call - sigset_t empty_signal_set; - sigemptyset(&empty_signal_set); - int nb_events = ::ppoll(this->fds, this->nfds, timeout_tsp, - &empty_signal_set); - if (nb_events < 0) - { - if (errno == EINTR) - return true; - log_error("poll failed: ", strerror(errno)); - throw std::runtime_error("Poll failed"); - } - // We cannot possibly have more ready events than the number of fds we are - // watching - assert(static_cast(nb_events) <= this->nfds); - for (size_t i = 0; i < this->nfds && nb_events != 0; ++i) - { - auto socket_handler = this->socket_handlers.at(this->fds[i].fd); - if (this->fds[i].revents == 0) - continue; - else if (this->fds[i].revents & POLLIN && socket_handler->is_connected()) - { - socket_handler->on_recv(); - nb_events--; - } - else if (this->fds[i].revents & POLLOUT && socket_handler->is_connected()) - { - socket_handler->on_send(); - nb_events--; - } - else if (this->fds[i].revents & POLLOUT || - this->fds[i].revents & POLLIN) - { - socket_handler->connect(); - nb_events--; - } - } - return 1; -#elif POLLER == EPOLL - static const size_t max_events = 12; - struct epoll_event revents[max_events]; - // Unblock all signals, only during the epoll_pwait call - sigset_t empty_signal_set; - sigemptyset(&empty_signal_set); - const int nb_events = ::epoll_pwait(this->epfd, revents, max_events, timeout.count(), - &empty_signal_set); - if (nb_events == -1) - { - if (errno == EINTR) - return 0; - log_error("epoll wait: ", strerror(errno)); - throw std::runtime_error("Epoll_wait failed"); - } - for (int i = 0; i < nb_events; ++i) - { - auto socket_handler = static_cast(revents[i].data.ptr); - if (revents[i].events & EPOLLIN && socket_handler->is_connected()) - socket_handler->on_recv(); - else if (revents[i].events & EPOLLOUT && socket_handler->is_connected()) - socket_handler->on_send(); - else if (revents[i].events & EPOLLOUT) - socket_handler->connect(); - } - return nb_events; -#endif -} - -size_t Poller::size() const -{ - return this->socket_handlers.size(); -} - -bool Poller::is_managing_socket(const socket_t socket) const -{ - return (this->socket_handlers.find(socket) != this->socket_handlers.end()); -} diff --git a/louloulibs/network/poller.hpp b/louloulibs/network/poller.hpp deleted file mode 100644 index e39e438..0000000 --- a/louloulibs/network/poller.hpp +++ /dev/null @@ -1,98 +0,0 @@ -#pragma once - - -#include - -#include -#include -#include - -#define POLL 1 -#define EPOLL 2 -#define KQUEUE 3 -#include -#ifndef POLLER - #define POLLER POLL -#endif - -#if POLLER == POLL - #include - #define MAX_POLL_FD_NUMBER 4096 -#elif POLLER == EPOLL - #include -#else - #error Invalid POLLER value -#endif - -/** - * We pass some SocketHandlers to this Poller, which uses - * poll/epoll/kqueue/select etc to wait for events on these SocketHandlers, - * and call the callbacks when event occurs. - * - * TODO: support these pollers: - * - kqueue(2) - */ - -class Poller -{ -public: - explicit Poller(); - ~Poller(); - Poller(const Poller&) = delete; - Poller(Poller&&) = delete; - Poller& operator=(const Poller&) = delete; - Poller& operator=(Poller&&) = delete; - /** - * Add a SocketHandler to be monitored by this Poller. All receive events - * are always automatically watched. - */ - void add_socket_handler(SocketHandler* socket_handler); - /** - * Remove (and stop managing) a SocketHandler, designated by the given socket_t. - */ - void remove_socket_handler(const socket_t socket); - /** - * Signal the poller that he needs to watch for send events for the given - * SocketHandler. - */ - void watch_send_events(SocketHandler* socket_handler); - /** - * Signal the poller that he needs to stop watching for send events for - * this SocketHandler. - */ - void stop_watching_send_events(SocketHandler* socket_handler); - /** - * Wait for all watched events, and call the SocketHandlers' callbacks - * when one is ready. Returns if nothing happened before the provided - * timeout. If the timeout is 0, it waits forever. If there is no - * watched event, returns -1 immediately, ignoring the timeout value. - * Otherwise, returns the number of event handled. If 0 is returned this - * means that we were interrupted by a signal, or the timeout occured. - */ - int poll(const std::chrono::milliseconds& timeout); - /** - * Returns the number of SocketHandlers managed by the poller. - */ - size_t size() const; - /** - * Whether the given socket is managed by the poller - */ - bool is_managing_socket(const socket_t socket) const; - -private: - /** - * A "list" of all the SocketHandlers that we manage, indexed by socket, - * because that's what is returned by select/poll/etc when an event - * occures. - */ - std::unordered_map socket_handlers; - -#if POLLER == POLL - struct pollfd fds[MAX_POLL_FD_NUMBER]; - nfds_t nfds; -#elif POLLER == EPOLL - int epfd; -#endif -}; - - diff --git a/louloulibs/network/resolver.cpp b/louloulibs/network/resolver.cpp deleted file mode 100644 index db7fb32..0000000 --- a/louloulibs/network/resolver.cpp +++ /dev/null @@ -1,281 +0,0 @@ -#include -#include -#include -#include -#include -#include -#ifdef UDNS_FOUND -# include -#endif - -#include -#include -#include -#include -#include - -using namespace std::string_literals; - -#ifdef UDNS_FOUND -static std::map dns_error_messages { - {DNS_E_TEMPFAIL, "Timeout while contacting DNS servers"}, - {DNS_E_PROTOCOL, "Misformatted DNS reply"}, - {DNS_E_NXDOMAIN, "Domain name not found"}, - {DNS_E_NOMEM, "Out of memory"}, - {DNS_E_BADQUERY, "Misformatted domain name"} -}; -#endif - -Resolver::Resolver(): -#ifdef UDNS_FOUND - resolved4(false), - resolved6(false), - resolving(false), - port{}, -#endif - resolved(false), - error_msg{} -{ -} - -void Resolver::resolve(const std::string& hostname, const std::string& port, - SuccessCallbackType success_cb, ErrorCallbackType error_cb) -{ - this->error_cb = error_cb; - this->success_cb = success_cb; -#ifdef UDNS_FOUND - this->port = port; -#endif - - this->start_resolving(hostname, port); -} - -int Resolver::call_getaddrinfo(const char *name, const char* port, int flags) -{ - struct addrinfo hints; - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_flags = flags; - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = 0; - - struct addrinfo* addr_res = nullptr; - const int res = ::getaddrinfo(name, port, - &hints, &addr_res); - - if (res == 0 && addr_res) - { - if (!this->addr) - this->addr.reset(addr_res); - else - { // Append this result at the end of the linked list - struct addrinfo *rp = this->addr.get(); - while (rp->ai_next) - rp = rp->ai_next; - rp->ai_next = addr_res; - } - } - - return res; -} - -#ifdef UDNS_FOUND -void Resolver::start_resolving(const std::string& hostname, const std::string& port) -{ - this->resolving = true; - this->resolved = false; - this->resolved4 = false; - this->resolved6 = false; - - this->error_msg.clear(); - this->addr.reset(nullptr); - - // We first try to use it as an IP address directly. We tell getaddrinfo - // to NOT use any DNS resolution. - if (this->call_getaddrinfo(hostname.data(), port.data(), AI_NUMERICHOST) == 0) - { - this->on_resolved(); - return; - } - - // Then we look into /etc/hosts to translate the given hostname - const auto hosts = this->look_in_etc_hosts(hostname); - if (!hosts.empty()) - { - for (const auto &host: hosts) - this->call_getaddrinfo(host.data(), port.data(), AI_NUMERICHOST); - this->on_resolved(); - return; - } - - // And finally, we try a DNS resolution - auto hostname6_resolved = [](dns_ctx*, dns_rr_a6* result, void* data) - { - Resolver* resolver = static_cast(data); - resolver->on_hostname6_resolved(result); - resolver->after_resolved(); - std::free(result); - }; - - auto hostname4_resolved = [](dns_ctx*, dns_rr_a4* result, void* data) - { - Resolver* resolver = static_cast(data); - resolver->on_hostname4_resolved(result); - resolver->after_resolved(); - std::free(result); - }; - - DNSHandler::watch(); - auto res = dns_submit_a4(nullptr, hostname.data(), 0, hostname4_resolved, this); - if (!res) - this->on_hostname4_resolved(nullptr); - res = dns_submit_a6(nullptr, hostname.data(), 0, hostname6_resolved, this); - if (!res) - this->on_hostname6_resolved(nullptr); - - this->start_timer(); -} - -void Resolver::start_timer() -{ - const auto timeout = dns_timeouts(nullptr, -1, 0); - if (timeout < 0) - return; - TimedEvent event(std::chrono::steady_clock::now() + std::chrono::seconds(timeout), [this]() { this->start_timer(); }, "DNS"); - TimedEventsManager::instance().add_event(std::move(event)); -} - -std::vector Resolver::look_in_etc_hosts(const std::string &hostname) -{ - std::ifstream hosts("/etc/hosts"); - std::string line; - - std::vector results; - while (std::getline(hosts, line)) - { - if (line.empty()) - continue; - - std::string ip; - std::istringstream line_stream(line); - line_stream >> ip; - if (ip.empty() || ip[0] == '#') - continue; - - std::string host; - while (line_stream >> host && !host.empty() && host[0] != '#') - { - if (hostname == host) - { - results.push_back(ip); - break; - } - } - } - return results; -} - -void Resolver::on_hostname4_resolved(dns_rr_a4 *result) -{ - this->resolved4 = true; - - const auto status = dns_status(nullptr); - - if (status >= 0 && result) - { - char buf[INET6_ADDRSTRLEN]; - - for (auto i = 0; i < result->dnsa4_nrr; ++i) - { - inet_ntop(AF_INET, &result->dnsa4_addr[i], buf, sizeof(buf)); - this->call_getaddrinfo(buf, this->port.data(), AI_NUMERICHOST); - } - } - else - { - const auto error = dns_error_messages.find(status); - if (error != end(dns_error_messages)) - this->error_msg = error->second; - } -} - -void Resolver::on_hostname6_resolved(dns_rr_a6 *result) -{ - this->resolved6 = true; - - const auto status = dns_status(nullptr); - - if (status >= 0 && result) - { - char buf[INET6_ADDRSTRLEN]; - for (auto i = 0; i < result->dnsa6_nrr; ++i) - { - inet_ntop(AF_INET6, &result->dnsa6_addr[i], buf, sizeof(buf)); - this->call_getaddrinfo(buf, this->port.data(), AI_NUMERICHOST); - } - } -} - -void Resolver::after_resolved() -{ - if (dns_active(nullptr) == 0) - DNSHandler::unwatch(); - - if (this->resolved6 && this->resolved4) - this->on_resolved(); -} - -void Resolver::on_resolved() -{ - this->resolved = true; - this->resolving = false; - if (!this->addr) - { - if (this->error_cb) - this->error_cb(this->error_msg.data()); - } - else - { - if (this->success_cb) - this->success_cb(this->addr.get()); - } -} - -#else // ifdef UDNS_FOUND - -void Resolver::start_resolving(const std::string& hostname, const std::string& port) -{ - // If the resolution fails, the addr will be unset - this->addr.reset(nullptr); - - const auto res = this->call_getaddrinfo(hostname.data(), port.data(), 0); - - this->resolved = true; - - if (res != 0) - { - this->error_msg = gai_strerror(res); - if (this->error_cb) - this->error_cb(this->error_msg.data()); - } - else - { - if (this->success_cb) - this->success_cb(this->addr.get()); - } -} -#endif // ifdef UDNS_FOUND - -std::string addr_to_string(const struct addrinfo* rp) -{ - char buf[INET6_ADDRSTRLEN]; - if (rp->ai_family == AF_INET) - return ::inet_ntop(rp->ai_family, - &reinterpret_cast(rp->ai_addr)->sin_addr, - buf, sizeof(buf)); - else if (rp->ai_family == AF_INET6) - return ::inet_ntop(rp->ai_family, - &reinterpret_cast(rp->ai_addr)->sin6_addr, - buf, sizeof(buf)); - return {}; -} diff --git a/louloulibs/network/resolver.hpp b/louloulibs/network/resolver.hpp deleted file mode 100644 index a560819..0000000 --- a/louloulibs/network/resolver.hpp +++ /dev/null @@ -1,122 +0,0 @@ -#pragma once - -#include "louloulibs.h" - -#include -#include -#include -#include - -#include -#include -#include -#ifdef UDNS_FOUND -# include -#endif - -class AddrinfoDeleter -{ - public: - void operator()(struct addrinfo* addr) - { - freeaddrinfo(addr); - } -}; - - -class Resolver -{ -public: - - using ErrorCallbackType = std::function; - using SuccessCallbackType = std::function; - - Resolver(); - ~Resolver() = default; - Resolver(const Resolver&) = delete; - Resolver(Resolver&&) = delete; - Resolver& operator=(const Resolver&) = delete; - Resolver& operator=(Resolver&&) = delete; - - bool is_resolving() const - { -#ifdef UDNS_FOUND - return this->resolving; -#else - return false; -#endif - } - - bool is_resolved() const - { - return this->resolved; - } - - const auto& get_result() const - { - return this->addr; - } - std::string get_error_message() const - { - return this->error_msg; - } - - void clear() - { -#ifdef UDNS_FOUND - this->resolved6 = false; - this->resolved4 = false; - this->resolving = false; - this->port.clear(); -#endif - this->resolved = false; - this->addr.reset(); - this->error_msg.clear(); - } - - void resolve(const std::string& hostname, const std::string& port, - SuccessCallbackType success_cb, ErrorCallbackType error_cb); - -private: - void start_resolving(const std::string& hostname, const std::string& port); - std::vector look_in_etc_hosts(const std::string& hostname); - /** - * Call getaddrinfo() on the given hostname or IP, and append the result - * to our internal addrinfo list. Return getaddrinfo()’s return value. - */ - int call_getaddrinfo(const char* name, const char* port, int flags); - -#ifdef UDNS_FOUND - void on_hostname4_resolved(dns_rr_a4 *result); - void on_hostname6_resolved(dns_rr_a6 *result); - /** - * Called after one record (4 or 6) has been resolved. - */ - void after_resolved(); - - void start_timer(); - - void on_resolved(); - - bool resolved4; - bool resolved6; - - bool resolving; - - std::string port; - -#endif - /** - * Tells if we finished the resolution process. It doesn't indicate if it - * was successful (it is true even if the result is an error). - */ - bool resolved; - std::string error_msg; - - std::unique_ptr addr; - - ErrorCallbackType error_cb; - SuccessCallbackType success_cb; -}; - -std::string addr_to_string(const struct addrinfo* rp); diff --git a/louloulibs/network/socket_handler.hpp b/louloulibs/network/socket_handler.hpp deleted file mode 100644 index 6a7220e..0000000 --- a/louloulibs/network/socket_handler.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include -#include - -class Poller; - -using socket_t = int; - -class SocketHandler -{ -public: - explicit SocketHandler(std::shared_ptr& poller, const socket_t socket): - poller(poller), - socket(socket) - {} - virtual ~SocketHandler() = default; - SocketHandler(const SocketHandler&) = delete; - SocketHandler(SocketHandler&&) = delete; - SocketHandler& operator=(const SocketHandler&) = delete; - SocketHandler& operator=(SocketHandler&&) = delete; - - virtual void on_recv() {} - virtual void on_send() {} - virtual void connect() {} - virtual bool is_connected() const = 0; - - socket_t get_socket() const - { return this->socket; } - -protected: - /** - * A pointer to the poller that manages us, because we need to communicate - * with it. - */ - std::shared_ptr poller; - /** - * The handled socket. - */ - socket_t socket; -}; - diff --git a/louloulibs/network/tcp_client_socket_handler.cpp b/louloulibs/network/tcp_client_socket_handler.cpp deleted file mode 100644 index 4628703..0000000 --- a/louloulibs/network/tcp_client_socket_handler.cpp +++ /dev/null @@ -1,261 +0,0 @@ -#include -#include -#include -#include - -#include - -#include -#include -#include - -using namespace std::string_literals; - -TCPClientSocketHandler::TCPClientSocketHandler(std::shared_ptr& 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 + std::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; - struct addrinfo hints; - memset(&hints, 0, sizeof(hints)); - hints.ai_flags = AI_NUMERICHOST; - hints.ai_family = AF_UNSPEC; - int err = ::getaddrinfo(this->bind_addr.data(), nullptr, &hints, &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; - for (rp = result; rp; rp = rp->ai_next) - { - if ((::bind(this->socket, - reinterpret_cast(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 + std::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 using udns), 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 DNS resolver resolved the hostname and the available addresses - // where saved in the 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(); - - // Get our local TCP port and store it - this->local_port = static_cast(-1); - if (rp->ai_family == AF_INET6) - { - struct sockaddr_in6 a; - socklen_t l = sizeof(a); - if (::getsockname(this->socket, (struct sockaddr*)&a, &l) != -1) - this->local_port = ntohs(a.sin6_port); - } - else if (rp->ai_family == AF_INET) - { - struct sockaddr_in a; - socklen_t l = sizeof(a); - if (::getsockname(this->socket, (struct sockaddr*)&a, &l) != -1) - this->local_port = ntohs(a.sin_port); - } - - log_debug("Local port: ", this->local_port, ", and remote port: ", this->port); - - 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(&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:", std::strerror(errno)); - } - log_error("All connection attempts failed."); - this->close(); - this->on_connection_failed(std::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; -} - -bool TCPClientSocketHandler::match_port_pairt(const uint16_t local, const uint16_t remote) const -{ - const uint16_t remote_port = static_cast(std::stoi(this->port)); - return this->is_connected() && local == this->local_port && remote == remote_port; -} diff --git a/louloulibs/network/tcp_client_socket_handler.hpp b/louloulibs/network/tcp_client_socket_handler.hpp deleted file mode 100644 index 74caca9..0000000 --- a/louloulibs/network/tcp_client_socket_handler.hpp +++ /dev/null @@ -1,82 +0,0 @@ -#pragma once - -#include - -class TCPClientSocketHandler: public TCPSocketHandler -{ - public: - TCPClientSocketHandler(std::shared_ptr& 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 override; - - std::string get_port() const; - - void close() override final; - std::chrono::system_clock::time_point connection_date; - - /** - * Whether or not this connection is using the two given TCP ports. - */ - bool match_port_pairt(const uint16_t local, const uint16_t remote) const; - - 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; - - uint16_t local_port{}; - - bool connected; - bool connecting; -}; diff --git a/louloulibs/network/tcp_server_socket.hpp b/louloulibs/network/tcp_server_socket.hpp deleted file mode 100644 index c511962..0000000 --- a/louloulibs/network/tcp_server_socket.hpp +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once - -#include -#include -#include - -#include - -#include -#include -#include -#include - -#include -#include - -template -class TcpSocketServer: public SocketHandler -{ - public: - TcpSocketServer(std::shared_ptr& poller, const uint16_t port): - SocketHandler(poller, -1) - { - if ((this->socket = ::socket(AF_INET6, SOCK_STREAM, 0)) == -1) - throw std::runtime_error(std::string{"Could not create socket: "} + std::strerror(errno)); - - int opt = 1; - if (::setsockopt(this->socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) - throw std::runtime_error(std::string{"Failed to set socket option: "} + std::strerror(errno)); - - struct sockaddr_in6 addr{}; - addr.sin6_family = AF_INET6; - addr.sin6_port = htons(port); - addr.sin6_addr = IN6ADDR_ANY_INIT; - if ((::bind(this->socket, (const struct sockaddr*)&addr, sizeof(addr))) == -1) - { // If we can’t listen on this port, we just give up, but this is not fatal. - log_warning("Failed to bind on port ", std::to_string(port), ": ", std::strerror(errno)); - return; - } - - if ((::listen(this->socket, 10)) == -1) - throw std::runtime_error("listen() failed"); - - this->accept(); - } - ~TcpSocketServer() = default; - - void on_recv() override - { - // Accept a RemoteSocketType - int socket = ::accept(this->socket, nullptr, nullptr); - - auto client = std::make_unique(poller, socket, *this); - this->poller->add_socket_handler(client.get()); - this->sockets.push_back(std::move(client)); - } - - protected: - std::vector> sockets; - - private: - void accept() - { - this->poller->add_socket_handler(this); - } - bool is_connected() const override - { - return true; - } -}; diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp deleted file mode 100644 index 7eebae0..0000000 --- a/louloulibs/network/tcp_socket_handler.cpp +++ /dev/null @@ -1,358 +0,0 @@ -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -#ifdef BOTAN_FOUND -# include -# include - -namespace -{ - Botan::AutoSeeded_RNG& get_rng() - { - static Botan::AutoSeeded_RNG rng{}; - return rng; - } - BiboumiTLSPolicy& get_policy() - { - static BiboumiTLSPolicy policy{}; - return policy; - } - Botan::TLS::Session_Manager_In_Memory& get_session_manager() - { - static Botan::TLS::Session_Manager_In_Memory session_manager{get_rng()}; - return session_manager; - } -} -#endif - -#ifndef UIO_FASTIOV -# define UIO_FASTIOV 8 -#endif - -using namespace std::string_literals; -using namespace std::chrono_literals; - -namespace ph = std::placeholders; - -TCPSocketHandler::TCPSocketHandler(std::shared_ptr& poller): - SocketHandler(poller, -1), - use_tls(false) -#ifdef BOTAN_FOUND - ,credential_manager(this) -#endif -{} - -TCPSocketHandler::~TCPSocketHandler() -{ - if (this->poller->is_managing_socket(this->get_socket())) - this->poller->remove_socket_handler(this->get_socket()); - if (this->socket != -1) - { - ::close(this->socket); - this->socket = -1; - } -} - -void TCPSocketHandler::on_recv() -{ -#ifdef BOTAN_FOUND - if (this->use_tls) - this->tls_recv(); - else -#endif - this->plain_recv(); -} - -void TCPSocketHandler::plain_recv() -{ - static constexpr size_t buf_size = 4096; - char buf[buf_size]; - void* recv_buf = this->get_receive_buffer(buf_size); - - if (recv_buf == nullptr) - recv_buf = buf; - - const ssize_t size = this->do_recv(recv_buf, buf_size); - - if (size > 0) - { - if (buf == recv_buf) - { - // data needs to be placed in the in_buf string, because no buffer - // was provided to receive that data directly. The in_buf buffer - // will be handled in parse_in_buffer() - this->in_buf += std::string(buf, size); - } - this->parse_in_buffer(size); - } -} - -ssize_t TCPSocketHandler::do_recv(void* recv_buf, const size_t buf_size) -{ - ssize_t size = ::recv(this->socket, recv_buf, buf_size, 0); - if (0 == size) - { - this->on_connection_close(""); - this->close(); - } - else if (-1 == size) - { - 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->is_connecting(); - this->close(); - if (were_connecting) - this->on_connection_failed(strerror(errno)); - else - this->on_connection_close(strerror(errno)); - } - return size; -} - -void TCPSocketHandler::on_send() -{ - struct iovec msg_iov[UIO_FASTIOV] = {}; - struct msghdr msg{nullptr, 0, - msg_iov, - 0, nullptr, 0, 0}; - for (const std::string& s: this->out_buf) - { - // unconsting the content of s is ok, sendmsg will never modify it - msg_iov[msg.msg_iovlen].iov_base = const_cast(s.data()); - msg_iov[msg.msg_iovlen].iov_len = s.size(); - msg.msg_iovlen++; - if (msg.msg_iovlen == UIO_FASTIOV) - break; - } - ssize_t res = ::sendmsg(this->socket, &msg, MSG_NOSIGNAL); - if (res < 0) - { - log_error("sendmsg failed: ", strerror(errno)); - this->on_connection_close(strerror(errno)); - this->close(); - } - else - { - // remove all the strings that were successfully sent. - auto it = this->out_buf.begin(); - while (it != this->out_buf.end()) - { - if (static_cast(res) >= it->size()) - { - res -= it->size(); - ++it; - } - else - { - // If one string has partially been sent, we use substr to - // crop it - if (res > 0) - *it = it->substr(res, std::string::npos); - break; - } - } - this->out_buf.erase(this->out_buf.begin(), it); - if (this->out_buf.empty()) - this->poller->stop_watching_send_events(this); - } -} - -void TCPSocketHandler::close() -{ - 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->in_buf.clear(); - this->out_buf.clear(); -} - -void TCPSocketHandler::send_data(std::string&& data) -{ -#ifdef BOTAN_FOUND - if (this->use_tls) - try { - this->tls_send(std::move(data)); - } catch (const Botan::TLS::TLS_Exception& e) { - this->on_connection_close("TLS error: "s + e.what()); - this->close(); - return ; - } - else -#endif - this->raw_send(std::move(data)); -} - -void TCPSocketHandler::raw_send(std::string&& data) -{ - if (data.empty()) - return ; - this->out_buf.emplace_back(std::move(data)); - if (this->is_connected()) - this->poller->watch_send_events(this); -} - -void TCPSocketHandler::send_pending_data() -{ - if (this->is_connected() && !this->out_buf.empty()) - this->poller->watch_send_events(this); -} - -bool TCPSocketHandler::is_using_tls() const -{ - return this->use_tls; -} - -void* TCPSocketHandler::get_receive_buffer(const size_t) const -{ - return nullptr; -} - -void TCPSocketHandler::consume_in_buffer(const std::size_t size) -{ - this->in_buf = this->in_buf.substr(size, std::string::npos); -} - -#ifdef BOTAN_FOUND -void TCPSocketHandler::start_tls(const std::string& address, const std::string& port) -{ - Botan::TLS::Server_Information server_info(address, "irc", std::stoul(port)); - this->tls = std::make_unique( -# if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,32) - *this, -# else - [this](const Botan::byte* data, size_t size) { this->tls_emit_data(data, size); }, - [this](const Botan::byte* data, size_t size) { this->tls_record_received(0, data, size); }, - [this](Botan::TLS::Alert alert, const Botan::byte*, size_t) { this->tls_alert(alert); }, - [this](const Botan::TLS::Session& session) { return this->tls_session_established(session); }, -# endif - get_session_manager(), this->credential_manager, get_policy(), - get_rng(), server_info, Botan::TLS::Protocol_Version::latest_tls_version()); -} - -void TCPSocketHandler::tls_recv() -{ - static constexpr size_t buf_size = 4096; - Botan::byte recv_buf[buf_size]; - - const ssize_t size = this->do_recv(recv_buf, buf_size); - if (size > 0) - { - const bool was_active = this->tls->is_active(); - try { - this->tls->received_data(recv_buf, static_cast(size)); - } catch (const Botan::TLS::TLS_Exception& e) { - // May happen if the server sends malformed TLS data (buggy server, - // or more probably we are just connected to a server that sends - // plain-text) - this->on_connection_close("TLS error: "s + e.what()); - this->close(); - return ; - } - if (!was_active && this->tls->is_active()) - this->on_tls_activated(); - } -} - -void TCPSocketHandler::tls_send(std::string&& data) -{ - // We may not be connected yet, or the tls session has - // not yet been negociated - if (this->tls && this->tls->is_active()) - { - const bool was_active = this->tls->is_active(); - if (!this->pre_buf.empty()) - { - this->tls->send(this->pre_buf.data(), this->pre_buf.size()); - this->pre_buf.clear(); - } - if (!data.empty()) - this->tls->send(reinterpret_cast(data.data()), - data.size()); - if (!was_active && this->tls->is_active()) - this->on_tls_activated(); - } - else - this->pre_buf.insert(this->pre_buf.end(), - std::make_move_iterator(data.begin()), - std::make_move_iterator(data.end())); -} - -void TCPSocketHandler::tls_record_received(uint64_t, const Botan::byte *data, size_t size) -{ - this->in_buf += std::string(reinterpret_cast(data), - size); - if (!this->in_buf.empty()) - this->parse_in_buffer(size); -} - -void TCPSocketHandler::tls_emit_data(const Botan::byte *data, size_t size) -{ - this->raw_send(std::string(reinterpret_cast(data), size)); -} - -void TCPSocketHandler::tls_alert(Botan::TLS::Alert alert) -{ - log_debug("tls_alert: ", alert.type_string()); -} - -bool TCPSocketHandler::tls_session_established(const Botan::TLS::Session& session) -{ - log_debug("Handshake with ", session.server_info().hostname(), " complete.", - " Version: ", session.version().to_string(), - " using ", session.ciphersuite().to_string()); - if (!session.session_id().empty()) - log_debug("Session ID ", Botan::hex_encode(session.session_id())); - if (!session.session_ticket().empty()) - log_debug("Session ticket ", Botan::hex_encode(session.session_ticket())); - return true; -} - -#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,34) -void TCPSocketHandler::tls_verify_cert_chain(const std::vector& cert_chain, - const std::vector>& ocsp_responses, - const std::vector& trusted_roots, - Botan::Usage_Type usage, const std::string& hostname, - const Botan::TLS::Policy& policy) -{ - log_debug("Checking remote certificate for hostname ", hostname); - try - { - Botan::TLS::Callbacks::tls_verify_cert_chain(cert_chain, ocsp_responses, trusted_roots, usage, hostname, policy); - log_debug("Certificate is valid"); - } - catch (const std::exception& tls_exception) - { - log_warning("TLS certificate check failed: ", tls_exception.what()); - std::exception_ptr exception_ptr{}; - if (this->abort_on_invalid_cert()) - exception_ptr = std::current_exception(); - - check_tls_certificate(cert_chain, hostname, this->credential_manager.get_trusted_fingerprint(), exception_ptr); - } -} -#endif - -void TCPSocketHandler::on_tls_activated() -{ - this->send_data({}); -} - -#endif // BOTAN_FOUND diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp deleted file mode 100644 index 3ee2f47..0000000 --- a/louloulibs/network/tcp_socket_handler.hpp +++ /dev/null @@ -1,251 +0,0 @@ -#pragma once - -#include "louloulibs.h" - -#include -#include - -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#ifdef BOTAN_FOUND - -# include -# include -# include - -class BiboumiTLSPolicy: public Botan::TLS::Policy -{ -public: -# if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,33) - bool use_ecc_point_compression() const override - { - return true; - } - bool require_cert_revocation_info() const override - { - return false; - } -# endif -}; - -# if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,32) -# define BOTAN_TLS_CALLBACKS_OVERRIDE override final -# else -# define BOTAN_TLS_CALLBACKS_OVERRIDE -# endif -#endif - -/** - * 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 -#ifdef BOTAN_FOUND -# if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,32) - ,public Botan::TLS::Callbacks -# endif -#endif -{ -protected: - ~TCPSocketHandler(); -public: - explicit TCPSocketHandler(std::shared_ptr& poller); - TCPSocketHandler(const TCPSocketHandler&) = delete; - TCPSocketHandler(TCPSocketHandler&&) = delete; - TCPSocketHandler& operator=(const TCPSocketHandler&) = delete; - TCPSocketHandler& operator=(TCPSocketHandler&&) = delete; - - /** - * 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() - */ - void on_recv() override final; - /** - * Write as much data from out_buf as possible, in the socket. - */ - void on_send() override final; - /** - * Add the given data to out_buf and tell our poller that we want to be - * notified when a send event is ready. - * - * This can be overriden if we want to modify the data before sending - * it. For example if we want to encrypt it. - */ - void send_data(std::string&& data); - /** - * Watch the socket for send events, if our out buffer is not empty. - */ - void send_pending_data(); - /** - * Close the connection, remove us from the poller - */ - 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 - * get_receive_buffer() returned. If some data is used from in_buf, it - * 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 - /** - * Tell whether the credential manager should cancel the connection when the - * certificate is invalid. - */ - virtual bool abort_on_invalid_cert() const - { - return true; - } -#endif - bool is_using_tls() const; - -private: - /** - * 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). - * - * Returns the value returned by ::recv(), so the buffer should not be - * used if it’s not positive. - */ - ssize_t do_recv(void* recv_buf, const size_t buf_size); - /** - * Reads data from the socket and calls parse_in_buffer with it. - */ - void plain_recv(); - /** - * Mark the given data as ready to be sent, as-is, on the socket, as soon - * as we can. - */ - void raw_send(std::string&& data); - - protected: - virtual bool is_connecting() const = 0; -#ifdef BOTAN_FOUND - /** - * 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(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. - */ - void tls_recv(); - /** - * Pass the data to the tls object in order to encrypt it. The tls object - * will then call raw_send as a callback whenever data as been encrypted - * and can be sent on the socket. - */ - void tls_send(std::string&& data); - /** - * Called by the tls object that some data has been decrypt. We call - * parse_in_buffer() to handle that unencrypted data. - */ - void tls_record_received(uint64_t rec_no, const Botan::byte* data, size_t size) BOTAN_TLS_CALLBACKS_OVERRIDE; - /** - * Called by the tls object to indicate that some data has been encrypted - * and is now ready to be sent on the socket as is. - */ - void tls_emit_data(const Botan::byte* data, size_t size) BOTAN_TLS_CALLBACKS_OVERRIDE; - /** - * Called by the tls object to indicate that a TLS alert has been - * received. We don’t use it, we just log some message, at the moment. - */ - void tls_alert(Botan::TLS::Alert alert) BOTAN_TLS_CALLBACKS_OVERRIDE; - /** - * Called by the tls object at the end of the TLS handshake. We don't do - * anything here appart from logging the TLS session information. - */ - bool tls_session_established(const Botan::TLS::Session& session) BOTAN_TLS_CALLBACKS_OVERRIDE; - -#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,34) - void tls_verify_cert_chain(const std::vector& cert_chain, - const std::vector>& ocsp_responses, - const std::vector& trusted_roots, - Botan::Usage_Type usage, - const std::string& hostname, - const Botan::TLS::Policy& policy) BOTAN_TLS_CALLBACKS_OVERRIDE; -#endif - /** - * Called whenever the tls session goes from inactive to active. This - * means that the handshake has just been successfully done, and we can - * now proceed to send any available data into our tls object. - */ - void on_tls_activated(); -#endif // BOTAN_FOUND - /** - * Where data is added, when we want to send something to the client. - */ - std::vector out_buf; -protected: - /** - * Whether we are using TLS on this connection or not. - */ - bool use_tls; - /** - * Where data read from the socket is added until we can extract a full - * and meaningful “message” from it. - * - * TODO: something more efficient than a string. - */ - std::string in_buf; - /** - * Remove the given “size” first bytes from our in_buf. - */ - 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 - * needs to be provided, nullptr is returned (the default implementation - * does that), in that case our internal in_buf will be used to save the - * data until it can be used by parse_in_buffer(). - */ - virtual void* get_receive_buffer(const size_t size) const; - /** - * Called when we detect a disconnection from the remote host. - */ - virtual void on_connection_close(const std::string&) {} - virtual void on_connection_failed(const std::string&) {} - -#ifdef BOTAN_FOUND -protected: - BasicCredentialsManager credential_manager; -private: - /** - * We use a unique_ptr because we may not want to create the object at - * all. The Botan::TLS::Client object generates a handshake message and - * calls the output_fn callback with it as soon as it is created. - * Therefore, we do not want to create it if we do not intend to send any - * TLS-encrypted message. We create the object only when needed (for - * example after we have negociated a TLS session using a STARTTLS - * message, or stuf like that). - * - * See start_tls for the method where this object is created. - */ - std::unique_ptr tls; - /** - * An additional buffer to keep data that the user wants to send, but - * cannot because the handshake is not done. - */ - std::vector pre_buf; -#endif // BOTAN_FOUND -}; -- cgit v1.2.3