reader.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. package maxminddb
  2. import (
  3. "bytes"
  4. "errors"
  5. "fmt"
  6. "net"
  7. "reflect"
  8. )
  9. const (
  10. // NotFound is returned by LookupOffset when a matched root record offset
  11. // cannot be found.
  12. NotFound = ^uintptr(0)
  13. dataSectionSeparatorSize = 16
  14. )
  15. var metadataStartMarker = []byte("\xAB\xCD\xEFMaxMind.com")
  16. // Reader holds the data corresponding to the MaxMind DB file. Its only public
  17. // field is Metadata, which contains the metadata from the MaxMind DB file.
  18. type Reader struct {
  19. hasMappedFile bool
  20. buffer []byte
  21. decoder decoder
  22. Metadata Metadata
  23. ipv4Start uint
  24. }
  25. // Metadata holds the metadata decoded from the MaxMind DB file. In particular
  26. // in has the format version, the build time as Unix epoch time, the database
  27. // type and description, the IP version supported, and a slice of the natural
  28. // languages included.
  29. type Metadata struct {
  30. BinaryFormatMajorVersion uint `maxminddb:"binary_format_major_version"`
  31. BinaryFormatMinorVersion uint `maxminddb:"binary_format_minor_version"`
  32. BuildEpoch uint `maxminddb:"build_epoch"`
  33. DatabaseType string `maxminddb:"database_type"`
  34. Description map[string]string `maxminddb:"description"`
  35. IPVersion uint `maxminddb:"ip_version"`
  36. Languages []string `maxminddb:"languages"`
  37. NodeCount uint `maxminddb:"node_count"`
  38. RecordSize uint `maxminddb:"record_size"`
  39. }
  40. // FromBytes takes a byte slice corresponding to a MaxMind DB file and returns
  41. // a Reader structure or an error.
  42. func FromBytes(buffer []byte) (*Reader, error) {
  43. metadataStart := bytes.LastIndex(buffer, metadataStartMarker)
  44. if metadataStart == -1 {
  45. return nil, newInvalidDatabaseError("error opening database: invalid MaxMind DB file")
  46. }
  47. metadataStart += len(metadataStartMarker)
  48. metadataDecoder := decoder{buffer[metadataStart:]}
  49. var metadata Metadata
  50. rvMetdata := reflect.ValueOf(&metadata)
  51. _, err := metadataDecoder.decode(0, rvMetdata, 0)
  52. if err != nil {
  53. return nil, err
  54. }
  55. searchTreeSize := metadata.NodeCount * metadata.RecordSize / 4
  56. dataSectionStart := searchTreeSize + dataSectionSeparatorSize
  57. dataSectionEnd := uint(metadataStart - len(metadataStartMarker))
  58. if dataSectionStart > dataSectionEnd {
  59. return nil, newInvalidDatabaseError("the MaxMind DB contains invalid metadata")
  60. }
  61. d := decoder{
  62. buffer[searchTreeSize+dataSectionSeparatorSize : metadataStart-len(metadataStartMarker)],
  63. }
  64. reader := &Reader{
  65. buffer: buffer,
  66. decoder: d,
  67. Metadata: metadata,
  68. ipv4Start: 0,
  69. }
  70. reader.ipv4Start, err = reader.startNode()
  71. return reader, err
  72. }
  73. func (r *Reader) startNode() (uint, error) {
  74. if r.Metadata.IPVersion != 6 {
  75. return 0, nil
  76. }
  77. nodeCount := r.Metadata.NodeCount
  78. node := uint(0)
  79. var err error
  80. for i := 0; i < 96 && node < nodeCount; i++ {
  81. node, err = r.readNode(node, 0)
  82. if err != nil {
  83. return 0, err
  84. }
  85. }
  86. return node, err
  87. }
  88. // Lookup takes an IP address as a net.IP structure and a pointer to the
  89. // result value to Decode into.
  90. func (r *Reader) Lookup(ipAddress net.IP, result interface{}) error {
  91. if r.buffer == nil {
  92. return errors.New("cannot call Lookup on a closed database")
  93. }
  94. pointer, err := r.lookupPointer(ipAddress)
  95. if pointer == 0 || err != nil {
  96. return err
  97. }
  98. return r.retrieveData(pointer, result)
  99. }
  100. // LookupOffset maps an argument net.IP to a corresponding record offset in the
  101. // database. NotFound is returned if no such record is found, and a record may
  102. // otherwise be extracted by passing the returned offset to Decode. LookupOffset
  103. // is an advanced API, which exists to provide clients with a means to cache
  104. // previously-decoded records.
  105. func (r *Reader) LookupOffset(ipAddress net.IP) (uintptr, error) {
  106. if r.buffer == nil {
  107. return 0, errors.New("cannot call LookupOffset on a closed database")
  108. }
  109. pointer, err := r.lookupPointer(ipAddress)
  110. if pointer == 0 || err != nil {
  111. return NotFound, err
  112. }
  113. return r.resolveDataPointer(pointer)
  114. }
  115. // Decode the record at |offset| into |result|. The result value pointed to
  116. // must be a data value that corresponds to a record in the database. This may
  117. // include a struct representation of the data, a map capable of holding the
  118. // data or an empty interface{} value.
  119. //
  120. // If result is a pointer to a struct, the struct need not include a field
  121. // for every value that may be in the database. If a field is not present in
  122. // the structure, the decoder will not decode that field, reducing the time
  123. // required to decode the record.
  124. //
  125. // As a special case, a struct field of type uintptr will be used to capture
  126. // the offset of the value. Decode may later be used to extract the stored
  127. // value from the offset. MaxMind DBs are highly normalized: for example in
  128. // the City database, all records of the same country will reference a
  129. // single representative record for that country. This uintptr behavior allows
  130. // clients to leverage this normalization in their own sub-record caching.
  131. func (r *Reader) Decode(offset uintptr, result interface{}) error {
  132. if r.buffer == nil {
  133. return errors.New("cannot call Decode on a closed database")
  134. }
  135. return r.decode(offset, result)
  136. }
  137. func (r *Reader) decode(offset uintptr, result interface{}) error {
  138. rv := reflect.ValueOf(result)
  139. if rv.Kind() != reflect.Ptr || rv.IsNil() {
  140. return errors.New("result param must be a pointer")
  141. }
  142. _, err := r.decoder.decode(uint(offset), reflect.ValueOf(result), 0)
  143. return err
  144. }
  145. func (r *Reader) lookupPointer(ipAddress net.IP) (uint, error) {
  146. if ipAddress == nil {
  147. return 0, errors.New("ipAddress passed to Lookup cannot be nil")
  148. }
  149. ipV4Address := ipAddress.To4()
  150. if ipV4Address != nil {
  151. ipAddress = ipV4Address
  152. }
  153. if len(ipAddress) == 16 && r.Metadata.IPVersion == 4 {
  154. return 0, fmt.Errorf("error looking up '%s': you attempted to look up an IPv6 address in an IPv4-only database", ipAddress.String())
  155. }
  156. return r.findAddressInTree(ipAddress)
  157. }
  158. func (r *Reader) findAddressInTree(ipAddress net.IP) (uint, error) {
  159. bitCount := uint(len(ipAddress) * 8)
  160. var node uint
  161. if bitCount == 32 {
  162. node = r.ipv4Start
  163. }
  164. nodeCount := r.Metadata.NodeCount
  165. for i := uint(0); i < bitCount && node < nodeCount; i++ {
  166. bit := uint(1) & (uint(ipAddress[i>>3]) >> (7 - (i % 8)))
  167. var err error
  168. node, err = r.readNode(node, bit)
  169. if err != nil {
  170. return 0, err
  171. }
  172. }
  173. if node == nodeCount {
  174. // Record is empty
  175. return 0, nil
  176. } else if node > nodeCount {
  177. return node, nil
  178. }
  179. return 0, newInvalidDatabaseError("invalid node in search tree")
  180. }
  181. func (r *Reader) readNode(nodeNumber uint, index uint) (uint, error) {
  182. RecordSize := r.Metadata.RecordSize
  183. baseOffset := nodeNumber * RecordSize / 4
  184. var nodeBytes []byte
  185. var prefix uint
  186. switch RecordSize {
  187. case 24:
  188. offset := baseOffset + index*3
  189. nodeBytes = r.buffer[offset : offset+3]
  190. case 28:
  191. prefix = uint(r.buffer[baseOffset+3])
  192. if index != 0 {
  193. prefix &= 0x0F
  194. } else {
  195. prefix = (0xF0 & prefix) >> 4
  196. }
  197. offset := baseOffset + index*4
  198. nodeBytes = r.buffer[offset : offset+3]
  199. case 32:
  200. offset := baseOffset + index*4
  201. nodeBytes = r.buffer[offset : offset+4]
  202. default:
  203. return 0, newInvalidDatabaseError("unknown record size: %d", RecordSize)
  204. }
  205. return uintFromBytes(prefix, nodeBytes), nil
  206. }
  207. func (r *Reader) retrieveData(pointer uint, result interface{}) error {
  208. offset, err := r.resolveDataPointer(pointer)
  209. if err != nil {
  210. return err
  211. }
  212. return r.decode(offset, result)
  213. }
  214. func (r *Reader) resolveDataPointer(pointer uint) (uintptr, error) {
  215. var resolved = uintptr(pointer - r.Metadata.NodeCount - dataSectionSeparatorSize)
  216. if resolved > uintptr(len(r.buffer)) {
  217. return 0, newInvalidDatabaseError("the MaxMind DB file's search tree is corrupt")
  218. }
  219. return resolved, nil
  220. }