77 lines
2.3 KiB
Python
77 lines
2.3 KiB
Python
import logging
|
|
import random
|
|
import shutil
|
|
import subprocess
|
|
import typing
|
|
|
|
if typing.TYPE_CHECKING:
|
|
from .tld_list import TLDList
|
|
from .word_list import WordList
|
|
|
|
|
|
class FactoryError(Exception):
|
|
pass
|
|
|
|
|
|
class Factory:
|
|
__slots__ = ["__found", "__tld_list", "__word_list", "logger", "logger"]
|
|
|
|
def __init__(self, tld_list: TLDList, word_list: WordList) -> None:
|
|
self.logger: logging.Logger = logging.getLogger(self.__class__.__name__)
|
|
self.__tld_list = tld_list
|
|
self.__word_list = word_list
|
|
self.__found: set[str] = set()
|
|
|
|
def __iter__(self) -> typing.Self:
|
|
return self
|
|
|
|
def __next__(self) -> str:
|
|
return self.random_domain()
|
|
|
|
@property
|
|
def __tld_list_shuffled(self) -> typing.Iterable[str]:
|
|
if self.__tld_list.tlds is None:
|
|
msg = "TLD list is not loaded"
|
|
raise FactoryError(msg)
|
|
return random.sample(self.__tld_list.tlds, len(self.__tld_list.tlds))
|
|
|
|
def __is_domain_available(self, domain: str) -> bool:
|
|
dig_bin = shutil.which("dig")
|
|
if dig_bin is None:
|
|
msg = "dig is not installed"
|
|
raise FactoryError(msg)
|
|
try:
|
|
output = subprocess.check_output([dig_bin, domain, "NS", "+short"]) # noqa: S603
|
|
return len(output) == 0
|
|
except subprocess.CalledProcessError:
|
|
return False
|
|
|
|
def random_domain(self) -> str:
|
|
k = 10000
|
|
while (k := k - 1) > 0 and (domain := self.__random_domain()) is None:
|
|
pass
|
|
if domain is None:
|
|
raise StopIteration
|
|
return domain
|
|
|
|
def random_available_domain(self) -> str:
|
|
k = 10000
|
|
while (k := k - 1) > 0 and not self.__is_domain_available(
|
|
domain := self.random_domain()
|
|
):
|
|
pass
|
|
if k <= 0:
|
|
raise StopIteration
|
|
return domain
|
|
|
|
def __random_domain(self) -> str | None:
|
|
word = random.choice(self.__word_list.words) # noqa: S311
|
|
for tld in self.__tld_list_shuffled:
|
|
tld_escaped = tld.replace(".", "")
|
|
if word.endswith(tld_escaped) and len(word) > len(tld_escaped):
|
|
domain = word[: -len(tld_escaped)] + "." + tld
|
|
if domain not in self.__found:
|
|
self.__found.add(domain)
|
|
return domain
|
|
return None
|