diff --git a/connection.cpp b/connection.cpp index dc64199..709da2b 100644 --- a/connection.cpp +++ b/connection.cpp @@ -10,7 +10,7 @@ Connection::Connection(boost::asio::io_context & io) { } -auto Connection::writer_() -> void +auto Connection::writer_immediate() -> void { std::vector buffers; buffers.reserve(write_strings_.size()); @@ -45,14 +45,14 @@ auto Connection::writer() -> void { if (not self->write_strings_.empty()) { - self->writer_(); + self->writer_immediate(); } } }); } else { - writer_(); + writer_immediate(); } } diff --git a/connection.hpp b/connection.hpp index 3c2b4a5..650f87b 100644 --- a/connection.hpp +++ b/connection.hpp @@ -59,7 +59,7 @@ private: EventDispatcher dispatcher_; auto writer() -> void; - auto writer_() -> void; + auto writer_immediate() -> void; public: Connection(boost::asio::io_context & io); diff --git a/self_thread.cpp b/self_thread.cpp index 58a1bd6..62af94e 100644 --- a/self_thread.cpp +++ b/self_thread.cpp @@ -1,5 +1,7 @@ #include "self_thread.hpp" +#include + #include "connection.hpp" #include "ircmsg.hpp" #include "irc_parse_thread.hpp" @@ -14,16 +16,23 @@ auto SelfThread::start(Connection& connection) -> std::shared_ptr { // Learn nickname from 001 case IrcCommand::RPL_WELCOME: + if (event.irc.args.size() < 1) + { + BOOST_LOG_TRIVIAL(debug) << "RPL_WELCOME has too few arguments"; + break; + } thread->nickname_ = event.irc.args[0]; break; // Track changes to our nickname case IrcCommand::NICK: { - auto const bang = event.irc.source.find('!'); - if (bang != std::string::npos - && thread->nickname_ == event.irc.source.substr(0, bang) - ) + if (event.irc.args.size() < 1) { + BOOST_LOG_TRIVIAL(debug) << "NICK has too few arguments"; + break; + } + + if (thread->is_my_mask(event.irc.source)) { thread->nickname_ = event.irc.args[0]; } @@ -32,14 +41,64 @@ auto SelfThread::start(Connection& connection) -> std::shared_ptr // Re-establish user modes case IrcCommand::RPL_UMODEIS: + if (event.irc.args.size() < 1) + { + BOOST_LOG_TRIVIAL(debug) << "RPL_UMODEIS has too few arguments"; + break; + } + thread->mode_ = event.irc.args[1]; break; + case IrcCommand::JOIN: { + if (event.irc.args.size() < 1) + { + BOOST_LOG_TRIVIAL(debug) << "JOIN has too few arguments"; + break; + } + + if (thread->is_my_mask(event.irc.source)) + { + thread->channels_.insert(std::string{event.irc.args[0]}); + } + break; + } + case IrcCommand::KICK: { + if (event.irc.args.size() < 2) + { + BOOST_LOG_TRIVIAL(debug) << "PART has too few arguments"; + break; + } + + if (thread->is_my_nick(event.irc.args[1])) + { + thread->channels_.erase(std::string{event.irc.args[0]}); + } + break; + } + case IrcCommand::PART: { + if (event.irc.args.size() < 1) + { + BOOST_LOG_TRIVIAL(debug) << "PART has too few arguments"; + break; + } + + if (thread->is_my_mask(event.irc.source)) + { + thread->channels_.erase(std::string{event.irc.args[0]}); + } + break; + } + // Interpret self mode changes case IrcCommand::MODE: - if (2 == event.irc.args.size() - && thread->nickname_ == event.irc.args[0] - ) + if (event.irc.args.size() < 2) + { + BOOST_LOG_TRIVIAL(debug) << "MODE has too few arguments"; + break; + } + + if (thread->is_my_nick(event.irc.args[0])) { auto polarity = true; for (char const c : event.irc.args[1]) @@ -75,3 +134,29 @@ auto SelfThread::start(Connection& connection) -> std::shared_ptr return thread; } + +auto SelfThread::get_my_nickname() const -> std::string const& +{ + return nickname_; +} + +auto SelfThread::get_my_mode() const -> std::string const& +{ + return mode_; +} + +auto SelfThread::get_my_channels() const -> std::unordered_set const& +{ + return channels_; +} + +auto SelfThread::is_my_nick(std::string_view nick) const -> bool +{ + return nick == nickname_; +} + +auto SelfThread::is_my_mask(std::string_view mask) const -> bool +{ + auto const bang = mask.find('!'); + return bang != std::string_view::npos && nickname_ == mask.substr(0, bang); +} diff --git a/self_thread.hpp b/self_thread.hpp index 915fdf9..88d7158 100644 --- a/self_thread.hpp +++ b/self_thread.hpp @@ -1,17 +1,30 @@ #pragma once -#include #include +#include +#include struct Connection; +/** + * @brief Thread to track this connection's identity, and IRC state. + * + */ class SelfThread { Connection& connection_; std::string nickname_; std::string mode_; + std::unordered_set channels_; public: SelfThread(Connection& connection) : connection_{connection} {} static auto start(Connection&) -> std::shared_ptr; + + auto get_my_nickname() const -> std::string const&; + auto get_my_mode() const -> std::string const&; + auto get_my_channels() const -> std::unordered_set const&; + + auto is_my_nick(std::string_view nick) const -> bool; + auto is_my_mask(std::string_view nick) const -> bool; }; diff --git a/snote_thread.cpp b/snote_thread.cpp index b85afe9..62fe077 100644 --- a/snote_thread.cpp +++ b/snote_thread.cpp @@ -31,7 +31,7 @@ struct SnotePattern std::regex regex; }; -SnotePattern const patterns[] = +SnotePattern static const patterns[] = { {SnoteTag::ClientConnecting, R"(^Client connecting: ([^ ]+) \(([^@ ]+)@([^) ]+)\) \[(.*)\] \{([^ ]*)\} <([^ ]*)> \[(.*)\]$)"}, @@ -79,7 +79,7 @@ SnotePattern const patterns[] = "^\x02([^ ]+)\x02 set vhost ([^ ]+) on the \x02MARKED\x02 account ([^ ]+).$"}, }; -auto setup_database() -> hs_database_t* +static auto setup_database() -> hs_database_t* { auto const n = std::size(patterns); std::vector expressions; diff --git a/snote_thread.hpp b/snote_thread.hpp index bc4ac59..63ed68c 100644 --- a/snote_thread.hpp +++ b/snote_thread.hpp @@ -58,7 +58,10 @@ struct SnoteThread auto operator()(hs_scratch * scratch) const -> void; }; + /// @brief Database of server notice patterns std::unique_ptr db_; + + /// @brief HyperScan scratch space std::unique_ptr scratch_; static auto start(Connection& connection) -> std::shared_ptr; diff --git a/watchdog_thread.cpp b/watchdog_thread.cpp index 6d92960..dd71f42 100644 --- a/watchdog_thread.cpp +++ b/watchdog_thread.cpp @@ -9,24 +9,22 @@ #include #include -using namespace std::chrono_literals; - WatchdogThread::WatchdogThread(Connection& connection) : connection_{connection} , timer_{connection.get_executor()} -, tried_ping{false} +, stalled_{false} { } auto WatchdogThread::on_activity() -> void { - tried_ping = false; - timer_.expires_from_now(30s); + stalled_ = false; + timer_.expires_from_now(WatchdogThread::TIMEOUT); } -auto WatchdogThread::timeout_token() +auto WatchdogThread::start_timer() { - return [weak = weak_from_this()](auto const& error) + timer_.async_wait([weak = weak_from_this()](auto const& error) { if (not error) { @@ -35,28 +33,28 @@ auto WatchdogThread::timeout_token() self->on_timeout(); } } - }; + }); } auto WatchdogThread::on_timeout() -> void { - if (tried_ping) + if (stalled_) { connection_.close(); } else { send_ping(connection_, "watchdog"); - tried_ping = true; - timer_.expires_from_now(30s); - timer_.async_wait(timeout_token()); + stalled_ = true; + timer_.expires_from_now(WatchdogThread::TIMEOUT); + start_timer(); } } auto WatchdogThread::on_connect() -> void { on_activity(); - timer_.async_wait(timeout_token()); + start_timer(); } auto WatchdogThread::on_disconnect() -> void diff --git a/watchdog_thread.hpp b/watchdog_thread.hpp index e0efa30..cba66d8 100644 --- a/watchdog_thread.hpp +++ b/watchdog_thread.hpp @@ -2,20 +2,43 @@ #include +#include #include class Connection; +/** + * @brief Watch for connection activity and disconnect on stall + * + * The thread will send a ping if no message is received in the + * last TIMEOUT seconds. After another period of no messages + * the thread will disconnect the connection. + * + */ class WatchdogThread : std::enable_shared_from_this { Connection& connection_; boost::asio::steady_timer timer_; - bool tried_ping; + /// @brief Set true and ping sent and false when reply received + bool stalled_; + + const std::chrono::steady_clock::duration TIMEOUT = std::chrono::seconds{30}; + + /// @brief Start the timer + /// @return + auto start_timer(); + + /// @brief auto on_activity() -> void; - auto timeout_token(); + + /// @brief auto on_timeout() -> void; + + /// @brief callback for ConnectEvent event auto on_connect() -> void; + + /// @brief callback for DisconnectEvent event auto on_disconnect() -> void; public: