summaryrefslogtreecommitdiff
path: root/lib/custodian/protocol-tests
diff options
context:
space:
mode:
Diffstat (limited to 'lib/custodian/protocol-tests')
-rwxr-xr-xlib/custodian/protocol-tests/ftp.rb144
-rwxr-xr-xlib/custodian/protocol-tests/http.rb187
-rwxr-xr-xlib/custodian/protocol-tests/https.rb188
-rwxr-xr-xlib/custodian/protocol-tests/jabber.rb146
-rwxr-xr-xlib/custodian/protocol-tests/ldap.rb140
-rwxr-xr-xlib/custodian/protocol-tests/ping.rb116
-rwxr-xr-xlib/custodian/protocol-tests/rsync.rb145
-rwxr-xr-xlib/custodian/protocol-tests/smtp.rb145
-rwxr-xr-xlib/custodian/protocol-tests/ssh.rb144
9 files changed, 1355 insertions, 0 deletions
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