Chatavion, une messagerie passe-partout

Conçue pour fonctionner sur des réseaux instables

a marqué ce sujet comme résolu.

Bonjour !

Je souhaite vous présenter un système de messagerie qui fonctionne uniquement avec des requêtes DNS. L’idée de base est de pouvoir communiquer depuis un avion en vol avec des gens sur le plancher des vaches.

Introduction

Communiquer en avion avec le sol ? Bah tu te connectes au réseau Wi-Fi de l’avion et basta, pourquoi vouloir faire compliqué ?

Parce qu’en avion, la connexion Wi-Fi coûte très cher et est très instable. Et la personne pour qui j’ai conçu le système n’avait pas envie de payer 50 euros pour un service médiocre pendant 10 heures de vol.

Alors oui, ma solution nécessite un accès à Internet à bord, mais pas de vider son porte-monnaie. En effet, la quasi-totalité des points d’accès Wi-Fi laissent passer les requêtes DNS sans trop de filtrage. En exploitant cela, on peut donc envoyer et recevoir des messages contenus à l’intérieur de requêtes DNS.

DNS, DNS, c’est quoi à la fin ?

Pour faire simple, le DNS est un système permettant d’interroger un serveur pour connaitre l’adresse IP associée à un nom de domaine, comme zestedesavoir.com. Les messages de ce type sont rarement filtrés et on peut y faire transiter ce qu’on veut. Ainsi, en exploitant les spécificités du système, on peut communiquer à peu près ce qu’on veut… moyennant un peu de travail en amont.

La mise en place de Chatavion est expliquée sur la page GitHub du projet. Le système est à l’état de prototype et fonctionne sous NodeJS, il s’adresse pour le moment à un public de geeks et autres bidouilleurs. Si quelqu’un veut faire une vrai appli Android ou iOS, c’est avec plaisir ! Le fonctionnement est schématisé dans ce post. Pour comprendre d’où sort ce projet, un petit retour en arrière s’impose.

2012, découverte de l’IP over DNS

C’est au début de la décennie que je découvre le concept d’IP over DNS. Il consiste à faire passer tout son trafic réseau sous forme de requêtes DNS qui arrivent jusqu’à un serveur, qui fait office d’intermédiaire, et qui renvoie les retours sous forme de réponses DNS. Après un bon moment à comprendre et mettre en place un tel système, je l’éprouve avec succès sur des hotspots Wi-Fi belges et français, le tout sans avoir besoin de compte associé à l’opérateur.

Un peu plus tard dans l’année, je tombe à court de crédit data sur mon smartphone. À l’époque, mon abonnement ne me permettait d’utiliser seulement 250 Mo par mois - c’était il y a seulement 7 ans ! Toutefois, je me rends compte que mon opérateur laisse quand même passer les requêtes DNS. Je commence à m’imaginer des programmes de transmission d’informations simples over DNS, comme la météo ou des tirages de loterie. Ça en restera au stade d’idée.

2014, réseau pourri sur toute la ligne

Alors que je prenais le train pour la Normandie, je souhaite aller sur Twitter. Il faut savoir que sur les lignes Paris-Normandie, on voyage dans des boites de conserve (ça va changer en 2020 \o/). Pas de Wi-Fi et un réseau mobile quasi inexistant dès qu’on s’éloigne des villes. Contemplant avec dépit le "G" m’indiquant que je ne dispose que d’une connexion GPRS, je me dis qu’une requête DNS, ça devrait passer. Je commence alors à concevoir une preuve de concept qui permet d’envoyer un tweet avec un réseau pourri, au moyen d’une seule requête DNS. N’étant pas développeur et n’ayant pas le courage de me former à des choses que je ne connais pas, je bricole et adapte quelques morceaux de code dans les seuls langages que je connais à peu près : le C et le bash.

2019, long-courrier et petits messages

Quelques mois avant un long voyage en avion, une amie me dit que sa mère risque de s’inquiéter pendant le vol. En se renseignant auprès de la compagnie aérienne, on apprend que la plupart des avions sont équipés d’une connexion Wi-Fi mais les tarifs sont "carrément abusés". Je ressors des cartons la POC mentionnée ci-dessus et la modifie pour que le message n’aille pas sur Twitter, mais sur une page web quelconque. Puis je tente quelque chose pour essayer de vérifier si le message a été bien envoyé. Du bricolage. Puis je rebricole par-dessus pour essayer de savoir s’il a été lu. Puis finalement, je passe plusieurs jours (voire nuits) à faire en sorte de pouvoir recevoir les messages, et que les gens sur Terre puissent en ajouter de manière simple (une bête page PHP). Au final, je réussis à utiliser le système dans un TGV sans m’authentifier. Quelques adaptations auront été faites jusqu’au départ. Et à bord du vol pour Tokyo, les choses fonctionnent à peu près : l’envoi de messages ne pose aucun souci, mais la réception est plus compliquée, certains caractères ne s’affichent pas. Cela n’a pas empêché leur lecture en faisant manuellement des requêtes DNS.

Suite à ce succès, j’ai décidé de partager ce projet. Tout le code est disponible sur GitHub, je suis ouvert aux propositions d’amélioration. Le programme d’origine tel que je l’ai utilisé en vol est disponible dans la branche first. C’est un assemblage bancal de programmes bash et C, rien à voir avec la version actuelle (branche master) entièrement en NodeJS !

Je peux aussi vous aider à monter votre Chatavion si le mode d’emploi est trop compliqué.

L’interface utilisateur est très austère, c’est de la ligne de commande, utilisable dans un émulateur de terminal sous Android (Termux). N’ayant aucune compétence en développement mobile, je serais ravi si quelqu’un d’aventureux souhaitait créer une véritable appli. L’image suivante date de la toute première version, mais ça n’a pas évolué des masses.

Utilisation de Chatavion sur Android
Utilisation de Chatavion sur Android

Pour rappel, c’est un prototype à la stabilité hasardeuse. Gardez ça en tête si vous tentez de monter votre Chatavion. Par ailleurs, les adresses de mes serveurs Chatavion ont été modifiées sur GitHub. Le système n’est absolument pas sécurisé, tous les messages peuvent potentiellement être interceptés et lus, en particulier sur les réseaux ouverts type hotspot.

Je vous laisse avec ce magnifique photomontage qui m’a pris une demi-heure.

Un chat, un avion, un fond à la Matrix. Parce que pourquoi pas.


Mise à jour 2023

2022 - 2023 : la concrétisation d’un rêve

Eh oui, l’histoire ne s’arrête pas là. Depuis que j’ai présenté ce projet ici, j’ai toujours voulu en faire une application facile d’utilisation. Mes compétences en développement mobile ne me permettent pas de m’y atteler, mais c’était sans compter sur un message Linkedin d’un ancien professeur sur lequel je tombe par une chance inouïe au bon moment. Pour faire court, cela m’a permis de présenter Chatavion à l’université Gustave Eiffel, et 6 mois plus tard, avec un groupe d’élèves ingénieurs qui ont été assez fous pour accepter le projet, nous avons rendu publique l’application Android dont je rêvais.

Communautés actuellement ouvertes au public : default@chatavion.com et public@chatavion.com - vous pouvez me demander pour en ouvrir d’autres sur le serveur chatavion.com, ou créer votre propre serveur.

Logo officiel de l'application Chatavion

+17 -0

Intéressant, il y a moyen de reproduire l’environnement d’utilisation ? Je veux dire, sans se trouver dans un long courrier et se connecter à leur wifi.

J’ai vu que tu utilises la commande dig mais il n’y aurait pas une meilleur intégration/résultat en reprogrammant directement l’intégration du protocole DNS ?

Par exemple, ce genre de module : dns-socket.

@A-312 Tu peux te connecter à n’importe quel hotspot Wi-Fi type orange, FreeWifi, le Wi-Fi du TGV… Après, il faut savoir que chaque fournisseur a ses spécificités. Avec FreeWifi, on ne peut utiliser que le serveur fourni par Free (en DHCP) pour envoyer ses requêtes. Avec le TGV, c’est open bar. Certains laissent passer les requêtes de type TXT (beaucoup plus rapides), la plupart ne laissent passer que les requêtes de type A (et c’est très lent, car on récupère les caractères 4 par 4, il faut les décoder, etc.).

Concernant l’emploi d’autre chose de dig, tout dépend de l’environnement de ton client. Techniquement, tu peux faire ce que tu veux pour envoyer / récupérer les messages tant que les serveurs sont bien configurés et que tes requêtes sont bien forgées (m1.n1.get…). Faut juste vouloir le faire.

+1 -0

Beau projet, belle POC ! :)

J’ai une ébauche de DNS en Node.js dans mes cartons répertoires, ça me donnera une occasion de tester ta POC parce que tu utilises un peu trop de serveur à mon goût, je suis sûr qu’on peut regrouper tout ça.

Avec FreeWifi, on ne peut utiliser que le serveur fourni par Free (en DHCP) pour envoyer ses requêtes.

Alors comment tu contournes le problème ? Si on ne peut pas mettre son serveur DNS en bout de chaîne, c’est compliqué … non ? :)

dig attend le retour de la première requête que se passerait-il si tu envois 5 requêtes simultanément ?

A-312

Il y a deux cas. Quand tu envoies un message, le serveur, aujourd’hui, n’est pas capable de répondre quelque chose de valable (si quelqu’un a une idée pour contourner le problème ou le résoudre avec le programme actuel, je suis preneur). Pour parer cela, je bloque le serveur SEND pendant 35 secondes le temps que dig laisse tomber l’affaire. Donc dans le cas d’envoi, si tu envoies 5 requêtes simultanément, seule la première qui parviendra au serveur SEND sera prise en compte.

Niveau réception, aucun souci, c’est un serveur DNS normal dans son fonctionnement qui répond. Il renvoie donc toutes les réponses que tu demandes, pour peu qu’il y ait un message à l’offset (numéro de ligne / quartet de colonnes) demandé.

Beau projet, belle POC ! :)

J’ai une ébauche de DNS en Node.js dans mes cartons répertoires, ça me donnera une occasion de tester ta POC parce que tu utilises un peu trop de serveur à mon goût, je suis sûr qu’on peut regrouper tout ça.

Yarflam

Merci, n’hésite pas à me donner tes retours. :)

Avec FreeWifi, on ne peut utiliser que le serveur fourni par Free (en DHCP) pour envoyer ses requêtes.

Alors comment tu contournes le problème ? Si on ne peut pas mettre son serveur DNS en bout de chaîne, c’est compliqué … non ? :)

Yarflam

Du tout ! :) On met toujours son serveur DNS en bout de chaine. C’est un système de rebond qui permet de l’atteindre.

Le serveur DNS contacté directement par le client, c’est généralement celui de l’opérateur (Orange par exemple). Prenons le cas de la réception du premier message du log en mode TXT. On émet donc une requête vers m1.getmmsg.xx. m1 = message numéro 1. getmmsg.xx, c’est un enregistrement DNS sur Internet de type NS, qui pointe vers le serveur de réception (RECV).

Concrètement, le serveur DNS d’Orange va voir "m1.getmmsg.xx". Il se dit "je ne connais pas, je vais demander ce que c’est au serveur d’autorité de ce domaine". Il transmet alors la requête au serveur de réception, qui fait autorité. Le serveur de réception va répondre avec l’enregistrement qu’il connait pour m1, soit le premier message du log de discussion. Le serveur DNS d’Orange prend cette réponse et la renvoie au client Chatavion.

C’est dur à expliquer avec des mots, j’essaie de faire un schéma prochainement.

+0 -0

Quelques schémas pour mieux illustrer le fonctionnement de Chatavion.

Fonctionnement global de Chatavion
Fonctionnement global de Chatavion
Envoi d'un message avec Chatavion
Envoi d'un message avec Chatavion
Réception d'un message en mode TXT avec Chatavion
Réception d'un message en mode TXT avec Chatavion

Pour la réception, en mode numérique, c’est légèrement différent. Les réponses arrivent sous forme de 4 nombres (adresses IPv4) retranscrites en caractères ASCII ensuite. Il y a autant de requêtes pour 1 message que ce message contient de quartets de caractères.

+0 -0

Bien plus clair avec le schéma. Très propre ! Merci. :)

Pour la réception, en mode numérique, c’est légèrement différent. Les réponses arrivent sous forme de 4 nombres (adresses IPv4) retranscrites en caractères ASCII ensuite. Il y a autant de requêtes pour 1 message que ce message contient de quartets de caractères.

C’est sûrement là-dessus que tu perds du temps.

On peut indiquer plusieurs IPv4 & IPv6 dans la réponse :

/* Header */
response += getAllString(chip.id); // Transaction ID
response += getAllString(chip.flags); // Flags
response += getAllString(chip.questions); // Questions
response += '\x00\x01'; // Answer Mode
response += getAllString(chip.rrs_authority); // Authority RRs
response += getAllString(chip.rrs_additional); // Additional RRs
/* Queries */
response += getAllString(chip.queries); // Announce
response += getAllString(chip.name) + '\x00'; // IP
response += getAllString(chip.type); // Type
response += getAllString(chip.class); // Class IN
/* Answers */
response += '\xc0\x0c'; // Name
response += '\x00\x01'; // Type A - IPv4
response += '\x00\x01'; // Class IN
response += '\x00\x00\x2f\x84'; // Time to live
response += '\x00\x04'; // Data Length
response += getAllString([127, 0, 0, 1]); // IPv4

C’est un extrait de mon DNS en Node.js. Il suffirait d’indiquer une taille de 4x pour les X quartets de caractères et d’ajouter tout le contenu du message à la suite. Idem avec l’IPv6.

+0 -0

@A-312 J’ai pensé à IPv6 après avoir commencé l’implémentation des requêtes de type A. Ça accélèrerait pas mal les choses. Je garde ça dans un coin de tête.

@Yarflam Je n’ai jamais utilisé Node.js, c’est pas pour les pages web ? Chatavion sert justement dans un environnement où on ne peut pas appeler de page web. Je reste prudent avec les entrées DNS à adresses multiples, certains hotspots reformatent les réponses à leur sauce et squizent des infos au passage.

+1 -0

@Yarflam Je n’ai jamais utilisé Node.js, c’est pas pour les pages web ? Chatavion sert justement dans un environnement où on ne peut pas appeler de page web. Je reste prudent avec les entrées DNS à adresses multiples, certains hotspots reformatent les réponses à leur sauce et squizent des infos au passage.

Vince

Ce n’est pas spécifique au web Node.js - tu peux coder un petit script automatique qui s’exécute en local ou ouvrir un socket pour traiter n’importe quel type de requête. Rien que dans ce cas-ci : une requête DNS ça passe par de l’UDP, alors qu’une requête HTTP passe par du TCP. Après que les hotspots appliquent un filtre sur les IP ça peut effectivement, c’est à vérifier ; de ce que je sais, un "nslookup yahoo.fr 8.8.8.8" depuis un FreeWifi retourne bien les 5 ip disponibles :

Name:   yahoo.fr
Address: 74.6.136.151
Name:   yahoo.fr
Address: 106.10.248.151
Name:   yahoo.fr
Address: 124.108.115.101
Name:   yahoo.fr
Address: 212.82.100.151
Name:   yahoo.fr
Address: 98.136.103.24

Faut penser ensuite à la pagination si le message dépasse la taille max.

Après au début on peut ajouter une phase d’initialisation pour voir ce que le hostspot supportent ou non. Ce qui obligerait à supporter plusieurs façons de faire, en les classant du plus efficaces au moins efficaces, on testerait d’abord la solution la plus efficace, avant d’utiliser une moins efficace si la première ne fonctionne pas.

Une fois, la fiabilité amélioré, il faudra penser au chiffrage.

Quelques améliorations apportées aujourd’hui : une vérification a été ajoutée pour le serveur SEND. Je reçois régulièrement des requêtes invalides (du bruit, disons) et me retrouve avec des logs système qui saturent jusqu’à parfois faire planter le serveur. J’ai donc ajouté une condition qui fait que si le décodage base32 échoue, la requête est ignorée.

Ensuite, j’ai suivi l’idée de A-312 de faire une détection automatique des types de requêtes supportées dans le client. J’y ai intégré IPv6, bien que je n’aie pas encore implémenté la création d’entrées DNS de type AAAA. Au moins, c’est déjà là.

Tout ça est sur le GitHub. Je ne suis pas super à l’aide avec l’outil et j’ai la sale habitude de coder à l’arrache, j’espère que ça ne pose pas de souci.

Concernant l’utilisation de Node.js, pour le serveur SEND, j’ai trouvé node-named qui ferait ce dont j’ai besoin de manière beaucoup plus propre, je me penche dessus asap.

Pour le chiffrement, c’est délicat, il faut limiter les échanges au maximum. La méthode devra faire le moins d’ajout possible en terme de taille. Je pense qu’une PSK par serveur pour chiffrer les messages est une solution intéressante, comme ça la conversation reste chiffrée sur le serveur et seuls ceux qui ont la clé peuvent envoyer/recevoir, un peu comme un serveur Radius ou une communauté SNMP. Il faudrait une commande déjà existante ou facile à installer sous Linux (gpg me semble être un bazooka pour tuer une mouche). Je garde à l’esprit qu’une vraie appli Android est à l’étude donc faudrait que ce soit pas trop chiant à implémenter dans une app.

Sur ma roadmap (mot bien pompeux pour désigner un bout de papier tout griffonné), j’ai donc la retranscription des messages en IPv6 dans la conf DNS, Node.js pour le serveur d’envoi et l’étude du chiffrement.

+0 -0

Mises à jour du jour :

  • prototype utilisant node.js sur SEND en remplacement de mes programmes en C, qui répond par l’adresse 42.42.42.42 quand un message est bien reçu (pas encore mis sur GitHub)
  • correction sur ip6ascii.sh qui empêchait les espaces de fonctionner
  • ajout du support de la réponse positive (42.42.42.42) dans send.sh : affiche "Message envoyé avec succès" quand le serveur SEND répond
  • correction de la partie IPv6 dans recepauto.sh (le cut n’était pas sur la bonne colonne)
  • ajout du support d’IPv6 au niveau de RECV : le serveur retranscrit désormais les messages aussi sous forme d’IPv6 grâce au module ip6.sh créé pour l’occasion

Prochainement : mise en ligne de la version de SEND utilisant node.js, étude du chiffrement, procédure d’installation plus simple.

+0 -0

Ce qui serait incroyable serait d’utiliser un proxy dans un DNS (qui permet d’accéder au web normal) et en plus d’avoir un driver pour celà. Sa permettrait d’accomplir ce que Chatavion fait mais en l’élargissant vers tout l’internet. Encore faut t’il quelqu’un pour tout faire..

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte