Improve and future-proof the `arti` CLI
This switches out `arti`'s argument-parsing library with `clap`, which is a lot more featureful (and very widely used within the Rust ecosystem). We also now use a lot of `clap`'s features to improve the CLI experience: - The CLI now expects a subcommand (currently, either "help", or "proxy" for the existing SOCKS proxy behaviour). This should let us add additional non-SOCKS-proxy features to arti in future. - `clap` supports default values determined at runtime, so the way the default config file is loaded was changed: now, we determine the OS-specific path for said file before invoking `clap`, so the help command can show it properly. - The behaviour of `tor_config` was also changed; now, one simply specifies a list of configuration files to load, together with whether they're required. - That function also way overused generics; this has been fixed. - Instead of using the ARTI_LOG environment variable to configure logging, one now uses the `-l, --log-level` CLI option. (The intent is for this option to be more discoverable by users.) - The `proxy` subcommand allows the user to override the SOCKS port used on the CLI without editing the config file.
This commit is contained in:
parent
134c04a67a
commit
4fa0122dde
|
@ -78,7 +78,7 @@ integration:
|
||||||
- ./chutney/chutney start chutney/networks/basic
|
- ./chutney/chutney start chutney/networks/basic
|
||||||
- CHUTNEY_START_TIME=180 ./chutney/chutney wait_for_bootstrap chutney/networks/basic
|
- CHUTNEY_START_TIME=180 ./chutney/chutney wait_for_bootstrap chutney/networks/basic
|
||||||
- ./chutney/chutney verify chutney/networks/basic
|
- ./chutney/chutney verify chutney/networks/basic
|
||||||
- ./target/x86_64-unknown-linux-gnu/debug/arti -f chutney/net/nodes/arti.toml & sleep 5
|
- ./target/x86_64-unknown-linux-gnu/debug/arti proxy -c chutney/net/nodes/arti.toml & sleep 5
|
||||||
- curl http://example.com -vs --socks5-hostname 127.0.0.1:9150 -o /dev/null
|
- curl http://example.com -vs --socks5-hostname 127.0.0.1:9150 -o /dev/null
|
||||||
- kill -s INT %%; wait
|
- kill -s INT %%; wait
|
||||||
- ./chutney/chutney stop chutney/networks/basic
|
- ./chutney/chutney stop chutney/networks/basic
|
||||||
|
|
|
@ -101,7 +101,7 @@ To do so, we will launch arti independently from Tor Browser. Build arti with
|
||||||
`cargo build --release`. After that launch it with some basic
|
`cargo build --release`. After that launch it with some basic
|
||||||
configuration parameters:
|
configuration parameters:
|
||||||
|
|
||||||
$ ./target/release/arti -c "socks_port = 9150" -c "logging.trace_filter = 'debug'"
|
$ ./target/release/arti proxy -l debug -p 9150
|
||||||
|
|
||||||
This will ensure that arti sets its SOCKS port on 9150. Now we need to launch
|
This will ensure that arti sets its SOCKS port on 9150. Now we need to launch
|
||||||
Tor Browser and instruct it to use that SOCKS port.
|
Tor Browser and instruct it to use that SOCKS port.
|
||||||
|
|
|
@ -32,6 +32,15 @@ dependencies = [
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ansi_term"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ansi_term"
|
name = "ansi_term"
|
||||||
version = "0.12.1"
|
version = "0.12.1"
|
||||||
|
@ -47,35 +56,6 @@ version = "1.0.44"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1"
|
checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "argh"
|
|
||||||
version = "0.1.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f023c76cd7975f9969f8e29f0e461decbdc7f51048ce43427107a3d192f1c9bf"
|
|
||||||
dependencies = [
|
|
||||||
"argh_derive",
|
|
||||||
"argh_shared",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "argh_derive"
|
|
||||||
version = "0.1.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "48ad219abc0c06ca788aface2e3a1970587e3413ab70acd20e54b6ec524c1f8f"
|
|
||||||
dependencies = [
|
|
||||||
"argh_shared",
|
|
||||||
"heck",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "argh_shared"
|
|
||||||
version = "0.1.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "38de00daab4eac7d753e97697066238d67ce9d7e2d823ab4f72fe14af29f3f33"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrayref"
|
name = "arrayref"
|
||||||
version = "0.3.6"
|
version = "0.3.6"
|
||||||
|
@ -93,9 +73,9 @@ name = "arti"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"argh",
|
|
||||||
"arti-client",
|
"arti-client",
|
||||||
"async-ctrlc",
|
"async-ctrlc",
|
||||||
|
"clap",
|
||||||
"config",
|
"config",
|
||||||
"futures",
|
"futures",
|
||||||
"libc",
|
"libc",
|
||||||
|
@ -357,6 +337,17 @@ version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a"
|
checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atty"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi",
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "0.1.7"
|
version = "0.1.7"
|
||||||
|
@ -492,6 +483,21 @@ dependencies = [
|
||||||
"generic-array",
|
"generic-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "2.33.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
|
||||||
|
dependencies = [
|
||||||
|
"ansi_term 0.11.0",
|
||||||
|
"atty",
|
||||||
|
"bitflags",
|
||||||
|
"strsim 0.8.0",
|
||||||
|
"textwrap",
|
||||||
|
"unicode-width",
|
||||||
|
"vec_map",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "concurrent-queue"
|
name = "concurrent-queue"
|
||||||
version = "1.2.2"
|
version = "1.2.2"
|
||||||
|
@ -662,7 +668,7 @@ dependencies = [
|
||||||
"ident_case",
|
"ident_case",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"strsim",
|
"strsim 0.10.0",
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1105,15 +1111,6 @@ dependencies = [
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "heck"
|
|
||||||
version = "0.3.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
|
|
||||||
dependencies = [
|
|
||||||
"unicode-segmentation",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.1.19"
|
version = "0.1.19"
|
||||||
|
@ -2299,6 +2296,12 @@ version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
|
@ -2348,6 +2351,15 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "textwrap"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.29"
|
version = "1.0.29"
|
||||||
|
@ -2945,7 +2957,7 @@ version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5cf865b5ddc38e503a29c41c4843e616a73028ae18c637bc3eb2afaef4909c84"
|
checksum = "5cf865b5ddc38e503a29c41c4843e616a73028ae18c637bc3eb2afaef4909c84"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi_term",
|
"ansi_term 0.12.1",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"matchers",
|
"matchers",
|
||||||
"regex",
|
"regex",
|
||||||
|
@ -2991,10 +3003,10 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-segmentation"
|
name = "unicode-width"
|
||||||
version = "1.8.0"
|
version = "0.1.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
|
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-xid"
|
name = "unicode-xid"
|
||||||
|
@ -3030,6 +3042,12 @@ version = "0.2.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vec_map"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.3"
|
version = "0.9.3"
|
||||||
|
|
|
@ -68,7 +68,7 @@ download more than one directory per run.
|
||||||
To try it out, run the demo program in `arti` as follows. It will open a
|
To try it out, run the demo program in `arti` as follows. It will open a
|
||||||
SOCKS proxy on port 9150.
|
SOCKS proxy on port 9150.
|
||||||
|
|
||||||
% cargo run --release
|
% cargo run --release -- proxy
|
||||||
|
|
||||||
Again, do not use this program yet if you seriously need anonymity, privacy,
|
Again, do not use this program yet if you seriously need anonymity, privacy,
|
||||||
security, or stability.
|
security, or stability.
|
||||||
|
|
|
@ -33,7 +33,7 @@ rlimit = "0.6.2"
|
||||||
serde = { version = "1.0.124", features = ["derive"] }
|
serde = { version = "1.0.124", features = ["derive"] }
|
||||||
tracing-subscriber = { version = "0.3.0", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3.0", features = ["env-filter"] }
|
||||||
tokio-crate = { package="tokio", version = "1.7.0", optional = true, features = ["signal"] }
|
tokio-crate = { package="tokio", version = "1.7.0", optional = true, features = ["signal"] }
|
||||||
argh = "0.1.6"
|
clap = "2.33"
|
||||||
tracing-journald = { version = "0.2.0", optional = true }
|
tracing-journald = { version = "0.2.0", optional = true }
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
|
|
|
@ -17,7 +17,7 @@ journald = false
|
||||||
# It can be as simple as a single loglevel, or as complicated as a
|
# It can be as simple as a single loglevel, or as complicated as a
|
||||||
# list with per-module settings.
|
# list with per-module settings.
|
||||||
#
|
#
|
||||||
# You can override this setting with the ARTI_LOG environment variable.
|
# You can override this setting with the -l, --log-level command-line option.
|
||||||
#
|
#
|
||||||
# Example:
|
# Example:
|
||||||
# trace_filter = "info,tor_proto::channel=trace"
|
# trace_filter = "info,tor_proto::channel=trace"
|
||||||
|
|
|
@ -14,16 +14,15 @@
|
||||||
//! It will listen on port 9150 by default, but you can override this in
|
//! It will listen on port 9150 by default, but you can override this in
|
||||||
//! the configuration.
|
//! the configuration.
|
||||||
//!
|
//!
|
||||||
//! # Command-line arguments
|
//! # Command-line interface
|
||||||
//!
|
//!
|
||||||
//! (This is not stable; future versions will break this.)
|
//! (This is not stable; future versions will break this.)
|
||||||
//!
|
//!
|
||||||
//! `-f <filename>` overrides the location to search for a
|
//! `arti` uses the [`clap`](https://docs.rs/clap/) crate for command-line
|
||||||
//! configuration file to the list of configuration file. You can use
|
//! argument parsing; run `arti help` to get it to print its documentation.
|
||||||
//! this multiple times: All files will be loaded and merged.
|
|
||||||
//!
|
//!
|
||||||
//! `-c <key>=<value>` sets a configuration option to be applied after all
|
//! The only currently implemented subcommand is `arti proxy`; try
|
||||||
//! configuration files are loaded.
|
//! `arti help proxy` for a list of options you can pass to it.
|
||||||
//!
|
//!
|
||||||
//! # Configuration
|
//! # Configuration
|
||||||
//!
|
//!
|
||||||
|
@ -99,28 +98,15 @@ use tor_config::CfgPath;
|
||||||
use tor_rtcompat::{Runtime, SpawnBlocking};
|
use tor_rtcompat::{Runtime, SpawnBlocking};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use argh::FromArgs;
|
use clap::{App, AppSettings, Arg, SubCommand};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::path::PathBuf;
|
||||||
use tracing::{info, warn};
|
use tracing::{info, warn};
|
||||||
use tracing_subscriber::layer::SubscriberExt;
|
use tracing_subscriber::layer::SubscriberExt;
|
||||||
use tracing_subscriber::prelude::*;
|
use tracing_subscriber::prelude::*;
|
||||||
use tracing_subscriber::{fmt, registry, EnvFilter};
|
use tracing_subscriber::{fmt, registry, EnvFilter};
|
||||||
|
|
||||||
#[derive(FromArgs, Debug, Clone)]
|
|
||||||
/// Connect to the Tor network, open a SOCKS port, and proxy
|
|
||||||
/// traffic.
|
|
||||||
///
|
|
||||||
/// This is a demo; you get no stability guarantee.
|
|
||||||
struct Args {
|
|
||||||
/// override the default location(s) for the configuration file
|
|
||||||
#[argh(option, short = 'f')]
|
|
||||||
rc: Vec<String>,
|
|
||||||
/// override a configuration option (uses toml syntax)
|
|
||||||
#[argh(option, short = 'c')]
|
|
||||||
cfg: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Default options to use for our configuration.
|
/// Default options to use for our configuration.
|
||||||
const ARTI_DEFAULTS: &str = concat!(include_str!("./arti_defaults.toml"),);
|
const ARTI_DEFAULTS: &str = concat!(include_str!("./arti_defaults.toml"),);
|
||||||
|
|
||||||
|
@ -131,7 +117,7 @@ struct LoggingConfig {
|
||||||
/// Filtering directives that determine tracing levels as described at
|
/// Filtering directives that determine tracing levels as described at
|
||||||
/// <https://docs.rs/tracing-subscriber/0.2.20/tracing_subscriber/filter/struct.EnvFilter.html>
|
/// <https://docs.rs/tracing-subscriber/0.2.20/tracing_subscriber/filter/struct.EnvFilter.html>
|
||||||
///
|
///
|
||||||
/// You can override this setting with the environment variable ARTI_LOG.
|
/// You can override this setting with the -l, --log-level command line parameter.
|
||||||
///
|
///
|
||||||
/// Example: "info,tor_proto::channel=trace"
|
/// Example: "info,tor_proto::channel=trace"
|
||||||
trace_filter: String,
|
trace_filter: String,
|
||||||
|
@ -254,19 +240,15 @@ fn filt_from_str_verbose(s: &str, source: &str) -> EnvFilter {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set up logging
|
/// Set up logging
|
||||||
fn setup_logging(config: &ArtiConfig) {
|
fn setup_logging(config: &ArtiConfig, cli: Option<&str>) {
|
||||||
use std::env;
|
let env_filter =
|
||||||
|
match cli.map(|s| filt_from_str_verbose(s, "--log-level command line parameter")) {
|
||||||
let env_filter = match env::var("ARTI_LOG")
|
Some(f) => f,
|
||||||
.as_ref()
|
None => filt_from_str_verbose(
|
||||||
.map(|s| filt_from_str_verbose(s, "ARTI_LOG environment variable"))
|
config.logging.trace_filter.as_str(),
|
||||||
{
|
"trace_filter configuration option",
|
||||||
Ok(f) => f,
|
),
|
||||||
Err(_) => filt_from_str_verbose(
|
};
|
||||||
config.logging.trace_filter.as_str(),
|
|
||||||
"trace_filter configuration option",
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
let registry = registry().with(fmt::Layer::default()).with(env_filter);
|
let registry = registry().with(fmt::Layer::default()).with(env_filter);
|
||||||
|
|
||||||
|
@ -284,50 +266,135 @@ fn setup_logging(config: &ArtiConfig) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let args: Args = argh::from_env();
|
let dflt_config = tor_config::default_config_file().unwrap_or_else(|| "./config.toml".into());
|
||||||
let dflt_config = tor_config::default_config_file();
|
|
||||||
|
let matches =
|
||||||
|
App::new("Arti")
|
||||||
|
.version(env!("CARGO_PKG_VERSION"))
|
||||||
|
.author("The Tor Project Developers")
|
||||||
|
.about("A Rust Tor implementation.")
|
||||||
|
// HACK(eta): clap generates "arti [OPTIONS] <SUBCOMMAND>" for this usage string by
|
||||||
|
// default, but then fails to parse options properly if you do put them
|
||||||
|
// before the subcommand.
|
||||||
|
// We just declare all options as `global` and then require them to be
|
||||||
|
// put after the subcommand, hence this new usage string.
|
||||||
|
.usage("arti <SUBCOMMAND> [OPTIONS]")
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("config-files")
|
||||||
|
.short("c")
|
||||||
|
.long("config")
|
||||||
|
.takes_value(true)
|
||||||
|
.value_name("FILE")
|
||||||
|
.default_value_os(dflt_config.as_ref())
|
||||||
|
.multiple(true)
|
||||||
|
// NOTE: don't forget the `global` flag on all arguments declared at this level!
|
||||||
|
.global(true)
|
||||||
|
.help("Specify which config file(s) to read."),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("option")
|
||||||
|
.short("o")
|
||||||
|
.takes_value(true)
|
||||||
|
.value_name("KEY=VALUE")
|
||||||
|
.multiple(true)
|
||||||
|
.global(true)
|
||||||
|
.help("Override config file parameters, using TOML-like syntax."),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("loglevel")
|
||||||
|
.short("l")
|
||||||
|
.long("log-level")
|
||||||
|
.global(true)
|
||||||
|
.takes_value(true)
|
||||||
|
.value_name("LEVEL")
|
||||||
|
.help("Override the log level (usually one of 'trace', 'debug', 'info', 'warn', 'error')."),
|
||||||
|
)
|
||||||
|
.subcommand(
|
||||||
|
SubCommand::with_name("proxy")
|
||||||
|
.about(
|
||||||
|
"Run Arti in SOCKS proxy mode, proxying connections through the Tor network.",
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("socks-port")
|
||||||
|
.short("p")
|
||||||
|
.takes_value(true)
|
||||||
|
.value_name("PORT")
|
||||||
|
.help("Port to listen on for SOCKS connections (overrides the port in the config if specified).")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.setting(AppSettings::SubcommandRequiredElseHelp)
|
||||||
|
.get_matches();
|
||||||
|
|
||||||
let mut cfg = config::Config::new();
|
let mut cfg = config::Config::new();
|
||||||
cfg.merge(config::File::from_str(
|
cfg.merge(config::File::from_str(
|
||||||
ARTI_DEFAULTS,
|
ARTI_DEFAULTS,
|
||||||
config::FileFormat::Toml,
|
config::FileFormat::Toml,
|
||||||
))?;
|
))?;
|
||||||
tor_config::load(&mut cfg, &dflt_config, &args.rc, &args.cfg)?;
|
|
||||||
|
let config_files = matches
|
||||||
|
.values_of_os("config-files")
|
||||||
|
// This shouldn't actually be possible given we specify a default.
|
||||||
|
.expect("no config files provided")
|
||||||
|
.into_iter()
|
||||||
|
// The second value in this 2-tuple specifies whether the config file is "required" (as in,
|
||||||
|
// failure to load it is an error). All config files that aren't the default are required.
|
||||||
|
.map(|x| (PathBuf::from(x), x != dflt_config))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let additional_opts = matches
|
||||||
|
.values_of("option")
|
||||||
|
.map(|x| x.into_iter().map(ToOwned::to_owned).collect::<Vec<_>>())
|
||||||
|
.unwrap_or_else(Vec::new);
|
||||||
|
|
||||||
|
tor_config::load(&mut cfg, &config_files, additional_opts)?;
|
||||||
|
|
||||||
let config: ArtiConfig = cfg.try_into()?;
|
let config: ArtiConfig = cfg.try_into()?;
|
||||||
|
|
||||||
setup_logging(&config);
|
setup_logging(&config, matches.value_of("loglevel"));
|
||||||
|
|
||||||
let statecfg = config.storage.state_dir.path()?;
|
if let Some(proxy_matches) = matches.subcommand_matches("proxy") {
|
||||||
let dircfg = config.get_dir_config()?;
|
let statecfg = config.storage.state_dir.path()?;
|
||||||
let circcfg = config.get_circ_config()?;
|
let dircfg = config.get_dir_config()?;
|
||||||
let addrcfg = config.addr_config;
|
let circcfg = config.get_circ_config()?;
|
||||||
|
let addrcfg = config.addr_config;
|
||||||
|
|
||||||
let socks_port = match config.socks_port {
|
let socks_port = match (proxy_matches.value_of("socks-port"), config.socks_port) {
|
||||||
Some(s) => s,
|
(Some(p), _) => p.parse().expect("Invalid port specified"),
|
||||||
None => {
|
(None, Some(s)) => s,
|
||||||
info!("Nothing to do: no socks_port configured.");
|
(None, None) => {
|
||||||
return Ok(());
|
warn!(
|
||||||
}
|
"No SOCKS port set; specify -p PORT or use the `socks_port` configuration option."
|
||||||
};
|
);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let client_config = TorClientConfig::builder()
|
info!(
|
||||||
.state_cfg(statecfg)
|
"Starting Arti {} in SOCKS proxy mode on port {}...",
|
||||||
.dir_cfg(dircfg)
|
env!("CARGO_PKG_VERSION"),
|
||||||
.circ_cfg(circcfg)
|
socks_port
|
||||||
.addr_cfg(addrcfg)
|
);
|
||||||
.build()?;
|
|
||||||
|
|
||||||
process::use_max_file_limit();
|
let client_config = TorClientConfig::builder()
|
||||||
|
.state_cfg(statecfg)
|
||||||
|
.dir_cfg(dircfg)
|
||||||
|
.circ_cfg(circcfg)
|
||||||
|
.addr_cfg(addrcfg)
|
||||||
|
.build()?;
|
||||||
|
|
||||||
#[cfg(feature = "tokio")]
|
process::use_max_file_limit();
|
||||||
let runtime = tor_rtcompat::tokio::create_runtime()?;
|
|
||||||
#[cfg(all(feature = "async-std", not(feature = "tokio")))]
|
|
||||||
let runtime = tor_rtcompat::async_std::create_runtime()?;
|
|
||||||
|
|
||||||
let rt_copy = runtime.clone();
|
#[cfg(feature = "tokio")]
|
||||||
rt_copy.block_on(run(runtime, socks_port, client_config))?;
|
let runtime = tor_rtcompat::tokio::create_runtime()?;
|
||||||
Ok(())
|
#[cfg(all(feature = "async-std", not(feature = "tokio")))]
|
||||||
|
let runtime = tor_rtcompat::async_std::create_runtime()?;
|
||||||
|
|
||||||
|
let rt_copy = runtime.clone();
|
||||||
|
rt_copy.block_on(run(runtime, socks_port, client_config))?;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
panic!("Subcommand added to clap subcommand list, but not yet implemented")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -45,53 +45,28 @@ pub use path::CfgPath;
|
||||||
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
/// Load a Config object based on a set of files and/or command-line
|
/// Load configuration into an existing `Config` object from configuration files on disk (`files`),
|
||||||
/// arguments.
|
/// and/or command-line arguments (`opts`).
|
||||||
///
|
///
|
||||||
/// The files should be toml; the command-line arguments have the extended
|
/// `files` should be a list of TOML config file paths to read, with the boolean specifying whether
|
||||||
/// syntax of [`CmdLine`].
|
/// a failure to read this file should be an error or not.
|
||||||
///
|
///
|
||||||
/// If `default_path` is present, and there is no list of files, then use a
|
/// `opts` are passed into a [`CmdLine`], and use the extended syntax of that mechanism.
|
||||||
/// default file if it exists.
|
pub fn load<P: AsRef<Path>>(
|
||||||
//
|
|
||||||
// XXXX Add an error type for this crate.
|
|
||||||
pub fn load<'a, P1, C1, P2, C2>(
|
|
||||||
cfg: &mut config::Config,
|
cfg: &mut config::Config,
|
||||||
default_path: &Option<P1>,
|
files: &[(P, bool)],
|
||||||
files: C1,
|
opts: Vec<String>,
|
||||||
opts: C2,
|
) -> Result<(), config::ConfigError> {
|
||||||
) -> Result<(), config::ConfigError>
|
for (path, required) in files {
|
||||||
where
|
|
||||||
P1: AsRef<Path> + 'a,
|
|
||||||
C1: IntoIterator<Item = &'a P2>,
|
|
||||||
P2: AsRef<Path> + 'a,
|
|
||||||
C2: IntoIterator,
|
|
||||||
C2::Item: AsRef<str>,
|
|
||||||
{
|
|
||||||
let mut search_path = Vec::new();
|
|
||||||
for f in files {
|
|
||||||
search_path.push(f.as_ref());
|
|
||||||
}
|
|
||||||
let mut missing_ok = false;
|
|
||||||
if search_path.is_empty() {
|
|
||||||
if let Some(f) = default_path {
|
|
||||||
// XXXX shouldn't be println, but no logs exist yet.
|
|
||||||
println!("looking for default configuration in {:?}", f.as_ref());
|
|
||||||
search_path.push(f.as_ref());
|
|
||||||
missing_ok = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for p in search_path {
|
|
||||||
// Not going to use File::with_name here, since it doesn't
|
// Not going to use File::with_name here, since it doesn't
|
||||||
// quite do what we want.
|
// quite do what we want.
|
||||||
let f: config::File<_> = p.into();
|
let f: config::File<_> = path.as_ref().into();
|
||||||
cfg.merge(f.format(config::FileFormat::Toml).required(!missing_ok))?;
|
cfg.merge(f.format(config::FileFormat::Toml).required(*required))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut cmdline = CmdLine::new();
|
let mut cmdline = CmdLine::new();
|
||||||
for opt in opts {
|
for opt in opts {
|
||||||
cmdline.push_toml_line(opt.as_ref().to_string());
|
cmdline.push_toml_line(opt);
|
||||||
}
|
}
|
||||||
cfg.merge(cmdline)?;
|
cfg.merge(cmdline)?;
|
||||||
|
|
||||||
|
@ -116,16 +91,12 @@ friends = 4242
|
||||||
";
|
";
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn load_default() {
|
fn non_required_file() {
|
||||||
let td = tempdir().unwrap();
|
let td = tempdir().unwrap();
|
||||||
let dflt = td.path().join("a_file");
|
let dflt = td.path().join("a_file");
|
||||||
|
let files = vec![(dflt, false)];
|
||||||
let mut c = config::Config::new();
|
let mut c = config::Config::new();
|
||||||
let v: Vec<&'static str> = Vec::new();
|
load(&mut c, &files, Default::default()).unwrap();
|
||||||
std::fs::write(&dflt, EX_TOML).unwrap();
|
|
||||||
load(&mut c, &Some(dflt), &v, &v).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(c.get_str("hello.friends").unwrap(), "4242".to_string());
|
|
||||||
assert_eq!(c.get_str("hello.world").unwrap(), "stuff".to_string());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static EX2_TOML: &str = "
|
static EX2_TOML: &str = "
|
||||||
|
@ -134,16 +105,14 @@ world = \"nonsense\"
|
||||||
";
|
";
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn load_one_file() {
|
fn both_required_and_not() {
|
||||||
let td = tempdir().unwrap();
|
let td = tempdir().unwrap();
|
||||||
let dflt = td.path().join("a_file");
|
let dflt = td.path().join("a_file");
|
||||||
let cf = td.path().join("other_file");
|
let cf = td.path().join("other_file");
|
||||||
let mut c = config::Config::new();
|
let mut c = config::Config::new();
|
||||||
std::fs::write(&dflt, EX_TOML).unwrap();
|
|
||||||
std::fs::write(&cf, EX2_TOML).unwrap();
|
std::fs::write(&cf, EX2_TOML).unwrap();
|
||||||
let v = vec![cf];
|
let files = vec![(dflt, false), (cf, true)];
|
||||||
let v2: Vec<&'static str> = Vec::new();
|
load(&mut c, &files, Default::default()).unwrap();
|
||||||
load(&mut c, &Some(dflt), &v, &v2).unwrap();
|
|
||||||
|
|
||||||
assert!(c.get_str("hello.friends").is_err());
|
assert!(c.get_str("hello.friends").is_err());
|
||||||
assert_eq!(c.get_str("hello.world").unwrap(), "nonsense".to_string());
|
assert_eq!(c.get_str("hello.world").unwrap(), "nonsense".to_string());
|
||||||
|
@ -157,10 +126,9 @@ world = \"nonsense\"
|
||||||
let mut c = config::Config::new();
|
let mut c = config::Config::new();
|
||||||
std::fs::write(&cf1, EX_TOML).unwrap();
|
std::fs::write(&cf1, EX_TOML).unwrap();
|
||||||
std::fs::write(&cf2, EX2_TOML).unwrap();
|
std::fs::write(&cf2, EX2_TOML).unwrap();
|
||||||
let v = vec![cf1, cf2];
|
let v = vec![(cf1, true), (cf2, true)];
|
||||||
let v2 = vec!["other.var=present"];
|
let v2 = vec!["other.var=present".to_string()];
|
||||||
let d: Option<String> = None;
|
load(&mut c, &v, v2).unwrap();
|
||||||
load(&mut c, &d, &v, &v2).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(c.get_str("hello.friends").unwrap(), "4242".to_string());
|
assert_eq!(c.get_str("hello.friends").unwrap(), "4242".to_string());
|
||||||
assert_eq!(c.get_str("hello.world").unwrap(), "nonsense".to_string());
|
assert_eq!(c.get_str("hello.world").unwrap(), "nonsense".to_string());
|
||||||
|
|
Loading…
Reference in New Issue