summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJonatan Rhodin <jonatan.rhodin@mullvad.net>2025-10-16 15:07:57 +0200
committerJonatan Rhodin <jonatan.rhodin@mullvad.net>2025-10-22 13:06:17 +0200
commit01e2ade8ba9c8ea4363eeca65a0d8ed989de4d2f (patch)
treefb55d756049f478fa4be40de48b239f2059393c9
parent4d3129808552e247a591e074a944d95ea9916a27 (diff)
downloadmullvadvpn-01e2ade8ba9c8ea4363eeca65a0d8ed989de4d2f.tar.xz
mullvadvpn-01e2ade8ba9c8ea4363eeca65a0d8ed989de4d2f.zip
Add entry and exit no relay errors
The old no relay error is still kept for single hop
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/inappnotification/TunnelStateNotificationUseCase.kt8
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt4
-rw-r--r--android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/mapper/ToDomain.kt4
-rw-r--r--android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/ParameterGenerationError.kt2
-rw-r--r--android/lib/resource/src/main/res/values/strings.xml2
-rw-r--r--android/lib/ui/component/build.gradle.kts1
-rw-r--r--android/lib/ui/component/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/component/AnimatedNotificationBanner.kt16
-rw-r--r--android/lib/ui/component/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/component/NotificationData.kt6
-rw-r--r--desktop/packages/mullvad-vpn/locales/messages.pot6
-rw-r--r--desktop/packages/mullvad-vpn/src/main/grpc-type-convertions.ts2
-rw-r--r--mullvad-daemon/src/tunnel.rs6
-rw-r--r--mullvad-management-interface/proto/management_interface.proto14
-rw-r--r--mullvad-management-interface/src/types/conversions/states.rs14
-rw-r--r--mullvad-relay-selector/src/error.rs6
-rw-r--r--mullvad-relay-selector/src/relay_selector/mod.rs91
-rw-r--r--talpid-types/src/tunnel.rs8
16 files changed, 125 insertions, 65 deletions
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/inappnotification/TunnelStateNotificationUseCase.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/inappnotification/TunnelStateNotificationUseCase.kt
index 8e58c58a36..442e562bac 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/inappnotification/TunnelStateNotificationUseCase.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/inappnotification/TunnelStateNotificationUseCase.kt
@@ -76,9 +76,11 @@ class TunnelStateNotificationUseCase(
} else this
private fun ErrorState.isPossiblePortError(): Boolean =
- cause is ErrorStateCause.TunnelParameterError &&
- (cause as ErrorStateCause.TunnelParameterError).error ==
- ParameterGenerationError.NoMatchingRelay
+ (cause as? ErrorStateCause.TunnelParameterError)?.error?.let {
+ it == ParameterGenerationError.NoMatchingRelayEntry ||
+ it == ParameterGenerationError.NoMatchingRelayExit ||
+ it == ParameterGenerationError.NoMatchingRelay
+ } ?: false
private fun Constraint<Port>.invalidPortOrNull(availablePortRanges: List<PortRange>): Port? =
getOrNull()?.takeIf { !it.inAnyOf(availablePortRanges) }
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt
index f4c460d880..d10feff8ca 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt
@@ -246,8 +246,4 @@ class ConnectViewModel(
data class PermissionDenied(val systemVpnSettingsAvailable: Boolean) : ConnectError
}
}
-
- companion object {
- const val UI_STATE_DEBOUNCE_DURATION_MILLIS: Long = 200
- }
}
diff --git a/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/mapper/ToDomain.kt b/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/mapper/ToDomain.kt
index 26207c5d57..b60e2d8444 100644
--- a/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/mapper/ToDomain.kt
+++ b/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/mapper/ToDomain.kt
@@ -307,6 +307,10 @@ internal fun ManagementInterface.ErrorState.FirewallPolicyError.toDomain():
internal fun ManagementInterface.ErrorState.GenerationError.toDomain(): ParameterGenerationError =
when (this) {
+ ManagementInterface.ErrorState.GenerationError.NO_MATCHING_RELAY_ENTRY ->
+ ParameterGenerationError.NoMatchingRelayEntry
+ ManagementInterface.ErrorState.GenerationError.NO_MATCHING_RELAY_EXIT ->
+ ParameterGenerationError.NoMatchingRelayExit
ManagementInterface.ErrorState.GenerationError.NO_MATCHING_RELAY ->
ParameterGenerationError.NoMatchingRelay
ManagementInterface.ErrorState.GenerationError.NO_MATCHING_BRIDGE_RELAY ->
diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/ParameterGenerationError.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/ParameterGenerationError.kt
index 5683dc08b1..dd4e65a03a 100644
--- a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/ParameterGenerationError.kt
+++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/ParameterGenerationError.kt
@@ -1,6 +1,8 @@
package net.mullvad.mullvadvpn.lib.model
enum class ParameterGenerationError {
+ NoMatchingRelayEntry,
+ NoMatchingRelayExit,
NoMatchingRelay,
NoMatchingBridgeRelay,
NoWireguardKey,
diff --git a/android/lib/resource/src/main/res/values/strings.xml b/android/lib/resource/src/main/res/values/strings.xml
index b03c4d7d36..1aac455e0c 100644
--- a/android/lib/resource/src/main/res/values/strings.xml
+++ b/android/lib/resource/src/main/res/values/strings.xml
@@ -462,4 +462,6 @@
<string name="send_email">Send email</string>
<string name="no_email_app_available">No email app available on the device</string>
<string name="include_account_token_checkbox_text">This is a question about account or payments (include account information)</string>
+ <string name="no_matching_relay_entry">No entry server match your settings, try changing server or other settings.</string>
+ <string name="no_matching_relay_exit">No exit server match your settings, try changing server or other settings.</string>
</resources>
diff --git a/android/lib/ui/component/build.gradle.kts b/android/lib/ui/component/build.gradle.kts
index 56ec9a63ef..54cf81bdcb 100644
--- a/android/lib/ui/component/build.gradle.kts
+++ b/android/lib/ui/component/build.gradle.kts
@@ -24,6 +24,7 @@ android {
compilerOptions {
jvmTarget = JvmTarget.fromTarget(libs.versions.jvm.target.get())
allWarningsAsErrors = true
+ freeCompilerArgs = listOf("-XXLanguage:+WhenGuards")
}
}
diff --git a/android/lib/ui/component/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/component/AnimatedNotificationBanner.kt b/android/lib/ui/component/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/component/AnimatedNotificationBanner.kt
index b9799fab33..2ed3ca4b0f 100644
--- a/android/lib/ui/component/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/component/AnimatedNotificationBanner.kt
+++ b/android/lib/ui/component/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/component/AnimatedNotificationBanner.kt
@@ -80,14 +80,14 @@ fun AnimatedNotificationBanner(
modifier = notificationModifier,
visibleNotification.toNotificationData(
isPlayBuild = isPlayBuild,
- openAppListing,
- onClickShowAccount,
- onClickShowChangelog,
- onClickShowAndroid16UpgradeInfo,
- onClickDismissChangelog,
- onClickDismissNewDevice,
- onClickShowWireguardPortSettings,
- onClickDismissAndroid16UpgradeWarning,
+ openAppListing = openAppListing,
+ onClickShowAccount = onClickShowAccount,
+ onClickShowChangelog = onClickShowChangelog,
+ onClickShowAndroid16UpgradeInfo = onClickShowAndroid16UpgradeInfo,
+ onClickDismissChangelog = onClickDismissChangelog,
+ onClickDismissNewDevice = onClickDismissNewDevice,
+ onClickShowWireguardPortSettings = onClickShowWireguardPortSettings,
+ onClickDismissAndroid16UpgradeWarning = onClickDismissAndroid16UpgradeWarning,
),
)
}
diff --git a/android/lib/ui/component/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/component/NotificationData.kt b/android/lib/ui/component/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/component/NotificationData.kt
index 9f4c17dbd9..53c56549b1 100644
--- a/android/lib/ui/component/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/component/NotificationData.kt
+++ b/android/lib/ui/component/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/component/NotificationData.kt
@@ -288,6 +288,12 @@ private fun ParameterGenerationError.errorMessageId(): Int =
ParameterGenerationError.NoMatchingBridgeRelay -> {
R.string.no_matching_relay
}
+ ParameterGenerationError.NoMatchingRelayExit -> {
+ R.string.no_matching_relay_exit
+ }
+ ParameterGenerationError.NoMatchingRelayEntry -> {
+ R.string.no_matching_relay_entry
+ }
ParameterGenerationError.NoWireguardKey -> R.string.no_wireguard_key
ParameterGenerationError.CustomTunnelHostResolutionError ->
R.string.custom_tunnel_host_resolution_error
diff --git a/desktop/packages/mullvad-vpn/locales/messages.pot b/desktop/packages/mullvad-vpn/locales/messages.pot
index efeecc4761..2317b5923a 100644
--- a/desktop/packages/mullvad-vpn/locales/messages.pot
+++ b/desktop/packages/mullvad-vpn/locales/messages.pot
@@ -3264,6 +3264,12 @@ msgstr ""
msgid "No email app available on the device"
msgstr ""
+msgid "No entry server match your settings, try changing server or other settings."
+msgstr ""
+
+msgid "No exit server match your settings, try changing server or other settings."
+msgstr ""
+
msgid "No internet connection"
msgstr ""
diff --git a/desktop/packages/mullvad-vpn/src/main/grpc-type-convertions.ts b/desktop/packages/mullvad-vpn/src/main/grpc-type-convertions.ts
index f29979c520..0387219756 100644
--- a/desktop/packages/mullvad-vpn/src/main/grpc-type-convertions.ts
+++ b/desktop/packages/mullvad-vpn/src/main/grpc-type-convertions.ts
@@ -329,6 +329,8 @@ function convertFromParameterError(
): TunnelParameterError {
switch (error) {
case grpcTypes.ErrorState.GenerationError.NO_MATCHING_RELAY:
+ case grpcTypes.ErrorState.GenerationError.NO_MATCHING_RELAY_ENTRY:
+ case grpcTypes.ErrorState.GenerationError.NO_MATCHING_RELAY_EXIT:
return TunnelParameterError.noMatchingRelay;
case grpcTypes.ErrorState.GenerationError.NO_MATCHING_BRIDGE_RELAY:
return TunnelParameterError.noMatchingBridgeRelay;
diff --git a/mullvad-daemon/src/tunnel.rs b/mullvad-daemon/src/tunnel.rs
index 4c35ee2be8..27c89020f5 100644
--- a/mullvad-daemon/src/tunnel.rs
+++ b/mullvad-daemon/src/tunnel.rs
@@ -303,6 +303,12 @@ impl From<Error> for ParameterGenerationError {
Error::SelectRelay(mullvad_relay_selector::Error::IpVersionUnavailable { family }) => {
ParameterGenerationError::IpVersionUnavailable { family }
}
+ Error::SelectRelay(mullvad_relay_selector::Error::NoRelayEntry(_)) => {
+ ParameterGenerationError::NoMatchingRelayEntry
+ }
+ Error::SelectRelay(mullvad_relay_selector::Error::NoRelayExit(_)) => {
+ ParameterGenerationError::NoMatchingRelayExit
+ }
Error::NoAuthDetails | Error::SelectRelay(_) | Error::Device(_) => {
ParameterGenerationError::NoMatchingRelay
}
diff --git a/mullvad-management-interface/proto/management_interface.proto b/mullvad-management-interface/proto/management_interface.proto
index 25e0321ce4..928b704b41 100644
--- a/mullvad-management-interface/proto/management_interface.proto
+++ b/mullvad-management-interface/proto/management_interface.proto
@@ -222,12 +222,14 @@ message ErrorState {
}
enum GenerationError {
- NO_MATCHING_RELAY = 0;
- NO_MATCHING_BRIDGE_RELAY = 1;
- NO_WIREGUARD_KEY = 2;
- CUSTOM_TUNNEL_HOST_RESOLUTION_ERROR = 3;
- NETWORK_IPV4_UNAVAILABLE = 4;
- NETWORK_IPV6_UNAVAILABLE = 5;
+ NO_MATCHING_RELAY_ENTRY = 0;
+ NO_MATCHING_RELAY_EXIT = 1;
+ NO_MATCHING_RELAY = 2;
+ NO_MATCHING_BRIDGE_RELAY = 3;
+ NO_WIREGUARD_KEY = 4;
+ CUSTOM_TUNNEL_HOST_RESOLUTION_ERROR = 5;
+ NETWORK_IPV4_UNAVAILABLE = 6;
+ NETWORK_IPV6_UNAVAILABLE = 7;
}
message FirewallPolicyError {
diff --git a/mullvad-management-interface/src/types/conversions/states.rs b/mullvad-management-interface/src/types/conversions/states.rs
index bbd6c500f2..002aa6ea69 100644
--- a/mullvad-management-interface/src/types/conversions/states.rs
+++ b/mullvad-management-interface/src/types/conversions/states.rs
@@ -179,9 +179,15 @@ impl From<mullvad_types::states::TunnelState> for proto::TunnelState {
error_state.cause()
{
match reason {
- talpid_tunnel::ParameterGenerationError::NoMatchingRelay => {
- i32::from(GenerationError::NoMatchingRelay)
+ talpid_tunnel::ParameterGenerationError::NoMatchingRelayEntry => {
+ i32::from(GenerationError::NoMatchingRelayEntry)
}
+ talpid_tunnel::ParameterGenerationError::NoMatchingRelayExit => {
+ i32::from(GenerationError::NoMatchingRelayExit)
+ }
+ talpid_tunnel::ParameterGenerationError::NoMatchingRelay => {
+ i32::from(GenerationError::NoMatchingRelay)
+ }
talpid_tunnel::ParameterGenerationError::NoMatchingBridgeRelay => {
i32::from(GenerationError::NoMatchingBridgeRelay)
}
@@ -391,10 +397,12 @@ impl TryFrom<proto::TunnelState> for mullvad_types::states::TunnelState {
let parameter_error = match proto::error_state::GenerationError::try_from(parameter_error) {
Ok(proto::error_state::GenerationError::CustomTunnelHostResolutionError) => talpid_tunnel::ParameterGenerationError::CustomTunnelHostResolutionError,
Ok(proto::error_state::GenerationError::NoMatchingBridgeRelay) => talpid_tunnel::ParameterGenerationError::NoMatchingBridgeRelay,
- Ok(proto::error_state::GenerationError::NoMatchingRelay) => talpid_tunnel::ParameterGenerationError::NoMatchingRelay,
+ Ok(proto::error_state::GenerationError::NoMatchingRelayEntry) => talpid_tunnel::ParameterGenerationError::NoMatchingRelayEntry,
+ Ok(proto::error_state::GenerationError::NoMatchingRelayExit) => talpid_tunnel::ParameterGenerationError::NoMatchingRelayExit,
Ok(proto::error_state::GenerationError::NoWireguardKey) => talpid_tunnel::ParameterGenerationError::NoWireguardKey,
Ok(proto::error_state::GenerationError::NetworkIpv4Unavailable) => talpid_tunnel::ParameterGenerationError::IpVersionUnavailable { family: IpVersion::V4 },
Ok(proto::error_state::GenerationError::NetworkIpv6Unavailable) => talpid_tunnel::ParameterGenerationError::IpVersionUnavailable { family: IpVersion::V6 },
+ Ok(proto::error_state::GenerationError::NoMatchingRelay) => talpid_tunnel::ParameterGenerationError::NoMatchingRelay,
_ => return Err(FromProtobufTypeError::InvalidArgument(
"invalid parameter error",
)),
diff --git a/mullvad-relay-selector/src/error.rs b/mullvad-relay-selector/src/error.rs
index 5dc7747f6c..0d5acf4ae8 100644
--- a/mullvad-relay-selector/src/error.rs
+++ b/mullvad-relay-selector/src/error.rs
@@ -16,6 +16,12 @@ pub enum Error {
#[error("The combination of relay constraints is invalid")]
InvalidConstraints,
+ #[error("No relays matching current entry constraints: {0:?}")]
+ NoRelayEntry(Box<RelayQuery>),
+
+ #[error("No relays matching current exit constraints: {0:?}")]
+ NoRelayExit(Box<RelayQuery>),
+
#[error("No relays matching current constraints: {0:?}")]
NoRelay(Box<RelayQuery>),
diff --git a/mullvad-relay-selector/src/relay_selector/mod.rs b/mullvad-relay-selector/src/relay_selector/mod.rs
index d5087f43a3..16e0013ffd 100644
--- a/mullvad-relay-selector/src/relay_selector/mod.rs
+++ b/mullvad-relay-selector/src/relay_selector/mod.rs
@@ -612,20 +612,20 @@ impl RelaySelector {
// Select a relay using the user's preferences merged with the nth compatible query
// in `retry_order`, looping back to the start of `retry_order` if
// necessary.
- retry_order
+ let maybe_relay = retry_order
.iter()
.filter_map(|query| query.clone().intersection(user_query.clone()))
.filter_map(|query| {
Self::get_relay_inner(&query, &parsed_relays, custom_lists).ok()
})
.cycle() // If the above filters remove all relays, cycle will also return an empty iterator
- .nth(retry_attempt)
+ .nth(retry_attempt);
+ match maybe_relay {
+ Some(v) => Ok(v),
// If none of the queries in `retry_order` merged with `user_preferences` yield any relays,
// attempt to only consider the user's preferences.
- .or_else(|| {
- Self::get_relay_inner(&user_query, &parsed_relays, custom_lists).ok()
- })
- .ok_or_else(|| Error::NoRelay(Box::new(user_query)))
+ None => Self::get_relay_inner(&user_query, &parsed_relays, custom_lists),
+ }
}
}
}
@@ -789,7 +789,7 @@ impl RelaySelector {
let exit_candidates =
filter_matching_relay_list(&exit_relay_query, parsed_relays, custom_lists);
let exit = helpers::pick_random_relay(&exit_candidates)
- .ok_or_else(|| Error::NoRelay(Box::new(exit_relay_query)))?;
+ .ok_or_else(|| Error::NoRelayExit(Box::new(exit_relay_query)))?;
// generate a list of potential entry relays, disregarding any location constraint
let mut entry_query = query.clone();
@@ -814,7 +814,7 @@ impl RelaySelector {
.map(|relay_with_distance| relay_with_distance.relay)
.collect_vec();
let entry = helpers::pick_random_relay_excluding(&entry_candidates, exit)
- .ok_or_else(|| Error::NoRelay(Box::new(entry_query)))?;
+ .ok_or_else(|| Error::NoRelayEntry(Box::new(entry_query)))?;
Ok(Multihop::new(entry.clone(), exit.clone()))
}
@@ -856,55 +856,66 @@ impl RelaySelector {
let entry_candidates =
filter_matching_relay_list(&entry_relay_query, parsed_relays, custom_lists);
- match Self::pick_working_entry_exit_combo(
+ Self::pick_working_entry_exit_combo(
+ query,
exit_candidates.as_slice(),
entry_candidates.as_slice(),
- ) {
- Some((exit, entry)) => Ok(Multihop::new(entry.clone(), exit.clone())),
- None => {
- // Sometimes, the set of relays is too small to consider the `include_in_country`
- // flag. It might just be that if we disregard the `include_in_country` flag, we
- // manage to find candidate relays. This is rather unlikely, but it might just
- // happen.
- let exit_candidates = filter_matching_relay_list_include_all(
- &exit_relay_query,
- parsed_relays,
- custom_lists,
- );
- let entry_candidates = filter_matching_relay_list_include_all(
- &entry_relay_query,
- parsed_relays,
- custom_lists,
- );
- let (exit, entry) = Self::pick_working_entry_exit_combo(
- exit_candidates.as_slice(),
- entry_candidates.as_slice(),
- )
- .ok_or_else(|| Error::NoRelay(Box::new(query.clone())))?;
- Ok(Multihop::new(entry.clone(), exit.clone()))
- }
- }
+ )
+ .map(|(exit, entry)| Multihop::new(entry.clone(), exit.clone()))
+ .or_else(|_e| {
+ // Sometimes, the set of relays is too small to consider the `include_in_country`
+ // flag. It might just be that if we disregard the `include_in_country` flag, we
+ // manage to find candidate relays. This is rather unlikely, but it might just
+ // happen.
+ let exit_candidates = filter_matching_relay_list_include_all(
+ &exit_relay_query,
+ parsed_relays,
+ custom_lists,
+ );
+ let entry_candidates = filter_matching_relay_list_include_all(
+ &entry_relay_query,
+ parsed_relays,
+ custom_lists,
+ );
+ Self::pick_working_entry_exit_combo(
+ query,
+ exit_candidates.as_slice(),
+ entry_candidates.as_slice(),
+ )
+ .map(|(exit, entry)| Multihop::new(entry.clone(), exit.clone()))
+ })
}
/// Avoid picking the same relay for entry and exit by choosing one and excluding it when
/// choosing the other.
fn pick_working_entry_exit_combo<'a>(
+ query: &RelayQuery,
exit_candidates: &'a [Relay],
entry_candidates: &'a [Relay],
- ) -> Option<(&'a Relay, &'a Relay)> {
+ ) -> Result<(&'a Relay, &'a Relay), Error> {
match (exit_candidates, entry_candidates) {
// In the case where there is only one entry to choose from, we have to pick it before
// the exit
(exits, [entry]) if exits.contains(entry) => {
- helpers::pick_random_relay_excluding(exits, entry).map(|exit| (exit, entry))
+ helpers::pick_random_relay_excluding(exits, entry)
+ .map(|exit| (exit, entry))
+ .ok_or_else(|| Error::NoRelayExit(Box::new(query.clone())))
}
// Vice versa for the case of only one exit
([exit], entries) if entries.contains(exit) => {
- helpers::pick_random_relay_excluding(entries, exit).map(|entry| (exit, entry))
+ helpers::pick_random_relay_excluding(entries, exit)
+ .map(|entry| (exit, entry))
+ .ok_or_else(|| Error::NoRelayEntry(Box::new(query.clone())))
+ }
+ (exits, entries) => {
+ let exit = helpers::pick_random_relay(exits);
+ match exit {
+ None => Err(Error::NoRelayExit(Box::new(query.clone()))),
+ Some(exit) => helpers::pick_random_relay_excluding(entries, exit)
+ .map(|entry| (exit, entry))
+ .ok_or_else(|| Error::NoRelayEntry(Box::new(query.clone()))),
+ }
}
- (exits, entries) => helpers::pick_random_relay(exits).and_then(|exit| {
- helpers::pick_random_relay_excluding(entries, exit).map(|entry| (exit, entry))
- }),
}
}
diff --git a/talpid-types/src/tunnel.rs b/talpid-types/src/tunnel.rs
index 3dc7bdc308..768c40a600 100644
--- a/talpid-types/src/tunnel.rs
+++ b/talpid-types/src/tunnel.rs
@@ -121,7 +121,13 @@ impl ErrorStateCause {
#[derive(thiserror::Error, Debug, Serialize, Clone, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ParameterGenerationError {
- /// Failure to select a matching tunnel relay
+ /// Failure to select a matching entry tunnel relay
+ #[error("Failure to select a matching entry tunnel relay")]
+ NoMatchingRelayEntry,
+ /// Failure to select a matching exit tunnel relay
+ #[error("Failure to select a matching exit tunnel relay")]
+ NoMatchingRelayExit,
+ /// Failure to select a matching tunnel relay, but we do not know if it is an entry or an exit
#[error("Failure to select a matching tunnel relay")]
NoMatchingRelay,
/// Failure to select a matching bridge relay