Эта страница была обновлена Май 2013 и содержит сведения для версии маршрутизатора 0.9.6.

Зачем писать код под I2P?

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

Если ваше приложение распределенное, ему придется подключаться к большому количеству узлов. Используя I2PTunnel, вам придется создавать новый туннель для каждого узла, с которым вы хотите взаимодействовать как показано на Figure 2. Конечно, этот процесс можно автоматизировать, но запуск большого количества экземпляров I2PTunnel создает большую нагрузку. Кроме того, с большим количеством протоколов, вам придется заставить всех использовать один порт для всех узлов, т.к., если вы хотите использовать DCC чат, то все должны согласиться, что порт 10001 - это Alice, порт 10002 - Bob, порт 10003 - Charlie, и т.д., т.к. протокол содержит специфичную для TCP/IP информацию (хост и порт).

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

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

Для создания подключения клиент-сервер при использовании I2PTunnel нужно создать всего лишь один туннель.

Рисунок 1: Для создания подключения клиент-сервер при использовании I2PTunnel нужно создать всего лишь один туннель.

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

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

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

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

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

Важные понятия

Есть несколько вещей, которые нужно отрегулировать для использования в I2P:

Пункт назначения ~= хост+порт

Приложение, выполняемое в I2P, отправляет и принимает сообщения через уникальную зашифрованную безопасную точку обмена - "пункт назначения". В терминах TCP или UDP, пункт назначения может (грубо) быть эквивалентом пары название хоста + номер порта, хотя есть некоторые отличия.

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

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

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

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

В I2P используются прозрачные "end to end"-шифрование и аутентификация для всех данных, передаваемых по сети - если Bob отправляет в пункт назначения Alice, только пункт назначения Alice может это получить, и, если Bob использует библиотеку дейтаграмм или потоковую, Alice знает наверняка, что именно пункт назначения Bob'а отправил данные.

Естественно, I2P прозрачно анонимизирует данные, пересылаемые между Alice и Bob, но он не делает ничего для анонимизирования содержимого того, что они отсылают. Например, если Alice отправляет Bob'у форму с ее именем, персональным ID и номера кредиток, то I2P тут уже ничего не сделает. Т.о., протоколы и приложения обязаны помнить, какую информацию они должны защищать и какую разглашать.

Размер дейтаграммы I2P может быть несколько КБ

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

Варианты развития

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

Потоковая библиотека Streaming Lib

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

BOB

BOB is the Basic Open Bridge, allowing an application in any language to make streaming connections to and from I2P. At this point in time it lacks UDP support, but UDP support is planned in the near future. BOB also contains several tools, such as destination key generation, and verification that an address conforms to I2P specifications. Up to date info and applications that use BOB can be found at this I2P Site.

SAM, SAM V2, SAM V3

SAM не рекомендован. SAM V2 одобрен, SAM V3 рекомендован.

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

  • потоки, для случая если Alice и Bob хотят отсылать друг другу данные надежно и упорядочено
  • repliable дейтаграммы, для случая когда Alice хочет отослать сообщение Bob'у, на которое Bob может ответить
  • raw дейтаграммы, для случая если Alice хочет выжать максимум из пропускной способности канала и из производительности, и Bob'у не важно - аутентифицирован ли отправитель данных или нет (т.к. передаваемые данные сами по себе аутентифицированы)

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

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

I2PTunnel

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

SOCKS

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

Ministreaming

Удалено

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

Датаграммы

Рекомендовано для UDP-подобных приложений

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

  • Дейтаграммы с возможностью ответа
  • Raw дейтаграммы

I2CP

Не рекомендовано

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

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

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

Начни разработку - простой путеводитель

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

Разработка с потоковой библиотекой

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

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

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

You can fetch these from an I2P installation, or add the following dependencies from Maven Central:

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

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

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

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
    6HdfYSLr45zxzWRGZnTXQay9HPuYcHysZHJP1lY28QsPz36DHr6IZ0vwMENQsnQ5rhq20
    jkB3iheYJeuO7MpL~1xrjgKzteirkCNHvXN8PjxNmxe-pj3QgOiow-R9rEYKyPAyGd2pe
    qMD-J12CGfB6MlnmH5qPHGdZ13bUuebHiyZ1jqSprWL-SVIPcynAxD2Uu85ynxnx31Fth
    nxFMk07vvggBrLM2Sw82pxNjKDbtO8reawe3cyksIXBBkuobOZdyOxp3NT~x6aLOxwkEq
    BOF6kbxV7NPRPnivbNekd1E1GUq08ltDPVMO1pKJuGMsFyZC4Q~osZ8nI59ryouXgn97Q
    5ZDEO8-Iazx50~yUQTRgLMOTC5hqnAAAA
    
Это base64-представление серверного пункта назначения. Клиенту понадобится эта строка, чтобы достучаться до сервера.

Теперь создадим клиентское приложение. И опять, для инициализации понадобятся несколько шагов. Также нам нужно начать с получения I2PSocketManager. Теперь мы не будем использовать I2PSession и I2PServerSocket. Вместо этого мы используем строку пункта назначения сервера, чтобы начать соединение. Мы запросим у пользователя строку пункта назначения, и создадим 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: запуск клиента и подключение к приложению сервера.

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

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

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

Свяжитесь с нами, если вы хотите сотрудничать.

See also all the plugins on plugins.i2p, the applications and source code listed on echelon.i2p, and the application code hosted on git.repo.i2p.

See also the bundled applications in the I2P distribution - SusiMail and I2PSnark.

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

  • NNTP сервер - это было в прошлом, не сейчас
  • Jabber сервер - что-то в прошлом было, и сейчас есть один, с доступом к публичному internet
  • PGP Key сервер и/или прокси
  • Распределение контента / DHT приложения - воскрешение feedspace, портирование dijjer, ищем альтернативы
  • Помощь в разработке Syndie
  • Основанные на веб приложения - безграничные возможности для размещения web-server-based приложений, таких как блоги, записки, хранилища, отслеживание, фиды и т.д. Будет работать любая веб или CGI технология, такие как Perl, Python или Ruby.
  • Воскресить некоторые старые приложения, которые прежде были в пакете исходников I2P - bogobot, pants, proxyscript, q, stasher, socks proxy, i2ping, feedspace