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 = """ SolidData – IP & RIPE Lookup
{% if show_logo %} {% endif %}
IP & RIPE Lookup

Il tuo IP pubblico è:

{{ ip }}
{% if ripe %}
Informazioni RIPE
{% if ripe.holder %}
Provider: {{ ripe.holder }}
{% endif %} {% if ripe.routing_status %}
Routing Status: {{ ripe.routing_status }}
{% endif %} {% if ripe.reverse_dns %}
Reverse DNS: {{ ripe.reverse_dns }}
{% endif %} {% if ripe.geoloc %}
GeoLocation: {% if ripe.geoloc.code %} {{ ripe.geoloc.code }} {% endif %} {{ ripe.geoloc.country }}
{% endif %} {% if ripe.abuse %}
Abuse Contact: {{ ripe.abuse }}
{% endif %} {% if ripe.netname %}
Netname: {{ ripe.netname }}
{% endif %} {% if ripe.org_name %}
Organisation: {{ ripe.org_name }}
{% endif %} {% if ripe.rir %}
RIR: {{ ripe.rir }}
{% endif %} {% if ripe.block %}
IP Block: {{ ripe.block }}
{% endif %} {% if ripe.rir %}
RIR: {{ ripe.rir }}
{% endif %}
Dati ottenuti tramite RIPEstat Data API.
{% else %}
Informazioni RIPE
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)