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
|
use crate::{format, format::print_keygen_event, new_rpc_client, Command, Error, Result};
use mullvad_management_interface::{
types::daemon_event::Event as EventType, ManagementServiceClient,
};
pub struct Status;
#[mullvad_management_interface::async_trait]
impl Command for Status {
fn name(&self) -> &'static str {
"status"
}
fn clap_subcommand(&self) -> clap::App<'static, 'static> {
clap::SubCommand::with_name(self.name())
.about("View the state of the VPN tunnel")
.arg(
clap::Arg::with_name("location")
.long("location")
.short("l")
.help("Prints the current location and IP. Based on GeoIP lookups"),
)
.subcommand(
clap::SubCommand::with_name("listen")
.about("Listen for VPN tunnel state changes")
.arg(
clap::Arg::with_name("verbose")
.short("v")
.help("Enables verbose output"),
),
)
}
async fn run(&self, matches: &clap::ArgMatches<'_>) -> Result<()> {
let mut rpc = new_rpc_client().await?;
let state = rpc.get_tunnel_state(()).await?.into_inner();
format::print_state(&state);
if matches.is_present("location") {
print_location(&mut rpc).await?;
}
if let Some(listen_matches) = matches.subcommand_matches("listen") {
let verbose = listen_matches.is_present("verbose");
let mut events = rpc.events_listen(()).await?.into_inner();
while let Some(event) = events.message().await? {
match event.event.unwrap() {
EventType::TunnelState(new_state) => {
format::print_state(&new_state);
use mullvad_management_interface::types::tunnel_state::State::*;
match new_state.state.unwrap() {
Connected(..) | Disconnected(..) => {
if matches.is_present("location") {
print_location(&mut rpc).await?;
}
}
_ => {}
}
}
EventType::Settings(settings) => {
if verbose {
println!("New settings: {:#?}", settings);
}
}
EventType::RelayList(relay_list) => {
if verbose {
println!("New relay list: {:#?}", relay_list);
}
}
EventType::VersionInfo(app_version_info) => {
if verbose {
println!("New app version info: {:#?}", app_version_info);
}
}
EventType::KeyEvent(key_event) => {
if verbose {
print!("Key event: ");
print_keygen_event(&key_event);
}
}
}
}
}
Ok(())
}
}
async fn print_location(rpc: &mut ManagementServiceClient) -> Result<()> {
let location = rpc.get_current_location(()).await;
let location = match location {
Ok(response) => response.into_inner(),
Err(status) => {
if status.code() == mullvad_management_interface::Code::NotFound {
println!("Location data unavailable");
return Ok(());
} else {
return Err(Error::RpcFailed(status));
}
}
};
if !location.hostname.is_empty() {
println!("Relay: {}", location.hostname);
}
if !location.ipv4.is_empty() {
println!("IPv4: {}", location.ipv4);
}
if !location.ipv6.is_empty() {
println!("IPv6: {}", location.ipv6);
}
print!("Location: ");
if !location.city.is_empty() {
print!("{}, ", location.city);
}
println!("{}", location.country);
println!(
"Position: {:.5}°N, {:.5}°W",
location.latitude, location.longitude
);
Ok(())
}
|