From dbb4386809eb8a1afa91a7891380fb6602ec0675 Mon Sep 17 00:00:00 2001
From: Steve Kemp <steve@steve.org.uk>
Date: Mon, 2 Mar 2015 12:57:32 +0000
Subject: Allow IPv4 and IPv6 tests to be carried out.

If a target is a hostname we'll explicitly resolve it for
both IPv4 and IPv6.
---
 lib/custodian/protocoltest/tcp.rb | 103 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 102 insertions(+), 1 deletion(-)

(limited to 'lib')

diff --git a/lib/custodian/protocoltest/tcp.rb b/lib/custodian/protocoltest/tcp.rb
index 742de31..7bceeac 100644
--- a/lib/custodian/protocoltest/tcp.rb
+++ b/lib/custodian/protocoltest/tcp.rb
@@ -132,16 +132,117 @@ module Custodian
 
 
 
-
       #
       # Run the connection test - optionally matching against the banner.
       #
       # If the banner is nil then we're merely testing we can connect and
       # send the string "quit".
       #
+      # There are two ways this method will be invoked, via the configuration file:
+      #
+      #         foo.vm.bytemark.co.uk must run tcp on 24 with banner 'smtp'
+      # or
+      #         1.2.3.44 must run tcp on 53 otherwise 'named failed'.
+      #
+      # Here we're going to try to resolve the hostname in the first version
+      # into both IPv6 and IPv4 addresses and test them both.
+      #
+      # A failure in either version will result in a failure.
       #
       def run_test_internal( host, port, banner = nil, do_read = false )
 
+        #
+        # Get the timeout period.
+        #
+        settings = Custodian::Settings.instance()
+        period   = settings.timeout()
+
+        #
+        # Perform the DNS lookups of the specified name.
+        #
+        ips = Array.new()
+
+        #
+        #  Does the name look like an IP?
+        #
+        begin
+          x = IPAddr.new( host )
+          if ( x.ipv4? or x.ipv6? )
+            ips.push( host )
+          end
+        rescue ArgumentError
+          #
+          # NOP - Just means the host wasn't an IP
+          #
+        end
+
+
+
+        #
+        # OK if it didn't look like an IP address then attempt to
+        # look it up, as both IPv4 and IPv6.
+        #
+        begin
+          timeout( period ) do
+
+            Resolv::DNS.open do |dns|
+              ress = dns.getresources(host, Resolv::DNS::Resource::IN::A)
+              ress.map { |r| ips.push( r.address.to_s ) }
+              ress = dns.getresources(host, Resolv::DNS::Resource::IN::AAAA)
+              ress.map { |r| ips.push( r.address.to_s ) }
+            end
+          end
+        rescue Timeout::Error => e
+          @error = "Timed-out performing DNS lookups: #{e}"
+          return nil
+        end
+
+
+        #
+        #  Did we fail to perform a DNS lookup?
+        #
+        if ( ips.empty? )
+          @error = "#{@host} failed to resolve to either IPv4 or IPv6"
+          return false
+        end
+
+
+        #
+        # Run the test, avoiding the use of the shell, for each of the
+        # IPv4 and IPv6 addresses we discovered, or the host that we
+        # were given.
+        #
+        ips.each do |ip|
+          if ( ! run_test_internal_real( ip, port, banner, do_read ) )
+
+            return false
+            #
+            # @error will be already set.
+            #
+          end
+        end
+
+
+        #
+        #  All was OK
+        #
+        @error = nil
+        return true
+      end
+
+
+
+      #
+      # Run the connection test - optionally matching against the banner.
+      #
+      # If the banner is nil then we're merely testing we can connect and
+      # send the string "quit".
+      #
+      # This method will ONLY ever be invoked with an IP-address for a
+      # destination.
+      #
+      def run_test_internal_real( host, port, banner = nil, do_read = false )
+
         #
         # Get the timeout period for this test.
         #
-- 
cgit v1.2.3