Date de publication du RFC : Mars 2011
Auteur(s) du RFC : T. Richardson, J. Levine (RealVNC)
Pour information
Première rédaction de cet article le 8 mars 2011
Il est assez fréquent à l'IETF qu'un protocole ne soit documenté dans un RFC que longtemps après son déploiement. C'est le cas de RFB (Remote Framebuffer Protocol), le protocole utilisé dans le système VNC, système qui permet de transmettre via un réseau TCP/IP l'interface graphique d'une machine. VNC permet, par exemple, d'avoir sur son ordinateur le bureau d'une machine distante, se s'y loguer, etc, comme si on était physiquement devant. C'est donc un concurrent de X11 avec XDMCP ou bien de NX mais avec l'avantage sur ce dernier d'avoir désormais une spécification publique, et de nombreuses mises en œuvre. Je compte une quarantaine de clients, de serveurs et de bibliothèques VNC sur mon Ubuntu.
RFB se veut un protocole simple, bien plus simple que X11, basé sur l'idée d'un serveur, tournant sur la machine distante, qui écrit dans une mémoire graphique (le framebuffer) qui est en fait située sur la machine de bureau (le client ; notez que « client » et « serveur » ont des sens différents de celui de X11). Le système VNC, qui utilise ce protocole, est très largement utilisé pour l'accès distant à une machine, exactement dans les mêmes conditions que si on était présent devant cette machine (système de fenêtrage, accès à la souris, etc).
RFB travaille au niveau de la mémoire graphique et est donc compatible avec tous les systèmes de fenêtrage comme X11 ou comme celui de Windows. Le protocole se veut simple, surtout côté client, pour pouvoir être mis en œuvre sur de petites machines. Il existe même un widget VNC/RFB pour GTK, pour ceux qui veulent mettre un client VNC dans leurs programmes.
Le client est sans état et peut donc redémarrer à tout moment et reprendre la session là où il l'avait laissée. C'est le serveur qui maintient l'état de l'interface utilisateur. Cela permet par exemple de changer de client en cours de session.
RFB, je l'ai dit, a été implémenté avant d'être spécifié et a connu plusieurs versions. Ce RFC essaie donc de documenter l'état actuel du protocole.
Si les détails prennent parfois de la place, les principes sont simples et les premières sections du RFC sont donc courtes. La section 2 décrit les connexions entre le client et le serveur. Le mode normal de fonctionnement de VNC est que le serveur tourne pendant une longue période, gardant en mémoire l'état de la mémoire graphique, le ou les clients se connectant à loisir, pendant parfois seulement de courtes périodes. Cela ressemble donc, adapté au graphique, au principe de screen. Le client peut être n'importe où sur l'Internet et se connecte par défaut au port 5900.
Loin de la richesse du protocole de
X11, RFB ne fournit qu'une seule primitive :
« afficher un rectangle à la position X,Y
indiquée » (section 3). Cela peut sembler très inefficace mais divers
encodages subtils permettent de rendre ce protocole supportable. Les
mises à jour (nouveaux rectangles à afficher) ne sont jamais envoyées par
le serveur mais demandées par le client (message
FramebufferUpdateRequest
, section 7.5.3). Le protocole est donc
adaptatif : un client lent ne demandera pas souvent des mises à jour
et aura donc moins de travail. Si une même région de la mémoire
graphique voit plusieurs modifications successives, le client lent
peut donc parfaitement sauter les étapes intermédiaires.
Afficher des rectangles, c'est très joli mais un environnement graphique digne de ce nom doit aussi permettre à l'utilisateur de dire quelque chose, par exemple de taper au clavier ou de cliquer avec la souris. La section 4 résume le protocole d'entrée de RFB : ce dernier ne connait que clavier et souris (multi-boutons). Tout autre périphérique d'entrée doit donc être modélisé comme clavier ou comme souris.
Et les pixels, comment sont-ils modélisés ? La section 5 décrit le modèle. Il repose sur une négociation entre client et serveur, à l'ouverture de la connexion, où le client est roi. C'est lui qui choisit la représentation des pixels sur le réseau, et il peut donc choisir ce qui lui fait le moins de travail, tout reposant alors sur le serveur. Les deux formats les plus classiques sont le « couleurs réelles » où chaque pixel est un nombre sur 24 bits, chaque groupe de 8 bits indiquant l'intensité de rouge, de vert ou de bleu, ou bien le « table des couleurs » où chaque pixel est un nombre sur 8 bits indiquant une entrée d'une table où sont stockées les 256 couleurs possibles.
Plusieurs encodages sont ensuite possibles dans RFB, six étant définis par notre RFC 6143, permettant plus ou moins de compression.
En parlant de négociation, comme RFB existe depuis un certain temps et a connu plusieurs versions, la section 6 décrit la notion de version du protocole. Notre RFC 6143 normalise la version 3.8. Les versions précédentes résumées dans l'annexe A. Si une version 4.x apparaîtra peut-être un jour, la version actuelle, la 3.8, est extensible sans avoir besoin de changer de version. On peut ainsi introduire :
Le protocole RFB lui-même figure dans la section 7. Elle est très longue, bien plus que les autres sections, mais pas forcément plus difficile, elle liste juste tous les messages possibles. RFB peut fonctionner sur n'importe quel protocole de transport fiable (comme TCP). La communication commence par une phase de négociation entre le client et le serveur, puis l'initialisation, puis l'envoi des messages de mise à jour de la mémoire graphique. La description des messages utilise quelques types de base comme U8 (entier de huits bits non signé), S16 (entier de seize bits signé, toujours en gros boutien), etc.
La section 7.1 décrit le protocole de négociation. Ainsi, le
serveur commence par envoyer un message où il indique la version
(message ProtocolVersion
, ici, le serveur à l'autre bout est x11vnc) :
% nc ec2-xxx.eu-west-1.compute.amazonaws.com 5900 RFB 003.008
La sécurité se négocie ensuite (section 7.2). La phase
d'initialisation est décrite en section 7.3. Par exemple, le message
ServerInit
contiendra la hauteur et la largeur de
la mémoire graphique en nombre de pixels (tous les deux en UA16),
ainsi que le format de représentation des pixels (décrit en section 7.4).
La représentation des caractères tapés au clavier du client
(message KeyEvent
, section 7.5.4) est
complexe. La valeur utilisée est un keysym
de X11
(même si ni le client, ni le serveur n'utilisent X11). Si vous avez
une machine Unix où X11 est installé, le
fichier /usr/include/X11/keysymdef.h
vous donnera
une idée des valeurs possibles. Cela aurait été
plus simple si un point de code Unicode était
envoyé... Mais il n'y a que pour ASCII que le
keysym
correspond au point de code. Le RFC va
même jusqu'à préciser qu'il vaut mieux utiliser le
keysymI
traditionnel, même s'il y en a un pour le
caractère Unicode. l faut dire
que le protocole RFB ne transmet pas que des caractères mais aussi des
états des touches (comme le fait que la touche Majuscule est pressée)
et des touches non affectées à un caractère particulier (comme F1,
F2, etc). Les règles d'interprétation des keysyms
sont d'une grande complexité. Le lecteur curieux doit se munir
d'aspirine avant de lire lui-même cette section.
Plus facile, le cas du « pointeur » (la souris) en section 7.5.5. Les événements sont simplement « bouton pressé » et « bouton relaché », les attributs étant la position X,Y et le numéro du bouton (huit boutons sont prévus, afin de pouvoir modéliser des dispositifs d'entrée plus complexes comme les roulettes).
Jusqu'à présent, je n'ai parlé que des messages envoyés par le
client RFB au serveur. En sens inverse, le principal message est
FramebufferUpdate
(section 7.6.1) qui indique un changement dans
la mémoire graphique. Il contient un certain nombre de rectangles et,
pour chacun d'eux, sa position X,Y, sa hauteur et sa largeur, et le
type d'encodage utilisé.
Les encodages sont définis en section 7.7. Les six standards à l'heure actuelle sont :
Raw
: comme son nom l'indique, aucun
effort, on envoie juste les pixels l'un après l'autre (largeur, puis
hauteur).CopyRect
: le serveur indique les
coordonnées d'un rectangle existant dans la mémoire du client. Cela
sert, par exemple, lorsqu'une fenêtre est déplacée sur l'écran.RRE encoding
: officiellement
abandonné.Hextile encoding
: officiellement
abandonné.TRLE encoding
: Tiled Run-Length
Encoding utilise un ensemble de techniques de
compression dont le
run-length encoding appliquée à des « tuiles » de 16x16
pixels.ZRLE encoding
: Zlib Run-Length
Encoding utilise la compression Zlib
(RFC 1950 et RFC 1951).La normalisation du Remote Framebuffer Protocol a nécessité quelques nouveaux registres IANA, décrits en section 8 : celui des types de sécurité, celui des messages RFB, et celui des encodages, qui permettra d'étendre la liste de six encodages présentée plus haut.
Quelle est la sécurité de RFB ? Par défaut, inexistante : comme le note justement la section 9, l'authentification est faible et la confidentialité et l'intégrité sont nulles. RFB est traditionnellement utilisé avec une protection fournie par, par exemple, un VPN chiffré, seule façon de l'utiliser de manière sûre.
L'annexe A du RFC résume les différences entre cette version du protocole (la 3.8) et les précédentes. Ces différences sont concentrées dans la phase de négociation.
À l'heure d'aujourd'hui, je n'ai pas trouvé de connaissance de ce
protocole dans tcpdump mais
Wireshark, par contre, le décode, sous son ancien
nom de VNC (qui est désormais uniquement le nom d'un logiciel). C'est
aussi le nom à utiliser sur pcapr : http://www.pcapr.net/browse?proto=vnc&order=date
.
Version PDF de cette page (mais vous pouvez aussi imprimer depuis votre navigateur, il y a une feuille de style prévue pour cela)
Source XML de cette page (cette page est distribuée sous les termes de la licence GFDL)