Encore un autre P2P Messenger
La lecture des revues et de la documentation linguistique ne suffit pas pour apprendre à y écrire des applications plus ou moins utiles.
Assurez-vous de consolider, vous devez créer quelque chose d'intéressant afin que les développements puissent être utilisés dans d'autres tâches.

Cet article est destiné aux débutants intéressés par la langue go et les réseaux peer-to-peer.
Et pour les professionnels qui peuvent proposer des idées raisonnables ou critiquer de manière constructive.
Je programme depuis un certain temps avec différents degrés d'immersion en java, php, js, python.
Et chaque langage de programmation est bon dans son domaine.
Le domaine principal de Go est la création de services distribués, de microservices.
Le plus souvent, un microservice est un petit programme qui exécute ses fonctionnalités hautement spécialisées.
Mais les microservices devraient toujours pouvoir communiquer entre eux, donc l'outil de création de microservices devrait permettre une mise en réseau facile et indolore.
Pour tester cela, nous allons écrire une application organisant un réseau décentralisé de pairs (Peer-To-Peer), le plus simple est un messager p2p (au fait, existe-t-il un synonyme russe pour ce mot?).
Dans le code, j'invente activement des vélos et monte sur le râteau pour sentir le golang, obtenir des critiques constructives et des suggestions rationnelles.
On fait quoi
Peer (peer) - une instance unique du messager.
Notre messager devrait pouvoir:
- Trouver des fêtes à proximité
- Établir une connexion avec d'autres pairs
- Crypter l'échange de données avec des pairs
- Recevoir des messages de l'utilisateur
- Afficher les messages à l'utilisateur
Pour rendre la tâche un peu plus intéressante, faisons-la passer par un seul port réseau.

Si vous tirez ce port sur HTTP, nous obtenons une application React qui tire le même port en établissant une connexion de socket Web.
Si vous tirez le port via HTTP et non depuis la machine locale, nous affichons la bannière.
Si un autre homologue est connecté à ce port, une connexion permanente est établie avec un chiffrement de bout en bout.
Déterminer le type de connexion entrante
Tout d'abord, ouvrez le port d'écoute et nous attendrons de nouvelles connexions.
net.ListenTCP("tcp", tcpAddr)
Sur la nouvelle connexion, lisez les 4 premiers octets.
Nous prenons la liste des verbes HTTP et comparons nos 4 octets avec elle.
Maintenant, nous déterminons si une connexion est établie à partir de la machine locale, et sinon, nous répondons avec une bannière et raccrochons.
buf, err := readWriter.Peek(4) if ItIsHttp(buf) { handleHttp(readWriter, conn, p) } else { peer := proto.NewPeer(conn) p.HandleProto(readWriter, peer) } if !strings.EqualFold(s, "127") && !strings.EqualFold(s, "[::") { response.Body = ioutil.NopCloser(strings.NewReader("Peer To Peer Messenger. see https://github.com/easmith/p2p-messenger")) }
Si la connexion est locale, nous répondons avec le fichier correspondant à la demande.
J'ai alors décidé d'écrire le traitement moi-même, même si je pouvais utiliser le gestionnaire disponible dans la bibliothèque standard.
Si le chemin /ws
demandé, alors nous essayons d'établir une connexion websocket.
Depuis que j'ai assemblé le vélo dans le traitement des demandes de fichiers, je vais faire le traitement de la connexion ws en utilisant la bibliothèque gorilla / websocket .
Pour ce faire, créez MyWriter
et implémentez-y des méthodes correspondant aux interfaces http.ResponseWriter
et http.Hijacker
.
Détection de pairs
Pour rechercher des homologues dans un réseau local, nous utiliserons la multidiffusion UDP.
Nous enverrons des paquets contenant des informations sur nous-mêmes à l'adresse IP de multidiffusion.
func startMeow(address string, p *proto.Proto) { conn, err := net.DialUDP("udp", nil, addr) for { _, err := conn.Write([]byte(fmt.Sprintf("meow:%v:%v", hex.EncodeToString(p.PubKey), p.Port))) time.Sleep(1 * time.Second) } }
Et écoutez séparément de Multicast IP pour tous les paquets UDP.
func listenMeow(address string, p *proto.Proto, handler func(p *proto.Proto, peerAddress string)) { conn, err := net.ListenMulticastUDP("udp", nil, addr) _, src, err := conn.ReadFromUDP(buffer)
Ainsi nous nous déclarons et apprenons l'apparition d'autres fêtes.
Il serait possible d'organiser cela au niveau IP, et même dans la documentation officielle du package IPv4, seul le paquet de données de multidiffusion est donné comme exemple de code.
Protocole d'interaction entre pairs
Nous emballerons toutes les communications entre pairs dans une enveloppe (enveloppe).
Sur toute enveloppe, il y a toujours un expéditeur et un destinataire, à cela nous ajouterons une commande (qu'il emporte avec lui), un identifiant (jusqu'à présent, c'est un nombre aléatoire, mais peut être fait comme un hachage de contenu), la longueur du contenu et le contenu de l'enveloppe elle-même - un message ou des paramètres de commande.

La commande, (ou le type de contenu) est placée avec succès au tout début de l'enveloppe et nous définissons une liste de commandes de 4 octets qui ne se croisent pas avec les noms des verbes HTTP.
L'enveloppe entière pendant la transmission est sérialisée en un tableau d'octets.
Poignée de main
Lorsque la connexion est établie, le festin tend immédiatement la main pour une poignée de main, donnant son nom, sa clé publique et sa clé publique éphémère pour générer une clé de session partagée.
En réponse, l'homologue reçoit un ensemble similaire de données, enregistre l'homologue trouvé dans sa liste et calcule (CalcSharedSecret) la clé de session commune.
func handShake(p *proto.Proto, conn net.Conn) *proto.Peer { peer := proto.NewPeer(conn) p.SendName(peer) envelope, err := proto.ReadEnvelope(bufio.NewReader(conn)) }
Échange de fête
Après une poignée de main, les pairs échangent leurs listes de pairs =)
Pour ce faire, une enveloppe contenant la commande LIST est envoyée et une liste JSON de pairs est placée dans son contenu.
En réponse, nous obtenons une enveloppe similaire.
On en retrouve dans les listes de nouveaux et avec chacun d'eux on essaie de se connecter, se serrer la main, échanger des fêtes etc.
Messagerie utilisateur
Les messages personnalisés sont de la plus grande valeur pour nous, nous allons donc crypter et signer chaque connexion.
À propos du chiffrement
Dans les bibliothèques golang standard (google) du paquet crypto, de nombreux algorithmes différents sont implémentés (il n'y a pas de normes GOST).
Le plus pratique pour les signatures, je pense, est la courbe Ed25519. Nous utiliserons la bibliothèque ed25519 pour signer des messages.
Au tout début, j'ai pensé à utiliser une paire de clés obtenue à partir de ed25519 non seulement pour signer, mais aussi pour générer une clé de session.
Cependant, les clés de signature ne sont pas applicables pour le calcul de la clé partagée - vous devez toujours les conjurer:
func CreateKeyExchangePair() (publicKey [32]byte, privateKey [32]byte) { pub, priv, err := ed25519.GenerateKey(nil) copy(publicKey[:], pub[:]) copy(privateKey[:], priv[:]) curve25519.ScalarBaseMult(&publicKey, &privateKey) }
Par conséquent, il a été décidé de générer des clés éphémères, et d'une manière générale, c'est la bonne approche qui ne laisse pas aux attaquants la possibilité de récupérer une clé commune.
Pour les amateurs de mathématiques, voici les liens wiki:
Protocole Diffie - Hellman_ sur les courbes elliptiques
Signature numérique EdDSA
La génération d'une clé partagée est assez standard: d'abord, pour une nouvelle connexion, nous générons des clés éphémères, nous envoyons une enveloppe avec une clé publique au socket.
Le côté opposé fait de même, mais dans un ordre différent: il reçoit une enveloppe avec une clé publique, génère sa propre paire et envoie la clé publique au socket.
Désormais, chaque participant dispose des clés éphémères publiques et privées de quelqu'un d'autre.
En les multipliant, nous obtenons la même clé pour les deux, que nous utiliserons pour crypter les messages.
Nous crypterons les messages par l'algorithme AES établi depuis longtemps en mode de couplage de blocs (CBC).
Toutes ces implémentations sont faciles à trouver dans la documentation de golang.
Le seul raffinement consiste à remplir automatiquement le message avec zéro octet pour la multiplicité de sa longueur à la longueur du bloc de chiffrement (16 octets).
En 2013, il a implémenté AES (avec un mode similaire à CBC) pour crypter les messages dans Telegram dans le cadre d'un concours de Pavel Durov.
À cette époque, le protocole Diffie-Hellman le plus courant était utilisé dans les télégrammes pour générer une clé éphémère.
Et afin d'exclure la charge des fausses connexions, avant chaque échange de clés, les clients ont résolu le problème de factorisation.
GUI
Nous devons afficher une liste de pairs et une liste de messages avec eux, et également répondre aux nouveaux messages en augmentant le compteur à côté du nom du pair.
Ici sans problèmes - ReactJS + websocket.
Les messages de socket Web sont essentiellement des enveloppes uniques, mais ils ne contiennent pas de texte chiffré.
Tous sont des "héritiers" de type WsCmd
et sont sérialisés en JSON lors du transfert.
Donc, une requête HTTP arrive à la racine ("/"), maintenant pour afficher le front, regardez dans le répertoire "front / build" et donnez index.html
Eh bien l'interface est constituée, maintenant le choix pour les utilisateurs est: l'exécuter dans un navigateur ou dans une fenêtre séparée - WebView.
Pour la dernière option utilisée zserge / webview
e := webview.Open("Peer To Peer Messenger", fmt.Sprintf("http://localhost:%v", initParams.Port), 800, 600, false)
Pour créer une application avec celle-ci, vous devez installer un autre système
sudo apt install libwebkit2gtk-4.0-dev
Au cours de la réflexion sur l'interface graphique, j'ai trouvé de nombreuses bibliothèques pour GTK, QT, et l'interface de la console aurait l'air très geek - https://github.com/jroimartin/gocui - à mon avis, une idée très intéressante.
Lancement de Messenger
Installation de Golang
Bien sûr, vous devez d'abord installer go.
Pour ce faire, je recommande fortement d'utiliser l'instruction golang.org/doc/install .
Instructions simplifiées pour bash script
Téléchargez une application dans GOPATH
Il est tellement arrangé que toutes les bibliothèques et même vos projets doivent être dans le soi-disant GOPATH.
Par défaut, c'est $ HOME / go. Go vous permet d'extraire la source du référentiel public avec une simple commande:
go get github.com/easmith/p2p-messenger
Maintenant, dans votre $HOME/go/src/github.com/easmith/p2p-messenger
source de la branche principale apparaîtra
Installation et montage avant du NPM
Comme je l'ai écrit ci-dessus, notre interface graphique est une application Web avec un front sur ReactJs, donc le front doit encore être assemblé.
Nodejs + npm - ici comme d'habitude.
Juste au cas où, voici les instructions pour Ubuntu
Maintenant, nous commençons l'assemblage avant en standard
cd front npm update npm run build
L'avant est prêt!
Lancement
Revenons à la racine et lançons la fête de notre messager.
Au démarrage, nous pouvons spécifier le nom de notre homologue, le port, le fichier avec les adresses des autres homologues et un indicateur indiquant s'il faut lancer WebView.
Par défaut, $USER@$HOSTNAME
est utilisé comme nom d' $USER@$HOSTNAME
et port 35035.
Donc, nous commençons et discutons avec des amis sur le réseau local.
go run app.go -name Snowden
Commentaires sur la programmation de Golang
Et ensuite?
Ainsi, le messager Peer-To-Peer le plus simple est implémenté.
Les cônes sont bondés, vous pouvez en outre améliorer les fonctionnalités de l'utilisateur: envoi de fichiers, d'images, d'audio, d'émoticônes, etc., etc.
Et vous ne pouvez pas inventer votre protocole et utiliser les tampons de protocole Google,
Connectez la chaîne de blocs et protégez-vous contre le spam à l'aide des contrats intelligents Ethereum.
Sur les contrats intelligents, organisez des discussions de groupe, des canaux, un système de noms, des avatars et des profils utilisateur.
Il est également impératif d'exécuter des homologues de départ, de mettre en œuvre un contournement NAT et d'envoyer des messages d'homologue à homologue.
En conséquence, vous obtenez un bon télégramme / téléphone de remplacement, il vous suffit d'y transférer tous vos amis =)
Utilité
Quelques liensAu cours des travaux sur le messager, j'ai trouvé des pages intéressantes pour un développeur débutant.
Je les partage avec vous:
golang.org/doc/ - documentation en langue, tout est simple, clair et avec des exemples. La même documentation peut être exécutée localement avec la commande
godoc -HTTP=:6060
gobyexample.com - une collection d'exemples simples
golang-book.ru - un bon livre en russe
github.com/dariubs/GoBooks est une collection de livres sur Go.
awesome-go.com - Une liste de bibliothèques, de frameworks et d'applications intéressantes à emporter . La catégorisation est plus ou moins, mais la description de beaucoup d'entre eux est très rare, ce qui n'aide pas la recherche par Ctrl + F