Skip to content

Zabbix SMTP server availability monitoring

Check

echo "QUIT" | nc mx.domain.com 25

on some mail servers (Exim) returns "554 SMTP synchronization error"

The reason is that Exim does not accept input sent before it is ready, i.e. at a moment when it should not have been sent. Therefore, checking the SMTP server via NETCAT does not work.

Zabbix Template: zabbix_template_smtp_servers.xml.7z

Comment: 2016-06-03 - the nodata() function in the trigger is completely useless, because its value for a network data item in "Not supported" status = UNKNOWN, and the trigger does not fire.

Zabbix agent configuration:

UserParameter=smtpcheck[*],/etc/zabbix/zabbix-check-smtp.sh $1

Script 1 (deprecated): /etc/zabbix/zabbix-check-smtp.sh

#!/bin/sh
# default - check error
result=0
if [ $# -lt 1 ]
then {}
else {
# Extract server name from email address
domain=$1
# Determine MX domain
mxserver=$( dig $domain MX |\
egrep 'MX[[:space:]]+[0-9]+[[:space:]]+[^[:space:]]+\.' |\
sort -n -k5,5 |\
head -1 | awk '{print $6}' | sed 's/\.$//' )
#
# echo 'mxserver='$mxserver
# MX found
if [ "$mxserver" != "" ]
then {
# Attempt SMTP dialogue
smtpconnect=`/usr/bin/php -f /etc/zabbix/check-smtp-server.php $mxserver 25`
# form result
if [ $smtpconnect = "1" ]
then
{
result=1
}
fi
}
fi # MX server for domain found
}
fi # number of parameters
echo $result

Script 2 : /etc/zabbix/zabbix-check-smtp.sh Advantages: checks all existing MX records, saves available MX record to /var/cache/zabbix Don't forget to create and set permissions on /var/cache/zabbix!

#!/bin/sh
#
CACHE=/var/cache/zabbix

checksmtp()
{
 echo `/usr/bin/php -f /etc/zabbix/check-smtp-server.php $1 25`
}

# Domain name set
domain=$1
ismx=$CACHE/ismx-$domain.list
goodmx=$CACHE/goodmx-$domain

# if there is NO file with good MX name list
if [ ! -f $goodmx ]
then
# Get all mx-record from DNS server to file
dig $domain MX |\
egrep 'MX[[:space:]]+[0-9]+[[:space:]]+[^[:space:]]+\.' |\
sort -n -k5,5 | awk '{print $6}' | sed 's/\.$//' > $ismx

# check all MX one by one
index=0
while read line; do
    mxserver=$line
    smtpconnect=$(checksmtp $mxserver)
    if [ $smtpconnect = "1" ]
    then
        rm $ismx
        echo $mxserver >> $goodmx
        # EXIT
        echo 1
        exit
    fi
done < $ismx

# If we are here that means THERE ARE NO answer from ALL MX-records
rm $ismx
echo 0
exit

# WE HAVE STORED FILE WITH GOOD MX RECORD
#-----------------------------------------
else
mxserver=$(head -1 $goodmx)
if [ "$mxserver" != "" ]
then
    smtpconnect=$(checksmtp $mxserver)
    if [ $smtpconnect = "1" ]
    then
        echo 1
        exit
    else
        rm $goodmx
        echo 0
        exit
    fi
fi
fi

PHP script /etc/zabbix/check-smtp-server.php:

Important: The timeout parameter in fsocketopen and stream_set_timeout functions must correlate with the TimeOut parameter value in the zabbix-agent configuration.

<?php

error_reporting(0);

function server_parse($socket, $response, $line = __LINE__) {
    $server_response ='';

    while (substr($server_response, 3, 1) != ' ') {
        //
        // TimeOut !!!
        stream_set_timeout($socket, 20);

        if (!($server_response = fgets($socket, 256))) {
                   return false;
        }
    }
    if (!(substr($server_response, 0, 3) == $response)) {
        return false;
    }
    return true;
}

// 
// TimeOut = 3 seconds
// Otherwise we get a long wait, and the data item does not receive values by timeout.
function checkSMTPServer( $smtp_host, $smtp_port ) {
    if ( !$socket = fsockopen( $smtp_host, $smtp_port, $errno, $errstr, 3) ) {
        return 0;
    }

    if (!server_parse($socket, "220", __LINE__)) {
        return 0;
    }

    fputs($socket, "QUIT\r\n");
    fclose($socket);
    return 1;
}


if ($argc != 3) {
    echo 'Command line: /usr/bin/php check-smtp-server.php mx.mydomain.com 25'."\n";
}
else {
    echo checkSMTPServer( $argv[1], $argv[2])."\n";
}