2023-11-25 20:09:20 -08:00
|
|
|
#include "snote_thread.hpp"
|
|
|
|
|
|
|
|
#include "irc_parse_thread.hpp"
|
2023-11-27 19:09:45 -08:00
|
|
|
#include "ircmsg.hpp"
|
2023-11-25 20:09:20 -08:00
|
|
|
#include "connection.hpp"
|
2023-11-27 18:47:32 -08:00
|
|
|
#include "c_callback.hpp"
|
2023-11-25 20:09:20 -08:00
|
|
|
|
2023-11-27 19:09:45 -08:00
|
|
|
#include <hs.h>
|
|
|
|
|
2023-11-29 13:13:48 -08:00
|
|
|
#include <boost/log/trivial.hpp>
|
|
|
|
|
2023-11-26 21:16:56 -08:00
|
|
|
#include <cstdlib>
|
2023-11-27 19:09:45 -08:00
|
|
|
#include <cstring>
|
|
|
|
#include <regex>
|
2023-11-26 21:16:56 -08:00
|
|
|
#include <stdexcept>
|
|
|
|
#include <utility>
|
2023-11-25 20:09:20 -08:00
|
|
|
|
2023-11-26 21:16:56 -08:00
|
|
|
namespace {
|
2023-11-25 20:09:20 -08:00
|
|
|
|
2023-11-26 21:16:56 -08:00
|
|
|
struct SnotePattern
|
2023-11-25 20:09:20 -08:00
|
|
|
{
|
2023-11-26 21:16:56 -08:00
|
|
|
SnotePattern(SnoteTag tag, char const* expression, unsigned flags = 0)
|
|
|
|
: tag{tag}
|
|
|
|
, expression{expression}
|
|
|
|
, regex{expression, std::regex_constants::ECMAScript | std::regex_constants::optimize}
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
SnoteTag tag;
|
|
|
|
char const* expression;
|
|
|
|
std::regex regex;
|
|
|
|
};
|
|
|
|
|
|
|
|
SnotePattern const patterns[] =
|
|
|
|
{
|
|
|
|
{SnoteTag::ClientConnecting,
|
|
|
|
R"(^Client connecting: ([^ ]+) \(([^@ ]+)@([^) ]+)\) \[(.*)\] \{([^ ]*)\} <([^ ]*)> \[(.*)\]$)"},
|
|
|
|
|
|
|
|
{SnoteTag::ClientExiting,
|
|
|
|
R"(^Client exiting: ([^ ]+) \(([^@ ]+)@([^) ]+)\) \[(.*)\] \[(.*)\]$)"},
|
2023-11-27 14:12:20 -08:00
|
|
|
|
|
|
|
{SnoteTag::RejectingKlined,
|
|
|
|
R"(^Rejecting K-Lined user ([^ ]+)\[([^@]+)@([^\]]+)\] \[([^\] ]+)\] \((.*)\)$)"},
|
|
|
|
|
|
|
|
{SnoteTag::NickChange,
|
|
|
|
R"(^Nick change: From ([^ ]+) to ([^ ]+) \[([^@]+)@([^ ]+)\]$)"},
|
|
|
|
|
|
|
|
{SnoteTag::CreateChannel,
|
|
|
|
R"(^([^ ]+) is creating new channel ([^ ]+)$)"},
|
|
|
|
|
|
|
|
{SnoteTag::TemporaryKlineExpired,
|
|
|
|
R"(^Temporary K-line for \[([^ ]+)\] expired$)"},
|
|
|
|
|
2023-11-27 18:47:32 -08:00
|
|
|
{SnoteTag::PropagatedBanExpired,
|
|
|
|
R"(^Propagated ban for \[([^ ]+)\] expired$)"},
|
|
|
|
|
2023-11-27 14:12:20 -08:00
|
|
|
{SnoteTag::DisconnectingKlined,
|
|
|
|
R"(^Disconnecting K-Lined user ([^ ]+)\[([^@]+)@([^ ]+)\] \((.*)\)$)"},
|
2023-11-27 18:47:32 -08:00
|
|
|
|
|
|
|
{SnoteTag::NewPropagatedKline,
|
|
|
|
R"(^([^ ]+)!([^ ]+)@([^ ]+)\{([^ ]+)\} added global ([^ ]+) min\. K-Line for \[([^ ]+)\] \[(.*)\]$)"},
|
|
|
|
|
|
|
|
{SnoteTag::NewTemporaryKline,
|
|
|
|
R"(^([^ ]+)!([^ ]+)@([^ ]+)\{([^ ]+)\} added temporary ([^ ]+) min\. K-Line for \[([^ ]+)\] \[(.*)\]$)"},
|
|
|
|
|
|
|
|
{SnoteTag::LoginAttempts,
|
|
|
|
"^Warning: \x02([^ ]+)\x02 failed login attempts to \x02([^ ]+)\x02\\. Last attempt received from \x02(.+)\x02.*$"},
|
|
|
|
|
|
|
|
{SnoteTag::PossibleFlooder,
|
|
|
|
R"(^Possible Flooder ([^ ]+)\[([^ ]+)@[^ ]+\] on ([^ ]+) target: ([^ ]+)$)"},
|
|
|
|
|
|
|
|
{SnoteTag::Killed,
|
|
|
|
R"(^Received KILL message for ([^ ]+)!([^ ]+)@([^ ]+)\. From ([^ ]+) Path: ([^ ]+) \((.*)\)$)"},
|
2023-11-29 13:54:34 -08:00
|
|
|
|
|
|
|
{SnoteTag::TooManyGlobalConnections,
|
|
|
|
R"(^Too many global connections for ([^ ]+)\[([^ ]+)@([^ ]+)\] \[(.*)\]$)"},
|
2023-11-29 14:52:57 -08:00
|
|
|
|
|
|
|
{SnoteTag::SetVhostOnMarkedAccount,
|
|
|
|
"^\x02([^ ]+)\x02 set vhost ([^ ]+) on the \x02MARKED\x02 account ([^ ]+).$"},
|
2023-11-26 21:16:56 -08:00
|
|
|
};
|
|
|
|
|
2023-11-27 14:12:20 -08:00
|
|
|
auto setup_database() -> hs_database_t*
|
2023-11-26 21:16:56 -08:00
|
|
|
{
|
2023-11-27 14:12:20 -08:00
|
|
|
auto const n = std::size(patterns);
|
2023-11-26 21:16:56 -08:00
|
|
|
std::vector<char const*> expressions;
|
2023-11-27 18:47:32 -08:00
|
|
|
std::vector<unsigned> flags(n, HS_FLAG_SINGLEMATCH);
|
2023-11-26 21:16:56 -08:00
|
|
|
std::vector<unsigned> ids;
|
|
|
|
|
2023-11-27 18:47:32 -08:00
|
|
|
expressions.reserve(n);
|
|
|
|
ids.reserve(n);
|
2023-11-26 21:16:56 -08:00
|
|
|
|
2023-11-27 14:12:20 -08:00
|
|
|
for (std::size_t i = 0; i < n; i++)
|
2023-11-26 21:16:56 -08:00
|
|
|
{
|
2023-11-27 14:12:20 -08:00
|
|
|
expressions.push_back(patterns[i].expression);
|
|
|
|
ids.push_back(i);
|
2023-11-26 21:16:56 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
hs_database_t* db;
|
|
|
|
hs_compile_error *error;
|
2023-11-27 14:12:20 -08:00
|
|
|
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))
|
2023-11-26 21:16:56 -08:00
|
|
|
{
|
|
|
|
case HS_COMPILER_ERROR:
|
|
|
|
{
|
|
|
|
std::string msg = error->message;
|
|
|
|
hs_free_compile_error(error);
|
|
|
|
throw std::runtime_error{std::move(msg)};
|
|
|
|
}
|
|
|
|
case HS_SUCCESS:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
abort();
|
|
|
|
}
|
2023-11-27 14:12:20 -08:00
|
|
|
return db;
|
2023-11-26 21:16:56 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
auto SnoteThread::start(Connection& connection) -> std::shared_ptr<SnoteThread>
|
|
|
|
{
|
|
|
|
auto thread = std::make_shared<SnoteThread>();
|
|
|
|
|
2023-11-27 14:12:20 -08:00
|
|
|
thread->db_.reset(setup_database());
|
2023-11-26 21:16:56 -08:00
|
|
|
|
|
|
|
hs_scratch_t* scratch = nullptr;
|
|
|
|
if (HS_SUCCESS != hs_alloc_scratch(thread->db_.get(), &scratch))
|
|
|
|
{
|
|
|
|
abort();
|
|
|
|
}
|
2023-11-27 14:12:20 -08:00
|
|
|
thread->scratch_.reset(scratch);
|
2023-11-26 21:16:56 -08:00
|
|
|
|
2023-11-25 20:09:20 -08:00
|
|
|
static char const* const prefix = "*** Notice -- ";
|
2023-11-26 21:16:56 -08:00
|
|
|
connection.add_listener<IrcMsgEvent>([&connection, thread](IrcMsgEvent& event)
|
2023-11-25 20:09:20 -08:00
|
|
|
{
|
2023-11-26 15:14:07 -08:00
|
|
|
auto& args = event.irc.args;
|
2023-11-27 18:47:32 -08:00
|
|
|
if (IrcCommand::NOTICE == event.command && "*" == args[0] && args[1].starts_with(prefix))
|
2023-11-25 20:09:20 -08:00
|
|
|
{
|
|
|
|
event.handled_ = true;
|
2023-11-27 18:47:32 -08:00
|
|
|
auto const message = args[1].substr(strlen(prefix));
|
|
|
|
|
|
|
|
unsigned match_id;
|
|
|
|
auto cb = [&match_id](unsigned id, unsigned long long, unsigned long long, unsigned) -> int
|
2023-11-26 21:16:56 -08:00
|
|
|
{
|
2023-11-27 18:47:32 -08:00
|
|
|
match_id = id;
|
|
|
|
return 1; // stop scanning
|
|
|
|
};
|
|
|
|
|
|
|
|
auto const scan_result =
|
|
|
|
hs_scan(
|
|
|
|
thread->db_.get(),
|
|
|
|
message.data(), message.size(),
|
|
|
|
0, // no flags
|
|
|
|
thread->scratch_.get(),
|
|
|
|
CCallback<decltype(cb)>::invoke, &cb
|
|
|
|
);
|
|
|
|
|
|
|
|
switch (scan_result)
|
2023-11-27 14:12:20 -08:00
|
|
|
{
|
2023-11-27 18:47:32 -08:00
|
|
|
case HS_SUCCESS:
|
2023-11-29 13:13:48 -08:00
|
|
|
BOOST_LOG_TRIVIAL(warning) << "Unknown snote: " << message;
|
2023-11-27 18:47:32 -08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case HS_SCAN_TERMINATED:
|
2023-11-26 21:16:56 -08:00
|
|
|
{
|
|
|
|
auto& pattern = patterns[match_id];
|
2023-11-28 11:34:27 -08:00
|
|
|
connection.make_event<SnoteEvent>(pattern.tag, pattern.regex, message);
|
2023-11-27 18:47:32 -08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
abort();
|
2023-11-26 21:16:56 -08:00
|
|
|
}
|
2023-11-25 20:09:20 -08:00
|
|
|
}
|
|
|
|
});
|
2023-11-26 21:16:56 -08:00
|
|
|
|
|
|
|
return thread;
|
2023-11-25 20:09:20 -08:00
|
|
|
}
|
2023-11-27 19:09:45 -08:00
|
|
|
|
2023-11-28 11:34:27 -08:00
|
|
|
auto SnoteEvent::get_tag() const -> SnoteTag
|
|
|
|
{
|
|
|
|
return tag_;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto SnoteEvent::get_results() -> std::match_results<std::string_view::const_iterator> const&
|
|
|
|
{
|
|
|
|
if (auto results = std::get_if<1>(&components_)) {
|
|
|
|
return *results;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto& regex = std::get<0>(components_).first;
|
|
|
|
auto message = std::get<0>(components_).second;
|
|
|
|
auto& results = components_.emplace<1>();
|
|
|
|
if (not std::regex_match(message.begin(), message.end(), results, regex))
|
|
|
|
{
|
|
|
|
// something went wrong - hyperscan disagrees with std::regex
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
return results;
|
|
|
|
}
|
|
|
|
|
2023-11-27 19:09:45 -08:00
|
|
|
auto SnoteThread::DbDeleter::operator()(hs_database_t * db) const -> void
|
|
|
|
{
|
|
|
|
if (HS_SUCCESS != hs_free_database(db))
|
|
|
|
{
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
auto SnoteThread::ScratchDeleter::operator()(hs_scratch_t * scratch) const -> void
|
|
|
|
{
|
|
|
|
if (HS_SUCCESS != hs_free_scratch(scratch))
|
|
|
|
{
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
}
|