PSR SE2a4 2022/2023 G6 : Différence entre versions
(→Evaluation) |
|||
(42 révisions intermédiaires par 2 utilisateurs non affichées) | |||
Ligne 1 : | Ligne 1 : | ||
− | == | + | == Fichier code Messagerie == |
+ | Ci-dessous le lien téléchargeable de nos différents fichiers constituant le projet Messagerie. <br/> | ||
+ | [[Média:groupe6.zip]] | ||
+ | == Infrastructure == | ||
+ | |||
+ | Le serveur est disponible sur <code>yamaha.germond.org</code> (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> <br/> | ||
+ | |||
+ | == Comment récupérer un certificat avec Let's Encrypt ? == | ||
+ | |||
+ | [0] Installer <code>certbot</code> sur la machine (<code>apt install certbot</code>). | ||
+ | |||
+ | [*] 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.<br/> | ||
+ | Dans notre cas le nom de domaine est <code>yamaha.germond.org</code>, si on lance <code>host yamaha.germond.org</code>, on peut bien observer qu'on obtient une réponse DNS et une IP routé <code>193.48.57.161</code>. | ||
+ | |||
+ | [1] On demande alors à Let's Encrypt[https://letsencrypt.org/] de nous fournir un certificat en lançant <code>certbot certonly --standalone -d yamaha.germond.org</code>.<br/> | ||
+ | Quelques questions seront demandées ainsi qu'une adresse mail et pour finir le certificat sera créé.<br/> | ||
+ | |||
+ | Le certificat ainsi que les clés seront stockés dans <code>/etc/letsencrypt/live/yamaha.germond.org/*</code>. | ||
+ | |||
+ | == Premier pas == | ||
+ | |||
+ | 03/02/2023<br/> | ||
Création VM : yamaha (IP : 193.48.57.161)<br/> | Création VM : yamaha (IP : 193.48.57.161)<br/> | ||
Lancement de la VM<br/> | Lancement de la VM<br/> | ||
Ouverture du projet beta<br/> | Ouverture du projet beta<br/> | ||
+ | Découverte SMTPin<br/> | ||
+ | |||
+ | |||
+ | == Réception message == | ||
+ | |||
+ | 10/02/2023<br/> | ||
+ | '''Réécriture du code'''<br/> | ||
+ | |||
+ | 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''' <code> root@yamaha:~/SMTP/src# vim MTAext.c </code> -> réception message ('''MTAext.c''')<br/> | ||
+ | Pour générer un identifiant unique, on utilise la librairie <libuuid> : #include <uuid/uuid.h> <br/> | ||
+ | https://stackoverflow.com/questions/51053568/generating-a-random-uuid-in-c<br/> | ||
+ | |||
+ | <pre> | ||
+ | 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 | ||
+ | </pre> | ||
+ | On définit ici un identifiant unique pour pouvoir suivre le mail envoyé (éviter toutes collisions ou perdition de données).<br/> | ||
+ | <pre> | ||
+ | if(fptr == NULL){ | ||
+ | LOG_E("Couldn't open Maildir tmp"); | ||
+ | exit(1); | ||
+ | } | ||
+ | </pre> | ||
+ | Ici, on utilise une fonction conditionnelle pour informer que l'on n'est pas parvenu à entrer dans le dossier de redirection de mail.<br/> | ||
+ | <pre> | ||
+ | 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); | ||
+ | </pre> | ||
+ | On transforme les informations stockées dans la structure "state" en format de mail avec la source, le destinataire et le corps.<br/> | ||
+ | <pre> | ||
+ | int server_process(int sfd) { | ||
+ | return smtp_process(sfd, processing_mail); | ||
+ | } | ||
+ | </pre> | ||
+ | <pre> | ||
+ | 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; | ||
+ | } | ||
+ | </pre> | ||
+ | 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.<br/> | ||
+ | |||
+ | '''Test code''' MTAext.c : vérification<br/> | ||
+ | Le mail est composé d'une entête et d'un corps de mail<br/> | ||
+ | On lance la compilation du code puis l’exécutable ./build/MTAext <br/> | ||
+ | La connexion s'établit entre deux terminaux : celui de notre machine virtuelle<br/> | ||
+ | <pre> | ||
+ | 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 | ||
+ | </pre> | ||
+ | |||
+ | Et celui de la machine PC zabeth19<br/> | ||
+ | <pre> | ||
+ | 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 | ||
+ | </pre> | ||
+ | |||
+ | -> compilation/execution de réception de mails (envoyeur, receveur, corps de mail, UUID)<br/> | ||
+ | On envoi un mail depuis la machine zabeth19<br/> | ||
+ | <pre> | ||
+ | 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. | ||
+ | </pre> | ||
+ | Et on le reçoit sur notre serveur root@yamaha<br/> | ||
+ | <pre> | ||
+ | [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. | ||
+ | </pre> | ||
+ | |||
+ | Avec la commande <mutt>, on peut vérifier que le mail est bien parvenu jusqu'à notre serveur : <br/> | ||
+ | <pre> | ||
+ | 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) | ||
+ | </pre> | ||
+ | |||
+ | == Envoi message == | ||
+ | |||
+ | '''Création code''' <code> root@yamaha:~/SMTP/src# vim MTAext.c </code>-> envoi message ('''MTAint.c''')<br/> | ||
+ | |||
+ | Pour pouvoir utiliser les expressions régulières, on utilise la librairie <regex> : #include <regex.h> <br/> | ||
+ | <pre> | ||
+ | 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]; | ||
+ | </pre> | ||
+ | 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.<br/> | ||
+ | La forme <code>char *regexString = "(.+)@(.+)";</code> 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 : @(.+).<br/> | ||
+ | Il y a donc 1 match pour 3 groupes (l'ensemble match, l'utilisateur, le destinataire).<br/> | ||
+ | <pre> | ||
+ | if(regcomp(®exCompiled, regexString, REG_EXTENDED)){ | ||
+ | LOG_D("Fail to compile regular expression."); | ||
+ | return -1; | ||
+ | } | ||
+ | |||
+ | if regexec(®exCompiled, 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 | ||
+ | } | ||
+ | </pre> | ||
+ | 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).<br/> | ||
+ | <pre> | ||
+ | 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); | ||
+ | </pre> | ||
+ | On utilise la fonction memcpy<br/> | ||
+ | <pre> | ||
+ | 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; | ||
+ | }; | ||
+ | </pre> | ||
− | == | + | == Évaluation == |
− | + | Réception <br/> | |
− | + | 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 <br/> | ||
+ | Dans le programme smtp.h : Changement du 250 Welcome en 220 Welcome (afin de pouvoir établir la communication pour recevoir le mail). <br/> | ||
+ | Dans le programme DATA.c : Changement du 354 : <pre> fprintf(state->sock, "354 Mail\r\n"); </pre> | ||
+ | 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. |
Version actuelle datée du 14 juin 2023 à 18:53
Sommaire
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]
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(®exCompiled, regexString, REG_EXTENDED)){ LOG_D("Fail to compile regular expression."); return -1; } if regexec(®exCompiled, 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).
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.