-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
172 lines (150 loc) · 4.63 KB
/
main.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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
package main
import (
"encoding/json"
"flag"
"fmt"
"go4.org/netipx"
"log"
"net/netip"
"os"
"github.com/maxmind/mmdbwriter"
"github.com/maxmind/mmdbwriter/inserter"
"github.com/maxmind/mmdbwriter/mmdbtype"
)
var (
datasetGeo = flag.String("d", "./dataset.json", "Dataset file path.")
inputGeo = flag.String("i", "./GeoLite2-City.mmdb", "Input GeoLite2-City.mmdb file path.")
outputGeo = flag.String("o", "./GeoLite2-City-mod.mmdb", "Output modified mmdb file path.")
version = flag.Bool("v", false, "Print current version and exit.")
merge = flag.String("m", "replace", "Merge strategy. It may be: toplevel, recurse or replace.")
)
// Version contains main version of build. Get from compiler variables
var Version string
// Dataset is for JSON dataset mapping
type Dataset struct {
Networks []string `json:"networks"`
Data map[string]any `json:"data"`
}
// Check func for defer functions
func Check(f func() error) {
if err := f(); err != nil {
fmt.Println("Received error:", err)
}
}
// toMMDBType key converts field values read from json into their corresponding mmdbtype.DataType.
// It makes some assumptions for numeric types based on previous knowledge about field types.
func toMMDBType(key string, value any) (mmdbtype.DataType, error) {
switch v := value.(type) {
case bool:
return mmdbtype.Bool(v), nil
case string:
return mmdbtype.String(v), nil
case map[string]any:
m := mmdbtype.Map{}
for innerKey, val := range v {
innerVal, err := toMMDBType(innerKey, val)
if err != nil {
return nil, fmt.Errorf("parsing mmdbtype.Map for key %q: %w", key, err)
}
m[mmdbtype.String(innerKey)] = innerVal
}
return m, nil
case []any:
s := mmdbtype.Slice{}
for _, val := range v {
innerVal, err := toMMDBType(key, val)
if err != nil {
return nil, fmt.Errorf("parsing mmdbtype.Slice for key %q: %w", key, err)
}
s = append(s, innerVal)
}
return s, nil
case float64:
switch key {
case "accuracy_radius", "confidence", "metro_code":
return mmdbtype.Uint16(v), nil
case "autonomous_system_number", "average_income",
"geoname_id", "ipv4_24", "ipv4_32", "ipv6_32",
"ipv6_48", "ipv6_64", "population_density":
return mmdbtype.Uint32(v), nil
case "ip_risk", "latitude", "longitude", "score",
"static_ip_score":
return mmdbtype.Float64(v), nil
default:
return nil, fmt.Errorf("unsupported numeric type for key %q: %T", key, value)
}
default:
return nil, fmt.Errorf("unsupported type for key %q: %T", key, value)
}
}
func main() {
// main data map for json dataset
var dataset []Dataset
// validate merge strategy.
var mergeStrategy inserter.FuncGenerator
flag.Parse()
if *version {
fmt.Println(Version)
os.Exit(0)
}
if *merge == "toplevel" {
mergeStrategy = inserter.TopLevelMergeWith
log.Printf("Using merge strategy: toplevel")
} else if *merge == "recurse" {
mergeStrategy = inserter.DeepMergeWith
log.Printf("Using merge strategy: recurse")
} else {
mergeStrategy = inserter.ReplaceWith
log.Printf("Using merge strategy: replace")
}
log.Printf("Loading mmdb: %v", *inputGeo)
// Load the database we wish to enrich.
dbWriter, err := mmdbwriter.Load(*inputGeo, mmdbwriter.Options{
Inserter: mergeStrategy,
IncludeReservedNetworks: true,
Description: map[string]string{"en": fmt.Sprintf("Compiled with mmdb-editor (%v) https://github.com/iglov/mmdb-editor", Version)},
})
if err != nil {
log.Fatal(err)
}
file, err := os.Open(*datasetGeo)
if err != nil {
log.Fatal("Opening json file error:", err)
}
defer Check(file.Close)
if err := json.NewDecoder(file).Decode(&dataset); err != nil {
log.Printf("error decoding response: %v", err)
if e, ok := err.(*json.SyntaxError); ok {
log.Printf("syntax error at byte offset %d", e.Offset)
}
log.Printf("response: %v", file)
log.Fatal("Error during Unmarshal(): ", err)
}
for _, record := range dataset {
for _, network := range record.Networks {
prefix, err := netip.ParsePrefix(network)
if err != nil {
log.Fatal("Parsing networks error:", err)
}
mmdbValue, err := toMMDBType(prefix.String(), record.Data)
if err != nil {
log.Fatal("Converting value to mmdbtype error:", err)
}
log.Printf("Modifying net: %s", prefix.String())
if err := dbWriter.Insert(netipx.PrefixIPNet(prefix), mmdbValue); err != nil {
log.Fatal(err)
}
}
}
log.Printf("Compiling and writing modified data into: %v", *outputGeo)
// Write the newly enriched DB to the filesystem.
fh, err := os.Create(*outputGeo)
if err != nil {
log.Fatal(err)
}
defer Check(fh.Close)
_, err = dbWriter.WriteTo(fh)
if err != nil {
log.Fatal(err)
}
}