Skip to content

Commit 5f4c745

Browse files
committed
fix(reachability): treat canary request timeouts as ineligible
Do not count request-level relay canary errors as failed relay probes. In mixed-version networks an older witness can authenticate and ignore /rr/relay-canary-v1, producing a timeout without ever evaluating the relay. Keep explicit canary-capable DialFailed, IdentityExchangeFailed, and IdentityMismatch responses as eligible relay failures. SemVer: patch
1 parent 4d9f208 commit 5f4c745

1 file changed

Lines changed: 29 additions & 10 deletions

File tree

src/reachability/canary.rs

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ use tracing::{debug, info, warn};
3232
use crate::address::is_lan_ip;
3333
use crate::dht::AddressType;
3434
use crate::dht_network_manager::{DHTNode, DhtNetworkManager, IDENTITY_EXCHANGE_TIMEOUT};
35-
use crate::error::{NetworkError, P2PError};
35+
use crate::error::P2PError;
3636
use crate::rate_limit::EngineConfig;
3737
use crate::security::canonicalize_ip;
3838
use crate::transport_handle::TransportHandle;
@@ -645,13 +645,11 @@ async fn request_relay_canary(
645645
}
646646
}
647647

648-
fn canary_request_error_disposition(error: &P2PError) -> RelayCanaryProbeDisposition {
649-
match error {
650-
P2PError::Timeout(_) | P2PError::Network(NetworkError::Timeout) => {
651-
RelayCanaryProbeDisposition::Failure
652-
}
653-
_ => RelayCanaryProbeDisposition::Ineligible,
654-
}
648+
fn canary_request_error_disposition(_error: &P2PError) -> RelayCanaryProbeDisposition {
649+
// Request-level failures do not prove the relay is bad: in mixed-version
650+
// networks the witness may simply not implement the relay canary protocol.
651+
// Only typed canary responses count as eligible probe attempts.
652+
RelayCanaryProbeDisposition::Ineligible
655653
}
656654

657655
#[cfg(test)]
@@ -663,6 +661,7 @@ mod tests {
663661
use rand::rngs::StdRng;
664662

665663
use super::*;
664+
use crate::error::NetworkError;
666665
use crate::rate_limit::Engine;
667666

668667
const TARGET_SEED: u8 = 1;
@@ -740,6 +739,22 @@ mod tests {
740739
);
741740
}
742741

742+
#[test]
743+
fn explicit_probe_failures_count_as_relay_failures() {
744+
assert_eq!(
745+
RelayCanaryProbeResult::DialFailed.disposition(),
746+
RelayCanaryProbeDisposition::Failure
747+
);
748+
assert_eq!(
749+
RelayCanaryProbeResult::IdentityExchangeFailed.disposition(),
750+
RelayCanaryProbeDisposition::Failure
751+
);
752+
assert_eq!(
753+
RelayCanaryProbeResult::IdentityMismatch.disposition(),
754+
RelayCanaryProbeDisposition::Failure
755+
);
756+
}
757+
743758
#[test]
744759
fn rate_limit_throttles_per_source_not_across_sources() {
745760
let limiter = Engine::new(relay_canary_rate_limit_config());
@@ -796,10 +811,14 @@ mod tests {
796811
}
797812

798813
#[test]
799-
fn request_timeout_counts_as_probe_failure() {
814+
fn request_timeout_is_ineligible_for_mixed_version_witness() {
800815
assert_eq!(
801816
canary_request_error_disposition(&P2PError::Timeout(RELAY_CANARY_REQUEST_TIMEOUT)),
802-
RelayCanaryProbeDisposition::Failure
817+
RelayCanaryProbeDisposition::Ineligible
818+
);
819+
assert_eq!(
820+
canary_request_error_disposition(&P2PError::Network(NetworkError::Timeout)),
821+
RelayCanaryProbeDisposition::Ineligible
803822
);
804823
}
805824

0 commit comments

Comments
 (0)