Denying bad DNS HELO/EHLOs

February 26, 2022 Roberto Puzzanghera 0 comments

The RFC-821 Section 3.5 states that 

The sender-SMTP MUST ensure that the <domain> parameter in a HELO command is a valid principal host domain name for the client host. As a result, the receiver-SMTP will not have to perform MX resolution on this name in order to validate the HELO parameter.

The HELO receiver MAY verify that the HELO parameter really corresponds to the IP address of the sender. However, the receiver MUST NOT refuse to accept a message, even if the sender's HELO command fails verification.

Not denying clients with a bad HELO/EHLO DNS can be also considered a wise thing, just not to update too frequently our whitelist for those clients who don't set up their DNS properly.

On the other hand, it is a matter of fact that most spammers use fake domains -very often our own domains-, or even random strings or not solving domains, as their HELO/EHLOs.

For example, consider the following log lines (I have plenty of them in my logs):

2022-02-01 10:19:53.142643500 helo-dns-check: HELO [yq3H9cDKgS] from [] doesn't solve
2022-02-01 09:53:05.772497500 helo-dns-check: HELO [] is a local domain but IP [] is not a RELAYCLIENT

I think that at least such kind of failures should be blocked. I'll explain below how to set up a filter which deny clients with these particular DNS failures:

  1. not solving HELO/EHLOs, i.e. random strings or fake domains with no A record at all.
  2. fake HELO/EHLOs containing one of our domains, when the DNS doesn't solve to one of our IPs and RELAYCLIENT is not defined;
  3. clients whose A record doesn't match the domain in their HELO/EHLO. This is completely against RFC-821, so my configuration will not refuse these connections, just log them.

We'll make use of a qmail-spp "helodnscheck" plugin that I derived from an original work of Perolo Silantico, Jason Frisvold and Ren Bing. Here is the original plugin and my one:

The logic of the original plugin is to deny clients of type 3, which of course includes types 1 and 2, but without being able to select 1 and/or 2 from the others. My modified version, instead, can ban only clients of type 1 and/or 2 or work as the original program.

Installation and configuration

I assume that you have already patched qmail with qmail-spp. If you are using my combined patch you are ok.

Download, compile and install:

cd /usr/local/src
gcc -o /var/qmail/plugins/helodnscheck helodnscheck7.c -lresolv

Now enable the plugin, adding it to /var/qmail/control/smtpplugins in the [helo] section:


List all your IPs inside the file control/moreipme (you should have already done this if you configured the "moreipme" patch):

Then enable qmail-spp and set up the plugin parameters to your needs. I suggest the following in your qmail-smtpd run file:

export ENABLE_SPP=1 

In this way only bad HELOs of type 1 (I) and 2 (V) will be denied unless RELAYCLIENT is defined (R). All other DNS failures will pass through (P) and each of them will be logged (L).

Be aware that the HELO check can't work well on the submission port, where your IP cannot match the HELO, so you don't have to define HELO_DNS_CHECK in your qmail-submission run file.

Of course you can define HELO_DNS_CHECK via tcprules or whitelist a particular IP via NOHELODNSCHECK as follows:

111.222.333.444:allow, NOHELODNSCHECK=""

The program's behaviour is defined in the HELO_DNS_CHECK variable:

  • [default] - deny if HELO doesn't solve to a record
  • P - passthrough, don't deny even when HELO doesn't solve to A record (of course, use with L and/or H)
  • B - Block if TCPREMOTEIP is not contained in the solved addresses
  • L - Log
  • H - add Header "X-Helo-Check"
  • R - if "RELAYCLIENT" is set, don't do anything
  • D - Debug mode (use with L)
  • V - Block if "RELAYCLIENT" is NOT set and the HELO is one of our IPs contained in control/moreipme. "localhost" will be denied as well. You don't want to use it together with B.
  • I - Invalid hostname in HELO/EHLO (not solving) are denied. Using this one together with B is redundant.

The above can be combined, so BL means block & log if TCPREMOTEIP is not set.

Note: If there is no HELO/EHLO argument, it defaults to a permanent block.

Add a comment