geoip.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. package proxy
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io/ioutil"
  6. "log"
  7. "net"
  8. "os"
  9. "github.com/oschwald/geoip2-golang"
  10. bingeoip "github.com/zu1k/proxypool/internal/bindata/geoip"
  11. )
  12. var geoIp GeoIP
  13. func InitGeoIpDB() {
  14. err := bingeoip.RestoreAsset("", "assets/GeoLite2-City.mmdb")
  15. if err != nil {
  16. panic(err)
  17. }
  18. geoIp = NewGeoIP("assets/GeoLite2-City.mmdb")
  19. }
  20. // GeoIP2
  21. type GeoIP struct {
  22. db *geoip2.Reader
  23. flags cclist
  24. }
  25. type CountryCode struct {
  26. Code string `json:"code"`
  27. Emoji string `json:"emoji"`
  28. Unicode string `json:"unicode`
  29. Name string `json:"name"`
  30. Title string `json:"title"`
  31. Dialcode string `json:"dialCode`
  32. }
  33. // new geoip from db file
  34. func NewGeoIP(filePath string) (geoip GeoIP) {
  35. var countrycodes cclist
  36. // 判断文件是否存在
  37. _, err := os.Stat(filePath)
  38. if err != nil && os.IsNotExist(err) {
  39. log.Println("文件不存在,请自行下载 Geoip2 City库,并保存在", filePath)
  40. os.Exit(1)
  41. } else {
  42. db, err := geoip2.Open(filePath)
  43. if err != nil {
  44. log.Fatal(err)
  45. }
  46. data, err := ioutil.ReadFile("assets/flags.json")
  47. if err != nil {
  48. log.Fatal(err)
  49. return
  50. }
  51. json.Unmarshal(data, &countrycodes)
  52. geoip = GeoIP{db: db, flags: countrycodes}
  53. }
  54. return
  55. }
  56. // find ip info
  57. func (g GeoIP) Find(ipORdomain string) (ip, country string, err error) {
  58. ips, err := net.LookupIP(ipORdomain)
  59. if err != nil {
  60. return "", "", err
  61. }
  62. ipData := net.ParseIP(ips[0].String())
  63. record, err := g.db.City(ipData)
  64. if err != nil {
  65. return "", "", err
  66. }
  67. countrycode := record.Country.IsoCode
  68. if countrycode == "" {
  69. // handle unknown country code
  70. return ips[0].String(), fmt.Sprintf("🏁 ZZ"), nil
  71. }
  72. countryflag := g.getFlag(countrycode)
  73. country = fmt.Sprintf("%v %v", countryflag, countrycode)
  74. return ips[0].String(), country, nil
  75. }
  76. // getFlag method take country code as input, return its corresponding country/region flag
  77. func (g GeoIP) getFlag(countrycode string) string {
  78. result := find(g.flags, countrycode)
  79. return result.Emoji
  80. }
  81. type cclist []CountryCode
  82. func (c cclist) in(code string, left bool) bool {
  83. length := len(c)
  84. if left {
  85. return c[length-1].Code >= code
  86. }
  87. return c[0].Code <= code
  88. }
  89. // find will find corresponding country flag emoji from flags for a given country code
  90. func find(list cclist, target string) CountryCode {
  91. var result CountryCode
  92. length := len(list)
  93. if length == 1 {
  94. if list[0].Code == target {
  95. return list[0]
  96. }
  97. return CountryCode{Emoji: "🏁"}
  98. }
  99. split := length / 2
  100. left, right := list[:split], list[split:]
  101. if left.in(target, true) {
  102. result = find(left, target)
  103. }
  104. if right.in(target, false) {
  105. result = find(right, target)
  106. }
  107. return result
  108. }