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/tcp_socket_handler.cpp | 230 +++--------------------------- 1 file changed, 16 insertions(+), 214 deletions(-) (limited to 'louloulibs/network/tcp_socket_handler.cpp') 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), -- 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 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs/network/tcp_socket_handler.cpp') 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 -- 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 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'louloulibs/network/tcp_socket_handler.cpp') 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(), -- 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/tcp_socket_handler.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'louloulibs/network/tcp_socket_handler.cpp') 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({}); -- 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 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) (limited to 'louloulibs/network/tcp_socket_handler.cpp') 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() -- 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/tcp_socket_handler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs/network/tcp_socket_handler.cpp') 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 -- 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/tcp_socket_handler.cpp | 358 ------------------------------ 1 file changed, 358 deletions(-) delete mode 100644 louloulibs/network/tcp_socket_handler.cpp (limited to 'louloulibs/network/tcp_socket_handler.cpp') 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 -- cgit v1.2.3