|
|
|
@ -2,6 +2,7 @@ package conf
|
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
|
"encoding/json" |
|
|
|
|
"runtime" |
|
|
|
|
"strconv" |
|
|
|
|
"strings" |
|
|
|
|
|
|
|
|
@ -147,46 +148,109 @@ func ParseIP(s string) (*router.CIDR, error) {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func loadGeoIP(country string) ([]*router.CIDR, error) { |
|
|
|
|
return loadIP("geoip.dat", country) |
|
|
|
|
func loadGeoIP(code string) ([]*router.CIDR, error) { |
|
|
|
|
return loadIP("geoip.dat", code) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func loadIP(filename, country string) ([]*router.CIDR, error) { |
|
|
|
|
geoipBytes, err := filesystem.ReadAsset(filename) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, newError("failed to open file: ", filename).Base(err) |
|
|
|
|
} |
|
|
|
|
var geoipList router.GeoIPList |
|
|
|
|
if err := proto.Unmarshal(geoipBytes, &geoipList); err != nil { |
|
|
|
|
return nil, err |
|
|
|
|
} |
|
|
|
|
var ( |
|
|
|
|
FileCache = make(map[string][]byte) |
|
|
|
|
IPCache = make(map[string]*router.GeoIP) |
|
|
|
|
SiteCache = make(map[string]*router.GeoSite) |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
for _, geoip := range geoipList.Entry { |
|
|
|
|
if geoip.CountryCode == country { |
|
|
|
|
return geoip.Cidr, nil |
|
|
|
|
func loadFile(file string) ([]byte, error) { |
|
|
|
|
if FileCache[file] == nil { |
|
|
|
|
bs, err := filesystem.ReadAsset(file) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, newError("failed to open file: ", file).Base(err) |
|
|
|
|
} |
|
|
|
|
if len(bs) == 0 { |
|
|
|
|
return nil, newError("empty file: ", file) |
|
|
|
|
} |
|
|
|
|
// Do not cache file, may save RAM when there
|
|
|
|
|
// are many files, but consume CPU each time.
|
|
|
|
|
return bs, nil |
|
|
|
|
FileCache[file] = bs |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return nil, newError("country not found in ", filename, ": ", country) |
|
|
|
|
return FileCache[file], nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func loadSite(filename, country string) ([]*router.Domain, error) { |
|
|
|
|
geositeBytes, err := filesystem.ReadAsset(filename) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, newError("failed to open file: ", filename).Base(err) |
|
|
|
|
} |
|
|
|
|
var geositeList router.GeoSiteList |
|
|
|
|
if err := proto.Unmarshal(geositeBytes, &geositeList); err != nil { |
|
|
|
|
return nil, err |
|
|
|
|
func loadIP(file, code string) ([]*router.CIDR, error) { |
|
|
|
|
index := file + ":" + code |
|
|
|
|
if IPCache[index] == nil { |
|
|
|
|
bs, err := loadFile(file) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, newError("failed to load file: ", file).Base(err) |
|
|
|
|
} |
|
|
|
|
bs = find(bs, []byte(code)) |
|
|
|
|
if bs == nil { |
|
|
|
|
return nil, newError("code not found in ", file, ": ", code) |
|
|
|
|
} |
|
|
|
|
var geoip router.GeoIP |
|
|
|
|
if err := proto.Unmarshal(bs, &geoip); err != nil { |
|
|
|
|
return nil, newError("error unmarshal IP in ", file, ": ", code).Base(err) |
|
|
|
|
} |
|
|
|
|
defer runtime.GC() // or debug.FreeOSMemory()
|
|
|
|
|
return geoip.Cidr, nil // do not cache geoip
|
|
|
|
|
IPCache[index] = &geoip |
|
|
|
|
} |
|
|
|
|
return IPCache[index].Cidr, nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for _, site := range geositeList.Entry { |
|
|
|
|
if site.CountryCode == country { |
|
|
|
|
return site.Domain, nil |
|
|
|
|
func loadSite(file, code string) ([]*router.Domain, error) { |
|
|
|
|
index := file + ":" + code |
|
|
|
|
if SiteCache[index] == nil { |
|
|
|
|
bs, err := loadFile(file) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, newError("failed to load file: ", file).Base(err) |
|
|
|
|
} |
|
|
|
|
bs = find(bs, []byte(code)) |
|
|
|
|
if bs == nil { |
|
|
|
|
return nil, newError("list not found in ", file, ": ", code) |
|
|
|
|
} |
|
|
|
|
var geosite router.GeoSite |
|
|
|
|
if err := proto.Unmarshal(bs, &geosite); err != nil { |
|
|
|
|
return nil, newError("error unmarshal Site in ", file, ": ", code).Base(err) |
|
|
|
|
} |
|
|
|
|
defer runtime.GC() // or debug.FreeOSMemory()
|
|
|
|
|
return geosite.Domain, nil // do not cache geosite
|
|
|
|
|
SiteCache[index] = &geosite |
|
|
|
|
} |
|
|
|
|
return SiteCache[index].Domain, nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return nil, newError("list not found in ", filename, ": ", country) |
|
|
|
|
func find(data, code []byte) []byte { |
|
|
|
|
codeL := len(code) |
|
|
|
|
if codeL == 0 { |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
for { |
|
|
|
|
dataL := len(data) |
|
|
|
|
if dataL < 2 { |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
x, y := proto.DecodeVarint(data[1:]) |
|
|
|
|
if x == 0 && y == 0 { |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
headL, bodyL := 1+y, int(x) |
|
|
|
|
dataL -= headL |
|
|
|
|
if dataL < bodyL { |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
data = data[headL:] |
|
|
|
|
if int(data[1]) == codeL { |
|
|
|
|
for i := 0; i < codeL && data[2+i] == code[i]; i++ { |
|
|
|
|
if i+1 == codeL { |
|
|
|
|
return data[:bodyL] |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if dataL == bodyL { |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
data = data[bodyL:] |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type AttributeMatcher interface { |
|
|
|
|