| import ipaddress |
| import maxminddb |
| from fastapi import FastAPI, Request |
| import json |
| import datetime |
| import logging |
| import sys |
| import os |
| from logging.handlers import RotatingFileHandler |
|
|
| LOG_FILE = os.path.join('/code', 'ip_query.log') |
|
|
| try: |
| formatter = logging.Formatter('%(message)s') |
| log_handler = RotatingFileHandler( |
| LOG_FILE, |
| maxBytes=10*1024*1024, |
| backupCount=5, |
| encoding='utf-8' |
| ) |
| log_handler.setFormatter(formatter) |
| logger = logging.getLogger('ip_query') |
| logger.setLevel(logging.INFO) |
| logger.addHandler(log_handler) |
| startup_log = { |
| "时间": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), |
| "事件": "系统启动", |
| "状态": "成功" |
| } |
| logger.info(json.dumps(startup_log, ensure_ascii=False)) |
| except Exception as e: |
| print(f"日志初始化失败: {e}") |
| sys.exit(1) |
|
|
| city_reader = maxminddb.open_database('GeoLite2-City.mmdb') |
| asn_reader = maxminddb.open_database('GeoLite2-ASN.mmdb') |
| cn_reader = maxminddb.open_database('GeoCN.mmdb') |
| lang = ["zh-CN", "en"] |
| asn_map = { |
| 9812: "东方有线", |
| 9389: "中国长城", |
| 17962: "天威视讯", |
| 17429: "歌华有线", |
| 7497: "科技网", |
| 24139: "华数", |
| 9801: "中关村", |
| 4538: "教育网", |
| 24151: "CNNIC", |
| 38019: "中国移动", 139080: "中国移动", 9808: "中国移动", 24400: "中国移动", 134810: "中国移动", 24547: "中国移动", |
| 56040: "中国移动", 56041: "中国移动", 56042: "中国移动", 56044: "中国移动", 132525: "中国移动", 56046: "中国移动", |
| 56047: "中国移动", 56048: "中国移动", 59257: "中国移动", 24444: "中国移动", |
| 24445: "中国移动", 137872: "中国移动", 9231: "中国移动", 58453: "中国移动", |
| 4134: "中国电信", 4812: "中国电信", 23724: "中国电信", 136188: "中国电信", 137693: "中国电信", 17638: "中国电信", |
| 140553: "中国电信", 4847: "中国电信", 140061: "中国电信", 136195: "中国电信", 17799: "中国电信", 139018: "中国电信", |
| 134764: "中国电信", 4837: "中国联通", 4808: "中国联通", 134542: "中国联通", 134543: "中国联通", |
| 59019: "金山云", |
| 135377: "优刻云", |
| 45062: "网易云", |
| 37963: "阿里云", 45102: "阿里云国际", |
| 45090: "腾讯云", 132203: "腾讯云国际", |
| 55967: "百度云", 38365: "百度云", |
| 58519: "华为云", 55990: "华为云", 136907: "华为云", |
| 4609: "澳門電訊", |
| 13335: "Cloudflare", |
| 55960: "亚马逊云", 14618: "亚马逊云", 16509: "亚马逊云", |
| 15169: "谷歌云", 396982: "谷歌云", 36492: "谷歌云", |
| } |
|
|
| def get_as_info(number): |
| r = asn_map.get(number) |
| if r: |
| return r |
|
|
| def get_des(d): |
| for i in lang: |
| if i in d['names']: |
| return d['names'][i] |
| return d['names']['en'] |
|
|
| def get_country(d): |
| r = get_des(d) |
| if r in ["香港", "澳门", "台湾"]: |
| return "中国" + r |
| return r |
|
|
| def province_match(s): |
| arr = ['内蒙古', '黑龙江', '河北', '山西', '吉林', '辽宁', '江苏', '浙江', '安徽', '福建', '江西', '山东', '河南', '湖北', '湖南', '广东', '海南', '四川', '贵州', '云南', '陕西', '甘肃', '青海', '广西', '西藏', '宁夏', '新疆', '北京', '天津', '上海', '重庆'] |
| for i in arr: |
| if i in s: |
| return i |
| return '' |
|
|
| def de_duplicate(regions): |
| regions = filter(bool, regions) |
| ret = [] |
| [ret.append(i) for i in regions if i not in ret] |
| return ret |
|
|
| def get_addr(ip, mask): |
| network = ipaddress.ip_network(f"{ip}/{mask}", strict=False) |
| first_ip = network.network_address |
| return f"{first_ip}/{mask}" |
|
|
| def get_maxmind(ip: str): |
| ret = {"ip": ip} |
| asn_info = asn_reader.get(ip) |
| if asn_info: |
| as_ = {"number": asn_info["autonomous_system_number"], "name": asn_info["autonomous_system_organization"]} |
| info = get_as_info(as_["number"]) |
| if info: |
| as_["info"] = info |
| ret["as"] = as_ |
|
|
| city_info, prefix = city_reader.get_with_prefix_len(ip) |
| ret["addr"] = get_addr(ip, prefix) |
| if not city_info: |
| return ret |
| |
| if "location" in city_info: |
| location = city_info["location"] |
| ret["location"] = { |
| "latitude": location.get("latitude"), |
| "longitude": location.get("longitude") |
| } |
| |
| if "country" in city_info: |
| country_code = city_info["country"]["iso_code"] |
| country_name = get_country(city_info["country"]) |
| ret["country"] = {"code": country_code, "name": country_name} |
| |
| if "registered_country" in city_info: |
| registered_country_code = city_info["registered_country"]["iso_code"] |
| ret["registered_country"] = {"code": registered_country_code, "name": get_country(city_info["registered_country"])} |
| |
| regions = [get_des(i) for i in city_info.get('subdivisions', [])] |
|
|
| if "city" in city_info: |
| c = get_des(city_info["city"]) |
| if (not regions or c not in regions[-1]) and c not in country_name: |
| regions.append(c) |
| |
| regions = de_duplicate(regions) |
| if regions: |
| ret["regions"] = regions |
| |
| return ret |
|
|
| def get_cn(ip: str, info={}): |
| ret, prefix = cn_reader.get_with_prefix_len(ip) |
| if not ret: |
| return |
| info["addr"] = get_addr(ip, prefix) |
| regions = de_duplicate([ret["province"], ret["city"], ret["districts"]]) |
| if regions: |
| info["regions"] = regions |
| info["regions_short"] = de_duplicate([province_match(ret["province"]), ret["city"].replace('市', ''), ret["districts"]]) |
| if "as" not in info: |
| info["as"] = {} |
| info["as"]["info"] = ret['isp'] |
| if ret['net']: |
| info["type"] = ret['net'] |
| return ret |
|
|
| def get_ip_info(ip): |
| info = get_maxmind(ip) |
| if "country" in info and info["country"]["code"] == "CN" and ("registered_country" not in info or info["registered_country"]["code"] == "CN"): |
| get_cn(ip, info) |
| return info |
|
|
| def query(): |
| while True: |
| try: |
| ip = input('IP: \t').strip() |
| info = get_ip_info(ip) |
| print(f"网段:\t{info['addr']}") |
| if "location" in info: |
| print(f"经纬度:\t{info['location']['latitude']}, {info['location']['longitude']}") |
| if "as" in info: |
| print(f"ISP:\t", end=' ') |
| if "info" in info["as"]: |
| print(info["as"]["info"], end=' ') |
| else: |
| print(info["as"]["name"], end=' ') |
| if "type" in info: |
| print(f"({info['type']})", end=' ') |
| print(f"ASN{info['as']['number']}", end=' ') |
| print(info['as']["name"]) |
| if "registered_country" in info and ("country" not in info or info["country"]["code"] != info["registered_country"]["code"]): |
| print(f"注册地:\t{info['registered_country']['name']}") |
| if "country" in info: |
| print(f"使用地:\t{info['country']['name']}") |
| if "regions" in info: |
| print(f"位置: \t{' '.join(info['regions'])}") |
| except Exception as e: |
| print(e) |
| raise e |
| finally: |
| print("\n") |
| |
| app = FastAPI() |
|
|
| @app.get("/") |
| async def api(request: Request, ip: str = None): |
| client_ip = request.headers.get("x-forwarded-for") or request.headers.get("x-real-ip") or request.client.host |
| query_ip = ip.strip() if ip else client_ip |
| result = get_ip_info(query_ip) |
| log_data = { |
| "时间": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), |
| "访问IP": client_ip, |
| "查询IP": query_ip, |
| "请求头": dict(request.headers), |
| "查询结果": result |
| } |
| logger.info(json.dumps(log_data, ensure_ascii=False)) |
| return result |
|
|
| @app.get("/{ip}") |
| async def path_api(request: Request, ip: str): |
| client_ip = request.headers.get("x-forwarded-for") or request.headers.get("x-real-ip") or request.client.host |
| result = get_ip_info(ip) |
| log_data = { |
| "时间": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), |
| "访问IP": client_ip, |
| "查询IP": ip, |
| "请求头": dict(request.headers), |
| "查询结果": result |
| } |
| logger.info(json.dumps(log_data, ensure_ascii=False)) |
| return result |
|
|
| if __name__ == '__main__': |
| query() |
| import uvicorn |
| uvicorn.run(app, host="0.0.0.0", port=8080, server_header=False, proxy_headers=True) |
|
|