Торрент-Файл. Что Внутри?



Введение

Торрент-файл.
</p><p>
 Что внутри?

Добрый день.

Я, как и многие, пользуюсь крупным торрент-трекером — rutracker.org, но есть одна особенность, которая меня раздражает. Это добавление адреса в список трекеров ix*.

rutracker.net , который служит неизвестным мне целям.

Однако, что часто (у меня - почти всегда) выдает ошибки( 502 Неверный шлюз И 0 Нет ответа ).

Торрент-клиент (я использую Transmission) помечает торрент как битый.

Что, конечно, меня очень беспокоит. Особенно если учесть функцию «Передача» — она устанавливает статус торрента на основе последнего ответа трекера.

То есть опрашиваем ix*, он возвращает ошибку, торрент помечен как Broken, через n минут/секунд опрашивается следующий трекер из списка - bt*.

rutracker.org или retracker.local , которые возвращают успешный код, и торрент снова становится нормальным.

Меня такая чехарда не особо радует. Решение простое – удалите этот плохой адрес из списка.

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

Поэтому я решил разобраться в формате и автоматизировать удаление трекера из списка.



Бенкод

Это название формата кодирования данных в файлах .

torrent. Он почти нигде больше не используется; Я тоже сталкивался с этим в формате хранения информации о резюме в Transmission. Для большинства современных языков написаны библиотеки для работы с этим форматом, но не для C++, да, конечно, есть такой вещь , но это чистый C и к тому же форма представления мне не показалась удачной, поэтому я написал свой простой велосипед, потому что формат предельно простой.

Описаны четыре типа данных: массив байтов, число, список, ассоциативный массив.

Давайте по порядку:

  • Числа даны в виде я е , являются числами в представлении ascii, то есть 1 указывается как «1» или 0x31. Заметно, что таким образом мы можем указывать огромные числа, которые не влезут ни в long, ни в long long, но большинство людей пренебрегают отсутствием ограничения и используют 64-битные числа.

  • Байтовый массив - : .

    Длина массива также формируется неограниченной последовательностью цифр.

  • Список - л е .

    Элемент может быть любым из типов данных.

    Включая вложенный список.

    Конец, как видно из формата, отмечен буквой «е».

  • Ассоциативный массив — д е .

    Каждый элемент массива выглядит следующим образом: .

    Массив байтов — это имя записи в форме из пункта 2. Элементом опять же может быть что угодно — список, массив, ассоциативный массив, число.

Это все.

Сам файл представляет собой последовательность таких записей.

Поэтому расшифровка предельно проста:

  
  
  
  
  
   

void CTorrentFile::ReadBencElement(ifstream & fin, tree <BencElement>::pre_order_iterator & parent, string name) { BencElement el; char c = fin.get(); el.name = name; if (c == 'i') { el.type = BencInteger; fin >> el.integer; m_tree.append_child(parent, el); } else if (c == 'l') { int l = fin.peek(); el.type = BencList; tree <BencElement>::pre_order_iterator it = m_tree.append_child(parent, el); while (l != 'e') { ReadBencElement(fin, it, string("")); l = fin.peek(); } fin.seekg(1, ios_base::cur); } else if (c == 'd') { int l = fin.peek(); el.type = BencDict; tree <BencElement>::pre_order_iterator it = m_tree.append_child(parent, el); while (l != 'e') { string name; int len; fin >> len; fin.seekg(1, ios_base::cur); while (len--) { char s = fin.get(); name += s; } ReadBencElement(fin, it, name); l = fin.peek(); } fin.seekg(1, ios_base::cur); } else if (c >= '0' && c <= '9') { fin.seekg(-1, ios_base::cur); int len; el.type = BencString; fin >> len; el.bstr.len = len; // skip ':' fin.seekg(1, ios_base::cur); el.bstr.byteStr = new char[len + 1]; for (int i = 0; i < len; i++) { char s = fin.get(); el.bstr.byteStr[i] = s; } el.bstr.byteStr[el.bstr.len] = 0; m_tree.append_child(parent, el); } }

Кодирование также легко:

void CTorrentFile::WriteBencElement(std::ofstream & fout, tree <BencElement>::sibling_iterator & el) { tree <BencElement>::sibling_iterator it; switch (el->type) { case BencInteger: fout << 'i' << el->integer << 'e'; break; case BencString: fout << el->bstr.len << ':'; fout.write(el->bstr.byteStr, el->bstr.len); break; case BencList: fout << 'l'; it = m_tree.child(el, 0); for (size_t i = 0; i < m_tree.number_of_children(el); i++, ++it) WriteBencElement(fout, it); fout << 'e'; break; case BencDict: fout << 'd'; tree <BencElement>::sibling_iterator it = m_tree.child(el, 0); for (size_t i = 0; i < m_tree.number_of_children(el); i++, ++it) { fout << it->name.length() << ':' << it->name.c_str(); WriteBencElement(fout, it); } fout << 'e'; break; } }



Структура файла .

torrent.

Как я писал выше, для кодирования используется Bencode. Стоит добавить, что если массив байтов можно интерпретировать как строку (имена элементов ассоциативного массива, просто строковые поля), то используется кодировка utf-8. Содержимое представляет собой один большой ассоциативный массив со следующими полями:
  • Информация — вложенный ассоциативный массив, собственно описывающий файлы, которые передает торрент.
  • объявить — URL трекера.

    Вместе с Информация Это обязательное поле, все остальное необязательно.

  • список анонсов — список трекеров, если их несколько.

    В форме Bencode — список списков.

  • Дата создания - дата создания.

    Временная метка UNIX.

  • комментарий — текстовое описание торрента.

    rutracker.org хранит здесь ссылку на тему форума.

  • сделано - рассказывает нам, кто создал этот торрент.
Необходимо отметить, что файлы представлены в протоколе частями.

То есть файлы, содержащиеся в торренте, объединяются в единый массив, а затем этот массив разбивается на сравнительно небольшие кусочки.

Именно так данные обрабатываются протоколом BitTorrent. Ассоциативный массив Информация включает:

  • длина детали - размер одного куска 512 килобайт, 1 метр и так далее.

    Слишком большое количество фрагментов приведет к раздуванию файла .

    torrent.

  • куски — строка, содержащая объединение хешей SHA1, описывающих каждую часть.

    Длина этой строки равна 20 * количество штук.

  • имя — рекомендательное имя файла (если файл только один) или каталога.

    К сожалению, многие торрент-клиенты воспринимают это как аксиому.

  • длина — если файл только один, то будет задано поле, в котором указана длина файла.

  • файлы — если файлов несколько, появится список ассоциативных массивов.

Формат элемента списка файлы :
  • длина — длина файла.

  • путь — список строк, задающих путь.

    Каждая строка представляет собой элемент пути относительно корневого каталога торрента.

    По пути а/б/с/d.jpg в этом списке будет 4 строки - ['a', 'b', 'c', 'd.jpg'] .

В общем, это все.

На данный момент нам нужно только одно поле — список анонсов .

Просматривая этот список, находим ненужный трекер и вырезаем его:

int CTorrentFile::RemoveTracker(const char * mask) { int deletedCount = 0; tree <BencElement>::pre_order_iterator root = m_tree.child(m_tree.begin(), 0); tree <BencElement>::sibling_iterator it = m_tree.child(root, 0); for (size_t i = 0; i < m_tree.number_of_children(root); i++, ++it) { if (it->type == BencString && !it->name.compare("announce") && it->bstr.len > 0 && it->bstr.byteStr) { if (wildcardMatch(it->bstr.byteStr, mask)) { it->bstr.len = 0; it->bstr.byteStr[0] = 0; deletedCount++; } } else if (it->type == BencList && !it->name.compare("announce-list")) { tree <BencElement>::sibling_iterator trackerList = m_tree.child(it, 0); for (size_t j = 0; j < it.number_of_children(); j++) { if (trackerList->type != BencList) { ++trackerList; continue; } tree <BencElement>::sibling_iterator tracker = m_tree.child(trackerList, 0); for (size_t k = 0; k < trackerList.number_of_children(); k++) { if (tracker->type != BencString || tracker->bstr.len <= 0 || !tracker->bstr.byteStr) { ++tracker; continue; } if (wildcardMatch(tracker->bstr.byteStr, mask)) { tracker = m_tree.erase(tracker); deletedCount++; } else ++tracker; } if (trackerList.number_of_children() == 0) trackerList = m_tree.erase(trackerList); else ++trackerList; } } } return deletedCount; }

Соберем все в один исходник: Скачать — кроссплатформенность (win + *nix), обязательно повышение::файловая система .

Легко использовать: торрент-редактор , где шаблон представляет собой строку подстановочных знаков ('*' и '?'), в моем случае - http://ix*rutracker.net/* Если вы подставите имя каталога вместо имени файла, то будет выполнен рекурсивный обход по этому каталогу и файлы *.

torrent будут изменены.

Резервное копирование для .

торрент сохранено в .

старый .



Демоны и смотреть-каталог.

Таким образом мы можем пройтись по существующим торрент-файлам и вырезать трекер, а как насчет новых файлов? Я пользуюсь удобной вещью - смотреть каталог.

Кидаем туда .

torrent и клиент, найдя его в этой папке, автоматически добавит его к себе.

Однако я вовсе не хочу предварительно резать трекер, а хочу автоматизировать это дело.

Поэтому я написал простой демон, который мониторит свою собственную директорию просмотра, удаляет трекер и кидает файл в директорию просмотра торрент-клиента.

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

Пишем демона на C, используя замечательную штуку — уведомлять ,

notifyDesc = inotify_init(); if (notifyDesc < 0) exit(EXIT_FAILURE); watchDesc = inotify_add_watch(notifyDesc, argv[1], IN_CREATE); if (watchDesc < 0) exit(EXIT_FAILURE); // endless loop while (1) { processEvents(notifyDesc, argv[2], argv[3], argv[1]); }

Мы инициализируем модуль, используя inotify_init() , затем добавьте каталог отслеживания inotify_add_watch() , нас интересует только создание файла, поэтому указываем галочку IN_CREATE .

А затем мы запускаем бесконечный цикл отслеживания каталога.



static void processEvents(int wd, char * moveDir, char * pattern, char * watchDir) { #define BUF_SIZE ((sizeof(struct inotify_event) + FILENAME_MAX) * 10) int len, i = 0; char buf[BUF_SIZE]; // blocked read, we wake up when directory changed len = read(wd, buf, BUF_SIZE); while (i < len) { struct inotify_event * ev; ev = (struct inotify_event *)&buf[i]; processNewFile(ev->name, moveDir, pattern, watchDir); i += sizeof(struct inotify_event) + ev->len; } }

Блокировка вызова читать() вернет нам управление, как только нужные нам изменения произойдут в одном из каталогов, которые мы отслеживаем.

Таким образом мы абсолютно не нагружаем процессор во время ожидания.

Сама обработка файлов ничего интересного - пара вызовов переименовать() и один звонок система() .

Демонизация также стандартна:

// create child-process pid = fork(); // error? if (pid < 0) exit(EXIT_FAILURE); // parent? if (pid > 0) exit(EXIT_SUCCESS); // new session for child sid = setsid(); if (sid < 0) exit(EXIT_FAILURE); // change current directory if (chdir("/") < 0) exit(EXIT_FAILURE); // close opened descriptors close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO);

Источник .

Теги: #Децентрализованные сети #метаданные #torrent #inotify #rutracker.org #rutracker.org #daemon #bencode

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.