Основы разработки клиента сети I2P. Часть 2

в 3:04, , рубрики: c++, i2p, Программирование, метки: ,

В предыдущей статье были рассмотрены задачи, необходимые для построения I2P маршутизатора, способного принимать участие в работе сети, включающие взаимодействие с другими маршутизаторами через обычный Интернет, построение тоннелей разных видов и сбор информации о других узлах сети. Несмотря на важность этих задач, I2P клиент, выполняющий лишь функции маршутизатора, с точки зрения пользователя является «вещью в себе», поскольку не делает ничего интересного пользователю. Данная статья посвящена протоколам прикладного уровня, предназначенных для передачи пользовательских данных через сеть I2P.


Если вопросы работы I2P маршутизатора более или менее логично освящены в официальной документации, то прикладные протоколы представляют собой мешанину различных идей, сводящихся к тому, что каждый волен реализовать собственный протокол для собственного приложения, используя точки назначения в качестве адресов. Однако это нисколько не приближает к реализации собственного клиента, поскольку существующие ресурсы сети уже используют какие то протоколы и всякая новая реализация должна уметь с ними работать. В качестве примера можно посмотреть описание «чеснока», из данной страницы реально лишь понять, как следует упаковывать «чесночины» и как шифровать. Что передается между Алисой и Бобом, а также откуда Боб знает, что ответ следует отправлять именно Алисе, совершенно непонятно. Там же содержится утверждение, что с целью подтверждения доставки в одной из «чесночин» передается сообщение DeliveryStatus с указанием маршутизатора-отправителя в инструкциях для доставки, тем самым раскрывая маршутизатор, на котором сидит отправитель. Разумеется это не так. К сожалению, единственным способом узнать, как обстоят дела на самом деле, был анализ трафика, генерируемого официальным джава-клиентом.

«Чесночная» передача данных

Разберемся в этом вопросе более подробно, несколько видоизменив оригинальный пример и сделав его более практическим. Предположим что Вася Пупкин обращается к сайту Флибусты, который отныне доступен только по I2P, в то же время у Васи есть собственный сайт, оскорбляющий чьи-нибудь чувства, потому и находящийся в I2P. Для этого у Васи запущен маршутизатор, постоянно поддерживающий как минимум один исходящий и один входящий тоннели. Для своего сайта Вася создал отдельную точку назначения и опубликовал везде ее адрес. Для обращения к Флибусте ее адрес Вася уже знает, и его маршутизатор знает ее LeaseSet и может отправить туда сообщение, проблема заключается лишь в том что Васе нужно получить ответ от Флибусты, а для этого ей нужно знать адрес Васи. С этой целью на васином маршутизаторе создается еще одна точка назначения, служащая в качестве обратного адреса для все соединений, инициируемых Васей. Не только для Флибусты, но и всех остальных сайтов. При необходимости можно создать и несколько таких обратных адресов, но тогда также придется строить LeaseSet-ы с разными непересекающимися наборам тоннелей.
garlic
Для передачи данных между точками назначения используется I2NP сообщение Garlic — «чеснок». Сами сообщения передаются и шифруются между маршутизаторами, используя для этого отдельную пару ключей шифрования, публичный ключ которой передается в LeaseSet-е. Этот ключ не совпадает с публичным ключом маршутизатора, и в отличие от последнего, генерируется по новой при каждом старте. Внутри сообщение состоит из «чесночин», каждая из которых состоит из I2NP сообщения и инструкций для его доставки (delivery instructions) 4-х видов:

  • Локальный. Сообщение предназначено самому маршутизатору. Как правило чей-то LeaseSet.
  • Точка назначения. Сообщение предназначено точке назначения, подключенной к маршутизатора. Является единственным способом отправки данных точке назначения.
  • Тоннель. Сообщение предназначено для отправки в указанный входящий тоннель, начинающийся на указанном маршутизаторе. Используется для подтверждения доставки «чеснока».
  • Маршутизатор. Сообщение предназначено для отправки другому маршутизатору. Черезвычайно опасно с точки зрения безопасности, если указанный маршутизатор отличен от собственного или пользующегося доверием. На практике не встречалось.

Как правило используются «чесноки», состоящие из двух «чесночин». Первой является I2NP сообщение DeliveryStatus с номером сообщения самого «чеснока» и доставкой в один из входяших тоннелей маршутизатора отправителя. Используется для подтверждения доставки всего «чеснока» по назначению. Второй является I2NP сообщение Data, содержащее сами передаваемые данные с доставкой в точку назначения. Иногда присутствует и третья «чесночина» — LeaseSet точки назначения (не маршутизатора) отправителя. В нашем примере это LeaseSet обратного адреса Васи. Такая «чесночина» присутствует в двух случаях: в самом первом сообщении и при изменении LeaseSet-а. Делается это для того чтобы маршутизатор знал каким образом отослать ответ отправителю, иначе пришлось бы запрашивать соответствующий LeaseSet у floodfill маршутизаторов, который скорее всего там будет отсутствовать, как, например, обратный адрес Васи, а LeaseSet секретного сайта Васи наоборот там будет присутствовать.
Возникает вопрос, откуда Флибуста знает, что ответ следует отправить именно Васе, а не Петей или еще кому нибудь из обращающихся к ней в тот же самый момент. Даже если бы было известно, что «чеснок» пришел от маршутизатора Васи, чего, конечно же, не имеет место быть, то это бы помогло не сильно, поскольку на маршутизаторе Васи помимо обратного адреса имеется также его сайт и, возможно, еще много чего. Оказывается, данная информация должна содержаться внутри передаваемых в сообщении Data данных, что свидетельствует о неудачном дизайне всей системы, поскольку не позволяет достичь изоляции протоколов разных уровней друг от друга. Иначе говоря, адрес точки назначения должен присутствовать одновременно как в самих данных так и в протоколе для передачи этих данных.

Данные протокола I2CP

Изначально протокол I2CP разрабатывался исключительно для обмена между различными приложениями и маршутизатором — его сообщения в саму сеть I2P попадать не должны. Однако содержимое сообщений SendMessageMessage и MessagePayloadMessage передается по сети внутри I2NP сообщений Data, и представляют собой архивированные gzip-ом данные со специальным образом измененным заголовком вида.

0x1F 0x8B 0x08 — префикс gzip-а
1 байт флагов gzip-а
2 байта TCP или UDP порт отправителя
2 байта TCP или UDP порт получателя
1 байт дополнительных флагов gzip-а
1 байт типа протокола: 6 — потоковый (streaming), 17 — дейтаграммный (datagram), 18 — «сырой» (raw)

Таким образом каждое сообщение Data, переданное через «чеснок» всегда будет начинаться с 0x1F 0x8B 0x08 и первым делом распаковывается gzip и, в зависимости от типа протокола, обрабатывается соответствующей реализацией.

Потоковый протокол (streaming)

Потоковый протокол аналогичен TCP протоколу и гарантирует последовательность передачи данных. Сообщения состоят из заголовка и собственно данных. Тип сообщения определяется полем флагов, аналогично TCP имеются флаги SYN/FIN для установки и разрыва соединения. В отличие от TCP также могут присутствовать вплоть до 255 NACK-ов — номеров пропущенных сообщений с требованием переслать их повторно, что более характерно для пакетных протоколов и требует более сложной реализации. Также содержатся стандартные для TCP поля: 4-х байтные порты отправителя и получателя, называемые потоками, номера последовательности и номера подверждения.
При установке соединения происходит обмен сообщениями с установленным флагом SYNCHRONIZE, при этом обязательно должны быть установлены флаги FROM_INCLUDED и SIGNATURE_INCLUDED. Первый означает что в заголовке присутствует полный 387-байтный I2P адрес, а второй, что все сообщение подписано закрытым ключом I2P адреса отправителя. Таким образом стороны узнают I2P адреса друг друга, а проверка подписи гарантирует, что эти адреса настоящие. Иначе говоря, Вася, подключаясь к адресу Флибусты, после установки соединения может быть уверен, что это именно Флибуста, а Флибуста узнает обратный адрес Васи.
Таким образом, интерфейс потоковых протоколов может быть реализован в виде обычных сокетов, что позволяет использование I2P в сетевых приложениях с минимальными изменениями.

Автор: orignal

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js