require 'custodian/settings' require 'custodian/testfactory' # # 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] 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 = [] # # 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 Custodian::TestResult::TEST_FAILED 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 Custodian::TestResult::TEST_FAILED 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 Custodian::TestResult::TEST_FAILED else return Custodian::TestResult::TEST_PASSED end end # # If the test fails then report the error. # def error @error end register_test_type 'mx' end end end