<commit_before># -*- coding: utf-8 -*-
# -------------------------------------------------------------------------------
# Name:         sfp_dnszonexfer
# Purpose:      SpiderFoot plug-in for attempting a DNS zone transfer.
#
# Author:      <NAME> <<EMAIL>>
#
# Created:     22/08/2018
# Copyright:   (c) <NAME> 2018
# Licence:     GPL
# -------------------------------------------------------------------------------

import re

import dns.query
import dns.zone

from spiderfoot import SpiderFootEvent, SpiderFootPlugin


class sfp_dnszonexfer(SpiderFootPlugin):

    meta = {
        'name': "DNS Zone Transfer",
        'summary': "Attempts to perform a full DNS zone transfer.",
        'flags': [],
        'useCases': ["Footprint", "Investigate"],
        'categories': ["DNS"]
    }

    opts = {
    }

    optdescs = {
    }

    events = None

    def setup(self, sfc, userOpts=dict()):
        self.sf = sfc
        self.events = self.tempStorage()
        self.__dataSource__ = "DNS"

        for opt in list(userOpts.keys()):
            self.opts[opt] = userOpts[opt]

    def watchedEvents(self):
        return ['PROVIDER_DNS']

    def producedEvents(self):
        return ["RAW_DNS_RECORDS", "INTERNET_NAME"]

    def handleEvent(self, event):
        eventName = event.eventType
        srcModuleName = event.module
        eventData = event.data
        eventDataHash = self.sf.hashstring(eventData)
        parentEvent = event

        self.sf.debug(f"Received event, {eventName}, from {srcModuleName}")

        if srcModuleName == "sfp_dnszonexfer":
            self.sf.debug(f"Ignoring {eventName}, from self.")
            return

        if eventDataHash in self.events:
            self.sf.debug("Skipping duplicate event for " + eventData)
            return

        self.events[eventDataHash] = True

        res = dns.resolver.Resolver()
        if self.opts.get('_dnsserver', "") != "":
            res.nameservers = [self.opts['_dnsserver']]

        # Get the name server's IP. This is to avoid DNS leaks
        # when attempting to resolve the name server during
        # the zone transfer.
        # TODO: Add support for IPv6 name servers
        if not self.sf.validIP(eventData):
            nsips = self.sf.resolveHost(eventData)
            if not nsips:
                return

            if not nsips:
                self.sf.error("Couldn't resolve the name server, so not attempting zone transfer.")
                return

            for n in nsips:
                if self.sf.validIP(n):
                    nsip = n
                    break
        else:
            nsip = eventData

        for name in self.getTarget().getNames():
            self.sf.debug("Trying for name: " + name)
            try:
                ret = list()
                z = dns.zone.from_xfr(dns.query.xfr(nsip, name))
                names = list(z.nodes.keys())<commit_msg>sfp_dnszonexfer: Add support for IPv6 name servers (#1423)<commit_after># -*- coding: utf-8 -*-
# -------------------------------------------------------------------------------
# Name:         sfp_dnszonexfer
# Purpose:      SpiderFoot plug-in for attempting a DNS zone transfer.
#
# Author:      <NAME> <<EMAIL>>
#
# Created:     22/08/2018
# Copyright:   (c) <NAME> 2018
# Licence:     GPL
# -------------------------------------------------------------------------------

import re

import dns.query
import dns.zone

from spiderfoot import SpiderFootEvent, SpiderFootPlugin


class sfp_dnszonexfer(SpiderFootPlugin):

    meta = {
        'name': "DNS Zone Transfer",
        'summary': "Attempts to perform a full DNS zone transfer.",
        'flags': [],
        'useCases': ["Footprint", "Investigate"],
        'categories': ["DNS"]
    }

    opts = {
    }

    optdescs = {
    }

    events = None

    def setup(self, sfc, userOpts=dict()):
        self.sf = sfc
        self.events = self.tempStorage()
        self.__dataSource__ = "DNS"

        for opt in list(userOpts.keys()):
            self.opts[opt] = userOpts[opt]

    def watchedEvents(self):
        return ['PROVIDER_DNS']

    def producedEvents(self):
        return ["RAW_DNS_RECORDS", "INTERNET_NAME"]

    def handleEvent(self, event):
        eventName = event.eventType
        srcModuleName = event.module
        eventData = event.data
        eventDataHash = self.sf.hashstring(eventData)
        parentEvent = event

        self.sf.debug(f"Received event, {eventName}, from {srcModuleName}")

        if srcModuleName == "sfp_dnszonexfer":
            self.sf.debug(f"Ignoring {eventName}, from self.")
            return

        if eventDataHash in self.events:
            self.sf.debug("Skipping duplicate event for " + eventData)
            return

        self.events[eventDataHash] = True

        res = dns.resolver.Resolver()
        if self.opts.get('_dnsserver', "") != "":
            res.nameservers = [self.opts['_dnsserver']]

        # Get the name server's IP. This is to avoid DNS leaks
        # when attempting to resolve the name server during
        # the zone transfer.
        if not self.sf.validIP(eventData) and not self.sf.validIP6(eventData):
            nsips = self.sf.resolveHost(eventData)
            if not nsips:
                return

            if not nsips:
                self.sf.error("Couldn't resolve the name server, so not attempting zone transfer.")
                return

            for n in nsips:
                if self.sf.validIP(n):
                    nsip = n
                    break
        else:
            nsip = eventData

        for name in self.getTarget().getNames():
            self.sf.debug("Trying for name: " + name)
            try:
                ret = list()
                z = dns.zone.from_xfr(dns.query.xfr(nsip, name))
                names = list(z.nodes.keys())
{'fraction_non_alphanumeric': 0.15802975885069268, 'fraction_numerical': 0.005643919958953309, 'mean_word_length': 1.6212460779919318, 'pattern_counts': {'":': 4, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7527108', 'n_tokens_mistral': 1734, 'n_tokens_neox': 1635, 'n_words': 424}