Initial commit

Includes IPv4 and IPv6 support, and a CLI for generating IpRanges from ip2location DB1 CSV files.
This commit is contained in:
2025-03-11 21:16:25 -06:00
commit 30a5e5c3e2
17 changed files with 789 additions and 0 deletions
+7
View File
@@ -0,0 +1,7 @@
# OS Files
*~
._*
.DS_Store
# Rust
/target
+2
View File
@@ -0,0 +1,2 @@
hard_tabs = true
wrap_comments = true
+9
View File
@@ -0,0 +1,9 @@
{
"recommendations": [
"vadimcn.vscode-lldb",
"barbosshack.crates-io",
"usernamehw.errorlens",
"tamasfe.even-better-toml",
"rust-lang.rust-analyzer",
]
}
+33
View File
@@ -0,0 +1,33 @@
{
// VSCode
"editor.detectIndentation": false,
"editor.insertSpaces": false,
"editor.tabSize": 4,
"files.exclude": {
"**/target": true,
"**/Cargo.lock": true,
},
"files.insertFinalNewline": true,
"files.trimFinalNewlines": true,
"files.trimTrailingWhitespace": true,
"files.watcherExclude": {
"**/.git/**": true,
"**/target/**": true,
},
// Extensions
"crates.listPreReleases": true,
"evenBetterToml.formatter.alignComments": true,
"evenBetterToml.formatter.alignEntries": false,
"evenBetterToml.formatter.allowedBlankLines": 1,
"evenBetterToml.formatter.arrayAutoExpand": true,
"evenBetterToml.formatter.arrayTrailingComma": true,
"evenBetterToml.formatter.columnWidth": 80,
"evenBetterToml.formatter.reorderKeys": true,
"evenBetterToml.formatter.trailingNewline": true,
"rust-analyzer.imports.granularity.enforce": true,
"rust-analyzer.imports.granularity.group": "module",
"rust-analyzer.imports.group.enable": true,
"rust-analyzer.imports.merge.glob": false,
"rust-analyzer.imports.preferNoStd": true,
"rust-analyzer.showUnlinkedFileNotification": false,
}
+6
View File
@@ -0,0 +1,6 @@
How to Contribute
=================
We'd love to accept your patches and contributions to this project.
We just need you to follow the Contributor License Agreement outlined
in the latest v0.0.x of https://github.com/Skrunix/license
Generated
+363
View File
@@ -0,0 +1,363 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "anstream"
version = "0.6.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
[[package]]
name = "anstyle-parse"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
dependencies = [
"anstyle",
"once_cell",
"windows-sys",
]
[[package]]
name = "anyhow"
version = "1.0.97"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
[[package]]
name = "bincode"
version = "2.0.0-rc.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f11ea1a0346b94ef188834a65c068a03aec181c94896d481d7a0a40d85b0ce95"
dependencies = [
"serde",
]
[[package]]
name = "clap"
version = "4.5.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.5.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.5.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
[[package]]
name = "colorchoice"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
[[package]]
name = "csv"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf"
dependencies = [
"csv-core",
"itoa",
"ryu",
"serde",
]
[[package]]
name = "csv-core"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d02f3b0da4c6504f86e9cd789d8dbafab48c2321be74e9987593de5a894d93d"
dependencies = [
"memchr",
]
[[package]]
name = "either"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7914353092ddf589ad78f25c5c1c21b7f80b0ff8621e7c814c3485b5306da9d"
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "ipfilter"
version = "0.0.0"
dependencies = [
"bincode",
"ipnet",
"iprange",
]
[[package]]
name = "ipfilter-cli"
version = "0.0.0"
dependencies = [
"anyhow",
"clap",
"csv",
"ipfilter",
"ipnet",
"itertools",
]
[[package]]
name = "ipnet"
version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
[[package]]
name = "iprange"
version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37209be0ad225457e63814401415e748e2453a5297f9b637338f5fb8afa4ec00"
dependencies = [
"ipnet",
"serde",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "itertools"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "once_cell"
version = "1.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
[[package]]
name = "proc-macro2"
version = "1.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
dependencies = [
"proc-macro2",
]
[[package]]
name = "ryu"
version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd"
[[package]]
name = "serde"
version = "1.0.218"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.218"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
version = "2.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+31
View File
@@ -0,0 +1,31 @@
[workspace]
members = ["cli", "ipfilter"]
resolver = "2"
[workspace.package]
authors = ["David Skrundz"]
edition = "2024"
license-file = "LICENSE.md"
rust-version = "1.85.0"
[workspace.lints.rust]
unsafe_code = "forbid"
[profile.release]
codegen-units = 1
lto = "fat"
opt-level = 3
strip = "debuginfo"
[workspace.dependencies]
ipfilter = { path = "ipfilter", version = "=0.0.0", default-features = false }
bincode = { version = "2.0.0-rc.3", default-features = false }
anyhow = { version = "^1", default-features = false }
clap = { version = "^4", default-features = false, features = ["std"] }
csv = { version = "^1", default-features = false }
ipnet = { version = "^2", default-features = false }
iprange = { version = "^0.6", default-features = false }
itertools = { version = "^0.14", default-features = false }
serde = { version = "^1", default-features = false }
+7
View File
@@ -0,0 +1,7 @@
Skrunix Software License
========================
Permission to use, copy, modify, and/or distribute this software is
outlined in the latest v0.0.x of https://github.com/Skrunix/license
THE SOFTWARE IS PROVIDED "AS IS"
+25
View File
@@ -0,0 +1,25 @@
# IP Filter
A set of crates for the construction and use of optimized IP filtering mechanisms.
### Setup
1. Create `private/secret.sh` containing `export IP2LOCATION_TOKEN=''`
1. Run `./private/dl.sh DB1LITECSV DB1LITECSVIPV6`
1. List available countries using `cargo run -- -f private/DB1-LITE-V4.CSV list`
1. Generate a filter using `cargo run -- -i ./private/DB1-LITE-V4.CSV merge -c BD,BR,CN,HK,IL,IN,IQ,IR,KP,PK,QA,RO,RS,RU,SA,SG,SO,SS,SY,TR,TW,UA,DZ -o ./private/filter4.bin`
1. Generate a filter using `cargo run -- -i ./private/DB1-LITE-V6.CSV merge -c BD,BR,CN,HK,IL,IN,IQ,IR,KP,PK,QA,RO,RS,RU,SA,SG,SO,SS,SY,TR,TW,UA,DZ -6 -o ./private/filter6.bin`
1. Verify IP ranges using `cargo run -- -i ./private/filter4.bin load` or `cargo run -- -i ./private/filter6.bin load -6`
###### Round trip checks
1. `diff <(cargo run -- -i ./private/DB1-LITE-V4.CSV merge -c BD,BR,CN,HK,IL,IN,IQ,IR,KP,PK,QA,RO,RS,RU,SA,SG,SO,SS,SY,TR,TW,UA,DZ) <(cargo run -- -i ./private/filter4.bin load)`
1. `diff <(cargo run -- -i ./private/DB1-LITE-V6.CSV merge -c BD,BR,CN,HK,IL,IN,IQ,IR,KP,PK,QA,RO,RS,RU,SA,SG,SO,SS,SY,TR,TW,UA,DZ -6) <(cargo run -- -i ./private/filter6.bin load -6)`
## Various builds
- build: `cargo hack --feature-powerset build`
- clippy: `cargo hack --feature-powerset clippy -- -D warnings`
- fmt: `cargo fmt --check`
- miri: `cargo +nightly hack --feature-powerset miri test`
- docs: `RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features`
- publish: `cargo publish --dry-run -p ipfilter`
+26
View File
@@ -0,0 +1,26 @@
[package]
name = "ipfilter-cli"
version = "0.0.0"
authors.workspace = true
edition.workspace = true
license-file.workspace = true
rust-version.workspace = true
[lints]
workspace = true
[dependencies]
ipfilter = { workspace = true, features = ["std"] }
anyhow = { workspace = true }
clap = { workspace = true, features = [
"derive",
"color",
"help",
"suggestions",
"usage",
] }
csv = { workspace = true }
ipnet = { workspace = true }
itertools = { workspace = true }
+38
View File
@@ -0,0 +1,38 @@
use std::path::PathBuf;
use clap::{Parser, Subcommand};
#[derive(Parser)]
#[command(version, about, long_about = None)]
pub struct Cli {
#[arg(short, long, value_name = "FILE")]
pub input: PathBuf,
#[command(subcommand)]
pub command: Commands,
}
#[derive(Subcommand)]
pub enum Commands {
/// Print a list of all countries and their codes
List,
/// Merge country IP blocks
Merge {
/// Comma separated list of country codes
#[arg(short, long)]
countries: String,
/// IPv6
#[arg(short, default_value = "false")]
_6: bool,
#[arg(short, long, value_name = "FILE")]
output: Option<PathBuf>,
},
/// Load a
Load {
/// IPv6
#[arg(short, default_value = "false")]
_6: bool,
},
}
+109
View File
@@ -0,0 +1,109 @@
use core::net::{Ipv4Addr, Ipv6Addr};
use std::collections::HashSet;
use std::fs;
use ipfilter::{v4, v6};
use anyhow::Result;
use clap::Parser;
use ipnet::{Ipv4Subnets, Ipv6Subnets};
mod cli;
use cli::{Cli, Commands};
use itertools::Itertools;
fn main() -> Result<()> {
let cli = Cli::parse();
let input = fs::File::open(cli.input)?;
match cli.command {
Commands::List => {
let mut reader = csv::ReaderBuilder::new()
.has_headers(false)
.from_reader(input);
let mut countries = HashSet::<(String, String)>::new();
for result in reader.records() {
let record = result?;
countries.insert((record[2].to_owned(), record[3].to_owned()));
}
let mut countries: Vec<_> = countries.drain().collect();
countries.sort_by(|lhs, rhs| lhs.0.cmp(&rhs.0));
for (code, country) in countries {
println!("{code} - {country}");
}
}
Commands::Merge {
countries,
output,
_6,
} => {
let mut reader = csv::ReaderBuilder::new()
.has_headers(false)
.from_reader(input);
let countries: HashSet<String> = countries.split(',').map(ToOwned::to_owned).collect();
let records = reader
.records()
.filter_ok(|r| countries.contains(&r[2]))
.map(|r| r.map_err(anyhow::Error::from));
macro_rules! merge_ip {
($v:ident, $net:ty, $addr:ty) => {
let r = records
.map(|r| {
r.and_then(|record| {
Ok(<$net>::new(
<$addr>::from_bits(record[0].parse()?),
<$addr>::from_bits(record[1].parse()?),
0,
))
})
})
.flatten_ok();
let range = $v::from_fallible_iter(r)?;
if let Some(o) = output {
let mut writer = fs::File::create(o)?;
$v::write_to(range, &mut writer)?;
} else {
for subnet in &range {
println!("{subnet}");
}
};
};
}
if _6 {
merge_ip!(v6, Ipv6Subnets, Ipv6Addr);
} else {
merge_ip!(v4, Ipv4Subnets, Ipv4Addr);
}
}
Commands::Load { _6 } => {
macro_rules! load_ip {
($v:ident, $net:ty, $addr:ty) => {
let range = $v::read_from(&mut &input)?;
for subnet in &range {
println!("{subnet}");
}
};
}
if _6 {
load_ip!(v6, Ipv6Subnets, Ipv6Addr);
} else {
load_ip!(v4, Ipv4Subnets, Ipv4Addr);
}
}
}
Ok(())
}
+28
View File
@@ -0,0 +1,28 @@
[package]
name = "ipfilter"
version = "0.0.0"
description = "A library to streamline IP filtering"
repository = "https://github.com/QuantumShade/ipfilter"
authors.workspace = true
edition.workspace = true
license-file.workspace = true
rust-version.workspace = true
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[lints]
workspace = true
[features]
default = []
std = ["bincode/std"]
[dependencies]
ipnet = { workspace = true }
iprange = { workspace = true, features = ["serde"] }
bincode = { workspace = true, optional = true, features = ["serde"] }
+67
View File
@@ -0,0 +1,67 @@
//! A library to streamline IP filtering. Supports creating, serializing, and
//! deserializing [Ipv4Range] and [Ipv6Range] for optimized filtering.
#![forbid(unused_doc_comments)]
#![forbid(missing_docs)]
#![forbid(unsafe_code)]
#![cfg_attr(docsrs, feature(doc_cfg))]
macro_rules! declare_filter {
($mod:ident, $net:ident) => {
#[doc = concat!("Types and functions for IP", stringify!($mod), " filtering")]
pub mod $mod {
use ipnet::$net;
/// The range type for this module
pub type IpRange = iprange::IpRange<$net>;
/// Create a simplified [IpRange] from a fallible [Iterator]
pub fn from_fallible_iter<I, E>(iter: I) -> Result<IpRange, E>
where
I: Iterator<Item = Result<$net, E>>,
{
let mut range = IpRange::new();
for subnet in iter {
range.add(subnet?);
}
range.simplify();
Ok(range)
}
/// Encode the [IpRange] into any type that implements [std::io::Write]
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub fn write_to<W: std::io::Write>(
range: IpRange,
writer: &mut W,
) -> Result<usize, bincode::error::EncodeError> {
bincode::encode_into_std_write(
bincode::serde::Compat(range),
writer,
bincode::config::standard(),
)
}
/// Decode a [IpRange] from any type that implements [std::io::Read]
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub fn read_from<R: std::io::Read>(
reader: &mut R,
) -> Result<IpRange, bincode::error::DecodeError> {
bincode::decode_from_std_read::<bincode::serde::Compat<_>, _, R>(
reader,
bincode::config::standard(),
)
.map(|x| x.0)
}
}
};
}
declare_filter!(v4, Ipv4Net);
pub use v4::IpRange as Ipv4Range;
declare_filter!(v6, Ipv6Net);
pub use v6::IpRange as Ipv6Range;
+5
View File
@@ -0,0 +1,5 @@
*
!.gitignore
!dl.sh
Executable
+31
View File
@@ -0,0 +1,31 @@
#!/usr/bin/env bash
# This script takes a list of arguments for each database to download
# and extract. Keep in mind that IP2LOCATION has a rate limit.
cd "$(dirname "$0")"
# This script requires `secret.sh` to define the following environment variables:
# - IP2LOCATION_TOKEN
. secret.sh
BASE_URL="https://www.ip2location.com/download/?token=$IP2LOCATION_TOKEN&file="
for CODE in "$@"
do
if [[ $CODE == *IPV6 ]]
then
DB="${CODE::${#CODE}-4}"
ver="V6"
else
DB="$CODE"
ver="V4"
fi
dbx="${DB::${#DB}-7}"
ext="${DB:(-3)}"
curl -L "$BASE_URL$CODE" > tmp.zip
unzip -p tmp.zip "*.$ext" > "$dbx-LITE-$ver.$ext"
rm tmp.zip
done
+2
View File
@@ -0,0 +1,2 @@
[toolchain]
channel = "stable"