Package Products :: Package ZenUtils :: Module PortScan |
[hide private]
[frames] | no frames]

# Source Code for Module Products.ZenUtils.PortScan

      1  ##############################################################################
      2  #
      3  # Copyright (C) Zenoss, Inc. 2007, all rights reserved.
      4  #
      5  # This content is made available according to terms specified in
      6  # License.zenoss under the directory where your Zenoss product is installed.
      7  #
      8  ##############################################################################
      9
     10
     11  import logging
     12
     13  from twisted.internet import defer
     14  from twisted.internet import reactor
     15  from twisted.internet.protocol import Protocol
     16  from twisted.internet.protocol import ClientFactory
     17  from twisted.internet.protocol import ReconnectingClientFactory
     18  from twisted.internet.error import ConnectBindError
     19
     20  log = logging.getLogger("zen.Portscanner")
     21
     22 -class ScanProtocol(Protocol):
     23
     24 -    def connectionMade(self):
     25          self.factory.deferred.callback("success")
     26          self.transport.loseConnection()
     27
     28 -class ScanFactory(ClientFactory):
     29
     30      protocol = ScanProtocol
     31
     32 -    def __init__(self):
     33          self.deferred = defer.Deferred()
     34
     35 -    def clientConnectionFailed(self, unused, reason):
     36          self.deferred.errback(reason)
     37          self.deferred = None
     38
     39 -    def clientConnectionLost(self, connector, reason):
     40          pass
     41
     42 -class ReconnectingScanFactory(ReconnectingClientFactory):
     43
     44      protocol = ScanProtocol
     45
     46 -    def __init__(self):
     47          self.deferred = defer.Deferred()
     48
     49 -    def clientConnectionFailed(self, connector, reason):
     50          self.deferred.errback(reason)
     51          if reason.type == ConnectBindError:
     52              self.connector = connector
     53              self.retry()
     54
     55 -class Scanner(object):
     56      '''
     57      '''
     58 -    def __init__(self, hosts, portRange=(1, 10000), portList=[],
     59          queueCount=50, timeout=30):
     60          if isinstance(hosts, list):
     61              self.hosts = hosts
     62          else:
     63              self.hosts = [hosts]
     64          self.portRange = (portRange[0], portRange[1] + 1)
     65          self.portList = portList
     66          self.queueCount = queueCount
     67          self.timeout = timeout
     68          self.data = {
     69              'success': {},
     70              'failure': {},
     71          }
     72
     73 -    def prepare(self):
     74          '''
     75          The use of DeferredSemaphore() here allows us to control the
     76          number of deferreds (and therefore connections) created at once,
     77          thus providing a way for systems to use the script efficiently.
     78          '''
     79
     80          dl = []
     81          semaphore = defer.DeferredSemaphore(self.queueCount)
     82          if self.portList:
     83              ports = self.portList
     84          else:
     85              ports = range(*self.portRange)
     86          for host in self.hosts:
     87              for port in ports:
     88                  d = semaphore.run(self.doFactory, host, port)
     89                  dl.append(d)
     90          dl = defer.DeferredList(dl, consumeErrors=True)
     91          return dl
     92
     93 -    def run(self):
     94          d = self.prepare()
     95          d.addCallback(self.finishRun)
     96          reactor.run()
     97
     98 -    def doFactory(self, host, port):
     99          factory = ScanFactory()
    100          reactor.connectTCP(host, port, factory, timeout=self.timeout)
    101          d = factory.deferred
    102          d.addCallback(self.recordConnection, host, port)
    103          d.addErrback(self.recordFailure, host, port)
    104          return d
    105
    106 -    def recordConnection(self, unused, host, port):
    107          hostData = self.data['success'].setdefault(host, [])
    108          log.debug('Connected to %s:%d' % (host, port))
    109          hostData.append(port)
    110
    111 -    def recordFailure(self, failure, host, port):
    112          hostData = self.data['failure'].setdefault(host, [])
    113          data = (port, failure.getErrorMessage())
    114          log.debug('Failed to connect to %s:%d -- %s' (host, port, data[1]))
    115          hostData.append(data)
    116
    117 -    def finishRun(self, unused=None):
    118          reactor.stop()
    119          self.printResults()
    120
    121 -    def printResults(self):
    122          print "Open Ports:"
    123          for host, ports in self.getSuccesses().items():
    124              print "Host: %s" % host
    125              for port in ports:
    126                  print "\topen port: %i" % port
    127          errors = {}
    128          for host, portAndError in self.getFailures().items():
    129              for port, error in portAndError:
    130                  errors.setdefault(error, 0)
    131                  errors[error] += 1
    132          print "\nErrors encountered, and their counts:"
    133          for error, count in errors.items():
    134              print "\t%s -- %i" % (error, count)
    135
    136 -    def getSuccesses(self):
    137          return self.data['success']
    138
    139 -    def getFailures(self):
    140          return self.data['failure']
    141