voc-telemetry/ripe/builder.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
}