reader.go 15 KB

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