/* Notre passerrelle TCP IPv4 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "chiffre.h" #include "pass.h" /* Variables globales */ static int RUN = 1; // booléen pour la boucle d'acceptation static int Chiff = 0; // valeur pour l'option de chiffrement, -1 pour local, 1 pour distant, 0 pour rien static in_port_t PORTL, PORTD; // ports distant et local au format du réseau ! static uint32_t ADDRD; // adresse distante au format réseau static int SidLoc, SidDist; // variables globales des deux taches de transfert /* Fonction qui agit en tant que handler pour signal() */ void interrupt(int S) { switch (S) { case SIGCHLD: while (waitpid(-1, NULL, WNOHANG) != -1); break; case SIGINT: RUN = 0; exit(EXIT_FAILURE); break; case SIGTERM: fprintf(stderr, "Arret de la passerelle !\n"); exit(0); break; default: fprintf(stderr, "Reçu signal %d !?!\n", S); break; } } /* Fonction qui traduit une adresse IPv4 en chaîne de caractères xxx.xxx.xxx.xxx */ char *adip(uint32_t A) { // 255.255.255.255 par exemple = 15 char + '\0' = 16 static char buffer[16]; sprintf(buffer, "%d.%d.%d.%d", (int) ((A>>24) & 0xFF), (int) ((A>>16) & 0xFF), (int) ((A>>8) & 0xFF), (int) (A & 0xFF) ); return buffer; } /* Fontion qui fabrique un IPv4 au format réseau */ uint32_t makeip4(int a, int b, int c, int d) { // exemple : makeip4(127,0,0,1); uint32_t addresse; char *p; p = (char *) &addresse; *p++ = (unsigned char) a; *p++ = (unsigned char) b; *p++ = (unsigned char) c; *p = (unsigned char) d; return addresse; } /* Fonction qui lit taille octets à la fois */ int readNbc(int fd, char *buffer, int taille) { char *debut; int n, T = taille; #ifdef TRACE_SP printf("readNbc %d octets !\n", taille); #endif debut = buffer; while (T > 0) { if ((n = read(fd, debut, T)) == -1) return -1; debut += n; T -= n; } return 0; } /* Fonction du thread qui lit le port local et écrit vers le port distant */ void *fct_th(void *p) { int n; uint16_t lb; char buf[LBUF]; /* On s'occupe du transfert local -> distant */ while (1) { /* Lecture côté local */ if (Chiff == -1) { // chiffrement côté local if (read(SidLoc, &lb, sizeof(lb)) != sizeof(lb)) n = -1; else { n = (int) ntohs(lb); if (readNbc(SidLoc, buf, n) == -1) n = -1; #ifdef XOR_SIMPLE chiffre_xor_simple(buf, n); #endif #ifdef XOR_FICHIER chiffre_xor_fichier(buf, n); #endif #ifdef PLAYFAIR chiffre_playfair(buf, n); #endif } } else n = read(SidLoc, buf, LBUF); if (n > 0) { /* Ecriture côté distant */ if (Chiff == 1) { #ifdef TRACE_SP printf("writeNbc %d octets !\n", n); #endif #ifdef XOR_SIMPLE chiffre_xor_simple(buf, n); #endif #ifdef XOR_FICHIER chiffre_xor_fichier(buf, n); #endif #ifdef PLAYFAIR dechiffre_playfair(buf, n); #endif lb = htons((uint16_t) n); write(SidDist, &lb, sizeof(lb)); // On envoie le nombre d'octets du paquet } write(SidDist, buf, n); } if (n == -1) break; } return NULL; } /* Création de la passerelle pour le client */ int liaison(int fd) { int sid, n; uint16_t lb; struct sockaddr_in Sin; pthread_t thid; char buf[LBUF]; /* On est dans le cas du client qui veut se connecter au serveur */ /* Fabrication de l'interface réseau en IPv4 */ SidLoc = fd; // pour que le thread l'utilise if ((sid = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { perror("socket"); return 1; } Sin.sin_family = AF_INET; Sin.sin_port = PORTD; Sin.sin_addr.s_addr = ADDRD; /* On fait la connexion */ if (connect(sid, (struct sockaddr *) &Sin, sizeof(Sin)) == -1) { perror("connect"); return 2; } /* On masque le signal SIGPIPE */ signal(SIGPIPE, SIG_IGN); /* La connexion est faite ! */ SidDist = sid; // pour que le thread l'utilise /* Création du thread qui va se charger du transfert local vers distant */ if (pthread_create(&thid, NULL, fct_th, NULL) != 0) { fprintf(stderr, "Erreur création thread !\n"); close(fd); close(sid); return 3; } /* On s'occupe du transfert distant -> local */ while (1) { if (Chiff == 1) { // chiffrement côté distant if (read(sid, &lb, sizeof(lb)) != sizeof(lb)) n = -1; else { n = (int) ntohs(lb); if (readNbc(sid, buf, n) == -1) n = -1; #ifdef XOR_SIMPLE chiffre_xor_simple(buf, n); #endif #ifdef XOR_FICHIER chiffre_xor_fichier(buf, n); #endif #ifdef PLAYFAIR dechiffre_playfair(buf, n); #endif } } else n = read(sid, buf, LBUF); if (n > 0) { if (Chiff == -1) { #ifdef TRACE_SP printf("writeNbc %d octets !\n", n); #endif #ifdef XOR_SIMPLE chiffre_xor_simple(buf, n); #endif #ifdef XOR_FICHIER chiffre_xor_fichier(buf, n); #endif #ifdef PLAYFAIR chiffre_playfair(buf, n); #endif lb = htons((uint16_t) n); write(fd, &lb, sizeof(lb)); } write(fd, buf, n); } if (n == -1) break; } close(fd); return 0; } /* Message d'aide pour l'utilisateur */ void finerr(char *N, int err) { fprintf(stderr, "%s : version %s\n", N, Version); fprintf(stderr, "Utilisation : %s -d|-l|-s port_local nom_ou_ip_serveur port_distant\n", N); exit(err); } int main(int N, char *P[]) { int sid, new_sid, err; // socket id = file descriptor struct sockaddr_in Sin, Srec; struct hostent *host; pid_t pid; long lg; /* Récupération des éventuels signaux qu'on pourrait recevoir, et appel de la fonction interrupt() */ signal(SIGCHLD, interrupt); signal(SIGINT, interrupt); signal(SIGTERM, interrupt); #ifdef XOR_FICHIER if (init_cle_xor_fichier()) { fprintf(stderr, "Erreur lecture fichier clé : '%s' !\n", NFCLE); return 1; } #endif #ifdef PLAYFAIR init_table_chiffre(CLE_PLAYFAIR); #endif /* Vérification que l'on a bien 4 paramètres et seulement 4 */ if (N != 5) finerr(P[0], 1); /* Vérification du 1er paramètre */ if (P[1][0] != '-') finerr(P[0], 1); if (strlen(P[1]) != 2) finerr(P[0], 2); switch (P[1][1]) { case 'l': Chiff = -1; break; case 'd': Chiff = 1; break; case 's': Chiff = 0; break; default: finerr(P[0], 2); break; } PORTL = htons(atoi(P[2])); PORTD = htons(atoi(P[4])); /* Récuperation de l'adresse */ if ((host = gethostbyname(P[3])) == NULL) { fprintf(stderr, "Erreur gethostbyname no %d !\n", h_errno); return 2; } bcopy((void*) (host->h_addr), (void*) (&ADDRD), host->h_length); /* Fabrication de l'interface réseau en IPv4 */ if ((sid = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { perror("socket"); return 5; } Sin.sin_family = AF_INET; Sin.sin_port = PORTL; Sin.sin_addr.s_addr = makeip4(0, 0, 0, 0); // Sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); /* Puis faire un bind() pour s'attacher à ce port */ /* La fonction bind est une fonction unique, aveugle à la structure qui va envoyer les données. Elle ne se soucie * pas de savoir si on va communiquer en IPv4 ou IPv6 par exemple. * La fonction bind prend donc en fait une structure générique, sockaddr. Dans cette structure elle lit sa_family, * qui lui indique qu'il s'agit d'IPv4. */ /* Pour faire la conversion des struct on fait un cast. */ if (bind(sid, (struct sockaddr *) &Sin, sizeof(Sin)) == -1) { perror("bind"); return 3; } /* On définit le nombre d'écoutes simultanées */ if (listen(sid, NBCLI) == -1) { perror("listen"); return 4; } /* On laisse le serveur accepter des connexions * A chaque connexion établie, le serveur crée un fils pour gérer celle-ci. */ while (RUN) { // on attend les clients locaux pour transmettre les données lg = sizeof(Sin); if ((new_sid = accept(sid, (struct sockaddr *) &Srec, (socklen_t *) &lg)) < 0) { perror("accept"); } else { #ifdef TRACE_1 printf("Connexion de %s !\n", adip(ntohl(Srec.sin_addr.s_addr))); #endif /* Création d'un processus dédié au client */ if ((pid = fork()) == -1) { perror("fork"); write(new_sid, "Erreur passerelle !\n", 21); } else { if (pid == 0) { // code du fils err = liaison(new_sid); // Création de la passerelle privée pour le client if (err) fprintf(stderr, "Erreur liaison %d !\n", err); return err; } } close(new_sid); // On ferme le file descriptor qu'on vient de créer puisque c'est le fils qui s'en occupe } } return 0; }