2022-01-20 18:27:56 +00:00
|
|
|
// This file Copyright © 2008-2022 Mnemosyne LLC.
|
2022-02-07 16:25:02 +00:00
|
|
|
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
|
2022-01-20 18:27:56 +00:00
|
|
|
// or any future license endorsed by Mnemosyne LLC.
|
|
|
|
// License text can be found in the licenses/ folder.
|
2008-03-29 18:37:07 +00:00
|
|
|
|
2022-05-15 16:32:22 +00:00
|
|
|
#include <algorithm>
|
2022-08-25 01:19:21 +00:00
|
|
|
#include <array>
|
2021-10-17 20:17:18 +00:00
|
|
|
#include <cstdio>
|
2022-07-27 21:53:39 +00:00
|
|
|
#include <cstdlib> // bsearch()
|
2022-08-27 19:05:21 +00:00
|
|
|
#include <fstream>
|
2022-09-30 23:29:26 +00:00
|
|
|
#include <memory>
|
2022-04-04 18:36:48 +00:00
|
|
|
#include <string_view>
|
2022-09-30 23:29:26 +00:00
|
|
|
#include <unordered_set>
|
2022-07-26 02:45:54 +00:00
|
|
|
#include <vector>
|
2008-03-29 18:37:07 +00:00
|
|
|
|
2022-03-14 04:43:35 +00:00
|
|
|
#include <fmt/core.h>
|
|
|
|
|
2008-03-29 18:37:07 +00:00
|
|
|
#include "transmission.h"
|
2022-03-11 21:09:22 +00:00
|
|
|
|
2008-03-30 13:22:45 +00:00
|
|
|
#include "blocklist.h"
|
2014-07-28 04:13:38 +00:00
|
|
|
#include "error.h"
|
2014-07-08 00:08:43 +00:00
|
|
|
#include "file.h"
|
2013-01-25 23:34:20 +00:00
|
|
|
#include "log.h"
|
2008-12-02 03:41:58 +00:00
|
|
|
#include "net.h"
|
2022-09-30 23:29:26 +00:00
|
|
|
#include "tr-strbuf.h"
|
2008-04-01 19:20:21 +00:00
|
|
|
#include "utils.h"
|
|
|
|
|
2022-09-30 23:29:26 +00:00
|
|
|
using namespace std::literals;
|
|
|
|
|
2008-04-01 19:20:21 +00:00
|
|
|
/***
|
|
|
|
**** PRIVATE
|
|
|
|
***/
|
2008-03-29 18:37:07 +00:00
|
|
|
|
2022-05-15 16:32:22 +00:00
|
|
|
void BlocklistFile::close()
|
2008-03-29 21:05:51 +00:00
|
|
|
{
|
2022-08-27 19:05:21 +00:00
|
|
|
rules_.clear();
|
2008-03-29 18:37:07 +00:00
|
|
|
}
|
|
|
|
|
2022-08-27 19:05:21 +00:00
|
|
|
void BlocklistFile::ensureLoaded() const
|
2008-03-29 18:37:07 +00:00
|
|
|
{
|
2022-08-27 19:05:21 +00:00
|
|
|
if (!std::empty(rules_))
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2008-03-29 18:37:07 +00:00
|
|
|
|
2022-08-27 19:05:21 +00:00
|
|
|
auto in = std::ifstream{ filename_, std::ios_base::in | std::ios_base::binary };
|
|
|
|
if (!in)
|
2008-09-23 19:11:04 +00:00
|
|
|
{
|
2022-03-14 04:43:35 +00:00
|
|
|
tr_logAddWarn(fmt::format(
|
2022-03-16 00:51:36 +00:00
|
|
|
_("Couldn't read '{path}': {error} ({error_code})"),
|
2022-08-27 19:05:21 +00:00
|
|
|
fmt::arg("path", filename_),
|
|
|
|
fmt::arg("error", tr_strerror(errno)),
|
|
|
|
fmt::arg("error_code", errno)));
|
2017-04-19 12:04:45 +00:00
|
|
|
return;
|
2008-03-29 18:37:07 +00:00
|
|
|
}
|
2008-04-05 16:45:35 +00:00
|
|
|
|
2022-09-30 17:11:52 +00:00
|
|
|
auto file_info = tr_sys_path_get_info(filename_);
|
|
|
|
auto zeroes_count = 0;
|
|
|
|
auto max_zeroes = 0;
|
|
|
|
|
|
|
|
static auto constexpr RangeSize = sizeof(AddressRange);
|
|
|
|
if (file_info->size >= RangeSize)
|
|
|
|
{
|
|
|
|
std::array<char, 40> first_struct = {};
|
|
|
|
|
|
|
|
in.read(reinterpret_cast<char*>(&first_struct), std::size(first_struct));
|
|
|
|
in.clear();
|
|
|
|
in.seekg(0, std::ios::beg);
|
|
|
|
|
|
|
|
for (auto const struct_byte : first_struct)
|
|
|
|
{
|
|
|
|
if (struct_byte != 0)
|
|
|
|
{
|
|
|
|
zeroes_count = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
++zeroes_count;
|
|
|
|
|
|
|
|
if (zeroes_count > max_zeroes)
|
|
|
|
{
|
|
|
|
max_zeroes = zeroes_count;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for old blocklist file format
|
|
|
|
// Old struct size was 8 bytes (2 IPv4), new struct size is 40 bytes (2 IPv4, 2 IPv6)
|
|
|
|
//
|
|
|
|
// If we encounter less than 4 continuous bytes containing 0 we are using old file format
|
|
|
|
// (as the new format guarantees at least 2 empty IPv4 OR 2 empty IPv6)
|
|
|
|
// If we confirm using old style convert to new style and rewrite blocklist file
|
|
|
|
if ((file_info->size >= 40 && max_zeroes < 4) || (file_info->size % 8 == 0 && file_info->size % 40 != 0))
|
|
|
|
{
|
|
|
|
auto range = AddressRange{};
|
|
|
|
while (in.read(reinterpret_cast<char*>(&range), 8))
|
|
|
|
{
|
|
|
|
rules_.emplace_back(range);
|
|
|
|
}
|
|
|
|
|
|
|
|
tr_logAddInfo(_("Rewriting old blocklist file format to new format"));
|
|
|
|
|
|
|
|
RewriteBlocklistFile();
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
2008-09-23 19:11:04 +00:00
|
|
|
{
|
2022-09-30 17:11:52 +00:00
|
|
|
auto range = AddressRange{};
|
|
|
|
while (in.read(reinterpret_cast<char*>(&range), sizeof(range)))
|
|
|
|
{
|
|
|
|
rules_.emplace_back(range);
|
|
|
|
}
|
2008-03-29 18:37:07 +00:00
|
|
|
}
|
|
|
|
|
2022-03-14 04:43:35 +00:00
|
|
|
tr_logAddInfo(fmt::format(
|
2022-08-27 19:05:21 +00:00
|
|
|
ngettext("Blocklist '{path}' has {count} entry", "Blocklist '{path}' has {count} entries", std::size(rules_)),
|
|
|
|
fmt::arg("path", tr_sys_path_basename(filename_)),
|
|
|
|
fmt::arg("count", std::size(rules_))));
|
2008-03-29 18:37:07 +00:00
|
|
|
}
|
|
|
|
|
2008-03-30 13:22:45 +00:00
|
|
|
/***
|
2008-04-01 19:20:21 +00:00
|
|
|
**** PACKAGE-VISIBLE
|
2008-03-30 13:22:45 +00:00
|
|
|
***/
|
|
|
|
|
2022-09-30 23:29:26 +00:00
|
|
|
std::vector<std::unique_ptr<BlocklistFile>> BlocklistFile::loadBlocklists(
|
|
|
|
std::string_view const config_dir,
|
|
|
|
bool const is_enabled)
|
|
|
|
{
|
|
|
|
auto loadme = std::unordered_set<std::string>{};
|
|
|
|
auto working_set = std::vector<std::unique_ptr<BlocklistFile>>{};
|
|
|
|
|
|
|
|
/* walk the blocklist directory... */
|
|
|
|
auto const dirname = tr_pathbuf{ config_dir, "/blocklists"sv };
|
|
|
|
auto const odir = tr_sys_dir_open(dirname);
|
|
|
|
|
|
|
|
if (odir == TR_BAD_SYS_DIR)
|
|
|
|
{
|
|
|
|
return working_set;
|
|
|
|
}
|
|
|
|
|
|
|
|
char const* name = nullptr;
|
|
|
|
while ((name = tr_sys_dir_read_name(odir)) != nullptr)
|
|
|
|
{
|
|
|
|
auto load = std::string{};
|
|
|
|
|
|
|
|
if (name[0] == '.') /* ignore dotfiles */
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (auto const path = tr_pathbuf{ dirname, '/', name }; tr_strvEndsWith(path, ".bin"sv))
|
|
|
|
{
|
|
|
|
load = path;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
auto const binname = tr_pathbuf{ dirname, '/', name, ".bin"sv };
|
|
|
|
|
|
|
|
if (auto const bininfo = tr_sys_path_get_info(binname); !bininfo)
|
|
|
|
{
|
|
|
|
// create it
|
|
|
|
auto b = BlocklistFile{ binname, is_enabled };
|
|
|
|
if (auto const n = b.setContent(path); n > 0)
|
|
|
|
{
|
|
|
|
load = binname;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (auto const pathinfo = tr_sys_path_get_info(path);
|
|
|
|
pathinfo && pathinfo->last_modified_at >= bininfo->last_modified_at)
|
|
|
|
{
|
|
|
|
// update it
|
|
|
|
auto const old = tr_pathbuf{ binname, ".old"sv };
|
|
|
|
tr_sys_path_remove(old);
|
|
|
|
tr_sys_path_rename(binname, old);
|
|
|
|
|
|
|
|
BlocklistFile b(binname, is_enabled);
|
|
|
|
|
|
|
|
if (b.setContent(path) > 0)
|
|
|
|
{
|
|
|
|
tr_sys_path_remove(old);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tr_sys_path_remove(binname);
|
|
|
|
tr_sys_path_rename(old, binname);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!std::empty(load))
|
|
|
|
{
|
|
|
|
loadme.emplace(load);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::transform(
|
|
|
|
std::begin(loadme),
|
|
|
|
std::end(loadme),
|
|
|
|
std::back_inserter(working_set),
|
|
|
|
[&is_enabled](auto const& path) { return std::make_unique<BlocklistFile>(path.c_str(), is_enabled); });
|
|
|
|
|
|
|
|
/* cleanup */
|
|
|
|
tr_sys_dir_close(odir);
|
|
|
|
|
|
|
|
return working_set;
|
|
|
|
}
|
|
|
|
|
2022-05-15 16:32:22 +00:00
|
|
|
bool BlocklistFile::hasAddress(tr_address const& addr)
|
2008-03-30 13:22:45 +00:00
|
|
|
{
|
2022-05-15 16:32:22 +00:00
|
|
|
TR_ASSERT(tr_address_is_valid(&addr));
|
2008-03-29 23:12:34 +00:00
|
|
|
|
2022-09-30 17:11:52 +00:00
|
|
|
if (!is_enabled_)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-03-29 23:12:34 +00:00
|
|
|
|
2022-05-15 16:32:22 +00:00
|
|
|
ensureLoaded();
|
2009-12-14 18:47:45 +00:00
|
|
|
|
2022-08-27 19:05:21 +00:00
|
|
|
if (std::empty(rules_))
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-03-29 18:37:07 +00:00
|
|
|
|
2022-09-30 17:11:52 +00:00
|
|
|
if (addr.isIPv4())
|
|
|
|
{
|
|
|
|
auto const needle = ntohl(addr.addr.addr4.s_addr);
|
|
|
|
|
|
|
|
// std::binary_search works differently and requires a less-than comparison
|
|
|
|
// and two arguments of the same type. std::bsearch is the right choice.
|
|
|
|
auto const* range = static_cast<AddressRange const*>(std::bsearch(
|
|
|
|
&needle,
|
|
|
|
std::data(rules_),
|
|
|
|
std::size(rules_),
|
|
|
|
sizeof(AddressRange),
|
|
|
|
AddressRange::compareIPv4AddressToRange));
|
|
|
|
|
|
|
|
return range != nullptr;
|
|
|
|
}
|
2008-03-29 18:37:07 +00:00
|
|
|
|
2022-09-30 17:11:52 +00:00
|
|
|
if (addr.isIPv6())
|
|
|
|
{
|
|
|
|
auto const needle = addr.addr.addr6;
|
|
|
|
|
|
|
|
auto const* range = static_cast<AddressRange const*>(std::bsearch(
|
|
|
|
&needle,
|
|
|
|
std::data(rules_),
|
|
|
|
std::size(rules_),
|
|
|
|
sizeof(AddressRange),
|
|
|
|
AddressRange::compareIPv6AddressToRange));
|
2008-03-29 21:05:51 +00:00
|
|
|
|
2022-09-30 17:11:52 +00:00
|
|
|
return range != nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2008-03-29 21:05:51 +00:00
|
|
|
}
|
2008-03-29 18:37:07 +00:00
|
|
|
|
2010-05-31 11:38:36 +00:00
|
|
|
/*
|
2022-09-30 17:11:52 +00:00
|
|
|
* P2P plaintext format: "comment:x.x.x.x-y.y.y.y" / "comment:x:x:x:x:x:x:x:x-x:x:x:x:x:x:x:x"
|
|
|
|
* https://web.archive.org/web/20100328075307/http://wiki.phoenixlabs.org/wiki/P2P_Format
|
2022-04-21 14:28:38 +00:00
|
|
|
* https://en.wikipedia.org/wiki/PeerGuardian#P2P_plaintext_format
|
2010-05-31 11:38:36 +00:00
|
|
|
*/
|
2022-09-30 17:11:52 +00:00
|
|
|
bool BlocklistFile::parseLine1(std::string_view line, struct AddressRange* range)
|
2010-05-31 11:38:36 +00:00
|
|
|
{
|
2022-04-04 18:36:48 +00:00
|
|
|
// remove leading "comment:"
|
|
|
|
auto pos = line.find(':');
|
|
|
|
if (pos == std::string_view::npos)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2022-04-04 18:36:48 +00:00
|
|
|
line = line.substr(pos + 1);
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2022-04-04 18:36:48 +00:00
|
|
|
// parse the leading 'x.x.x.x'
|
|
|
|
pos = line.find('-');
|
|
|
|
if (pos == std::string_view::npos)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2022-07-25 22:25:55 +00:00
|
|
|
if (auto const addr = tr_address::fromString(line.substr(0, pos)); addr)
|
2022-04-04 18:36:48 +00:00
|
|
|
{
|
2022-09-30 17:11:52 +00:00
|
|
|
if (addr->isIPv4())
|
|
|
|
{
|
|
|
|
range->begin_ = ntohl(addr->addr.addr4.s_addr);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
range->begin6_ = addr->addr.addr6;
|
|
|
|
}
|
2022-04-04 18:36:48 +00:00
|
|
|
}
|
|
|
|
else
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2022-04-04 18:36:48 +00:00
|
|
|
line = line.substr(pos + 1);
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2022-04-04 18:36:48 +00:00
|
|
|
// parse the trailing 'y.y.y.y'
|
2022-07-25 22:25:55 +00:00
|
|
|
if (auto const addr = tr_address::fromString(line); addr)
|
2022-04-04 18:36:48 +00:00
|
|
|
{
|
2022-09-30 17:11:52 +00:00
|
|
|
if (addr->isIPv4())
|
|
|
|
{
|
|
|
|
range->end_ = ntohl(addr->addr.addr4.s_addr);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
range->end6_ = addr->addr.addr6;
|
|
|
|
}
|
2022-04-04 18:36:48 +00:00
|
|
|
}
|
|
|
|
else
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2010-05-31 11:38:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2022-04-04 18:36:48 +00:00
|
|
|
* DAT / eMule format: "000.000.000.000 - 000.255.255.255 , 000 , invalid ip"a
|
|
|
|
* https://sourceforge.net/p/peerguardian/wiki/dev-blocklist-format-dat/
|
2010-05-31 11:38:36 +00:00
|
|
|
*/
|
2022-09-30 17:11:52 +00:00
|
|
|
bool BlocklistFile::parseLine2(std::string_view line, struct AddressRange* range)
|
2010-05-31 11:38:36 +00:00
|
|
|
{
|
2022-04-04 18:36:48 +00:00
|
|
|
static auto constexpr Delim1 = std::string_view{ " - " };
|
|
|
|
static auto constexpr Delim2 = std::string_view{ " , " };
|
|
|
|
|
|
|
|
auto pos = line.find(Delim1);
|
|
|
|
if (pos == std::string_view::npos)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-07-25 22:25:55 +00:00
|
|
|
if (auto const addr = tr_address::fromString(line.substr(0, pos)); addr)
|
2022-04-04 18:36:48 +00:00
|
|
|
{
|
2022-07-25 22:25:55 +00:00
|
|
|
range->begin_ = ntohl(addr->addr.addr4.s_addr);
|
2022-04-04 18:36:48 +00:00
|
|
|
}
|
|
|
|
else
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-04-04 18:36:48 +00:00
|
|
|
line = line.substr(pos + std::size(Delim1));
|
|
|
|
pos = line.find(Delim2);
|
|
|
|
if (pos == std::string_view::npos)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-07-25 22:25:55 +00:00
|
|
|
if (auto const addr = tr_address::fromString(line.substr(0, pos)); addr)
|
2022-04-04 18:36:48 +00:00
|
|
|
{
|
2022-07-25 22:25:55 +00:00
|
|
|
range->end_ = ntohl(addr->addr.addr4.s_addr);
|
2022-04-04 18:36:48 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
|
|
|
|
return true;
|
2010-05-31 11:38:36 +00:00
|
|
|
}
|
|
|
|
|
2018-10-13 21:20:40 +00:00
|
|
|
/*
|
2022-09-30 17:11:52 +00:00
|
|
|
* CIDR notation: "0.0.0.0/8", "::/64"
|
2018-10-13 21:20:40 +00:00
|
|
|
* https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing#CIDR_notation
|
|
|
|
*/
|
2022-09-30 17:11:52 +00:00
|
|
|
bool BlocklistFile::parseLine3(char const* line, AddressRange* range)
|
2019-02-10 11:05:16 +00:00
|
|
|
{
|
2022-08-25 01:19:21 +00:00
|
|
|
auto ip = std::array<unsigned int, 4>{};
|
2021-11-03 03:55:43 +00:00
|
|
|
unsigned int pflen = 0;
|
|
|
|
uint32_t ip_u = 0;
|
2018-10-13 21:20:40 +00:00
|
|
|
uint32_t mask = 0xffffffff;
|
|
|
|
|
2022-08-25 01:19:21 +00:00
|
|
|
// NOLINTNEXTLINE readability-container-data-pointer
|
2021-08-15 09:41:48 +00:00
|
|
|
if (sscanf(line, "%u.%u.%u.%u/%u", TR_ARG_TUPLE(&ip[0], &ip[1], &ip[2], &ip[3]), &pflen) != 5)
|
2019-02-10 11:05:16 +00:00
|
|
|
{
|
2018-10-13 21:20:40 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-02-10 11:05:16 +00:00
|
|
|
if (pflen > 32 || ip[0] > 0xff || ip[1] > 0xff || ip[2] > 0xff || ip[3] > 0xff)
|
|
|
|
{
|
2018-10-13 21:20:40 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-10-17 13:41:19 +00:00
|
|
|
/* this is host order */
|
2018-10-14 16:44:01 +00:00
|
|
|
mask <<= 32 - pflen;
|
2018-10-13 21:20:40 +00:00
|
|
|
ip_u = ip[0] << 24 | ip[1] << 16 | ip[2] << 8 | ip[3];
|
|
|
|
|
|
|
|
/* fill the non-prefix bits the way we need it */
|
2022-05-15 16:32:22 +00:00
|
|
|
range->begin_ = ip_u & mask;
|
|
|
|
range->end_ = ip_u | (~mask);
|
2018-10-13 21:20:40 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-09-30 17:11:52 +00:00
|
|
|
bool BlocklistFile::parseLine(char const* line, AddressRange* range)
|
2010-05-31 11:38:36 +00:00
|
|
|
{
|
2018-10-13 21:20:40 +00:00
|
|
|
return parseLine1(line, range) || parseLine2(line, range) || parseLine3(line, range);
|
2010-05-31 11:38:36 +00:00
|
|
|
}
|
|
|
|
|
2022-09-30 17:11:52 +00:00
|
|
|
bool BlocklistFile::compareAddressRangesByFirstAddress(AddressRange const& a, AddressRange const& b)
|
2011-02-13 16:14:31 +00:00
|
|
|
{
|
2022-09-30 17:11:52 +00:00
|
|
|
if (a.begin_ == 0 && a.end_ == 0)
|
|
|
|
{
|
|
|
|
// IPv6
|
|
|
|
return (memcmp(a.begin6_.s6_addr, b.begin6_.s6_addr, sizeof(a.begin6_.s6_addr)) < 0);
|
|
|
|
}
|
|
|
|
|
2022-05-15 16:32:22 +00:00
|
|
|
return a.begin_ < b.begin_;
|
2011-02-13 16:14:31 +00:00
|
|
|
}
|
|
|
|
|
2022-05-15 16:32:22 +00:00
|
|
|
size_t BlocklistFile::setContent(char const* filename)
|
2008-03-29 18:37:07 +00:00
|
|
|
{
|
2021-09-15 00:18:09 +00:00
|
|
|
if (filename == nullptr)
|
2008-09-23 19:11:04 +00:00
|
|
|
{
|
2022-08-27 19:05:21 +00:00
|
|
|
return {};
|
2008-03-29 18:37:07 +00:00
|
|
|
}
|
|
|
|
|
2022-08-27 19:05:21 +00:00
|
|
|
auto in = std::ifstream{ filename };
|
|
|
|
if (!in.is_open())
|
2008-09-23 19:11:04 +00:00
|
|
|
{
|
2022-03-14 04:43:35 +00:00
|
|
|
tr_logAddWarn(fmt::format(
|
2022-03-16 00:51:36 +00:00
|
|
|
_("Couldn't read '{path}': {error} ({error_code})"),
|
2022-03-14 04:43:35 +00:00
|
|
|
fmt::arg("path", filename),
|
2022-08-27 19:05:21 +00:00
|
|
|
fmt::arg("error", tr_strerror(errno)),
|
|
|
|
fmt::arg("error_code", errno)));
|
|
|
|
return {};
|
2008-03-29 18:37:07 +00:00
|
|
|
}
|
|
|
|
|
2022-08-27 19:05:21 +00:00
|
|
|
auto line = std::string{};
|
|
|
|
auto line_number = size_t{ 0U };
|
2022-09-30 17:11:52 +00:00
|
|
|
auto ranges = std::vector<AddressRange>{};
|
2022-08-27 19:05:21 +00:00
|
|
|
while (std::getline(in, line))
|
2008-09-23 19:11:04 +00:00
|
|
|
{
|
2022-08-27 19:05:21 +00:00
|
|
|
++line_number;
|
2022-09-30 17:11:52 +00:00
|
|
|
auto range = AddressRange{};
|
2022-08-27 19:05:21 +00:00
|
|
|
if (!parseLine(line.c_str(), &range))
|
2010-04-30 22:58:03 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
/* don't try to display the actual lines - it causes issues */
|
2022-08-27 19:05:21 +00:00
|
|
|
tr_logAddWarn(fmt::format(_("Couldn't parse line: '{line}'"), fmt::arg("line", line_number)));
|
2017-04-19 12:04:45 +00:00
|
|
|
continue;
|
2010-04-30 22:58:03 +00:00
|
|
|
}
|
2022-05-15 16:32:22 +00:00
|
|
|
ranges.push_back(range);
|
2008-03-29 18:37:07 +00:00
|
|
|
}
|
2022-08-27 19:05:21 +00:00
|
|
|
in.close();
|
2008-03-29 18:37:07 +00:00
|
|
|
|
2022-08-27 19:05:21 +00:00
|
|
|
if (std::empty(ranges))
|
2008-06-11 16:15:45 +00:00
|
|
|
{
|
2022-08-27 19:05:21 +00:00
|
|
|
return {};
|
|
|
|
}
|
2011-02-13 16:14:31 +00:00
|
|
|
|
2022-11-02 14:41:05 +00:00
|
|
|
//separate before sorting
|
|
|
|
auto ipv4_ranges = std::vector<AddressRange>{};
|
|
|
|
auto ipv6_ranges = std::vector<AddressRange>{};
|
|
|
|
|
|
|
|
for (auto const& range : ranges)
|
|
|
|
{
|
|
|
|
if (range.begin_ == 0 && range.end_ == 0)
|
|
|
|
{
|
|
|
|
// IPv6
|
|
|
|
ipv6_ranges.emplace_back(range);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ipv4_ranges.emplace_back(range);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::sort(std::begin(ipv4_ranges), std::end(ipv4_ranges), BlocklistFile::compareAddressRangesByFirstAddress);
|
|
|
|
std::sort(std::begin(ipv6_ranges), std::end(ipv6_ranges), BlocklistFile::compareAddressRangesByFirstAddress);
|
2013-01-21 17:39:20 +00:00
|
|
|
|
2022-11-02 14:41:05 +00:00
|
|
|
// combine sorted
|
|
|
|
ranges.clear();
|
|
|
|
ranges.insert(ranges.end(), ipv4_ranges.begin(), ipv4_ranges.end());
|
|
|
|
ranges.insert(ranges.end(), ipv6_ranges.begin(), ipv6_ranges.end());
|
|
|
|
|
|
|
|
size_t keep = 0; // index in ranges
|
2022-08-27 19:05:21 +00:00
|
|
|
|
|
|
|
// merge
|
|
|
|
for (auto const& range : ranges)
|
|
|
|
{
|
2022-09-30 17:11:52 +00:00
|
|
|
if (range.begin_ == 0 && range.end_ == 0)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2022-09-30 17:11:52 +00:00
|
|
|
// IPv6
|
|
|
|
if (memcmp(ranges[keep].end6_.s6_addr, range.begin6_.s6_addr, sizeof(range.begin6_.s6_addr)) < 0)
|
|
|
|
{
|
|
|
|
ranges[++keep] = range;
|
|
|
|
}
|
|
|
|
else if (memcmp(ranges[keep].end6_.s6_addr, range.end6_.s6_addr, sizeof(range.begin6_.s6_addr)) < 0)
|
|
|
|
{
|
|
|
|
ranges[keep].end6_ = range.end6_;
|
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2022-09-30 17:11:52 +00:00
|
|
|
else
|
2022-08-27 19:05:21 +00:00
|
|
|
{
|
2022-09-30 17:11:52 +00:00
|
|
|
if (ranges[keep].end_ < range.begin_)
|
|
|
|
{
|
|
|
|
ranges[++keep] = range;
|
|
|
|
}
|
|
|
|
else if (ranges[keep].end_ < range.end_)
|
|
|
|
{
|
|
|
|
ranges[keep].end_ = range.end_;
|
|
|
|
}
|
2022-08-27 19:05:21 +00:00
|
|
|
}
|
|
|
|
}
|
2013-01-21 17:39:20 +00:00
|
|
|
|
2022-08-27 19:05:21 +00:00
|
|
|
TR_ASSERT_MSG(keep + 1 <= std::size(ranges), "Can shrink `ranges` or leave intact, but not grow");
|
|
|
|
ranges.resize(keep + 1);
|
2011-02-13 16:14:31 +00:00
|
|
|
|
2017-06-08 07:24:12 +00:00
|
|
|
#ifdef TR_ENABLE_ASSERTS
|
2022-08-27 19:05:21 +00:00
|
|
|
assertValidRules(ranges);
|
2011-02-13 16:14:31 +00:00
|
|
|
#endif
|
2022-08-27 19:05:21 +00:00
|
|
|
|
|
|
|
auto out = std::ofstream{ filename_, std::ios_base::out | std::ios_base::trunc | std::ios_base::binary };
|
|
|
|
if (!out.is_open())
|
|
|
|
{
|
|
|
|
tr_logAddWarn(fmt::format(
|
|
|
|
_("Couldn't read '{path}': {error} ({error_code})"),
|
|
|
|
fmt::arg("path", filename_),
|
|
|
|
fmt::arg("error", tr_strerror(errno)),
|
|
|
|
fmt::arg("error_code", errno)));
|
|
|
|
return {};
|
2011-02-13 16:14:31 +00:00
|
|
|
}
|
|
|
|
|
2022-09-30 17:11:52 +00:00
|
|
|
if (!out.write(reinterpret_cast<char const*>(ranges.data()), std::size(ranges) * sizeof(AddressRange)))
|
2013-01-21 17:39:20 +00:00
|
|
|
{
|
2022-03-14 04:43:35 +00:00
|
|
|
tr_logAddWarn(fmt::format(
|
2022-03-16 00:51:36 +00:00
|
|
|
_("Couldn't save '{path}': {error} ({error_code})"),
|
2022-08-27 19:05:21 +00:00
|
|
|
fmt::arg("path", filename_),
|
|
|
|
fmt::arg("error", tr_strerror(errno)),
|
|
|
|
fmt::arg("error_code", errno)));
|
2013-01-21 17:39:20 +00:00
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
else
|
2013-01-21 17:39:20 +00:00
|
|
|
{
|
2022-03-14 04:43:35 +00:00
|
|
|
tr_logAddInfo(fmt::format(
|
2022-08-27 19:05:21 +00:00
|
|
|
ngettext("Blocklist '{path}' has {count} entry", "Blocklist '{path}' has {count} entries", std::size(rules_)),
|
|
|
|
fmt::arg("path", tr_sys_path_basename(filename_)),
|
|
|
|
fmt::arg("count", std::size(rules_))));
|
2008-06-11 16:15:45 +00:00
|
|
|
}
|
|
|
|
|
2022-08-27 19:05:21 +00:00
|
|
|
out.close();
|
2008-03-30 00:00:28 +00:00
|
|
|
|
2022-08-27 19:05:21 +00:00
|
|
|
close();
|
|
|
|
ensureLoaded();
|
|
|
|
return std::size(rules_);
|
2008-03-29 18:37:07 +00:00
|
|
|
}
|
2022-05-15 16:32:22 +00:00
|
|
|
|
2022-09-30 17:11:52 +00:00
|
|
|
void BlocklistFile::RewriteBlocklistFile() const
|
|
|
|
{
|
|
|
|
auto out = std::ofstream{ filename_, std::ios_base::out | std::ios_base::trunc | std::ios_base::binary };
|
|
|
|
if (!out.is_open())
|
|
|
|
{
|
|
|
|
tr_logAddWarn(fmt::format(
|
|
|
|
_("Couldn't read '{path}': {error} ({error_code})"),
|
|
|
|
fmt::arg("path", filename_),
|
|
|
|
fmt::arg("error", tr_strerror(errno)),
|
|
|
|
fmt::arg("error_code", errno)));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!out.write(reinterpret_cast<char const*>(rules_.data()), std::size(rules_) * sizeof(AddressRange)))
|
|
|
|
{
|
|
|
|
tr_logAddWarn(fmt::format(
|
|
|
|
_("Couldn't save '{path}': {error} ({error_code})"),
|
|
|
|
fmt::arg("path", filename_),
|
|
|
|
fmt::arg("error", tr_strerror(errno)),
|
|
|
|
fmt::arg("error_code", errno)));
|
|
|
|
}
|
|
|
|
|
|
|
|
out.close();
|
|
|
|
ensureLoaded();
|
|
|
|
}
|
|
|
|
|
2022-05-15 16:32:22 +00:00
|
|
|
#ifdef TR_ENABLE_ASSERTS
|
2022-09-30 17:11:52 +00:00
|
|
|
void BlocklistFile::assertValidRules(std::vector<AddressRange> const& ranges)
|
2022-05-15 16:32:22 +00:00
|
|
|
{
|
|
|
|
for (auto const& r : ranges)
|
|
|
|
{
|
2022-09-30 17:11:52 +00:00
|
|
|
if (r.begin_ == 0 && r.end_ == 0)
|
|
|
|
{
|
|
|
|
TR_ASSERT(memcmp(r.begin6_.s6_addr, r.end6_.s6_addr, sizeof(r.begin6_.s6_addr)) <= 0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TR_ASSERT(r.begin_ <= r.end_);
|
|
|
|
}
|
2022-05-15 16:32:22 +00:00
|
|
|
}
|
|
|
|
|
2022-10-02 22:56:58 +00:00
|
|
|
auto ranges_ipv4 = std::vector<AddressRange>{};
|
|
|
|
auto ranges_ipv6 = std::vector<AddressRange>{};
|
2022-09-30 17:11:52 +00:00
|
|
|
|
|
|
|
for (size_t i = 0; i < std::size(ranges); i++)
|
2022-05-15 16:32:22 +00:00
|
|
|
{
|
2022-09-30 17:11:52 +00:00
|
|
|
if (ranges[i].begin_ == 0 && ranges[i].end_ == 0)
|
|
|
|
{
|
2022-10-02 22:56:58 +00:00
|
|
|
ranges_ipv6.emplace_back(ranges[i]);
|
2022-09-30 17:11:52 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-10-02 22:56:58 +00:00
|
|
|
ranges_ipv4.emplace_back(ranges[i]);
|
2022-09-30 17:11:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-02 14:41:05 +00:00
|
|
|
TR_ASSERT(is_sorted(std::begin(ranges_ipv4), std::end(ranges_ipv4), BlocklistFile::compareAddressRangesByFirstAddress));
|
|
|
|
TR_ASSERT(is_sorted(std::begin(ranges_ipv6), std::end(ranges_ipv6), BlocklistFile::compareAddressRangesByFirstAddress));
|
2022-05-15 16:32:22 +00:00
|
|
|
}
|
|
|
|
#endif
|