diff options
| author | David Lönnhager <david.l@mullvad.net> | 2024-01-10 13:06:03 +0100 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2024-01-10 13:06:03 +0100 |
| commit | 75eb89c820f12d488a76934f59ba29fe999cf59c (patch) | |
| tree | dee82ab3f307f8d1a72a0cf01fceb8fa51612ac1 /mullvad-cli/src | |
| parent | edbd1f52c44ba6ee9a290146f714453fc87e689d (diff) | |
| parent | 01bcf9ed16a903aa80ff8bc48cc6f2aaf3d6e80d (diff) | |
| download | mullvadvpn-75eb89c820f12d488a76934f59ba29fe999cf59c.tar.xz mullvadvpn-75eb89c820f12d488a76934f59ba29fe999cf59c.zip | |
Merge branch 'add-settings-json-export'
Diffstat (limited to 'mullvad-cli/src')
| -rw-r--r-- | mullvad-cli/src/cmds/import_settings.rs | 101 | ||||
| -rw-r--r-- | mullvad-cli/src/cmds/mod.rs | 2 | ||||
| -rw-r--r-- | mullvad-cli/src/cmds/patch.rs | 60 | ||||
| -rw-r--r-- | mullvad-cli/src/main.rs | 13 |
4 files changed, 72 insertions, 104 deletions
diff --git a/mullvad-cli/src/cmds/import_settings.rs b/mullvad-cli/src/cmds/import_settings.rs deleted file mode 100644 index a89c6814c3..0000000000 --- a/mullvad-cli/src/cmds/import_settings.rs +++ /dev/null @@ -1,101 +0,0 @@ -use anyhow::{anyhow, Context, Result}; -use mullvad_management_interface::MullvadProxyClient; -use std::{ - fs::File, - io::{stdin, BufRead, BufReader}, - path::Path, -}; - -/// Maximum size of a settings patch. Bigger files/streams cause the read to fail. -const MAX_PATCH_BYTES: usize = 10 * 1024; - -/// If source is specified, read from the provided file and send it as a settings patch to the -/// daemon. Otherwise, read the patch from standard input. -pub async fn handle(source: String) -> Result<()> { - let json_blob = tokio::task::spawn_blocking(|| get_blob(source)) - .await - .unwrap()?; - - let mut rpc = MullvadProxyClient::new().await?; - rpc.apply_json_settings(json_blob) - .await - .context("Error applying patch")?; - - println!("Settings applied"); - - Ok(()) -} - -fn get_blob(source: String) -> Result<String> { - match source.as_str() { - "-" => read_settings_from_stdin().context("Failed to read from stdin"), - _ => read_settings_from_file(source).context("Failed to read from path: {source}"), - } -} - -/// Read settings from standard input -fn read_settings_from_stdin() -> Result<String> { - read_settings_from_reader(BufReader::new(stdin())) -} - -/// Read settings from a path -fn read_settings_from_file(path: impl AsRef<Path>) -> Result<String> { - read_settings_from_reader(BufReader::new(File::open(path)?)) -} - -/// Read until EOF or until newline when the last pair of braces has been closed -fn read_settings_from_reader(mut reader: impl BufRead) -> Result<String> { - let mut buf = [0u8; MAX_PATCH_BYTES]; - - let mut was_open = false; - let mut close_after_newline = false; - let mut brace_count: usize = 0; - let mut cursor_pos = 0; - - loop { - let Some(cursor) = buf.get_mut(cursor_pos..) else { - return Err(anyhow!( - "Patch too long: maximum length is {MAX_PATCH_BYTES} bytes" - )); - }; - - let prev_cursor_pos = cursor_pos; - let read_n = reader.read(cursor)?; - if read_n == 0 { - // EOF - break; - } - cursor_pos += read_n; - - let additional_bytes = &buf[prev_cursor_pos..cursor_pos]; - - if !close_after_newline { - for next in additional_bytes { - match next { - b'{' => brace_count += 1, - b'}' => { - brace_count = brace_count.checked_sub(1).with_context(|| { - // exit: too many closing braces - "syntax error: unexpected '}'" - })? - } - _ => (), - } - was_open |= brace_count > 0; - } - if brace_count == 0 && was_open { - // complete settings - close_after_newline = true; - } - } - - if close_after_newline && additional_bytes.contains(&b'\n') { - // done - break; - } - } - - Ok(std::str::from_utf8(&buf[0..cursor_pos]) - .context("settings must be utf8 encoded")? - .to_owned()) -} diff --git a/mullvad-cli/src/cmds/mod.rs b/mullvad-cli/src/cmds/mod.rs index 29e0508d80..5a9cede691 100644 --- a/mullvad-cli/src/cmds/mod.rs +++ b/mullvad-cli/src/cmds/mod.rs @@ -9,10 +9,10 @@ pub mod bridge; pub mod custom_list; pub mod debug; pub mod dns; -pub mod import_settings; pub mod lan; pub mod lockdown; pub mod obfuscation; +pub mod patch; pub mod proxies; pub mod relay; pub mod relay_constraints; diff --git a/mullvad-cli/src/cmds/patch.rs b/mullvad-cli/src/cmds/patch.rs new file mode 100644 index 0000000000..bec686e56d --- /dev/null +++ b/mullvad-cli/src/cmds/patch.rs @@ -0,0 +1,60 @@ +use anyhow::{Context, Result}; +use mullvad_management_interface::MullvadProxyClient; +use std::{ + fs::File, + io::{stdin, BufReader, Read}, +}; + +/// If source is specified, read from the provided file and send it as a settings patch to the +/// daemon. Otherwise, read the patch from standard input. +pub async fn import(source: String) -> Result<()> { + let json_blob = tokio::task::spawn_blocking(|| get_blob(source)) + .await + .unwrap()?; + + let mut rpc = MullvadProxyClient::new().await?; + rpc.apply_json_settings(json_blob) + .await + .context("Error applying patch")?; + + println!("Settings applied"); + + Ok(()) +} + +/// If source is specified, write a patch to the file. Otherwise, write the patch to standard +/// output. +pub async fn export(dest: String) -> Result<()> { + let mut rpc = MullvadProxyClient::new().await?; + let blob = rpc + .export_json_settings() + .await + .context("Error exporting patch")?; + + match dest.as_str() { + "-" => { + println!("{blob}"); + Ok(()) + } + _ => tokio::fs::write(&dest, blob) + .await + .context(format!("Failed to write to path {dest}")), + } +} + +fn get_blob(source: String) -> Result<String> { + match source.as_str() { + "-" => { + read_settings_from_reader(BufReader::new(stdin())).context("Failed to read from stdin") + } + _ => read_settings_from_reader(File::open(&source)?) + .context(format!("Failed to read from path: {source}")), + } +} + +/// Read until EOF or until newline when the last pair of braces has been closed +fn read_settings_from_reader(mut reader: impl Read) -> Result<String> { + let mut s = String::new(); + reader.read_to_string(&mut s)?; + Ok(s) +} diff --git a/mullvad-cli/src/main.rs b/mullvad-cli/src/main.rs index 270d3c293e..c0a8a8b992 100644 --- a/mullvad-cli/src/main.rs +++ b/mullvad-cli/src/main.rs @@ -138,11 +138,19 @@ enum Cli { #[clap(subcommand)] CustomList(custom_list::CustomList), - /// Apply a JSON patch + /// Apply a JSON patch generated by 'export-settings' + #[clap(arg_required_else_help = true)] ImportSettings { /// File to read from. If this is "-", read from standard input file: String, }, + + /// Export a JSON patch based on the current settings + #[clap(arg_required_else_help = true)] + ExportSettings { + /// File to write to. If this is "-", write to standard output + file: String, + }, } #[tokio::main] @@ -169,7 +177,8 @@ async fn main() -> Result<()> { Cli::SplitTunnel(cmd) => cmd.handle().await, Cli::Status { cmd, args } => status::handle(cmd, args).await, Cli::CustomList(cmd) => cmd.handle().await, - Cli::ImportSettings { file } => import_settings::handle(file).await, + Cli::ImportSettings { file } => patch::import(file).await, + Cli::ExportSettings { file } => patch::export(file).await, #[cfg(all(unix, not(target_os = "android")))] Cli::ShellCompletions { shell, dir } => { |
