dpibreak 0.6.1

fast and easy-to-use DPI circumvention tool in Rust.
#!/usr/bin/env python3
import asyncio
import httpx
import sys
from urllib.parse import urlparse
import socket

SUCCESS_CODES = {200, 403, 404, 405}

async def check_url(client, url):
    url = url.strip()
    if not url:
        return None
    if not url.startswith(('http://', 'https://')):
        url = 'https://' + url

    host = urlparse(url).hostname
    try:
        ip = socket.gethostbyname(host)
    except Exception as e:
        ip = str(e)
    try:
        response = await client.head(url, timeout=5.0, follow_redirects=False)
        print(f"{url}: {ip}: {response.status_code}", file=sys.stderr)
        return (True, response.status_code, ip, url)

    except Exception as e:
        print(f"{url}: {ip}: {type(e).__name__}", file=sys.stderr)
        return (False, type(e).__name__, ip, url)

async def main():
    urls = sys.stdin.readlines()
    if not urls:
        print("No url provided.")
        return

    async with httpx.AsyncClient() as client:
        tasks = [check_url(client, url) for url in urls]
        results = await asyncio.gather(*tasks)

    results = [r for r in results if r is not None]
    ok = [r for r in results if r[0]]
    fail = [r for r in results if not r[0]]

    print('====================failed====================', file=sys.stderr)
    for _, code, ip, url in fail:
        print(f"{url}: {ip}: {code}")

    print(f"\n{len(ok)}/{len(results)} OK", file=sys.stderr)

if __name__ == "__main__":
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        pass