from flask import Flask, jsonify, render_template_string import subprocess import threading import queue import requests import psycopg2 from psycopg2 import pool import time from datetime import datetime import re # --- Config --- MASSCAN_RATE = 10000 # Reduced to avoid ISP blocks TARGET_PORT = 11434 # Ollama default port TARGET_RANGE = "0.0.0.0/0" # Scan everything NUM_CHECKERS = 50 # Workers PROCESSED_IPS_LIMIT = 1000000 # --- DB Config --- DB_URL = "postgresql://neondb_owner:npg_r7oFwW5XsmtG@ep-patient-lake-agwy3kca-pooler.c-2.eu-central-1.aws.neon.tech/ollama?sslmode=require" # --- Global State --- processed_ips = set() ip_queue = queue.Queue() active_checks = 0 stats = { "found": 0, "verified_models": 0, "verification_failed": 0, "api_success": 0, "api_failed": 0, "cache_clears": 0, "last_scan_time": time.time(), } # --- DB Connection Pool --- db_pool = psycopg2.pool.SimpleConnectionPool( minconn=1, maxconn=10, dsn=DB_URL ) # --- Flask App --- app = Flask(__name__) # --- Homepage (Real-Time Stats) --- @app.route('/') def home(): return render_template_string(''' Niansuh Masscan - Live Stats

NIANSUH MASSCAN - LIVE STATS

🔍 Scanned IPs: {{ stats.found }}

Verified Ollama: {{ stats.verified_models }}

🚀 Queue: {{ queue_size }}

🔄 Active Checks: {{ active_checks }}

🗃️ In Memory: {{ in_memory }}

⏱️ Uptime: {{ uptime }} seconds

''', stats=stats, queue_size=ip_queue.qsize(), active_checks=active_checks, in_memory=len(processed_ips), uptime=int(time.time() - stats["last_scan_time"])) # --- Stats API --- @app.route('/v1/stats') def get_stats(): return jsonify({ **stats, "queue_size": ip_queue.qsize(), "active_checks": active_checks, "in_memory": len(processed_ips), "uptime": int(time.time() - stats["last_scan_time"]) }) # --- API Endpoint (Save IPs) --- @app.route('/add-provider', methods=['POST']) def add_provider(): ip = request.json.get('ip') if not ip: stats["api_failed"] += 1 return jsonify({"error": "No IP provided"}), 400 conn = db_pool.getconn() try: with conn.cursor() as cur: cur.execute(""" INSERT INTO providers (ip, port, first_seen, last_seen) VALUES (%s, %s, %s, %s) ON CONFLICT (ip) DO UPDATE SET last_seen = %s """, (ip, TARGET_PORT, datetime.utcnow(), datetime.utcnow(), datetime.utcnow())) conn.commit() stats["api_success"] += 1 return jsonify({"status": "success"}), 200 except Exception as e: print(f"DB Error: {e}") stats["api_failed"] += 1 return jsonify({"error": str(e)}), 500 finally: db_pool.putconn(conn) # --- Verify Ollama Instance --- def verify_and_send_ip(ip): global active_checks active_checks += 1 url = f"http://{ip}:{TARGET_PORT}/api/tags" try: response = requests.get(url, timeout=3) if response.status_code == 200 and "models" in response.json(): stats["verified_models"] += 1 requests.post("http://127.0.0.1:5000/add-provider", json={"ip": ip}, timeout=2) except: stats["verification_failed"] += 1 finally: active_checks -= 1 # --- Worker (Process IPs) --- def worker(): while True: if not ip_queue.empty(): ip = ip_queue.get() verify_and_send_ip(ip) time.sleep(0.01) # --- Masscan Runner (Now Works) --- def run_masscan(): print("=== NIANSUH MASSCAN STARTED ===") print(f"Scanning {TARGET_RANGE} at {MASSCAN_RATE} pps...") # Run masscan with sudo (required for raw sockets) process = subprocess.Popen( ["sudo", "masscan", TARGET_RANGE, "-p", str(TARGET_PORT), "--rate", str(MASSCAN_RATE), "--output-format", "list"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True ) for line in process.stdout: line = line.strip() if re.match(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$", line): # Match IPs if line not in processed_ips: processed_ips.add(line) stats["found"] += 1 ip_queue.put(line) if len(processed_ips) >= PROCESSED_IPS_LIMIT: processed_ips.clear() stats["cache_clears"] += 1 print("Masscan stopped. Restarting in 5 sec...") time.sleep(5) run_masscan() # --- Start Everything --- if __name__ == '__main__': # Start workers for _ in range(NUM_CHECKERS): threading.Thread(target=worker, daemon=True).start() # Start masscan in a separate thread threading.Thread(target=run_masscan, daemon=True).start() # Start Flask app.run(host='0.0.0.0', port=5000, threaded=True)