import sys
import ctypes
import subprocess
import ipaddress
import signal
import time

SUBNET_STR = "192.168.2.1/24" # strict=False автоматически превратит это в 192.168.2.0/24

def is_admin():
    """Проверка наличия прав администратора."""
    try:
        return ctypes.windll.shell32.IsUserAnAdmin()
    except:
        return False

def run_ps(command):
    """Выполнение команды PowerShell."""
    return subprocess.run(["powershell", "-Command", command], capture_output=True, text=True)

def cleanup_rules():
    """Удаление всех правил с префиксом VIBE_."""
    print("\n[*] Очистка системы: удаление правил брандмауэра (VIBE_*)...")
    # -ErrorAction SilentlyContinue скрывает ошибки, если правил еще нет
    run_ps("Remove-NetFirewallRule -DisplayName 'VIBE_*' -ErrorAction SilentlyContinue")
    print("[+] Правила успешно удалены.")

def signal_handler(sig, frame):
    """Красивое завершение по Ctrl+C."""
    print("\n\n[!] Получен сигнал завершения (Ctrl+C).")
    cleanup_rules()
    print("[+] Выход по красоте. До связи!")
    sys.exit(0)

def get_block_ranges(target_ip_str, subnet_str):
    """
    Вычисляет диапазоны IP-адресов в подсети, исключая целевой IP.
    Это необходимо, так как правила Block в Windows имеют приоритет над Allow.
    """
    target_ip = ipaddress.IPv4Address(target_ip_str)
    subnet = ipaddress.IPv4Network(subnet_str, strict=False)

    if target_ip not in subnet:
        raise ValueError(f"IP-адрес {target_ip} не принадлежит подсети {subnet}")

    ip_int = int(target_ip)
    net_start = int(subnet.network_address)
    net_end = int(subnet.broadcast_address)

    ranges = []
    # Диапазон ДО целевого IP
    if ip_int > net_start:
        ranges.append(f"{ipaddress.IPv4Address(net_start)}-{ipaddress.IPv4Address(ip_int - 1)}")
    # Диапазон ПОСЛЕ целевого IP
    if ip_int < net_end:
        ranges.append(f"{ipaddress.IPv4Address(ip_int + 1)}-{ipaddress.IPv4Address(net_end)}")

    return ranges

def apply_firewall_rules(target_ip_str):
    """Создание новых правил брандмауэра."""
    cleanup_rules() # Превентивная зачистка от мусора
    
    try:
        ranges = get_block_ranges(target_ip_str, SUBNET_STR)
    except ValueError as e:
        print(f"[-] Ошибка: {e}")
        return False

    ranges_ps = ",".join(f"'{r}'" for r in ranges)

    print(f"[*] Добавление разрешающих правил (In/Out) для {target_ip_str}...")
    run_ps(f"New-NetFirewallRule -DisplayName 'VIBE_Allow_In' -Direction Inbound -Action Allow -RemoteAddress '{target_ip_str}'")
    run_ps(f"New-NetFirewallRule -DisplayName 'VIBE_Allow_Out' -Direction Outbound -Action Allow -RemoteAddress '{target_ip_str}'")

    if ranges_ps:
        print(f"[*] Блокировка остальных адресов подсети (In/Out): {ranges_ps}...")
        run_ps(f"New-NetFirewallRule -DisplayName 'VIBE_Block_In' -Direction Inbound -Action Block -RemoteAddress {ranges_ps}")
        run_ps(f"New-NetFirewallRule -DisplayName 'VIBE_Block_Out' -Direction Outbound -Action Block -RemoteAddress {ranges_ps}")
    
    print("[+] Брандмауэр успешно настроен.")
    return True

def main():
    if not is_admin():
        print("[-] Ошибка: Скрипт необходимо запустить от имени Администратора!")
        input("Нажмите Enter для выхода...")
        sys.exit(1)

    # Привязываем обработчик Ctrl+C
    signal.signal(signal.SIGINT, signal_handler)

    print("=== Управление изоляцией подсети ===")
    print(f"Рабочая подсеть: {ipaddress.IPv4Network(SUBNET_STR, strict=False)}")
    
    target_ip = input("Введите IP-адрес для белого списка: ").strip()

    if apply_firewall_rules(target_ip):
        print("\n[!] Правила активны. Приложение работает в фоновом режиме.")
        print("[!] Нажми Ctrl+C, чтобы сбросить правила и закрыть программу.")
        
        # Держим скрипт запущенным, пока пользователь не нажмет Ctrl+C
        try:
            while True:
                time.sleep(1)
        except KeyboardInterrupt:
            # На Windows иногда KeyboardInterrupt проскакивает мимо signal handler
            signal_handler(signal.SIGINT, None)

if __name__ == "__main__":
    main()
    