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 --- src/network/tcp_socket_handler.cpp | 358 +++++++++++++++++++++++++++++++++++++ 1 file changed, 358 insertions(+) create mode 100644 src/network/tcp_socket_handler.cpp (limited to 'src/network/tcp_socket_handler.cpp') diff --git a/src/network/tcp_socket_handler.cpp b/src/network/tcp_socket_handler.cpp new file mode 100644 index 0000000..7eebae0 --- /dev/null +++ b/src/network/tcp_socket_handler.cpp @@ -0,0 +1,358 @@ +#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 From 5402a256d1f0ebbeafa32d250d000cf38fe748fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 7 Apr 2017 18:45:24 +0200 Subject: Apply all the clang-tidy modernize-* fixes --- src/network/tcp_socket_handler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/network/tcp_socket_handler.cpp') diff --git a/src/network/tcp_socket_handler.cpp b/src/network/tcp_socket_handler.cpp index 7eebae0..b5e5db1 100644 --- a/src/network/tcp_socket_handler.cpp +++ b/src/network/tcp_socket_handler.cpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #ifdef BOTAN_FOUND -- cgit v1.2.3 From ccb4ee098f0416ab47a46650705dba6495e8bec7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 7 Apr 2017 18:53:12 +0200 Subject: Apply all the clang-tidy misc-* fixes --- src/network/tcp_socket_handler.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'src/network/tcp_socket_handler.cpp') diff --git a/src/network/tcp_socket_handler.cpp b/src/network/tcp_socket_handler.cpp index b5e5db1..1bb8bf3 100644 --- a/src/network/tcp_socket_handler.cpp +++ b/src/network/tcp_socket_handler.cpp @@ -42,7 +42,6 @@ namespace using namespace std::string_literals; using namespace std::chrono_literals; -namespace ph = std::placeholders; TCPSocketHandler::TCPSocketHandler(std::shared_ptr& poller): SocketHandler(poller, -1), -- cgit v1.2.3 From 69fd6cc4c3b88443ce98ae32e34909654feb1e4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Thu, 20 Apr 2017 19:14:59 +0200 Subject: Explicitely init the msghdr fields, to be compatible with any implementation --- src/network/tcp_socket_handler.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'src/network/tcp_socket_handler.cpp') diff --git a/src/network/tcp_socket_handler.cpp b/src/network/tcp_socket_handler.cpp index 1bb8bf3..789a4a7 100644 --- a/src/network/tcp_socket_handler.cpp +++ b/src/network/tcp_socket_handler.cpp @@ -125,9 +125,14 @@ ssize_t TCPSocketHandler::do_recv(void* recv_buf, const size_t buf_size) void TCPSocketHandler::on_send() { struct iovec msg_iov[UIO_FASTIOV] = {}; - struct msghdr msg{nullptr, 0, - msg_iov, - 0, nullptr, 0, 0}; + struct msghdr msg; + msg.msg_name = nullptr; + msg.msg_namelen = 0; + msg.msg_iov = msg_iov; + msg.msg_iovlen = 0; + msg.msg_control = nullptr; + msg.msg_controllen = 0; + msg.msg_flags = 0; for (const std::string& s: this->out_buf) { // unconsting the content of s is ok, sendmsg will never modify it -- cgit v1.2.3 From cf87cf089251eddf2c33322e07b0cde9f70ec24b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Thu, 20 Apr 2017 15:44:46 +0200 Subject: Better way to init the msghdr fields --- src/network/tcp_socket_handler.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'src/network/tcp_socket_handler.cpp') diff --git a/src/network/tcp_socket_handler.cpp b/src/network/tcp_socket_handler.cpp index 789a4a7..02265ec 100644 --- a/src/network/tcp_socket_handler.cpp +++ b/src/network/tcp_socket_handler.cpp @@ -125,14 +125,9 @@ ssize_t TCPSocketHandler::do_recv(void* recv_buf, const size_t buf_size) void TCPSocketHandler::on_send() { struct iovec msg_iov[UIO_FASTIOV] = {}; - struct msghdr msg; - msg.msg_name = nullptr; - msg.msg_namelen = 0; + struct msghdr msg{}; msg.msg_iov = msg_iov; msg.msg_iovlen = 0; - msg.msg_control = nullptr; - msg.msg_controllen = 0; - msg.msg_flags = 0; for (const std::string& s: this->out_buf) { // unconsting the content of s is ok, sendmsg will never modify it -- cgit v1.2.3 From 7b3e0e0cf3eddd3537455a3605b04a48ee663f47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sun, 30 Apr 2017 15:04:40 +0200 Subject: =?UTF-8?q?Make=20botan=E2=80=99s=20policy=20configurable=20from?= =?UTF-8?q?=20a=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix #3244 --- src/network/tcp_socket_handler.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'src/network/tcp_socket_handler.cpp') diff --git a/src/network/tcp_socket_handler.cpp b/src/network/tcp_socket_handler.cpp index 02265ec..1bd5315 100644 --- a/src/network/tcp_socket_handler.cpp +++ b/src/network/tcp_socket_handler.cpp @@ -14,6 +14,8 @@ #ifdef BOTAN_FOUND # include # include +# include +# include namespace { @@ -22,11 +24,6 @@ namespace 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()}; @@ -233,6 +230,11 @@ void TCPSocketHandler::consume_in_buffer(const std::size_t size) void TCPSocketHandler::start_tls(const std::string& address, const std::string& port) { Botan::TLS::Server_Information server_info(address, "irc", std::stoul(port)); + auto policy_directory = Config::get("policy_directory", utils::dirname(Config::get_filename())); + if (!policy_directory.empty() && policy_directory[policy_directory.size()-1] != '/') + policy_directory += '/'; + this->policy.load(policy_directory + "policy.txt"); + this->policy.load(policy_directory + address + ".policy.txt"); this->tls = std::make_unique( # if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,32) *this, @@ -242,7 +244,7 @@ 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 - get_session_manager(), this->credential_manager, get_policy(), + get_session_manager(), this->credential_manager, this->policy, get_rng(), server_info, Botan::TLS::Protocol_Version::latest_tls_version()); } -- cgit v1.2.3 From f7e4adb10bff1c278a8543b230b10881ff3799fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 9 May 2017 15:46:20 +0200 Subject: Avoid any potential int overflow --- src/network/tcp_socket_handler.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/network/tcp_socket_handler.cpp') diff --git a/src/network/tcp_socket_handler.cpp b/src/network/tcp_socket_handler.cpp index 1bd5315..1049375 100644 --- a/src/network/tcp_socket_handler.cpp +++ b/src/network/tcp_socket_handler.cpp @@ -227,9 +227,10 @@ void TCPSocketHandler::consume_in_buffer(const std::size_t size) } #ifdef BOTAN_FOUND -void TCPSocketHandler::start_tls(const std::string& address, const std::string& port) +void TCPSocketHandler::start_tls(const std::string& address, const std::string& port_string) { - Botan::TLS::Server_Information server_info(address, "irc", std::stoul(port)); + auto port = std::min(std::stoul(port_string), static_cast(std::numeric_limits::max())); + Botan::TLS::Server_Information server_info(address, "irc", static_cast(port)); auto policy_directory = Config::get("policy_directory", utils::dirname(Config::get_filename())); if (!policy_directory.empty() && policy_directory[policy_directory.size()-1] != '/') policy_directory += '/'; -- cgit v1.2.3