newspaint

Documenting Problems That Were Difficult To Find The Answer To

Exim: Helo Command Rejected Need Fully-Qualified Hostname

I’ve been running my Exim configuration for years when received a mail delivery failure message with the error:

504 5.5.2 <myhost>: Helo command rejected: need fully-qualified hostname

I’d not come across this issue before and my Exim set-up had been happily introducing itself to other mail servers as myhost which was the unqualified hostname of the virtual machine Exim was running on.

So – what to do?

The helo_data command, when added to a transport configuration, could be set to whatever string you want sent by Exim with the HELO message during the SMTP handshake phase of a connection.

But rather than just simply give a fixed string – I wanted my configuration to be a little more clever – and determine the MX host from the sent e-mail’s from domain.

To do this the ${lookup dnsdb} function was used.

The configuration setting I use is as follows, which I’ll break down and explain afterwards:

helo_data = ${extract{2}{,}{${extract{1}{:}{${sort {${lookup dnsdb{>:,,mx=$sender_address_domain}}} {<} {${extract{1}{,}{$item}}}}}}}{$value}{unknown}}

If I expand this out and add comments it may aid in understanding, I’ll start from the centre and work my way out.

First the sender’s domain lookup:

# Do a DNS lookup of the MX (mail exchange) records of the sender's domain
#
# EXAMPLE OUTPUT (for "gmail.com"):
#   40,alt3.aspmx.l.google.com:20,alt1.aspmx.l.google.com:30,alt2.aspmx.l.google.com:50,alt4.aspmx.l.google.com:10,aspmx.l.google.com

${lookup dnsdb{>:,,mx=$sender_address_domain}}

# NOTES
#   records are not given in any particular order, and are colon-separated

Obviously having a list of MX hosts is too much, we just want one, and we want the highest priority host (with the smallest number). So we have to sort the list using the ${sort} function:

# Sort the list of MX records in order of priority from smallest to largest
#
# EXAMPLE OUTPUT (for "gmail.com"):
#   10,aspmx.l.google.com:20,alt1.aspmx.l.google.com:30,alt2.aspmx.l.google.com:40,alt3.aspmx.l.google.com:50,alt4.aspmx.l.google.com

${sort
  { ${lookup dnsdb {>:,,mx=$domain} } }  # text to sort
  { < }                                  # comparison operator
  { ${extract {1} {,} {$item} } }        # fn to run on each item
}

# NOTES
#   each item to be sorted must have the number extracted

Okay now we have a sorted list. We just want the first entry in the list and to do this we use the ${extract} function.

# Sort the list of MX records in order of priority from smallest to largest
# and select the first record in the list
#
# EXAMPLE OUTPUT (for "gmail.com"):
#   10,aspmx.l.google.com

${extract
  {1}                                       # select first field
  {:}                                       # separator is colon
  {                                         # text to extract from
    ${sort
      { ${lookup dnsdb {>:,,mx=$domain} } }
      {<}
      { ${extract {1} {,} {$item} } }
    }
  }
}

Finally we just want the hostname which is the second field in a comma-separated list of that string.

We can provide a default value in the event nothing is extracted. Which leads us to our final formula:

# Sort the list of MX records in order of priority from smallest to largest
# and select the first record in the list, then select the second column
# which is the hostname, otherwise return "unknown"
#
# EXAMPLE OUTPUT (for "gmail.com"):
#   aspmx.l.google.com

${extract
  {2}                                           # select 2nd field
  {,}                                           # separator is comma
  {                                             # text to extract from
    ${extract
      {1}
      {:}
      {
        ${sort
          { ${lookup dnsdb {>:,,mx=$domain} } }
          {<}
          { ${extract {1} {,} {$item} } }
        }
      }
    }
  }
  {$value}                                      # result on success
  {unknown}                                     # result on failure
}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: