reader.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. // Package geoip2 provides an easy-to-use API for the MaxMind GeoIP2 and
  2. // GeoLite2 databases; this package does not support GeoIP Legacy databases.
  3. //
  4. // The structs provided by this package match the internal structure of
  5. // the data in the MaxMind databases.
  6. //
  7. // See github.com/oschwald/maxminddb-golang for more advanced used cases.
  8. package geoip2
  9. import (
  10. "fmt"
  11. "net"
  12. "github.com/oschwald/maxminddb-golang"
  13. )
  14. // The City struct corresponds to the data in the GeoIP2/GeoLite2 City
  15. // databases.
  16. type City struct {
  17. City struct {
  18. GeoNameID uint `maxminddb:"geoname_id"`
  19. Names map[string]string `maxminddb:"names"`
  20. } `maxminddb:"city"`
  21. Continent struct {
  22. Code string `maxminddb:"code"`
  23. GeoNameID uint `maxminddb:"geoname_id"`
  24. Names map[string]string `maxminddb:"names"`
  25. } `maxminddb:"continent"`
  26. Country struct {
  27. GeoNameID uint `maxminddb:"geoname_id"`
  28. IsoCode string `maxminddb:"iso_code"`
  29. Names map[string]string `maxminddb:"names"`
  30. } `maxminddb:"country"`
  31. Location struct {
  32. AccuracyRadius uint16 `maxminddb:"accuracy_radius"`
  33. Latitude float64 `maxminddb:"latitude"`
  34. Longitude float64 `maxminddb:"longitude"`
  35. MetroCode uint `maxminddb:"metro_code"`
  36. TimeZone string `maxminddb:"time_zone"`
  37. } `maxminddb:"location"`
  38. Postal struct {
  39. Code string `maxminddb:"code"`
  40. } `maxminddb:"postal"`
  41. RegisteredCountry struct {
  42. GeoNameID uint `maxminddb:"geoname_id"`
  43. IsoCode string `maxminddb:"iso_code"`
  44. Names map[string]string `maxminddb:"names"`
  45. } `maxminddb:"registered_country"`
  46. RepresentedCountry struct {
  47. GeoNameID uint `maxminddb:"geoname_id"`
  48. IsoCode string `maxminddb:"iso_code"`
  49. Names map[string]string `maxminddb:"names"`
  50. Type string `maxminddb:"type"`
  51. } `maxminddb:"represented_country"`
  52. Subdivisions []struct {
  53. GeoNameID uint `maxminddb:"geoname_id"`
  54. IsoCode string `maxminddb:"iso_code"`
  55. Names map[string]string `maxminddb:"names"`
  56. } `maxminddb:"subdivisions"`
  57. Traits struct {
  58. IsAnonymousProxy bool `maxminddb:"is_anonymous_proxy"`
  59. IsSatelliteProvider bool `maxminddb:"is_satellite_provider"`
  60. } `maxminddb:"traits"`
  61. }
  62. // The Country struct corresponds to the data in the GeoIP2/GeoLite2
  63. // Country databases.
  64. type Country struct {
  65. Continent struct {
  66. Code string `maxminddb:"code"`
  67. GeoNameID uint `maxminddb:"geoname_id"`
  68. Names map[string]string `maxminddb:"names"`
  69. } `maxminddb:"continent"`
  70. Country struct {
  71. GeoNameID uint `maxminddb:"geoname_id"`
  72. IsoCode string `maxminddb:"iso_code"`
  73. Names map[string]string `maxminddb:"names"`
  74. } `maxminddb:"country"`
  75. RegisteredCountry struct {
  76. GeoNameID uint `maxminddb:"geoname_id"`
  77. IsoCode string `maxminddb:"iso_code"`
  78. Names map[string]string `maxminddb:"names"`
  79. } `maxminddb:"registered_country"`
  80. RepresentedCountry struct {
  81. GeoNameID uint `maxminddb:"geoname_id"`
  82. IsoCode string `maxminddb:"iso_code"`
  83. Names map[string]string `maxminddb:"names"`
  84. Type string `maxminddb:"type"`
  85. } `maxminddb:"represented_country"`
  86. Traits struct {
  87. IsAnonymousProxy bool `maxminddb:"is_anonymous_proxy"`
  88. IsSatelliteProvider bool `maxminddb:"is_satellite_provider"`
  89. } `maxminddb:"traits"`
  90. }
  91. // The AnonymousIP struct corresponds to the data in the GeoIP2
  92. // Anonymous IP database.
  93. type AnonymousIP struct {
  94. IsAnonymous bool `maxminddb:"is_anonymous"`
  95. IsAnonymousVPN bool `maxminddb:"is_anonymous_vpn"`
  96. IsHostingProvider bool `maxminddb:"is_hosting_provider"`
  97. IsPublicProxy bool `maxminddb:"is_public_proxy"`
  98. IsTorExitNode bool `maxminddb:"is_tor_exit_node"`
  99. }
  100. // The ASN struct corresponds to the data in the GeoLite2 ASN database.
  101. type ASN struct {
  102. AutonomousSystemNumber uint `maxminddb:"autonomous_system_number"`
  103. AutonomousSystemOrganization string `maxminddb:"autonomous_system_organization"`
  104. }
  105. // The ConnectionType struct corresponds to the data in the GeoIP2
  106. // Connection-Type database.
  107. type ConnectionType struct {
  108. ConnectionType string `maxminddb:"connection_type"`
  109. }
  110. // The Domain struct corresponds to the data in the GeoIP2 Domain database.
  111. type Domain struct {
  112. Domain string `maxminddb:"domain"`
  113. }
  114. // The ISP struct corresponds to the data in the GeoIP2 ISP database.
  115. type ISP struct {
  116. AutonomousSystemNumber uint `maxminddb:"autonomous_system_number"`
  117. AutonomousSystemOrganization string `maxminddb:"autonomous_system_organization"`
  118. ISP string `maxminddb:"isp"`
  119. Organization string `maxminddb:"organization"`
  120. }
  121. type databaseType int
  122. const (
  123. isAnonymousIP = 1 << iota
  124. isASN
  125. isCity
  126. isConnectionType
  127. isCountry
  128. isDomain
  129. isEnterprise
  130. isISP
  131. )
  132. // Reader holds the maxminddb.Reader struct. It can be created using the
  133. // Open and FromBytes functions.
  134. type Reader struct {
  135. mmdbReader *maxminddb.Reader
  136. databaseType databaseType
  137. }
  138. // InvalidMethodError is returned when a lookup method is called on a
  139. // database that it does not support. For instance, calling the ISP method
  140. // on a City database.
  141. type InvalidMethodError struct {
  142. Method string
  143. DatabaseType string
  144. }
  145. func (e InvalidMethodError) Error() string {
  146. return fmt.Sprintf(`geoip2: the %s method does not support the %s database`,
  147. e.Method, e.DatabaseType)
  148. }
  149. // UnknownDatabaseTypeError is returned when an unknown database type is
  150. // opened.
  151. type UnknownDatabaseTypeError struct {
  152. DatabaseType string
  153. }
  154. func (e UnknownDatabaseTypeError) Error() string {
  155. return fmt.Sprintf(`geoip2: reader does not support the "%s" database type`,
  156. e.DatabaseType)
  157. }
  158. // Open takes a string path to a file and returns a Reader struct or an error.
  159. // The database file is opened using a memory map. Use the Close method on the
  160. // Reader object to return the resources to the system.
  161. func Open(file string) (*Reader, error) {
  162. reader, err := maxminddb.Open(file)
  163. if err != nil {
  164. return nil, err
  165. }
  166. dbType, err := getDBType(reader)
  167. return &Reader{reader, dbType}, err
  168. }
  169. // FromBytes takes a byte slice corresponding to a GeoIP2/GeoLite2 database
  170. // file and returns a Reader struct or an error. Note that the byte slice is
  171. // use directly; any modification of it after opening the database will result
  172. // in errors while reading from the database.
  173. func FromBytes(bytes []byte) (*Reader, error) {
  174. reader, err := maxminddb.FromBytes(bytes)
  175. if err != nil {
  176. return nil, err
  177. }
  178. dbType, err := getDBType(reader)
  179. return &Reader{reader, dbType}, err
  180. }
  181. func getDBType(reader *maxminddb.Reader) (databaseType, error) {
  182. switch reader.Metadata.DatabaseType {
  183. case "GeoIP2-Anonymous-IP":
  184. return isAnonymousIP, nil
  185. case "GeoLite2-ASN":
  186. return isASN, nil
  187. // We allow City lookups on Country for back compat
  188. case "GeoLite2-City",
  189. "GeoIP2-City",
  190. "GeoIP2-City-Africa",
  191. "GeoIP2-City-Asia-Pacific",
  192. "GeoIP2-City-Europe",
  193. "GeoIP2-City-North-America",
  194. "GeoIP2-City-South-America",
  195. "GeoIP2-Precision-City",
  196. "GeoLite2-Country",
  197. "GeoIP2-Country":
  198. return isCity | isCountry, nil
  199. case "GeoIP2-Connection-Type":
  200. return isConnectionType, nil
  201. case "GeoIP2-Domain":
  202. return isDomain, nil
  203. case "GeoIP2-Enterprise":
  204. return isEnterprise | isCity | isCountry, nil
  205. case "GeoIP2-ISP", "GeoIP2-Precision-ISP":
  206. return isISP, nil
  207. default:
  208. return 0, UnknownDatabaseTypeError{reader.Metadata.DatabaseType}
  209. }
  210. }
  211. // City takes an IP address as a net.IP struct and returns a City struct
  212. // and/or an error. Although this can be used with other databases, this
  213. // method generally should be used with the GeoIP2 or GeoLite2 City databases.
  214. func (r *Reader) City(ipAddress net.IP) (*City, error) {
  215. if isCity&r.databaseType == 0 {
  216. return nil, InvalidMethodError{"City", r.Metadata().DatabaseType}
  217. }
  218. var city City
  219. err := r.mmdbReader.Lookup(ipAddress, &city)
  220. return &city, err
  221. }
  222. // Country takes an IP address as a net.IP struct and returns a Country struct
  223. // and/or an error. Although this can be used with other databases, this
  224. // method generally should be used with the GeoIP2 or GeoLite2 Country
  225. // databases.
  226. func (r *Reader) Country(ipAddress net.IP) (*Country, error) {
  227. if isCountry&r.databaseType == 0 {
  228. return nil, InvalidMethodError{"Country", r.Metadata().DatabaseType}
  229. }
  230. var country Country
  231. err := r.mmdbReader.Lookup(ipAddress, &country)
  232. return &country, err
  233. }
  234. // AnonymousIP takes an IP address as a net.IP struct and returns a
  235. // AnonymousIP struct and/or an error.
  236. func (r *Reader) AnonymousIP(ipAddress net.IP) (*AnonymousIP, error) {
  237. if isAnonymousIP&r.databaseType == 0 {
  238. return nil, InvalidMethodError{"AnonymousIP", r.Metadata().DatabaseType}
  239. }
  240. var anonIP AnonymousIP
  241. err := r.mmdbReader.Lookup(ipAddress, &anonIP)
  242. return &anonIP, err
  243. }
  244. // ASN takes an IP address as a net.IP struct and returns a ASN struct and/or
  245. // an error
  246. func (r *Reader) ASN(ipAddress net.IP) (*ASN, error) {
  247. if isASN&r.databaseType == 0 {
  248. return nil, InvalidMethodError{"ASN", r.Metadata().DatabaseType}
  249. }
  250. var val ASN
  251. err := r.mmdbReader.Lookup(ipAddress, &val)
  252. return &val, err
  253. }
  254. // ConnectionType takes an IP address as a net.IP struct and returns a
  255. // ConnectionType struct and/or an error
  256. func (r *Reader) ConnectionType(ipAddress net.IP) (*ConnectionType, error) {
  257. if isConnectionType&r.databaseType == 0 {
  258. return nil, InvalidMethodError{"ConnectionType", r.Metadata().DatabaseType}
  259. }
  260. var val ConnectionType
  261. err := r.mmdbReader.Lookup(ipAddress, &val)
  262. return &val, err
  263. }
  264. // Domain takes an IP address as a net.IP struct and returns a
  265. // Domain struct and/or an error
  266. func (r *Reader) Domain(ipAddress net.IP) (*Domain, error) {
  267. if isDomain&r.databaseType == 0 {
  268. return nil, InvalidMethodError{"Domain", r.Metadata().DatabaseType}
  269. }
  270. var val Domain
  271. err := r.mmdbReader.Lookup(ipAddress, &val)
  272. return &val, err
  273. }
  274. // ISP takes an IP address as a net.IP struct and returns a ISP struct and/or
  275. // an error
  276. func (r *Reader) ISP(ipAddress net.IP) (*ISP, error) {
  277. if isISP&r.databaseType == 0 {
  278. return nil, InvalidMethodError{"ISP", r.Metadata().DatabaseType}
  279. }
  280. var val ISP
  281. err := r.mmdbReader.Lookup(ipAddress, &val)
  282. return &val, err
  283. }
  284. // Metadata takes no arguments and returns a struct containing metadata about
  285. // the MaxMind database in use by the Reader.
  286. func (r *Reader) Metadata() maxminddb.Metadata {
  287. return r.mmdbReader.Metadata
  288. }
  289. // Close unmaps the database file from virtual memory and returns the
  290. // resources to the system.
  291. func (r *Reader) Close() error {
  292. return r.mmdbReader.Close()
  293. }