bgpkit-parser 0.21.0

MRT/BGP/BMP data processing library
Documentation
//! Classify malformed MRT records and export their raw bytes.
//!
//! The diagnostic iterator reports every record with its raw bytes attached:
//! clean records have empty warning lists, records with recoverable RFC 7606
//! validation findings carry them in `warnings`, and fatal parse failures
//! retain the consumed bytes plus a best-effort partial dissection tree
//! showing where parsing stopped.
//!
//! Run with:
//! ```bash
//! cargo run --release --example diagnostic_iterator -- <MRT_FILE_OR_URL> [OUTPUT_DIR]
//! ```

use bgpkit_parser::{BgpkitParser, DiagnosticEvent};
use std::path::{Path, PathBuf};

fn main() {
    let source = std::env::args().nth(1).unwrap_or_else(|| {
        eprintln!("Usage: diagnostic_iterator <MRT_FILE_OR_URL> [OUTPUT_DIR]");
        std::process::exit(2);
    });
    let output_dir = std::env::args()
        .nth(2)
        .map(PathBuf::from)
        .unwrap_or_else(|| PathBuf::from("diagnostic-records"));

    let parser = BgpkitParser::new(&source).unwrap_or_else(|error| {
        eprintln!("Unable to open {source}: {error}");
        std::process::exit(1);
    });

    let mut clean = 0;
    let mut validation = 0;
    let mut parse_errors = 0;

    for (index, event) in parser.into_diagnostic_iter().enumerate() {
        match event {
            DiagnosticEvent::Record { warnings, raw, .. } => {
                if warnings.is_empty() {
                    clean += 1;
                } else {
                    validation += 1;
                    let path = output_path(&output_dir, index, "validation");
                    save_raw_record(&raw, &path);
                    eprintln!(
                        "record {index}: {} validation finding(s) -> {}",
                        warnings.len(),
                        path.display()
                    );
                }
            }
            DiagnosticEvent::ParseError {
                error,
                common_header,
                raw_bytes,
                ..
            } => {
                parse_errors += 1;
                eprintln!("record {index}: {error}");
                if let Some(header) = common_header {
                    eprintln!("  header: {header}");
                }
                if let Some(bytes) = raw_bytes {
                    let path = output_path(&output_dir, index, "parse-error");
                    save_bytes(&bytes, &path);
                }
            }
            _ => {}
        }
    }

    println!("clean records: {clean}");
    println!("records with validation findings: {validation}");
    println!("fatal parse errors: {parse_errors}");
}

fn output_path(output_dir: &Path, index: usize, kind: &str) -> PathBuf {
    output_dir.join(format!("{index:08}-{kind}.mrt"))
}

fn save_raw_record(raw_record: &bgpkit_parser::RawMrtRecord, path: &PathBuf) {
    if let Some(parent) = path.parent() {
        std::fs::create_dir_all(parent).expect("failed to create output directory");
    }
    raw_record
        .write_raw_bytes(path)
        .expect("failed to write diagnostic record");
}

fn save_bytes(bytes: &[u8], path: &PathBuf) {
    if let Some(parent) = path.parent() {
        std::fs::create_dir_all(parent).expect("failed to create output directory");
    }
    std::fs::write(path, bytes).expect("failed to write diagnostic record");
    eprintln!("  saved {} bytes to {}", bytes.len(), path.display());
}