├── remove_logs.sh ├── setsuid.sh ├── .gitignore ├── initial.sh ├── .vscode └── launch.json ├── src ├── bin │ ├── netnsp-sub.rs │ └── netnsp-main.rs ├── util.rs ├── nft.rs ├── lib.rs ├── watcher.rs └── configurer.rs ├── Cargo.toml ├── readme.md ├── LICENSE └── Cargo.lock /remove_logs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | rm ./*.log -------------------------------------------------------------------------------- /setsuid.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | sudo chown 0:0 ./target/debug/netnsp-main 4 | sudo chmod +s ./target/debug/netnsp-main 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | *.log 3 | secret.json 4 | netnsp.json 5 | core 6 | local 7 | .cargo 8 | .vscode/settings.json 9 | .vscode/launch.json 10 | -------------------------------------------------------------------------------- /initial.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | setcap 'cap_net_bind_service=ep' $(which dnsproxy) 3 | setcap -v 'cap_net_bind_service=ep' $(which dnsproxy) # -v does not change caps. 4 | setcap 'cap_net_bind_service=ep' $(which tun2socks) 5 | setcap -v 'cap_net_bind_service=ep' $(which tun2socks) -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "gdb", 9 | "request": "attach", 10 | "name": "Attach to gdbserver", 11 | "executable": "${workspaceRoot}/taget/debug/netnsp-main", 12 | "target": "10.212.113.95:2345", 13 | "remote": true, 14 | "cwd": "${workspaceRoot}", 15 | "valuesFormatting": "parseText" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /src/bin/netnsp-sub.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use netns_proxy::configurer::{self}; 3 | 4 | // this will run inside netns 5 | 6 | #[tokio::main] 7 | async fn main() -> Result<()> { 8 | let mut args = std::env::args(); 9 | args.next(); 10 | let arglen = args.len(); 11 | if arglen >= 4 { 12 | if let Err(err) = netns_proxy::inner_daemon( 13 | args.next(), 14 | args.next(), 15 | args.next(), 16 | args.next(), 17 | args.next(), 18 | ) 19 | .await 20 | { 21 | return Err(err); 22 | }; 23 | } else { 24 | if arglen == 2 { 25 | configurer::config_in_ns(args.next().unwrap().parse()?, args.next().unwrap()).await?; 26 | } else { 27 | std::process::exit(1); 28 | } 29 | } 30 | Ok(()) 31 | } 32 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "netns-proxy" 3 | version = "0.1.0" 4 | edition = "2021" 5 | default-run = "netnsp-main" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | clap = { version = "4.2.4", features = ["derive"] } 11 | tokio = { version = "1.27.0", features = ["full"] } 12 | sysinfo = "0.28.4" 13 | flexi_logger = "0.25" 14 | log = "0.4.17" 15 | futures = "0.3.28" 16 | ipnetwork = "0.20.0" 17 | users = "0.11.0" 18 | nix = { version = "0.26.2", features = ["user", "sched"] } 19 | serde = { version = "1.0.160", features = ["derive"] } 20 | serde_json = "1.0.96" 21 | anyhow = { version = "1.0.71", features = ["backtrace"] } 22 | libc = "0.2.143" 23 | procfs = "0.15.1" 24 | rtnetlink = "0.12.0" 25 | netlink-packet-route = "0.15.0" 26 | netns-rs = "0.1.0" 27 | tidy-tuntap = { git = "https://github.com/planetoryd/tidy-tuntap", branch = "main" } 28 | mnl = "0.2.2" 29 | zbus = { version = "3.13.1", features = ["tokio"] } 30 | inotify = "0.10.1" 31 | rust-ini = "0.19.0" 32 | rustables = { branch = "netnsp", git = "https://github.com/planetoryd/rustables" } 33 | either = "1.8.1" 34 | pidfd = "0.2.4" 35 | xdg = "2.5.0" 36 | 37 | [profile.release] 38 | strip = true 39 | opt-level = "z" # bottleneck is size, not cpu 40 | lto = true 41 | 42 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Use https://github.com/planetoryd/nsproxy instead 2 | 3 | # Netns-based selective proxying, for identity isolation 4 | 5 | > *identity isolation* because environ-variable based proxying is not safe for that. 6 | 7 | - set your firewall to allow `10.27.0.0/16` 8 | - install the following, make them available in $PATH 9 | - [tun2socks](https://github.com/xjasonlyu/tun2socks) 10 | - [dnsproxy](https://github.com/AdguardTeam/dnsproxy) 11 | - for building 12 | - `libnftnl` 13 | - `libmnl` 14 | 15 | ## features 16 | 17 | 1. Configure a few persistent Network Namespaces 18 | 2. Watch for new flatpak processes and configure Network Namespaces for them 19 | 20 | Network namespace is more secure than netfilter-only approaches. When netns-proxy stops/crashes, or before it configures your app, the internet is disconnected in the netns. 21 | 22 | The default profile (like the `"base_p": {}` below) configures the associated NetNSes to be proxied by a socks5 proxy listening on `host_ip:9909`. Typically you can set your proxy to listen on `0.0.0.0:9909`, and secure it with a firewall. 23 | 24 | **Notice**: You need set flatpak applications to have `Network` *disabled*, in Flatseal, in order to use this tool. Netns-proxy would try to disable it. 25 | 26 | ## usage 27 | 28 | start it under a working directory with `secret.json` and `netnsp.json` (optionally) present. 29 | 30 | ```json 31 | { 32 | "params": { 33 | "base_p": {}, 34 | "proxy-a": { 35 | "cmd": { 36 | "program": "gost", 37 | "argv": [ 38 | "-L=socks5://localhost:1080", 39 | "-F=socks5://$ip_vh:9909", 40 | "-F=socks5://user:pass@ip:port" 41 | ] 42 | }, 43 | "chain": true 44 | } 45 | }, 46 | "flatpak": { 47 | "io.github.NhekoReborn.Nheko": "base_p" 48 | } 49 | } 50 | ``` 51 | 52 | example `secret.json`. 53 | 54 | 1. It configures two profiles, and they will be instantiated as persistent NetNSes if you run `netnsp-main --pre`. 55 | 2. It matches flatpak process with app ID as they start, which you can see by `flatpak list` or `flatpak ps`, and applies the profiles. 56 | 57 | ```bash 58 | cargo b 59 | ./initial.sh # set capabilities 60 | ./setsuid.sh # run this every build 61 | netnsp-main # starts the flatpak watcher, only 62 | netnsp-main --pre # configures the persistent namespaces, and starts the flatpak watcher 63 | netnsp-main exec --ns base_p # enter a shell in netns. 64 | netnsp-main exec --ns base_p --cmd bash # specify the command to execute 65 | ``` 66 | 67 | with `netns-main exec --ns target_ns` it can start a process with everything unchanged but netns. 68 | `sudo` with `ip netns exec` would mess up a lot of things. 69 | 70 | - use [opensnitch firewall](https://github.com/evilsocket/opensnitch) as the second layer of defense, in case you do anything wrong, like launching an app outside netns. 71 | 72 | ## use with mullvad-browser 73 | 74 | 0. enter netns with `netnsp-main exec --ns base_p` 75 | 1. run `./start-mullvad-browser.desktop -p` and create your profiles, name them, `i2p` and `base_p` 76 | 2. use `./start-mullvad-browser.desktop -p base_p` next time 77 | 78 | using the tarball of mullvadbrowser seems better than other packagings, for now. 79 | 80 | ## random 81 | 82 | you probably need application state isolation, for different identities. 83 | 84 | so that, for example, IPFS does not use the same peerID with and without VPN. that achieves anonymity, even though IPFS has no anonymity whatever. 85 | 86 | It's possible to have network namespaces recursively, but directly running this script would run into file name conflicts. You need some kind of filesystem sandbox, or modify the script to use a different directory. 87 | 88 | - https://github.com/nixpak/nixpak 89 | - https://sr.ht/~fgaz/nix-bubblewrap/ 90 | -------------------------------------------------------------------------------- /src/util.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::path::PathBuf; 3 | 4 | use anyhow::Ok; 5 | use anyhow::Result; 6 | use nix::sys::signal::kill; 7 | use nix::sys::signal::Signal; 8 | use nix::unistd::Pid; 9 | use procfs::process::Process; 10 | 11 | pub fn convert_strings_to_strs(strings: &Vec) -> Vec<&str> { 12 | strings.iter().map(|s| s.as_str()).collect() 13 | } 14 | 15 | pub fn get_non_priv_user(uid: Option, gid: Option) -> Result<(u32, u32)> { 16 | let r_ui: u32; 17 | let r_gi: u32; 18 | if let core::result::Result::Ok(log_name) = env::var("SUDO_USER") { 19 | // run from sudo 20 | let log_user = users::get_user_by_name(&log_name).unwrap(); 21 | r_ui = log_user.uid(); 22 | r_gi = log_user.primary_group_id(); 23 | } else if uid.is_some() && gid.is_some() { 24 | // supplied 25 | r_gi = gid.unwrap().parse()?; 26 | r_ui = uid.unwrap().parse()?; 27 | } else { 28 | // as child process of some non-root 29 | let parent_pid = nix::unistd::getppid(); 30 | let parent_process = match procfs::process::Process::new(parent_pid.into()) { 31 | core::result::Result::Ok(process) => process, 32 | Err(_) => panic!("cannot access parent process"), 33 | }; 34 | r_ui = parent_process.status()?.euid; 35 | r_gi = parent_process.status()?.egid; 36 | } 37 | 38 | Ok((r_ui, r_gi)) 39 | } 40 | 41 | #[test] 42 | fn t_pidfd() -> Result<()> { 43 | use pidfd::PidFuture; 44 | tokio::runtime::Builder::new_current_thread() 45 | .enable_all() 46 | .build() 47 | .unwrap() 48 | .block_on(async { 49 | let f = unsafe { pidfd::PidFd::open(853481, 0)?.into_future() }; 50 | println!("opened"); 51 | f.await; 52 | println!("finished"); 53 | Ok(()) 54 | }) 55 | } 56 | 57 | #[test] 58 | fn get_all_child_pids() -> Result<()> { 59 | use procfs::process::Process; 60 | 61 | let su = Process::new(942129)?; 62 | let mt = su.task_main_thread()?; 63 | dbg!(mt.children()?); 64 | let chi: Vec = su 65 | .tasks()? 66 | .filter_map(|t| t.ok().and_then(|x| x.children().ok())) 67 | .flatten() 68 | .collect(); 69 | 70 | dbg!(chi); 71 | Ok(()) 72 | } 73 | 74 | pub fn kill_children(pid: i32) -> Result<()> { 75 | let su = Process::new(pid)?; 76 | let chi: Vec = su 77 | .tasks()? 78 | .filter_map(|t| t.ok().and_then(|x| x.children().ok())) 79 | .flatten() 80 | .collect(); 81 | 82 | for c in chi { 83 | kill(Pid::from_raw(c.try_into()?), Signal::SIGTERM).map_err(anyhow::Error::from)?; 84 | } 85 | 86 | Ok(()) 87 | } 88 | 89 | #[cfg(not(test))] 90 | use log::{info, warn}; 91 | 92 | #[cfg(test)] 93 | use std::{println as info, println as warn}; 94 | 95 | pub fn flatpak_perms_checkup(list: Vec) -> Result<()> { 96 | let basedirs = xdg::BaseDirectories::with_prefix("flatpak")?; 97 | info!("trying to adapt flatpak app permissions"); 98 | for appid in list { 99 | let mut sub = PathBuf::from("overrides"); 100 | sub.push(appid); 101 | let p = basedirs.get_data_file(&sub); 102 | if p.exists() { 103 | let mut conf = ini::Ini::load_from_file(p.as_path())?; 104 | let k = conf.get_from(Some("Context"), "shared"); 105 | if k.is_some() { 106 | if k.unwrap().contains("!network") { 107 | info!("{} found. it has correct config", p.to_string_lossy()); 108 | } else { 109 | let o = k.unwrap().to_owned(); 110 | let v = o + ";!network"; 111 | conf.set_to(Some("Context"), "shared".to_owned(), v); 112 | conf.write_to_file(p.as_path())?; 113 | info!("{} written", p.to_string_lossy()); 114 | } 115 | } else { 116 | conf.set_to(Some("Context"), "shared".to_owned(), "!network".to_owned()); 117 | conf.write_to_file(p.as_path())?; 118 | info!("{} written", p.to_string_lossy()); 119 | } 120 | } else { 121 | // create a new file for it 122 | let mut conf = ini::Ini::new(); 123 | conf.set_to(Some("Context"), "shared".to_owned(), "!network".to_owned()); 124 | info!("{} written. new file", p.to_string_lossy()); 125 | conf.write_to_file(p.as_path())?; 126 | } 127 | } 128 | Ok(()) 129 | } 130 | 131 | #[test] 132 | fn test_flatpakperm() { 133 | flatpak_perms_checkup( 134 | [ 135 | "org.mozilla.firefox".to_owned(), 136 | "im.fluffychat.Fluffychat".to_owned(), 137 | ] 138 | .to_vec(), 139 | ) 140 | .unwrap(); 141 | } 142 | -------------------------------------------------------------------------------- /src/nft.rs: -------------------------------------------------------------------------------- 1 | use rustables::{ 2 | expr::{Cmp, CmpOp, Immediate, Meta, MetaType, VerdictKind}, 3 | iface_index, list_chains_for_table, list_rules_for_chain, list_tables, Batch, Chain, Hook, 4 | MsgType, ProtocolFamily, Rule, Table, 5 | }; 6 | use std::collections::{HashMap, HashSet}; 7 | 8 | use anyhow::{Ok, Result}; 9 | 10 | pub const TABLE_NAME: &str = "netnsp"; 11 | pub const FO_CHAIN: &str = "block-forward"; 12 | 13 | #[derive(Default, Debug)] 14 | pub struct NftProposal { 15 | // name -> table. indexed 16 | tables: HashMap, 17 | } 18 | 19 | #[derive(Default, Debug)] 20 | pub struct PTable { 21 | table: Table, 22 | chains: HashMap, 23 | } 24 | 25 | #[derive(Default, Debug)] 26 | pub struct PChain { 27 | chain: Chain, 28 | rules: HashSet, 29 | // I think they are not ordered 30 | } 31 | 32 | // it's actually quite awkward to spend so much effort synchronizing the nft state and local state. 33 | 34 | // ensure those rules exist 35 | // if not, incrementally update 36 | pub fn ensure_rules(proposal: NftProposal) -> Result<()> { 37 | let ts: Vec = list_tables()?; 38 | let e_set: HashSet<&String> = ts.iter().flat_map(|table| table.get_name()).collect(); 39 | // do a quick scan for consistency, and if diff is small, update incrementally, else remove & re-add 40 | let mut batch: Batch = Batch::new(); 41 | for (name, ta) in proposal.tables { 42 | if e_set.contains(&name) { 43 | // the comparison isn't that strict / careful. mostly against misconfig, not adversaries 44 | let chains = list_chains_for_table(&ta.table)?; 45 | let c_set: HashSet<&String> = 46 | chains.iter().flat_map(|table| table.get_name()).collect(); 47 | for (c_name, ca) in ta.chains { 48 | if c_set.contains(&c_name) { 49 | let mut rules: Vec = list_rules_for_chain(&ca.chain)?; 50 | for r in rules.iter_mut() { 51 | r.essentialize(); 52 | } 53 | let exi_set: HashSet<&Rule> = HashSet::from_iter(rules.iter()); 54 | let expec_set: HashSet<&Rule> = HashSet::from_iter(ca.rules.iter()); 55 | if exi_set.is_subset(&expec_set) { 56 | // add all the missing rules 57 | let add_diff = &expec_set - &exi_set; 58 | for expr in add_diff.iter() { 59 | batch.add(*expr, MsgType::Add); 60 | } 61 | log::trace!( 62 | "incrementally adding {} new rules to chain {}", 63 | add_diff.len(), 64 | c_name 65 | ) 66 | } else { 67 | // remove and re-add 68 | // we take full-control of a chain 69 | log::trace!("chain {} contaminated, re-adding", c_name); 70 | for rule in rules { 71 | batch.add(&rule, MsgType::Del); 72 | } 73 | for rule in &ca.rules { 74 | batch.add(rule, MsgType::Add); 75 | } 76 | } 77 | } else { 78 | // add chain 79 | log::trace!("adding new chain {}", c_name); 80 | batch.add(&ca.chain, MsgType::Add); 81 | for rule in &ca.rules { 82 | batch.add(rule, MsgType::Add); 83 | } 84 | } 85 | } 86 | // do nothing to other chains if they exist 87 | } else { 88 | log::trace!("adding new table {}", name); 89 | batch.add(&ta.table, MsgType::Add); 90 | for (_c_name, ca) in ta.chains.iter() { 91 | batch.add(&ca.chain, MsgType::Add); 92 | for rule in &ca.rules { 93 | batch.add(rule, MsgType::Add); 94 | } 95 | } 96 | // add table 97 | } 98 | } 99 | // do nothing to other tables if any 100 | log::trace!("ensure_rules, batch.send"); 101 | batch.send()?; 102 | 103 | Ok(()) 104 | } 105 | 106 | pub fn apply_block_forwad(veth_list: &[&str]) -> Result<()> { 107 | log::info!("applying nft rules to block forwarding of {:?}", veth_list); 108 | let table = Table::new(ProtocolFamily::Inet).with_name(TABLE_NAME.to_owned()); 109 | let chain = Chain::new(&table) 110 | .with_name(FO_CHAIN) 111 | .with_hook(Hook::new(rustables::HookClass::Forward, 0)) 112 | .with_policy(rustables::ChainPolicy::Accept); 113 | let mut rules: HashSet = HashSet::new(); 114 | 115 | for v in veth_list { 116 | rules.insert(drop_interface_rule(v, &chain)?); 117 | } 118 | 119 | let prop = NftProposal { 120 | tables: HashMap::from_iter([( 121 | TABLE_NAME.to_owned(), 122 | PTable { 123 | table, 124 | chains: HashMap::from([(FO_CHAIN.to_owned(), PChain { chain, rules })]), 125 | }, 126 | )]), 127 | }; 128 | 129 | ensure_rules(prop)?; 130 | 131 | Ok(()) 132 | } 133 | 134 | pub fn drop_interface_rule(i_name: &str, chain: &Chain) -> Result { 135 | let mut r = Rule::new(&chain)?; 136 | let i = iface_index(i_name)?; 137 | 138 | r = r 139 | .with_expr(Meta::new(MetaType::Iif)) 140 | .with_expr(Cmp::new(CmpOp::Eq, i.to_le_bytes())) 141 | .with_expr(Immediate::new_verdict(VerdictKind::Drop)); 142 | 143 | Ok(r) 144 | } 145 | -------------------------------------------------------------------------------- /src/bin/netnsp-main.rs: -------------------------------------------------------------------------------- 1 | #![feature(setgroups)] 2 | use anyhow::{anyhow, Result}; 3 | use clap::{Parser, Subcommand}; 4 | 5 | use netns_proxy::util::get_non_priv_user; 6 | 7 | use std::{env, path::PathBuf}; 8 | use tokio::{fs::File, io::AsyncReadExt, process::Command, task::JoinSet}; 9 | 10 | use netns_proxy::configurer::*; 11 | 12 | use procfs::process::Process; 13 | 14 | #[derive(Parser)] 15 | #[command( 16 | author, 17 | version, 18 | about = "utility for using netns as socks proxy containers.", 19 | long_about = "utility for using netns as socks proxy containers. may need root. \n example commandline `RUST_LOG=error,netns_proxy=info sudo -E netns-proxy`, errors may be fine" 20 | )] 21 | struct Cli { 22 | #[command(subcommand)] 23 | command: Option, 24 | /// configure each profile as a persistent net namespace. pre means before using apps. 25 | #[arg(short, long)] 26 | pre: bool, 27 | } 28 | 29 | #[derive(Subcommand)] 30 | enum Commands { 31 | /// stops all suspected netns-proxy processes/daemons 32 | Stop {}, 33 | /// exec in the netns, with EVERYTHING else untampered. requires SUID 34 | Exec { 35 | #[arg(short, long)] 36 | ns: String, 37 | #[arg(short, long)] 38 | cmd: Option, 39 | #[arg(short, long)] 40 | pid: Option, 41 | }, 42 | Id {}, 43 | } 44 | 45 | #[tokio::main] 46 | async fn main() -> Result<()> { 47 | let mut set = JoinSet::new(); 48 | 49 | // this is very safe 50 | // unsafe { 51 | // netns_proxy::logger = Some( 52 | // flexi_logger::Logger::try_with_env_or_str("error,netnsp_main=debug,netns_proxy=debug") 53 | // .unwrap() 54 | // .log_to_file(FileSpec::default()) 55 | // .duplicate_to_stdout(flexi_logger::Duplicate::All) 56 | // .start() 57 | // .unwrap(), 58 | // ); 59 | // } 60 | #[cfg(debug_assertions)] 61 | unsafe { 62 | netns_proxy::logger = Some( 63 | flexi_logger::Logger::try_with_env_or_str( 64 | "error,netnsp_main=trace,netns_proxy=trace,netnsp_sub=trace", 65 | ) 66 | .unwrap() 67 | .log_to_stdout() 68 | .start() 69 | .unwrap(), 70 | ); 71 | } 72 | #[cfg(not(debug_assertions))] 73 | unsafe { 74 | netns_proxy::logger = Some( 75 | flexi_logger::Logger::try_with_env_or_str( 76 | "error,netnsp_main=info,netns_proxy=debug,netnsp_sub=info", 77 | ) 78 | .unwrap() 79 | .log_to_stdout() 80 | .start() 81 | .unwrap(), 82 | ); 83 | } 84 | let cli = Cli::parse(); 85 | 86 | tokio::spawn(async { 87 | tokio::signal::ctrl_c().await.unwrap(); 88 | log::warn!("Received Ctrl+C"); 89 | std::process::exit(0); 90 | // let i = nix::unistd::getpgrp(); 91 | // TODO: kill all sub processes in case of crash 92 | }); 93 | 94 | match cli.command { 95 | Some(Commands::Stop {}) => { 96 | netns_proxy::kill_suspected(); 97 | } 98 | Some(Commands::Id {}) => { 99 | let got_ns = self_netns_identify() 100 | .await? 101 | .ok_or_else(|| anyhow!("no matches under the given netns directory"))?; 102 | println!("{:?}", got_ns); 103 | } 104 | Some(Commands::Exec { mut cmd, ns, pid }) => { 105 | let path = "./netnsp.json"; 106 | let mut file = File::open(path).await?; 107 | let mut contents = String::new(); 108 | file.read_to_string(&mut contents).await?; 109 | 110 | let config: ConfigRes = serde_json::from_str(&contents)?; 111 | let curr_inode = get_self_netns_inode()?; 112 | let self_pid = nix::unistd::getpid(); 113 | 114 | let parent_pid = nix::unistd::getppid(); 115 | let parent_process = match Process::new(parent_pid.into()) { 116 | Ok(process) => process, 117 | Err(_) => panic!("cannot access parent process"), 118 | }; 119 | let puid = parent_process.status()?.euid; 120 | let pgid = parent_process.status()?.egid; 121 | 122 | if curr_inode != config.root_inode { 123 | log::error!("ACCESS DENIED"); 124 | std::process::exit(1); 125 | } 126 | 127 | if cmd.is_none() { 128 | cmd = Some("fish".to_owned()); 129 | } 130 | log::info!( 131 | "uid: {:?}, gid: {:?}, pid: {:?}", 132 | nix::unistd::getresuid()?, 133 | nix::unistd::getresgid()?, 134 | self_pid 135 | ); 136 | if let Some(pi) = pid { 137 | enter_ns_by_pid(pi)?; 138 | } else { 139 | enter_ns_by_name(&ns).await?; 140 | } 141 | 142 | drop_privs1( 143 | nix::unistd::Gid::from_raw(pgid), 144 | nix::unistd::Uid::from_raw(puid), 145 | )?; 146 | 147 | let proc = std::process::Command::new(cmd.unwrap()); 148 | 149 | // proc.args(&[""]); 150 | 151 | let mut cmd_async: Command = proc.into(); 152 | let mut t = cmd_async.spawn()?; 153 | t.wait().await?; 154 | } 155 | None => { 156 | // Run as a daemon 157 | use netns_proxy::util; 158 | use netns_proxy::watcher; 159 | let mut state = NetnspState::load(Default::default()).await?; 160 | 161 | let configrer = Configurer::new(); 162 | 163 | if cli.pre { 164 | match config_network(&configrer, &mut state).await { 165 | Ok(_) => { 166 | state.dump().await?; 167 | 168 | let mut sp = env::current_exe()?; 169 | sp.pop(); 170 | sp.push("netnsp-sub"); 171 | let sp1 = sp.into_os_string(); 172 | // start daemons 173 | let (puid, pgid) = get_non_priv_user(None, None)?; 174 | for name in state.profile_names() { 175 | let spx = sp1.clone(); 176 | 177 | set.spawn(async move { 178 | let spx = spx.clone(); 179 | let mut path = PathBuf::from(NETNS_PATH); 180 | path.push(name.clone()); 181 | 182 | use nix::fcntl::{open, OFlag}; 183 | use nix::sys::stat::Mode; 184 | let fd = open(&path, OFlag::O_RDONLY, Mode::empty()).unwrap(); 185 | 186 | log::info!("wait on {:?}, ns {}", spx, &name); 187 | 188 | let mut cmd = Command::new(spx.clone()) 189 | .arg(name.clone()) 190 | .arg(puid.to_string()) 191 | .arg(pgid.to_string()) 192 | .arg(fd.to_string()) 193 | .uid(0) // run it as root 194 | .spawn() 195 | .unwrap(); 196 | let task = cmd.wait(); 197 | let res = task.await; 198 | 199 | (name, res) 200 | }); 201 | } 202 | } 203 | Err(x) => { 204 | log::error!("There is irrecoverable error in configuring. Try reseting the state, like rebooting"); 205 | let euid = nix::unistd::geteuid(); 206 | if !euid.is_root() { 207 | // the only way of checking if we have the perms is to try. so no mandating root. 208 | log::warn!("Your uid is {euid}. Do you have enough perms") 209 | } 210 | return Err(x); 211 | } 212 | } 213 | } 214 | 215 | let hold = state.profile_names(); 216 | let watcher = watcher::WatcherState::create(configrer, state).await?; 217 | let w_t = watcher.start(); 218 | 219 | let persis_ns = async move { 220 | let ns_names1 = util::convert_strings_to_strs(&hold); 221 | while let Some(res) = set.join_next().await { 222 | let idx = res.unwrap(); 223 | log::error!( 224 | "{} exited, with {:?}. re-cap, {}/{} running", 225 | idx.0, 226 | idx.1, 227 | set.len(), 228 | ns_names1.len() 229 | ); 230 | } 231 | }; 232 | // exit when either of them exits, because it shouldn't. 233 | tokio::select! { 234 | _ = tokio::spawn(w_t) => {} 235 | _ = tokio::spawn(persis_ns) => {} 236 | } 237 | } 238 | } 239 | 240 | Ok(()) 241 | } 242 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(ip)] 2 | #![feature(async_closure)] 3 | #![feature(exit_status_error)] 4 | #![feature(setgroups)] 5 | #![feature(get_mut_unchecked)] 6 | 7 | use futures::TryFutureExt; 8 | use ipnetwork::IpNetwork; 9 | 10 | use anyhow::{Ok, Result}; 11 | 12 | use nix::{ 13 | libc::{kill, SIGTERM}, 14 | unistd::{Gid, Uid}, 15 | }; 16 | 17 | use std::collections::HashMap; 18 | use std::{os::unix::process::CommandExt, process::Stdio}; 19 | 20 | use sysinfo::{self, PidExt, ProcessExt, System, SystemExt}; 21 | use tokio::{ 22 | self, 23 | io::{AsyncBufReadExt, AsyncReadExt}, 24 | process::Command, 25 | }; 26 | pub mod configurer; 27 | mod nft; 28 | pub mod util; 29 | pub mod watcher; 30 | 31 | use configurer::*; 32 | 33 | // Standard procedure 34 | // Creates various netns, base-vpn, socks, i2p, lokinet, un-firewalled 35 | // Kill other running processes, suspected 36 | // Fork, setns, drop privs, start daemons 37 | pub static mut logger: Option = None; 38 | 39 | pub fn substitute_argv<'a>(n_info: &'a NetnsInfo, argv: &mut Vec) { 40 | let mut sub_map: HashMap = HashMap::new(); 41 | sub_map.insert(format!("${}", "subnet_veth"), &n_info.subnet_veth); 42 | sub_map.insert(format!("${}", "subnet6_veth"), &n_info.subnet6_veth); 43 | sub_map.insert(format!("${}", "ip_vh"), &n_info.ip_vh); 44 | sub_map.insert(format!("${}", "ip6_vh"), &n_info.ip6_vh); 45 | sub_map.insert(format!("${}", "ip_vn"), &n_info.ip_vn); 46 | sub_map.insert(format!("${}", "ip6_vn"), &n_info.ip6_vn); 47 | 48 | for s in argv.iter_mut() { 49 | let mut s_ = s.to_owned(); 50 | for (key, value) in &sub_map { 51 | s_ = s_.replace(key, value); 52 | } 53 | *s = s_; 54 | } 55 | } 56 | 57 | #[test] 58 | fn test_substitute_argv() { 59 | let n_info = NetnsInfo { 60 | base_name: "x".to_owned(), 61 | subnet_veth: "eth0".to_string(), 62 | subnet6_veth: "eth1".to_string(), 63 | ip_vh: "192.168.0.1".to_string(), 64 | ip6_vh: "2001:db8::1".to_string(), 65 | ip_vn: "192.168.0.2".to_string(), 66 | ip6_vn: "2001:db8::2".to_string(), 67 | veth_base_name: "ss".to_owned(), 68 | id: 2, 69 | }; 70 | 71 | let mut argv = vec![ 72 | "ping".to_string(), 73 | "-c".to_string(), 74 | "1".to_string(), 75 | "$ip_vnxx".to_string(), 76 | ]; 77 | 78 | substitute_argv(&n_info, &mut argv); 79 | 80 | assert_eq!( 81 | argv, 82 | vec![ 83 | "ping".to_string(), 84 | "-c".to_string(), 85 | "1".to_string(), 86 | "192.168.0.2xx".to_string(), 87 | ] 88 | ); 89 | } 90 | 91 | pub fn kill_suspected() { 92 | let s = System::new_all(); 93 | for (pid, process) in s.processes() { 94 | // kill by saved pids 95 | // or by matching commandlines 96 | let c = process.cmd(); 97 | if c.into_iter().any(|x| x.contains("tun2socks")) 98 | || c.into_iter().any(|x| x.contains("gost")) 99 | || c.into_iter().any(|x| x.contains("dnsproxy")) 100 | { 101 | println!("killed {pid} {}", c[0]); 102 | unsafe { 103 | kill(pid.as_u32() as i32, SIGTERM); 104 | } 105 | } 106 | } 107 | } 108 | 109 | // first four args must be Some() 110 | pub async fn inner_daemon( 111 | profile: Option, 112 | uid: Option, 113 | gid: Option, 114 | fd: Option, 115 | pid: Option, // for non-persistent netns 116 | ) -> Result<()> { 117 | let mut tun_target_port = 9909; 118 | use tidy_tuntap::{flags, Tun}; 119 | // enters the netns 120 | // add addrs to the veth 121 | // makes a tun 122 | // runs daemons 123 | 124 | unsafe { 125 | log::trace!("set logger"); 126 | // logger = Some( 127 | // flexi_logger::Logger::try_with_env_or_str("error,netns_proxy=debug") 128 | // .unwrap() 129 | // .log_to_file(FileSpec::default()) 130 | // .duplicate_to_stdout(flexi_logger::Duplicate::All) 131 | // .start() 132 | // .unwrap(), 133 | // ); 134 | logger = Some( 135 | flexi_logger::Logger::try_with_env_or_str("error,netns_proxy=debug") 136 | .unwrap() 137 | .log_to_stdout() 138 | .start() 139 | .unwrap(), 140 | ); 141 | } 142 | 143 | let profile = profile.unwrap(); 144 | let fd = fd.unwrap().parse()?; 145 | 146 | let state = NetnspState::load(Default::default()).await?; 147 | 148 | let config: ConfigRes = state.res; 149 | let secret: Secret = state.conf; 150 | 151 | log::info!("netns-proxy of profile {profile}, sub-process started"); 152 | 153 | // get into a process' netns 154 | enter_ns_by_fd(fd)?; 155 | 156 | let tun_name = "s_tun"; 157 | 158 | let netconf = if pid.is_none() { 159 | log::trace!("pid not supplied for netnsp-sub"); 160 | config.netns_info.get(&profile).unwrap() 161 | } else { 162 | // assumption: PIDs in the config do not collide 163 | // which stands if we run it from the same PID namespace all along 164 | config.try_get_netinfo(pid.as_ref().unwrap().parse()?)? 165 | }; 166 | 167 | let configurer = Configurer::new(); 168 | 169 | configurer.set_up("lo").await?; 170 | configurer 171 | .add_addrs_guest(&netconf.veth_base_name, netconf) 172 | .await?; 173 | 174 | let tun = Tun::new(tun_name, false)?; // prepare a TUN for tun2socks, as root. 175 | // the TUN::new here creates a non-persistent TUN 176 | // empirically, TUN::new does not error when there is existing TUN with the same name, and says the dev to be up 177 | 178 | let flags = tun.flags().unwrap(); 179 | log::info!("got TUN {}, flags {:?}", tun_name, flags); 180 | 181 | if !flags.intersects(flags::Flags::IFF_UP) { 182 | log::info!("bring TUN up, {}", tun_name); 183 | tun.bring_up()?; 184 | let flags = tun.flags().unwrap(); 185 | anyhow::ensure!(flags.intersects(flags::Flags::IFF_UP)); 186 | } 187 | 188 | tun_ops(tun)?; // drop File 189 | 190 | let r_ui: u32; 191 | let r_gi: u32; 192 | use util::get_non_priv_user; 193 | (r_ui, r_gi) = get_non_priv_user(uid, gid)?; 194 | 195 | // the uid and gid for non-privileged processes 196 | let gi = Gid::from_raw(r_gi); 197 | let ui = Uid::from_raw(r_ui); 198 | 199 | assert!(!ui.is_root()); 200 | assert!(gi.as_raw() != 0); 201 | log::debug!("unprileged processes will be run with, gid {gi}, uid {ui}"); 202 | 203 | let params = &secret.params[&profile]; 204 | if let Some(tp) = params.hport { 205 | tun_target_port = tp; 206 | } 207 | let mut proc_set = tokio::task::JoinSet::new(); 208 | let ip_vh: IpNetwork = netconf.ip_vh.parse()?; 209 | let ip_vh_ip = ip_vh.ip().to_string(); 210 | let mut base_prxy_v4 = "socks5://".to_owned() + &ip_vh_ip + ":" + &tun_target_port.to_string(); 211 | if params.hport.is_some() { 212 | base_prxy_v4 = format!("socks5://{}:{}", &ip_vh_ip, params.hport.unwrap()); 213 | } 214 | if params.chain { 215 | // so, this takes precedence 216 | base_prxy_v4 = format!("socks5://127.0.0.1:1080") 217 | } 218 | 219 | // Tun2socks 220 | let mut tun2 = std::process::Command::new("tun2socks"); 221 | tun2.uid(ui.into()).gid(gi.into()).groups(&[gi.into()]); 222 | tun2.args(&["-device", tun_name, "-proxy", &base_prxy_v4]); 223 | let mut tun2_async: Command = tun2.into(); 224 | tun2_async.stdout(Stdio::piped()); 225 | let mut tun2h = tun2_async.spawn()?; 226 | let stdout = tun2h.stdout.take().unwrap(); 227 | let reader = tokio::io::BufReader::new(stdout).lines(); 228 | let (tx, rx) = tokio::sync::oneshot::channel(); 229 | let pre = format!("{}/tun2socks", netconf.base_name); 230 | tokio::spawn(watch_log(reader, Some(tx), pre)); 231 | rx.await?; 232 | configurer.set_up(tun_name).await?; 233 | let vn = &veth_from_base(&netconf.veth_base_name, false); 234 | configurer.set_up(&vn).await?; 235 | configurer 236 | .ip_add_route(tun_name, None, Some(true)) 237 | .await 238 | .ok(); 239 | configurer 240 | .ip_add_route(tun_name, None, Some(false)) 241 | .await 242 | .ok(); 243 | proc_set.spawn((async move || { 244 | tun2h 245 | .wait() 246 | .map_err(|e| anyhow::Error::from(e)) 247 | .map_ok(|o| (o, "tun2socks".to_owned())) 248 | .await 249 | })()); 250 | 251 | // Dnsproxy 252 | let mut dns = std::process::Command::new("dnsproxy"); 253 | dns.uid(ui.into()).gid(gi.into()).groups(&[gi.into()]); 254 | 255 | if let Some(dv) = ¶ms.dns_argv { 256 | dns.args(dv); 257 | } else { 258 | // dnsproxy is behind the proxy 259 | // DNSSEC or such is unnecessary. 260 | if params.ipv6 { 261 | dns.args(&[ 262 | "-l", 263 | "127.0.0.1", 264 | "-l", 265 | "127.0.0.53", // systemd-resolved 266 | "-l", 267 | "::1", 268 | "-p", 269 | "53", 270 | "-u", 271 | "tcp://[2620:119:35::35]:53", 272 | "--cache", 273 | ]); 274 | } else { 275 | dns.args(&[ 276 | "-l", 277 | "127.0.0.1", 278 | "-l", 279 | "127.0.0.53", // systemd-resolved 280 | "-l", 281 | "::1", 282 | "-p", 283 | "53", 284 | "-u", 285 | "tcp://1.1.1.1:53", 286 | "--cache", 287 | ]); 288 | } 289 | } 290 | let mut dns_async: Command = dns.into(); 291 | dns_async.stdout(Stdio::piped()); 292 | dns_async.stderr(Stdio::piped()); 293 | let mut dnsh = dns_async.spawn()?; 294 | let pre = format!("{}/dnsproxy", &netconf.base_name); 295 | watch_both(&mut dnsh, pre, None).await?; 296 | proc_set.spawn((async move || { 297 | dnsh.wait() 298 | .map_err(|e| anyhow::Error::from(e)) 299 | .map_ok(|o| (o, "dnsproxy".to_owned())) 300 | .await 301 | })()); 302 | 303 | // User-supplied process 304 | if let Some(cmd) = ¶ms.cmd { 305 | let mut uproc = std::process::Command::new(&cmd.program); 306 | let mut cmd_c: NetnsParamCmd = cmd.to_owned(); 307 | substitute_argv(&netconf, &mut cmd_c.argv); 308 | 309 | uproc.uid(ui.into()).gid(gi.into()).groups(&[gi.into()]); 310 | uproc.args(&cmd_c.argv); 311 | let mut uproc_async: Command = uproc.into(); 312 | uproc_async.stdout(Stdio::piped()); 313 | uproc_async.stderr(Stdio::piped()); 314 | let mut uproch = uproc_async.spawn()?; 315 | let pre = format!("{}/cmd", &netconf.base_name); 316 | watch_both(&mut uproch, pre, None).await?; 317 | 318 | proc_set.spawn((async move || { 319 | uproch 320 | .wait() 321 | .map_err(|e| anyhow::Error::from(e)) 322 | .map_ok(|o| (o, format!("{}, argv {:?}", cmd_c.program, cmd_c.argv))) 323 | .await 324 | })()); 325 | } 326 | 327 | while let Some(r) = proc_set.join_next().await { 328 | let r = r??; 329 | log::warn!( 330 | "\"{}\" exited with {}, for {}", 331 | r.1, 332 | r.0, 333 | &netconf.base_name 334 | ) 335 | } 336 | 337 | Ok(()) 338 | } 339 | -------------------------------------------------------------------------------- /src/watcher.rs: -------------------------------------------------------------------------------- 1 | // watcher interfaces with the OS, by watching for apps to proxify. 2 | 3 | // through any way, the client finds a new netns to proxify 4 | // send command through the channel 5 | // the server manipulates the netns as commanded 6 | // specify netns by fd, or pid 7 | 8 | // Warning: depending on the security model, the watching may be vulnerable in security 9 | // one may trigger the watcher by faking that a new flatpak app is launched. 10 | // however, starting a flatpak does not need superuser privileges, nor does faking a new instance 11 | // the difference is that the user intends to do the former, but not the latter. 12 | // It is recommended to sandbox untrusted apps, so the faking can not be done. 13 | // TODO: validate what is being started by checking GetAppState 14 | 15 | use anyhow::{Ok, Result}; 16 | use futures::{Future, StreamExt}; 17 | use inotify::{EventMask, WatchMask}; 18 | 19 | use serde::{Deserialize, Serialize}; 20 | use std::pin::Pin; 21 | use std::{ 22 | collections::{HashMap, HashSet}, 23 | ffi::OsString, 24 | os::fd::AsRawFd, 25 | path::Path, 26 | path::PathBuf, 27 | result::Result as stdRes, 28 | sync::Arc, 29 | }; 30 | use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; 31 | use tokio::{io::AsyncReadExt, sync::RwLock, task::JoinSet}; 32 | use zbus::{dbus_interface, dbus_proxy, Connection}; 33 | 34 | use crate::configurer::{ 35 | self, ensure_ns_not_root, get_self_netns_inode, nsfd_by_path, veth_from_base, NetnsInfo, 36 | NetnspState, 37 | }; 38 | use crate::util::{flatpak_perms_checkup, kill_children}; 39 | use ini; 40 | 41 | struct NetnspDbus; 42 | 43 | #[dbus_interface(name = "app.netnsp")] 44 | impl NetnspDbus { 45 | fn transform_netns_by_pid(&self, _pid: i32) {} 46 | fn transform_netns_by_path(&self, _path: String) {} 47 | } 48 | 49 | // Portal backend APIs provided by flatpak. 50 | // It also monitors non-flatpak apps 51 | 52 | #[dbus_proxy( 53 | interface = "org.freedesktop.impl.portal.Background", 54 | default_service = "org.freedesktop.impl.portal.desktop.kde", 55 | default_path = "/org/freedesktop/portal/desktop" 56 | )] 57 | pub trait KDEPortal { 58 | #[dbus_proxy(signal)] 59 | fn RunningApplicationsChanged(&self) -> zbus::Result<()>; 60 | fn GetAppState(&self) -> zbus::Result>; 61 | } 62 | 63 | // track active profiles by pid 64 | 65 | pub type ActiveProfiles = HashMap; 66 | 67 | #[derive(Serialize, Deserialize, Default, Clone)] 68 | pub struct ProfileState { 69 | // root-most process that is to be in the netns 70 | pub pid: i32, 71 | // eg. com.github.tchx84.Flatseal, if for flatpak 72 | pub app_id: String, 73 | // Profile in demand, as configured 74 | pub default_pofile: String, 75 | pub net: NetnsInfo, 76 | } 77 | 78 | pub struct WatcherState { 79 | netnsp: NetnspState, 80 | daemons: RwLock)>>, 81 | configurer: configurer::Configurer, 82 | send_task: UnboundedSender)> + Send>>>, 83 | recv_task: UnboundedReceiver)> + Send>>>, 84 | } 85 | 86 | impl WatcherState { 87 | pub async fn create( 88 | configurer: configurer::Configurer, 89 | state: NetnspState, 90 | ) -> Result { 91 | log::info!("watcher started"); 92 | let (send_task, recv_task) = unbounded_channel(); 93 | let mut n = WatcherState { 94 | netnsp: state, 95 | daemons: RwLock::new(JoinSet::new()), 96 | configurer, 97 | send_task, 98 | recv_task, 99 | }; 100 | // TODO: check for flatpak app perms 101 | n.auth().await?; 102 | 103 | Ok(n) 104 | } 105 | pub async fn auth(&mut self) -> Result<()> { 106 | // If it is the first run, do nothing and record netns inode 107 | // If there is recorded inode, do auth. 108 | 109 | if self.netnsp.res.root_inode > 0 { 110 | let si = get_self_netns_inode()?; 111 | if si != self.netnsp.res.root_inode { 112 | log::error!("Access denied. This run is in a netns different from what is recorded in the netnsp.json"); 113 | std::process::exit(1); 114 | } else { 115 | log::info!("Netns inodes match"); 116 | } 117 | } else { 118 | // if the json does not contain inode (which is malformed), serde should error 119 | // therefore, here it is initalizing. 120 | self.netnsp.res.root_inode = crate::get_pid1_netns_inode().await?; 121 | } 122 | 123 | Ok(()) 124 | } 125 | pub async fn resume_netns(&mut self) -> Result<()> { 126 | // re-start NETNSes that are associated with processes 127 | log::debug!("resuming netns for processes"); 128 | if let Some(flat) = self.netnsp.res.flatpak.as_ref() { 129 | let pids: Vec = flat.keys().map(|x| *x).collect(); 130 | // we may just restart netnsp-sub 131 | // since netlink should have been configured 132 | // we have this exact process running ==> the user must have not rebooted 133 | // ==> the config probably has not been changed 134 | // and we will not re-configure. it's temporary anyway 135 | for item in pids { 136 | self.start_netnsp_sub(item).await?; 137 | } 138 | } 139 | Ok(()) 140 | } 141 | pub async fn start(mut self) -> Result<()> { 142 | // start all watching coroutines 143 | 144 | self.cleanup()?; 145 | let list_appids = self.netnsp.conf.flatpak.keys().map(|s| s.clone()).collect(); 146 | flatpak_perms_checkup(list_appids)?; 147 | self.resume_netns().await?; 148 | 149 | let mut arc = Arc::new(self); 150 | let arc1 = arc.clone(); 151 | let f = async move { 152 | let mut ownit: Arc = arc1; 153 | let m = unsafe { Arc::get_mut_unchecked(&mut ownit) }; 154 | let res = fs_watcher(m).await; 155 | ("fs_watcher".to_owned(), res) 156 | }; 157 | // circumventing borrow checker 158 | let m = unsafe { Arc::get_mut_unchecked(&mut arc) }; 159 | m.send_task.send(Box::pin(f)).ok().unwrap(); 160 | 161 | loop { 162 | tokio::select! { 163 | maybe_task = m.recv_task.recv() => { 164 | log::trace!("received new daemon"); 165 | if let Some(task) = maybe_task { 166 | 167 | m.daemons.write().await.spawn(task); 168 | } 169 | }, 170 | maybe_res = { 171 | let f1 = async { 172 | let mut joinset = m.daemons.write().await; 173 | joinset.join_next().await 174 | }; 175 | f1 176 | } => { 177 | if let Some(res) = maybe_res { 178 | let res = res?; 179 | log::error!("{:?}", res); 180 | } 181 | } 182 | } 183 | } 184 | 185 | Ok(()) 186 | } 187 | pub fn get_flatpak_profile_state(&mut self, pid: i32) -> Option<&mut ProfileState> { 188 | self.netnsp.res.flatpak.as_mut().unwrap().get_mut(&pid) 189 | } 190 | pub async fn start_netnsp_sub(&mut self, pid: i32) -> Result<()> { 191 | let ps = self.netnsp.res.flatpak.as_ref().unwrap().get(&pid).unwrap(); 192 | let task_name = format!("netnsp-sub of {}", ps.net.base_name); 193 | 194 | let mut path = std::env::current_exe()?; 195 | path.pop(); 196 | path.push("netnsp-sub"); 197 | 198 | let default_pofile = ps.default_pofile.to_owned(); 199 | 200 | let tsk = async move { 201 | ( 202 | task_name.clone(), 203 | async move { 204 | use crate::util::get_non_priv_user; 205 | let (puid, pgid) = get_non_priv_user(None, None)?; 206 | use nix::fcntl::{open, OFlag}; 207 | use nix::sys::stat::Mode; 208 | let process = procfs::process::Process::new(pid)?; 209 | let o: OsString = OsString::from("net"); 210 | let nss = process.namespaces()?; 211 | let proc_ns = nss 212 | .get(&o) 213 | .ok_or(anyhow::anyhow!("ns/net not found for given pid"))?; 214 | let fd = open(&proc_ns.path, OFlag::O_RDONLY, Mode::empty()).unwrap(); 215 | ensure_ns_not_root(fd)?; 216 | let mut cmd: tokio::process::Child = tokio::process::Command::new(path.clone()) 217 | .arg(default_pofile) 218 | .arg(puid.to_string()) 219 | .arg(pgid.to_string()) 220 | .arg(fd.to_string()) 221 | .arg(pid.to_string()) 222 | .uid(0) 223 | .spawn() 224 | .unwrap(); 225 | let cmdpid = cmd.id().unwrap().try_into().unwrap(); 226 | let task = cmd.wait(); 227 | let proc_finish = async { 228 | let f = unsafe { pidfd::PidFd::open(pid, 0)?.into_future() }; 229 | f.await?; 230 | log::debug!("process {} exited", pid); 231 | Ok(()) 232 | }; 233 | 234 | tokio::select! { 235 | res = task => { 236 | if res.is_err() { 237 | return res.map(|_| ()).map_err(anyhow::Error::from); 238 | } 239 | }, 240 | _ = proc_finish => { 241 | kill_children(cmdpid)?; 242 | log::debug!("terminate netnsp-sub, {:?}", task_name); 243 | } 244 | }; 245 | 246 | Ok(()) 247 | } 248 | .await, 249 | ) 250 | }; 251 | 252 | self.send_task.send(Box::pin(tsk)).ok().unwrap(); 253 | Ok(()) 254 | } 255 | pub async fn apply_profile_by_pid(&mut self, pid: i32) -> Result<()> { 256 | let ps = self.netnsp.res.flatpak.as_ref().unwrap().get(&pid).unwrap(); 257 | anyhow::ensure!(!ps.default_pofile.is_empty()); 258 | let process = procfs::process::Process::new(pid)?; 259 | let o: OsString = OsString::from("net"); 260 | let nss = process.namespaces()?; 261 | let proc_ns = nss 262 | .get(&o) 263 | .ok_or(anyhow::anyhow!("ns/net not found for given pid"))?; 264 | let r = nsfd_by_path(&proc_ns.path.as_path())?; 265 | ensure_ns_not_root(r)?; 266 | configurer::config_pre_enter_ns(&ps.net, &self.configurer, r.as_raw_fd()).await?; 267 | self.netnsp 268 | .nft_for_interface(&veth_from_base(&ps.net.veth_base_name, true)) 269 | .await?; 270 | configurer::config_pre_enter_ns_up(&ps.net, &self.configurer).await?; 271 | 272 | // may be flatpak app_id or that of other sandbox systems 273 | self.start_netnsp_sub(pid).await?; 274 | Ok(()) 275 | } 276 | pub fn cleanup(&mut self) -> Result<()> { 277 | // on my machine, dead processes' pids get removed from /proc 278 | if let Some(flat) = self.netnsp.res.flatpak.as_mut() { 279 | use procfs::process::*; 280 | let allproc: ProcessesIter = all_processes()?; 281 | let res = allproc.into_iter().filter_map(|p| p.ok()).map(|p| p.pid); 282 | let exiset: HashSet = HashSet::from_iter(res); 283 | let ite = flat.keys().map(|x| *x); 284 | let confset: HashSet = HashSet::from_iter(ite); 285 | // all that exists in Conf but not in Exi 286 | let to_remove = &confset - &exiset; 287 | flat.retain(|k, _v| !to_remove.contains(k)); 288 | } 289 | 290 | Ok(()) 291 | } 292 | } 293 | 294 | pub struct DBusClient<'a> { 295 | pub kde: KDEPortalProxy<'a>, 296 | } 297 | 298 | impl<'a> DBusClient<'a> { 299 | pub async fn new() -> Result> { 300 | let conn = Connection::session().await?; 301 | let proxy = KDEPortalProxy::new(&conn).await?; 302 | 303 | Ok(DBusClient { kde: proxy }) 304 | } 305 | pub async fn watcher(self) -> Result<()> { 306 | let mut r = self.kde.receive_RunningApplicationsChanged().await?; 307 | loop { 308 | r.next().await; 309 | log::debug!("RunningApplicationsChanged"); 310 | // find the newly started app. it could be a flatpak, or anything. 311 | } 312 | Ok(()) 313 | } 314 | } 315 | 316 | use tokio::fs; 317 | 318 | async fn read_pid_file_from_dir(dir_path: &Path) -> Result> { 319 | let mut pid_file_contents: Option = None; 320 | let mut entries = fs::read_dir(dir_path).await?; 321 | while let Some(entry) = entries.next_entry().await? { 322 | if let Some(file_name) = entry.file_name().to_str() { 323 | if file_name.contains("pid") { 324 | let file_path = entry.path(); 325 | pid_file_contents = Some(fs::read_to_string(file_path).await?); 326 | break; 327 | } 328 | } 329 | } 330 | Ok(pid_file_contents) 331 | } 332 | 333 | async fn process_event<'a>( 334 | ev: inotify::Event, 335 | flatpak_dir: &PathBuf, 336 | watcher: &mut WatcherState, 337 | once_set: &mut HashSet, 338 | ) -> Result<()> { 339 | // it may be an instance dir, or elses 340 | if let Some(fname) = ev.name.clone() { 341 | let fname = fname.to_string_lossy().into_owned(); 342 | let instance = fname.parse::(); 343 | if let stdRes::Ok(iid) = instance { 344 | if !once_set.contains(&iid) { 345 | if ev 346 | .mask 347 | .intersects(EventMask::ISDIR | EventMask::CLOSE_NOWRITE) 348 | { 349 | let mut instance_dir = flatpak_dir.clone(); 350 | instance_dir.push(fname.clone()); 351 | 352 | // we have only three files at this point 353 | // "bwrapinfo.json" - empty 354 | // "info" - filled 355 | // ".ref" - empty 356 | 357 | let mut info = instance_dir.clone(); 358 | info.push("info"); 359 | 360 | let mut info_file = tokio::fs::File::open(info).await?; 361 | let mut instnace_info_str = String::new(); 362 | info_file.read_to_string(&mut instnace_info_str).await?; 363 | 364 | log::debug!("flatpak instance {} detected", iid); 365 | // this waits for a close event until the info file is present 366 | // it will eventually appear 367 | once_set.insert(iid); 368 | 369 | let c = ini::Ini::load_from_str(&instnace_info_str).unwrap(); 370 | // if any of the following ops fail, we'll just error and give up 371 | let s_app = c 372 | .section(Some("Application")) 373 | .ok_or(anyhow::anyhow!("flatpak section missing"))?; 374 | let flatpak_id = s_app 375 | .get("name") 376 | .ok_or(anyhow::anyhow!("unexpected flatpak data. name missing"))?; 377 | 378 | let profile_or_not = watcher.netnsp.conf.flatpak.get(flatpak_id); 379 | 380 | if let Some(profile_name) = profile_or_not { 381 | let profile_name = profile_name.to_owned(); 382 | let pid_f = read_pid_file_from_dir(&instance_dir).await?; 383 | if pid_f.is_none() { 384 | log::error!("unexpected, pid_f is none, {:?}", &ev); 385 | } else { 386 | let pid_f = pid_f.unwrap(); 387 | log::debug!("flatpak app with pid {}", pid_f); 388 | let proc = procfs::process::Process::new(pid_f.parse()?)?; 389 | // not sure about the task part. lets get the main thread then 390 | let maint = proc.task_main_thread()?; 391 | let children = maint.children()?; 392 | if children.len() != 1 { 393 | log::info!("unexpected number of threads, {:?}", proc); 394 | } 395 | let the_child_pid = children[0] as i32; // pid of the process, in unshared netns 396 | let ifany = watcher.get_flatpak_profile_state(the_child_pid); 397 | let mut base_name4flatpak = flatpak_id.to_owned(); 398 | base_name4flatpak.push_str(&the_child_pid.to_string()); 399 | match ifany { 400 | Some(prof) => { 401 | prof.default_pofile = profile_name; 402 | } 403 | None => { 404 | let neti = 405 | watcher.netnsp.new_netinfo(base_name4flatpak).await?; 406 | watcher.netnsp.res.flatpak.as_mut().unwrap().insert( 407 | the_child_pid, 408 | ProfileState { 409 | pid: the_child_pid, 410 | app_id: profile_name.to_owned(), 411 | default_pofile: profile_name, 412 | net: neti, 413 | }, 414 | ); 415 | } 416 | } 417 | watcher.netnsp.dump().await?; 418 | watcher.apply_profile_by_pid(the_child_pid as i32).await?; 419 | } 420 | } else { 421 | log::info!( 422 | "flatpak app {} has no associated profile, skipping", 423 | flatpak_id 424 | ); 425 | } 426 | } 427 | } 428 | } 429 | } 430 | 431 | Ok(()) 432 | } 433 | 434 | // |-fish(632634)---bwrap(706664)---bwrap(706674)---com.github.tchx(706675)-+-{com.github+ 435 | // | | | |-{com.github+ 436 | // | | | |-{com.github+ 437 | // | | | |-{com.github+ 438 | // | | | |-{com.github+ 439 | // | | | |-{com.github+ 440 | // | | | |-{com.github+ 441 | // | | | |-{com.github+ 442 | // | | | |-{com.github+ 443 | // | | | |-{com.github+ 444 | // | | | |-{com.github+ 445 | // | | | |-{com.github+ 446 | // | | | |-{com.github+ 447 | // | | | |-{com.github+ 448 | // | | | |-{com.github+ 449 | // | | | |-{com.github+ 450 | // | | | |-{com.github+ 451 | // | | | |-{com.github+ 452 | // | | | |-{com.github+ 453 | // | | | |-{com.github+ 454 | // | | | | |-{com.github+ 455 | // detected pid 456 | // NSpid: 706664, NSpid: 706674 1, NSpid: 706675 2 457 | // /proc/706664/ns/net -> 'net:[4026531840]' 458 | // /proc/706674/ns/net -> 'net:[4026534547]' 459 | // /proc/706675/ns/net -> 'net:[4026534547]' 460 | // /proc/self/ns/net -> 'net:[4026531840]' (my shell in default netns) 461 | // ip netns identify 706674 462 | // (blank) 463 | // apparently we should take the immediate child 464 | // ~> nsenter --target 706674 --net 465 | // nsenter: reassociate to namespace 'ns/net' failed: Operation not permitted 466 | // sudo nsenter --target 706674 --net 467 | // # ip l 468 | // 1: lo: mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 469 | // link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 470 | // ───────┬──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 471 | // │ File: /proc/706674/status 472 | // ───────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 473 | // 1 │ Name: bwrap 474 | // ───────┬──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 475 | // │ File: /proc/706675/status 476 | // ───────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 477 | // 1 │ Name: com.github.tchx 478 | // 2 │ Umask: 0022 479 | // 3 │ State: S (sleeping) 480 | // GetAppState, 'com.github.tchx84.Flatseal': , 481 | // com.github.tchx84.Flatseal == its flatpak app id != process name 482 | // non-flatpak names from GetAppState are derived in an unknown way 483 | // but, `info` file links the flatpak id and the process 484 | 485 | // for flatpak and more 486 | pub async fn fs_watcher<'a>(watcher: &mut WatcherState) -> Result<()> { 487 | use crate::util::get_non_priv_user; 488 | let (uid, _) = get_non_priv_user(None, None)?; 489 | let flatpak_dir = PathBuf::from(format!("/run/user/{}/.flatpak/", uid)); 490 | log::debug!("flatpak watcher started"); 491 | let mut inoti = inotify::Inotify::init()?; 492 | inoti 493 | .watches() 494 | .add(&flatpak_dir, WatchMask::CLOSE_NOWRITE)?; 495 | // sometimes directories are opened without CREATE. no idea 496 | // CLOSE_NOWRITE seems to be the best 497 | 498 | // let mut wdmap: HashMap = HashMap::new(); // map wd to instance path 499 | let mut once_set: HashSet = HashSet::new(); // whether an instance has been examined. prevent looping. 500 | 501 | let mut buf = [0; 1024]; 502 | let mut stream = inoti.into_event_stream(&mut buf)?; 503 | while let Some(event_or_error) = stream.next().await { 504 | // event that is triggered for each `flatpak run` (each run creates a new instance) 505 | // dir name is instance_id. 506 | // event: Event { wd: WatchDescriptor { id: 1, fd: (Weak) }, mask: CREATE | ISDIR, cookie: 0, name: Some("2005139353") } 507 | // pid file -> bwrap --args 41 com.github.tchx84.Flatseal 508 | match event_or_error { 509 | stdRes::Ok(ev) => { 510 | // log::trace!("event: {:?}", &ev); 511 | let ino = stream.into_inotify(); 512 | match process_event(ev, &flatpak_dir, watcher, &mut once_set).await { 513 | anyhow::Result::Ok(_) => {} 514 | Err(e) => { 515 | log::error!("fs watcher, {:?}", e); 516 | } 517 | } 518 | inoti = ino; 519 | stream = inoti.into_event_stream(&mut buf)?; 520 | } 521 | Err(e) => { 522 | log::error!("fs watcher, {:?}", e); 523 | } 524 | } 525 | } 526 | 527 | Ok(()) 528 | } 529 | 530 | #[test] 531 | fn test_parse_flatpak_info() { 532 | let s = "[Application] 533 | name=com.github.tchx84.Flatseal 534 | runtime=runtime/org.gnome.Platform/x86_64/44 535 | 536 | [Instance] 537 | instance-id=742735114 538 | "; 539 | let c = ini::Ini::load_from_str(s).unwrap(); 540 | dbg!(&c); 541 | let s_app = c.section(Some("Application")); 542 | let n = s_app.unwrap().get("name"); 543 | dbg!(s_app, n); 544 | } 545 | -------------------------------------------------------------------------------- /src/configurer.rs: -------------------------------------------------------------------------------- 1 | #![feature(ip)] 2 | #![feature(async_closure)] 3 | #![feature(exit_status_error)] 4 | #![feature(setgroups)] 5 | 6 | 7 | use futures::{FutureExt, TryFutureExt}; 8 | use ipnetwork::IpNetwork; 9 | use tokio::io::AsyncWriteExt; 10 | 11 | use crate::watcher::ActiveProfiles; 12 | use crate::{nft::FO_CHAIN}; 13 | use anyhow::{anyhow, Ok, Result}; 14 | use netns_rs::{NetNs}; 15 | use nix::{ 16 | sched::CloneFlags, 17 | unistd::{setgroups, Gid, Uid}, 18 | }; 19 | use serde::{Deserialize, Serialize}; 20 | use std::net::Ipv6Addr; 21 | 22 | use std::{ 23 | ffi::{CString, OsString}, 24 | net::{Ipv4Addr}, 25 | os::{fd::RawFd, unix::process::CommandExt}, 26 | path::{Path, PathBuf}, 27 | process::{exit}, 28 | }; 29 | use std::{collections::HashMap}; 30 | use std::{os::fd::AsRawFd}; 31 | use tokio::{ 32 | self, 33 | fs::File, 34 | io::{AsyncBufReadExt, AsyncReadExt}, 35 | }; 36 | 37 | pub const NETNS_PATH: &str = "/run/netns/"; 38 | 39 | // generated info and state store 40 | #[derive(Serialize, Deserialize, Default)] 41 | pub struct ConfigRes { 42 | // resultant/generated info for each persistent/named netns 43 | pub netns_info: HashMap, 44 | // Flatpak instance pids to profile names. Transient. 45 | pub flatpak: Option, 46 | pub root_inode: u64, 47 | // Counter, for any number x > counter such that x is never used 48 | // which ensures non-collision within the scope of one ConfigRes 49 | // currently not in use. I don't bother deleting it 50 | pub counter: u16, 51 | } 52 | 53 | // It may contain secret proxy parameters, so let's just consider them a secret as a whole 54 | #[derive(Serialize, Deserialize, Default, Clone)] 55 | pub struct Secret { 56 | // netns_name to params 57 | // aka, Profiles 58 | pub params: HashMap, 59 | // Flatpak app IDs to profile names 60 | pub flatpak: HashMap, 61 | } 62 | 63 | pub struct NetnspState { 64 | pub res: ConfigRes, 65 | pub conf: Secret, 66 | pub paths: ConfPaths, 67 | nft_refresh_once: bool, 68 | } 69 | 70 | pub struct ConfPaths { 71 | conf: String, 72 | res: String, 73 | } 74 | 75 | // Each instance has a unique NetnsInfo 76 | // identified by pid OR persistent name 77 | pub type InstanceID = either::Either; 78 | 79 | impl Default for ConfPaths { 80 | fn default() -> Self { 81 | Self { 82 | conf: "./secret.json".to_owned(), 83 | res: "./netnsp.json".to_owned(), 84 | } 85 | } 86 | } 87 | 88 | impl ConfigRes { 89 | pub fn try_get_netinfo(&self, pid: i32) -> Result<&NetnsInfo> { 90 | Ok(&self 91 | .flatpak 92 | .as_ref() 93 | .ok_or_else(|| anyhow!("no flatpak: HashMap"))? 94 | .get(&pid) 95 | .ok_or_else(|| anyhow!("Failed to retrieve net info for process {}", pid))? 96 | .net) 97 | // map1.get(key).or_else(|| map2.get(key)).unwrap_or(&"not found"); 98 | } 99 | } 100 | 101 | impl NetnspState { 102 | // for nftables 103 | pub async fn get_link_names_persistent(&self) -> Result> { 104 | let base_names: Vec<&String> = self 105 | .res 106 | .netns_info 107 | .iter() 108 | .map(|x| &x.1.veth_base_name) 109 | .collect(); 110 | 111 | let veth_host: Vec = base_names 112 | .iter() 113 | .map(|base| veth_from_base(base, true)) 114 | .collect(); 115 | Ok(veth_host) 116 | } 117 | // do a full sync of firewall intention 118 | pub async fn apply_nft(&mut self) -> Result<()> { 119 | let i_names = self.get_link_names_persistent().await?; 120 | let inames = i_names.iter().map(|s| s.as_str()).collect::>(); 121 | 122 | nft::apply_block_forwad(&inames)?; 123 | // added the tables and chains 124 | self.nft_refresh_once = true; 125 | // after that only individual rules need to be added for each flatpak 126 | Ok(()) 127 | } 128 | // incrementally apply rules for an interface 129 | // may error if the a full sync hasn't been done beforehand 130 | pub async fn nft_for_interface(&self, name: &str) -> Result<()> { 131 | use rustables::*; 132 | log::info!("add nft rule for {}", name); 133 | let table = Table::new(ProtocolFamily::Inet).with_name(nft::TABLE_NAME.to_owned()); 134 | let chain = Chain::new(&table) 135 | .with_hook(Hook::new(HookClass::Forward, 0)) 136 | .with_name(FO_CHAIN) 137 | .with_policy(ChainPolicy::Accept); 138 | let rule = nft::drop_interface_rule(name, &chain)?; 139 | let mut batch: Batch = Batch::new(); 140 | batch.add(&rule, MsgType::Add); 141 | batch.send()?; 142 | 143 | Ok(()) 144 | } 145 | pub async fn load(paths: ConfPaths) -> Result { 146 | let path = Path::new(&paths.conf); 147 | let mut file = File::open(path).await?; 148 | let mut contents = String::new(); 149 | file.read_to_string(&mut contents).await?; 150 | 151 | let secret: Secret = serde_json::from_str(&contents)?; 152 | 153 | let path = Path::new(&paths.res); 154 | let mut res: ConfigRes = if path.exists() { 155 | let mut file = tokio::fs::File::open(path).await?; 156 | let mut contents = String::new(); 157 | file.read_to_string(&mut contents).await?; 158 | // will reject if missing fields 159 | serde_json::from_str(&contents)? 160 | } else { 161 | // allow it to not exist 162 | ConfigRes::default() 163 | }; 164 | if res.flatpak.is_none() { 165 | res.flatpak = Some(HashMap::new()) 166 | } 167 | 168 | Ok(Self { 169 | res, 170 | conf: secret, 171 | paths, 172 | nft_refresh_once: false, 173 | }) 174 | } 175 | pub fn profile_names(&self) -> Vec { 176 | self.conf.clone().params.into_keys().collect() 177 | } 178 | pub async fn dump(&self) -> Result<()> { 179 | let serialized = serde_json::to_string_pretty(&self.res)?; 180 | let mut file = tokio::fs::File::create(&self.paths.res).await?; 181 | log::info!("config result dumped in ./netnsp.json."); 182 | file.write_all(serialized.as_bytes()).await?; 183 | Ok(()) 184 | } 185 | pub fn get_avail_id(&self) -> Result { 186 | const MAX: u16 = 255; 187 | let mut ids: Vec = self 188 | .res 189 | .netns_info 190 | .iter() 191 | .map(|x| x.1.id) 192 | .chain( 193 | self.res 194 | .flatpak 195 | .as_ref() 196 | .unwrap() 197 | .iter() 198 | .map(|x| x.1.net.id), 199 | ) 200 | .collect(); 201 | ids.sort(); 202 | let mut i = 0; 203 | while i + 1 < ids.len() { 204 | if ids[i + 1] - ids[i] > 1 { 205 | return Ok(ids[i] + 1); 206 | } 207 | i += 1; 208 | } 209 | if ids.len() > 0 { 210 | if ids.last().unwrap() < &MAX { 211 | return Ok(ids.last().unwrap() + 1); 212 | } 213 | Err(anyhow::anyhow!("no id avail")) 214 | } else { 215 | Ok(0) 216 | } 217 | } 218 | // generate NetnsInfo for a new isntance, ie. non-duplicating IPs 219 | pub async fn new_netinfo(&self, base_name: String) -> Result { 220 | let id: u16 = self.get_avail_id()?; 221 | let id8: u8 = id.try_into()?; 222 | let pre4: u8 = 24; 223 | let pre6: u8 = 112; 224 | let subnet_veth: IpNetwork = IpNetwork::new(Ipv4Addr::new(10, 27, id8, 0).into(), pre4)?; 225 | let subnet6_veth: IpNetwork = IpNetwork::new( 226 | Ipv6Addr::new(0xfc0f, 0x2cdd, 0xeeff, 0, 0, 0, id, 0).into(), 227 | pre6, 228 | )?; 229 | let ip_vh: IpNetwork = IpNetwork::new(Ipv4Addr::new(10, 27, id8, 1).into(), pre4)?; 230 | let ip_vn: IpNetwork = IpNetwork::new(Ipv4Addr::new(10, 27, id8, 2).into(), pre4)?; 231 | let ip6_vh: IpNetwork = IpNetwork::new( 232 | Ipv6Addr::new(0xfc0f, 0x2cdd, 0xeeff, 0, 0, 0, id, 1).into(), 233 | pre6, 234 | )?; 235 | let ip6_vn: IpNetwork = IpNetwork::new( 236 | Ipv6Addr::new(0xfc0f, 0x2cdd, 0xeeff, 0, 0, 0, id, 2).into(), 237 | pre6, 238 | )?; 239 | 240 | Ok(NetnsInfo { 241 | base_name: base_name.clone(), 242 | subnet_veth: subnet_veth.to_string(), 243 | subnet6_veth: subnet6_veth.to_string(), 244 | ip_vh: ip_vh.to_string(), 245 | ip_vn: ip_vn.to_string(), 246 | ip6_vh: ip6_vh.to_string(), 247 | ip6_vn: ip6_vn.to_string(), 248 | veth_base_name: if base_name.len() < 12 { 249 | base_name 250 | } else { 251 | "nsp".to_owned() + &id.to_string() 252 | }, 253 | id: id.into(), 254 | }) 255 | } 256 | } 257 | 258 | // aka, Profiles 259 | #[derive(Serialize, Deserialize, Default, Clone)] 260 | pub struct NetnsParams { 261 | // the program to be run along 262 | // it is run as non-root, but not sandboxed too. 263 | pub cmd: Option, 264 | // the port which the socks5 proxy is at 265 | // defaults to 9909 266 | pub hport: Option, 267 | // whether you want to chain proxies 268 | // set to true and Tun2socks will direct traffic to socks5://localhost:1080 269 | // set to false and traffic will be directed to socks5:://veth_host:hport 270 | #[serde(default)] 271 | pub chain: bool, 272 | // if you have an ipv6 only proxy 273 | // this would force all DNS to go ipv6 274 | #[serde(default)] 275 | pub ipv6: bool, 276 | pub dns_argv: Option>, 277 | } 278 | 279 | #[derive(Serialize, Deserialize, Default, Clone)] 280 | pub struct NetnsParamCmd { 281 | pub program: String, 282 | pub argv: Vec, 283 | } 284 | 285 | #[derive(Serialize, Deserialize, Default, Clone)] 286 | pub struct NetnsInfo { 287 | pub base_name: String, // has no length limit 288 | pub subnet_veth: String, 289 | pub subnet6_veth: String, 290 | pub ip_vh: String, 291 | pub ip6_vh: String, 292 | pub ip_vn: String, 293 | pub ip6_vn: String, 294 | pub veth_base_name: String, // veth names have length limit 295 | pub id: u16, // unique 296 | } 297 | 298 | fn set_initgroups(user: &nix::unistd::User, gid: u32) { 299 | let gid = Gid::from_raw(gid); 300 | let s = user.name.clone(); 301 | let c_str = CString::new(s).unwrap(); 302 | match nix::unistd::initgroups(&c_str, gid) { 303 | std::result::Result::Ok(_) => log::debug!("Setting initgroups..."), 304 | Err(e) => { 305 | log::error!("Failed to set init groups: {:#?}", e); 306 | exit(1); 307 | } 308 | } 309 | } 310 | 311 | pub fn drop_privs(name: &str) -> Result<()> { 312 | log::trace!("drop privs, to {name}"); 313 | let log_user = users::get_user_by_name(name).unwrap(); 314 | let gi = Gid::from_raw(log_user.primary_group_id()); 315 | let ui = Uid::from_raw(log_user.uid()); 316 | log::trace!("GID to {gi}"); 317 | nix::unistd::setresgid(gi, gi, gi)?; 318 | log::trace!("change groups"); 319 | setgroups(&[gi])?; 320 | log::trace!("UID to {ui}"); 321 | nix::unistd::setresuid(ui, ui, ui)?; 322 | 323 | log::info!("dropped privs"); 324 | 325 | Ok(()) 326 | } 327 | 328 | pub fn drop_privs1(gi: Gid, ui: Uid) -> Result<()> { 329 | log::trace!("groups, {:?}", nix::unistd::getgroups()?); 330 | log::trace!("GID to {gi}"); 331 | nix::unistd::setresgid(gi, gi, gi)?; 332 | let user = nix::unistd::User::from_uid(ui).unwrap().unwrap(); 333 | set_initgroups(&user, gi.as_raw()); 334 | log::trace!("UID to {ui}"); 335 | nix::unistd::setresuid(ui, ui, ui)?; 336 | 337 | log::info!("dropped privs to resuid={ui} resgid={gi}"); 338 | 339 | Ok(()) 340 | } 341 | 342 | // fd will not have CLOEXEC 343 | pub fn nsfd(ns_name: &str) -> Result { 344 | use nix::fcntl::{open, OFlag}; 345 | use nix::sys::stat::Mode; 346 | let mut p = PathBuf::from(NETNS_PATH); 347 | p.push(ns_name); 348 | open(&p, OFlag::O_RDONLY, Mode::empty()).map_err(anyhow::Error::from) 349 | } 350 | 351 | // fd will not have CLOEXEC 352 | pub fn nsfd_by_path(p: &Path) -> Result { 353 | use nix::fcntl::{open, OFlag}; 354 | use nix::sys::stat::Mode; 355 | open(p, OFlag::O_RDONLY, Mode::empty()).map_err(anyhow::Error::from) 356 | } 357 | 358 | pub fn ns_exists(ns_name: &str) -> Result { 359 | let mut p = PathBuf::from(NETNS_PATH); 360 | p.push(ns_name); 361 | let r = p.try_exists().map_err(anyhow::Error::from)?; 362 | if r { 363 | anyhow::ensure!(p.is_file()); 364 | } 365 | Ok(r) 366 | // throws error if abnormality beyond exists-or-not appears 367 | } 368 | 369 | use futures::stream::TryStreamExt; 370 | use netlink_packet_route::{rtnl::link::LinkMessage, IFF_UP}; 371 | use rtnetlink::Handle; 372 | 373 | pub struct Configurer { 374 | handle: Handle, 375 | } 376 | 377 | use crate::nft; 378 | 379 | // in the root ns 380 | // creates a pair of veths, and moves one into netns 381 | // returns addrs 382 | // must be used without CLOEXEC 383 | pub async fn config_pre_enter_ns( 384 | neti: &NetnsInfo, 385 | configurer: &Configurer, 386 | fd: RawFd, 387 | ) -> Result<()> { 388 | configurer.add_veth_pair(&neti.veth_base_name).await?; 389 | // we always get a fresh pair of veths after that 390 | 391 | configurer 392 | .add_addr_dev( 393 | neti.ip_vh.parse()?, 394 | &veth_from_base(&neti.veth_base_name, true).as_ref(), 395 | ) 396 | .await?; 397 | configurer 398 | .add_addr_dev( 399 | neti.ip6_vh.parse()?, 400 | &veth_from_base(&neti.veth_base_name, true).as_ref(), 401 | ) 402 | .await?; 403 | // it will be set up after nftables gets confgiured 404 | // configurer 405 | // .set_up(&veth_from_base(&neti.veth_base_name, true)) 406 | // .await?; 407 | 408 | configurer 409 | .add_addr_dev( 410 | neti.ip_vn.parse()?, 411 | &veth_from_base(&neti.veth_base_name, false), 412 | ) 413 | .await?; 414 | configurer 415 | .add_addr_dev( 416 | neti.ip6_vn.parse()?, 417 | &veth_from_base(&neti.veth_base_name, false), 418 | ) 419 | .await?; 420 | configurer 421 | .set_up(&veth_from_base(&neti.veth_base_name, false)) 422 | .await?; 423 | 424 | // briefly enter the ns 425 | // have to use a process, or tokio will be messed up 426 | 427 | let mut path = std::env::current_exe()?; 428 | path.pop(); 429 | path.push("netnsp-sub"); 430 | 431 | let mut cmd: tokio::process::Child = tokio::process::Command::new(path.clone()) 432 | .arg(fd.to_string()) 433 | .arg(&neti.veth_base_name) 434 | .uid(0) 435 | .spawn() 436 | .unwrap(); 437 | cmd.wait().await?; 438 | 439 | // move a veth into ns 440 | configurer 441 | .ip_setns_by_fd(fd, &veth_from_base(&neti.veth_base_name, false)) 442 | .await?; 443 | 444 | log::trace!("ns {} configured", neti.base_name); 445 | nix::unistd::close(fd)?; 446 | Ok(()) 447 | } 448 | 449 | // one last step of the above fn 450 | pub async fn config_pre_enter_ns_up(neti: &NetnsInfo, configurer: &Configurer) -> Result<()> { 451 | configurer 452 | .set_up(&veth_from_base(&neti.veth_base_name, true)) 453 | .await?; 454 | 455 | Ok(()) 456 | } 457 | 458 | pub async fn config_in_ns(fd: RawFd, veth_base_name: String) -> Result<()> { 459 | enter_ns_by_fd(fd)?; 460 | let configurer = Configurer::new(); 461 | let rh = configurer 462 | .get_link(&veth_from_base(&veth_base_name, false)) 463 | .await; 464 | if rh.is_err() { 465 | // do nothing, and later netnsp-main will move a veth in 466 | } else { 467 | configurer 468 | .handle 469 | .link() 470 | .del(rh.unwrap().header.index) // the one in root ns 471 | .execute() 472 | .await 473 | .map_err(|e| anyhow!("removing {veth_base_name} veth in guest ns fails. {e}"))?; 474 | } 475 | Ok(()) 476 | } 477 | 478 | pub async fn config_network(configurer: &Configurer, state: &mut NetnspState) -> Result<()> { 479 | let ns_names: Vec = state.profile_names(); 480 | for ns in &ns_names { 481 | let netinfo_o; 482 | let netinfo; 483 | match state.res.netns_info.get(ns) { 484 | None => { 485 | netinfo_o = state.new_netinfo(ns.clone()).await?; 486 | state 487 | .res 488 | .netns_info 489 | .insert(ns.to_owned(), netinfo_o.clone()); 490 | netinfo = &netinfo_o 491 | } 492 | Some(n) => netinfo = n, 493 | } 494 | 495 | Configurer::add_netns(&ns).await?; 496 | let fd = nsfd(&ns)?; 497 | config_pre_enter_ns(&netinfo, configurer, fd.as_raw_fd()).await?; 498 | } 499 | state.res.root_inode = get_pid1_netns_inode().await?; 500 | state.dump().await?; 501 | state.apply_nft().await?; 502 | for ns in &ns_names { 503 | let info = state.res.netns_info.get(ns).unwrap(); 504 | config_pre_enter_ns_up(info, configurer).await?; 505 | } 506 | Ok(()) 507 | } 508 | 509 | pub async fn get_pid1_netns_inode() -> Result { 510 | use procfs::process::Process; 511 | let pid1_process = Process::new(1)?; 512 | let nslist = pid1_process.namespaces()?; 513 | let pid1_net_ns = nslist 514 | .get(&OsString::from("net")) 515 | .ok_or_else(|| anyhow::anyhow!("PID 1 net namespace not found"))?; 516 | 517 | Ok(pid1_net_ns.identifier) 518 | } 519 | 520 | pub fn get_self_netns_inode() -> Result { 521 | use procfs::process::Process; 522 | let selfproc = Process::myself()?; 523 | let nslist = selfproc.namespaces()?; 524 | let selfns = nslist.get(&OsString::from("net")); 525 | match selfns { 526 | None => anyhow::bail!("self net ns file missing"), 527 | Some(ns) => Ok(ns.identifier), 528 | } 529 | } 530 | 531 | pub fn get_self_netns() -> Result { 532 | use procfs::process::Process; 533 | let selfproc = Process::myself()?; 534 | let nslist = selfproc.namespaces()?; 535 | let selfns = nslist.get(&OsString::from("net")); 536 | match selfns { 537 | None => anyhow::bail!("self net ns file missing"), 538 | Some(ns) => { 539 | use netns_rs::get_from_path; 540 | let netns_ = get_from_path(&ns.path)?; 541 | Ok(netns_) 542 | } 543 | } 544 | } 545 | 546 | // None for non-persistent ns 547 | pub async fn self_netns_identify() -> Result> { 548 | use netns_rs::get_from_path; 549 | 550 | let selfns = get_self_netns()?; 551 | let path = Path::new(NETNS_PATH); 552 | for entry in path.read_dir()? { 553 | if let core::result::Result::Ok(entry) = entry { 554 | let ns = get_from_path(entry.path())?; 555 | if ns == selfns { 556 | // identified to be x netns 557 | // ==> there is a file under netns path, readable and matches proc netns 558 | return Ok(Some(( 559 | ns.path() 560 | .file_name() 561 | .ok_or_else(|| anyhow::anyhow!("OsStr"))? 562 | .to_string_lossy() 563 | .into_owned(), 564 | ns, 565 | ))); 566 | } 567 | } 568 | // some iter may fail and get ignored but that should be fine 569 | } 570 | Ok(None) // means, "no matches under NETNS_PATH" 571 | } 572 | 573 | use netns_rs::Env; 574 | 575 | pub struct NsEnv; 576 | 577 | impl Env for NsEnv { 578 | fn persist_dir(&self) -> PathBuf { 579 | NETNS_PATH.into() 580 | } 581 | } 582 | 583 | pub fn veth_from_base(basename: &str, host: bool) -> String { 584 | let basename = basename.to_owned(); 585 | if basename.len() > 12 { 586 | unreachable!() 587 | } 588 | if host { 589 | format!("{basename}_vh") 590 | } else { 591 | format!("{basename}_vn") 592 | } 593 | } 594 | 595 | impl Configurer { 596 | pub fn new() -> Self { 597 | use rtnetlink::new_connection; 598 | let (connection, handle, _) = new_connection().unwrap(); 599 | tokio::spawn(connection); 600 | 601 | Self { handle } 602 | } 603 | pub async fn get_link(&self, name: &str) -> Result { 604 | let mut links = self 605 | .handle 606 | .link() 607 | .get() 608 | .match_name(name.to_owned()) 609 | .execute(); 610 | if let Some(link) = links.try_next().await? { 611 | Ok(link) 612 | } else { 613 | Err(anyhow!("link message None")) 614 | } 615 | } 616 | pub async fn set_up(&self, name: &str) -> Result<()> { 617 | log::trace!("get link {} up", name); 618 | let mut links = self 619 | .handle 620 | .link() 621 | .get() 622 | .match_name(name.to_owned()) 623 | .execute(); 624 | if let Some(link) = links.try_next().await? { 625 | let is_up = link.header.flags & IFF_UP != 0; 626 | if is_up { 627 | Ok(()) 628 | } else { 629 | self.handle 630 | .link() 631 | .set(link.header.index) 632 | .up() 633 | .execute() 634 | .await 635 | .map_err(anyhow::Error::from) 636 | } 637 | } else { 638 | Err(anyhow!("link message None")) 639 | } 640 | } 641 | pub async fn add_veth_pair(&self, base_name: &str) -> Result { 642 | // netlink would error if name is too long 643 | let rh = self.get_link(&veth_from_base(base_name, true)).await; 644 | 645 | if rh.is_err() { 646 | // do nothing 647 | } else { 648 | // remove them 649 | self.handle 650 | .link() 651 | .del(rh.unwrap().header.index) // the one in root ns 652 | .execute() 653 | .await 654 | .map_err(|e| anyhow!("removing {base_name} veth in root ns fails. {e}"))?; 655 | // also the one in ns 656 | // but it will be done by other fn later 657 | }; 658 | let r1 = self 659 | .handle 660 | .link() 661 | .add() 662 | .veth( 663 | veth_from_base(base_name, true), 664 | veth_from_base(base_name, false), 665 | ) 666 | .execute() 667 | .await 668 | .map_err(|e| anyhow!("adding {base_name} veth pair fails. {e}")); 669 | return match r1 { 670 | Err(e) => { 671 | let rh = self.get_link(&veth_from_base(base_name, false)).await; 672 | if rh.is_ok() { 673 | log::warn!( 674 | "Are you running from a sub-netns. {} exists. Or, you killed netnsproxy process half-way", 675 | &veth_from_base(base_name, false) 676 | ); 677 | } 678 | Err(e) 679 | } 680 | _ => Ok(false), // veths dont exist, adding suceeded 681 | }; 682 | } 683 | pub async fn add_addr_dev(&self, addr: IpNetwork, dev: &str) -> Result<()> { 684 | let mut links = self 685 | .handle 686 | .link() 687 | .get() 688 | .match_name(dev.to_string()) 689 | .execute(); 690 | if let Some(link) = links.try_next().await? { 691 | let mut get_addr = self 692 | .handle 693 | .address() 694 | .get() 695 | .set_link_index_filter(link.header.index) 696 | .set_prefix_length_filter(addr.prefix()) 697 | .set_address_filter(addr.ip()) 698 | .execute(); 699 | if let Some(_addrmsg) = get_addr.try_next().await? { 700 | Ok(()) 701 | } else { 702 | // the desired IP has not been added 703 | self.handle 704 | .address() 705 | .add(link.header.index, addr.ip(), addr.prefix()) 706 | .execute() 707 | .await 708 | .map_err(anyhow::Error::from) 709 | } 710 | } else { 711 | Err(anyhow!("link message None")) 712 | } 713 | } 714 | 715 | pub async fn ip_setns_by_fd(&self, fd: RawFd, dev: &str) -> Result<()> { 716 | let mut links = self 717 | .handle 718 | .link() 719 | .get() 720 | .match_name(dev.to_owned()) 721 | .execute(); 722 | let linkmsg = links.try_next().await; 723 | match linkmsg { 724 | core::result::Result::Ok(Some(link)) => self 725 | .handle 726 | .link() 727 | .set(link.header.index) 728 | .setns_by_fd(fd) 729 | .execute() 730 | .await 731 | .map_err(anyhow::Error::from), 732 | _ => { 733 | // should be present in the netns 734 | // omit checks here. netns-sub should check them 735 | Ok(()) 736 | } 737 | } 738 | } 739 | 740 | pub async fn ip_setns(&self, ns_name: &str, dev: &str) -> Result<()> { 741 | let fd = nsfd(ns_name)?; 742 | let mut links = self 743 | .handle 744 | .link() 745 | .get() 746 | .match_name(dev.to_owned()) 747 | .execute(); 748 | let linkmsg = links.try_next().await; 749 | match linkmsg { 750 | core::result::Result::Ok(Some(link)) => self 751 | .handle 752 | .link() 753 | .set(link.header.index) 754 | .setns_by_fd(fd.as_raw_fd()) 755 | .execute() 756 | .await 757 | .map_err(anyhow::Error::from), 758 | _ => { 759 | // should be present in the netns 760 | // omit checks here. netns-sub should check them 761 | Ok(()) 762 | } 763 | } 764 | } 765 | 766 | pub async fn add_netns(ns_name: &str) -> Result<()> { 767 | use rtnetlink::NetworkNamespace; 768 | if ns_exists(ns_name)? { 769 | Ok(()) 770 | } else { 771 | NetworkNamespace::add(ns_name.to_string()) 772 | .await 773 | .map_err(anyhow::Error::from) 774 | } 775 | } 776 | 777 | // one of dst and v4 must be Some 778 | pub async fn ip_add_route( 779 | &self, 780 | dev: &str, 781 | dst: Option, 782 | v4: Option, 783 | ) -> Result<()> { 784 | let mut links = self 785 | .handle 786 | .link() 787 | .get() 788 | .match_name(dev.to_owned()) 789 | .execute(); 790 | if let Some(link) = links.try_next().await? { 791 | let index = link.header.index; 792 | let req = self.handle.route().add().output_interface(index); 793 | match dst { 794 | Some(IpNetwork::V4(ip)) => req 795 | .v4() 796 | .destination_prefix(ip.ip(), ip.prefix()) 797 | .execute() 798 | .await 799 | .map_err(anyhow::Error::from), 800 | Some(IpNetwork::V6(ip)) => req 801 | .v6() 802 | .destination_prefix(ip.ip(), ip.prefix()) 803 | .execute() 804 | .await 805 | .map_err(anyhow::Error::from), 806 | _ => { 807 | if v4.is_some() { 808 | if v4.unwrap() { 809 | req.v4().execute().await.map_err(anyhow::Error::from) 810 | } else { 811 | req.v6().execute().await.map_err(anyhow::Error::from) 812 | } 813 | } else { 814 | unreachable!() 815 | } 816 | } 817 | } 818 | } else { 819 | Err(anyhow!("link message None")) 820 | } 821 | } 822 | 823 | pub async fn add_addrs_guest(&self, base_name: &str, info: &NetnsInfo) -> Result<()> { 824 | log::trace!("add addrs in guest ns"); 825 | self.add_addr_dev( 826 | info.ip_vn.clone().parse()?, 827 | veth_from_base(&base_name, false).as_ref(), 828 | ) 829 | .await 830 | .ok(); 831 | self.add_addr_dev( 832 | info.ip6_vn.clone().parse()?, 833 | veth_from_base(&base_name, false).as_ref(), 834 | ) 835 | .await 836 | .ok(); 837 | 838 | Ok(()) 839 | } 840 | } 841 | 842 | nix::ioctl_write_int!(tunsetowner, 'T', 204); 843 | nix::ioctl_write_int!(tunsetpersist, 'T', 203); 844 | 845 | // prepare a TUN for tun2socks 846 | pub fn tun_ops(tun: tidy_tuntap::Tun) -> Result<()> { 847 | let fd = tun.as_raw_fd(); 848 | 849 | // as tested, the line below is needless. 850 | // unsafe { tunsetowner(fd, 1000)? }; 851 | unsafe { tunsetpersist(fd, 1)? }; // works if uncommented 852 | 853 | Ok(()) 854 | } 855 | 856 | pub async fn watch_log( 857 | mut reader: tokio::io::Lines>, 858 | tx: Option>, 859 | pre: String, 860 | ) -> Result<()> { 861 | if let Some(line) = reader.next_line().await? { 862 | if tx.is_some() { 863 | tx.unwrap().send(true).unwrap(); 864 | } 865 | log::debug!("{pre} {}", line); 866 | while let Some(line) = reader.next_line().await? { 867 | log::trace!("{pre} {}", line); 868 | } 869 | } 870 | Ok(()) 871 | } 872 | 873 | pub async fn watch_both( 874 | chil: &mut tokio::process::Child, 875 | pre: String, 876 | tx: Option>, 877 | ) -> Result<()> { 878 | let stdout = chil.stdout.take().unwrap(); 879 | let stderr = chil.stderr.take().unwrap(); 880 | let reader = tokio::io::BufReader::new(stdout).lines(); 881 | let reader2 = tokio::io::BufReader::new(stderr).lines(); 882 | tokio::spawn(watch_log(reader, tx, pre.clone())); 883 | tokio::spawn(watch_log(reader2, None, pre)); 884 | 885 | Ok(()) 886 | } 887 | 888 | pub async fn enter_ns_by_name(ns_name: &str) -> Result<()> { 889 | let fd = nsfd(ns_name)?; 890 | nix::sched::setns(fd, CloneFlags::CLONE_NEWNET)?; 891 | nix::unistd::close(fd)?; 892 | let got_ns = self_netns_identify().await?.ok_or_else(|| { 893 | anyhow::anyhow!("failed to identify netns. no matches under the given netns directory") 894 | })?; 895 | 896 | anyhow::ensure!(got_ns.0 == ns_name); 897 | log::info!("current ns {} (named and persistent)", got_ns.0); 898 | 899 | Ok(()) 900 | } 901 | 902 | pub fn enter_ns_by_fd(ns_fd: RawFd) -> Result<()> { 903 | nix::sched::setns(ns_fd, CloneFlags::CLONE_NEWNET)?; 904 | let stat = nix::sys::stat::fstat(ns_fd)?; 905 | let selfi = get_self_netns_inode()?; 906 | anyhow::ensure!(stat.st_ino == selfi); 907 | Ok(()) 908 | } 909 | 910 | // ensure that the ns does not match self ns 911 | pub fn ensure_ns_not_root(ns_fd: RawFd) -> Result<()> { 912 | let stat = nix::sys::stat::fstat(ns_fd)?; 913 | let selfi = get_self_netns_inode()?; 914 | anyhow::ensure!(stat.st_ino != selfi); 915 | Ok(()) 916 | } 917 | 918 | pub fn enter_ns_by_pid(pi: i32) -> Result<()> { 919 | let process = procfs::process::Process::new(pi)?; 920 | let o: OsString = OsString::from("net"); 921 | let nss = process.namespaces()?; 922 | let proc_ns = nss 923 | .get(&o) 924 | .ok_or(anyhow!("ns/net not found for given pid"))?; 925 | let r = std::fs::File::open(&proc_ns.path)?; 926 | nix::sched::setns(r.as_raw_fd(), CloneFlags::CLONE_NEWNET)?; 927 | let self_inode = get_self_netns_inode()?; 928 | anyhow::ensure!(proc_ns.identifier == self_inode); 929 | log::info!("current ns is from pid {}", pi); 930 | Ok(()) 931 | } 932 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.19.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "1.0.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 | 20 | [[package]] 21 | name = "aho-corasick" 22 | version = "1.0.2" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" 25 | dependencies = [ 26 | "memchr", 27 | ] 28 | 29 | [[package]] 30 | name = "android-tzdata" 31 | version = "0.1.1" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 34 | 35 | [[package]] 36 | name = "android_system_properties" 37 | version = "0.1.5" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 40 | dependencies = [ 41 | "libc", 42 | ] 43 | 44 | [[package]] 45 | name = "ansi_term" 46 | version = "0.12.1" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" 49 | dependencies = [ 50 | "winapi", 51 | ] 52 | 53 | [[package]] 54 | name = "anstream" 55 | version = "0.3.2" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" 58 | dependencies = [ 59 | "anstyle", 60 | "anstyle-parse", 61 | "anstyle-query", 62 | "anstyle-wincon", 63 | "colorchoice", 64 | "is-terminal", 65 | "utf8parse", 66 | ] 67 | 68 | [[package]] 69 | name = "anstyle" 70 | version = "1.0.1" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" 73 | 74 | [[package]] 75 | name = "anstyle-parse" 76 | version = "0.2.1" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" 79 | dependencies = [ 80 | "utf8parse", 81 | ] 82 | 83 | [[package]] 84 | name = "anstyle-query" 85 | version = "1.0.0" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" 88 | dependencies = [ 89 | "windows-sys 0.48.0", 90 | ] 91 | 92 | [[package]] 93 | name = "anstyle-wincon" 94 | version = "1.0.1" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" 97 | dependencies = [ 98 | "anstyle", 99 | "windows-sys 0.48.0", 100 | ] 101 | 102 | [[package]] 103 | name = "anyhow" 104 | version = "1.0.71" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" 107 | dependencies = [ 108 | "backtrace", 109 | ] 110 | 111 | [[package]] 112 | name = "async-broadcast" 113 | version = "0.5.1" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | checksum = "7c48ccdbf6ca6b121e0f586cbc0e73ae440e56c67c30fa0873b4e110d9c26d2b" 116 | dependencies = [ 117 | "event-listener", 118 | "futures-core", 119 | ] 120 | 121 | [[package]] 122 | name = "async-channel" 123 | version = "1.8.0" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833" 126 | dependencies = [ 127 | "concurrent-queue", 128 | "event-listener", 129 | "futures-core", 130 | ] 131 | 132 | [[package]] 133 | name = "async-executor" 134 | version = "1.5.1" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb" 137 | dependencies = [ 138 | "async-lock", 139 | "async-task", 140 | "concurrent-queue", 141 | "fastrand", 142 | "futures-lite", 143 | "slab", 144 | ] 145 | 146 | [[package]] 147 | name = "async-fs" 148 | version = "1.6.0" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" 151 | dependencies = [ 152 | "async-lock", 153 | "autocfg", 154 | "blocking", 155 | "futures-lite", 156 | ] 157 | 158 | [[package]] 159 | name = "async-io" 160 | version = "1.13.0" 161 | source = "registry+https://github.com/rust-lang/crates.io-index" 162 | checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" 163 | dependencies = [ 164 | "async-lock", 165 | "autocfg", 166 | "cfg-if 1.0.0", 167 | "concurrent-queue", 168 | "futures-lite", 169 | "log", 170 | "parking", 171 | "polling", 172 | "rustix 0.37.20", 173 | "slab", 174 | "socket2", 175 | "waker-fn", 176 | ] 177 | 178 | [[package]] 179 | name = "async-lock" 180 | version = "2.7.0" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7" 183 | dependencies = [ 184 | "event-listener", 185 | ] 186 | 187 | [[package]] 188 | name = "async-process" 189 | version = "1.7.0" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | checksum = "7a9d28b1d97e08915212e2e45310d47854eafa69600756fc735fb788f75199c9" 192 | dependencies = [ 193 | "async-io", 194 | "async-lock", 195 | "autocfg", 196 | "blocking", 197 | "cfg-if 1.0.0", 198 | "event-listener", 199 | "futures-lite", 200 | "rustix 0.37.20", 201 | "signal-hook", 202 | "windows-sys 0.48.0", 203 | ] 204 | 205 | [[package]] 206 | name = "async-recursion" 207 | version = "1.0.4" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba" 210 | dependencies = [ 211 | "proc-macro2", 212 | "quote", 213 | "syn 2.0.18", 214 | ] 215 | 216 | [[package]] 217 | name = "async-task" 218 | version = "4.4.0" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" 221 | 222 | [[package]] 223 | name = "async-trait" 224 | version = "0.1.68" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" 227 | dependencies = [ 228 | "proc-macro2", 229 | "quote", 230 | "syn 2.0.18", 231 | ] 232 | 233 | [[package]] 234 | name = "atomic-waker" 235 | version = "1.1.1" 236 | source = "registry+https://github.com/rust-lang/crates.io-index" 237 | checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" 238 | 239 | [[package]] 240 | name = "atty" 241 | version = "0.2.14" 242 | source = "registry+https://github.com/rust-lang/crates.io-index" 243 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 244 | dependencies = [ 245 | "hermit-abi 0.1.19", 246 | "libc", 247 | "winapi", 248 | ] 249 | 250 | [[package]] 251 | name = "autocfg" 252 | version = "1.1.0" 253 | source = "registry+https://github.com/rust-lang/crates.io-index" 254 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 255 | 256 | [[package]] 257 | name = "backtrace" 258 | version = "0.3.67" 259 | source = "registry+https://github.com/rust-lang/crates.io-index" 260 | checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" 261 | dependencies = [ 262 | "addr2line", 263 | "cc", 264 | "cfg-if 1.0.0", 265 | "libc", 266 | "miniz_oxide 0.6.2", 267 | "object", 268 | "rustc-demangle", 269 | ] 270 | 271 | [[package]] 272 | name = "bindgen" 273 | version = "0.53.3" 274 | source = "registry+https://github.com/rust-lang/crates.io-index" 275 | checksum = "c72a978d268b1d70b0e963217e60fdabd9523a941457a6c42a7315d15c7e89e5" 276 | dependencies = [ 277 | "bitflags", 278 | "cexpr", 279 | "cfg-if 0.1.10", 280 | "clang-sys", 281 | "clap 2.34.0", 282 | "env_logger", 283 | "lazy_static", 284 | "lazycell", 285 | "log", 286 | "peeking_take_while", 287 | "proc-macro2", 288 | "quote", 289 | "regex", 290 | "rustc-hash", 291 | "shlex", 292 | "which", 293 | ] 294 | 295 | [[package]] 296 | name = "bitflags" 297 | version = "1.3.2" 298 | source = "registry+https://github.com/rust-lang/crates.io-index" 299 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 300 | 301 | [[package]] 302 | name = "block-buffer" 303 | version = "0.10.4" 304 | source = "registry+https://github.com/rust-lang/crates.io-index" 305 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 306 | dependencies = [ 307 | "generic-array", 308 | ] 309 | 310 | [[package]] 311 | name = "blocking" 312 | version = "1.3.1" 313 | source = "registry+https://github.com/rust-lang/crates.io-index" 314 | checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" 315 | dependencies = [ 316 | "async-channel", 317 | "async-lock", 318 | "async-task", 319 | "atomic-waker", 320 | "fastrand", 321 | "futures-lite", 322 | "log", 323 | ] 324 | 325 | [[package]] 326 | name = "bumpalo" 327 | version = "3.13.0" 328 | source = "registry+https://github.com/rust-lang/crates.io-index" 329 | checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" 330 | 331 | [[package]] 332 | name = "byteorder" 333 | version = "1.4.3" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 336 | 337 | [[package]] 338 | name = "bytes" 339 | version = "1.4.0" 340 | source = "registry+https://github.com/rust-lang/crates.io-index" 341 | checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" 342 | 343 | [[package]] 344 | name = "cc" 345 | version = "1.0.79" 346 | source = "registry+https://github.com/rust-lang/crates.io-index" 347 | checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" 348 | 349 | [[package]] 350 | name = "cexpr" 351 | version = "0.4.0" 352 | source = "registry+https://github.com/rust-lang/crates.io-index" 353 | checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27" 354 | dependencies = [ 355 | "nom", 356 | ] 357 | 358 | [[package]] 359 | name = "cfg-if" 360 | version = "0.1.10" 361 | source = "registry+https://github.com/rust-lang/crates.io-index" 362 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 363 | 364 | [[package]] 365 | name = "cfg-if" 366 | version = "1.0.0" 367 | source = "registry+https://github.com/rust-lang/crates.io-index" 368 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 369 | 370 | [[package]] 371 | name = "chrono" 372 | version = "0.4.26" 373 | source = "registry+https://github.com/rust-lang/crates.io-index" 374 | checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" 375 | dependencies = [ 376 | "android-tzdata", 377 | "iana-time-zone", 378 | "num-traits", 379 | "winapi", 380 | ] 381 | 382 | [[package]] 383 | name = "clang-sys" 384 | version = "0.29.3" 385 | source = "registry+https://github.com/rust-lang/crates.io-index" 386 | checksum = "fe6837df1d5cba2397b835c8530f51723267e16abbf83892e9e5af4f0e5dd10a" 387 | dependencies = [ 388 | "glob", 389 | "libc", 390 | "libloading", 391 | ] 392 | 393 | [[package]] 394 | name = "clap" 395 | version = "2.34.0" 396 | source = "registry+https://github.com/rust-lang/crates.io-index" 397 | checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" 398 | dependencies = [ 399 | "ansi_term", 400 | "atty", 401 | "bitflags", 402 | "strsim 0.8.0", 403 | "textwrap", 404 | "unicode-width", 405 | "vec_map", 406 | ] 407 | 408 | [[package]] 409 | name = "clap" 410 | version = "4.3.5" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "2686c4115cb0810d9a984776e197823d08ec94f176549a89a9efded477c456dc" 413 | dependencies = [ 414 | "clap_builder", 415 | "clap_derive", 416 | "once_cell", 417 | ] 418 | 419 | [[package]] 420 | name = "clap_builder" 421 | version = "4.3.5" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | checksum = "2e53afce1efce6ed1f633cf0e57612fe51db54a1ee4fd8f8503d078fe02d69ae" 424 | dependencies = [ 425 | "anstream", 426 | "anstyle", 427 | "bitflags", 428 | "clap_lex", 429 | "strsim 0.10.0", 430 | ] 431 | 432 | [[package]] 433 | name = "clap_derive" 434 | version = "4.3.2" 435 | source = "registry+https://github.com/rust-lang/crates.io-index" 436 | checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" 437 | dependencies = [ 438 | "heck", 439 | "proc-macro2", 440 | "quote", 441 | "syn 2.0.18", 442 | ] 443 | 444 | [[package]] 445 | name = "clap_lex" 446 | version = "0.5.0" 447 | source = "registry+https://github.com/rust-lang/crates.io-index" 448 | checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" 449 | 450 | [[package]] 451 | name = "colorchoice" 452 | version = "1.0.0" 453 | source = "registry+https://github.com/rust-lang/crates.io-index" 454 | checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" 455 | 456 | [[package]] 457 | name = "concurrent-queue" 458 | version = "2.2.0" 459 | source = "registry+https://github.com/rust-lang/crates.io-index" 460 | checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" 461 | dependencies = [ 462 | "crossbeam-utils", 463 | ] 464 | 465 | [[package]] 466 | name = "const-random" 467 | version = "0.1.15" 468 | source = "registry+https://github.com/rust-lang/crates.io-index" 469 | checksum = "368a7a772ead6ce7e1de82bfb04c485f3db8ec744f72925af5735e29a22cc18e" 470 | dependencies = [ 471 | "const-random-macro", 472 | "proc-macro-hack", 473 | ] 474 | 475 | [[package]] 476 | name = "const-random-macro" 477 | version = "0.1.15" 478 | source = "registry+https://github.com/rust-lang/crates.io-index" 479 | checksum = "9d7d6ab3c3a2282db210df5f02c4dab6e0a7057af0fb7ebd4070f30fe05c0ddb" 480 | dependencies = [ 481 | "getrandom", 482 | "once_cell", 483 | "proc-macro-hack", 484 | "tiny-keccak", 485 | ] 486 | 487 | [[package]] 488 | name = "core-foundation-sys" 489 | version = "0.8.4" 490 | source = "registry+https://github.com/rust-lang/crates.io-index" 491 | checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" 492 | 493 | [[package]] 494 | name = "cpufeatures" 495 | version = "0.2.8" 496 | source = "registry+https://github.com/rust-lang/crates.io-index" 497 | checksum = "03e69e28e9f7f77debdedbaafa2866e1de9ba56df55a8bd7cfc724c25a09987c" 498 | dependencies = [ 499 | "libc", 500 | ] 501 | 502 | [[package]] 503 | name = "crc32fast" 504 | version = "1.3.2" 505 | source = "registry+https://github.com/rust-lang/crates.io-index" 506 | checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" 507 | dependencies = [ 508 | "cfg-if 1.0.0", 509 | ] 510 | 511 | [[package]] 512 | name = "crossbeam-channel" 513 | version = "0.5.8" 514 | source = "registry+https://github.com/rust-lang/crates.io-index" 515 | checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" 516 | dependencies = [ 517 | "cfg-if 1.0.0", 518 | "crossbeam-utils", 519 | ] 520 | 521 | [[package]] 522 | name = "crossbeam-deque" 523 | version = "0.8.3" 524 | source = "registry+https://github.com/rust-lang/crates.io-index" 525 | checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" 526 | dependencies = [ 527 | "cfg-if 1.0.0", 528 | "crossbeam-epoch", 529 | "crossbeam-utils", 530 | ] 531 | 532 | [[package]] 533 | name = "crossbeam-epoch" 534 | version = "0.9.15" 535 | source = "registry+https://github.com/rust-lang/crates.io-index" 536 | checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" 537 | dependencies = [ 538 | "autocfg", 539 | "cfg-if 1.0.0", 540 | "crossbeam-utils", 541 | "memoffset 0.9.0", 542 | "scopeguard", 543 | ] 544 | 545 | [[package]] 546 | name = "crossbeam-utils" 547 | version = "0.8.16" 548 | source = "registry+https://github.com/rust-lang/crates.io-index" 549 | checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" 550 | dependencies = [ 551 | "cfg-if 1.0.0", 552 | ] 553 | 554 | [[package]] 555 | name = "crunchy" 556 | version = "0.2.2" 557 | source = "registry+https://github.com/rust-lang/crates.io-index" 558 | checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" 559 | 560 | [[package]] 561 | name = "crypto-common" 562 | version = "0.1.6" 563 | source = "registry+https://github.com/rust-lang/crates.io-index" 564 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 565 | dependencies = [ 566 | "generic-array", 567 | "typenum", 568 | ] 569 | 570 | [[package]] 571 | name = "derivative" 572 | version = "2.2.0" 573 | source = "registry+https://github.com/rust-lang/crates.io-index" 574 | checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" 575 | dependencies = [ 576 | "proc-macro2", 577 | "quote", 578 | "syn 1.0.109", 579 | ] 580 | 581 | [[package]] 582 | name = "digest" 583 | version = "0.10.7" 584 | source = "registry+https://github.com/rust-lang/crates.io-index" 585 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 586 | dependencies = [ 587 | "block-buffer", 588 | "crypto-common", 589 | ] 590 | 591 | [[package]] 592 | name = "dlv-list" 593 | version = "0.5.0" 594 | source = "registry+https://github.com/rust-lang/crates.io-index" 595 | checksum = "d529fd73d344663edfd598ccb3f344e46034db51ebd103518eae34338248ad73" 596 | dependencies = [ 597 | "const-random", 598 | ] 599 | 600 | [[package]] 601 | name = "either" 602 | version = "1.8.1" 603 | source = "registry+https://github.com/rust-lang/crates.io-index" 604 | checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" 605 | 606 | [[package]] 607 | name = "enumflags2" 608 | version = "0.7.7" 609 | source = "registry+https://github.com/rust-lang/crates.io-index" 610 | checksum = "c041f5090df68b32bcd905365fd51769c8b9d553fe87fde0b683534f10c01bd2" 611 | dependencies = [ 612 | "enumflags2_derive", 613 | "serde", 614 | ] 615 | 616 | [[package]] 617 | name = "enumflags2_derive" 618 | version = "0.7.7" 619 | source = "registry+https://github.com/rust-lang/crates.io-index" 620 | checksum = "5e9a1f9f7d83e59740248a6e14ecf93929ade55027844dfcea78beafccc15745" 621 | dependencies = [ 622 | "proc-macro2", 623 | "quote", 624 | "syn 2.0.18", 625 | ] 626 | 627 | [[package]] 628 | name = "env_logger" 629 | version = "0.7.1" 630 | source = "registry+https://github.com/rust-lang/crates.io-index" 631 | checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" 632 | dependencies = [ 633 | "atty", 634 | "humantime", 635 | "log", 636 | "regex", 637 | "termcolor", 638 | ] 639 | 640 | [[package]] 641 | name = "errno" 642 | version = "0.3.1" 643 | source = "registry+https://github.com/rust-lang/crates.io-index" 644 | checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" 645 | dependencies = [ 646 | "errno-dragonfly", 647 | "libc", 648 | "windows-sys 0.48.0", 649 | ] 650 | 651 | [[package]] 652 | name = "errno-dragonfly" 653 | version = "0.1.2" 654 | source = "registry+https://github.com/rust-lang/crates.io-index" 655 | checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" 656 | dependencies = [ 657 | "cc", 658 | "libc", 659 | ] 660 | 661 | [[package]] 662 | name = "event-listener" 663 | version = "2.5.3" 664 | source = "registry+https://github.com/rust-lang/crates.io-index" 665 | checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" 666 | 667 | [[package]] 668 | name = "fastrand" 669 | version = "1.9.0" 670 | source = "registry+https://github.com/rust-lang/crates.io-index" 671 | checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" 672 | dependencies = [ 673 | "instant", 674 | ] 675 | 676 | [[package]] 677 | name = "fd-reactor" 678 | version = "0.1.0" 679 | source = "registry+https://github.com/rust-lang/crates.io-index" 680 | checksum = "9a030235bc430f977b66e18f365efea552caaee650981ed2c4490c2892b048f8" 681 | dependencies = [ 682 | "bitflags", 683 | "libc", 684 | "once_cell", 685 | ] 686 | 687 | [[package]] 688 | name = "flate2" 689 | version = "1.0.26" 690 | source = "registry+https://github.com/rust-lang/crates.io-index" 691 | checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" 692 | dependencies = [ 693 | "crc32fast", 694 | "miniz_oxide 0.7.1", 695 | ] 696 | 697 | [[package]] 698 | name = "flexi_logger" 699 | version = "0.25.5" 700 | source = "registry+https://github.com/rust-lang/crates.io-index" 701 | checksum = "37e7b68b1f7ce9c62856598e99cd6742b9cedb6186b47aa989a82640f20bfa9b" 702 | dependencies = [ 703 | "chrono", 704 | "glob", 705 | "is-terminal", 706 | "lazy_static", 707 | "log", 708 | "nu-ansi-term", 709 | "regex", 710 | "thiserror", 711 | ] 712 | 713 | [[package]] 714 | name = "futures" 715 | version = "0.3.28" 716 | source = "registry+https://github.com/rust-lang/crates.io-index" 717 | checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" 718 | dependencies = [ 719 | "futures-channel", 720 | "futures-core", 721 | "futures-executor", 722 | "futures-io", 723 | "futures-sink", 724 | "futures-task", 725 | "futures-util", 726 | ] 727 | 728 | [[package]] 729 | name = "futures-channel" 730 | version = "0.3.28" 731 | source = "registry+https://github.com/rust-lang/crates.io-index" 732 | checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" 733 | dependencies = [ 734 | "futures-core", 735 | "futures-sink", 736 | ] 737 | 738 | [[package]] 739 | name = "futures-core" 740 | version = "0.3.28" 741 | source = "registry+https://github.com/rust-lang/crates.io-index" 742 | checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" 743 | 744 | [[package]] 745 | name = "futures-executor" 746 | version = "0.3.28" 747 | source = "registry+https://github.com/rust-lang/crates.io-index" 748 | checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" 749 | dependencies = [ 750 | "futures-core", 751 | "futures-task", 752 | "futures-util", 753 | ] 754 | 755 | [[package]] 756 | name = "futures-io" 757 | version = "0.3.28" 758 | source = "registry+https://github.com/rust-lang/crates.io-index" 759 | checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" 760 | 761 | [[package]] 762 | name = "futures-lite" 763 | version = "1.13.0" 764 | source = "registry+https://github.com/rust-lang/crates.io-index" 765 | checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" 766 | dependencies = [ 767 | "fastrand", 768 | "futures-core", 769 | "futures-io", 770 | "memchr", 771 | "parking", 772 | "pin-project-lite", 773 | "waker-fn", 774 | ] 775 | 776 | [[package]] 777 | name = "futures-macro" 778 | version = "0.3.28" 779 | source = "registry+https://github.com/rust-lang/crates.io-index" 780 | checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" 781 | dependencies = [ 782 | "proc-macro2", 783 | "quote", 784 | "syn 2.0.18", 785 | ] 786 | 787 | [[package]] 788 | name = "futures-sink" 789 | version = "0.3.28" 790 | source = "registry+https://github.com/rust-lang/crates.io-index" 791 | checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" 792 | 793 | [[package]] 794 | name = "futures-task" 795 | version = "0.3.28" 796 | source = "registry+https://github.com/rust-lang/crates.io-index" 797 | checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" 798 | 799 | [[package]] 800 | name = "futures-util" 801 | version = "0.3.28" 802 | source = "registry+https://github.com/rust-lang/crates.io-index" 803 | checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" 804 | dependencies = [ 805 | "futures-channel", 806 | "futures-core", 807 | "futures-io", 808 | "futures-macro", 809 | "futures-sink", 810 | "futures-task", 811 | "memchr", 812 | "pin-project-lite", 813 | "pin-utils", 814 | "slab", 815 | ] 816 | 817 | [[package]] 818 | name = "generic-array" 819 | version = "0.14.7" 820 | source = "registry+https://github.com/rust-lang/crates.io-index" 821 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 822 | dependencies = [ 823 | "typenum", 824 | "version_check", 825 | ] 826 | 827 | [[package]] 828 | name = "getrandom" 829 | version = "0.2.10" 830 | source = "registry+https://github.com/rust-lang/crates.io-index" 831 | checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" 832 | dependencies = [ 833 | "cfg-if 1.0.0", 834 | "libc", 835 | "wasi", 836 | ] 837 | 838 | [[package]] 839 | name = "gimli" 840 | version = "0.27.3" 841 | source = "registry+https://github.com/rust-lang/crates.io-index" 842 | checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" 843 | 844 | [[package]] 845 | name = "glob" 846 | version = "0.3.1" 847 | source = "registry+https://github.com/rust-lang/crates.io-index" 848 | checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" 849 | 850 | [[package]] 851 | name = "hashbrown" 852 | version = "0.12.3" 853 | source = "registry+https://github.com/rust-lang/crates.io-index" 854 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 855 | 856 | [[package]] 857 | name = "hashbrown" 858 | version = "0.13.2" 859 | source = "registry+https://github.com/rust-lang/crates.io-index" 860 | checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" 861 | 862 | [[package]] 863 | name = "heck" 864 | version = "0.4.1" 865 | source = "registry+https://github.com/rust-lang/crates.io-index" 866 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 867 | 868 | [[package]] 869 | name = "hermit-abi" 870 | version = "0.1.19" 871 | source = "registry+https://github.com/rust-lang/crates.io-index" 872 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 873 | dependencies = [ 874 | "libc", 875 | ] 876 | 877 | [[package]] 878 | name = "hermit-abi" 879 | version = "0.2.6" 880 | source = "registry+https://github.com/rust-lang/crates.io-index" 881 | checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" 882 | dependencies = [ 883 | "libc", 884 | ] 885 | 886 | [[package]] 887 | name = "hermit-abi" 888 | version = "0.3.1" 889 | source = "registry+https://github.com/rust-lang/crates.io-index" 890 | checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" 891 | 892 | [[package]] 893 | name = "hex" 894 | version = "0.4.3" 895 | source = "registry+https://github.com/rust-lang/crates.io-index" 896 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 897 | 898 | [[package]] 899 | name = "home" 900 | version = "0.5.5" 901 | source = "registry+https://github.com/rust-lang/crates.io-index" 902 | checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" 903 | dependencies = [ 904 | "windows-sys 0.48.0", 905 | ] 906 | 907 | [[package]] 908 | name = "humantime" 909 | version = "1.3.0" 910 | source = "registry+https://github.com/rust-lang/crates.io-index" 911 | checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" 912 | dependencies = [ 913 | "quick-error", 914 | ] 915 | 916 | [[package]] 917 | name = "iana-time-zone" 918 | version = "0.1.57" 919 | source = "registry+https://github.com/rust-lang/crates.io-index" 920 | checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" 921 | dependencies = [ 922 | "android_system_properties", 923 | "core-foundation-sys", 924 | "iana-time-zone-haiku", 925 | "js-sys", 926 | "wasm-bindgen", 927 | "windows", 928 | ] 929 | 930 | [[package]] 931 | name = "iana-time-zone-haiku" 932 | version = "0.1.2" 933 | source = "registry+https://github.com/rust-lang/crates.io-index" 934 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 935 | dependencies = [ 936 | "cc", 937 | ] 938 | 939 | [[package]] 940 | name = "indexmap" 941 | version = "1.9.3" 942 | source = "registry+https://github.com/rust-lang/crates.io-index" 943 | checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" 944 | dependencies = [ 945 | "autocfg", 946 | "hashbrown 0.12.3", 947 | ] 948 | 949 | [[package]] 950 | name = "inotify" 951 | version = "0.10.1" 952 | source = "registry+https://github.com/rust-lang/crates.io-index" 953 | checksum = "ff335215fb898bf09c45833b657233d8c0b699a616d7dd64d0513080da270ab6" 954 | dependencies = [ 955 | "bitflags", 956 | "futures-core", 957 | "inotify-sys", 958 | "libc", 959 | "tokio", 960 | ] 961 | 962 | [[package]] 963 | name = "inotify-sys" 964 | version = "0.1.5" 965 | source = "registry+https://github.com/rust-lang/crates.io-index" 966 | checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" 967 | dependencies = [ 968 | "libc", 969 | ] 970 | 971 | [[package]] 972 | name = "instant" 973 | version = "0.1.12" 974 | source = "registry+https://github.com/rust-lang/crates.io-index" 975 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 976 | dependencies = [ 977 | "cfg-if 1.0.0", 978 | ] 979 | 980 | [[package]] 981 | name = "io-lifetimes" 982 | version = "1.0.11" 983 | source = "registry+https://github.com/rust-lang/crates.io-index" 984 | checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" 985 | dependencies = [ 986 | "hermit-abi 0.3.1", 987 | "libc", 988 | "windows-sys 0.48.0", 989 | ] 990 | 991 | [[package]] 992 | name = "ipnetwork" 993 | version = "0.20.0" 994 | source = "registry+https://github.com/rust-lang/crates.io-index" 995 | checksum = "bf466541e9d546596ee94f9f69590f89473455f88372423e0008fc1a7daf100e" 996 | dependencies = [ 997 | "serde", 998 | ] 999 | 1000 | [[package]] 1001 | name = "is-terminal" 1002 | version = "0.4.7" 1003 | source = "registry+https://github.com/rust-lang/crates.io-index" 1004 | checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" 1005 | dependencies = [ 1006 | "hermit-abi 0.3.1", 1007 | "io-lifetimes", 1008 | "rustix 0.37.20", 1009 | "windows-sys 0.48.0", 1010 | ] 1011 | 1012 | [[package]] 1013 | name = "itoa" 1014 | version = "1.0.6" 1015 | source = "registry+https://github.com/rust-lang/crates.io-index" 1016 | checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" 1017 | 1018 | [[package]] 1019 | name = "js-sys" 1020 | version = "0.3.64" 1021 | source = "registry+https://github.com/rust-lang/crates.io-index" 1022 | checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" 1023 | dependencies = [ 1024 | "wasm-bindgen", 1025 | ] 1026 | 1027 | [[package]] 1028 | name = "lazy_static" 1029 | version = "1.4.0" 1030 | source = "registry+https://github.com/rust-lang/crates.io-index" 1031 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 1032 | 1033 | [[package]] 1034 | name = "lazycell" 1035 | version = "1.3.0" 1036 | source = "registry+https://github.com/rust-lang/crates.io-index" 1037 | checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" 1038 | 1039 | [[package]] 1040 | name = "libc" 1041 | version = "0.2.146" 1042 | source = "registry+https://github.com/rust-lang/crates.io-index" 1043 | checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" 1044 | 1045 | [[package]] 1046 | name = "libloading" 1047 | version = "0.5.2" 1048 | source = "registry+https://github.com/rust-lang/crates.io-index" 1049 | checksum = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" 1050 | dependencies = [ 1051 | "cc", 1052 | "winapi", 1053 | ] 1054 | 1055 | [[package]] 1056 | name = "linux-raw-sys" 1057 | version = "0.1.4" 1058 | source = "registry+https://github.com/rust-lang/crates.io-index" 1059 | checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" 1060 | 1061 | [[package]] 1062 | name = "linux-raw-sys" 1063 | version = "0.3.8" 1064 | source = "registry+https://github.com/rust-lang/crates.io-index" 1065 | checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" 1066 | 1067 | [[package]] 1068 | name = "lock_api" 1069 | version = "0.4.10" 1070 | source = "registry+https://github.com/rust-lang/crates.io-index" 1071 | checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" 1072 | dependencies = [ 1073 | "autocfg", 1074 | "scopeguard", 1075 | ] 1076 | 1077 | [[package]] 1078 | name = "log" 1079 | version = "0.4.19" 1080 | source = "registry+https://github.com/rust-lang/crates.io-index" 1081 | checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" 1082 | 1083 | [[package]] 1084 | name = "memchr" 1085 | version = "2.5.0" 1086 | source = "registry+https://github.com/rust-lang/crates.io-index" 1087 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 1088 | 1089 | [[package]] 1090 | name = "memoffset" 1091 | version = "0.6.5" 1092 | source = "registry+https://github.com/rust-lang/crates.io-index" 1093 | checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" 1094 | dependencies = [ 1095 | "autocfg", 1096 | ] 1097 | 1098 | [[package]] 1099 | name = "memoffset" 1100 | version = "0.7.1" 1101 | source = "registry+https://github.com/rust-lang/crates.io-index" 1102 | checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" 1103 | dependencies = [ 1104 | "autocfg", 1105 | ] 1106 | 1107 | [[package]] 1108 | name = "memoffset" 1109 | version = "0.9.0" 1110 | source = "registry+https://github.com/rust-lang/crates.io-index" 1111 | checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" 1112 | dependencies = [ 1113 | "autocfg", 1114 | ] 1115 | 1116 | [[package]] 1117 | name = "miniz_oxide" 1118 | version = "0.6.2" 1119 | source = "registry+https://github.com/rust-lang/crates.io-index" 1120 | checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" 1121 | dependencies = [ 1122 | "adler", 1123 | ] 1124 | 1125 | [[package]] 1126 | name = "miniz_oxide" 1127 | version = "0.7.1" 1128 | source = "registry+https://github.com/rust-lang/crates.io-index" 1129 | checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" 1130 | dependencies = [ 1131 | "adler", 1132 | ] 1133 | 1134 | [[package]] 1135 | name = "mio" 1136 | version = "0.8.8" 1137 | source = "registry+https://github.com/rust-lang/crates.io-index" 1138 | checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" 1139 | dependencies = [ 1140 | "libc", 1141 | "wasi", 1142 | "windows-sys 0.48.0", 1143 | ] 1144 | 1145 | [[package]] 1146 | name = "mnl" 1147 | version = "0.2.2" 1148 | source = "registry+https://github.com/rust-lang/crates.io-index" 1149 | checksum = "d1a5469630da93e1813bb257964c0ccee3b26b6879dd858039ddec35cc8681ed" 1150 | dependencies = [ 1151 | "libc", 1152 | "log", 1153 | "mnl-sys", 1154 | ] 1155 | 1156 | [[package]] 1157 | name = "mnl-sys" 1158 | version = "0.2.1" 1159 | source = "registry+https://github.com/rust-lang/crates.io-index" 1160 | checksum = "9750685b201e1ecfaaf7aa5d0387829170fa565989cc481b49080aa155f70457" 1161 | dependencies = [ 1162 | "libc", 1163 | "pkg-config", 1164 | ] 1165 | 1166 | [[package]] 1167 | name = "netlink-packet-core" 1168 | version = "0.5.0" 1169 | source = "registry+https://github.com/rust-lang/crates.io-index" 1170 | checksum = "7e5cf0b54effda4b91615c40ff0fd12d0d4c9a6e0f5116874f03941792ff535a" 1171 | dependencies = [ 1172 | "anyhow", 1173 | "byteorder", 1174 | "libc", 1175 | "netlink-packet-utils", 1176 | ] 1177 | 1178 | [[package]] 1179 | name = "netlink-packet-route" 1180 | version = "0.15.0" 1181 | source = "registry+https://github.com/rust-lang/crates.io-index" 1182 | checksum = "ea993e32c77d87f01236c38f572ecb6c311d592e56a06262a007fd2a6e31253c" 1183 | dependencies = [ 1184 | "anyhow", 1185 | "bitflags", 1186 | "byteorder", 1187 | "libc", 1188 | "netlink-packet-core", 1189 | "netlink-packet-utils", 1190 | ] 1191 | 1192 | [[package]] 1193 | name = "netlink-packet-utils" 1194 | version = "0.5.2" 1195 | source = "registry+https://github.com/rust-lang/crates.io-index" 1196 | checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" 1197 | dependencies = [ 1198 | "anyhow", 1199 | "byteorder", 1200 | "paste", 1201 | "thiserror", 1202 | ] 1203 | 1204 | [[package]] 1205 | name = "netlink-proto" 1206 | version = "0.11.1" 1207 | source = "registry+https://github.com/rust-lang/crates.io-index" 1208 | checksum = "26305d12193227ef7b8227e7d61ae4eaf174607f79bd8eeceff07aacaefde497" 1209 | dependencies = [ 1210 | "bytes", 1211 | "futures", 1212 | "log", 1213 | "netlink-packet-core", 1214 | "netlink-sys", 1215 | "thiserror", 1216 | "tokio", 1217 | ] 1218 | 1219 | [[package]] 1220 | name = "netlink-sys" 1221 | version = "0.8.5" 1222 | source = "registry+https://github.com/rust-lang/crates.io-index" 1223 | checksum = "6471bf08e7ac0135876a9581bf3217ef0333c191c128d34878079f42ee150411" 1224 | dependencies = [ 1225 | "bytes", 1226 | "futures", 1227 | "libc", 1228 | "log", 1229 | "tokio", 1230 | ] 1231 | 1232 | [[package]] 1233 | name = "netns-proxy" 1234 | version = "0.1.0" 1235 | dependencies = [ 1236 | "anyhow", 1237 | "clap 4.3.5", 1238 | "either", 1239 | "flexi_logger", 1240 | "futures", 1241 | "inotify", 1242 | "ipnetwork", 1243 | "libc", 1244 | "log", 1245 | "mnl", 1246 | "netlink-packet-route", 1247 | "netns-rs", 1248 | "nix 0.26.2", 1249 | "pidfd", 1250 | "procfs", 1251 | "rtnetlink", 1252 | "rust-ini", 1253 | "rustables", 1254 | "serde", 1255 | "serde_json", 1256 | "sysinfo", 1257 | "tidy-tuntap", 1258 | "tokio", 1259 | "users", 1260 | "xdg", 1261 | "zbus", 1262 | ] 1263 | 1264 | [[package]] 1265 | name = "netns-rs" 1266 | version = "0.1.0" 1267 | source = "registry+https://github.com/rust-lang/crates.io-index" 1268 | checksum = "23541694f1d7d18cd1a0da3a1352a6ea48b01cbb4a8e7a6e547963823fd5276e" 1269 | dependencies = [ 1270 | "nix 0.23.2", 1271 | "thiserror", 1272 | ] 1273 | 1274 | [[package]] 1275 | name = "nix" 1276 | version = "0.23.2" 1277 | source = "registry+https://github.com/rust-lang/crates.io-index" 1278 | checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" 1279 | dependencies = [ 1280 | "bitflags", 1281 | "cc", 1282 | "cfg-if 1.0.0", 1283 | "libc", 1284 | "memoffset 0.6.5", 1285 | ] 1286 | 1287 | [[package]] 1288 | name = "nix" 1289 | version = "0.25.1" 1290 | source = "registry+https://github.com/rust-lang/crates.io-index" 1291 | checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" 1292 | dependencies = [ 1293 | "autocfg", 1294 | "bitflags", 1295 | "cfg-if 1.0.0", 1296 | "libc", 1297 | "memoffset 0.6.5", 1298 | "pin-utils", 1299 | ] 1300 | 1301 | [[package]] 1302 | name = "nix" 1303 | version = "0.26.2" 1304 | source = "registry+https://github.com/rust-lang/crates.io-index" 1305 | checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" 1306 | dependencies = [ 1307 | "bitflags", 1308 | "cfg-if 1.0.0", 1309 | "libc", 1310 | "memoffset 0.7.1", 1311 | "pin-utils", 1312 | "static_assertions", 1313 | ] 1314 | 1315 | [[package]] 1316 | name = "nom" 1317 | version = "5.1.3" 1318 | source = "registry+https://github.com/rust-lang/crates.io-index" 1319 | checksum = "08959a387a676302eebf4ddbcbc611da04285579f76f88ee0506c63b1a61dd4b" 1320 | dependencies = [ 1321 | "memchr", 1322 | "version_check", 1323 | ] 1324 | 1325 | [[package]] 1326 | name = "ntapi" 1327 | version = "0.4.1" 1328 | source = "registry+https://github.com/rust-lang/crates.io-index" 1329 | checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" 1330 | dependencies = [ 1331 | "winapi", 1332 | ] 1333 | 1334 | [[package]] 1335 | name = "nu-ansi-term" 1336 | version = "0.47.0" 1337 | source = "registry+https://github.com/rust-lang/crates.io-index" 1338 | checksum = "1df031e117bca634c262e9bd3173776844b6c17a90b3741c9163663b4385af76" 1339 | dependencies = [ 1340 | "windows-sys 0.45.0", 1341 | ] 1342 | 1343 | [[package]] 1344 | name = "num-traits" 1345 | version = "0.2.15" 1346 | source = "registry+https://github.com/rust-lang/crates.io-index" 1347 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" 1348 | dependencies = [ 1349 | "autocfg", 1350 | ] 1351 | 1352 | [[package]] 1353 | name = "num_cpus" 1354 | version = "1.15.0" 1355 | source = "registry+https://github.com/rust-lang/crates.io-index" 1356 | checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" 1357 | dependencies = [ 1358 | "hermit-abi 0.2.6", 1359 | "libc", 1360 | ] 1361 | 1362 | [[package]] 1363 | name = "object" 1364 | version = "0.30.4" 1365 | source = "registry+https://github.com/rust-lang/crates.io-index" 1366 | checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" 1367 | dependencies = [ 1368 | "memchr", 1369 | ] 1370 | 1371 | [[package]] 1372 | name = "once_cell" 1373 | version = "1.18.0" 1374 | source = "registry+https://github.com/rust-lang/crates.io-index" 1375 | checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" 1376 | 1377 | [[package]] 1378 | name = "ordered-multimap" 1379 | version = "0.6.0" 1380 | source = "registry+https://github.com/rust-lang/crates.io-index" 1381 | checksum = "4ed8acf08e98e744e5384c8bc63ceb0364e68a6854187221c18df61c4797690e" 1382 | dependencies = [ 1383 | "dlv-list", 1384 | "hashbrown 0.13.2", 1385 | ] 1386 | 1387 | [[package]] 1388 | name = "ordered-stream" 1389 | version = "0.2.0" 1390 | source = "registry+https://github.com/rust-lang/crates.io-index" 1391 | checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" 1392 | dependencies = [ 1393 | "futures-core", 1394 | "pin-project-lite", 1395 | ] 1396 | 1397 | [[package]] 1398 | name = "parking" 1399 | version = "2.1.0" 1400 | source = "registry+https://github.com/rust-lang/crates.io-index" 1401 | checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" 1402 | 1403 | [[package]] 1404 | name = "parking_lot" 1405 | version = "0.12.1" 1406 | source = "registry+https://github.com/rust-lang/crates.io-index" 1407 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" 1408 | dependencies = [ 1409 | "lock_api", 1410 | "parking_lot_core", 1411 | ] 1412 | 1413 | [[package]] 1414 | name = "parking_lot_core" 1415 | version = "0.9.8" 1416 | source = "registry+https://github.com/rust-lang/crates.io-index" 1417 | checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" 1418 | dependencies = [ 1419 | "cfg-if 1.0.0", 1420 | "libc", 1421 | "redox_syscall", 1422 | "smallvec", 1423 | "windows-targets 0.48.0", 1424 | ] 1425 | 1426 | [[package]] 1427 | name = "paste" 1428 | version = "1.0.12" 1429 | source = "registry+https://github.com/rust-lang/crates.io-index" 1430 | checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" 1431 | 1432 | [[package]] 1433 | name = "peeking_take_while" 1434 | version = "0.1.2" 1435 | source = "registry+https://github.com/rust-lang/crates.io-index" 1436 | checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" 1437 | 1438 | [[package]] 1439 | name = "pidfd" 1440 | version = "0.2.4" 1441 | source = "registry+https://github.com/rust-lang/crates.io-index" 1442 | checksum = "5fd1c49cdc58d1424622ff3c1e1bf7b9176e4370ced14063cbc44cfb5b62a408" 1443 | dependencies = [ 1444 | "fd-reactor", 1445 | "libc", 1446 | ] 1447 | 1448 | [[package]] 1449 | name = "pin-project-lite" 1450 | version = "0.2.9" 1451 | source = "registry+https://github.com/rust-lang/crates.io-index" 1452 | checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" 1453 | 1454 | [[package]] 1455 | name = "pin-utils" 1456 | version = "0.1.0" 1457 | source = "registry+https://github.com/rust-lang/crates.io-index" 1458 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1459 | 1460 | [[package]] 1461 | name = "pkg-config" 1462 | version = "0.3.27" 1463 | source = "registry+https://github.com/rust-lang/crates.io-index" 1464 | checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" 1465 | 1466 | [[package]] 1467 | name = "polling" 1468 | version = "2.8.0" 1469 | source = "registry+https://github.com/rust-lang/crates.io-index" 1470 | checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" 1471 | dependencies = [ 1472 | "autocfg", 1473 | "bitflags", 1474 | "cfg-if 1.0.0", 1475 | "concurrent-queue", 1476 | "libc", 1477 | "log", 1478 | "pin-project-lite", 1479 | "windows-sys 0.48.0", 1480 | ] 1481 | 1482 | [[package]] 1483 | name = "ppv-lite86" 1484 | version = "0.2.17" 1485 | source = "registry+https://github.com/rust-lang/crates.io-index" 1486 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 1487 | 1488 | [[package]] 1489 | name = "proc-macro-crate" 1490 | version = "1.3.1" 1491 | source = "registry+https://github.com/rust-lang/crates.io-index" 1492 | checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" 1493 | dependencies = [ 1494 | "once_cell", 1495 | "toml_edit", 1496 | ] 1497 | 1498 | [[package]] 1499 | name = "proc-macro-error" 1500 | version = "1.0.4" 1501 | source = "registry+https://github.com/rust-lang/crates.io-index" 1502 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 1503 | dependencies = [ 1504 | "proc-macro-error-attr", 1505 | "proc-macro2", 1506 | "quote", 1507 | "syn 1.0.109", 1508 | "version_check", 1509 | ] 1510 | 1511 | [[package]] 1512 | name = "proc-macro-error-attr" 1513 | version = "1.0.4" 1514 | source = "registry+https://github.com/rust-lang/crates.io-index" 1515 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 1516 | dependencies = [ 1517 | "proc-macro2", 1518 | "quote", 1519 | "version_check", 1520 | ] 1521 | 1522 | [[package]] 1523 | name = "proc-macro-hack" 1524 | version = "0.5.20+deprecated" 1525 | source = "registry+https://github.com/rust-lang/crates.io-index" 1526 | checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" 1527 | 1528 | [[package]] 1529 | name = "proc-macro2" 1530 | version = "1.0.60" 1531 | source = "registry+https://github.com/rust-lang/crates.io-index" 1532 | checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" 1533 | dependencies = [ 1534 | "unicode-ident", 1535 | ] 1536 | 1537 | [[package]] 1538 | name = "procfs" 1539 | version = "0.15.1" 1540 | source = "registry+https://github.com/rust-lang/crates.io-index" 1541 | checksum = "943ca7f9f29bab5844ecd8fdb3992c5969b6622bb9609b9502fef9b4310e3f1f" 1542 | dependencies = [ 1543 | "bitflags", 1544 | "byteorder", 1545 | "chrono", 1546 | "flate2", 1547 | "hex", 1548 | "lazy_static", 1549 | "rustix 0.36.14", 1550 | ] 1551 | 1552 | [[package]] 1553 | name = "quick-error" 1554 | version = "1.2.3" 1555 | source = "registry+https://github.com/rust-lang/crates.io-index" 1556 | checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" 1557 | 1558 | [[package]] 1559 | name = "quote" 1560 | version = "1.0.28" 1561 | source = "registry+https://github.com/rust-lang/crates.io-index" 1562 | checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" 1563 | dependencies = [ 1564 | "proc-macro2", 1565 | ] 1566 | 1567 | [[package]] 1568 | name = "rand" 1569 | version = "0.8.5" 1570 | source = "registry+https://github.com/rust-lang/crates.io-index" 1571 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 1572 | dependencies = [ 1573 | "libc", 1574 | "rand_chacha", 1575 | "rand_core", 1576 | ] 1577 | 1578 | [[package]] 1579 | name = "rand_chacha" 1580 | version = "0.3.1" 1581 | source = "registry+https://github.com/rust-lang/crates.io-index" 1582 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1583 | dependencies = [ 1584 | "ppv-lite86", 1585 | "rand_core", 1586 | ] 1587 | 1588 | [[package]] 1589 | name = "rand_core" 1590 | version = "0.6.4" 1591 | source = "registry+https://github.com/rust-lang/crates.io-index" 1592 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 1593 | dependencies = [ 1594 | "getrandom", 1595 | ] 1596 | 1597 | [[package]] 1598 | name = "rayon" 1599 | version = "1.7.0" 1600 | source = "registry+https://github.com/rust-lang/crates.io-index" 1601 | checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" 1602 | dependencies = [ 1603 | "either", 1604 | "rayon-core", 1605 | ] 1606 | 1607 | [[package]] 1608 | name = "rayon-core" 1609 | version = "1.11.0" 1610 | source = "registry+https://github.com/rust-lang/crates.io-index" 1611 | checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" 1612 | dependencies = [ 1613 | "crossbeam-channel", 1614 | "crossbeam-deque", 1615 | "crossbeam-utils", 1616 | "num_cpus", 1617 | ] 1618 | 1619 | [[package]] 1620 | name = "redox_syscall" 1621 | version = "0.3.5" 1622 | source = "registry+https://github.com/rust-lang/crates.io-index" 1623 | checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" 1624 | dependencies = [ 1625 | "bitflags", 1626 | ] 1627 | 1628 | [[package]] 1629 | name = "regex" 1630 | version = "1.8.4" 1631 | source = "registry+https://github.com/rust-lang/crates.io-index" 1632 | checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" 1633 | dependencies = [ 1634 | "aho-corasick", 1635 | "memchr", 1636 | "regex-syntax", 1637 | ] 1638 | 1639 | [[package]] 1640 | name = "regex-syntax" 1641 | version = "0.7.2" 1642 | source = "registry+https://github.com/rust-lang/crates.io-index" 1643 | checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" 1644 | 1645 | [[package]] 1646 | name = "rtnetlink" 1647 | version = "0.12.0" 1648 | source = "registry+https://github.com/rust-lang/crates.io-index" 1649 | checksum = "ed7d42da676fdf7e470e2502717587dd1089d8b48d9d1b846dcc3c01072858cb" 1650 | dependencies = [ 1651 | "futures", 1652 | "log", 1653 | "netlink-packet-core", 1654 | "netlink-packet-route", 1655 | "netlink-packet-utils", 1656 | "netlink-proto", 1657 | "netlink-sys", 1658 | "nix 0.26.2", 1659 | "thiserror", 1660 | "tokio", 1661 | ] 1662 | 1663 | [[package]] 1664 | name = "rust-ini" 1665 | version = "0.19.0" 1666 | source = "registry+https://github.com/rust-lang/crates.io-index" 1667 | checksum = "7e2a3bcec1f113553ef1c88aae6c020a369d03d55b58de9869a0908930385091" 1668 | dependencies = [ 1669 | "cfg-if 1.0.0", 1670 | "ordered-multimap", 1671 | ] 1672 | 1673 | [[package]] 1674 | name = "rustables" 1675 | version = "0.8.1" 1676 | dependencies = [ 1677 | "bindgen", 1678 | "bitflags", 1679 | "ipnetwork", 1680 | "libc", 1681 | "log", 1682 | "nix 0.23.2", 1683 | "regex", 1684 | "rustables-macros", 1685 | "thiserror", 1686 | ] 1687 | 1688 | [[package]] 1689 | name = "rustables-macros" 1690 | version = "0.1.1" 1691 | dependencies = [ 1692 | "once_cell", 1693 | "proc-macro-error", 1694 | "proc-macro2", 1695 | "quote", 1696 | "syn 1.0.109", 1697 | ] 1698 | 1699 | [[package]] 1700 | name = "rustc-demangle" 1701 | version = "0.1.23" 1702 | source = "registry+https://github.com/rust-lang/crates.io-index" 1703 | checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" 1704 | 1705 | [[package]] 1706 | name = "rustc-hash" 1707 | version = "1.1.0" 1708 | source = "registry+https://github.com/rust-lang/crates.io-index" 1709 | checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 1710 | 1711 | [[package]] 1712 | name = "rustix" 1713 | version = "0.36.14" 1714 | source = "registry+https://github.com/rust-lang/crates.io-index" 1715 | checksum = "14e4d67015953998ad0eb82887a0eb0129e18a7e2f3b7b0f6c422fddcd503d62" 1716 | dependencies = [ 1717 | "bitflags", 1718 | "errno", 1719 | "io-lifetimes", 1720 | "libc", 1721 | "linux-raw-sys 0.1.4", 1722 | "windows-sys 0.45.0", 1723 | ] 1724 | 1725 | [[package]] 1726 | name = "rustix" 1727 | version = "0.37.20" 1728 | source = "registry+https://github.com/rust-lang/crates.io-index" 1729 | checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0" 1730 | dependencies = [ 1731 | "bitflags", 1732 | "errno", 1733 | "io-lifetimes", 1734 | "libc", 1735 | "linux-raw-sys 0.3.8", 1736 | "windows-sys 0.48.0", 1737 | ] 1738 | 1739 | [[package]] 1740 | name = "ryu" 1741 | version = "1.0.13" 1742 | source = "registry+https://github.com/rust-lang/crates.io-index" 1743 | checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" 1744 | 1745 | [[package]] 1746 | name = "scopeguard" 1747 | version = "1.1.0" 1748 | source = "registry+https://github.com/rust-lang/crates.io-index" 1749 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 1750 | 1751 | [[package]] 1752 | name = "serde" 1753 | version = "1.0.164" 1754 | source = "registry+https://github.com/rust-lang/crates.io-index" 1755 | checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" 1756 | dependencies = [ 1757 | "serde_derive", 1758 | ] 1759 | 1760 | [[package]] 1761 | name = "serde_derive" 1762 | version = "1.0.164" 1763 | source = "registry+https://github.com/rust-lang/crates.io-index" 1764 | checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" 1765 | dependencies = [ 1766 | "proc-macro2", 1767 | "quote", 1768 | "syn 2.0.18", 1769 | ] 1770 | 1771 | [[package]] 1772 | name = "serde_json" 1773 | version = "1.0.97" 1774 | source = "registry+https://github.com/rust-lang/crates.io-index" 1775 | checksum = "bdf3bf93142acad5821c99197022e170842cdbc1c30482b98750c688c640842a" 1776 | dependencies = [ 1777 | "itoa", 1778 | "ryu", 1779 | "serde", 1780 | ] 1781 | 1782 | [[package]] 1783 | name = "serde_repr" 1784 | version = "0.1.12" 1785 | source = "registry+https://github.com/rust-lang/crates.io-index" 1786 | checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab" 1787 | dependencies = [ 1788 | "proc-macro2", 1789 | "quote", 1790 | "syn 2.0.18", 1791 | ] 1792 | 1793 | [[package]] 1794 | name = "sha1" 1795 | version = "0.10.5" 1796 | source = "registry+https://github.com/rust-lang/crates.io-index" 1797 | checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" 1798 | dependencies = [ 1799 | "cfg-if 1.0.0", 1800 | "cpufeatures", 1801 | "digest", 1802 | ] 1803 | 1804 | [[package]] 1805 | name = "shlex" 1806 | version = "0.1.1" 1807 | source = "registry+https://github.com/rust-lang/crates.io-index" 1808 | checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" 1809 | 1810 | [[package]] 1811 | name = "signal-hook" 1812 | version = "0.3.15" 1813 | source = "registry+https://github.com/rust-lang/crates.io-index" 1814 | checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9" 1815 | dependencies = [ 1816 | "libc", 1817 | "signal-hook-registry", 1818 | ] 1819 | 1820 | [[package]] 1821 | name = "signal-hook-registry" 1822 | version = "1.4.1" 1823 | source = "registry+https://github.com/rust-lang/crates.io-index" 1824 | checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" 1825 | dependencies = [ 1826 | "libc", 1827 | ] 1828 | 1829 | [[package]] 1830 | name = "slab" 1831 | version = "0.4.8" 1832 | source = "registry+https://github.com/rust-lang/crates.io-index" 1833 | checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" 1834 | dependencies = [ 1835 | "autocfg", 1836 | ] 1837 | 1838 | [[package]] 1839 | name = "smallvec" 1840 | version = "1.10.0" 1841 | source = "registry+https://github.com/rust-lang/crates.io-index" 1842 | checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" 1843 | 1844 | [[package]] 1845 | name = "socket2" 1846 | version = "0.4.9" 1847 | source = "registry+https://github.com/rust-lang/crates.io-index" 1848 | checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" 1849 | dependencies = [ 1850 | "libc", 1851 | "winapi", 1852 | ] 1853 | 1854 | [[package]] 1855 | name = "static_assertions" 1856 | version = "1.1.0" 1857 | source = "registry+https://github.com/rust-lang/crates.io-index" 1858 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 1859 | 1860 | [[package]] 1861 | name = "strsim" 1862 | version = "0.8.0" 1863 | source = "registry+https://github.com/rust-lang/crates.io-index" 1864 | checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 1865 | 1866 | [[package]] 1867 | name = "strsim" 1868 | version = "0.10.0" 1869 | source = "registry+https://github.com/rust-lang/crates.io-index" 1870 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 1871 | 1872 | [[package]] 1873 | name = "syn" 1874 | version = "1.0.109" 1875 | source = "registry+https://github.com/rust-lang/crates.io-index" 1876 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 1877 | dependencies = [ 1878 | "proc-macro2", 1879 | "quote", 1880 | "unicode-ident", 1881 | ] 1882 | 1883 | [[package]] 1884 | name = "syn" 1885 | version = "2.0.18" 1886 | source = "registry+https://github.com/rust-lang/crates.io-index" 1887 | checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" 1888 | dependencies = [ 1889 | "proc-macro2", 1890 | "quote", 1891 | "unicode-ident", 1892 | ] 1893 | 1894 | [[package]] 1895 | name = "sysinfo" 1896 | version = "0.28.4" 1897 | source = "registry+https://github.com/rust-lang/crates.io-index" 1898 | checksum = "b4c2f3ca6693feb29a89724516f016488e9aafc7f37264f898593ee4b942f31b" 1899 | dependencies = [ 1900 | "cfg-if 1.0.0", 1901 | "core-foundation-sys", 1902 | "libc", 1903 | "ntapi", 1904 | "once_cell", 1905 | "rayon", 1906 | "winapi", 1907 | ] 1908 | 1909 | [[package]] 1910 | name = "tempfile" 1911 | version = "3.6.0" 1912 | source = "registry+https://github.com/rust-lang/crates.io-index" 1913 | checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" 1914 | dependencies = [ 1915 | "autocfg", 1916 | "cfg-if 1.0.0", 1917 | "fastrand", 1918 | "redox_syscall", 1919 | "rustix 0.37.20", 1920 | "windows-sys 0.48.0", 1921 | ] 1922 | 1923 | [[package]] 1924 | name = "termcolor" 1925 | version = "1.2.0" 1926 | source = "registry+https://github.com/rust-lang/crates.io-index" 1927 | checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" 1928 | dependencies = [ 1929 | "winapi-util", 1930 | ] 1931 | 1932 | [[package]] 1933 | name = "textwrap" 1934 | version = "0.11.0" 1935 | source = "registry+https://github.com/rust-lang/crates.io-index" 1936 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 1937 | dependencies = [ 1938 | "unicode-width", 1939 | ] 1940 | 1941 | [[package]] 1942 | name = "thiserror" 1943 | version = "1.0.40" 1944 | source = "registry+https://github.com/rust-lang/crates.io-index" 1945 | checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" 1946 | dependencies = [ 1947 | "thiserror-impl", 1948 | ] 1949 | 1950 | [[package]] 1951 | name = "thiserror-impl" 1952 | version = "1.0.40" 1953 | source = "registry+https://github.com/rust-lang/crates.io-index" 1954 | checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" 1955 | dependencies = [ 1956 | "proc-macro2", 1957 | "quote", 1958 | "syn 2.0.18", 1959 | ] 1960 | 1961 | [[package]] 1962 | name = "tidy-tuntap" 1963 | version = "0.3.1" 1964 | source = "git+https://github.com/planetoryd/tidy-tuntap?branch=main#5439b09f01a7dac0094fae394c4a65b53fc13ba5" 1965 | dependencies = [ 1966 | "bindgen", 1967 | "bitflags", 1968 | "futures", 1969 | "nix 0.25.1", 1970 | "thiserror", 1971 | ] 1972 | 1973 | [[package]] 1974 | name = "tiny-keccak" 1975 | version = "2.0.2" 1976 | source = "registry+https://github.com/rust-lang/crates.io-index" 1977 | checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" 1978 | dependencies = [ 1979 | "crunchy", 1980 | ] 1981 | 1982 | [[package]] 1983 | name = "tokio" 1984 | version = "1.28.2" 1985 | source = "registry+https://github.com/rust-lang/crates.io-index" 1986 | checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" 1987 | dependencies = [ 1988 | "autocfg", 1989 | "bytes", 1990 | "libc", 1991 | "mio", 1992 | "num_cpus", 1993 | "parking_lot", 1994 | "pin-project-lite", 1995 | "signal-hook-registry", 1996 | "socket2", 1997 | "tokio-macros", 1998 | "tracing", 1999 | "windows-sys 0.48.0", 2000 | ] 2001 | 2002 | [[package]] 2003 | name = "tokio-macros" 2004 | version = "2.1.0" 2005 | source = "registry+https://github.com/rust-lang/crates.io-index" 2006 | checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" 2007 | dependencies = [ 2008 | "proc-macro2", 2009 | "quote", 2010 | "syn 2.0.18", 2011 | ] 2012 | 2013 | [[package]] 2014 | name = "toml_datetime" 2015 | version = "0.6.2" 2016 | source = "registry+https://github.com/rust-lang/crates.io-index" 2017 | checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f" 2018 | 2019 | [[package]] 2020 | name = "toml_edit" 2021 | version = "0.19.8" 2022 | source = "registry+https://github.com/rust-lang/crates.io-index" 2023 | checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" 2024 | dependencies = [ 2025 | "indexmap", 2026 | "toml_datetime", 2027 | "winnow", 2028 | ] 2029 | 2030 | [[package]] 2031 | name = "tracing" 2032 | version = "0.1.37" 2033 | source = "registry+https://github.com/rust-lang/crates.io-index" 2034 | checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" 2035 | dependencies = [ 2036 | "cfg-if 1.0.0", 2037 | "pin-project-lite", 2038 | "tracing-attributes", 2039 | "tracing-core", 2040 | ] 2041 | 2042 | [[package]] 2043 | name = "tracing-attributes" 2044 | version = "0.1.26" 2045 | source = "registry+https://github.com/rust-lang/crates.io-index" 2046 | checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" 2047 | dependencies = [ 2048 | "proc-macro2", 2049 | "quote", 2050 | "syn 2.0.18", 2051 | ] 2052 | 2053 | [[package]] 2054 | name = "tracing-core" 2055 | version = "0.1.31" 2056 | source = "registry+https://github.com/rust-lang/crates.io-index" 2057 | checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" 2058 | dependencies = [ 2059 | "once_cell", 2060 | ] 2061 | 2062 | [[package]] 2063 | name = "typenum" 2064 | version = "1.16.0" 2065 | source = "registry+https://github.com/rust-lang/crates.io-index" 2066 | checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" 2067 | 2068 | [[package]] 2069 | name = "uds_windows" 2070 | version = "1.0.2" 2071 | source = "registry+https://github.com/rust-lang/crates.io-index" 2072 | checksum = "ce65604324d3cce9b966701489fbd0cf318cb1f7bd9dd07ac9a4ee6fb791930d" 2073 | dependencies = [ 2074 | "tempfile", 2075 | "winapi", 2076 | ] 2077 | 2078 | [[package]] 2079 | name = "unicode-ident" 2080 | version = "1.0.9" 2081 | source = "registry+https://github.com/rust-lang/crates.io-index" 2082 | checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" 2083 | 2084 | [[package]] 2085 | name = "unicode-width" 2086 | version = "0.1.10" 2087 | source = "registry+https://github.com/rust-lang/crates.io-index" 2088 | checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" 2089 | 2090 | [[package]] 2091 | name = "users" 2092 | version = "0.11.0" 2093 | source = "registry+https://github.com/rust-lang/crates.io-index" 2094 | checksum = "24cc0f6d6f267b73e5a2cadf007ba8f9bc39c6a6f9666f8cf25ea809a153b032" 2095 | dependencies = [ 2096 | "libc", 2097 | "log", 2098 | ] 2099 | 2100 | [[package]] 2101 | name = "utf8parse" 2102 | version = "0.2.1" 2103 | source = "registry+https://github.com/rust-lang/crates.io-index" 2104 | checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" 2105 | 2106 | [[package]] 2107 | name = "vec_map" 2108 | version = "0.8.2" 2109 | source = "registry+https://github.com/rust-lang/crates.io-index" 2110 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 2111 | 2112 | [[package]] 2113 | name = "version_check" 2114 | version = "0.9.4" 2115 | source = "registry+https://github.com/rust-lang/crates.io-index" 2116 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 2117 | 2118 | [[package]] 2119 | name = "waker-fn" 2120 | version = "1.1.0" 2121 | source = "registry+https://github.com/rust-lang/crates.io-index" 2122 | checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" 2123 | 2124 | [[package]] 2125 | name = "wasi" 2126 | version = "0.11.0+wasi-snapshot-preview1" 2127 | source = "registry+https://github.com/rust-lang/crates.io-index" 2128 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 2129 | 2130 | [[package]] 2131 | name = "wasm-bindgen" 2132 | version = "0.2.87" 2133 | source = "registry+https://github.com/rust-lang/crates.io-index" 2134 | checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" 2135 | dependencies = [ 2136 | "cfg-if 1.0.0", 2137 | "wasm-bindgen-macro", 2138 | ] 2139 | 2140 | [[package]] 2141 | name = "wasm-bindgen-backend" 2142 | version = "0.2.87" 2143 | source = "registry+https://github.com/rust-lang/crates.io-index" 2144 | checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" 2145 | dependencies = [ 2146 | "bumpalo", 2147 | "log", 2148 | "once_cell", 2149 | "proc-macro2", 2150 | "quote", 2151 | "syn 2.0.18", 2152 | "wasm-bindgen-shared", 2153 | ] 2154 | 2155 | [[package]] 2156 | name = "wasm-bindgen-macro" 2157 | version = "0.2.87" 2158 | source = "registry+https://github.com/rust-lang/crates.io-index" 2159 | checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" 2160 | dependencies = [ 2161 | "quote", 2162 | "wasm-bindgen-macro-support", 2163 | ] 2164 | 2165 | [[package]] 2166 | name = "wasm-bindgen-macro-support" 2167 | version = "0.2.87" 2168 | source = "registry+https://github.com/rust-lang/crates.io-index" 2169 | checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" 2170 | dependencies = [ 2171 | "proc-macro2", 2172 | "quote", 2173 | "syn 2.0.18", 2174 | "wasm-bindgen-backend", 2175 | "wasm-bindgen-shared", 2176 | ] 2177 | 2178 | [[package]] 2179 | name = "wasm-bindgen-shared" 2180 | version = "0.2.87" 2181 | source = "registry+https://github.com/rust-lang/crates.io-index" 2182 | checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" 2183 | 2184 | [[package]] 2185 | name = "which" 2186 | version = "3.1.1" 2187 | source = "registry+https://github.com/rust-lang/crates.io-index" 2188 | checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724" 2189 | dependencies = [ 2190 | "libc", 2191 | ] 2192 | 2193 | [[package]] 2194 | name = "winapi" 2195 | version = "0.3.9" 2196 | source = "registry+https://github.com/rust-lang/crates.io-index" 2197 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 2198 | dependencies = [ 2199 | "winapi-i686-pc-windows-gnu", 2200 | "winapi-x86_64-pc-windows-gnu", 2201 | ] 2202 | 2203 | [[package]] 2204 | name = "winapi-i686-pc-windows-gnu" 2205 | version = "0.4.0" 2206 | source = "registry+https://github.com/rust-lang/crates.io-index" 2207 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 2208 | 2209 | [[package]] 2210 | name = "winapi-util" 2211 | version = "0.1.5" 2212 | source = "registry+https://github.com/rust-lang/crates.io-index" 2213 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 2214 | dependencies = [ 2215 | "winapi", 2216 | ] 2217 | 2218 | [[package]] 2219 | name = "winapi-x86_64-pc-windows-gnu" 2220 | version = "0.4.0" 2221 | source = "registry+https://github.com/rust-lang/crates.io-index" 2222 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 2223 | 2224 | [[package]] 2225 | name = "windows" 2226 | version = "0.48.0" 2227 | source = "registry+https://github.com/rust-lang/crates.io-index" 2228 | checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" 2229 | dependencies = [ 2230 | "windows-targets 0.48.0", 2231 | ] 2232 | 2233 | [[package]] 2234 | name = "windows-sys" 2235 | version = "0.45.0" 2236 | source = "registry+https://github.com/rust-lang/crates.io-index" 2237 | checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" 2238 | dependencies = [ 2239 | "windows-targets 0.42.2", 2240 | ] 2241 | 2242 | [[package]] 2243 | name = "windows-sys" 2244 | version = "0.48.0" 2245 | source = "registry+https://github.com/rust-lang/crates.io-index" 2246 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 2247 | dependencies = [ 2248 | "windows-targets 0.48.0", 2249 | ] 2250 | 2251 | [[package]] 2252 | name = "windows-targets" 2253 | version = "0.42.2" 2254 | source = "registry+https://github.com/rust-lang/crates.io-index" 2255 | checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" 2256 | dependencies = [ 2257 | "windows_aarch64_gnullvm 0.42.2", 2258 | "windows_aarch64_msvc 0.42.2", 2259 | "windows_i686_gnu 0.42.2", 2260 | "windows_i686_msvc 0.42.2", 2261 | "windows_x86_64_gnu 0.42.2", 2262 | "windows_x86_64_gnullvm 0.42.2", 2263 | "windows_x86_64_msvc 0.42.2", 2264 | ] 2265 | 2266 | [[package]] 2267 | name = "windows-targets" 2268 | version = "0.48.0" 2269 | source = "registry+https://github.com/rust-lang/crates.io-index" 2270 | checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" 2271 | dependencies = [ 2272 | "windows_aarch64_gnullvm 0.48.0", 2273 | "windows_aarch64_msvc 0.48.0", 2274 | "windows_i686_gnu 0.48.0", 2275 | "windows_i686_msvc 0.48.0", 2276 | "windows_x86_64_gnu 0.48.0", 2277 | "windows_x86_64_gnullvm 0.48.0", 2278 | "windows_x86_64_msvc 0.48.0", 2279 | ] 2280 | 2281 | [[package]] 2282 | name = "windows_aarch64_gnullvm" 2283 | version = "0.42.2" 2284 | source = "registry+https://github.com/rust-lang/crates.io-index" 2285 | checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" 2286 | 2287 | [[package]] 2288 | name = "windows_aarch64_gnullvm" 2289 | version = "0.48.0" 2290 | source = "registry+https://github.com/rust-lang/crates.io-index" 2291 | checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" 2292 | 2293 | [[package]] 2294 | name = "windows_aarch64_msvc" 2295 | version = "0.42.2" 2296 | source = "registry+https://github.com/rust-lang/crates.io-index" 2297 | checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" 2298 | 2299 | [[package]] 2300 | name = "windows_aarch64_msvc" 2301 | version = "0.48.0" 2302 | source = "registry+https://github.com/rust-lang/crates.io-index" 2303 | checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" 2304 | 2305 | [[package]] 2306 | name = "windows_i686_gnu" 2307 | version = "0.42.2" 2308 | source = "registry+https://github.com/rust-lang/crates.io-index" 2309 | checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" 2310 | 2311 | [[package]] 2312 | name = "windows_i686_gnu" 2313 | version = "0.48.0" 2314 | source = "registry+https://github.com/rust-lang/crates.io-index" 2315 | checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" 2316 | 2317 | [[package]] 2318 | name = "windows_i686_msvc" 2319 | version = "0.42.2" 2320 | source = "registry+https://github.com/rust-lang/crates.io-index" 2321 | checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" 2322 | 2323 | [[package]] 2324 | name = "windows_i686_msvc" 2325 | version = "0.48.0" 2326 | source = "registry+https://github.com/rust-lang/crates.io-index" 2327 | checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" 2328 | 2329 | [[package]] 2330 | name = "windows_x86_64_gnu" 2331 | version = "0.42.2" 2332 | source = "registry+https://github.com/rust-lang/crates.io-index" 2333 | checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" 2334 | 2335 | [[package]] 2336 | name = "windows_x86_64_gnu" 2337 | version = "0.48.0" 2338 | source = "registry+https://github.com/rust-lang/crates.io-index" 2339 | checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" 2340 | 2341 | [[package]] 2342 | name = "windows_x86_64_gnullvm" 2343 | version = "0.42.2" 2344 | source = "registry+https://github.com/rust-lang/crates.io-index" 2345 | checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" 2346 | 2347 | [[package]] 2348 | name = "windows_x86_64_gnullvm" 2349 | version = "0.48.0" 2350 | source = "registry+https://github.com/rust-lang/crates.io-index" 2351 | checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" 2352 | 2353 | [[package]] 2354 | name = "windows_x86_64_msvc" 2355 | version = "0.42.2" 2356 | source = "registry+https://github.com/rust-lang/crates.io-index" 2357 | checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" 2358 | 2359 | [[package]] 2360 | name = "windows_x86_64_msvc" 2361 | version = "0.48.0" 2362 | source = "registry+https://github.com/rust-lang/crates.io-index" 2363 | checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" 2364 | 2365 | [[package]] 2366 | name = "winnow" 2367 | version = "0.4.1" 2368 | source = "registry+https://github.com/rust-lang/crates.io-index" 2369 | checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28" 2370 | dependencies = [ 2371 | "memchr", 2372 | ] 2373 | 2374 | [[package]] 2375 | name = "xdg" 2376 | version = "2.5.0" 2377 | source = "registry+https://github.com/rust-lang/crates.io-index" 2378 | checksum = "688597db5a750e9cad4511cb94729a078e274308099a0382b5b8203bbc767fee" 2379 | dependencies = [ 2380 | "home", 2381 | ] 2382 | 2383 | [[package]] 2384 | name = "xdg-home" 2385 | version = "1.0.0" 2386 | source = "registry+https://github.com/rust-lang/crates.io-index" 2387 | checksum = "2769203cd13a0c6015d515be729c526d041e9cf2c0cc478d57faee85f40c6dcd" 2388 | dependencies = [ 2389 | "nix 0.26.2", 2390 | "winapi", 2391 | ] 2392 | 2393 | [[package]] 2394 | name = "zbus" 2395 | version = "3.13.1" 2396 | source = "registry+https://github.com/rust-lang/crates.io-index" 2397 | checksum = "6c3d77c9966c28321f1907f0b6c5a5561189d1f7311eea6d94180c6be9daab29" 2398 | dependencies = [ 2399 | "async-broadcast", 2400 | "async-executor", 2401 | "async-fs", 2402 | "async-io", 2403 | "async-lock", 2404 | "async-process", 2405 | "async-recursion", 2406 | "async-task", 2407 | "async-trait", 2408 | "byteorder", 2409 | "derivative", 2410 | "enumflags2", 2411 | "event-listener", 2412 | "futures-core", 2413 | "futures-sink", 2414 | "futures-util", 2415 | "hex", 2416 | "nix 0.26.2", 2417 | "once_cell", 2418 | "ordered-stream", 2419 | "rand", 2420 | "serde", 2421 | "serde_repr", 2422 | "sha1", 2423 | "static_assertions", 2424 | "tokio", 2425 | "tracing", 2426 | "uds_windows", 2427 | "winapi", 2428 | "xdg-home", 2429 | "zbus_macros", 2430 | "zbus_names", 2431 | "zvariant", 2432 | ] 2433 | 2434 | [[package]] 2435 | name = "zbus_macros" 2436 | version = "3.13.1" 2437 | source = "registry+https://github.com/rust-lang/crates.io-index" 2438 | checksum = "f6e341d12edaff644e539ccbbf7f161601294c9a84ed3d7e015da33155b435af" 2439 | dependencies = [ 2440 | "proc-macro-crate", 2441 | "proc-macro2", 2442 | "quote", 2443 | "regex", 2444 | "syn 1.0.109", 2445 | "winnow", 2446 | "zvariant_utils", 2447 | ] 2448 | 2449 | [[package]] 2450 | name = "zbus_names" 2451 | version = "2.5.1" 2452 | source = "registry+https://github.com/rust-lang/crates.io-index" 2453 | checksum = "82441e6033be0a741157a72951a3e4957d519698f3a824439cc131c5ba77ac2a" 2454 | dependencies = [ 2455 | "serde", 2456 | "static_assertions", 2457 | "zvariant", 2458 | ] 2459 | 2460 | [[package]] 2461 | name = "zvariant" 2462 | version = "3.14.0" 2463 | source = "registry+https://github.com/rust-lang/crates.io-index" 2464 | checksum = "622cc473f10cef1b0d73b7b34a266be30ebdcfaea40ec297dd8cbda088f9f93c" 2465 | dependencies = [ 2466 | "byteorder", 2467 | "enumflags2", 2468 | "libc", 2469 | "serde", 2470 | "static_assertions", 2471 | "zvariant_derive", 2472 | ] 2473 | 2474 | [[package]] 2475 | name = "zvariant_derive" 2476 | version = "3.14.0" 2477 | source = "registry+https://github.com/rust-lang/crates.io-index" 2478 | checksum = "5d9c1b57352c25b778257c661f3c4744b7cefb7fc09dd46909a153cce7773da2" 2479 | dependencies = [ 2480 | "proc-macro-crate", 2481 | "proc-macro2", 2482 | "quote", 2483 | "syn 1.0.109", 2484 | "zvariant_utils", 2485 | ] 2486 | 2487 | [[package]] 2488 | name = "zvariant_utils" 2489 | version = "1.0.1" 2490 | source = "registry+https://github.com/rust-lang/crates.io-index" 2491 | checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200" 2492 | dependencies = [ 2493 | "proc-macro2", 2494 | "quote", 2495 | "syn 1.0.109", 2496 | ] 2497 | --------------------------------------------------------------------------------