Files
myip/app.py
2025-11-27 16:52:24 +00:00

258 lines
8.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from flask import Flask, request, render_template_string
import requests
from werkzeug.middleware.proxy_fix import ProxyFix
app = Flask(__name__)
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1)
RIPESTAT_BASE = "https://stat.ripe.net/data"
HTML_TEMPLATE = """<!doctype html>
<html lang="it">
<head>
<meta charset="utf-8">
<title>SolidData IP & RIPE Lookup</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700&display=swap" rel="stylesheet">
<link rel="icon" type="image/png" href="/static/favicon.png">
<link rel="stylesheet" href="/static/myip.css">
</head>
<body>
<div class="card">
{% if show_logo %}
<a href="https://www.soliddata.cloud" class="logo-link" target="_blank" rel="noopener">
<img src="/static/logo.png" class="logo" alt="Logo">
</a>
{% endif %}
<div class="badge">
<span class="badge-dot"></span>
<span>IP &amp; RIPE Lookup</span>
</div>
<h1 class="title">Il tuo IP pubblico è:</h1>
<div class="ip-wrapper">
<span id="ip-value" class="ip">{{ ip }}</span>
</div>
<button id="copy-btn" class="copy-btn copy-btn-below" type="button">
<span class="copy-icon">📋</span>
<span class="copy-label">Copia IP</span>
</button>
<div class="divider"></div>
{% if ripe %}
<div class="ripe-title">Informazioni RIPE</div>
{% if ripe.holder %}
<div class="ripe-item"><strong>Provider:</strong> {{ ripe.holder }}</div>
{% endif %}
{% if ripe.prefix %}
<div class="ripe-item"><strong>Prefisso annunciato:</strong> {{ ripe.prefix }}</div>
{% endif %}
{% if ripe.asns %}
<div class="ripe-item"><strong>ASN:</strong> {{ ripe.asns | join(', ') }}</div>
{% endif %}
{% if ripe.routing_status %}
<div class="ripe-item"><strong>Routing Status:</strong> {{ ripe.routing_status }}</div>
{% endif %}
{% if ripe.reverse_dns %}
<div class="ripe-item"><strong>Reverse DNS:</strong> {{ ripe.reverse_dns }}</div>
{% endif %}
{% if ripe.geoloc %}
<div class="ripe-item">
<strong>GeoLocation:</strong>
{% if ripe.geoloc.code %}
<img src="https://flagsapi.com/{{ ripe.geoloc.code }}/flat/24.png" class="flag-icon" alt="{{ ripe.geoloc.code }}">
{% endif %}
{{ ripe.geoloc.country }}
</div>
{% endif %}
{% if ripe.abuse %}
<div class="ripe-item"><strong>Abuse Contact:</strong> {{ ripe.abuse }}</div>
{% endif %}
<div class="ripe-muted">Dati ottenuti tramite RIPEstat Data API.</div>
{% else %}
<div class="ripe-title">Informazioni RIPE</div>
<div class="ripe-muted">
Non è stato possibile recuperare i dettagli RIPE per questo indirizzo.
</div>
{% endif %}
</div>
<script>
document.addEventListener("DOMContentLoaded", function () {
var card = document.querySelector(".card");
if (card) card.classList.add("card-visible");
var btn = document.getElementById("copy-btn");
var ipSpan = document.getElementById("ip-value");
if (!btn || !ipSpan) return;
btn.addEventListener("click", function () {
var ip = ipSpan.textContent.trim();
if (!ip) return;
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(ip).then(function () {
var labelEl = btn.querySelector(".copy-label");
var original = labelEl.textContent;
btn.classList.add("copy-btn-success");
labelEl.textContent = "Copiato!";
setTimeout(function () {
btn.classList.remove("copy-btn-success");
labelEl.textContent = original;
}, 1500);
}).catch(console.error);
}
});
});
</script>
</body>
</html>
"""
import ipaddress
from flask import Flask, request, render_template_string
# ...
def _is_private_ip(ip: str) -> bool:
try:
return ipaddress.ip_address(ip).is_private
except ValueError:
# se non è un IP valido, lo consideriamo "non utilizzabile"
return True
def get_client_ip():
"""
Prova a determinare l'IP pubblico del client:
- prende la lista da X-Forwarded-For
- sceglie il primo IP NON privato (non 10.x/192.168/172.16/127.x ecc.)
- fallback su X-Real-IP
- fallback su request.remote_addr
"""
# 1) X-Forwarded-For: "client, proxy1, proxy2..."
xff = request.headers.get("X-Forwarded-For", "")
if xff:
parts = [p.strip() for p in xff.split(",") if p.strip()]
# cerco il primo IP pubblico nella lista
for ip in parts:
if not _is_private_ip(ip):
return ip
# se proprio non trovo nulla di pubblico, prendo il primo
if parts:
return parts[0]
# 2) X-Real-IP
xri = request.headers.get("X-Real-IP")
if xri and not _is_private_ip(xri):
return xri.strip()
# 3) Fallback: remote_addr così com'è
return request.remote_addr or "Sconosciuto"
def fetch_ripe_info(ip: str):
"""Recupera informazioni RIPEstat estese per l'IP."""
info = {
"prefix": None,
"asns": [],
"holder": None,
"routing_status": None,
"reverse_dns": None,
"geoloc": None,
"abuse": None,
}
try:
# 1. Network info (prefisso + ASN)
ni = requests.get(
f"{RIPESTAT_BASE}/network-info/data.json",
params={"resource": ip},
timeout=2,
).json().get("data", {})
info["prefix"] = ni.get("prefix")
asns = ni.get("asns") or []
info["asns"] = [f"AS{a}" for a in asns]
# 2. AS overview (holder/provider)
if asns:
ao = requests.get(
f"{RIPESTAT_BASE}/as-overview/data.json",
params={"resource": asns[0]},
timeout=2,
).json().get("data", {})
info["holder"] = ao.get("holder")
# 3. Routing status / visibility
rs = requests.get(
f"{RIPESTAT_BASE}/routing-status/data.json",
params={"resource": ip},
timeout=2,
).json().get("data", {})
info["routing_status"] = rs.get("status")
# 4. Reverse DNS
rd = requests.get(
f"{RIPESTAT_BASE}/reverse-dns/data.json",
params={"resource": ip},
timeout=2,
).json().get("data", {}).get("result", [])
info["reverse_dns"] = rd[0].get("name") if rd else None
# 5. GeoLocation
gl = requests.get(
f"{RIPESTAT_BASE}/geoloc/data.json",
params={"resource": ip},
timeout=2,
).json().get("data", {})
country = None
if gl.get("locations"):
country = gl["locations"][0].get("country", {})
if country:
info["geoloc"] = {
"country": country.get("name"),
"code": country.get("code"),
}
# 6. Abuse Contact
ac = requests.get(
f"{RIPESTAT_BASE}/abuse-contact-finder/data.json",
params={"resource": ip},
timeout=2,
).json().get("data", {})
emails = ac.get("emails") or []
info["abuse"] = emails[0] if emails else None
except Exception:
# In caso di errore lasciamo info parziali / vuote
pass
return info
@app.route("/")
def index():
ip = get_client_ip()
ripe_info = fetch_ripe_info(ip) if ip != "Sconosciuto" else None
return render_template_string(
HTML_TEMPLATE,
ip=ip,
ripe=ripe_info,
show_logo=True,
)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000)