Страница 1 из 1

Идея: фильтрация "лишних" правил по критерию вхождения доменного имени правила в доменное имя другого правила

Добавлено: Вс дек 27, 2020 18:20
bogdan108
В списках для группы banner есть такие правила:

Код: Выделить всё

adriver.ru
ad.adriver.ru
ad.adriver.ru/cgi-bin
ad.adriver.ru/cgi-bin/click.cgi
ad.adriver.ru/cgi-bin/erle.cgi
ad.adriver.ru/cgi-bin/rle.cgi
content.adriver.ru/plugins/autoUpdate.adriver.js
Проблемма:
Все эти правила эквивалентны правилу adriver.ru, так как если есть правило adriver.ru, то и домен и все его поддомены будут заблокированы. Т.е. кроме первого - все эти правила являются лишним балластом.
Все они перекрываются правилом adriver.ru. В итоге можно сэекономить длину списка, по которому система фильтрации ищет блокируемые домены и как следствие - сократить время на поиск по этому списку.
В общем, чем меньше правил в списке - тем быстрее работает система фильтрации.

Что хочу:
- экспортировать правило adriver.ru;
- игнорировать остальные правила, чьи домены являются поддоменами для adriver.ru.

Вариант решения "в лоб": добавить к "ненужным" правилам ответ "нет" через форму добавления новых правил.
Как следствие, при экспорте скрипт будет считать, что эти правила не входят в соответствующие группы и проигнорирует их. А я получу оптимизированный список.
Диллема: для других пользователей мои ответы "нет", для домена явно рекламного содержания, будут по меньшей мере "странными" и мой ник определят в "список игнорируемых пользователей".

Вариант решения "правильный": создать свой собственный список игнорируемых правил и при экспорте из DBL игнорировать правила из этого списка. В системе останутся ответы, которые означают именно то, что от них ожидается:
- да - "по адресу URI, которое соответствует правилу, расположено содержимое, характерное для указанной группы";
- нет - "по адресу URI, которое соответствует правилу, отсутствует содержимое, характерное для указанной группы".
Недостаток: это уже своя "система фильтрации списка фильтров" - некий сторонний костыль.

Вариант решения "идеальный": прямо в системе DBL ставить метки "игнорировать" для необходимых правил.
Чем больше пользователей отметят правило как "игнорировать", тем менее значимыми будут эти правила.
При экспорте:
- игнорировать правило, если у поравила есть метка "игнорировать" от себя;
- игнорировать правило, если у поравила есть метка "игнорировать" от более чем 20 других пользоватлей.
Спустя несколько лет отмечать такие "игнорируемые" правила как удалённые и больше не использовать.
Недостаток: данные в системе потенциально расширяются в экспоненциальной прогрессии (количество правил * количество пользователей), что чревато лишним расходом памяти при множественных повторениях одинаковых записей для разных пользователей.

Вариант решения самый идеальный (по моему мнению):
- для каждого правила создать атрибуты (добавить колонки в таблицу Rules):
  • ruleIsDomain - флаг, если 1 - текст правила является именем домена (rule is domain). Значение по умолчанию - 0;
  • Domain - имя домена из текущего правила;
  • RootRuleId - идентификатор правила, в домен из которого входит домен данного правила.
- создать функции:
  • getDomainFromRule(rule) - возвращает имя домена из Rule (уверен, для этого в Perl есть стандартные библиотеки обработки URI, если домен не указан - возвращает пустую строку);
  • expandRule(id) - анализирует Rule и заполняет атрибуты ruleIsDomain/Domain/RootRuleId;
  • expandAllRules() - делает expandRuleдля всех правил.
  • findRootId(domain) - ищет идентификатор правила, в домен которого входит domain.
- вначале выполнить expandAllRules(), чтобы заполнить данные;
- при добавлении нового правила - выполнять для него expandRule(Id);
- при экспорте SQL > XML, в запись <record> добавлять атрибут <rootid> и <ruleisdomain>;
- в скрипте "dbl_expand":
  • перед анализом XML-файла, вначале пройтись по этому файлу и извлечь Rule тех правил, у которых RuleIsDomain=1. Это будет "эталонный список балластных правил";
  • перед записью правила в файл "+urls", проверить домен из Rule на вхождение в домены из "эталонного списка балластных правил". Если домен из правила или родительский домен из правила есть в этом списке - игнорировать такое правило и не заносить в файл "+urls".
Вот рабочий кусок кода:

Код: Выделить всё

proc ballastRules::RegisterInvalidDomainNameInRule {rule} {
  return [sql "INSERT OR IGNORE INTO _invalidDomainName (Rule) VALUES ('$rule'); SELECT Id FROM _invalidDomainName WHERE Rule='$rule';"]
}
proc ballastRules::getDomainFromRule {rule} {
  set rule [string trimleft $rule "*."]
  if {[catch {array set vector [uri::split $rule]} res]} {
    set invalidRuleId [RegisterInvalidDomainNameInRule $rule]
    puts "WARN: [namespace current]::getDomainFromRule: fail get domain name from rule. InvalidRuleId = $invalidRuleId, Rule = {$rule}"
    return ""
  }
  return $vector(host)
}
proc ballastRules::ruleIsIp {rule} {
  if {[::ip::version $rule] != -1} {return 1}
  return 0
}
proc ballastRules::FindRootId {domain} {
  set hostsList [split $domain "."]; # разделяем домен на список хостов
  set rootId ""; # вначале идентификатор правила пуст
  set rootDomain "";

  for {set i 0} {$i < [llength $hostsList]} {incr i} {
    set rootHosts [lrange $hostsList $i end]; # берем вначале все хосты, потом все кроме первого, далее все кроме первых двух и т.д.
    set domain [join $rootHosts "."]; # собираем список хостов в имя домена, разделяя имена хостов символом "точка"
    set sqlCmd "SELECT Id, Domain FROM Record WHERE RuleIsDomain=1 AND Domain='$domain' LIMIT 1 ;"
    lassign [sql $sqlCmd] rootId rootDomain; # ищем только среди правил, у которых Domain=Rule
    if {$rootId ne ""} break; # останавливаем поиск как только нашли первое совпадение
  }

  return [list $rootId $rootDomain]
}
proc ballastRules::ExpandRuleInit {id Rule} {
  if {[ruleIsIp $Rule]} {return {}}; # если Rule является IP-адресом - пропускаем обработку
  set domain [getDomainFromRule $Rule]; # вытаскиваем имя домена из правила
  if {$domain eq ""} return; # если в Rule отсутствует имя домена - пропускаем такое правило

  set RuleIsDomain 0; if {$Rule eq $domain} {set RuleIsDomain 1}; # если строка Rule равна строке $domain - выставляем флаг
  sql "UPDATE Record SET Domain='$domain', RuleIsDomain=$RuleIsDomain, RootRuleId=0 WHERE Id=$id;";

  return $domain
}
proc ballastRules::ExpandRuleRoot {id domain} {
  if {$domain eq {}} return;

  lassign [FindRootId $domain] RootId RootDomain; # ищем идентификатор правила, в чей домен входит домен из текущего правила
  if {$RootId ne ""} { # обновляем атрибут в базе только если нашли Id родительского правила
    sql "UPDATE Record SET RootRuleId=$RootId WHERE Id=$id;"; # обновляем атрибут RootRuleId в базе
    puts [format {%7s | %25s | %7s | %s} $RootId $RootDomain $id $domain]
  }
}
proc ballastRules::expandRule {id} {
  set Domain [ExpandRuleInit $id]; # заполнить атрибуты RuleIsDomain и Domain
  ExpandRuleRoot $id $Domain; # заполнить атрибут RootRuleId
}
proc ballastRules::expandAllRules {args} {
  set qWHERE "RootRuleId=0 AND"
  if {[lsearch $args "-force"] != -1} {set qWHERE ""}
  set qListIdSelector "(SELECT Id FROM List WHERE TypeID = (SELECT Id FROM lst_ListTypes WHERE name='urls'))";
  set sqlCmd "SELECT Id,Rule FROM Record WHERE ${qWHERE} ListId in $qListIdSelector;"
  set sqlCmd "SELECT Id, Rule, Domain FROM Record WHERE ListId in $qListIdSelector AND ruleIsDomain=0 AND Domain != '';"

  foreach {Id Rule Domain} [sql $sqlCmd] {
    if {[ruleIsIp $Rule]} continue
    ExpandRuleRoot $Id $Domain;
  }
}
proc ballastRules::freeRootId {id} {
  # после удаления правила, необходимо удалить его из указателей на него в атрибуте RootId в других правилах
  sql "UPDATE Record SET RuleId=0 WHERE RuleId=$id;"
}
На выходе получается система автоматической оптимизации списка правил, которая исключит из выходящего списка те правила, которые являются балластом по той причине, что никогда не будут применены, так как они перекрываются другими правилами.
Такая система:
- обрабатывает только правила типа "*.urls";
- игнорирует правила, у которых не указано имя домена (правила типа "/banner.gif");
- фильтрует только правила с таким доменом, который входит в имя другого домена, для которого в базе есть точное правило Rule==Domain (если есть правила "ad.adriver.ru/cgi-bin", "content.adriver.ru/plugins/autoUpdate.adriver.js" и "https://www.adriver.ru/index.html", то они будут проигнорированы только в том случае, если в базе есть правило "adriver.ru", у которого строка правила является также и именем домена).

Re: Идея: фильтрация "лишних" правил по критерию вхождения доменного имени правила в доменное имя другого правила

Добавлено: Пн дек 28, 2020 6:07
bogdan108
У себя я применяю фильтрацию по доменному имени в DNS. Насобирал порядка 40 списков в один общий список из около 6 млн доменных имён. При этом даже перезагрузка DNS-сервера для применения изменений требует до 5 минут на считывание списка в память, что слегка отталкивает от идеи такой фильтрации... Решено было что-то предпринять.
Анализ показа, что в этом общем списке для некоторых доменов - по нескольку десятков/сотен тысяч поддоменов.
Я выстроил список из имени вышестоящего домена и количества поддоменов для этого домена, отсортировал по количеству поддоменов. Вначале списка оказались крупные рекламные/макретинговые домены. Домены, у которых поддоменов больше 1000 - сразу в вечный список блокировки по шаблону доменного имени. Для доменов с количеством поддоменов больше 100 - в список блокировки по шаблону доменного имени. Остальные - ручным перебором в список блокировки по шаблону доменного имени. Остальные домены перенёс как есть в список блокировки по точному совпадению доменного имени.
После подобной оптимизации по исключению балластных правил для порядка 150 доменов, из около 6млн доменных имён осталось порядка 600 тысяч и DNS-серервер перезагружается всего за несколько секунд.

После выполнения ballastRules::expandAllRules, в базе версии "2020-12-04 4:55:02" обнаружили себя 8964 балластные правила.
Это те самые правила, для имени домена из которых в базе DBL есть правило с точно таким именем домена.

Re: Идея: фильтрация "лишних" правил по критерию вхождения доменного имени правила в доменное имя другого правила

Добавлено: Пн дек 28, 2020 9:02
bogdan108
del

Re: Идея: фильтрация "лишних" правил по критерию вхождения доменного имени правила в доменное имя другого правила

Добавлено: Пн дек 28, 2020 9:04
Slava
1. В режике уже есть подобная очистка:
cat redirector.err |grep adriv

Код: Выделить всё

2020-12-28 07:57:36 [288444] MAKE-CACHE: delete rule: ad.adriver.ru because exist rule: adriver.ru
2020-12-28 07:57:36 [288444] MAKE-CACHE: delete rule: ad.adriver.ru/cgi-bin because exist rule: adriver.ru
2020-12-28 07:57:36 [288444] MAKE-CACHE: delete rule: ad.adriver.ru/cgi-bin/click.cgi because exist rule: adriver.ru
2020-12-28 07:57:36 [288444] MAKE-CACHE: delete rule: ad.adriver.ru/cgi-bin/erle.cgi because exist rule: adriver.ru
2020-12-28 07:57:36 [288444] MAKE-CACHE: delete rule: ad.adriver.ru/cgi-bin/rle.cgi because exist rule: adriver.ru
2020-12-28 07:57:36 [288444] MAKE-CACHE: delete rule: ad.adriver.ru/cgi-bin/rle.cgi? because exist rule: adriver.ru
2020-12-28 07:57:36 [288444] MAKE-CACHE: delete rule: content.adriver.ru/plugins/autoUpdate.adriver.js because exist rule: adriver.ru
2.Резать домены за поддомены может быть неправильно.
Соглачно такой логике можно .com вырезать итд.
Нужно вручную проверять такой домен.

Re: Идея: фильтрация "лишних" правил по критерию вхождения доменного имени правила в доменное имя другого правила

Добавлено: Пн дек 28, 2020 14:25
bogdan108
Slava писал(а):
Пн дек 28, 2020 9:04
1. В режике уже есть подобная очистка:
cat redirector.err |grep adriv
Оказывается - в rejik такое уже есть!
Копаю DBL и даже не обратил внимание на redirector... (DBL и redirector взаимосвязаны - это я и упустил)
Slava писал(а):
Пн дек 28, 2020 9:04
2.Резать домены за поддомены может быть неправильно.
Соглачно такой логике можно .com вырезать итд.
Нужно вручную проверять такой домен.
В функции FindRootId происходит проверка, чтобы имя родительского домена было в базе, в качестве Rule и чтобы группы совпадали. Т.е. алгоритм похож на поведение redirector - если имя родительского домена есть в базе, то все его поддомены пропускать.