summaryrefslogtreecommitdiffhomepage
path: root/mullvad-cli/src/cmds/obfuscation.rs
blob: 3f015c896e9bfe922c01e498aa145138a3370dda (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
use crate::{new_rpc_client, Command, Result};

use mullvad_management_interface::{types as grpc_types, ManagementServiceClient};

use mullvad_types::relay_constraints::{ObfuscationSettings, SelectedObfuscation};

use std::convert::TryFrom;

pub struct Obfuscation;

#[mullvad_management_interface::async_trait]
impl Command for Obfuscation {
    fn name(&self) -> &'static str {
        "obfuscation"
    }

    fn clap_subcommand(&self) -> clap::App<'static> {
        clap::App::new(self.name())
            .about(
                "Manage use of obfuscation protocols for WireGuard. \
                Can make WireGuard traffic look like something else on the network. \
                Helps circumvent censorship and to establish a tunnel when on restricted networks",
            )
            .setting(clap::AppSettings::SubcommandRequiredElseHelp)
            .subcommand(create_obfuscation_set_subcommand())
            .subcommand(create_obfuscation_get_subcommand())
    }

    async fn run(&self, matches: &clap::ArgMatches) -> Result<()> {
        match matches.subcommand() {
            Some(("set", set_matches)) => Self::handle_set(set_matches).await,
            Some(("get", _get_matches)) => Self::handle_get().await,
            _ => unreachable!("unhandled command"),
        }
    }
}

impl Obfuscation {
    async fn handle_set(matches: &clap::ArgMatches) -> Result<()> {
        match matches.subcommand() {
            Some(("mode", mode_matches)) => {
                let obfuscator_type = mode_matches.value_of("mode").unwrap();
                let mut rpc = new_rpc_client().await?;
                let mut settings = Self::get_obfuscation_settings(&mut rpc).await?;
                settings.selected_obfuscation = match obfuscator_type {
                    "auto" => SelectedObfuscation::Auto,
                    "off" => SelectedObfuscation::Off,
                    "udp2tcp" => SelectedObfuscation::Udp2Tcp,
                    _ => unreachable!("Unhandled obfuscator mode"),
                };
                Self::set_obfuscation_settings(&mut rpc, &settings).await?;
            }
            Some(("udp2tcp", settings_matches)) => {
                let port: String = settings_matches.value_of_t_or_exit("port");
                let mut rpc = new_rpc_client().await?;
                let mut settings = Self::get_obfuscation_settings(&mut rpc).await?;
                settings.udp2tcp.port = if port == "any" {
                    mullvad_types::relay_constraints::Constraint::Any
                } else {
                    mullvad_types::relay_constraints::Constraint::Only(
                        port.parse::<u16>().expect("Invalid port number"),
                    )
                };
                Self::set_obfuscation_settings(&mut rpc, &settings).await?;
            }
            _ => unreachable!("unhandled command"),
        }
        Ok(())
    }

    async fn handle_get() -> Result<()> {
        let mut rpc = new_rpc_client().await?;
        let obfuscation_settings = Self::get_obfuscation_settings(&mut rpc).await?;
        println!(
            "Obfuscation mode: {}",
            obfuscation_settings.selected_obfuscation
        );
        println!("udp2tcp settings: {}", obfuscation_settings.udp2tcp);
        Ok(())
    }

    async fn get_obfuscation_settings(
        rpc: &mut ManagementServiceClient,
    ) -> Result<ObfuscationSettings> {
        let settings = rpc.get_settings(()).await?.into_inner();

        let obfuscation_settings = ObfuscationSettings::try_from(
            settings
                .obfuscation_settings
                .expect("No obfuscation settings"),
        )
        .expect("failed to parse obfuscation settings");
        Ok(obfuscation_settings)
    }

    async fn set_obfuscation_settings(
        rpc: &mut ManagementServiceClient,
        settings: &ObfuscationSettings,
    ) -> Result<()> {
        let grpc_settings: grpc_types::ObfuscationSettings = settings.into();
        let _ = rpc.set_obfuscation_settings(grpc_settings).await?;
        Ok(())
    }
}

fn create_obfuscation_set_subcommand() -> clap::App<'static> {
    clap::App::new("set")
        .about("Set obfuscation settings")
        .setting(clap::AppSettings::SubcommandRequiredElseHelp)
        .subcommand(
            clap::App::new("mode").about("Set obfuscation mode").arg(
                clap::Arg::new("mode")
                    .help(
                        "Specifies if obfuscation should be used with WireGuard connections. \
                        And if so, what obfuscation protocol it should use.",
                    )
                    .required(true)
                    .index(1)
                    .possible_values(&["auto", "off", "udp2tcp"]),
            ),
        )
        .subcommand(
            clap::App::new("udp2tcp")
                .about("Specifies the config for the udp2tcp obfuscator")
                .setting(clap::AppSettings::ArgRequiredElseHelp)
                .arg(
                    clap::Arg::new("port")
                        .help("TCP port of remote endpoint. Either 'any' or a specific port")
                        .long("port")
                        .takes_value(true),
                ),
        )
}

fn create_obfuscation_get_subcommand() -> clap::App<'static> {
    clap::App::new("get").about("Get current obfuscation settings")
}