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 }