decode.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. // Package geodata includes utilities to decode and parse the geoip & geosite dat files for V2Ray.
  2. //
  3. // It relies on the proto structure of GeoIP, GeoIPList, GeoSite and GeoSiteList in
  4. // github.com/v2fly/v2ray-core/v4/app/router/config.proto to comply with following rules:
  5. //
  6. // 1. GeoIPList and GeoSiteList cannot be changed
  7. // 2. The country_code in GeoIP and GeoSite must be
  8. // a length-delimited `string`(wired type) and has field_number set to 1
  9. //
  10. package geodata
  11. import (
  12. "io"
  13. "runtime"
  14. "strings"
  15. "google.golang.org/protobuf/encoding/protowire"
  16. "github.com/v2fly/v2ray-core/v4/common/errors"
  17. "github.com/v2fly/v2ray-core/v4/common/platform/filesystem"
  18. )
  19. //go:generate go run github.com/v2fly/v2ray-core/v4/common/errors/errorgen
  20. var (
  21. errFailedToReadBytes = errors.New("failed to read bytes")
  22. errFailedToReadExpectedLenBytes = errors.New("failed to read expected length of bytes")
  23. errInvalidGeodataFile = errors.New("invalid geodata file")
  24. errInvalidGeodataVarintLength = errors.New("invalid geodata varint length")
  25. errCodeNotFound = errors.New("code not found")
  26. )
  27. func emitBytes(f io.ReadSeeker, code string) ([]byte, error) {
  28. count := 1
  29. isInner := false
  30. tempContainer := make([]byte, 0, 5)
  31. var result []byte
  32. var advancedN uint64 = 1
  33. var geoDataVarintLength, codeVarintLength, varintLenByteLen uint64 = 0, 0, 0
  34. Loop:
  35. for {
  36. container := make([]byte, advancedN)
  37. bytesRead, err := f.Read(container)
  38. if err == io.EOF {
  39. return nil, errCodeNotFound
  40. }
  41. if err != nil {
  42. return nil, errFailedToReadBytes
  43. }
  44. if bytesRead != len(container) {
  45. return nil, errFailedToReadExpectedLenBytes
  46. }
  47. switch count {
  48. case 1, 3: // data type ((field_number << 3) | wire_type)
  49. if container[0] != 10 { // byte `0A` equals to `10` in decimal
  50. return nil, errInvalidGeodataFile
  51. }
  52. advancedN = 1
  53. count++
  54. case 2, 4: // data length
  55. tempContainer = append(tempContainer, container...)
  56. if container[0] > 127 { // max one-byte-length byte `7F`(0FFF FFFF) equals to `127` in decimal
  57. advancedN = 1
  58. goto Loop
  59. }
  60. lenVarint, n := protowire.ConsumeVarint(tempContainer)
  61. if n < 0 {
  62. return nil, errInvalidGeodataVarintLength
  63. }
  64. tempContainer = nil
  65. if !isInner {
  66. isInner = true
  67. geoDataVarintLength = lenVarint
  68. advancedN = 1
  69. } else {
  70. isInner = false
  71. codeVarintLength = lenVarint
  72. varintLenByteLen = uint64(n)
  73. advancedN = codeVarintLength
  74. }
  75. count++
  76. case 5: // data value
  77. if strings.EqualFold(string(container), code) {
  78. count++
  79. offset := -(1 + int64(varintLenByteLen) + int64(codeVarintLength))
  80. f.Seek(offset, 1) // back to the start of GeoIP or GeoSite varint
  81. advancedN = geoDataVarintLength // the number of bytes to be read in next round
  82. } else {
  83. count = 1
  84. offset := int64(geoDataVarintLength) - int64(codeVarintLength) - int64(varintLenByteLen) - 1
  85. f.Seek(offset, 1) // skip the unmatched GeoIP or GeoSite varint
  86. advancedN = 1 // the next round will be the start of another GeoIPList or GeoSiteList
  87. }
  88. case 6: // matched GeoIP or GeoSite varint
  89. result = container
  90. break Loop
  91. }
  92. runtime.GC() // run GC every round to save memory
  93. }
  94. runtime.GC() // run GC at the end to save memory
  95. return result, nil
  96. }
  97. func Decode(filename, code string) ([]byte, error) {
  98. f, err := filesystem.NewFileSeeker(filename)
  99. if err != nil {
  100. return nil, newError("failed to open file: ", filename).Base(err)
  101. }
  102. defer f.Close()
  103. geoBytes, err := emitBytes(f, code)
  104. if err != nil {
  105. return nil, err
  106. }
  107. return geoBytes, nil
  108. }