Этот перевод был создан с помощью машинного обучения и может быть не на 100% точным. Просмотреть английскую версию

Разработка приложений

Зачем писать приложения для I2P, ключевые концепции, варианты разработки и руководство для начинающих

Зачем писать код специально для I2P?

Существует несколько способов использования приложений в I2P. Используя I2PTunnel , вы можете использовать обычные приложения без необходимости программировать явную поддержку I2P. Это очень эффективно для сценариев клиент-сервер, когда вам нужно подключиться к одному веб-сайту. Вы можете просто создать tunnel с помощью I2PTunnel для подключения к этому веб-сайту, как показано на Рисунке 1.

Если ваше приложение является распределенным, оно потребует соединений с большим количеством узлов. При использовании I2PTunnel вам потребуется создать новый tunnel для каждого узла, с которым вы хотите связаться, как показано на рисунке 2. Этот процесс, конечно, может быть автоматизирован, но запуск множества экземпляров I2PTunnel создает большие накладные расходы. Кроме того, со многими протоколами вам потребуется принудить всех использовать один и тот же набор портов для всех узлов — например, если вы хотите надежно запустить DCC чат, все должны согласиться, что порт 10001 это Алиса, порт 10002 это Боб, порт 10003 это Чарли, и так далее, поскольку протокол включает специфичную для TCP/IP информацию (хост и порт).

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

Также следует учитывать соображения эффективности при определении способов взаимодействия поверх I2P. Библиотека потоков и построенные на её основе решения работают с рукопожатиями, аналогичными TCP, в то время как основные протоколы I2P (I2NP и I2CP) строго основаны на сообщениях (как UDP или в некоторых случаях raw IP). Важное различие заключается в том, что с I2P связь осуществляется по длинной толстой сети — каждое сообщение между конечными точками будет иметь значительные задержки, но может содержать полезную нагрузку размером до нескольких КБ. Приложение, которому нужен простой запрос и ответ, может избавиться от любого состояния и снизить задержку, возникающую при рукопожатиях установки и завершения соединения, используя датаграммы (с максимальными усилиями) без необходимости беспокоиться об обнаружении MTU или фрагментации сообщений.

Создание соединения сервер-клиент с помощью I2PTunnel требует создания только одного tunnel.

Рисунок 1: Создание соединения сервер-клиент с использованием I2PTunnel требует создания только одного tunnel.

Настройка соединений для peer-to-peer приложений требует очень большого количества tunnel.

Рисунок 2: Настройка соединений для peer-to-peer приложений требует очень большого количества tunnel.

Подводя итог, вот несколько причин для написания кода, специфичного для I2P:

  • Создание большого количества экземпляров I2PTunnel потребляет значительное количество ресурсов, что проблематично для распределенных приложений (новый tunnel требуется для каждого узла).
  • Общие сетевые протоколы часто отправляют много дополнительных данных, которые могут использоваться для идентификации пользователей. Программирование специально для I2P позволяет создавать сетевые протоколы, которые не допускают утечки такой информации, сохраняя анонимность и безопасность пользователей.
  • Сетевые протоколы, разработанные для использования в обычном интернете, могут быть неэффективными в I2P, которая представляет собой сеть с гораздо более высокой задержкой.

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

Приложения, написанные на Java и доступные/запускаемые через HTML-интерфейс с использованием стандартного webapps/app.war, могут быть рассмотрены для включения в дистрибутив I2P.


Важные концепции

При использовании I2P есть несколько изменений, к которым нужно приспособиться:

Destination ~= хост+порт

Приложение, работающее в I2P, отправляет сообщения из уникальной криптографически защищенной конечной точки — “destination” (пункт назначения) — и получает сообщения в неё. В терминах TCP или UDP destination можно (в основном) считать эквивалентом пары имя хоста плюс номер порта, хотя есть несколько отличий.

  • I2P destination сам по себе является криптографической конструкцией — все данные, отправляемые на него, шифруются так, как если бы повсеместно использовался IPsec с подписанным (анонимизированным) местоположением конечной точки, как если бы повсеместно использовался DNSSEC.
  • I2P destinations являются мобильными идентификаторами — их можно перемещать с одного I2P router на другой (или они могут даже работать в режиме “multihome” — функционировать на нескольких router одновременно). Это сильно отличается от мира TCP или UDP, где одна конечная точка (порт) должна оставаться на одном хосте.
  • I2P destinations выглядят некрасиво и имеют большой размер — за кулисами они содержат 2048-битный открытый ключ ElGamal для шифрования, 1024-битный открытый ключ DSA для подписи и сертификат переменного размера, который может содержать доказательство работы или зашифрованные данные.

Существуют способы обращения к этим длинным и неудобным адресам назначения через короткие и красивые имена (например, “irc.duck.i2p”), но эти методы не гарантируют глобальную уникальность (поскольку они хранятся локально в базе данных на машине каждого пользователя), а текущий механизм не особенно масштабируем и не безопасен (обновления списка хостов управляются с помощью “подписок” на службы именования). Возможно, когда-нибудь появится безопасная, читаемая человеком, масштабируемая и глобально уникальная система именования, но приложения не должны полагаться на её наличие, поскольку есть те, кто не считает такую систему возможной. Дополнительная информация о системе именования доступна.

Хотя большинство приложений не нуждаются в различении протоколов и портов, I2P действительно их поддерживает. Сложные приложения могут указывать протокол, исходящий порт и целевой порт для каждого сообщения отдельно, чтобы мультиплексировать трафик на одном назначении. Подробности см. на странице datagram . Простые приложения работают, прослушивая “все протоколы” на “всех портах” назначения.

Анонимность и конфиденциальность

I2P обеспечивает прозрачное сквозное шифрование и аутентификацию для всех данных, передаваемых по сети — если Bob отправляет данные на destination Alice, только destination Alice может их получить, а если Bob использует библиотеку датаграмм или потоковую библиотеку, Alice точно знает, что данные отправил именно destination Bob.

Конечно, I2P прозрачно анонимизирует данные, передаваемые между Алисой и Бобом, но не делает ничего для анонимизации содержимого того, что они отправляют. Например, если Алиса отправляет Бобу форму со своим полным именем, удостоверениями личности и номерами кредитных карт, I2P ничего не может с этим поделать. Поэтому протоколы и приложения должны учитывать, какую информацию они пытаются защитить и какую информацию готовы раскрыть.

Датаграммы I2P могут достигать нескольких КБ

Приложения, использующие I2P датаграммы (как обычные, так и с возможностью ответа), можно рассматривать в терминах UDP — датаграммы неупорядочены, доставляются по принципу “лучших усилий” и не требуют установления соединения — но в отличие от UDP, приложениям не нужно беспокоиться об определении MTU и можно просто отправлять большие датаграммы. Хотя верхний лимит номинально составляет 32 КБ, сообщение фрагментируется для транспортировки, что снижает надёжность передачи в целом. Датаграммы размером более 10 КБ в настоящее время не рекомендуются. См. страницу датаграмм для подробностей. Для многих приложений 10 КБ данных достаточно для целого запроса или ответа, что позволяет им прозрачно работать в I2P как UDP-подобному приложению без необходимости реализации фрагментации, повторных отправок и т.д.


Опции разработки

Существует несколько способов отправки данных через I2P, каждый со своими преимуществами и недостатками. Библиотека потоковой передачи является рекомендуемым интерфейсом, используемым большинством I2P-приложений.

Библиотека потокового вещания

Полная потоковая библиотека теперь является стандартным интерфейсом. Она позволяет программировать с использованием TCP-подобных сокетов, как объясняется в руководстве по разработке с потоковой библиотекой .

BOB

BOB — это Basic Open Bridge (базовый открытый мост), позволяющий приложениям на любом языке программирования устанавливать потоковые соединения к I2P и из I2P. В настоящее время он не поддерживает UDP, но поддержка UDP планируется в ближайшем будущем. BOB также содержит несколько инструментов, таких как генерация ключей destination и проверка соответствия адреса спецификациям I2P. Актуальную информацию и приложения, использующие BOB, можно найти на этом I2P-сайте .

SAM, SAM V2, SAM V3

SAM не рекомендуется. SAM V2 приемлем, SAMv3 рекомендуется.

SAM — это протокол Simple Anonymous Messaging (простая анонимная передача сообщений), позволяющий приложению, написанному на любом языке, взаимодействовать с SAM-мостом через обычный TCP-сокет и обеспечивающий мультиплексирование всего I2P-трафика этого приложения, с прозрачной координацией шифрования/дешифрования и обработкой на основе событий. SAM поддерживает три стиля работы:

  • потоки, когда Алиса и Боб хотят надёжно отправлять данные друг другу в правильном порядке
  • датаграммы с возможностью ответа, когда Алиса хочет отправить Бобу сообщение, на которое Боб может ответить
  • сырые датаграммы, когда Алиса хочет получить максимальную пропускную способность и производительность, а Боб не заботится о том, аутентифицирован ли отправитель данных или нет (например, передаваемые данные самоаутентифицируются)

SAMv3 преследует ту же цель, что и SAM и SAM V2, но не требует мультиплексирования/демультиплексирования. Каждый I2P поток обрабатывается своим собственным сокетом между приложением и SAM мостом. Кроме того, датаграммы могут отправляться и получаться приложением через датаграммные коммуникации с SAM мостом.

SAM V2 — это новая версия, используемая imule, которая исправляет некоторые проблемы в SAM .

SAM V3 используется imule начиная с версии 1.4.0.

I2PTunnel

Приложение I2PTunnel позволяет приложениям создавать специфические TCP-подобные туннели к узлам, создавая либо ‘клиентские’ приложения I2PTunnel (которые слушают определенный порт и подключаются к определенному I2P destination всякий раз, когда открывается сокет к этому порту), либо ‘серверные’ приложения I2PTunnel (которые слушают определенный I2P destination и при получении нового I2P соединения проксируют его к определенному TCP хосту/порту). Эти потоки являются 8-битно чистыми и аутентифицированы и защищены через ту же библиотеку потоковой передачи, которую использует SAM, но существуют значительные накладные расходы при создании множественных уникальных экземпляров I2PTunnel, поскольку каждый имеет свой уникальный I2P destination и свой собственный набор tunnel, ключей и т.д.

SOCKS

I2P поддерживает SOCKS V4 и V5 прокси. Исходящие соединения работают хорошо. Входящие (серверные) и UDP функции могут быть неполными и непротестированными.

Ministreaming

Удалено

Раньше существовала простая библиотека “ministreaming”, но теперь ministreaming.jar содержит только интерфейсы для полной streaming библиотеки.

Датаграммы

Рекомендуется для UDP-подобных приложений

Библиотека датаграмм позволяет отправлять UDP-подобные пакеты. Возможно использование:

  • Repliable datagrams (дейтаграммы с возможностью ответа)
  • Raw datagrams (необработанные дейтаграммы)

I2CP

Не рекомендуется

I2CP сам по себе является языконезависимым протоколом, но для реализации библиотеки I2CP на языке, отличном от Java, необходимо написать значительное количество кода (процедуры шифрования, маршалинг объектов, асинхронная обработка сообщений и т.д.). Хотя кто-то может написать библиотеку I2CP на C или другом языке, скорее всего будет полезнее использовать вместо этого библиотеку SAM для C.

Веб-приложения

I2P поставляется с веб-сервером Jetty, и настройка для использования сервера Apache вместо него довольно проста. Любая стандартная технология веб-приложений должна работать.


Начало разработки — Простое руководство

Разработка с использованием I2P требует рабочей установки I2P и среды разработки на ваш выбор. Если вы используете Java, вы можете начать разработку с библиотеки потокового вещания или библиотеки датаграмм. При использовании другого языка программирования можно использовать SAM или BOB.

Разработка с использованием библиотеки потоковой передачи

Следующий пример показывает, как создать клиентские и серверные приложения, подобные TCP, используя библиотеку потоковой передачи.

Для этого потребуются следующие библиотеки в вашем classpath:

  • $I2P/lib/streaming.jar: Сама библиотека streaming
  • $I2P/lib/mstreaming.jar: Фабрика и интерфейсы для библиотеки streaming
  • $I2P/lib/i2p.jar: Стандартные классы I2P, структуры данных, API и утилиты

Вы можете получить их из установки I2P или добавить следующие зависимости из Maven Central:

  • net.i2p:i2p
  • net.i2p.client:streaming

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

Начнем с инициализации серверного приложения. Для этого необходимо получить I2PSocketManager и создать I2PServerSocket. Мы не будем предоставлять I2PSocketManagerFactory сохраненные ключи для существующего Destination, поэтому он создаст новый Destination для нас. Затем мы запросим у I2PSocketManager объект I2PSession, чтобы узнать созданный Destination, поскольку нам потребуется скопировать и вставить эту информацию позже, чтобы клиент смог подключиться к нам.

package i2p.echoserver;

import net.i2p.client.I2PSession;
import net.i2p.client.streaming.I2PServerSocket;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.client.streaming.I2PSocketManagerFactory;

public class Main {

    public static void main(String[] args) {
        //Initialize application
        I2PSocketManager manager = I2PSocketManagerFactory.createManager();
        I2PServerSocket serverSocket = manager.getServerSocket();
        I2PSession session = manager.getSession();
        //Print the base64 string, the regular string would look like garbage.
        System.out.println(session.getMyDestination().toBase64());
        //The additional main method code comes here...
    }

}

Пример кода 1: инициализация серверного приложения.

После создания I2PServerSocket мы можем создавать экземпляры I2PSocket для принятия соединений от клиентов. В этом примере мы создадим один экземпляр I2PSocket, который может обрабатывать только одного клиента за раз. Реальный сервер должен уметь обрабатывать несколько клиентов. Для этого необходимо создать несколько экземпляров I2PSocket, каждый в отдельном потоке. После создания экземпляра I2PSocket мы читаем данные, выводим их и отправляем обратно клиенту.

package i2p.echoserver;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import net.i2p.I2PException;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.util.I2PThread;

import net.i2p.client.I2PSession;
import net.i2p.client.streaming.I2PServerSocket;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.client.streaming.I2PSocketManagerFactory;

public class Main {

    public static void main(String[] args) {
        I2PSocketManager manager = I2PSocketManagerFactory.createManager();
        I2PServerSocket serverSocket = manager.getServerSocket();
        I2PSession session = manager.getSession();
        //Print the base64 string, the regular string would look like garbage.
        System.out.println(session.getMyDestination().toBase64());

        //Create socket to handle clients
        I2PThread t = new I2PThread(new ClientHandler(serverSocket));
        t.setName("clienthandler1");
        t.setDaemon(false);
        t.start();
    }

    private static class ClientHandler implements Runnable {

        public ClientHandler(I2PServerSocket socket) {
            this.socket = socket;
        }

        public void run() {
            while(true) {
                try {
                    I2PSocket sock = this.socket.accept();
                    if(sock != null) {
                        //Receive from clients
                        BufferedReader br = new BufferedReader(new InputStreamReader(sock.getInputStream()));
                        //Send to clients
                        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(sock.getOutputStream()));
                        String line = br.readLine();
                        if(line != null) {
                            System.out.println("Received from client: " + line);
                            bw.write(line);
                            bw.flush(); //Flush to make sure everything got sent
                        }
                        sock.close();
                    }
                } catch (I2PException ex) {
                    System.out.println("General I2P exception!");
                } catch (ConnectException ex) {
                    System.out.println("Error connecting!");
                } catch (SocketTimeoutException ex) {
                    System.out.println("Timeout!");
                } catch (IOException ex) {
                    System.out.println("General read/write-exception!");
                }
            }
        }

        private I2PServerSocket socket;

    }

}

Пример кода 2: принятие соединений от клиентов и обработка сообщений.

Когда вы запустите приведенный выше серверный код, он должен вывести что-то подобное (но без переносов строк, это должен быть просто один огромный блок символов):

y17s~L3H9q5xuIyyynyWahAuj6Jeg5VC~Klu9YPquQvD4vlgzmxn4yy~5Z0zVvKJiS2Lk
poPIcB3r9EbFYkz1mzzE3RYY~XFyPTaFQY8omDv49nltI2VCQ5cx7gAt~y4LdWqkyk3au
...

Это base64-представление серверного Destination. Клиенту понадобится эта строка для подключения к серверу.

Теперь мы создадим клиентское приложение. Опять же, для инициализации требуется выполнить несколько шагов. Снова нам нужно начать с получения I2PSocketManager. На этот раз мы не будем использовать I2PSession и I2PServerSocket. Вместо этого мы будем использовать строку Destination сервера для установления соединения. Мы запросим у пользователя строку Destination и создадим I2PSocket, используя эту строку. Как только у нас будет I2PSocket, мы сможем начать отправлять и получать данные с сервера.

package i2p.echoclient;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.ConnectException;
import java.net.NoRouteToHostException;
import net.i2p.I2PException;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.client.streaming.I2PSocketManagerFactory;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;

public class Main {

    public static void main(String[] args) {
        I2PSocketManager manager = I2PSocketManagerFactory.createManager();
        System.out.println("Please enter a Destination:");
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String destinationString;
        try {
            destinationString = br.readLine();
        } catch (IOException ex) {
            System.out.println("Failed to get a Destination string.");
            return;
        }
        Destination destination;
        try {
            destination = new Destination(destinationString);
        } catch (DataFormatException ex) {
            System.out.println("Destination string incorrectly formatted.");
            return;
        }
        I2PSocket socket;
        try {
            socket = manager.connect(destination);
        } catch (I2PException ex) {
            System.out.println("General I2P exception occurred!");
            return;
        } catch (ConnectException ex) {
            System.out.println("Failed to connect!");
            return;
        } catch (NoRouteToHostException ex) {
            System.out.println("Couldn't find host!");
            return;
        } catch (InterruptedIOException ex) {
            System.out.println("Sending/receiving was interrupted!");
            return;
        }
        try {
            //Write to server
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            bw.write("Hello I2P!\n");
            //Flush to make sure everything got sent
            bw.flush();
            //Read from server
            BufferedReader br2 = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String s = null;
            while ((s = br2.readLine()) != null) {
                System.out.println("Received from server: " + s);
            }
            socket.close();
        } catch (IOException ex) {
            System.out.println("Error occurred while sending/receiving!");
        }
    }

}

Пример кода 3: запуск клиента и подключение к серверному приложению.

Наконец, вы можете запустить как серверное, так и клиентское приложение. Сначала запустите серверное приложение. Оно выведет строку Destination (как показано выше). Затем запустите клиентское приложение. Когда оно запросит строку Destination, вы можете ввести строку, выведенную сервером. Клиент затем отправит ‘Hello I2P!’ (вместе с переводом строки) серверу, который выведет сообщение и отправит его обратно клиенту.

Поздравляем, вы успешно установили связь через I2P!


Существующие приложения

Свяжитесь с нами, если хотите внести свой вклад.

См. также все плагины на plugins.i2p , приложения и исходный код, перечисленные на echelon.i2p , а также код приложений, размещенный на git.repo.i2p .

См. также встроенные приложения в дистрибутиве I2P - SusiMail и I2PSnark.


Идеи приложений

  • NNTP сервер - в прошлом было несколько, в данный момент ни одного
  • Jabber сервер - в прошлом было несколько, и в данный момент есть один с доступом к публичному интернету
  • PGP сервер ключей и/или прокси
  • Приложения для распространения контента / DHT - воскресить feedspace, портировать dijjer, искать альтернативы
  • Помочь с разработкой Syndie
  • Веб-приложения - безграничные возможности для размещения приложений на основе веб-сервера, таких как блоги, pastebins, хранилища, трекинг, ленты и т.д. Любая веб или CGI технология, такая как Perl, PHP, Python или Ruby, будет работать.
  • Воскресить некоторые старые приложения, ранее включенные в исходный пакет i2p - bogobot, pants, proxyscript, q, stasher, socks proxy, i2ping, feedspace

Was this page helpful?