ietf-smtp
[Top] [All Lists]

Re: [ietf-smtp] SMTP Greylisting Retry Hints

2019-02-05 12:00:07
On 2/5/2019 11:31 AM, Paul Smith wrote:
On 05/02/2019 16:11, Hector Santos wrote:
Which smtp servers and client follow or honor Greylist SMTP retry
hint for rescheduling retries?

Well, I thought our server indicated the retry time, but it appears
that we were using a format from a previous discussion about it, not
the proper draft, so we'll fix the return format to match the current
draft.

The client doesn't use the hint though.

(To be honest, I thought the idea had disappeared, so we returned the
retry time because that was a trivial change, but actually using the
hint is a bit more complicated so we were waiting to see if it would
be useful).

As far as minimizing the impact of greylisting delays, it has proven to be very useful over the years.

Server side:

The default has been set to 55 seconds. My client will honor it and works as expected among similar setups. Many other clients will try immediate within the 55 secs and of course, they are blocked. Yesterday, I was exploring signing up with InstaCart and I was waiting for their (confirmation/acknowledgement) email. It got in at the 3rd attempts, 40, then 90 seconds apart. Hence why I posted this to see if modern services are learning from the greylisting delays.

Client Side:

Yes, you have to adjust your client retry queuing/timing/schedule logic. The SMTP client code would check for any possible server 4yz "retry hints" and extracts/returns the recommended time/delta. The optional delta is then passed your scheduler and used to adjust/set the next retry. It does work very work to minimize the retries to 2 with the minimum time.

If it helps, I have this C code (port as required for *nix) with the functions used to extract the time from a response string checking the learned common strings seen:

// smtpgrey-example.cpp

#include <stdio.h>
#include <windows.h>

//-----------------------------------------------------------
// int GetRetrySecs(const char *psz)
//
// return # of seconds from formats
//
//  [DD-]HH:MM:SS  formal syntax
//  # seconds
//  # minutes
//  # hours
//
//-----------------------------------------------------------

int GetRetrySecs(const char *psz)
{
    char *pOut = NULL;
    int days = 0;
    int secs = strtol(psz,&pOut,10);
    // check for formal syntax [DD-]HH:MM:SS
    if (*pOut == '-') {
       days = secs;
       secs = strtol(pOut+1,&pOut,10);
    }
    if (*pOut == ':') {
       secs = days*24*3600+secs*3600+strtol(pOut+1,&pOut,10)*60;
       if (*pOut == ':') secs += atoi(pOut+1);
       return secs;
    }
    // check for informal syntax
    if (secs) {
       if (*pOut == ' ') pOut++;
       switch (*pOut) {
         case 's': break;
         case 'm': secs=secs*60; break;
         case 'h': secs=secs*3600; break;
       }
    }
    return secs;
}

//-----------------------------------------------------------
// int ExtractRetryHint(const char *psz)
//
// For given SMTP 4yz response, return any known retry time
// hint in seconds. Return 0 for no time hint or non 4yz
// response.  The routine checks for the formal syntax and
// for non-formal existing syntax:
//
//  retry=[DD-]HH:MM:SS                        proposed draft std
//  greylisted for # seconds|minutes|hours     common, non-std
//  try again in # seconds|minutes|hours       common, non-std
//  please come back in HH:MM::SS              common, non-std
//-----------------------------------------------------------

int ExtractRetryHint(const char *psz)
{
    int nsecs = 0;
    if (!psz || !psz[0] || ((atoi(psz)/100*100) != 400)) {
        return nsecs;
    }
    const char *tags[] = {
        "retry=",
        "greylisted for ",
        "try again in ",
        "please come back in ",
        NULL};
    const char **p = tags;
    char *lpsz = _strlwr(_strdup(psz));
    while (*p) {
      char *pos = strstr(lpsz,*p);
      if (pos) {
         nsecs = GetRetrySecs(pos+strlen(*p));
         break;
      }
      p++;
    }
    free(lpsz);
    return nsecs;
}

void test(const char *sz)
{
    int secs = ExtractRetryHint(sz);
    printf("secs: %5d | %s\n",secs, sz);
}

void main(char argc, char *argv[])
{
test("421 This server implements greylisting, please try again in 90 seconds"); test("450 4.7.1 <RCPT>: Recipient address rejected: Greylisted for 5 minutes"); test("450 4.7.1 <RCPT>: Recipient address rejected: Greylisted for 120 seconds"); test("451 4.7.1 Greylisting in action, please come back in 00:30:00"); test("451 4.7.1 Greylisting in action, PLEASE COME BACK in 01:05:20");
    test("451 Greylisted for 55 seconds");
    test("451 Greylisted, please try again in 167 seconds");
    test("451 Greylisting enabled, try again in 22 minutes");
    test("451 Greylisting. Try again later.");
    test("451 Greylisting. Try again later. retry=5 mins");
    test("471 Greylisting. Try again later. retry=300 secs");
    test("471 Greylisting. Try again later. retry=2 hours");
    test("471 Greylisting. Try again later. retry=2h");
    test("471 Greylisting. Try again later. retry=120");
    test("471 Greylisting. Try again later. retry=01:01:02");
    test("471 retry=01:01:02 Greylisting. Try again later.");
    test("471 Greylist: RETRY=1M Try again later.");
    test("550 Permanent Reject");
}

--
HLS


_______________________________________________
ietf-smtp mailing list
ietf-smtp(_at_)ietf(_dot_)org
https://www.ietf.org/mailman/listinfo/ietf-smtp