-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdatabase.go
132 lines (119 loc) · 3.15 KB
/
database.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package main
import (
"bufio"
"encoding/csv"
"errors"
"fmt"
"io"
"net"
"os"
"strings"
"time"
"github.com/oschwald/geoip2-golang"
log "github.com/sirupsen/logrus"
)
// GeoIPDatabase ...
type GeoIPDatabase struct {
GeoDb *geoip2.Reader
CityState map[string]map[string]string
}
// GeoRecordLocation ...
type GeoRecordLocation struct {
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
AccuracyRadius uint16 `json:"accuracy_radius"`
}
// GeoRecord ...
type GeoRecord struct {
RemoteIP string `json:"remote_ip"`
Country string `json:"country"`
City string `json:"city"`
State string `json:"state,omitempty"`
Location GeoRecordLocation `json:"location"`
}
// NewDatabase ...
func NewDatabase(config *Config) (*GeoIPDatabase, error) {
getLite2DbFile := config.GeoLite2DB
cityStateDbFile := config.CityStateDB
geoIPDatabase := GeoIPDatabase{}
if getLite2DbFile == "" {
return nil, errors.New("Geolite database file not defined")
}
log.Debugf("Loading GeoIP2 database %s", getLite2DbFile)
gdb, err := geoip2.Open(getLite2DbFile)
if err != nil {
return nil, err
}
geoIPDatabase.GeoDb = gdb
log.Infof("GeoIP2 database loaded")
if cityStateDbFile == "" {
log.Infof("City State csv file not defined. City input won't be available")
} else {
log.Debugf("Loading City State CSV file %s", cityStateDbFile)
csvFile, err := os.Open(cityStateDbFile)
if err != nil {
log.Fatal(err)
}
geoIPDatabase.CityState = make(map[string]map[string]string)
reader := csv.NewReader(bufio.NewReader(csvFile))
for {
line, err := reader.Read()
if err == io.EOF {
break
} else if err != nil {
log.Fatal(err)
}
country := strings.ToLower(line[0])
city := strings.ToLower(line[1])
state := line[2]
cm, exists := geoIPDatabase.CityState[country]
if !exists {
cm = make(map[string]string)
geoIPDatabase.CityState[country] = cm
}
cm[city] = state
}
log.Infof("City State CSV loaded")
}
return &geoIPDatabase, nil
}
// Find ...
func (g *GeoIPDatabase) Find(ipStr string) (*GeoRecord, error) {
if g.GeoDb != nil {
ip := net.ParseIP(ipStr)
start := time.Now()
ipRecord, err := g.GeoDb.City(ip)
log.Debugf("Time to find getIp data: %s", time.Since(start))
if err != nil {
errStr := fmt.Sprintf("Couldn't find geo info for ip %s. err=%s", ipStr, err)
log.Debugf(errStr)
return nil, errors.New(errStr)
}
geoRecord := &GeoRecord{
RemoteIP: ipStr,
Country: ipRecord.Country.Names["en"],
City: ipRecord.City.Names["en"],
Location: GeoRecordLocation{
Latitude: ipRecord.Location.Latitude,
Longitude: ipRecord.Location.Longitude,
AccuracyRadius: ipRecord.Location.AccuracyRadius,
},
}
if g.CityState != nil {
cs, exists := g.CityState[strings.ToLower(ipRecord.Country.IsoCode)]
if exists {
state, exists := cs[strings.ToLower(ipRecord.City.Names["en"])]
if exists {
geoRecord.State = state
}
}
}
return geoRecord, nil
}
return nil, errors.New("No geolite database")
}
// Close ...
func (g *GeoIPDatabase) Close() error {
err := g.GeoDb.Close()
return err
}