PSR SE2a4 2022/2023 G5

De Wiki d'activités IMA

TP SMTP 2022 - Spécifications techniques

Farid LAZOUACHE / William BECUE

GITHUB

 https://github.com/FaridLazouache/SMTP 

Paramètres du TP

Groupe Domaine Nom VM IPv4 virtuelle IPv4 routée VLAN privé Réseau local IPv4 Cisco 6509-E Cisco 9200 ISR4331 IPv6
Groupe 5 miserable.site VMalex2 10.24.0.250 193.48.57.167/28 24 10.24.0.0/16 10.24.0.251 10.24.0.252 10.24.0.253 2001:660:4401:60A0:216:3eff:fe92:8ec5

Machine virtuelle

  • Connexion au serveur Capbreton :
ssh root@capbreton.plil.info

    → Dossier de stockage des données de la VM : /usr/local/xen/domains/VMalex2
    → Fichier de configuration de la VM : /etc/xen/VMalex2.cfg

  • Connexion à la VM :
ssh root@193.48.57.167

Journal de bord

Séance 1

Découverte du projet, de la strucuture des serveurs, des sockets, etc.
Nous avons édité le Wiki afin de choisir notre machine virtuelle.
Nous avons testé la machine virtuelle avec la commande suivante : ssh root@193.48.57.167

Séance 2

Nous avons découvert le projet, le contenu et la structure des programmes. Nous avons, notamment, examiné la gestion des SMTPs rentrants et sortants, nous avons commencé notre analyse du fichier in.c. La fonction qui gère la gestion des clients SMTP (les clients qui souhaitent se connecter de l'extérieur) puis la gestion des courriels (des demandes venants de l'extèrieur) qui permet de récupérer les noms de domaines et les MX. Ensuite, la fonction gestionCourriel() permet à partir d'une boucle de gérer la connexion au serveur stocké dans ss (socket). Le contenu des messages va être stocké dans un fichier, celui-ci va être envoyé vers le serveur SMTP qui, lui, va envoyer le message sur internet ou bien sur le disque si c'est un utilisateur local. Nous, nous sommes rendu compte, que le message de base envoyait une réponse à tous les MX. Cependant, il suffit d'un MX qui réponde à notre demande. De fait nous avons ajouté un else break ligne 66 dans le cas où nous avions déjà eu un retour, pour stopper la boucle. Nous avons compris que gestion SMTP rempli struct courriel qui appel gestion courriel une fois remplie.

Ce qui a été fait

  • les serveurs SMTP doivent gérer les enregistrements MX permettant d’associer un nom de domaine à un serveur de messagerie, le recours aux enregistrements AAAA et A est à utiliser en cas d’absence de MX
  • pour prévenir les failles, les deux serveurs SMTP seront écrits à partir de zéro en respectant la RFC 5321 mais avec des simplifications
  • les serveurs doivent être codés en langage C en utilisant la bibliothèque des sockets
  • les commandes VRFY et EXPN qui permettent au client de vérifier si une messagerie est disponible pour la transmission, ne seront pas implémentées
  • le routage des messages ne doit pas être implémenté
  • les communications peuvent ne pas être chiffrées, les communications non chiffrés sont reçues sur le port 25
  • chaque connexion doit être gérée via un processus léger (Thread) et chaque commande par une fonction à laquelle est passée une structure représentant l’état du dialogue entre le client et le serveur
  • les serveurs SMTP doivent être contactables en IPv4 et en IPv6, ils doivent aussi prendre en charge IPv4 et IPv6 pour la transmission vers les serveurs SMTP cibles ; => connexion et inittialisation serveur
  • les deux serveurs ne diffèrent que par la méthode de distribution finale (stockage dans un système de fichiers ou envoi à un serveur SMTP cible)
  • le serveur SMTP sortant ne doit être lié qu’à l’interface réseau loopback
  • ne doivent être stockés que les courriels à destination d’un utilisateur local
  • le format de stockage doit être le format maildir qui est une structure de répertoires particulière utilisée pour sauvegarder des courriers électroniques (le format maildir est très bien décrit sur Wikipédia)
  • chaque message entrant doit être ajouté au dossier de réception Maildir propre à l’utilisateur de destination
  • pour assurer un nom unique aux fichiers de stockage, utilisez la date en seconde (fonction time), le PID et un compteur commun à tous les flux d’exécution (utilisation de sémaphores indispensable). => time
  • Free mutexes => ajout via libs/Flux/mutex.c dans la fonction, ajout
    int mutex_fin(void){
    free(mutexes)}

    à la fin de la fonction, ainsi le thread est libérer mais pas l'allocation mémoire
  • via src/SMTPout/out.c dans la fonction void gestionCourriel, ajout
    char newfile[MAX_CHEMIN];
    sprintf(newfile,"%s/%s/%s/%ld_%010d_%010ld",dossier,id,MAILDIR_NEW,time(NULL),pid,messages);
    rename(fichier, newfile);

    Dans le but de gérer MAILDIR_NEW avec le rename
  • Nous nous sommes rendu compte via src/SMTPin/in.c dans la fonction void gestionCourriel qu'il y avait une continuité d'envoi de mail aux autres MX, même si l'envoi était un succès. De fait, nous avons ajouté fclose(dialogue); à la fin de la fonction

Reste à faire

  • un système doit être réalisé permettant au serveur SMTP sortant de mettre en file d’attente les messages non remis à destination pour cause d’erreur transitoire du serveur SMTP cible ; => data gestion_dialog erreur temporaire mais pas definitive => courriel file d'attente et le renvoyer comme un script
  • les communications peuvent être chiffrées par TLS, les communication chiffrées peuvent être négociées directement sur le port 465 ; => serveur deux ports différents, un en claire et un directement chiffré
  • il doit être possible de basculer sur une communication chiffrée à partir du port 25 en utilisant les commandes EHLO et STARTTLS ; => 4 caractères
  • Faire une communication chiffré
  • pour la transmission vers les serveurs SMTP cibles, une communication chiffrée doit être préférée ; => envoyer un helo à la place => lancer la commande starTTLS => ehlo a partir de cette commande reponse plusieur lignes, si connait starttls reponse 250

Séance 3

Dans le cadre de la troisième séance nous avons essayé d'établir une communication avec le serveur. Pour ce faire nous avons envoyé à la machine virtuel (notre serveur) le fichier de gestion de serveur
    Nous nous sommes rendus compte que SMTPin et SMTPout écouter sur les mêmes interfaces.
    Alors nous avons modifier SMTPout de sorte à ce qu'il n'écrive que sur eth0

    Fait cette séance

    • Nous avons instancié une nouvelle interface. Tout d'abord, via src/SMTPout/args.h, nous avons définis la taille de l'interface #define CONFIG_MAX_INTERFACE 1024 , et nous avons ajouté une variable interface dans la structure struct smtp_config , char interface[CONFIG_MAX_INTERFACE]
    • Dans la fonction analyseArguments, de SMTPout/args.c : Structure long_options modifiée pour prendre en compte le paramètre d'interface i
    • Ajout dans la fonction void afficheSyntaxe(char *executable) fprintf(stderr," -i|--interface # lier à l'interface eth0\n");
      Puis dans la fonction int analyseArguments {"interface", required_argument, 0, 'i'},
    • Dans la fonction analyseArguments, de SMTPout/args.c : Paramètres de la fonction getotp_long modifiés pour inclure i et un argument supplémentaire
    • Ensuite ajout de l'argument i => int c=getopt_long(argc,argv,"lp:j:n:d:u:i:",long_options,NULL);
      Afin d'ajouter un case i dans le switch c,case 'i': strncpy(config->interface, optarg, sizeof(config->interface)-1);

    À faire la prochaine fois

    • Gestion des erreurs
    • Dans SMTPout/out.c ajouter gestion config.interface (à ajouter dans la structure config)

    Séance 4

    Fait cette séance

    • Ajout d'interfaces au niveau du SMTPout, nous avons complété le fichier SMTPout/out.c pour gérer l'argument i.
    • En effet, dans la fonction principale nous avons ajouté else interface = config.interface;
    • Gestion d'erreurs : notification de la part du serveur de l'erreur 502, en cas de EHLO mal orthographié.
    • Test serveur mail avec le site mxtoolbox.com.
    • Test serveur mail avec telnet.

    À faire la prochaine fois

    • DEBUGGER le SMTPin

    Séance 5

    Fait cette séance

    • Analyse de boucleServeur dans reseau.c, appelé par SMTPin et SMTPout. Puis nous avons ajouté des #ifdef DEVERMINE dans les fonctions appelées.
    • Nous avons ajouté via /libs/SMTP/smtp.c dans la fonction static int retour_generique
      static int retour_generique(FILE *dialogue,int succes,char *erreur,int taille) { char ligne[MAX_LIGNE];
      //while(fgets(ligne, MAX_LIGNE, dialogue) != '-') // changed fgets to consider hyphen
      //{
      if(fgets(ligne,MAX_LIGNE,dialogue)==NULL) return -1;
      ligne[MAX_LIGNE-1]='\0';

      int code;
      int statut=sscanf(ligne,"%d",&code);
      #ifdef DEVERMINE
      printf("code = %d\n", code);
      printf("statut = %d\n", statut);
      printf("succes = %d\n", succes);
      #endif
      if(statut==1 && code!=succes) strncpy(erreur,ligne,taille-1);
      return (statut==1 && code==succes)?0:-1;
      //} }

    • SMTP Dialogue ne fonctionnait pas, commande strace -f ./SMTPin -l
    • Nous avons modifié via libs/SMTP/smtp_private.h la valeur de :

      #define ACCUEIL_CODE 220
      #define SUCCES_QUIT_CODE 250
      Pour correspondre à ce que nous renvoyer la commande strace dans le but d'envoyer des mails

    À faire la prochaine fois

    • modifier fgets dans libs/smtp.c, gérer la gestion d'une commande à deux lignes : si présence d'un tiret dans la quatrième colonne, il faut prendre en compte la commande complète.

    Séance 6

    Fait cette séance

    • On a réalisé différents test sur le serveur de mail. Cependant, en se connectant nous avons bien les mx, mais on en fait rien. C'est à dire on se retrouve dans une boucle while qui est dans boucleServeur, de in.c.
    • On a modifié connexionServeur dans reseau.c à l'aide du programme envoyé par le professeur.

    À faire la prochaine fois

    • Réussir à sortir de la boucle while dans boucleserveur de in.c
    • Problème on devrait sortir de la boucle while, on y arrive avec gmail, mais la fac le bloque Trouver comment faire avec un IPV4

      Séance 7

      Fait cette séance

    • Suite à nos tentatives d'envoi de mail via le test de SMTPin, nous nous sommes rendu compte que nous avions un problème dans connexionServeur. Nous avons ajouté if(connect(s,p->ai_addr,p->ai_addrlen)==0) break;
      if(p->ai_family==AF_INET6) ipv6=1;
      if(p->ai_family==AF_INET) ipv4=1;
      p=p->ai_next;
      }

    • Problème rencontré : notre serveur résolution de nom n'était pas opérationnelle, nous l'avions kill, la semaine précédente, nous ne l'avions pas restart.
    • Nous arrivons a envoyé des mails.
      Nous avons définis text.txt
      vi text.txt
      EHLO
      MAIL FROM: root@miserable.site
      RCPT TO: william.becue@polyetch-lille.fr
      DATA : CECI EST UN TEST
      SUBJECT: test
      .
      QUIT
      Puis on utilise nc -C -q0 -i1 localhost 25 < text.txt
      Test2.PNG
    • Maintenant on doit faire en sorte, que l'on attende que le serveur nous réponde. Pour ce faire, nous avons modifié via /libs/SMTP/smtp.c la fonction static int dialogue_DATA
      static int dialogue_DATA(FILE *dialogue,char *corps,char *erreur,int taille)
      {
      int code;
      // FAIRE APPEL A DIALOGUE_GENERIQUE POUR ENVOYER DATA AVANT D'ENVOYER LE CORPS ET LE POINT
      code = dialogue_generique(dialogue, "DATA", "", CONTINUE_DATA_CODE, erreur, taille);
      #ifdef DEVERMINE
      printf("\nCODE = %d\n", code);
      #endif
      if(!code)
      { if(fprintf(dialogue,corps)<0) return -1;
      if(fprintf(dialogue,".\r\n")<0) return -1;
      //dialogue_generique(dialogue, NULL, NULL, NULL, erreur, taille);
      return retour_generique(dialogue,SUCCES_DATA_CODE,erreur,taille);
      }
      else
      return -1;
      }

      À faire la prochaine fois

    • Essayer de rendre SMTPout fonctionnel.
    • Séance 8

      Fait cette séance

    • On créé une map à la racine de notre VM, map.txt
    • On déclare des utilisateurs :
      root : root@miserable.site
      wbecue : wbecue@miserable.site
      flazouac : flazouac@miserable.site

    • on a du créer manuellement l'arborescence des fichiers et des utilisateurs. En effet, le serveur ne créé pas de strcuture MAILDIR, composé de ses 3 sous répertoires (tmp,cur et new) :
      mkdir /tmp/root
      mkdir /tmp/root/tmp
      mkdir /tmp/root/new
      Puis,
      mkdir /tmp/flazouac
      mkdir /tmp/flazouac/tmp
      mkdir /tmp/flazouac/new
      Et,
      mkdir /tmp/wbecue
      mkdir /tmp/wbecue/tmp
      mkdir /tmp/wbecue/new
    • On a lancé sur un terminal strace ./SMTPout -i eth0 -u map.txt et on teste "de l'extérieur" depuis notre session nc 193.48.57.167 25 mais nous ne recevions rien, via différent #ifdef DEVERMINE{}#endif.
    • De fait, nous avons modifé via src/SMTPout/ou.c la fonction adresseVersUtilisateur(). Nous avons modifié for(i=0;i<strlen(adresse);i++){ par for(i=0;i<strlen(destinataire);i++){
    • Ensuite, via src/SMTPout/ou.c la fonction scanCarteUtilisateur(). Nous avons modifié if(strcasecmp(adresses[i].adresse,courriel)) return adresses[i].id; par if(strcasecmp(adresses[i].adresse,courriel)== 0) return adresses[i].id;
    • On a créé sur notre machine un testout.txt que l'on va tester via nc -C -i1 -q0 193.48.57.167 25 < testout.txt
    • Test1.PNG

    • On a définit un exécutable shell testcharge.sh
    • for i in {1..10}
      do
      nc -C -i1 -q0 193.48.57.167 25 < testout.txt &
      echo $i
      done

      Testcharge.PNG

    • Dans le but de pouvoir envoyer des mails depuis une adresse de l'extérieur et de le recevoir sur le serveur. On modifié via src/smtp.c la fonction gestion_MAIL. On avait un problème de gestion d'espace via la commande MAIL FROM:
    • static int gestion_MAIL(char *ligne,FILE *client,struct courriel *donnees){
      char cmd[MAX_LIGNE];
      char tag[MAX_LIGNE];
      char arg[MAX_LIGNE];
      char suite[MAX_LIGNE];
      char format[MAX_LIGNE];
      int statut=sscanf(ligne,"%4s %s %s %s",cmd,tag,arg,suite);
      #ifdef DEVERMINE
      printf("\nstatut = %d\n", statut);
      #endif
      if(statut!=3 || strcasecmp(tag,MAIL_TAG)!=0){
      sprintf(format,"%%4s %s%%s %%s", MAIL_TAG);
      #ifdef DEVERMINE
      printf("\nformat = %s\n", format);
      #endif
      int statut2=sscanf(ligne,format,cmd,arg,suite);
      #ifdef DEVERMINE
      printf("\nstatut2 = %d\n", statut2);
      #endif
      if(statut2 !=2){
      if(fprintf(client,"%03d %s\r\n",ERREUR_MAIL_CODE,ERREUR_MAIL_TEXTE)<0) return GESTION_STOP;
      #ifdef DEVERMINE
      printf("\nclient = %s\n", client);
      #endif
      return GESTION_ERREUR;}
      }

      FARID EVAL

      Pour être évalué, il me fallait enlever le fait que le programme SMTPout imprime autant de lignes de "..." qu'il n'y a de lignes dans le fichier, pour cela il fallait modifier smtp.c Dans la fonction afficheCourriel de smtp.c

      int end = 0;
      while(donnees->corps[c]!='\0'){
       if(cpt2<AFFICHE_COURRIEL_DEBUT || cpt2>cpt-AFFICHE_COURRIEL_FIN) fwrite(donnees->c  orps+c,sizeof(char),1,sortie);
       if(cpt2==AFFICHE_COURRIEL_DEBUT && cpt2<=cpt-AFFICHE_COURRIEL_FIN && !end)
       {
        fprintf(sortie,"...\n");
        end = 1;
       }
      
      if(donnees->corps[c]=='\n') cpt2++;