# Описание принципа работы базы SkyDNS Octo

#### Введение

В данном документе описывается формат базы категорированных интернет-ресурсов СкайДНС Octo, а так же способы доступа к ней.

- База представляет из себя файл базы данных в формате SQLite с несколькими таблицами, подробнее в [Описание базы данных и таблиц](https://docs.skydns.ru/link/101#bkmrk-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-%D0%B1%D0%B0%D0%B7%D1%8B-%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85).
- Для запроса в базу данных исходный URL требуется представить в каноничной форме по специальному алгоритму.
- Алгоритм описан в [Преобразование URL в каноничную форму](https://docs.skydns.ru/link/101#bkmrk-%D0%9F%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-url-%D0%B2).
- Для прямого поиска по базе URL требуется совершить преобразование с помощью хеширования. Способ хеширования и прочие преобразования описаны в [Хеширование URL](https://docs.skydns.ru/link/101#bkmrk-%D0%A5%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-url).
- В [Запросы и интерпретация результатов поиска](https://docs.skydns.ru/link/101#bkmrk-%D0%97%D0%B0%D0%BF%D1%80%D0%BE%D1%81%D1%8B-%D0%B8-%D0%B8%D0%BD%D1%82%D0%B5%D1%80%D0%BF%D1%80%D0%B5%D1%82%D0%B0) приведены инструкции по интерпретации результатов поиска.

<p class="callout info">Обратите внимание, что для упрощения и ускорения интеграции базы в ваш продукт мы настоятельно рекомендуем использовать доступ через библиотеку, а не прямой доступ к базе.</p>

#### Формат и режим работы базы

Файл базы данных записан в формате SQLite. SQLite это компактная встраиваемая реляционная база данных. Слово «встраиваемый» (embedded) означает, что SQLite не использует парадигму клиент-сервер, движок SQLite не является отдельно работающим процессом, с которым взаимодействует программа, а предоставляет библиотеку, с которой программа компонуется и движок становится составной частью программы. Таким образом, в качестве протокола обмена используются вызовы функций (API) библиотеки SQLite. Такой подход уменьшает накладные расходы, время отклика и упрощает программу. SQLite хранит базу данных в единственном файле базы данных.

База SQLite может работать в двух режимах: rollback и wal. Разница в них в том как записываются изменения в базу. В первом случае файл нельзя изменить когда его кто-то читает или изменяет в данный момент. Второй режим позволяет одновременно читать и изменять файл базы, но при этом создает рядом с базой служебные файлы.

При использовании [Модуль skydns\_url2cat на языке программирования Python](https://docs.skydns.ru/books/sdk-kategorizacii-domenov/page/modul-kategorizacii-domenov-dlya-python) база работает в режиме wal.

<p class="callout warning">Для работы необходимо иметь права на запись для тех пользователей которые будут даже читать базу. При копировании базы на устройство только для чтения необходимо перевести базу в режим rollback. О том как это сделать можно прочитать в документации по [SQLite3](https://sqlite.org/).</p>

#### Описание базы данных и таблиц

База данных содержит две таблицы:

- Таблица result
- Таблица cat

Подробнее рассмотрим каждую таблицу.

##### Таблица result

Таблица «result» содержит хешированные записи URL и их категории.

Схема базы данных

<table id="bkmrk-%D0%9D%D0%B0%D0%B7%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-%D0%94%D0%B0%D0%BD%D0%BD%D1%8B%D0%B5-doma"><thead valign="bottom"><tr class="row-odd"><th class="head">Название</th><th class="head">Данные</th></tr></thead><tbody valign="top"><tr class="row-even"><td>domain\_hash</td><td>хеш от домена</td></tr><tr class="row-odd"><td>path\_hash</td><td>хеш от пути</td></tr><tr class="row-even"><td>cat\_id</td><td>список категорий</td></tr></tbody></table>

С первичным ключом по полям «domain\_hash», «path\_hash». Это «таблица измерений» в терминах реляционных баз данных. Способ формирования запроса в эту таблицу описан в Запросы и интерпретация результатов поиска.

Данные в поле «cat\_id» хранятся в виде blob массива, для стандартизации перечисляемого типа, где каждая категория хранится в виде unsigned short.

##### Таблица cat

Таблица «cat» содержит список записей с названиями и идентификаторами категорий. В зависимости от требований клиента, база СкайДНС Octo может поставляться с различным числом категорий с более детальной категоризацией или уникальными именами категорий.

Схема базы данных

<table id="bkmrk-%D0%9D%D0%B0%D0%B7%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-%D0%94%D0%B0%D0%BD%D0%BD%D1%8B%D0%B5-loca"><thead valign="bottom"><tr class="row-odd"><th class="head">Название</th><th class="head">Данные</th></tr></thead><tbody valign="top"><tr class="row-even"><td>locale</td><td>идентификатор локализации</td></tr><tr class="row-odd"><td>cat\_id</td><td>идентификатор категории</td></tr><tr class="row-even"><td>name</td><td>название категории</td></tr></tbody></table>

C первичным составным ключом («locale», «cat\_id». Это таблица «справочник» в терминах реляционных баз данных. Идентификаторы из поля «cat\_id» таблицы «result» являются внешним ключом на эту таблицу. После locale содержит идентификатор локализации языка на котором записано название категории в поле «name». Поле «cat\_id» содержит числовой идентификатор категории, данные cat\_id не являются последовательными. Поле «name» содержит локализованные названия категорий.

Присутствие в таблице записей на английском языке в любой локализации не считается ошибкой, а указывают на переведенные категории.

#### Преобразование URL в каноничную форму

Единый указатель ресурса URL состоит из нескольких частей, но нас интересует только два из них:

1. Доменное имя (domain)
2. Путь (path)

Рассмотрим на примере случайного URL:

directory.google.com/example/test.php?key=value&amp;one=1

где домен это:

*directory.google.com*

а это путь URL:

/example/test.php?key=value&amp;one=1

Поскольку URL указывает на ресурс, он может быть записан в различных формах и различными способами. Для прямого поиска в базе надо привести все разнообразные формы URL указывающие на один ресурс к одному каноничному виду. Это очень важно для получения правильной категоризации запрашиваемого URL.

Мы используем стандартный вариант каноникализации URL из проекта Google Safe Browsing [https://developers.google.com/safe-browsing/](https://developers.google.com/safe-browsing/) с версией API больше 2. В этом проекте описаны техники для канонизации идентификатора ресурса, с примерами и алгоритмами которого можно ознакомится на странице [https://developers.google.com/safe-browsing/v4/urls-hashing#canonicalization](https://developers.google.com/safe-browsing/v4/urls-hashing#canonicalization) (версия от 27.09.2016).

Примеры каноникализации:

<table id="bkmrk-%D0%98%D1%81%D1%85%D0%BE%D0%B4%D0%BD%D1%8B%D0%B9-%D1%83%D1%80%D0%BB-%D0%9A%D0%B0%D0%BD%D0%BE%D0%BD%D0%B8%D1%87"><thead valign="bottom"><tr class="row-odd"><th class="head">Исходный урл</th><th class="head">Каноничный</th></tr></thead><tbody valign="top"><tr class="row-even"><td>*http://evil.com/foo-bar-baz*</td><td>*http://evil.com/foo*</td></tr><tr class="row-odd"><td>*http://host/%25%32%35*</td><td>*http://host/%25*</td></tr><tr class="row-even"><td>*http://evil.com/foo-bar-baz*</td><td>*http://evil.com/foo*</td></tr><tr class="row-odd"><td>*http://test.com/kek/../*</td><td>*http://test.com/*</td></tr></tbody></table>

Таким образом мы получим каноничную форму URL. Но из-за природы URL нельзя положиться на один URL, для поиска наилучшего соответствия ему в базе требуется сформировать производные URL, путем поочередного отбрасывания частей первичного URL с левого и правого края.

Рассмотрим на примере случайного URL:

directory.google.com/example/test.php?key=value&amp;one=1

производные URL будут следующими:

1. directory.google.com/example/test.php
2. directory.google.com/example/
3. directory.google.com/
4. google.com/example/test.php?key=value&amp;one=1
5. google.com/example/test.php
6. google.com/example/
7. google.com/

Получившиеся URL требуется проверить по базе.

#### Хеширование URL

Для прямого поиска URL в базе нужно по отдельности произвести хеширование доменного имени и пути. Хеширование производится функцией MD5 [**RFC 1321**](https://tools.ietf.org/html/rfc1321.html), но от результата функции берутся только первые 8 байт. У корня пути (/) особое значение - пустой blob массив.

Примеры на некоторых языках:

Python

```
import hashlib

_root_path_hash = buffer('')

def skydns_hash(value):
    if value == '/':
        return _root_path_hash
    return buffer(hashlib.md5(value).digest()[:8])
```

#### Запросы и интерпретация результатов поиска

Прямые запросы в базу производятся с помощью языка SQL, в таблицу «result».

Пример запроса:

```
SELECT cat_id FROM result
 WHERE domain_hash = ? AND path_hash = ?;
```

Где вместо вопросительных знаков требуется подставить результаты хеширования доменного имени и пути, преобразовав их blob. Результат запроса различается в зависимости от присутствия этого URL в базе. Если запрос вернул 0 строк, означает что данного URL в базе нет, и про него ничего не известно. Если URL найден в базе, результатом на выше приведенный запрос будет одна строка с одной колонкой «cat\_id» с типом blob. Это список категорий, для интерпретации надо разобрать это как массив unsigned short. Пример с преобразованием:

```
import struct

def parse_blob_cats(data):
    return struct.unpack('>%dH' % (len(data) // 2), data)
```

Числа в массиве означают идентификатор категории, значения и имена которых описаны в Таблица cat.