| import os |
| import requests |
| import json |
| import subprocess |
| import time |
| import socket |
| import base64 |
| from flask import Flask, request, Response |
|
|
| |
| PROXY_LIST_URL = "https://raw.githubusercontent.com/igareck/vpn-configs-for-russia/refs/heads/main/BLACK_SS%2BAll_RUS.txt" |
| |
| TEST_URL = "https://www.google.com" |
|
|
| app = Flask(__name__) |
|
|
| def safe_base64_decode(s): |
| padding = 4 - (len(s) % 4) |
| if padding != 4: |
| s += "=" * padding |
| return base64.urlsafe_b64decode(s).decode('utf-8') |
|
|
| def parse_ss(ss_link): |
| try: |
| link = ss_link.replace("ss://", "") |
| if "#" in link: link = link.split("#")[0] |
| if "@" not in link: return None |
| user_info_b64, host_port = link.split("@", 1) |
| try: |
| user_info = safe_base64_decode(user_info_b64) |
| except: |
| user_info = user_info_b64 |
| method, password = user_info.split(":", 1) |
| host_str, port_str = host_port.rsplit(":", 1) |
| port = int(port_str.split("?")[0]) |
| |
| return { |
| "address": host_str, |
| "port": port, |
| "method": method, |
| "password": password, |
| "uot": False |
| } |
| except: |
| return None |
|
|
| |
| def create_config(server_obj): |
| config = { |
| "log": {"loglevel": "warning"}, |
| "inbounds": [ |
| |
| |
| { |
| "port": 5353, |
| "protocol": "dokodemo-door", |
| "settings": {"network": "tcp", "address": "8.8.8.8", "port": 53}, |
| "tag": "dns-in" |
| }, |
| |
| { |
| "port": 10808, |
| "protocol": "socks", |
| "settings": {"auth": "noauth", "udp": False}, |
| "tag": "check-in" |
| } |
| ], |
| "outbounds": [ |
| { |
| "protocol": "shadowsocks", |
| "settings": {"servers": [server_obj]}, |
| "tag": "proxy" |
| }, |
| {"protocol": "freedom", "tag": "direct"} |
| ], |
| "routing": { |
| "rules": [ |
| {"type": "field", "inboundTag": ["dns-in", "check-in"], "outboundTag": "proxy"} |
| ] |
| } |
| } |
| with open("config.json", "w") as f: |
| json.dump(config, f, indent=4) |
|
|
| |
| def check_proxy(): |
| print("Проверка соединения через прокси...") |
| proxies = { |
| 'http': 'socks5h://127.0.0.1:10808', |
| 'https': 'socks5h://127.0.0.1:10808' |
| } |
| try: |
| r = requests.get(TEST_URL, proxies=proxies, timeout=5) |
| if r.status_code == 200: |
| return True |
| except Exception as e: |
| print(f"Тест провален: {e}") |
| return False |
|
|
| |
| def find_working_proxy(): |
| print("Скачивание списка...") |
| try: |
| r = requests.get(PROXY_LIST_URL) |
| lines = r.text.splitlines() |
| except: |
| print("Ошибка сети при скачивании листа") |
| return False |
|
|
| candidates = [] |
| for line in lines: |
| if line.strip().startswith("ss://"): |
| parsed = parse_ss(line.strip()) |
| if parsed: candidates.append(parsed) |
| |
| print(f"Найдено {len(candidates)} ключей Shadowsocks. Начинаем перебор...") |
|
|
| xray_process = None |
|
|
| |
| for i, server in enumerate(candidates[:20]): |
| print(f"[{i+1}] Тестируем: {server['address']}...") |
| |
| create_config(server) |
| |
| if xray_process: |
| xray_process.terminate() |
| xray_process.wait() |
| |
| xray_process = subprocess.Popen(["/usr/local/bin/xray", "run", "-c", "config.json"]) |
| time.sleep(2) |
| |
| if check_proxy(): |
| print(f"✅ УСПЕХ! Рабочий сервер найден: {server['address']}") |
| return True |
| else: |
| print("❌ Не работает.") |
| |
| if xray_process: xray_process.terminate() |
| return False |
|
|
| |
| @app.route("/dns-query", methods=["GET", "POST"]) |
| def dns_query(): |
| |
| if request.method == "POST": |
| dns_msg = request.data |
| else: |
| dns_base64 = request.args.get("dns") |
| if not dns_base64: return "Error", 400 |
| padding = 4 - (len(dns_base64) % 4) |
| if padding != 4: dns_base64 += "=" * padding |
| try: |
| dns_msg = base64.urlsafe_b64decode(dns_base64) |
| except: return "Invalid B64", 400 |
|
|
| |
| sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
| sock.settimeout(5) |
| try: |
| sock.connect(("127.0.0.1", 5353)) |
| |
| |
| length_bytes = len(dns_msg).to_bytes(2, byteorder='big') |
| sock.sendall(length_bytes + dns_msg) |
| |
| |
| resp_len_bytes = sock.recv(2) |
| if not resp_len_bytes: |
| return "Empty response from DNS", 502 |
| |
| resp_len = int.from_bytes(resp_len_bytes, byteorder='big') |
| |
| |
| response_data = b"" |
| while len(response_data) < resp_len: |
| chunk = sock.recv(resp_len - len(response_data)) |
| if not chunk: break |
| response_data += chunk |
| |
| return Response(response_data, mimetype="application/dns-message") |
| |
| except Exception as e: |
| return f"DNS TCP Error: {e}", 502 |
| finally: |
| sock.close() |
|
|
| @app.route("/") |
| def index(): |
| return "DoH Server (TCP Mode) Running." |
|
|
| if __name__ == "__main__": |
| if find_working_proxy(): |
| app.run(host="0.0.0.0", port=7860) |
| else: |
| print("FATAL: Нет рабочих прокси.") |