Bu yazılım, Python ile geliştirilmiş gelişmiş bir domain varyasyon ve durum kontrol aracıdır. Kullanıcı tarafından girilen domain adının olası varyasyonlarını (tipografi hataları, leetspeak dönüşümleri vb.) otomatik olarak üretir, ardından DNS, WHOIS, SSL sertifika kontrolleri, ve Selenium destekli ekran görüntüsü alma gibi kapsamlı kontroller gerçekleştirir. Çoklu iş parçacığı (multithreading) desteği ve hata yönetimi sayesinde hızlı, güvenilir sonuçlar elde edilir. Sonuçlar CSV, JSON ve HTML formatlarında raporlanır. Hem komut satırı argümanlarıyla hem de etkileşimli modda çalışabilen bu araç, domain araştırmaları ve siber güvenlik uygulamalarında pratik ve etkili bir çözüm sunar.

import socket
import whois
import pyfiglet
import csv
import json
import ssl
import time
import argparse
import os
from concurrent.futures import ThreadPoolExecutor
from tenacity import retry, stop_after_attempt
from tqdm import tqdm
from colorama import init, Fore
import dns.resolver
import asyncio
import aiohttp
from selenium import webdriver
from selenium.webdriver.chrome.options import Options

init(autoreset=True)

socket.setdefaulttimeout(10)

LEET_MAP = {
    'o': '0',
    'i': '1',
    'l': '1',
    's': '5'
}

TLD_LIST = ['.com.tr', '.com', '.net', '.tr']

dns_cache = {}
whois_cache = {}

def load_common_typos(file_path="common_typos.txt"):
    typos = set()
    if os.path.exists(file_path):
        with open(file_path, "r") as f:
            for line in f:
                typo = line.strip()
                if typo:
                    typos.add(typo)
    return typos

def apply_leetspeak(domain):
    for tld in TLD_LIST:
        if domain.endswith(tld):
            name = domain[:-len(tld)]
            ext = tld
            break
    else:
        name = domain
        ext = ""
    new_name = name
    for char, replacement in LEET_MAP.items():
        new_name = new_name.replace(char, replacement)
    return new_name + ext

def generate_variations(domain):
    variations = set()
    variations.add(domain)
    if '.' in domain:
        name, tld = domain.rsplit('.', 1)
        base_tld = '.' + tld
    else:
        name = domain
        base_tld = ''
    if len(name) > 1:
        variations.add(name[1:] + base_tld)
        variations.add(name[:-1] + base_tld)
    for i in range(1, len(name)):
        lst = list(name)
        lst[i - 1], lst[i] = lst[i], lst[i - 1]
        variations.add("".join(lst) + base_tld)
    if len(name) > 3:
        mid = len(name) // 2
        variations.add(name[:mid] + '-' + name[mid:] + base_tld)
    common_typos = load_common_typos()
    for typo in common_typos:
        if name in typo or typo in name:
            variations.add(typo + base_tld)
    for tld_variant in TLD_LIST:
        variations.add(name + tld_variant)
    leet_variations = set()
    for var in list(variations):
        leet_var = apply_leetspeak(var)
        if leet_var != var:
            leet_variations.add(leet_var)
    variations.update(leet_variations)
    return list(variations)

@retry(stop=stop_after_attempt(3))
def check_whois(domain):
    if domain in whois_cache:
        return whois_cache[domain]
    try:
        w = whois.whois(domain)
        whois_cache[domain] = w
        return w
    except Exception:
        whois_cache[domain] = None
        return None

@retry(stop=stop_after_attempt(3))
def check_dns(domain):
    if domain in dns_cache:
        return dns_cache[domain]
    result = {}
    try:
        ip = socket.gethostbyname(domain)
        result['A'] = ip
    except Exception:
        result['A'] = None
    try:
        answers = dns.resolver.resolve(domain, 'MX')
        result['MX'] = [r.exchange.to_text() for r in answers]
    except Exception:
        result['MX'] = []
    try:
        answers = dns.resolver.resolve(domain, 'NS')
        result['NS'] = [r.target.to_text() for r in answers]
    except Exception:
        result['NS'] = []
    try:
        answers = dns.resolver.resolve(domain, 'TXT')
        result['TXT'] = [r.to_text() for r in answers]
    except Exception:
        result['TXT'] = []
    dns_cache[domain] = result
    return result

def check_ssl_certificate(domain):
    context = ssl.create_default_context()
    try:
        with socket.create_connection((domain, 443)) as sock:
            with context.wrap_socket(sock, server_hostname=domain) as ssock:
                cert = ssock.getpeercert()
                return cert.get('notAfter', None)
    except Exception:
        return None

def take_screenshot(domain):
    try:
        from selenium.webdriver.chrome.service import Service as ChromeService
        from webdriver_manager.chrome import ChromeDriverManager
        chrome_options = Options()
        chrome_options.add_argument("--headless")
        chrome_options.add_argument("--no-sandbox")
        driver = webdriver.Chrome(service=ChromeService(ChromeDriverManager().install()), options=chrome_options)
        url = "http://" + domain
        driver.get(url)
        screenshot_path = f"{domain.replace('.', '_')}.png"
        driver.save_screenshot(screenshot_path)
        driver.quit()
        return screenshot_path
    except Exception:
        return None

def check_phishing(domain):
    return False

def check_domain(domain):
    time.sleep(1)
    result = {"domain": domain}
    dns_result = check_dns(domain)
    result['dns'] = dns_result
    whois_result = check_whois(domain)
    result['whois'] = whois_result
    result['ssl'] = check_ssl_certificate(domain)
    result['phishing'] = check_phishing(domain)
    result['screenshot'] = take_screenshot(domain)
    is_taken = False
    if dns_result.get('A'):
        is_taken = True
    elif whois_result and (
         (isinstance(whois_result, dict) and whois_result.get('domain_name')) or
         (hasattr(whois_result, 'domain_name') and whois_result.domain_name)
    ):
        is_taken = True
    result['taken'] = is_taken
    return result

def export_to_csv(results, filename="results.csv"):
    with open(filename, "w", newline="") as f:
        writer = csv.writer(f)
        writer.writerow(["Domain", "Taken", "IP", "MX", "NS", "TXT", "SSL Expiry", "Phishing", "Screenshot"])
        for res in results:
            writer.writerow([
                res['domain'],
                res['taken'],
                res['dns'].get('A'),
                ";".join(res['dns'].get('MX', [])),
                ";".join(res['dns'].get('NS', [])),
                ";".join(res['dns'].get('TXT', [])),
                res['ssl'],
                res['phishing'],
                res['screenshot'] if res['screenshot'] else ""
            ])

def export_to_json(results, filename="results.json"):
    with open(filename, "w") as f:
        json.dump(results, f, indent=4, default=str)

def export_to_html(results, filename="report.html"):
    try:
        from jinja2 import Template
    except ImportError:
        print("Jinja2 yüklü değil. HTML raporu oluşturulamıyor.")
        return
    template_str = """
    <html>
      <head>
        <title>Domain Check Report</title>
        <meta charset="utf-8">
      </head>
      <body>
        <h1>Domain Check Report</h1>
        <table border="1" cellspacing="0" cellpadding="5">
          <tr>
            <th>Domain</th><th>Taken</th><th>IP</th><th>MX</th><th>NS</th><th>TXT</th><th>SSL Expiry</th><th>Phishing</th><th>Screenshot</th>
          </tr>
          {% for res in results %}
          <tr>
            <td>{{ res.domain }}</td>
            <td>{{ res.taken }}</td>
            <td>{{ res.dns.A }}</td>
            <td>{{ res.dns.MX|join('; ') }}</td>
            <td>{{ res.dns.NS|join('; ') }}</td>
            <td>{{ res.dns.TXT|join('; ') }}</td>
            <td>{{ res.ssl }}</td>
            <td>{{ res.phishing }}</td>
            <td>{% if res.screenshot %}<a href="{{ res.screenshot }}">View</a>{% endif %}</td>
          </tr>
          {% endfor %}
        </table>
      </body>
    </html>
    """
    template = Template(template_str)
    html_content = template.render(results=results)
    with open(filename, "w") as f:
        f.write(html_content)

def interactive_mode():
    banner = pyfiglet.figlet_format("Mariently", font="ansi_shadow")
    print(Fore.GREEN + banner)
    print(Fore.YELLOW + "Hoşgeldiniz! Domain varyasyon ve durum kontrol aracına hoşgeldiniz.\n")
    mode = input("Domain girişi için 'd', dosya yüklemek için 'f' giriniz: ").strip().lower()
    domains = []
    if mode == 'f':
        file_path = input("Dosya yolunu giriniz: ").strip()
        if os.path.exists(file_path):
            with open(file_path, "r") as f:
                for line in f:
                    domain_line = line.strip()
                    if domain_line:
                        domains.append(domain_line)
        else:
            print("Dosya bulunamadı. Çıkılıyor.")
            return
    else:
        domain_input = input("Varyasyonlarını oluşturmak istediğiniz domaini giriniz (örn: ornek.com): ").strip()
        domains = generate_variations(domain_input)
    results = []
    print("\nKontroller başlatılıyor...\n")
    with ThreadPoolExecutor(max_workers=10) as executor:
        for res in tqdm(executor.map(check_domain, domains), total=len(domains)):
            results.append(res)
    for res in results:
        print(Fore.BLUE + f"Kontrol ediliyor: {res['domain']}")
        if res['taken']:
            print(Fore.RED + "Sonuç: Bu domain dolu!")
        else:
            print(Fore.WHITE + "Sonuç: Bu domain uygun!")
        print("-" * 50)
    export_to_csv(results)
    export_to_json(results)
    export_to_html(results)
    print("Sonuçlar 'results.csv', 'results.json' ve 'report.html' dosyalarına kaydedildi.")

def main():
    parser = argparse.ArgumentParser(description="Gelişmiş Domain Varyasyon ve Durum Kontrol Aracı")
    parser.add_argument("--file", help="Domain listesinin bulunduğu dosya yolu", default=None)
    parser.add_argument("--domain", help="Tek bir domain (örn: ornek.com)", default=None)
    args = parser.parse_args()
    if args.file:
        domains = []
        file_path = args.file
        if os.path.exists(file_path):
            with open(file_path, "r") as f:
                for line in f:
                    domain_line = line.strip()
                    if domain_line:
                        domains.append(domain_line)
        else:
            print("Dosya bulunamadı. Çıkılıyor.")
            return
    elif args.domain:
        domains = generate_variations(args.domain)
    else:
        interactive_mode()
        return
    results = []
    print("\nKontroller başlatılıyor...\n")
    with ThreadPoolExecutor(max_workers=10) as executor:
        for res in tqdm(executor.map(check_domain, domains), total=len(domains)):
            results.append(res)
    for res in results:
        print(Fore.BLUE + f"Kontrol ediliyor: {res['domain']}")
        if res['taken']:
            print(Fore.RED + "Sonuç: Bu domain dolu!")
        else:
            print(Fore.WHITE + "Sonuç: Bu domain uygun!")
        print("-" * 50)
    export_to_csv(results)
    export_to_json(results)
    export_to_html(results)
    print("Sonuçlar 'results.csv', 'results.json' ve 'report.html' dosyalarına kaydedildi.")

if __name__ == "__main__":
    main()