SM2TP - Librairie Sisimai

Je découvre par pur hasard la librairie Perl « Sisimai » qui permet de parser les bounces : https://github.com/sisimai/p5-sisimai

Ça reconnaît pas mal de cas de figures, comme décrit ici : https://libsisimai.org/en/reason/

Et pas mal de providers, notamment exemple ici pour Laposte.net/Orange.fr : https://github.com/sisimai/p5-sisimai/blob/5-stable/lib/Sisimai/Rhost/FrancePTT.pm

Ça sort du JSON, donc possible de l’exécuter sur un message et récupérer le JSON pour un autre outil dans un autre langage.

3 « J'aime »

C’est intéressant. J’ai réfléchi un peu à comment cela pourrait s’intégrer.

Si on prend le cas d’un mail amenant à un bounce :

  • Le serveur émetteur/relai se connecte au serveur destinataire et transmet le mail
  • Le serveur destinataire renvoie un hard-bounce (5xx) ou un soft-bounce (4xx)
    • Hard-bounce
      • Le serveur mail loggue le code et le message
      • Le serveur mail génère immédiatement un mail de rejet vers l’émetteur
      • Dans le cas de postfix, on peut configurer le serveur pour générer une notification supplémentaire vers une autre adresse (pour supervision)
      • On peut donc exécuter le script sur le mail généré.
    • Soft-bounce
      • Le serveur mail loggue le code et le message
      • Le serveur mail garde le mail en queue mais ne génère pas de mail de rejet

Donc, à priori, si on veut pouvoir traiter les hard-bounces et les soft-bounce, je pense qu’il faudra exécuter le script sur les logs.

Je peux tester la lib et voir si ça peut se faire, auquel cas on pourrait imaginer un service indépendant développé en perl qui parse les logs et pousse les bounces dans une db.

Je me suis permis de splitter dans un nouveau sujet parce que le forum voulait pas que je réponde, il trouvait que je monopolisais la discussion. :stuck_out_tongue:

J’ai regardé un peu plus comment Sisimai fonctionne.

  • Le code est de bonne qualité. La librairie est bien organisée et modulaire.
  • Comme je le craignais, il travaille sur les mails de bounce, avec les problèmes que j’évoquais plus haut, c’est à dire qu’il faut attendre que le bounce génère l’envoi d’un mail pour qu’il soit traité.
  • Si on l’appelle via son API publique, il faut lui donner un mail, un mailbox ou un maildir. Il va ensuite parser le mail pour extraire les infos utiles et les passe dans les modules d’analyse « Rhost ».
  • Les modules « Rhost » sont ceux qui nous intéressent, ils lisent les codes de réponse SMTP et retournent la raison du soft ou hard bounce (boite pleine, adresse inexistante, etc). Les fournisseurs suivant sont ceux qui sont implémentés : p5-sisimai/lib/Sisimai/Rhost at e90952bc7ce0054c9729285df6c985d4bcb4211b · sisimai/p5-sisimai · GitHub

Sur la base de ce constat, j’ai fait un script pour passer nos logs postfix au travers de leur module Rhost. Mon script parse un log postfix pour extraire les infos nécessaires aux modules Rhost de Sisimai et lui passe les données. Je l’ai poussé sur le dépôt SM2TP sur une branche : experimental/parse-mail-logs-rhost.pl · test-sisimai · CHATONS / sm2tp · GitLab

Je ne peux pas partager les résultats bruts parce que cela contient des données personnelles de nos adhérents, mais voici les statistiques :

Summary:
- bounced-authfailure: 12
- bounced-blocked: 3
- bounced-hostnotfound: 9
- bounced-mailboxfull: 1
- bounced-smtputf8required: 2
- bounced-suspend: 2
- bounced-undecided: 14
- bounced-undefined: 1
- bounced-userunknown: 41
- deferred-blocked: 44
- deferred-hostnotfound: 55
- deferred-lostconnection: 464
- deferred-mailboxfull: 140
- deferred-noroutetohost: 44
- deferred-timedout: 422
- deferred-undecided: 327
- sent: 4619
- total: 6200

On voit donc qu’il y a 14 bounced-undecided et 327 deferred-undecided (pour lesquels la lib n’a pas de verdict).

Pour moi, on a les options suivantes si on veut utiliser cette lib :

  • Accepter de travailler sur les mails de bounce, avec les inconvénients que ça cause
  • Comme je l’ai fait, utiliser la lib de manière détournée en lui passant des données déjà parsées au module spécifique
  • Utiliser le code de la lib comme d’une base de connaissance des codes de retour pour réimplémenter les mêmes traitements dans notre soft (dans le langage qu’on aura choisi, parce que je pense que je ne suis pas le seul à ne pas être coutumier de Perl)

@kepon Je crois que tu disais que omailgw parsait les logs postfix. Est-ce que tu as un traitement similaire dedans ?

Non pas vraiment. j’ai même pas vraiment différencier les hard/soft bounce… :blush:

Mais ça serait pas trop méchant à implémenté.

Moi j’ai juste une action de blacklist quand j’ai un UserUnknown : SM2TP : GT mutualisation SMTP - #10 par kepon

Je suis pas fort en Perl, mais je dirais bien qu’il doit être possible de rajouter une interface à la lib pour parser directement le message du SMTP sans passer par le message de bounce, qui ne fait que contenir le message du SMTP au final… Ça serait plus simple. Sinon oui reprendre les quelques lignes de code d’erreur dans un autre soft ne me semble pas très complexe.

Ce n’est pas nécessaire. Les interfaces des modules internes sont accessibles, il n’y a pas de notion de visibilité. Si tu regardes mon code, tu verras que j’importe le module Sisimai::Rhost pour l’utiliser directement et bypasser les autres traitements. Et la lib est bien découpée.

Je voulais juste dire que c’est un usage pour lequel la lib n’est pas faite à l’origine donc on n’a pas de garantie que cette interface ne bougera pas. Je voulais être transparent là dessus.

Alors, compliqué non, mais fastidieux probablement. Si tu vas voir les classes dans le dossier Rhost tu verras qu’il traite beaucoup de cas.

Ah oui pardon j’avais mal compris. Mais ça a l’air assez bien fichu comme lib donc je pense pas que ça pose trop de souci.

Je ne sais pas si on a besoin d’un tel niveau de précision non plus.

J’ai du mal à comprendre la logique de la librairie qui est dispo en Perl et en Ruby, avec toutes ces chaînes de caractère en dur, pourquoi ne pas mettre ces chaînes de caractère dans un fichier texte externe, genre JSON, qui serait lu par la lib, ainsi l’implémentation est séparée des données, qui là doivent être maintenues dans deux repos / projets différents… J’ai commencé à regarder pour extraire tout ça en JSON : Export Sisimai reasons to JSON · GitHub

J’extraie bien les reasons, et les messages génériques, idem pour les providers (rhost), c’est assez facile à porter en PHP (surtout du copier / coller), mais pour tout ce qui est lhost c’est plus chaud…

Ah ça je serais bien incapable de te dire pourquoi il n’a pas fait ça, je suis bien d’accord avec toi. :wink:

Lhost on n’en a pas besoin. Lhost c’est le serveur local, donc on n’a pas besoin des codes de raison, ou plutôt on sait les définir.

Mais ton message m’interpelle, je n’avais pas fait attention au module Reason, qui pour le coup ne dépend pas du provider et est beaucoup plus simple à utiliser (il est d’ailleurs exposé dans l’API officielle).

Je viens de refaire des tests dans les trois configurations :

Voici le résultat :

Rhost seul
----------

Summary:
- bounced-authfailure: 12
- bounced-blocked: 3
- bounced-hostnotfound: 9
- bounced-mailboxfull: 1
- bounced-smtputf8required: 2
- bounced-suspend: 2
- bounced-undecided: 14
- bounced-undefined: 1
- bounced-userunknown: 41
- deferred-blocked: 44
- deferred-hostnotfound: 55
- deferred-lostconnection: 464
- deferred-mailboxfull: 140
- deferred-noroutetohost: 44
- deferred-timedout: 422
- deferred-undecided: 327
- sent: 4619
- total: 6200

Reason seul
-----------

Summary:
- bounced-authfailure: 12
- bounced-blocked: 5
- bounced-filtered: 1
- bounced-hostnotfound: 9
- bounced-mailboxfull: 1
- bounced-securityerror: 2
- bounced-smtputf8required: 2
- bounced-suspend: 2
- bounced-userunknown: 51
- deferred-blocked: 50
- deferred-hostnotfound: 55
- deferred-lostconnection: 464
- deferred-mailboxfull: 140
- deferred-norelaying: 153
- deferred-noroutetohost: 44
- deferred-systemerror: 5
- deferred-timedout: 422
- deferred-undefined: 2
- deferred-userunknown: 161
- sent: 4619
- total: 6200

Rhost + reason
--------------

Summary:
- bounced-authfailure: 12
- bounced-blocked: 5
- bounced-hostnotfound: 9
- bounced-mailboxfull: 1
- bounced-securityerror: 2
- bounced-smtputf8required: 2
- bounced-suspend: 2
- bounced-undefined: 1
- bounced-userunknown: 51
- deferred-blocked: 50
- deferred-hostnotfound: 55
- deferred-lostconnection: 464
- deferred-mailboxfull: 140
- deferred-norelaying: 153
- deferred-noroutetohost: 44
- deferred-systemerror: 5
- deferred-timedout: 422
- deferred-undefined: 2
- deferred-userunknown: 161
- sent: 4619
- total: 6200

J’ai l’impression que Reason se débrouille très bien sans avoir besoin de différencier le remote host. A voir si on pense que ça nous suffit.

Intéressant merci. J’ai réuni les messages de rejet des 30 derniers jours de mon côté mais ça ne fait que 400 messages de rejet, pas très indicatif.

Lhost: j’ai pas compris la même chose, j’ai compris que ça détecte de quel type de serveur de mail vient le bounce ? https://libsisimai.org/en/engine/

Pour être clair :

  • Lhost détecte le MTA local et ses codes de diagnostique
  • Rhost détecte le MTA distant et ses codes de diagnostique

Typiquement, j’envoie un mail de MTA A (disons Postfix) vers MTA B (disons Google), qui va produire un bounce. Le MTA A va générer le mail de bounce à partir de l’erreur retournée par le MTA B.

Dans ce cas :

  • MTA A qui a généré le mail de bounce sera détecté par le module Lhost
  • MTA B qui a généré l’erreur et le message de bounce sera détecté par le module Rhost

Le module Lhost n’a de sens que si l’on traite un mail de bounce généré par le serveur. Si on ne traite que la reason, ce module n’est pas utile pour nous.

Oui OK j’ai compris :slight_smile:

Après avoir remonté une typo dans sisimai (Perl) le dév m’apprend qu’une version Go est en cours de développement… Une de plus. Du coup j’ai ouvert une issue pour évoquer la question de pouvoir séparer les données du code au maximum: https://github.com/sisimai/go-sisimai/issues/13

Ne serait-ce que par curiosité pour savoir pourquoi ce choix n’a pas été fait, y’a ptet une raison qui m’échappe.

En tout cas le fait qu’une version Go sorte nous ouvre éventuellement des possibilités pour intégrer ça dans un « SM2TP » en Go :slight_smile:

1 « J'aime »