import pandas as pd
from sqlalchemy import create_engine
import logging
from collections import defaultdict
from datetime import datetime, timedelta

# Configurazione del logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)

# Connessione al database
engine = create_engine('mysql+mysqlconnector://root:Hdgtejskjjc0-@localhost/LOG_MIKROTIK')

# Soglie di rilevamento
THRESHOLD_SYN_FLOOD = 100  # Numero di pacchetti SYN senza ACK in un intervallo di tempo
THRESHOLD_ACK_FLOOD = 100  # Numero di pacchetti ACK senza sessioni attive
THRESHOLD_FIN_RST_FLOOD = 100  # Numero di pacchetti FIN o RST
THRESHOLD_NULL_XMAS_SCAN = 50  # Numero di pacchetti Null o Xmas

# Strutture dati per memorizzare i pacchetti
syn_packets = defaultdict(int)
ack_packets = defaultdict(int)
fin_rst_packets = defaultdict(int)
null_xmas_packets = defaultdict(int)

# Report dei rilevamenti
detection_report = {
    "SYN Flood": 0,
    "ACK Flood": 0,
    "FIN/RST Flood": 0,
    "Null/Xmas Scan": 0,
    "Suspicious IPs": defaultdict(int)
}

# Nome del file per tenere traccia dell'ultimo ID analizzato
ULTIMO_ANALIZZATO_FILE = 'ultimo_analizzato.txt'

# Funzione per leggere l'ultimo ID analizzato dal file
def read_ultimo_analizzato():
    try:
        with open(ULTIMO_ANALIZZATO_FILE, 'r') as f:
            return int(f.read().strip())
    except FileNotFoundError:
        logging.info(f"File '{ULTIMO_ANALIZZATO_FILE}' non trovato. Partenza da ID = 0.")
        return 0
    except ValueError:
        logging.error(f"Errore nella lettura del file '{ULTIMO_ANALIZZATO_FILE}'.")
        return 0

# Funzione per scrivere l'ultimo ID analizzato nel file
def write_ultimo_analizzato(last_analyzed_id):
    with open(ULTIMO_ANALIZZATO_FILE, 'w') as f:
        f.write(str(last_analyzed_id))

# Funzione per analizzare il traffico sulla base delle regole
def analyze_traffic(data):
    for index, row in data.iterrows():
        ip_src = row['Messaggio2'].split(':')[0]  # IP di origine (estratto da Messaggio2)
        flags = row['Messaggio1']  # Flags TCP (da Messaggio1)

        # Rileva SYN flood
        if "SYN" in flags and "ACK" not in flags:
            syn_packets[ip_src] += 1
            if syn_packets[ip_src] > THRESHOLD_SYN_FLOOD:
                detection_report["SYN Flood"] += 1
                detection_report["Suspicious IPs"][ip_src] += 1
                logging.info(f"[ALERT] Possibile SYN Flood da {ip_src} - {syn_packets[ip_src]} pacchetti SYN senza ACK")

        # Rileva ACK flood
        if "ACK" in flags and "SYN" not in flags:
            ack_packets[ip_src] += 1
            if ack_packets[ip_src] > THRESHOLD_ACK_FLOOD:
                detection_report["ACK Flood"] += 1
                detection_report["Suspicious IPs"][ip_src] += 1
                logging.info(f"[ALERT] Possibile ACK Flood da {ip_src} - {ack_packets[ip_src]} pacchetti ACK sospetti")

        # Rileva FIN/RST flood
        if "FIN" in flags or "RST" in flags:
            fin_rst_packets[ip_src] += 1
            if fin_rst_packets[ip_src] > THRESHOLD_FIN_RST_FLOOD:
                detection_report["FIN/RST Flood"] += 1
                detection_report["Suspicious IPs"][ip_src] += 1
                logging.info(f"[ALERT] Possibile FIN/RST Flood da {ip_src} - {fin_rst_packets[ip_src]} pacchetti FIN/RST")

        # Rileva Null Scan o Xmas Tree Scan
        if flags == "" or "FIN, PSH, URG" in flags:  # Verifica per Null o Xmas Tree Scan
            null_xmas_packets[ip_src] += 1
            if null_xmas_packets[ip_src] > THRESHOLD_NULL_XMAS_SCAN:
                detection_report["Null/Xmas Scan"] += 1
                detection_report["Suspicious IPs"][ip_src] += 1
                logging.info(f"[ALERT] Possibile Null/Xmas Scan da {ip_src} - {null_xmas_packets[ip_src]} pacchetti")

# Funzione per estrarre i dati dal database in blocchi
def fetch_network_data(last_analyzed_id, limit=10000):
    query = f"""
    SELECT ID, Data, Ora, IndirizzoIP, Messaggio1, Messaggio2, Messaggio3
    FROM Esterna
    WHERE ID > {last_analyzed_id}
    ORDER BY ID ASC
    LIMIT {limit}
    """
    return pd.read_sql(query, engine)

# Funzione per generare il report al termine dell'analisi
def generate_report():
    logging.info("\n--- Report di rilevamento DDoS ---\n")
    logging.info(f"Totale SYN Flood rilevati: {detection_report['SYN Flood']}")
    logging.info(f"Totale ACK Flood rilevati: {detection_report['ACK Flood']}")
    logging.info(f"Totale FIN/RST Flood rilevati: {detection_report['FIN/RST Flood']}")
    logging.info(f"Totale Null/Xmas Scan rilevati: {detection_report['Null/Xmas Scan']}")
    
    logging.info("\n--- IP sospetti rilevati ---\n")
    for ip, count in detection_report['Suspicious IPs'].items():
        logging.info(f"IP {ip} rilevato in {count} attività sospette")

# Funzione principale
def main():
    last_analyzed_id = read_ultimo_analizzato()  # Inizializza l'ID di partenza
    batch_size = 10000
    total_records_analyzed = 0

    while True:
        logging.info(f"Estrazione di un nuovo blocco di dati da ID > {last_analyzed_id}...")
        network_data = fetch_network_data(last_analyzed_id, limit=batch_size)

        if network_data.empty:
            logging.info("Nessun nuovo dato da analizzare. Analisi completata.")
            break
        
        # Aggiorna l'ultimo ID analizzato
        last_analyzed_id = network_data['ID'].max()
        total_records_analyzed += len(network_data)
        write_ultimo_analizzato(last_analyzed_id)  # Salva l'ID aggiornato

        # Analizza il traffico del blocco corrente
        analyze_traffic(network_data)

        # Logging per ogni blocco processato
        logging.info(f"Blocco analizzato: {len(network_data)} record.")
    
    logging.info(f"Totale record analizzati: {total_records_analyzed}")
    
    # Genera il report finale
    generate_report()

if __name__ == "__main__":
    main()
