require 'custodian/protocoltest/tcp'
#
# The MX (DNS + smtp) test.
#
# This object is instantiated if the parser sees a line such as:
#
###
### bytemark.co.uk must run mx otherwise 'mail fail'.
###
#
#
module Custodian
module ProtocolTest
class MXTest < TestFactory
#
# Constructor
#
def initialize( line )
# Save the line away
@line = line
# The main domain we're querying
@host = line.split(/\s+/)[0]
if ( line =~ /must\s+not\s+run\s+/ )
@inverted = true
else
@inverted = false
end
end
#
# Allow this test to be serialized.
#
def to_s
@line
end
#
# Run the test.
#
def run_test
# reset the error, in case we were previously executed.
@error = nil
#
# Get the timeout period.
#
settings = Custodian::Settings.instance()
period = settings.timeout()
#
# The MX-hosts
#
mx = Array.new()
#
# Lookup the MX record
#
begin
timeout( period ) do
Resolv::DNS.open do |dns|
ress = dns.getresources(@host, Resolv::DNS::Resource::IN::MX)
ress.map { |r| mx.push( IPSocket::getaddress(r.exchange.to_s) ) }
end
end
rescue Timeout::Error => e
@error = "Timed-out performing DNS lookups: #{e}"
return nil
end
#
# At this point we should have an array of IPv4 or IPv6 addresses.
#
# If that array is empty then there will be no incoming mail because
# there are now working MX records in DNS - or because the domain
# has expired, etc.
#
# So on that basis we must alert.
#
if ( mx.empty? ) then
@error = "Failed to perform DNS lookup of MX record(s) for host #{@host}"
return false
end
#
# For each host we must make a connection.
#
# We'll keep count of failures.
#
failed = 0
passed = 0
error = ""
mx.each do |backend|
begin
timeout(period) do
begin
socket = TCPSocket.new( backend, 25 )
read = socket.sysread(1024)
# trim to a sane length & strip newlines.
if ( ! read.nil? )
read = read[0,255]
read.gsub!(/[\n\r]/, "")
end
if ( read =~ /^220/ )
passed += 1
else
failed += 1
end
rescue
# Failure to connect.
failed +=1
error += "Error connecting to #{backend}:25. "
end
end
rescue Timeout::Error => ex
# Timeout
failed +=1
error += "Timeout connecting to #{backend}:25. "
end
end
#
# At this point we should have tested the things
#
if ( failed > 0 )
@error = "There are #{mx.size} hosts running as MX-servers for domain #{@host} - #{passed}:OK #{failed}:FAILED - #{error}"
return false
else
return true;
end
end
#
# If the test fails then report the error.
#
def error
@error
end
register_test_type "mx"
end
end
end