| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133 |
- import os
- import re
- import time
- import datetime
- import subprocess
- import urllib.request
- import json
- import config
- from core.file_manager import read_file, write_file, load_adblock_lists, load_custom_rules, load_panel_settings, save_panel_settings
- from core.dns_manager import get_zone_block
- def is_valid_domain(d):
- if not d or len(d) > 253: return False
- if '..' in d or d.startswith('.'): return False
- # Επιτρέπει μόνο αλφαριθμητικά, παύλες, underscores και τελείες.
- if not re.match(r'^[a-zA-Z0-9\-_.]+$', d): return False
- if '.' not in d: return False
- return True
- def generate_and_apply_rpz():
- lists = load_adblock_lists()
- active_lists = [l for l in lists if l.get('enabled')]
- custom_rules = load_custom_rules()
-
- domains_set = set()
-
- try:
- # 1. Fetch Lists
- for lst in active_lists:
- req = urllib.request.Request(lst['url'], headers={'User-Agent': 'Mozilla/5.0'})
- try:
- with urllib.request.urlopen(req, timeout=15, context=config.ssl_ctx) as response:
- lines = response.read().decode('utf-8', errors='ignore').splitlines()
- for line in lines:
- l = line.split('#')[0].strip() # Κόβουμε inline comments
-
- # Γρήγορα φίλτρα (Αγνοούμε σχόλια, HTML, και Regex τύπου Pi-hole)
- if not l or l.startswith(('!', ':', '[', '@')): continue
- if '/' in l or '*' in l or '|' in l and not l.startswith('||'): continue
-
- d = ""
- if l.startswith('||'):
- d = re.split(r'[\^/\$:]', l[2:])[0]
- elif l.startswith(('0.0.0.0 ', '127.0.0.1 ')):
- parts = l.split()
- if len(parts) >= 2 and parts[1] not in ('localhost', 'broadcasthost', 'localhost.localdomain'):
- d = parts[1]
- elif ' ' not in l:
- d = re.split(r'[\^/\$:]', l)[0]
-
- d = d.strip().rstrip('.')
- # Αυστηρός Έλεγχος (Strict Validation)
- if is_valid_domain(d):
- domains_set.add(d)
- except Exception as e:
- print(f"Failed to fetch {lst['name']}: {e}")
- # 2. Add Custom Deny
- for d in custom_rules.get('deny', []):
- clean_d = d.strip().rstrip('.')
- if clean_d.startswith('*.'): clean_d = clean_d[2:]
- if is_valid_domain(clean_d):
- domains_set.add(clean_d)
- # 3. Create RPZ
- rpz_records = []
-
- # Μετατροπή allowed λίστας σε SET + καθάρισμα (Για ακαριαία ταχύτητα O(1))
- allowed_cleaned = set()
- for d in custom_rules.get('allowed', []):
- clean_d = d.strip().rstrip('.')
- if clean_d.startswith('*.'): clean_d = clean_d[2:]
- if is_valid_domain(clean_d):
- allowed_cleaned.add(clean_d)
-
- # ΠΡΩΤΑ τα Allowed (PASSTHRU)
- for d in allowed_cleaned:
- rpz_records.append(f"{d}\tCNAME\trpz-passthru.")
- rpz_records.append(f"*.{d}\tCNAME\trpz-passthru.")
- # ΜΕΤΑ τα Blocked
- for d in domains_set:
- # Ακαριαίος έλεγχος χάρη στο set()
- if d in allowed_cleaned:
- continue
- rpz_records.append(f"{d}\tCNAME\t.")
- rpz_records.append(f"*.{d}\tCNAME\t.")
- serial = time.strftime('%Y%m%d%H') # Ώρα αντί για 01 για σίγουρο increment
- header = f"$TTL 60\n$ORIGIN rpz.blacklist.\n@ IN SOA localhost. root.localhost. ({serial} 3h 1h 1w 1h)\n IN NS localhost.\n"
- content = header + "\n".join(rpz_records) + "\n"
-
- write_file(config.ADBLOCK_DB_PATH, content)
- subprocess.run(["chown", "bind:bind", config.ADBLOCK_DB_PATH])
-
- # Ενημέρωση named.conf.local
- conf_local = read_file(config.LOCAL_CONF)
- if 'zone "rpz.blacklist"' not in conf_local:
- conf_local += f'\nzone "rpz.blacklist" {{\n type master;\n file "{config.ADBLOCK_DB_PATH}";\n allow-query {{ any; }};\n allow-transfer {{ any; }};\n}};\n'
- write_file(config.LOCAL_CONF, conf_local)
- # Ενημέρωση named.conf.options
- options = read_file(config.OPTIONS_FILE)
- if "response-policy" not in options:
- options = re.sub(r'(options\s*\{)', r'\1\n response-policy { zone "rpz.blacklist"; };', options)
- write_file(config.OPTIONS_FILE, options)
- subprocess.run(["rndc", "reconfig"])
- subprocess.run(["rndc", "reload", "rpz.blacklist"])
-
- s = load_panel_settings()
- s['adblock_last_update'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
- s['adblock_domains_count'] = len(domains_set)
- save_panel_settings(s)
-
- return {"status": "success", "count": len(domains_set), "time": s['adblock_last_update']}
- except Exception as e:
- return {"status": "error", "message": str(e)}
- def disable_rpz():
- conf_local = read_file(config.LOCAL_CONF)
- options = read_file(config.OPTIONS_FILE)
-
- block = get_zone_block(conf_local, "rpz.blacklist")
- if block:
- conf_local = conf_local.replace(block, "")
- write_file(config.LOCAL_CONF, conf_local)
-
- options = re.sub(r'\s*response-policy\s*\{\s*zone\s*"rpz\.blacklist";\s*\};\s*', '\n', options)
- write_file(config.OPTIONS_FILE, options)
-
- subprocess.run(["rndc", "reconfig"])
- return {"status": "success", "message": "AdBlock (RPZ) disabled."}
|