Le protocole réseau ZeroNet¶
- Tous les messages sont encodés avec MessagePack
- Toute requête possède 3 paramètres:
cmd
: La requête commandereq_id
: L'unique id de la requête (simple, nonce incrémenté à chaque connection)params
: Paramètre de la requête
- Exemple de requête :
{"cmd": "getFile", "req_id": 1, "params:" {"site": "1EU...", "inner_path": "content.json", "location": 0}}
- Exemple de réponse :
{"cmd": "response", "to": 1, "body": "content.json content", "location": 1132, "size": 1132}
- Exemple de réponse signalant une erreur:
{"cmd": "response", "to": 1, "error": "Unknown site"}
Poignée de main (Handshake)¶
Chaque connection commence avec une poignée de main en envoyant une requête à l'adresse du réseau spécifiée :
Paramètre | Description |
---|---|
crypt | Null/None, seulement utilisé dans les réponses |
crypt_supported | Une collection de méthode de chiffrement supportée par le client |
fileserver_port | Le port de distribution des fichiers du client |
onion | (Seulement avec Tor) L'adresse onion du client |
protocol | La version du protocole utilisé (v1 ou v2) |
port_opened | Le status du port client du client |
peer_id | (Pas utilisé avec Tor) L'id du client |
rev | La version de révision du client |
version | La version du client |
target_ip | L'adresse du serveur sur le réseau |
La cible initialise la connection chiffrée via socket en se basant sur le paramètre crypt_supported
, puis retourne :
Résultat | Description |
---|---|
crypt | La méthode de chrifremment utilisée |
crypt_supported | Une collection de méthode de chiffrement supportée par le serveur |
fileserver_port | Le port de distribution des fichiers du serveur |
onion | (Seulement avec Tor) L'adresse onion du serveur |
protocol | La version du protocole utilisé (v1 ou v2) |
port_opened | Le status du port client du serveur |
peer_id | (Pas utilisé avec Tor) L'id du serveur |
rev | La version de révision du serveur |
version | La version du serveur |
target_ip | L'adresse du client sur le réseau |
Note: Pas de chiffrement sur utilisé pour les connections en .onion car le réseau Tor a déjà une couche de chiffrement activé par défaut. Note: Vous pouvez aussi explicitement initialisé SSL avant le "handshake" si vous pensez que le client le supporte.
Example:
Envoi "Handshake":
{ "cmd": "handshake", "req_id": 0, "params": { "crypt": None, "crypt_supported": ["tls-rsa"], "fileserver_port": 15441, "onion": "zp2ynpztyxj2kw7x", "protocol": "v2", "port_opened": True, "peer_id": "-ZN0056-DMK3XX30mOrw", "rev": 2122, "target_ip": "192.168.1.13", "version": "0.5.6" } }
Résultat:
{ "protocol": "v2", "onion": "boot3rdez4rzn36x", "to": 0, "crypt": None, "cmd": "response", "rev": 2092, "crypt_supported": [], "target_ip": "zp2ynpztyxj2kw7x.onion", "version": "0.5.5", "fileserver_port": 15441, "port_opened": False, "peer_id": "" }
Requêtes de pair¶
getFile site, inner_path, location, [file_size]¶
Requête d'un fichier depuis le client
Paramètre | Déscription |
---|---|
site | Adresse du site (exemple: 1EU1tbG9oC1A8jz2ouVwGZyQ5asrNsE4Vr) |
inner_path | Chemin relative du fichier dans le répertoire du site |
location | Demander ce fichier en commençant par ce byte (max 512 bytes sont envoyés dans la requête, donc il faut plusieurs requêtes pour un large fichier) |
file_size | Taille totale du fichier demandé (optionel) |
Return:
Résultat | Déscription |
---|---|
body | Le contenu du fichier demandé |
location | La place du dernier byte envoyé dans le fichier |
size | La taille totale du fichier |
streamFile site, inner_path, location, [file_size]¶
Stream un fichier depuis le client
Return:
Résultat | Déscription |
---|---|
stream_bytes | La taille des données du fichier après MessagePack payload |
Afin d'éviter d'avoir python-msgpack sérialiser de chaîne de cractère trop conséquent, le corps du fichier est directement ajouté après le MessagePack payload. Par exemple,
> {"cmd": "streamFile", "id": 1, "inner_path": "content.json", "size": 1234} < {"cmd": "response", "to": 1, "stream_bytes": 1234} < content of the file
Détail sur l'implémentation dans ZeroNet : Pour les ségments de fichier plus gros que 256kb, le streaming est activé par défaut.
ping¶
Vérifie si le client est toujours en vie
Return:
Résultat | Déscription |
---|---|
body | Pong |
pex site, peers, need¶
Echange de paris avec le client. Pairs packagé en 6 bytes (4 bytes IP avec inet_ntoa + 2 bytes pour le port)
Pramètres | Déscription |
---|---|
site | Adresse du site (exemple: 1EU1tbG9oC1A8jz2ouVwGZyQ5asrNsE4Vr) |
peers | Liste des pairs que la personne envoyant la requête connait (packagé) |
peers_onion | Liste des pairs Tor Onion que la personne envoyant connait (packagé) |
need | Nombre de pairs demandé |
Return:
Résultat | Déscription |
---|---|
peers | Liste des pairs IPV4 connu pour ce site (packagé) |
peers_onion | Liste des pairs Tor Onion pour ce site (packagé) |
Chaque élèment dans la liste peers
est sous la forme d'adresse IPv4 packagé.
Adresse IP | Port |
---|---|
4 bytes |
2 bytes |
Chaque élèment de la liste peers_onion
est sous la forme d'adresse Tor Onion Service.
B32-decoded onion address | Port |
---|---|
binary_str[0:-2] |
binary_str[-2:] |
Pour retrouver la l'adresse onion, passe la première partie dans base64.b32encode
puis ajoute .onion
à la valeur retournée.¶
update site, inner_path, body, [diffs]¶
Met à jour le fichier d'un site.
Paramètre | Déscription |
---|---|
site | Adresse du site (exemple: 1EU1tbG9oC1A8jz2ouVwGZyQ5asrNsE4Vr) |
inner_path | Chemin relative du fichier dans le répertoire du site |
body | Contenu du fichier content.json mis à jour |
diffs (optionel) | Code modifié (diff) des fichiers modifiés dans le content.json |
Return:
Résultat | Déscription |
---|---|
ok | Message de remerciement lors d'une mise à jour réussite :) |
Format Diffs¶
A "dict" qui contient les modifications
- Key: chemin du fichier modifié relative au content.json (eg.:
data.json
) - Value: La liste des diff opcodes pour le fichier (eg.:
[['=', 5], ['+', '\nhello new line'], ['-', 6]]
)
Diff opcodes possibles:¶
Opcode | Déscription |
---|---|
['=', nombre de même caractère] | Partie du fichier sans modification (eg.: ['=', 5] ) |
['+', nouveau texte] | Caractères ajoutés (eg.: ['+', '\nhello new line'] ) |
['-', nombre de caractère supprimés] | Caractères suprimés (eg.: ['-', 6] ) |
Après avoir reçu la mise à jour, le client essaye de patcher les fichiers en utilisant les diffs. Si le résultat ne correspond pas au hash SHA256 fourni par le fichier content.json (différente version du fichier), le client re-télécharge le fichier dans son intégralité depuis le pair qui a émit le message de mis à jour.
Note: Les patches sont limités à 30KB par fichier et seulement utilisé pour les fichiers en .json.
listModified site, since¶
Listes les fichiers répertoriés dans le content.json et modifié depuis le paramètre since
envoyé. Il était utilisé pour récupérer le contenu soumit par un utilisateur du site.
Paramètre | Déscription |
---|---|
site | Adresse du site (exemple: 1EU1tbG9oC1A8jz2ouVwGZyQ5asrNsE4Vr) |
since | Liste des fichiers modifiés depuis ce timestamp. |
Return:
Résultat | Déscription |
---|---|
modified_files | Key: content.json inner_path Value: Date de la dernière modification |
Exemple:
> zeronet.py --silent peerCmd 127.0.0.1 15441 listModified "{'site': '1BLogC9LN4oPDcruNz3qo1ysa133E9AGg8', 'since': 1497507030}" { "to": 1, "cmd": "response", "modified_files": { "data/users/1NM9k7VJfrb1UWw5agAvyRfSn3ws1wTJ5U/content.json": 1497579272, "data/users/1QEfmMwKVxgR4rkREbdJYjgUmF3Zy8pwHt/content.json": 1497565986, "data/users/16NS3rBdW9zpLmLSQoD8nLTtNVsRFtVBhd/content.json": 1497575039, "data/users/1CjXarXgvcNeCJ2nMQxUi4DRFWp3GEur2W/content.json": 1497513808, "data/users/1L5rGDgTs4W2V7gekSvJNhKa7XaHkVwotD/content.json": 1497615798, "data/users/1LWuc6JBhUGrKEAh1aPrPU85dEMcKmg3pS/content.json": 1497594716, "data/users/1KdnTJVBGzEZrJppFZtzfG9chukuMv8xSb/content.json": 1497584640, "data/users/1GMNmr2bDPbT4c8yVnyCoDHke52CNCdqAa/content.json": 1497614188, "data/users/1GRm9rED83Tkfi3iWS9m3LWHiRpPZehWLd/content.json": 1497827772, "data/users/12Ugp53jiMdvj1Kxa1w7c2LcXUBdGPs1oK/content.json": 1497692901, "data/users/1F6BMqittjWUStzUbRXm2kG2GQ3RdBLqFQ/content.json": 1497571485, "data/users/1GgNo3CmxPd7n2pMSF3uyqf1XHvgtTUqCe/content.json": 1497560829, "data/users/16nArdxrSaNThNp83kL8E6NLL9WD98iUne/content.json": 1497627929, "data/users/16CAJkbfNRxNJq4aKdrZ2MSYFfFGvQ8JPi/content.json": 1497664899, "data/users/1DrBS2sTD3BX5BBxG8eqYsxXSvGt9kc5HE/content.json": 1497632000, "data/users/19sggoAZ4hcorrrfWoFWP9rwfpVsL29cnZ/content.json": 1497928134, "data/users/1NYpJupegoTXL4cFpkNdLNJ4XaAhTNhPe1/content.json": 1497535771, "data/users/1R67TfYzNkCnh89EFfGmXn5LMb4hXaMRQ/content.json": 1497691787, "data/users/1C9HXUYFSVafLxanwkaFPZRcRgCEGsj2Cn/content.json": 1497572833, "data/users/1LgoHzNGWeijeZbJ8a1YgGjMCnjaM4BWG/content.json": 1497620232, "content.json": 1497623639 } }
getHashfield site¶
Récupérer les identifiants uniques des fichiers optionels.
Paramètre | Déscription |
---|---|
site | Adresse du site (exemple: 1EU1tbG9oC1A8jz2ouVwGZyQ5asrNsE4Vr) |
Return:
Résultat | Déscription |
---|---|
hashfield_raw | Identifiants des fichiers optionels encodés avec array.array("H", [1000, 1001..]).tostring() |
Exemple:
> zeronet.py --silent peerCmd 192.168.1.13 15441 getHashfield "{'site': '1Gif7PqWTzVWDQ42Mo7np3zXmGAo3DXc7h'} { 'to': 1, 'hashfield_raw': 'iG\xde\x02\xc6o\r;...', 'cmd': 'response' }
setHashfield site, hashfield_raw¶
Ajoute la liste des identifiants des fichiers optionels que le client requêteur possède.
Paramètre | Déscription |
---|---|
site | Adresse du site (exemple: 1EU1tbG9oC1A8jz2ouVwGZyQ5asrNsE4Vr) |
hashfield_raw | Identifiants des fichiers optionels encodés avec array.array("H", [1000, 1001..]).tostring() |
Return:
Résultat | Déscription |
---|---|
ok | Mis à jour :) |
findHashIds site, hash_ids¶
Demande si le client connait un pair possèdant ces hash_ids
Paramètre | Déscription |
---|---|
site | Adresse du site (exemple: 1EU1tbG9oC1A8jz2ouVwGZyQ5asrNsE4Vr) |
hash_ids | Liste des identifiants des fichiers optionels que le client recherche |
Return:
Résultat | Déscription |
---|---|
peers | Key: identifiant du fichier optionel Value: Liste des pairs ipv4 encodés avec socket.inet_aton(ip) + struct.pack("H", port) |
peers_onion | Key: identifiant du fichier optionel Value: Liste des pairs onion encodés avec base64.b32decode(onion.replace(".onion", "").upper()) + struct.pack("H", port) |
Exemple:
> zeronet.py --silent peerCmd 192.168.1.13 15441 findHashIds "{'site': '1Gif7PqWTzVWDQ42Mo7np3zXmGAo3DXc7h', 'hash_ids': [59948, 29811]}" { 'to': 1, 'peers': { 29811: [ 'S&9\xd3Q<', '>f\x94\x98N\xa4', 'gIB\x90Q<', '\xb4\xady\xf7Q<' ], 59948: [ 'x\xcc>\xf6Q<', 'S\xa1\xddkQ<', '\x05\xac\xe8\x8dQ<', '\x05\xc4\xe1\x93Q<', 'Q\x02\xed\nQ<' ] }, 'cmd': 'response', 'peers_onion': { 29811: ['\xc7;A\xce\xbc\xd9O\xe2w<Q<'], 59948: ['\xc7;A\xce\xbc\xd9O\xe2w<Q<'] } }
Identifiant pour fichier optionel¶
Nombre entier représentant les 4 premiers cractères du hash :
>>> int("ea2c2acb30bd5e1249021976536574dd3f0fd83340e023bb4e78d0d818adf30a"[0:4], 16) 59948
checkport port¶
Vérifie le port spécifié de l'autre pair.
Paramètre | Déscription |
---|---|
port | Port à vérifier. |
Return:
Résultat | Déscription |
---|---|
status | Statut du port spécifié ("open"/ouvert ou "closed"/fermé) |
ip_external | IP externe du demandeur |
Le plugin BigFile¶
getPieceFields site¶
Renvoie tous les morceaux de fichiers de tout les larges fichiers du client dans une liste.
Paramètre | Déscription |
---|---|
site | Adresse du site |
Return:
Résultat | Déscription |
---|---|
piecefields_packed | Key: Hash Sha512/256 de la racine de merkle du large fichier Value: piecefield packagé. |
setPieceFields site, piecefields_packed¶
Ajoute au client les piecefields pour ce site.
Paramètre | Déscription |
---|---|
site | Adresse du site |
piecefields_packed | Key: Hash Sha512/256 de la racine de merkle du large fichier Value: piecefield packagé. |
Return:
Return key | Description |
---|---|
ok | Updated |
Bigfile piecefield¶
Détient les informations sur les pièces d'un large fichier, l'information est représenté par les valeurs 1/0 (1 = téléchargé, 0 = pas téléchargé).
Example:
1110000001
signifie que le fichier est de taille 9-10MB et le client a téléchargé le 3MB du debut du fichier et le dernier 1MB (chaque pièce fait 1MB).s
Format packagé:
Transforme la chaîne de cractère en une liste d'entier en comptant le nombre de répititions en commençant avec 1
.
Example:
1110000001
devient[3, 6, 1]
,0000000001
devient[0, 9, 1]
,1111111111
devient[10]
Après la conversion il est ensuite transformé en un array
avec array.array('H', piecefield)
Bigfile merkle root¶
Pendant la procédure qui consiste à hasher un large fichier, en plus de collelecter les sha512/256 dans le fichier piecemap, l'algorithme calcule aussi le SHA-512/256 l'arbre de merkle avec l'outil merkle-tools
.
L'arbre de merkle est seulement utilisé comme un identifiant unique pour le large fichier, pas (encore) pour vérifier les morceaux du fichier.
Note: L'arbre de merkle est choisi pour identifier le fichier, au lieu de l'actuel hash SHA-512/256 du fichier. De toute évidence, utilisant ce dernier résultat pour le hasher deux fois. (une fois pour piecemap et une autre pour le fichier en entier)
Note: L'arbre de merkle n'est pas utilisé pour vérifier l'intégrité des pièces ou du large fichier, parce que cela nécessiterait beaucoup de bande passante et d'espace pour transférer et collecter les preuves partiels de vérification, alors que le fichier
piecemap
est suffisant et prend moins de place.
Bigfile piecemap¶
Il contient les hashs SHA-512/256 de chaque morceau. La taille du morceau et le nom du ficher piecemap sont défini dans le content.json
, exemple :
... "files_optional": { "bigfile.mp4": { "piece_size": 1048576, "piecemap": "bigfile.mp4.piecemap.msgpack", "sha512": "d1f0d150e1e73bb1e684d370224315d7ba21e656189eb646ef7cc394d033bc2b", "size": 42958831 }, ...
Avec la structure définit, le fichier piecemap est packagé au format msgpack :
{ b'bigfile.mp4': {b'sha512_pieces': [ b"e\xde\x0fx\xec\xc5LZ9\x0e\xe7\x85E\x1b\xd5\xe4C'\xe7req\xe3<\xff\\\xbb\xc8b\xc2\xc1\x8e", b'\xef\xe8\xed\xfe\x16/\x96\xdb;;\x06n[8_\x06\x9ak|\xe1\x9f\xe1\xaf\x87\x96\xdd\xfd\x9bEf\xd9!', b'\x1c\xd6-\x1f\xce\xde{\xcd\x01\x93un =D\x0brmB-\xd1\x8c\xbf\xfe\xca\x8a\x1c\xf60\xbb\xedD', b'\x1aQdF\xd2\xbc\xdff{\xb7\x89\xf2\xd3\r\xa9\xe1\xefA-V\x18\xa4\xc8e\x13\x88v\x13\\&\xfbW', ... ]} }