/* * cesard.c : serveur de cryptographie faible par la méthode de César : * faire tourner les lettres dans l'alphabet.r * * Ce programme parfaitement inutile est juste un exemple * d'implémentation de serveur TCP. On peut facilement s'en inspirer * pour implémenter d'autres serveur. Pour celà il faut * - changer les #defines ci-dessous ; * - reécrire la fonction answer(). * * Auteur : Edgar Bonet Orozco * Inspiré du travail de : * Juan Arturo Nolazco Flores * Pavel Moreno Orozco * Brenda Sanchez */ #include #include #include #include #include #include #include #include #include #include #include #include /* * Pour définir le service "cesar", il faut ajouter la ligne * « cesar 3591/tcp » dans /etc/services. On peut lui donner un numéro * de port différent de 3591 mais si le numéro de port est inférieur à * 1024, le démon ne pourra fonctionner que sous l'UID root (à moins * d'être lancé par inetd). Si le service n'est pas ainsi défini, le * port numéro DEF_PORT est pris par défaut. * * On peut lancer le serveur à chaque connexion via inetd. Pour cela il * faut rajouter dans /etc/inetd.conf la ligne « cesar stream tcp nowait * nobody /usr/local/bin/cesard cesard » puis demander à inetd de * relire son fichier de configuration en tapant « killall -HUP inetd ». * Ceci suppose que le service cesar a été défini dans /etc/services, * que l'utilisateur nobody est défini dans /etc/passwd et que * l'exécutable se trouve dans /usr/local/bin/cesard. */ #define SERV_NAME "cesar" /* service tcp fourni */ #define DEF_PORT 3591 /* port par défaut */ #define LINE_SZ 256 /* taille du tampon de lecture */ /* Intervalle des caractères ASCII imprimables (entre SP et ~) */ #define ST_ASCII ' ' /* début de l'intervalle */ #define LEN_ASCII ('~' - ' ' + 1) /* longueur */ /* Aide pour l'utilisateur */ #define usage() fprintf(stderr, msg_usage, argv[0], SERV_NAME, DEF_PORT) char msg_usage[] = "syntaxe : %s [-h] [-a adresse] [-p port] [-s] -h : affiche cette aide ; -a adresse : précise l'adresse IP sur laquelle il faut écouter ; ce peut être une adresse numérique ou un nom de domaine ; par défaut le serveur écoute toutes les adresse possibles ; -p port : précise le port sur lequel il faut se connecter ; le port par défaut est celui attribué au service %s où %d si ce service n'est pas défini ; -s : traite une seule connexion sur stdin/stdout ; ce comportement est adopté par défaut si stdin est un socket, l'option -s est donc inutile si le serveur est lancé par inetd.\n"; /* * Décale de offset les caractères imprimables * de la chaîne s sans toucher aux autres */ void cesar(int offset, char *s) { offset %= LEN_ASCII; if (offset < 0) offset += LEN_ASCII; for ( ; *s != '\0' ; s++) { if (*s < ST_ASCII || *s >= ST_ASCII + LEN_ASCII) continue; *s = ((*s - ST_ASCII) + offset) % LEN_ASCII + ST_ASCII; } } /* * Répond à une connexion ouverte sur stdin et stdout et ferme la connexion */ void answer(void) { char line[LINE_SZ]; char *pos; int offset; int offset_ok = 0; /* Le tampon stdout doit être vidé pour chaque ligne écrite */ setlinebuf(stdout); /* Il faut saluer le client */ printf("Serveur cesard prêt\r\n"); /* Le client doit demander un décalage */ do { if (fgets(line, LINE_SZ, stdin) == NULL) { fprintf(stderr, "fils : erreur fgets"); return; } if (sscanf(line, "offset %d", &offset) == 1) offset_ok = 1; else printf("Précisez d'abord un décalage avec " "la commande \'offset\'\r\n"); } while (!offset_ok); /* On est prêt à traiter le texte */ printf("entrez votre texte, terminez par \'.\'\r\n"); while (1) { if (fgets(line, LINE_SZ, stdin) == NULL) { fprintf(stderr, "fils : erreur fgets"); break; } /* Un point en début de ligne doit être supprimé... */ pos = line; if (*pos == '.') { pos++; /* ...mais un point seul sur une ligne marque la * fin de celle-ci */ if (*pos == '\r' || *pos == '\n') { printf("Au revoir\r\n"); break; } } /* On « crypte » le texte et on le renvoie */ cesar(offset, pos); printf("%s", pos); } /* Y'a plus qu'à fermer la connexion */ if(fclose(stdout) == EOF) perror("fils : fclose"); } /* * La fonction main n'a rien de spécifique à ce serveur. Elle peut être * utilisée pour implémenter d'autres serveurs TCP. */ int main(int argc, char **argv) { int opt; char my_name[LINE_SZ] = "*"; int my_port = -1; struct servent *service; struct hostent *resolv; struct in_addr my_in_addr; struct sockaddr_in my_addr; struct sockaddr_in his_addr; int addr_size; int s, t; int child; /* Lecture de la ligne de commande */ while ((opt=getopt(argc, argv, "ha:p:s")) != EOF) switch (opt) { case 'a' : strncpy(my_name, optarg, LINE_SZ-1); break; case 'p' : my_port = atoi(optarg); break; case 'h' : usage(); return 0; case 's' : answer(); return 0; default : usage(); return -1; } /* Si stdin est un socket, il n'y a plus qu'à répondre */ addr_size = sizeof(struct sockaddr); if (getpeername(fileno(stdin), (struct sockaddr *) &his_addr, &addr_size) == 0) { answer(); return 0; } else if (errno != ENOTSOCK) { perror("getpeername"); return -1; } /* Recherche de l'adresse IP */ if (strcmp(my_name, "*") == 0) /* Toutes les adresse disponibles */ my_in_addr.s_addr = htonl(INADDR_ANY); else if (strspn(my_name, "0123456789.") == strlen(my_name)) { /* Adresse numérique */ if (inet_aton(my_name, &my_in_addr) == 0) { fprintf(stderr, "erreur inet_aton\n"); return -1; } } else { /* Nom de domaine */ resolv = gethostbyname(my_name); if (resolv == NULL) { perror("gethostbyname"); return -1; } my_in_addr.s_addr = *((unsigned long *) resolv->h_addr_list[0]); } /* Recherche du numéro de port */ if (my_port == -1) { service = getservbyname(SERV_NAME, "tcp"); if (service != NULL) my_port = service->s_port; else my_port = DEF_PORT; } /* Créer le socket */ s = socket(AF_INET, SOCK_STREAM, 0); if (s == -1) { perror("socket"); return -1; } /* L'attacher à mon adresse */ my_addr.sin_family = AF_INET; my_addr.sin_port = htons(my_port); my_addr.sin_addr = my_in_addr; if (bind(s, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) == -1) { perror("bind"); return -1; } if (listen(s, 5) == -1) { perror("listen"); return -1; } addr_size = sizeof(struct sockaddr); /* Éviter les zombies : cf. man sigaction */ signal(SIGCHLD, SIG_IGN); /* Boucle d'attente des connexions */ while (1) { /* On prend une connexion */ if ((t = accept(s, (struct sockaddr *) &his_addr, &addr_size)) == -1) { /* Interruption reçue, on reprend */ if (errno == EINTR) continue; perror("accept"); return -1; } /* On crée un fils pour s'en occuper */ if ((child = fork()) == -1) { perror("fork"); return -1; } /* Code du fils */ if (child == 0) { /* Fermer le socket qui reste en écoute */ if (close(s) == -1) { perror("fils : close socket"); return -1; } /* Rediriger les entrées/sorties */ if (close(fileno(stdin)) == -1) { perror("fils : close stdin"); return -1; } if (dup(t) == -1) { perror("fils : dup stdin"); return -1; } if (close(fileno(stdout)) == -1) { perror("fils : close stout"); return -1; } if (dup(t) == -1) { perror("fils : dup stout"); return -1; } /* Répondre à la requête */ answer(); return 0; } /* Code du père */ if(close(t) == -1) { perror("père : close"); return -1; } } }