Page 1 sur 1

Problème de Socket: Visualisation du datagram en TCP

Posté : 23 juin 2009, 16:55
par virzon
Bonjour à tous,

Je sèche sur un problème quelque peu particulier et je cherche un petit coup de pouce.

Ce que je cherche à faire est une sorte de serveur en un script Php qui écoute sur un port et répond en fonction des data reçues. Mais il doit lire le datagramme brut.

Exemple:
1° Le device distant envoie en TCP sur le port 2222 le datagramme suivant (tel que vu par Wireshark par exemple): 000f333533393736303133373134363734

2° Le serveur répond: 0x01 (en hexa)

Je sais que au point 1° le 000f est la longueur des infos envoyées en HEXA et le reste les octets en hexa qui contiennent des données. La longueur est variable.

Comment à l'aide des fonction socket, obtenir la trame IP elle-même et non pas sont interpretation???
En effet, le code suivant me donne pour la séquence du point 1 un résultat qui ne me convient pas (353976013714674) car les octets doivent être interpretés parfois ensemble et parfois séparément:

Code : Tout sélectionner

// On crée la socket $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); // On le lie, ici sur le port 2222 socket_bind($socket, '0.0.0.0', 2222); // On la fait écouter socket_listen($socket, 5); // On attend qu'un client se connecte $client = socket_accept($socket); // On lit un paquet de réponse du client $response = socket_read($client, 17, PHP_BINARY_READ); echo"recu=".$response."\n"; //353976013714674
J'avais pensé à utiliser comme paramètre dans socket_create:SOCK_DGRAM mais celui-ci n'est compatible qu'avec UDP!!!

Je ne vois pas comment obtenir la trame TCP en direct et ensuite lui répondre en fonction des données reçues en Hexa.

Une idée, ou une piste serait vraiment bienvenu.

Nico

Posté : 23 juin 2009, 17:32
par Calimero
Je pense que tu as besoin d'utiliser SOCK_RAW pour voir les trames.

PS: ça va servir à quoi ton truc ?

Posté : 23 juin 2009, 17:58
par virzon
Je pense que tu as besoin d'utiliser SOCK_RAW pour voir les trames.

PS: ça va servir à quoi ton truc ?
Hello,

si j'utilise SOCK_RAW, outre le fait que c'est l'utilisateur root qui doit exécuter le script, socket_listen($socket, 5); ne fonctionne plus!
Il me donne: Warning: socket_listen(): unable to listen on socket [95]: Operation not supported

Quand à savoir à quoi cela va servir, c'est afin de récupérer les données envoyées par un collecteur de données. Il envoie d'abord son numéro de série, et ensuite envoie les données collectées.

Je continue à chercher

Nico

[Note : ce message a été posté de manière anonyme avant d'être réattribué à son auteur]

Posté : 23 juin 2009, 19:05
par Calimero
Merci pour ta réponse.

Je viens de voir un truc en te relisant :

Tu décris le paquet de départ envoyé par l'objet en expliquant que celui-ci attend ensuite une réponse (0x01). Hors tu écris ceci :
   // On lit un paquet de réponse du client
   $response = socket_read($client, 17, PHP_BINARY_READ);
Ne serait-ce pas 15 octets que tu veux lire plutôt que 17 ? Il ne faut te préoccupper que du contenu utile que tu veux lire, la structure des paquets n'est pas sous ta responsabilité mais sous celle de la pile TCP de ton OS.

Je pense qu'il y a un gros souci au niveau de l'utilisation de socket_listen(), il ne faut pas le faire suivre immédiatement d'un socket_accept mais attendre qu'une connexion soit recue à l'aide de socket_select() et d'une boucle d'attente.

Enfin, toujours en te relisant, je ne suis finalement pas convaincu que tu aies besoin de lire les trames (ce qui serait illogique puisque le protocole TCP est justement là pour t'éviter de voir les protocoles de couches inférieures).

Hope that helps.

Posté : 23 juin 2009, 22:22
par virzon
Merci pour ta réponse.

Enfin, toujours en te relisant, je ne suis finalement pas convaincu que tu aies besoin de lire les trames (ce qui serait illogique puisque le protocole TCP est justement là pour t'éviter de voir les protocoles de couches inférieures).

Hope that helps.
Je suis assez d'accord en ce qui concerne le data lenght et le checksum, mais le content lui est pourtant interpreté par php.

Pour info je rame là-dessus depuis 2 semaines. J'ai même pensé laissé tourner un serveur basé sur Netcat, mais c'était la porte ouverte aux ennuis.

Anyone else?

Nico

[Note : ce message a été posté de manière anonyme avant d'être réattribué à son auteur]

Posté : 23 juin 2009, 22:52
par Calimero
Je suis assez d'accord en ce qui concerne le data lenght et le checksum, mais le content lui est pourtant interpreté par php.
Soit dit en passant php n'interprète rien, il pense juste avoir affaire à du texte et te l'affiche comme tel. Si tu prends ce bloc de données hexadécimales :

Code : Tout sélectionner

333533393736303133373134363734
groupe les par octets...

Code : Tout sélectionner

33 35 33 39 37 36 30 31 33 37 31 34 36 37 34
et convertis chaque octet par le code ascii du caractère équivalent (little endian), et ça donne :

Code : Tout sélectionner

3 5 3 9 7 6 0 1 3 7 1 4 6 7 4
Soit ce que tu trouves. Regarde ici ( http://fr3.php.net/ord ) pour faire la conversion en sens inverse (dans un but d'affichage uniquement ! car les données sont là et intactes, c'est juste leur représentation sur laquelle tu sembles buter ), caractère par caractère.

Posté : 24 juin 2009, 01:26
par Sékiltoyai
Je suis choqué par ce que je vois. Sniffer et parser les paquets IP pour les décoder toi même c'est juste contraire à tout les principes du développement réseau.
Déjà un paquet IP tu ne sauras pas le parser à tous les coups. Si tu tombes sur des entêtes inconnus, ou bien si tu as d'autres protocoles applicatifs qui envoient des données, il faut savoir les ignorer.
Ensuite obviously il faut être root pour sniffer des paquets. Tu ne t'attends pas quand même à ce qu'on laisse n'importe qui voir tout le réseau qui transite par ta machine ?

Je veux bien que tu m'expliques en quoi les fonctions du réseau ne te suffisent pas. J'ai bien l'impression que c'est toi qui utilises mal les fonctions standards, plutôt qu'elles qui seraient mal faites (Elles sont utilisées depuis des dizaines d'année par des millions de développeurs…).

Posté : 24 juin 2009, 11:46
par virzon
Je suis choqué par ce que je vois. Sniffer et parser les paquets IP pour les décoder toi même c'est juste contraire à tout les principes du développement réseau.
Sorti de son contexte, je comprend ta réaction, mais je vais donc être un peu plus explicite dans ce que je fais, et pourquoi!

Le device que j'ai, collecte des données (vitesses de rotation, angle, température,...) et il les envoie via un modem gsm intégré en GPRS. Ces données sont codées/compressées.
La société qui vend cet appareil de mesure, vend un software serveur multi-utilisateurs à un prix prohibitif, mais donne, à qui le veut, le format de codage des données.

Non décidé à acheter ce soft, et vu que les données doivent être intégrées dans un db sql, et affichée en temps réel sur une page web dédiée, on a décidé de parser les données nous mêmes.

DONC:
Les données arrivent au format hexadécimal, et chaque octet ou série d'octet d'octet a une valeur, ou contient deux valeurs distinctes.
Une exemple simple pour illustrer ce que je mentionne ici, considérons un octet équivalent à 0xA1 (hexa) il equivaut à 10100001, ce qui décodé par le parser, lui signale que le 1 bit =1 et que donc il a des données de température, les 4 bits suivant sont à 0, donc il n'y a pas de données spécifiques pour les valeurs d'humidité,... Le 6ème bit étant à 1, le device a des données de températures relatives dans les octets qui suivent. Le 7ème et 8ème bit, indique la sévérité, soit 10 (mais qui peut être aussi 11, 01, 00).
Voilà déjà un tas d'informations sur un seul octet.

Je suis bien conscient que php, n'est pas l'outil idéal pour notre utilisation, mais n'ayant que cette connaissance (pas Java, Perl ou autre), on fait avec les moyens du bord.

J'ai trouvé entre temps sur un forum une piste qui m'indique que ce que je désire faire semble impossible, ou alors je n'aurais pas compris :oops:
http://www.codingforums.com/archive/ind ... 79166.html

J'espère avoir ici éclairer quelques lanternes!

Nico

Posté : 26 juin 2009, 02:55
par Sékiltoyai
Tu mélanges ici allègrement deux problèmes. Et du coup ça devient difficile de savoir si même pour toi c'est clair. Il y a le problème de la récupération des données en hexadécimal d'une part, et le problème de la taille variable de l'autre.

Pour le problème de l'hexa, il y a des fonctions qui existent déjà pour transformer une chaine binaire en hexa. Par exemple bin2hex. Mais il y en a d'autres, tu chercheras. La preuve que cela marche :

Client :
<?php

	if(!($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)))
	{
		echo "Erreur socket_create\n";
	}
	
	if(!socket_connect($sock, "localhost", 4))
	{
		echo "Erreur socket_connect\n";
	}
	
	$msg = NULL;
	if(($size = socket_recv($sock, $msg, 4, 0)) <= -1)
	{
		echo "Erreur socket_read\n";
	}
	
	socket_close($sock);
	
	echo "Size : " . $size . "\nData : " . bin2hex($msg) . "\n";
	

?>
Serveur :

Code : Tout sélectionner

#include <sys/errno.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> int main(int argc, char *argv[]) { int sock, client; struct sockaddr_in addr = {sizeof(struct sockaddr_in), AF_INET, htons(4), INADDR_ANY, {0,0,0,0,0,0,0,0}}; if((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { printf("Erreur socket : %d\n", errno); return -2; } if(bind(sock, (struct sockaddr*) &addr, sizeof(struct sockaddr_in))) { printf("Erreur bind : %d\n", errno); return -3; } if(listen(sock,4)) { printf("Erreur listen : %d\n", errno); return -4; } struct sockaddr cl_addr; socklen_t cl_len; while((client = accept(sock, &cl_addr, &cl_len)) != -1) { printf(".\n"); char buffer[4] = {0xff, 0x20, 0x84, 0xac}; send(client, buffer, sizeof(char[4]), 0); close(client); } printf("Erreur accept : %d\n", errno); }
Il affiche :
Size : 4
Data : ff2084ac
Ce qui est strictement correct, et parfaitement exploitable moyennant quelques modifications supplémentaires. Tu parlais d'octets contenant plusieurs bits d'informations, tu dois aussi pouvoir travailler directement sur chacun des octets en utilisant ta chaine comme un tableau, et en utilisant les opérateurs de décalage.



Et il y a le second problème, celui de la taille. Est ce que tu peux décrire exactement le protocole entre le moment où le matériel ouvre la connexion TCP et le moment où il la ferme (Tu le fais sur une seule connexion, c'est le plus important). Par exemple tu disais qu'il envoyait son numéro de série, quand est-ce qu'il l'envoie ? Va-t-il envoyer plusieurs paquets sur une même connexion Plusieurs types de paquets ?



Sinon, je te rassure, ce que tu cherchais à faire (sniffer les paquets), ce n'est pas impossible, mais disons … hautement foireux pour être exact. C'est techniquement possible mais c'est vraiment une hérésie que de le faire (je pourrais sortir des dizaines et des dizaines d'arguments dans ce sens mais à vrai dire je ne juge pas cela utile…
Sinon, en effet, tu devrais faire le traitement en C (et ensuite éventuellement en effet le passer à un langage de script pour la mise à jour des données), c'est vraiment le langage le plus adapté. Un informaticien doit savoir évoluer. Si pour ce genre de projets là, tu le fais en PHP, il n'y aura aucun projet pour te faire évoluer…

Posté : 26 juin 2009, 11:45
par Hywan
Hey :-),

Je suis le sujet depuis le début et la remarque de Sékil' est tout à fait fondée. À savoir :
Sinon, je te rassure, ce que tu cherchais à faire (sniffer les paquets), ce n'est pas impossible, mais disons … hautement foireux pour être exact. C'est techniquement possible mais c'est vraiment une hérésie que de le faire (je pourrais sortir des dizaines et des dizaines d'arguments dans ce sens mais à vrai dire je ne juge pas cela utile…
Sinon, en effet, tu devrais faire le traitement en C (et ensuite éventuellement en effet le passer à un langage de script pour la mise à jour des données), c'est vraiment le langage le plus adapté. Un informaticien doit savoir évoluer. Si pour ce genre de projets là, tu le fais en PHP, il n'y aura aucun projet pour te faire évoluer…
Je suis tout à fait d'accord avec ça.
Ce n'est pas impossible (car rien n'est imposible) mais c'est très bancale et ça risque de se péter la tronche sans prévenir.

Posté : 26 juin 2009, 19:38
par Sékiltoyai
Disons que quand tu sniffes les paquets tu bypasses toute la couche réseau du kernel… Donc au final tu dois réimplémenter ça (liste non exhaustive) :
http://www.rfc-editor.org/rfc/rfc791.txt
http://www.rfc-editor.org/rfc/rfc792.txt
http://www.rfc-editor.org/rfc/rfc793.txt
http://www.rfc-editor.org/rfc/rfc1122.txt
http://www.rfc-editor.org/rfc/rfc768.txt
C'est un peu ballo quand même…

NB : Et l'IPv6 il y en a 3 fois plus…

Posté : 29 juin 2009, 12:30
par virzon
Hello,

Avec beaucoup d'acharnement j'ai trouvé ce qui m'induisait en erreur.

Les données sont envoyées en hexadécimal, avec un lenght et un CRC mais comme data, et donc il n'est nul besoin de retourner dans la trame tcp basique, seul le segment data était à reprendre.

Pour la conversion j'ai utiliser une conversion de ascii to hex pour obtenir les valeur de ma trame.

Merci pour les lumières

Posté : 29 juin 2009, 19:48
par Sékiltoyai
Hello,

Avec beaucoup d'acharnement j'ai trouvé ce qui m'induisait en erreur.

Les données sont envoyées en hexadécimal, avec un lenght et un CRC mais comme data, et donc il n'est nul besoin de retourner dans la trame tcp basique, seul le segment data était à reprendre.

Pour la conversion j'ai utiliser une conversion de ascii to hex pour obtenir les valeur de ma trame.

Merci pour les lumières
C'est complètement con de foutre un CRC et un length au niveau applicatif, c'est déjà fait au niveau transport… T'es tombé sur un protocole en carton là…