Pourquoi écrire du code spécifique à I2P ?
Il y a de multiples façons d’utiliser des applications dans I2P. En utilisant I2PTunnel, vous pouvez utiliser des applications classiques sans avoir besoin de programmer un support explicite à I2P. Ceci est très efficace pour un scénario client-serveur, où vous avez besoin de vous connecter à un simple site Web. Vous pouvez simplement créer un tunnel utilisant I2PTunnel pour connecter à ce site Web, comme indiqué dans Figure 1.
Si votre application est distribuée, elle exigera des connexions vers un grand nombre de pairs. En utilisant I2PTunnel, vous devrez créer un nouveau tunnel pour chaque pair que vous voulez contacter, comme illustré sur la Figure 2. Ce processus peut bien sûr être automatisé, mais exécuter beaucoup d’instances I2PTunnel augmente grandement la surcharge. De plus, avec de nombreux protocoles vous devrez forcer tout le monde à utiliser le même jeu de ports pour tous les pairs - par exemple si vous souhaitez exécuter de façon fiable des conversations DCC, tout le monde doit accepter que le port 10001 soit Alice, le port 10002 soit Bob, le port 10003 soit Charlie et ainsi de suite, puisque le protocole inclut des renseignements propres à TCP/IP (hôte et port).
Les applications de réseau générales envoient souvent beaucoup de données supplémentaires qui pourraient être utilisées pour identifier des utilisateurs. Noms d’hôtes, numéros de ports, fuseaux horaires, jeux de caractères, etc sont souvent envoyés sans informer l’utilisateur. Par conséquent, concevoir le protocole de réseau avec spécifiquement l’anonymat en mémoire peut éviter de mettre en péril les identités d’utilisateurs.
Il y a aussi des considérations d’efficacité à examiner en déterminant comment interagir au-dessus d’I2P. La bibliothèque de diffusion et les choses construites au-dessus d’elle fonctionnent avec des poignées de main semblables à TCP, tandis que les protocoles du cœur d’I2P (I2NP et I2CP) s’appuient strictement sur les messages (comme UDP ou dans quelques instances IP bruts). La distinction importante est qu’avec I2P, la communication fonctionne sur un long gros réseau ; chaque message de bout en bout aura des latences relativement importantes, mais peut contenir des charges utiles pesant jusqu’à plusieurs kilooctets. Une application qui a besoin d’une simple requête et d’une réponse peut se débarrasser de n’importe quel état et réduire la latence encourue par le démarrage et les poignées de main de lancement et démontage, en utilisant (meilleur effort) des datagrammes sans avoir à s’inquiéter de la détection de la MTU ni de la fragmentation des messages.
Figure 1 : La création d’une connexion client-serveur utilisant I2PTunnel exige seulement la création d’un seul tunnel.
Figure 2 : Mettre en place des connexions pour une application pair à pair exige de très nombreux tunnels.
En résumé, un certain nombre de raisons d’écrire du code spécifique à I2P :
- Créer une grande quantité d’instances I2PTunnel consomme une quantité non-insignifiante de ressources, ce qui est problématique pour des applications distribuées (un nouveau tunnel est exigé pour chaque pair).
- Des protocoles réseau généraux envoient souvent beaucoup de données supplémentaires qui peuvent être utilisées pour identifier des utilisateurs. Programmer spécifiquement pour I2P permet la création d’un protocole de réseau qui ne laisse pas fuiter de telles informations, gardant les utilisateurs anonymes et sécurisés.
- Les protocoles réseau conçus pour être utilisés sur l’Internet ordinaire peuvent être inefficaces sur I2P qui est un réseau avec une latence beaucoup plus élevée.
I2P prend en charge une interface de greffons normale pour développeurs afin que les applications puissent facilement être intégrées et distribuées.
Applications écrites en Java et accessibles ou exécutables par les applisWeb, app.war normaux en utilisant une interface HTML peuvent être considérées pour inclusion dans la distribution I2P.
Concepts importants
Il y a peu de changements qui exigent un réglage lors de l’utilisation d’I2P :
Destination ~= hôte+port
Une application fonctionnant sur I2P envoie des messages depuis et reçoit des messages vers un unique point final sécurisé cryptographiquement - "une destination". En termes TCP ou UDP, une destination pourrait (largement) être considérée l’équivalent d’un nom d’hôte plus le numéro de port du pair, quoiqu’il y ait quelques différences.
- Une destination I2P est un concept cryptographique. Toutes les données qui y sont envoyées sont chiffrés comme s’il y avait déploiement universel d’IPSEC avec l’emplacement (anonymisé) du point terminal signé comme s’il y avait déploiement universel de DNSSEC.
- Les destinations I2P sont des identifiants mobiles — elles peuvent être déplacées d’un routeur I2P à un autre (ou elles peuvent même multiconnectées à plusieurs multiples routeurs en même temps). Cela diffère plutôt du monde TCP ou UDP où une seule extrémité (port) doit rester sur un seul hôte.
-
Les destinations I2P sont laides et longues — dans les coulisses, elles contiennent une clé publique 2048 bits ElGamal pour le chiffrement, une clé publique 1024 bits DSA pour la signature et un certificat à taille variable qui peuvent contenir une preuve de travail ou des données anonymes.
Il existe des façons de faire référence à ces grandes et laides destinations par de courts et beaux noms (p. ex « irc.duck.i2p »), mais ces techniques ne garantissent pas l’unicité globale (puisqu’ils sont stockés localement dans une base de données sur la machine de chacun) et le mécanisme actuel n’est pas particulièrement extensible ni sécurisé (les mises à jour de la liste des hôtes sont gérées en utilisant des abonnements à des services de noms). Il pourrait un jour y avoir un système de noms sécurisé, interprétable par l’utilisateur, extensible et globalement unique, mais les applications ne devraient pas dépendre de sa mise en place, puisque certains pensent qu’un tel monstre est impossible. Il existe de plus amples informations sur le système de noms.
Alors que la plupart des applications n’ont pas besoin de faire la distinction entre protocoles et ports, I2P les prend en charge. Des applications complexes peuvent indiquer un protocole, un port de, et un port vers, pour chaque message, afin de multiplexer le trafic sur une seule destination. Voir la page des datagrammes pour plus de précisions. Les applications simples fonctionnent en écoutant « tous les protocoles » sur « tous les ports » d’une destination.
Anonymat et confidentialité
I2P possède un chiffrement bout à bout transparent et l’authentification de toutes les données passées sur le réseau - si Bob envoie à la destination Alice, seule la destination d’Alice peut recevoir, et si Bob utilise les datagrammes ou la bibliothèque streaming, Alice sait à coup sûr que la destination de Bob est celle qui a envoyé les données.
Bien sûr, I2P rend anonyme d’une manière transparente les données envoyées entre Alice et Bob, mais il ne fait rien pour rendre anonyme le contenu de ce qu’ils envoient. Par exemple, si Alice envoie un formulaire à Bob avec son nom complet, carte d’identité gouvernementale, et numéro de carte de crédit, il n’y a rien que I2P puisse faire. En tant que tel, les protocoles et les applications devraient garder à l’esprit quelles sont les informations qu’ils essaient de protéger et quelles sont les informations qu’ils sont enclins à exposer.
Les datagrammes I2P peut avoir une taille jusqu’à plusieurs Ko
Les applications qui utilisent des datagrammes I2P (soit bruts ou réflexibles) peuvent essentiellement être pensées en termes d’UDP - les datagrammes ne sont pas ordonnés, meilleur effort, et sans connexion - mais contrairement à UDP, les demandes ne doivent pas s’inquiéter de la détection MTU et peuvent décharger simplement de grands datagrammes. Tandis que la limite supérieure est nominalement 32 Ko, le message est fragmenté pour le transport, baissant ainsi la fiabilité de l’ensemble. On ne recommande pas actuellement les datagrammes de 10 Ko. Voyez la page datagrammes pour plus de précisions. Pour beaucoup d’applications, 10 Ko de données sont suffisantes pour une requête entière ou une réponse, leur permettant d’opérer d’une manière transparente dans I2P telle une application comme-UDP sans devoir écrire fragmentation, renvoi, etc.
Options de développement
Il y a plusieurs moyens d’envoyer des données par I2P, chacune avec ses propres pour et contre. La bibliothèque streaming est l’interface recommandée, utilisée par la majorité des applications I2P.
Lib streaming
La bibliothèque full streaming est maintenant l’interface standard. Elle permet de programmer en utilisant des sockets comme TCP, comme expliqué dans le Guide de développement streaming.
BOB
BOB est le pont basique ouvert, permettant une application en n'importe quelle langue de faire des connections en continu de et vers I2P. En ce moment il manque de prise en charge UDP mais la prise en charge est prévue dans le futur proche. BOB contient aussi plusieurs outils comme la génération de destination de clés et la vérification qu'une adresse est conforme aux spécifications d'I2P. Les informations à jour et les applications qui utilisent BOB sont trouvable sur le site I2P.
SAM, SAM V2, SAM V3
SAM n’est pas recommandé. Sam V2 est acceptable, SAM V3 est recommandée.
SAM est le protocole de messagerie simple et anonyme (Simple Anonymous Messaging), qui permet à une application écrite dans n’importe quel langage de parler à un pont SAM par un simple connecteur TCP, et de faire multiplexer tout son trafic I2P à ce pont, coordonnant ainsi de façon transparente le chiffrement, le déchiffrement et le traitement d’après les événements. SAM prend trois sortes d’opérations en charge :
- streams, pour quand Alice et Bob veulent envoyer des données l’un à l’autre de façon fiable et en ordre
- datagrammes réflexibles, pour quand Alice veut envoyer un message à Bob auquel Bob puisse répondre
- datagrammes en brut, pour quand Alice veut serrer la plupart de la bande passante et performance autant que possible et que Bob ne se soucie pas si l’expéditeur des données est authentifié ou pas (par exemple si les données transférées sont elles-mêmes authentifiantes)
SAM V3 vise le même but que SAM et SAM V2, mais ne nécessite pas de multiplexage/démultiplexage. Chaque stream I2P est traité par son propre socket entre l’application et le pont SAM. En plus, les datagrammes peuvent être envoyés et reçus par l’application à travers les communications de datagramme avec le pont SAM.
SAM V2 est une nouvelle version utilisée par imule
qui résoud certains des problèmes dans SAM.
SAM V3 est utilisé par imule depuis sa version 1.4.0.
I2PTunnel
L’application I2PTunnel permet aux applications de construire des tunnels particuliers, qui ressemblent à TCP, vers des pairs en créant soit des applications I2PTunnel « client » (qui écoutent sur un port particulier et se connectent à une destination I2P particulière quand un connecteur logiciel est ouvert vers ce port) ou des applications I2PTunnel « serveur » (qui écoutent une destination I2P particulière et, quand elles obtiennent une nouvelle connexion I2P, la relaient vers un hôte, port TCP précis). Ces flux sont compatibles 8 bits et sont authentifiés et sécurisés par la même bibliothèque de diffusion en continu que SAM utilise, mais la création de plusieurs instances d’I2PTunnel uniques impliquent une surcharge substantielle, puisque chacune a sa propre destination I2P et son propre jeu de tunnels, clés, etc.
SOCKS
I2P prend en charge les mandataires SOCKS V4 et V5. Les connexions sortantes fonctionnent bien. Les fonctions entrantes (serveur) et UDP pourraient être incomplètes et non testées.
Ministreaming
Enlevé
Il y a eu une bibliothèque "ministreaming" simple, mais maintenant ministreaming.jar contient seulement les interfaces pour la bibliothèque ministreaming complète.
Datagrammes
Recommandé pour les applications semblables-UDP
The Bibliothèque de datagramme permet d’envoyer des paquets semblables–UDP–. Il est possible d’utiliser :
- Datagrammes réflexibles
- Datagrammes bruts
I2CP
Non recommandé
I2CP lui-même est un protocole indépendant du language, mais mettre en œuvre une bibliothèque I2CP dans quelque chose d’autre que Java nécessite une quantité significative de code à écrire (des routines de chiffrement, rassemblement d’objet, traitement de message asynchrone, etc). Tandis que quelqu’un pourrait écrire une bibliothèque I2CP en C ou en quelque chose d’autre, au lieu de cela il serait très probablement plus utile d’utiliser la bibliothèque C de SAM.
Applications Web
I2P arrive avec le serveur Web Jetty, et au lieu de cela vous pouvez directement configurer I2P pour utiliser le serveur Apache. N’importe quelle technologie d’application Web standard devrait marcher.
Commencer à développer — un guide simple
Développer en utilisant I2P exige de travailler sur une installation d’I2P et un environnement de développement de votre propre choix. Si vous utilisez Java, vous pouvez commencer le développement avec la bibliothèque streaming ou la bibliothèque de datagramme. Si vous utilisez un autre language de programmation, vous pouvez utiliser SAM ou BOB.
Développer avec la bibliothèque streaming
L’exemple suivant montre comment créer des applications client et serveur semblables-TCP en utilisant la bibliothèque streaming.
Ceci exigera les bibliothèques suivantes dans votre classpath :
- $I2P/lib/streaming.jar: La bibliothèque streaming elle-même
- $I2P/lib/mstreaming.jar: Factory (?) et interfaces pour la bibliothèque streaming
- $I2P/lib/i2p.jar: Classes standard I2P, structures de données, API et utilitaires
Vous pouvez les obtenir depuis un installation I2P ou ajouter les dépendances suivantes depuis Maven Central :
net.i2p:i2p:2.7.0
net.i2p.client:streaming:2.7.0
La communication réseau exige l’utilisation de sockets réseau I2P. Pour démontrer ceci, nous créerons une application dans laquelle un client peut envoyer des messages texte à un serveur qui imprimera les messages et les renverra au client. Autrement dit, le serveur fonctionnera comme un écho.
Nous commencerons par initialiser l’application serveur. Cela exige d’obtenir un I2PSocketManager et de créer une I2PServerSocket. Nous ne fournirons pas les clés enregistrées d’une destination existante à l’I2PSocketManagerFactory, donc elle créera une nouvelle destination pour nous. Nous demanderons alors une I2PSession à l’I2PSocketManager, afin que nous puissions prendre connaissance de la destination qui a été créée, car nous devrons copier-coller ces informations ultérieurement afin que le client puisse se connecter à nous.
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...
}
}
Exemple de code 1 : initialisation de l’application serveur.
Une fois que nous avons une I2PServerSocket, nous pouvons créer des instances I2PSocket pour accepter des connexions issues de clients. Dans cet exemple, nous créerons une seule instance I2PSocket, qui peut seulement traiter un client à la fois. Un vrai serveur devrait pouvoir traiter de multiples clients. Pour faire ceci, de multiples instances I2PSocket devraient être créés, chacune dans des fils séparés. Une fois que nous avons créé l’instance I2PSocket, nous lisons les données, les imprimons et les renvoyons au client. Le code en gras est le nouveau code que nous ajoutons.
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;
}
}
Exemple de code 2 : acceptation de connexions issues de clients et traitement de messages.
Quand vous exécutez le code de serveur ci–dessus, il devrait imprimer quelque chose comme ceci (mais sans les fins de ligne, il devrait juste être un énorme bloc de caractères) :
y17s~L3H9q5xuIyyynyWahAuj6Jeg5VC~Klu9YPquQvD4vlgzmxn4yy~5Z0zVvKJiS2Lk poPIcB3r9EbFYkz1mzzE3RYY~XFyPTaFQY8omDv49nltI2VCQ5cx7gAt~y4LdWqkyk3au 6HdfYSLr45zxzWRGZnTXQay9HPuYcHysZHJP1lY28QsPz36DHr6IZ0vwMENQsnQ5rhq20 jkB3iheYJeuO7MpL~1xrjgKzteirkCNHvXN8PjxNmxe-pj3QgOiow-R9rEYKyPAyGd2pe qMD-J12CGfB6MlnmH5qPHGdZ13bUuebHiyZ1jqSprWL-SVIPcynAxD2Uu85ynxnx31Fth nxFMk07vvggBrLM2Sw82pxNjKDbtO8reawe3cyksIXBBkuobOZdyOxp3NT~x6aLOxwkEq BOF6kbxV7NPRPnivbNekd1E1GUq08ltDPVMO1pKJuGMsFyZC4Q~osZ8nI59ryouXgn97Q 5ZDEO8-Iazx50~yUQTRgLMOTC5hqnAAAACeci est la représentation en Base64 de la destination du serveur. Le client aura besoin de cette chaîne pour atteindre le serveur.
Nous allons maintenant créer l’application client. Un certain nombre d’étapes sont exigées pour l’initialisation. Nous devrons de nouveau commencer en obtenant un I2PSocketManager. Nous n’utiliserons cette fois pas d’I2PSession ni d’I2PServerSocket. Nous utiliserons plutôt la chaîne de serveur Destination pour commencer notre connexion. Nous demanderons la chaîne Destination à l’utilisateur et créerons une I2PSocket utilisant cette chaîne. Une fois que nous aurons une I2PSocket, nous pourrons commencer à envoyer et à recevoir des données vers et à partir du serveur.
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!");
}
}
}
Exemple de code 3 : lancer le client et le connecter à l’application serveur.
Finalement, vous pouvez exécuter à la fois l’application serveur et client. Lancez l’application serveur en premier. Elle imprimera une chaîne Destination (comme montrer ci-dessus). Ensuite, lancez l’application client. Quand elle demandera une chaîne Destination, vous pourrez saisir la chaîne imprimée par le serveur. Le client enverra alors « Hello I2P! » (avec un retour à la ligne) au serveur, qui imprimera le message et le renverra au client.
Félicitations, vous avez communiqué avec succès au moyen d’I2P !
Applications existantes
Contactez-nous si vous voudriez contribuer.
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.
Idées d’applications
- Serveur NNTP — il y en a eu par le passé, mais aucun à l’heure actuelle
- Serveur Jabber — il y en a eu par le passé, et il y en a un à l’heure actuelle, avec accès à l’Internet public
- Serveur de clés PGP ou mandataire
- Applications de diffusion de contenu/de table de hachage distribuée — ressusciter feedspace, porter dijjer, chercher des solutions de remplacements
- Donner un coup de main au développement de Syndie
- Applications Web — Il n’y a pas de limite à l’hébergement d’applications serveur Web telles que blogues, pastebins, stockage, suivi, flux, etc. N’importe quelle technologie Web ou CGI telle que Perl, PHP, Python ou Ruby fonctionnera.
- Ressusciter quelques vieilles applis, plusieurs qui se trouvaient avant dans le paquet source d’I2P ; bogobot, pants, proxyscript, q, stasher, socks proxy, i2ping, feedspace