diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/README | 17 | ||||
| -rwxr-xr-x | lib/custodian/protocol-tests/ftp.rb | 144 | ||||
| -rwxr-xr-x | lib/custodian/protocol-tests/http.rb | 187 | ||||
| -rwxr-xr-x | lib/custodian/protocol-tests/https.rb | 188 | ||||
| -rwxr-xr-x | lib/custodian/protocol-tests/jabber.rb | 146 | ||||
| -rwxr-xr-x | lib/custodian/protocol-tests/ldap.rb | 140 | ||||
| -rwxr-xr-x | lib/custodian/protocol-tests/ping.rb | 116 | ||||
| -rwxr-xr-x | lib/custodian/protocol-tests/rsync.rb | 145 | ||||
| -rwxr-xr-x | lib/custodian/protocol-tests/smtp.rb | 145 | ||||
| -rwxr-xr-x | lib/custodian/protocol-tests/ssh.rb | 144 | 
10 files changed, 1372 insertions, 0 deletions
| diff --git a/lib/README b/lib/README new file mode 100644 index 0000000..5d8b6d3 --- /dev/null +++ b/lib/README @@ -0,0 +1,17 @@ + +  This directory contains the protocol-tests. + +  For the protocol "xxx" we must have: + +        - The file called xxx.rb + +        - The definition of class "XXXTest" - note upper-case + +  The class must implement the methods: + +    run_test() + +    error() + +  'run_test' will be called to run the test, returning true if passed, and false + otherwise.  In the event of a test failure 'error' will return  something useful. diff --git a/lib/custodian/protocol-tests/ftp.rb b/lib/custodian/protocol-tests/ftp.rb new file mode 100755 index 0000000..63b6714 --- /dev/null +++ b/lib/custodian/protocol-tests/ftp.rb @@ -0,0 +1,144 @@ +#!/usr/bin/ruby  + + +require 'timeout' +require 'socket' + +# +# This class is responsible for performing tests against a remote FTP server +# +# +class FTPTest + +  # +  # 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 probe +    # +    if ( @test_data["target_host"].nil? ) +      @error = "Missing target for the test." +      raise ArgumentError, @error +    end + +    # +    # Ensure we have a port to test. +    # +    if ( @test_data["test_port"].nil? ) +      @error = "Missing port for the test." +      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 + +    # +    #  Get the hostname & port to test against. +    # +    host = @test_data["target_host"] +    port = @test_data["test_port"] + +    puts "FTP testing host #{host}:#{port}" if ( @test_data['verbose'] ) + +    begin +      timeout(3) do + +        begin +          socket = TCPSocket.new( host, port ) +          socket.puts( "QUIT") + +          banner = socket.gets(nil) +          banner = banner[0,20] + +          socket.close() + +          if ( banner =~ /^220/ ) +            return true +          else +            @error = "Banner didn't report OK: #{banner}" +          end +        rescue +          @error = "FTP exception on host #{host}:#{port} - #{$!}" +          return false +        end +      end +    rescue Timeout::Error => e +      @error = "Timed-out connecting #{e}" +      return false +    end +    @error = "Misc. failure." +    return false +  end + + +  # +  #  Return the error. +  # +  def error +    return @error +  end + +end + + + + + +# +# Sample test, for testing. +# +if __FILE__ == $0 then + +  # +  #  Sample data. +  # +  test = { +    "target_host" => "mirror.bytemark.co.uk", +    "test_type"   => "ftp", +    "test_port"   => 21, +    "verbose"     => 1, +    "test_alert"  => "The FTP server no worky", +  } + + +  # +  #  Run the test. +  # +  tst = FTPTest.new( test ) +  if ( tst.run_test ) +    puts "TEST OK" +  else +    puts "TEST FAILED" +    puts tst.error() +  end + +end diff --git a/lib/custodian/protocol-tests/http.rb b/lib/custodian/protocol-tests/http.rb new file mode 100755 index 0000000..0d4cdd3 --- /dev/null +++ b/lib/custodian/protocol-tests/http.rb @@ -0,0 +1,187 @@ +#!/usr/bin/ruby + +require 'net/http' +require 'net/https' +require 'uri' + + + +class HTTPTest + +  # +  # Data passed from the JSON hash. +  # +  attr_reader :test_data + +  # +  # The HTTP status, the HTTP response body, and the error text +  # we return on failure. +  # +  attr_reader :status, :body, :error + + + +  # +  # Save the data away. +  # +  def initialize( data ) +    @test_data = data +    @error     = nil + +    # +    # Ensure we have an URL +    # +    if ( @test_data["target_host"].nil? ) +      @error = "Missing URL for the test." +      raise ArgumentError, @error +    end + +    # +    # Ensure we have a port +    # +    if ( @test_data["test_port"].nil? ) +      @error = "Missing port for the test." +      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 + +    # +    #  Do the fetch, if this success then we'll have the +    # @status + @text setup +    # +    if ( getURL (@test_data["target_host"] ) ) + +      # +      #  Do we need to test for a HTTP status code? +      # +      if ( @test_data["http_status"] ) +        puts "Testing for HTTP status code: #{@test_data['http_status']}" if ( @test_data['verbose'] ) + +        if ( @status != @test_data['http_status'].to_i) +          @error = "#{@error} status code was #{@status} not #{@test_data['http_status']}" +        end +      end + +      # +      #  Do we need to search for text in the body of the reply? +      # +      if ( @test_data['http_text'] ) +        puts "Testing for text in the response: #{@test_data['http_text']}" if ( @test_data['verbose'] ) + +        if (! @body.match(/#{@test_data['http_text']}/i) ) +          @error = "#{@error} The respond did not contain #{test_data['http_text']}" +        end +      end + +      return true if ( @error.nil? ) + +      return false +    end + +    return false +  end + + +  # +  #  Return the error text for why this test failed. +  # +  def error +    return @error +  end + + +  # +  # Retrieve a HTTP page from the web. +  # +  # NOTE:  This came from sentinel. +  def getURL (uri_str) +    begin +      uri_str = 'http://' + uri_str unless uri_str.match(/^http/) +      url = URI.parse(uri_str) +      http = Net::HTTP.new(url.host, url.port) +      http.open_timeout = 3 +      http.read_timeout = 3 + +      if (url.scheme == "https") +        http.use_ssl = true +        http.verify_mode = OpenSSL::SSL::VERIFY_NONE +      end + +      response = nil + +      if nil == url.query +        response = http.start { http.get(url.path) } +      else +        response = http.start { http.get("#{url.path}?#{url.query}") } +      end + +      @status = response.code.to_i +      @body   =  response.body + +      return true +    rescue Errno::EHOSTUNREACH => ex +      @error = "no route to host" +      return false +    rescue Timeout::Error => ex +      @error = "time out reached" +      return false +    rescue Errno::ECONNREFUSED => ex +      @error = "Connection refused" +      return false +    rescue => ex +      raise ex +      return false +    end +    return false +  end + + + +end + + + +# +# Sample test, for testing. +# +if __FILE__ == $0 then + +  # +  #  Sample data. +  # +  test = { +    "target_host" => "http://www.steve.org.uk/", +    "test_type"   => "http", +    "verbose"     => 1, +    "test_port"   => 80, +    "test_alert"  => "Steve's website is unavailable", +    "http_text"   => "Steve Kemp", +    "http_status" => "200" +  } + + +  # +  #  Run the test. +  # +  http = HTTPTest.new( test ) +  if ( http.run_test ) +    puts "TEST OK" +  else +    puts "TEST FAILED" +    puts http.error() +  end + +end diff --git a/lib/custodian/protocol-tests/https.rb b/lib/custodian/protocol-tests/https.rb new file mode 100755 index 0000000..49cdcef --- /dev/null +++ b/lib/custodian/protocol-tests/https.rb @@ -0,0 +1,188 @@ +#!/usr/bin/ruby + +require 'net/http' +require 'net/https' +require 'uri' + + + +class HTTPSTest + +  # +  # Data passed from the JSON hash. +  # +  attr_reader :test_data + +  # +  # The HTTP status, the HTTP response body, and the error text +  # we return on failure. +  # +  attr_reader :status, :body, :error + + + +  # +  # Save the data away. +  # +  def initialize( data ) +    @test_data = data +    @error     = nil + +    # +    # Ensure we have an URL +    # +    if ( @test_data["target_host"].nil? ) +      @error = "Missing URL for the test." +      raise ArgumentError, @error +    end + +    # +    # Ensure we have a port +    # +    if ( @test_data["test_port"].nil? ) +      @error = "Missing port for the test." +      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 + +    # +    #  Do the fetch, if this success then we'll have the +    # @status + @text setup +    # +    if ( getURL (@test_data["target_host"] ) ) + +      # +      #  Do we need to test for a HTTP status code? +      # +      if ( @test_data["http_status"] ) +        puts "Testing for HTTP status code: #{@test_data['http_status']}" if ( @test_data['verbose'] ) + +        if ( @status != @test_data['http_status'].to_i) +          @error = "#{@error} status code was #{@status} not #{@test_data['http_status']}" +        end +      end + +      # +      #  Do we need to search for text in the body of the reply? +      # +      if ( @test_data['http_text'] ) +        puts "Testing for text in the response: #{@test_data['http_text']}" if ( @test_data['verbose'] ) + +        if (! @body.match(/#{@test_data['http_text']}/i) ) +          @error = "#{@error} The respond did not contain #{test_data['http_text']}" +        end +      end + +      return true if ( @error.nil? ) + +      return false +    end + +    return false +  end + + +  # +  #  Return the error text for why this test failed. +  # +  def error +    return @error +  end + + +  # +  # Retrieve a HTTP page from the web. +  # +  # NOTE:  This came from sentinel. +  def getURL (uri_str) +    begin +      uri_str = 'http://' + uri_str unless uri_str.match(/^http/) +      url = URI.parse(uri_str) +      http = Net::HTTP.new(url.host, url.port) +      http.open_timeout = 3 +      http.read_timeout = 3 + +      if (url.scheme == "https") +        http.use_ssl = true +        http.verify_mode = OpenSSL::SSL::VERIFY_NONE +      end + +      response = nil + +      if nil == url.query +        response = http.start { http.get(url.path) } +      else +        response = http.start { http.get("#{url.path}?#{url.query}") } +      end + +      @status = response.code.to_i +      @body   =  response.body + +      return true +    rescue Errno::EHOSTUNREACH => ex +      @error = "no route to host" +      return false +    rescue Timeout::Error => ex +      @error = "time out reached" +      return false +    rescue Errno::ECONNREFUSED => ex +      @error = "Connection refused" +      return false +    rescue => ex +      raise ex +      return false +    end +    return false +  end + + + +end + + + +# +# Sample test, for testing. +# +if __FILE__ == $0 then + +  # +  #  Sample data. +  # +  test = { +    "target_host" => "http://www.steve.org.uk/", +    "test_type"   => "http", +    "verbose"     => 1, +    "test_port"   => 80, +    "test_alert"  => "Steve's website is unavailable", +    "http_text"   => "Steve Kemp", +    "http_status" => "200" +  } + + +  # +  #  Run the test. +  # +  http = HTTPSTest.new( test ) +  if ( http.run_test ) +    puts "TEST OK" +  else +    puts "TEST FAILED" +    puts http.error() +  end + +end diff --git a/lib/custodian/protocol-tests/jabber.rb b/lib/custodian/protocol-tests/jabber.rb new file mode 100755 index 0000000..ab628d5 --- /dev/null +++ b/lib/custodian/protocol-tests/jabber.rb @@ -0,0 +1,146 @@ +#!/usr/bin/ruby + + + +require 'socket' +require 'timeout' + + +# +# Test that we can receive a response from a Jabber server that looks +# reasonable. +# +class JABBERTest + +  # +  # 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 probe +    # +    if ( @test_data["target_host"].nil? ) +      @error = "Missing target for the test." +      raise ArgumentError, @error +    end + +    # +    # Ensure we have a port to test. +    # +    if ( @test_data["test_port"].nil? ) +      @error = "Missing port for the test." +      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 +    @error = "" + +    # +    #  Get the hostname & port to test against. +    # +    host = @test_data['target_host'] +    port = @test_data['test_port'] + +    puts "Jabber testing host #{host}:#{port}" if ( @test_data['verbose'] ) + +    begin +      timeout(3) do + +        begin +          socket = TCPSocket.new( host, port ) +          socket.puts( "QUIT") + +          banner = socket.gets(nil) +          banner = banner[0,20] + +          socket.close() + +          if ( banner =~ /xml version/i ) +            puts "Jabber alive: #{banner}" if ( @test_data['verbose'] ) +            return true +          else +            @error = "Banner didn't seem reasonable: #{banner}" +            return false; +          end +        rescue +          @error = "Jabber exception on host #{host}:#{port} - #{$!}" +          return false +        end +      end +    rescue Timeout::Error => e +      @error = "TIMEOUT: #{e}" +      return false +    end + +    @error = "Misc failure" +    return false +  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" => "chat.bytemark.co.uk", +    "test_type"   => "jabber", +    "verbose"     => 1, +    "test_alert"  => "Chat is down?", +  } + + +  # +  #  Run the test. +  # +  obj = JABBERTest.new( test ) +  if ( obj.run_test ) +    puts "TEST OK" +  else +    puts "TEST FAILED" +    puts obj.error() +  end + +end diff --git a/lib/custodian/protocol-tests/ldap.rb b/lib/custodian/protocol-tests/ldap.rb new file mode 100755 index 0000000..e15f4e0 --- /dev/null +++ b/lib/custodian/protocol-tests/ldap.rb @@ -0,0 +1,140 @@ +#!/usr/bin/ruby + + + +require 'socket' +require 'timeout' + + +# +# Test that we can receive a response from an LDAP server. +# +class LDAPTest + +  # +  # 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 probe +    # +    if ( @test_data["target_host"].nil? ) +      @error = "Missing target for the test." +      raise ArgumentError, @error +    end + +    # +    # Ensure we have a port to test. +    # +    if ( @test_data["test_port"].nil? ) +      @error = "Missing port for the test." +      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 + +    # +    # Until the test runs we have no error. +    # +    @error = "" + +    # +    #  Get the hostname & port to test against. +    # +    host = @test_data['target_host'] +    port = @test_data['test_port'] + +    puts "LDAP testing host #{host}:#{port}" if ( @test_data['verbose'] ) + +    begin +      timeout(3) do + +        begin +          socket = TCPSocket.new( host, port ) +          socket.puts( "QUIT") +          socket.close() + +          puts "LDAP alive" if ( @test_data['verbose'] ) +          return true +        rescue +          @error = "Exception connecting to host #{host}:#{port} - #{$!}" +          return false +        end +      end +    rescue Timeout::Error => e +      @error = "TIMEOUT: #{e}" +      return false +    end + +    @error = "Misc failure" +    return false +  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" => "auth.bytemark.co.uk", +    "test_type"   => "ldap", +    "test_port"   => 389, +    "verbose"     => 1, +    "test_alert"  => "LDAP is down?", +  } + + +  # +  #  Run the test. +  # +  obj = LDAPTest.new( test ) +  if ( obj.run_test ) +    puts "TEST OK" +  else +    puts "TEST FAILED" +    puts obj.error() +  end + +end diff --git a/lib/custodian/protocol-tests/ping.rb b/lib/custodian/protocol-tests/ping.rb new file mode 100755 index 0000000..d6ac877 --- /dev/null +++ b/lib/custodian/protocol-tests/ping.rb @@ -0,0 +1,116 @@ +#!/usr/bin/ruby + + + +require 'socket' +require 'timeout' + + +# +# Test that we can receive a ping response from the remote host. +# +class PINGTest + +  # +  # 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 +  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 +    @error = "" + + +    # +    # Find the binary +    # +    binary = nil +    binary = "./util/multi-ping"  if ( File.exists?( "./util/multi-ping" ) ) +    binary = "../util/multi-ping" if ( File.exists?( "../util/multi-ping" ) ) +    binary = "../../util/multi-ping" if ( File.exists?( "../../util/multi-ping" ) ) + +    if ( binary.nil? ) +      @error = "Failed to find 'multi-ping'" +      return false +    end + + +    # +    #  Get the hostname to test against. +    # +    host = @test_data['target_host'] +    puts "ping testing host #{host}" if ( @test_data['verbose'] ) + + +    if ( system( "#{binary} #{host}" ) == true ) +      puts "PING OK" if ( @test_data['verbose'] ) +      return  true +    else +      @error = "Ping failed.  TODO: Mtr" +      return false +    end + +  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" => "upload.ns.bytemark.co.uk", +    "test_type"   => "ping", +    "verbose"     => 1, +    "test_alert"  => "Pingly faily", +  } + + +  # +  #  Run the test. +  # +  obj = PINGTest.new( test ) +  if ( obj.run_test ) +    puts "TEST OK" +  else +    puts "TEST FAILED" +    puts obj.error() +  end + +end diff --git a/lib/custodian/protocol-tests/rsync.rb b/lib/custodian/protocol-tests/rsync.rb new file mode 100755 index 0000000..02f2dfa --- /dev/null +++ b/lib/custodian/protocol-tests/rsync.rb @@ -0,0 +1,145 @@ +#!/usr/bin/ruby + + + +require 'socket' +require 'timeout' + + +# +# Test that we can receive a response from an rsync server that looks +# reasonable. +# +class RSYNCTest + +  # +  # 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 probe +    # +    if ( @test_data["target_host"].nil? ) +      @error = "Missing target for the test." +      raise ArgumentError, @error +    end + +    # +    # Ensure we have a port to test. +    # +    if ( @test_data["test_port"].nil? ) +      @error = "Missing port for the test." +      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 +    @error = "" + +    # +    #  Get the hostname & port to test against. +    # +    host = @test_data['target_host'] +    port = @test_data['test_port'] + +    puts "rsync testing host #{host}:#{port}" if ( @test_data['verbose'] ) + +    begin +      timeout(3) do + +        begin +          socket = TCPSocket.new( host, port ) +          socket.puts( "QUIT") + +          banner = socket.gets(nil) +          banner = banner[0,20] + +          socket.close() + +          if ( banner =~ /rsyncd/i ) +            puts "rsync alive: #{banner}" if ( @test_data['verbose'] ) +            return true +          else +            @error = "Banner didn't seem reasonable: #{banner}" +            return false; +          end +        rescue +          @error = "rsync exception on host #{host}:#{port} - #{$!}" +          return false +        end +      end +    rescue Timeout::Error => e +      @error = "TIMEOUT: #{e}" +      return false +    end + +    @error = "Misc failure" +    return false +  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" => "upload.ns.bytemark.co.uk", +    "test_type"   => "rsync", +    "verbose"     => 1, +    "test_alert"  => "DNS upload service failure", +  } + + +  # +  #  Run the test. +  # +  obj = RSYNCTest.new( test ) +  if ( obj.run_test ) +    puts "TEST OK" +  else +    puts "TEST FAILED" +    puts obj.error() +  end + +end + diff --git a/lib/custodian/protocol-tests/smtp.rb b/lib/custodian/protocol-tests/smtp.rb new file mode 100755 index 0000000..aa577d6 --- /dev/null +++ b/lib/custodian/protocol-tests/smtp.rb @@ -0,0 +1,145 @@ +#!/usr/bin/ruby + + + +require 'socket' +require 'timeout' + + +# +# Test that we can receive a response from an SMTP server that looks +# reasonable. +# +class SMTPTest + +  # +  # 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 probe +    # +    if ( @test_data["target_host"].nil? ) +      @error = "Missing target for the test." +      raise ArgumentError, @error +    end + +    # +    # Ensure we have a port to test. +    # +    if ( @test_data["test_port"].nil? ) +      @error = "Missing port for the test." +      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 +    @error = "" + +    # +    #  Get the hostname & port to test against. +    # +    host = @test_data['target_host'] +    port = @test_data['test_port'] + +    puts "SMTP testing host #{host}:#{port}" if ( @test_data['verbose'] ) + +    begin +      timeout(3) do + +        begin +          socket = TCPSocket.new( host, port ) +          socket.puts( "QUIT") + +          banner = socket.gets(nil) +          banner = banner[0,40] + +          socket.close() + +          if ( banner =~ /SMTP/i ) +            puts "SMTP alive: #{banner}" if ( @test_data['verbose'] ) +            return true +          else +            @error = "Banner didn't seem reasonable: #{banner}" +            return false; +          end +        rescue +          @error = "SMTP exception on host #{host}:#{port} - #{$!}" +          return false +        end +      end +    rescue Timeout::Error => e +      @error = "TIMEOUT: #{e}" +      return false +    end + +    @error = "Misc failure" +    return false +  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" => "mail.steve.org.uk", +    "test_type"   => "smtp", +    "verbose"     => 1, +    "test_alert"  => "SMTP failure", +  } + + +  # +  #  Run the test. +  # +  obj = SMTPTest.new( test ) +  if ( obj.run_test ) +    puts "TEST OK" +  else +    puts "TEST FAILED" +    puts obj.error() +  end + +end diff --git a/lib/custodian/protocol-tests/ssh.rb b/lib/custodian/protocol-tests/ssh.rb new file mode 100755 index 0000000..18815b4 --- /dev/null +++ b/lib/custodian/protocol-tests/ssh.rb @@ -0,0 +1,144 @@ +#!/usr/bin/ruby + +require 'socket' +require 'timeout' + + +# +# Test that we can receive a response from an SSH server that looks +# reasonable. +# +class SSHTest + +  # +  # 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 probe +    # +    if ( @test_data["target_host"].nil? ) +      @error = "Missing target for the test." +      raise ArgumentError, @error +    end + +    # +    # Ensure we have a port to test. +    # +    if ( @test_data["test_port"].nil? ) +      @error = "Missing port for the test." +      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 +    @error = "" + +    # +    #  Get the hostname & port to test against. +    # +    host = @test_data['target_host'] +    port = @test_data['test_port'] + +    puts "ssh testing host #{host}:#{port}" if ( @test_data['verbose'] ) + +    begin +      timeout(3) do + +        begin +          socket = TCPSocket.new( host, port ) +          socket.puts( "QUIT") + +          banner = socket.gets(nil) +          banner = banner[0,20] + +          socket.close() + +          if ( banner =~ /ssh/i ) +            puts "ssh alive: #{banner}" if ( @test_data['verbose'] ) +            return true +          else +            @error = "Banner didn't seem reasonable: #{banner}" +            return false; +          end +        rescue +          @error = "ssh exception on host #{host}:#{port} - #{$!}" +          return false +        end +      end +    rescue Timeout::Error => e +      @error = "TIMEOUT: #{e}" +      return false +    end + +    @error = "Misc failure" +    return false +  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" => "ssh.steve.org.uk", +    "test_type"   => "ssh", +    "test_port"   => 2222, +    "verbose"     => 1, +    "test_alert"  => "Steve's host isn't running SSH?", +  } + + +  # +  #  Run the test. +  # +  obj = SSHTest.new( test ) +  if ( obj.run_test ) +    puts "TEST OK" +  else +    puts "TEST FAILED" +    puts obj.error() +  end + +end | 
