{"id":59,"date":"2009-05-16T19:51:06","date_gmt":"2009-05-16T17:51:06","guid":{"rendered":"http:\/\/46.105.150.90\/tstnunix\/?p=59"},"modified":"2016-08-21T19:38:14","modified_gmt":"2016-08-21T17:38:14","slug":"gts","status":"publish","type":"post","link":"https:\/\/nunix.fr\/?p=59","title":{"rendered":"GTS"},"content":{"rendered":"<h1>GTS : Generic Tcp Server<\/h1>\n<p>Nous allons r\u00e9aliser en C un serveur tcp\/ip multi-clients g\u00e9n\u00e9rique&#8230;<\/p>\n<p>oul\u00e0&#8230; doucement :<\/p>\n<p>-serveur : va attendre que des clients se connectent<\/p>\n<p>-tcp\/ip : les connections clientes se feront par le protocole tcp\/ip<\/p>\n<p>-multi-clients : le serveur peut g\u00e9rer plusieurs clients en m\u00eame temps<\/p>\n<p>-g\u00e9n\u00e9rique \ud83d\ude2e : le serveur s&rsquo;occupe uniquement de l&rsquo;aspect communications r\u00e9seaux. Lorsqu&rsquo;un client se connecte, il ex\u00e9cute une commande shell de notre choix.<\/p>\n<p>  <!--more-->  <\/p>\n<p>\u00a0<\/p>\n<p>On va voir quelques exemples pour bien comprendre :<\/p>\n<h3>exemple 1 :<\/h3>\n<pre>09:57:33 apesle:~ $ gts \"echo $SHELL\" 1234 &amp;<br \/>[1] 28862<br \/><\/pre>\n<p>Je lance notre programme gts en tache de fond. Je lui demande d&rsquo;attendre des clients sur le port 1234 et, pour chaque client, d&rsquo;ex\u00e9cuter la commande \u00ab\u00a0echo $SHELL\u00a0\u00bb.<\/p>\n<pre>09:57:50 apesle:~ $ nc localhost 1234<br \/>\/bin\/bash<br \/>09:58:03 apesle:~ $ nc localhost 1234<br \/>\/bin\/bash<br \/><\/pre>\n<p>Avec <a href=\"http:\/\/46.105.150.90\/tstnunix\/?p=37\">NetCat<\/a>, je cr\u00e9\u00e9 deux clients sur le port 1234 qui re\u00e7oivent bien le nom du shell utilis\u00e9.<\/p>\n<p>\u00a0<\/p>\n<h3>exemple 2 :<\/h3>\n<pre>09:58:03 apesle:~ $ gts \"echo $PWD\" 1235 &amp;<br \/>[2] 28872<br \/><\/pre>\n<p>Je lance un nouveau processus gts en tache de fond. Cette fois ci je lui demande d&rsquo;attendre des clients sur le port 1235 et, pour chaque client, d&rsquo;ex\u00e9cuter la commande \u00ab\u00a0echo $PWD\u00a0\u00bb.<\/p>\n<pre>09:58:16 apesle:~ $ nc localhost 1235<br \/>\/home\/apesle<br \/><\/pre>\n<p>On peut voir que le client re\u00e7oit bien le r\u00e9pertoire courant du shell.<\/p>\n<p>\u00a0<\/p>\n<h3>exemple 3 :<\/h3>\n<pre>10:08:56 apesle:~ $ gts \"cd ~\/Desktop\/tests &amp;&amp; .\/anagramme\" 1236 &amp;<br \/><\/pre>\n<p>Je cr\u00e9\u00e9 un serveur sur le port 1236 qui lance notre programme C d&rsquo;anagrammes pour chaque client.<\/p>\n<pre>10:09:26 apesle:~ $ nc localhost 1236<br \/>nuuuunnnix<br \/>uuxunnunin<br \/>generic tcp server <br \/>grrcrs ee cievtpen<br \/>^C<br \/><\/pre>\n<p>Le client acc\u00e8de bien au programme d&rsquo;anagramme \u00e0 travers la connection tcp\/ip.<\/p>\n<p>\u00a0<\/p>\n<h3>exemple 4 :<\/h3>\n<pre>09:58:20 apesle:~ $ gts \"nc localhost 1234\" 1237 &amp;<br \/>[3] 28885<br \/><\/pre>\n<p>Cette fois ci c&rsquo;est plus compliqu\u00e9 : je demande au serveur de s&rsquo;\u00e9tablir sur le port 1237, et lorsqu&rsquo;il re\u00e7oit un client d&rsquo;ex\u00e9cuter la commande \u00ab\u00a0nc localhost 1234\u00a0\u00bb qui cr\u00e9\u00e9 en fait un client pour notre premier serveur.<\/p>\n<pre>09:59:06 apesle:~ $ nc localhost 1237<br \/>\/bin\/bash<br \/><\/pre>\n<p>Le client re\u00e7oit donc le nom du shell utilis\u00e9 ! En effet, on s&rsquo;est connect\u00e9 au serveur, le serveur a donc ex\u00e9cut\u00e9 la commande \u00ab\u00a0nc localhost 1234\u00a0\u00bb qui cr\u00e9\u00e9 un client pour le serveur de l&rsquo;exemple 1. Le serveur de l&rsquo;exemple 1 a donc ex\u00e9cut\u00e9 la commande \u00ab\u00a0echo $SHELL\u00a0\u00bb et envois le r\u00e9sultat \u00e0 notre serveur, qui l&rsquo;envois \u00e0 notre client !<\/p>\n<p>\u00a0<\/p>\n<p>Avec la commande \u00ab\u00a0netstat -tln\u00a0\u00bb, on peut voir nos 4 serveurs en attente de connections tcp\/ip :<\/p>\n<pre>tcp        0      0 0.0.0.0:1234            0.0.0.0:*               LISTEN     <br \/>tcp        0      0 0.0.0.0:1235            0.0.0.0:*               LISTEN     <br \/>tcp        0      0 0.0.0.0:1236            0.0.0.0:*               LISTEN     <br \/>tcp        0      0 0.0.0.0:1237            0.0.0.0:*               LISTEN <br \/><\/pre>\n<p>\u00a0<\/p>\n<h3>Bon, mais comment \u00e7a marche ?<\/h3>\n<p>Le principe de fonctionnement est le suivant :<\/p>\n<ul>\n<li>le programme cr\u00e9\u00e9 une socket serveur, puis dans une boucle infinie accepte les clients qui s&rsquo;y    connectent. <\/li>\n<\/ul>\n<ul>\n<li>\u00c0 chaque fois qu&rsquo;un client se connecte, on cr\u00e9\u00e9 un nouveau processus avec un \u00ab\u00a0fork\u00a0\u00bb. C&rsquo;est ce processus fils qui va traiter la communication, alors que le p\u00e8re retourne en attente d&rsquo;un client.<\/li>\n<\/ul>\n<ul>\n<li>On a donc un processus fils par client. Le processus fils redirige ses entr\u00e9es\/sorties standards sur la socket ouverte par son p\u00e8re puis ex\u00e9cute un programme tierce gr\u00e2ce \u00e0 un \u00ab\u00a0exec\u00a0\u00bb.<\/li>\n<\/ul>\n<p>Le r\u00f4le du serveur est donc uniquement de g\u00e9rer les sockets. Il d\u00e9l\u00e8gue la communication avec les clients \u00e0 un programme tierce qui manipule uniquement stdin et stdout.<\/p>\n<p>Ce programme peut, par ailleurs, \u00eatre \u00e9crit en n&rsquo;importe quel langage (c,c++, script shell, etc).<\/p>\n<p>\u00a0<\/p>\n<h3>Ok, et le source alors ?<\/h3>\n<p>Le programme, bien qu&rsquo;assez succint, est int\u00e9ressant.<\/p>\n<p>Il montre l&rsquo;utilisation des sockets en mode tcp\/ip (notamment l&rsquo;option SO_REUSEADDR qui permet d&rsquo;\u00e9viter l&rsquo;erreur \u00ab\u00a0bind: Address already in use\u00a0\u00bb), la cr\u00e9ation de processus avec fork, la redirection de flux standard avec dup, ainsi que la mise en place d&rsquo;un gestionnaire de signaux.<\/p>\n<p>Le code source est comment\u00e9, cependant quelques explications au niveau de la gestion des signaux n&rsquo;est peut \u00eatre pas de trop :<\/p>\n<p>Le programme met en place un gestionnaire de signaux pour SIGCHLD (signal \u00e9mis par le noyau lors de la terminaison d&rsquo;un processus fils).<\/p>\n<p>Ce signal, re\u00e7ut par le processus gts, interrompt l&rsquo;appel syst\u00e8me accept (qui permet d&rsquo;accepter des connections clientes sur la socket). On utilise donc errno pour d\u00e9tecter ce cas et r\u00e9-appeler accept.<\/p>\n<p>Notez \u00e9galement que le gestionnaire de signaux mis en place pour SIGCHLD lit le code de retour du processus fils termin\u00e9 pour \u00e9viter qu&rsquo;il ne reste \u00e0 l&rsquo;\u00e9tat zombie (\u00e9tat sp\u00e9cial d&rsquo;un processus qui attend ind\u00e9finiment que son p\u00e8re lise son code de retour).<\/p>\n<p>\u00a0<\/p>\n<p>PS :<\/p>\n<p>-Pour compiler : gcc -Wall -g -o gts gts.c<\/p>\n<p>-Installer : sudo cp gts \/usr\/bin\/<\/p>\n<p>{codecitation class=\u00a0\u00bbbrush: c;\u00a0\u00bb}<\/p>\n<pre>\/* <br \/>Copyright (c) 2009 Adrien Pesle - apesle at nunix.fr<br \/><br \/>Permission is hereby granted, free of charge, to any person obtaining a copy<br \/>of this software and associated documentation files (the \"Software\"), to deal<br \/>in the Software without restriction, including without limitation the rights<br \/>to use, copy, modify, merge, publish, distribute, sublicense, and\/or sell<br \/>copies of the Software, and to permit persons to whom the Software is<br \/>furnished to do so, subject to the following conditions:<br \/><br \/>The above copyright notice and this permission notice shall be included in<br \/>all copies or substantial portions of the Software.<br \/><br \/>THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR<br \/>IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,<br \/>FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE<br \/>AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER<br \/>LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,<br \/>OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN<br \/>THE SOFTWARE.<br \/>*\/<\/pre>\n<p>#include &lt;stdio.h&gt;<\/p>\n<p>#include &lt;stdlib.h&gt;<\/p>\n<p>#include &lt;string.h&gt;<\/p>\n<p>#include &lt;unistd.h&gt;<\/p>\n<p>#include &lt;errno.h&gt;<\/p>\n<p>#include &lt;signal.h&gt;<\/p>\n<p>#include &lt;sys\/wait.h&gt;<\/p>\n<p>#include &lt;arpa\/inet.h&gt;<\/p>\n<p>#include &lt;netdb.h&gt;<\/p>\n<p>#include &lt;netinet\/in.h&gt;<\/p>\n<p>#include &lt;sys\/types.h&gt;<\/p>\n<p>#include &lt;sys\/socket.h&gt;<\/p>\n<p>extern char **environ;<\/p>\n<p>int<\/p>\n<p>cree_socket_stream (const unsigned int port)<\/p>\n<p>{<\/p>\n<p>int sock;<\/p>\n<p>struct sockaddr_in adresse;<\/p>\n<p>memset (&amp;adresse, 0, sizeof (struct sockaddr_in));<\/p>\n<p>\/* cr\u00e9ation de la socket *\/<\/p>\n<p>if ((sock = socket (AF_INET, SOCK_STREAM, 0)) &lt; 0)<\/p>\n<p>{<\/p>\n<p>perror (\u00ab\u00a0socket\u00a0\u00bb);<\/p>\n<p>return -1;<\/p>\n<p>}<\/p>\n<p>\/* appliquer l&rsquo;option SO_REUSEADDR qui dit au kernel de r\u00e9utiliser<\/p>\n<p>le port m\u00eame si il est occup\u00e9 (\u00e9tat TIME_WAIT). Ceci permet<\/p>\n<p>d&rsquo;\u00e9viter l&rsquo;erreure \u00ab\u00a0bind: Address already in use\u00a0\u00bb *\/<\/p>\n<p>int yes = 1;<\/p>\n<p>if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &amp;yes, sizeof (int)) == -1)<\/p>\n<p>{<\/p>\n<p>perror (\u00ab\u00a0Impossible d&rsquo;appliquer setsockopt avec SO_REUSEADDR !\u00a0\u00bb);<\/p>\n<p>return -1;<\/p>\n<p>}<\/p>\n<p>\/* param\u00e9trage *\/<\/p>\n<p>adresse.sin_family = AF_INET;<\/p>\n<p>adresse.sin_port = htons (port);<\/p>\n<p>adresse.sin_addr.s_addr = htonl (INADDR_ANY);<\/p>\n<p>if (bind<\/p>\n<p>(sock, (struct sockaddr *) &amp;adresse, sizeof (struct sockaddr_in)) &lt; 0)<\/p>\n<p>{<\/p>\n<p>close (sock);<\/p>\n<p>perror (\u00ab\u00a0bind\u00a0\u00bb);<\/p>\n<p>return -1;<\/p>\n<p>}<\/p>\n<p>return sock;<\/p>\n<p>}<\/p>\n<p>void<\/p>\n<p>traite_connexion (int sock, const char *commande)<\/p>\n<p>{<\/p>\n<p>\/* redirection de stdout et stdin sur la socket *\/<\/p>\n<p>close (STDOUT_FILENO);<\/p>\n<p>if (dup (sock) &lt; 0)<\/p>\n<p>{<\/p>\n<p>perror (\u00ab\u00a0dup\u00a0\u00bb);<\/p>\n<p>exit (EXIT_FAILURE);<\/p>\n<p>}<\/p>\n<p>close (STDIN_FILENO);<\/p>\n<p>if (dup (sock) &lt; 0)<\/p>\n<p>{<\/p>\n<p>perror (\u00ab\u00a0dup\u00a0\u00bb);<\/p>\n<p>exit (EXIT_FAILURE);<\/p>\n<p>}<\/p>\n<p>\/* execution de la commande pass\u00e9e en param\u00e8tre du serveur *\/<\/p>\n<p>char *argv[] = { \u00ab\u00a0sh\u00a0\u00bb, \u00ab\u00a0-c\u00a0\u00bb, (char *) commande, (char *) NULL };<\/p>\n<p>execve (\u00ab\u00a0\/bin\/sh\u00a0\u00bb, argv, environ);<\/p>\n<p>fprintf (stdout, \u00ab\u00a0Rat\u00e9 : erreur = %dn\u00a0\u00bb, errno);<\/p>\n<p>close (sock);<\/p>\n<p>}<\/p>\n<p>int<\/p>\n<p>serveur_tcp (const char *commande, const unsigned int port)<\/p>\n<p>{<\/p>\n<p>int sock_contact;<\/p>\n<p>int sock_connectee;<\/p>\n<p>struct sockaddr_in adresse;<\/p>\n<p>socklen_t longueur;<\/p>\n<p> sock_contact = cree_socket_stream (port);<\/p>\n<p>if (sock_contact &lt; 0)<\/p>\n<p>return -1;<\/p>\n<p>listen (sock_contact, 5);<\/p>\n<p>while (1)<\/p>\n<p>{<\/p>\n<p>longueur = sizeof (struct sockaddr_in);<\/p>\n<p>sock_connectee =<\/p>\n<p>accept (sock_contact, (struct sockaddr *) &amp;adresse, &amp;longueur);<\/p>\n<p>if (sock_connectee == -1)<\/p>\n<p>{<\/p>\n<p>if (EINTR == errno)<\/p>\n<p>\/* l&rsquo;appel system a \u00e9t\u00e9 interrompu \u00e0 cause du signal SIGCHLD<\/p>\n<p>provoqu\u00e9 par la d\u00e9connection d&rsquo;un client. *\/<\/p>\n<p>continue;<\/p>\n<p>else<\/p>\n<p>{<\/p>\n<p>\/* l&rsquo;appel system accept a \u00e9chou\u00e9 *\/<\/p>\n<p>perror (\u00ab\u00a0accept\u00a0\u00bb);<\/p>\n<p>return -1;<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>\/* cr\u00e9ation d&rsquo;un processus fils pour chaque client *\/<\/p>\n<p>switch (fork ())<\/p>\n<p>{<\/p>\n<p>case 0:\t\t\/* fils *\/<\/p>\n<p>close (sock_contact);<\/p>\n<p>traite_connexion (sock_connectee, commande);<\/p>\n<p>exit (EXIT_SUCCESS);<\/p>\n<p>case -1:\t\t\/* erreure *\/<\/p>\n<p>perror (\u00ab\u00a0fork\u00a0\u00bb);<\/p>\n<p>return -1;<\/p>\n<p>default:\t\t\/* p\u00e8re *\/<\/p>\n<p>close (sock_connectee);<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>return 0;<\/p>\n<p>}<\/p>\n<p>static void<\/p>\n<p>gestionnaire_signaux (int ignore)<\/p>\n<p>{<\/p>\n<p>\/* lecture du code de retour du fils termin\u00e9 pour eviter<\/p>\n<p>qu&rsquo;il ne reste zombi. On passe NULL car on n&rsquo;est pas<\/p>\n<p>int\u00e9rr\u00e9ss\u00e9 par les circonstances de la fin du processus. *\/<\/p>\n<p>wait (NULL);<\/p>\n<p>}<\/p>\n<p>int<\/p>\n<p>main (int argc, char *argv[])<\/p>\n<p>{<\/p>\n<p>if (argc &lt; 3)<\/p>\n<p>{<\/p>\n<p>printf (\u00ab\u00a0Usage : %s commande portn\u00a0\u00bb, argv[0]);<\/p>\n<p>printf<\/p>\n<p>(\u00ab\u00a0comande = commande ex\u00e9cut\u00e9e par le shell pour chaque client.n\u00a0\u00bb);<\/p>\n<p>printf (\u00ab\u00a0port    = port d&rsquo;\u00e9coute.n\u00a0\u00bb);<\/p>\n<p>exit (EXIT_FAILURE);<\/p>\n<p>}<\/p>\n<p>\/*&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<\/p>\n<p>*  gestionnaire de signaux qui capture SIGGHLD pour \u00e9viter que les<\/p>\n<p>*  processus fils deviennent zombi.<\/p>\n<p>*&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;*\/<\/p>\n<p>struct sigaction action;<\/p>\n<p>action.sa_handler = gestionnaire_signaux;<\/p>\n<p>sigemptyset (&amp;(action.sa_mask));<\/p>\n<p>action.sa_flags = 0;<\/p>\n<p>if (sigaction (SIGCHLD, &amp;action, NULL) != 0)<\/p>\n<p>{<\/p>\n<p>fprintf (stderr, \u00ab\u00a0Signal non captur\u00e9.n\u00a0\u00bb);<\/p>\n<p>exit (EXIT_FAILURE);<\/p>\n<p>}<\/p>\n<p>return serveur_tcp (argv[1], atoi (argv[2]));<\/p>\n<p>}<\/p>\n<p>{\/codecitation}<\/p>\n<p>{jcomments on}<\/p>\n<p>\u00a0<\/p>\n<p>\u00a0<\/p>\n<p>\u00a0<\/p>\n<p>\u00a0<\/p>\n","protected":false},"excerpt":{"rendered":"<p>GTS : Generic Tcp Server Nous allons r\u00e9aliser en C un serveur tcp\/ip multi-clients g\u00e9n\u00e9rique&#8230; oul\u00e0&#8230; doucement : -serveur : va attendre que des clients se connectent -tcp\/ip : les connections clientes se feront par le protocole tcp\/ip -multi-clients : le serveur peut g\u00e9rer plusieurs clients en m\u00eame temps -g\u00e9n\u00e9rique \ud83d\ude2e : le serveur s&rsquo;occupe &hellip; <a href=\"https:\/\/nunix.fr\/?p=59\" class=\"more-link\">Continuer la lecture<span class=\"screen-reader-text\"> de &laquo;&nbsp;GTS&nbsp;&raquo;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[9],"tags":[],"class_list":["post-59","post","type-post","status-publish","format-standard","hentry","category-c"],"_links":{"self":[{"href":"https:\/\/nunix.fr\/index.php?rest_route=\/wp\/v2\/posts\/59","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nunix.fr\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nunix.fr\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nunix.fr\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/nunix.fr\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=59"}],"version-history":[{"count":1,"href":"https:\/\/nunix.fr\/index.php?rest_route=\/wp\/v2\/posts\/59\/revisions"}],"predecessor-version":[{"id":121,"href":"https:\/\/nunix.fr\/index.php?rest_route=\/wp\/v2\/posts\/59\/revisions\/121"}],"wp:attachment":[{"href":"https:\/\/nunix.fr\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=59"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nunix.fr\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=59"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nunix.fr\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=59"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}