#!/usr/bin/ruby1.8


require 'resolv-replace'


#
#  This is a protocol tester for DNS.
#
class DNSTest

  #
  # Data passed from the JSON hash.
  #
  attr_reader :test_data

  #
  # The error text we return on failure.
  #
  attr_reader :error



  #
  # Save the data away.
  #
  def initialize( data )
    @test_data = data
    @error     = nil

    #
    # Ensure we have a host to monitor
    #
    if ( @test_data["target_host"].nil? )
      @error = "Missing host to test against."
      raise ArgumentError, @error
    end

    #
    #  Ensure we have test-specific keys.
    #
    if ( @test_data["resolve_name"].nil? )
      @error = "Missing host to resolve."
      raise ArgumentError, @error
    end
    if ( @test_data["resolve_type"].nil? )
      @error = "Missing type to resolve, for example A/MX/NS."
      raise ArgumentError, @error
    end
    if ( @test_data["resolve_expected"].nil? )
      @error = "Missing expected results to compare against.."
      raise ArgumentError, @error
    end
  end


  #
  # Run the test.
  #
  #  Return "true" on success
  #
  #  Return "false" on failure.
  #
  # If the test fails the details should be retrieved from "error()".
  #
  def run_test

    #
    # Reset state from previous test.
    #
    @error = nil

    #
    # Get the nameserver to resolve with
    #
    nameserver = @test_data["target_host"]

    #
    # Get the record type to lookup
    #
    record = @test_data["resolve_type"]

    #
    # Get the hostname to lookup
    #
    lookup = @test_data["resolve_name"]

    #
    # Get the expected results
    #
    expected = @test_data["resolve_expected"]

    #
    # Do the lookup
    #
    results = resolve_via( nameserver, record, lookup )

    #
    # Show the results if we found something.
    #
    if ( @test_data['verbose'] )
      results.each do |res|
        puts "Result: #{res}"
      end
    end

    #
    # OK we have an array of results.  If every one of the expected
    # results is contained in those results returned then we're good.
    #
    expected.split( /;/ ).each do |required|
      if ( ! results.include?( required ) )
        @error = "The expected result #{required} was not found in the results: #{results.join(",")}"
        return false
      end
    end

    return true
  end


  #
  # Resolve an IP
  #
  def resolve_via( server, ltype, name )
    puts "Resolving #{name} [#{ltype}] via server #{server}"

    results = Array.new()

    Resolv::DNS.open(:nameserver=>[server]) do |dns|
      case ltype
      when /^A$/ then
        dns.getresources(name, Resolv::DNS::Resource::IN::A).map{ |r| results.push r.address.to_s() }
      when /^AAAA$/ then
        dns.getresources(name, Resolv::DNS::Resource::IN::AAAA).map{ |r| results.push r.address.to_s() }

      when /^NS$/ then
        dns.getresources(name, Resolv::DNS::Resource::IN::MX).map{ |r| results.pushResolv.getaddresses(r.name.to_s()) }

      when /^MX$/ then
        dns.getresources(name, Resolv::DNS::Resource::IN::MX).map{ |r| results.push Resolv.getaddresses(r.exchange.to_s()) }
      end
    end

    results.flatten!
    return results
  end


  #
  #  Return the error text for why this test failed.
  #
  def error
    return @error
  end




end



#
# Sample test, for testing.
#
if __FILE__ == $0 then

  #
  #  Sample data.
  #
  test = {
    "target_host"      => "a.ns.bytemark.co.uk",
    "test_type"        => "dns",
    "verbose"          => 1,
    "test_alert"       => "DNS failure",
    "resolve_name"     => "support.bytemark.co.uk",
    "resolve_type"     => "MX",
    "resolve_expected" => "89.16.184.148;89.16.184.149;89.16.184.150"
  }


  #
  #  Run the test.
  #
  obj = DNSTest.new( test )
  if ( obj.run_test )
    puts "TEST OK"
  else
    puts "TEST FAILED"
    puts obj.error()
  end

end