diff --git a/driver/main.cpp b/driver/main.cpp index c48c0a5..a2b1b5e 100644 --- a/driver/main.cpp +++ b/driver/main.cpp @@ -61,8 +61,8 @@ static auto start_irc( // Configure CHALLENGE on registration if applicable if (not settings.challenge_username.empty() && not settings.challenge_key_file.empty()) { if (auto key = myirc::key_from_file(settings.challenge_key_file, settings.challenge_key_password)) { - client->sig_registered.connect([&settings, connection, key = std::move(key)]() { - Challenge::start(connection, settings.challenge_username, key); + client->sig_registered.connect([&settings, client, key = std::move(key)]() { + Challenge::start(client, settings.challenge_username, key); }); } } @@ -74,7 +74,7 @@ static auto start_irc( // On disconnect reconnect in 5 seconds // connection is captured in the disconnect handler so it can keep itself alive connection->sig_disconnect.connect( - [&io, &settings, connection, webhook]() { + [&io, &settings, connection, webhook](auto) { webhook->clear_client(); auto timer = std::make_shared(io); timer->expires_after(5s); diff --git a/driver/web.cpp b/driver/web.cpp index 5df8a14..965359b 100644 --- a/driver/web.cpp +++ b/driver/web.cpp @@ -64,7 +64,7 @@ auto compute_signature(const std::string_view secret, const std::string_view bod unsigned int digest_length = EVP_MAX_MD_SIZE; unsigned char digest[EVP_MAX_MD_SIZE]; - HMAC(EVP_sha256(), secret.data(), secret.size(), reinterpret_cast(body.data()), body.size(), digest, &digest_length); + HMAC(EVP_sha256(), secret.data(), static_cast(secret.size()), reinterpret_cast(body.data()), body.size(), digest, &digest_length); std::stringstream ss; ss << "sha256="; @@ -415,7 +415,7 @@ auto Webhooks::send_notice(std::string_view target, std::string message) -> void { if (client_) { - client_->get_connection().send_notice(target, message); + client_->send_notice(target, message); } else { @@ -428,7 +428,7 @@ auto Webhooks::set_client(std::shared_ptr client) -> void client_ = std::move(client); for (auto &&[target, message] : std::move(events_)) { - client_->get_connection().send_notice(target, message); + client_->send_notice(target, message); } events_.clear(); } diff --git a/mybase64/include/mybase64.hpp b/mybase64/include/mybase64.hpp index eb2ef11..30a15f4 100644 --- a/mybase64/include/mybase64.hpp +++ b/mybase64/include/mybase64.hpp @@ -2,15 +2,14 @@ * @file mybase64.hpp * @author Eric Mertens (emertens@gmail.com) * @brief Base64 encoding and decoding - * + * */ #pragma once #include #include -namespace mybase64 -{ +namespace mybase64 { inline constexpr auto encoded_size(std::size_t len) -> std::size_t { @@ -24,7 +23,7 @@ inline constexpr auto decoded_size(std::size_t len) -> std::size_t /** * @brief Encode a string into base64 - * + * * @param input input text * @param output Target buffer for encoded value */ @@ -32,13 +31,11 @@ auto encode(std::string_view input, char* output) -> void; /** * @brief Decode a base64 encoded string - * + * * @param input Base64 input text * @param output Target buffer for decoded value - * @param outlen Output parameter for decoded length - * @return true success - * @return false failure + * @return pointer to end of output on success */ -auto decode(std::string_view input, char* output, std::size_t* outlen) -> bool; +auto decode(std::string_view input, char* output) -> char*; } // namespace diff --git a/mybase64/mybase64.cpp b/mybase64/mybase64.cpp index a48c9d0..caf4b6e 100644 --- a/mybase64/mybase64.cpp +++ b/mybase64/mybase64.cpp @@ -1,113 +1,102 @@ #include "mybase64.hpp" +#include #include #include #include -namespace mybase64 -{ +namespace mybase64 { + +namespace { + + constexpr std::array alphabet{ + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' + }; + + constexpr std::array alphabet_values = []() constexpr { + std::array result; + result.fill(-1); + std::int8_t v = 0; + for (auto const k : alphabet) + { + result[k] = v++; + } + return result; + }(); + +} static_assert(CHAR_BIT == 8); auto encode(std::string_view const input, char* output) -> void { - static char const* const alphabet = - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/"; + auto cursor = std::begin(input); + auto const end = std::end(input); - auto cursor = std::begin(input); - auto const end = std::end(input); + while (end - cursor >= 3) + { + std::uint32_t buffer = std::uint8_t(*cursor++); + buffer <<= 8; + buffer |= std::uint8_t(*cursor++); + buffer <<= 8; + buffer |= std::uint8_t(*cursor++); - while (end - cursor >= 3) - { - uint32_t buffer = uint8_t(*cursor++); - buffer <<= 8; buffer |= uint8_t(*cursor++); - buffer <<= 8; buffer |= uint8_t(*cursor++); + *output++ = alphabet[(buffer >> 6 * 3) % 64]; + *output++ = alphabet[(buffer >> 6 * 2) % 64]; + *output++ = alphabet[(buffer >> 6 * 1) % 64]; + *output++ = alphabet[(buffer >> 6 * 0) % 64]; + } - *output++ = alphabet[(buffer >> 6 * 3) % 64]; - *output++ = alphabet[(buffer >> 6 * 2) % 64]; - *output++ = alphabet[(buffer >> 6 * 1) % 64]; - *output++ = alphabet[(buffer >> 6 * 0) % 64]; - } + if (cursor < end) + { + std::uint32_t buffer = std::uint8_t(*cursor++) << 10; + if (cursor < end) + buffer |= std::uint8_t(*cursor) << 2; - if (cursor < end) - { - uint32_t buffer = uint8_t(*cursor++) << 10; - if (cursor < end) buffer |= uint8_t(*cursor) << 2; - - *output++ = alphabet[(buffer >> 12) % 64]; - *output++ = alphabet[(buffer >> 6) % 64]; - *output++ = cursor < end ? alphabet[(buffer % 64)] : '='; - *output++ = '='; - } - *output = '\0'; + *output++ = alphabet[(buffer >> 12) % 64]; + *output++ = alphabet[(buffer >> 6) % 64]; + *output++ = cursor < end ? alphabet[(buffer % 64)] : '='; + *output++ = '='; + } + *output = '\0'; } -auto decode(std::string_view const input, char* const output, std::size_t* const outlen) -> bool +auto decode(std::string_view const input, char* output) -> char* { - static int8_t const alphabet_values[] = { - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 0x3e, -1, -1, -1, 0x3f, - 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, - 0x3c, 0x3d, -1, -1, -1, -1, -1, -1, - -1, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, - 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, - 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, - 0x17, 0x18, 0x19, -1, -1, -1, -1, -1, - -1, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, - 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, - 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, - 0x31, 0x32, 0x33, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - }; + std::uint32_t buffer = 1; - uint32_t buffer = 1; - char* cursor = output; - - for (char c : input) { - int8_t const value = alphabet_values[uint8_t(c)]; - if (-1 == value) continue; - - buffer = (buffer << 6) | value; - - if (buffer & 1<<6*4) { - *cursor++ = buffer >> 8*2; - *cursor++ = buffer >> 8*1; - *cursor++ = buffer >> 8*0; - buffer = 1; + for (auto const c : input) + { + if (auto const value = alphabet_values[uint8_t(c)]; -1 != value) + { + buffer = (buffer << 6) | value; + if (buffer & 1 << 6 * 4) + { + *output++ = buffer >> 16; + *output++ = buffer >> 8; + *output++ = buffer >> 0; + buffer = 1; + } } } - if (buffer & 1<<6*3) { - *cursor++ = buffer >> 10; - *cursor++ = buffer >> 2; - } else if (buffer & 1<<6*2) { - *cursor++ = buffer >> 4; - } else if (buffer & 1<<6*1) { - return false; + if (buffer & 1 << 6 * 3) + { + *output++ = buffer >> 10; + *output++ = buffer >> 2; } - *outlen = cursor - output; - return true; + else if (buffer & 1 << 6 * 2) + { + *output++ = buffer >> 4; + } + else if (buffer & 1 << 6 * 1) + { + return nullptr; + } + return output; } } // namespace diff --git a/myirc/CMakeLists.txt b/myirc/CMakeLists.txt index a1b40f8..5ef7e87 100644 --- a/myirc/CMakeLists.txt +++ b/myirc/CMakeLists.txt @@ -1,7 +1,7 @@ add_custom_command( OUTPUT irc_commands.inc COMMAND - gperf + /opt/homebrew/bin/gperf -C -Z IrcCommandHash -K text -L C++ -t --output-file irc_commands.inc ${CMAKE_CURRENT_SOURCE_DIR}/irc_commands.gperf @@ -20,6 +20,7 @@ add_library(myirc STATIC ratelimit.cpp sasl_mechanism.cpp snote.cpp + linebuffer.cpp ) target_include_directories(myirc PUBLIC include) diff --git a/myirc/bot.cpp b/myirc/bot.cpp index dd0df85..4750cc5 100644 --- a/myirc/bot.cpp +++ b/myirc/bot.cpp @@ -9,7 +9,7 @@ auto Bot::start(std::shared_ptr self) -> std::shared_ptr const auto thread = std::make_shared(std::move(self)); - thread->self_->sig_chat.connect([thread](auto &chat) { + thread->self_->sig_chat.connect([thread](auto &chat, bool) { thread->on_chat(chat); }); diff --git a/myirc/challenge.cpp b/myirc/challenge.cpp index c6ef5d9..b6e0a61 100644 --- a/myirc/challenge.cpp +++ b/myirc/challenge.cpp @@ -14,9 +14,9 @@ namespace myirc { -Challenge::Challenge(Ref key, std::shared_ptr connection) +Challenge::Challenge(Ref key, std::shared_ptr client) : key_{std::move(key)} - , connection_{std::move(connection)} + , client_{std::move(client)} {} auto Challenge::on_ircmsg(IrcCommand cmd, const IrcMsg &msg) -> void { @@ -31,7 +31,6 @@ auto Challenge::on_ircmsg(IrcCommand cmd, const IrcMsg &msg) -> void { break; case IrcCommand::RPL_YOUREOPER: slot_.disconnect(); - //connection_->send_ping("mitigation"); break; case IrcCommand::RPL_ENDOFRSACHALLENGE2: finish_challenge(); @@ -46,9 +45,10 @@ auto Challenge::finish_challenge() -> void size_t len = mybase64::decoded_size(buffer_.size()); std::vector ciphertext(len, 0); - if (not mybase64::decode(buffer_, reinterpret_cast(ciphertext.data()), &len)) + auto decode_end = mybase64::decode(buffer_, reinterpret_cast(ciphertext.data())); + if (decode_end == nullptr ) return log_openssl_errors("Challenge base64::decode: "); - ciphertext.resize(len); + ciphertext.resize(decode_end - reinterpret_cast(ciphertext.data())); // Setup decryption context Ref ctx{EVP_PKEY_CTX_new(key_.get(), nullptr)}; @@ -80,15 +80,15 @@ auto Challenge::finish_challenge() -> void buffer_[0] = '+'; mybase64::encode(std::string_view{(char*)digest, digestlen}, buffer_.data() + 1); - connection_->send_challenge(buffer_); + client_->send_challenge(buffer_); buffer_.clear(); } -auto Challenge::start(std::shared_ptr connection, const std::string_view user, Ref ref) -> std::shared_ptr +auto Challenge::start(std::shared_ptr client, const std::string_view user, Ref ref) -> std::shared_ptr { - auto self = std::make_shared(std::move(ref), connection); - self->slot_ = connection->sig_ircmsg.connect([self](auto cmd, auto &msg) { self->on_ircmsg(cmd, msg); }); - connection->send_challenge(user); + auto self = std::make_shared(std::move(ref), client); + self->slot_ = client->get_connection().sig_ircmsg.connect([self](auto cmd, auto &msg, bool) { self->on_ircmsg(cmd, msg); }); + client->send_challenge(user); return self; } diff --git a/myirc/client.cpp b/myirc/client.cpp index 3feabb7..37dabc3 100644 --- a/myirc/client.cpp +++ b/myirc/client.cpp @@ -2,6 +2,7 @@ #include "myirc/connection.hpp" #include "myirc/sasl_mechanism.hpp" +#include "myirc/snote.hpp" #include @@ -121,7 +122,7 @@ auto Client::on_isupport(const IrcMsg &msg) -> void } } -auto Client::on_chat(bool notice, const IrcMsg &irc) -> void +auto Client::on_chat(bool notice, const IrcMsg &irc, bool flush) -> void { char status_msg = '\0'; std::string_view target = irc.args[0]; @@ -137,21 +138,26 @@ auto Client::on_chat(bool notice, const IrcMsg &irc) -> void .source = irc.source, .target = irc.args[0], .message = irc.args[1], - }); + }, flush); } auto Client::start(std::shared_ptr connection) -> std::shared_ptr { auto thread = std::make_shared(connection); - connection->sig_ircmsg.connect([thread](auto cmd, auto &msg) { + connection->sig_ircmsg.connect([thread](auto cmd, auto &msg, bool flush) { switch (cmd) { case IrcCommand::PRIVMSG: - thread->on_chat(false, msg); + thread->on_chat(false, msg, flush); break; case IrcCommand::NOTICE: - thread->on_chat(true, msg); + if (auto match = snoteCore.match(msg)) + { + thread->sig_snote(*match, flush); + } else { + thread->on_chat(true, msg, flush); + } break; case IrcCommand::JOIN: thread->on_join(msg); @@ -184,15 +190,14 @@ auto Client::start(std::shared_ptr connection) -> std::shared_ptron_cap(msg); break; + case IrcCommand::AUTHENTICATE: + thread->on_authenticate_chunk(msg.args[0]); + break; default: break; } }); - connection->sig_authenticate.connect([thread](auto msg) { - thread->on_authenticate(msg); - }); - return thread; } @@ -242,20 +247,20 @@ auto Client::on_authenticate(const std::string_view body) -> void if (not sasl_mechanism_) { BOOST_LOG_TRIVIAL(warning) << "Unexpected AUTHENTICATE from server"sv; - connection_->send_authenticate_abort(); + send_authenticate_abort(); return; } std::visit( overloaded{ [this](const std::string &reply) { - connection_->send_authenticate_encoded(reply); + send_authenticate_encoded(reply); }, [this](SaslMechanism::NoReply) { - connection_->send_authenticate("*"sv); + send_authenticate("*"sv); }, [this](SaslMechanism::Failure) { - connection_->send_authenticate_abort(); + send_authenticate_abort(); }, }, sasl_mechanism_->step(body)); @@ -270,11 +275,11 @@ auto Client::start_sasl(std::unique_ptr mechanism) -> void { if (sasl_mechanism_) { - connection_->send_authenticate("*"sv); // abort SASL + send_authenticate("*"sv); // abort SASL } sasl_mechanism_ = std::move(mechanism); - connection_->send_authenticate(sasl_mechanism_->mechanism_name()); + send_authenticate(sasl_mechanism_->mechanism_name()); } static auto tolower_rfc1459(int c) -> int @@ -334,7 +339,7 @@ auto Client::casemap_compare(std::string_view lhs, std::string_view rhs) const - auto Client::list_caps() -> void { caps_available_.clear(); - connection_->send_cap_ls(); + send_cap_ls(); } auto Client::on_cap(const IrcMsg &msg) -> void @@ -429,4 +434,206 @@ auto Client::on_cap(const IrcMsg &msg) -> void } } +auto Client::on_authenticate_chunk(const std::string_view chunk) -> void +{ + if (chunk != "+"sv) + { + authenticate_buffer_ += chunk; + } + + if (chunk.size() != 400) + { + std::string decoded; + decoded.resize(mybase64::decoded_size(authenticate_buffer_.size())); + std::size_t len; + + if (auto decode_end = mybase64::decode(authenticate_buffer_, decoded.data())) + { + decoded.resize(decode_end - decoded.data()); + on_authenticate(decoded); + } + else + { + BOOST_LOG_TRIVIAL(debug) << "Invalid AUTHENTICATE base64"sv; + send_authenticate("*"sv); // abort SASL + } + + authenticate_buffer_.clear(); + } + else if (authenticate_buffer_.size() > 1024) + { + BOOST_LOG_TRIVIAL(debug) << "AUTHENTICATE buffer overflow"sv; + authenticate_buffer_.clear(); + send_authenticate("*"sv); // abort SASL + } +} + +auto Client::send_ping(std::string_view txt) -> void +{ + connection_->write_irc("PING", txt); +} + +auto Client::send_pong(std::string_view txt) -> void +{ + connection_->write_irc("PONG", txt); +} + +auto Client::send_pass(std::string_view password) -> void +{ + connection_->write_irc("PASS", password); +} + +auto Client::send_user(std::string_view user, std::string_view real) -> void +{ + connection_->write_irc("USER", user, "*", "*", real); +} + +auto Client::send_nick(std::string_view nick) -> void +{ + connection_->write_irc("NICK", nick); +} + +auto Client::send_cap_ls() -> void +{ + connection_->write_irc("CAP", "LS", "302"); +} + +auto Client::send_cap_end() -> void +{ + connection_->write_irc("CAP", "END"); +} + +auto Client::send_cap_req(std::string_view caps) -> void +{ + connection_->write_irc("CAP", "REQ", caps); +} + +auto Client::send_privmsg(std::string_view target, std::string_view message) -> void +{ + connection_->write_irc("PRIVMSG", target, message); +} + +auto Client::send_notice(std::string_view target, std::string_view message) -> void +{ + connection_->write_irc("NOTICE", target, message); +} + +auto Client::send_wallops(std::string_view message) -> void +{ + connection_->write_irc("WALLOPS", message); +} + +auto Client::send_names(std::string_view channel) -> void +{ + connection_->write_irc("NAMES", channel); +} + +auto Client::send_map() -> void +{ + connection_->write_irc("MAP"); +} + +auto Client::send_get_topic(std::string_view channel) -> void +{ + connection_->write_irc("TOPIC", channel); +} + +auto Client::send_set_topic(std::string_view channel, std::string_view message) -> void +{ + connection_->write_irc("TOPIC", channel, message); +} + +auto Client::send_testline(std::string_view target) -> void +{ + connection_->write_irc("TESTLINE", target); +} + +auto Client::send_masktrace_gecos(std::string_view target, std::string_view gecos) -> void +{ + connection_->write_irc("MASKTRACE", target, gecos); +} + +auto Client::send_masktrace(std::string_view target) -> void +{ + connection_->write_irc("MASKTRACE", target); +} + +auto Client::send_testmask_gecos(std::string_view target, std::string_view gecos) -> void +{ + connection_->write_irc("TESTMASK", target, gecos); +} + +auto Client::send_testmask(std::string_view target) -> void +{ + connection_->write_irc("TESTMASK", target); +} + +auto Client::send_authenticate(std::string_view message) -> void +{ + connection_->write_irc("AUTHENTICATE", message); +} + +auto Client::send_join(std::string_view channel) -> void +{ + connection_->write_irc("JOIN", channel); +} + +auto Client::send_challenge(std::string_view message) -> void +{ + connection_->write_irc("CHALLENGE", message); +} + +auto Client::send_oper(std::string_view user, std::string_view pass) -> void +{ + connection_->write_irc("OPER", user, pass); +} + +auto Client::send_kick(std::string_view channel, std::string_view nick, std::string_view reason) -> void +{ + connection_->write_irc("KICK", channel, nick, reason); +} + +auto Client::send_kill(std::string_view nick, std::string_view reason) -> void +{ + connection_->write_irc("KILL", nick, reason); +} + +auto Client::send_quit(std::string_view message) -> void +{ + connection_->write_irc("QUIT", message); +} + +auto Client::send_whois(std::string_view arg1) -> void +{ + connection_->write_irc("WHOIS", arg1); +} + +auto Client::send_whois_remote(std::string_view arg1, std::string_view arg2) -> void +{ + connection_->write_irc("WHOIS", arg1, arg2); +} + +auto Client::send_authenticate_abort() -> void +{ + send_authenticate("*"); +} + +auto Client::send_authenticate_encoded(std::string_view body) -> void +{ + std::string encoded(mybase64::encoded_size(body.size()), 0); + mybase64::encode(body, encoded.data()); + + for (size_t lo = 0; lo < encoded.size(); lo += 400) + { + const auto hi = std::min(lo + 400, encoded.size()); + const std::string_view chunk{encoded.begin() + lo, encoded.begin() + hi}; + send_authenticate(chunk); + } + + if (encoded.size() % 400 == 0) + { + send_authenticate("+"sv); + } +} + } // namespace myirc diff --git a/myirc/connection.cpp b/myirc/connection.cpp index 437549f..9345ae8 100644 --- a/myirc/connection.cpp +++ b/myirc/connection.cpp @@ -1,12 +1,17 @@ #include "myirc/connection.hpp" #include "myirc/linebuffer.hpp" -#include "myirc/snote.hpp" +#include +#include +#include +#include #include #include #include +#include +#include namespace myirc { @@ -99,7 +104,7 @@ auto Connection::watchdog() -> void } else { - send_ping("watchdog"); + write_irc("PING", "watchdog"); stalled_ = true; watchdog(); } @@ -114,7 +119,7 @@ auto Connection::watchdog_activity() -> void } /// Parse IRC message line and dispatch it to the ircmsg slot. -auto Connection::dispatch_line(char *line) -> void +auto Connection::dispatch_line(char *line, bool flush) -> void { const auto msg = parse_irc_message(line); const auto recognized = IrcCommandHash::in_word_set(msg.command.data(), msg.command.size()); @@ -130,7 +135,7 @@ auto Connection::dispatch_line(char *line) -> void // Respond to pings immediate and discard case IrcCommand::PING: - send_pong(msg.args[0]); + write_irc("PONG", msg.args[0]); break; // Unknown message generate warnings but do not dispatch @@ -139,22 +144,9 @@ auto Connection::dispatch_line(char *line) -> void BOOST_LOG_TRIVIAL(warning) << "Unrecognized command: " << msg.command << " " << msg.args.size(); break; - case IrcCommand::AUTHENTICATE: - on_authenticate(msg.args[0]); - break; - - // Server notice generate snote events but not IRC command events - case IrcCommand::NOTICE: - if (auto match = snoteCore.match(msg)) - { - sig_snote(*match); - break; - } - /* FALLTHROUGH */ - // Normal IRC commands default: - sig_ircmsg(command, msg); + sig_ircmsg(command, msg, flush); break; } } @@ -202,208 +194,6 @@ auto Connection::write_irc(std::string front, std::string_view last) -> void write_line(std::move(front)); } -auto Connection::send_ping(std::string_view txt) -> void -{ - write_irc("PING", txt); -} - -auto Connection::send_pong(std::string_view txt) -> void -{ - write_irc("PONG", txt); -} - -auto Connection::send_pass(std::string_view password) -> void -{ - write_irc("PASS", password); -} - -auto Connection::send_user(std::string_view user, std::string_view real) -> void -{ - write_irc("USER", user, "*", "*", real); -} - -auto Connection::send_nick(std::string_view nick) -> void -{ - write_irc("NICK", nick); -} - -auto Connection::send_cap_ls() -> void -{ - write_irc("CAP", "LS", "302"); -} - -auto Connection::send_cap_end() -> void -{ - write_irc("CAP", "END"); -} - -auto Connection::send_cap_req(std::string_view caps) -> void -{ - write_irc("CAP", "REQ", caps); -} - -auto Connection::send_privmsg(std::string_view target, std::string_view message) -> void -{ - write_irc("PRIVMSG", target, message); -} - -auto Connection::send_notice(std::string_view target, std::string_view message) -> void -{ - write_irc("NOTICE", target, message); -} - -auto Connection::send_wallops(std::string_view message) -> void -{ - write_irc("WALLOPS", message); -} - -auto Connection::send_names(std::string_view channel) -> void -{ - write_irc("NAMES", channel); -} - -auto Connection::send_map() -> void -{ - write_irc("MAP"); -} - -auto Connection::send_get_topic(std::string_view channel) -> void -{ - write_irc("TOPIC", channel); -} - -auto Connection::send_set_topic(std::string_view channel, std::string_view message) -> void -{ - write_irc("TOPIC", channel, message); -} - -auto Connection::send_testline(std::string_view target) -> void -{ - write_irc("TESTLINE", target); -} - -auto Connection::send_masktrace_gecos(std::string_view target, std::string_view gecos) -> void -{ - write_irc("MASKTRACE", target, gecos); -} - -auto Connection::send_masktrace(std::string_view target) -> void -{ - write_irc("MASKTRACE", target); -} - -auto Connection::send_testmask_gecos(std::string_view target, std::string_view gecos) -> void -{ - write_irc("TESTMASK", target, gecos); -} - -auto Connection::send_testmask(std::string_view target) -> void -{ - write_irc("TESTMASK", target); -} - -auto Connection::send_authenticate(std::string_view message) -> void -{ - write_irc("AUTHENTICATE", message); -} - -auto Connection::send_join(std::string_view channel) -> void -{ - write_irc("JOIN", channel); -} - -auto Connection::send_challenge(std::string_view message) -> void -{ - write_irc("CHALLENGE", message); -} - -auto Connection::send_oper(std::string_view user, std::string_view pass) -> void -{ - write_irc("OPER", user, pass); -} - -auto Connection::send_kick(std::string_view channel, std::string_view nick, std::string_view reason) -> void -{ - write_irc("KICK", channel, nick, reason); -} - -auto Connection::send_kill(std::string_view nick, std::string_view reason) -> void -{ - write_irc("KILL", nick, reason); -} - -auto Connection::send_quit(std::string_view message) -> void -{ - write_irc("QUIT", message); -} - -auto Connection::send_whois(std::string_view arg1) -> void -{ - write_irc("WHOIS", arg1); -} - -auto Connection::send_whois_remote(std::string_view arg1, std::string_view arg2) -> void -{ - write_irc("WHOIS", arg1, arg2); -} - -auto Connection::on_authenticate(const std::string_view chunk) -> void -{ - if (chunk != "+"sv) - { - authenticate_buffer_ += chunk; - } - - if (chunk.size() != 400) - { - std::string decoded; - decoded.resize(mybase64::decoded_size(authenticate_buffer_.size())); - std::size_t len; - - if (mybase64::decode(authenticate_buffer_, decoded.data(), &len)) - { - decoded.resize(len); - sig_authenticate(decoded); - } - else - { - BOOST_LOG_TRIVIAL(debug) << "Invalid AUTHENTICATE base64"sv; - send_authenticate("*"sv); // abort SASL - } - - authenticate_buffer_.clear(); - } - else if (authenticate_buffer_.size() > 1024) - { - BOOST_LOG_TRIVIAL(debug) << "AUTHENTICATE buffer overflow"sv; - authenticate_buffer_.clear(); - send_authenticate("*"sv); // abort SASL - } -} - -auto Connection::send_authenticate_abort() -> void -{ - send_authenticate("*"); -} - -auto Connection::send_authenticate_encoded(std::string_view body) -> void -{ - std::string encoded(mybase64::encoded_size(body.size()), 0); - mybase64::encode(body, encoded.data()); - - for (size_t lo = 0; lo < encoded.size(); lo += 400) - { - const auto hi = std::min(lo + 400, encoded.size()); - const std::string_view chunk{encoded.begin() + lo, encoded.begin() + hi}; - send_authenticate(chunk); - } - - if (encoded.size() % 400 == 0) - { - send_authenticate("+"sv); - } -} - static auto set_buffer_size(tls_type& stream, std::size_t const n) -> void { @@ -504,6 +294,23 @@ auto build_ssl_context( return ssl_context; } +static auto peer_fingerprint(X509 *cer) -> std::string +{ + std::ostringstream os; + std::vector result; + EVP_MD *md_used; + if (auto digest = X509_digest_sig(cer, &md_used, nullptr)) + { + os << EVP_MD_name(md_used) << ":" << std::hex << std::setfill('0'); + EVP_MD_free(md_used); + for (int i = 0; i < digest->length; ++i) { + os << std::setw(2) << static_cast(digest->data[i]); + } + ASN1_OCTET_STRING_free(digest); + } + return os.str(); +} + auto Connection::connect( Settings settings ) -> boost::asio::awaitable @@ -514,6 +321,10 @@ auto Connection::connect( const auto self = shared_from_this(); const size_t irc_buffer_size = 32'768; + boost::asio::ip::tcp::endpoint socket_endpoint; + std::optional socks_endpoint; + std::string fingerprint; + { // Name resolution auto resolver = boost::asio::ip::tcp::resolver{stream_.get_executor()}; @@ -524,13 +335,37 @@ auto Connection::connect( // Connect to the IRC server auto& socket = stream_.reset(); - const auto endpoint = co_await boost::asio::async_connect(socket, endpoints, boost::asio::use_awaitable); - BOOST_LOG_TRIVIAL(debug) << "CONNECTED: " << endpoint; + + // If we're going to use SOCKS then the TCP connection host is actually the socks + // server and then the IRC server gets passed over the SOCKS protocol + auto const use_socks = not settings.socks_host.empty() && settings.socks_port != 0; + if (use_socks) + { + std::swap(settings.host, settings.socks_host); + std::swap(settings.port, settings.socks_port); + } + + socket_endpoint = co_await boost::asio::async_connect(socket, endpoints, boost::asio::use_awaitable); + BOOST_LOG_TRIVIAL(debug) << "CONNECTED: " << socket_endpoint; // Set socket options socket.set_option(boost::asio::ip::tcp::no_delay(true)); set_buffer_size(socket, irc_buffer_size); set_cloexec(socket.native_handle()); + + // Optionally negotiate SOCKS connection + if (use_socks) + { + auto auth = not settings.socks_user.empty() || not settings.socks_pass.empty() + ? socks5::Auth{socks5::UsernamePasswordCredential{settings.socks_user, settings.socks_pass}} + : socks5::Auth{socks5::NoCredential{}}; + + socks_endpoint = co_await socks5::async_connect( + socket, + settings.socks_host, settings.socks_port, std::move(auth), + boost::asio::use_awaitable + ); + } } if (settings.tls) @@ -555,25 +390,39 @@ auto Connection::connect( } co_await stream.async_handshake(stream.client, boost::asio::use_awaitable); + const auto cer = SSL_get0_peer_certificate(stream.native_handle()); + fingerprint = peer_fingerprint(cer); } - sig_connect(); + sig_connect(socket_endpoint, socks_endpoint, std::move(fingerprint)); watchdog(); for (LineBuffer buffer{irc_buffer_size};;) { boost::system::error_code error; - const auto n = co_await stream_.async_read_some(buffer.get_buffer(), boost::asio::redirect_error(boost::asio::use_awaitable, error)); + auto const chunk = buffer.prepare(); + if (chunk.size() == 0) break; + const auto n = co_await stream_.async_read_some(chunk, boost::asio::redirect_error(boost::asio::use_awaitable, error)); if (error) { break; } - buffer.add_bytes(n, [this](char *line) { - BOOST_LOG_TRIVIAL(debug) << "RECV: " << line; + buffer.commit(n); + + auto line = buffer.next_nonempty_line(); + if (line) + { watchdog_activity(); - dispatch_line(line); - }); + do + { + BOOST_LOG_TRIVIAL(debug) << "RECV: " << line; + const auto next_line = buffer.next_nonempty_line(); + dispatch_line(line, next_line == nullptr); + line = next_line; + } while (line); + } + buffer.shift(); } watchdog_timer_.cancel(); @@ -600,10 +449,8 @@ auto Connection::start(Settings settings) -> void // Disconnect all slots to avoid circular references self->sig_connect.disconnect_all_slots(); self->sig_ircmsg.disconnect_all_slots(); - self->sig_snote.disconnect_all_slots(); - self->sig_authenticate.disconnect_all_slots(); - self->sig_disconnect(); + self->sig_disconnect(e); self->sig_disconnect.disconnect_all_slots(); }); } diff --git a/myirc/include/myirc/challenge.hpp b/myirc/include/myirc/challenge.hpp index 0e83a70..9f8323c 100644 --- a/myirc/include/myirc/challenge.hpp +++ b/myirc/include/myirc/challenge.hpp @@ -1,7 +1,7 @@ #pragma once -#include "connection.hpp" -#include "ref.hpp" +#include "myirc/client.hpp" +#include "myirc/ref.hpp" #include @@ -14,7 +14,7 @@ namespace myirc { class Challenge : std::enable_shared_from_this { Ref key_; - std::shared_ptr connection_; + std::shared_ptr client_; boost::signals2::scoped_connection slot_; std::string buffer_; @@ -22,14 +22,14 @@ class Challenge : std::enable_shared_from_this auto finish_challenge() -> void; public: - Challenge(Ref, std::shared_ptr); + Challenge(Ref, std::shared_ptr); /// @brief Starts the CHALLENGE protocol. /// @param connection Registered connection. /// @param user Operator username /// @param key Operator private RSA key /// @return Handle to the challenge object. - static auto start(std::shared_ptr, std::string_view user, Ref key) -> std::shared_ptr; + static auto start(std::shared_ptr, std::string_view user, Ref key) -> std::shared_ptr; }; } // namespace myirc diff --git a/myirc/include/myirc/client.hpp b/myirc/include/myirc/client.hpp index a61c3a5..c625306 100644 --- a/myirc/include/myirc/client.hpp +++ b/myirc/include/myirc/client.hpp @@ -50,6 +50,12 @@ class Client std::unordered_map caps_available_; std::unordered_set caps_; + // AUTHENTICATE support + std::string authenticate_buffer_; + + auto on_authenticate(std::string_view) -> void; + auto on_authenticate_chunk(std::string_view) -> void; + auto on_welcome(const IrcMsg &irc) -> void; auto on_isupport(const IrcMsg &irc) -> void; auto on_nick(const IrcMsg &irc) -> void; @@ -59,14 +65,14 @@ class Client auto on_part(const IrcMsg &irc) -> void; auto on_mode(const IrcMsg &irc) -> void; auto on_cap(const IrcMsg &irc) -> void; - auto on_authenticate(std::string_view) -> void; auto on_registered() -> void; - auto on_chat(bool, const IrcMsg &irc) -> void; + auto on_chat(bool, const IrcMsg &irc, bool flush) -> void; public: boost::signals2::signal sig_registered; boost::signals2::signal &)> sig_cap_ls; - boost::signals2::signal sig_chat; + boost::signals2::signal sig_chat; + boost::signals2::signal sig_snote; Client(std::shared_ptr connection) : connection_{std::move(connection)} @@ -101,6 +107,38 @@ public: auto casemap_compare(std::string_view, std::string_view) const -> int; auto shutdown() -> void; + + auto send_ping(std::string_view) -> void; + auto send_pong(std::string_view) -> void; + auto send_pass(std::string_view) -> void; + auto send_user(std::string_view, std::string_view) -> void; + auto send_nick(std::string_view) -> void; + auto send_join(std::string_view) -> void; + auto send_names(std::string_view channel) -> void; + auto send_kick(std::string_view, std::string_view, std::string_view) -> void; + auto send_kill(std::string_view, std::string_view) -> void; + auto send_quit(std::string_view) -> void; + auto send_cap_ls() -> void; + auto send_cap_end() -> void; + auto send_map() -> void; + auto send_testline(std::string_view) -> void; + auto send_testmask(std::string_view) -> void; + auto send_testmask_gecos(std::string_view, std::string_view) -> void; + auto send_masktrace(std::string_view) -> void; + auto send_masktrace_gecos(std::string_view, std::string_view) -> void; + auto send_get_topic(std::string_view) -> void; + auto send_set_topic(std::string_view, std::string_view) -> void; + auto send_cap_req(std::string_view) -> void; + auto send_privmsg(std::string_view, std::string_view) -> void; + auto send_wallops(std::string_view) -> void; + auto send_notice(std::string_view, std::string_view) -> void; + auto send_authenticate(std::string_view message) -> void; + auto send_authenticate_encoded(std::string_view message) -> void; + auto send_authenticate_abort() -> void; + auto send_whois(std::string_view) -> void; + auto send_whois_remote(std::string_view, std::string_view) -> void; + auto send_challenge(std::string_view) -> void; + auto send_oper(std::string_view, std::string_view) -> void; }; } // namespace myirc diff --git a/myirc/include/myirc/connection.hpp b/myirc/include/myirc/connection.hpp index 90e10fb..aa7e3a0 100644 --- a/myirc/include/myirc/connection.hpp +++ b/myirc/include/myirc/connection.hpp @@ -47,43 +47,33 @@ private: // Set false when message received. bool stalled_; - // AUTHENTICATE support - std::string authenticate_buffer_; - /// write buffers after consulting with rate limit auto write_buffers() -> void; /// write a specific number of messages now auto write_buffers(size_t) -> void; - auto dispatch_line(char *line) -> void; + auto dispatch_line(char *line, bool) -> void; static constexpr std::chrono::seconds watchdog_duration = std::chrono::seconds{30}; auto watchdog() -> void; auto watchdog_activity() -> void; auto connect(Settings settings) -> boost::asio::awaitable; - auto on_authenticate(std::string_view) -> void; - - /// Build and send well-formed IRC message from individual parameters - auto write_irc(std::string) -> void; - auto write_irc(std::string, std::string_view) -> void; - template - auto write_irc(std::string front, std::string_view next, Args... rest) -> void; public: - boost::signals2::signal sig_connect; - boost::signals2::signal sig_disconnect; - boost::signals2::signal sig_ircmsg; - boost::signals2::signal sig_snote; - boost::signals2::signal sig_authenticate; + boost::signals2::signal, + std::string + )> sig_connect; + + boost::signals2::signal sig_disconnect; + boost::signals2::signal sig_ircmsg; std::unique_ptr rate_limit; Connection(boost::asio::io_context &io); - /// Write bytes into the socket. - auto write_line(std::string message) -> void; - auto get_executor() -> boost::asio::any_io_executor { return stream_.get_executor(); @@ -92,37 +82,14 @@ public: auto start(Settings) -> void; auto close() -> void; - auto send_ping(std::string_view) -> void; - auto send_pong(std::string_view) -> void; - auto send_pass(std::string_view) -> void; - auto send_user(std::string_view, std::string_view) -> void; - auto send_nick(std::string_view) -> void; - auto send_join(std::string_view) -> void; - auto send_names(std::string_view channel) -> void; - auto send_kick(std::string_view, std::string_view, std::string_view) -> void; - auto send_kill(std::string_view, std::string_view) -> void; - auto send_quit(std::string_view) -> void; - auto send_cap_ls() -> void; - auto send_cap_end() -> void; - auto send_map() -> void; - auto send_testline(std::string_view) -> void; - auto send_testmask(std::string_view) -> void; - auto send_testmask_gecos(std::string_view, std::string_view) -> void; - auto send_masktrace(std::string_view) -> void; - auto send_masktrace_gecos(std::string_view, std::string_view) -> void; - auto send_get_topic(std::string_view) -> void; - auto send_set_topic(std::string_view, std::string_view) -> void; - auto send_cap_req(std::string_view) -> void; - auto send_privmsg(std::string_view, std::string_view) -> void; - auto send_wallops(std::string_view) -> void; - auto send_notice(std::string_view, std::string_view) -> void; - auto send_authenticate(std::string_view message) -> void; - auto send_authenticate_encoded(std::string_view message) -> void; - auto send_authenticate_abort() -> void; - auto send_whois(std::string_view) -> void; - auto send_whois_remote(std::string_view, std::string_view) -> void; - auto send_challenge(std::string_view) -> void; - auto send_oper(std::string_view, std::string_view) -> void; + /// Write bytes into the socket. + auto write_line(std::string message) -> void; + + /// Build and send well-formed IRC message from individual parameters + auto write_irc(std::string) -> void; + auto write_irc(std::string, std::string_view) -> void; + template + auto write_irc(std::string front, std::string_view next, Args... rest) -> void; }; template diff --git a/myirc/include/myirc/irc_coroutine.hpp b/myirc/include/myirc/irc_coroutine.hpp index 99a7615..fcd671c 100644 --- a/myirc/include/myirc/irc_coroutine.hpp +++ b/myirc/include/myirc/irc_coroutine.hpp @@ -238,7 +238,7 @@ auto Wait::await_suspend(std::coroutine_handle handle) -> vo const auto tuple_size = std::tuple_size_v; start_modes(std::make_index_sequence{}); - disconnect_slot_ = get_connection().sig_disconnect.connect([this]() { + disconnect_slot_ = get_connection().sig_disconnect.connect([this](auto) { handle_.resume(); }); } diff --git a/myirc/include/myirc/linebuffer.hpp b/myirc/include/myirc/linebuffer.hpp index 8b613be..e7b7467 100644 --- a/myirc/include/myirc/linebuffer.hpp +++ b/myirc/include/myirc/linebuffer.hpp @@ -12,8 +12,6 @@ #include -#include -#include #include namespace myirc { @@ -24,11 +22,13 @@ namespace myirc { */ class LineBuffer { - std::vector buffer; + std::vector buffer_; - // [buffer.begin(), end_) contains buffered data - // [end_, buffer.end()) is available buffer space - std::vector::iterator end_; + // [std::begin(buffer), end_) contains buffered data + // [end_, std::end(buffer)) is available buffer space + decltype(buffer_)::iterator start_; + decltype(buffer_)::iterator search_; + decltype(buffer_)::iterator end_; public: /** @@ -37,19 +37,27 @@ public: * @param n Buffer size */ LineBuffer(std::size_t n) - : buffer(n) - , end_{buffer.begin()} + : buffer_(n) + , start_{buffer_.begin()} + , search_{buffer_.begin()} + , end_{buffer_.begin()} { } + // can't copy the iterator member safely + LineBuffer(LineBuffer const&) = delete; + LineBuffer(LineBuffer&&) = delete; + auto operator=(LineBuffer const&) -> LineBuffer& = delete; + auto operator=(LineBuffer&&) -> LineBuffer& = delete; + /** * @brief Get the available buffer space * * @return boost::asio::mutable_buffer */ - auto get_buffer() -> boost::asio::mutable_buffer + auto prepare() -> boost::asio::mutable_buffer { - return boost::asio::buffer(&*end_, std::distance(end_, buffer.end())); + return boost::asio::buffer(&*end_, std::distance(end_, buffer_.end())); } /** @@ -58,48 +66,39 @@ public: * The first n bytes of the buffer will be considered to be * populated. The line callback function will be called once * per completed line. Those lines are removed from the buffer - * and the is ready for additional calls to get_buffer and - * add_bytes. + * and the is ready for additional calls to prepare and + * commit. * - * @param n Bytes written to the last call of get_buffer + * @param n Bytes written to the last call of prepare * @param line_cb Callback function to run on each completed line */ - auto add_bytes(std::size_t n, std::invocable auto line_cb) -> void + auto commit(std::size_t const n) -> void { - const auto start = end_; std::advance(end_, n); - - // new data is now located in [start, end_) - - // cursor marks the beginning of the current line - auto cursor = buffer.begin(); - - for (auto nl = std::find(start, end_, '\n'); - nl != end_; - nl = std::find(cursor, end_, '\n')) - { - // Null-terminate the line. Support both \n and \r\n - if (cursor < nl && *std::prev(nl) == '\r') - { - *std::prev(nl) = '\0'; - } - else - { - *nl = '\0'; - } - - line_cb(&*cursor); - - cursor = std::next(nl); - } - - // If any lines were processed, move all processed lines to - // the front of the buffer - if (cursor != buffer.begin()) - { - end_ = std::move(cursor, end_, buffer.begin()); - } } + + /** + * @brief Return the next null-terminated line in the buffer + * + * This function should be repeatedly called until it returns + * nullptr. After that shift can be used to reclaim the + * previously used buffer. + * + * @return null-terminated line or nullptr if no line is ready + */ + auto next_line() -> char*; + + /** + * @brief Return the next non-empty line if there is one. + */ + auto next_nonempty_line() -> char*; + + /** + * @brief Reclaim used buffer space invalidating all previous + * next_line() results; + * + */ + auto shift() -> void; }; -} // namespace myirc +} // namespace diff --git a/myirc/include/myirc/ref.hpp b/myirc/include/myirc/ref.hpp index 449471e..3ca9c6e 100644 --- a/myirc/include/myirc/ref.hpp +++ b/myirc/include/myirc/ref.hpp @@ -42,6 +42,12 @@ struct Ref : std::unique_ptr> /// Takes ownership of the pointer explicit Ref(T *x) noexcept : base{x} {} + /// Takes ownership of the pointer + static auto borrow(T *x) -> Ref { + RefTraits::UpRef(x); + return Ref{x}; + } + Ref(Ref &&ref) noexcept = default; Ref(const Ref &ref) noexcept : base{ref.get()} { if (*this) { diff --git a/myirc/irc_commands.gperf b/myirc/irc_commands.gperf index 9cf741f..a8c1450 100644 --- a/myirc/irc_commands.gperf +++ b/myirc/irc_commands.gperf @@ -260,8 +260,8 @@ struct RecognizedCommand { ACCOUNT, IrcCommand::ACCOUNT, 1, 1 AUTHENTICATE, IrcCommand::AUTHENTICATE, 1, 1 AWAY, IrcCommand::AWAY, 0, 1 -BATCH, IrcCommand::BATCH -BOUNCER, IrcCommand::BOUNCER +BATCH, IrcCommand::BATCH, 1, 15 +BOUNCER, IrcCommand::BOUNCER, 1, 15 CAP, IrcCommand::CAP, 1, 15 CHGHOST, IrcCommand::CHGHOST, 2, 2 ERROR, IrcCommand::ERROR, 1, 1 diff --git a/myirc/linebuffer.cpp b/myirc/linebuffer.cpp new file mode 100644 index 0000000..3b51d7f --- /dev/null +++ b/myirc/linebuffer.cpp @@ -0,0 +1,54 @@ +#include "myirc/linebuffer.hpp" + +namespace myirc { + +auto LineBuffer::next_line() -> char* +{ + auto const nl = std::find(search_, end_, '\n'); + if (nl == end_) // no newline found, line incomplete + { + search_ = end_; + return nullptr; + } + + // Null-terminate the line. Support both \n and \r\n + *(start_ < nl && *std::prev(nl) == '\r' ? std::prev(nl) : nl) = '\0'; + + auto const result = start_; + start_ = search_ = std::next(nl); + + return &*result; +} + +// Get the next complete line skipping over empty lines +auto LineBuffer::next_nonempty_line() -> char* +{ + char* line; + while ((line = next_line())) + { + while (*line == ' ') + { + line++; + } + if ('\0' != *line) + { + break; + } + } + return line; +} + + +auto LineBuffer::shift() -> void +{ + auto const first = std::begin(buffer_); + auto const gap = std::distance(start_, first); + if (gap != 0) // relocate incomplete line to front of buffer + { + end_ = std::move(start_, end_, first); + start_ = first; + std::advance(search_, gap); + } +} + +} // namespace myirc diff --git a/myirc/openssl_utils.cpp b/myirc/openssl_utils.cpp index d2b5c0c..9ef2a7c 100644 --- a/myirc/openssl_utils.cpp +++ b/myirc/openssl_utils.cpp @@ -48,9 +48,9 @@ auto key_from_file(const std::string &filename, const std::string_view password) if (const auto fp = fopen(filename.c_str(), "r")) { auto cb = [password](char * const buf, int const size, int) -> int { - if (size < password.size()) { return -1; } + if (std::cmp_less(size, password.size())) { return -1; } std::copy(password.begin(), password.end(), buf); - return password.size(); + return static_cast(password.size()); }; key.reset(PEM_read_PrivateKey(fp, nullptr, CCallback::invoke, &cb)); diff --git a/myirc/registration.cpp b/myirc/registration.cpp index 7bba57e..357c8ac 100644 --- a/myirc/registration.cpp +++ b/myirc/registration.cpp @@ -29,7 +29,7 @@ auto Registration::on_connect() -> void }); slot_ = connection.sig_ircmsg.connect( - [self = shared_from_this()](const auto cmd, auto &msg) + [self = shared_from_this()](const auto cmd, auto &msg, auto) { self->on_ircmsg(cmd, msg); } @@ -37,10 +37,10 @@ auto Registration::on_connect() -> void if (not settings_.password.empty()) { - connection.send_pass(settings_.password); + client_->send_pass(settings_.password); } - connection.send_user(settings_.username, settings_.realname); - connection.send_nick(settings_.nickname); + client_->send_user(settings_.username, settings_.realname); + client_->send_nick(settings_.nickname); } auto Registration::on_cap_list(const std::unordered_map &caps) -> void @@ -79,13 +79,13 @@ auto Registration::on_cap_list(const std::unordered_mapget_connection().send_cap_req(request); + client_->send_cap_req(request); } if (do_sasl) { client_->start_sasl(std::move(settings_.sasl_mechanism)); } else { - client_->get_connection().send_cap_end(); + client_->send_cap_end(); } } @@ -96,7 +96,7 @@ auto Registration::start( { const auto thread = std::make_shared(std::move(settings), std::move(client)); - thread->slot_ = thread->client_->get_connection().sig_connect.connect([thread]() { + thread->slot_ = thread->client_->get_connection().sig_connect.connect([thread](auto, auto, auto) { thread->slot_.disconnect(); thread->on_connect(); }); @@ -118,7 +118,7 @@ auto Registration::randomize_nick() -> void new_nick += x < 10 ? '0' + x : 'A' + (x-10); } - client_->get_connection().send_nick(new_nick); + client_->send_nick(new_nick); } auto Registration::on_ircmsg(const IrcCommand cmd, const IrcMsg &msg) -> void @@ -140,7 +140,7 @@ auto Registration::on_ircmsg(const IrcCommand cmd, const IrcMsg &msg) -> void case IrcCommand::RPL_SASLSUCCESS: case IrcCommand::ERR_SASLFAIL: - client_->get_connection().send_cap_end(); + client_->send_cap_end(); break; } } diff --git a/myirc/snote.cpp b/myirc/snote.cpp index bfbee3a..eb29049 100644 --- a/myirc/snote.cpp +++ b/myirc/snote.cpp @@ -167,7 +167,7 @@ static auto setup_database() -> hs_database_t * expressions.reserve(n); ids.reserve(n); - for (std::size_t i = 0; i < n; i++) + for (unsigned i = 0; i < n; i++) { expressions.push_back(patterns[i].expression); ids.push_back(i); @@ -176,7 +176,7 @@ static auto setup_database() -> hs_database_t * hs_database_t *db; hs_compile_error *error; hs_platform_info_t *platform = nullptr; // target current platform - switch (hs_compile_multi(expressions.data(), flags.data(), ids.data(), expressions.size(), HS_MODE_BLOCK, platform, &db, &error)) + switch (hs_compile_multi(expressions.data(), flags.data(), ids.data(), static_cast(expressions.size()), HS_MODE_BLOCK, platform, &db, &error)) { case HS_COMPILER_ERROR: { std::string msg = std::to_string(error->expression) + ": " + error->message; @@ -225,7 +225,8 @@ auto SnoteCore::match(const IrcMsg &msg) -> std::optional const auto scan_result = hs_scan( db_.get(), - message.data(), message.size(), + message.data(), + static_cast(message.size()), 0, // no flags scratch_.get(), CCallback::invoke, &cb