guard sample: Change semantics of `contains` to handle ambiguity.

This is necessary for the (somewhat undesirable) lookup_ids function
to return an ID that the dirmgr can actually use to report successes
and failures.

As noted, lookup_ids will create problems down the road when we
implement relays. We should refactor it out before then.
This commit is contained in:
Nick Mathewson 2022-10-25 10:47:22 -04:00
parent 28f2e2997e
commit d65c351b33
2 changed files with 33 additions and 10 deletions

View File

@ -1055,7 +1055,11 @@ impl GuardMgrInner {
}
/// Return every currently extant FirstHopId for a guard or fallback
/// directory matching the provided keys.
/// directory matching (or possibly matching) the provided keys.
///
/// An identity is _possibly matching_ if it contains some of the IDs in the
/// provided identity, and it has no _contradictory_ identities, but it does
/// not necessarily contain _all_ of those identities.
///
/// # TODO
///
@ -1064,6 +1068,10 @@ impl GuardMgrInner {
/// doesn't know whether its circuit came from a guard or a fallback. To
/// solve that, we'll need CircMgr to record and report which one it was
/// using, which will take some more plumbing.
///
/// TODO relay: we will have to make the change above when we implement
/// relays; otherwise, it would be possible for an attacker to exploit it to
/// mislead us about our guard status.
fn lookup_ids<T>(&self, identity: &T) -> Vec<FirstHopId>
where
T: tor_linkspec::HasRelayIds + ?Sized,
@ -1073,9 +1081,12 @@ impl GuardMgrInner {
let id = ids::GuardId::from_relay_ids(identity);
for sample in GuardSetSelector::iter() {
if self.guards.guards(&sample).contains(&id) {
vec.push(FirstHopId(FirstHopIdInner::Guard(sample, id.clone())));
}
let guard_id = match self.guards.guards(&sample).contains(&id) {
Ok(true) => &id,
Err(other) => other,
Ok(false) => continue,
};
vec.push(FirstHopId(FirstHopIdInner::Guard(sample, guard_id.clone())));
}
let id = ids::FallbackId::from_relay_ids(identity);

View File

@ -278,12 +278,24 @@ impl GuardSet {
self.guards.all_overlapping(relay).is_empty()
}
/// Return true if `id` is definitely a member of this set.
pub(crate) fn contains(&self, id: &GuardId) -> bool {
// XXXX TODO pt-client: fix next.
// (This could be yes/no/maybe.)
self.guards.by_all_ids(id).is_some()
/// Return `Ok(true)` if `id` is definitely a member of this set, and
/// `Ok(false)` if it is definitely not a member.
///
/// If we cannot tell, it's because there is a guard in this sample that has
/// a _subset_ of the IDs in `id`. In that case, we return
/// `Err(guard_ident)`, where `guard_ident` is the identity of that guard.
pub(crate) fn contains(&self, id: &GuardId) -> Result<bool, &GuardId> {
let overlapping = self.guards.all_overlapping(id);
match &overlapping[..] {
[singleton] => {
if singleton.has_all_relay_ids_from(id) {
Ok(true)
} else {
Err(singleton.guard_id())
}
}
_ => Ok(false),
}
}
/// If there are not enough filter-permitted usable guards in this