| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293 |
- import os
- import time
- import datetime
- import re
- import json
- import threading
- import config
- from core.file_manager import load_panel_settings, write_file
- stats_lock = threading.Lock()
- last_file_size = 0
- def stats_collector():
- global last_file_size
- while True:
- time.sleep(60)
- try:
- s = load_panel_settings()
- if s.get('query_log_enabled') != 'yes' or not os.path.exists(config.QUERY_LOG_PATH):
- continue
-
- current_size = os.path.getsize(config.QUERY_LOG_PATH)
- if current_size < last_file_size:
- last_file_size = 0
-
- lines = []
- with open(config.QUERY_LOG_PATH, 'r', errors='ignore') as f:
- f.seek(last_file_size)
- lines = f.readlines()
- last_file_size = f.tell()
-
- if not lines: continue
-
- now = datetime.datetime.now()
- d_key, t_key = now.strftime("%Y-%m-%d"), now.strftime("%H:%M")
-
- with stats_lock:
- st = {}
- if os.path.exists(config.STATS_FILE):
- try:
- with open(config.STATS_FILE, "r") as f: st = json.load(f)
- except: pass
-
- if d_key not in st:
- st[d_key] = {
- "queries": 0, "ips": {}, "tlds": {}, "timeline": {},
- "blocked_queries": 0, "blocked_domains": {},
- "blocked_clients": {}, "block_timeline": {}
- }
- day = st[d_key]
-
- q_count_min = 0
- b_count_min = 0
-
- for line in lines:
- line_lower = line.lower()
-
- # 1. Κανονικά Queries
- if 'query:' in line_lower:
- q_count_min += 1; day["queries"] = day.get("queries", 0) + 1
- ip_match = re.search(r'([0-9a-fA-F\.\:]+)#\d+', line)
- if ip_match:
- ip = ip_match.group(1); day["ips"][ip] = day["ips"].get(ip, 0) + 1
- q_match = re.search(r'query:\s+([^\s/]+)', line, re.I)
- if q_match:
- q_val = q_match.group(1).lower().strip('.')
- tld = q_val.split('.')[-1] if '.' in q_val else q_val
- day["tlds"][tld] = day.get("tlds", {}).get(tld, 0) + 1
-
- # 2. Blocked / RPZ Queries (Πιάνει όλα τα formats του BIND)
- if 'rpz' in line_lower and ('rewrite' in line_lower or 'qname' in line_lower or 'passthru' in line_lower):
- b_count_min += 1
- day["blocked_queries"] = day.get("blocked_queries", 0) + 1
-
- # Ψάχνει IP και Domain αγνοώντας τυχόν "@0x..."
- m_rpz = re.search(r'client\s+(?:@[^\s]+\s+)?([0-9a-fA-F\.\:]+)#\d+\s+\((.*?)\)', line)
- if m_rpz:
- b_ip = m_rpz.group(1)
- b_dom = m_rpz.group(2).strip('.')
- day.setdefault("blocked_clients", {})[b_ip] = day.get("blocked_clients", {}).get(b_ip, 0) + 1
- day.setdefault("blocked_domains", {})[b_dom] = day.get("blocked_domains", {}).get(b_dom, 0) + 1
-
- day.setdefault("timeline", {})[t_key] = {"rps": round(q_count_min / 60.0, 3), "unique_ips": len(day["ips"])}
- day.setdefault("block_timeline", {})[t_key] = round(b_count_min / 60.0, 3)
-
- cutoff = (now - datetime.timedelta(days=int(s.get('stats_retention', 30)))).strftime("%Y-%m-%d")
- st = {k: v for k, v in st.items() if k >= cutoff}
- write_file(config.STATS_FILE, json.dumps(st))
- except Exception as e:
- print(f"Stats Collector Error: {e}")
- def start_stats_daemon():
- threading.Thread(target=stats_collector, daemon=True).start()
|