-
-
Notifications
You must be signed in to change notification settings - Fork 16
/
ipgeobase.go
119 lines (109 loc) · 3.03 KB
/
ipgeobase.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
package main
import (
"archive/zip"
"encoding/base64"
"errors"
"fmt"
"io"
"net/http"
)
// IPGeobase - GeoBase compatible generator for ipgeobase.ru
type IPGeobase struct {
OutputDir string
ErrorsChan chan Error
archive []*zip.File
}
func (ipgeobase *IPGeobase) name() string {
return "IPGeobase"
}
func (ipgeobase *IPGeobase) addError(err Error) {
ipgeobase.ErrorsChan <- err
}
func (ipgeobase *IPGeobase) download() ([]byte, error) {
resp, err := http.Get("http://ipgeobase.ru/files/db/Main/geo_files.zip")
if err != nil {
printMessage("IPGeobase", "Download no answer", "FAIL")
return nil, err
}
defer resp.Body.Close()
answer, err := io.ReadAll(resp.Body)
if err != nil {
printMessage("IPGeobase", "Download bad answer", "FAIL")
return nil, err
}
return answer, nil
}
func (ipgeobase *IPGeobase) unpack(response []byte) error {
file, err := Unpack(response)
if err == nil {
ipgeobase.archive = file
}
return err
}
func (ipgeobase *IPGeobase) citiesDB() (map[string]geoItem, error) {
cities := make(map[string]geoItem)
for record := range readCSVDatabase(ipgeobase.archive, "cities.txt", "IPGeobase", '\t', true) {
if len(record) < 3 {
printMessage("IPGeobase", fmt.Sprintf("cities.txt too short line: %s", record), "FAIL")
continue
}
// Format is: <city_id>\t<city_name>\t<region>\t<district>\t<lattitude>\t<longitude>
cid, city, regionName := record[0], record[1], record[2]
if region, ok := REGIONS[regionName]; ok {
if cid == "1199" {
region = REGIONS["Москва"]
}
cities[cid] = geoItem{
City: city,
RegID: region.ID,
TZ: region.TZ,
}
}
}
if len(cities) < 1 {
return nil, errors.New("Cities db is empty")
}
return cities, nil
}
func (ipgeobase *IPGeobase) parseNetwork(cities map[string]geoItem) <-chan geoItem {
database := make(chan geoItem)
go func() {
for record := range readCSVDatabase(ipgeobase.archive, "cidr_optim.txt", "IPGeobase", '\t', true) {
if len(record) < 5 {
printMessage("IPGeobase", fmt.Sprintf("cidr_optim.txt too short line: %s", record), "FAIL")
continue
}
// Format is: <int_start>\t<int_end>\t<ip_range>\t<country_code>\tcity_id
ipRange, country, cid := removeSpace(record[2]), record[3], record[4]
if info, ok := cities[cid]; country == "RU" && ok {
info.Network = ipRange
database <- info
}
}
close(database)
}()
return database
}
func (ipgeobase *IPGeobase) writeMap(cities map[string]geoItem) error {
reg, err := openMapFile(ipgeobase.OutputDir, "region.txt")
if err != nil {
return err
}
city, err := openMapFile(ipgeobase.OutputDir, "city.txt")
if err != nil {
return err
}
tz, err := openMapFile(ipgeobase.OutputDir, "tz.txt")
if err != nil {
return err
}
defer reg.Close()
defer city.Close()
defer tz.Close()
for info := range ipgeobase.parseNetwork(cities) {
fmt.Fprintf(city, "%s %s;\n", info.Network, base64.StdEncoding.EncodeToString([]byte(info.City)))
fmt.Fprintf(reg, "%s %02d;\n", info.Network, info.RegID)
fmt.Fprintf(tz, "%s %s;\n", info.Network, info.TZ)
}
return nil
}