Skip to content

Commit

Permalink
Merge pull request #14 from nicholaschiasson/minor/add-cli-binary
Browse files Browse the repository at this point in the history
Add CLI binary
  • Loading branch information
nicholaschiasson authored Jul 10, 2023
2 parents b51c3da + 2ae6ac0 commit 5f2c978
Show file tree
Hide file tree
Showing 5 changed files with 238 additions and 16 deletions.
34 changes: 33 additions & 1 deletion .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,37 @@ jobs:
name: 'Cargo.toml'
path: 'Cargo.toml'

build:
if: github.event_name == 'push' || (github.base_ref == 'main' && github.event.pull_request.merged == true)
strategy:
matrix:
platform: [macos-latest, windows-latest]
runs-on: ${{ matrix.platform }}
needs: [tag]
steps:
- name: Checkout
uses: actions/[email protected]
- name: Download Build Artifacts
uses: actions/[email protected]
with:
name: 'Cargo.toml'
- name: Build
shell: bash
run: |
RAW_BINARY_NAME=fcidr
BINARY_NAME=${RAW_BINARY_NAME}
if [[ ${{ startsWith(matrix.platform, 'windows') }} == true ]]
then
BINARY_NAME=${BINARY_NAME}.exe
fi
cargo build --release --verbose
cp target/release/${BINARY_NAME} ./
tar czf ${RAW_BINARY_NAME}-${{ runner.os }}-${{ runner.arch }}.tar.gz ${BINARY_NAME}
- name: Upload Build Artifact
uses: actions/[email protected]
with:
path: '*.tar.gz'

publish:
if: github.event_name == 'push' || (github.base_ref == 'main' && github.event.pull_request.merged == true)
runs-on: ubuntu-latest
Expand All @@ -108,11 +139,12 @@ jobs:
release:
if: github.event_name == 'push' || (github.base_ref == 'main' && github.event.pull_request.merged == true)
runs-on: ubuntu-latest
needs: [tag]
needs: [tag, build]
steps:
- name: Download Build Artifacts
uses: actions/[email protected]
- name: Release
uses: softprops/[email protected]
with:
files: 'artifact/*.tar.gz'
tag_name: ${{ needs.tag.outputs.version }}
21 changes: 13 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,28 @@ version = "0.0.0"
authors = ["Nicholas Omer Chiasson <[email protected]>"]
edition = "2021"
license = "MIT"
description = """
Fragmented Classless Inter-Domain Routing (FCIDR)
A library exposing a data structure to represent a set of CIDR ranges and
easily manipulate its entries using set-like operations.
"""
description = """Fragmented Classless Inter-Domain Routing (FCIDR)"""
readme = "README.md"
homepage = "https://github.com/nicholaschiasson/fcidr"
repository = "https://github.com/nicholaschiasson/fcidr"
keywords = ["network", "ip", "ipv4", "cidr"]
categories = ["data-structures", "network-programming"]
keywords = ["network", "ip", "ipv4", "cidr", "cli"]
categories = ["command-line-utilities", "data-structures", "network-programming"]
rust-version = "1.70.0"

[lib]
name = "fcidr"
path = "src/lib.rs"

[[bin]]
name = "fcidr"
path = "src/main.rs"

[badges]
github = { repository = "nicholaschiasson/fcidr" }
maintenance = { status = "passively-maintained" }

[dependencies]
clap = { version = "4.3", features = ["derive"] }
serde = { version = "1.0", optional = true }

[dev-dependencies]
Expand Down
109 changes: 107 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,117 @@

Fragmented Classless Inter-Domain Routing (FCIDR)

A library exposing a data structure to represent a set of CIDR ranges and
easily manipulate its entries using set-like operations.
A library exposing a data structure to represent a set of CIDR ranges as well
as an interface to compute set operations over CIDRs.

This data structure can be applied, for example, in configuring firewalls that
*implicitly deny* (AWS Security Groups) using a rule set that explicitly
expresses rules for both allow and deny.

> **Note**
> Currently, only IPv4 is supported. IPv6 support is tracked by [#6](https://github.com/nicholaschiasson/fcidr/issues/6).
## CLI

This project also publishes a binary application for use on the command line to
support composing chains of set operations on CIDRs by reading from standard
input.

### Installation

For now, crates.io is the only place this is being distributed.

```
cargo install fcidr
```

### Usage

```
Fragmented Classless Inter-Domain Routing (FCIDR)
Usage: fcidr [CIDR] <COMMAND>
Commands:
complement Compute the complement of the input CIDR(s) [aliases: !, not]
difference Compute the set difference between the input CIDR(s) and another CIDR [aliases: -, exclude, minus]
union Compute the set union of the input CIDR(s) and another CIDR [aliases: +, include, plus]
help Print this message or the help of the given subcommand(s)
Arguments:
[CIDR] The input CIDR range and first operand to the computation. If omitted, input is taken from stdin. In this way, multiple computations can be chained together
Options:
-h, --help Print help
-V, --version Print version`
```

### Example

```
fcidr 10.0.0.0/8 difference 10.0.64.0/20 | fcidr difference 10.0.82.0/24 | fcidr union 10.0.82.74/31
10.0.0.0/18
10.0.80.0/23
10.0.82.74/31
10.0.83.0/24
10.0.84.0/22
10.0.88.0/21
10.0.96.0/19
10.0.128.0/17
10.1.0.0/16
10.2.0.0/15
10.4.0.0/14
10.8.0.0/13
10.16.0.0/12
10.32.0.0/11
10.64.0.0/10
10.128.0.0/9
```

```
fcidr 10.0.0.0/8 difference 10.0.64.0/20 | fcidr difference 10.0.82.0/24 | fcidr union 10.0.82.74/31 | fcidr complement
0.0.0.0/5
8.0.0.0/7
10.0.64.0/20
10.0.82.0/26
10.0.82.64/29
10.0.82.72/31
10.0.82.76/30
10.0.82.80/28
10.0.82.96/27
10.0.82.128/25
11.0.0.0/8
12.0.0.0/6
16.0.0.0/4
32.0.0.0/3
64.0.0.0/2
128.0.0.0/1
```

Alternative concise syntax:

```
fcidr 10.0.0.0/8 + 127.0.0.0/16 | fcidr - 10.64.0.0/16 | fcidr !
0.0.0.0/5
8.0.0.0/7
10.64.0.0/16
11.0.0.0/8
12.0.0.0/6
16.0.0.0/4
32.0.0.0/3
64.0.0.0/3
96.0.0.0/4
112.0.0.0/5
120.0.0.0/6
124.0.0.0/7
126.0.0.0/8
127.1.0.0/16
127.2.0.0/15
127.4.0.0/14
127.8.0.0/13
127.16.0.0/12
127.32.0.0/11
127.64.0.0/10
127.128.0.0/9
128.0.0.0/1
```
14 changes: 9 additions & 5 deletions src/fcidr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,11 @@ impl Iterator for FcidrIntoIterator {

// #[test]
// fn it_works() {
// // let mut fcidr = Fcidr::default();
// let mut fcidr = Fcidr::default();
// fcidr.union("10.0.0.0/8".parse().unwrap());
// fcidr.union("10.0.128.0/24".parse().unwrap());
// fcidr.difference("10.0.80.0/20".parse().unwrap());
// fcidr.union("10.0.82.0/24".parse().unwrap());
// // fcidr.union("10.0.0.0/24".parse().unwrap());
// // fcidr.union("10.0.128.0/25".parse().unwrap());
// // fcidr.union("11.0.0.0/8".parse().unwrap());
Expand All @@ -262,9 +266,9 @@ impl Iterator for FcidrIntoIterator {
// // fcidr.union("0.0.0.0/0".parse().unwrap());
// // fcidr.difference("10.0.0.1/32".parse().unwrap());
// // println!("{:?}", fcidr.iter().collect::<Vec<_>>());
// // for cidr in &fcidr {
// // println!("{cidr}");
// // }
// // println!("{fcidr:?}");
// for cidr in &fcidr {
// println!("{cidr}");
// }
// println!("{fcidr:?}");
// }
// }
76 changes: 76 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
use std::{
error::Error,
io::{stdin, IsTerminal},
};

use clap::{CommandFactory, Parser, Subcommand};
use fcidr::{Cidr, Fcidr};

#[derive(Debug, Parser)]
#[command(about, author, version, long_about = None)]
struct Cli {
/// The input CIDR range and first operand to the computation. If omitted,
/// input is taken from stdin. In this way, multiple computations can be
/// chained together.
cidr: Option<Cidr>,
#[command(subcommand)]
command: FcidrCommand,
}

#[derive(Debug, Subcommand)]
enum FcidrCommand {
/// Compute the complement of the input CIDR(s)
#[command(visible_alias = "!", visible_alias = "not")]
Complement,
/// Compute the set difference between the input CIDR(s) and another CIDR
#[command(
visible_alias = "-",
visible_alias = "exclude",
visible_alias = "minus"
)]
Difference {
/// The second CIDR range operand for the difference function
cidr: Cidr,
},
#[command(visible_alias = "+", visible_alias = "include", visible_alias = "plus")]
/// Compute the set union of the input CIDR(s) and another CIDR
Union {
/// The second CIDR range operand for the union function
cidr: Cidr,
},
}

fn main() -> Result<(), Box<dyn Error>> {
let cli = Cli::parse();

let mut fcidr: Fcidr = if let Some(cidr) = cli.cidr {
Fcidr::new(cidr)
} else {
if stdin().is_terminal() {
Cli::command().print_help().unwrap();
::std::process::exit(2);
}
stdin().lines().fold(
Ok(Fcidr::default()),
|fcidr: Result<Fcidr, Box<dyn Error>>, l| {
if let Ok(mut fcidr) = fcidr {
fcidr.union(l?.parse()?);
return Ok(fcidr);
}
fcidr
},
)?
};

match cli.command {
FcidrCommand::Complement => fcidr.complement(),
FcidrCommand::Difference { cidr } => fcidr.difference(cidr),
FcidrCommand::Union { cidr } => fcidr.union(cidr),
};

for cidr in fcidr {
println!("{cidr}");
}

Ok(())
}

0 comments on commit 5f2c978

Please sign in to comment.