From 0ec82c104ded01a44ed36d20e25220fa41887fd0 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Fri, 27 Feb 2015 12:18:34 +0100 Subject: Add louloulibs as a submodule --- louloulibs | 1 + 1 file changed, 1 insertion(+) create mode 160000 louloulibs (limited to 'louloulibs/network') diff --git a/louloulibs b/louloulibs new file mode 160000 index 0000000..b6af145 --- /dev/null +++ b/louloulibs @@ -0,0 +1 @@ +Subproject commit b6af145bfb9561a1bb1ecb940f50163c5ce4dbbb -- cgit v1.2.3 From e6569a1090be063f34624474f0d4578f37a169ae Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Fri, 27 Feb 2015 12:40:50 +0100 Subject: Only use include_directory() if the directory path is defined --- louloulibs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs/network') diff --git a/louloulibs b/louloulibs index b6af145..d6a3724 160000 --- a/louloulibs +++ b/louloulibs @@ -1 +1 @@ -Subproject commit b6af145bfb9561a1bb1ecb940f50163c5ce4dbbb +Subproject commit d6a3724c6a0127a49a9e7adb1090bb7438c8d0f2 -- cgit v1.2.3 From e4c696861d86b62305ca0ec8136e79f147837b94 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 2 Mar 2015 11:06:40 +0100 Subject: Update louloulibs to last revision --- louloulibs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs/network') diff --git a/louloulibs b/louloulibs index d6a3724..5f3a1bb 160000 --- a/louloulibs +++ b/louloulibs @@ -1 +1 @@ -Subproject commit d6a3724c6a0127a49a9e7adb1090bb7438c8d0f2 +Subproject commit 5f3a1bb54df4de5f332282bbdf791bdce07c71c4 -- cgit v1.2.3 From d88ec5fdf10ecb168355bc38dc81d83ff59a0234 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 2 Mar 2015 11:32:18 +0100 Subject: Update to latest louloulibs revision --- louloulibs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs/network') diff --git a/louloulibs b/louloulibs index 5f3a1bb..d0b8695 160000 --- a/louloulibs +++ b/louloulibs @@ -1 +1 @@ -Subproject commit 5f3a1bb54df4de5f332282bbdf791bdce07c71c4 +Subproject commit d0b8695ceb13e0c6d72821fe605de36e494afcdf -- cgit v1.2.3 From c243fea660723eba00b65e639b76d0783cb59064 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Wed, 4 Mar 2015 05:56:44 +0100 Subject: Update to latest louloulibs revision --- louloulibs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs/network') diff --git a/louloulibs b/louloulibs index d0b8695..99757a4 160000 --- a/louloulibs +++ b/louloulibs @@ -1 +1 @@ -Subproject commit d0b8695ceb13e0c6d72821fe605de36e494afcdf +Subproject commit 99757a44b49619ff59cae9e6d983a3b7c20c56bf -- cgit v1.2.3 From ad0465b32051e224f6a234f3ed36494905e59cbf Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 20 Apr 2015 20:33:02 +0200 Subject: Decode incoming JIDs local part according to xep 0106 This let users send message to nicks such as Q@CServe.quakenet.org fix #3047 --- louloulibs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs/network') diff --git a/louloulibs b/louloulibs index 99757a4..88d2b13 160000 --- a/louloulibs +++ b/louloulibs @@ -1 +1 @@ -Subproject commit 99757a44b49619ff59cae9e6d983a3b7c20c56bf +Subproject commit 88d2b136e5f133f0d0dc01f59449284f663d53ea -- cgit v1.2.3 From 0d706741c6b3a8bdf6b4f8ca0b1ac00cb27bd8b8 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 20 Apr 2015 20:35:32 +0200 Subject: Update louloulibs submodule to the correct revision --- louloulibs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs/network') diff --git a/louloulibs b/louloulibs index 88d2b13..b53ae92 160000 --- a/louloulibs +++ b/louloulibs @@ -1 +1 @@ -Subproject commit 88d2b136e5f133f0d0dc01f59449284f663d53ea +Subproject commit b53ae922f48f1465a7fa61136f65ec39e38a452e -- cgit v1.2.3 From a8225dc54c019788722bda3bda8d55151c1ccdef Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Tue, 21 Apr 2015 15:35:10 +0200 Subject: Properly check for connecting or connected status before reconnecting Note, in our context, is_connecting() includes the resolving part as well as the actual connection (if we are using c-ares) fix #3048 --- louloulibs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs/network') diff --git a/louloulibs b/louloulibs index b53ae92..6c812cd 160000 --- a/louloulibs +++ b/louloulibs @@ -1 +1 @@ -Subproject commit b53ae922f48f1465a7fa61136f65ec39e38a452e +Subproject commit 6c812cd86e31569db61cac4e30f77e296d207191 -- cgit v1.2.3 From 71fec776c4d7b99b76a44deae6f333d9cffa1496 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Thu, 7 May 2015 17:42:37 +0200 Subject: Update to latest louloulibs fix #3042 --- louloulibs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs/network') diff --git a/louloulibs b/louloulibs index 6c812cd..eaa4fbb 160000 --- a/louloulibs +++ b/louloulibs @@ -1 +1 @@ -Subproject commit 6c812cd86e31569db61cac4e30f77e296d207191 +Subproject commit eaa4fbba814b56b4fe7ffb62984fddfbb9280291 -- cgit v1.2.3 From fbeb5af364db54c8a82f5ea30b83df441988ea4b Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Wed, 13 May 2015 20:17:43 +0200 Subject: Update to latest louloulibs revision, and add test for hostname validity fix #2694 --- louloulibs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs/network') diff --git a/louloulibs b/louloulibs index eaa4fbb..89398b5 160000 --- a/louloulibs +++ b/louloulibs @@ -1 +1 @@ -Subproject commit eaa4fbba814b56b4fe7ffb62984fddfbb9280291 +Subproject commit 89398b5d886744c3812b65195308cae57eca2b53 -- cgit v1.2.3 From 897b281e67dc82700db9fd9c2dedc5e01e5871ee Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Wed, 27 May 2015 23:44:23 +0200 Subject: Avoid some potential race conditions by blocking the signals we manage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit They are atomically unblocked in the ppoll/epoll_pwait calls, avoiding any race condition on the check of the “stop” or “reload” booleans. --- louloulibs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs/network') diff --git a/louloulibs b/louloulibs index 89398b5..0f3c118 160000 --- a/louloulibs +++ b/louloulibs @@ -1 +1 @@ -Subproject commit 89398b5d886744c3812b65195308cae57eca2b53 +Subproject commit 0f3c1183e2bf0941ae2bffd3f31577bce4f3001c -- cgit v1.2.3 From e1a7114c8daa10589c830ce972cf461c3540111b Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Thu, 28 May 2015 23:42:52 +0200 Subject: louloulibs is directly included, instead of being a submodule Because this is a nightmare to manage --- louloulibs | 1 - louloulibs/network/dns_handler.cpp | 112 ++++++ louloulibs/network/dns_handler.hpp | 62 ++++ louloulibs/network/dns_socket_handler.cpp | 45 +++ louloulibs/network/dns_socket_handler.hpp | 46 +++ louloulibs/network/poller.cpp | 225 ++++++++++++ louloulibs/network/poller.hpp | 95 +++++ louloulibs/network/socket_handler.hpp | 45 +++ louloulibs/network/tcp_socket_handler.cpp | 590 ++++++++++++++++++++++++++++++ louloulibs/network/tcp_socket_handler.hpp | 293 +++++++++++++++ 10 files changed, 1513 insertions(+), 1 deletion(-) delete mode 160000 louloulibs create mode 100644 louloulibs/network/dns_handler.cpp create mode 100644 louloulibs/network/dns_handler.hpp create mode 100644 louloulibs/network/dns_socket_handler.cpp create mode 100644 louloulibs/network/dns_socket_handler.hpp create mode 100644 louloulibs/network/poller.cpp create mode 100644 louloulibs/network/poller.hpp create mode 100644 louloulibs/network/socket_handler.hpp create mode 100644 louloulibs/network/tcp_socket_handler.cpp create mode 100644 louloulibs/network/tcp_socket_handler.hpp (limited to 'louloulibs/network') diff --git a/louloulibs b/louloulibs deleted file mode 160000 index 0f3c118..0000000 --- a/louloulibs +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0f3c1183e2bf0941ae2bffd3f31577bce4f3001c diff --git a/louloulibs/network/dns_handler.cpp b/louloulibs/network/dns_handler.cpp new file mode 100644 index 0000000..ec53683 --- /dev/null +++ b/louloulibs/network/dns_handler.cpp @@ -0,0 +1,112 @@ +#include +#ifdef CARES_FOUND + +#include +#include +#include +#include + +#include +#include + +DNSHandler DNSHandler::instance; + +using namespace std::string_literals; + +void on_hostname4_resolved(void* arg, int status, int, struct hostent* hostent) +{ + TCPSocketHandler* socket_handler = static_cast(arg); + socket_handler->on_hostname4_resolved(status, hostent); +} + +void on_hostname6_resolved(void* arg, int status, int, struct hostent* hostent) +{ + TCPSocketHandler* socket_handler = static_cast(arg); + socket_handler->on_hostname6_resolved(status, hostent); +} + +DNSHandler::DNSHandler() +{ + 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)); + if ((ares_error = ::ares_init(&this->channel)) != 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; +} + +void DNSHandler::destroy() +{ + this->socket_handlers.clear(); + ::ares_destroy(this->channel); + ::ares_library_cleanup(); +} + +void DNSHandler::gethostbyname(const std::string& name, + TCPSocketHandler* socket_handler, int family) +{ + socket_handler->free_cares_addrinfo(); + if (family == AF_INET) + ::ares_gethostbyname(this->channel, name.data(), family, + &::on_hostname4_resolved, socket_handler); + else + ::ares_gethostbyname(this->channel, name.data(), family, + &::on_hostname6_resolved, socket_handler); +} + +void DNSHandler::watch_dns_sockets(std::shared_ptr& poller) +{ + 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_front(std::make_unique(poller, i)); + it = this->socket_handlers.begin(); + } + poller->add_socket_handler(it->get()); + if (write) + poller->watch_send_events(it->get()); + } + } +} + +#endif /* CARES_FOUND */ diff --git a/louloulibs/network/dns_handler.hpp b/louloulibs/network/dns_handler.hpp new file mode 100644 index 0000000..a515f52 --- /dev/null +++ b/louloulibs/network/dns_handler.hpp @@ -0,0 +1,62 @@ +#ifndef DNS_HANDLER_HPP_INCLUDED +#define DNS_HANDLER_HPP_INCLUDED + +#include +#ifdef CARES_FOUND + +class TCPSocketHandler; +class Poller; +class DNSSocketHandler; + +# include +# include +# include +# include + +void on_hostname4_resolved(void* arg, int status, int, struct hostent* hostent); +void on_hostname6_resolved(void* arg, int status, int, struct hostent* hostent); + +/** + * Class managing DNS resolution. It should only be statically instanciated + * once in SocketHandler. It manages ares channel and calls various + * functions of that library. + */ + +class DNSHandler +{ +public: + DNSHandler(); + ~DNSHandler() = default; + void gethostbyname(const std::string& name, TCPSocketHandler* 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(); + ares_channel& get_channel(); + + static DNSHandler instance; + +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 + */ + std::list> socket_handlers; + ares_channel channel; + + DNSHandler(const DNSHandler&) = delete; + DNSHandler(DNSHandler&&) = delete; + DNSHandler& operator=(const DNSHandler&) = delete; + DNSHandler& operator=(DNSHandler&&) = delete; +}; + +#endif /* CARES_FOUND */ +#endif /* DNS_HANDLER_HPP_INCLUDED */ diff --git a/louloulibs/network/dns_socket_handler.cpp b/louloulibs/network/dns_socket_handler.cpp new file mode 100644 index 0000000..124c9b2 --- /dev/null +++ b/louloulibs/network/dns_socket_handler.cpp @@ -0,0 +1,45 @@ +#include +#ifdef CARES_FOUND + +#include +#include +#include + +#include + +DNSSocketHandler::DNSSocketHandler(std::shared_ptr poller, + const socket_t socket): + SocketHandler(poller, socket) +{ +} + +DNSSocketHandler::~DNSSocketHandler() +{ +} + +void DNSSocketHandler::connect() +{ +} + +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->poller->remove_socket_handler(this->socket); + ::ares_process_fd(DNSHandler::instance.get_channel(), this->socket, ARES_SOCKET_BAD); +} + +void DNSSocketHandler::on_send() +{ + // always stop watching send and read events. We will re-watch them if the + // next call to ares_fds tell us to + this->poller->remove_socket_handler(this->socket); + ::ares_process_fd(DNSHandler::instance.get_channel(), ARES_SOCKET_BAD, this->socket); +} + +bool DNSSocketHandler::is_connected() const +{ + return true; +} + +#endif /* CARES_FOUND */ diff --git a/louloulibs/network/dns_socket_handler.hpp b/louloulibs/network/dns_socket_handler.hpp new file mode 100644 index 0000000..ad119e1 --- /dev/null +++ b/louloulibs/network/dns_socket_handler.hpp @@ -0,0 +1,46 @@ +#ifndef DNS_SOCKET_HANDLER_HPP +# define DNS_SOCKET_HANDLER_HPP + +#include +#ifdef CARES_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) + */ + +class DNSSocketHandler: public SocketHandler +{ +public: + explicit DNSSocketHandler(std::shared_ptr poller, const socket_t socket); + ~DNSSocketHandler(); + /** + * 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; + /** + * 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; + +private: + DNSSocketHandler(const DNSSocketHandler&) = delete; + DNSSocketHandler(DNSSocketHandler&&) = delete; + DNSSocketHandler& operator=(const DNSSocketHandler&) = delete; + DNSSocketHandler& operator=(DNSSocketHandler&&) = delete; +}; + +#endif // CARES_FOUND +#endif // DNS_SOCKET_HANDLER_HPP diff --git a/louloulibs/network/poller.cpp b/louloulibs/network/poller.cpp new file mode 100644 index 0000000..329e1c8 --- /dev/null +++ b/louloulibs/network/poller.cpp @@ -0,0 +1,225 @@ +#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() +{ +} + +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) + { + if (this->fds[i].revents == 0) + continue; + else if (this->fds[i].revents & POLLIN) + { + auto socket_handler = this->socket_handlers.at(this->fds[i].fd); + socket_handler->on_recv(); + nb_events--; + } + else if (this->fds[i].revents & POLLOUT) + { + auto socket_handler = this->socket_handlers.at(this->fds[i].fd); + if (socket_handler->is_connected()) + socket_handler->on_send(); + else + 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->on_recv(); + else if (revents[i].events & EPOLLOUT) + { + if (socket_handler->is_connected()) + socket_handler->on_send(); + else + socket_handler->connect(); + } + } + return nb_events; +#endif +} + +size_t Poller::size() const +{ + return this->socket_handlers.size(); +} diff --git a/louloulibs/network/poller.hpp b/louloulibs/network/poller.hpp new file mode 100644 index 0000000..de0cb48 --- /dev/null +++ b/louloulibs/network/poller.hpp @@ -0,0 +1,95 @@ +#ifndef POLLER_INCLUDED +# define POLLER_INCLUDED + +#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(); + /** + * 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; + +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 + + Poller(const Poller&) = delete; + Poller(Poller&&) = delete; + Poller& operator=(const Poller&) = delete; + Poller& operator=(Poller&&) = delete; +}; + +#endif // POLLER_INCLUDED diff --git a/louloulibs/network/socket_handler.hpp b/louloulibs/network/socket_handler.hpp new file mode 100644 index 0000000..d01ac5d --- /dev/null +++ b/louloulibs/network/socket_handler.hpp @@ -0,0 +1,45 @@ +#ifndef SOCKET_HANDLER_HPP +# define SOCKET_HANDLER_HPP + +#include +#include + +class Poller; + +typedef int socket_t; + +class SocketHandler +{ +public: + explicit SocketHandler(std::shared_ptr poller, const socket_t socket): + poller(poller), + socket(socket) + {} + virtual ~SocketHandler() {} + virtual void on_recv() = 0; + virtual void on_send() = 0; + virtual void connect() = 0; + 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; + +private: + SocketHandler(const SocketHandler&) = delete; + SocketHandler(SocketHandler&&) = delete; + SocketHandler& operator=(const SocketHandler&) = delete; + SocketHandler& operator=(SocketHandler&&) = delete; +}; + +#endif // SOCKET_HANDLER_HPP diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp new file mode 100644 index 0000000..f647b86 --- /dev/null +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -0,0 +1,590 @@ +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef BOTAN_FOUND +# include + +Botan::AutoSeeded_RNG TCPSocketHandler::rng; +Permissive_Credentials_Manager TCPSocketHandler::credential_manager; +Botan::TLS::Policy TCPSocketHandler::policy; +Botan::TLS::Session_Manager_In_Memory TCPSocketHandler::session_manager(TCPSocketHandler::rng); + +#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), + connected(false), + connecting(false) +#ifdef CARES_FOUND + ,resolving(false), + resolved(false), + resolved4(false), + resolved6(false), + cares_addrinfo(nullptr), + cares_error() +#endif +{} + +TCPSocketHandler::~TCPSocketHandler() +{ +#ifdef CARES_FOUND + this->free_cares_addrinfo(); +#endif +} + +void TCPSocketHandler::init_socket(const struct addrinfo* rp) +{ + 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)); + 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; + + utils::ScopeGuard sg; + + 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. +#ifdef CARES_FOUND + if (!this->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->resolving = true; + DNSHandler::instance.gethostbyname(address, this, AF_INET6); + DNSHandler::instance.gethostbyname(address, this, AF_INET); + 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->cares_addrinfo; + if (!addr_res) + { + this->close(); + this->on_connection_failed(this->cares_error); + return ; + } + } +#else + log_info("Trying to connect to " << address << ":" << port); + 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; + + const int res = ::getaddrinfo(address.c_str(), port.c_str(), &hints, &addr_res); + + if (res != 0) + { + log_warning("getaddrinfo failed: "s + gai_strerror(res)); + this->close(); + this->on_connection_failed(gai_strerror(res)); + return ; + } + // Make sure the alloced structure is always freed at the end of the + // function + sg.add_callback([&addr_res](){ freeaddrinfo(addr_res); }); +#endif + } + 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; + } + } + 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->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)); + } + 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() +{ +#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) + { + 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; + 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 (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(); + 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. + for (auto it = this->out_buf.begin(); + it != this->out_buf.end();) + { + if (static_cast(res) >= (*it).size()) + { + res -= (*it).size(); + it = this->out_buf.erase(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; + } + } + if (this->out_buf.empty()) + this->poller->stop_watching_send_events(this); + } +} + +void TCPSocketHandler::close() +{ + TimedEventsManager::instance().cancel("connection_timeout"s + + std::to_string(this->socket)); + if (this->connected || this->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; +#ifdef CARES_FOUND + this->resolving = false; + this->resolved = false; + this->resolved4 = false; + this->resolved6 = false; + this->free_cares_addrinfo(); + this->cares_error.clear(); +#endif + this->in_buf.clear(); + this->out_buf.clear(); + this->port.clear(); +} + +void TCPSocketHandler::send_data(std::string&& data) +{ +#ifdef BOTAN_FOUND + if (this->use_tls) + this->tls_send(std::move(data)); + 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->connected) + this->poller->watch_send_events(this); +} + +void TCPSocketHandler::send_pending_data() +{ + if (this->connected && !this->out_buf.empty()) + this->poller->watch_send_events(this); +} + +bool TCPSocketHandler::is_connected() const +{ + return this->connected; +} + +bool TCPSocketHandler::is_connecting() const +{ +#ifdef CARES_FOUND + return this->connecting || this->resolving; +#else + return this->connecting; +#endif +} + +void* TCPSocketHandler::get_receive_buffer(const size_t) const +{ + return nullptr; +} + +#ifdef BOTAN_FOUND +void TCPSocketHandler::start_tls() +{ + Botan::TLS::Server_Information server_info(this->address, "irc", std::stoul(this->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), + session_manager, credential_manager, policy, + rng, server_info, Botan::TLS::Protocol_Version::latest_tls_version()); +} + +void TCPSocketHandler::tls_recv() +{ + static constexpr size_t buf_size = 4096; + char 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(); + this->tls->received_data(reinterpret_cast(recv_buf), + static_cast(size)); + if (!was_active && this->tls->is_active()) + this->on_tls_activated(); + } +} + +void TCPSocketHandler::tls_send(std::string&& data) +{ + if (this->tls->is_active()) + { + const bool was_active = this->tls->is_active(); + if (!this->pre_buf.empty()) + { + this->tls->send(reinterpret_cast(this->pre_buf.data()), + this->pre_buf.size()); + this->pre_buf = ""; + } + 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 += data; +} + +void TCPSocketHandler::tls_data_cb(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_output_fn(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) +{ + log_debug("tls_alert: " << alert.type_string()); +} + +bool TCPSocketHandler::tls_handshake_cb(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; +} + +void TCPSocketHandler::on_tls_activated() +{ + this->send_data(""); +} + +void Permissive_Credentials_Manager::verify_certificate_chain(const std::string& type, + const std::string& purported_hostname, + const std::vector&) +{ // TODO: Offer the admin to disallow connection on untrusted + // certificates + log_debug("Checking remote certificate (" << type << ") for hostname " << purported_hostname); +} + +#endif // BOTAN_FOUND + +#ifdef CARES_FOUND + +void TCPSocketHandler::on_hostname4_resolved(int status, struct hostent* hostent) +{ + this->resolved4 = true; + if (status == ARES_SUCCESS) + this->fill_ares_addrinfo4(hostent); + else + this->cares_error = ::ares_strerror(status); + + if (this->resolved4 && this->resolved6) + { + this->resolved = true; + this->resolving = false; + this->connect(); + } +} + +void TCPSocketHandler::on_hostname6_resolved(int status, struct hostent* hostent) +{ + this->resolved6 = true; + if (status == ARES_SUCCESS) + this->fill_ares_addrinfo6(hostent); + else + this->cares_error = ::ares_strerror(status); + + if (this->resolved4 && this->resolved6) + { + this->resolved = true; + this->resolving = false; + this->connect(); + } +} + +void TCPSocketHandler::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* addr = new struct sockaddr_in; + addr->sin_family = hostent->h_addrtype; + addr->sin_port = htons(strtoul(this->port.data(), nullptr, 10)); + addr->sin_addr.s_addr = (*address)->s_addr; + + current->ai_addr = reinterpret_cast(addr); + current->ai_next = nullptr; + current->ai_canonname = nullptr; + + current->ai_next = prev; + this->cares_addrinfo = current; + prev = current; + ++address; + } +} + +void TCPSocketHandler::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* addr = new struct sockaddr_in6; + addr->sin6_family = hostent->h_addrtype; + addr->sin6_port = htons(strtoul(this->port.data(), nullptr, 10)); + ::memcpy(addr->sin6_addr.s6_addr, (*address)->s6_addr, 16); + addr->sin6_flowinfo = 0; + addr->sin6_scope_id = 0; + + current->ai_addr = reinterpret_cast(addr); + current->ai_next = nullptr; + current->ai_canonname = nullptr; + + current->ai_next = prev; + this->cares_addrinfo = current; + prev = current; + ++address; + } +} + +void TCPSocketHandler::free_cares_addrinfo() +{ + while (this->cares_addrinfo) + { + delete this->cares_addrinfo->ai_addr; + auto next = this->cares_addrinfo->ai_next; + delete this->cares_addrinfo; + this->cares_addrinfo = next; + } +} + +#endif // CARES_FOUND diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp new file mode 100644 index 0000000..2b5cd49 --- /dev/null +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -0,0 +1,293 @@ +#ifndef SOCKET_HANDLER_INCLUDED +# define SOCKET_HANDLER_INCLUDED + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "louloulibs.h" + +#ifdef CARES_FOUND +# include +#endif + +#ifdef BOTAN_FOUND +# include +# include + +/** + * A very simple credential manager that accepts any certificate. + */ +class Permissive_Credentials_Manager: public Botan::Credentials_Manager +{ +public: + void verify_certificate_chain(const std::string& type, + const std::string& purported_hostname, + const std::vector&); +}; +#endif // BOTAN_FOUND + +/** + * 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) + */ +class TCPSocketHandler: public SocketHandler +{ +protected: + ~TCPSocketHandler(); + +public: + explicit TCPSocketHandler(std::shared_ptr poller); + /** + * 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() + */ + 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 + */ + 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; + /** + * 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. + */ + virtual void parse_in_buffer(const size_t size) = 0; + bool is_connected() const override final; + bool is_connecting() const; + +#ifdef CARES_FOUND + void on_hostname4_resolved(int status, struct hostent* hostent); + void on_hostname6_resolved(int status, struct hostent* hostent); + + void free_cares_addrinfo(); + + void fill_ares_addrinfo4(const struct hostent* hostent); + void fill_ares_addrinfo6(const struct hostent* hostent); +#endif + +private: + /** + * Initialize the socket with the parameters contained in the given + * addrinfo structure. + */ + void init_socket(const struct addrinfo* rp); + /** + * Reads from the socket into the provided buffer. If an error occurs + * (read returns <= 0), the handling of the error is done here (close the + * connection, log a message, etc). + * + * 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); + +#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(); + /** + * 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_data_cb(const Botan::byte* data, size_t size); + /** + * 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); + /** + * 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); + /** + * 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); + /** + * 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::list out_buf; + /** + * Keep the details of the addrinfo 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; + +protected: + /** + * 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; + /** + * Whether we are using TLS on this connection or not. + */ + bool use_tls; + /** + * 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; + /** + * Hostname we are connected/connecting to + */ + std::string address; + /** + * Port we are connected/connecting to + */ + std::string port; + + bool connected; + bool connecting; + +#ifdef CARES_FOUND + bool resolving; + /** + * Whether or not the DNS resolution was successfully done + */ + bool resolved; + bool resolved4; + bool resolved6; + /** + * When using c-ares to resolve the host asynchronously, we need the + * c-ares callback 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 next call of connect() will then try all these values + * (exactly like we do with the result of getaddrinfo) and save the one + * that worked (or returned EINPROGRESS) in the other struct addrinfo (see + * the members addrinfo, ai_addrlen, and ai_addr). + */ + struct addrinfo* cares_addrinfo; + std::string cares_error; +#endif // CARES_FOUND + +private: + TCPSocketHandler(const TCPSocketHandler&) = delete; + TCPSocketHandler(TCPSocketHandler&&) = delete; + TCPSocketHandler& operator=(const TCPSocketHandler&) = delete; + TCPSocketHandler& operator=(TCPSocketHandler&&) = delete; + +#ifdef BOTAN_FOUND + /** + * Botan stuff to manipulate a TLS session. + */ + static Botan::AutoSeeded_RNG rng; + static Permissive_Credentials_Manager credential_manager; + static Botan::TLS::Policy policy; + static Botan::TLS::Session_Manager_In_Memory session_manager; + /** + * 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 as + * soon and calls the output_fn callback with it as soon as it is + * created. Therefore, we do not want to create it if do not intend to do + * 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::string pre_buf; +#endif // BOTAN_FOUND +}; + +#endif // SOCKET_HANDLER_INCLUDED -- cgit v1.2.3 From 0af536a2be6d23fa221f61a77e89436b8fb2a015 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Wed, 8 Jul 2015 18:51:05 +0200 Subject: Minor comment typo --- louloulibs/network/tcp_socket_handler.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index 2b5cd49..0dde2cf 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -272,11 +272,11 @@ private: static Botan::TLS::Session_Manager_In_Memory session_manager; /** * 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 as - * soon and calls the output_fn callback with it as soon as it is - * created. Therefore, we do not want to create it if do not intend to do - * send any TLS-encrypted message. We create the object only when needed - * (for example after we have negociated a TLS session using a STARTTLS + * 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. -- cgit v1.2.3 From 4130f1679d909e90d691d47d07cd0e0d4fbe64e1 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Thu, 9 Jul 2015 13:54:24 +0200 Subject: Check for timeouts on the DNS resolution using c-ares ref #3083 --- louloulibs/network/dns_handler.cpp | 30 ++++++++++++++++++++++++++++-- louloulibs/network/dns_handler.hpp | 4 ++-- 2 files changed, 30 insertions(+), 4 deletions(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/dns_handler.cpp b/louloulibs/network/dns_handler.cpp index ec53683..324784b 100644 --- a/louloulibs/network/dns_handler.cpp +++ b/louloulibs/network/dns_handler.cpp @@ -6,6 +6,8 @@ #include #include +#include + #include #include @@ -30,7 +32,13 @@ DNSHandler::DNSHandler() 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)); - if ((ares_error = ::ares_init(&this->channel)) != ARES_SUCCESS) + 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)); } @@ -99,7 +107,8 @@ void DNSHandler::watch_dns_sockets(std::shared_ptr& poller) { // If not found, create it because we need to watch it if (it == this->socket_handlers.end()) { - this->socket_handlers.emplace_front(std::make_unique(poller, i)); + this->socket_handlers.emplace(this->socket_handlers.begin(), + std::make_unique(poller, i)); it = this->socket_handlers.begin(); } poller->add_socket_handler(it->get()); @@ -107,6 +116,23 @@ void DNSHandler::watch_dns_sockets(std::shared_ptr& poller) 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")); + } } #endif /* CARES_FOUND */ diff --git a/louloulibs/network/dns_handler.hpp b/louloulibs/network/dns_handler.hpp index a515f52..ec35374 100644 --- a/louloulibs/network/dns_handler.hpp +++ b/louloulibs/network/dns_handler.hpp @@ -11,7 +11,7 @@ class DNSSocketHandler; # include # include # include -# include +# include void on_hostname4_resolved(void* arg, int status, int, struct hostent* hostent); void on_hostname6_resolved(void* arg, int status, int, struct hostent* hostent); @@ -49,7 +49,7 @@ private: * call to ares_fds. DNSSocketHandlers are added to it or removed from it * in the watch_dns_sockets() method */ - std::list> socket_handlers; + std::vector> socket_handlers; ares_channel channel; DNSHandler(const DNSHandler&) = delete; -- cgit v1.2.3 From ac6a74595c679c22ae13eca0609a745e431b339c Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Thu, 9 Jul 2015 13:56:22 +0200 Subject: Send the cares error message before cleaning it fix #3083 --- louloulibs/network/tcp_socket_handler.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index f647b86..e20357b 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -109,8 +109,9 @@ void TCPSocketHandler::connect(const std::string& address, const std::string& po addr_res = this->cares_addrinfo; if (!addr_res) { + const auto msg = this->cares_error; this->close(); - this->on_connection_failed(this->cares_error); + this->on_connection_failed(msg); return ; } } -- cgit v1.2.3 From ece4b4969b296a3da010fb22768348650e70962d Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Thu, 9 Jul 2015 15:30:24 +0200 Subject: If hostname resolution fails, do not try all possible ports --- louloulibs/network/tcp_socket_handler.cpp | 9 ++++++--- louloulibs/network/tcp_socket_handler.hpp | 2 ++ 2 files changed, 8 insertions(+), 3 deletions(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index e20357b..04bb321 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -40,15 +40,16 @@ TCPSocketHandler::TCPSocketHandler(std::shared_ptr poller): SocketHandler(poller, -1), use_tls(false), connected(false), - connecting(false) + connecting(false), #ifdef CARES_FOUND - ,resolving(false), + resolving(false), resolved(false), resolved4(false), resolved6(false), cares_addrinfo(nullptr), - cares_error() + cares_error(), #endif + hostname_resolution_failed(false) {} TCPSocketHandler::~TCPSocketHandler() @@ -109,6 +110,7 @@ void TCPSocketHandler::connect(const std::string& address, const std::string& po addr_res = this->cares_addrinfo; if (!addr_res) { + this->hostname_resolution_failed = true; const auto msg = this->cares_error; this->close(); this->on_connection_failed(msg); @@ -129,6 +131,7 @@ void TCPSocketHandler::connect(const std::string& address, const std::string& po if (res != 0) { log_warning("getaddrinfo failed: "s + gai_strerror(res)); + this->hostname_resolution_failed = true; this->close(); this->on_connection_failed(gai_strerror(res)); return ; diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index 0dde2cf..8069825 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -256,6 +256,8 @@ protected: std::string cares_error; #endif // CARES_FOUND + bool hostname_resolution_failed; + private: TCPSocketHandler(const TCPSocketHandler&) = delete; TCPSocketHandler(TCPSocketHandler&&) = delete; -- cgit v1.2.3 From 45fd41eb38ce9cd0c5fd14d7278514b79d7debcd Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Thu, 9 Jul 2015 15:30:54 +0200 Subject: Display the resolved IP in debug logs --- louloulibs/network/tcp_socket_handler.cpp | 17 +++++++++++++++++ louloulibs/network/tcp_socket_handler.hpp | 5 +++++ 2 files changed, 22 insertions(+) (limited to 'louloulibs/network') diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index 04bb321..cca6cd2 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -16,6 +16,7 @@ #include #include +#include #ifdef BOTAN_FOUND # include @@ -159,6 +160,9 @@ void TCPSocketHandler::connect(const std::string& address, const std::string& po break; } } + + this->display_resolved_ip(rp); + if (::connect(this->socket, rp->ai_addr, rp->ai_addrlen) == 0 || errno == EISCONN) { @@ -343,6 +347,19 @@ void TCPSocketHandler::close() this->port.clear(); } +void TCPSocketHandler::display_resolved_ip(struct addrinfo* rp) const +{ + char buf[INET6_ADDRSTRLEN]; + if (rp->ai_family == AF_INET) + log_debug("Connecting to IP address " << ::inet_ntop(rp->ai_family, + &reinterpret_cast(rp->ai_addr)->sin_addr, + buf, sizeof(buf))); + else if (rp->ai_family == AF_INET6) + log_debug("Connecting to IPv6 address " << ::inet_ntop(rp->ai_family, + &reinterpret_cast(rp->ai_addr)->sin6_addr, + buf, sizeof(buf))); +} + void TCPSocketHandler::send_data(std::string&& data) { #ifdef BOTAN_FOUND diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index 8069825..835c509 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -264,6 +264,11 @@ private: TCPSocketHandler& operator=(const TCPSocketHandler&) = delete; TCPSocketHandler& operator=(TCPSocketHandler&&) = delete; + /** + * 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 2928598e773ca6708efdda1b6c35786cd3fa5587 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Tue, 22 Sep 2015 03:59:14 +0200 Subject: Catch TLS exceptions, close the connection and inform the user of the error --- louloulibs/network/tcp_socket_handler.cpp | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index cca6cd2..9424a1a 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -20,6 +20,7 @@ #ifdef BOTAN_FOUND # include +# include Botan::AutoSeeded_RNG TCPSocketHandler::rng; Permissive_Credentials_Manager TCPSocketHandler::credential_manager; @@ -364,7 +365,13 @@ void TCPSocketHandler::send_data(std::string&& data) { #ifdef BOTAN_FOUND if (this->use_tls) - this->tls_send(std::move(data)); + 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)); @@ -426,8 +433,17 @@ void TCPSocketHandler::tls_recv() if (size > 0) { const bool was_active = this->tls->is_active(); - this->tls->received_data(reinterpret_cast(recv_buf), - static_cast(size)); + try { + this->tls->received_data(reinterpret_cast(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(); } @@ -441,7 +457,7 @@ void TCPSocketHandler::tls_send(std::string&& data) if (!this->pre_buf.empty()) { this->tls->send(reinterpret_cast(this->pre_buf.data()), - this->pre_buf.size()); + this->pre_buf.size()); this->pre_buf = ""; } if (!data.empty()) -- cgit v1.2.3 From aa340e1c5e4e28397ef212aa210633e9dcb81f98 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Thu, 15 Oct 2015 04:32:02 +0200 Subject: Separate the DNS resolution logic from the TCP communication logic fix #3137 --- louloulibs/network/dns_handler.cpp | 23 +--- louloulibs/network/dns_handler.hpp | 7 +- louloulibs/network/resolver.cpp | 219 ++++++++++++++++++++++++++++++ louloulibs/network/resolver.hpp | 129 ++++++++++++++++++ louloulibs/network/tcp_socket_handler.cpp | 202 +++------------------------ louloulibs/network/tcp_socket_handler.hpp | 49 ++----- 6 files changed, 383 insertions(+), 246 deletions(-) create mode 100644 louloulibs/network/resolver.cpp create mode 100644 louloulibs/network/resolver.hpp (limited to 'louloulibs/network') diff --git a/louloulibs/network/dns_handler.cpp b/louloulibs/network/dns_handler.cpp index 324784b..a8d0f9c 100644 --- a/louloulibs/network/dns_handler.cpp +++ b/louloulibs/network/dns_handler.cpp @@ -2,7 +2,6 @@ #ifdef CARES_FOUND #include -#include #include #include @@ -14,19 +13,6 @@ DNSHandler DNSHandler::instance; using namespace std::string_literals; - -void on_hostname4_resolved(void* arg, int status, int, struct hostent* hostent) -{ - TCPSocketHandler* socket_handler = static_cast(arg); - socket_handler->on_hostname4_resolved(status, hostent); -} - -void on_hostname6_resolved(void* arg, int status, int, struct hostent* hostent) -{ - TCPSocketHandler* socket_handler = static_cast(arg); - socket_handler->on_hostname6_resolved(status, hostent); -} - DNSHandler::DNSHandler() { int ares_error; @@ -54,16 +40,15 @@ void DNSHandler::destroy() ::ares_library_cleanup(); } -void DNSHandler::gethostbyname(const std::string& name, - TCPSocketHandler* socket_handler, int family) +void DNSHandler::gethostbyname(const std::string& name, ares_host_callback callback, + void* data, int family) { - socket_handler->free_cares_addrinfo(); if (family == AF_INET) ::ares_gethostbyname(this->channel, name.data(), family, - &::on_hostname4_resolved, socket_handler); + callback, data); else ::ares_gethostbyname(this->channel, name.data(), family, - &::on_hostname6_resolved, socket_handler); + callback, data); } void DNSHandler::watch_dns_sockets(std::shared_ptr& poller) diff --git a/louloulibs/network/dns_handler.hpp b/louloulibs/network/dns_handler.hpp index ec35374..61e2302 100644 --- a/louloulibs/network/dns_handler.hpp +++ b/louloulibs/network/dns_handler.hpp @@ -13,9 +13,6 @@ class DNSSocketHandler; # include # include -void on_hostname4_resolved(void* arg, int status, int, struct hostent* hostent); -void on_hostname6_resolved(void* arg, int status, int, struct hostent* hostent); - /** * Class managing DNS resolution. It should only be statically instanciated * once in SocketHandler. It manages ares channel and calls various @@ -27,8 +24,8 @@ class DNSHandler public: DNSHandler(); ~DNSHandler() = default; - void gethostbyname(const std::string& name, TCPSocketHandler* socket_handler, - int family); + 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. diff --git a/louloulibs/network/resolver.cpp b/louloulibs/network/resolver.cpp new file mode 100644 index 0000000..bfd75d7 --- /dev/null +++ b/louloulibs/network/resolver.cpp @@ -0,0 +1,219 @@ +#include +#include +#include +#include + +// remove me +#include + +using namespace std::string_literals; + +Resolver::Resolver(): +#ifdef CARES_FOUND + resolved4(false), + resolved6(false), + resolving(false), + cares_addrinfo(nullptr), + 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; + this->port = port; + + this->start_resolving(hostname, port); +} + +#ifdef CARES_FOUND +void Resolver::start_resolving(const std::string& hostname, const std::string& port) +{ + std::cout << "start_resolving: " << hostname << port << std::endl; + this->resolving = true; + this->resolved = false; + this->resolved4 = false; + this->resolved6 = false; + + this->error_msg.clear(); + this->cares_addrinfo = nullptr; + + auto hostname4_resolved = [](void* arg, int status, int, + struct hostent* hostent) + { + Resolver* resolver = static_cast(arg); + resolver->on_hostname4_resolved(status, hostent); + }; + auto hostname6_resolved = [](void* arg, int status, int, + struct hostent* hostent) + { + 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); +} + +void Resolver::on_hostname4_resolved(int status, struct hostent* hostent) +{ + this->resolved4 = true; + if (status == ARES_SUCCESS) + this->fill_ares_addrinfo4(hostent); + else + this->error_msg = ::ares_strerror(status); + + if (this->resolved4 && this->resolved6) + this->on_resolved(); +} + +void Resolver::on_hostname6_resolved(int status, struct hostent* hostent) +{ + this->resolved6 = true; + if (status == ARES_SUCCESS) + this->fill_ares_addrinfo6(hostent); + else + this->error_msg = ::ares_strerror(status); + + if (this->resolved4 && this->resolved6) + this->on_resolved(); +} + +void Resolver::on_resolved() +{ + this->resolved = true; + this->resolving = false; + if (!this->cares_addrinfo) + { + 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) +{ + std::cout << "fill_ares_addrinfo4" << this->port << std::endl; + 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* addr = new struct sockaddr_in; + addr->sin_family = hostent->h_addrtype; + addr->sin_port = htons(strtoul(this->port.data(), nullptr, 10)); + addr->sin_addr.s_addr = (*address)->s_addr; + + current->ai_addr = reinterpret_cast(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) +{ + std::cout << "fill_ares_addrinfo6" << this->port << std::endl; + 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* addr = new struct sockaddr_in6; + addr->sin6_family = hostent->h_addrtype; + addr->sin6_port = htons(strtoul(this->port.data(), nullptr, 10)); + ::memcpy(addr->sin6_addr.s6_addr, (*address)->s6_addr, 16); + addr->sin6_flowinfo = 0; + addr->sin6_scope_id = 0; + + current->ai_addr = reinterpret_cast(addr); + current->ai_next = nullptr; + current->ai_canonname = nullptr; + + current->ai_next = prev; + this->cares_addrinfo = current; + prev = current; + ++address; + } +} + +#else // ifdef CARES_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); + + this->resolved = true; + + if (res != 0) + { + this->error_msg = gai_strerror(res); + if (this->error_cb) + this->error_cb(this->error_msg.data()); + } + else + { + this->addr.reset(addr_res); + if (this->success_cb) + this->success_cb(this->addr.get()); + } +} +#endif // ifdef CARES_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 new file mode 100644 index 0000000..511afd2 --- /dev/null +++ b/louloulibs/network/resolver.hpp @@ -0,0 +1,129 @@ +#ifndef RESOLVER_HPP_INCLUDED +#define RESOLVER_HPP_INCLUDED + +#include "louloulibs.h" + +#include +#include +#include + +#include +#include +#include + +struct AddrinfoDeleter +{ + 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; + + Resolver(); + ~Resolver() = default; + + bool is_resolving() const + { +#ifdef CARES_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 CARES_FOUND + this->resolved6 = false; + this->resolved4 = false; +#endif + this->resolved = false; + this->resolving = false; + this->addr.reset(); + this->cares_addrinfo = nullptr; + this->port.clear(); + 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); +#ifdef CARES_FOUND + void on_hostname4_resolved(int status, struct hostent* hostent); + void on_hostname6_resolved(int status, struct hostent* hostent); + + void fill_ares_addrinfo4(const struct hostent* hostent); + void fill_ares_addrinfo6(const struct hostent* hostent); + + void on_resolved(); + + bool resolved4; + bool resolved6; + + 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 + /** + * 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; + + Resolver(const Resolver&) = delete; + Resolver(Resolver&&) = delete; + Resolver& operator=(const Resolver&) = delete; + Resolver& operator=(Resolver&&) = delete; +}; + +std::string addr_to_string(const struct addrinfo* rp); + +#endif /* RESOLVER_HPP_INCLUDED */ diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index 9424a1a..72abdcc 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -11,13 +11,9 @@ #include #include #include -#include #include #include -#include -#include - #ifdef BOTAN_FOUND # include # include @@ -43,24 +39,9 @@ TCPSocketHandler::TCPSocketHandler(std::shared_ptr poller): use_tls(false), connected(false), connecting(false), -#ifdef CARES_FOUND - resolving(false), - resolved(false), - resolved4(false), - resolved6(false), - cares_addrinfo(nullptr), - cares_error(), -#endif hostname_resolution_failed(false) {} -TCPSocketHandler::~TCPSocketHandler() -{ -#ifdef CARES_FOUND - this->free_cares_addrinfo(); -#endif -} - void TCPSocketHandler::init_socket(const struct addrinfo* rp) { if ((this->socket = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) == -1) @@ -91,17 +72,24 @@ void TCPSocketHandler::connect(const std::string& address, const std::string& po { // Get the addrinfo from getaddrinfo (or ares_gethostbyname), only if // this is the first call of this function. -#ifdef CARES_FOUND - if (!this->resolved) + 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->resolving = true; - DNSHandler::instance.gethostbyname(address, this, AF_INET6); - DNSHandler::instance.gethostbyname(address, this, AF_INET); + 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 @@ -109,39 +97,16 @@ void TCPSocketHandler::connect(const std::string& address, const std::string& po // 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->cares_addrinfo; + addr_res = this->resolver.get_result().get(); if (!addr_res) { this->hostname_resolution_failed = true; - const auto msg = this->cares_error; + const auto msg = this->resolver.get_error_message(); this->close(); this->on_connection_failed(msg); return ; } } -#else - log_info("Trying to connect to " << address << ":" << port); - 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; - - const int res = ::getaddrinfo(address.c_str(), port.c_str(), &hints, &addr_res); - - if (res != 0) - { - log_warning("getaddrinfo failed: "s + gai_strerror(res)); - this->hostname_resolution_failed = true; - this->close(); - this->on_connection_failed(gai_strerror(res)); - return ; - } - // Make sure the alloced structure is always freed at the end of the - // function - sg.add_callback([&addr_res](){ freeaddrinfo(addr_res); }); -#endif } else { // This function is called again, use the saved addrinfo structure, @@ -335,30 +300,18 @@ void TCPSocketHandler::close() } this->connected = false; this->connecting = false; -#ifdef CARES_FOUND - this->resolving = false; - this->resolved = false; - this->resolved4 = false; - this->resolved6 = false; - this->free_cares_addrinfo(); - this->cares_error.clear(); -#endif this->in_buf.clear(); this->out_buf.clear(); this->port.clear(); + this->resolver.clear(); } void TCPSocketHandler::display_resolved_ip(struct addrinfo* rp) const { - char buf[INET6_ADDRSTRLEN]; if (rp->ai_family == AF_INET) - log_debug("Connecting to IP address " << ::inet_ntop(rp->ai_family, - &reinterpret_cast(rp->ai_addr)->sin_addr, - buf, sizeof(buf))); + log_debug("Trying IPv4 address " << addr_to_string(rp)); else if (rp->ai_family == AF_INET6) - log_debug("Connecting to IPv6 address " << ::inet_ntop(rp->ai_family, - &reinterpret_cast(rp->ai_addr)->sin6_addr, - buf, sizeof(buf))); + log_debug("Trying IPv6 address " << addr_to_string(rp)); } void TCPSocketHandler::send_data(std::string&& data) @@ -399,11 +352,7 @@ bool TCPSocketHandler::is_connected() const bool TCPSocketHandler::is_connecting() const { -#ifdef CARES_FOUND - return this->connecting || this->resolving; -#else - return this->connecting; -#endif + return this->connecting || this->resolver.is_resolving(); } void* TCPSocketHandler::get_receive_buffer(const size_t) const @@ -506,122 +455,11 @@ void TCPSocketHandler::on_tls_activated() } void Permissive_Credentials_Manager::verify_certificate_chain(const std::string& type, - const std::string& purported_hostname, - const std::vector&) + const std::string& purported_hostname, + const std::vector&) { // TODO: Offer the admin to disallow connection on untrusted // certificates log_debug("Checking remote certificate (" << type << ") for hostname " << purported_hostname); } #endif // BOTAN_FOUND - -#ifdef CARES_FOUND - -void TCPSocketHandler::on_hostname4_resolved(int status, struct hostent* hostent) -{ - this->resolved4 = true; - if (status == ARES_SUCCESS) - this->fill_ares_addrinfo4(hostent); - else - this->cares_error = ::ares_strerror(status); - - if (this->resolved4 && this->resolved6) - { - this->resolved = true; - this->resolving = false; - this->connect(); - } -} - -void TCPSocketHandler::on_hostname6_resolved(int status, struct hostent* hostent) -{ - this->resolved6 = true; - if (status == ARES_SUCCESS) - this->fill_ares_addrinfo6(hostent); - else - this->cares_error = ::ares_strerror(status); - - if (this->resolved4 && this->resolved6) - { - this->resolved = true; - this->resolving = false; - this->connect(); - } -} - -void TCPSocketHandler::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* addr = new struct sockaddr_in; - addr->sin_family = hostent->h_addrtype; - addr->sin_port = htons(strtoul(this->port.data(), nullptr, 10)); - addr->sin_addr.s_addr = (*address)->s_addr; - - current->ai_addr = reinterpret_cast(addr); - current->ai_next = nullptr; - current->ai_canonname = nullptr; - - current->ai_next = prev; - this->cares_addrinfo = current; - prev = current; - ++address; - } -} - -void TCPSocketHandler::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* addr = new struct sockaddr_in6; - addr->sin6_family = hostent->h_addrtype; - addr->sin6_port = htons(strtoul(this->port.data(), nullptr, 10)); - ::memcpy(addr->sin6_addr.s6_addr, (*address)->s6_addr, 16); - addr->sin6_flowinfo = 0; - addr->sin6_scope_id = 0; - - current->ai_addr = reinterpret_cast(addr); - current->ai_next = nullptr; - current->ai_canonname = nullptr; - - current->ai_next = prev; - this->cares_addrinfo = current; - prev = current; - ++address; - } -} - -void TCPSocketHandler::free_cares_addrinfo() -{ - while (this->cares_addrinfo) - { - delete this->cares_addrinfo->ai_addr; - auto next = this->cares_addrinfo->ai_next; - delete this->cares_addrinfo; - this->cares_addrinfo = next; - } -} - -#endif // CARES_FOUND diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index 835c509..997d575 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -2,6 +2,7 @@ # define SOCKET_HANDLER_INCLUDED #include +#include #include #include @@ -14,10 +15,6 @@ #include "louloulibs.h" -#ifdef CARES_FOUND -# include -#endif - #ifdef BOTAN_FOUND # include # include @@ -43,7 +40,7 @@ public: class TCPSocketHandler: public SocketHandler { protected: - ~TCPSocketHandler(); + ~TCPSocketHandler() = default; public: explicit TCPSocketHandler(std::shared_ptr poller); @@ -109,16 +106,6 @@ public: bool is_connected() const override final; bool is_connecting() const; -#ifdef CARES_FOUND - void on_hostname4_resolved(int status, struct hostent* hostent); - void on_hostname6_resolved(int status, struct hostent* hostent); - - void free_cares_addrinfo(); - - void fill_ares_addrinfo4(const struct hostent* hostent); - void fill_ares_addrinfo6(const struct hostent* hostent); -#endif - private: /** * Initialize the socket with the parameters contained in the given @@ -194,9 +181,13 @@ private: */ std::list out_buf; /** - * Keep the details of the addrinfo that triggered a EINPROGRESS error when - * connect()ing to it, to reuse it directly when connect() is called - * again. + * 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; @@ -234,28 +225,6 @@ protected: bool connected; bool connecting; -#ifdef CARES_FOUND - bool resolving; - /** - * Whether or not the DNS resolution was successfully done - */ - bool resolved; - bool resolved4; - bool resolved6; - /** - * When using c-ares to resolve the host asynchronously, we need the - * c-ares callback 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 next call of connect() will then try all these values - * (exactly like we do with the result of getaddrinfo) and save the one - * that worked (or returned EINPROGRESS) in the other struct addrinfo (see - * the members addrinfo, ai_addrlen, and ai_addr). - */ - struct addrinfo* cares_addrinfo; - std::string cares_error; -#endif // CARES_FOUND - bool hostname_resolution_failed; private: -- cgit v1.2.3 From ec53fc931533a66872c9dd49b8e11aa084caf75f Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 26 Oct 2015 02:34:55 +0100 Subject: Remove some debug thing that should not have been committed --- louloulibs/network/resolver.cpp | 4 ---- 1 file changed, 4 deletions(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/resolver.cpp b/louloulibs/network/resolver.cpp index bfd75d7..5324191 100644 --- a/louloulibs/network/resolver.cpp +++ b/louloulibs/network/resolver.cpp @@ -3,9 +3,6 @@ #include #include -// remove me -#include - using namespace std::string_literals; Resolver::Resolver(): @@ -34,7 +31,6 @@ void Resolver::resolve(const std::string& hostname, const std::string& port, #ifdef CARES_FOUND void Resolver::start_resolving(const std::string& hostname, const std::string& port) { - std::cout << "start_resolving: " << hostname << port << std::endl; this->resolving = true; this->resolved = false; this->resolved4 = false; -- cgit v1.2.3 From f57a43f33767082e574d6cb74480d09c80e72e64 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 26 Oct 2015 02:43:33 +0100 Subject: Remove some more debug thing --- louloulibs/network/resolver.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/resolver.cpp b/louloulibs/network/resolver.cpp index 5324191..8028c5a 100644 --- a/louloulibs/network/resolver.cpp +++ b/louloulibs/network/resolver.cpp @@ -101,7 +101,6 @@ void Resolver::on_resolved() void Resolver::fill_ares_addrinfo4(const struct hostent* hostent) { - std::cout << "fill_ares_addrinfo4" << this->port << std::endl; struct addrinfo* prev = this->cares_addrinfo; struct in_addr** address = reinterpret_cast(hostent->h_addr_list); @@ -133,7 +132,6 @@ void Resolver::fill_ares_addrinfo4(const struct hostent* hostent) void Resolver::fill_ares_addrinfo6(const struct hostent* hostent) { - std::cout << "fill_ares_addrinfo6" << this->port << std::endl; struct addrinfo* prev = this->cares_addrinfo; struct in6_addr** address = reinterpret_cast(hostent->h_addr_list); -- cgit v1.2.3 From c8606e1858363f0f9b8f9cd8ac8b96dfe181fd31 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 26 Oct 2015 02:45:20 +0100 Subject: Remove a unused_parameter warning --- 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 8028c5a..0150fc8 100644 --- a/louloulibs/network/resolver.cpp +++ b/louloulibs/network/resolver.cpp @@ -29,7 +29,7 @@ void Resolver::resolve(const std::string& hostname, const std::string& port, } #ifdef CARES_FOUND -void Resolver::start_resolving(const std::string& hostname, const std::string& port) +void Resolver::start_resolving(const std::string& hostname, const std::string&) { this->resolving = true; this->resolved = false; -- cgit v1.2.3 From 0fcba76c793e7c0b1ccc09eaa85016caa11ca3f2 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 26 Oct 2015 19:44:31 +0100 Subject: Fix a build issue when compiling without cares --- louloulibs/network/resolver.cpp | 2 ++ louloulibs/network/resolver.hpp | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/resolver.cpp b/louloulibs/network/resolver.cpp index 0150fc8..cb6c445 100644 --- a/louloulibs/network/resolver.cpp +++ b/louloulibs/network/resolver.cpp @@ -23,7 +23,9 @@ void Resolver::resolve(const std::string& hostname, const std::string& port, { this->error_cb = error_cb; this->success_cb = success_cb; +#ifdef CARES_FOUND this->port = port; +#endif this->start_resolving(hostname, port); } diff --git a/louloulibs/network/resolver.hpp b/louloulibs/network/resolver.hpp index 511afd2..f9e9134 100644 --- a/louloulibs/network/resolver.hpp +++ b/louloulibs/network/resolver.hpp @@ -66,12 +66,12 @@ public: #ifdef CARES_FOUND this->resolved6 = false; this->resolved4 = false; -#endif - this->resolved = false; this->resolving = false; - this->addr.reset(); this->cares_addrinfo = nullptr; this->port.clear(); +#endif + this->resolved = false; + this->addr.reset(); this->error_msg.clear(); } -- cgit v1.2.3 From 142516a69bb000ce80cbb6509d1f407438a94663 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 26 Oct 2015 20:09:39 +0100 Subject: Fix some trivial issues reported by cppcheck --- louloulibs/network/tcp_socket_handler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index 72abdcc..f2a2466 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -247,7 +247,7 @@ void TCPSocketHandler::on_send() struct msghdr msg{nullptr, 0, msg_iov, 0, nullptr, 0, 0}; - for (std::string& s: this->out_buf) + 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()); -- cgit v1.2.3 From ccd185d3f59b545e018bf3c50280e401a3715ab4 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Sat, 31 Oct 2015 06:18:29 +0100 Subject: Remove a useless assignment --- louloulibs/network/resolver.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/resolver.cpp b/louloulibs/network/resolver.cpp index cb6c445..9d6de23 100644 --- a/louloulibs/network/resolver.cpp +++ b/louloulibs/network/resolver.cpp @@ -155,7 +155,6 @@ void Resolver::fill_ares_addrinfo6(const struct hostent* hostent) addr->sin6_scope_id = 0; current->ai_addr = reinterpret_cast(addr); - current->ai_next = nullptr; current->ai_canonname = nullptr; current->ai_next = prev; -- cgit v1.2.3 From f928f7627247ceaafcf3538066ac17609b652aac Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 2 Nov 2015 03:26:13 +0100 Subject: Verify the remote TLS certificates using the system-wide trusted CAs --- louloulibs/network/credentials_manager.cpp | 33 ++++++++++++++++++++++++++++++ louloulibs/network/credentials_manager.hpp | 22 ++++++++++++++++++++ louloulibs/network/tcp_socket_handler.cpp | 12 ++--------- louloulibs/network/tcp_socket_handler.hpp | 24 ++++++---------------- 4 files changed, 63 insertions(+), 28 deletions(-) create mode 100644 louloulibs/network/credentials_manager.cpp create mode 100644 louloulibs/network/credentials_manager.hpp (limited to 'louloulibs/network') diff --git a/louloulibs/network/credentials_manager.cpp b/louloulibs/network/credentials_manager.cpp new file mode 100644 index 0000000..77198a4 --- /dev/null +++ b/louloulibs/network/credentials_manager.cpp @@ -0,0 +1,33 @@ +#include +#include + +Basic_Credentials_Manager::Basic_Credentials_Manager(): + Botan::Credentials_Manager() +{ + this->load_certs(); +} +void Basic_Credentials_Manager::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); + Botan::Credentials_Manager::verify_certificate_chain(type, "louiz.org", certs); + log_debug("Certificate is valid"); +} +void Basic_Credentials_Manager::load_certs() +{ + const std::vector paths = {"/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem"}; + for (const auto& path: paths) + { + Botan::DataSource_Stream bundle(path); + while (!bundle.end_of_data() && bundle.check_available(27)) + { + const Botan::X509_Certificate cert(bundle); + this->certificate_store.add_certificate(cert); + } + } +} +std::vector Basic_Credentials_Manager::trusted_certificate_authorities(const std::string&, const std::string&) +{ + return {&this->certificate_store}; +} diff --git a/louloulibs/network/credentials_manager.hpp b/louloulibs/network/credentials_manager.hpp new file mode 100644 index 0000000..ea89eca --- /dev/null +++ b/louloulibs/network/credentials_manager.hpp @@ -0,0 +1,22 @@ +#ifndef BIBOUMI_CREDENTIALS_MANAGER_HPP +#define BIBOUMI_CREDENTIALS_MANAGER_HPP + +#include +#include + +class Basic_Credentials_Manager: public Botan::Credentials_Manager +{ +public: + Basic_Credentials_Manager(); + void verify_certificate_chain(const std::string& type, + const std::string& purported_hostname, + const std::vector&) override final; + std::vector trusted_certificate_authorities(const std::string& type, + const std::string& context) override final; + +private: + void load_certs(); + Botan::Certificate_Store_In_Memory certificate_store; +}; + +#endif //BIBOUMI_CREDENTIALS_MANAGER_HPP diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index f2a2466..81a36ef 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -19,7 +19,7 @@ # include Botan::AutoSeeded_RNG TCPSocketHandler::rng; -Permissive_Credentials_Manager TCPSocketHandler::credential_manager; +Basic_Credentials_Manager TCPSocketHandler::credential_manager; Botan::TLS::Policy TCPSocketHandler::policy; Botan::TLS::Session_Manager_In_Memory TCPSocketHandler::session_manager(TCPSocketHandler::rng); @@ -451,15 +451,7 @@ bool TCPSocketHandler::tls_handshake_cb(const Botan::TLS::Session& session) void TCPSocketHandler::on_tls_activated() { - this->send_data(""); -} - -void Permissive_Credentials_Manager::verify_certificate_chain(const std::string& type, - const std::string& purported_hostname, - const std::vector&) -{ // TODO: Offer the admin to disallow connection on untrusted - // certificates - log_debug("Checking remote certificate (" << type << ") for hostname " << purported_hostname); + this->send_data({}); } #endif // BOTAN_FOUND diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index 997d575..d173c1f 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -1,9 +1,13 @@ #ifndef SOCKET_HANDLER_INCLUDED # define SOCKET_HANDLER_INCLUDED +#include "louloulibs.h" + #include #include +#include + #include #include #include @@ -13,23 +17,6 @@ #include #include -#include "louloulibs.h" - -#ifdef BOTAN_FOUND -# include -# include - -/** - * A very simple credential manager that accepts any certificate. - */ -class Permissive_Credentials_Manager: public Botan::Credentials_Manager -{ -public: - void verify_certificate_chain(const std::string& type, - const std::string& purported_hostname, - const std::vector&); -}; -#endif // BOTAN_FOUND /** * An interface, with a series of callbacks that should be implemented in @@ -243,7 +230,7 @@ private: * Botan stuff to manipulate a TLS session. */ static Botan::AutoSeeded_RNG rng; - static Permissive_Credentials_Manager credential_manager; + static Basic_Credentials_Manager credential_manager; static Botan::TLS::Policy policy; static Botan::TLS::Session_Manager_In_Memory session_manager; /** @@ -267,3 +254,4 @@ private: }; #endif // SOCKET_HANDLER_INCLUDED + -- cgit v1.2.3 From 4a51850173e86f3682e9220704c9b17c09091222 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 2 Nov 2015 17:15:32 +0100 Subject: Verify TLS certificate with the given hostname, instead of a hardcoded one --- louloulibs/network/credentials_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/credentials_manager.cpp b/louloulibs/network/credentials_manager.cpp index 77198a4..8608f90 100644 --- a/louloulibs/network/credentials_manager.cpp +++ b/louloulibs/network/credentials_manager.cpp @@ -11,7 +11,7 @@ void Basic_Credentials_Manager::verify_certificate_chain(const std::string& type const std::vector& certs) { log_debug("Checking remote certificate (" << type << ") for hostname " << purported_hostname); - Botan::Credentials_Manager::verify_certificate_chain(type, "louiz.org", certs); + Botan::Credentials_Manager::verify_certificate_chain(type, purported_hostname, certs); log_debug("Certificate is valid"); } void Basic_Credentials_Manager::load_certs() -- cgit v1.2.3 From 06db9b366a83121e0c914e527a367f90ec71940a Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 2 Nov 2015 17:31:35 +0100 Subject: Fix the build without botan, caused by credentials_manager --- louloulibs/network/credentials_manager.cpp | 6 ++++++ louloulibs/network/credentials_manager.hpp | 5 +++++ 2 files changed, 11 insertions(+) (limited to 'louloulibs/network') diff --git a/louloulibs/network/credentials_manager.cpp b/louloulibs/network/credentials_manager.cpp index 8608f90..7c13319 100644 --- a/louloulibs/network/credentials_manager.cpp +++ b/louloulibs/network/credentials_manager.cpp @@ -1,5 +1,9 @@ +#include "louloulibs.h" + +#ifdef BOTAN_FOUND #include #include +#include Basic_Credentials_Manager::Basic_Credentials_Manager(): Botan::Credentials_Manager() @@ -31,3 +35,5 @@ std::vector Basic_Credentials_Manager::trusted_certif { return {&this->certificate_store}; } + +#endif diff --git a/louloulibs/network/credentials_manager.hpp b/louloulibs/network/credentials_manager.hpp index ea89eca..8641f1d 100644 --- a/louloulibs/network/credentials_manager.hpp +++ b/louloulibs/network/credentials_manager.hpp @@ -1,6 +1,10 @@ #ifndef BIBOUMI_CREDENTIALS_MANAGER_HPP #define BIBOUMI_CREDENTIALS_MANAGER_HPP +#include "louloulibs.h" + +#ifdef BOTAN_FOUND + #include #include @@ -19,4 +23,5 @@ private: Botan::Certificate_Store_In_Memory certificate_store; }; +#endif //BOTAN_FOUND #endif //BIBOUMI_CREDENTIALS_MANAGER_HPP -- cgit v1.2.3 From e8386bd14e9783f0bef39bdf577545522e33e719 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Tue, 3 Nov 2015 16:56:38 +0100 Subject: Provide an adhoc option to let user pass the cert verif for some IRC servers --- louloulibs/network/credentials_manager.cpp | 35 +++++++++++++++++++++++++----- louloulibs/network/credentials_manager.hpp | 11 +++++++--- louloulibs/network/tcp_socket_handler.cpp | 6 +++-- louloulibs/network/tcp_socket_handler.hpp | 12 +++++++++- 4 files changed, 53 insertions(+), 11 deletions(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/credentials_manager.cpp b/louloulibs/network/credentials_manager.cpp index 7c13319..b9a9af8 100644 --- a/louloulibs/network/credentials_manager.cpp +++ b/louloulibs/network/credentials_manager.cpp @@ -1,25 +1,48 @@ #include "louloulibs.h" #ifdef BOTAN_FOUND +#include #include #include #include -Basic_Credentials_Manager::Basic_Credentials_Manager(): - Botan::Credentials_Manager() +#ifdef USE_DATABASE +# include +#endif + +Botan::Certificate_Store_In_Memory Basic_Credentials_Manager::certificate_store; +bool Basic_Credentials_Manager::certs_loaded = false; + +Basic_Credentials_Manager::Basic_Credentials_Manager(const TCPSocketHandler* const socket_handler): + Botan::Credentials_Manager(), + socket_handler(socket_handler) { this->load_certs(); } + void Basic_Credentials_Manager::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); - Botan::Credentials_Manager::verify_certificate_chain(type, purported_hostname, certs); - log_debug("Certificate is valid"); + 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()); + if (this->socket_handler->abort_on_invalid_cert()) + throw; + } } + void Basic_Credentials_Manager::load_certs() { + // Only load the certificates the first time + if (Basic_Credentials_Manager::certs_loaded) + return; const std::vector paths = {"/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem"}; for (const auto& path: paths) { @@ -27,10 +50,12 @@ void Basic_Credentials_Manager::load_certs() while (!bundle.end_of_data() && bundle.check_available(27)) { const Botan::X509_Certificate cert(bundle); - this->certificate_store.add_certificate(cert); + Basic_Credentials_Manager::certificate_store.add_certificate(cert); } } + Basic_Credentials_Manager::certs_loaded = true; } + std::vector Basic_Credentials_Manager::trusted_certificate_authorities(const std::string&, const std::string&) { return {&this->certificate_store}; diff --git a/louloulibs/network/credentials_manager.hpp b/louloulibs/network/credentials_manager.hpp index 8641f1d..e292321 100644 --- a/louloulibs/network/credentials_manager.hpp +++ b/louloulibs/network/credentials_manager.hpp @@ -8,10 +8,12 @@ #include #include +class TCPSocketHandler; + class Basic_Credentials_Manager: public Botan::Credentials_Manager { public: - Basic_Credentials_Manager(); + Basic_Credentials_Manager(const TCPSocketHandler* const socket_handler); void verify_certificate_chain(const std::string& type, const std::string& purported_hostname, const std::vector&) override final; @@ -19,8 +21,11 @@ public: const std::string& context) override final; private: - void load_certs(); - Botan::Certificate_Store_In_Memory certificate_store; + const TCPSocketHandler* const socket_handler; + + static void load_certs(); + static Botan::Certificate_Store_In_Memory certificate_store; + static bool certs_loaded; }; #endif //BOTAN_FOUND diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index 81a36ef..0ed74a2 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -19,7 +19,6 @@ # include Botan::AutoSeeded_RNG TCPSocketHandler::rng; -Basic_Credentials_Manager TCPSocketHandler::credential_manager; Botan::TLS::Policy TCPSocketHandler::policy; Botan::TLS::Session_Manager_In_Memory TCPSocketHandler::session_manager(TCPSocketHandler::rng); @@ -40,6 +39,9 @@ TCPSocketHandler::TCPSocketHandler(std::shared_ptr poller): connected(false), connecting(false), hostname_resolution_failed(false) +#ifdef BOTAN_FOUND + ,credential_manager(this) +#endif {} void TCPSocketHandler::init_socket(const struct addrinfo* rp) @@ -369,7 +371,7 @@ void TCPSocketHandler::start_tls() 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), - session_manager, credential_manager, policy, + session_manager, this->credential_manager, policy, rng, server_info, Botan::TLS::Protocol_Version::latest_tls_version()); } diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index d173c1f..213e286 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -90,6 +90,16 @@ public: * The size argument is the size of the last chunk of data that was added to the 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_connected() const override final; bool is_connecting() const; @@ -230,9 +240,9 @@ private: * Botan stuff to manipulate a TLS session. */ static Botan::AutoSeeded_RNG rng; - static Basic_Credentials_Manager credential_manager; static Botan::TLS::Policy policy; static Botan::TLS::Session_Manager_In_Memory session_manager; + Basic_Credentials_Manager credential_manager; /** * 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 -- cgit v1.2.3 From 5ce9d3f1429228746fcee724a44860f16ad166f5 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Thu, 5 Nov 2015 02:16:21 +0100 Subject: Make the CA file configurable --- louloulibs/network/credentials_manager.cpp | 39 ++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 5 deletions(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/credentials_manager.cpp b/louloulibs/network/credentials_manager.cpp index b9a9af8..57100ee 100644 --- a/louloulibs/network/credentials_manager.cpp +++ b/louloulibs/network/credentials_manager.cpp @@ -5,11 +5,22 @@ #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 Basic_Credentials_Manager::certificate_store; bool Basic_Credentials_Manager::certs_loaded = false; @@ -43,16 +54,34 @@ void Basic_Credentials_Manager::load_certs() // Only load the certificates the first time if (Basic_Credentials_Manager::certs_loaded) return; - const std::vector paths = {"/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem"}; + 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); for (const auto& path: paths) { - Botan::DataSource_Stream bundle(path); - while (!bundle.end_of_data() && bundle.check_available(27)) + try + { + Botan::DataSource_Stream bundle(path); + log_debug("Using ca bundle: " << path); + while (!bundle.end_of_data() && bundle.check_available(27)) + { + const Botan::X509_Certificate cert(bundle); + Basic_Credentials_Manager::certificate_store.add_certificate(cert); + } + // Only use the first file that can successfully be read. + goto success; + } + catch (Botan::Stream_IO_Error& e) { - const Botan::X509_Certificate cert(bundle); - Basic_Credentials_Manager::certificate_store.add_certificate(cert); + log_debug(e.what()); } } + // If we could not open one of the files, print a warning + log_warning("The CA could not be loaded, TLS negociation will probably fail."); + success: Basic_Credentials_Manager::certs_loaded = true; } -- cgit v1.2.3 From 0620c8e589c713fa0f0d0f66a4c4d203e8f3f87f Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Sat, 7 Nov 2015 23:41:06 +0100 Subject: Avoid leaking socket filedescriptors When trying the various results of getaddrinfo, we forgot to close the socket when one fails, before trying the next one. Also use the destructor to make sure we do not have some other unrelated leak. --- louloulibs/network/tcp_socket_handler.cpp | 8 ++++++++ louloulibs/network/tcp_socket_handler.hpp | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index 0ed74a2..d5c0dfa 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -44,8 +44,16 @@ TCPSocketHandler::TCPSocketHandler(std::shared_ptr poller): #endif {} +TCPSocketHandler::~TCPSocketHandler() +{ + this->close(); +} + + void TCPSocketHandler::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)); int optval = 1; diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index 213e286..d33b919 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -27,7 +27,7 @@ class TCPSocketHandler: public SocketHandler { protected: - ~TCPSocketHandler() = default; + ~TCPSocketHandler(); public: explicit TCPSocketHandler(std::shared_ptr poller); -- cgit v1.2.3 From 296a16fa75da5900395a7cf70f63a6ff266ef0cc Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 30 Nov 2015 03:20:51 +0100 Subject: Do not segfault when trying to send TLS data over a not-yet connected socket --- louloulibs/network/tcp_socket_handler.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index d5c0dfa..78efdce 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -410,7 +410,9 @@ void TCPSocketHandler::tls_recv() void TCPSocketHandler::tls_send(std::string&& data) { - if (this->tls->is_active()) + // 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()) -- cgit v1.2.3 From 241768836ddfb9e3609f987224cd821058fcc948 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Tue, 1 Dec 2015 16:08:40 +0100 Subject: Add the outgoing_bind option Lets the admin choose a local address to bind each outgoing (IRC) socket. --- louloulibs/network/tcp_socket_handler.cpp | 28 ++++++++++++++++++++++++++++ louloulibs/network/tcp_socket_handler.hpp | 6 ++++++ 2 files changed, 34 insertions(+) (limited to 'louloulibs/network') diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index 78efdce..e6901c8 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -56,6 +56,34 @@ void TCPSocketHandler::init_socket(const struct addrinfo* rp) ::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) + log_error("Failed to bind socket to " << this->bind_addr << ": " + << gai_strerror(err)); + else + { + struct addrinfo* rp; + int bind_error; + 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(bind_error)); + 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)); diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index d33b919..9f5caa3 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -224,6 +224,12 @@ 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; + private: TCPSocketHandler(const TCPSocketHandler&) = delete; TCPSocketHandler(TCPSocketHandler&&) = delete; -- cgit v1.2.3 From 05a82b810f09d5cf3a0388fc4eeb523d33662c9c Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Tue, 1 Dec 2015 16:09:21 +0100 Subject: Display a better error when connection fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The error should not tell “while reading” when we are just connecting. --- louloulibs/network/tcp_socket_handler.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index e6901c8..83863b0 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -266,7 +266,10 @@ ssize_t TCPSocketHandler::do_recv(void* recv_buf, const size_t buf_size) } else if (-1 == size) { - log_warning("Error while reading from socket: " << strerror(errno)); + if (this->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; -- cgit v1.2.3 From 6a7cd5eecfa4df0552969e5d5d57452d290b7fc5 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Thu, 3 Dec 2015 20:41:48 +0100 Subject: Do not forget to call freeaddrinfo, fix a memleak For the getaddrinfo call we added in the previous commit, to convert the IP provided in the conf. --- louloulibs/network/tcp_socket_handler.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'louloulibs/network') diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index 83863b0..6ed981c 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -68,6 +68,7 @@ void TCPSocketHandler::init_socket(const struct addrinfo* rp) << gai_strerror(err)); else { + utils::ScopeGuard sg([result](){ freeaddrinfo(result); }); struct addrinfo* rp; int bind_error; for (rp = result; rp; rp = rp->ai_next) -- cgit v1.2.3 From 1f8333f23f060750673d0b7c573f2e2d12142adf Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Wed, 30 Dec 2015 21:34:11 +0100 Subject: Support a trusted SHA1 fingerprint to be configured for each IRC server --- louloulibs/network/credentials_manager.cpp | 15 ++++++++++++++- louloulibs/network/credentials_manager.hpp | 2 ++ louloulibs/network/tcp_socket_handler.hpp | 2 ++ 3 files changed, 18 insertions(+), 1 deletion(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/credentials_manager.cpp b/louloulibs/network/credentials_manager.cpp index 57100ee..f92aef8 100644 --- a/louloulibs/network/credentials_manager.cpp +++ b/louloulibs/network/credentials_manager.cpp @@ -26,11 +26,17 @@ bool Basic_Credentials_Manager::certs_loaded = false; Basic_Credentials_Manager::Basic_Credentials_Manager(const TCPSocketHandler* const socket_handler): Botan::Credentials_Manager(), - socket_handler(socket_handler) + socket_handler(socket_handler), + trusted_fingerprint{} { this->load_certs(); } +void Basic_Credentials_Manager::set_trusted_fingerprint(const std::string& fingerprint) +{ + this->trusted_fingerprint = fingerprint; +} + void Basic_Credentials_Manager::verify_certificate_chain(const std::string& type, const std::string& purported_hostname, const std::vector& certs) @@ -44,6 +50,13 @@ void Basic_Credentials_Manager::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; + if (this->socket_handler->abort_on_invalid_cert()) throw; } diff --git a/louloulibs/network/credentials_manager.hpp b/louloulibs/network/credentials_manager.hpp index e292321..dbd1d95 100644 --- a/louloulibs/network/credentials_manager.hpp +++ b/louloulibs/network/credentials_manager.hpp @@ -19,6 +19,7 @@ public: const std::vector&) override final; std::vector trusted_certificate_authorities(const std::string& type, const std::string& context) override final; + void set_trusted_fingerprint(const std::string& fingerprint); private: const TCPSocketHandler* const socket_handler; @@ -26,6 +27,7 @@ private: 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/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index 9f5caa3..fb4195d 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -248,7 +248,9 @@ private: static Botan::AutoSeeded_RNG rng; static Botan::TLS::Policy policy; static Botan::TLS::Session_Manager_In_Memory session_manager; +protected: Basic_Credentials_Manager 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 -- cgit v1.2.3 From 0864f2ab99073b62e4173da694a13aa4ccc8b554 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Thu, 14 Jan 2016 00:39:08 +0100 Subject: Quiet a warning --- louloulibs/network/tcp_socket_handler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index 6ed981c..81369dd 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -63,14 +63,14 @@ void TCPSocketHandler::init_socket(const struct addrinfo* rp) // used in bind() struct addrinfo* result; int err = ::getaddrinfo(this->bind_addr.data(), nullptr, nullptr, &result); - if (err != 0) + 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; + int bind_error = 0; for (rp = result; rp; rp = rp->ai_next) { if ((bind_error = ::bind(this->socket, -- cgit v1.2.3 From a13285d0ff360d0a83e007a776d9efbcfc347c76 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Wed, 10 Feb 2016 20:10:40 +0100 Subject: Rename BasicCredentialManager --- louloulibs/network/credentials_manager.cpp | 24 ++++++++++++------------ louloulibs/network/credentials_manager.hpp | 4 ++-- louloulibs/network/tcp_socket_handler.hpp | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/credentials_manager.cpp b/louloulibs/network/credentials_manager.cpp index f92aef8..d5b0eb3 100644 --- a/louloulibs/network/credentials_manager.cpp +++ b/louloulibs/network/credentials_manager.cpp @@ -21,10 +21,10 @@ static const std::vector default_cert_files = { "/etc/ca-certificates/extracted/tls-ca-bundle.pem" }; -Botan::Certificate_Store_In_Memory Basic_Credentials_Manager::certificate_store; -bool Basic_Credentials_Manager::certs_loaded = false; +Botan::Certificate_Store_In_Memory BasicCredentialsManager::certificate_store; +bool BasicCredentialsManager::certs_loaded = false; -Basic_Credentials_Manager::Basic_Credentials_Manager(const TCPSocketHandler* const socket_handler): +BasicCredentialsManager::BasicCredentialsManager(const TCPSocketHandler* const socket_handler): Botan::Credentials_Manager(), socket_handler(socket_handler), trusted_fingerprint{} @@ -32,14 +32,14 @@ Basic_Credentials_Manager::Basic_Credentials_Manager(const TCPSocketHandler* con this->load_certs(); } -void Basic_Credentials_Manager::set_trusted_fingerprint(const std::string& fingerprint) +void BasicCredentialsManager::set_trusted_fingerprint(const std::string& fingerprint) { this->trusted_fingerprint = fingerprint; } -void Basic_Credentials_Manager::verify_certificate_chain(const std::string& type, - const std::string& purported_hostname, - const std::vector& certs) +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 @@ -62,10 +62,10 @@ void Basic_Credentials_Manager::verify_certificate_chain(const std::string& type } } -void Basic_Credentials_Manager::load_certs() +void BasicCredentialsManager::load_certs() { // Only load the certificates the first time - if (Basic_Credentials_Manager::certs_loaded) + if (BasicCredentialsManager::certs_loaded) return; const std::string conf_path = Config::get("ca_file", ""); std::vector paths; @@ -82,7 +82,7 @@ void Basic_Credentials_Manager::load_certs() while (!bundle.end_of_data() && bundle.check_available(27)) { const Botan::X509_Certificate cert(bundle); - Basic_Credentials_Manager::certificate_store.add_certificate(cert); + BasicCredentialsManager::certificate_store.add_certificate(cert); } // Only use the first file that can successfully be read. goto success; @@ -95,10 +95,10 @@ void Basic_Credentials_Manager::load_certs() // If we could not open one of the files, print a warning log_warning("The CA could not be loaded, TLS negociation will probably fail."); success: - Basic_Credentials_Manager::certs_loaded = true; + BasicCredentialsManager::certs_loaded = true; } -std::vector Basic_Credentials_Manager::trusted_certificate_authorities(const std::string&, const std::string&) +std::vector BasicCredentialsManager::trusted_certificate_authorities(const std::string&, const std::string&) { return {&this->certificate_store}; } diff --git a/louloulibs/network/credentials_manager.hpp b/louloulibs/network/credentials_manager.hpp index dbd1d95..153c4d6 100644 --- a/louloulibs/network/credentials_manager.hpp +++ b/louloulibs/network/credentials_manager.hpp @@ -10,10 +10,10 @@ class TCPSocketHandler; -class Basic_Credentials_Manager: public Botan::Credentials_Manager +class BasicCredentialsManager: public Botan::Credentials_Manager { public: - Basic_Credentials_Manager(const TCPSocketHandler* const socket_handler); + BasicCredentialsManager(const TCPSocketHandler* const socket_handler); void verify_certificate_chain(const std::string& type, const std::string& purported_hostname, const std::vector&) override final; diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index fb4195d..62b2ce7 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -249,7 +249,7 @@ private: static Botan::TLS::Policy policy; static Botan::TLS::Session_Manager_In_Memory session_manager; protected: - Basic_Credentials_Manager credential_manager; + BasicCredentialsManager credential_manager; private: /** * We use a unique_ptr because we may not want to create the object at -- cgit v1.2.3 From 6ee80ad26858eeee9a99b448320025ca439eee55 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 22 Feb 2016 16:56:13 +0100 Subject: Fix the ordering of poll callbacks (recv, connect, send) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because if we have a send event to signal the connection sucess + a recv event to signal something to read on the socket, we need to first finish the connect process before reading the available data. That’s what we do now. --- louloulibs/network/poller.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/poller.cpp b/louloulibs/network/poller.cpp index 329e1c8..50e9806 100644 --- a/louloulibs/network/poller.cpp +++ b/louloulibs/network/poller.cpp @@ -205,15 +205,12 @@ int Poller::poll(const std::chrono::milliseconds& timeout) for (int i = 0; i < nb_events; ++i) { auto socket_handler = static_cast(revents[i].data.ptr); - if (revents[i].events & EPOLLIN) + 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) - { - if (socket_handler->is_connected()) - socket_handler->on_send(); - else - socket_handler->connect(); - } + socket_handler->connect(); } return nb_events; #endif -- cgit v1.2.3 From b29d94444d176604f0847985f3ecf57091d4a47b Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Wed, 24 Feb 2016 09:28:59 +0100 Subject: Close the epoll fd --- louloulibs/network/poller.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'louloulibs/network') diff --git a/louloulibs/network/poller.cpp b/louloulibs/network/poller.cpp index 50e9806..0559644 100644 --- a/louloulibs/network/poller.cpp +++ b/louloulibs/network/poller.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -27,6 +28,8 @@ Poller::Poller() Poller::~Poller() { + if (this->epfd > 0) + ::close(this->epfd); } void Poller::add_socket_handler(SocketHandler* socket_handler) -- cgit v1.2.3 From b47179f1256f2a3fe6ce0d8f30eceb0f91b33b2e Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Wed, 24 Feb 2016 09:29:28 +0100 Subject: Fix the ordering of poll callbacks, with ppoll too --- louloulibs/network/poller.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/poller.cpp b/louloulibs/network/poller.cpp index 0559644..26e5a8f 100644 --- a/louloulibs/network/poller.cpp +++ b/louloulibs/network/poller.cpp @@ -171,21 +171,22 @@ int Poller::poll(const std::chrono::milliseconds& timeout) 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) + else if (this->fds[i].revents & POLLIN && socket_handler->is_connected()) { - auto socket_handler = this->socket_handlers.at(this->fds[i].fd); socket_handler->on_recv(); nb_events--; } - else if (this->fds[i].revents & POLLOUT) + else if (this->fds[i].revents & POLLOUT && socket_handler->is_connected()) { - auto socket_handler = this->socket_handlers.at(this->fds[i].fd); - if (socket_handler->is_connected()) socket_handler->on_send(); - else - socket_handler->connect(); + nb_events--; + } + else if (this->fds[i].revents & POLLOUT) + { + socket_handler->connect(); nb_events--; } } -- cgit v1.2.3 From 73ad709ddf5db77e721b7080da64603aab10de17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Mon, 11 Apr 2016 17:17:48 +0200 Subject: Add a TEMPORARY work-around for botan 1.11.29 --- louloulibs/network/credentials_manager.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/credentials_manager.cpp b/louloulibs/network/credentials_manager.cpp index d5b0eb3..c5b8493 100644 --- a/louloulibs/network/credentials_manager.cpp +++ b/louloulibs/network/credentials_manager.cpp @@ -81,8 +81,18 @@ void BasicCredentialsManager::load_certs() log_debug("Using ca bundle: " << path); while (!bundle.end_of_data() && bundle.check_available(27)) { - const Botan::X509_Certificate cert(bundle); - BasicCredentialsManager::certificate_store.add_certificate(cert); + // 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 { + const Botan::X509_Certificate cert(bundle); + BasicCredentialsManager::certificate_store.add_certificate(cert); + } catch (const Botan::Decoding_Error& error) + { + continue; + } } // Only use the first file that can successfully be read. goto success; -- cgit v1.2.3 From af42073830087d97385e507f27f601e8769541b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 4 May 2016 14:16:40 +0200 Subject: Style fix Move all constructors at the top of classes --- louloulibs/network/credentials_manager.hpp | 6 ++++++ louloulibs/network/dns_handler.cpp | 4 +++- louloulibs/network/dns_handler.hpp | 10 +++++----- louloulibs/network/dns_socket_handler.cpp | 4 ---- louloulibs/network/dns_socket_handler.hpp | 11 ++++++----- louloulibs/network/poller.hpp | 9 ++++----- louloulibs/network/resolver.hpp | 9 ++++----- louloulibs/network/socket_handler.hpp | 11 +++++------ louloulibs/network/tcp_socket_handler.hpp | 11 +++++------ 9 files changed, 38 insertions(+), 37 deletions(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/credentials_manager.hpp b/louloulibs/network/credentials_manager.hpp index 153c4d6..c0d23ae 100644 --- a/louloulibs/network/credentials_manager.hpp +++ b/louloulibs/network/credentials_manager.hpp @@ -14,6 +14,12 @@ 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; + void verify_certificate_chain(const std::string& type, const std::string& purported_hostname, const std::vector&) override final; diff --git a/louloulibs/network/dns_handler.cpp b/louloulibs/network/dns_handler.cpp index a8d0f9c..5e19f8c 100644 --- a/louloulibs/network/dns_handler.cpp +++ b/louloulibs/network/dns_handler.cpp @@ -13,7 +13,9 @@ DNSHandler DNSHandler::instance; using namespace std::string_literals; -DNSHandler::DNSHandler() +DNSHandler::DNSHandler(): + socket_handlers{}, + channel{nullptr} { int ares_error; if ((ares_error = ::ares_library_init(ARES_LIB_INIT_ALL)) != 0) diff --git a/louloulibs/network/dns_handler.hpp b/louloulibs/network/dns_handler.hpp index 61e2302..d2b48d2 100644 --- a/louloulibs/network/dns_handler.hpp +++ b/louloulibs/network/dns_handler.hpp @@ -24,6 +24,11 @@ class DNSHandler public: DNSHandler(); ~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); /** @@ -48,11 +53,6 @@ private: */ std::vector> socket_handlers; ares_channel channel; - - DNSHandler(const DNSHandler&) = delete; - DNSHandler(DNSHandler&&) = delete; - DNSHandler& operator=(const DNSHandler&) = delete; - DNSHandler& operator=(DNSHandler&&) = delete; }; #endif /* CARES_FOUND */ diff --git a/louloulibs/network/dns_socket_handler.cpp b/louloulibs/network/dns_socket_handler.cpp index 124c9b2..faaabdb 100644 --- a/louloulibs/network/dns_socket_handler.cpp +++ b/louloulibs/network/dns_socket_handler.cpp @@ -13,10 +13,6 @@ DNSSocketHandler::DNSSocketHandler(std::shared_ptr poller, { } -DNSSocketHandler::~DNSSocketHandler() -{ -} - void DNSSocketHandler::connect() { } diff --git a/louloulibs/network/dns_socket_handler.hpp b/louloulibs/network/dns_socket_handler.hpp index ad119e1..5ea9846 100644 --- a/louloulibs/network/dns_socket_handler.hpp +++ b/louloulibs/network/dns_socket_handler.hpp @@ -18,7 +18,12 @@ class DNSSocketHandler: public SocketHandler { public: explicit DNSSocketHandler(std::shared_ptr poller, const socket_t socket); - ~DNSSocketHandler(); + ~DNSSocketHandler() = default; + 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. @@ -36,10 +41,6 @@ public: bool is_connected() const override final; private: - DNSSocketHandler(const DNSSocketHandler&) = delete; - DNSSocketHandler(DNSSocketHandler&&) = delete; - DNSSocketHandler& operator=(const DNSSocketHandler&) = delete; - DNSSocketHandler& operator=(DNSSocketHandler&&) = delete; }; #endif // CARES_FOUND diff --git a/louloulibs/network/poller.hpp b/louloulibs/network/poller.hpp index de0cb48..4bf7432 100644 --- a/louloulibs/network/poller.hpp +++ b/louloulibs/network/poller.hpp @@ -38,6 +38,10 @@ 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. @@ -85,11 +89,6 @@ private: #elif POLLER == EPOLL int epfd; #endif - - Poller(const Poller&) = delete; - Poller(Poller&&) = delete; - Poller& operator=(const Poller&) = delete; - Poller& operator=(Poller&&) = delete; }; #endif // POLLER_INCLUDED diff --git a/louloulibs/network/resolver.hpp b/louloulibs/network/resolver.hpp index f9e9134..fd57e25 100644 --- a/louloulibs/network/resolver.hpp +++ b/louloulibs/network/resolver.hpp @@ -37,6 +37,10 @@ public: Resolver(); ~Resolver() = default; + Resolver(const Resolver&) = delete; + Resolver(Resolver&&) = delete; + Resolver& operator=(const Resolver&) = delete; + Resolver& operator=(Resolver&&) = delete; bool is_resolving() const { @@ -117,11 +121,6 @@ private: ErrorCallbackType error_cb; SuccessCallbackType success_cb; - - Resolver(const Resolver&) = delete; - Resolver(Resolver&&) = delete; - Resolver& operator=(const Resolver&) = delete; - Resolver& operator=(Resolver&&) = delete; }; std::string addr_to_string(const struct addrinfo* rp); diff --git a/louloulibs/network/socket_handler.hpp b/louloulibs/network/socket_handler.hpp index d01ac5d..daa4413 100644 --- a/louloulibs/network/socket_handler.hpp +++ b/louloulibs/network/socket_handler.hpp @@ -16,6 +16,11 @@ public: socket(socket) {} virtual ~SocketHandler() {} + SocketHandler(const SocketHandler&) = delete; + SocketHandler(SocketHandler&&) = delete; + SocketHandler& operator=(const SocketHandler&) = delete; + SocketHandler& operator=(SocketHandler&&) = delete; + virtual void on_recv() = 0; virtual void on_send() = 0; virtual void connect() = 0; @@ -34,12 +39,6 @@ protected: * The handled socket. */ socket_t socket; - -private: - SocketHandler(const SocketHandler&) = delete; - SocketHandler(SocketHandler&&) = delete; - SocketHandler& operator=(const SocketHandler&) = delete; - SocketHandler& operator=(SocketHandler&&) = delete; }; #endif // SOCKET_HANDLER_HPP diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index 62b2ce7..dd4e82e 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -28,9 +28,13 @@ class TCPSocketHandler: public SocketHandler { 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; + /** * 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 @@ -231,11 +235,6 @@ protected: std::string bind_addr; private: - TCPSocketHandler(const TCPSocketHandler&) = delete; - TCPSocketHandler(TCPSocketHandler&&) = delete; - TCPSocketHandler& operator=(const TCPSocketHandler&) = delete; - TCPSocketHandler& operator=(TCPSocketHandler&&) = delete; - /** * Display the resolved IP, just for information purpose. */ -- cgit v1.2.3 From ce798b1e8cfce7df69e0e91400c96090447a5ea8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 10 May 2016 12:48:39 +0200 Subject: Fix build with POLLER=POLL --- louloulibs/network/poller.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'louloulibs/network') diff --git a/louloulibs/network/poller.cpp b/louloulibs/network/poller.cpp index 26e5a8f..959567e 100644 --- a/louloulibs/network/poller.cpp +++ b/louloulibs/network/poller.cpp @@ -28,8 +28,10 @@ Poller::Poller() Poller::~Poller() { +#if POLLER == EPOLL if (this->epfd > 0) ::close(this->epfd); +#endif } void Poller::add_socket_handler(SocketHandler* socket_handler) -- cgit v1.2.3 From fdddd447b3976f26a4146ebf91abfc04736a006b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 13 May 2016 01:22:49 +0200 Subject: =?UTF-8?q?Use=20=E2=80=9Cusing=E2=80=9D=20instead=20of=20typedef?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- louloulibs/network/socket_handler.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/socket_handler.hpp b/louloulibs/network/socket_handler.hpp index daa4413..feabcf1 100644 --- a/louloulibs/network/socket_handler.hpp +++ b/louloulibs/network/socket_handler.hpp @@ -6,7 +6,7 @@ class Poller; -typedef int socket_t; +using socket_t = int; class SocketHandler { -- cgit v1.2.3 From 66609cfba2b581be52de6096193751d1bb4ec3c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sun, 22 May 2016 12:52:05 +0200 Subject: Remove all usage of std::list --- 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 dd4e82e..55f4113 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -180,7 +180,7 @@ private: /** * Where data is added, when we want to send something to the client. */ - std::list out_buf; + std::vector out_buf; /** * DNS resolver */ -- cgit v1.2.3 From 28300dff5b87d6a45ef5bba2a9b916b39022c7da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 8 Jun 2016 10:31:19 +0200 Subject: Add a missing vector include --- 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 55f4113..91fa7a8 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -13,11 +13,11 @@ #include #include +#include #include #include #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 -- cgit v1.2.3 From 5a2e61161792cf51209f240e40e28036195f35be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Mon, 13 Jun 2016 19:59:17 +0200 Subject: Show off, with some variadic templates, for the logger module --- louloulibs/network/credentials_manager.cpp | 6 ++--- louloulibs/network/poller.cpp | 14 +++++------ louloulibs/network/tcp_socket_handler.cpp | 40 +++++++++++++++--------------- 3 files changed, 30 insertions(+), 30 deletions(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/credentials_manager.cpp b/louloulibs/network/credentials_manager.cpp index c5b8493..ee83c3b 100644 --- a/louloulibs/network/credentials_manager.cpp +++ b/louloulibs/network/credentials_manager.cpp @@ -41,7 +41,7 @@ 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); + log_debug("Checking remote certificate (", type, ") for hostname ", purported_hostname); try { Botan::Credentials_Manager::verify_certificate_chain(type, purported_hostname, certs); @@ -49,7 +49,7 @@ void BasicCredentialsManager::verify_certificate_chain(const std::string& type, } catch (const std::exception& tls_exception) { - log_warning("TLS certificate check failed: " << tls_exception.what()); + 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)) @@ -78,7 +78,7 @@ void BasicCredentialsManager::load_certs() try { Botan::DataSource_Stream bundle(path); - log_debug("Using ca 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 diff --git a/louloulibs/network/poller.cpp b/louloulibs/network/poller.cpp index 959567e..8a6fd97 100644 --- a/louloulibs/network/poller.cpp +++ b/louloulibs/network/poller.cpp @@ -20,7 +20,7 @@ Poller::Poller() this->epfd = ::epoll_create1(0); if (this->epfd == -1) { - log_error("epoll failed: " << strerror(errno)); + log_error("epoll failed: ", strerror(errno)); throw std::runtime_error("Could not create epoll instance"); } #endif @@ -54,7 +54,7 @@ void Poller::add_socket_handler(SocketHandler* 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)); + log_error("epoll_ctl failed: ", strerror(errno)); throw std::runtime_error("Could not add socket to epoll"); } #endif @@ -86,7 +86,7 @@ void Poller::remove_socket_handler(const socket_t socket) const int res = ::epoll_ctl(this->epfd, EPOLL_CTL_DEL, socket, nullptr); if (res == -1) { - log_error("epoll_ctl failed: " << strerror(errno)); + log_error("epoll_ctl failed: ", strerror(errno)); throw std::runtime_error("Could not remove socket from epoll"); } #endif @@ -109,7 +109,7 @@ void Poller::watch_send_events(SocketHandler* 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)); + log_error("epoll_ctl failed: ", strerror(errno)); throw std::runtime_error("Could not modify socket flags in epoll"); } #endif @@ -132,7 +132,7 @@ void Poller::stop_watching_send_events(SocketHandler* 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)); + log_error("epoll_ctl failed: ", strerror(errno)); throw std::runtime_error("Could not modify socket flags in epoll"); } #endif @@ -165,7 +165,7 @@ int Poller::poll(const std::chrono::milliseconds& timeout) { if (errno == EINTR) return true; - log_error("poll failed: " << strerror(errno)); + 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 @@ -205,7 +205,7 @@ int Poller::poll(const std::chrono::milliseconds& timeout) { if (errno == EINTR) return 0; - log_error("epoll wait: " << strerror(errno)); + log_error("epoll wait: ", strerror(errno)); throw std::runtime_error("Epoll_wait failed"); } for (int i = 0; i < nb_events; ++i) diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index 81369dd..5420b1c 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -64,8 +64,8 @@ void TCPSocketHandler::init_socket(const struct addrinfo* rp) 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)); + log_error("Failed to bind socket to ", this->bind_addr, ": ", + gai_strerror(err)); else { utils::ScopeGuard sg([result](){ freeaddrinfo(result); }); @@ -79,15 +79,15 @@ void TCPSocketHandler::init_socket(const struct addrinfo* rp) break; } if (!rp) - log_error("Failed to bind socket to " << this->bind_addr << ": " - << strerror(bind_error)); + log_error("Failed to bind socket to ", this->bind_addr, ": ", + strerror(bind_error)); else - log_info("Socket successfully bound to " << this->bind_addr); + 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)); + 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. @@ -113,7 +113,7 @@ void TCPSocketHandler::connect(const std::string& address, const std::string& po // this is the first call of this function. if (!this->resolver.is_resolved()) { - log_info("Trying to connect to " << address << ":" << port); + 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() @@ -161,7 +161,7 @@ void TCPSocketHandler::connect(const std::string& address, const std::string& po this->init_socket(rp); } catch (const std::runtime_error& error) { - log_error("Failed to init socket: " << error.what()); + log_error("Failed to init socket: ", error.what()); break; } } @@ -204,7 +204,7 @@ void TCPSocketHandler::connect(const std::string& address, const std::string& po "connection_timeout"s + std::to_string(this->socket))); return ; } - log_info("Connection failed:" << strerror(errno)); + log_info("Connection failed:", strerror(errno)); } log_error("All connection attempts failed."); this->close(); @@ -268,9 +268,9 @@ ssize_t TCPSocketHandler::do_recv(void* recv_buf, const size_t buf_size) else if (-1 == size) { if (this->connecting) - log_warning("Error connecting: " << strerror(errno)); + log_warning("Error connecting: ", strerror(errno)); else - log_warning("Error while reading from socket: " << strerror(errno)); + 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; @@ -300,7 +300,7 @@ void TCPSocketHandler::on_send() ssize_t res = ::sendmsg(this->socket, &msg, MSG_NOSIGNAL); if (res < 0) { - log_error("sendmsg failed: " << strerror(errno)); + log_error("sendmsg failed: ", strerror(errno)); this->on_connection_close(strerror(errno)); this->close(); } @@ -351,9 +351,9 @@ void TCPSocketHandler::close() void TCPSocketHandler::display_resolved_ip(struct addrinfo* rp) const { if (rp->ai_family == AF_INET) - log_debug("Trying IPv4 address " << addr_to_string(rp)); + 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)); + log_debug("Trying IPv6 address ", addr_to_string(rp)); } void TCPSocketHandler::send_data(std::string&& data) @@ -478,18 +478,18 @@ void TCPSocketHandler::tls_output_fn(const Botan::byte* data, size_t size) void TCPSocketHandler::tls_alert_cb(Botan::TLS::Alert alert, const Botan::byte*, size_t) { - log_debug("tls_alert: " << alert.type_string()); + log_debug("tls_alert: ", alert.type_string()); } bool TCPSocketHandler::tls_handshake_cb(const Botan::TLS::Session& session) { - log_debug("Handshake with " << session.server_info().hostname() << " complete." - << " Version: " << session.version().to_string() - << " using " << session.ciphersuite().to_string()); + 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())); + 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())); + log_debug("Session ticket ", Botan::hex_encode(session.session_ticket())); return true; } -- cgit v1.2.3 From 81f8f45b371d1a0ef72c2768fbd1f9188fe83616 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Mon, 4 Jul 2016 17:53:53 +0200 Subject: Replace all include guards by #pragma once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It’s $CURRENT_YEAR --- louloulibs/network/credentials_manager.hpp | 5 ++--- louloulibs/network/dns_handler.hpp | 4 +--- louloulibs/network/dns_socket_handler.hpp | 4 +--- louloulibs/network/poller.hpp | 6 +++--- louloulibs/network/resolver.hpp | 6 +++--- louloulibs/network/socket_handler.hpp | 4 +--- louloulibs/network/tcp_socket_handler.hpp | 6 +++--- 7 files changed, 14 insertions(+), 21 deletions(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/credentials_manager.hpp b/louloulibs/network/credentials_manager.hpp index c0d23ae..0fc4b89 100644 --- a/louloulibs/network/credentials_manager.hpp +++ b/louloulibs/network/credentials_manager.hpp @@ -1,5 +1,4 @@ -#ifndef BIBOUMI_CREDENTIALS_MANAGER_HPP -#define BIBOUMI_CREDENTIALS_MANAGER_HPP +#pragma once #include "louloulibs.h" @@ -37,4 +36,4 @@ private: }; #endif //BOTAN_FOUND -#endif //BIBOUMI_CREDENTIALS_MANAGER_HPP + diff --git a/louloulibs/network/dns_handler.hpp b/louloulibs/network/dns_handler.hpp index d2b48d2..e0feb11 100644 --- a/louloulibs/network/dns_handler.hpp +++ b/louloulibs/network/dns_handler.hpp @@ -1,5 +1,4 @@ -#ifndef DNS_HANDLER_HPP_INCLUDED -#define DNS_HANDLER_HPP_INCLUDED +#pragma once #include #ifdef CARES_FOUND @@ -56,4 +55,3 @@ private: }; #endif /* CARES_FOUND */ -#endif /* DNS_HANDLER_HPP_INCLUDED */ diff --git a/louloulibs/network/dns_socket_handler.hpp b/louloulibs/network/dns_socket_handler.hpp index 5ea9846..dba2f26 100644 --- a/louloulibs/network/dns_socket_handler.hpp +++ b/louloulibs/network/dns_socket_handler.hpp @@ -1,5 +1,4 @@ -#ifndef DNS_SOCKET_HANDLER_HPP -# define DNS_SOCKET_HANDLER_HPP +#pragma once #include #ifdef CARES_FOUND @@ -44,4 +43,3 @@ private: }; #endif // CARES_FOUND -#endif // DNS_SOCKET_HANDLER_HPP diff --git a/louloulibs/network/poller.hpp b/louloulibs/network/poller.hpp index 4bf7432..fc1a1a1 100644 --- a/louloulibs/network/poller.hpp +++ b/louloulibs/network/poller.hpp @@ -1,5 +1,5 @@ -#ifndef POLLER_INCLUDED -# define POLLER_INCLUDED +#pragma once + #include @@ -91,4 +91,4 @@ private: #endif }; -#endif // POLLER_INCLUDED + diff --git a/louloulibs/network/resolver.hpp b/louloulibs/network/resolver.hpp index fd57e25..afe6e2b 100644 --- a/louloulibs/network/resolver.hpp +++ b/louloulibs/network/resolver.hpp @@ -1,5 +1,5 @@ -#ifndef RESOLVER_HPP_INCLUDED -#define RESOLVER_HPP_INCLUDED +#pragma once + #include "louloulibs.h" @@ -125,4 +125,4 @@ private: std::string addr_to_string(const struct addrinfo* rp); -#endif /* RESOLVER_HPP_INCLUDED */ + diff --git a/louloulibs/network/socket_handler.hpp b/louloulibs/network/socket_handler.hpp index feabcf1..eeb41fe 100644 --- a/louloulibs/network/socket_handler.hpp +++ b/louloulibs/network/socket_handler.hpp @@ -1,5 +1,4 @@ -#ifndef SOCKET_HANDLER_HPP -# define SOCKET_HANDLER_HPP +#pragma once #include #include @@ -41,4 +40,3 @@ protected: socket_t socket; }; -#endif // SOCKET_HANDLER_HPP diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index 91fa7a8..b0ba493 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -1,5 +1,5 @@ -#ifndef SOCKET_HANDLER_INCLUDED -# define SOCKET_HANDLER_INCLUDED +#pragma once + #include "louloulibs.h" @@ -270,5 +270,5 @@ private: #endif // BOTAN_FOUND }; -#endif // SOCKET_HANDLER_INCLUDED + -- cgit v1.2.3 From 5328d0806fdc5becb9344b4d4320787a2b7c0712 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Thu, 7 Jul 2016 09:27:22 +0200 Subject: =?UTF-8?q?Don=E2=80=99t=20use=20unique=5Fptr=20to=20store=20dns?= =?UTF-8?q?=20socket=20handlers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- louloulibs/network/dns_handler.cpp | 14 +++++++------- louloulibs/network/dns_handler.hpp | 2 +- louloulibs/network/dns_socket_handler.hpp | 6 +++--- louloulibs/network/socket_handler.hpp | 6 +++--- 4 files changed, 14 insertions(+), 14 deletions(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/dns_handler.cpp b/louloulibs/network/dns_handler.cpp index 5e19f8c..24c4bcf 100644 --- a/louloulibs/network/dns_handler.cpp +++ b/louloulibs/network/dns_handler.cpp @@ -68,7 +68,7 @@ void DNSHandler::watch_dns_sockets(std::shared_ptr& poller) std::remove_if(this->socket_handlers.begin(), this->socket_handlers.end(), [&readers](const auto& dns_socket) { - return !FD_ISSET(dns_socket->get_socket(), &readers); + return !FD_ISSET(dns_socket.get_socket(), &readers); }), this->socket_handlers.end()); @@ -81,8 +81,8 @@ void DNSHandler::watch_dns_sockets(std::shared_ptr& poller) this->socket_handlers.end(), [i](const auto& socket_handler) { - return i == socket_handler->get_socket(); - }); + 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 @@ -95,12 +95,12 @@ void DNSHandler::watch_dns_sockets(std::shared_ptr& poller) if (it == this->socket_handlers.end()) { this->socket_handlers.emplace(this->socket_handlers.begin(), - std::make_unique(poller, i)); + poller, i); it = this->socket_handlers.begin(); } - poller->add_socket_handler(it->get()); + poller->add_socket_handler(&*it); if (write) - poller->watch_send_events(it->get()); + poller->watch_send_events(&*it); } } // Cancel previous timer, if any. @@ -116,7 +116,7 @@ void DNSHandler::watch_dns_sockets(std::shared_ptr& poller) [this]() { for (auto& dns_socket_handler: this->socket_handlers) - dns_socket_handler->on_recv(); + dns_socket_handler.on_recv(); }, "DNS timeout")); } diff --git a/louloulibs/network/dns_handler.hpp b/louloulibs/network/dns_handler.hpp index e0feb11..d8b55c8 100644 --- a/louloulibs/network/dns_handler.hpp +++ b/louloulibs/network/dns_handler.hpp @@ -50,7 +50,7 @@ private: * call to ares_fds. DNSSocketHandlers are added to it or removed from it * in the watch_dns_sockets() method */ - std::vector> socket_handlers; + std::vector socket_handlers; ares_channel channel; }; diff --git a/louloulibs/network/dns_socket_handler.hpp b/louloulibs/network/dns_socket_handler.hpp index dba2f26..21ae4e1 100644 --- a/louloulibs/network/dns_socket_handler.hpp +++ b/louloulibs/network/dns_socket_handler.hpp @@ -17,11 +17,11 @@ class DNSSocketHandler: public SocketHandler { public: explicit DNSSocketHandler(std::shared_ptr poller, const socket_t socket); - ~DNSSocketHandler() = default; + ~DNSSocketHandler(); + DNSSocketHandler(DNSSocketHandler&&) = default; + DNSSocketHandler& operator=(DNSSocketHandler&&) = default; 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 diff --git a/louloulibs/network/socket_handler.hpp b/louloulibs/network/socket_handler.hpp index eeb41fe..869ac3b 100644 --- a/louloulibs/network/socket_handler.hpp +++ b/louloulibs/network/socket_handler.hpp @@ -15,10 +15,10 @@ public: socket(socket) {} virtual ~SocketHandler() {} - SocketHandler(const SocketHandler&) = delete; - SocketHandler(SocketHandler&&) = delete; + SocketHandler& operator=(SocketHandler&&) = default; + SocketHandler(SocketHandler&&) = default; SocketHandler& operator=(const SocketHandler&) = delete; - SocketHandler& operator=(SocketHandler&&) = delete; + SocketHandler(const SocketHandler&) = delete; virtual void on_recv() = 0; virtual void on_send() = 0; -- cgit v1.2.3 From fa42d0c178faa74b6872c4e0121709ef6682175d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 12 Jul 2016 11:25:38 +0200 Subject: Bring back DNSSocketHandler's destructor --- louloulibs/network/dns_socket_handler.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/dns_socket_handler.hpp b/louloulibs/network/dns_socket_handler.hpp index 21ae4e1..bd431e1 100644 --- a/louloulibs/network/dns_socket_handler.hpp +++ b/louloulibs/network/dns_socket_handler.hpp @@ -17,7 +17,7 @@ class DNSSocketHandler: public SocketHandler { public: explicit DNSSocketHandler(std::shared_ptr poller, const socket_t socket); - ~DNSSocketHandler(); + ~DNSSocketHandler() = default; DNSSocketHandler(DNSSocketHandler&&) = default; DNSSocketHandler& operator=(DNSSocketHandler&&) = default; DNSSocketHandler(const DNSSocketHandler&) = delete; -- cgit v1.2.3 From de0eff6e944db11ae3552e0cd2c191997eebaa2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 27 Jul 2016 10:04:15 +0200 Subject: =?UTF-8?q?Revert=20"Don=E2=80=99t=20use=20unique=5Fptr=20to=20sto?= =?UTF-8?q?re=20dns=20socket=20handlers"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 5328d0806fdc5becb9344b4d4320787a2b7c0712. --- louloulibs/network/dns_handler.cpp | 14 +++++++------- louloulibs/network/dns_handler.hpp | 2 +- louloulibs/network/dns_socket_handler.hpp | 4 ++-- louloulibs/network/socket_handler.hpp | 6 +++--- 4 files changed, 13 insertions(+), 13 deletions(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/dns_handler.cpp b/louloulibs/network/dns_handler.cpp index 24c4bcf..5e19f8c 100644 --- a/louloulibs/network/dns_handler.cpp +++ b/louloulibs/network/dns_handler.cpp @@ -68,7 +68,7 @@ void DNSHandler::watch_dns_sockets(std::shared_ptr& poller) std::remove_if(this->socket_handlers.begin(), this->socket_handlers.end(), [&readers](const auto& dns_socket) { - return !FD_ISSET(dns_socket.get_socket(), &readers); + return !FD_ISSET(dns_socket->get_socket(), &readers); }), this->socket_handlers.end()); @@ -81,8 +81,8 @@ void DNSHandler::watch_dns_sockets(std::shared_ptr& poller) this->socket_handlers.end(), [i](const auto& socket_handler) { - return i == socket_handler.get_socket(); - }); + 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 @@ -95,12 +95,12 @@ void DNSHandler::watch_dns_sockets(std::shared_ptr& poller) if (it == this->socket_handlers.end()) { this->socket_handlers.emplace(this->socket_handlers.begin(), - poller, i); + std::make_unique(poller, i)); it = this->socket_handlers.begin(); } - poller->add_socket_handler(&*it); + poller->add_socket_handler(it->get()); if (write) - poller->watch_send_events(&*it); + poller->watch_send_events(it->get()); } } // Cancel previous timer, if any. @@ -116,7 +116,7 @@ void DNSHandler::watch_dns_sockets(std::shared_ptr& poller) [this]() { for (auto& dns_socket_handler: this->socket_handlers) - dns_socket_handler.on_recv(); + dns_socket_handler->on_recv(); }, "DNS timeout")); } diff --git a/louloulibs/network/dns_handler.hpp b/louloulibs/network/dns_handler.hpp index d8b55c8..e0feb11 100644 --- a/louloulibs/network/dns_handler.hpp +++ b/louloulibs/network/dns_handler.hpp @@ -50,7 +50,7 @@ private: * call to ares_fds. DNSSocketHandlers are added to it or removed from it * in the watch_dns_sockets() method */ - std::vector socket_handlers; + std::vector> socket_handlers; ares_channel channel; }; diff --git a/louloulibs/network/dns_socket_handler.hpp b/louloulibs/network/dns_socket_handler.hpp index bd431e1..dba2f26 100644 --- a/louloulibs/network/dns_socket_handler.hpp +++ b/louloulibs/network/dns_socket_handler.hpp @@ -18,10 +18,10 @@ class DNSSocketHandler: public SocketHandler public: explicit DNSSocketHandler(std::shared_ptr poller, const socket_t socket); ~DNSSocketHandler() = default; - DNSSocketHandler(DNSSocketHandler&&) = default; - DNSSocketHandler& operator=(DNSSocketHandler&&) = default; 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 diff --git a/louloulibs/network/socket_handler.hpp b/louloulibs/network/socket_handler.hpp index 869ac3b..eeb41fe 100644 --- a/louloulibs/network/socket_handler.hpp +++ b/louloulibs/network/socket_handler.hpp @@ -15,10 +15,10 @@ public: socket(socket) {} virtual ~SocketHandler() {} - SocketHandler& operator=(SocketHandler&&) = default; - SocketHandler(SocketHandler&&) = default; - SocketHandler& operator=(const SocketHandler&) = delete; SocketHandler(const SocketHandler&) = delete; + SocketHandler(SocketHandler&&) = delete; + SocketHandler& operator=(const SocketHandler&) = delete; + SocketHandler& operator=(SocketHandler&&) = delete; virtual void on_recv() = 0; virtual void on_send() = 0; -- cgit v1.2.3 From 11a1c0cc99af9629302184fac2b7adf3ac48516b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Thu, 28 Jul 2016 10:34:01 +0200 Subject: Always remove all the DNS sockets on an c-ares event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because c-ares may close one of its socket, even if it’s not the one that has the event. Otherwise we may not know when a socket has been removed from our poller (automatically, when close()ed) and this leads to bugs. --- louloulibs/network/dns_handler.cpp | 11 ++++++++++- louloulibs/network/dns_handler.hpp | 1 + louloulibs/network/dns_socket_handler.cpp | 13 ++++++++++--- louloulibs/network/dns_socket_handler.hpp | 6 +++++- 4 files changed, 26 insertions(+), 5 deletions(-) (limited to 'louloulibs/network') diff --git a/louloulibs/network/dns_handler.cpp b/louloulibs/network/dns_handler.cpp index 5e19f8c..e267944 100644 --- a/louloulibs/network/dns_handler.cpp +++ b/louloulibs/network/dns_handler.cpp @@ -37,6 +37,7 @@ ares_channel& DNSHandler::get_channel() void DNSHandler::destroy() { + this->remove_all_sockets_from_poller(); this->socket_handlers.clear(); ::ares_destroy(this->channel); ::ares_library_cleanup(); @@ -95,7 +96,7 @@ void DNSHandler::watch_dns_sockets(std::shared_ptr& poller) if (it == this->socket_handlers.end()) { this->socket_handlers.emplace(this->socket_handlers.begin(), - std::make_unique(poller, i)); + std::make_unique(poller, *this, i)); it = this->socket_handlers.begin(); } poller->add_socket_handler(it->get()); @@ -122,4 +123,12 @@ void DNSHandler::watch_dns_sockets(std::shared_ptr& poller) } } +void DNSHandler::remove_all_sockets_from_poller() +{ + for (const auto& socket_handler: this->socket_handlers) + { + socket_handler->remove_from_poller(); + } +} + #endif /* CARES_FOUND */ diff --git a/louloulibs/network/dns_handler.hpp b/louloulibs/network/dns_handler.hpp index e0feb11..fd1729d 100644 --- a/louloulibs/network/dns_handler.hpp +++ b/louloulibs/network/dns_handler.hpp @@ -40,6 +40,7 @@ public: * and library. */ void destroy(); + void remove_all_sockets_from_poller(); ares_channel& get_channel(); static DNSHandler instance; diff --git a/louloulibs/network/dns_socket_handler.cpp b/louloulibs/network/dns_socket_handler.cpp index faaabdb..5fd08cb 100644 --- a/louloulibs/network/dns_socket_handler.cpp +++ b/louloulibs/network/dns_socket_handler.cpp @@ -8,8 +8,10 @@ #include DNSSocketHandler::DNSSocketHandler(std::shared_ptr poller, + DNSHandler& handler, const socket_t socket): - SocketHandler(poller, socket) + SocketHandler(poller, socket), + handler(handler) { } @@ -21,7 +23,7 @@ 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->poller->remove_socket_handler(this->socket); + this->handler.remove_all_sockets_from_poller(); ::ares_process_fd(DNSHandler::instance.get_channel(), this->socket, ARES_SOCKET_BAD); } @@ -29,7 +31,7 @@ void DNSSocketHandler::on_send() { // always stop watching send and read events. We will re-watch them if the // next call to ares_fds tell us to - this->poller->remove_socket_handler(this->socket); + this->handler.remove_all_sockets_from_poller(); ::ares_process_fd(DNSHandler::instance.get_channel(), ARES_SOCKET_BAD, this->socket); } @@ -38,4 +40,9 @@ bool DNSSocketHandler::is_connected() const return true; } +void DNSSocketHandler::remove_from_poller() +{ + this->poller->remove_socket_handler(this->socket); +} + #endif /* CARES_FOUND */ diff --git a/louloulibs/network/dns_socket_handler.hpp b/louloulibs/network/dns_socket_handler.hpp index dba2f26..0570196 100644 --- a/louloulibs/network/dns_socket_handler.hpp +++ b/louloulibs/network/dns_socket_handler.hpp @@ -13,10 +13,12 @@ * (Poller reported it to be writable or readeable) */ +class DNSHandler; + class DNSSocketHandler: public SocketHandler { public: - explicit DNSSocketHandler(std::shared_ptr poller, const socket_t socket); + explicit DNSSocketHandler(std::shared_ptr poller, DNSHandler& handler, const socket_t socket); ~DNSSocketHandler() = default; DNSSocketHandler(const DNSSocketHandler&) = delete; DNSSocketHandler(DNSSocketHandler&&) = delete; @@ -38,8 +40,10 @@ public: * Always true, see the comment for connect() */ bool is_connected() const override final; + void remove_from_poller(); private: + DNSHandler& handler; }; #endif // CARES_FOUND -- cgit v1.2.3