Non è stato possibile recuperare i dettagli RIPE per questo indirizzo.
{% endif %}
"""
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 get_effective_ip():
"""
Restituisce (ip, overridden)
- ip: indirizzo IP da usare per la lookup
- overridden: True se l'IP è stato forzato via ?checkip=
"""
override = request.args.get("checkip")
if override:
override = override.strip()
try:
# valida che sia un IP v4/v6
ipaddress.ip_address(override)
return override, True
except ValueError:
# se non è valido, ignora e usa il flusso normale
pass
# default: uso l'IP reale del client
return get_client_ip(), False
def fetch_ripe_info(ip: str):
"""Recupera informazioni RIPEstat estese per l'IP, compatibili col template."""
info = {
"prefix": None,
"asns": [],
"holder": None,
"routing_status": None,
"reverse_dns": None,
"geoloc": None,
"abuse": None,
# campi extra che stai già usando in pagina
"block": None,
"rir": None,
"country": None, # non usato ora in UI
"country_whois": None, # usato in "Country (WHOIS)"
"netname": None, # usato in "Netname"
"org_name": None,
"raw": {}, # solo per debug
}
try:
# 0) PREFIX-OVERVIEW: blocco IP + RIR + stato announced/routed
po_resp = requests.get(
f"{RIPESTAT_BASE}/prefix-overview/data.json",
params={"resource": ip},
timeout=2,
)
po = po_resp.json().get("data", {})
info["raw"]["prefix_overview"] = po
if po:
# es: 77.43.64.0/18
info["block"] = po.get("resource")
# es: block.desc = "RIPE NCC (Status: ALLOCATED)"
block = po.get("block") or {}
desc = block.get("desc")
if desc:
# prendiamo la parte prima della parentesi
info["rir"] = desc.split("(")[0].strip()
# announced / routed (bool)
info["routing_status"] = None
if po.get("announced") is not None:
info["routing_status"] = "Announced" if po.get("announced") else "Not announced"
# prefix principale
if not info["prefix"]:
info["prefix"] = po.get("resource")
# 1) NETWORK-INFO: prefisso + ASNs
ni_resp = requests.get(
f"{RIPESTAT_BASE}/network-info/data.json",
params={"resource": ip},
timeout=2,
)
ni = ni_resp.json().get("data", {})
info["raw"]["network_info"] = ni
if ni:
if not info["prefix"]:
info["prefix"] = ni.get("prefix")
asns = ni.get("asns") or []
info["asns"] = [f"AS{a}" for a in asns]
# 2) AS-OVERVIEW: holder/provider (es: "AS-IRIDEOS - Retelit Digital Services S.p.A.")
if ni and ni.get("asns"):
ao_resp = requests.get(
f"{RIPESTAT_BASE}/as-overview/data.json",
params={"resource": ni["asns"][0]},
timeout=2,
)
ao = ao_resp.json().get("data", {})
info["raw"]["as_overview"] = ao
if ao:
info["holder"] = ao.get("holder") or info["holder"]
# 3) ROUTING-STATUS: origin AS + visibilità
rs_resp = requests.get(
f"{RIPESTAT_BASE}/routing-status/data.json",
params={"resource": info["prefix"] or ip},
timeout=2,
)
rs = rs_resp.json().get("data", {})
info["raw"]["routing_status"] = rs
if rs:
origins = rs.get("origins") or []
origin_as = None
if origins:
origin_as = origins[0].get("origin")
v4 = (rs.get("visibility") or {}).get("v4") or {}
peers = v4.get("ris_peers_seeing")
total = v4.get("total_ris_peers")
if origin_as and peers is not None and total is not None:
info["routing_status"] = f"Origin AS{origin_as}, visibility {peers}/{total} peers"
elif origin_as:
info["routing_status"] = f"Origin AS{origin_as}"
# 4) REVERSE-DNS
rd_resp = requests.get(
f"{RIPESTAT_BASE}/reverse-dns/data.json",
params={"resource": ip},
timeout=2,
)
rd = rd_resp.json().get("data", {}).get("result", [])
info["raw"]["reverse_dns"] = rd
if rd:
info["reverse_dns"] = rd[0].get("name")
# 5) GEOLOC (usa located_resources → locations)
gl_resp = requests.get(
f"{RIPESTAT_BASE}/geoloc/data.json",
params={"resource": ip},
timeout=2,
)
gl = gl_resp.json().get("data", {})
info["raw"]["geoloc"] = gl
located_resources = gl.get("located_resources") or []
if located_resources:
first_res = located_resources[0]
locs = first_res.get("locations") or []
if locs:
loc0 = locs[0]
city = loc0.get("city")
country_code = loc0.get("country")
# flagsapi vuole il codice (es: IT)
label = country_code
if city and country_code:
label = f"{city}, {country_code}"
info["geoloc"] = {
"country": label,
"code": country_code,
}
# 6) ABUSE CONTACT (abuse_contacts, non emails)
ac_resp = requests.get(
f"{RIPESTAT_BASE}/abuse-contact-finder/data.json",
params={"resource": ip},
timeout=2,
)
ac = ac_resp.json().get("data", {})
info["raw"]["abuse"] = ac
abuse_contacts = ac.get("abuse_contacts") or []
if abuse_contacts:
info["abuse"] = abuse_contacts[0]
# 7) WHOIS: netname, country (WHOIS), org / descr
whois_resp = requests.get(
f"{RIPESTAT_BASE}/whois/data.json",
params={"resource": ip},
timeout=3,
)
whois = whois_resp.json().get("data", {})
info["raw"]["whois"] = whois
records = whois.get("records") or []
for block in records:
for entry in block:
k = entry.get("key", "").lower()
v = entry.get("value", "")
if k == "netname" and not info["netname"]:
info["netname"] = v
elif k == "country" and not info["country_whois"]:
info["country_whois"] = v
elif k in ("org-name", "organisation", "descr") and not info["org_name"]:
info["org_name"] = v
except Exception:
pass
return info
@app.route("/")
def index():
ip, overridden = get_effective_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,
override=overridden,
)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000)