Dans le domaine du jeu vidéo, développer un client dans le but d'obtenir des informations sur un serveur (interrogation) relève souvent du parcours du combattant. Et pour cause : peu d'informations existent sur Internet à propos de ces protocoles.
Et quand bien même une documentation est transmise par le développeur (souvent dans le SDK), celle-ci est parfois très incomplète voire en partie erronnée (c'est le cas pour les protocoles que nous allons étudier).
J'ai écrit ce document afin de démystifier ces protocoles et par là même offrir une base solide aux développeurs. Il pourra aussi intéresser les curieux qui, dans leur soif de connaissance, souhaitent comprendre comment tout cela fonctionne.
Ce document est purement théorique. Il décrit les échanges entre un client et un serveur durant les opérations suivantes :
NOTE : depuis le 7 août 2005, ce document couvre aussi les serveurs HLTV et SourceTV, lesquels suivent les mêmes protocoles que leurs équivalents en serveurs de jeu, à quelques subtilités près.
NOTE : ce document remplace son prédécesseur, intitulé "Les protocoles d'Half-Life et Steam". Entièrement réécrit, il inclut le nouveau protocole Source, qui, depuis le 7 juin 2005, a été étendu à Half-Life 1. Le document précédent est donc obsolète et ne peut plus être utilisé pour interroger des serveurs Half-Life 1.
Les paquets d'exemple illustrant les échanges sont toujours déclinés en version héxadécimale, décimale et ASCII, pour une meilleure compréhension.
Ceci est un exemple de notation héxadécimale.
Ceci est un exemple de notation décimale.
Ceci est un exemple de chaîne ASCII.
NOTE : lorsque j'écris par exemple les 4 octets
, il s'agit de 4 octets de valeur héxadécimale FF FF FF FFFF, et non de la chaîne de caractères "FF FF FF FF". Cela peut paraître évident aux yeux de certains, mais beaucoup de développeurs PHP, peu habitués à utiliser autre chose que des chaînes, peuvent tomber dans le piège.
Le contenu des paquets échangés met en jeu 5 types de données :
byte : il s'agit d'un nombre entier codé sur un octet non signé; la plage s'étend de 0 à 255.short : nombre entier codé sur deux octets signé; de -32 768 à +32 767.long : nombre entier codé sur quatre octets signé; de -2 147 483 648 à +2 147 483 647.float : nombre à virgule flottante codé sur quatre octets signé; de 3.4 * 10-38 à 3.4 * 10+38.string : chaîne de caractères ASCII, terminée par le caractère NULL (0). Dans le cas du moteur Source, certaines chaînes peuvent contenir des caractères spéciaux; dans ce cas, l'encodage à utiliser est UTF-8.NOTE : les types de données byte, short, long et float peuvent être décodés en Perl et PHP grâce à la fonction unpack().
Lorsque vous cherchez des serveurs en utilisant le navigateur intégré à Steam, celui-ci va demander une liste de serveurs de jeu à un serveur appellé serveur Master, en précisant un certain nombre de filtres. Le Master répond ensuite en envoyant une liste d'adresses IP de serveurs de jeu.
Il y a deux serveurs Master connus :
steam1.steampowered.comhlmaster1.hlauth.netLe protocole est identique pour les deux moteurs, en revanche le port à utiliser diffère : 27010 pour HL1 et 27011 pour Source. La couche de communication à employer est UDP. Attention, la taille des données UDP envoyées par le serveur peut atteindre 1392 octets.
La requête se compose de 3 parties : le filtre de région, l'adresse IP de départ, et les autres filtres. Que signifie "adresse IP de départ" ? Eh bien en fait, en raison de la restriction de la taille des paquets UDP et du fait que ceux ci ne possèdent pas de système de réordonnancement, le serveur ne renvoie pas toute la liste d'un seul coup : il en envoie une partie, puis vous devez réenvoyer une requête pour avoir la partie suivante. Pour que le serveur sache à partir de quelle adresse le serveur doit continuer, il faut lui envoyer la dernière adresse IP que vous avez reçue dans les requêtes suivant la première.
Voici la composition du paquet à envoyer au serveur pour effectuer une requête :
1 (31 ou encore 49).byte, pouvant prendre les valeurs suivantes :
0 : Côte Est des Etats-Unis1 : Côte Ouest des Etats-Unis2 : Amérique du Sud3 : Europe4 : Asie5 : Australie6 : Pays de l'Est7 : Afrique255 : Toutes régionsstring sous la forme XXX.XXX.XXX.XXX:XXXXX. Pour le premier paquet, utilisez l'adresse 0.0.0.0:0.string. En voici la liste :
\gamedir\[mod] : filtrer selon le mod\map\[map] : filtrer selon la map\empty\1 : serveurs non vides\full\1 : serveurs non pleins\secure\1 : serveurs éxécutant l'anticheat VAC\type\d : serveurs dédiés\linux\1 : serveurs Linux\proxy\1 : serveurs HLTVExemple de requête :
31 ff 30 2e 30 2e 30 2e049 255 048 046 048 046 048 0461.0.0.0.30 3a 30 00 5c 67 61 6d048 058 048 000 092 103 097 1090:0.\gam65 64 69 72 5c 73 74 61101 100 105 114 092 115 116 097edir\sta72 67 61 74 65 74 63 5c114 103 097 116 101 116 099 092rgatetc\66 75 6c 6c 5c 31 5c 6c102 117 108 108 092 049 092 108full\1\l69 6e 75 78 5c 31 00105 110 117 120 092 049 000inux\1.
Dans cette requête, nous demandons la liste des serveurs de jeu Linux éxécutant le mod Stargate TC non pleins. Le serveur nous répond ceci :
ff ff ff ff 66 0a255 255 255 255 102 010....f.d5 ba 34 a3 69 8a213 186 052 163 105 138..4.i.d5 ba 3b 8d 69 89213 186 059 141 105 137..;.i.d5 fb 94 24 69 c3213 251 148 036 105 195...$i.d5 fb 94 ae 69 be213 251 148 174 105 190....i.00 00 00 00 00 00000 000 000 000 000 000......
Rassurez-vous, cette réponse est un véritable jeu d'enfant à analyser. Voici sa composition :
FF ou 255.f (66 ou encore 102).0a ou 10.byte, contiennent les 4 composantes de l'adresse IP.short (attention, non signé et ordre des bits little endian), forment le numéro de port.NULL (0) - ce qui donne donc 0.0.0.0:0 - la liste est terminée. Si ce n'est pas le cas, vous devez relancer une requête comme expliqué plus haut.
A la lumière de ces informations, nous pouvons en conclure que 4 serveurs de jeu correspondent à nos critères, et ceux ci ont comme adresses 213.186.52.163:27018, 213.186.59.141:27017, 213.251.148.36:27075 et 213.251.148.174:27070. Fort de ces données, vous pouvez à présent interroger ces différents serveurs, et c'est l'objet de la partie suivante.
Il s'agit du protocole le plus souvent utilisé dans le milieu des serveurs HL/Source (ou HLTV/SourceTV). Il permet d'obtenir toutes sortes d'informations sur un serveur de jeu :
Le challenge est un numéro fourni par le serveur suite à une requête CHALLENGE.
Ce numéro long de 4 octets doit ensuite obligatoirement être utilisé dans les requêtes PLAYERS et RULES, faute de quoi le serveur ne répondra pas. La documentation précise en outre que l'on peut utiliser le numéro de challenge -1 dans la requête; dans ce cas, le serveur renverra un nouveau challenge qu'il faudra utiliser dans une deuxième requête pour obtenir la véritable réponse.
Toutes les requêtes s'effectuent sur le port d'éxécution du serveur, en UDP.
Cette requête permet d'obtenir un Challenge.
Requête :
ff ff ff ff 57255 255 255 255 087....W
Réponse :
ff ff ff ff 41 b5 74 2b255 255 255 255 065 181 116 043....A.t+0f015.
Et voilà la décomposition :
FF ou 255.A (41 ou 65).long. Ici, 875835443.Voici la requête à envoyer :
ff ff ff ff 54 53 6f 75255 255 255 255 084 083 111 117....TSou72 63 65 20 45 6e 67 69114 099 101 032 069 110 103 105rce Engi6e 65 20 51 75 65 72 79110 101 032 081 117 101 114 121ne Query00000.
Oui, c'est bien "Source Engine Query", même pour un serveur HL1.
La réponse d'un serveur HL1 diffère de celle d'un serveur Source.
ff ff ff ff 49 07 46 52255 255 255 255 073 007 070 082....I.FR20 2d 20 44 49 41 43 52032 045 032 068 073 065 067 082- DIACR45 20 2d 20 54 65 61 6d069 032 045 032 084 101 097 109E - Team70 6c 61 79 20 46 75 6e112 108 097 121 032 070 117 110play Fun20 2d 20 52 45 43 52 55032 045 032 082 069 067 082 085- RECRU54 45 20 73 75 72 20 68084 069 032 115 117 114 032 104TE sur h74 74 70 3a 2f 2f 69 70116 116 112 058 047 047 105 112ttp://ip37 38 2e 66 72 65 65 2e055 056 046 102 114 101 101 04678.free.66 72 00 63 73 5f 74 77102 114 000 099 115 095 116 119fr.cs_tw69 6c 69 67 68 74 00 63105 108 105 103 104 116 000 099ilight.c73 74 72 69 6b 65 00 43115 116 114 105 107 101 000 067strike.C6f 75 6e 74 65 72 2d 53111 117 110 116 101 114 045 083ounter-S74 72 69 6b 65 3a 20 53116 114 105 107 101 058 032 083trike: S6f 75 72 63 65 00 f0 00111 117 114 099 101 000 240 000ource...17 20 00 64 6c 00 00 31023 032 000 100 108 000 000 049. .dl..12e 30 2e 30 2e 32 32 00046 048 046 048 046 050 050 000.0.0.22.
Voici la décomposition de tout ce joyeux foutoir :
FF ou 255.I (49 ou 73).byte indiquant la version du protocole utilisé. Ici, 7.string. Ici, FR - DIACRE - Teamplay Fun - RECRUTE sur http://ip78.free.fr.string. Ici, cs_twilight.string. Ici, cstrike.string. Ici, Counter-Strike: Source.byte indiquant l'ID de l'application dans Steam. Ici, 240. Ce numéro identifie le jeu sous Steam et permet par exemple de le lancer via l'éxécutable steam.exe avec le paramètre -applaunch XXX. NOTE : la documentation officielle est erronée au sujet de cette variable (ils indiquent un type short).NULL (0). Pour être franc, je ne sais pas exactement ce qu'il fout là, mais il doit faire partie de l'erreur susdite.byte indiquant le nombre de joueurs (ou le nombre de spectateurs pour un SourceTV) présents sur le serveur. Ici, 23.byte indiquant le nombre de slots, c'est à dire le nombre de joueurs (ou le nombre de spectateurs pour un SourceTV) maximum. Ici, 32.byte indiquant le nombre de bots présents sur le serveur. Ici, 0. Les add-ons de bots modifient automatiquement cette variable.d/64/100 : dédié.l/6c/108 : non dédié (listen).p/70/112 : SourceTV.l/6c/108 : Linux.w/77/119 : Windows.byte positionné à 1 si le serveur est protégé par un mot de passe, 0 sinon.byte positionné à 1 si le serveur éxécute l'anticheat VAC, 0 sinon.string. Ici, 1.0.0.22.ff ff ff ff 6d 32 31 33255 255 255 255 109 050 049 051....m2132e 32 35 31 2e 31 34 35046 050 053 049 046 049 052 053.251.1452e 32 33 35 3a 32 37 30046 050 051 053 058 050 055 048.235:27031 35 00 46 72 20 2d 20049 053 000 070 114 032 045 03215.Fr -44 65 61 74 68 20 4d 61068 101 097 116 104 032 077 097Death Ma74 63 68 20 23 6f 47 6b116 099 104 032 035 111 071 107tch #oGk20 53 65 72 76 65 75 72032 083 101 114 118 101 117 114Serveur20 00 64 65 5f 6e 75 6b032 000 100 101 095 110 117 107.de_nuk65 00 63 73 74 72 69 6b101 000 099 115 116 114 105 107e.cstrik65 00 43 6f 75 6e 74 65101 000 067 111 117 110 116 101e.Counte72 2d 53 74 72 69 6b 65114 045 083 116 114 105 107 101r-Strike00 11 12 2f 64 6c 00 01000 017 018 047 100 108 000 001.../dl..77 77 77 2e 63 6f 75 6e119 119 119 046 099 111 117 110www.coun74 65 72 2d 73 74 72 69116 101 114 045 115 116 114 105ter-stri6b 65 2e 6e 65 74 00 00107 101 046 110 101 116 000 000ke.net..00 01 00 00 00 00 9e f7000 001 000 000 000 000 158 247........0a 00 01 01 00010 000 001 001 000.....
FF ou 255.m (31 ou 109).string. Ici, 213.251.145.235:27015.string. Ici, Fr - Death Match #oGk Serveur.string. Ici, de_nuke.string. Ici, cstrike.string. Ici, Counter-Strike. Si vous interrogez un serveur HLTV, cette chaîne sera toujours fixée à HLTV.byte indiquant le nombre de joueurs présents sur le serveur (ou le nombre de spectateurs pour un HLTV). Ici, 17.byte indiquant le nombre de slots, c'est à dire le nombre de joueurs maximum (ou le nombre de spectateurs pour un HLTV). Ici, 18.byte indiquant la version du protocole utilisé. Ici, 47.d/64/100 : dédié.l/6c/108 : non dédié (listen).p/70/112 : HLTV.l/6c/108 : Linux.w/77/119 : Windows.byte positionné à 1 si le serveur est protégé par un mot de passe, 0 sinon.byte indiquant si les détails du mod sont présents ou non dans la réponse. Si tel est le cas, 6 données supplémentaires apparaissent après cet octet. Dans le cas contraire, celles-ci ne sont pas présentes. Dans notre cas, ces données supplémentaires sont présentes. Notez que ces détails se retrouvent dans le fichier liblist.gam situé dans le répertoire du mod sur le serveur.string. Ici, www.counter-strike.net.string. Ici, il n'y en a pas, Counter-Strike 1.6 se téléchargeant via Steam. On trouve donc une chaîne de longueur 0.string vide. La documentation officielle indique qu'il s'agit d'une chaîne inutilisée.long indiquant la version du mod. Ici, 1.long indiquant la taille du fichier à télécharger pour installer le mod. Ici, 184000000.byte positionné à 1 si il s'agit d'un mod serveur uniquement (en clair : un mod solo), 0 si il s'agit d'un mod client-serveur. Il est théoriquement possible d'interroger une partie solo, bien que je n'aie jamais essayé et que je n'y voie aucun intérêt.byte positionné à 1 si le mod utilise une DLL client différente de celle d'HL (ce qui est le cas pour tous les "vrais" mods), 0 sinon.byte positionné à 1 si le serveur est protégé par l'anticheat VAC, 0 sinon.byte indiquant le nombre de bots présents sur le serveur. Ici, 0. Les add-ons de bots modifient automatiquement cette variable.ATTENTION : un bug touche les serveurs HLTV dans la réponse à cette requête. Ce bug ne survient que si le nombre de slots est égal à 255 (le maximum) : dans ce cas précis, le serveur va "péter les plombs" juste après l'octet indiquant la présence d'un mot de passe, et va envoyer une chaîne d'octets incompréhensible (00 00 00 00 00 00 ff 00 00 00 ou 000 000 000 000 000 000 255 000 000 000) là où auraient dû se trouver les informations sur VAC et le nombre de bots. Cette chaîne d'octets n'a pas de signification et doit être ignorée. Malheureusement, je n'ai pas réussi à déterminer où se trouvent les informations sur VAC et les bots dans cette chaîne buggée; néanmoins, cela importe peu car ces octets sont toujours à 0 sur un HLTV.
NOTE : n'essayez pas cette requête sur un serveur SourceTV, ce dernier ne répondra pas. En revanche, un HLTV y répondra, lui.
Requête :
ff ff ff ff 55 33 30 34255 255 255 255 085 051 048 052....U304340524
Les 4 derniers octets consituent le fameux Challenge (type long). Si vous envoyez un challenge eronné, le serveur vous répondra par une réponse CHALLENGE (voir requête Challenge, plus haut).
Et voici l'exemple de réponse :
ff ff ff ff 44 05 00 3c255 255 255 255 068 005 000 060....D..<6f 5f 30 3e 20 4a 69 6e111 095 048 062 032 074 105 110o_0> Jin78 65 64 00 05 00 00 00120 101 100 000 005 000 000 000xed.....0a 07 98 43 06 53 74 65010 007 152 067 006 083 116 101...C.Ste79 72 6d 34 30 00 02 00121 114 109 052 048 000 002 000yrm40...00 00 be 07 bd 44 09 4b000 000 190 007 189 068 009 075.....D.K75 70 6f 20 4b 75 70 6f117 112 111 032 075 117 112 111upo Kupo00 12 00 00 00 40 76 48000 018 000 000 000 064 118 072.....@vH45 0c 53 67 74 53 73 73069 012 083 103 116 083 115 115E.SgtSss79 78 00 08 00 00 00 89121 120 000 008 000 000 000 137yx......9c 1b 45 24 5b 35 5d 6b156 027 069 036 091 053 093 107..E$[5]k6f 6c e2 80 a2 4b 39 2e111 108 226 128 162 075 057 046ol...K9.53 6f 6c 64 69 65 72 5b083 111 108 100 105 101 114 091Soldier[5d 00 24 00 00 00 32 6b093 000 036 000 000 000 050 107].$...2kb8 46184 070.F
Et sa décomposition :
NOTE : si vous interrogez un serveur HLTV, la réponse concerne les joueurs en train de jouer, et non les spectateurs.
FF ou 255.D (44 ou 68).byte indiquant le nombre de joueurs sur le serveur.byte indiquant l'index (ID) du joueur. NOTE : la documentation officielle indique que les joueurs sont numérotés selon ID Є [0;X[, où X est le nombre de joueurs. En réalité, les IDs de joueurs peuvent être n'importe quel nombre.string.long indiquant le score du joueur (toujours égal à 0 si vous interrogez un HLTV).float indiquant depuis combien de temps le joueur est connecté au serveur, en secondes (toujours égal à 1 si vous interrogez un HLTV).On a donc, pour notre exemple, 5 joueurs :
0 : <o_0> Jinxed, 5 frags, connecté depuis 304.05499267578 secondes.6 : Steyrm40, 2 frags, connecté depuis 1512.2419433594 secondes.9 : SgtSssyx, 8 frags, connecté depuis 2489.7834472656 secondes.36 : [5]kol•K9.Soldier[], 36 frags, connecté depuis 23605.59765625 secondes.Mais au fait, où est le cinquième joueur ? Eh bien, lorsque le nombre de joueurs annoncé au début du paquet est supérieur au nombre de joueurs de la liste, cela signifie que le (ou les) joueur "fantôme" est en train de se connecter. On peut donc en conclure que le nombre au début donne le nombre de connectés, tandis que la suite donne la liste des connectés réellement en train de jouer.
NOTE : n'essayez pas cette requête sur un serveur SourceTV, ce dernier ne répondra pas. En revanche, un HLTV y répondra, lui.
Requête :
ff ff ff ff 56 33 30 34255 255 255 255 086 051 048 052....V304340524
Les 4 derniers octets consituent le fameux Challenge (type long). Si vous envoyez un challenge eronné, le serveur vous répondra par une réponse CHALLENGE (voir requête Challenge, plus haut).
Réponse :
ff ff ff ff 45 36 00 6d255 255 255 255 069 054 000 109....E6.m70 5f 68 6f 73 74 61 67112 095 104 111 115 116 097 103p_hostag65 70 65 6e 61 6c 74 79101 112 101 110 097 108 116 121epenalty00 31 33 00 6d 70 5f 61000 049 051 000 109 112 095 097.13.mp_a75 74 6f 74 65 61 6d 62117 116 111 116 101 097 109 098utoteamb61 6c 61 6e 63 65 00 31097 108 097 110 099 101 000 049alance.100 6d 70 5f 6d 61 78 72000 109 112 095 109 097 120 114.mp_maxr6f 75 6e 64 73 00 30 00111 117 110 100 115 000 048 000ounds.0.[...] [...] [...]73 61 62 6c 65 00 30 00115 097 098 108 101 000 048 000sable.0.73 76 5f 63 68 65 61 74115 118 095 099 104 101 097 116sv_cheat73 00 30 00 63 6f 6f 70115 000 048 000 099 111 111 112s.0.coop00 30 00 64 65 61 74 68000 048 000 100 101 097 116 104.0.death6d 61 74 63 68 00 31 00109 097 116 099 104 000 049 000match.1.74 76 5f 72 65 6c 61 79116 118 095 114 101 108 097 121tv_relay70 61 73 73 77 6f 72 64112 097 115 115 119 111 114 100password00 30 00 74 76 5f 70 61000 048 000 116 118 095 112 097.0.tv_pa73 73 77 6f 72 64 00 30115 115 119 111 114 100 000 048ssword.000 73 76 5f 70 61 73 73000 115 118 095 112 097 115 115.sv_pass77 6f 72 64 00 30 00119 111 114 100 000 048 000word.0.
Le listing a été volontairement coupé pour faciliter la lecture.
FF ou 255.E (45 ou 69).byte indiquant le nombre de CVARs publiques envoyées.string.string.En raison de la taille du paquet et de la simplicité de la décomposition, je n'analyserai pas l'exemple ici.
NOTE : avec cette requête, il peut vous arriver très souvent de recevoir une réponse multipaquets (FE FF FF FF au lieu de FF FF FF FF). Explications dans la section suivante.
Pour le protocole d'interrogation, UDP était tout destiné. En effet, cette couche est clairement adaptée à des transmissions de type requête/réponse; là où il faut 2 paquets en UDP (sauf pour les requêtes PLAYERS et RULES) pour formuler une requête et obtenir une réponse, l'utilisation de TCP aurait nécéssité l'ouverture d'une connexion, soit 4 paquets supplémentaires.
Le MTU recommandé pour UDP est de 512 octets; au-delà, certains routeurs pourraient poser problème. Valve a cependant décidé d'outrepasser cette limite et a fixé le MTU de son protocole à 1400 octets. Mais certaines réponses, comme celle d'une requête RULES, peuvent largement dépasser cette taille maximale.
A ce stade, vous pouvez vous demander quel est le problème : il suffit d'envoyer plusieurs paquets. En effet, mais rapellons que UDP, à l'inverse de TCP, ne gère pas le réordonnancement des paquets à l'arrivée. Autrement dit, vous pouvez très bien recevoir le deuxième paquet avant le premier, ça arrive même souvent...
Valve a donc prévu son propre système de réordonnancement. Il s'agit d'un en-tête spécial ajouté au début de chacun des deux paquets (une sorte de "pseudo-couche").
Voici un exemple d'en-tête :
fe ff ff ff cd 5e 00 00254 255 255 255 205 094 000 000.....^..02002.
Voici sa décomposition :
FE FF FF FF (et non FF FF FF FF)long indiquant le numéro de transaction. Il s'agit d'un nombre sans signification particulière, identique dans tous les paquets de la réponse, et permet de différencier deux réponses multipaquets différentes. Ainsi, si vous recevez à la fois une réponse PLAYERS multipaquets et réponse RULES elle aussi multipaquets, vous pourrez faire la différence entre les paquets de l'une et les paquets de l'autre.0. Ici, le numéro du paquet est 0 (c'est donc le premier) et le total des paquets est 2.Voici un exemple d'une réponse multipaquets à une requête RULES sur un serveur Counter-Strike 1.6 :
Premier paquet reçu :
fe ff ff ff da 60 00 00254 255 255 255 218 096 000 000.....`..12 6d 61 78 72 61 74 65018 109 097 120 114 097 116 101.maxrate00 32 35 30 30 30 00 73000 050 053 048 048 048 000 115.25000.s76 5f 6d 61 78 73 70 65118 095 109 097 120 115 112 101v_maxspe65 64 00 39 30 30 00 73101 100 000 057 048 048 000 115ed.900.s76 5f 6d 69 6e 72 61 74118 095 109 105 110 114 097 116v_minrat65 00 32 35 30 30 00 73101 000 050 053 048 048 000 115e.2500.s76 5f 70 61 73 73 77 6f118 095 112 097 115 115 119 111v_passwo72 64 00 30 00 73 76 5f114 100 000 048 000 115 118 095rd.0.sv_70 72 6f 78 69 65 73 00112 114 111 120 105 101 115 000proxies.[...] [...] [...]31 30 00 73 76 5f 77 61049 048 000 115 118 095 119 09710.sv_wa74 65 72 66 72 69 63 74116 101 114 102 114 105 099 116terfrict69 6f 6e 00 31 00105 111 110 000 049 000ion.1.
Deuxième paquet reçu :
fe ff ff ff da 60 00 00254 255 255 255 218 096 000 000.....`..02 ff ff ff ff 45 57 00002 255 255 255 255 069 087 000.....EW.5f 74 75 74 6f 72 5f 62095 116 117 116 111 114 095 098_tutor_b6f 6d 62 5f 76 69 65 77111 109 098 095 118 105 101 119omb_view61 62 6c 65 5f 63 68 65097 098 108 101 095 099 104 101able_che63 6b 5f 69 6e 74 65 72099 107 095 105 110 116 101 114ck_inter76 61 6c 00 30 2e 35 00118 097 108 000 048 046 053 000val.0.5.[...] [...] [...]73 76 5f 61 6c 6c 74 61115 118 095 097 108 108 116 097sv_allta6c 6b 00 30 00 73 76 5f108 107 000 048 000 115 118 095lk.0.sv_62 6f 75 6e 63 65 00 31098 111 117 110 099 101 000 049bounce.100 73 76 5f 63 68 65 61000 115 118 095 099 104 101 097.sv_chea74 73 00 30 00 73 76 5f116 115 000 048 000 115 118 095ts.0.sv_63 6c 69 65 6e 74 74 72099 108 105 101 110 116 116 114clienttr61 63 65 00 31 00 73 76097 099 101 000 049 000 115 118ace.1.sv5f 63 6c 69 70 6d 6f 64095 099 108 105 112 109 111 100_clipmod65 00 30 00 73 76 5f 63101 000 048 000 115 118 095 099e.0.sv_c6f 6e 74 61 63 74 00 77111 110 116 097 099 116 000 119ontact.w77 77 2e 54 41 44 2d 47119 119 046 084 065 068 045 071ww.TAD-G61 6d 65 73 2e 63 6f 6d097 109 101 115 046 099 111 109ames.com00 73 76 5f 66 72 69 63000 115 118 095 102 114 105 099.sv_fric74 69 6f 6e 00 34 00 73116 105 111 110 000 052 000 115tion.4.s76 5f 67 72 61 76 69 74118 095 103 114 097 118 105 116v_gravit79 00 38 30 30 00 73 76121 000 056 048 048 000 115 118y.800.sv5f 6c 6f 67 62 6c 6f 63095 108 111 103 098 108 111 099_logbloc6b 73 00 30 00 73 76 5f107 115 000 048 000 115 118 095ks.0.sv_
Vous pouvez constater que le deuxième partie est arrivée avant la première. Une fois les deux parties remises dans l'ordre et les deux en-têtes supprimés, on retourne à une réponse monopaquet :
ff ff ff ff 45 57 00 5f255 255 255 255 069 087 000 095....EW._74 75 74 6f 72 5f 62 6f116 117 116 111 114 095 098 111tutor_bo6d 62 5f 76 69 65 77 61109 098 095 118 105 101 119 097mb_viewa62 6c 65 5f 63 68 65 63098 108 101 095 099 104 101 099ble_chec6b 5f 69 6e 74 65 72 76107 095 105 110 116 101 114 118k_interv61 6c 00 30 2e 35 00 5f097 108 000 048 046 053 000 095al.0.5._74 75 74 6f 72 5f 64 65116 117 116 111 114 095 100 101tutor_de62 75 67 5f 6c 65 76 65098 117 103 095 108 101 118 101bug_leve6c 00 30 00 5f 74 75 74108 000 048 000 095 116 117 116l.0._tut6f 72 5f 65 78 61 6d 69111 114 095 101 120 097 109 105or_exami6e 65 5f 74 69 6d 65 00110 101 095 116 105 109 101 000ne_time.[...] [...] [...]73 76 5f 72 65 67 69 6f115 118 095 114 101 103 105 111sv_regio6e 00 33 00 73 76 5f 72110 000 051 000 115 118 095 114n.3.sv_r65 73 74 61 72 74 00 30101 115 116 097 114 116 000 048estart.000 73 76 5f 72 65 73 74000 115 118 095 114 101 115 116.sv_rest61 72 74 72 6f 75 6e 64097 114 116 114 111 117 110 100artround00 30 00 73 76 5f 73 74000 048 000 115 118 095 115 116.0.sv_st65 70 73 69 7a 65 00 31101 112 115 105 122 101 000 049epsize.138 00 73 76 5f 73 74 6f056 000 115 118 095 115 116 1118.sv_sto70 73 70 65 65 64 00 37112 115 112 101 101 100 000 055pspeed.735 00 73 76 5f 76 6f 69053 000 115 118 095 118 111 1055.sv_voi63 65 65 6e 61 62 6c 65099 101 101 110 097 098 108 101ceenable00 31 00 73 76 5f 77 61000 049 000 115 118 095 119 097.1.sv_wa74 65 72 61 63 63 65 6c116 101 114 097 099 099 101 108teraccel65 72 61 74 65 00 31 30101 114 097 116 101 000 049 048erate.1000 73 76 5f 77 61 74 65000 115 118 095 119 097 116 101.sv_wate72 66 72 69 63 74 69 6f114 102 114 105 099 116 105 111rfrictio6e 00 31 00110 000 049 000n.1.
Le paquet reconstitué est prêt pour l'analyse.
Ce protocole vous permet d'envoyer des commandes à la console du serveur à distance. Vous pouvez éxécuter via rCON toutes les commandes que vous pourriez faire en étant directement sur la console.
Notez que ce document n'explique pas les commandes disponibles, mais comment les encapsuler dans des paquets rCON pour les envoyer au serveur. Si vous cherchez une liste des commandes, consultez les sites spécialisés.
Pour ce protocole, HL 1 et Source utilisent des protocoles radicalement différents.
Comme pour une partie du protocole d'interrogation, l'éxécution de commandes rCON nécéssite la demande d'un challenge (que ce soit pour HL1 ou pour Source). Le challenge améliore ici grandement la sécurité en établissant une "poignée de main en 3 parties", ce qui rend impossible une éventuelle usurpation de l'adresse source (spoofing).
Pour établir une connexion rCON, il est donc obligatoire de demander un challenge.
Les échanges se font, comme pour le protocole d'interrogation, sur le port du serveur en UDP. Bonne nouvelle : une grande partie du protocole est en texte clair.
Vous devez obligatoirement commencer par cette requête. La voici :
ff ff ff ff 63 68 61 6c255 255 255 255 099 104 097 108....chal6c 65 6e 67 65 20 72 63108 101 110 103 101 032 114 099lenge rc6f 6e 0a111 110 010on.
Voici un exemple de réponse :
ff ff ff ff 63 68 61 6c255 255 255 255 099 104 097 108....chal6c 65 6e 67 65 20 72 63108 101 110 103 101 032 114 099lenge rc6f 6e 20 32 30 37 36 37111 110 032 050 048 055 054 055on 2076737 34 32 31 0a 00055 052 050 049 010 0007421..
Analyse :
FF ou 255.string contenant :
challenge rcon .207677421.Voici la composition de la requête :
FF ou 255.string contenant :
rcon .Voici un exemple de requête, éxécutant une commande status :
ff ff ff ff 72 63 6f 6e255 255 255 255 114 099 111 110....rcon20 32 30 37 36 37 37 34032 050 048 055 054 055 055 052207677432 31 20 22 70 61 73 73050 049 032 034 112 097 115 11521 "pass65 22 20 73 74 61 74 75101 034 032 115 116 097 116 117e" statu73 00115 000s.
De là, nous avons 3 cas de réponses possibles.
ff ff ff ff 39 42 61 64255 255 255 255 057 066 097 100....9Bad20 63 68 61 6c 6c 65 6e032 099 104 097 108 108 101 110challen67 65 2e 0a 00103 101 046 010 000ge...
ff ff ff ff 6c 42 61 64255 255 255 255 108 066 097 100....lBad20 72 63 6f 6e 5f 70 61032 114 099 111 110 095 112 097rcon_pa73 73 77 6f 72 64 2e 0a115 115 119 111 114 100 046 010ssword..00 00000 000..
NOTE : l'encapsulation de la réponse est en tous points identique à celle d'une éxécution réussie. Cela est dû au fait que HL considère la réponse "Bad rcon_password" comme une réponse à la tentative d'éxécution de la commande.
ff ff ff ff 6c 68 6f 73255 255 255 255 108 104 111 115....lhos74 6e 61 6d 65 3a 20 20116 110 097 109 101 058 032 032tname:46 52 20 2d 20 53 65 72070 082 032 045 032 083 101 114FR - Ser76 65 75 72 20 2a 49 6d118 101 117 114 032 042 073 109veur *Im75 6e 69 54 79 2a 20 2d117 110 105 084 121 042 032 045uniTy* -20 31 38 20 73 6c 6f 74032 049 056 032 115 108 111 11618 slot73 20 50 75 62 6c 69 63115 032 080 117 098 108 105 099s Public0a 76 65 72 73 69 6f 6e010 118 101 114 115 105 111 110.version20 3a 20 20 34 37 2f 31032 058 032 032 052 055 047 049: 47/12e 31 2e 32 2e 35 2f 53046 049 046 050 046 053 047 083.1.2.5/S74 64 69 6f 20 33 31 34116 100 105 111 032 051 049 052tdio 31434 20 69 6e 73 65 63 75052 032 105 110 115 101 099 1174 insecu72 65 0a 74 63 70 2f 69114 101 010 116 099 112 047 105re.tcp/i70 20 20 3a 20 20 32 31112 032 032 058 032 032 050 049p : 2133 2e 32 35 31 2e 31 36051 046 050 053 049 046 049 0543.251.1631 2e 35 33 3a 32 37 30049 046 053 051 058 050 055 0481.53:27031 37 0a 6d 61 70 20 20049 055 010 109 097 112 032 03217.map20 20 20 3a 20 20 64 65032 032 032 058 032 032 100 101: de5f 61 7a 74 65 63 20 61095 097 122 116 101 099 032 097_aztec a74 3a 20 30 20 78 2c 20116 058 032 048 032 120 044 032t: 0 x,30 20 79 2c 20 30 20 7a048 032 121 044 032 048 032 1220 y, 0 z0a 70 6c 61 79 65 72 73010 112 108 097 121 101 114 115.players20 3a 20 20 30 20 61 63032 058 032 032 048 032 097 099: 0 ac74 69 76 65 20 28 31 38116 105 118 101 032 040 049 056tive (1820 6d 61 78 29 0a 0a 23032 109 097 120 041 010 010 035max)..#20 20 20 20 20 20 6e 61032 032 032 032 032 032 110 097na6d 65 20 75 73 65 72 69109 101 032 117 115 101 114 105me useri64 20 75 6e 69 71 75 65100 032 117 110 105 113 117 101d unique69 64 20 66 72 61 67 20105 100 032 102 114 097 103 032id frag74 69 6d 65 20 70 69 6e116 105 109 101 032 112 105 110time pin67 20 6c 6f 73 73 20 61103 032 108 111 115 115 032 097g loss a64 72 0a 30 20 75 73 65100 114 010 048 032 117 115 101dr.0 use72 73 0a 00 00114 115 010 000 000rs...
FF ou 255.l (6c ou 108).string. Ici :
hostname: FR - Serveur *ImuniTy* - 18 slots Public version : 47/1.1.2.5/Stdio 3144 insecure tcp/ip : 213.251.161.53:27017 map : de_aztec at: 0 x, 0 y, 0 z players : 0 active (18 max) # name userid uniqueid frag time ping loss adr 0 users
NULL (0).Nous sommes toujours en UDP, et le MTU des paquets rcon envoyés par le serveur est toujours fixé à 1400 octets. La réponse à certaines commandes, telles que cmdlist par exemple, peut allégrement dépasser ce nombre. Le serveur enverra donc plusieurs paquets.
La gestion des réponses multipaquets du protocole rCON pose bien plus de problèmes que le système de réponse multipaquets du protocole d'interrogation : ici, il n'y a pas d'en-tête additionnel. Du coup, il est impossible de savoir par avance si une réponse est multimorceaux, et il est impossible de savoir quand la transmission est réellement terminée. Cela implique d'écouter le socket en permanence; les développeurs travaillant sur une architecture évenementielle (un client GUI, par exemple) ne seront pas vraiment gênés par ce problème, en revanche ça peut rapidement devenir un véritable casse-tête pour ceux développant sur des architectures procédurales (telles que des scripts PHP tournant dans des pages Web).
Il n'existe aucune solution connue à ce problème.
Le protocole rCON du moteur Source paraît mieux organisé que celui de HL1. Mais surtout, ce qui fait la différence, c'est bien le fait que ce protocole utilise TCP, une première dans les protocoles HL. Cela garantit une bien meilleure fiabilité et un contrôle de connexion transparent, ce qui rend obsolète l'usage d'un Challenge.
Ici, tout se fait via TCP, sur le port du serveur.
Chaque transmission (et non plus "paquet", car on est en TCP), dans les deux sens, est encapsulée dans un modèle dont la composition est la suivante :
long indiquant la taille totale de la transmission sans compter l'indication de taille elle-même (autrement dit, la taille totale du paquet moins 4).long indiquant le numéro de transaction. Ce numéro permet de savoir à quelle question correspond telle réponse. Vous pouvez choisir le numéro que vous désirez, mais je vous recommande de partir de 0 et d'incrémenter à chaque requête.NULL (0).A partir de maintenant, toutes les analyses de transmissions seront faites sans mentionner ce modèle. En revanche, les listings d'échanges sont en entier.
Avant d'envoyer des commandes, il faut s'authentifier. Pour ce faire, il suffit d'envoyer une requête de cette composition :
3, sous le type long.string.Exemple avec le mot de passe "pass" :
0e 00 00 00 00 00 00 00014 000 000 000 000 000 000 000........03 00 00 00 70 61 73 73003 000 000 000 112 097 115 115....pass00 00000 000..
La réponse est en fait constituée de deux transmissions, vous devez donc lire deux fois votre socket. Voici la première :
0a 00 00 00 00 00 00 00010 000 000 000 000 000 000 000........00 00 00 00 00 00000 000 000 000 000 000......
En fait, ce paquet est une sorte de bug : il ne sert à rien. Sautez-le en utilisant la taille donnée dans l'en-tête, et passez au suivant pour avoir le vrai résultat.
0a 00 00 00 ff ff ff ff010 000 000 000 255 255 255 255........02 00 00 00 00 00002 000 000 000 000 000......
Remarquez le numéro de transaction fixé à -1.
0a 00 00 00 00 00 00 00010 000 000 000 000 000 000 000........02 00 00 00 00 00002 000 000 000 000 000......
Une fois l'authentification réussie, vous pouvez commencer à envoyer des commandes.
Voici la composition de la requête :
2, sous le type long.string.Exemple avec une commande "status" :
10 00 00 00 01 00 00 00016 000 000 000 001 000 000 000........02 00 00 00 73 74 61 74002 000 000 000 115 116 097 116....stat75 73 00 00117 115 000 000us..
Si vous n'étiez pas authentifié lorsque vous avez envoyé la requête, le serveur vous enverra une réponse identique à celle "authentification échouée" (voir plus haut).
Sinon, voici un exemple de réponse lors d'une éxécution réussie :
f6 00 00 00 01 00 00 00246 000 000 000 001 000 000 000........00 00 00 00 68 6f 73 74000 000 000 000 104 111 115 116....host6e 61 6d 65 3a 20 20 46110 097 109 101 058 032 032 070name: F52 2d 2a 41 2e 46 2e 4b082 045 042 065 046 070 046 075R-*A.F.K2a 20 53 65 72 76 65 75042 032 083 101 114 118 101 117* Serveu72 2d 56 65 6e 65 7a 20114 045 086 101 110 101 122 032r-Venez6c 65 73 20 67 61 72 73108 101 115 032 103 097 114 115les gars21 5b 52 65 63 72 75 74033 091 082 101 099 114 117 116![Recrut5d 0a 76 65 72 73 69 6f093 010 118 101 114 115 105 111].versio6e 20 20 20 3a 20 31 2e110 032 032 032 058 032 049 046n : 1.30 2e 30 2e 32 32 2f 37048 046 048 046 050 050 047 0550.0.22/720 32 34 32 35 20 73 65032 050 052 050 053 032 115 1012425 se63 75 72 65 0a 75 64 70099 117 114 101 010 117 100 112cure.udp2f 69 70 20 20 3a 20 20047 105 112 032 032 058 032 032/ip :32 31 33 2e 32 35 31 2e050 049 051 046 050 053 049 046213.251.31 36 30 2e 32 30 36 3a049 054 048 046 050 048 054 058160.206:32 37 30 32 37 0a 6d 61050 055 048 050 055 010 109 09727027.ma70 20 20 20 20 20 3a 20112 032 032 032 032 032 058 032p :20 64 65 5f 64 75 73 74032 100 101 095 100 117 115 116de_dust32 20 61 74 3a 20 30 20050 032 097 116 058 032 048 0322 at: 078 2c 20 30 20 79 2c 20120 044 032 048 032 121 044 032x, 0 y,30 20 7a 0a 70 6c 61 79048 032 122 010 112 108 097 1210 z.play65 72 73 20 3a 20 20 30101 114 115 032 058 032 032 048ers : 020 28 31 38 20 6d 61 78032 040 049 056 032 109 097 120(18 max29 0a 0a 23 20 75 73 65041 010 010 035 032 117 115 101)..# use72 69 64 20 6e 61 6d 65114 105 100 032 110 097 109 101rid name20 75 6e 69 71 75 65 69032 117 110 105 113 117 101 105uniquei64 20 63 6f 6e 6e 65 63100 032 099 111 110 110 101 099d connec74 65 64 20 70 69 6e 67116 101 100 032 112 105 110 103ted ping20 6c 6f 73 73 20 73 74032 108 111 115 115 032 115 116loss st61 74 65 20 61 64 72 0a097 116 101 032 097 100 114 010ate adr.00 00000 000..
Qui s'analyse comme suit :
0, sous le type long.string.Le résultat d'une commande telle que cvarlist peut dépasser les 4000 octets. Or, aussi stupide que cela puisse paraître, le serveur renverra ce résultat en plusieurs morceaux. A se demander pourquoi Valve a choisi TCP si c'est pour nous resservir des fragmentations de données...
A l'instar du protocole rcon HL1, la gestion des réponses multimorceaux du protocole rCON Source pose bien plus de problèmes que le système de réponse multipaquets du protocole d'interrogation : ici, il n'y a pas d'en-tête additionnel. Du coup, il est impossible de savoir par avance si une réponse est multimorceaux, et il est impossible de savoir quand la transmission est réellement terminée. Cela implique d'écouter le socket en permanence; les développeurs travaillant sur une architecture évenementielle (un client GUI, par exemple) ne seront pas vraiment gênés par ce problème, en revanche ça peut rapidement devenir un véritable casse-tête pour ceux développant sur des architectures procédurales (telles que des scripts PHP tournant dans des pages Web).
En pratique, la réponse est coupée tous les 4000 octets environ. Ce nombre varie de quelques unités, le serveur faisant en sorte que les coupures se fassent entre deux éléments de la réponse (dans le cas de cvarlist, entre deux CVARs).
Cela se traduit par plusieurs transmissions (donc plusieurs en-têtes de taille, de numéro de transaction, et de type de réponse (le nombre long 0)), mises bout-à-bout. Elles portent toutes le même numéro de transaction.
Je viens de découvrir une solution à ce problème. Cette solution peut, avec une moindre fiabilité, être transposée sur HL1.
Cette solution permet d'obtenir un EOT de la part du serveur avec une fiabilité de 100%.
Comment ? Eh bien c'est très simple : il suffit d'envoyer deux commandes. La première est la commande que l'on veut véritablement éxécuter (donc où la réponse peut être consituée d'un nombre de morceaux variable). Peu importe en revanche du nom de la seconde commande, il faut juste que vous soyiez certain qu'elle ne renvoie qu'un seul morceau de données (par exemple, en voici une : echo EOT). Vous devez envoyer ces deux commandes l'une après l'autre, avant de commencer à recevoir les réponses.
Dans la réponse, le serveur va renvoyer la réponse à la première commande (multimorceaux ou pas), et la réponse à la deuxième... en un seul morceau. Du coup, la deuxième réponse constitue un EOT utilisable pour déterminer si la transmission est terminée ou pas. Concrètement, vous devez arrêter d'écouter dès que vous recevez le paquet de réponse à la commande echo EOT (que vous aurez repéré grâce au numéro de transaction), ce qui signifiera que la réponse à la première commande est terminée.
Cette astuce est facilement transposable sur HL1, mais attention, avec une fiabilité moindre, en raison du fait que les paquets ne sont pas réordonnancés en UDP.
Un serveur de jeu HL / Source permet de stocker des logs dans des fichiers, via la commande log. Une autre commande, nettement moins connue, permet de diriger ce log non pas vers des fichiers, mais vers une adresse IP distante.
Lorsque cette fonction est activée, le serveur enverra, chaque fois qu'un évenement se produit, les informations sur cet évenement à l'adresse IP spécifiée. La CVAR serveur mp_logdetail permet, comme pour les logs classiques, de régler le niveau de log. A son niveau maximal (3), vous pouvez recevoir jusqu'aux dommages infligés entre joueurs en temps réel !
L'activation peut se faire soit par rCON, soit directement sur la console du serveur.
NOTE : les logs doivent préalablement être activés par la commande log on.
Vous devez utiliser la commande logadress pour activer l'envoi des logs. Exemple :
logaddress 84.100.34.113 15458
Cette commande enverra les évenements sur la machine 84.100.34.113, port 15458.
Pour arrêter la transmission, éxécutez cette commande :
logaddress 0.0.0.0 1
Source dispose d'un système de log distant un poil plus évolué qui permet d'entrer plusieurs destinations. Voici comment activer l'envoi de log sur votre machine :
logaddress_add 84.100.34.113:15458
Et pour arrêter :
logaddress_del 84.100.34.113:15458
Vous avez égalemement à votre disposition les commandes logaddress_delall, pour arrêter tout envoi de log, et logaddress_list, pour lister toutes les destinations de log distant.
Abus de langage : on ne peut pas parler d'échanges ici, vu que la communication ne se fait que dans le sens serveur > client...
Afin de recevoir les événements, vous devez faire écouter votre programme sur le port que vous avez donné dans le logaddress, en UDP.
Ensuite, vous recevrez un évenement par paquet, comme suit :
ff ff ff ff 6c 6f 67 20255 255 255 255 108 111 103 032....log4c 20 30 38 2f 30 34 2f076 032 048 056 047 048 052 047L 08/04/32 30 30 35 20 2d 20 31050 048 048 053 032 045 032 0492005 - 135 3a 30 38 3a 34 33 3a053 058 048 056 058 052 051 0585:08:43:20 22 2a 73 65 76 65 73032 034 042 115 101 118 101 115"*seves20 3a 3a 20 50 69 72 6f032 058 058 032 080 105 114 111:: Piro75 65 74 74 65 20 3c 20117 101 116 116 101 032 060 032uette <53 69 52 2a 44 45 4b 20083 105 082 042 068 069 075 032SiR*DEK3e 3c 32 32 3e 3c 53 54062 060 050 050 062 060 083 084><22><ST45 41 4d 5f 30 3a 30 3a069 065 077 095 048 058 048 058EAM_0:0:33 32 34 37 34 34 3e 3c051 050 052 055 052 052 062 060324744><43 54 3e 22 20 73 61 79067 084 062 034 032 115 097 121CT>" say5f 74 65 61 6d 20 22 6c095 116 101 097 109 032 034 108_team "l6f 6c 22 20 28 64 65 61111 108 034 032 040 100 101 097ol" (dea64 29 0a 00100 041 010 000d)..
FF ou 255.string contenant :
log L pour les serveurs HL1, ou RL pour les serveurs Source.08/04/2005 - 15:08:43.: ."*seves :: Pirouette < SiR*DEK ><22><STEAM_0:0:324744><CT>" say_team "lol" (dead) (signifiant : le joueur "*seves :: Pirouette < SiR*DEK >", ID 22, de SteamID STEAM_0:0:324744, équipe contre-terroriste, a éxécuté la commande say_team "lol", en spectateur)MSN : eti172@msn.com
e-mail : e-t172 at e-t172 dot net