Files
2026-06-05 17:26:44 +08:00

79 lines
1.8 KiB
Go

package geo
import (
"context"
"fmt"
"net"
"strings"
"sync"
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
recordstore "speedtest/internal/store"
)
type XDBResolver struct {
mu sync.Mutex
searcher *xdb.Searcher
}
func NewXDBResolver(db []byte) (*XDBResolver, error) {
if len(db) == 0 {
return nil, fmt.Errorf("empty ip2region xdb")
}
searcher, err := xdb.NewWithBuffer(xdb.IPv4, db)
if err != nil {
return nil, fmt.Errorf("create ip2region searcher: %w", err)
}
return &XDBResolver{searcher: searcher}, nil
}
func (r *XDBResolver) Resolve(ctx context.Context, ip string) (recordstore.ClientInfo, error) {
if err := ctx.Err(); err != nil {
return recordstore.ClientInfo{}, err
}
parsed := net.ParseIP(strings.TrimSpace(ip))
if parsed == nil || parsed.To4() == nil {
return recordstore.ClientInfo{}, fmt.Errorf("ip2region supports IPv4 only in embedded database")
}
r.mu.Lock()
region, err := r.searcher.Search(parsed.String())
r.mu.Unlock()
if err != nil {
return recordstore.ClientInfo{}, err
}
if strings.TrimSpace(region) == "" {
return recordstore.ClientInfo{}, fmt.Errorf("ip2region returned empty region")
}
info := clientInfoFromIP2Region(ip, region)
info.Source = "ip2region"
return info, nil
}
func clientInfoFromIP2Region(ip string, region string) recordstore.ClientInfo {
parts := strings.Split(region, "|")
country := field(parts, 0)
regionName := field(parts, 1)
city := field(parts, 2)
isp := field(parts, 3)
return recordstore.ClientInfo{
IP: ip,
ISP: isp,
Region: regionName,
Country: country,
City: city,
}
}
func field(parts []string, index int) string {
if index >= len(parts) {
return ""
}
value := strings.TrimSpace(parts[index])
if value == "0" || value == "未知" || strings.EqualFold(value, "unknown") {
return ""
}
return value
}