diff options
Diffstat (limited to 'plugins')
-rw-r--r-- | plugins/smolblok_hostlist/corpus/apple.txt | 1 | ||||
-rw-r--r-- | plugins/smolblok_hostlist/corpus/banana.txt | 1 | ||||
-rw-r--r-- | plugins/smolblok_hostlist/corpus/kiwi.txt | 1 | ||||
-rw-r--r-- | plugins/smolblok_hostlist/corpus/orange.txt | 1 | ||||
-rw-r--r-- | plugins/smolblok_hostlist/filterlist.cpp | 58 | ||||
-rw-r--r-- | plugins/smolblok_hostlist/filterlist.h | 58 | ||||
-rw-r--r-- | plugins/smolblok_hostlist/meson.build | 50 | ||||
-rw-r--r-- | plugins/smolblok_hostlist/plugin/plugin.cpp | 32 | ||||
-rw-r--r-- | plugins/smolblok_hostlist/plugin/plugin.h | 28 | ||||
-rw-r--r-- | plugins/smolblok_hostlist/plugin/smolblokHostlistPlugin.json | 4 | ||||
-rw-r--r-- | plugins/smolblok_hostlist/test/filterlist.cpp | 29 | ||||
-rw-r--r-- | plugins/smolblok_hostlist/test/hostlist.txt | 6 | ||||
-rw-r--r-- | plugins/smolblok_hostlist/test/plugin.cpp | 27 | ||||
-rw-r--r-- | plugins/smolblok_hostlist/test/rule.cpp | 57 |
14 files changed, 353 insertions, 0 deletions
diff --git a/plugins/smolblok_hostlist/corpus/apple.txt b/plugins/smolblok_hostlist/corpus/apple.txt new file mode 100644 index 0000000..3a8973b --- /dev/null +++ b/plugins/smolblok_hostlist/corpus/apple.txt @@ -0,0 +1 @@ +127.0.0.1 localhost.localdomain diff --git a/plugins/smolblok_hostlist/corpus/banana.txt b/plugins/smolblok_hostlist/corpus/banana.txt new file mode 100644 index 0000000..c30aa84 --- /dev/null +++ b/plugins/smolblok_hostlist/corpus/banana.txt @@ -0,0 +1 @@ +0.0.0.0 blockeddomain.com diff --git a/plugins/smolblok_hostlist/corpus/kiwi.txt b/plugins/smolblok_hostlist/corpus/kiwi.txt new file mode 100644 index 0000000..77c325c --- /dev/null +++ b/plugins/smolblok_hostlist/corpus/kiwi.txt @@ -0,0 +1 @@ +# This is a comment, and after it comes a blank line diff --git a/plugins/smolblok_hostlist/corpus/orange.txt b/plugins/smolblok_hostlist/corpus/orange.txt new file mode 100644 index 0000000..583273d --- /dev/null +++ b/plugins/smolblok_hostlist/corpus/orange.txt @@ -0,0 +1 @@ +0.0.0.0 blockeddomain.first blockeddomain.second diff --git a/plugins/smolblok_hostlist/filterlist.cpp b/plugins/smolblok_hostlist/filterlist.cpp new file mode 100644 index 0000000..a0fd414 --- /dev/null +++ b/plugins/smolblok_hostlist/filterlist.cpp @@ -0,0 +1,58 @@ +/* + * This file is part of smolbote. It's copyrighted by the contributors recorded + * in the version control history of the file, available from its original + * location: https://library.iserlohn-fortress.net/aqua/smolbote.git + * + * SPDX-License-Identifier: GPL-3.0 + */ + +#include "filterlist.h" +#include <QIODevice> +#include <QTextStream> + +using namespace Hostlist; + +#ifdef FUZZER +extern "C" int LLVMFuzzerTestOneInput(const char *Data, long long Size) +{ + Filterlist::parseRule(QString::fromLatin1(Data, Size)); + return 0; +} +#endif + +std::map<Filterlist::DomainHash, Filterlist::Rule> Filterlist::parseRule(const QString &line) +{ + if(line.isEmpty() || line.at(0) == '#') { + return {}; + } + + auto parts = line.trimmed().split(' '); + if(parts.size() < 2) { + return {}; + } + + const auto redirect = (parts[0] == "0.0.0.0") ? QString() : parts[0]; + + std::map<DomainHash, Rule> r; + for(int i = 1; i < parts.size(); ++i) { + r.emplace(qHash(parts[i], 0), Filterlist::Rule{ parts[i], redirect }); + } + return r; +} + +bool Filterlist::load(QIODevice &from) +{ + if(!from.isReadable() || !from.isTextModeEnabled()) { + return false; + } + + while(from.bytesAvailable() > 0) { + const auto line = from.readLine(512).trimmed(); + auto r = parseRule(line); + if(!r.empty()) { + qDebug("merging in %lu rules", r.size()); + rules.merge(r); + } + } + return true; +} diff --git a/plugins/smolblok_hostlist/filterlist.h b/plugins/smolblok_hostlist/filterlist.h new file mode 100644 index 0000000..7301f20 --- /dev/null +++ b/plugins/smolblok_hostlist/filterlist.h @@ -0,0 +1,58 @@ +/* + * This file is part of smolbote. It's copyrighted by the contributors recorded + * in the version control history of the file, available from its original + * location: https://library.iserlohn-fortress.net/aqua/smolbote.git + * + * SPDX-License-Identifier: GPL-3.0 + */ + +#pragma once + +#include <map> +#include <smolbote/filterinterface.hpp> + +namespace Hostlist +{ + +class Filterlist final : public FilterList +{ +public: + typedef uint DomainHash; + struct Rule { + QString domain; + QString redirect; + }; + + Filterlist() = default; + ~Filterlist() = default; + + [[nodiscard]] bool findMatch(const QString &domain) const + { + const auto hash = qHash(domain, 0); + const auto found = rules.find(hash); + if(found != rules.end()) { + return true; + } + return false; + } + int count() const + { + return rules.size(); + } + + [[nodiscard]] bool filter(QWebEngineUrlRequestInfo &info) const + { + return false; + } + [[nodiscard]] bool isUpToDate() const + { + return true; + } + + bool load(QIODevice &device); + [[nodiscard]] static std::map<DomainHash, Rule> parseRule(const QString &line); + +private: + std::map<DomainHash, Rule> rules; +}; +} // namespace Hostlist diff --git a/plugins/smolblok_hostlist/meson.build b/plugins/smolblok_hostlist/meson.build new file mode 100644 index 0000000..c9ff303 --- /dev/null +++ b/plugins/smolblok_hostlist/meson.build @@ -0,0 +1,50 @@ +lib_hostlistfilter = static_library('hostlistfilter', + [ 'filterlist.cpp' ], + include_directories: smolbote_interfaces, + dependencies: [dep_qt5] +) + +dep_hostlistfilter = declare_dependency( + include_directories: [ '.', smolbote_interfaces ], + link_with: lib_hostlistfilter +) + +# plugin +plugin = shared_library('smolblokHostlistPlugin', + [ 'plugin/plugin.cpp', + mod_qt5.preprocess(include_directories: smolbote_interfaces, moc_headers: 'plugin/plugin.h', dependencies: dep_qt5) ], + include_directories: smolbote_interfaces, + dependencies: [ dep_hostlistfilter, dep_qt5 ], + install: true, + install_dir: get_option('libdir')/'smolbote/plugins' +) + +# tests +test('rule', executable('rule', sources: 'test/rule.cpp', dependencies: [dep_qt5, dep_catch, dep_hostlistfilter]), suite: 'hostlist') + +test('filterlist', executable('filterlist', + sources: 'test/filterlist.cpp', + dependencies: [dep_qt5, dep_catch, dep_hostlistfilter]), + env: 'HOSTLIST_TXT='+meson.current_source_dir()/'test/hostlist.txt', + suite: 'hostlist' +) +test('plugin', executable('filterlist-plugin', + sources: [ 'test/plugin.cpp', 'plugin/plugin.cpp', + mod_qt5.preprocess(include_directories: smolbote_interfaces, moc_headers: 'plugin/plugin.h', dependencies: dep_qt5) ], + dependencies: [dep_qt5, dep_catch, dep_hostlistfilter]), + env: 'HOSTLIST_TXT='+meson.current_source_dir()/'test/hostlist.txt', + suite: 'hostlist' +) + +test('smolblok-load', smolblok_load, workdir: meson.build_root(), args: plugin.full_path(), suite: 'hostlist') + +# fuzzer +if meson.get_compiler('cpp').has_multi_arguments('-g', '-fsanitize=fuzzer') +executable('hostlist-fuzzer', + sources: 'filterlist.cpp', + include_directories: smolbote_interfaces, + dependencies: dep_qt5, + cpp_args: [ '-g', '-fsanitize=fuzzer', '-DFUZZER' ], + link_args: [ '-fsanitize=fuzzer' ] +) +endif diff --git a/plugins/smolblok_hostlist/plugin/plugin.cpp b/plugins/smolblok_hostlist/plugin/plugin.cpp new file mode 100644 index 0000000..28a7706 --- /dev/null +++ b/plugins/smolblok_hostlist/plugin/plugin.cpp @@ -0,0 +1,32 @@ +/* + * This file is part of smolbote. It's copyrighted by the contributors recorded + * in the version control history of the file, available from its original + * location: https://library.iserlohn-fortress.net/aqua/smolbote.git + * + * SPDX-License-Identifier: GPL-3.0 + */ + +#include "plugin.h" +#include "filterlist.h" + +FilterList* HostlistFilterPlugin::load(QIODevice &from) const +{ + if(!from.isOpen()) + return nullptr; + + auto *list = new Hostlist::Filterlist; + return list; +} + +bool HostlistFilterPlugin::parse(FilterList *list, QIODevice &from) const +{ + if(list == nullptr || !from.isOpen()) { + return false; + } + auto *l = dynamic_cast<Hostlist::Filterlist*>(list); + if(l == nullptr) { + return false; + } + return l->load(from); +} + diff --git a/plugins/smolblok_hostlist/plugin/plugin.h b/plugins/smolblok_hostlist/plugin/plugin.h new file mode 100644 index 0000000..53b5d36 --- /dev/null +++ b/plugins/smolblok_hostlist/plugin/plugin.h @@ -0,0 +1,28 @@ +/* + * This file is part of smolbote. It's copyrighted by the contributors recorded + * in the version control history of the file, available from its original + * location: https://library.iserlohn-fortress.net/aqua/smolbote.git + * + * SPDX-License-Identifier: GPL-3.0 + */ + +#ifndef SMOLBLOK_FILTER_PLUGIN_H +#define SMOLBLOK_FILTER_PLUGIN_H + +#include <smolbote/filterinterface.hpp> + +class HostlistFilterPlugin : public QObject, public FilterPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID FilterPluginIid FILE "smolblokHostlistPlugin.json") + Q_INTERFACES(FilterPlugin) + +public: + ~HostlistFilterPlugin() = default; + + FilterList *load(QIODevice &from) const override; + bool parse(FilterList *list, QIODevice &from) const override; +}; + +#endif // SMOLBLOK_FILTER_PLUGIN_H + diff --git a/plugins/smolblok_hostlist/plugin/smolblokHostlistPlugin.json b/plugins/smolblok_hostlist/plugin/smolblokHostlistPlugin.json new file mode 100644 index 0000000..aa53cdd --- /dev/null +++ b/plugins/smolblok_hostlist/plugin/smolblokHostlistPlugin.json @@ -0,0 +1,4 @@ +{ + "name": "smolblok Hostlist filter plugin", + "author": "Aqua <aqua@iserlohn-fortress.net>" +} diff --git a/plugins/smolblok_hostlist/test/filterlist.cpp b/plugins/smolblok_hostlist/test/filterlist.cpp new file mode 100644 index 0000000..4aa532b --- /dev/null +++ b/plugins/smolblok_hostlist/test/filterlist.cpp @@ -0,0 +1,29 @@ +#define CATCH_CONFIG_MAIN +#include "filterlist.h" +#include <QFile> +#include <catch2/catch.hpp> + +using namespace Hostlist; + +TEST_CASE("Hostlist") +{ + Filterlist list; + + const QString filename(qgetenv("HOSTLIST_TXT")); + REQUIRE(!filename.isEmpty()); + + QFile f(filename); + REQUIRE(f.open(QIODevice::ReadOnly | QIODevice::Text)); + + REQUIRE(list.load(f)); + f.close(); + + REQUIRE(list.count() == 4); + + REQUIRE(list.findMatch("blockeddomain.first")); + REQUIRE(list.findMatch("blockeddomain.second")); + + REQUIRE(list.findMatch("localhost.localdomain")); + + REQUIRE(!list.findMatch("other.domain")); +} diff --git a/plugins/smolblok_hostlist/test/hostlist.txt b/plugins/smolblok_hostlist/test/hostlist.txt new file mode 100644 index 0000000..a0b4e5c --- /dev/null +++ b/plugins/smolblok_hostlist/test/hostlist.txt @@ -0,0 +1,6 @@ +# This is a comment, and after it comes a blank line + +127.0.0.1 localhost.localdomain + +0.0.0.0 blockeddomain.com +0.0.0.0 blockeddomain.first blockeddomain.second diff --git a/plugins/smolblok_hostlist/test/plugin.cpp b/plugins/smolblok_hostlist/test/plugin.cpp new file mode 100644 index 0000000..fad34f2 --- /dev/null +++ b/plugins/smolblok_hostlist/test/plugin.cpp @@ -0,0 +1,27 @@ +#define CATCH_CONFIG_MAIN +#include "plugin/plugin.h" +#include <QFile> +#include <catch2/catch.hpp> + +TEST_CASE("Hostlist") +{ + HostlistFilterPlugin plugin; + + const QString filename(qgetenv("HOSTLIST_TXT")); + REQUIRE(!filename.isEmpty()); + QFile f(filename); + + // shouldn't be able to load an unopened QIODevice + REQUIRE(plugin.load(f) == nullptr); + + REQUIRE(f.open(QIODevice::ReadOnly | QIODevice::Text)); + + auto *list = plugin.load(f); + REQUIRE(list != nullptr); + f.seek(0); + REQUIRE_FALSE(plugin.parse(nullptr, f)); + REQUIRE(plugin.parse(list, f)); + f.close(); + REQUIRE_FALSE(plugin.parse(list, f)); +} + diff --git a/plugins/smolblok_hostlist/test/rule.cpp b/plugins/smolblok_hostlist/test/rule.cpp new file mode 100644 index 0000000..b5ba6e0 --- /dev/null +++ b/plugins/smolblok_hostlist/test/rule.cpp @@ -0,0 +1,57 @@ +#define CATCH_CONFIG_MAIN +#include "filterlist.h" +#include <catch2/catch.hpp> + +using namespace Hostlist; + +SCENARIO("Hostlist::Rule") +{ + GIVEN("an invalid rule") + { + const auto rule = Filterlist::parseRule("0.0.0.0 "); + REQUIRE(rule.empty()); + } + GIVEN("127.0.0.1 localhost.localdomain") + { + auto rule = Filterlist::parseRule("127.0.0.1 localhost.localdomain"); + + REQUIRE(!rule.empty()); + REQUIRE(rule.size() == 1); + + // note: you need to force it to hash a string, rather than the address itself + const auto index = qHash(QString("localhost.localdomain"), 0); + REQUIRE(rule[index].domain == "localhost.localdomain"); + REQUIRE(rule[index].redirect == "127.0.0.1"); + } + + GIVEN("0.0.0.0 blockeddomain.com") + { + auto rule = Filterlist::parseRule("0.0.0.0 blockeddomain.com"); + + REQUIRE(!rule.empty()); + REQUIRE(rule.size() == 1); + + const auto index = qHash(QString("blockeddomain.com"), 0); + REQUIRE(rule[index].domain == "blockeddomain.com"); + REQUIRE(rule[index].redirect.isEmpty()); + ; + } + + GIVEN("0.0.0.0 blockeddomain.first blockeddomain.second") + { + auto rule = Filterlist::parseRule("0.0.0.0 blockeddomain.first blockeddomain.second"); + + REQUIRE(!rule.empty()); + REQUIRE(rule.size() == 2); + { + const auto index = qHash(QString("blockeddomain.first"), 0); + REQUIRE(rule[index].domain == "blockeddomain.first"); + REQUIRE(rule[index].redirect.isEmpty()); + } + { + const auto index = qHash(QString("blockeddomain.second"), 0); + REQUIRE(rule[index].domain == "blockeddomain.second"); + REQUIRE(rule[index].redirect.isEmpty()); + } + } +} |