Skip to content

Commit 9c91290

Browse files
committed
feat: IP定位服务 #7
1,实现ip-api.com、ipapi.co、ipwhois.app、ip-api.io、ipinfo.io免费版API 2,实现ipip.net、纯真库API 3,实现ipip.net、纯真离线库
1 parent 274b387 commit 9c91290

13 files changed

Lines changed: 2088 additions & 28 deletions
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
using System;
2+
using System.Net.Http;
3+
using System.Text.Json;
4+
using System.Threading.Tasks;
5+
using Microsoft.Extensions.Logging;
6+
7+
namespace Cyaim.WebSocketServer.Infrastructure.AccessControl
8+
{
9+
/// <summary>
10+
/// Geographic location provider using ChunZhen (纯真) IP database online service
11+
/// 使用纯真 IP 数据库在线服务的地理位置提供者
12+
/// Note: This is a placeholder implementation. Actual API may vary / 注意:这是占位符实现,实际 API 可能不同
13+
/// </summary>
14+
public class ChunZhenGeoLocationProvider : IGeoLocationProvider
15+
{
16+
private readonly ILogger<ChunZhenGeoLocationProvider> _logger;
17+
private readonly HttpClient _httpClient;
18+
private readonly string _apiUrl;
19+
20+
/// <summary>
21+
/// Constructor / 构造函数
22+
/// </summary>
23+
/// <param name="logger">Logger instance / 日志实例</param>
24+
/// <param name="httpClient">HTTP client (optional, will create if null) / HTTP 客户端(可选,如果为 null 则创建)</param>
25+
/// <param name="apiUrl">API URL (if available) / API URL(如果可用)</param>
26+
public ChunZhenGeoLocationProvider(
27+
ILogger<ChunZhenGeoLocationProvider> logger,
28+
HttpClient httpClient = null,
29+
string apiUrl = null)
30+
{
31+
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
32+
_httpClient = httpClient ?? new HttpClient();
33+
// Note: ChunZhen may not have a public online API, this is a placeholder
34+
// 注意:纯真可能没有公开的在线 API,这是占位符
35+
_apiUrl = apiUrl;
36+
}
37+
38+
/// <summary>
39+
/// Get geographic location information for an IP address / 获取 IP 地址的地理位置信息
40+
/// </summary>
41+
/// <param name="ipAddress">IP address / IP 地址</param>
42+
/// <returns>Geographic location information or null if not found / 地理位置信息,如果未找到则返回 null</returns>
43+
public async Task<GeoLocationInfo> GetLocationAsync(string ipAddress)
44+
{
45+
if (string.IsNullOrEmpty(ipAddress))
46+
{
47+
return null;
48+
}
49+
50+
try
51+
{
52+
// Skip local/private IPs / 跳过本地/私有 IP
53+
if (IsLocalOrPrivateIp(ipAddress))
54+
{
55+
_logger.LogDebug($"IP {ipAddress} is local/private, skipping geo lookup");
56+
return null;
57+
}
58+
59+
// Note: ChunZhen typically uses offline database (qqwry.dat)
60+
// For online service, you would need to implement the actual API call here
61+
// 注意:纯真通常使用离线数据库(qqwry.dat)
62+
// 对于在线服务,您需要在此处实现实际的 API 调用
63+
64+
if (string.IsNullOrEmpty(_apiUrl))
65+
{
66+
_logger.LogWarning("ChunZhen online API URL is not configured. Please use ChunZhenOfflineGeoLocationProvider instead.");
67+
return null;
68+
}
69+
70+
var url = _apiUrl.Replace("{ip}", ipAddress);
71+
var response = await _httpClient.GetStringAsync(url);
72+
73+
if (string.IsNullOrEmpty(response))
74+
{
75+
return null;
76+
}
77+
78+
var jsonDoc = JsonDocument.Parse(response);
79+
var root = jsonDoc.RootElement;
80+
81+
var geoInfo = new GeoLocationInfo
82+
{
83+
CountryCode = root.TryGetProperty("country_code", out var countryCode)
84+
? countryCode.GetString()
85+
: null,
86+
CountryName = root.TryGetProperty("country", out var country)
87+
? country.GetString()
88+
: null,
89+
RegionName = root.TryGetProperty("region", out var region)
90+
? region.GetString()
91+
: null,
92+
CityName = root.TryGetProperty("city", out var city)
93+
? city.GetString()
94+
: null
95+
};
96+
97+
_logger.LogDebug($"Geo lookup for IP {ipAddress}: {geoInfo.CountryCode}/{geoInfo.CityName}");
98+
return geoInfo;
99+
}
100+
catch (Exception ex)
101+
{
102+
_logger.LogWarning(ex, $"Error getting geographic location for IP {ipAddress}");
103+
return null;
104+
}
105+
}
106+
107+
/// <summary>
108+
/// Check if IP is local or private / 检查 IP 是否为本地或私有
109+
/// </summary>
110+
private bool IsLocalOrPrivateIp(string ipAddress)
111+
{
112+
if (string.IsNullOrEmpty(ipAddress))
113+
return true;
114+
115+
if (ipAddress == "::1" || ipAddress == "127.0.0.1" || ipAddress == "localhost")
116+
return true;
117+
118+
if (System.Net.IPAddress.TryParse(ipAddress, out var ip))
119+
{
120+
// Check for private IP ranges / 检查私有 IP 范围
121+
if (ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
122+
{
123+
var bytes = ip.GetAddressBytes();
124+
// 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
125+
if (bytes[0] == 10 ||
126+
(bytes[0] == 172 && bytes[1] >= 16 && bytes[1] <= 31) ||
127+
(bytes[0] == 192 && bytes[1] == 168))
128+
{
129+
return true;
130+
}
131+
}
132+
}
133+
134+
return false;
135+
}
136+
}
137+
}
138+

0 commit comments

Comments
 (0)