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
|
use futures::join;
use mullvad_rpc::{
self,
rest::{Error, RequestServiceHandle},
};
use mullvad_types::location::{AmIMullvad, GeoIpLocation};
use talpid_types::ErrorExt;
const URI_V4: &str = "https://ipv4.am.i.mullvad.net/json";
const URI_V6: &str = "https://ipv6.am.i.mullvad.net/json";
pub async fn send_location_request(
request_sender: RequestServiceHandle,
) -> Result<GeoIpLocation, Error> {
let v4_sender = request_sender.clone();
let v4_future = async move {
let location = send_location_request_internal(URI_V4, v4_sender).await?;
Ok::<GeoIpLocation, Error>(GeoIpLocation::from(location))
};
let v6_sender = request_sender.clone();
let v6_future = async move {
let location = send_location_request_internal(URI_V6, v6_sender).await?;
Ok::<GeoIpLocation, Error>(GeoIpLocation::from(location))
};
let (v4_result, v6_result) = join!(v4_future, v6_future);
match (v4_result, v6_result) {
(Ok(mut v4), Ok(v6)) => {
v4.ipv6 = v6.ipv6;
v4.mullvad_exit_ip = v4.mullvad_exit_ip && v6.mullvad_exit_ip;
Ok(v4)
}
(Ok(v4), Err(e)) => {
log_network_error(e, "IPv6");
Ok(v4)
}
(Err(e), Ok(v6)) => {
log_network_error(e, "IPv4");
Ok(v6)
}
(Err(e_v4), Err(_)) => Err(e_v4),
}
}
async fn send_location_request_internal(
uri: &'static str,
service: RequestServiceHandle,
) -> Result<AmIMullvad, Error> {
let future_service = service.clone();
let request = mullvad_rpc::rest::RestRequest::get(uri)?;
let response = future_service.request(request).await?;
mullvad_rpc::rest::deserialize_body(response).await
}
fn log_network_error(err: Error, version: &'static str) {
let err_message = &format!("Unable to fetch {} GeoIP location", version);
match err {
Error::HyperError(hyper_err) if hyper_err.is_connect() => {
if let Some(cause) = hyper_err.into_cause() {
if let Some(err) = cause.downcast_ref::<std::io::Error>() {
// Don't log ENETUNREACH errors, they are not informative.
if err.raw_os_error() == Some(libc::ENETUNREACH) {
return;
}
log::debug!("{}: Hyper connect error: {}", err_message, cause);
}
} else {
log::error!("Hyper Connection error did not contain a cause!");
}
}
any_other_error => {
log::debug!("{}", any_other_error.display_chain_with_msg(err_message));
}
};
}
|