Skip to content

Commit 62c4ade

Browse files
committed
init
0 parents  commit 62c4ade

18 files changed

+1614
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
testdata/GeoIP2-City.mmdb
2+
testdata/GeoIP2-Connection-Type.mmdb
3+
testdata/GeoIP2-Country.mmdb
4+
testdata/GeoIP2-ISP.mmdb

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2017 Aleksey Lin <[email protected]> (https://incsw.in)
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
2+
[![Go Report Card](https://goreportcard.com/badge/github.com/IncSW/geoip2?style=flat-square)](https://goreportcard.com/report/github.com/IncSW/geoip2)
3+
4+
# GeoIP2 Reader for Go
5+
6+
This library reads MaxMind GeoIP2 databases.
7+
8+
## Installation
9+
10+
`go get github.com/IncSW/geoip2`
11+
12+
## Quick Start
13+
14+
```go
15+
import "github.com/IncSW/geoip2"
16+
17+
reader, err := geoip2.NewReaderFromFile("path/to/GeoIP2-City.mmdb")
18+
if err != nil {
19+
panic(err)
20+
}
21+
record, err := reader.LookupCity(net.ParseIP("1.1.1.1"))
22+
if err != nil {
23+
panic(err)
24+
}
25+
data, err := json.Marshal(record)
26+
if err != nil {
27+
panic(err)
28+
}
29+
println(string(data))
30+
// {
31+
// "Continent": {
32+
// "GeoNameID": 6255151,
33+
// "Code": "OC",
34+
// "Names": {
35+
// "de": "Ozeanien",
36+
// "en": "Oceania",
37+
// "es": "Oceanía",
38+
// "fr": "Océanie",
39+
// "ja": "オセアニア",
40+
// "pt-BR": "Oceania",
41+
// "ru": "Океания",
42+
// "zh-CN": "大洋洲"
43+
// }
44+
// },
45+
// "City": {
46+
// "GeoNameID": 0,
47+
// "Names": null
48+
// },
49+
// "Country": {
50+
// "GeoNameID": 2077456,
51+
// "ISOCode": "AU",
52+
// "IsInEuropeanEnion": false,
53+
// "Names": {
54+
// "de": "Australien",
55+
// "en": "Australia",
56+
// "es": "Australia",
57+
// "fr": "Australie",
58+
// "ja": "オーストラリア",
59+
// "pt-BR": "Austrália",
60+
// "ru": "Австралия",
61+
// "zh-CN": "澳大利亚"
62+
// },
63+
// "Type": ""
64+
// },
65+
// "Subdivisions": null,
66+
// "Location": {
67+
// "AccuracyRadius": 1000,
68+
// "MetroCode": 0,
69+
// "Latitude": -33.494,
70+
// "Longitude": 143.2104,
71+
// "TimeZone": "Australia/Sydney"
72+
// },
73+
// "Postal": {
74+
// "Code": ""
75+
// },
76+
// "RegisteredCountry": {
77+
// "GeoNameID": 2077456,
78+
// "ISOCode": "AU",
79+
// "IsInEuropeanEnion": false,
80+
// "Names": {
81+
// "de": "Australien",
82+
// "en": "Australia",
83+
// "es": "Australia",
84+
// "fr": "Australie",
85+
// "ja": "オーストラリア",
86+
// "pt-BR": "Austrália",
87+
// "ru": "Австралия",
88+
// "zh-CN": "澳大利亚"
89+
// },
90+
// "Type": ""
91+
// },
92+
// "RepresentedCountry": {
93+
// "GeoNameID": 0,
94+
// "ISOCode": "",
95+
// "IsInEuropeanEnion": false,
96+
// "Names": null,
97+
// "Type": ""
98+
// },
99+
// "Traits": {
100+
// "IsAnonymousProxy": false,
101+
// "IsSatelliteProvider": false,
102+
// "StaticIPScore": 0
103+
// }
104+
// }
105+
```
106+
107+
## Performance
108+
109+
TODO
110+
111+
## License
112+
113+
[MIT License](LICENSE).

city.go

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
package geoip2
2+
3+
import (
4+
"errors"
5+
"strconv"
6+
)
7+
8+
func readCityResponse(buffer []byte, offset uint) (*CityResponse, error) {
9+
dataType, citySize, offset, err := readControl(buffer, offset)
10+
if err != nil {
11+
return nil, err
12+
}
13+
if dataType != dataTypeMap {
14+
return nil, errors.New("invalid city type: " + strconv.Itoa(int(dataType)))
15+
}
16+
var key []byte
17+
response := &CityResponse{}
18+
for i := uint(0); i < citySize; i++ {
19+
key, offset, err = readMapKey(buffer, offset)
20+
if err != nil {
21+
return nil, err
22+
}
23+
switch b2s(key) {
24+
case "city":
25+
offset, err = readCity(&response.City, buffer, offset)
26+
if err != nil {
27+
return nil, err
28+
}
29+
case "continent":
30+
offset, err = readContinent(&response.Continent, buffer, offset)
31+
if err != nil {
32+
return nil, err
33+
}
34+
case "country":
35+
offset, err = readCountry(&response.Country, buffer, offset)
36+
if err != nil {
37+
return nil, err
38+
}
39+
case "location":
40+
offset, err = readLocation(&response.Location, buffer, offset)
41+
if err != nil {
42+
return nil, err
43+
}
44+
case "postal":
45+
offset, err = readPostal(&response.Postal, buffer, offset)
46+
if err != nil {
47+
return nil, err
48+
}
49+
case "registered_country":
50+
offset, err = readCountry(&response.RegisteredCountry, buffer, offset)
51+
if err != nil {
52+
return nil, err
53+
}
54+
case "represented_country":
55+
offset, err = readCountry(&response.RepresentedCountry, buffer, offset)
56+
if err != nil {
57+
return nil, err
58+
}
59+
case "subdivisions":
60+
response.Subdivisions, offset, err = readSubdivisions(buffer, offset)
61+
if err != nil {
62+
return nil, err
63+
}
64+
case "traits":
65+
offset, err = readTraits(&response.Traits, buffer, offset)
66+
if err != nil {
67+
return nil, err
68+
}
69+
default:
70+
return nil, errors.New("unknown city response key: " + string(key) + ", type: " + strconv.Itoa(int(dataType)))
71+
}
72+
}
73+
return response, nil
74+
}
75+
76+
func readCity(city *City, buffer []byte, offset uint) (uint, error) {
77+
dataType, size, offset, err := readControl(buffer, offset)
78+
if err != nil {
79+
return 0, err
80+
}
81+
switch dataType {
82+
case dataTypeMap:
83+
return readCityMap(city, buffer, size, offset)
84+
case dataTypePointer:
85+
pointer, newOffset, err := readPointer(buffer, size, offset)
86+
if err != nil {
87+
return 0, err
88+
}
89+
dataType, size, offset, err := readControl(buffer, pointer)
90+
if err != nil {
91+
return 0, err
92+
}
93+
if dataType != dataTypeMap {
94+
return 0, errors.New("invalid city pointer type: " + strconv.Itoa(int(dataType)))
95+
}
96+
_, err = readCityMap(city, buffer, size, offset)
97+
if err != nil {
98+
return 0, err
99+
}
100+
return newOffset, nil
101+
default:
102+
return 0, errors.New("invalid city type: " + strconv.Itoa(int(dataType)))
103+
}
104+
}
105+
106+
func readCityMap(city *City, buffer []byte, mapSize uint, offset uint) (uint, error) {
107+
var key []byte
108+
var err error
109+
for i := uint(0); i < mapSize; i++ {
110+
key, offset, err = readMapKey(buffer, offset)
111+
if err != nil {
112+
return 0, err
113+
}
114+
switch b2s(key) {
115+
case "geoname_id":
116+
city.GeoNameID, offset, err = readUInt32(buffer, offset)
117+
if err != nil {
118+
return 0, err
119+
}
120+
case "names":
121+
city.Names, offset, err = readStringMap(buffer, offset)
122+
if err != nil {
123+
return 0, err
124+
}
125+
default:
126+
return 0, errors.New("unknown city key: " + string(key))
127+
}
128+
}
129+
return offset, nil
130+
}

0 commit comments

Comments
 (0)