Skip to content

Commit

Permalink
Merge pull request #1 from single9/feature/mmdb
Browse files Browse the repository at this point in the history
Feature/mmdb
  • Loading branch information
single9 authored Jul 12, 2024
2 parents c85e6f6 + f1230aa commit f9f92ee
Show file tree
Hide file tree
Showing 12 changed files with 451 additions and 87 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/target
!/configs/domain.conf.sample
/configs
/mmdb
24 changes: 23 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 11 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "dns-geolocation-checker"
version = "0.1.0"
version = "0.2.0"
edition = "2021"
description = "A tool to check the geolocation of a domain based on its DNS records."
homepage = "https://github.com/single9/dns-geolocation-checker"
Expand All @@ -10,12 +10,18 @@ readme = "README.md"
keywords = ["tool"]

[[bin]]
name = "geo-checker"
path = "src/bin/geo_checker.rs"
name = "dns-geo-checker"
path = "src/bin/dns_geo_checker.rs"

[features]
default = ["mmdb"]
full = ["ip-api", "mmdb"]
ip-api = ["reqwest"]
mmdb = ["maxminddb"]

[dependencies]
anyhow = "1.0.86"
reqwest = { version = "0.12.5", features = ["json"] }
reqwest = { version = "0.12.5", features = ["json"], optional = true}
serde = { version = "1.0.204", features = ["serde_derive"] }
serde_json = "1.0.120"
tokio = { version = "1.38.0", features = ["full"] }
Expand All @@ -25,3 +31,4 @@ hickory-client = "0.24.1"
hickory-proto = "0.24.1"
rand = "0.8.5"
futures = "0.3.30"
maxminddb = { version = "0.24.0", optional = true }
69 changes: 64 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ To get started with this project, clone the repository and ensure you have Rust

### Installation

#### Cargo

Binary releases are available on [crates.io](https://crates.io/crates/dns-geolocation-checker). You can install the DNS Geolocation Checker using the following command:

```sh
cargo install dns-geolocation-checker
```

#### Manually

1. Clone the repository:

```sh
Expand All @@ -28,6 +38,25 @@ cargo build

## Usage

### Feature Flags

The DNS Geolocation Checker supports the following feature flags:

- `ip-api`: Enables the IP Geolocation API provider.
- `mmdb`: Enables the MaxMind GeoLite2 database provider.

To enable a feature flag, use the following command:

```sh
cargo build -F ip-api
```

To enable multiple feature flags, use the following command:

```sh
cargo build -F full
```

### Configuration

You can configure the DNS Geolocation Checker by modifying the `config.toml` file. The configuration file contains the following sections:
Expand All @@ -52,12 +81,40 @@ geo_routing = ["sg", "us"]

Put the file `config.toml` in the `configs` directory of the project. Or you can specify the path to the configuration file using the `CONFIG_PATH` environment variable when running the application.

### IP Geolocation Providers

#### MMDB

This is the default IP geolocation provider.

If you want to use the MaxMind GeoLite2 database, you need to download the database from the [MaxMind website](https://dev.maxmind.com/geoip/geoip2/geolite2/). After downloading the database, you need to specify the path to the database in the `config.toml` file:

```toml
ip_geo_provider = "mmdb"

# Set the path to the MMDB file
# Default: "./mmdb/GeoLite2-City.mmdb"
mmdb_path = "/path/to/GeoLite2-City.mmdb"
```

The default path is `./mmdb/GeoLite2-City.mmdb`.

#### IP-API

The [IP Geolocation API](https://ip-api.com/) is a free service that provides geolocation information for IP addresses.

If you want to use the IP Geolocation API service, you need to specify the provider in the `config.toml` file:

```toml
ip_geo_provider = "ip-api"
```

### Run

To run the DNS Geolocation Checker, use the following command:

```sh
cargo run --bin geo-checker
cargo run --bin dns-geo-checker
```

When you run the DNS Geolocation Checker, it will query the DNS records for each domain and check the geolocation of the IP addresses returned. If the IP address falls within one of the subnets specified in the `test_subnets` section, the geolocation will be considered a match.
Expand All @@ -80,11 +137,13 @@ To run the tests for this project, execute:
cargo test --verbose
```

## Notice

This application use the [IP Geolocation API](https://ip-api.com/) to get the geolocation of IP addresses.

## TODO

- [ ] CLI mode
- [X] Support multiple IP geolocation providers
- [ ] Support IPv6 addresses
- [ ] Map IP addresses to geographical locations

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
72 changes: 72 additions & 0 deletions src/bin/dns_geo_checker.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use anyhow::Result;
use dns_geolocation_checker::{
configs_parser::ConfigParser,
ip_geo_checker::{IpGeoChecker, IpGeoCheckerTestedData},
ip_geo_client::IpGeoProviderType,
};
use std::env;

#[cfg(feature = "ip-api")]
use dns_geolocation_checker::ip_geo_client::ip_api_client::IpApiClient;
#[cfg(feature = "mmdb")]
use dns_geolocation_checker::ip_geo_client::mmdb_client::MMDBClient;

fn print_tested_data(data: Vec<IpGeoCheckerTestedData>) {
data.clone()
.into_iter()
.filter(|r| r.is_ok())
.for_each(|r| {
println!(
"[Matched] {}, ip: {}, subnet: {}, expected: {}, actual: {}",
r.host, r.ip, r.subnet, r.expected, r.actual
);
});

data.clone()
.into_iter()
.filter(|r: &IpGeoCheckerTestedData| r.is_err())
.for_each(|r| {
eprintln!(
"[Mismatched] {}, ip: {}, subnet: {}, expected: {}, actual: {}, error: {:?}",
r.host,
r.ip,
r.subnet,
r.expected,
r.actual,
r.err()
);
});
}

#[tokio::main]
async fn main() -> Result<()> {
let path = env::var("CONFIG_PATH").unwrap_or("./configs/config.toml".to_string());
let parser = ConfigParser::new_with_path(path);
let config = parser.config();
let geo_ip_provider = config.ip_geo_provider.clone();
let data = match geo_ip_provider {
#[cfg(feature = "ip-api")]
IpGeoProviderType::IpApi => {
IpGeoChecker::<IpApiClient>::new()
.config(&config)
.with_ip_api_client()
.check()
.await
}
#[cfg(feature = "mmdb")]
IpGeoProviderType::MMDB => {
IpGeoChecker::<MMDBClient>::new()
.config(&config)
.with_mmdb_client()
.check()
.await
}
_ => panic!(
"[Error] Invalid IP Geo Provider. Please add a valid provider in the config file."
),
};

print_tested_data(data);

Ok(())
}
42 changes: 0 additions & 42 deletions src/bin/geo_checker.rs

This file was deleted.

8 changes: 8 additions & 0 deletions src/configs_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,23 @@
use serde::Deserialize;
use std::{collections::HashMap, fs};

use crate::ip_geo_client::IpGeoProviderType;

/// A struct to hold the parsed config
#[derive(Default, Debug, Clone, Deserialize)]
pub struct Config {
/// The IP geo provider
#[serde(default)]
pub ip_geo_provider: IpGeoProviderType,
#[serde(default)]
pub mmdb_path: Option<String>,
/// A map of country codes to their respective subnets
pub test_subnets: HashMap<String, RoutingCountryConfig>,
/// A list of domains and their respective geo routing
pub domain: Vec<DomainConfig>,
}

/// A struct to hold the domain config
#[derive(Default, Debug, Clone, Deserialize)]
pub struct DomainConfig {
/// The host of the domain
Expand Down
Loading

0 comments on commit f9f92ee

Please sign in to comment.