79 lines
1.8 KiB
Go
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
|
|
}
|