maxminddb 0.28.0

Library for reading MaxMind DB format used by GeoIP2 and GeoLite2
Documentation

Rust MaxMind DB Reader

crates.io Released API docs

This library reads the MaxMind DB format, including the GeoIP2 and GeoLite2 databases.

Building

To build everything:

cargo build

Testing

This crate manages its test data within a git submodule. To run the tests, you will first need to run the following command.

git submodule update --init

Usage

Add this to your Cargo.toml:

[dependencies]
maxminddb = "0.28"

Enable optional features as needed:

[dependencies]
maxminddb = { version = "0.28", features = ["mmap"] }

Example

use maxminddb::{geoip2, path, Reader};
use std::net::IpAddr;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let reader = Reader::open_readfile("/path/to/GeoLite2-City.mmdb")?;

    let ip: IpAddr = "89.160.20.128".parse()?;
    let result = reader.lookup(ip)?;
    println!("Network: {}", result.network()?);

    if let Some(city) = result.decode::<geoip2::City>()? {
        println!("Country: {}", city.country.iso_code.unwrap_or("N/A"));
        println!("City: {}", city.city.names.english.unwrap_or("N/A"));
    }

    let iso_code: Option<&str> = result.decode_path(&path!["country", "iso_code"])?;
    println!("Country code via decode_path: {}", iso_code.unwrap_or("N/A"));

    Ok(())
}

lookup() returns a lightweight LookupResult handle. You can:

  • Check whether a record exists with has_data()
  • Read the matched network with network()
  • Decode the full record with decode()
  • Decode one field with decode_path()
  • Reuse offset() as a cache key when many IPs share the same record

Iterating networks

Use within() to iterate over the networks contained in a CIDR range, or networks() to iterate over the whole database. The example below uses the ipnetwork crate, which is not re-exported by maxminddb; add it to your own Cargo.toml to run this code.

use ipnetwork::IpNetwork;
use maxminddb::{Reader, WithinOptions};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let reader = Reader::open_readfile("/path/to/GeoLite2-City.mmdb")?;
    let cidr: IpNetwork = "89.160.20.0/24".parse()?;
    let opts = WithinOptions::default().skip_empty_values();

    for result in reader.within(cidr, opts)? {
        let lookup = result?;
        println!("{}", lookup.network()?);
    }

    Ok(())
}

See the examples directory for runnable programs, including:

  • cargo run --example lookup -- <database.mmdb> <ip>
  • cargo run --example within -- <database.mmdb> <cidr>

Features

Optional features:

  • mmap: Memory-mapped file access for long-running applications
  • simdutf8: SIMD-accelerated UTF-8 validation
  • unsafe-str-decode: Skip UTF-8 validation (requires trusted data)

Enable in Cargo.toml:

[dependencies]
maxminddb = { version = "0.28", features = ["mmap"] }

Note: simdutf8 and unsafe-str-decode are mutually exclusive.

Documentation

API documentation on docs.rs

Benchmarks

The project includes benchmarks using Criterion.rs.

First you need to have a working copy of the GeoIP City database. You can fetch it from here.

Place it in the root folder as GeoIP2-City.mmdb.

Once this is done, run

cargo bench

Two focused benchmarks are especially useful while iterating on changes:

cargo bench --bench lookup
cargo bench --bench serde_usage

If gnuplot is installed, Criterion.rs can generate an HTML report displaying the results of the benchmark under target/criterion/report/index.html.

Contributing

Contributions welcome! Please fork the repository and open a pull request with your changes.

License

This is free software, licensed under the ISC license.