127 lines
2.6 KiB
Go
127 lines
2.6 KiB
Go
package ripe
|
|
|
|
import (
|
|
"bufio"
|
|
"compress/gzip"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/maxmind/mmdbwriter"
|
|
"github.com/maxmind/mmdbwriter/mmdbtype"
|
|
)
|
|
|
|
type MMDBEntry struct {
|
|
Range string `json:"range",maxminddb:"range"`
|
|
ASN string `json:"asn",maxminddb:"asn"`
|
|
}
|
|
|
|
const (
|
|
ripe4_download_url = "https://ftp.ripe.net/ripe/dbase/split/ripe.db.route.gz"
|
|
ripe6_download_url = "https://ftp.ripe.net/ripe/dbase/split/ripe.db.route6.gz"
|
|
)
|
|
|
|
func BuildNewMMDB(filepath string) error {
|
|
writer, err := mmdbwriter.New(
|
|
mmdbwriter.Options{
|
|
DatabaseType: "VOC-ASN-DB",
|
|
RecordSize: 32,
|
|
},
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("error creating mmdb writer: %w", err)
|
|
}
|
|
|
|
if err := fillMMDB(writer, ripe4_download_url); err != nil {
|
|
return fmt.Errorf("error building IPv4 mmdb: %w", err)
|
|
}
|
|
|
|
if err := fillMMDB(writer, ripe6_download_url); err != nil {
|
|
return fmt.Errorf("error building IPv6 mmdb: %w", err)
|
|
}
|
|
|
|
fh, err := os.Create(filepath)
|
|
if err != nil {
|
|
return fmt.Errorf("error creating mmdb output file: %w", err)
|
|
}
|
|
|
|
if _, err = writer.WriteTo(fh); err != nil {
|
|
return fmt.Errorf("error writing mmdb to file: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func fillMMDB(mmdb *mmdbwriter.Tree, url string) error {
|
|
// Fetch data from URL
|
|
resp, err := http.Get(url)
|
|
if err != nil {
|
|
return fmt.Errorf("error fetching data from %s: %w", url, err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
var s *bufio.Scanner
|
|
|
|
// Check if response is gzip compressed
|
|
reader, err := gzip.NewReader(resp.Body)
|
|
if err != nil {
|
|
s = bufio.NewScanner(resp.Body)
|
|
} else {
|
|
s = bufio.NewScanner(reader)
|
|
defer reader.Close()
|
|
}
|
|
|
|
var (
|
|
entries []MMDBEntry
|
|
entry MMDBEntry
|
|
)
|
|
|
|
for tok := s.Scan(); tok; tok = s.Scan() {
|
|
//remove all spaces
|
|
line := strings.ReplaceAll(s.Text(), " ", "")
|
|
|
|
tokens := strings.Split(line, ":")
|
|
|
|
if len(tokens) != 2 {
|
|
continue
|
|
}
|
|
|
|
key := strings.TrimSpace(tokens[0])
|
|
if key == "route" {
|
|
entry = MMDBEntry{
|
|
Range: strings.TrimSpace(tokens[1]),
|
|
}
|
|
} else if key == "origin" {
|
|
entry.ASN = strings.TrimSpace(tokens[1])
|
|
entries = append(entries, entry)
|
|
}
|
|
|
|
}
|
|
|
|
if err := s.Err(); err != nil {
|
|
fmt.Printf("error scanning ripe file: %s", err)
|
|
}
|
|
|
|
var entrieErrors error
|
|
for _, e := range entries {
|
|
record := mmdbtype.Map{
|
|
"range": mmdbtype.String(e.Range),
|
|
"asn": mmdbtype.String(e.ASN),
|
|
}
|
|
|
|
_, network, err := net.ParseCIDR(e.Range)
|
|
if err != nil {
|
|
errors.Join(entrieErrors, fmt.Errorf("error parsing CIDR: %w", err))
|
|
continue
|
|
}
|
|
|
|
err = mmdb.Insert(network, record)
|
|
errors.Join(entrieErrors, fmt.Errorf("error inserting record: %w", err))
|
|
}
|
|
|
|
return entrieErrors
|
|
}
|