class InfectedHostsDetector:
    """
    InfectedHostsDetector takes found portscans and policy violations 
    and tries to find indected hosts.
    """

    def __init__(self, tcpPortscans, udpPortscans, policyViolations, suspPolVio):

        self.tcpPortscans = tcpPortscans
        self.udpPortscans = udpPortscans
        self.policyViolations = policyViolations
        self.portscans = RuleSet()
        self.doubleIPs = IPsMap()
        self.portscanIPs = IPsMap()
        self.violationIPs = IPsMap()
        self.clearedPolicyViolations = RuleSet()
        self.suspPolVio = suspPolVio.split(":")

    def detectInfectedHosts(self):
        for ps in self.tcpPortscans.portscanSet:
            if ps.direction == "out" or ps.direction == "none-inner":
                self.portscans.insert(ps)
        for ps in self.udpPortscans.portscanSet:
            if ps.direction == "out" or ps.direction == "none-inner":
                self.portscans.insert(ps)
        for pv in self.policyViolations:
            if pv.direction == "out" or pv.direction == "none-inner":
                if str(pv.dPorts.values()[0].portNumber) in self.suspPolVio:
                    self.clearedPolicyViolations.insert(pv)

        for portscan in self.portscans:
            for violation in self.clearedPolicyViolations:
                if portscan.sIPs == violation.sIPs:
                    self.doubleIPs.insert(portscan.sIPs)

        for doubleIP in self.doubleIPs:
            for policyVio in self.clearedPolicyViolations.values():
                if policyVio.sIPs == doubleIP:
                    try:
                        self.clearedPolicyViolations.remove(policyVio)
                    except:
                        # already removed
                        pass

        for doubleIP in self.doubleIPs:
            for portscan in self.portscans.values():
                if portscan.sIPs == doubleIP:
                    try:
                        self.portscans.remove(portscan)
                    except:
                        # already removed
                        pass

        for violation in self.clearedPolicyViolations:
            self.violationIPs.insert(violation.sIPs)
        for portscan in self.portscans:
            self.portscanIPs.insert(portscan.sIPs)

    def printInfectedHosts(self):
        for host in self.doubleIPs:
            print "%s might be infected! Warning signals include: portscans and policy violations" % str(host)
        for host in self.portscanIPs:
            print "%s might be infected! Warning signals include: portscans" % str(host)
        for host in self.violationIPs:
            print "%s might be infected! Warning signals include: policy violations" % str(host)
class PortscanDetector(RuleGenerator):
    """
    PortscanDetector takes a ruleSet and tries to find portscans.
    """
    
    def __init__(self, ruleSet, proto, style, numChecksP, numChecksIP, \
                 numAnyP, numAnyIP, distanceRange, slashSize, ipsPerSlash, \
                 numPortsPortscan, numIPsPortscan):

        RuleGenerator.__init__(self, ruleSet, proto, style, numChecksP, numChecksIP, \
                               numAnyP, numAnyIP, distanceRange, \
                               slashSize, ipsPerSlash)

        self.MAX_NUM_PORTS_PORTSCAN = numPortsPortscan
        self.MAX_NUM_IPS_PORTSCAN = numIPsPortscan
        self.MAX_DIST_RANGE_SCAN = 65535
        self.portscanSet = RuleSet()

    def detectPortscans(self):
        self.generateRules()
        self._checkForPortscans()
        self.portscanSet.checkTables(self.NUM_CHECKS_P, self.MAX_DIST_RANGE_SCAN,\
                                     self.NUM_ANY_P, self.NUM_CHECKS_IP, \
                                     self.NUM_EXP, self.NUM_ANY_IP, \
                                     self.SLASH_SIZE, self.MIN_IPS_PER_SLASH)

    def _checkForPortscans(self):
        ruleList = self.ruleSet.values()
        for r in ruleList:
            if (len(r.dPorts.elements) > self.MAX_NUM_PORTS_PORTSCAN) and \
               (len(r.sIPs) == 1) or \
               (len(r.dIPs.elements) > self.MAX_NUM_IPS_PORTSCAN and \
               len(r.sIPs) == 1) and (len(r.dPorts.elements) == 1)\
               and not r.direction == "out":
                psr = PortscanRule(r.direction, r.sIPs, r.sPorts, r.dIPs, \
                                   r.dPorts, r.proto, r.interface, "block", \
                                   self.style, r.timeStamp, r.flag)
                self.portscanSet.insert(psr)
    
    def removeRulesContainingPortscanners(self, ruleSet):
        for r in ruleSet.values():
            for pr in self.portscanSet.values():
                for e in pr.sIPs.elements.values():   
                    if r.sIPs.values()[0] == e \
                       or r.dIPs.values()[0] == e:
                       try:
                           ruleSet.remove(r)
                       except KeyError:
                           #Is already removed
                           pass
                       break

    def generateRules(self):
        self.generateTables()
        del self.sortingDict
        del self.tablesDict

    def printRules(self):
        l = len(self.portscanSet)
        if l > 0:
            if l == 1:
                print "%d rule which results from traffic which looks like a portscan\n" % l
            else:
                print "%d rules which result from traffic which looks like portscans\n" % l
            print self.portscanSet 

    def generateTables(self):
        slashSizeBackup = self.SLASH_SIZE
        self.SLASH_SIZE = 32
        if not self.proto == "icmp":
            # 2. sport is the varying part
            sPort = True
            self._portsVary(sPort)

            # 3. dport is the varying part
            sPort = False
            self._portsVary(sPort)

        # 4. dIP is the varying part
        sIP = False
        self._ipsVary(sIP)
        self.SLASH_SIZE = slashSizeBackup