PSR SE2a4 2022/2023 G6

De Wiki d'activités IMA

Fichier code Messagerie

Ci-dessous le lien téléchargeable de nos différents fichiers constituant le projet Messagerie.
Média:groupe6.zip

Infrastructure

Le serveur est disponible sur yamaha.germond.org (193.48.57.161), le DNS est hébergé sur l'infrastructure de @Bastien.Germond [1]

[1] https://github.com/BastienGermond/NixOs-config/blob/ecdf095a8ea55ed00d8b612dde74400cd11a444e/hosts/coral/data/dns/zones/germond.org.db.nix#L48

Architecture

Le projet est sous Meson ; pour compiler, on utilise la commande <meson compile -C build>

Comment récupérer un certificat avec Let's Encrypt ?

[0] Installer certbot sur la machine (apt install certbot).

[*] Avant de lancer la prochaine étape, il faut bien évidement qu'un nom de domaine soit correctement pointé sur la machine sur laquelle on cherche à obtenir un certificat.
Dans notre cas le nom de domaine est yamaha.germond.org, si on lance host yamaha.germond.org, on peut bien observer qu'on obtient une réponse DNS et une IP routé 193.48.57.161.

[1] On demande alors à Let's Encrypt[1] de nous fournir un certificat en lançant certbot certonly --standalone -d yamaha.germond.org.
Quelques questions seront demandées ainsi qu'une adresse mail et pour finir le certificat sera créé.

Le certificat ainsi que les clés seront stockés dans /etc/letsencrypt/live/yamaha.germond.org/*.

Premier pas

03/02/2023
Création VM : yamaha (IP : 193.48.57.161)
Lancement de la VM
Ouverture du projet beta
Découverte SMTPin


Réception message

10/02/2023
Réécriture du code

Le nouveau code reprend l'architecture globale du code fourni, ils rassemblent juste les librairies dans une architecture sous src et utilise Meson.

Création code root@yamaha:~/SMTP/src# vim MTAext.c -> réception message (MTAext.c)
Pour générer un identifiant unique, on utilise la librairie <libuuid> : #include <uuid/uuid.h>
https://stackoverflow.com/questions/51053568/generating-a-random-uuid-in-c

int processing_mail(struct smtp_state *state){
    LOG_D("Processing mail...");
    
    uuid_t binuuid;// Unique ID in binary
    uuid_generate_random(binuuid);
    char *uuid = malloc(37);//Nb of characters for unique id
    uuid_unparse_lower(binuuid, uuid);//unique id en lettre minuscule

On définit ici un identifiant unique pour pouvoir suivre le mail envoyé (éviter toutes collisions ou perdition de données).

if(fptr == NULL){
    LOG_E("Couldn't open Maildir tmp");
    exit(1);
}

Ici, on utilise une fonction conditionnelle pour informer que l'on n'est pas parvenu à entrer dans le dossier de redirection de mail.

fprintf(fptr, "From: %s\r\n", state->sender); 
fprintf(fptr, "To: %s\r\n", state->recipient);
fprintf(fptr, "%s\r\n", state->body);
fclose(fptr);

On transforme les informations stockées dans la structure "state" en format de mail avec la source, le destinataire et le corps.

int server_process(int sfd) {
    return smtp_process(sfd, processing_mail);
}
int main(void) {
    log_init_default("-", LOG_DEBUG);

    int sock = init_server("yamaha.germond.org", "8025", NULL);

    if (sock < 0) {
        LOG_C("Failed to start server");
        return EXIT_FAILURE;
    }

    int status = loop_server(sock, server_process);

    if (status < 0) {
        LOG_E("MTAext: server closed unexpectedly");
    };

    return 0;
}

La fonction main permet d'initialiser et de lancer notre serveur <yamaha.germond.org> (récupérer l'adresse IPV4/V6) et d'informer des erreurs.

Test code MTAext.c : vérification
Le mail est composé d'une entête et d'un corps de mail
On lance la compilation du code puis l’exécutable ./build/MTAext
La connexion s'établit entre deux terminaux : celui de notre machine virtuelle

root@yamaha:~/SMTP# ./build/MTAext  
[DEBUG] Server opened on 2001:660:4401:60a0::100:0:8025
[DEBUG] loop_server: waiting for incomming connection
[DEBUG] loop_server: waiting for incomming connection
[DEBUG] loop_server: waiting for incomming connection

Et celui de la machine PC zabeth19

pifou@zabeth19:~$ telnet yamaha.germond.org 8025
Trying 2001:660:4401:60a0:216:3eff:fe63:515a...
Connected to yamaha.germond.org.
Escape character is '^]'.
250 Welcome

-> compilation/execution de réception de mails (envoyeur, receveur, corps de mail, UUID)
On envoi un mail depuis la machine zabeth19

pifou@zabeth19:~$ telnet yamaha.germond.org 8025
Trying 2001:660:4401:60a0:216:3eff:fe63:515a...
Connected to yamaha.germond.org.
Escape character is '^]'.
250 Welcome
HELO zabeth19
250 Hi!
MAIL FROM:<testreception@yamaha.germond.org>            
250 Sender recorded
RCPT TO:<receptiontest@yamaha.germond.org>
250 Target recorded
DATA 
Subject: Démonstration du bon fonctionnement de la réception

Avec ce mail nous voyons que nous arrivons à communiquer (envoyer d'une machine quelconque et recevoir sur notre serveur.

Et on le reçoit sur notre serveur root@yamaha

[DEBUG] loop_server: waiting for incomming connection
[DEBUG] smtp_process: incomming (cmd: HELO): HELO zabeth19
[DEBUG] _thread_main: process done, closing connection.
[DEBUG] smtp_process: incomming (cmd: MAIL): MAIL FROM:<testreception@yamaha.germond.org>
[DEBUG] smtp_process: incomming (cmd: RCPT): RCPT TO:<receptiontest@yamaha.germond.org>
[DEBUG] smtp_process: incomming (cmd: DATA): DATA
[DEBUG] Processing mail...
[DEBUG] Mail UUID 3a59c1ca-c52a-4af9-8b22-9e9beb1faf53
[DEBUG] Writing mails at /root/Maildir/tmp/3a59c1ca-c52a-4af9-8b22-9e9beb1faf53
[DEBUG] Move mails at /root/Maildir/new/3a59c1ca-c52a-4af9-8b22-9e9beb1faf53
[DEBUG] _thread_main: process done, closing connection.

Avec la commande <mutt>, on peut vérifier que le mail est bien parvenu jusqu'à notre serveur :

 
   1 N   Jan 01 testreception@y (0.1K) Démonstration du bon fonctionnement de la réception
   2     Jan 01 bastien@yamaha. (0.1K)
   3 O   Jan 01 bastien@yamaha. (0.1K) Test mail
   4     Jan 01 a@b.c           (0.1K)
   5     Jan 01 a@b.c           (0.1K)
   6     Jan 01 a@b.c           (0.1K)

Envoi message

Création code root@yamaha:~/SMTP/src# vim MTAext.c -> envoi message (MTAint.c)

Pour pouvoir utiliser les expressions régulières, on utilise la librairie <regex> : #include <regex.h>

 
int split_email(char* email, char *local_part, char *domain_name, size_t lp_size, size_t dn_size) {                                                                                 
  char *regexString = "(.+)@(.+)";                                                        
  size_t maxGroups = 3;                                                                   
  regex_t regexCompiled;                                                                  
  regmatch_t groupArray[maxGroups];

On commence par créer une fonction d'extraction du nom de domaine qui prend en paramètre le nom du domaine et sa taille.
La forme char *regexString = "(.+)@(.+)"; est l'expression régulière nous permettant de récupérer le nom d'utilisateur (tout ce qui ce situe avant le "@")et le nom de domaine (tout ce qui ce situe après le "@"). Le site https://regex101.com/ nous permet de rentrer notre test string et une expression régulière et d'obtenir le match correspondant, soit, dans notre cas : @(.+).
Il y a donc 1 match pour 3 groupes (l'ensemble match, l'utilisateur, le destinataire).

 
if(regcomp(&regexCompiled, regexString, REG_EXTENDED)){
        LOG_D("Fail to compile regular expression.");
        return -1;
    }

if regexec(&regexCompiled, cursor, maxGroups, groupArray, 0)){
        LOG_D("There is no recipient domain.")
        return -1; // No match
    }

if(groupArray[0].rm_so == (size_t)-1){
        return -1; // No group in match
    }

Ici, on utilise plusieurs fonctions conditionnelles pour informer que l'on n'est pas parvenu à compiler l'expression régulière, qu'il n'y a pas de match (donc de domaine destinataire), qu'il n'y a pas de groupe (dans le domaine destinataire).

 
memcpy(local_part, email + groupArray[1].rm_so + 1, groupArray[1].rm_eo - groupArray[1].rm_so - 1);                                                                               
memcpy(domain_name, email + groupArray[2].rm_so, groupArray[2].rm_eo - groupArray[2].rm_so - 1); 

On utilise la fonction memcpy

int processing_mail(struct smtp_state *state) {     
  LOG_D("Processing mail...");                                                            
                                                                                          
  int status = split_email(state->recipient, LOCAL_PART + 0, DOMAIN + 0, sizeof(LOCAL_PART), sizeof(DOMAIN));

  if (status < 0) {
    LOG_E("There has been an error during recipient email spliting");
    return -1;
  }

  LOG_D("local_part: %s", LOCAL_PART);
  LOG_D("domain_name: %s", DOMAIN);

  return -1;
};

Évaluation

Réception
Test de reception VM depuis l'envoi d'un mail en mode manuel (console PC). Test de reception IPV4 pour le portail polytech puis gmail. (Changement adresse IP 193.48.57.161) Test de reception IPV6 : (nom de domaine yamaha.germond.org) : Modification du code
Dans le programme smtp.h : Changement du 250 Welcome en 220 Welcome (afin de pouvoir établir la communication pour recevoir le mail).

Dans le programme DATA.c : Changement du 354 :
 fprintf(state->sock, "354 Mail\r\n"); 

On modifie cette partie pour éviter qu'il ne se perde dans les retours de lignes ( originellement "/r/n./r/n") et comprend qu'il y a plusieurs arguments attendus.