|
|
from flask import Flask, render_template_string |
|
|
import subprocess |
|
|
import threading |
|
|
import queue |
|
|
import requests |
|
|
import time |
|
|
import re |
|
|
from datetime import datetime |
|
|
import psycopg2 |
|
|
from psycopg2 import pool |
|
|
|
|
|
|
|
|
TARGET_PORT = 11434 |
|
|
NUM_CHECKERS = 50 |
|
|
DB_URL = "postgresql://neondb_owner:npg_r7oFwW5XsmtG@ep-patient-lake-agwy3kca-pooler.c-2.eu-central-1.aws.neon.tech/ollama?sslmode=require" |
|
|
|
|
|
|
|
|
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, |
|
|
"start_time": time.time() |
|
|
} |
|
|
|
|
|
|
|
|
db_pool = psycopg2.pool.SimpleConnectionPool( |
|
|
minconn=1, |
|
|
maxconn=10, |
|
|
dsn=DB_URL |
|
|
) |
|
|
|
|
|
|
|
|
app = Flask(__name__) |
|
|
|
|
|
|
|
|
@app.route('/') |
|
|
def home(): |
|
|
uptime = int(time.time() - stats["start_time"]) |
|
|
return render_template_string(''' |
|
|
<!DOCTYPE html> |
|
|
<html> |
|
|
<head> |
|
|
<title>Niansuh ZMap Scanner</title> |
|
|
<meta http-equiv="refresh" content="1"> |
|
|
<style> |
|
|
body { font-family: monospace; background: #000; color: #0f0; padding: 20px; } |
|
|
.stats { font-size: 1.5em; line-height: 1.8; } |
|
|
.scanned { color: #ff0; } |
|
|
.verified { color: #0ff; } |
|
|
.queue { color: #f0f; } |
|
|
h1 { color: #f00; } |
|
|
</style> |
|
|
</head> |
|
|
<body> |
|
|
<h1>NIANSUH ZMAP SCANNER</h1> |
|
|
<div class="stats"> |
|
|
<p>π Scanned IPs: <span class="scanned">{{ stats.found }}</span></p> |
|
|
<p>β
Verified Ollama: <span class="verified">{{ stats.verified_models }}</span></p> |
|
|
<p>π Queue Size: <span class="queue">{{ queue_size }}</span></p> |
|
|
<p>π Active Checks: {{ active_checks }}</p> |
|
|
<p>β±οΈ Uptime: {{ uptime }}s</p> |
|
|
</div> |
|
|
</body> |
|
|
</html> |
|
|
''', stats=stats, queue_size=ip_queue.qsize(), active_checks=active_checks, uptime=uptime) |
|
|
|
|
|
|
|
|
def verify_ollama(ip): |
|
|
global active_checks, stats |
|
|
active_checks += 1 |
|
|
try: |
|
|
url = f"http://{ip}:{TARGET_PORT}/api/tags" |
|
|
response = requests.get(url, timeout=3) |
|
|
if response.status_code == 200 and "models" in response.json(): |
|
|
stats["verified_models"] += 1 |
|
|
conn = db_pool.getconn() |
|
|
try: |
|
|
with conn.cursor() as cur: |
|
|
cur.execute(""" |
|
|
INSERT INTO providers (ip, port, first_seen) |
|
|
VALUES (%s, %s, %s) |
|
|
ON CONFLICT (ip) DO NOTHING |
|
|
""", (ip, TARGET_PORT, datetime.utcnow())) |
|
|
conn.commit() |
|
|
stats["api_success"] += 1 |
|
|
except Exception as e: |
|
|
print(f"DB Error: {e}") |
|
|
stats["api_failed"] += 1 |
|
|
finally: |
|
|
db_pool.putconn(conn) |
|
|
except: |
|
|
stats["verification_failed"] += 1 |
|
|
finally: |
|
|
active_checks -= 1 |
|
|
|
|
|
|
|
|
def worker(): |
|
|
while True: |
|
|
if not ip_queue.empty(): |
|
|
ip = ip_queue.get() |
|
|
verify_ollama(ip) |
|
|
time.sleep(0.01) |
|
|
|
|
|
|
|
|
def run_zmap(): |
|
|
print("=== Starting ZMap Scan ===") |
|
|
process = subprocess.Popen( |
|
|
["sudo", "zmap", "-p", str(TARGET_PORT), "-o", "-"], |
|
|
stdout=subprocess.PIPE, |
|
|
stderr=subprocess.PIPE, |
|
|
universal_newlines=True |
|
|
) |
|
|
|
|
|
for line in process.stdout: |
|
|
ip = line.strip() |
|
|
if re.match(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$", ip): |
|
|
if ip not in processed_ips: |
|
|
processed_ips.add(ip) |
|
|
stats["found"] += 1 |
|
|
ip_queue.put(ip) |
|
|
|
|
|
print("ZMap finished. Restarting in 5 sec...") |
|
|
time.sleep(5) |
|
|
run_zmap() |
|
|
|
|
|
|
|
|
if __name__ == '__main__': |
|
|
|
|
|
for _ in range(NUM_CHECKERS): |
|
|
threading.Thread(target=worker, daemon=True).start() |
|
|
|
|
|
|
|
|
threading.Thread(target=run_zmap, daemon=True).start() |
|
|
|
|
|
app.run(host='0.0.0.0', port=5000, threaded=True) |
|
|
|