From b5b895a62b605276a270fa48aba9c4644330f60c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Gamb=C3=B6ck?= Date: Sun, 7 May 2023 11:55:26 +0200 Subject: [PATCH 01/26] Start new development cycle 0.5.0-SNAPSHOT --- CHANGES.md | 2 ++ Cargo.lock | 2 +- Cargo.toml | 2 +- VERSION | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index a88df4d..228493c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,5 @@ +# Changes since latest release + # Changes in 0.4.0 - Upgrade clap for a better CLI experience diff --git a/Cargo.lock b/Cargo.lock index f97c7a1..3a4e11e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -704,7 +704,7 @@ dependencies = [ [[package]] name = "upnp-daemon" -version = "0.4.0" +version = "0.5.0-SNAPSHOT" dependencies = [ "anyhow", "cidr-utils", diff --git a/Cargo.toml b/Cargo.toml index 87359cc..c2fead6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "upnp-daemon" -version = "0.4.0" +version = "0.5.0-SNAPSHOT" authors = ["Florian Gamböck "] edition = "2021" diff --git a/VERSION b/VERSION index 1d0ba9e..f0334e9 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.4.0 +0.5.0-SNAPSHOT From 1ea1e8a86bfb751c5788c9b915db870ec41ae6fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Gamb=C3=B6ck?= Date: Sun, 11 Jun 2023 13:52:23 +0200 Subject: [PATCH 02/26] Continue current development cycle 0.5.0-SNAPSHOT --- Cargo.lock | 2 +- Cargo.toml | 2 +- VERSION | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d31527b..8f15b02 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -702,7 +702,7 @@ dependencies = [ [[package]] name = "upnp-daemon" -version = "0.4.1" +version = "0.5.0-SNAPSHOT" dependencies = [ "anyhow", "cidr-utils", diff --git a/Cargo.toml b/Cargo.toml index 6cba324..c2fead6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "upnp-daemon" -version = "0.4.1" +version = "0.5.0-SNAPSHOT" authors = ["Florian Gamböck "] edition = "2021" diff --git a/VERSION b/VERSION index 267577d..f0334e9 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.4.1 +0.5.0-SNAPSHOT From 1ff3abcc4777638b0f20ba84140763ab2c971be5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Gamb=C3=B6ck?= Date: Sun, 7 May 2023 18:55:25 +0200 Subject: [PATCH 03/26] Split application and lib into workspaces --- Cargo.lock | 20 +++ Cargo.toml | 43 ++++-- easy-upnp/Cargo.toml | 29 ++++ easy-upnp/LICENSE | 13 ++ easy-upnp/README.md | 3 + easy-upnp/VERSION | 1 + {src => easy-upnp/src}/lib.rs | 8 +- src/cli.rs | 248 ---------------------------------- src/main.rs | 247 ++++++++++++++++++++++++++++++++- 9 files changed, 348 insertions(+), 264 deletions(-) create mode 100644 easy-upnp/Cargo.toml create mode 100644 easy-upnp/LICENSE create mode 100644 easy-upnp/README.md create mode 100644 easy-upnp/VERSION rename {src => easy-upnp/src}/lib.rs (99%) delete mode 100644 src/cli.rs diff --git a/Cargo.lock b/Cargo.lock index 8f15b02..cf488e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -222,6 +222,25 @@ version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f578e8e2c440e7297e008bb5486a3a8a194775224bbc23729b0dbdfaeebf162e" +[[package]] +name = "easy-upnp" +version = "0.1.0-SNAPSHOT" +dependencies = [ + "anyhow", + "cidr-utils", + "clap", + "csv", + "ctrlc", + "daemonize", + "env_logger", + "get_if_addrs", + "igd", + "log", + "serde", + "serde_json", + "tempfile", +] + [[package]] name = "env_logger" version = "0.10.0" @@ -710,6 +729,7 @@ dependencies = [ "csv", "ctrlc", "daemonize", + "easy-upnp", "env_logger", "get_if_addrs", "igd", diff --git a/Cargo.toml b/Cargo.toml index c2fead6..3fb86df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,22 +1,50 @@ [package] name = "upnp-daemon" version = "0.5.0-SNAPSHOT" -authors = ["Florian Gamböck "] -edition = "2021" - description = "A daemon for continuously opening ports via UPnP." -repository = "https://github.com/FloGa/upnp-daemon" readme = "README.md" -categories = ["network-programming"] -license = "WTFPL" +categories = ["command-line-utilities", "network-programming"] include = ["src/**/*", "LICENSE", "README.md"] +authors.workspace = true +edition.workspace = true +repository.workspace = true +license.workspace = true + [dependencies] +easy-upnp.workspace = true + +anyhow.workspace = true +cidr-utils.workspace = true +clap.workspace = true +csv.workspace = true +ctrlc.workspace = true +env_logger.workspace = true +get_if_addrs.workspace = true +igd.workspace = true +log.workspace = true +serde.workspace = true +serde_json.workspace = true +tempfile.workspace = true + +[target.'cfg(unix)'.dependencies] +daemonize.workspace = true + +[workspace.package] +authors = ["Florian Gamböck "] +edition = "2021" +repository = "https://github.com/FloGa/upnp-daemon" +license = "WTFPL" + +[workspace.dependencies] +easy-upnp = { version = "0.1.0-SNAPSHOT", path = "easy-upnp" } + anyhow = "1.0.70" cidr-utils = { version = "0.5.10", features = ["serde"] } clap = { version = "4.2.4", features = ["derive"] } csv = "1.1" ctrlc = { version = "3.0", features = ["termination"] } +daemonize = "0.5.0" env_logger = "0.10.0" get_if_addrs = "0.5.3" igd = "0.12.0" @@ -24,6 +52,3 @@ log = "0.4.11" serde = { version = "1", features = ["derive"] } serde_json = "1.0.96" tempfile = "3.5.0" - -[target.'cfg(unix)'.dependencies] -daemonize = "0.5.0" diff --git a/easy-upnp/Cargo.toml b/easy-upnp/Cargo.toml new file mode 100644 index 0000000..ee42dc7 --- /dev/null +++ b/easy-upnp/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "easy-upnp" +version = "0.1.0-SNAPSHOT" +description = "The business logic for upnp-daemon." +readme = "README.md" +categories = ["network-programming"] +include = ["src/**/*", "LICENSE", "README.md"] + +authors.workspace = true +edition.workspace = true +repository.workspace = true +license.workspace = true + +[dependencies] +anyhow.workspace = true +cidr-utils.workspace = true +clap.workspace = true +csv.workspace = true +ctrlc.workspace = true +env_logger.workspace = true +get_if_addrs.workspace = true +igd.workspace = true +log.workspace = true +serde.workspace = true +serde_json.workspace = true +tempfile.workspace = true + +[target.'cfg(unix)'.dependencies] +daemonize.workspace = true diff --git a/easy-upnp/LICENSE b/easy-upnp/LICENSE new file mode 100644 index 0000000..5c93f45 --- /dev/null +++ b/easy-upnp/LICENSE @@ -0,0 +1,13 @@ + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + + Copyright (C) 2004 Sam Hocevar + + Everyone is permitted to copy and distribute verbatim or modified + copies of this license document, and changing it is allowed as long + as the name is changed. + + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO. diff --git a/easy-upnp/README.md b/easy-upnp/README.md new file mode 100644 index 0000000..109ee0f --- /dev/null +++ b/easy-upnp/README.md @@ -0,0 +1,3 @@ +# easy-upnp + +The business logic for upnp-daemon. diff --git a/easy-upnp/VERSION b/easy-upnp/VERSION new file mode 100644 index 0000000..b694fe3 --- /dev/null +++ b/easy-upnp/VERSION @@ -0,0 +1 @@ +0.1.0-SNAPSHOT diff --git a/src/lib.rs b/easy-upnp/src/lib.rs similarity index 99% rename from src/lib.rs rename to easy-upnp/src/lib.rs index 44b0d61..e2c0761 100644 --- a/src/lib.rs +++ b/easy-upnp/src/lib.rs @@ -341,10 +341,6 @@ use igd::{AddPortError, Gateway, SearchOptions}; use log::{debug, error, info, warn}; use serde::Deserialize; -pub use cli::Cli; - -mod cli; - #[derive(Clone, Copy, Debug, Deserialize)] pub enum PortMappingProtocol { TCP, @@ -492,7 +488,7 @@ impl UpnpConfig { } } -fn add_ports(configs: impl Iterator>) { +pub fn add_ports(configs: impl Iterator>) { for config in configs { match config { Ok(config) => { @@ -508,7 +504,7 @@ fn add_ports(configs: impl Iterator>) { } } -fn delete_ports(configs: impl Iterator>) { +pub fn delete_ports(configs: impl Iterator>) { for config in configs { match config { Ok(config) => { diff --git a/src/cli.rs b/src/cli.rs deleted file mode 100644 index 4bec967..0000000 --- a/src/cli.rs +++ /dev/null @@ -1,248 +0,0 @@ -use std::error::Error; -use std::fs::File; -use std::io::{stdin, BufReader, BufWriter, Seek}; -use std::path::PathBuf; -use std::sync::mpsc::{channel, RecvTimeoutError}; -use std::time::Duration; - -use anyhow::anyhow; -use clap::{ - builder::{PathBufValueParser, TypedValueParser}, - Parser, ValueEnum, -}; -use csv::Reader; -#[cfg(unix)] -use daemonize::Daemonize; -use serde_json::Value; -use tempfile::tempfile; - -use crate::{add_ports, delete_ports, UpnpConfig}; - -#[derive(Clone)] -enum CliInput { - File(PathBuf), - Stdin, -} - -impl TryFrom for CliInput { - type Error = std::io::Error; - - fn try_from(path: PathBuf) -> Result { - Ok(if path == PathBuf::from("-") { - CliInput::Stdin - } else { - CliInput::File(path.canonicalize()?) - }) - } -} - -enum Input { - File(File), - PathBuf(PathBuf), -} - -impl TryFrom for Input { - type Error = std::io::Error; - - fn try_from(cli_input: CliInput) -> Result { - Ok(match cli_input { - CliInput::File(pathbuf) => Self::PathBuf(pathbuf), - CliInput::Stdin => { - // Write contents of stdin to temporary file, so we can read it multiple times. - let tempfile = tempfile()?; - { - let mut reader = BufReader::new(stdin()); - let mut writer = BufWriter::new(&tempfile); - std::io::copy(&mut reader, &mut writer)?; - } - Self::File(tempfile) - } - }) - } -} - -fn get_csv_reader(input: &Input, delim: char) -> Result, std::io::Error> { - let mut builder = csv::ReaderBuilder::new(); - let reader_builder = builder.delimiter(delim as u8); - - Ok(match input { - Input::File(file) => { - // Clone file handle, so we don't move the original handle away. - let mut file = file.try_clone()?; - - // File may have been advanced in previous iteration, so rewind it first. - file.rewind()?; - reader_builder.from_reader(file) - } - Input::PathBuf(pathbuf) => reader_builder.from_path(pathbuf)?, - }) -} - -fn get_configs_from_csv_reader( - reader: &mut Reader, -) -> impl Iterator> + '_ { - reader - .deserialize() - .map(|result| result.map_err(anyhow::Error::from)) -} - -fn get_configs_from_json( - input: &Input, -) -> anyhow::Result> + '_> { - let file = match input { - Input::File(file) => { - // Clone file handle, so we don't move the original handle away. - let mut file = file.try_clone()?; - - // File may have been advanced in previous iteration, so rewind it first. - file.rewind()?; - file - } - Input::PathBuf(pathbuf) => File::open(pathbuf)?, - }; - - let v: Value = serde_json::from_reader(file)?; - - if !v.is_array() { - return Err(anyhow!("Input is not a JSON array")); - } - - Ok(if let Value::Array(v) = v { - v.into_iter() - .map(|v| serde_json::from_value::(v).map_err(anyhow::Error::from)) - } else { - unreachable!() - }) -} - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] -enum CliInputFormat { - Csv, - Json, -} - -#[derive(Parser)] -#[clap(author, version, about, long_about = None)] -pub struct Cli { - #[arg(long, short, value_parser = PathBufValueParser::new().try_map(CliInput::try_from))] - /// The file (or "-" for stdin) with the port descriptions - file: CliInput, - - #[arg(long, value_enum, default_value_t = CliInputFormat::Csv)] - /// The format of the configuration file - format: CliInputFormat, - - #[arg(long, short = 'd', default_value_t = ';')] - /// Field delimiter when using CSV files - csv_delimiter: char, - - #[cfg(unix)] - #[arg(long, short = 'F')] - /// Run in foreground instead of forking to background - foreground: bool, - - #[arg(long, short = '1')] - /// Run just one time instead of continuously - oneshot: bool, - - #[arg(long, short = 'n', default_value_t = 60)] - /// Specify update interval in seconds - interval: u64, - - #[arg(long)] - /// Close specified ports on program exit - close_ports_on_exit: bool, - - #[arg(long)] - /// Only close specified ports and exit - only_close_ports: bool, - - #[cfg(unix)] - #[arg(long, default_value = "/tmp/upnp-daemon.pid")] - /// Absolute path to PID file for daemon mode - pid_file: PathBuf, -} - -impl Cli { - pub fn run() -> Result<(), Box> { - let cli = Cli::parse(); - - // Handle file here, because reading from stdin will fail in daemon mode. - let file = cli.file.try_into()?; - - #[cfg(unix)] - if !cli.foreground { - Daemonize::new() - .pid_file(cli.pid_file) - .start() - .expect("Failed to daemonize."); - } - - let (tx_quitter, rx_quitter) = channel(); - - { - let tx_quitter = tx_quitter.clone(); - ctrlc::set_handler(move || { - tx_quitter.send(true).unwrap(); - }) - .expect("Error setting Ctrl-C handler"); - } - - loop { - if !cli.only_close_ports { - match cli.format { - CliInputFormat::Csv => { - let mut rdr = get_csv_reader(&file, cli.csv_delimiter)?; - let configs = get_configs_from_csv_reader(&mut rdr); - add_ports(configs); - } - CliInputFormat::Json => { - let configs = get_configs_from_json(&file)?; - add_ports(configs); - } - } - } - - if cli.oneshot || cli.only_close_ports { - tx_quitter.send(true)?; - } - - match rx_quitter.recv_timeout(Duration::from_secs(cli.interval)) { - Err(RecvTimeoutError::Timeout) => { - // Timeout reached without being interrupted, continue with loop - } - Err(e) => { - // Something bad happened - panic!("{}", e); - } - Ok(_) => { - // Quit signal received, break loop and quit nicely - - if cli.close_ports_on_exit || cli.only_close_ports { - match cli.format { - CliInputFormat::Csv => { - let mut rdr = get_csv_reader(&file, cli.csv_delimiter)?; - let configs = get_configs_from_csv_reader(&mut rdr); - delete_ports(configs); - } - CliInputFormat::Json => { - let configs = get_configs_from_json(&file)?; - delete_ports(configs); - } - } - } - - break; - } - } - } - - Ok(()) - } -} - -#[test] -fn verify_app() { - use clap::CommandFactory; - Cli::command().debug_assert() -} diff --git a/src/main.rs b/src/main.rs index 7fc374f..654e80d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,245 @@ use std::error::Error; +use std::fs::File; +use std::io::{stdin, BufReader, BufWriter, Seek}; +use std::path::PathBuf; +use std::sync::mpsc::{channel, RecvTimeoutError}; +use std::time::Duration; -use upnp_daemon::Cli; +use anyhow::anyhow; +use clap::{ + builder::{PathBufValueParser, TypedValueParser}, + Parser, ValueEnum, +}; +use csv::Reader; +#[cfg(unix)] +use daemonize::Daemonize; +use serde_json::Value; +use tempfile::tempfile; + +use easy_upnp::{add_ports, delete_ports, UpnpConfig}; + +#[derive(Clone)] +enum CliInput { + File(PathBuf), + Stdin, +} + +impl TryFrom for CliInput { + type Error = std::io::Error; + + fn try_from(path: PathBuf) -> Result { + Ok(if path == PathBuf::from("-") { + CliInput::Stdin + } else { + CliInput::File(path.canonicalize()?) + }) + } +} + +enum Input { + File(File), + PathBuf(PathBuf), +} + +impl TryFrom for Input { + type Error = std::io::Error; + + fn try_from(cli_input: CliInput) -> Result { + Ok(match cli_input { + CliInput::File(pathbuf) => Self::PathBuf(pathbuf), + CliInput::Stdin => { + // Write contents of stdin to temporary file, so we can read it multiple times. + let tempfile = tempfile()?; + { + let mut reader = BufReader::new(stdin()); + let mut writer = BufWriter::new(&tempfile); + std::io::copy(&mut reader, &mut writer)?; + } + Self::File(tempfile) + } + }) + } +} + +fn get_csv_reader(input: &Input, delim: char) -> Result, std::io::Error> { + let mut builder = csv::ReaderBuilder::new(); + let reader_builder = builder.delimiter(delim as u8); + + Ok(match input { + Input::File(file) => { + // Clone file handle, so we don't move the original handle away. + let mut file = file.try_clone()?; + + // File may have been advanced in previous iteration, so rewind it first. + file.rewind()?; + reader_builder.from_reader(file) + } + Input::PathBuf(pathbuf) => reader_builder.from_path(pathbuf)?, + }) +} + +fn get_configs_from_csv_reader( + reader: &mut Reader, +) -> impl Iterator> + '_ { + reader + .deserialize() + .map(|result| result.map_err(anyhow::Error::from)) +} + +fn get_configs_from_json( + input: &Input, +) -> anyhow::Result> + '_> { + let file = match input { + Input::File(file) => { + // Clone file handle, so we don't move the original handle away. + let mut file = file.try_clone()?; + + // File may have been advanced in previous iteration, so rewind it first. + file.rewind()?; + file + } + Input::PathBuf(pathbuf) => File::open(pathbuf)?, + }; + + let v: Value = serde_json::from_reader(file)?; + + if !v.is_array() { + return Err(anyhow!("Input is not a JSON array")); + } + + Ok(if let Value::Array(v) = v { + v.into_iter() + .map(|v| serde_json::from_value::(v).map_err(anyhow::Error::from)) + } else { + unreachable!() + }) +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] +enum CliInputFormat { + Csv, + Json, +} + +#[derive(Parser)] +#[clap(author, version, about, long_about = None)] +pub struct Cli { + #[arg(long, short, value_parser = PathBufValueParser::new().try_map(CliInput::try_from))] + /// The file (or "-" for stdin) with the port descriptions + file: CliInput, + + #[arg(long, value_enum, default_value_t = CliInputFormat::Csv)] + /// The format of the configuration file + format: CliInputFormat, + + #[arg(long, short = 'd', default_value_t = ';')] + /// Field delimiter when using CSV files + csv_delimiter: char, + + #[cfg(unix)] + #[arg(long, short = 'F')] + /// Run in foreground instead of forking to background + foreground: bool, + + #[arg(long, short = '1')] + /// Run just one time instead of continuously + oneshot: bool, + + #[arg(long, short = 'n', default_value_t = 60)] + /// Specify update interval in seconds + interval: u64, + + #[arg(long)] + /// Close specified ports on program exit + close_ports_on_exit: bool, + + #[arg(long)] + /// Only close specified ports and exit + only_close_ports: bool, + + #[cfg(unix)] + #[arg(long, default_value = "/tmp/upnp-daemon.pid")] + /// Absolute path to PID file for daemon mode + pid_file: PathBuf, +} + +impl Cli { + pub fn run() -> Result<(), Box> { + let cli = Cli::parse(); + + // Handle file here, because reading from stdin will fail in daemon mode. + let file = cli.file.try_into()?; + + #[cfg(unix)] + if !cli.foreground { + Daemonize::new() + .pid_file(cli.pid_file) + .start() + .expect("Failed to daemonize."); + } + + let (tx_quitter, rx_quitter) = channel(); + + { + let tx_quitter = tx_quitter.clone(); + ctrlc::set_handler(move || { + tx_quitter.send(true).unwrap(); + }) + .expect("Error setting Ctrl-C handler"); + } + + loop { + if !cli.only_close_ports { + match cli.format { + CliInputFormat::Csv => { + let mut rdr = get_csv_reader(&file, cli.csv_delimiter)?; + let configs = get_configs_from_csv_reader(&mut rdr); + add_ports(configs); + } + CliInputFormat::Json => { + let configs = get_configs_from_json(&file)?; + add_ports(configs); + } + } + } + + if cli.oneshot || cli.only_close_ports { + tx_quitter.send(true)?; + } + + match rx_quitter.recv_timeout(Duration::from_secs(cli.interval)) { + Err(RecvTimeoutError::Timeout) => { + // Timeout reached without being interrupted, continue with loop + } + Err(e) => { + // Something bad happened + panic!("{}", e); + } + Ok(_) => { + // Quit signal received, break loop and quit nicely + + if cli.close_ports_on_exit || cli.only_close_ports { + match cli.format { + CliInputFormat::Csv => { + let mut rdr = get_csv_reader(&file, cli.csv_delimiter)?; + let configs = get_configs_from_csv_reader(&mut rdr); + delete_ports(configs); + } + CliInputFormat::Json => { + let configs = get_configs_from_json(&file)?; + delete_ports(configs); + } + } + } + + break; + } + } + } + + Ok(()) + } +} fn main() -> Result<(), Box> { env_logger::init(); @@ -9,3 +248,9 @@ fn main() -> Result<(), Box> { Ok(()) } + +#[test] +fn verify_app() { + use clap::CommandFactory; + Cli::command().debug_assert() +} From 066c330de65140ecd6a0012c10112394a624f207 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Gamb=C3=B6ck?= Date: Sun, 7 May 2023 20:11:55 +0200 Subject: [PATCH 04/26] Update workflows to support workspaces --- .github/workflows/release.yml | 5 ++--- .github/workflows/test.yml | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0f9adf0..47923dd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,10 +3,9 @@ name: Release on: push: branches: + - master - 'hotfix/**' - 'release/**' - tags-ignore: - - '_**' workflow_dispatch: @@ -17,7 +16,7 @@ jobs: test-args: -- --include-ignored call-release-workflow: - uses: FloGa/rust-workflows/.github/workflows/release.yml@0.2.0 + uses: FloGa/rust-workflows/.github/workflows/release.yml@0.4.0 with: targets-config: ./.github/targets.json secrets: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 67d664e..d119dc8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -29,4 +29,4 @@ jobs: run-check: true run-clippy: true run-fmt: true - test-args: ${{ inputs.test-args }} + test-args: --workspace ${{ inputs.test-args }} From a6aecb08a9493b4900b1ba8d0135fb1c49dcde02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Gamb=C3=B6ck?= Date: Sat, 15 Jul 2023 15:53:30 +0200 Subject: [PATCH 05/26] Move documentation to bin package --- easy-upnp/src/lib.rs | 335 ------------------------------------------- src/main.rs | 335 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 335 insertions(+), 335 deletions(-) diff --git a/easy-upnp/src/lib.rs b/easy-upnp/src/lib.rs index e2c0761..d96fdfb 100644 --- a/easy-upnp/src/lib.rs +++ b/easy-upnp/src/lib.rs @@ -1,338 +1,3 @@ -//! # UPnP daemon -//! -//! [![badge github]][url github] -//! [![badge crates.io]][url crates.io] -//! [![badge docs.rs]][url docs.rs] -//! [![badge license]][url license] -//! -//! [badge github]: https://img.shields.io/badge/github-FloGa%2Fupnp--daemon-green -//! [badge crates.io]: https://img.shields.io/crates/v/upnp-daemon -//! [badge docs.rs]: https://img.shields.io/docsrs/upnp-daemon -//! [badge license]: https://img.shields.io/crates/l/upnp-daemon -//! -//! [url github]: https://github.com/FloGa/upnp-daemon -//! [url crates.io]: https://crates.io/crates/upnp-daemon -//! [url docs.rs]: https://docs.rs/upnp-daemon -//! [url license]: https://github.com/FloGa/upnp-daemon/blob/develop/LICENSE -//! -//! A daemon for continuously opening ports via UPnP. -//! -//! ## Motivation -//! -//! There are quite some programs out there that need certain network ports to be -//! open to work properly, but do not provide the capability for opening them -//! automatically via UPnP. Sure, one could always argue about the security -//! implications that come with UPnP, but if you are willing to take the risk, it -//! is just annoying, that for example your webserver is not reachable from the -//! internet, because you forgot to open port 80, or your router rebooted and -//! cleared the table of open ports. Or your machine does for whatever reason not -//! have a static IP address, so you cannot add a consistent port mapping. -//! -//! Because of this frustration, I created `upnp-daemon`, a small service written -//! in Rust, that will periodically check a file with your defined port mappings -//! and send them to your router. The main usage will be that you start it once -//! and let it run as a background service forever. The file with the port -//! mappings will be newly read in on each iteration, so you can add new mappings -//! on the fly. -//! -//! ## Installation -//! -//! upnp-daemon can be installed easily through Cargo via `crates.io`: -//! -//! ```shell script -//! cargo install --locked upnp-daemon -//! ``` -//! -//! Please note that the `--locked` flag is necessary here to have the exact same -//! dependencies as when the application was tagged and tested. Without it, you -//! might get more up-to-date versions of dependencies, but you have the risk of -//! undefined and unexpected behavior if the dependencies changed some -//! functionalities. The application might even fail to build if the public API of -//! a dependency changed too much. -//! -//! Alternatively, pre-built binaries can be downloaded from the [GitHub -//! releases][gh-releases] page. -//! -//! [gh-releases]: https://github.com/FloGa/upnp-daemon/releases -//! -//! ## Usage -//! -//! ```text -//! Usage: upnp-daemon [OPTIONS] --file -//! -//! Options: -//! -f, --file The file (or "-" for stdin) with the port descriptions -//! --format The format of the configuration file [default: csv] [possible values: csv, json] -//! -d, --csv-delimiter Field delimiter when using CSV files [default: ;] -//! -F, --foreground Run in foreground instead of forking to background -//! -1, --oneshot Run just one time instead of continuously -//! -n, --interval Specify update interval in seconds [default: 60] -//! --close-ports-on-exit Close specified ports on program exit -//! --only-close-ports Only close specified ports and exit -//! --pid-file Absolute path to PID file for daemon mode [default: /tmp/upnp-daemon.pid] -//! -h, --help Print help -//! -V, --version Print version -//! ``` -//! -//! In the most basic case, a call might look like so: -//! -//! ```shell script -//! upnp-daemon --file ports.csv -//! ``` -//! -//! This will start a background process (daemon) that reads in port mappings from -//! a CSV file (see [config file format](#config-file-format)) every minute and -//! ask the appropriate routers to open those ports. -//! -//! The PID of the process will be written to `/tmp/upnp-daemon.pid` by default -//! and locked exclusively, so that only one instance is running at a time. To -//! quit it, kill the PID that is written in this file. -//! -//! Bash can do it like so: -//! -//! ```shell script -//! kill $( +//! +//! Options: +//! -f, --file The file (or "-" for stdin) with the port descriptions +//! --format The format of the configuration file [default: csv] [possible values: csv, json] +//! -d, --csv-delimiter Field delimiter when using CSV files [default: ;] +//! -F, --foreground Run in foreground instead of forking to background +//! -1, --oneshot Run just one time instead of continuously +//! -n, --interval Specify update interval in seconds [default: 60] +//! --close-ports-on-exit Close specified ports on program exit +//! --only-close-ports Only close specified ports and exit +//! --pid-file Absolute path to PID file for daemon mode [default: /tmp/upnp-daemon.pid] +//! -h, --help Print help +//! -V, --version Print version +//! ``` +//! +//! In the most basic case, a call might look like so: +//! +//! ```shell script +//! upnp-daemon --file ports.csv +//! ``` +//! +//! This will start a background process (daemon) that reads in port mappings from +//! a CSV file (see [config file format](#config-file-format)) every minute and +//! ask the appropriate routers to open those ports. +//! +//! The PID of the process will be written to `/tmp/upnp-daemon.pid` by default +//! and locked exclusively, so that only one instance is running at a time. To +//! quit it, kill the PID that is written in this file. +//! +//! Bash can do it like so: +//! +//! ```shell script +//! kill $( Date: Sat, 15 Jul 2023 15:55:33 +0200 Subject: [PATCH 06/26] Use global include pattern --- Cargo.toml | 4 +++- easy-upnp/Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3fb86df..3978fa1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,12 +4,12 @@ version = "0.5.0-SNAPSHOT" description = "A daemon for continuously opening ports via UPnP." readme = "README.md" categories = ["command-line-utilities", "network-programming"] -include = ["src/**/*", "LICENSE", "README.md"] authors.workspace = true edition.workspace = true repository.workspace = true license.workspace = true +include.workspace = true [dependencies] easy-upnp.workspace = true @@ -36,6 +36,8 @@ edition = "2021" repository = "https://github.com/FloGa/upnp-daemon" license = "WTFPL" +include = ["src/**/*", "LICENSE", "README.md"] + [workspace.dependencies] easy-upnp = { version = "0.1.0-SNAPSHOT", path = "easy-upnp" } diff --git a/easy-upnp/Cargo.toml b/easy-upnp/Cargo.toml index ee42dc7..d1a42bf 100644 --- a/easy-upnp/Cargo.toml +++ b/easy-upnp/Cargo.toml @@ -4,12 +4,12 @@ version = "0.1.0-SNAPSHOT" description = "The business logic for upnp-daemon." readme = "README.md" categories = ["network-programming"] -include = ["src/**/*", "LICENSE", "README.md"] authors.workspace = true edition.workspace = true repository.workspace = true license.workspace = true +include.workspace = true [dependencies] anyhow.workspace = true From 083e9cccfe990111dc93ed187be526b0f229ff6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Gamb=C3=B6ck?= Date: Sat, 15 Jul 2023 16:08:23 +0200 Subject: [PATCH 07/26] Keep only relevant dependencies --- Cargo.lock | 12 ------------ Cargo.toml | 5 ----- easy-upnp/Cargo.toml | 9 --------- 3 files changed, 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cf488e6..0fefbb2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -228,17 +228,10 @@ version = "0.1.0-SNAPSHOT" dependencies = [ "anyhow", "cidr-utils", - "clap", - "csv", - "ctrlc", - "daemonize", - "env_logger", "get_if_addrs", "igd", "log", "serde", - "serde_json", - "tempfile", ] [[package]] @@ -724,17 +717,12 @@ name = "upnp-daemon" version = "0.5.0-SNAPSHOT" dependencies = [ "anyhow", - "cidr-utils", "clap", "csv", "ctrlc", "daemonize", "easy-upnp", "env_logger", - "get_if_addrs", - "igd", - "log", - "serde", "serde_json", "tempfile", ] diff --git a/Cargo.toml b/Cargo.toml index 3978fa1..e9e06b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,15 +15,10 @@ include.workspace = true easy-upnp.workspace = true anyhow.workspace = true -cidr-utils.workspace = true clap.workspace = true csv.workspace = true ctrlc.workspace = true env_logger.workspace = true -get_if_addrs.workspace = true -igd.workspace = true -log.workspace = true -serde.workspace = true serde_json.workspace = true tempfile.workspace = true diff --git a/easy-upnp/Cargo.toml b/easy-upnp/Cargo.toml index d1a42bf..ac95b01 100644 --- a/easy-upnp/Cargo.toml +++ b/easy-upnp/Cargo.toml @@ -14,16 +14,7 @@ include.workspace = true [dependencies] anyhow.workspace = true cidr-utils.workspace = true -clap.workspace = true -csv.workspace = true -ctrlc.workspace = true -env_logger.workspace = true get_if_addrs.workspace = true igd.workspace = true log.workspace = true serde.workspace = true -serde_json.workspace = true -tempfile.workspace = true - -[target.'cfg(unix)'.dependencies] -daemonize.workspace = true From 15ca66dda584452f4b909476b9eeae5a420c89ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Gamb=C3=B6ck?= Date: Sat, 15 Jul 2023 16:09:11 +0200 Subject: [PATCH 08/26] Move crate to subfolder --- Cargo.toml | 2 +- {easy-upnp => crates/easy-upnp}/Cargo.toml | 0 {easy-upnp => crates/easy-upnp}/LICENSE | 0 {easy-upnp => crates/easy-upnp}/README.md | 0 {easy-upnp => crates/easy-upnp}/VERSION | 0 {easy-upnp => crates/easy-upnp}/src/lib.rs | 0 6 files changed, 1 insertion(+), 1 deletion(-) rename {easy-upnp => crates/easy-upnp}/Cargo.toml (100%) rename {easy-upnp => crates/easy-upnp}/LICENSE (100%) rename {easy-upnp => crates/easy-upnp}/README.md (100%) rename {easy-upnp => crates/easy-upnp}/VERSION (100%) rename {easy-upnp => crates/easy-upnp}/src/lib.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index e9e06b5..53bf206 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ license = "WTFPL" include = ["src/**/*", "LICENSE", "README.md"] [workspace.dependencies] -easy-upnp = { version = "0.1.0-SNAPSHOT", path = "easy-upnp" } +easy-upnp = { version = "0.1.0-SNAPSHOT", path = "crates/easy-upnp" } anyhow = "1.0.70" cidr-utils = { version = "0.5.10", features = ["serde"] } diff --git a/easy-upnp/Cargo.toml b/crates/easy-upnp/Cargo.toml similarity index 100% rename from easy-upnp/Cargo.toml rename to crates/easy-upnp/Cargo.toml diff --git a/easy-upnp/LICENSE b/crates/easy-upnp/LICENSE similarity index 100% rename from easy-upnp/LICENSE rename to crates/easy-upnp/LICENSE diff --git a/easy-upnp/README.md b/crates/easy-upnp/README.md similarity index 100% rename from easy-upnp/README.md rename to crates/easy-upnp/README.md diff --git a/easy-upnp/VERSION b/crates/easy-upnp/VERSION similarity index 100% rename from easy-upnp/VERSION rename to crates/easy-upnp/VERSION diff --git a/easy-upnp/src/lib.rs b/crates/easy-upnp/src/lib.rs similarity index 100% rename from easy-upnp/src/lib.rs rename to crates/easy-upnp/src/lib.rs From 35e248cccf57c27a858b0f8435789e19a39b94c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Gamb=C3=B6ck?= Date: Sun, 16 Jul 2023 15:22:19 +0200 Subject: [PATCH 09/26] Don't use Result as parameter In the lib it does not make sense to do error handling from the CLI. This should happen in the CLI module. --- Cargo.lock | 2 +- Cargo.toml | 1 + crates/easy-upnp/Cargo.toml | 1 - crates/easy-upnp/src/lib.rs | 28 +++++++--------------------- src/main.rs | 22 ++++++++++++++++++---- 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0fefbb2..66e1fdd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -226,7 +226,6 @@ checksum = "f578e8e2c440e7297e008bb5486a3a8a194775224bbc23729b0dbdfaeebf162e" name = "easy-upnp" version = "0.1.0-SNAPSHOT" dependencies = [ - "anyhow", "cidr-utils", "get_if_addrs", "igd", @@ -723,6 +722,7 @@ dependencies = [ "daemonize", "easy-upnp", "env_logger", + "log", "serde_json", "tempfile", ] diff --git a/Cargo.toml b/Cargo.toml index 53bf206..e001c99 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ clap.workspace = true csv.workspace = true ctrlc.workspace = true env_logger.workspace = true +log.workspace = true serde_json.workspace = true tempfile.workspace = true diff --git a/crates/easy-upnp/Cargo.toml b/crates/easy-upnp/Cargo.toml index ac95b01..865a32d 100644 --- a/crates/easy-upnp/Cargo.toml +++ b/crates/easy-upnp/Cargo.toml @@ -12,7 +12,6 @@ license.workspace = true include.workspace = true [dependencies] -anyhow.workspace = true cidr-utils.workspace = true get_if_addrs.workspace = true igd.workspace = true diff --git a/crates/easy-upnp/src/lib.rs b/crates/easy-upnp/src/lib.rs index d96fdfb..c7ceb94 100644 --- a/crates/easy-upnp/src/lib.rs +++ b/crates/easy-upnp/src/lib.rs @@ -153,32 +153,18 @@ impl UpnpConfig { } } -pub fn add_ports(configs: impl Iterator>) { +pub fn add_ports(configs: impl Iterator) { for config in configs { - match config { - Ok(config) => { - info!("Add port: {:?}", config); - if let Err(err) = config.add_port() { - error!("{}", err); - } - } - Err(err) => { - error!("{}", err); - } + info!("Add port: {:?}", config); + if let Err(err) = config.add_port() { + error!("{}", err); } } } -pub fn delete_ports(configs: impl Iterator>) { +pub fn delete_ports(configs: impl Iterator) { for config in configs { - match config { - Ok(config) => { - info!("Remove port: {:?}", config); - config.remove_port(); - } - Err(err) => { - error!("{}", err); - } - } + info!("Remove port: {:?}", config); + config.remove_port(); } } diff --git a/src/main.rs b/src/main.rs index 934639a..af7b939 100644 --- a/src/main.rs +++ b/src/main.rs @@ -348,6 +348,7 @@ use clap::{ use csv::Reader; #[cfg(unix)] use daemonize::Daemonize; +use log::error; use serde_json::Value; use tempfile::tempfile; @@ -450,6 +451,15 @@ fn get_configs_from_json( }) } +fn filter_out_and_log_errors(result: anyhow::Result) -> Option { + result + .map_err(|err| { + error!("{}", err); + err + }) + .ok() +} + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] enum CliInputFormat { Csv, @@ -528,11 +538,13 @@ impl Cli { match cli.format { CliInputFormat::Csv => { let mut rdr = get_csv_reader(&file, cli.csv_delimiter)?; - let configs = get_configs_from_csv_reader(&mut rdr); + let configs = get_configs_from_csv_reader(&mut rdr) + .filter_map(filter_out_and_log_errors); add_ports(configs); } CliInputFormat::Json => { - let configs = get_configs_from_json(&file)?; + let configs = + get_configs_from_json(&file)?.filter_map(filter_out_and_log_errors); add_ports(configs); } } @@ -557,11 +569,13 @@ impl Cli { match cli.format { CliInputFormat::Csv => { let mut rdr = get_csv_reader(&file, cli.csv_delimiter)?; - let configs = get_configs_from_csv_reader(&mut rdr); + let configs = get_configs_from_csv_reader(&mut rdr) + .filter_map(filter_out_and_log_errors); delete_ports(configs); } CliInputFormat::Json => { - let configs = get_configs_from_json(&file)?; + let configs = get_configs_from_json(&file)? + .filter_map(filter_out_and_log_errors); delete_ports(configs); } } From de298d73b47f3aec525d469224a4ef64af69318b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Gamb=C3=B6ck?= Date: Sun, 16 Jul 2023 15:32:00 +0200 Subject: [PATCH 10/26] Use IntoIterator to be more flexible --- crates/easy-upnp/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/easy-upnp/src/lib.rs b/crates/easy-upnp/src/lib.rs index c7ceb94..3629be0 100644 --- a/crates/easy-upnp/src/lib.rs +++ b/crates/easy-upnp/src/lib.rs @@ -153,7 +153,7 @@ impl UpnpConfig { } } -pub fn add_ports(configs: impl Iterator) { +pub fn add_ports(configs: impl IntoIterator) { for config in configs { info!("Add port: {:?}", config); if let Err(err) = config.add_port() { @@ -162,7 +162,7 @@ pub fn add_ports(configs: impl Iterator) { } } -pub fn delete_ports(configs: impl Iterator) { +pub fn delete_ports(configs: impl IntoIterator) { for config in configs { info!("Remove port: {:?}", config); config.remove_port(); From 7e8f13fc7eae5b26a725c93e0ddedc182a352edd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Gamb=C3=B6ck?= Date: Sun, 16 Jul 2023 15:34:01 +0200 Subject: [PATCH 11/26] [auto] Update README and sync doc --- crates/easy-upnp/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/easy-upnp/src/lib.rs b/crates/easy-upnp/src/lib.rs index 3629be0..55af1e7 100644 --- a/crates/easy-upnp/src/lib.rs +++ b/crates/easy-upnp/src/lib.rs @@ -1,3 +1,7 @@ +//! # easy-upnp +//! +//! The business logic for upnp-daemon. + use std::error::Error; use std::net::{IpAddr, SocketAddr, SocketAddrV4}; From 67799c80e2c05a00930ed2b83d4e5428952eef92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Gamb=C3=B6ck?= Date: Sun, 16 Jul 2023 17:59:57 +0200 Subject: [PATCH 12/26] Use automatic link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 76e05bb..5b393e5 100644 --- a/README.md +++ b/README.md @@ -310,7 +310,7 @@ to specify a JSON array. `192.168.0`. More examples can be found in the responsible library's documentation: - https://docs.rs/cidr-utils/0.5.10/cidr_utils/index.html + - port From 55989ac577eb29505643e646afb5710c17ce9983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Gamb=C3=B6ck?= Date: Sun, 16 Jul 2023 18:02:39 +0200 Subject: [PATCH 13/26] Use docstrings before attributes --- src/main.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main.rs b/src/main.rs index af7b939..2f0b952 100644 --- a/src/main.rs +++ b/src/main.rs @@ -469,42 +469,42 @@ enum CliInputFormat { #[derive(Parser)] #[clap(author, version, about, long_about = None)] pub struct Cli { - #[arg(long, short, value_parser = PathBufValueParser::new().try_map(CliInput::try_from))] /// The file (or "-" for stdin) with the port descriptions + #[arg(long, short, value_parser = PathBufValueParser::new().try_map(CliInput::try_from))] file: CliInput, - #[arg(long, value_enum, default_value_t = CliInputFormat::Csv)] /// The format of the configuration file + #[arg(long, value_enum, default_value_t = CliInputFormat::Csv)] format: CliInputFormat, - #[arg(long, short = 'd', default_value_t = ';')] /// Field delimiter when using CSV files + #[arg(long, short = 'd', default_value_t = ';')] csv_delimiter: char, + /// Run in foreground instead of forking to background #[cfg(unix)] #[arg(long, short = 'F')] - /// Run in foreground instead of forking to background foreground: bool, - #[arg(long, short = '1')] /// Run just one time instead of continuously + #[arg(long, short = '1')] oneshot: bool, - #[arg(long, short = 'n', default_value_t = 60)] /// Specify update interval in seconds + #[arg(long, short = 'n', default_value_t = 60)] interval: u64, - #[arg(long)] /// Close specified ports on program exit + #[arg(long)] close_ports_on_exit: bool, - #[arg(long)] /// Only close specified ports and exit + #[arg(long)] only_close_ports: bool, + /// Absolute path to PID file for daemon mode #[cfg(unix)] #[arg(long, default_value = "/tmp/upnp-daemon.pid")] - /// Absolute path to PID file for daemon mode pid_file: PathBuf, } From 8c1d0c6458e997d4efddecfe3530d16ba60db622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Gamb=C3=B6ck?= Date: Sun, 16 Jul 2023 18:03:06 +0200 Subject: [PATCH 14/26] Remove unnecessary pub modifiers --- src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index 2f0b952..00ce382 100644 --- a/src/main.rs +++ b/src/main.rs @@ -468,7 +468,7 @@ enum CliInputFormat { #[derive(Parser)] #[clap(author, version, about, long_about = None)] -pub struct Cli { +struct Cli { /// The file (or "-" for stdin) with the port descriptions #[arg(long, short, value_parser = PathBufValueParser::new().try_map(CliInput::try_from))] file: CliInput, @@ -509,7 +509,7 @@ pub struct Cli { } impl Cli { - pub fn run() -> Result<(), Box> { + fn run() -> Result<(), Box> { let cli = Cli::parse(); // Handle file here, because reading from stdin will fail in daemon mode. From 8b13cefc63ca84dfa3596f64596a81ceff80f8c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Gamb=C3=B6ck?= Date: Sun, 16 Jul 2023 18:04:53 +0200 Subject: [PATCH 15/26] Document public components --- crates/easy-upnp/src/lib.rs | 114 ++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/crates/easy-upnp/src/lib.rs b/crates/easy-upnp/src/lib.rs index 55af1e7..31f63a1 100644 --- a/crates/easy-upnp/src/lib.rs +++ b/crates/easy-upnp/src/lib.rs @@ -10,6 +10,9 @@ use igd::{AddPortError, Gateway, SearchOptions}; use log::{debug, error, info, warn}; use serde::Deserialize; +/// The protocol for which the given port will be opened. Possible values are +/// [`UDP`](PortMappingProtocol::UDP) and [`TCP`](PortMappingProtocol::TCP). +#[allow(missing_docs)] #[derive(Clone, Copy, Debug, Deserialize)] pub enum PortMappingProtocol { TCP, @@ -109,12 +112,79 @@ fn get_gateway_and_address_from_options( } } +/// This struct defines a configuration for a port mapping. +/// +/// The configuration consists of all necessary pieces of information for a proper port opening. +/// +/// # Examples +/// +/// ``` +/// use cidr_utils::cidr::Ipv4Cidr; +/// +/// use easy_upnp::{PortMappingProtocol, UpnpConfig}; +/// +/// # fn main() -> Result<(), Box> { +/// let config_no_address = UpnpConfig { +/// address: None, +/// port: 80, +/// protocol: PortMappingProtocol::TCP, +/// duration: 3600, +/// comment: "Webserver".to_string(), +/// }; +/// +/// let config_specific_address = UpnpConfig { +/// address: Some(Ipv4Cidr::from_str("192.168.0.10/24")?), +/// port: 80, +/// protocol: PortMappingProtocol::TCP, +/// duration: 3600, +/// comment: "Webserver".to_string(), +/// }; +/// +/// let config_address_range = UpnpConfig { +/// address: Some(Ipv4Cidr::from_str("192.168.0")?), +/// port: 80, +/// protocol: PortMappingProtocol::TCP, +/// duration: 3600, +/// comment: "Webserver".to_string(), +/// }; +/// # +/// # Ok(()) +/// # } +/// ``` #[derive(Debug, Deserialize)] pub struct UpnpConfig { + /// The IP address for which the port mapping should be added. + /// + /// This field can be [None], in which case every connected interface will be tried, until one + /// gateway reports success. Useful if the IP address is dynamic and not consistent over + /// reboots. + /// + /// Fill in an IP address if you want to add a port mapping for a foreign device, or if you + /// know your machine's address and want to slightly speed up the process. + /// + /// For examples how to specify IP addresses, check the documentation of [Ipv4Cidr]. pub address: Option, + + /// The port number to open for the given IP address. + /// + /// Note that we are greedy at the moment, if a port mapping is already in place, it will be + /// deleted and re-added with the given IP address. This might be configurable in a future + /// release. pub port: u16, + + /// The protocol for which the given port will be opened. Possible values are + /// [`UDP`](PortMappingProtocol::UDP) and [`TCP`](PortMappingProtocol::TCP). pub protocol: PortMappingProtocol, + + /// The lease duration for the port mapping in seconds. + /// + /// Please note that some UPnP capable routers might choose to ignore this value, so do not + /// exclusively rely on this. pub duration: u32, + + /// A comment about the reason for the port mapping. + /// + /// Will be stored together with the mapping in the router. pub comment: String, } @@ -157,6 +227,28 @@ impl UpnpConfig { } } +/// Add port mappings. +/// +/// This function takes an iterable of [UpnpConfig]s and opens all configures ports. +/// +/// Errors are logged, but otherwise ignored. An error during opening a port will not stop the +/// processing of the other ports. +/// +/// # Example +/// +/// ```no_run +/// use easy_upnp::{add_ports, PortMappingProtocol, UpnpConfig}; +/// +/// let config = UpnpConfig { +/// address: None, +/// port: 80, +/// protocol: PortMappingProtocol::TCP, +/// duration: 3600, +/// comment: "Webserver".to_string(), +/// }; +/// +/// add_ports([config]); +/// ``` pub fn add_ports(configs: impl IntoIterator) { for config in configs { info!("Add port: {:?}", config); @@ -166,6 +258,28 @@ pub fn add_ports(configs: impl IntoIterator) { } } +/// Delete port mappings. +/// +/// This function takes an iterable of [UpnpConfig]s and closes all configures ports. +/// +/// Errors are logged, but otherwise ignored. An error during closing a port will not stop the +/// processing of the other ports. +/// +/// # Example +/// +/// ```no_run +/// use easy_upnp::{delete_ports, PortMappingProtocol, UpnpConfig}; +/// +/// let config = UpnpConfig { +/// address: None, +/// port: 80, +/// protocol: PortMappingProtocol::TCP, +/// duration: 3600, +/// comment: "Webserver".to_string(), +/// }; +/// +/// delete_ports([config]); +/// ``` pub fn delete_ports(configs: impl IntoIterator) { for config in configs { info!("Remove port: {:?}", config); From f63f2cb50c3b3edb8e8765e55db08594fd48e7d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Gamb=C3=B6ck?= Date: Sun, 16 Jul 2023 18:05:06 +0200 Subject: [PATCH 16/26] Enforce documentation --- crates/easy-upnp/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/easy-upnp/src/lib.rs b/crates/easy-upnp/src/lib.rs index 31f63a1..e7ff7a4 100644 --- a/crates/easy-upnp/src/lib.rs +++ b/crates/easy-upnp/src/lib.rs @@ -2,6 +2,8 @@ //! //! The business logic for upnp-daemon. +#![deny(missing_docs)] + use std::error::Error; use std::net::{IpAddr, SocketAddr, SocketAddrV4}; From eb44279de45c06c502d641b0a9f198a7d7af1d99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Gamb=C3=B6ck?= Date: Sun, 16 Jul 2023 22:11:41 +0200 Subject: [PATCH 17/26] Add proper README --- crates/easy-upnp/README.md | 76 +++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/crates/easy-upnp/README.md b/crates/easy-upnp/README.md index 109ee0f..fbc9312 100644 --- a/crates/easy-upnp/README.md +++ b/crates/easy-upnp/README.md @@ -1,3 +1,77 @@ # easy-upnp -The business logic for upnp-daemon. +[![badge github]][url github] +[![badge crates.io]][url crates.io] +[![badge docs.rs]][url docs.rs] +[![badge license]][url license] + +[badge github]: https://img.shields.io/badge/github-FloGa%2Fupnp--daemon-green +[badge crates.io]: https://img.shields.io/crates/v/easy-upnp +[badge docs.rs]: https://img.shields.io/docsrs/easy-upnp +[badge license]: https://img.shields.io/crates/l/easy-upnp + +[url github]: https://github.com/FloGa/upnp-daemon/crates/easy-upnp +[url crates.io]: https://crates.io/crates/easy-upnp +[url docs.rs]: https://docs.rs/easy-upnp +[url license]: +https://github.com/FloGa/upnp-daemon/blob/develop/crates/easy-upnp/LICENSE + +A minimalistic wrapper around [IGD] to open and close network ports via +[UPnP]. Mainly this library is used in the CLI application [`upnp-daemon`], +but it can also be used as a library in other crates that just want to open +and close ports with minimal possible configuration. + +[IGD]: https://docs.rs/igd/ +[UPnP]: https://en.wikipedia.org/wiki/Universal_Plug_and_Play +[`upnp-daemon`]: https://github.com/FloGa/upnp-daemon + +## Example + +Here is a hands-on example to demonstrate the usage. It will add some ports and immediately remove them again. + +```rust no_run +use std::error::Error; + +use cidr_utils::cidr::Ipv4Cidr; +use easy_upnp::{add_ports, delete_ports, PortMappingProtocol, UpnpConfig}; + +fn get_configs() -> Result<[UpnpConfig; 3], Box> { + let config_no_address = UpnpConfig { + address: None, + port: 80, + protocol: PortMappingProtocol::TCP, + duration: 3600, + comment: "Webserver".to_string(), + }; + + let config_specific_address = UpnpConfig { + address: Some(Ipv4Cidr::from_str("192.168.0.10/24")?), + port: 8080, + protocol: PortMappingProtocol::TCP, + duration: 3600, + comment: "Webserver alternative".to_string(), + }; + + let config_address_range = UpnpConfig { + address: Some(Ipv4Cidr::from_str("192.168.0")?), + port: 8081, + protocol: PortMappingProtocol::TCP, + duration: 3600, + comment: "Webserver second alternative".to_string(), + }; + + Ok([ + config_no_address, + config_specific_address, + config_address_range, + ]) +} + +fn main() -> Result<(), Box> { + add_ports(get_configs()?); + + delete_ports(get_configs()?); + + Ok(()) +} +``` From e562dc05d24f0a34419a35009ea63537ca605ef2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Gamb=C3=B6ck?= Date: Sun, 16 Jul 2023 22:12:37 +0200 Subject: [PATCH 18/26] [auto] Update README and sync doc --- crates/easy-upnp/src/lib.rs | 76 ++++++++++++++++++++++++++++++++++++- src/main.rs | 2 +- 2 files changed, 76 insertions(+), 2 deletions(-) diff --git a/crates/easy-upnp/src/lib.rs b/crates/easy-upnp/src/lib.rs index e7ff7a4..b24a935 100644 --- a/crates/easy-upnp/src/lib.rs +++ b/crates/easy-upnp/src/lib.rs @@ -1,6 +1,80 @@ //! # easy-upnp //! -//! The business logic for upnp-daemon. +//! [![badge github]][url github] +//! [![badge crates.io]][url crates.io] +//! [![badge docs.rs]][url docs.rs] +//! [![badge license]][url license] +//! +//! [badge github]: https://img.shields.io/badge/github-FloGa%2Fupnp--daemon-green +//! [badge crates.io]: https://img.shields.io/crates/v/easy-upnp +//! [badge docs.rs]: https://img.shields.io/docsrs/easy-upnp +//! [badge license]: https://img.shields.io/crates/l/easy-upnp +//! +//! [url github]: https://github.com/FloGa/upnp-daemon/crates/easy-upnp +//! [url crates.io]: https://crates.io/crates/easy-upnp +//! [url docs.rs]: https://docs.rs/easy-upnp +//! [url license]: +//! https://github.com/FloGa/upnp-daemon/blob/develop/crates/easy-upnp/LICENSE +//! +//! A minimalistic wrapper around [IGD] to open and close network ports via +//! [UPnP]. Mainly this library is used in the CLI application [`upnp-daemon`], +//! but it can also be used as a library in other crates that just want to open +//! and close ports with minimal possible configuration. +//! +//! [IGD]: https://docs.rs/igd/ +//! [UPnP]: https://en.wikipedia.org/wiki/Universal_Plug_and_Play +//! [`upnp-daemon`]: https://github.com/FloGa/upnp-daemon +//! +//! ## Example +//! +//! Here is a hands-on example to demonstrate the usage. It will add some ports and immediately remove them again. +//! +//! ```rust no_run +//! use std::error::Error; +//! +//! use cidr_utils::cidr::Ipv4Cidr; +//! use easy_upnp::{add_ports, delete_ports, PortMappingProtocol, UpnpConfig}; +//! +//! fn get_configs() -> Result<[UpnpConfig; 3], Box> { +//! let config_no_address = UpnpConfig { +//! address: None, +//! port: 80, +//! protocol: PortMappingProtocol::TCP, +//! duration: 3600, +//! comment: "Webserver".to_string(), +//! }; +//! +//! let config_specific_address = UpnpConfig { +//! address: Some(Ipv4Cidr::from_str("192.168.0.10/24")?), +//! port: 8080, +//! protocol: PortMappingProtocol::TCP, +//! duration: 3600, +//! comment: "Webserver alternative".to_string(), +//! }; +//! +//! let config_address_range = UpnpConfig { +//! address: Some(Ipv4Cidr::from_str("192.168.0")?), +//! port: 8081, +//! protocol: PortMappingProtocol::TCP, +//! duration: 3600, +//! comment: "Webserver second alternative".to_string(), +//! }; +//! +//! Ok([ +//! config_no_address, +//! config_specific_address, +//! config_address_range, +//! ]) +//! } +//! +//! fn main() -> Result<(), Box> { +//! add_ports(get_configs()?); +//! +//! delete_ports(get_configs()?); +//! +//! Ok(()) +//! } +//! ``` #![deny(missing_docs)] diff --git a/src/main.rs b/src/main.rs index 00ce382..c9e663f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -308,7 +308,7 @@ //! `192.168.0`. //! //! More examples can be found in the responsible library's documentation: -//! https://docs.rs/cidr-utils/0.5.10/cidr_utils/index.html +//! //! //! - port //! From 3a4602904ef9d1f73934f104d9c4a5d231bc2c23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Gamb=C3=B6ck?= Date: Wed, 2 Aug 2023 22:54:20 +0200 Subject: [PATCH 19/26] Add dependencies for integration tests --- Cargo.lock | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 9 ++++ 2 files changed, 138 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 66e1fdd..ff6450a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -66,6 +66,21 @@ version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +[[package]] +name = "assert_cmd" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88903cb14723e4d4003335bb7f8a14f27691649105346a0f0957466c096adfe6" +dependencies = [ + "anstyle", + "bstr", + "doc-comment", + "predicates", + "predicates-core", + "predicates-tree", + "wait-timeout", +] + [[package]] name = "attohttpc" version = "0.16.3" @@ -90,6 +105,17 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bstr" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" +dependencies = [ + "memchr", + "regex-automata", + "serde", +] + [[package]] name = "bytes" version = "1.4.0" @@ -222,6 +248,18 @@ version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f578e8e2c440e7297e008bb5486a3a8a194775224bbc23729b0dbdfaeebf162e" +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + [[package]] name = "easy-upnp" version = "0.1.0-SNAPSHOT" @@ -233,6 +271,12 @@ dependencies = [ "serde", ] +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + [[package]] name = "env_logger" version = "0.10.0" @@ -276,6 +320,15 @@ dependencies = [ "instant", ] +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +dependencies = [ + "num-traits", +] + [[package]] name = "fnv" version = "1.0.7" @@ -414,12 +467,27 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.146" @@ -456,6 +524,12 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + [[package]] name = "num-bigint" version = "0.4.3" @@ -504,6 +578,37 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "predicates" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09963355b9f467184c04017ced4a2ba2d75cbcb4e7462690d388233253d4b1a9" +dependencies = [ + "anstyle", + "difflib", + "float-cmp", + "itertools", + "normalize-line-endings", + "predicates-core", + "regex", +] + +[[package]] +name = "predicates-core" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" + +[[package]] +name = "predicates-tree" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" +dependencies = [ + "predicates-core", + "termtree", +] + [[package]] name = "proc-macro2" version = "1.0.60" @@ -572,6 +677,12 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "regex-automata" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" + [[package]] name = "regex-syntax" version = "0.7.2" @@ -675,6 +786,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "termtree" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" + [[package]] name = "tinyvec" version = "1.6.0" @@ -716,13 +833,16 @@ name = "upnp-daemon" version = "0.5.0-SNAPSHOT" dependencies = [ "anyhow", + "assert_cmd", "clap", "csv", "ctrlc", "daemonize", "easy-upnp", "env_logger", + "lazy_static", "log", + "predicates", "serde_json", "tempfile", ] @@ -744,6 +864,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index e001c99..23b2db1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,11 @@ tempfile.workspace = true [target.'cfg(unix)'.dependencies] daemonize.workspace = true +[dev-dependencies] +assert_cmd.workspace = true +lazy_static.workspace = true +predicates.workspace = true + [workspace.package] authors = ["Florian Gamböck "] edition = "2021" @@ -50,3 +55,7 @@ log = "0.4.11" serde = { version = "1", features = ["derive"] } serde_json = "1.0.96" tempfile = "3.5.0" + +assert_cmd = "2.0.11" +lazy_static = "1.4.0" +predicates = "*" # resolve via assert_cmd From a54d64a4b045e0935f2bd8deb2306bd858c51855 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Gamb=C3=B6ck?= Date: Wed, 2 Aug 2023 22:54:50 +0200 Subject: [PATCH 20/26] Add some integration tests --- tests/cli.rs | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 tests/cli.rs diff --git a/tests/cli.rs b/tests/cli.rs new file mode 100644 index 0000000..20f8f85 --- /dev/null +++ b/tests/cli.rs @@ -0,0 +1,53 @@ +use std::path::PathBuf; + +use assert_cmd::Command; +use lazy_static::lazy_static; +use predicates::prelude::*; + +lazy_static! { + static ref BIN_PATH: PathBuf = assert_cmd::cargo::cargo_bin("upnp-daemon"); +} + +#[test] +fn help_works() { + Command::new(&*BIN_PATH) + .arg("--help") + .assert() + .success() + .stdout(predicate::str::contains("\nUsage:")); +} + +#[test] +fn correct_version() { + let version = env!("CARGO_PKG_VERSION"); + + Command::new(&*BIN_PATH) + .arg("--version") + .assert() + .success() + .stdout(format!("{} {}\n", "upnp-daemon", version)); +} + +#[test] +fn empty_csv_input_passes() { + Command::new(&*BIN_PATH).arg("-1Ff-").assert().success(); +} + +#[test] +fn empty_json_input_fails() { + Command::new(&*BIN_PATH) + .arg("-1Ff-") + .arg("--format=json") + .assert() + .failure(); +} + +#[test] +fn empty_json_array_input_passes() { + Command::new(&*BIN_PATH) + .arg("-1Ff-") + .arg("--format=json") + .write_stdin("[]") + .assert() + .success(); +} From 1a84228d90f51a38e57bfd8736c34b265455d42a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Gamb=C3=B6ck?= Date: Sat, 5 Aug 2023 13:08:21 +0200 Subject: [PATCH 21/26] Add some comments to config sections --- Cargo.toml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 23b2db1..0409fe1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,8 @@ assert_cmd.workspace = true lazy_static.workspace = true predicates.workspace = true +# --- Workspace configuration --- + [workspace.package] authors = ["Florian Gamböck "] edition = "2021" @@ -40,8 +42,13 @@ license = "WTFPL" include = ["src/**/*", "LICENSE", "README.md"] [workspace.dependencies] + +# Internal dependencies + easy-upnp = { version = "0.1.0-SNAPSHOT", path = "crates/easy-upnp" } +# Runtime dependencies + anyhow = "1.0.70" cidr-utils = { version = "0.5.10", features = ["serde"] } clap = { version = "4.2.4", features = ["derive"] } @@ -56,6 +63,8 @@ serde = { version = "1", features = ["derive"] } serde_json = "1.0.96" tempfile = "3.5.0" +# Development / test dependencies + assert_cmd = "2.0.11" lazy_static = "1.4.0" predicates = "*" # resolve via assert_cmd From 0a652fe6168ef495a84e39feaf381ebf071a0ab8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Gamb=C3=B6ck?= Date: Sat, 5 Aug 2023 13:23:04 +0200 Subject: [PATCH 22/26] Bump versions to RC - 0.5.0-RC - easy-upnp-0.1.0-RC --- CHANGES.md | 2 +- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- VERSION | 2 +- crates/easy-upnp/Cargo.toml | 2 +- crates/easy-upnp/VERSION | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 4ca9b30..df94657 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,4 +1,4 @@ -# Changes since latest release +# Changes in 0.5.0-RC # Changes in 0.4.1 diff --git a/Cargo.lock b/Cargo.lock index ff6450a..61b2fca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -262,7 +262,7 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "easy-upnp" -version = "0.1.0-SNAPSHOT" +version = "0.1.0-RC" dependencies = [ "cidr-utils", "get_if_addrs", @@ -830,7 +830,7 @@ dependencies = [ [[package]] name = "upnp-daemon" -version = "0.5.0-SNAPSHOT" +version = "0.5.0-RC" dependencies = [ "anyhow", "assert_cmd", diff --git a/Cargo.toml b/Cargo.toml index 0409fe1..9b76d4d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "upnp-daemon" -version = "0.5.0-SNAPSHOT" +version = "0.5.0-RC" description = "A daemon for continuously opening ports via UPnP." readme = "README.md" categories = ["command-line-utilities", "network-programming"] @@ -45,7 +45,7 @@ include = ["src/**/*", "LICENSE", "README.md"] # Internal dependencies -easy-upnp = { version = "0.1.0-SNAPSHOT", path = "crates/easy-upnp" } +easy-upnp = { version = "0.1.0-RC", path = "crates/easy-upnp" } # Runtime dependencies diff --git a/VERSION b/VERSION index f0334e9..819d957 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.5.0-SNAPSHOT +0.5.0-RC diff --git a/crates/easy-upnp/Cargo.toml b/crates/easy-upnp/Cargo.toml index 865a32d..363ae5d 100644 --- a/crates/easy-upnp/Cargo.toml +++ b/crates/easy-upnp/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "easy-upnp" -version = "0.1.0-SNAPSHOT" +version = "0.1.0-RC" description = "The business logic for upnp-daemon." readme = "README.md" categories = ["network-programming"] diff --git a/crates/easy-upnp/VERSION b/crates/easy-upnp/VERSION index b694fe3..c87e310 100644 --- a/crates/easy-upnp/VERSION +++ b/crates/easy-upnp/VERSION @@ -1 +1 @@ -0.1.0-SNAPSHOT +0.1.0-RC From 43c94933c014ba9504a7f098e748577588c29ab6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Gamb=C3=B6ck?= Date: Sat, 5 Aug 2023 13:40:33 +0200 Subject: [PATCH 23/26] Add proper description for easy-upnp --- crates/easy-upnp/Cargo.toml | 2 +- crates/easy-upnp/README.md | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/easy-upnp/Cargo.toml b/crates/easy-upnp/Cargo.toml index 363ae5d..ed8b33c 100644 --- a/crates/easy-upnp/Cargo.toml +++ b/crates/easy-upnp/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "easy-upnp" version = "0.1.0-RC" -description = "The business logic for upnp-daemon." +description = "Easily open and close UPnP ports." readme = "README.md" categories = ["network-programming"] diff --git a/crates/easy-upnp/README.md b/crates/easy-upnp/README.md index fbc9312..85d26dc 100644 --- a/crates/easy-upnp/README.md +++ b/crates/easy-upnp/README.md @@ -16,6 +16,8 @@ [url license]: https://github.com/FloGa/upnp-daemon/blob/develop/crates/easy-upnp/LICENSE +Easily open and close UPnP ports. + A minimalistic wrapper around [IGD] to open and close network ports via [UPnP]. Mainly this library is used in the CLI application [`upnp-daemon`], but it can also be used as a library in other crates that just want to open From 73a024434103e3bffb23a942730676951363ff44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Gamb=C3=B6ck?= Date: Sat, 5 Aug 2023 13:43:05 +0200 Subject: [PATCH 24/26] Update dependencies --- Cargo.lock | 172 +++++++++++++++++++++++++---------------------------- 1 file changed, 80 insertions(+), 92 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 61b2fca..448216c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,15 +28,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" [[package]] name = "anstyle-parse" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" dependencies = [ "utf8parse", ] @@ -62,9 +62,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.71" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" [[package]] name = "assert_cmd" @@ -105,6 +105,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" + [[package]] name = "bstr" version = "1.6.0" @@ -130,9 +136,12 @@ checksum = "4964518bd3b4a8190e832886cdc0da9794f12e8e6c1613a9e90ff331c4c8724b" [[package]] name = "cc" -version = "1.0.79" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "6c6b2562119bf28c3439f7f02db99faf0aa1a8cdfe5772a2ee155d32227239f0" +dependencies = [ + "libc", +] [[package]] name = "cfg-if" @@ -156,9 +165,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.3" +version = "4.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca8f255e4b8027970e78db75e78831229c9815fdbfa67eb1a1b777a62e24b4a0" +checksum = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d" dependencies = [ "clap_builder", "clap_derive", @@ -167,22 +176,21 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.3.3" +version = "4.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acd4f3c17c83b0ba34ffbc4f8bbd74f079413f747f84a6f89292f138057e36ab" +checksum = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1" dependencies = [ "anstream", "anstyle", - "bitflags", "clap_lex", "strsim", ] [[package]] name = "clap_derive" -version = "4.3.2" +version = "4.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" +checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" dependencies = [ "heck", "proc-macro2", @@ -273,9 +281,9 @@ dependencies = [ [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "env_logger" @@ -292,9 +300,9 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" dependencies = [ "errno-dragonfly", "libc", @@ -313,12 +321,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.9.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" [[package]] name = "float-cmp" @@ -391,9 +396,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" [[package]] name = "http" @@ -435,34 +440,13 @@ dependencies = [ "xmltree", ] -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi", - "libc", - "windows-sys", -] - [[package]] name = "is-terminal" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi", - "io-lifetimes", "rustix", "windows-sys", ] @@ -478,9 +462,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "lazy_static" @@ -490,15 +474,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.146" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "linux-raw-sys" -version = "0.3.8" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" +checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" [[package]] name = "log" @@ -518,7 +502,7 @@ version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "libc", "static_assertions", @@ -553,9 +537,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", ] @@ -611,18 +595,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.60" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.28" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] @@ -663,41 +647,46 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] name = "regex" -version = "1.8.4" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" dependencies = [ "aho-corasick", "memchr", + "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" +checksum = "b7b6d6190b7594385f61bd3911cd1be99dfddcfc365a4160cc2ab5bff4aed294" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] [[package]] name = "regex-syntax" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] name = "rustix" -version = "0.37.19" +version = "0.38.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" +checksum = "1ee020b1716f0a80e2ace9b03441a749e402e86712f15f16fe8a8f75afac732f" dependencies = [ - "bitflags", + "bitflags 2.3.3", "errno", - "io-lifetimes", "libc", "linux-raw-sys", "windows-sys", @@ -705,24 +694,24 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "serde" -version = "1.0.164" +version = "1.0.181" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" +checksum = "6d3e73c93c3240c0bda063c239298e633114c69a888c3e37ca8bb33f343e9890" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.164" +version = "1.0.181" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" +checksum = "be02f6cb0cd3a5ec20bbcfbcbd749f57daddb1a0882dc2e46a6c236c90b977ed" dependencies = [ "proc-macro2", "quote", @@ -731,9 +720,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" dependencies = [ "itoa", "ryu", @@ -754,9 +743,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "2.0.18" +version = "2.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" dependencies = [ "proc-macro2", "quote", @@ -765,11 +754,10 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.6.0" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" +checksum = "5486094ee78b2e5038a6382ed7645bc084dc2ec433426ca4c3cb61e2007b8998" dependencies = [ - "autocfg", "cfg-if", "fastrand", "redox_syscall", @@ -815,9 +803,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "unicode-normalization" @@ -933,9 +921,9 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.48.0" +version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", @@ -990,9 +978,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "xml-rs" -version = "0.8.14" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52839dc911083a8ef63efa4d039d1f58b5e409f923e44c80828f206f66e5541c" +checksum = "47430998a7b5d499ccee752b41567bc3afc57e1327dc855b1a2aa44ce29b5fa1" [[package]] name = "xmltree" From 8733c6ea5605c73d086c6703d01a63e08f83bec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Gamb=C3=B6ck?= Date: Sat, 5 Aug 2023 13:53:16 +0200 Subject: [PATCH 25/26] Bump versions to final - 0.5.0 - easy-upnp-0.1.0 --- CHANGES.md | 2 +- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- VERSION | 2 +- crates/easy-upnp/Cargo.toml | 2 +- crates/easy-upnp/VERSION | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index df94657..0a78148 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,4 +1,4 @@ -# Changes in 0.5.0-RC +# Changes in 0.5.0 # Changes in 0.4.1 diff --git a/Cargo.lock b/Cargo.lock index 448216c..add25f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -270,7 +270,7 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "easy-upnp" -version = "0.1.0-RC" +version = "0.1.0" dependencies = [ "cidr-utils", "get_if_addrs", @@ -818,7 +818,7 @@ dependencies = [ [[package]] name = "upnp-daemon" -version = "0.5.0-RC" +version = "0.5.0" dependencies = [ "anyhow", "assert_cmd", diff --git a/Cargo.toml b/Cargo.toml index 9b76d4d..c101090 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "upnp-daemon" -version = "0.5.0-RC" +version = "0.5.0" description = "A daemon for continuously opening ports via UPnP." readme = "README.md" categories = ["command-line-utilities", "network-programming"] @@ -45,7 +45,7 @@ include = ["src/**/*", "LICENSE", "README.md"] # Internal dependencies -easy-upnp = { version = "0.1.0-RC", path = "crates/easy-upnp" } +easy-upnp = { version = "0.1.0", path = "crates/easy-upnp" } # Runtime dependencies diff --git a/VERSION b/VERSION index 819d957..8f0916f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.5.0-RC +0.5.0 diff --git a/crates/easy-upnp/Cargo.toml b/crates/easy-upnp/Cargo.toml index ed8b33c..25c0a37 100644 --- a/crates/easy-upnp/Cargo.toml +++ b/crates/easy-upnp/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "easy-upnp" -version = "0.1.0-RC" +version = "0.1.0" description = "Easily open and close UPnP ports." readme = "README.md" categories = ["network-programming"] diff --git a/crates/easy-upnp/VERSION b/crates/easy-upnp/VERSION index c87e310..6e8bf73 100644 --- a/crates/easy-upnp/VERSION +++ b/crates/easy-upnp/VERSION @@ -1 +1 @@ -0.1.0-RC +0.1.0 From 6b733574b95b93c3aa5196a2a110198e133353e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Gamb=C3=B6ck?= Date: Sat, 5 Aug 2023 13:56:38 +0200 Subject: [PATCH 26/26] Update Changelog --- CHANGES.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 0a78148..600424c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,16 @@ # Changes in 0.5.0 +- Split application and lib into workspaces + +- Don't use Result as parameter + + In the lib it does not make sense to do error handling from the CLI. + This should happen in the CLI module. + +- Use IntoIterator to be more flexible + +- Add some integration tests + # Changes in 0.4.1 - Update dependencies to get security fixes