reader.go 11 KB

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